commit 58ee357787a9a094cb811494607b9d32876f7640 Author: vickytechkey Date: Mon Oct 19 19:13:34 2026 +0530 feat: initialize project structure with core components, notifications, and database migrations diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..47bf476 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[*.yaml] +indent_size = 2 + +[*.yml] +indent_size = 2 + +[*.vue] +trim_trailing_whitespace = false + +[*.go] +indent_style = tab diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..9a63b41 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,123 @@ +module.exports = { + ignorePatterns: ["test/*.js", "server/modules/*", "src/util.js"], + root: true, + env: { + browser: true, + commonjs: true, + es2020: true, + node: true, + }, + extends: [ + "eslint:recommended", + "plugin:vue/vue3-recommended", + "plugin:vue-scoped-css/vue3-recommended", + "plugin:jsdoc/recommended-error", + "prettier", // Disables ESLint formatting rules that conflict with Prettier + ], + parser: "vue-eslint-parser", + parserOptions: { + parser: "@typescript-eslint/parser", + sourceType: "module", + requireConfigFile: false, + }, + plugins: ["jsdoc", "@typescript-eslint"], + rules: { + 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/multi-word-component-names": "off", + "vue-scoped-css/no-unused-selector": "warn", + curly: "error", + "no-var": "error", + "no-throw-literal": "error", + "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-control-regex": "off", + "one-var": ["error", "never"], + "max-statements-per-line": ["error", { max: 1 }], + "jsdoc/check-tag-names": [ + "error", + { + definedTags: ["link"], + }, + ], + "jsdoc/no-undefined-types": "off", + "jsdoc/no-defaults": ["error", { noOptionalParamNames: true }], + "jsdoc/require-throws": "warn", + "jsdoc/require-jsdoc": [ + "error", + { + 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": [ + "warn", + { + forceRequireReturn: true, + forceReturnsWithAsync: true, + }, + ], + "jsdoc/require-param-type": "warn", + "jsdoc/require-param-description": "warn", + }, + overrides: [ + // Override for TypeScript + { + files: ["**/*.ts"], + extends: ["plugin:@typescript-eslint/recommended"], + rules: { + "jsdoc/require-returns-type": "off", + "jsdoc/require-returns": [ + "warn", + { + forceRequireReturn: false, + forceReturnsWithAsync: false, + }, + ], + "jsdoc/require-param-type": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/ban-ts-comment": "off", + "prefer-const": "off", + "@typescript-eslint/no-unused-vars": "warn", + eqeqeq: "off", + }, + }, + ], +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1dcaa2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +.idea + +/data +!/data/.gitkeep +/data* +.vscode + +/private +/out +/tmp +.env + +/extra/healthcheck.exe +/extra/healthcheck +/extra/healthcheck-armv7 + +extra/exe-builder/bin +extra/exe-builder/obj + +.vs +.vscode +.devcontainer diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..456c255 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +legacy-peer-deps=true +min-release-age=7 +min-release-age-exclude[]=redbean-node diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..9fdef27 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# language files +src/lang/*.json diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..bee4a20 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,65 @@ +/** + * Prettier Configuration for Uptime Kuma + * + * Usage: + * npm run fmt - Format all files (auto-runs in CI via autofix workflow) + * npm run fmt -- --check - Check formatting without making changes + * + * TIP: This formatter is automatically run in CI, so no need to worry about it + */ +module.exports = { + // Core formatting options - matching original ESLint rules + semi: true, + singleQuote: false, + trailingComma: "es5", + printWidth: 120, + tabWidth: 4, + useTabs: false, + endOfLine: "lf", + arrowParens: "always", + bracketSpacing: true, + bracketSameLine: false, + + // Vue-specific settings + vueIndentScriptAndStyle: false, + singleAttributePerLine: false, + htmlWhitespaceSensitivity: "ignore", // More forgiving with whitespace in HTML + + // Override settings for specific file types + overrides: [ + { + files: "*.vue", + options: { + parser: "vue", + }, + }, + { + files: ["*.json"], + options: { + tabWidth: 4, + trailingComma: "none", + }, + }, + { + files: ["*.yml", "*.yaml"], + options: { + tabWidth: 2, + trailingComma: "none", + }, + }, + { + files: ["src/icon.js"], + options: { + trailingComma: "all", + }, + }, + { + files: ["*.md"], + options: { + printWidth: 100, + proseWrap: "preserve", + tabWidth: 2, + }, + }, + ], +}; diff --git a/.stylelintrc b/.stylelintrc new file mode 100644 index 0000000..c0b540d --- /dev/null +++ b/.stylelintrc @@ -0,0 +1,17 @@ +{ + "extends": [ + "stylelint-config-standard", + "stylelint-config-prettier" + ], + "customSyntax": "postcss-html", + "rules": { + "no-descending-specificity": null, + "declaration-empty-line-before": null, + "alpha-value-notation": "number", + "color-function-notation": "legacy", + "shorthand-property-no-redundant-values": null, + "color-hex-length": null, + "declaration-block-no-redundant-longhand-properties": null, + "at-rule-no-unknown": null + } +} diff --git a/.ws-context b/.ws-context new file mode 100644 index 0000000..2e05ec7 --- /dev/null +++ b/.ws-context @@ -0,0 +1,6 @@ +{ + "framework": "none", + "src/**": { + "framework": "vue" + } +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..83b1f07 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,566 @@ +# Project Info + +First of all, I want to thank everyone who has submitted issues or shared pull +requests for Uptime Kuma. I never thought the GitHub community would be so nice! +Because of this, I also never thought that other people would actually read and +edit my code. Parts of the code are not very well-structured or commented, sorry +about that. + +Before you start, please read our [Code of Conduct](CODE_OF_CONDUCT.md) to understand our community standards. + +The project was created with `vite` and is written in `vue3`. Our backend +lives in the `server`-directory and mostly communicates via websockets. +Both frontend and backend share the same `package.json`. + +For production, the frontend is built into the `dist`-directory and the server +(`express.js`) exposes the `dist` directory as the root of the endpoint. For +development, we run vite in development mode on another port. + +## Directories + +- `config` (dev config files) +- `data` (App data) +- `db` (Base database and migration scripts) +- `dist` (Frontend build) +- `docker` (Dockerfiles) +- `extra` (Extra useful scripts) +- `public` (Frontend resources for dev only) +- `server` (Server source code) +- `src` (Frontend source code) +- `test` (unit test) + +## Can I Create a Pull Request for Uptime Kuma? + +Whether or not you can create a pull request depends on the nature of your +contribution. We value both your time and our maintainers' time, so we want to +make sure it's spent efficiently. + +If you're unsure about any process or step, you're probably not the only one +with that question—please feel free to ask. We're happy to help! + +Different types of pull requests (PRs) may have different guidelines, so be sure +to review the appropriate one for your contribution. + +-
Security Fixes (click to expand) +

+ + Submitting security fixes is something that may put the community at risk. + Please read through our [security policy](SECURITY.md) and submit + vulnerabilities via an [advisory] + [issue] instead. We encourage you to + submit how to fix a vulnerability if you know how to, this is not required. + Following the security policy allows us to properly test, fix bugs. This + review allows us to notice, if there are any changes necessary to unrelated + parts like the documentation. + [**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 + +

+
+ +-
Small, Non-Breaking Bug Fixes (click to expand) +

+ + 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. + + Because maintainer time is precious, junior maintainers may merge + uncontroversial PRs in this area. + +

+
+ +-
Translations / Internationalisation (i18n) (click to expand) +

+ + 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 [``](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). + + Because maintainer time is precious, junior maintainers may merge uncontroversial PRs in this area. + +

+
+ +-
New Notification Providers (click to expand) +

+ + 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. + + - Both `monitorJSON` and `heartbeatJSON` can be `null` for some events. If + + both are `null`, this is a general testing message, but if just + `heartbeatJSON` is `null` this is a certificate expiry. + + - Please wrap the axios call into a + + ```js + try { + let result = await axios.post(...); + if (result.status === ...) ... + } catch (error) { + this.throwGeneralAxiosError(error); + } + ``` + + - `server/notification.js` is where the backend of the notification provider + needs to be registered. _If you have an idea how we can skip this step, we + would love to hear about it ^^_ + + - `src/components/NotificationDialog.vue` you need to decide if the provider + is a regional or a global one and add it with a name to the respective list + + - `src/components/notifications/PROVIDER_NAME.vue` is where the frontend of + each provider lives. Please make sure that you have: + + - used `HiddenInput` for secret credentials + - included all the necessary helptexts/placeholder/.. to make sure the + + notification provider is simple to setup for new users. - include all + 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 ^^_ + + Offering notifications is close to the core of what we are as an uptime + monitor. Therefore, making sure that they work is also really important. + Because testing notification providers is quite time intensive, we mostly + offload this onto the person contributing a notification provider. + + 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 + - Domain Expiry via and a larger time set + - Testing (the test button on the notification provider setup page) + +
+ + Using the following way to format this is encouraged: + + ```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) | + | Domain-expiry | ![Before](image-link) | ![After](image-link) | + | Testing | ![Before](image-link) | ![After](image-link) | + ``` + + Because maintainer time is precious, junior maintainers may merge + uncontroversial PRs in this area. + +

+
+ +-
New Monitoring Types (click to expand) +

+ + 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` + - in the unhappy-path: throw an `Error` for each fault that is detected with an actionable error message. + - NEVER set `heartbeat.status = DOWN` unless you want to explicitly ignore retries. + + - `server/uptime-kuma-server.js` is where the monitoring backend needs to be + registered. _If you have an idea how we can skip this step, we would love to + hear about it ^^_ + + - `src/pages/EditMonitor.vue` is the shared frontend users interact with. + Please make sure that you have: - used `HiddenInput` for secret + credentials - included all the necessary helptexts/placeholder/.. to make + sure the notification provider is simple to setup for new users. - include + all translations (`{{ $t("Translation key") }}`, + [``](https://vue-i18n.intlify.dev/guide/advanced/component.html)) + in `src/lang/en.json` to enable our translators to translate this + + Because maintainer time is precious, junior maintainers may merge + uncontroversial PRs in this area. + +

+
+ +-
New Features / Major Changes / Breaking Bugfixes (click to expand) +

+ + be sure to **create an empty draft pull request or open an issue, so we can + have a discussion first**. + This is especially important for large pull requests or when you don't know if it will be merged or not. + When adding new features, please also add tests to ensure your changes work as expected and to prevent future regressions. + + Because of the large impact of this work, only senior maintainers may + merge PRs in this area. + +

+
+ +-
As a First-Time Contributor (click to expand) +

+ + 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. + 4. **Push** your changes to your forked repository. + 5. **Open a pull request** to the `master` branch of the Uptime Kuma repository. + - For large changes, please open a **draft pull request** first to discuss the changes with the maintainers. + 6. **Provide a clear and concise description** of the changes you've made and link any related issues. + 7. **Complete the PR checklist** and make sure all CI checks pass. + 8. **Request a review** when your pull request is ready. + + ## When Can You Change the PR Status to "Ready for Review"? + + 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. + - You have verified that CI/CD checks pass successfully. + + A volunteer maintainer will review your PR as soon as possible. + You can help us by reviewing other PRs or taking a look at open issues. + +## The following rules are essential for making your PR mergeable + +- 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** + +- Make sure your **PR passes our continuous integration**. PRs will not be + merged unless all CI-Checks are green. +- **Breaking changes** (unless for a good reason and discussed beforehand) will + not get merged / not get merged quickly. Such changes require a major version + release. +- **Test your code** before submitting a PR. Buggy PRs will not be merged. +- Make sure the **UI/UX is close to Uptime Kuma**. +- **Think about the maintainability**: Don't add functionality that is + completely **out of scope**. Keep in mind that we need to be able to maintain + the functionality. +- Don't modify or delete existing logic without a valid reason. +- Don't convert existing code into other programming languages for no reason. + +### 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`. + +I ([@louislam](https://github.com/louislam)) have the final say. +If your pull request does not meet my expectations, I will reject it, no matter how much time +you spent on it. + +We will assign your pull request to a [milestone](https://github.com/louislam/uptime-kuma/milestones), if we plan to review and merge it. + +Please don't rush or ask for an ETA. +We have to understand the pull request, make sure it has no breaking changes and stick to the vision of this project, especially for large pull requests. + +## I'd Like to Work on an Issue. How Do I Do That? + +We have found that assigning people to issues is unnecessary management +overhead. Instead, a short comment stating that you want to work on an issue is +appreciated, as it saves time for other developers. If you encounter any +problems during development, feel free to leave a comment describing what you +are stuck on. We are here to help. + +## Project Style + +I personally do not like something that requires a lot of configuration before +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`/`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 + +- Settings should be configurable in the frontend. Environment variables are + discouraged, unless it is related to startup such as `DATA_DIR` +- Easy to use +- The web UI styling should be consistent and nice + +## Coding Styles + +- 4 spaces indentation +- Follow `.editorconfig` +- Follow ESLint +- Methods and functions should be documented with JSDoc + +## Name Conventions + +- Javascript/Typescript: camelCaseType +- SQLite: snake_case (Underscore) +- CSS/SCSS: kebab-case (Dash) + +## Tools + +- [`Node.js`](https://nodejs.org/) >= 20.4.0 +- [`npm`](https://www.npmjs.com/) >= 9.3 +- [`git`](https://git-scm.com/) +- IDE that supports [`ESLint`](https://eslint.org/) and EditorConfig (I am using + [`IntelliJ IDEA`](https://www.jetbrains.com/idea/)) +- A SQLite GUI tool (f.ex. + [`SQLite Expert Personal`](https://www.sqliteexpert.com/download.html) or + [`DBeaver Community`](https://dbeaver.io/download/)) + +## Git Branches + +- `master`: 2.X.X development. If you want to add a new feature, your pull + request should base on this. +- `1.23.X`: 1.23.X development. If you want to fix a bug for v1 and v2, your + pull request should base on this. +- All other branches are unused, outdated or for dev. + +## Install Dependencies for Development + +```bash +npm ci +``` + +## Dev Server + +We can start the frontend dev server and the backend dev server in one command. + +Port `3000` and port `3001` will be used. + +```bash +npm run dev +``` + +But sometimes you may want to restart the server without restarting the +frontend. In that case, you can run these commands in two terminals: + +```bash +npm run start-frontend-dev +npm run start-server-dev +``` + +## Backend Server + +It binds to `0.0.0.0:3001` by default. + +The backend is an `express.js` server with `socket.io` integrated. It uses +`socket.io` to communicate with clients, and most server logic is encapsulated +in the `socket.io` handlers. `express.js` is also used to serve: + +- as an entry point for redirecting to a status page or the dashboard +- the frontend built files (`index.html`, `*.js`, `*.css`, etc.) +- internal APIs of the status page + +### Structure in `/server/` + +- `jobs/` (Jobs that are running in another process) +- `model/` (Object model, auto-mapping to the database table name) +- `modules/` (Modified 3rd-party modules) +- `monitor_types/` (Monitor Types) +- `notification-providers/` (individual notification logic) +- `routers/` (Express Routers) +- `socket-handler/` (Socket.io Handlers) +- `server.js` (Server entry point) +- `uptime-kuma-server.js` (UptimeKumaServer class, main logic should be here, + but some still in `server.js`) + +## Frontend Dev Server + +It binds to `0.0.0.0:3000` by default. The frontend dev server is used for +development only. + +For production, it is not used. It will be compiled to `dist` directory instead via `npm run build`. + +You can use Vue.js devtools Chrome extension for debugging. + +### Frontend Details + +Uptime Kuma Frontend is a single page application (SPA). Most paths are handled +by Vue Router. + +The router is in `src/router.js` + +Most data in the frontend is stored at the root level, even though the router can navigate to different pages. + +The data and socket logic are in `src/mixins/socket.js`. + +## Database Migration + +See: + +## Unit Test + +To run unit tests, use the following command: + +```bash +npm run build +npm test +``` + +## Dependencies + +Both frontend and backend share the same `package.json`. +However, the frontend dependencies are eventually not used in the production environment, because it +is usually also baked into `dist` files. So: + +- Frontend dependencies = "devDependencies" + - Examples: - `vue`, `chart.js` +- Backend dependencies = "dependencies" + - Examples: `socket.io`, `sqlite3` +- Development dependencies = "devDependencies" + - Examples: `eslint`, `sass` + +### Update Dependencies + +Since previously updating Vite 2.5.10 to 2.6.0 broke the application completely, +from now on, it should update the patch release version only. + +Patch release = the third digit ([Semantic Versioning](https://semver.org/)) + +If for security / bug / other reasons, a library must be updated, breaking +changes need to be checked by the person proposing the change. + +## Spelling & Grammar + +Feel free to correct the spelling and grammar in the documentation or code. +English is not the native language of the maintainers. + +## Wiki + +Since there is no way to make a pull request to the wiki, I have set up another +repo to do that. + + + +## Maintainer + +### What is a maintainer and what are their roles? + +This project has multiple maintainers who specialise in different areas. +Currently, there are 3 maintainers: + +| Person | Role | Main Area | +| ----------------- | ----------------- | ---------------- | +| `@louislam` | senior maintainer | major features | +| `@chakflying` | junior maintainer | fixing bugs | +| `@commanderstorm` | junior maintainer | issue-management | + +### Procedures + +We have a few procedures we follow. These are documented here: + +-

Set up a Docker Builder (click to expand) +

+ - amd64 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" + ``` + + 3. Create a new builder. + + ```bash + docker buildx create --name kuma-builder --platform linux/amd64 + docker buildx use kuma-builder + docker buildx inspect --bootstrap + ``` + + 4. Append the remote context to the builder. + + ```bash + docker buildx create --append --name kuma-builder --platform linux/arm64 oracle-arm64-jp + ``` + + 5. Verify the builder and check if the builder is using `kuma-builder`. + `docker buildx inspect kuma-builder docker buildx ls` + +

+
+ +-
Release (click to expand) +

+ 1. Draft a release note + 2. Make sure the repo is cleared + 3. If the healthcheck is updated, remember to re-compile it: + `npm run build-docker-builder-go` + 4. `npm run release-final` with env vars: `VERSION` and `GITHUB_TOKEN` + 5. Wait until the `Press any key to continue` + 6. `git push` + 7. Publish the release note as `1.X.X` + 8. Press any key to continue + 9. Deploy to the demo server: `npm run deploy-demo-server` + + These Items need to be checked: + - [ ] Check all tags is fine on + + - [ ] Try the Docker image with tag 1.X.X (Clean install / amd64 / arm64) + - [ ] Try clean installation with Node.js + +

+
+ +-
Release Beta (click to expand) +

+ 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` + 4. Wait until the `Press any key to continue` + 5. Publish the release note as `1.X.X-beta.X` + 6. Press any key to continue + +

+
+ +-
Release Wiki (click to expand) +

+ + **Setup Repo** + + ```bash + git clone https://github.com/louislam/uptime-kuma-wiki.git + cd uptime-kuma-wiki + git remote add production https://github.com/louislam/uptime-kuma.wiki.git + ``` + + **Push to Production Wiki** + + ```bash + git pull + git push production master + ``` + +

+
+ +-
Change the base of a pull request such as master to 1.23.X (click to expand) +

+ + ```bash + git rebase --onto + ``` + +

+
diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b811362 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Louis Lam + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d90d46c --- /dev/null +++ b/README.md @@ -0,0 +1,205 @@ +
+ Uptime Kuma Logo +
+ +# Uptime Kuma + +Uptime Kuma is an easy-to-use self-hosted monitoring tool. + + +[![GitHub Sponsors](https://img.shields.io/github/sponsors/louislam?label=GitHub%20Sponsors)](https://github.com/sponsors/louislam) +Translation status + + +Uptime Kuma Dashboard Screenshot + +## 🥔 Live Demo + +Try it! + +Demo Server (Location: Frankfurt - Germany): + +It is a temporary live demo, all data will be deleted after 10 minutes. Sponsored by [Uptime Kuma Sponsors](https://github.com/louislam/uptime-kuma#%EF%B8%8F-sponsors). + +## ⭐ Features + +- Monitoring uptime for HTTP(s) / TCP / HTTP(s) Keyword / HTTP(s) Json Query / Websocket / Ping / DNS Record / Push / Steam Game Server / Docker Containers +- Fancy, Reactive, Fast UI/UX +- Notifications via Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP), and [90+ notification services, click here for the full list](https://github.com/louislam/uptime-kuma/tree/master/src/components/notifications) +- 20-second intervals +- [Multi Languages](https://github.com/louislam/uptime-kuma/tree/master/src/lang) +- Multiple status pages +- Map status pages to specific domains +- Ping chart +- Certificate info +- Proxy support +- 2FA support + +## 🔧 How to Install + +### 🐳 Docker Compose + +```bash +mkdir uptime-kuma +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] +> File Systems like **NFS** (Network File System) are **NOT** supported. Please map to a local directory or volume. + +### 🐳 Docker Command + +```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: + +```bash +docker run ... -p 127.0.0.1:3001:3001 ... +``` + +### 💪🏻 Non-Docker + +Requirements: + +- Platform + - ✅ Major Linux distros such as Debian, Ubuntu, Fedora and ArchLinux etc. + - ✅ Windows 10 (x64), Windows Server 2012 R2 (x64) or higher + - ❌ FreeBSD / OpenBSD / NetBSD + - ❌ Replit / Heroku +- [Node.js](https://nodejs.org/en/download/) >= 20.4 +- [Git](https://git-scm.com/downloads) +- [pm2](https://pm2.keymetrics.io/) - For running Uptime Kuma in the background + +```bash +git clone https://github.com/louislam/uptime-kuma.git +cd uptime-kuma +npm run setup + +# Option 1. Try it +node server/server.js + +# (Recommended) Option 2. Run in the background using PM2 +# Install PM2 if you don't have it: +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 + +```bash +# If you want to see the current console output +pm2 monit + +# If you want to add it to startup +pm2 startup && pm2 save +``` + +### Advanced Installation + +If you need more options or need to browse via a reverse proxy, please read: + + + +## 🆙 How to Update + +Please read: + + + +## 🆕 What's Next? + +I will assign requests/issues to the next milestone. + + + +## ❤️ Sponsors + +[![Powered by Atlas Cloud](https://www.atlascloud.ai/oss-program/powered-by-atlas-cloud.svg)](https://www.atlascloud.ai/?ref=UZCYBN) + +Thank you so much! (GitHub Sponsors will be updated manually. OpenCollective sponsors will be updated automatically, the list will be cached by GitHub though. It may need some time to be updated) + +Uptime Kuma Sponsors + +## 🖼 More Screenshots + +Light Mode: + +Uptime Kuma Light Mode Screenshot of how the Dashboard looks + +Status Page: + +Uptime Kuma Status Page Screenshot + +Settings Page: + +Uptime Kuma Settings Page Screenshot + +Telegram Notification Sample: + +Uptime Kuma Telegram Notification Sample Screenshot + +## Motivation + +- I was looking for a self-hosted monitoring tool like "Uptime Robot", but it is hard to find a suitable one. One of the closest ones is statping. Unfortunately, it is not stable and no longer maintained. +- Wanted to build a fancy UI. +- Learn Vue 3 and vite.js. +- Show the power of Bootstrap 5. +- Try to use WebSocket with SPA instead of a REST API. +- Deploy my first Docker image to Docker Hub. + +If you love this project, please consider giving it a ⭐. + +## 🗣️ Discussion / Ask for Help + +⚠️ For any general or technical questions, please don't send me an email, as I am unable to provide support in that manner. I will not respond if you ask questions there. + +I recommend using Google, GitHub Issues, or Uptime Kuma's subreddit for finding answers to your question. If you cannot find the information you need, feel free to ask: + +- [GitHub Issues](https://github.com/louislam/uptime-kuma/issues) +- [Subreddit (r/UptimeKuma)](https://www.reddit.com/r/UptimeKuma/) + +My Reddit account: [u/louislamlam](https://reddit.com/u/louislamlam) +You can mention me if you ask a question on the subreddit. + +## Contributions + +### Create Pull Requests + +Pull requests are awesome. +To keep reviews fast and effective, please make sure you’ve [read our pull request guidelines](https://github.com/louislam/uptime-kuma/blob/master/CONTRIBUTING.md#can-i-create-a-pull-request-for-uptime-kuma). + +### Test Pull Requests + +There are a lot of pull requests right now, but I don't have time to test them all. + +If you want to help, you can check this: + + +### Test Beta Version + +Check out the latest beta release here: + +### Bug Reports / Feature Requests + +If you want to report a bug or request a new feature, feel free to open a [new issue](https://github.com/louislam/uptime-kuma/issues). + +### Translations + +If you want to translate Uptime Kuma into your language, please visit [Weblate Readme](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md). + +### Spelling & Grammar + +Feel free to correct the grammar in the documentation or code. +My mother language is not English and my grammar is not that great. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..5637221 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,47 @@ +# Security Policy + +> [!CAUTION] +> Unfortunately, AI slop reports keep wasting my time. It will be closed and you will get banned immediately if you try to do that. + +## Reporting a Vulnerability + +1. Please report security issues to + . +2. Please also create an empty security issue to alert me, as GitHub Advisories + do not send a notification, I probably will miss it without this. + + +- 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. +- Do not report any SSRF issues. + +## Do you accept other 3rd-party bug bounty platforms? + +At this moment, I DO NOT accept other bug bounty platforms, because I am not +familiar with these platforms and someone has tried to send a phishing link to +me by doing this already. To minimize my own risk, please report through GitHub +Advisories only. I will ignore all 3rd-party bug bounty platforms emails. + +## Supported Versions + +### Uptime Kuma Versions + +You should use or upgrade to the latest version of Uptime Kuma. +All versions are upgradable to the latest version. + +### Upgradable Docker Tags + +| Tag | Supported | +| --------------- | ------------------------------------------------------------------------------------- | +| 2 | :white_check_mark: | +| 2-slim | :white_check_mark: | +| next | :white_check_mark: | +| next-slim | :white_check_mark: | +| 2-rootless | :white_check_mark: | +| 2-slim-rootless | :white_check_mark: | +| 1 | [⚠️ Deprecated](https://github.com/louislam/uptime-kuma/wiki/Migration-From-v1-To-v2) | +| 1-debian | [⚠️ Deprecated](https://github.com/louislam/uptime-kuma/wiki/Migration-From-v1-To-v2) | +| latest | [⚠️ Deprecated](https://github.com/louislam/uptime-kuma/wiki/Migration-From-v1-To-v2) | +| debian | [⚠️ Deprecated](https://github.com/louislam/uptime-kuma/wiki/Migration-From-v1-To-v2) | +| All other tags | ❌ | diff --git a/db/knex_init_db.js b/db/knex_init_db.js new file mode 100644 index 0000000..9d83414 --- /dev/null +++ b/db/knex_init_db.js @@ -0,0 +1,611 @@ +const { R } = require("redbean-node"); +const { log } = require("../src/util"); + +/** + * ⚠️⚠️⚠️⚠️⚠️⚠️ DO NOT ADD ANYTHING HERE! + * IF YOU NEED TO ADD FIELDS, ADD IT TO ./db/knex_migrations + * See ./db/knex_migrations/README.md for more information + * @returns {Promise} + */ +async function createTables() { + log.info("mariadb", "Creating basic tables for MariaDB"); + const knex = R.knex; + + // TODO: Should check later if it is really the final patch sql file. + + // docker_host + await knex.schema.createTable("docker_host", (table) => { + table.increments("id"); + table.integer("user_id").unsigned().notNullable(); + table.string("docker_daemon", 255); + table.string("docker_type", 255); + table.string("name", 255); + }); + + // group + await knex.schema.createTable("group", (table) => { + table.increments("id"); + table.string("name", 255).notNullable(); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + table.boolean("public").notNullable().defaultTo(false); + table.boolean("active").notNullable().defaultTo(true); + table.integer("weight").notNullable().defaultTo(1000); + table.integer("status_page_id").unsigned(); + }); + + // proxy + await knex.schema.createTable("proxy", (table) => { + table.increments("id"); + 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.boolean("auth").notNullable(); + table.string("username", 255).nullable(); + table.string("password", 255).nullable(); + table.boolean("active").notNullable().defaultTo(true); + table.boolean("default").notNullable().defaultTo(false); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + + table.index("user_id", "proxy_user_id"); + }); + + // user + await knex.schema.createTable("user", (table) => { + table.increments("id"); + table.string("username", 255).notNullable().unique().collate("utf8_general_ci"); + table.string("password", 255); + table.boolean("active").notNullable().defaultTo(true); + table.string("timezone", 150); + table.string("twofa_secret", 64); + table.boolean("twofa_status").notNullable().defaultTo(false); + table.string("twofa_last_token", 6); + }); + + // monitor + await knex.schema.createTable("monitor", (table) => { + 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("interval").notNullable().defaultTo(20); + table.text("url"); + table.string("type", 20); + table.integer("weight").defaultTo(2000); + table.string("hostname", 255); + table.integer("port"); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + table.string("keyword", 255); + table.integer("maxretries").notNullable().defaultTo(0); + 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.string("dns_resolve_type", 5); + table.string("dns_resolve_server", 255); + table.string("dns_last_result", 255); + table.integer("retry_interval").notNullable().defaultTo(0); + table.string("push_token", 20).defaultTo(null); + table.text("method").notNullable().defaultTo("GET"); + table.text("body").defaultTo(null); + 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.string("docker_container", 255); + 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); + table.string("mqtt_username", 255); + table.string("mqtt_password", 255); + table.string("database_connection_string", 2000); + table.text("database_query"); + table.string("auth_method", 250); + table.text("auth_domain"); + table.text("auth_workstation"); + table.string("grpc_url", 255).defaultTo(null); + table.text("grpc_protobuf").defaultTo(null); + table.text("grpc_body").defaultTo(null); + table.text("grpc_metadata").defaultTo(null); + table.text("grpc_method").defaultTo(null); + table.text("grpc_service_name").defaultTo(null); + table.boolean("grpc_enable_tls").notNullable().defaultTo(false); + table.string("radius_username", 255); + table.string("radius_password", 255); + table.string("radius_calling_station_id", 50); + table.string("radius_called_station_id", 50); + table.string("radius_secret", 255); + table.integer("resend_interval").notNullable().defaultTo(0); + table.integer("packet_size").notNullable().defaultTo(56); + table.string("game", 255); + }); + + // heartbeat + 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") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.smallint("status").notNullable(); + + table.text("msg"); + table.datetime("time").notNullable(); + table.integer("ping"); + table.integer("duration").notNullable().defaultTo(0); + table.integer("down_count").notNullable().defaultTo(0); + + table.index("important"); + table.index(["monitor_id", "time"], "monitor_time_index"); + table.index("monitor_id"); + table.index(["monitor_id", "important", "time"], "monitor_important_time_index"); + }); + + // incident + await knex.schema.createTable("incident", (table) => { + table.increments("id"); + table.string("title", 255).notNullable(); + table.text("content", 255).notNullable(); + table.string("style", 30).notNullable().defaultTo("warning"); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + table.datetime("last_updated_date"); + table.boolean("pin").notNullable().defaultTo(true); + table.boolean("active").notNullable().defaultTo(true); + table.integer("status_page_id").unsigned(); + }); + + // maintenance + await knex.schema.createTable("maintenance", (table) => { + 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.boolean("active").notNullable().defaultTo(true); + table.string("strategy", 50).notNullable().defaultTo("single"); + table.datetime("start_date"); + table.datetime("end_date"); + table.time("start_time"); + table.time("end_time"); + table.string("weekdays", 250).defaultTo("[]"); + table.text("days_of_month").defaultTo("[]"); + table.integer("interval_day"); + + table.index("active"); + table.index(["strategy", "active"], "manual_active"); + table.index("user_id", "maintenance_user_id"); + }); + + // status_page + await knex.schema.createTable("status_page", (table) => { + table.increments("id"); + table.string("slug", 255).notNullable().unique().collate("utf8_general_ci"); + table.string("title", 255).notNullable(); + table.text("description"); + table.string("icon", 255).notNullable(); + table.string("theme", 30).notNullable(); + table.boolean("published").notNullable().defaultTo(true); + table.boolean("search_engine_index").notNullable().defaultTo(true); + table.boolean("show_tags").notNullable().defaultTo(false); + table.string("password"); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + table.datetime("modified_date").notNullable().defaultTo(knex.fn.now()); + table.text("footer_text"); + table.text("custom_css"); + table.boolean("show_powered_by").notNullable().defaultTo(true); + table.string("google_analytics_tag_id"); + }); + + // maintenance_status_page + await knex.schema.createTable("maintenance_status_page", (table) => { + table.increments("id"); + + 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") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + }); + + // maintenance_timeslot + await knex.schema.createTable("maintenance_timeslot", (table) => { + table.increments("id"); + table + .integer("maintenance_id") + .unsigned() + .notNullable() + .references("id") + .inTable("maintenance") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.datetime("start_date").notNullable(); + table.datetime("end_date"); + table.boolean("generated_next").defaultTo(false); + + table.index("maintenance_id"); + 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") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + 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"); + }); + // monitor_maintenance + await knex.schema.createTable("monitor_maintenance", (table) => { + table.increments("id"); + table + .integer("monitor_id") + .unsigned() + .notNullable() + .references("id") + .inTable("monitor") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table + .integer("maintenance_id") + .unsigned() + .notNullable() + .references("id") + .inTable("maintenance") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + + table.index("maintenance_id", "maintenance_id_index2"); + table.index("monitor_id", "monitor_id_index"); + }); + + // notification + await knex.schema.createTable("notification", (table) => { + table.increments("id"); + table.string("name", 255); + table.boolean("active").notNullable().defaultTo(true); + table.integer("user_id").unsigned(); + table.boolean("is_default").notNullable().defaultTo(false); + table.text("config", "longtext"); + }); + + // 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") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table + .integer("notification_id") + .unsigned() + .notNullable() + .references("id") + .inTable("notification") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + + table.index(["monitor_id", "notification_id"], "monitor_notification_index"); + }); + + // tag + await knex.schema.createTable("tag", (table) => { + table.increments("id"); + table.string("name", 255).notNullable(); + table.string("color", 255).notNullable(); + table.datetime("created_date").notNullable().defaultTo(knex.fn.now()); + }); + + // monitor_tag + await knex.schema.createTable("monitor_tag", (table) => { + table.increments("id"); + table + .integer("monitor_id") + .unsigned() + .notNullable() + .references("id") + .inTable("monitor") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table + .integer("tag_id") + .unsigned() + .notNullable() + .references("id") + .inTable("tag") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.text("value"); + }); + + // monitor_tls_info + await knex.schema.createTable("monitor_tls_info", (table) => { + table.increments("id"); + table + .integer("monitor_id") + .unsigned() + .notNullable() + .references("id") + .inTable("monitor") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.text("info_json"); + }); + + // notification_sent_history + await knex.schema.createTable("notification_sent_history", (table) => { + table.increments("id"); + 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"); + }); + + // setting + await knex.schema.createTable("setting", (table) => { + table.increments("id"); + table.string("key", 200).notNullable().unique().collate("utf8_general_ci"); + table.text("value"); + table.string("type", 20); + }); + + // 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") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.string("domain").notNullable().unique().collate("utf8_general_ci"); + }); + + /********************* + * Converted Patch here + *********************/ + + // 2023-06-30-1348-http-body-encoding.js + // ALTER TABLE monitor ADD http_body_encoding VARCHAR(25); + // UPDATE monitor SET http_body_encoding = 'json' WHERE (type = 'http' or type = 'keyword') AND http_body_encoding IS NULL; + await knex.schema.table("monitor", function (table) { + table.string("http_body_encoding", 25); + }); + + await knex("monitor") + .where(function () { + this.where("type", "http").orWhere("type", "keyword"); + }) + .whereNull("http_body_encoding") + .update({ + http_body_encoding: "json", + }); + + // 2023-06-30-1354-add-description-monitor.js + // ALTER TABLE monitor ADD description TEXT default null; + await knex.schema.table("monitor", function (table) { + table.text("description").defaultTo(null); + }); + + // 2023-06-30-1357-api-key-table.js + /* + CREATE TABLE [api_key] ( + [id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + [key] VARCHAR(255) NOT NULL, + [name] VARCHAR(255) NOT NULL, + [user_id] INTEGER NOT NULL, + [created_date] DATETIME DEFAULT (DATETIME('now')) NOT NULL, + [active] BOOLEAN DEFAULT 1 NOT NULL, + [expires] DATETIME DEFAULT NULL, + CONSTRAINT FK_user FOREIGN KEY ([user_id]) REFERENCES [user]([id]) ON DELETE CASCADE ON UPDATE CASCADE + ); + */ + await knex.schema.createTable("api_key", function (table) { + table.increments("id").primary(); + table.string("key", 255).notNullable(); + table.string("name", 255).notNullable(); + table + .integer("user_id") + .unsigned() + .notNullable() + .references("id") + .inTable("user") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + table.dateTime("created_date").defaultTo(knex.fn.now()).notNullable(); + table.boolean("active").defaultTo(1).notNullable(); + table.dateTime("expires").defaultTo(null); + }); + + // 2023-06-30-1400-monitor-tls.js + /* + ALTER TABLE monitor + ADD tls_ca TEXT default null; + + ALTER TABLE monitor + ADD tls_cert TEXT default null; + + ALTER TABLE monitor + ADD tls_key TEXT default null; + */ + await knex.schema.table("monitor", function (table) { + table.text("tls_ca").defaultTo(null); + table.text("tls_cert").defaultTo(null); + table.text("tls_key").defaultTo(null); + }); + + // 2023-06-30-1401-maintenance-cron.js + /* + -- 999 characters. https://stackoverflow.com/questions/46134830/maximum-length-for-cron-job + DROP TABLE maintenance_timeslot; + ALTER TABLE maintenance ADD cron TEXT; + 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"); + }); + + // 2023-06-30-1413-add-parent-monitor.js. + /* + ALTER TABLE monitor + 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"); + }); + + /* + patch-add-invert-keyword.sql + ALTER TABLE monitor + ADD invert_keyword BOOLEAN default 0 not null; + */ + await knex.schema.table("monitor", function (table) { + table.boolean("invert_keyword").defaultTo(0).notNullable(); + }); + + /* + patch-added-json-query.sql + ALTER TABLE monitor + ADD json_path TEXT; + + ALTER TABLE monitor + ADD expected_value VARCHAR(255); + */ + await knex.schema.table("monitor", function (table) { + table.text("json_path"); + table.string("expected_value", 255); + }); + + /* + patch-added-kafka-producer.sql + + ALTER TABLE monitor + ADD kafka_producer_topic VARCHAR(255); + +ALTER TABLE monitor + ADD kafka_producer_brokers TEXT; + +ALTER TABLE monitor + ADD kafka_producer_ssl INTEGER; + +ALTER TABLE monitor + ADD kafka_producer_allow_auto_topic_creation VARCHAR(255); + +ALTER TABLE monitor + ADD kafka_producer_sasl_options TEXT; + +ALTER TABLE monitor + ADD kafka_producer_message TEXT; + */ + await knex.schema.table("monitor", function (table) { + table.string("kafka_producer_topic", 255); + table.text("kafka_producer_brokers"); + + // patch-fix-kafka-producer-booleans.sql + table.boolean("kafka_producer_ssl").defaultTo(0).notNullable(); + table.boolean("kafka_producer_allow_auto_topic_creation").defaultTo(0).notNullable(); + + table.text("kafka_producer_sasl_options"); + table.text("kafka_producer_message"); + }); + + /* + patch-add-certificate-expiry-status-page.sql + ALTER TABLE status_page + ADD show_certificate_expiry BOOLEAN default 0 NOT NULL; + */ + await knex.schema.table("status_page", function (table) { + table.boolean("show_certificate_expiry").defaultTo(0).notNullable(); + }); + + /* + patch-monitor-oauth-cc.sql + ALTER TABLE monitor + ADD oauth_client_id TEXT default null; + +ALTER TABLE monitor + ADD oauth_client_secret TEXT default null; + +ALTER TABLE monitor + ADD oauth_token_url TEXT default null; + +ALTER TABLE monitor + ADD oauth_scopes TEXT default null; + +ALTER TABLE monitor + ADD oauth_auth_method TEXT default null; + */ + await knex.schema.table("monitor", function (table) { + table.text("oauth_client_id").defaultTo(null); + table.text("oauth_client_secret").defaultTo(null); + table.text("oauth_token_url").defaultTo(null); + table.text("oauth_scopes").defaultTo(null); + table.text("oauth_auth_method").defaultTo(null); + }); + + /* + patch-add-timeout-monitor.sql + ALTER TABLE monitor + ADD timeout DOUBLE default 0 not null; + */ + await knex.schema.table("monitor", function (table) { + table.double("timeout").defaultTo(0).notNullable(); + }); + + /* + patch-add-gamedig-given-port.sql + ALTER TABLE monitor + ADD gamedig_given_port_only BOOLEAN default 1 not null; + */ + await knex.schema.table("monitor", function (table) { + table.boolean("gamedig_given_port_only").defaultTo(1).notNullable(); + }); + + log.info("mariadb", "Created basic tables for MariaDB"); +} + +module.exports = { + createTables, +}; diff --git a/db/knex_migrations/2023-08-16-0000-create-uptime.js b/db/knex_migrations/2023-08-16-0000-create-uptime.js new file mode 100644 index 0000000..d87844c --- /dev/null +++ b/db/knex_migrations/2023-08-16-0000-create-uptime.js @@ -0,0 +1,43 @@ +exports.up = function (knex) { + return knex.schema + .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") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + 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"]); + }) + .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") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + 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"]); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTable("stat_minutely").dropTable("stat_daily"); +}; diff --git a/db/knex_migrations/2023-08-18-0301-heartbeat.js b/db/knex_migrations/2023-08-18-0301-heartbeat.js new file mode 100644 index 0000000..15ba6fd --- /dev/null +++ b/db/knex_migrations/2023-08-18-0301-heartbeat.js @@ -0,0 +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); + }); +}; + +exports.down = function (knex) { + // Rename heartbeat.start_time to heartbeat.time + return knex.schema.alterTable("heartbeat", function (table) { + table.dropColumn("end_time"); + }); +}; diff --git a/db/knex_migrations/2023-09-29-0000-heartbeat-retires.js b/db/knex_migrations/2023-09-29-0000-heartbeat-retires.js new file mode 100644 index 0000000..b0cb451 --- /dev/null +++ b/db/knex_migrations/2023-09-29-0000-heartbeat-retires.js @@ -0,0 +1,12 @@ +exports.up = function (knex) { + // Add new column heartbeat.retries + 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"); + }); +}; diff --git a/db/knex_migrations/2023-10-08-0000-mqtt-query.js b/db/knex_migrations/2023-10-08-0000-mqtt-query.js new file mode 100644 index 0000000..2606e2e --- /dev/null +++ b/db/knex_migrations/2023-10-08-0000-mqtt-query.js @@ -0,0 +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"); + }); +}; + +exports.down = function (knex) { + // Drop column monitor.mqtt_check_type + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("mqtt_check_type"); + }); +}; diff --git a/db/knex_migrations/2023-10-11-1915-push-token-to-32.js b/db/knex_migrations/2023-10-11-1915-push-token-to-32.js new file mode 100644 index 0000000..989e8c5 --- /dev/null +++ b/db/knex_migrations/2023-10-11-1915-push-token-to-32.js @@ -0,0 +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(); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.string("push_token", 20).alter(); + }); +}; diff --git a/db/knex_migrations/2023-10-16-0000-create-remote-browsers.js b/db/knex_migrations/2023-10-16-0000-create-remote-browsers.js new file mode 100644 index 0000000..3918e33 --- /dev/null +++ b/db/knex_migrations/2023-10-16-0000-create-remote-browsers.js @@ -0,0 +1,26 @@ +exports.up = function (knex) { + return knex.schema + .createTable("remote_browser", function (table) { + table.increments("id"); + table.string("name", 255).notNullable(); + table.string("url", 255).notNullable(); + table.integer("user_id").unsigned(); + }) + .alterTable("monitor", function (table) { + // Add new column monitor.remote_browser + table + .integer("remote_browser") + .nullable() + .defaultTo(null) + .unsigned() + .index() + .references("id") + .inTable("remote_browser"); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTable("remote_browser").alterTable("monitor", function (table) { + table.dropColumn("remote_browser"); + }); +}; diff --git a/db/knex_migrations/2023-12-20-0000-alter-status-page.js b/db/knex_migrations/2023-12-20-0000-alter-status-page.js new file mode 100644 index 0000000..d23edf7 --- /dev/null +++ b/db/knex_migrations/2023-12-20-0000-alter-status-page.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.alterTable("status_page", function (table) { + table.integer("auto_refresh_interval").defaultTo(300).unsigned(); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("status_page", function (table) { + table.dropColumn("auto_refresh_interval"); + }); +}; diff --git a/db/knex_migrations/2023-12-21-0000-stat-ping-min-max.js b/db/knex_migrations/2023-12-21-0000-stat-ping-min-max.js new file mode 100644 index 0000000..4a5103b --- /dev/null +++ b/db/knex_migrations/2023-12-21-0000-stat-ping-min-max.js @@ -0,0 +1,39 @@ +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"); + }) + .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"); + }); +}; + +exports.down = function (knex) { + return knex.schema + .alterTable("stat_daily", function (table) { + table.dropColumn("ping_min"); + table.dropColumn("ping_max"); + }) + .alterTable("stat_minutely", function (table) { + table.dropColumn("ping_min"); + table.dropColumn("ping_max"); + }); +}; diff --git a/db/knex_migrations/2023-12-22-0000-hourly-uptime.js b/db/knex_migrations/2023-12-22-0000-hourly-uptime.js new file mode 100644 index 0000000..f5cee97 --- /dev/null +++ b/db/knex_migrations/2023-12-22-0000-hourly-uptime.js @@ -0,0 +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(); + + table.unique(["monitor_id", "timestamp"]); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTable("stat_hourly"); +}; diff --git a/db/knex_migrations/2024-01-22-0000-stats-extras.js b/db/knex_migrations/2024-01-22-0000-stats-extras.js new file mode 100644 index 0000000..e78c3ed --- /dev/null +++ b/db/knex_migrations/2024-01-22-0000-stats-extras.js @@ -0,0 +1,25 @@ +exports.up = function (knex) { + return knex.schema + .alterTable("stat_daily", function (table) { + table.text("extras").defaultTo(null).comment("Extra statistics during this time period"); + }) + .alterTable("stat_minutely", function (table) { + table.text("extras").defaultTo(null).comment("Extra statistics during this time period"); + }) + .alterTable("stat_hourly", function (table) { + table.text("extras").defaultTo(null).comment("Extra statistics during this time period"); + }); +}; + +exports.down = function (knex) { + return knex.schema + .alterTable("stat_daily", function (table) { + table.dropColumn("extras"); + }) + .alterTable("stat_minutely", function (table) { + table.dropColumn("extras"); + }) + .alterTable("stat_hourly", function (table) { + table.dropColumn("extras"); + }); +}; diff --git a/db/knex_migrations/2024-04-26-0000-snmp-monitor.js b/db/knex_migrations/2024-04-26-0000-snmp-monitor.js new file mode 100644 index 0000000..4e68314 --- /dev/null +++ b/db/knex_migrations/2024-04-26-0000-snmp-monitor.js @@ -0,0 +1,15 @@ +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); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("snmp_oid"); + table.dropColumn("snmp_version"); + table.dropColumn("json_path_operator"); + }); +}; diff --git a/db/knex_migrations/2024-08-24-000-add-cache-bust.js b/db/knex_migrations/2024-08-24-000-add-cache-bust.js new file mode 100644 index 0000000..b0864ee --- /dev/null +++ b/db/knex_migrations/2024-08-24-000-add-cache-bust.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + 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"); + }); +}; diff --git a/db/knex_migrations/2024-08-24-0000-conditions.js b/db/knex_migrations/2024-08-24-0000-conditions.js new file mode 100644 index 0000000..5a2096e --- /dev/null +++ b/db/knex_migrations/2024-08-24-0000-conditions.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.text("conditions").notNullable().defaultTo("[]"); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("conditions"); + }); +}; diff --git a/db/knex_migrations/2024-10-1315-rabbitmq-monitor.js b/db/knex_migrations/2024-10-1315-rabbitmq-monitor.js new file mode 100644 index 0000000..8b875ba --- /dev/null +++ b/db/knex_migrations/2024-10-1315-rabbitmq-monitor.js @@ -0,0 +1,15 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.text("rabbitmq_nodes"); + table.string("rabbitmq_username"); + table.string("rabbitmq_password"); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("rabbitmq_nodes"); + table.dropColumn("rabbitmq_username"); + table.dropColumn("rabbitmq_password"); + }); +}; diff --git a/db/knex_migrations/2024-10-31-0000-fix-snmp-monitor.js b/db/knex_migrations/2024-10-31-0000-fix-snmp-monitor.js new file mode 100644 index 0000000..7b070ac --- /dev/null +++ b/db/knex_migrations/2024-10-31-0000-fix-snmp-monitor.js @@ -0,0 +1,7 @@ +exports.up = function (knex) { + return knex("monitor").whereNull("json_path_operator").update("json_path_operator", "=="); +}; +exports.down = function (knex) { + // changing the json_path_operator back to null for all "==" is not possible anymore + // we have lost the context which fields have been set explicitly in >= v2.0 and which would need to be reverted +}; diff --git a/db/knex_migrations/2024-11-27-1927-fix-info-json-data-type.js b/db/knex_migrations/2024-11-27-1927-fix-info-json-data-type.js new file mode 100644 index 0000000..4b46d3a --- /dev/null +++ b/db/knex_migrations/2024-11-27-1927-fix-info-json-data-type.js @@ -0,0 +1,12 @@ +// 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(); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor_tls_info", function (table) { + table.text("info_json", "text").alter(); + }); +}; diff --git a/db/knex_migrations/2025-01-01-0000-add-smtp.js b/db/knex_migrations/2025-01-01-0000-add-smtp.js new file mode 100644 index 0000000..68fa722 --- /dev/null +++ b/db/knex_migrations/2025-01-01-0000-add-smtp.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.string("smtp_security").defaultTo(null); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("smtp_security"); + }); +}; diff --git a/db/knex_migrations/2025-02-15-2312-add-wstest.js b/db/knex_migrations/2025-02-15-2312-add-wstest.js new file mode 100644 index 0000000..dd5fdc2 --- /dev/null +++ b/db/knex_migrations/2025-02-15-2312-add-wstest.js @@ -0,0 +1,14 @@ +// 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(""); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("ws_ignore_sec_websocket_accept_header"); + table.dropColumn("ws_subprotocol"); + }); +}; diff --git a/db/knex_migrations/2025-02-17-2142-generalize-analytics.js b/db/knex_migrations/2025-02-17-2142-generalize-analytics.js new file mode 100644 index 0000000..e172eb8 --- /dev/null +++ b/db/knex_migrations/2025-02-17-2142-generalize-analytics.js @@ -0,0 +1,23 @@ +// Update status_page table to generalize analytics fields +exports.up = function (knex) { + return knex.schema + .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(() => { + // After a successful migration, add google as default for previous pages + knex("status_page").whereNotNull("analytics_id").update({ + analytics_type: "google", + }); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("status_page", function (table) { + table.renameColumn("analytics_id", "google_analytics_tag_id"); + table.dropColumn("analytics_script_url"); + table.dropColumn("analytics_type"); + }); +}; diff --git a/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js b/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js new file mode 100644 index 0000000..6978194 --- /dev/null +++ b/db/knex_migrations/2025-03-04-0000-ping-advanced-options.js @@ -0,0 +1,21 @@ +/* SQL: +ALTER TABLE monitor ADD ping_count INTEGER default 1 not null; +ALTER TABLE monitor ADD ping_numeric BOOLEAN default true not null; +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(); + }); +}; + +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"); + }); +}; diff --git a/db/knex_migrations/2025-03-25-0127-fix-5721.js b/db/knex_migrations/2025-03-25-0127-fix-5721.js new file mode 100644 index 0000000..5dc11e7 --- /dev/null +++ b/db/knex_migrations/2025-03-25-0127-fix-5721.js @@ -0,0 +1,12 @@ +// 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(); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("proxy", function (table) { + table.smallint("port").alter(); + }); +}; diff --git a/db/knex_migrations/2025-05-09-0000-add-custom-url.js b/db/knex_migrations/2025-05-09-0000-add-custom-url.js new file mode 100644 index 0000000..87fd684 --- /dev/null +++ b/db/knex_migrations/2025-05-09-0000-add-custom-url.js @@ -0,0 +1,12 @@ +// 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"); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor_group", function (table) { + table.dropColumn("custom_url"); + }); +}; diff --git a/db/knex_migrations/2025-06-03-0000-add-ip-family.js b/db/knex_migrations/2025-06-03-0000-add-ip-family.js new file mode 100644 index 0000000..be0b6bd --- /dev/null +++ b/db/knex_migrations/2025-06-03-0000-add-ip-family.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + 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"); + }); +}; diff --git a/db/knex_migrations/2025-06-11-0000-add-manual-monitor.js b/db/knex_migrations/2025-06-11-0000-add-manual-monitor.js new file mode 100644 index 0000000..176dca6 --- /dev/null +++ b/db/knex_migrations/2025-06-11-0000-add-manual-monitor.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.string("manual_status").defaultTo(null); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("manual_status"); + }); +}; diff --git a/db/knex_migrations/2025-06-13-0000-maintenance-add-last-start.js b/db/knex_migrations/2025-06-13-0000-maintenance-add-last-start.js new file mode 100644 index 0000000..5e6ec0f --- /dev/null +++ b/db/knex_migrations/2025-06-13-0000-maintenance-add-last-start.js @@ -0,0 +1,33 @@ +// 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"); + }); + + // Perform migration for recurring-interval strategy + 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 hour = parseInt(hourStr, 10); + const minute = parseInt(minuteStr, 10); + + const cron = `${minute} ${hour} * * *`; + + await knex("maintenance").where({ id }).update({ cron }); + }); + await Promise.all(maintenanceUpdates); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("maintenance", function (table) { + table.dropColumn("last_start_date"); + }); +}; diff --git a/db/knex_migrations/2025-06-15-0001-manual-monitor-fix.js b/db/knex_migrations/2025-06-15-0001-manual-monitor-fix.js new file mode 100644 index 0000000..a736f36 --- /dev/null +++ b/db/knex_migrations/2025-06-15-0001-manual-monitor-fix.js @@ -0,0 +1,12 @@ +// Fix: Change manual_status column type to smallint +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.smallint("manual_status").alter(); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.string("manual_status").alter(); + }); +}; diff --git a/db/knex_migrations/2025-06-24-0000-add-audience-to-oauth.js b/db/knex_migrations/2025-06-24-0000-add-audience-to-oauth.js new file mode 100644 index 0000000..0582cc3 --- /dev/null +++ b/db/knex_migrations/2025-06-24-0000-add-audience-to-oauth.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.string("oauth_audience").nullable().defaultTo(null); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.string("oauth_audience").alter(); + }); +}; diff --git a/db/knex_migrations/2025-07-17-0000-mqtt-websocket-path.js b/db/knex_migrations/2025-07-17-0000-mqtt-websocket-path.js new file mode 100644 index 0000000..5dcc5c2 --- /dev/null +++ b/db/knex_migrations/2025-07-17-0000-mqtt-websocket-path.js @@ -0,0 +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(); + }); +}; + +exports.down = function (knex) { + // Drop column monitor.mqtt_websocket_path + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("mqtt_websocket_path"); + }); +}; diff --git a/db/knex_migrations/2025-09-02-0000-add-domain-expiry.js b/db/knex_migrations/2025-09-02-0000-add-domain-expiry.js new file mode 100644 index 0000000..b204a44 --- /dev/null +++ b/db/knex_migrations/2025-09-02-0000-add-domain-expiry.js @@ -0,0 +1,23 @@ +exports.up = function (knex) { + return knex.schema + .alterTable("monitor", function (table) { + table.boolean("domain_expiry_notification").defaultTo(1); + }) + .createTable("domain_expiry", (table) => { + table.increments("id"); + table.datetime("last_check"); + // Use VARCHAR(255) for MySQL/MariaDB compatibility with unique constraint + // Maximum domain name length is 253 characters (255 octets on the wire) + table.string("domain", 255).unique().notNullable(); + table.datetime("expiry"); + table.integer("last_expiry_notification_sent").defaultTo(null); + }); +}; + +exports.down = function (knex) { + return knex.schema + .alterTable("monitor", function (table) { + table.boolean("domain_expiry_notification").alter(); + }) + .dropTable("domain_expiry"); +}; diff --git a/db/knex_migrations/2025-10-14-0000-add-ip-family-fix.js b/db/knex_migrations/2025-10-14-0000-add-ip-family-fix.js new file mode 100644 index 0000000..7a62cbf --- /dev/null +++ b/db/knex_migrations/2025-10-14-0000-add-ip-family-fix.js @@ -0,0 +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(); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + // Rollback to boolean + table.boolean("ip_family").defaultTo(null).alter(); + }); +}; diff --git a/db/knex_migrations/2025-10-15-0000-stat-table-fix.js b/db/knex_migrations/2025-10-15-0000-stat-table-fix.js new file mode 100644 index 0000000..7c30386 --- /dev/null +++ b/db/knex_migrations/2025-10-15-0000-stat-table-fix.js @@ -0,0 +1,27 @@ +// Fix for #4315. Logically, setting it to 0 ping may not be correct, but it is better than throwing errors + +exports.up = function (knex) { + return knex.schema + .alterTable("stat_daily", function (table) { + table.integer("ping").defaultTo(0).alter(); + }) + .alterTable("stat_hourly", function (table) { + table.integer("ping").defaultTo(0).alter(); + }) + .alterTable("stat_minutely", function (table) { + table.integer("ping").defaultTo(0).alter(); + }); +}; + +exports.down = function (knex) { + return knex.schema + .alterTable("stat_daily", function (table) { + table.integer("ping").alter(); + }) + .alterTable("stat_hourly", function (table) { + table.integer("ping").alter(); + }) + .alterTable("stat_minutely", function (table) { + table.integer("ping").alter(); + }); +}; diff --git a/db/knex_migrations/2025-10-15-0001-add-monitor-response-config.js b/db/knex_migrations/2025-10-15-0001-add-monitor-response-config.js new file mode 100644 index 0000000..3901397 --- /dev/null +++ b/db/knex_migrations/2025-10-15-0001-add-monitor-response-config.js @@ -0,0 +1,15 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.boolean("save_response").notNullable().defaultTo(false); + table.boolean("save_error_response").notNullable().defaultTo(true); + table.integer("response_max_length").notNullable().defaultTo(1024); // Default 1KB + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("save_response"); + table.dropColumn("save_error_response"); + table.dropColumn("response_max_length"); + }); +}; diff --git a/db/knex_migrations/2025-10-15-0002-add-response-to-heartbeat.js b/db/knex_migrations/2025-10-15-0002-add-response-to-heartbeat.js new file mode 100644 index 0000000..6f41ce1 --- /dev/null +++ b/db/knex_migrations/2025-10-15-0002-add-response-to-heartbeat.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.alterTable("heartbeat", function (table) { + table.text("response").nullable().defaultTo(null); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("heartbeat", function (table) { + table.dropColumn("response"); + }); +}; diff --git a/db/knex_migrations/2025-10-24-0000-show-only-last-heartbeat.js b/db/knex_migrations/2025-10-24-0000-show-only-last-heartbeat.js new file mode 100644 index 0000000..848d8c7 --- /dev/null +++ b/db/knex_migrations/2025-10-24-0000-show-only-last-heartbeat.js @@ -0,0 +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); + }); +}; + +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"); + }); +}; diff --git a/db/knex_migrations/2025-10-29-0000-better-auth.js b/db/knex_migrations/2025-10-29-0000-better-auth.js new file mode 100644 index 0000000..5c4bf11 --- /dev/null +++ b/db/knex_migrations/2025-10-29-0000-better-auth.js @@ -0,0 +1,56 @@ +/* + * The schema from: https://www.better-auth.com/docs/concepts/database#core-schema + */ +exports.up = function (knex) { + return knex.schema + .createTable("better_auth_user", (t) => { + t.string("id").primary(); + t.string("name").notNullable(); + t.string("email").notNullable(); + t.boolean("emailVerified").notNullable(); + t.string("image"); + t.timestamp("createdAt").notNullable(); + t.timestamp("updatedAt").notNullable(); + }) + .createTable("better_auth_session", (t) => { + t.string("id").primary(); + t.string("userId").notNullable().references("id").inTable("better_auth_user"); + t.string("token").notNullable(); + t.timestamp("expiresAt").notNullable(); + t.string("ipAddress"); + t.string("userAgent"); + t.timestamp("createdAt").notNullable(); + t.timestamp("updatedAt").notNullable(); + }) + .createTable("better_auth_account", (t) => { + t.string("id").primary(); + t.string("userId").notNullable().references("id").inTable("better_auth_user"); + t.string("accountId").notNullable(); + t.string("providerId").notNullable(); + t.string("accessToken"); + t.string("refreshToken"); + t.timestamp("accessTokenExpiresAt"); + t.timestamp("refreshTokenExpiresAt"); + t.string("scope"); + t.string("idToken"); + t.string("password"); + t.timestamp("createdAt").notNullable(); + t.timestamp("updatedAt").notNullable(); + }) + .createTable("better_auth_verification", (t) => { + t.string("id").primary(); + t.string("identifier").notNullable(); + t.string("value").notNullable(); + t.timestamp("expiresAt").notNullable(); + t.timestamp("createdAt").notNullable(); + t.timestamp("updatedAt").notNullable(); + }); +}; + +exports.down = function (knex) { + return knex.schema + .dropTableIfExists("better_auth_verification") + .dropTableIfExists("better_auth_account") + .dropTableIfExists("better_auth_session") + .dropTableIfExists("better_auth_user"); +}; diff --git a/db/knex_migrations/2025-12-09-0000-add-system-service-monitor.js b/db/knex_migrations/2025-12-09-0000-add-system-service-monitor.js new file mode 100644 index 0000000..b5a9a51 --- /dev/null +++ b/db/knex_migrations/2025-12-09-0000-add-system-service-monitor.js @@ -0,0 +1,19 @@ +/** + * @param {import("knex").Knex} knex The Knex.js instance for database interaction. + * @returns {Promise} + */ +exports.up = async (knex) => { + await knex.schema.alterTable("monitor", (table) => { + table.string("system_service_name"); + }); +}; + +/** + * @param {import("knex").Knex} knex The Knex.js instance for database interaction. + * @returns {Promise} + */ +exports.down = async (knex) => { + await knex.schema.alterTable("monitor", (table) => { + table.dropColumn("system_service_name"); + }); +}; diff --git a/db/knex_migrations/2025-12-17-0000-add-globalping-monitor.js b/db/knex_migrations/2025-12-17-0000-add-globalping-monitor.js new file mode 100644 index 0000000..b5ce401 --- /dev/null +++ b/db/knex_migrations/2025-12-17-0000-add-globalping-monitor.js @@ -0,0 +1,17 @@ +exports.up = function (knex) { + // Add new columns + return knex.schema.alterTable("monitor", function (table) { + table.string("subtype", 10).nullable(); + table.string("location", 255).nullable(); + table.string("protocol", 20).nullable(); + }); +}; + +exports.down = function (knex) { + // Drop columns + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("subtype"); + table.dropColumn("location"); + table.dropColumn("protocol"); + }); +}; diff --git a/db/knex_migrations/2025-12-22-0121-optimize-important-indexes.js b/db/knex_migrations/2025-12-22-0121-optimize-important-indexes.js new file mode 100644 index 0000000..b54538b --- /dev/null +++ b/db/knex_migrations/2025-12-22-0121-optimize-important-indexes.js @@ -0,0 +1,37 @@ +exports.up = async function (knex) { + const isSQLite = knex.client.dialect === "sqlite3"; + + if (isSQLite) { + // For SQLite: Use partial indexes with WHERE important = 1 + // Drop existing indexes using IF EXISTS + await knex.raw("DROP INDEX IF EXISTS monitor_important_time_index"); + await knex.raw("DROP INDEX IF EXISTS heartbeat_important_index"); + + // 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(["important"], "heartbeat_important_index", { + predicate: knex.whereRaw("important = 1"), + }); + }); + } + // For MariaDB/MySQL: No changes (partial indexes not supported) +}; + +exports.down = async function (knex) { + const isSQLite = knex.client.dialect === "sqlite3"; + + if (isSQLite) { + // Restore original indexes + await knex.raw("DROP INDEX IF EXISTS monitor_important_time_index"); + 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"]); + }); + } + // For MariaDB/MySQL: No changes +}; diff --git a/db/knex_migrations/2025-12-29-0000-remove-line-notify.js b/db/knex_migrations/2025-12-29-0000-remove-line-notify.js new file mode 100644 index 0000000..2011ce8 --- /dev/null +++ b/db/knex_migrations/2025-12-29-0000-remove-line-notify.js @@ -0,0 +1,30 @@ +exports.up = async function (knex) { + const notifications = await knex("notification").select("id", "config"); + const lineNotifyIDs = []; + + for (const { id, config } of notifications) { + try { + const parsedConfig = JSON.parse(config || "{}"); + const type = typeof parsedConfig.type === "string" ? parsedConfig.type.toLowerCase() : ""; + + if (type === "linenotify" || type === "line-notify") { + lineNotifyIDs.push(id); + } + } catch (error) { + // Ignore invalid JSON blobs here; they are handled elsewhere in the app. + } + } + + if (lineNotifyIDs.length === 0) { + return; + } + + await knex.transaction(async (trx) => { + await trx("monitor_notification").whereIn("notification_id", lineNotifyIDs).del(); + await trx("notification").whereIn("id", lineNotifyIDs).del(); + }); +}; + +exports.down = async function () { + // Removal of LINE Notify configs is not reversible. +}; diff --git a/db/knex_migrations/2025-12-31-2143-add-snmp-v3-username.js b/db/knex_migrations/2025-12-31-2143-add-snmp-v3-username.js new file mode 100644 index 0000000..9304ebe --- /dev/null +++ b/db/knex_migrations/2025-12-31-2143-add-snmp-v3-username.js @@ -0,0 +1,11 @@ +exports.up = async function (knex) { + await knex.schema.alterTable("monitor", (table) => { + table.string("snmp_v3_username", 255); + }); +}; + +exports.down = async function (knex) { + await knex.schema.alterTable("monitor", (table) => { + table.dropColumn("snmp_v3_username"); + }); +}; diff --git a/db/knex_migrations/2026-01-02-0551-dns-last-result-to-text.js b/db/knex_migrations/2026-01-02-0551-dns-last-result-to-text.js new file mode 100644 index 0000000..d8435e9 --- /dev/null +++ b/db/knex_migrations/2026-01-02-0551-dns-last-result-to-text.js @@ -0,0 +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(); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.string("dns_last_result", 255).alter(); + }); +}; diff --git a/db/knex_migrations/2026-01-02-0713-gamedig-v4-to-v5.js b/db/knex_migrations/2026-01-02-0713-gamedig-v4-to-v5.js new file mode 100644 index 0000000..94bf0b5 --- /dev/null +++ b/db/knex_migrations/2026-01-02-0713-gamedig-v4-to-v5.js @@ -0,0 +1,186 @@ +// Migration to update monitor.game from GameDig v4 to v5 game IDs +// Reference: https://github.com/gamedig/node-gamedig/blob/master/MIGRATE_IDS.md + +// Lookup table mapping v4 game IDs to v5 game IDs +const gameDig4to5IdMap = { + 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", +}; + +/** + * Migrate game IDs from v4 to v5 + * @param {import("knex").Knex} knex - Knex instance + * @returns {Promise} + */ +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"); + + // Update each monitor with the new game ID if it needs migration + for (const monitor of monitors) { + const oldGameId = monitor.game; + const newGameId = gameDig4to5IdMap[oldGameId]; + + if (newGameId) { + await trx("monitor").where("id", monitor.id).update({ game: newGameId }); + } + } + }); +}; + +/** + * Revert game IDs from v5 back to v4 + * @param {import("knex").Knex} knex - Knex instance + * @returns {Promise} + */ +exports.down = async function (knex) { + // Create reverse mapping from the same LUT + 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"); + + // Revert each monitor back to the old game ID if it was migrated + for (const monitor of monitors) { + const newGameId = monitor.game; + const oldGameId = gameDig5to4IdMap[newGameId]; + + if (oldGameId) { + await trx("monitor").where("id", monitor.id).update({ game: oldGameId }); + } + } + }); +}; diff --git a/db/knex_migrations/2026-01-05-0000-add-rss-title.js b/db/knex_migrations/2026-01-05-0000-add-rss-title.js new file mode 100644 index 0000000..0d1372d --- /dev/null +++ b/db/knex_migrations/2026-01-05-0000-add-rss-title.js @@ -0,0 +1,11 @@ +exports.up = async function (knex) { + await knex.schema.alterTable("status_page", function (table) { + table.string("rss_title", 255); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("status_page", function (table) { + table.dropColumn("rss_title"); + }); +}; diff --git a/db/knex_migrations/2026-01-05-0000-add-tls-monitor.js b/db/knex_migrations/2026-01-05-0000-add-tls-monitor.js new file mode 100644 index 0000000..81d244e --- /dev/null +++ b/db/knex_migrations/2026-01-05-0000-add-tls-monitor.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.string("expected_tls_alert", 50).defaultTo(null); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("expected_tls_alert"); + }); +}; diff --git a/db/knex_migrations/2026-01-06-0000-fix-domain-expiry-column-type.js b/db/knex_migrations/2026-01-06-0000-fix-domain-expiry-column-type.js new file mode 100644 index 0000000..6728c5f --- /dev/null +++ b/db/knex_migrations/2026-01-06-0000-fix-domain-expiry-column-type.js @@ -0,0 +1,14 @@ +// Ensure domain column is VARCHAR(255) across all database types. +// This migration ensures MySQL, SQLite, and MariaDB have consistent column type, +// even if a user installed 2.1.0-beta.0 or 2.1.0-beta.1 which had TEXT type for this column. +// Maximum domain name length is 253 characters (255 octets on the wire). +// Note: The unique constraint is already present from the original migration. +exports.up = function (knex) { + return knex.schema.alterTable("domain_expiry", function (table) { + table.string("domain", 255).notNullable().alter(); + }); +}; + +exports.down = function (knex) { + // No rollback needed - keeping VARCHAR(255) is the correct state +}; diff --git a/db/knex_migrations/2026-01-10-0000-convert-float-precision.js b/db/knex_migrations/2026-01-10-0000-convert-float-precision.js new file mode 100644 index 0000000..084bf10 --- /dev/null +++ b/db/knex_migrations/2026-01-10-0000-convert-float-precision.js @@ -0,0 +1,43 @@ +exports.up = function (knex) { + return knex.schema + .alterTable("heartbeat", function (table) { + table.bigInteger("ping").alter(); + }) + .alterTable("stat_minutely", function (table) { + table.float("ping", 20, 2).notNullable().alter(); + table.float("ping_min", 20, 2).notNullable().defaultTo(0).alter(); + table.float("ping_max", 20, 2).notNullable().defaultTo(0).alter(); + }) + .alterTable("stat_daily", function (table) { + table.float("ping", 20, 2).notNullable().alter(); + table.float("ping_min", 20, 2).notNullable().defaultTo(0).alter(); + table.float("ping_max", 20, 2).notNullable().defaultTo(0).alter(); + }) + .alterTable("stat_hourly", function (table) { + table.float("ping", 20, 2).notNullable().alter(); + table.float("ping_min", 20, 2).notNullable().defaultTo(0).alter(); + table.float("ping_max", 20, 2).notNullable().defaultTo(0).alter(); + }); +}; + +exports.down = function (knex) { + return knex.schema + .alterTable("heartbeat", function (table) { + table.integer("ping").alter(); + }) + .alterTable("stat_minutely", function (table) { + table.float("ping").notNullable().alter(); + table.float("ping_min").notNullable().defaultTo(0).alter(); + table.float("ping_max").notNullable().defaultTo(0).alter(); + }) + .alterTable("stat_daily", function (table) { + table.float("ping").notNullable().alter(); + table.float("ping_min").notNullable().defaultTo(0).alter(); + table.float("ping_max").notNullable().defaultTo(0).alter(); + }) + .alterTable("stat_hourly", function (table) { + table.float("ping").notNullable().alter(); + table.float("ping_min").notNullable().defaultTo(0).alter(); + table.float("ping_max").notNullable().defaultTo(0).alter(); + }); +}; diff --git a/db/knex_migrations/2026-01-15-0000-add-json-query-retry-only-status-code.js b/db/knex_migrations/2026-01-15-0000-add-json-query-retry-only-status-code.js new file mode 100644 index 0000000..dd5f095 --- /dev/null +++ b/db/knex_migrations/2026-01-15-0000-add-json-query-retry-only-status-code.js @@ -0,0 +1,12 @@ +exports.up = function (knex) { + // Add new column to table monitor for json-query retry behavior + return knex.schema.alterTable("monitor", function (table) { + table.boolean("retry_only_on_status_code_failure").defaultTo(false).notNullable(); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("retry_only_on_status_code_failure"); + }); +}; diff --git a/db/knex_migrations/2026-01-16-0000-add-screenshot-delay.js b/db/knex_migrations/2026-01-16-0000-add-screenshot-delay.js new file mode 100644 index 0000000..fa0829b --- /dev/null +++ b/db/knex_migrations/2026-01-16-0000-add-screenshot-delay.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.integer("screenshot_delay").notNullable().unsigned().defaultTo(0); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("screenshot_delay"); + }); +}; diff --git a/db/knex_migrations/2026-02-07-0000-disable-domain-expiry-unsupported-tlds.js b/db/knex_migrations/2026-02-07-0000-disable-domain-expiry-unsupported-tlds.js new file mode 100644 index 0000000..995b8ea --- /dev/null +++ b/db/knex_migrations/2026-02-07-0000-disable-domain-expiry-unsupported-tlds.js @@ -0,0 +1,93 @@ +const { parse: parseTld } = require("tldts"); + +/* + * TODO: + * This migration file is scary, because the json file is dynamically updated. + * Problem 1: Migration files should ideally be stateless. + * Problem 2: This migration only runs once, what happens if rdp-dns.json is updated after this migration has run? + * Have to investigate later. + */ +const rdapDnsData = require("../../extra/rdap-dns.json"); + +const TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD = { + http: "url", + keyword: "url", + "json-query": "url", + "real-browser": "url", + "websocket-upgrade": "url", + port: "hostname", + ping: "hostname", + "grpc-keyword": "grpc_url", + dns: "hostname", + smtp: "hostname", + snmp: "hostname", + gamedig: "hostname", + steam: "hostname", + mqtt: "hostname", + radius: "hostname", + "tailscale-ping": "hostname", + "sip-options": "hostname", +}; + +/** + * Build set of root TLDs that have RDAP support + * @returns {Set} Set of supported root TLDs + */ +function getSupportedTlds() { + const supported = new Set(); + const services = rdapDnsData["services"] ?? []; + for (const [tlds] of services) { + for (const tld of tlds) { + supported.add(tld); + } + } + return supported; +} + +/** + * Check if a target URL/hostname has RDAP support + * @param {string} target URL or hostname + * @param {Set} supportedTlds Set of supported root TLDs + * @returns {boolean} Whether the target's TLD has RDAP support + */ +function hasRdapSupport(target, supportedTlds) { + if (!target || typeof target !== "string") { + return false; + } + const tld = parseTld(target); + if (!tld.publicSuffix || !tld.isIcann) { + return false; + } + const rootTld = tld.publicSuffix.split(".").pop(); + return supportedTlds.has(rootTld); +} + +exports.up = async function (knex) { + const supportedTlds = getSupportedTlds(); + + const monitors = await knex("monitor") + .where("domain_expiry_notification", 1) + .select("id", "type", "url", "hostname", "grpc_url"); + + const idsToDisable = []; + for (const monitor of monitors) { + const targetField = TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD[monitor.type]; + if (!targetField || !hasRdapSupport(monitor[targetField], supportedTlds)) { + idsToDisable.push(monitor.id); + } + } + + if (idsToDisable.length > 0) { + await knex("monitor").whereIn("id", idsToDisable).update("domain_expiry_notification", 0); + } + + await knex.schema.alterTable("monitor", function (table) { + table.boolean("domain_expiry_notification").defaultTo(0).alter(); + }); +}; + +exports.down = async function (knex) { + await knex.schema.alterTable("monitor", function (table) { + table.boolean("domain_expiry_notification").defaultTo(1).alter(); + }); +}; diff --git a/db/knex_migrations/2026-03-27-0000-add-ntp-monitor.js b/db/knex_migrations/2026-03-27-0000-add-ntp-monitor.js new file mode 100644 index 0000000..81899dc --- /dev/null +++ b/db/knex_migrations/2026-03-27-0000-add-ntp-monitor.js @@ -0,0 +1,15 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.integer("ntp_stratum_threshold").defaultTo(5); + table.integer("ntp_time_offset_threshold").defaultTo(1000); + table.integer("ntp_root_dispersion_threshold").defaultTo(500); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("ntp_stratum_threshold"); + table.dropColumn("ntp_time_offset_threshold"); + table.dropColumn("ntp_root_dispersion_threshold"); + }); +}; diff --git a/db/knex_migrations/2026-05-20-0000-add-bearer-token.js b/db/knex_migrations/2026-05-20-0000-add-bearer-token.js new file mode 100644 index 0000000..77a2a5f --- /dev/null +++ b/db/knex_migrations/2026-05-20-0000-add-bearer-token.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.text("bearer_token").defaultTo(null); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("bearer_token"); + }); +}; diff --git a/db/knex_migrations/2026-05-25-0000-add-gamedig-token.js b/db/knex_migrations/2026-05-25-0000-add-gamedig-token.js new file mode 100644 index 0000000..7717250 --- /dev/null +++ b/db/knex_migrations/2026-05-25-0000-add-gamedig-token.js @@ -0,0 +1,11 @@ +exports.up = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.text("gamedig_token").defaultTo(null); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("monitor", function (table) { + table.dropColumn("gamedig_token"); + }); +}; diff --git a/db/knex_migrations/2026-05-26-0000-better-auth-username.js b/db/knex_migrations/2026-05-26-0000-better-auth-username.js new file mode 100644 index 0000000..957d3da --- /dev/null +++ b/db/knex_migrations/2026-05-26-0000-better-auth-username.js @@ -0,0 +1,16 @@ +/* + * The schema from: https://better-auth.com/docs/plugins/username#schema + */ +exports.up = function (knex) { + return knex.schema.table("better_auth_user", (t) => { + t.string("username").unique(); + t.string("displayUsername"); + }); +}; + +exports.down = function (knex) { + return knex.schema.table("better_auth_user", (t) => { + t.dropColumn("username"); + t.dropColumn("displayUsername"); + }); +}; diff --git a/db/knex_migrations/2026-05-26-0010-better-auth-admin.js b/db/knex_migrations/2026-05-26-0010-better-auth-admin.js new file mode 100644 index 0000000..ae28b6f --- /dev/null +++ b/db/knex_migrations/2026-05-26-0010-better-auth-admin.js @@ -0,0 +1,28 @@ +/* + * The schema from: https://better-auth.com/docs/plugins/admin#schema + */ +exports.up = function (knex) { + return knex.schema + .table("better_auth_user", function (table) { + table.text("role"); + table.boolean("banned"); + table.text("banReason"); + table.timestamp("banExpires"); + }) + .table("better_auth_session", function (table) { + table.text("impersonatedBy"); + }); +}; + +exports.down = function (knex) { + return knex.schema + .table("better_auth_user", function (table) { + table.dropColumn("role"); + table.dropColumn("banned"); + table.dropColumn("banReason"); + table.dropColumn("banExpires"); + }) + .table("better_auth_session", function (table) { + table.dropColumn("impersonatedBy"); + }); +}; diff --git a/db/knex_migrations/2026-05-28-0010-better-auth-foreign-key.js b/db/knex_migrations/2026-05-28-0010-better-auth-foreign-key.js new file mode 100644 index 0000000..ef44233 --- /dev/null +++ b/db/knex_migrations/2026-05-28-0010-better-auth-foreign-key.js @@ -0,0 +1,83 @@ +const tables = [ + { + name: "docker_host", + onDelete: "SET NULL", + }, + { + name: "proxy", + onDelete: "SET NULL", + }, + { + name: "monitor", + onDelete: "SET NULL", + }, + { + name: "maintenance", + onDelete: "SET NULL", + }, + { + name: "notification", + onDelete: "SET NULL", + }, + { + name: "api_key", + onDelete: "CASCADE", + }, + { + name: "remote_browser", + onDelete: "SET NULL", + }, +]; + +exports.up = async function (knex) { + for (const table of tables) { + try { + await knex.schema.alterTable(table.name, (t) => { + t.dropForeign("user_id"); + }); + } catch (error) { + // Not sure why it only happens on Ubuntu 22.04 + Node.js 26 + // Error: Can't DROP 'docker_host_user_id_foreign'; check that column/key exists + console.error(`Error dropping foreign key for table ${table.name}:`, error); + + const columns = await knex(table.name).columnInfo(); + console.error(`Schema for ${table.name}:`, JSON.stringify(columns, null, 2)); + + // Try to ignore it, because the foreign key not existing, we can just drop user_id + // throw error; + } + + await knex.schema.alterTable(table.name, (t) => { + t.dropColumn("user_id"); + }); + + await knex.schema.alterTable(table.name, (t) => { + t.string("user_id", 255).nullable(); + }); + + await knex.schema.alterTable(table.name, (t) => { + t.foreign("user_id") + .references("id") + .inTable("better_auth_user") + .onDelete(table.onDelete) + .onUpdate("CASCADE"); + }); + } +}; + +exports.down = async function (knex) { + for (const table of tables) { + await knex.schema.alterTable(table.name, (t) => { + t.dropForeign("user_id"); + t.dropColumn("user_id"); + }); + + await knex.schema.alterTable(table.name, (t) => { + t.integer("user_id").unsigned().nullable(); + }); + + await knex.schema.alterTable(table.name, (t) => { + t.foreign("user_id").references("id").inTable("user").onDelete(table.onDelete).onUpdate("CASCADE"); + }); + } +}; diff --git a/db/knex_migrations/2026-06-08-0000-better-auth-2fa.js b/db/knex_migrations/2026-06-08-0000-better-auth-2fa.js new file mode 100644 index 0000000..c05f397 --- /dev/null +++ b/db/knex_migrations/2026-06-08-0000-better-auth-2fa.js @@ -0,0 +1,27 @@ +/* + * The schema from: https://better-auth.com/docs/plugins/2fa#schema + */ +exports.up = function (knex) { + return knex.schema + .createTable("better_auth_twoFactor", (t) => { + t.string("id").primary(); + t.string("secret").notNullable(); + t.string("backupCodes").notNullable(); + t.boolean("verified").notNullable(); + t.string("userId") + .notNullable() + .references("id") + .inTable("better_auth_user") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + }) + .table("better_auth_user", (t) => { + t.boolean("twoFactorEnabled"); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists("better_auth_twoFactor").table("better_auth_user", (t) => { + t.dropColumn("twoFactorEnabled"); + }); +}; diff --git a/db/knex_migrations/2026-06-19-1411-add-rybbit-analytics-type.js b/db/knex_migrations/2026-06-19-1411-add-rybbit-analytics-type.js new file mode 100644 index 0000000..086cd36 --- /dev/null +++ b/db/knex_migrations/2026-06-19-1411-add-rybbit-analytics-type.js @@ -0,0 +1,9 @@ +exports.up = function (knex) { + // Intentionally left blank - no operation + return Promise.resolve(); +}; + +exports.down = async function (knex) { + // Intentionally left blank - no operation + return Promise.resolve(); +}; diff --git a/db/knex_migrations/2026-06-19-1412-analytics-type-to-string.js b/db/knex_migrations/2026-06-19-1412-analytics-type-to-string.js new file mode 100644 index 0000000..45b27ad --- /dev/null +++ b/db/knex_migrations/2026-06-19-1412-analytics-type-to-string.js @@ -0,0 +1,18 @@ +const oldValues = ["google", "umami", "plausible", "matomo", "rybbit"]; + +exports.up = function (knex) { + return knex.schema.alterTable("status_page", function (table) { + table.string("analytics_type").nullable().defaultTo(null).alter(); + }); +}; + +exports.down = async function (knex) { + await knex("status_page") + .whereNotNull("analytics_type") + .whereNotIn("analytics_type", oldValues) + .update({ analytics_type: null }); + + return knex.schema.alterTable("status_page", function (table) { + table.enu("analytics_type", oldValues).nullable().defaultTo(null).alter(); + }); +}; diff --git a/db/knex_migrations/2026-06-29-0000-better-auth-api-key.js b/db/knex_migrations/2026-06-29-0000-better-auth-api-key.js new file mode 100644 index 0000000..ffde849 --- /dev/null +++ b/db/knex_migrations/2026-06-29-0000-better-auth-api-key.js @@ -0,0 +1,40 @@ +/* + * The api key schema from: https://www.better-auth.com/docs/plugins/api-key/reference#schema + * Plugin: @better-auth/api-key (modelName: better_auth_apikey) + */ +exports.up = function (knex) { + return knex.schema.createTable("better_auth_apikey", (t) => { + t.string("id").primary(); + t.string("configId").notNullable().defaultTo("default").index(); + t.string("name"); + t.string("start"); + t.string("prefix"); + t.string("key").notNullable().index(); + t.string("referenceId") + .notNullable() + .index() + .references("id") + .inTable("better_auth_user") + .onDelete("CASCADE") + .onUpdate("CASCADE"); + t.integer("refillInterval"); + t.integer("refillAmount"); + t.timestamp("lastRefillAt"); + t.boolean("enabled").defaultTo(true); + t.boolean("rateLimitEnabled").defaultTo(true); + t.bigInteger("rateLimitTimeWindow"); + t.integer("rateLimitMax"); + t.integer("requestCount").defaultTo(0); + t.integer("remaining"); + t.timestamp("lastRequest"); + t.timestamp("expiresAt"); + t.timestamp("createdAt").notNullable(); + t.timestamp("updatedAt").notNullable(); + t.text("permissions"); + t.text("metadata"); + }); +}; + +exports.down = function (knex) { + return knex.schema.dropTableIfExists("better_auth_apikey"); +}; diff --git a/db/knex_migrations/2026-07-22-0000-fix-stat-daily-overflow.js b/db/knex_migrations/2026-07-22-0000-fix-stat-daily-overflow.js new file mode 100644 index 0000000..b8f8070 --- /dev/null +++ b/db/knex_migrations/2026-07-22-0000-fix-stat-daily-overflow.js @@ -0,0 +1,13 @@ +exports.up = function (knex) { + return knex.schema.alterTable("stat_daily", function (table) { + table.integer("up").unsigned().notNullable().alter(); + table.integer("down").unsigned().notNullable().alter(); + }); +}; + +exports.down = function (knex) { + return knex.schema.alterTable("stat_daily", function (table) { + table.smallint("up").notNullable().alter(); + table.smallint("down").notNullable().alter(); + }); +}; diff --git a/db/knex_migrations/2026-08-07-0000-add-sftp-monitor.js b/db/knex_migrations/2026-08-07-0000-add-sftp-monitor.js new file mode 100644 index 0000000..53f2b52 --- /dev/null +++ b/db/knex_migrations/2026-08-07-0000-add-sftp-monitor.js @@ -0,0 +1,21 @@ +exports.up = async (knex) => { + await knex.schema.alterTable("monitor", (table) => { + table.string("ssh_username"); + table.string("ssh_password"); + table.string("sftp_path"); + table.text("ssh_private_key"); + table.string("ssh_passphrase"); + table.string("ssh_auth_method").defaultTo("password"); + }); +}; + +exports.down = async (knex) => { + await knex.schema.alterTable("monitor", (table) => { + table.dropColumn("ssh_username"); + table.dropColumn("ssh_password"); + table.dropColumn("sftp_path"); + table.dropColumn("ssh_private_key"); + table.dropColumn("ssh_passphrase"); + table.dropColumn("ssh_auth_method"); + }); +}; diff --git a/db/knex_migrations/2026-08-18-0000-sqlite-only-drop-analytics-type-check.js b/db/knex_migrations/2026-08-18-0000-sqlite-only-drop-analytics-type-check.js new file mode 100644 index 0000000..9f5f416 --- /dev/null +++ b/db/knex_migrations/2026-08-18-0000-sqlite-only-drop-analytics-type-check.js @@ -0,0 +1,46 @@ +// Fix #7727, force recreate analytics_type the column on SQLite to remove the enum check +// Because alter to string won't remove the enum the check (so stupid....) + +exports.up = async function (knex) { + if (knex.client.dialect === "sqlite3") { + // Save all analytics_type to memory + const rows = await knex("status_page").select("id", "analytics_type"); + + await knex.schema.alterTable("status_page", function (table) { + table.dropColumn("analytics_type"); + }); + + await knex.schema.alterTable("status_page", function (table) { + table.string("analytics_type").nullable().defaultTo(null); + }); + + for (const row of rows) { + if (row.analytics_type !== null) { + await knex("status_page").where({ id: row.id }).update({ analytics_type: row.analytics_type }); + } + } + } +}; + +exports.down = async function (knex) { + if (knex.client.dialect === "sqlite3") { + const rows = await knex("status_page").select("id", "analytics_type"); + + await knex.schema.alterTable("status_page", function (table) { + table.dropColumn("analytics_type"); + }); + + await knex.schema.alterTable("status_page", function (table) { + table + .enu("analytics_type", ["google", "umami", "plausible", "matomo", "rybbit"]) + .nullable() + .defaultTo(null); + }); + + for (const row of rows) { + if (row.analytics_type !== null) { + await knex("status_page").where({ id: row.id }).update({ analytics_type: row.analytics_type }); + } + } + } +}; diff --git a/db/knex_migrations/README.md b/db/knex_migrations/README.md new file mode 100644 index 0000000..a80ee71 --- /dev/null +++ b/db/knex_migrations/README.md @@ -0,0 +1,51 @@ +# Info + +https://knexjs.org/guide/migrations.html#knexfile-in-other-languages + +## Basic rules + +- All tables must have a primary key named `id` +- Filename format: `YYYY-MM-DD-HHMM-patch-name.js` +- Avoid native SQL syntax, use knex methods, because Uptime Kuma supports SQLite and MariaDB. + +## Template + +```js +exports.up = function (knex) {}; + +exports.down = function (knex) {}; + +// exports.config = { transaction: false }; +``` + +## Example + +Filename: 2023-06-30-1348-create-user-and-product.js + +```js +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("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"); +}; +``` + +https://knexjs.org/guide/migrations.html#transactions-in-migrations diff --git a/db/old_migrations/README.md b/db/old_migrations/README.md new file mode 100644 index 0000000..3b2bd96 --- /dev/null +++ b/db/old_migrations/README.md @@ -0,0 +1,3 @@ +# Don't create a new migration file here + +Please go to ./db/knex_migrations/README.md diff --git a/db/old_migrations/patch-2fa-invalidate-used-token.sql b/db/old_migrations/patch-2fa-invalidate-used-token.sql new file mode 100644 index 0000000..2f0b42c --- /dev/null +++ b/db/old_migrations/patch-2fa-invalidate-used-token.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE user + ADD twofa_last_token VARCHAR(6); + +COMMIT; diff --git a/db/old_migrations/patch-2fa.sql b/db/old_migrations/patch-2fa.sql new file mode 100644 index 0000000..35069d8 --- /dev/null +++ b/db/old_migrations/patch-2fa.sql @@ -0,0 +1,10 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE user + ADD twofa_secret VARCHAR(64); + +ALTER TABLE user + ADD twofa_status BOOLEAN default 0 NOT NULL; + +COMMIT; diff --git a/db/old_migrations/patch-add-certificate-expiry-status-page.sql b/db/old_migrations/patch-add-certificate-expiry-status-page.sql new file mode 100644 index 0000000..63a2010 --- /dev/null +++ b/db/old_migrations/patch-add-certificate-expiry-status-page.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE status_page + ADD show_certificate_expiry BOOLEAN default 0 NOT NULL; + +COMMIT; diff --git a/db/old_migrations/patch-add-clickable-status-page-link.sql b/db/old_migrations/patch-add-clickable-status-page-link.sql new file mode 100644 index 0000000..cd11cdd --- /dev/null +++ b/db/old_migrations/patch-add-clickable-status-page-link.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor_group + ADD send_url BOOLEAN DEFAULT 0 NOT NULL; + +COMMIT; diff --git a/db/old_migrations/patch-add-description-monitor.sql b/db/old_migrations/patch-add-description-monitor.sql new file mode 100644 index 0000000..da1aa55 --- /dev/null +++ b/db/old_migrations/patch-add-description-monitor.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD description TEXT default null; + +COMMIT; diff --git a/db/old_migrations/patch-add-docker-columns.sql b/db/old_migrations/patch-add-docker-columns.sql new file mode 100644 index 0000000..4cea448 --- /dev/null +++ b/db/old_migrations/patch-add-docker-columns.sql @@ -0,0 +1,18 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +CREATE TABLE docker_host ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + user_id INT NOT NULL, + docker_daemon VARCHAR(255), + docker_type VARCHAR(255), + name VARCHAR(255) +); + +ALTER TABLE monitor + ADD docker_host INTEGER REFERENCES docker_host(id); + +ALTER TABLE monitor + ADD docker_container VARCHAR(255); + +COMMIT; diff --git a/db/old_migrations/patch-add-gamedig-given-port.sql b/db/old_migrations/patch-add-gamedig-given-port.sql new file mode 100644 index 0000000..897a9c7 --- /dev/null +++ b/db/old_migrations/patch-add-gamedig-given-port.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD gamedig_given_port_only BOOLEAN default 1 not null; + +COMMIT; diff --git a/db/old_migrations/patch-add-gamedig-monitor.sql b/db/old_migrations/patch-add-gamedig-monitor.sql new file mode 100644 index 0000000..e20a0cd --- /dev/null +++ b/db/old_migrations/patch-add-gamedig-monitor.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + + ALTER TABLE monitor + ADD game VARCHAR(255); + +COMMIT; diff --git a/db/old_migrations/patch-add-google-analytics-status-page-tag.sql b/db/old_migrations/patch-add-google-analytics-status-page-tag.sql new file mode 100644 index 0000000..fe6fa34 --- /dev/null +++ b/db/old_migrations/patch-add-google-analytics-status-page-tag.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE status_page + ADD google_analytics_tag_id VARCHAR; + +COMMIT; diff --git a/db/old_migrations/patch-add-invert-keyword.sql b/db/old_migrations/patch-add-invert-keyword.sql new file mode 100644 index 0000000..8ca199e --- /dev/null +++ b/db/old_migrations/patch-add-invert-keyword.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD invert_keyword BOOLEAN default 0 not null; + +COMMIT; diff --git a/db/old_migrations/patch-add-other-auth.sql b/db/old_migrations/patch-add-other-auth.sql new file mode 100644 index 0000000..b83f1ee --- /dev/null +++ b/db/old_migrations/patch-add-other-auth.sql @@ -0,0 +1,18 @@ +BEGIN TRANSACTION; + + ALTER TABLE monitor + ADD auth_method VARCHAR(250); + + ALTER TABLE monitor + ADD auth_domain TEXT; + ALTER TABLE monitor + + ADD auth_workstation TEXT; + +COMMIT; + +BEGIN TRANSACTION; + UPDATE monitor + SET auth_method = 'basic' + WHERE basic_auth_user is not null; +COMMIT; diff --git a/db/old_migrations/patch-add-parent-monitor.sql b/db/old_migrations/patch-add-parent-monitor.sql new file mode 100644 index 0000000..ac2697f --- /dev/null +++ b/db/old_migrations/patch-add-parent-monitor.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD parent INTEGER REFERENCES [monitor] ([id]) ON DELETE SET NULL ON UPDATE CASCADE; + +COMMIT; diff --git a/db/old_migrations/patch-add-radius-monitor.sql b/db/old_migrations/patch-add-radius-monitor.sql new file mode 100644 index 0000000..4625f21 --- /dev/null +++ b/db/old_migrations/patch-add-radius-monitor.sql @@ -0,0 +1,19 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD radius_username VARCHAR(255); + +ALTER TABLE monitor + ADD radius_password VARCHAR(255); + +ALTER TABLE monitor + ADD radius_calling_station_id VARCHAR(50); + +ALTER TABLE monitor + ADD radius_called_station_id VARCHAR(50); + +ALTER TABLE monitor + ADD radius_secret VARCHAR(255); + +COMMIT; diff --git a/db/old_migrations/patch-add-retry-interval-monitor.sql b/db/old_migrations/patch-add-retry-interval-monitor.sql new file mode 100644 index 0000000..adb6462 --- /dev/null +++ b/db/old_migrations/patch-add-retry-interval-monitor.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD retry_interval INTEGER default 0 not null; + +COMMIT; \ No newline at end of file diff --git a/db/old_migrations/patch-add-sqlserver-monitor.sql b/db/old_migrations/patch-add-sqlserver-monitor.sql new file mode 100644 index 0000000..f253dd2 --- /dev/null +++ b/db/old_migrations/patch-add-sqlserver-monitor.sql @@ -0,0 +1,10 @@ +BEGIN TRANSACTION; + + ALTER TABLE monitor + ADD database_connection_string VARCHAR(2000); + + ALTER TABLE monitor + ADD database_query TEXT; + + + COMMIT diff --git a/db/old_migrations/patch-add-timeout-monitor.sql b/db/old_migrations/patch-add-timeout-monitor.sql new file mode 100644 index 0000000..b62eb14 --- /dev/null +++ b/db/old_migrations/patch-add-timeout-monitor.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD timeout DOUBLE default 0 not null; + +COMMIT; diff --git a/db/old_migrations/patch-added-json-query.sql b/db/old_migrations/patch-added-json-query.sql new file mode 100644 index 0000000..d5b7f1a --- /dev/null +++ b/db/old_migrations/patch-added-json-query.sql @@ -0,0 +1,10 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD json_path TEXT; + +ALTER TABLE monitor + ADD expected_value VARCHAR(255); + +COMMIT; diff --git a/db/old_migrations/patch-added-kafka-producer.sql b/db/old_migrations/patch-added-kafka-producer.sql new file mode 100644 index 0000000..933d30b --- /dev/null +++ b/db/old_migrations/patch-added-kafka-producer.sql @@ -0,0 +1,22 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD kafka_producer_topic VARCHAR(255); + +ALTER TABLE monitor + ADD kafka_producer_brokers TEXT; + +ALTER TABLE monitor + ADD kafka_producer_ssl INTEGER; + +ALTER TABLE monitor + ADD kafka_producer_allow_auto_topic_creation VARCHAR(255); + +ALTER TABLE monitor + ADD kafka_producer_sasl_options TEXT; + +ALTER TABLE monitor + ADD kafka_producer_message TEXT; + +COMMIT; diff --git a/db/old_migrations/patch-added-mqtt-monitor.sql b/db/old_migrations/patch-added-mqtt-monitor.sql new file mode 100644 index 0000000..02b0b09 --- /dev/null +++ b/db/old_migrations/patch-added-mqtt-monitor.sql @@ -0,0 +1,16 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD mqtt_topic TEXT; + +ALTER TABLE monitor + ADD mqtt_success_message VARCHAR(255); + +ALTER TABLE monitor + ADD mqtt_username VARCHAR(255); + +ALTER TABLE monitor + ADD mqtt_password VARCHAR(255); + +COMMIT; diff --git a/db/old_migrations/patch-api-key-table.sql b/db/old_migrations/patch-api-key-table.sql new file mode 100644 index 0000000..4116db3 --- /dev/null +++ b/db/old_migrations/patch-api-key-table.sql @@ -0,0 +1,15 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +CREATE TABLE [api_key] ( + [id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + [key] VARCHAR(255) NOT NULL, + [name] VARCHAR(255) NOT NULL, + [user_id] INTEGER NOT NULL, + [created_date] DATETIME DEFAULT (DATETIME('now')) NOT NULL, + [active] BOOLEAN DEFAULT 1 NOT NULL, + [expires] DATETIME DEFAULT NULL, + CONSTRAINT FK_user FOREIGN KEY ([user_id]) REFERENCES [user]([id]) ON DELETE CASCADE ON UPDATE CASCADE +); + +COMMIT; diff --git a/db/old_migrations/patch-fix-kafka-producer-booleans.sql b/db/old_migrations/patch-fix-kafka-producer-booleans.sql new file mode 100644 index 0000000..be2e992 --- /dev/null +++ b/db/old_migrations/patch-fix-kafka-producer-booleans.sql @@ -0,0 +1,34 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +-- Rename COLUMNs to another one (suffixed by `_old`) +ALTER TABLE monitor + RENAME COLUMN kafka_producer_ssl TO kafka_producer_ssl_old; + +ALTER TABLE monitor + RENAME COLUMN kafka_producer_allow_auto_topic_creation TO kafka_producer_allow_auto_topic_creation_old; + +-- Add correct COLUMNs +ALTER TABLE monitor + ADD COLUMN kafka_producer_ssl BOOLEAN default 0 NOT NULL; + +ALTER TABLE monitor + ADD COLUMN kafka_producer_allow_auto_topic_creation BOOLEAN default 0 NOT NULL; + +-- These SQL is still not fully safe. See https://github.com/louislam/uptime-kuma/issues/4039. + +-- Set bring old values from `_old` COLUMNs to correct ones +-- UPDATE monitor SET kafka_producer_allow_auto_topic_creation = monitor.kafka_producer_allow_auto_topic_creation_old +-- WHERE monitor.kafka_producer_allow_auto_topic_creation_old IS NOT NULL; + +-- UPDATE monitor SET kafka_producer_ssl = monitor.kafka_producer_ssl_old +-- WHERE monitor.kafka_producer_ssl_old IS NOT NULL; + +-- Remove old COLUMNs +ALTER TABLE monitor + DROP COLUMN kafka_producer_allow_auto_topic_creation_old; + +ALTER TABLE monitor + DROP COLUMN kafka_producer_ssl_old; + +COMMIT; diff --git a/db/old_migrations/patch-group-table.sql b/db/old_migrations/patch-group-table.sql new file mode 100644 index 0000000..1c6f366 --- /dev/null +++ b/db/old_migrations/patch-group-table.sql @@ -0,0 +1,30 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +create table `group` +( + id INTEGER not null + constraint group_pk + primary key autoincrement, + name VARCHAR(255) not null, + created_date DATETIME default (DATETIME('now')) not null, + public BOOLEAN default 0 not null, + active BOOLEAN default 1 not null, + weight BOOLEAN NOT NULL DEFAULT 1000 +); + +CREATE TABLE [monitor_group] +( + [id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + [monitor_id] INTEGER NOT NULL REFERENCES [monitor] ([id]) ON DELETE CASCADE ON UPDATE CASCADE, + [group_id] INTEGER NOT NULL REFERENCES [group] ([id]) ON DELETE CASCADE ON UPDATE CASCADE, + weight BOOLEAN NOT NULL DEFAULT 1000 +); + +CREATE INDEX [fk] + ON [monitor_group] ( + [monitor_id], + [group_id]); + + +COMMIT; diff --git a/db/old_migrations/patch-grpc-monitor.sql b/db/old_migrations/patch-grpc-monitor.sql new file mode 100644 index 0000000..bac024e --- /dev/null +++ b/db/old_migrations/patch-grpc-monitor.sql @@ -0,0 +1,25 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD grpc_url VARCHAR(255) default null; + +ALTER TABLE monitor + ADD grpc_protobuf TEXT default null; + +ALTER TABLE monitor + ADD grpc_body TEXT default null; + +ALTER TABLE monitor + ADD grpc_metadata TEXT default null; + +ALTER TABLE monitor + ADD grpc_method VARCHAR(255) default null; + +ALTER TABLE monitor + ADD grpc_service_name VARCHAR(255) default null; + +ALTER TABLE monitor + ADD grpc_enable_tls BOOLEAN default 0 not null; + +COMMIT; diff --git a/db/old_migrations/patch-http-body-encoding.sql b/db/old_migrations/patch-http-body-encoding.sql new file mode 100644 index 0000000..322c8b8 --- /dev/null +++ b/db/old_migrations/patch-http-body-encoding.sql @@ -0,0 +1,12 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor ADD http_body_encoding VARCHAR(25); + +COMMIT; + +BEGIN TRANSACTION; + +UPDATE monitor SET http_body_encoding = 'json' WHERE (type = 'http' or type = 'keyword') AND http_body_encoding IS NULL; + +COMMIT; diff --git a/db/old_migrations/patch-http-monitor-method-body-and-headers.sql b/db/old_migrations/patch-http-monitor-method-body-and-headers.sql new file mode 100644 index 0000000..dc2526b --- /dev/null +++ b/db/old_migrations/patch-http-monitor-method-body-and-headers.sql @@ -0,0 +1,13 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD method TEXT default 'GET' not null; + +ALTER TABLE monitor + ADD body TEXT default null; + +ALTER TABLE monitor + ADD headers TEXT default null; + +COMMIT; diff --git a/db/old_migrations/patch-improve-performance.sql b/db/old_migrations/patch-improve-performance.sql new file mode 100644 index 0000000..c07d629 --- /dev/null +++ b/db/old_migrations/patch-improve-performance.sql @@ -0,0 +1,10 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +-- For sendHeartbeatList +CREATE INDEX monitor_time_index ON heartbeat (monitor_id, time); + +-- For sendImportantHeartbeatList +CREATE INDEX monitor_important_time_index ON heartbeat (monitor_id, important,time); + +COMMIT; diff --git a/db/old_migrations/patch-incident-table.sql b/db/old_migrations/patch-incident-table.sql new file mode 100644 index 0000000..531cfb3 --- /dev/null +++ b/db/old_migrations/patch-incident-table.sql @@ -0,0 +1,18 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +create table incident +( + id INTEGER not null + constraint incident_pk + primary key autoincrement, + title VARCHAR(255) not null, + content TEXT not null, + style VARCHAR(30) default 'warning' not null, + created_date DATETIME default (DATETIME('now')) not null, + last_updated_date DATETIME, + pin BOOLEAN default 1 not null, + active BOOLEAN default 1 not null +); + +COMMIT; diff --git a/db/old_migrations/patch-maintenance-cron.sql b/db/old_migrations/patch-maintenance-cron.sql new file mode 100644 index 0000000..bc51b88 --- /dev/null +++ b/db/old_migrations/patch-maintenance-cron.sql @@ -0,0 +1,11 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +DROP TABLE maintenance_timeslot; + +-- 999 characters. https://stackoverflow.com/questions/46134830/maximum-length-for-cron-job +ALTER TABLE maintenance ADD cron TEXT; +ALTER TABLE maintenance ADD timezone VARCHAR(255); +ALTER TABLE maintenance ADD duration INTEGER; + +COMMIT; diff --git a/db/old_migrations/patch-maintenance-table2.sql b/db/old_migrations/patch-maintenance-table2.sql new file mode 100644 index 0000000..96b2ebd --- /dev/null +++ b/db/old_migrations/patch-maintenance-table2.sql @@ -0,0 +1,83 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +-- Just for someone who tested maintenance before (patch-maintenance-table.sql) +DROP TABLE IF EXISTS maintenance_status_page; +DROP TABLE IF EXISTS monitor_maintenance; +DROP TABLE IF EXISTS maintenance; +DROP TABLE IF EXISTS maintenance_timeslot; + +-- maintenance +CREATE TABLE [maintenance] ( + [id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + [title] VARCHAR(150) NOT NULL, + [description] TEXT NOT NULL, + [user_id] INTEGER REFERENCES [user]([id]) ON DELETE SET NULL ON UPDATE CASCADE, + [active] BOOLEAN NOT NULL DEFAULT 1, + [strategy] VARCHAR(50) NOT NULL DEFAULT 'single', + [start_date] DATETIME, + [end_date] DATETIME, + [start_time] TIME, + [end_time] TIME, + [weekdays] VARCHAR2(250) DEFAULT '[]', + [days_of_month] TEXT DEFAULT '[]', + [interval_day] INTEGER +); + +CREATE INDEX [manual_active] ON [maintenance] ( + [strategy], + [active] +); + +CREATE INDEX [active] ON [maintenance] ([active]); + +CREATE INDEX [maintenance_user_id] ON [maintenance] ([user_id]); + +-- maintenance_status_page +CREATE TABLE maintenance_status_page ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + status_page_id INTEGER NOT NULL, + maintenance_id INTEGER NOT NULL, + CONSTRAINT FK_maintenance FOREIGN KEY (maintenance_id) REFERENCES maintenance (id) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT FK_status_page FOREIGN KEY (status_page_id) REFERENCES status_page (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE INDEX [status_page_id_index] + ON [maintenance_status_page]([status_page_id]); + +CREATE INDEX [maintenance_id_index] + ON [maintenance_status_page]([maintenance_id]); + +-- maintenance_timeslot +CREATE TABLE [maintenance_timeslot] ( + [id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + [maintenance_id] INTEGER NOT NULL CONSTRAINT [FK_maintenance] REFERENCES [maintenance]([id]) ON DELETE CASCADE ON UPDATE CASCADE, + [start_date] DATETIME NOT NULL, + [end_date] DATETIME, + [generated_next] BOOLEAN DEFAULT 0 +); + +CREATE INDEX [maintenance_id] ON [maintenance_timeslot] ([maintenance_id] DESC); + +CREATE INDEX [active_timeslot_index] ON [maintenance_timeslot] ( + [maintenance_id] DESC, + [start_date] DESC, + [end_date] DESC +); + +CREATE INDEX [generated_next_index] ON [maintenance_timeslot] ([generated_next]); + +-- monitor_maintenance +CREATE TABLE monitor_maintenance ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + monitor_id INTEGER NOT NULL, + maintenance_id INTEGER NOT NULL, + CONSTRAINT FK_maintenance FOREIGN KEY (maintenance_id) REFERENCES maintenance (id) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT FK_monitor FOREIGN KEY (monitor_id) REFERENCES monitor (id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE INDEX [maintenance_id_index2] ON [monitor_maintenance]([maintenance_id]); + +CREATE INDEX [monitor_id_index] ON [monitor_maintenance]([monitor_id]); + +COMMIT; diff --git a/db/old_migrations/patch-monitor-add-resend-interval.sql b/db/old_migrations/patch-monitor-add-resend-interval.sql new file mode 100644 index 0000000..8e28bf6 --- /dev/null +++ b/db/old_migrations/patch-monitor-add-resend-interval.sql @@ -0,0 +1,10 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD resend_interval INTEGER default 0 not null; + +ALTER TABLE heartbeat + ADD down_count INTEGER default 0 not null; + +COMMIT; diff --git a/db/old_migrations/patch-monitor-basic-auth.sql b/db/old_migrations/patch-monitor-basic-auth.sql new file mode 100644 index 0000000..de4bdef --- /dev/null +++ b/db/old_migrations/patch-monitor-basic-auth.sql @@ -0,0 +1,10 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD basic_auth_user TEXT default null; + +ALTER TABLE monitor + ADD basic_auth_pass TEXT default null; + +COMMIT; diff --git a/db/old_migrations/patch-monitor-expiry-notification.sql b/db/old_migrations/patch-monitor-expiry-notification.sql new file mode 100644 index 0000000..7a33001 --- /dev/null +++ b/db/old_migrations/patch-monitor-expiry-notification.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD expiry_notification BOOLEAN default 1; + +COMMIT; diff --git a/db/old_migrations/patch-monitor-oauth-cc.sql b/db/old_migrations/patch-monitor-oauth-cc.sql new file mode 100644 index 0000000..f33e952 --- /dev/null +++ b/db/old_migrations/patch-monitor-oauth-cc.sql @@ -0,0 +1,19 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD oauth_client_id TEXT default null; + +ALTER TABLE monitor + ADD oauth_client_secret TEXT default null; + +ALTER TABLE monitor + ADD oauth_token_url TEXT default null; + +ALTER TABLE monitor + ADD oauth_scopes TEXT default null; + +ALTER TABLE monitor + ADD oauth_auth_method TEXT default null; + +COMMIT; diff --git a/db/old_migrations/patch-monitor-push_token.sql b/db/old_migrations/patch-monitor-push_token.sql new file mode 100644 index 0000000..8c2e7a4 --- /dev/null +++ b/db/old_migrations/patch-monitor-push_token.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD push_token VARCHAR(20) DEFAULT NULL; + +COMMIT; diff --git a/db/old_migrations/patch-monitor-tls-info-add-fk.sql b/db/old_migrations/patch-monitor-tls-info-add-fk.sql new file mode 100644 index 0000000..9b9c2d2 --- /dev/null +++ b/db/old_migrations/patch-monitor-tls-info-add-fk.sql @@ -0,0 +1,18 @@ +BEGIN TRANSACTION; + +PRAGMA writable_schema = TRUE; + +UPDATE + SQLITE_MASTER +SET + sql = replace(sql, + 'monitor_id INTEGER NOT NULL', + 'monitor_id INTEGER NOT NULL REFERENCES [monitor] ([id]) ON DELETE CASCADE ON UPDATE CASCADE' +) +WHERE + name = 'monitor_tls_info' + AND type = 'table'; + +PRAGMA writable_schema = RESET; + +COMMIT; diff --git a/db/old_migrations/patch-monitor-tls.sql b/db/old_migrations/patch-monitor-tls.sql new file mode 100644 index 0000000..ac4edb7 --- /dev/null +++ b/db/old_migrations/patch-monitor-tls.sql @@ -0,0 +1,13 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD tls_ca TEXT default null; + +ALTER TABLE monitor + ADD tls_cert TEXT default null; + +ALTER TABLE monitor + ADD tls_key TEXT default null; + +COMMIT; diff --git a/db/old_migrations/patch-notification-config.sql b/db/old_migrations/patch-notification-config.sql new file mode 100644 index 0000000..16944fb --- /dev/null +++ b/db/old_migrations/patch-notification-config.sql @@ -0,0 +1,10 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +-- SQLite: Change the data type of the column "config" from VARCHAR to TEXT +ALTER TABLE notification RENAME COLUMN config TO config_old; +ALTER TABLE notification ADD COLUMN config TEXT; +UPDATE notification SET config = config_old; +ALTER TABLE notification DROP COLUMN config_old; + +COMMIT; diff --git a/db/old_migrations/patch-notification_sent_history.sql b/db/old_migrations/patch-notification_sent_history.sql new file mode 100644 index 0000000..759eb38 --- /dev/null +++ b/db/old_migrations/patch-notification_sent_history.sql @@ -0,0 +1,18 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +CREATE TABLE [notification_sent_history] ( + [id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + [type] VARCHAR(50) NOT NULL, + [monitor_id] INTEGER NOT NULL, + [days] INTEGER NOT NULL, + UNIQUE([type], [monitor_id], [days]) +); + +CREATE INDEX [good_index] ON [notification_sent_history] ( + [type], + [monitor_id], + [days] +); + +COMMIT; diff --git a/db/old_migrations/patch-ping-packet-size.sql b/db/old_migrations/patch-ping-packet-size.sql new file mode 100644 index 0000000..f127fc2 --- /dev/null +++ b/db/old_migrations/patch-ping-packet-size.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD packet_size INTEGER DEFAULT 56 NOT NULL; + +COMMIT; diff --git a/db/old_migrations/patch-proxy.sql b/db/old_migrations/patch-proxy.sql new file mode 100644 index 0000000..41897b1 --- /dev/null +++ b/db/old_migrations/patch-proxy.sql @@ -0,0 +1,23 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +CREATE TABLE proxy ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + user_id INT NOT NULL, + protocol VARCHAR(10) NOT NULL, + host VARCHAR(255) NOT NULL, + port SMALLINT NOT NULL, + auth BOOLEAN NOT NULL, + username VARCHAR(255) NULL, + password VARCHAR(255) NULL, + active BOOLEAN NOT NULL DEFAULT 1, + 'default' BOOLEAN NOT NULL DEFAULT 0, + created_date DATETIME DEFAULT (DATETIME('now')) NOT NULL +); + +ALTER TABLE monitor ADD COLUMN proxy_id INTEGER REFERENCES proxy(id); + +CREATE INDEX proxy_id ON monitor (proxy_id); +CREATE INDEX proxy_user_id ON proxy (user_id); + +COMMIT; diff --git a/db/old_migrations/patch-setting-value-type.sql b/db/old_migrations/patch-setting-value-type.sql new file mode 100644 index 0000000..4816bc6 --- /dev/null +++ b/db/old_migrations/patch-setting-value-type.sql @@ -0,0 +1,21 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +-- Generated by Intellij IDEA +create table setting_dg_tmp +( + id INTEGER + primary key autoincrement, + key VARCHAR(200) not null + unique, + value TEXT, + type VARCHAR(20) +); + +insert into setting_dg_tmp(id, key, value, type) select id, key, value, type from setting; + +drop table setting; + +alter table setting_dg_tmp rename to setting; + +COMMIT; diff --git a/db/old_migrations/patch-status-page-footer-css.sql b/db/old_migrations/patch-status-page-footer-css.sql new file mode 100644 index 0000000..beef922 --- /dev/null +++ b/db/old_migrations/patch-status-page-footer-css.sql @@ -0,0 +1,11 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE status_page + ADD footer_text TEXT; +ALTER TABLE status_page + ADD custom_css TEXT; +ALTER TABLE status_page + ADD show_powered_by BOOLEAN NOT NULL DEFAULT 1; + +COMMIT; diff --git a/db/old_migrations/patch-status-page.sql b/db/old_migrations/patch-status-page.sql new file mode 100644 index 0000000..d23b75b --- /dev/null +++ b/db/old_migrations/patch-status-page.sql @@ -0,0 +1,31 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +CREATE TABLE [status_page]( + [id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + [slug] VARCHAR(255) NOT NULL UNIQUE, + [title] VARCHAR(255) NOT NULL, + [description] TEXT, + [icon] VARCHAR(255) NOT NULL, + [theme] VARCHAR(30) NOT NULL, + [published] BOOLEAN NOT NULL DEFAULT 1, + [search_engine_index] BOOLEAN NOT NULL DEFAULT 1, + [show_tags] BOOLEAN NOT NULL DEFAULT 0, + [password] VARCHAR, + [created_date] DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + [modified_date] DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE UNIQUE INDEX [slug] ON [status_page]([slug]); + + +CREATE TABLE [status_page_cname]( + [id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + [status_page_id] INTEGER NOT NULL REFERENCES [status_page]([id]) ON DELETE CASCADE ON UPDATE CASCADE, + [domain] VARCHAR NOT NULL UNIQUE +); + +ALTER TABLE incident ADD status_page_id INTEGER; +ALTER TABLE [group] ADD status_page_id INTEGER; + +COMMIT; diff --git a/db/old_migrations/patch-timeout.sql b/db/old_migrations/patch-timeout.sql new file mode 100644 index 0000000..f257112 --- /dev/null +++ b/db/old_migrations/patch-timeout.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +UPDATE monitor SET timeout = (interval * 0.8) +WHERE timeout IS NULL OR timeout <= 0; + +COMMIT; diff --git a/db/old_migrations/patch1.sql b/db/old_migrations/patch1.sql new file mode 100644 index 0000000..6a31fa2 --- /dev/null +++ b/db/old_migrations/patch1.sql @@ -0,0 +1,37 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +-- Change Monitor.created_date from "TIMESTAMP" to "DATETIME" +-- SQL Generated by Intellij Idea +PRAGMA foreign_keys=off; + +BEGIN TRANSACTION; + +create table monitor_dg_tmp +( + id INTEGER not null + primary key autoincrement, + name VARCHAR(150), + active BOOLEAN default 1 not null, + user_id INTEGER + references user + on update cascade on delete set null, + interval INTEGER default 20 not null, + url TEXT, + type VARCHAR(20), + weight INTEGER default 2000, + hostname VARCHAR(255), + port INTEGER, + created_date DATETIME, + keyword VARCHAR(255) +); + +insert into monitor_dg_tmp(id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword) select id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword from monitor; + +drop table monitor; + +alter table monitor_dg_tmp rename to monitor; + +create index user_id on monitor (user_id); + +COMMIT; + +PRAGMA foreign_keys=on; diff --git a/db/old_migrations/patch10.sql b/db/old_migrations/patch10.sql new file mode 100644 index 0000000..488db11 --- /dev/null +++ b/db/old_migrations/patch10.sql @@ -0,0 +1,19 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +CREATE TABLE tag ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name VARCHAR(255) NOT NULL, + color VARCHAR(255) NOT NULL, + created_date DATETIME DEFAULT (DATETIME('now')) NOT NULL +); + +CREATE TABLE monitor_tag ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + monitor_id INTEGER NOT NULL, + tag_id INTEGER NOT NULL, + value TEXT, + CONSTRAINT FK_tag FOREIGN KEY (tag_id) REFERENCES tag(id) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT FK_monitor FOREIGN KEY (monitor_id) REFERENCES monitor(id) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE INDEX monitor_tag_monitor_id_index ON monitor_tag (monitor_id); +CREATE INDEX monitor_tag_tag_id_index ON monitor_tag (tag_id); diff --git a/db/old_migrations/patch2.sql b/db/old_migrations/patch2.sql new file mode 100644 index 0000000..2f34e29 --- /dev/null +++ b/db/old_migrations/patch2.sql @@ -0,0 +1,10 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +CREATE TABLE monitor_tls_info ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + monitor_id INTEGER NOT NULL, + info_json TEXT +); + +COMMIT; diff --git a/db/old_migrations/patch3.sql b/db/old_migrations/patch3.sql new file mode 100644 index 0000000..e615632 --- /dev/null +++ b/db/old_migrations/patch3.sql @@ -0,0 +1,37 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +-- Add maxretries column to monitor +PRAGMA foreign_keys=off; + +BEGIN TRANSACTION; + +create table monitor_dg_tmp +( + id INTEGER not null + primary key autoincrement, + name VARCHAR(150), + active BOOLEAN default 1 not null, + user_id INTEGER + references user + on update cascade on delete set null, + interval INTEGER default 20 not null, + url TEXT, + type VARCHAR(20), + weight INTEGER default 2000, + hostname VARCHAR(255), + port INTEGER, + created_date DATETIME, + keyword VARCHAR(255), + maxretries INTEGER NOT NULL DEFAULT 0 +); + +insert into monitor_dg_tmp(id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword) select id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword from monitor; + +drop table monitor; + +alter table monitor_dg_tmp rename to monitor; + +create index user_id on monitor (user_id); + +COMMIT; + +PRAGMA foreign_keys=on; diff --git a/db/old_migrations/patch4.sql b/db/old_migrations/patch4.sql new file mode 100644 index 0000000..ff40da2 --- /dev/null +++ b/db/old_migrations/patch4.sql @@ -0,0 +1,40 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +-- OK.... serious wrong, missing maxretries column +-- Developers should patch it manually if you have missing the maxretries column +PRAGMA foreign_keys=off; + +BEGIN TRANSACTION; + +create table monitor_dg_tmp +( + id INTEGER not null + primary key autoincrement, + name VARCHAR(150), + active BOOLEAN default 1 not null, + user_id INTEGER + references user + on update cascade on delete set null, + interval INTEGER default 20 not null, + url TEXT, + type VARCHAR(20), + weight INTEGER default 2000, + hostname VARCHAR(255), + port INTEGER, + created_date DATETIME, + keyword VARCHAR(255), + maxretries INTEGER NOT NULL DEFAULT 0, + ignore_tls BOOLEAN default 0 not null, + upside_down BOOLEAN default 0 not null +); + +insert into monitor_dg_tmp(id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword, maxretries) select id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword, maxretries from monitor; + +drop table monitor; + +alter table monitor_dg_tmp rename to monitor; + +create index user_id on monitor (user_id); + +COMMIT; + +PRAGMA foreign_keys=on; diff --git a/db/old_migrations/patch5.sql b/db/old_migrations/patch5.sql new file mode 100644 index 0000000..5730b2d --- /dev/null +++ b/db/old_migrations/patch5.sql @@ -0,0 +1,70 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +PRAGMA foreign_keys = off; + +BEGIN TRANSACTION; + +create table monitor_dg_tmp ( + id INTEGER not null primary key autoincrement, + name VARCHAR(150), + active BOOLEAN default 1 not null, + user_id INTEGER references user on update cascade on delete + set + null, + interval INTEGER default 20 not null, + url TEXT, + type VARCHAR(20), + weight INTEGER default 2000, + hostname VARCHAR(255), + port INTEGER, + created_date DATETIME default (DATETIME('now')) not null, + keyword VARCHAR(255), + maxretries INTEGER NOT NULL DEFAULT 0, + ignore_tls BOOLEAN default 0 not null, + upside_down BOOLEAN default 0 not null +); + +insert into + monitor_dg_tmp( + id, + name, + active, + user_id, + interval, + url, + type, + weight, + hostname, + port, + keyword, + maxretries, + ignore_tls, + upside_down + ) +select + id, + name, + active, + user_id, + interval, + url, + type, + weight, + hostname, + port, + keyword, + maxretries, + ignore_tls, + upside_down +from + monitor; + +drop table monitor; + +alter table + monitor_dg_tmp rename to monitor; + +create index user_id on monitor (user_id); + +COMMIT; + +PRAGMA foreign_keys = on; diff --git a/db/old_migrations/patch6.sql b/db/old_migrations/patch6.sql new file mode 100644 index 0000000..4f539a2 --- /dev/null +++ b/db/old_migrations/patch6.sql @@ -0,0 +1,74 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +PRAGMA foreign_keys = off; + +BEGIN TRANSACTION; + +create table monitor_dg_tmp ( + id INTEGER not null primary key autoincrement, + name VARCHAR(150), + active BOOLEAN default 1 not null, + user_id INTEGER references user on update cascade on delete + set + null, + interval INTEGER default 20 not null, + url TEXT, + type VARCHAR(20), + weight INTEGER default 2000, + hostname VARCHAR(255), + port INTEGER, + created_date DATETIME default (DATETIME('now')) not null, + keyword VARCHAR(255), + maxretries INTEGER NOT NULL DEFAULT 0, + ignore_tls BOOLEAN default 0 not null, + upside_down BOOLEAN default 0 not null, + maxredirects INTEGER default 10 not null, + accepted_statuscodes_json TEXT default '["200-299"]' not null +); + +insert into + monitor_dg_tmp( + id, + name, + active, + user_id, + interval, + url, + type, + weight, + hostname, + port, + created_date, + keyword, + maxretries, + ignore_tls, + upside_down + ) +select + id, + name, + active, + user_id, + interval, + url, + type, + weight, + hostname, + port, + created_date, + keyword, + maxretries, + ignore_tls, + upside_down +from + monitor; + +drop table monitor; + +alter table + monitor_dg_tmp rename to monitor; + +create index user_id on monitor (user_id); + +COMMIT; + +PRAGMA foreign_keys = on; diff --git a/db/old_migrations/patch7.sql b/db/old_migrations/patch7.sql new file mode 100644 index 0000000..2e8eba1 --- /dev/null +++ b/db/old_migrations/patch7.sql @@ -0,0 +1,10 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD dns_resolve_type VARCHAR(5); + +ALTER TABLE monitor + ADD dns_resolve_server VARCHAR(255); + +COMMIT; diff --git a/db/old_migrations/patch8.sql b/db/old_migrations/patch8.sql new file mode 100644 index 0000000..d63a594 --- /dev/null +++ b/db/old_migrations/patch8.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD dns_last_result VARCHAR(255); + +COMMIT; diff --git a/db/old_migrations/patch9.sql b/db/old_migrations/patch9.sql new file mode 100644 index 0000000..d4d13aa --- /dev/null +++ b/db/old_migrations/patch9.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE notification + ADD is_default BOOLEAN default 0 NOT NULL; + +COMMIT; diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..a8bb87e --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,8 @@ +module.exports = { + apps: [ + { + name: "uptime-kuma", + script: "./server/server.js", + }, + ], +}; diff --git a/extra/build-healthcheck.js b/extra/build-healthcheck.js new file mode 100644 index 0000000..698e960 --- /dev/null +++ b/extra/build-healthcheck.js @@ -0,0 +1,10 @@ +const childProcess = require("child_process"); +const platform = process.argv[2]; + +if (!platform) { + console.error("No platform??"); + process.exit(1); +} + +const output = childProcess.execSync("go build -x -o ./extra/healthcheck ./extra/healthcheck.go").toString("utf8"); +console.log(output); diff --git a/extra/check-knex-filenames.mjs b/extra/check-knex-filenames.mjs new file mode 100644 index 0000000..4911fc5 --- /dev/null +++ b/extra/check-knex-filenames.mjs @@ -0,0 +1,72 @@ +import fs from "fs"; +const dir = "./db/knex_migrations"; + +// Get the file list (ending with .js) from the directory +const files = fs.readdirSync(dir).filter((file) => file !== "README.md"); + +// They are wrong, but they had been merged, so allowed. +const exceptionList = [ + "2024-08-24-000-add-cache-bust.js", + "2024-10-1315-rabbitmq-monitor.js", +]; + +// Correct format: YYYY-MM-DD-HHmm-description.js + +for (const file of files) { + if (exceptionList.includes(file)) { + continue; + } + + // Check ending with .js + if (!file.endsWith(".js")) { + console.error(`It should end with .js: ${file}`); + process.exit(1); + } + + const parts = file.split("-"); + + // Should be at least 5 parts + if (parts.length < 5) { + console.error(`Invalid format: ${file}`); + process.exit(1); + } + + // First part should be a year >= 2024 + const year = parseInt(parts[0], 10); + if (isNaN(year) || year < 2023) { + console.error(`Invalid year: ${file}`); + process.exit(1); + } + + // Second part should be a month + const month = parseInt(parts[1], 10); + if (isNaN(month) || month < 1 || month > 12) { + console.error(`Invalid month: ${file}`); + process.exit(1); + } + + // Third part should be a day + const day = parseInt(parts[2], 10); + if (isNaN(day) || day < 1 || day > 31) { + console.error(`Invalid day: ${file}`); + process.exit(1); + } + + // Fourth part should be HHmm + const time = parts[3]; + + // Check length is 4 + if (time.length !== 4) { + console.error(`Invalid time: ${file}`); + process.exit(1); + } + + const hour = parseInt(time.substring(0, 2), 10); + const minute = parseInt(time.substring(2), 10); + if (isNaN(hour) || hour < 0 || hour > 23 || isNaN(minute) || minute < 0 || minute > 59) { + console.error(`Invalid time: ${file}`); + process.exit(1); + } +} + +console.log("All knex filenames are correct."); diff --git a/extra/check-lang-json.js b/extra/check-lang-json.js new file mode 100644 index 0000000..dfda348 --- /dev/null +++ b/extra/check-lang-json.js @@ -0,0 +1,27 @@ +// For #5231 + +const fs = require("fs"); + +let path = "./src/lang"; + +// list directories in the lang directory +let jsonFileList = fs.readdirSync(path); + +for (let jsonFile of jsonFileList) { + if (!jsonFile.endsWith(".json")) { + continue; + } + + let jsonPath = path + "/" + jsonFile; + let originalContent = fs.readFileSync(jsonPath, "utf8"); + let langData = JSON.parse(originalContent); + + let formattedContent = JSON.stringify(langData, null, 4) + "\n"; + + if (originalContent !== formattedContent) { + console.error(`File ${jsonFile} is not formatted correctly.`); + process.exit(1); + } +} + +console.log("All lang json files are formatted correctly."); diff --git a/extra/check-package-json.mjs b/extra/check-package-json.mjs new file mode 100644 index 0000000..e9d16a5 --- /dev/null +++ b/extra/check-package-json.mjs @@ -0,0 +1,17 @@ +import packageJSON from "../package.json" with { type: "json" }; + +let hasError = false; + +for (const dep in packageJSON.dependencies) { + const semver = packageJSON.dependencies[dep]; + if (semver.startsWith("^")) { + console.error(`Dependency ${dep} has a caret (^) in its version. Please change it to (~)`); + hasError = true; + } +} + +if (hasError) { + process.exit(1); +} else { + console.log("All dependencies are valid."); +} diff --git a/extra/checkout-pr.mjs b/extra/checkout-pr.mjs new file mode 100644 index 0000000..6536644 --- /dev/null +++ b/extra/checkout-pr.mjs @@ -0,0 +1,34 @@ +import childProcess from "child_process"; +import { parsePrName } from "./kuma-pr/pr-lib.mjs"; + +let { name, branch } = parsePrName(process.env.UPTIME_KUMA_GH_REPO); + +console.log(`Checking out PR from ${name}:${branch}`); + +// Checkout the pr +let result = childProcess.spawnSync("git", [ "remote", "add", name, `https://github.com/${name}/uptime-kuma` ], { + stdio: "inherit" +}); + +if (result.status !== 0) { + console.error("Failed to add remote repository."); + process.exit(1); +} + +result = childProcess.spawnSync("git", [ "fetch", name, branch ], { + stdio: "inherit" +}); + +if (result.status !== 0) { + console.error("Failed to fetch the branch."); + process.exit(1); +} + +result = childProcess.spawnSync("git", [ "checkout", `${name}/${branch}`, "--force" ], { + stdio: "inherit" +}); + +if (result.status !== 0) { + console.error("Failed to checkout the branch."); + process.exit(1); +} diff --git a/extra/close-incorrect-issue.mjs b/extra/close-incorrect-issue.mjs new file mode 100644 index 0000000..16733c3 --- /dev/null +++ b/extra/close-incorrect-issue.mjs @@ -0,0 +1,57 @@ +import * as github from "@actions/github"; + +(async () => { + try { + const token = process.argv[2]; + const issueNumber = process.argv[3]; + const username = process.argv[4]; + + const client = github.getOctokit(token).rest; + + const issue = { + owner: "louislam", + repo: "uptime-kuma", + number: issueNumber, + }; + + const labels = ( + await client.issues.listLabelsOnIssue({ + owner: issue.owner, + repo: issue.repo, + issue_number: issue.number, + }) + ).data.map(({ name }) => name); + + if (labels.length === 0) { + console.log("Bad format here"); + + await client.issues.addLabels({ + owner: issue.owner, + repo: issue.repo, + issue_number: issue.number, + labels: ["invalid-format"], + }); + + // Add the issue closing comment + await client.issues.createComment({ + 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 discussion.`, + }); + + // Close the issue + await client.issues.update({ + owner: issue.owner, + repo: issue.repo, + issue_number: issue.number, + state: "closed", + }); + } else { + console.log("Pass!"); + } + } catch (e) { + console.error(e); + process.exitCode = 1; + } +})(); diff --git a/extra/compile-install-script.ps1 b/extra/compile-install-script.ps1 new file mode 100644 index 0000000..dd44798 --- /dev/null +++ b/extra/compile-install-script.ps1 @@ -0,0 +1,2 @@ +# Must enable File Sharing in Docker Desktop +docker run -it --rm -v ${pwd}:/app louislam/batsh /usr/bin/batsh bash --output ./install.sh ./extra/install.batsh diff --git a/extra/deploy-demo-server.js b/extra/deploy-demo-server.js new file mode 100644 index 0000000..60158f8 --- /dev/null +++ b/extra/deploy-demo-server.js @@ -0,0 +1,62 @@ +const { loadEnvFile } = require("node:process"); +const { NodeSSH } = require("node-ssh"); +const readline = require("readline"); +const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); +const prompt = (query) => new Promise((resolve) => rl.question(query, resolve)); + +try { + loadEnvFile(); +} catch (_) {} + +(async () => { + try { + console.log("SSH to demo server"); + const ssh = new NodeSSH(); + await ssh.connect({ + 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, + }); + + let cwd = process.env.UPTIME_KUMA_DEMO_CWD; + let result; + + const version = await prompt("Enter Version: "); + + result = await ssh.execCommand("git fetch --all", { + cwd, + }); + console.log(result.stdout + result.stderr); + + await prompt("Press any key to continue..."); + + result = await ssh.execCommand(`git checkout ${version} --force`, { + cwd, + }); + console.log(result.stdout + result.stderr); + + result = await ssh.execCommand("npm run download-dist", { + cwd, + }); + console.log(result.stdout + result.stderr); + + result = await ssh.execCommand("npm install --production", { + cwd, + }); + console.log(result.stdout + result.stderr); + + /* + result = await ssh.execCommand("pm2 restart 1", { + cwd, + }); + console.log(result.stdout + result.stderr);*/ + } catch (e) { + console.log(e); + } finally { + rl.close(); + } +})(); + +// When done reading prompt, exit program +rl.on("close", () => process.exit(0)); diff --git a/extra/download-dist.js b/extra/download-dist.js new file mode 100644 index 0000000..848da17 --- /dev/null +++ b/extra/download-dist.js @@ -0,0 +1,68 @@ +console.log("Downloading dist"); +const https = require("https"); +const tar = require("tar"); + +const packageJSON = require("../package.json"); +const fs = require("fs"); +const version = packageJSON.version; + +const filename = "dist.tar.gz"; + +const url = `https://github.com/louislam/uptime-kuma/releases/download/${version}/${filename}`; +download(url); + +/** + * Downloads the latest version of the dist from a GitHub release. + * @param {string} url The URL to download from. + * @returns {void} + * + * Generated by Trelent + */ +function download(url) { + console.log(url); + + https.get(url, (response) => { + if (response.statusCode === 200) { + console.log("Extracting dist..."); + + if (fs.existsSync("./dist")) { + if (fs.existsSync("./dist-backup")) { + fs.rmSync("./dist-backup", { + recursive: true, + force: true, + }); + } + + fs.renameSync("./dist", "./dist-backup"); + } + + const tarStream = tar.x({ + cwd: "./", + }); + + tarStream.on("close", () => { + if (fs.existsSync("./dist-backup")) { + fs.rmSync("./dist-backup", { + recursive: true, + force: true, + }); + } + console.log("Done"); + process.exit(0); + }); + + tarStream.on("error", () => { + if (fs.existsSync("./dist-backup")) { + fs.renameSync("./dist-backup", "./dist"); + } + console.error("Error from tarStream"); + }); + + response.pipe(tarStream); + } else if (response.statusCode === 302) { + download(response.headers.location); + } else { + console.log("dist not found"); + } + }); +} diff --git a/extra/healthcheck.go b/extra/healthcheck.go new file mode 100644 index 0000000..f79b3e6 --- /dev/null +++ b/extra/healthcheck.go @@ -0,0 +1,90 @@ +/* + * If changed, have to run `npm run build-docker-builder-go`. + * This script should be run after a period of time (180s), because the server may need some time to prepare. + */ +package main + +import ( + "crypto/tls" + "io/ioutil" + "log" + "net/http" + "os" + "runtime" + "strings" + "time" +) + +func main() { + isFreeBSD := runtime.GOOS == "freebsd" + + // Is K8S + uptime-kuma as the container name + // See #2083 + isK8s := strings.HasPrefix(os.Getenv("UPTIME_KUMA_PORT"), "tcp://") + + // process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, + } + + client := http.Client{ + Timeout: 28 * time.Second, + } + + sslKey := os.Getenv("UPTIME_KUMA_SSL_KEY") + if len(sslKey) == 0 { + sslKey = os.Getenv("SSL_KEY") + } + + sslCert := os.Getenv("UPTIME_KUMA_SSL_CERT") + if len(sslCert) == 0 { + sslCert = os.Getenv("SSL_CERT") + } + + hostname := os.Getenv("UPTIME_KUMA_HOST") + if len(hostname) == 0 && !isFreeBSD { + hostname = os.Getenv("HOST") + } + if len(hostname) == 0 { + hostname = "127.0.0.1" + } + + port := "" + // UPTIME_KUMA_PORT is override by K8S unexpectedly, + if !isK8s { + port = os.Getenv("UPTIME_KUMA_PORT") + } + if len(port) == 0 { + port = os.Getenv("PORT") + } + if len(port) == 0 { + port = "3001" + } + + protocol := "" + if len(sslKey) != 0 && len(sslCert) != 0 { + protocol = "https" + } else { + protocol = "http" + } + + url := protocol + "://" + hostname + ":" + port + + log.Println("Checking " + url) + resp, err := client.Get(url) + + if err != nil { + log.Fatalln(err) + } + + defer resp.Body.Close() + + _, err = ioutil.ReadAll(resp.Body) + + if err != nil { + log.Fatalln(err) + } + + log.Printf("Health Check OK [Res Code: %d]\n", resp.StatusCode) + +} diff --git a/extra/kuma-pr/index.mjs b/extra/kuma-pr/index.mjs new file mode 100644 index 0000000..bcda9d3 --- /dev/null +++ b/extra/kuma-pr/index.mjs @@ -0,0 +1,26 @@ +#!/usr/bin/env node +import { spawn } from "child_process"; +import { parsePrName } from "./pr-lib.mjs"; + +const prName = process.argv[2]; + +// Pre-check the prName here, so testers don't need to wait until the Docker image is pulled to see the error. +try { + parsePrName(prName); +} catch (error) { + console.error(error.message); + process.exit(1); +} + +spawn("docker", [ + "run", + "--rm", + "-it", + "-p", "3000:3000", + "-p", "3001:3001", + "--pull", "always", + "-e", `UPTIME_KUMA_GH_REPO=${prName}`, + "louislam/uptime-kuma:pr-test2" +], { + stdio: "inherit", +}); diff --git a/extra/kuma-pr/package.json b/extra/kuma-pr/package.json new file mode 100644 index 0000000..16f30b1 --- /dev/null +++ b/extra/kuma-pr/package.json @@ -0,0 +1,8 @@ +{ + "name": "kuma-pr", + "version": "1.0.0", + "type": "module", + "bin": { + "kuma-pr": "./index.mjs" + } +} diff --git a/extra/kuma-pr/pr-lib.mjs b/extra/kuma-pr/pr-lib.mjs new file mode 100644 index 0000000..1cb5050 --- /dev/null +++ b/extra/kuma-pr/pr-lib.mjs @@ -0,0 +1,39 @@ +/** + * Parse : to an object. + * @param {string} prName : + * @returns {object} An object with name and branch properties. + */ +export function parsePrName(prName) { + let name = "louislam"; + let branch; + + const errorMessage = "Please set a repo to the environment variable 'UPTIME_KUMA_GH_REPO' (e.g. mhkarimi1383:goalert-notification)"; + + if (!prName) { + throw new Error(errorMessage); + } + + prName = prName.trim(); + if (prName === "") { + throw new Error(errorMessage); + } + + let inputArray = prName.split(":"); + + // Just realized that owner's prs are not prefixed with "louislam:" + if (inputArray.length === 1) { + branch = inputArray[0]; + + } else if (inputArray.length === 2) { + name = inputArray[0]; + branch = inputArray[1]; + + } else { + throw new Error("Invalid format. The format is like this: mhkarimi1383:goalert-notification"); + } + + return { + name, + branch + }; +} diff --git a/extra/mark-as-nightly.js b/extra/mark-as-nightly.js new file mode 100644 index 0000000..83decd2 --- /dev/null +++ b/extra/mark-as-nightly.js @@ -0,0 +1,21 @@ +const pkg = require("../package.json"); +const fs = require("fs"); +const dayjs = require("dayjs"); + +const oldVersion = pkg.version; +const newVersion = oldVersion + "-nightly-" + dayjs().format("YYYYMMDDHHmmss"); + +console.log("Old Version: " + oldVersion); +console.log("New Version: " + newVersion); + +if (newVersion) { + // Process package.json + pkg.version = newVersion; + pkg.scripts.setup = pkg.scripts.setup.replaceAll(oldVersion, newVersion); + fs.writeFileSync("package.json", JSON.stringify(pkg, null, 4) + "\n"); + + // Process README.md + if (fs.existsSync("README.md")) { + fs.writeFileSync("README.md", fs.readFileSync("README.md", "utf8").replaceAll(oldVersion, newVersion)); + } +} diff --git a/extra/prepare-v1-test-db.mjs b/extra/prepare-v1-test-db.mjs new file mode 100644 index 0000000..88f09b3 --- /dev/null +++ b/extra/prepare-v1-test-db.mjs @@ -0,0 +1,143 @@ +/** + * Regenerate test/fixtures/v1-kuma.db.gz, a pristine Uptime Kuma 1.23.17 SQLite database. + * + * The migration test (test/backend-test/test-migration-v1-to-v3.ts) upgrades this real + * 1.23.17 database straight to v3 to make sure the v1 -> v3 upgrade path keeps working. + * + * It is generated by booting the official 1.23.17 image once with an empty data dir, + * letting it create and patch the database, then checkpointing the WAL so the file is standalone. + * + * Usage: + * node extra/prepare-v1-test-db.mjs + * + * Requires Docker. Only run this when the v1 schema actually changed. + */ +import { execFileSync } from "node:child_process"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import zlib from "node:zlib"; +import { fileURLToPath } from "node:url"; +import BetterSqlite3 from "better-sqlite3"; +import bcrypt from "bcryptjs"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.join(__dirname, ".."); +const fixturePath = path.join(repoRoot, "test/fixtures/v1-kuma.db.gz"); +const image = "louislam/uptime-kuma:1.23.17"; +const containerName = "uptime-kuma-v1-fixture"; +const adminPassword = "Kuma-V1-Test-Password"; + +/** + * Run a command and inherit stdio. + * @param {string} command Command to run + * @param {string[]} args Arguments + * @returns {void} + */ +function run(command, args) { + console.log(`$ ${command} ${args.join(" ")}`); + execFileSync(command, args, { stdio: "inherit" }); +} + +/** + * Copy a file if it exists. + * @param {string} from Source + * @param {string} to Destination + * @returns {void} + */ +function copyIfExists(from, to) { + if (fs.existsSync(from)) { + fs.copyFileSync(from, to); + } +} + +const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "uk-v1-fixture-")); +const workDir = fs.mkdtempSync(path.join(os.tmpdir(), "uk-v1-work-")); + +try { + run("docker", ["rm", "-f", containerName]); + run("docker", [ + "run", + "-d", + "--name", + containerName, + "-e", + "UPTIME_KUMA_DB_TYPE=sqlite", + "-v", + `${dataDir}:/app/data`, + image, + ]); + + // Wait until the container finished patching the database and started the server + const dbPath = path.join(dataDir, "kuma.db"); + const deadline = Date.now() + 120_000; + while (Date.now() < deadline) { + let logs = ""; + try { + logs = execFileSync("docker", ["logs", containerName], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }); + } catch (e) { + logs = ""; + } + if (logs.includes("Database Patched Successfully") && fs.existsSync(dbPath) && fs.statSync(dbPath).size > 0) { + break; + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + if (!fs.existsSync(dbPath)) { + throw new Error("Timed out waiting for the v1 database to be created"); + } + + run("docker", ["stop", containerName]); + + // Like v2, the image keeps the schema in the WAL file, so copy all three files and checkpoint + copyIfExists(dbPath, path.join(workDir, "kuma.db")); + copyIfExists(`${dbPath}-wal`, path.join(workDir, "kuma.db-wal")); + copyIfExists(`${dbPath}-shm`, path.join(workDir, "kuma.db-shm")); + + const db = new BetterSqlite3(path.join(workDir, "kuma.db")); + db.pragma("wal_checkpoint(TRUNCATE)"); + + // Seed a realistic v1 dataset (an admin with one of each owned resource), so the fixture + // can be used to boot the server directly. v1 has no remote_browser table yet; the v3 + // migrations create it during the upgrade. + const now = new Date().toISOString().slice(0, 19).replace("T", " "); + const adminPasswordHash = await bcrypt.hash(adminPassword, 10); + db.prepare("INSERT INTO user (username, password, active, timezone, twofa_status) VALUES (?, ?, 1, 'UTC', 0)").run( + "admin", + adminPasswordHash + ); + db.prepare( + "INSERT INTO monitor (name, active, user_id, interval, type, url, method, maxretries, maxredirects, accepted_statuscodes_json, retry_interval, created_date) VALUES ('Migrated Monitor', 0, 1, 60, 'http', 'https://example.com', 'GET', 0, 10, '[\"200-299\"]', 60, ?)" + ).run(now); + db.prepare("INSERT INTO notification (name, active, user_id, is_default, config) VALUES ('Notif 1', 1, 1, 0, '{}')").run(); + db.prepare( + "INSERT INTO maintenance (title, description, user_id, active, strategy, weekdays, days_of_month) VALUES ('Maintenance 1', '', 1, 1, 'manual', '[]', '[]')" + ).run(); + db.prepare( + "INSERT INTO proxy (user_id, protocol, host, port, auth, active, `default`, created_date) VALUES (1, 'http', '127.0.0.1', 8080, 0, 1, 0, ?)" + ).run(now); + db.prepare("INSERT INTO docker_host (user_id, docker_daemon, docker_type, name) VALUES (1, 'tcp://127.0.0.1:2375', 'socket', 'Docker 1')").run(); + db.prepare("INSERT INTO api_key (key, name, user_id, created_date, active) VALUES (?, 'API Key 1', 1, ?, 1)").run( + await bcrypt.hash("test-api-key", 10), + now + ); + + // Keep the fixture compact and in rollback-journal mode so simply opening it does not create + // -wal/-shm sidecar files. Set VACUUM for a compact file, then it is gzipped before being saved. + db.pragma("journal_mode = DELETE"); + db.exec("VACUUM"); + db.close(); + + fs.mkdirSync(path.dirname(fixturePath), { recursive: true }); + fs.writeFileSync(fixturePath, zlib.gzipSync(fs.readFileSync(path.join(workDir, "kuma.db")), { level: 9 })); + console.log(`Wrote ${fixturePath}`); +} finally { + try { + run("docker", ["rm", "-f", containerName]); + } catch (e) { + // Ignore + } + fs.rmSync(dataDir, { recursive: true, force: true }); + fs.rmSync(workDir, { recursive: true, force: true }); +} diff --git a/extra/prepare-v2-test-db.mjs b/extra/prepare-v2-test-db.mjs new file mode 100644 index 0000000..63b8315 --- /dev/null +++ b/extra/prepare-v2-test-db.mjs @@ -0,0 +1,140 @@ +/** + * Regenerate test/fixtures/v2-kuma.db, a pristine Uptime Kuma 2.5.3 SQLite database. + * + * The migration test (test/backend-test/test-migration-v2-to-v3.ts) upgrades this real + * 2.5.3 database to v3 to make sure the upgrade path keeps working (see #7944). + * + * It is generated by booting the official 2.5.3 slim image once with an empty data dir, + * letting it create the database, then checkpointing the WAL so the file is standalone. + * + * Usage: + * node extra/prepare-v2-test-db.mjs + * + * Requires Docker. Only run this when the v2 schema actually changed. + */ +import { execFileSync } from "node:child_process"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import zlib from "node:zlib"; +import { fileURLToPath } from "node:url"; +import BetterSqlite3 from "better-sqlite3"; +import bcrypt from "bcryptjs"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.join(__dirname, ".."); +const fixturePath = path.join(repoRoot, "test/fixtures/v2-kuma.db.gz"); +const image = "louislam/uptime-kuma:2.5.3-slim"; +const containerName = "uptime-kuma-v2-fixture"; +const adminPassword = "Kuma-V2-Test-Password"; + +/** + * Run a command and inherit stdio. + * @param {string} command Command to run + * @param {string[]} args Arguments + * @returns {void} + */ +function run(command, args) { + console.log(`$ ${command} ${args.join(" ")}`); + execFileSync(command, args, { stdio: "inherit" }); +} + +/** + * Copy a file if it exists. + * @param {string} from Source + * @param {string} to Destination + * @returns {void} + */ +function copyIfExists(from, to) { + if (fs.existsSync(from)) { + fs.copyFileSync(from, to); + } +} + +const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "uk-v2-fixture-")); +const workDir = fs.mkdtempSync(path.join(os.tmpdir(), "uk-v2-work-")); + +try { + run("docker", ["rm", "-f", containerName]); + run("docker", [ + "run", + "-d", + "--name", + containerName, + "-e", + "UPTIME_KUMA_DB_TYPE=sqlite", + "-v", + `${dataDir}:/app/data`, + image, + ]); + + // Wait until the container created and migrated the database + const dbPath = path.join(dataDir, "kuma.db"); + const deadline = Date.now() + 120_000; + while (Date.now() < deadline) { + if (fs.existsSync(dbPath) && fs.statSync(dbPath).size > 0) { + const walPath = `${dbPath}-wal`; + if (fs.existsSync(walPath) && fs.statSync(walPath).size > 0) { + break; + } + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + if (!fs.existsSync(dbPath)) { + throw new Error("Timed out waiting for the v2 database to be created"); + } + + run("docker", ["stop", containerName]); + + // The 2.5.3 image keeps the schema in the WAL file, so copy all three files and checkpoint + copyIfExists(dbPath, path.join(workDir, "kuma.db")); + copyIfExists(`${dbPath}-wal`, path.join(workDir, "kuma.db-wal")); + copyIfExists(`${dbPath}-shm`, path.join(workDir, "kuma.db-shm")); + + const db = new BetterSqlite3(path.join(workDir, "kuma.db")); + db.pragma("wal_checkpoint(TRUNCATE)"); + + // Seed a realistic 2.5.3 dataset (an admin with one of each owned resource), so the fixture + // can be used to boot the server directly. + const now = new Date().toISOString().slice(0, 19).replace("T", " "); + const adminPasswordHash = await bcrypt.hash(adminPassword, 10); + db.prepare("INSERT INTO user (username, password, active, timezone, twofa_status) VALUES (?, ?, 1, 'UTC', 0)").run( + "admin", + adminPasswordHash + ); + db.prepare( + "INSERT INTO monitor (name, active, user_id, interval, type, url, method, maxretries, maxredirects, accepted_statuscodes_json, retry_interval, created_date) VALUES ('Migrated Monitor', 0, 1, 60, 'http', 'https://example.com', 'GET', 0, 10, '[\"200-299\"]', 60, ?)" + ).run(now); + db.prepare("INSERT INTO notification (name, active, user_id, is_default, config) VALUES ('Notif 1', 1, 1, 0, '{}')").run(); + db.prepare( + "INSERT INTO maintenance (title, description, user_id, active, strategy, weekdays, days_of_month) VALUES ('Maintenance 1', '', 1, 1, 'manual', '[]', '[]')" + ).run(); + db.prepare( + "INSERT INTO proxy (user_id, protocol, host, port, auth, active, `default`, created_date) VALUES (1, 'http', '127.0.0.1', 8080, 0, 1, 0, ?)" + ).run(now); + db.prepare("INSERT INTO docker_host (user_id, docker_daemon, docker_type, name) VALUES (1, 'tcp://127.0.0.1:2375', 'socket', 'Docker 1')").run(); + db.prepare("INSERT INTO remote_browser (name, url, user_id) VALUES ('Browser 1', 'http://127.0.0.1:9222', 1)").run(); + db.prepare("INSERT INTO api_key (key, name, user_id, created_date, active) VALUES (?, 'API Key 1', 1, ?, 1)").run( + await bcrypt.hash("test-api-key", 10), + now + ); + + // Keep the fixture compact and in rollback-journal mode so simply opening it does not create + // -wal/-shm sidecar files. Set VACUUM for a compact file, then it is gzipped before being saved. + db.pragma("journal_mode = DELETE"); + db.exec("VACUUM"); + db.close(); + + fs.mkdirSync(path.dirname(fixturePath), { recursive: true }); + fs.writeFileSync(fixturePath, zlib.gzipSync(fs.readFileSync(path.join(workDir, "kuma.db")), { level: 9 })); + console.log(`Wrote ${fixturePath}`); +} finally { + try { + run("docker", ["rm", "-f", containerName]); + } catch (e) { + // Ignore + } + fs.rmSync(dataDir, { recursive: true, force: true }); + fs.rmSync(workDir, { recursive: true, force: true }); +} diff --git a/extra/push-examples/.gitignore b/extra/push-examples/.gitignore new file mode 100644 index 0000000..717394d --- /dev/null +++ b/extra/push-examples/.gitignore @@ -0,0 +1,3 @@ +java/Index.class +csharp/index.exe +typescript-fetch/index.js diff --git a/extra/push-examples/bash-curl/index.sh b/extra/push-examples/bash-curl/index.sh new file mode 100644 index 0000000..3031255 --- /dev/null +++ b/extra/push-examples/bash-curl/index.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Filename: index.sh +PUSH_URL="https://example.com/api/push/key?status=up&msg=OK&ping=" +INTERVAL=60 + +while true; do + curl -s -o /dev/null $PUSH_URL + echo "Pushed!" + sleep $INTERVAL +done diff --git a/extra/push-examples/csharp/index.cs b/extra/push-examples/csharp/index.cs new file mode 100644 index 0000000..94eecfb --- /dev/null +++ b/extra/push-examples/csharp/index.cs @@ -0,0 +1,24 @@ +using System; +using System.Net; +using System.Threading; + +/** + * Compile: C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe index.cs + * Run: index.exe + */ +class Index +{ + const string PushURL = "https://example.com/api/push/key?status=up&msg=OK&ping="; + const int Interval = 60; + + static void Main(string[] args) + { + while (true) + { + WebClient client = new WebClient(); + client.DownloadString(PushURL); + Console.WriteLine("Pushed!"); + Thread.Sleep(Interval * 1000); + } + } +} diff --git a/extra/push-examples/go/index.go b/extra/push-examples/go/index.go new file mode 100644 index 0000000..2e518e7 --- /dev/null +++ b/extra/push-examples/go/index.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + "net/http" + "time" +) + +func main() { + const PushURL = "https://example.com/api/push/key?status=up&msg=OK&ping=" + const Interval = 60 + + for { + _, err := http.Get(PushURL) + if err == nil { + fmt.Println("Pushed!") + } + time.Sleep(Interval * time.Second) + } +} diff --git a/extra/push-examples/java/index.java b/extra/push-examples/java/index.java new file mode 100644 index 0000000..5a77342 --- /dev/null +++ b/extra/push-examples/java/index.java @@ -0,0 +1,32 @@ +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * Compile: javac index.java + * Run: java Index + */ +class Index { + + public static final String PUSH_URL = "https://example.com/api/push/key?status=up&msg=OK&ping="; + public static final int INTERVAL = 60; + + public static void main(String[] args) { + while (true) { + try { + URL url = new URL(PUSH_URL); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod("GET"); + con.getResponseCode(); + con.disconnect(); + System.out.println("Pushed!"); + } catch (Exception e) { + e.printStackTrace(); + } + try { + Thread.sleep(INTERVAL * 1000); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/extra/push-examples/javascript-fetch/index.js b/extra/push-examples/javascript-fetch/index.js new file mode 100644 index 0000000..9d21d0d --- /dev/null +++ b/extra/push-examples/javascript-fetch/index.js @@ -0,0 +1,11 @@ +// Supports: Node.js >= 18, Deno, Bun +const pushURL = "https://example.com/api/push/key?status=up&msg=OK&ping="; +const interval = 60; + +const push = async () => { + await fetch(pushURL); + console.log("Pushed!"); +}; + +push(); +setInterval(push, interval * 1000); diff --git a/extra/push-examples/javascript-fetch/package.json b/extra/push-examples/javascript-fetch/package.json new file mode 100644 index 0000000..78aefc2 --- /dev/null +++ b/extra/push-examples/javascript-fetch/package.json @@ -0,0 +1,5 @@ +{ + "scripts": { + "start": "node index.js" + } +} diff --git a/extra/push-examples/php/index.php b/extra/push-examples/php/index.php new file mode 100644 index 0000000..d08b451 --- /dev/null +++ b/extra/push-examples/php/index.php @@ -0,0 +1,13 @@ += 18 (ts-node) +const pushURL: string = "https://example.com/api/push/key?status=up&msg=OK&ping="; +const interval: number = 60; + +const push = async () => { + await fetch(pushURL); + console.log("Pushed!"); +}; + +push(); +setInterval(push, interval * 1000); diff --git a/extra/push-examples/typescript-fetch/package.json b/extra/push-examples/typescript-fetch/package.json new file mode 100644 index 0000000..9d7e974 --- /dev/null +++ b/extra/push-examples/typescript-fetch/package.json @@ -0,0 +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" + } +} diff --git a/extra/rdap-dns.json b/extra/rdap-dns.json new file mode 100644 index 0000000..8c6842f --- /dev/null +++ b/extra/rdap-dns.json @@ -0,0 +1,1197 @@ +{ + "description": "RDAP bootstrap file for Domain Name System registrations", + "publication": "2026-02-13T23:00:01Z", + "services": [ + [["kg"], ["http://rdap.cctld.kg/"]], + [["mg"], ["http://rdap.nic.mg/"]], + [["ng"], ["http://rdap.nic.net.ng/"]], + [["xn--kpry57d"], ["https://ccrdap.twnic.tw/taiwan/"]], + [["tw"], ["https://ccrdap.twnic.tw/tw/"]], + [["na"], ["https://keetmans.omadhina.co.na/"]], + [["samsung", "xn--cg4bki"], ["https://nic.samsung/rdap/"]], + [ + [ + "ads", + "android", + "app", + "boo", + "cal", + "channel", + "chrome", + "dad", + "day", + "dclk", + "dev", + "docs", + "drive", + "eat", + "esq", + "fly", + "foo", + "gbiz", + "gle", + "gmail", + "goog", + "google", + "guge", + "hangout", + "here", + "how", + "ing", + "map", + "meet", + "meme", + "mov", + "new", + "nexus", + "page", + "phd", + "play", + "prod", + "prof", + "rsvp", + "search", + "soy", + "xn--flw351e", + "xn--q9jyb4c", + "xn--qcka1pmc", + "youtube", + "zip" + ], + ["https://pubapi.registry.google/rdap/"] + ], + [["blog"], ["https://rdap.blog.fury.ca/rdap/"]], + [["ca"], ["https://rdap.ca.fury.ca/rdap/"]], + [["au"], ["https://rdap.cctld.au/rdap/"]], + [["uz"], ["https://rdap.cctld.uz/"]], + [["allfinanz"], ["https://rdap.centralnic.com/allfinanz/"]], + [["art"], ["https://rdap.centralnic.com/art/"]], + [["audio"], ["https://rdap.centralnic.com/audio/"]], + [["auto"], ["https://rdap.centralnic.com/auto/"]], + [["autos"], ["https://rdap.centralnic.com/autos/"]], + [["baby"], ["https://rdap.centralnic.com/baby/"]], + [["beauty"], ["https://rdap.centralnic.com/beauty/"]], + [["best"], ["https://rdap.centralnic.com/best/"]], + [["bmw"], ["https://rdap.centralnic.com/bmw/"]], + [["boats"], ["https://rdap.centralnic.com/boats/"]], + [["bond"], ["https://rdap.centralnic.com/bond/"]], + [["box"], ["https://rdap.centralnic.com/box/"]], + [["build"], ["https://rdap.centralnic.com/build/"]], + [["cam"], ["https://rdap.centralnic.com/cam/"]], + [["car"], ["https://rdap.centralnic.com/car/"]], + [["cars"], ["https://rdap.centralnic.com/cars/"]], + [["case"], ["https://rdap.centralnic.com/case/"]], + [["ceo"], ["https://rdap.centralnic.com/ceo/"]], + [["cfd"], ["https://rdap.centralnic.com/cfd/"]], + [["christmas"], ["https://rdap.centralnic.com/christmas/"]], + [["college"], ["https://rdap.centralnic.com/college/"]], + [["cyou"], ["https://rdap.centralnic.com/cyou/"]], + [["dealer"], ["https://rdap.centralnic.com/dealer/"]], + [["deloitte"], ["https://rdap.centralnic.com/deloitte/"]], + [["dhl"], ["https://rdap.centralnic.com/dhl/"]], + [["diet"], ["https://rdap.centralnic.com/diet/"]], + [["dvag"], ["https://rdap.centralnic.com/dvag/"]], + [["fans"], ["https://rdap.centralnic.com/fans/"]], + [["flowers"], ["https://rdap.centralnic.com/flowers/"]], + [["fm"], ["https://rdap.centralnic.com/fm/"]], + [["fo"], ["https://rdap.centralnic.com/fo/"]], + [["fresenius"], ["https://rdap.centralnic.com/fresenius/"]], + [["frl"], ["https://rdap.centralnic.com/frl/"]], + [["fun"], ["https://rdap.centralnic.com/fun/"]], + [["game"], ["https://rdap.centralnic.com/game/"]], + [["gd"], ["https://rdap.centralnic.com/gd/"]], + [["gent"], ["https://rdap.centralnic.com/gent/"]], + [["guitars"], ["https://rdap.centralnic.com/guitars/"]], + [["hair"], ["https://rdap.centralnic.com/hair/"]], + [["help"], ["https://rdap.centralnic.com/help/"]], + [["homes"], ["https://rdap.centralnic.com/homes/"]], + [["host"], ["https://rdap.centralnic.com/host/"]], + [["hosting"], ["https://rdap.centralnic.com/hosting/"]], + [["icu"], ["https://rdap.centralnic.com/icu/"]], + [["inc"], ["https://rdap.centralnic.com/inc/"]], + [["kfh"], ["https://rdap.centralnic.com/kfh/"]], + [["kpn"], ["https://rdap.centralnic.com/kpn/"]], + [["kred"], ["https://rdap.centralnic.com/kred/"]], + [["lat"], ["https://rdap.centralnic.com/lat/"]], + [["lidl"], ["https://rdap.centralnic.com/lidl/"]], + [["llp"], ["https://rdap.centralnic.com/llp/"]], + [["lol"], ["https://rdap.centralnic.com/lol/"]], + [["london"], ["https://rdap.centralnic.com/london/"]], + [["lpl"], ["https://rdap.centralnic.com/lpl/"]], + [["lplfinancial"], ["https://rdap.centralnic.com/lplfinancial/"]], + [["luxury"], ["https://rdap.centralnic.com/luxury/"]], + [["makeup"], ["https://rdap.centralnic.com/makeup/"]], + [["mini"], ["https://rdap.centralnic.com/mini/"]], + [["mom"], ["https://rdap.centralnic.com/mom/"]], + [["monster"], ["https://rdap.centralnic.com/monster/"]], + [["motorcycles"], ["https://rdap.centralnic.com/motorcycles/"]], + [["nokia"], ["https://rdap.centralnic.com/nokia/"]], + [["online"], ["https://rdap.centralnic.com/online/"]], + [["ooo"], ["https://rdap.centralnic.com/ooo/"]], + [["pics"], ["https://rdap.centralnic.com/pics/"]], + [["pohl"], ["https://rdap.centralnic.com/pohl/"]], + [["press"], ["https://rdap.centralnic.com/press/"]], + [["protection"], ["https://rdap.centralnic.com/protection/"]], + [["pw"], ["https://rdap.centralnic.com/pw/"]], + [["qpon"], ["https://rdap.centralnic.com/qpon/"]], + [["quest"], ["https://rdap.centralnic.com/quest/"]], + [["reit"], ["https://rdap.centralnic.com/reit/"]], + [["rent"], ["https://rdap.centralnic.com/rent/"]], + [["ruhr"], ["https://rdap.centralnic.com/ruhr/"]], + [["saarland"], ["https://rdap.centralnic.com/saarland/"]], + [["sbs"], ["https://rdap.centralnic.com/sbs/"]], + [["schwarz"], ["https://rdap.centralnic.com/schwarz/"]], + [["security"], ["https://rdap.centralnic.com/security/"]], + [["sfr"], ["https://rdap.centralnic.com/sfr/"]], + [["site"], ["https://rdap.centralnic.com/site/"]], + [["skin"], ["https://rdap.centralnic.com/skin/"]], + [["smart"], ["https://rdap.centralnic.com/smart/"]], + [["space"], ["https://rdap.centralnic.com/space/"]], + [["stc"], ["https://rdap.centralnic.com/stc/"]], + [["stcgroup"], ["https://rdap.centralnic.com/stcgroup/"]], + [["storage"], ["https://rdap.centralnic.com/storage/"]], + [["store"], ["https://rdap.centralnic.com/store/"]], + [["tech"], ["https://rdap.centralnic.com/tech/"]], + [["theatre"], ["https://rdap.centralnic.com/theatre/"]], + [["tickets"], ["https://rdap.centralnic.com/tickets/"]], + [["tui"], ["https://rdap.centralnic.com/tui/"]], + [["uno"], ["https://rdap.centralnic.com/uno/"]], + [["vg"], ["https://rdap.centralnic.com/vg/"]], + [["viva"], ["https://rdap.centralnic.com/viva/"]], + [["website"], ["https://rdap.centralnic.com/website/"]], + [["wme"], ["https://rdap.centralnic.com/wme/"]], + [["xn--4gbrim"], ["https://rdap.centralnic.com/xn--4gbrim/"]], + [["xn--ngbe9e0a"], ["https://rdap.centralnic.com/xn--ngbe9e0a/"]], + [["xn--vermgensberater-ctb"], ["https://rdap.centralnic.com/xn--vermgensberater-ctb/"]], + [["xn--vermgensberatung-pwb"], ["https://rdap.centralnic.com/xn--vermgensberatung-pwb/"]], + [["xyz"], ["https://rdap.centralnic.com/xyz/"]], + [["yachts"], ["https://rdap.centralnic.com/yachts/"]], + [["zuerich"], ["https://rdap.centralnic.com/zuerich/"]], + [["jnj"], ["https://rdap.centralnicregistry.com/jnj/"]], + [["xn--55qw42g", "xn--zfr164b"], ["https://rdap.conac.cn/"]], + [["crown"], ["https://rdap.crown.fury.ca/rdap/"]], + [["pl"], ["https://rdap.dns.pl/"]], + [["eco"], ["https://rdap.eco.fury.ca/rdap/"]], + [["fi"], ["https://rdap.fi/rdap/rdap/"]], + [["moscow", "xn--80adxhks"], ["https://rdap.flexireg.net/"]], + [ + [ + "bridgestone", + "brother", + "canon", + "datsun", + "dnp", + "epson", + "firestone", + "fujitsu", + "ggee", + "gmo", + "goldpoint", + "hisamitsu", + "hitachi", + "honda", + "hyundai", + "infiniti", + "jcb", + "kddi", + "kia", + "komatsu", + "kyoto", + "lexus", + "lotte", + "mitsubishi", + "nagoya", + "nec", + "nhk", + "nico", + "nissan", + "okinawa", + "otsuka", + "panasonic", + "playstation", + "ricoh", + "ryukyu", + "sharp", + "shop", + "softbank", + "sony", + "suzuki", + "tokyo", + "toray", + "toshiba", + "toyota", + "yodobashi", + "yokohama" + ], + ["https://rdap.gmoregistry.net/rdap/"] + ], + [["bom", "final", "globo", "rio", "uol"], ["https://rdap.gtlds.nic.br/"]], + [["ua"], ["https://rdap.hostmaster.ua/"]], + [["int"], ["https://rdap.iana.org/"]], + [ + [ + "abb", + "abbott", + "abc", + "academy", + "accenture", + "accountants", + "actor", + "aeg", + "aero", + "agakhan", + "agency", + "ai", + "airbus", + "airforce", + "akdn", + "alibaba", + "alipay", + "allstate", + "aol", + "apartments", + "archi", + "army", + "arte", + "asda", + "asia", + "associates", + "attorney", + "auction", + "audi", + "band", + "barclaycard", + "barclays", + "barefoot", + "bargains", + "bbt", + "bcg", + "beats", + "bestbuy", + "bet", + "bike", + "bingo", + "bio", + "black", + "bloomberg", + "blue", + "bm", + "bms", + "bnpparibas", + "boehringer", + "bofa", + "bosch", + "boutique", + "bradesco", + "broker", + "builders", + "business", + "cab", + "cafe", + "camera", + "camp", + "capital", + "cards", + "care", + "careers", + "cash", + "casino", + "catering", + "center", + "cern", + "cfa", + "chanel", + "chat", + "cheap", + "church", + "cipriani", + "citadel", + "city", + "claims", + "cleaning", + "clinic", + "clinique", + "clothing", + "clubmed", + "coach", + "codes", + "coffee", + "community", + "company", + "computer", + "condos", + "construction", + "consulting", + "contact", + "contractors", + "cool", + "coupon", + "coupons", + "credit", + "creditcard", + "crs", + "cruise", + "cruises", + "dance", + "dating", + "deals", + "degree", + "delivery", + "delta", + "democrat", + "dental", + "dentist", + "diamonds", + "digital", + "direct", + "directory", + "discount", + "discover", + "doctor", + "dog", + "domains", + "edeka", + "education", + "email", + "emerck", + "energy", + "engineer", + "engineering", + "enterprises", + "equipment", + "ericsson", + "estate", + "events", + "exchange", + "expert", + "exposed", + "express", + "extraspace", + "fage", + "fail", + "family", + "fan", + "farm", + "fedex", + "ferrari", + "fidelity", + "fido", + "finance", + "financial", + "fish", + "fitness", + "flights", + "florist", + "football", + "forex", + "forsale", + "frogans", + "fund", + "furniture", + "futbol", + "fyi", + "gallery", + "gallo", + "gallup", + "games", + "genting", + "gifts", + "glass", + "global", + "gmbh", + "gold", + "golf", + "goodyear", + "graphics", + "gratis", + "green", + "gripe", + "group", + "guide", + "guru", + "haus", + "hdfc", + "hdfcbank", + "healthcare", + "helsinki", + "hermes", + "hkt", + "hockey", + "holdings", + "holiday", + "homedepot", + "hospital", + "house", + "hughes", + "ice", + "imamat", + "immo", + "immobilien", + "industries", + "info", + "institute", + "insure", + "international", + "investments", + "irish", + "ismaili", + "ist", + "istanbul", + "itv", + "jaguar", + "java", + "jeep", + "jetzt", + "jewelry", + "jio", + "jll", + "juegos", + "juniper", + "kaufen", + "kerryhotels", + "kerryproperties", + "kids", + "kim", + "kitchen", + "kosher", + "kuokgroup", + "lamborghini", + "lamer", + "land", + "landrover", + "lasalle", + "lawyer", + "lds", + "lease", + "lefrak", + "legal", + "lego", + "lgbt", + "life", + "lighting", + "limited", + "limo", + "live", + "llc", + "loans", + "lotto", + "ltd", + "ltda", + "lundbeck", + "maif", + "maison", + "management", + "market", + "marketing", + "markets", + "marriott", + "mba", + "mckinsey", + "media", + "memorial", + "mit", + "mobi", + "moda", + "money", + "mormon", + "mortgage", + "movie", + "mu", + "nab", + "navy", + "network", + "news", + "next", + "nextdirect", + "nikon", + "ninja", + "nissay", + "nowtv", + "nra", + "obi", + "onl", + "oracle", + "orange", + "organic", + "origins", + "partners", + "parts", + "pccw", + "pet", + "photography", + "photos", + "pictet", + "pictures", + "pink", + "pizza", + "place", + "plumbing", + "plus", + "pnc", + "poker", + "post", + "pro", + "productions", + "progressive", + "promo", + "properties", + "pub", + "pwc", + "recipes", + "red", + "redumbrella", + "rehab", + "reise", + "reisen", + "reliance", + "rentals", + "repair", + "report", + "republican", + "restaurant", + "reviews", + "rexroth", + "rich", + "richardli", + "ril", + "rip", + "rocks", + "rogers", + "run", + "rwe", + "sale", + "salon", + "sanofi", + "sarl", + "saxo", + "sbi", + "scholarships", + "school", + "schule", + "sener", + "services", + "sew", + "shangrila", + "shiksha", + "shoes", + "shopping", + "show", + "sina", + "singles", + "ski", + "soccer", + "social", + "software", + "solar", + "solutions", + "song", + "spa", + "srl", + "stada", + "star", + "statebank", + "stockholm", + "studio", + "style", + "supplies", + "supply", + "support", + "surgery", + "systems", + "taobao", + "tatamotors", + "tax", + "taxi", + "team", + "technology", + "temasek", + "tennis", + "thd", + "theater", + "tiaa", + "tienda", + "tips", + "tires", + "tmall", + "today", + "tools", + "tours", + "town", + "toys", + "trading", + "training", + "travel", + "travelers", + "travelersinsurance", + "trv", + "tvs", + "ubank", + "ubs", + "university", + "ups", + "vacations", + "vanguard", + "vegas", + "ventures", + "vet", + "viajes", + "video", + "vig", + "viking", + "villas", + "vin", + "visa", + "vision", + "volvo", + "vote", + "voto", + "voyage", + "watch", + "watches", + "weber", + "weibo", + "weir", + "wine", + "works", + "world", + "wtf", + "xin", + "xn--1ck2e1b", + "xn--5su34j936bgsg", + "xn--5tzm5g", + "xn--6frz82g", + "xn--9krt00a", + "xn--b4w605ferd", + "xn--bck1b9a5dre4c", + "xn--cck2b3b", + "xn--czrs0t", + "xn--eckvdtc9d", + "xn--fct429k", + "xn--fjq720a", + "xn--fzys8d69uvgm", + "xn--gckr3f0f", + "xn--gk3at1e", + "xn--jvr189m", + "xn--rovu88b", + "xn--unup4y", + "xn--vhquv", + "xn--w4r85el8fhu5dnra", + "xn--w4rs40l", + "yahoo", + "zara", + "zero", + "zone" + ], + ["https://rdap.identitydigital.services/rdap/"] + ], + [["is"], ["https://rdap.isnic.is/rdap/"]], + [["ke"], ["https://rdap.kenic.or.ke/"]], + [["kiwi"], ["https://rdap.kiwi.fury.ca/rdap/"]], + [["lb"], ["https://rdap.lbdr.org.lb/"]], + [["mls"], ["https://rdap.mls.fury.ca/rdap/"]], + [ + ["blockbuster", "data", "dish", "dot", "dtv", "dvr", "latino", "mobile", "ollo", "ott", "phone", "sling"], + ["https://rdap.mobile-registry.com/rdap/"] + ], + [["aaa"], ["https://rdap.nic.aaa/"]], + [["aarp"], ["https://rdap.nic.aarp/"]], + [["able"], ["https://rdap.nic.able/"]], + [["abogado"], ["https://rdap.nic.abogado/"]], + [["abudhabi"], ["https://rdap.nic.abudhabi/"]], + [["accountant"], ["https://rdap.nic.accountant/"]], + [["aco"], ["https://rdap.nic.aco/"]], + [["ad"], ["https://rdap.nic.ad/"]], + [["adult"], ["https://rdap.nic.adult/"]], + [["aetna"], ["https://rdap.nic.aetna/"]], + [["afl"], ["https://rdap.nic.afl/"]], + [["africa"], ["https://rdap.nic.africa/rdap/"]], + [["aig"], ["https://rdap.nic.aig/"]], + [["airtel"], ["https://rdap.nic.airtel/"]], + [["ally"], ["https://rdap.nic.ally/"]], + [["alsace"], ["https://rdap.nic.alsace/"]], + [["alstom"], ["https://rdap.nic.alstom/"]], + [["americanexpress"], ["https://rdap.nic.americanexpress/"]], + [["americanfamily"], ["https://rdap.nic.americanfamily/"]], + [["amex"], ["https://rdap.nic.amex/"]], + [["amfam"], ["https://rdap.nic.amfam/"]], + [["amica"], ["https://rdap.nic.amica/"]], + [["amsterdam"], ["https://rdap.nic.amsterdam/"]], + [["analytics"], ["https://rdap.nic.analytics/"]], + [["anz"], ["https://rdap.nic.anz/"]], + [["apple"], ["https://rdap.nic.apple/"]], + [["aquarelle"], ["https://rdap.nic.aquarelle/"]], + [["ar"], ["https://rdap.nic.ar/"]], + [["arab"], ["https://rdap.nic.arab/"]], + [["aramco"], ["https://rdap.nic.aramco/"]], + [["as"], ["https://rdap.nic.as/"]], + [["athleta"], ["https://rdap.nic.athleta/"]], + [["auspost"], ["https://rdap.nic.auspost/"]], + [["axa"], ["https://rdap.nic.axa/"]], + [["banamex"], ["https://rdap.nic.banamex/"]], + [["bank"], ["https://rdap.nic.bank/"]], + [["barcelona"], ["https://rdap.nic.barcelona/"]], + [["baseball"], ["https://rdap.nic.baseball/"]], + [["basketball"], ["https://rdap.nic.basketball/"]], + [["bauhaus"], ["https://rdap.nic.bauhaus/"]], + [["bayern"], ["https://rdap.nic.bayern/"]], + [["bcn"], ["https://rdap.nic.bcn/"]], + [["beer"], ["https://rdap.nic.beer/"]], + [["berlin"], ["https://rdap.nic.berlin/v1/"]], + [["bharti"], ["https://rdap.nic.bharti/"]], + [["bible"], ["https://rdap.nic.bible/"]], + [["bid"], ["https://rdap.nic.bid/"]], + [["biz"], ["https://rdap.nic.biz/"]], + [["blackfriday"], ["https://rdap.nic.blackfriday/"]], + [["booking"], ["https://rdap.nic.booking/"]], + [["bostik"], ["https://rdap.nic.bostik/"]], + [["boston"], ["https://rdap.nic.boston/"]], + [["brussels"], ["https://rdap.nic.brussels/"]], + [["buzz"], ["https://rdap.nic.buzz/"]], + [["bzh"], ["https://rdap.nic.bzh/"]], + [["calvinklein"], ["https://rdap.nic.calvinklein/"]], + [["capetown"], ["https://rdap.nic.capetown/rdap/"]], + [["capitalone"], ["https://rdap.nic.capitalone/"]], + [["caravan"], ["https://rdap.nic.caravan/"]], + [["casa"], ["https://rdap.nic.casa/"]], + [["cat"], ["https://rdap.nic.cat/"]], + [["catholic"], ["https://rdap.nic.catholic/"]], + [["cba"], ["https://rdap.nic.cba/"]], + [["cbn"], ["https://rdap.nic.cbn/"]], + [["cbre"], ["https://rdap.nic.cbre/"]], + [["chase"], ["https://rdap.nic.chase/"]], + [["chintai"], ["https://rdap.nic.chintai/"]], + [["cisco"], ["https://rdap.nic.cisco/"]], + [["citi"], ["https://rdap.nic.citi/"]], + [["club"], ["https://rdap.nic.club/"]], + [["cm"], ["https://rdap.nic.cm/"]], + [["commbank"], ["https://rdap.nic.commbank/"]], + [["compare"], ["https://rdap.nic.compare/"]], + [["cooking"], ["https://rdap.nic.cooking/"]], + [["corsica"], ["https://rdap.nic.corsica/"]], + [["courses"], ["https://rdap.nic.courses/"]], + [["cpa"], ["https://rdap.nic.cpa/"]], + [["cr"], ["https://rdap.nic.cr/"]], + [["cricket"], ["https://rdap.nic.cricket/"]], + [["cuisinella"], ["https://rdap.nic.cuisinella/"]], + [["cv"], ["https://rdap.nic.cv/"]], + [["cx"], ["https://rdap.nic.cx/"]], + [["cz"], ["https://rdap.nic.cz/"]], + [["date"], ["https://rdap.nic.date/"]], + [["dds"], ["https://rdap.nic.dds/"]], + [["dell"], ["https://rdap.nic.dell/"]], + [["design"], ["https://rdap.nic.design/"]], + [["download"], ["https://rdap.nic.download/"]], + [["dubai"], ["https://rdap.nic.dubai/"]], + [["dupont"], ["https://rdap.nic.dupont/"]], + [["durban"], ["https://rdap.nic.durban/rdap/"]], + [["earth"], ["https://rdap.nic.earth/"]], + [["erni"], ["https://rdap.nic.erni/"]], + [["eurovision"], ["https://rdap.nic.eurovision/"]], + [["eus"], ["https://rdap.nic.eus/"]], + [["faith"], ["https://rdap.nic.faith/"]], + [["farmers"], ["https://rdap.nic.farmers/"]], + [["fashion"], ["https://rdap.nic.fashion/"]], + [["ferrero"], ["https://rdap.nic.ferrero/"]], + [["film"], ["https://rdap.nic.film/"]], + [["firmdale"], ["https://rdap.nic.firmdale/"]], + [["fishing"], ["https://rdap.nic.fishing/"]], + [["fit"], ["https://rdap.nic.fit/"]], + [["flickr"], ["https://rdap.nic.flickr/"]], + [["flir"], ["https://rdap.nic.flir/"]], + [["ford"], ["https://rdap.nic.ford/"]], + [["fox"], ["https://rdap.nic.fox/"]], + [["fr"], ["https://rdap.nic.fr/"]], + [["frontier"], ["https://rdap.nic.frontier/"]], + [["ftr"], ["https://rdap.nic.ftr/"]], + [["gal"], ["https://rdap.nic.gal/"]], + [["gap"], ["https://rdap.nic.gap/"]], + [["garden"], ["https://rdap.nic.garden/"]], + [["gay"], ["https://rdap.nic.gay/"]], + [["gdn"], ["https://rdap.nic.gdn/"]], + [["gea"], ["https://rdap.nic.gea/"]], + [["george"], ["https://rdap.nic.george/"]], + [["gmx"], ["https://rdap.nic.gmx/"]], + [["godaddy"], ["https://rdap.nic.godaddy/"]], + [["gov"], ["https://rdap.nic.gov/rdap/"]], + [["grainger"], ["https://rdap.nic.grainger/"]], + [["grocery"], ["https://rdap.nic.grocery/"]], + [["gs"], ["https://rdap.nic.gs/"]], + [["hamburg"], ["https://rdap.nic.hamburg/v1/"]], + [["hbo"], ["https://rdap.nic.hbo/"]], + [["health"], ["https://rdap.nic.health/"]], + [["hn"], ["https://rdap.nic.hn/"]], + [["homegoods"], ["https://rdap.nic.homegoods/"]], + [["homesense"], ["https://rdap.nic.homesense/"]], + [["horse"], ["https://rdap.nic.horse/"]], + [["hotels"], ["https://rdap.nic.hotels/"]], + [["hsbc"], ["https://rdap.nic.hsbc/"]], + [["ht"], ["https://rdap.nic.ht/"]], + [["hyatt"], ["https://rdap.nic.hyatt/"]], + [["ibm"], ["https://rdap.nic.ibm/"]], + [["ifm"], ["https://rdap.nic.ifm/"]], + [["ikano"], ["https://rdap.nic.ikano/v1/"]], + [["ink"], ["https://rdap.nic.ink/"]], + [["insurance"], ["https://rdap.nic.insurance/"]], + [["intuit"], ["https://rdap.nic.intuit/"]], + [["ipiranga"], ["https://rdap.nic.ipiranga/"]], + [["itau"], ["https://rdap.nic.itau/"]], + [["jmp"], ["https://rdap.nic.jmp/"]], + [["joburg"], ["https://rdap.nic.joburg/rdap/"]], + [["jpmorgan"], ["https://rdap.nic.jpmorgan/"]], + [["jprs"], ["https://rdap.nic.jprs/rdap/"]], + [["kpmg"], ["https://rdap.nic.kpmg/"]], + [["krd"], ["https://rdap.nic.krd/"]], + [["lacaixa"], ["https://rdap.nic.lacaixa/"]], + [["lanxess"], ["https://rdap.nic.lanxess/"]], + [["latrobe"], ["https://rdap.nic.latrobe/"]], + [["law"], ["https://rdap.nic.law/"]], + [["leclerc"], ["https://rdap.nic.leclerc/"]], + [["lifeinsurance"], ["https://rdap.nic.lifeinsurance/"]], + [["lilly"], ["https://rdap.nic.lilly/"]], + [["lincoln"], ["https://rdap.nic.lincoln/"]], + [["loan"], ["https://rdap.nic.loan/"]], + [["locker"], ["https://rdap.nic.locker/rdap/"]], + [["luxe"], ["https://rdap.nic.luxe/"]], + [["ly"], ["https://rdap.nic.ly/"]], + [["madrid"], ["https://rdap.nic.madrid/"]], + [["man"], ["https://rdap.nic.man/"]], + [["mango"], ["https://rdap.nic.mango/"]], + [["marshalls"], ["https://rdap.nic.marshalls/"]], + [["mattel"], ["https://rdap.nic.mattel/"]], + [["melbourne"], ["https://rdap.nic.melbourne/"]], + [["men"], ["https://rdap.nic.men/"]], + [["menu"], ["https://rdap.nic.menu/"]], + [["merckmsd"], ["https://rdap.nic.merckmsd/"]], + [["miami"], ["https://rdap.nic.miami/"]], + [["mint"], ["https://rdap.nic.mint/"]], + [["ml"], ["https://rdap.nic.ml/"]], + [["mlb"], ["https://rdap.nic.mlb/"]], + [["mma"], ["https://rdap.nic.mma/"]], + [["moe"], ["https://rdap.nic.moe/"]], + [["monash"], ["https://rdap.nic.monash/"]], + [["moto"], ["https://rdap.nic.moto/"]], + [["ms"], ["https://rdap.nic.ms/"]], + [["msd"], ["https://rdap.nic.msd/"]], + [["museum"], ["https://rdap.nic.museum/"]], + [["nba"], ["https://rdap.nic.nba/"]], + [["netbank"], ["https://rdap.nic.netbank/"]], + [["netflix"], ["https://rdap.nic.netflix/"]], + [["neustar"], ["https://rdap.nic.neustar/"]], + [["nf"], ["https://rdap.nic.nf/"]], + [["nfl"], ["https://rdap.nic.nfl/"]], + [["nike"], ["https://rdap.nic.nike/"]], + [["norton"], ["https://rdap.nic.norton/"]], + [["nrw"], ["https://rdap.nic.nrw/"]], + [["ntt"], ["https://rdap.nic.ntt/rdap/"]], + [["nyc"], ["https://rdap.nic.nyc/"]], + [["olayan"], ["https://rdap.nic.olayan/"]], + [["olayangroup"], ["https://rdap.nic.olayangroup/"]], + [["one"], ["https://rdap.nic.one/"]], + [["open"], ["https://rdap.nic.open/"]], + [["osaka"], ["https://rdap.nic.osaka/"]], + [["ovh"], ["https://rdap.nic.ovh/"]], + [["paris"], ["https://rdap.nic.paris/"]], + [["party"], ["https://rdap.nic.party/"]], + [["pfizer"], ["https://rdap.nic.pfizer/"]], + [["pg"], ["https://rdap.nic.pg/"]], + [["philips"], ["https://rdap.nic.philips/"]], + [["photo"], ["https://rdap.nic.photo/"]], + [["physio"], ["https://rdap.nic.physio/"]], + [["ping"], ["https://rdap.nic.ping/"]], + [["pm"], ["https://rdap.nic.pm/"]], + [["politie"], ["https://rdap.nic.politie/"]], + [["porn"], ["https://rdap.nic.porn/"]], + [["praxi"], ["https://rdap.nic.praxi/"]], + [["pru"], ["https://rdap.nic.pru/"]], + [["prudential"], ["https://rdap.nic.prudential/"]], + [["quebec"], ["https://rdap.nic.quebec/"]], + [["racing"], ["https://rdap.nic.racing/"]], + [["radio"], ["https://rdap.nic.radio/"]], + [["re"], ["https://rdap.nic.re/"]], + [["review"], ["https://rdap.nic.review/"]], + [["rodeo"], ["https://rdap.nic.rodeo/"]], + [["rugby"], ["https://rdap.nic.rugby/"]], + [["safety"], ["https://rdap.nic.safety/"]], + [["sakura"], ["https://rdap.nic.sakura/rdap/"]], + [["samsclub"], ["https://rdap.nic.samsclub/"]], + [["sandvik"], ["https://rdap.nic.sandvik/"]], + [["sandvikcoromant"], ["https://rdap.nic.sandvikcoromant/"]], + [["sap"], ["https://rdap.nic.sap/"]], + [["sas"], ["https://rdap.nic.sas/"]], + [["scb"], ["https://rdap.nic.scb/"]], + [["schaeffler"], ["https://rdap.nic.schaeffler/"]], + [["schmidt"], ["https://rdap.nic.schmidt/"]], + [["science"], ["https://rdap.nic.science/"]], + [["scot"], ["https://rdap.nic.scot/"]], + [["sd"], ["https://rdap.nic.sd/"]], + [["seat"], ["https://rdap.nic.seat/"]], + [["seek"], ["https://rdap.nic.seek/"]], + [["select"], ["https://rdap.nic.select/"]], + [["seven"], ["https://rdap.nic.seven/"]], + [["sex"], ["https://rdap.nic.sex/"]], + [["sncf"], ["https://rdap.nic.sncf/"]], + [["sport"], ["https://rdap.nic.sport/"]], + [["ss"], ["https://rdap.nic.ss/"]], + [["staples"], ["https://rdap.nic.staples/"]], + [["statefarm"], ["https://rdap.nic.statefarm/"]], + [["stream"], ["https://rdap.nic.stream/"]], + [["study"], ["https://rdap.nic.study/"]], + [["sucks"], ["https://rdap.nic.sucks/"]], + [["surf"], ["https://rdap.nic.surf/"]], + [["swiss"], ["https://rdap.nic.swiss/"]], + [["sydney"], ["https://rdap.nic.sydney/"]], + [["tab"], ["https://rdap.nic.tab/"]], + [["taipei"], ["https://rdap.nic.taipei/"]], + [["target"], ["https://rdap.nic.target/"]], + [["tattoo"], ["https://rdap.nic.tattoo/"]], + [["tdk"], ["https://rdap.nic.tdk/"]], + [["tel"], ["https://rdap.nic.tel/"]], + [["teva"], ["https://rdap.nic.teva/"]], + [["tf"], ["https://rdap.nic.tf/"]], + [["tjmaxx"], ["https://rdap.nic.tjmaxx/"]], + [["tjx"], ["https://rdap.nic.tjx/"]], + [["tkmaxx"], ["https://rdap.nic.tkmaxx/"]], + [["total"], ["https://rdap.nic.total/"]], + [["trade"], ["https://rdap.nic.trade/"]], + [["tube"], ["https://rdap.nic.tube/"]], + [["tv"], ["https://rdap.nic.tv/"]], + [["versicherung"], ["https://rdap.nic.versicherung/v1/"]], + [["vi"], ["https://rdap.nic.vi/"]], + [["vip"], ["https://rdap.nic.vip/"]], + [["vivo"], ["https://rdap.nic.vivo/"]], + [["vlaanderen"], ["https://rdap.nic.vlaanderen/"]], + [["vodka"], ["https://rdap.nic.vodka/"]], + [["voting"], ["https://rdap.nic.voting/"]], + [["walmart"], ["https://rdap.nic.walmart/"]], + [["walter"], ["https://rdap.nic.walter/"]], + [["weather"], ["https://rdap.nic.weather/"]], + [["weatherchannel"], ["https://rdap.nic.weatherchannel/"]], + [["webcam"], ["https://rdap.nic.webcam/"]], + [["wedding"], ["https://rdap.nic.wedding/"]], + [["wf"], ["https://rdap.nic.wf/"]], + [["whoswho"], ["https://rdap.nic.whoswho/"]], + [["wiki"], ["https://rdap.nic.wiki/"]], + [["williamhill"], ["https://rdap.nic.williamhill/"]], + [["win"], ["https://rdap.nic.win/"]], + [["winners"], ["https://rdap.nic.winners/"]], + [["woodside"], ["https://rdap.nic.woodside/"]], + [["work"], ["https://rdap.nic.work/"]], + [["wtc"], ["https://rdap.nic.wtc/"]], + [["xerox"], ["https://rdap.nic.xerox/"]], + [["xn--80aqecdr1a"], ["https://rdap.nic.xn--80aqecdr1a/"]], + [["xn--80asehdb"], ["https://rdap.nic.xn--80asehdb/"]], + [["xn--80aswg"], ["https://rdap.nic.xn--80aswg/"]], + [["xn--g2xx48c"], ["https://rdap.nic.xn--g2xx48c/"]], + [["xn--kcrx77d1x4a"], ["https://rdap.nic.xn--kcrx77d1x4a/"]], + [["xn--mgba3a3ejt"], ["https://rdap.nic.xn--mgba3a3ejt/"]], + [["xn--mgba7c0bbn0a"], ["https://rdap.nic.xn--mgba7c0bbn0a/"]], + [["xn--mgbab2bd"], ["https://rdap.nic.xn--mgbab2bd/"]], + [["xn--mgbca7dzdo"], ["https://rdap.nic.xn--mgbca7dzdo/"]], + [["xn--mgbi4ecexp"], ["https://rdap.nic.xn--mgbi4ecexp/"]], + [["xn--ngbc5azd"], ["https://rdap.nic.xn--ngbc5azd/"]], + [["xn--ngbrx"], ["https://rdap.nic.xn--ngbrx/"]], + [["xn--p1acf"], ["https://rdap.nic.xn--p1acf/"]], + [["xn--tiq49xqyj"], ["https://rdap.nic.xn--tiq49xqyj/"]], + [["xxx"], ["https://rdap.nic.xxx/"]], + [["yandex"], ["https://rdap.nic.yandex/rdap/"]], + [["yoga"], ["https://rdap.nic.yoga/"]], + [["yt"], ["https://rdap.nic.yt/"]], + [["zm"], ["https://rdap.nic.zm/"]], + [["in"], ["https://rdap.nixiregistry.in/rdap/"]], + [["abbvie"], ["https://rdap.nominet.uk/abbvie/"]], + [["amazon"], ["https://rdap.nominet.uk/amazon/"]], + [["audible"], ["https://rdap.nominet.uk/audible/"]], + [["author"], ["https://rdap.nominet.uk/author/"]], + [["aws"], ["https://rdap.nominet.uk/aws/"]], + [["azure"], ["https://rdap.nominet.uk/azure/"]], + [["bbc"], ["https://rdap.nominet.uk/bbc/"]], + [["bbva"], ["https://rdap.nominet.uk/bbva/"]], + [["bing"], ["https://rdap.nominet.uk/bing/"]], + [["book"], ["https://rdap.nominet.uk/book/"]], + [["bot"], ["https://rdap.nominet.uk/bot/"]], + [["broadway"], ["https://rdap.nominet.uk/broadway/"]], + [["buy"], ["https://rdap.nominet.uk/buy/"]], + [["call"], ["https://rdap.nominet.uk/call/"]], + [["career"], ["https://rdap.nominet.uk/career/"]], + [["circle"], ["https://rdap.nominet.uk/circle/"]], + [["cymru"], ["https://rdap.nominet.uk/cymru/"]], + [["deal"], ["https://rdap.nominet.uk/deal/"]], + [["desi"], ["https://rdap.nominet.uk/desi/"]], + [["fairwinds"], ["https://rdap.nominet.uk/fairwinds/"]], + [["fast"], ["https://rdap.nominet.uk/fast/"]], + [["fire"], ["https://rdap.nominet.uk/fire/"]], + [["free"], ["https://rdap.nominet.uk/free/"]], + [["gop"], ["https://rdap.nominet.uk/gop/"]], + [["got"], ["https://rdap.nominet.uk/got/"]], + [["gucci"], ["https://rdap.nominet.uk/gucci/"]], + [["hot"], ["https://rdap.nominet.uk/hot/"]], + [["hotmail"], ["https://rdap.nominet.uk/hotmail/"]], + [["ieee"], ["https://rdap.nominet.uk/ieee/"]], + [["imdb"], ["https://rdap.nominet.uk/imdb/"]], + [["jobs"], ["https://rdap.nominet.uk/jobs/"]], + [["jot"], ["https://rdap.nominet.uk/jot/"]], + [["joy"], ["https://rdap.nominet.uk/joy/"]], + [["kindle"], ["https://rdap.nominet.uk/kindle/"]], + [["like"], ["https://rdap.nominet.uk/like/"]], + [["locus"], ["https://rdap.nominet.uk/locus/"]], + [["med"], ["https://rdap.nominet.uk/med/"]], + [["microsoft"], ["https://rdap.nominet.uk/microsoft/"]], + [["moi"], ["https://rdap.nominet.uk/moi/"]], + [["mtn"], ["https://rdap.nominet.uk/mtn/"]], + [["now"], ["https://rdap.nominet.uk/now/"]], + [["nowruz"], ["https://rdap.nominet.uk/nowruz/"]], + [["office"], ["https://rdap.nominet.uk/office/"]], + [["omega"], ["https://rdap.nominet.uk/omega/"]], + [["pars"], ["https://rdap.nominet.uk/pars/"]], + [["pay"], ["https://rdap.nominet.uk/pay/"]], + [["pharmacy"], ["https://rdap.nominet.uk/pharmacy/"]], + [["pin"], ["https://rdap.nominet.uk/pin/"]], + [["pioneer"], ["https://rdap.nominet.uk/pioneer/"]], + [["pn"], ["https://rdap.nominet.uk/pn/"]], + [["prime"], ["https://rdap.nominet.uk/prime/"]], + [["read"], ["https://rdap.nominet.uk/read/"]], + [["realestate"], ["https://rdap.nominet.uk/realestate/"]], + [["realtor"], ["https://rdap.nominet.uk/realtor/"]], + [["room"], ["https://rdap.nominet.uk/room/"]], + [["safe"], ["https://rdap.nominet.uk/safe/"]], + [["save"], ["https://rdap.nominet.uk/save/"]], + [["secure"], ["https://rdap.nominet.uk/secure/"]], + [["shell"], ["https://rdap.nominet.uk/shell/"]], + [["shia"], ["https://rdap.nominet.uk/shia/"]], + [["silk"], ["https://rdap.nominet.uk/silk/"]], + [["sky"], ["https://rdap.nominet.uk/sky/"]], + [["skype"], ["https://rdap.nominet.uk/skype/"]], + [["smile"], ["https://rdap.nominet.uk/smile/"]], + [["spot"], ["https://rdap.nominet.uk/spot/"]], + [["swatch"], ["https://rdap.nominet.uk/swatch/"]], + [["talk"], ["https://rdap.nominet.uk/talk/"]], + [["tci"], ["https://rdap.nominet.uk/tci/"]], + [["tunes"], ["https://rdap.nominet.uk/tunes/"]], + [["tushu"], ["https://rdap.nominet.uk/tushu/"]], + [["uk"], ["https://rdap.nominet.uk/uk/"]], + [["virgin"], ["https://rdap.nominet.uk/virgin/"]], + [["wales"], ["https://rdap.nominet.uk/wales/"]], + [["wanggou"], ["https://rdap.nominet.uk/wanggou/"]], + [["wed"], ["https://rdap.nominet.uk/wed/"]], + [["windows"], ["https://rdap.nominet.uk/windows/"]], + [["wow"], ["https://rdap.nominet.uk/wow/"]], + [["xbox"], ["https://rdap.nominet.uk/xbox/"]], + [["xn--cckwcxetd"], ["https://rdap.nominet.uk/xn--cckwcxetd/"]], + [["xn--jlq480n2rg"], ["https://rdap.nominet.uk/xn--jlq480n2rg/"]], + [["xn--mgbt3dhd"], ["https://rdap.nominet.uk/xn--mgbt3dhd/"]], + [["yamaxun"], ["https://rdap.nominet.uk/yamaxun/"]], + [["you"], ["https://rdap.nominet.uk/you/"]], + [["zappos"], ["https://rdap.nominet.uk/zappos/"]], + [["no"], ["https://rdap.norid.no/"]], + [["id"], ["https://rdap.pandi.id/rdap/"]], + [ + [ + "charity", + "foundation", + "gives", + "giving", + "ngo", + "ong", + "org", + "xn--c1avg", + "xn--i1b6b1a6a2e", + "xn--nqv7f", + "xn--nqv7fs00ema" + ], + ["https://rdap.publicinterestregistry.org/rdap/"] + ], + [["si"], ["https://rdap.register.si/"]], + [["br"], ["https://rdap.registro.br/"]], + [["bar", "rest"], ["https://rdap.registry.bar/rdap/"]], + [["feedback", "forum", "observer", "pid", "realty"], ["https://rdap.registry.click/rdap/"]], + [["cloud"], ["https://rdap.registry.cloud/rdap/"]], + [["coop", "creditunion"], ["https://rdap.registry.coop/rdap/"]], + [["ec"], ["https://rdap.registry.ec/"]], + [["gy"], ["https://rdap.registry.gy/"]], + [["hiphop"], ["https://rdap.registry.hiphop/rdap/"]], + [["love"], ["https://rdap.registry.love/rdap/"]], + [["music"], ["https://rdap.registryservices.music/rdap/"]], + [["rw"], ["https://rdap.ricta.org.rw/"]], + [["cologne", "koeln", "tirol", "wien"], ["https://rdap.ryce-rsp.com/rdap/"]], + [["sg"], ["https://rdap.sgnic.sg/rdap/"]], + [["nl"], ["https://rdap.sidn.nl/"]], + [["xn--clchc0ea0b2g2a9gcd"], ["https://rdap.ta.sgnic.sg/rdap/"]], + [["anquan", "shouji", "xihuan", "xn--vuq861b", "yun"], ["https://rdap.teleinfo.cn/"]], + [["xn--3ds443g"], ["https://rdap.teleinfo.cn/xn--3ds443g/"]], + [["xn--fiq228c5hs"], ["https://rdap.teleinfo.cn/xn--fiq228c5hs/"]], + [["xn--kput3i"], ["https://rdap.teleinfo.cn/xn--kput3i/"]], + [["xn--nyqy26a"], ["https://rdap.teleinfo.cn/xn--nyqy26a/"]], + [["xn--rhqv96g"], ["https://rdap.teleinfo.cn/xn--rhqv96g/"]], + [["th", "xn--o3cw4h"], ["https://rdap.thains.co.th/"]], + [["to"], ["https://rdap.tonicregistry.to/rdap/"]], + [ + [ + "click", + "country", + "diy", + "food", + "gift", + "hiv", + "lifestyle", + "link", + "living", + "property", + "sexy", + "trust", + "vana" + ], + ["https://rdap.tucowsregistry.net/rdap/"] + ], + [["xn--mxtq1m"], ["https://rdap.twnic.tw/rdap/"]], + [["com"], ["https://rdap.verisign.com/com/v1/"]], + [["net"], ["https://rdap.verisign.com/net/v1/"]], + [["ye"], ["https://rdap.y.net.ye/"]], + [["xn--45q11c"], ["https://rdap.zdnsgtld.com/XN--45Q11C/"]], + [["xn--efvy88h"], ["https://rdap.zdnsgtld.com/XN--EFVY88H/"]], + [["baidu"], ["https://rdap.zdnsgtld.com/baidu/"]], + [["citic"], ["https://rdap.zdnsgtld.com/citic/"]], + [["icbc"], ["https://rdap.zdnsgtld.com/icbc/"]], + [["ren"], ["https://rdap.zdnsgtld.com/ren/"]], + [["sohu"], ["https://rdap.zdnsgtld.com/sohu/"]], + [["top"], ["https://rdap.zdnsgtld.com/top/"]], + [["unicom"], ["https://rdap.zdnsgtld.com/unicom/"]], + [["wang"], ["https://rdap.zdnsgtld.com/wang/"]], + [["xn--30rr7y"], ["https://rdap.zdnsgtld.com/xn--30rr7y/"]], + [["xn--3bst00m"], ["https://rdap.zdnsgtld.com/xn--3bst00m/"]], + [["xn--6qq986b3xl"], ["https://rdap.zdnsgtld.com/xn--6qq986b3xl/"]], + [["xn--8y0a063a"], ["https://rdap.zdnsgtld.com/xn--8y0a063a/"]], + [["xn--9et52u"], ["https://rdap.zdnsgtld.com/xn--9et52u/"]], + [["xn--czr694b"], ["https://rdap.zdnsgtld.com/xn--czr694b/"]], + [["xn--czru2d"], ["https://rdap.zdnsgtld.com/xn--czru2d/"]], + [["xn--fiq64b"], ["https://rdap.zdnsgtld.com/xn--fiq64b/"]], + [["xn--hxt814e"], ["https://rdap.zdnsgtld.com/xn--hxt814e/"]], + [["xn--imr513n"], ["https://rdap.zdnsgtld.com/xn--imr513n/"]], + [["xn--otu796d"], ["https://rdap.zdnsgtld.com/xn--otu796d/"]], + [["xn--ses554g"], ["https://rdap.zdnsgtld.com/xn--ses554g/"]], + [["xn--yfro4i67o"], ["https://rdap.zh.sgnic.sg/rdap/"]], + [["xn--1qqw23a", "xn--55qx5d", "xn--io0a7i", "xn--xhq521b"], ["https://restwhois.ngtld.cn/"]], + [["cc"], ["https://tld-rdap.verisign.com/cc/v1/"]], + [["comsec"], ["https://tld-rdap.verisign.com/comsec/v1/"]], + [["name"], ["https://tld-rdap.verisign.com/name/v1/"]], + [["verisign"], ["https://tld-rdap.verisign.com/verisign/v1/"]], + [["xn--11b4c3d"], ["https://tld-rdap.verisign.com/xn--11b4c3d/v1/"]], + [["xn--3pxu8k"], ["https://tld-rdap.verisign.com/xn--3pxu8k/v1/"]], + [["xn--42c2d9a"], ["https://tld-rdap.verisign.com/xn--42c2d9a/v1/"]], + [["xn--9dbq2a"], ["https://tld-rdap.verisign.com/xn--9dbq2a/v1/"]], + [["xn--c2br7g"], ["https://tld-rdap.verisign.com/xn--c2br7g/v1/"]], + [["xn--fhbei"], ["https://tld-rdap.verisign.com/xn--fhbei/v1/"]], + [["xn--j1aef"], ["https://tld-rdap.verisign.com/xn--j1aef/v1/"]], + [["xn--mk1bu44c"], ["https://tld-rdap.verisign.com/xn--mk1bu44c/v1/"]], + [["xn--pssy2u"], ["https://tld-rdap.verisign.com/xn--pssy2u/v1/"]], + [["xn--t60b56a"], ["https://tld-rdap.verisign.com/xn--t60b56a/v1/"]], + [["xn--tckwe"], ["https://tld-rdap.verisign.com/xn--tckwe/v1/"]], + [["ky"], ["https://whois.kyregistry.ky/rdap/"]], + [["mtr"], ["https://whois.nic.mtr/rdap/"]], + [["tatar"], ["https://whois.nic.tatar/rdap/"]], + [["xn--d1acj3b"], ["https://whois.nic.xn--d1acj3b/rdap/"]], + [["sr"], ["https://whois.sr/rdap/"]], + [["tz"], ["https://whois.tznic.or.tz/rdap/"]], + [["fj"], ["https://www.rdap.fj/"]] + ], + "version": "1.0" +} diff --git a/extra/rebase-pr.js b/extra/rebase-pr.js new file mode 100644 index 0000000..31eb909 --- /dev/null +++ b/extra/rebase-pr.js @@ -0,0 +1,40 @@ +const { execSync } = require("child_process"); + +/** + * Rebase a PR onto such as 1.23.X or master + * @returns {Promise} + */ +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(); + console.log("Pr ID: ", currentBranchPRID); + + // Use gh commend to get pr commits + const prCommits = JSON.parse(execSync(`gh pr view ${currentBranchPRID} --json commits`).toString().trim()).commits; + + console.log("Found commits: ", prCommits.length); + + // Sort the commits by authoredDate + prCommits.sort((a, b) => { + return new Date(a.authoredDate) - new Date(b.authoredDate); + }); + + // Get the oldest commit id + const oldestCommitID = prCommits[0].oid; + console.log("Oldest commit id of this pr:", oldestCommitID); + + // Get the latest commit id of the target branch + const latestCommitID = execSync(`git rev-parse origin/${branch}`).toString().trim(); + console.log("Latest commit id of " + branch + ":", latestCommitID); + + // Get the original parent commit id of the oldest commit + const originalParentCommitID = execSync(`git log --pretty=%P -n 1 "${oldestCommitID}"`).toString().trim(); + console.log("Original parent commit id of the oldest commit:", originalParentCommitID); + + // Rebase the pr onto the target branch + execSync(`git rebase --onto ${latestCommitID} ${originalParentCommitID}`); +} + +main(); diff --git a/extra/release/build-and-upload-assets.mjs b/extra/release/build-and-upload-assets.mjs new file mode 100644 index 0000000..5d4eeed --- /dev/null +++ b/extra/release/build-and-upload-assets.mjs @@ -0,0 +1,58 @@ +// Build + asset upload stage of a release: +// 1. Build the frontend dist ONCE (only if ./tmp/dist.tar.gz is missing) +// 2. Create ./tmp/dist.tar.gz from the built dist +// 3. Upload it to the draft GitHub Release (--clobber, so this stage can be re-run) +// +// The docker-images job reuses this built dist, so the frontend is compiled a +// single time per release. +// +// Usage: node extra/release/build-and-upload-assets.mjs + +import "dotenv/config"; +import * as childProcess from "child_process"; +import fs from "fs"; +import { + createDistTarGz, + getVersionFromEnv, + releaseExists, + uploadReleaseAssets, +} from "./lib.mjs"; + +const distTarGz = "./tmp/dist.tar.gz"; + +/** + * Run the build + asset upload stage + * @returns {Promise} + */ +export async function runBuildAndUploadAssets() { + const version = getVersionFromEnv(); + const dryRun = process.env.DRY_RUN === "true"; + + if (!version) { + console.error("RELEASE_VERSION or RELEASE_BETA_VERSION is required"); + process.exit(1); + } + + if (!fs.existsSync(distTarGz)) { + console.log(`${distTarGz} not found, building...`); + // Build directly to also work in dry run mode + childProcess.execSync("npm run build", { stdio: "inherit" }); + await createDistTarGz(); + } + + if (dryRun) { + console.log("[DRY RUN] Skip uploading assets."); + return; + } + + if (!(await releaseExists(version))) { + console.error(`Release ${version} does not exist. Please run the draft release stage first.`); + process.exit(1); + } + + await uploadReleaseAssets(version, [distTarGz]); +} + +if (import.meta.main) { + await runBuildAndUploadAssets(); +} diff --git a/extra/release/build-images.mjs b/extra/release/build-images.mjs new file mode 100644 index 0000000..d124509 --- /dev/null +++ b/extra/release/build-images.mjs @@ -0,0 +1,61 @@ +// Image build stage of a release: +// 1. Check the version tag does not exist on Docker Hub yet (double-release guard) +// 2. Build and push all docker images (skipped in dry run) +// +// The frontend dist is built once by the build-and-upload-assets job (or the +// local orchestrator) and is expected to already exist in ./dist. +// +// Usage: node extra/release/build-images.mjs + +import "dotenv/config"; +import fs from "fs"; +import { + buildAllImages, + checkDocker, + checkTagExists, + checkVersionFormat, + getRepoNames, + getVersionFromEnv, + isBetaRelease, +} from "./lib.mjs"; + +/** + * Run the image build stage + * @returns {Promise} + */ +export async function runBuildImages() { + const version = getVersionFromEnv(); + const dryRun = process.env.DRY_RUN === "true"; + const isBeta = isBetaRelease(); + const repoNames = getRepoNames(); + + if (!version) { + console.error("RELEASE_VERSION or RELEASE_BETA_VERSION is required"); + process.exit(1); + } + + // Check if the version is a valid semver + checkVersionFormat(version); + + // Check if docker is running + checkDocker(); + + // Check if the tag exists on Docker Hub + await checkTagExists(repoNames, version); + + if (!dryRun) { + // dist must be built by the build-and-upload-assets job before this stage + if (!fs.existsSync("dist")) { + console.error("dist directory not found. Run the build-and-upload-assets job first."); + process.exit(1); + } + + buildAllImages(repoNames, version, isBeta); + } else { + console.log("Dry run mode - skipping image build and push."); + } +} + +if (import.meta.main) { + await runBuildImages(); +} diff --git a/extra/release/create-draft-release.mjs b/extra/release/create-draft-release.mjs new file mode 100644 index 0000000..24bca26 --- /dev/null +++ b/extra/release/create-draft-release.mjs @@ -0,0 +1,56 @@ +// Draft release stage of a release: +// 1. Generate the changelog (LLM categorization via opencode, with retries) +// 2. Create the draft GitHub Release (skipped if it already exists, so this stage +// can be re-run independently when the LLM call fails) +// +// Usage: node extra/release/create-draft-release.mjs + +import "dotenv/config"; +import { + createDraftRelease, + getVersionFromEnv, + isBetaRelease, + readMergeSha, +} from "./lib.mjs"; +import { generateChangelogAI } from "./generate-changelog.mjs"; + +/** + * Run the draft release stage + * @returns {Promise} + */ +export async function runCreateDraftRelease() { + const version = getVersionFromEnv(); + const previousVersion = process.env.RELEASE_PREVIOUS_VERSION; + const dryRun = process.env.DRY_RUN === "true"; + const isBeta = isBetaRelease(); + + if (!version) { + console.error("RELEASE_VERSION or RELEASE_BETA_VERSION is required"); + process.exit(1); + } + + if (!previousVersion) { + console.error("RELEASE_PREVIOUS_VERSION is required"); + process.exit(1); + } + + console.log(`Generating changelog for ${version}...`); + const changelog = await generateChangelogAI(previousVersion); + + if (dryRun) { + console.log("[DRY RUN] Skip creating the draft release."); + console.log("--- Changelog Start ---"); + console.log(changelog); + console.log("--- Changelog End ---"); + return; + } + + // Tag the exact merge commit when publishing, even if master moved on in the meantime + const targetSha = readMergeSha(); + + await createDraftRelease(version, changelog, isBeta, targetSha); +} + +if (import.meta.main) { + await runCreateDraftRelease(); +} diff --git a/extra/release/create-release.mjs b/extra/release/create-release.mjs new file mode 100644 index 0000000..8716850 --- /dev/null +++ b/extra/release/create-release.mjs @@ -0,0 +1,19 @@ +import { createDraftRelease, getVersionFromEnv, isBetaRelease } from "./lib.mjs"; +import { generateChangelogAI } from "./generate-changelog.mjs"; + +const version = getVersionFromEnv(); +const previousVersion = process.env.RELEASE_PREVIOUS_VERSION; + +if (!version) { + console.error("RELEASE_VERSION is required"); + process.exit(1); +} + +if (!previousVersion) { + console.error("RELEASE_PREVIOUS_VERSION is required"); + process.exit(1); +} + +const isBeta = isBetaRelease(); +const changelog = await generateChangelogAI(previousVersion); +await createDraftRelease(version, changelog, isBeta); \ No newline at end of file diff --git a/extra/release/generate-changelog.mjs b/extra/release/generate-changelog.mjs new file mode 100644 index 0000000..8969c24 --- /dev/null +++ b/extra/release/generate-changelog.mjs @@ -0,0 +1,377 @@ +// Script to generate changelog +// Usage: node extra/release/generate-changelog.mjs +// GitHub CLI (gh command) is required + +import * as childProcess from "child_process"; +import * as fs from "fs"; + +const ignoreList = [ + "louislam", + "CommanderStorm", + "UptimeKumaBot", + "weblate", + "Copilot", + "autofix-ci[bot]", + "app/copilot-swe-agent", + "app/github-actions", + "github-actions[bot]", +]; + +const mergeList = ["chore: Translations Update from Weblate", "chore: Update dependencies"]; + +const outputFormat = JSON.stringify({ + improvements: [123, 456], + newFeatures: [789], + bugFixes: [101, 112], + securityFixes: [131, 415], + translationContributions: [161, 718], + others: [192, 21], +}); + +const prompt = `Input Data: {{ input }} +LLM Task: +- Output a one-line JSON object in the following format: +{{ outputFormat }} +- Empty arrays included if there are no items for that category. +- Exclude reverted pull requests. +- "fix: " type pull requests should be categorized as "bugFixes". +- "chore: " type pull requests should be categorized as "others" +- "feat: " type pull requests should be categorized as "newFeatures" or "improvements" based on the content of the title, you should determine it. +- "refactor: " type pull requests should be categorized as "improvements". +`.replace("{{ outputFormat }}", outputFormat); + +const categoryList = { + // In case the LLM cannot categorize some items + uncategorized: { + title: "Uncategorized", + items: [], + }, + newFeatures: { + title: "🆕 New Features", + items: [], + }, + improvements: { + title: "💇‍♀️ Improvements", + items: [], + }, + bugFixes: { + title: "🐞 Bug Fixes", + items: [], + }, + securityFixes: { + title: "⬆️ Security Fixes", + items: [], + }, + translationContributions: { + title: "🦎 Translation Contributions", + items: [], + }, + others: { + title: "Others", + items: [], + }, +}; + +if (import.meta.main) { + await main(); +} + +/** + * Main Function + * @returns {Promise} + */ +async function main() { + const previousVersion = process.argv[2]; + const action = process.argv[3]; + const categorizedMap = process.argv[4] ? JSON.parse(process.argv[4]) : null; + + if (action === "generate") { + console.log(`Generating changelog since version ${previousVersion}...`); + console.log(await generateChangelog(previousVersion, categorizedMap)); + } else { + if (!previousVersion) { + console.error("Please provide the previous version as the first argument."); + process.exit(1); + } + console.log(await getPrompt(previousVersion)); + } +} + +/** + * Get Prompt for LLM + * @param {string} previousVersion Previous Version Tag + * @returns {Promise} Prompt for LLM + */ +export async function getPrompt(previousVersion) { + const input = JSON.stringify(await getPullRequestList(previousVersion, true)); + return prompt.replace("{{ input }}", input); +} + +/** + * Generate Changelog + * @param {string} previousVersion Previous Version Tag + * @param {object} categorizedMap It should be generated by the LLM based on the prompt + * @returns {Promise} Changelog Content + */ +export async function generateChangelog(previousVersion, categorizedMap) { + const prList = await getPullRequestList(previousVersion); + const list = []; + + let i = 1; + for (const pr of prList) { + console.log(`Progress: ${i++}/${prList.length}`); + let authorSet = await getAuthorList(pr.number); + authorSet = await mainAuthorToFront(pr.author.login, authorSet); + + if (mergeList.includes(pr.title)) { + // Check if it is already in the list + const existingItem = list.find((item) => item.title === pr.title); + if (existingItem) { + existingItem.numbers.push(pr.number); + for (const author of authorSet) { + existingItem.authors.add(author); + // Sort the authors + existingItem.authors = new Set([...existingItem.authors].sort((a, b) => a.localeCompare(b))); + } + continue; + } + } + + const item = { + numbers: [pr.number], + title: pr.title, + authors: authorSet, + }; + + list.push(item); + } + + for (const item of list) { + // Concat pr numbers into a string like #123 #456 + const prPart = item.numbers.map((num) => `#${num}`).join(" "); + + // Concat authors into a string like @user1 @user2 + let authorPart = [...item.authors].map((author) => `@${author}`).join(" "); + + if (authorPart) { + authorPart = `(Thanks ${authorPart})`; + } + + const line = `- ${prPart} ${item.title} ${authorPart}`; + + // Determine the category of the item, based on the title and the categorizedMap + let category = "uncategorized"; + let prNumber = item.numbers[0]; + + for (const cat in categorizedMap) { + if (categorizedMap[cat].includes(prNumber)) { + category = cat; + break; + } + } + + categoryList[category].items.push(line); + } + + // Generate markdown + let content = ""; + + for (const cat in categoryList) { + content += `### ${categoryList[cat].title}\n`; + for (const item of categoryList[cat].items) { + content += `${item}\n`; + } + content += `\n`; + } + + return content; +} + +/** + * Generate Changelog using AI + * The LLM API can be flaky, so it tries a list of models in order, falling back to the next model + * on failure before falling back to uncategorized. + * @param {string} previousVersion Previous Version Tag + * @returns {Promise} Changelog Content + */ +export async function generateChangelogAI(previousVersion) { + // 1. Generate changelog + let categorizedMap = null; + + console.log("Running opencode to categorize PRs..."); + const llmPrompt = (await getPrompt(previousVersion)).replaceAll("\n", " "); + + console.log(llmPrompt); + + // Try each model in order (see .github/opencode-models.json), falling back to the next one on failure. + const models = JSON.parse( + await fs.promises.readFile(new URL("../../.github/opencode-models.json", import.meta.url), "utf8") + ); + const retryDelayMs = 15000; + + for (const model of models) { + if (categorizedMap) { + break; + } + console.log(`Running opencode with the above prompt... (model: ${model})`); + + try { + const result = childProcess.spawnSync("opencode", ["run", "-m", model, "--format", "json", llmPrompt], { + encoding: "utf-8", + timeout: 300000, + shell: true, + cwd: process.cwd(), + env: process.env, + }); + + if (result.status === 0 && result.stdout) { + // Parse NDJSON output: find "type":"text" line + for (const line of result.stdout.trim().split("\n")) { + try { + const obj = JSON.parse(line); + if (obj.type === "text" && obj.part?.text) { + const jsonMatch = obj.part.text.match(/\{[\s\S]*\}/); + if (jsonMatch) { + categorizedMap = JSON.parse(jsonMatch[0]); + console.log("LLM categorization applied."); + break; + } + } + } catch { + // skip unparseable lines + } + } + + if (!categorizedMap) { + console.warn("No JSON found in opencode response."); + console.warn(result.stdout); + } + } else { + console.warn("opencode failed or returned no output (status:", result.status, ")"); + if (result.stderr) { + console.warn("stderr:", result.stderr.slice(0, 500)); + } + } + } catch (e) { + console.warn("Failed to run opencode:", e.message); + } + + if (!categorizedMap) { + console.warn(`Model "${model}" failed. Trying next fallback in ${retryDelayMs / 1000}s...`); + await new Promise((resolve) => setTimeout(resolve, retryDelayMs)); + } + } + + if (!categorizedMap) { + categorizedMap = {}; + console.warn( + `OpenCode unavailable on all fallback models (${models.join(", ")}), using uncategorized fallback.` + ); + } + + return await generateChangelog(previousVersion, categorizedMap); +} + +/** + * @param {string} previousVersion Previous Version Tag + * @param {boolean} removeAuthor Whether to strip the author field from the returned PR list + * @returns {Promise} List of Pull Requests merged since previousVersion + */ +export async function getPullRequestList(previousVersion, removeAuthor = false) { + // Get the date of previousVersion in iso8601-strict format (2026-02-19T13:34:03+08:00) from git + const previousVersionDate = childProcess + .execSync(`git log -1 --format=%cd --date=iso8601-strict ${previousVersion}`) + .toString() + .trim(); + + if (!previousVersionDate) { + throw new Error( + `Unable to find the date of version ${previousVersion}. Please make sure the version tag exists.` + ); + } + + const ghProcess = childProcess.spawnSync( + "gh", + [ + "pr", + "list", + "--state", + "merged", + "--base", + process.env.RELEASE_BASE_BRANCH || "master", + "--search", + `merged:>=${previousVersionDate}`, + "--json", + "number,title,author", + "--limit", + "1000", + ], + { + encoding: "utf-8", + } + ); + + if (ghProcess.error) { + throw ghProcess.error; + } + + if (ghProcess.status !== 0) { + throw new Error(`gh command failed with status ${ghProcess.status}: ${ghProcess.stderr}`); + } + + const obj = JSON.parse(ghProcess.stdout); + + if (removeAuthor) { + for (const pr of obj) { + delete pr.author; + } + } + + return obj; +} + +/** + * @param {number} prID Pull Request ID + * @returns {Promise>} Set of Authors' GitHub Usernames + */ +async function getAuthorList(prID) { + const ghProcess = childProcess.spawnSync("gh", ["pr", "view", prID, "--json", "commits"], { + encoding: "utf-8", + }); + + if (ghProcess.error) { + throw ghProcess.error; + } + + if (ghProcess.status !== 0) { + throw new Error(`gh command failed with status ${ghProcess.status}: ${ghProcess.stderr}`); + } + + const prInfo = JSON.parse(ghProcess.stdout); + const commits = prInfo.commits; + + const set = new Set(); + + for (const commit of commits) { + for (const author of commit.authors) { + if (author.login && !ignoreList.includes(author.login)) { + set.add(author.login); + } + } + } + + // Sort the set + return new Set([...set].sort((a, b) => a.localeCompare(b))); +} + +/** + * @param {string} mainAuthor Main Author + * @param {Set} authorSet Set of Authors + * @returns {Set} New Set with mainAuthor at the front + */ +export async function mainAuthorToFront(mainAuthor, authorSet) { + if (ignoreList.includes(mainAuthor)) { + return authorSet; + } + return new Set([mainAuthor, ...authorSet]); +} diff --git a/extra/release/install-deps.mjs b/extra/release/install-deps.mjs new file mode 100644 index 0000000..d782dec --- /dev/null +++ b/extra/release/install-deps.mjs @@ -0,0 +1,46 @@ +// Install only the npm packages needed by the release scripts, skipping the +// full project dependency tree (which is slow). Temporarily empties the +// dependency lists of package.json and removes package-lock.json so `npm install` +// only fetches dotenv/semver/tar, then restores both files before the release +// scripts run (they commit package.json, so the originals must be intact). +// +// Usage: node extra/release/install-deps.mjs + +import * as childProcess from "child_process"; +import fs from "fs"; + +const pkgBackup = "package.json.bak"; +const lockBackup = "package-lock.json.bak"; + +// Back up the original files +fs.copyFileSync("package.json", pkgBackup); +const hadLock = fs.existsSync("package-lock.json"); +if (hadLock) { + fs.copyFileSync("package-lock.json", lockBackup); +} + +// Strip deps so npm install only fetches the packages below +const rootPkg = JSON.parse(fs.readFileSync("package.json", "utf8")); +rootPkg.dependencies = {}; +rootPkg.devDependencies = {}; +fs.writeFileSync("package.json", JSON.stringify(rootPkg, null, 4) + "\n"); +fs.rmSync("package-lock.json", { force: true }); + +const npm = process.platform === "win32" ? "npm.cmd" : "npm"; +const result = childProcess.spawnSync( + npm, + ["install", "--no-save", "--no-fund", "dotenv@~16.0.3", "semver@~7.5.4", "tar@~6.2.1"], + { stdio: "inherit" } +); + +// Restore the original files before the release scripts run +fs.copyFileSync(pkgBackup, "package.json"); +fs.rmSync(pkgBackup, { force: true }); +if (hadLock) { + fs.copyFileSync(lockBackup, "package-lock.json"); + fs.rmSync(lockBackup, { force: true }); +} + +if (result.status !== 0) { + process.exit(result.status ?? 1); +} \ No newline at end of file diff --git a/extra/release/lib.mjs b/extra/release/lib.mjs new file mode 100644 index 0000000..9e776e6 --- /dev/null +++ b/extra/release/lib.mjs @@ -0,0 +1,677 @@ +import { loadEnvFile } from "node:process"; +import * as childProcess from "child_process"; +import semver from "semver"; +import fs from "fs"; +import tar from "tar"; + +try { + loadEnvFile(); +} catch (_) {} + +// Support both the legacy RELEASE_DRY_RUN=1 format and DRY_RUN=true used by GitHub Actions workflows +export const dryRun = process.env.RELEASE_DRY_RUN === "1" || process.env.DRY_RUN === "true"; + +/** + * Read the release version from environment variables + * @returns {string|undefined} Release version + */ +export function getVersionFromEnv() { + return process.env.RELEASE_VERSION || process.env.RELEASE_BETA_VERSION; +} + +/** + * Check if this is a beta release + * @returns {boolean} Is a beta release + */ +export function isBetaRelease() { + return process.env.RELEASE_IS_BETA === "true" || !!process.env.RELEASE_BETA_VERSION; +} + +/** + * Save PR number to tmp/pr-number.txt for later stages + * @param {number} prNumber PR number + * @returns {void} + */ +export function savePrNumber(prNumber) { + const tmpDir = "./tmp"; + if (!fs.existsSync(tmpDir)) { + fs.mkdirSync(tmpDir, { recursive: true }); + } + fs.writeFileSync(`${tmpDir}/pr-number.txt`, String(prNumber)); +} + +/** + * Save the squash merge commit SHA to tmp/merge-sha.txt for later stages + * @param {string} sha Merge commit SHA + * @returns {void} + */ +export function saveMergeSha(sha) { + const tmpDir = "./tmp"; + if (!fs.existsSync(tmpDir)) { + fs.mkdirSync(tmpDir, { recursive: true }); + } + fs.writeFileSync(`${tmpDir}/merge-sha.txt`, sha); +} + +/** + * Read the merge commit SHA from env or tmp/merge-sha.txt + * @returns {string|null} Merge commit SHA or null + */ +export function readMergeSha() { + if (process.env.MERGE_SHA) { + return process.env.MERGE_SHA; + } + const file = "./tmp/merge-sha.txt"; + if (fs.existsSync(file)) { + return fs.readFileSync(file, "utf-8").trim() || null; + } + return null; +} + +if (dryRun) { + console.info("Dry run enabled."); +} + +/** + * Check if docker is running + * @returns {void} + */ +export function checkDocker() { + try { + childProcess.execSync("docker ps"); + } catch (error) { + console.error("Docker is not running. Please start docker and try again."); + process.exit(1); + } +} + +/** + * Get Docker Hub repository name + * @returns {string[]} List of repository names + */ +export function getRepoNames() { + if (process.env.RELEASE_REPO_NAMES) { + // Split by comma + return process.env.RELEASE_REPO_NAMES.split(",").map((name) => name.trim()); + } + return ["louislam/uptime-kuma", "ghcr.io/louislam/uptime-kuma"]; +} + +/** + * Build frontend dist + * @returns {void} + */ +export function buildDist() { + if (!dryRun) { + childProcess.execSync("npm run build", { stdio: "inherit" }); + } else { + console.info("[DRY RUN] npm run build"); + } +} + +/** + * Build docker image and push to Docker Hub + * @param {string[]} repoNames Docker Hub repository names + * @param {string[]} tags Docker image tags + * @param {string} target Dockerfile's target name + * @param {string} buildArgs Docker build args + * @param {string} dockerfile Path to Dockerfile + * @param {string} platform Build platform + * @returns {void} + */ +export function buildImage( + repoNames, + tags, + target, + buildArgs = "", + dockerfile = "docker/dockerfile", + platform = "linux/amd64,linux/arm64" +) { + let args = ["buildx", "build", "-f", dockerfile, "--platform", platform]; + + for (let repoName of repoNames) { + // Add tags + for (let tag of tags) { + args.push("-t", `${repoName}:${tag}`); + } + } + + args = [...args, "--target", target]; + + // Add build args + if (buildArgs) { + args.push("--build-arg", buildArgs); + } + + args = [...args, ".", "--push"]; + + if (!dryRun) { + childProcess.spawnSync("docker", args, { stdio: "inherit" }); + } else { + console.log(`[DRY RUN] docker ${args.join(" ")}`); + } +} + +/** + * Check if the version already exists on Docker Hub + * TODO: use semver to compare versions if it is greater than the previous? + * @param {string[]} repoNames repository name (Only check the name with single slash) + * @param {string} version Version to check + * @returns {void} + */ +export async function checkTagExists(repoNames, version) { + // Skip if the tag is not on Docker Hub + // louislam/uptime-kuma + let dockerHubRepoNames = repoNames.filter((name) => { + return name.split("/").length === 2; + }); + + for (let repoName of dockerHubRepoNames) { + await checkTagExistsSingle(repoName, version); + } +} + +/** + * Check if the version already exists on Docker Hub + * @param {string} repoName repository name + * @param {string} version Version to check + * @returns {Promise} + */ +export async function checkTagExistsSingle(repoName, version) { + console.log(`Checking if version ${version} exists on Docker Hub:`, repoName); + + // Get a list of tags from the Docker Hub repository + let tags = []; + + // It is mainly to check my careless mistake that I forgot to update the release version in .env, so `page_size` is set to 100 is enough, I think. + const response = await fetch(`https://hub.docker.com/v2/repositories/${repoName}/tags/?page_size=100`); + if (response.ok) { + const data = await response.json(); + tags = data.results.map((tag) => tag.name); + } else { + console.error("Failed to get tags from Docker Hub"); + process.exit(1); + } + + // Check if the version already exists + if (tags.includes(version)) { + console.error(`Version ${version} already exists`); + process.exit(1); + } +} + +/** + * Check the version format + * @param {string} version Version to check + * @returns {void} + */ +export function checkVersionFormat(version) { + if (!version) { + console.error("VERSION is required"); + process.exit(1); + } + + // Check the version format, it should be a semver and must be like this: "2.0.0-beta.0" + if (!semver.valid(version)) { + console.error("VERSION is not a valid semver version"); + process.exit(1); + } +} + +/** + * Find an open pull request by its head branch + * @param {string} branchName Head branch name + * @returns {Promise} PR number or null if not found + */ +export async function findOpenPrByHead(branchName) { + const result = childProcess.spawnSync( + "gh", + ["pr", "list", "--head", branchName, "--state", "open", "--json", "number", "--limit", "1"], + { + encoding: "utf-8", + } + ); + + if (result.status !== 0) { + console.error(result.stderr); + console.error("Failed to list pull requests"); + process.exit(1); + } + + const prs = JSON.parse(result.stdout); + return prs.length > 0 ? prs[0].number : null; +} + +/** + * Check whether a pull request has been merged + * @param {number} prNumber PR number + * @returns {Promise} Is the PR merged + */ +export async function isPrMerged(prNumber) { + const result = childProcess.spawnSync("gh", ["pr", "view", String(prNumber), "--json", "state"], { + encoding: "utf-8", + }); + + if (result.status !== 0) { + console.error(result.stderr); + console.error(`Failed to get state of PR #${prNumber}`); + process.exit(1); + } + + return JSON.parse(result.stdout).state === "MERGED"; +} + +/** + * Get the squash merge commit SHA of a merged pull request + * @param {number} prNumber PR number + * @returns {Promise} Merge commit SHA or null if not merged + */ +export async function getPrMergeCommit(prNumber) { + const result = childProcess.spawnSync("gh", ["pr", "view", String(prNumber), "--json", "mergeCommit"], { + encoding: "utf-8", + }); + + if (result.status !== 0) { + console.error(result.stderr); + console.error(`Failed to get merge commit of PR #${prNumber}`); + process.exit(1); + } + + const obj = JSON.parse(result.stdout); + return obj.mergeCommit ? obj.mergeCommit.oid : null; +} + +/** + * Check whether a GitHub release already exists (draft or published) + * @param {string} version Version tag + * @returns {Promise} Does the release exist + */ +export async function releaseExists(version) { + const result = childProcess.spawnSync("gh", ["release", "view", version, "--json", "tagName"], { + encoding: "utf-8", + }); + + if (result.status === 0) { + return true; + } + + // gh exits with a non-zero code both when the release is missing and on real errors + if ((result.stderr || "").includes("not found")) { + return false; + } + + console.error(result.stderr); + console.error(`Failed to check if release ${version} exists`); + process.exit(1); +} + +/** + * Create a draft GitHub Release without assets + * Skips creation if the release already exists, so it can be safely re-run + * @param {string} version Version tag + * @param {string} changelog Changelog content + * @param {boolean} isBeta Mark as pre-release + * @param {string|null} targetSha Commit SHA the tag should point to when published + * @returns {Promise} true if created, false if it already existed + */ +export async function createDraftRelease(version, changelog, isBeta = false, targetSha = null) { + if (await releaseExists(version)) { + console.log(`Release ${version} already exists, skipping creation.`); + return false; + } + + console.log(`Creating draft release ${version}...`); + + const releaseBody = `## ${version} + +${changelog}`; + + let releaseArgs = ["release", "create", version]; + + if (targetSha) { + releaseArgs.push("--target", targetSha); + } + + releaseArgs = releaseArgs.concat(["--draft", "--title", version, "--notes", releaseBody]); + + if (isBeta) { + releaseArgs.push("--prerelease"); + } + + const result = childProcess.spawnSync("gh", releaseArgs, { + encoding: "utf-8", + }); + + if (result.status !== 0) { + console.error(result.stderr); + console.error("Failed to create release"); + process.exit(1); + } + + console.log(`Draft release ${version} created.`); + console.log("Next steps:"); + console.log(` 1. Review the draft release: https://github.com/louislam/uptime-kuma/releases/tag/${version}`); + console.log(" 2. Edit if needed and publish."); + return true; +} + +/** + * Upload asset files to an existing GitHub release + * Uses --clobber, so it can be safely re-run + * @param {string} version Version tag + * @param {string[]} files File paths to upload + * @returns {Promise} + */ +export async function uploadReleaseAssets(version, files) { + if (dryRun) { + console.log(`[DRY RUN] gh release upload ${version} --clobber ${files.join(" ")}`); + return; + } + + const args = ["release", "upload", version, "--clobber", ...files]; + + const result = childProcess.spawnSync("gh", args, { + stdio: "inherit", + }); + + if (result.status !== 0) { + console.error("Failed to upload assets"); + process.exit(1); + } + + console.log("Assets uploaded."); +} + +/** + * Build and push all docker images for a release + * @param {string[]} repoNames Docker repository names + * @param {string} version Version tag + * @param {boolean} isBeta Beta tags instead of final ones + * @returns {void} + */ +export function buildAllImages(repoNames, version, isBeta) { + const majorVersion = semver.parse(version).major; + + if (isBeta) { + // Build slim image (rootless) + buildImage( + repoNames, + ["beta-slim-rootless", ver(version, "slim-rootless")], + "rootless", + `BASE_IMAGE=louislam/uptime-kuma:base${majorVersion}-slim` + ); + + // Build full image (rootless) + buildImage(repoNames, ["beta-rootless", ver(version, "rootless")], "rootless"); + + // Build slim image + buildImage( + repoNames, + ["beta-slim", ver(version, "slim")], + "release", + `BASE_IMAGE=louislam/uptime-kuma:base${majorVersion}-slim` + ); + + // Build full image + buildImage(repoNames, ["beta", version], "release"); + } else { + // Build slim image (rootless) + buildImage( + repoNames, + [`next-slim-rootless`, `${majorVersion}-slim-rootless`, ver(version, "slim-rootless")], + "rootless", + `BASE_IMAGE=louislam/uptime-kuma:base${majorVersion}-slim` + ); + + // Build full image (rootless) + buildImage(repoNames, ["next-rootless", `${majorVersion}-rootless`, ver(version, "rootless")], "rootless"); + + // Build slim image + buildImage( + repoNames, + ["next-slim", `${majorVersion}-slim`, ver(version, "slim")], + "release", + `BASE_IMAGE=louislam/uptime-kuma:base${majorVersion}-slim` + ); + + // Build full image + buildImage(repoNames, ["next", `${majorVersion}`, version], "release"); + } +} + +/** + * Press any key to continue + * @returns {Promise} + */ +export function pressAnyKey() { + console.log("Git Push and Publish the release note on github, then press any key to continue"); + process.stdin.setRawMode(true); + process.stdin.resume(); + return new Promise((resolve) => + process.stdin.once("data", (data) => { + process.stdin.setRawMode(false); + process.stdin.pause(); + resolve(); + }) + ); +} + +/** + * Append version identifier + * @param {string} version Version + * @param {string} identifier Identifier + * @returns {string} Version with identifier + */ +export function ver(version, identifier) { + const obj = semver.parse(version); + + if (obj.prerelease.length === 0) { + obj.prerelease = [identifier]; + } else { + obj.prerelease[0] = [obj.prerelease[0], identifier].join("-"); + } + return obj.format(); +} + +/** + * Upload artifacts to GitHub + * docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain + * @param {string} version Version + * @param {string} githubToken GitHub token + * @returns {void} + * @deprecated + */ +export function uploadArtifacts(version, githubToken) { + let args = [ + "buildx", + "build", + "-f", + "docker/dockerfile", + "--platform", + "linux/amd64", + "-t", + "louislam/uptime-kuma:upload-artifact", + "--build-arg", + `VERSION=${version}`, + "--build-arg", + "GITHUB_TOKEN", + "--target", + "upload-artifact", + ".", + "--progress", + "plain", + ]; + + if (!dryRun) { + childProcess.spawnSync("docker", args, { + stdio: "inherit", + env: { + ...process.env, + GITHUB_TOKEN: githubToken, + }, + }); + } else { + console.log(`[DRY RUN] docker ${args.join(" ")}`); + } +} + +/** + * Execute a command + * @param {string} cmd Command to execute + * @returns {void} + */ +export function execSync(cmd) { + if (!dryRun) { + childProcess.execSync(cmd, { stdio: "inherit" }); + } else { + console.info(`[DRY RUN] ${cmd}`); + } +} + +/** + * Check if the current branch matches the expected release branch pattern + * @param {string} expectedBranch Expected branch name (can be "release" or "release-{version}") + * @returns {void} + */ +export function checkReleaseBranch(expectedBranch = "release") { + const res = childProcess.spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"]); + const branch = res.stdout.toString().trim(); + if (branch !== expectedBranch) { + console.error(`Current branch is ${branch}, please switch to "${expectedBranch}" branch`); + process.exit(1); + } +} + +/** + * Create dist.tar.gz from the dist directory + * Similar to "tar -zcvf dist.tar.gz dist", but using nodejs + * @returns {Promise} + */ +export async function createDistTarGz() { + const distPath = "dist"; + const outputPath = "./tmp/dist.tar.gz"; + const tmpDir = "./tmp"; + + // Ensure tmp directory exists + if (!fs.existsSync(tmpDir)) { + fs.mkdirSync(tmpDir, { recursive: true }); + } + + // Check if dist directory exists + if (!fs.existsSync(distPath)) { + console.error("Error: dist directory not found"); + process.exit(1); + } + + console.log(`Creating ${outputPath} from ${distPath}...`); + + try { + await tar.create( + { + gzip: true, + file: outputPath, + }, + [distPath] + ); + console.log(`Successfully created ${outputPath}`); + } catch (error) { + console.error(`Failed to create tarball: ${error.message}`); + process.exit(1); + } +} + +/** + * Create a release PR + * @param {string} version Version + * @param {string} previousVersion Previous version tag + * @param {boolean} dryRun Still create the PR, but add "[DRY RUN]" to the title + * @param {string} branchName The branch name to use for the PR head (defaults to "release") + * @param {string} githubRunId The GitHub Actions run ID for linking to artifacts + * @returns {Promise} The PR number + */ +export async function createReleasePR(version, previousVersion, dryRun, branchName = "release", githubRunId = null) { + // Reuse the existing open PR for this branch, so re-running does not close or duplicate it + const existingPrNumber = await findOpenPrByHead(branchName); + if (existingPrNumber) { + console.log(`Found existing open PR #${existingPrNumber} for branch ${branchName}, reusing it.`); + savePrNumber(existingPrNumber); + return existingPrNumber; + } + + const title = dryRun ? `chore: update to ${version} (dry run)` : `chore: update to ${version}`; + + // Build the artifact link - use direct run link if available, otherwise link to workflow file + const artifactLink = githubRunId + ? `https://github.com/louislam/uptime-kuma/actions/runs/${githubRunId}/workflow` + : `https://github.com/louislam/uptime-kuma/actions/workflows/release.yml`; + + const tmpDir = "./tmp"; + if (!fs.existsSync(tmpDir)) { + fs.mkdirSync(tmpDir, { recursive: true }); + } + + const body = `## Release ${version} + +This PR prepares the release for version ${version}. + +### Release Artifacts +The \`dist.tar.gz\` archive will be available as an artifact in the [workflow run](${artifactLink}). +`; + + // Create the PR using gh CLI + // RELEASE_BASE_BRANCH lets the release workflow target a maintenance branch (e.g. 2.5.X) instead of master + const baseBranch = process.env.RELEASE_BASE_BRANCH || "master"; + const args = ["pr", "create", "--title", title, "--body", body, "--base", baseBranch, "--head", branchName]; + + console.log(`Creating PR: ${title}`); + + const result = childProcess.spawnSync("gh", args, { + encoding: "utf-8", + stdio: "pipe", + }); + + if (result.status !== 0) { + console.error(result.stderr); + console.error("Failed to create pull request"); + process.exit(1); + } + + const prUrl = result.stdout.trim(); + console.log(prUrl); + + // Extract PR number from URL (e.g., https://github.com/louislam/uptime-kuma/pull/1234) + const prNumberMatch = prUrl.match(/\/pull\/(\d+)/); + const prNumber = prNumberMatch ? parseInt(prNumberMatch[1], 10) : null; + + if (prNumber) { + console.log(`PR number: ${prNumber}`); + // Save PR number to file for the finish script + savePrNumber(prNumber); + } else { + console.warn("Could not extract PR number from URL, auto-finish will not be possible"); + } + + console.log("Successfully created pull request"); + return prNumber; +} + +/** + * Create a draft release, optionally with dist.tar.gz uploaded as an asset + * @param {string} version Version tag + * @param {string} changelog Changelog content + * @param {boolean} isBeta Mark as pre-release + * @param {string} distTarGz If empty, it will not be uploaded to the release + * @returns {Promise} + */ +export async function createRelease(version, changelog, isBeta = false, distTarGz = undefined) { + await createDraftRelease(version, changelog, isBeta); + + if (distTarGz) { + if (!fs.existsSync(distTarGz)) { + console.error(`dist.tar.gz not found: ${distTarGz}`); + process.exit(1); + } + + await uploadReleaseAssets(version, [distTarGz]); + } +} diff --git a/extra/release/merge-pr.mjs b/extra/release/merge-pr.mjs new file mode 100644 index 0000000..aa55eaa --- /dev/null +++ b/extra/release/merge-pr.mjs @@ -0,0 +1,101 @@ +// Merge stage of a release: +// Squash merge the release PR into master. +// Safe to re-run: skips if the PR is already merged, and records the merge commit SHA +// in ./tmp/merge-sha.txt for the draft release stage. +// +// Usage: node extra/release/merge-pr.mjs + +import "dotenv/config"; +import * as childProcess from "child_process"; +import fs from "fs"; +import { + findOpenPrByHead, + getPrMergeCommit, + getVersionFromEnv, + isPrMerged, + saveMergeSha, +} from "./lib.mjs"; + +const version = getVersionFromEnv(); +const dryRun = process.env.DRY_RUN === "true"; +const prNumberFile = "./tmp/pr-number.txt"; + +if (!version) { + console.error("RELEASE_VERSION or RELEASE_BETA_VERSION is required"); + process.exit(1); +} + +/** + * Resolve the PR number from env, file, or by looking it up on GitHub + * @returns {Promise} PR number or null if not found + */ +async function resolvePrNumber() { + // 1. From PR_NUMBER env var (GitHub Actions output of the prepare job) + if (process.env.PR_NUMBER) { + return parseInt(process.env.PR_NUMBER, 10); + } + + // 2. From the file written during the prepare stage (local usage) + if (fs.existsSync(prNumberFile)) { + return parseInt(fs.readFileSync(prNumberFile, "utf-8").trim(), 10); + } + + // 3. Look up the open PR for the release branch + return await findOpenPrByHead(`release-${version}`); +} + +/** + * Run the merge stage + * @returns {Promise} + */ +export async function runMergePR() { + const prNumber = await resolvePrNumber(); + + if (!prNumber) { + console.error("No open release PR found. Please run the prepare stage first."); + process.exit(1); + } + + console.log(`Release PR: #${prNumber}`); + + // Idempotency: skip if the PR is already merged (e.g. a previous run failed afterwards) + if (await isPrMerged(prNumber)) { + console.log(`PR #${prNumber} is already merged, skipping.`); + await saveCurrentMergeSha(prNumber); + return; + } + + if (dryRun) { + console.log(`[DRY RUN] gh pr merge ${prNumber} --squash --delete-branch --subject "Update to ${version}" --admin`); + return; + } + + console.log(`Squash merging PR #${prNumber}...`); + childProcess.execSync( + `gh pr merge ${prNumber} --squash --delete-branch --subject "Update to ${version}" --admin`, + { + stdio: "inherit", + } + ); + + await saveCurrentMergeSha(prNumber); +} + +/** + * Get the merge commit SHA of the PR and save it to ./tmp/merge-sha.txt + * @param {number} prNumber PR number + * @returns {Promise} + */ +async function saveCurrentMergeSha(prNumber) { + const sha = await getPrMergeCommit(prNumber); + if (sha) { + console.log(`Merge commit: ${sha}`); + saveMergeSha(sha); + } else { + console.warn("Could not determine the merge commit SHA."); + } +} + +if (import.meta.main) { + await runMergePR(); +} diff --git a/extra/release/nightly.mjs b/extra/release/nightly.mjs new file mode 100644 index 0000000..9a7c531 --- /dev/null +++ b/extra/release/nightly.mjs @@ -0,0 +1,16 @@ +import { buildDist, buildImage, checkDocker, getRepoNames } from "./lib.mjs"; + +// Docker Hub repository name +const repoNames = getRepoNames(); + +// Check if docker is running +checkDocker(); + +// Build frontend dist (it will build on the host machine, TODO: build on a container?) +buildDist(); + +// Build full image (rootless) +buildImage(repoNames, ["nightly3-rootless", "nightly-rootless"], "nightly-rootless"); + +// Build full image +buildImage(repoNames, ["nightly3", "nightly"], "nightly"); diff --git a/extra/release/prepare-release.mjs b/extra/release/prepare-release.mjs new file mode 100644 index 0000000..9f680d6 --- /dev/null +++ b/extra/release/prepare-release.mjs @@ -0,0 +1,101 @@ +// Prepare stage of a release: +// 1. Validate version format +// 2. Reuse the existing open release PR if there is one (safe to re-run) +// 3. Otherwise create the release branch (--setup-branch, used by GitHub Actions), +// bump the version (commit + push), and create a PR +// +// Usage: node extra/release/prepare-release.mjs [--setup-branch] + +import "dotenv/config"; +import * as childProcess from "child_process"; +import semver from "semver"; +import { + checkReleaseBranch, + checkVersionFormat, + createReleasePR, + findOpenPrByHead, + getVersionFromEnv, + isBetaRelease, + savePrNumber, +} from "./lib.mjs"; + +/** + * Run the prepare stage + * @param {object} options Options + * @param {boolean} options.setupBranch Delete and recreate the release branch (GitHub Actions only) + * @returns {Promise} The PR number + */ +export async function runPrepare({ setupBranch = false } = {}) { + const version = getVersionFromEnv(); + const previousVersion = process.env.RELEASE_PREVIOUS_VERSION; + const dryRun = process.env.DRY_RUN === "true"; + const isBeta = isBetaRelease(); + const branchName = `release-${version}`; + const githubRunId = process.env.GITHUB_RUN_ID; + + console.log("VERSION:", version); + + // Check if the version is a valid semver + checkVersionFormat(version); + + // Check the semver identifier + const semverIdentifier = semver.prerelease(version); + console.log("Semver identifier:", semverIdentifier); + if (isBeta) { + if (!semverIdentifier || semverIdentifier[0] !== "beta") { + console.error("VERSION should have a semver identifier of 'beta'"); + process.exit(1); + } + } else { + if (semverIdentifier) { + console.error("VERSION should not have a semver identifier for final release"); + process.exit(1); + } + } + + // Resume support: reuse the existing open PR instead of recreating the branch + const existingPrNumber = await findOpenPrByHead(branchName); + if (existingPrNumber) { + console.log(`Found existing open PR #${existingPrNumber} for ${branchName}, skipping branch setup.`); + savePrNumber(existingPrNumber); + return existingPrNumber; + } + + if (setupBranch) { + exec(`git push origin --delete "${branchName}"`, true); + exec(`git branch -D "${branchName}"`, true); + exec(`git checkout -b "${branchName}"`); + } else { + // Local usage: the developer must be on the release branch already + checkReleaseBranch(branchName); + } + + // Bump the version, commit and force push the branch + await import("./update-version.mjs"); + + // Create Pull Request (gh pr create will handle pushing the branch) + return await createReleasePR(version, previousVersion, dryRun, branchName, githubRunId); +} + +/** + * Execute a shell command + * @param {string} cmd Command to execute + * @param {boolean} ignoreFailure Do not exit on failure + * @returns {void} + */ +function exec(cmd, ignoreFailure = false) { + const result = childProcess.spawnSync(cmd, { + shell: true, + stdio: "inherit", + }); + + if (result.status !== 0 && !ignoreFailure) { + console.error(`Command failed: ${cmd}`); + process.exit(1); + } +} + +if (import.meta.main) { + const setupBranch = process.argv.includes("--setup-branch"); + await runPrepare({ setupBranch }); +} diff --git a/extra/release/release.mjs b/extra/release/release.mjs new file mode 100644 index 0000000..1a7b57c --- /dev/null +++ b/extra/release/release.mjs @@ -0,0 +1,54 @@ +// Unified release orchestrator for both final and beta releases. +// Beta is enabled with the --beta flag (or RELEASE_IS_BETA=true env var). +// +// Sequence: +// 1. Prepare: validate version, bump it on the release branch, create the draft PR +// 2. Merge: squash merge the PR into master +// 3. Draft release: generate changelog (LLM) and create the draft GitHub Release +// 4. Images: build the frontend dist once, then build and push all docker images +// 5. Assets: upload dist.tar.gz to the draft release +// +// Usage: +// node extra/release/release.mjs # final release +// node extra/release/release.mjs --beta # beta release + +import "dotenv/config"; +import { buildDist, checkDocker, getVersionFromEnv } from "./lib.mjs"; +import { runPrepare } from "./prepare-release.mjs"; +import { runMergePR } from "./merge-pr.mjs"; +import { runCreateDraftRelease } from "./create-draft-release.mjs"; +import { runBuildImages } from "./build-images.mjs"; +import { runBuildAndUploadAssets } from "./build-and-upload-assets.mjs"; + +if (process.argv.includes("--beta")) { + process.env.RELEASE_IS_BETA = "true"; +} + +const version = getVersionFromEnv(); +const dryRun = process.env.DRY_RUN === "true"; + +if (dryRun) { + console.log("Dry run mode enabled. No images will be pushed."); +} + +console.log("RELEASE_VERSION:", version); + +// Fail fast if docker is not running +checkDocker(); + +// 1. Validate the version, bump it on the release branch and create the draft PR +// (reuses the existing open PR if there is one) +await runPrepare(); + +// 2. Squash merge the PR into master +await runMergePR(); + +// 3. Generate changelog (LLM categorization) and create the draft release +await runCreateDraftRelease(); + +// 4. Build the frontend dist once, then build and push all docker images +buildDist(); +await runBuildImages(); + +// 5. Upload dist.tar.gz to the draft release +await runBuildAndUploadAssets(); diff --git a/extra/release/update-version.mjs b/extra/release/update-version.mjs new file mode 100644 index 0000000..2f0f6eb --- /dev/null +++ b/extra/release/update-version.mjs @@ -0,0 +1,102 @@ +import { createRequire } from "module"; +import semver from "semver"; +import { isBetaRelease } from "./lib.mjs"; + +const require = createRequire(import.meta.url); + +const pkg = require("../../package.json"); +const fs = require("fs"); +const childProcess = require("child_process"); + +const version = process.env.RELEASE_VERSION; +const isBeta = isBetaRelease(); + +console.log("New Version: " + version); + +if (!version) { + console.error("invalid version"); + process.exit(1); +} + +if (isBeta) { + const identifier = semver.prerelease(version)?.[0]; + if (identifier !== "beta") { + console.error("invalid version, beta version only"); + process.exit(1); + } +} + +const exists = tagExists(version); + +if (!exists) { + // Process package.json + pkg.version = version; + + if (!isBeta) { + // Replace the version: https://regex101.com/r/hmj2Bc/1 + pkg.scripts.setup = pkg.scripts.setup.replace(/(git checkout )([^\s]+)/, `$1${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 }); + if (resultVersion.error) { + console.error(resultVersion.error); + console.error("error npm version!"); + process.exit(1); + } + const resultInstall = childProcess.spawnSync(npm, ["install", "--package-lock-only"], { 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); +} + +/** + * Commit updated files + * @param {string} version Version to update to + * @returns {void} + * @throws Error when committing files + */ +function commit(version) { + let msg = "Update to " + version; + + let res = childProcess.spawnSync("git", ["commit", "-m", msg, "-a"]); + let stdout = res.stdout.toString().trim(); + console.log(stdout); + + if (stdout.includes("no changes added to commit")) { + throw new Error("commit error"); + } + + // Get the current branch name + res = childProcess.spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"]); + let branchName = res.stdout.toString().trim(); + console.log("Current branch:", branchName); + + // Git push the branch + childProcess.spawnSync("git", ["push", "origin", branchName, "--force"], { stdio: "inherit" }); +} + +/** + * Check if a tag exists for the specified version + * @param {string} version Version to check + * @returns {boolean} Does the tag already exist + * @throws Version is not valid + */ +function tagExists(version) { + if (!version) { + throw new Error("invalid version"); + } + + let res = childProcess.spawnSync("git", ["tag", "-l", version]); + + return res.stdout.toString().trim() === version; +} \ No newline at end of file diff --git a/extra/release/upload-artifacts-beta.mjs b/extra/release/upload-artifacts-beta.mjs new file mode 100644 index 0000000..2897097 --- /dev/null +++ b/extra/release/upload-artifacts-beta.mjs @@ -0,0 +1,6 @@ +import { uploadArtifacts } from "./lib.mjs"; + +const version = process.env.RELEASE_BETA_VERSION; +const githubToken = process.env.RELEASE_GITHUB_TOKEN; + +uploadArtifacts(version, githubToken); diff --git a/extra/release/upload-artifacts.mjs b/extra/release/upload-artifacts.mjs new file mode 100644 index 0000000..cd34bd7 --- /dev/null +++ b/extra/release/upload-artifacts.mjs @@ -0,0 +1,6 @@ +import { uploadArtifacts } from "./lib.mjs"; + +const version = process.env.RELEASE_VERSION; +const githubToken = process.env.RELEASE_GITHUB_TOKEN; + +uploadArtifacts(version, githubToken); diff --git a/extra/remove-2fa.js b/extra/remove-2fa.js new file mode 100644 index 0000000..d0fe693 --- /dev/null +++ b/extra/remove-2fa.js @@ -0,0 +1,5 @@ +console.log("== Uptime Kuma Remove 2FA Tool =="); +console.log("Loading the database"); +console.log("TODO"); + +// TODO diff --git a/extra/remove-empty-lang-keys.js b/extra/remove-empty-lang-keys.js new file mode 100644 index 0000000..6b34fa0 --- /dev/null +++ b/extra/remove-empty-lang-keys.js @@ -0,0 +1,25 @@ +// For #5231 + +const fs = require("fs"); + +let path = "../src/lang"; + +// list directories in the lang directory +let jsonFileList = fs.readdirSync(path); + +for (let jsonFile of jsonFileList) { + if (!jsonFile.endsWith(".json")) { + continue; + } + + let jsonPath = path + "/" + jsonFile; + let langData = JSON.parse(fs.readFileSync(jsonPath, "utf8")); + + for (let key in langData) { + if (langData[key] === "") { + delete langData[key]; + } + } + + fs.writeFileSync(jsonPath, JSON.stringify(langData, null, 4) + "\n"); +} diff --git a/extra/remove-playwright-test-data.js b/extra/remove-playwright-test-data.js new file mode 100644 index 0000000..0628807 --- /dev/null +++ b/extra/remove-playwright-test-data.js @@ -0,0 +1,6 @@ +const fs = require("fs"); + +fs.rmSync("./data/playwright-test", { + recursive: true, + force: true, +}); diff --git a/extra/reset-migrate-aggregate-table-state.js b/extra/reset-migrate-aggregate-table-state.js new file mode 100644 index 0000000..10e3416 --- /dev/null +++ b/extra/reset-migrate-aggregate-table-state.js @@ -0,0 +1,23 @@ +const { R } = require("redbean-node"); +const Database = require("../server/database"); +const args = require("args-parser")(process.argv); +const { Settings } = require("../server/settings"); + +const main = async () => { + console.log("Connecting the database"); + Database.initDataDir(args); + await Database.connect(false, false, true); + + console.log("Deleting all data from aggregate tables"); + await R.exec("DELETE FROM stat_minutely"); + await R.exec("DELETE FROM stat_hourly"); + await R.exec("DELETE FROM stat_daily"); + + console.log("Resetting the aggregate table state"); + await Settings.set("migrateAggregateTableState", ""); + + await Database.close(); + console.log("Done"); +}; + +main(); diff --git a/extra/reset-password.mts b/extra/reset-password.mts new file mode 100644 index 0000000..a468ca8 --- /dev/null +++ b/extra/reset-password.mts @@ -0,0 +1,103 @@ +console.log("== Uptime Kuma Reset Password Tool =="); + +import { loadEnvFile } from "node:process"; +import { auth } from "../server/better-auth"; +import { password as passwordInput, select } from "@inquirer/prompts"; +import { ExitPromptError } from "@inquirer/core"; +import { isDevEnv } from "../src/util"; +import { hashPassword } from "better-auth/crypto"; + +// @ts-ignore +import Database from "../server/database.js"; + +// @ts-ignore No type package is available +import parseArgs from "args-parser"; + +/** + * + */ +async function main() { + const args = parseArgs(process.argv); + try { + loadEnvFile(); + } catch { } + + console.log("Dev Environment:", isDevEnv()); + + Database.initDataDir(args); + await Database.connect(); + + const context = await auth().$context; + const internalAdapter = context.internalAdapter; + + const users = await internalAdapter.listUsers(); + const choices = users.map((user) => { + const userId = user.id; + const username = user.name || user.email; + + return { + name: username, + value: userId, + description: "", + }; + }); + + let userId: string; + + while (true) { + const selectedUserId = await select({ + message: "Select a username:", + choices, + }); + + if (!selectedUserId) { + console.log("Invalid choice"); + continue; + } + + userId = selectedUserId; + break; + } + + while (true) { + const password = await passwordInput({ + message: "Enter new password:", + mask: "*", + }); + + try { + const hash = await hashPassword(password); // We do that because the method under doesn't hash the password (it stores it in plain text) + await internalAdapter.updatePassword(userId, hash); + + console.log("Password reset succesfully!"); + break; + } catch (_) { + // Better Auth shows Cli error message in cli already, we don't need to + } + } + + await cleanUp(); +} + +/** + * + */ +async function cleanUp() { + await Database.close(); +} + +// Gracefully handle Ctrl+C +process.on("SIGINT", async () => { + console.log("\nGracefully shutting down from SIGINT (Ctrl+C)"); + await cleanUp(); + process.exit(0); +}); + +try { + await main(); +} catch (e) { + // Ignore prompt exit errors + if (!(e instanceof ExitPromptError)) { + throw e; + } +} diff --git a/extra/simple-dns-server.js b/extra/simple-dns-server.js new file mode 100644 index 0000000..33d27cc --- /dev/null +++ b/extra/simple-dns-server.js @@ -0,0 +1,147 @@ +/* + * Simple DNS Server + * For testing DNS monitoring type, dev only + */ +const dns2 = require("dns2"); + +const { Packet } = dns2; + +const server = dns2.createServer({ + udp: true, +}); + +server.on("request", (request, send, rinfo) => { + for (let question of request.questions) { + console.log(question.name, type(question.type), question.class); + + 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", + }); + } else if (question.type === Packet.TYPE.AAAA) { + response.answers.push({ + name: question.name, + type: question.type, + class: question.class, + ttl: 300, + address: "fe80::::1234:5678:abcd:ef00", + }); + } else if (question.type === Packet.TYPE.CNAME) { + response.answers.push({ + name: question.name, + type: question.type, + class: question.class, + ttl: 300, + domain: "cname1.existing.com", + }); + } else if (question.type === Packet.TYPE.MX) { + response.answers.push({ + name: question.name, + type: question.type, + class: question.class, + ttl: 300, + exchange: "mx1.existing.com", + priority: 5, + }); + } else if (question.type === Packet.TYPE.NS) { + response.answers.push({ + name: question.name, + type: question.type, + class: question.class, + ttl: 300, + ns: "ns1.existing.com", + }); + } else if (question.type === Packet.TYPE.SOA) { + response.answers.push({ + name: question.name, + type: question.type, + class: question.class, + ttl: 300, + primary: "existing.com", + admin: "admin@existing.com", + serial: 2021082701, + refresh: 300, + retry: 3, + expiration: 10, + minimum: 10, + }); + } else if (question.type === Packet.TYPE.SRV) { + response.answers.push({ + name: question.name, + type: question.type, + class: question.class, + ttl: 300, + priority: 5, + weight: 5, + port: 8080, + target: "srv1.existing.com", + }); + } else if (question.type === Packet.TYPE.TXT) { + response.answers.push({ + name: question.name, + type: question.type, + class: question.class, + ttl: 300, + data: "#v=spf1 include:_spf.existing.com ~all", + }); + } else if (question.type === Packet.TYPE.CAA) { + response.answers.push({ + name: question.name, + type: question.type, + class: question.class, + ttl: 300, + flags: 0, + tag: "issue", + value: "ca.existing.com", + }); + } + } + + if (question.name === "4.3.2.1.in-addr.arpa") { + if (question.type === Packet.TYPE.PTR) { + response.answers.push({ + name: question.name, + type: question.type, + class: question.class, + ttl: 300, + domain: "ptr1.existing.com", + }); + } + } + + send(response); + } +}); + +server.on("listening", () => { + console.log("Listening"); + console.log(server.addresses()); +}); + +server.on("close", () => { + console.log("server closed"); +}); + +server.listen({ + udp: 5300, +}); + +/** + * Get human readable request type from request code + * @param {number} code Request code to translate + * @returns {string|void} Human readable request type + */ +function type(code) { + for (let name in Packet.TYPE) { + if (Packet.TYPE[name] === code) { + return name; + } + } +} diff --git a/extra/simple-mqtt-server.js b/extra/simple-mqtt-server.js new file mode 100644 index 0000000..b4b3cb1 --- /dev/null +++ b/extra/simple-mqtt-server.js @@ -0,0 +1,59 @@ +const { log } = require("../src/util"); + +const mqttUsername = "louis1"; +const mqttPassword = "!@#$LLam"; + +class SimpleMqttServer { + aedes = require("aedes")(); + server = require("net").createServer(this.aedes.handle); + + /** + * @param {number} port Port to listen on + */ + constructor(port) { + this.port = port; + } + + /** + * Start the MQTT server + * @returns {void} + */ + start() { + this.server.listen(this.port, () => { + console.log("server started and listening on port ", this.port); + }); + } +} + +let server1 = new SimpleMqttServer(10000); + +server1.aedes.authenticate = function (client, username, password, callback) { + if (username && password) { + console.log(password.toString("utf-8")); + callback(null, username === mqttUsername && password.toString("utf-8") === mqttPassword); + } else { + callback(null, false); + } +}; + +server1.aedes.on("subscribe", (subscriptions, client) => { + console.log(subscriptions); + + 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.start(); diff --git a/extra/start-clear-server.mjs b/extra/start-clear-server.mjs new file mode 100644 index 0000000..a003617 --- /dev/null +++ b/extra/start-clear-server.mjs @@ -0,0 +1,18 @@ +import { rmSync } from "fs"; +import { spawn } from "child_process"; + +const dataDir = "./data/playwright-test"; +const port = "3001"; + +console.log(`Cleaning up ${dataDir}...`); +rmSync(dataDir, { recursive: true, force: true }); + +console.log(`Starting server on port ${port}...`); +const proc = spawn("node", ["--import=tsx", "server/server.js", `--port=${port}`, `--data-dir=${dataDir}`], { + stdio: "inherit", + env: { ...process.env, NODE_ENV: "development" }, +}); + +proc.on("exit", (code) => { + process.exit(code); +}); diff --git a/extra/uptime-kuma-push/.gitignore b/extra/uptime-kuma-push/.gitignore new file mode 100644 index 0000000..a007fea --- /dev/null +++ b/extra/uptime-kuma-push/.gitignore @@ -0,0 +1 @@ +build/* diff --git a/extra/uptime-kuma-push/build.js b/extra/uptime-kuma-push/build.js new file mode 100644 index 0000000..c5db580 --- /dev/null +++ b/extra/uptime-kuma-push/build.js @@ -0,0 +1,47 @@ +const fs = require("fs"); +const platform = process.argv[2]; + +if (!platform) { + console.error("No platform??"); + process.exit(1); +} + +const supportedPlatforms = [ + { + name: "linux/amd64", + bin: "./build/uptime-kuma-push-amd64", + }, + { + name: "linux/arm64", + bin: "./build/uptime-kuma-push-arm64", + }, + { + name: "linux/arm/v7", + bin: "./build/uptime-kuma-push-armv7", + }, +]; + +let platformObj = null; + +// Check if the platform is supported +for (let i = 0; i < supportedPlatforms.length; i++) { + if (supportedPlatforms[i].name === platform) { + platformObj = supportedPlatforms[i]; + break; + } +} + +if (platformObj) { + let filename = platformObj.bin; + + if (!fs.existsSync(filename)) { + console.error(`prebuilt: ${filename} is not found, please build it first`); + process.exit(1); + } + + fs.renameSync(filename, "./uptime-kuma-push"); + process.exit(0); +} else { + console.error("Unsupported platform: " + platform); + process.exit(1); +} diff --git a/extra/uptime-kuma-push/package.json b/extra/uptime-kuma-push/package.json new file mode 100644 index 0000000..f215436 --- /dev/null +++ b/extra/uptime-kuma-push/package.json @@ -0,0 +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" + } +} diff --git a/extra/uptime-kuma-push/uptime-kuma-push.go b/extra/uptime-kuma-push/uptime-kuma-push.go new file mode 100644 index 0000000..69cd1f8 --- /dev/null +++ b/extra/uptime-kuma-push/uptime-kuma-push.go @@ -0,0 +1,44 @@ +package main + +import ( + "fmt" + "net/http" + os "os" + "time" +) + +func main() { + if len(os.Args) < 2 { + fmt.Fprintln(os.Stderr, "Usage: uptime-kuma-push []") + os.Exit(1) + } + + pushURL := os.Args[1] + + var interval time.Duration + + if len(os.Args) >= 3 { + intervalString, err := time.ParseDuration(os.Args[2] + "s") + interval = intervalString + + if err != nil { + fmt.Fprintln(os.Stderr, "Error: Invalid interval", err) + os.Exit(1) + } + + } else { + interval = 60 * time.Second + } + + for { + _, err := http.Get(pushURL) + if err == nil { + fmt.Print("Pushed!") + } else { + fmt.Print("Error: ", err) + } + + fmt.Println(" Sleeping for", interval) + time.Sleep(interval) + } +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..87aa606 --- /dev/null +++ b/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + + Uptime Kuma + + + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..adf8be3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,17534 @@ +{ + "name": "uptime-kuma", + "version": "3.0.0-beta.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "uptime-kuma", + "version": "3.0.0-beta.0", + "license": "MIT", + "dependencies": { + "@better-auth/api-key": "1.6.11", + "@grpc/grpc-js": "~1.8.22", + "@inquirer/core": "~11.2.1", + "@inquirer/prompts": "~8.5.2", + "@louislam/ping": "~0.4.4-mod.1", + "@vvo/tzdb": "~6.198.0", + "args-parser": "~1.3.0", + "axios": "~0.33.0", + "badge-maker": "~3.3.1", + "bcryptjs": "~2.4.3", + "better-auth": "~1.6.11", + "better-sqlite3": "~12.11.1", + "chardet": "~1.4.0", + "cheerio": "~1.0.0", + "chroma-js": "~2.4.2", + "command-exists": "~1.2.9", + "compare-versions": "~3.6.0", + "country-flag-emoji-polyfill": "~0.1.8", + "croner": "~8.1.2", + "dayjs": "~1.11.19", + "dev-null": "~0.1.1", + "express": "~4.22.1", + "express-basic-auth": "~1.2.1", + "express-static-gzip": "~2.1.8", + "feed": "~4.2.2", + "form-data": "~4.0.5", + "gamedig": "~5.3.2", + "globalping": "~0.2.0", + "html-escaper": "~3.0.3", + "http-cookie-agent": "~5.0.4", + "http-graceful-shutdown": "~3.1.15", + "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.2.2", + "kafkajs": "~2.2.4", + "knex": "~3.3.0", + "limiter": "~2.1.0", + "liquidjs": "~10.29.0", + "marked": "~14.1.4", + "mitt": "~3.0.1", + "mongodb": "~4.17.2", + "mqtt": "~4.3.8", + "mssql": "~12.0.0", + "mysql2": "~3.24.2", + "nanoid": "~3.3.11", + "net-snmp": "~3.26.1", + "node-cloudflared-tunnel": "~1.0.10", + "node-radius-utils": "~1.2.0", + "nodemailer": "~10.0.0", + "nostr-tools": "~2.20.0", + "openid-client": "~5.7.1", + "oracledb": "~6.10.0", + "password-hash": "~1.2.2", + "pg": "~8.11.6", + "pg-connection-string": "~2.6.4", + "playwright-core": "~1.61.1", + "prom-client": "~13.2.0", + "prometheus-api-metrics": "~3.2.2", + "promisify-child-process": "~4.1.2", + "protobufjs": "~8.8.0", + "qs": "~6.16.0", + "radius": "~1.1.4", + "redbean-node": "~0.4.0", + "redis": "~5.9.0", + "semver": "~7.5.4", + "socket.io": "~4.8.3", + "socket.io-client": "~4.8.3", + "socks-proxy-agent": "~8.0.5", + "sqlstring": "~2.3.3", + "ssh2-sftp-client": "~12.0.1", + "tar": "~6.2.1", + "tcp-ping": "~0.1.1", + "tldts": "~7.0.23", + "tough-cookie": "~4.1.4", + "tsx": "~4.23.1", + "unlimited-timeout": "~0.1.0", + "validator": "~13.15.26", + "web-push": "~3.6.7", + "ws": "~8.21.3", + "zod": "~4.4.3" + }, + "devDependencies": { + "@actions/github": "~9.1.1", + "@fortawesome/fontawesome-svg-core": "~1.2.36", + "@fortawesome/free-regular-svg-icons": "~5.15.4", + "@fortawesome/free-solid-svg-icons": "~5.15.4", + "@fortawesome/vue-fontawesome": "~3.1.3", + "@playwright/test": "~1.61.1", + "@popperjs/core": "~2.10.2", + "@testcontainers/hivemq": "10.28.0", + "@testcontainers/mariadb": "10.28.0", + "@testcontainers/mssqlserver": "10.28.0", + "@testcontainers/mysql": "11.14.0", + "@testcontainers/oraclefree": "11.14.0", + "@testcontainers/postgresql": "11.14.0", + "@testcontainers/rabbitmq": "10.28.0", + "@types/better-sqlite3": "~7.6.13", + "@types/bootstrap": "~5.1.13", + "@types/node": "^26.1.1", + "@types/web-push": "^3.6.4", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "@vitejs/plugin-vue": "~6.0.7", + "@vue/compiler-sfc": "~3.5.39", + "@vuepic/vue-datepicker": "~3.4.8", + "aedes": "~1.0.0", + "bootstrap": "5.1.3", + "chart.js": "~4.2.1", + "chartjs-adapter-dayjs-4": "~1.0.4", + "concurrently": "^7.6.0", + "cronstrue": "~2.24.0", + "cross-env": "~7.0.3", + "dns2": "~2.0.5", + "dompurify": "~3.4.0", + "eslint": "~8.14.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-jsdoc": "~46.4.6", + "eslint-plugin-vue": "~8.7.1", + "eslint-plugin-vue-scoped-css": "~2.7.2", + "favico.js": "~0.3.10", + "node-ssh": "~13.1.0", + "postcss-html": "~1.8.1", + "postcss-rtlcss": "~5.7.1", + "postcss-scss": "~4.0.9", + "prettier": "^3.8.1", + "prismjs": "~1.30.0", + "qrcode": "~1.5.4", + "rollup-plugin-visualizer": "~7.0.1", + "sass": "^1.101.0", + "ssh2": "~1.17.0", + "stylelint": "^15.11.0", + "stylelint-config-prettier": "^9.0.5", + "stylelint-config-standard": "~25.0.0", + "terser": "~5.15.1", + "test": "~3.3.0", + "testcontainers": "^11.12.0", + "typescript": "~4.4.4", + "v-pagination-3": "~0.1.7", + "vite": "~8.1.2", + "vite-plugin-compression": "^0.5.1", + "vue": "~3.5.39", + "vue-chartjs": "~5.2.0", + "vue-contenteditable": "~3.0.4", + "vue-i18n": "~11.2.8", + "vue-image-crop-upload": "~3.0.3", + "vue-multiselect": "~3.0.0", + "vue-prism-editor": "~2.0.0-alpha.2", + "vue-qrcode": "~1.0.1", + "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" + }, + "engines": { + "node": ">= 26.2.0" + } + }, + "node_modules/@actions/github": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-9.1.1.tgz", + "integrity": "sha512-tL5JbYOBZHc0ngEnCsaDcryUizIUIlQyIMwy1Wkx93H5HzbBJ7TbiPx2PnFjBwZW0Vh05JmfFZhecE6gglYegA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@actions/http-client": "^3.0.2", + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0", + "@octokit/request": "^10.0.7", + "@octokit/request-error": "^7.1.0", + "undici": "^6.23.0" + } + }, + "node_modules/@actions/http-client": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz", + "integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^6.23.0" + } + }, + "node_modules/@aws-sdk/credential-providers": { + "name": "useless-module", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/useless-module/-/useless-module-1.0.0.tgz", + "integrity": "sha512-BV2KT2mDMF/+ya/LDwnjjm+K9CoiyM5BfBwA/5QhXFuTBFGIImTCPIj0r6FviOqyGBSq8t3F5KMTaqizhZscbA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.2.4" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/identity": { + "name": "useless-module", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/useless-module/-/useless-module-1.0.0.tgz", + "integrity": "sha512-BV2KT2mDMF/+ya/LDwnjjm+K9CoiyM5BfBwA/5QhXFuTBFGIImTCPIj0r6FviOqyGBSq8t3F5KMTaqizhZscbA==", + "license": "MIT", + "engines": { + "node": ">=4.2.4" + } + }, + "node_modules/@azure/keyvault-keys": { + "name": "useless-module", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/useless-module/-/useless-module-1.0.0.tgz", + "integrity": "sha512-BV2KT2mDMF/+ya/LDwnjjm+K9CoiyM5BfBwA/5QhXFuTBFGIImTCPIj0r6FviOqyGBSq8t3F5KMTaqizhZscbA==", + "license": "MIT", + "engines": { + "node": ">=4.2.4" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@balena/dockerignore": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", + "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@better-auth/api-key": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@better-auth/api-key/-/api-key-1.6.11.tgz", + "integrity": "sha512-717Bmbs1Y2h0KrPIwrutxI/HwdqKlga1a1OHlL5TrRbN35IxHeY3GilqvyI/92n7RxFGa/AQhIajX0OL+FJkvw==", + "license": "MIT", + "dependencies": { + "zod": "^4.3.6" + }, + "peerDependencies": { + "@better-auth/core": "^1.6.11", + "@better-auth/utils": "0.4.0", + "better-auth": "^1.6.11" + } + }, + "node_modules/@better-auth/core": { + "version": "1.6.31", + "resolved": "https://registry.npmjs.org/@better-auth/core/-/core-1.6.31.tgz", + "integrity": "sha512-6IEBBXKwD/h+A5QLrNmr2j7BybsoVygUUQOK7gbbbarklsn695njU+wfCVYvYo+7SuPXcMxDj8vnj6E0cfog/A==", + "license": "MIT", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.39.0", + "@standard-schema/spec": "^1.1.0", + "zod": "^4.3.6" + }, + "peerDependencies": { + "@better-auth/utils": "0.4.2", + "@better-fetch/fetch": "1.3.1", + "@cloudflare/workers-types": ">=4", + "@opentelemetry/api": "^1.9.0", + "better-call": "1.4.0", + "jose": "^6.1.0", + "kysely": "^0.28.5 || ^0.29.0", + "nanostores": "^1.0.1" + }, + "peerDependenciesMeta": { + "@cloudflare/workers-types": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/@better-auth/drizzle-adapter": { + "version": "1.6.31", + "resolved": "https://registry.npmjs.org/@better-auth/drizzle-adapter/-/drizzle-adapter-1.6.31.tgz", + "integrity": "sha512-hBknvoBygIvgSLC5rrrBQfzOlq4r2uQjehVOZuopUTAcLeUSUVms63g0v1eAC9ofMVCYdn/gRhbcDeSgoiG8lw==", + "license": "MIT", + "peerDependencies": { + "@better-auth/core": "^1.6.31", + "@better-auth/utils": "0.4.2", + "drizzle-orm": "^0.45.2" + }, + "peerDependenciesMeta": { + "drizzle-orm": { + "optional": true + } + } + }, + "node_modules/@better-auth/kysely-adapter": { + "version": "1.6.31", + "resolved": "https://registry.npmjs.org/@better-auth/kysely-adapter/-/kysely-adapter-1.6.31.tgz", + "integrity": "sha512-HFSlMKGnWDtuHBk/AueZU4XIjBCB+n8mSY32uhHQr9DZuwBr199SYfdWaOWxcCUyjW5Bke1Ct1I7UuFfrPjntg==", + "license": "MIT", + "peerDependencies": { + "@better-auth/core": "^1.6.31", + "@better-auth/utils": "0.4.2", + "kysely": "^0.28.17 || ^0.29.0" + }, + "peerDependenciesMeta": { + "kysely": { + "optional": true + } + } + }, + "node_modules/@better-auth/memory-adapter": { + "version": "1.6.31", + "resolved": "https://registry.npmjs.org/@better-auth/memory-adapter/-/memory-adapter-1.6.31.tgz", + "integrity": "sha512-2Q5LVIVI0mUOXpJC56phUyjPCehxtLvKGaaspVlZTU1MVBsK+d7xjhu9/ODI5W64jqLIgokXND13NfkUshw1mg==", + "license": "MIT", + "peerDependencies": { + "@better-auth/core": "^1.6.31", + "@better-auth/utils": "0.4.2" + } + }, + "node_modules/@better-auth/mongo-adapter": { + "version": "1.6.31", + "resolved": "https://registry.npmjs.org/@better-auth/mongo-adapter/-/mongo-adapter-1.6.31.tgz", + "integrity": "sha512-Dfref6WFmogPUnXxjcgCklNlBr9Pi52UldOrZ0P5cK8mApRcii7/1yzFNPq3pS9+SzZ2I2qatonekQpDUYkuWA==", + "license": "MIT", + "peerDependencies": { + "@better-auth/core": "^1.6.31", + "@better-auth/utils": "0.4.2", + "mongodb": "^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "mongodb": { + "optional": true + } + } + }, + "node_modules/@better-auth/prisma-adapter": { + "version": "1.6.31", + "resolved": "https://registry.npmjs.org/@better-auth/prisma-adapter/-/prisma-adapter-1.6.31.tgz", + "integrity": "sha512-jFQa6GboV+1lHysBs+U9MDSJq+KfziiMYfw6vkdQ5BrKGWkpMd/3Kmg+4jibcBRDdlIgvl8Mkuv4CeCS97uKKA==", + "license": "MIT", + "peerDependencies": { + "@better-auth/core": "^1.6.31", + "@better-auth/utils": "0.4.2", + "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", + "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "@prisma/client": { + "optional": true + }, + "prisma": { + "optional": true + } + } + }, + "node_modules/@better-auth/telemetry": { + "version": "1.6.31", + "resolved": "https://registry.npmjs.org/@better-auth/telemetry/-/telemetry-1.6.31.tgz", + "integrity": "sha512-fCMTEohfWN47R9a+VLXf3i324dat0xJgmi4QQ5kiXaKkNe0iUkgb32JnBxnKPZTtKXCju6IBtmrFRvqPgqiDkA==", + "license": "MIT", + "peerDependencies": { + "@better-auth/core": "^1.6.31", + "@better-auth/utils": "0.4.2", + "@better-fetch/fetch": "1.3.1" + } + }, + "node_modules/@better-auth/utils": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.4.2.tgz", + "integrity": "sha512-AUxrvu+HaaODsUyzDxFgwd/8RZ1yZaYo42LXKSrU2oGgR38pS1ij8nqQKNgtTWoYGpNevNXtCfgTy6loHveW9A==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.0.1" + } + }, + "node_modules/@better-fetch/fetch": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@better-fetch/fetch/-/fetch-1.3.1.tgz", + "integrity": "sha512-ABkD1WhyfPZprKRQI3bhATjeiFuNWC9PXhfGWqL+sg/gKrM977oFrYkdb4msM3hgUGonr7KlOsOFT5TU2rht9g==", + "license": "MIT" + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.7.1.tgz", + "integrity": "sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^2.4.1" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.4.1.tgz", + "integrity": "sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.13.tgz", + "integrity": "sha512-XaHr+16KRU9Gf8XLi3q8kDlI18d5vzKSKCY510Vrtc9iNR0NJzbY9hhTmwhzYZj/ZwGL4VmB3TA9hJW0Um2qFA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^2.7.1", + "@csstools/css-tokenizer": "^2.4.1" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.1.1.tgz", + "integrity": "sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.13" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.40.1", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz", + "integrity": "sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "comment-parser": "1.4.0", + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "0.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", + "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "1.2.36", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", + "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz", + "integrity": "sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw==", + "dev": true, + "hasInstallScript": true, + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "5.15.4", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", + "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "dev": true, + "hasInstallScript": true, + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "^0.2.36" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/vue-fontawesome": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.1.3.tgz", + "integrity": "sha512-OHHUTLPEzdwP8kcYIzhioUdUOjZ4zzmi+midwa4bqscza4OJCOvTKJEHkXNz8PgZ23kWci1HkKVX0bm8f9t9gQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6 || ~7", + "vue": ">= 3.0.0 < 4" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.8.22", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.22.tgz", + "integrity": "sha512-oAjDdN7fzbUi+4hZjKG96MR6KTEubAeMpQEb+77qy+3r0Ua5xTFuie6JOLr4ZZgl5g+W5/uRTS2M1V8mVAFPuA==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.7.0", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader/node_modules/protobufjs": { + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", + "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@hey-api/client-fetch": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@hey-api/client-fetch/-/client-fetch-0.10.2.tgz", + "integrity": "sha512-AGiFYDx+y8VT1wlQ3EbzzZtfU8EfV+hLLRTtr8Y/tjYZaxIECwJagVZf24YzNbtEBXONFV50bwcU1wLVGXe1ow==", + "deprecated": "Starting with v0.73.0, this package is bundled directly inside @hey-api/openapi-ts.", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/hey-api" + }, + "peerDependencies": { + "@hey-api/openapi-ts": "< 2" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@inquirer/ansi": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.8.tgz", + "integrity": "sha512-WpQM+Ti6Z40EFwwt+uL2p4UabT+W179zHp6HhLVOzfbwnVn05IPO/eXIZXGNqcT1jbQ15SujNLzQ39k4QPPxBQ==", + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-5.2.5.tgz", + "integrity": "sha512-bRt8J8m+Fot9CXv+zNQGXUq2ET0MggR1fPz7v6edN6MFYmsbfGnMmkmWZJEegMKqrAC8ej/o1sqisHZXZJMAfQ==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/core": "^12.0.3", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/checkbox/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.3.2.tgz", + "integrity": "sha512-Xvr/0HggjddPtGppuqVmxhTw+Hr8PvsZ/k0HmOEaAqQEt80OITNkFWnsdNmyT0/eM4Ab+iJLx2R8rctlEyfSVg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz", + "integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.7", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-5.3.3.tgz", + "integrity": "sha512-YsKkS2q63IiLtaDK/9nqzdComN97SDQrmKiyNggN+ceP4ty+Z6VwyTz3FpjeUWeW1Efss2xHFKCC9sx7hnrsxg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/external-editor": "^3.0.5", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-5.1.5.tgz", + "integrity": "sha512-uHuXLmXW+TtIfT/9vSBotypAkqn1n34Ul+CLGPos/xANyO4Ff5xZzkYhbKR4NEcfVK4a9mHQOpwVZzluSHFRGw==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-3.0.5.tgz", + "integrity": "sha512-f3QQJRIX5ZEneBHNUIuPjmbdzHnmRFJA8r2dkcb8q+OM5Uv5KtnuAttQumnrjcBVBM3mcTX1CkmtAkU58VRZxg==", + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.2" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor/node_modules/chardet": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", + "license": "MIT" + }, + "node_modules/@inquirer/external-editor/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@inquirer/figures": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.9.tgz", + "integrity": "sha512-EAWgUTGQ/Umgga51dE3B2PUHbufuXarDfg86uVgoSgNHNNQnyFKcOrQLWVqYMghuSyHh8+2HUH0Js9cTC1WAdg==", + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@inquirer/input": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.1.6.tgz", + "integrity": "sha512-HtcJhB2QFVXbLuJ5S3syhNbTUVxYvwqV4VRBDkQceBloC9bmTViUoRFP5PbSaDZb3HzfPmpuU/gG4ybVBz4FHA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/input/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-4.2.3.tgz", + "integrity": "sha512-6Yuwh1NGSbu1Lo4N1EWjXs1jKRntLg/ZCwhmeorEHde90v1XxAozdbd4Iu30eOQLW+6h1hp2O9ujNfLSbTPJnA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-5.2.2.tgz", + "integrity": "sha512-W9zYdyzogK+6110mqwaSJWCBu2yA5Q/OfnGSjjZB1bNpHlmUozXxTl0+QOZBNeVd6Qo81/qT75gW05gLAtITxw==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/core": "^12.0.3", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-8.5.2.tgz", + "integrity": "sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==", + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^5.2.1", + "@inquirer/confirm": "^6.1.1", + "@inquirer/editor": "^5.2.2", + "@inquirer/expand": "^5.1.1", + "@inquirer/input": "^5.1.2", + "@inquirer/number": "^4.1.1", + "@inquirer/password": "^5.1.1", + "@inquirer/rawlist": "^5.3.1", + "@inquirer/search": "^4.2.1", + "@inquirer/select": "^5.2.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-5.3.5.tgz", + "integrity": "sha512-1oHky1ONfCOwNrnkQGDE1oaSij/3fI6HFMSf2H/WsGO2lEyDX9My82iggITSy9ddSZ8yk8j9v41OI0fVoSIoaA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.3.3.tgz", + "integrity": "sha512-fyuIU1Nbpvwlikjg3gXwJFDI11+EFjqQ7P+iByfmivIKQ1vmaykNrD/vy5unHuUqUpsOsnvJ25//tPF7E/RBRA==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^12.0.3", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.2.5.tgz", + "integrity": "sha512-9kc15hr8r/kI+3DO/xLog5nOzTz1jqsHXa6JBFzmQKhkoJ8Slda1I1L/uD8ZSZ9tF1yp79wwXe7mclvX1rqR2Q==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/core": "^12.0.3", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select/node_modules/@inquirer/core": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-12.0.3.tgz", + "integrity": "sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.8", + "@inquirer/figures": "^2.0.9", + "@inquirer/type": "4.1.1", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.1.1.tgz", + "integrity": "sha512-yJoHYrMnxIsJZCY+0Vb66Dy3he3kL3e2wOBKhoSwWWAzZAY82emlxwgprCtp6yRixvNRNq9ztfRWQYPNr3Go7A==", + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@intlify/core-base": { + "version": "11.2.8", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.2.8.tgz", + "integrity": "sha512-nBq6Y1tVkjIUsLsdOjDSJj4AsjvD0UG3zsg9Fyc+OivwlA/oMHSKooUy9tpKj0HqZ+NWFifweHavdljlBLTwdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@intlify/message-compiler": "11.2.8", + "@intlify/shared": "11.2.8" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "11.2.8", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.2.8.tgz", + "integrity": "sha512-A5n33doOjmHsBtCN421386cG1tWp5rpOjOYPNsnpjIJbQ4POF0QY2ezhZR9kr0boKwaHjbOifvyQvHj2UTrDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@intlify/shared": "11.2.8", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "11.2.8", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.2.8.tgz", + "integrity": "sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-joda/core": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@js-joda/core/-/core-6.1.0.tgz", + "integrity": "sha512-H8NTMRDJqad/leyv/D/A3kSOsf5/58Ydj4DJGDyaCWk9OU/zuZOLhndVffJgQjsgrn5GC0znHMHie7TfvPPG4w==", + "license": "BSD-3-Clause" + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@louislam/ping": { + "version": "0.4.4-mod.1", + "resolved": "https://registry.npmjs.org/@louislam/ping/-/ping-0.4.4-mod.1.tgz", + "integrity": "sha512-uMq6qwL9/VYh2YBbDEhM7ZzJ8YON+juw/3k+28P3s9ue3uDMQ56MNPfywXoRpsxkU8RgjN0TDzEhQDzO1WisMw==", + "license": "MIT", + "dependencies": { + "command-exists": "~1.2.9", + "underscore": "~1.13.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.5.4.tgz", + "integrity": "sha512-05UC0jQsjKAOuXQ0H9Ud9vUTJpZIg+n/FinpR30tI5I8pY2inTfPOZ5OF/cg3Ce/N9MoD1xhRCeOsJtuTbFYlw==", + "license": "MIT", + "optional": true, + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.4.tgz", + "integrity": "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, + "node_modules/@noble/ciphers": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-2.4.0.tgz", + "integrity": "sha512-AnjFn0Jv92laAkvMrghlFZq4qQCIN/4DxFV/eooqtC2YTjB7kBeLMS2T9KJX4Dn+ZVXLOwK0lSgqDtx9gvxtiw==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodable/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/core": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.8.tgz", + "integrity": "sha512-L7y8eYc+AwxGr2PWI4WFt1VG4TiJ66c26BD16mXpYIlXxG0SMigM1+m4aTSlYyBr5BlQsGAlz8uDCoZN4SEMcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.5", + "@octokit/request": "^10.0.16", + "@octokit/request-error": "^7.1.2", + "@octokit/types": "^18.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/endpoint": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.5.tgz", + "integrity": "sha512-iXa654H3yFafF/ieHkukfbgWo2rmXD2ceD0ZOtrPhw1bc3FDch1d9N/TNs0FQ1/cIbwb7kspUX8jzIs8nzb9DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^18.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/graphql": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.5.tgz", + "integrity": "sha512-bt/hm03LeU6Vy7FwTrkkC9p3XGT/lBwClglMqxBSe5/q0E5CdJTXeAqEI0vlw89/LF/G6tryTIH8HirZ3prMVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^10.0.16", + "@octokit/types": "^18.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-29.0.1.tgz", + "integrity": "sha512-9qWOMFNxxLokERcms42rU0PTLqQmVs7g5E41TI4mCOxmpFayD1rfC7XxOL55cG9MBZLFlC31BrR37myMKardwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^27.0.0" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^27.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "10.0.16", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.16.tgz", + "integrity": "sha512-A0zWGjHzISIb+9ccG8s0dq7LKO5zVpJLRICjgUb+sJxEWqn8RUHB1rD3AE51+PECvXHIxqZ1VVvs4fHTSD9nUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^11.0.5", + "@octokit/request-error": "^7.1.2", + "@octokit/types": "^18.0.0", + "content-type": "^3.0.0", + "json-with-bigint": "^3.5.12", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/request-error": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.2.tgz", + "integrity": "sha512-XZRuT3xZ84D3gYErI1DZvhJ33dCWVV6uzBtWkaBB4TvA/L6eOeTZodxLFVB44bBEEo3vEx7y00UfX1tBLrtLRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^18.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/types": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-18.0.0.tgz", + "integrity": "sha512-l6bAF43PNxkJp6g+W4PjoUSSkxHomXw2nOum5CTftJz1NlV3vu93NImgOYtLf6CbBUb5j+fiuzW0PPQ5JTSvZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^29.0.1" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", + "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.6.0", + "@parcel/watcher-darwin-arm64": "2.6.0", + "@parcel/watcher-darwin-x64": "2.6.0", + "@parcel/watcher-freebsd-x64": "2.6.0", + "@parcel/watcher-linux-arm-glibc": "2.6.0", + "@parcel/watcher-linux-arm-musl": "2.6.0", + "@parcel/watcher-linux-arm64-glibc": "2.6.0", + "@parcel/watcher-linux-arm64-musl": "2.6.0", + "@parcel/watcher-linux-x64-glibc": "2.6.0", + "@parcel/watcher-linux-x64-musl": "2.6.0", + "@parcel/watcher-win32-arm64": "2.6.0", + "@parcel/watcher-win32-x64": "2.6.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", + "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", + "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", + "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", + "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", + "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", + "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", + "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", + "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", + "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", + "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", + "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", + "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@playwright/test": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", + "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@popperjs/core": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.2.tgz", + "integrity": "sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", + "license": "BSD-3-Clause" + }, + "node_modules/@redis/bloom": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.9.0.tgz", + "integrity": "sha512-W9D8yfKTWl4tP8lkC3MRYkMz4OfbuzE/W8iObe0jFgoRmgMfkBV+Vj38gvIqZPImtY0WB34YZkX3amYuQebvRQ==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0" + } + }, + "node_modules/@redis/client": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.9.0.tgz", + "integrity": "sha512-EI0Ti5pojD2p7TmcS7RRa+AJVahdQvP/urpcSbK/K9Rlk6+dwMJTQ354pCNGCwfke8x4yKr5+iH85wcERSkwLQ==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@redis/json": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-5.9.0.tgz", + "integrity": "sha512-Bm2jjLYaXdUWPb9RaEywxnjmzw7dWKDZI4MS79mTWPV16R982jVWBj6lY2ZGelJbwxHtEVg4/FSVgYDkuO/MxA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0" + } + }, + "node_modules/@redis/search": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-5.9.0.tgz", + "integrity": "sha512-jdk2csmJ29DlpvCIb2ySjix2co14/0iwIT3C0I+7ZaToXgPbgBMB+zfEilSuncI2F9JcVxHki0YtLA0xX3VdpA==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0" + } + }, + "node_modules/@redis/time-series": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.9.0.tgz", + "integrity": "sha512-W6ILxcyOqhnI7ELKjJXOktIg3w4+aBHugDbVpgVLPZ+YDjObis1M0v7ZzwlpXhlpwsfePfipeSK+KWNuymk52w==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@redis/client": "^5.9.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", + "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@tediousjs/connection-string": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.6.0.tgz", + "integrity": "sha512-GxlsW354Vi6QqbUgdPyQVcQjI7cZBdGV5vOYVYuCVDTylx2wl3WHR2HlhcxxHTrMigbelpXsdcZso+66uxPfow==", + "license": "MIT" + }, + "node_modules/@testcontainers/hivemq": { + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/@testcontainers/hivemq/-/hivemq-10.28.0.tgz", + "integrity": "sha512-d/4EH6DiMx3bA3XakGo89B8A3cLMtVThc7IDaVsFXlasCoHqGoxbr2k3azK1tcvq3eKpM5gEJ7VWY7vnWYGjDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "testcontainers": "^10.28.0" + } + }, + "node_modules/@testcontainers/hivemq/node_modules/@types/dockerode": { + "version": "3.3.47", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.47.tgz", + "integrity": "sha512-ShM1mz7rCjdssXt7Xz0u1/R2BJC7piWa3SJpUBiVjCf2A3XNn4cP6pUVaD8bLanpPVVn4IKzJuw3dOvkJ8IbYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@testcontainers/hivemq/node_modules/docker-compose": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@testcontainers/hivemq/node_modules/properties-reader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", + "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/properties?sponsor=1" + } + }, + "node_modules/@testcontainers/hivemq/node_modules/tar-fs": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.3.tgz", + "integrity": "sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/@testcontainers/hivemq/node_modules/tar-stream": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz", + "integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/@testcontainers/hivemq/node_modules/testcontainers": { + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.28.0.tgz", + "integrity": "sha512-1fKrRRCsgAQNkarjHCMKzBKXSJFmzNTiTbhb5E/j5hflRXChEtHvkefjaHlgkNUjfw92/Dq8LTgwQn6RDBFbMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@types/dockerode": "^3.3.35", + "archiver": "^7.0.1", + "async-lock": "^1.4.1", + "byline": "^5.0.0", + "debug": "^4.3.5", + "docker-compose": "^0.24.8", + "dockerode": "^4.0.5", + "get-port": "^7.1.0", + "proper-lockfile": "^4.1.2", + "properties-reader": "^2.3.0", + "ssh-remote-port-forward": "^1.0.4", + "tar-fs": "^3.0.7", + "tmp": "^0.2.3", + "undici": "^5.29.0" + } + }, + "node_modules/@testcontainers/hivemq/node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/@testcontainers/mariadb": { + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/@testcontainers/mariadb/-/mariadb-10.28.0.tgz", + "integrity": "sha512-+ETpRbHOWxEj6uwMfhTVvE6ap0U+olD+v8XbAE2+88YgsHzlmfWWi/EXsOfW1VZsWblYE5kR0k1O//a9Sei4Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "testcontainers": "^10.28.0" + } + }, + "node_modules/@testcontainers/mariadb/node_modules/@types/dockerode": { + "version": "3.3.47", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.47.tgz", + "integrity": "sha512-ShM1mz7rCjdssXt7Xz0u1/R2BJC7piWa3SJpUBiVjCf2A3XNn4cP6pUVaD8bLanpPVVn4IKzJuw3dOvkJ8IbYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@testcontainers/mariadb/node_modules/docker-compose": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@testcontainers/mariadb/node_modules/properties-reader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", + "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/properties?sponsor=1" + } + }, + "node_modules/@testcontainers/mariadb/node_modules/tar-fs": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.3.tgz", + "integrity": "sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/@testcontainers/mariadb/node_modules/tar-stream": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz", + "integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/@testcontainers/mariadb/node_modules/testcontainers": { + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.28.0.tgz", + "integrity": "sha512-1fKrRRCsgAQNkarjHCMKzBKXSJFmzNTiTbhb5E/j5hflRXChEtHvkefjaHlgkNUjfw92/Dq8LTgwQn6RDBFbMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@types/dockerode": "^3.3.35", + "archiver": "^7.0.1", + "async-lock": "^1.4.1", + "byline": "^5.0.0", + "debug": "^4.3.5", + "docker-compose": "^0.24.8", + "dockerode": "^4.0.5", + "get-port": "^7.1.0", + "proper-lockfile": "^4.1.2", + "properties-reader": "^2.3.0", + "ssh-remote-port-forward": "^1.0.4", + "tar-fs": "^3.0.7", + "tmp": "^0.2.3", + "undici": "^5.29.0" + } + }, + "node_modules/@testcontainers/mariadb/node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/@testcontainers/mssqlserver": { + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/@testcontainers/mssqlserver/-/mssqlserver-10.28.0.tgz", + "integrity": "sha512-edyXfjU6rK7Jt/2Pei4HI4tt2rgOBJ6MM79YbwAp60fU0XIQHEdaDdlVtpVjArVNfnaS0kMUG3WxGINjtDhOyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "testcontainers": "^10.28.0" + } + }, + "node_modules/@testcontainers/mssqlserver/node_modules/@types/dockerode": { + "version": "3.3.47", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.47.tgz", + "integrity": "sha512-ShM1mz7rCjdssXt7Xz0u1/R2BJC7piWa3SJpUBiVjCf2A3XNn4cP6pUVaD8bLanpPVVn4IKzJuw3dOvkJ8IbYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@testcontainers/mssqlserver/node_modules/docker-compose": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@testcontainers/mssqlserver/node_modules/properties-reader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", + "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/properties?sponsor=1" + } + }, + "node_modules/@testcontainers/mssqlserver/node_modules/tar-fs": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.3.tgz", + "integrity": "sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/@testcontainers/mssqlserver/node_modules/tar-stream": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz", + "integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/@testcontainers/mssqlserver/node_modules/testcontainers": { + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.28.0.tgz", + "integrity": "sha512-1fKrRRCsgAQNkarjHCMKzBKXSJFmzNTiTbhb5E/j5hflRXChEtHvkefjaHlgkNUjfw92/Dq8LTgwQn6RDBFbMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@types/dockerode": "^3.3.35", + "archiver": "^7.0.1", + "async-lock": "^1.4.1", + "byline": "^5.0.0", + "debug": "^4.3.5", + "docker-compose": "^0.24.8", + "dockerode": "^4.0.5", + "get-port": "^7.1.0", + "proper-lockfile": "^4.1.2", + "properties-reader": "^2.3.0", + "ssh-remote-port-forward": "^1.0.4", + "tar-fs": "^3.0.7", + "tmp": "^0.2.3", + "undici": "^5.29.0" + } + }, + "node_modules/@testcontainers/mssqlserver/node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/@testcontainers/mysql": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@testcontainers/mysql/-/mysql-11.14.0.tgz", + "integrity": "sha512-0/OKd1gOvnl0qS+RIpmT6J0XKWpjkVyIbOtS3cFTVR8t6Ps7W9TV0U+zp7FPxCmitWuWYPXwH/+RQXF+1jZuZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "testcontainers": "^11.14.0" + } + }, + "node_modules/@testcontainers/oraclefree": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@testcontainers/oraclefree/-/oraclefree-11.14.0.tgz", + "integrity": "sha512-7Lm1309BZQiU07bntF6gNzPGNqQHf/OALQ4Wz2HKnTbmNLWBI13XN119hc/Xx4HXKW6rGT5/fnNqi7mkJB9s6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "testcontainers": "^11.14.0" + } + }, + "node_modules/@testcontainers/postgresql": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@testcontainers/postgresql/-/postgresql-11.14.0.tgz", + "integrity": "sha512-wYbJn8GRTj8qfqzfVubxioYWlHJU/ImIjuzPwyy9C5Qfo6g3GLduPZAj+BifvqTZjgT3gd4gFVLCPhBji7dc1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "testcontainers": "^11.14.0" + } + }, + "node_modules/@testcontainers/rabbitmq": { + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/@testcontainers/rabbitmq/-/rabbitmq-10.28.0.tgz", + "integrity": "sha512-Gl8/gAYfRCsjuhTfAIT7/0e49ozMRe05RDFr2CFQWcZDgB4A7qx2QaUqEIhcQnHqRjfEQTWoV1PovnGNZU+dGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "testcontainers": "^10.28.0" + } + }, + "node_modules/@testcontainers/rabbitmq/node_modules/@types/dockerode": { + "version": "3.3.47", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.47.tgz", + "integrity": "sha512-ShM1mz7rCjdssXt7Xz0u1/R2BJC7piWa3SJpUBiVjCf2A3XNn4cP6pUVaD8bLanpPVVn4IKzJuw3dOvkJ8IbYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@testcontainers/rabbitmq/node_modules/docker-compose": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@testcontainers/rabbitmq/node_modules/properties-reader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", + "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/properties?sponsor=1" + } + }, + "node_modules/@testcontainers/rabbitmq/node_modules/tar-fs": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.3.tgz", + "integrity": "sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/@testcontainers/rabbitmq/node_modules/tar-stream": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz", + "integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/@testcontainers/rabbitmq/node_modules/testcontainers": { + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.28.0.tgz", + "integrity": "sha512-1fKrRRCsgAQNkarjHCMKzBKXSJFmzNTiTbhb5E/j5hflRXChEtHvkefjaHlgkNUjfw92/Dq8LTgwQn6RDBFbMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@types/dockerode": "^3.3.35", + "archiver": "^7.0.1", + "async-lock": "^1.4.1", + "byline": "^5.0.0", + "debug": "^4.3.5", + "docker-compose": "^0.24.8", + "dockerode": "^4.0.5", + "get-port": "^7.1.0", + "proper-lockfile": "^4.1.2", + "properties-reader": "^2.3.0", + "ssh-remote-port-forward": "^1.0.4", + "tar-fs": "^3.0.7", + "tmp": "^0.2.3", + "undici": "^5.29.0" + } + }, + "node_modules/@testcontainers/rabbitmq/node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.13", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bootstrap": { + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.1.13.tgz", + "integrity": "sha512-1hIIOgfkMlyQCQz/3ae53xr6ZN2d6EDj/n3G+Sh/LBsBUVigyDmnCbLwsaXJJ1GBGlkjgfXVoyIvEPowQw25xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@popperjs/core": "^2.9.2" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/content-disposition": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.9.tgz", + "integrity": "sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==", + "license": "MIT" + }, + "node_modules/@types/cookies": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.9.2.tgz", + "integrity": "sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/express": "*", + "@types/keygrip": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/docker-modem": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", + "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/dockerode": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-4.0.1.tgz", + "integrity": "sha512-cmUpB+dPN955PxBEuXE3f6lKO1hHiIGYJA46IVF3BJpNsZGvtBDcRnlrHYHtOH/B6vtDOyl2kZ2ShAu3mgc27Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-assert": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.6.tgz", + "integrity": "sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==", + "license": "MIT" + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "license": "MIT" + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keygrip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz", + "integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==", + "license": "MIT" + }, + "node_modules/@types/koa": { + "version": "2.15.2", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.15.2.tgz", + "integrity": "sha512-CB+iyjjh1uS5N6/CKwXvw0qA7USMS2WVc4Tjf660yCjhdvqzNr8gdFcIawB41zGGptOQ+d1fnpaQWIIUXYxR3w==", + "license": "MIT", + "dependencies": { + "@types/accepts": "*", + "@types/content-disposition": "*", + "@types/cookies": "*", + "@types/http-assert": "*", + "@types/http-errors": "*", + "@types/keygrip": "*", + "@types/koa-compose": "*", + "@types/node": "*" + } + }, + "node_modules/@types/koa-compose": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.9.tgz", + "integrity": "sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==", + "license": "MIT", + "dependencies": { + "@types/koa": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.5.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.5.1.tgz", + "integrity": "sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g==", + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/readable-stream": { + "version": "4.0.24", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.24.tgz", + "integrity": "sha512-NRvUNC/JFGPJvqdAfEve8oginbM6V08u5NzLWpG8MwA2kTPOLnqk+wpwuPT+mp3aUsxyuT6m2gnrPuHYCruzEg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/ssh2": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.6.tgz", + "integrity": "sha512-oGdxhBqcRTwSTKFm+9EiKzkNVYRLEFkcW44lhguvBalGJbWfGnDt/ezwSUZc+SF9m9bMc3VyklNAtp7zICjS5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.18" + } + }, + "node_modules/@types/ssh2-streams": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.13.tgz", + "integrity": "sha512-faHyY3brO9oLEA0QlcO8N2wT7R0+1sHWZvQ+y3rMLwdY1ZyS1z0W3t65j9PqT4HmQ6ALzNe7RZlNuCNE0wBSWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ssh2/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ssh2/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/web-push": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/@types/web-push/-/web-push-3.6.4.tgz", + "integrity": "sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.6.tgz", + "integrity": "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og==", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz", + "integrity": "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.42.tgz", + "integrity": "sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/shared": "3.5.42", + "entities": "^7.0.1", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.42.tgz", + "integrity": "sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.42.tgz", + "integrity": "sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@vue/compiler-core": "3.5.42", + "@vue/compiler-dom": "3.5.42", + "@vue/compiler-ssr": "3.5.42", + "@vue/shared": "3.5.42", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.19", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.42.tgz", + "integrity": "sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.42.tgz", + "integrity": "sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.42.tgz", + "integrity": "sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.42.tgz", + "integrity": "sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.42", + "@vue/runtime-core": "3.5.42", + "@vue/shared": "3.5.42", + "csstype": "^3.2.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.42.tgz", + "integrity": "sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.42", + "@vue/runtime-dom": "3.5.42", + "@vue/shared": "3.5.42" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.42.tgz", + "integrity": "sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vuepic/vue-datepicker": { + "version": "3.4.8", + "resolved": "https://registry.npmjs.org/@vuepic/vue-datepicker/-/vue-datepicker-3.4.8.tgz", + "integrity": "sha512-nbuMA7IgjtT99LqcjSTSUcl7omTZSB+7vYSWQ9gQm31Frm/1wn54fT1Q0HaRD9nHXX982AACbqeND4K80SKONw==", + "dev": true, + "license": "MIT", + "dependencies": { + "date-fns": "^2.29.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "vue": ">=3.2.0" + } + }, + "node_modules/@vvo/tzdb": { + "version": "6.198.0", + "resolved": "https://registry.npmjs.org/@vvo/tzdb/-/tzdb-6.198.0.tgz", + "integrity": "sha512-bNRWBhWYl0edVgyX6AYbhoCM2tk2lXJjGCyO2VDc2xn6Dw8dLd7WGj2DDXkVOkmOIQTNjEAcxrEpIzz5pWVwFg==", + "license": "MIT" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/aedes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/aedes/-/aedes-1.0.2.tgz", + "integrity": "sha512-yVb/7ZEcUAV3MqMkMHv5aOTnyeHDLDlkHo0tD1YnBoC3+rdUcMfILTvTl7i4Y7zTaujQD5TTznXdJFOBeaUlrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "aedes-packet": "^3.0.0", + "aedes-persistence": "^10.2.2", + "end-of-stream": "^1.4.5", + "fastfall": "^1.5.1", + "fastparallel": "^2.4.1", + "fastseries": "^2.0.0", + "hyperid": "^3.3.0", + "mqemitter": "^7.1.0", + "mqtt-packet": "^9.0.2", + "retimer": "^4.0.0", + "reusify": "^1.1.0", + "uuid": "^11.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/aedes" + }, + "peerDependencies": { + "aedes-persistence-level": "^9.1.2", + "aedes-persistence-mongodb": "^9.3.1", + "aedes-persistence-redis": "^11.2.1" + }, + "peerDependenciesMeta": { + "aedes-persistence-level": { + "optional": true + }, + "aedes-persistence-mongodb": { + "optional": true + }, + "aedes-persistence-redis": { + "optional": true + } + } + }, + "node_modules/aedes-packet": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aedes-packet/-/aedes-packet-3.0.0.tgz", + "integrity": "sha512-swASey0BxGs4/npZGWoiVDmnEyPvVFIRY6l2LVKL4rbiW8IhcIGDLfnb20Qo8U20itXlitAKPQ3MVTEbOGG5ZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mqtt-packet": "^7.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/aedes-packet/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/aedes-packet/node_modules/mqtt-packet": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-7.1.2.tgz", + "integrity": "sha512-FFZbcZ2omsf4c5TxEQfcX9hI+JzDpDKPT46OmeIBpVA7+t32ey25UNqlqNXTmeZOr5BLsSIERpQQLsFWJS94SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/aedes-packet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/aedes-packet/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/aedes-persistence": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/aedes-persistence/-/aedes-persistence-10.3.1.tgz", + "integrity": "sha512-C/z0zLEXY9PkUH+7pz31H5gGzlQZxqhFONK1lt5VON0hiHXQq6tr4cSIAtFUVQ1kyUdd7SsfkeKLAECp3gjjpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "aedes-packet": "^4.0.1", + "qlobber": "^8.0.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/aedes-persistence/node_modules/aedes-packet": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/aedes-packet/-/aedes-packet-4.0.1.tgz", + "integrity": "sha512-PLjKJElXEYB66RzHhJCw0SrRLfSmGRbbg2mtO5t/6FAbFp8vC0cG5ErhvPvfa9iKBrKZwoluWLgn8doBU8sRAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mqtt-packet": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/anafanafo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-2.0.0.tgz", + "integrity": "sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==", + "license": "MIT", + "dependencies": { + "char-width-table-consumer": "^1.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/archiver/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/archiver/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/archiver/node_modules/tar-stream": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz", + "integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/args-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/args-parser/-/args-parser-1.3.0.tgz", + "integrity": "sha512-If3Zi4BSjlQIJ9fgAhSiKi0oJtgMzSqh0H4wvl7XSeO16FKx7QqaHld8lZeEajPX7y1C5qKKeNgyrfyvmjmjUQ==", + "license": "Apache-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/asn1-ber": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/asn1-ber/-/asn1-ber-1.2.2.tgz", + "integrity": "sha512-CbNem/7hxrjSiOAOOTX4iZxu+0m3jiLqlsERQwwPM1IDR/22M8IPpA1VVndCLw5KtjRYyRODbvAEIfuTogNDng==", + "license": "MIT" + }, + "node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/await-lock": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", + "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==", + "license": "MIT" + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.33.0.tgz", + "integrity": "sha512-juz19g7B3UWT9r3+tgRFQf2Bdy6IKDVroiyuVOUrkILVKHpqHL++K1l8ybvfdEP9B/VE72vk8vllbnedVCm/og==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/babel-plugin-add-module-exports": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz", + "integrity": "sha512-3AN/9V/rKuv90NG65m4tTHsI04XrCKsWbztIcW7a8H5iIN7WlvWucRtVV0V/rT4QvtA11n5Vmp20fLwfMWqp6g==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "node_modules/badge-maker": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/badge-maker/-/badge-maker-3.3.1.tgz", + "integrity": "sha512-OO/PS7Zg2E6qaUWzHEHt21Q5VjcFBAJVA8ztgT/fIdSZFBUwoyeo0ZhA6V5tUM8Vcjq8DJl6jfGhpjESssyqMQ==", + "license": "CC0-1.0", + "dependencies": { + "anafanafo": "2.0.0", + "css-color-converter": "^2.0.0" + }, + "bin": { + "badge": "lib/badge-cli.js" + }, + "engines": { + "node": ">= 10", + "npm": ">= 5" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", + "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.1.tgz", + "integrity": "sha512-N1nnXdHZAOSstz0XiHikGS4HGMH4CnSwhqWdGQQMqqdvp4Jybm9sE3R1WVnpWVd4SFkc8ryPDBLViNLwiEqECg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.28.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-path": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.2.tgz", + "integrity": "sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bare-stream": { + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.4.tgz", + "integrity": "sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.8.1", + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.4.tgz", + "integrity": "sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/barse": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/barse/-/barse-0.4.3.tgz", + "integrity": "sha512-UEpvriJqAn8zuVinYICuKoPttZy3XxXEoqX/V2uYAL4zzJRuNzCK3+20nAu3YUIa2U7G53kf90wfBIp9/A+Odw==", + "license": "MIT", + "dependencies": { + "readable-stream": "~1.0.2" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/better-auth": { + "version": "1.6.31", + "resolved": "https://registry.npmjs.org/better-auth/-/better-auth-1.6.31.tgz", + "integrity": "sha512-FSFgoBhRn7bFEpTDxICMo8PizqG1ARkEOqSlOujyUA65mzlZwF6EQ7WymywhSi5J2uqUck0Fdo3xw+WY/ieJcw==", + "license": "MIT", + "dependencies": { + "@better-auth/core": "1.6.31", + "@better-auth/drizzle-adapter": "1.6.31", + "@better-auth/kysely-adapter": "1.6.31", + "@better-auth/memory-adapter": "1.6.31", + "@better-auth/mongo-adapter": "1.6.31", + "@better-auth/prisma-adapter": "1.6.31", + "@better-auth/telemetry": "1.6.31", + "@better-auth/utils": "0.4.2", + "@better-fetch/fetch": "1.3.1", + "@noble/ciphers": "^2.1.1", + "@noble/hashes": "^2.0.1", + "better-call": "1.4.0", + "defu": "^6.1.4", + "jose": "^6.1.3", + "kysely": "^0.28.17 || ^0.29.0", + "nanostores": "^1.1.1", + "zod": "^4.3.6" + }, + "peerDependencies": { + "@lynx-js/react": "*", + "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", + "@sveltejs/kit": "^2.0.0", + "@tanstack/react-start": "^1.0.0", + "@tanstack/solid-start": "^1.0.0", + "better-sqlite3": "^12.0.0", + "drizzle-kit": ">=0.31.4", + "drizzle-orm": "^0.45.2", + "mongodb": "^6.0.0 || ^7.0.0", + "mysql2": "^3.0.0", + "next": "^14.0.0 || ^15.0.0 || ^16.0.0", + "pg": "^8.0.0", + "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0", + "solid-js": "^1.0.0", + "svelte": "^4.0.0 || ^5.0.0", + "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", + "vue": "^3.0.0" + }, + "peerDependenciesMeta": { + "@lynx-js/react": { + "optional": true + }, + "@prisma/client": { + "optional": true + }, + "@sveltejs/kit": { + "optional": true + }, + "@tanstack/react-start": { + "optional": true + }, + "@tanstack/solid-start": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "drizzle-kit": { + "optional": true + }, + "drizzle-orm": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "next": { + "optional": true + }, + "pg": { + "optional": true + }, + "prisma": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "solid-js": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vitest": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/better-call": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/better-call/-/better-call-1.4.0.tgz", + "integrity": "sha512-bBKOT4vv1kZLDgxVePdilk/Jwkn+dtRRsmi3DzHcDP+WnswyVl6dR59l2HEeP/0cB+bDoopASAesWDPIdd/zZA==", + "license": "MIT", + "dependencies": { + "@better-auth/utils": "^0.5.0", + "@better-fetch/fetch": "^1.3.1", + "rou3": "^0.9.1", + "set-cookie-parser": "^3.1.2" + }, + "peerDependencies": { + "zod": "^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/better-call/node_modules/@better-auth/utils": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.5.0.tgz", + "integrity": "sha512-BL8W4EfIZFwlu0r54m3v1ztjDhu6dDe/amLTm0xybmbZaNgYUqhD3SjpAsnq0q8YD6/ki4iwIgxJNLP/N3TxiA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^2.0.1" + } + }, + "node_modules/better-sqlite3": { + "version": "12.11.1", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.11.1.tgz", + "integrity": "sha512-dq9AtApgg5PGFtBzPFSBl3HZQjHok5gaQCM6zh2Yk0aSmDCs1CbnVI8/HgASQkNKsWFpseIO9beg5xxpYhbIfA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x || 26.x" + } + }, + "node_modules/binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==", + "license": "CC0-1.0" + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bintrees": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", + "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==", + "license": "MIT" + }, + "node_modules/bl": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.6.tgz", + "integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/bn.js": { + "version": "4.12.5", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.5.tgz", + "integrity": "sha512-3aRg6/JxfffFD+OlOjOFR3Vo79l39ooBTFucxx+MT3dhCtzn3EmiUPQo+6/OZuI2jbXi3YKgmiTFBgChQMwIRQ==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.8", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.8.tgz", + "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.16.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/bootstrap": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", + "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + }, + "peerDependencies": { + "@popperjs/core": "^2.10.2" + } + }, + "node_modules/brace-expansion": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "license": "Apache-2.0", + "dependencies": { + "buffer": "^5.6.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/buildcheck": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", + "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", + "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.3.0", + "map-obj": "^4.1.0", + "quick-lru": "^5.1.1", + "type-fest": "^1.2.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/char-width-table-consumer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz", + "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==", + "license": "MIT", + "dependencies": { + "binary-search": "^1.3.5" + } + }, + "node_modules/chardet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-1.4.0.tgz", + "integrity": "sha512-NpwMDdSIprbYx1CLnfbxEIarI0Z+s9MssEgggMNheGM+WD68yOhV7IEA/3r6tr0yTRgQD0HuZJDw32s99i6L+A==", + "license": "MIT" + }, + "node_modules/chart.js": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.2.1.tgz", + "integrity": "sha512-6YbpQ0nt3NovAgOzbkSSeeAQu/3za1319dPUQTXn9WcOpywM8rGKxJHrhS8V8xEkAlk8YhEfjbuAPfUyp6jIsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": "^7.0.0" + } + }, + "node_modules/chartjs-adapter-dayjs-4": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chartjs-adapter-dayjs-4/-/chartjs-adapter-dayjs-4-1.0.4.tgz", + "integrity": "sha512-yy9BAYW4aNzPVrCWZetbILegTRb7HokhgospPoC3b5iZ5qdlqNmXts2KdSp6AqnjkPAp/YWyHDxLvIvwt5x81w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "chart.js": ">=4.0.1", + "dayjs": "^1.9.7" + } + }, + "node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chroma-js": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.4.2.tgz", + "integrity": "sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==", + "license": "(BSD-3-Clause AND Apache-2.0)" + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.10.0.tgz", + "integrity": "sha512-AidJptpBJmjTclAp9BkLwJi0T93fo5epJnbaZslpg6QVzpHjAiveF55mE9AcUJiGMqRHgMDY8soMsQtuNYMHfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/comment-parser": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.0.tgz", + "integrity": "sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/commist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz", + "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==", + "license": "MIT", + "dependencies": { + "leven": "^2.1.0", + "minimist": "^1.1.0" + } + }, + "node_modules/compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "license": "MIT" + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/compress-commons/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/concurrently": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-7.6.0.tgz", + "integrity": "sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "date-fns": "^2.29.1", + "lodash": "^4.17.21", + "rxjs": "^7.0.0", + "shell-quote": "^1.7.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^17.3.1" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-3.1.0.tgz", + "integrity": "sha512-z26wZR7iXahgpSK7XgnB4IbRVmUNSODvsX9qC9TAru7AcWGiqn5YKy+OH3PLRrwyAnewykKhCqnD+VV3a8uT8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/country-flag-emoji-polyfill": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/country-flag-emoji-polyfill/-/country-flag-emoji-polyfill-0.1.10.tgz", + "integrity": "sha512-5O50p2mq/d8+4AfHz3monRQaBA4TIQ0XcKqJUhCYR6SMsLqlO4m8f87og6R1D68Z1sNXGkjLkcEccuzC6T9i5g==", + "license": "MIT" + }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/crc32-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/crc32-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/croner": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/croner/-/croner-8.1.2.tgz", + "integrity": "sha512-ypfPFcAXHuAZRCzo3vJL6ltENzniTjwe/qsLleH1V2/7SRDjgvRQyrLmumFTLmjFax4IuSxfGXEn79fozXcJog==", + "license": "MIT", + "engines": { + "node": ">=18.0" + } + }, + "node_modules/cronstrue": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/cronstrue/-/cronstrue-2.24.0.tgz", + "integrity": "sha512-A1of24mAGz+OWrdGsxT9BOnDqn2ba182hie8Jx0UcEC2t+ZKtfAJxaFntKUgR7sIisU297fgHBSlNhMIfvAkSA==", + "dev": true, + "license": "MIT", + "bin": { + "cronstrue": "bin/cli.js" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, + "node_modules/css-color-converter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-color-converter/-/css-color-converter-2.0.0.tgz", + "integrity": "sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==", + "license": "MIT", + "dependencies": { + "color-convert": "^0.5.2", + "color-name": "^1.1.4", + "css-unit-converter": "^1.1.2" + } + }, + "node_modules/css-color-converter/node_modules/color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha512-RwBeO/B/vZR3dfKL1ye/vx8MHZ40ugzpyfeVG5GsiuGnrlMWe2o8wxBbLCpw9CsxV+wHuzYlCiWnybrIA0ling==" + }, + "node_modules/css-functions-list": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.3.3.tgz", + "integrity": "sha512-8HFEBPKhOpJPEPu70wJJetjKta86Gw9+CCyCnB3sui2qQfOvRyqBy4IKLKKAwdMpWb2lHXWk9Wb4Z6AmaUT1Pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==", + "license": "MIT" + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/dayjs": { + "version": "1.11.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", + "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dev-null": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dev-null/-/dev-null-0.1.1.tgz", + "integrity": "sha512-nMNZG0zfMgmdv8S5O0TM5cpwNbGKRGPCxVsr0SmA3NZZy9CYBbuNLL0PD3Acx9e5LIUgwONXtM9kM6RlawPxEQ==", + "license": "MIT" + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/dns2/-/dns2-2.0.5.tgz", + "integrity": "sha512-dznYrQU+Txcz++klGLBY9YR3WLOGHTy2vAKAqF+yYw1KaKFm5f5Y4jbbFEvohJf8YtZ0J2SzZlZx2k6LV4zJqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/docker-compose": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-1.4.2.tgz", + "integrity": "sha512-rPHigTKGaEHpkUmfd69QgaOp+Os5vGJwG/Ry8lcr8W/382AmI+z/D7qoa9BybKIkqNppaIbs8RYeHSevdQjWww==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/docker-modem": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.7.tgz", + "integrity": "sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^1.15.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/docker-modem/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/docker-modem/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/dockerode": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.12.tgz", + "integrity": "sha512-/bCZd6KlGcjZO8Buqmi/vXuqEGVEZ0PNjx/biBNqJD3MhK9DmdiAuKxqfNhflgDESDIiBz3qF+0e55+CpnrUcw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@grpc/grpc-js": "^1.11.1", + "@grpc/proto-loader": "^0.7.13", + "docker-modem": "^5.0.7", + "protobufjs": "^7.3.2", + "tar-fs": "^2.1.4", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/dockerode/node_modules/@grpc/grpc-js": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.4.tgz", + "integrity": "sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/dockerode/node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dockerode/node_modules/protobufjs": { + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", + "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/dockerode/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.15.tgz", + "integrity": "sha512-EUBjM+B+lkDE41iE82DDSCfkoPGfXx8IxFxPMjNzm/Uk4xDet77rTN9wqlxlVg71kK7XGuUMv6wUxJUwwv+Xyw==", + "dev": true, + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexify": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", + "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.2" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/duplexify/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/emitter-component": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", + "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.10", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.10.tgz", + "integrity": "sha512-9/lX2bdlizlCXMHRMOIm03VBQHQYC7VvydcxtTAUJRxNW1QzM/2PMFSmr6h/lCiMHcyCP6abK+t9Q+j4vekk8Q==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "@types/ws": "^8.5.12", + "accepts": "~1.3.4", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.21.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.6.tgz", + "integrity": "sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.21.0", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", + "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "is-callable": "^1.2.7", + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", + "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint/eslintrc": "^1.2.2", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.4.1.tgz", + "integrity": "sha512-5N7ZaJG5pZxUeNNJfUchurLVrunD1xJvyg5kYOIVF8kg1f3ajTikmAu/5fZ9w100omNPOoMjngRszh/Q/uFGMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc": { + "version": "46.4.6", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.6.tgz", + "integrity": "sha512-z4SWYnJfOqftZI+b3RM9AtWL1vF/sLWE/LlO9yOKDof9yN2+n3zOdOJTGX/pRE/xnPsooOLG2Rq6e4d+XW3lNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.40.1", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.0", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.5.0", + "is-builtin-module": "^3.2.1", + "semver": "^7.5.4", + "spdx-expression-parse": "^3.0.1" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-vue": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.7.1.tgz", + "integrity": "sha512-28sbtm4l4cOzoO1LtzQPxfxhQABararUb1JtqusQqObJpWX2e/gmVyeYVfepizPFne0Q5cILkYGiBoV36L12Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-utils": "^3.0.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.0.1", + "postcss-selector-parser": "^6.0.9", + "semver": "^7.3.5", + "vue-eslint-parser": "^8.0.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-vue-scoped-css": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue-scoped-css/-/eslint-plugin-vue-scoped-css-2.7.2.tgz", + "integrity": "sha512-myJ99CJuwmAx5kq1WjgIeaUkxeU6PIEUh7age79Alm30bhN4fVTapOQLSMlvVTgxr36Y3igsZ3BCJM32LbHHig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "eslint-compat-utils": "^0.4.0", + "lodash": "^4.17.21", + "postcss": "^8.4.31", + "postcss-safe-parser": "^6.0.0", + "postcss-scss": "^4.0.3", + "postcss-selector-parser": "^6.0.9", + "postcss-styl": "^0.12.0" + }, + "engines": { + "node": "^12.22 || ^14.17 || >=16" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=5.0.0", + "vue-eslint-parser": ">=7.1.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/event-to-promise": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/event-to-promise/-/event-to-promise-0.7.0.tgz", + "integrity": "sha512-VOBBfyaADfe378ZzG0tgkzmsvzUyeU5arehrFzNRt5yaASUDshgctTwSrPI17ocAwR3+YftsxRClHF+GBKFByQ==", + "deprecated": "Use promise-toolbox/fromEvent instead", + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-basic-auth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz", + "integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==", + "license": "MIT", + "dependencies": { + "basic-auth": "^2.0.1" + } + }, + "node_modules/express-static-gzip": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/express-static-gzip/-/express-static-gzip-2.1.8.tgz", + "integrity": "sha512-g8tiJuI9Y9Ffy59ehVXvqb0hhP83JwZiLxzanobPaMbkB5qBWA8nuVgd+rcd5qzH3GkgogTALlc0BaADYwnMbQ==", + "license": "MIT", + "dependencies": { + "serve-static": "^1.16.2" + } + }, + "node_modules/express/node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/express/node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, + "node_modules/fast-uri": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", + "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fast-xml-builder": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.1.tgz", + "integrity": "sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, + "node_modules/fast-xml-builder/node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.8.0.tgz", + "integrity": "sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.2.0", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.3.0", + "xml-naming": "^0.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastfall": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/fastfall/-/fastfall-1.5.1.tgz", + "integrity": "sha512-KH6p+Z8AKPXnmA7+Iz2Lh8ARCMr+8WNPVludm1LGkZoD2MjY6LVnRMtTKhkdzI+jr0RzQWXKzKyBJm1zoHEL4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "reusify": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fastparallel": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/fastparallel/-/fastparallel-2.4.1.tgz", + "integrity": "sha512-qUmhxPgNHmvRjZKBFUNI0oZuuH9OlSIOXmJ98lhKPxMZZ7zS/Fi0wRHOihDSz0R1YiIOjxzOY4bq65YTcdBi2Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4", + "xtend": "^4.0.2" + } + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fastseries": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fastseries/-/fastseries-2.0.0.tgz", + "integrity": "sha512-XBU9RXeoYc2/VnvMhplAxEmZLfIk7cvTBu+xwoBuTI8pL19E03cmca17QQycKIdxgwCeFA/a4u27gv1h3ya5LQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/favico.js": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/favico.js/-/favico.js-0.3.10.tgz", + "integrity": "sha512-S5KvqAOczRjlyjQPPZPSlUEybBkfBgKosY/pzTIxkvKgigB+DkITvIEI70dxQarbv4PZ+UD77QzquCAcU/6LHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/feed": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/feed/-/feed-4.2.2.tgz", + "integrity": "sha512-u5/sxGfiMfZNtJ3OvQpXcvotFpYkL0n9u9mM2vkui2nGo8b4wvDkJ8gAkYqbA8QpGyFCv3RK0Z+Iv+9veCS9bQ==", + "license": "MIT", + "dependencies": { + "xml-js": "^1.6.11" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "license": "MIT", + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gamedig": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/gamedig/-/gamedig-5.3.3.tgz", + "integrity": "sha512-35z+ijImna5Ty84efoVXDHBYwH0VmvcKQzwvZarCrcpqWe6nn8X3rbKlLAtmt1HC7EvdPvBE7VjXJmwn/e1K9A==", + "license": "MIT", + "dependencies": { + "fast-xml-parser": "5.8.0", + "gbxremote": "0.2.1", + "got": "13.0.0", + "iconv-lite": "0.7.2", + "long": "5.3.2", + "minimist": "1.2.8", + "seek-bzip": "2.0.0", + "telnet-client": "2.2.13", + "varint": "6.0.0" + }, + "bin": { + "gamedig": "bin/gamedig.js" + }, + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/gamedig/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/gbxremote": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/gbxremote/-/gbxremote-0.2.1.tgz", + "integrity": "sha512-SMehu6Y6ndq2Qgp9VxAb8Np3f+UUD+RWoW2SAMaxzGS96rWXyr4T1GGkecO0HHtxeH1m7pEh4FJWB8a/6aM2XQ==", + "dependencies": { + "any-promise": "^1.1.0", + "barse": "~0.4.2", + "event-to-promise": "^0.7.0", + "string-to-stream": "^1.0.1", + "xmlrpc": "^1.3.1" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-port": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.2.0.tgz", + "integrity": "sha512-afP4W205ONCuMoPBqcR6PSXnzX35KTcJygfJfcp+QY+uwm3p20p1YczWXhlICIzGMCxYBQcySEcOgsJcrkyobg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/getopts": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", + "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==", + "license": "MIT" + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globalping": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/globalping/-/globalping-0.2.0.tgz", + "integrity": "sha512-rxyvqXF/oz5yBGJHR58TEsCkcbc4WQVmdKC+uZ8LXfIt4KwTdTvbzkfwhhjuyUmuQr8U/b8m/ZicdrA8ZIroKw==", + "license": "MIT", + "dependencies": { + "@hey-api/client-fetch": "^0.10.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true, + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz", + "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/help-me": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-3.0.0.tgz", + "integrity": "sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==", + "license": "MIT", + "dependencies": { + "glob": "^7.1.6", + "readable-stream": "^3.6.0" + } + }, + "node_modules/help-me/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/help-me/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http_ece": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http_ece/-/http_ece-1.2.0.tgz", + "integrity": "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/http-cookie-agent": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-5.0.4.tgz", + "integrity": "sha512-OtvikW69RvfyP6Lsequ0fN5R49S+8QcS9zwd58k6VSr6r57T8G29BkPdyrBcSwLq6ExLs9V+rBlfxu7gDstJag==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0" + }, + "engines": { + "node": ">=14.18.0 <15.0.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/3846masa" + }, + "peerDependencies": { + "deasync": "^0.1.26", + "tough-cookie": "^4.0.0", + "undici": "^5.11.0" + }, + "peerDependenciesMeta": { + "deasync": { + "optional": true + }, + "undici": { + "optional": true + } + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-graceful-shutdown": { + "version": "3.1.16", + "resolved": "https://registry.npmjs.org/http-graceful-shutdown/-/http-graceful-shutdown-3.1.16.tgz", + "integrity": "sha512-984h5GmEjnlPR2xW73Z/+r5rguYowKbIYlAN0uqXClo9xjZm+8ZeVN0hMh+0mlw6+H3CThchhZREr0/IHqUO7w==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/hyperid": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hyperid/-/hyperid-3.3.0.tgz", + "integrity": "sha512-7qhCVT4MJIoEsNcbhglhdmBKb09QtcmJNiIQGq7js/Khf5FtQQ9bzcAuloeqBeee7XD7JqDeve9KNlQya5tSGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.2.1", + "uuid": "^8.3.2", + "uuid-parse": "^1.1.0" + } + }, + "node_modules/hyperid/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ip-address": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", + "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.1.0.tgz", + "integrity": "sha512-bUi/yjmtKYcRVUtWRGr0UA6xEFh2I6zWUwMrUXB3s7bmYCaZ8a+0ZsTRkrawh/mzlSD1Y0Ph8bp/U+TvBpWDNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT" + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/joi": { + "version": "17.13.8", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.8.tgz", + "integrity": "sha512-iPKOGmiRw1jxf/JOPwxmCcUQAOdF359mdzYiP2DJ+TMX0YK2zjK3D+zYOaGjpumWxOFF/l2xVWjRVK5bGSLdEw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/jose": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "license": "MIT" + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", + "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-with-bigint": { + "version": "3.5.12", + "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.12.tgz", + "integrity": "sha512-uwbF/wSSuOgC7qqlq27Xp5B6a2MHVug3t0idZdTqu0JnlFvgJuH7ju+KAk/J06C7GfhoYy2gnb9wz2INqcne7w==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonata": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/jsonata/-/jsonata-2.2.2.tgz", + "integrity": "sha512-XDFH2PuaAXv0AXJEWwElXbqADmKFGKMLMkFb+qOz0EhjQW2EIJ6pgtordLU+4u3IVERyBfqy6bi6kZKEncvRqA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonfile/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/just-performance": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/just-performance/-/just-performance-4.3.0.tgz", + "integrity": "sha512-L7RjvtJsL0QO8xFs5wEoDDzzJwoiowRw6Rn/GnvldlchS2JQr9wFYPiwZcDfrbbujEKqKN0tvENdbjXdYhDp5Q==", + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kafkajs": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/kafkajs/-/kafkajs-2.2.4.tgz", + "integrity": "sha512-j/YeapB1vfPT2iOIUn/vxdyKEuhuY2PxMBvf5JWux6iSaukAccrMtXEY/Lb7OvavDhOWME589bpLrEdnVHjfjA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/knex": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.3.0.tgz", + "integrity": "sha512-LgWl031hNuLv9Lhxdd9093zULa2aNcoP04Bk29e5NidgRcEr/T0rAr2WYi4BhjTiCDoQiRU56meUE5rppLKZzQ==", + "license": "MIT", + "dependencies": { + "colorette": "2.0.19", + "commander": "^10.0.0", + "debug": "4.3.4", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "get-package-type": "^0.1.0", + "getopts": "2.3.0", + "interpret": "^2.2.0", + "lodash": "^4.18.1", + "pg-connection-string": "2.6.2", + "rechoir": "^0.8.0", + "resolve-from": "^5.0.0", + "tarn": "^3.1.0", + "tildify": "2.0.0" + }, + "bin": { + "knex": "bin/cli.js" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "pg-query-stream": "^4.14.0" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/knex/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/knex/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, + "node_modules/knex/node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==", + "license": "MIT" + }, + "node_modules/knex/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/known-css-properties": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.29.0.tgz", + "integrity": "sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/kysely": { + "version": "0.28.17", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.17.tgz", + "integrity": "sha512-nbD8lB9EB3wNdMhOCdx5Li8DxnLbvKByylRLcJ1h+4SkrowVeECAyZlyiKMThF7xFdRz0jSQ2MoJr+wXux2y0Q==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/leven": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", + "integrity": "sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/limiter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-2.1.0.tgz", + "integrity": "sha512-361TYz6iay6n+9KvUUImqdLuFigK+K79qrUtBsXhJTLdH4rIt/r1y8r1iozwh8KbZNpujbFTSh74mJ7bwbAMOw==", + "license": "MIT", + "dependencies": { + "just-performance": "4.3.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/liquidjs": { + "version": "10.29.0", + "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.29.0.tgz", + "integrity": "sha512-pCVOhs6FLAR8su3ItJ07diN26t6W5dHQRnmTMy8HPyTFuv1+oSCVJIGp5pGjfQyOZfh50KswvKtMTp6p4JEIdw==", + "license": "MIT", + "dependencies": { + "commander": "^10.0.0" + }, + "bin": { + "liquid": "bin/liquid.js", + "liquidjs": "bin/liquid.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/liquidjs" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.sortedlastindex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.sortedlastindex/-/lodash.sortedlastindex-4.1.0.tgz", + "integrity": "sha512-s8xEQdsp2Tu5zUqVdFSe9C0kR8YlnAJYLqMdkh+pIRBRxF6/apWseLdHl3/+jv2I61dhPwtI/Ff+EqvCpc+N8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru.min": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.5.tgz", + "integrity": "sha512-5J9ysMYUpYIg9RF2vJpy9SinEmSviFSe0GyPpCQ4L5QSkLAgeLXlTAOu2ZwWUU5m+0SBl6gUU1R1ZQB3aKypfA==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/marked": { + "version": "14.1.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.4.tgz", + "integrity": "sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT", + "optional": true + }, + "node_modules/meow": { + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", + "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.2", + "camelcase-keys": "^7.0.0", + "decamelize": "^5.0.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.2", + "read-pkg-up": "^8.0.0", + "redent": "^4.0.0", + "trim-newlines": "^4.0.2", + "type-fest": "^1.2.2", + "yargs-parser": "^20.2.9" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", + "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/mongodb": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.2.tgz", + "integrity": "sha512-mLV7SEiov2LHleRJPMPrK2PMyhXFZt2UQLC4VD4pnth3jMjYKHhtqfwwkkvS/NXuo/Fp3vbhaNcXrIDaLRb9Tg==", + "license": "Apache-2.0", + "dependencies": { + "bson": "^4.7.2", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" + }, + "engines": { + "node": ">=12.9.0" + }, + "optionalDependencies": { + "@aws-sdk/credential-providers": "^3.186.0", + "@mongodb-js/saslprep": "^1.1.0" + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mongodb-connection-string-url/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mqemitter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/mqemitter/-/mqemitter-7.1.0.tgz", + "integrity": "sha512-GnBDNz3lxmllW201ne0mrmdy5tPOTnc79jjVcsfUa2LG2pUGeyGWVeiae6ZysfC/64XrYOqCKRAQYrB7pGyBVQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "fastparallel": "^2.4.1", + "qlobber": "^8.0.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/mqtt": { + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.3.8.tgz", + "integrity": "sha512-2xT75uYa0kiPEF/PE0VPdavmEkoBzMT/UL9moid0rAvlCtV48qBwxD62m7Ld/4j8tSkIO1E/iqRl/S72SEOhOw==", + "license": "MIT", + "dependencies": { + "commist": "^1.0.0", + "concat-stream": "^2.0.0", + "debug": "^4.1.1", + "duplexify": "^4.1.1", + "help-me": "^3.0.0", + "inherits": "^2.0.3", + "lru-cache": "^6.0.0", + "minimist": "^1.2.5", + "mqtt-packet": "^6.8.0", + "number-allocator": "^1.0.9", + "pump": "^3.0.0", + "readable-stream": "^3.6.0", + "reinterval": "^1.1.0", + "rfdc": "^1.3.0", + "split2": "^3.1.0", + "ws": "^7.5.5", + "xtend": "^4.0.2" + }, + "bin": { + "mqtt": "bin/mqtt.js", + "mqtt_pub": "bin/pub.js", + "mqtt_sub": "bin/sub.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/mqtt/node_modules/mqtt-packet": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.10.0.tgz", + "integrity": "sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mqtt/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/mqtt/node_modules/ws": { + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mssql": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-12.0.0.tgz", + "integrity": "sha512-FcDQ1Gwe4g3Mhw25R1Onr8N+jmqBTWE/pmtcgxYnAUSIf/vBQMvJfMnyMY8ruOICtBch5+Wgbcfd3REDQSlWpA==", + "license": "MIT", + "dependencies": { + "@tediousjs/connection-string": "^0.6.0", + "commander": "^11.0.0", + "debug": "^4.3.3", + "tarn": "^3.0.2", + "tedious": "^19.0.0" + }, + "bin": { + "mssql": "bin/mssql" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/mssql/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/mute-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", + "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/mysql2": { + "version": "3.24.4", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.24.4.tgz", + "integrity": "sha512-A2olluVlj0mvgyIRRISMEzXc51m+21mRtcMVjJyIpt2GG98+XrC9m9HzsqcMsX2LcnfccJvY5NB22g8fENBnOA==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.2", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.3", + "long": "^5.3.2", + "lru.min": "^1.1.4", + "named-placeholders": "^1.1.6", + "sql-escaper": "^1.5.1" + }, + "engines": { + "node": ">= 8.0" + }, + "peerDependencies": { + "@types/node": ">= 8" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz", + "integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==", + "license": "MIT", + "dependencies": { + "lru.min": "^1.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/nan": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz", + "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==", + "license": "MIT", + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanostores": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/nanostores/-/nanostores-1.5.3.tgz", + "integrity": "sha512-rQLB6eV4f2AW/n3L0JmwCROpaisYy9EDEADvEFSd1C/qG8hB6O5TPlh9A791JRbJr4CnMQBzptDcvD9OR1+6WA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/native-duplexpair": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz", + "integrity": "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/net": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz", + "integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ==", + "license": "MIT" + }, + "node_modules/net-snmp": { + "version": "3.26.3", + "resolved": "https://registry.npmjs.org/net-snmp/-/net-snmp-3.26.3.tgz", + "integrity": "sha512-DTnzoz8Bk5Vz+7WWflo2VgSXMguadFMvsPmSOrknv+YTdA65lLUL1WZhYj6ZA/7pJvKscLJPOeU4LoEd6A1EZg==", + "license": "MIT", + "dependencies": { + "asn1-ber": "^1.2.1", + "smart-buffer": "^4.1.0" + } + }, + "node_modules/node-abi": { + "version": "3.96.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.96.0.tgz", + "integrity": "sha512-rebQ/lz7i0EkoLzUVSrKRzA69zMkwLp95kKMWoMDkkM00Suxz0D7zEQPwRml5fQum24mj7bPvmlgLAmu2JCiYg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-cloudflared-tunnel": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/node-cloudflared-tunnel/-/node-cloudflared-tunnel-1.0.10.tgz", + "integrity": "sha512-QBTH4bcTePgiwYdK5vYZhZl7GNr3Pqnj5V3Tg1w5HvnK4VhLZe5BUuhqKvzzCA604yhoD2I1ATdnOl6axSJu/w==", + "license": "MIT", + "dependencies": { + "command-exists": "^1.2.9" + } + }, + "node_modules/node-radius-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/node-radius-utils/-/node-radius-utils-1.2.0.tgz", + "integrity": "sha512-i3Sf6khnenl0aXumo0whAlfPWTaBqHxEnVBBxpu3dZ7q69NkPPv71rvPjlDZ5wkeKCTNNUTECljerS5kcYQxRw==", + "license": "MIT" + }, + "node_modules/node-ssh": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/node-ssh/-/node-ssh-13.1.0.tgz", + "integrity": "sha512-GLcw49yFd9+rUpP+FgX6wrF/N90cmuDl2n0i8d3L828b6riRjkb9w3SS+XvviRWbrAhLxuMKywFqxvQDZQ1bug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ssh2": "^1.11.9", + "is-stream": "^2.0.0", + "make-dir": "^3.1.0", + "sb-promise-queue": "^2.1.0", + "sb-scandir": "^3.1.0", + "shell-escape": "^0.2.0", + "ssh2": "^1.11.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/nodemailer": { + "version": "10.0.9", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-10.0.9.tgz", + "integrity": "sha512-BF0qcyplCwp+jMk6HCjFykBz/YhhZSsxrARhOldLwFWH+8kGjQsd2WIMIZhqEuyXRoGFi0ONbDeWDMDoL8MhLw==", + "license": "MIT-0", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nostr-tools": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nostr-tools/-/nostr-tools-2.20.0.tgz", + "integrity": "sha512-Kq/2lMyeOdGvpDsYH2an8HP4H0aFCqwKythhTzxfgZTVv4L3NOgrJw2SxH8jkWlH8xPhWxGfN6lFtC+EAa2qYQ==", + "license": "Unlicense", + "dependencies": { + "@noble/ciphers": "^0.5.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.1", + "@scure/base": "1.1.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1", + "nostr-wasm": "0.1.0" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/nostr-tools/node_modules/@noble/ciphers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz", + "integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/nostr-tools/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/nostr-wasm": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz", + "integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==", + "license": "MIT" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/oidc-token-hash": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.2.0.tgz", + "integrity": "sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.3.tgz", + "integrity": "sha512-b3souUgU0VaAvMmzA4+9cRgrOgMdE0vc9H9VhQ0FYnjjcyZ8R/f9DiwMU9V8rl+vjf05On9UJhbgb06fsTs87Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.5.1", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.2.1", + "wsl-utils": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openid-client": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", + "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "license": "MIT", + "dependencies": { + "jose": "^4.15.9", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openid-client/node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/oracledb": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/oracledb/-/oracledb-6.10.0.tgz", + "integrity": "sha512-kGUumXmrEWbSpBuKJyb9Ip3rXcNgKK6grunI3/cLPzrRvboZ6ZoLi9JQ+z6M/RIG924tY8BLflihL4CKKQAYMA==", + "hasInstallScript": true, + "license": "(Apache-2.0 OR UPL-1.0)", + "engines": { + "node": ">=14.17" + } + }, + "node_modules/own-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", + "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/password-hash": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/password-hash/-/password-hash-1.2.2.tgz", + "integrity": "sha512-Dy/5+Srojwv+1XnMrK2bn7f2jN3k2p90DfBVA0Zd6PrjWF7lXHOTWgKT4uBp1gIsqV7/llYqm+hj+gwDBF/Fmg==", + "bin": { + "nodepw": "bin/nodepw" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pg": { + "version": "8.11.6", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.6.tgz", + "integrity": "sha512-6CyL4F0j3vPmakU9rWdeRY8qF5Cjc3OE86y6YpgDI6YtKHhNyCjGEIE8U5ZRfBjKTZikwolKIFWh3I22MeRnoA==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.0.tgz", + "integrity": "sha512-sILXutLVjCLjcDuOmvhX5e2Z4cS5qG/6Bu3VkpFwdf/633ElGLpEh9bgmuI5I4sqKqkifQiGyiCcx1HdtrK7tg==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.6.tgz", + "integrity": "sha512-lqIfH7bdgsxHAY/ZnUOwm+aCFKrsHBDhSFuk9O0B9uCqJAIkrKTo/+LQqLPLUS4e04+jCmQVikxE3QipH5chPw==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkginfo": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", + "integrity": "sha512-8xCNE/aT/EXKenuMDZ+xTVwkT8gsoHN2z/Q29l80u0ppGEXVvsKRzNMbtKhg8LS8k1tJLAHHylf6p4VFmP6XUQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.28", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", + "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.18", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-html": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.8.1.tgz", + "integrity": "sha512-OLF6P7qctfAWayOhLpcVnTGqVeJzu2W3WpIYelfz2+JV5oGxfkcEvweN9U4XpeqE0P98dcD9ssusGwlF0TK0uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "htmlparser2": "^8.0.0", + "js-tokens": "^9.0.0", + "postcss": "^8.5.0", + "postcss-safe-parser": "^6.0.0" + }, + "engines": { + "node": "^12 || >=14" + } + }, + "node_modules/postcss-html/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/postcss-html/node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss-rtlcss": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-5.7.1.tgz", + "integrity": "sha512-zE68CuARv5StOG/UQLa0W1Y/raUTzgJlfjtas43yh3/G1BFmoPEaHxPRHgeowXRFFhW33FehrNgsljxRLmPVWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "rtlcss": "4.3.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.29" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-styl": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/postcss-styl/-/postcss-styl-0.12.3.tgz", + "integrity": "sha512-8I7Cd8sxiEITIp32xBK4K/Aj1ukX6vuWnx8oY/oAH35NfQI4OZaY5nd68Yx8HeN5S49uhQ6DL0rNk0ZBu/TaLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fast-diff": "^1.2.0", + "lodash.sortedlastindex": "^4.1.0", + "postcss": "^7.0.27 || ^8.0.0", + "stylus": "^0.57.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || ^11.10.1 || >=12.13.0" + }, + "funding": { + "url": "https://opencollective.com/stylus" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/powershell-utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz", + "integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/prom-client": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-13.2.0.tgz", + "integrity": "sha512-wGr5mlNNdRNzEhRYXgboUU2LxHWIojxscJKmtG3R8f4/KiWqyYgXTLHs0+Ted7tG3zFT7pgHJbtomzZ1L0ARaQ==", + "deprecated": "prom-client has been replaced by @prometheus-io/client", + "license": "Apache-2.0", + "dependencies": { + "tdigest": "^0.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prometheus-api-metrics": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/prometheus-api-metrics/-/prometheus-api-metrics-3.2.2.tgz", + "integrity": "sha512-5hT17HAjflPkrHSYQ7lorsKygo0PhLau/FQ6SQhw0XWAm10GwKfLQmIVP6b3LJBnc4WNOf/QKHce2RfcZMLjJQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/express": "^4.17.13", + "@types/express-serve-static-core": "^4.17.28", + "@types/koa": "^2.13.4", + "debug": "^3.2.6", + "lodash.get": "^4.4.2", + "pkginfo": "^0.4.1" + }, + "peerDependencies": { + "prom-client": ">=12 <15" + } + }, + "node_modules/prometheus-api-metrics/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/promisify-child-process": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/promisify-child-process/-/promisify-child-process-4.1.2.tgz", + "integrity": "sha512-APnkIgmaHNJpkAn7k+CrJSi9WMuff5ctYFbD0CO2XIPkM8yO7d/ShouU2clywbpHV/DUsyc4bpJCsNgddNtx4g==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/properties-reader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-3.0.1.tgz", + "integrity": "sha512-WPn+h9RGEExOKdu4bsF4HksG/uzd3cFq3MFtq8PsFeExPse5Ha/VOjQNyHhjboBFwGXGev6muJYTSPAOkROq2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "mkdirp": "^3.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/properties?sponsor=1" + } + }, + "node_modules/properties-reader/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/protobufjs": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-8.8.0.tgz", + "integrity": "sha512-N3xhQ5yyBx3vQq4gubBfASzYhJGNzeDbjqBpu61g7UVylsN/qyffU96TKWD3GbbLOKF82VGNRNvv1+BFgE31Eg==", + "license": "BSD-3-Clause", + "dependencies": { + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qlobber": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/qlobber/-/qlobber-8.0.1.tgz", + "integrity": "sha512-O+Wd1chXj5YE1DwmD+ae0bXiSLehmnS3czlC1R9FL/Nt/3q8uMS1bIHmg2lJfCoiimCxClWM8AAuJrF0EvNiog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/qrcode/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qrcode/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/radius": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/radius/-/radius-1.1.4.tgz", + "integrity": "sha512-UWuzdF6xf3NpsXFZZmUEkxtEalDXj8hdmMXgbGzn7vOk6zXNsiIY2I6SJ1euHt7PTQuMoz2qDEJB+AfJDJgQYw==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz", + "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^3.0.2", + "parse-json": "^5.2.0", + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz", + "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0", + "read-pkg": "^6.0.0", + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/redbean-node": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/redbean-node/-/redbean-node-0.4.0.tgz", + "integrity": "sha512-kxjUYm5N+BluwhLjSsLbDG/MRjhNj94AJAeUtoHgYb2y4V9CHe3fsSepNSApd5w9W+XY1h+INQ2J64eYf5lhMw==", + "license": "MIT", + "dependencies": { + "@types/node": "~26.6.1", + "await-lock": "~2.2.2", + "dayjs": "~1.11.23", + "glob": "~13.0.6", + "knex": "~3.3.0" + } + }, + "node_modules/redbean-node/node_modules/@types/node": { + "version": "26.6.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.1.tgz", + "integrity": "sha512-VqGJBMCtdhqkBUCcBLvywI0NJ+KLuVzgNnlBUNFOQjqVxzo2lxLUNg1DSey8+u2u6ktswSAxg+s68QLzWHNOuA==", + "license": "MIT", + "dependencies": { + "undici-types": "~8.9.0" + } + }, + "node_modules/redbean-node/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/redbean-node/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/redbean-node/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/redbean-node/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/redbean-node/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/redbean-node/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/redbean-node/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/redent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", + "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^5.0.0", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/redis": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-5.9.0.tgz", + "integrity": "sha512-E8dQVLSyH6UE/C9darFuwq4usOPrqfZ1864kI4RFbr5Oj9ioB9qPF0oJMwX7s8mf6sPYrz84x/Dx1PGF3/0EaQ==", + "license": "MIT", + "dependencies": { + "@redis/bloom": "5.9.0", + "@redis/client": "5.9.0", + "@redis/json": "5.9.0", + "@redis/search": "5.9.0", + "@redis/time-series": "5.9.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, + "license": "ISC" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retimer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retimer/-/retimer-4.0.0.tgz", + "integrity": "sha512-fZIVtvbOsQsxNSDhpdPOX4lx5Ss2ni+S72AUBitARpFhtA3UzrAjQ6gDtypB2/+l7L+1VQgAgpvAKY66mElH0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "worker-timers": "^7.0.75" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/rollup-plugin-visualizer": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-7.0.1.tgz", + "integrity": "sha512-UJUT4+1Ho4OcWmPYU3sYXgUqI8B8Ayfe06MX7y0qCJ1K8aGoKtR/NDd/2nZqM7ADkrzny+I99Ul7GgyoiVNAgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "open": "^11.0.0", + "picomatch": "^4.0.2", + "source-map": "^0.7.4", + "yargs": "^18.0.0" + }, + "bin": { + "rollup-plugin-visualizer": "dist/bin/cli.js" + }, + "engines": { + "node": ">=22" + }, + "peerDependencies": { + "rolldown": "1.x || ^1.0.0-beta || ^1.0.0-rc", + "rollup": "2.x || 3.x || 4.x" + }, + "peerDependenciesMeta": { + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + } + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup-plugin-visualizer/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/string-width": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", + "integrity": "sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/yargs": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", + "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/rollup-plugin-visualizer/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/rou3": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/rou3/-/rou3-0.9.2.tgz", + "integrity": "sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ==", + "license": "MIT" + }, + "node_modules/rtlcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", + "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", + "dev": true, + "license": "MIT", + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0", + "postcss": "^8.4.21", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "rtlcss": "bin/rtlcss.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.104.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.104.1.tgz", + "integrity": "sha512-yDA+1aIG3EHgN4V/BvuhCvu61FF6hEd4e+9DxikUm9U0CAGuvdIZ/UYy7qbOxjhbbWQraoyLlMuzdRGOSV5Bmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "license": "ISC" + }, + "node_modules/sb-promise-queue": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/sb-promise-queue/-/sb-promise-queue-2.1.1.tgz", + "integrity": "sha512-qXfdcJQMxMljxmPprn4Q4hl3pJmoljSCzUvvEBa9Kscewnv56n0KqrO6yWSrGLOL9E021wcGdPa39CHGKA6G0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/sb-scandir": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/sb-scandir/-/sb-scandir-3.1.1.tgz", + "integrity": "sha512-Q5xiQMtoragW9z8YsVYTAZcew+cRzdVBefPbb9theaIKw6cBo34WonP9qOCTKgyAmn/Ch5gmtAxT/krUgMILpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sb-promise-queue": "^2.1.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/seek-bzip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-2.0.0.tgz", + "integrity": "sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg==", + "license": "MIT", + "dependencies": { + "commander": "^6.0.0" + }, + "bin": { + "seek-bunzip": "bin/seek-bunzip", + "seek-table": "bin/seek-bzip-table" + } + }, + "node_modules/seek-bzip/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/set-cookie-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.2.tgz", + "integrity": "sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==", + "license": "MIT" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-escape": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/shell-escape/-/shell-escape-0.2.0.tgz", + "integrity": "sha512-uRRBT2MfEOyxuECseCZd28jC1AJ8hmqqneWQ4VWUTgCAFvb3wKU1jLqj6egC4Exrr88ogg3dp+zroH4wJuaXzw==", + "dev": true, + "license": "MIT" + }, + "node_modules/shell-quote": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz", + "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", + "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.4.1", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz", + "integrity": "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==", + "license": "MIT", + "dependencies": { + "debug": "~4.4.1", + "ws": "~8.21.0" + } + }, + "node_modules/socket.io-client": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz", + "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.7.tgz", + "integrity": "sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.10.tgz", + "integrity": "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/sortablejs": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz", + "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "license": "MIT", + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/split2/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/split2/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, + "node_modules/sql-escaper": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.5.1.tgz", + "integrity": "sha512-4toX5E1fQbBrpfXidaHnF0669nkAdETeIPTs2SUjxxD7RRIs9ICG4gtpmfc68JCEKehsdwLFqBu9VlQqZ1P1gg==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=2.0.0", + "node": ">=12.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/mysqljs/sql-escaper?sponsor=1" + } + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ssh-remote-port-forward": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz", + "integrity": "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ssh2": "^0.5.48", + "ssh2": "^1.4.0" + } + }, + "node_modules/ssh-remote-port-forward/node_modules/@types/ssh2": { + "version": "0.5.52", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.52.tgz", + "integrity": "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ssh2-streams": "*" + } + }, + "node_modules/ssh2": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", + "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.10", + "nan": "^2.23.0" + } + }, + "node_modules/ssh2-sftp-client": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/ssh2-sftp-client/-/ssh2-sftp-client-12.0.1.tgz", + "integrity": "sha512-ICJ1L2PmBel2Q2ctbyxzTFZCPKSHYYD6s2TFZv7NXmZDrDNGk8lHBb/SK2WgXLMXNANH78qoumeJzxlWZqSqWg==", + "license": "Apache-2.0", + "dependencies": { + "concat-stream": "^2.0.0", + "ssh2": "^1.16.0" + }, + "engines": { + "node": ">=18.20.4" + }, + "funding": { + "type": "individual", + "url": "https://square.link/u/4g7sPflL" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stream": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz", + "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==", + "license": "MIT", + "dependencies": { + "emitter-component": "^1.1.1" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" + }, + "node_modules/streamx": { + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", + "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/string-to-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string-to-stream/-/string-to-stream-1.1.1.tgz", + "integrity": "sha512-QySF2+3Rwq0SdO3s7BAp4x+c3qsClpPQ6abAmb0DGViiSBAkT5kL6JT2iyzEVP+T1SmzHrQD1TwlP9QAHCc+Sw==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.1.0" + } + }, + "node_modules/string-to-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/string-to-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/string-to-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/string-to-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.replaceall": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.replaceall/-/string.prototype.replaceall-1.0.11.tgz", + "integrity": "sha512-MtmYTo9i6i3Jpc0xuGVYd5GraPTml7vlZh4030YXRiBktXwYKYU7IDGJeMi008Dk8QKlgJUi/Q+oNnGKB++/fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-indent": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.1.1.tgz", + "integrity": "sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.2.tgz", + "integrity": "sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } + }, + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", + "dev": true, + "license": "ISC" + }, + "node_modules/stylelint": { + "version": "15.11.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.11.0.tgz", + "integrity": "sha512-78O4c6IswZ9TzpcIiQJIN49K3qNoXTM8zEJzhaTE/xRTCZswaovSEVIa/uwbOltZrk16X4jAxjaOhzz/hTm1Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-parser-algorithms": "^2.3.1", + "@csstools/css-tokenizer": "^2.2.0", + "@csstools/media-query-list-parser": "^2.1.4", + "@csstools/selector-specificity": "^3.0.0", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^8.2.0", + "css-functions-list": "^3.2.1", + "css-tree": "^2.3.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.1", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^7.0.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^5.2.4", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.29.0", + "mathml-tag-names": "^2.1.3", + "meow": "^10.1.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.28", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.13", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "style-search": "^0.1.0", + "supports-hyperlinks": "^3.0.0", + "svg-tags": "^1.0.0", + "table": "^6.8.1", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + } + }, + "node_modules/stylelint-config-prettier": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.5.tgz", + "integrity": "sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==", + "dev": true, + "license": "MIT", + "bin": { + "stylelint-config-prettier": "bin/check.js", + "stylelint-config-prettier-check": "bin/check.js" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "stylelint": ">= 11.x < 15" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", + "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint-config-standard": { + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", + "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "stylelint-config-recommended": "^7.0.0" + }, + "peerDependencies": { + "stylelint": "^14.4.0" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-7.0.2.tgz", + "integrity": "sha512-TfW7/1iI4Cy7Y8L6iqNdZQVvdXn0f8B4QcIXmkIbtTIe/Okm/nSlHb4IwGzRVOd3WfSieCgvf5cMzEfySAIl0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/stylelint/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stylus": { + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.57.0.tgz", + "integrity": "sha512-yOI6G8WYfr0q8v8rRvE91wbxFU+rJPo760Va4MF6K0I6BZjO4r+xSynkvyPBP9tV1CIEUeRsiidjIs2rzb1CnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css": "^3.0.0", + "debug": "^4.3.2", + "glob": "^7.1.6", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz", + "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tar-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/tarn": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.1.2.tgz", + "integrity": "sha512-3RTvqKZcK/17jnJ8rMKFXbyNogywTs1z0gVPPwFsJGX46rkmUHOdIaSQ/aVO1rS7nH+soiXiWk7rvUXxndm8Dg==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/tcp-ping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/tcp-ping/-/tcp-ping-0.1.1.tgz", + "integrity": "sha512-7Ed10Ds0hYnF+O1lfiZ2iSZ1bCAj+96Madctebmq7Y1ALPWlBY4YI8C6pCL+UTlshFY5YogixKLpgDP/4BlHrw==", + "license": "MIT" + }, + "node_modules/tdigest": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.3.tgz", + "integrity": "sha512-zbRt+lT+/H4fRItHshczHErVCQnitJk8MfMT24MqFJf3YL7SJJPqGIGeuOdvxXxM/AHFzKBl7WoyaYwqO9s3Kw==", + "license": "MIT", + "dependencies": { + "bintrees": "1.0.2" + } + }, + "node_modules/tedious": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/tedious/-/tedious-19.2.2.tgz", + "integrity": "sha512-ITsLV/XXXAuJ/+pgnod4lcJH83WPK7+y2aqOwK/ERoC4zX73wfRIVc7BofuHCqDTqpd6j1RwTBtjKNuuOLEvfw==", + "license": "MIT", + "dependencies": { + "@azure/core-auth": "^1.7.2", + "@azure/identity": "^4.2.1", + "@azure/keyvault-keys": "^4.4.0", + "@js-joda/core": "^6.0.1", + "@types/node": ">=18", + "bl": "^6.1.4", + "iconv-lite": "^0.7.0", + "js-md4": "^0.3.2", + "native-duplexpair": "^1.0.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">=18.17" + } + }, + "node_modules/tedious/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/telnet-client": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/telnet-client/-/telnet-client-2.2.13.tgz", + "integrity": "sha512-pXAGggyeE7/STEnXhN8oe0V1hRzT6oNiGvs4CU4ilppledV40dok+70yl/Xb2newAXXdM9C5rW8otOKmRvkZxA==", + "license": "MIT", + "dependencies": { + "net": "^1.0.2", + "stream": "^0.0.2" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/kozjak" + } + }, + "node_modules/terser": { + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz", + "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/test": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/test/-/test-3.3.0.tgz", + "integrity": "sha512-JKlEohxDIJRjwBH/+BrTcAPHljBALrAHw3Zs99RqZlaC605f6BggqXhxkdqZThbSHgaYPwpNJlf9bTSWkb/1rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6", + "readable-stream": "^4.3.0", + "string.prototype.replaceall": "^1.0.6" + }, + "bin": { + "node--test": "bin/node--test.js", + "node--test-name-pattern": "bin/node--test-name-pattern.js", + "node--test-only": "bin/node--test-only.js", + "test": "bin/node-core-test.js" + } + }, + "node_modules/test/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/test/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/test/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/testcontainers": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-11.14.0.tgz", + "integrity": "sha512-r9pniwv/iwzyHaI7gwAvAm4Y+IvjJg3vBWdjrUCaDMc2AXIr4jKbq7jJO18Mw2ybs73pZy1Aj7p/4RVBGMRWjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@types/dockerode": "^4.0.1", + "archiver": "^7.0.1", + "async-lock": "^1.4.1", + "byline": "^5.0.0", + "debug": "^4.4.3", + "docker-compose": "^1.4.2", + "dockerode": "^4.0.10", + "get-port": "^7.2.0", + "proper-lockfile": "^4.1.2", + "properties-reader": "^3.0.1", + "ssh-remote-port-forward": "^1.0.4", + "tar-fs": "^3.1.2", + "tmp": "^0.2.5", + "undici": "^7.24.5" + } + }, + "node_modules/testcontainers/node_modules/tar-fs": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.3.tgz", + "integrity": "sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/testcontainers/node_modules/tar-stream": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.1.tgz", + "integrity": "sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/testcontainers/node_modules/undici": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.1.tgz", + "integrity": "sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tldts": { + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.32.tgz", + "integrity": "sha512-5eDV0tK2NhLAAqBeXDAQ36+EwuStd1HbsSOnGsp+JbExITnExcALLL5M1kTH8gjDYN5QvwmUWimE3GoMZ2A7xQ==", + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.32" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.12", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.12.tgz", + "integrity": "sha512-nYNzS2WRf4QJmjzFFgAxLOBjyBxAGRbCy9PVBPaglcYyYajh40VBn+v5Ngr96ZMc7oM0+aCJdtQnNejvdBnXMQ==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/trim-newlines": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", + "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.23.13", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.13.tgz", + "integrity": "sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==", + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "license": "MIT" + }, + "node_modules/undici": { + "version": "6.28.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.1.tgz", + "integrity": "sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "license": "MIT" + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unlimited-timeout": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unlimited-timeout/-/unlimited-timeout-0.1.0.tgz", + "integrity": "sha512-D4g+mxFeQGQHzCfnvij+R35ukJ0658Zzudw7j16p4tBBbNasKkKM4SocYxqhwT5xA7a9JYWDzKkEFyMlRi5sng==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/uuid-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/uuid-parse/-/uuid-parse-1.1.0.tgz", + "integrity": "sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A==", + "dev": true, + "license": "MIT" + }, + "node_modules/v-pagination-3": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/v-pagination-3/-/v-pagination-3-0.1.7.tgz", + "integrity": "sha512-b5H+SdL+yIhkqyWI+Uj5lGk1VK3Q/hjqN44okerMa9smtk55DJX3Jg+ecU/vJAFrEhNCqgNzLsJ8pLRcHrbjrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-add-module-exports": "^0.2.1", + "merge": "^2.1.1", + "vue": ">=3.0.0" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validator": { + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/varint": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", + "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-compression": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz", + "integrity": "sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "debug": "^4.3.3", + "fs-extra": "^10.0.0" + }, + "peerDependencies": { + "vite": ">=2.0.0" + } + }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vue": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.42.tgz", + "integrity": "sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.42", + "@vue/compiler-sfc": "3.5.42", + "@vue/runtime-dom": "3.5.42", + "@vue/server-renderer": "3.5.42", + "@vue/shared": "3.5.42" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-chartjs": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.2.0.tgz", + "integrity": "sha512-d3zpKmGZr2OWHQ1xmxBcAn5ShTG917+/UCLaSpaCDDqT0U7DBsvFzTs69ZnHCgKoXT55GZDW8YEj9Av+dlONLA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "chart.js": "^4.1.1", + "vue": "^3.0.0-0 || ^2.7.0" + } + }, + "node_modules/vue-contenteditable": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/vue-contenteditable/-/vue-contenteditable-3.0.4.tgz", + "integrity": "sha512-CmtqT4zHQwLoJEyNVaXUjjUFPUVYlXXBHfSbRCHCUjODMqrn6L293LM1nc1ELx8epitZZvecTfIqOLlSzB3d+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-demi": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz", + "integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-eslint-parser": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", + "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.2", + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-i18n": { + "version": "11.2.8", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.2.8.tgz", + "integrity": "sha512-vJ123v/PXCZntd6Qj5Jumy7UBmIuE92VrtdX+AXr+1WzdBHojiBxnAxdfctUFL+/JIN+VQH4BhsfTtiGsvVObg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@intlify/core-base": "11.2.8", + "@intlify/shared": "11.2.8", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-image-crop-upload": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vue-image-crop-upload/-/vue-image-crop-upload-3.0.3.tgz", + "integrity": "sha512-VeBsU0oI1hXeCvdpnu19DM/r3KTlI8SUXTxsHsU4MhDXR0ahRziiL9tf4FbILGx+gRVNZhGbl32yuM6TiaGNhA==", + "dev": true, + "license": "ISC", + "dependencies": { + "babel-runtime": "^6.11.6" + } + }, + "node_modules/vue-multiselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vue-multiselect/-/vue-multiselect-3.0.0.tgz", + "integrity": "sha512-uupKdINgz7j83lQToCL7KkgQQxvG43el++hsR39YT9pCe1DwzUGmKzPxjVP6rqskXed5P6DtUASYAlCliW740Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.1", + "npm": ">= 6.14.15" + } + }, + "node_modules/vue-prism-editor": { + "version": "2.0.0-alpha.2", + "resolved": "https://registry.npmjs.org/vue-prism-editor/-/vue-prism-editor-2.0.0-alpha.2.tgz", + "integrity": "sha512-Gu42ba9nosrE+gJpnAEuEkDMqG9zSUysIR8SdXUw8MQKDjBnnNR9lHC18uOr/ICz7yrA/5c7jHJr9lpElODC7w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-qrcode": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vue-qrcode/-/vue-qrcode-1.0.1.tgz", + "integrity": "sha512-LHEsHA8mVR+mL8REKeLrvP0h0lelwzkJjFe3ToygKjQS9Mo85m9I7/axdCnRl9ZiZIFjTWkAW1dCK+f8rq0wIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.2.0", + "vue-demi": "^0.12.5" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0", + "qrcode": "^1.0.0", + "vue": "^2.0.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz", + "integrity": "sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue-toastification": { + "version": "2.0.0-rc.5", + "resolved": "https://registry.npmjs.org/vue-toastification/-/vue-toastification-2.0.0-rc.5.tgz", + "integrity": "sha512-q73e5jy6gucEO/U+P48hqX+/qyXDozAGmaGgLFm5tXX4wJBcVsnGp4e/iJqlm9xzHETYOilUuwOUje2Qg1JdwA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "vue": "^3.0.2" + } + }, + "node_modules/vuedraggable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz", + "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==", + "dev": true, + "license": "MIT", + "dependencies": { + "sortablejs": "1.14.0" + }, + "peerDependencies": { + "vue": "^3.0.1" + } + }, + "node_modules/wait-on": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", + "integrity": "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^1.6.1", + "joi": "^17.11.0", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "rxjs": "^7.8.1" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/wait-on/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/wait-on/node_modules/axios": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", + "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/wait-on/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/wait-on/node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/web-push": { + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/web-push/-/web-push-3.6.7.tgz", + "integrity": "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==", + "license": "MPL-2.0", + "dependencies": { + "asn1.js": "^5.3.0", + "http_ece": "1.2.0", + "https-proxy-agent": "^7.0.0", + "jws": "^4.0.0", + "minimist": "^1.2.5" + }, + "bin": { + "web-push": "src/cli.js" + }, + "engines": { + "node": ">= 16" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", + "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/which-typed-array": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.0.tgz", + "integrity": "sha512-Hl0ZOAs672vg+06kfujwRhoS6/jehvULrlFkuF2dRu6pHgA8U06h3xqNIqNNU1LTXPcedxByAR4GS6pwQK0mgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, + "node_modules/xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz", + "integrity": "sha512-eKRAFz04jghooy8muekqzo8uCSVNeyRedbuJrp0fovbLIi7wlsYtdUn3vBAAPq2Y3/0xMz2WMEUQ8yhVVO9Stw==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/xmlrpc": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/xmlrpc/-/xmlrpc-1.3.2.tgz", + "integrity": "sha512-jQf5gbrP6wvzN71fgkcPPkF4bF/Wyovd7Xdff8d6/ihxYmgETQYSuTc+Hl+tsh/jmgPLro/Aro48LMFlIyEKKQ==", + "license": "MIT", + "dependencies": { + "sax": "1.2.x", + "xmlbuilder": "8.2.x" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.0.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/zip-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/zip-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..91872e0 --- /dev/null +++ b/package.json @@ -0,0 +1,247 @@ +{ + "name": "uptime-kuma", + "version": "3.0.0-beta.0", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/louislam/uptime-kuma.git" + }, + "engines": { + "node": ">= 26.2.0" + }, + "scripts": { + "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .", + "lint:js-prod": "node --run lint:js", + "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": "node --run lint:js && node --run lint:style", + "fmt": "prettier --write \"**/*.{js,ts,vue,css,scss,json,md,yml,yaml}\"", + "lint:prod": "node --run lint:js-prod && node --run lint:style", + "dev": "concurrently -k -r \"wait-on tcp:3000 && node --run start-server-dev \" \"node --run start-frontend-dev\"", + "start-frontend-dev": "cross-env NODE_ENV=development vite --host", + "start": "node --run start-server", + "start-server": "node --import=tsx server/server.js", + "start-server-dev": "cross-env NODE_ENV=development tsx server/server.js", + "start-server-dev:watch": "cross-env NODE_ENV=development tsx --watch server/server.js", + "build": "vite build", + "test": "node --run test-backend && node --run test-e2e", + "test-with-build": "node --run build && npm test", + "test-backend": "cross-env TEST_BACKEND=1 node --import=tsx --test --test-reporter=spec \"test/backend-test/**/*.{ts,js}\"", + "test-e2e": "playwright test", + "test-e2e-local": "cross-env SKIP_TESTCONTAINER=1 npm run build && npm run test-e2e", + "test-e2e-ui": "playwright test --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", + "vite-preview-dist": "vite preview --host", + "upload-artifacts": "node extra/release/upload-artifacts.mjs", + "upload-artifacts-beta": "node extra/release/upload-artifacts-beta.mjs", + "setup": "git checkout 2.5.5 && 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 --import=tsx extra/reset-password.mts", + "remove-2fa": "node extra/remove-2fa.js", + "start-clear-server": "node extra/start-clear-server.mjs", + "simple-dns-server": "node extra/simple-dns-server.js", + "simple-mqtt-server": "node extra/simple-mqtt-server.js", + "release-final": "node ./extra/release/release.mjs", + "release-beta": "node ./extra/release/release.mjs --beta", + "release-nightly": "node ./extra/release/nightly.mjs", + "git-remove-tag": "git tag -d", + "build-dist-and-restart": "node --run build && node --run start-server-dev", + "start-pr-test": "node extra/checkout-pr.mjs && npm install && node --run dev", + "deploy-demo-server": "node extra/deploy-demo-server.js", + "quick-run-nightly": "docker run --rm --env NODE_ENV=development -p 3001:3001 louislam/uptime-kuma:nightly3", + "start-dev-container": "cd docker && docker-compose -f docker-compose-dev.yml up --force-recreate", + "reset-migrate-aggregate-table-state": "node extra/reset-migrate-aggregate-table-state.js", + "generate-changelog": "node ./extra/release/generate-changelog.mjs", + "create-release": "node ./extra/release/create-release.mjs", + "audit": "npm audit --omit=dev" + }, + "dependencies": { + "@better-auth/api-key": "1.6.11", + "@grpc/grpc-js": "~1.8.22", + "@inquirer/core": "~11.2.1", + "@inquirer/prompts": "~8.5.2", + "@louislam/ping": "~0.4.4-mod.1", + "@vvo/tzdb": "~6.198.0", + "args-parser": "~1.3.0", + "axios": "~0.33.0", + "badge-maker": "~3.3.1", + "bcryptjs": "~2.4.3", + "better-auth": "~1.6.11", + "better-sqlite3": "~12.11.1", + "chardet": "~1.4.0", + "cheerio": "~1.0.0", + "chroma-js": "~2.4.2", + "command-exists": "~1.2.9", + "compare-versions": "~3.6.0", + "country-flag-emoji-polyfill": "~0.1.8", + "croner": "~8.1.2", + "dayjs": "~1.11.19", + "dev-null": "~0.1.1", + "express": "~4.22.1", + "express-basic-auth": "~1.2.1", + "express-static-gzip": "~2.1.8", + "feed": "~4.2.2", + "form-data": "~4.0.5", + "gamedig": "~5.3.2", + "globalping": "~0.2.0", + "html-escaper": "~3.0.3", + "http-cookie-agent": "~5.0.4", + "http-graceful-shutdown": "~3.1.15", + "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.2.2", + "kafkajs": "~2.2.4", + "knex": "~3.3.0", + "limiter": "~2.1.0", + "liquidjs": "~10.29.0", + "marked": "~14.1.4", + "mitt": "~3.0.1", + "mongodb": "~4.17.2", + "mqtt": "~4.3.8", + "mssql": "~12.0.0", + "mysql2": "~3.24.2", + "nanoid": "~3.3.11", + "net-snmp": "~3.26.1", + "node-cloudflared-tunnel": "~1.0.10", + "node-radius-utils": "~1.2.0", + "nodemailer": "~10.0.0", + "nostr-tools": "~2.20.0", + "openid-client": "~5.7.1", + "oracledb": "~6.10.0", + "password-hash": "~1.2.2", + "pg": "~8.11.6", + "pg-connection-string": "~2.6.4", + "playwright-core": "~1.61.1", + "prom-client": "~13.2.0", + "prometheus-api-metrics": "~3.2.2", + "promisify-child-process": "~4.1.2", + "protobufjs": "~8.8.0", + "qs": "~6.16.0", + "radius": "~1.1.4", + "redbean-node": "~0.4.0", + "redis": "~5.9.0", + "semver": "~7.5.4", + "socket.io": "~4.8.3", + "socket.io-client": "~4.8.3", + "socks-proxy-agent": "~8.0.5", + "sqlstring": "~2.3.3", + "ssh2-sftp-client": "~12.0.1", + "tar": "~6.2.1", + "tcp-ping": "~0.1.1", + "tldts": "~7.0.23", + "tough-cookie": "~4.1.4", + "tsx": "~4.23.1", + "unlimited-timeout": "~0.1.0", + "validator": "~13.15.26", + "web-push": "~3.6.7", + "ws": "~8.21.3", + "zod": "~4.4.3" + }, + "devDependencies": { + "@actions/github": "~9.1.1", + "@fortawesome/fontawesome-svg-core": "~1.2.36", + "@fortawesome/free-regular-svg-icons": "~5.15.4", + "@fortawesome/free-solid-svg-icons": "~5.15.4", + "@fortawesome/vue-fontawesome": "~3.1.3", + "@playwright/test": "~1.61.1", + "@popperjs/core": "~2.10.2", + "@testcontainers/hivemq": "10.28.0", + "@testcontainers/mariadb": "10.28.0", + "@testcontainers/mssqlserver": "10.28.0", + "@testcontainers/mysql": "11.14.0", + "@testcontainers/oraclefree": "11.14.0", + "@testcontainers/postgresql": "11.14.0", + "@testcontainers/rabbitmq": "10.28.0", + "@types/better-sqlite3": "~7.6.13", + "@types/bootstrap": "~5.1.13", + "@types/node": "^26.1.1", + "@types/web-push": "^3.6.4", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "@vitejs/plugin-vue": "~6.0.7", + "@vue/compiler-sfc": "~3.5.39", + "@vuepic/vue-datepicker": "~3.4.8", + "aedes": "~1.0.0", + "bootstrap": "5.1.3", + "chart.js": "~4.2.1", + "chartjs-adapter-dayjs-4": "~1.0.4", + "concurrently": "^7.6.0", + "cronstrue": "~2.24.0", + "cross-env": "~7.0.3", + "dns2": "~2.0.5", + "dompurify": "~3.4.0", + "eslint": "~8.14.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-jsdoc": "~46.4.6", + "eslint-plugin-vue": "~8.7.1", + "eslint-plugin-vue-scoped-css": "~2.7.2", + "favico.js": "~0.3.10", + "node-ssh": "~13.1.0", + "postcss-html": "~1.8.1", + "postcss-rtlcss": "~5.7.1", + "postcss-scss": "~4.0.9", + "prettier": "^3.8.1", + "prismjs": "~1.30.0", + "qrcode": "~1.5.4", + "rollup-plugin-visualizer": "~7.0.1", + "sass": "^1.101.0", + "ssh2": "~1.17.0", + "stylelint": "^15.11.0", + "stylelint-config-prettier": "^9.0.5", + "stylelint-config-standard": "~25.0.0", + "terser": "~5.15.1", + "test": "~3.3.0", + "testcontainers": "^11.12.0", + "typescript": "~4.4.4", + "v-pagination-3": "~0.1.7", + "vite": "~8.1.2", + "vite-plugin-compression": "^0.5.1", + "vue": "~3.5.39", + "vue-chartjs": "~5.2.0", + "vue-contenteditable": "~3.0.4", + "vue-i18n": "~11.2.8", + "vue-image-crop-upload": "~3.0.3", + "vue-multiselect": "~3.0.0", + "vue-prism-editor": "~2.0.0-alpha.2", + "vue-qrcode": "~1.0.1", + "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" + }, + "overrides": { + "@aws-sdk/credential-providers": "npm:useless-module@1.0.0", + "tedious": { + "@azure/identity": "npm:useless-module@1.0.0", + "@azure/keyvault-keys": "npm:useless-module@1.0.0" + }, + "knex": "$knex" + }, + "allowScripts": { + "better-sqlite3@12.11.1": true, + "@fortawesome/fontawesome-common-types@0.2.36": true, + "@fortawesome/fontawesome-svg-core@1.2.36": true, + "@fortawesome/free-regular-svg-icons@5.15.4": true, + "@fortawesome/free-solid-svg-icons@5.15.4": true, + "core-js@2.6.12": true, + "core-js@3.26.1": true, + "cpu-features@0.0.10": true, + "protobufjs@7.5.4": true, + "protobufjs@7.6.1": true, + "protobufjs@7.2.6": true, + "protobufjs@7.6.6": true, + "oracledb@6.10.0": true, + "ssh2@1.17.0": true, + "vue-demi@0.12.5": true, + "esbuild@0.28.2": true, + "@parcel/watcher@2.6.0": true + } +} diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 0000000..15d520c --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,84 @@ +import { defineConfig, devices } from "@playwright/test"; +import { loadEnvFile } from "node:process"; + +// Load environment variables from `.env` (same as server.js), so local-only options +// such as HEADLESS_E2E_TESTS can be set there. +try { + loadEnvFile(); +} catch (_) {} + +const port = 3001; + +// CI is always headless. Locally, `HEADLESS_E2E_TESTS=1` (in `.env` or the environment) forces it too. +const headless = !!process.env.CI || process.env.HEADLESS_E2E_TESTS === "1"; +export const url = `http://localhost:${port}`; + +export default defineConfig({ + // Look for test files in the "tests" directory, relative to this configuration file. + testDir: "test/e2e/specs", + outputDir: "private/playwright-test-results", + fullyParallel: false, + locale: "en-US", + maxFailures: 1, + + // Fail the build on CI if you accidentally left test.only in the source code. + forbidOnly: !!process.env.CI, + + // Retry on CI only. + retries: process.env.CI ? 2 : 0, + + // Opt out of parallel tests on CI. + workers: 1, + + // Reporter to use + reporter: [ + [ + "html", + { + outputFolder: "private/playwright-report", + open: "never", + }, + ], + ], + + use: { + // Base URL to use in actions like `await page.goto('/')`. + baseURL: url, + + headless, + + // Collect trace when retrying the failed test. + trace: "on-first-retry", + + launchOptions: { + args: ["--start-minimized"], + }, + }, + + // Configure projects for major browsers. + projects: [ + { + name: "run-once setup", + testMatch: /setup-process\.once\.js/, + use: { ...devices["Desktop Chrome"] }, + }, + { + name: "specs", + use: { ...devices["Desktop Chrome"] }, + dependencies: ["run-once setup"], + }, + /* + { + name: "firefox", + use: { browserName: "firefox" } + },*/ + ], + + // Run your local dev server before starting the tests. + webServer: { + command: `node extra/remove-playwright-test-data.js && cross-env NODE_ENV=development node --import=tsx server/server.js --port=${port} --data-dir=./data/playwright-test`, + url, + reuseExistingServer: false, + cwd: "./", + }, +}); diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 0000000..8dae0df Binary files /dev/null and b/public/apple-touch-icon.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..dedf0cd Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/icon-192x192.png b/public/icon-192x192.png new file mode 100644 index 0000000..03c8a4d Binary files /dev/null and b/public/icon-192x192.png differ diff --git a/public/icon-512x512.png b/public/icon-512x512.png new file mode 100644 index 0000000..11278c6 Binary files /dev/null and b/public/icon-512x512.png differ diff --git a/public/icon.png b/public/icon.png new file mode 100644 index 0000000..4ff5fdc Binary files /dev/null and b/public/icon.png differ diff --git a/public/icon.svg b/public/icon.svg new file mode 100644 index 0000000..c421791 --- /dev/null +++ b/public/icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..faf3f70 --- /dev/null +++ b/public/manifest.json @@ -0,0 +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": [ + { + "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" + } + ] + }, + { + "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" + } + ] + } + ] +} diff --git a/public/serviceWorker.js b/public/serviceWorker.js new file mode 100644 index 0000000..b02eaf0 --- /dev/null +++ b/public/serviceWorker.js @@ -0,0 +1,35 @@ +self.addEventListener("install", function (event) { + self.skipWaiting(); +}); + +// Clear old caches from vite-plugin-pwa +self.addEventListener("activate", function (event) { + event.waitUntil( + (async function () { + const cacheNames = await caches.keys(); + for (const cacheName of cacheNames) { + await caches.delete(cacheName); + } + await self.clients.claim(); + })() + ); +}); + +// Receive push notifications +self.addEventListener("push", function (event) { + if (self.Notification?.permission !== "granted") { + console.error("Notifications aren't supported or permission not granted!"); + return; + } + + if (event.data) { + let message = event.data.json(); + try { + self.registration.showNotification(message.title, { + body: message.body, + }); + } catch (error) { + console.error("Failed to show notification:", error); + } + } +}); diff --git a/server/analytics/analytics.js b/server/analytics/analytics.js new file mode 100644 index 0000000..3b3cb48 --- /dev/null +++ b/server/analytics/analytics.js @@ -0,0 +1,55 @@ +const googleAnalytics = require("./google-analytics"); +const umamiAnalytics = require("./umami-analytics"); +const plausibleAnalytics = require("./plausible-analytics"); +const matomoAnalytics = require("./matomo-analytics"); +const rybbitAnalytics = require("./rybbit-analytics"); + +/** + * Returns a string that represents the javascript that is required to insert the selected Analytics' script + * into a webpage. + * @param {typeof import("../model/status_page").StatusPage} statusPage Status page populate HTML with + * @returns {string} HTML script tags to inject into page + */ +function getAnalyticsScript(statusPage) { + switch (statusPage.analyticsType) { + case "google": + return googleAnalytics.getGoogleAnalyticsScript(statusPage.analyticsId); + case "umami": + return umamiAnalytics.getUmamiAnalyticsScript(statusPage.analyticsScriptUrl, statusPage.analyticsId); + case "plausible": + return plausibleAnalytics.getPlausibleAnalyticsScript( + statusPage.analyticsScriptUrl, + statusPage.analyticsId + ); + case "matomo": + return matomoAnalytics.getMatomoAnalyticsScript(statusPage.analyticsScriptUrl, statusPage.analyticsId); + case "rybbit": + return rybbitAnalytics.getRybbitAnalyticsScript(statusPage.analyticsScriptUrl, statusPage.analyticsId); + default: + return null; + } +} + +/** + * Function that checks wether the selected analytics has been configured properly + * @param {typeof import("../model/status_page").StatusPage} statusPage Status page populate HTML with + * @returns {boolean} Boolean defining if the analytics config is valid + */ +function isValidAnalyticsConfig(statusPage) { + switch (statusPage.analyticsType) { + case "google": + return statusPage.analyticsId != null; + case "umami": + case "plausible": + case "matomo": + case "rybbit": + return statusPage.analyticsId != null && statusPage.analyticsScriptUrl != null; + default: + return false; + } +} + +module.exports = { + getAnalyticsScript, + isValidAnalyticsConfig, +}; diff --git a/server/analytics/google-analytics.js b/server/analytics/google-analytics.js new file mode 100644 index 0000000..57ae7b7 --- /dev/null +++ b/server/analytics/google-analytics.js @@ -0,0 +1,28 @@ +const jsesc = require("jsesc"); +const { escape } = require("html-escaper"); + +/** + * Returns a string that represents the javascript that is required to insert the Google Analytics scripts + * into a webpage. + * @param {string} tagId Google UA/G/AW/DC Property ID to use with the Google Analytics script. + * @returns {string} HTML script tags to inject into page + */ +function getGoogleAnalyticsScript(tagId) { + let escapedTagIdJS = jsesc(tagId, { isScriptContext: true }); + + if (escapedTagIdJS) { + escapedTagIdJS = escapedTagIdJS.trim(); + } + + // Escape the tag ID for use in an HTML attribute. + let escapedTagIdHTMLAttribute = escape(tagId); + + return ` + + + `; +} + +module.exports = { + getGoogleAnalyticsScript, +}; diff --git a/server/analytics/matomo-analytics.js b/server/analytics/matomo-analytics.js new file mode 100644 index 0000000..fdc009e --- /dev/null +++ b/server/analytics/matomo-analytics.js @@ -0,0 +1,47 @@ +const jsesc = require("jsesc"); +const { escape } = require("html-escaper"); + +/** + * Returns a string that represents the javascript that is required to insert the Matomo Analytics script + * into a webpage. + * @param {string} matomoUrl Domain name with tld to use with the Matomo Analytics script. + * @param {string} siteId Site ID to use with the Matomo Analytics script. + * @returns {string} HTML script tags to inject into page + */ +function getMatomoAnalyticsScript(matomoUrl, siteId) { + let escapedMatomoUrlJS = jsesc(matomoUrl, { isScriptContext: true }); + let escapedSiteIdJS = jsesc(siteId, { isScriptContext: true }); + + if (escapedMatomoUrlJS) { + escapedMatomoUrlJS = escapedMatomoUrlJS.trim(); + } + + if (escapedSiteIdJS) { + escapedSiteIdJS = escapedSiteIdJS.trim(); + } + + // Escape the domain url for use in an HTML attribute. + let escapedMatomoUrlHTMLAttribute = escape(escapedMatomoUrlJS); + + // Escape the website id for use in an HTML attribute. + let escapedSiteIdHTMLAttribute = escape(escapedSiteIdJS); + + return ` + + `; +} + +module.exports = { + getMatomoAnalyticsScript, +}; diff --git a/server/analytics/plausible-analytics.js b/server/analytics/plausible-analytics.js new file mode 100644 index 0000000..015f633 --- /dev/null +++ b/server/analytics/plausible-analytics.js @@ -0,0 +1,36 @@ +const jsesc = require("jsesc"); +const { escape } = require("html-escaper"); + +/** + * Returns a string that represents the javascript that is required to insert the Plausible Analytics script + * into a webpage. + * @param {string} scriptUrl the Plausible Analytics script url. + * @param {string} domainsToMonitor Domains to track separated by a ',' to add Plausible Analytics script. + * @returns {string} HTML script tags to inject into page + */ +function getPlausibleAnalyticsScript(scriptUrl, domainsToMonitor) { + let escapedScriptUrlJS = jsesc(scriptUrl, { isScriptContext: true }); + let escapedWebsiteIdJS = jsesc(domainsToMonitor, { isScriptContext: true }); + + if (escapedScriptUrlJS) { + escapedScriptUrlJS = escapedScriptUrlJS.trim(); + } + + if (escapedWebsiteIdJS) { + escapedWebsiteIdJS = escapedWebsiteIdJS.trim(); + } + + // Escape the domain url for use in an HTML attribute. + let escapedScriptUrlHTMLAttribute = escape(escapedScriptUrlJS); + + // Escape the website id for use in an HTML attribute. + let escapedWebsiteIdHTMLAttribute = escape(escapedWebsiteIdJS); + + return ` + + `; +} + +module.exports = { + getPlausibleAnalyticsScript, +}; diff --git a/server/analytics/rybbit-analytics.js b/server/analytics/rybbit-analytics.js new file mode 100644 index 0000000..06d70ec --- /dev/null +++ b/server/analytics/rybbit-analytics.js @@ -0,0 +1,36 @@ +const jsesc = require("jsesc"); +const { escape } = require("html-escaper"); + +/** + * Returns a string that represents the javascript that is required to insert the Rybbit Analytics script + * into a webpage. + * @param {string} scriptUrl the Rybbit Analytics script url. + * @param {string} siteId Site ID to use with the Rybbit Analytics script. + * @returns {string} HTML script tags to inject into page + */ +function getRybbitAnalyticsScript(scriptUrl, siteId) { + let escapedScriptUrlJS = jsesc(scriptUrl, { isScriptContext: true }); + let escapedSiteIdJS = jsesc(siteId, { isScriptContext: true }); + + if (escapedScriptUrlJS) { + escapedScriptUrlJS = escapedScriptUrlJS.trim(); + } + + if (escapedSiteIdJS) { + escapedSiteIdJS = escapedSiteIdJS.trim(); + } + + // Escape the Script url for use in an HTML attribute. + let escapedScriptUrlHTMLAttribute = escape(escapedScriptUrlJS); + + // Escape the site id for use in an HTML attribute. + let escapedSiteIdHTMLAttribute = escape(escapedSiteIdJS); + + return ` + + `; +} + +module.exports = { + getRybbitAnalyticsScript, +}; diff --git a/server/analytics/umami-analytics.js b/server/analytics/umami-analytics.js new file mode 100644 index 0000000..48c8b2e --- /dev/null +++ b/server/analytics/umami-analytics.js @@ -0,0 +1,36 @@ +const jsesc = require("jsesc"); +const { escape } = require("html-escaper"); + +/** + * Returns a string that represents the javascript that is required to insert the Umami Analytics script + * into a webpage. + * @param {string} scriptUrl the Umami Analytics script url. + * @param {string} websiteId Website ID to use with the Umami Analytics script. + * @returns {string} HTML script tags to inject into page + */ +function getUmamiAnalyticsScript(scriptUrl, websiteId) { + let escapedScriptUrlJS = jsesc(scriptUrl, { isScriptContext: true }); + let escapedWebsiteIdJS = jsesc(websiteId, { isScriptContext: true }); + + if (escapedScriptUrlJS) { + escapedScriptUrlJS = escapedScriptUrlJS.trim(); + } + + if (escapedWebsiteIdJS) { + escapedWebsiteIdJS = escapedWebsiteIdJS.trim(); + } + + // Escape the Script url for use in an HTML attribute. + let escapedScriptUrlHTMLAttribute = escape(escapedScriptUrlJS); + + // Escape the website id for use in an HTML attribute. + let escapedWebsiteIdHTMLAttribute = escape(escapedWebsiteIdJS); + + return ` + + `; +} + +module.exports = { + getUmamiAnalyticsScript, +}; diff --git a/server/auth.js b/server/auth.js new file mode 100644 index 0000000..214f0e9 --- /dev/null +++ b/server/auth.js @@ -0,0 +1,183 @@ +const basicAuth = require("express-basic-auth"); +const passwordHash = require("./password-hash"); +const { R } = require("redbean-node"); +const { log } = require("../src/util"); +const { loginRateLimiter, apiRateLimiter } = require("./rate-limiter"); +const { Settings } = require("./settings"); +const dayjs = require("dayjs"); +const { checkPassword } = require("./better-auth"); + +/** + * @deprecated DO NOT CALL IT. Use Better Auth instead. + * Old Login function, keep it for migration purposes. + * @param {string} username Username to login with + * @param {string} password Password to login with + * @returns {Promise<(Bean|null)>} User or null if login failed + */ +exports.login = async function (username, password) { + if (typeof username !== "string" || typeof password !== "string") { + return null; + } + + 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 + if (passwordHash.needRehash(user.password)) { + await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [ + await passwordHash.generate(password), + user.id, + ]); + } + return user; + } + + return null; +}; + +/** + * Validate a provided API key + * @param {string} key API key to verify + * @returns {boolean} API is ok? + */ +async function verifyAPIKey(key) { + if (typeof key !== "string") { + return false; + } + + // uk prefix + key ID is before _ + 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]); + + if (hash === null) { + return false; + } + + let current = dayjs(); + let expiry = dayjs(hash.expires); + if (expiry.diff(current) < 0 || !hash.active) { + return false; + } + + return hash && passwordHash.verify(clear, hash.key); +} + +/** + * Callback for basic auth authorizers + * @callback authCallback + * @param {any} err Any error encountered + * @param {boolean} authorized Is the client authorized? + */ + +/** + * Custom authorizer for express-basic-auth + * @param {string} username Username to login with + * @param {string} password Password to login with + * @param {authCallback} callback Callback to handle login result + * @returns {void} + */ +function apiAuthorizer(username, password, callback) { + // API Rate Limit + apiRateLimiter.pass(null, 0).then((pass) => { + if (pass) { + verifyAPIKey(password).then((valid) => { + if (!valid) { + log.warn("api-auth", "Failed API auth attempt: invalid API Key"); + } + callback(null, valid); + // Only allow a set number of api requests per minute + // (currently set to 60) + apiRateLimiter.removeTokens(1); + }); + } else { + log.warn("api-auth", "Failed API auth attempt: rate limit exceeded"); + callback(null, false); + } + }); +} + +/** + * Custom authorizer for express-basic-auth + * @param {string} username Username to login with + * @param {string} password Password to login with + * @param {authCallback} callback Callback to handle login result + * @returns {void} + */ +function userAuthorizer(username, password, callback) { + // Login Rate Limit + loginRateLimiter.pass(null, 0).then((pass) => { + if (pass) { + checkPassword(username, password) + .then((valid) => { + callback(null, valid); + + if (!valid) { + log.warn("basic-auth", "Failed basic auth attempt: invalid username/password"); + loginRateLimiter.removeTokens(1); + } + }) + .catch((e) => { + log.error("basic-auth", "Auth error:", e); + callback(null, false); + }); + } else { + log.warn("basic-auth", "Failed basic auth attempt: rate limit exceeded"); + callback(null, false); + } + }); +} + +/** + * Use basic auth if auth is not disabled + * @param {express.Request} req Express request object + * @param {express.Response} res Express response object + * @param {express.NextFunction} next Next handler in chain + * @returns {Promise} + */ +exports.basicAuth = async function (req, res, next) { + const middleware = basicAuth({ + authorizer: userAuthorizer, + authorizeAsync: true, + challenge: true, + }); + + const disabledAuth = await Settings.get("disableAuth"); + + if (!disabledAuth) { + middleware(req, res, next); + } else { + next(); + } +}; + +/** + * Use use API Key if API keys enabled, else use basic auth + * @param {express.Request} req Express request object + * @param {express.Response} res Express response object + * @param {express.NextFunction} next Next handler in chain + * @returns {Promise} + */ +exports.apiAuth = async function (req, res, next) { + if (!(await Settings.get("disableAuth"))) { + let usingAPIKeys = await Settings.get("apiKeysEnabled"); + let middleware; + if (usingAPIKeys) { + middleware = basicAuth({ + authorizer: apiAuthorizer, + authorizeAsync: true, + challenge: true, + }); + } else { + middleware = basicAuth({ + authorizer: userAuthorizer, + authorizeAsync: true, + challenge: true, + }); + } + middleware(req, res, next); + } else { + next(); + } +}; diff --git a/server/better-auth.ts b/server/better-auth.ts new file mode 100644 index 0000000..141543a --- /dev/null +++ b/server/better-auth.ts @@ -0,0 +1,408 @@ +import { betterAuth } from "better-auth"; +// @ts-ignore +import * as Database from "./database.js"; +import { genSecret, isDevEnv, log } from "../src/util"; +import { R } from "redbean-node"; +import { username } from "better-auth/plugins"; +import { admin } from "better-auth/plugins"; +import { Socket } from "socket.io"; +import { haveIBeenPwned } from "better-auth/plugins"; +import { twoFactor } from "better-auth/plugins"; +import { apiKey } from "@better-auth/api-key"; +import { createAuthMiddleware } from "better-auth/api"; +// @ts-ignore +import * as oldAuth from "./auth.js"; +import { hasUser } from "./routers/better-auth-router"; +import { symmetricEncrypt } from "better-auth/crypto"; +// @ts-ignore +import config from "./config.js"; + +export type BetterAuthUser = ReturnType["$Infer"]["Session"]["user"]; + +let authInstance: ReturnType; + +/** + * Whether the HIBP password check is enabled (`UPTIME_KUMA_DISABLE_LEAK_PWD_CHECK` / `--disable-leak-pwd-check`). + * @returns True to run the check + */ +function isHaveIBeenPwnedEnabled() { + const disableHibp = process.env.UPTIME_KUMA_DISABLE_LEAK_PWD_CHECK; + const disabledByEnv = !!disableHibp && disableHibp !== "false" && disableHibp !== "0"; + return !disabledByEnv && !config.args["disable-leak-pwd-check"]; +} + +// Shared haveIBeenPwned plugin options; `enabled` is turned off during legacy user migration (#7946). +const haveIBeenPwnedOptions = { + enabled: true, +}; + +/** + * Get the singleton instance of better-auth + * Mainly used for http and socket.io authentication + * @param force If true, create a new instance even if one already exists. Should only be used for testing or special cases. + * @returns The singleton instance of better-auth + */ +export function auth(force = false) { + if (authInstance && !force) { + return authInstance; + } + + authInstance = createAuthInstance(); + return authInstance; +} + +/** + * Do staff without any auth + * For internal usage only, the api should be a superset of auth().api + * @returns Internal adapter of better-auth + */ +export async function authInternal() { + return (await auth().$context).internalAdapter; +} + +/** + * Create a new instance of better-auth + * @throws Error if Database.initDataDir() has not been called + * @returns A new instance + */ +function createAuthInstance() { + // Check if Database.initDataDir() has been called before using this function + if (!Database.dataDir) { + throw new Error( + "Database data directory is not initialized. Please call Database.initDataDir() before using auth." + ); + } + + // Apply the UPTIME_KUMA_DISABLE_LEAK_PWD_CHECK / --disable-leak-pwd-check setting + haveIBeenPwnedOptions.enabled = isHaveIBeenPwnedEnabled(); + if (!haveIBeenPwnedOptions.enabled) { + log.warn("auth", "haveIBeenPwned password check is disabled (UPTIME_KUMA_DISABLE_LEAK_PWD_CHECK)."); + } + + const authDatabase = Database.createAuthDatabase(Database.dbConfig); + + return betterAuth({ + database: authDatabase, + + // Just want to silent the warning message + // As we don't use callback/redirect, it is not used + baseURL: "http://localhost:3000", + + secret: getAuthSecret(), + // Should be handled in Express.js, check better-auth-router.ts + trustedOrigins: ["*"], + + // Since multiple users are not supported yet, disable these paths to prevent accidental usage + disabledPaths: [ + "/admin/create-user", + "/admin/get-user", + "/admin/list-users", + "/admin/update-user", + "/admin/set-user-password", + "/admin/set-role", + "/admin/ban-user", + "/admin/unban-user", + "/admin/impersonate-user", + "/admin/stop-impersonating", + "/admin/remove-user", + "/admin/list-user-sessions", + "/admin/revoke-user-session", + "/admin/revoke-user-sessions", + "/admin/has-permission", + ], + + emailAndPassword: { + revokeSessionsOnPasswordReset: true, + enabled: true, + disableSignUp: true, + }, + rateLimit: { + // Disabled in development (e.g. E2E tests) to avoid throttling rapid logins + enabled: !isDevEnv(), + + // Seconds + window: 60, + + // Requests per window + max: 10, + }, + plugins: [ + // Enable login by username + username(), + + // Enable user management API (used for creating the first admin user) + admin(), + + // Check if the password has been pwned in data breaches + haveIBeenPwned(haveIBeenPwnedOptions), + + twoFactor({ + schema: { + twoFactor: { + modelName: "better_auth_twoFactor", + }, + }, + }), + + apiKey({ + schema: { + apikey: { + modelName: "better_auth_apikey", + }, + }, + }), + + // It is not suitable for "Disable Auth", because it can not turn on/off after init. + //anonymous(), + ], + user: { + modelName: "better_auth_user", + }, + account: { + modelName: "better_auth_account", + }, + session: { + modelName: "better_auth_session", + }, + verification: { + modelName: "better_auth_verification", + }, + + hooks: { + before: createAuthMiddleware(async (ctx) => { + if (ctx.path.startsWith("/sign-in/")) { + const username = ctx.body?.username; + const password = ctx.body?.password; + + // Migrate legacy user from old user table to better-auth + // Only do this when there is no user in better-auth + if (!(await hasUser())) { + await migrateUser(username, password); + } + } + }), + }, + }); +} + +/** + * Get the authentication secret for better-auth + * @returns The authentication secret + */ +export function getAuthSecret() { + const env = process.env.UPTIME_KUMA_AUTH_SECRET; + if (env) { + return env; + } + + if (!Database.dbConfig.authSecret) { + Database.dbConfig.authSecret = genSecret(); + Database.writeDBConfig(Database.dbConfig); + } + + return Database.dbConfig.authSecret; +} + +/** + * Encrypt a string using the BetterAuth encryption method + * @param data The string to encrypt + * @returns The encrypted string + */ +export async function betterAuthEncrypt(data: string): Promise { + const encrypted = await symmetricEncrypt({ + key: auth().options.secret, + data, + }); + + return encrypted; +} + +/** + * Get session from cookie + * @param cookie Cookie string + * @returns Session Object + */ +export function getSession(cookie: string) { + log.info("auth", "Logged in with httpOnly cookie session"); + const context = { + headers: createHeaders(cookie), + }; + return authInstance.api.getSession(context); +} + +/** + * Create Headers object with cookie for API calls + * @param cookie Cookie string + * @returns Headers object + */ +export function createHeaders(cookie: string) { + const headers = new Headers(); + headers.set("cookie", cookie || ""); + return headers; +} + +/** + * Unfortunatety, there is no way to get a user object from better-auth api, so we have to craft a session object here. + * @returns Crafted Session Object + */ +export async function getDisableAuthSession(): ReturnType { + log.info("auth", "Logged in with Disable Auth"); + + const obj = await R.getRow("SELECT * FROM better_auth_user LIMIT 1"); + + if (!obj) { + throw new Error("Unexpected Error: No user found in the database."); + } + + const user = { + id: obj.id as string, + createdAt: obj.createdAt as Date, + updatedAt: obj.updatedAt as Date, + email: obj.email as string, + emailVerified: obj.emailVerified === 1, + name: obj.name as string, + image: obj.image as string | null, + username: obj.username as string | null, + displayUsername: obj.displayUsername as string | null, + banned: obj.banned === 1, + role: obj.role as string | null, + banReason: obj.banReason as string | null, + banExpires: obj.banExpires as Date | null, + twoFactorEnabled: obj.twoFactorEnabled === 1, + }; + + return { + user, + session: { + id: "disable-auth", + userId: user.id, + ipAddress: null, + userAgent: null, + createdAt: new Date(), + updatedAt: new Date(), + expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365), + token: "disable-auth", + }, + }; +} + +/** + * Check Login (Better Auth New!) + * @param socket Socket.IO Socket + * @throws Error if not logged in + */ +export function checkLogin(socket: Socket) { + // @ts-ignore + if (!socket.session) { + throw new Error("You are not logged in."); + } +} + +/** + * For logged-in users, double-check the password + * @param cookie Cookie string + * @param currentPassword Password to verify + * @throws Error if the password is incorrect or the user is not found + */ +export async function doubleCheckPassword(cookie: string, currentPassword: string): Promise { + const { status } = await authInstance.api.verifyPassword({ + body: { + password: currentPassword, + }, + headers: createHeaders(cookie), + }); + + if (!status) { + throw new Error("Incorrect current password"); + } +} + +/** + * Migrate a legacy user from the old `user` table to better-auth tables. + * @param username Legacy username + * @param password Plain-text password from the login form + */ +export async function migrateUser(username: string, password: string) { + const legacyUser = await oldAuth.login(username, password); + if (legacyUser) { + // Skip the HIBP check: the legacy user's existing password must not be rejected (#7946) + haveIBeenPwnedOptions.enabled = false; + try { + // Create BetterAuth user with the same username and password + const newUser = await auth().api.createUser({ + body: { + name: username, + email: `${username}@noreply.uptime-kuma.internal`, + password, + role: "admin", + data: { + username, + }, + }, + }); + + // Migrate 2FA settings if they exist + if (legacyUser.twofa_status) { + log.info("auth", `Migrating 2FA settings for user: ${username}`); + + const rowId = genSecret(32); + const userId = newUser.user.id; + + const encryptedSecret = await betterAuthEncrypt(legacyUser.twofa_secret); + const encryptedBackupCodes = await betterAuthEncrypt(JSON.stringify([])); // Empty backup codes for migration, as we don't have the original backup codes + + await R.knex("better_auth_twoFactor").insert({ + id: rowId, + secret: encryptedSecret, + backupCodes: encryptedBackupCodes, + verified: true, + userId: userId, + }); + + await R.knex("better_auth_user").where({ id: userId }).update({ + twoFactorEnabled: true, + }); + } + log.info("auth", `Migrated legacy user: ${username}`); + } catch (e) { + log.error("auth", `Failed to migrate legacy user ${username}:`, e); + } finally { + haveIBeenPwnedOptions.enabled = isHaveIBeenPwnedEnabled(); + } + } else { + log.info("auth", `No legacy user found for username: ${username}, do not migrate.`); + } +} + +/** + * @deprecated DO NOT USE THIS FUNCTION, just for backward compatibility + * Check username / password without creating a session, mainly for basic auth + * @param username Username + * @param password Plain-text password + * @returns True if the username and password are correct, false otherwise + */ +export async function checkPassword(username: string, password: string): Promise { + const { adapter, password: passwordVerifier } = await auth().$context; + const internalAdapter = await authInternal(); + + const user = await adapter.findOne>({ + model: "user", + where: [{ field: "username", value: username }], + }); + + if (!user) { + return false; + } + + const accounts = await internalAdapter.findAccounts(user.id); + const credentialAccount = accounts.find((ac) => ac.providerId === "credential"); + + if (!credentialAccount?.password) { + return false; + } + + return passwordVerifier.verify({ + hash: credentialAccount.password, + password, + }); +} diff --git a/server/check-version.js b/server/check-version.js new file mode 100644 index 0000000..79dfa10 --- /dev/null +++ b/server/check-version.js @@ -0,0 +1,67 @@ +const { setSetting, setting } = require("./util-server"); +const axios = require("axios"); +const compareVersions = require("compare-versions"); +const { log } = require("../src/util"); + +exports.version = require("../package.json").version; +exports.latestVersion = null; + +// How much time in ms to wait between update checks +const UPDATE_CHECKER_INTERVAL_MS = 1000 * 60 * 60 * 48; +const UPDATE_CHECKER_LATEST_VERSION_URL = "https://uptime.kuma.pet/version"; + +let interval; + +exports.startInterval = () => { + let check = async () => { + if ((await setting("checkUpdate")) === false) { + return; + } + + log.debug("update-checker", "Retrieving latest versions"); + + try { + const res = await axios.get(UPDATE_CHECKER_LATEST_VERSION_URL); + + // For debug + if (process.env.TEST_CHECK_VERSION === "1") { + res.data.slow = "1000.0.0"; + } + + let checkBeta = await setting("checkBeta"); + + if (checkBeta && res.data.beta) { + if (compareVersions.compare(res.data.beta, res.data.slow, ">")) { + exports.latestVersion = res.data.beta; + return; + } + } + + if (res.data.slow) { + exports.latestVersion = res.data.slow; + } + } catch (_) { + log.info("update-checker", "Failed to check for new versions"); + } + }; + + check(); + interval = setInterval(check, UPDATE_CHECKER_INTERVAL_MS); +}; + +/** + * Enable the check update feature + * @param {boolean} value Should the check update feature be enabled? + * @returns {Promise} + */ +exports.enableCheckUpdate = async (value) => { + await setSetting("checkUpdate", value); + + clearInterval(interval); + + if (value) { + exports.startInterval(); + } +}; + +exports.socket = null; diff --git a/server/client.js b/server/client.js new file mode 100644 index 0000000..5e7596c --- /dev/null +++ b/server/client.js @@ -0,0 +1,248 @@ +/* + * For Client Socket + */ +const { TimeLogger } = require("../src/util"); +const { R } = require("redbean-node"); +const { UptimeKumaServer } = require("./uptime-kuma-server"); +const server = UptimeKumaServer.getInstance(); +const io = server.io; +const { setting } = require("./util-server"); +const checkVersion = require("./check-version"); +const Database = require("./database"); + +/** + * Send list of notification providers to client + * @param {Socket} socket Socket.io socket instance + * @returns {Promise} List of notifications + */ +async function sendNotificationList(socket) { + const timeLogger = new TimeLogger(); + + let result = []; + let list = await R.find("notification"); + + for (let bean of list) { + let notificationObject = bean.export(); + notificationObject.isDefault = notificationObject.isDefault === 1; + notificationObject.active = notificationObject.active === 1; + result.push(notificationObject); + } + + io.to(socket.userID).emit("notificationList", result); + + timeLogger.print("Send Notification List"); + + return list; +} + +/** + * Send Heartbeat History list to socket + * @param {Socket} socket Socket.io instance + * @param {number} monitorID ID of monitor to send heartbeat history + * @param {boolean} toUser True = send to all browsers with the same user id, False = send to the current browser only + * @param {boolean} overwrite Overwrite client-side's heartbeat list + * @returns {Promise} + */ +async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { + let list = await R.getAll( + ` + SELECT * FROM heartbeat + WHERE monitor_id = ? + ORDER BY time DESC + LIMIT 100 + `, + [monitorID] + ); + + let result = list.reverse(); + + if (toUser) { + io.to(socket.userID).emit("heartbeatList", monitorID, result, overwrite); + } else { + socket.emit("heartbeatList", monitorID, result, overwrite); + } +} + +/** + * Important Heart beat list (aka event list) + * @param {Socket} socket Socket.io instance + * @param {number} monitorID ID of monitor to send heartbeat history + * @param {boolean} toUser True = send to all browsers with the same user id, False = send to the current browser only + * @param {boolean} overwrite Overwrite client-side's heartbeat list + * @returns {Promise} + */ +async function sendImportantHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { + const timeLogger = new TimeLogger(); + + let list = await R.find( + "heartbeat", + ` + monitor_id = ? + AND important = 1 + ORDER BY time DESC + LIMIT 500 + `, + [monitorID] + ); + + timeLogger.print(`[Monitor: ${monitorID}] sendImportantHeartbeatList`); + + const result = list.map((bean) => bean.toJSON()); + + if (toUser) { + io.to(socket.userID).emit("importantHeartbeatList", monitorID, result, overwrite); + } else { + socket.emit("importantHeartbeatList", monitorID, result, overwrite); + } +} + +/** + * Emit proxy list to client + * @param {Socket} socket Socket.io socket instance + * @returns {Promise} List of proxies + */ +async function sendProxyList(socket) { + const timeLogger = new TimeLogger(); + + const list = await R.find("proxy"); + io.to(socket.userID).emit( + "proxyList", + list.map((bean) => bean.export()) + ); + + timeLogger.print("Send Proxy List"); + + return list; +} + +/** + * Emit API key list to client + * @param {Socket} socket Socket.io socket instance + * @returns {Promise} + */ +async function sendAPIKeyList(socket) { + const timeLogger = new TimeLogger(); + + let result = []; + const list = await R.find("api_key"); + + for (let bean of list) { + result.push(bean.toPublicJSON()); + } + + io.to(socket.userID).emit("apiKeyList", result); + timeLogger.print("Sent API Key List"); + + return list; +} + +/** + * Emits the version information to the client. + * @param {Socket} socket Socket.io socket instance + * @param {boolean} hideVersion Should we hide the version information in the response? + * @returns {Promise} + */ +async function sendInfo(socket, hideVersion = false) { + const info = { + primaryBaseURL: await setting("primaryBaseURL"), + serverTimezone: await server.getTimezone(), + serverTimezoneOffset: server.getTimezoneOffset(), + }; + if (!hideVersion) { + info.version = checkVersion.version; + info.latestVersion = checkVersion.latestVersion; + info.isContainer = process.env.UPTIME_KUMA_IS_CONTAINER === "1"; + info.dbType = Database.dbConfig.type; + info.runtime = { + platform: process.platform, // linux or win32 + arch: process.arch, // x86 or arm + }; + } + + socket.emit("info", info); +} + +/** + * Send list of docker hosts to client + * @param {Socket} socket Socket.io socket instance + * @returns {Promise} List of docker hosts + */ +async function sendDockerHostList(socket) { + const timeLogger = new TimeLogger(); + + let result = []; + let list = await R.find("docker_host"); + + for (let bean of list) { + result.push(bean.toJSON()); + } + + io.to(socket.userID).emit("dockerHostList", result); + + timeLogger.print("Send Docker Host List"); + + return list; +} + +/** + * Send list of docker hosts to client + * @param {Socket} socket Socket.io socket instance + * @returns {Promise} List of docker hosts + */ +async function sendRemoteBrowserList(socket) { + const timeLogger = new TimeLogger(); + + let result = []; + let list = await R.find("remote_browser"); + + for (let bean of list) { + result.push(bean.toJSON()); + } + + io.to(socket.userID).emit("remoteBrowserList", result); + + timeLogger.print("Send Remote Browser List"); + + return list; +} + +/** + * Send list of monitor types to client + * @param {Socket} socket Socket.io socket instance + * @returns {Promise} + */ +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, + }; + }), + }; + }), + }, + ]; + }); + + io.to(socket.userID).emit("monitorTypeList", Object.fromEntries(result)); +} + +module.exports = { + sendNotificationList, + sendImportantHeartbeatList, + sendHeartbeatList, + sendProxyList, + sendAPIKeyList, + sendInfo, + sendDockerHostList, + sendRemoteBrowserList, + sendMonitorTypeList, +}; diff --git a/server/config.js b/server/config.js new file mode 100644 index 0000000..72eb582 --- /dev/null +++ b/server/config.js @@ -0,0 +1,50 @@ +const isFreeBSD = /^freebsd/.test(process.platform); + +// Interop with browser +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 (::) +// Also read HOST if not FreeBSD, as HOST is a system environment variable in FreeBSD +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 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 isSSL = sslKey && sslCert; + +/** + * Get the local WebSocket URL + * @returns {string} The local WebSocket URL + */ +function getLocalWebSocketURL() { + const protocol = isSSL ? "wss" : "ws"; + const host = hostname || "localhost"; + return `${protocol}://${host}:${port}`; +} + +const localWebSocketURL = getLocalWebSocketURL(); + +const demoMode = args["demo"] || false; + +module.exports = { + args, + hostname, + port, + sslKey, + sslCert, + sslKeyPassphrase, + isSSL, + localWebSocketURL, + demoMode, +}; diff --git a/server/database.js b/server/database.js new file mode 100644 index 0000000..1dc0664 --- /dev/null +++ b/server/database.js @@ -0,0 +1,1077 @@ +const fs = require("fs"); +const fsAsync = fs.promises; +const { R } = require("redbean-node"); +const { log, sleep, isDevEnv } = require("../src/util"); +const knex = require("knex"); +const path = require("path"); +const { EmbeddedMariaDB } = require("./embedded-mariadb"); +const mysql = require("mysql2/promise"); +const { Pool } = mysql; +const { Settings } = require("./settings"); +const { UptimeCalculator } = require("./uptime-calculator"); +const dayjs = require("dayjs"); +const { SimpleMigrationServer } = require("./utils/simple-migration-server"); +const BetterSqlite3Database = require("better-sqlite3"); +const KumaColumnCompiler = require("./utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler"); +const SqlString = require("sqlstring"); +const { auth } = require("./better-auth"); + +/** + * Database & App Data Folder + */ +class Database { + /** + * Bootstrap database for SQLite + * @type {string} + */ + static templatePath = "./db/kuma.db"; + + /** + * Data Dir (Default: ./data) + * @type {string} + */ + static dataDir; + + /** + * User Upload Dir (Default: ./data/upload) + * @type {string} + */ + static uploadDir; + + /** + * Chrome Screenshot Dir (Default: ./data/screenshots) + * @type {string} + */ + static screenshotDir; + + /** + * SQLite file path (Default: ./data/kuma.db) + * @type {string} + */ + static sqlitePath; + + /** + * For storing Docker TLS certs (Default: ./data/docker-tls) + * @type {string} + */ + static dockerTLSDir; + + /** + * @type {boolean} + */ + static patched = false; + + /** + * @type {BetterSqlite3Database.Database} + */ + static authSQLite = null; + + /** + * @type {Pool} + */ + static authMariaDB = null; + + /** + * SQLite only + * Add patch filename in key + * Values: + * true: Add it regardless of order + * false: Do nothing + * { parents: []}: Need parents before add it + * @deprecated + */ + static patchList = { + "patch-setting-value-type.sql": true, + "patch-improve-performance.sql": true, + "patch-2fa.sql": true, + "patch-add-retry-interval-monitor.sql": true, + "patch-incident-table.sql": true, + "patch-group-table.sql": true, + "patch-monitor-push_token.sql": true, + "patch-http-monitor-method-body-and-headers.sql": true, + "patch-2fa-invalidate-used-token.sql": true, + "patch-notification_sent_history.sql": true, + "patch-monitor-basic-auth.sql": true, + "patch-add-docker-columns.sql": true, + "patch-status-page.sql": true, + "patch-proxy.sql": true, + "patch-monitor-expiry-notification.sql": true, + "patch-status-page-footer-css.sql": true, + "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-grpc-monitor.sql": true, + "patch-add-radius-monitor.sql": true, + "patch-monitor-add-resend-interval.sql": true, + "patch-ping-packet-size.sql": true, + "patch-maintenance-table2.sql": true, + "patch-add-gamedig-monitor.sql": true, + "patch-add-google-analytics-status-page-tag.sql": true, + "patch-http-body-encoding.sql": true, + "patch-add-description-monitor.sql": true, + "patch-api-key-table.sql": true, + "patch-monitor-tls.sql": true, + "patch-maintenance-cron.sql": true, + "patch-add-parent-monitor.sql": true, + "patch-add-invert-keyword.sql": true, + "patch-added-json-query.sql": true, + "patch-added-kafka-producer.sql": true, + "patch-add-certificate-expiry-status-page.sql": true, + "patch-monitor-oauth-cc.sql": true, + "patch-add-timeout-monitor.sql": true, + "patch-add-gamedig-given-port.sql": true, + "patch-notification-config.sql": true, + "patch-fix-kafka-producer-booleans.sql": true, + "patch-timeout.sql": true, + "patch-monitor-tls-info-add-fk.sql": true, // The last file so far converted to a knex migration file + }; + + /** + * The final version should be 10 after merged tag feature + * @deprecated Use patchList for any new feature + */ + static latestVersion = 10; + + static noReject = true; + + /** + * @type {Record} + */ + static dbConfig = {}; + + static knexMigrationsPath = "./db/knex_migrations"; + + /** + * Initialize the data directory + * @param {object} args Arguments to initialize DB with + * @returns {void} + */ + static initDataDir(args) { + // Data Directory (must be end with "/") + Database.dataDir = process.env.DATA_DIR || args["data-dir"] || Database.getDevDataDir() || "./data/"; + + Database.sqlitePath = path.join(Database.dataDir, "kuma.db"); + if (!fs.existsSync(Database.dataDir)) { + fs.mkdirSync(Database.dataDir, { recursive: true }); + } + + Database.uploadDir = path.join(Database.dataDir, "upload/"); + + 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)) { + fs.mkdirSync(Database.screenshotDir, { recursive: true }); + } + + Database.dockerTLSDir = path.join(Database.dataDir, "docker-tls/"); + if (!fs.existsSync(Database.dockerTLSDir)) { + fs.mkdirSync(Database.dockerTLSDir, { recursive: true }); + } + + log.info("server", `Data Dir: ${Database.dataDir}`); + } + + /** + * Development + non-master branch + no custom only + * To avoid database migration issue during different pull request testing. + * Path: ./data/dev-data// + * @returns {string} The dev data dir, empty string if not in dev mode or in master branch + */ + static getDevDataDir() { + if (isDevEnv()) { + const gitBranch = this.getCurrentGitBranch(); + + // HEAD means detached head. Don't handle this case, becasuse it is not common. + if (gitBranch !== "" && gitBranch !== "master" && gitBranch !== "HEAD") { + log.info("server", `Using development data directory for branch ${gitBranch}`); + return path.join("./data/dev-data/", gitBranch, "/"); + } else { + log.debug("server", "Do not use development data directory because it is master branch"); + } + } + return ""; + } + + /** + * Get the current git branch name + * @returns {string} The current git branch name, or empty string if it cannot be determined + */ + static getCurrentGitBranch() { + try { + const { execSync } = require("child_process"); + // Reference: https://stackoverflow.com/questions/6245570/how-do-i-get-the-current-branch-name-in-git + return execSync("git rev-parse --abbrev-ref HEAD").toString().trim(); + } catch (e) { + return ""; + } + } + + /** + * Read the database config + * @throws {Error} If the config is invalid + * @typedef {string|undefined} envString + * @returns {{type: "sqlite"} | {type:envString, hostname:envString, port:envString, database:envString, username:envString, password:envString, socketPath:envString}} Database config + */ + static readDBConfig() { + let dbConfig; + + let dbConfigString = fs.readFileSync(path.join(Database.dataDir, "db-config.json")).toString("utf-8"); + dbConfig = JSON.parse(dbConfigString); + + if (typeof dbConfig !== "object") { + throw new Error("Invalid db-config.json, it must be an object"); + } + + if (typeof dbConfig.type !== "string") { + throw new Error("Invalid db-config.json, type must be a string"); + } + return dbConfig; + } + + /** + * @typedef {string|undefined} envString + * @param {Record} dbConfig the database configuration that should be written + * @returns {void} + */ + static writeDBConfig(dbConfig) { + fs.writeFileSync(path.join(Database.dataDir, "db-config.json"), JSON.stringify(dbConfig, null, 4)); + } + + /** + * Connect to the database + * @param {boolean} testMode Should the connection be started in test mode? + * @param {boolean} autoloadModels Should models be automatically loaded? + * @param {boolean} noLog Should logs not be output? + * @returns {Promise} + */ + static async connect(testMode = false, autoloadModels = true, noLog = false) { + // Patch "mysql2" knex client + // Workaround: Tried extending the ColumnCompiler class, but it didn't work for unknown reasons, so I override the function via prototype + const { getDialectByNameOrAlias } = require("knex/lib/dialects"); + const mysql2 = getDialectByNameOrAlias("mysql2"); + mysql2.prototype.columnCompiler = function () { + return new KumaColumnCompiler(this, ...arguments); + }; + + const acquireConnectionTimeout = 120 * 1000; + let dbConfig; + try { + dbConfig = this.readDBConfig(); + Database.dbConfig = dbConfig; + } catch (err) { + log.warn("db", err.message); + dbConfig = { + type: "sqlite", + }; + } + + let config = {}; + + let parsedMaxPoolConnections = parseInt(process.env.UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS); + + 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." + ); + 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." + ); + 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." + ); + parsedMaxPoolConnections = 100; + } + + let mariadbPoolConfig = { + min: 0, + max: parsedMaxPoolConnections, + idleTimeoutMillis: 30000, + afterCreate(conn, done) { + // Set to utf8mb4 for MariaDB + conn.query("SET CHARACTER SET utf8mb4;", (err) => done(err, conn)); + }, + }; + + log.info("db", `Database Type: ${dbConfig.type}`); + + if (dbConfig.type === "sqlite") { + if (!fs.existsSync(Database.sqlitePath)) { + log.info("server", "Copying Database"); + fs.copyFileSync(Database.templatePath, Database.sqlitePath); + } + + // SQLite is actually multiple connections for WAL mode, so we can set it to a higher number. + // See: https://github.com/knex/knex/issues/3176#issuecomment-3389054899 + let poolConfig = { + min: 0, + max: 20, + }; + + // Default is still single connection. + // Multiple connection could run into "SQLITE_BUSY: database is locked" error. + if (process.env.UPTIME_KUMA_SQLITE_SINGLE_CONNECTION !== "false") { + log.info("db", "Using single connection for SQLite"); + poolConfig = { + min: 1, + max: 1, + }; + } + + config = { + client: "better-sqlite3", + connection: { + filename: Database.sqlitePath, + acquireConnectionTimeout: acquireConnectionTimeout, + }, + useNullAsDefault: true, + pool: { + ...poolConfig, + acquireTimeoutMillis: acquireConnectionTimeout, + afterCreate: (rawConn, done) => { + try { + this.initSQLite(rawConn, testMode); + done(undefined, rawConn); + } catch (err) { + done(err, rawConn); + } + }, + }, + }; + } else if (dbConfig.type === "mariadb") { + const connection = await mysql.createConnection({ + host: dbConfig.hostname, + port: dbConfig.port, + user: dbConfig.username, + password: dbConfig.password, + socketPath: dbConfig.socketPath, + ...(dbConfig.ssl + ? { + ssl: { + rejectUnauthorized: true, + ...(dbConfig.ca && dbConfig.ca.trim() !== "" ? { ca: [dbConfig.ca] } : {}), + }, + } + : {}), + }); + + // Set to true, so for example "uptime.kuma", becomes `uptime.kuma`, not `uptime`.`kuma` + // Doc: https://github.com/mysqljs/sqlstring?tab=readme-ov-file#escaping-query-identifiers + const escapedDBName = SqlString.escapeId(dbConfig.dbName, true); + + await connection.execute("CREATE DATABASE IF NOT EXISTS " + escapedDBName + " CHARACTER SET utf8mb4"); + connection.end(); + + config = { + client: "mysql2", + connection: { + host: dbConfig.hostname, + port: dbConfig.port, + user: dbConfig.username, + password: dbConfig.password, + database: dbConfig.dbName, + socketPath: dbConfig.socketPath, + timezone: "Z", + typeCast: function (field, next) { + if (field.type === "DATETIME") { + // Do not perform timezone conversion + return field.string(); + } + return next(); + }, + ...(dbConfig.ssl + ? { + ssl: { + rejectUnauthorized: true, + ...(dbConfig.ca && dbConfig.ca.trim() !== "" ? { ca: [dbConfig.ca] } : {}), + }, + } + : {}), + }, + pool: mariadbPoolConfig, + }; + } else if (dbConfig.type === "embedded-mariadb") { + let embeddedMariaDB = EmbeddedMariaDB.getInstance(); + await embeddedMariaDB.start(); + log.info("mariadb", "Embedded MariaDB started"); + config = { + client: "mysql2", + connection: { + socketPath: embeddedMariaDB.socketPath, + user: embeddedMariaDB.username, + database: "kuma", + timezone: "Z", + typeCast: function (field, next) { + if (field.type === "DATETIME") { + // Do not perform timezone conversion + return field.string(); + } + return next(); + }, + }, + pool: mariadbPoolConfig, + }; + } else { + throw new Error("Unknown Database type: " + dbConfig.type); + } + + const knexInstance = knex(config); + + R.setup(knexInstance); + + if (process.env.SQL_LOG === "1") { + R.debug(true); + } + + // Auto map the model to a bean object + R.freeze(true); + + if (autoloadModels) { + await R.autoloadModels("./server/model"); + } + + if (dbConfig.type === "sqlite") { + if (!noLog) { + 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()"))); + } + } else if (dbConfig.type.endsWith("mariadb")) { + await this.initMariaDB(); + } + + // Also connect better-auth + auth(true); + } + + /** + * Initialize SQLite for each connection + * @param {import("better-sqlite3").Database} rawConn The raw better-sqlite3 Database object + * @param {boolean} testMode Should the connection be started in test mode? + */ + static initSQLite(rawConn, testMode) { + // Required for legacy SQL patch files that use unsafe PRAGMAs (e.g. PRAGMA writable_schema) + rawConn.unsafeMode(true); + + if (testMode) { + // Change to MEMORY + rawConn.exec("PRAGMA journal_mode = MEMORY"); + } else { + // Change to WAL + rawConn.exec("PRAGMA journal_mode = WAL"); + } + + rawConn.exec("PRAGMA foreign_keys = ON"); + rawConn.exec("PRAGMA cache_size = -12000"); + rawConn.exec("PRAGMA auto_vacuum = INCREMENTAL"); + + // Avoid error "SQLITE_BUSY: database is locked" by allowing SQLITE to wait up to 5 seconds to do a write + rawConn.exec("PRAGMA busy_timeout = 5000"); + + // This ensures that an operating system crash or power failure will not corrupt the database. + // FULL synchronous is very safe, but it is also slower. + // Read more: https://sqlite.org/pragma.html#pragma_synchronous + rawConn.exec("PRAGMA synchronous = NORMAL"); + } + + /** + * Initialize MariaDB + * @returns {Promise} + */ + static async initMariaDB() { + log.debug("db", "Checking if MariaDB database exists..."); + + let hasTable = await R.hasTable("docker_host"); + if (!hasTable) { + const { createTables } = require("../db/knex_init_db"); + await createTables(); + } else { + log.debug("db", "MariaDB database already exists"); + } + } + + /** + * Patch the database + * @param {number} port Start the migration server for aggregate tables on this port if provided + * @param {string} hostname Start the migration server for aggregate tables on this hostname if provided + * @returns {Promise} + */ + static async patch(port = undefined, hostname = undefined) { + // Still need to keep this for old versions of Uptime Kuma + if (Database.dbConfig.type === "sqlite") { + await this.patchSqlite(); + } + + // Using knex migrations + // https://knexjs.org/guide/migrations.html + // https://gist.github.com/NigelEarle/70db130cc040cc2868555b29a0278261 + try { + // Disable foreign key check for SQLite + // Known issue of knex: https://github.com/drizzle-team/drizzle-orm/issues/1813 + if (Database.dbConfig.type === "sqlite") { + await R.exec("PRAGMA foreign_keys = OFF"); + } + + await R.knex.migrate.latest({ + directory: Database.knexMigrationsPath, + }); + + // Enable foreign key check for SQLite + if (Database.dbConfig.type === "sqlite") { + await R.exec("PRAGMA foreign_keys = ON"); + } + + 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:")) { + log.warn("db", e.message); + log.warn("db", "Database migration failed, you may be downgrading Uptime Kuma."); + } else { + log.error("db", "Database migration failed"); + throw e; + } + } + } + + /** + * Create a database connection for better-auth, because better-auth doesn't use knex, so it needs a separate connection. + * @param {object} dbConfig The database configuration + * @returns {Pool|BetterSqlite3Database.Database} The database connection + */ + static createAuthDatabase(dbConfig) { + let database; + if (dbConfig.type.includes("mariadb")) { + database = mysql.createPool({ + host: dbConfig.hostname, + port: Number(dbConfig.port), + database: dbConfig.dbName, + user: dbConfig.username, + password: dbConfig.password, + timezone: "Z", + ...(dbConfig.socketPath ? { socketPath: dbConfig.socketPath } : {}), + }); + Database.authMariaDB = database; + } else if (dbConfig.type === "embedded-mariadb") { + let embeddedMariaDB = EmbeddedMariaDB.getInstance(); + database = mysql.createPool({ + socketPath: embeddedMariaDB.socketPath, + user: embeddedMariaDB.username, + database: "kuma", + timezone: "Z", + }); + Database.authMariaDB = database; + } else { + database = new BetterSqlite3Database(Database.sqlitePath); + Database.initSQLite(database, false); + Database.authSQLite = database; + } + return database; + } + + /** + * TODO + * @returns {Promise} + */ + static async rollbackLatestPatch() {} + + /** + * Patch the database for SQLite + * @returns {Promise} + * @deprecated + */ + static async patchSqlite() { + let version = parseInt(await Settings.get("database_version")); + + if (!version) { + version = 0; + } + + if (version !== this.latestVersion) { + log.info("db", "Your database version: " + version); + log.info("db", "Latest database version: " + this.latestVersion); + } + + if (version === this.latestVersion) { + log.debug("db", "Database patch not needed"); + } else if (version > this.latestVersion) { + log.warn("db", "Warning: Database version is newer than expected"); + } else { + log.info("db", "Database patch is needed"); + + // Try catch anything here + try { + for (let i = version + 1; i <= this.latestVersion; i++) { + const sqlFile = `./db/old_migrations/patch${i}.sql`; + log.info("db", `Patching ${sqlFile}`); + await Database.importSQLFile(sqlFile); + log.info("db", `Patched ${sqlFile}`); + await Settings.set("database_version", i); + } + } 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 a bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues" + ); + + process.exit(1); + } + } + + await this.patchSqlite2(); + await this.migrateNewStatusPage(); + } + + /** + * Patch DB using new process + * Call it from patch() only + * @deprecated + * @private + * @returns {Promise} + */ + static async patchSqlite2() { + log.debug("db", "Database Patch 2.0 Process"); + let databasePatchedFiles = await Settings.get("databasePatchedFiles"); + + if (!databasePatchedFiles) { + databasePatchedFiles = {}; + } + + log.debug("db", "Patched files:"); + log.debug("db", databasePatchedFiles); + + try { + for (let sqlFilename in this.patchList) { + await this.patch2Recursion(sqlFilename, databasePatchedFiles); + } + + 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" + ); + + process.exit(1); + } + + await Settings.set("databasePatchedFiles", databasePatchedFiles); + } + + /** + * SQlite only + * Migrate status page value in setting to "status_page" table + * @returns {Promise} + */ + static async migrateNewStatusPage() { + // Fix 1.13.0 empty slug bug + await R.exec("UPDATE status_page SET slug = 'empty-slug-recover' WHERE TRIM(slug) = ''"); + + let title = await Settings.get("title"); + + if (title) { + log.info("database", "Migrating Status Page"); + + let statusPageCheck = await R.findOne("status_page", " slug = 'default' "); + + if (statusPageCheck !== null) { + log.info("database", "Migrating Status Page - Skip, default slug record is already existing"); + return; + } + + let statusPage = R.dispense("status_page"); + statusPage.slug = "default"; + statusPage.title = title; + statusPage.description = await Settings.get("description"); + statusPage.icon = await Settings.get("icon"); + statusPage.theme = await Settings.get("statusPageTheme"); + statusPage.published = !!(await Settings.get("statusPagePublished")); + statusPage.search_engine_index = !!(await Settings.get("searchEngineIndex")); + statusPage.show_tags = !!(await Settings.get("statusPageTags")); + statusPage.password = null; + + if (!statusPage.title) { + statusPage.title = "My Status Page"; + } + + if (!statusPage.icon) { + statusPage.icon = ""; + } + + if (!statusPage.theme) { + statusPage.theme = "light"; + } + + 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 [group] SET status_page_id = ? WHERE status_page_id IS NULL", [id]); + + await R.exec("DELETE FROM setting WHERE type = 'statusPage'"); + + // Migrate Entry Page if it is status page + let entryPage = await Settings.get("entryPage"); + + if (entryPage === "statusPage") { + await Settings.set("entryPage", "statusPage-default", "general"); + } + + log.info("database", "Migrating Status Page - Done"); + } + } + + /** + * Patch database using new patching process + * Used it patch2() only + * @private + * @param {string} sqlFilename Name of SQL file to load + * @param {object} databasePatchedFiles Patch status of database files + * @returns {Promise} + */ + static async patch2Recursion(sqlFilename, databasePatchedFiles) { + let value = this.patchList[sqlFilename]; + + if (!value) { + log.info("db", sqlFilename + " skip"); + return; + } + + // Check if patched + if (!databasePatchedFiles[sqlFilename]) { + log.info("db", sqlFilename + " is not patched"); + + if (value.parents) { + log.info("db", sqlFilename + " need parents"); + for (let parentSQLFilename of value.parents) { + await this.patch2Recursion(parentSQLFilename, databasePatchedFiles); + } + } + + log.info("db", sqlFilename + " is patching"); + this.patched = true; + 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"); + } + } + + /** + * Load an SQL file and execute it + * @param {string} filename Filename of SQL file to import + * @returns {Promise} + */ + static async importSQLFile(filename) { + // Sadly, multi sql statements is not supported by many sqlite libraries, I have to implement it myself + await R.getCell("SELECT 1"); + + let text = fs.readFileSync(filename).toString(); + + // Remove all comments (--) + let lines = text.split("\n"); + lines = lines.filter((line) => { + return !line.startsWith("--"); + }); + + // Split statements by semicolon + // Filter out empty line + text = lines.join("\n"); + + let statements = text + .split(";") + .map((statement) => { + return statement.trim(); + }) + .filter((statement) => { + return statement !== ""; + }); + + for (let statement of statements) { + await R.exec(statement); + } + } + + /** + * Close Database + * Warning: It is used for shutdown the application. After closed database, it cannot be connected again! Because Better-auth would not reconnect the database. + * Special handle, because tarn.js throw a promise reject that cannot be caught + * @returns {Promise} + */ + static async close() { + const listener = (reason, p) => { + Database.noReject = false; + }; + process.addListener("unhandledRejection", listener); + + log.info("db", "Closing the database"); + + // Flush WAL to main database + if (Database.dbConfig.type === "sqlite") { + await R.exec("PRAGMA wal_checkpoint(TRUNCATE)"); + } + + while (true) { + Database.noReject = true; + + await R.close(); + + // Also close auth db + if (Database.authMariaDB) { + await Database.authMariaDB.end(); + Database.authMariaDB = null; + } + if (Database.authSQLite) { + Database.authSQLite.close(); + Database.authSQLite = null; + } + + await sleep(2000); + + if (Database.noReject) { + break; + } else { + log.info("db", "Waiting to close the database"); + } + } + log.info("db", "Database closed"); + + process.removeListener("unhandledRejection", listener); + } + + /** + * Get the size of the database (SQLite only) + * @returns {Promise} Size of database + */ + static async getSize() { + if (Database.dbConfig.type === "sqlite") { + log.debug("db", "Database.getSize()"); + let stats = await fsAsync.stat(Database.sqlitePath); + log.debug("db", stats); + return stats.size; + } + return 0; + } + + /** + * Shrink the database + * @returns {Promise} + */ + static async shrink() { + if (Database.dbConfig.type === "sqlite") { + await R.exec("VACUUM"); + } + } + + /** + * @returns {string} Get the SQL for the current time plus a number of hours + */ + static sqlHourOffset() { + if (Database.dbConfig.type === "sqlite") { + return "DATETIME('now', ? || ' hours')"; + } else { + return "DATE_ADD(UTC_TIMESTAMP(), INTERVAL ? HOUR)"; + } + } + + /** + * Migrate the old data in the heartbeat table to the new format (stat_daily, stat_hourly, stat_minutely) + * It should be run once while upgrading V1 to V2 + * + * Normally, it should be in transaction, but UptimeCalculator wasn't designed to be in transaction before that. + * I don't want to heavily modify the UptimeCalculator, so it is not in transaction. + * Run `npm run reset-migrate-aggregate-table-state` to reset, in case the migration is interrupted. + * @param {number} port Start the migration server on this port if provided + * @param {string} hostname Start the migration server on this hostname if provided + * @returns {Promise} + */ + static async migrateAggregateTable(port, hostname = undefined) { + log.debug("db", "Enter Migrate Aggregate Table function"); + + // 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)" + ); + await Settings.set("migrateAggregateTableState", "migrated"); + } + + let migrateState = await Settings.get("migrateAggregateTableState"); + + // Skip if already migrated + // If it is migrating, it possibly means the migration was interrupted, or the migration is in progress + if (migrateState === "migrated") { + log.debug("db", "Migrated aggregate table already, skip"); + return; + } else if (migrateState === "migrating") { + log.warn("db", "Aggregate table migration is already in progress, or it was interrupted"); + throw new Error("Aggregate table migration is already in progress"); + } + + /** + * Start migration server for displaying the migration status + * @type {SimpleMigrationServer} + */ + let migrationServer; + let msg; + + if (port) { + migrationServer = new SimpleMigrationServer(); + await migrationServer.start(port, hostname); + } + + log.info("db", "Migrating Aggregate Table"); + + log.info("db", "Getting list of unique monitors"); + + // Get a list of unique monitors from the heartbeat table, using raw sql + let monitors = await R.getAll(` + SELECT DISTINCT monitor_id + FROM heartbeat + ORDER BY monitor_id ASC + `); + + // Stop if stat_* tables are not empty + 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?)` + ); + await migrationServer?.stop(); + return; + } + } + + await Settings.set("migrateAggregateTableState", "migrating"); + + let progressPercent = 0; + 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( + ` + SELECT DISTINCT DATE(time) AS date + FROM heartbeat + WHERE monitor_id = ? + ORDER BY date ASC + `, + [monitor.monitor_id] + ); + + 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( + ` + SELECT status, ping, time + FROM heartbeat + WHERE monitor_id = ? + AND DATE(time) = ? + ORDER BY time ASC + `, + [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)}%`; + log.info("db", msg); + migrationServer?.update(msg); + } + + for (let heartbeat of heartbeats) { + await calculator.update(heartbeat.status, parseFloat(heartbeat.ping), dayjs(heartbeat.time)); + } + + // Calculate progress: (current_monitor_index + relative_date_progress) / total_monitors + 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) { + progressPercent = 100; + } + } + } + + msg = "Clearing non-important heartbeats"; + log.info("db", msg); + migrationServer?.update(msg); + + await Database.clearHeartbeatData(true); + await Settings.set("migrateAggregateTableState", "migrated"); + await migrationServer?.stop(); + + if (monitors.length > 0) { + log.info("db", "Aggregate Table Migration Completed"); + } else { + log.info("db", "No data to migrate"); + } + } + + /** + * Remove all non-important heartbeats from heartbeat table, keep last 24-hour or {KEEP_LAST_ROWS} rows for each monitor + * @param {boolean} detailedLog Log detailed information + * @returns {Promise} + */ + static async clearHeartbeatData(detailedLog = false) { + let monitors = await R.getAll("SELECT id FROM monitor"); + const sqlHourOffset = Database.sqlHourOffset(); + + for (let monitor of monitors) { + if (detailedLog) { + log.info("db", "Deleting non-important heartbeats for monitor " + monitor.id); + } + await R.exec( + ` + DELETE FROM heartbeat + WHERE monitor_id = ? + AND important = 0 + AND time < ${sqlHourOffset} + AND id NOT IN ( + SELECT id FROM ( -- written this way for Maria's support + SELECT id + FROM heartbeat + WHERE monitor_id = ? + ORDER BY time DESC + LIMIT ? + ) AS limited_ids + ) + `, + [monitor.id, -24, monitor.id, 100] + ); + } + } +} + +module.exports = Database; diff --git a/server/docker.js b/server/docker.js new file mode 100644 index 0000000..f38fec1 --- /dev/null +++ b/server/docker.js @@ -0,0 +1,179 @@ +const axios = require("axios"); +const { R } = require("redbean-node"); +const https = require("https"); +const fsAsync = require("fs").promises; +const path = require("path"); +const Database = require("./database"); +const { axiosAbortSignal, fsExists } = require("./util-server"); + +class DockerHost { + static CertificateFileNameCA = "ca.pem"; + static CertificateFileNameCert = "cert.pem"; + static CertificateFileNameKey = "key.pem"; + + /** + * Save a docker host + * @param {object} dockerHost Docker host to save + * @param {?number} dockerHostID ID of the docker host to update + * @param {number} userID ID of the user who adds the docker host + * @returns {Promise} Updated docker host + */ + static async save(dockerHost, dockerHostID, userID) { + let bean; + + if (dockerHostID) { + bean = await R.findOne("docker_host", " id = ? ", [dockerHostID]); + + if (!bean) { + throw new Error("docker host not found"); + } + } else { + bean = R.dispense("docker_host"); + } + + bean.user_id = userID; + bean.docker_daemon = dockerHost.dockerDaemon; + bean.docker_type = dockerHost.dockerType; + bean.name = dockerHost.name; + + await R.store(bean); + + return bean; + } + + /** + * Delete a Docker host + * @param {number} dockerHostID ID of the Docker host to delete + * @returns {Promise} + */ + static async delete(dockerHostID) { + let bean = await R.findOne("docker_host", " id = ? ", [dockerHostID]); + + 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.trash(bean); + } + + /** + * Fetches the amount of containers on the Docker host + * @param {object} dockerHost Docker host to check for + * @returns {Promise} Total amount of containers on the host + */ + static async testDockerHost(dockerHost) { + const options = { + url: "/containers/json?all=true", + timeout: 5000, + headers: { + Accept: "*/*", + }, + signal: axiosAbortSignal(6000), + }; + + if (dockerHost.dockerType === "socket") { + 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) + ); + } + + 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?"); + } + } catch (e) { + if (e.code === "ECONNABORTED" || e.name === "CanceledError") { + throw new Error("Connection to Docker daemon timed out."); + } else { + throw e; + } + } + } + + /** + * Since axios 0.27.X, it does not accept `tcp://` protocol. + * Change it to `http://` on the fly in order to fix it. (https://github.com/louislam/uptime-kuma/issues/2165) + * @param {any} url URL to fix + * @returns {any} URL with tcp:// replaced by http:// + */ + static patchDockerURL(url) { + if (typeof url === "string") { + // Replace the first occurrence only with g + return url.replace(/tcp:\/\//g, "http://"); + } + return url; + } + + /** + * Returns HTTPS agent options with client side TLS parameters if certificate files + * for the given host are available under a predefined directory path. + * + * The base path where certificates are looked for can be set with the + * 'DOCKER_TLS_DIR_PATH' environmental variable or defaults to 'data/docker-tls/'. + * + * If a directory in this path exists with a name matching the FQDN of the docker host + * (e.g. the FQDN of 'https://example.com:2376' is 'example.com' so the directory + * 'data/docker-tls/example.com/' would be searched for certificate files), + * then 'ca.pem', 'key.pem' and 'cert.pem' files are included in the agent options. + * File names can also be overridden via 'DOCKER_TLS_FILE_NAME_(CA|KEY|CERT)'. + * @param {string} dockerType i.e. "tcp" or "socket" + * @param {string} url The docker host URL rewritten to https:// + * @returns {Promise} HTTP agent options + */ + static async getHttpsAgentOptions(dockerType, url) { + let baseOptions = { + maxCachedSessions: 0, + rejectUnauthorized: true, + }; + let certOptions = {}; + + 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)) + ) { + let ca = await fsAsync.readFile(caPath); + let key = await fsAsync.readFile(keyPath); + let cert = await fsAsync.readFile(certPath); + certOptions = { + ca, + key, + cert, + }; + } + + return { + ...baseOptions, + ...certOptions, + }; + } +} + +module.exports = { + DockerHost, +}; diff --git a/server/embedded-mariadb.js b/server/embedded-mariadb.js new file mode 100644 index 0000000..9dd9c08 --- /dev/null +++ b/server/embedded-mariadb.js @@ -0,0 +1,219 @@ +const { log } = require("../src/util"); +const childProcess = require("child_process"); +const fs = require("fs"); +const mysql = require("mysql2"); + +/** + * It is only used inside the docker container + */ +class EmbeddedMariaDB { + static instance = null; + + exec = "mariadbd"; + + mariadbDataDir = "/app/data/mariadb"; + + runDir = "/app/data/run"; + + socketPath = this.runDir + "/mariadb.sock"; + + /** + * The username to connect to the MariaDB + * @type {string} + */ + username = null; + + /** + * @type {ChildProcessWithoutNullStreams} + * @private + */ + childProcess = null; + running = false; + + started = false; + + /** + * @returns {EmbeddedMariaDB} The singleton instance + */ + static getInstance() { + if (!EmbeddedMariaDB.instance) { + EmbeddedMariaDB.instance = new EmbeddedMariaDB(); + } + return EmbeddedMariaDB.instance; + } + + /** + * @returns {boolean} If the singleton instance is created + */ + static hasInstance() { + return !!EmbeddedMariaDB.instance; + } + + /** + * Start the embedded MariaDB + * @throws {Error} If the current user is not "node" or "root" + * @returns {Promise|void} A promise that resolves when the MariaDB is started or void if it is already started + */ + start() { + // 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 + ); + } + + this.initDB(); + + this.startChildProcess(); + + return new Promise((resolve) => { + let interval = setInterval(() => { + if (this.started) { + clearInterval(interval); + resolve(); + } else { + log.info("mariadb", "Waiting for Embedded MariaDB to start..."); + } + }, 1000); + }); + } + + /** + * Start the child process + * @returns {void} + */ + startChildProcess() { + if (this.childProcess) { + log.info("mariadb", "Already started"); + return; + } + + this.running = true; + log.info("mariadb", "Starting Embedded MariaDB"); + this.childProcess = childProcess.spawn(this.exec, [ + "--user=node", + "--datadir=" + this.mariadbDataDir, + `--socket=${this.socketPath}`, + `--pid-file=${this.runDir}/mysqld.pid`, + // Don't add the following option, the mariadb will not report message to the console, which affects initDBAfterStarted() + // "--log-error=" + `${this.mariadbDataDir}/mariadb-error.log`, + ]); + + this.childProcess.on("close", (code) => { + this.running = false; + this.childProcess = null; + this.started = false; + log.info("mariadb", "Stopped Embedded MariaDB: " + code); + + if (code !== 0) { + log.error("mariadb", "Try to restart Embedded MariaDB as it is not stopped by user"); + this.startChildProcess(); + } + }); + + this.childProcess.on("error", (err) => { + if (err.code === "ENOENT") { + log.error("mariadb", `Embedded MariaDB: ${this.exec} is not found`); + } else { + log.error("mariadb", err); + } + }); + + let handler = (data) => { + log.info("mariadb", data.toString("utf-8")); + if (data.toString("utf-8").includes("ready for connections")) { + this.initDBAfterStarted(); + } + }; + + this.childProcess.stdout.on("data", handler); + this.childProcess.stderr.on("data", handler); + } + + /** + * Stop all the child processes + * @returns {void} + */ + stop() { + if (this.childProcess) { + this.childProcess.kill("SIGINT"); + this.childProcess = null; + } + } + + /** + * Install MariaDB if it is not installed and make sure the `runDir` directory exists + * @returns {void} + */ + initDB() { + if (!fs.existsSync(this.mariadbDataDir)) { + log.info("mariadb", `Embedded MariaDB: ${this.mariadbDataDir} is not found, create one now.`); + fs.mkdirSync(this.mariadbDataDir, { + recursive: true, + }); + + let result = childProcess.spawnSync("mariadb-install-db", [ + "--user=node", + "--auth-root-socket-user=node", + "--datadir=" + this.mariadbDataDir, + "--auth-root-authentication-method=socket", + ]); + + if (result.status !== 0) { + let error = result.stderr.toString("utf-8"); + log.error("mariadb", error); + return; + } else { + log.info("mariadb", "Embedded MariaDB: mysql_install_db done:" + result.stdout.toString("utf-8")); + } + } + + // Check the owner of the mariadb directory, and change it if necessary + let stat = fs.statSync(this.mariadbDataDir); + if (stat.uid !== 1000 || stat.gid !== 1000) { + fs.chownSync(this.mariadbDataDir, 1000, 1000); + } + + // Check the permission of the mariadb directory, and change it if it is not 755 + if (stat.mode !== 0o755) { + fs.chmodSync(this.mariadbDataDir, 0o755); + } + + if (!fs.existsSync(this.runDir)) { + log.info("mariadb", `Embedded MariaDB: ${this.runDir} is not found, create one now.`); + fs.mkdirSync(this.runDir, { + recursive: true, + }); + } + + stat = fs.statSync(this.runDir); + if (stat.uid !== 1000 || stat.gid !== 1000) { + fs.chownSync(this.runDir, 1000, 1000); + } + if (stat.mode !== 0o755) { + fs.chmodSync(this.runDir, 0o755); + } + } + + /** + * Initialise the "kuma" database in mariadb if it does not exist + * @returns {Promise} + */ + async initDBAfterStarted() { + const connection = mysql.createConnection({ + socketPath: this.socketPath, + user: this.username, + }); + + let result = await connection.execute("CREATE DATABASE IF NOT EXISTS `kuma`"); + log.debug("mariadb", "CREATE DATABASE: " + JSON.stringify(result)); + + log.info("mariadb", "Embedded MariaDB is ready for connections"); + this.started = true; + } +} + +module.exports = { + EmbeddedMariaDB, +}; diff --git a/server/image-data-uri.js b/server/image-data-uri.js new file mode 100644 index 0000000..4cedfd9 --- /dev/null +++ b/server/image-data-uri.js @@ -0,0 +1,78 @@ +/* + From https://github.com/DiegoZoracKy/image-data-uri/blob/master/lib/image-data-uri.js + Modified with 0 dependencies + */ +let fs = require("fs"); +const { log } = require("../src/util"); + +let ImageDataURI = (() => { + /** + * Decode the data:image/ URI + * @param {string} dataURI data:image/ URI to decode + * @returns {?object} An object with properties "imageType" and "dataBase64". + * The former is the image type, e.g., "png", and the latter is a base64 + * encoded string of the image's binary data. If it fails to parse, returns + * null instead of an object. + */ + 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/"'); + return null; + } + + let regExMatches = dataURI.match("data:(image/.*);base64,(.*)"); + return { + imageType: regExMatches[1], + dataBase64: regExMatches[2], + dataBuffer: new Buffer(regExMatches[2], "base64"), + }; + } + + /** + * Endcode an image into data:image/ URI + * @param {(Buffer|string)} data Data to encode + * @param {string} mediaType Media type of data + * @returns {(string|null)} A string representing the base64-encoded + * version of the given Buffer object or null if an error occurred. + */ + function encode(data, mediaType) { + if (!data || !mediaType) { + log.error("image-data-uri", "Missing some of the required params: data, mediaType"); + return null; + } + + 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; + } + + /** + * Write data URI to file + * @param {string} dataURI data:image/ URI + * @param {string} filePath Path to write file to + * @returns {Promise} Write file error + */ + function outputFile(dataURI, filePath) { + filePath = filePath || "./"; + return new Promise((resolve, reject) => { + let imageDecoded = decode(dataURI); + + fs.writeFile(filePath, imageDecoded.dataBuffer, (err) => { + if (err) { + return reject("ImageDataURI :: Error :: " + JSON.stringify(err, null, 4)); + } + resolve(filePath); + }); + }); + } + + return { + decode: decode, + encode: encode, + outputFile: outputFile, + }; +})(); + +module.exports = ImageDataURI; diff --git a/server/jobs.js b/server/jobs.js new file mode 100644 index 0000000..d9333fd --- /dev/null +++ b/server/jobs.js @@ -0,0 +1,57 @@ +const { UptimeKumaServer } = require("./uptime-kuma-server"); +const { clearOldData } = require("./jobs/clear-old-data"); +const { incrementalVacuum } = require("./jobs/incremental-vacuum"); +const Cron = require("croner"); + +const jobs = [ + { + name: "clear-old-data", + interval: "14 03 * * *", + jobFunc: clearOldData, + croner: null, + }, + { + name: "incremental-vacuum", + interval: "*/5 * * * *", + jobFunc: incrementalVacuum, + croner: null, + }, +]; + +/** + * Initialize background jobs + * @returns {Promise} + */ +const initBackgroundJobs = async function () { + const timezone = await UptimeKumaServer.getInstance().getTimezone(); + + for (const job of jobs) { + const cornerJob = new Cron( + job.interval, + { + name: job.name, + timezone, + }, + job.jobFunc + ); + job.croner = cornerJob; + } +}; + +/** + * Stop all background jobs if running + * @returns {void} + */ +const stopBackgroundJobs = function () { + for (const job of jobs) { + if (job.croner) { + job.croner.stop(); + job.croner = null; + } + } +}; + +module.exports = { + initBackgroundJobs, + stopBackgroundJobs, +}; diff --git a/server/jobs/clear-old-data.js b/server/jobs/clear-old-data.js new file mode 100644 index 0000000..330a81e --- /dev/null +++ b/server/jobs/clear-old-data.js @@ -0,0 +1,64 @@ +const { R } = require("redbean-node"); +const { log } = require("../../src/util"); +const Database = require("../database"); +const { Settings } = require("../settings"); +const dayjs = require("dayjs"); + +const DEFAULT_KEEP_PERIOD = 365; + +/** + * Clears old data from the heartbeat table and the stat_daily of the database. + * @returns {Promise} A promise that resolves when the data has been cleared. + */ +const clearOldData = async () => { + await Database.clearHeartbeatData(); + let period = await Settings.get("keepDataPeriodDays"); + + // Set Default Period + if (period == null) { + await Settings.set("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general"); + period = DEFAULT_KEEP_PERIOD; + } + + // Try parse setting + let parsedPeriod; + try { + parsedPeriod = parseInt(period); + } catch (_) { + log.warn("clearOldData", "Failed to parse setting, resetting to default.."); + await Settings.set("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general"); + parsedPeriod = DEFAULT_KEEP_PERIOD; + } + + if (parsedPeriod < 1) { + 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]); + + let timestamp = dayjs().subtract(parsedPeriod, "day").utc().startOf("day").unix(); + + // stat_daily + await R.exec("DELETE FROM stat_daily WHERE timestamp < ? ", [timestamp]); + + if (Database.dbConfig.type === "sqlite") { + await R.exec("PRAGMA optimize;"); + } + } catch (e) { + log.error("clearOldData", `Failed to clear old data: ${e.message}`); + } + } + + log.debug("clearOldData", "Data cleared."); +}; + +module.exports = { + clearOldData, +}; diff --git a/server/jobs/incremental-vacuum.js b/server/jobs/incremental-vacuum.js new file mode 100644 index 0000000..f0fa78a --- /dev/null +++ b/server/jobs/incremental-vacuum.js @@ -0,0 +1,27 @@ +const { R } = require("redbean-node"); +const { log } = require("../../src/util"); +const Database = require("../database"); + +/** + * Run incremental_vacuum and checkpoint the WAL. + * @returns {Promise} A promise that resolves when the process is finished. + */ + +const incrementalVacuum = async () => { + try { + if (Database.dbConfig.type !== "sqlite") { + log.debug("incrementalVacuum", "Skipping incremental_vacuum, not using SQLite."); + return; + } + + log.debug("incrementalVacuum", "Running incremental_vacuum and wal_checkpoint(PASSIVE)..."); + await R.exec("PRAGMA incremental_vacuum(200)"); + await R.exec("PRAGMA wal_checkpoint(PASSIVE)"); + } catch (e) { + log.error("incrementalVacuum", `Failed: ${e.message}`); + } +}; + +module.exports = { + incrementalVacuum, +}; diff --git a/server/model/api_key.js b/server/model/api_key.js new file mode 100644 index 0000000..96de984 --- /dev/null +++ b/server/model/api_key.js @@ -0,0 +1,76 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); +const { R } = require("redbean-node"); +const dayjs = require("dayjs"); + +class APIKey extends BeanModel { + /** + * Get the current status of this API key + * @returns {string} active, inactive or expired + */ + getStatus() { + let current = dayjs(); + let expiry = dayjs(this.expires); + if (expiry.diff(current) < 0) { + return "expired"; + } + + return this.active ? "active" : "inactive"; + } + + /** + * Returns an object that ready to parse to JSON + * @returns {object} Object ready to parse + */ + toJSON() { + return { + id: this.id, + key: this.key, + name: this.name, + userID: this.user_id, + createdDate: this.created_date, + active: this.active, + expires: this.expires, + status: this.getStatus(), + }; + } + + /** + * Returns an object that ready to parse to JSON with sensitive fields + * removed + * @returns {object} Object ready to parse + */ + toPublicJSON() { + return { + id: this.id, + name: this.name, + userID: this.user_id, + createdDate: this.created_date, + active: this.active, + expires: this.expires, + status: this.getStatus(), + }; + } + + /** + * Create a new API Key and store it in the database + * @param {object} key Object sent by client + * @param {int} userID ID of socket user + * @returns {Promise} API key + */ + static async save(key, userID) { + let bean; + bean = R.dispense("api_key"); + + bean.key = key.key; + bean.name = key.name; + bean.user_id = userID; + bean.active = key.active; + bean.expires = key.expires; + + await R.store(bean); + + return bean; + } +} + +module.exports = APIKey; diff --git a/server/model/docker_host.js b/server/model/docker_host.js new file mode 100644 index 0000000..ceb8f4a --- /dev/null +++ b/server/model/docker_host.js @@ -0,0 +1,19 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); + +class DockerHost extends BeanModel { + /** + * Returns an object that ready to parse to JSON + * @returns {object} Object ready to parse + */ + toJSON() { + return { + id: this.id, + userID: this.user_id, + dockerDaemon: this.docker_daemon, + dockerType: this.docker_type, + name: this.name, + }; + } +} + +module.exports = DockerHost; diff --git a/server/model/domain_expiry.js b/server/model/domain_expiry.js new file mode 100644 index 0000000..6e4fcf5 --- /dev/null +++ b/server/model/domain_expiry.js @@ -0,0 +1,368 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); +const { R } = require("redbean-node"); +const { log, TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD } = require("../../src/util"); +const { parse: parseTld } = require("tldts"); +const { setting, setSetting } = require("../util-server"); +const { Notification } = require("../notification"); +const TranslatableError = require("../translatable-error"); +const dayjs = require("dayjs"); +const { Settings } = require("../settings"); + +let cacheRdapDnsData = null; +let nextChecking = 0; +let running = false; + +/** + * Find the RDAP server for a given TLD + * @param {string} tld TLD + * @returns {string|null} First RDAP server found + */ +async function getRdapServer(tld) { + const rdapDnsData = await getRdapDnsData(); + const services = rdapDnsData["services"] ?? []; + const rootTld = tld?.split(".").pop(); + if (rootTld) { + for (const [tlds, urls] of services) { + if (tlds.includes(rootTld)) { + return urls[0]; + } + } + } + log.debug("rdap", `No RDAP server found for TLD ${tld}`); + return null; +} + +/** + * Get RDAP DNS data from IANA and save to Setting + * @returns {Promise<{}>} RDAP DNS data + */ +async function getRdapDnsData() { + // Cache for one week + if (cacheRdapDnsData && Date.now() < nextChecking) { + return cacheRdapDnsData; + } + + // Avoid multiple simultaneous updates + // Use older data first if another update is in progress + if (running) { + return await getOfflineRdapDnsData(); + } + + try { + running = true; + log.info("rdap", "Updating RDAP DNS data from IANA..."); + const response = await fetch("https://data.iana.org/rdap/dns.json"); + if (!response.ok) { + throw new Error(`HTTP error: ${response.status}`); + } + + const data = await response.json(); + + // Simple validation + if (!data.services || !Array.isArray(data.services)) { + throw new Error("Invalid RDAP DNS data structure"); + } + + cacheRdapDnsData = data; + + // Next week + nextChecking = Date.now() + 7 * 24 * 60 * 60 * 1000; + await Settings.set("rdapDnsData", data); + log.info("rdap", "RDAP DNS data updated successfully. Number of services: " + data.services.length); + } catch (error) { + log.info("rdap", `Uable to update RDAP DNS data from source: ${error.message}`); + cacheRdapDnsData = await getOfflineRdapDnsData(); + + // Check again next day + nextChecking = Date.now() + 24 * 60 * 60 * 1000; + } + + running = false; + return cacheRdapDnsData; +} + +/** + * Get RDAP DNS data from Setting or hardcoded file as fallback + * Fail safe + * @returns {Promise<{}>} RDAP DNS data + */ +async function getOfflineRdapDnsData() { + let data = null; + try { + data = await Settings.get("rdapDnsData"); + + // Simple validation + if (!data.services || !Array.isArray(data.services)) { + throw new Error("Invalid RDAP DNS data structure"); + } + } catch (e) { + // If not downloaded previously, use the hardcoded data + data = require("../../extra/rdap-dns.json"); + } + return data; +} + +/** + * Request RDAP server to retrieve the expiry date of a domain + * @param {string} domain Domain to retrieve the expiry date from + * @returns {Promise<(Date|null)>} Expiry date from RDAP server + */ +async function getRdapDomainExpiryDate(domain) { + const tld = DomainExpiry.parseTld(domain).publicSuffix; + const rdapServer = await getRdapServer(tld); + if (rdapServer === null) { + log.warn("rdap", `No RDAP server found, TLD ${tld} not supported.`); + return null; + } + const url = `${rdapServer}domain/${domain}`; + + let rdapInfos; + try { + const res = await fetch(url); + if (res.status !== 200) { + return null; + } + rdapInfos = await res.json(); + } catch { + log.warn("rdap", "Not able to get expiry date from RDAP"); + return null; + } + + if (rdapInfos["events"] === undefined) { + return null; + } + for (const event of rdapInfos["events"]) { + if (event["eventAction"] === "expiration") { + return new Date(event["eventDate"]); + } + } + return null; +} + +/** + * Send a certificate notification when domain expires in less than target days + * @param {string} domain Domain we monitor + * @param {number} daysRemaining Number of days remaining on certificate + * @param {number} targetDays Number of days to alert after + * @param {LooseObject[]} notificationList List of notification providers + * @returns {Promise} + */ +async function sendDomainNotificationByTargetDays(domain, daysRemaining, targetDays, notificationList) { + let sent = false; + log.debug("domain_expiry", `Send domain expiry notification for ${targetDays} deadline.`); + + for (let notification of notificationList) { + try { + log.debug("domain_expiry", `Sending to ${notification.name}`); + await Notification.send( + JSON.parse(notification.config), + `Domain name ${domain} will expire in ${daysRemaining} days` + ); + sent = true; + } catch (e) { + log.error("domain_expiry", `Cannot send domain notification to ${notification.name}:`, e); + } + } + + return sent; +} + +class DomainExpiry extends BeanModel { + /** + * @param {string} domain Domain name + * @returns {Promise} Domain bean + */ + static async findByName(domain) { + return R.findOne("domain_expiry", "domain = ?", [domain]); + } + + /** + * @param {string} domain Domain name + * @returns {DomainExpiry} Domain bean + */ + static createByName(domain) { + const d = R.dispense("domain_expiry"); + d.domain = domain; + return d; + } + + static parseTld = parseTld; + + /** + * @typedef {import("tldts-core").IResult} DomainComponents + * @returns {DomainComponents} parsed domain components + */ + parseName() { + return parseTld(this.domain); + } + + /** + * @returns {(null|object)} parsed domain tld + */ + get tld() { + return this.parseName().publicSuffix; + } + + /** + * @param {Monitor} monitor Monitor object + * @throws {TranslatableError} Throws an error if the monitor type is unsupported or missing target. + * @returns {Promise<{ domain: string, tld: string }>} Domain expiry support info + */ + static async checkSupport(monitor) { + if (!(monitor.type in TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD)) { + throw new TranslatableError("domain_expiry_unsupported_monitor_type"); + } + const targetField = TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD[monitor.type]; + const target = monitor[targetField]; + if (typeof target !== "string" || target.length === 0) { + throw new TranslatableError("domain_expiry_unsupported_missing_target"); + } + + const tld = parseTld(target); + + // It must be checked first, filter out non-ICANN domains. + if (!tld.isIcann) { + throw new TranslatableError("domain_expiry_unsupported_is_icann", { + // If domain is null, use hostname as fallback for better error message. + domain: tld.domain ?? tld.hostname ?? "EMPTY DOMAIN", + publicSuffix: tld.publicSuffix, + }); + } + + const publicSuffix = tld.publicSuffix; + const rootTld = publicSuffix.split(".").pop(); + const rdap = await getRdapServer(publicSuffix); + if (!rdap) { + throw new TranslatableError("domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint", { + publicSuffix, + }); + } + + return { + domain: tld.domain, + tld: rootTld, + }; + } + + /** + * @param {string} domainName Domain name + * @returns {Promise} Domain expiry bean + */ + static async findByDomainNameOrCreate(domainName) { + let domain = await DomainExpiry.findByName(domainName); + if (!domain && domainName) { + domain = await DomainExpiry.createByName(domainName); + } + return domain; + } + + /** + * @returns {number} number of days remaining before expiry + */ + get daysRemaining() { + return dayjs.utc(this.expiry).diff(dayjs.utc(), "day"); + } + + /** + * @returns {Promise<(Date|null)>} Expiry date from RDAP + */ + async getExpiryDate() { + return getRdapDomainExpiryDate(this.domain); + } + + /** + * @param {string} domainName Monitor object + * @throws {TranslatableError} If the domain is not supported + * @returns {Promise} the expiry date + */ + static async checkExpiry(domainName) { + let bean = await DomainExpiry.findByDomainNameOrCreate(domainName); + let expiryDate; + + if (bean?.lastCheck && dayjs.utc().diff(dayjs.utc(bean.lastCheck), "day") < 1) { + log.debug("domain_expiry", `Domain expiry already checked recently for ${bean.domain}, won't re-check.`); + return bean.expiry; + } else if (bean) { + expiryDate = await bean.getExpiryDate(); + + if (dayjs.utc(expiryDate).isAfter(dayjs.utc(bean.expiry))) { + bean.lastExpiryNotificationSent = null; + } + + bean.expiry = R.isoDateTimeMillis(expiryDate); + bean.lastCheck = R.isoDateTimeMillis(dayjs.utc()); + await R.store(bean); + } + + if (expiryDate === null) { + return; + } + + return expiryDate; + } + + /** + * @param {string} domainName the domain name to send notifications for + * @param {LooseObject[]} notificationList notification List + * @returns {Promise} + */ + static async sendNotifications(domainName, notificationList) { + const domain = await DomainExpiry.findByDomainNameOrCreate(domainName); + if (!notificationList.length > 0) { + // fail fast. If no notification is set, all the following checks can be skipped. + log.debug("domain_expiry", "No notification, no need to send domain notification"); + return; + } + // 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` + ); + return; + } + + const daysRemaining = domain.daysRemaining; + const lastSent = domain.lastExpiryNotificationSent; + log.debug("domain_expiry", `${domainName} expires in ${daysRemaining} days`); + + let notifyDays = await setting("domainExpiryNotifyDays"); + if (notifyDays == null || !Array.isArray(notifyDays)) { + // Reset Default + 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 + notifyDays.sort((a, b) => a - b); + for (const targetDays of notifyDays) { + if (daysRemaining > targetDays) { + log.debug( + "domain_expiry", + `No need to send domain notification for ${domainName} (${daysRemaining} days valid) on ${targetDays} deadline.` + ); + continue; + } else if (lastSent && lastSent <= targetDays) { + log.debug( + "domain_expiry", + `Notification for ${domainName} on ${targetDays} deadline sent already, no need to send again.` + ); + continue; + } + const sent = await sendDomainNotificationByTargetDays( + domainName, + daysRemaining, + targetDays, + notificationList + ); + if (sent) { + domain.lastExpiryNotificationSent = targetDays; + await R.store(domain); + return targetDays; + } + } + } + } +} + +module.exports = DomainExpiry; diff --git a/server/model/group.js b/server/model/group.js new file mode 100644 index 0000000..5188955 --- /dev/null +++ b/server/model/group.js @@ -0,0 +1,49 @@ +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 + * @param {boolean} showTags Should the JSON include monitor tags + * @param {boolean} certExpiry Should JSON include info about + * certificate expiry? + * @returns {Promise} Object ready to parse + */ + async toPublicJSON(showTags = false, certExpiry = false) { + let monitorBeanList = await this.getMonitorList(); + let monitorList = []; + + for (let bean of monitorBeanList) { + monitorList.push(await bean.toPublicJSON(showTags, certExpiry)); + } + + return { + id: this.id, + name: this.name, + weight: this.weight, + monitorList, + }; + } + + /** + * Get all monitors + * @returns {Promise} List of monitors + */ + async getMonitorList() { + 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] + ) + ); + } +} + +module.exports = Group; diff --git a/server/model/heartbeat.js b/server/model/heartbeat.js new file mode 100644 index 0000000..3b2c75a --- /dev/null +++ b/server/model/heartbeat.js @@ -0,0 +1,84 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); +const zlib = require("node:zlib"); +const { promisify } = require("node:util"); +const brotliDecompress = promisify(zlib.brotliDecompress); + +/** + * status: + * 0 = DOWN + * 1 = UP + * 2 = PENDING + * 3 = MAINTENANCE + */ +class Heartbeat extends BeanModel { + /** + * Return an object that ready to parse to JSON for public + * Only show necessary data to public + * @returns {object} Object ready to parse + */ + toPublicJSON() { + return { + status: this.status, + time: this.time, + msg: "", // Hide for public + ping: this.ping, + }; + } + + /** + * Return an object that ready to parse to JSON + * @returns {object} Object ready to parse + */ + toJSON() { + return { + monitorID: this._monitorId, + status: this._status, + time: this._time, + msg: this._msg, + ping: this._ping, + important: this._important, + duration: this._duration, + retries: this._retries, + response: this._response, + }; + } + + /** + * Return an object that ready to parse to JSON + * @param {{ decodeResponse?: boolean }} opts Options for JSON serialization + * @returns {Promise} Object ready to parse + */ + async toJSONAsync(opts) { + return { + monitorID: this._monitorId, + status: this._status, + time: this._time, + msg: this._msg, + ping: this._ping, + important: this._important, + duration: this._duration, + retries: this._retries, + response: opts?.decodeResponse ? await Heartbeat.decodeResponseValue(this._response) : undefined, + }; + } + + /** + * Decode compressed response payload stored in database. + * @param {string|null} response Encoded response payload. + * @returns {string|null} Decoded response payload. + */ + static async decodeResponseValue(response) { + if (!response) { + return response; + } + + try { + // Offload brotli decode from main event loop to libuv thread pool + return (await brotliDecompress(Buffer.from(response, "base64"))).toString("utf8"); + } catch (error) { + return response; + } + } +} + +module.exports = Heartbeat; diff --git a/server/model/incident.js b/server/model/incident.js new file mode 100644 index 0000000..ecf400f --- /dev/null +++ b/server/model/incident.js @@ -0,0 +1,36 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); +const { R } = require("redbean-node"); +const dayjs = require("dayjs"); + +class Incident extends BeanModel { + /** + * Resolve the incident and mark it as inactive + * @returns {Promise} + */ + async resolve() { + this.active = false; + this.pin = false; + this.last_updated_date = R.isoDateTime(dayjs.utc()); + await R.store(this); + } + + /** + * Return an object that ready to parse to JSON for public + * @returns {object} Object ready to parse + */ + toPublicJSON() { + return { + id: this.id, + style: this.style, + title: this.title, + content: this.content, + pin: !!this.pin, + active: !!this.active, + createdDate: this.created_date, + lastUpdatedDate: this.last_updated_date, + status_page_id: this.status_page_id, + }; + } +} + +module.exports = Incident; diff --git a/server/model/maintenance.js b/server/model/maintenance.js new file mode 100644 index 0000000..a32666d --- /dev/null +++ b/server/model/maintenance.js @@ -0,0 +1,506 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); +const { parseTimeObject, parseTimeFromTimeObject, log, SQL_DATETIME_FORMAT } = require("../../src/util"); +const { R } = require("redbean-node"); +const dayjs = require("dayjs"); +const Cron = require("croner"); +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 ready to parse + */ + async toPublicJSON() { + let dateRange = []; + if (this.start_date) { + dateRange.push(this.start_date); + } else { + dateRange.push(null); + } + + if (this.end_date) { + dateRange.push(this.end_date); + } + + let timeRange = []; + let startTime = parseTimeObject(this.start_time); + timeRange.push(startTime); + let endTime = parseTimeObject(this.end_time); + timeRange.push(endTime); + + let obj = { + id: this.id, + title: this.title, + description: this.description, + strategy: this.strategy, + intervalDay: this.interval_day, + 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) : [], + 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" + timezoneOffset: await this.getTimezoneOffset(), + status: await this.getStatus(), + }; + + if (this.strategy === "manual") { + // Do nothing, no timeslots + } else if (this.strategy === "single") { + obj.timeslotList.push({ + startDate: this.start_date, + endDate: this.end_date, + }); + } else { + // Should be cron or recurring here + if (this.beanMeta.job) { + let runningTimeslot = this.getRunningTimeslot(); + + if (runningTimeslot) { + obj.timeslotList.push(runningTimeslot); + } + + let nextRunDate = this.beanMeta.job.nextRun(); + if (nextRunDate) { + let startDateDayjs = dayjs(nextRunDate); + + let startDate = startDateDayjs.toISOString(); + let endDate = startDateDayjs.add(this.duration, "second").toISOString(); + + obj.timeslotList.push({ + startDate, + endDate, + }); + } + } + } + + if (!Array.isArray(obj.weekdays)) { + obj.weekdays = []; + } + + if (!Array.isArray(obj.daysOfMonth)) { + obj.daysOfMonth = []; + } + + return obj; + } + + /** + * Return an object that ready to parse to JSON + * @param {string} timezone If not specified, the timeRange will be in UTC + * @returns {Promise} Object ready to parse + */ + async toJSON(timezone = null) { + return this.toPublicJSON(timezone); + } + + /** + * Get a list of weekdays that the maintenance is active for + * Monday=1, Tuesday=2 etc. + * @returns {number[]} Array of active weekdays + */ + getDayOfWeekList() { + log.debug("timeslot", "List: " + this.weekdays); + return JSON.parse(this.weekdays).sort(function (a, b) { + return a - b; + }); + } + + /** + * Get a list of days in month that maintenance is active for + * @returns {number[]|string[]} Array of active days in month + */ + getDayOfMonthList() { + return JSON.parse(this.days_of_month).sort(function (a, b) { + return a - b; + }); + } + + /** + * Get the duration of maintenance in seconds + * @returns {number} Duration of maintenance + */ + calcDuration() { + let duration = dayjs.utc(this.end_time, "HH:mm").diff(dayjs.utc(this.start_time, "HH:mm"), "second"); + // Add 24hours if it is across day + if (duration < 0) { + duration += 24 * 3600; + } + return duration; + } + + /** + * Convert data from socket to bean + * @param {Bean} bean Bean to fill in + * @param {object} obj Data to fill bean with + * @returns {Promise} Filled bean + */ + static async jsonToBean(bean, obj) { + if (obj.id) { + bean.id = obj.id; + } + + bean.title = obj.title; + bean.description = obj.description; + bean.strategy = obj.strategy; + bean.interval_day = obj.intervalDay; + bean.timezone = obj.timezoneOption; + bean.active = obj.active; + + if (obj.dateRange[0]) { + const parsedDate = new Date(obj.dateRange[0]); + if (isNaN(parsedDate.getTime()) || parsedDate.getFullYear() > 9999) { + throw new Error("Invalid start date"); + } + + bean.start_date = obj.dateRange[0]; + } else { + bean.start_date = null; + } + + if (obj.dateRange[1]) { + const parsedDate = new Date(obj.dateRange[1]); + if (isNaN(parsedDate.getTime()) || parsedDate.getFullYear() > 9999) { + throw new Error("Invalid end date"); + } + + bean.end_date = obj.dateRange[1]; + } else { + bean.end_date = null; + } + + if (bean.strategy === "cron") { + bean.duration = obj.durationMinutes * 60; + bean.cron = obj.cron; + this.validateCron(bean.cron); + } + + if (bean.strategy.startsWith("recurring-")) { + bean.start_time = parseTimeFromTimeObject(obj.timeRange[0]); + bean.end_time = parseTimeFromTimeObject(obj.timeRange[1]); + bean.weekdays = JSON.stringify(obj.weekdays); + bean.days_of_month = JSON.stringify(obj.daysOfMonth); + await bean.generateCron(); + this.validateCron(bean.cron); + } + return bean; + } + + /** + * Throw error if cron is invalid + * @param {string|Date} cron Pattern or date + * @returns {void} + */ + static validateCron(cron) { + let job = new Cron(cron, () => {}); + job.stop(); + } + + /** + * Run the cron + * @param {boolean} throwError Should an error be thrown on failure + * @returns {Promise} + */ + async run(throwError = false) { + if (this.beanMeta.job) { + log.debug("maintenance", "Maintenance is already running, stop it first. id: " + this.id); + this.stop(); + } + + log.debug("maintenance", "Run maintenance id: " + this.id); + + // 1.21.2 migration + if (!this.cron) { + await this.generateCron(); + if (!this.timezone) { + this.timezone = "UTC"; + } + if (this.cron) { + await R.store(this); + } + } + + if (this.strategy === "manual") { + // Do nothing, because it is controlled by the user + } else if (this.strategy === "single") { + this.beanMeta.job = new Cron(this.start_date, { timezone: await this.getTimezone() }, () => { + log.info("maintenance", "Maintenance id: " + this.id + " is under maintenance now"); + UptimeKumaServer.getInstance().sendMaintenanceListByUserID(this.user_id); + apicache.clear(); + }); + } else if (this.cron != null) { + let current = dayjs(); + + // Here should be cron or recurring + try { + this.beanMeta.status = "scheduled"; + + let startEvent = async (customDuration = 0) => { + log.info("maintenance", "Maintenance id: " + this.id + " is under maintenance now"); + + this.beanMeta.status = "under-maintenance"; + clearTimeout(this.beanMeta.durationTimeout); + + let duration = this.inferDuration(customDuration); + + UptimeKumaServer.getInstance().sendMaintenanceListByUserID(this.user_id); + + this.beanMeta.durationTimeout = setTimeout(() => { + // End of maintenance for this timeslot + this.beanMeta.status = "scheduled"; + UptimeKumaServer.getInstance().sendMaintenanceListByUserID(this.user_id); + }, duration); + + // Set last start date to current time + this.last_start_date = current.utc().format(SQL_DATETIME_FORMAT); + await R.store(this); + }; + + // Create Cron + 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 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() + let startAt = undefined; + try { + startAt = startDateTime.toISOString(); + } catch (_) {} + + 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(); + } + ); + } else { + this.beanMeta.job = new Cron( + this.cron, + { + timezone: await this.getTimezone(), + }, + startEvent + ); + } + + // Continue if the maintenance is still in the window + let runningTimeslot = this.getRunningTimeslot(); + + if (runningTimeslot) { + let duration = dayjs(runningTimeslot.endDate).diff(current, "second") * 1000; + 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); + log.error("maintenance", e); + + if (throwError) { + throw e; + } + } + } else { + log.error("maintenance", "Maintenance id: " + this.id + " has no cron"); + } + } + + /** + * Get timeslots where maintenance is running + * @returns {object|null} Maintenance time slot + */ + getRunningTimeslot() { + let start = dayjs(this.beanMeta.job.nextRun(dayjs().add(-this.duration, "second").toDate())); + let end = start.add(this.duration, "second"); + let current = dayjs(); + + if (current.isAfter(start) && current.isBefore(end)) { + return { + startDate: start.toISOString(), + endDate: end.toISOString(), + }; + } else { + return null; + } + } + + /** + * Calculate the maintenance duration + * @param {number} customDuration - The custom duration in milliseconds. + * @returns {number} The inferred duration in milliseconds. + */ + inferDuration(customDuration) { + // Check if duration is still in the window. If not, use the duration from the current time to the end of the window + if (customDuration > 0) { + return customDuration; + } else if (this.end_date) { + let d = dayjs(this.end_date).diff(dayjs(), "second"); + if (d < this.duration) { + return d * 1000; + } + } + return this.duration * 1000; + } + + /** + * Stop the maintenance + * @returns {void} + */ + stop() { + if (this.beanMeta.job) { + this.beanMeta.job.stop(); + delete this.beanMeta.job; + } + } + + /** + * Is this maintenance currently active + * @returns {Promise} The maintenance is active? + */ + async isUnderMaintenance() { + return (await this.getStatus()) === "under-maintenance"; + } + + /** + * Get the timezone of the maintenance + * @returns {Promise} timezone + */ + async getTimezone() { + if (!this.timezone || this.timezone === "SAME_AS_SERVER") { + return await UptimeKumaServer.getInstance().getTimezone(); + } + return this.timezone; + } + + /** + * Get offset for timezone + * @returns {Promise} offset + */ + async getTimezoneOffset() { + return dayjs.tz(dayjs(), await this.getTimezone()).format("Z"); + } + + /** + * Get the current status of the maintenance + * @returns {Promise} Current status + */ + async getStatus() { + if (!this.active) { + return "inactive"; + } + + if (this.strategy === "manual") { + return "under-maintenance"; + } + + // Check if the maintenance is started + if (this.start_date && dayjs().isBefore(dayjs.tz(this.start_date, await this.getTimezone()))) { + return "scheduled"; + } + + // Check if the maintenance is ended + if (this.end_date && dayjs().isAfter(dayjs.tz(this.end_date, await this.getTimezone()))) { + return "ended"; + } + + if (this.strategy === "single") { + return "under-maintenance"; + } + + if (!this.beanMeta.status) { + return "unknown"; + } + + return this.beanMeta.status; + } + + /** + * Generate Cron for recurring maintenance + * @returns {Promise} + */ + async generateCron() { + log.info("maintenance", "Generate cron for maintenance id: " + this.id); + + if (this.strategy === "cron") { + // Do nothing for cron + } else if (!this.strategy.startsWith("recurring-")) { + this.cron = ""; + } else if (this.strategy === "recurring-interval") { + // For intervals, the pattern is used to check if the execution should be started + let array = this.start_time.split(":"); + let hour = parseInt(array[0]); + let minute = parseInt(array[1]); + this.cron = `${minute} ${hour} * * *`; + this.duration = this.calcDuration(); + log.debug("maintenance", "Cron: " + this.cron); + log.debug("maintenance", "Duration: " + this.duration); + } else if (this.strategy === "recurring-weekday") { + let list = this.getDayOfWeekList(); + let array = this.start_time.split(":"); + let hour = parseInt(array[0]); + let minute = parseInt(array[1]); + this.cron = minute + " " + hour + " * * " + list.join(","); + this.duration = this.calcDuration(); + } else if (this.strategy === "recurring-day-of-month") { + let list = this.getDayOfMonthList(); + let array = this.start_time.split(":"); + let hour = parseInt(array[0]); + let minute = parseInt(array[1]); + + let dayList = []; + + for (let day of list) { + if (typeof day === "string" && day.startsWith("lastDay")) { + if (day === "lastDay1") { + dayList.push("L"); + } + // Unfortunately, lastDay2-4 is not supported by cron + } else { + dayList.push(day); + } + } + + // Remove duplicate + dayList = [...new Set(dayList)]; + + this.cron = minute + " " + hour + " " + dayList.join(",") + " * *"; + this.duration = this.calcDuration(); + } + } +} + +module.exports = Maintenance; diff --git a/server/model/monitor.js b/server/model/monitor.js new file mode 100644 index 0000000..0850890 --- /dev/null +++ b/server/model/monitor.js @@ -0,0 +1,2086 @@ +const dayjs = require("dayjs"); +const axios = require("axios"); +const { setTimeout, clearTimeout } = require("unlimited-timeout"); +const { Prometheus } = require("../prometheus"); +const { + log, + UP, + DOWN, + PENDING, + MAINTENANCE, + flipStatus, + MIN_INTERVAL_SECOND, + SQL_DATETIME_FORMAT, + evaluateJsonQuery, + PING_PACKET_SIZE_MIN, + PING_PACKET_SIZE_MAX, + PING_PACKET_SIZE_DEFAULT, + PING_GLOBAL_TIMEOUT_MIN, + PING_GLOBAL_TIMEOUT_MAX, + PING_GLOBAL_TIMEOUT_DEFAULT, + PING_COUNT_MIN, + PING_COUNT_MAX, + PING_COUNT_DEFAULT, + PING_PER_REQUEST_TIMEOUT_MIN, + PING_PER_REQUEST_TIMEOUT_MAX, + PING_PER_REQUEST_TIMEOUT_DEFAULT, + RESPONSE_BODY_LENGTH_DEFAULT, + RESPONSE_BODY_LENGTH_MAX, +} = require("../../src/util"); +const { + ping, + checkCertificate, + checkStatusCode, + getTotalClientInRoom, + httpNtlm, + radius, + kafkaProducerAsync, + getOidcTokenClientCredentials, + rootCertificatesFingerprints, + axiosAbortSignal, + checkCertificateHostname, + encodeBase64, + checkCertExpiryNotifications, +} = require("../util-server"); +const { R } = require("redbean-node"); +const { BeanModel } = require("redbean-node/dist/bean-model"); +const { Notification } = require("../notification"); +const { Proxy } = require("../proxy"); +const { demoMode } = require("../config"); +const version = require("../../package.json").version; +const apicache = require("../modules/apicache"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); +const { DockerHost } = require("../docker"); +const crypto = require("crypto"); +const { UptimeCalculator } = require("../uptime-calculator"); +const { CookieJar } = require("tough-cookie"); +const { HttpsCookieAgent } = require("http-cookie-agent/http"); +const https = require("https"); +const http = require("http"); +const zlib = require("node:zlib"); +const { promisify } = require("node:util"); +const brotliCompress = promisify(zlib.brotliCompress); +const DomainExpiry = require("./domain_expiry"); + +const rootCertificates = rootCertificatesFingerprints(); + +/** + * status: + * 0 = DOWN + * 1 = UP + * 2 = PENDING + * 3 = MAINTENANCE + */ +class Monitor extends BeanModel { + /** + * Return an object that ready to parse to JSON for public Only show + * necessary data to public + * @param {boolean} showTags Include tags in JSON + * @param {boolean} certExpiry Include certificate expiry info in + * JSON + * @returns {Promise} Object ready to parse + */ + async toPublicJSON(showTags = false, certExpiry = false) { + let obj = { + id: this.id, + name: this.name, + sendUrl: this.sendUrl, + type: this.type, + }; + + if (this.sendUrl) { + obj.url = this.customUrl ?? this.url; + } + + if (showTags) { + obj.tags = await this.getTags(); + } + + if (certExpiry) { + const { certExpiryDaysRemaining, validCert } = await this.getCertExpiry(this.id); + obj.certExpiryDaysRemaining = certExpiryDaysRemaining; + obj.validCert = validCert; + } + + return obj; + } + + /** + * Return an object that ready to parse to JSON + * @param {object} preloadData to prevent n+1 problems, we query the data in a batch outside of this function + * @param {boolean} includeSensitiveData Include sensitive data in + * JSON + * @returns {object} Object ready to parse + */ + toJSON(preloadData = {}, includeSensitiveData = true) { + let screenshot = null; + + if (this.type === "real-browser") { + screenshot = "/screenshots/" + this.id + ".png"; + } + + const path = preloadData.paths.get(this.id) || []; + const pathName = path.join(" / "); + + let data = { + id: this.id, + name: this.name, + description: this.description, + path, + pathName, + parent: this.parent, + childrenIDs: preloadData.childrenIDs.get(this.id) || [], + url: this.url, + wsIgnoreSecWebsocketAcceptHeader: this.getWsIgnoreSecWebsocketAcceptHeader(), + wsSubprotocol: this.wsSubprotocol, + method: this.method, + hostname: this.hostname, + port: this.port, + location: this.location, + protocol: this.protocol, + maxretries: this.maxretries, + weight: this.weight, + active: preloadData.activeStatus.get(this.id), + forceInactive: preloadData.forceInactive.get(this.id), + type: this.type, + subtype: this.subtype, + timeout: this.timeout, + interval: this.interval, + retryInterval: this.retryInterval, + retryOnlyOnStatusCodeFailure: Boolean(this.retry_only_on_status_code_failure), + resendInterval: this.resendInterval, + keyword: this.keyword, + invertKeyword: this.isInvertKeyword(), + expiryNotification: this.isEnabledExpiryNotification(), + domainExpiryNotification: Boolean(this.domainExpiryNotification), + ignoreTls: this.getIgnoreTls(), + upsideDown: this.isUpsideDown(), + packetSize: this.packetSize, + maxredirects: this.maxredirects, + accepted_statuscodes: this.getAcceptedStatuscodes(), + dns_resolve_type: this.dns_resolve_type, + dns_resolve_server: this.dns_resolve_server, + dns_last_result: this.dns_last_result, + docker_container: this.docker_container, + docker_host: this.docker_host, + proxyId: this.proxy_id, + notificationIDList: preloadData.notifications.get(this.id) || {}, + tags: preloadData.tags.get(this.id) || [], + maintenance: preloadData.maintenanceStatus.get(this.id), + mqttTopic: this.mqttTopic, + mqttSuccessMessage: this.mqttSuccessMessage, + mqttCheckType: this.mqttCheckType, + databaseQuery: this.databaseQuery, + authMethod: this.authMethod, + grpcUrl: this.grpcUrl, + grpcProtobuf: this.grpcProtobuf, + grpcMethod: this.grpcMethod, + grpcServiceName: this.grpcServiceName, + grpcEnableTls: this.getGrpcEnableTls(), + radiusCalledStationId: this.radiusCalledStationId, + radiusCallingStationId: this.radiusCallingStationId, + game: this.game, + gamedigGivenPortOnly: this.getGameDigGivenPortOnly(), + httpBodyEncoding: this.httpBodyEncoding, + jsonPath: this.jsonPath, + expectedValue: this.expectedValue, + system_service_name: this.system_service_name, + kafkaProducerTopic: this.kafkaProducerTopic, + kafkaProducerBrokers: JSON.parse(this.kafkaProducerBrokers), + kafkaProducerSsl: this.getKafkaProducerSsl(), + kafkaProducerAllowAutoTopicCreation: this.getKafkaProducerAllowAutoTopicCreation(), + kafkaProducerMessage: this.kafkaProducerMessage, + screenshot, + cacheBust: this.getCacheBust(), + remote_browser: this.remote_browser, + screenshot_delay: this.screenshot_delay, + snmpOid: this.snmpOid, + jsonPathOperator: this.jsonPathOperator, + snmpVersion: this.snmpVersion, + smtpSecurity: this.smtpSecurity, + rabbitmqNodes: JSON.parse(this.rabbitmqNodes), + conditions: JSON.parse(this.conditions), + ntpStratumThreshold: this.ntp_stratum_threshold, + ntpTimeOffsetThreshold: this.ntp_time_offset_threshold, + ntpRootDispersionThreshold: this.ntp_root_dispersion_threshold, + ipFamily: this.ipFamily, + expectedTlsAlert: this.expected_tls_alert, + sftpPath: this.sftpPath, + sshAuthMethod: this.sshAuthMethod || "password", + + // ping advanced options + ping_numeric: this.isPingNumeric(), + ping_count: this.ping_count, + ping_per_request_timeout: this.ping_per_request_timeout, + + // response saving options + saveResponse: this.getSaveResponse(), + saveErrorResponse: this.getSaveErrorResponse(), + responseMaxLength: this.response_max_length ?? RESPONSE_BODY_LENGTH_DEFAULT, + }; + + if (includeSensitiveData) { + data = { + ...data, + headers: this.headers, + body: this.body, + grpcBody: this.grpcBody, + grpcMetadata: this.grpcMetadata, + basic_auth_user: this.basic_auth_user, + basic_auth_pass: this.basic_auth_pass, + oauth_client_id: this.oauth_client_id, + oauth_client_secret: this.oauth_client_secret, + oauth_token_url: this.oauth_token_url, + oauth_scopes: this.oauth_scopes, + oauth_audience: this.oauth_audience, + oauth_auth_method: this.oauth_auth_method, + bearer_token: this.bearer_token, + gamedigToken: this.gamedigToken, + pushToken: this.pushToken, + databaseConnectionString: this.databaseConnectionString, + radiusUsername: this.radiusUsername, + radiusPassword: this.radiusPassword, + radiusSecret: this.radiusSecret, + mqttUsername: this.mqttUsername, + mqttPassword: this.mqttPassword, + mqttWebsocketPath: this.mqttWebsocketPath, + authWorkstation: this.authWorkstation, + authDomain: this.authDomain, + tlsCa: this.tlsCa, + tlsCert: this.tlsCert, + tlsKey: this.tlsKey, + kafkaProducerSaslOptions: JSON.parse(this.kafkaProducerSaslOptions), + rabbitmqUsername: this.rabbitmqUsername, + rabbitmqPassword: this.rabbitmqPassword, + sshUsername: this.sshUsername, + sshPassword: this.sshPassword, + sshPrivateKey: this.sshPrivateKey, + sshPassphrase: this.sshPassphrase, + }; + } + + data.includeSensitiveData = includeSensitiveData; + return data; + } + + /** + * Get all tags applied to this monitor + * @returns {Promise[]>} List of tags on the + * monitor + */ + async getTags() { + return await R.getAll( + "SELECT mt.*, tag.name, tag.color FROM monitor_tag mt JOIN tag ON mt.tag_id = tag.id WHERE mt.monitor_id = ? ORDER BY tag.name", + [this.id] + ); + } + + /** + * Gets certificate expiry for this monitor + * @param {number} monitorID ID of monitor to send + * @returns {Promise>} Certificate expiry info for + * monitor + */ + async getCertExpiry(monitorID) { + let tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [monitorID]); + let tlsInfo; + if (tlsInfoBean) { + tlsInfo = JSON.parse(tlsInfoBean?.info_json); + if (tlsInfo?.valid && tlsInfo?.certInfo?.daysRemaining) { + return { + certExpiryDaysRemaining: tlsInfo.certInfo.daysRemaining, + validCert: true, + }; + } + } + return { + certExpiryDaysRemaining: "", + validCert: false, + }; + } + + /** + * Is the TLS expiry notification enabled? + * @returns {boolean} Enabled? + */ + isEnabledExpiryNotification() { + return Boolean(this.expiryNotification); + } + + /** + * Check if ping should use numeric output only + * @returns {boolean} True if IP addresses will be output instead of symbolic hostnames + */ + isPingNumeric() { + return Boolean(this.ping_numeric); + } + + /** + * Parse to boolean + * @returns {boolean} Should TLS errors be ignored? + */ + getIgnoreTls() { + return Boolean(this.ignoreTls); + } + + /** + * Parse to boolean + * @returns {boolean} Should WS headers be ignored? + */ + getWsIgnoreSecWebsocketAcceptHeader() { + return Boolean(this.wsIgnoreSecWebsocketAcceptHeader); + } + + /** + * Parse to boolean + * @returns {boolean} Is the monitor in upside down mode? + */ + isUpsideDown() { + return Boolean(this.upsideDown); + } + + /** + * Parse to boolean + * @returns {boolean} Invert keyword match? + */ + isInvertKeyword() { + return Boolean(this.invertKeyword); + } + + /** + * Parse to boolean + * @returns {boolean} Enable TLS for gRPC? + */ + getGrpcEnableTls() { + return Boolean(this.grpcEnableTls); + } + + /** + * Parse to boolean + * @returns {boolean} if cachebusting is enabled + */ + getCacheBust() { + return Boolean(this.cacheBust); + } + + /** + * Get accepted status codes + * @returns {object} Accepted status codes + */ + getAcceptedStatuscodes() { + return JSON.parse(this.accepted_statuscodes_json); + } + + /** + * Get if game dig should only use the port which was provided + * @returns {boolean} gamedig should only use the provided port + */ + getGameDigGivenPortOnly() { + return Boolean(this.gamedigGivenPortOnly); + } + + /** + * Parse to boolean + * @returns {boolean} Kafka Producer Ssl enabled? + */ + getKafkaProducerSsl() { + return Boolean(this.kafkaProducerSsl); + } + + /** + * Parse to boolean + * @returns {boolean} Kafka Producer Allow Auto Topic Creation Enabled? + */ + getKafkaProducerAllowAutoTopicCreation() { + return Boolean(this.kafkaProducerAllowAutoTopicCreation); + } + + /** + * Parse to boolean + * @returns {boolean} Should save response data on success? + */ + getSaveResponse() { + return Boolean(this.save_response); + } + + /** + * Parse to boolean + * @returns {boolean} Should save response data on error? + */ + getSaveErrorResponse() { + return Boolean(this.save_error_response); + } + + /** + * Start monitor + * @param {Server} io Socket server instance + * @returns {Promise} + */ + async start(io) { + let previousBeat = null; + let retries = 0; + + this.rootCertificates = rootCertificates; + + try { + this.prometheus = new Prometheus(this, await this.getTags()); + } catch (e) { + log.error("prometheus", "Please submit an issue to our GitHub repo. Prometheus update error: ", e.message); + } + + const beat = async () => { + let beatInterval = this.interval; + + if (!beatInterval) { + beatInterval = 1; + } + + if (demoMode) { + if (beatInterval < 20) { + console.log("beat interval too low, reset to 20s"); + beatInterval = 20; + } + } + + // Expose here for prometheus update + // undefined if not https + let tlsInfo = undefined; + + if (!previousBeat || this.type === "push") { + previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [this.id]); + if (previousBeat) { + retries = previousBeat.retries; + } + } + + const isFirstBeat = !previousBeat; + + let bean = R.dispense("heartbeat"); + bean.monitor_id = this.id; + bean.time = R.isoDateTimeMillis(dayjs.utc()); + bean.status = DOWN; + bean.downCount = previousBeat?.downCount || 0; + + if (this.isUpsideDown()) { + bean.status = flipStatus(bean.status); + } + + // Runtime patch timeout if it is 0 + // See https://github.com/louislam/uptime-kuma/pull/3961#issuecomment-1804149144 + if (!this.timeout || this.timeout <= 0) { + this.timeout = this.interval * 1000 * 0.8; + } + + try { + if (await Monitor.isUnderMaintenance(this.id)) { + bean.msg = "Monitor under maintenance"; + bean.status = MAINTENANCE; + } else if (this.type === "http" || this.type === "keyword" || this.type === "json-query") { + // Do not do any queries/high loading things before the "bean.ping" + let startTime = dayjs().valueOf(); + + // HTTP basic auth + let basicAuthHeader = {}; + if (this.auth_method === "basic") { + basicAuthHeader = { + Authorization: "Basic " + encodeBase64(this.basic_auth_user, this.basic_auth_pass), + }; + } + + // Bearer token auth + let bearerAuthHeader = {}; + if (this.auth_method === "bearer") { + bearerAuthHeader = { + Authorization: "Bearer " + this.bearer_token, + }; + } + + // OIDC: Basic client credential flow. + // Additional grants might be implemented in the future + let oauth2AuthHeader = {}; + if (this.auth_method === "oauth2-cc") { + try { + if ( + this.oauthAccessToken === undefined || + new Date(this.oauthAccessToken.expires_at * 1000) <= new Date() + ) { + this.oauthAccessToken = await this.makeOidcTokenClientCredentialsRequest(); + } + oauth2AuthHeader = { + Authorization: + this.oauthAccessToken.token_type + " " + this.oauthAccessToken.access_token, + }; + } catch (e) { + throw new Error("The oauth config is invalid. " + e.message); + } + } + + let agentFamily = undefined; + if (this.ipFamily === "ipv4") { + agentFamily = 4; + } + if (this.ipFamily === "ipv6") { + agentFamily = 6; + } + + const httpsAgentOptions = { + maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) + rejectUnauthorized: !this.getIgnoreTls(), + secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT, + autoSelectFamily: true, + ...(agentFamily ? { family: agentFamily } : {}), + }; + + const httpAgentOptions = { + maxCachedSessions: 0, + autoSelectFamily: true, + ...(agentFamily ? { family: agentFamily } : {}), + }; + + log.debug("monitor", `[${this.name}] Prepare Options for axios`); + + let contentType = null; + let bodyValue = null; + + if (this.body && typeof this.body === "string" && this.body.trim().length > 0) { + if (!this.httpBodyEncoding || this.httpBodyEncoding === "json") { + try { + bodyValue = JSON.parse(this.body); + contentType = "application/json"; + } catch (e) { + throw new Error("Your JSON body is invalid. " + e.message); + } + } else if (this.httpBodyEncoding === "form") { + bodyValue = this.body; + contentType = "application/x-www-form-urlencoded"; + } else if (this.httpBodyEncoding === "xml") { + bodyValue = this.body; + contentType = "text/xml; charset=utf-8"; + } + } + + // Axios Options + const options = { + url: this.url, + method: (this.method || "get").toLowerCase(), + timeout: this.timeout * 1000, + headers: { + Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + ...(contentType ? { "Content-Type": contentType } : {}), + ...basicAuthHeader, + ...bearerAuthHeader, + ...oauth2AuthHeader, + ...(this.headers ? JSON.parse(this.headers) : {}), + }, + maxRedirects: this.maxredirects, + validateStatus: (status) => { + return checkStatusCode(status, this.getAcceptedStatuscodes()); + }, + signal: axiosAbortSignal((this.timeout + 10) * 1000), + }; + + if (bodyValue) { + options.data = bodyValue; + } + + if (this.cacheBust) { + const randomFloatString = Math.random().toString(36); + const cacheBust = randomFloatString.substring(2); + options.params = { + uptime_kuma_cachebuster: cacheBust, + }; + } + + if (this.proxy_id) { + const proxy = await R.load("proxy", this.proxy_id); + + if (proxy && proxy.active) { + const { httpAgent, httpsAgent } = Proxy.createAgents(proxy, { + httpsAgentOptions: httpsAgentOptions, + httpAgentOptions: httpAgentOptions, + }); + + options.proxy = false; + options.httpAgent = httpAgent; + options.httpsAgent = httpsAgent; + } + } + + if (!options.httpAgent) { + options.httpAgent = new http.Agent(httpAgentOptions); + } + + if (!options.httpsAgent) { + let jar = new CookieJar(); + let httpsCookieAgentOptions = { + ...httpsAgentOptions, + cookies: { jar }, + }; + options.httpsAgent = new HttpsCookieAgent(httpsCookieAgentOptions); + } + + if (this.auth_method === "mtls") { + if (this.tlsCert !== null && this.tlsCert !== "") { + options.httpsAgent.options.cert = Buffer.from(this.tlsCert); + } + if (this.tlsCa !== null && this.tlsCa !== "") { + options.httpsAgent.options.ca = Buffer.from(this.tlsCa); + } + if (this.tlsKey !== null && this.tlsKey !== "") { + options.httpsAgent.options.key = Buffer.from(this.tlsKey); + } + } + + let tlsInfo = {}; + // Store tlsInfo when secureConnect event is emitted + // The keylog event listener is a workaround to access the tlsSocket + options.httpsAgent.once("keylog", async (line, tlsSocket) => { + tlsSocket.once("secureConnect", async () => { + tlsInfo = checkCertificate(tlsSocket); + tlsInfo.valid = tlsSocket.authorized || false; + tlsInfo.hostnameMatchMonitorUrl = checkCertificateHostname( + tlsInfo.certInfo.raw, + this.getUrl()?.hostname + ); + + await this.handleTlsInfo(tlsInfo); + }); + }); + + log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`); + log.debug("monitor", `[${this.name}] Axios Request`); + + // Make Request + let res = await this.makeAxiosRequest(options); + + bean.msg = `${res.status} - ${res.statusText}`; + bean.ping = dayjs().valueOf() - startTime; + + // in the frontend, the save response is only shown if the saveErrorResponse is set + if (this.getSaveResponse() && this.getSaveErrorResponse()) { + await this.saveResponseData(bean, res.data); + } + + // fallback for if kelog event is not emitted, but we may still have tlsInfo, + // e.g. if the connection is made through a proxy + if (this.getUrl()?.protocol === "https:" && tlsInfo.valid === undefined) { + const tlsSocket = res.request.res.socket; + + if (tlsSocket) { + tlsInfo = checkCertificate(tlsSocket); + tlsInfo.valid = tlsSocket.authorized || false; + tlsInfo.hostnameMatchMonitorUrl = checkCertificateHostname( + tlsInfo.certInfo.raw, + this.getUrl()?.hostname + ); + + await this.handleTlsInfo(tlsInfo); + } + } + + // eslint-disable-next-line eqeqeq + if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID == this.id) { + log.info("monitor", res.data); + } + + if (this.type === "http") { + bean.status = UP; + } else if (this.type === "keyword") { + let data = res.data; + + // Convert to string for object/array + if (typeof data !== "string") { + data = JSON.stringify(data); + } + + let keywordFound = data.includes(this.keyword); + if (keywordFound === !this.isInvertKeyword()) { + bean.msg += ", keyword " + (keywordFound ? "is" : "not") + " found"; + bean.status = UP; + } else { + data = data.replace(/<[^>]*>?|[\n\r]|\s+/gm, " ").trim(); + if (data.length > 50) { + data = data.substring(0, 47) + "..."; + } + throw new Error( + bean.msg + + ", but keyword is " + + (keywordFound ? "present" : "not") + + " in [" + + data + + "]" + ); + } + } else if (this.type === "json-query") { + let data = res.data; + + const { status, response } = await evaluateJsonQuery( + data, + this.jsonPath, + this.jsonPathOperator, + this.expectedValue + ); + + if (status) { + bean.status = UP; + bean.msg = `JSON query passes (comparing ${response} ${this.jsonPathOperator} ${this.expectedValue})`; + } else { + throw new Error( + `JSON query does not pass (comparing ${response} ${this.jsonPathOperator} ${this.expectedValue})` + ); + } + } + } else if (this.type === "ping") { + bean.ping = await ping( + this.hostname, + this.ping_count, + "", + this.ping_numeric, + this.packetSize, + this.timeout, + this.ping_per_request_timeout + ); + bean.msg = ""; + bean.status = UP; + } else if (this.type === "push") { + // Type: Push + log.debug( + "monitor", + `[${this.name}] Checking monitor at ${dayjs().format("YYYY-MM-DD HH:mm:ss.SSS")}` + ); + const bufferTime = 1000; // 1s buffer to accommodate clock differences + + if (previousBeat) { + const msSinceLastBeat = dayjs.utc().valueOf() - dayjs.utc(previousBeat.time).valueOf(); + + log.debug("monitor", `[${this.name}] msSinceLastBeat = ${msSinceLastBeat}`); + + // If the previous beat was down or pending we use the regular + // beatInterval/retryInterval in the setTimeout further below + if ( + previousBeat.status !== (this.isUpsideDown() ? DOWN : UP) || + msSinceLastBeat > beatInterval * 1000 + bufferTime + ) { + bean.duration = Math.round(msSinceLastBeat / 1000); + throw new Error("No heartbeat in the time window"); + } else { + let timeout = beatInterval * 1000 - msSinceLastBeat; + if (timeout < 0) { + timeout = bufferTime; + } else { + timeout += bufferTime; + } + // No need to insert successful heartbeat for push type, so end here + retries = 0; + log.debug("monitor", `[${this.name}] timeout = ${timeout}`); + this.heartbeatInterval = setTimeout(safeBeat, timeout); + return; + } + } else { + bean.duration = beatInterval; + throw new Error("No heartbeat in the time window"); + } + } else if (this.type === "docker") { + log.debug("monitor", `[${this.name}] Prepare Options for Axios`); + + const options = { + url: `/containers/${this.docker_container}/json`, + timeout: this.interval * 1000 * 0.8, + headers: { + Accept: "*/*", + }, + httpsAgent: new https.Agent({ + maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) + rejectUnauthorized: !this.getIgnoreTls(), + secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT, + }), + httpAgent: new http.Agent({ + maxCachedSessions: 0, + }), + }; + + const dockerHost = await R.load("docker_host", this.docker_host); + + if (!dockerHost) { + throw new Error("Failed to load docker host config"); + } + + if (dockerHost._dockerType === "socket") { + 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) + ); + } + + log.debug("monitor", `[${this.name}] Axios Request`); + let res = await axios.request(options); + + if (!res.data.State) { + throw Error("Container state is not available"); + } + if (!res.data.State.Running) { + throw Error("Container State is " + res.data.State.Status); + } + if (res.data.State.Paused) { + throw Error("Container is in a paused state"); + } + if (res.data.State.Restarting) { + bean.status = PENDING; + bean.msg = "Container is reporting it is currently restarting"; + } else if (res.data.State.Health && res.data.State.Health.Status !== "none") { + // if healthchecks are disabled (?), Health MAY not be present + if (res.data.State.Health.Status === "healthy") { + bean.status = UP; + bean.msg = "healthy"; + } else if (res.data.State.Health.Status === "unhealthy") { + throw Error("Container State is unhealthy according to its healthcheck"); + } else { + bean.status = PENDING; + bean.msg = res.data.State.Health.Status; + } + } else { + bean.status = UP; + bean.msg = `Container has not reported health and is currently ${res.data.State.Status}. As it is running, it is considered UP. Consider adding a health check for better service visibility`; + } + } else if (this.type === "radius") { + let startTime = dayjs().valueOf(); + + // Handle monitors that were created before the + // update and as such don't have a value for + // this.port. + let port; + if (this.port == null) { + port = 1812; + } else { + port = this.port; + } + + const resp = await radius( + this.hostname, + this.radiusUsername, + this.radiusPassword, + this.radiusCalledStationId, + this.radiusCallingStationId, + this.radiusSecret, + port, + this.interval * 1000 * 0.4 + ); + + bean.msg = resp.code; + bean.status = UP; + bean.ping = dayjs().valueOf() - startTime; + } else if (this.type in UptimeKumaServer.monitorTypeList) { + let startTime = dayjs().valueOf(); + const monitorType = UptimeKumaServer.monitorTypeList[this.type]; + await monitorType.check(this, bean, UptimeKumaServer.getInstance()); + + if (!monitorType.allowCustomStatus && bean.status !== UP) { + throw new Error( + "The monitor implementation is incorrect, non-UP error must throw error inside check()" + ); + } + + if (bean.ping === undefined || bean.ping === null) { + bean.ping = dayjs().valueOf() - startTime; + } + } else if (this.type === "kafka-producer") { + let startTime = dayjs().valueOf(); + + bean.msg = await kafkaProducerAsync( + JSON.parse(this.kafkaProducerBrokers), + this.kafkaProducerTopic, + this.kafkaProducerMessage, + { + allowAutoTopicCreation: this.kafkaProducerAllowAutoTopicCreation, + ssl: this.kafkaProducerSsl, + clientId: `Uptime-Kuma/${version}`, + interval: this.interval, + connectionTimeout: this.timeout, + }, + JSON.parse(this.kafkaProducerSaslOptions) + ); + bean.status = UP; + bean.ping = dayjs().valueOf() - startTime; + } else { + throw new Error("Unknown Monitor Type"); + } + + if (this.isUpsideDown()) { + bean.status = flipStatus(bean.status); + + if (bean.status === DOWN) { + throw new Error("Flip UP to DOWN"); + } + } + + retries = 0; + } catch (error) { + if (error?.name === "CanceledError") { + bean.msg = `timeout by AbortSignal (${this.timeout}s)`; + } else { + bean.msg = error.message; + } + + if (this.getSaveErrorResponse() && error?.response?.data !== undefined) { + await this.saveResponseData(bean, error.response.data); + } + + // If UP come in here, it must be upside down mode + // Just reset the retries + if (this.isUpsideDown() && bean.status === UP) { + retries = 0; + } else if (this.type === "json-query" && this.retry_only_on_status_code_failure) { + // For json-query monitors with retry_only_on_status_code_failure enabled, + // only retry if the error is NOT from JSON query evaluation + // JSON query errors have the message "JSON query does not pass..." + const isJsonQueryError = + typeof error.message === "string" && error.message.includes("JSON query does not pass"); + + if (isJsonQueryError) { + // Don't retry on JSON query failures, mark as DOWN immediately + retries = 0; + } else if (this.maxretries > 0 && retries < this.maxretries) { + retries++; + bean.status = PENDING; + } else { + // Continue counting retries during DOWN + retries++; + } + } else { + // General retry logic for all other monitor types + if (this.maxretries > 0 && retries < this.maxretries) { + retries++; + bean.status = PENDING; + } else { + // Continue counting retries during DOWN + retries++; + } + } + } + + bean.retries = retries; + + log.debug("monitor", `[${this.name}] Check isImportant`); + let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status); + + // Mark as important if status changed, ignore pending pings, + // Don't notify if disrupted changes to up + if (isImportant) { + bean.important = true; + + if (Monitor.isImportantForNotification(isFirstBeat, previousBeat?.status, bean.status)) { + log.debug("monitor", `[${this.name}] sendNotification`); + await Monitor.sendNotification(isFirstBeat, this, bean); + } else { + log.debug( + "monitor", + `[${this.name}] will not sendNotification because it is (or was) under maintenance` + ); + } + + // Reset down count + bean.downCount = 0; + + // Clear Status Page Cache + log.debug("monitor", `[${this.name}] apicache clear`); + apicache.clear(); + + await UptimeKumaServer.getInstance().sendMaintenanceListByUserID(this.user_id); + } else { + bean.important = false; + + if (bean.status === DOWN && this.resendInterval > 0) { + ++bean.downCount; + if (bean.downCount >= this.resendInterval) { + // Send notification again, because we are still DOWN + log.debug( + "monitor", + `[${this.name}] sendNotification again: Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}` + ); + await Monitor.sendNotification(isFirstBeat, this, bean); + + // Reset down count + bean.downCount = 0; + } + } + } + + if (bean.status !== MAINTENANCE && Boolean(this.domainExpiryNotification)) { + try { + const supportInfo = await DomainExpiry.checkSupport(this); + const domainExpiryDate = await DomainExpiry.checkExpiry(supportInfo.domain); + if (domainExpiryDate) { + DomainExpiry.sendNotifications( + supportInfo.domain, + (await Monitor.getNotificationList(this)) || [] + ); + } else { + log.debug("monitor", `Failed getting expiration date for domain ${supportInfo.domain}`); + } + } catch (error) { + if ( + error.message === "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint" && + Boolean(this.domainExpiryNotification) + ) { + log.warn( + "domain_expiry", + `Domain expiry unsupported for '.${error.meta.publicSuffix}' because it lacks an RDAP endpoint in the IANA database. This isn’t an Uptime Kuma bug, a limitation of your registry. If an RDAP server exists, ask your registrar politely to submit it to IANA so expiry checks can work.` + ); + } + } + } + + if (bean.status === UP) { + log.debug( + "monitor", + `Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${beatInterval} seconds | Type: ${this.type}` + ); + } else if (bean.status === PENDING) { + if (this.retryInterval > 0) { + beatInterval = this.retryInterval; + } + log.warn( + "monitor", + `Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Max retries: ${this.maxretries} | Retry: ${retries} | Retry Interval: ${beatInterval} seconds | Type: ${this.type}` + ); + } else if (bean.status === MAINTENANCE) { + log.warn("monitor", `Monitor #${this.id} '${this.name}': Under Maintenance | Type: ${this.type}`); + } else { + log.warn( + "monitor", + `Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Interval: ${beatInterval} seconds | Type: ${this.type} | Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}` + ); + } + + // Calculate uptime + let uptimeCalculator = await UptimeCalculator.getUptimeCalculator(this.id); + let endTimeDayjs = await uptimeCalculator.update(bean.status, parseFloat(bean.ping)); + bean.end_time = R.isoDateTimeMillis(endTimeDayjs); + + // Send to frontend + log.debug("monitor", `[${this.name}] Send to socket`); + io.to(this.user_id).emit("heartbeat", bean.toJSON()); + Monitor.sendStats(io, this.id, this.user_id); + + // Store to database + log.debug("monitor", `[${this.name}] Store`); + await R.store(bean); + + log.debug("monitor", `[${this.name}] prometheus.update`); + const data24h = uptimeCalculator.get24Hour(); + const data30d = uptimeCalculator.get30Day(); + const data1y = uptimeCalculator.get1Year(); + this.prometheus?.update(bean, tlsInfo, { data24h, data30d, data1y }); + + previousBeat = bean; + + if (!this.isStop) { + log.debug("monitor", `[${this.name}] SetTimeout for next check.`); + + let intervalRemainingMs = Math.max(1, beatInterval * 1000 - dayjs().diff(dayjs.utc(bean.time))); + + log.debug("monitor", `[${this.name}] Next heartbeat in: ${intervalRemainingMs}ms`); + + this.heartbeatInterval = setTimeout(safeBeat, intervalRemainingMs); + } else { + log.info("monitor", `[${this.name}] isStop = true, no next check.`); + } + }; + + /** + * Get a heartbeat and handle errors7 + * @returns {void} + */ + const safeBeat = async () => { + try { + await beat(); + } catch (e) { + console.trace(e); + UptimeKumaServer.errorLog(e, false); + log.error("monitor", "Please report to https://github.com/louislam/uptime-kuma/issues"); + + if (!this.isStop) { + log.info("monitor", "Try to restart the monitor"); + this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000); + } + } + }; + + // Delay Push Type + if (this.type === "push") { + this.heartbeatInterval = setTimeout(() => { + safeBeat(); + }, this.interval * 1000); + } else { + safeBeat(); + } + } + + /** + * Save response body to a heartbeat if response saving is enabled. + * @param {import("redbean-node").Bean} bean Heartbeat bean to populate. + * @param {unknown} data Response payload. + * @returns {void} + */ + async saveResponseData(bean, data) { + if (data === undefined) { + return; + } + + let responseData = data; + if (typeof responseData !== "string") { + try { + responseData = JSON.stringify(responseData); + } catch (error) { + responseData = String(responseData); + } + } + + const maxSize = this.response_max_length ?? RESPONSE_BODY_LENGTH_DEFAULT; + if (responseData.length > maxSize) { + responseData = responseData.substring(0, maxSize) + "... (truncated)"; + } + + // Offload brotli compression from main event loop to libuv thread pool + bean.response = (await brotliCompress(Buffer.from(responseData, "utf8"))).toString("base64"); + } + + /** + * Make a request using axios + * @param {object} options Options for Axios + * @param {boolean} finalCall Should this be the final call i.e + * don't retry on failure + * @returns {object} Axios response + */ + async makeAxiosRequest(options, finalCall = false) { + try { + let res; + if (this.auth_method === "ntlm") { + options.httpsAgent.keepAlive = true; + options.httpAgent.keepAlive = true; + + res = await httpNtlm(options, { + username: this.basic_auth_user, + password: this.basic_auth_pass, + domain: this.authDomain, + workstation: this.authWorkstation ? this.authWorkstation : undefined, + }); + } else { + res = await axios.request(options); + } + + return res; + } catch (error) { + /** + * Make a single attempt to obtain an new access token in the event that + * the recent api request failed for authentication purposes + */ + if (this.auth_method === "oauth2-cc" && error.response.status === 401 && !finalCall) { + this.oauthAccessToken = await this.makeOidcTokenClientCredentialsRequest(); + let oauth2AuthHeader = { + Authorization: this.oauthAccessToken.token_type + " " + this.oauthAccessToken.access_token, + }; + options.headers = { ...options.headers, ...oauth2AuthHeader }; + + return this.makeAxiosRequest(options, true); + } + + // Fix #2253 + // Read more: https://stackoverflow.com/questions/1759956/curl-error-18-transfer-closed-with-outstanding-read-data-remaining + if ( + !finalCall && + typeof error.message === "string" && + error.message.includes("maxContentLength size of -1 exceeded") + ) { + log.debug("monitor", "makeAxiosRequest with gzip"); + options.headers["Accept-Encoding"] = "gzip, deflate"; + return this.makeAxiosRequest(options, true); + } else { + if ( + typeof error.message === "string" && + error.message.includes("maxContentLength size of -1 exceeded") + ) { + error.message = "response timeout: incomplete response within a interval"; + } + throw error; + } + } + } + + /** + * Stop monitor + * @returns {Promise} + */ + async stop() { + clearTimeout(this.heartbeatInterval); + this.isStop = true; + + this.prometheus?.remove(); + } + + /** + * Get prometheus instance + * @returns {Prometheus|undefined} Current prometheus instance + */ + getPrometheus() { + return this.prometheus; + } + + /** + * Helper Method: + * returns URL object for further usage + * returns null if url is invalid + * @returns {(null|URL)} Monitor URL + */ + getUrl() { + try { + return new URL(this.url); + } catch (_) { + return null; + } + } + + /** + * Example: http: or https: + * @returns {(null|string)} URL's protocol + */ + getURLProtocol() { + const url = this.getUrl(); + if (url) { + return this.getUrl().protocol; + } else { + return null; + } + } + + /** + * Store TLS info to database + * @param {object} checkCertificateResult Certificate to update + * @returns {Promise} Updated certificate + */ + async updateTlsInfo(checkCertificateResult) { + let tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [this.id]); + + if (tlsInfoBean == null) { + tlsInfoBean = R.dispense("monitor_tls_info"); + tlsInfoBean.monitor_id = this.id; + } else { + // Clear sent history if the cert changed. + try { + let oldCertInfo = JSON.parse(tlsInfoBean.info_json); + + let isValidObjects = + oldCertInfo && oldCertInfo.certInfo && checkCertificateResult && checkCertificateResult.certInfo; + + if (isValidObjects) { + if (oldCertInfo.certInfo.fingerprint256 !== checkCertificateResult.certInfo.fingerprint256) { + log.debug("monitor", "Resetting sent_history"); + await R.exec( + "DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", + [this.id] + ); + } else { + log.debug("monitor", "No need to reset sent_history"); + log.debug("monitor", oldCertInfo.certInfo.fingerprint256); + log.debug("monitor", checkCertificateResult.certInfo.fingerprint256); + } + } else { + log.debug("monitor", "Not valid object"); + } + } catch (e) {} + } + + tlsInfoBean.info_json = JSON.stringify(checkCertificateResult); + await R.store(tlsInfoBean); + + return checkCertificateResult; + } + + /** + * Checks if the monitor is active based on itself and its parents + * @param {number} monitorID ID of monitor to send + * @param {boolean} active is active + * @returns {Promise} Is the monitor active? + */ + static async isActive(monitorID, active) { + const parentActive = await Monitor.isParentActive(monitorID); + + return active === 1 && parentActive; + } + + /** + * Send statistics to clients + * @param {Server} io Socket server instance + * @param {number} monitorID ID of monitor to send + * @param {number} userID ID of user to send to + * @returns {Promise} + */ + static async sendStats(io, monitorID, userID) { + const hasClients = getTotalClientInRoom(io, userID) > 0; + let uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID); + + if (hasClients) { + // Send 24 hour average ping + let data24h = await uptimeCalculator.get24Hour(); + io.to(userID).emit("avgPing", monitorID, data24h.avgPing ? Number(data24h.avgPing.toFixed(2)) : null); + + // Send 24 hour uptime + io.to(userID).emit("uptime", monitorID, 24, data24h.uptime); + + // Send 30 day uptime + let data30d = await uptimeCalculator.get30Day(); + io.to(userID).emit("uptime", monitorID, 720, data30d.uptime); + + // Send 1-year uptime + let data1y = await uptimeCalculator.get1Year(); + io.to(userID).emit("uptime", monitorID, "1y", data1y.uptime); + + // Send Cert Info + await Monitor.sendCertInfo(io, monitorID, userID); + + // Send domain info + await Monitor.sendDomainInfo(io, monitorID, userID); + } else { + log.debug("monitor", "No clients in the room, no need to send stats"); + } + } + + /** + * Send certificate information to client + * @param {Server} io Socket server instance + * @param {number} monitorID ID of monitor to send + * @param {number} userID ID of user to send to + * @returns {void} + */ + static async sendCertInfo(io, monitorID, userID) { + let tlsInfo = await R.findOne("monitor_tls_info", "monitor_id = ?", [monitorID]); + if (tlsInfo != null) { + io.to(userID).emit("certInfo", monitorID, tlsInfo.info_json); + } + } + + /** + * Send domain name information to client + * @param {Server} io Socket server instance + * @param {number} monitorID ID of monitor to send + * @param {number} userID ID of user to send to + * @returns {void} + */ + static async sendDomainInfo(io, monitorID, userID) { + const monitor = await R.findOne("monitor", "id = ?", [monitorID]); + + try { + const supportInfo = await DomainExpiry.checkSupport(monitor); + const domain = await DomainExpiry.findByDomainNameOrCreate(supportInfo.domain); + if (domain?.expiry) { + io.to(userID).emit("domainInfo", monitorID, domain.daysRemaining, new Date(domain.expiry)); + } + } catch (e) {} + } + + /** + * Has status of monitor changed since last beat? + * @param {boolean} isFirstBeat Is this the first beat of this monitor? + * @param {const} previousBeatStatus Status of the previous beat + * @param {const} currentBeatStatus Status of the current beat + * @returns {boolean} True if is an important beat else false + */ + static isImportantBeat(isFirstBeat, previousBeatStatus, currentBeatStatus) { + // * ? -> ANY STATUS = important [isFirstBeat] + // UP -> PENDING = not important + // * UP -> DOWN = important + // UP -> UP = not important + // PENDING -> PENDING = not important + // * PENDING -> DOWN = important + // PENDING -> UP = not important + // DOWN -> PENDING = this case not exists + // DOWN -> DOWN = not important + // * DOWN -> UP = important + // MAINTENANCE -> MAINTENANCE = not important + // * MAINTENANCE -> UP = important + // * MAINTENANCE -> DOWN = important + // * DOWN -> MAINTENANCE = important + // * UP -> MAINTENANCE = important + return ( + isFirstBeat || + (previousBeatStatus === DOWN && currentBeatStatus === MAINTENANCE) || + (previousBeatStatus === UP && currentBeatStatus === MAINTENANCE) || + (previousBeatStatus === MAINTENANCE && currentBeatStatus === DOWN) || + (previousBeatStatus === MAINTENANCE && currentBeatStatus === UP) || + (previousBeatStatus === UP && currentBeatStatus === DOWN) || + (previousBeatStatus === DOWN && currentBeatStatus === UP) || + (previousBeatStatus === PENDING && currentBeatStatus === DOWN) + ); + } + + /** + * Is this beat important for notifications? + * @param {boolean} isFirstBeat Is this the first beat of this monitor? + * @param {const} previousBeatStatus Status of the previous beat + * @param {const} currentBeatStatus Status of the current beat + * @returns {boolean} True if is an important beat else false + */ + static isImportantForNotification(isFirstBeat, previousBeatStatus, currentBeatStatus) { + // * ? -> ANY STATUS = important [isFirstBeat] + // UP -> PENDING = not important + // * UP -> DOWN = important + // UP -> UP = not important + // PENDING -> PENDING = not important + // * PENDING -> DOWN = important + // PENDING -> UP = not important + // DOWN -> PENDING = this case not exists + // DOWN -> DOWN = not important + // * DOWN -> UP = important + // MAINTENANCE -> MAINTENANCE = not important + // MAINTENANCE -> UP = not important + // * MAINTENANCE -> DOWN = important + // DOWN -> MAINTENANCE = not important + // UP -> MAINTENANCE = not important + return ( + isFirstBeat || + (previousBeatStatus === MAINTENANCE && currentBeatStatus === DOWN) || + (previousBeatStatus === UP && currentBeatStatus === DOWN) || + (previousBeatStatus === DOWN && currentBeatStatus === UP) || + (previousBeatStatus === PENDING && currentBeatStatus === DOWN) + ); + } + + /** + * Send a notification about a monitor + * @param {boolean} isFirstBeat Is this beat the first of this monitor? + * @param {Monitor} monitor The monitor to send a notification about + * @param {import("./heartbeat")} bean Status information about monitor + * @returns {Promise} + */ + static async sendNotification(isFirstBeat, monitor, bean) { + if (!isFirstBeat || bean.status === DOWN) { + const notificationList = await Monitor.getNotificationList(monitor); + + let text; + if (bean.status === UP) { + text = "✅ Up"; + } else { + text = "🔴 Down"; + } + + let msg = `[${monitor.name}] [${text}] ${bean.msg}`; + + const heartbeatJSON = await bean.toJSONAsync({ decodeResponse: true }); + const monitorData = [{ id: monitor.id, active: monitor.active, name: monitor.name }]; + const preloadData = await Monitor.preparePreloadData(monitorData); + // Prevent if the msg is undefined, notifications such as Discord cannot send out. + if (!heartbeatJSON["msg"]) { + heartbeatJSON["msg"] = "N/A"; + } + + // Also provide the time in server timezone + heartbeatJSON["timezone"] = await UptimeKumaServer.getInstance().getTimezone(); + heartbeatJSON["timezoneOffset"] = UptimeKumaServer.getInstance().getTimezoneOffset(); + heartbeatJSON["localDateTime"] = dayjs + .utc(heartbeatJSON["time"]) + .tz(heartbeatJSON["timezone"]) + .format(SQL_DATETIME_FORMAT); + + // Calculate downtime tracking information when service comes back up + // This makes downtime information available to all notification providers + if (bean.status === UP && monitor.id) { + try { + // Filter by important = 1 to get the state transition heartbeat (e.g. UP→DOWN), + // not the most recent DOWN heartbeat which would be the last check before recovery. + const lastDownHeartbeat = await R.getRow( + "SELECT time FROM heartbeat WHERE monitor_id = ? AND status = ? AND important = 1 ORDER BY time DESC LIMIT 1", + [monitor.id, DOWN] + ); + + if (lastDownHeartbeat && lastDownHeartbeat.time) { + heartbeatJSON["lastDownTime"] = lastDownHeartbeat.time; + } + } catch (error) { + // If we can't calculate downtime, just continue without it + // Silently fail to avoid disrupting notification sending + log.debug( + "monitor", + `[${monitor.name}] Could not calculate downtime information: ${error.message}` + ); + } + } + + for (let notification of notificationList) { + try { + await Notification.send( + JSON.parse(notification.config), + msg, + monitor.toJSON(preloadData, false), + heartbeatJSON + ); + } catch (e) { + log.error("monitor", "Cannot send notification to " + notification.name); + log.error("monitor", e); + } + } + } + } + + /** + * Get list of notification providers for a given monitor + * @param {Monitor} monitor Monitor to get notification providers for + * @returns {Promise[]>} List of notifications + */ + static async getNotificationList(monitor) { + let notificationList = await R.getAll( + "SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", + [monitor.id] + ); + return notificationList; + } + + /** + * Send a certificate notification when certificate expires in less + * than target days + * @param {string} certCN Common Name attribute from the certificate subject + * @param {string} certType certificate type + * @param {number} daysRemaining Number of days remaining on certificate + * @param {number} targetDays Number of days to alert after + * @param {LooseObject[]} notificationList List of notification providers + * @returns {Promise} + */ + async sendCertNotificationByTargetDays(certCN, certType, daysRemaining, targetDays, notificationList) { + let row = await R.getRow( + "SELECT * FROM notification_sent_history WHERE type = ? AND monitor_id = ? AND days <= ?", + ["certificate", this.id, targetDays] + ); + + // Sent already, no need to send again + if (row) { + log.debug("monitor", "Sent already, no need to send again"); + return; + } + + let sent = false; + log.debug("monitor", "Send certificate notification"); + + for (let notification of notificationList) { + try { + log.debug("monitor", "Sending to " + notification.name); + await Notification.send( + JSON.parse(notification.config), + `[${this.name}][${this.url}] ${certType} certificate ${certCN} will expire in ${daysRemaining} days` + ); + sent = true; + } catch (e) { + log.error("monitor", "Cannot send cert notification to " + notification.name); + log.error("monitor", e); + } + } + + if (sent) { + await R.exec("INSERT INTO notification_sent_history (type, monitor_id, days) VALUES(?, ?, ?)", [ + "certificate", + this.id, + targetDays, + ]); + } + } + + /** + * Get the status of the previous heartbeat + * @param {number} monitorID ID of monitor to check + * @returns {Promise>} Previous heartbeat + */ + static async getPreviousHeartbeat(monitorID) { + return await R.findOne("heartbeat", " id = (select MAX(id) from heartbeat where monitor_id = ?)", [monitorID]); + } + + /** + * Check if monitor is under maintenance + * @param {number} monitorID ID of monitor to check + * @returns {Promise} Is the monitor under maintenance + */ + static async isUnderMaintenance(monitorID) { + const maintenanceIDList = await R.getCol( + ` + SELECT maintenance_id FROM monitor_maintenance + WHERE monitor_id = ? + `, + [monitorID] + ); + + for (const maintenanceID of maintenanceIDList) { + const maintenance = await UptimeKumaServer.getInstance().getMaintenance(maintenanceID); + if (maintenance && (await maintenance.isUnderMaintenance())) { + return true; + } + } + + const parent = await Monitor.getParent(monitorID); + if (parent != null) { + return await Monitor.isUnderMaintenance(parent.id); + } + + return false; + } + + /** + * Validate monitor configuration + * @returns {void} + * @throws {Error} If validation fails + */ + validate() { + if (this.interval < MIN_INTERVAL_SECOND) { + throw new Error(`Interval cannot be less than ${MIN_INTERVAL_SECOND} seconds`); + } + + if (this.retryInterval < MIN_INTERVAL_SECOND) { + throw new Error(`Retry interval cannot be less than ${MIN_INTERVAL_SECOND} seconds`); + } + + if (this.response_max_length !== undefined) { + if (this.response_max_length < 0) { + throw new Error(`Response max length cannot be less than 0`); + } + + if (this.response_max_length > RESPONSE_BODY_LENGTH_MAX) { + throw new Error(`Response max length cannot be more than ${RESPONSE_BODY_LENGTH_MAX} bytes`); + } + } + + // Validate JSON fields to prevent invalid JSON from being stored in database + if (this.kafkaProducerBrokers) { + try { + JSON.parse(this.kafkaProducerBrokers); + } catch (e) { + throw new Error(`Kafka Producer Brokers must be valid JSON: ${e.message}`); + } + } + + if (this.kafkaProducerSaslOptions) { + try { + JSON.parse(this.kafkaProducerSaslOptions); + } catch (e) { + throw new Error(`Kafka Producer SASL Options must be valid JSON: ${e.message}`); + } + } + + if (this.rabbitmqNodes) { + try { + JSON.parse(this.rabbitmqNodes); + } catch (e) { + throw new Error(`RabbitMQ Nodes must be valid JSON: ${e.message}`); + } + } + + if (this.conditions) { + try { + JSON.parse(this.conditions); + } catch (e) { + throw new Error(`Conditions must be valid JSON: ${e.message}`); + } + } + + if (this.headers) { + try { + JSON.parse(this.headers); + } catch (e) { + throw new Error(`Headers must be valid JSON: ${e.message}`); + } + } + + if (this.accepted_statuscodes_json) { + try { + JSON.parse(this.accepted_statuscodes_json); + } catch (e) { + throw new Error(`Accepted status codes must be valid JSON: ${e.message}`); + } + } + + if (["system-service", "pm2"].includes(this.type)) { + this.system_service_name = (this.system_service_name || "").trim(); + + if (!this.system_service_name) { + throw new Error(this.type === "pm2" ? "PM2 process name is required." : "Service Name is required."); + } + } + + if (this.type === "system-service" && !/^[a-zA-Z0-9._\-@]+$/.test(this.system_service_name)) { + throw new Error("Invalid service name. Please use the internal Service Name (no spaces)."); + } + + if (this.type === "pm2" && /[\u0000-\u001F\u007F]/.test(this.system_service_name)) { + throw new Error("Invalid PM2 process name."); + } + + if (this.type === "ping") { + // ping parameters validation + if (this.packetSize && (this.packetSize < PING_PACKET_SIZE_MIN || this.packetSize > PING_PACKET_SIZE_MAX)) { + throw new Error( + `Packet size must be between ${PING_PACKET_SIZE_MIN} and ${PING_PACKET_SIZE_MAX} (default: ${PING_PACKET_SIZE_DEFAULT})` + ); + } + + if ( + this.ping_per_request_timeout && + (this.ping_per_request_timeout < PING_PER_REQUEST_TIMEOUT_MIN || + this.ping_per_request_timeout > PING_PER_REQUEST_TIMEOUT_MAX) + ) { + throw new Error( + `Per-ping timeout must be between ${PING_PER_REQUEST_TIMEOUT_MIN} and ${PING_PER_REQUEST_TIMEOUT_MAX} seconds (default: ${PING_PER_REQUEST_TIMEOUT_DEFAULT})` + ); + } + + if (this.ping_count && (this.ping_count < PING_COUNT_MIN || this.ping_count > PING_COUNT_MAX)) { + throw new Error( + `Echo requests count must be between ${PING_COUNT_MIN} and ${PING_COUNT_MAX} (default: ${PING_COUNT_DEFAULT})` + ); + } + + if (this.timeout) { + const pingGlobalTimeout = Math.round(Number(this.timeout)); + + if ( + pingGlobalTimeout < this.ping_per_request_timeout || + pingGlobalTimeout < PING_GLOBAL_TIMEOUT_MIN || + pingGlobalTimeout > PING_GLOBAL_TIMEOUT_MAX + ) { + throw new Error( + `Timeout must be between ${PING_GLOBAL_TIMEOUT_MIN} and ${PING_GLOBAL_TIMEOUT_MAX} seconds (default: ${PING_GLOBAL_TIMEOUT_DEFAULT})` + ); + } + + this.timeout = pingGlobalTimeout; + } + } + + if (this.type === "real-browser") { + // screenshot_delay validation + if (this.screenshot_delay !== undefined && this.screenshot_delay !== null) { + const delay = Number(this.screenshot_delay); + if (isNaN(delay) || delay < 0) { + throw new Error("Screenshot delay must be a non-negative number"); + } + + // Must not exceed 0.8 * timeout (page.goto timeout is interval * 1000 * 0.8) + const maxDelayFromTimeout = this.interval * 1000 * 0.8; + if (delay >= maxDelayFromTimeout) { + throw new Error(`Screenshot delay must be less than ${maxDelayFromTimeout}ms (0.8 × interval)`); + } + + // Must not exceed 0.5 * interval to prevent blocking next check + const maxDelayFromInterval = this.interval * 1000 * 0.5; + if (delay >= maxDelayFromInterval) { + throw new Error(`Screenshot delay must be less than ${maxDelayFromInterval}ms (0.5 × interval)`); + } + } + } + + if (this.type === "mongodb" && this.databaseQuery) { + // Validate that databaseQuery is valid JSON + try { + JSON.parse(this.databaseQuery); + } catch (error) { + throw new Error(`Invalid JSON in database query: ${error.message}`); + } + } + } + + /** + * Gets monitor notification of multiple monitor + * @param {Array} monitorIDs IDs of monitor to get + * @returns {Promise>} object + */ + static async getMonitorNotification(monitorIDs) { + return await R.getAll( + ` + SELECT monitor_notification.monitor_id, monitor_notification.notification_id + FROM monitor_notification + WHERE monitor_notification.monitor_id IN (${monitorIDs.map((_) => "?").join(",")}) + `, + monitorIDs + ); + } + + /** + * Gets monitor tags of multiple monitor + * @param {Array} monitorIDs IDs of monitor to get + * @returns {Promise>} object + */ + static async getMonitorTag(monitorIDs) { + return await R.getAll( + ` + SELECT monitor_tag.monitor_id, monitor_tag.tag_id, monitor_tag.value, tag.name, tag.color + FROM monitor_tag + JOIN tag ON monitor_tag.tag_id = tag.id + WHERE monitor_tag.monitor_id IN (${monitorIDs.map((_) => "?").join(",")}) + `, + monitorIDs + ); + } + + /** + * prepare preloaded data for efficient access + * @param {Array} monitorData IDs & active field of monitor to get + * @returns {Promise>} object + */ + static async preparePreloadData(monitorData) { + const notificationsMap = new Map(); + const tagsMap = new Map(); + const maintenanceStatusMap = new Map(); + const childrenIDsMap = new Map(); + const activeStatusMap = new Map(); + const forceInactiveMap = new Map(); + const pathsMap = new Map(); + + if (monitorData.length > 0) { + const monitorIDs = monitorData.map((monitor) => monitor.id); + const notifications = await Monitor.getMonitorNotification(monitorIDs); + const tags = await Monitor.getMonitorTag(monitorIDs); + const maintenanceStatuses = await Promise.all( + monitorData.map((monitor) => Monitor.isUnderMaintenance(monitor.id)) + ); + const childrenIDs = await Promise.all(monitorData.map((monitor) => Monitor.getAllChildrenIDs(monitor.id))); + const activeStatuses = await Promise.all( + monitorData.map((monitor) => Monitor.isActive(monitor.id, monitor.active)) + ); + const forceInactiveStatuses = await Promise.all( + monitorData.map((monitor) => Monitor.isParentActive(monitor.id)) + ); + const paths = await Promise.all(monitorData.map((monitor) => Monitor.getAllPath(monitor.id, monitor.name))); + + notifications.forEach((row) => { + if (!notificationsMap.has(row.monitor_id)) { + notificationsMap.set(row.monitor_id, {}); + } + notificationsMap.get(row.monitor_id)[row.notification_id] = true; + }); + + tags.forEach((row) => { + if (!tagsMap.has(row.monitor_id)) { + tagsMap.set(row.monitor_id, []); + } + tagsMap.get(row.monitor_id).push({ + tag_id: row.tag_id, + monitor_id: row.monitor_id, + value: row.value, + name: row.name, + color: row.color, + }); + }); + + monitorData.forEach((monitor, index) => { + maintenanceStatusMap.set(monitor.id, maintenanceStatuses[index]); + }); + + monitorData.forEach((monitor, index) => { + childrenIDsMap.set(monitor.id, childrenIDs[index]); + }); + + monitorData.forEach((monitor, index) => { + activeStatusMap.set(monitor.id, activeStatuses[index]); + }); + + monitorData.forEach((monitor, index) => { + forceInactiveMap.set(monitor.id, !forceInactiveStatuses[index]); + }); + + monitorData.forEach((monitor, index) => { + pathsMap.set(monitor.id, paths[index]); + }); + } + + return { + notifications: notificationsMap, + tags: tagsMap, + maintenanceStatus: maintenanceStatusMap, + childrenIDs: childrenIDsMap, + activeStatus: activeStatusMap, + forceInactive: forceInactiveMap, + paths: pathsMap, + }; + } + + /** + * Gets Parent of the monitor + * @param {number} monitorID ID of monitor to get + * @returns {Promise>} Parent + */ + static async getParent(monitorID) { + return await R.getRow( + ` + SELECT parent.* FROM monitor parent + LEFT JOIN monitor child + ON child.parent = parent.id + WHERE child.id = ? + `, + [monitorID] + ); + } + + /** + * Gets all Children of the monitor + * @param {number} monitorID ID of monitor to get + * @returns {Promise[]>} Children + */ + static async getChildren(monitorID) { + return await R.getAll( + ` + SELECT * FROM monitor + WHERE parent = ? + `, + [monitorID] + ); + } + + /** + * Gets the full path + * @param {number} monitorID ID of the monitor to get + * @param {string} name of the monitor to get + * @returns {Promise} Full path (includes groups and the name) of the monitor + */ + static async getAllPath(monitorID, name) { + const path = [name]; + + if (this.parent === null) { + return path; + } + + let parent = await Monitor.getParent(monitorID); + while (parent !== null) { + path.unshift(parent.name); + parent = await Monitor.getParent(parent.id); + } + + return path; + } + + /** + * Gets recursive all child ids + * @param {number} monitorID ID of the monitor to get + * @returns {Promise} IDs of all children + */ + static async getAllChildrenIDs(monitorID) { + const childs = await Monitor.getChildren(monitorID); + + if (childs === null) { + return []; + } + + let childrenIDs = []; + + for (const child of childs) { + childrenIDs.push(child.id); + childrenIDs = childrenIDs.concat(await Monitor.getAllChildrenIDs(child.id)); + } + + return childrenIDs; + } + + /** + * Unlinks all children of the group monitor + * @param {number} groupID ID of group to remove children of + * @returns {Promise} + */ + static async unlinkAllChildren(groupID) { + return await R.exec("UPDATE `monitor` SET parent = ? WHERE parent = ? ", [null, groupID]); + } + + /** + * Delete a monitor from the system + * @param {number} monitorID ID of the monitor to delete + * @returns {Promise} + */ + static async deleteMonitor(monitorID) { + const server = UptimeKumaServer.getInstance(); + + // Stop the monitor if it's running + if (monitorID in server.monitorList) { + await server.monitorList[monitorID].stop(); + delete server.monitorList[monitorID]; + } + + // Delete from database + await R.exec("DELETE FROM monitor WHERE id = ? ", [monitorID]); + } + + /** + * Recursively delete a monitor and all its descendants + * @param {number} monitorID ID of the monitor to delete + * @returns {Promise} + */ + static async deleteMonitorRecursively(monitorID) { + // Check if this monitor is a group + const monitor = await R.findOne("monitor", " id = ? ", [monitorID]); + + if (monitor && monitor.type === "group") { + // Get all children and delete them recursively + const children = await Monitor.getChildren(monitorID); + if (children && children.length > 0) { + for (const child of children) { + await Monitor.deleteMonitorRecursively(child.id); + } + } + } + + // Delete the monitor itself + await Monitor.deleteMonitor(monitorID); + } + + /** + * Checks recursive if parent (ancestors) are active + * @param {number} monitorID ID of the monitor to get + * @returns {Promise} Is the parent monitor active? + */ + static async isParentActive(monitorID) { + const parent = await Monitor.getParent(monitorID); + + if (parent === null) { + return true; + } + + const parentActive = await Monitor.isParentActive(parent.id); + return parent.active === 1 && parentActive; + } + + /** + * Obtains a new Oidc Token + * @returns {Promise} OAuthProvider client + */ + async makeOidcTokenClientCredentialsRequest() { + log.debug("monitor", `[${this.name}] The oauth access-token undefined or expired. Requesting a new token`); + const oAuthAccessToken = await getOidcTokenClientCredentials( + this.oauth_token_url, + this.oauth_client_id, + this.oauth_client_secret, + this.oauth_scopes, + this.oauth_audience, + this.oauth_auth_method + ); + if (this.oauthAccessToken?.expires_at) { + log.debug( + "monitor", + `[${this.name}] Obtained oauth access-token. Expires at ${new Date(this.oauthAccessToken?.expires_at * 1000)}` + ); + } else { + log.debug("monitor", `[${this.name}] Obtained oauth access-token. Time until expiry was not provided`); + } + + return oAuthAccessToken; + } + + /** + * Store TLS certificate information and check for expiry + * @param {object} tlsInfo Information about the TLS connection + * @returns {Promise} + */ + async handleTlsInfo(tlsInfo) { + await this.updateTlsInfo(tlsInfo); + this.prometheus?.update(null, tlsInfo, null); + + if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) { + log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`); + await checkCertExpiryNotifications(this, tlsInfo); + } + } +} + +module.exports = Monitor; diff --git a/server/model/proxy.js b/server/model/proxy.js new file mode 100644 index 0000000..ec78403 --- /dev/null +++ b/server/model/proxy.js @@ -0,0 +1,25 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); + +class Proxy extends BeanModel { + /** + * Return an object that ready to parse to JSON + * @returns {object} Object ready to parse + */ + toJSON() { + return { + id: this._id, + userId: this._user_id, + protocol: this._protocol, + host: this._host, + port: this._port, + auth: !!this._auth, + username: this._username, + password: this._password, + active: !!this._active, + default: !!this._default, + createdDate: this._created_date, + }; + } +} + +module.exports = Proxy; diff --git a/server/model/remote_browser.js b/server/model/remote_browser.js new file mode 100644 index 0000000..49299ad --- /dev/null +++ b/server/model/remote_browser.js @@ -0,0 +1,17 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); + +class RemoteBrowser extends BeanModel { + /** + * Returns an object that ready to parse to JSON + * @returns {object} Object ready to parse + */ + toJSON() { + return { + id: this.id, + url: this.url, + name: this.name, + }; + } +} + +module.exports = RemoteBrowser; diff --git a/server/model/status_page.js b/server/model/status_page.js new file mode 100644 index 0000000..66728d3 --- /dev/null +++ b/server/model/status_page.js @@ -0,0 +1,590 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); +const { R } = require("redbean-node"); +const cheerio = require("cheerio"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); +const jsesc = require("jsesc"); +const analytics = require("../analytics/analytics"); +const { marked } = require("marked"); +const { Feed } = require("feed"); +const config = require("../config"); +const dayjs = require("dayjs"); + +const { setting } = require("../util-server"); +const { + STATUS_PAGE_ALL_DOWN, + STATUS_PAGE_ALL_UP, + STATUS_PAGE_MAINTENANCE, + STATUS_PAGE_PARTIAL_DOWN, + UP, + MAINTENANCE, + DOWN, + INCIDENT_PAGE_SIZE, +} = require("../../src/util"); + +class StatusPage extends BeanModel { + /** + * Like this: { "test-uptime.kuma.pet": "default" } + * @type {{}} + */ + static domainMappingList = {}; + + /** + * Handle responses to RSS pages + * @param {Response} response Response object + * @param {string} slug Status page slug + * @param {Request} request Request object + * @returns {Promise} + */ + static async handleStatusPageRSSResponse(response, slug, request) { + let statusPage = await R.findOne("status_page", " slug = ? ", [slug]); + + if (statusPage) { + const feedUrl = await StatusPage.buildRSSUrl(slug, request); + response.type("application/rss+xml"); + response.send(await StatusPage.renderRSS(statusPage, feedUrl)); + } else { + response.status(404).send(UptimeKumaServer.getInstance().indexHTML); + } + } + + /** + * Handle responses to status page + * @param {Response} response Response object + * @param {string} indexHTML HTML to render + * @param {string} slug Status page slug + * @returns {Promise} + */ + static async handleStatusPageResponse(response, indexHTML, slug) { + // Handle url with trailing slash (http://localhost:3001/status/) + // The slug comes from the route "/status/:slug". If the slug is empty, express converts it to "index.html" + if (slug === "index.html") { + slug = "default"; + } + + let statusPage = await R.findOne("status_page", " slug = ? ", [slug]); + + if (statusPage) { + response.send(await StatusPage.renderHTML(indexHTML, statusPage)); + } else { + response.status(404).send(UptimeKumaServer.getInstance().indexHTML); + } + } + + /** + * SSR for RSS feed + * @param {StatusPage} statusPage Status page object + * @param {string} feedUrl The URL for the RSS feed + * @returns {Promise} The rendered RSS XML + */ + static async renderRSS(statusPage, feedUrl) { + const { incidents, heartbeats, statusDescription } = await StatusPage.getRSSPageData(statusPage); + + // Use custom RSS title if set, otherwise fall back to status page title + let feedTitle = "Uptime Kuma RSS Feed"; + if (statusPage.rss_title) { + feedTitle = statusPage.rss_title; + } else if (statusPage.title) { + feedTitle = `${statusPage.title} RSS Feed`; + } + + const feed = new Feed({ + title: feedTitle, + description: `Current status: ${statusDescription}`, + link: feedUrl, + language: "en", // optional, used only in RSS 2.0, possible values: http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes + updated: new Date(), // optional, default = today + }); + + incidents.forEach((incident) => { + let lastUpdatedDate = incident.lastUpdatedDate || incident.createdDate; + feed.addItem({ + title: incident.title, + description: incident.content, + id: `i${incident.id}-${lastUpdatedDate}`, + link: feedUrl, + date: dayjs.utc(lastUpdatedDate).toDate(), + }); + }); + + heartbeats.forEach((heartbeat) => { + feed.addItem({ + title: `${heartbeat.name} is down`, + description: `${heartbeat.name} has been down since ${heartbeat.time} UTC`, + id: `${heartbeat.monitorID}-${heartbeat.time}`, + link: feedUrl, + date: dayjs.utc(heartbeat.time).toDate(), + }); + }); + + return feed.rss2(); + } + + /** + * Build RSS feed URL, handling proxy headers + * @param {string} slug Status page slug + * @param {Request} request Express request object + * @returns {Promise} The full URL for the RSS feed + */ + static async buildRSSUrl(slug, request) { + if (request) { + const trustProxy = await setting("trustProxy"); + + // Determine protocol (check X-Forwarded-Proto if behind proxy) + let proto = request.protocol; + if (trustProxy && request.headers["x-forwarded-proto"]) { + proto = request.headers["x-forwarded-proto"].split(",")[0].trim(); + } + + // Determine host (check X-Forwarded-Host if behind proxy) + let host = request.get("host"); + if (trustProxy && request.headers["x-forwarded-host"]) { + host = request.headers["x-forwarded-host"]; + } + + return `${proto}://${host}/status/${slug}`; + } + + // Fallback to config values + const proto = config.isSSL ? "https" : "http"; + const host = config.hostname || "localhost"; + const port = config.port; + return `${proto}://${host}:${port}/status/${slug}`; + } + + /** + * SSR for status pages + * @param {string} indexHTML HTML page to render + * @param {StatusPage} statusPage Status page populate HTML with + * @returns {Promise} the rendered html + */ + static async renderHTML(indexHTML, statusPage) { + const $ = cheerio.load(indexHTML); + + const description155 = marked(statusPage.description ?? "") + .replace(/<[^>]+>/gm, "") + .trim() + .substring(0, 155); + + $("title").text(statusPage.title); + $("meta[name=description]").attr("content", description155); + + if (statusPage.icon) { + $("link[rel=icon]").attr("href", statusPage.icon).removeAttr("type"); + + $("link[rel=apple-touch-icon]").remove(); + } + + const head = $("head"); + + if (analytics.isValidAnalyticsConfig(statusPage)) { + let escapedAnalyticsScript = analytics.getAnalyticsScript(statusPage); + head.append($(escapedAnalyticsScript)); + } + + // OG Meta Tags + let ogTitle = $('').attr("content", statusPage.title); + head.append(ogTitle); + + let ogDescription = $('').attr("content", description155); + head.append(ogDescription); + + let ogType = $(''); + 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, + }); + + const script = $(` + + `); + + head.append(script); + + // manifest.json + $("link[rel=manifest]").attr("href", `/api/status-page/${statusPage.slug}/manifest.json`); + + return $.root().html(); + } + + /** + * @param {heartbeats} heartbeats from getRSSPageData + * @returns {number} status_page constant from util.ts + */ + static overallStatus(heartbeats) { + if (heartbeats.length === 0) { + return -1; + } + + let status = STATUS_PAGE_ALL_UP; + let hasUp = false; + + for (let beat of heartbeats) { + if (beat.status === MAINTENANCE) { + return STATUS_PAGE_MAINTENANCE; + } else if (beat.status === UP) { + hasUp = true; + } else { + status = STATUS_PAGE_PARTIAL_DOWN; + } + } + + if (!hasUp) { + status = STATUS_PAGE_ALL_DOWN; + } + + return status; + } + + /** + * @param {number} status from overallStatus + * @returns {string} description + */ + static getStatusDescription(status) { + if (status === -1) { + return "No Services"; + } + + if (status === STATUS_PAGE_ALL_UP) { + return "All Systems Operational"; + } + + if (status === STATUS_PAGE_PARTIAL_DOWN) { + return "Partially Degraded Service"; + } + + if (status === STATUS_PAGE_ALL_DOWN) { + return "Degraded Service"; + } + + // TODO: show the real maintenance information: title, description, time + if (status === MAINTENANCE) { + return "Under maintenance"; + } + + return "?"; + } + + /** + * Get all data required for RSS + * @param {StatusPage} statusPage Status page to get data for + * @returns {object} Status page data + */ + static async getRSSPageData(statusPage) { + const { incidents, publicGroupList } = await StatusPage.getStatusPageData(statusPage); + + let heartbeats = []; + + for (let monitorGroup of publicGroupList) { + for (const monitor of monitorGroup.monitorList) { + 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, + }); + } + } + } + + // calculate RSS feed description + let status = StatusPage.overallStatus(heartbeats); + let statusDescription = StatusPage.getStatusDescription(status); + + // keep only DOWN heartbeats in the RSS feed + const downHeartbeats = heartbeats.filter((heartbeat) => heartbeat.status === DOWN); + + return { + incidents, + heartbeats: downHeartbeats, + statusDescription, + }; + } + + /** + * Get all status page data in one call + * @param {StatusPage} statusPage Status page to get data for + * @returns {object} Status page data + */ + static async getStatusPageData(statusPage) { + const config = await statusPage.toPublicJSON(); + + // All active incidents + let incidents = await R.find( + "incident", + "pin = 1 AND active = 1 AND status_page_id = ? ORDER BY created_date DESC", + [statusPage.id] + ); + incidents = incidents.map((i) => i.toPublicJSON()); + + let maintenanceList = await StatusPage.getMaintenanceList(statusPage.id); + + // Public Group List + const publicGroupList = []; + const showTags = !!statusPage.show_tags; + + 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); + publicGroupList.push(monitorGroup); + } + + // Response + return { + config, + incidents, + publicGroupList, + maintenanceList, + }; + } + + /** + * Loads domain mapping from DB + * Return object like this: { "test-uptime.kuma.pet": "default" } + * @returns {Promise} + */ + static async loadDomainMappingList() { + StatusPage.domainMappingList = await R.getAssoc(` + SELECT domain, slug + FROM status_page, status_page_cname + WHERE status_page.id = status_page_cname.status_page_id + `); + } + + /** + * Send status page list to client + * @param {Server} io io Socket server instance + * @param {Socket} socket Socket.io instance + * @returns {Promise} Status page list + */ + static async sendStatusPageList(io, socket) { + let result = {}; + + let list = await R.findAll("status_page", " ORDER BY title "); + + for (let item of list) { + result[item.id] = await item.toJSON(); + } + + io.to(socket.userID).emit("statusPageList", result); + return list; + } + + /** + * Update list of domain names + * @param {string[]} domainNameList List of status page domains + * @returns {Promise} + */ + 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]); + + try { + for (let domain of domainNameList) { + if (typeof domain !== "string") { + throw new Error("Invalid domain"); + } + + if (domain.trim() === "") { + continue; + } + + // If the domain name is used in another status page, delete it + await trx.exec("DELETE FROM status_page_cname WHERE domain = ?", [domain]); + + let mapping = trx.dispense("status_page_cname"); + mapping.status_page_id = this.id; + mapping.domain = domain; + await trx.store(mapping); + } + await trx.commit(); + } catch (error) { + await trx.rollback(); + throw error; + } + } + + /** + * Get list of domain names + * @returns {object[]} List of status page domains + */ + getDomainNameList() { + let domainList = []; + for (let domain in StatusPage.domainMappingList) { + let s = StatusPage.domainMappingList[domain]; + + if (this.slug === s) { + domainList.push(domain); + } + } + return domainList; + } + + /** + * Return an object that ready to parse to JSON + * @returns {object} Object ready to parse + */ + async toJSON() { + return { + id: this.id, + slug: this.slug, + title: this.title, + description: this.description, + icon: this.getIcon(), + theme: this.theme, + autoRefreshInterval: this.autoRefreshInterval, + published: !!this.published, + showTags: !!this.show_tags, + domainNameList: this.getDomainNameList(), + customCSS: this.custom_css, + footerText: this.footer_text, + showPoweredBy: !!this.show_powered_by, + analyticsId: this.analytics_id, + analyticsScriptUrl: this.analytics_script_url, + analyticsType: this.analytics_type, + showCertificateExpiry: !!this.show_certificate_expiry, + showOnlyLastHeartbeat: !!this.show_only_last_heartbeat, + rssTitle: this.rss_title, + }; + } + + /** + * Return an object that ready to parse to JSON for public + * Only show necessary data to public + * @returns {object} Object ready to parse + */ + async toPublicJSON() { + return { + slug: this.slug, + title: this.title, + description: this.description, + icon: this.getIcon(), + autoRefreshInterval: this.autoRefreshInterval, + theme: this.theme, + published: !!this.published, + showTags: !!this.show_tags, + customCSS: this.custom_css, + footerText: this.footer_text, + showPoweredBy: !!this.show_powered_by, + analyticsId: this.analytics_id, + analyticsScriptUrl: this.analytics_script_url, + analyticsType: this.analytics_type, + showCertificateExpiry: !!this.show_certificate_expiry, + showOnlyLastHeartbeat: !!this.show_only_last_heartbeat, + rssTitle: this.rss_title, + }; + } + + /** + * Convert slug to status page ID + * @param {string} slug Status page slug + * @returns {Promise} ID of status page + */ + static async slugToID(slug) { + return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [slug]); + } + + /** + * Get path to the icon for the page + * @returns {string} Path + */ + getIcon() { + if (!this.icon) { + return "/icon.svg"; + } else { + return this.icon; + } + } + + /** + * Get paginated incident history for a status page using cursor-based pagination + * @param {number} statusPageId ID of the status page + * @param {string|null} cursor ISO date string cursor (created_date of last item from previous page) + * @param {boolean} isPublic Whether to return public or admin data + * @returns {Promise} Paginated incident data with cursor + */ + static async getIncidentHistory(statusPageId, cursor = null, isPublic = true) { + let incidents; + + if (cursor) { + incidents = await R.find( + "incident", + " status_page_id = ? AND created_date < ? ORDER BY created_date DESC LIMIT ? ", + [statusPageId, cursor, INCIDENT_PAGE_SIZE] + ); + } else { + incidents = await R.find("incident", " status_page_id = ? ORDER BY created_date DESC LIMIT ? ", [ + statusPageId, + INCIDENT_PAGE_SIZE, + ]); + } + + const total = await R.count("incident", " status_page_id = ? ", [statusPageId]); + + const lastIncident = incidents[incidents.length - 1]; + let nextCursor = null; + let hasMore = false; + + if (lastIncident) { + const moreCount = await R.count("incident", " status_page_id = ? AND created_date < ? ", [ + statusPageId, + lastIncident.created_date, + ]); + hasMore = moreCount > 0; + if (hasMore) { + nextCursor = lastIncident.created_date; + } + } + + return { + incidents: incidents.map((i) => i.toPublicJSON()), + total, + nextCursor, + hasMore, + }; + } + + /** + * Get list of maintenances + * @param {number} statusPageId ID of status page to get maintenance for + * @returns {object} Object representing maintenances sanitized for public + */ + static async getMaintenanceList(statusPageId) { + try { + const publicMaintenanceList = []; + + let maintenanceIDList = await R.getCol( + ` + SELECT DISTINCT maintenance_id + FROM maintenance_status_page + WHERE status_page_id = ? + `, + [statusPageId] + ); + + for (const maintenanceID of maintenanceIDList) { + let maintenance = UptimeKumaServer.getInstance().getMaintenance(maintenanceID); + if (maintenance && (await maintenance.isUnderMaintenance())) { + publicMaintenanceList.push(await maintenance.toPublicJSON()); + } + } + + return publicMaintenanceList; + } catch (error) { + return []; + } + } +} + +module.exports = StatusPage; diff --git a/server/model/tag.js b/server/model/tag.js new file mode 100644 index 0000000..9f5709f --- /dev/null +++ b/server/model/tag.js @@ -0,0 +1,17 @@ +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 + */ + toJSON() { + return { + id: this._id, + name: this._name, + color: this._color, + }; + } +} + +module.exports = Tag; diff --git a/server/model/user.js b/server/model/user.js new file mode 100644 index 0000000..b51259e --- /dev/null +++ b/server/model/user.js @@ -0,0 +1,5 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); + +class User extends BeanModel {} + +module.exports = User; diff --git a/server/modules/apicache/apicache.js b/server/modules/apicache/apicache.js new file mode 100644 index 0000000..5e68ee0 --- /dev/null +++ b/server/modules/apicache/apicache.js @@ -0,0 +1,905 @@ +let url = require("url"); +let MemoryCache = require("./memory-cache"); + +let t = { + ms: 1, + second: 1000, + minute: 60000, + hour: 3600000, + day: 3600000 * 24, + week: 3600000 * 24 * 7, + month: 3600000 * 24 * 30, +}; + +let instances = []; + +/** + * Does a === b + * @param {any} a + * @returns {function(any): boolean} + */ +let matches = function (a) { + return function (b) { + return a === b; + }; +}; + +/** + * Does a!==b + * @param {any} a + * @returns {function(any): boolean} + */ +let doesntMatch = function (a) { + return function (b) { + return !matches(a)(b); + }; +}; + +/** + * Get log duration + * @param {number} d Time in ms + * @param {string} prefix Prefix for log + * @returns {string} Coloured log string + */ +let logDuration = function (d, prefix) { + let str = d > 1000 ? (d / 1000).toFixed(2) + "sec" : d + "ms"; + return "\x1b[33m- " + (prefix ? prefix + " " : "") + str + "\x1b[0m"; +}; + +/** + * Get safe headers + * @param {Object} res Express response object + * @returns {Object} + */ +function getSafeHeaders(res) { + return res.getHeaders ? res.getHeaders() : res._headers; +} + +/** Constructor for ApiCache instance */ +function ApiCache() { + let memCache = new MemoryCache(); + + let globalOptions = { + debug: false, + defaultDuration: 3600000, + enabled: true, + appendKey: [], + jsonp: false, + redisClient: false, + headerBlacklist: [], + statusCodes: { + include: [], + exclude: [], + }, + events: { + expire: undefined, + }, + headers: { + // 'cache-control': 'no-cache' // example of header overwrite + }, + trackPerformance: false, + respectCacheControl: false, + }; + + let middlewareOptions = []; + let instance = this; + let index = null; + let timers = {}; + let performanceArray = []; // for tracking cache hit rate + + instances.push(this); + this.id = instances.length; + + /** + * Logs a message to the console if the `DEBUG` environment variable is set. + * @param {string} a The first argument to log. + * @param {string} b The second argument to log. + * @param {string} c The third argument to log. + * @param {string} d The fourth argument to log, and so on... (optional) + * + * Generated by Trelent + */ + function debug(a, b, c, d) { + let arr = ["\x1b[36m[apicache]\x1b[0m", a, b, c, d].filter(function (arg) { + return arg !== undefined; + }); + let debugEnv = process.env.DEBUG && process.env.DEBUG.split(",").indexOf("apicache") !== -1; + + return (globalOptions.debug || debugEnv) && console.log.apply(null, arr); + } + + /** + * Returns true if the given request and response should be logged. + * @param {Object} request The HTTP request object. + * @param {Object} response The HTTP response object. + * @param {function(Object, Object):boolean} toggle + * @returns {boolean} + */ + function shouldCacheResponse(request, response, toggle) { + let opt = globalOptions; + let codes = opt.statusCodes; + + if (!response) { + return false; + } + + if (toggle && !toggle(request, response)) { + return false; + } + + if (codes.exclude && codes.exclude.length && codes.exclude.indexOf(response.statusCode) !== -1) { + return false; + } + if (codes.include && codes.include.length && codes.include.indexOf(response.statusCode) === -1) { + return false; + } + + return true; + } + + /** + * Add key to index array + * @param {string} key Key to add + * @param {Object} req Express request object + */ + function addIndexEntries(key, req) { + let groupName = req.apicacheGroup; + + if (groupName) { + debug('group detected "' + groupName + '"'); + let group = (index.groups[groupName] = index.groups[groupName] || []); + group.unshift(key); + } + + index.all.unshift(key); + } + + /** + * Returns a new object containing only the whitelisted headers. + * @param {Object} headers The original object of header names and + * values. + * @param {string[]} globalOptions.headerWhitelist An array of + * strings representing the whitelisted header names to keep in the + * output object. + * + * Generated by Trelent + */ + function filterBlacklistedHeaders(headers) { + return Object.keys(headers) + .filter(function (key) { + return globalOptions.headerBlacklist.indexOf(key) === -1; + }) + .reduce(function (acc, header) { + acc[header] = headers[header]; + return acc; + }, {}); + } + + /** + * Create a cache object + * @param {Object} headers The response headers to filter. + * @returns {Object} A new object containing only the whitelisted + * response headers. + * + * Generated by Trelent + */ + function createCacheObject(status, headers, data, encoding) { + return { + status: status, + headers: filterBlacklistedHeaders(headers), + data: data, + encoding: encoding, + timestamp: new Date().getTime() / 1000, // seconds since epoch. This is used to properly decrement max-age headers in cached responses. + }; + } + + /** + * Sets a cache value for the given key. + * @param {string} key The cache key to set. + * @param {any} value The cache value to set. + * @param {number} duration How long in milliseconds the cached + * response should be valid for (defaults to 1 hour). + * + * Generated by Trelent + */ + function cacheResponse(key, value, duration) { + let redis = globalOptions.redisClient; + let expireCallback = globalOptions.events.expire; + + if (redis && redis.connected) { + try { + redis.hset(key, "response", JSON.stringify(value)); + redis.hset(key, "duration", duration); + redis.expire(key, duration / 1000, expireCallback || function () {}); + } catch (err) { + debug("[apicache] error in redis.hset()"); + } + } else { + memCache.add(key, value, duration, expireCallback); + } + + // add automatic cache clearing from duration, includes max limit on setTimeout + timers[key] = setTimeout( + function () { + instance.clear(key, true); + }, + Math.min(duration, 2147483647) + ); + } + + /** + * Appends content to the response. + * @param {Object} res Express response object + * @param {(string|Buffer)} content The content to append. + * + * Generated by Trelent + */ + function accumulateContent(res, content) { + if (content) { + if (typeof content == "string") { + res._apicache.content = (res._apicache.content || "") + content; + } else if (Buffer.isBuffer(content)) { + let oldContent = res._apicache.content; + + if (typeof oldContent === "string") { + oldContent = !Buffer.from ? new Buffer(oldContent) : Buffer.from(oldContent); + } + + if (!oldContent) { + oldContent = !Buffer.alloc ? new Buffer(0) : Buffer.alloc(0); + } + + res._apicache.content = Buffer.concat([oldContent, content], oldContent.length + content.length); + } else { + res._apicache.content = content; + } + } + } + + /** + * Monkeypatches the response object to add cache control headers + * and create a cache object. + * @param {Object} req Express request object + * @param {Object} res Express response object + * @param {function} next Function to call next + * @param {string} key Key to add response as + * @param {number} duration Time to cache response for + * @param {string} strDuration Duration in string form + * @param {function(Object, Object):boolean} toggle + */ + function makeResponseCacheable(req, res, next, key, duration, strDuration, toggle) { + // monkeypatch res.end to create cache object + res._apicache = { + write: res.write, + writeHead: res.writeHead, + end: res.end, + cacheable: true, + content: undefined, + }; + + // append header overwrites if applicable + Object.keys(globalOptions.headers).forEach(function (name) { + res.setHeader(name, globalOptions.headers[name]); + }); + + res.writeHead = function () { + // add cache control headers + if (!globalOptions.headers["cache-control"]) { + if (shouldCacheResponse(req, res, toggle)) { + res.setHeader("cache-control", "max-age=" + (duration / 1000).toFixed(0)); + } else { + res.setHeader("cache-control", "no-cache, no-store, must-revalidate"); + } + } + + res._apicache.headers = Object.assign({}, getSafeHeaders(res)); + return res._apicache.writeHead.apply(this, arguments); + }; + + // patch res.write + res.write = function (content) { + accumulateContent(res, content); + return res._apicache.write.apply(this, arguments); + }; + + // patch res.end + res.end = function (content, encoding) { + if (shouldCacheResponse(req, res, toggle)) { + accumulateContent(res, content); + + 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); + cacheResponse(key, cacheObject, duration); + + // display log entry + let elapsed = new Date() - req.apicacheTimer; + debug('adding cache entry for "' + key + '" @ ' + strDuration, logDuration(elapsed)); + debug("_apicache.headers: ", res._apicache.headers); + debug("res.getHeaders(): ", getSafeHeaders(res)); + debug("cacheObject: ", cacheObject); + } + } + + return res._apicache.end.apply(this, arguments); + }; + + next(); + } + + /** + * Send a cached response to client + * @param {Request} request Express request object + * @param {Response} response Express response object + * @param {object} cacheObject Cache object to send + * @param {function(Object, Object):boolean} toggle + * @param {function} next Function to call next + * @param {number} duration Not used + * @returns {boolean|undefined} true if the request should be + * cached, false otherwise. If undefined, defaults to true. + */ + function sendCachedResponse(request, response, cacheObject, toggle, next, duration) { + if (toggle && !toggle(request, response)) { + return next(); + } + + let headers = getSafeHeaders(response); + + // Modified by @louislam, removed Cache-control, since I don't need client side cache! + // Original Source: https://github.com/kwhitley/apicache/blob/0d5686cc21fad353c6dddee646288c2fca3e4f50/src/apicache.js#L254 + Object.assign(headers, filterBlacklistedHeaders(cacheObject.headers || {})); + + // only embed apicache headers when not in production environment + if (process.env.NODE_ENV !== "production") { + Object.assign(headers, { + "apicache-store": globalOptions.redisClient ? "redis" : "memory", + "apicache-version": "1.6.2-modified", + }); + } + + // 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); + } + + // test Etag against If-None-Match for 304 + let cachedEtag = cacheObject.headers.etag; + let requestEtag = request.headers["if-none-match"]; + + if (requestEtag && cachedEtag === requestEtag) { + delete headers["content-length"]; + delete headers["content-type"]; + response.writeHead(304, headers); + return response.end(); + } + + response.writeHead(cacheObject.status || 200, headers); + + return response.end(data, cacheObject.encoding); + } + + /** Sync caching options */ + function syncOptions() { + for (let i in middlewareOptions) { + Object.assign(middlewareOptions[i].options, globalOptions, middlewareOptions[i].localOptions); + } + } + + /** + * Clear key from cache + * @param {string} target Key to clear + * @param {boolean} isAutomatic Is the key being cleared automatically + * @returns {number} + */ + this.clear = function (target, isAutomatic) { + let group = index.groups[target]; + let redis = globalOptions.redisClient; + + if (group) { + debug('clearing group "' + target + '"'); + + group.forEach(function (key) { + debug('clearing cached entry for "' + key + '"'); + clearTimeout(timers[key]); + delete timers[key]; + if (!globalOptions.redisClient) { + memCache.delete(key); + } else { + try { + redis.del(key); + } catch (err) { + console.log('[apicache] error in redis.del("' + key + '")'); + } + } + index.all = index.all.filter(doesntMatch(key)); + }); + + delete index.groups[target]; + } else if (target) { + debug("clearing " + (isAutomatic ? "expired" : "cached") + ' entry for "' + target + '"'); + clearTimeout(timers[target]); + delete timers[target]; + // clear actual cached entry + if (!redis) { + memCache.delete(target); + } else { + try { + redis.del(target); + } catch (err) { + console.log('[apicache] error in redis.del("' + target + '")'); + } + } + + // remove from global index + index.all = index.all.filter(doesntMatch(target)); + + // remove target from each group that it may exist in + Object.keys(index.groups).forEach(function (groupName) { + index.groups[groupName] = index.groups[groupName].filter(doesntMatch(target)); + + // delete group if now empty + if (!index.groups[groupName].length) { + delete index.groups[groupName]; + } + }); + } else { + debug("clearing entire index"); + + if (!redis) { + memCache.clear(); + } else { + // clear redis keys one by one from internal index to prevent clearing non-apicache entries + index.all.forEach(function (key) { + clearTimeout(timers[key]); + delete timers[key]; + try { + redis.del(key); + } catch (err) { + console.log('[apicache] error in redis.del("' + key + '")'); + } + }); + } + this.resetIndex(); + } + + return this.getIndex(); + }; + + /** + * Converts a duration string to an integer number of milliseconds. + * @param {(string|number)} duration The string to convert. + * @param {number} defaultDuration The default duration to return if + * can't parse duration + * @returns {number} The converted value in milliseconds, or the + * defaultDuration if it can't be parsed. + */ + function parseDuration(duration, defaultDuration) { + if (typeof duration === "number") { + return duration; + } + + if (typeof duration === "string") { + let split = duration.match(/^([\d\.,]+)\s?([a-zA-Z]+)$/); + + if (split.length === 3) { + let len = parseFloat(split[1]); + let unit = split[2].replace(/s$/i, "").toLowerCase(); + if (unit === "m") { + unit = "ms"; + } + + return (len || 1) * (t[unit] || 0); + } + } + + return defaultDuration; + } + + /** + * Parse duration + * @param {(number|string)} duration + * @returns {number} Duration parsed to a number + */ + this.getDuration = function (duration) { + return parseDuration(duration, globalOptions.defaultDuration); + }; + + /** + * Return cache performance statistics (hit rate). Suitable for + * putting into a route: + * + * app.get('/api/cache/performance', (req, res) => { + * res.json(apicache.getPerformance()) + * }) + * + * @returns {any[]} + */ + this.getPerformance = function () { + return performanceArray.map(function (p) { + return p.report(); + }); + }; + + /** + * Get index of a group + * @param {string} group + * @returns {number} + */ + this.getIndex = function (group) { + if (group) { + return index.groups[group]; + } else { + return index; + } + }; + + /** + * Express middleware + * @param {(string|number)} strDuration Duration to cache responses + * for. + * @param {function(Object, Object):boolean} middlewareToggle + * @param {Object} localOptions Options for APICache + * @returns + */ + this.middleware = function cache(strDuration, middlewareToggle, localOptions) { + let duration = instance.getDuration(strDuration); + let opt = {}; + + middlewareOptions.push({ + options: opt, + }); + + let options = function (localOptions) { + if (localOptions) { + middlewareOptions.find(function (middleware) { + return middleware.options === opt; + }).localOptions = localOptions; + } + + syncOptions(); + + return opt; + }; + + options(localOptions); + + /** + * A Function for non tracking performance + */ + function NOOPCachePerformance() { + this.report = this.hit = this.miss = function () {}; // noop; + } + + /** + * A function for tracking and reporting hit rate. These + * statistics are returned by the getPerformance() call above. + */ + function CachePerformance() { + /** + * Tracks the hit rate for the last 100 requests. If there + * have been fewer than 100 requests, the hit rate just + * considers the requests that have happened. + */ + this.hitsLast100 = new Uint8Array(100 / 4); // each hit is 2 bits + + /** + * Tracks the hit rate for the last 1000 requests. If there + * have been fewer than 1000 requests, the hit rate just + * considers the requests that have happened. + */ + this.hitsLast1000 = new Uint8Array(1000 / 4); // each hit is 2 bits + + /** + * Tracks the hit rate for the last 10000 requests. If there + * have been fewer than 10000 requests, the hit rate just + * considers the requests that have happened. + */ + this.hitsLast10000 = new Uint8Array(10000 / 4); // each hit is 2 bits + + /** + * Tracks the hit rate for the last 100000 requests. If + * there have been fewer than 100000 requests, the hit rate + * just considers the requests that have happened. + */ + this.hitsLast100000 = new Uint8Array(100000 / 4); // each hit is 2 bits + + /** + * The number of calls that have passed through the + * middleware since the server started. + */ + this.callCount = 0; + + /** + * The total number of hits since the server started + */ + this.hitCount = 0; + + /** + * The key from the last cache hit. This is useful in + * identifying which route these statistics apply to. + */ + this.lastCacheHit = null; + + /** + * The key from the last cache miss. This is useful in + * identifying which route these statistics apply to. + */ + this.lastCacheMiss = null; + + /** + * Return performance statistics + * @returns {Object} + */ + this.report = function () { + return { + lastCacheHit: this.lastCacheHit, + lastCacheMiss: this.lastCacheMiss, + callCount: this.callCount, + hitCount: this.hitCount, + missCount: this.callCount - this.hitCount, + hitRate: this.callCount == 0 ? null : this.hitCount / this.callCount, + hitRateLast100: this.hitRate(this.hitsLast100), + hitRateLast1000: this.hitRate(this.hitsLast1000), + hitRateLast10000: this.hitRate(this.hitsLast10000), + hitRateLast100000: this.hitRate(this.hitsLast100000), + }; + }; + + /** + * Computes a cache hit rate from an array of hits and + * misses. + * @param {Uint8Array} array An array representing hits and + * misses. + * @returns {?number} a number between 0 and 1, or null if + * the array has no hits or misses + */ + this.hitRate = function (array) { + let hits = 0; + let misses = 0; + for (let i = 0; i < array.length; i++) { + let n8 = array[i]; + for (let j = 0; j < 4; j++) { + switch (n8 & 3) { + case 1: + hits++; + break; + case 2: + misses++; + break; + } + n8 >>= 2; + } + } + let total = hits + misses; + if (total == 0) { + return null; + } + return hits / total; + }; + + /** + * Record a hit or miss in the given array. It will be + * recorded at a position determined by the current value of + * the callCount variable. + * @param {Uint8Array} array An array representing hits and + * misses. + * @param {boolean} hit true for a hit, false for a miss + * Each element in the array is 8 bits, and encodes 4 + * hit/miss records. Each hit or miss is encoded as to bits + * as follows: 00 means no hit or miss has been recorded in + * these bits 01 encodes a hit 10 encodes a miss + */ + this.recordHitInArray = function (array, hit) { + let arrayIndex = ~~(this.callCount / 4) % array.length; + let bitOffset = (this.callCount % 4) * 2; // 2 bits per record, 4 records per uint8 array element + let clearMask = ~(3 << bitOffset); + let record = (hit ? 1 : 2) << bitOffset; + array[arrayIndex] = (array[arrayIndex] & clearMask) | record; + }; + + /** + * Records the hit or miss in the tracking arrays and + * increments the call count. + * @param {boolean} hit true records a hit, false records a + * miss + */ + this.recordHit = function (hit) { + this.recordHitInArray(this.hitsLast100, hit); + this.recordHitInArray(this.hitsLast1000, hit); + this.recordHitInArray(this.hitsLast10000, hit); + this.recordHitInArray(this.hitsLast100000, hit); + if (hit) { + this.hitCount++; + } + this.callCount++; + }; + + /** + * Records a hit event, setting lastCacheMiss to the given key + * @param {string} key The key that had the cache hit + */ + this.hit = function (key) { + this.recordHit(true); + this.lastCacheHit = key; + }; + + /** + * Records a miss event, setting lastCacheMiss to the given key + * @param {string} key The key that had the cache miss + */ + this.miss = function (key) { + this.recordHit(false); + this.lastCacheMiss = key; + }; + } + + let perf = globalOptions.trackPerformance ? new CachePerformance() : new NOOPCachePerformance(); + + performanceArray.push(perf); + + /** + * Cache a request + * @param {Object} req Express request object + * @param {Object} res Express response object + * @param {function} next Function to call next + * @returns {any} + */ + let cache = function (req, res, next) { + function bypass() { + debug("bypass detected, skipping cache."); + return next(); + } + + // initial bypass chances + if (!opt.enabled) { + return bypass(); + } + if ( + req.headers["x-apicache-bypass"] || + req.headers["x-apicache-force-fetch"] || + (opt.respectCacheControl && req.headers["cache-control"] == "no-cache") + ) { + return bypass(); + } + + // REMOVED IN 0.11.1 TO CORRECT MIDDLEWARE TOGGLE EXECUTE ORDER + // if (typeof middlewareToggle === 'function') { + // if (!middlewareToggle(req, res)) return bypass() + // } else if (middlewareToggle !== undefined && !middlewareToggle) { + // return bypass() + // } + + // embed timer + req.apicacheTimer = new Date(); + + // In Express 4.x the url is ambigious based on where a router is mounted. originalUrl will give the full Url + let key = req.originalUrl || req.url; + + // Remove querystring from key if jsonp option is enabled + if (opt.jsonp) { + key = url.parse(key).pathname; + } + + // add appendKey (either custom function or response path) + if (typeof opt.appendKey === "function") { + key += "$$appendKey=" + opt.appendKey(req, res); + } else if (opt.appendKey.length > 0) { + let appendKey = req; + + for (let i = 0; i < opt.appendKey.length; i++) { + appendKey = appendKey[opt.appendKey[i]]; + } + key += "$$appendKey=" + appendKey; + } + + // attempt cache hit + let redis = opt.redisClient; + let cached = !redis ? memCache.getValue(key) : null; + + // send if cache hit from memory-cache + if (cached) { + let elapsed = new Date() - req.apicacheTimer; + debug("sending cached (memory-cache) version of", key, logDuration(elapsed)); + + perf.hit(key); + return sendCachedResponse(req, res, cached, middlewareToggle, next, duration); + } + + // send if cache hit from redis + if (redis && redis.connected) { + try { + redis.hgetall(key, function (err, obj) { + if (!err && obj && obj.response) { + let elapsed = new Date() - req.apicacheTimer; + debug("sending cached (redis) version of", key, logDuration(elapsed)); + + perf.hit(key); + return sendCachedResponse( + req, + res, + JSON.parse(obj.response), + middlewareToggle, + next, + duration + ); + } else { + perf.miss(key); + return makeResponseCacheable(req, res, next, key, duration, strDuration, middlewareToggle); + } + }); + } catch (err) { + // bypass redis on error + perf.miss(key); + return makeResponseCacheable(req, res, next, key, duration, strDuration, middlewareToggle); + } + } else { + perf.miss(key); + return makeResponseCacheable(req, res, next, key, duration, strDuration, middlewareToggle); + } + }; + + cache.options = options; + + return cache; + }; + + /** + * Process options + * @param {Object} options + * @returns {Object} + */ + this.options = function (options) { + if (options) { + Object.assign(globalOptions, options); + syncOptions(); + + if ("defaultDuration" in options) { + // Convert the default duration to a number in milliseconds (if needed) + globalOptions.defaultDuration = parseDuration(globalOptions.defaultDuration, 3600000); + } + + if (globalOptions.trackPerformance) { + debug("WARNING: using trackPerformance flag can cause high memory usage!"); + } + + return this; + } else { + return globalOptions; + } + }; + + /** Reset the index */ + this.resetIndex = function () { + index = { + all: [], + groups: {}, + }; + }; + + /** + * Create a new instance of ApiCache + * @param {Object} config Config to pass + * @returns {ApiCache} + */ + this.newInstance = function (config) { + let instance = new ApiCache(); + + if (config) { + instance.options(config); + } + + return instance; + }; + + /** Clone this instance */ + this.clone = function () { + return this.newInstance(this.options()); + }; + + // initialize index + this.resetIndex(); +} + +module.exports = new ApiCache(); diff --git a/server/modules/apicache/index.js b/server/modules/apicache/index.js new file mode 100644 index 0000000..6332bad --- /dev/null +++ b/server/modules/apicache/index.js @@ -0,0 +1,12 @@ +const apicache = require("./apicache"); + +apicache.options({ + headerBlacklist: ["cache-control"], + headers: { + // Disable client side cache, only server side cache. + // BUG! Not working for the second request + "cache-control": "no-cache", + }, +}); + +module.exports = apicache; diff --git a/server/modules/apicache/memory-cache.js b/server/modules/apicache/memory-cache.js new file mode 100644 index 0000000..1e75bae --- /dev/null +++ b/server/modules/apicache/memory-cache.js @@ -0,0 +1,87 @@ +function MemoryCache() { + this.cache = {}; + this.size = 0; +} + +/** + * + * @param {string} key Key to store cache as + * @param {any} value Value to store + * @param {number} time Time to store for + * @param {function(any, string)} timeoutCallback Callback to call in + * case of timeout + * @returns {Object} + */ +MemoryCache.prototype.add = function (key, value, time, timeoutCallback) { + let old = this.cache[key]; + let instance = this; + + let entry = { + value: value, + expire: time + Date.now(), + timeout: setTimeout(function () { + instance.delete(key); + return timeoutCallback && typeof timeoutCallback === "function" && timeoutCallback(value, key); + }, time), + }; + + this.cache[key] = entry; + this.size = Object.keys(this.cache).length; + + return entry; +}; + +/** + * Delete a cache entry + * @param {string} key Key to delete + * @returns {null} + */ +MemoryCache.prototype.delete = function (key) { + let entry = this.cache[key]; + + if (entry) { + clearTimeout(entry.timeout); + } + + delete this.cache[key]; + + this.size = Object.keys(this.cache).length; + + return null; +}; + +/** + * Get value of key + * @param {string} key + * @returns {Object} + */ +MemoryCache.prototype.get = function (key) { + let entry = this.cache[key]; + + return entry; +}; + +/** + * Get value of cache entry + * @param {string} key + * @returns {any} + */ +MemoryCache.prototype.getValue = function (key) { + let entry = this.get(key); + + return entry && entry.value; +}; + +/** + * Clear cache + * @returns {boolean} + */ +MemoryCache.prototype.clear = function () { + Object.keys(this.cache).forEach(function (key) { + this.delete(key); + }, this); + + return true; +}; + +module.exports = MemoryCache; diff --git a/server/modules/axios-ntlm/LICENSE b/server/modules/axios-ntlm/LICENSE new file mode 100644 index 0000000..1744ee4 --- /dev/null +++ b/server/modules/axios-ntlm/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 CatButtes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/server/modules/axios-ntlm/lib/flags.js b/server/modules/axios-ntlm/lib/flags.js new file mode 100644 index 0000000..86c0603 --- /dev/null +++ b/server/modules/axios-ntlm/lib/flags.js @@ -0,0 +1,77 @@ +"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 + data. */ +module.exports.NTLMFLAG_NEGOTIATE_OEM = 1 << 1; +/* Indicates that OEM strings are supported for use in security buffer data. */ +module.exports.NTLMFLAG_REQUEST_TARGET = 1 << 2; +/* Requests that the server's authentication realm be included in the Type 2 + message. */ +/* unknown (1<<3) */ +module.exports.NTLMFLAG_NEGOTIATE_SIGN = 1 << 4; +/* Specifies that authenticated communication between the client and server + should carry a digital signature (message integrity). */ +module.exports.NTLMFLAG_NEGOTIATE_SEAL = 1 << 5; +/* Specifies that authenticated communication between the client and server + should be encrypted (message confidentiality). */ +module.exports.NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE = 1 << 6; +/* Indicates that datagram authentication is being used. */ +module.exports.NTLMFLAG_NEGOTIATE_LM_KEY = 1 << 7; +/* Indicates that the LAN Manager session key should be used for signing and + sealing authenticated communications. */ +module.exports.NTLMFLAG_NEGOTIATE_NETWARE = 1 << 8; +/* unknown purpose */ +module.exports.NTLMFLAG_NEGOTIATE_NTLM_KEY = 1 << 9; +/* Indicates that NTLM authentication is being used. */ +/* unknown (1<<10) */ +module.exports.NTLMFLAG_NEGOTIATE_ANONYMOUS = 1 << 11; +/* Sent by the client in the Type 3 message to indicate that an anonymous + context has been established. This also affects the response fields. */ +module.exports.NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED = 1 << 12; +/* Sent by the client in the Type 1 message to indicate that a desired + authentication realm is included in the message. */ +module.exports.NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED = 1 << 13; +/* Sent by the client in the Type 1 message to indicate that the client + workstation's name is included in the message. */ +module.exports.NTLMFLAG_NEGOTIATE_LOCAL_CALL = 1 << 14; +/* Sent by the server to indicate that the server and client are on the same + machine. Implies that the client may use a pre-established local security + context rather than responding to the challenge. */ +module.exports.NTLMFLAG_NEGOTIATE_ALWAYS_SIGN = 1 << 15; +/* Indicates that authenticated communication between the client and server + should be signed with a "dummy" signature. */ +module.exports.NTLMFLAG_TARGET_TYPE_DOMAIN = 1 << 16; +/* Sent by the server in the Type 2 message to indicate that the target + authentication realm is a domain. */ +module.exports.NTLMFLAG_TARGET_TYPE_SERVER = 1 << 17; +/* Sent by the server in the Type 2 message to indicate that the target + authentication realm is a server. */ +module.exports.NTLMFLAG_TARGET_TYPE_SHARE = 1 << 18; +/* Sent by the server in the Type 2 message to indicate that the target + authentication realm is a share. Presumably, this is for share-level + authentication. Usage is unclear. */ +module.exports.NTLMFLAG_NEGOTIATE_NTLM2_KEY = 1 << 19; +/* Indicates that the NTLM2 signing and sealing scheme should be used for + protecting authenticated communications. */ +module.exports.NTLMFLAG_REQUEST_INIT_RESPONSE = 1 << 20; +/* unknown purpose */ +module.exports.NTLMFLAG_REQUEST_ACCEPT_RESPONSE = 1 << 21; +/* unknown purpose */ +module.exports.NTLMFLAG_REQUEST_NONNT_SESSION_KEY = 1 << 22; +/* unknown purpose */ +module.exports.NTLMFLAG_NEGOTIATE_TARGET_INFO = 1 << 23; +/* Sent by the server in the Type 2 message to indicate that it is including a + Target Information block in the message. */ +/* unknown (1<24) */ +/* unknown (1<25) */ +/* unknown (1<26) */ +/* unknown (1<27) */ +/* unknown (1<28) */ +module.exports.NTLMFLAG_NEGOTIATE_128 = 1 << 29; +/* Indicates that 128-bit encryption is supported. */ +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 diff --git a/server/modules/axios-ntlm/lib/hash.js b/server/modules/axios-ntlm/lib/hash.js new file mode 100644 index 0000000..ab8546c --- /dev/null +++ b/server/modules/axios-ntlm/lib/hash.js @@ -0,0 +1,127 @@ +"use strict"; +// Original source at https://github.com/elasticio/node-ntlm-client/blob/master/lib/hash.js +var crypto = require("crypto"); +function createLMResponse(challenge, lmhash) { + 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); + calculateDES(pwBuffer.slice(14), challenge).copy(buf, 16); + return buf; +} +function createLMHash(password) { + 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)]); +} +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; + 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; + } + 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); + ntlmhash.copy(ntlmBuffer); + calculateDES(ntlmBuffer.slice(0, 7), challenge).copy(buf); + calculateDES(ntlmBuffer.slice(7, 14), challenge).copy(buf, 8); + calculateDES(ntlmBuffer.slice(14), challenge).copy(buf, 16); + return buf; +} +function createNTLMHash(password) { + 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")); + 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); + //server challenge + type2message.challenge.copy(buf, 8); + //client nonce + buf.write(nonce || createPseudoRandomValue(16), 16, "hex"); + //create hash + hmac.update(buf.slice(8)); + var hashedBuffer = hmac.digest(); + hashedBuffer.copy(buf); + 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); + //the first 8 bytes are spare to store the hashed value before the blob + //server challenge + type2message.challenge.copy(buf, 8); + //blob signature + buf.writeUInt32BE(0x01010000, 16); + //reserved + buf.writeUInt32LE(0, 20); + //timestamp + //TODO: we are losing precision here since js is not able to handle those large integers + // 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))); + buf.writeUInt32LE(timestampLow, 24, false); + buf.writeUInt32LE(timestampHigh, 28, false); + //random client nonce + buf.write(nonce || createPseudoRandomValue(16), 32, "hex"); + //zero + buf.writeUInt32LE(0, 40); + //complete target information block from type 2 message + type2message.targetInfo.buffer.copy(buf, 44); + //zero + buf.writeUInt32LE(0, 44 + type2message.targetInfo.buffer.length); + hmac.update(buf.slice(8)); + var hashedBuffer = hmac.digest(); + hashedBuffer.copy(buf); + return buf; +} +function createPseudoRandomValue(length) { + var str = ""; + while (str.length < length) { + str += crypto.randomInt(16).toString(16); + } + return str; +} +module.exports = { + createLMHash: createLMHash, + createNTLMHash: createNTLMHash, + createLMResponse: createLMResponse, + createNTLMResponse: createNTLMResponse, + createLMv2Response: createLMv2Response, + createNTLMv2Response: createNTLMv2Response, + createPseudoRandomValue: createPseudoRandomValue, +}; +//# sourceMappingURL=hash.js.map diff --git a/server/modules/axios-ntlm/lib/ntlm.js b/server/modules/axios-ntlm/lib/ntlm.js new file mode 100644 index 0000000..f253741 --- /dev/null +++ b/server/modules/axios-ntlm/lib/ntlm.js @@ -0,0 +1,233 @@ +"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 NTLMSIGNATURE = "NTLMSSP\0"; +function createType1Message(workstation, target) { + var dataPos = 32, + pos = 0, + buf = new Buffer.alloc(1024); + workstation = workstation === undefined ? os.hostname() : workstation; + target = target === undefined ? "" : target; + //signature + 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 + ); + pos += 4; + //domain security buffer + buf.writeUInt16LE(target.length, pos); + pos += 2; + buf.writeUInt16LE(target.length, pos); + pos += 2; + buf.writeUInt32LE(target.length === 0 ? 0 : dataPos, pos); + pos += 4; + if (target.length > 0) { + dataPos += buf.write(target, dataPos, "ascii"); + } + //workstation security buffer + buf.writeUInt16LE(workstation.length, pos); + pos += 2; + buf.writeUInt16LE(workstation.length, pos); + pos += 2; + buf.writeUInt32LE(workstation.length === 0 ? 0 : dataPos, pos); + pos += 4; + if (workstation.length > 0) { + dataPos += buf.write(workstation, dataPos, "ascii"); + } + return "NTLM " + buf.toString("base64", 0, dataPos); +} +function decodeType2Message(str) { + if (str === undefined) { + 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"); + } + } + var ntlmMatch = /^NTLM ([^,\s]+)/.exec(str); + if (ntlmMatch) { + str = ntlmMatch[1]; + } + 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); + } + //check message type + if (buf.readUInt32LE(NTLMSIGNATURE.length) !== 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.challenge = buf.slice(24, 32); + //read target name + obj.targetName = (function () { + var length = buf.readUInt16LE(12); + //skipping allocated space + var offset = buf.readUInt32LE(16); + if (length === 0) { + return ""; + } + if (offset + length > buf.length || offset < 32) { + throw new Error("Bad type 2 message"); + } + return buf.toString(obj.encoding, offset, offset + length); + })(); + //read target info + if (obj.flags & flags.NTLMFLAG_NEGOTIATE_TARGET_INFO) { + obj.targetInfo = (function () { + var info = {}; + var length = buf.readUInt16LE(40); + //skipping allocated space + var offset = buf.readUInt32LE(44); + var targetInfoBuffer = new Buffer.alloc(length); + buf.copy(targetInfoBuffer, 0, offset, offset + length); + if (length === 0) { + return info; + } + if (offset + length > buf.length || offset < 32) { + throw new Error("Bad type 2 message"); + } + var pos = offset; + while (pos < offset + length) { + var blockType = buf.readUInt16LE(pos); + pos += 2; + var blockLength = buf.readUInt16LE(pos); + pos += 2; + if (blockType === 0) { + //reached the terminator subblock + break; + } + var blockTypeStr = void 0; + switch (blockType) { + case 1: + blockTypeStr = "SERVER"; + break; + case 2: + blockTypeStr = "DOMAIN"; + break; + case 3: + blockTypeStr = "FQDN"; + break; + case 4: + blockTypeStr = "DNS"; + break; + case 5: + blockTypeStr = "PARENT_DNS"; + break; + default: + blockTypeStr = ""; + break; + } + if (blockTypeStr) { + info[blockTypeStr] = buf.toString("ucs2", pos, pos + blockLength); + } + pos += blockLength; + } + return { + parsed: info, + buffer: targetInfoBuffer, + }; + })(); + } + return obj; +} +function createType3Message(type2Message, username, password, workstation, target) { + var dataPos = 52, + buf = new Buffer.alloc(1024); + if (workstation === undefined) { + workstation = os.hostname(); + } + if (target === undefined) { + target = type2Message.targetName; + } + //signature + 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); + //lmv2 security buffer + buf.writeUInt16LE(lmv2.length, 12); + buf.writeUInt16LE(lmv2.length, 14); + buf.writeUInt32LE(dataPos, 16); + lmv2.copy(buf, dataPos); + dataPos += lmv2.length; + //ntlmv2 security buffer + buf.writeUInt16LE(ntlmv2.length, 20); + buf.writeUInt16LE(ntlmv2.length, 22); + 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); + //lm security buffer + buf.writeUInt16LE(lm.length, 12); + buf.writeUInt16LE(lm.length, 14); + buf.writeUInt32LE(dataPos, 16); + lm.copy(buf, dataPos); + dataPos += lm.length; + //ntlm security buffer + buf.writeUInt16LE(ntlm.length, 20); + buf.writeUInt16LE(ntlm.length, 22); + buf.writeUInt32LE(dataPos, 24); + ntlm.copy(buf, dataPos); + 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.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.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.writeUInt32LE(dataPos, 48); + dataPos += buf.write(workstation, dataPos, type2Message.encoding); + if (type2Message.version === 2) { + //session key security buffer + buf.writeUInt16LE(0, 52); + buf.writeUInt16LE(0, 54); + buf.writeUInt32LE(0, 56); + //flags + buf.writeUInt32LE(type2Message.flags, 60); + } + return "NTLM " + buf.toString("base64", 0, dataPos); +} +module.exports = { + createType1Message: createType1Message, + decodeType2Message: decodeType2Message, + createType3Message: createType3Message, +}; +//# sourceMappingURL=ntlm.js.map diff --git a/server/modules/axios-ntlm/lib/ntlmClient.js b/server/modules/axios-ntlm/lib/ntlmClient.js new file mode 100644 index 0000000..1e6e7a9 --- /dev/null +++ b/server/modules/axios-ntlm/lib/ntlmClient.js @@ -0,0 +1,267 @@ +"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; + } + 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")); +var ntlm = __importStar(require("./ntlm")); +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 + */ +function NtlmClient(credentials, AxiosConfig) { + var _this = this; + var config = AxiosConfig !== null && AxiosConfig !== void 0 ? AxiosConfig : {}; + if (!config.httpAgent) { + config.httpAgent = new http.Agent({ keepAlive: true }); + } + if (!config.httpsAgent) { + 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; + } 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 diff --git a/server/modules/dayjs/plugin/timezone.d.ts b/server/modules/dayjs/plugin/timezone.d.ts new file mode 100644 index 0000000..4d98fc7 --- /dev/null +++ b/server/modules/dayjs/plugin/timezone.d.ts @@ -0,0 +1,20 @@ +import { PluginFunc, ConfigType } from "dayjs"; + +declare const plugin: PluginFunc; +export = plugin; + +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; + } + + const tz: DayjsTimezone; +} diff --git a/server/modules/dayjs/plugin/timezone.js b/server/modules/dayjs/plugin/timezone.js new file mode 100644 index 0000000..b973ece --- /dev/null +++ b/server/modules/dayjs/plugin/timezone.js @@ -0,0 +1,130 @@ +/** + * Copy from node_modules/dayjs/plugin/timezone.js + * Try to fix https://github.com/louislam/uptime-kuma/issues/2318 + * Source: https://github.com/iamkun/dayjs/tree/dev/src/plugin/utc + * License: MIT + */ +!(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 () { + "use strict"; + let t = { + year: 0, + month: 1, + day: 2, + hour: 3, + minute: 4, + second: 5, + }; + let e = {}; + return function (n, i, o) { + let r; + let a = function (t, n, i) { + void 0 === i && (i = {}); + let o = new Date(t); + 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.formatToParts(o); + }; + let u = function (e, n) { + let i = a(e, n); + let r = []; + let u = 0; + for (; u < i.length; u += 1) { + let f = i[u]; + let s = f.type; + let m = f.value; + let c = t[s]; + c >= 0 && (r[c] = parseInt(m, 10)); + } + let d = r[3]; + let l = d === 24 ? 0 : d; + let v = r[0] + "-" + r[1] + "-" + r[2] + " " + l + ":" + r[4] + ":" + r[5] + ":000"; + let h = +e; + return (o.utc(v).valueOf() - (h -= h % 1e3)) / 6e4; + }; + let f = i.prototype; + ((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); + 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 n && n.value; + })); + let s = f.startOf; + ((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]; + } + 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; + })); + }; +}); diff --git a/server/monitor-conditions/evaluator.js b/server/monitor-conditions/evaluator.js new file mode 100644 index 0000000..5b9dcff --- /dev/null +++ b/server/monitor-conditions/evaluator.js @@ -0,0 +1,83 @@ +const { ConditionExpressionGroup, ConditionExpression, LOGICAL } = require("./expression"); +const { operatorMap } = require("./operators"); + +/** + * @param {ConditionExpression} expression Expression to evaluate + * @param {object} context Context to evaluate against; These are values for variables in the expression + * @returns {boolean} Whether the expression evaluates true or false + * @throws {Error} + */ +function evaluateExpression(expression, context) { + /** + * @type {import("./operators").ConditionOperator|null} + */ + const operator = operatorMap.get(expression.operator) || null; + if (operator === null) { + throw new Error( + "Unexpected expression operator ID '" + + expression.operator + + "'. Expected one of [" + + operatorMap.keys().join(",") + + "]" + ); + } + + if (!Object.prototype.hasOwnProperty.call(context, expression.variable)) { + throw new Error("Variable missing in context: " + expression.variable); + } + + return operator.test(context[expression.variable], expression.value); +} + +/** + * @param {ConditionExpressionGroup} group Group of expressions to evaluate + * @param {object} context Context to evaluate against; These are values for variables in the expression + * @returns {boolean} Whether the group evaluates true or false + * @throws {Error} + */ +function evaluateExpressionGroup(group, context) { + if (!group.children.length) { + throw new Error("ConditionExpressionGroup must contain at least one child."); + } + + let result = null; + + for (const child of group.children) { + let childResult; + + if (child instanceof ConditionExpression) { + childResult = evaluateExpression(child, context); + } else if (child instanceof ConditionExpressionGroup) { + childResult = evaluateExpressionGroup(child, context); + } else { + throw new Error( + "Invalid child type in ConditionExpressionGroup. Expected ConditionExpression or ConditionExpressionGroup" + ); + } + + if (result === null) { + result = childResult; // Initialize result with the first child's result + } else if (child.andOr === LOGICAL.OR) { + result = result || childResult; + } else if (child.andOr === LOGICAL.AND) { + result = result && childResult; + } else { + throw new Error( + "Invalid logical operator in child of ConditionExpressionGroup. Expected 'and' or 'or'. Got '" + + group.andOr + + "'" + ); + } + } + + if (result === null) { + throw new Error("ConditionExpressionGroup did not result in a boolean."); + } + + return result; +} + +module.exports = { + evaluateExpression, + evaluateExpressionGroup, +}; diff --git a/server/monitor-conditions/expression.js b/server/monitor-conditions/expression.js new file mode 100644 index 0000000..e537550 --- /dev/null +++ b/server/monitor-conditions/expression.js @@ -0,0 +1,111 @@ +/** + * @readonly + * @enum {string} + */ +const LOGICAL = { + AND: "and", + OR: "or", +}; + +/** + * Recursively processes an array of raw condition objects and populates the given parent group with + * corresponding ConditionExpression or ConditionExpressionGroup instances. + * @param {Array} conditions Array of raw condition objects, where each object represents either a group or an expression. + * @param {ConditionExpressionGroup} parentGroup The parent group to which the instantiated ConditionExpression or ConditionExpressionGroup objects will be added. + * @returns {void} + */ +function processMonitorConditions(conditions, parentGroup) { + conditions.forEach((condition) => { + const andOr = condition.andOr === LOGICAL.OR ? LOGICAL.OR : LOGICAL.AND; + + if (condition.type === "group") { + const group = new ConditionExpressionGroup([], andOr); + + // Recursively process the group's children + processMonitorConditions(condition.children, group); + + parentGroup.children.push(group); + } else if (condition.type === "expression") { + const expression = new ConditionExpression(condition.variable, condition.operator, condition.value, andOr); + parentGroup.children.push(expression); + } + }); +} + +class ConditionExpressionGroup { + /** + * @type {ConditionExpressionGroup[]|ConditionExpression[]} Groups and/or expressions to test + */ + children = []; + + /** + * @type {LOGICAL} Connects group result with previous group/expression results + */ + andOr; + + /** + * @param {ConditionExpressionGroup[]|ConditionExpression[]} children Groups and/or expressions to test + * @param {LOGICAL} andOr Connects group result with previous group/expression results + */ + constructor(children = [], andOr = LOGICAL.AND) { + this.children = children; + this.andOr = andOr; + } + + /** + * @param {Monitor} monitor Monitor instance + * @returns {ConditionExpressionGroup|null} A ConditionExpressionGroup with the Monitor's conditions + */ + static fromMonitor(monitor) { + const conditions = JSON.parse(monitor.conditions); + if (conditions.length === 0) { + return null; + } + + const root = new ConditionExpressionGroup(); + processMonitorConditions(conditions, root); + + return root; + } +} + +class ConditionExpression { + /** + * @type {string} ID of variable + */ + variable; + + /** + * @type {string} ID of operator + */ + operator; + + /** + * @type {string} Value to test with the operator + */ + value; + + /** + * @type {LOGICAL} Connects expression result with previous group/expression results + */ + andOr; + + /** + * @param {string} variable ID of variable to test against + * @param {string} operator ID of operator to test the variable with + * @param {string} value Value to test with the operator + * @param {LOGICAL} andOr Connects expression result with previous group/expression results + */ + constructor(variable, operator, value, andOr = LOGICAL.AND) { + this.variable = variable; + this.operator = operator; + this.value = value; + this.andOr = andOr; + } +} + +module.exports = { + LOGICAL, + ConditionExpressionGroup, + ConditionExpression, +}; diff --git a/server/monitor-conditions/operators.js b/server/monitor-conditions/operators.js new file mode 100644 index 0000000..66370df --- /dev/null +++ b/server/monitor-conditions/operators.js @@ -0,0 +1,318 @@ +class ConditionOperator { + id = undefined; + caption = undefined; + + /** + * @type {mixed} variable + * @type {mixed} value + */ + test(variable, value) { + throw new Error("You need to override test()"); + } +} + +const OP_STR_EQUALS = "equals"; + +const OP_STR_NOT_EQUALS = "not_equals"; + +const OP_CONTAINS = "contains"; + +const OP_NOT_CONTAINS = "not_contains"; + +const OP_STARTS_WITH = "starts_with"; + +const OP_NOT_STARTS_WITH = "not_starts_with"; + +const OP_ENDS_WITH = "ends_with"; + +const OP_NOT_ENDS_WITH = "not_ends_with"; + +const OP_NUM_EQUALS = "num_equals"; + +const OP_NUM_NOT_EQUALS = "num_not_equals"; + +const OP_LT = "lt"; + +const OP_GT = "gt"; + +const OP_LTE = "lte"; + +const OP_GTE = "gte"; + +/** + * Asserts a variable is equal to a value. + */ +class StringEqualsOperator extends ConditionOperator { + id = OP_STR_EQUALS; + caption = "equals"; + + /** + * @inheritdoc + */ + test(variable, value) { + return variable === value; + } +} + +/** + * Asserts a variable is not equal to a value. + */ +class StringNotEqualsOperator extends ConditionOperator { + id = OP_STR_NOT_EQUALS; + caption = "not equals"; + + /** + * @inheritdoc + */ + test(variable, value) { + return variable !== value; + } +} + +/** + * Asserts a variable contains a value. + * Handles both Array and String variable types. + */ +class ContainsOperator extends ConditionOperator { + id = OP_CONTAINS; + caption = "contains"; + + /** + * @inheritdoc + */ + test(variable, value) { + if (Array.isArray(variable)) { + return variable.includes(value); + } + + return variable.indexOf(value) !== -1; + } +} + +/** + * Asserts a variable does not contain a value. + * Handles both Array and String variable types. + */ +class NotContainsOperator extends ConditionOperator { + id = OP_NOT_CONTAINS; + caption = "not contains"; + + /** + * @inheritdoc + */ + test(variable, value) { + if (Array.isArray(variable)) { + return !variable.includes(value); + } + + return variable.indexOf(value) === -1; + } +} + +/** + * Asserts a variable starts with a value. + */ +class StartsWithOperator extends ConditionOperator { + id = OP_STARTS_WITH; + caption = "starts with"; + + /** + * @inheritdoc + */ + test(variable, value) { + return variable.startsWith(value); + } +} + +/** + * Asserts a variable does not start with a value. + */ +class NotStartsWithOperator extends ConditionOperator { + id = OP_NOT_STARTS_WITH; + caption = "not starts with"; + + /** + * @inheritdoc + */ + test(variable, value) { + return !variable.startsWith(value); + } +} + +/** + * Asserts a variable ends with a value. + */ +class EndsWithOperator extends ConditionOperator { + id = OP_ENDS_WITH; + caption = "ends with"; + + /** + * @inheritdoc + */ + test(variable, value) { + return variable.endsWith(value); + } +} + +/** + * Asserts a variable does not end with a value. + */ +class NotEndsWithOperator extends ConditionOperator { + id = OP_NOT_ENDS_WITH; + caption = "not ends with"; + + /** + * @inheritdoc + */ + test(variable, value) { + return !variable.endsWith(value); + } +} + +/** + * Asserts a numeric variable is equal to a value. + */ +class NumberEqualsOperator extends ConditionOperator { + id = OP_NUM_EQUALS; + caption = "equals"; + + /** + * @inheritdoc + */ + test(variable, value) { + return variable === Number(value); + } +} + +/** + * Asserts a numeric variable is not equal to a value. + */ +class NumberNotEqualsOperator extends ConditionOperator { + id = OP_NUM_NOT_EQUALS; + caption = "not equals"; + + /** + * @inheritdoc + */ + test(variable, value) { + return variable !== Number(value); + } +} + +/** + * Asserts a variable is less than a value. + */ +class LessThanOperator extends ConditionOperator { + id = OP_LT; + caption = "less than"; + + /** + * @inheritdoc + */ + test(variable, value) { + return variable < Number(value); + } +} + +/** + * Asserts a variable is greater than a value. + */ +class GreaterThanOperator extends ConditionOperator { + id = OP_GT; + caption = "greater than"; + + /** + * @inheritdoc + */ + test(variable, value) { + return variable > Number(value); + } +} + +/** + * Asserts a variable is less than or equal to a value. + */ +class LessThanOrEqualToOperator extends ConditionOperator { + id = OP_LTE; + caption = "less than or equal to"; + + /** + * @inheritdoc + */ + test(variable, value) { + return variable <= Number(value); + } +} + +/** + * Asserts a variable is greater than or equal to a value. + */ +class GreaterThanOrEqualToOperator extends ConditionOperator { + id = OP_GTE; + caption = "greater than or equal to"; + + /** + * @inheritdoc + */ + test(variable, value) { + return variable >= Number(value); + } +} + +const operatorMap = new Map([ + [OP_STR_EQUALS, new StringEqualsOperator()], + [OP_STR_NOT_EQUALS, new StringNotEqualsOperator()], + [OP_CONTAINS, new ContainsOperator()], + [OP_NOT_CONTAINS, new NotContainsOperator()], + [OP_STARTS_WITH, new StartsWithOperator()], + [OP_NOT_STARTS_WITH, new NotStartsWithOperator()], + [OP_ENDS_WITH, new EndsWithOperator()], + [OP_NOT_ENDS_WITH, new NotEndsWithOperator()], + [OP_NUM_EQUALS, new NumberEqualsOperator()], + [OP_NUM_NOT_EQUALS, new NumberNotEqualsOperator()], + [OP_LT, new LessThanOperator()], + [OP_GT, new GreaterThanOperator()], + [OP_LTE, new LessThanOrEqualToOperator()], + [OP_GTE, new GreaterThanOrEqualToOperator()], +]); + +const defaultStringOperators = [ + operatorMap.get(OP_STR_EQUALS), + operatorMap.get(OP_STR_NOT_EQUALS), + operatorMap.get(OP_CONTAINS), + operatorMap.get(OP_NOT_CONTAINS), + operatorMap.get(OP_STARTS_WITH), + operatorMap.get(OP_NOT_STARTS_WITH), + operatorMap.get(OP_ENDS_WITH), + operatorMap.get(OP_NOT_ENDS_WITH), +]; + +const defaultNumberOperators = [ + operatorMap.get(OP_NUM_EQUALS), + operatorMap.get(OP_NUM_NOT_EQUALS), + operatorMap.get(OP_LT), + operatorMap.get(OP_GT), + operatorMap.get(OP_LTE), + operatorMap.get(OP_GTE), +]; + +module.exports = { + OP_STR_EQUALS, + OP_STR_NOT_EQUALS, + OP_CONTAINS, + OP_NOT_CONTAINS, + OP_STARTS_WITH, + OP_NOT_STARTS_WITH, + OP_ENDS_WITH, + OP_NOT_ENDS_WITH, + OP_NUM_EQUALS, + OP_NUM_NOT_EQUALS, + OP_LT, + OP_GT, + OP_LTE, + OP_GTE, + operatorMap, + defaultStringOperators, + defaultNumberOperators, + ConditionOperator, +}; diff --git a/server/monitor-conditions/variables.js b/server/monitor-conditions/variables.js new file mode 100644 index 0000000..af98d2f --- /dev/null +++ b/server/monitor-conditions/variables.js @@ -0,0 +1,31 @@ +/** + * Represents a variable used in a condition and the set of operators that can be applied to this variable. + * + * A `ConditionVariable` holds the ID of the variable and a list of operators that define how this variable can be evaluated + * in conditions. For example, if the variable is a request body or a specific field in a request, the operators can include + * operations such as equality checks, comparisons, or other custom evaluations. + */ +class ConditionVariable { + /** + * @type {string} + */ + id; + + /** + * @type {import("./operators").ConditionOperator[]} + */ + operators = {}; + + /** + * @param {string} id ID of variable + * @param {import("./operators").ConditionOperator[]} operators Operators the condition supports + */ + constructor(id, operators = []) { + this.id = id; + this.operators = operators; + } +} + +module.exports = { + ConditionVariable, +}; diff --git a/server/monitor-types/dns.js b/server/monitor-types/dns.js new file mode 100644 index 0000000..725e861 --- /dev/null +++ b/server/monitor-types/dns.js @@ -0,0 +1,186 @@ +const { MonitorType } = require("./monitor-type"); +const { UP, log } = require("../../src/util"); +const dayjs = require("dayjs"); +const { R } = require("redbean-node"); +const { ConditionVariable } = require("../monitor-conditions/variables"); +const { defaultStringOperators } = require("../monitor-conditions/operators"); +const { ConditionExpressionGroup } = require("../monitor-conditions/expression"); +const { evaluateExpressionGroup } = require("../monitor-conditions/evaluator"); +const { Resolver } = require("node:dns/promises"); +const net = require("node:net"); + +class DnsMonitorType extends MonitorType { + name = "dns"; + + supportsConditions = true; + + conditionVariables = [new ConditionVariable("record", defaultStringOperators)]; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + let startTime = dayjs().valueOf(); + let dnsMessage = ""; + + const resolverServers = await this.resolveDnsResolverServers(monitor.dns_resolve_server); + let dnsRes = await this.dnsResolve(monitor.hostname, resolverServers, monitor.port, monitor.dns_resolve_type); + heartbeat.ping = dayjs().valueOf() - startTime; + + const conditions = ConditionExpressionGroup.fromMonitor(monitor); + let conditionsResult = true; + const handleConditions = (data) => (conditions ? evaluateExpressionGroup(conditions, data) : true); + + switch (monitor.dns_resolve_type) { + case "A": + case "AAAA": + case "PTR": + dnsMessage = `Records: ${dnsRes.join(" | ")}`; + conditionsResult = dnsRes.some((record) => handleConditions({ record })); + break; + + case "TXT": + dnsMessage = `Records: ${dnsRes.join(" | ")}`; + conditionsResult = dnsRes.flat().some((record) => handleConditions({ record })); + break; + + case "CNAME": + dnsMessage = dnsRes[0]; + conditionsResult = handleConditions({ record: dnsRes[0] }); + break; + + case "CAA": + // .filter(Boolean) was added because some CAA records do not contain an issue key, resulting in a blank list item. + // Hypothetical dnsRes [{ critical: 0, issuewild: 'letsencrypt.org' }, { critical: 0, issue: 'letsencrypt.org' }] + dnsMessage = `Records: ${dnsRes + .map((record) => record.issue) + .filter(Boolean) + .join(" | ")}`; + conditionsResult = dnsRes.some((record) => handleConditions({ record: record.issue })); + break; + + case "MX": + dnsMessage = dnsRes + .map((record) => `Hostname: ${record.exchange} - Priority: ${record.priority}`) + .join(" | "); + conditionsResult = dnsRes.some((record) => handleConditions({ record: record.exchange })); + break; + + case "NS": + dnsMessage = `Servers: ${dnsRes.join(" | ")}`; + conditionsResult = dnsRes.some((record) => handleConditions({ record })); + break; + + case "SOA": + dnsMessage = `NS-Name: ${dnsRes.nsname} | Hostmaster: ${dnsRes.hostmaster} | Serial: ${dnsRes.serial} | Refresh: ${dnsRes.refresh} | Retry: ${dnsRes.retry} | Expire: ${dnsRes.expire} | MinTTL: ${dnsRes.minttl}`; + conditionsResult = handleConditions({ record: dnsRes.nsname }); + break; + + case "SRV": + dnsMessage = dnsRes + .map( + (record) => + `Name: ${record.name} | Port: ${record.port} | Priority: ${record.priority} | Weight: ${record.weight}` + ) + .join(" | "); + conditionsResult = dnsRes.some((record) => handleConditions({ record: record.name })); + break; + } + + if (monitor.dns_last_result !== dnsMessage && dnsMessage !== undefined) { + await R.exec("UPDATE `monitor` SET dns_last_result = ? WHERE id = ? ", [dnsMessage, monitor.id]); + } + + if (!conditionsResult) { + throw new Error(dnsMessage); + } + + heartbeat.msg = dnsMessage; + heartbeat.status = UP; + } + + /** + * Parses a comma-separated list of DNS resolver servers and resolves any hostnames + * to their corresponding IPv4 and/or IPv6 addresses. + * + * We are primarily doing this to support hostnames of docker containers like adguard. + * + * - Whitespace is removed from the input string + * - Empty entries are ignored + * - IP literals (IPv4 / IPv6) are accepted as-is + * - Hostnames are resolved to both A and AAAA records in parallel + * - Invalid or unresolvable entries are logged and skipped + * @param {string} dnsResolveServer - Comma-separated list of resolver servers (IPs or hostnames) + * @returns {Promise>} Array of resolved IP addresses + * @throws {Error} If no valid resolver servers could be parsed or resolved + */ + async resolveDnsResolverServers(dnsResolveServer) { + // Remove all spaces, split into array, remove all elements that are empty + const addresses = dnsResolveServer + .replace(/\s/g, "") + .split(",") + .filter((x) => x !== ""); + if (!addresses.length) { + throw new Error( + "No Resolver Servers specified. Please specify at least one resolver server like 1.1.1.1 or a hostname" + ); + } + const resolver = new Resolver(); + + // Make promises to be resolved concurrently + const promises = addresses.map(async (e) => { + if (net.isIP(e)) { + // If IPv4 or IPv6 addr, immediately return + return [e]; + } + + // Otherwise, attempt to resolve hostname + const [v4, v6] = await Promise.allSettled([resolver.resolve4(e), resolver.resolve6(e)]); + + const addrs = [ + ...(v4.status === "fulfilled" ? v4.value : []), + ...(v6.status === "fulfilled" ? v6.value : []), + ]; + + if (!addrs.length) { + log.error("DNS", `Invalid resolver server ${e}`); + } + return addrs; + }); + + // [[ips of hostname1],[ips hostname2],...] + const ips = await Promise.all(promises); + // Append all the ips in [[]] to a single [] + const parsed = ips.flat(); + + // only the resolver resolution can discard an address + // -> no special error message for only the net.isIP case is necessary + if (!parsed.length) { + throw new Error( + "None of the configured resolver servers could be resolved to an IP address. Please provide a comma-separated list of valid resolver hostnames or IP addresses." + ); + } + return parsed; + } + + /** + * Resolves a given record using the specified DNS server + * @param {string} hostname The hostname of the record to lookup + * @param {string[]} resolverServer Array of DNS server IP addresses to use + * @param {string} resolverPort Port the DNS server is listening on + * @param {string} rrtype The type of record to request + * @returns {Promise<(string[] | object[] | object)>} DNS response + */ + async dnsResolve(hostname, resolverServer, resolverPort, rrtype) { + const resolver = new Resolver(); + resolver.setServers(resolverServer.map((server) => `[${server}]:${resolverPort}`)); + if (rrtype === "PTR") { + return await resolver.reverse(hostname); + } + return await resolver.resolve(hostname, rrtype); + } +} + +module.exports = { + DnsMonitorType, +}; diff --git a/server/monitor-types/gamedig.js b/server/monitor-types/gamedig.js new file mode 100644 index 0000000..993dd96 --- /dev/null +++ b/server/monitor-types/gamedig.js @@ -0,0 +1,32 @@ +const { MonitorType } = require("./monitor-type"); +const { UP } = require("../../src/util"); +const { GameDig } = require("gamedig"); + +class GameDigMonitorType extends MonitorType { + name = "gamedig"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, server) { + try { + const state = await GameDig.query({ + type: monitor.game, + host: monitor.hostname, + port: monitor.port, + givenPortOnly: Boolean(monitor.gamedigGivenPortOnly), + ...(monitor.gamedigToken ? { token: monitor.gamedigToken } : {}), + }); + + heartbeat.msg = state.name; + heartbeat.status = UP; + heartbeat.ping = state.ping; + } catch (e) { + throw new Error(e.message); + } + } +} + +module.exports = { + GameDigMonitorType, +}; diff --git a/server/monitor-types/globalping.js b/server/monitor-types/globalping.js new file mode 100644 index 0000000..5b235d7 --- /dev/null +++ b/server/monitor-types/globalping.js @@ -0,0 +1,632 @@ +const { MonitorType } = require("./monitor-type"); +const { Globalping, IpVersion } = require("globalping"); +const { Settings } = require("../settings"); +const { log, UP, evaluateJsonQuery } = require("../../src/util"); +const { + checkStatusCode, + getOidcTokenClientCredentials, + encodeBase64, + getDaysRemaining, + checkCertExpiryNotifications, +} = require("../util-server"); +const { R } = require("redbean-node"); + +/** + * Globalping is a free and open-source tool that allows you to run network tests + * and measurements from thousands of community hosted probes around the world. + * + * Library documentation: https://github.com/jsdelivr/globalping-typescript + * + * API documentation: https://globalping.io/docs/api.globalping.io + */ +class GlobalpingMonitorType extends MonitorType { + name = "globalping"; + + httpUserAgent = ""; + + /** + * @inheritdoc + */ + constructor(httpUserAgent) { + super(); + this.httpUserAgent = httpUserAgent; + } + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + const apiKey = await Settings.get("globalpingApiToken"); + const client = new Globalping({ + auth: apiKey, + agent: this.httpUserAgent, + }); + + const hasAPIToken = !!apiKey; + switch (monitor.subtype) { + case "ping": + await this.ping(client, monitor, heartbeat, hasAPIToken); + break; + case "http": + await this.http(client, monitor, heartbeat, hasAPIToken); + break; + case "dns": + await this.dns(client, monitor, heartbeat, hasAPIToken, R); + break; + } + } + + /** + * Handles ping monitors. + * @param {Client} client - The client object. + * @param {Monitor} monitor - The monitor object. + * @param {Heartbeat} heartbeat - The heartbeat object. + * @param {boolean} hasAPIToken - Whether the monitor has an API token. + * @returns {Promise} A promise that resolves when the ping monitor is handled. + */ + async ping(client, monitor, heartbeat, hasAPIToken) { + const opts = { + type: "ping", + target: monitor.hostname, + inProgressUpdates: false, + limit: 1, + locations: [{ magic: monitor.location }], + measurementOptions: { + packets: monitor.ping_count, + protocol: monitor.protocol, + }, + }; + + if (monitor.protocol === "TCP" && monitor.port) { + opts.measurementOptions.port = monitor.port; + } + + if (monitor.ipFamily === "ipv4") { + opts.measurementOptions.ipVersion = IpVersion[4]; + } else if (monitor.ipFamily === "ipv6") { + opts.measurementOptions.ipVersion = IpVersion[6]; + } + + log.debug("monitor", `Globalping create measurement: ${JSON.stringify(opts)}`); + let res = await client.createMeasurement(opts); + + // Retry if the server returns a 500 error + if (!res.ok && Globalping.isHttpStatus(500, res)) { + res = await client.createMeasurement(opts); + } + + if (!res.ok) { + if (Globalping.isHttpStatus(429, res)) { + throw new Error(`Failed to create measurement: ${this.formatTooManyRequestsError(hasAPIToken)}`); + } + throw new Error(`Failed to create measurement: ${this.formatApiError(res.data.error)}`); + } + + log.debug("monitor", `Globalping fetch measurement: ${res.data.id}`); + let measurement = await client.awaitMeasurement(res.data.id); + + if (!measurement.ok) { + throw new Error( + `Failed to fetch measurement (${res.data.id}): ${this.formatApiError(measurement.data.error)}` + ); + } + + const probe = measurement.data.results[0].probe; + const result = measurement.data.results[0].result; + + if (result.status === "failed") { + throw new Error(this.formatResponse(probe, `Failed: ${result.rawOutput}`)); + } + + if (!result.timings?.length) { + throw new Error(this.formatResponse(probe, `Failed: ${result.rawOutput}`)); + } + + heartbeat.ping = result.stats.avg || 0; + heartbeat.msg = this.formatResponse(probe, "OK"); + heartbeat.status = UP; + } + + /** + * Handles HTTP monitors. + * @param {Client} client - The client object. + * @param {Monitor} monitor - The monitor object. + * @param {Heartbeat} heartbeat - The heartbeat object. + * @param {boolean} hasAPIToken - Whether the monitor has an API token. + * @returns {Promise} A promise that resolves when the HTTP monitor is handled. + */ + async http(client, monitor, heartbeat, hasAPIToken) { + const url = new URL(monitor.url); + + let protocol = url.protocol.replace(":", "").toUpperCase(); + if (monitor.protocol === "HTTP2") { + protocol = "HTTP2"; + } + + const basicAuthHeader = this.getBasicAuthHeader(monitor); + const bearerAuthHeader = this.getBearerAuthHeader(monitor); + const oauth2AuthHeader = await this.getOauth2AuthHeader(monitor); + const headers = { + ...basicAuthHeader, + ...bearerAuthHeader, + ...oauth2AuthHeader, + ...(monitor.headers ? JSON.parse(monitor.headers) : {}), + }; + + if (monitor.cacheBust) { + const randomFloatString = Math.random().toString(36); + const cacheBust = randomFloatString.substring(2); + url.searchParams.set("uptime_kuma_cachebuster", cacheBust); + } + + const opts = { + type: "http", + target: url.hostname, + inProgressUpdates: false, + limit: 1, + locations: [{ magic: monitor.location }], + measurementOptions: { + request: { + host: url.hostname, + path: url.pathname, + query: url.search ? url.search.slice(1) : undefined, + method: monitor.method, + headers, + }, + protocol: protocol, + }, + }; + + if (url.port) { + opts.measurementOptions.port = parseInt(url.port); + } + + if (monitor.ipFamily === "ipv4") { + opts.measurementOptions.ipVersion = IpVersion[4]; + } else if (monitor.ipFamily === "ipv6") { + opts.measurementOptions.ipVersion = IpVersion[6]; + } + + if (monitor.dns_resolve_server) { + opts.measurementOptions.resolver = monitor.dns_resolve_server; + } + + log.debug("monitor", `Globalping create measurement: ${JSON.stringify(opts)}`); + let res = await client.createMeasurement(opts); + + // Retry if the server returns a 500 error + if (!res.ok && Globalping.isHttpStatus(500, res)) { + res = await client.createMeasurement(opts); + } + + if (!res.ok) { + if (Globalping.isHttpStatus(429, res)) { + throw new Error(`Failed to create measurement: ${this.formatTooManyRequestsError(hasAPIToken)}`); + } + throw new Error(`Failed to create measurement: ${this.formatApiError(res.data.error)}`); + } + + log.debug("monitor", `Globalping fetch measurement: ${res.data.id}`); + let measurement = await client.awaitMeasurement(res.data.id); + + if (!measurement.ok) { + throw new Error( + `Failed to fetch measurement (${res.data.id}): ${this.formatApiError(measurement.data.error)}` + ); + } + + const probe = measurement.data.results[0].probe; + const result = measurement.data.results[0].result; + + if (result.status === "failed") { + throw new Error(this.formatResponse(probe, `Failed: ${result.rawOutput}`)); + } + + heartbeat.ping = result.timings.total || 0; + + if (!checkStatusCode(result.statusCode, JSON.parse(monitor.accepted_statuscodes_json))) { + throw new Error( + this.formatResponse(probe, `Status code ${result.statusCode} not accepted. Output: ${result.rawOutput}`) + ); + } + + heartbeat.msg = this.formatResponse(probe, `${result.statusCode} - ${result.statusCodeName}`); + + // keyword + if (monitor.keyword) { + await this.handleKeywordForHTTP(monitor, heartbeat, result, probe); + return; + } + + // json-query + if (monitor.expectedValue) { + await this.handleJSONQueryForHTTP(monitor, heartbeat, result, probe); + return; + } + + await this.handleTLSInfo(monitor, protocol, probe, result.tls); + + heartbeat.msg = this.formatResponse(probe, "OK"); + heartbeat.status = UP; + } + + /** + * Handles DNS monitors. + * @param {Client} client - The client object. + * @param {Monitor} monitor - The monitor object. + * @param {Heartbeat} heartbeat - The heartbeat object. + * @param {boolean} hasAPIToken - Whether the monitor has an API token. + * @param {R} redbean - The redbean object. + * @returns {Promise} A promise that resolves when the HTTP monitor is handled. + */ + async dns(client, monitor, heartbeat, hasAPIToken, redbean) { + const opts = { + type: "dns", + target: monitor.hostname, + inProgressUpdates: false, + limit: 1, + locations: [{ magic: monitor.location }], + measurementOptions: { + query: { + type: monitor.dns_resolve_type, + }, + port: monitor.port, + protocol: monitor.protocol, + }, + }; + + if (monitor.ipFamily === "ipv4") { + opts.measurementOptions.ipVersion = IpVersion[4]; + } else if (monitor.ipFamily === "ipv6") { + opts.measurementOptions.ipVersion = IpVersion[6]; + } + + if (monitor.dns_resolve_server) { + opts.measurementOptions.resolver = monitor.dns_resolve_server; + } + + log.debug("monitor", `Globalping create measurement: ${JSON.stringify(opts)}`); + let res = await client.createMeasurement(opts); + + log.debug("monitor", `Globalping ${JSON.stringify(res)}`); + + // Retry if the server returns a 500 error + if (!res.ok && Globalping.isHttpStatus(500, res)) { + res = await client.createMeasurement(opts); + } + + if (!res.ok) { + if (Globalping.isHttpStatus(429, res)) { + throw new Error(`Failed to create measurement: ${this.formatTooManyRequestsError(hasAPIToken)}`); + } + throw new Error(`Failed to create measurement: ${this.formatApiError(res.data.error)}`); + } + + log.debug("monitor", `Globalping fetch measurement: ${res.data.id}`); + let measurement = await client.awaitMeasurement(res.data.id); + + if (!measurement.ok) { + throw new Error( + `Failed to fetch measurement (${res.data.id}): ${this.formatApiError(measurement.data.error)}` + ); + } + + const probe = measurement.data.results[0].probe; + const result = measurement.data.results[0].result; + + if (result.status === "failed") { + throw new Error(this.formatResponse(probe, `Failed: ${result.rawOutput}`)); + } + + let dnsMessage = (result.answers || []).map((answer) => answer.value).join(" | "); + const values = (result.answers || []).map((answer) => answer.value); + + let recordMatched = true; + + // keyword + if (monitor.keyword) { + recordMatched = this.checkDNSRecordValueMatch(monitor, values, monitor.keyword); + } + + if (monitor.dns_last_result !== dnsMessage && dnsMessage !== undefined) { + await redbean.exec("UPDATE `monitor` SET dns_last_result = ? WHERE id = ? ", [dnsMessage, monitor.id]); + } + + heartbeat.ping = result.timings.total || 0; + + if (!dnsMessage) { + dnsMessage = `No records found. ${result.statusCodeName}`; + } + + if (!recordMatched) { + throw new Error(this.formatResponse(probe, "No record matched. " + dnsMessage)); + } + + heartbeat.msg = this.formatResponse(probe, dnsMessage); + heartbeat.status = UP; + } + + /** + * Handles keyword for HTTP monitors. + * @param {Monitor} monitor - The monitor object. + * @param {Array} values - The values to search for. + * @param {string} keyword - The keyword to search for. + * @returns {boolean} True if the regex matches, false otherwise. + */ + checkDNSRecordValueMatch(monitor, values, keyword) { + const regex = new RegExp(keyword, "i"); + + switch (monitor.dns_resolve_type) { + case "A": + case "AAAA": + case "ANY": + case "CNAME": + case "DNSKEY": + case "DS": + case "HTTPS": + case "MX": + case "NS": + case "NSEC": + case "PTR": + case "RRSIG": + case "SOA": + case "SRV": + case "SVCB": + case "TXT": + return values.some((record) => regex.test(record)); + } + + return false; + } + + /** + * Handles keyword search for HTTP monitors. + * @param {Monitor} monitor - The monitor object. + * @param {Heartbeat} heartbeat - The heartbeat object. + * @param {Result} result - The result object. + * @param {Probe} probe - The probe object. + * @returns {Promise} + */ + async handleKeywordForHTTP(monitor, heartbeat, result, probe) { + let data = result.rawOutput; + let keywordFound = data.includes(monitor.keyword); + + if (keywordFound === Boolean(monitor.invertKeyword)) { + data = data.replace(/<[^>]*>?|[\n\r]|\s+/gm, " ").trim(); + if (data.length > 50) { + data = data.substring(0, 47) + "..."; + } + throw new Error( + heartbeat.msg + ", but keyword is " + (keywordFound ? "present" : "not") + " in [" + data + "]" + ); + } + + heartbeat.msg += ", keyword " + (keywordFound ? "is" : "not") + " found"; + heartbeat.status = UP; + } + + /** + * Handles JSON query for HTTP monitors. + * @param {Monitor} monitor - The monitor object. + * @param {Heartbeat} heartbeat - The heartbeat object. + * @param {Result} result - The result object. + * @param {Probe} probe - The probe object. + * @returns {Promise} A promise that resolves when the JSON query is handled. + */ + async handleJSONQueryForHTTP(monitor, heartbeat, result, probe) { + const { status, response } = await evaluateJsonQuery( + result.rawOutput, + monitor.jsonPath, + monitor.jsonPathOperator, + monitor.expectedValue + ); + + if (!status) { + throw new Error( + this.formatResponse( + probe, + `JSON query does not pass (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})` + ) + ); + } + + heartbeat.msg = this.formatResponse( + probe, + `JSON query passes (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})` + ); + heartbeat.status = UP; + } + + /** + * Updates the TLS information for a monitor. + * @param {object} monitor - The monitor object. + * @param {string} protocol - The protocol used for the monitor. + * @param {object} probe - The probe object containing location information. + * @param {object} tlsInfo - The TLS information object. + * @returns {Promise} + */ + async handleTLSInfo(monitor, protocol, probe, tlsInfo) { + if (!tlsInfo) { + return; + } + + if (!monitor.ignoreTls && protocol === "HTTPS" && !tlsInfo.authorized) { + throw new Error(this.formatResponse(probe, `TLS certificate is not authorized: ${tlsInfo.error}`)); + } + + let tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [monitor.id]); + + if (tlsInfoBean == null) { + tlsInfoBean = R.dispense("monitor_tls_info"); + tlsInfoBean.monitor_id = monitor.id; + } else { + try { + let oldCertInfo = JSON.parse(tlsInfoBean.info_json); + + if ( + oldCertInfo && + oldCertInfo.certInfo && + oldCertInfo.certInfo.fingerprint256 !== tlsInfo.fingerprint256 + ) { + log.debug("monitor", "Resetting sent_history"); + await R.exec( + "DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", + [monitor.id] + ); + } + } catch (e) {} + } + + const validTo = new Date(tlsInfo.expiresAt); + const certResult = { + valid: tlsInfo.authorized, + certInfo: { + subject: tlsInfo.subject, + issuer: tlsInfo.issuer, + validTo: validTo, + daysRemaining: getDaysRemaining(new Date(), validTo), + fingerprint: tlsInfo.fingerprint256, + fingerprint256: tlsInfo.fingerprint256, + certType: "", + }, + }; + + tlsInfoBean.info_json = JSON.stringify(certResult); + await R.store(tlsInfoBean); + + if (monitor.prometheus) { + monitor.prometheus.update(null, certResult); + } + + if (!monitor.ignoreTls && monitor.expiryNotification) { + await checkCertExpiryNotifications(monitor, certResult); + } + } + + /** + * Generates the OAuth2 authorization header for the monitor if it is enabled. + * @param {object} monitor - The monitor object containing authentication information. + * @returns {Promise} The OAuth2 authorization header. + */ + async getOauth2AuthHeader(monitor) { + if (monitor.auth_method !== "oauth2-cc") { + return {}; + } + + try { + if (new Date((monitor.oauthAccessToken?.expires_at || 0) * 1000) <= new Date()) { + const oAuthAccessToken = await getOidcTokenClientCredentials( + monitor.oauth_token_url, + monitor.oauth_client_id, + monitor.oauth_client_secret, + monitor.oauth_scopes, + monitor.oauth_audience, + monitor.oauth_auth_method + ); + log.debug( + "monitor", + `[${monitor.name}] Obtained oauth access-token. Expires at ${new Date(oAuthAccessToken.expires_at * 1000)}` + ); + + monitor.oauthAccessToken = oAuthAccessToken; + } + return { + Authorization: monitor.oauthAccessToken.token_type + " " + monitor.oauthAccessToken.access_token, + }; + } catch (e) { + throw new Error("The oauth config is invalid. " + e.message); + } + } + + /** + * Generates the basic authentication header for a monitor if it is enabled. + * @param {object} monitor - The monitor object. + * @returns {object} The basic authentication header. + */ + getBasicAuthHeader(monitor) { + if (monitor.auth_method !== "basic") { + return {}; + } + + return { + Authorization: "Basic " + encodeBase64(monitor.basic_auth_user, monitor.basic_auth_pass), + }; + } + + /** + * Generates the bearer authentication header for a monitor if it is enabled. + * @param {object} monitor - The monitor object. + * @returns {object} The bearer authentication header. + */ + getBearerAuthHeader(monitor) { + if (monitor.auth_method !== "bearer") { + return {}; + } + + return { + Authorization: "Bearer " + monitor.bearer_token, + }; + } + + /** + * Generates a formatted error message for API errors. + * @param {Error} error - The API error object. + * @returns {string} The formatted error message. + */ + formatApiError(error) { + let str = `${error.type} ${error.message}.`; + if (error.params) { + for (const key in error.params) { + str += `\n${key}: ${error.params[key]}`; + } + } + return str; + } + + /** + * Generates a formatted error message for too many requests. + * @param {boolean} hasAPIToken - Indicates whether an API token is available. + * @returns {string} The formatted error message. + */ + formatTooManyRequestsError(hasAPIToken) { + const creditsHelpLink = "https://dash.globalping.io?view=add-credits"; + if (hasAPIToken) { + return `You have run out of credits. Get higher limits by sponsoring us or hosting probes. Learn more at ${creditsHelpLink}.`; + } + return `You have run out of credits. Get higher limits by creating an account. Sign up at ${creditsHelpLink}.`; + } + + /** + * Returns the formatted probe location string. e.g "Ashburn (VA), US, NA, Amazon.com (AS14618), (aws-us-east-1)" + * @param {object} probe - The probe object containing location information. + * @returns {string} The formatted probe location string. + */ + formatProbeLocation(probe) { + let tag = ""; + + for (const t of probe.tags) { + // If tag ends in a number, it's likely a region code and should be displayed + if (Number.isInteger(Number(t.slice(-1)))) { + tag = t; + break; + } + } + return `${probe.city}${probe.state ? ` (${probe.state})` : ""}, ${probe.country}, ${probe.continent}, ${ + probe.network + } (AS${probe.asn})${tag ? `, (${tag})` : ""}`; + } + + /** + * Formats the response text with the probe location. + * @param {object} probe - The probe object containing location information. + * @param {string} text - The response text to append. + * @returns {string} The formatted response text. + */ + formatResponse(probe, text) { + return `${this.formatProbeLocation(probe)} : ${text}`; + } +} + +module.exports = { + GlobalpingMonitorType, +}; diff --git a/server/monitor-types/group.js b/server/monitor-types/group.js new file mode 100644 index 0000000..b418114 --- /dev/null +++ b/server/monitor-types/group.js @@ -0,0 +1,79 @@ +const { UP, PENDING, DOWN } = require("../../src/util"); +const { MonitorType } = require("./monitor-type"); +const Monitor = require("../model/monitor"); + +class GroupMonitorType extends MonitorType { + name = "group"; + allowCustomStatus = true; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + const children = await Monitor.getChildren(monitor.id); + + if (children.length === 0) { + // Set status pending if group is empty + heartbeat.status = PENDING; + heartbeat.msg = "Group empty"; + return; + } + + let worstStatus = UP; + const downChildren = []; + const pendingChildren = []; + + for (const child of children) { + if (!child.active) { + // Ignore inactive (=paused) children + continue; + } + + const label = child.name || `#${child.id}`; + const lastBeat = await Monitor.getPreviousHeartbeat(child.id); + + if (!lastBeat) { + if (worstStatus === UP) { + worstStatus = PENDING; + } + pendingChildren.push(label); + continue; + } + + if (lastBeat.status === DOWN) { + worstStatus = DOWN; + downChildren.push(label); + } else if (lastBeat.status === PENDING) { + if (worstStatus !== DOWN) { + worstStatus = PENDING; + } + pendingChildren.push(label); + } + } + + if (worstStatus === UP) { + heartbeat.status = UP; + heartbeat.msg = "All children up and running"; + return; + } + + if (worstStatus === PENDING) { + heartbeat.status = PENDING; + heartbeat.msg = `Pending child monitors: ${pendingChildren.join(", ")}`; + return; + } + + let message = `Child monitors down: ${downChildren.join(", ")}`; + + if (pendingChildren.length > 0) { + message += `; pending: ${pendingChildren.join(", ")}`; + } + + // Throw to leverage the generic retry handling and notification flow + throw new Error(message); + } +} + +module.exports = { + GroupMonitorType, +}; diff --git a/server/monitor-types/grpc.js b/server/monitor-types/grpc.js new file mode 100644 index 0000000..0fa9d5c --- /dev/null +++ b/server/monitor-types/grpc.js @@ -0,0 +1,104 @@ +const { MonitorType } = require("./monitor-type"); +const { UP, log } = require("../../src/util"); +const dayjs = require("dayjs"); +const grpc = require("@grpc/grpc-js"); +const protojs = require("protobufjs"); + +class GrpcKeywordMonitorType extends MonitorType { + name = "grpc-keyword"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + const startTime = dayjs().valueOf(); + const service = this.constructGrpcService( + monitor.grpcUrl, + monitor.grpcProtobuf, + monitor.grpcServiceName, + monitor.grpcEnableTls + ); + let response = await this.grpcQuery(service, monitor.grpcMethod, monitor.grpcBody); + heartbeat.ping = dayjs().valueOf() - startTime; + log.debug(this.name, "gRPC response:", response); + let keywordFound = response.toString().includes(monitor.keyword); + if (keywordFound !== !monitor.isInvertKeyword()) { + log.debug( + this.name, + `GRPC response [${response}] + ", but keyword [${monitor.keyword}] is ${keywordFound ? "present" : "not"} in [" + ${response} + "]"` + ); + + let truncatedResponse = response.length > 50 ? response.toString().substring(0, 47) + "..." : response; + + throw new Error( + `keyword [${monitor.keyword}] is ${keywordFound ? "present" : "not"} in [" + ${truncatedResponse} + "]` + ); + } + heartbeat.status = UP; + heartbeat.msg = `${response}, keyword [${monitor.keyword}] ${keywordFound ? "is" : "not"} found`; + } + + /** + * Create gRPC client + * @param {string} url grpc Url + * @param {string} protobufData grpc ProtobufData + * @param {string} serviceName grpc ServiceName + * @param {string} enableTls grpc EnableTls + * @returns {grpc.Service} grpc Service + */ + constructGrpcService(url, protobufData, serviceName, enableTls) { + const protocObject = protojs.parse(protobufData); + const protoServiceObject = protocObject.root.lookupService(serviceName); + const Client = grpc.makeGenericClientConstructor({}); + const credentials = enableTls ? grpc.credentials.createSsl() : grpc.credentials.createInsecure(); + const client = new Client(url, credentials); + return protoServiceObject.create( + (method, requestData, cb) => { + const fullServiceName = method.fullName; + const serviceFQDN = fullServiceName.split("."); + const serviceMethod = serviceFQDN.pop(); + const serviceMethodClientImpl = `/${serviceFQDN.slice(1).join(".")}/${serviceMethod}`; + log.debug(this.name, `gRPC method ${serviceMethodClientImpl}`); + client.makeUnaryRequest( + serviceMethodClientImpl, + (arg) => arg, + (arg) => arg, + requestData, + cb + ); + }, + false, + false + ); + } + + /** + * Create gRPC client stib + * @param {grpc.Service} service grpc Url + * @param {string} method grpc Method + * @param {string} body grpc Body + * @returns {Promise} Result of gRPC query + */ + async grpcQuery(service, method, body) { + return new Promise((resolve, reject) => { + try { + service[method](JSON.parse(body), (err, response) => { + if (err) { + if (err.code !== 1) { + reject(err); + } + log.debug(this.name, `ignoring ${err.code} ${err.details}, as code=1 is considered OK`); + resolve(`${err.code} is considered OK because ${err.details}`); + } + resolve(JSON.stringify(response)); + }); + } catch (err) { + reject(err); + } + }); + } +} + +module.exports = { + GrpcKeywordMonitorType, +}; diff --git a/server/monitor-types/manual.js b/server/monitor-types/manual.js new file mode 100644 index 0000000..2b277a1 --- /dev/null +++ b/server/monitor-types/manual.js @@ -0,0 +1,38 @@ +const { MonitorType } = require("./monitor-type"); +const { UP, DOWN, PENDING } = require("../../src/util"); + +class ManualMonitorType extends MonitorType { + name = "Manual"; + type = "manual"; + description = "A monitor that allows manual control of the status"; + supportsConditions = false; + conditionVariables = []; + + allowCustomStatus = true; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat) { + if (monitor.manual_status !== null) { + heartbeat.status = monitor.manual_status; + switch (monitor.manual_status) { + case UP: + heartbeat.msg = "Up"; + break; + case DOWN: + heartbeat.msg = "Down"; + break; + default: + heartbeat.msg = "Pending"; + } + } else { + heartbeat.status = PENDING; + heartbeat.msg = "Manual monitoring - No status set"; + } + } +} + +module.exports = { + ManualMonitorType, +}; diff --git a/server/monitor-types/mongodb.js b/server/monitor-types/mongodb.js new file mode 100644 index 0000000..bbe217c --- /dev/null +++ b/server/monitor-types/mongodb.js @@ -0,0 +1,67 @@ +const { MonitorType } = require("./monitor-type"); +const { UP } = require("../../src/util"); +const { MongoClient } = require("mongodb"); +const jsonata = require("jsonata"); + +class MongodbMonitorType extends MonitorType { + name = "mongodb"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + let command = { ping: 1 }; + if (monitor.databaseQuery) { + command = JSON.parse(monitor.databaseQuery); + } + + let result = await this.runMongodbCommand(monitor.databaseConnectionString, command); + + if (result["ok"] !== 1) { + throw new Error("MongoDB command failed"); + } else { + heartbeat.msg = "Command executed successfully"; + } + + if (monitor.jsonPath) { + let expression = jsonata(monitor.jsonPath); + result = await expression.evaluate(result); + if (result) { + heartbeat.msg = "Command executed successfully and the jsonata expression produces a result."; + } else { + throw new Error("Queried value not found."); + } + } + + if (monitor.expectedValue) { + if (result.toString() === monitor.expectedValue) { + heartbeat.msg = "Command executed successfully and expected value was found"; + } else { + throw new Error( + "Query executed, but value is not equal to expected value, value was: [" + + JSON.stringify(result) + + "]" + ); + } + } + + heartbeat.status = UP; + } + + /** + * Connect to and run MongoDB command on a MongoDB database + * @param {string} connectionString The database connection string + * @param {object} command MongoDB command to run on the database + * @returns {Promise<(string[] | object[] | object)>} Response from server + */ + async runMongodbCommand(connectionString, command) { + let client = await MongoClient.connect(connectionString); + let result = await client.db().command(command); + await client.close(); + return result; + } +} + +module.exports = { + MongodbMonitorType, +}; diff --git a/server/monitor-types/monitor-type.js b/server/monitor-types/monitor-type.js new file mode 100644 index 0000000..aa7b1d2 --- /dev/null +++ b/server/monitor-types/monitor-type.js @@ -0,0 +1,40 @@ +class MonitorType { + name = undefined; + + /** + * Whether or not this type supports monitor conditions. Controls UI visibility in monitor form. + * @type {boolean} + */ + supportsConditions = false; + + /** + * Variables supported by this type. e.g. an HTTP type could have a "response_code" variable to test against. + * This property controls the choices displayed in the monitor edit form. + * @type {import("../monitor-conditions/variables").ConditionVariable[]} + */ + conditionVariables = []; + + /** + * Allows setting any custom status to heartbeat, other than UP. + * @type {boolean} + */ + allowCustomStatus = false; + + /** + * Run the monitoring check on the given monitor + * + * Successful cases: Should update heartbeat.status to "up" and set response time. + * Failure cases: Throw an error with a descriptive message. + * @param {Monitor} monitor Monitor to check + * @param {Heartbeat} heartbeat Monitor heartbeat to update + * @param {UptimeKumaServer} server Uptime Kuma server + * @returns {Promise} + */ + async check(monitor, heartbeat, server) { + throw new Error("You need to override check()"); + } +} + +module.exports = { + MonitorType, +}; diff --git a/server/monitor-types/mqtt.js b/server/monitor-types/mqtt.js new file mode 100644 index 0000000..abf9189 --- /dev/null +++ b/server/monitor-types/mqtt.js @@ -0,0 +1,204 @@ +const { MonitorType } = require("./monitor-type"); +const { log, UP } = require("../../src/util"); +const mqtt = require("mqtt"); +const jsonata = require("jsonata"); +const { ConditionVariable } = require("../monitor-conditions/variables"); +const { defaultStringOperators, defaultNumberOperators } = require("../monitor-conditions/operators"); +const { ConditionExpressionGroup } = require("../monitor-conditions/expression"); +const { evaluateExpressionGroup } = require("../monitor-conditions/evaluator"); + +class MqttMonitorType extends MonitorType { + name = "mqtt"; + + supportsConditions = true; + + conditionVariables = [ + new ConditionVariable("topic", defaultStringOperators), + new ConditionVariable("message", defaultStringOperators), + new ConditionVariable("json_value", defaultStringOperators.concat(defaultNumberOperators)), + ]; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, server) { + const [messageTopic, receivedMessage] = await this.mqttAsync(monitor.hostname, monitor.mqttTopic, { + port: monitor.port, + username: monitor.mqttUsername, + password: monitor.mqttPassword, + interval: monitor.interval, + websocketPath: monitor.mqttWebsocketPath, + }); + + if (monitor.mqttCheckType == null || monitor.mqttCheckType === "") { + monitor.mqttCheckType = "keyword"; + } + + // Check if conditions are defined + const conditions = monitor.conditions ? ConditionExpressionGroup.fromMonitor(monitor) : null; + const hasConditions = conditions && conditions.children && conditions.children.length > 0; + + if (hasConditions) { + await this.checkConditions(monitor, heartbeat, messageTopic, receivedMessage, conditions); + } else if (monitor.mqttCheckType === "keyword") { + this.checkKeyword(monitor, heartbeat, messageTopic, receivedMessage); + } else if (monitor.mqttCheckType === "json-query") { + await this.checkJsonQuery(monitor, heartbeat, receivedMessage); + } else { + throw new Error("Unknown MQTT Check Type"); + } + } + + /** + * Check using keyword matching + * @param {object} monitor Monitor object + * @param {object} heartbeat Heartbeat object + * @param {string} messageTopic Received MQTT topic + * @param {string} receivedMessage Received MQTT message + * @returns {void} + * @throws {Error} If keyword is not found in message + */ + checkKeyword(monitor, heartbeat, messageTopic, receivedMessage) { + if (receivedMessage != null && receivedMessage.includes(monitor.mqttSuccessMessage)) { + heartbeat.msg = `Topic: ${messageTopic}; Message: ${receivedMessage}`; + heartbeat.status = UP; + } else { + throw new Error(`Message Mismatch - Topic: ${monitor.mqttTopic}; Message: ${receivedMessage}`); + } + } + + /** + * Check using JSONata query + * @param {object} monitor Monitor object + * @param {object} heartbeat Heartbeat object + * @param {string} receivedMessage Received MQTT message + * @returns {Promise} + */ + async checkJsonQuery(monitor, heartbeat, receivedMessage) { + const parsedMessage = JSON.parse(receivedMessage); + const expression = jsonata(monitor.jsonPath); + const result = await expression.evaluate(parsedMessage); + + if (result?.toString() === monitor.expectedValue) { + heartbeat.msg = "Message received, expected value is found"; + heartbeat.status = UP; + } else { + throw new Error("Message received but value is not equal to expected value, value was: [" + result + "]"); + } + } + + /** + * Check using conditions system + * @param {object} monitor Monitor object + * @param {object} heartbeat Heartbeat object + * @param {string} messageTopic Received MQTT topic + * @param {string} receivedMessage Received MQTT message + * @param {ConditionExpressionGroup} conditions Parsed conditions + * @returns {Promise} + */ + async checkConditions(monitor, heartbeat, messageTopic, receivedMessage, conditions) { + let jsonValue = null; + + // Parse JSON and extract value if jsonPath is defined + if (monitor.jsonPath) { + try { + const parsedMessage = JSON.parse(receivedMessage); + const expression = jsonata(monitor.jsonPath); + jsonValue = await expression.evaluate(parsedMessage); + } catch (e) { + // JSON parsing failed, jsonValue remains null + } + } + + const conditionData = { + topic: messageTopic, + message: receivedMessage, + json_value: jsonValue?.toString() ?? "", + }; + + const conditionsResult = evaluateExpressionGroup(conditions, conditionData); + + if (conditionsResult) { + heartbeat.msg = `Topic: ${messageTopic}; Message: ${receivedMessage}`; + heartbeat.status = UP; + } else { + throw new Error(`Conditions not met - Topic: ${messageTopic}; Message: ${receivedMessage}`); + } + } + + /** + * Connect to MQTT Broker, subscribe to topic and receive message as String + * @param {string} hostname Hostname / address of machine to test + * @param {string} topic MQTT topic + * @param {object} options MQTT options. Contains port, username, + * password, websocketPath and interval (interval defaults to 20) + * @returns {Promise} Received MQTT message + */ + mqttAsync(hostname, topic, options = {}) { + return new Promise((resolve, reject) => { + const { port, username, password, websocketPath, interval = 20 } = options; + + // Adds MQTT protocol to the hostname if not already present + if (!/^(?:http|mqtt|ws)s?:\/\//.test(hostname)) { + hostname = "mqtt://" + hostname; + } + + const timeoutID = setTimeout( + () => { + log.debug(this.name, "MQTT timeout triggered"); + client.end(); + reject(new Error("Timeout, Message not received")); + }, + interval * 1000 * 0.8 + ); + + // Construct the URL based on protocol + let mqttUrl = `${hostname}:${port}`; + if (hostname.startsWith("ws://") || hostname.startsWith("wss://")) { + if (websocketPath && !websocketPath.startsWith("/")) { + mqttUrl = `${hostname}:${port}/${websocketPath || ""}`; + } else { + mqttUrl = `${hostname}:${port}${websocketPath || ""}`; + } + } + + log.debug(this.name, `MQTT connecting to ${mqttUrl}`); + + let client = mqtt.connect(mqttUrl, { + username, + password, + clientId: "uptime-kuma_" + Math.random().toString(16).substr(2, 8), + }); + + client.on("connect", () => { + log.debug(this.name, "MQTT connected"); + + try { + client.subscribe(topic, () => { + log.debug(this.name, "MQTT subscribed to topic"); + }); + } catch (e) { + client.end(); + clearTimeout(timeoutID); + reject(new Error("Cannot subscribe topic")); + } + }); + + client.on("error", (error) => { + client.end(); + clearTimeout(timeoutID); + reject(error); + }); + + client.on("message", (messageTopic, message) => { + client.end(); + clearTimeout(timeoutID); + resolve([messageTopic, message.toString("utf8")]); + }); + }); + } +} + +module.exports = { + MqttMonitorType, +}; diff --git a/server/monitor-types/mssql.js b/server/monitor-types/mssql.js new file mode 100644 index 0000000..653dc7a --- /dev/null +++ b/server/monitor-types/mssql.js @@ -0,0 +1,135 @@ +const { MonitorType } = require("./monitor-type"); +const { log, UP } = require("../../src/util"); +const dayjs = require("dayjs"); +const mssql = require("mssql"); +const { ConditionVariable } = require("../monitor-conditions/variables"); +const { defaultStringOperators } = require("../monitor-conditions/operators"); +const { ConditionExpressionGroup } = require("../monitor-conditions/expression"); +const { evaluateExpressionGroup } = require("../monitor-conditions/evaluator"); + +class MssqlMonitorType extends MonitorType { + name = "sqlserver"; + + supportsConditions = true; + conditionVariables = [new ConditionVariable("result", defaultStringOperators)]; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + let query = monitor.databaseQuery; + // No query provided by user, use SELECT 1 + if (!query || (typeof query === "string" && query.trim() === "")) { + query = "SELECT 1"; + } + + const conditions = monitor.conditions ? ConditionExpressionGroup.fromMonitor(monitor) : null; + const hasConditions = conditions && conditions.children && conditions.children.length > 0; + + const startTime = dayjs().valueOf(); + try { + if (hasConditions) { + // When conditions are enabled, expect a single value result + const result = await this.mssqlQuerySingleValue(monitor.databaseConnectionString, query); + heartbeat.ping = dayjs().valueOf() - startTime; + + const conditionsResult = evaluateExpressionGroup(conditions, { result: String(result) }); + + if (!conditionsResult) { + throw new Error(`Query result did not meet the specified conditions (${result})`); + } + + heartbeat.status = UP; + heartbeat.msg = "Query did meet specified conditions"; + } else { + // Backwards compatible: just check connection and return row count + const result = await this.mssqlQuery(monitor.databaseConnectionString, query); + heartbeat.ping = dayjs().valueOf() - startTime; + heartbeat.status = UP; + heartbeat.msg = result; + } + } catch (error) { + heartbeat.ping = dayjs().valueOf() - startTime; + // Re-throw condition errors as-is, wrap database errors + if (error.message.includes("did not meet the specified conditions")) { + throw error; + } + throw new Error(`Database connection/query failed: ${error.message}`); + } + } + + /** + * Run a query on MSSQL server (backwards compatible - returns row count) + * @param {string} connectionString The database connection string + * @param {string} query The query to validate the database with + * @returns {Promise} Row count message + */ + async mssqlQuery(connectionString, query) { + let pool; + try { + pool = new mssql.ConnectionPool(connectionString); + await pool.connect(); + const result = await pool.request().query(query); + + if (result.recordset) { + return "Rows: " + result.recordset.length; + } else { + return "No Error, but the result is not an array. Type: " + typeof result.recordset; + } + } catch (err) { + log.debug("sqlserver", "Error caught in the query execution.", err.message); + throw err; + } finally { + if (pool) { + await pool.close(); + } + } + } + + /** + * Run a query on MSSQL server expecting a single value result + * @param {string} connectionString The database connection string + * @param {string} query The query to validate the database with + * @returns {Promise} Single value from the first column of the first row + */ + async mssqlQuerySingleValue(connectionString, query) { + let pool; + try { + pool = new mssql.ConnectionPool(connectionString); + await pool.connect(); + const result = await pool.request().query(query); + + // Check if we have results + if (!result.recordset || result.recordset.length === 0) { + throw new Error("Query returned no results"); + } + + // Check if we have multiple rows + if (result.recordset.length > 1) { + throw new Error("Multiple values were found, expected only one value"); + } + + const firstRow = result.recordset[0]; + const columnNames = Object.keys(firstRow); + + // Check if we have multiple columns + if (columnNames.length > 1) { + throw new Error("Multiple columns were found, expected only one value"); + } + + // Return the single value from the first (and only) column + return firstRow[columnNames[0]]; + } catch (err) { + log.debug("sqlserver", "Error caught in the query execution.", err.message); + throw err; + } finally { + if (pool) { + await pool.close(); + } + } + } +} + +module.exports = { + MssqlMonitorType, +}; diff --git a/server/monitor-types/mysql.js b/server/monitor-types/mysql.js new file mode 100644 index 0000000..4eb99a7 --- /dev/null +++ b/server/monitor-types/mysql.js @@ -0,0 +1,163 @@ +const { MonitorType } = require("./monitor-type"); +const { UP } = require("../../src/util"); +const dayjs = require("dayjs"); +const mysql = require("mysql2"); +const { ConditionVariable } = require("../monitor-conditions/variables"); +const { defaultStringOperators } = require("../monitor-conditions/operators"); +const { ConditionExpressionGroup } = require("../monitor-conditions/expression"); +const { evaluateExpressionGroup } = require("../monitor-conditions/evaluator"); + +class MysqlMonitorType extends MonitorType { + name = "mysql"; + + supportsConditions = true; + conditionVariables = [new ConditionVariable("result", defaultStringOperators)]; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + let query = monitor.databaseQuery; + if (!query || (typeof query === "string" && query.trim() === "")) { + query = "SELECT 1"; + } + + // Use `radius_password` as `password` field for backwards compatibility + // TODO: rename `radius_password` to `password` later for general use + const password = monitor.radiusPassword; + + const conditions = monitor.conditions ? ConditionExpressionGroup.fromMonitor(monitor) : null; + const hasConditions = conditions && conditions.children && conditions.children.length > 0; + + const startTime = dayjs().valueOf(); + try { + if (hasConditions) { + // When conditions are enabled, expect a single value result + const result = await this.mysqlQuerySingleValue(monitor.databaseConnectionString, query, password); + heartbeat.ping = dayjs().valueOf() - startTime; + + const conditionsResult = evaluateExpressionGroup(conditions, { result: String(result) }); + + if (!conditionsResult) { + throw new Error(`Query result did not meet the specified conditions (${result})`); + } + + heartbeat.status = UP; + heartbeat.msg = "Query did meet specified conditions"; + } else { + // Backwards compatible: just check connection and return row count + const result = await this.mysqlQuery(monitor.databaseConnectionString, query, password); + heartbeat.ping = dayjs().valueOf() - startTime; + heartbeat.status = UP; + heartbeat.msg = result; + } + } catch (error) { + heartbeat.ping = dayjs().valueOf() - startTime; + // Re-throw condition errors as-is, wrap database errors + if (error.message.includes("did not meet the specified conditions")) { + throw error; + } + throw new Error(`Database connection/query failed: ${error.message}`); + } + } + + /** + * Run a query on MySQL/MariaDB (backwards compatible - returns row count) + * @param {string} connectionString The database connection string + * @param {string} query The query to execute + * @param {string} password Optional password override + * @returns {Promise} Row count message + */ + mysqlQuery(connectionString, query, password = undefined) { + return new Promise((resolve, reject) => { + const connection = mysql.createConnection({ + uri: connectionString, + password, + }); + + connection.on("error", (err) => { + reject(err); + }); + + connection.query(query, (err, res) => { + try { + connection.end(); + } catch (_) { + connection.destroy(); + } + + if (err) { + reject(err); + return; + } + + if (Array.isArray(res)) { + resolve("Rows: " + res.length); + } else { + resolve("No Error, but the result is not an array. Type: " + typeof res); + } + }); + }); + } + + /** + * Run a query on MySQL/MariaDB expecting a single value result + * @param {string} connectionString The database connection string + * @param {string} query The query to execute + * @param {string} password Optional password override + * @returns {Promise} Single value from the first column of the first row + */ + mysqlQuerySingleValue(connectionString, query, password = undefined) { + return new Promise((resolve, reject) => { + const connection = mysql.createConnection({ + uri: connectionString, + password, + }); + + connection.on("error", (err) => { + reject(err); + }); + + connection.query(query, (err, res) => { + try { + connection.end(); + } catch (_) { + connection.destroy(); + } + + if (err) { + reject(err); + return; + } + + // Check if we have results + if (!Array.isArray(res) || res.length === 0) { + reject(new Error("Query returned no results")); + return; + } + + // Check if we have multiple rows + if (res.length > 1) { + reject(new Error("Multiple values were found, expected only one value")); + return; + } + + const firstRow = res[0]; + const columnNames = Object.keys(firstRow); + + // Check if we have multiple columns + if (columnNames.length > 1) { + reject(new Error("Multiple columns were found, expected only one value")); + return; + } + + // Return the single value from the first (and only) column + resolve(firstRow[columnNames[0]]); + }); + }); + } +} + +module.exports = { + MysqlMonitorType, +}; diff --git a/server/monitor-types/ntp.js b/server/monitor-types/ntp.js new file mode 100644 index 0000000..003a66b --- /dev/null +++ b/server/monitor-types/ntp.js @@ -0,0 +1,202 @@ +const { MonitorType } = require("./monitor-type"); +const { UP } = require("../../src/util"); +const dayjs = require("dayjs"); +const dgram = require("dgram"); +const dns = require("dns"); + +/** + * NTP Monitor Type + * Monitors NTP servers for availability, time accuracy, and quality metrics + */ +class NTPMonitorType extends MonitorType { + name = "ntp"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + const startTime = dayjs().valueOf(); + + if (!monitor.hostname) { + throw new Error("Hostname is required"); + } + + const port = monitor.port || 123; + const timeout = (monitor.timeout || 10) * 1000; + + const ntpResult = await this.queryNTP(monitor.hostname, port, timeout); + + heartbeat.ping = dayjs().valueOf() - startTime; + + const { stratum, offset, rootDispersion, refid, roundTripDelay } = ntpResult; + + heartbeat.msg = `Stratum: ${stratum}, RefID: ${refid}, Offset: ${offset.toFixed(3)}ms, Delay: ${roundTripDelay.toFixed(3)}ms, Dispersion: ${rootDispersion.toFixed(3)}ms`; + + if (stratum === 16) { + throw new Error("NTP server is unsynchronized (stratum 16)"); + } + + const stratumThreshold = monitor.ntp_stratum_threshold || 5; + if (stratum >= stratumThreshold) { + throw new Error(`Stratum ${stratum} meets or exceeds threshold ${stratumThreshold}`); + } + + const offsetThreshold = monitor.ntp_time_offset_threshold || 1000; + if (Math.abs(offset) >= offsetThreshold) { + throw new Error(`Time offset ${offset.toFixed(3)}ms exceeds threshold ${offsetThreshold}ms`); + } + + const dispersionThreshold = monitor.ntp_root_dispersion_threshold || 500; + if (rootDispersion >= dispersionThreshold) { + throw new Error( + `Root dispersion ${rootDispersion.toFixed(3)}ms exceeds threshold ${dispersionThreshold}ms` + ); + } + + heartbeat.status = UP; + } + + /** + * Query an NTP server via UDP + * @param {string} hostname NTP server hostname or IP + * @param {number} port NTP server port (usually 123) + * @param {number} timeout Timeout in milliseconds + * @returns {Promise} Parsed NTP response data + */ + queryNTP(hostname, port, timeout) { + return new Promise((resolve, reject) => { + let client = null; + let settled = false; + let timeoutHandle = null; + + const finish = (fn, value) => { + if (settled) { + return; + } + settled = true; + clearTimeout(timeoutHandle); + if (client) { + client.close(); + } + fn(value); + }; + + timeoutHandle = setTimeout(() => { + finish(reject, new Error("NTP request timed out")); + }, timeout); + + dns.lookup(hostname, (dnsErr, address, family) => { + if (settled) { + return; + } + if (dnsErr) { + finish(reject, new Error(`DNS lookup failed for ${hostname}: ${dnsErr.message}`)); + return; + } + + client = dgram.createSocket(family === 6 ? "udp6" : "udp4"); + const ntpPacket = this.createNTPPacket(); + + const NTP_EPOCH_OFFSET_MS = 2208988800000; + const t1 = Date.now() + NTP_EPOCH_OFFSET_MS; + + client.on("error", (err) => { + finish(reject, new Error(`UDP socket error: ${err.message}`)); + }); + + client.on("message", (msg) => { + const t4 = Date.now() + NTP_EPOCH_OFFSET_MS; + try { + const result = this.parseNTPResponse(msg, t1, t4); + finish(resolve, result); + } catch (err) { + finish(reject, err); + } + }); + + client.send(ntpPacket, 0, ntpPacket.length, port, address, (err) => { + if (err) { + finish(reject, new Error(`Failed to send NTP request: ${err.message}`)); + } + }); + }); + }); + } + + /** + * Create an NTP version 3 client request packet (48 bytes) + * Byte 0: LI=0 (no warning), VN=3 (NTPv3), Mode=3 (client) = 0x1B + * @returns {Buffer} NTP request packet + */ + createNTPPacket() { + const packet = Buffer.alloc(48); + packet[0] = 0x1b; + return packet; + } + + /** + * Parse an NTP response packet and calculate offset/delay + * @param {Buffer} msg NTP response packet (48+ bytes) + * @param {number} t1 Client originate timestamp in ms since NTP epoch (1900) + * @param {number} t4 Client receive timestamp in ms since NTP epoch (1900) + * @returns {object} Parsed NTP data including stratum, offset, refid, rootDispersion, roundTripDelay + * @throws {Error} If the packet is shorter than 48 bytes + */ + parseNTPResponse(msg, t1, t4) { + if (msg.length < 48) { + throw new Error(`Invalid NTP response: expected 48+ bytes, got ${msg.length}`); + } + + const leapIndicator = (msg[0] >> 6) & 0x03; + const stratum = msg[1]; + + // Root dispersion: 32-bit unsigned fixed-point at offset 8, unit = seconds + const rootDispersionRaw = msg.readUInt32BE(8); + const rootDispersion = (rootDispersionRaw / 65536) * 1000; + + // Reference ID: ASCII for stratum 0-1, IPv4 address for stratum 2+ + let refid; + if (stratum <= 1) { + refid = msg.toString("ascii", 12, 16).replace(/\0/g, "").trim(); + } else { + refid = `${msg[12]}.${msg[13]}.${msg[14]}.${msg[15]}`; + } + + // Server receive timestamp (T2) at offset 32 + const t2 = this.readNTPTimestamp(msg, 32); + // Server transmit timestamp (T3) at offset 40 + const t3 = this.readNTPTimestamp(msg, 40); + + // RFC 5905 offset and delay calculations + // offset = ((T2 - T1) + (T3 - T4)) / 2 + // delay = (T4 - T1) - (T3 - T2) + const offset = (t2 - t1 + (t3 - t4)) / 2; + const roundTripDelay = t4 - t1 - (t3 - t2); + + return { + leapIndicator, + stratum, + rootDispersion, + refid, + offset, + roundTripDelay, + }; + } + + /** + * Read a 64-bit NTP timestamp from a buffer and convert to milliseconds since NTP epoch + * NTP timestamps are 32 bits of seconds + 32 bits of fractional seconds since 1900-01-01 + * @param {Buffer} buf Packet buffer + * @param {number} offset Byte offset in the buffer + * @returns {number} Timestamp in milliseconds since NTP epoch (1900) + */ + readNTPTimestamp(buf, offset) { + const seconds = buf.readUInt32BE(offset); + const fraction = buf.readUInt32BE(offset + 4); + return seconds * 1000 + (fraction * 1000) / 0x100000000; + } +} + +module.exports = { + NTPMonitorType, +}; diff --git a/server/monitor-types/oracledb.js b/server/monitor-types/oracledb.js new file mode 100644 index 0000000..8b5af97 --- /dev/null +++ b/server/monitor-types/oracledb.js @@ -0,0 +1,155 @@ +const { MonitorType } = require("./monitor-type"); +const { log, UP } = require("../../src/util"); +const dayjs = require("dayjs"); +const oracledb = require("oracledb"); +const { ConditionVariable } = require("../monitor-conditions/variables"); +const { defaultStringOperators } = require("../monitor-conditions/operators"); +const { ConditionExpressionGroup } = require("../monitor-conditions/expression"); +const { evaluateExpressionGroup } = require("../monitor-conditions/evaluator"); + +class OracleDbMonitorType extends MonitorType { + name = "oracledb"; + + supportsConditions = true; + conditionVariables = [new ConditionVariable("result", defaultStringOperators)]; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + let query = monitor.databaseQuery; + if (!query || (typeof query === "string" && query.trim() === "")) { + query = "SELECT 1 FROM DUAL"; + } + + const conditions = monitor.conditions ? ConditionExpressionGroup.fromMonitor(monitor) : null; + const hasConditions = conditions && conditions.children && conditions.children.length > 0; + + const startTime = dayjs().valueOf(); + try { + if (hasConditions) { + const result = await this.oracledbQuerySingleValue( + monitor.databaseConnectionString, + query, + monitor.basic_auth_user, + monitor.basic_auth_pass + ); + heartbeat.ping = dayjs().valueOf() - startTime; + + const conditionsResult = evaluateExpressionGroup(conditions, { result: String(result) }); + + if (!conditionsResult) { + throw new Error(`Query result did not meet the specified conditions (${result})`); + } + + heartbeat.status = UP; + heartbeat.msg = "Query did meet specified conditions"; + } else { + const result = await this.oracledbQuery( + monitor.databaseConnectionString, + query, + monitor.basic_auth_user, + monitor.basic_auth_pass + ); + heartbeat.ping = dayjs().valueOf() - startTime; + heartbeat.status = UP; + heartbeat.msg = result; + } + } catch (error) { + heartbeat.ping = dayjs().valueOf() - startTime; + if (error.message.includes("did not meet the specified conditions")) { + throw error; + } + throw new Error(`Database connection/query failed: ${error.message}`); + } + } + + /** + * Run a query on Oracle Database. + * @param {string} connectionString The Oracle DB connection string + * @param {string} query The query to execute + * @param {string} username Oracle DB username + * @param {string} password Oracle DB password + * @returns {Promise} Row count or execution message + */ + async oracledbQuery(connectionString, query, username, password) { + let connection; + try { + connection = await oracledb.getConnection({ + connectString: connectionString.trim(), + user: username.trim(), + password: password.trim(), + }); + const result = await connection.execute(query, [], { + outFormat: oracledb.OUT_FORMAT_OBJECT, + }); + + if (Array.isArray(result.rows)) { + return `Rows: ${result.rows.length}`; + } + + if (typeof result.rowsAffected === "number") { + return `Rows affected: ${result.rowsAffected}`; + } + + return "Query executed successfully"; + } catch (error) { + log.debug(this.name, "Error caught in the query execution.", error.message); + throw error; + } finally { + if (connection) { + await connection.close(); + } + } + } + + /** + * Run a query on Oracle Database expecting a single value result. + * @param {string} connectionString The Oracle DB connection string + * @param {string} query The query to execute + * @param {string} username Oracle DB username + * @param {string} password Oracle DB password + * @returns {Promise} Single value from the first column of the first row + */ + async oracledbQuerySingleValue(connectionString, query, username, password) { + let connection; + try { + connection = await oracledb.getConnection({ + connectString: connectionString, + user: username, + password: password, + }); + const result = await connection.execute(query, [], { + outFormat: oracledb.OUT_FORMAT_OBJECT, + }); + + if (!result.rows || result.rows.length === 0) { + throw new Error("Query returned no results"); + } + + if (result.rows.length > 1) { + throw new Error("Multiple values were found, expected only one value"); + } + + const firstRow = result.rows[0]; + const columnNames = Object.keys(firstRow); + + if (columnNames.length > 1) { + throw new Error("Multiple columns were found, expected only one value"); + } + + return firstRow[columnNames[0]]; + } catch (error) { + log.debug(this.name, "Error caught in the query execution.", error.message); + throw error; + } finally { + if (connection) { + await connection.close(); + } + } + } +} + +module.exports = { + OracleDbMonitorType, +}; diff --git a/server/monitor-types/pm2.js b/server/monitor-types/pm2.js new file mode 100644 index 0000000..c212174 --- /dev/null +++ b/server/monitor-types/pm2.js @@ -0,0 +1,36 @@ +const { MonitorType } = require("./monitor-type"); +const { UP } = require("../../src/util"); +const { getPM2ProcessList } = require("../util/pm2"); + +class PM2MonitorType extends MonitorType { + name = "pm2"; + description = "Checks if a PM2 process is online."; + + /** + * Check the PM2 process status. + * @param {object} monitor The monitor object containing monitor.system_service_name. + * @param {object} heartbeat The heartbeat object to update. + * @returns {Promise} + */ + async check(monitor, heartbeat) { + const processName = (monitor.system_service_name || "").trim(); + const processList = await getPM2ProcessList(); + const entry = processList.find((item) => item.name === processName || item.id === processName); + + if (!entry) { + throw new Error(`PM2 process '${processName}' was not found.`); + } + + if (entry.status === "online") { + heartbeat.status = UP; + heartbeat.msg = `PM2 process '${processName}' is online.`; + return; + } + + throw new Error(`PM2 process '${processName}' is ${entry.status}.`); + } +} + +module.exports = { + PM2MonitorType, +}; diff --git a/server/monitor-types/postgres.js b/server/monitor-types/postgres.js new file mode 100644 index 0000000..fb6cc9b --- /dev/null +++ b/server/monitor-types/postgres.js @@ -0,0 +1,83 @@ +const { MonitorType } = require("./monitor-type"); +const { log, UP } = require("../../src/util"); +const dayjs = require("dayjs"); +const postgresConParse = require("pg-connection-string").parse; +const { Client } = require("pg"); + +class PostgresMonitorType extends MonitorType { + name = "postgres"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + let startTime = dayjs().valueOf(); + + let query = monitor.databaseQuery; + // No query provided by user, use SELECT 1 + if (!query || (typeof query === "string" && query.trim() === "")) { + query = "SELECT 1"; + } + await this.postgresQuery(monitor.databaseConnectionString, query); + + heartbeat.msg = ""; + heartbeat.status = UP; + heartbeat.ping = dayjs().valueOf() - startTime; + } + + /** + * Run a query on Postgres + * @param {string} connectionString The database connection string + * @param {string} query The query to validate the database with + * @returns {Promise<(string[] | object[] | object)>} Response from + * server + */ + async postgresQuery(connectionString, query) { + return new Promise((resolve, reject) => { + const config = postgresConParse(connectionString); + + // Fix #3868, which true/false is not parsed to boolean + if (typeof config.ssl === "string") { + config.ssl = config.ssl === "true"; + } + + if (config.password === "") { + // See https://github.com/brianc/node-postgres/issues/1927 + reject(new Error("Password is undefined.")); + return; + } + const client = new Client(config); + + client.on("error", (error) => { + log.debug(this.name, "Error caught in the error event handler."); + reject(error); + }); + + client.connect((err) => { + if (err) { + reject(err); + client.end(); + } else { + // Connected here + try { + client.query(query, (err, res) => { + if (err) { + reject(err); + } else { + resolve(res); + } + client.end(); + }); + } catch (e) { + reject(e); + client.end(); + } + } + }); + }); + } +} + +module.exports = { + PostgresMonitorType, +}; diff --git a/server/monitor-types/rabbitmq.js b/server/monitor-types/rabbitmq.js new file mode 100644 index 0000000..bd07e99 --- /dev/null +++ b/server/monitor-types/rabbitmq.js @@ -0,0 +1,114 @@ +const { MonitorType } = require("./monitor-type"); +const { log, UP } = require("../../src/util"); +const { axiosAbortSignal } = require("../util-server"); +const axios = require("axios"); + +class RabbitMqMonitorType extends MonitorType { + name = "rabbitmq"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, server) { + let baseUrls = []; + try { + baseUrls = JSON.parse(monitor.rabbitmqNodes); + } catch (error) { + throw new Error("Invalid RabbitMQ Nodes"); + } + + if (baseUrls.length === 0) { + throw new Error("No RabbitMQ nodes configured"); + } + + const errors = []; + + for (let i = 0; i < baseUrls.length; i++) { + const baseUrl = baseUrls[i]; + const nodeIndex = i + 1; + + try { + await this.checkSingleNode(monitor, baseUrl, `${nodeIndex}/${baseUrls.length}`); + // If checkSingleNode succeeds (doesn't throw), set heartbeat to UP + heartbeat.status = UP; + heartbeat.msg = + baseUrls.length === 1 + ? "Node is reachable and there are no alerts in the cluster" + : `One of the ${baseUrls.length} nodes is reachable and there are no alerts in the cluster`; + return; + } catch (error) { + log.warn(this.name, `Node ${nodeIndex}: ${error.message}`); + errors.push(`Node ${nodeIndex}: ${error.message}`); + } + } + + // If we reach here, all nodes failed + throw new Error(`All ${errors.length} nodes failed because ${errors.join("; ")}`); + } + + /** + * Check a single RabbitMQ node + * @param {object} monitor Monitor configuration + * @param {string} baseUrl Base URL of the RabbitMQ node + * @param {string} nodeInfo Node index info for logging (e.g., "1/3") + * @returns {Promise} + * @throws {Error} If the node check fails + */ + async checkSingleNode(monitor, baseUrl, nodeInfo) { + // Without a trailing slash, path in baseUrl will be removed. https://example.com/api -> https://example.com + let normalizedUrl = baseUrl; + if (!normalizedUrl.endsWith("/")) { + normalizedUrl += "/"; + } + + const options = { + // Do not start with slash, it will strip the trailing slash from baseUrl + url: new URL("api/health/checks/alarms/", normalizedUrl).href, + method: "get", + timeout: monitor.timeout * 1000, + headers: { + Accept: "application/json", + Authorization: + "Basic " + + Buffer.from(`${monitor.rabbitmqUsername || ""}:${monitor.rabbitmqPassword || ""}`).toString( + "base64" + ), + }, + signal: axiosAbortSignal((monitor.timeout + 10) * 1000), + // Capture reason for 503 status + validateStatus: (status) => status === 200 || status === 503, + }; + + log.debug("monitor", `[${monitor.name}] Checking node ${nodeInfo}: ${baseUrl}`); + + try { + const res = await axios.request(options); + log.debug( + "monitor", + `[${monitor.name}] Axios Response: status=${res.status} body=${JSON.stringify(res.data)}` + ); + + if (res.status === 200) { + log.debug("monitor", `[${monitor.name}] Node ${nodeInfo} is healthy`); + // Success - return without error + } else if (res.status === 503) { + throw new Error(res.data.reason); + } else { + throw new Error(`${res.status} - ${res.statusText}`); + } + } catch (error) { + if (axios.isCancel(error)) { + throw new Error("Request timed out"); + } else if (error.response) { + // Re-throw with the original error message if it's already formatted + throw error; + } else { + throw new Error(error.message); + } + } + } +} + +module.exports = { + RabbitMqMonitorType, +}; diff --git a/server/monitor-types/real-browser-monitor-type.js b/server/monitor-types/real-browser-monitor-type.js new file mode 100644 index 0000000..45e495e --- /dev/null +++ b/server/monitor-types/real-browser-monitor-type.js @@ -0,0 +1,300 @@ +const { MonitorType } = require("./monitor-type"); +const { chromium } = require("playwright-core"); +const { UP, log } = require("../../src/util"); +const { Settings } = require("../settings"); +const childProcess = require("child_process"); +const path = require("path"); +const Database = require("../database"); +const config = require("../config"); +const { RemoteBrowser } = require("../remote-browser"); +const { commandExists } = require("../util-server"); + +/** + * Cached instance of a browser + * @type {import ("playwright-core").Browser} + */ +let browser = null; + +let allowedList = []; +let lastAutoDetectChromeExecutable = null; + +if (process.platform === "win32") { + allowedList.push(process.env.LOCALAPPDATA + "\\Google\\Chrome\\Application\\chrome.exe"); + allowedList.push(process.env.PROGRAMFILES + "\\Google\\Chrome\\Application\\chrome.exe"); + allowedList.push(process.env["ProgramFiles(x86)"] + "\\Google\\Chrome\\Application\\chrome.exe"); + + // Allow Chromium too + allowedList.push(process.env.LOCALAPPDATA + "\\Chromium\\Application\\chrome.exe"); + allowedList.push(process.env.PROGRAMFILES + "\\Chromium\\Application\\chrome.exe"); + allowedList.push(process.env["ProgramFiles(x86)"] + "\\Chromium\\Application\\chrome.exe"); + + // Allow MS Edge + allowedList.push(process.env["ProgramFiles(x86)"] + "\\Microsoft\\Edge\\Application\\msedge.exe"); + + // For Loop A to Z + for (let i = 65; i <= 90; i++) { + let drive = String.fromCharCode(i); + allowedList.push(drive + ":\\Program Files\\Google\\Chrome\\Application\\chrome.exe"); + allowedList.push(drive + ":\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"); + } +} else if (process.platform === "linux") { + allowedList = [ + "chromium", + "chromium-browser", + "google-chrome", + + "/usr/bin/chromium", + "/usr/bin/chromium-browser", + "/usr/bin/google-chrome", + "/snap/bin/chromium", // Ubuntu + ]; +} else if (process.platform === "darwin") { + allowedList = [ + "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", + "/Applications/Chromium.app/Contents/MacOS/Chromium", + ]; +} + +/** + * Is the executable path allowed? + * @param {string} executablePath Path to executable + * @returns {Promise} The executable is allowed? + */ +async function isAllowedChromeExecutable(executablePath) { + if (config.args["allow-all-chrome-exec"] || process.env.UPTIME_KUMA_ALLOW_ALL_CHROME_EXEC === "1") { + return true; + } + + // Check if the executablePath is in the list of allowed executables + return allowedList.includes(executablePath); +} + +/** + * Get the current instance of the browser. If there isn't one, create + * it. + * @returns {Promise} The browser + */ +async function getBrowser() { + if (browser && browser.isConnected()) { + return browser; + } else { + let executablePath = await Settings.get("chromeExecutable"); + + executablePath = await prepareChromeExecutable(executablePath); + + browser = await chromium.launch({ + //headless: false, + executablePath, + }); + + return browser; + } +} + +/** + * Get the current instance of the browser. If there isn't one, create it + * @param {integer} remoteBrowserID Path to executable + * @returns {Promise} The browser + */ +async function getRemoteBrowser(remoteBrowserID) { + let remoteBrowser = await RemoteBrowser.get(remoteBrowserID); + log.debug("chromium", `Using remote browser: ${remoteBrowser.name} (${remoteBrowser.id})`); + browser = await chromium.connect(remoteBrowser.url); + return browser; +} + +/** + * Prepare the chrome executable path + * @param {string} executablePath Path to chrome executable + * @returns {Promise} Executable path + */ +async function prepareChromeExecutable(executablePath) { + // Special code for using the playwright_chromium + if (typeof executablePath === "string" && executablePath.toLocaleLowerCase() === "#playwright_chromium") { + // Set to undefined = use playwright_chromium + executablePath = undefined; + } else if (!executablePath) { + if (process.env.UPTIME_KUMA_IS_CONTAINER) { + executablePath = "/usr/bin/chromium"; + await installChromiumViaApt(executablePath); + } else { + executablePath = await findChrome(allowedList); + } + } else { + // User specified a path + // Check if the executablePath is in the list of allowed + if (!(await isAllowedChromeExecutable(executablePath))) { + throw new Error( + "This Chromium executable path is not allowed by default. If you are sure this is safe, please add an environment variable UPTIME_KUMA_ALLOW_ALL_CHROME_EXEC=1 to allow it." + ); + } + } + return executablePath; +} + +/** + * Installs Chromium and required font packages via APT if the Chromium executable + * is not already available. + * @async + * @param {string} executablePath - Path to the Chromium executable used to check + * whether Chromium is available and to query its version after installation. + * @returns {Promise} Resolves when Chromium is successfully installed or + * when no installation is required. + * @throws {Error} If the APT installation fails or exits with an unexpected + * exit code. + */ +async function installChromiumViaApt(executablePath) { + if (await commandExists(executablePath)) { + return; + } + await new Promise((resolve, reject) => { + log.info("chromium", "Installing Chromium..."); + let child = childProcess.exec( + "apt update && apt --yes --no-install-recommends install chromium fonts-indic fonts-noto fonts-noto-cjk" + ); + + // On exit + child.on("exit", (code) => { + log.info("chromium", "apt install chromium exited with code " + code); + + if (code === 0) { + log.info("chromium", "Installed Chromium"); + let version = childProcess.execSync(executablePath + " --version").toString("utf8"); + log.info("chromium", "Chromium version: " + version); + resolve(); + } else if (code === 100) { + reject(new Error("Installing Chromium, please wait...")); + } else { + reject(new Error("apt install chromium failed with code " + code)); + } + }); + }); +} + +/** + * Find the chrome executable + * @param {string[]} executables Executables to search through + * @returns {Promise} Executable + * @throws {Error} Could not find executable + */ +async function findChrome(executables) { + // Use the last working executable, so we don't have to search for it again + if (lastAutoDetectChromeExecutable) { + if (await commandExists(lastAutoDetectChromeExecutable)) { + return lastAutoDetectChromeExecutable; + } + } + + for (let executable of executables) { + if (await commandExists(executable)) { + lastAutoDetectChromeExecutable = executable; + return executable; + } + } + throw new Error("Chromium not found, please specify Chromium executable path in the settings page."); +} + +/** + * Reset chrome + * @returns {Promise} + */ +async function resetChrome() { + if (browser) { + await browser.close(); + browser = null; + } +} + +/** + * Test if the chrome executable is valid and return the version + * @param {string} executablePath Path to executable + * @returns {Promise} Chrome version + */ +async function testChrome(executablePath) { + try { + executablePath = await prepareChromeExecutable(executablePath); + + log.info("chromium", "Testing Chromium executable: " + executablePath); + + const browser = await chromium.launch({ + executablePath, + }); + const version = browser.version(); + await browser.close(); + return version; + } catch (e) { + throw new Error(e.message); + } +} +// test remote browser +/** + * @param {string} remoteBrowserURL Remote Browser URL + * @returns {Promise} Returns if connection worked + */ +async function testRemoteBrowser(remoteBrowserURL) { + try { + const browser = await chromium.connect(remoteBrowserURL); + browser.version(); + await browser.close(); + return true; + } catch (e) { + throw new Error(e.message); + } +} +class RealBrowserMonitorType extends MonitorType { + name = "real-browser"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, server) { + const browser = monitor.remote_browser ? await getRemoteBrowser(monitor.remote_browser) : await getBrowser(); + const context = await browser.newContext(); + try { + const page = await context.newPage(); + + // Prevent Local File Inclusion + // Accept only http:// and https:// + // https://github.com/louislam/uptime-kuma/security/advisories/GHSA-2qgm-m29m-cj2h + let url = new URL(monitor.url); + if (url.protocol !== "http:" && url.protocol !== "https:") { + throw new Error("Invalid url protocol, only http and https are allowed."); + } + + const res = await page.goto(monitor.url, { + waitUntil: "networkidle", + timeout: monitor.interval * 1000 * 0.8, + }); + + // Wait for additional time before taking screenshot if configured + if (monitor.screenshot_delay > 0) { + await page.waitForTimeout(monitor.screenshot_delay); + } + + let filename = monitor.id + ".png"; + + await page.screenshot({ + path: path.join(Database.screenshotDir, filename), + }); + + if (res.status() >= 200 && res.status() < 400) { + heartbeat.status = UP; + heartbeat.msg = res.status(); + + const timing = res.request().timing(); + heartbeat.ping = timing.responseEnd; + } else { + throw new Error(res.status() + ""); + } + } finally { + await context.close(); + } + } +} + +module.exports = { + RealBrowserMonitorType, + testChrome, + resetChrome, + testRemoteBrowser, +}; diff --git a/server/monitor-types/redis.js b/server/monitor-types/redis.js new file mode 100644 index 0000000..b83de31 --- /dev/null +++ b/server/monitor-types/redis.js @@ -0,0 +1,60 @@ +const { MonitorType } = require("./monitor-type"); +const { UP } = require("../../src/util"); +const redis = require("redis"); + +class RedisMonitorType extends MonitorType { + name = "redis"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + heartbeat.msg = await this.redisPingAsync(monitor.databaseConnectionString, !monitor.ignoreTls); + heartbeat.status = UP; + } + + /** + * Redis server ping + * @param {string} dsn The redis connection string + * @param {boolean} rejectUnauthorized If false, allows unverified server certificates. + * @returns {Promise} Response from redis server + */ + redisPingAsync(dsn, rejectUnauthorized) { + return new Promise((resolve, reject) => { + const client = redis.createClient({ + url: dsn, + socket: { + rejectUnauthorized, + }, + }); + client.on("error", (err) => { + if (client.isOpen) { + client.disconnect(); + } + reject(err); + }); + client.connect().then(() => { + if (!client.isOpen) { + client.emit("error", new Error("connection isn't open")); + } + client + .ping() + .then((res, err) => { + if (client.isOpen) { + client.disconnect(); + } + if (err) { + reject(err); + } else { + resolve(res); + } + }) + .catch((error) => reject(error)); + }); + }); + } +} + +module.exports = { + RedisMonitorType, +}; diff --git a/server/monitor-types/sftp.js b/server/monitor-types/sftp.js new file mode 100644 index 0000000..a61b130 --- /dev/null +++ b/server/monitor-types/sftp.js @@ -0,0 +1,113 @@ +const { MonitorType } = require("./monitor-type"); +const { UP, log } = require("../../src/util"); +const dayjs = require("dayjs"); +const SftpClient = require("ssh2-sftp-client"); + +/** + * Converts a raw ssh2-sftp-client error into a clean, human-readable message. + * The library prefixes every error with the internal method name (e.g. "getConnection: "), + * and sometimes produces an empty remainder when the underlying socket closes silently. + * @param {Error} err The raw error thrown by sftp.connect() + * @param {string} host Hostname that was being connected to + * @param {number|string} port Port that was being connected to + * @returns {string} Human-readable error message + */ +function formatSftpError(err, host, port) { + const code = err.code || ""; + + // Strip the "getConnection: " / "sftp: " prefix the library adds to every message + const rawMsg = (err.message || "").replace(/^[\w-]+\s*:\s*/, "").trim(); + + switch (code) { + case "ECONNREFUSED": + return `Connection refused — ${host}:${port} actively rejected the connection`; + case "ENOTFOUND": + return `Host not found — cannot resolve hostname "${host}"`; + case "ECONNRESET": + return `Connection reset by remote host ${host}:${port}`; + case "ENETUNREACH": + case "EHOSTUNREACH": + return `Network unreachable — cannot reach ${host}:${port}`; + case "ETIMEDOUT": + return `Connection timed out — ${host}:${port} did not respond in time`; + default: + // If the remainder is empty the socket closed without any detail (server offline) + if (!rawMsg) { + return `Connection failed — ${host}:${port} is unreachable or offline`; + } + return rawMsg; + } +} + +class SFTPMonitorType extends MonitorType { + name = "sftp"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + const sftp = new SftpClient(); + const host = monitor.hostname; + const port = monitor.port || 22; + + const timeoutSeconds = typeof monitor.timeout === "number" && monitor.timeout > 0 ? monitor.timeout : 10; + const timeoutMs = timeoutSeconds * 1000; + + const connectOptions = { + host, + port, + username: monitor.sshUsername, + readyTimeout: timeoutMs, + timeout: timeoutMs, + }; + + if (monitor.sshAuthMethod === "privateKey") { + if (!monitor.sshPrivateKey) { + throw new Error("SSH private key is required for key-based authentication"); + } + connectOptions.privateKey = monitor.sshPrivateKey; + if (monitor.sshPassphrase) { + connectOptions.passphrase = monitor.sshPassphrase; + } + } else { + connectOptions.password = monitor.sshPassword; + } + + let connected = false; + + try { + const startTime = dayjs().valueOf(); + + await sftp.connect(connectOptions); + connected = true; + log.debug("sftp", `Connected to SFTP server: ${host}:${port}`); + + if (monitor.sftpPath) { + const exists = await sftp.exists(monitor.sftpPath); + if (!exists) { + throw new Error(`Path "${monitor.sftpPath}" does not exist on the SFTP server`); + } + log.debug("sftp", `Path "${monitor.sftpPath}" exists on SFTP server`); + } + + heartbeat.ping = dayjs().valueOf() - startTime; + heartbeat.status = UP; + heartbeat.msg = "SFTP connection successful"; + } catch (err) { + throw new Error(formatSftpError(err, host, port)); + } finally { + if (connected) { + try { + await sftp.end(); + } catch (teardownErr) { + // cleanup errors are ignored, only log them for debugging purposes + log.debug("sftp", `Error during SFTP teardown: ${teardownErr.message}`); + } + } + } + } +} + +module.exports = { + SFTPMonitorType, +}; diff --git a/server/monitor-types/sip-options.js b/server/monitor-types/sip-options.js new file mode 100644 index 0000000..c42af0d --- /dev/null +++ b/server/monitor-types/sip-options.js @@ -0,0 +1,66 @@ +const { MonitorType } = require("./monitor-type"); +const { UP } = require("../../src/util"); +const { execFile } = require("promisify-child-process"); + +class SIPMonitorType extends MonitorType { + name = "sip-options"; + supportsConditions = false; + + /** + * Run the monitoring check on the given monitor + * @param {Monitor} monitor Monitor to check + * @param {Heartbeat} heartbeat Monitor heartbeat to update + * @param {UptimeKumaServer} _server Uptime Kuma server + * @returns {Promise} + * @throws Will throw an error if the command execution encounters any error. + */ + async check(monitor, heartbeat, _server) { + let sipsakOutput = await this.runSipSak(monitor.hostname, monitor.port, 3000); + this.parseSipsakResponse(sipsakOutput, heartbeat); + } + + /** + * Runs Sipsak options ping + * @param {string} hostname SIP server address to send options. + * @param {number} port SIP server port + * @param {number} timeout timeout of options reply + * @returns {Promise} A Promise that resolves to the output of the Sipsak options ping + * @throws Will throw an error if the command execution encounters any error. + */ + async runSipSak(hostname, port, timeout) { + const { stdout, stderr } = await execFile( + "sipsak", + ["-s", `sip:${hostname}:${port}`, "--from", `sip:sipsak@${hostname}`, "-v"], + { timeout } + ); + + if (!stdout && stderr && stderr.toString()) { + throw new Error(`Error in output: ${stderr.toString()}`); + } + if (stdout && stdout.toString()) { + return stdout.toString(); + } else { + throw new Error("No output from sipsak"); + } + } + + /** + * @param {string} res response to be parsed + * @param {object} heartbeat heartbeat object to update + * @returns {void} returns nothing + */ + parseSipsakResponse(res, heartbeat) { + let lines = res.split("\n"); + for (let line of lines) { + if (line.includes("200 OK")) { + heartbeat.status = UP; + heartbeat.msg = line; + break; + } + } + } +} + +module.exports = { + SIPMonitorType, +}; diff --git a/server/monitor-types/smtp.js b/server/monitor-types/smtp.js new file mode 100644 index 0000000..f4a49e2 --- /dev/null +++ b/server/monitor-types/smtp.js @@ -0,0 +1,35 @@ +const { MonitorType } = require("./monitor-type"); +const { UP } = require("../../src/util"); +const nodemailer = require("nodemailer"); + +class SMTPMonitorType extends MonitorType { + name = "smtp"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + let options = { + port: monitor.port || 25, + host: monitor.hostname, + secure: monitor.smtpSecurity === "secure", // use SMTPS (not STARTTLS) + ignoreTLS: monitor.smtpSecurity === "nostarttls", // don't use STARTTLS even if it's available + requireTLS: monitor.smtpSecurity === "starttls", // use STARTTLS or fail + }; + let transporter = nodemailer.createTransport(options); + try { + await transporter.verify(); + + heartbeat.status = UP; + heartbeat.msg = "SMTP connection verifies successfully"; + } catch (e) { + throw new Error(`SMTP connection doesn't verify: ${e}`); + } finally { + transporter.close(); + } + } +} + +module.exports = { + SMTPMonitorType, +}; diff --git a/server/monitor-types/snmp.js b/server/monitor-types/snmp.js new file mode 100644 index 0000000..719ec5f --- /dev/null +++ b/server/monitor-types/snmp.js @@ -0,0 +1,88 @@ +const { MonitorType } = require("./monitor-type"); +const { UP, log, evaluateJsonQuery } = require("../../src/util"); +const snmp = require("net-snmp"); + +class SNMPMonitorType extends MonitorType { + name = "snmp"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + let session; + try { + const sessionOptions = { + port: monitor.port || "161", + retries: monitor.maxretries, + timeout: monitor.timeout * 1000, + version: snmp.Version[monitor.snmpVersion], + }; + + if (monitor.snmpVersion === "3") { + if (!monitor.snmp_v3_username) { + throw new Error("SNMPv3 username is required"); + } + // SNMPv3 currently defaults to noAuthNoPriv. + // Supporting authNoPriv / authPriv requires additional inputs + // (auth/priv protocols, passwords), validation, secure storage, + // and database migrations, which is intentionally left for + // a follow-up PR to keep this change scoped. + sessionOptions.securityLevel = snmp.SecurityLevel.noAuthNoPriv; + sessionOptions.username = monitor.snmp_v3_username; + session = snmp.createV3Session(monitor.hostname, monitor.snmp_v3_username, sessionOptions); + } else { + session = snmp.createSession(monitor.hostname, monitor.radiusPassword, sessionOptions); + } + + // Handle errors during session creation + session.on("error", (error) => { + throw new Error(`Error creating SNMP session: ${error.message}`); + }); + + const varbinds = await new Promise((resolve, reject) => { + session.get([monitor.snmpOid], (error, varbinds) => { + error ? reject(error) : resolve(varbinds); + }); + }); + log.debug( + this.name, + `SNMP: Received varbinds (Type: ${snmp.ObjectType[varbinds[0].type]} Value: ${varbinds[0].value})` + ); + + if (varbinds.length === 0) { + throw new Error(`No varbinds returned from SNMP session (OID: ${monitor.snmpOid})`); + } + + if (varbinds[0].type === snmp.ObjectType.NoSuchInstance) { + throw new Error(`The SNMP query returned that no instance exists for OID ${monitor.snmpOid}`); + } + + // We restrict querying to one OID per monitor, therefore `varbinds[0]` will always contain the value we're interested in. + const value = varbinds[0].value; + + const { status, response } = await evaluateJsonQuery( + value, + monitor.jsonPath, + monitor.jsonPathOperator, + monitor.expectedValue + ); + + if (status) { + heartbeat.status = UP; + heartbeat.msg = `JSON query passes (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})`; + } else { + throw new Error( + `JSON query does not pass (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})` + ); + } + } finally { + if (session) { + session.close(); + } + } + } +} + +module.exports = { + SNMPMonitorType, +}; diff --git a/server/monitor-types/steam.js b/server/monitor-types/steam.js new file mode 100644 index 0000000..ec797ea --- /dev/null +++ b/server/monitor-types/steam.js @@ -0,0 +1,134 @@ +const { MonitorType } = require("./monitor-type"); +const { + UP, + PING_COUNT_DEFAULT, + PING_GLOBAL_TIMEOUT_DEFAULT, + PING_PER_REQUEST_TIMEOUT_DEFAULT, +} = require("../../src/util"); +const { Settings } = require("../settings"); +const { ping, checkStatusCode } = require("../util-server"); +const axios = require("axios"); +const crypto = require("crypto"); +const dns = require("node:dns/promises"); +const http = require("http"); +const https = require("https"); +const net = require("node:net"); + +class SteamMonitorType extends MonitorType { + name = "steam"; + + /** + * Creates a Steam monitor type. + * @param {object} options Optional dependencies for tests. + * @param {object} options.steamApiClient Axios-compatible Steam API client. + * @param {Function} options.lookup DNS lookup function. + * @param {Function} options.getSteamAPIKey Steam API key provider. + * @param {Function} options.ping Steam server ping function. + */ + constructor(options = {}) { + super(); + + this.steamApiClient = options.steamApiClient || axios; + this.lookup = options.lookup || dns.lookup; + this.getSteamAPIKey = options.getSteamAPIKey || (() => Settings.get("steamAPIKey")); + this.ping = options.ping || ping; + } + + /** + * @inheritdoc + */ + async check(monitor, heartbeat) { + const steamApiUrl = "https://api.steampowered.com/IGameServersService/GetServerList/v1/"; + const steamAPIKey = await this.getSteamAPIKey(); + + if (!steamAPIKey) { + throw new Error("Steam API Key not found"); + } + + const filter = await this.buildServerFilter(monitor.hostname, monitor.port); + + let res = await this.steamApiClient.get(steamApiUrl, { + timeout: monitor.timeout * 1000, + headers: { + Accept: "*/*", + }, + httpsAgent: new https.Agent({ + maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) + rejectUnauthorized: !monitor.getIgnoreTls(), + secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT, + }), + httpAgent: new http.Agent({ + maxCachedSessions: 0, + }), + maxRedirects: monitor.maxredirects, + validateStatus: (status) => { + return checkStatusCode(status, monitor.getAcceptedStatuscodes()); + }, + params: { + filter: filter, + key: steamAPIKey, + }, + }); + + if (res.data.response && res.data.response.servers && res.data.response.servers.length > 0) { + heartbeat.status = UP; + heartbeat.msg = res.data.response.servers[0].name; + + try { + heartbeat.ping = await this.ping( + monitor.hostname, + PING_COUNT_DEFAULT, + "", + true, + monitor.packetSize, + PING_GLOBAL_TIMEOUT_DEFAULT, + PING_PER_REQUEST_TIMEOUT_DEFAULT + ); + } catch (_) {} + } else { + throw new Error("Server not found on Steam"); + } + } + + /** + * Builds the Steam API server filter. + * @param {string} hostname Steam server hostname or IP address. + * @param {number} port Steam server port. + * @returns {Promise} Steam API addr filter. + */ + async buildServerFilter(hostname, port) { + const resolvedHostname = await this.resolveSteamHostname(hostname); + return `addr\\${resolvedHostname}:${port}`; + } + + /** + * Resolves hostnames before passing them to Steam's addr filter. + * @param {string} hostname Steam server hostname or IP address. + * @returns {Promise} IP address accepted by the Steam API. + * @throws {Error} When the hostname cannot be resolved. + */ + async resolveSteamHostname(hostname) { + if (net.isIP(hostname)) { + return hostname; + } + + try { + const lookupResult = await this.lookup(hostname, { all: true }); + const addresses = Array.isArray(lookupResult) ? lookupResult : [lookupResult]; + const ipv4Address = addresses.find(({ address }) => net.isIP(address) === 4); + const resolvedAddress = ipv4Address?.address || addresses[0]?.address; + + if (!resolvedAddress) { + throw new Error("DNS lookup returned no addresses"); + } + + return resolvedAddress; + } catch (error) { + throw new Error(`Unable to resolve Steam server hostname "${hostname}": ${error.message}`); + } + } +} + +module.exports = { + SteamMonitorType, +}; diff --git a/server/monitor-types/system-service.js b/server/monitor-types/system-service.js new file mode 100644 index 0000000..0b9aff0 --- /dev/null +++ b/server/monitor-types/system-service.js @@ -0,0 +1,113 @@ +const { MonitorType } = require("./monitor-type"); +const { execFile } = require("child_process"); +const process = require("process"); +const { UP } = require("../../src/util"); + +class SystemServiceMonitorType extends MonitorType { + name = "system-service"; + description = "Checks if a system service is running (systemd on Linux, Service Manager on Windows)."; + + /** + * Check the system service status. + * Detects OS and dispatches to the appropriate check method. + * @param {object} monitor The monitor object containing monitor.system_service_name. + * @param {object} heartbeat The heartbeat object to update. + * @returns {Promise} Resolves when check is complete. + */ + async check(monitor, heartbeat) { + const serviceName = (monitor.system_service_name || "").trim(); + + if (!serviceName) { + throw new Error("Service Name is required."); + } + + if (process.platform === "win32") { + return this.checkWindows(serviceName, heartbeat); + } else if (process.platform === "linux") { + return this.checkLinux(serviceName, heartbeat); + } + + throw new Error(`System Service monitoring is not supported on ${process.platform}`); + } + + /** + * Linux Check (Systemd) + * @param {string} serviceName The name of the service to check. + * @param {object} heartbeat The heartbeat object. + * @returns {Promise} + */ + async checkLinux(serviceName, heartbeat) { + return new Promise((resolve, reject) => { + // SECURITY: Prevent Argument Injection + // Only allow alphanumeric, dots, dashes, underscores, and @ + if (!serviceName || !/^[a-zA-Z0-9._\-@]+$/.test(serviceName)) { + reject(new Error("Invalid service name. Please use the internal Service Name (no spaces).")); + return; + } + + execFile("systemctl", ["is-active", serviceName], { timeout: 5000 }, (error, stdout, stderr) => { + // Combine output and truncate to ~200 chars to prevent DB bloat + let output = (stderr || stdout || "").toString().trim(); + if (output.length > 200) { + output = output.substring(0, 200) + "..."; + } + + if (error) { + reject(new Error(output || `Service '${serviceName}' is not running.`)); + return; + } + + heartbeat.status = UP; + heartbeat.msg = `Service '${serviceName}' is running.`; + resolve(); + }); + }); + } + + /** + * Windows Check (PowerShell) + * @param {string} serviceName The name of the service to check. + * @param {object} heartbeat The heartbeat object. + * @returns {Promise} Resolves on success, rejects on error. + */ + async checkWindows(serviceName, heartbeat) { + return new Promise((resolve, reject) => { + // SECURITY: Validate service name to reduce command-injection risk + if (!/^[A-Za-z0-9._-]+$/.test(serviceName)) { + throw new Error("Invalid service name. Only alphanumeric characters and '.', '_', '-' are allowed."); + } + + const cmd = "powershell"; + const args = [ + "-NoProfile", + "-NonInteractive", + "-Command", + `(Get-Service -Name '${serviceName.replaceAll("'", "''")}').Status`, + ]; + + execFile(cmd, args, { timeout: 5000 }, (error, stdout, stderr) => { + let output = (stderr || stdout || "").toString().trim(); + if (output.length > 200) { + output = output.substring(0, 200) + "..."; + } + + if (error || stderr) { + reject(new Error(`Service '${serviceName}' is not running/found.`)); + return; + } + + if (output === "Running") { + heartbeat.status = UP; + heartbeat.msg = `Service '${serviceName}' is running.`; + resolve(); + } else { + reject(new Error(`Service '${serviceName}' is ${output}.`)); + } + }); + }); + } +} + +module.exports = { + SystemServiceMonitorType, +}; diff --git a/server/monitor-types/tailscale-ping.js b/server/monitor-types/tailscale-ping.js new file mode 100644 index 0000000..c18593b --- /dev/null +++ b/server/monitor-types/tailscale-ping.js @@ -0,0 +1,77 @@ +const { MonitorType } = require("./monitor-type"); +const { UP } = require("../../src/util"); +const childProcessAsync = require("promisify-child-process"); + +class TailscalePing extends MonitorType { + name = "tailscale-ping"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + try { + let tailscaleOutput = await this.runTailscalePing(monitor.hostname, monitor.interval); + this.parseTailscaleOutput(tailscaleOutput, heartbeat); + } catch (err) { + // trigger log function somewhere to display a notification or alert to the user (but how?) + throw new Error(`Error checking Tailscale ping: ${err}`); + } + } + + /** + * Runs the Tailscale ping command to the given URL. + * @param {string} hostname The hostname to ping. + * @param {number} interval Interval to send ping + * @returns {Promise} A Promise that resolves to the output of the Tailscale ping command + * @throws Will throw an error if the command execution encounters any error. + */ + async runTailscalePing(hostname, interval) { + let timeout = interval * 1000 * 0.8; + let res = await childProcessAsync.spawn("tailscale", ["ping", "--c", "1", hostname], { + timeout: timeout, + encoding: "utf8", + }); + if (res.stderr && res.stderr.toString() && res.code !== 0) { + throw new Error(`Error in output: ${res.stderr.toString()}`); + } + if (res.stdout && res.stdout.toString()) { + return res.stdout.toString(); + } else { + throw new Error("No output from Tailscale ping"); + } + } + + /** + * Parses the output of the Tailscale ping command to update the heartbeat. + * @param {string} tailscaleOutput The output of the Tailscale ping command. + * @param {object} heartbeat The heartbeat object to update. + * @returns {void} + * @throws Will throw an eror if the output contains any unexpected string. + */ + parseTailscaleOutput(tailscaleOutput, heartbeat) { + let lines = tailscaleOutput.split("\n"); + + for (let line of lines) { + if (line.includes("pong from")) { + heartbeat.status = UP; + let time = line.split(" in ")[1].split(" ")[0]; + heartbeat.ping = parseInt(time); + heartbeat.msg = "OK"; + break; + } else if (line.includes("timed out")) { + throw new Error(`Ping timed out: "${line}"`); + // Immediately throws upon "timed out" message, the server is expected to re-call the check function + } else if (line.includes("no matching peer")) { + throw new Error(`Nonexistant or inaccessible due to ACLs: "${line}"`); + } else if (line.includes("is local Tailscale IP")) { + throw new Error(`Tailscale only works if used on other machines: "${line}"`); + } else if (line !== "") { + throw new Error(`Unexpected output: "${line}"`); + } + } + } +} + +module.exports = { + TailscalePing, +}; diff --git a/server/monitor-types/tcp.js b/server/monitor-types/tcp.js new file mode 100644 index 0000000..bf05ff0 --- /dev/null +++ b/server/monitor-types/tcp.js @@ -0,0 +1,416 @@ +const { MonitorType } = require("./monitor-type"); +const { UP, PING_GLOBAL_TIMEOUT_DEFAULT: TIMEOUT, log } = require("../../src/util"); +const { checkCertificate } = require("../util-server"); +const tls = require("tls"); +const net = require("net"); +const tcpp = require("tcp-ping"); + +/** + * TLS Alert codes as defined in RFC 5246 and RFC 8446 + * @see https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-6 + */ +const TLS_ALERT_CODES = { + 0: "close_notify", + 10: "unexpected_message", + 20: "bad_record_mac", + 21: "decryption_failed", + 22: "record_overflow", + 30: "decompression_failure", + 40: "handshake_failure", + 41: "no_certificate", + 42: "bad_certificate", + 43: "unsupported_certificate", + 44: "certificate_revoked", + 45: "certificate_expired", + 46: "certificate_unknown", + 47: "illegal_parameter", + 48: "unknown_ca", + 49: "access_denied", + 50: "decode_error", + 51: "decrypt_error", + 60: "export_restriction", + 70: "protocol_version", + 71: "insufficient_security", + 80: "internal_error", + 86: "inappropriate_fallback", + 90: "user_canceled", + 100: "no_renegotiation", + 109: "missing_extension", + 110: "unsupported_extension", + 111: "certificate_unobtainable", + 112: "unrecognized_name", + 113: "bad_certificate_status_response", + 114: "bad_certificate_hash_value", + 115: "unknown_psk_identity", + 116: "certificate_required", + 120: "no_application_protocol", +}; + +/** + * Parse TLS alert number from error message + * @param {string} errorMessage Error message from TLS connection + * @returns {number|null} TLS alert number or null if not found + */ +function parseTlsAlertNumber(errorMessage) { + const match = errorMessage.match(/alert number (\d+)/i); + if (match) { + return parseInt(match[1], 10); + } + return null; +} + +/** + * Get TLS alert name from alert number + * @param {number} alertNumber TLS alert number + * @returns {string} TLS alert name or "unknown_alert" + */ +function getTlsAlertName(alertNumber) { + return TLS_ALERT_CODES[alertNumber] || `unknown_alert_${alertNumber}`; +} + +/** + * Send TCP request to specified hostname and port + * @param {string} hostname Hostname / address of machine + * @param {number} port TCP port to test + * @returns {Promise} Maximum time in ms rounded to nearest integer + */ +const tcping = (hostname, port) => { + return new Promise((resolve, reject) => { + tcpp.ping( + { + address: hostname, + port: port, + attempts: 1, + }, + (err, data) => { + if (err) { + reject(err); + } + + if (data.results.length >= 1 && data.results[0].err) { + reject(data.results[0].err); + } + + resolve(Math.round(data.max)); + } + ); + }); +}; + +class TCPMonitorType extends MonitorType { + name = "port"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + const expectedTlsAlert = monitor.expected_tls_alert; + + // If expecting a TLS alert, use TLS connection with alert detection + if (expectedTlsAlert && expectedTlsAlert !== "none") { + await this.checkTlsAlert(monitor, heartbeat, expectedTlsAlert); + return; + } + + // Standard TCP check + await this.checkTcp(monitor, heartbeat); + } + + /** + * Standard TCP connectivity check + * @param {object} monitor Monitor object + * @param {object} heartbeat Heartbeat object + * @returns {Promise} + */ + async checkTcp(monitor, heartbeat) { + try { + const resp = await tcping(monitor.hostname, monitor.port); + heartbeat.ping = resp; + heartbeat.msg = `${resp} ms`; + heartbeat.status = UP; + } catch { + throw new Error("Connection failed"); + } + + let socket_; + + // Handle TLS certificate checking for secure/starttls connections + if (["secure", "starttls"].includes(monitor.smtpSecurity) && monitor.isEnabledExpiryNotification()) { + const reuseSocket = monitor.smtpSecurity === "starttls" ? await this.performStartTls(monitor) : {}; + socket_ = reuseSocket.socket; + await this.checkTlsCertificate(monitor, reuseSocket); + } + + if (socket_ && !socket_.destroyed) { + socket_.end(); + } + } + + /** + * Perform STARTTLS handshake for various protocols (SMTP, IMAP, XMPP) + * @param {object} monitor Monitor object + * @returns {Promise<{socket: net.Socket}>} Object containing the socket + */ + performStartTls(monitor) { + return new Promise((resolve, reject) => { + let dialogTimeout; + let bannerTimeout; + const socket_ = net.connect(monitor.port, monitor.hostname); + + const onTimeout = () => { + log.debug(this.name, `[${monitor.name}] Pre-TLS connection timed out`); + doReject("Connection timed out"); + }; + + const onBannerTimeout = () => { + log.debug(this.name, `[${monitor.name}] Pre-TLS timed out waiting for banner`); + // No banner. Could be a XMPP server? + socket_.write( + `` + ); + }; + + const doResolve = () => { + dialogTimeout && clearTimeout(dialogTimeout); + bannerTimeout && clearTimeout(bannerTimeout); + resolve({ socket: socket_ }); + }; + + const doReject = (error) => { + dialogTimeout && clearTimeout(dialogTimeout); + bannerTimeout && clearTimeout(bannerTimeout); + socket_.end(); + reject(error); + }; + + socket_.on("connect", () => { + log.debug(this.name, `[${monitor.name}] Pre-TLS connection: ${JSON.stringify(socket_)}`); + }); + + socket_.on("data", (data) => { + const response = data.toString(); + const response_ = response.toLowerCase(); + log.debug(this.name, `[${monitor.name}] Pre-TLS response: ${response}`); + clearTimeout(bannerTimeout); + switch (true) { + case response_.includes("start tls") || response_.includes("begin tls"): + doResolve(); + break; + case response.startsWith("* OK") || response.match(/CAPABILITY.+STARTTLS/): + socket_.write("a001 STARTTLS\r\n"); + break; + case response.startsWith("220") || response.includes("ESMTP"): + socket_.write(`EHLO ${monitor.hostname}\r\n`); + break; + case response.includes("250-STARTTLS"): + socket_.write("STARTTLS\r\n"); + break; + case response_.includes("'); + break; + case response_.includes(""): + break; + default: + doReject(`Unexpected response: ${response}`); + } + }); + socket_.on("error", (error) => { + log.debug(this.name, `[${monitor.name}] ${error.toString()}`); + reject(error); + }); + socket_.setTimeout(1000 * TIMEOUT, onTimeout); + dialogTimeout = setTimeout(onTimeout, 1000 * TIMEOUT); + bannerTimeout = setTimeout(onBannerTimeout, 1000 * 1.5); + }); + } + + /** + * Check TLS certificate validity + * @param {object} monitor Monitor object + * @param {object} reuseSocket Socket to reuse for STARTTLS + * @returns {Promise} + */ + async checkTlsCertificate(monitor, reuseSocket) { + let socket = null; + try { + const options = { + host: monitor.hostname, + port: monitor.port, + servername: monitor.hostname, + ...reuseSocket, + }; + + const tlsInfoObject = await new Promise((resolve, reject) => { + socket = tls.connect(options); + + socket.on("secureConnect", () => { + try { + const info = checkCertificate(socket); + resolve(info); + } catch (error) { + reject(error); + } + }); + + socket.on("error", (error) => { + reject(error); + }); + + socket.setTimeout(1000 * TIMEOUT, () => { + reject(new Error("Connection timed out")); + }); + }); + + await monitor.handleTlsInfo(tlsInfoObject); + if (!tlsInfoObject.valid) { + throw new Error("Certificate is invalid"); + } + } catch (error) { + const message = error instanceof Error ? error.message : "Unknown error"; + throw new Error(`TLS Connection failed: ${message}`); + } finally { + if (socket && !socket.destroyed) { + socket.destroy(); + } + } + } + + /** + * Check for expected TLS alert (for mTLS verification) + * @param {object} monitor Monitor object + * @param {object} heartbeat Heartbeat object + * @param {string} expectedTlsAlert Expected TLS alert name + * @returns {Promise} + */ + async checkTlsAlert(monitor, heartbeat, expectedTlsAlert) { + const timeout = monitor.timeout * 1000 || 30000; + const startTime = Date.now(); + + const options = { + host: monitor.hostname, + port: monitor.port || 443, + servername: monitor.hostname, + rejectUnauthorized: !monitor.getIgnoreTls(), + timeout: timeout, + }; + + // Add client certificate if provided (for mTLS testing with cert) + if (monitor.tlsCert && monitor.tlsKey) { + options.cert = monitor.tlsCert; + options.key = monitor.tlsKey; + if (monitor.tlsCa) { + options.ca = monitor.tlsCa; + } + } + + const result = await this.attemptTlsConnection(monitor, options, startTime, timeout); + + heartbeat.ping = result.responseTime; + + // Handle TLS info for certificate expiry monitoring + if (result.tlsInfo && monitor.isEnabledExpiryNotification()) { + await monitor.handleTlsInfo(result.tlsInfo); + } + + // Check if we got the expected alert + // Note: Error messages below could be translated, but alert names (e.g., certificate_required) + // are from RFC 8446 spec and should remain in English for consistency with the spec. + if (result.alertName === expectedTlsAlert) { + heartbeat.status = UP; + heartbeat.msg = `TLS alert received as expected: ${result.alertName} (${result.alertNumber})`; + } else if (result.success) { + throw new Error( + `Expected TLS alert '${expectedTlsAlert}' but connection succeeded. The server accepted the connection without requiring a client certificate.` + ); + } else if (result.alertNumber !== null) { + throw new Error( + `Expected TLS alert '${expectedTlsAlert}' but received '${result.alertName}' (${result.alertNumber})` + ); + } else { + throw new Error( + `Expected TLS alert '${expectedTlsAlert}' but got unexpected error: ${result.errorMessage}` + ); + } + } + + /** + * Attempt TLS connection and capture result/alert + * @param {object} monitor Monitor object + * @param {object} options TLS connection options + * @param {number} startTime Connection start timestamp + * @param {number} timeout Connection timeout in ms + * @returns {Promise} Connection result with success, responseTime, tlsInfo, alertNumber, alertName, errorMessage + */ + attemptTlsConnection(monitor, options, startTime, timeout) { + return new Promise((resolve, reject) => { + const socket = tls.connect(options); + + const timeoutId = setTimeout(() => { + socket.destroy(); + reject(new Error("TLS connection timed out")); + }, timeout); + + socket.on("secureConnect", () => { + clearTimeout(timeoutId); + const responseTime = Date.now() - startTime; + + let tlsInfo = null; + if (monitor.isEnabledExpiryNotification()) { + try { + tlsInfo = checkCertificate(socket); + } catch (e) { + log.debug(this.name, `[${monitor.name}] Error checking certificate: ${e.message}`); + } + } + + socket.end(); + resolve({ + success: true, + responseTime, + tlsInfo, + alertNumber: null, + alertName: null, + }); + }); + + socket.on("error", (error) => { + clearTimeout(timeoutId); + const responseTime = Date.now() - startTime; + const errorMessage = error.message || error.toString(); + + const alertNumber = parseTlsAlertNumber(errorMessage); + const alertName = alertNumber !== null ? getTlsAlertName(alertNumber) : null; + + log.debug( + this.name, + `[${monitor.name}] TLS error: ${errorMessage}, alert: ${alertNumber} (${alertName})` + ); + + resolve({ + success: false, + responseTime, + tlsInfo: null, + alertNumber, + alertName, + errorMessage, + }); + }); + + socket.on("timeout", () => { + clearTimeout(timeoutId); + socket.destroy(); + reject(new Error("TLS connection timed out")); + }); + }); + } +} + +module.exports = { + TCPMonitorType, + TLS_ALERT_CODES, + parseTlsAlertNumber, + getTlsAlertName, +}; diff --git a/server/monitor-types/websocket-upgrade.js b/server/monitor-types/websocket-upgrade.js new file mode 100644 index 0000000..97ef77b --- /dev/null +++ b/server/monitor-types/websocket-upgrade.js @@ -0,0 +1,158 @@ +const { MonitorType } = require("./monitor-type"); +const WebSocket = require("ws"); +const { UP } = require("../../src/util"); +const { checkStatusCode, getOidcTokenClientCredentials } = require("../util-server"); +// Define closing error codes https://www.iana.org/assignments/websocket/websocket.xml#close-code-number +const WS_ERR_CODE = { + 1002: "Protocol error", + 1003: "Unsupported Data", + 1005: "No Status Received", + 1006: "Abnormal Closure", + 1007: "Invalid frame payload data", + 1008: "Policy Violation", + 1009: "Message Too Big", + 1010: "Mandatory Extension Missing", + 1011: "Internal Error", + 1012: "Service Restart", + 1013: "Try Again Later", + 1014: "Bad Gateway", + 1015: "TLS Handshake Failed", + 3000: "Unauthorized", + 3003: "Forbidden", + 3008: "Timeout", +}; + +class WebSocketMonitorType extends MonitorType { + name = "websocket-upgrade"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + const [message, code] = await this.attemptUpgrade(monitor); + + if (typeof code !== "undefined") { + // If returned status code matches user controlled accepted status code(default 1000), return success + if (checkStatusCode(code, JSON.parse(monitor.accepted_statuscodes_json))) { + heartbeat.status = UP; + heartbeat.msg = message; + return; // success at this point + } + + // Throw an error using friendly name if defined, fallback to generic msg + throw new Error(WS_ERR_CODE[code] || `Unexpected status code: ${code}`); + } + // If no close code, then an error has occurred, display to user + if (typeof message !== "undefined") { + throw new Error(`${message}`); + } + // Throw generic error if nothing is defined, should never happen + throw new Error("Unknown Websocket Error"); + } + + /** + * Builds the WebSocket options object for authentication and TLS. + * Supports basic auth, OAuth2 client credentials, and mTLS. + * @param {object} monitor The monitor object for input parameters. + * @returns {Promise} The options object to pass to the WebSocket constructor. + */ + async buildWsOptions(monitor) { + const options = {}; + + const timeoutMs = (monitor.timeout ?? 20) * 1000; + options.handshakeTimeout = timeoutMs; + + // Parse custom headers if provided + if (monitor.headers) { + try { + options.headers = JSON.parse(monitor.headers); + } catch (e) { + // If headers is not valid JSON, ignore it + options.headers = {}; + } + } else { + options.headers = {}; + } + + if (monitor.authMethod === "basic") { + if (monitor.basic_auth_user || monitor.basic_auth_pass) { + const credentials = Buffer.from( + `${monitor.basic_auth_user ?? ""}:${monitor.basic_auth_pass ?? ""}` + ).toString("base64"); + options.headers.Authorization = `Basic ${credentials}`; + } + } else if (monitor.authMethod === "bearer") { + if (monitor.bearer_token) { + options.headers.Authorization = `Bearer ${monitor.bearer_token}`; + } + } else if (monitor.authMethod === "oauth2-cc") { + if (new Date((monitor.oauthAccessToken?.expires_at || 0) * 1000) <= new Date()) { + monitor.oauthAccessToken = await getOidcTokenClientCredentials( + monitor.oauth_token_url, + monitor.oauth_client_id, + monitor.oauth_client_secret, + monitor.oauth_scopes, + monitor.oauth_audience, + monitor.oauth_auth_method + ); + } + options.headers.Authorization = `${monitor.oauthAccessToken.token_type} ${monitor.oauthAccessToken.access_token}`; + } else if (monitor.authMethod === "mtls") { + if (monitor.tlsCert) { + options.cert = monitor.tlsCert; + } + if (monitor.tlsKey) { + options.key = monitor.tlsKey; + } + if (monitor.tlsCa) { + options.ca = monitor.tlsCa; + } + options.rejectUnauthorized = !monitor.getIgnoreTls(); + } + + return options; + } + + /** + * Uses the ws Node.js library to establish a connection to target server + * @param {object} monitor The monitor object for input parameters. + * @returns {Promise<[ string, int ]>} Array containing a status message and response code + */ + async attemptUpgrade(monitor) { + const authOptions = await this.buildWsOptions(monitor); + + return new Promise((resolve) => { + // If user inputs subprotocol(s), convert to array, set Sec-WebSocket-Protocol header, timeout in ms. Subprotocol Identifier column: https://www.iana.org/assignments/websocket/websocket.xml#subprotocol-name + const subprotocol = monitor.wsSubprotocol ? monitor.wsSubprotocol.replace(/\s/g, "").split(",") : undefined; + const ws = new WebSocket(monitor.url, subprotocol, authOptions); + + ws.addEventListener("open", (event) => { + // Immediately close the connection + ws.close(1000); + }); + + ws.onerror = (error) => { + // Give user the choice to ignore Sec-WebSocket-Accept header for non compliant servers + // Header in HTTP 101 Switching Protocols response from server, technically already upgraded to WS + if ( + monitor.wsIgnoreSecWebsocketAcceptHeader && + error.message === "Invalid Sec-WebSocket-Accept header" + ) { + resolve(["1000 - OK", 1000]); + return; + } + // Upgrade failed, return message to user + resolve([error.message, error.code]); + }; + + ws.onclose = (event) => { + // Return the close code, if connection didn't close cleanly, return the reason if present + resolve([event.wasClean ? event.code.toString() + " - OK" : event.reason, event.code]); + }; + }); + } +} + +module.exports = { + WebSocketMonitorType, +}; diff --git a/server/notification-providers/360messenger.js b/server/notification-providers/360messenger.js new file mode 100644 index 0000000..90d926d --- /dev/null +++ b/server/notification-providers/360messenger.js @@ -0,0 +1,161 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Whatsapp360messenger extends NotificationProvider { + name = "Whatsapp360messenger"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + Authorization: "Bearer " + notification.Whatsapp360messengerAuthToken, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + // Use custom template if enabled + let message = msg; + if (notification.Whatsapp360messengerUseTemplate && notification.Whatsapp360messengerTemplate) { + message = this.applyTemplate( + notification.Whatsapp360messengerTemplate, + msg, + monitorJSON, + heartbeatJSON + ); + } + + // Normalize recipients: support comma/semicolon-separated list + const recipients = (notification.Whatsapp360messengerRecipient || "") + .split(/[;,]/) + .map((r) => r.trim()) + .filter((r) => r !== ""); + + // Normalize group IDs: support array (multi-select) and fallback to single value / delimited string + const rawGroupIds = + notification.Whatsapp360messengerGroupIds || notification.Whatsapp360messengerGroupId || ""; + + let groupIds = []; + if (Array.isArray(rawGroupIds)) { + groupIds = rawGroupIds + .map((g) => { + if (typeof g === "string") { + return g.trim(); + } + if (g && typeof g === "object" && g.id) { + return String(g.id).trim(); + } + return ""; + }) + .filter((g) => g !== ""); + } else if (typeof rawGroupIds === "string" && rawGroupIds.trim() !== "") { + groupIds = rawGroupIds + .split(/[;,]/) + .map((g) => g.trim()) + .filter((g) => g !== ""); + } + + const hasGroupId = groupIds.length > 0; + const hasRecipient = recipients.length > 0; + + // Send to both if both are provided + if (hasGroupId && hasRecipient) { + // Send to all individual recipients + await Promise.all( + recipients.map((recipient) => { + const recipientData = { + phonenumber: recipient, + text: message, + }; + return axios.post("https://api.360messenger.com/v2/sendMessage", recipientData, config); + }) + ); + + // Send to all selected groups + await Promise.all( + groupIds.map((groupId) => { + const groupData = { + groupId, + text: message, + }; + return axios.post("https://api.360messenger.com/v2/sendGroup", groupData, config); + }) + ); + + return `${okMsg} (Sent to ${recipients.length} recipient(s) and ${groupIds.length} group(s))`; + } else if (hasGroupId) { + // Send to group(s) only + await Promise.all( + groupIds.map((groupId) => { + const data = { + groupId, + text: message, + }; + return axios.post("https://api.360messenger.com/v2/sendGroup", data, config); + }) + ); + + return `${okMsg} (Sent to ${groupIds.length} group(s))`; + } else if (hasRecipient) { + // Send to recipient(s) only + await Promise.all( + recipients.map((recipient) => { + const data = { + phonenumber: recipient, + text: message, + }; + return axios.post("https://api.360messenger.com/v2/sendMessage", data, config); + }) + ); + + return `${okMsg} (Sent to ${recipients.length} recipient(s))`; + } else { + throw new Error("No recipient or group specified"); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Apply template with variables + * @param {string} template - Template string + * @param {string} msg - Default message + * @param {object} monitorJSON - Monitor data + * @param {object} heartbeatJSON - Heartbeat data + * @returns {string} Formatted message + */ + applyTemplate(template, msg, monitorJSON, heartbeatJSON) { + try { + // Simple template replacement + let result = template; + + // Replace monitor variables + if (monitorJSON) { + result = result.replace(/{{ monitorJSON\['name'\] }}/g, monitorJSON.name || ""); + result = result.replace(/{{ monitorJSON\['url'\] }}/g, monitorJSON.url || ""); + } + + // Replace message variable + result = result.replace(/{{ msg }}/g, msg); + + // Handle conditional blocks (simple if statements) + result = result.replace(/{% if monitorJSON %}([\s\S]*?){% endif %}/g, (match, content) => { + return monitorJSON ? content : ""; + }); + + return result; + } catch (error) { + // If template parsing fails, return original message + return msg; + } + } +} + +module.exports = Whatsapp360messenger; diff --git a/server/notification-providers/46elks.js b/server/notification-providers/46elks.js new file mode 100644 index 0000000..3008dd4 --- /dev/null +++ b/server/notification-providers/46elks.js @@ -0,0 +1,39 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Elks extends NotificationProvider { + name = "Elks"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://api.46elks.com/a1/sms"; + + try { + let data = new URLSearchParams(); + data.append("from", notification.elksFromNumber); + data.append("to", notification.elksToNumber); + data.append("message", msg); + + let config = { + headers: { + Authorization: + "Basic " + + Buffer.from(`${notification.elksUsername}:${notification.elksAuthToken}`).toString("base64"), + }, + }; + + config = this.getAxiosConfigWithProxy(config); + + await axios.post(url, data, config); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Elks; diff --git a/server/notification-providers/HaloPSA.js b/server/notification-providers/HaloPSA.js new file mode 100644 index 0000000..abcaa80 --- /dev/null +++ b/server/notification-providers/HaloPSA.js @@ -0,0 +1,80 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +/** + * Halo PSA notification provider implementation + */ +class HaloPSA extends NotificationProvider { + /** + * Provider name used in registration + * @type {string} + */ + name = "HaloPSA"; + + /** + * Send notification to Halo PSA webhook + * @param {object} notification - Notification configuration + * @param {string} msg - Message content + * @param {object|null} monitorJSON - Monitor configuration (null for cert expiry) + * @param {object|null} heartbeatJSON - Heartbeat data + * @returns {Promise} Success message + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent successfully."; + + try { + // Determine status based on heartbeat + let status = "UNKNOWN"; + if (heartbeatJSON?.status === 1) { + status = "UP"; + } else if (heartbeatJSON?.status === 0) { + status = "DOWN"; + } else if (monitorJSON == null && heartbeatJSON != null) { + status = "NOTIFICATION"; + } + + /** + * Payload structure expected by Halo PSA webhook + * @type {object} + */ + const payload = { + title: "Uptime Kuma Alert", + status: status, + monitor: monitorJSON?.name || "No Monitor", + monitor_id: monitorJSON?.id || null, + message: msg, + timestamp: new Date().toISOString(), + uptime_kuma_version: process.env.npm_package_version || "unknown", + }; + + // Send POST request to Halo PSA webhook + let config = { + headers: { + "Content-Type": "application/json", + }, + }; + + if (notification.haloUsername && notification.haloPassword) { + const data = notification.haloUsername + ":" + notification.haloPassword; + const base64data = Buffer.from(data).toString("base64"); + + config.headers.Authorization = `Basic ${base64data}`; + } + + config = this.getAxiosConfigWithProxy(config); + + const result = await axios.post(notification.halowebhookurl, payload, config); + + // Check for successful HTTP response + if (result.status === 200 || result.status === 201 || result.status === 204) { + return okMsg; + } + + throw new Error(`Received unexpected status code ${result.status} from notification provider HaloPSA`); + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = HaloPSA; diff --git a/server/notification-providers/Webpush.js b/server/notification-providers/Webpush.js new file mode 100644 index 0000000..86b94c3 --- /dev/null +++ b/server/notification-providers/Webpush.js @@ -0,0 +1,34 @@ +const NotificationProvider = require("./notification-provider"); +const webpush = require("web-push"); +const { setting } = require("../util-server"); + +class Webpush extends NotificationProvider { + name = "Webpush"; + + /** + * @inheritDoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + const publicVapidKey = await setting("webpushPublicVapidKey"); + const privateVapidKey = await setting("webpushPrivateVapidKey"); + + webpush.setVapidDetails("https://github.com/louislam/uptime-kuma", publicVapidKey, privateVapidKey); + + const data = JSON.stringify({ + title: "Uptime Kuma", + body: msg, + }); + + await webpush.sendNotification(notification.subscription, data); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Webpush; diff --git a/server/notification-providers/alerta.js b/server/notification-providers/alerta.js new file mode 100644 index 0000000..b02f1b4 --- /dev/null +++ b/server/notification-providers/alerta.js @@ -0,0 +1,75 @@ +const NotificationProvider = require("./notification-provider"); +const { DOWN, UP } = require("../../src/util"); +const axios = require("axios"); + +class Alerta extends NotificationProvider { + name = "alerta"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + "Content-Type": "application/json;charset=UTF-8", + Authorization: "Key " + notification.alertaApiKey, + }, + }; + let data = { + environment: notification.alertaEnvironment, + severity: "critical", + correlate: [], + service: ["UptimeKuma"], + value: "Timeout", + tags: ["uptimekuma"], + attributes: {}, + origin: "uptimekuma", + type: "exceptionAlert", + }; + + config = this.getAxiosConfigWithProxy(config); + + if (heartbeatJSON == null) { + let postData = Object.assign( + { + event: "msg", + text: msg, + group: "uptimekuma-msg", + resource: "Message", + }, + data + ); + + await axios.post(notification.alertaApiEndpoint, postData, config); + } else { + let datadup = Object.assign( + { + correlate: ["service_up", "service_down"], + event: monitorJSON["type"], + group: "uptimekuma-" + monitorJSON["type"], + resource: monitorJSON["name"], + }, + data + ); + + if (heartbeatJSON["status"] === DOWN) { + datadup.severity = notification.alertaAlertState; // critical + datadup.text = "Service " + monitorJSON["type"] + " is down."; + await axios.post(notification.alertaApiEndpoint, datadup, config); + } else if (heartbeatJSON["status"] === UP) { + datadup.severity = notification.alertaRecoverState; // cleaned + datadup.text = "Service " + monitorJSON["type"] + " is up."; + await axios.post(notification.alertaApiEndpoint, datadup, config); + } + } + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Alerta; diff --git a/server/notification-providers/alertnow.js b/server/notification-providers/alertnow.js new file mode 100644 index 0000000..e95d82e --- /dev/null +++ b/server/notification-providers/alertnow.js @@ -0,0 +1,54 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { getMonitorRelativeURL, UP, DOWN } = require("../../src/util"); +const { Settings } = require("../settings"); + +class AlertNow extends NotificationProvider { + name = "AlertNow"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let textMsg = ""; + let status = "open"; + let eventType = "ERROR"; + let eventId = new Date().toISOString().slice(0, 10).replace(/-/g, ""); + + if (heartbeatJSON && heartbeatJSON.status === UP) { + textMsg = `[${heartbeatJSON.name}] ✅ Application is back online`; + status = "close"; + eventType = "INFO"; + eventId += `_${heartbeatJSON.name.replace(/\s/g, "")}`; + } else if (heartbeatJSON && heartbeatJSON.status === DOWN) { + textMsg = `[${heartbeatJSON.name}] 🔴 Application went down`; + } + + textMsg += ` - ${msg}`; + + const baseURL = await Settings.get("primaryBaseURL"); + if (baseURL && monitorJSON) { + textMsg += ` >> ${baseURL + getMonitorRelativeURL(monitorJSON.id)}`; + } + + const data = { + summary: textMsg, + status: status, + event_type: eventType, + event_id: eventId, + }; + + let config = this.getAxiosConfigWithProxy({}); + + await axios.post(notification.alertNowWebhookURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = AlertNow; diff --git a/server/notification-providers/aliyun-sms.js b/server/notification-providers/aliyun-sms.js new file mode 100644 index 0000000..52eb219 --- /dev/null +++ b/server/notification-providers/aliyun-sms.js @@ -0,0 +1,178 @@ +const NotificationProvider = require("./notification-provider"); +const { DOWN, UP } = require("../../src/util"); +const { default: axios } = require("axios"); +const Crypto = require("crypto"); +const qs = require("qs"); + +class AliyunSMS extends NotificationProvider { + name = "AliyunSMS"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + if (heartbeatJSON != null) { + let msgBody = JSON.stringify({ + name: monitorJSON["name"], + time: heartbeatJSON["localDateTime"], + status: this.statusToString(heartbeatJSON["status"]), + ...(notification.optionalParameters && { + msg: this.removeIpAndDomain(heartbeatJSON["msg"]), + }), + }); + if (await this.sendSms(notification, msgBody)) { + return okMsg; + } + } else { + let msgBody = JSON.stringify({ + name: "", + time: "", + status: "", + ...(notification.optionalParameters && { + msg: this.removeIpAndDomain(msg), + }), + }); + if (await this.sendSms(notification, msgBody)) { + return okMsg; + } + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Send the SMS notification + * @param {BeanModel} notification Notification details + * @param {string} msgbody Message template + * @returns {Promise} True if successful else false + */ + async sendSms(notification, msgbody) { + let params = { + PhoneNumbers: notification.phonenumber, + TemplateCode: notification.templateCode, + SignName: notification.signName, + TemplateParam: msgbody, + AccessKeyId: notification.accessKeyId, + Format: "JSON", + SignatureMethod: "HMAC-SHA1", + SignatureVersion: "1.0", + SignatureNonce: Math.random().toString(), + Timestamp: new Date().toISOString(), + Action: "SendSms", + Version: "2017-05-25", + }; + + params.Signature = this.sign(params, notification.secretAccessKey); + let config = { + method: "POST", + url: "http://dysmsapi.aliyuncs.com/", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + data: qs.stringify(params), + }; + + config = this.getAxiosConfigWithProxy(config); + + let result = await axios(config); + if (result.data.Message === "OK") { + return true; + } + + throw new Error(result.data.Message); + } + + /** + * Aliyun request sign + * @param {object} param Parameters object to sign + * @param {string} AccessKeySecret Secret key to sign parameters with + * @returns {string} Base64 encoded request + */ + sign(param, AccessKeySecret) { + let param2 = {}; + let data = []; + + let oa = Object.keys(param).sort(); + + for (let i = 0; i < oa.length; i++) { + let key = oa[i]; + param2[key] = param[key]; + } + + // Escape more characters than encodeURIComponent does. + // For generating Aliyun signature, all characters except A-Za-z0-9~-._ are encoded. + // See https://help.aliyun.com/document_detail/315526.html + // This encoding methods as known as RFC 3986 (https://tools.ietf.org/html/rfc3986) + let moreEscapesTable = function (m) { + return { + "!": "%21", + "*": "%2A", + "'": "%27", + "(": "%28", + ")": "%29", + }[m]; + }; + + for (let key in param2) { + let value = encodeURIComponent(param2[key]).replace(/[!*'()]/g, moreEscapesTable); + data.push(`${encodeURIComponent(key)}=${value}`); + } + + let StringToSign = `POST&${encodeURIComponent("/")}&${encodeURIComponent(data.join("&"))}`; + return Crypto.createHmac("sha1", `${AccessKeySecret}&`).update(Buffer.from(StringToSign)).digest("base64"); + } + + /** + * Convert status constant to string + * @param {const} status The status constant + * @returns {string} Status + */ + statusToString(status) { + switch (status) { + case DOWN: + return "DOWN"; + case UP: + return "UP"; + default: + return status; + } + } + + /** + * Remove IP addresses and domains from message to comply with Aliyun SMS restrictions + * @param {string} message Original message + * @returns {string} Message with IP addresses and domains removed + */ + removeIpAndDomain(message) { + if (!message) { + return message; + } + + // 1. Remove URLs first to avoid domain being matched separately + message = message.replace(/(?:https?|ftp|ws|wss):\/\/[^\s]+/gi, "[URL]"); + + // 2. Remove IPv4 addresses (with or without port) + message = message.replace(/\b(?:\d{1,3}\.){3}\d{1,3}(?::\d+)?\b/g, "[IP]"); + + // 3. Remove IPv6 addresses (with or without port) + message = message.replace(/\[?(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\]?(?::\d+)?/g, "[IP]"); + + // 4. Remove domain names (including subdomains and ports) + // Matches example.com, www.example.com, sub.example.com:8080, etc. + message = message.replace( + /\b(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}(?::\d+)?\b/g, + "[Domain]" + ); + + // 5. Remove CIDR notation (e.g., 192.168.0.0/24) + message = message.replace(/\b(?:\d{1,3}\.){3}\d{1,3}\/\d{1,2}\b/g, "[CIDR]"); + + return message; + } +} + +module.exports = AliyunSMS; diff --git a/server/notification-providers/amootsms.js b/server/notification-providers/amootsms.js new file mode 100644 index 0000000..1ace9de --- /dev/null +++ b/server/notification-providers/amootsms.js @@ -0,0 +1,299 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class AmootSMS extends NotificationProvider { + name = "amootsms"; + + /** + * @param {object} notification Notification configuration + * @param {string} msg Notification message + * @param {object|null} monitorJSON Monitor data + * @param {object|null} heartbeatJSON Heartbeat data + * @returns {Promise} Result message + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const statusMessages = { + "-300": "System_Disabled", + "-208": "Token_NotAllowMethod", + "-207": "Token_NotAllowIP", + "-204": "Token_Expired", + "-203": "Token_Disabled", + "-202": "Token_Invalid", + "-201": "Token_NotExists", + "-109": "User_NotAllowHttp", + "-108": "User_NotAllowMethod", + "-107": "User_NotAllowIP", + "-106": "User_NotAllowWebService", + "-105": "User_WebServiceBanned", + "-104": "User_Expired", + "-103": "User_Disabled", + "-102": "User_MobileNotVerified", + "-1": "User_NotExists", + 0: "Failed", + 1: "Success", + 2: "AccountIsDemo", + 4: "CreditNotEnough", + 5: "LineNumber_NotExist", + 6: "BackupLineNumber_NotExist", + 7: "Avanak_NotAvailable", + 10: "UserName_Empty", + 11: "Password_Empty", + 12: "LineNumber_Empty", + 13: "BackupLineNumber_Empty", + 14: "SMSMessageText_Empty", + 15: "AvanakMessageText_Empty", + 16: "Mobile_Empty", + 17: "Mobiles_Empty", + 18: "Title_Empty", + 19: "FirstNameOrLastName_Empty", + 20: "URLAddress_Empty", + 100: "UserNameOrPassword_Invalid", + 101: "Mobile_Invalid", + 102: "Mobiles_Invalid", + 103: "Count_Invalid", + 104: "FromRow_Invalid", + 105: "FromDate_Invalid", + 106: "FromDateTime_Invalid", + 107: "ToDate_Invalid", + 108: "ToDateTime_Invalid", + 109: "FromDateIsAfterThanToDate", + 110: "FromDateTimeIsAfterThanToDateTime", + 111: "MessageID_Invalid", + 112: "BulkID_Invalid", + 113: "ContactID_Invalid", + 114: "ContactGroupID_Invalid", + 115: "CourseID_Invalid", + 116: "CourseGroupID_Invalid", + 117: "URLAddress_Duplicate", + 118: "RelayMessageDeliveryID_Invalid", + 119: "RelayRecieveMessageID_Invalid", + 120: "Length_Invalid", + 121: "Length_Exceeded", + 500: "ServerError", + }; + + const mobiles = String(notification.amootMobiles || "") + .split(",") + .map((mobile) => mobile.trim()) + .filter(Boolean); + + if (!notification.amootApiToken) { + throw new Error("Amoot SMS API token is required."); + } + + if (mobiles.length === 0) { + throw new Error("At least one Amoot SMS recipient number is required."); + } + + if (notification.amootUsePattern && !notification.amootPatternCodeId) { + throw new Error("Amoot SMS Pattern Code ID is required."); + } + + if ((!notification.amootUsePattern || notification.amootUseOwnLine) && !notification.amootLineNumber) { + throw new Error("Amoot SMS line number is required."); + } + + if (notification.amootUsePattern) { + if (notification.amootUseOwnLine) { + return this.sendWithPatternOwn(notification, msg, mobiles); + } + + return this.sendWithPattern(notification, msg, mobiles); + } + + const getIranDateTime = () => { + const parts = new Intl.DateTimeFormat("en-US", { + timeZone: "Asia/Tehran", + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hourCycle: "h23", + }).formatToParts(new Date()); + + const values = {}; + + for (const part of parts) { + values[part.type] = part.value; + } + + return `${values.year}-${values.month}-${values.day} ${values.hour}:${values.minute}:${values.second}`; + }; + + const data = new URLSearchParams(); + + data.append("SendDateTime", getIranDateTime()); + data.append("SMSMessageText", msg); + data.append("LineNumber", notification.amootLineNumber); + data.append("Mobiles", mobiles.join(",")); + + const config = { + headers: { + Authorization: notification.amootApiToken, + "Content-Type": "application/x-www-form-urlencoded", + }, + }; + + try { + const response = await axios.post( + "https://portal.amootsms.com/rest/SendSimple", + data, + this.getAxiosConfigWithProxy(config) + ); + + const result = response.data; + + if (!result || typeof result !== "object") { + throw new Error("Amoot SMS API returned an unexpected response."); + } + + if (result.Status && result.Status !== "Success") { + const errorMessage = result.ErrorMessage ? `: ${result.ErrorMessage}` : ""; + + throw new Error(`Amoot SMS API error: ${result.Status}${errorMessage}`); + } + + if (!Array.isArray(result.Data) || result.Data.length === 0) { + throw new Error("Amoot SMS API returned an unexpected response."); + } + + const failedMessage = result.Data.find((item) => { + const status = item?.Status; + + return status !== 1 && status !== "1" && status !== "Success"; + }); + + if (failedMessage) { + const rawStatus = failedMessage.Status; + const numericStatus = Number(rawStatus); + + if (!Number.isNaN(numericStatus)) { + const statusTitle = statusMessages[numericStatus] || "UnknownError"; + + throw new Error(`Amoot SMS API error: ${statusTitle} (${numericStatus})`); + } + + throw new Error(`Amoot SMS API error: ${rawStatus}`); + } + + return "Sent Successfully."; + } catch (error) { + if (axios.isAxiosError(error)) { + this.throwGeneralAxiosError(error); + } + + throw error; + } + } + + /** + * Send an SMS notification using an Amoot SMS pattern. + * @param {object} notification Notification configuration + * @param {string} msg Notification message + * @param {string[]} mobiles Recipient mobile numbers + * @returns {Promise} Result message + */ + async sendWithPattern(notification, msg, mobiles) { + const config = { + headers: { + Authorization: notification.amootApiToken, + "Content-Type": "application/x-www-form-urlencoded", + }, + }; + + try { + for (const mobile of mobiles) { + const data = new URLSearchParams(); + + data.append("Token", notification.amootApiToken); + data.append("Mobile", mobile); + data.append("PatternCodeID", String(notification.amootPatternCodeId)); + data.append("PatternValues", msg); + + const response = await axios.post( + "https://portal.amootsms.com/rest/SendWithPattern", + data, + this.getAxiosConfigWithProxy(config) + ); + + const result = response.data; + + if (!result || typeof result !== "object") { + throw new Error("Amoot SMS API returned an unexpected response."); + } + + if (result.Status && result.Status !== "Success") { + const errorMessage = result.ErrorMessage ? `: ${result.ErrorMessage}` : ""; + + throw new Error(`Amoot SMS API error: ${result.Status}${errorMessage}`); + } + } + + return "Sent Successfully."; + } catch (error) { + if (axios.isAxiosError(error)) { + this.throwGeneralAxiosError(error); + } + + throw error; + } + } + + /** + * Send an SMS notification using an Amoot SMS pattern with an own line. + * @param {object} notification Notification configuration + * @param {string} msg Notification message + * @param {string[]} mobiles Recipient mobile numbers + * @returns {Promise} Result message + */ + async sendWithPatternOwn(notification, msg, mobiles) { + const config = { + headers: { + Authorization: `Bearer ${notification.amootApiToken}`, + "Content-Type": "application/json", + }, + }; + + try { + for (const mobile of mobiles) { + const data = { + Token: notification.amootApiToken, + LineNumber: notification.amootLineNumber, + Mobile: mobile, + PatternCodeID: Number(notification.amootPatternCodeId), + PatternValues: msg, + }; + + const response = await axios.post( + "https://portal.amootsms.com/rest/SendWithPatternOWN", + data, + this.getAxiosConfigWithProxy(config) + ); + + const result = response.data; + + if (!result || typeof result !== "object") { + throw new Error("Amoot SMS API returned an unexpected response."); + } + + if (result.Status && result.Status !== "Success") { + const errorMessage = result.ErrorMessage ? `: ${result.ErrorMessage}` : ""; + + throw new Error(`Amoot SMS API error: ${result.Status}${errorMessage}`); + } + } + + return "Sent Successfully."; + } catch (error) { + if (axios.isAxiosError(error)) { + this.throwGeneralAxiosError(error); + } + + throw error; + } + } +} + +module.exports = AmootSMS; diff --git a/server/notification-providers/apprise.js b/server/notification-providers/apprise.js new file mode 100644 index 0000000..ce157a0 --- /dev/null +++ b/server/notification-providers/apprise.js @@ -0,0 +1,34 @@ +const NotificationProvider = require("./notification-provider"); +const childProcessAsync = require("promisify-child-process"); + +class Apprise extends NotificationProvider { + name = "apprise"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + const args = ["-vv", "-b", msg, notification.appriseURL]; + if (notification.title) { + args.push("-t"); + args.push(notification.title); + } + const s = await childProcessAsync.spawn("apprise", args, { + encoding: "utf8", + }); + + // A successful run (exit code 0) may legitimately print nothing to + // stdout, so empty output must not be treated as a failure (#5547). + const output = s.stdout ? s.stdout.toString() : ""; + + if (output.includes("ERROR")) { + throw new Error(output); + } + + return okMsg; + } +} + +module.exports = Apprise; diff --git a/server/notification-providers/bale.js b/server/notification-providers/bale.js new file mode 100644 index 0000000..3216496 --- /dev/null +++ b/server/notification-providers/bale.js @@ -0,0 +1,34 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Bale extends NotificationProvider { + name = "bale"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://tapi.bale.ai"; + + try { + await axios.post( + `${url}/bot${notification.baleBotToken}/sendMessage`, + { + chat_id: notification.baleChatID, + text: msg, + }, + { + headers: { + "content-type": "application/json", + }, + } + ); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Bale; diff --git a/server/notification-providers/bark.js b/server/notification-providers/bark.js new file mode 100644 index 0000000..89e02b7 --- /dev/null +++ b/server/notification-providers/bark.js @@ -0,0 +1,128 @@ +// +// bark.js +// UptimeKuma +// +// Created by Lakr Aream on 2021/10/24. +// Copyright © 2021 Lakr Aream. All rights reserved. +// + +const NotificationProvider = require("./notification-provider"); +const { DOWN, UP } = require("../../src/util"); +const { default: axios } = require("axios"); + +// bark is an APN bridge that sends notifications to Apple devices. + +const barkNotificationAvatar = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; +const successMessage = "Successes!"; + +class Bark extends NotificationProvider { + name = "Bark"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + let barkEndpoint = notification.barkEndpoint; + + // check if the endpoint has a "/" suffix, if so, delete it first + if (barkEndpoint.endsWith("/")) { + barkEndpoint = barkEndpoint.substring(0, barkEndpoint.length - 1); + } + + if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] === UP) { + let title = "UptimeKuma Monitor Up"; + return await this.postNotification(notification, title, msg, barkEndpoint); + } + + if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] === DOWN) { + let title = "UptimeKuma Monitor Down"; + return await this.postNotification(notification, title, msg, barkEndpoint); + } + + if (msg != null) { + let title = "UptimeKuma Message"; + return await this.postNotification(notification, title, msg, barkEndpoint); + } + } + + /** + * Add additional parameter for Bark v1 endpoints. + * Leads to better on device styles (iOS 15 optimized) + * @param {BeanModel} notification Notification to send + * @returns {string} Additional URL parameters + */ + additionalParameters(notification) { + // set icon to uptime kuma icon, 11kb should be fine + let params = "?icon=" + barkNotificationAvatar; + // grouping all our notifications + if (notification.barkGroup != null) { + params += "&group=" + notification.barkGroup; + } else { + // default name + params += "&group=" + "UptimeKuma"; + } + // picked a sound, this should follow system's mute status when arrival + if (notification.barkSound != null) { + params += "&sound=" + notification.barkSound; + } else { + // default sound + params += "&sound=" + "telegraph"; + } + return params; + } + + /** + * Check if result is successful + * @param {object} result Axios response object + * @returns {void} + * @throws {Error} The status code is not in range 2xx + */ + checkResult(result) { + if (result.status == null) { + throw new Error("Bark notification failed with invalid response!"); + } + if (result.status < 200 || result.status >= 300) { + throw new Error("Bark notification failed with status code " + result.status); + } + } + + /** + * Send the message + * @param {BeanModel} notification Notification to send + * @param {string} title Message title + * @param {string} subtitle Message + * @param {string} endpoint Endpoint to send request to + * @returns {Promise} Success message + */ + async postNotification(notification, title, subtitle, endpoint) { + let result; + let config = this.getAxiosConfigWithProxy({}); + if (notification.apiVersion === "v1" || notification.apiVersion == null) { + // url encode title and subtitle + title = encodeURIComponent(title); + subtitle = encodeURIComponent(subtitle); + const params = this.additionalParameters(notification); + result = await axios.get(`${endpoint}/${title}/${subtitle}${params}`, config); + } else { + result = await axios.post( + endpoint, + { + title, + body: subtitle, + icon: barkNotificationAvatar, + sound: notification.barkSound || "telegraph", // default sound is telegraph + group: notification.barkGroup || "UptimeKuma", // default group is UptimeKuma + }, + config + ); + } + this.checkResult(result); + if (result.statusText != null) { + return "Bark notification succeed: " + result.statusText; + } + // because returned in range 200 ..< 300 + return successMessage; + } +} + +module.exports = Bark; diff --git a/server/notification-providers/bearsms.js b/server/notification-providers/bearsms.js new file mode 100644 index 0000000..7169fe3 --- /dev/null +++ b/server/notification-providers/bearsms.js @@ -0,0 +1,54 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class BearSMS extends NotificationProvider { + name = "bearsms"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + // BearSMS rejects messages containing emoji (astral characters) with error 200 + const cleanMsg = msg.replaceAll("🔴 ", "").replaceAll("✅ ", ""); + const params = new URLSearchParams({ + app: "ws", + u: notification.bearsmsUsername, + h: notification.bearsmsHashKey, + op: "pv", + to: notification.bearsmsPhoneNumber, + msg: cleanMsg, + }); + + if (notification.bearsmsSenderId) { + params.append("from", notification.bearsmsSenderId); + } + + // Non-GSM text (e.g. Hebrew) must be flagged as unicode + if (/[^\x00-\x7F]/.test(cleanMsg)) { + params.append("unicode", "1"); + } + + const url = `https://app.bearsms.com/index.php?${params.toString()}`; + let config = this.getAxiosConfigWithProxy({}); + const response = await axios.get(url, config); + + // BearSMS responds with HTTP 200 even on failure. + // Failure: {"status":"ERR","error":"100","error_string":"authentication failed"} + // Success: {"data":[{"status":"OK","error":"0","smslog_id":"..."}],"error_string":null} + const data = response.data; + const results = Array.isArray(data?.data) ? data.data : []; + if (data?.status === "ERR" || data?.error_string || !results.some((r) => r.status === "OK")) { + throw new Error(data?.error_string || JSON.stringify(data)); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = BearSMS; diff --git a/server/notification-providers/bitrix24.js b/server/notification-providers/bitrix24.js new file mode 100644 index 0000000..8948401 --- /dev/null +++ b/server/notification-providers/bitrix24.js @@ -0,0 +1,31 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP } = require("../../src/util"); + +class Bitrix24 extends NotificationProvider { + name = "Bitrix24"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + const params = { + user_id: notification.bitrix24UserID, + message: "[B]Uptime Kuma[/B]", + "ATTACH[COLOR]": (heartbeatJSON ?? {})["status"] === UP ? "#b73419" : "#67b518", + "ATTACH[BLOCKS][0][MESSAGE]": msg, + }; + + let config = this.getAxiosConfigWithProxy({ params }); + await axios.get(`${notification.bitrix24WebhookURL}/im.notify.system.add.json`, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Bitrix24; diff --git a/server/notification-providers/brevo.js b/server/notification-providers/brevo.js new file mode 100644 index 0000000..65a7bfd --- /dev/null +++ b/server/notification-providers/brevo.js @@ -0,0 +1,55 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Brevo extends NotificationProvider { + name = "Brevo"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + "api-key": notification.brevoApiKey, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + let to = [{ email: notification.brevoToEmail }]; + + let data = { + sender: { + email: notification.brevoFromEmail.trim(), + name: notification.brevoFromName || "Uptime Kuma", + }, + to: to, + subject: notification.brevoSubject || "Notification from Your Uptime Kuma", + htmlContent: `

${msg.replace(/\n/g, "
")}

`, + }; + + if (notification.brevoCcEmail) { + data.cc = notification.brevoCcEmail.split(",").map((email) => ({ email: email.trim() })); + } + + if (notification.brevoBccEmail) { + data.bcc = notification.brevoBccEmail.split(",").map((email) => ({ email: email.trim() })); + } + + let result = await axios.post("https://api.brevo.com/v3/smtp/email", data, config); + if (result.status === 201) { + return okMsg; + } else { + throw new Error(`Unexpected status code: ${result.status}`); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Brevo; diff --git a/server/notification-providers/call-me-bot.js b/server/notification-providers/call-me-bot.js new file mode 100644 index 0000000..c0541ae --- /dev/null +++ b/server/notification-providers/call-me-bot.js @@ -0,0 +1,24 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class CallMeBot extends NotificationProvider { + name = "CallMeBot"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + try { + const url = new URL(notification.callMeBotEndpoint); + url.searchParams.set("text", msg); + let config = this.getAxiosConfigWithProxy({}); + await axios.get(url.toString(), config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = CallMeBot; diff --git a/server/notification-providers/cellsynt.js b/server/notification-providers/cellsynt.js new file mode 100644 index 0000000..68a44ff --- /dev/null +++ b/server/notification-providers/cellsynt.js @@ -0,0 +1,40 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Cellsynt extends NotificationProvider { + name = "Cellsynt"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const data = { + // docs at https://www.cellsynt.com/en/sms/api-integration + params: { + username: notification.cellsyntLogin, + password: notification.cellsyntPassword, + destination: notification.cellsyntDestination, + text: msg.replace(/[^\x00-\x7F]/g, ""), + originatortype: notification.cellsyntOriginatortype, + originator: notification.cellsyntOriginator, + allowconcat: notification.cellsyntAllowLongSMS ? 6 : 1, + }, + }; + try { + let config = this.getAxiosConfigWithProxy(data); + const resp = await axios.post("https://se-1.cellsynt.net/sms.php", null, config); + if (resp.data == null) { + throw new Error("Could not connect to Cellsynt, please try again."); + } else if (resp.data.includes("Error:")) { + resp.data = resp.data.replaceAll("Error:", ""); + throw new Error(resp.data); + } + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Cellsynt; diff --git a/server/notification-providers/clicksendsms.js b/server/notification-providers/clicksendsms.js new file mode 100644 index 0000000..6fb58e8 --- /dev/null +++ b/server/notification-providers/clicksendsms.js @@ -0,0 +1,50 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class ClickSendSMS extends NotificationProvider { + name = "clicksendsms"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://rest.clicksend.com/v3/sms/send"; + + try { + let config = { + headers: { + "Content-Type": "application/json", + Authorization: + "Basic " + + Buffer.from(notification.clicksendsmsLogin + ":" + notification.clicksendsmsPassword).toString( + "base64" + ), + Accept: "text/json", + }, + }; + let data = { + messages: [ + { + body: msg.replace(/[^\x00-\x7F]/g, ""), + to: notification.clicksendsmsToNumber, + source: "uptime-kuma", + from: notification.clicksendsmsSenderName, + }, + ], + }; + config = this.getAxiosConfigWithProxy(config); + let resp = await axios.post(url, data, config); + if (resp.data.data.messages[0].status !== "SUCCESS") { + let error = "Something gone wrong. Api returned " + resp.data.data.messages[0].status + "."; + this.throwGeneralAxiosError(error); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = ClickSendSMS; diff --git a/server/notification-providers/clickup.js b/server/notification-providers/clickup.js new file mode 100644 index 0000000..08e2360 --- /dev/null +++ b/server/notification-providers/clickup.js @@ -0,0 +1,47 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class ClickUp extends NotificationProvider { + name = "ClickUp"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = this.getAxiosConfigWithProxy({ + headers: { + Authorization: notification.clickupToken, + "Content-Type": "application/json", + }, + }); + + let content = msg; + if (heartbeatJSON) { + content += `\n\n**Time (${heartbeatJSON["timezone"]}):** ${heartbeatJSON["localDateTime"]}`; + } + + let address = this.extractAddress(monitorJSON); + if (address && !notification.clickupDisableUrl) { + content += `\n**Address:** ${address}`; + } + + const data = { + type: "message", + content, + content_format: "text/md", + }; + + const url = `https://api.clickup.com/api/v3/workspaces/${notification.clickupWorkspaceId}/chat/channels/${notification.clickupChannelId}/messages`; + + await axios.post(url, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = ClickUp; diff --git a/server/notification-providers/dingding.js b/server/notification-providers/dingding.js new file mode 100644 index 0000000..465c8e0 --- /dev/null +++ b/server/notification-providers/dingding.js @@ -0,0 +1,112 @@ +const NotificationProvider = require("./notification-provider"); +const { DOWN, UP } = require("../../src/util"); +const { default: axios } = require("axios"); +const Crypto = require("crypto"); + +class DingDing extends NotificationProvider { + name = "DingDing"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const mentionAll = notification.mentioning === "everyone"; + const mobileList = notification.mentioning === "specify-mobiles" ? notification.mobileList : []; + const userList = notification.mentioning === "specify-users" ? notification.userList : []; + const finalList = [...(mobileList || []), ...(userList || [])]; + const mentionStr = finalList.length > 0 ? "\n" : "" + finalList.map((item) => `@${item}`).join(" "); + try { + if (heartbeatJSON != null) { + let params = { + msgtype: "markdown", + markdown: { + title: `[${this.statusToString(heartbeatJSON["status"])}] ${monitorJSON["name"]}`, + text: `## [${this.statusToString(heartbeatJSON["status"])}] ${monitorJSON["name"]} \n> ${heartbeatJSON["msg"]}\n> Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}${mentionStr}`, + }, + at: { + isAtAll: mentionAll, + atUserIds: userList, + atMobiles: mobileList, + }, + }; + if (await this.sendToDingDing(notification, params)) { + return okMsg; + } + } else { + let params = { + msgtype: "text", + text: { + content: `${msg}${mentionStr}`, + }, + at: { + isAtAll: mentionAll, + atUserIds: userList, + atMobiles: mobileList, + }, + }; + if (await this.sendToDingDing(notification, params)) { + return okMsg; + } + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Send message to DingDing + * @param {BeanModel} notification Notification to send + * @param {object} params Parameters of message + * @returns {Promise} True if successful else false + */ + async sendToDingDing(notification, params) { + let timestamp = Date.now(); + + let config = { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + url: `${notification.webHookUrl}×tamp=${timestamp}&sign=${encodeURIComponent(this.sign(timestamp, notification.secretKey))}`, + data: JSON.stringify(params), + }; + config = this.getAxiosConfigWithProxy(config); + + let result = await axios(config); + if (result.data.errmsg === "ok") { + return true; + } + throw new Error(result.data.errmsg); + } + + /** + * DingDing sign + * @param {Date} timestamp Timestamp of message + * @param {string} secretKey Secret key to sign data with + * @returns {string} Base64 encoded signature + */ + sign(timestamp, secretKey) { + return Crypto.createHmac("sha256", Buffer.from(secretKey, "utf8")) + .update(Buffer.from(`${timestamp}\n${secretKey}`, "utf8")) + .digest("base64"); + } + + /** + * Convert status constant to string + * @param {const} status The status constant + * @returns {string} Status + */ + statusToString(status) { + switch (status) { + case DOWN: + return "DOWN"; + case UP: + return "UP"; + default: + return status; + } + } +} + +module.exports = DingDing; diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js new file mode 100644 index 0000000..c6fb8b3 --- /dev/null +++ b/server/notification-providers/discord.js @@ -0,0 +1,290 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const dayjs = require("dayjs"); +const { DOWN, UP } = require("../../src/util"); + +class Discord extends NotificationProvider { + name = "discord"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + // Discord Message Flags + // @see https://discord.com/developers/docs/resources/message#message-object-message-flags + // This message will not trigger push and desktop notifications + const SUPPRESS_NOTIFICATIONS_FLAG = 1 << 12; + + try { + let config = this.getAxiosConfigWithProxy({}); + const discordDisplayName = notification.discordUsername || "Uptime Kuma"; + const webhookUrl = new URL(notification.discordWebhookUrl); + if (notification.discordChannelType === "postToThread") { + webhookUrl.searchParams.append("thread_id", notification.threadId); + } + + // Check if the webhook has an avatar + let webhookHasAvatar = true; + try { + const webhookInfo = await axios.get(webhookUrl.toString(), config); + webhookHasAvatar = !!webhookInfo.data.avatar; + } catch (e) { + // If we can't verify, we assume he has an avatar to avoid forcing the default avatar + webhookHasAvatar = true; + } + + const messageFormat = + notification.discordMessageFormat || (notification.discordUseMessageTemplate ? "custom" : "normal"); + + // If heartbeatJSON is null, assume we're testing. + if (heartbeatJSON == null) { + let content = msg; + if (messageFormat === "minimalist") { + content = "Test: " + msg; + } else if (messageFormat === "custom") { + const customMessage = notification.discordMessageTemplate?.trim() || ""; + if (customMessage !== "") { + content = await this.renderTemplate(customMessage, msg, monitorJSON, heartbeatJSON); + } + } + let discordtestdata = { + username: discordDisplayName, + content: content, + }; + if (!webhookHasAvatar) { + discordtestdata.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; + } + if (notification.discordChannelType === "createNewForumPost") { + discordtestdata.thread_name = notification.postName; + } + if (notification.discordSuppressNotifications) { + discordtestdata.flags = SUPPRESS_NOTIFICATIONS_FLAG; + } + await axios.post(webhookUrl.toString(), discordtestdata, config); + return okMsg; + } + + // If heartbeatJSON is not null, we go into the normal alerting loop. + let addess = this.extractAddress(monitorJSON); + + // Minimalist: status + name only (is down / is up; no "back up" — may be first trigger) + if (messageFormat === "minimalist") { + const content = + heartbeatJSON["status"] === DOWN + ? "🔴 " + monitorJSON["name"] + " is down." + : "🟢 " + monitorJSON["name"] + " is up."; + let payload = { + username: discordDisplayName, + content: content, + }; + if (!webhookHasAvatar) { + payload.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; + } + if (notification.discordChannelType === "createNewForumPost") { + payload.thread_name = notification.postName; + } + if (notification.discordSuppressNotifications) { + payload.flags = SUPPRESS_NOTIFICATIONS_FLAG; + } + await axios.post(webhookUrl.toString(), payload, config); + return okMsg; + } + + // Custom template: send only content (no embeds) + const useCustomTemplate = + messageFormat === "custom" && (notification.discordMessageTemplate?.trim() || "") !== ""; + if (useCustomTemplate) { + const content = await this.renderTemplate( + notification.discordMessageTemplate.trim(), + msg, + monitorJSON, + heartbeatJSON + ); + let payload = { + username: discordDisplayName, + content: content, + }; + if (!webhookHasAvatar) { + payload.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; + } + if (notification.discordChannelType === "createNewForumPost") { + payload.thread_name = notification.postName; + } + if (notification.discordSuppressNotifications) { + payload.flags = SUPPRESS_NOTIFICATIONS_FLAG; + } + await axios.post(webhookUrl.toString(), payload, config); + return okMsg; + } + + if (heartbeatJSON["status"] === DOWN) { + const wentOfflineTimestamp = dayjs.utc(heartbeatJSON["time"]).unix(); + + let discorddowndata = { + username: discordDisplayName, + embeds: [ + { + title: "❌ Your service " + monitorJSON["name"] + " went down. ❌", + color: 16711680, + timestamp: heartbeatJSON["time"], + fields: [ + { + name: "Service Name", + value: monitorJSON["name"], + }, + ...(!notification.disableUrl && addess + ? [ + { + name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL", + value: addess, + }, + ] + : []), + { + name: "Went Offline", + // F for full date/time + value: ``, + }, + { + name: `Time (${heartbeatJSON["timezone"]})`, + value: heartbeatJSON["localDateTime"], + }, + { + name: "Error", + value: heartbeatJSON["msg"] == null ? "N/A" : heartbeatJSON["msg"], + }, + ], + }, + ], + }; + if (!webhookHasAvatar) { + discorddowndata.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; + } + if (notification.discordChannelType === "createNewForumPost") { + discorddowndata.thread_name = notification.postName; + } + if (notification.discordPrefixMessage) { + discorddowndata.content = notification.discordPrefixMessage; + } + if (notification.discordSuppressNotifications) { + discorddowndata.flags = SUPPRESS_NOTIFICATIONS_FLAG; + } + + await axios.post(webhookUrl.toString(), discorddowndata, config); + return okMsg; + } else if (heartbeatJSON["status"] === UP) { + const backOnlineTimestamp = dayjs.utc(heartbeatJSON["time"]).unix(); + let downtimeDuration = null; + let wentOfflineTimestamp = null; + if (heartbeatJSON["lastDownTime"]) { + wentOfflineTimestamp = dayjs.utc(heartbeatJSON["lastDownTime"]).unix(); + downtimeDuration = this.formatDuration(backOnlineTimestamp - wentOfflineTimestamp); + } + + let discordupdata = { + username: discordDisplayName, + embeds: [ + { + title: "✅ Your service " + monitorJSON["name"] + " is up! ✅", + color: 65280, + timestamp: heartbeatJSON["time"], + fields: [ + { + name: "Service Name", + value: monitorJSON["name"], + }, + ...(!notification.disableUrl && addess + ? [ + { + name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL", + value: addess, + }, + ] + : []), + ...(wentOfflineTimestamp + ? [ + { + name: "Went Offline", + // F for full date/time + value: ``, + }, + ] + : []), + ...(downtimeDuration + ? [ + { + name: "Downtime Duration", + value: downtimeDuration, + }, + ] + : []), + // Show server timezone for parity with the DOWN notification embed + { + name: `Time (${heartbeatJSON["timezone"]})`, + value: heartbeatJSON["localDateTime"], + }, + ...(heartbeatJSON["ping"] != null + ? [ + { + name: "Ping", + value: heartbeatJSON["ping"] + " ms", + }, + ] + : []), + ], + }, + ], + }; + if (!webhookHasAvatar) { + discordupdata.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; + } + + if (notification.discordChannelType === "createNewForumPost") { + discordupdata.thread_name = notification.postName; + } + + if (notification.discordPrefixMessage) { + discordupdata.content = notification.discordPrefixMessage; + } + if (notification.discordSuppressNotifications) { + discordupdata.flags = SUPPRESS_NOTIFICATIONS_FLAG; + } + + await axios.post(webhookUrl.toString(), discordupdata, config); + return okMsg; + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Format duration as human-readable string (e.g., "1h 23m", "45m 30s") + * TODO: Update below to `Intl.DurationFormat("en", { style: "short" }).format(duration)` once we are on a newer node version + * @param {number} timeInSeconds The time in seconds to format a duration for + * @returns {string} The formatted duration + */ + formatDuration(timeInSeconds) { + const hours = Math.floor(timeInSeconds / 3600); + const minutes = Math.floor((timeInSeconds % 3600) / 60); + const seconds = timeInSeconds % 60; + + const durationParts = []; + if (hours > 0) { + durationParts.push(`${hours}h`); + } + if (minutes > 0) { + durationParts.push(`${minutes}m`); + } + if (seconds > 0 && hours === 0) { + // Only show seconds if less than an hour + durationParts.push(`${seconds}s`); + } + + return durationParts.length > 0 ? durationParts.join(" ") : "0s"; + } +} + +module.exports = Discord; diff --git a/server/notification-providers/egosms.js b/server/notification-providers/egosms.js new file mode 100644 index 0000000..884f912 --- /dev/null +++ b/server/notification-providers/egosms.js @@ -0,0 +1,34 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class EgoSMS extends NotificationProvider { + name = "egosms"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + const params = new URLSearchParams({ + number: notification.egosmsPhoneNumber, + message: msg, + username: notification.egosmsUsername, + password: notification.egosmsPassword, + sender: notification.egosmsSender || "EGOSMS", + priority: "0", + }); + + const url = `https://www.egosms.co/api/v1/plain/?${params.toString()}`; + let config = this.getAxiosConfigWithProxy({}); + const response = await axios.get(url, config); + + return response.data || okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = EgoSMS; diff --git a/server/notification-providers/evolution.js b/server/notification-providers/evolution.js new file mode 100644 index 0000000..b445514 --- /dev/null +++ b/server/notification-providers/evolution.js @@ -0,0 +1,48 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Evolution extends NotificationProvider { + name = "evolution"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + apikey: notification.evolutionAuthToken, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + let text = msg; + const customMessage = notification.evolutionCustomMessage; + if (notification.evolutionUseCustomMessage && typeof customMessage === "string" && customMessage.trim()) { + text = await this.renderTemplate(customMessage, msg, monitorJSON, heartbeatJSON); + } + + let data = { + number: notification.evolutionRecipient, + text, + }; + + let url = + (notification.evolutionApiUrl || "https://evolapicloud.com/").replace(/([^/])\/+$/, "$1") + + "/message/sendText/" + + encodeURIComponent(notification.evolutionInstanceName); + + await axios.post(url, data, config); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Evolution; diff --git a/server/notification-providers/feishu.js b/server/notification-providers/feishu.js new file mode 100644 index 0000000..a235050 --- /dev/null +++ b/server/notification-providers/feishu.js @@ -0,0 +1,105 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class Feishu extends NotificationProvider { + name = "Feishu"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = this.getAxiosConfigWithProxy({}); + if (heartbeatJSON == null) { + let testdata = { + msg_type: "text", + content: { + text: msg, + }, + }; + await axios.post(notification.feishuWebHookUrl, testdata, config); + return okMsg; + } + + if (heartbeatJSON["status"] === DOWN) { + let downdata = { + msg_type: "interactive", + card: { + config: { + update_multi: false, + wide_screen_mode: true, + }, + header: { + title: { + tag: "plain_text", + content: "UptimeKuma Alert: [Down] " + monitorJSON["name"], + }, + template: "red", + }, + elements: [ + { + tag: "div", + text: { + tag: "lark_md", + content: getContent(heartbeatJSON), + }, + }, + ], + }, + }; + await axios.post(notification.feishuWebHookUrl, downdata, config); + return okMsg; + } + + if (heartbeatJSON["status"] === UP) { + let updata = { + msg_type: "interactive", + card: { + config: { + update_multi: false, + wide_screen_mode: true, + }, + header: { + title: { + tag: "plain_text", + content: "UptimeKuma Alert: [UP] " + monitorJSON["name"], + }, + template: "green", + }, + elements: [ + { + tag: "div", + text: { + tag: "lark_md", + content: getContent(heartbeatJSON), + }, + }, + ], + }, + }; + await axios.post(notification.feishuWebHookUrl, updata, config); + return okMsg; + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +/** + * Get content + * @param {?object} heartbeatJSON Heartbeat details (For Up/Down only) + * @returns {string} Return Successful Message + */ +function getContent(heartbeatJSON) { + return [ + "**Message**: " + heartbeatJSON["msg"], + "**Ping**: " + (heartbeatJSON["ping"] == null ? "N/A" : heartbeatJSON["ping"] + " ms"), + `**Time (${heartbeatJSON["timezone"]})**: ${heartbeatJSON["localDateTime"]}`, + ].join("\n"); +} + +module.exports = Feishu; diff --git a/server/notification-providers/flashduty.js b/server/notification-providers/flashduty.js new file mode 100644 index 0000000..6a72387 --- /dev/null +++ b/server/notification-providers/flashduty.js @@ -0,0 +1,117 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); +const { Settings } = require("../settings"); +const successMessage = "Sent Successfully."; + +class FlashDuty extends NotificationProvider { + name = "FlashDuty"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + try { + if (heartbeatJSON == null) { + const title = "Uptime Kuma Alert"; + const monitor = { + type: "ping", + url: msg, + name: "https://flashcat.cloud", + }; + return this.postNotification(notification, title, msg, monitor); + } + + if (heartbeatJSON.status === UP) { + const title = "Uptime Kuma Monitor ✅ Up"; + + return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "Ok"); + } + + if (heartbeatJSON.status === DOWN) { + const title = "Uptime Kuma Monitor 🔴 Down"; + return this.postNotification( + notification, + title, + heartbeatJSON.msg, + monitorJSON, + notification.flashdutySeverity + ); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Generate a monitor url from the monitors information + * @param {object} monitorInfo Monitor details + * @returns {string|undefined} Monitor URL + */ + genMonitorUrl(monitorInfo) { + if (monitorInfo.type === "port" && monitorInfo.port) { + return monitorInfo.hostname + ":" + monitorInfo.port; + } + if (monitorInfo.hostname != null) { + return monitorInfo.hostname; + } + return monitorInfo.url; + } + + /** + * Send the message + * @param {BeanModel} notification Message title + * @param {string} title Message + * @param {string} body Message + * @param {object} monitorInfo Monitor details + * @param {string} eventStatus Monitor status (Info, Warning, Critical, Ok) + * @returns {string} Success message + */ + async postNotification(notification, title, body, monitorInfo, eventStatus) { + let labels = { + resource: this.genMonitorUrl(monitorInfo), + check: monitorInfo.name, + }; + if (monitorInfo.tags && monitorInfo.tags.length > 0) { + for (let tag of monitorInfo.tags) { + labels[tag.name] = tag.value; + } + } + const options = { + method: "POST", + url: notification.flashdutyIntegrationKey.startsWith("http") + ? notification.flashdutyIntegrationKey + : "https://api.flashcat.cloud/event/push/alert/standard?integration_key=" + + notification.flashdutyIntegrationKey, + headers: { "Content-Type": "application/json" }, + data: { + description: `[${title}] [${monitorInfo.name}] ${body}`, + title, + event_status: eventStatus || "Info", + alert_key: monitorInfo.id ? String(monitorInfo.id) : Math.random().toString(36).substring(7), + labels, + }, + }; + + const baseURL = await Settings.get("primaryBaseURL"); + if (baseURL && monitorInfo) { + options.client = "Uptime Kuma"; + options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id); + } + + let result = await axios.request(options); + if (result.status == null) { + throw new Error("FlashDuty notification failed with invalid response!"); + } + if (result.status < 200 || result.status >= 300) { + throw new Error("FlashDuty notification failed with status code " + result.status); + } + if (result.statusText != null) { + return "FlashDuty notification succeed: " + result.statusText; + } + + return successMessage; + } +} + +module.exports = FlashDuty; diff --git a/server/notification-providers/flowtriq.js b/server/notification-providers/flowtriq.js new file mode 100644 index 0000000..dcd27a9 --- /dev/null +++ b/server/notification-providers/flowtriq.js @@ -0,0 +1,79 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class Flowtriq extends NotificationProvider { + name = "Flowtriq"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let status = "info"; + let monitorName = monitorJSON?.name || "Unknown"; + + if (heartbeatJSON != null) { + if (heartbeatJSON.status === DOWN) { + status = "down"; + } else if (heartbeatJSON.status === UP) { + status = "up"; + } + } + + let data = { + source: "uptime-kuma", + status: status, + monitor: monitorName, + msg: msg, + }; + + if (heartbeatJSON) { + data.heartbeat = { + status: heartbeatJSON.status, + time: heartbeatJSON.time, + ping: heartbeatJSON.ping, + msg: heartbeatJSON.msg, + timezone: heartbeatJSON.timezone, + }; + } + + if (monitorJSON) { + data.monitorInfo = { + id: monitorJSON.id, + name: monitorJSON.name, + type: monitorJSON.type, + url: monitorJSON.url, + hostname: monitorJSON.hostname, + port: monitorJSON.port, + }; + } + + let headers = { + "Content-Type": "application/json", + }; + + if (notification.flowtriqApiKey) { + headers["X-API-Key"] = notification.flowtriqApiKey; + } + + let config = this.getAxiosConfigWithProxy({ + headers: headers, + }); + + let result = await axios.post(notification.flowtriqWebhookUrl, data, config); + + if (result.status < 200 || result.status >= 300) { + throw new Error("Flowtriq notification failed with status code " + result.status); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Flowtriq; diff --git a/server/notification-providers/fluxer.js b/server/notification-providers/fluxer.js new file mode 100644 index 0000000..421f774 --- /dev/null +++ b/server/notification-providers/fluxer.js @@ -0,0 +1,249 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class Fluxer extends NotificationProvider { + name = "fluxer"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = this.getAxiosConfigWithProxy({}); + const fluxerDisplayName = notification.fluxerUsername || "Uptime Kuma"; + const webhookUrl = new URL(notification.fluxerWebhookUrl); + + // Check if the webhook has an avatar + let webhookHasAvatar = true; + try { + const webhookInfo = await axios.get(webhookUrl.toString(), config); + webhookHasAvatar = !!webhookInfo.data.avatar; + } catch (e) { + // If we can't verify, we assume he has an avatar to avoid forcing the default avatar + webhookHasAvatar = true; + } + + const messageFormat = + notification.fluxerMessageFormat || (notification.fluxerUseMessageTemplate ? "custom" : "normal"); + + // If heartbeatJSON is null, assume we're testing. + if (heartbeatJSON == null) { + let content = msg; + if (messageFormat === "minimalist") { + content = "Test: " + msg; + } else if (messageFormat === "custom") { + const customMessage = notification.fluxerMessageTemplate?.trim() || ""; + if (customMessage !== "") { + content = await this.renderTemplate(customMessage, msg, monitorJSON, heartbeatJSON); + } + } + let fluxertestdata = { + username: fluxerDisplayName, + content: content, + }; + if (!webhookHasAvatar) { + fluxertestdata.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; + } + await axios.post(webhookUrl.toString(), fluxertestdata, config); + return okMsg; + } + + // If heartbeatJSON is not null, we go into the normal alerting loop. + let addess = this.extractAddress(monitorJSON); + + // Minimalist: status + name only (is down / is up; no "back up" — may be first trigger) + if (messageFormat === "minimalist") { + const content = + heartbeatJSON["status"] === DOWN + ? "🔴 " + monitorJSON["name"] + " is down." + : "🟢 " + monitorJSON["name"] + " is up."; + let payload = { + username: fluxerDisplayName, + content: content, + }; + if (!webhookHasAvatar) { + payload.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; + } + + await axios.post(webhookUrl.toString(), payload, config); + return okMsg; + } + + // Custom template: send only content (no embeds) + const useCustomTemplate = + messageFormat === "custom" && (notification.fluxerMessageTemplate?.trim() || "") !== ""; + if (useCustomTemplate) { + const content = await this.renderTemplate( + notification.fluxerMessageTemplate.trim(), + msg, + monitorJSON, + heartbeatJSON + ); + let payload = { + username: fluxerDisplayName, + content: content, + }; + if (!webhookHasAvatar) { + payload.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; + } + + await axios.post(webhookUrl.toString(), payload, config); + return okMsg; + } + + if (heartbeatJSON["status"] === DOWN) { + const wentOfflineTimestamp = Math.floor(new Date(heartbeatJSON["time"]).getTime() / 1000); + + let fluxerdowndata = { + username: fluxerDisplayName, + embeds: [ + { + title: "❌ Your service " + monitorJSON["name"] + " went down. ❌", + color: 16711680, + fields: [ + { + name: "Service Name", + value: monitorJSON["name"], + }, + ...(!notification.disableUrl && addess + ? [ + { + name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL", + value: addess, + }, + ] + : []), + { + name: "Went Offline", + // F for full date/time + value: ``, + }, + { + name: `Time (${heartbeatJSON["timezone"]})`, + value: heartbeatJSON["localDateTime"], + }, + { + name: "Error", + value: heartbeatJSON["msg"] == null ? "N/A" : heartbeatJSON["msg"], + }, + ], + }, + ], + }; + if (!webhookHasAvatar) { + fluxerdowndata.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; + } + if (notification.fluxerPrefixMessage) { + fluxerdowndata.content = notification.fluxerPrefixMessage; + } + + await axios.post(webhookUrl.toString(), fluxerdowndata, config); + return okMsg; + } else if (heartbeatJSON["status"] === UP) { + const backOnlineTimestamp = Math.floor(new Date(heartbeatJSON["time"]).getTime() / 1000); + let downtimeDuration = null; + let wentOfflineTimestamp = null; + if (heartbeatJSON["lastDownTime"]) { + wentOfflineTimestamp = Math.floor(new Date(heartbeatJSON["lastDownTime"]).getTime() / 1000); + downtimeDuration = this.formatDuration(backOnlineTimestamp - wentOfflineTimestamp); + } + + let fluxerupdata = { + username: fluxerDisplayName, + embeds: [ + { + title: "✅ Your service " + monitorJSON["name"] + " is up! ✅", + color: 65280, + fields: [ + { + name: "Service Name", + value: monitorJSON["name"], + }, + ...(!notification.disableUrl && addess + ? [ + { + name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL", + value: addess, + }, + ] + : []), + ...(wentOfflineTimestamp + ? [ + { + name: "Went Offline", + // F for full date/time + value: ``, + }, + ] + : []), + ...(downtimeDuration + ? [ + { + name: "Downtime Duration", + value: downtimeDuration, + }, + ] + : []), + // Show server timezone for parity with the DOWN notification embed + { + name: `Time (${heartbeatJSON["timezone"]})`, + value: heartbeatJSON["localDateTime"], + }, + ...(heartbeatJSON["ping"] != null + ? [ + { + name: "Ping", + value: heartbeatJSON["ping"] + " ms", + }, + ] + : []), + ], + }, + ], + }; + if (!webhookHasAvatar) { + fluxerupdata.avatar_url = "https://github.com/louislam/uptime-kuma/raw/master/public/icon.png"; + } + if (notification.fluxerPrefixMessage) { + fluxerupdata.content = notification.fluxerPrefixMessage; + } + + await axios.post(webhookUrl.toString(), fluxerupdata, config); + return okMsg; + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Format duration as human-readable string (e.g., "1h 23m", "45m 30s") + * TODO: Update below to `Intl.DurationFormat("en", { style: "short" }).format(duration)` once we are on a newer node version + * @param {number} timeInSeconds The time in seconds to format a duration for + * @returns {string} The formatted duration + */ + formatDuration(timeInSeconds) { + const hours = Math.floor(timeInSeconds / 3600); + const minutes = Math.floor((timeInSeconds % 3600) / 60); + const seconds = timeInSeconds % 60; + + const durationParts = []; + if (hours > 0) { + durationParts.push(`${hours}h`); + } + if (minutes > 0) { + durationParts.push(`${minutes}m`); + } + if (seconds > 0 && hours === 0) { + // Only show seconds if less than an hour + durationParts.push(`${seconds}s`); + } + + return durationParts.length > 0 ? durationParts.join(" ") : "0s"; + } +} + +module.exports = Fluxer; diff --git a/server/notification-providers/freemobile.js b/server/notification-providers/freemobile.js new file mode 100644 index 0000000..b988542 --- /dev/null +++ b/server/notification-providers/freemobile.js @@ -0,0 +1,31 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class FreeMobile extends NotificationProvider { + name = "FreeMobile"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = this.getAxiosConfigWithProxy({}); + await axios.post( + `https://smsapi.free-mobile.fr/sendmsg?msg=${encodeURIComponent(msg.replace("🔴", "⛔️"))}`, + { + user: notification.freemobileUser, + pass: notification.freemobilePass, + }, + config + ); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = FreeMobile; diff --git a/server/notification-providers/goalert.js b/server/notification-providers/goalert.js new file mode 100644 index 0000000..4285126 --- /dev/null +++ b/server/notification-providers/goalert.js @@ -0,0 +1,41 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP } = require("../../src/util"); + +class GoAlert extends NotificationProvider { + name = "GoAlert"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let data = { + summary: msg, + }; + if (heartbeatJSON != null && heartbeatJSON["status"] === UP) { + data["action"] = "close"; + } + let headers = { + "Content-Type": "multipart/form-data", + }; + let config = { + headers: headers, + }; + config = this.getAxiosConfigWithProxy(config); + await axios.post( + `${notification.goAlertBaseURL}/api/v2/generic/incoming?token=${notification.goAlertToken}`, + data, + config + ); + return okMsg; + } catch (error) { + let msg = error.response.data ? error.response.data : "Error without response"; + throw new Error(msg); + } + } +} + +module.exports = GoAlert; diff --git a/server/notification-providers/google-chat.js b/server/notification-providers/google-chat.js new file mode 100644 index 0000000..5c15c71 --- /dev/null +++ b/server/notification-providers/google-chat.js @@ -0,0 +1,140 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { getMonitorRelativeURL, UP } = require("../../src/util"); +const { Settings } = require("../settings"); + +class GoogleChat extends NotificationProvider { + name = "GoogleChat"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + // If Google Chat Webhook rate limit is reached, retry to configured max retries defaults to 3, delay between 60-180 seconds + const post = async (url, data, config) => { + let retries = notification.googleChatMaxRetries || 1; // Default to 1 retries + retries = retries > 10 ? 10 : retries; // Enforce maximum retries in backend + while (retries > 0) { + try { + await axios.post(url, data, config); + return; + } catch (error) { + if (error.response && error.response.status === 429) { + retries--; + if (retries === 0) { + throw error; + } + const delay = 60000 + Math.random() * 120000; + await new Promise((resolve) => setTimeout(resolve, delay)); + } else { + throw error; + } + } + } + }; + + try { + let config = this.getAxiosConfigWithProxy({}); + // Google Chat message formatting: https://developers.google.com/chat/api/guides/message-formats/basic + if (notification.googleChatUseTemplate && notification.googleChatTemplate) { + // Send message using template + const renderedText = await this.renderTemplate( + notification.googleChatTemplate, + msg, + monitorJSON, + heartbeatJSON + ); + const data = { text: renderedText }; + await post(notification.googleChatWebhookURL, data, config); + return okMsg; + } + + let chatHeader = { + title: "Uptime Kuma Alert", + }; + + if (monitorJSON && heartbeatJSON) { + chatHeader["title"] = + heartbeatJSON["status"] === UP + ? `✅ ${monitorJSON["name"]} is back online` + : `🔴 ${monitorJSON["name"]} went down`; + } + + // always show msg + let sectionWidgets = [ + { + textParagraph: { + text: `Message:\n${msg}`, + }, + }, + ]; + + // add time if available + if (heartbeatJSON) { + sectionWidgets.push({ + textParagraph: { + text: `Time (${heartbeatJSON["timezone"]}):\n${heartbeatJSON["localDateTime"]}`, + }, + }); + } + + // add monitor address if available + const address = this.extractAddress(monitorJSON); + if (address) { + sectionWidgets.push({ + textParagraph: { + text: `Address:\n${address}`, + }, + }); + } + + // add button for monitor link if available + const baseURL = await Settings.get("primaryBaseURL"); + if (baseURL) { + const urlPath = monitorJSON ? getMonitorRelativeURL(monitorJSON.id) : "/"; + sectionWidgets.push({ + buttonList: { + buttons: [ + { + text: "Visit Uptime Kuma", + onClick: { + openLink: { + url: baseURL + urlPath, + }, + }, + }, + ], + }, + }); + } + + let chatSections = [ + { + widgets: sectionWidgets, + }, + ]; + + // construct json data + let data = { + fallbackText: chatHeader["title"], + cardsV2: [ + { + card: { + header: chatHeader, + sections: chatSections, + }, + }, + ], + }; + + await post(notification.googleChatWebhookURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = GoogleChat; diff --git a/server/notification-providers/google-sheets.js b/server/notification-providers/google-sheets.js new file mode 100644 index 0000000..6220e27 --- /dev/null +++ b/server/notification-providers/google-sheets.js @@ -0,0 +1,60 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class GoogleSheets extends NotificationProvider { + name = "GoogleSheets"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + // Prepare the data to be logged + const timestamp = new Date().toISOString(); + let status = "N/A"; + let monitorName = "N/A"; + let monitorUrl = "N/A"; + let responseTime = "N/A"; + let statusCode = "N/A"; + + if (monitorJSON) { + monitorName = monitorJSON.name || "N/A"; + monitorUrl = this.extractAddress(monitorJSON) || "N/A"; + } + + if (heartbeatJSON) { + status = heartbeatJSON.status === DOWN ? "DOWN" : heartbeatJSON.status === UP ? "UP" : "UNKNOWN"; + responseTime = heartbeatJSON.ping || "N/A"; + statusCode = heartbeatJSON.statusCode || "N/A"; + } + + // Send data to Google Apps Script webhook + const config = this.getAxiosConfigWithProxy({ + headers: { + "Content-Type": "application/json", + }, + }); + + const data = { + timestamp, + status, + monitorName, + monitorUrl, + message: msg, + responseTime, + statusCode, + }; + + await axios.post(notification.googleSheetsWebhookUrl, data, config); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = GoogleSheets; diff --git a/server/notification-providers/gorush.js b/server/notification-providers/gorush.js new file mode 100644 index 0000000..193595a --- /dev/null +++ b/server/notification-providers/gorush.js @@ -0,0 +1,43 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Gorush extends NotificationProvider { + name = "gorush"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + let platformMapping = { + ios: 1, + android: 2, + huawei: 3, + }; + + try { + let data = { + notifications: [ + { + tokens: [notification.gorushDeviceToken], + platform: platformMapping[notification.gorushPlatform], + message: msg, + // Optional + title: notification.gorushTitle, + priority: notification.gorushPriority, + retry: parseInt(notification.gorushRetry) || 0, + topic: notification.gorushTopic, + }, + ], + }; + let config = this.getAxiosConfigWithProxy({}); + await axios.post(`${notification.gorushServerURL}/api/push`, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Gorush; diff --git a/server/notification-providers/gotify.js b/server/notification-providers/gotify.js new file mode 100644 index 0000000..40437d0 --- /dev/null +++ b/server/notification-providers/gotify.js @@ -0,0 +1,35 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Gotify extends NotificationProvider { + name = "gotify"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = this.getAxiosConfigWithProxy({}); + if (notification.gotifyserverurl && notification.gotifyserverurl.endsWith("/")) { + notification.gotifyserverurl = notification.gotifyserverurl.slice(0, -1); + } + await axios.post( + `${notification.gotifyserverurl}/message?token=${notification.gotifyapplicationToken}`, + { + message: msg, + priority: notification.gotifyPriority || 8, + title: "Uptime-Kuma", + }, + config + ); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Gotify; diff --git a/server/notification-providers/grafana-oncall.js b/server/notification-providers/grafana-oncall.js new file mode 100644 index 0000000..70b9b92 --- /dev/null +++ b/server/notification-providers/grafana-oncall.js @@ -0,0 +1,51 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class GrafanaOncall extends NotificationProvider { + name = "GrafanaOncall"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + if (!notification.GrafanaOncallURL) { + throw new Error("GrafanaOncallURL cannot be empty"); + } + + try { + let config = this.getAxiosConfigWithProxy({}); + if (heartbeatJSON === null) { + let grafanaupdata = { + title: "General notification", + message: msg, + state: "alerting", + }; + await axios.post(notification.GrafanaOncallURL, grafanaupdata, config); + return okMsg; + } else if (heartbeatJSON["status"] === DOWN) { + let grafanadowndata = { + title: monitorJSON["name"] + " is down", + message: heartbeatJSON["msg"], + state: "alerting", + }; + await axios.post(notification.GrafanaOncallURL, grafanadowndata, config); + return okMsg; + } else if (heartbeatJSON["status"] === UP) { + let grafanaupdata = { + title: monitorJSON["name"] + " is up", + message: heartbeatJSON["msg"], + state: "ok", + }; + await axios.post(notification.GrafanaOncallURL, grafanaupdata, config); + return okMsg; + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = GrafanaOncall; diff --git a/server/notification-providers/gtx-messaging.js b/server/notification-providers/gtx-messaging.js new file mode 100644 index 0000000..f8715c1 --- /dev/null +++ b/server/notification-providers/gtx-messaging.js @@ -0,0 +1,34 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class GtxMessaging extends NotificationProvider { + name = "gtxmessaging"; + + /** + * @inheritDoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + // The UP/DOWN symbols will be replaced with `???` by gtx-messaging + const text = msg.replaceAll("🔴 ", "").replaceAll("✅ ", ""); + + try { + let config = this.getAxiosConfigWithProxy({}); + const data = new URLSearchParams(); + data.append("from", notification.gtxMessagingFrom.trim()); + data.append("to", notification.gtxMessagingTo.trim()); + data.append("text", text); + + const url = `https://rest.gtx-messaging.net/smsc/sendsms/${notification.gtxMessagingApiKey}/json`; + + await axios.post(url, data, config); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = GtxMessaging; diff --git a/server/notification-providers/heii-oncall.js b/server/notification-providers/heii-oncall.js new file mode 100644 index 0000000..1e66f0d --- /dev/null +++ b/server/notification-providers/heii-oncall.js @@ -0,0 +1,53 @@ +const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); +const { setting } = require("../util-server"); + +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +class HeiiOnCall extends NotificationProvider { + name = "HeiiOnCall"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const payload = heartbeatJSON || {}; + + const baseURL = await setting("primaryBaseURL"); + if (baseURL && monitorJSON) { + payload["url"] = baseURL + getMonitorRelativeURL(monitorJSON.id); + } + + let config = { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + Authorization: "Bearer " + notification.heiiOnCallApiKey, + }, + }; + const heiiUrl = `https://heiioncall.com/triggers/${notification.heiiOnCallTriggerId}/`; + // docs https://heiioncall.com/docs#manual-triggers + try { + config = this.getAxiosConfigWithProxy(config); + if (!heartbeatJSON) { + // Testing or general notification like certificate expiry + payload["msg"] = msg; + await axios.post(heiiUrl + "alert", payload, config); + return okMsg; + } + + if (heartbeatJSON.status === DOWN) { + await axios.post(heiiUrl + "alert", payload, config); + return okMsg; + } + if (heartbeatJSON.status === UP) { + await axios.post(heiiUrl + "resolve", payload, config); + return okMsg; + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = HeiiOnCall; diff --git a/server/notification-providers/home-assistant.js b/server/notification-providers/home-assistant.js new file mode 100644 index 0000000..433abad --- /dev/null +++ b/server/notification-providers/home-assistant.js @@ -0,0 +1,49 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +const defaultNotificationService = "notify"; + +class HomeAssistant extends NotificationProvider { + name = "HomeAssistant"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + const notificationService = notification?.notificationService || defaultNotificationService; + + try { + let config = { + headers: { + Authorization: `Bearer ${notification.longLivedAccessToken}`, + "Content-Type": "application/json", + }, + }; + config = this.getAxiosConfigWithProxy(config); + await axios.post( + `${notification.homeAssistantUrl.trim().replace(/\/*$/, "")}/api/services/notify/${notificationService}`, + { + title: "Uptime Kuma", + message: msg, + ...(notificationService !== "persistent_notification" && { + data: { + name: monitorJSON?.name, + status: heartbeatJSON?.status, + channel: "Uptime Kuma", + icon_url: "https://github.com/louislam/uptime-kuma/blob/master/public/icon.png?raw=true", + }, + }), + }, + config + ); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = HomeAssistant; diff --git a/server/notification-providers/indigo.js b/server/notification-providers/indigo.js new file mode 100644 index 0000000..2e759ce --- /dev/null +++ b/server/notification-providers/indigo.js @@ -0,0 +1,67 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const https = require("https"); + +class Indigo extends NotificationProvider { + name = "Indigo"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = `${notification.indigoUrl.trim().replace(/\/*$/, "")}/v2/api/command`; + const variableId = parseInt(notification.indigoVariableId); + const actionGroupId = parseInt(notification.indigoActionGroupId); + + if (!variableId && !actionGroupId) { + throw new Error("Set an Indigo variable ID, an action group ID, or both."); + } + + // The variable is written first so the action group can read it. + const commands = []; + if (variableId) { + commands.push({ + message: "indigo.variable.updateValue", + objectId: variableId, + parameters: { value: msg }, + }); + } + if (actionGroupId) { + commands.push({ + message: "indigo.actionGroup.execute", + objectId: actionGroupId, + }); + } + + try { + let config = { + headers: { + Authorization: `Bearer ${notification.indigoApiKey}`, + "Content-Type": "application/json", + }, + }; + // The Indigo Web Server on a LAN uses a self-signed certificate; + // the Reflector (NAME.indigodomo.net) has a valid one. + if (notification.indigoIgnoreTlsError) { + config.httpsAgent = new https.Agent({ rejectUnauthorized: false }); + } + config = this.getAxiosConfigWithProxy(config); + + for (const command of commands) { + const result = await axios.post(url, { id: "uptime-kuma", ...command }, config); + if (result.data?.error || result.data?.validationErrors) { + throw new Error( + `Indigo rejected ${command.message}: ${result.data.error || JSON.stringify(result.data.validationErrors)}` + ); + } + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Indigo; diff --git a/server/notification-providers/jira-service-management.js b/server/notification-providers/jira-service-management.js new file mode 100644 index 0000000..578224c --- /dev/null +++ b/server/notification-providers/jira-service-management.js @@ -0,0 +1,105 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP, DOWN } = require("../../src/util"); + +const okMsg = "Sent Successfully."; + +class JiraServiceManagement extends NotificationProvider { + name = "JiraServiceManagement"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const priority = notification.jsmPriority || 3; + const baseUrl = `https://api.atlassian.com/jsm/ops/api/${notification.jsmCloudId}/v1`; + const textMsg = "Uptime Kuma Alert"; + + try { + if (heartbeatJSON == null) { + // Test notification + let notificationTestAlias = "uptime-kuma-notification-test"; + let data = { + message: msg, + alias: notificationTestAlias, + source: "Uptime Kuma", + priority: "P5", + tags: ["Uptime Kuma"], + }; + + return this.post(notification, `${baseUrl}/alerts`, data); + } + + if (heartbeatJSON.status === DOWN) { + let data = { + message: monitorJSON ? `${textMsg}: ${monitorJSON.name}` : textMsg, + alias: monitorJSON.name, + description: msg, + source: "Uptime Kuma", + priority: `P${priority}`, + tags: ["Uptime Kuma"], + }; + + return this.post(notification, `${baseUrl}/alerts`, data); + } + + if (heartbeatJSON.status === UP) { + // JSM requires getting the alert ID first, then closing by ID + const getUrl = `${baseUrl}/alerts/alias?alias=${encodeURIComponent(monitorJSON.name)}`; + const config = this.getConfig(notification); + + let alertResponse = await axios.get(getUrl, config); + const alertId = alertResponse.data.id; + + const closeUrl = `${baseUrl}/alerts/${alertId}/close`; + let data = { + source: "Uptime Kuma", + }; + + return this.post(notification, closeUrl, data); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Get axios config with Basic Auth for JSM + * @param {BeanModel} notification Notification details + * @returns {object} Axios config object + */ + getConfig(notification) { + const authToken = Buffer.from(`${notification.jsmEmail}:${notification.jsmApiToken}`).toString("base64"); + let config = { + headers: { + "Content-Type": "application/json", + Accept: "application/json", + Authorization: `Basic ${authToken}`, + }, + }; + return this.getAxiosConfigWithProxy(config); + } + + /** + * Make POST request to Jira Service Management + * @param {BeanModel} notification Notification to send + * @param {string} url Request url + * @param {object} data Request body + * @returns {Promise} Success message + */ + async post(notification, url, data) { + let config = this.getConfig(notification); + + let res = await axios.post(url, data, config); + if (res.status == null) { + return "Jira Service Management notification failed with invalid response!"; + } + if (res.status < 200 || res.status >= 300) { + return `Jira Service Management notification failed with status code ${res.status}`; + } + + return okMsg; + } +} + +module.exports = JiraServiceManagement; diff --git a/server/notification-providers/keep.js b/server/notification-providers/keep.js new file mode 100644 index 0000000..69740a2 --- /dev/null +++ b/server/notification-providers/keep.js @@ -0,0 +1,44 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Keep extends NotificationProvider { + name = "Keep"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let data = { + heartbeat: heartbeatJSON, + monitor: monitorJSON, + msg, + }; + let config = { + headers: { + "x-api-key": notification.webhookAPIKey, + "content-type": "application/json", + }, + }; + + let url = notification.webhookURL; + + if (url.endsWith("/")) { + url = url.slice(0, -1); + } + + let webhookURL = url + "/alerts/event/uptimekuma"; + + config = this.getAxiosConfigWithProxy(config); + + await axios.post(webhookURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Keep; diff --git a/server/notification-providers/kook.js b/server/notification-providers/kook.js new file mode 100644 index 0000000..d1f3b93 --- /dev/null +++ b/server/notification-providers/kook.js @@ -0,0 +1,34 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Kook extends NotificationProvider { + name = "Kook"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://www.kookapp.cn/api/v3/message/create"; + + let data = { + target_id: notification.kookGuildID, + content: msg, + }; + let config = { + headers: { + Authorization: "Bot " + notification.kookBotToken, + "Content-Type": "application/json", + }, + }; + try { + config = this.getAxiosConfigWithProxy(config); + await axios.post(url, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Kook; diff --git a/server/notification-providers/line.js b/server/notification-providers/line.js new file mode 100644 index 0000000..40f3316 --- /dev/null +++ b/server/notification-providers/line.js @@ -0,0 +1,76 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class Line extends NotificationProvider { + name = "line"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://api.line.me/v2/bot/message/push"; + + try { + let config = { + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + notification.lineChannelAccessToken, + }, + }; + config = this.getAxiosConfigWithProxy(config); + if (heartbeatJSON == null) { + let testMessage = { + to: notification.lineUserID, + messages: [ + { + type: "text", + text: "Test Successful!", + }, + ], + }; + await axios.post(url, testMessage, config); + } else if (heartbeatJSON["status"] === DOWN) { + let downMessage = { + to: notification.lineUserID, + messages: [ + { + type: "text", + text: + "UptimeKuma Alert: [🔴 Down]\n" + + "Name: " + + monitorJSON["name"] + + " \n" + + heartbeatJSON["msg"] + + `\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`, + }, + ], + }; + await axios.post(url, downMessage, config); + } else if (heartbeatJSON["status"] === UP) { + let upMessage = { + to: notification.lineUserID, + messages: [ + { + type: "text", + text: + "UptimeKuma Alert: [✅ Up]\n" + + "Name: " + + monitorJSON["name"] + + " \n" + + heartbeatJSON["msg"] + + `\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`, + }, + ], + }; + await axios.post(url, upMessage, config); + } + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Line; diff --git a/server/notification-providers/lunasea.js b/server/notification-providers/lunasea.js new file mode 100644 index 0000000..f579634 --- /dev/null +++ b/server/notification-providers/lunasea.js @@ -0,0 +1,68 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class LunaSea extends NotificationProvider { + name = "lunasea"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://notify.lunasea.app/v1"; + + try { + let config = this.getAxiosConfigWithProxy({}); + const target = this.getTarget(notification); + if (heartbeatJSON == null) { + let testdata = { + title: "Uptime Kuma Alert", + body: msg, + }; + await axios.post(`${url}/custom/${target}`, testdata, config); + return okMsg; + } + + if (heartbeatJSON["status"] === DOWN) { + let downdata = { + title: "UptimeKuma Alert: " + monitorJSON["name"], + body: + "[🔴 Down] " + + heartbeatJSON["msg"] + + `\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`, + }; + await axios.post(`${url}/custom/${target}`, downdata, config); + return okMsg; + } + + if (heartbeatJSON["status"] === UP) { + let updata = { + title: "UptimeKuma Alert: " + monitorJSON["name"], + body: + "[✅ Up] " + + heartbeatJSON["msg"] + + `\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`, + }; + await axios.post(`${url}/custom/${target}`, updata, config); + return okMsg; + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Generates the lunasea target to send the notification to + * @param {BeanModel} notification Notification details + * @returns {string} The target to send the notification to + */ + getTarget(notification) { + if (notification.lunaseaTarget === "user") { + return "user/" + notification.lunaseaUserID; + } + return "device/" + notification.lunaseaDevice; + } +} + +module.exports = LunaSea; diff --git a/server/notification-providers/matrix.js b/server/notification-providers/matrix.js new file mode 100644 index 0000000..2e1ab22 --- /dev/null +++ b/server/notification-providers/matrix.js @@ -0,0 +1,52 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const Crypto = require("crypto"); +const { log } = require("../../src/util"); + +class Matrix extends NotificationProvider { + name = "matrix"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + const size = 20; + const randomString = encodeURIComponent(Crypto.randomBytes(size).toString("base64").slice(0, size)); + + log.debug("notification", "Random String: " + randomString); + + const roomId = encodeURIComponent(notification.internalRoomId); + + log.debug("notification", "Matrix Room ID: " + roomId); + + try { + let config = { + headers: { + Authorization: `Bearer ${notification.accessToken}`, + }, + }; + let data = { + msgtype: "m.text", + body: msg, + }; + + if (notification.matrixUseTemplate) { + data.body = await this.renderTemplate(notification.matrixTemplate, msg, monitorJSON, heartbeatJSON); + } + + config = this.getAxiosConfigWithProxy(config); + await axios.put( + `${notification.homeserverUrl}/_matrix/client/r0/rooms/${roomId}/send/m.room.message/${randomString}`, + data, + config + ); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Matrix; diff --git a/server/notification-providers/mattermost.js b/server/notification-providers/mattermost.js new file mode 100644 index 0000000..d1e4485 --- /dev/null +++ b/server/notification-providers/mattermost.js @@ -0,0 +1,103 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class Mattermost extends NotificationProvider { + name = "mattermost"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = this.getAxiosConfigWithProxy({}); + const mattermostUserName = notification.mattermostusername || "Uptime Kuma"; + // If heartbeatJSON is null, assume non monitoring notification (Certificate warning) or testing. + if (heartbeatJSON == null) { + let mattermostTestData = { + username: mattermostUserName, + text: msg, + }; + await axios.post(notification.mattermostWebhookUrl, mattermostTestData, config); + return okMsg; + } + + let mattermostChannel; + + if (typeof notification.mattermostchannel === "string") { + mattermostChannel = notification.mattermostchannel.toLowerCase(); + } + + const mattermostIconEmoji = notification.mattermosticonemo; + let mattermostIconEmojiOnline = ""; + let mattermostIconEmojiOffline = ""; + + if (mattermostIconEmoji && typeof mattermostIconEmoji === "string") { + const emojiArray = mattermostIconEmoji.split(" "); + if (emojiArray.length >= 2) { + mattermostIconEmojiOnline = emojiArray[0]; + mattermostIconEmojiOffline = emojiArray[1]; + } + } + const mattermostIconUrl = notification.mattermosticonurl; + let iconEmoji = mattermostIconEmoji; + let statusField = { + short: false, + title: "Error", + value: heartbeatJSON.msg, + }; + let statusText = "unknown"; + let color = "#000000"; + if (heartbeatJSON.status === DOWN) { + iconEmoji = mattermostIconEmojiOffline || mattermostIconEmoji; + statusField = { + short: false, + title: "Error", + value: heartbeatJSON.msg, + }; + statusText = "down."; + color = "#FF0000"; + } else if (heartbeatJSON.status === UP) { + iconEmoji = mattermostIconEmojiOnline || mattermostIconEmoji; + statusField = { + short: false, + title: "Ping", + value: heartbeatJSON.ping + "ms", + }; + statusText = "up!"; + color = "#32CD32"; + } + + let mattermostdata = { + username: monitorJSON.name + " " + mattermostUserName, + channel: mattermostChannel, + icon_emoji: iconEmoji, + icon_url: mattermostIconUrl, + attachments: [ + { + fallback: "Your " + monitorJSON.pathName + " service went " + statusText, + color: color, + title: monitorJSON.pathName + " service went " + statusText, + title_link: monitorJSON.url, + fields: [ + statusField, + { + short: true, + title: `Time (${heartbeatJSON["timezone"]})`, + value: heartbeatJSON.localDateTime, + }, + ], + }, + ], + }; + await axios.post(notification.mattermostWebhookUrl, mattermostdata, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Mattermost; diff --git a/server/notification-providers/max.js b/server/notification-providers/max.js new file mode 100644 index 0000000..4bf0ed3 --- /dev/null +++ b/server/notification-providers/max.js @@ -0,0 +1,47 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Max extends NotificationProvider { + name = "max"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const baseUrl = (notification.maxApiUrl || "https://platform-api.max.ru").replace(/\/$/, ""); + const chatId = notification.maxChatID; + + try { + const config = this.getAxiosConfigWithProxy({ + headers: { + Authorization: notification.maxBotToken, + "Content-Type": "application/json", + }, + }); + + const body = { + text: msg, + }; + + if (notification.maxUseTemplate && notification.maxTemplate) { + const rendered = await this.renderTemplate(notification.maxTemplate, msg, monitorJSON, heartbeatJSON); + + body.text = rendered; + + if (notification.maxTemplateFormat && notification.maxTemplateFormat !== "plain") { + body.format = notification.maxTemplateFormat; + } + } + + const url = `${baseUrl}/messages?chat_id=${encodeURIComponent(chatId)}`; + await axios.post(url, body, config); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Max; diff --git a/server/notification-providers/milky.js b/server/notification-providers/milky.js new file mode 100644 index 0000000..f0c79a3 --- /dev/null +++ b/server/notification-providers/milky.js @@ -0,0 +1,57 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Milky extends NotificationProvider { + name = "Milky"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let url = notification.httpAddr; + if (!url.startsWith("http")) { + url = "http://" + url; + } + if (!url.endsWith("/")) { + url += "/api/"; + } + if (notification.msgType === "group") { + url += "send_group_message"; + } else { + url += "send_private_message"; + } + let config = { + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + notification.accessToken, + }, + }; + config = this.getAxiosConfigWithProxy(config); + let pushText = "UptimeKuma Alert: " + msg; + let data = { + message: [ + { + type: "text", + data: { + text: pushText, + }, + }, + ], + }; + if (notification.msgType === "group") { + data["group_id"] = notification.recieverId; + } else { + data["user_id"] = notification.recieverId; + } + await axios.post(url, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Milky; diff --git a/server/notification-providers/nextcloudtalk.js b/server/notification-providers/nextcloudtalk.js new file mode 100644 index 0000000..6ebe3bd --- /dev/null +++ b/server/notification-providers/nextcloudtalk.js @@ -0,0 +1,60 @@ +const { UP, DOWN } = require("../../src/util"); +const Crypto = require("crypto"); + +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class NextcloudTalk extends NotificationProvider { + name = "nextcloudtalk"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + // See documentation at https://nextcloud-talk.readthedocs.io/en/latest/bots/#sending-a-chat-message + const okMsg = "Sent Successfully."; + + // Create a random string + const talkRandom = encodeURIComponent(Crypto.randomBytes(64).toString("hex").slice(0, 64)); + + // Create the signature over random and message + const talkSignature = Crypto.createHmac("sha256", Buffer.from(notification.botSecret, "utf8")) + .update(Buffer.from(`${talkRandom}${msg}`, "utf8")) + .digest("hex"); + + let silentUp = heartbeatJSON?.status === UP && notification.sendSilentUp; + let silentDown = heartbeatJSON?.status === DOWN && notification.sendSilentDown; + let silent = silentUp || silentDown; + + let url = `${notification.host}/ocs/v2.php/apps/spreed/api/v1/bot/${notification.conversationToken}/message`; + let config = this.getAxiosConfigWithProxy({}); + + const data = { + message: msg, + silent, + }; + + const options = { + ...config, + headers: { + "X-Nextcloud-Talk-Bot-Random": talkRandom, + "X-Nextcloud-Talk-Bot-Signature": talkSignature, + "OCS-APIRequest": true, + }, + }; + + try { + let result = await axios.post(url, data, options); + + if (result?.status === 201) { + return okMsg; + } + + throw new Error("Nextcloud Talk Error " + (result?.status ?? "Unknown")); + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = NextcloudTalk; diff --git a/server/notification-providers/nostr.js b/server/notification-providers/nostr.js new file mode 100644 index 0000000..1dc1e08 --- /dev/null +++ b/server/notification-providers/nostr.js @@ -0,0 +1,115 @@ +const NotificationProvider = require("./notification-provider"); +const { finalizeEvent, Relay, nip19, nip59 } = require("nostr-tools"); + +// polyfill WebSocket for nostr-tools +global.WebSocket = require("isomorphic-ws"); + +class Nostr extends NotificationProvider { + name = "nostr"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const senderPrivateKey = await this.getPrivateKey(notification.sender); + const recipientsPublicKeys = await this.getPublicKeys(notification.recipients); + + // Create NIP-59 gift-wrapped events for each recipient + // This uses NIP-17 kind 14 (private direct message) wrapped with NIP-59 + // to prevent metadata leakage (sender/recipient public keys are hidden) + const createdAt = Math.floor(Date.now() / 1000); + const events = []; + for (const recipientPublicKey of recipientsPublicKeys) { + const event = { + kind: 14, // NIP-17 private direct message + created_at: createdAt, + tags: [["p", recipientPublicKey]], + content: msg, + }; + try { + const wrappedEvent = nip59.wrapEvent(event, senderPrivateKey, recipientPublicKey); + events.push(wrappedEvent); + } catch (error) { + throw new Error(`Failed to create gift-wrapped event for recipient: ${error.message}`); + } + } + + // Publish events to each relay + const relays = notification.relays.split("\n"); + let successfulRelays = 0; + for (const relayUrl of relays) { + const relay = await Relay.connect(relayUrl); + let eventIndex = 0; + + // Authenticate to the relay, if required + try { + await relay.publish(events[0]); + eventIndex = 1; + } catch (error) { + if (relay.challenge) { + await relay.auth(async (evt) => { + return finalizeEvent(evt, senderPrivateKey); + }); + } + } + + try { + for (let i = eventIndex; i < events.length; i++) { + await relay.publish(events[i]); + } + successfulRelays++; + } catch (error) { + console.error(`Failed to publish event to ${relayUrl}:`, error); + } finally { + relay.close(); + } + } + + // Report success or failure + if (successfulRelays === 0) { + throw Error("Failed to connect to any relays."); + } + return `${successfulRelays}/${relays.length} relays connected.`; + } + + /** + * Get the private key for the sender + * @param {string} sender Sender to retrieve key for + * @returns {nip19.DecodeResult} Private key + */ + async getPrivateKey(sender) { + try { + const senderDecodeResult = await nip19.decode(sender); + const { data } = senderDecodeResult; + return data; + } catch (error) { + throw new Error(`Failed to decode private key for sender ${sender}: ${error.message}`); + } + } + + /** + * Get public keys for recipients + * @param {string} recipients Newline delimited list of recipients + * @returns {Promise} Public keys + */ + async getPublicKeys(recipients) { + const recipientsList = recipients.split("\n"); + const publicKeys = []; + for (const recipient of recipientsList) { + try { + const recipientDecodeResult = await nip19.decode(recipient); + const { type, data } = recipientDecodeResult; + if (type === "npub") { + publicKeys.push(data); + } else { + throw new Error(`Recipient ${recipient} is not an npub`); + } + } catch (error) { + throw new Error(`Error decoding recipient ${recipient}: ${error}`); + } + } + return publicKeys; + } +} + +module.exports = Nostr; diff --git a/server/notification-providers/notifery.js b/server/notification-providers/notifery.js new file mode 100644 index 0000000..99cc7be --- /dev/null +++ b/server/notification-providers/notifery.js @@ -0,0 +1,54 @@ +const { getMonitorRelativeURL, UP } = require("../../src/util"); +const { setting } = require("../util-server"); +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Notifery extends NotificationProvider { + name = "notifery"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://api.notifery.com/event"; + + let data = { + title: notification.notiferyTitle || "Uptime Kuma Alert", + message: msg, + }; + + if (notification.notiferyGroup) { + data.group = notification.notiferyGroup; + } + + // Link to the monitor + const baseURL = await setting("primaryBaseURL"); + if (baseURL && monitorJSON) { + data.message += `\n\nMonitor: ${baseURL}${getMonitorRelativeURL(monitorJSON.id)}`; + } + + if (heartbeatJSON) { + data.code = heartbeatJSON.status === UP ? 0 : 1; + + if (heartbeatJSON.ping) { + data.duration = heartbeatJSON.ping; + } + } + + try { + const headers = { + "Content-Type": "application/json", + "x-api-key": notification.notiferyApiKey, + }; + + let config = this.getAxiosConfigWithProxy({ headers }); + await axios.post(url, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Notifery; diff --git a/server/notification-providers/notification-provider.js b/server/notification-providers/notification-provider.js new file mode 100644 index 0000000..a8caa0b --- /dev/null +++ b/server/notification-providers/notification-provider.js @@ -0,0 +1,199 @@ +const { Liquid } = require("liquidjs"); +const { DOWN } = require("../../src/util"); +const { HttpProxyAgent } = require("http-proxy-agent"); +const { HttpsProxyAgent } = require("https-proxy-agent"); +const { SocksProxyAgent } = require("socks-proxy-agent"); + +class NotificationProvider { + /** + * Notification Provider Name + * @type {string} + */ + name = undefined; + + /** + * Send a notification + * @param {BeanModel} notification Notification to send + * @param {string} msg General Message + * @param {?object} monitorJSON Monitor details (For Up/Down only) + * @param {?object} heartbeatJSON Heartbeat details (For Up/Down only) + * @returns {Promise} Return Successful Message + * @throws Error with fail msg + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + throw new Error("Have to override Notification.send(...)"); + } + + /** + * Extracts the address from a monitor JSON object based on its type. + * @param {?object} monitorJSON Monitor details (For Up/Down only) + * @returns {string} The extracted address based on the monitor type. + */ + extractAddress(monitorJSON) { + if (!monitorJSON) { + return ""; + } + switch (monitorJSON["type"]) { + case "push": + return "Heartbeat"; + case "ping": + case "dns": + return monitorJSON["hostname"]; + case "port": + case "gamedig": + case "steam": + if (monitorJSON["port"]) { + return monitorJSON["hostname"] + ":" + monitorJSON["port"]; + } + return monitorJSON["hostname"]; + case "globalping": + switch (monitorJSON["subtype"]) { + case "ping": + case "dns": + return monitorJSON["hostname"]; + case "http": + return monitorJSON["url"]; + default: + return ""; + } + default: + if (!["https://", "http://", ""].includes(monitorJSON["url"])) { + return monitorJSON["url"]; + } + return ""; + } + } + + /** + * Renders a message template with notification context + * @param {string} template the template + * @param {string} msg the message that will be included in the context + * @param {?object} monitorJSON Monitor details (For Up/Down/Cert-Expiry only) + * @param {?object} heartbeatJSON Heartbeat details (For Up/Down only) + * @returns {Promise} rendered template + */ + async renderTemplate(template, msg, monitorJSON, heartbeatJSON) { + const engine = new Liquid({ + root: "./no-such-directory-uptime-kuma", + relativeReference: false, + dynamicPartials: false, + }); + const parsedTpl = engine.parse(template); + + // Let's start with dummy values to simplify code + let monitorName = "Monitor Name not available"; + let monitorHostnameOrURL = "testing.hostname"; + + if (monitorJSON !== null) { + monitorName = monitorJSON["name"]; + monitorHostnameOrURL = this.extractAddress(monitorJSON); + } + + let serviceStatus = "⚠️ Test"; + if (heartbeatJSON !== null) { + serviceStatus = heartbeatJSON["status"] === DOWN ? "🔴 Down" : "✅ Up"; + } + + const context = { + // for v1 compatibility, to be removed in v3 + STATUS: serviceStatus, + NAME: monitorName, + HOSTNAME_OR_URL: monitorHostnameOrURL, + + // variables which are officially supported + status: serviceStatus, + name: monitorName, + hostnameOrURL: monitorHostnameOrURL, + monitorJSON, + heartbeatJSON, + msg, + }; + + return engine.render(parsedTpl, context); + } + + /** + * Throws an error + * @param {any} error The error to throw + * @returns {void} + * @throws {any} The error specified + */ + throwGeneralAxiosError(error) { + let msg = error && error.message ? error.message : String(error); + + if (error && error.code) { + msg += ` (code=${error.code})`; + } + + if (error && error.response && error.response.status) { + msg += ` (HTTP ${error.response.status}${error.response.statusText ? " " + error.response.statusText : ""})`; + } + + if (error && error.response && error.response.data) { + if (typeof error.response.data === "string") { + msg += " " + error.response.data; + } else { + try { + msg += " " + JSON.stringify(error.response.data); + } catch (e) { + msg += " " + String(error.response.data); + } + } + } + + // Expand AggregateError to show underlying causes + let agg = null; + if (error && error.name === "AggregateError" && Array.isArray(error.errors)) { + agg = error; + } else if (error && error.cause && error.cause.name === "AggregateError" && Array.isArray(error.cause.errors)) { + agg = error.cause; + } + + if (agg) { + let causes = agg.errors + .map((e) => { + let m = e && e.message ? e.message : String(e); + if (e && e.code) { + m += ` (code=${e.code})`; + } + return m; + }) + .join("; "); + msg += " - caused by: " + causes; + } else if (error && error.cause && error.cause.message) { + msg += " - cause: " + error.cause.message; + } + + throw new Error(msg); + } + + /** + * Returns axios config with proxy agent if proxy env is set. + * @param {object} axiosConfig - Axios config containing params + * @returns {object} Axios config + */ + getAxiosConfigWithProxy(axiosConfig = {}) { + const proxyEnv = process.env.notification_proxy || process.env.NOTIFICATION_PROXY; + if (proxyEnv) { + const proxyUrl = new URL(proxyEnv); + + if (proxyUrl.protocol === "http:") { + axiosConfig.httpAgent = new HttpProxyAgent(proxyEnv); + axiosConfig.httpsAgent = new HttpsProxyAgent(proxyEnv); + } else if (proxyUrl.protocol === "https:") { + const agent = new HttpsProxyAgent(proxyEnv); + axiosConfig.httpAgent = agent; + axiosConfig.httpsAgent = agent; + } else if (["socks:", "socks4:", "socks5:", "socks5h:"].includes(proxyUrl.protocol)) { + const agent = new SocksProxyAgent(proxyEnv); + axiosConfig.httpAgent = agent; + axiosConfig.httpsAgent = agent; + } + + axiosConfig.proxy = false; + } + return axiosConfig; + } +} + +module.exports = NotificationProvider; diff --git a/server/notification-providers/notifyapp.js b/server/notification-providers/notifyapp.js new file mode 100644 index 0000000..87a2629 --- /dev/null +++ b/server/notification-providers/notifyapp.js @@ -0,0 +1,49 @@ +const NotificationProvider = require("./notification-provider"); +const { DOWN, UP } = require("../../src/util"); +const axios = require("axios"); + +const NOTIFY_API_BASE = "https://push.getnotifyapp.com"; + +class NotifyApp extends NotificationProvider { + name = "notifyapp"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let title = monitorJSON?.name || "Uptime Kuma"; + if (heartbeatJSON != null) { + title += ` is ${heartbeatJSON["status"] === UP ? "UP" : "DOWN"}`; + } + + const body = { + text: msg, + title: title, + }; + + if (notification.notifyAppIconUrl) { + body.iconUrl = notification.notifyAppIconUrl; + } + + // Notify! uses this to break through Focus and quiet hours. + if (heartbeatJSON != null && heartbeatJSON["status"] === DOWN) { + body.timeSensitive = true; + } + + await axios.post( + `${NOTIFY_API_BASE}/notify-json/${encodeURIComponent(notification.notifyAppDeviceId)}?token=${encodeURIComponent(notification.notifyAppToken)}`, + body, + this.getAxiosConfigWithProxy({}) + ); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = NotifyApp; diff --git a/server/notification-providers/ntfy.js b/server/notification-providers/ntfy.js new file mode 100644 index 0000000..ebe5a17 --- /dev/null +++ b/server/notification-providers/ntfy.js @@ -0,0 +1,138 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class Ntfy extends NotificationProvider { + name = "ntfy"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let headers = {}; + if (notification.ntfyAuthenticationMethod === "usernamePassword") { + headers = { + Authorization: + "Basic " + + Buffer.from(notification.ntfyusername + ":" + notification.ntfypassword).toString("base64"), + }; + } else if (notification.ntfyAuthenticationMethod === "accessToken") { + headers = { + Authorization: "Bearer " + notification.ntfyaccesstoken, + }; + } + if (notification.ntfyCall) { + headers["X-Call"] = notification.ntfyCall; + } + let config = { + headers, + }; + config = this.getAxiosConfigWithProxy(config); + // If heartbeatJSON is null, assume non monitoring notification (Certificate warning) or testing. + if (heartbeatJSON == null) { + // Default values for test notification + let title = (monitorJSON?.name || notification.ntfytopic) + " [Uptime-Kuma]"; + let message = msg; + + // Apply custom templates from notification settings if enabled + if (notification.ntfyUseTemplate) { + const customTitle = notification.ntfyCustomTitle?.trim() || ""; + if (customTitle !== "") { + title = await this.renderTemplate(customTitle, msg, monitorJSON, heartbeatJSON); + } + + const customMessage = notification.ntfyCustomMessage?.trim() || ""; + if (customMessage !== "") { + message = await this.renderTemplate(customMessage, msg, monitorJSON, heartbeatJSON); + } + } + + let ntfyTestData = { + topic: notification.ntfytopic, + title: title, + message: message, + priority: notification.ntfyPriority, + tags: ["test_tube"], + }; + await axios.post(notification.ntfyserverurl, ntfyTestData, config); + return okMsg; + } + let tags = []; + let status = "unknown"; + let priority = notification.ntfyPriority || 4; + if ("status" in heartbeatJSON) { + if (heartbeatJSON.status === DOWN) { + tags = ["red_circle"]; + status = "Down"; + // defaults to max(priority + 1, 5) + priority = notification.ntfyPriorityDown || (priority === 5 ? priority : priority + 1); + } else if (heartbeatJSON["status"] === UP) { + tags = ["green_circle"]; + status = "Up"; + } + } + + // Include monitor's assigned tags + if (monitorJSON && monitorJSON.tags && Array.isArray(monitorJSON.tags)) { + const monitorTagNames = monitorJSON.tags.map((tag) => { + // Include value if it exists + if (tag.value) { + return `${tag.name}: ${tag.value}`; + } + return tag.name; + }); + tags = tags.concat(monitorTagNames); + } + + // Default values + let title = monitorJSON.name + " " + status + " [Uptime-Kuma]"; + let message = heartbeatJSON.msg; + + // Apply custom templates from notification settings if enabled + if (notification.ntfyUseTemplate) { + const customTitle = notification.ntfyCustomTitle?.trim() || ""; + const customMessage = notification.ntfyCustomMessage?.trim() || ""; + + if (customTitle !== "") { + title = await this.renderTemplate(customTitle, msg, monitorJSON, heartbeatJSON); + } + if (customMessage !== "") { + message = await this.renderTemplate(customMessage, msg, monitorJSON, heartbeatJSON); + } + } + + let data = { + topic: notification.ntfytopic, + message: message, + priority: priority, + title: title, + tags: tags, + }; + + if (monitorJSON.url && monitorJSON.url !== "https://") { + data.actions = [ + { + action: "view", + label: "Open " + monitorJSON.name, + url: monitorJSON.url, + }, + ]; + } + + if (notification.ntfyIcon) { + data.icon = notification.ntfyIcon; + } + + await axios.post(notification.ntfyserverurl, data, config); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Ntfy; diff --git a/server/notification-providers/octopush.js b/server/notification-providers/octopush.js new file mode 100644 index 0000000..167455d --- /dev/null +++ b/server/notification-providers/octopush.js @@ -0,0 +1,78 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Octopush extends NotificationProvider { + name = "octopush"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const urlV2 = "https://api.octopush.com/v1/public/sms-campaign/send"; + const urlV1 = "https://www.octopush-dm.com/api/sms/json"; + + try { + // Default - V2 + if (notification.octopushVersion === "2" || !notification.octopushVersion) { + let config = { + headers: { + "api-key": notification.octopushAPIKey, + "api-login": notification.octopushLogin, + "cache-control": "no-cache", + }, + }; + config = this.getAxiosConfigWithProxy(config); + let data = { + recipients: [ + { + phone_number: notification.octopushPhoneNumber, + }, + ], + //octopush not supporting non ascii char + text: msg.replace(/[^\x00-\x7F]/g, ""), + type: notification.octopushSMSType, + purpose: "alert", + sender: notification.octopushSenderName, + }; + await axios.post(urlV2, data, config); + } else if (notification.octopushVersion === "1") { + let data = { + user_login: notification.octopushDMLogin, + api_key: notification.octopushDMAPIKey, + sms_recipients: notification.octopushDMPhoneNumber, + sms_sender: notification.octopushDMSenderName, + sms_type: notification.octopushDMSMSType === "sms_premium" ? "FR" : "XXX", + transactional: "1", + //octopush not supporting non ascii char + sms_text: msg.replace(/[^\x00-\x7F]/g, ""), + }; + + let config = { + headers: { + "cache-control": "no-cache", + }, + params: data, + }; + config = this.getAxiosConfigWithProxy(config); + + // V1 API returns 200 even on error so we must check + // response data + let response = await axios.post(urlV1, {}, config); + if ("error_code" in response.data) { + if (response.data.error_code !== "000") { + this.throwGeneralAxiosError(`Octopush error ${JSON.stringify(response.data)}`); + } + } + } else { + throw new Error("Unknown Octopush version!"); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Octopush; diff --git a/server/notification-providers/onebot.js b/server/notification-providers/onebot.js new file mode 100644 index 0000000..0462e9d --- /dev/null +++ b/server/notification-providers/onebot.js @@ -0,0 +1,49 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class OneBot extends NotificationProvider { + name = "OneBot"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let url = notification.httpAddr; + if (!url.startsWith("http")) { + url = "http://" + url; + } + if (!url.endsWith("/")) { + url += "/"; + } + url += "send_msg"; + let config = { + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + notification.accessToken, + }, + }; + config = this.getAxiosConfigWithProxy(config); + let pushText = "UptimeKuma Alert: " + msg; + let data = { + auto_escape: true, + message: pushText, + }; + if (notification.msgType === "group") { + data["message_type"] = "group"; + data["group_id"] = notification.recieverId; + } else { + data["message_type"] = "private"; + data["user_id"] = notification.recieverId; + } + await axios.post(url, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = OneBot; diff --git a/server/notification-providers/onechat.js b/server/notification-providers/onechat.js new file mode 100644 index 0000000..789af31 --- /dev/null +++ b/server/notification-providers/onechat.js @@ -0,0 +1,70 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class OneChat extends NotificationProvider { + name = "OneChat"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://chat-api.one.th/message/api/v1/push_message"; + + try { + let config = { + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + notification.accessToken, + }, + }; + config = this.getAxiosConfigWithProxy(config); + if (heartbeatJSON == null) { + const testMessage = { + to: notification.recieverId, + bot_id: notification.botId, + type: "text", + message: "Test Successful!", + }; + await axios.post(url, testMessage, config); + } else if (heartbeatJSON["status"] === DOWN) { + const downMessage = { + to: notification.recieverId, + bot_id: notification.botId, + type: "text", + message: `UptimeKuma Alert: +[🔴 Down] +Name: ${monitorJSON["name"]} +${heartbeatJSON["msg"]} +Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`, + }; + await axios.post(url, downMessage, config); + } else if (heartbeatJSON["status"] === UP) { + const upMessage = { + to: notification.recieverId, + bot_id: notification.botId, + type: "text", + message: `UptimeKuma Alert: +[🟢 Up] +Name: ${monitorJSON["name"]} +${heartbeatJSON["msg"]} +Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`, + }; + await axios.post(url, upMessage, config); + } + + return okMsg; + } catch (error) { + // Handle errors and throw a descriptive message + if (error.response) { + const errorMessage = error.response.data?.message || "Unknown API error occurred."; + throw new Error(`OneChat API Error: ${errorMessage}`); + } else { + this.throwGeneralAxiosError(error); + } + } + } +} + +module.exports = OneChat; diff --git a/server/notification-providers/onesender.js b/server/notification-providers/onesender.js new file mode 100644 index 0000000..1ad4fe7 --- /dev/null +++ b/server/notification-providers/onesender.js @@ -0,0 +1,45 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Onesender extends NotificationProvider { + name = "Onesender"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let data = { + heartbeat: heartbeatJSON, + monitor: monitorJSON, + msg, + to: notification.onesenderReceiver, + type: "text", + recipient_type: "individual", + text: { + body: msg, + }, + }; + if (notification.onesenderTypeReceiver === "private") { + data.to = notification.onesenderReceiver + "@s.whatsapp.net"; + } else { + data.recipient_type = "group"; + data.to = notification.onesenderReceiver + "@g.us"; + } + let config = { + headers: { + Authorization: "Bearer " + notification.onesenderToken, + }, + }; + config = this.getAxiosConfigWithProxy(config); + await axios.post(notification.onesenderURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Onesender; diff --git a/server/notification-providers/ooredoo.js b/server/notification-providers/ooredoo.js new file mode 100644 index 0000000..32985bb --- /dev/null +++ b/server/notification-providers/ooredoo.js @@ -0,0 +1,89 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +// The Ooredoo bulk_sms/v2 gateway accepts at most this many recipients per +// request, so larger recipient lists are split into batches of this size. +const MAX_RECIPIENTS_PER_REQUEST = 20; + +class Ooredoo extends NotificationProvider { + name = "Ooredoo"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = notification.ooredooServerUrl || "https://o-papi1-lb01.ooredoo.mv/bulk_sms/v2"; + + // Users may enter recipients separated by comma, semicolon, space or + // newline. + const recipients = notification.ooredooToNumber + .split(/[\s,;]+/) + .map((number) => this.normalizePhoneNumber(number)) + .filter((number) => number !== ""); + + if (recipients.length === 0) { + throw new Error("No valid recipient phone number was provided."); + } + + try { + let config = { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + Authorization: "Bearer " + notification.ooredooBearerToken, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + // The gateway only accepts MAX_RECIPIENTS_PER_REQUEST numbers per + // call, so send the recipients in batches of that size. + for (let i = 0; i < recipients.length; i += MAX_RECIPIENTS_PER_REQUEST) { + const batch = recipients.slice(i, i + MAX_RECIPIENTS_PER_REQUEST); + const data = new URLSearchParams({ + username: notification.ooredooUsername, + // The gateway expects the access key to be Base64 encoded. + access_key: Buffer.from(notification.ooredooAccessKey).toString("base64"), + message: msg, + batch: batch.join(" "), + }); + + const resp = await axios.post(url, data.toString(), config); + + // The gateway returns HTTP 200 even on failure; the real outcome + // is carried in "response_code" (0 means the batch was accepted). + if (!resp.data || Number(resp.data.response_code) !== 0) { + const reason = + resp.data && resp.data.response_message + ? resp.data.response_message + : "response_code=" + (resp.data ? resp.data.response_code : "unknown"); + throw new Error("Ooredoo rejected the message: " + reason); + } + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Normalize a Maldivian phone number to the "960XXXXXXX" format expected + * by the Ooredoo gateway. Numbers already in that form are returned as is. + * @param {string} phoneNumber The phone number to normalize + * @returns {string} The normalized phone number, or "" if it is empty + */ + normalizePhoneNumber(phoneNumber) { + const number = phoneNumber.replace(/[\s+]/g, ""); + + if (number === "") { + return ""; + } + // A bare 7-digit local number gets the Maldives (960) country code. + if (/^\d{7}$/.test(number)) { + return "960" + number; + } + return number; + } +} + +module.exports = Ooredoo; diff --git a/server/notification-providers/openwa.js b/server/notification-providers/openwa.js new file mode 100644 index 0000000..3856efe --- /dev/null +++ b/server/notification-providers/openwa.js @@ -0,0 +1,60 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class OpenWa extends NotificationProvider { + name = "openwa"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let text = msg; + const customMessage = notification.openwaCustomMessage; + if (notification.openwaUseCustomMessage && typeof customMessage === "string" && customMessage.trim()) { + text = await this.renderTemplate(customMessage, msg, monitorJSON, heartbeatJSON); + } + + let config = { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + "X-Api-Key": notification.openwaApiKey, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + const chatIds = (notification.openwaChatId || "") + .split(",") + .map((id) => id.trim()) + .filter((id) => id.length > 0); + + if (chatIds.length === 0) { + throw new Error("No valid OpenWA chat ID found."); + } + + const baseUrl = notification.openwaApiUrl.replace(/([^/])\/+$/, "$1"); + const sessionId = encodeURIComponent(notification.openwaSession); + const url = `${baseUrl}/api/sessions/${sessionId}/messages/send-text`; + + for (const chatId of chatIds) { + await axios.post( + url, + { + chatId, + text, + }, + config + ); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = OpenWa; diff --git a/server/notification-providers/opsgenie.js b/server/notification-providers/opsgenie.js new file mode 100644 index 0000000..ac725bb --- /dev/null +++ b/server/notification-providers/opsgenie.js @@ -0,0 +1,97 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP, DOWN } = require("../../src/util"); + +const opsgenieAlertsUrlEU = "https://api.eu.opsgenie.com/v2/alerts"; +const opsgenieAlertsUrlUS = "https://api.opsgenie.com/v2/alerts"; +const okMsg = "Sent Successfully."; + +class Opsgenie extends NotificationProvider { + name = "Opsgenie"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + let opsgenieAlertsUrl; + let priority = !notification.opsgeniePriority ? 3 : notification.opsgeniePriority; + const textMsg = "Uptime Kuma Alert"; + + try { + switch (notification.opsgenieRegion) { + case "us": + opsgenieAlertsUrl = opsgenieAlertsUrlUS; + break; + case "eu": + opsgenieAlertsUrl = opsgenieAlertsUrlEU; + break; + default: + opsgenieAlertsUrl = opsgenieAlertsUrlUS; + } + + if (heartbeatJSON == null) { + let notificationTestAlias = "uptime-kuma-notification-test"; + let data = { + message: msg, + alias: notificationTestAlias, + source: "Uptime Kuma", + priority: "P5", + }; + + return this.post(notification, opsgenieAlertsUrl, data); + } + + if (heartbeatJSON.status === DOWN) { + let data = { + message: monitorJSON ? textMsg + `: ${monitorJSON.name}` : textMsg, + alias: monitorJSON.name, + description: msg, + source: "Uptime Kuma", + priority: `P${priority}`, + }; + + return this.post(notification, opsgenieAlertsUrl, data); + } + + if (heartbeatJSON.status === UP) { + let opsgenieAlertsCloseUrl = `${opsgenieAlertsUrl}/${encodeURIComponent(monitorJSON.name)}/close?identifierType=alias`; + let data = { + source: "Uptime Kuma", + }; + + return this.post(notification, opsgenieAlertsCloseUrl, data); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Make POST request to Opsgenie + * @param {BeanModel} notification Notification to send + * @param {string} url Request url + * @param {object} data Request body + * @returns {Promise} Success message + */ + async post(notification, url, data) { + let config = { + headers: { + "Content-Type": "application/json", + Authorization: `GenieKey ${notification.opsgenieApiKey}`, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + let res = await axios.post(url, data, config); + if (res.status == null) { + return "Opsgenie notification failed with invalid response!"; + } + if (res.status < 200 || res.status >= 300) { + return `Opsgenie notification failed with status code ${res.status}`; + } + + return okMsg; + } +} + +module.exports = Opsgenie; diff --git a/server/notification-providers/pagerduty.js b/server/notification-providers/pagerduty.js new file mode 100644 index 0000000..bc3e607 --- /dev/null +++ b/server/notification-providers/pagerduty.js @@ -0,0 +1,114 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); +const { Settings } = require("../settings"); +let successMessage = "Sent Successfully."; + +class PagerDuty extends NotificationProvider { + name = "PagerDuty"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + try { + if (heartbeatJSON == null) { + const title = "Uptime Kuma Alert"; + const monitor = { + type: "ping", + url: "Uptime Kuma Test Button", + }; + return this.postNotification(notification, title, msg, monitor); + } + + if (heartbeatJSON.status === UP) { + const title = "Uptime Kuma Monitor ✅ Up"; + return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "resolve"); + } + + if (heartbeatJSON.status === DOWN) { + const title = "Uptime Kuma Monitor 🔴 Down"; + return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "trigger"); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Check if result is successful, result code should be in range 2xx + * @param {object} result Axios response object + * @returns {void} + * @throws {Error} The status code is not in range 2xx + */ + checkResult(result) { + if (result.status == null) { + throw new Error("PagerDuty notification failed with invalid response!"); + } + if (result.status < 200 || result.status >= 300) { + throw new Error("PagerDuty notification failed with status code " + result.status); + } + } + + /** + * Send the message + * @param {BeanModel} notification Message title + * @param {string} title Message title + * @param {string} body Message + * @param {object} monitorInfo Monitor details (For Up/Down only) + * @param {?string} eventAction Action event for PagerDuty (trigger, acknowledge, resolve) + * @returns {Promise} Success message + */ + async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") { + let monitorUrl; + if (monitorInfo.type === "port") { + monitorUrl = monitorInfo.hostname; + if (monitorInfo.port) { + monitorUrl += ":" + monitorInfo.port; + } + } else if (monitorInfo.hostname != null) { + monitorUrl = monitorInfo.hostname; + } else { + monitorUrl = monitorInfo.url; + } + + if (eventAction === "resolve") { + if (notification.pagerdutyAutoResolve === "0") { + return "no action required"; + } + eventAction = notification.pagerdutyAutoResolve; + } + + const options = { + method: "POST", + url: notification.pagerdutyIntegrationUrl, + headers: { "Content-Type": "application/json" }, + data: { + payload: { + summary: monitorInfo.name ? `[${title}] [${monitorInfo.name}] ${body}` : `[${title}] ${body}`, + severity: notification.pagerdutyPriority || "warning", + source: monitorUrl, + }, + routing_key: notification.pagerdutyIntegrationKey, + event_action: eventAction, + dedup_key: monitorInfo.id ? "Uptime Kuma/" + monitorInfo.id : "Uptime Kuma/test", + }, + }; + + const baseURL = await Settings.get("primaryBaseURL"); + if (baseURL && monitorInfo) { + options.client = "Uptime Kuma"; + options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id); + } + + let result = await axios.request(options); + this.checkResult(result); + if (result.statusText != null) { + return "PagerDuty notification succeed: " + result.statusText; + } + + return successMessage; + } +} + +module.exports = PagerDuty; diff --git a/server/notification-providers/pagertree.js b/server/notification-providers/pagertree.js new file mode 100644 index 0000000..ea7f0fb --- /dev/null +++ b/server/notification-providers/pagertree.js @@ -0,0 +1,98 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); +const { Settings } = require("../settings"); +let successMessage = "Sent Successfully."; + +class PagerTree extends NotificationProvider { + name = "PagerTree"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + try { + if (heartbeatJSON == null) { + // general messages + return this.postNotification(notification, msg, monitorJSON, heartbeatJSON); + } + + if (heartbeatJSON.status === UP && notification.pagertreeAutoResolve === "resolve") { + return this.postNotification( + notification, + null, + monitorJSON, + heartbeatJSON, + notification.pagertreeAutoResolve + ); + } + + if (heartbeatJSON.status === DOWN) { + const title = `Uptime Kuma Monitor "${monitorJSON.name}" is DOWN`; + return this.postNotification(notification, title, monitorJSON, heartbeatJSON); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Check if result is successful, result code should be in range 2xx + * @param {object} result Axios response object + * @returns {void} + * @throws {Error} The status code is not in range 2xx + */ + checkResult(result) { + if (result.status == null) { + throw new Error("PagerTree notification failed with invalid response!"); + } + if (result.status < 200 || result.status >= 300) { + throw new Error("PagerTree notification failed with status code " + result.status); + } + } + + /** + * Send the message + * @param {BeanModel} notification Message title + * @param {string} title Message title + * @param {object} monitorJSON Monitor details (For Up/Down only) + * @param {object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {?string} eventAction Action event for PagerTree (create, resolve) + * @returns {Promise} Success state + */ + async postNotification(notification, title, monitorJSON, heartbeatJSON, eventAction = "create") { + if (eventAction == null) { + return "No action required"; + } + + const options = { + method: "POST", + url: notification.pagertreeIntegrationUrl, + headers: { "Content-Type": "application/json" }, + data: { + event_type: eventAction, + id: heartbeatJSON?.monitorID || "uptime-kuma", + title: title, + urgency: notification.pagertreeUrgency, + heartbeat: heartbeatJSON, + monitor: monitorJSON, + }, + }; + + const baseURL = await Settings.get("primaryBaseURL"); + if (baseURL && monitorJSON) { + options.client = "Uptime Kuma"; + options.client_url = baseURL + getMonitorRelativeURL(monitorJSON.id); + } + + let result = await axios.request(options); + this.checkResult(result); + if (result.statusText != null) { + return "PagerTree notification succeed: " + result.statusText; + } + + return successMessage; + } +} + +module.exports = PagerTree; diff --git a/server/notification-providers/pinglet.js b/server/notification-providers/pinglet.js new file mode 100644 index 0000000..4c84bae --- /dev/null +++ b/server/notification-providers/pinglet.js @@ -0,0 +1,48 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Pinglet extends NotificationProvider { + name = "pinglet"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let publishUrl = notification.pingletPublishUrl; + if (publishUrl.endsWith("/")) { + publishUrl = publishUrl.slice(0, -1); + } + + const config = this.getAxiosConfigWithProxy({ + headers: { + Authorization: "Bearer " + notification.pingletApiKey, + }, + params: { + rewrite: "uptimekuma", + }, + }); + + // Pinglet's uptimekuma rewriter accepts Uptime Kuma's webhook payload + // as-is and maps it to a titled message with level and priority + // server-side, so this provider just forwards the raw shape. + await axios.post( + publishUrl, + { + heartbeat: heartbeatJSON, + monitor: monitorJSON, + msg, + }, + config + ); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Pinglet; diff --git a/server/notification-providers/plivo.js b/server/notification-providers/plivo.js new file mode 100644 index 0000000..d807207 --- /dev/null +++ b/server/notification-providers/plivo.js @@ -0,0 +1,53 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Plivo extends NotificationProvider { + name = "plivo"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + "Content-Type": "application/json", + Authorization: + "Basic " + + Buffer.from(notification.plivoAuthID + ":" + notification.plivoAuthToken).toString("base64"), + }, + timeout: 10000, + }; + config = this.getAxiosConfigWithProxy(config); + + const baseURL = `https://api.plivo.com/v1/Account/${notification.plivoAuthID}`; + + if (notification.plivoMessageType === "call") { + const answerUrl = new URL(notification.plivoAnswerUrl); + answerUrl.searchParams.set("message", msg); + const data = { + from: notification.plivoFromNumber, + to: notification.plivoToNumber, + answer_url: answerUrl.toString(), + answer_method: "GET", + }; + await axios.post(`${baseURL}/Call/`, data, config); + } else { + const data = { + src: notification.plivoFromNumber, + dst: notification.plivoToNumber, + text: msg, + }; + await axios.post(`${baseURL}/Message/`, data, config); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Plivo; diff --git a/server/notification-providers/promosms.js b/server/notification-providers/promosms.js new file mode 100644 index 0000000..81daee5 --- /dev/null +++ b/server/notification-providers/promosms.js @@ -0,0 +1,58 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class PromoSMS extends NotificationProvider { + name = "promosms"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://promosms.com/api/rest/v3_2/sms"; + + if (notification.promosmsAllowLongSMS === undefined) { + notification.promosmsAllowLongSMS = false; + } + + //TODO: Add option for enabling special characters. It will decrease message max length from 160 to 70 chars. + //Lets remove non ascii char + let cleanMsg = msg.replace(/[^\x00-\x7F]/g, ""); + + try { + let config = { + headers: { + "Content-Type": "application/json", + Authorization: + "Basic " + + Buffer.from(notification.promosmsLogin + ":" + notification.promosmsPassword).toString( + "base64" + ), + Accept: "text/json", + }, + }; + config = this.getAxiosConfigWithProxy(config); + let data = { + recipients: [notification.promosmsPhoneNumber], + //Trim message to maximum length of 1 SMS or 4 if we allowed long messages + text: notification.promosmsAllowLongSMS ? cleanMsg.substring(0, 639) : cleanMsg.substring(0, 159), + "long-sms": notification.promosmsAllowLongSMS, + type: Number(notification.promosmsSMSType), + sender: notification.promosmsSenderName, + }; + + let resp = await axios.post(url, data, config); + + if (resp.data.response.status !== 0) { + let error = "Something gone wrong. Api returned " + resp.data.response.status + "."; + this.throwGeneralAxiosError(error); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = PromoSMS; diff --git a/server/notification-providers/pumble.js b/server/notification-providers/pumble.js new file mode 100644 index 0000000..8b2835e --- /dev/null +++ b/server/notification-providers/pumble.js @@ -0,0 +1,49 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP } = require("../../src/util"); + +class Pumble extends NotificationProvider { + name = "pumble"; + + /** + * @inheritDoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = this.getAxiosConfigWithProxy({}); + if (heartbeatJSON === null && monitorJSON === null) { + let data = { + attachments: [ + { + title: "Uptime Kuma Alert", + text: msg, + color: "#5BDD8B", + }, + ], + }; + + await axios.post(notification.webhookURL, data, config); + return okMsg; + } + + let data = { + attachments: [ + { + title: `${monitorJSON["name"]} is ${heartbeatJSON["status"] === UP ? "up" : "down"}`, + text: heartbeatJSON["msg"], + color: heartbeatJSON["status"] === UP ? "#5BDD8B" : "#DC3645", + }, + ], + }; + + await axios.post(notification.webhookURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Pumble; diff --git a/server/notification-providers/pushbullet.js b/server/notification-providers/pushbullet.js new file mode 100644 index 0000000..4558aeb --- /dev/null +++ b/server/notification-providers/pushbullet.js @@ -0,0 +1,59 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +const { DOWN, UP } = require("../../src/util"); + +class Pushbullet extends NotificationProvider { + name = "pushbullet"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://api.pushbullet.com/v2/pushes"; + + try { + let config = { + headers: { + "Access-Token": notification.pushbulletAccessToken, + "Content-Type": "application/json", + }, + }; + config = this.getAxiosConfigWithProxy(config); + if (heartbeatJSON == null) { + let data = { + type: "note", + title: "Uptime Kuma Alert", + body: msg, + }; + await axios.post(url, data, config); + } else if (heartbeatJSON["status"] === DOWN) { + let downData = { + type: "note", + title: "UptimeKuma Alert: " + monitorJSON["name"], + body: + "[🔴 Down] " + + heartbeatJSON["msg"] + + `\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`, + }; + await axios.post(url, downData, config); + } else if (heartbeatJSON["status"] === UP) { + let upData = { + type: "note", + title: "UptimeKuma Alert: " + monitorJSON["name"], + body: + "[✅ Up] " + + heartbeatJSON["msg"] + + `\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`, + }; + await axios.post(url, upData, config); + } + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Pushbullet; diff --git a/server/notification-providers/pushdeer.js b/server/notification-providers/pushdeer.js new file mode 100644 index 0000000..0df4051 --- /dev/null +++ b/server/notification-providers/pushdeer.js @@ -0,0 +1,57 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class PushDeer extends NotificationProvider { + name = "PushDeer"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const serverUrl = notification.pushdeerServer || "https://api2.pushdeer.com"; + // capture group below is necessary to prevent an ReDOS-attack + const url = `${serverUrl.trim().replace(/([^/])\/+$/, "$1")}/message/push`; + + let valid = msg != null && monitorJSON != null && heartbeatJSON != null; + + let title; + if (valid && heartbeatJSON.status === UP) { + title = "## Uptime Kuma: " + monitorJSON.name + " up"; + } else if (valid && heartbeatJSON.status === DOWN) { + title = "## Uptime Kuma: " + monitorJSON.name + " down"; + } else { + title = "## Uptime Kuma Message"; + } + + let data = { + pushkey: notification.pushdeerKey, + text: title, + desp: msg.replace(/\n/g, "\n\n"), + type: "markdown", + }; + + try { + let config = this.getAxiosConfigWithProxy({}); + let res = await axios.post(url, data, config); + + if ("error" in res.data) { + let error = res.data.error; + this.throwGeneralAxiosError(error); + } + if (res.data.content.result.length === 0) { + let error = "Invalid PushDeer key"; + this.throwGeneralAxiosError(error); + } else if (JSON.parse(res.data.content.result[0]).success !== "ok") { + let error = "Unknown error"; + this.throwGeneralAxiosError(error); + } + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = PushDeer; diff --git a/server/notification-providers/pushover.js b/server/notification-providers/pushover.js new file mode 100644 index 0000000..6d935a3 --- /dev/null +++ b/server/notification-providers/pushover.js @@ -0,0 +1,64 @@ +const { getMonitorRelativeURL } = require("../../src/util"); +const { setting } = require("../util-server"); +const { UP } = require("../../src/util"); + +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Pushover extends NotificationProvider { + name = "pushover"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://api.pushover.net/1/messages.json"; + + let data = { + message: msg, + user: notification.pushoveruserkey, + token: notification.pushoverapptoken, + sound: notification.pushoversounds, + priority: notification.pushoverpriority, + title: notification.pushovertitle, + retry: "30", + expire: "3600", + html: 1, + }; + + const baseURL = await setting("primaryBaseURL"); + if (baseURL && monitorJSON) { + data["url"] = baseURL + getMonitorRelativeURL(monitorJSON.id); + data["url_title"] = "Link to Monitor"; + } + + if (notification.pushoverdevice) { + data.device = notification.pushoverdevice; + } + if (notification.pushoverttl) { + data.ttl = notification.pushoverttl; + } + + try { + let config = this.getAxiosConfigWithProxy({}); + if (heartbeatJSON == null) { + await axios.post(url, data, config); + return okMsg; + } + + if (heartbeatJSON.status === UP && notification.pushoversounds_up) { + // default = DOWN => DOWN-sound is also played for non-UP/DOWN notiifcations + data.sound = notification.pushoversounds_up; + } + + data.message += `\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`; + await axios.post(url, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Pushover; diff --git a/server/notification-providers/pushplus.js b/server/notification-providers/pushplus.js new file mode 100644 index 0000000..b807c17 --- /dev/null +++ b/server/notification-providers/pushplus.js @@ -0,0 +1,57 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class PushPlus extends NotificationProvider { + name = "PushPlus"; + + /** + * @inheritdoc + * @param {BeanModel} notification Notification object + * @param {string} msg Message content + * @param {?object} monitorJSON Monitor details + * @param {?object} heartbeatJSON Heartbeat details + * @returns {Promise} Success message + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://www.pushplus.plus/send"; + try { + let config = { + headers: { + "Content-Type": "application/json", + }, + }; + config = this.getAxiosConfigWithProxy(config); + const params = { + token: notification.pushPlusSendKey, + title: this.checkStatus(heartbeatJSON, monitorJSON), + content: msg, + template: "html", + }; + await axios.post(url, params, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Get the formatted title for message + * @param {?object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {?object} monitorJSON Monitor details (For Up/Down only) + * @returns {string} Formatted title + */ + checkStatus(heartbeatJSON, monitorJSON) { + let title = "UptimeKuma Message"; + if (heartbeatJSON != null && heartbeatJSON["status"] === UP) { + title = "UptimeKuma Monitor Up " + monitorJSON["name"]; + } + if (heartbeatJSON != null && heartbeatJSON["status"] === DOWN) { + title = "UptimeKuma Monitor Down " + monitorJSON["name"]; + } + return title; + } +} + +module.exports = PushPlus; diff --git a/server/notification-providers/pushy.js b/server/notification-providers/pushy.js new file mode 100644 index 0000000..c922cae --- /dev/null +++ b/server/notification-providers/pushy.js @@ -0,0 +1,37 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Pushy extends NotificationProvider { + name = "pushy"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = this.getAxiosConfigWithProxy({}); + await axios.post( + `https://api.pushy.me/push?api_key=${notification.pushyAPIKey}`, + { + to: notification.pushyToken, + data: { + message: "Uptime-Kuma", + }, + notification: { + body: msg, + badge: 1, + sound: "ping.aiff", + }, + }, + config + ); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Pushy; diff --git a/server/notification-providers/resend.js b/server/notification-providers/resend.js new file mode 100644 index 0000000..15599b8 --- /dev/null +++ b/server/notification-providers/resend.js @@ -0,0 +1,44 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Resend extends NotificationProvider { + name = "Resend"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + Authorization: `Bearer ${notification.resendApiKey}`, + "Content-Type": "application/json", + }, + }; + config = this.getAxiosConfigWithProxy(config); + const email = notification.resendFromEmail.trim(); + + const fromName = notification.resendFromName?.trim() || "Uptime Kuma"; + let data = { + from: `${fromName} <${email}>`, + to: notification.resendToEmail, + subject: notification.resendSubject || "Notification from Your Uptime Kuma", + // supplied text directly instead of html + text: msg, + }; + + let result = await axios.post("https://api.resend.com/emails", data, config); + if (result.status === 200) { + return okMsg; + } else { + throw new Error(`Unexpected status code: ${result.status}`); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Resend; diff --git a/server/notification-providers/rocket-chat.js b/server/notification-providers/rocket-chat.js new file mode 100644 index 0000000..2e5879f --- /dev/null +++ b/server/notification-providers/rocket-chat.js @@ -0,0 +1,67 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const Slack = require("./slack"); +const { getMonitorRelativeURL, DOWN } = require("../../src/util"); +const { Settings } = require("../settings"); + +class RocketChat extends NotificationProvider { + name = "rocket.chat"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = this.getAxiosConfigWithProxy({}); + if (heartbeatJSON == null) { + let data = { + text: msg, + channel: notification.rocketchannel, + username: notification.rocketusername, + icon_emoji: notification.rocketiconemo, + }; + await axios.post(notification.rocketwebhookURL, data, config); + return okMsg; + } + + let data = { + text: "Uptime Kuma Alert", + channel: notification.rocketchannel, + username: notification.rocketusername, + icon_emoji: notification.rocketiconemo, + attachments: [ + { + title: `Uptime Kuma Alert *Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`, + text: "*Message*\n" + msg, + }, + ], + }; + + // Color + if (heartbeatJSON.status === DOWN) { + data.attachments[0].color = "#ff0000"; + } else { + data.attachments[0].color = "#32cd32"; + } + + if (notification.rocketbutton) { + await Slack.deprecateURL(notification.rocketbutton); + } + + const baseURL = await Settings.get("primaryBaseURL"); + + if (baseURL) { + data.attachments[0].title_link = baseURL + getMonitorRelativeURL(monitorJSON.id); + } + + await axios.post(notification.rocketwebhookURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = RocketChat; diff --git a/server/notification-providers/send-grid.js b/server/notification-providers/send-grid.js new file mode 100644 index 0000000..b401831 --- /dev/null +++ b/server/notification-providers/send-grid.js @@ -0,0 +1,57 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class SendGrid extends NotificationProvider { + name = "SendGrid"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${notification.sendgridApiKey}`, + }, + }; + config = this.getAxiosConfigWithProxy(config); + let personalizations = { + to: [{ email: notification.sendgridToEmail }], + }; + + // Add CC recipients if provided + if (notification.sendgridCcEmail) { + personalizations.cc = notification.sendgridCcEmail.split(",").map((email) => ({ email: email.trim() })); + } + + // Add BCC recipients if provided + if (notification.sendgridBccEmail) { + personalizations.bcc = notification.sendgridBccEmail + .split(",") + .map((email) => ({ email: email.trim() })); + } + + let data = { + personalizations: [personalizations], + from: { email: notification.sendgridFromEmail.trim() }, + subject: notification.sendgridSubject || "Notification from Your Uptime Kuma", + content: [ + { + type: "text/plain", + value: msg, + }, + ], + }; + + await axios.post("https://api.sendgrid.com/v3/mail/send", data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SendGrid; diff --git a/server/notification-providers/serverchan.js b/server/notification-providers/serverchan.js new file mode 100644 index 0000000..888141b --- /dev/null +++ b/server/notification-providers/serverchan.js @@ -0,0 +1,56 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class ServerChan extends NotificationProvider { + name = "ServerChan"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + // serverchan3 requires sending via ft07.com + const matchResult = String(notification.serverChanSendKey).match(/^sctp(\d+)t/i); + const url = + matchResult && matchResult[1] + ? `https://${matchResult[1]}.push.ft07.com/send/${notification.serverChanSendKey}.send` + : `https://sctapi.ftqq.com/${notification.serverChanSendKey}.send`; + + try { + let config = this.getAxiosConfigWithProxy({}); + await axios.post( + url, + { + title: this.checkStatus(heartbeatJSON, monitorJSON), + desp: msg, + }, + config + ); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Get the formatted title for message + * @param {?object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {?object} monitorJSON Monitor details (For Up/Down only) + * @returns {string} Formatted title + */ + checkStatus(heartbeatJSON, monitorJSON) { + let title = "UptimeKuma Message"; + if (heartbeatJSON != null && heartbeatJSON["status"] === UP) { + title = "UptimeKuma Monitor Up " + monitorJSON["name"]; + } + if (heartbeatJSON != null && heartbeatJSON["status"] === DOWN) { + title = "UptimeKuma Monitor Down " + monitorJSON["name"]; + } + return title; + } +} + +module.exports = ServerChan; diff --git a/server/notification-providers/serwersms.js b/server/notification-providers/serwersms.js new file mode 100644 index 0000000..1711f7b --- /dev/null +++ b/server/notification-providers/serwersms.js @@ -0,0 +1,53 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class SerwerSMS extends NotificationProvider { + name = "serwersms"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://api2.serwersms.pl/messages/send_sms"; + + try { + let config = { + headers: { + "Content-Type": "application/json", + }, + }; + config = this.getAxiosConfigWithProxy(config); + let data = { + username: notification.serwersmsUsername, + password: notification.serwersmsPassword, + text: msg.replace(/[^\x00-\x7F]/g, ""), + sender: notification.serwersmsSenderName, + }; + + if (notification.serwersmsRecipientType === "group") { + data.group_id = notification.serwersmsGroupId; + } else { + data.phone = notification.serwersmsPhoneNumber; + } + + let resp = await axios.post(url, data, config); + + if (!resp.data.success) { + if (resp.data.error) { + let error = `SerwerSMS.pl API returned error code ${resp.data.error.code} (${resp.data.error.type}) with error message: ${resp.data.error.message}`; + this.throwGeneralAxiosError(error); + } else { + let error = "SerwerSMS.pl API returned an unexpected response"; + this.throwGeneralAxiosError(error); + } + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SerwerSMS; diff --git a/server/notification-providers/sevenio.js b/server/notification-providers/sevenio.js new file mode 100644 index 0000000..91290ef --- /dev/null +++ b/server/notification-providers/sevenio.js @@ -0,0 +1,59 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class SevenIO extends NotificationProvider { + name = "SevenIO"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + const data = { + to: notification.sevenioReceiver, + from: notification.sevenioSender || "Uptime Kuma", + text: msg, + }; + + let config = { + baseURL: "https://gateway.seven.io/api/", + headers: { + "Content-Type": "application/json", + "X-API-Key": notification.sevenioApiKey, + }, + }; + + try { + config = this.getAxiosConfigWithProxy(config); + // testing or certificate expiry notification + if (heartbeatJSON == null) { + await axios.post("sms", data, config); + return okMsg; + } + + let address = this.extractAddress(monitorJSON); + if (address !== "") { + address = `(${address}) `; + } + + // If heartbeatJSON is not null, we go into the normal alerting loop. + if (heartbeatJSON["status"] === DOWN) { + data.text = + `Your service ${monitorJSON["name"]} ${address}went down at ${heartbeatJSON["localDateTime"]} ` + + `(${heartbeatJSON["timezone"]}). Error: ${heartbeatJSON["msg"]}`; + } else if (heartbeatJSON["status"] === UP) { + data.text = + `Your service ${monitorJSON["name"]} ${address}went back up at ${heartbeatJSON["localDateTime"]} ` + + `(${heartbeatJSON["timezone"]}).`; + } + await axios.post("sms", data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SevenIO; diff --git a/server/notification-providers/signal.js b/server/notification-providers/signal.js new file mode 100644 index 0000000..badabdf --- /dev/null +++ b/server/notification-providers/signal.js @@ -0,0 +1,35 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Signal extends NotificationProvider { + name = "signal"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let message = msg; + + if (notification.signalUseTemplate) { + message = await this.renderTemplate(notification.signalTemplate, msg, monitorJSON, heartbeatJSON); + } + + let data = { + message, + number: notification.signalNumber, + recipients: notification.signalRecipients.replace(/\s/g, "").split(","), + }; + let config = {}; + config = this.getAxiosConfigWithProxy(config); + await axios.post(notification.signalURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Signal; diff --git a/server/notification-providers/signalgrid.js b/server/notification-providers/signalgrid.js new file mode 100644 index 0000000..cc8cd19 --- /dev/null +++ b/server/notification-providers/signalgrid.js @@ -0,0 +1,51 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class Signalgrid extends NotificationProvider { + name = "signalgrid"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + let type = "INFO"; + let title = "Uptime Kuma"; + + if (heartbeatJSON) { + if (heartbeatJSON.status === DOWN) { + type = "CRIT"; + title = `${monitorJSON.name} Down`; + } else if (heartbeatJSON.status === UP) { + type = "SUCCESS"; + title = `${monitorJSON.name} Up`; + } + } + + try { + const config = this.getAxiosConfigWithProxy({}); + const data = new URLSearchParams(); + + data.append("client_key", notification.signalgridClientKey); + data.append("channel", notification.signalgridChannel); + data.append("title", title); + data.append("body", msg); + data.append("type", type); + data.append("critical", "false"); + + const response = await axios.post("https://api.signalgrid.co/v1/push", data, config); + + if (response.data?.code && String(response.data.code) !== "200") { + throw new Error(response.data.text || "Signalgrid API request failed"); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Signalgrid; diff --git a/server/notification-providers/signl4.js b/server/notification-providers/signl4.js new file mode 100644 index 0000000..6ddf08d --- /dev/null +++ b/server/notification-providers/signl4.js @@ -0,0 +1,53 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP, DOWN } = require("../../src/util"); + +class SIGNL4 extends NotificationProvider { + name = "SIGNL4"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let data = { + heartbeat: heartbeatJSON, + monitor: monitorJSON, + msg, + // Source system + "X-S4-SourceSystem": "UptimeKuma", + monitorUrl: this.extractAddress(monitorJSON), + }; + + let config = { + headers: { + "Content-Type": "application/json", + }, + }; + config = this.getAxiosConfigWithProxy(config); + + if (heartbeatJSON == null) { + // Test alert + data.title = "Uptime Kuma Alert"; + data.message = msg; + } else if (heartbeatJSON.status === UP) { + data.title = "Uptime Kuma Monitor ✅ Up"; + data["X-S4-ExternalID"] = "UptimeKuma-" + monitorJSON.monitorID; + data["X-S4-Status"] = "resolved"; + } else if (heartbeatJSON.status === DOWN) { + data.title = "Uptime Kuma Monitor 🔴 Down"; + data["X-S4-ExternalID"] = "UptimeKuma-" + monitorJSON.monitorID; + data["X-S4-Status"] = "new"; + } + + await axios.post(notification.webhookURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SIGNL4; diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js new file mode 100644 index 0000000..32f0664 --- /dev/null +++ b/server/notification-providers/slack.js @@ -0,0 +1,222 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { setSettings, setting } = require("../util-server"); +const { getMonitorRelativeURL, UP, log } = require("../../src/util"); +const isUrl = require("is-url"); + +class Slack extends NotificationProvider { + name = "slack"; + + /** + * Deprecated property notification.slackbutton + * Set it as primary base url if this is not yet set. + * @deprecated + * @param {string} url The primary base URL to use + * @returns {Promise} + */ + static async deprecateURL(url) { + let currentPrimaryBaseURL = await setting("primaryBaseURL"); + + if (!currentPrimaryBaseURL) { + console.log("Move the url to be the primary base URL"); + await setSettings("general", { + primaryBaseURL: url, + }); + } else { + console.log("Already there, no need to move the primary base URL"); + } + } + + /** + * Builds the actions available in the slack message + * @param {string} baseURL Uptime Kuma base URL + * @param {object} monitorJSON The monitor config + * @returns {Array} The relevant action objects + */ + buildActions(baseURL, monitorJSON) { + const actions = []; + + if (baseURL) { + actions.push({ + type: "button", + text: { + type: "plain_text", + text: "Visit Uptime Kuma", + }, + value: "Uptime-Kuma", + url: baseURL + getMonitorRelativeURL(monitorJSON.id), + }); + } + + const address = this.extractAddress(monitorJSON); + if (isUrl(address)) { + try { + actions.push({ + type: "button", + text: { + type: "plain_text", + text: "Visit site", + }, + value: "Site", + url: new URL(address), + }); + } catch (e) { + log.debug("slack", `Failed to parse address ${address} as URL`); + } + } + + return actions; + } + + /** + * Builds the different blocks the Slack message consists of. + * @param {string} baseURL Uptime Kuma base URL + * @param {object} monitorJSON The monitor object + * @param {object} heartbeatJSON The heartbeat object + * @param {string} title The message title + * @param {string} msg The message body + * @param {boolean} includeGroupName Whether to include group name in the message + * @returns {Array} The rich content blocks for the Slack message + */ + buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg, includeGroupName) { + //create an array to dynamically add blocks + const blocks = []; + + // the header block + blocks.push({ + type: "header", + text: { + type: "plain_text", + text: title, + }, + }); + + // Optional context line for monitor group path (excluding monitor name) + if (includeGroupName) { + const groupPath = monitorJSON?.path?.length > 1 ? monitorJSON.path.slice(0, -1).join(" / ") : ""; + if (groupPath) { + blocks.push({ + type: "context", + elements: [ + { + type: "mrkdwn", + text: `_${groupPath}_`, + }, + ], + }); + } + } + + // the body block, containing the details + blocks.push({ + type: "section", + fields: [ + { + type: "mrkdwn", + text: "*Message*\n" + msg, + }, + { + type: "mrkdwn", + text: `*Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`, + }, + ], + }); + + const actions = this.buildActions(baseURL, monitorJSON); + if (actions.length > 0) { + //the actions block, containing buttons + blocks.push({ + type: "actions", + elements: actions, + }); + } + + return blocks; + } + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + if (notification.slackchannelnotify) { + msg += " "; + } + + try { + let config = this.getAxiosConfigWithProxy({}); + if (heartbeatJSON == null) { + let data = { + text: msg, + channel: notification.slackchannel, + username: notification.slackusername, + icon_emoji: notification.slackiconemo, + }; + await axios.post(notification.slackwebhookURL, data, config); + return okMsg; + } + + const baseURL = await setting("primaryBaseURL"); + + // Check if templating is enabled + if (notification.slackUseTemplate) { + const renderedText = await this.renderTemplate( + notification.slackTemplate, + msg, + monitorJSON, + heartbeatJSON + ); + + let data = { + text: renderedText, + channel: notification.slackchannel, + username: notification.slackusername, + icon_emoji: notification.slackiconemo, + }; + + await axios.post(notification.slackwebhookURL, data, config); + return okMsg; + } + + const includeGroupName = notification.slackIncludeGroupName ?? true; + + const groupPath = + includeGroupName && monitorJSON?.path?.length > 1 ? monitorJSON.path.slice(0, -1).join(" / ") : ""; + + const title = monitorJSON?.name || "Uptime Kuma Alert"; + let data = { + text: msg, + channel: notification.slackchannel, + username: notification.slackusername, + icon_emoji: notification.slackiconemo, + attachments: [], + }; + + if (notification.slackrichmessage) { + data.attachments.push({ + color: heartbeatJSON["status"] === UP ? "#2eb886" : "#e01e5a", + blocks: this.buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg, includeGroupName), + }); + } else { + // Include group name in plain text messages if enabled + if (includeGroupName && groupPath) { + data.text = `_${groupPath}_\n${title}\n${msg}`; + } else { + data.text = `${title}\n${msg}`; + } + } + + if (notification.slackbutton) { + await Slack.deprecateURL(notification.slackbutton); + } + + await axios.post(notification.slackwebhookURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Slack; diff --git a/server/notification-providers/sms-gateway.js b/server/notification-providers/sms-gateway.js new file mode 100644 index 0000000..7eb268f --- /dev/null +++ b/server/notification-providers/sms-gateway.js @@ -0,0 +1,55 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class SMSGateway extends NotificationProvider { + name = "SMSGateway"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = notification.smsgatewayUrl.replace(/\/+$/, "") + "/api/v1/sms/send"; + + const recipients = notification.smsgatewayTo + .split(",") + .map((number) => number.trim()) + .filter((number) => number !== ""); + + try { + const config = this.getAxiosConfigWithProxy({ + headers: { + "X-API-Key": notification.smsgatewayApiKey, + }, + }); + + const failures = []; + for (const to of recipients) { + const result = await axios.post( + url, + { + to, + body: msg, + }, + config + ); + + // The gateway responds with HTTP 200 even if the modem could + // not send the message, so the body status must be checked. + if (result.data.status === "failed") { + failures.push(`${to}: ${result.data.message || "unknown error"}`); + } + } + + if (failures.length > 0) { + throw new Error("Failed to send SMS to " + failures.join("; ")); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SMSGateway; diff --git a/server/notification-providers/sms-planet.js b/server/notification-providers/sms-planet.js new file mode 100644 index 0000000..5c3649a --- /dev/null +++ b/server/notification-providers/sms-planet.js @@ -0,0 +1,41 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class SMSPlanet extends NotificationProvider { + name = "SMSPlanet"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://api2.smsplanet.pl/sms"; + + try { + let config = { + headers: { + Authorization: "Bearer " + notification.smsplanetApiToken, + "content-type": "multipart/form-data", + }, + }; + config = this.getAxiosConfigWithProxy(config); + + let data = { + from: notification.smsplanetSenderName, + to: notification.smsplanetPhoneNumbers, + msg: msg.replace(/🔴/, "❌"), + }; + + let response = await axios.post(url, data, config); + if (!response.data?.messageId) { + throw new Error(response.data?.errorMsg ?? "SMSPlanet server did not respond with the expected result"); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SMSPlanet; diff --git a/server/notification-providers/smsc.js b/server/notification-providers/smsc.js new file mode 100644 index 0000000..daa379e --- /dev/null +++ b/server/notification-providers/smsc.js @@ -0,0 +1,48 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class SMSC extends NotificationProvider { + name = "smsc"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://smsc.kz/sys/send.php?"; + + try { + let config = { + headers: { + "Content-Type": "application/json", + Accept: "text/json", + }, + }; + config = this.getAxiosConfigWithProxy(config); + + let getArray = [ + "fmt=3", + "translit=" + notification.smscTranslit, + "login=" + notification.smscLogin, + "psw=" + notification.smscPassword, + "phones=" + notification.smscToNumber, + "mes=" + encodeURIComponent(msg.replace(/[^\x00-\x7F]/g, "")), + ]; + if (notification.smscSenderName !== "") { + getArray.push("sender=" + notification.smscSenderName); + } + + let resp = await axios.get(url + getArray.join("&"), config); + if (resp.data.id === undefined) { + let error = `Something gone wrong. Api returned code ${resp.data.error_code}: ${resp.data.error}`; + this.throwGeneralAxiosError(error); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SMSC; diff --git a/server/notification-providers/smseagle.js b/server/notification-providers/smseagle.js new file mode 100644 index 0000000..5de8af9 --- /dev/null +++ b/server/notification-providers/smseagle.js @@ -0,0 +1,144 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class SMSEagle extends NotificationProvider { + name = "SMSEagle"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + if (notification.smseagleApiType === "smseagle-apiv1") { + // according to https://www.smseagle.eu/apiv1/ + let config = { + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + }; + config = this.getAxiosConfigWithProxy(config); + + let sendMethod; + let recipientType; + let duration; + let voiceId; + + if (notification.smseagleRecipientType === "smseagle-contact") { + recipientType = "contactname"; + sendMethod = "/send_tocontact"; + } else if (notification.smseagleRecipientType === "smseagle-group") { + recipientType = "groupname"; + sendMethod = "/send_togroup"; + } else if (notification.smseagleRecipientType === "smseagle-to") { + recipientType = "to"; + sendMethod = "/send_sms"; + if (notification.smseagleMsgType !== "smseagle-sms") { + duration = notification.smseagleDuration ?? 10; + + if (notification.smseagleMsgType === "smseagle-ring") { + sendMethod = "/ring_call"; + } else if (notification.smseagleMsgType === "smseagle-tts") { + sendMethod = "/tts_call"; + } else if (notification.smseagleMsgType === "smseagle-tts-advanced") { + sendMethod = "/tts_adv_call"; + voiceId = notification.smseagleTtsModel ? notification.smseagleTtsModel : 1; + } + } + } + + const url = new URL(notification.smseagleUrl + "/http_api" + sendMethod); + + url.searchParams.append("access_token", notification.smseagleToken); + url.searchParams.append(recipientType, notification.smseagleRecipient); + if (!notification.smseagleRecipientType || notification.smseagleRecipientType === "smseagle-sms") { + url.searchParams.append("unicode", notification.smseagleEncoding ? "1" : "0"); + url.searchParams.append("highpriority", notification.smseaglePriority ?? "0"); + } else { + url.searchParams.append("duration", duration); + } + if (notification.smseagleRecipientType !== "smseagle-ring") { + url.searchParams.append("message", msg); + } + if (voiceId) { + url.searchParams.append("voice_id", voiceId); + } + + let resp = await axios.get(url.toString(), config); + + if (resp.data.indexOf("OK") === -1) { + let error = `SMSEagle API returned error: ${resp.data}`; + throw new Error(error); + } + + return okMsg; + } else if (notification.smseagleApiType === "smseagle-apiv2") { + // according to https://www.smseagle.eu/docs/apiv2/ + let config = { + headers: { + "access-token": notification.smseagleToken, + "Content-Type": "application/json", + }, + }; + config = this.getAxiosConfigWithProxy(config); + + let encoding = notification.smseagleEncoding ? "unicode" : "standard"; + let priority = notification.smseaglePriority ?? 0; + + let postData = { + text: msg, + encoding: encoding, + priority: priority, + }; + + if (notification.smseagleRecipientContact) { + postData["contacts"] = notification.smseagleRecipientContact.split(",").map(Number); + } + if (notification.smseagleRecipientGroup) { + postData["groups"] = notification.smseagleRecipientGroup.split(",").map(Number); + } + if (notification.smseagleRecipientTo) { + postData["to"] = notification.smseagleRecipientTo.split(","); + } + + let endpoint = "/messages/sms"; + + if (notification.smseagleMsgType !== "smseagle-sms") { + postData["duration"] = notification.smseagleDuration ?? 10; + + if (notification.smseagleMsgType === "smseagle-ring") { + endpoint = "/calls/ring"; + } else if (notification.smseagleMsgType === "smseagle-tts") { + endpoint = "/calls/tts"; + } else if (notification.smseagleMsgType === "smseagle-tts-advanced") { + endpoint = "/calls/tts_advanced"; + postData["voice_id"] = notification.smseagleTtsModel ?? 1; + } + } + + let resp = await axios.post(notification.smseagleUrl + "/api/v2" + endpoint, postData, config); + + const queuedCount = resp.data.filter((x) => x.status === "queued").length; + const unqueuedCount = resp.data.length - queuedCount; + + if (resp.status !== 200 || queuedCount === 0) { + if (!resp.data.length) { + throw new Error("SMSEagle API returned an empty response"); + } + throw new Error(`SMSEagle API returned error: ${JSON.stringify(resp.data)}`); + } + + if (unqueuedCount) { + return `Sent ${queuedCount}/${resp.data.length} Messages Successfully.`; + } + + return okMsg; + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SMSEagle; diff --git a/server/notification-providers/smsir.js b/server/notification-providers/smsir.js new file mode 100644 index 0000000..37c1190 --- /dev/null +++ b/server/notification-providers/smsir.js @@ -0,0 +1,75 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class SMSIR extends NotificationProvider { + name = "smsir"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://api.sms.ir/v1/send/verify"; + + try { + let config = { + headers: { + "Content-Type": "application/json", + Accept: "application/json", + "X-API-Key": notification.smsirApiKey, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + const formattedMobiles = notification.smsirNumber.split(",").map((mobile) => { + if ( + mobile.length === 11 && + mobile.startsWith("09") && + String(parseInt(mobile)) === mobile.substring(1) + ) { + // 09xxxxxxxxx Format + return mobile.substring(1); + } + + return mobile; + }); + + const MAX_MESSAGE_LENGTH = 20; // This is a limitation placed by SMSIR + // Shorten By removing spaces, keeping context is better than cutting off the text + // If that does not work, truncate. Still better than not receiving an SMS + if (msg.length > MAX_MESSAGE_LENGTH) { + msg = msg.replace(/\s/g, ""); + } + + if (msg.length > MAX_MESSAGE_LENGTH) { + msg = msg.substring(0, MAX_MESSAGE_LENGTH - 1 - "...".length) + "..."; + } + + // Run multiple network requests at once + const requestPromises = formattedMobiles.map((mobile) => { + axios.post( + url, + { + mobile: mobile, + templateId: parseInt(notification.smsirTemplate), + parameters: [ + { + name: "uptkumaalert", + value: msg, + }, + ], + }, + config + ); + }); + + await Promise.all(requestPromises); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SMSIR; diff --git a/server/notification-providers/smsmanager.js b/server/notification-providers/smsmanager.js new file mode 100644 index 0000000..0d435cb --- /dev/null +++ b/server/notification-providers/smsmanager.js @@ -0,0 +1,33 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class SMSManager extends NotificationProvider { + name = "SMSManager"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://http-api.smsmanager.cz/Send"; + + try { + let data = { + apikey: notification.smsmanagerApiKey, + message: msg.replace(/[^\x00-\x7F]/g, ""), + number: notification.numbers, + gateway: notification.messageType, + }; + let config = this.getAxiosConfigWithProxy({}); + await axios.get( + `${url}?apikey=${data.apikey}&message=${data.message}&number=${data.number}&gateway=${data.messageType}`, + config + ); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SMSManager; diff --git a/server/notification-providers/smspartner.js b/server/notification-providers/smspartner.js new file mode 100644 index 0000000..a53ed83 --- /dev/null +++ b/server/notification-providers/smspartner.js @@ -0,0 +1,47 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class SMSPartner extends NotificationProvider { + name = "SMSPartner"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://api.smspartner.fr/v1/send"; + + try { + // smspartner does not support non ascii characters and only a maximum 639 characters + let cleanMsg = msg.replace(/[^\x00-\x7F]/g, "").substring(0, 639); + + let data = { + apiKey: notification.smspartnerApikey, + sender: notification.smspartnerSenderName.substring(0, 11), + phoneNumbers: notification.smspartnerPhoneNumber, + message: cleanMsg, + }; + + let config = { + headers: { + "Content-Type": "application/json", + "cache-control": "no-cache", + Accept: "application/json", + }, + }; + config = this.getAxiosConfigWithProxy(config); + + let resp = await axios.post(url, data, config); + + if (resp.data.success !== true) { + throw Error(`Api returned ${resp.data.response.status}.`); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SMSPartner; diff --git a/server/notification-providers/smtp.js b/server/notification-providers/smtp.js new file mode 100644 index 0000000..acd2ac3 --- /dev/null +++ b/server/notification-providers/smtp.js @@ -0,0 +1,108 @@ +const nodemailer = require("nodemailer"); +const NotificationProvider = require("./notification-provider"); +const { log } = require("../../src/util"); + +class SMTP extends NotificationProvider { + name = "smtp"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + const config = { + host: notification.smtpHost, + port: notification.smtpPort, + secure: notification.smtpSecure, + headers: {}, + }; + + // Handle TLS/STARTTLS options + if (!notification.smtpSecure && notification.smtpIgnoreSTARTTLS) { + // Disable STARTTLS completely for servers that don't support it + // Connection will remain unencrypted + log.warn( + "notification", + `SMTP notification using unencrypted connection (STARTTLS disabled) to ${notification.smtpHost}:${notification.smtpPort}` + ); + config.ignoreTLS = true; + } else { + // SMTPS (implicit TLS on port 465) + // or STARTTLS (default behavior for ports 25, 587) + config.tls = { + rejectUnauthorized: !notification.smtpIgnoreTLSError || false, + }; + } + + // Fix #1129 + if (notification.smtpDkimDomain) { + config.dkim = { + domainName: notification.smtpDkimDomain, + keySelector: notification.smtpDkimKeySelector, + privateKey: notification.smtpDkimPrivateKey, + hashAlgo: notification.smtpDkimHashAlgo, + headerFieldNames: notification.smtpDkimheaderFieldNames, + skipFields: notification.smtpDkimskipFields, + }; + } + + // Should fix the issue in https://github.com/louislam/uptime-kuma/issues/26#issuecomment-896373904 + if (notification.smtpUsername || notification.smtpPassword) { + config.auth = { + user: notification.smtpUsername, + pass: notification.smtpPassword, + }; + } + + // Handle additional headers + if (notification.smtpAdditionalHeaders) { + try { + config.headers = { + ...config.headers, + ...JSON.parse(notification.smtpAdditionalHeaders), + }; + } catch (err) { + throw new Error("Additional Headers is not a valid JSON"); + } + } + + // default values in case the user does not want to template + let subject = msg; + let body = msg; + let useHTMLBody = false; + if (heartbeatJSON) { + body = `${msg}\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`; + } + // subject and body are templated + if ((monitorJSON && heartbeatJSON) || msg.endsWith("Testing")) { + // cannot end with whitespace as this often raises spam scores + const customSubject = notification.customSubject?.trim() || ""; + const customBody = notification.customBody?.trim() || ""; + if (customSubject !== "") { + subject = await this.renderTemplate(customSubject, msg, monitorJSON, heartbeatJSON); + } + if (customBody !== "") { + useHTMLBody = notification.htmlBody || false; + body = await this.renderTemplate(customBody, msg, monitorJSON, heartbeatJSON); + } + } + + // send mail with defined transport object + let transporter = nodemailer.createTransport(config); + await transporter.sendMail({ + from: notification.smtpFrom, + cc: notification.smtpCC, + bcc: notification.smtpBCC, + to: notification.smtpTo, + subject: subject, + headers: config.headers, + // If the email body is custom, and the user wants it, set the email body as HTML + [useHTMLBody ? "html" : "text"]: body, + }); + + return okMsg; + } +} + +module.exports = SMTP; diff --git a/server/notification-providers/splunk.js b/server/notification-providers/splunk.js new file mode 100644 index 0000000..885eba6 --- /dev/null +++ b/server/notification-providers/splunk.js @@ -0,0 +1,113 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); +const { Settings } = require("../settings"); +let successMessage = "Sent Successfully."; + +class Splunk extends NotificationProvider { + name = "Splunk"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + try { + if (heartbeatJSON == null) { + const title = "Uptime Kuma Alert"; + const monitor = { + type: "ping", + url: "Uptime Kuma Test Button", + }; + return this.postNotification(notification, title, msg, monitor, "trigger"); + } + + if (heartbeatJSON.status === UP) { + const title = "Uptime Kuma Monitor ✅ Up"; + return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "recovery"); + } + + if (heartbeatJSON.status === DOWN) { + const title = "Uptime Kuma Monitor 🔴 Down"; + return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "trigger"); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Check if result is successful, result code should be in range 2xx + * @param {object} result Axios response object + * @returns {void} + * @throws {Error} The status code is not in range 2xx + */ + checkResult(result) { + if (result.status == null) { + throw new Error("Splunk notification failed with invalid response!"); + } + if (result.status < 200 || result.status >= 300) { + throw new Error("Splunk notification failed with status code " + result.status); + } + } + + /** + * Send the message + * @param {BeanModel} notification Message title + * @param {string} title Message title + * @param {string} body Message + * @param {object} monitorInfo Monitor details (For Up/Down only) + * @param {?string} eventAction Action event for PagerDuty (trigger, acknowledge, resolve) + * @returns {Promise} Success state + */ + async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") { + let monitorUrl; + if (monitorInfo.type === "port") { + monitorUrl = monitorInfo.hostname; + if (monitorInfo.port) { + monitorUrl += ":" + monitorInfo.port; + } + } else if (monitorInfo.hostname != null) { + monitorUrl = monitorInfo.hostname; + } else { + monitorUrl = monitorInfo.url; + } + + if (eventAction === "recovery") { + if (notification.splunkAutoResolve === "0") { + return "No action required"; + } + eventAction = notification.splunkAutoResolve; + } else { + eventAction = notification.splunkSeverity; + } + + const options = { + method: "POST", + url: notification.splunkRestURL, + headers: { "Content-Type": "application/json" }, + data: { + message_type: eventAction, + state_message: `[${title}] [${monitorUrl}] ${body}`, + entity_display_name: "Uptime Kuma Alert: " + monitorInfo.name, + routing_key: notification.pagerdutyIntegrationKey, + entity_id: "Uptime Kuma/" + monitorInfo.id, + }, + }; + + const baseURL = await Settings.get("primaryBaseURL"); + if (baseURL && monitorInfo) { + options.client = "Uptime Kuma"; + options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id); + } + + let result = await axios.request(options); + this.checkResult(result); + if (result.statusText != null) { + return "Splunk notification succeed: " + result.statusText; + } + + return successMessage; + } +} + +module.exports = Splunk; diff --git a/server/notification-providers/spugpush.js b/server/notification-providers/spugpush.js new file mode 100644 index 0000000..5da901e --- /dev/null +++ b/server/notification-providers/spugpush.js @@ -0,0 +1,37 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class SpugPush extends NotificationProvider { + name = "SpugPush"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + let okMsg = "Sent Successfully."; + try { + let formData = { + title: "Uptime Kuma Message", + content: msg, + }; + if (heartbeatJSON) { + if (heartbeatJSON["status"] === UP) { + formData.title = `UptimeKuma 「${monitorJSON["name"]}」 is Up`; + formData.content = `[✅ Up] ${heartbeatJSON["msg"]}`; + } else if (heartbeatJSON["status"] === DOWN) { + formData.title = `UptimeKuma 「${monitorJSON["name"]}」 is Down`; + formData.content = `[🔴 Down] ${heartbeatJSON["msg"]}`; + } + } + const apiUrl = `https://push.spug.cc/send/${notification.templateKey}`; + let config = this.getAxiosConfigWithProxy({}); + await axios.post(apiUrl, formData, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = SpugPush; diff --git a/server/notification-providers/squadcast.js b/server/notification-providers/squadcast.js new file mode 100644 index 0000000..ea4838f --- /dev/null +++ b/server/notification-providers/squadcast.js @@ -0,0 +1,57 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN } = require("../../src/util"); + +class Squadcast extends NotificationProvider { + name = "squadcast"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = {}; + let data = { + message: msg, + description: "", + tags: {}, + heartbeat: heartbeatJSON, + source: "uptime-kuma", + }; + + if (heartbeatJSON !== null) { + data.description = heartbeatJSON["msg"]; + data.event_id = heartbeatJSON["monitorID"]; + + if (heartbeatJSON["status"] === DOWN) { + data.message = `${monitorJSON["name"]} is DOWN`; + data.status = "trigger"; + } else { + data.message = `${monitorJSON["name"]} is UP`; + data.status = "resolve"; + } + + data.tags["AlertAddress"] = this.extractAddress(monitorJSON); + + monitorJSON["tags"].forEach((tag) => { + data.tags[tag["name"]] = { + value: tag["value"], + }; + if (tag["color"] !== null) { + data.tags[tag["name"]]["color"] = tag["color"]; + } + }); + } + config = this.getAxiosConfigWithProxy(config); + + await axios.post(notification.squadcastWebhookURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Squadcast; diff --git a/server/notification-providers/stackfield.js b/server/notification-providers/stackfield.js new file mode 100644 index 0000000..18914b0 --- /dev/null +++ b/server/notification-providers/stackfield.js @@ -0,0 +1,45 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { getMonitorRelativeURL } = require("../../src/util"); +const { Settings } = require("../settings"); + +class Stackfield extends NotificationProvider { + name = "stackfield"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + // Stackfield message formatting: https://www.stackfield.com/help/formatting-messages-2001 + + let textMsg = "+Uptime Kuma Alert+"; + + if (monitorJSON && monitorJSON.name) { + textMsg += `\n*${monitorJSON.name}*`; + } + + textMsg += `\n${msg}`; + + const baseURL = await Settings.get("primaryBaseURL"); + if (baseURL) { + const urlPath = monitorJSON ? getMonitorRelativeURL(monitorJSON.id) : "/"; + textMsg += `\n${baseURL + urlPath}`; + } + + const data = { + Title: textMsg, + }; + let config = this.getAxiosConfigWithProxy({}); + + await axios.post(notification.stackfieldwebhookURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Stackfield; diff --git a/server/notification-providers/teams.js b/server/notification-providers/teams.js new file mode 100644 index 0000000..5fd93af --- /dev/null +++ b/server/notification-providers/teams.js @@ -0,0 +1,275 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { setting } = require("../util-server"); +const { DOWN, UP, getMonitorRelativeURL } = require("../../src/util"); + +class Teams extends NotificationProvider { + name = "teams"; + + /** + * Generate the message to send + * @param {const} status The status constant + * @param {string} monitorName Name of monitor + * @param {boolean} withStatusSymbol If the status should be prepended as symbol + * @returns {string} Status message + */ + _statusMessageFactory = (status, monitorName, withStatusSymbol) => { + if (status === DOWN) { + return (withStatusSymbol ? "🔴 " : "") + `[${monitorName}] went down`; + } else if (status === UP) { + return (withStatusSymbol ? "✅ " : "") + `[${monitorName}] is back online`; + } + return "Notification"; + }; + + /** + * Select the style to use based on status + * @param {const} status The status constant + * @returns {string} Selected style for adaptive cards + */ + _getStyle = (status) => { + if (status === DOWN) { + return "attention"; + } + if (status === UP) { + return "good"; + } + return "emphasis"; + }; + + /** + * Format the tag for display. If the tag has a value, display as "name: value", otherwise just "name". + * @param {object} tag The tag object to format + * @returns {string} Formatted tag for display + */ + _tagDisplayText = (tag) => { + if (tag.value === "" || tag.value === undefined || tag.value === null) { + return tag.name; + } else { + return `${tag.name}: ${tag.value}`; + } + }; + + /** + * Generate payload for notification + * @param {object} args Method arguments + * @param {object} args.heartbeatJSON Heartbeat details + * @param {object} args.monitorJSON Monitor details + * @param {string} args.dashboardUrl URL of the dashboard affected + * @param {boolean} args.enableTags Whether to include tags in the notification + * @returns {object} Notification payload + */ + _notificationPayloadFactory = ({ heartbeatJSON, monitorJSON, dashboardUrl, enableTags }) => { + const monitorUrl = this.extractAddress(monitorJSON); + const monitorName = monitorJSON?.name; + const status = heartbeatJSON?.status; + const facts = []; + const actions = []; + + if (dashboardUrl) { + actions.push({ + type: "Action.OpenUrl", + title: "Visit Uptime Kuma", + url: dashboardUrl, + }); + } + + if (heartbeatJSON?.msg) { + facts.push({ + title: "Description", + value: heartbeatJSON.msg, + }); + } + + if (monitorName) { + facts.push({ + title: "Monitor", + value: monitorName, + }); + } + + if (monitorUrl && monitorUrl !== "https://") { + facts.push({ + title: "URL", + // format URL as markdown syntax, to be clickable + value: `[${monitorUrl}](${monitorUrl})`, + }); + actions.push({ + type: "Action.OpenUrl", + title: "Visit Monitor URL", + url: monitorUrl, + }); + } + + if (heartbeatJSON?.localDateTime) { + facts.push({ + title: "Time", + value: heartbeatJSON.localDateTime + (heartbeatJSON.timezone ? ` (${heartbeatJSON.timezone})` : ""), + }); + } + + const payloadBody = [ + { + type: "Container", + verticalContentAlignment: "Center", + items: [ + { + type: "ColumnSet", + style: this._getStyle(status), + columns: [ + { + type: "Column", + width: "auto", + verticalContentAlignment: "Center", + items: [ + { + type: "Image", + width: "32px", + style: "Person", + url: "https://raw.githubusercontent.com/louislam/uptime-kuma/master/public/icon.png", + altText: "Uptime Kuma Logo", + }, + ], + }, + { + type: "Column", + width: "stretch", + items: [ + { + type: "TextBlock", + size: "Medium", + weight: "Bolder", + text: `**${this._statusMessageFactory(status, monitorName, false)}**`, + }, + { + type: "TextBlock", + size: "Small", + weight: "Default", + text: "Uptime Kuma Alert", + isSubtle: true, + spacing: "None", + }, + ], + }, + ], + }, + ], + }, + { + type: "FactSet", + separator: false, + facts: facts, + }, + ]; + + if (enableTags && monitorJSON?.tags?.length > 0) { + payloadBody.push({ + type: "Container", + layouts: [ + { + type: "Layout.Flow", + columnSpacing: "Small", + rowSpacing: "Small", + horizontalItemsAlignment: "Left", + }, + ], + items: monitorJSON.tags.map((tag) => { + return { + type: "Badge", + text: this._tagDisplayText(tag), + size: "Medium", + style: "Accent", + }; + }), + }); + } + + const payload = { + type: "message", + // message with status prefix as notification text + summary: this._statusMessageFactory(status, monitorName, true), + attachments: [ + { + contentType: "application/vnd.microsoft.card.adaptive", + contentUrl: "", + content: { + type: "AdaptiveCard", + body: payloadBody, + $schema: "http://adaptivecards.io/schemas/adaptive-card.json", + version: "1.5", + }, + }, + ], + }; + + if (actions) { + payload.attachments[0].content.body.push({ + type: "ActionSet", + actions: actions, + }); + } + + return payload; + }; + + /** + * Send the notification + * @param {string} webhookUrl URL to send the request to + * @param {object} payload Payload generated by _notificationPayloadFactory + * @returns {Promise} + */ + _sendNotification = async (webhookUrl, payload) => { + let config = this.getAxiosConfigWithProxy({}); + await axios.post(webhookUrl, payload, config); + }; + + /** + * Send a general notification + * @param {string} webhookUrl URL to send request to + * @param {string} msg Message to send + * @returns {Promise} + */ + _handleGeneralNotification = (webhookUrl, msg) => { + const payload = this._notificationPayloadFactory({ + heartbeatJSON: { + msg: msg, + }, + }); + + return this._sendNotification(webhookUrl, payload); + }; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + if (heartbeatJSON == null) { + await this._handleGeneralNotification(notification.webhookUrl, msg); + return okMsg; + } + + const baseURL = await setting("primaryBaseURL"); + let dashboardUrl; + if (baseURL) { + dashboardUrl = baseURL + getMonitorRelativeURL(monitorJSON.id); + } + + const payload = this._notificationPayloadFactory({ + heartbeatJSON: heartbeatJSON, + monitorJSON: monitorJSON, + dashboardUrl: dashboardUrl, + enableTags: notification.teamsEnableTags ?? false, + }); + + await this._sendNotification(notification.webhookUrl, payload); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Teams; diff --git a/server/notification-providers/techulus-push.js b/server/notification-providers/techulus-push.js new file mode 100644 index 0000000..a04ab0a --- /dev/null +++ b/server/notification-providers/techulus-push.js @@ -0,0 +1,37 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class TechulusPush extends NotificationProvider { + name = "PushByTechulus"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + let data = { + title: notification?.pushTitle?.length ? notification.pushTitle : "Uptime-Kuma", + body: msg, + timeSensitive: notification.pushTimeSensitive ?? true, + }; + + if (notification.pushChannel) { + data.channel = notification.pushChannel; + } + + if (notification.pushSound) { + data.sound = notification.pushSound; + } + + try { + let config = this.getAxiosConfigWithProxy({}); + await axios.post(`https://push.techulus.com/api/v1/notify/${notification.pushAPIKey}`, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = TechulusPush; diff --git a/server/notification-providers/telegram.js b/server/notification-providers/telegram.js new file mode 100644 index 0000000..9aa598f --- /dev/null +++ b/server/notification-providers/telegram.js @@ -0,0 +1,112 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Telegram extends NotificationProvider { + name = "telegram"; + + /** + * Escapes special characters for Telegram MarkdownV2 format + * @param {string} text Text to escape + * @returns {string} Escaped text + */ + escapeMarkdownV2(text) { + if (!text) { + return text; + } + + // Characters that need to be escaped in MarkdownV2 + // https://core.telegram.org/bots/api#markdownv2-style + return String(text).replace(/[_*[\]()~>#+\-=|{}.!\\]/g, "\\$&"); + } + + /** + * Recursively escapes string properties of an object for Telegram MarkdownV2 + * @param {object|string} obj Object or string to escape + * @returns {object|string} Escaped object or string + */ + escapeObjectRecursive(obj) { + if (typeof obj === "string") { + return this.escapeMarkdownV2(obj); + } + if (typeof obj === "object" && obj !== null) { + // Check if array + if (Array.isArray(obj)) { + return obj.map((item) => this.escapeObjectRecursive(item)); + } + + const newObj = {}; + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + newObj[key] = this.escapeObjectRecursive(obj[key]); + } + } + return newObj; + } + return obj; + } + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = notification.telegramServerUrl ?? "https://api.telegram.org"; + + try { + let params = { + chat_id: notification.telegramChatID, + text: msg, + disable_notification: notification.telegramSendSilently ?? false, + protect_content: notification.telegramProtectContent ?? false, + link_preview_options: { is_disabled: true }, + }; + if (notification.telegramMessageThreadID) { + params.message_thread_id = notification.telegramMessageThreadID; + } + + if (notification.telegramUseTemplate) { + let monitorJSONCopy = monitorJSON; + let heartbeatJSONCopy = heartbeatJSON; + + if (notification.telegramTemplateParseMode === "MarkdownV2") { + msg = this.escapeMarkdownV2(msg); + + if (monitorJSONCopy) { + monitorJSONCopy = this.escapeObjectRecursive(monitorJSONCopy); + } else { + // for testing monitorJSON is null, provide escaped defaults + monitorJSONCopy = { + name: this.escapeMarkdownV2("Monitor Name not available"), + hostname: this.escapeMarkdownV2("testing.hostname"), + url: this.escapeMarkdownV2("testing.hostname"), + }; + } + + if (heartbeatJSONCopy) { + heartbeatJSONCopy = this.escapeObjectRecursive(heartbeatJSONCopy); + } + } + + params.text = await this.renderTemplate( + notification.telegramTemplate, + msg, + monitorJSONCopy, + heartbeatJSONCopy + ); + + if (notification.telegramTemplateParseMode !== "plain") { + params.parse_mode = notification.telegramTemplateParseMode; + } + } + + let config = this.getAxiosConfigWithProxy(); + + await axios.post(`${url}/bot${notification.telegramBotToken}/sendMessage`, params, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Telegram; diff --git a/server/notification-providers/telnyx.js b/server/notification-providers/telnyx.js new file mode 100644 index 0000000..53e21df --- /dev/null +++ b/server/notification-providers/telnyx.js @@ -0,0 +1,41 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Telnyx extends NotificationProvider { + name = "telnyx"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let data = { + from: notification.telnyxPhoneNumber, + to: notification.telnyxToNumber, + text: msg, + }; + + if (notification.telnyxMessagingProfileId) { + data.messaging_profile_id = notification.telnyxMessagingProfileId; + } + + let config = { + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + notification.telnyxApiKey, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + await axios.post("https://api.telnyx.com/v2/messages", data, config); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Telnyx; diff --git a/server/notification-providers/teltonika.js b/server/notification-providers/teltonika.js new file mode 100644 index 0000000..f3d0896 --- /dev/null +++ b/server/notification-providers/teltonika.js @@ -0,0 +1,95 @@ +// This notification provider is only compatible with Teltonika RMS >= 7.14.0 devices. +// See: https://community.teltonika.lt/t/implementation-of-read-only-system-files-and-mobile-and-i-o-post-get-service-removal-with-rutos-7-14/12470 +// API reference https://developers.teltonika-networks.com/reference/rut241/7.19.4/v1.11.1/messages + +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const https = require("https"); + +class Teltonika extends NotificationProvider { + name = "Teltonika"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + // baseUrl is passed via the configuration screen. + // Must be limited to _just_ the full origin, so: proto://host:port. + // Everything else should be stripped. Best way to validate is to use URL(). + + let passedUrl = ""; + try { + passedUrl = new URL(notification.teltonikaUrl); + } catch (error) { + throw Error("Invalid URL: " + notification.teltonikaUrl); + } + + const baseUrl = passedUrl.origin; + const loginUrl = baseUrl + "/api/login"; + const smsUrl = baseUrl + "/api/messages/actions/send"; + + // Teltonika SMS gateway supports a max of 160 chars for its messages. + const cleanMsg = msg.substring(0, 159); + + // Starting communications with the API from here on out. + try { + let axiosConfig = { + headers: { + "Content-Type": "application/json", + "cache-control": "no-cache", + Accept: "application/json", + }, + }; + + // In many cases, Teltonika routers will be setup using a self-signed + // certificate. Here we give them an option to disable certificate + // validation. It's not desirable, but sometimes the only option. + if (notification.teltonikaUnsafeTls) { + axiosConfig.httpsAgent = new https.Agent({ + rejectUnauthorized: false, // Danger! Disables SSL verification + }); + } + + axiosConfig = this.getAxiosConfigWithProxy(axiosConfig); + + // Logging in, to get an access token. + // API reference https://developers.teltonika-networks.com/reference/rut241/7.19.4/v1.11.1/authentication + // Teltonika's API access tokens expire in 5 minutes, so we always get a new one. + let loginData = { + username: notification.teltonikaUsername, + password: notification.teltonikaPassword, + }; + + let loginResp = await axios.post(loginUrl, loginData, axiosConfig); + + if (loginResp.data.success !== true) { + throw Error("Login failed: " + loginResp.data.errors.error); + } + + // Sending the SMS. + let smsData = { + data: { + modem: notification.teltonikaModem, + number: notification.teltonikaPhoneNumber, + message: cleanMsg, + }, + }; + + axiosConfig.headers.Authorization = "Bearer " + loginResp.data.data.token; + + let smsResp = await axios.post(smsUrl, smsData, axiosConfig); + + if (smsResp.data.success !== true) { + throw Error("Api returned: ", smsResp.data.errors.error); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Teltonika; diff --git a/server/notification-providers/threema.js b/server/notification-providers/threema.js new file mode 100644 index 0000000..b1b71dd --- /dev/null +++ b/server/notification-providers/threema.js @@ -0,0 +1,78 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Threema extends NotificationProvider { + name = "threema"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const url = "https://msgapi.threema.ch/send_simple"; + + let config = { + headers: { + Accept: "*/*", + "Content-Type": "application/x-www-form-urlencoded; charset=utf-8", + }, + }; + config = this.getAxiosConfigWithProxy(config); + + const data = { + from: notification.threemaSenderIdentity, + secret: notification.threemaSecret, + text: msg, + }; + + switch (notification.threemaRecipientType) { + case "identity": + data.to = notification.threemaRecipient; + break; + case "phone": + data.phone = notification.threemaRecipient; + break; + case "email": + data.email = notification.threemaRecipient; + break; + default: + throw new Error(`Unsupported recipient type: ${notification.threemaRecipientType}`); + } + + try { + await axios.post(url, new URLSearchParams(data), config); + return "Threema notification sent successfully."; + } catch (error) { + const errorMessage = this.handleApiError(error); + this.throwGeneralAxiosError(errorMessage); + } + } + + /** + * Handle Threema API errors + * @param {any} error The error to handle + * @returns {string} Additional error context + */ + handleApiError(error) { + if (!error.response) { + return error.message; + } + switch (error.response.status) { + case 400: + return "Invalid recipient identity or account not set up for basic mode (400)."; + case 401: + return "Incorrect API identity or secret (401)."; + case 402: + return "No credits remaining (402)."; + case 404: + return "Recipient not found (404)."; + case 413: + return "Message is too long (413)."; + case 500: + return "Temporary internal server error (500)."; + default: + return error.message; + } + } +} + +module.exports = Threema; diff --git a/server/notification-providers/turbosmtp.js b/server/notification-providers/turbosmtp.js new file mode 100644 index 0000000..22a4f7d --- /dev/null +++ b/server/notification-providers/turbosmtp.js @@ -0,0 +1,55 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class TurboSMTP extends NotificationProvider { + name = "TurboSMTP"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + "Content-Type": "application/json", + consumerKey: notification.turbosmtpConsumerKey, + consumerSecret: notification.turbosmtpConsumerSecret, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + const host = notification.turbosmtpRegion === "eu" ? "api.eu.turbo-smtp.com" : "api.turbo-smtp.com"; + + let data = { + from: notification.turbosmtpFromEmail.trim(), + to: notification.turbosmtpToEmail, + subject: notification.turbosmtpSubject || "Notification from Your Uptime Kuma", + content: msg, + }; + + // Comma-separated lists, same format as "to" + if (notification.turbosmtpCcEmail) { + data.cc = notification.turbosmtpCcEmail + .split(",") + .map((email) => email.trim()) + .join(","); + } + + if (notification.turbosmtpBccEmail) { + data.bcc = notification.turbosmtpBccEmail + .split(",") + .map((email) => email.trim()) + .join(","); + } + + await axios.post(`https://${host}/api/v2/mail/send`, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = TurboSMTP; diff --git a/server/notification-providers/twilio.js b/server/notification-providers/twilio.js new file mode 100644 index 0000000..c2de00a --- /dev/null +++ b/server/notification-providers/twilio.js @@ -0,0 +1,46 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Twilio extends NotificationProvider { + name = "twilio"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + let apiKey = notification.twilioApiKey ? notification.twilioApiKey : notification.twilioAccountSID; + + try { + let config = { + headers: { + "Content-Type": "application/x-www-form-urlencoded;charset=utf-8", + Authorization: + "Basic " + Buffer.from(apiKey + ":" + notification.twilioAuthToken).toString("base64"), + }, + }; + config = this.getAxiosConfigWithProxy(config); + + let data = new URLSearchParams(); + data.append("To", notification.twilioToNumber); + data.append("From", notification.twilioFromNumber); + data.append("Body", msg); + if (notification.twilioMessagingServiceSID) { + data.append("MessagingServiceSid", notification.twilioMessagingServiceSID); + } + + await axios.post( + `https://api.twilio.com/2010-04-01/Accounts/${notification.twilioAccountSID}/Messages.json`, + data, + config + ); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Twilio; diff --git a/server/notification-providers/vk.js b/server/notification-providers/vk.js new file mode 100644 index 0000000..9912fa9 --- /dev/null +++ b/server/notification-providers/vk.js @@ -0,0 +1,44 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class VK extends NotificationProvider { + name = "VK"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const url = "https://api.vk.ru/method/messages.send"; + + try { + const data = new URLSearchParams({ + access_token: notification.vkAccessToken, + v: notification.vkApiVersion, + peer_id: notification.vkPeerId, + message: msg, + dont_parse_links: notification.vkDontParseLinks ? "1" : "0", + random_id: String(Math.floor(Math.random() * 2147483647)), + }); + + const config = this.getAxiosConfigWithProxy({}); + const response = await axios.post(url, data, config); + + if (response.data?.error) { + throw new Error( + `VK API returned error ${response.data.error.error_code}: ${response.data.error.error_msg}` + ); + } + + if (typeof response.data?.response === "undefined") { + throw new Error("Invalid VK API response"); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = VK; diff --git a/server/notification-providers/vkteams.js b/server/notification-providers/vkteams.js new file mode 100644 index 0000000..4db0fee --- /dev/null +++ b/server/notification-providers/vkteams.js @@ -0,0 +1,48 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class VKTeams extends NotificationProvider { + name = "VKTeams"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + const baseUrl = (notification.vkteamsBaseUrl || "https://myteam.mail.ru").replace(/\/$/, ""); + + try { + const rawParams = { + token: notification.vkteamsBotToken, + chatId: notification.vkteamsChatId, + text: msg, + }; + + if (notification.vkteamsUseTemplate && notification.vkteamsTemplate) { + rawParams.text = await this.renderTemplate( + notification.vkteamsTemplate, + msg, + monitorJSON, + heartbeatJSON + ); + + if (notification.vkteamsTemplateFormat && notification.vkteamsTemplateFormat !== "plain") { + rawParams.parseMode = notification.vkteamsTemplateFormat; + } + } + + const params = new URLSearchParams(rawParams).toString(); + const config = this.getAxiosConfigWithProxy({}); + const response = await axios.get(`${baseUrl}/bot/v1/messages/sendText?${params}`, config); + if (response.data?.ok === false) { + throw new Error(`VKTeams API returned error: ${response.data.description}`); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = VKTeams; diff --git a/server/notification-providers/waha.js b/server/notification-providers/waha.js new file mode 100644 index 0000000..4b745fe --- /dev/null +++ b/server/notification-providers/waha.js @@ -0,0 +1,40 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class WAHA extends NotificationProvider { + name = "waha"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + "X-Api-Key": notification.wahaApiKey, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + let data = { + session: notification.wahaSession, + chatId: notification.wahaChatId, + text: msg, + }; + + let url = notification.wahaApiUrl.replace(/([^/])\/+$/, "$1") + "/api/sendText"; + + await axios.post(url, data, config); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = WAHA; diff --git a/server/notification-providers/webhook.js b/server/notification-providers/webhook.js new file mode 100644 index 0000000..207646b --- /dev/null +++ b/server/notification-providers/webhook.js @@ -0,0 +1,73 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const FormData = require("form-data"); + +class Webhook extends NotificationProvider { + name = "webhook"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + const httpMethod = notification.httpMethod?.toLowerCase() || "post"; + + let data = { + heartbeat: heartbeatJSON, + monitor: monitorJSON, + msg, + }; + let config = { + headers: {}, + }; + + if (httpMethod === "get") { + config.params = { + msg: msg, + }; + + if (heartbeatJSON) { + config.params.heartbeat = JSON.stringify(heartbeatJSON); + } + + if (monitorJSON) { + config.params.monitor = JSON.stringify(monitorJSON); + } + } else if (notification.webhookContentType === "form-data") { + const formData = new FormData(); + formData.append("data", JSON.stringify(data)); + config.headers = formData.getHeaders(); + data = formData; + } else if (notification.webhookContentType === "custom") { + data = await this.renderTemplate(notification.webhookCustomBody, msg, monitorJSON, heartbeatJSON); + } + + if (notification.webhookAdditionalHeaders) { + try { + config.headers = { + ...config.headers, + ...JSON.parse(notification.webhookAdditionalHeaders), + }; + } catch (err) { + throw new Error("Additional Headers is not a valid JSON"); + } + } + + config = this.getAxiosConfigWithProxy(config); + + if (httpMethod === "get") { + await axios.get(notification.webhookURL, config); + } else { + await axios.post(notification.webhookURL, data, config); + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Webhook; diff --git a/server/notification-providers/wecom.js b/server/notification-providers/wecom.js new file mode 100644 index 0000000..d0fbc4a --- /dev/null +++ b/server/notification-providers/wecom.js @@ -0,0 +1,72 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class WeCom extends NotificationProvider { + name = "WeCom"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + "Content-Type": "application/json", + }, + }; + config = this.getAxiosConfigWithProxy(config); + let body = this.composeMessage(notification, heartbeatJSON, msg); + await axios.post( + `https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${notification.weComBotKey}`, + body, + config + ); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Generate the message to send + * @param {object} notification Notification configuration + * @param {object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {string} msg General message + * @returns {object} Message + */ + composeMessage(notification, heartbeatJSON, msg) { + let title = "UptimeKuma Message"; + if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] === UP) { + title = "UptimeKuma Monitor Up"; + } + if (msg != null && heartbeatJSON != null && heartbeatJSON["status"] === DOWN) { + title = "UptimeKuma Monitor Down"; + } + + let textObj = { + content: title + "\n" + msg, + }; + + // Handle mentioned_mobile_list if configured + if (notification.weComMentionedMobileList?.trim()) { + let mentionedMobiles = notification.weComMentionedMobileList + .split(",") + .map((mobile) => mobile.trim()) + .filter((mobile) => mobile.length > 0); + + if (mentionedMobiles.length > 0) { + textObj.mentioned_mobile_list = mentionedMobiles; + } + } + + return { + msgtype: "text", + text: textObj, + }; + } +} + +module.exports = WeCom; diff --git a/server/notification-providers/whapi.js b/server/notification-providers/whapi.js new file mode 100644 index 0000000..47d1b80 --- /dev/null +++ b/server/notification-providers/whapi.js @@ -0,0 +1,41 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Whapi extends NotificationProvider { + name = "whapi"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let config = { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + Authorization: "Bearer " + notification.whapiAuthToken, + }, + }; + config = this.getAxiosConfigWithProxy(config); + + let data = { + to: notification.whapiRecipient, + body: msg, + }; + + let url = + (notification.whapiApiUrl || "https://gate.whapi.cloud/").replace(/([^/])\/+$/, "$1") + + "/messages/text"; + + await axios.post(url, data, config); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Whapi; diff --git a/server/notification-providers/wpush.js b/server/notification-providers/wpush.js new file mode 100644 index 0000000..51b1e6d --- /dev/null +++ b/server/notification-providers/wpush.js @@ -0,0 +1,51 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class WPush extends NotificationProvider { + name = "WPush"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + const context = { + title: this.checkStatus(heartbeatJSON, monitorJSON), + content: msg, + apikey: notification.wpushAPIkey, + channel: notification.wpushChannel, + }; + let config = this.getAxiosConfigWithProxy({}); + const result = await axios.post("https://api.wpush.cn/api/v1/send", context, config); + if (result.data.code !== 0) { + throw result.data.message; + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Get the formatted title for message + * @param {?object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {?object} monitorJSON Monitor details (For Up/Down only) + * @returns {string} Formatted title + */ + checkStatus(heartbeatJSON, monitorJSON) { + let title = "UptimeKuma Message"; + if (heartbeatJSON != null && heartbeatJSON["status"] === UP) { + title = "UptimeKuma Monitor Up " + monitorJSON["name"]; + } + if (heartbeatJSON != null && heartbeatJSON["status"] === DOWN) { + title = "UptimeKuma Monitor Down " + monitorJSON["name"]; + } + return title; + } +} + +module.exports = WPush; diff --git a/server/notification-providers/wxpusher.js b/server/notification-providers/wxpusher.js new file mode 100644 index 0000000..0eb8427 --- /dev/null +++ b/server/notification-providers/wxpusher.js @@ -0,0 +1,73 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class WxPusher extends NotificationProvider { + name = "WxPusher"; + + // WxPusher's simple-push accepts at most 10 SPTs per request. + static SPT_PER_REQUEST = 10; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + // Accept one or multiple SPTs, comma-separated. + const sptList = String(notification.wxpusherSPT || "") + .split(",") + .map((spt) => spt.trim()) + .filter((spt) => spt.length > 0); + + try { + if (sptList.length === 0) { + throw new Error("No WxPusher SPT is configured"); + } + + const summary = this.checkStatus(heartbeatJSON, monitorJSON).slice(0, 100); + const config = this.getAxiosConfigWithProxy({}); + + // Send in batches so more than 10 SPTs are all delivered, never silently dropped. + for (let i = 0; i < sptList.length; i += WxPusher.SPT_PER_REQUEST) { + const context = { + content: msg, + summary, + contentType: 1, + sptList: sptList.slice(i, i + WxPusher.SPT_PER_REQUEST), + }; + const result = await axios.post( + "https://wxpusher.zjiecode.com/api/send/message/simple-push", + context, + config + ); + if (result.data.code !== 1000) { + throw result.data.msg; + } + } + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Get the formatted title for message + * @param {?object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {?object} monitorJSON Monitor details (For Up/Down only) + * @returns {string} Formatted title + */ + checkStatus(heartbeatJSON, monitorJSON) { + let title = "UptimeKuma Message"; + if (heartbeatJSON != null && heartbeatJSON["status"] === UP) { + title = "UptimeKuma Monitor Up " + monitorJSON["name"]; + } + if (heartbeatJSON != null && heartbeatJSON["status"] === DOWN) { + title = "UptimeKuma Monitor Down " + monitorJSON["name"]; + } + return title; + } +} + +module.exports = WxPusher; diff --git a/server/notification-providers/yzj.js b/server/notification-providers/yzj.js new file mode 100644 index 0000000..6629d47 --- /dev/null +++ b/server/notification-providers/yzj.js @@ -0,0 +1,58 @@ +const NotificationProvider = require("./notification-provider"); +const { DOWN, UP } = require("../../src/util"); +const { default: axios } = require("axios"); + +class YZJ extends NotificationProvider { + name = "YZJ"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + let okMsg = "Sent Successfully."; + + try { + if (heartbeatJSON !== null) { + msg = `${this.statusToString(heartbeatJSON["status"])} ${monitorJSON["name"]} \n> ${heartbeatJSON["msg"]}\n> Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`; + } + + let config = { + headers: { + "Content-Type": "application/json", + }, + }; + const params = { + content: msg, + }; + // yzjtype=0 => general robot + const url = `${notification.yzjWebHookUrl}?yzjtype=0&yzjtoken=${notification.yzjToken}`; + config = this.getAxiosConfigWithProxy(config); + + const result = await axios.post(url, params, config); + if (!result.data?.success) { + throw new Error(result.data?.errmsg ?? "yzj's server did not respond with the expected result"); + } + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Convert status constant to string + * @param {string} status The status constant + * @returns {string} status + */ + statusToString(status) { + switch (status) { + case DOWN: + return "❌"; + case UP: + return "✅"; + default: + return status; + } + } +} + +module.exports = YZJ; diff --git a/server/notification-providers/zoho-cliq.js b/server/notification-providers/zoho-cliq.js new file mode 100644 index 0000000..10ca612 --- /dev/null +++ b/server/notification-providers/zoho-cliq.js @@ -0,0 +1,96 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class ZohoCliq extends NotificationProvider { + name = "ZohoCliq"; + + /** + * Generate the message to send + * @param {const} status The status constant + * @param {string} monitorName Name of monitor + * @returns {string} Status message + */ + _statusMessageFactory = (status, monitorName) => { + if (status === DOWN) { + return `🔴 [${monitorName}] went down\n`; + } else if (status === UP) { + return `### ✅ [${monitorName}] is back online\n`; + } + return "Notification\n"; + }; + + /** + * Send the notification + * @param {string} webhookUrl URL to send the request to + * @param {Array} payload Payload generated by _notificationPayloadFactory + * @returns {Promise} + */ + _sendNotification = async (webhookUrl, payload) => { + let config = this.getAxiosConfigWithProxy({}); + await axios.post(webhookUrl, { text: payload.join("\n") }, config); + }; + + /** + * Generate payload for notification + * @param {object} args Method arguments + * @param {const} args.status The status of the monitor + * @param {string} args.monitorMessage Message to send + * @param {string} args.monitorName Name of monitor affected + * @param {string} args.monitorUrl URL of monitor affected + * @returns {Array} Notification payload + */ + _notificationPayloadFactory = ({ status, monitorMessage, monitorName, monitorUrl }) => { + const payload = []; + payload.push(this._statusMessageFactory(status, monitorName)); + payload.push(`*Description:* ${monitorMessage}`); + + if (monitorUrl && monitorUrl !== "https://") { + payload.push(`*URL:* ${monitorUrl}`); + } + + return payload; + }; + + /** + * Send a general notification + * @param {string} webhookUrl URL to send request to + * @param {string} msg Message to send + * @returns {Promise} + */ + _handleGeneralNotification = (webhookUrl, msg) => { + const payload = this._notificationPayloadFactory({ + monitorMessage: msg, + }); + + return this._sendNotification(webhookUrl, payload); + }; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + if (heartbeatJSON == null) { + await this._handleGeneralNotification(notification.webhookUrl, msg); + return okMsg; + } + + const payload = this._notificationPayloadFactory({ + monitorMessage: heartbeatJSON.msg, + monitorName: monitorJSON.name, + monitorUrl: this.extractAddress(monitorJSON), + status: heartbeatJSON.status, + }); + + await this._sendNotification(notification.webhookUrl, payload); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = ZohoCliq; diff --git a/server/notification.js b/server/notification.js new file mode 100644 index 0000000..5779b1b --- /dev/null +++ b/server/notification.js @@ -0,0 +1,354 @@ +const { R } = require("redbean-node"); +const { log } = require("../src/util"); +const Alerta = require("./notification-providers/alerta"); +const AlertNow = require("./notification-providers/alertnow"); +const AliyunSms = require("./notification-providers/aliyun-sms"); +const Apprise = require("./notification-providers/apprise"); +const Bale = require("./notification-providers/bale"); +const Bark = require("./notification-providers/bark"); +const BearSMS = require("./notification-providers/bearsms"); +const Bitrix24 = require("./notification-providers/bitrix24"); +const ClickUp = require("./notification-providers/clickup"); +const ClickSendSMS = require("./notification-providers/clicksendsms"); +const CallMeBot = require("./notification-providers/call-me-bot"); +const SMSC = require("./notification-providers/smsc"); +const DingDing = require("./notification-providers/dingding"); +const Discord = require("./notification-providers/discord"); +const Fluxer = require("./notification-providers/fluxer"); +const Elks = require("./notification-providers/46elks"); +const EgoSMS = require("./notification-providers/egosms"); +const Feishu = require("./notification-providers/feishu"); +const Notifery = require("./notification-providers/notifery"); +const FreeMobile = require("./notification-providers/freemobile"); +const GoogleChat = require("./notification-providers/google-chat"); +const GoogleSheets = require("./notification-providers/google-sheets"); +const Gorush = require("./notification-providers/gorush"); +const Gotify = require("./notification-providers/gotify"); +const GrafanaOncall = require("./notification-providers/grafana-oncall"); +const HomeAssistant = require("./notification-providers/home-assistant"); +const Indigo = require("./notification-providers/indigo"); +const HeiiOnCall = require("./notification-providers/heii-oncall"); +const Keep = require("./notification-providers/keep"); +const Kook = require("./notification-providers/kook"); +const Line = require("./notification-providers/line"); +const LunaSea = require("./notification-providers/lunasea"); +const Matrix = require("./notification-providers/matrix"); +const Mattermost = require("./notification-providers/mattermost"); +const NextcloudTalk = require("./notification-providers/nextcloudtalk"); +const Nostr = require("./notification-providers/nostr"); +const NotifyApp = require("./notification-providers/notifyapp"); +const Ntfy = require("./notification-providers/ntfy"); +const Octopush = require("./notification-providers/octopush"); +const OneChat = require("./notification-providers/onechat"); +const OneBot = require("./notification-providers/onebot"); +const Ooredoo = require("./notification-providers/ooredoo"); +const Opsgenie = require("./notification-providers/opsgenie"); +const JiraServiceManagement = require("./notification-providers/jira-service-management"); +const PagerDuty = require("./notification-providers/pagerduty"); +const Pumble = require("./notification-providers/pumble"); +const FlashDuty = require("./notification-providers/flashduty"); +const Flowtriq = require("./notification-providers/flowtriq"); +const PagerTree = require("./notification-providers/pagertree"); +const Pinglet = require("./notification-providers/pinglet"); +const Plivo = require("./notification-providers/plivo"); +const PromoSMS = require("./notification-providers/promosms"); +const Pushbullet = require("./notification-providers/pushbullet"); +const PushDeer = require("./notification-providers/pushdeer"); +const Pushover = require("./notification-providers/pushover"); +const PushPlus = require("./notification-providers/pushplus"); +const Pushy = require("./notification-providers/pushy"); +const RocketChat = require("./notification-providers/rocket-chat"); +const SerwerSMS = require("./notification-providers/serwersms"); +const Signal = require("./notification-providers/signal"); +const Signalgrid = require("./notification-providers/signalgrid"); +const SIGNL4 = require("./notification-providers/signl4"); +const Slack = require("./notification-providers/slack"); +const SMSPartner = require("./notification-providers/smspartner"); +const SMSEagle = require("./notification-providers/smseagle"); +const SMSGateway = require("./notification-providers/sms-gateway"); +const SMTP = require("./notification-providers/smtp"); +const Squadcast = require("./notification-providers/squadcast"); +const Stackfield = require("./notification-providers/stackfield"); +const Teams = require("./notification-providers/teams"); +const TechulusPush = require("./notification-providers/techulus-push"); +const Telegram = require("./notification-providers/telegram"); +const Teltonika = require("./notification-providers/teltonika"); +const Telnyx = require("./notification-providers/telnyx"); +const Threema = require("./notification-providers/threema"); +const Twilio = require("./notification-providers/twilio"); +const Splunk = require("./notification-providers/splunk"); +const Webhook = require("./notification-providers/webhook"); +const WeCom = require("./notification-providers/wecom"); +const GoAlert = require("./notification-providers/goalert"); +const SMSManager = require("./notification-providers/smsmanager"); +const ServerChan = require("./notification-providers/serverchan"); +const ZohoCliq = require("./notification-providers/zoho-cliq"); +const SevenIO = require("./notification-providers/sevenio"); +const Whapi = require("./notification-providers/whapi"); +const WAHA = require("./notification-providers/waha"); +const Evolution = require("./notification-providers/evolution"); +const OpenWa = require("./notification-providers/openwa"); +const GtxMessaging = require("./notification-providers/gtx-messaging"); +const Cellsynt = require("./notification-providers/cellsynt"); +const Onesender = require("./notification-providers/onesender"); +const Wpush = require("./notification-providers/wpush"); +const WxPusher = require("./notification-providers/wxpusher"); +const SendGrid = require("./notification-providers/send-grid"); +const TurboSMTP = require("./notification-providers/turbosmtp"); +const Brevo = require("./notification-providers/brevo"); +const Resend = require("./notification-providers/resend"); +const YZJ = require("./notification-providers/yzj"); +const AmootSMS = require("./notification-providers/amootsms"); +const SMSPlanet = require("./notification-providers/sms-planet"); +const SpugPush = require("./notification-providers/spugpush"); +const SMSIR = require("./notification-providers/smsir"); +const { commandExists } = require("./util-server"); +const Whatsapp360messenger = require("./notification-providers/360messenger"); +const Webpush = require("./notification-providers/Webpush"); +const HaloPSA = require("./notification-providers/HaloPSA"); +const Max = require("./notification-providers/max"); +const VK = require("./notification-providers/vk"); +const VKTeams = require("./notification-providers/vkteams"); +const Milky = require("./notification-providers/milky"); + +class Notification { + providerList = {}; + + /** + * Initialize the notification providers + * @returns {void} + * @throws Notification provider does not have a name + * @throws Duplicate notification providers in list + */ + static init() { + log.debug("notification", "Prepare Notification Providers"); + + this.providerList = {}; + + const list = [ + new Alerta(), + new AlertNow(), + new AliyunSms(), + new Apprise(), + new Bale(), + new Bark(), + new BearSMS(), + new Bitrix24(), + new ClickUp(), + new ClickSendSMS(), + new CallMeBot(), + new SMSC(), + new DingDing(), + new Discord(), + new Fluxer(), + new Elks(), + new EgoSMS(), + new Feishu(), + new FreeMobile(), + new GoogleChat(), + new GoogleSheets(), + new Gorush(), + new Gotify(), + new GrafanaOncall(), + new HomeAssistant(), + new Indigo(), + new HeiiOnCall(), + new Keep(), + new Kook(), + new Line(), + new LunaSea(), + new Matrix(), + new Mattermost(), + new NextcloudTalk(), + new Nostr(), + new NotifyApp(), + new Ntfy(), + new Octopush(), + new OneChat(), + new OneBot(), + new Onesender(), + new Ooredoo(), + new Opsgenie(), + new JiraServiceManagement(), + new PagerDuty(), + new FlashDuty(), + new Flowtriq(), + new PagerTree(), + new Pinglet(), + new Plivo(), + new PromoSMS(), + new Pumble(), + new Pushbullet(), + new PushDeer(), + new Pushover(), + new PushPlus(), + new Pushy(), + new RocketChat(), + new ServerChan(), + new SerwerSMS(), + new Signal(), + new Signalgrid(), + new SIGNL4(), + new SMSManager(), + new SMSPartner(), + new Slack(), + new SMSEagle(), + new SMSGateway(), + new SMTP(), + new Squadcast(), + new Stackfield(), + new Teams(), + new TechulusPush(), + new Telegram(), + new Teltonika(), + new Telnyx(), + new Threema(), + new Twilio(), + new Splunk(), + new Webhook(), + new WeCom(), + new GoAlert(), + new ZohoCliq(), + new SevenIO(), + new Whapi(), + new WAHA(), + new Evolution(), + new OpenWa(), + new GtxMessaging(), + new Cellsynt(), + new Wpush(), + new WxPusher(), + new Brevo(), + new Resend(), + new YZJ(), + new AmootSMS(), + new SMSPlanet(), + new SpugPush(), + new Notifery(), + new SMSIR(), + new SendGrid(), + new TurboSMTP(), + new Whatsapp360messenger(), + new Webpush(), + new HaloPSA(), + new Max(), + new VK(), + new VKTeams(), + new Milky(), + ]; + for (let item of list) { + if (!item.name) { + throw new Error("Notification provider without name"); + } + + if (this.providerList[item.name]) { + throw new Error("Duplicate notification provider name"); + } + this.providerList[item.name] = item; + } + } + + /** + * Send a notification + * @param {BeanModel} notification Notification to send + * @param {string} msg General Message + * @param {object} monitorJSON Monitor details (For Up/Down only) + * @param {object} heartbeatJSON Heartbeat details (For Up/Down only) + * @returns {Promise} Successful msg + * @throws Error with fail msg + */ + static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + if (this.providerList[notification.type]) { + return this.providerList[notification.type].send(notification, msg, monitorJSON, heartbeatJSON); + } else { + throw new Error("Notification type is not supported"); + } + } + + /** + * Save a notification + * @param {object} notification Notification to save + * @param {?number} notificationID ID of notification to update + * @param {number} userID ID of user who adds notification + * @returns {Promise} Notification that was saved + */ + static async save(notification, notificationID, userID) { + let bean; + + if (notificationID) { + bean = await R.findOne("notification", " id = ? ", [notificationID]); + + if (!bean) { + throw new Error("notification not found"); + } + } else { + bean = R.dispense("notification"); + } + + // applyExisting is one time only, don't save it to database. + const applyExisting = notification.applyExisting || false; + notification.applyExisting = false; + + bean.name = notification.name; + bean.user_id = userID; + bean.config = JSON.stringify(notification); + bean.is_default = notification.isDefault || false; + await R.store(bean); + + if (applyExisting) { + await applyNotificationEveryMonitor(bean.id); + } + + return bean; + } + + /** + * Delete a notification + * @param {number} notificationID ID of notification to delete + * @returns {Promise} + */ + static async delete(notificationID) { + let bean = await R.findOne("notification", " id = ? ", [notificationID]); + + if (!bean) { + throw new Error("notification not found"); + } + + await R.trash(bean); + } + + /** + * Check if apprise exists + * @returns {Promise} Does the command apprise exist? + */ + static async checkApprise() { + return await commandExists("apprise"); + } +} + +/** + * Apply the notification to every monitor + * @param {number} notificationID ID of notification to apply + * @returns {Promise} + */ +async function applyNotificationEveryMonitor(notificationID) { + let monitors = await R.getAll("SELECT id FROM monitor"); + + for (let i = 0; i < monitors.length; i++) { + let checkNotification = await R.findOne("monitor_notification", " monitor_id = ? AND notification_id = ? ", [ + monitors[i].id, + notificationID, + ]); + + if (!checkNotification) { + let relation = R.dispense("monitor_notification"); + relation.monitor_id = monitors[i].id; + relation.notification_id = notificationID; + await R.store(relation); + } + } +} + +module.exports = { + Notification, +}; diff --git a/server/password-hash.js b/server/password-hash.js new file mode 100644 index 0000000..84fad55 --- /dev/null +++ b/server/password-hash.js @@ -0,0 +1,44 @@ +const passwordHashOld = require("password-hash"); +const bcrypt = require("bcryptjs"); +const saltRounds = 10; + +/** + * Hash a password + * @param {string} password Password to hash + * @returns {Promise} Hash + */ +exports.generate = function (password) { + return bcrypt.hash(password, saltRounds); +}; + +/** + * Verify a password against a hash + * @param {string} password Password to verify + * @param {string} hash Hash to verify against + * @returns {boolean} Does the password match the hash? + */ +exports.verify = function (password, hash) { + if (isSHA1(hash)) { + return passwordHashOld.verify(password, hash); + } + + return bcrypt.compareSync(password, hash); +}; + +/** + * Is the hash a SHA1 hash + * @param {string} hash Hash to check + * @returns {boolean} Is SHA1 hash? + */ +function isSHA1(hash) { + return typeof hash === "string" && hash.startsWith("sha1"); +} + +/** + * Does the hash need to be rehashed? + * @param {string} hash Hash to check + * @returns {boolean} Needs to be rehashed? + */ +exports.needRehash = function (hash) { + return isSHA1(hash); +}; diff --git a/server/prometheus.js b/server/prometheus.js new file mode 100644 index 0000000..e30b86f --- /dev/null +++ b/server/prometheus.js @@ -0,0 +1,283 @@ +const PrometheusClient = require("prom-client"); +const { log } = require("../src/util"); +const { R } = require("redbean-node"); + +let monitorCertDaysRemaining = null; +let monitorCertIsValid = null; +let monitorUptimeRatio = null; +let monitorAverageResponseTimeSeconds = null; +let monitorResponseTime = null; +let monitorStatus = null; + +class Prometheus { + monitorLabelValues = {}; + + /** + * @param {object} monitor Monitor object to monitor + * @param {Array<{name:string,value:?string}>} tags Tags to add to the monitor + */ + constructor(monitor, tags) { + this.monitorLabelValues = { + ...this.mapTagsToLabels(tags), + monitor_id: monitor.id, + monitor_name: monitor.name, + monitor_type: monitor.type, + monitor_url: monitor.url, + monitor_hostname: monitor.hostname, + monitor_port: monitor.port, + }; + } + + /** + * Initialize Prometheus metrics, and add all available tags as possible labels. + * This should be called once at the start of the application. + * New tags will NOT be added dynamically, a restart is sadly required to add new tags to the metrics. + * Existing tags added to monitors will be updated automatically. + * @returns {Promise} + */ + static async init() { + // Add all available tags as possible labels, + // and use Set to remove possible duplicates (for when multiple tags contain non-ascii characters, and thus are sanitized to the same label) + const tags = new Set( + (await R.findAll("tag")) + .map((tag) => { + return Prometheus.sanitizeForPrometheus(tag.name); + }) + .filter((tagName) => { + return tagName !== ""; + }) + .sort(this.sortTags) + ); + + const commonLabels = [ + ...tags, + "monitor_id", + "monitor_name", + "monitor_type", + "monitor_url", + "monitor_hostname", + "monitor_port", + ]; + + monitorCertDaysRemaining = new PrometheusClient.Gauge({ + name: "monitor_cert_days_remaining", + help: "The number of days remaining until the certificate expires", + labelNames: commonLabels, + }); + + monitorCertIsValid = new PrometheusClient.Gauge({ + name: "monitor_cert_is_valid", + help: "Is the certificate still valid? (1 = Yes, 0= No)", + labelNames: commonLabels, + }); + + monitorUptimeRatio = new PrometheusClient.Gauge({ + name: "monitor_uptime_ratio", + help: "Uptime ratio calculated over sliding window specified by the 'window' label. (0.0 - 1.0)", + labelNames: [...commonLabels, "window"], + }); + + monitorAverageResponseTimeSeconds = new PrometheusClient.Gauge({ + name: "monitor_response_time_seconds", + help: "Average response time in seconds calculated over sliding window specified by the 'window' label", + labelNames: [...commonLabels, "window"], + }); + + monitorResponseTime = new PrometheusClient.Gauge({ + name: "monitor_response_time", + help: "Monitor Response Time (ms)", + labelNames: commonLabels, + }); + + monitorStatus = new PrometheusClient.Gauge({ + name: "monitor_status", + help: "Monitor Status (1 = UP, 0= DOWN, 2= PENDING, 3= MAINTENANCE)", + labelNames: commonLabels, + }); + } + + /** + * Sanitize a string to ensure it can be used as a Prometheus label or value. + * See https://github.com/louislam/uptime-kuma/pull/4704#issuecomment-2366524692 + * @param {string} text The text to sanitize + * @returns {string} The sanitized text + */ + static sanitizeForPrometheus(text) { + text = text.replace(/[^a-zA-Z0-9_]/g, ""); + text = text.replace(/^[^a-zA-Z_]+/, ""); + return text; + } + + /** + * Map the tags value to valid labels used in Prometheus. Sanitize them in the process. + * @param {Array<{name: string, value:?string}>} tags The tags to map + * @returns {object} The mapped tags, usable as labels + */ + mapTagsToLabels(tags) { + let mappedTags = {}; + tags.forEach((tag) => { + let sanitizedTag = Prometheus.sanitizeForPrometheus(tag.name); + if (sanitizedTag === "") { + return; // Skip empty tag names + } + + if (mappedTags[sanitizedTag] === undefined) { + mappedTags[sanitizedTag] = []; + } + + let tagValue = Prometheus.sanitizeForPrometheus(tag.value || ""); + if (tagValue !== "") { + mappedTags[sanitizedTag].push(tagValue); + } + + mappedTags[sanitizedTag] = mappedTags[sanitizedTag].sort(); + }); + + // Order the tags alphabetically + return Object.keys(mappedTags) + .sort(this.sortTags) + .reduce((obj, key) => { + obj[key] = mappedTags[key]; + return obj; + }, {}); + } + + /** + * Update the metrics page + * @typedef {import("./uptime-calculator").UptimeDataResult} UptimeDataResult + * @param {object} heartbeat Heartbeat details + * @param {object} tlsInfo TLS details + * @param {{data24h: UptimeDataResult, data30d: UptimeDataResult, data1y:UptimeDataResult} | null} uptime the uptime and average response rate over a variety of fixed windows + * @returns {void} + */ + update(heartbeat, tlsInfo, uptime) { + if (typeof tlsInfo !== "undefined") { + try { + let isValid; + if (tlsInfo.valid === true) { + isValid = 1; + } else { + isValid = 0; + } + monitorCertIsValid.set(this.monitorLabelValues, isValid); + } catch (e) { + log.error("prometheus", "Caught error", e); + } + + try { + if (tlsInfo.certInfo != null) { + monitorCertDaysRemaining.set(this.monitorLabelValues, tlsInfo.certInfo.daysRemaining); + } + } catch (e) { + log.error("prometheus", "Caught error", e); + } + } + + if (uptime) { + try { + monitorAverageResponseTimeSeconds.set( + { ...this.monitorLabelValues, window: "1d" }, + uptime.data24h.avgPing / 1000 + ); + } catch (e) { + log.error("prometheus", "Caught error", e); + } + try { + monitorAverageResponseTimeSeconds.set( + { ...this.monitorLabelValues, window: "30d" }, + uptime.data30d.avgPing / 1000 + ); + } catch (e) { + log.error("prometheus", "Caught error", e); + } + try { + monitorAverageResponseTimeSeconds.set( + { ...this.monitorLabelValues, window: "365d" }, + uptime.data1y.avgPing / 1000 + ); + } catch (e) { + log.error("prometheus", "Caught error", e); + } + try { + monitorUptimeRatio.set({ ...this.monitorLabelValues, window: "1d" }, uptime.data24h.uptime); + } catch (e) { + log.error("prometheus", "Caught error", e); + } + try { + monitorUptimeRatio.set({ ...this.monitorLabelValues, window: "30d" }, uptime.data30d.uptime); + } catch (e) { + log.error("prometheus", "Caught error", e); + } + try { + monitorUptimeRatio.set({ ...this.monitorLabelValues, window: "365d" }, uptime.data1y.uptime); + } catch (e) { + log.error("prometheus", "Caught error", e); + } + } + + if (heartbeat) { + try { + monitorStatus.set(this.monitorLabelValues, heartbeat.status); + } catch (e) { + log.error("prometheus", "Caught error"); + log.error("prometheus", e); + } + + try { + if (typeof heartbeat.ping === "number") { + monitorResponseTime.set(this.monitorLabelValues, heartbeat.ping); + } else { + // Is it good? + monitorResponseTime.set(this.monitorLabelValues, -1); + } + } catch (e) { + log.error("prometheus", "Caught error"); + log.error("prometheus", e); + } + } + } + + /** + * Remove monitor from prometheus + * @returns {void} + */ + remove() { + try { + monitorCertDaysRemaining.remove(this.monitorLabelValues); + monitorCertIsValid.remove(this.monitorLabelValues); + ["1d", "30d", "365d"].forEach((window) => { + monitorUptimeRatio.remove({ ...this.monitorLabelValues, window }); + monitorAverageResponseTimeSeconds.remove({ ...this.monitorLabelValues, window }); + }); + monitorResponseTime.remove(this.monitorLabelValues); + monitorStatus.remove(this.monitorLabelValues); + } catch (e) { + console.error(e); + } + } + + /** + * Sort the tags alphabetically, case-insensitive. + * @param {string} a The first tag to compare + * @param {string} b The second tag to compare + * @returns {number} The alphabetical order number + */ + sortTags(a, b) { + const aLowerCase = a.toLowerCase(); + const bLowerCase = b.toLowerCase(); + + if (aLowerCase < bLowerCase) { + return -1; + } + + if (aLowerCase > bLowerCase) { + return 1; + } + + return 0; + } +} + +module.exports = { + Prometheus, +}; diff --git a/server/proxy.js b/server/proxy.js new file mode 100644 index 0000000..92b8ddd --- /dev/null +++ b/server/proxy.js @@ -0,0 +1,197 @@ +const { R } = require("redbean-node"); +const { HttpProxyAgent } = require("http-proxy-agent"); +const { HttpsProxyAgent } = require("https-proxy-agent"); +const { SocksProxyAgent } = require("socks-proxy-agent"); +const { debug } = require("../src/util"); +const { UptimeKumaServer } = require("./uptime-kuma-server"); +const { CookieJar } = require("tough-cookie"); +const { createCookieAgent } = require("http-cookie-agent/http"); + +class Proxy { + static SUPPORTED_PROXY_PROTOCOLS = ["http", "https", "socks", "socks5", "socks5h", "socks4"]; + + /** + * Saves and updates given proxy entity + * @param {object} proxy Proxy to store + * @param {number} proxyID ID of proxy to update + * @param {number} userID ID of user the proxy belongs to + * @returns {Promise} Updated proxy + */ + static async save(proxy, proxyID, userID) { + let bean; + + if (proxyID) { + bean = await R.findOne("proxy", " id = ? ", [proxyID]); + + if (!bean) { + throw new Error("proxy not found"); + } + } else { + bean = R.dispense("proxy"); + } + + // Make sure given proxy protocol is supported + if (!this.SUPPORTED_PROXY_PROTOCOLS.includes(proxy.protocol)) { + throw new Error(` + Unsupported proxy protocol "${proxy.protocol}. + Supported protocols are ${this.SUPPORTED_PROXY_PROTOCOLS.join(", ")}."`); + } + + // When proxy is default update deactivate old default proxy + if (proxy.default) { + await R.exec("UPDATE proxy SET `default` = 0 WHERE `default` = 1"); + } + + bean.user_id = userID; + bean.protocol = proxy.protocol; + bean.host = proxy.host; + bean.port = proxy.port; + bean.auth = proxy.auth; + bean.username = proxy.username; + bean.password = proxy.password; + bean.active = proxy.active || true; + bean.default = proxy.default || false; + + await R.store(bean); + + if (proxy.applyExisting) { + await applyProxyEveryMonitor(bean.id); + } + + return bean; + } + + /** + * Deletes proxy with given id and removes it from monitors + * @param {number} proxyID ID of proxy to delete + * @returns {Promise} + */ + static async delete(proxyID) { + const bean = await R.findOne("proxy", " id = ? ", [proxyID]); + + if (!bean) { + throw new Error("proxy not found"); + } + + // Delete removed proxy from monitors if exists + await R.exec("UPDATE monitor SET proxy_id = null WHERE proxy_id = ?", [proxyID]); + + // Delete proxy from list + await R.trash(bean); + } + + /** + * Create HTTP and HTTPS agents related with given proxy bean object + * @param {object} proxy proxy bean object + * @param {object} options http and https agent options + * @returns {{httpAgent: Agent, httpsAgent: Agent}} New HTTP and HTTPS agents + * @throws Proxy protocol is unsupported + */ + static createAgents(proxy, options) { + const { httpAgentOptions, httpsAgentOptions } = options || {}; + let agent; + let httpAgent; + let httpsAgent; + + let jar = new CookieJar(); + + const proxyOptions = { + cookies: { jar }, + }; + + const proxyUrl = new URL(`${proxy.protocol}://${proxy.host}:${proxy.port}`); + + if (proxy.auth) { + proxyUrl.username = proxy.username; + proxyUrl.password = proxy.password; + } + + debug(`Proxy URL: ${proxyUrl.toString()}`); + debug(`HTTP Agent Options: ${JSON.stringify(httpAgentOptions)}`); + debug(`HTTPS Agent Options: ${JSON.stringify(httpsAgentOptions)}`); + + switch (proxy.protocol) { + case "http": + case "https": + // eslint-disable-next-line no-case-declarations + const HttpCookieProxyAgent = createCookieAgent(HttpProxyAgent); + // eslint-disable-next-line no-case-declarations + const HttpsCookieProxyAgent = createCookieAgent(HttpsProxyAgent); + + httpAgent = new HttpCookieProxyAgent(proxyUrl.toString(), { + ...(httpAgentOptions || {}), + ...proxyOptions, + }); + httpsAgent = new HttpsCookieProxyAgent(proxyUrl.toString(), { + ...(httpsAgentOptions || {}), + ...proxyOptions, + }); + + break; + case "socks": + case "socks5": + case "socks5h": + case "socks4": + // eslint-disable-next-line no-case-declarations + const SocksCookieProxyAgent = createCookieAgent(SocksProxyAgent); + agent = new SocksCookieProxyAgent(proxyUrl.toString(), { + ...httpAgentOptions, + ...httpsAgentOptions, + tls: { + rejectUnauthorized: httpsAgentOptions.rejectUnauthorized, + }, + }); + + httpAgent = agent; + httpsAgent = agent; + break; + + default: + throw new Error(`Unsupported proxy protocol provided. ${proxy.protocol}`); + } + + return { + httpAgent, + httpsAgent, + }; + } + + /** + * Reload proxy settings for current monitors + * @returns {Promise} + */ + static async reloadProxy() { + const server = UptimeKumaServer.getInstance(); + + let updatedList = await R.getAssoc("SELECT id, proxy_id FROM monitor"); + + for (let monitorID in server.monitorList) { + let monitor = server.monitorList[monitorID]; + + if (updatedList[monitorID]) { + monitor.proxy_id = updatedList[monitorID].proxy_id; + } + } + } +} + +/** + * Applies given proxy id to monitors + * @param {number} proxyID ID of proxy to apply + * @returns {Promise} + */ +async function applyProxyEveryMonitor(proxyID) { + // Find all monitors with id and proxy id + const monitors = await R.getAll("SELECT id, proxy_id FROM monitor"); + + // Update proxy id not match with given proxy id + for (const monitor of monitors) { + if (monitor.proxy_id !== proxyID) { + await R.exec("UPDATE monitor SET proxy_id = ? WHERE id = ?", [proxyID, monitor.id]); + } + } +} + +module.exports = { + Proxy, +}; diff --git a/server/radius-client.js b/server/radius-client.js new file mode 100644 index 0000000..169eda7 --- /dev/null +++ b/server/radius-client.js @@ -0,0 +1,174 @@ +/** + * Custom RADIUS Client Implementation + * + * This is a lightweight RADIUS client implementation using the base `radius` package + * Due to lack of maintenance in node-radius-client this was forked + * + * Implements RADIUS Access-Request functionality compatible with the original + * node-radius-client API used in Uptime Kuma. + */ + +const dgram = require("dgram"); +const radius = require("radius"); + +/** + * RADIUS Client class + */ +class RadiusClient { + /** + * @param {object} options Client configuration + * @param {string} options.host RADIUS server hostname + * @param {number} options.hostPort RADIUS server port (default: 1812) + * @param {number} options.timeout Request timeout in milliseconds (default: 2500) + * @param {number} options.retries Number of retry attempts (default: 1) + * @param {Array} options.dictionaries RADIUS dictionaries for attribute encoding + */ + constructor(options) { + this.host = options.host; + this.port = options.hostPort || 1812; + this.timeout = options.timeout || 2500; + this.retries = options.retries || 1; + this.dictionaries = options.dictionaries || []; + } + + /** + * Send RADIUS Access-Request + * @param {object} params Request parameters + * @param {string} params.secret RADIUS shared secret + * @param {Array} params.attributes Array of [attribute, value] pairs + * @returns {Promise} RADIUS response + */ + accessRequest(params) { + return new Promise((resolve, reject) => { + const { secret, attributes } = params; + + // Build RADIUS packet + const packet = { + code: "Access-Request", + secret: secret, + attributes: {}, + }; + + // Convert attributes array to object + attributes.forEach(([attr, value]) => { + packet.attributes[attr] = value; + }); + + // Encode packet + let encodedPacket; + try { + encodedPacket = radius.encode(packet); + } catch (error) { + return reject(new Error(`RADIUS packet encoding failed: ${error.message}`)); + } + + // Create UDP socket + const socket = dgram.createSocket("udp4"); + let attempts = 0; + let responseReceived = false; + let timeoutHandle; + let socketClosed = false; + + /** + * Safely close socket and clear timeout + * @returns {void} + */ + const cleanup = () => { + if (timeoutHandle) { + clearTimeout(timeoutHandle); + timeoutHandle = null; + } + if (!socketClosed) { + socketClosed = true; + try { + socket.close(); + } catch (err) { + // Ignore errors during cleanup + } + } + }; + + /** + * Send RADIUS request with retry logic + * @returns {void} + */ + const sendRequest = () => { + if (responseReceived || socketClosed) { + return; + } + + attempts++; + + socket.send(encodedPacket, 0, encodedPacket.length, this.port, this.host, (err) => { + if (err) { + cleanup(); + return reject(new Error(`Failed to send RADIUS request: ${err.message}`)); + } + + // Set timeout for this attempt + timeoutHandle = setTimeout(() => { + if (responseReceived || socketClosed) { + return; + } + + if (attempts < this.retries + 1) { + // Retry + sendRequest(); + } else { + // All retries exhausted + cleanup(); + reject(new Error(`RADIUS request timeout after ${attempts} attempts`)); + } + }, this.timeout); + }); + }; + + // Handle response + socket.on("message", (msg) => { + if (responseReceived || socketClosed) { + return; + } + + responseReceived = true; + cleanup(); + + let response; + try { + response = radius.decode({ packet: msg, secret: secret }); + } catch (error) { + return reject(new Error(`RADIUS response decoding failed: ${error.message}`)); + } + + // Map response code to match node-radius-client format + const responseCode = response.code; + + if (responseCode === "Access-Accept") { + resolve({ code: "Access-Accept", ...response }); + } else if (responseCode === "Access-Reject") { + // Reject as error to match original behavior + const error = new Error("Access-Reject"); + error.response = { code: "Access-Reject" }; + reject(error); + } else if (responseCode === "Access-Challenge") { + resolve({ code: "Access-Challenge", ...response }); + } else { + resolve({ code: responseCode, ...response }); + } + }); + + // Handle socket errors + socket.on("error", (err) => { + if (!responseReceived && !socketClosed) { + responseReceived = true; + cleanup(); + reject(new Error(`RADIUS socket error: ${err.message}`)); + } + }); + + // Start first request + sendRequest(); + }); + } +} + +module.exports = RadiusClient; diff --git a/server/rate-limiter.js b/server/rate-limiter.js new file mode 100644 index 0000000..1afba1a --- /dev/null +++ b/server/rate-limiter.js @@ -0,0 +1,78 @@ +const { RateLimiter } = require("limiter"); +const { log } = require("../src/util"); + +class KumaRateLimiter { + /** + * @param {object} config Rate limiter configuration object + */ + constructor(config) { + this.errorMessage = config.errorMessage; + this.tokensPerInterval = config.tokensPerInterval; + this.rateLimiter = new RateLimiter(config); + } + + /** + * Callback for pass + * @callback passCB + * @param {object} err Too many requests + */ + + /** + * Should the request be passed through + * @param {passCB} callback Callback function to call with decision + * @param {number} num Number of tokens to remove + * @returns {Promise} Should the request be allowed? + */ + async pass(callback, num = 1) { + const remainingRequests = await this.removeTokens(num); + if (remainingRequests < this.tokensPerInterval * 0.2) { + log.warn( + "rate-limit", + `${remainingRequests}/${this.tokensPerInterval} remaining requests until rate limiting` + ); + } else { + log.debug( + "rate-limit", + `${remainingRequests}/${this.tokensPerInterval} remaining requests until rate limiting` + ); + } + if (remainingRequests < 0) { + if (callback) { + callback({ + ok: false, + msg: this.errorMessage, + }); + } + return false; + } + return true; + } + + /** + * Remove a given number of tokens + * @param {number} num Number of tokens to remove + * @returns {Promise} Number of remaining tokens + */ + async removeTokens(num = 1) { + return await this.rateLimiter.removeTokens(num); + } +} + +const loginRateLimiter = new KumaRateLimiter({ + tokensPerInterval: 20, + interval: "minute", + fireImmediately: true, + errorMessage: "Too frequently, try again later.", +}); + +const apiRateLimiter = new KumaRateLimiter({ + tokensPerInterval: 60, + interval: "minute", + fireImmediately: true, + errorMessage: "Too frequently, try again later.", +}); + +module.exports = { + loginRateLimiter, + apiRateLimiter, +}; diff --git a/server/remote-browser.js b/server/remote-browser.js new file mode 100644 index 0000000..221043b --- /dev/null +++ b/server/remote-browser.js @@ -0,0 +1,69 @@ +const { R } = require("redbean-node"); + +class RemoteBrowser { + /** + * Gets remote browser from ID + * @param {number} remoteBrowserID ID of the remote browser + * @returns {Promise} Remote Browser + */ + static async get(remoteBrowserID) { + let bean = await R.findOne("remote_browser", " id = ? ", [remoteBrowserID]); + + if (!bean) { + throw new Error("Remote browser not found"); + } + + return bean; + } + + /** + * Save a Remote Browser + * @param {object} remoteBrowser Remote Browser to save + * @param {?number} remoteBrowserID ID of the Remote Browser to update + * @param {number} userID ID of the user who adds the Remote Browser + * @returns {Promise} Updated Remote Browser + */ + static async save(remoteBrowser, remoteBrowserID, userID) { + let bean; + + if (remoteBrowserID) { + bean = await R.findOne("remote_browser", " id = ? ", [remoteBrowserID]); + + if (!bean) { + throw new Error("Remote browser not found"); + } + } else { + bean = R.dispense("remote_browser"); + } + + bean.user_id = userID; + bean.name = remoteBrowser.name; + bean.url = remoteBrowser.url; + + await R.store(bean); + + return bean; + } + + /** + * Delete a Remote Browser + * @param {number} remoteBrowserID ID of the Remote Browser to delete + * @returns {Promise} + */ + static async delete(remoteBrowserID) { + let bean = await R.findOne("remote_browser", " id = ? ", [remoteBrowserID]); + + if (!bean) { + throw new Error("Remote Browser not found"); + } + + // Delete removed remote browser from monitors if exists + await R.exec("UPDATE monitor SET remote_browser = null WHERE remote_browser = ?", [remoteBrowserID]); + + await R.trash(bean); + } +} + +module.exports = { + RemoteBrowser, +}; diff --git a/server/routers/api-router.js b/server/routers/api-router.js new file mode 100644 index 0000000..6835693 --- /dev/null +++ b/server/routers/api-router.js @@ -0,0 +1,645 @@ +let express = require("express"); +const { + allowDevAllOrigin, + allowAllOrigin, + percentageToColor, + filterAndJoin, + sendHttpError, +} = require("../util-server"); +const { R } = require("redbean-node"); +const apicache = require("../modules/apicache"); +const Monitor = require("../model/monitor"); +const dayjs = require("dayjs"); +const { UP, MAINTENANCE, DOWN, PENDING, flipStatus, log, badgeConstants } = require("../../src/util"); +const StatusPage = require("../model/status_page"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); +const { makeBadge } = require("badge-maker"); +const { Prometheus } = require("../prometheus"); +const { UptimeCalculator } = require("../uptime-calculator"); +const { Settings } = require("../settings"); + +let router = express.Router(); + +let cache = apicache.middleware; +const server = UptimeKumaServer.getInstance(); +let io = server.io; + +router.get("/api/entry-page", async (request, response) => { + allowDevAllOrigin(response); + + let result = {}; + let hostname = request.hostname; + if ((await Settings.get("trustProxy")) && request.headers["x-forwarded-host"]) { + hostname = request.headers["x-forwarded-host"]; + } + + if (hostname in StatusPage.domainMappingList) { + result.type = "statusPageMatchedDomain"; + result.statusPageSlug = StatusPage.domainMappingList[hostname]; + } else { + result.type = "entryPage"; + result.entryPage = server.entryPage; + } + response.json(result); +}); + +router.all("/api/push/:pushToken", async (request, response) => { + try { + let pushToken = request.params.pushToken; + let msg = request.query.msg || "OK"; + let ping = parseFloat(request.query.ping) || null; + let statusString = request.query.status || "up"; + const statusFromParam = statusString === "up" ? UP : DOWN; + + // Check if status=down was explicitly provided (not defaulting to "up") + // When explicitly pushing down, bypass retry logic and go directly to DOWN + const isExplicitDown = request.query.status === "down"; + + // Validate ping value - max 100 billion ms (~3.17 years) + // Fits safely in both BIGINT and FLOAT(20,2) + const MAX_PING_MS = 100000000000; + if (ping !== null && (ping < 0 || ping > MAX_PING_MS)) { + throw new Error(`Invalid ping value. Must be between 0 and ${MAX_PING_MS} ms.`); + } + + let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [pushToken]); + + if (!monitor) { + throw new Error("Monitor not found or not active."); + } + + const previousHeartbeat = await Monitor.getPreviousHeartbeat(monitor.id); + + let isFirstBeat = true; + + let bean = R.dispense("heartbeat"); + bean.time = R.isoDateTimeMillis(dayjs.utc()); + bean.monitor_id = monitor.id; + bean.ping = ping; + bean.msg = msg; + bean.downCount = previousHeartbeat?.downCount || 0; + + if (previousHeartbeat) { + isFirstBeat = false; + bean.duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second"); + } + + if (await Monitor.isUnderMaintenance(monitor.id)) { + msg = "Monitor under maintenance"; + bean.status = MAINTENANCE; + } else { + determineStatus( + statusFromParam, + previousHeartbeat, + monitor.maxretries, + monitor.isUpsideDown(), + bean, + isExplicitDown + ); + } + + // Calculate uptime + let uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitor.id); + let endTimeDayjs = await uptimeCalculator.update(bean.status, parseFloat(bean.ping)); + bean.end_time = R.isoDateTimeMillis(endTimeDayjs); + + log.debug("router", `/api/push/ called at ${dayjs().format("YYYY-MM-DD HH:mm:ss.SSS")}`); + log.debug("router", "PreviousStatus: " + previousHeartbeat?.status); + log.debug("router", "Current Status: " + bean.status); + + bean.important = Monitor.isImportantBeat(isFirstBeat, previousHeartbeat?.status, bean.status); + + if (Monitor.isImportantForNotification(isFirstBeat, previousHeartbeat?.status, bean.status)) { + // Reset down count + bean.downCount = 0; + + log.debug("monitor", `[${monitor.name}] sendNotification`); + await Monitor.sendNotification(isFirstBeat, monitor, bean); + } else { + if (bean.status === DOWN && monitor.resendInterval > 0) { + ++bean.downCount; + if (bean.downCount >= monitor.resendInterval) { + // Send notification again, because we are still DOWN + log.debug( + "monitor", + `[${monitor.name}] sendNotification again: Down Count: ${bean.downCount} | Resend Interval: ${monitor.resendInterval}` + ); + await Monitor.sendNotification(isFirstBeat, monitor, bean); + + // Reset down count + bean.downCount = 0; + } + } + } + + await R.store(bean); + + io.to(monitor.user_id).emit("heartbeat", bean.toJSON()); + + Monitor.sendStats(io, monitor.id, monitor.user_id); + + try { + new Prometheus(monitor, await monitor.getTags()).update(bean, undefined); + } catch (e) { + log.error("prometheus", "Please submit an issue to our GitHub repo. Prometheus update error: ", e.message); + } + + response.json({ + ok: true, + }); + } catch (e) { + response.status(404).json({ + ok: false, + msg: e.message, + }); + } +}); + +router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response) => { + allowAllOrigin(response); + + const { + label, + upLabel = "Up", + downLabel = "Down", + pendingLabel = "Pending", + maintenanceLabel = "Maintenance", + upColor = badgeConstants.defaultUpColor, + downColor = badgeConstants.defaultDownColor, + pendingColor = badgeConstants.defaultPendingColor, + maintenanceColor = badgeConstants.defaultMaintenanceColor, + style = badgeConstants.defaultStyle, + value, // for demo purpose only + } = request.query; + + try { + const requestedMonitorId = parseInt(request.params.id, 10); + if (Number.isNaN(requestedMonitorId)) { + throw new Error("Invalid monitor ID"); + } + const overrideValue = value !== undefined ? parseInt(value) : undefined; + const publicMonitor = await isMonitorPublic(requestedMonitorId); + const badgeValues = { style }; + + if (!publicMonitor) { + // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant + + badgeValues.message = "N/A"; + badgeValues.color = badgeConstants.naColor; + } else { + const heartbeat = await Monitor.getPreviousHeartbeat(requestedMonitorId); + const state = overrideValue !== undefined ? overrideValue : heartbeat.status; + + if (label === undefined) { + badgeValues.label = "Status"; + } else { + badgeValues.label = label; + } + switch (state) { + case DOWN: + badgeValues.color = downColor; + badgeValues.message = downLabel; + break; + case UP: + badgeValues.color = upColor; + badgeValues.message = upLabel; + break; + case PENDING: + badgeValues.color = pendingColor; + badgeValues.message = pendingLabel; + break; + case MAINTENANCE: + badgeValues.color = maintenanceColor; + badgeValues.message = maintenanceLabel; + break; + default: + badgeValues.color = badgeConstants.naColor; + badgeValues.message = "N/A"; + } + } + + // build the svg based on given values + const svg = makeBadge(badgeValues); + + response.type("image/svg+xml"); + response.send(svg); + } catch (error) { + sendHttpError(response, error.message); + } +}); + +router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (request, response) => { + allowAllOrigin(response); + + const { + label, + labelPrefix, + labelSuffix = badgeConstants.defaultUptimeLabelSuffix, + prefix, + suffix = badgeConstants.defaultUptimeValueSuffix, + color, + labelColor, + style = badgeConstants.defaultStyle, + value, // for demo purpose only + } = request.query; + + try { + const requestedMonitorId = parseInt(request.params.id, 10); + if (Number.isNaN(requestedMonitorId)) { + throw new Error("Invalid monitor ID"); + } + // if no duration is given, set value to 24 (h) + let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h"; + const overrideValue = value && parseFloat(value); + + if (/^[0-9]+$/.test(requestedDuration)) { + requestedDuration = `${requestedDuration}h`; + } + + const publicMonitor = await isMonitorPublic(requestedMonitorId); + const badgeValues = { style }; + + if (!publicMonitor) { + // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non existent + badgeValues.message = "N/A"; + badgeValues.color = badgeConstants.naColor; + } else { + const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(requestedMonitorId); + const uptime = overrideValue ?? uptimeCalculator.getDataByDuration(requestedDuration).uptime; + + // limit the displayed uptime percentage to four (two, when displayed as percent) decimal digits + const cleanUptime = (uptime * 100).toPrecision(4); + + // use a given, custom color or calculate one based on the uptime value + badgeValues.color = color ?? percentageToColor(uptime); + // use a given, custom labelColor or use the default badge label color (defined by badge-maker) + badgeValues.labelColor = labelColor ?? ""; + // build a label string. If a custom label is given, override the default one (requestedDuration) + badgeValues.label = filterAndJoin([ + labelPrefix, + label ?? `Uptime (${requestedDuration.slice(0, -1)}${labelSuffix})`, + ]); + badgeValues.message = filterAndJoin([prefix, cleanUptime, suffix]); + } + + // build the SVG based on given values + const svg = makeBadge(badgeValues); + + response.type("image/svg+xml"); + response.send(svg); + } catch (error) { + sendHttpError(response, error.message); + } +}); + +router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, response) => { + allowAllOrigin(response); + + const { + label, + labelPrefix, + labelSuffix = badgeConstants.defaultPingLabelSuffix, + prefix, + suffix = badgeConstants.defaultPingValueSuffix, + color = badgeConstants.defaultPingColor, + labelColor, + style = badgeConstants.defaultStyle, + value, // for demo purpose only + } = request.query; + + try { + const requestedMonitorId = parseInt(request.params.id, 10); + if (Number.isNaN(requestedMonitorId)) { + throw new Error("Invalid monitor ID"); + } + + // Default duration is 24 (h) if not defined in queryParam, limited to 720h (30d) + let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h"; + const overrideValue = value && parseFloat(value); + + if (/^[0-9]+$/.test(requestedDuration)) { + requestedDuration = `${requestedDuration}h`; + } + + // Check if monitor is public + const publicMonitor = await isMonitorPublic(requestedMonitorId); + const badgeValues = { style }; + + if (!publicMonitor) { + // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant + badgeValues.message = "N/A"; + badgeValues.color = badgeConstants.naColor; + } else { + const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(requestedMonitorId); + const avgPing = uptimeCalculator.getDataByDuration(requestedDuration).avgPing; + const avgPingValue = parseInt(overrideValue ?? avgPing); + + badgeValues.color = color; + // use a given, custom labelColor or use the default badge label color (defined by badge-maker) + badgeValues.labelColor = labelColor ?? ""; + // build a lable string. If a custom label is given, override the default one (requestedDuration) + badgeValues.label = filterAndJoin([ + labelPrefix, + label ?? `Avg. Ping (${requestedDuration.slice(0, -1)}${labelSuffix})`, + ]); + badgeValues.message = filterAndJoin([prefix, avgPingValue, suffix]); + } + + // build the SVG based on given values + const svg = makeBadge(badgeValues); + + response.type("image/svg+xml"); + response.send(svg); + } catch (error) { + sendHttpError(response, error.message); + } +}); + +router.get("/api/badge/:id/avg-response/:duration?", cache("5 minutes"), async (request, response) => { + allowAllOrigin(response); + + const { + label, + labelPrefix, + labelSuffix, + prefix, + suffix = badgeConstants.defaultPingValueSuffix, + color = badgeConstants.defaultPingColor, + labelColor, + style = badgeConstants.defaultStyle, + value, // for demo purpose only + } = request.query; + + try { + const requestedMonitorId = parseInt(request.params.id, 10); + if (Number.isNaN(requestedMonitorId)) { + throw new Error("Invalid monitor ID"); + } + + // Default duration is 24 (h) if not defined in queryParam, limited to 720h (30d) + const requestedDuration = Math.min(request.params.duration ? parseInt(request.params.duration, 10) : 24, 720); + const overrideValue = value && parseFloat(value); + + const publicMonitor = await isMonitorPublic(requestedMonitorId); + + let avgPing; + + if (publicMonitor) { + const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(requestedMonitorId); + avgPing = uptimeCalculator.getDataByDuration(`${requestedDuration}h`).avgPing; + } + + const badgeValues = { style }; + + if (!publicMonitor || !avgPing) { + badgeValues.message = "N/A"; + badgeValues.color = badgeConstants.naColor; + } else { + const avgPingValue = parseInt(overrideValue ?? avgPing); + + badgeValues.color = color; + // use a given, custom labelColor or use the default badge label color (defined by badge-maker) + badgeValues.labelColor = labelColor ?? ""; + // build a label string. If a custom label is given, override the default one (requestedDuration) + badgeValues.label = filterAndJoin([ + labelPrefix, + label ?? `Avg. Response (${requestedDuration}h)`, + labelSuffix, + ]); + badgeValues.message = filterAndJoin([prefix, avgPingValue, suffix]); + } + + // build the SVG based on given values + const svg = makeBadge(badgeValues); + + response.type("image/svg+xml"); + response.send(svg); + } catch (error) { + sendHttpError(response, error.message); + } +}); + +router.get("/api/badge/:id/cert-exp", cache("5 minutes"), async (request, response) => { + allowAllOrigin(response); + + const date = request.query.date; + + const { + label, + labelPrefix, + labelSuffix, + prefix, + suffix = date ? "" : badgeConstants.defaultCertExpValueSuffix, + upColor = badgeConstants.defaultUpColor, + warnColor = badgeConstants.defaultWarnColor, + downColor = badgeConstants.defaultDownColor, + warnDays = badgeConstants.defaultCertExpireWarnDays, + downDays = badgeConstants.defaultCertExpireDownDays, + labelColor, + style = badgeConstants.defaultStyle, + value, // for demo purpose only + } = request.query; + + try { + const requestedMonitorId = parseInt(request.params.id, 10); + if (Number.isNaN(requestedMonitorId)) { + throw new Error("Invalid monitor ID"); + } + + const overrideValue = value && parseFloat(value); + const publicMonitor = await isMonitorPublic(requestedMonitorId); + const badgeValues = { style }; + + if (!publicMonitor) { + // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non existent + + badgeValues.message = "N/A"; + badgeValues.color = badgeConstants.naColor; + } else { + const tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [requestedMonitorId]); + + if (!tlsInfoBean) { + // return a "No/Bad Cert" badge in naColor (grey), if no cert saved (does not save bad certs?) + badgeValues.message = "No/Bad Cert"; + badgeValues.color = badgeConstants.naColor; + } else { + const tlsInfo = JSON.parse(tlsInfoBean.info_json); + + if (!tlsInfo.valid) { + // return a "Bad Cert" badge in naColor (grey), when cert is not valid + badgeValues.message = "Bad Cert"; + badgeValues.color = downColor; + } else { + const daysRemaining = parseInt(overrideValue ?? tlsInfo.certInfo.daysRemaining); + + if (daysRemaining > warnDays) { + badgeValues.color = upColor; + } else if (daysRemaining > downDays) { + badgeValues.color = warnColor; + } else { + badgeValues.color = downColor; + } + // use a given, custom labelColor or use the default badge label color (defined by badge-maker) + badgeValues.labelColor = labelColor ?? ""; + // build a label string. If a custom label is given, override the default one + badgeValues.label = filterAndJoin([labelPrefix, label ?? "Cert Exp.", labelSuffix]); + badgeValues.message = filterAndJoin([ + prefix, + date ? tlsInfo.certInfo.validTo : daysRemaining, + suffix, + ]); + } + } + } + + // build the SVG based on given values + const svg = makeBadge(badgeValues); + + response.type("image/svg+xml"); + response.send(svg); + } catch (error) { + sendHttpError(response, error.message); + } +}); + +router.get("/api/badge/:id/response", cache("5 minutes"), async (request, response) => { + allowAllOrigin(response); + + const { + label, + labelPrefix, + labelSuffix, + prefix, + suffix = badgeConstants.defaultPingValueSuffix, + color = badgeConstants.defaultPingColor, + labelColor, + style = badgeConstants.defaultStyle, + value, // for demo purpose only + } = request.query; + + try { + const requestedMonitorId = parseInt(request.params.id, 10); + if (Number.isNaN(requestedMonitorId)) { + throw new Error("Invalid monitor ID"); + } + + const overrideValue = value && parseFloat(value); + const publicMonitor = await isMonitorPublic(requestedMonitorId); + const badgeValues = { style }; + + if (!publicMonitor) { + // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non existent + + badgeValues.message = "N/A"; + badgeValues.color = badgeConstants.naColor; + } else { + const heartbeat = await Monitor.getPreviousHeartbeat(requestedMonitorId); + + if (!heartbeat.ping) { + // return a "N/A" badge in naColor (grey), if previous heartbeat has no ping + + badgeValues.message = "N/A"; + badgeValues.color = badgeConstants.naColor; + } else { + const ping = parseInt(overrideValue ?? heartbeat.ping); + + badgeValues.color = color; + // use a given, custom labelColor or use the default badge label color (defined by badge-maker) + badgeValues.labelColor = labelColor ?? ""; + // build a label string. If a custom label is given, override the default one + badgeValues.label = filterAndJoin([labelPrefix, label ?? "Response", labelSuffix]); + badgeValues.message = filterAndJoin([prefix, ping, suffix]); + } + } + + // build the SVG based on given values + const svg = makeBadge(badgeValues); + + response.type("image/svg+xml"); + response.send(svg); + } catch (error) { + sendHttpError(response, error.message); + } +}); + +/** + * Determines the status of the next beat in the push route handling. + * @param {string} status - The reported new status. + * @param {object} previousHeartbeat - The previous heartbeat object. + * @param {number} maxretries - The maximum number of retries allowed. + * @param {boolean} isUpsideDown - Indicates if the monitor is upside down. + * @param {object} bean - The new heartbeat object. + * @param {boolean} isExplicitDown - If status=down was explicitly pushed, bypass retries. + * @returns {void} + */ +function determineStatus(status, previousHeartbeat, maxretries, isUpsideDown, bean, isExplicitDown) { + if (isUpsideDown) { + status = flipStatus(status); + } + + // If status=down was explicitly pushed, bypass retry logic and go directly to DOWN + if (isExplicitDown && status === DOWN) { + bean.retries = 0; + bean.status = DOWN; + return; + } + + if (previousHeartbeat) { + if (previousHeartbeat.status === UP && status === DOWN) { + // Going Down + if (maxretries > 0 && previousHeartbeat.retries < maxretries) { + // Retries available + bean.retries = previousHeartbeat.retries + 1; + bean.status = PENDING; + } else { + // No more retries + bean.retries = 0; + bean.status = DOWN; + } + } else if (previousHeartbeat.status === PENDING && status === DOWN && previousHeartbeat.retries < maxretries) { + // Retries available + bean.retries = previousHeartbeat.retries + 1; + bean.status = PENDING; + } else { + // No more retries or not pending + if (status === DOWN) { + bean.retries = previousHeartbeat.retries + 1; + bean.status = status; + } else { + bean.retries = 0; + bean.status = status; + } + } + } else { + // First beat? + if (status === DOWN && maxretries > 0) { + // Retries available + bean.retries = 1; + bean.status = PENDING; + } else { + // Retires not enabled + bean.retries = 0; + bean.status = status; + } + } +} + +/** + * Check whether a monitor is publc + * @param {number} monitorID - Monitor id + * @returns {Promise} true if the monitor is public, otherwise false + */ +async function isMonitorPublic(monitorID) { + let publicMonitor = await R.getRow( + ` + SELECT monitor_group.monitor_id FROM monitor_group, \`group\` + WHERE monitor_group.group_id = \`group\`.id + AND monitor_group.monitor_id = ? + AND public = 1 + `, + [monitorID] + ); + return !!publicMonitor; +} + +module.exports = router; diff --git a/server/routers/better-auth-router.ts b/server/routers/better-auth-router.ts new file mode 100644 index 0000000..6bad8ac --- /dev/null +++ b/server/routers/better-auth-router.ts @@ -0,0 +1,134 @@ +import express from "express"; +import { toNodeHandler } from "better-auth/node"; +import { auth } from "../better-auth"; +import { R } from "redbean-node"; +import { log } from "../../src/util"; + +// @ts-ignore +import { allowDevOrigin } from "../util-server.js"; +import { generalErrorResponse } from "../util2"; + +let processingSetup = false; +let _hasUser = false; +let expired = false; +let setupTimeout: NodeJS.Timeout | null = null; + +const expiredMsg = "Setup has expired. Please restart the server to try again."; + +/** + * For testing: http://localhost:3001/api/auth/ok + * @returns Express Router with better-auth routes and setup route. + */ +export async function createBetterAuthRouter() { + const betterAuthRouter = express.Router(); + betterAuthRouter.all("/api/auth/*", async (req, res) => { + allowDevOrigin(req, res); + return toNodeHandler(auth())(req, res); + }); + + // First Setup + betterAuthRouter.post("/api/setup", async (req, res) => { + allowDevOrigin(req, res); + + try { + if (expired) { + log.error("auth", expiredMsg); + throw new Error(expiredMsg); + } + + if (processingSetup) { + throw new Error("Setup is already in progress. Please wait."); + } + + try { + if (!(await needSetup())) { + throw new Error( + "Uptime Kuma has been initialized. If you want to run setup again, please delete the database." + ); + } + processingSetup = true; + + const username = req.body.username; + const password = req.body.password; + + let user; + try { + user = await auth().api.createUser({ + body: { + name: username, + email: `${username}@noreply.uptime-kuma.internal`, + password, + role: "admin", + data: { + username, + }, + }, + }); + } catch (createError) { + // HIBP rejected after admin user creation, we need to clean up the partially created user + // very stupid.... https://github.com/better-auth/better-auth/issues/11706 + try { + if (username) { + await R.knex("better_auth_user").where({ username }).del(); + } + } catch (cleanupError) {} + throw createError; + } + + log.debug("auth", "First user created:", user); + + _hasUser = true; + res.json({ ok: true }); + } finally { + processingSetup = false; + } + } catch (e) { + log.error("auth", "Setup error:", e instanceof Error ? e.message : e); + generalErrorResponse(res, e); + } + }); + + return betterAuthRouter; +} + +/** + * + */ +export async function hasUser() { + if (_hasUser) { + return true; + } + const { count } = await R.knex("better_auth_user").count("id as count").first(); + return Number(count) !== 0; +} + +/** + * @returns Whether setup is needed. + */ +export async function needSetup() { + if (!setupTimeout) { + setupTimeout = setTimeout( + () => { + expired = true; + log.error("auth", expiredMsg); + }, + 1000 * 60 * 10 + ); + } + + if (expired) { + return false; + } + if (processingSetup) { + return false; + } + + if (await hasUser()) { + return false; + } + + // The user may be in the old user table, check that as well + const oldUserCountRow = await R.knex("user").count("id as count").first(); + const hasOldUser = Number(oldUserCountRow.count) !== 0; + return !hasOldUser; +} diff --git a/server/routers/status-page-router.js b/server/routers/status-page-router.js new file mode 100644 index 0000000..fda2962 --- /dev/null +++ b/server/routers/status-page-router.js @@ -0,0 +1,264 @@ +let express = require("express"); +const apicache = require("../modules/apicache"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); +const StatusPage = require("../model/status_page"); +const { allowDevAllOrigin, sendHttpError } = require("../util-server"); +const { R } = require("redbean-node"); +const { badgeConstants } = require("../../src/util"); +const { makeBadge } = require("badge-maker"); +const { UptimeCalculator } = require("../uptime-calculator"); + +let router = express.Router(); + +let cache = apicache.middleware; +const server = UptimeKumaServer.getInstance(); + +router.get("/status/:slug", cache("5 minutes"), async (request, response) => { + let slug = request.params.slug; + slug = slug.toLowerCase(); + await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug); +}); + +router.get("/status/:slug/rss", cache("5 minutes"), async (request, response) => { + let slug = request.params.slug; + slug = slug.toLowerCase(); + await StatusPage.handleStatusPageRSSResponse(response, slug, request); +}); + +router.get("/status", cache("5 minutes"), async (request, response) => { + let slug = "default"; + await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug); +}); + +router.get("/status-page", cache("5 minutes"), async (request, response) => { + let slug = "default"; + await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug); +}); + +// Status page config, incident, monitor list +router.get("/api/status-page/:slug", cache("5 minutes"), async (request, response) => { + allowDevAllOrigin(response); + let slug = request.params.slug; + slug = slug.toLowerCase(); + + try { + // Get Status Page + let statusPage = await R.findOne("status_page", " slug = ? ", [slug]); + + if (!statusPage) { + sendHttpError(response, "Status Page Not Found"); + return null; + } + + let statusPageData = await StatusPage.getStatusPageData(statusPage); + + // Response + response.json(statusPageData); + } catch (error) { + sendHttpError(response, error.message); + } +}); + +// Status Page Polling Data +// Can fetch only if published +router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (request, response) => { + allowDevAllOrigin(response); + + try { + let heartbeatList = {}; + let uptimeList = {}; + + let slug = request.params.slug; + slug = slug.toLowerCase(); + let statusPageID = await StatusPage.slugToID(slug); + + let monitorIDList = await R.getCol( + ` + SELECT monitor_group.monitor_id FROM monitor_group, \`group\` + WHERE monitor_group.group_id = \`group\`.id + AND public = 1 + AND \`group\`.status_page_id = ? + `, + [statusPageID] + ); + + for (let monitorID of monitorIDList) { + let list = await R.getAll( + ` + SELECT * FROM heartbeat + WHERE monitor_id = ? + ORDER BY time DESC + LIMIT 100 + `, + [monitorID] + ); + + list = R.convertToBeans("heartbeat", list); + heartbeatList[monitorID] = list.reverse().map((row) => row.toPublicJSON()); + + const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID); + uptimeList[`${monitorID}_24`] = uptimeCalculator.get24Hour().uptime; + } + + response.json({ + heartbeatList, + uptimeList, + }); + } catch (error) { + sendHttpError(response, error.message); + } +}); + +// Status page's manifest.json +router.get("/api/status-page/:slug/manifest.json", cache("1440 minutes"), async (request, response) => { + allowDevAllOrigin(response); + let slug = request.params.slug; + slug = slug.toLowerCase(); + + try { + // Get Status Page + let statusPage = await R.findOne("status_page", " slug = ? ", [slug]); + + if (!statusPage) { + sendHttpError(response, "Not Found"); + return; + } + + // Response + response.json({ + name: statusPage.title, + start_url: "/status/" + statusPage.slug, + display: "standalone", + icons: [ + { + src: statusPage.icon, + sizes: "128x128", + type: "image/png", + }, + ], + }); + } catch (error) { + sendHttpError(response, error.message); + } +}); + +router.get("/api/status-page/:slug/incident-history", cache("5 minutes"), async (request, response) => { + allowDevAllOrigin(response); + + try { + let slug = request.params.slug; + slug = slug.toLowerCase(); + let statusPageID = await StatusPage.slugToID(slug); + + if (!statusPageID) { + sendHttpError(response, "Status Page Not Found"); + return; + } + + const cursor = request.query.cursor || null; + const result = await StatusPage.getIncidentHistory(statusPageID, cursor, true); + response.json({ + ok: true, + ...result, + }); + } catch (error) { + sendHttpError(response, error.message); + } +}); + +// overall status-page status badge +router.get("/api/status-page/:slug/badge", cache("5 minutes"), async (request, response) => { + allowDevAllOrigin(response); + let slug = request.params.slug; + slug = slug.toLowerCase(); + const statusPageID = await StatusPage.slugToID(slug); + const { + label, + upColor = badgeConstants.defaultUpColor, + downColor = badgeConstants.defaultDownColor, + partialColor = "#F6BE00", + maintenanceColor = "#808080", + style = badgeConstants.defaultStyle, + } = request.query; + + try { + let monitorIDList = await R.getCol( + ` + SELECT monitor_group.monitor_id FROM monitor_group, \`group\` + WHERE monitor_group.group_id = \`group\`.id + AND public = 1 + AND \`group\`.status_page_id = ? + `, + [statusPageID] + ); + + let hasUp = false; + let hasDown = false; + let hasMaintenance = false; + + for (let monitorID of monitorIDList) { + // retrieve the latest heartbeat + let beat = await R.getAll( + ` + SELECT * FROM heartbeat + WHERE monitor_id = ? + ORDER BY time DESC + LIMIT 1 + `, + [monitorID] + ); + + // to be sure, when corresponding monitor not found + if (beat.length === 0) { + continue; + } + // handle status of beat + if (beat[0].status === 3) { + hasMaintenance = true; + } else if (beat[0].status === 2) { + // ignored + } else if (beat[0].status === 1) { + hasUp = true; + } else { + hasDown = true; + } + } + + const badgeValues = { style }; + + if (!hasUp && !hasDown && !hasMaintenance) { + // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant + + badgeValues.message = "N/A"; + badgeValues.color = badgeConstants.naColor; + } else { + if (hasMaintenance) { + badgeValues.label = label ? label : ""; + badgeValues.color = maintenanceColor; + badgeValues.message = "Maintenance"; + } else if (hasUp && !hasDown) { + badgeValues.label = label ? label : ""; + badgeValues.color = upColor; + badgeValues.message = "Up"; + } else if (hasUp && hasDown) { + badgeValues.label = label ? label : ""; + badgeValues.color = partialColor; + badgeValues.message = "Degraded"; + } else { + badgeValues.label = label ? label : ""; + badgeValues.color = downColor; + badgeValues.message = "Down"; + } + } + + // build the svg based on given values + const svg = makeBadge(badgeValues); + + response.type("image/svg+xml"); + response.send(svg); + } catch (error) { + sendHttpError(response, error.message); + } +}); + +module.exports = router; diff --git a/server/server.js b/server/server.js new file mode 100644 index 0000000..c27fc75 --- /dev/null +++ b/server/server.js @@ -0,0 +1,1612 @@ +/* + * Uptime Kuma Server + * node "server/server.js" + * DO NOT require("./server") in other modules, it likely creates circular dependency! + */ +import { getRandomInt, isDevEnv, log, sleep } from "../src/util"; +import { auth, doubleCheckPassword, getDisableAuthSession, getSession } from "./better-auth"; +import { createBetterAuthRouter, needSetup } from "./routers/better-auth-router"; +import { betterAuthSocketHandler } from "./socket-handlers/better-auth-socket-handler"; +import { loadEnvFile } from "node:process"; +import * as fs from "node:fs"; + +console.log("Welcome to Uptime Kuma"); + +// As the log function need to use dayjs, it should be very top +const dayjs = require("dayjs"); +dayjs.extend(require("dayjs/plugin/utc")); +dayjs.extend(require("./modules/dayjs/plugin/timezone")); +dayjs.extend(require("dayjs/plugin/customParseFormat")); + +// Load environment variables from `.env` +try { + loadEnvFile(); +} catch (_) {} + +// Check Node.js Version +const nodeVersion = process.versions.node; + +// Get the required Node.js version from package.json +const requiredNodeVersions = require("../package.json").engines.node; +const bannedNodeVersions = "< 24"; +console.log(`Your Node.js version: ${nodeVersion}`); + +const semver = require("semver"); +const requiredNodeVersionsComma = requiredNodeVersions + .split("||") + .map((version) => version.trim()) + .join(", "); + +// Exit Uptime Kuma immediately if the Node.js version is banned +if (semver.satisfies(nodeVersion, bannedNodeVersions)) { + console.error( + "\x1b[31m%s\x1b[0m", + `Error: Your Node.js version: ${nodeVersion} is not supported, please upgrade your Node.js to ${requiredNodeVersionsComma}.` + ); + process.exit(-1); +} + +// Warning if the Node.js version is not in the support list, but it maybe still works +if (!semver.satisfies(nodeVersion, requiredNodeVersions)) { + console.warn( + "\x1b[31m%s\x1b[0m", + `Warning: Your Node.js version: ${nodeVersion} is not officially supported, please upgrade your Node.js to ${requiredNodeVersionsComma}.` + ); +} + +const args = require("args-parser")(process.argv); +const config = require("./config"); + +process.title = "uptime-kuma"; + +log.debug("server", "Arguments"); +log.debug("server", args); + +if (!process.env.NODE_ENV) { + process.env.NODE_ENV = "production"; +} + +if (!process.env.UPTIME_KUMA_WS_ORIGIN_CHECK) { + process.env.UPTIME_KUMA_WS_ORIGIN_CHECK = "cors-like"; +} + +log.info("server", "Env: " + process.env.NODE_ENV); +log.debug("server", "Inside Container: " + (process.env.UPTIME_KUMA_IS_CONTAINER === "1")); + +if (process.env.UPTIME_KUMA_WS_ORIGIN_CHECK === "bypass") { + log.warn("server", "WebSocket Origin Check: " + process.env.UPTIME_KUMA_WS_ORIGIN_CHECK); +} + +if (isDevEnv() || process.env.UPTIME_KUMA_DEBUG_INSPECTOR === "1") { + const inspector = require("inspector"); + let inspectorHost = "127.0.0.1"; + + log.warn("server", "Node.js Inspector is enabled. You can connect to it via Chrome DevTools or VSCode."); + log.warn("server", "Node.js Inspector is listening on:", inspector.url()); + + if (process.env.UPTIME_KUMA_IS_CONTAINER === "1") { + log.warn( + "server", + "You need to expose the port 9229:9229 in your docker command or docker compose, and ssh tunneling in order to connect to it." + ); + inspectorHost = "0.0.0.0"; + } + + inspector.open(9229, inspectorHost); +} + +const checkVersion = require("./check-version"); +log.info("server", "Uptime Kuma Version:", checkVersion.version); + +log.info("server", "Loading modules"); + +log.debug("server", "Importing express"); +const express = require("express"); +const expressStaticGzip = require("express-static-gzip"); +log.debug("server", "Importing redbean-node"); +const { R } = require("redbean-node"); +log.debug("server", "Importing http-graceful-shutdown"); +const gracefulShutdown = require("http-graceful-shutdown"); +log.debug("server", "Importing prometheus-api-metrics"); +const prometheusAPIMetrics = require("prometheus-api-metrics"); + +const { UptimeKumaServer } = require("./uptime-kuma-server"); +const server = UptimeKumaServer.getInstance(); +const io = (module.exports.io = server.io); +const app = server.app; + +log.debug("server", "Importing Monitor"); +const Monitor = require("./model/monitor"); + +log.debug("server", "Importing Settings"); +const { + getSettings, + setSettings, + setting, + checkLogin, + allowDevAllOrigin, + printServerUrls, + allowDevOrigin, +} = require("./util-server"); + +log.debug("server", "Importing Notification"); +const { Notification } = require("./notification"); +Notification.init(); +log.debug("server", "Importing Web-Push"); +const webpush = require("web-push"); + +log.debug("server", "Importing Database"); +const Database = require("./database"); + +log.debug("server", "Importing Background Jobs"); +const { initBackgroundJobs, stopBackgroundJobs } = require("./jobs"); +const { apiAuth } = require("./auth"); +const { Prometheus } = require("./prometheus"); +const { UptimeCalculator } = require("./uptime-calculator"); + +const hostname = config.hostname; + +if (hostname) { + log.info("server", "Custom hostname: " + hostname); +} + +const port = config.port; + +const disableFrameSameOrigin = + !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || args["disable-frame-sameorigin"] || false; +const cloudflaredToken = args["cloudflared-token"] || process.env.UPTIME_KUMA_CLOUDFLARED_TOKEN || undefined; + +/** + * Run unit test after the server is ready + * @type {boolean} + */ +const testMode = !!args["test"] || false; + +// Must be after io instantiation +const { + sendNotificationList, + sendHeartbeatList, + sendInfo, + sendProxyList, + sendDockerHostList, + sendAPIKeyList, + sendRemoteBrowserList, + sendMonitorTypeList, +} = require("./client"); +const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler"); +const { databaseSocketHandler } = require("./socket-handlers/database-socket-handler"); +const { remoteBrowserSocketHandler } = require("./socket-handlers/remote-browser-socket-handler"); +const StatusPage = require("./model/status_page"); +const { + cloudflaredSocketHandler, + autoStart: cloudflaredAutoStart, + stop: cloudflaredStop, +} = require("./socket-handlers/cloudflared-socket-handler"); +const { proxySocketHandler } = require("./socket-handlers/proxy-socket-handler"); +const { dockerSocketHandler } = require("./socket-handlers/docker-socket-handler"); +const { maintenanceSocketHandler } = require("./socket-handlers/maintenance-socket-handler"); +const { apiKeySocketHandler } = require("./socket-handlers/api-key-socket-handler"); +const { generalSocketHandler } = require("./socket-handlers/general-socket-handler"); +const { Settings } = require("./settings"); +const apicache = require("./modules/apicache"); +const { resetChrome } = require("./monitor-types/real-browser-monitor-type"); +const { EmbeddedMariaDB } = require("./embedded-mariadb"); +const { SetupDatabase } = require("./setup-database"); +const { chartSocketHandler } = require("./socket-handlers/chart-socket-handler"); + +app.use(express.json()); + +// Global Middleware +app.use(function (req, res, next) { + if (!disableFrameSameOrigin) { + res.setHeader("X-Frame-Options", "SAMEORIGIN"); + } + res.removeHeader("X-Powered-By"); + next(); +}); + +(async () => { + // Create a data directory + Database.initDataDir(args); + + // Check if is chosen a database type + let setupDatabase = new SetupDatabase(args, server); + if (setupDatabase.isNeedSetup()) { + // Hold here and start a special setup page until user choose a database type + await setupDatabase.start(hostname, port); + } + + // Connect to database + try { + await initDatabase(testMode); + } catch (e) { + log.error("server", "Failed to prepare your database: " + e.message); + process.exit(1); + } + + // Init Better Auth + auth(); + + // Database should be ready now + await server.initAfterDatabaseReady(); + server.entryPage = await Settings.get("entryPage"); + await StatusPage.loadDomainMappingList(); + + log.debug("server", "Initializing Prometheus"); + await Prometheus.init(); + + log.debug("server", "Adding route"); + + // *************************** + // Normal Router here + // *************************** + + // Entry Page + app.get("/", async (request, response) => { + let hostname = request.hostname; + if (await setting("trustProxy")) { + const proxy = request.headers["x-forwarded-host"]; + if (proxy) { + hostname = proxy; + } + } + + log.debug("entry", `Request Domain: ${hostname}`); + + const uptimeKumaEntryPage = server.entryPage; + if (hostname in StatusPage.domainMappingList) { + log.debug("entry", "This is a status page domain"); + + let slug = StatusPage.domainMappingList[hostname]; + await StatusPage.handleStatusPageResponse(response, server.indexHTML, slug); + } else if (uptimeKumaEntryPage && uptimeKumaEntryPage.startsWith("statusPage-")) { + response.redirect("/status/" + uptimeKumaEntryPage.replace("statusPage-", "")); + } else { + response.redirect("/dashboard"); + } + }); + + app.get("/setup-database-info", (request, response) => { + allowDevAllOrigin(response); + response.json({ + runningSetup: false, + needSetup: false, + }); + }); + + if (isDevEnv()) { + app.options("/*", async (request, response) => { + allowDevOrigin(request, response); + response.end(); + }); + + app.use(express.urlencoded({ extended: true })); + app.post("/test-webhook", async (request, response) => { + log.debug("test", request.headers); + log.debug("test", request.body); + response.send("OK"); + }); + + app.get("/_e2e/take-sqlite-snapshot", async (request, response) => { + // Checkpoint WAL to flush all data to the main .db file, then copy. + // No close/reopen needed — the file is consistent after checkpoint. + await R.exec("PRAGMA wal_checkpoint(TRUNCATE)"); + fs.cpSync(Database.sqlitePath, `${Database.sqlitePath}.e2e-snapshot`); + response.send("Snapshot taken."); + }); + + app.get("/_e2e/restore-sqlite-snapshot", async (request, response) => { + if (!fs.existsSync(`${Database.sqlitePath}.e2e-snapshot`)) { + throw new Error("Snapshot doesn't exist."); + } + + await Database.close(); + try { + fs.cpSync(`${Database.sqlitePath}.e2e-snapshot`, Database.sqlitePath); + } catch (err) { + throw new Error("Unable to copy snapshot file."); + } + await Database.connect(); + + response.send("Snapshot restored."); + }); + + app.post("/test-x-www-form-urlencoded", async (request, response) => { + log.debug("test", request.headers); + log.debug("test", request.body); + response.send("OK"); + }); + } + + // Robots.txt + app.get("/robots.txt", async (_request, response) => { + let txt = "User-agent: *\nDisallow:"; + if (!(await setting("searchEngineIndex"))) { + txt += " /"; + } + response.setHeader("Content-Type", "text/plain"); + response.send(txt); + }); + + // Basic Auth Router here + + // Prometheus API metrics /metrics + // With Basic Auth using the first user's username/password + app.get("/metrics", apiAuth, prometheusAPIMetrics()); + + app.use( + "/", + expressStaticGzip("dist", { + enableBrotli: true, + }) + ); + + // ./data/upload + app.use("/upload", express.static(Database.uploadDir)); + + app.get("/.well-known/change-password", async (_, response) => { + response.redirect("https://github.com/louislam/uptime-kuma/wiki/Reset-Password-via-CLI"); + }); + + // API Router + const apiRouter = require("./routers/api-router"); + app.use(apiRouter); + + // Status Page Router + const statusPageRouter = require("./routers/status-page-router"); + app.use(statusPageRouter); + + // better auth API Router + const betterAuthRouter = await createBetterAuthRouter(); + app.use(betterAuthRouter); + + // Universal Route Handler, must be at the end of all express routes. + app.get("*", async (_request, response) => { + if (_request.originalUrl.startsWith("/upload/")) { + response.status(404).send("File not found."); + } else { + response.send(server.indexHTML); + } + }); + + log.debug("server", "Adding socket handler"); + io.on("connection", async (socket) => { + await sendInfo(socket, true); + + if (await needSetup()) { + log.info("server", "Redirect to setup page"); + socket.emit("setup"); + } + + let session; + + if (!(await Settings.get("disableAuth"))) { + session = await getSession(socket.request.headers.cookie); + } else { + session = await getDisableAuthSession(); + } + + if (session) { + socket.userID = session.user.id; + socket.session = session; + socket.emit("session", session.user.username); + log.debug("auth", `Session active:`, session.session.ipAddress, session.user.username); + } + + // *************************** + // Auth Only API + // *************************** + + // Add a new monitor + socket.on("add", async (monitor, callback) => { + try { + checkLogin(socket); + let bean = R.dispense("monitor"); + + let notificationIDList = monitor.notificationIDList; + delete monitor.notificationIDList; + + // Ensure status code ranges are strings + if (!monitor.accepted_statuscodes.every((code) => typeof code === "string")) { + throw new Error("Accepted status codes are not all strings"); + } + monitor.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes); + delete monitor.accepted_statuscodes; + + monitor.kafkaProducerBrokers = JSON.stringify(monitor.kafkaProducerBrokers); + monitor.kafkaProducerSaslOptions = JSON.stringify(monitor.kafkaProducerSaslOptions); + + monitor.conditions = JSON.stringify(monitor.conditions); + + monitor.rabbitmqNodes = JSON.stringify(monitor.rabbitmqNodes); + + /* + * List of frontend-only properties that should not be saved to the database. + * Should clean up before saving to the database. + */ + const frontendOnlyProperties = [ + "humanReadableInterval", + "globalpingdnsresolvetypeoptions", + "responsecheck", + ]; + for (const prop of frontendOnlyProperties) { + if (prop in monitor) { + delete monitor[prop]; + } + } + + bean.import(monitor); + // Map camelCase frontend property to snake_case database column + if (monitor.retryOnlyOnStatusCodeFailure !== undefined) { + bean.retry_only_on_status_code_failure = monitor.retryOnlyOnStatusCodeFailure; + } + bean.user_id = socket.userID; + + bean.validate(); + + await R.store(bean); + + await updateMonitorNotification(bean.id, notificationIDList); + + await server.sendUpdateMonitorIntoList(socket, bean.id); + + if (monitor.active !== false) { + await startMonitor(socket.userID, bean.id); + } + + log.info("monitor", `Added Monitor: ${bean.id} User ID: ${socket.userID}`); + + callback({ + ok: true, + msg: "successAdded", + msgi18n: true, + monitorID: bean.id, + }); + } catch (e) { + log.error("monitor", `Error adding Monitor: ${monitor.id} User ID: ${socket.userID}`); + + callback({ + ok: false, + msg: e.message, + }); + } + }); + + // Edit a monitor + socket.on("editMonitor", async (monitor, callback) => { + try { + let removeGroupChildren = false; + checkLogin(socket); + + let bean = await R.findOne("monitor", " id = ? ", [monitor.id]); + + // Check if Parent is Descendant (would cause endless loop) + if (monitor.parent !== null) { + const childIDs = await Monitor.getAllChildrenIDs(monitor.id); + if (childIDs.includes(monitor.parent)) { + throw new Error("Invalid Monitor Group"); + } + } + + // Remove children if monitor type has changed (from group to non-group) + if (bean.type === "group" && monitor.type !== bean.type) { + removeGroupChildren = true; + } + + // Ensure status code ranges are strings + if (!monitor.accepted_statuscodes.every((code) => typeof code === "string")) { + throw new Error("Accepted status codes are not all strings"); + } + + bean.name = monitor.name; + bean.description = monitor.description; + bean.parent = monitor.parent; + bean.type = monitor.type; + bean.subtype = monitor.subtype; + bean.url = monitor.url; + bean.wsIgnoreSecWebsocketAcceptHeader = monitor.wsIgnoreSecWebsocketAcceptHeader; + bean.wsSubprotocol = monitor.wsSubprotocol; + bean.method = monitor.method; + bean.body = monitor.body; + bean.ipFamily = monitor.ipFamily; + bean.headers = monitor.headers; + bean.basic_auth_user = monitor.basic_auth_user; + bean.basic_auth_pass = monitor.basic_auth_pass; + bean.bearer_token = monitor.bearer_token; + bean.timeout = monitor.timeout; + bean.oauth_client_id = monitor.oauth_client_id; + bean.oauth_client_secret = monitor.oauth_client_secret; + bean.oauth_auth_method = monitor.oauth_auth_method; + bean.oauth_token_url = monitor.oauth_token_url; + bean.oauth_scopes = monitor.oauth_scopes; + bean.oauth_audience = monitor.oauth_audience; + bean.tlsCa = monitor.tlsCa; + bean.tlsCert = monitor.tlsCert; + bean.tlsKey = monitor.tlsKey; + bean.interval = monitor.interval; + bean.retryInterval = monitor.retryInterval; + bean.resendInterval = monitor.resendInterval; + bean.hostname = monitor.hostname; + bean.game = monitor.game; + bean.maxretries = monitor.maxretries; + bean.port = parseInt(monitor.port); + bean.location = monitor.location; + bean.protocol = monitor.protocol; + + if (isNaN(bean.port)) { + bean.port = null; + } + + bean.keyword = monitor.keyword; + bean.invertKeyword = monitor.invertKeyword; + bean.ignoreTls = monitor.ignoreTls; + bean.expiryNotification = monitor.expiryNotification; + bean.domainExpiryNotification = monitor.domainExpiryNotification; + bean.upsideDown = monitor.upsideDown; + bean.packetSize = monitor.packetSize; + bean.maxredirects = monitor.maxredirects; + bean.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes); + bean.save_response = monitor.saveResponse; + bean.save_error_response = monitor.saveErrorResponse; + bean.response_max_length = monitor.responseMaxLength; + bean.dns_resolve_type = monitor.dns_resolve_type; + bean.dns_resolve_server = monitor.dns_resolve_server; + bean.pushToken = monitor.pushToken; + bean.docker_container = monitor.docker_container; + bean.docker_host = monitor.docker_host; + bean.proxyId = Number.isInteger(monitor.proxyId) ? monitor.proxyId : null; + bean.mqttUsername = monitor.mqttUsername; + bean.mqttPassword = monitor.mqttPassword; + bean.mqttTopic = monitor.mqttTopic; + bean.mqttSuccessMessage = monitor.mqttSuccessMessage; + bean.mqttCheckType = monitor.mqttCheckType; + bean.mqttWebsocketPath = monitor.mqttWebsocketPath; + bean.databaseConnectionString = monitor.databaseConnectionString; + bean.databaseQuery = monitor.databaseQuery; + bean.authMethod = monitor.authMethod; + bean.authWorkstation = monitor.authWorkstation; + bean.authDomain = monitor.authDomain; + bean.grpcUrl = monitor.grpcUrl; + bean.grpcProtobuf = monitor.grpcProtobuf; + bean.grpcServiceName = monitor.grpcServiceName; + bean.grpcMethod = monitor.grpcMethod; + bean.grpcBody = monitor.grpcBody; + bean.grpcMetadata = monitor.grpcMetadata; + bean.grpcEnableTls = monitor.grpcEnableTls; + bean.radiusUsername = monitor.radiusUsername; + bean.radiusPassword = monitor.radiusPassword; + bean.radiusCalledStationId = monitor.radiusCalledStationId; + bean.radiusCallingStationId = monitor.radiusCallingStationId; + bean.radiusSecret = monitor.radiusSecret; + bean.httpBodyEncoding = monitor.httpBodyEncoding; + bean.expectedValue = monitor.expectedValue; + bean.jsonPath = monitor.jsonPath; + bean.kafkaProducerTopic = monitor.kafkaProducerTopic; + bean.kafkaProducerBrokers = JSON.stringify(monitor.kafkaProducerBrokers); + bean.kafkaProducerAllowAutoTopicCreation = monitor.kafkaProducerAllowAutoTopicCreation; + bean.kafkaProducerSaslOptions = JSON.stringify(monitor.kafkaProducerSaslOptions); + bean.kafkaProducerMessage = monitor.kafkaProducerMessage; + bean.cacheBust = monitor.cacheBust; + bean.kafkaProducerSsl = monitor.kafkaProducerSsl; + bean.kafkaProducerAllowAutoTopicCreation = monitor.kafkaProducerAllowAutoTopicCreation; + bean.gamedigGivenPortOnly = monitor.gamedigGivenPortOnly; + bean.gamedigToken = monitor.gamedigToken; + bean.remote_browser = monitor.remote_browser; + bean.screenshot_delay = monitor.screenshot_delay; + bean.smtpSecurity = monitor.smtpSecurity; + bean.snmpVersion = monitor.snmpVersion; + bean.snmpOid = monitor.snmpOid; + bean.jsonPathOperator = monitor.jsonPathOperator; + bean.retry_only_on_status_code_failure = Boolean(monitor.retryOnlyOnStatusCodeFailure); + bean.timeout = monitor.timeout; + bean.rabbitmqNodes = JSON.stringify(monitor.rabbitmqNodes); + bean.rabbitmqUsername = monitor.rabbitmqUsername; + bean.rabbitmqPassword = monitor.rabbitmqPassword; + bean.conditions = JSON.stringify(monitor.conditions); + bean.manual_status = monitor.manual_status; + bean.system_service_name = monitor.system_service_name; + bean.expected_tls_alert = monitor.expectedTlsAlert; + bean.sshUsername = monitor.sshUsername; + bean.sshPassword = monitor.sshPassword; + bean.sftpPath = monitor.sftpPath; + bean.sshPrivateKey = monitor.sshPrivateKey; + bean.sshPassphrase = monitor.sshPassphrase; + bean.sshAuthMethod = monitor.sshAuthMethod; + bean.ntp_stratum_threshold = monitor.ntpStratumThreshold; + bean.ntp_time_offset_threshold = monitor.ntpTimeOffsetThreshold; + bean.ntp_root_dispersion_threshold = monitor.ntpRootDispersionThreshold; + + // ping advanced options + bean.ping_numeric = monitor.ping_numeric; + bean.ping_count = monitor.ping_count; + bean.ping_per_request_timeout = monitor.ping_per_request_timeout; + + bean.validate(); + + await R.store(bean); + + if (removeGroupChildren) { + await Monitor.unlinkAllChildren(monitor.id); + } + + await updateMonitorNotification(bean.id, monitor.notificationIDList); + + if (await Monitor.isActive(bean.id, bean.active)) { + await restartMonitor(socket.userID, bean.id); + } + + await server.sendUpdateMonitorIntoList(socket, bean.id); + + callback({ + ok: true, + msg: "Saved.", + msgi18n: true, + monitorID: bean.id, + }); + } catch (e) { + log.error("monitor", e); + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("getMonitorList", async (callback) => { + try { + checkLogin(socket); + await server.sendMonitorList(socket); + callback({ + ok: true, + }); + } catch (e) { + log.error("monitor", e); + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("getMonitor", async (monitorID, callback) => { + try { + checkLogin(socket); + + log.info("monitor", `Get Monitor: ${monitorID} User ID: ${socket.userID}`); + + let monitor = await R.findOne("monitor", " id = ? ", [monitorID]); + const monitorData = [{ id: monitor.id, active: monitor.active }]; + const preloadData = await Monitor.preparePreloadData(monitorData); + callback({ + ok: true, + monitor: monitor.toJSON(preloadData), + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + // partial { type, url, hostname, grpcUrl } + socket.on("checkDomain", async (partial, callback) => { + try { + checkLogin(socket); + const DomainExpiry = require("./model/domain_expiry"); + const supportInfo = await DomainExpiry.checkSupport(partial); + callback({ + ok: true, + domain: supportInfo.domain, + tld: supportInfo.tld, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + msgi18n: !!e.msgi18n, + meta: e.meta ?? {}, + }); + } + }); + + socket.on("getMonitorBeats", async (monitorID, period, callback) => { + try { + checkLogin(socket); + + log.info("monitor", `Get Monitor Beats: ${monitorID} User ID: ${socket.userID}`); + + if (period == null) { + throw new Error("Invalid period."); + } + + const sqlHourOffset = Database.sqlHourOffset(); + + let list = await R.getAll( + ` + SELECT * + FROM heartbeat + WHERE monitor_id = ? + AND time > ${sqlHourOffset} + ORDER BY time ASC + `, + [monitorID, -period] + ); + + callback({ + ok: true, + data: list, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + // Start or Resume the monitor + socket.on("resumeMonitor", async (monitorID, callback) => { + try { + checkLogin(socket); + await startMonitor(socket.userID, monitorID); + await server.sendUpdateMonitorIntoList(socket, monitorID); + + callback({ + ok: true, + msg: "successResumed", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("pauseMonitor", async (monitorID, callback) => { + try { + checkLogin(socket); + await pauseMonitor(socket.userID, monitorID); + await server.sendUpdateMonitorIntoList(socket, monitorID); + + callback({ + ok: true, + msg: "successPaused", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("deleteMonitor", async (monitorID, deleteChildren, callback) => { + try { + // Backward compatibility: if deleteChildren is omitted, the second parameter is the callback + if (typeof deleteChildren === "function") { + callback = deleteChildren; + deleteChildren = false; + } + + checkLogin(socket); + + const startTime = Date.now(); + + // Check if this is a group monitor + const monitor = await R.findOne("monitor", " id = ? ", [monitorID]); + + // Log with context about deletion type + if (monitor && monitor.type === "group") { + if (deleteChildren) { + log.info("manage", `Delete Group and Children: ${monitorID} User ID: ${socket.userID}`); + } else { + log.info("manage", `Delete Group (unlink children): ${monitorID} User ID: ${socket.userID}`); + } + } else { + log.info("manage", `Delete Monitor: ${monitorID} User ID: ${socket.userID}`); + } + + if (monitor && monitor.type === "group") { + // Get all children before processing + const children = await Monitor.getChildren(monitorID); + + if (deleteChildren) { + // Delete all child monitors recursively + if (children && children.length > 0) { + for (const child of children) { + await Monitor.deleteMonitorRecursively(child.id); + await server.sendDeleteMonitorFromList(socket, child.id); + } + } + } else { + // Unlink all children from the group (set parent to null) + await Monitor.unlinkAllChildren(monitorID); + + // Notify frontend to update each child monitor's parent to null + if (children && children.length > 0) { + for (const child of children) { + await server.sendUpdateMonitorIntoList(socket, child.id); + } + } + } + } + + // Delete the monitor itself + await Monitor.deleteMonitor(monitorID); + + // Fix #2880 + apicache.clear(); + + const endTime = Date.now(); + + // Log completion with context about children handling + if (monitor && monitor.type === "group") { + if (deleteChildren) { + log.info( + "DB", + `Delete Monitor completed (group and children deleted) in: ${endTime - startTime} ms` + ); + } else { + log.info( + "DB", + `Delete Monitor completed (group deleted, children unlinked) in: ${endTime - startTime} ms` + ); + } + } else { + log.info("DB", `Delete Monitor completed in: ${endTime - startTime} ms`); + } + + callback({ + ok: true, + msg: "successDeleted", + msgi18n: true, + }); + await server.sendDeleteMonitorFromList(socket, monitorID); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("getTags", async (callback) => { + try { + checkLogin(socket); + + const list = await R.findAll("tag"); + + callback({ + ok: true, + tags: list.map((bean) => bean.toJSON()), + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("addTag", async (tag, callback) => { + try { + checkLogin(socket); + + let bean = R.dispense("tag"); + bean.name = tag.name; + bean.color = tag.color; + await R.store(bean); + + callback({ + ok: true, + tag: await bean.toJSON(), + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("editTag", async (tag, callback) => { + try { + checkLogin(socket); + + let bean = await R.findOne("tag", " id = ? ", [tag.id]); + if (bean == null) { + callback({ + ok: false, + msg: "tagNotFound", + msgi18n: true, + }); + return; + } + bean.name = tag.name; + bean.color = tag.color; + await R.store(bean); + + callback({ + ok: true, + msg: "Saved.", + msgi18n: true, + tag: await bean.toJSON(), + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("deleteTag", async (tagID, callback) => { + try { + checkLogin(socket); + + await R.exec("DELETE FROM tag WHERE id = ? ", [tagID]); + + callback({ + ok: true, + msg: "successDeleted", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("addMonitorTag", async (tagID, monitorID, value, callback) => { + try { + checkLogin(socket); + + await R.exec("INSERT INTO monitor_tag (tag_id, monitor_id, value) VALUES (?, ?, ?)", [ + tagID, + monitorID, + value, + ]); + + await server.sendUpdateMonitorIntoList(socket, monitorID); + + callback({ + ok: true, + msg: "successAdded", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("editMonitorTag", async (tagID, monitorID, value, callback) => { + try { + checkLogin(socket); + + await R.exec("UPDATE monitor_tag SET value = ? WHERE tag_id = ? AND monitor_id = ?", [ + value, + tagID, + monitorID, + ]); + + await server.sendUpdateMonitorIntoList(socket, monitorID); + + callback({ + ok: true, + msg: "successEdited", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("deleteMonitorTag", async (tagID, monitorID, value, callback) => { + try { + checkLogin(socket); + + await R.exec("DELETE FROM monitor_tag WHERE tag_id = ? AND monitor_id = ? AND value = ?", [ + tagID, + monitorID, + value, + ]); + + await server.sendUpdateMonitorIntoList(socket, monitorID); + + callback({ + ok: true, + msg: "successDeleted", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("monitorImportantHeartbeatListCount", async (monitorID, callback) => { + try { + checkLogin(socket); + + let count; + if (monitorID == null) { + count = await R.count("heartbeat", "important = 1"); + } else { + count = await R.count("heartbeat", "monitor_id = ? AND important = 1", [monitorID]); + } + + callback({ + ok: true, + count: count, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("monitorImportantHeartbeatListPaged", async (monitorID, offset, count, callback) => { + try { + checkLogin(socket); + + let list; + if (monitorID == null) { + list = await R.find( + "heartbeat", + ` + important = 1 + ORDER BY time DESC + LIMIT ? + OFFSET ? + `, + [count, offset] + ); + } else { + list = await R.find( + "heartbeat", + ` + monitor_id = ? + AND important = 1 + ORDER BY time DESC + LIMIT ? + OFFSET ? + `, + [monitorID, count, offset] + ); + } + + callback({ + ok: true, + data: list, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("getSettings", async (callback) => { + try { + checkLogin(socket); + const data = await getSettings("general"); + + if (!data.serverTimezone) { + data.serverTimezone = await server.getTimezone(); + } + + callback({ + ok: true, + data: data, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("setSettings", async (data, currentPassword, callback) => { + try { + checkLogin(socket); + + // If currently is disabled auth, don't need to check + // Disabled Auth + Want to Disable Auth => No Check + // Disabled Auth + Want to Enable Auth => No Check + // Enabled Auth + Want to Disable Auth => Check!! + // Enabled Auth + Want to Enable Auth => No Check + const currentDisabledAuth = await setting("disableAuth"); + if (!currentDisabledAuth && data.disableAuth) { + await doubleCheckPassword(socket.request.headers.cookie, currentPassword); + } + + // Log out all clients if enabling auth + // GHSA-23q2-5gf8-gjpp + if (currentDisabledAuth && !data.disableAuth) { + server.disconnectAllSocketClients(socket.userID, socket.id); + } + + const previousChromeExecutable = await Settings.get("chromeExecutable"); + const previousNSCDStatus = await Settings.get("nscd"); + + await setSettings("general", data); + server.entryPage = data.entryPage; + + // Also need to apply timezone globally + if (data.serverTimezone) { + await server.setTimezone(data.serverTimezone); + } + + // If Chrome Executable is changed, need to reset the browser + if (previousChromeExecutable !== data.chromeExecutable) { + log.info("settings", "Chrome executable is changed. Resetting Chrome..."); + await resetChrome(); + } + + // Update nscd status + if (previousNSCDStatus !== data.nscd) { + if (data.nscd) { + await server.startNSCDServices(); + } else { + await server.stopNSCDServices(); + } + } + + callback({ + ok: true, + msg: "Saved.", + msgi18n: true, + }); + + await sendInfo(socket); + await server.sendMaintenanceList(socket); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + // Add or Edit + socket.on("addNotification", async (notification, notificationID, callback) => { + try { + checkLogin(socket); + + let notificationBean = await Notification.save(notification, notificationID, socket.userID); + await sendNotificationList(socket); + + callback({ + ok: true, + msg: "Saved.", + msgi18n: true, + id: notificationBean.id, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("deleteNotification", async (notificationID, callback) => { + try { + checkLogin(socket); + + await Notification.delete(notificationID); + await sendNotificationList(socket); + + callback({ + ok: true, + msg: "successDeleted", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("testNotification", async (notification, callback) => { + try { + checkLogin(socket); + + let msg = await Notification.send(notification, notification.name + " Testing"); + + callback({ + ok: true, + msg, + }); + } catch (e) { + log.error("server", e); + + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("checkApprise", async (callback) => { + try { + checkLogin(socket); + callback(await Notification.checkApprise()); + } catch (e) { + callback(false); + } + }); + + socket.on("getWebpushVapidPublicKey", async (callback) => { + try { + let publicVapidKey = await Settings.get("webpushPublicVapidKey"); + + if (!publicVapidKey) { + log.debug("webpush", "Generating new VAPID keys"); + const vapidKeys = webpush.generateVAPIDKeys(); + + await Settings.set("webpushPublicVapidKey", vapidKeys.publicKey); + await Settings.set("webpushPrivateVapidKey", vapidKeys.privateKey); + + publicVapidKey = vapidKeys.publicKey; + } + + callback({ + ok: true, + msg: publicVapidKey, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("clearEvents", async (monitorID, callback) => { + try { + checkLogin(socket); + + log.info("manage", `Clear Events Monitor: ${monitorID} User ID: ${socket.userID}`); + + await R.exec("UPDATE heartbeat SET msg = ?, important = ? WHERE monitor_id = ? ", ["", "0", monitorID]); + + callback({ + ok: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("clearHeartbeats", async (monitorID, callback) => { + try { + checkLogin(socket); + + log.info("manage", `Clear Heartbeats Monitor: ${monitorID} User ID: ${socket.userID}`); + + await UptimeCalculator.clearStatistics(monitorID); + + if (monitorID in server.monitorList) { + const monitor = server.monitorList[monitorID]; + if (monitor.active) { + await restartMonitor(socket.userID, monitorID); + } + } + + await sendHeartbeatList(socket, monitorID, true, true); + + callback({ + ok: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("clearStatistics", async (callback) => { + try { + checkLogin(socket); + + log.info("manage", `Clear Statistics User ID: ${socket.userID}`); + + await UptimeCalculator.clearAllStatistics(); + + // Restart all monitors to reset the stats + for (let monitorID in server.monitorList) { + const monitor = server.monitorList[monitorID]; + if (monitor.active) { + await restartMonitor(socket.userID, monitorID); + } + } + + callback({ + ok: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + betterAuthSocketHandler(socket); + + // Status Page Socket Handler for admin only + statusPageSocketHandler(socket); + cloudflaredSocketHandler(socket); + databaseSocketHandler(socket); + proxySocketHandler(socket); + dockerSocketHandler(socket); + maintenanceSocketHandler(socket); + apiKeySocketHandler(socket); + remoteBrowserSocketHandler(socket); + generalSocketHandler(socket, server); + chartSocketHandler(socket); + + log.debug("server", "added all socket handlers"); + + // *************************** + // Better do anything after added all socket handlers here + // *************************** + + if (session) { + await afterLogin(socket, session.user); + } else { + socket.emit("loginRequired"); + log.debug("auth", "need auth"); + } + }); + + log.debug("server", "Init the server"); + + server.httpServer.once("error", async (err) => { + log.error("server", "Cannot listen: " + err.message); + await shutdownFunction(); + process.exit(1); + }); + + await server.start(); + + server.httpServer.listen(port, hostname, async () => { + printServerUrls("server", port, hostname, config.isSSL); + + await startMonitors(); + + // Put this here. Start background jobs after the db and server is ready to prevent clear up during db migration. + await initBackgroundJobs(); + + checkVersion.startInterval(); + }); + + // Start cloudflared at the end if configured + await cloudflaredAutoStart(cloudflaredToken); +})(); + +/** + * Update notifications for a given monitor + * @param {number} monitorID ID of monitor to update + * @param {number[]} notificationIDList List of new notification + * providers to add + * @returns {Promise} + */ +async function updateMonitorNotification(monitorID, notificationIDList) { + await R.exec("DELETE FROM monitor_notification WHERE monitor_id = ? ", [monitorID]); + + for (let notificationID in notificationIDList) { + if (notificationIDList[notificationID]) { + let relation = R.dispense("monitor_notification"); + relation.monitor_id = monitorID; + relation.notification_id = notificationID; + await R.store(relation); + } + } +} + +/** + * Function called after user login + * This function is used to send the heartbeat list of a monitor. + * @param {Socket} socket Socket.io instance + * @param {object} user User object + * @returns {Promise} + */ +async function afterLogin(socket, user) { + socket.userID = user.id; + socket.join(user.id); + + let monitorList = await server.sendMonitorList(socket); + await Promise.allSettled([ + sendInfo(socket), + server.sendMaintenanceList(socket), + sendNotificationList(socket), + sendProxyList(socket), + sendDockerHostList(socket), + sendAPIKeyList(socket), + sendRemoteBrowserList(socket), + sendMonitorTypeList(socket), + ]); + + await StatusPage.sendStatusPageList(io, socket); + + const monitorPromises = []; + for (let monitorID in monitorList) { + monitorPromises.push(sendHeartbeatList(socket, monitorID)); + monitorPromises.push(Monitor.sendStats(io, monitorID, user.id)); + } + + await Promise.all(monitorPromises); + + // Set server timezone from client browser if not set + // It should be run once only + if (!(await Settings.get("initServerTimezone"))) { + log.debug("server", "emit initServerTimezone"); + socket.emit("initServerTimezone"); + } +} + +/** + * Initialize the database + * @param {boolean} testMode Should the connection be + * started in test mode? + * @returns {Promise} + */ +async function initDatabase(testMode = false) { + log.debug("server", "Connecting to the database"); + await Database.connect(testMode); + log.info("server", "Connected to the database"); + + // Patch the database + await Database.patch(port, hostname); +} + +/** + * Start the specified monitor + * @param {number} userID ID of user who owns monitor + * @param {number} monitorID ID of monitor to start + * @returns {Promise} + */ +async function startMonitor(userID, monitorID) { + log.info("manage", `Resume Monitor: ${monitorID} User ID: ${userID}`); + + await R.exec("UPDATE monitor SET active = 1 WHERE id = ? ", [monitorID]); + + let monitor = await R.findOne("monitor", " id = ? ", [monitorID]); + + if (monitor.id in server.monitorList) { + await server.monitorList[monitor.id].stop(); + } + + server.monitorList[monitor.id] = monitor; + await monitor.start(io); +} + +/** + * Restart a given monitor + * @param {number} userID ID of user who owns monitor + * @param {number} monitorID ID of monitor to start + * @returns {Promise} + */ +async function restartMonitor(userID, monitorID) { + return await startMonitor(userID, monitorID); +} + +/** + * Pause a given monitor + * @param {number} userID ID of user who owns monitor + * @param {number} monitorID ID of monitor to start + * @returns {Promise} + */ +async function pauseMonitor(userID, monitorID) { + log.info("manage", `Pause Monitor: ${monitorID} User ID: ${userID}`); + + await R.exec("UPDATE monitor SET active = 0 WHERE id = ? ", [monitorID]); + + if (monitorID in server.monitorList) { + await server.monitorList[monitorID].stop(); + server.monitorList[monitorID].active = 0; + } +} + +/** + * Resume active monitors + * @returns {Promise} + */ +async function startMonitors() { + let list = await R.find("monitor", " active = 1 "); + + for (let monitor of list) { + server.monitorList[monitor.id] = monitor; + } + + for (let monitor of list) { + try { + await monitor.start(io); + } catch (e) { + log.error("monitor", e); + } + // Give some delays, so all monitors won't make request at the same moment when just start the server. + await sleep(getRandomInt(300, 1000)); + } +} + +/** + * Shutdown the application + * Stops all monitors and closes the database connection. + * @param {string} signal The signal that triggered this function to be called. + * @returns {Promise} + */ +async function shutdownFunction(signal) { + log.info("server", "Shutdown requested"); + log.info("server", "Called signal: " + signal); + + await server.stop(); + + log.info("server", "Stopping all monitors"); + for (let id in server.monitorList) { + let monitor = server.monitorList[id]; + await monitor.stop(); + } + await sleep(2000); + await Database.close(); + + if (EmbeddedMariaDB.hasInstance()) { + EmbeddedMariaDB.getInstance().stop(); + } + + stopBackgroundJobs(); + await cloudflaredStop(); + Settings.stopCacheCleaner(); +} + +/** + * Final function called before application exits + * @returns {void} + */ +function finalFunction() { + log.info("server", "Graceful shutdown successful!"); +} + +gracefulShutdown(server.httpServer, { + signals: "SIGINT SIGTERM", + timeout: 30000, // timeout: 30 secs + development: false, // not in dev mode + forceExit: true, // triggers process.exit() at the end of shutdown process + onShutdown: shutdownFunction, // shutdown function (async) - e.g. for cleanup DB, ... + finally: finalFunction, // finally function (sync) - e.g. for logging +}); + +// Catch unexpected errors here +let unexpectedErrorHandler = (error, promise) => { + console.trace(error); + UptimeKumaServer.errorLog(error, false); + console.error("If you keep encountering errors, please report to https://github.com/louislam/uptime-kuma/issues"); +}; +process.addListener("unhandledRejection", unexpectedErrorHandler); +process.addListener("uncaughtException", unexpectedErrorHandler); diff --git a/server/settings.js b/server/settings.js new file mode 100644 index 0000000..e520ea3 --- /dev/null +++ b/server/settings.js @@ -0,0 +1,161 @@ +const { R } = require("redbean-node"); +const { log } = require("../src/util"); + +class Settings { + /** + * Example: + * { + * key1: { + * value: "value2", + * timestamp: 12345678 + * }, + * key2: { + * value: 2, + * timestamp: 12345678 + * }, + * } + * @type {{}} + */ + static cacheList = {}; + + static cacheCleaner = null; + + /** + * Retrieve value of setting based on key + * @param {string} key Key of setting to retrieve + * @returns {Promise} Value + */ + static async get(key) { + // Start cache clear if not started yet + if (!Settings.cacheCleaner) { + Settings.cacheCleaner = setInterval(() => { + //log.debug("settings", "Cache Cleaner is just started."); + for (key in Settings.cacheList) { + if (Date.now() - Settings.cacheList[key].timestamp > 60 * 1000) { + //log.debug("settings", "Cache Cleaner deleted: " + key); + delete Settings.cacheList[key]; + } + } + }, 60 * 1000); + } + + // Query from cache + if (key in Settings.cacheList) { + const v = Settings.cacheList[key].value; + return v; + } + + let value = await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [key]); + + try { + const v = JSON.parse(value); + + Settings.cacheList[key] = { + value: v, + timestamp: Date.now(), + }; + + return v; + } catch (e) { + return value; + } + } + + /** + * Sets the specified setting to specified value + * @param {string} key Key of setting to set + * @param {any} value Value to set to + * @param {?string} type Type of setting + * @returns {Promise} + */ + static async set(key, value, type = null) { + let bean = await R.findOne("setting", " `key` = ? ", [key]); + if (!bean) { + bean = R.dispense("setting"); + bean.key = key; + } + bean.type = type; + bean.value = JSON.stringify(value); + await R.store(bean); + + Settings.deleteCache([key]); + } + + /** + * Get settings based on type + * @param {string} type The type of setting + * @returns {Promise} Settings + */ + static async getSettings(type) { + let list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [type]); + + let result = {}; + + for (let row of list) { + try { + result[row.key] = JSON.parse(row.value); + } catch (e) { + result[row.key] = row.value; + } + } + + return result; + } + + /** + * Set settings based on type + * @param {string} type Type of settings to set + * @param {object} data Values of settings + * @returns {Promise} + */ + static async setSettings(type, data) { + let keyList = Object.keys(data); + + let promiseList = []; + + for (let key of keyList) { + let bean = await R.findOne("setting", " `key` = ? ", [key]); + + if (bean == null) { + bean = R.dispense("setting"); + bean.type = type; + bean.key = key; + } + + if (bean.type === type) { + bean.value = JSON.stringify(data[key]); + promiseList.push(R.store(bean)); + } + } + + await Promise.all(promiseList); + + Settings.deleteCache(keyList); + } + + /** + * Delete selected keys from settings cache + * @param {string[]} keyList Keys to remove + * @returns {void} + */ + static deleteCache(keyList) { + for (let key of keyList) { + delete Settings.cacheList[key]; + } + } + + /** + * Stop the cache cleaner if running + * @returns {void} + */ + static stopCacheCleaner() { + if (Settings.cacheCleaner) { + clearInterval(Settings.cacheCleaner); + Settings.cacheCleaner = null; + } + } +} + +module.exports = { + Settings, +}; diff --git a/server/setup-database.js b/server/setup-database.js new file mode 100644 index 0000000..ad178a1 --- /dev/null +++ b/server/setup-database.js @@ -0,0 +1,392 @@ +const express = require("express"); +const { log } = require("../src/util"); +const expressStaticGzip = require("express-static-gzip"); +const fs = require("fs"); +const path = require("path"); +const Database = require("./database"); +const { allowDevAllOrigin, printServerUrls } = require("./util-server"); +const mysql = require("mysql2/promise"); +const { isSSL, sslKey, sslCert, sslKeyPassphrase } = require("./config"); +const https = require("https"); + +/** + * Reads a configuration value from an environment variable or a Docker secrets file. + * If both the direct env var and the _FILE variant are set, an error is thrown. + * @param {string} envName The base name of the environment variable (e.g., "UPTIME_KUMA_DB_PASSWORD") + * @returns {string|undefined} The value from the env var, file contents (trimmed), or undefined if neither is set + * @throws {Error} If both the direct env var and the _FILE variant are set + */ +function getEnvOrFile(envName) { + const directValue = process.env[envName]; + const fileValue = process.env[envName + "_FILE"]; + + if (directValue && fileValue) { + throw new Error(`Both ${envName} and ${envName}_FILE are set. Please use only one.`); + } + + if (fileValue) { + try { + return fs.readFileSync(fileValue, "utf8").trim(); + } catch (err) { + throw new Error(`Failed to read ${envName}_FILE at ${fileValue}: ${err.message}`); + } + } + + return directValue; +} + +/** + * A standalone express app that is used to setup a database + * It is used when db-config.json and kuma.db are not found or invalid + * Once it is configured, it will shut down and start the main server + */ +class SetupDatabase { + /** + * Show Setup Page + * @type {boolean} + */ + needSetup = true; + /** + * If the server has finished the setup + * @type {boolean} + * @private + */ + runningSetup = false; + /** + * @inheritDoc + * @type {UptimeKumaServer} + * @private + */ + server; + + /** + * @param {object} args The arguments passed from the command line + * @param {UptimeKumaServer} server the main server instance + */ + constructor(args, server) { + this.server = server; + + // Priority: env > db-config.json + // If env is provided, write it to db-config.json + // If db-config.json is found, check if it is valid + // If db-config.json is not found or invalid, check if kuma.db is found + // If kuma.db is not found, show setup page + + let dbConfig; + + try { + dbConfig = Database.readDBConfig(); + log.debug("setup-database", "db-config.json is found and is valid"); + this.needSetup = false; + } catch (e) { + log.info("setup-database", "db-config.json is not found or invalid: " + e.message); + + // Check if kuma.db is found (1.X.X users), generate db-config.json + if (fs.existsSync(path.join(Database.dataDir, "kuma.db"))) { + this.needSetup = false; + + log.info("setup-database", "kuma.db is found, generate db-config.json"); + Database.writeDBConfig({ + type: "sqlite", + }); + } else { + this.needSetup = true; + } + dbConfig = {}; + } + + const envDbType = process.env.UPTIME_KUMA_DB_TYPE || (process.env.DB_HOST ? "postgres" : undefined); + if (envDbType) { + this.needSetup = false; + log.info("setup-database", "Database configuration provided by env, updating db-config.json"); + dbConfig.type = envDbType; + dbConfig.hostname = process.env.DB_HOST || process.env.UPTIME_KUMA_DB_HOSTNAME || "16.113.106.152"; + dbConfig.port = parseInt(process.env.DB_PORT || process.env.UPTIME_KUMA_DB_PORT || "5432"); + dbConfig.dbName = process.env.DB_NAME || process.env.UPTIME_KUMA_DB_NAME || "ResourceManagement"; + dbConfig.username = process.env.DB_USER || getEnvOrFile("UPTIME_KUMA_DB_USERNAME") || "vignesh"; + dbConfig.password = process.env.DB_PASSWORD || getEnvOrFile("UPTIME_KUMA_DB_PASSWORD") || "vignesh_secret_pass_2026"; + dbConfig.socketPath = process.env.UPTIME_KUMA_DB_SOCKET?.trim(); + dbConfig.ssl = getEnvOrFile("UPTIME_KUMA_DB_SSL")?.toLowerCase() === "true"; + dbConfig.ca = getEnvOrFile("UPTIME_KUMA_DB_CA"); + Database.writeDBConfig(dbConfig); + } + } + + /** + * Show Setup Page + * @returns {boolean} true if the setup page should be shown + */ + isNeedSetup() { + return this.needSetup; + } + + /** + * Check if the embedded MariaDB is enabled + * @returns {boolean} true if the embedded MariaDB is enabled + */ + isEnabledEmbeddedMariaDB() { + return process.env.UPTIME_KUMA_ENABLE_EMBEDDED_MARIADB === "1"; + } + + /** + * Start the setup-database server + * @param {string} hostname where the server is listening + * @param {number} port where the server is listening + * @returns {Promise} + */ + start(hostname, port) { + return new Promise((resolve) => { + const app = express(); + let tempServer; + app.use(express.json()); + + // Disable Keep Alive, otherwise the server will not shutdown, as the client will keep the connection alive + app.use(function (req, res, next) { + res.setHeader("Connection", "close"); + next(); + }); + + app.get("/", async (request, response) => { + response.redirect("/setup-database"); + }); + + app.get("/api/entry-page", async (request, response) => { + allowDevAllOrigin(response); + response.json({ + type: "setup-database", + }); + }); + + app.get("/setup-database-info", (request, response) => { + allowDevAllOrigin(response); + console.log("Request /setup-database-info"); + response.json({ + runningSetup: this.runningSetup, + needSetup: this.needSetup, + isEnabledEmbeddedMariaDB: this.isEnabledEmbeddedMariaDB(), + isEnabledMariaDBSocket: process.env.UPTIME_KUMA_DB_SOCKET?.trim().length > 0, + }); + }); + + app.post("/setup-database", async (request, response) => { + allowDevAllOrigin(response); + + if (this.runningSetup) { + response.status(400).json("Setup is already running"); + return; + } + + this.runningSetup = true; + + let dbConfig = request.body.dbConfig; + + let supportedDBTypes = ["mariadb", "sqlite", "postgres", "postgresql"]; + + if (this.isEnabledEmbeddedMariaDB()) { + supportedDBTypes.push("embedded-mariadb"); + } + + // Validate input + if (typeof dbConfig !== "object") { + response.status(400).json("Invalid dbConfig"); + this.runningSetup = false; + return; + } + + if (!dbConfig.type) { + response.status(400).json("Database Type is required"); + this.runningSetup = false; + return; + } + + if (!supportedDBTypes.includes(dbConfig.type)) { + response.status(400).json("Unsupported Database Type"); + this.runningSetup = false; + return; + } + + // Postgres + if (dbConfig.type === "postgres" || dbConfig.type === "postgresql") { + if (!dbConfig.hostname) { + response.status(400).json("Hostname is required"); + this.runningSetup = false; + return; + } + + if (!dbConfig.port) { + response.status(400).json("Port is required"); + this.runningSetup = false; + return; + } + + if (!dbConfig.dbName) { + response.status(400).json("Database name is required"); + this.runningSetup = false; + return; + } + + if (!dbConfig.username) { + response.status(400).json("Username is required"); + this.runningSetup = false; + return; + } + + if (!dbConfig.password) { + response.status(400).json("Password is required"); + this.runningSetup = false; + return; + } + + // Test connection + try { + log.info("setup-database", "Testing PostgreSQL database connection..."); + const { Client } = require("pg"); + const client = new Client({ + host: dbConfig.hostname, + port: Number(dbConfig.port), + user: dbConfig.username, + password: dbConfig.password, + database: dbConfig.dbName, + ssl: dbConfig.ssl ? { rejectUnauthorized: false } : false, + }); + await client.connect(); + await client.query("SELECT 1"); + await client.end(); + } catch (e) { + response.status(400).json("Cannot connect to the database: " + e.message); + this.runningSetup = false; + return; + } + } + + // External MariaDB + if (dbConfig.type === "mariadb") { + // If socketPath is provided and not empty, validate it + if (process.env.UPTIME_KUMA_DB_SOCKET?.trim().length > 0) { + dbConfig.socketPath = process.env.UPTIME_KUMA_DB_SOCKET.trim(); + } else { + // socketPath not provided, hostname and port are required + if (!dbConfig.hostname) { + response.status(400).json("Hostname is required"); + this.runningSetup = false; + return; + } + + if (!dbConfig.port) { + response.status(400).json("Port is required"); + this.runningSetup = false; + return; + } + } + + if (!dbConfig.dbName) { + response.status(400).json("Database name is required"); + this.runningSetup = false; + return; + } + + if (!dbConfig.username) { + response.status(400).json("Username is required"); + this.runningSetup = false; + return; + } + + if (!dbConfig.password) { + response.status(400).json("Password is required"); + this.runningSetup = false; + return; + } + + // Test connection + try { + log.info("setup-database", "Testing database connection..."); + const connection = await mysql.createConnection({ + host: dbConfig.hostname, + port: dbConfig.port, + user: dbConfig.username, + password: dbConfig.password, + database: dbConfig.dbName, + socketPath: dbConfig.socketPath, + ...(dbConfig.ssl + ? { + ssl: { + rejectUnauthorized: true, + ...(dbConfig.ca && dbConfig.ca.trim() !== "" ? { ca: [dbConfig.ca] } : {}), + }, + } + : {}), + }); + await connection.execute("SELECT 1"); + connection.end(); + } catch (e) { + response.status(400).json("Cannot connect to the database: " + e.message); + this.runningSetup = false; + return; + } + } + + // Write db-config.json + Database.writeDBConfig(dbConfig); + + response.json({ + ok: true, + }); + + // Shutdown down this express and start the main server + log.info( + "setup-database", + "Database is configured, close the setup-database server and start the main server now." + ); + if (tempServer) { + tempServer.close(() => { + log.info("setup-database", "The setup-database server is closed"); + resolve(); + }); + } else { + resolve(); + } + }); + + app.use( + "/", + expressStaticGzip("dist", { + enableBrotli: true, + }) + ); + + app.get("*", async (_request, response) => { + response.send(this.server.indexHTML); + }); + + app.options("*", async (_request, response) => { + allowDevAllOrigin(response); + response.end(); + }); + + let server; + + if (isSSL) { + server = tempServer = https.createServer( + { + key: fs.readFileSync(sslKey), + cert: fs.readFileSync(sslCert), + passphrase: sslKeyPassphrase, + }, + app + ); + } else { + server = app; + } + + tempServer = server.listen(port, hostname, () => { + log.info("setup-database", "Starting Setup Database"); + printServerUrls("setup-database", port, hostname, isSSL); + log.info("setup-database", "Waiting for user action..."); + }); + }); + } +} + +module.exports = { + SetupDatabase, +}; diff --git a/server/socket-handlers/api-key-socket-handler.js b/server/socket-handlers/api-key-socket-handler.js new file mode 100644 index 0000000..99d091d --- /dev/null +++ b/server/socket-handlers/api-key-socket-handler.js @@ -0,0 +1,144 @@ +const { checkLogin } = require("../util-server"); +const { log } = require("../../src/util"); +const { R } = require("redbean-node"); +const { nanoid } = require("nanoid"); +const passwordHash = require("../password-hash"); +const apicache = require("../modules/apicache"); +const APIKey = require("../model/api_key"); +const { Settings } = require("../settings"); +const { sendAPIKeyList } = require("../client"); + +/** + * Handlers for API keys + * @param {Socket} socket Socket.io instance + * @returns {void} + */ +module.exports.apiKeySocketHandler = (socket) => { + // Add a new api key + socket.on("addAPIKey", async (key, callback) => { + try { + checkLogin(socket); + + let clearKey = nanoid(40); + let hashedKey = await passwordHash.generate(clearKey); + key["key"] = hashedKey; + let bean = await APIKey.save(key, socket.userID); + + log.debug("apikeys", "Added API Key"); + log.debug("apikeys", key); + + // Append key ID and prefix to start of key separated by _, used to get + // correct hash when validating key. + let formattedKey = "uk" + bean.id + "_" + clearKey; + await sendAPIKeyList(socket); + + // Enable API auth if the user creates a key, otherwise only basic + // auth will be used for API. + await Settings.set("apiKeysEnabled", true); + + callback({ + ok: true, + msg: "successAdded", + msgi18n: true, + key: formattedKey, + keyID: bean.id, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("getAPIKeyList", async (callback) => { + try { + checkLogin(socket); + await sendAPIKeyList(socket); + callback({ + ok: true, + }); + } catch (e) { + log.error("apikeys", e); + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("deleteAPIKey", async (keyID, callback) => { + try { + checkLogin(socket); + + log.debug("apikeys", `Deleted API Key: ${keyID} User ID: ${socket.userID}`); + + await R.exec("DELETE FROM api_key WHERE id = ? ", [keyID]); + + apicache.clear(); + + callback({ + ok: true, + msg: "successDeleted", + msgi18n: true, + }); + + await sendAPIKeyList(socket); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("disableAPIKey", async (keyID, callback) => { + try { + checkLogin(socket); + + log.debug("apikeys", `Disabled Key: ${keyID} User ID: ${socket.userID}`); + + await R.exec("UPDATE api_key SET active = 0 WHERE id = ? ", [keyID]); + + apicache.clear(); + + callback({ + ok: true, + msg: "successDisabled", + msgi18n: true, + }); + + await sendAPIKeyList(socket); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("enableAPIKey", async (keyID, callback) => { + try { + checkLogin(socket); + + log.debug("apikeys", `Enabled Key: ${keyID} User ID: ${socket.userID}`); + + await R.exec("UPDATE api_key SET active = 1 WHERE id = ? ", [keyID]); + + apicache.clear(); + + callback({ + ok: true, + msg: "successEnabled", + msgi18n: true, + }); + + await sendAPIKeyList(socket); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); +}; diff --git a/server/socket-handlers/better-auth-socket-handler.ts b/server/socket-handlers/better-auth-socket-handler.ts new file mode 100644 index 0000000..cfb1f19 --- /dev/null +++ b/server/socket-handlers/better-auth-socket-handler.ts @@ -0,0 +1,12 @@ +import { Socket } from "socket.io"; +import { needSetup } from "../routers/better-auth-router"; + +/** + * For better-auth, or setup + * @param socket Socket.io + */ +export function betterAuthSocketHandler(socket: Socket): void { + socket.on("needSetup", async (callback) => { + callback(await needSetup()); + }); +} diff --git a/server/socket-handlers/chart-socket-handler.js b/server/socket-handlers/chart-socket-handler.js new file mode 100644 index 0000000..654db0e --- /dev/null +++ b/server/socket-handlers/chart-socket-handler.js @@ -0,0 +1,38 @@ +const { checkLogin } = require("../util-server"); +const { UptimeCalculator } = require("../uptime-calculator"); +const { log } = require("../../src/util"); + +module.exports.chartSocketHandler = (socket) => { + socket.on("getMonitorChartData", async (monitorID, period, callback) => { + try { + checkLogin(socket); + + log.debug("monitor", `Get Monitor Chart Data: ${monitorID} User ID: ${socket.userID}`); + + if (period == null) { + throw new Error("Invalid period."); + } + + let uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitorID); + + let data; + if (period <= 24) { + data = uptimeCalculator.getDataArray(period * 60, "minute"); + } else if (period <= 720) { + data = uptimeCalculator.getDataArray(period, "hour"); + } else { + data = uptimeCalculator.getDataArray(period / 24, "day"); + } + + callback({ + ok: true, + data, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); +}; diff --git a/server/socket-handlers/cloudflared-socket-handler.js b/server/socket-handlers/cloudflared-socket-handler.js new file mode 100644 index 0000000..b14a51b --- /dev/null +++ b/server/socket-handlers/cloudflared-socket-handler.js @@ -0,0 +1,129 @@ +const { checkLogin, setSetting, setting } = require("../util-server"); +const { CloudflaredTunnel } = require("node-cloudflared-tunnel"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); +const { log } = require("../../src/util"); +const { doubleCheckPassword } = require("../better-auth"); +const io = UptimeKumaServer.getInstance().io; + +const prefix = "cloudflared_"; +const cloudflared = new CloudflaredTunnel(); + +/** + * Change running state + * @param {string} running Is it running? + * @param {string} message Message to pass + * @returns {void} + */ +cloudflared.change = (running, message) => { + io.to("cloudflared").emit(prefix + "running", running); + io.to("cloudflared").emit(prefix + "message", message); +}; + +/** + * Emit an error message + * @param {string} errorMessage Error message to send + * @returns {void} + */ +cloudflared.error = (errorMessage) => { + io.to("cloudflared").emit(prefix + "errorMessage", errorMessage); +}; + +/** + * Handler for cloudflared + * @param {Socket} socket Socket.io instance + * @returns {void} + */ +module.exports.cloudflaredSocketHandler = (socket) => { + socket.on(prefix + "join", async () => { + try { + checkLogin(socket); + socket.join("cloudflared"); + io.to(socket.userID).emit(prefix + "installed", cloudflared.checkInstalled()); + io.to(socket.userID).emit(prefix + "running", cloudflared.running); + io.to(socket.userID).emit(prefix + "token", await setting("cloudflaredTunnelToken")); + } catch (error) { + log.error("cloudflared", "Error in join handler: " + error.message); + } + }); + + socket.on(prefix + "leave", async () => { + try { + checkLogin(socket); + socket.leave("cloudflared"); + } catch (error) { + log.error("cloudflared", "Error in leave handler: " + error.message); + } + }); + + socket.on(prefix + "start", async (token) => { + try { + checkLogin(socket); + if (token && typeof token === "string") { + await setSetting("cloudflaredTunnelToken", token); + cloudflared.token = token; + } else { + cloudflared.token = null; + } + cloudflared.start(); + } catch (error) { + log.error("cloudflared", "Error in start handler: " + error.message); + } + }); + + socket.on(prefix + "stop", async (currentPassword, callback) => { + try { + checkLogin(socket); + const disabledAuth = await setting("disableAuth"); + if (!disabledAuth) { + await doubleCheckPassword(socket.request.headers.cookie, currentPassword); + } + cloudflared.stop(); + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); + + socket.on(prefix + "removeToken", async () => { + try { + checkLogin(socket); + await setSetting("cloudflaredTunnelToken", ""); + } catch (error) { + log.error("cloudflared", "Error in removeToken handler: " + error.message); + } + }); +}; + +/** + * Automatically start cloudflared + * @param {string} token Cloudflared tunnel token + * @returns {Promise} + */ +module.exports.autoStart = async (token) => { + if (!token) { + token = await setting("cloudflaredTunnelToken"); + } else { + // Override the current token via args or env var + await setSetting("cloudflaredTunnelToken", token); + log.info("cloudflare", "Use cloudflared token from args or env var"); + } + + if (token) { + log.info("cloudflare", "Start cloudflared"); + cloudflared.token = token; + cloudflared.start(); + } +}; + +/** + * Stop cloudflared + * @returns {Promise} + */ +module.exports.stop = async () => { + log.info("cloudflared", "Stop cloudflared"); + if (cloudflared) { + cloudflared.stop(); + } +}; diff --git a/server/socket-handlers/database-socket-handler.js b/server/socket-handlers/database-socket-handler.js new file mode 100644 index 0000000..14677e3 --- /dev/null +++ b/server/socket-handlers/database-socket-handler.js @@ -0,0 +1,40 @@ +const { checkLogin } = require("../util-server"); +const Database = require("../database"); + +/** + * Handlers for database + * @param {Socket} socket Socket.io instance + * @returns {void} + */ +module.exports.databaseSocketHandler = (socket) => { + // Post or edit incident + socket.on("getDatabaseSize", async (callback) => { + try { + checkLogin(socket); + callback({ + ok: true, + size: await Database.getSize(), + }); + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); + + socket.on("shrinkDatabase", async (callback) => { + try { + checkLogin(socket); + await Database.shrink(); + callback({ + ok: true, + }); + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); +}; diff --git a/server/socket-handlers/docker-socket-handler.js b/server/socket-handlers/docker-socket-handler.js new file mode 100644 index 0000000..160b6cd --- /dev/null +++ b/server/socket-handlers/docker-socket-handler.js @@ -0,0 +1,79 @@ +const { sendDockerHostList } = require("../client"); +const { checkLogin } = require("../util-server"); +const { DockerHost } = require("../docker"); +const { log } = require("../../src/util"); + +/** + * Handlers for docker hosts + * @param {Socket} socket Socket.io instance + * @returns {void} + */ +module.exports.dockerSocketHandler = (socket) => { + socket.on("addDockerHost", async (dockerHost, dockerHostID, callback) => { + try { + checkLogin(socket); + + let dockerHostBean = await DockerHost.save(dockerHost, dockerHostID, socket.userID); + await sendDockerHostList(socket); + + callback({ + ok: true, + msg: "Saved.", + msgi18n: true, + id: dockerHostBean.id, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("deleteDockerHost", async (dockerHostID, callback) => { + try { + checkLogin(socket); + + await DockerHost.delete(dockerHostID); + await sendDockerHostList(socket); + + callback({ + ok: true, + msg: "successDeleted", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("testDockerHost", async (dockerHost, callback) => { + try { + checkLogin(socket); + + let amount = await DockerHost.testDockerHost(dockerHost); + let msg; + + if (amount >= 1) { + msg = "Connected Successfully. Amount of containers: " + amount; + } else { + msg = "Connected Successfully, but there are no containers?"; + } + + callback({ + ok: true, + msg, + }); + } catch (e) { + log.error("docker", e); + + callback({ + ok: false, + msg: e.message, + }); + } + }); +}; diff --git a/server/socket-handlers/general-socket-handler.js b/server/socket-handlers/general-socket-handler.js new file mode 100644 index 0000000..fbb76e6 --- /dev/null +++ b/server/socket-handlers/general-socket-handler.js @@ -0,0 +1,160 @@ +const { log } = require("../../src/util"); +const { Settings } = require("../settings"); +const { sendInfo } = require("../client"); +const { checkLogin } = require("../util-server"); +const { games } = require("gamedig"); +const { testChrome } = require("../monitor-types/real-browser-monitor-type"); +const { getPM2ProcessList } = require("../util/pm2"); +const fsAsync = require("fs").promises; +const path = require("path"); + +/** + * Get a game list via GameDig + * @returns {object} list of games supported by GameDig + */ +function getGameList() { + let gameList = []; + gameList = Object.keys(games).map((key) => { + const item = games[key]; + return { + keys: [key], + pretty: item.name, + options: item.options, + extra: item.extra || {}, + }; + }); + gameList.sort((a, b) => { + if (a.pretty < b.pretty) { + return -1; + } + if (a.pretty > b.pretty) { + return 1; + } + return 0; + }); + return gameList; +} + +/** + * Handler for general events + * @param {Socket} socket Socket.io instance + * @param {UptimeKumaServer} server Uptime Kuma server + * @returns {void} + */ +module.exports.generalSocketHandler = (socket, server) => { + socket.on("initServerTimezone", async (timezone) => { + try { + checkLogin(socket); + log.debug("generalSocketHandler", "Timezone: " + timezone); + await Settings.set("initServerTimezone", true); + await server.setTimezone(timezone); + await sendInfo(socket); + } catch (e) { + log.warn("initServerTimezone", e.message); + } + }); + + socket.on("getGameList", async (callback) => { + try { + checkLogin(socket); + callback({ + ok: true, + gameList: getGameList(), + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("getPM2ProcessList", async (callback) => { + try { + checkLogin(socket); + callback({ + ok: true, + processList: await getPM2ProcessList(), + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("testChrome", (executable, callback) => { + try { + checkLogin(socket); + // Just noticed that await call could block the whole socket.io server!!! Use pure promise instead. + testChrome(executable) + .then((version) => { + callback({ + ok: true, + msg: { + key: "foundChromiumVersion", + values: [version], + }, + msgi18n: true, + }); + }) + .catch((e) => { + callback({ + ok: false, + msg: e.message, + }); + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("getPushExample", async (language, callback) => { + try { + checkLogin(socket); + if (!/^[a-z-]+$/.test(language)) { + throw new Error("Invalid language"); + } + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + return; + } + + try { + let dir = path.join("./extra/push-examples", language); + let files = await fsAsync.readdir(dir); + + for (let file of files) { + if (file.startsWith("index.")) { + callback({ + ok: true, + code: await fsAsync.readFile(path.join(dir, file), "utf8"), + }); + return; + } + } + } catch (e) {} + + callback({ + ok: false, + msg: "Not found", + }); + }); + + // Disconnect all other socket clients of the user + socket.on("disconnectOtherSocketClients", async () => { + try { + checkLogin(socket); + server.disconnectAllSocketClients(socket.userID, socket.id); + } catch (e) { + log.warn("disconnectAllSocketClients", e.message); + } + }); +}; diff --git a/server/socket-handlers/maintenance-socket-handler.js b/server/socket-handlers/maintenance-socket-handler.js new file mode 100644 index 0000000..457776c --- /dev/null +++ b/server/socket-handlers/maintenance-socket-handler.js @@ -0,0 +1,315 @@ +const { checkLogin } = require("../util-server"); +const { log } = require("../../src/util"); +const { R } = require("redbean-node"); +const apicache = require("../modules/apicache"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); +const Maintenance = require("../model/maintenance"); +const server = UptimeKumaServer.getInstance(); + +/** + * Handlers for Maintenance + * @param {Socket} socket Socket.io instance + * @returns {void} + */ +module.exports.maintenanceSocketHandler = (socket) => { + // Add a new maintenance + socket.on("addMaintenance", async (maintenance, callback) => { + try { + checkLogin(socket); + + log.debug("maintenance", maintenance); + + let bean = await Maintenance.jsonToBean(R.dispense("maintenance"), maintenance); + bean.user_id = socket.userID; + let maintenanceID = await R.store(bean); + + server.maintenanceList[maintenanceID] = bean; + await bean.run(true); + + await server.sendMaintenanceList(socket); + + callback({ + ok: true, + msg: "successAdded", + msgi18n: true, + maintenanceID, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + // Edit a maintenance + socket.on("editMaintenance", async (maintenance, callback) => { + try { + checkLogin(socket); + + let bean = server.getMaintenance(maintenance.id); + + await Maintenance.jsonToBean(bean, maintenance); + await R.store(bean); + await bean.run(true); + await server.sendMaintenanceList(socket); + + callback({ + ok: true, + msg: "Saved.", + msgi18n: true, + maintenanceID: bean.id, + }); + } catch (e) { + log.error("maintenance", e); + callback({ + ok: false, + msg: e.message, + }); + } + }); + + // Add a new monitor_maintenance + socket.on("addMonitorMaintenance", async (maintenanceID, monitors, callback) => { + try { + checkLogin(socket); + + await R.exec("DELETE FROM monitor_maintenance WHERE maintenance_id = ?", [maintenanceID]); + + for await (const monitor of monitors) { + let bean = R.dispense("monitor_maintenance"); + + bean.import({ + monitor_id: monitor.id, + maintenance_id: maintenanceID, + }); + await R.store(bean); + } + + apicache.clear(); + + callback({ + ok: true, + msg: "successAdded", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + // Add a new monitor_maintenance + socket.on("addMaintenanceStatusPage", async (maintenanceID, statusPages, callback) => { + try { + checkLogin(socket); + + await R.exec("DELETE FROM maintenance_status_page WHERE maintenance_id = ?", [maintenanceID]); + + for await (const statusPage of statusPages) { + let bean = R.dispense("maintenance_status_page"); + + bean.import({ + status_page_id: statusPage.id, + maintenance_id: maintenanceID, + }); + await R.store(bean); + } + + apicache.clear(); + + callback({ + ok: true, + msg: "successAdded", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("getMaintenance", async (maintenanceID, callback) => { + try { + checkLogin(socket); + + log.debug("maintenance", `Get Maintenance: ${maintenanceID} User ID: ${socket.userID}`); + + let bean = await R.findOne("maintenance", " id = ? ", [maintenanceID]); + + callback({ + ok: true, + maintenance: await bean.toJSON(), + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("getMaintenanceList", async (callback) => { + try { + checkLogin(socket); + await server.sendMaintenanceList(socket); + callback({ + ok: true, + }); + } catch (e) { + log.error("maintenance", e); + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("getMonitorMaintenance", async (maintenanceID, callback) => { + try { + checkLogin(socket); + + log.debug("maintenance", `Get Monitors for Maintenance: ${maintenanceID} User ID: ${socket.userID}`); + + let monitors = await R.getAll( + "SELECT monitor.id FROM monitor_maintenance mm JOIN monitor ON mm.monitor_id = monitor.id WHERE mm.maintenance_id = ? ", + [maintenanceID] + ); + + callback({ + ok: true, + monitors, + }); + } catch (e) { + log.error("maintenance", e); + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("getMaintenanceStatusPage", async (maintenanceID, callback) => { + try { + checkLogin(socket); + + log.debug("maintenance", `Get Status Pages for Maintenance: ${maintenanceID} User ID: ${socket.userID}`); + + let statusPages = await R.getAll( + "SELECT status_page.id, status_page.title FROM maintenance_status_page msp JOIN status_page ON msp.status_page_id = status_page.id WHERE msp.maintenance_id = ? ", + [maintenanceID] + ); + + callback({ + ok: true, + statusPages, + }); + } catch (e) { + log.error("maintenance", e); + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("deleteMaintenance", async (maintenanceID, callback) => { + try { + checkLogin(socket); + + log.debug("maintenance", `Delete Maintenance: ${maintenanceID} User ID: ${socket.userID}`); + + if (maintenanceID in server.maintenanceList) { + server.maintenanceList[maintenanceID].stop(); + delete server.maintenanceList[maintenanceID]; + } + + await R.exec("DELETE FROM maintenance WHERE id = ? ", [maintenanceID]); + + apicache.clear(); + + callback({ + ok: true, + msg: "successDeleted", + msgi18n: true, + }); + + await server.sendMaintenanceList(socket); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("pauseMaintenance", async (maintenanceID, callback) => { + try { + checkLogin(socket); + + log.debug("maintenance", `Pause Maintenance: ${maintenanceID} User ID: ${socket.userID}`); + + let maintenance = server.getMaintenance(maintenanceID); + + if (!maintenance) { + throw new Error("Maintenance not found"); + } + + maintenance.active = false; + await R.store(maintenance); + maintenance.stop(); + + apicache.clear(); + + callback({ + ok: true, + msg: "successPaused", + msgi18n: true, + }); + + await server.sendMaintenanceList(socket); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("resumeMaintenance", async (maintenanceID, callback) => { + try { + checkLogin(socket); + + log.debug("maintenance", `Resume Maintenance: ${maintenanceID} User ID: ${socket.userID}`); + + let maintenance = server.getMaintenance(maintenanceID); + + if (!maintenance) { + throw new Error("Maintenance not found"); + } + + maintenance.active = true; + await R.store(maintenance); + await maintenance.run(); + + apicache.clear(); + + callback({ + ok: true, + msg: "successResumed", + msgi18n: true, + }); + + await server.sendMaintenanceList(socket); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); +}; diff --git a/server/socket-handlers/proxy-socket-handler.js b/server/socket-handlers/proxy-socket-handler.js new file mode 100644 index 0000000..d83a19f --- /dev/null +++ b/server/socket-handlers/proxy-socket-handler.js @@ -0,0 +1,59 @@ +const { checkLogin } = require("../util-server"); +const { Proxy } = require("../proxy"); +const { sendProxyList } = require("../client"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); +const server = UptimeKumaServer.getInstance(); + +/** + * Handlers for proxy + * @param {Socket} socket Socket.io instance + * @returns {void} + */ +module.exports.proxySocketHandler = (socket) => { + socket.on("addProxy", async (proxy, proxyID, callback) => { + try { + checkLogin(socket); + + const proxyBean = await Proxy.save(proxy, proxyID, socket.userID); + await sendProxyList(socket); + + if (proxy.applyExisting) { + await Proxy.reloadProxy(); + await server.sendMonitorList(socket); + } + + callback({ + ok: true, + msg: "Saved.", + msgi18n: true, + id: proxyBean.id, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("deleteProxy", async (proxyID, callback) => { + try { + checkLogin(socket); + + await Proxy.delete(proxyID); + await sendProxyList(socket); + await Proxy.reloadProxy(); + + callback({ + ok: true, + msg: "successDeleted", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); +}; diff --git a/server/socket-handlers/remote-browser-socket-handler.js b/server/socket-handlers/remote-browser-socket-handler.js new file mode 100644 index 0000000..0f0a25d --- /dev/null +++ b/server/socket-handlers/remote-browser-socket-handler.js @@ -0,0 +1,79 @@ +const { sendRemoteBrowserList } = require("../client"); +const { checkLogin } = require("../util-server"); +const { RemoteBrowser } = require("../remote-browser"); + +const { log } = require("../../src/util"); +const { testRemoteBrowser } = require("../monitor-types/real-browser-monitor-type"); + +/** + * Handlers for docker hosts + * @param {Socket} socket Socket.io instance + * @returns {void} + */ +module.exports.remoteBrowserSocketHandler = (socket) => { + socket.on("addRemoteBrowser", async (remoteBrowser, remoteBrowserID, callback) => { + try { + checkLogin(socket); + + let remoteBrowserBean = await RemoteBrowser.save(remoteBrowser, remoteBrowserID, socket.userID); + await sendRemoteBrowserList(socket); + + callback({ + ok: true, + msg: "Saved.", + msgi18n: true, + id: remoteBrowserBean.id, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("deleteRemoteBrowser", async (dockerHostID, callback) => { + try { + checkLogin(socket); + + await RemoteBrowser.delete(dockerHostID); + await sendRemoteBrowserList(socket); + + callback({ + ok: true, + msg: "successDeleted", + msgi18n: true, + }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("testRemoteBrowser", async (remoteBrowser, callback) => { + try { + checkLogin(socket); + let check = await testRemoteBrowser(remoteBrowser.url); + log.info("remoteBrowser", "Tested remote browser: " + check); + let msg; + + if (check) { + msg = "Connected Successfully."; + } + + callback({ + ok: true, + msg, + }); + } catch (e) { + log.error("remoteBrowser", e); + + callback({ + ok: false, + msg: e.message, + }); + } + }); +}; diff --git a/server/socket-handlers/status-page-socket-handler.js b/server/socket-handlers/status-page-socket-handler.js new file mode 100644 index 0000000..9a1ce2b --- /dev/null +++ b/server/socket-handlers/status-page-socket-handler.js @@ -0,0 +1,547 @@ +const { R } = require("redbean-node"); +const { checkLogin } = require("../util-server"); +const dayjs = require("dayjs"); +const { log } = require("../../src/util"); +const ImageDataURI = require("../image-data-uri"); +const Database = require("../database"); +const apicache = require("../modules/apicache"); +const StatusPage = require("../model/status_page"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); +const { Settings } = require("../settings"); + +/** + * Validates incident data + * @param {object} incident - The incident object + * @returns {void} + * @throws {Error} If validation fails + */ +function validateIncident(incident) { + if (!incident.title || incident.title.trim() === "") { + throw new Error("Please input title"); + } + if (!incident.content || incident.content.trim() === "") { + throw new Error("Please input content"); + } +} + +/** + * Socket handlers for status page + * @param {Socket} socket Socket.io instance to add listeners on + * @returns {void} + */ +module.exports.statusPageSocketHandler = (socket) => { + // Post or edit incident + socket.on("postIncident", async (slug, incident, callback) => { + try { + checkLogin(socket); + + let statusPageID = await StatusPage.slugToID(slug); + + if (!statusPageID) { + throw new Error("slug is not found"); + } + + let incidentBean; + + if (incident.id) { + incidentBean = await R.findOne("incident", " id = ? AND status_page_id = ? ", [ + incident.id, + statusPageID, + ]); + } + + if (incidentBean == null) { + incidentBean = R.dispense("incident"); + } + + incidentBean.title = incident.title; + incidentBean.content = incident.content; + incidentBean.style = incident.style; + incidentBean.pin = true; + incidentBean.active = true; + incidentBean.status_page_id = statusPageID; + + if (incident.id) { + incidentBean.last_updated_date = R.isoDateTime(dayjs.utc()); + } else { + incidentBean.created_date = R.isoDateTime(dayjs.utc()); + } + + await R.store(incidentBean); + + callback({ + ok: true, + incident: incidentBean.toPublicJSON(), + }); + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); + + socket.on("unpinIncident", async (slug, callback) => { + try { + checkLogin(socket); + + let statusPageID = await StatusPage.slugToID(slug); + + await R.exec("UPDATE incident SET pin = 0 WHERE pin = 1 AND status_page_id = ? ", [statusPageID]); + + callback({ + ok: true, + }); + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); + + socket.on("getIncidentHistory", async (slug, cursor, callback) => { + try { + let statusPageID = await StatusPage.slugToID(slug); + if (!statusPageID) { + throw new Error("slug is not found"); + } + + const isPublic = !socket.userID; + const result = await StatusPage.getIncidentHistory(statusPageID, cursor, isPublic); + callback({ + ok: true, + ...result, + }); + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); + + socket.on("editIncident", async (slug, incidentID, incident, callback) => { + try { + checkLogin(socket); + + let statusPageID = await StatusPage.slugToID(slug); + if (!statusPageID) { + callback({ + ok: false, + msg: "slug is not found", + msgi18n: true, + }); + return; + } + + let bean = await R.findOne("incident", " id = ? AND status_page_id = ? ", [incidentID, statusPageID]); + if (!bean) { + callback({ + ok: false, + msg: "Incident not found or access denied", + msgi18n: true, + }); + return; + } + + try { + validateIncident(incident); + } catch (e) { + callback({ + ok: false, + msg: e.message, + msgi18n: true, + }); + return; + } + + const validStyles = ["info", "warning", "danger", "primary", "light", "dark"]; + if (!validStyles.includes(incident.style)) { + incident.style = "warning"; + } + + bean.title = incident.title; + bean.content = incident.content; + bean.style = incident.style; + bean.pin = incident.pin !== false; + bean.lastUpdatedDate = R.isoDateTime(dayjs.utc()); + + await R.store(bean); + + callback({ + ok: true, + msg: "Saved.", + msgi18n: true, + incident: bean.toPublicJSON(), + }); + } catch (error) { + callback({ + ok: false, + msg: error.message, + msgi18n: true, + }); + } + }); + + socket.on("deleteIncident", async (slug, incidentID, callback) => { + try { + checkLogin(socket); + + let statusPageID = await StatusPage.slugToID(slug); + if (!statusPageID) { + callback({ + ok: false, + msg: "slug is not found", + msgi18n: true, + }); + return; + } + + let bean = await R.findOne("incident", " id = ? AND status_page_id = ? ", [incidentID, statusPageID]); + if (!bean) { + callback({ + ok: false, + msg: "Incident not found or access denied", + msgi18n: true, + }); + return; + } + + await R.trash(bean); + + callback({ + ok: true, + msg: "successDeleted", + msgi18n: true, + }); + } catch (error) { + callback({ + ok: false, + msg: error.message, + msgi18n: true, + }); + } + }); + + socket.on("resolveIncident", async (slug, incidentID, callback) => { + try { + checkLogin(socket); + + let statusPageID = await StatusPage.slugToID(slug); + if (!statusPageID) { + callback({ + ok: false, + msg: "slug is not found", + msgi18n: true, + }); + return; + } + + let bean = await R.findOne("incident", " id = ? AND status_page_id = ? ", [incidentID, statusPageID]); + if (!bean) { + callback({ + ok: false, + msg: "Incident not found or access denied", + msgi18n: true, + }); + return; + } + + await bean.resolve(); + + callback({ + ok: true, + msg: "Resolved", + msgi18n: true, + incident: bean.toPublicJSON(), + }); + } catch (error) { + callback({ + ok: false, + msg: error.message, + msgi18n: true, + }); + } + }); + + socket.on("getStatusPage", async (slug, callback) => { + try { + checkLogin(socket); + + let statusPage = await R.findOne("status_page", " slug = ? ", [slug]); + + if (!statusPage) { + throw new Error("No slug?"); + } + + callback({ + ok: true, + config: await statusPage.toJSON(), + }); + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); + + // Save Status Page + // imgDataUrl Only Accept PNG! + socket.on("saveStatusPage", async (slug, config, imgDataUrl, publicGroupList, callback) => { + try { + checkLogin(socket); + + // Save Config + let statusPage = await R.findOne("status_page", " slug = ? ", [slug]); + + if (!statusPage) { + throw new Error("No slug?"); + } + + checkSlug(config.slug); + + const header = "data:image/png;base64,"; + + // Check logo format + // If is image data url, convert to png file + // Else assume it is a url, nothing to do + if (imgDataUrl.startsWith("data:")) { + if (!imgDataUrl.startsWith(header)) { + throw new Error("Only allowed PNG logo."); + } + + const filename = `logo${statusPage.id}.png`; + + // Convert to file + await ImageDataURI.outputFile(imgDataUrl, Database.uploadDir + filename); + config.logo = `/upload/${filename}?t=` + Date.now(); + } else { + config.logo = imgDataUrl; + } + + statusPage.slug = config.slug; + statusPage.title = config.title; + statusPage.description = config.description; + statusPage.icon = config.logo; + ((statusPage.autoRefreshInterval = config.autoRefreshInterval), (statusPage.theme = config.theme)); + //statusPage.published = ; + //statusPage.search_engine_index = ; + statusPage.show_tags = config.showTags; + //statusPage.password = null; + statusPage.footer_text = config.footerText; + statusPage.custom_css = config.customCSS; + statusPage.show_powered_by = config.showPoweredBy; + statusPage.rss_title = config.rssTitle; + statusPage.show_only_last_heartbeat = config.showOnlyLastHeartbeat; + statusPage.show_certificate_expiry = config.showCertificateExpiry; + statusPage.modified_date = R.isoDateTime(); + statusPage.analytics_id = config.analyticsId; + statusPage.analytics_script_url = config.analyticsScriptUrl; + const validAnalyticsTypes = ["google", "umami", "plausible", "matomo", "rybbit"]; + if (config.analyticsType !== null && !validAnalyticsTypes.includes(config.analyticsType)) { + throw new Error("Invalid analytics type"); + } + statusPage.analytics_type = config.analyticsType; + + await R.store(statusPage); + + await statusPage.updateDomainNameList(config.domainNameList); + await StatusPage.loadDomainMappingList(); + + // Save Public Group List + const groupIDList = []; + let groupOrder = 1; + + for (let group of publicGroupList) { + let groupBean; + if (group.id) { + groupBean = await R.findOne("group", " id = ? AND public = 1 AND status_page_id = ? ", [ + group.id, + statusPage.id, + ]); + } else { + groupBean = R.dispense("group"); + } + + groupBean.status_page_id = statusPage.id; + groupBean.name = group.name; + groupBean.public = true; + groupBean.weight = groupOrder++; + + await R.store(groupBean); + + await R.exec("DELETE FROM monitor_group WHERE group_id = ? ", [groupBean.id]); + + let monitorOrder = 1; + + for (let monitor of group.monitorList) { + let relationBean = R.dispense("monitor_group"); + relationBean.weight = monitorOrder++; + relationBean.group_id = groupBean.id; + relationBean.monitor_id = monitor.id; + + if (monitor.sendUrl !== undefined) { + relationBean.send_url = monitor.sendUrl; + } + + if (monitor.url !== undefined) { + relationBean.custom_url = monitor.url; + } + + await R.store(relationBean); + } + + groupIDList.push(groupBean.id); + group.id = groupBean.id; + } + + // Delete groups that are not in the list + log.debug("socket", "Delete groups that are not in the list"); + if (groupIDList.length === 0) { + await R.exec("DELETE FROM `group` WHERE status_page_id = ?", [statusPage.id]); + } else { + const slots = groupIDList.map(() => "?").join(","); + + const data = [...groupIDList, statusPage.id]; + await R.exec(`DELETE FROM \`group\` WHERE id NOT IN (${slots}) AND status_page_id = ?`, data); + } + + const server = UptimeKumaServer.getInstance(); + + // Also change entry page to new slug if it is the default one, and slug is changed. + if (server.entryPage === "statusPage-" + slug && statusPage.slug !== slug) { + server.entryPage = "statusPage-" + statusPage.slug; + await Settings.set("entryPage", server.entryPage, "general"); + } + + apicache.clear(); + + callback({ + ok: true, + publicGroupList, + }); + } catch (error) { + log.error("socket", error); + + callback({ + ok: false, + msg: error.message, + }); + } + }); + + // Add a new status page + socket.on("addStatusPage", async (title, slug, callback) => { + try { + checkLogin(socket); + + title = title?.trim(); + slug = slug?.trim(); + + // Check empty + if (!title || !slug) { + throw new Error("Please input all fields"); + } + + // Make sure slug is string + if (typeof slug !== "string") { + throw new Error("Slug -Accept string only"); + } + + // lower case only + slug = slug.toLowerCase(); + + checkSlug(slug); + + let statusPage = R.dispense("status_page"); + statusPage.slug = slug; + statusPage.title = title; + statusPage.theme = "auto"; + statusPage.icon = ""; + statusPage.autoRefreshInterval = 300; + await R.store(statusPage); + + callback({ + ok: true, + msg: "successAdded", + msgi18n: true, + slug: slug, + }); + } catch (error) { + log.error("socket", error); + callback({ + ok: false, + msg: error.message, + }); + } + }); + + // Delete a status page + socket.on("deleteStatusPage", async (slug, callback) => { + const server = UptimeKumaServer.getInstance(); + + try { + checkLogin(socket); + + let statusPageID = await StatusPage.slugToID(slug); + + if (statusPageID) { + // Reset entry page if it is the default one. + if (server.entryPage === "statusPage-" + slug) { + server.entryPage = "dashboard"; + await Settings.set("entryPage", server.entryPage, "general"); + } + + // No need to delete records from `status_page_cname`, because it has cascade foreign key. + // But for incident & group, it is hard to add cascade foreign key during migration, so they have to be deleted manually. + + // Delete incident + await R.exec("DELETE FROM incident WHERE status_page_id = ? ", [statusPageID]); + + // Delete group + await R.exec("DELETE FROM `group` WHERE status_page_id = ? ", [statusPageID]); + + // Delete status_page + await R.exec("DELETE FROM status_page WHERE id = ? ", [statusPageID]); + + apicache.clear(); + } else { + throw new Error("Status Page is not found"); + } + + callback({ + ok: true, + }); + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); +}; + +/** + * Check slug a-z, 0-9, - only + * Regex from: https://stackoverflow.com/questions/22454258/js-regex-string-validation-for-slug + * @param {string} slug Slug to test + * @returns {void} + * @throws Slug is not valid + */ +function checkSlug(slug) { + if (typeof slug !== "string") { + throw new Error("Slug must be string"); + } + + slug = slug.trim(); + + if (!slug) { + throw new Error("Slug cannot be empty"); + } + + if (!slug.match(/^[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*$/)) { + throw new Error("Invalid Slug"); + } +} diff --git a/server/translatable-error.js b/server/translatable-error.js new file mode 100644 index 0000000..dbd4641 --- /dev/null +++ b/server/translatable-error.js @@ -0,0 +1,22 @@ +/** + * Error whose message is a translation key. + * @augments Error + */ +class TranslatableError extends Error { + /** + * Indicates that the error message is a translation key. + */ + msgi18n = true; + + /** + * Create a TranslatableError. + * @param {string} key - Translation key present in src/lang/en.json + * @param {object} meta Arbitrary metadata + */ + constructor(key, meta = {}) { + super(key); + this.meta = meta; + Error.captureStackTrace(this, this.constructor); + } +} +module.exports = TranslatableError; diff --git a/server/uptime-calculator.js b/server/uptime-calculator.js new file mode 100644 index 0000000..3e44cb8 --- /dev/null +++ b/server/uptime-calculator.js @@ -0,0 +1,891 @@ +const dayjs = require("dayjs"); +const { UP, MAINTENANCE, DOWN, PENDING } = require("../src/util"); +const { LimitQueue } = require("./utils/limit-queue"); +const { log } = require("../src/util"); +const { R } = require("redbean-node"); + +/** + * Calculates the uptime of a monitor. + */ +class UptimeCalculator { + /** + * @private + * @type {{string:UptimeCalculator}} + */ + static list = {}; + + /** + * For testing purposes, we can set the current date to a specific date. + * @type {dayjs.Dayjs} + */ + static currentDate = null; + + /** + * monitorID the id of the monitor + * @type {number} + */ + monitorID; + + /** + * Recent 24-hour uptime, each item is a 1-minute interval + * Key: {number} DivisionKey + * @type {LimitQueue} + */ + minutelyUptimeDataList = new LimitQueue(24 * 60); + + /** + * Recent 30-day uptime, each item is a 1-hour interval + * Key: {number} DivisionKey + * @type {LimitQueue} + */ + hourlyUptimeDataList = new LimitQueue(30 * 24); + + /** + * Daily uptime data, + * Key: {number} DailyKey + */ + dailyUptimeDataList = new LimitQueue(365); + + lastUptimeData = null; + lastHourlyUptimeData = null; + lastDailyUptimeData = null; + + lastDailyStatBean = null; + lastHourlyStatBean = null; + lastMinutelyStatBean = null; + + /** + * For migration purposes. + * @type {boolean} + */ + migrationMode = false; + + statMinutelyKeepHour = 24; + statHourlyKeepDay = 30; + + /** + * Get the uptime calculator for a monitor + * Initializes and returns the monitor if it does not exist + * @param {number} monitorID the id of the monitor + * @returns {Promise} UptimeCalculator + */ + static async getUptimeCalculator(monitorID) { + if (!monitorID) { + throw new Error("Monitor ID is required"); + } + + if (!UptimeCalculator.list[monitorID]) { + UptimeCalculator.list[monitorID] = new UptimeCalculator(); + await UptimeCalculator.list[monitorID].init(monitorID); + } + return UptimeCalculator.list[monitorID]; + } + + /** + * Remove a monitor from the list + * @param {number} monitorID the id of the monitor + * @returns {Promise} + */ + static async remove(monitorID) { + delete UptimeCalculator.list[monitorID]; + } + + /** + * Remove all monitors from the list + * @returns {Promise} + */ + static async removeAll() { + UptimeCalculator.list = {}; + } + + /** + * + */ + constructor() { + if (process.env.TEST_BACKEND) { + // Override the getCurrentDate() method to return a specific date + // Only for testing + this.getCurrentDate = () => { + if (UptimeCalculator.currentDate) { + return UptimeCalculator.currentDate; + } else { + return dayjs.utc(); + } + }; + } + } + + /** + * Initialize the uptime calculator for a monitor + * @param {number} monitorID the id of the monitor + * @returns {Promise} + */ + async init(monitorID) { + this.monitorID = monitorID; + + let now = this.getCurrentDate(); + + // Load minutely data from database (recent 24 hours only) + let minutelyStatBeans = await R.find("stat_minutely", " monitor_id = ? AND timestamp > ? ORDER BY timestamp", [ + monitorID, + this.getMinutelyKey(now.subtract(24, "hour")), + ]); + + for (let bean of minutelyStatBeans) { + let data = { + up: bean.up, + down: bean.down, + avgPing: bean.ping, + minPing: bean.pingMin, + maxPing: bean.pingMax, + }; + + if (bean.extras != null) { + data = { + ...data, + ...JSON.parse(bean.extras), + }; + } + + let key = bean.timestamp; + this.minutelyUptimeDataList.push(key, data); + } + + // Load hourly data from database (recent 30 days only) + let hourlyStatBeans = await R.find("stat_hourly", " monitor_id = ? AND timestamp > ? ORDER BY timestamp", [ + monitorID, + this.getHourlyKey(now.subtract(30, "day")), + ]); + + for (let bean of hourlyStatBeans) { + let data = { + up: bean.up, + down: bean.down, + avgPing: bean.ping, + minPing: bean.pingMin, + maxPing: bean.pingMax, + }; + + if (bean.extras != null) { + data = { + ...data, + ...JSON.parse(bean.extras), + }; + } + + this.hourlyUptimeDataList.push(bean.timestamp, data); + } + + // Load daily data from database (recent 365 days only) + let dailyStatBeans = await R.find("stat_daily", " monitor_id = ? AND timestamp > ? ORDER BY timestamp", [ + monitorID, + this.getDailyKey(now.subtract(365, "day")), + ]); + + for (let bean of dailyStatBeans) { + let data = { + up: bean.up, + down: bean.down, + avgPing: bean.ping, + minPing: bean.pingMin, + maxPing: bean.pingMax, + }; + + if (bean.extras != null) { + data = { + ...data, + ...JSON.parse(bean.extras), + }; + } + + this.dailyUptimeDataList.push(bean.timestamp, data); + } + } + + /** + * @param {number} status status + * @param {number} ping Ping + * @param {dayjs.Dayjs} date Date (Only for migration) + * @returns {Promise} date + * @throws {Error} Invalid status + */ + async update(status, ping = 0, date) { + if (!date) { + date = this.getCurrentDate(); + } + + let flatStatus = this.flatStatus(status); + + if (flatStatus === DOWN && ping > 0) { + log.debug("uptime_calc", "The ping is not effective when the status is DOWN"); + } + + let divisionKey = this.getMinutelyKey(date); + let hourlyKey = this.getHourlyKey(date); + let dailyKey = this.getDailyKey(date); + + let minutelyData = this.minutelyUptimeDataList[divisionKey]; + let hourlyData = this.hourlyUptimeDataList[hourlyKey]; + let dailyData = this.dailyUptimeDataList[dailyKey]; + + if (status === MAINTENANCE) { + minutelyData.maintenance = minutelyData.maintenance ? minutelyData.maintenance + 1 : 1; + hourlyData.maintenance = hourlyData.maintenance ? hourlyData.maintenance + 1 : 1; + dailyData.maintenance = dailyData.maintenance ? dailyData.maintenance + 1 : 1; + } else if (flatStatus === UP) { + minutelyData.up += 1; + hourlyData.up += 1; + dailyData.up += 1; + + // Only UP status can update the ping + if (!isNaN(ping)) { + // Add avg ping + // The first beat of the minute, the ping is the current ping + if (minutelyData.up === 1) { + minutelyData.avgPing = ping; + minutelyData.minPing = ping; + minutelyData.maxPing = ping; + } else { + minutelyData.avgPing = (minutelyData.avgPing * (minutelyData.up - 1) + ping) / minutelyData.up; + minutelyData.minPing = Math.min(minutelyData.minPing, ping); + minutelyData.maxPing = Math.max(minutelyData.maxPing, ping); + } + + // Add avg ping + // The first beat of the hour, the ping is the current ping + if (hourlyData.up === 1) { + hourlyData.avgPing = ping; + hourlyData.minPing = ping; + hourlyData.maxPing = ping; + } else { + hourlyData.avgPing = (hourlyData.avgPing * (hourlyData.up - 1) + ping) / hourlyData.up; + hourlyData.minPing = Math.min(hourlyData.minPing, ping); + hourlyData.maxPing = Math.max(hourlyData.maxPing, ping); + } + + // Add avg ping (daily) + // The first beat of the day, the ping is the current ping + if (dailyData.up === 1) { + dailyData.avgPing = ping; + dailyData.minPing = ping; + dailyData.maxPing = ping; + } else { + dailyData.avgPing = (dailyData.avgPing * (dailyData.up - 1) + ping) / dailyData.up; + dailyData.minPing = Math.min(dailyData.minPing, ping); + dailyData.maxPing = Math.max(dailyData.maxPing, ping); + } + } + } else if (flatStatus === DOWN) { + minutelyData.down += 1; + hourlyData.down += 1; + dailyData.down += 1; + } + + if (minutelyData !== this.lastUptimeData) { + this.lastUptimeData = minutelyData; + } + + if (hourlyData !== this.lastHourlyUptimeData) { + this.lastHourlyUptimeData = hourlyData; + } + + if (dailyData !== this.lastDailyUptimeData) { + this.lastDailyUptimeData = dailyData; + } + + // Don't store data in test mode + if (process.env.TEST_BACKEND) { + log.debug("uptime_calc", "Skip storing data in test mode"); + return date; + } + + let dailyStatBean = await this.getDailyStatBean(dailyKey); + dailyStatBean.up = dailyData.up; + dailyStatBean.down = dailyData.down; + dailyStatBean.ping = dailyData.avgPing; + dailyStatBean.pingMin = dailyData.minPing; + dailyStatBean.pingMax = dailyData.maxPing; + { + // eslint-disable-next-line no-unused-vars + const { up, down, avgPing, minPing, maxPing, timestamp, ...extras } = dailyData; + if (Object.keys(extras).length > 0) { + dailyStatBean.extras = JSON.stringify(extras); + } + } + await R.store(dailyStatBean); + + let currentDate = this.getCurrentDate(); + + // For migration mode, we don't need to store old hourly and minutely data, but we need 30-day's hourly data + // Run anyway for non-migration mode + if (!this.migrationMode || date.isAfter(currentDate.subtract(this.statHourlyKeepDay, "day"))) { + let hourlyStatBean = await this.getHourlyStatBean(hourlyKey); + hourlyStatBean.up = hourlyData.up; + hourlyStatBean.down = hourlyData.down; + hourlyStatBean.ping = hourlyData.avgPing; + hourlyStatBean.pingMin = hourlyData.minPing; + hourlyStatBean.pingMax = hourlyData.maxPing; + { + // eslint-disable-next-line no-unused-vars + const { up, down, avgPing, minPing, maxPing, timestamp, ...extras } = hourlyData; + if (Object.keys(extras).length > 0) { + hourlyStatBean.extras = JSON.stringify(extras); + } + } + await R.store(hourlyStatBean); + } + + // For migration mode, we don't need to store old hourly and minutely data, but we need 24-hour's minutely data + // Run anyway for non-migration mode + if (!this.migrationMode || date.isAfter(currentDate.subtract(this.statMinutelyKeepHour, "hour"))) { + let minutelyStatBean = await this.getMinutelyStatBean(divisionKey); + minutelyStatBean.up = minutelyData.up; + minutelyStatBean.down = minutelyData.down; + minutelyStatBean.ping = minutelyData.avgPing; + minutelyStatBean.pingMin = minutelyData.minPing; + minutelyStatBean.pingMax = minutelyData.maxPing; + { + // eslint-disable-next-line no-unused-vars + const { up, down, avgPing, minPing, maxPing, timestamp, ...extras } = minutelyData; + if (Object.keys(extras).length > 0) { + minutelyStatBean.extras = JSON.stringify(extras); + } + } + await R.store(minutelyStatBean); + } + + // No need to remove old data in migration mode + if (!this.migrationMode) { + // Remove the old data + // TODO: Improvement: Convert it to a job? + log.debug("uptime_calc", "Remove old data"); + await R.exec("DELETE FROM stat_minutely WHERE monitor_id = ? AND timestamp < ?", [ + this.monitorID, + this.getMinutelyKey(currentDate.subtract(this.statMinutelyKeepHour, "hour"), false), + ]); + + await R.exec("DELETE FROM stat_hourly WHERE monitor_id = ? AND timestamp < ?", [ + this.monitorID, + this.getHourlyKey(currentDate.subtract(this.statHourlyKeepDay, "day"), false), + ]); + } + + return date; + } + + /** + * Get the daily stat bean + * @param {number} timestamp milliseconds + * @returns {Promise} stat_daily bean + */ + async getDailyStatBean(timestamp) { + if (this.lastDailyStatBean && this.lastDailyStatBean.timestamp === timestamp) { + return this.lastDailyStatBean; + } + + let bean = await R.findOne("stat_daily", " monitor_id = ? AND timestamp = ?", [this.monitorID, timestamp]); + + if (!bean) { + bean = R.dispense("stat_daily"); + bean.monitor_id = this.monitorID; + bean.timestamp = timestamp; + } + + this.lastDailyStatBean = bean; + return this.lastDailyStatBean; + } + + /** + * Get the hourly stat bean + * @param {number} timestamp milliseconds + * @returns {Promise} stat_hourly bean + */ + async getHourlyStatBean(timestamp) { + if (this.lastHourlyStatBean && this.lastHourlyStatBean.timestamp === timestamp) { + return this.lastHourlyStatBean; + } + + let bean = await R.findOne("stat_hourly", " monitor_id = ? AND timestamp = ?", [this.monitorID, timestamp]); + + if (!bean) { + bean = R.dispense("stat_hourly"); + bean.monitor_id = this.monitorID; + bean.timestamp = timestamp; + } + + this.lastHourlyStatBean = bean; + return this.lastHourlyStatBean; + } + + /** + * Get the minutely stat bean + * @param {number} timestamp milliseconds + * @returns {Promise} stat_minutely bean + */ + async getMinutelyStatBean(timestamp) { + if (this.lastMinutelyStatBean && this.lastMinutelyStatBean.timestamp === timestamp) { + return this.lastMinutelyStatBean; + } + + let bean = await R.findOne("stat_minutely", " monitor_id = ? AND timestamp = ?", [this.monitorID, timestamp]); + + if (!bean) { + bean = R.dispense("stat_minutely"); + bean.monitor_id = this.monitorID; + bean.timestamp = timestamp; + } + + this.lastMinutelyStatBean = bean; + return this.lastMinutelyStatBean; + } + + /** + * Convert timestamp to minutely key + * @param {dayjs.Dayjs} date The heartbeat date + * @param {boolean} createIfMissing Whether to create a missing bucket, defaults to true + * @returns {number} Timestamp + */ + getMinutelyKey(date, createIfMissing = true) { + // Truncate value to minutes (e.g. 2021-01-01 12:34:56 -> 2021-01-01 12:34:00) + date = date.startOf("minute"); + + // Convert to timestamp in second + let divisionKey = date.unix(); + + if (createIfMissing && !(divisionKey in this.minutelyUptimeDataList)) { + this.minutelyUptimeDataList.push(divisionKey, { + up: 0, + down: 0, + avgPing: 0, + minPing: 0, + maxPing: 0, + }); + } + + return divisionKey; + } + + /** + * Convert timestamp to hourly key + * @param {dayjs.Dayjs} date The heartbeat date + * @param {boolean} createIfMissing Whether to create a missing bucket, defaults to true + * @returns {number} Timestamp + */ + getHourlyKey(date, createIfMissing = true) { + // Truncate value to hours (e.g. 2021-01-01 12:34:56 -> 2021-01-01 12:00:00) + date = date.startOf("hour"); + + // Convert to timestamp in second + let divisionKey = date.unix(); + + if (createIfMissing && !(divisionKey in this.hourlyUptimeDataList)) { + this.hourlyUptimeDataList.push(divisionKey, { + up: 0, + down: 0, + avgPing: 0, + minPing: 0, + maxPing: 0, + }); + } + + return divisionKey; + } + + /** + * Convert timestamp to daily key + * @param {dayjs.Dayjs} date The heartbeat date + * @param {boolean} createIfMissing Whether to create a missing bucket, defaults to true + * @returns {number} Timestamp + */ + getDailyKey(date, createIfMissing = true) { + // Truncate value to start of day (e.g. 2021-01-01 12:34:56 -> 2021-01-01 00:00:00) + // Considering if the user keep changing could affect the calculation, so use UTC time to avoid this problem. + date = date.utc().startOf("day"); + let dailyKey = date.unix(); + + if (createIfMissing && !this.dailyUptimeDataList[dailyKey]) { + this.dailyUptimeDataList.push(dailyKey, { + up: 0, + down: 0, + avgPing: 0, + minPing: 0, + maxPing: 0, + }); + } + + return dailyKey; + } + + /** + * Convert timestamp to key + * @param {dayjs.Dayjs} datetime Datetime + * @param {"day" | "hour" | "minute"} type the type of data which is expected to be returned + * @returns {number} Timestamp + * @throws {Error} If the type is invalid + */ + getKey(datetime, type) { + switch (type) { + case "day": + return this.getDailyKey(datetime); + case "hour": + return this.getHourlyKey(datetime); + case "minute": + return this.getMinutelyKey(datetime); + default: + throw new Error("Invalid type"); + } + } + + /** + * Flat status to UP or DOWN + * @param {number} status the status which should be turned into a flat status + * @returns {UP|DOWN|PENDING} The flat status + * @throws {Error} Invalid status + */ + flatStatus(status) { + switch (status) { + case UP: + case MAINTENANCE: + return UP; + case DOWN: + case PENDING: + return DOWN; + } + throw new Error("Invalid status"); + } + + /** + * @param {number} num the number of data points which are expected to be returned + * @param {"day" | "hour" | "minute"} type the type of data which is expected to be returned + * @returns {UptimeDataResult} UptimeDataResult + * @throws {Error} The maximum number of minutes greater than 1440 + */ + getData(num, type = "day") { + if (type === "hour" && num > 24 * 30) { + throw new Error("The maximum number of hours is 720"); + } + if (type === "minute" && num > 24 * 60) { + throw new Error("The maximum number of minutes is 1440"); + } + if (type === "day" && num > 365) { + throw new Error("The maximum number of days is 365"); + } + // Get the current time period key based on the type + let key = this.getKey(this.getCurrentDate(), type); + + let total = { + up: 0, + down: 0, + }; + + let totalPing = 0; + let endTimestamp; + + // Get the earliest timestamp of the required period based on the type + switch (type) { + case "day": + endTimestamp = key - 86400 * (num - 1); + break; + case "hour": + endTimestamp = key - 3600 * (num - 1); + break; + case "minute": + endTimestamp = key - 60 * (num - 1); + break; + default: + throw new Error("Invalid type"); + } + + // Sum up all data in the specified time range + while (key >= endTimestamp) { + let data; + + switch (type) { + case "day": + data = this.dailyUptimeDataList[key]; + break; + case "hour": + data = this.hourlyUptimeDataList[key]; + break; + case "minute": + data = this.minutelyUptimeDataList[key]; + break; + default: + throw new Error("Invalid type"); + } + + if (data) { + total.up += data.up; + total.down += data.down; + totalPing += data.avgPing * data.up; + } + + // Set key to the previous time period + switch (type) { + case "day": + key -= 86400; + break; + case "hour": + key -= 3600; + break; + case "minute": + key -= 60; + break; + default: + throw new Error("Invalid type"); + } + } + + let uptimeData = new UptimeDataResult(); + + // If there is no data in the previous time ranges, use the last data? + if (total.up === 0 && total.down === 0) { + switch (type) { + case "day": + if (this.lastDailyUptimeData) { + total = this.lastDailyUptimeData; + totalPing = total.avgPing * total.up; + } else { + return uptimeData; + } + break; + case "hour": + if (this.lastHourlyUptimeData) { + total = this.lastHourlyUptimeData; + totalPing = total.avgPing * total.up; + } else { + return uptimeData; + } + break; + case "minute": + if (this.lastUptimeData) { + total = this.lastUptimeData; + totalPing = total.avgPing * total.up; + } else { + return uptimeData; + } + break; + default: + throw new Error("Invalid type"); + } + } + + let avgPing; + + if (total.up === 0) { + avgPing = null; + } else { + avgPing = totalPing / total.up; + } + + if (total.up + total.down === 0) { + uptimeData.uptime = 0; + } else { + uptimeData.uptime = total.up / (total.up + total.down); + } + uptimeData.avgPing = avgPing; + return uptimeData; + } + + /** + * Get data in form of an array + * @param {number} num the number of data points which are expected to be returned + * @param {"day" | "hour" | "minute"} type the type of data which is expected to be returned + * @returns {Array} uptime data + * @throws {Error} The maximum number of minutes greater than 1440 + */ + getDataArray(num, type = "day") { + if (type === "hour" && num > 24 * 30) { + throw new Error("The maximum number of hours is 720"); + } + if (type === "minute" && num > 24 * 60) { + throw new Error("The maximum number of minutes is 1440"); + } + + // Get the current time period key based on the type + let key = this.getKey(this.getCurrentDate(), type); + + let result = []; + + let endTimestamp; + + // Get the earliest timestamp of the required period based on the type + switch (type) { + case "day": + endTimestamp = key - 86400 * (num - 1); + break; + case "hour": + endTimestamp = key - 3600 * (num - 1); + break; + case "minute": + endTimestamp = key - 60 * (num - 1); + break; + default: + throw new Error("Invalid type"); + } + + // Get datapoints in the specified time range + while (key >= endTimestamp) { + let data; + + switch (type) { + case "day": + data = this.dailyUptimeDataList[key]; + break; + case "hour": + data = this.hourlyUptimeDataList[key]; + break; + case "minute": + data = this.minutelyUptimeDataList[key]; + break; + default: + throw new Error("Invalid type"); + } + + if (data) { + data.timestamp = key; + result.push(data); + } + + // Set key to the previous time period + switch (type) { + case "day": + key -= 86400; + break; + case "hour": + key -= 3600; + break; + case "minute": + key -= 60; + break; + default: + throw new Error("Invalid type"); + } + } + + return result; + } + + /** + * Get the uptime data for given duration. + * @param {string} duration A string with a number and a unit (m,h,d,w,M,y), such as 24h, 30d, 1y. + * @returns {UptimeDataResult} UptimeDataResult + * @throws {Error} Invalid duration / Unsupported unit + */ + getDataByDuration(duration) { + const durationNumStr = duration.slice(0, -1); + + if (!/^[0-9]+$/.test(durationNumStr)) { + throw new Error(`Invalid duration: ${duration}`); + } + const num = Number(durationNumStr); + const unit = duration.slice(-1); + + switch (unit) { + case "m": + return this.getData(num, "minute"); + case "h": + return this.getData(num, "hour"); + case "d": + return this.getData(num, "day"); + case "w": + return this.getData(7 * num, "day"); + case "M": + return this.getData(30 * num, "day"); + case "y": + return this.getData(365 * num, "day"); + default: + throw new Error(`Unsupported unit (${unit}) for badge duration ${duration}`); + } + } + + /** + * 1440 = 24 * 60mins + * @returns {UptimeDataResult} UptimeDataResult + */ + get24Hour() { + return this.getData(1440, "minute"); + } + + /** + * @returns {UptimeDataResult} UptimeDataResult + */ + get7Day() { + return this.getData(168, "hour"); + } + + /** + * @returns {UptimeDataResult} UptimeDataResult + */ + get30Day() { + return this.getData(30); + } + + /** + * @returns {UptimeDataResult} UptimeDataResult + */ + get1Year() { + return this.getData(365); + } + + /** + * @returns {dayjs.Dayjs} Current datetime in UTC + */ + getCurrentDate() { + return dayjs.utc(); + } + + /** + * For migration purposes. + * @param {boolean} value Migration mode on/off + * @returns {void} + */ + setMigrationMode(value) { + this.migrationMode = value; + } + + /** + * Clear all statistics and heartbeats for a monitor + * @param {number} monitorID the id of the monitor + * @returns {Promise} + */ + static async clearStatistics(monitorID) { + await R.exec("DELETE FROM heartbeat WHERE monitor_id = ?", [monitorID]); + + await R.exec("DELETE FROM stat_minutely WHERE monitor_id = ?", [monitorID]); + await R.exec("DELETE FROM stat_hourly WHERE monitor_id = ?", [monitorID]); + await R.exec("DELETE FROM stat_daily WHERE monitor_id = ?", [monitorID]); + + await UptimeCalculator.remove(monitorID); + } + + /** + * Clear all statistics and heartbeats for all monitors + * @returns {Promise} + */ + static async clearAllStatistics() { + await R.exec("DELETE FROM heartbeat"); + await R.exec("DELETE FROM stat_minutely"); + await R.exec("DELETE FROM stat_hourly"); + await R.exec("DELETE FROM stat_daily"); + + await UptimeCalculator.removeAll(); + } +} + +class UptimeDataResult { + /** + * @type {number} Uptime + */ + uptime = 0; + + /** + * @type {number} Average ping + */ + avgPing = null; +} + +module.exports = { + UptimeCalculator, + UptimeDataResult, +}; diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js new file mode 100644 index 0000000..ecbd7a3 --- /dev/null +++ b/server/uptime-kuma-server.js @@ -0,0 +1,617 @@ +const express = require("express"); +const https = require("https"); +const fs = require("fs"); +const http = require("http"); +const { Server } = require("socket.io"); +const { R } = require("redbean-node"); +const { log, isDevEnv, devOriginList } = require("../src/util"); +const Database = require("./database"); +const util = require("util"); +const { Settings } = require("./settings"); +const dayjs = require("dayjs"); +const childProcessAsync = require("promisify-child-process"); +const path = require("path"); +const axios = require("axios"); +const { isSSL, sslKey, sslCert, sslKeyPassphrase } = require("./config"); +const { getSession, getDisableAuthSession } = require("./better-auth"); +// DO NOT IMPORT HERE IF THE MODULES USED `UptimeKumaServer.getInstance()`, put at the bottom of this file instead. + +/** + * `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue. + * @type {UptimeKumaServer} + */ +class UptimeKumaServer { + /** + * Current server instance + * @type {UptimeKumaServer} + */ + static instance = null; + + /** + * Main monitor list + * @type {{}} + */ + monitorList = {}; + + /** + * Main maintenance list + * @type {{}} + */ + maintenanceList = {}; + + entryPage = "dashboard"; + app = undefined; + httpServer = undefined; + io = undefined; + + /** + * Cache Index HTML + * @type {string} + */ + indexHTML = ""; + + /** + * @type {{}} + */ + static monitorTypeList = {}; + + /** + * Get the current instance of the server if it exists, otherwise + * create a new instance. + * @returns {UptimeKumaServer} Server instance + */ + static getInstance() { + if (UptimeKumaServer.instance == null) { + UptimeKumaServer.instance = new UptimeKumaServer(); + } + return UptimeKumaServer.instance; + } + + /** + * + */ + constructor() { + // Set axios default user-agent to Uptime-Kuma/version + axios.defaults.headers.common["User-Agent"] = this.getUserAgent(); + + // Set default axios timeout to 5 minutes instead of infinity + axios.defaults.timeout = 300 * 1000; + + log.info("server", "Creating express and socket.io instance"); + this.app = express(); + if (isSSL) { + log.info("server", "Server Type: HTTPS"); + this.httpServer = https.createServer( + { + key: fs.readFileSync(sslKey), + cert: fs.readFileSync(sslCert), + passphrase: sslKeyPassphrase, + }, + this.app + ); + } else { + log.info("server", "Server Type: HTTP"); + this.httpServer = http.createServer(this.app); + } + + try { + this.indexHTML = fs.readFileSync("./dist/index.html").toString(); + } catch (e) { + // "dist/index.html" is not necessary for development + if (process.env.NODE_ENV !== "development") { + log.error("server", "Error: Cannot find 'dist/index.html', did you install correctly?"); + process.exit(1); + } + } + + // Set Monitor Types + UptimeKumaServer.monitorTypeList["real-browser"] = new RealBrowserMonitorType(); + UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing(); + UptimeKumaServer.monitorTypeList["websocket-upgrade"] = new WebSocketMonitorType(); + UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType(); + UptimeKumaServer.monitorTypeList["postgres"] = new PostgresMonitorType(); + UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType(); + UptimeKumaServer.monitorTypeList["smtp"] = new SMTPMonitorType(); + UptimeKumaServer.monitorTypeList["group"] = new GroupMonitorType(); + UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType(); + UptimeKumaServer.monitorTypeList["grpc-keyword"] = new GrpcKeywordMonitorType(); + UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType(); + UptimeKumaServer.monitorTypeList["rabbitmq"] = new RabbitMqMonitorType(); + UptimeKumaServer.monitorTypeList["sip-options"] = new SIPMonitorType(); + UptimeKumaServer.monitorTypeList["gamedig"] = new GameDigMonitorType(); + UptimeKumaServer.monitorTypeList["steam"] = new SteamMonitorType(); + UptimeKumaServer.monitorTypeList["port"] = new TCPMonitorType(); + UptimeKumaServer.monitorTypeList["manual"] = new ManualMonitorType(); + UptimeKumaServer.monitorTypeList["globalping"] = new GlobalpingMonitorType(this.getUserAgent()); + UptimeKumaServer.monitorTypeList["redis"] = new RedisMonitorType(); + UptimeKumaServer.monitorTypeList["pm2"] = new PM2MonitorType(); + UptimeKumaServer.monitorTypeList["system-service"] = new SystemServiceMonitorType(); + UptimeKumaServer.monitorTypeList["sqlserver"] = new MssqlMonitorType(); + UptimeKumaServer.monitorTypeList["mysql"] = new MysqlMonitorType(); + UptimeKumaServer.monitorTypeList["sftp"] = new SFTPMonitorType(); + UptimeKumaServer.monitorTypeList["oracledb"] = new OracleDbMonitorType(); + UptimeKumaServer.monitorTypeList["ntp"] = new NTPMonitorType(); + + // Allow all CORS origins (polling) in development + let cors = undefined; + if (isDevEnv()) { + cors = { + origin: devOriginList, + credentials: true, + methods: ["GET", "POST"], + }; + } + + this.io = new Server(this.httpServer, { + cors, + cookie: true, + allowRequest: async (req, callback) => { + let transport; + // It should be always true, but just in case, because this property is not documented + if (req._query) { + transport = req._query.transport; + } else { + log.error("socket", "Ops!!! Cannot get transport type, assume that it is polling"); + transport = "polling"; + } + + const clientIP = await this.getClientIPwithProxy(req.connection.remoteAddress, req.headers); + log.info("socket", `New ${transport} connection, IP = ${clientIP}`); + + // The following check is only for websocket connections, polling connections are already protected by CORS + if (transport === "polling") { + callback(null, true); + } else if (transport === "websocket") { + const bypass = process.env.UPTIME_KUMA_WS_ORIGIN_CHECK === "bypass"; + if (bypass) { + log.info("auth", "WebSocket origin check is bypassed"); + callback(null, true); + } else if (!req.headers.origin) { + log.info("auth", "WebSocket with no origin is allowed"); + callback(null, true); + } else { + let host = req.headers.host; + let origin = req.headers.origin; + + try { + let originURL = new URL(origin); + let xForwardedFor; + if (await Settings.get("trustProxy")) { + xForwardedFor = req.headers["x-forwarded-for"]; + } + + if (host !== originURL.host && xForwardedFor !== originURL.host) { + callback(null, false); + log.error("auth", `Origin (${origin}) does not match host (${host}), IP: ${clientIP}`); + } else { + callback(null, true); + } + } catch (e) { + // Invalid origin url, probably not from browser + callback(null, false); + log.error("auth", `Invalid origin url (${origin}), IP: ${clientIP}`); + } + } + } + }, + }); + } + + /** + * Initialise app after the database has been set up + * @returns {Promise} + */ + async initAfterDatabaseReady() { + // Screenshots + this.app.use("/screenshots", async (req, res, next) => { + try { + let session; + if (!(await Settings.get("disableAuth"))) { + session = await getSession(req.headers.cookie); + } else { + session = await getDisableAuthSession(); + } + if (session) { + express.static(Database.screenshotDir)(req, res, next); + } else { + res.sendStatus(401); + } + } catch (_) { + res.sendStatus(401); + } + }); + + process.env.TZ = await this.getTimezone(); + dayjs.tz.setDefault(process.env.TZ); + log.debug("DEBUG", "Timezone: " + process.env.TZ); + log.debug("DEBUG", "Current Time: " + dayjs.tz().format()); + + await this.loadMaintenanceList(); + } + + /** + * Send list of monitors to client + * @param {Socket} socket Socket to send list on + * @returns {Promise} List of monitors + */ + async sendMonitorList(socket) { + let list = await this.getMonitorJSONList(); + this.io.to(socket.userID).emit("monitorList", list); + return list; + } + + /** + * Update Monitor into list + * @param {Socket} socket Socket to send list on + * @param {number} monitorID update or deleted monitor id + * @returns {Promise} + */ + async sendUpdateMonitorIntoList(socket, monitorID) { + let list = await this.getMonitorJSONList(monitorID); + if (list && list[monitorID]) { + this.io.to(socket.userID).emit("updateMonitorIntoList", list); + } + } + + /** + * Delete Monitor from list + * @param {Socket} socket Socket to send list on + * @param {number} monitorID update or deleted monitor id + * @returns {Promise} + */ + async sendDeleteMonitorFromList(socket, monitorID) { + this.io.to(socket.userID).emit("deleteMonitorFromList", monitorID); + } + + /** + * Get a list of monitors, optionally filtered to a single monitor. + * @param {number} monitorID - The ID of monitor for. + * @returns {Promise} A promise that resolves to an object with monitor IDs as keys and monitor objects as values. + * + * Generated by Trelent + */ + async getMonitorJSONList(monitorID = null) { + let queryKeys = []; + let queryParams = []; + + if (monitorID) { + queryKeys.push(" id = ? "); + queryParams.push(monitorID); + } + + const query = queryKeys.join(" AND "); + const orderBy = "ORDER BY weight DESC, name"; + + let monitorList; + + if (queryKeys.length === 0) { + monitorList = await R.findAll("monitor", orderBy, queryParams); + } else { + monitorList = await R.find("monitor", query + orderBy, queryParams); + } + + const monitorData = monitorList.map((monitor) => ({ + id: monitor.id, + active: monitor.active, + name: monitor.name, + })); + const preloadData = await Monitor.preparePreloadData(monitorData); + + const result = {}; + monitorList.forEach((monitor) => (result[monitor.id] = monitor.toJSON(preloadData))); + return result; + } + + /** + * Send maintenance list to client + * @param {Socket} socket Socket.io instance to send to + * @returns {Promise} Maintenance list + */ + async sendMaintenanceList(socket) { + return await this.sendMaintenanceListByUserID(socket.userID); + } + + /** + * Send list of maintenances to user + * @param {number} userID User to send list to + * @returns {Promise} Maintenance list + */ + async sendMaintenanceListByUserID(userID) { + let list = await this.getMaintenanceJSONList(userID); + this.io.to(userID).emit("maintenanceList", list); + return list; + } + + /** + * Get a list of maintenances for the given user. + * @param {string} userID - The ID of the user to get maintenances for. + * @returns {Promise} A promise that resolves to an object with maintenance IDs as keys and maintenances objects as values. + */ + async getMaintenanceJSONList(userID) { + let result = {}; + for (let maintenanceID in this.maintenanceList) { + result[maintenanceID] = await this.maintenanceList[maintenanceID].toJSON(); + } + return result; + } + + /** + * Load maintenance list and run + * @param {any} userID Unused + * @returns {Promise} + */ + async loadMaintenanceList(userID) { + let maintenanceList = await R.findAll("maintenance", " ORDER BY end_date DESC, title", []); + + for (let maintenance of maintenanceList) { + this.maintenanceList[maintenance.id] = maintenance; + maintenance.run(this); + } + } + + /** + * Retrieve a specific maintenance + * @param {number} maintenanceID ID of maintenance to retrieve + * @returns {(object|null)} Maintenance if it exists + */ + getMaintenance(maintenanceID) { + if (this.maintenanceList[maintenanceID]) { + return this.maintenanceList[maintenanceID]; + } + return null; + } + + /** + * Write error to log file + * @param {any} error The error to write + * @param {boolean} outputToConsole Should the error also be output to console? + * @returns {void} + */ + static errorLog(error, outputToConsole = true) { + const errorLogStream = fs.createWriteStream(path.join(Database.dataDir, "/error.log"), { + flags: "a", + }); + + errorLogStream.on("error", () => { + log.info("", "Cannot write to error.log"); + }); + + if (errorLogStream) { + const dateTime = R.isoDateTime(); + errorLogStream.write(`[${dateTime}] ` + util.format(error) + "\n"); + + if (outputToConsole) { + console.error(error); + } + } + + errorLogStream.end(); + } + + /** + * Get the IP of the client connected to the socket + * @param {Socket} socket Socket to query + * @returns {Promise} IP of client + */ + getClientIP(socket) { + return this.getClientIPwithProxy(socket.client.conn.remoteAddress, socket.client.conn.request.headers); + } + + /** + * @param {string} clientIP Raw client IP + * @param {IncomingHttpHeaders} headers HTTP headers + * @returns {Promise} Client IP with proxy (if trusted) + */ + async getClientIPwithProxy(clientIP, headers) { + if (clientIP === undefined) { + clientIP = ""; + } + + if (await Settings.get("trustProxy")) { + const forwardedFor = headers["x-forwarded-for"]; + + return ( + (typeof forwardedFor === "string" ? forwardedFor.split(",")[0].trim() : null) || + headers["x-real-ip"] || + clientIP.replace(/^::ffff:/, "") + ); + } else { + return clientIP.replace(/^::ffff:/, ""); + } + } + + /** + * Attempt to get the current server timezone + * If this fails, fall back to environment variables and then make a + * guess. + * @returns {Promise} Current timezone + */ + async getTimezone() { + // From process.env.TZ + try { + if (process.env.TZ) { + this.checkTimezone(process.env.TZ); + return process.env.TZ; + } + } catch (e) { + log.warn("timezone", e.message + " in process.env.TZ"); + } + + let timezone = await Settings.get("serverTimezone"); + + // From Settings + try { + log.debug("timezone", "Using timezone from settings: " + timezone); + if (timezone) { + this.checkTimezone(timezone); + return timezone; + } + } catch (e) { + log.warn("timezone", e.message + " in settings"); + } + + // Guess + try { + let guess = dayjs.tz.guess(); + log.debug("timezone", "Guessing timezone: " + guess); + if (guess) { + this.checkTimezone(guess); + return guess; + } else { + return "UTC"; + } + } catch (e) { + // Guess failed, fall back to UTC + log.debug("timezone", "Guessed an invalid timezone. Use UTC as fallback"); + return "UTC"; + } + } + + /** + * Get the current offset + * @returns {string} Time offset + */ + getTimezoneOffset() { + return dayjs().format("Z"); + } + + /** + * Throw an error if the timezone is invalid + * @param {string} timezone Timezone to test + * @returns {void} + * @throws The timezone is invalid + */ + checkTimezone(timezone) { + try { + dayjs.utc("2013-11-18 11:55").tz(timezone).format(); + } catch (e) { + throw new Error("Invalid timezone:" + timezone); + } + } + + /** + * Set the current server timezone and environment variables + * @param {string} timezone Timezone to set + * @returns {Promise} + */ + async setTimezone(timezone) { + this.checkTimezone(timezone); + await Settings.set("serverTimezone", timezone, "general"); + process.env.TZ = timezone; + dayjs.tz.setDefault(timezone); + } + + /** + * TODO: Listen logic should be moved to here + * @returns {Promise} + */ + async start() { + let enable = await Settings.get("nscd"); + + if (enable || enable === null) { + await this.startNSCDServices(); + } + } + + /** + * Stop the server + * @returns {Promise} + */ + async stop() { + let enable = await Settings.get("nscd"); + + if (enable || enable === null) { + await this.stopNSCDServices(); + } + } + + /** + * Start all system services (e.g. nscd) + * For now, only used in Docker + * @returns {void} + */ + async startNSCDServices() { + if (process.env.UPTIME_KUMA_IS_CONTAINER) { + try { + log.info("services", "Starting nscd"); + await childProcessAsync.exec("sudo service nscd start"); + } catch (e) { + log.info("services", "Failed to start nscd"); + } + } + } + + /** + * Stop all system services + * @returns {void} + */ + async stopNSCDServices() { + if (process.env.UPTIME_KUMA_IS_CONTAINER) { + try { + log.info("services", "Stopping nscd"); + await childProcessAsync.exec("sudo service nscd stop"); + } catch (e) { + log.info("services", "Failed to stop nscd"); + } + } + } + + /** + * Default User-Agent when making HTTP requests + * @returns {string} User-Agent + */ + getUserAgent() { + return "Uptime-Kuma/" + require("../package.json").version; + } + + /** + * Force connected sockets of a user to refresh and disconnect. + * Used for resetting password. + * @param {string} userID User ID + * @param {string?} currentSocketID Current socket ID + * @returns {void} + */ + disconnectAllSocketClients(userID, currentSocketID = undefined) { + for (const socket of this.io.sockets.sockets.values()) { + if (socket.userID === userID && socket.id !== currentSocketID) { + try { + socket.emit("refresh"); + socket.disconnect(); + } catch (e) {} + } + } + } +} + +module.exports = { + UptimeKumaServer, +}; + +// Must be at the end to avoid circular dependencies +const { RealBrowserMonitorType } = require("./monitor-types/real-browser-monitor-type"); +const { TailscalePing } = require("./monitor-types/tailscale-ping"); +const { WebSocketMonitorType } = require("./monitor-types/websocket-upgrade"); +const { DnsMonitorType } = require("./monitor-types/dns"); +const { PostgresMonitorType } = require("./monitor-types/postgres"); +const { MqttMonitorType } = require("./monitor-types/mqtt"); +const { SMTPMonitorType } = require("./monitor-types/smtp"); +const { GroupMonitorType } = require("./monitor-types/group"); +const { SNMPMonitorType } = require("./monitor-types/snmp"); +const { GrpcKeywordMonitorType } = require("./monitor-types/grpc"); +const { MongodbMonitorType } = require("./monitor-types/mongodb"); +const { RabbitMqMonitorType } = require("./monitor-types/rabbitmq"); +const { SIPMonitorType } = require("./monitor-types/sip-options"); +const { GameDigMonitorType } = require("./monitor-types/gamedig"); +const { SteamMonitorType } = require("./monitor-types/steam"); +const { TCPMonitorType } = require("./monitor-types/tcp.js"); +const { ManualMonitorType } = require("./monitor-types/manual"); +const { GlobalpingMonitorType } = require("./monitor-types/globalping"); +const { RedisMonitorType } = require("./monitor-types/redis"); +const { PM2MonitorType } = require("./monitor-types/pm2"); +const { SystemServiceMonitorType } = require("./monitor-types/system-service"); +const { MssqlMonitorType } = require("./monitor-types/mssql"); +const { MysqlMonitorType } = require("./monitor-types/mysql"); +const { SFTPMonitorType } = require("./monitor-types/sftp"); +const { OracleDbMonitorType } = require("./monitor-types/oracledb"); +const { NTPMonitorType } = require("./monitor-types/ntp"); +const Monitor = require("./model/monitor"); diff --git a/server/util-server.js b/server/util-server.js new file mode 100644 index 0000000..01b5438 --- /dev/null +++ b/server/util-server.js @@ -0,0 +1,1046 @@ +import { checkLogin as betterAuthCheckLogin } from "./better-auth"; + +const ping = require("@louislam/ping"); +const { R } = require("redbean-node"); +const { + log, + badgeConstants, + PING_PACKET_SIZE_DEFAULT, + PING_GLOBAL_TIMEOUT_DEFAULT, + PING_COUNT_DEFAULT, + PING_PER_REQUEST_TIMEOUT_DEFAULT, +} = require("../src/util"); +const iconv = require("iconv-lite"); +const chardet = require("chardet"); +const chroma = require("chroma-js"); +const { NtlmClient } = require("./modules/axios-ntlm/lib/ntlmClient.js"); +const { Settings } = require("./settings"); +const RadiusClient = require("./radius-client"); +const oidc = require("openid-client"); +const tls = require("tls"); +const { exists } = require("fs"); +const { networkInterfaces } = require("os"); + +const { + dictionaries: { + rfc2865: { file, attributes }, + }, +} = require("node-radius-utils"); +const dayjs = require("dayjs"); +dayjs.extend(require("dayjs/plugin/utc")); + +// SASLOptions used in JSDoc +// eslint-disable-next-line no-unused-vars +const { Kafka, SASLOptions } = require("kafkajs"); +const crypto = require("crypto"); + +const isWindows = process.platform === /^win/.test(process.platform); + +/** + * Gets an Access Token from an oidc/oauth2 provider + * @param {string} tokenEndpoint The token URI from the auth service provider + * @param {string} clientId The oidc/oauth application client id + * @param {string} clientSecret The oidc/oauth application client secret + * @param {string} scope The scope(s) for which the token should be issued for + * @param {string} audience The audience for which the token should be issued for + * @param {string} authMethod The method used to send the credentials. Default client_secret_basic + * @returns {Promise} TokenSet promise if the token request was successful + */ +exports.getOidcTokenClientCredentials = async ( + tokenEndpoint, + clientId, + clientSecret, + scope, + audience, + authMethod = "client_secret_basic" +) => { + const oauthProvider = new oidc.Issuer({ token_endpoint: tokenEndpoint }); + let client = new oauthProvider.Client({ + client_id: clientId, + client_secret: clientSecret, + token_endpoint_auth_method: authMethod, + }); + + // Increase default timeout and clock tolerance + client[oidc.custom.http_options] = () => ({ timeout: 10000 }); + client[oidc.custom.clock_tolerance] = 5; + + let grantParams = { grant_type: "client_credentials" }; + if (scope) { + grantParams.scope = scope; + } + + if (audience) { + grantParams.audience = audience; + } + return await client.grant(grantParams); +}; + +/** + * Ping the specified machine + * @param {string} destAddr Hostname / IP address of machine to ping + * @param {number} count Number of packets to send before stopping + * @param {string} sourceAddr Source address for sending/receiving echo requests + * @param {boolean} numeric If true, IP addresses will be output instead of symbolic hostnames + * @param {number} size Size (in bytes) of echo request to send + * @param {number} deadline Maximum time in seconds before ping stops, regardless of packets sent + * @param {number} timeout Maximum time in seconds to wait for each response + * @returns {Promise} Time for ping in ms rounded to nearest integer + */ +exports.ping = async ( + destAddr, + count = PING_COUNT_DEFAULT, + sourceAddr = "", + numeric = true, + size = PING_PACKET_SIZE_DEFAULT, + deadline = PING_GLOBAL_TIMEOUT_DEFAULT, + timeout = PING_PER_REQUEST_TIMEOUT_DEFAULT +) => { + try { + return await exports.pingAsync(destAddr, false, count, sourceAddr, numeric, size, deadline, timeout); + } catch (e) { + // If the host cannot be resolved, try again with ipv6 + log.debug("ping", "IPv6 error message: " + e.message); + + // As node-ping does not report a specific error for this, try again if it is an empty message with ipv6 no matter what. + if (!e.message) { + return await exports.pingAsync(destAddr, true, count, sourceAddr, numeric, size, deadline, timeout); + } else { + throw e; + } + } +}; + +/** + * Ping the specified machine + * @param {string} destAddr Hostname / IP address of machine to ping + * @param {boolean} ipv6 Should IPv6 be used? + * @param {number} count Number of packets to send before stopping + * @param {string} sourceAddr Source address for sending/receiving echo requests + * @param {boolean} numeric If true, IP addresses will be output instead of symbolic hostnames + * @param {number} size Size (in bytes) of echo request to send + * @param {number} deadline Maximum time in seconds before ping stops, regardless of packets sent + * @param {number} timeout Maximum time in seconds to wait for each response + * @returns {Promise} Time for ping in ms rounded to nearest integer + */ +exports.pingAsync = function ( + destAddr, + ipv6 = false, + count = PING_COUNT_DEFAULT, + sourceAddr = "", + numeric = true, + size = PING_PACKET_SIZE_DEFAULT, + deadline = PING_GLOBAL_TIMEOUT_DEFAULT, + timeout = PING_PER_REQUEST_TIMEOUT_DEFAULT +) { + try { + const url = new URL(`http://${destAddr}`); + destAddr = url.hostname; + if (destAddr.startsWith("[") && destAddr.endsWith("]")) { + destAddr = destAddr.slice(1, -1); + } + } catch (e) { + // ignore + } + + return new Promise((resolve, reject) => { + ping.promise + .probe(destAddr, { + v6: ipv6, + min_reply: count, + sourceAddr: sourceAddr, + numeric: numeric, + packetSize: size, + deadline: deadline, + timeout: timeout, + }) + .then((res) => { + // If ping failed, it will set field to unknown + if (res.alive) { + resolve(res.time); + } else { + if (isWindows) { + reject(new Error(exports.convertToUTF8(res.output))); + } else { + reject(new Error(res.output)); + } + } + }) + .catch((err) => { + reject(err); + }); + }); +}; + +/** + * Monitor Kafka using Producer + * @param {string[]} brokers List of kafka brokers to connect, host and + * port joined by ':' + * @param {string} topic Topic name to produce into + * @param {string} message Message to produce + * @param {object} options Kafka client options. Contains ssl, clientId, + * allowAutoTopicCreation and interval (interval defaults to 20, + * allowAutoTopicCreation defaults to false, clientId defaults to + * "Uptime-Kuma" and ssl defaults to false) + * @param {SASLOptions} saslOptions Options for kafka client + * Authentication (SASL) (defaults to {}) + * @returns {Promise} Status message + */ +exports.kafkaProducerAsync = function (brokers, topic, message, options = {}, saslOptions = {}) { + return new Promise((resolve, reject) => { + const { + interval = 20, + allowAutoTopicCreation = false, + ssl = false, + clientId = "Uptime-Kuma", + connectionTimeout = 1, + } = options; + + let connectedToKafka = false; + + const timeoutID = setTimeout( + () => { + log.debug("kafkaProducer", "KafkaProducer timeout triggered"); + connectedToKafka = true; + reject(new Error("Timeout")); + }, + interval * 1000 * 0.8 + ); + + if (saslOptions.mechanism === "None") { + saslOptions = undefined; + } + + let client = new Kafka({ + brokers: brokers, + clientId: clientId, + sasl: saslOptions, + retry: { + retries: 0, + }, + ssl: ssl, + connectionTimeout: connectionTimeout * 1000, + }); + + let producer = client.producer({ + allowAutoTopicCreation: allowAutoTopicCreation, + retry: { + retries: 0, + }, + }); + + producer + .connect() + .then(() => { + producer + .send({ + topic: topic, + messages: [ + { + value: message, + }, + ], + }) + .then((_) => { + resolve("Message sent successfully"); + }) + .catch((e) => { + connectedToKafka = true; + producer.disconnect(); + clearTimeout(timeoutID); + reject(new Error("Error sending message: " + e.message)); + }) + .finally(() => { + connectedToKafka = true; + clearTimeout(timeoutID); + }); + }) + .catch((e) => { + connectedToKafka = true; + producer.disconnect(); + clearTimeout(timeoutID); + reject(new Error("Error in producer connection: " + e.message)); + }); + + producer.on("producer.network.request_timeout", (_) => { + if (!connectedToKafka) { + clearTimeout(timeoutID); + reject(new Error("producer.network.request_timeout")); + } + }); + + producer.on("producer.disconnect", (_) => { + if (!connectedToKafka) { + clearTimeout(timeoutID); + reject(new Error("producer.disconnect")); + } + }); + }); +}; + +/** + * Use NTLM Auth for a http request. + * @param {object} options The http request options + * @param {object} ntlmOptions The auth options + * @returns {Promise<(string[] | object[] | object)>} NTLM response + */ +exports.httpNtlm = function (options, ntlmOptions) { + return new Promise((resolve, reject) => { + let client = NtlmClient(ntlmOptions); + + client(options) + .then((resp) => { + resolve(resp); + }) + .catch((err) => { + reject(err); + }); + }); +}; + +/** + * Query radius server + * @param {string} hostname Hostname of radius server + * @param {string} username Username to use + * @param {string} password Password to use + * @param {string} calledStationId ID of called station + * @param {string} callingStationId ID of calling station + * @param {string} secret Secret to use + * @param {number} port Port to contact radius server on + * @param {number} timeout Timeout for connection to use + * @returns {Promise} Response from server + */ +exports.radius = function ( + hostname, + username, + password, + calledStationId, + callingStationId, + secret, + port = 1812, + timeout = 2500 +) { + const client = new RadiusClient({ + host: hostname, + hostPort: port, + timeout: timeout, + retries: 1, + dictionaries: [file], + }); + + return client + .accessRequest({ + secret: secret, + attributes: [ + [attributes.USER_NAME, username], + [attributes.USER_PASSWORD, password], + [attributes.CALLING_STATION_ID, callingStationId], + [attributes.CALLED_STATION_ID, calledStationId], + ], + }) + .catch((error) => { + // Preserve error stack trace and provide better context + if (error.response?.code) { + const radiusError = new Error(`RADIUS ${error.response.code} from ${hostname}:${port}`); + radiusError.response = error.response; + radiusError.originalError = error; + throw radiusError; + } else { + // Preserve original error message and stack trace + const enhancedError = new Error( + `RADIUS authentication failed for ${hostname}:${port}: ${error.message}` + ); + enhancedError.originalError = error; + enhancedError.stack = error.stack || enhancedError.stack; + throw enhancedError; + } + }); +}; + +/** + * Retrieve value of setting based on key + * @param {string} key Key of setting to retrieve + * @returns {Promise} Value + * @deprecated Use await Settings.get(key) + */ +exports.setting = async function (key) { + return await Settings.get(key); +}; + +/** + * Sets the specified setting to specified value + * @param {string} key Key of setting to set + * @param {any} value Value to set to + * @param {?string} type Type of setting + * @returns {Promise} + */ +exports.setSetting = async function (key, value, type = null) { + await Settings.set(key, value, type); +}; + +/** + * Get settings based on type + * @param {string} type The type of setting + * @returns {Promise} Settings of requested type + */ +exports.getSettings = async function (type) { + return await Settings.getSettings(type); +}; + +/** + * Set settings based on type + * @param {string} type Type of settings to set + * @param {object} data Values of settings + * @returns {Promise} + */ +exports.setSettings = async function (type, data) { + await Settings.setSettings(type, data); +}; + +// ssl-checker by @dyaa +//https://github.com/dyaa/ssl-checker/blob/master/src/index.ts + +/** + * Get number of days between two dates + * @param {Date} validFrom Start date + * @param {Date} validTo End date + * @returns {number} Number of days + */ +const getDaysBetween = (validFrom, validTo) => Math.round(Math.abs(+validFrom - +validTo) / 8.64e7); + +/** + * Get days remaining from a time range + * @param {Date} validFrom Start date + * @param {Date} validTo End date + * @returns {number} Number of days remaining + */ +const getDaysRemaining = (validFrom, validTo) => { + const daysRemaining = getDaysBetween(validFrom, validTo); + if (new Date(validTo).getTime() < new Date().getTime()) { + return -daysRemaining; + } + return daysRemaining; +}; +module.exports.getDaysRemaining = getDaysRemaining; + +/** + * Fix certificate info for display + * @param {object} info The chain obtained from getPeerCertificate() + * @returns {object} An object representing certificate information + * @throws The certificate chain length exceeded 500. + */ +const parseCertificateInfo = function (info) { + let link = info; + let i = 0; + + const existingList = {}; + + while (link) { + log.debug("cert", `[${i}] ${link.fingerprint}`); + + if (!link.valid_from || !link.valid_to) { + break; + } + link.validTo = new Date(link.valid_to); + link.validFor = link.subjectaltname?.replace(/DNS:|IP Address:/g, "").split(", "); + link.daysRemaining = dayjs.utc(link.validTo).diff(dayjs.utc(), "day"); + + existingList[link.fingerprint] = true; + + // Move up the chain until loop is encountered + if (link.issuerCertificate == null) { + link.certType = i === 0 ? "self-signed" : "root CA"; + break; + } else if (link.issuerCertificate.fingerprint in existingList) { + // a root CA certificate is typically "signed by itself" (=> "self signed certificate") and thus the "issuerCertificate" is a reference to itself. + log.debug("cert", `[Last] ${link.issuerCertificate.fingerprint}`); + link.certType = i === 0 ? "self-signed" : "root CA"; + link.issuerCertificate = null; + break; + } else { + link.certType = i === 0 ? "server" : "intermediate CA"; + link = link.issuerCertificate; + } + + // Should be no use, but just in case. + if (i > 500) { + throw new Error("Dead loop occurred in parseCertificateInfo"); + } + i++; + } + + return info; +}; + +/** + * Check if certificate is valid + * @param {tls.TLSSocket} socket TLSSocket, which may or may not be connected + * @returns {null | {valid: boolean, certInfo: object}} Object containing certificate information + */ +exports.checkCertificate = function (socket) { + let certInfoStartTime = dayjs().valueOf(); + + // Return null if there is no socket + if (socket === undefined || socket == null) { + return null; + } + + const info = socket.getPeerCertificate(true); + const valid = socket.authorized || false; + + log.debug("cert", "Parsing Certificate Info"); + const parsedInfo = parseCertificateInfo(info); + + if (process.env.TIMELOGGER === "1") { + log.debug("monitor", "Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms"); + } + + return { + valid: valid, + certInfo: parsedInfo, + }; +}; + +/** + * Checks if the certificate is valid for the provided hostname. + * Defaults to true if feature `X509Certificate` is not available, or input is not valid. + * @param {Buffer} certBuffer - The certificate buffer. + * @param {string} hostname - The hostname to compare against. + * @returns {boolean} True if the certificate is valid for the provided hostname, false otherwise. + */ +exports.checkCertificateHostname = function (certBuffer, hostname) { + let X509Certificate; + try { + X509Certificate = require("node:crypto").X509Certificate; + } catch (_) { + // X509Certificate is not available in this version of Node.js + return true; + } + + if (!X509Certificate || !certBuffer || !hostname) { + return true; + } + + let certObject = new X509Certificate(certBuffer); + return certObject.checkHost(hostname) !== undefined; +}; + +/** + * Check if the provided status code is within the accepted ranges + * @param {number} status The status code to check + * @param {string[]} acceptedCodes An array of accepted status codes + * @returns {boolean} True if status code within range, false otherwise + */ +exports.checkStatusCode = function (status, acceptedCodes) { + if (acceptedCodes == null || acceptedCodes.length === 0) { + return false; + } + + for (const codeRange of acceptedCodes) { + if (typeof codeRange !== "string") { + log.error("monitor", `Accepted status code not a string. ${codeRange} is of type ${typeof codeRange}`); + continue; + } + + const codeRangeSplit = codeRange.split("-").map((string) => parseInt(string)); + if (codeRangeSplit.length === 1) { + if (status === codeRangeSplit[0]) { + return true; + } + } else if (codeRangeSplit.length === 2) { + if (status >= codeRangeSplit[0] && status <= codeRangeSplit[1]) { + return true; + } + } else { + log.error("monitor", `${codeRange} is not a valid status code range`); + continue; + } + } + + return false; +}; + +/** + * Get total number of clients in room + * @param {Server} io Socket server instance + * @param {string} roomName Name of room to check + * @returns {number} Total clients in room + */ +exports.getTotalClientInRoom = (io, roomName) => { + const sockets = io.sockets; + + if (!sockets) { + return 0; + } + + const adapter = sockets.adapter; + + if (!adapter) { + return 0; + } + + const room = adapter.rooms.get(roomName); + + if (room) { + return room.size; + } else { + return 0; + } +}; + +/** + * @deprecated Use allowDevOrigin + * Allow CORS all origins if development + * @param {object} res Response object from axios + * @returns {void} + */ +exports.allowDevAllOrigin = (res) => { + if (process.env.NODE_ENV === "development") { + exports.allowAllOrigin(res); + } +}; + +/** + * @deprecated Use allowOrigin + * Allow CORS all origins + * @param {object} res Response object from axios + * @returns {void} + */ +exports.allowAllOrigin = (res) => { + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS"); + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); +}; + +/** + * Allow CORS all origins if development + * @param {Request} req Express request object + * @param {Response} res Express response object + * @returns {void} + */ +exports.allowDevOrigin = (req, res) => { + if (process.env.NODE_ENV === "development") { + exports.allowOrigin(req, res); + } +}; + +/** + * Allow CORS all origins + * Since Allow-Credentials is set to true, the Access-Control-Allow-Origin cannot be *, so we will set it to the request origin. + * @param {Request} req Express request object + * @param {Response} res Response object + * @returns {void} + */ +exports.allowOrigin = (req, res) => { + res.header("Access-Control-Allow-Origin", req.get("origin")); + res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS"); + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + res.header("Access-Control-Allow-Credentials", "true"); +}; + +/** + * @deprecated Use better-auth's checkLogin + * Check if a user is logged in + * @param {Socket} socket Socket instance + * @returns {void} + * @throws The user is not logged in + */ +exports.checkLogin = (socket) => { + betterAuthCheckLogin(socket); +}; + +/** + * Convert unknown string to UTF8 + * @param {Uint8Array} body Buffer + * @returns {string} UTF8 string + */ +exports.convertToUTF8 = (body) => { + const guessEncoding = chardet.detect(body); + const str = iconv.decode(body, guessEncoding); + return str.toString(); +}; + +/** + * Returns a color code in hex format based on a given percentage: + * 0% => hue = 10 => red + * 100% => hue = 90 => green + * @param {number} percentage float, 0 to 1 + * @param {number} maxHue Maximum hue - int + * @param {number} minHue Minimum hue - int + * @returns {string} Color in hex + */ +exports.percentageToColor = (percentage, maxHue = 90, minHue = 10) => { + const hue = percentage * (maxHue - minHue) + minHue; + try { + return chroma(`hsl(${hue}, 90%, 40%)`).hex(); + } catch (err) { + return badgeConstants.naColor; + } +}; + +/** + * Joins and array of string to one string after filtering out empty values + * @param {string[]} parts Strings to join + * @param {string} connector Separator for joined strings + * @returns {string} Joined strings + */ +exports.filterAndJoin = (parts, connector = "") => { + return parts.filter((part) => !!part && part !== "").join(connector); +}; + +/** + * Send an Error response + * @param {object} res Express response object + * @param {string} msg Message to send + * @returns {void} + */ +module.exports.sendHttpError = (res, msg = "") => { + if (msg.includes("SQLITE_BUSY") || msg.includes("SQLITE_LOCKED")) { + res.status(503).json({ + status: "fail", + msg: msg, + }); + } else if (msg.toLowerCase().includes("not found")) { + res.status(404).json({ + status: "fail", + msg: msg, + }); + } else { + res.status(403).json({ + status: "fail", + msg: msg, + }); + } +}; + +/** + * Convert timezone of time object + * @param {object} obj Time object to update + * @param {string} timezone New timezone to set + * @param {boolean} timeObjectToUTC Convert time object to UTC + * @returns {object} Time object with updated timezone + */ +function timeObjectConvertTimezone(obj, timezone, timeObjectToUTC = true) { + let offsetString; + + if (timezone) { + offsetString = dayjs().tz(timezone).format("Z"); + } else { + offsetString = dayjs().format("Z"); + } + + let hours = parseInt(offsetString.substring(1, 3)); + let minutes = parseInt(offsetString.substring(4, 6)); + + if ((timeObjectToUTC && offsetString.startsWith("+")) || (!timeObjectToUTC && offsetString.startsWith("-"))) { + hours *= -1; + minutes *= -1; + } + + obj.hours += hours; + obj.minutes += minutes; + + // Handle out of bound + if (obj.minutes < 0) { + obj.minutes += 60; + obj.hours--; + } else if (obj.minutes > 60) { + obj.minutes -= 60; + obj.hours++; + } + + if (obj.hours < 0) { + obj.hours += 24; + } else if (obj.hours > 24) { + obj.hours -= 24; + } + + return obj; +} + +/** + * Convert time object to UTC + * @param {object} obj Object to convert + * @param {string} timezone Timezone of time object + * @returns {object} Updated time object + */ +module.exports.timeObjectToUTC = (obj, timezone = undefined) => { + return timeObjectConvertTimezone(obj, timezone, true); +}; + +/** + * Convert time object to local time + * @param {object} obj Object to convert + * @param {string} timezone Timezone to convert to + * @returns {object} Updated object + */ +module.exports.timeObjectToLocal = (obj, timezone = undefined) => { + return timeObjectConvertTimezone(obj, timezone, false); +}; + +/** + * Returns an array of SHA256 fingerprints for all known root certificates. + * @returns {Set} A set of SHA256 fingerprints. + */ +module.exports.rootCertificatesFingerprints = () => { + let fingerprints = tls.rootCertificates.map((cert) => { + let certLines = cert.split("\n"); + certLines.shift(); + certLines.pop(); + let certBody = certLines.join(""); + let buf = Buffer.from(certBody, "base64"); + + const shasum = crypto.createHash("sha256"); + shasum.update(buf); + + return shasum + .digest("hex") + .toUpperCase() + .replace(/(.{2})(?!$)/g, "$1:"); + }); + + fingerprints.push( + "6D:99:FB:26:5E:B1:C5:B3:74:47:65:FC:BC:64:8F:3C:D8:E1:BF:FA:FD:C4:C2:F9:9B:9D:47:CF:7F:F1:C2:4F" + ); // ISRG X1 cross-signed with DST X3 + fingerprints.push( + "8B:05:B6:8C:C6:59:E5:ED:0F:CB:38:F2:C9:42:FB:FD:20:0E:6F:2F:F9:F8:5D:63:C6:99:4E:F5:E0:B0:27:01" + ); // ISRG X2 cross-signed with ISRG X1 + + return new Set(fingerprints); +}; + +module.exports.SHAKE256_LENGTH = 16; + +/** + * @param {string} data The data to be hashed + * @param {number} len Output length of the hash + * @returns {string} The hashed data in hex format + */ +module.exports.shake256 = (data, len) => { + if (!data) { + return ""; + } + return crypto.createHash("shake256", { outputLength: len }).update(data).digest("hex"); +}; + +/** + * Non await sleep + * Source: https://stackoverflow.com/questions/59099454/is-there-a-way-to-call-sleep-without-await-keyword + * @param {number} n Milliseconds to wait + * @returns {void} + */ +module.exports.wait = (n) => { + Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, n); +}; + +// For unit test, export functions +if (process.env.TEST_BACKEND) { + module.exports.__test = { + parseCertificateInfo, + }; + module.exports.__getPrivateFunction = (functionName) => { + return module.exports.__test[functionName]; + }; +} + +/** + * Generates an abort signal with the specified timeout. + * @param {number} timeoutMs - The timeout in milliseconds. + * @returns {AbortSignal | null} - The generated abort signal, or null if not supported. + */ +module.exports.axiosAbortSignal = (timeoutMs) => { + try { + // Just in case, as 0 timeout here will cause the request to be aborted immediately + if (!timeoutMs || timeoutMs <= 0) { + timeoutMs = 5000; + } + return AbortSignal.timeout(timeoutMs); + } catch (_) { + // v16-: AbortSignal.timeout is not supported + try { + const abortController = new AbortController(); + setTimeout(() => abortController.abort(), timeoutMs); + + return abortController.signal; + } catch (_) { + // v15-: AbortController is not supported + return null; + } + } +}; + +/** + * Async version of fs.existsSync + * @param {PathLike} path File path + * @returns {Promise} True if file exists, false otherwise + */ +function fsExists(path) { + return new Promise(function (resolve, reject) { + exists(path, function (exists) { + resolve(exists); + }); + }); +} +module.exports.fsExists = fsExists; + +/** + * Encode user and password to Base64 encoding + * for HTTP "basic" auth, as per RFC-7617 + * @param {string|null} user - The username (defaults to empty string if null/undefined) + * @param {string|null} pass - The password (defaults to empty string if null/undefined) + * @returns {string} Encoded Base64 string + */ +function encodeBase64(user, pass) { + return Buffer.from(`${user || ""}:${pass || ""}`).toString("base64"); +} +module.exports.encodeBase64 = encodeBase64; + +/** + * checks certificate chain for expiring certificates + * @param {object} monitor - The monitor object + * @param {object} tlsInfoObject Information about certificate + * @returns {Promise} + */ +async function checkCertExpiryNotifications(monitor, tlsInfoObject) { + if (!tlsInfoObject || !tlsInfoObject.certInfo || !tlsInfoObject.certInfo.daysRemaining) { + return; + } + + let notificationList = await R.getAll( + "SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", + [monitor.id] + ); + + if (!notificationList.length > 0) { + // fail fast. If no notification is set, all the following checks can be skipped. + log.debug("monitor", "No notification, no need to send cert notification"); + return; + } + + let notifyDays = await Settings.get("tlsExpiryNotifyDays"); + if (notifyDays == null || !Array.isArray(notifyDays)) { + // Reset Default + await Settings.set("tlsExpiryNotifyDays", [7, 14, 21], "general"); + notifyDays = [7, 14, 21]; + } + + for (const targetDays of notifyDays) { + let certInfo = tlsInfoObject.certInfo; + while (certInfo) { + let subjectCN = certInfo.subject["CN"]; + if (monitor.rootCertificates.has(certInfo.fingerprint256)) { + log.debug( + "monitor", + `Known root cert: ${certInfo.certType} certificate "${subjectCN}" (${certInfo.daysRemaining} days valid) on ${targetDays} deadline.` + ); + break; + } else if (certInfo.daysRemaining > targetDays) { + log.debug( + "monitor", + `No need to send cert notification for ${certInfo.certType} certificate "${subjectCN}" (${certInfo.daysRemaining} days valid) on ${targetDays} deadline.` + ); + } else { + log.debug( + "monitor", + `call sendCertNotificationByTargetDays for ${targetDays} deadline on certificate ${subjectCN}.` + ); + await monitor.sendCertNotificationByTargetDays( + subjectCN, + certInfo.certType, + certInfo.daysRemaining, + targetDays, + notificationList + ); + } + certInfo = certInfo.issuerCertificate; + } + } +} +module.exports.checkCertExpiryNotifications = checkCertExpiryNotifications; + +/** + * By default, command-exists will throw a null error if the command does not exist, which is ugly. The function makes it better. + * Read more: https://github.com/mathisonian/command-exists/issues/22 + * @param {string} command Command to check + * @returns {Promise} True if command exists, false otherwise + */ +async function commandExists(command) { + try { + await require("command-exists")(command); + return true; + } catch (e) { + return false; + } +} +module.exports.commandExists = commandExists; + +/** + * Log the server's listening URLs, similar to Vite's dev server output. + * When no hostname is specified (bound to all interfaces), it prints + * localhost plus every non-internal network address. + * @param {string} tag Log tag (e.g. "server", "setup-database") + * @param {number} port Port number + * @param {string} hostname Bound hostname, if any + * @param {boolean} isHTTPS Whether the server is using HTTPS + * @returns {void} + */ +module.exports.printServerUrls = (tag, port, hostname, isHTTPS = false) => { + try { + // If hostname is specified, just print that one. + if (hostname) { + log.info(tag, `Listening on: `, createURL(isHTTPS, hostname, port)); + return; + } + + // Since no hostname is specified, which means the server is bound to all interfaces, we need to print all possible URLs. + const nets = networkInterfaces(); + + log.info(tag, "Listening on:"); + log.info(tag, `- `, createURL(isHTTPS, "localhost", port)); + + // Prepare a list of valid address + const addressList = []; + for (const iface of Object.values(nets)) { + for (const addr of iface) { + if (!addr.internal) { + addressList.push(addr); + } + } + } + + // Sort IPv4 addresses first + addressList.sort((a, b) => { + if (a.family === "IPv4" && b.family === "IPv6") { + return -1; + } else if (a.family === "IPv6" && b.family === "IPv4") { + return 1; + } else { + return a.address.localeCompare(b.address); + } + }); + + for (const address of addressList) { + if (!address.internal) { + const host = address.family === "IPv6" ? `[${address.address}]` : address.address; + log.info(tag, `- `, createURL(isHTTPS, host, port)); + } + } + } catch (e) { + log.error(tag, "Error printing server URLs: " + e.message); + } +}; + +/** + * Construct a URL a bit more safely + * @param {boolean} isHTTPS Whether the URL should use HTTPS protocol + * @param {string} hostname The hostname to use in the URL + * @param {number} port The port + * @returns {string} The constructed URL as a string + */ +function createURL(isHTTPS, hostname, port = 80) { + const url = new URL((isHTTPS ? "https" : "http") + `://` + hostname); + url.port = String(port); + + // Prefer origin if available, it doesn't contain the trailing slash + return url.origin || url.toString(); +} diff --git a/server/util/pm2.js b/server/util/pm2.js new file mode 100644 index 0000000..e3aebe2 --- /dev/null +++ b/server/util/pm2.js @@ -0,0 +1,74 @@ +const { execFile } = require("child_process"); +const process = require("process"); + +const PM2_EXEC_OPTIONS = { + timeout: 5000, + maxBuffer: 10 * 1024 * 1024, +}; + +/** + * Truncate command output to keep error messages compact. + * @param {string | Buffer} output Command output. + * @returns {string} The truncated output text. + */ +function truncateOutput(output) { + const text = (output || "").toString().trim(); + if (text.length > 200) { + return text.substring(0, 200) + "..."; + } + return text; +} + +/** + * Query PM2 for the current process list. + * @returns {Promise<{id: string, name: string, status: string}[]>} The normalized PM2 process list. + */ +function getPM2ProcessList() { + return new Promise((resolve, reject) => { + execFile( + process.platform === "win32" ? "pm2.cmd" : "pm2", + ["jlist"], + PM2_EXEC_OPTIONS, + (error, stdout, stderr) => { + if (error) { + const details = truncateOutput(stderr) || error.code || error.message; + reject(new Error(`Unable to query PM2 process list (${details}).`)); + return; + } + + try { + const parsed = JSON.parse((stdout || "").toString()); + if (!Array.isArray(parsed)) { + reject(new Error("Unexpected PM2 process list output.")); + return; + } + + resolve( + parsed + .map((item) => { + const id = item?.pm_id != null ? String(item.pm_id) : null; + const name = item?.name || null; + + if (!id && !name) { + return null; + } + + return { + id: id || name, + name: name || id, + status: item?.pm2_env?.status?.toString().toLowerCase() || "unknown", + }; + }) + .filter(Boolean) + ); + } catch (parseError) { + reject(new Error(truncateOutput(stderr) || "Unable to parse PM2 process list output.")); + } + } + ); + }); +} + +module.exports = { + getPM2ProcessList, +}; diff --git a/server/util2.ts b/server/util2.ts new file mode 100644 index 0000000..8d06d86 --- /dev/null +++ b/server/util2.ts @@ -0,0 +1,29 @@ +import { ZodError } from "zod"; +import { Response } from "express"; + +/** + * @param res Express response + * @param e Any error + */ +export function generalErrorResponse(res: Response, e: unknown) { + if (e instanceof ZodError) { + let message = ""; + for (const issue of e.issues) { + message += `${issue.path.join(".")}: ${issue.message}\n`; + } + res.status(400).json({ + ok: false, + msg: message, + }); + } else if (e instanceof Error) { + res.status(400).json({ + ok: false, + msg: e.message, + }); + } else { + res.status(400).json({ + ok: false, + msg: "Unknown error", + }); + } +} diff --git a/server/utils/array-with-key.js b/server/utils/array-with-key.js new file mode 100644 index 0000000..1adbd9f --- /dev/null +++ b/server/utils/array-with-key.js @@ -0,0 +1,85 @@ +/** + * An object that can be used as an array with a key + * Like PHP's array + * @template K + * @template V + */ +class ArrayWithKey { + /** + * All keys that are stored in the current object + * @type {K[]} + * @private + */ + __stack = []; + + /** + * Push an element to the end of the array + * @param {K} key The key of the element + * @param {V} value The value of the element + * @returns {void} + */ + push(key, value) { + this[key] = value; + this.__stack.push(key); + } + + /** + * Get the last element and remove it from the array + * @returns {V|undefined} The first value, or undefined if there is no element to pop + */ + pop() { + let key = this.__stack.pop(); + let prop = this[key]; + delete this[key]; + return prop; + } + + /** + * Get the last key + * @returns {K|null} The last key, or null if the array is empty + */ + getLastKey() { + if (this.__stack.length === 0) { + return null; + } + return this.__stack[this.__stack.length - 1]; + } + + /** + * Get the first element + * @returns {{key:K,value:V}|null} The first element, or null if the array is empty + */ + shift() { + let key = this.__stack.shift(); + let value = this[key]; + delete this[key]; + return { + key, + value, + }; + } + + /** + * Get the length of the array + * @returns {number} Amount of elements stored + */ + length() { + return this.__stack.length; + } + + /** + * Get the last value + * @returns {V|null} The last element without removing it, or null if the array is empty + */ + last() { + let key = this.getLastKey(); + if (key === null) { + return null; + } + return this[key]; + } +} + +module.exports = { + ArrayWithKey, +}; diff --git a/server/utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler.js b/server/utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler.js new file mode 100644 index 0000000..d05a6bc --- /dev/null +++ b/server/utils/knex/lib/dialects/mysql2/schema/mysql2-columncompiler.js @@ -0,0 +1,22 @@ +const ColumnCompilerMySQL = require("knex/lib/dialects/mysql/schema/mysql-columncompiler"); +const { formatDefault } = require("knex/lib/formatter/formatterUtils"); +const { log } = require("../../../../../../../src/util"); + +class KumaColumnCompiler extends ColumnCompilerMySQL { + /** + * Override defaultTo method to handle default value for TEXT fields + * @param {any} value Value + * @returns {string|void} Default value (Don't understand why it can return void or string, but it's the original code, lol) + */ + defaultTo(value) { + if (this.type === "text" && typeof value === "string") { + log.debug("defaultTo", `${this.args[0]}: ${this.type} ${value} ${typeof value}`); + // MySQL 8.0 is required and only if the value is written as an expression: https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html + // MariaDB 10.2 is required: https://mariadb.com/kb/en/text/ + return `default (${formatDefault(value, this.type, this.client)})`; + } + return super.defaultTo.apply(this, arguments); + } +} + +module.exports = KumaColumnCompiler; diff --git a/server/utils/limit-queue.js b/server/utils/limit-queue.js new file mode 100644 index 0000000..f9b1dc9 --- /dev/null +++ b/server/utils/limit-queue.js @@ -0,0 +1,46 @@ +const { ArrayWithKey } = require("./array-with-key"); + +/** + * Limit Queue + * The first element will be removed when the length exceeds the limit + */ +class LimitQueue extends ArrayWithKey { + /** + * The limit of the queue after which the first element will be removed + * @private + * @type {number} + */ + __limit; + /** + * The callback function when the queue exceeds the limit + * @private + * @callback onExceedCallback + * @param {{key:K,value:V}|nul} item + */ + __onExceed = null; + + /** + * @param {number} limit The limit of the queue after which the first element will be removed + */ + constructor(limit) { + super(); + this.__limit = limit; + } + + /** + * @inheritDoc + */ + push(key, value) { + super.push(key, value); + if (this.length() > this.__limit) { + let item = this.shift(); + if (this.__onExceed) { + this.__onExceed(item); + } + } + } +} + +module.exports = { + LimitQueue, +}; diff --git a/server/utils/simple-migration-server.js b/server/utils/simple-migration-server.js new file mode 100644 index 0000000..5245840 --- /dev/null +++ b/server/utils/simple-migration-server.js @@ -0,0 +1,95 @@ +const express = require("express"); +const http = require("node:http"); +const { printServerUrls } = require("../util-server"); + +/** + * SimpleMigrationServer + * For displaying the migration status of the server + * Also, it is used to let Docker healthcheck know the status of the server, as the main server is not started yet, healthcheck will think the server is down incorrectly. + */ +class SimpleMigrationServer { + /** + * Express app instance + * @type {?Express} + */ + app; + + /** + * Server instance + * @type {?Server} + */ + server; + + /** + * Response object + * @type {?Response} + */ + response; + + /** + * Start the server + * @param {number} port Port + * @param {string} hostname Hostname + * @returns {Promise} + */ + start(port, hostname) { + this.app = express(); + this.server = http.createServer(this.app); + + this.app.get("/", (req, res) => { + res.set("Content-Type", "text/html"); + + // Don't use meta tag redirect, it may cause issues in Chrome (#6223) + res.end(` + + Uptime Kuma Migration + + Migration is in progress, it may take some time. You can check the progress in the console, or + click here to check. + + + `); + }); + + this.app.get("/migrate-status", (req, res) => { + res.set("Content-Type", "text/plain"); + res.write("Migration is in progress, listening message...\n"); + if (this.response) { + this.response.write("Disconnected\n"); + this.response.end(); + } + this.response = res; + // never ending response + }); + + return new Promise((resolve) => { + this.server.listen(port, hostname, () => { + printServerUrls("migration", port, hostname); + resolve(); + }); + }); + } + + /** + * Update the message + * @param {string} msg Message to update + * @returns {void} + */ + update(msg) { + this.response?.write(msg + "\n"); + } + + /** + * Stop the server + * @returns {Promise} + */ + async stop() { + this.response?.write("Finished, please refresh this page.\n"); + this.response?.end(); + await this.server?.close(); + } +} + +module.exports = { + SimpleMigrationServer, +}; diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..a7bb69b --- /dev/null +++ b/src/App.vue @@ -0,0 +1,14 @@ + + + diff --git a/src/assets/app.scss b/src/assets/app.scss new file mode 100644 index 0000000..37fc450 --- /dev/null +++ b/src/assets/app.scss @@ -0,0 +1,676 @@ +@import "vars.scss"; +@import "multiselect.scss"; +@import "node_modules/bootstrap/scss/bootstrap"; + +#app { + font-family: + "Twemoji Country Flags", + BlinkMacSystemFont, + segoe ui, + Roboto, + helvetica neue, + Arial, + noto sans, + sans-serif, + apple color emoji, + segoe ui emoji, + segoe ui symbol, + noto color emoji; +} + +h1 { + font-size: 32px; +} + +h2 { + font-size: 26px; +} + +textarea.form-control { + border-radius: 19px; +} + +::-webkit-scrollbar { + width: 10px; +} + +.bg-maintenance { + color: white !important; + background-color: $maintenance !important; +} + +.bg-dark { + color: white; +} + +.text-maintenance { + color: $maintenance !important; +} + +.incident a, +.bg-maintenance a { + color: inherit; +} + +.list-group { + border-radius: 0.75rem; + + .dark & { + .list-group-item { + background-color: $dark-bg; + color: $dark-font-color; + border-color: $dark-border-color; + } + } +} + +// optgroup +optgroup { + color: #b1b1b1; + option { + color: #212529; + } +} + +.dark { + optgroup { + color: #535864; + option { + color: $dark-font-color; + } + } +} + +// Scrollbar +::-webkit-scrollbar-thumb { + background: #ccc; + border-radius: 20px; +} + +.modal { + backdrop-filter: blur(3px); +} + +.modal-content { + border-radius: 1rem; + box-shadow: 0 15px 70px rgba(0, 0, 0, 0.1); + + .dark & { + box-shadow: 0 15px 70px rgb(0 0 0); + background-color: $dark-bg; + } +} + +.VuePagination__count { + font-size: 13px; + text-align: center; +} + +.shadow-box { + //overflow: hidden; // Forget why add this, but multiple select hide by this + box-shadow: 0 15px 70px rgba(0, 0, 0, 0.1); + padding: 10px; + border-radius: 10px; + + &.big-padding { + padding: 20px; + } +} + +.btn { + padding-left: 20px; + padding-right: 20px; +} + +.btn-sm { + border-radius: 25px; +} + +.btn-primary { + color: white; + + &:hover, + &:active, + &:focus, + &.active { + color: white; + background-color: $highlight; + border-color: $highlight; + } + + .dark & { + color: $dark-font-color2; + } +} + +.btn-normal { + $bg-color: #f5f5f5; + + background-color: $bg-color; + border-color: $bg-color; + + &:hover { + $hover-color: darken($bg-color, 3%); + background-color: $hover-color; + border-color: $hover-color; + } +} + +.btn-warning { + color: white; + + &:hover, + &:active, + &:focus, + &.active { + color: white; + } +} + +.btn-info { + color: white; + + &:hover, + &:active, + &:focus, + &.active { + color: white; + } +} + +.btn-dark { + background-color: #161b22; +} + +.btn-outline-normal { + padding: 4px 10px; + border: 1px solid #ced4da; + border-radius: 25px; + background-color: transparent; + + .dark & { + color: $dark-font-color; + border: 1px solid $dark-font-color2; + } + + &.active { + background-color: $highlight-white; + + .dark & { + background-color: $dark-font-color2; + } + } +} + +@media (max-width: 550px) { + .table-shadow-box { + padding: 10px !important; + + thead { + display: none; + } + + tbody { + .shadow-box { + background-color: white; + } + } + + tr { + margin-top: 0 !important; + padding: 4px 10px !important; + display: block; + margin-bottom: 6px; + + td:first-child { + font-weight: bold; + } + + td:nth-child(-n + 3) { + text-align: center; + } + + td:last-child { + text-align: left; + } + + td { + border-bottom: 1px solid $dark-font-color; + display: block; + padding: 4px; + + .badge { + margin: auto; + display: block; + width: 30%; + } + } + } + } +} + +// Dark Theme override here +.dark { + background-color: #090c10; + color: $dark-font-color; + + mark, + .mark { + background-color: #b6ad86; + } + + &::-webkit-scrollbar-thumb, + ::-webkit-scrollbar-thumb { + background: $dark-border-color; + } + + .shadow-box { + &:not(.alert) { + background-color: $dark-bg; + } + } + + .form-check-input { + background-color: $dark-bg2; + border-color: $dark-border-color; + } + + .input-group-text { + background-color: #282f39; + border-color: $dark-border-color; + color: $dark-font-color; + } + + .form-check-input:checked { + border-color: $primary; // Re-apply bootstrap border + } + + .form-switch .form-check-input { + background-color: #232f3b; + } + + a:not(.btn), + .table, + .nav-link { + color: $dark-font-color; + + &.btn-info { + color: white; + } + } + + .incident a, + .bg-maintenance a { + color: inherit; + } + + .form-control, + .form-control:focus, + .form-select, + .form-select:focus { + color: $dark-font-color; + background-color: $dark-bg2; + } + + .form-select:disabled { + color: rgba($dark-font-color, 0.7); + background-color: $dark-bg; + } + + .form-control, + .form-select { + border-color: $dark-border-color; + } + + .form-control:disabled, + .form-control[readonly] { + background-color: #232f3b; + opacity: 1; + } + + .table-hover > tbody > tr:hover > * { + --bs-table-accent-bg: #070a10; + color: $dark-font-color; + } + + .nav-pills .nav-link.active, + .nav-pills .show > .nav-link { + color: $dark-font-color2; + } + + .bg-primary { + color: $dark-font-color2; + } + + .btn-secondary { + color: white; + } + + .btn-normal { + $bg-color: $dark-header-bg; + + color: $dark-font-color; + background-color: $bg-color; + border-color: $bg-color; + + &:hover { + $hover-color: darken($bg-color, 3%); + background-color: $hover-color; + border-color: $hover-color; + } + } + + .btn-warning { + color: $dark-font-color2; + + &:hover, + &:active, + &:focus, + &.active { + color: $dark-font-color2; + } + } + + .btn-close { + box-shadow: none; + filter: invert(1); + + &:hover { + opacity: 0.6; + } + } + + .modal-header { + border-color: $dark-bg; + } + + .modal-footer { + border-color: $dark-bg; + } + + // Pagination + .page-item.disabled .page-link { + background-color: $dark-bg; + border-color: $dark-border-color; + } + + .page-link { + background-color: $dark-bg; + border-color: $dark-border-color; + color: $dark-font-color; + } + + .monitor-list { + .item { + &:hover { + background-color: $dark-bg2; + } + + &.active { + background-color: $dark-bg2; + } + } + } + + @media (max-width: 550px) { + .table-shadow-box { + tbody { + .shadow-box { + background-color: $dark-bg2; + + td { + border-bottom: 1px solid $dark-border-color; + } + } + } + } + } + + .alert { + &.bg-info, + &.bg-warning, + &.bg-danger, + &.bg-maintenance, + &.bg-light { + color: $dark-font-color2; + } + } +} + +/* + * Transitions + */ + +// page-change +.slide-fade-enter-active { + transition: all 0.2s $easing-in; +} + +.slide-fade-leave-active { + transition: all 0.2s $easing-in; +} + +.slide-fade-enter-from, +.slide-fade-leave-to { + transform: translateY(50px); + opacity: 0; +} + +.slide-fade-right-enter-active { + transition: all 0.2s $easing-in; +} + +.slide-fade-right-leave-active { + transition: all 0.2s $easing-in; +} + +.slide-fade-right-enter-from, +.slide-fade-right-leave-to { + transform: translateX(50px); + opacity: 0; +} + +.slide-fade-up-enter-active { + transition: all 0.2s $easing-in; +} + +.slide-fade-up-leave-active { + transition: all 0.2s $easing-in; +} + +.slide-fade-up-enter-from, +.slide-fade-up-leave-to { + transform: translateY(-50px); + opacity: 0; +} + +.monitor-list { + &.scrollbar { + overflow-y: auto; + } + + @media (max-width: 770px) { + &.scrollbar { + height: calc(100% - 97px); + } + } + + .item { + display: block; + text-decoration: none; + padding: 13px 15px 10px 15px; + border-radius: 10px; + transition: all ease-in-out 0.15s; + + &.disabled { + opacity: 0.3; + } + + .info { + white-space: nowrap; + overflow: hidden; + } + + &:hover { + background-color: $highlight-white; + } + + &.active { + background-color: #cdf8f4; + } + .tags { + // Removes margin to line up tags list with uptime percentage + margin-left: -0.25rem; + } + } +} + +.alert-success { + color: #122f21; + background-color: $primary; + border-color: $primary; +} + +.alert-info { + color: #055160; + background-color: #cff4fc; + border-color: #cff4fc; +} + +.alert-danger { + color: #842029; + background-color: #f8d7da; + border-color: #f8d7da; +} + +.btn-success { + color: #fff; + background-color: #4caf50; + border-color: #4caf50; +} + +[contenteditable="true"] { + transition: all $easing-in 0.2s; + background-color: rgba(239, 239, 239, 0.7); + border-radius: 8px; + + &.no-bg { + background-color: transparent !important; + } + + &:focus { + outline: 0 solid #eee; + background-color: rgba(245, 245, 245, 0.9); + } + + &:hover { + background-color: rgba(239, 239, 239, 0.8); + } + + .dark & { + background-color: rgba(239, 239, 239, 0.2); + } + + /* + &::after { + margin-left: 5px; + content: "🖊️"; + font-size: 13px; + color: #eee; + } + */ +} + +.action { + transition: all $easing-in 0.2s; + + &:hover { + cursor: pointer; + transform: scale(1.2); + } +} + +.vue-image-crop-upload .vicp-wrap { + border-radius: 10px !important; +} + +.spinner { + color: $primary; +} + +.prism-editor__textarea { + outline: none !important; +} + +.prism-editor__container { + .important { + font-weight: var(--bs-body-font-weight) !important; + } +} + +h5.settings-subheading::after { + content: ""; + display: block; + width: 50%; + padding-top: 8px; + border-bottom: 1px solid $dark-border-color; +} + +/* required class */ +.code-editor, +.css-editor { + /* we dont use `language-` classes anymore so that's why we need to add background and text color manually */ + + border-radius: 1rem; + padding: 10px 5px; + border: 1px solid #ced4da; + + .dark & { + background: $dark-bg2; + border: 1px solid $dark-border-color; + } +} + +$shadow-box-padding: 20px; + +.shadow-box-with-fixed-bottom-bar { + padding-top: $shadow-box-padding; + padding-bottom: 0; + padding-right: $shadow-box-padding; + padding-left: $shadow-box-padding; +} + +.fixed-bottom-bar { + position: sticky; + bottom: 0; + margin-left: -$shadow-box-padding; + margin-right: -$shadow-box-padding; + z-index: 10; + background-color: rgba(white, 0.2); + backdrop-filter: blur(2px); + border-radius: 0 0 10px 10px; + + .dark & { + background-color: rgba($dark-header-bg, 0.9); + } +} + +@media (max-width: 770px) { + .toast-container { + margin-bottom: 100px !important; + } +} + +@media (max-width: 550px) { + .toast-container { + margin-bottom: 126px !important; + } +} + +.zoom-cursor { + cursor: zoom-in; +} + +// Localization + +@import "localization.scss"; diff --git a/src/assets/localization.scss b/src/assets/localization.scss new file mode 100644 index 0000000..bbb75fc --- /dev/null +++ b/src/assets/localization.scss @@ -0,0 +1,28 @@ +html[lang="fa"] { + #app { + font-family: + "Vazirmatn", + "IRANSans", + "Iranian Sans", + "B Nazanin", + "Tahoma", + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + segoe ui, + Roboto, + helvetica neue, + Arial, + noto sans, + sans-serif, + apple color emoji, + segoe ui emoji, + segoe ui symbol, + noto color emoji; + } +} + +ul.multiselect__content { + padding-left: 0 !important; +} diff --git a/src/assets/multiselect.scss b/src/assets/multiselect.scss new file mode 100644 index 0000000..bb24db7 --- /dev/null +++ b/src/assets/multiselect.scss @@ -0,0 +1,82 @@ +@import "vars.scss"; +@import "node_modules/vue-multiselect/dist/vue-multiselect"; + +.multiselect { + .dark & { + color: $dark-font-color; + } +} + +.multiselect__tags { + border-radius: 1.5rem; + border: 1px solid #ced4da; + min-height: 38px; + padding: 6px 40px 0 8px; +} + +.multiselect--active .multiselect__tags { + border-radius: 1rem; +} + +.multiselect__option--highlight { + background: $primary !important; + color: $dark-font-color2 !important; +} + +.multiselect__option--highlight::after { + background: $primary !important; + color: $dark-font-color2 !important; +} + +.multiselect__tag { + border-radius: $border-radius; + margin-bottom: 0; + padding: 6px 26px 6px 10px; + background: $primary !important; +} + +.multiselect__placeholder { + font-size: 1rem; + padding-left: 6px; + padding-top: 0; + padding-bottom: 0; + margin-bottom: 0; + opacity: 0.67; +} + +.multiselect__input, +.multiselect__single { + line-height: 14px; + margin-bottom: 0; +} + +.dark { + .multiselect__tag { + color: $dark-font-color2; + } + + .multiselect__tags { + background-color: $dark-bg2; + border-color: $dark-border-color; + } + + .multiselect__input, + .multiselect__single { + background-color: $dark-bg2; + color: $dark-font-color; + } + + .multiselect__content-wrapper { + background-color: $dark-bg2; + border-color: $dark-border-color; + z-index: 150; + } + + .multiselect--above .multiselect__content-wrapper { + border-color: $dark-border-color; + } + + .multiselect__option--selected { + background-color: $dark-bg; + } +} diff --git a/src/assets/vars.scss b/src/assets/vars.scss new file mode 100644 index 0000000..7250de1 --- /dev/null +++ b/src/assets/vars.scss @@ -0,0 +1,23 @@ +$primary: #5cdd8b; +$danger: #dc3545; +$warning: #f8a306; +$maintenance: #1747f5; +$link-color: #111; +$border-radius: 50rem; +$secondary-text: #aaa; + +$highlight: #7ce8a4; +$highlight-white: #e7faec; + +$dark-font-color: #b1b8c0; +$dark-font-color2: #020b05; +$dark-bg: #0d1117; +$dark-bg2: #070a10; +$dark-border-color: #1d2634; +$dark-header-bg: #161b22; + +$easing-in: cubic-bezier(0.54, 0.78, 0.55, 0.97); +$easing-out: cubic-bezier(0.25, 0.46, 0.45, 0.94); +$easing-in-out: cubic-bezier(0.79, 0.14, 0.15, 0.86); + +$dropdown-border-radius: 0.5rem; diff --git a/src/assets/vue-datepicker.scss b/src/assets/vue-datepicker.scss new file mode 100644 index 0000000..dedbc08 --- /dev/null +++ b/src/assets/vue-datepicker.scss @@ -0,0 +1,39 @@ +@import "@vuepic/vue-datepicker/dist/main.css"; +@import "vars.scss"; + +// Must use #{ } +// Remark: https://stackoverflow.com/questions/50202991/unable-to-set-scss-variable-to-css-variable +.dp__theme_dark { + --dp-background-color: #{$dark-bg2}; + --dp-text-color: #{$dark-font-color}; + --dp-hover-color: #484848; + --dp-hover-text-color: #ffffff; + --dp-hover-icon-color: #959595; + --dp-primary-color: #{#5cdd8b}; + --dp-primary-text-color: #ffffff; + --dp-secondary-color: #494949; + --dp-border-color: #{$dark-border-color}; + --dp-menu-border-color: #2d2d2d; + --dp-border-color-hover: #{$dark-border-color}; + --dp-disabled-color: #212121; + --dp-scroll-bar-background: #212121; + --dp-scroll-bar-color: #484848; + --dp-success-color: #{$primary}; + --dp-success-color-disabled: #428f59; + --dp-icon-color: #959595; + --dp-danger-color: #e53935; + --dp-highlight-color: rgba(0, 92, 178, 0.2); +} + +.dp__input { + border-radius: $border-radius; +} + +// Fix: Full width of text input when using "inline textInput inlineWithInput" mode +.dp__main > div[aria-label="Datepicker input"] { + width: 100%; +} + +.dp__main > div[aria-label="Datepicker menu"]:nth-child(2) { + margin-top: 20px; +} diff --git a/src/auth-client.ts b/src/auth-client.ts new file mode 100644 index 0000000..5fdad79 --- /dev/null +++ b/src/auth-client.ts @@ -0,0 +1,70 @@ +import { createAuthClient } from "better-auth/vue"; +import { twoFactorClient, usernameClient } from "better-auth/client/plugins"; +import { apiKeyClient } from "@better-auth/api-key/client"; +import { reconnectSocket } from "./mixins/socket"; + +export const baseURL = + process.env.NODE_ENV === "development" || localStorage.dev === "dev" + ? location.protocol + "//" + location.hostname + ":3001" + : ""; + +export const authClient = createAuthClient({ + baseURL, + plugins: [usernameClient(), twoFactorClient(), apiKeyClient()], +}); +/** + * @returns Check if the user is logged in + */ +export async function isLoggedIn() { + const session = await authClient.getSession(); + return session.data !== null; +} + +/** + * @param username Username + * @param password Password + * @param remember Remember Me + */ +export async function login(username: string, password: string, remember: boolean = true) { + const { data, error } = await authClient.signIn.username({ + username, + password, + rememberMe: remember, + }); + + if (error) { + throw new Error(error.message); + } + + if ("twoFactorRedirect" in data) { + return "twoFactorRequired"; + } + + reconnectSocket(); +} + +/** + * @param token Token + */ +export async function verifyTotp(token: string) { + const { error } = await authClient.twoFactor.verifyTotp({ + code: token, + }); + + if (error) { + throw new Error(error.message); + } + + reconnectSocket(); +} + +/** + * @param onSuccess + */ +export async function logout(onSuccess = () => {}) { + await authClient.signOut({ + fetchOptions: { + onSuccess, + }, + }); +} diff --git a/src/components/APIKeyDialog.vue b/src/components/APIKeyDialog.vue new file mode 100644 index 0000000..ec56c68 --- /dev/null +++ b/src/components/APIKeyDialog.vue @@ -0,0 +1,228 @@ + + + + + diff --git a/src/components/ActionInput.vue b/src/components/ActionInput.vue new file mode 100644 index 0000000..479184d --- /dev/null +++ b/src/components/ActionInput.vue @@ -0,0 +1,93 @@ + + + diff --git a/src/components/ActionSelect.vue b/src/components/ActionSelect.vue new file mode 100644 index 0000000..87dc13f --- /dev/null +++ b/src/components/ActionSelect.vue @@ -0,0 +1,108 @@ + + + diff --git a/src/components/BadgeLinkGeneratorDialog.vue b/src/components/BadgeLinkGeneratorDialog.vue new file mode 100644 index 0000000..066d920 --- /dev/null +++ b/src/components/BadgeLinkGeneratorDialog.vue @@ -0,0 +1,356 @@ + + + + + diff --git a/src/components/CertificateInfo.vue b/src/components/CertificateInfo.vue new file mode 100644 index 0000000..8c96456 --- /dev/null +++ b/src/components/CertificateInfo.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/src/components/CertificateInfoRow.vue b/src/components/CertificateInfoRow.vue new file mode 100644 index 0000000..be6a816 --- /dev/null +++ b/src/components/CertificateInfoRow.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/src/components/Confirm.vue b/src/components/Confirm.vue new file mode 100644 index 0000000..ced6082 --- /dev/null +++ b/src/components/Confirm.vue @@ -0,0 +1,84 @@ + + + diff --git a/src/components/CopyableInput.vue b/src/components/CopyableInput.vue new file mode 100644 index 0000000..6ded7f7 --- /dev/null +++ b/src/components/CopyableInput.vue @@ -0,0 +1,137 @@ + + + diff --git a/src/components/CountUp.vue b/src/components/CountUp.vue new file mode 100644 index 0000000..6723589 --- /dev/null +++ b/src/components/CountUp.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/components/CreateGroupDialog.vue b/src/components/CreateGroupDialog.vue new file mode 100644 index 0000000..6209120 --- /dev/null +++ b/src/components/CreateGroupDialog.vue @@ -0,0 +1,85 @@ + + + diff --git a/src/components/Datetime.vue b/src/components/Datetime.vue new file mode 100644 index 0000000..0d4e182 --- /dev/null +++ b/src/components/Datetime.vue @@ -0,0 +1,30 @@ + + + diff --git a/src/components/DockerHostDialog.vue b/src/components/DockerHostDialog.vue new file mode 100644 index 0000000..09a0ed9 --- /dev/null +++ b/src/components/DockerHostDialog.vue @@ -0,0 +1,221 @@ + + + + + diff --git a/src/components/EditMonitorCondition.vue b/src/components/EditMonitorCondition.vue new file mode 100644 index 0000000..c459099 --- /dev/null +++ b/src/components/EditMonitorCondition.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/src/components/EditMonitorConditionGroup.vue b/src/components/EditMonitorConditionGroup.vue new file mode 100644 index 0000000..03d3278 --- /dev/null +++ b/src/components/EditMonitorConditionGroup.vue @@ -0,0 +1,205 @@ + + + + + diff --git a/src/components/EditMonitorConditions.vue b/src/components/EditMonitorConditions.vue new file mode 100644 index 0000000..df886d5 --- /dev/null +++ b/src/components/EditMonitorConditions.vue @@ -0,0 +1,153 @@ + + + + + diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue new file mode 100644 index 0000000..8774a4f --- /dev/null +++ b/src/components/HeartbeatBar.vue @@ -0,0 +1,854 @@ + + + + + diff --git a/src/components/HiddenInput.vue b/src/components/HiddenInput.vue new file mode 100644 index 0000000..5c7b463 --- /dev/null +++ b/src/components/HiddenInput.vue @@ -0,0 +1,91 @@ + + + diff --git a/src/components/IncidentEditForm.vue b/src/components/IncidentEditForm.vue new file mode 100644 index 0000000..08597df --- /dev/null +++ b/src/components/IncidentEditForm.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/src/components/IncidentHistory.vue b/src/components/IncidentHistory.vue new file mode 100644 index 0000000..d756b7f --- /dev/null +++ b/src/components/IncidentHistory.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/src/components/IncidentManageModal.vue b/src/components/IncidentManageModal.vue new file mode 100644 index 0000000..78a4262 --- /dev/null +++ b/src/components/IncidentManageModal.vue @@ -0,0 +1,204 @@ + + + + + diff --git a/src/components/Login.vue b/src/components/Login.vue new file mode 100644 index 0000000..c6ad217 --- /dev/null +++ b/src/components/Login.vue @@ -0,0 +1,162 @@ + + + + + diff --git a/src/components/MaintenanceTime.vue b/src/components/MaintenanceTime.vue new file mode 100644 index 0000000..e244906 --- /dev/null +++ b/src/components/MaintenanceTime.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/src/components/MonitorList.vue b/src/components/MonitorList.vue new file mode 100644 index 0000000..b85728f --- /dev/null +++ b/src/components/MonitorList.vue @@ -0,0 +1,837 @@ + + + + + diff --git a/src/components/MonitorListFilter.vue b/src/components/MonitorListFilter.vue new file mode 100644 index 0000000..1b27e8f --- /dev/null +++ b/src/components/MonitorListFilter.vue @@ -0,0 +1,378 @@ + + + + + diff --git a/src/components/MonitorListFilterDropdown.vue b/src/components/MonitorListFilterDropdown.vue new file mode 100644 index 0000000..bd27aca --- /dev/null +++ b/src/components/MonitorListFilterDropdown.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/src/components/MonitorListItem.vue b/src/components/MonitorListItem.vue new file mode 100644 index 0000000..f5cdcb9 --- /dev/null +++ b/src/components/MonitorListItem.vue @@ -0,0 +1,404 @@ + + + + + diff --git a/src/components/MonitorSettingDialog.vue b/src/components/MonitorSettingDialog.vue new file mode 100644 index 0000000..24bd812 --- /dev/null +++ b/src/components/MonitorSettingDialog.vue @@ -0,0 +1,174 @@ + + + + + diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue new file mode 100644 index 0000000..aa615b6 --- /dev/null +++ b/src/components/NotificationDialog.vue @@ -0,0 +1,550 @@ + + + + + diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue new file mode 100644 index 0000000..46fd30a --- /dev/null +++ b/src/components/PingChart.vue @@ -0,0 +1,665 @@ + + + + + diff --git a/src/components/ProxyDialog.vue b/src/components/ProxyDialog.vue new file mode 100644 index 0000000..ab0a0ed --- /dev/null +++ b/src/components/ProxyDialog.vue @@ -0,0 +1,325 @@ + + + + + diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue new file mode 100644 index 0000000..6701217 --- /dev/null +++ b/src/components/PublicGroupList.vue @@ -0,0 +1,412 @@ + + + + + diff --git a/src/components/RemoteBrowserDialog.vue b/src/components/RemoteBrowserDialog.vue new file mode 100644 index 0000000..988dc32 --- /dev/null +++ b/src/components/RemoteBrowserDialog.vue @@ -0,0 +1,204 @@ + + + + + diff --git a/src/components/ScreenshotDialog.vue b/src/components/ScreenshotDialog.vue new file mode 100644 index 0000000..c9999c0 --- /dev/null +++ b/src/components/ScreenshotDialog.vue @@ -0,0 +1,53 @@ + + + + + diff --git a/src/components/Status.vue b/src/components/Status.vue new file mode 100644 index 0000000..4941a04 --- /dev/null +++ b/src/components/Status.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/components/Tag.vue b/src/components/Tag.vue new file mode 100644 index 0000000..3bc89b4 --- /dev/null +++ b/src/components/Tag.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/src/components/TagEditDialog.vue b/src/components/TagEditDialog.vue new file mode 100644 index 0000000..95fc091 --- /dev/null +++ b/src/components/TagEditDialog.vue @@ -0,0 +1,514 @@ + + + + + diff --git a/src/components/TagsManager.vue b/src/components/TagsManager.vue new file mode 100644 index 0000000..1b6d17c --- /dev/null +++ b/src/components/TagsManager.vue @@ -0,0 +1,702 @@ + + + + + diff --git a/src/components/TemplatedInput.vue b/src/components/TemplatedInput.vue new file mode 100644 index 0000000..1591529 --- /dev/null +++ b/src/components/TemplatedInput.vue @@ -0,0 +1,89 @@ + + + diff --git a/src/components/TemplatedTextarea.vue b/src/components/TemplatedTextarea.vue new file mode 100644 index 0000000..d304cc1 --- /dev/null +++ b/src/components/TemplatedTextarea.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/src/components/ToggleSection.vue b/src/components/ToggleSection.vue new file mode 100644 index 0000000..a1b47e9 --- /dev/null +++ b/src/components/ToggleSection.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/src/components/Tooltip.vue b/src/components/Tooltip.vue new file mode 100644 index 0000000..febc488 --- /dev/null +++ b/src/components/Tooltip.vue @@ -0,0 +1,276 @@ + + + + + diff --git a/src/components/TwoFADialog.vue b/src/components/TwoFADialog.vue new file mode 100644 index 0000000..c1a96ed --- /dev/null +++ b/src/components/TwoFADialog.vue @@ -0,0 +1,268 @@ + + + + + diff --git a/src/components/Uptime.vue b/src/components/Uptime.vue new file mode 100644 index 0000000..7048fa2 --- /dev/null +++ b/src/components/Uptime.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/src/components/notifications/360messenger.vue b/src/components/notifications/360messenger.vue new file mode 100644 index 0000000..74c5e91 --- /dev/null +++ b/src/components/notifications/360messenger.vue @@ -0,0 +1,300 @@ + + + + + diff --git a/src/components/notifications/46elks.vue b/src/components/notifications/46elks.vue new file mode 100644 index 0000000..30642fb --- /dev/null +++ b/src/components/notifications/46elks.vue @@ -0,0 +1,75 @@ + + + diff --git a/src/components/notifications/AlertNow.vue b/src/components/notifications/AlertNow.vue new file mode 100644 index 0000000..dab8543 --- /dev/null +++ b/src/components/notifications/AlertNow.vue @@ -0,0 +1,28 @@ + diff --git a/src/components/notifications/Alerta.vue b/src/components/notifications/Alerta.vue new file mode 100644 index 0000000..5dc059b --- /dev/null +++ b/src/components/notifications/Alerta.vue @@ -0,0 +1,46 @@ + diff --git a/src/components/notifications/AliyunSms.vue b/src/components/notifications/AliyunSms.vue new file mode 100644 index 0000000..a031fdd --- /dev/null +++ b/src/components/notifications/AliyunSms.vue @@ -0,0 +1,91 @@ + + diff --git a/src/components/notifications/AmootSMS.vue b/src/components/notifications/AmootSMS.vue new file mode 100644 index 0000000..f5b1ac2 --- /dev/null +++ b/src/components/notifications/AmootSMS.vue @@ -0,0 +1,113 @@ + + + diff --git a/src/components/notifications/Apprise.vue b/src/components/notifications/Apprise.vue new file mode 100644 index 0000000..9280936 --- /dev/null +++ b/src/components/notifications/Apprise.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/components/notifications/Bale.vue b/src/components/notifications/Bale.vue new file mode 100644 index 0000000..576898a --- /dev/null +++ b/src/components/notifications/Bale.vue @@ -0,0 +1,108 @@ + + + diff --git a/src/components/notifications/Bark.vue b/src/components/notifications/Bark.vue new file mode 100644 index 0000000..c75f6f8 --- /dev/null +++ b/src/components/notifications/Bark.vue @@ -0,0 +1,66 @@ + diff --git a/src/components/notifications/BearSMS.vue b/src/components/notifications/BearSMS.vue new file mode 100644 index 0000000..ee98527 --- /dev/null +++ b/src/components/notifications/BearSMS.vue @@ -0,0 +1,56 @@ + + diff --git a/src/components/notifications/Bitrix24.vue b/src/components/notifications/Bitrix24.vue new file mode 100644 index 0000000..d0fbd88 --- /dev/null +++ b/src/components/notifications/Bitrix24.vue @@ -0,0 +1,37 @@ + + diff --git a/src/components/notifications/Brevo.vue b/src/components/notifications/Brevo.vue new file mode 100644 index 0000000..30f94b7 --- /dev/null +++ b/src/components/notifications/Brevo.vue @@ -0,0 +1,79 @@ + + + diff --git a/src/components/notifications/CallMeBot.vue b/src/components/notifications/CallMeBot.vue new file mode 100644 index 0000000..3ead242 --- /dev/null +++ b/src/components/notifications/CallMeBot.vue @@ -0,0 +1,21 @@ + diff --git a/src/components/notifications/Cellsynt.vue b/src/components/notifications/Cellsynt.vue new file mode 100644 index 0000000..b4f9695 --- /dev/null +++ b/src/components/notifications/Cellsynt.vue @@ -0,0 +1,120 @@ + + + diff --git a/src/components/notifications/ClickSendSMS.vue b/src/components/notifications/ClickSendSMS.vue new file mode 100644 index 0000000..b3af645 --- /dev/null +++ b/src/components/notifications/ClickSendSMS.vue @@ -0,0 +1,71 @@ + + diff --git a/src/components/notifications/ClickUp.vue b/src/components/notifications/ClickUp.vue new file mode 100644 index 0000000..14eac51 --- /dev/null +++ b/src/components/notifications/ClickUp.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/components/notifications/DingDing.vue b/src/components/notifications/DingDing.vue new file mode 100644 index 0000000..888f60a --- /dev/null +++ b/src/components/notifications/DingDing.vue @@ -0,0 +1,177 @@ + + + diff --git a/src/components/notifications/Discord.vue b/src/components/notifications/Discord.vue new file mode 100644 index 0000000..7b8e91b --- /dev/null +++ b/src/components/notifications/Discord.vue @@ -0,0 +1,173 @@ + + diff --git a/src/components/notifications/EgoSMS.vue b/src/components/notifications/EgoSMS.vue new file mode 100644 index 0000000..5555dac --- /dev/null +++ b/src/components/notifications/EgoSMS.vue @@ -0,0 +1,57 @@ + + diff --git a/src/components/notifications/Evolution.vue b/src/components/notifications/Evolution.vue new file mode 100644 index 0000000..7ba44f7 --- /dev/null +++ b/src/components/notifications/Evolution.vue @@ -0,0 +1,97 @@ + + + diff --git a/src/components/notifications/Feishu.vue b/src/components/notifications/Feishu.vue new file mode 100644 index 0000000..0a816ee --- /dev/null +++ b/src/components/notifications/Feishu.vue @@ -0,0 +1,24 @@ + diff --git a/src/components/notifications/FlashDuty.vue b/src/components/notifications/FlashDuty.vue new file mode 100644 index 0000000..fe3c958 --- /dev/null +++ b/src/components/notifications/FlashDuty.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/components/notifications/Flowtriq.vue b/src/components/notifications/Flowtriq.vue new file mode 100644 index 0000000..9fd1bf6 --- /dev/null +++ b/src/components/notifications/Flowtriq.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/components/notifications/Fluxer.vue b/src/components/notifications/Fluxer.vue new file mode 100644 index 0000000..ab22aa6 --- /dev/null +++ b/src/components/notifications/Fluxer.vue @@ -0,0 +1,87 @@ + + diff --git a/src/components/notifications/FreeMobile.vue b/src/components/notifications/FreeMobile.vue new file mode 100644 index 0000000..9bbceab --- /dev/null +++ b/src/components/notifications/FreeMobile.vue @@ -0,0 +1,29 @@ + diff --git a/src/components/notifications/GoAlert.vue b/src/components/notifications/GoAlert.vue new file mode 100644 index 0000000..675dcab --- /dev/null +++ b/src/components/notifications/GoAlert.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/components/notifications/GoogleChat.vue b/src/components/notifications/GoogleChat.vue new file mode 100644 index 0000000..4d7865f --- /dev/null +++ b/src/components/notifications/GoogleChat.vue @@ -0,0 +1,101 @@ + + + diff --git a/src/components/notifications/GoogleSheets.vue b/src/components/notifications/GoogleSheets.vue new file mode 100644 index 0000000..261cc4c --- /dev/null +++ b/src/components/notifications/GoogleSheets.vue @@ -0,0 +1,94 @@ + + + diff --git a/src/components/notifications/Notifery.vue b/src/components/notifications/Notifery.vue new file mode 100644 index 0000000..f1ca7ef --- /dev/null +++ b/src/components/notifications/Notifery.vue @@ -0,0 +1,47 @@ + + + diff --git a/src/components/notifications/NotifyApp.vue b/src/components/notifications/NotifyApp.vue new file mode 100644 index 0000000..885ebdd --- /dev/null +++ b/src/components/notifications/NotifyApp.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/components/notifications/Ntfy.vue b/src/components/notifications/Ntfy.vue new file mode 100644 index 0000000..fb094f6 --- /dev/null +++ b/src/components/notifications/Ntfy.vue @@ -0,0 +1,196 @@ + + + diff --git a/src/components/notifications/Octopush.vue b/src/components/notifications/Octopush.vue new file mode 100644 index 0000000..ffcbda1 --- /dev/null +++ b/src/components/notifications/Octopush.vue @@ -0,0 +1,78 @@ + + + diff --git a/src/components/notifications/OneBot.vue b/src/components/notifications/OneBot.vue new file mode 100644 index 0000000..5369957 --- /dev/null +++ b/src/components/notifications/OneBot.vue @@ -0,0 +1,45 @@ + diff --git a/src/components/notifications/OneChat.vue b/src/components/notifications/OneChat.vue new file mode 100644 index 0000000..cf64458 --- /dev/null +++ b/src/components/notifications/OneChat.vue @@ -0,0 +1,60 @@ + + + diff --git a/src/components/notifications/Onesender.vue b/src/components/notifications/Onesender.vue new file mode 100644 index 0000000..af2ed1d --- /dev/null +++ b/src/components/notifications/Onesender.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/src/components/notifications/Ooredoo.vue b/src/components/notifications/Ooredoo.vue new file mode 100644 index 0000000..c90a0db --- /dev/null +++ b/src/components/notifications/Ooredoo.vue @@ -0,0 +1,64 @@ + + diff --git a/src/components/notifications/OpenWa.vue b/src/components/notifications/OpenWa.vue new file mode 100644 index 0000000..1981353 --- /dev/null +++ b/src/components/notifications/OpenWa.vue @@ -0,0 +1,91 @@ + + diff --git a/src/components/notifications/Opsgenie.vue b/src/components/notifications/Opsgenie.vue new file mode 100644 index 0000000..8abb15b --- /dev/null +++ b/src/components/notifications/Opsgenie.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/components/notifications/PagerDuty.vue b/src/components/notifications/PagerDuty.vue new file mode 100644 index 0000000..3a7baa6 --- /dev/null +++ b/src/components/notifications/PagerDuty.vue @@ -0,0 +1,56 @@ + + + diff --git a/src/components/notifications/PagerTree.vue b/src/components/notifications/PagerTree.vue new file mode 100644 index 0000000..868bfee --- /dev/null +++ b/src/components/notifications/PagerTree.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/components/notifications/Pinglet.vue b/src/components/notifications/Pinglet.vue new file mode 100644 index 0000000..dd9b986 --- /dev/null +++ b/src/components/notifications/Pinglet.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/components/notifications/Plivo.vue b/src/components/notifications/Plivo.vue new file mode 100644 index 0000000..2ce11a1 --- /dev/null +++ b/src/components/notifications/Plivo.vue @@ -0,0 +1,101 @@ + + + diff --git a/src/components/notifications/PromoSMS.vue b/src/components/notifications/PromoSMS.vue new file mode 100644 index 0000000..a28a0f9 --- /dev/null +++ b/src/components/notifications/PromoSMS.vue @@ -0,0 +1,74 @@ + + + diff --git a/src/components/notifications/Pumble.vue b/src/components/notifications/Pumble.vue new file mode 100644 index 0000000..8a49e69 --- /dev/null +++ b/src/components/notifications/Pumble.vue @@ -0,0 +1,18 @@ + diff --git a/src/components/notifications/PushDeer.vue b/src/components/notifications/PushDeer.vue new file mode 100644 index 0000000..a5bde5e --- /dev/null +++ b/src/components/notifications/PushDeer.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/components/notifications/PushPlus.vue b/src/components/notifications/PushPlus.vue new file mode 100644 index 0000000..ca8b35c --- /dev/null +++ b/src/components/notifications/PushPlus.vue @@ -0,0 +1,24 @@ + + + diff --git a/src/components/notifications/Pushbullet.vue b/src/components/notifications/Pushbullet.vue new file mode 100644 index 0000000..a1403c2 --- /dev/null +++ b/src/components/notifications/Pushbullet.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/components/notifications/Pushover.vue b/src/components/notifications/Pushover.vue new file mode 100644 index 0000000..4a3d617 --- /dev/null +++ b/src/components/notifications/Pushover.vue @@ -0,0 +1,113 @@ + + + diff --git a/src/components/notifications/Pushy.vue b/src/components/notifications/Pushy.vue new file mode 100644 index 0000000..2a53370 --- /dev/null +++ b/src/components/notifications/Pushy.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/components/notifications/Resend.vue b/src/components/notifications/Resend.vue new file mode 100644 index 0000000..723d5b3 --- /dev/null +++ b/src/components/notifications/Resend.vue @@ -0,0 +1,67 @@ + + + diff --git a/src/components/notifications/RocketChat.vue b/src/components/notifications/RocketChat.vue new file mode 100644 index 0000000..c3cf6b5 --- /dev/null +++ b/src/components/notifications/RocketChat.vue @@ -0,0 +1,41 @@ + diff --git a/src/components/notifications/SIGNL4.vue b/src/components/notifications/SIGNL4.vue new file mode 100644 index 0000000..d557c2d --- /dev/null +++ b/src/components/notifications/SIGNL4.vue @@ -0,0 +1,16 @@ + diff --git a/src/components/notifications/SMSC.vue b/src/components/notifications/SMSC.vue new file mode 100644 index 0000000..cc2cbab --- /dev/null +++ b/src/components/notifications/SMSC.vue @@ -0,0 +1,67 @@ + + diff --git a/src/components/notifications/SMSEagle.vue b/src/components/notifications/SMSEagle.vue new file mode 100644 index 0000000..4a8baea --- /dev/null +++ b/src/components/notifications/SMSEagle.vue @@ -0,0 +1,243 @@ + + + diff --git a/src/components/notifications/SMSGateway.vue b/src/components/notifications/SMSGateway.vue new file mode 100644 index 0000000..54e7592 --- /dev/null +++ b/src/components/notifications/SMSGateway.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/components/notifications/SMSIR.vue b/src/components/notifications/SMSIR.vue new file mode 100644 index 0000000..893e1ea --- /dev/null +++ b/src/components/notifications/SMSIR.vue @@ -0,0 +1,51 @@ + + diff --git a/src/components/notifications/SMSManager.vue b/src/components/notifications/SMSManager.vue new file mode 100644 index 0000000..6d88604 --- /dev/null +++ b/src/components/notifications/SMSManager.vue @@ -0,0 +1,40 @@ + diff --git a/src/components/notifications/SMSPartner.vue b/src/components/notifications/SMSPartner.vue new file mode 100644 index 0000000..b0b532b --- /dev/null +++ b/src/components/notifications/SMSPartner.vue @@ -0,0 +1,62 @@ + + + diff --git a/src/components/notifications/SMSPlanet.vue b/src/components/notifications/SMSPlanet.vue new file mode 100644 index 0000000..269c467 --- /dev/null +++ b/src/components/notifications/SMSPlanet.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/components/notifications/SMTP.vue b/src/components/notifications/SMTP.vue new file mode 100644 index 0000000..f78a4bb --- /dev/null +++ b/src/components/notifications/SMTP.vue @@ -0,0 +1,331 @@ + + + diff --git a/src/components/notifications/SendGrid.vue b/src/components/notifications/SendGrid.vue new file mode 100644 index 0000000..8d6a3d5 --- /dev/null +++ b/src/components/notifications/SendGrid.vue @@ -0,0 +1,71 @@ + + + diff --git a/src/components/notifications/ServerChan.vue b/src/components/notifications/ServerChan.vue new file mode 100644 index 0000000..c79a376 --- /dev/null +++ b/src/components/notifications/ServerChan.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/notifications/SerwerSMS.vue b/src/components/notifications/SerwerSMS.vue new file mode 100644 index 0000000..cc29a5a --- /dev/null +++ b/src/components/notifications/SerwerSMS.vue @@ -0,0 +1,70 @@ + + + diff --git a/src/components/notifications/SevenIO.vue b/src/components/notifications/SevenIO.vue new file mode 100644 index 0000000..6b53762 --- /dev/null +++ b/src/components/notifications/SevenIO.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/components/notifications/Signal.vue b/src/components/notifications/Signal.vue new file mode 100644 index 0000000..1535b4c --- /dev/null +++ b/src/components/notifications/Signal.vue @@ -0,0 +1,99 @@ + + + diff --git a/src/components/notifications/Signalgrid.vue b/src/components/notifications/Signalgrid.vue new file mode 100644 index 0000000..ec78ac4 --- /dev/null +++ b/src/components/notifications/Signalgrid.vue @@ -0,0 +1,32 @@ + + + diff --git a/src/components/notifications/Slack.vue b/src/components/notifications/Slack.vue new file mode 100644 index 0000000..9f64132 --- /dev/null +++ b/src/components/notifications/Slack.vue @@ -0,0 +1,132 @@ + + + diff --git a/src/components/notifications/Splunk.vue b/src/components/notifications/Splunk.vue new file mode 100644 index 0000000..d50ae6b --- /dev/null +++ b/src/components/notifications/Splunk.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/components/notifications/SpugPush.vue b/src/components/notifications/SpugPush.vue new file mode 100644 index 0000000..492c37a --- /dev/null +++ b/src/components/notifications/SpugPush.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/components/notifications/Squadcast.vue b/src/components/notifications/Squadcast.vue new file mode 100644 index 0000000..4907223 --- /dev/null +++ b/src/components/notifications/Squadcast.vue @@ -0,0 +1,13 @@ + diff --git a/src/components/notifications/Stackfield.vue b/src/components/notifications/Stackfield.vue new file mode 100644 index 0000000..449198e --- /dev/null +++ b/src/components/notifications/Stackfield.vue @@ -0,0 +1,25 @@ + diff --git a/src/components/notifications/Teams.vue b/src/components/notifications/Teams.vue new file mode 100644 index 0000000..4820d83 --- /dev/null +++ b/src/components/notifications/Teams.vue @@ -0,0 +1,31 @@ + diff --git a/src/components/notifications/TechulusPush.vue b/src/components/notifications/TechulusPush.vue new file mode 100644 index 0000000..ca7e3cd --- /dev/null +++ b/src/components/notifications/TechulusPush.vue @@ -0,0 +1,96 @@ + + + diff --git a/src/components/notifications/Telegram.vue b/src/components/notifications/Telegram.vue new file mode 100644 index 0000000..cc0797b --- /dev/null +++ b/src/components/notifications/Telegram.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/src/components/notifications/Telnyx.vue b/src/components/notifications/Telnyx.vue new file mode 100644 index 0000000..7b40b3d --- /dev/null +++ b/src/components/notifications/Telnyx.vue @@ -0,0 +1,67 @@ + + + diff --git a/src/components/notifications/Teltonika.vue b/src/components/notifications/Teltonika.vue new file mode 100644 index 0000000..a93d0e2 --- /dev/null +++ b/src/components/notifications/Teltonika.vue @@ -0,0 +1,97 @@ + + + diff --git a/src/components/notifications/Threema.vue b/src/components/notifications/Threema.vue new file mode 100644 index 0000000..fc249cc --- /dev/null +++ b/src/components/notifications/Threema.vue @@ -0,0 +1,92 @@ + + diff --git a/src/components/notifications/TurboSMTP.vue b/src/components/notifications/TurboSMTP.vue new file mode 100644 index 0000000..fc0819c --- /dev/null +++ b/src/components/notifications/TurboSMTP.vue @@ -0,0 +1,104 @@ + + + diff --git a/src/components/notifications/Twilio.vue b/src/components/notifications/Twilio.vue new file mode 100644 index 0000000..a209c2e --- /dev/null +++ b/src/components/notifications/Twilio.vue @@ -0,0 +1,74 @@ + diff --git a/src/components/notifications/VK.vue b/src/components/notifications/VK.vue new file mode 100644 index 0000000..507d746 --- /dev/null +++ b/src/components/notifications/VK.vue @@ -0,0 +1,57 @@ + + + diff --git a/src/components/notifications/VKTeams.vue b/src/components/notifications/VKTeams.vue new file mode 100644 index 0000000..1df6e59 --- /dev/null +++ b/src/components/notifications/VKTeams.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/components/notifications/WAHA.vue b/src/components/notifications/WAHA.vue new file mode 100644 index 0000000..f37f7cc --- /dev/null +++ b/src/components/notifications/WAHA.vue @@ -0,0 +1,66 @@ + + diff --git a/src/components/notifications/WPush.vue b/src/components/notifications/WPush.vue new file mode 100644 index 0000000..48523ad --- /dev/null +++ b/src/components/notifications/WPush.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/components/notifications/WeCom.vue b/src/components/notifications/WeCom.vue new file mode 100644 index 0000000..3183f01 --- /dev/null +++ b/src/components/notifications/WeCom.vue @@ -0,0 +1,38 @@ + diff --git a/src/components/notifications/Webhook.vue b/src/components/notifications/Webhook.vue new file mode 100644 index 0000000..128688f --- /dev/null +++ b/src/components/notifications/Webhook.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/src/components/notifications/Webpush.vue b/src/components/notifications/Webpush.vue new file mode 100644 index 0000000..0d1f214 --- /dev/null +++ b/src/components/notifications/Webpush.vue @@ -0,0 +1,95 @@ + + + diff --git a/src/components/notifications/Whapi.vue b/src/components/notifications/Whapi.vue new file mode 100644 index 0000000..9c045bc --- /dev/null +++ b/src/components/notifications/Whapi.vue @@ -0,0 +1,59 @@ + + diff --git a/src/components/notifications/WxPusher.vue b/src/components/notifications/WxPusher.vue new file mode 100644 index 0000000..2f8e431 --- /dev/null +++ b/src/components/notifications/WxPusher.vue @@ -0,0 +1,48 @@ + + + diff --git a/src/components/notifications/YZJ.vue b/src/components/notifications/YZJ.vue new file mode 100644 index 0000000..01120e4 --- /dev/null +++ b/src/components/notifications/YZJ.vue @@ -0,0 +1,43 @@ + + diff --git a/src/components/notifications/ZohoCliq.vue b/src/components/notifications/ZohoCliq.vue new file mode 100644 index 0000000..b595313 --- /dev/null +++ b/src/components/notifications/ZohoCliq.vue @@ -0,0 +1,15 @@ + diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js new file mode 100644 index 0000000..c9fda1a --- /dev/null +++ b/src/components/notifications/index.js @@ -0,0 +1,227 @@ +import Alerta from "./Alerta.vue"; +import AlertNow from "./AlertNow.vue"; +import AliyunSMS from "./AliyunSms.vue"; +import Apprise from "./Apprise.vue"; +import Bale from "./Bale.vue"; +import Bark from "./Bark.vue"; +import BearSMS from "./BearSMS.vue"; +import Bitrix24 from "./Bitrix24.vue"; +import ClickUp from "./ClickUp.vue"; +import Notifery from "./Notifery.vue"; +import ClickSendSMS from "./ClickSendSMS.vue"; +import CallMeBot from "./CallMeBot.vue"; +import SMSC from "./SMSC.vue"; +import DingDing from "./DingDing.vue"; +import Discord from "./Discord.vue"; +import Fluxer from "./Fluxer.vue"; +import Elks from "./46elks.vue"; +import EgoSMS from "./EgoSMS.vue"; +import Feishu from "./Feishu.vue"; +import FreeMobile from "./FreeMobile.vue"; +import GoogleChat from "./GoogleChat.vue"; +import GoogleSheets from "./GoogleSheets.vue"; +import Gorush from "./Gorush.vue"; +import Gotify from "./Gotify.vue"; +import GrafanaOncall from "./GrafanaOncall.vue"; +import GtxMessaging from "./GtxMessaging.vue"; +import HomeAssistant from "./HomeAssistant.vue"; +import Indigo from "./Indigo.vue"; +import HeiiOnCall from "./HeiiOnCall.vue"; +import Keep from "./Keep.vue"; +import Kook from "./Kook.vue"; +import Line from "./Line.vue"; +import LunaSea from "./LunaSea.vue"; +import Matrix from "./Matrix.vue"; +import Mattermost from "./Mattermost.vue"; +import NextcloudTalk from "./NextcloudTalk.vue"; +import Nostr from "./Nostr.vue"; +import NotifyApp from "./NotifyApp.vue"; +import Ntfy from "./Ntfy.vue"; +import Octopush from "./Octopush.vue"; +import OneChat from "./OneChat.vue"; +import OneBot from "./OneBot.vue"; +import Onesender from "./Onesender.vue"; +import Ooredoo from "./Ooredoo.vue"; +import Opsgenie from "./Opsgenie.vue"; +import JiraServiceManagement from "./JiraServiceManagement.vue"; +import PagerDuty from "./PagerDuty.vue"; +import FlashDuty from "./FlashDuty.vue"; +import Flowtriq from "./Flowtriq.vue"; +import PagerTree from "./PagerTree.vue"; +import Pinglet from "./Pinglet.vue"; +import Plivo from "./Plivo.vue"; +import PromoSMS from "./PromoSMS.vue"; +import Pumble from "./Pumble.vue"; +import Pushbullet from "./Pushbullet.vue"; +import PushDeer from "./PushDeer.vue"; +import Pushover from "./Pushover.vue"; +import PushPlus from "./PushPlus.vue"; +import Pushy from "./Pushy.vue"; +import RocketChat from "./RocketChat.vue"; +import ServerChan from "./ServerChan.vue"; +import SerwerSMS from "./SerwerSMS.vue"; +import Signal from "./Signal.vue"; +import Signalgrid from "./Signalgrid.vue"; +import SMSGateway from "./SMSGateway.vue"; +import SMSManager from "./SMSManager.vue"; +import SMSPartner from "./SMSPartner.vue"; +import Slack from "./Slack.vue"; +import Squadcast from "./Squadcast.vue"; +import SMSEagle from "./SMSEagle.vue"; +import Stackfield from "./Stackfield.vue"; +import STMP from "./SMTP.vue"; +import Teams from "./Teams.vue"; +import TechulusPush from "./TechulusPush.vue"; +import Telegram from "./Telegram.vue"; +import Teltonika from "./Teltonika.vue"; +import Telnyx from "./Telnyx.vue"; +import Threema from "./Threema.vue"; +import Twilio from "./Twilio.vue"; +import Webhook from "./Webhook.vue"; +import WeCom from "./WeCom.vue"; +import GoAlert from "./GoAlert.vue"; +import ZohoCliq from "./ZohoCliq.vue"; +import Splunk from "./Splunk.vue"; +import SpugPush from "./SpugPush.vue"; +import SevenIO from "./SevenIO.vue"; +import Whapi from "./Whapi.vue"; +import WAHA from "./WAHA.vue"; +import Whatsapp360messenger from "./360messenger.vue"; +import Evolution from "./Evolution.vue"; +import OpenWa from "./OpenWa.vue"; +import Cellsynt from "./Cellsynt.vue"; +import WPush from "./WPush.vue"; +import WxPusher from "./WxPusher.vue"; +import SIGNL4 from "./SIGNL4.vue"; +import SendGrid from "./SendGrid.vue"; +import TurboSMTP from "./TurboSMTP.vue"; +import Brevo from "./Brevo.vue"; +import YZJ from "./YZJ.vue"; +import AmootSMS from "./AmootSMS.vue"; +import SMSPlanet from "./SMSPlanet.vue"; +import SMSIR from "./SMSIR.vue"; +import Webpush from "./Webpush.vue"; +import HaloPSA from "./HaloPSA.vue"; +import Resend from "./Resend.vue"; +import Max from "./Max.vue"; +import VK from "./VK.vue"; +import VKTeams from "./VKTeams.vue"; +import Milky from "./Milky.vue"; + +/** + * Manage all notification form. + * @type { Record } + */ +const NotificationFormList = { + alerta: Alerta, + AlertNow: AlertNow, + AliyunSMS: AliyunSMS, + apprise: Apprise, + bale: Bale, + Bark: Bark, + bearsms: BearSMS, + Bitrix24: Bitrix24, + ClickUp: ClickUp, + clicksendsms: ClickSendSMS, + CallMeBot: CallMeBot, + amootsms: AmootSMS, + smsc: SMSC, + smsir: SMSIR, + DingDing: DingDing, + discord: Discord, + fluxer: Fluxer, + Elks: Elks, + egosms: EgoSMS, + Feishu: Feishu, + FreeMobile: FreeMobile, + GoogleChat: GoogleChat, + GoogleSheets: GoogleSheets, + gorush: Gorush, + gotify: Gotify, + GrafanaOncall: GrafanaOncall, + HomeAssistant: HomeAssistant, + Indigo: Indigo, + HeiiOnCall: HeiiOnCall, + Keep: Keep, + Kook: Kook, + line: Line, + lunasea: LunaSea, + matrix: Matrix, + mattermost: Mattermost, + nextcloudtalk: NextcloudTalk, + nostr: Nostr, + notifyapp: NotifyApp, + ntfy: Ntfy, + octopush: Octopush, + OneChat: OneChat, + OneBot: OneBot, + Onesender: Onesender, + Ooredoo: Ooredoo, + Opsgenie: Opsgenie, + JiraServiceManagement: JiraServiceManagement, + PagerDuty: PagerDuty, + FlashDuty: FlashDuty, + Flowtriq: Flowtriq, + PagerTree: PagerTree, + pinglet: Pinglet, + plivo: Plivo, + promosms: PromoSMS, + pumble: Pumble, + pushbullet: Pushbullet, + PushByTechulus: TechulusPush, + PushDeer: PushDeer, + pushover: Pushover, + PushPlus: PushPlus, + pushy: Pushy, + "rocket.chat": RocketChat, + serwersms: SerwerSMS, + signal: Signal, + signalgrid: Signalgrid, + SIGNL4: SIGNL4, + SMSGateway: SMSGateway, + SMSManager: SMSManager, + SMSPartner: SMSPartner, + slack: Slack, + squadcast: Squadcast, + SMSEagle: SMSEagle, + smtp: STMP, + stackfield: Stackfield, + teams: Teams, + telegram: Telegram, + Teltonika: Teltonika, + telnyx: Telnyx, + threema: Threema, + twilio: Twilio, + Splunk: Splunk, + SpugPush: SpugPush, + webhook: Webhook, + WeCom: WeCom, + GoAlert: GoAlert, + ServerChan: ServerChan, + ZohoCliq: ZohoCliq, + SevenIO: SevenIO, + whapi: Whapi, + evolution: Evolution, + openwa: OpenWa, + notifery: Notifery, + waha: WAHA, + Whatsapp360messenger: Whatsapp360messenger, + gtxmessaging: GtxMessaging, + Cellsynt: Cellsynt, + WPush: WPush, + WxPusher: WxPusher, + SendGrid: SendGrid, + TurboSMTP: TurboSMTP, + Brevo: Brevo, + Resend: Resend, + YZJ: YZJ, + SMSPlanet: SMSPlanet, + Webpush: Webpush, + HaloPSA: HaloPSA, + max: Max, + VK: VK, + VKTeams: VKTeams, + Milky: Milky, +}; + +export default NotificationFormList; diff --git a/src/components/settings/APIKeys.vue b/src/components/settings/APIKeys.vue new file mode 100644 index 0000000..f5b32fa --- /dev/null +++ b/src/components/settings/APIKeys.vue @@ -0,0 +1,267 @@ + + + + + diff --git a/src/components/settings/About.vue b/src/components/settings/About.vue new file mode 100644 index 0000000..0ae4b22 --- /dev/null +++ b/src/components/settings/About.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/src/components/settings/Appearance.vue b/src/components/settings/Appearance.vue new file mode 100644 index 0000000..c500c29 --- /dev/null +++ b/src/components/settings/Appearance.vue @@ -0,0 +1,176 @@ + + + + + diff --git a/src/components/settings/Docker.vue b/src/components/settings/Docker.vue new file mode 100644 index 0000000..54d427a --- /dev/null +++ b/src/components/settings/Docker.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/components/settings/General.vue b/src/components/settings/General.vue new file mode 100644 index 0000000..5e93d8a --- /dev/null +++ b/src/components/settings/General.vue @@ -0,0 +1,282 @@ + + + diff --git a/src/components/settings/MonitorHistory.vue b/src/components/settings/MonitorHistory.vue new file mode 100644 index 0000000..ab7d410 --- /dev/null +++ b/src/components/settings/MonitorHistory.vue @@ -0,0 +1,145 @@ + + + diff --git a/src/components/settings/Notifications.vue b/src/components/settings/Notifications.vue new file mode 100644 index 0000000..6db2518 --- /dev/null +++ b/src/components/settings/Notifications.vue @@ -0,0 +1,304 @@ + + + + + diff --git a/src/components/settings/Proxies.vue b/src/components/settings/Proxies.vue new file mode 100644 index 0000000..d1070ee --- /dev/null +++ b/src/components/settings/Proxies.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/src/components/settings/RemoteBrowsers.vue b/src/components/settings/RemoteBrowsers.vue new file mode 100644 index 0000000..863328c --- /dev/null +++ b/src/components/settings/RemoteBrowsers.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/components/settings/ReverseProxy.vue b/src/components/settings/ReverseProxy.vue new file mode 100644 index 0000000..d4ad8db --- /dev/null +++ b/src/components/settings/ReverseProxy.vue @@ -0,0 +1,224 @@ + + + + + diff --git a/src/components/settings/Security.vue b/src/components/settings/Security.vue new file mode 100644 index 0000000..22f5b10 --- /dev/null +++ b/src/components/settings/Security.vue @@ -0,0 +1,245 @@ + + + diff --git a/src/components/settings/Tags.vue b/src/components/settings/Tags.vue new file mode 100644 index 0000000..eb1730e --- /dev/null +++ b/src/components/settings/Tags.vue @@ -0,0 +1,191 @@ + + + + + diff --git a/src/i18n.js b/src/i18n.js new file mode 100644 index 0000000..109e9e8 --- /dev/null +++ b/src/i18n.js @@ -0,0 +1,110 @@ +import { createI18n } from "vue-i18n/dist/vue-i18n.esm-browser.prod.js"; +import en from "./lang/en.json"; + +const languageList = { + "ar-SY": "العربية", + "cs-CZ": "Čeština", + "zh-HK": "繁體中文 (香港)", + "bg-BG": "Български", + be: "Беларуская", + "de-DE": "Deutsch (Deutschland)", + "de-CH": "Deutsch (Schweiz)", + bar: "Bairisch", + "nl-NL": "Nederlands", + "nb-NO": "Norsk", + "es-ES": "Español", + eu: "Euskara", + fa: "Farsi", + "pt-PT": "Português (Portugal)", + "pt-BR": "Português (Brasileiro)", + fi: "Suomi", + "fr-FR": "Français (France)", + "he-IL": "עברית", + hu: "Magyar", + "hr-HR": "Hrvatski", + "it-IT": "Italiano (Italian)", + "id-ID": "Bahasa Indonesia (Indonesian)", + ja: "日本語", + "da-DK": "Danish (Danmark)", + sr: "Српски", + "sl-SI": "Slovenščina", + "sr-latn": "Srpski", + "sv-SE": "Svenska", + "tr-TR": "Türkçe", + "ko-KR": "한국어", + lt: "Lietuvių", + "ru-RU": "Русский", + "zh-CN": "简体中文", + pl: "Polski", + "et-EE": "eesti", + "vi-VN": "Tiếng Việt", + "zh-TW": "繁體中文 (台灣)", + "uk-UA": "Українська", + "th-TH": "ไทย", + "el-GR": "Ελληνικά", + yue: "繁體中文 (廣東話 / 粵語)", + ro: "Limba română", + ur: "Urdu", + ge: "ქართული", + uz: "O'zbek tili", + ga: "Gaeilge", + sk: "Slovenčina", + "en-GB": "English (United Kingdom)", +}; + +let messages = { + en, +}; + +for (let lang in languageList) { + messages[lang] = { + languageName: languageList[lang], + }; +} + +const rtlLangs = ["he-IL", "fa", "ar-SY", "ur"]; + +/** + * Find the best matching locale to display + * If no locale can be matched, the default is "en" + * @returns {string} the locale that should be displayed + */ +export function currentLocale() { + for (const locale of [localStorage.locale, navigator.language, ...navigator.languages]) { + // localstorage might not have a value or there might not be a language in `navigator.language` + if (!locale) { + continue; + } + if (locale in messages) { + return locale; + } + // If the locale is a 2-letter code, we can try to find a regional variant + // e.g. "fr" may not be in the messages, but "fr-FR" is + if (locale.length === 2) { + const regionalLocale = `${locale}-${locale.toUpperCase()}`; + if (regionalLocale in messages) { + return regionalLocale; + } + } else { + // Some locales are further specified such as "en-US". + // If we only have a generic locale for this, we can use it too + const genericLocale = locale.slice(0, 2); + if (genericLocale in messages) { + return genericLocale; + } + } + } + return "en"; +} + +export const localeDirection = () => { + return rtlLangs.includes(currentLocale()) ? "rtl" : "ltr"; +}; + +export const i18n = createI18n({ + locale: currentLocale(), + fallbackLocale: "en", + silentFallbackWarn: true, + silentTranslationWarn: true, + messages: messages, +}); diff --git a/src/icon.js b/src/icon.js new file mode 100644 index 0000000..eeb953f --- /dev/null +++ b/src/icon.js @@ -0,0 +1,108 @@ +import { library } from "@fortawesome/fontawesome-svg-core"; +import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; + +// Add Free Font Awesome Icons +// https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free +// In order to add an icon, you have to: +// 1) add the icon name in the import statement below; +// 2) add the icon name to the library.add() statement below. +import { + faArrowAltCircleUp, + faCog, + faEdit, + faExclamationTriangle, + faEye, + faEyeSlash, + faList, + faPause, + faPlay, + faPlus, + faSearch, + faTachometerAlt, + faTimes, + faTimesCircle, + faTrash, + faCheckCircle, + faStream, + faSave, + faExclamationCircle, + faBullhorn, + faArrowsAltV, + faUnlink, + faQuestionCircle, + faImages, + faUpload, + faCopy, + faCheck, + faFile, + faAward, + faLink, + faChevronDown, + faSignOutAlt, + faPen, + faExternalLinkSquareAlt, + faSpinner, + faUndo, + faPlusCircle, + faAngleDown, + faWrench, + faHeartbeat, + faFilter, + faInfoCircle, + faClone, + faCertificate, + faFolder, + faFolderOpen, +} from "@fortawesome/free-solid-svg-icons"; + +library.add( + faArrowAltCircleUp, + faCog, + faEdit, + faExclamationTriangle, + faEye, + faEyeSlash, + faList, + faPause, + faPlay, + faPlus, + faSearch, + faTachometerAlt, + faTimes, + faTimesCircle, + faTrash, + faCheckCircle, + faStream, + faSave, + faExclamationCircle, + faBullhorn, + faArrowsAltV, + faUnlink, + faQuestionCircle, + faImages, + faUpload, + faCopy, + faCheck, + faFile, + faAward, + faLink, + faChevronDown, + faSignOutAlt, + faPen, + faExternalLinkSquareAlt, + faSpinner, + faUndo, + faPlusCircle, + faAngleDown, + faLink, + faWrench, + faHeartbeat, + faFilter, + faInfoCircle, + faClone, + faCertificate, + faFolder, + faFolderOpen, +); + +export { FontAwesomeIcon }; diff --git a/src/lang/README.md b/src/lang/README.md new file mode 100644 index 0000000..8c9b237 --- /dev/null +++ b/src/lang/README.md @@ -0,0 +1,20 @@ +# Translations + +## How to translate + +(2023-01-24 Updated) + +1. Go to [https://weblate.kuma.pet](https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/) +2. Register an account on Weblate +3. Make sure your GitHub email is matched with Weblate's account, so that it could show you as a contributor on GitHub +4. Choose your language on Weblate and start translating. + +## How to add a new language in the dropdown + +1. Add your language at https://weblate.kuma.pet/projects/uptime-kuma/uptime-kuma/ +2. Find the language code (You can find it at the end of the URL) +3. Go to https://github.com/louislam/uptime-kuma/blob/master/src/i18n.js and click `Edit` icon +4. Add your language at the end of `languageList`, format: `"zh-TW": "繁體中文 (台灣)",` +5. Commit and make a pull request for me to approve + +If you do not have programming skills, let me know in [the issues section](https://github.com/louislam/uptime-kuma/issues). I will assist you. 😏 diff --git a/src/lang/ab.json b/src/lang/ab.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/lang/ab.json @@ -0,0 +1 @@ +{} diff --git a/src/lang/af.json b/src/lang/af.json new file mode 100644 index 0000000..26d2313 --- /dev/null +++ b/src/lang/af.json @@ -0,0 +1,66 @@ +{ + "languageName": "Afrikaans", + "setupDatabaseChooseDatabase": "Watter databasis wil u gebruik?", + "setupDatabaseMariaDB": "Konnekteer aan eksterne MariaDB databasis. U sal die databasis konneksie informasie moet insleutel.", + "settingUpDatabaseMSG": "Besig om die databasis op te stel. Dit mag n rukkie neem, so wees asseblief geduldig.", + "dbName": "Databasis Naam", + "Settings": "Verstellings", + "Dashboard": "Paneelbord", + "Help": "Hulp", + "New Update": "Nuwe Weergawe", + "Language": "Taal", + "Appearance": "Voorkoms", + "Theme": "Tema", + "General": "Algemeen", + "Game": "Spel", + "Version": "Weergawe", + "Check Update On GitHub": "Sien GitHub vir opdateering", + "List": "Lys", + "Home": "Tuis", + "Add": "Voeg By", + "Add New Monitor": "Voeg Nuwe Monitor by", + "Quick Stats": "Vinnige Statistiek", + "Up": "Op", + "Down": "Af", + "Pending": "Hangend (or) Besig", + "Maintenance": "Onderhoud", + "Unknown": "Onseker", + "Cannot connect to the socket server": "Kan nie konnekteer aan die sok-bediener nie", + "Reconnecting...": "Besig om te konnekteer...", + "General Monitor Type": "Algemene Monitor Tipe", + "Specific Monitor Type": "Spesifieke Monitor Tipe", + "pauseDashboardHome": "Pouse", + "Pause": "Pouse", + "Name": "Naam", + "Status": "Status", + "DateTime": "DatumTyd", + "Message": "Boodskap", + "No important events": "Geen belangrike gebeurtenisse nie", + "Resume": "Gaan Voort", + "Edit": "Verstel", + "Delete": "Skrap", + "Current": "Huidig(e)", + "Uptime": "Tyd Op", + "Cert Exp.": "Sertifikaat Verval", + "Monitor": "Monitor | Monitors", + "day": "dag | dae", + "-day": "-dag", + "hour": "uur", + "-hour": "-uur", + "Response": "Terugvoer(ing)", + "Ping": "Ping", + "Monitor Type": "Monitor Tipe", + "Keyword": "Sleutelwoord", + "Invert Keyword": "Omkeer Sleutelwoord", + "Expected Value": "Verwagte Waarde", + "Json Query": "JSon Navraag", + "Friendly Name": "Vriendelike Naam", + "URL": "URL", + "Hostname": "Gasheernaam", + "Port": "Poort", + "setupDatabaseEmbeddedMariaDB": "U hoef niks te stel nie. Hierdie docker beeld het MariaDB ingebou en vooraf opgestel vir u. Uptime Kuma sal aan hierdie databasis konnekteer via unix sok verbinding.", + "setupDatabaseSQLite": "'n Eenvoudige databasis leêr, aanbeveel vir klein-skaalse ontploiings. Voor weergawe 2.0.0 het Uptime Kuma SQLite as die verstekopsie.", + "Primary Base URL": "Primêre Basis URL", + "statusMaintenance": "Onderhoud", + "Passive Monitor Type": "Passiewe Monitor Tipe" +} diff --git a/src/lang/ang.json b/src/lang/ang.json new file mode 100644 index 0000000..240b8ec --- /dev/null +++ b/src/lang/ang.json @@ -0,0 +1,3 @@ +{ + "languageName": "" +} diff --git a/src/lang/ar-SY.json b/src/lang/ar-SY.json new file mode 100644 index 0000000..456a91f --- /dev/null +++ b/src/lang/ar-SY.json @@ -0,0 +1,718 @@ +{ + "languageName": "العربية", + "checkEverySecond": "تحقق من كل {0} ثانية", + "retryCheckEverySecond": "أعد محاولة كل {0} ثانية", + "resendEveryXTimes": "إعادة تقديم كل {0} مرات", + "resendDisabled": "إعادة الالتزام بالتعطيل", + "retriesDescription": "الحد الأقصى لإعادة المحاولة قبل تمييز الخدمة على أنها لأسفل وإرسال إشعار", + "ignoreTLSError": "تجاهل خطأ TLS/SSL لمواقع HTTPS", + "upsideDownModeDescription": "اقلب الحالة رأسًا على عقب. إذا كانت الخدمة قابلة للوصول إلى أسفل.", + "maxRedirectDescription": "الحد الأقصى لعدد إعادة التوجيه لمتابعة. ضبط على 0 لتعطيل إعادة التوجيه.", + "enableGRPCTls": "السماح لإرسال طلب GRPC مع اتصال TLS", + "grpcMethodDescription": "يتم تحويل اسم الطريقة إلى تنسيق Cammelcase مثل Sayhello Check وما إلى ذلك.", + "acceptedStatusCodesDescription": "حدد رموز الحالة التي تعتبر استجابة ناجحة.", + "Maintenance": "صيانة", + "statusMaintenance": "صيانة", + "Schedule maintenance": "جدولة الصيانة", + "Affected Monitors": "الشاشات المتأثرة", + "Pick Affected Monitors...": "اختر الشاشات المتأثرة …", + "Start of maintenance": "بداية الصيانة", + "All Status Pages": "جميع صفحات الحالة", + "Select status pages...": "حدد صفحات الحالة …", + "recurringIntervalMessage": "ركض مرة واحدة كل يوم | قم بالتشغيل مرة واحدة كل يوم {0}", + "affectedMonitorsDescription": "حدد المراقبين المتأثرة بالصيانة الحالية", + "affectedStatusPages": "إظهار رسالة الصيانة هذه على صفحات الحالة المحددة", + "atLeastOneMonitor": "حدد شاشة واحدة على الأقل من المتأثرين", + "passwordNotMatchMsg": "كلمة المرور المتكررة لا تتطابق.", + "notificationDescription": "يجب تعيين الإخطارات إلى شاشة للعمل.", + "keywordDescription": "ابحث في الكلمة الرئيسية في استجابة HTML العادية أو JSON. البحث حساس للحالة.", + "pauseDashboardHome": "وقفة", + "deleteMonitorMsg": "هل أنت متأكد من حذف هذا الشاشة؟", + "deleteMaintenanceMsg": "هل أنت متأكد من حذف هذه الصيانة؟", + "deleteNotificationMsg": "هل أنت متأكد من حذف هذا الإشعار لجميع الشاشات؟", + "dnsPortDescription": "منفذ خادم DNS. الافتراضيات إلى 53. يمكنك تغيير المنفذ في أي وقت.", + "resolverserverDescription": "CloudFlare هو الخادم الافتراضي. يمكنك تغيير خادم المحوّل في أي وقت.", + "rrtypeDescription": "حدد نوع RR الذي تريد مراقبته", + "pauseMonitorMsg": "هل أنت متأكد من أن تتوقف مؤقتًا؟", + "enableDefaultNotificationDescription": "سيتم تمكين هذا الإشعار افتراضيًا للشاشات الجديدة. لا يزال بإمكانك تعطيل الإخطار بشكل منفصل لكل شاشة.", + "clearEventsMsg": "هل أنت متأكد من حذف جميع الأحداث لهذا الشاشة؟", + "clearHeartbeatsMsg": "هل أنت متأكد من حذف جميع دقات القلب لهذا الشاشة؟", + "confirmClearStatisticsMsg": "هل أنت متأكد من أنك تريد حذف جميع الإحصائيات؟", + "importHandleDescription": "اختر 'تخطي موجود' إذا كنت تريد تخطي كل شاشة أو إشعار بنفس الاسم. 'الكتابة فوق' سوف يحذف كل شاشة وإخطار موجود.", + "confirmImportMsg": "هل أنت متأكد من أنك تريد استيراد النسخ الاحتياطي؟ يرجى التحقق من أنك حددت خيار الاستيراد الصحيح.", + "twoFAVerifyLabel": "الرجاء إدخال الرمز المميز الخاص بك للتحقق من 2FA", + "tokenValidSettingsMsg": "الرمز المميز صالح! يمكنك الآن حفظ إعدادات 2FA.", + "confirmEnableTwoFAMsg": "هل أنت متأكد من أنك تريد تمكين 2FA؟", + "confirmDisableTwoFAMsg": "هل أنت متأكد من أنك تريد تعطيل 2FA؟", + "Settings": "إعدادات", + "Dashboard": "لوحة التحكم", + "New Update": "تحديث جديد", + "Language": "لغة", + "Appearance": "مظهر", + "Theme": "سمة", + "General": "عام", + "Primary Base URL": "عنوان URL الأساسي", + "Version": "الإصدار", + "Check Update On GitHub": "تحقق من التحديث على GitHub", + "List": "قائمة", + "Add": "يضيف", + "Add New Monitor": "أضف شاشة جديدة", + "Quick Stats": "إحصائيات سريعة", + "Up": "فوق", + "Down": "أسفل", + "Pending": "قيد الانتظار", + "Unknown": "غير معرّف", + "Pause": "إيقاف مؤقت", + "Name": "الاسم", + "Status": "الحالة", + "DateTime": "الوقت والتاريخ", + "Message": "الرسالة", + "No important events": "لا توجد أحداث مهمة", + "Resume": "استمرار", + "Edit": "تعديل", + "Delete": "حذف", + "Current": "حالي", + "Uptime": "مدة التشغيل", + "Cert Exp.": "تصدير الشهادة", + "Monitor": "مراقب | مراقبات", + "day": "يوم | أيام", + "-day": "-يوم", + "hour": "ساعة", + "-hour": "-ساعة", + "Response": "استجاية", + "Ping": "بينغ", + "Monitor Type": "نوع المراقب", + "Keyword": "كلمة مفتاحية", + "Friendly Name": "اسم معروف", + "URL": "عنوان URL", + "Hostname": "اسم المضيف", + "Port": "المنفذ", + "Heartbeat Interval": "فاصل نبضات القلب", + "Retries": "يحاول مجدداً", + "Heartbeat Retry Interval": "الفاصل الزمني لإعادة محاكمة نبضات القلب", + "Resend Notification if Down X times consecutively": "إعادة تقديم الإخطار إذا انخفض x مرات بالتالي", + "Advanced": "متقدم", + "Upside Down Mode": "وضع أسفل أسفل", + "Max. Redirects": "الأعلى. إعادة التوجيه", + "Accepted Status Codes": "رموز الحالة المقبولة", + "Push URL": "دفع عنوان URL", + "needPushEvery": "يجب عليك استدعاء عنوان URL هذا كل ثانية.", + "pushOptionalParams": "المعلمات الاختيارية", + "Save": "يحفظ", + "Notifications": "إشعارات", + "Not available, please setup.": "غير متوفر من فضلك الإعداد.", + "Setup Notification": "إشعار الإعداد", + "Light": "نور", + "Dark": "داكن", + "Auto": "آلي", + "Theme - Heartbeat Bar": "موضوع - بار نبضات", + "Normal": "طبيعي", + "Bottom": "الأسفل", + "None": "لا أحد", + "Timezone": "وحدة زمنية", + "Search Engine Visibility": "محرك بحث الرؤية", + "Allow indexing": "السماح الفهرسة", + "Discourage search engines from indexing site": "تثبيط محركات البحث من موقع الفهرسة", + "Change Password": "غير كلمة السر", + "Current Password": "كلمة المرور الحالي", + "New Password": "كلمة سر جديدة", + "Repeat New Password": "كرر كلمة المرور الجديدة", + "Update Password": "تطوير كلمة السر", + "Disable Auth": "تعطيل المصادقة", + "Enable Auth": "تمكين المصادقة", + "disableauth.message1": "هل أنت متأكد من أن {disableAuth}؟", + "disable authentication": "تعطيل المصادقة", + "disableauth.message2": "تم تصميمه للسيناريوهات {intendThirdPartyAuth} أمام كوما في وقت التشغيل مثل CloudFlare Access Authelia أو آليات المصادقة الأخرى.", + "where you intend to implement third-party authentication": "حيث تنوي تنفيذ مصادقة الطرف الثالث", + "Please use this option carefully!": "الرجاء استخدام هذا الخيار بعناية!", + "Logout": "تسجيل خروج", + "Leave": "غادر", + "I understand, please disable": "أنا أفهم من فضلك تعطيل", + "Confirm": "يتأكد", + "Yes": "نعم", + "No": "رقم", + "Username": "اسم المستخدم", + "Password": "كلمة المرور", + "Remember me": "تذكرنى", + "Login": "تسجيل الدخول", + "No Monitors, please": "لا شاشات من فضلك", + "add one": "أضف واحدا", + "Notification Type": "نوع إعلام", + "Email": "بريد إلكتروني", + "Test": "امتحان", + "Certificate Info": "معلومات الشهادة", + "Resolver Server": "خادم Resolver", + "Resource Record Type": "نوع سجل الموارد", + "Last Result": "اخر نتيجة", + "Create your admin account": "إنشاء حساب المسؤول الخاص بك", + "Repeat Password": "اعد كلمة السر", + "Import Backup": "استيراد النسخ الاحتياطي", + "Export Backup": "النسخ الاحتياطي تصدير", + "Export": "يصدّر", + "Import": "يستورد", + "respTime": "resp. الوقت (MS)", + "notAvailableShort": "ن/أ", + "Default enabled": "التمكين الافتراضي", + "Apply on all existing monitors": "تنطبق على جميع الشاشات الحالية", + "Create": "خلق", + "Clear Data": "امسح البيانات", + "Events": "الأحداث", + "Heartbeats": "نبضات القلب", + "Auto Get": "الحصول على السيارات", + "backupDescription": "يمكنك النسخ الاحتياطي لجميع الشاشات والإشعارات في ملف JSON.", + "backupDescription2": "ملحوظة", + "backupDescription3": "يتم تضمين البيانات الحساسة مثل الرموز الإخطار في ملف التصدير ؛ يرجى تخزين التصدير بشكل آمن.", + "alertNoFile": "الرجاء تحديد ملف للاستيراد.", + "alertWrongFileType": "الرجاء تحديد ملف JSON.", + "Clear all statistics": "مسح جميع الإحصاءات", + "Skip existing": "تخطي الموجود", + "Overwrite": "الكتابة فوق", + "Options": "خيارات", + "Keep both": "احتفظ بكليهما", + "Verify Token": "تحقق من الرمز المميز", + "Setup 2FA": "الإعداد 2FA", + "Enable 2FA": "تمكين 2FA", + "Disable 2FA": "تعطيل 2FA", + "2FA Settings": "2FA إعدادات", + "Two Factor Authentication": "توثيق ذو عاملين", + "Active": "نشيط", + "Inactive": "غير نشط", + "Token": "رمز", + "Show URI": "أظهر URI", + "Tags": "العلامات", + "Add New below or Select...": "إضافة جديد أدناه أو تحديد …", + "Tag with this name already exist.": "علامة مع هذا الاسم موجود بالفعل.", + "Tag with this value already exist.": "علامة مع هذه القيمة موجودة بالفعل.", + "color": "اللون", + "value (optional)": "القيمة (اختياري)", + "Gray": "رمادي", + "Red": "أحمر", + "Orange": "البرتقالي", + "Green": "لون أخضر", + "Blue": "أزرق", + "Indigo": "النيلي", + "Purple": "نفسجي", + "Pink": "لون القرنفل", + "Custom": "العادة", + "Search...": "يبحث…", + "Avg. Ping": "متوسط. بينغ", + "Avg. Response": "متوسط. إجابة", + "Entry Page": "صفحة الدخول", + "statusPageNothing": "لا شيء هنا الرجاء إضافة مجموعة أو شاشة.", + "No Services": "لا توجد خدمات", + "All Systems Operational": "جميع الأنظمة التشغيلية", + "Partially Degraded Service": "الخدمة المتدهورة جزئيا", + "Degraded Service": "خدمة متدهورة", + "Add Group": "أضف مجموعة", + "Add a monitor": "إضافة شاشة", + "Edit Status Page": "تحرير صفحة الحالة", + "Go to Dashboard": "الذهاب إلى لوحة القيادة", + "Status Page": "صفحة الحالة", + "Status Pages": "صفحات الحالة", + "defaultNotificationName": "تنبيه {الإخطار} ({number})", + "here": "هنا", + "Required": "مطلوب", + "telegram": "برقية", + "ZohoCliq": "Zohocliq", + "Bot Token": "رمز الروبوت", + "wayToGetTelegramToken": "يمكنك الحصول على رمز من {0}.", + "Chat ID": "معرف الدردشة", + "telegramMessageThreadID": "معرف المواضيع", + "supportTelegramChatID": "دعم الدردشة المباشرة / معرف الدردشة للقناة", + "wayToGetTelegramChatID": "يمكنك الحصول على معرف الدردشة الخاص بك عن طريق إرسال رسالة إلى الروبوت والانتقال إلى عنوان URL هذا لعرض Chat_id", + "YOUR BOT TOKEN HERE": "رمز الروبوت الخاص بك هنا", + "chatIDNotFound": "لم يتم العثور على معرف الدردشة ؛ الرجاء إرسال رسالة إلى هذا الروبوت أولاً", + "webhook": "webhook", + "Post URL": "بعد عنوان URL", + "Content Type": "نوع المحتوى", + "webhookJsonDesc": "{0} مفيد لأي خوادم HTTP الحديثة مثل Express.js", + "webhookFormDataDesc": "{multipart} مفيد لـ PHP. سيحتاج JSON إلى تحليل {decodefunction}", + "webhookAdditionalHeadersTitle": "رؤوس إضافية", + "webhookAdditionalHeadersDesc": "يحدد رؤوس إضافية مرسلة مع webhook.", + "smtp": "البريد الإلكتروني (SMTP)", + "secureOptionNone": "لا شيء / startTls (25 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "تجاهل خطأ TLS", + "From Email": "من البريد الإلكترونى", + "emailCustomSubject": "موضوع مخصص", + "To Email": "للبريد الإلكتروني", + "smtpCC": "نسخة", + "smtpBCC": "BCC", + "discord": "خلاف", + "Discord Webhook URL": "Discord Webhook URL", + "wayToGetDiscordURL": "يمكنك الحصول على هذا بالانتقال إلى إعدادات الخادم -> عمليات التكامل -> عرض الخطافات على الويب -> خطاف ويب جديد", + "Bot Display Name": "اسم عرض الروبوت", + "Prefix Custom Message": "بادئة رسالة مخصصة", + "Hello @everyone is...": "مرحبًا {'@'} الجميع…", + "teams": "فرق Microsoft", + "Webhook URL": "Webhook URL", + "wayToGetTeamsURL": "يمكنك معرفة كيفية إنشاء عنوان URL webhook {0}.", + "wayToGetZohoCliqURL": "يمكنك معرفة كيفية إنشاء عنوان URL webhook {0}.", + "signal": "الإشارة", + "Number": "رقم", + "Recipients": "المستلمين", + "needSignalAPI": "تحتاج إلى وجود عميل إشارة مع REST API.", + "wayToCheckSignalURL": "يمكنك التحقق من عنوان URL هذا لعرض كيفية إعداد واحد", + "signalImportant": "مهم", + "gotify": "gotify", + "Application Token": "رمز التطبيق", + "Server URL": "عنوان URL الخادم", + "Priority": "أولوية", + "slack": "تثاقل", + "Icon Emoji": "أيقونة الرموز التعبيرية", + "Channel Name": "اسم القناة", + "Uptime Kuma URL": "UPTIME KUMA URL", + "aboutWebhooks": "مزيد من المعلومات حول Webhooks ON", + "aboutChannelName": "أدخل اسم القناة في حقل اسم القناة {0} إذا كنت تريد تجاوز قناة WebHook. السابق", + "aboutKumaURL": "إذا تركت حقل URL في وقت التشغيل KUMA فارغًا ، فسيتم افتراضيًا إلى صفحة GitHub Project.", + "emojiCheatSheet": "ورقة الغش في الرموز التعبيرية", + "rocket.chat": "صاروخ", + "pushover": "مهمة سهلة", + "pushy": "انتهازي", + "PushByTechulus": "دفع بواسطة Techulus", + "octopush": "أوكتوبوش", + "promosms": "الترويجيات", + "clicksendsms": "نقرات SMS", + "lunasea": "لوناسيا", + "apprise": "إبلاغ (دعم 50+ خدمات الإخطار)", + "GoogleChat": "دردشة Google", + "pushbullet": "حماس", + "Kook": "كووك", + "wayToGetKookBotToken": "قم بإنشاء تطبيق واحصل على رمز الروبوت الخاص بك على {0}", + "wayToGetKookGuildID": "قم بتشغيل 'وضع المطور' في إعداد Kook وانقر بزر الماوس الأيمن على النقابة للحصول على معرفه", + "Guild ID": "معرف النقابة", + "line": "خط", + "mattermost": "المادة", + "User Key": "مفتاح المستخدم", + "Device": "جهاز", + "Message Title": "عنوان الرسالة", + "Notification Sound": "صوت الإشعار", + "More info on": "مزيد من المعلومات حول", + "pushoverDesc1": "أولوية الطوارئ (2) لها مهلة افتراضية 30 ثانية بين إعادة المحاولة وستنتهي صلاحيتها بعد ساعة واحدة.", + "pushoverDesc2": "إذا كنت ترغب في إرسال إشعارات إلى أجهزة مختلفة ، قم بملء حقل الجهاز.", + "SMS Type": "نوع الرسائل القصيرة", + "octopushTypePremium": "قسط (سريع - موصى به للتنبيه)", + "octopushTypeLowCost": "التكلفة المنخفضة (بطيئة - تم حظرها أحيانًا بواسطة المشغل)", + "checkPrice": "تحقق من الأسعار {0}", + "apiCredentials": "بيانات اعتماد API", + "octopushLegacyHint": "هل تستخدم الإصدار القديم من Octopush (2011-2020) أو الإصدار الجديد؟", + "Check octopush prices": "تحقق من أسعار Octopush {0}.", + "octopushPhoneNumber": "رقم الهاتف (تنسيق intl على سبيل المثال", + "octopushSMSSender": "اسم مرسل الرسائل القصيرة", + "LunaSea Device ID": "معرف جهاز Lunasea", + "Apprise URL": "إبلاغ عنوان URL", + "Example": "مثال", + "Read more:": "{0} :قراءة المزيد", + "Status:": "{0} :حالة", + "Read more": "قراءة المزيد", + "appriseInstalled": "تم تثبيت Prosise.", + "appriseNotInstalled": "الإبرام غير مثبت. {0}", + "Access Token": "رمز وصول", + "Channel access token": "قناة الوصول إلى الرمز", + "Line Developers Console": "تحكم المطورين", + "lineDevConsoleTo": "وحدة المطورين Line Console - {0}", + "Basic Settings": "الإعدادات الأساسية", + "User ID": "معرف المستخدم", + "Messaging API": "واجهة برمجة تطبيقات المراسلة", + "wayToGetLineChannelToken": "قم أولاً بالوصول إلى {0} إنشاء مزود وقناة (واجهة برمجة تطبيقات المراسلة) ، ثم يمكنك الحصول على رمز الوصول إلى القناة ومعرف المستخدم من عناصر القائمة المذكورة أعلاه.", + "Icon URL": "url url icon", + "aboutIconURL": "يمكنك توفير رابط لصورة في \"Icon URL\" لتجاوز صورة الملف الشخصي الافتراضي. لن يتم استخدامه إذا تم تعيين رمز رمز رمز.", + "aboutMattermostChannelName": "يمكنك تجاوز القناة الافتراضية التي تنشرها WebHook من خلال إدخال اسم القناة في \"Channel Name\" الحقل. يجب تمكين هذا في إعدادات Webhook Mattern. السابق", + "matrix": "مصفوفة", + "promosmsTypeEco": "SMS Eco - رخيصة ولكن بطيئة وغالبًا ما تكون محملة. يقتصر فقط على المستفيدين البولنديين.", + "promosmsTypeFlash": "SMS Flash - سيتم عرض الرسالة تلقائيًا على جهاز المستلم. يقتصر فقط على المستفيدين البولنديين.", + "promosmsTypeFull": "SMS Full - Tier Premium SMS يمكنك استخدام اسم المرسل الخاص بك (تحتاج إلى تسجيل الاسم أولاً). موثوقة للتنبيهات.", + "promosmsTypeSpeed": "سرعة الرسائل القصيرة - أولوية قصوى في النظام. سريع وموثوق للغاية ولكنه مكلف (حوالي مرتين من الرسائل القصيرة السعر الكامل).", + "promosmsPhoneNumber": "رقم الهاتف (للمستلم البولندي ، يمكنك تخطي رموز المنطقة)", + "promosmsSMSSender": "اسم مرسل الرسائل القصيرة", + "promosmsAllowLongSMS": "السماح الرسائل القصيرة الطويلة", + "Feishu WebHookUrl": "Feishu Webhookurl", + "matrixHomeserverURL": "عنوان URL HomeServer (مع HTTP (S)", + "Internal Room Id": "معرف الغرفة الداخلية", + "matrixDesc1": "يمكنك العثور على معرف الغرفة الداخلي من خلال البحث في القسم المتقدم من إعدادات الغرفة في عميل Matrix الخاص بك. يجب أن تبدو مثل! QMDRCPUIFLWSFJXYE6", + "matrixDesc2": "يوصى بشدة بإنشاء مستخدم جديد ولا تستخدم رمز الوصول إلى مستخدم Matrix الخاص بك لأنه سيتيح الوصول الكامل إلى حسابك وجميع الغرف التي انضمت إليها. بدلاً من ذلك ، قم بإنشاء مستخدم جديد ودعوته فقط إلى الغرفة التي تريد تلقيها الإشعار فيها. يمكنك الحصول على رمز الوصول عن طريق تشغيل {0}", + "Method": "طريقة", + "Body": "الجسم", + "Headers": "الرؤوس", + "PushUrl": "دفع عنوان URL", + "HeadersInvalidFormat": "رؤوس الطلبات غير صالحة JSON", + "BodyInvalidFormat": "هيئة الطلب غير صالحة JSON", + "Monitor History": "مراقبة التاريخ", + "clearDataOlderThan": "الحفاظ على بيانات سجل المراقبة للأيام {0}.", + "PasswordsDoNotMatch": "كلمة المرور غير مطابقة.", + "records": "السجلات", + "One record": "سجل واحد", + "steamApiKeyDescription": "لمراقبة خادم لعبة Steam ، تحتاج إلى مفتاح Steam Web-API. يمكنك تسجيل مفتاح API الخاص بك هنا", + "Current User": "المستخدم الحالي", + "topic": "عنوان", + "topicExplanation": "موضوع MQTT لرصد", + "successMessage": "نجاح رسالة", + "successMessageExplanation": "رسالة MQTT التي ستعتبر نجاحًا", + "recent": "الأخيرة", + "Done": "فعله", + "Info": "معلومات", + "Security": "حماية", + "Steam API Key": "مفتاح API Steam", + "Shrink Database": "تقلص قاعدة البيانات", + "Pick a RR-Type...": "اختر نوع RR …", + "Pick Accepted Status Codes...": "اختر أكواد الحالة المقبولة …", + "Default": "تقصير", + "HTTP Options": "خيارات HTTP", + "Create Incident": "إنشاء حادث", + "Title": "لقب", + "Content": "المحتوى", + "Style": "أسلوب", + "info": "معلومات", + "warning": "تحذير", + "danger": "خطر", + "error": "خطأ", + "critical": "شديد الأهمية", + "primary": "الأولية", + "light": "نور", + "dark": "ظلام", + "Post": "بريد", + "Please input title and content": "الرجاء إدخال العنوان والمحتوى", + "Created": "مخلوق", + "Last Updated": "التحديث الاخير", + "Unpin": "إلغاء", + "Switch to Light Theme": "التبديل إلى موضوع الضوء", + "Switch to Dark Theme": "التبديل إلى موضوع الظلام", + "Show Tags": "أضهر العلامات", + "Hide Tags": "إخفاء العلامات", + "Description": "وصف", + "No monitors available.": "لا شاشات المتاحة.", + "Add one": "أضف واحدا", + "No Monitors": "لا شاشات", + "Untitled Group": "مجموعة بلا عنوان", + "Services": "خدمات", + "Discard": "تجاهل", + "Cancel": "يلغي", + "Powered by": "مشغل بواسطة", + "serwersms": "Serwersms.pl", + "serwersmsAPIUser": "اسم مستخدم API (بما في ذلك بادئة WebAPI_)", + "serwersmsAPIPassword": "كلمة مرور API", + "serwersmsPhoneNumber": "رقم الهاتف", + "serwersmsSenderName": "اسم مرسل الرسائل القصيرة (مسجل عبر بوابة العملاء)", + "smseagle": "smseagle", + "smseagleTo": "أرقام الهواتف)", + "smseagleGroup": "اسم مجموعة كتب الهاتف (S)", + "smseagleContact": "كتاب الاتصال اسم (S)", + "smseagleRecipientType": "نوع المستلم", + "smseagleRecipient": "المتلقي (المتلقيين) (يجب فصل المتعددة مع فاصلة)", + "smseagleToken": "API وصول الرمز المميز", + "smseagleUrl": "عنوان URL لجهاز SMSEGLE الخاص بك", + "smseagleEncoding": "إرسال Unicode", + "smseaglePriority": "أولوية الرسالة (0-9 افتراضي = 0)", + "stackfield": "Stackfield", + "Customize": "يعدل أو يكيف", + "Custom Footer": "تذييل مخصص", + "Custom CSS": "لغة تنسيق ويب حسب الطلب", + "smtpDkimSettings": "إعدادات DKIM", + "smtpDkimDesc": "يرجى الرجوع إلى Nodemailer dkim {0} للاستخدام.", + "documentation": "توثيق", + "smtpDkimDomain": "اسم النطاق", + "smtpDkimKeySelector": "المحدد الرئيسي", + "smtpDkimPrivateKey": "مفتاح سري", + "smtpDkimHashAlgo": "خوارزمية التجزئة (اختياري)", + "smtpDkimheaderFieldNames": "مفاتيح الرأس للتوقيع (اختياري)", + "smtpDkimskipFields": "مفاتيح الرأس لا توقيع (اختياري)", + "wayToGetPagerDutyKey": "يمكنك الحصول على هذا عن طريق الانتقال إلى الخدمة -> دليل الخدمة -> (حدد خدمة) -> تكامل -> إضافة التكامل. هنا يمكنك البحث عن \"Events API V2\". مزيد من المعلومات {0}", + "Integration Key": "مفتاح التكامل", + "Integration URL": "URL تكامل", + "Auto resolve or acknowledged": "حل السيارات أو الاعتراف به", + "do nothing": "لا تفعل شيئا", + "auto acknowledged": "اعترف السيارات", + "auto resolve": "عزم السيارات", + "gorush": "جورش", + "alerta": "أليتا", + "alertaApiEndpoint": "نقطة نهاية API", + "alertaEnvironment": "بيئة", + "alertaApiKey": "مفتاح API", + "alertaAlertState": "حالة التنبيه", + "alertaRecoverState": "استعادة الدولة", + "deleteStatusPageMsg": "هل أنت متأكد من حذف صفحة الحالة هذه؟", + "Proxies": "وكلاء", + "default": "تقصير", + "enabled": "تمكين", + "setAsDefault": "تعيين كافتراضي", + "deleteProxyMsg": "هل أنت متأكد من حذف هذا الوكيل لجميع الشاشات؟", + "proxyDescription": "يجب تعيين الوكلاء إلى شاشة للعمل.", + "enableProxyDescription": "لن يؤثر هذا الوكيل على طلبات الشاشة حتى يتم تنشيطه. يمكنك التحكم مؤقتًا في تعطيل الوكيل من جميع الشاشات حسب حالة التنشيط.", + "setAsDefaultProxyDescription": "سيتم تمكين هذا الوكيل افتراضيًا للشاشات الجديدة. لا يزال بإمكانك تعطيل الوكيل بشكل منفصل لكل شاشة.", + "Certificate Chain": "سلسلة الشهادة", + "Valid": "صالح", + "Invalid": "غير صالح", + "AccessKeyId": "معرف AccessKey", + "SecretAccessKey": "Accesskey Secret", + "PhoneNumbers": "أرقام الهواتف", + "TemplateCode": "TemplateCode", + "SignName": "اسم تسجيل الدخول", + "Sms template must contain parameters: ": "يجب أن يحتوي قالب الرسائل القصيرة على معلمات:", + "Bark Endpoint": "نقطة نهاية اللحاء", + "Bark Group": "مجموعة اللحاء", + "Bark Sound": "صوت اللحاء", + "WebHookUrl": "webhookurl", + "SecretKey": "Secretkey", + "For safety, must use secret key": "للسلامة يجب استخدام المفتاح السري", + "Device Token": "رمز الجهاز", + "Platform": "منصة", + "Huawei": "هواوي", + "High": "عالٍ", + "Retry": "إعادة المحاولة", + "Topic": "عنوان", + "WeCom Bot Key": "WECOM BOT KEY", + "Setup Proxy": "وكيل الإعداد", + "Proxy Protocol": "بروتوكول الوكيل", + "Proxy Server": "مخدم بروكسي", + "Proxy server has authentication": "خادم الوكيل لديه مصادقة", + "User": "المستعمل", + "Installed": "المثبتة", + "Not installed": "غير مثبت", + "Running": "جري", + "Not running": "لا يعمل", + "Remove Token": "إزالة الرمز المميز", + "Start": "بداية", + "Stop": "قف", + "Uptime Kuma": "وقت التشغيل كوما", + "Add New Status Page": "أضف صفحة حالة جديدة", + "Slug": "سبيكة", + "Accept characters": "قبول الشخصيات", + "startOrEndWithOnly": "ابدأ أو ينتهي بـ {0} فقط", + "No consecutive dashes": "لا شرطات متتالية", + "Next": "التالي", + "The slug is already taken. Please choose another slug.": "تم أخذ سبيكة بالفعل. الرجاء اختيار سبيكة أخرى.", + "No Proxy": "لا الوكيل", + "Authentication": "المصادقة", + "HTTP Basic Auth": "HTTP الأساسي Auth", + "New Status Page": "صفحة حالة جديدة", + "Page Not Found": "الصفحة غير موجودة", + "Reverse Proxy": "وكيل عكسي", + "Backup": "دعم", + "About": "عن", + "wayToGetCloudflaredURL": "(قم بتنزيل CloudFlared من {0})", + "cloudflareWebsite": "موقع CloudFlare", + "Message:": ":رسالة", + "Don't know how to get the token? Please read the guide": "لا أعرف كيف تحصل على الرمز المميز؟ يرجى قراءة الدليل", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "قد يضيع الاتصال الحالي إذا كنت تتصل حاليًا عبر نفق CloudFlare. هل أنت متأكد تريد إيقافها؟ اكتب كلمة المرور الحالية لتأكيدها.", + "HTTP Headers": "رؤوس HTTP", + "Trust Proxy": "الوكيل الثقة", + "Other Software": "برامج أخرى", + "For example: nginx, Apache and Traefik.": "على سبيل المثال: nginx و Apache و Traefik.", + "Please read": "يرجى القراءة", + "Subject": "موضوع", + "Valid To": "صالحة ل", + "Days Remaining": "الأيام المتبقية", + "Issuer": "المصدر", + "Fingerprint": "بصمة", + "No status pages": "لا صفحات الحالة", + "Domain Name Expiry Notification": "اسم النطاق إشعار انتهاء الصلاحية", + "Proxy": "الوكيل", + "Date Created": "تاريخ الإنشاء", + "HomeAssistant": "مساعد المنزل", + "onebotHttpAddress": "OneBot HTTP عنوان", + "onebotMessageType": "نوع رسالة OneBot", + "onebotGroupMessage": "مجموعة", + "onebotPrivateMessage": "خاص", + "onebotUserOrGroupId": "معرف المجموعة/المستخدم", + "onebotSafetyTips": "للسلامة يجب ضبط الرمز المميز للوصول", + "PushDeer Key": "مفتاح PushDeer", + "Footer Text": "نص تذييل", + "Show Powered By": "عرض مدعوم من قبل", + "Domain Names": "أسماء المجال", + "signedInDisp": "وقعت في {0}", + "signedInDispDisabled": "معاق المصادقة.", + "RadiusSecret": "سر نصف القطر", + "RadiusSecretDescription": "السر المشترك بين العميل والخادم", + "RadiusCalledStationId": "يسمى معرف المحطة", + "RadiusCalledStationIdDescription": "معرف الجهاز المتصل", + "RadiusCallingStationId": "معرف محطة الاتصال", + "RadiusCallingStationIdDescription": "معرف جهاز الاتصال", + "Certificate Expiry Notification": "إشعار انتهاء الصلاحية", + "API Username": "اسم المستخدم API", + "API Key": "مفتاح API", + "Recipient Number": "رقم المستلم", + "From Name/Number": "من الاسم/الرقم", + "Leave blank to use a shared sender number.": "اترك فارغًا لاستخدام رقم المرسل المشترك.", + "Octopush API Version": "إصدار Octopush API", + "Legacy Octopush-DM": "Legacy Octopush-DM", + "endpoint": "نقطة النهاية", + "octopushAPIKey": "\"API key\" from HTTP API بيانات اعتماد في لوحة التحكم", + "octopushLogin": "\"Login\" من بيانات اعتماد API HTTP في لوحة التحكم", + "promosmsLogin": "اسم تسجيل الدخول API", + "promosmsPassword": "كلمة مرور API", + "pushoversounds pushover": "سداد (افتراضي)", + "pushoversounds bike": "دراجة هوائية", + "pushoversounds bugle": "بوق", + "pushoversounds cashregister": "ماكينة تسجيل المدفوعات النقدية", + "pushoversounds classical": "كلاسيكي", + "pushoversounds cosmic": "كونية", + "pushoversounds falling": "هبوط", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "واردة", + "pushoversounds intermission": "استراحة", + "pushoversounds magic": "سحر", + "pushoversounds mechanical": "ميكانيكي", + "pushoversounds pianobar": "شريط البيانو", + "pushoversounds siren": "صفارة إنذار", + "pushoversounds spacealarm": "إنذار الفضاء", + "pushoversounds tugboat": "قارب السحب", + "pushoversounds alien": "إنذار أجنبي (طويل)", + "pushoversounds climb": "تسلق (طويل)", + "pushoversounds persistent": "مستمر (طويل)", + "pushoversounds echo": "صدى مهووس (طويل)", + "pushoversounds updown": "صعودا (طويلة)", + "pushoversounds vibrate": "يهتز فقط", + "pushoversounds none": "لا شيء (صامت)", + "pushyAPIKey": "مفتاح API السري", + "pushyToken": "رمز الجهاز", + "Show update if available": "عرض التحديث إذا كان ذلك متاحًا", + "Also check beta release": "تحقق أيضًا من الإصدار التجريبي", + "Using a Reverse Proxy?": "باستخدام وكيل عكسي؟", + "Check how to config it for WebSocket": "تحقق من كيفية تكوينه لـ WebSocket", + "Steam Game Server": "خادم لعبة البخار", + "Most likely causes": "الأسباب المرجحة", + "The resource is no longer available.": "لم يعد المورد متاحًا.", + "There might be a typing error in the address.": "قد يكون هناك خطأ مطبعي في العنوان.", + "What you can try": "ماذا تستطيع أن تجرب", + "Retype the address.": "اعد كتابة العنوان.", + "Go back to the previous page.": "عد للصفحة السابقة.", + "Coming Soon": "قريبا", + "wayToGetClickSendSMSToken": "يمكنك الحصول على اسم مستخدم API ومفتاح API من {0}.", + "Connection String": "سلسلة الاتصال", + "Query": "استفسار", + "settingsCertificateExpiry": "شهادة TLS انتهاء الصلاحية", + "certificationExpiryDescription": "شاشات HTTPS تضيء عندما تنتهي شهادة TLS في", + "Setup Docker Host": "إعداد مضيف Docker", + "Connection Type": "نوع الاتصال", + "Docker Daemon": "Docker Daemon", + "deleteDockerHostMsg": "هل أنت متأكد من حذف مضيف Docker لجميع الشاشات؟", + "socket": "قابس كهرباء", + "tcp": "TCP / HTTP", + "Docker Container": "حاوية Docker", + "Container Name / ID": "اسم الحاوية / معرف", + "Docker Host": "مضيف Docker", + "Docker Hosts": "مضيفي Docker", + "ntfy Topic": "موضوع ntfy", + "Domain": "اِختِصاص", + "Workstation": "محطة العمل", + "disableCloudflaredNoAuthMsg": "أنت في وضع مصادقة لا توجد كلمة مرور غير مطلوبة.", + "trustProxyDescription": "ثق في رؤوس \"X-Forwarded- *\". إذا كنت ترغب في الحصول على عنوان IP الصحيح للعميل وكان Uptime Kuma خلف وكيل مثل Nginx أو Apache ، فيجب عليك تمكين هذا.", + "wayToGetLineNotifyToken": "يمكنك الحصول على رمز الوصول من {0}", + "Examples": "أمثلة", + "Home Assistant URL": "Home Assistant URL", + "Long-Lived Access Token": "الرمز المميز للوصول منذ فترة طويلة", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "يمكن إنشاء رمز الوصول منذ فترة طويلة عن طريق النقر على اسم ملف التعريف الخاص بك (أسفل اليسار) والتمرير إلى الأسفل ثم انقر فوق إنشاء الرمز المميز.", + "Notification Service": "خدمة الإخطار", + "default: notify all devices": "الافتراضي: إخطار جميع الأجهزة", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "يمكن العثور على قائمة بخدمات الإخطار في المساعد المنزلي ضمن \"Developer Tools > Services\" ابحث عن \"notification\" للعثور على اسم جهازك/هاتفك.", + "Automations can optionally be triggered in Home Assistant": "يمكن أن يتم تشغيل الأتمتة اختياريًا في مساعد المنزل", + "Trigger type": "نوع الزناد", + "Event type": "نوع الحدث", + "Event data": "بيانات الحدث", + "Then choose an action, for example switch the scene to where an RGB light is red.": "ثم اختر إجراءً على سبيل المثال قم بتبديل المشهد إلى حيث يكون ضوء RGB أحمر.", + "Frontend Version": "إصدار الواجهة الأمامية", + "Frontend Version do not match backend version!": "إصدار Frontend لا يتطابق مع الإصدار الخلفي!", + "Base URL": "عنوان URL الأساسي", + "goAlertInfo": "الهدف هو تطبيق مفتوح المصدر لجدولة الجدولة التلقائية والإشعارات (مثل الرسائل القصيرة أو المكالمات الصوتية). إشراك الشخص المناسب تلقائيًا بالطريقة الصحيحة وفي الوقت المناسب! {0}", + "goAlertIntegrationKeyInfo": "احصل على مفتاح تكامل API العام للخدمة في هذا التنسيق \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" عادةً قيمة المعلمة الرمزية لعنوان url المنسق.", + "goAlert": "المرمى", + "backupOutdatedWarning": "مهمل: نظرًا لأنه تمت إضافة الكثير من الميزات وأن ميزة النسخ الاحتياطي هذه لم يتم الحفاظ عليها قليلاً ، فلا يمكنها إنشاء نسخة احتياطية كاملة أو استعادتها.", + "backupRecommend": "يرجى النسخ الاحتياطي لحجم الصوت أو مجلد البيانات (./data/) مباشرة بدلاً من ذلك.", + "Optional": "اختياري", + "squadcast": "القاء فريقي", + "SendKey": "Sendkey", + "SMSManager API Docs": "مستندات SMSManager API", + "Gateway Type": "نوع البوابة", + "SMSManager": "smsmanager", + "You can divide numbers with": "يمكنك تقسيم الأرقام مع", + "or": "أو", + "recurringInterval": "فترة", + "Recurring": "يتكرر", + "strategyManual": "نشط/غير نشط يدويًا", + "warningTimezone": "إنه يستخدم المنطقة الزمنية للخادم", + "weekdayShortMon": "الاثنين", + "weekdayShortTue": "الثلاثاء", + "weekdayShortWed": "تزوج", + "weekdayShortThu": "الخميس", + "weekdayShortFri": "الجمعة", + "weekdayShortSat": "جلس", + "weekdayShortSun": "شمس", + "dayOfWeek": "يوم من الأسبوع", + "dayOfMonth": "يوم من الشهر", + "lastDay": "بالأمس", + "lastDay1": "آخر يوم من الشهر", + "lastDay2": "الثاني في اليوم الأخير من الشهر", + "lastDay3": "الثالث في اليوم الأخير من الشهر", + "lastDay4": "الرابع في اليوم الأخير من الشهر", + "No Maintenance": "لا صيانة", + "pauseMaintenanceMsg": "هل أنت متأكد من أن تتوقف مؤقتًا؟", + "maintenanceStatus-under-maintenance": "تحت الصيانة", + "maintenanceStatus-inactive": "غير نشط", + "maintenanceStatus-scheduled": "المقرر", + "maintenanceStatus-ended": "انتهى", + "maintenanceStatus-unknown": "مجهول", + "Display Timezone": "عرض المنطقة الزمنية", + "Server Timezone": "المنطقة الزمنية الخادم", + "statusPageMaintenanceEndDate": "نهاية", + "IconUrl": "url url icon", + "Enable DNS Cache": "تمكين ذاكرة التخزين المؤقت DNS", + "Enable": "يُمكَِن", + "Disable": "إبطال", + "dnsCacheDescription": "قد لا يعمل في بعض بيئات IPv6 تعطيله إذا واجهت أي مشكلات.", + "Single Maintenance Window": "نافذة صيانة واحدة", + "Maintenance Time Window of a Day": "نافذة وقت الصيانة لليوم", + "Effective Date Range": "نطاق التاريخ السريع", + "Schedule Maintenance": "جدولة الصيانة", + "Date and Time": "التاريخ و الوقت", + "DateTime Range": "نطاق DateTime", + "Strategy": "إستراتيجية", + "Free Mobile User Identifier": "معرف مستخدم الهاتف المحمول المجاني", + "Free Mobile API Key": "مفتاح واجهة برمجة تطبيقات مجانية للهاتف المحمول", + "Enable TLS": "تمكين TLS", + "Proto Service Name": "اسم خدمة البروتو", + "Proto Method": "طريقة البروتو", + "Proto Content": "محتوى proto", + "Economy": "اقتصاد", + "Lowcost": "تكلفة منخفضة", + "high": "عالي", + "General Monitor Type": "نوع الشاشة العامة", + "Passive Monitor Type": "نوع الشاشة السلبي", + "Specific Monitor Type": "نوع شاشة محدد", + "dataRetentionTimeError": "يجب أن تكون فترة الاستبقاء 0 أو أكبر", + "infiniteRetention": "ضبط على 0 للاحتفاظ لا نهائي.", + "confirmDeleteTagMsg": "هل أنت متأكد من أنك تريد حذف هذه العلامة؟ لن يتم حذف الشاشات المرتبطة بهذه العلامة.", + "Custom Monitor Type": "نوع الشاشة المخصص", + "Game": "لعبة", + "Don't know how to get the token? Please read the guide:": "لا أعرف كيفية الحصول على الرمز المميز؟ يرجى قراءة الدليل:", + "Subject:": "موضوع:", + "Valid To:": "صالحة ل:", + "Days Remaining:": "الأيام المتبقية:", + "Issuer:": "المُصدر:", + "Fingerprint:": "بصمة:", + "Most likely causes:": "الأسباب المرجحة:", + "Help": "يساعد", + "Accept characters:": "قبول الأحرف:", + "plugin": "البرنامج المساعد | الإضافات", + "install": "ثَبَّتَ", + "installing": "التثبيت", + "uninstall": "الغاء التثبيت", + "uninstalling": "إلغاء التثبيت", + "loadingError": "لا يمكن جلب البيانات ، يرجى المحاولة مرة أخرى في وقت لاحق.", + "Example:": "مثال: {0}", + "Google Analytics ID": "معرف Google Analytics", + "markdownSupported": "دعم صيغة Markdown", + "Edit Tag": "تحرير العلامة", + "Server Address": "عنوان المستقبل", + "Learn More": "يتعلم أكثر", + "Automations can optionally be triggered in Home Assistant:": "يمكن تشغيل الأتمتة اختياريًا في Home Assistant:", + "Trigger type:": "نوع الزناد:", + "Event type:": "نوع الحدث:", + "Event data:": "بيانات الحدث:", + "More info on:": "مزيد من المعلومات حول: {0}", + "What you can try:": "ماذا تستطيع أن تجرب:", + "Packet Size": "حجم الحزمة", + "confirmUninstallPlugin": "هل أنت متأكد من أنك تريد إلغاء تثبيت هذا المكون الإضافي؟", + "settingUpDatabaseMSG": "يرجى الإنتظار ، نقوم بإعداد قاعدة البيانات و قد يستغرق الأمر بعض الوقت.", + "setupDatabaseChooseDatabase": "ما هي قاعدة البيانات التي تريد استخدامها؟", + "dbName": "إسم قاعدة البيانات" +} diff --git a/src/lang/ar.json b/src/lang/ar.json new file mode 100644 index 0000000..22deb4e --- /dev/null +++ b/src/lang/ar.json @@ -0,0 +1,848 @@ +{ + "Edit": "تعديل", + "Delete": "حذف", + "Current": "حالي", + "Uptime": "مدة التشغيل", + "Monitor": "مراقبة | مراقبات", + "day": "يوم | أيام", + "-day": "-يوم", + "hour": "ساعة", + "-hour": "-ساعة", + "Response": "استجاية", + "Ping": "بينغ", + "Monitor Type": "نوع المراقبة", + "Cert Exp.": "انتهاء صلاحية شهادة الأمان SSL.", + "Theme - Heartbeat Bar": "موضوع - بار نبضات", + "Normal": "طبيعي", + "Bottom": "الأسفل", + "None": "لا أحد", + "Current Password": "كلمة المرور الحالي", + "New Password": "كلمة سر جديدة", + "Repeat New Password": "كرر كلمة المرور الجديدة", + "Update Password": "تطوير كلمة السر", + "Disable Auth": "تعطيل المصادقة", + "Enable Auth": "تمكين المصادقة", + "disableauth.message1": "هل أنت متأكد من أن {disableAuth}؟", + "disable authentication": "تعطيل المصادقة", + "disableauth.message2": "تم تصميمه للسيناريوهات {intendThirdPartyAuth} أمام كوما في وقت التشغيل مثل CloudFlare Access Authelia أو آليات المصادقة الأخرى.", + "where you intend to implement third-party authentication": "حيث تنوي تنفيذ مصادقة الطرف الثالث", + "Please use this option carefully!": "الرجاء استخدام هذا الخيار بعناية!", + "Logout": "تسجيل خروج", + "Leave": "غادر", + "I understand, please disable": "أنا أفهم من فضلك تعطيل", + "Confirm": "يتأكد", + "Yes": "نعم", + "No": "رقم", + "Username": "اسم المستخدم", + "Password": "كلمة المرور", + "Remember me": "تذكرنى", + "Login": "تسجيل الدخول", + "No Monitors, please": "بدون مراقبة من فضلك", + "alertNoFile": "الرجاء تحديد ملف للاستيراد.", + "Skip existing": "تخطي الموجود", + "Search...": "يبحث…", + "Avg. Ping": "متوسط. بينغ", + "Avg. Response": "متوسط. إجابة", + "Entry Page": "صفحة الدخول", + "statusPageNothing": "لا شيء هنا الرجاء إضافة مجموعة أو مراقبة.", + "No Services": "لا توجد خدمات", + "All Systems Operational": "جميع الأنظمة نشطة", + "Partially Degraded Service": "الخدمة المتدهورة جزئيا", + "Degraded Service": "خدمة متدهورة", + "Add Group": "أضف مجموعة", + "Add a monitor": "إضافة مراقبة", + "Edit Status Page": "تحرير صفحة الحالة", + "Go to Dashboard": "الذهاب إلى لوحة القيادة", + "Status Page": "صفحة الحالة", + "Application Token": "رمز التطبيق", + "Server URL": "عنوان URL الخادم", + "Priority": "أولوية", + "Read more": "قراءة المزيد", + "topic": "عنوان", + "Last Updated": "التحديث الاخير", + "Unpin": "إلغاء", + "Show Tags": "أضهر العلامات", + "Add one": "أضف واحدا", + "wayToGetCloudflaredURL": "(قم بتنزيل CloudFlared من {0})", + "cloudflareWebsite": "موقع CloudFlare", + "Message:": "رسالة:", + "Don't know how to get the token? Please read the guide:": "لا أعرف كيفية الحصول على الرمز المميز؟ يرجى قراءة الدليل:", + "telegramSendSilently": "أرسل بصمت", + "telegramSendSilentlyDescription": "ترسل الرسالة بصمت ويتلقى المستخدمون إشعارا بدون صوت.", + "Enable": "يُمكَِن", + "notificationRegional": "إقليمي", + "Clone": "استنسخ", + "cloneOf": "مُستنسَخ من {0}", + "grpcMethodDescription": "إسم الدالة يتم تحويله إلى تنسيق الجمل السفلي مثل sayHello, check, etc.", + "acceptedStatusCodesDescription": "حدد رموز الحالة التي تعتبر استجابة ناجحة.", + "deleteNotificationMsg": "هل أنت متأكد من حذف هذا الإشعار لجميع الشاشات؟", + "dnsPortDescription": "منفذ خادم DNS. الافتراضيات إلى 53. يمكنك تغيير المنفذ في أي وقت.", + "pauseMonitorMsg": "هل أنت متأكد من أن تتوقف مؤقتًا؟", + "API Keys": "مفاتيح API", + "Expiry": "نهاية الصلاحية", + "Expiry date": "تاريخ نهاية الصلاحية", + "Continue": "مواصلة", + "Add Another": "إضافة آخر", + "Add API Key": "أضف مفتاح API", + "apiKey-active": "نشط", + "apiKey-expired": "منتهي الصلاحية", + "Generate": "توليد", + "Settings": "الإعدادات", + "Dashboard": "لوحة التحكم", + "Help": "المساعدة", + "New Update": "تحديث جديد متوفر", + "Language": "اللغة", + "Appearance": "المظهر", + "Theme": "الحُلة", + "General": "العامة", + "Version": "الإصدار", + "Primary Base URL": "الرابط التشعبي الأساسي", + "Check Update On GitHub": "التحقق من التحديث على GitHub", + "Add New Monitor": "أضف مراقبة جديدة", + "Quick Stats": "إحصائيات سريعة", + "Pending": "قيد الانتظار", + "General Monitor Type": "نوع المراقبة العامة", + "Passive Monitor Type": "نوع المراقبة السلبي", + "Specific Monitor Type": "نوع المراقبة المحدد", + "markdownSupported": "يدعم هذا النظام صيغة Markdown. في حال استخدام لغة HTML، تجنب اضافة المسافات في البداية لمنع مشاكل التنسيق.", + "pauseDashboardHome": "وقفة", + "Pause": "إيقاف مؤقت", + "Name": "الاسم", + "Status": "الحالة", + "DateTime": "الوقت والتاريخ", + "Message": "الرسالة", + "No important events": "لا توجد أحداث مهمة", + "Resume": "استمرار", + "Keyword": "كلمة مفتاحية", + "Friendly Name": "اسم معروف", + "URL": "عنوان URL", + "Hostname": "اسم المضيف", + "Port": "المنفذ", + "Heartbeat Interval": "فاصل نبضات القلب", + "Add": "إضافة", + "Up": "متصل", + "Down": "غير متصل", + "Maintenance": "الصيانة", + "Unknown": "مجهول", + "Retries": "يحاول مجدداً", + "Heartbeat Retry Interval": "الفاصل الزمني لإعادة محاكمة نبضات القلب", + "Resend Notification if Down X times consecutively": "إعادة تقديم الإخطار إذا انخفض x مرات بالتالي", + "Advanced": "متقدم", + "checkEverySecond": "تحقق من كل {0} ثانية", + "retryCheckEverySecond": "أعد محاولة كل {0} ثانية", + "resendEveryXTimes": "إعادة تقديم كل {0} مرات", + "resendDisabled": "إعادة الالتزام بالتعطيل", + "retriesDescription": "الحد الأقصى لإعادة المحاولة قبل تمييز الخدمة على أنها لأسفل وإرسال إشعار", + "ignoreTLSError": "تجاهل أخطاء TLS/SSL لمواقع HTTPS", + "upsideDownModeDescription": "اقلب الحالة رأسًا على عقب. إذا كانت الخدمة قابلة للوصول إلى أسفل.", + "maxRedirectDescription": "الحد الأقصى لعدد إعادة التوجيه لمتابعة. ضبط على 0 لتعطيل إعادة التوجيه.", + "Upside Down Mode": "وضع رأسا على عقب", + "Max. Redirects": "الأعلى. إعادة التوجيه", + "Accepted Status Codes": "رموز الحالة المقبولة", + "Push URL": "دفع عنوان URL", + "needPushEvery": "يجب عليك استدعاء عنوان URL هذا كل ثانية.", + "pushOptionalParams": "المعلمات الاختيارية", + "Save": "يحفظ", + "Notifications": "إشعارات", + "Not available, please setup.": "غير متوفر من فضلك الإعداد.", + "Setup Notification": "إشعار جديد", + "Light": "نور", + "Dark": "داكن", + "Auto": "آلي", + "Timezone": "وحدة زمنية", + "Search Engine Visibility": "محرك بحث الرؤية", + "Allow indexing": "السماح الفهرسة", + "Discourage search engines from indexing site": "تثبيط محركات البحث من موقع الفهرسة", + "Change Password": "غير كلمة السر", + "add one": "أضف واحدا", + "Notification Type": "نوع إعلام", + "Email": "بريد إلكتروني", + "Test": "امتحان", + "Certificate Info": "معلومات الشهادة", + "Resolver Server": "خادم Resolver", + "Resource Record Type": "نوع سجل الموارد", + "Last Result": "اخر نتيجة", + "Create your admin account": "إنشاء حساب المسؤول الخاص بك", + "Repeat Password": "اعد كلمة السر", + "Import Backup": "استيراد النسخ الاحتياطي", + "Export Backup": "النسخ الاحتياطي تصدير", + "Export": "يصدّر", + "Import": "يستورد", + "respTime": "resp. الوقت (MS)", + "notAvailableShort": "ن/أ", + "Default enabled": "التمكين الافتراضي", + "Apply on all existing monitors": "تنطبق على جميع المراقبات الحالية", + "Create": "خلق", + "Clear Data": "امسح البيانات", + "Events": "الأحداث", + "Heartbeats": "نبضات القلب", + "Schedule maintenance": "جدولة الصيانة", + "Affected Monitors": "المراقبات المتأثرة", + "Pick Affected Monitors...": "اختر المراقبات المتأثرة …", + "Start of maintenance": "بداية الصيانة", + "All Status Pages": "جميع صفحات الحالة", + "Select status pages...": "حدد صفحات الحالة …", + "alertWrongFileType": "الرجاء تحديد ملف JSON.", + "Clear all statistics": "مسح جميع الإحصاءات", + "Overwrite": "الكتابة فوق", + "Options": "خيارات", + "Keep both": "احتفظ بكليهما", + "Verify Token": "تحقق من الرمز المميز", + "Setup 2FA": "الإعداد 2FA", + "Enable 2FA": "تمكين 2FA", + "Disable 2FA": "تعطيل 2FA", + "2FA Settings": "2FA إعدادات", + "Two Factor Authentication": "توثيق ذو عاملين", + "Active": "نشيط", + "Inactive": "غير نشط", + "Token": "رمز", + "Show URI": "أظهر URI", + "Tags": "العلامات", + "Add New below or Select...": "إضافة جديد أدناه أو تحديد …", + "Tag with this name already exist.": "علامة مع هذا الاسم موجود بالفعل.", + "Tag with this value already exist.": "علامة مع هذه القيمة موجودة بالفعل.", + "color": "اللون", + "value (optional)": "القيمة (اختياري)", + "Gray": "رمادي", + "Red": "أحمر", + "Orange": "البرتقالي", + "Green": "لون أخضر", + "Blue": "أزرق", + "Indigo": "النيلي", + "Purple": "نفسجي", + "webhookAdditionalHeadersDesc": "يحدد رؤوس إضافية مرسلة مع webhook. كل رأس إضافي يجب تعريفه كقيمة أو مفتاح JSON.", + "Webhook URL": "عنوان URL للخطاف الإلكتروني", + "Pink": "لون القرنفل", + "Custom": "العادة", + "Status Pages": "صفحات الحالة", + "defaultNotificationName": "تنبيه {الإخطار} ({number})", + "here": "هنا", + "Required": "مطلوب", + "Post URL": "بعد عنوان URL", + "Content Type": "نوع المحتوى", + "webhookJsonDesc": "{0} مفيد لأي خوادم HTTP الحديثة مثل Express.js", + "webhookFormDataDesc": "{multipart} مفيد لـ PHP. سيحتاج JSON إلى تحليل {decodefunction}", + "webhookAdditionalHeadersTitle": "رؤوس إضافية", + "emojiCheatSheet": "ورقة الغش في الرموز التعبيرية", + "appriseInstalled": "تم تثبيت Prosise.", + "appriseNotInstalled": "الإبرام غير مثبت. {0}", + "Method": "طريقة", + "Body": "الجسم", + "Headers": "الرؤوس", + "PushUrl": "دفع عنوان URL", + "HeadersInvalidFormat": "رؤوس الطلبات غير صالحة JSON ", + "BodyInvalidFormat": "هيئة الطلب غير صالحة JSON ", + "Monitor History": "تاريخ المراقبة", + "clearDataOlderThan": "الحفاظ على بيانات سجل المراقبة لـ{0} أيام.", + "PasswordsDoNotMatch": "كلمة المرور غير مطابقة.", + "records": "السجلات", + "One record": "سجل واحد", + "steamApiKeyDescription": "لمراقبة خادم لعبة Steam ، تحتاج إلى مفتاح Steam Web-API. يمكنك تسجيل مفتاح API الخاص بك هنا ", + "Current User": "المستخدم الحالي", + "topicExplanation": "موضوع MQTT للمراقبة", + "successMessage": "نجاح رسالة", + "successMessageExplanation": "رسالة MQTT التي ستعتبر نجاحًا", + "recent": "الأخيرة", + "Done": "فعله", + "Info": "معلومات", + "Security": "حماية", + "Steam API Key": "مفتاح API Steam", + "Shrink Database": "تقلص قاعدة البيانات", + "Pick a RR-Type...": "اختر نوع RR …", + "Pick Accepted Status Codes...": "اختر أكواد الحالة المقبولة …", + "Default": "تقصير", + "HTTP Options": "خيارات HTTP", + "Create Incident": "إنشاء حادث", + "Title": "لقب", + "Content": "المحتوى", + "Style": "أسلوب", + "info": "معلومات", + "warning": "تحذير", + "danger": "خطر", + "error": "خطأ", + "critical": "شديد الأهمية", + "primary": "الأولية", + "light": "نور", + "dark": "ظلام", + "Post": "بريد", + "Please input title and content": "الرجاء إدخال العنوان والمحتوى", + "Created": "مخلوق", + "Switch to Light Theme": "التبديل إلى موضوع الضوء", + "Switch to Dark Theme": "التبديل إلى موضوع الظلام", + "Hide Tags": "إخفاء العلامات", + "Description": "وصف", + "No monitors available.": "لا يوجد مراقبات متاحة.", + "No Monitors": "لا يوجد مراقبات", + "Untitled Group": "مجموعة بلا عنوان", + "Services": "خدمات", + "Discard": "تجاهل", + "Cancel": "يلغي", + "Powered by": "مشغل بواسطة", + "Customize": "يعدل أو يكيف", + "Custom Footer": "تذييل مخصص", + "Custom CSS": "لغة تنسيق ويب حسب الطلب", + "deleteStatusPageMsg": "هل أنت متأكد من حذف صفحة الحالة هذه؟", + "Proxies": "وكلاء", + "default": "تقصير", + "enabled": "تمكين", + "setAsDefault": "تعيين كافتراضي", + "deleteProxyMsg": "هل أنت متأكد من حذف هذا الوكيل لجميع المراقبات؟", + "proxyDescription": "يجب تعيين وكيل للمراقبة من أجل أن تعمل.", + "enableProxyDescription": "لن يؤثر هذا الوكيل على طلبات المراقبة حتى يتم تنشيطه. يمكنك التحكم مؤقتًا في تعطيل الوكيل من جميع المراقبات حسب حالة التنشيط.", + "setAsDefaultProxyDescription": "سيتم تمكين هذا الوكيل افتراضيًا للمراقبات الجديدة. لا يزال بإمكانك تعطيل الوكيل بشكل منفصل لكل مراقبة.", + "Certificate Chain": "سلسلة الشهادة", + "Valid": "صالح", + "Invalid": "غير صالح", + "User": "المستعمل", + "Installed": "المثبتة", + "Not installed": "غير مثبت", + "Running": "جري", + "Not running": "لا يعمل", + "Remove Token": "إزالة الرمز المميز", + "Start": "بداية", + "Stop": "قف", + "Add New Status Page": "أضف صفحة حالة جديدة", + "Slug": "سبيكة", + "Accept characters:": "قبول الأحرف:", + "startOrEndWithOnly": "ابدأ أو ينتهي بـ {0} فقط", + "No consecutive dashes": "لا شرطات متتالية", + "Next": "التالي", + "The slug is already taken. Please choose another slug.": "تم أخذ سبيكة بالفعل. الرجاء اختيار سبيكة أخرى.", + "No Proxy": "لا الوكيل", + "Authentication": "المصادقة", + "HTTP Basic Auth": "HTTP الأساسي Auth", + "New Status Page": "صفحة حالة جديدة", + "Page Not Found": "الصفحة غير موجودة", + "Reverse Proxy": "وكيل عكسي", + "Backup": "دعم", + "About": "عن", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "قد يضيع الاتصال الحالي إذا كنت تتصل حاليًا عبر نفق CloudFlare. هل أنت متأكد تريد إيقافها؟ اكتب كلمة المرور الحالية لتأكيدها.", + "HTTP Headers": "رؤوس HTTP", + "Trust Proxy": "الوكيل الثقة", + "Other Software": "برامج أخرى", + "For example: nginx, Apache and Traefik.": "على سبيل المثال: nginx و Apache و Traefik.", + "Please read": "يرجى القراءة", + "Subject:": "موضوع:", + "Valid To:": "صالحة ل:", + "Days Remaining:": "الأيام المتبقية:", + "Issuer:": "المُصدر:", + "Fingerprint:": "بصمة:", + "No status pages": "لا صفحات الحالة", + "Domain Name Expiry Notification": "اسم النطاق إشعار انتهاء الصلاحية", + "Proxy": "الوكيل", + "Date Created": "تاريخ الإنشاء", + "Footer Text": "نص تذييل", + "Show Powered By": "عرض مدعوم من قبل", + "Domain Names": "أسماء المجال", + "signedInDisp": "وقعت في {0}", + "signedInDispDisabled": "معاق المصادقة.", + "RadiusSecret": "سر نصف القطر", + "RadiusSecretDescription": "السر المشترك بين العميل والخادم", + "RadiusCalledStationId": "يسمى معرف المحطة", + "RadiusCalledStationIdDescription": "معرف الجهاز المتصل", + "RadiusCallingStationId": "معرف محطة الاتصال", + "RadiusCallingStationIdDescription": "معرف جهاز الاتصال", + "Certificate Expiry Notification": "إشعار انتهاء الصلاحية", + "API Username": "اسم المستخدم API", + "API Key": "مفتاح API", + "Show update if available": "عرض التحديث إذا كان ذلك متاحًا", + "Also check beta release": "تحقق أيضًا من الإصدار التجريبي", + "Using a Reverse Proxy?": "باستخدام وكيل عكسي؟", + "Check how to config it for WebSocket": "تحقق من كيفية تكوينه لـ WebSocket", + "Steam Game Server": "خادم لعبة البخار", + "Most likely causes:": "الأسباب المرجحة:", + "The resource is no longer available.": "لم يعد المورد متاحًا.", + "There might be a typing error in the address.": "قد يكون هناك خطأ مطبعي في العنوان.", + "What you can try:": "ماذا تستطيع أن تجرب:", + "Retype the address.": "اعد كتابة العنوان.", + "Go back to the previous page.": "عد للصفحة السابقة.", + "Coming Soon": "قريبا", + "Connection String": "سلسلة الاتصال", + "Query": "استفسار", + "settingsCertificateExpiry": "شهادة TLS انتهاء الصلاحية", + "certificationExpiryDescription": "تقوم مراقبات HTTPS بتشغيل إشعار عند انتهاء صلاحية شهادة TLS في:", + "Setup Docker Host": "إعداد مضيف Docker", + "Connection Type": "نوع الاتصال", + "Docker Daemon": "دُوكر Daemon", + "deleteDockerHostMsg": "هل أنت متأكد من حذف مضيف Docker لجميع المراقبات؟", + "socket": "قابس كهرباء", + "tcp": "TCP / HTTP", + "Docker Container": "حاوية Docker", + "Container Name / ID": "اسم الحاوية / معرف", + "Docker Host": "مضيف Docker", + "Docker Hosts": "مضيفي Docker", + "Domain": "اِختِصاص", + "Workstation": "محطة العمل", + "Packet Size": "حجم الحزمة", + "Bot Token": "رمز الروبوت", + "wayToGetTelegramToken": "يمكنك الحصول على رمز من {0}.", + "Chat ID": "معرف الدردشة", + "telegramMessageThreadID": "معرف المواضيع", + "supportTelegramChatID": "دعم الدردشة المباشرة / معرف الدردشة للقناة", + "wayToGetTelegramChatID": "يمكنك الحصول على معرف الدردشة الخاص بك عن طريق إرسال رسالة إلى البوت والانتقال إلى عنوان URL هذا لعرض chat_id:", + "YOUR BOT TOKEN HERE": "رمز الروبوت الخاص بك هنا", + "chatIDNotFound": "لم يتم العثور على معرف الدردشة ؛ الرجاء إرسال رسالة إلى هذا الروبوت أولاً", + "disableCloudflaredNoAuthMsg": "أنت في وضع مصادقة لا توجد كلمة مرور غير مطلوبة.", + "trustProxyDescription": "ثق في رؤوس \"X-Forwarded- *\". إذا كنت ترغب في الحصول على عنوان IP الصحيح للعميل وكان Uptime Kuma خلف وكيل مثل Nginx أو Apache ، فيجب عليك تمكين هذا.", + "wayToGetLineNotifyToken": "يمكنك الحصول على رمز الوصول من {0}", + "Examples": "أمثلة", + "Home Assistant URL": "عنوان URL لـ Home Assistant", + "Long-Lived Access Token": "الرمز المميز للوصول منذ فترة طويلة", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "يمكن إنشاء رمز الوصول لفترة طويلة عن طريق النقر على اسم ملف التعريف الخاص بك (أسفل اليسار) والتمرير إلى الأسفل ثم انقر فوق إنشاء الرمز المميز.", + "Notification Service": "خدمة الإخطار", + "default: notify all devices": "الافتراضي: إخطار جميع الأجهزة", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "يمكن العثور على قائمة بخدمات الإخطار في المساعد المنزلي ضمن \"Developer Tools > Services\" ابحث عن \"notification\" للعثور على اسم جهازك/هاتفك.", + "Automations can optionally be triggered in Home Assistant:": "الاتمته يمكن اختياريا تحفيزها عبر Home Assistant:", + "Trigger type:": "نوع الزناد:", + "Event type:": "نوع الحدث:", + "Event data:": "بيانات الحدث:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "ثم اختر إجراءً على سبيل المثال قم بتبديل المشهد إلى حيث يكون ضوء RGB أحمر.", + "Frontend Version": "إصدار الواجهة الأمامية", + "Frontend Version do not match backend version!": "إصدار Frontend لا يتطابق مع الإصدار الخلفي!", + "backupOutdatedWarning": "مهمل: نظرًا لأنه تمت إضافة الكثير من الميزات وأن ميزة النسخ الاحتياطي هذه لم يتم الحفاظ عليها قليلاً ، فلا يمكنها إنشاء نسخة احتياطية كاملة أو استعادتها.", + "backupRecommend": "يرجى النسخ الاحتياطي لحجم الصوت أو مجلد البيانات (./data/) مباشرة بدلاً من ذلك.", + "Optional": "اختياري", + "or": "أو", + "recurringInterval": "فترة", + "Recurring": "يتكرر", + "strategyManual": "نشط/غير نشط يدويًا", + "warningTimezone": "إنه يستخدم المنطقة الزمنية للخادم", + "weekdayShortMon": "الاثنين", + "weekdayShortTue": "الثلاثاء", + "weekdayShortWed": "تزوج", + "weekdayShortThu": "الخميس", + "weekdayShortFri": "الجمعة", + "No Maintenance": "لا صيانة", + "weekdayShortSat": "جلس", + "weekdayShortSun": "شمس", + "dayOfWeek": "يوم من الأسبوع", + "dayOfMonth": "يوم من الشهر", + "lastDay": "بالأمس", + "lastDay1": "آخر يوم من الشهر", + "lastDay2": "الثاني في اليوم الأخير من الشهر", + "lastDay3": "الثالث في اليوم الأخير من الشهر", + "lastDay4": "الرابع في اليوم الأخير من الشهر", + "pauseMaintenanceMsg": "هل أنت متأكد من أن تتوقف مؤقتًا؟", + "maintenanceStatus-under-maintenance": "تحت الصيانة", + "maintenanceStatus-inactive": "غير نشط", + "maintenanceStatus-scheduled": "المقرر", + "maintenanceStatus-ended": "انتهى", + "maintenanceStatus-unknown": "مجهول", + "Display Timezone": "عرض المنطقة الزمنية", + "Server Timezone": "المنطقة الزمنية الخادم", + "statusPageMaintenanceEndDate": "نهاية", + "IconUrl": "عنوان URL للرمز", + "Enable DNS Cache": "(مُهمَل) تمكين ذاكرة التخزين المؤقت لنظام أسماء النطاقات لمراقب HTTP(s)", + "Disable": "إبطال", + "dnsCacheDescription": "قد لا يعمل في بعض بيئات IPv6 تعطيله إذا واجهت أي مشكلات.", + "Single Maintenance Window": "نافذة صيانة واحدة", + "Maintenance Time Window of a Day": "نافذة وقت الصيانة لليوم", + "Effective Date Range": "نطاق التاريخ الفعلي (اختياري)", + "Schedule Maintenance": "جدولة الصيانة", + "Date and Time": "التاريخ و الوقت", + "DateTime Range": "نطاق DateTime", + "loadingError": "لا يمكن جلب البيانات ، يرجى المحاولة مرة أخرى في وقت لاحق.", + "plugin": "البرنامج المساعد | الإضافات", + "install": "ثَبَّتَ", + "installing": "التثبيت", + "uninstall": "الغاء التثبيت", + "uninstalling": "إلغاء التثبيت", + "confirmUninstallPlugin": "هل أنت متأكد من أنك تريد إلغاء تثبيت هذا المكون الإضافي؟", + "smtp": "البريد الإلكتروني (SMTP)", + "secureOptionNone": "لا شيء / startTls (25 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "تجاهل خطأ TLS", + "From Email": "من البريد الإلكترونى", + "emailCustomSubject": "موضوع مخصص", + "To Email": "للبريد الإلكتروني", + "smtpCC": "نسخة", + "smtpBCC": "BCC", + "Discord Webhook URL": "عنوان URL للخّطاف على الويب للديسكورد", + "wayToGetDiscordURL": "يمكنك الحصول على هذا بالانتقال إلى إعدادات الخادم -> عمليات التكامل -> عرض الخطافات على الويب -> خطاف ويب جديد", + "Bot Display Name": "اسم عرض الروبوت", + "Prefix Custom Message": "بادئة رسالة مخصصة", + "Hello @everyone is...": "مرحبًا {'@'} الجميع…", + "wayToGetTeamsURL": "يمكنك معرفة كيفية إنشاء عنوان URL webhook {0}.", + "wayToGetZohoCliqURL": "يمكنك معرفة كيفية إنشاء عنوان URL webhook {0}.", + "needSignalAPI": "تحتاج إلى وجود عميل إشارة مع REST API.", + "wayToCheckSignalURL": "يمكنك التحقق من عنوان URL هذا لعرض كيفية إعداد واحد:", + "Number": "رقم", + "Recipients": "المستلمين", + "Access Token": "رمز وصول", + "Channel access token": "قناة الوصول إلى الرمز", + "Line Developers Console": "تحكم المطورين", + "lineDevConsoleTo": "وحدة المطورين Line Console - {0}", + "Basic Settings": "الإعدادات الأساسية", + "confirmClearStatisticsMsg": "هل أنت متأكد من أنك تريد حذف جميع الإحصائيات؟", + "importHandleDescription": "اختر 'تخطي موجود' إذا كنت تريد تخطي كل شاشة أو إشعار بنفس الاسم. 'الكتابة فوق' سوف يحذف كل شاشة وإخطار موجود.", + "User ID": "معرف المستخدم", + "Messaging API": "واجهة برمجة تطبيقات المراسلة", + "wayToGetLineChannelToken": "قم أولاً بالوصول إلى {0} إنشاء مزود وقناة (واجهة برمجة تطبيقات المراسلة) ، ثم يمكنك الحصول على رمز الوصول إلى القناة ومعرف المستخدم من عناصر القائمة المذكورة أعلاه.", + "Icon URL": "عنوان URL للرمز", + "aboutIconURL": "يمكنك توفير رابط لصورة في \"رمز URL\" لتجاوز صورة الملف الشخصي الافتراضية. لن يتم استخدامه إذا تم تعيين ايقونة اموجي.", + "aboutMattermostChannelName": "يمكنك تجاوز القناة الافتراضية التي تنشرها WebHook من خلال إدخال اسم القناة في \"Channel Name\" الحقل. يجب تمكين هذا في إعدادات Webhook Mattern. السابق", + "dataRetentionTimeError": "يجب أن تكون فترة الاستبقاء 0 أو أكبر", + "infiniteRetention": "ضبط على 0 للاحتفاظ لا نهائي.", + "confirmDeleteTagMsg": "هل أنت متأكد من أنك تريد حذف هذه العلامة؟ لن يتم حذف المراقبات المرتبطة بهذه العلامة.", + "enableGRPCTls": "السماح لإرسال طلب GRPC مع اتصال TLS", + "deleteMonitorMsg": "هل أنت متأكد من حذف هذا الشاشة؟", + "deleteMaintenanceMsg": "هل أنت متأكد من حذف هذه الصيانة؟", + "resolverserverDescription": "CloudFlare هو الخادم الافتراضي. يمكنك تغيير خادم المحوّل في أي وقت.", + "rrtypeDescription": "حدد نوع RR الذي تريد مراقبته", + "enableDefaultNotificationDescription": "سيتم تمكين هذا الإشعار افتراضيًا للشاشات الجديدة. لا يزال بإمكانك تعطيل الإخطار بشكل منفصل لكل شاشة.", + "clearEventsMsg": "هل أنت متأكد من حذف جميع الأحداث لهذا الشاشة؟", + "clearHeartbeatsMsg": "هل أنت متأكد من حذف جميع دقات القلب لهذا الشاشة؟", + "confirmImportMsg": "هل أنت متأكد من أنك تريد استيراد النسخ الاحتياطي؟ يرجى التحقق من أنك حددت خيار الاستيراد الصحيح.", + "twoFAVerifyLabel": "يرجى إدخال الرمز المميز الخاص بك للتحقق من المصادقة الثنائية (2FA):", + "pushoversounds pushover": "سداد (افتراضي)", + "pushoversounds bike": "دراجة هوائية", + "pushoversounds bugle": "بوق", + "tokenValidSettingsMsg": "الرمز المميز صالح! يمكنك الآن حفظ إعدادات 2FA.", + "confirmEnableTwoFAMsg": "هل أنت متأكد من أنك تريد تمكين 2FA؟", + "confirmDisableTwoFAMsg": "هل أنت متأكد من أنك تريد تعطيل 2FA؟", + "recurringIntervalMessage": "ركض مرة واحدة كل يوم | قم بالتشغيل مرة واحدة كل يوم {0}", + "affectedMonitorsDescription": "حدد المراقبين المتأثرة بالصيانة الحالية", + "affectedStatusPages": "إظهار رسالة الصيانة هذه على صفحات الحالة المحددة", + "atLeastOneMonitor": "حدد شاشة واحدة على الأقل من المتأثرين", + "passwordNotMatchMsg": "كلمة المرور المتكررة لا تتطابق.", + "notificationDescription": "يجب تعيين الإخطارات إلى شاشة للعمل.", + "keywordDescription": "ابحث في الكلمة الرئيسية في استجابة HTML العادية أو JSON. البحث حساس للحالة.", + "backupDescription": "يمكنك النسخ الاحتياطي لجميع الشاشات والإشعارات في ملف JSON.", + "backupDescription3": "يتم تضمين البيانات الحساسة مثل الرموز الإخطار في ملف التصدير ؛ يرجى تخزين التصدير بشكل آمن.", + "endpoint": "نقطة النهاية", + "octopushAPIKey": "\"API key\" from HTTP API بيانات اعتماد في لوحة التحكم", + "octopushLogin": "\"Login\" من بيانات اعتماد API HTTP في لوحة التحكم", + "promosmsLogin": "اسم تسجيل الدخول API", + "promosmsPassword": "كلمة مرور API", + "pushoversounds cashregister": "ماكينة تسجيل المدفوعات النقدية", + "pushoversounds classical": "كلاسيكي", + "pushoversounds cosmic": "كونية", + "pushoversounds falling": "هبوط", + "pushoversounds gamelan": "غاميلان", + "pushoversounds incoming": "واردة", + "pushoversounds intermission": "استراحة", + "pushoversounds magic": "سحر", + "pushoversounds mechanical": "ميكانيكي", + "pushoversounds pianobar": "شريط البيانو", + "pushoversounds siren": "صفارة إنذار", + "pushoversounds spacealarm": "إنذار الفضاء", + "pushoversounds tugboat": "قارب السحب", + "pushoversounds alien": "إنذار أجنبي (طويل)", + "pushoversounds climb": "تسلق (طويل)", + "pushoversounds persistent": "مستمر (طويل)", + "pushoversounds echo": "صدى مهووس (طويل)", + "pushoversounds updown": "صعودا (طويلة)", + "pushoversounds vibrate": "يهتز فقط", + "pushoversounds none": "لا شيء (صامت)", + "pushyAPIKey": "مفتاح API السري", + "pushyToken": "رمز الجهاز", + "apprise": "إبلاغ (دعم 50+ خدمات الإخطار)", + "GoogleChat": "دردشة Google", + "wayToGetKookBotToken": "قم بإنشاء تطبيق واحصل على رمز الروبوت الخاص بك على {0}", + "wayToGetKookGuildID": "قم بتشغيل 'وضع المطور' في إعداد Kook وانقر بزر الماوس الأيمن على النقابة للحصول على معرفه", + "Guild ID": "معرف النقابة", + "User Key": "مفتاح المستخدم", + "Device": "جهاز", + "Message Title": "عنوان الرسالة", + "Notification Sound": "صوت الإشعار", + "More info on:": "مزيد من المعلومات حول: {0}", + "pushoverDesc1": "أولوية الطوارئ (2) لها مهلة افتراضية 30 ثانية بين إعادة المحاولة وستنتهي صلاحيتها بعد ساعة واحدة.", + "pushoverDesc2": "إذا كنت ترغب في إرسال إشعارات إلى أجهزة مختلفة ، قم بملء حقل الجهاز.", + "SMS Type": "نوع الرسائل القصيرة", + "octopushTypePremium": "قسط (سريع - موصى به للتنبيه)", + "octopushTypeLowCost": "التكلفة المنخفضة (بطيئة - تم حظرها أحيانًا بواسطة المشغل)", + "checkPrice": "تحقق من أسعار {0}:", + "apiCredentials": "بيانات اعتماد API", + "octopushLegacyHint": "هل تستخدم الإصدار القديم من Octopush (2011-2020) أو الإصدار الجديد؟", + "Check octopush prices": "تحقق من أسعار Octopush {0}.", + "AccessKeyId": "معرف AccessKey", + "SecretAccessKey": "Accesskey Secret", + "PhoneNumbers": "أرقام الهواتف", + "octopushPhoneNumber": "رقم الهاتف (بالصيغة الدولية، مثلا: 33612345678+)", + "octopushSMSSender": "اسم مرسل الرسائل القصيرة", + "LunaSea Device ID": "معرف جهاز Lunasea", + "Apprise URL": "إبلاغ عنوان URL", + "Example:": "مثال: {0}", + "Read more:": "{0} :قراءة المزيد", + "Status:": "{0} :حالة", + "Strategy": "إستراتيجية", + "Free Mobile User Identifier": "معرف مستخدم الهاتف المحمول المجاني", + "Free Mobile API Key": "مفتاح واجهة برمجة تطبيقات مجانية للهاتف المحمول", + "Enable TLS": "تمكين TLS", + "Proto Service Name": "اسم خدمة البروتو", + "Proto Method": "طريقة البروتو", + "Proto Content": "محتوى proto", + "Economy": "اقتصاد", + "Lowcost": "تكلفة منخفضة", + "high": "عالي", + "SendKey": "Sendkey", + "SMSManager API Docs": "مستندات SMSManager API", + "Gateway Type": "نوع البوابة", + "You can divide numbers with": "يمكنك تقسيم الأرقام مع", + "Base URL": "عنوان URL الأساسي", + "goAlertInfo": "الهدف هو تطبيق مفتوح المصدر لجدولة الجدولة التلقائية والإشعارات (مثل الرسائل القصيرة أو المكالمات الصوتية). إشراك الشخص المناسب تلقائيًا بالطريقة الصحيحة وفي الوقت المناسب! {0}", + "goAlertIntegrationKeyInfo": "احصل على مفتاح تكامل API العام للخدمة في هذا التنسيق \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" عادةً قيمة المعلمة الرمزية لعنوان url المنسق.", + "TemplateCode": "رمز القالب", + "SignName": "اسم تسجيل الدخول", + "Sms template must contain parameters: ": "يجب أن يحتوي قالب الرسائل القصيرة على معلمات: ", + "Bark Endpoint": "نقطة نهاية اللحاء", + "Bark Group": "مجموعة اللحاء", + "Bark Sound": "صوت اللحاء", + "WebHookUrl": "webhookurl", + "SecretKey": "Secretkey", + "For safety, must use secret key": "للسلامة يجب استخدام المفتاح السري", + "Device Token": "رمز الجهاز", + "Platform": "منصة", + "Huawei": "هواوي", + "High": "عالٍ", + "Retry": "إعادة المحاولة", + "Topic": "عنوان", + "WeCom Bot Key": "WECOM BOT KEY", + "Setup Proxy": "إعداد الوكيل", + "Proxy Protocol": "بروتوكول الوكيل", + "Proxy Server": "مخدم بروكسي", + "Proxy server has authentication": "خادم الوكيل لديه مصادقة", + "promosmsTypeEco": "SMS Eco - رخيصة ولكن بطيئة وغالبًا ما تكون محملة. يقتصر فقط على المستفيدين البولنديين.", + "promosmsTypeFlash": "SMS Flash - سيتم عرض الرسالة تلقائيًا على جهاز المستلم. يقتصر فقط على المستفيدين البولنديين.", + "promosmsTypeFull": "SMS Full - Tier Premium SMS يمكنك استخدام اسم المرسل الخاص بك (تحتاج إلى تسجيل الاسم أولاً). موثوقة للتنبيهات.", + "promosmsTypeSpeed": "سرعة الرسائل القصيرة - أولوية قصوى في النظام. سريع وموثوق للغاية ولكنه مكلف (حوالي مرتين من الرسائل القصيرة السعر الكامل).", + "promosmsPhoneNumber": "رقم الهاتف (للمستلم البولندي ، يمكنك تخطي رموز المنطقة)", + "matrixDesc2": "يوصى بشدة بإنشاء مستخدم جديد ولا تستخدم رمز الوصول إلى مستخدم Matrix الخاص بك لأنه سيتيح الوصول الكامل إلى حسابك وجميع الغرف التي انضمت إليها. بدلاً من ذلك ، قم بإنشاء مستخدم جديد ودعوته فقط إلى الغرفة التي تريد تلقيها الإشعار فيها. يمكنك الحصول على رمز الوصول عن طريق تشغيل {0}", + "Channel Name": "اسم القناة", + "promosmsSMSSender": "اسم مرسل الرسائل القصيرة", + "promosmsAllowLongSMS": "السماح الرسائل القصيرة الطويلة", + "Feishu WebHookUrl": "Feishu Webhookurl", + "matrixHomeserverURL": "عنوان URL HomeServer (مع HTTP (S)", + "Internal Room Id": "معرف الغرفة الداخلية", + "matrixDesc1": "يمكنك العثور على معرف الغرفة الداخلي من خلال البحث في القسم المتقدم لإعدادات الغرفة في عميل Matrix الخاص بك. ينبغي أن يبدو مثل QMdRCpUIfLwsfjxye6:home.server!.", + "Uptime Kuma URL": "UPTIME KUMA URL", + "Icon Emoji": "أيقونة الرموز التعبيرية", + "signalImportant": "هام: لا يمكنك المزج بين المجموعات والأرقام في المستلمين!", + "aboutWebhooks": "مزيد من المعلومات حول Webhooks ON", + "aboutChannelName": "أدخل اسم القناة في حقل اسم القناة {0} إذا كنت تريد تجاوز قناة WebHook. السابق", + "aboutKumaURL": "إذا تركت حقل URL في وقت التشغيل KUMA فارغًا ، فسيتم افتراضيًا إلى صفحة GitHub Project.", + "smtpDkimSettings": "إعدادات DKIM", + "smtpDkimDesc": "يرجى الرجوع إلى Nodemailer dkim {0} للاستخدام.", + "documentation": "توثيق", + "smtpDkimDomain": "اسم النطاق", + "smtpDkimKeySelector": "المحدد الرئيسي", + "smtpDkimPrivateKey": "مفتاح سري", + "smtpDkimHashAlgo": "خوارزمية التجزئة (اختياري)", + "smtpDkimheaderFieldNames": "مفاتيح الرأس للتوقيع (اختياري)", + "smtpDkimskipFields": "مفاتيح الرأس لا توقيع (اختياري)", + "wayToGetPagerDutyKey": "يمكنك الحصول على هذا عن طريق الانتقال إلى الخدمة -> دليل الخدمة -> (حدد خدمة) -> تكامل -> إضافة التكامل. هنا يمكنك البحث عن \"Events API V2\". مزيد من المعلومات {0}", + "Integration Key": "مفتاح التكامل", + "Integration URL": "URL تكامل", + "do nothing": "لا تفعل شيئا", + "alertaApiEndpoint": "نقطة نهاية API", + "alertaEnvironment": "بيئة", + "alertaApiKey": "مفتاح API", + "alertaAlertState": "حالة التنبيه", + "alertaRecoverState": "استعادة الدولة", + "auto acknowledged": "تم الاقرار تلقائياُ", + "auto resolve": "تم الحل تلقائياُ", + "serwersmsAPIUser": "اسم مستخدم API (بما في ذلك بادئة WebAPI_)", + "serwersmsAPIPassword": "كلمة مرور API", + "serwersmsPhoneNumber": "رقم الهاتف", + "serwersmsSenderName": "اسم مرسل الرسائل القصيرة (مسجل عبر بوابة العملاء)", + "smseagleTo": "أرقام الهواتف)", + "smseagleGroup": "اسم مجموعة كتب الهاتف (S)", + "smseagleContact": "كتاب الاتصال اسم (S)", + "smseagleRecipientType": "نوع المستلم", + "smseagleRecipient": "المتلقي (المتلقيين) (يجب فصل المتعددة مع فاصلة)", + "smseagleToken": "API وصول الرمز المميز", + "smseagleUrl": "عنوان URL لجهاز SMSEGLE الخاص بك", + "smseagleEncoding": "إرسال Unicode", + "smseaglePriority": "أولوية الرسالة (0-9 افتراضي = 0)", + "Recipient Number": "رقم المستلم", + "From Name/Number": "من الاسم/الرقم", + "Leave blank to use a shared sender number.": "اترك فارغًا لاستخدام رقم المرسل المشترك.", + "Octopush API Version": "إصدار Octopush API", + "Legacy Octopush-DM": "Octopush-DM القديم", + "ntfy Topic": "موضوع ntfy", + "onebotHttpAddress": "OneBot HTTP عنوان", + "onebotMessageType": "نوع رسالة OneBot", + "onebotGroupMessage": "مجموعة", + "onebotPrivateMessage": "خاص", + "onebotUserOrGroupId": "معرف المجموعة/المستخدم", + "onebotSafetyTips": "للسلامة يجب ضبط الرمز المميز للوصول", + "PushDeer Key": "مفتاح PushDeer", + "wayToGetClickSendSMSToken": "يمكنك الحصول على اسم مستخدم API ومفتاح API من {0}.", + "Custom Monitor Type": "نوع الشاشة المخصص", + "Google Analytics ID": "معرف Google Analytics", + "Edit Tag": "تحرير العلامة", + "Server Address": "عنوان المستقبل", + "Learn More": "يتعلم أكثر", + "apiKeyAddedMsg": "تمت إضافة مفتاح API خاص بك. يرجى تدوين ذلك لأنه لن يتم عرضه مرة أخرى.", + "No API Keys": "لا توجد مفاتيح API", + "apiKey-inactive": "غير نشط", + "disableAPIKeyMsg": "هل أنت متأكد أنك تريد تعطيل مفتاح API هذا؟", + "deleteAPIKeyMsg": "هل أنت متأكد أنك تريد حذف مفتاح API هذا؟", + "Auto Get": "الحصول التلقائي", + "Auto resolve or acknowledged": "تم الحل أو الاقرار تلقائياً", + "backupDescription2": "ملحوظة: لم يتم تضمين بيانات السجل والأحداث.", + "languageName": "العربية", + "Game": "الألعاب", + "List": "القائمة", + "statusMaintenance": "الصيانة", + "Home": "الرئيسة", + "setupDatabaseChooseDatabase": "ما هي قاعدة البيانات التي تريد استخدامها؟", + "setupDatabaseEmbeddedMariaDB": "لا تحتاج إلى تعيين أي شيء. قامت نسخة دُوكر بتضمين MariaDB لك تلقائيًا. سيتصل (آب تايم كوما) بقاعدة البيانات هذه عبر مقبس Unix.", + "setupDatabaseMariaDB": "للاتصال بقاعدة بيانات MariaDB خارجية. تحتاج إلى تعيين معلومات اتصال قاعدة البيانات.", + "pushViewCode": "كيف يتم إستخدام مراقبة Push؟ (عرض المصدر)", + "pushOthers": "أخرى", + "programmingLanguages": "لغات البرمجة", + "Reset Token": "إعادة تعيين الرمز الخاص", + "noDockerHostMsg": "غير متوفر. قم بإعداد مضيف Docker أولاً.", + "DockerHostRequired": "الرجاء تعيين مضيف Docker لهذة المراقبة.", + "liquidIntroduction": "يتم تحقيق قابلية القولبة من خلال لغة القوالب السائلة. يرجى الرجوع إلى {0} للحصول على تعليمات الاستخدام.", + "templateMsg": "رسالة التنبيه", + "templateHeartbeatJSON": "العنصر الذي يصف نبضات القلب", + "templateMonitorJSON": "العنصر الذي يصف المراقبة", + "templateLimitedToUpDownCertNotifications": "متوفر فقط لنتبيهات انتهاء صلاحية الشهادات", + "templateLimitedToUpDownNotifications": "متوفر فقط لتنبيهات الأعلى/الأسفل", + "setupDatabaseSQLite": "ملف قاعدة بيانات بسيط، يوصى به لعمليات النشر على نطاق صغير. قبل الإصدار 2.0.0، استخدم (آب تايم كوما) SQLite كقاعدة بيانات افتراضية.", + "dbName": "اسم قاعدة البيانات", + "Request Timeout": "انتهت مهلة الطلب", + "timeoutAfter": "انتهاء المهلة بعد {0} ثانية", + "styleElapsedTime": "الوقت المنقضي تحت شريط نبضات القلب", + "styleElapsedTimeShowNoLine": "إظهار (بدون خط)", + "styleElapsedTimeShowWithLine": "إظهار (مع خط)", + "webhookBodyPresetOption": "اختيار مسبق - {0}", + "webhookBodyCustomOption": "هيئة مخصصة", + "Select": "اختيار", + "selectedMonitorCount": "تم اختيار : {0}", + "Check/Uncheck": "تحديد/إلغاء تحديد", + "Invert Keyword": "عكس الكلمة الأساسية", + "Expected Value": "القيمة المتوقعة", + "Json Query": "استعلام Json", + "filterActive": "نشيط", + "Cannot connect to the socket server": "لا يمكن الاتصال بخادم المقبس", + "Reconnecting...": "إعادة الاتصال...", + "filterActivePaused": "متوقف مؤقتا", + "Add New Tag": "إضافة وسم جديد", + "statusPageRefreshIn": "تحديث خلال: {0}", + "successKeywordExplanation": "كلمة MQTT المفتاحية التي ستعتبر رد ناجح", + "successKeyword": "الكلمة المفتاحية الناجحة", + "Search monitored sites": "البحث في المواقع المراقبة", + "statusPageSpecialSlugDesc": "المعرف الخاص {0}: سيتم عرض هذه الصفحة عندما لا يتم توفير معرف", + "telegramProtectContent": "حماية إعادة التوجيه/الحفظ", + "settingUpDatabaseMSG": "جاري إعداد قاعدة البيانات. قد يستغرق بعض الوقت، الرجاء الإنتظار.", + "emailTemplateMonitorJSON": "العنصر الذي يصف المراقبة", + "Add a new expiry notification day": "قم بإضافة تنبيه تاريخ يوم إنتهاء", + "Remove the expiry notification": "إزالة تنبيه يوم تاريخ الإنتهاء", + "tailscalePingWarning": "من أجل استخدام مراقبة Tailscale Ping، تحتاج إلى تثبيت Uptime Kuma بدون Docker وكذلك تثبيت عميل Tailscale على الخادم الخاص بك.", + "Clone Monitor": "نسخ المراقبة", + "telegramMessageThreadIDDescription": "معرف فريد اختياري لسلسلة الرسائل المستهدفة (الموضوع) في المنتدى؛ لمجموعات المنتدى الكبرى فقط", + "emailCustomBody": "نص مخصص", + "emailTemplateStatus": "الحالة", + "leave blank for default subject": "اترك فارغاً ليتم تعيين الموضوع تلقائياً", + "leave blank for default body": "اترك فارغاً ليتم تعيين النص تلقائياً", + "emailTemplateServiceName": "اسم الخدمة", + "emailTemplateHostnameOrURL": "اسم المضيف أو عنوان URL", + "smspartnerPhoneNumber": "رقم الهاتف", + "endDateTime": "تاريخ/وقت الإنتهاء", + "chromeExecutableAutoDetect": "كشف تلقائي", + "Edit Maintenance": "تعديل الصيانة", + "smspartnerSenderName": "اسم مرسل الرسائل القصيرة", + "telegramServerUrl": "(اختياري) رابط الخادم", + "emailCustomisableContent": "محتوى قابل للتخصيص", + "emailTemplateMsg": "رسالة الإشعار", + "Select message type": "اختر نوع الرسالة", + "Your User ID": "معرف المستخدم الخاص بك", + "setup a new monitor group": "إنشاء مجموعة مراقبة جديدة", + "Expires": "تاريخ الانتهاء", + "templateStatus": "الحالة", + "telegramUseTemplate": "استخدم قالب رسالة مخصص", + "telegramUseTemplateDescription": "إذا تم التفعيل، سيتم إرسال الرسالة باستخدام قالب مخصص.", + "now": "الآن", + "-year": "-سنة", + "and": "و", + "Add a domain": "إضافة نطاق", + "Remove domain": "إزالة النطاق '{0}'", + "time ago": "منذ {0}", + "startDateTime": "تاريخ/وقت البدء", + "example": "مثال", + "certHostnameMismatch": "اسم مضيف الشهادة لا يتطابق مع عنوان URL الخاص بالمراقبة.", + "wsCodeDescription": "للحصول على مزيد من المعلومات حول رموز الحالة، يرجى مراجعة {rfc6455}", + "saveResponseForNotifications": "حفظ استجابة HTTP الناجحة للإشعارات", + "saveErrorResponseForNotifications": "حفظ استجابة HTTP الفاشلة للإشعارات", + "responseMaxLength": "أقصى طول للاستجابة (بايت)", + "Incident title": "عنوان الحادثة", + "tagAlreadyStaged": "تم تجهيز هذا الوسم (الاسم والقيمة) بالفعل لهذه الدفعة.", + "tagNameExists": "يوجد بالفعل وسم نظام بهذا الاسم. اختره من القائمة أو استخدم اسمًا مختلفًا.", + "Pinned incidents are shown prominently on the status page": "تظهر الحوادث المثبتة بشكل بارز في صفحة الحالة", + "templateAvailableVariables": "المتغيرات المتاحة", + "Result": "النتيجة", + "steamApiKeyDescriptionAt": "لمراقبة خادم ألعاب Steam، أنت بحاجة إلى مفتاح Steam Web-API. يمكنك تسجيل مفتاح API الخاص بك على {url}", + "mqttHostnameTip": "يرجى استخدام هذا التنسيق {hostnameFormat}", + "lastUpdatedAt": "آخر تحديث: {date}", + "lastUpdatedAtFromNow": "آخر تحديث: {date} ({fromNow})", + "selectMonitorMsg": "حدد المراقبين لتنفيذ الإجراءات", + "deselectAllMonitorsAria": "قم بإلغاء تحديد جميع المراقبين", + "deleteIncidentMsg": "هل أنت متأكد من رغبتك في حذف هذا الحادث؟", + "Certificate Chain:": "سلسلة الشهادة:", + "Monitors": "{n} مراقب | {n} مراقبين", + "saveResponseDescription": "احتفظ بإستجابة HTTP واجعلها متاحة لقوالب الإشعارات كـ {templateVariable}", + "unknownDays": "أيام غير معروفة", + "responseMaxLengthDescription": "الحد الأقصى لحجم بيانات الاستجابة المراد تخزينها. اضبطه على 0 لتخزين بيانات غير محدودة. سيتم اقتطاع الردود الطويلة. القيمة الافتراضية: 1024 (1 كيلوبايت)", + "certificateExpiryNotificationHelp": "يمكن ضبط عدد الأيام مقدماً في الإعدادات.", + "Please input content": "يرجى إدخال المحتوى", + "Please input title": "يرجى إدخال العنوان", + "RSS Title": "عنوان RSS", + "ignoreSecWebsocketAcceptHeaderDescription": "يسمح للخادم بعدم الرد بـ Sec-WebSocket-Accept، إذا نجحت ترقية websocket.", + "invalidURL": "عنوان URL غير صالح", + "Path": "المسار", + "Add Tags": "إضافة وسوم", + "tagAlreadyOnMonitor": "هذه العلامة (الاسم والقيمة) موجودة بالفعل في واحدة من مؤشرات المراقبة أو في انتظار الإضافة.", + "templateServiceName": "اسم الخدمة", + "templateHostnameOrURL": "اسم المضيف أو عنوان URL", + "defaultFriendlyName": "مراقب جديد", + "monitorTypeGameServer": "سيرفر ألعاب", + "monitorTypeSpecial": "خاص", + "monitorTypeDatabase": "نوع مراقب قاعدة البيانات", + "wsSubprotocolDescription": "أدخل قائمة بالبروتوكولات الفرعية مفصولة بفواصل. لمزيد من المعلومات حول البروتوكولات الفرعية، يُرجى مراجعة {documentation}", + "Edit Incident": "تعديل الحادثة", + "Actions": "الإجراءات", + "Only retry if status code check fails": "أعد المحاولة فقط في حالة فشل فحص رمز الحالة", + "retryOnlyOnStatusCodeFailureDescription": "في حالة التمكين، لن تحدث عمليات إعادة المحاولة إلا عندما يفشل فحص رمز حالة HTTP (على سبيل المثال، الخادم معطل). إذا نجح فحص رمز الحالة ولكن فشل استعلام JSON، فسيتم وضع علامة على الشاشة بأنها معطلة على الفور دون إعادة المحاولة.", + "Subprotocol(s)": "البروتوكول الفرعي (البروتوكولات الفرعية)", + "versionIs": "الإصدار: {version}", + "logoutCurrentUser": "تسجيل الخروج {username}", + "createdAt": "تاريخ الإنشاء: {date}", + "auto-select": "تحديد تلقائي", + "Load More": "عرض المزيد", + "No incidents recorded": "لم يتم تسجيل أي حوادث", + "mariadbUseSSLHelptext": "قم بتمكين استخدام اتصال مشفر بقاعدة البيانات الخاصة بك. مطلوب لمعظم قواعد البيانات السحابية.", + "mariadbCaCertificateLabel": "شهادة CA", + "enableSSL": "تفعيل SSL/TLS", + "oracledbConnectionString": "قاعدة بيانات اوراكال: {connectionString}", + "mariadbCaCertificateHelptext": "الصق شهادة الـ CA بصيغة PEM لتستخدمها مع الشهادات ذاتية التوقيع. اتركها فارغة اذا كانت قاعدة بياناتك تستخدم شهادة موقعة بواسطة CA عام.", + "Loading...": "جاري التحميل...", + "days": "{n} يوم | {n} أيام", + "hours": "{n} يوم | {n} أيام", + "minutes": "{n} دقيقة | {n} دقائق", + "minuteShort": "{n} د | {n} د", + "years": "{n} سنة | {n} سنين", + "Pin this incident": "ثبت هذه الحادثة", + "Incident description": "وصف الحادثة", + "Incident not found or access denied": "لم يتم العثور على الحادث أو تم رفض الوصول", + "Past Incidents": "الحوادث السابقة", + "mqttWebSocketPath": "مسار MQTT WebSocket", + "mqttWebsocketPathExplanation": "مسار WebSocket لبروتوكول MQTT عبر اتصالات WebSocket (على سبيل المثال، /mqtt)", + "mqttWebsocketPathInvalid": "يرجى استخدام تنسيق مسار WebSocket صالح", + "selectedMonitorCountMsg": "تم اختيار: {n} | تم اختيار: {n}", + "selectAllMonitorsAria": "حدد جميع المراقبين", + "slug is not found": "لم يتم العثور على Slug", + "dateCreatedAtFromNow": "تاريخ الإنشاء: {date} ({fromNow})", + "Refresh Interval": "فترة التحديث", + "Refresh Interval Description": "ستقوم صفحة الحالة بتحديث الموقع بالكامل كل {0} ثانية", + "See Jira Cloud Docs": "راجع وثائق Jira Cloud", + "API Token": "رمز API", + "locally configured mail transfer agent": "وكيل نقل البريد المُهيأ محليًا", + "ignoreTLSErrorGeneral": "تجاهل خطأ TLS/SSL للاتصال", + "Host URL": "رابط المضيف", + "Json Query Expression": "تعبير استعلام JSON", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "أدخل اسم مضيف الخادم الذي تريد الاتصال به، أو {localhost} اذا كانت ستستخدم {local_mta}", + "ignoredTLSError": "تم تجاهل أخطاء TLS/SSL" +} diff --git a/src/lang/az.json b/src/lang/az.json new file mode 100644 index 0000000..c92a8a2 --- /dev/null +++ b/src/lang/az.json @@ -0,0 +1,1616 @@ +{ + "languageName": "Azərbaycanca", + "setupDatabaseChooseDatabase": "Hansı verilənlər bazasından istifadə etmək istəyirsiniz?", + "setupDatabaseEmbeddedMariaDB": "Heç nə tənzimləməyə ehtiyac yoxdur. Bu Docker imicinin içində MariaDB artıq quraşdırılıb və sizin üçün tənzimlənib. Uptime Kuma bu bazaya Unix soketi vasitəsilə qoşulacaq.", + "setupDatabaseMariaDB": "Kənar MariaDB bazasına qoşulun. Bazanın bağlantı məlumatlarını daxil etməlisiniz.", + "setupDatabaseSQLite": "Sadə verilənlər bazası faylı, kiçik miqyaslı quraşdırmalar üçün tövsiyə olunur. v2.0.0-dan əvvəl Uptime Kuma standart baza kimi SQLite işlədirdi.", + "settingUpDatabaseMSG": "Verilənlər bazası qurulur. Bu, bir qədər vaxt apara bilər, səbirli olun.", + "dbName": "Bazanın adı", + "oracledbConnectionString": "Oracle bazası: {connectionString}", + "enableSSL": "SSL/TLS-i aktivləşdir", + "mariadbUseSSLHelptext": "Bazaya şifrələnmiş bağlantı işlətmək üçün aktivləşdirin. Əksər bulud bazaları üçün tələb olunur.", + "mariadbCaCertificateLabel": "CA sertifikatı", + "mariadbCaCertificateHelptext": "Öz-özünə imzalanmış sertifikatlarla işlətmək üçün CA sertifikatını PEM formatında yapışdırın. Bazanız açıq CA tərəfindən imzalanmış sertifikat işlədirsə, boş buraxın.", + "Settings": "Parametrlər", + "Dashboard": "İdarə paneli", + "Help": "Kömək", + "New Update": "Yeni yeniləmə", + "Language": "Dil", + "Appearance": "Görünüş", + "Theme": "Tema", + "General": "Ümumi", + "Game": "Oyun", + "Primary Base URL": "Əsas baza URL-i", + "versionIs": "Versiya: {version}", + "Check Update On GitHub": "Yeniləməni GitHub-da yoxla", + "List": "Siyahı", + "Home": "Ana səhifə", + "Add": "Əlavə et", + "Add New Monitor": "Yeni monitor əlavə et", + "Quick Stats": "Qısa statistika", + "Up": "İşləyir", + "Down": "İşləmir", + "Pending": "Gözləyir", + "statusMaintenance": "Texniki xidmət", + "Maintenance": "Texniki xidmət", + "Unknown": "Naməlum", + "unknownDays": "Naməlum gün", + "Cannot connect to the socket server": "Soket serverinə qoşulmaq mümkün olmadı", + "Reconnecting...": "Yenidən qoşulur...", + "General Monitor Type": "Ümumi monitor növü", + "Passive Monitor Type": "Passiv monitor növü", + "Specific Monitor Type": "Xüsusi monitor növü", + "monitorTypeGameServer": "Oyun serveri", + "monitorTypeDatabase": "Verilənlər bazası monitor növü", + "monitorTypeSpecial": "Xüsusi", + "markdownSupported": "Markdown sintaksisi dəstəklənir. HTML işlədirsinizsə, formatlaşma problemlərinin qarşısını almaq üçün sətrin əvvəlində boşluq qoymayın.", + "pauseDashboardHome": "Dayandır", + "Pause": "Dayandır", + "Name": "Ad", + "Status": "Vəziyyət", + "DateTime": "Tarix və vaxt", + "Message": "Mesaj", + "No incidents recorded": "Qeydə alınmış hadisə yoxdur", + "Load More": "Daha çox yüklə", + "Loading...": "Yüklənir...", + "No important events": "Vacib hadisə yoxdur", + "Resume": "Davam et", + "Edit": "Redaktə et", + "Delete": "Sil", + "Current": "Cari", + "Uptime": "Əlçatanlıq", + "Cert Exp.": "Sert. bitmə tarixi.", + "Monitors": "{n} monitor | {n} monitor", + "now": "indi", + "time ago": "{0} əvvəl", + "days": "{n} gün | {n} gün", + "hours": "{n} saat | {n} saat", + "minutes": "{n} dəqiqə | {n} dəqiqə", + "minuteShort": "{n} dəq | {n} dəq", + "years": "{n} il | {n} il", + "Response": "Cavab", + "Pin this incident": "Bu hadisəni sabitlə", + "Ping": "Ping", + "Monitor Type": "Monitor növü", + "Keyword": "Açar söz", + "Invert Keyword": "Açar sözü tərsinə çevir", + "Expected Value": "Gözlənilən dəyər", + "Json Query Expression": "JSON Sorğu İfadəsi", + "Friendly Name": "Görünən ad", + "defaultFriendlyName": "Yeni monitor", + "URL": "URL", + "Hostname": "Host adı", + "Host URL": "Host URL-i", + "locally configured mail transfer agent": "lokal tənzimlənmiş poçt ötürmə agenti", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Ya qoşulmaq istədiyiniz serverin host adını yazın, ya da {local_mta} işlətmək niyyətindəsinizsə {localhost} yazın", + "Port": "Port", + "Path": "Yol", + "Heartbeat Interval": "Yoxlama aralığı", + "Request Timeout": "Sorğunun gözləmə müddəti", + "Connection Timeout": "Bağlantının gözləmə müddəti", + "timeoutAfter": "{0} saniyədən sonra vaxt bitir", + "Retries": "Təkrar cəhdlər", + "Heartbeat Retry Interval": "Təkrar yoxlama aralığı", + "Resend Notification if Down X times consecutively": "Ardıcıl X dəfə işləmirsə bildirişi yenidən göndər", + "Advanced": "Qabaqcıl", + "checkEverySecond": "Hər {0} saniyədən bir yoxla", + "retryCheckEverySecond": "Hər {0} saniyədən bir təkrar cəhd et", + "resendEveryXTimes": "Hər {0} dəfədən bir yenidən göndər", + "resendDisabled": "Yenidən göndərmə söndürülüb", + "retriesDescription": "Xidmət işləmir kimi işarələnib bildiriş göndərilənə qədər maksimum təkrar cəhd sayı", + "Only retry if status code check fails": "Yalnız status kodu yoxlaması uğursuz olanda təkrar cəhd et", + "retryOnlyOnStatusCodeFailureDescription": "Aktivləşdirilsə, təkrar cəhdlər yalnız HTTP status kodu yoxlaması uğursuz olanda edilir (məsələn, server çöküb). Status kodu keçirsə, amma JSON sorğusu uğursuz olsa, monitor təkrar cəhd olmadan dərhal işləmir kimi işarələnəcək.", + "ignoredTLSError": "TLS/SSL xətaları nəzərə alınmadı", + "ignoreTLSError": "HTTPS saytları üçün TLS/SSL xətalarını nəzərə alma", + "ignoreTLSErrorGeneral": "Bağlantı üçün TLS/SSL xətasını nəzərə alma", + "upsideDownModeDescription": "Vəziyyəti tərsinə çevirir. Xidmət əlçatandırsa, o, İŞLƏMİR sayılır.", + "ignoreSecWebsocketAcceptHeaderDescription": "WebSocket keçidi uğurlu olarsa, serverin Sec-WebSocket-Accept başlığı ilə cavab verməməsinə icazə verir.", + "Ignore Sec-WebSocket-Accept header": "{0} başlığını nəzərə alma", + "wsSubprotocolDescription": "Alt protokolların vergüllə ayrılmış siyahısını yazın. Alt protokollar haqqında ətraflı məlumat üçün baxın: {documentation}", + "wsCodeDescription": "Status kodları haqqında ətraflı məlumat üçün baxın: {rfc6455}", + "Subprotocol(s)": "Alt protokol(lar)", + "maxRedirectDescription": "İzləniləcək maksimum yönləndirmə sayı. Yönləndirmələri söndürmək üçün 0 qoyun.", + "Upside Down Mode": "Tərsinə rejim", + "Max. Redirects": "Maks. yönləndirmə", + "saveResponseForNotifications": "Uğurlu HTTP cavabını bildirişlər üçün saxla", + "saveErrorResponseForNotifications": "HTTP xəta cavabını bildirişlər üçün saxla", + "saveResponseDescription": "HTTP cavabını saxlayır və onu bildiriş şablonlarına {templateVariable} kimi verir", + "responseMaxLength": "Cavabın maksimum uzunluğu (bayt)", + "responseMaxLengthDescription": "Saxlanılacaq cavab məlumatının maksimum ölçüsü. Limitsiz üçün 0 qoyun. Daha böyük cavablar kəsiləcək. Standart: 1024 (1 KB)", + "Accepted Status Codes": "Qəbul edilən status kodları", + "Push URL": "Push URL-i", + "needPushEvery": "Bu URL-i hər {0} saniyədən bir çağırmalısınız.", + "pushOptionalParams": "Könüllü parametrlər: {0}", + "pushViewCode": "Push monitorunu necə işlətməli? (Koda bax)", + "pushOthers": "Digərləri", + "programmingLanguages": "Proqramlaşdırma dilləri", + "Save": "Yadda saxla", + "Notifications": "Bildirişlər", + "Not available, please setup.": "Əlçatan deyil, tənzimləyin.", + "Setup Notification": "Bildirişi tənzimlə", + "Light": "İşıqlı", + "Dark": "Qaranlıq", + "Auto": "Avtomatik", + "Theme - Heartbeat Bar": "Tema - yoxlama zolağı", + "styleElapsedTime": "Yoxlama zolağının altındakı keçən vaxt", + "styleElapsedTimeShowNoLine": "Göstər (xəttsiz)", + "styleElapsedTimeShowWithLine": "Göstər (xətlə)", + "Normal": "Normal", + "Bottom": "Aşağıda", + "None": "Yoxdur", + "Timezone": "Saat qurşağı", + "Search Engine Visibility": "Axtarış sistemlərində görünürlük", + "Allow indexing": "İndeksləşdirməyə icazə ver", + "Discourage search engines from indexing site": "Axtarış sistemlərinin saytı indeksləşdirməsinin qarşısını al", + "Change Password": "Parolu dəyiş", + "Current Password": "Cari parol", + "New Password": "Yeni parol", + "Repeat New Password": "Yeni parolu təkrarlayın", + "Update Password": "Parolu yenilə", + "Disable Auth": "Kimlik doğrulamasını söndür", + "Enable Auth": "Kimlik doğrulamasını aktivləşdir", + "disableauth.message1": "{disableAuth} istədiyinizə əminsiniz?", + "disable authentication": "kimlik doğrulamasını söndürmək", + "disableauth.message2": "Bu, Uptime Kuma-nın qarşısında Cloudflare Access, Authelia və ya digər kimlik doğrulama mexanizmləri kimi {intendThirdPartyAuth} ssenariləri üçün nəzərdə tutulub.", + "where you intend to implement third-party authentication": "üçüncü tərəf kimlik doğrulaması tətbiq etmək istədiyiniz", + "Please use this option carefully!": "Bu seçimi ehtiyatla işlədin!", + "Logout": "Çıxış", + "logoutCurrentUser": "{username} hesabından çıx", + "Leave": "Çıx", + "I understand, please disable": "Başa düşürəm, söndürün", + "Confirm": "Təsdiqlə", + "Yes": "Bəli", + "No": "Xeyr", + "Username": "İstifadəçi adı", + "Password": "Parol", + "Remember me": "Məni yadda saxla", + "Login": "Daxil ol", + "No Monitors, please": "Monitor yoxdur,", + "add one": "bir dənə əlavə edin", + "Notification Type": "Bildiriş növü", + "Email": "E-poçt", + "Test": "Yoxla", + "Certificate Info": "Sertifikat məlumatı", + "Resolver Server(s)": "Həlledici server(lər)", + "Resource Record Type": "Resurs qeydinin növü", + "Last Result": "Son nəticə", + "Create your admin account": "Administrator hesabınızı yaradın", + "Repeat Password": "Parolu təkrarlayın", + "Incident description": "Hadisənin təsviri", + "Incident not found or access denied": "Hadisə tapılmadı və ya giriş qadağandır", + "Past Incidents": "Keçmiş hadisələr", + "Incident title": "Hadisənin başlığı", + "Import Backup": "Ehtiyat nüsxəni idxal et", + "Export Backup": "Ehtiyat nüsxəni ixrac et", + "Export": "İxrac", + "Import": "İdxal", + "respTime": "Cavab vaxtı (ms)", + "notAvailableShort": "Yoxdur", + "Default enabled": "Standart olaraq aktiv", + "Apply on all existing monitors": "Bütün mövcud monitorlara tətbiq et", + "Create": "Yarat", + "Clear Data": "Məlumatı təmizlə", + "Events": "Hadisələr", + "Heartbeats": "Yoxlamalar", + "Auto Get": "Avtomatik al", + "Schedule maintenance": "Texniki xidmət planlaşdır", + "Affected Monitors": "Təsirə məruz qalan monitorlar", + "Pick Affected Monitors...": "Təsirə məruz qalan monitorları seçin…", + "Start of maintenance": "Texniki xidmətin başlanğıcı", + "All Status Pages": "Bütün vəziyyət səhifələri", + "Select status pages...": "Vəziyyət səhifələrini seçin…", + "alertNoFile": "İdxal etmək üçün fayl seçin.", + "alertWrongFileType": "JSON faylı seçin.", + "Clear all statistics": "Bütün statistikanı təmizlə", + "Skip existing": "Mövcud olanları ötür", + "Overwrite": "Üzərinə yaz", + "Options": "Seçimlər", + "Keep both": "Hər ikisini saxla", + "Verify Token": "Tokeni yoxla", + "Setup 2FA": "2FA-nı tənzimlə", + "Enable 2FA": "2FA-nı aktivləşdir", + "Disable 2FA": "2FA-nı söndür", + "2FA Settings": "2FA parametrləri", + "Two Factor Authentication": "İkifaktorlu doğrulama", + "filterActive": "Aktiv", + "filterActivePaused": "Dayandırılıb", + "Active": "Aktiv", + "Inactive": "Qeyri-aktiv", + "Token": "Token", + "Show URI": "URI-ni göstər", + "Tags": "Teqlər", + "Add New Tag": "Yeni teq əlavə et", + "Add Tags": "Teq əlavə et", + "Add New below or Select...": "Aşağıda yenisini əlavə edin və ya seçin…", + "Tag with this name already exist.": "Bu adla teq artıq mövcuddur.", + "Tag with this value already exist.": "Bu dəyərlə teq artıq mövcuddur.", + "tagAlreadyOnMonitor": "Bu teq (ad və dəyər) artıq monitordadır və ya əlavə olunmağı gözləyir.", + "tagAlreadyStaged": "Bu teq (ad və dəyər) artıq bu dəstə üçün hazırlanıb.", + "tagNameExists": "Bu adla sistem teqi artıq mövcuddur. Onu siyahıdan seçin və ya başqa ad işlədin.", + "color": "Rəng", + "value (optional)": "dəyər (könüllü)", + "Gray": "Boz", + "Red": "Qırmızı", + "Orange": "Narıncı", + "Green": "Yaşıl", + "Blue": "Mavi", + "Indigo": "İndiqo", + "Purple": "Bənövşəyi", + "Pinned incidents are shown prominently on the status page": "Sabitlənmiş hadisələr vəziyyət səhifəsində önə çıxarılır", + "Pink": "Çəhrayı", + "Custom": "Fərdi", + "Search...": "Axtarış…", + "Search monitored sites": "İzlənilən saytlarda axtar", + "Avg. Ping": "Orta ping", + "Avg. Response": "Orta cavab", + "Entry Page": "Giriş səhifəsi", + "statusPageNothing": "Burada heç nə yoxdur, qrup və ya monitor əlavə edin.", + "statusPageRefreshIn": "Yenilənməyə qalıb: {0}", + "No Services": "Xidmət yoxdur", + "All Systems Operational": "Bütün sistemlər işləkdir", + "Partially Degraded Service": "Xidmət qismən zəifləyib", + "Degraded Service": "Xidmət zəifləyib", + "Add Group": "Qrup əlavə et", + "Add a monitor": "Monitor əlavə et", + "Edit Incident": "Hadisəni redaktə et", + "Edit Status Page": "Vəziyyət səhifəsini redaktə et", + "Go to Dashboard": "İdarə panelinə keç", + "Status Page": "Vəziyyət səhifəsi", + "Status Pages": "Vəziyyət səhifələri", + "defaultNotificationName": "Mənim {notification} bildirişim ({number})", + "here": "burada", + "Required": "Mütləqdir", + "Post URL": "Post URL-i", + "Content Type": "Məzmun növü", + "webhookJsonDesc": "{0} Express.js kimi müasir HTTP serverləri üçün uyğundur", + "webhookFormDataDesc": "{multipart} PHP üçün uyğundur. JSON {decodeFunction} ilə oxunmalı olacaq", + "liquidIntroduction": "Şablonlaşdırma Liquid şablon dili ilə edilir. İstifadə göstərişləri üçün {0} baxın.", + "templateAvailableVariables": "Əlçatan dəyişənlər", + "example": "Nümunə", + "Result": "Nəticə", + "templateMsg": "bildirişin mətni", + "templateHeartbeatJSON": "yoxlamanı təsvir edən obyekt", + "templateMonitorJSON": "monitoru təsvir edən obyekt", + "templateLimitedToUpDownCertNotifications": "yalnız İŞLƏYİR/İŞLƏMİR/sertifikatın bitməsi bildirişlərində əlçatandır", + "templateLimitedToUpDownNotifications": "yalnız İŞLƏYİR/İŞLƏMİR bildirişlərində əlçatandır", + "templateServiceName": "xidmətin adı", + "templateHostnameOrURL": "host adı və ya URL", + "templateStatus": "vəziyyət", + "webhookAdditionalHeadersTitle": "Əlavə başlıqlar", + "webhookAdditionalHeadersDesc": "Webhook ilə göndərilən əlavə başlıqları təyin edir. Hər başlıq JSON açar/dəyər cütü kimi yazılmalıdır.", + "webhookBodyPresetOption": "Hazır qəlib - {0}", + "webhookBodyCustomOption": "Fərdi gövdə", + "Webhook URL": "Webhook URL-i", + "Application Token": "Tətbiq tokeni", + "Server URL": "Server URL-i", + "Priority": "Prioritet", + "emojiCheatSheet": "Emoji siyahısı: {0}", + "Read more": "Ətraflı oxu", + "appriseInstalled": "Apprise quraşdırılıb.", + "appriseNotInstalled": "Apprise quraşdırılmayıb. {0}", + "Method": "Metod", + "Body": "Gövdə", + "Headers": "Başlıqlar", + "PushUrl": "Push URL-i", + "HeadersInvalidFormatBecause": "Sorğu başlıqları düzgün JSON deyil, çünki {error}", + "BodyInvalidFormatBecause": "Sorğunun gövdəsi düzgün JSON deyil, çünki {error}", + "Monitor History": "Monitorun tarixçəsi", + "clearDataOlderThan": "Monitorun tarixçə məlumatını {0} gün saxla.", + "PasswordsDoNotMatch": "Parollar uyğun gəlmir.", + "records": "qeyd", + "One record": "Bir qeyd", + "steamApiKeyDescriptionAt": "Steam oyun serverini izləmək üçün Steam Web-API açarı lazımdır. API açarınızı burada qeydiyyatdan keçirə bilərsiniz: {url}", + "topic": "Mövzu", + "topicExplanation": "İzləniləcək MQTT mövzusu", + "mqttWebSocketPath": "MQTT WebSocket yolu", + "mqttWebsocketPathExplanation": "WebSocket üzərindən MQTT bağlantıları üçün WebSocket yolu (məsələn, /mqtt)", + "mqttWebsocketPathInvalid": "Düzgün WebSocket yolu formatı işlədin", + "mqttHostnameTip": "Bu formatı işlədin: {hostnameFormat}", + "hostnameCannotBeIP": "DNS host adı IP ola bilməz. Həlledici sahəsini nəzərdə tutmusunuz?", + "invalidHostnameOrIP": "Host adı və ya IP yanlışdır. Host adı düzgün FQDN olmalıdır. Ulduz işarəsi işlədilə bilməz. Alt xətt ola bilər və ya nöqtə ilə bitə bilər.", + "invalidDNSHostname": "Host adı yanlışdır. Host adı düzgün FQDN olmalıdır. Ulduz işarəli ola bilər, alt xətt saxlaya bilər və ya nöqtə ilə bitə bilər.", + "wildcardOnlyForDNS": "Ulduz işarəli host adları yalnız DNS monitorlarında dəstəklənir.", + "invalidURL": "URL yanlışdır", + "successKeyword": "Uğur açar sözü", + "successKeywordExplanation": "Uğur sayılacaq MQTT açar sözü", + "recent": "Son", + "Resolve": "Həll et", + "Resolved": "Həll edilib", + "Reset Token": "Tokeni sıfırla", + "Done": "Hazırdır", + "Info": "Məlumat", + "Security": "Təhlükəsizlik", + "Steam API Key": "Steam API açarı", + "Shrink Database": "Verilənlər bazasını sıxlaşdır", + "shrinkDatabaseDescriptionSqlite": "SQLite üçün {vacuum} əmrini işə salır. {auto_vacuum} artıq aktivdir, amma o, bazanı defraqmentasiya etmir və ayrı-ayrı baza səhifələrini {vacuum} əmrinin etdiyi kimi yenidən yığmır.", + "Pick a RR-Type...": "RR növünü seçin…", + "Pick Accepted Status Codes...": "Qəbul edilən status kodlarını seçin…", + "Default": "Standart", + "HTTP Options": "HTTP seçimləri", + "Create Incident": "Hadisə yarat", + "Title": "Başlıq", + "Content": "Məzmun", + "Style": "Üslub", + "info": "məlumat", + "warning": "xəbərdarlıq", + "danger": "təhlükə", + "error": "xəta", + "critical": "kritik", + "primary": "əsas", + "light": "işıqlı", + "dark": "qaranlıq", + "Post": "Dərc et", + "Please input title and content": "Başlıq və məzmun yazın", + "createdAt": "Yaradılıb: {date}", + "lastUpdatedAt": "Son yenilənmə: {date}", + "lastUpdatedAtFromNow": "Son yenilənmə: {date} ({fromNow})", + "Switch to Light Theme": "İşıqlı temaya keç", + "Switch to Dark Theme": "Qaranlıq temaya keç", + "Show Tags": "Teqləri göstər", + "Hide Tags": "Teqləri gizlət", + "Description": "Təsvir", + "descriptionHelpText": "Daxili idarə panelində göstərilir. Markdown-a icazə verilir və göstərilməzdən əvvəl təmizlənir (boşluqlar və abzaslar qorunur).", + "No monitors available.": "Əlçatan monitor yoxdur.", + "Add one": "Bir dənə əlavə edin", + "No Monitors": "Monitor yoxdur", + "Untitled Group": "Başlıqsız qrup", + "Services": "Xidmətlər", + "Discard": "Ləğv et", + "Cancel": "Ləğv et", + "auto-select": "Avtomatik seç", + "Select": "Seç", + "Actions": "Əməliyyatlar", + "selectedMonitorCountMsg": "seçilib: {n} | seçilib: {n}", + "selectMonitorMsg": "Əməliyyat aparmaq üçün monitorları seçin", + "selectAllMonitorsAria": "Bütün monitorları seç", + "deselectAllMonitorsAria": "Bütün monitorların seçimini ləğv et", + "Check/Uncheck": "İşarələ/işarəni götür", + "Powered by": "Müəllif", + "Customize": "Fərdiləşdir", + "Custom Footer": "Fərdi altlıq", + "Custom CSS": "Fərdi CSS", + "deleteIncidentMsg": "Bu hadisəni silmək istədiyinizə əminsiniz?", + "deleteStatusPageMsg": "Bu vəziyyət səhifəsini silmək istədiyinizə əminsiniz?", + "Proxies": "Proksilər", + "default": "Standart", + "enabled": "Aktivdir", + "setAsDefault": "Standart kimi təyin et", + "deleteProxyMsg": "Bu proksini bütün monitorlar üçün silmək istədiyinizə əminsiniz?", + "proxyDescription": "İşləməsi üçün proksi monitora təyin edilməlidir.", + "enableProxyDescription": "Bu proksi aktivləşdirilənə qədər monitor sorğularına təsir etməyəcək. Aktivlik vəziyyəti ilə proksini bütün monitorlarda müvəqqəti söndürə bilərsiniz.", + "setAsDefaultProxyDescription": "Bu proksi yeni monitorlar üçün standart olaraq aktiv olacaq. Yenə də hər monitor üçün onu ayrıca söndürə bilərsiniz.", + "Certificate Chain:": "Sertifikat zənciri:", + "Valid": "Etibarlıdır", + "Invalid": "Etibarsızdır", + "User": "İstifadəçi", + "Installed": "Quraşdırılıb", + "Not installed": "Quraşdırılmayıb", + "Running": "İşləyir", + "Not running": "İşləmir", + "Remove Token": "Tokeni sil", + "Start": "Başlat", + "Stop": "Dayandır", + "Add New Status Page": "Yeni vəziyyət səhifəsi əlavə et", + "Slug": "Slug", + "slug is not found": "Slug tapılmadı", + "Accept characters:": "Qəbul edilən simvollar:", + "startOrEndWithOnly": "Yalnız {0} ilə başlaya və ya bitə bilər", + "No consecutive dashes": "Ardıcıl tire olmaz", + "statusPageSpecialSlugDesc": "Xüsusi slug {0}: slug göstərilmədikdə bu səhifə göstəriləcək", + "Next": "Növbəti", + "The slug is already taken. Please choose another slug.": "Bu slug artıq tutulub. Başqa slug seçin.", + "No Proxy": "Proksi yoxdur", + "Authentication": "Kimlik doğrulaması", + "HTTP Basic Auth": "HTTP Basic Auth", + "Bearer Token": "Bearer tokeni", + "New Status Page": "Yeni vəziyyət səhifəsi", + "Page Not Found": "Səhifə tapılmadı", + "Reverse Proxy": "Tərs proksi", + "Backup": "Ehtiyat nüsxə", + "About": "Haqqında", + "wayToGetCloudflaredURL": "(cloudflared-i buradan endirin: {0})", + "cloudflareWebsite": "Cloudflare saytı", + "Message:": "Mesaj:", + "Don't know how to get the token? Please read the guide:": "Tokeni necə almağı bilmirsiniz? Təlimatı oxuyun:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Hazırda Cloudflare Tunnel vasitəsilə qoşulmusunuzsa, cari bağlantı kəsilə bilər. Onu dayandırmaq istədiyinizə əminsiniz? Təsdiq üçün cari parolunuzu yazın.", + "HTTP Headers": "HTTP başlıqları", + "Trust Proxy": "Proksiyə etibar et", + "Other Software": "Digər proqram təminatı", + "For example: nginx, Apache and Traefik.": "Məsələn: nginx, Apache və Traefik.", + "Please input content": "Məzmun yazın", + "Please input title": "Başlıq yazın", + "Please read": "Oxuyun", + "Subject:": "Mövzu:", + "Valid To:": "Etibarlıdır:", + "Days Remaining:": "Qalan gün:", + "Issuer:": "Verən:", + "Fingerprint:": "Barmaq izi:", + "No status pages": "Vəziyyət səhifəsi yoxdur", + "Domain Name Expiry Notification": "Domen adının bitməsi bildirişi", + "Add a new expiry notification day": "Yeni bitmə bildirişi günü əlavə et", + "Remove the expiry notification": "Bitmə bildirişi gününü sil", + "Proxy": "Proksi", + "dateCreatedAtFromNow": "Yaradılma tarixi: {date} ({fromNow})", + "Footer Text": "Altlıq mətni", + "RSS Title": "RSS başlığı", + "Leave blank to use status page title": "Vəziyyət səhifəsinin başlığını işlətmək üçün boş buraxın", + "Refresh Interval": "Yenilənmə aralığı", + "Refresh Interval Description": "Vəziyyət səhifəsi hər {0} saniyədən bir tam yenilənəcək", + "Show Powered By": "Təminatçını göstər", + "Domain Names": "Domen adları", + "signedInDisp": "Daxil olmusunuz: {0}", + "signedInDispDisabled": "Kimlik doğrulaması söndürülüb.", + "RadiusSecret": "Radius gizli açarı", + "RadiusSecretDescription": "Müştəri ilə server arasında paylaşılan gizli açar", + "RadiusCalledStationId": "Çağırılan stansiyanın id-si", + "RadiusCalledStationIdDescription": "Çağırılan cihazın identifikatoru", + "RadiusCallingStationId": "Çağıran stansiyanın id-si", + "RadiusCallingStationIdDescription": "Çağıran cihazın identifikatoru", + "Certificate Expiry Notification": "Sertifikatın bitməsi bildirişi", + "certificateExpiryNotificationHelp": "Neçə gün əvvəldən xəbərdarlıq ediləcəyi Parametrlərdə tənzimlənə bilər.", + "Cloud ID": "Cloud ID", + "API Username": "API istifadəçi adı", + "API Key": "API açarı", + "API Token": "API tokeni", + "See Jira Cloud Docs": "Jira Cloud sənədlərinə bax", + "Show update if available": "Yeniləmə varsa göstər", + "Also check beta release": "Beta buraxılışını da yoxla", + "Using a Reverse Proxy?": "Tərs proksi işlədirsiniz?", + "Check how to config it for WebSocket": "Onu WebSocket üçün necə tənzimləməyi oxuyun", + "Steam Game Server": "Steam oyun serveri", + "Most likely causes:": "Ən ehtimallı səbəblər:", + "The resource is no longer available.": "Resurs artıq əlçatan deyil.", + "There might be a typing error in the address.": "Ünvanda yazı səhvi ola bilər.", + "What you can try:": "Nə edə bilərsiniz:", + "Retype the address.": "Ünvanı yenidən yazın.", + "Go back to the previous page.": "Əvvəlki səhifəyə qayıdın.", + "Coming Soon": "Tezliklə", + "Connection String": "Bağlantı sətri", + "Query": "Sorğu", + "settingsCertificateExpiry": "TLS sertifikatının bitməsi", + "certificationExpiryDescription": "HTTPS monitorları TLS sertifikatının bitməsinə bu qədər qalanda bildiriş göndərir:", + "certHostnameMismatch": "Sertifikatın host adı monitorun URL-i ilə uyğun gəlmir.", + "Setup Docker Host": "Docker hostunu tənzimlə", + "Connection Type": "Bağlantı növü", + "Docker Daemon": "Docker demonu", + "noDockerHostMsg": "Əlçatan deyil. Əvvəlcə Docker hostu tənzimləyin.", + "DockerHostRequired": "Bu monitor üçün Docker hostunu təyin edin.", + "deleteDockerHostMsg": "Bu Docker hostunu bütün monitorlar üçün silmək istədiyinizə əminsiniz?", + "socket": "Soket", + "tcp": "TCP / HTTP", + "tailscalePingWarning": "Tailscale Ping monitorunu işlətmək üçün Uptime Kuma-nı Docker olmadan quraşdırmalı və serverinizə Tailscale müştərisini də quraşdırmalısınız.", + "sipsakPingWarning": "SIP Options Ping monitorunu işlətmək üçün Uptime Kuma-nı Docker olmadan quraşdırmalı və serverinizə Sipsak müştərisini də quraşdırmalısınız.", + "Docker Container": "Docker konteyneri", + "Container Name / ID": "Konteynerin adı / ID-si", + "Docker Host": "Docker hostu", + "Docker Hosts": "Docker hostları", + "Domain": "Domen", + "Workstation": "İş stansiyası", + "Packet Size": "Paketin ölçüsü", + "pingPacketSizeWarning": "16-dan kiçik paket ölçüsü Linux-da gediş-gəliş vaxtı vermir", + "Bot Token": "Bot tokeni", + "wayToGetTelegramToken": "Tokeni buradan ala bilərsiniz: {0}.", + "wayToGetMaxToken": "MAX bot tokenini və digər məlumatları buradan ala bilərsiniz: {0}.", + "maxMessenger": "MAX messencer", + "Chat ID": "Söhbət ID-si", + "telegramMessageThreadID": "(Könüllü) Mesaj mövzusunun ID-si", + "telegramMessageThreadIDDescription": "Forumun hədəf mesaj mövzusunun könüllü unikal identifikatoru; yalnız forum superqrupları üçün", + "telegramSendSilently": "Səssiz göndər", + "telegramSendSilentlyDescription": "Mesajı səssiz göndərir. İstifadəçilər səssiz bildiriş alacaq.", + "telegramProtectContent": "Yönləndirmə/saxlamadan qoru", + "telegramProtectContentDescription": "Aktivləşdirilsə, Telegram-dakı bot mesajları yönləndirmə və saxlamadan qorunacaq.", + "telegramUseTemplate": "Fərdi mesaj şablonu işlət", + "telegramUseTemplateDescription": "Aktivləşdirilsə, mesaj fərdi şablonla göndəriləcək.", + "matrixUseTemplate": "Fərdi mesaj şablonu işlət", + "matrixUseTemplateDescription": "Aktivləşdirilsə, mesaj fərdi şablonla göndəriləcək.", + "telegramTemplateFormatDescription": "Telegram mesajlar üçün müxtəlif işarələmə dillərinə icazə verir, təfərrüat üçün Telegram {0} sənədinə baxın.", + "supportTelegramChatID": "Birbaşa söhbətin / qrupun / kanalın söhbət ID-si dəstəklənir", + "wayToGetTelegramChatID": "Söhbət ID-nizi bota mesaj göndərib bu URL-ə keçməklə chat_id-ni görə bilərsiniz:", + "maxApiUrlDescription": "MAX messencer üçün baza API URL-i. Standart: https://platform-api.max.ru", + "wayToGetMaxChatID": "MAX-da mesajların çatdırılacağı söhbətin identifikatorunu göstərin.", + "maxUseTemplate": "Fərdi mesaj şablonu işlət", + "maxUseTemplateDescription": "Aktivləşdirilsə, mesaj fərdi şablonla göndəriləcək.", + "maxTemplateFormatDescription": "MAX messencer adi mətn, Markdown və HTML formatlaşdırmasını dəstəkləyir.", + "telegramServerUrl": "(Könüllü) Server URL-i", + "telegramServerUrlDescription": "Telegram bot API-sinin məhdudiyyətlərini aradan qaldırmaq və ya bloklanmış ərazilərdə (Çin, İran və s.) giriş əldə etmək üçün. Ətraflı məlumat üçün {0} klikləyin. Standart: {1}", + "YOUR BOT TOKEN HERE": "BOT TOKENİNİZİ BURA YAZIN", + "chatIDNotFound": "Söhbət ID-si tapılmadı; əvvəlcə bu bota mesaj göndərin", + "disableCloudflaredNoAuthMsg": "Kimlik doğrulamasız rejimdəsiniz, parol lazım deyil.", + "trustProxyDescription": "'X-Forwarded-*' başlıqlarına etibar et. Düzgün müştəri IP-sini almaq istəyirsinizsə və Uptime Kuma-nız Nginx və ya Apache kimi proksinin arxasındadırsa, bunu aktivləşdirin.", + "wayToGetLineNotifyToken": "Giriş tokenini buradan ala bilərsiniz: {0}", + "Examples:": "Nümunələr: {0}", + "supportBaleChatID": "Birbaşa söhbətin / qrupun / kanalın söhbət ID-si dəstəklənir", + "wayToGetBaleChatID": "Söhbət ID-nizi bota mesaj göndərib bu URL-ə keçməklə chat_id-ni görə bilərsiniz:", + "wayToGetBaleToken": "Tokeni buradan ala bilərsiniz: {0}.", + "Home Assistant URL": "Home Assistant URL-i", + "Long-Lived Access Token": "Uzunömürlü giriş tokeni", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Uzunömürlü giriş tokenini profil adınıza (sol alt küncdə) klikləyib ən aşağı sürüşdürməklə və sonra Create Token düyməsini basmaqla yarada bilərsiniz.", + "Notification Action": "Bildiriş əməliyyatı", + "default: notify all devices": "standart: bütün cihazlara bildir", + "homeAssistantNotificationActionHelptext": "Bildiriş əməliyyatlarının siyahısını Home Assistant-da \"Settings > Developer Tools > Actions\" bölməsində tapa bilərsiniz. Əməliyyatlarınızı tapmaq üçün \"notify\" axtarın. Yalnız \"notify.\" hissəsindən sonrakını yazın, məsələn \"notify.mobile_app_xyz\" əməliyyatı üçün \"mobile_app_xyz\" yazın. Daxili mobil bildirişlər üçün \"Send a notification via mobile_app_xyz\" seçimini axtarın (\"Send a notification\" yox).", + "Automations can optionally be triggered in Home Assistant:": "Home Assistant-da avtomatlaşdırmalar könüllü olaraq işə salına bilər:", + "Trigger type:": "Tetikləyicinin növü:", + "Event type:": "Hadisənin növü:", + "Event data:": "Hadisənin məlumatı:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Sonra əməliyyat seçin, məsələn RGB işığın qırmızı olduğu səhnəyə keçin.", + "frontendVersionIs": "Frontend versiyası: {version}", + "Frontend Version do not match backend version!": "Frontend versiyası backend versiyası ilə uyğun gəlmir!", + "backupOutdatedWarning": "Köhnəlib: çoxlu yeni imkan əlavə edildiyi və bu ehtiyat nüsxə imkanı bir qədər baxımsız qaldığı üçün o, tam ehtiyat nüsxə yarada və bərpa edə bilmir.", + "backupRecommend": "Bunun əvəzinə birbaşa volume-un və ya məlumat qovluğunun (./data/) ehtiyat nüsxəsini çıxarın.", + "Optional": "Könüllü", + "and": "və", + "or": "və ya", + "sameAsServerTimezone": "Serverin saat qurşağı ilə eyni", + "startDateTime": "Başlama tarixi/vaxtı", + "endDateTime": "Bitmə tarixi/vaxtı", + "cronExpression": "Cron ifadəsi", + "cronScheduleDescription": "Cədvəl: {description}", + "Duration (Minutes)": "Müddət (dəqiqə)", + "invalidCronExpression": "Yanlış cron ifadəsi: {0}", + "recurringInterval": "Aralıq", + "Recurring": "Təkrarlanan", + "strategyManual": "Əl ilə aktiv/qeyri-aktiv", + "warningTimezone": "Serverin saat qurşağı işlədilir", + "weekdayShortMon": "B.e.", + "weekdayShortTue": "Ç.a.", + "weekdayShortWed": "Ç.", + "weekdayShortThu": "C.a.", + "weekdayShortFri": "C.", + "weekdayShortSat": "Ş.", + "weekdayShortSun": "B.", + "dayOfWeek": "Həftənin günü", + "dayOfMonth": "Ayın günü", + "lastDay": "Son gün", + "lastDay1": "Ayın son günü", + "lastDay2": "Ayın sondan 2-ci günü", + "lastDay3": "Ayın sondan 3-cü günü", + "lastDay4": "Ayın sondan 4-cü günü", + "No Maintenance": "Texniki xidmət yoxdur", + "pauseMaintenanceMsg": "Dayandırmaq istədiyinizə əminsiniz?", + "maintenanceStatus-under-maintenance": "Texniki xidmətdədir", + "maintenanceStatus-inactive": "Qeyri-aktiv", + "maintenanceStatus-scheduled": "Planlaşdırılıb", + "maintenanceStatus-ended": "Bitib", + "maintenanceStatus-unknown": "Naməlum", + "Display Timezone": "Göstərilən saat qurşağı", + "Server Timezone": "Serverin saat qurşağı", + "statusPageMaintenanceEndDate": "Son", + "IconUrl": "İkon URL-i", + "Enable DNS Cache": "(Köhnəlib) HTTP(s) monitorları üçün DNS keşini aktivləşdir", + "Enable": "Aktivləşdir", + "Disable": "Söndür", + "enableNSCD": "Bütün DNS sorğularını keşləmək üçün NSCD-ni (Name Service Cache Daemon) aktivləşdir", + "chromeExecutable": "Chrome/Chromium icra faylı", + "chromeExecutableAutoDetect": "Avtomatik tap", + "chromeExecutableDescription": "Docker istifadəçiləri üçün: Chromium hələ quraşdırılmayıbsa, quraşdırma və yoxlama nəticəsinin göstərilməsi bir neçə dəqiqə çəkə bilər. Diskdə 1 GB yer tutur.", + "dnsCacheDescription": "Bəzi IPv6 mühitlərində işləməyə bilər, problem yaşasanız söndürün.", + "Single Maintenance Window": "Tək texniki xidmət pəncərəsi", + "Maintenance Time Window of a Day": "Günün texniki xidmət vaxt pəncərəsi", + "Effective Date Range": "Qüvvədə olma tarix aralığı (könüllü)", + "Schedule Maintenance": "Texniki xidmət planlaşdır", + "Edit Maintenance": "Texniki xidməti redaktə et", + "Clone Maintenance": "Texniki xidməti klonla", + "ariaPauseMaintenance": "Bu texniki xidmət cədvəlini dayandır", + "ariaResumeMaintenance": "Bu texniki xidmət cədvəlini davam etdir", + "ariaCloneMaintenance": "Bu texniki xidmət cədvəlinin nüsxəsini yarat", + "ariaEditMaintenance": "Bu texniki xidmət cədvəlini redaktə et", + "ariaDeleteMaintenance": "Bu texniki xidmət cədvəlini sil", + "Date and Time": "Tarix və vaxt", + "DateTime Range": "Tarix və vaxt aralığı", + "loadingError": "Məlumatı almaq mümkün olmadı, sonra yenidən cəhd edin.", + "plugin": "Plagin | Plaginlər", + "install": "Quraşdır", + "installing": "Quraşdırılır", + "uninstall": "Sistemdən sil", + "uninstalling": "Sistemdən silinir", + "confirmUninstallPlugin": "Bu plagini sistemdən silmək istədiyinizə əminsiniz?", + "notificationRegional": "Regional", + "notificationUniversal": "Universal", + "notificationChatPlatforms": "Söhbət platformaları", + "notificationPushServices": "Push xidmətləri", + "notificationSmsServices": "SMS xidmətləri", + "notificationEmail": "E-poçt", + "notificationIncidentManagement": "Hadisələrin idarə edilməsi", + "notificationHomeAutomation": "Ev avtomatlaşdırması", + "notificationOther": "Digər inteqrasiyalar", + "Clone Monitor": "Monitoru klonla", + "Clone": "Klonla", + "cloneOf": "{0} klonu", + "smtp": "E-poçt (SMTP)", + "SMTP Security": "SMTP təhlükəsizliyi", + "Ignore STARTTLS": "STARTTLS-i nəzərə alma", + "Use STARTTLS": "STARTTLS işlət", + "Use HTML for custom E-mail body": "Fərdi e-poçt gövdəsi üçün HTML işlət", + "secureOptionNone": "Yoxdur / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "TLS xətasını nəzərə alma", + "Disable STARTTLS": "STARTTLS-i söndür", + "disableSTARTTLSDescription": "Bu seçimi STARTTLS-i dəstəkləməyən SMTP serverləri üçün aktivləşdirin. Bu, e-poçtları şifrələnməmiş bağlantı ilə göndərəcək.", + "From Email": "Göndərənin e-poçtu", + "emailCustomisableContent": "Fərdiləşdirilə bilən məzmun", + "smtpLiquidIntroduction": "Aşağıdakı iki sahə Liquid şablon dili ilə şablonlaşdırıla bilər. İstifadə göstərişləri üçün baxın: {0}. Əlçatan dəyişənlər bunlardır:", + "emailCustomSubject": "Fərdi mövzu", + "leave blank for default subject": "standart mövzu üçün boş buraxın", + "emailCustomBody": "Fərdi gövdə", + "leave blank for default body": "standart gövdə üçün boş buraxın", + "emailTemplateMonitorJSON": "monitoru təsvir edən obyekt", + "emailTemplateHeartbeatJSON": "yoxlamanı təsvir edən obyekt", + "emailTemplateMsg": "bildirişin mətni", + "emailTemplateLimitedToUpDownNotification": "yalnız İŞLƏYİR/İŞLƏMİR yoxlamalarında əlçatandır, əks halda null", + "To Email": "Alıcının e-poçtu", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord webhook URL-i", + "wayToGetDiscordURL": "Bunu Server Settings -> Integrations -> View Webhooks -> New Webhook yolu ilə ala bilərsiniz", + "Bot Display Name": "Botun görünən adı", + "Prefix Custom Message": "Fərdi mesaj önlüyü", + "Hello @everyone is...": "Salam {'@'}everyone…", + "Select message type": "Mesaj növünü seçin", + "Send to channel": "Kanala göndər", + "Create new forum post": "Yeni forum yazısı yarat", + "postToExistingThread": "Mövcud mövzuya / forum yazısına göndər", + "forumPostName": "Forum yazısının adı", + "threadForumPostID": "Mövzunun / forum yazısının ID-si", + "e.g. {discordThreadID}": "məsələn, {discordThreadID}", + "whatHappensAtForumPost": "Yeni forum yazısı yaradır. Bu, mövcud yazıya mesaj GÖNDƏRMİR. Mövcud yazıya göndərmək üçün \"{option}\" seçimini işlədin", + "wayToGetDiscordThreadId": "Mövzunun / forum yazısının id-sini almaq kanal id-sini almağa bənzəyir. Id-lərin necə alındığı barədə ətraflı: {0}", + "wayToGetTeamsURL": "Webhook URL-inin necə yaradıldığını buradan öyrənə bilərsiniz: {0}.", + "teamsEnableTags": "Teqləri daxil et", + "teamsEnableTagsDescription": "Aktivləşdirilsə, mesaja monitorun teqləri daxil ediləcək.", + "wayToGetZohoCliqURL": "Webhook URL-inin necə yaradıldığını buradan öyrənə bilərsiniz: {0}.", + "needSignalAPI": "REST API-si olan Signal müştəriniz olmalıdır.", + "wayToCheckSignalURL": "Onun necə qurulduğunu bu URL-də görə bilərsiniz:", + "Number": "Nömrə", + "Recipients": "Alıcılar", + "Access Token": "Giriş tokeni", + "API Version": "API versiyası", + "Channel access token": "Kanalın giriş tokeni", + "Channel access token (Long-lived)": "Kanalın giriş tokeni (uzunömürlü)", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console - {0}", + "Basic Settings": "Əsas parametrlər", + "User ID": "İstifadəçi ID-si", + "Your User ID": "Sizin istifadəçi ID-niz", + "Messaging API": "Messaging API", + "wayToGetLineChannelToken": "Əvvəlcə {0} açın, provayder və kanal (Messaging API) yaradın, sonra yuxarıda göstərilən menyulardan kanalın giriş tokenini və istifadəçi ID-sini ala bilərsiniz.", + "Icon URL": "İkon URL-i", + "aboutIconURL": "Standart profil şəklini əvəz etmək üçün \"İkon URL-i\" sahəsinə şəkil linki verə bilərsiniz. İkon emojisi təyin edilibsə, işlədilməyəcək.", + "aboutMattermostChannelName": "\"Channel Name\" sahəsinə kanalın adını yazmaqla webhook-un mesaj göndərdiyi standart kanalı dəyişə bilərsiniz. Bunun Mattermost webhook parametrlərində aktiv olması lazımdır. Məsələn: #other-channel", + "dataRetentionTimeError": "Saxlama müddəti 0 və ya daha çox olmalıdır", + "infiniteRetention": "Limitsiz saxlama üçün 0 qoyun.", + "confirmDeleteTagMsg": "Bu teqi silmək istədiyinizə əminsiniz? Bu teqlə əlaqəli monitorlar silinməyəcək.", + "enableGRPCTls": "gRPC sorğusunun TLS bağlantısı ilə göndərilməsinə icazə ver", + "grpcMethodDescription": "Metodun adı camelCase formatına çevrilir, məsələn sayHello, check və s.", + "acceptedStatusCodesDescription": "Uğurlu cavab sayılan status kodlarını seçin.", + "deleteMonitorMsg": "Bu monitoru silmək istədiyinizə əminsiniz?", + "deleteMonitorsMsg": "Seçilmiş monitorları silmək istədiyinizə əminsiniz?", + "pausedMonitorsMsg": "{n} monitor dayandırıldı | {n} monitor dayandırıldı", + "resumedMonitorsMsg": "{n} monitor davam etdirildi | {n} monitor davam etdirildi", + "deletedMonitorsMsg": "{n} monitor silindi | {n} monitor silindi", + "noMonitorsPausedMsg": "Heç bir monitor dayandırılmadı (aktiv olan yox idi)", + "noMonitorsResumedMsg": "Heç bir monitor davam etdirilmədi (qeyri-aktiv olan yox idi)", + "bulkDeleteErrorMsg": "{n} monitoru silmək mümkün olmadı | {n} monitoru silmək mümkün olmadı", + "deleteGroupMsg": "Bu qrupu silmək istədiyinizə əminsiniz?", + "deleteChildrenMonitors": "Varsa, birbaşa alt monitorları və onların alt monitorlarını da sil | Varsa, bütün {count} birbaşa alt monitoru və onların alt monitorlarını da sil", + "deleteMaintenanceMsg": "Bu texniki xidməti silmək istədiyinizə əminsiniz?", + "deleteNotificationMsg": "Bu bildirişi bütün monitorlar üçün silmək istədiyinizə əminsiniz?", + "dnsPortDescription": "DNS serverinin portu. Standart 53-dür. Portu istənilən vaxt dəyişə bilərsiniz.", + "resolverserverDescription": "Standart server Cloudflare-dir. Vergüllə ayrılmış IP ünvanları və ya host adları siyahısı göstərə bilərsiniz.", + "rrtypeDescription": "İzləmək istədiyiniz RR növünü seçin", + "pauseMonitorMsg": "Dayandırmaq istədiyinizə əminsiniz?", + "enableDefaultNotificationDescription": "Bu bildiriş yeni monitorlar üçün standart olaraq aktiv olacaq. Yenə də hər monitor üçün onu ayrıca söndürə bilərsiniz.", + "Clear All Events": "Bütün hadisələri təmizlə", + "clearAllEventsMsg": "Bütün monitorların bütün hadisələrini silmək istədiyinizə əminsiniz? Bu, əlaqəli mesaj məlumatını həmişəlik silir və geri qaytarıla bilməz.", + "Events cleared successfully": "Hadisələr uğurla təmizləndi.", + "No monitors found": "Monitor tapılmadı.", + "Could not clear events": "{failed}/{total} hadisəni təmizləmək mümkün olmadı", + "clearEventsMsg": "Bu monitorun bütün hadisələrini silmək istədiyinizə əminsiniz? Bu, əlaqəli mesaj məlumatını həmişəlik silir və geri qaytarıla bilməz.", + "clearHeartbeatsMsg": "Bu monitorun bütün yoxlamalarını silmək istədiyinizə əminsiniz? Bu, bütün yoxlama məlumatını həmişəlik silir və geri qaytarıla bilməz.", + "confirmClearStatisticsMsg": "BÜTÜN statistikanı silmək istədiyinizə əminsiniz?", + "importHandleDescription": "Eyni adlı hər monitoru və bildirişi ötürmək istəyirsinizsə 'Mövcud olanları ötür' seçin. 'Üzərinə yaz' isə mövcud hər monitoru və bildirişi siləcək.", + "confirmImportMsg": "Ehtiyat nüsxəni idxal etmək istədiyinizə əminsiniz? Düzgün idxal seçimini işarələdiyinizi yoxlayın.", + "twoFAVerifyLabel": "2FA-nı yoxlamaq üçün tokeninizi daxil edin:", + "tokenValidSettingsMsg": "Token etibarlıdır! İndi 2FA parametrlərini yadda saxlaya bilərsiniz.", + "confirmEnableTwoFAMsg": "2FA-nı aktivləşdirmək istədiyinizə əminsiniz?", + "confirmDisableTwoFAMsg": "2FA-nı söndürmək istədiyinizə əminsiniz?", + "recurringIntervalMessage": "Hər gün bir dəfə işlət | Hər {0} gündən bir işlət", + "affectedMonitorsDescription": "Cari texniki xidmətdən təsirlənən monitorları seçin", + "affectedStatusPages": "Bu texniki xidmət mesajını seçilmiş vəziyyət səhifələrində göstər", + "Sets end time based on start time": "Bitmə vaxtını başlama vaxtına görə təyin edir", + "Please set start time first": "Əvvəlcə başlama vaxtını təyin edin", + "noMonitorsSelectedWarning": "Heç bir təsirlənən monitor olmadan texniki xidmət yaradırsınız. Davam etmək istədiyinizə əminsiniz?", + "noMonitorsOrStatusPagesSelectedError": "Təsirlənən monitorlar və ya vəziyyət səhifələri olmadan texniki xidmət yaradıla bilməz", + "passwordNotMatchMsg": "Təkrar parol uyğun gəlmir.", + "notificationDescription": "İşləməsi üçün bildiriş monitora təyin edilməlidir.", + "Peer ID": "Peer ID", + "vkApiVersionDescription": "Sorğular üçün işlədilən VK API versiyası. Uyğunluq üçün başqa VK API versiyası lazım deyilsə, standart dəyəri saxlayın.", + "vkDontParseLinks": "Link parçalarını söndür", + "vkDontParseLinksDescription": "Aktivləşdirilsə, VK link önizləmələri/parçaları yaratmayacaq.", + "vkPeerIdDescription": "Hədəf VK peer_id dəyərini yazın. Bu dəyər API-yə olduğu kimi göndərilir.", + "keywordDescription": "Adi HTML və ya JSON cavabında açar söz axtarır. Axtarış böyük-kiçik hərfə həssasdır.", + "invertKeywordDescription": "Açar sözün olmasını yox, olmamasını axtar.", + "jsonQueryDescription": "JSON sorğusu ilə serverin JSON cavabından konkret məlumatı oxuyub çıxarır; JSON gözlənilmirsə xam cavab üçün \"$\" işlədin. Nəticə sonra gözlənilən dəyərlə mətn kimi müqayisə olunur. Sənədlər üçün {0}, sorğuları sınamaq üçün {1} açın.", + "backupDescription": "Bütün monitorların və bildirişlərin ehtiyat nüsxəsini JSON faylına çıxara bilərsiniz.", + "backupDescription2": "Qeyd: tarixçə və hadisə məlumatı daxil edilmir.", + "backupDescription3": "Bildiriş tokenləri kimi həssas məlumatlar ixrac faylına daxil edilir; faylı təhlükəsiz saxlayın.", + "octopushAPIKey": "İdarə panelindəki HTTP API məlumatlarından \"API key\"", + "octopushLogin": "İdarə panelindəki HTTP API məlumatlarından \"Login\"", + "promosmsLogin": "API giriş adı", + "promosmsPassword": "API parolu", + "pushoversounds pushover": "Pushover (standart)", + "pushoversounds bike": "Velosiped", + "pushoversounds bugle": "Şeypur", + "pushoversounds cashregister": "Kassa aparatı", + "pushoversounds classical": "Klassik", + "pushoversounds cosmic": "Kosmik", + "pushoversounds falling": "Düşmə", + "pushoversounds gamelan": "Qamelan", + "pushoversounds incoming": "Gələn", + "pushoversounds intermission": "Fasilə", + "pushoversounds magic": "Sehr", + "pushoversounds mechanical": "Mexaniki", + "pushoversounds pianobar": "Piano bar", + "pushoversounds siren": "Siren", + "pushoversounds spacealarm": "Kosmik həyəcan", + "pushoversounds tugboat": "Yedək gəmisi", + "pushoversounds alien": "Yad həyəcan (uzun)", + "pushoversounds climb": "Dırmanma (uzun)", + "pushoversounds persistent": "Israrlı (uzun)", + "pushoversounds echo": "Pushover Echo (uzun)", + "pushoversounds updown": "Yuxarı-aşağı (uzun)", + "pushoversounds vibrate": "Yalnız vibrasiya", + "pushoversounds none": "Yoxdur (səssiz)", + "pushyAPIKey": "Gizli API açarı", + "pushyToken": "Cihaz tokeni", + "apprise": "Apprise (50-dən çox bildiriş xidmətini dəstəkləyir)", + "GoogleChat": "Google Chat (yalnız Google Workspace)", + "Google Apps Script Webhook URL": "Google Apps Script webhook URL-i", + "Deploy a Google Apps Script as a web app and paste the URL here": "Google Apps Script-i veb tətbiq kimi yerləşdirin və URL-i buraya yapışdırın", + "Quick Setup Guide": "Qısa quraşdırma təlimatı", + "Open your Google Spreadsheet": "Google Cədvəlinizi açın", + "Go to Extensions → Apps Script": "Extensions → Apps Script bölməsinə keçin", + "Paste the script code (see below)": "Skript kodunu yapışdırın (aşağıya baxın)", + "Click Deploy → New deployment → Web app": "Deploy → New deployment → Web app düymələrini basın", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "'Execute as: Me' və 'Who has access: Anyone' seçin", + "Copy the web app URL and paste it above": "Veb tətbiqin URL-ini kopyalayıb yuxarıya yapışdırın", + "Google Apps Script Code": "Google Apps Script kodu", + "Copy to Clipboard": "Mübadilə buferinə kopyala", + "Copied to clipboard!": "Mübadilə buferinə kopyalandı!", + "Failed to copy to clipboard": "Mübadilə buferinə kopyalamaq mümkün olmadı", + "Template plain text instead of using cards": "Kart əvəzinə adi mətn şablonu işlət", + "issueWithGoogleChatOnAndroidHelptext": "Bu, həm də {issuetackerURL} kimi yuxarı axındakı baqları keçməyə imkan verir", + "wayToGetKookBotToken": "Tətbiq yaradın və bot tokeninizi buradan alın: {0}", + "wayToGetKookGuildID": "Kook parametrlərində 'Developer Mode'-u aktivləşdirin və qildiyaya sağ klikləyib ID-sini alın", + "Guild ID": "Qildiya ID-si", + "User Key": "İstifadəçi açarı", + "Device": "Cihaz", + "Message Title": "Mesajın başlığı", + "Notification Sound": "Bildiriş səsi", + "More info on:": "Ətraflı məlumat: {0}", + "pushoverDesc1": "Təcili prioritetin (2) təkrar cəhdlər arasında standart 30 saniyəlik gözləmə müddəti var və o, 1 saatdan sonra bitir.", + "pushoverDesc2": "Bildirişləri müxtəlif cihazlara göndərmək istəyirsinizsə, Cihaz sahəsini doldurun.", + "pushoverMessageTtl": "Mesajın ömrü (saniyə)", + "SMS Type": "SMS növü", + "octopushTypePremium": "Premium (sürətli - xəbərdarlıq üçün tövsiyə olunur)", + "octopushTypeLowCost": "Ucuz (yavaş - bəzən operator tərəfindən bloklanır)", + "checkPriceAt": "{service} qiymətlərini burada yoxlayın: {url}", + "apiCredentials": "API giriş məlumatları", + "octopushLegacyHint": "Octopush-un köhnə versiyasını (2011-2020), yoxsa yenisini işlədirsiniz?", + "Check octopush prices": "Octopush qiymətlərini yoxlayın: {0}.", + "octopushPhoneNumber": "Telefon nömrəsi (beynəlxalq format, məsələn: +33612345678)", + "octopushSMSSender": "SMS göndərənin adı: 3-11 hərf-rəqəm və boşluq (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea cihaz ID-si", + "Apprise URL": "Apprise URL-i", + "Example:": "Nümunə: {0}", + "Read more:": "Ətraflı: {0}", + "Status:": "Vəziyyət: {0}", + "Strategy": "Strategiya", + "Free Mobile User Identifier": "Free Mobile istifadəçi identifikatoru", + "Free Mobile API Key": "Free Mobile API açarı", + "Enable TLS": "TLS-i aktivləşdir", + "Proto Service Name": "Proto xidmətinin adı", + "Proto Method": "Proto metodu", + "Proto Content": "Proto məzmunu", + "Economy": "Qənaətli", + "Lowcost": "Ucuz", + "high": "yüksək", + "SendKey": "SendKey", + "SMSManager API Docs": "SMSManager API sənədləri", + "Gateway Type": "Şlüz növü", + "You can divide numbers with commas or semicolons": "Nömrələri {comma} və ya {semicolon} ilə ayıra bilərsiniz", + "Base URL": "Baza URL-i", + "goAlertInfo": "GoAlert növbə cədvəli, avtomatik eskalasiya və bildirişlər (SMS və ya səsli zənglər kimi) üçün açıq mənbəli tətbiqdir. Lazım olan adamı, lazım olan üsulla və lazım olan vaxtda avtomatik cəlb edin! {0}", + "goAlertIntegrationKeyInfo": "Xidmət üçün ümumi API inteqrasiya açarını bu formatda alın: \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" - adətən kopyalanmış URL-dəki token parametrinin dəyəridir.", + "AccessKeyId": "AccessKey ID", + "SecretAccessKey": "AccessKey Secret", + "PhoneNumbers": "Telefon nömrələri", + "TemplateCode": "Şablon kodu", + "SignName": "İmza adı", + "OptionalParameters": "Könüllü parametrlər", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Operator məhdudiyyətlərinə görə könüllü dəyişənləri çatdırılmama riski ilə aktivləşdirin", + "aliyun-template-requirements-and-parameters": "Aliyun SMS şablonunda bu parametrlər olmalıdır: {parameters}", + "aliyun-template-optional-parameters": "Könüllü parametrlər: {parameters}", + "Bark API Version": "Bark API versiyası", + "Bark Endpoint": "Bark ünvanı", + "Bark Group": "Bark qrupu", + "Bark Sound": "Bark səsi", + "WebHookUrl": "WebHookUrl", + "SecretKey": "SecretKey", + "For safety, must use secret key": "Təhlükəsizlik üçün gizli açar işlədilməlidir", + "Mentioning": "Qeyd etmə", + "Don't mention people": "İnsanları qeyd etmə", + "Mention group": "{group} qeyd et", + "Mention Mobile List": "Qeyd ediləcək mobil nömrələr", + "Mention User List": "Qeyd ediləcək istifadəçi id-ləri", + "Dingtalk Mobile List": "Mobil nömrələr siyahısı", + "Dingtalk User List": "İstifadəçi ID-ləri siyahısı", + "Enter a list of userId": "userId siyahısını daxil edin", + "Enter a list of mobile": "Mobil nömrələrin siyahısını daxil edin", + "Invalid mobile": "Yanlış mobil nömrə [{mobile}]", + "Invalid userId": "Yanlış userId [{userId}]", + "Device Token": "Cihaz tokeni", + "Platform": "Platforma", + "Huawei": "Huawei", + "High": "Yüksək", + "Retry": "Təkrar cəhd", + "Topic": "Mövzu", + "WeCom Bot Key": "WeCom bot açarı", + "WeCom Mentioned Mobile List": "WeCom-da qeyd ediləcək mobil nömrələr", + "WeCom Mentioned Mobile List Description": "Qeyd ediləcək telefon nömrələrini yazın. Bir neçə nömrəni vergüllə ayırın. Hamını qeyd etmək üçün {'@'}all işlədin.", + "Setup Proxy": "Proksini tənzimlə", + "Proxy Protocol": "Proksi protokolu", + "Proxy Server": "Proksi serveri", + "Proxy server has authentication": "Proksi serverində kimlik doğrulaması var", + "promosmsTypeEco": "SMS ECO - ucuz, amma yavaş və tez-tez yüklüdür. Yalnız Polşa alıcıları üçün.", + "promosmsTypeFlash": "SMS FLASH - mesaj alıcının cihazında avtomatik görünəcək. Yalnız Polşa alıcıları üçün.", + "promosmsTypeFull": "SMS FULL - SMS-in premium səviyyəsi, öz göndərən adınızı işlədə bilərsiniz (əvvəlcə adı qeydiyyatdan keçirmək lazımdır). Xəbərdarlıqlar üçün etibarlıdır.", + "promosmsTypeSpeed": "SMS SPEED - sistemdə ən yüksək prioritet. Çox sürətli və etibarlıdır, amma bahalıdır (təxminən SMS FULL qiymətinin iki misli).", + "promosmsPhoneNumber": "Telefon nömrəsi (Polşa alıcıları üçün şəhər kodunu buraxa bilərsiniz)", + "promosmsSMSSender": "SMS göndərənin adı: əvvəlcədən qeydiyyatdan keçmiş ad və ya standartlardan biri: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "promosmsAllowLongSMS": "Uzun SMS-ə icazə ver", + "Feishu WebHookUrl": "Feishu WebHookURL", + "matrixHomeserverURL": "Homeserver URL-i (http(s):// ilə və istəyə görə portla)", + "Internal Room Id": "Daxili otaq ID-si", + "matrixDesc1": "Daxili otaq ID-sini Matrix müştərinizdə otaq parametrlərinin qabaqcıl bölməsinə baxaraq tapa bilərsiniz. O, !QMdRCpUIfLwsfjxye6:home.server kimi görünməlidir.", + "matrixDesc2": "Yeni istifadəçi yaratmağınız və öz Matrix istifadəçinizin giriş tokenini işlətməməyiniz güclü tövsiyə olunur, çünki o, hesabınıza və qoşulduğunuz bütün otaqlara tam giriş verir. Bunun əvəzinə yeni istifadəçi yaradın və onu yalnız bildiriş almaq istədiyiniz otağa dəvət edin. Giriş tokenini bunu işlətməklə ala bilərsiniz: {0}", + "Channel Name": "Kanalın adı", + "Notify Channel": "Kanalı xəbərdar et", + "aboutNotifyChannel": "Kanalı xəbərdar etmək kanalın bütün üzvləri üçün masaüstü və ya mobil bildiriş yaradacaq - onların vəziyyəti aktiv və ya uzaqda olmasından asılı olmayaraq.", + "Uptime Kuma URL": "Uptime Kuma URL-i", + "setup a new monitor group": "yeni monitor qrupu yarat", + "openModalTo": "{0} üçün pəncərəni aç", + "Add a domain": "Domen əlavə et", + "Remove domain": "'{0}' domenini sil", + "Icon Emoji": "İkon emojisi", + "signalImportant": "VACİB: alıcılarda qrupları və nömrələri qarışdıra bilməzsiniz!", + "signalUseTemplate": "Fərdi mesaj şablonu işlət", + "signalUseTemplateDescription": "Aktivləşdirilsə, mesaj fərdi şablonla göndəriləcək. Bildiriş formatını fərdiləşdirmək üçün Liquid şablonlarından istifadə edə bilərsiniz.", + "aboutWebhooks": "Webhook-lar haqqında ətraflı: {0}", + "aboutJiraCloudId": "Jira Cloud ID haqqında ətraflı: {0}", + "see Jira Cloud Docs": "Jira Cloud sənədlərinə bax", + "Jira Service Management": "Jira Service Management", + "aboutSlackUsername": "Mesajı göndərənin görünən adını dəyişir. Kimisə qeyd etmək istəyirsinizsə, onu görünən ada yox, mətnə əlavə edin.", + "slackIncludeGroupName": "Monitor qrupunun adını daxil et", + "slackIncludeGroupNameDescription": "Aktivləşdirilsə, bildirişlərə monitor qrupunun yolu daxil ediləcək ki, müxtəlif qruplardakı eyni adlı monitorları ayırd etmək asan olsun.", + "slackUseTemplate": "Fərdi mesaj şablonu işlət", + "slackUseTemplateDescription": "Aktivləşdirilsə, mesaj fərdi şablonla göndəriləcək. Monitor qrupu məlumatını monitorJSON.path və ya monitorJSON.pathName vasitəsilə daxil etmək üçün Liquid şablonlarından istifadə edə bilərsiniz.", + "aboutChannelName": "Webhook kanalını keçmək istəyirsinizsə, kanalın adını {0} Kanalın adı sahəsinə yazın. Məsələn: #other-channel", + "aboutKumaURL": "Uptime Kuma URL-i sahəsini boş buraxsanız, standart olaraq layihənin GitHub səhifəsi işlədiləcək.", + "smtpAdditionalHeadersTitle": "Əlavə başlıqlar", + "smtpAdditionalHeadersDesc": "İstifadə üçün Nodemailer Custom Headers {0} sənədinə baxın.", + "smtpDkimSettings": "DKIM parametrləri", + "smtpDkimDesc": "İstifadə üçün Nodemailer DKIM {0} sənədinə baxın.", + "documentation": "sənədlər", + "smtpDkimDomain": "Domen adı", + "smtpDkimKeySelector": "Açar seçicisi", + "smtpDkimPrivateKey": "Gizli açar", + "smtpDkimHashAlgo": "Heş alqoritmi (könüllü)", + "smtpDkimheaderFieldNames": "İmzalanacaq başlıq açarları (könüllü)", + "smtpDkimskipFields": "İmzalanmayacaq başlıq açarları (könüllü)", + "wayToGetPagerDutyKey": "Bunu Service -> Service Directory -> (xidmət seçin) -> Integrations -> Add integration yolu ilə ala bilərsiniz. Burada \"Events API V2\" axtara bilərsiniz. Ətraflı: {0}", + "Integration Key": "İnteqrasiya açarı", + "Integration URL": "İnteqrasiya URL-i", + "Auto resolve or acknowledged": "Avtomatik həll et və ya təsdiqlə", + "do nothing": "heç nə etmə", + "auto acknowledged": "avtomatik təsdiqlə", + "auto resolve": "avtomatik həll et", + "alertaApiEndpoint": "API ünvanı", + "alertaEnvironment": "Mühit", + "alertaApiKey": "API açarı", + "alertaAlertState": "Xəbərdarlıq vəziyyəti", + "alertaRecoverState": "Bərpa vəziyyəti", + "serwersmsAPIUser": "API istifadəçi adı (webapi_ önlüyü ilə)", + "serwersmsAPIPassword": "API parolu", + "serwersmsPhoneNumber": "Telefon nömrəsi", + "serwersmsRecipientType": "Alıcının növü", + "serwersmsRecipientTypePhone": "Telefon nömrəsi", + "serwersmsRecipientTypeGroup": "Qrup", + "serwersmsGroupId": "Qrup ID-si", + "serwersmsGroupIdHelptext": "Müştəri panelindəki ID və ya qrup ID-ləri. Bu identifikatorları action groups / index vasitəsilə endirmək və ya Müştəri panelində redaktə edilən qrupdan kopyalamaq olar.", + "serwersmsSenderName": "SMS göndərənin adı (müştəri portalı vasitəsilə qeydiyyatdan keçirilib)", + "smseagleTo": "Telefon nömrə(lər)i", + "smseagleGroup": "Telefon kitabçasındakı qrup ad(lar)ı", + "smseagleContact": "Telefon kitabçasındakı kontakt ad(lar)ı", + "smseagleGroupV2": "Telefon kitabçasındakı qrup ID-ləri", + "smseagleContactV2": "Telefon kitabçasındakı kontakt ID-ləri", + "smseagleRecipientType": "Alıcının növü", + "smseagleRecipient": "Alıcı(lar) (bir neçəsi vergüllə ayrılmalıdır)", + "smseagleToken": "API giriş tokeni", + "smseagleUrl": "SMSEagle cihazınızın URL-i", + "smseagleEncoding": "Unicode kimi göndər (standart=GSM-7)", + "smseaglePriority": "Mesajın prioriteti (0-9, ən yüksək prioritet = 9)", + "smseagleMsgType": "Mesajın növü", + "smseagleMsgSms": "SMS mesajı (standart)", + "smseagleMsgRing": "Zəng", + "smseagleMsgTts": "Mətndən nitqə zəng", + "smseagleMsgTtsAdvanced": "Qabaqcıl mətndən nitqə zəng", + "smseagleDuration": "Müddət (saniyə ilə)", + "smseagleTtsModel": "Mətndən nitqə modelinin ID-si", + "smseagleApiType": "API versiyası", + "smseagleApiv1": "APIv1 (mövcud layihələr və geriyə uyğunluq üçün)", + "smseagleApiv2": "APIv2 (yeni inteqrasiyalar üçün tövsiyə olunur)", + "smseagleDocs": "Sənədləri və ya APIv2-nin əlçatanlığını yoxlayın: {0}", + "smseagleComma": "Bir neçəsi vergüllə ayrılmalıdır", + "smspartnerApiurl": "API açarınızı idarə panelinizdə burada tapa bilərsiniz: {0}", + "smspartnerPhoneNumber": "Telefon nömrə(lər)i", + "smspartnerPhoneNumberHelptext": "Nömrə beynəlxalq formatda olmalıdır: {0}, {1}. Bir neçə nömrə {2} ilə ayrılmalıdır", + "smspartnerSenderName": "SMS göndərənin adı", + "smspartnerSenderNameInfo": "3-11 adi simvol arasında olmalıdır", + "Recipient Number": "Alıcının nömrəsi", + "Access Key": "Giriş açarı", + "ooredooUsernameDescription": "Ooredoo toplu SMS panelinə daxil olmaq üçün işlətdiyiniz e-poçt ünvanı.", + "ooredooAccessKeyDescription": "Ooredoo SMS panelinizdə \"API Access Key\" altında yerləşir. Göründüyü kimi yapışdırın - göndərməzdən əvvəl avtomatik base64 ilə kodlaşdırılır.", + "ooredooBearerTokenDescription": "Ooredoo SMS panelinizdəki nümunə cURL əmrində \"Authorization: Bearer\" ifadəsindən sonra göstərilən token.", + "ooredooRecipientDescription": "Bir və ya bir neçə nömrəni vergül və ya boşluqla ayıraraq yazın. Yerli 7 rəqəmli nömrələrin əvvəlinə avtomatik 960 əlavə olunur.", + "Leave blank to use the default Ooredoo endpoint.": "Standart Ooredoo ünvanını işlətmək üçün boş buraxın.", + "From Name/Number": "Göndərənin adı/nömrəsi", + "Leave blank to use a shared sender number.": "Paylaşılan göndərən nömrəsini işlətmək üçün boş buraxın.", + "Octopush API Version": "Octopush API versiyası", + "octopushEndpoint": "octopush (ünvan: {url})", + "legacyOctopushEndpoint": "Köhnə Octopush-DM (ünvan: {url})", + "ntfy Topic": "ntfy mövzusu", + "Server URL should not contain the nfty topic": "Server URL-ində ntfy mövzusu olmamalıdır", + "onebotHttpAddress": "OneBot HTTP ünvanı", + "onebotMessageType": "OneBot mesaj növü", + "onebotGroupMessage": "Qrup", + "onebotPrivateMessage": "Şəxsi", + "onebotUserOrGroupId": "Qrup/İstifadəçi ID-si", + "onebotSafetyTips": "Təhlükəsizlik üçün giriş tokeni təyin edilməlidir", + "PushDeer Server": "PushDeer serveri", + "pushDeerServerDescription": "Rəsmi serveri işlətmək üçün boş buraxın", + "PushDeer Key": "PushDeer açarı", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) müstəqil WxPusher tətbiqi vasitəsilə 6 platformada çatdırır - Android, iOS, HarmonyOS, Windows, macOS və Linux ({download}). SPT-nizi almaq üçün aşağıdakı QR kodu skan edin və göndərməyə hazırsınız. Bir neçə SPT-ni vergüllə (,) ayırın - ətraflı üçün {docs} baxın.", + "wxPusherDownloadApp": "tətbiqi endir", + "wxPusherDocs": "rəsmi təlimat", + "SpugPush Template Code": "Şablon kodu", + "wayToGetClickSendSMSToken": "API istifadəçi adını və API açarını buradan ala bilərsiniz: {here}.", + "wayToGetEgoSMSToken": "EgoSMS API istifadəçi adınızı və parolunuzu buradan ala bilərsiniz: {here}.", + "egosmsSenderDescription": "Alıcılara görünən göndərən ID-si (maksimum 11 simvol). Boş olarsa, standart olaraq EGOSMS işlədilir.", + "egosmsPhoneNumberDescription": "Alıcının nömrəsi ölkə kodu ilə, + və ya 00 olmadan (məsələn, 2567XXXXXXXX).", + "Hash Key": "Heş açarı", + "wayToGetBearSMSToken": "BearSMS API istifadəçi adınızı və heş açarınızı buradan ala bilərsiniz: {0}.", + "bearsmsPhoneNumberDescription": "Alıcının nömrəsi ölkə kodu ilə, + və ya 00 olmadan (məsələn, 9725XXXXXXXX).", + "bearsmsSenderDescription": "Alıcılara görünən könüllü göndərən ID-si (maksimum 11 simvol). O, BearSMS hesabınızda təsdiqlənməlidir, əks halda hesabın standart göndərəni işlədilir.", + "Custom Monitor Type": "Fərdi monitor növü", + "Google Analytics ID": "Google Analytics ID", + "Analytics Type": "Analitika növü", + "Analytics ID": "Analitika ID-si", + "Analytics Script URL": "Analitika skriptinin URL-i", + "Edit Tag": "Teqi redaktə et", + "WebSocket Options": "WebSocket seçimləri", + "Server Address": "Serverin ünvanı", + "Learn More": "Ətraflı öyrən", + "Body Encoding": "Gövdənin kodlaşdırması", + "API Keys": "API açarları", + "Expiry": "Bitmə", + "Expiry date": "Bitmə tarixi", + "Don't expire": "Bitməsin", + "Continue": "Davam et", + "Add Another": "Daha bir əlavə et", + "Key Added": "Açar əlavə edildi", + "apiKeyAddedMsg": "API açarınız əlavə edildi. Onu bir yerə yazın, çünki bir daha göstərilməyəcək.", + "Add API Key": "API açarı əlavə et", + "No API Keys": "API açarı yoxdur", + "apiKey-active": "Aktiv", + "apiKey-expired": "Vaxtı bitib", + "apiKey-inactive": "Qeyri-aktiv", + "Expires": "Bitir", + "disableAPIKeyMsg": "Bu API açarını söndürmək istədiyinizə əminsiniz?", + "deleteAPIKeyMsg": "Bu API açarını silmək istədiyinizə əminsiniz?", + "Generate": "Yarat", + "pagertreeIntegrationUrl": "İnteqrasiya URL-i", + "pagertreeUrgency": "Təcililik", + "pagertreeSilent": "Səssiz", + "pagertreeLow": "Aşağı", + "pagertreeMedium": "Orta", + "pagertreeHigh": "Yüksək", + "pagertreeCritical": "Kritik", + "pagertreeResolve": "Avtomatik həll et", + "pagertreeDoNothing": "Heç nə etmə", + "wayToGetPagerTreeIntegrationURL": "PagerTree-də Uptime Kuma inteqrasiyasını yaratdıqdan sonra Endpoint dəyərini kopyalayın. Tam təfərrüat: {0}", + "lunaseaTarget": "Hədəf", + "lunaseaDeviceID": "Cihaz ID-si", + "lunaseaUserID": "İstifadəçi ID-si", + "ntfyAuthenticationMethod": "Kimlik doğrulama üsulu", + "ntfyPriorityHelptextAllEvents": "Bütün hadisələr maksimum prioritetlə göndərilir", + "ntfyPriorityHelptextAllExceptDown": "Bütün hadisələr bu prioritetlə göndərilir, {0} hadisələri istisna olmaqla - onların prioriteti {1}-dir", + "ntfyPriorityHelptextPriorityHigherThanDown": "Adi prioritet {0} prioritetindən yüksək olmalıdır. {1} prioriteti {0} prioritetindən ({2}) yüksəkdir", + "ntfyPriorityDown": "İŞLƏMİR hadisələri üçün prioritet", + "ntfyUsernameAndPassword": "İstifadəçi adı və parol", + "ntfyCall": "Telefon zəngi", + "ntfyCallHelptext": "Xəbərdarlıq işə düşəndə telefon zəngi et. Birinci təsdiqlənmiş nömrənizi işlətmək üçün 'yes' qoyun və ya konkret telefon nömrəsi yazın (məsələn, +12223334444). ntfy Pro və təsdiqlənmiş telefon nömrəsi tələb olunur.", + "ntfyUseTemplate": "Bildiriş şablonlarını fərdiləşdir", + "ntfyUseTemplateDescription": "Bildiriş başlıqlarını və mətnlərini LiquidJS şablonları ilə fərdiləşdirmək üçün bunu aktivləşdirin", + "ntfyCustomTitle": "Fərdi başlıq şablonu", + "ntfyCustomMessage": "Fərdi mesaj şablonu", + "ntfyNotificationTemplateFallback": "Standart Uptime Kuma formatını işlətmək üçün boş buraxın", + "Device ID or Group ID": "Cihaz ID-si və ya qrup ID-si", + "notifyAppDeviceIdDescription": "Notify! tətbiqindəki cihaz ID-si və ya qrupdakı bütün cihazları eyni anda xəbərdar etmək üçün qrup ID-si.", + "notifyAppSetup": "Notify! tətbiqini açın və cihaz ID-nizi və tokeninizi Settings bölməsində tapın. Hesab tələb olunmur. Ətraflı: {0}.", + "telnyxApiKey": "API açarı (V2)", + "telnyxApiKeyHelptext": "Telnyx portalındakı API Keys bölməsindən Telnyx V2 API açarınız", + "telnyxPhoneNumber": "Göndərən nömrə", + "telnyxPhoneNumberHelptext": "Göndərmək üçün Telnyx telefon nömrəniz (Telnyx hesabınızda qeydiyyatdan keçmiş olmalıdır, məsələn +15551234567)", + "telnyxToNumber": "Alıcı nömrə", + "telnyxToNumberHelptext": "SMS bildirişlərini alacaq hədəf telefon nömrəsi (məsələn, +15559876543)", + "telnyxMessagingProfileId": "Messaging Profile ID (könüllü)", + "telnyxMessagingProfileIdHelptext": "Göndərən parametrlərini və imkanları idarə etmək üçün istəyə görə Telnyx Messaging Profile təyin edin", + "plivoAuthID": "Auth ID", + "plivoAuthIDHelptext": "Plivo konsolundan Plivo Auth ID-niz", + "plivoAuthToken": "Auth tokeni", + "plivoAuthTokenHelptext": "Plivo konsolundan Plivo Auth tokeniniz", + "plivoFromNumber": "Göndərən nömrə", + "plivoFromNumberHelptext": "Göndərmək üçün Plivo telefon nömrəsi (məsələn, +15551234567). SMS üçün təsdiqlənmiş hərf-rəqəmli göndərən ID-si də işlədilə bilər; səsli zənglər üçün Plivo telefon nömrəsi lazımdır.", + "plivoToNumber": "Alıcı nömrə", + "plivoToNumberHelptext": "SMS və ya səsli zəngi qəbul edən hədəf telefon nömrəsi (məsələn, +15559876543)", + "plivoMessageType": "Mesajın növü", + "plivoVoiceCall": "Səsli zəng", + "plivoAnswerUrl": "Cavab URL-i", + "plivoAnswerUrlHelptext": "Zəng üçün Plivo XML qaytaran URL. Xəbərdarlıq mətni ?message= sorğu parametri kimi əlavə olunur, ona görə ünvanınız məsələn {message} qaytara bilər.", + "twilioAccountSID": "Account SID", + "twilioApiKey": "API açarı (könüllü)", + "twilioApiKeyHelptext": "API açarı könüllüdür, amma tövsiyə olunur. Ya Twilio konsolu səhifəsindən Account SID və AuthToken, ya da Account SID ilə birlikdə API açarı və API açarının gizli hissəsini verə bilərsiniz", + "twilioMessagingServiceSID": "Messaging Service SID (könüllü)", + "twilloMessagingServiceSIDHelptext": "Göndərənləri və imkanları idarə etmək üçün {twillo_messaging_service_help_link} işlədirsinizsə, Messaging Service SID-nizi bura yazın", + "twilioAuthToken": "Auth tokeni / API açarının gizli hissəsi", + "twilioFromNumber": "Göndərən nömrə", + "twilioToNumber": "Alıcı nömrə", + "Monitor Setting": "{0} monitorunun parametrləri", + "Show Clickable Link": "Kliklənə bilən linki göstər", + "Show Clickable Link Description": "İşarələnsə, bu vəziyyət səhifəsinə girişi olan hər kəs monitorun URL-inə də giriş əldə edəcək.", + "Open Badge Link Generator": "Nişan linki generatorunu aç", + "Badge Link Generator": "{0} üçün nişan linki generatoru", + "Badge Link Generator Helptext": "Nişan linkləri açıq vəziyyət səhifələrinə təyin edilmiş bütün monitorlar üçün əlçatandır. Ətraflı məlumat üçün {documentation} baxın.", + "Badge Type": "Nişanın növü", + "Badge Duration (in hours)": "Nişanın müddəti (saatla)", + "Badge Label": "Nişanın etiketi", + "Badge Prefix": "Nişan dəyərinin önlüyü", + "Badge Suffix": "Nişan dəyərinin sonluğu", + "Badge Label Color": "Nişan etiketinin rəngi", + "Badge Color": "Nişanın rəngi", + "Badge Label Prefix": "Nişan etiketinin önlüyü", + "Badge Preview": "Nişanın önizləməsi", + "Badge Label Suffix": "Nişan etiketinin sonluğu", + "Badge Up Color": "İşləyir vəziyyətinin nişan rəngi", + "Badge Down Color": "İşləmir vəziyyətinin nişan rəngi", + "Badge Pending Color": "Gözləyir vəziyyətinin nişan rəngi", + "Badge Maintenance Color": "Texniki xidmət vəziyyətinin nişan rəngi", + "Badge Warn Color": "Xəbərdarlıq nişanının rəngi", + "Badge Warn Days": "Xəbərdarlıq nişanının günləri", + "Badge Down Days": "İşləmir nişanının günləri", + "Badge Style": "Nişanın üslubu", + "Badge value (For Testing only.)": "Nişanın dəyəri (yalnız sınaq üçün.)", + "Badge URL": "Nişanın URL-i", + "Group": "Qrup", + "Monitor Group": "Monitor qrupu", + "monitorToastMessagesLabel": "Monitor bildiriş pəncərələri", + "monitorToastMessagesDescription": "Monitorların bildiriş pəncərələri verilmiş saniyədən sonra yox olur. -1 qoysanız vaxt limiti söndürülür. 0 qoysanız bildiriş pəncərələri söndürülür.", + "toastErrorTimeout": "Xəta bildirişlərinin vaxt limiti", + "toastSuccessTimeout": "Uğur bildirişlərinin vaxt limiti", + "Kafka Brokers": "Kafka brokerləri", + "Enter the list of brokers": "Brokerlərin siyahısını yazın", + "Press Enter to add broker": "Broker əlavə etmək üçün Enter basın", + "Kafka Topic Name": "Kafka mövzusunun adı", + "Kafka Producer Message": "Kafka producer mesajı", + "Enable Kafka SSL": "Kafka SSL-i aktivləşdir", + "Enable Kafka Producer Auto Topic Creation": "Kafka producer üçün mövzunun avtomatik yaradılmasını aktivləşdir", + "Kafka SASL Options": "Kafka SASL seçimləri", + "Mechanism": "Mexanizm", + "Pick a SASL Mechanism...": "SASL mexanizmini seçin…", + "Authorization Identity": "Avtorizasiya kimliyi", + "AccessKey Id": "AccessKey Id", + "Secret AccessKey": "Secret AccessKey", + "Session Token": "Sessiya tokeni", + "noGroupMonitorMsg": "Əlçatan deyil. Əvvəlcə qrup monitoru yaradın.", + "Close": "Bağla", + "Request Body": "Sorğunun gövdəsi", + "HTTP Method": "HTTP metodu", + "webhookPostMethodDesc": "POST əksər müasir HTTP serverləri üçün uyğundur.", + "webhookGetMethodDesc": "GET məlumatı sorğu parametrləri kimi göndərir və gövdə tənzimləməyə imkan vermir. Uptime Kuma Push monitorlarını işə salmaq üçün faydalıdır.", + "wayToGetFlashDutyKey": "Uptime Kuma-nı Flashduty ilə birləşdirmək üçün: Channels > kanal seçin > Integrations > Add a new integration yolu ilə gedin, Uptime Kuma seçin və Push URL-i kopyalayın.", + "FlashDuty Severity": "Ciddilik", + "FlashDuty Push URL": "Push URL-i", + "FlashDuty Push URL Placeholder": "Xəbərdarlıq inteqrasiyası səhifəsindən kopyalayın", + "Flowtriq Webhook URL": "Webhook URL-i", + "Flowtriq API Key": "API açarı", + "flowtriqApiKeyDescription": "Flowtriq nüsxənizə göndərilən webhook sorğularının kimliyini doğrulamaq üçün könüllü API açarı.", + "Pinglet Topic URL": "Mövzu URL-i", + "pingletTopicUrlDescription": "Xəbərdarlıqların göndəriləcəyi Pinglet mövzusunun dərc URL-i, məsələn https://app.pinglet.co.uk/your-namespace/alerts. Mövzu hələ yoxdursa, ilk dərcdə yaradılır.", + "nostrRelays": "Nostr relayları", + "nostrRelaysHelp": "Hər sətirdə bir relay URL-i", + "nostrSender": "Göndərənin gizli açarı (nsec)", + "nostrRecipients": "Alıcıların açıq açarları (npub)", + "nostrRecipientsHelp": "npub formatında, hər sətirdə bir dənə", + "showCertificateExpiry": "Sertifikatın bitməsini göstər", + "showOnlyLastHeartbeat": "Yalnız son yoxlamanı göstər", + "noOrBadCertificate": "Sertifikat yoxdur/qüsurludur", + "cacheBusterParam": "{0} parametrini əlavə et", + "cacheBusterParamDescription": "Keşi keçmək üçün təsadüfi yaradılan parametr.", + "gamedigToken": "Token", + "gamedigTokenDescription": "Könüllü. Bəzi oyun serverləri kimlik doğrulama tokeni tələb edir.", + "gamedigGuessPort": "Gamedig: portu təxmin et", + "gamedigGuessPortDescription": "Valve Server Query protokolunun işlətdiyi port müştəri portundan fərqli ola bilər. Monitor serverinizə qoşula bilmirsə, bunu sınayın.", + "Message format": "Mesajın formatı", + "Send rich messages": "Zəngin mesajlar göndər", + "Bitrix24 Webhook URL": "Bitrix24 webhook URL-i", + "wayToGetBitrix24Webhook": "Webhook-u {0} ünvanındakı addımları izləyərək yarada bilərsiniz", + "bitrix24SupportUserID": "Bitrix24-dəki istifadəçi ID-nizi yazın. ID-ni istifadəçinin profilinə keçib linkdən öyrənə bilərsiniz.", + "ClickUp API Token": "ClickUp API tokeni", + "ClickUp API Token Description": "ClickUp-da şəxsi giriş tokenini Settings -> Integrations & ClickApps -> ClickUp API -> API Token yolu ilə yarada bilərsiniz", + "ClickUp Workspace ID": "ClickUp iş sahəsinin ID-si", + "ClickUp Workspace ID Description": "Onu iş sahənizin URL-ində, app.clickup.com/ hissəsindən dərhal sonra tapa bilərsiniz, məsələn app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ClickUp kanalının ID-si", + "ClickUp Channel ID Description": "Onu söhbət kanalının URL-ində, /chat/r/ hissəsindən dərhal sonra tapa bilərsiniz, məsələn .../chat/r/abc123-4567.", + "Saved.": "Yadda saxlanıldı.", + "authUserInactiveOrDeleted": "İstifadəçi qeyri-aktivdir və ya silinib.", + "authInvalidToken": "Token etibarsızdır.", + "authIncorrectCreds": "İstifadəçi adı və ya parol yanlışdır.", + "2faAlreadyEnabled": "2FA artıq aktivdir.", + "2faEnabled": "2FA aktivləşdirildi.", + "2faDisabled": "2FA söndürüldü.", + "successAdded": "Uğurla əlavə edildi.", + "successResumed": "Uğurla davam etdirildi.", + "successPaused": "Uğurla dayandırıldı.", + "successDeleted": "Uğurla silindi.", + "successEdited": "Uğurla redaktə edildi.", + "successAuthChangePassword": "Parol uğurla yeniləndi.", + "successBackupRestored": "Ehtiyat nüsxə uğurla bərpa edildi.", + "successDisabled": "Uğurla söndürüldü.", + "successEnabled": "Uğurla aktivləşdirildi.", + "tagNotFound": "Teq tapılmadı.", + "foundChromiumVersion": "Chromium/Chrome tapıldı. Versiya: {0}", + "Remote Browsers": "Uzaq brauzerlər", + "Remote Browser": "Uzaq brauzer", + "Add a Remote Browser": "Uzaq brauzer əlavə et", + "Remote Browser not found!": "Uzaq brauzer tapılmadı!", + "remoteBrowsersDescription": "Uzaq brauzerlər Chromium-u lokal işlətməyin alternatividir. browserless.io kimi xidmətlə qurun və ya özünüzünkinə qoşulun", + "self-hosted container": "öz serverinizdəki konteyner", + "remoteBrowserToggle": "Standart olaraq Chromium Uptime Kuma konteynerinin içində işləyir. Bu açarı çevirməklə uzaq brauzer işlədə bilərsiniz.", + "useRemoteBrowser": "Uzaq brauzer işlət", + "deleteRemoteBrowserMessage": "Bu uzaq brauzeri bütün monitorlar üçün silmək istədiyinizə əminsiniz?", + "Screenshot Delay": "Ekran görüntüsü gecikməsi ({milliseconds} gözləyir)", + "milliseconds": "{n} millisaniyə | {n} millisaniyə", + "screenshotDelayDescription": "İstəyə görə ekran görüntüsü almazdan əvvəl bu qədər millisaniyə gözləyin. Maksimum: {maxValueMs} ms (aralığın 0,5 misli).", + "screenshotDelayWarning": "Daha böyük dəyərlər brauzeri daha uzun açıq saxlayır, bu isə çoxlu eyni anda işləyən monitorda yaddaş istifadəsini artıra bilər.", + "GrafanaOncallUrl": "Grafana Oncall URL-i", + "systemService": "Sistem xidməti", + "systemServiceName": "Xidmətin adı", + "systemServiceDescription": "{service_name} sistem xidmətinin aktiv olub-olmadığını yoxlayır", + "systemServiceDescriptionLinux": "{service_name} Linux systemd xidmətinin aktiv olub-olmadığını yoxlayır", + "systemServiceDescriptionWindows": "{service_name} Windows Service Manager xidmətinin işləyib-işləmədiyini yoxlayır", + "systemServiceCommandHint": "İşlədilən əmr: {command}", + "systemServiceExpectedOutput": "Gözlənilən nəticə: \"{0}\"", + "pm2Description": "{service_name} PM2 prosesinin onlayn olub-olmadığını yoxlayır", + "pm2ExpectedStates": "Gözlənilən vəziyyət: onlayn. stopped və errored vəziyyətləri İŞLƏMİR kimi qəbul edilir.", + "Browser Screenshot": "Brauzerin ekran görüntüsü", + "Command": "Əmr", + "mongodbCommandDescription": "Verilənlər bazasına qarşı MongoDB əmri işlədin. Əlçatan əmrlər haqqında məlumat üçün {documentation} baxın", + "wayToGetSevenIOApiKey": "app.seven.io > developer > api key > yaşıl əlavə etmə düyməsi yolu ilə idarə panelinə keçin", + "senderSevenIO": "Göndərən nömrə və ya ad", + "receiverSevenIO": "Alıcı nömrə", + "receiverInfoSevenIO": "Alıcı nömrə Almaniyada deyilsə, nömrənin əvvəlinə ölkə kodunu əlavə etməlisiniz (məsələn, ABŞ-ın 1 ölkə kodu üçün 017612121212 əvəzinə 117612121212 yazın)", + "apiKeySevenIO": "SevenIO API açarı", + "wayToWriteWhapiRecipient": "Beynəlxalq önlüklə, amma əvvəlində plyus işarəsi olmadan telefon nömrəsi ({0}), kontakt ID-si ({1}) və ya qrup ID-si ({2}).", + "wayToGetWhapiUrlAndToken": "API URL-ini və tokeni {0} ünvanından istədiyiniz kanala keçməklə ala bilərsiniz", + "whapiRecipient": "Telefon nömrəsi / kontakt ID-si / qrup ID-si", + "API URL": "API URL-i", + "wayToWriteEvolutionRecipient": "Beynəlxalq önlüklə, amma əvvəlində plyus işarəsi olmadan telefon nömrəsi ({0}), kontakt ID-si ({1}) və ya qrup ID-si ({2}).", + "wayToGetEvolutionUrlAndToken": "API URL-ini və tokeni {0} ünvanından istədiyiniz kanala keçməklə ala bilərsiniz", + "evolutionRecipient": "Telefon nömrəsi / kontakt ID-si / qrup ID-si", + "evolutionInstanceName": "Nüsxənin adı", + "evolutionCustomMessageTitle": "Fərdi mesaj (könüllü)", + "evolutionCustomMessageDesc": "Standart mesajı işlətmək üçün boş buraxın.", + "What is a Remote Browser?": "Uzaq brauzer nədir?", + "wayToGetHeiiOnCallDetails": "Trigger ID və API açarlarının necə alındığı {documentation} bölməsində izah olunub", + "documentationOf": "{0} sənədləri", + "callMeBotGet": "Burada {0}, {1} və {2} üçün ünvan yarada bilərsiniz. Nəzərə alın ki, sorğu limitinə düşə bilərsiniz. Limitlər belədir: {3}", + "gtxMessagingApiKeyHint": "API açarınızı burada tapa bilərsiniz: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Göndərən telefon nömrəsi / ötürmə yolunun mənbə ünvanı (TPOA)", + "gtxMessagingFromHint": "Mobil telefonlarda alıcılarınız TPOA-nı mesajın göndərəni kimi görür. 11-ə qədər hərf-rəqəmli simvol, qısa kod, yerli uzun kod və ya beynəlxalq nömrələr ({e164}, {e212} və ya {e214}) qəbul edilir", + "To Phone Number": "Alıcı telefon nömrəsi", + "gtxMessagingToHint": "Beynəlxalq format, əvvəlində \"+\" ilə ({e164}, {e212} və ya {e214})", + "Originator type": "Göndərənin növü", + "Alphanumeric (recommended)": "Hərf-rəqəmli (tövsiyə olunur)", + "Telephone number": "Telefon nömrəsi", + "cellsyntOriginatortypeAlphanumeric": "Hərf-rəqəmli sətir (maksimum 11 simvol). Alıcılar mesaja cavab verə bilməzlər.", + "cellsyntOriginatortypeNumeric": "Rəqəmli dəyər (maksimum 15 rəqəm), telefon nömrəsi beynəlxalq formatda, əvvəlində 00 olmadan (məsələn, Britaniya nömrəsi 07920 110 000 belə yazılmalıdır: 447920110000). Alıcılar mesaja cavab verə bilər.", + "Originator": "Göndərən", + "cellsyntOriginator": "Alıcının mobil telefonunda mesajın göndərəni kimi görünür. İcazə verilən dəyərlər və funksiya originatortype parametrindən asılıdır.", + "Destination": "Hədəf", + "cellsyntDestination": "Alıcının telefon nömrəsi beynəlxalq formatda, əvvəlində 00 və sonra ölkə kodu ilə, məsələn Britaniya nömrəsi 07920 110 000 üçün 00447920110000 (ümumilikdə maksimum 17 rəqəm). Hər HTTP sorğusunda vergüllə ayrılmış maksimum 25000 alıcı.", + "Allow Long SMS": "Uzun SMS-ə icazə ver", + "cellsyntSplitLongMessages": "Uzun mesajları 6-ya qədər hissəyə böl. 153 x 6 = 918 simvol.", + "max 15 digits": "maksimum 15 rəqəm", + "max 11 alphanumeric characters": "maksimum 11 hərf-rəqəmli simvol", + "Community String": "Community sətri", + "snmpCommunityStringHelptext": "Bu sətir SNMP dəstəkli cihazlara girişi doğrulamaq və idarə etmək üçün parol kimi işləyir. Onu SNMP cihazınızın konfiqurasiyası ilə uyğunlaşdırın.", + "OID (Object Identifier)": "OID (obyekt identifikatoru)", + "snmpOIDHelptext": "İzləmək istədiyiniz sensor və ya vəziyyət üçün OID yazın. OID barədə əmin deyilsinizsə, MIB brauzerləri və ya SNMP proqramları kimi şəbəkə idarəetmə alətlərindən istifadə edin.", + "snmpV3Username": "SNMPv3 istifadəçi adı", + "Condition": "Şərt", + "SNMP Version": "SNMP versiyası", + "Please enter a valid OID.": "Düzgün OID daxil edin.", + "wayToGetThreemaGateway": "Threema Gateway üçün {0} ünvanında qeydiyyatdan keçə bilərsiniz.", + "threemaRecipient": "Alıcı", + "threemaRecipientType": "Alıcının növü", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypeIdentityFormat": "8 simvol", + "threemaRecipientTypePhone": "Telefon nömrəsi", + "threemaRecipientTypePhoneFormat": "E.164, əvvəlində + olmadan", + "threemaRecipientTypeEmail": "E-poçt ünvanı", + "threemaSenderIdentity": "Gateway-ID", + "threemaSenderIdentityFormat": "8 simvol, adətən * ilə başlayır", + "threemaApiAuthenticationSecret": "Gateway-ID gizli açarı", + "threemaBasicModeInfo": "Qeyd: bu inteqrasiya Threema Gateway-i əsas rejimdə (server tərəfli şifrələmə) işlədir. Ətraflı təfərrüatı {0} ünvanında tapa bilərsiniz.", + "apiKeysDisabledMsg": "Kimlik doğrulaması söndürüldüyü üçün API açarları da söndürülüb.", + "Host Onesender": "Onesender hostu", + "Token Onesender": "Onesender tokeni", + "Recipient Type": "Alıcının növü", + "Private Number": "Şəxsi nömrə", + "privateOnesenderDesc": "Telefon nömrəsinin düzgün olduğuna əmin olun. Şəxsi nömrəyə mesaj göndərmək üçün, məsələn: 628123456789", + "groupOnesenderDesc": "GroupID-nin düzgün olduğuna əmin olun. Qrupa mesaj göndərmək üçün, məsələn: 628123456789-342345", + "Group ID": "Qrup ID-si", + "wayToGetOnesenderUrlandToken": "URL-i və tokeni Onesender saytına keçməklə ala bilərsiniz. Ətraflı: {0}", + "Add Remote Browser": "Uzaq brauzer əlavə et", + "New Group": "Yeni qrup", + "Group Name": "Qrupun adı", + "OAuth2: Client Credentials": "OAuth2: müştəri məlumatları", + "Authentication Method": "Kimlik doğrulama üsulu", + "Authorization Header": "Avtorizasiya başlığı", + "Form Data Body": "Forma məlumatı gövdəsi", + "OAuth Token URL": "OAuth token URL-i", + "Client ID": "Client ID", + "Client Secret": "Client Secret", + "OAuth Scope": "OAuth əhatəsi", + "OAuth Audience": "OAuth auditoriyası", + "Optional: The audience to request the JWT for": "Könüllü: JWT-nin hansı auditoriya üçün istəniləcəyi", + "Optional: Space separated list of scopes": "Könüllü: boşluqla ayrılmış əhatə siyahısı", + "Go back to home page.": "Ana səhifəyə qayıdın.", + "No tags found.": "Teq tapılmadı.", + "Lost connection to the socket server.": "Soket serveri ilə bağlantı kəsildi.", + "Cannot connect to the socket server.": "Soket serverinə qoşulmaq mümkün olmadı.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 webhook URL-i", + "signl4Docs": "SIGNL4-ün necə tənzimlənəcəyi və SIGNL4 webhook URL-inin necə alınacağı barədə ətraflı məlumatı {0} bölməsində tapa bilərsiniz.", + "Conditions": "Şərtlər", + "conditionAdd": "Şərt əlavə et", + "conditionDelete": "Şərti sil", + "conditionAddGroup": "Qrup əlavə et", + "conditionDeleteGroup": "Qrupu sil", + "conditionValuePlaceholder": "Dəyər", + "equals": "bərabərdir", + "not equals": "bərabər deyil", + "contains": "tərkibində var", + "not contains": "tərkibində yoxdur", + "starts with": "bununla başlayır", + "not starts with": "bununla başlamır", + "ends with": "bununla bitir", + "not ends with": "bununla bitmir", + "less than": "kiçikdir", + "greater than": "böyükdür", + "less than or equal to": "kiçik və ya bərabərdir", + "greater than or equal to": "böyük və ya bərabərdir", + "record": "qeyd", + "message": "mesaj", + "json_value": "JSON dəyəri", + "Notification Channel": "Bildiriş kanalı", + "Sound": "Səs", + "Alphanumerical string and hyphens only": "Yalnız hərf-rəqəmli sətir və tire", + "Arcade": "Arkada", + "Correct": "Düzgün", + "Fail": "Uğursuz", + "Harp": "Arfa", + "Reveal": "Açılış", + "Bubble": "Qabarcıq", + "Doorbell": "Qapı zəngi", + "Flute": "Fleyta", + "Money": "Pul", + "Scifi": "Elmi-fantastik", + "Clear": "Təmizlə", + "Select All": "Hamısını seç", + "Deselect All": "Seçimi ləğv et", + "Elevator": "Lift", + "Guitar": "Gitara", + "Pop": "Pop", + "Custom sound to override default notification sound": "Standart bildiriş səsini əvəz edən fərdi səs", + "Time Sensitive (iOS Only)": "Vaxta həssas (yalnız iOS)", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Vaxta həssas bildirişlər cihaz narahat etməyin rejimində olsa belə dərhal çatdırılacaq.", + "From": "Göndərən", + "Can be found on:": "Burada tapıla bilər: {0}", + "The phone number of the recipient in E.164 format.": "Alıcının telefon nömrəsi E.164 formatında.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Ya mətn göndərən ID-si, ya da cavab ala bilmək istəyirsinizsə E.164 formatında telefon nömrəsi.", + "RabbitMQ Nodes": "RabbitMQ idarəetmə node-ları", + "Enter the list of nodes": "RabbitMQ idarəetmə node-larının siyahısını yazın", + "Press Enter to add node": "Node əlavə etmək üçün Enter basın", + "rabbitmqNodesDescription": "RabbitMQ idarəetmə node-larının URL-ini protokol və portla birlikdə yazın. Məsələn: {0}", + "rabbitmqNodesRequired": "Bu monitor üçün node-ları təyin edin.", + "rabbitmqNodesInvalid": "RabbitMQ node-ları üçün tam URL işlədin ('http' ilə başlayan).", + "RabbitMQ Username": "RabbitMQ istifadəçi adı", + "RabbitMQ Password": "RabbitMQ parolu", + "rabbitmqHelpText": "Bu monitoru işlətmək üçün RabbitMQ quraşdırmanızda Management Plugin-i aktivləşdirməlisiniz. Ətraflı məlumat üçün {rabitmq_documentation} baxın.", + "SendGrid API Key": "SendGrid API açarı", + "turbosmtpConsumerKey": "Consumer Key", + "turbosmtpConsumerSecret": "Consumer Secret", + "Separate multiple email addresses with commas": "Bir neçə e-poçt ünvanını vergüllə ayırın", + "brevoApiKey": "Brevo API açarı", + "brevoApiHelp": "API açarını burada yaradın: {0}", + "brevoFromEmail": "Göndərənin e-poçtu", + "brevoFromName": "Göndərənin adı", + "brevoLeaveBlankForDefaultName": "standart ad üçün boş buraxın", + "brevoToEmail": "Alıcının e-poçtu", + "brevoCcEmail": "CC e-poçtu", + "brevoBccEmail": "BCC e-poçtu", + "brevoSeparateMultipleEmails": "Bir neçə e-poçt ünvanını vergüllə ayırın", + "brevoSubject": "Mövzu", + "brevoLeaveBlankForDefaultSubject": "standart mövzu üçün boş buraxın", + "resendApiKey": "Resend API açarı", + "resendApiHelp": "API açarını burada yaradın: {0}", + "resendFromName": "Göndərənin adı", + "resendFromEmail": "Göndərənin e-poçtu", + "resendLeaveBlankForDefaultName": "standart ad üçün boş buraxın", + "resendLeaveBlankForDefaultSubject": "Standart mövzu üçün boş buraxın", + "resendToEmail": "Alıcının e-poçtu", + "resendSubject": "Mövzu", + "pingCountLabel": "Maksimum paket", + "pingCountDescription": "Dayanmazdan əvvəl göndəriləcək paketlərin sayı", + "pingNumericLabel": "Rəqəmli nəticə", + "pingNumericDescription": "İşarələnsə, simvolik host adları əvəzinə IP ünvanları göstəriləcək", + "pingGlobalTimeoutLabel": "Ümumi gözləmə müddəti", + "pingGlobalTimeoutDescription": "Göndərilən paketlərdən asılı olmayaraq ping dayanana qədər ümumi vaxt (saniyə ilə)", + "pingPerRequestTimeoutLabel": "Hər ping üçün gözləmə müddəti", + "pingPerRequestTimeoutDescription": "Bu, tək bir ping paketini itmiş saymazdan əvvəl maksimum gözləmə müddətidir (saniyə ilə)", + "pingIntervalAdjustedInfo": "Aralıq paket sayına, ümumi gözləmə müddətinə və hər ping üçün gözləmə müddətinə görə tənzimlənib", + "smtpHelpText": "'SMTPS' SMTP/TLS-in işlədiyini yoxlayır; 'TLS-i nəzərə alma' şifrələnməmiş bağlantı qurur; 'STARTTLS' qoşulur, STARTTLS əmrini verir və serverin sertifikatını yoxlayır. Bunların heç biri e-poçt göndərmir.", + "Custom URL": "Fərdi URL", + "customUrlDescription": "Monitorun URL-i əvəzinə kliklənə bilən URL kimi işlədiləcək.", + "OneChatAccessToken": "OneChat giriş tokeni", + "OneChatUserIdOrGroupId": "OneChat istifadəçi ID-si və ya qrup ID-si", + "OneChatBotId": "OneChat bot ID-si", + "wahaSession": "Sessiya", + "wahaChatId": "Söhbət ID-si (telefon nömrəsi / kontakt ID-si / qrup ID-si)", + "wayToGetWahaApiUrl": "WAHA nüsxənizin URL-i.", + "wayToGetWahaApiKey": "API açarı WAHA-nı işə salarkən işlətdiyiniz WHATSAPP_API_KEY mühit dəyişəninin dəyəridir.", + "wayToGetWahaSession": "WAHA bildirişləri bu sessiyadan söhbət ID-sinə göndərir. Onu WAHA panelində tapa bilərsiniz.", + "wayToWriteWahaChatId": "Beynəlxalq önlüklə, amma əvvəlində plyus işarəsi olmadan telefon nömrəsi ({0}), kontakt ID-si ({1}) və ya qrup ID-si ({2}). Bildirişlər WAHA sessiyasından bu söhbət ID-sinə göndərilir.", + "openwaSession": "Sessiya", + "openwaChatId": "Söhbət ID-si (telefon nömrəsi / kontakt ID-si / qrup ID-si)", + "wayToGetOpenwaApiUrl": "OpenWA nüsxənizin URL-i.", + "wayToGetOpenwaApiKey": "OpenWA nüsxəniz üçün işlədilən API açarı.", + "wayToGetOpenwaSession": "OpenWA-nın bildiriş göndərmək üçün işlətdiyi sessiya ID-si.", + "wayToWriteOpenwaChatId": "Beynəlxalq önlüklə, amma əvvəlində plyus işarəsi olmadan telefon nömrəsi ({0}), kontakt ID-si ({1}) və ya qrup ID-si ({2}). Vergüllə ayırmaqla bir neçə söhbət ID-sinə göndərə bilərsiniz.", + "openwaCustomMessageTitle": "Fərdi mesaj (könüllü)", + "openwaCustomMessageDesc": "Standart mesajı işlətmək üçün boş buraxın.", + "360messengerAuthToken": "360messenger API açarı", + "360messengerRecipient": "Alıcının telefon nömrə(lər)i", + "360messengerGroupId": "360messenger qrup ID-si", + "360messengerUseTemplate": "Fərdi mesaj şablonu işlət", + "360messengerTemplate": "360messenger mesaj şablonu", + "360messengerGroupList": "WhatsApp qrupları", + "360messengerSelectGroupList": "Əlavə etmək üçün qrup seçin", + "360messengerSelectedGroupID": "Seçilmiş qrup ID-ləri", + "360messengerEnableSendToGroup": "WhatsApp qruplarına göndərməni aktivləşdir", + "360messengerCustomMessageTemplate": "Fərdi mesaj şablonu", + "360messengerEnableCustomMessage": "Standart mesaj əvəzinə fərdi mesaj şablonunu aktivləşdir.", + "360messengerMessageTemplate": "Mesaj şablonu", + "360messengerWayToGetUrlAndToken": "360messenger API açarınızı buradan ala bilərsiniz: {0}.", + "360messengerWayToWriteRecipient": "Bir və ya bir neçə telefon nömrəsini beynəlxalq formatda, əvvəlində plyus olmadan yazın (məsələn, {0}). Bir neçə nömrəni vergüllə ayırın.", + "360messengerErrorNoApiKey": "Əvvəlcə 360messenger API açarınızı yazın.", + "360messengerErrorNoGroups": "Bu hesab üçün WhatsApp qrupu tapılmadı.", + "360messengerErrorApi": "WhatsApp qrup siyahısını yükləmək mümkün olmadı (Xəta {statusCode}: {message}).", + "360messengerErrorGeneric": "WhatsApp qrup siyahısını yükləmək mümkün olmadı: {message}", + "YZJ Webhook URL": "YZJ webhook URL-i", + "YZJ Robot Token": "YZJ robot tokeni", + "Plain Text": "Adi mətn", + "Message Template": "Mesaj şablonu", + "Template Format": "Şablonun formatı", + "Font Twemoji by Twitter licensed under": "Twitter-in Twemoji şrifti, lisenziya", + "smsplanetApiToken": "SMSPlanet API-si üçün token", + "smsplanetApiDocs": "API tokenlərinin necə alındığı barədə ətraflı məlumatı {the_smsplanet_documentation} bölməsində tapa bilərsiniz.", + "the smsplanet documentation": "smsplanet sənədləri", + "Phone numbers": "Telefon nömrələri", + "Sender name": "Göndərənin adı", + "smsplanetNeedToApproveName": "Müştəri panelində təsdiqlənməlidir", + "Disable URL in Notification": "Bildirişdə URL-i söndür", + "Suppress Notifications": "Bildirişləri sıxışdır", + "discordSuppressNotificationsHelptext": "Aktivləşdirilsə, mesajlar kanala göndəriləcək, amma alıcılarda push və ya masaüstü bildirişi yaratmayacaq.", + "discordMessageFormat": "Mesajın formatı", + "discordMessageFormatNormal": "Normal (zəngin qoşmalar)", + "discordMessageFormatMinimalist": "Minimalist (qısa vəziyyət)", + "discordMessageFormatCustom": "Fərdi şablon", + "discordUseMessageTemplate": "Fərdi mesaj şablonu işlət", + "discordUseMessageTemplateDescription": "Aktivləşdirilsə, mesaj fərdi şablonla (LiquidJS) göndəriləcək. Standart Uptime Kuma formatını işlətmək üçün boş buraxın.", + "discordMessageTemplate": "Mesaj şablonu", + "fluxerMessageFormat": "Mesajın formatı", + "fluxerMessageFormatNormal": "Normal (zəngin qoşmalar)", + "fluxerMessageFormatMinimalist": "Minimalist (qısa vəziyyət)", + "fluxerMessageFormatCustom": "Fərdi şablon", + "fluxerUseMessageTemplate": "Fərdi mesaj şablonu işlət", + "fluxerUseMessageTemplateDescription": "Aktivləşdirilsə, mesaj fərdi şablonla (LiquidJS) göndəriləcək. Standart Uptime Kuma formatını işlətmək üçün boş buraxın.", + "fluxerMessageTemplate": "Mesaj şablonu", + "Fluxer Webhook URL": "Fluxer webhook URL-i", + "wayToGetFluxerURL": "Bunu hədəf kanalın parametrləri > Webhooks > Create Webhook > Copy Webhook URL yolu ilə ala bilərsiniz.", + "Ip Family": "IP ailəsi", + "ipFamilyDescriptionAutoSelect": "IP ailəsini müəyyən etmək üçün {happyEyeballs} işlədilir.", + "Happy Eyeballs algorithm": "Happy Eyeballs alqoritmi", + "Add Another Tag": "Daha bir teq əlavə et", + "Staged Tags for Batch Add": "Toplu əlavə üçün hazırlanmış teqlər", + "Clear Form": "Formanı təmizlə", + "pause": "Dayandır", + "Manual": "Əl ilə", + "Nextcloud host": "Nextcloud hostu", + "Conversation token": "Söhbət tokeni", + "Bot secret": "Botun gizli açarı", + "Send UP silently": "İŞLƏYİR bildirişini səssiz göndər", + "Send DOWN silently": "İŞLƏMİR bildirişini səssiz göndər", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Nextcloud Talk botunu quraşdırmaq üçün serverə administrator girişi lazımdır.", + "Globalping - Access global monitoring probes": "Globalping - qlobal izləmə zondlarına giriş", + "GlobalpingMonitorDescription": "Globalping şəbəkə testləri və ölçmələri aparmaq üçün icma tərəfindən saxlanılan minlərlə zonda giriş verir. Bütün anonim istifadəçilər üçün saatda 250 test limiti qoyulub. Limiti saatda 500-ə qaldırmaq üçün tokeninizi {accountSettings} bölməsində saxlayın. Ətraflı məlumat üçün {docs} baxın.", + "Globalping API Token": "Globalping API tokeni", + "globalpingApiTokenDescription": "Globalping API tokeninizi buradan alın: {0}.", + "GlobalpingHostname": "Açıq şəkildə əlçatan ölçmə hədəfi. Adətən host adı və ya IPv4/IPv6 ünvanı - ölçmənin növündən asılı olaraq.", + "GlobalpingLocationDescription": "Yer sahəsi qitələri, ölkələri, regionları, şəhərləri, ASN-ləri, provayderləri və ya bulud regionlarını qəbul edir. Süzgəcləri {plus} ilə birləşdirə bilərsiniz (məsələn, {amazonPlusGermany} və ya {comcastPlusCalifornia}). Gecikmə vacib göstəricidirsə, sıçrayışların qarşısını almaq üçün süzgəclərlə yeri kiçik regiona daraldın və daha yaxşı sabitlik üçün {datacenter} süzgəcini təyin edin. {fullDocs}.", + "GlobalpingLocationDocs": "Yer sahəsi üzrə tam sənədlər", + "GlobalpingMultipleLocationsError": "Bir neçə yer dəstəklənmir, hər monitor üçün tək yer işlədin.", + "GlobalpingIpFamilyInfo": "İşlədiləcək IP versiyası. Yalnız hədəf host adı olduqda icazə verilir.", + "GlobalpingResolverInfo": "IPv4/IPv6 ünvanı və ya tam domen adı (FQDN). Standart olaraq zondun lokal şəbəkə həlledicisi işlədilir. Həlledici serveri istənilən vaxt dəyişə bilərsiniz.", + "RecordMatch": "Qeydin dəyəri ilə uyğunluq", + "RegexMatch": "Qeydin dəyəri ilə uyğunlaşacaq requlyar ifadə yazın", + "Resolver Server": "Həlledici server", + "Protocol": "Protokol", + "account settings": "hesab parametrləri", + "Location": "Yer", + "Monitor Subtype": "Monitorun alt növü", + "Check for": "Nəyi yoxla", + "Number of retry attempts if webhook fails": "Webhook uğursuz olarsa təkrar cəhdlərin sayı (hər 60-180 saniyədən bir).", + "Maximum Retries": "Maksimum təkrar cəhd", + "Template ID": "Şablon ID-si", + "wayToGetClickSMSIRTemplateID": "Şablonunuzda {uptkumaalert} sahəsi olmalıdır. Yeni şablonu {here} yarada bilərsiniz.", + "Recipient Numbers": "Alıcı nömrələri", + "Notifications Enabled": "Bildirişlər aktivdir", + "Allow Notifications": "Bildirişlərə icazə ver", + "Browser not supported": "Brauzer dəstəklənmir", + "Unable to get permission to notify": "Bildiriş icazəsi alınmadı (sorğu rədd edildi və ya nəzərə alınmadı).", + "Webpush Helptext": "Veb push yalnız SSL (HTTPS) bağlantıları ilə işləyir. iOS cihazlarında səhifə əvvəlcədən ana ekrana əlavə edilməlidir.", + "settingsDomainExpiry": "Domenin bitməsi", + "labelDomainExpiry": "Domen bitmə tarixi.", + "labelDomainNameExpiryNotification": "Domen adının bitməsi bildirişi", + "domainExpiryNotificationHelp": "Neçə gün əvvəldən xəbərdarlıq ediləcəyi Parametrlərdə tənzimlənə bilər.", + "domainExpiryDescription": "Domen adlarının bitməsinə bu qədər qalanda bildiriş göndər:", + "domain_expiry_unsupported_monitor_type": "Bu monitor növü üçün domenin bitməsinin izlənilməsi dəstəklənmir", + "domain_expiry_unsupported_missing_target": "Bu monitor üçün düzgün domen və ya host adı təyin edilməyib", + "domain_expiry_unsupported_is_icann": "\"{domain}\" domeni domenin bitməsinin izlənilməsi üçün uyğun deyil, çünki onun açıq sonluğu \".{publicSuffix}\" ICANN tərəfindən idarə olunmur", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "\".{publicSuffix}\" üçün domenin bitməsinin izlənilməsi əlçatan deyil, çünki IANA heç bir RDAP xidməti göstərmir", + "minimumIntervalWarning": "20 saniyədən qısa aralıqlar məhsuldarlığı aşağı sala bilər.", + "lowIntervalWarning": "Aralıq dəyərini 20 saniyədən aşağı qoymaq istədiyinizə əminsiniz? Xüsusən çoxlu monitor varsa, məhsuldarlıq aşağı düşə bilər.", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "Halo PSA webhook URL-i", + "halopsa_webhook_url_desc": "Halo PSA Integration Runbook-unuzdan webhook URL-ini yazın (Configuration > Integrations > Custom Integrations > Integration Runbooks). Webhook yaradarkən 'Can only be started from Halo and from a public endpoint' seçin.", + "username": "İstifadəçi adı", + "password": "Parol", + "halopsa_username_desc": "Halo PSA webhook-u ilə kimlik doğrulaması üçün istifadəçi adı", + "halopsa_password_desc": "Halo PSA webhook-u ilə kimlik doğrulaması üçün parol", + "imageResetConfirmation": "Şəkil standarta qaytarıldı", + "screenshot of the website": "Saytın ekran görüntüsü", + "Basic checkbox toggle button group": "Sadə xanalı açar düymələr qrupu", + "Basic radio toggle button group": "Sadə radio açar düymələr qrupu", + "mtls-auth-server-cert-label": "Sertifikat", + "mtls-auth-server-cert-placeholder": "Sertifikatın gövdəsi", + "mtls-auth-server-key-label": "Açar", + "mtls-auth-server-key-placeholder": "Açarın gövdəsi", + "mtls-auth-server-ca-label": "CA", + "mtls-auth-server-ca-placeholder": "Serverin CA-sı", + "avgPing": "Orta ping", + "Uptime Kuma": "Uptime Kuma", + "maxPing": "Maksimum ping", + "minPing": "Minimum ping", + "Setup Instructions": "Quraşdırma göstərişləri", + "halopsa_setup_step1": "HaloPSA-da Integration Runbook yaradın (Configuration → Integrations → Integration Runbooks)", + "halopsa_setup_step2": "Xəbərdarlıqları emal etmək üçün runbook əməliyyatlarını tənzimləyin (məsələn, Create Ticket)", + "halopsa_setup_step3": "Webhook URL-ini kopyalayıb yuxarıdakı mətn sahəsinə yapışdırın", + "halopsa_setup_step4": "Basic kimlik doğrulamasını seçin, istifadəçi adı və parol yaradın. Sonra həmin istifadəçi adını və parolu yuxarıdakı sahələrə yazın və ya yapışdırın", + "Clear current filters": "Cari süzgəcləri təmizlə", + "Sort options": "Sıralama seçimləri", + "Sort by status": "Vəziyyətə görə sırala", + "Sort by name": "Ada görə sırala", + "Sort by uptime": "Əlçatanlığa görə sırala", + "Sort by certificate expiry": "Sertifikatın bitməsinə görə sırala", + "Splunk Rest URL": "Splunk Rest URL-i", + "Severity": "Ciddilik", + "Message Format": "Mesajın formatı", + "smscTranslit": "smscTranslit", + "Region": "Region", + "PushDeer Server URL": "PushDeer server URL-i", + "To Number": "Alıcı nömrə", + "GrafanaOncallURL": "Grafana Oncall URL-i", + "Never": "Heç vaxt", + "Json Query": "JSON sorğusu", + "PM2 Process": "PM2 prosesi", + "Refresh": "Yenilə", + "Select PM2 process": "PM2 prosesini seçin", + "SSL/TLS": "SSL/TLS", + "playground": "sınaq meydanı", + "Check Type": "Yoxlamanın növü", + "Service Name": "Xidmətin adı", + "GRPC Options": "GRPC seçimləri", + "Metadata": "Metaməlumat", + "End": "Son", + "Show this Maintenance Message on which Status Pages": "Bu texniki xidmət mesajı hansı vəziyyət səhifələrində göstərilsin", + "Endpoint": "Ünvan", + "Details": "Təfərrüatlar", + "passwordTooWeak": "Parol çox zəifdir. O, hərf və rəqəmlərdən ibarət olmalıdır və ən azı 6 simvol uzunluğunda olmalıdır.", + "TLS Alerts": "TLS xəbərdarlıqları", + "Expected TLS Alert": "Gözlənilən TLS xəbərdarlığı", + "None (Successful Connection)": "Yoxdur (uğurlu bağlantı)", + "expectedTlsAlertDescription": "Serverin qaytaracağını gözlədiyiniz TLS xəbərdarlığını seçin. mTLS ünvanlarının müştəri sertifikatı olmadan bağlantıları rədd etdiyini yoxlamaq üçün {code} işlədin. Təfərrüat üçün {link} baxın.", + "TLS Alert Spec": "RFC 8446", + "mariadbSocketPathDetectedHelptext": "Verilənlər bazasına {0} mühit dəyişəni ilə göstərildiyi kimi qoşulur.", + "Expand All Groups": "Bütün qrupları aç", + "Collapse All Groups": "Bütün qrupları yığ", + "Webhook Payload Fields": "Webhook məlumat sahələri", + "halopsa_payload_desc": "Halo PSA webhook-unuza aşağıdakı sahələr göndərilir:", + "halopsa_field_title": "Xəbərdarlığın başlığı (həmişə 'Uptime Kuma Alert')", + "halopsa_field_status": "Monitorun vəziyyəti: UP, DOWN, NOTIFICATION və ya UNKNOWN", + "halopsa_field_monitor": "Monitorun adı", + "halopsa_field_monitor_id": "Monitorun unikal identifikatoru (sınaq bildirişləri üçün null) - xəbərdarlıqları biletlərlə uyğunlaşdırmaq üçün bunu işlədin", + "halopsa_field_message": "Vəziyyət və təfərrüatlarla birlikdə tam xəbərdarlıq mətni", + "halopsa_field_timestamp": "Hadisənin vaxt möhürü ISO 8601 formatında", + "halopsa_field_uptime_kuma_version": "Uptime Kuma versiyasının nömrəsi", + "halopsa_id_usage_hint": "💡 Məsləhət: xəbərdarlıqları biletlərlə etibarlı uyğunlaşdırmaq üçün monitor_id, hadisə tarixçəsini izləmək üçün heartbeat_id işlədin", + "halopsa_setup_step5": "Runbook-u xəbərdarlıqları mövcud biletlərlə uyğunlaşdırmaq üçün monitor_id işlədəcək şəkildə tənzimləyin", + "Teltonika SMS Gateway": "Teltonika SMS şlüzü", + "teltonikaVersionWarning": "Bu bildiriş provayderi Teltonika cihazınızın RMS 7.14.0 və ya daha yeni versiyasında işləməsini tələb edir.", + "teltonikaUrl": "Teltonika cihazınızın URL-i", + "teltonikaUrlHelptext": "URL tam mənbə kimi göstərilməlidir, məsələn {0} və ya {1}.", + "teltonikaUnsafeTls": "Sertifikat yoxlamasını nəzərə alma", + "teltonikaUnsafeTlsDescription": "TLS sertifikatının yoxlanmasını söndürmək sizi yol üstü (man-in-the-middle) hücumlarına açır, bu isə məlumat sızmasına və sistemin ələ keçirilməsinə gətirib çıxara bilər. Bu hücum yolunu qəbul etmirsinizsə, sertifikat yoxlamasını söndürməyin. Avtomatik yenilənmə ilə LetsEncrypt işlətməyi tövsiyə edirik.", + "teltonikaUsername": "API istifadəçi adı", + "teltonikaUsernameHelptext": "Tövsiyə: yalnız SMS göndərməklə məhdudlaşan ayrıca hesab yaradın və onun istifadəçi adını bura yazın", + "teltonikaPassword": "API parolu", + "teltonikaPasswordHelptext": "API istifadəçisinin parolunu Teltonika routerinizdə təyin edə bilərsiniz, məsələn {0}", + "teltonikaModem": "Modemin id-si", + "teltonikaModemHelptext": "SMS modeminin id-si bu formatda olmalıdır: {0}. Göstərişlər üçün https://developers.teltonika-networks.com/reference/ ünvanına baxın.", + "teltonikaPhoneNumber": "Telefon nömrəsi", + "teltonikaPhoneNumberHelptext": "Nömrə beynəlxalq formatda olmalıdır: {0}, {1}. Yalnız bir nömrəyə icazə verilir.", + "ntpThresholdsTitle": "NTP hədləri", + "ntpStratumThreshold": "Stratum həddi", + "ntpStratumThresholdHelp": "Stratum bu dəyərdə və ya ondan yuxarıdırsa, İŞLƏMİR kimi işarələ. Stratum vaxt mənbəyinin keyfiyyətini göstərir (1=GPS/atom saatı, 16=sinxronlaşdırılmayıb).", + "ntpOffsetThreshold": "Vaxt sapması həddi (ms)", + "ntpOffsetThresholdHelp": "Vaxt sapması bu dəyəri millisaniyə ilə keçirsə, işləmir kimi işarələ.", + "ntpDispersionThreshold": "Kök dispersiyası həddi (ms)", + "ntpDispersionThresholdHelp": "Kök dispersiyası bu dəyəri keçirsə, işləmir kimi işarələ. Dispersiya serverin saat dəqiqliyini göstərir.", + "VKTeams Bot Token Description": "Bot tokeninin və digər məlumatların necə alındığını buradan öyrənə bilərsiniz: {0}.", + "VKTeams Base Url Description": "VKTeams baza API URL-i, On-Premise həllində fərqli ola bilər. Standart: https://myteam.mail.ru", + "VKTeams Chat Id Description": "İstifadəçilər üçün bu, onların e-poçt ünvanıdır. Qruplar və kanallar üçün ID-ni parametrlərdən götürüb sonuna {'@'}chat.agent əlavə edin", + "VKTeams Use Template": "Fərdi mesaj şablonu işlət", + "VKTeams Use Template Description": "Aktivləşdirilsə, mesaj fərdi şablonla göndəriləcək.", + "VKTeams Template Format Description": "Mesajın üslublandırılması üçün VKTeams adi mətn, MarkdownV2 və HTML formatlaşdırmasını dəstəkləyir.", + "milkyHttpAddress": "Milky HTTP ünvanı ('/api' sonluğu olmadan)", + "milkyMessageType": "Milky mesaj növü", + "milkyGroupMessage": "Qrup", + "milkyPrivateMessage": "Şəxsi", + "milkyUserOrGroupId": "Qrup/İstifadəçi ID-si", + "milkySafetyTips": "Təhlükəsizlik üçün giriş tokeni təyin edilməlidir", + "smsgatewayUrlHelptext": "SMS şlüzünüzün baza URL-i, API yolu olmadan.", + "smsgatewayTo": "Telefon nömrə(lər)i", + "smsgatewayToHelptext": "Alıcı telefon nömrələrinin vergüllə ayrılmış siyahısı beynəlxalq formatda (məsələn, +15551234567)" +} diff --git a/src/lang/bar.json b/src/lang/bar.json new file mode 100644 index 0000000..efb84a5 --- /dev/null +++ b/src/lang/bar.json @@ -0,0 +1,1493 @@ +{ + "Language": "Sprach", + "Home": "Dahoam", + "setupDatabaseChooseDatabase": "Wos für a Datenbank wuist du?", + "setupDatabaseEmbeddedMariaDB": "Da brauchst nix mocha. Des Docker Buidl hod a Datenbank dabai und nimmt MariaDB. Uptime Kuma verbint se mit Unix Socket.", + "Import": "Eine spuin", + "respTime": "Antwort-Zeit (ms)", + "Create your admin account": "Mach Dein Admin-Zugang", + "matrixDesc2": "Es wead dringend empfohln, dass Du an neia Benutza a'legst und ned de Zugangs-Schlüssl (Access Token) vo Deim eigana Matrix-Benutza vawendst, wei de do den voiständign Zuagriff auf Dei Konto und olle Räum, wo Du dabei bist, zualassn dadn.Stattdessn: Leg an neia Benutza a' und lads eam nua in den Raum ei, in dem wo Du de Benachrichtigunga griang wuist.Du kriagst den Zugangs-Schlüssl (Access Token) iatz, indemst des ausfiahrst: {0}", + "jsonQueryDescription": "Nutz a JSON-Abfrag (JSON query), um spezifische Datn aus da JSON-Antwort vom Server auszulesn und z'extrahiern. Oder: Wenn Du koa JSON-Antwort erwarts, nimm \"$\" fia de rohe Antwort (raw response).Des Ergebnis wead dann mit'm erwartetn Wert (expected value) oiss wia a Text (as strings) vaglichn. Fia d'Dokumentation schaugst bittschee bei {0} noch, und zum Testn und Experimentiern is {1} da richtige Blotz.", + "receiverInfoSevenIO": "Wenn de Numma vom Empfänger ned in Deitschland (Germany) is, muaßt den Lända-Code (country code) davo setzn. Beispui: Fia d'Lända-Kennzoi 1 vo de USA (US) nimmst 117612121212 statt 017612121212", + "trustProxyDescription": "Vertrau de 'X-Forwarded-*' Header. Wenn Du de korrekte Client IP kriagn wuixt und Dei Uptime Kuma hinter am Proxy (wia Nginx oda Apache) lafft, dann muaßt Du des eischaltn.", + "wayToGetFlashDutyKey": "Um Uptime Kuma mit Flashduty z'integrieran: Geh auf Kanäl (Channels) > An Kanal auswähln > Integrationa (Integrations) > A neie Integration hizuafeagn (Add a new integration), wähld Uptime Kuma, und kopierst de Push URL.", + "shrinkDatabaseDescriptionSqlite": "Fia SQLite: Start de Datenbank-Reinigung ({vacuum}). Zwoa is {auto_vacuum} scho eingschoitn, owa des ko de Datenbank ned defragmentiern und aa ned de oanzalnan Datenbank-Seitn nei packa, so wia des da {vacuum}-Befehl macha tuat.", + "matrixDesc1": "Du findst de interne Raum-ID (internal room ID), indemst in Deim Matrix Client in de erweitertn Eistellunga vom Raum (advanced section of the room settings) nochschaust. De soitat wia !QMdRCpUIfLwsfjxye6:home.server ausschaun.", + "lowIntervalWarning": "Bist da sicha, dassd den Intervall-Wert unta 20 Sekundn setzn wuixt? D'Leistung ko schlechter wern, bsondas wenn a Haufa Monitore laffa.", + "rabbitmqHelpText": "Um den Monitor nutzn z'könna, muaßt in Deiner RabbitMQ-Eirichtung des Vawoitungs-Plugin (Management Plugin) aktiviern. Fia mehr Informationa, schau bitte in de {rabitmq_documentation}.", + "promosmsTypeFull": "SMS VOLL - De Premium-Stufn vo SMS. Du kannst Deinen O'senda-Nam vawendn (Du muaßt den Nam eascht registriern). Zuavässig fia Warnunga.", + "disableauth.message2": "Des is fia Szenarien {intendThirdPartyAuth} voa Uptime Kuma denkt, wia Cloudflare Access, Authelia oda andane Authentifizierungs-Mechanisma.", + "webhookGetMethodDesc": "GET schickt Datn ois Abfrage-Parameter (Query Parameters) und erlaubt's ned, an Body z'konfiguriarn. Is nützlich zum Auslös'n vo Uptime Kuma Push Monitore.", + "steamApiKeyDescription": "Zum Iabawachn vo am Steam Spui-Server brauchst an Steam Web-API Schlüssl. Du kannst Deinen API Schlüssl do registriern: ", + "tailscalePingWarning": "Damitst den Tailscale Ping Monitor nutzn kannst, muaßt Du Uptime Kuma ohne Docker installiern und auf Deim Server aa den Tailscale Client installiern.", + "telegramServerUrlDescription": "Um de Begrenzunga vo da Telegram Bot API aufz'hebn oda um Zuagang in blockierte Gengdn (China, Iran, etc.) z'kriang. Fia mehr Informationa klick {0}. Standard: {1}", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "An langlebandn Zuagangs-Schlüssl kannst erstella, indemst auf Deinen Profilnama (unten links) klickst, dann ganz nach untn scrolldst und auf 'Token erstella' klickst.", + "backupOutdatedWarning": "Vaoitet: Wei vui neie Funktiona dazua kemma san und de Backup-Funktion a weng vachlässigt worn is, ko's koa vollständigs Backup erstella oda widaherstella.", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "A Listn vo de Benachrichtigungs-Dienst' findst Du im Home Assistant unta \"Entwickler-Wergzeig' > Dienst'\" (Developer Tools > Services). Suach nach \"notification\", um de Namens- oda Telefonnumma vo Deim Gerät/Handy z'findn.", + "wayToGetBaleChatID": "Du kannst Dei Chat-ID kriang, indemst a Nachricht an den Bot schickst und dann auf dea URL schaust, um de Chat-ID z'sehn:", + "aboutMattermostChannelName": "Du kannst den Standardkanal, in den wo da Webhook postet, iabaschreim, indemst den Kanal-Nama in des Feld \"Kanal-Nama\" (Channel Name) eigibst. Des muaß owa in de Eistellunga vom Mattermost Webhook freigebn (enabled) sei. Beispui: #andana-kanal", + "deleteChildrenMonitors": "A de direkt untagordnatn Monitore und dera Kind-Monitore, wenns welche ham, wead glöscht. | A olle {count} direkt untagordnatn Monitore und dera Kind-Monitore, wenns welche ham, wern glöscht", + "enableDefaultNotificationDescription": "De Benachrichtigung wead standardmäßig fia neie Monitore aktiv sei. Du kannst de Benachrichtigung owa fia jedn Monitor separat deaktivern.", + "importHandleDescription": "Wähld 'Bestehends iabaschpring', wennst jedn Monitor oda jede Benachrichtigung mit'm gleichn Nama iabaschpring wuixt. 'Iabaschreim' löscht jedn scho vorhandna Monitor und jede Benachrichtigung.", + "aboutNotifyChannel": "'Kanal benachrichtign' löst a Desktop- oda Handy-Benachrichtigung ia olle Mitglieda vom Kanal aus, egal ob de grad auf 'aktiv' oda 'weg' gstellt san.", + "aboutSlackUsername": "Ändert den Anzeignama vom Nachrichtn-O'senda. Wennst jemandn erwähna wuixt, pock des stattdessn in den freindlichn Nam (friendly name) nei.", + "aboutChannelName": "Gib den Kanalnam im {0} Kanalnam-Feld ei, wennst den Webhook Kanal umgeh wuixt. Zum Beispui: #anderer-kanal", + "wayToGetPagerDutyKey": "Du kriagst des, indemst auf Service -> Service-Vazeichnis (Service Directory) -> (An Service auswähln) -> Integrationa (Integrations) -> Integration hizuafeagn (Add integration) gähst. Do kannst nach \"Events API V2\" suacha. Mehr Info: {0}", + "apiKeyAddedMsg": "Dein API-Schlüssl is hinzuagfügt worn. Bittschön notierst eam, wei a nimma zoagt wead.", + "monitorToastMessagesDescription": "De Toast-Benachrichtigunga fia Monitore vaschwindn nach da eigebnan Zeit in Sekundn. Setz auf -1, um d'Zeitbeschränkung (Timeout) auszschaltn. Setz auf 0, um de Toast-Benachrichtigunga komplett auszschaltn.", + "gamedigGuessPortDescription": "Da Port, der wo vom Valve Server Query Protokoll gnutzt wead, ko andas sei ois wia da Client-Port. Probier des, wenn da Monitor koane Vabindung zum Server herstella ko.", + "bitrix24SupportUserID": "Gib Dei Benutzernummer in Bitrix24 ei. De ID kannst aus'm Link auselesn, wennst aufs Profil vom Nutzer gehst.", + "remoteBrowserToggle": "Normalawais lafft Chromium im Uptime Kuma Container. Du kannst an Fern-Browser vawendn, indemst den Schalta (Switch) umlegst.", + "callMeBotGet": "Do kannst an Endpunkt (endpoint) fia {0}, {1} und {2} generiern. Owa pass auf, dassd Du gfladert wern kannst (rate limited). De Begrenzunga san: {3}", + "gtxMessagingFromHint": "Auf de Handys (mobile phones) segt da Empfänger de TPOA (Absender-Adress) oiss wia den Schicka vo da Nochricht. Erlaubt san: Maximal 11 alphanumerische Zeichen, a Kurz-Code (shortcode), da lokale Lang-Code (longcode), oda internationale Nummern ({e164}, {e212} oda {e214})", + "cellsyntOriginatortypeNumeric": "Numerischa Wert (maximal 15 Stellan) mit da Telefonnumma im internationaln Format, owa ohne de \"00\" am Ofang (zum Beispui muaß d'UK-Nummer 07920 110 000 auf 447920110000 gsetzt wern). De Empfänger kenna auf de Nochricht antwortn.", + "cellsyntOriginator": "Auf'm Handy vom Empfänger ois da O'senda vo da Nachricht sichtbar. De erlaubtn Wert' und de Funktion hängan vom Parameter 'Originatortyp' (originatortype) ob.", + "snmpOIDHelptext": "Gib de OID (Object Identifier) fia den Sensor oda Status ei, denst iabawachn wuixt. Wennst ned woaßt, wos de OID is, nutz netzwerk-vawoitung-Wergzeig (network management tools) wia MIB-Browser oda SNMP-Software.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Entweda a Text-O'senda-ID oda a Telefonnumma im E.164-Format, wennst Antworta kriang wuixt.", + "smtpHelpText": "'SMTPS' test, ob SMTP iaba TLS (Vaschlüsselung) funktioniern tuat; 'TLS ignoriern' (Ignore TLS) vabindt si iaba an unvaschlüsseltn Text (plaintext); 'STARTTLS' vabindt si, gibst an STARTTLS-Befehl aus und prüft des Server-Zertifikat. Koana vo dene Modi schickt a E-Mail.", + "wayToWriteWahaChatId": "Nimm entweda:D'Telefonnumma mit da internationaln Vuawoi, owa ohne des Plus-Zeichn am Ofang ({0}),d'Kontakt-ID ({1}),oda d'Gruppn-ID ({2}).De Benachrichtigunga wern dann vo da WAHA Session an de Chat ID gschickt.", + "Webpush Helptext": "Web Push funktioniert nur mid SSL (HTTPS) Verbindunga. Fia iOS-Gerät' muaß de Webseitn vorher zum Homescreen zuagfügt wern.", + "Yes": "Basst", + "Username": "Wia Du hoaßt", + "Password": "Dei G'heimnis", + "Remember me": "Eingloggt bleim", + "Login": "Eiloggn", + "No Monitors, please": "Koane Ibachunga fia de Betriebsdauer, biddschee", + "add one": "Oana dazua", + "Notification Type": "Wia da Alarm kimmt", + "Email": "D'E-Mail", + "Test": "Probelauf", + "Certificate Info": "Wos am Zertifikat dro steht", + "Last Result": "Letzts Ergebnis", + "Export": "Aussagebn", + "notAvailableShort": "Koa Angob", + "Default enabled": "Vo Haus aus eigschalt", + "Apply on all existing monitors": "Bei olle bstehndn o'wendn", + "Create": "Mach'n", + "Clear Data": "Ois wida leer macha", + "Events": "Wos gscheng is", + "Heartbeats": "Lebenszeicha", + "Auto Get": "vo aloa nehma", + "Schedule maintenance": "Termin fia d'Wartung macha", + "alertWrongFileType": "Bittschee, such a JSON-Datei aus.", + "Clear all statistics": "Olle Statistiken putzn", + "Skip existing": "Bstehnd's auslassn", + "Overwrite": "Ibaschreim", + "Options": "Eistellunga", + "Keep both": "Boade bhalta", + "Verify Token": "Schlüssl ibaprüfn", + "Setup 2FA": "2FA eistelln", + "Enable 2FA": "2FA aktiviern", + "Disable 2FA": "2FA deaktivern", + "tagNameExists": "A System-Tag mid dem Nam existiert scho. Bittschön wählst eam aus da Listn aus oda nimmst an andern Nam.", + "wayToGetTelegramChatID": "Du kannst Dei Chat-ID kriang, indemst a Nachricht an den Bot schickst und dann auf dea URL schaust, um de Chat-ID z'sehn:", + "chromeExecutableDescription": "Fia de Docker-Nutza: Wenn Chromium no ned installiert is, ko des a boar Minutn dauern, bis' installiert is und des Test-Ergebnis angezeigt wead. Des braucht 1 GB Plotz auf da Festplattn.", + "smtpLiquidIntroduction": "De foigndn zwoa Föida kenna iaba de Liquid Templating Sproch mid Vorlagn bfüllt wern. Schaug da de {0} fia d'Vawendungs-Anweisung'n o. Des san de vafügboarn Variabln:", + "wayToGetDiscordThreadId": "A Thread-/Forumspost-ID kriang is ähnlich wia a Kanal-ID kriang. Schau da mehr iaba de IDs in {0} o", + "whatHappensAtForumPost": "Erstell an neia Forumsbeitrag. Des postet NED Nachrichtn in an existieradn Beitrag. Um in an existieradn Beitrag z'postn, nutz \"{option}\"", + "wayToGetDiscordURL": "Des kannst kriagn, indemst auf Server Einstellunga -> Integrations -> Webhooks oschaung -> Neia Webhook gehst", + "Affected Monitors": "Wos ois btroffa is", + "Pick Affected Monitors...": "Btroffne wehln…", + "Start of maintenance": "O'fang vo da Wartung", + "All Status Pages": "Olle Status-Seitn", + "Select status pages...": "Status-Seitn aussuacha…", + "alertNoFile": "Bittschee, such a Datei zum Eine spuin aus.", + "liquidIntroduction": "Vorlagn-Eistejung wead iaba de Liquid Templating Sproch erreicht. Schau da de {0} fia d'Vawendungs-Anweisung'n o.", + "twilioApiKeyHelptext": "Da API-Schlüssl (API key) is optional, owa ma riat dazua. Du kannst entweda de Account SID und des AuthToken vo da Twilio-Konsoin-Seitn (TwilioConsole) hernehma, oda de Account SID und des Paar aus API-Schlüssl und API-Schlüssl-Geheimnis (Api Key secret)", + "remoteBrowsersDescription": "Fern-Browser (Remote Browsers) san a Alternative dazua, Chromium lokal laufn z'lassn. Du kannst des mid am Dienst wia browserless.io eirichtn oda di mit Deim eigena Browser vabindn", + "wayToWriteWhapiRecipient": "De Telefonnumma mit da internationaln Vuawoi, owa ohne des Plus-Zeichn am Ofang ({0}), de Kontakt-ID ({1}) oda de Gruppn-ID ({2}).", + "gtxMessagingApiKeyHint": "Dein API-Schlüssl findst Du unta: Mei Routing Kontn > Kontn-Informationa zang > API Zugangsdatn > REST API (v2.x)", + "cellsyntDestination": "D'Telefonnummer vom Empfänger muaß im internationaln Format eigebn wern, mit \"00\" am Ofang, gfoigt vo da Lända-Kennzoi. Beispui: Fia d'UK-Nummer 07920 110 000 schreibst 00447920110000 (maximal 17 Stellan insgesamt). Begrenzung: Pro HTTP-Anfrag san maximal 25.000 Empfänger erlaubt, de wo mit am Komma trennt san.", + "snmpCommunityStringHelptext": "Dea Zeichnkettn (String) funktioniert ois Passwort, um den Zuagang zu de SNMP-fähign Gerät' z'bestätign und z'steiern. Schaug, dass des mid Deiner SNMP-Geräte-Konfiguration zammbasst.", + "2FA Settings": "2FA Eistellunga", + "Two Factor Authentication": "Zwoafache Bestätigung", + "wayToWriteEvolutionRecipient": "De Telefonnumma mit da internationaln Vuawoi, owa ohne des Plus-Zeichn am Ofang ({0}), de Kontakt-ID ({1}) oda de Gruppn-ID ({2}).", + "confirmDeleteTagMsg": "Bist da sicher, dass Du den Tag lösch'n wuixt? Monitore, de wo mid dem Tag vabundn san, wean ned g'löscht.", + "goAlertInfo": "GoAlert is a o'ffane Quell-Code-Anwendung (open source application) fia Bereitschafts-Planung, automatisierte Eskalationa und Benachrichtigunga (wia SMS oda telefonische O'ruaf). Automatisch de richtige Person, auf de richtige Weis und zum richtign Zeitpunkt eischaltn! {0}", + "goAlertIntegrationKeyInfo": "Hoi da an generischn API Integrations-Schlüssl fia den Dienst in dem Format \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\". Normalawais is des da Wert vom Token-Parameter in da URL, de wo kopiert worn is.", + "promosmsTypeSpeed": "SMS GAS GEBN - Höchste Priorität im System. Sehr schnell und zuavässig, owa kostspülig (zirka doppelt so teia wia SMS VOLL).", + "enableProxyDescription": "Dea Proxy wirkt se eascht auf de Monitor-Anfrogn aus, wenn er aktiviert is. Du kannst den Proxy fia olle Monitore iaba den Aktivierungs-Status temporär deaktiviern.", + "setAsDefaultProxyDescription": "Dea Proxy wead standardmäßig fia neie Monitore aktiv sei. Du kannst den Proxy owa fia jedn Monitor separat deaktivern.", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "D'aktuelle Vabindung ko valorn geh, wennst grod iaba an Cloudflare Tunnel vabundn bist. Bist da sicha, dass Du des been'dn wuixt? Tipp Dei aktuells Passwort ei, ums z'bestätign.", + "wayToGetLineChannelToken": "Greif z'eascht auf {0} zua, erstöi an Provider und an Kanal (Channel) (Messaging API). Nachad kriagst de Zugangs-Schlüssl (Channel access token) und d'Benutza-ID (User ID) in de obn gnanntn Menü-Punkte.", + "aboutIconURL": "Du kannst a Link zu am Buidl im Feld \"Buidl-URL\" (Icon URL) eigebn, um des Standard-Profilbuidl z'iabaschreim. Des wead owa ned vawendt, wenn a Icon Emoji eigstellt is.", + "Resume": "Weida macha", + "Edit": "Ändern", + "Current": "Iatzad", + "Monitor": "Da Monitor | De Monitor", + "day": "da dog | de dog", + "setupDatabaseSQLite": "A einfachs Datenbank-Datei, empfehlenswert für kloane Installationen. Vorma v2.0.0 hod Uptime Kuma SQLite als Standard-Datenbank nutzt.", + "Settings": "Einstellungen", + "New Update": "Neies Update", + "Appearance": "Aussehng", + "General": "Allgemein", + "General Monitor Type": "Allgemeiner Monitortyp", + "Game": "Spiel", + "Add": "Hinzuafügn", + "Quick Stats": "Kurze Statistik", + "Up": "Beinand", + "statusMaintenance": "Wartung", + "pauseDashboardHome": "Pausn", + "Name": "Nam", + "Status": "Zuastand", + "-day": "-dog", + "hour": "Stund | Stund", + "Ping": "Ping", + "Monitor Type": "De Sorte vom Monitor", + "Keyword": "Schlüsselwort", + "Invert Keyword": "Wosd ned findn wuisd", + "Expected Value": "Wosd davo ausgähst", + "URL": "URL", + "Import Backup": "Backup aufspuin", + "Port": "Port", + "Retries": "Wia oft ma's no probiern", + "resendEveryXTimes": "Wieda schicka olle {0} Mal", + "ignoreTLSError": "Auf d'Vaschlüsselungs-Fehler pfeifn", + "Upside Down Mode": "Umdraht-Modus", + "Max. Redirects": "Max. Wos umleiten deaf", + "No": "Nix da", + "setupDatabaseMariaDB": "Verbinda di mit a externa MariaDB-Datenbank. Du miast de Verbindungsinfos für d’Datenbank oistelln.", + "settingUpDatabaseMSG": "Datenbank wird einrichtet. Des ko a Weile dauern, bitte hob a bisserl Geduld.", + "dbName": "Datenbank-Name", + "Dashboard": "Dashboard", + "Help": "Hilfe", + "Theme": "Design", + "Primary Base URL": "Primäre Basis-URL", + "Version": "Version", + "Check Update On GitHub": "Auf GitHub nach’m Update kucken", + "List": "Liast", + "Add New Monitor": "Neien Monitor hinzufügn", + "Down": "Eiganga", + "Pending": "Grod am Schaugn", + "Maintenance": "Wartung", + "Unknown": "Unbekannt", + "Cannot connect to the socket server": "Ko ned mit’m Socket-Server verbinden", + "Reconnecting...": "Verbinda wieda…", + "Passive Monitor Type": "Passiver Aufbasser", + "Specific Monitor Type": "Besonderer Monitor", + "markdownSupported": "Markdown-Schreibweis geht klar. Wennst HTML hernimmst, lass de Leerzeichn am Anfang weg, sonst schaut des am End ganz schiach aus.", + "Pause": "Pausn", + "DateTime": "Zeit und's Datum", + "Message": "Botschaft", + "No important events": "Koane wichtig'n G'schichtn", + "Delete": "Wegmacha", + "Uptime": "Laufzeit", + "Cert Exp.": "Zertifikat Ablauf", + "-hour": "-stund", + "Response": "Wos zruck kimmt", + "Friendly Name": "Da Nam, wo leicht zum Vasteh is", + "Heartbeat Interval": "'S Interval vom Lebnszeichn", + "Request Timeout": "Max. G'wart", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Gib entweda den Hostnama vom Server ei, mid demst di vabindn wuixt, oda {localhost}, wennst a {local_mta} nutzn wuixt", + "Hostname": "Hostname", + "timeoutAfter": "D'Zeit is rum nach {0} Sekundn", + "Heartbeat Retry Interval": "Wia lang's dauert, bis ma's Lebnszeichn no amoi probiert", + "Resend Notification if Down X times consecutively": "D'Nachricht wieda schicka, wann X-mal glei ausgfalln is", + "Advanced": "De tiefa Sach", + "checkEverySecond": "Noch {0} Sekundn no amoi frogn", + "retryCheckEverySecond": "Noch {0} Sekundn no amoi frogn", + "resendDisabled": "Wieda schicka ausgschoidt", + "retriesDescription": "Max. Vasuch, bevur de Maschin \"Ausgfalln\" hoaßt und's a Nachricht gibt", + "upsideDownModeDescription": "Status umdraha: Is d'Maschin do, is ned do.", + "maxRedirectDescription": "Hächste Anzoi an Umleitunga, dene ma nacha gähd. Bei 0 is's ausgschoidt.", + "Resolver Server": "Auflös-Server", + "Resource Record Type": "Art vom Eitrag", + "Repeat Password": "Passwoat nomoi eigehm", + "Export Backup": "Sicherung aussanehgn", + "languageName": "Deutsch (Bayern)", + "signl4Docs": "Mehr Informationa, wia ma SIGNL4 konfiguriert und wia ma de SIGNL4 Webhook URL findt, gibts in de {0}.", + "Notifications": "Benachrichtigunga", + "Not available, please setup.": "Ned vafügbar, bittschee earichdn.", + "telegramMessageThreadIDDescription": "Optionale eindeutige Kennung fia den Ziel-Thread (Thema) im Forum; bloß fia Forum-Supergruppn", + "promosmsSMSSender": "SMS-Absendernama: Vorab registriata Nama oda oana vo de Standardwerte: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "promosmsPhoneNumber": "Telefonnumma (fia Poin konnst de Vorwoi weglossn)", + "deleteAPIKeyMsg": "Bist da ganz sicher, dass den API-Schlissl leschn wuist?", + "ntfyPriorityHelptextPriorityHigherThanDown": "A normale Priorität suitad höher sei ois de {0} Priorität. Priorität {1} is höher ois de {0} Priorität {2}", + "ntfyPriorityHelptextAllExceptDown": "Olle Ereignisse wern mit dera Priorität verschickt, außer {0}-Ereignisse, de Priorität {1} hom", + "wayToGetOnesenderUrlandToken": "D'URL und den Schlüssel kriagst auf da Onesender-Webseitn. Mehr Infos dazu findst unta {0}", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Zeitkritische Benachrichtigunga wern sofort zuagstait, a wann des Gerät im \"Bittschee ned störn\"-Modus is.", + "pingPerRequestTimeoutDescription": "Des is de maximale Wartezeit (in Sekundn), bevor a oanzlns Ping-Paket ois verloren guit", + "wayToGetWahaSession": "Vo dera Sitzung aus schickt WAHA Benachrichtigunga an d'Chat-ID. De findst im WAHA-Dashboard.", + "Advanced Message Queuing Protocol": "Fortgschriddns Nachrichten-Schlanga-Protokoll (AMQP) 1.0+", + "Broadband Forum User Services Platform": "USP (Breitband-Forum Nutza-Dinst-Plattform)", + "Extensible Messaging and Presence Protocol": "WebSocket-Ibadrog fia's Extensible Messaging and Presence Protocol (XMPP)", + "BACnet Secure Connect Direct Connection": "BACnet Secure Connect Direkt-Vabindung", + "Penguin Statistics Live Protocol v3": "Penguin Statistics Live-Protokoll v3 (Protobuf-Kodierung)", + "Discourage search engines from indexing site": "Suachmaschinan davo obhoidn, dass de Seitn indexiern", + "confirmImportMsg": "Bist da ganz sicher, dass d' d'Sicherung eispuin wuist? Schau liaba nomoi noch, ob d' de richtige Import-Option ausgwählt host.", + "aboutKumaURL": "Wannst des Feld fia de Uptime Kuma URL laar lasst, dann werd automatisch de GitHub-Seitn vom Projekt hergnumma.", + "mongodbCommandDescription": "Führ an MongoDB-Befehl gegn de Datenbank aus. Informationa über de verfügbarn Befehle findst in da {documentation}", + "threemaBasicModeInfo": "Hinweis: De Einbindung nutzt Threema Gateway im \"Basic Mode\" (serverbasierte Verschlüsselung). Mehr Details dazu findst unta {0}.", + "smspartnerPhoneNumberHelptext": "De Nummer muaß im internationaln Format {0}, {1} sei. Mehrere Nummern muaß ma mit {2} trenna", + "webhookAdditionalHeadersDesc": "Legt zusätzliche Header fest, de mit dem Webhook verschickt wern. Jeda Header muaß ois JSON-Key/Value-Paar agem wern.", + "dnsCacheDescription": "Des kannt in manche IPv6-Umgebunga evtl. ned hinhaua. Schoids as aus, wanns Probleme gabat.", + "backupDescription3": "Empfindliche Daten wia Benachrichtigungs-Token san in da Export-Datei drin; bittschee de Datei sicher aufbewahrn.", + "wayToGetKookGuildID": "Schoid den \"Entwickler-Modus\" in de Kook-Einstellunga ei und mach an Rechtsklick auf d'Gilde, damitst de ID kriagst", + "pushoverDesc1": "De Notfall-Priorität (2) hod standardmäßig an Zeitablauf vo 30 Sekundn zwischn de Versuach und lafft nacha oaner Stund aus.", + "privateOnesenderDesc": "Schau noch, ob de Telefonnumma gscheid is. Zum Verschicka an a private Numma, nimm zum Beispui: 628123456789", + "promosmsTypeFlash": "SMS FLASH - De Nachricht werd beim Empfänger automatisch am Gerät ozoagt. Geht bloß fia Empfänger aus Poln.", + "Content Type": "Inhoitstyp", + "templateLimitedToUpDownCertNotifications": "bloß fia AUF/O (UP/DOWN)/Zertifikats-Ablauf-Benachrichtigungen vafügbar", + "HeadersInvalidFormat": "De Kopfzeuin vo da Onfrog san koa gültigs JSON: ", + "invertKeywordDescription": "Schaug liaba, dass des Stichwortl ned do is, ois dass 's do is.", + "octopushSMSSender": "SMS-Absender: 3 bis 11 Zeichn (Buachstobm, Zoin und Leerzeichn)", + "wahaChatId": "Chat-ID (Telefonnumma / Kontakt-ID / Gruppn-ID)", + "issueWithGoogleChatOnAndroidHelptext": "Damit konnst aa Fehlers in da Software weida obm umgeh, so wia bei {issuetackerURL}", + "Add Tags": "Kennzeichnunga dazuafügn", + "color": "Farb", + "ignoreSecWebsocketAcceptHeaderDescription": "Erlaubt am Server, dass a ned mid am Sec-WebSocket-Accept-Header antword, wenn da Websocket-Upgrade hinhaut.", + "Ignore Sec-WebSocket-Accept header": "{0}-Header ignoria", + "wsSubprotocolDescription": "Gib a Listn vo de Unter-Protokolle ei, de mit Kommas trennt is. Wennst mehr dazua wissn wuist, schau bittschön in d' {documentation} nei", + "Session Initiation Protocol": "WebSocket-Ibadrog fia SIP (Session Initiation Protocol)", + "WebSocket Application Messaging Protocol": "WAMP (Des WebSocket-Anwendung-Meldungsprotokoll)", + "Network API for Notification Channel": "OMA RESTful Netzwerk-API fia Benachrichtigungs-Kanäle", + "Web Process Control Protocol": "Web-Prozess-Steiarungs-Protokoll (WPCP)", + "jsflow": "jsFlow pubsub/queue Protokoll", + "Reverse Web Process Control": "Invers' Web-Prozess-Steiarungs-Protokoll (RWPCP)", + "Miele Cloud Connect Protocol": "Miele Cloud-Vabindungs-Protokoll", + "Push Channel Protocol": "Push-Kanal-Protokoll", + "Message Session Relay Protocol": "WebSocket-Ibadrog fia MSRP (Message Session Relay Protocol)", + "Binary Floor Control Protocol": "WebSocket-Ibadrog fia BFCP (Binary Floor Control Protocol)", + "Smart Home IP": "SHIP - Gscheids Dahoam IP", + "Softvelum Low Delay Protocol": "Softvelum Protokoll fia a gringe Vazögarung", + "OPC UA Connection Protocol": "OPC UA Vabindungsprotokoll", + "OPC UA JSON Encoding": "OPC UA JSON Kodierung", + "Swindon Web Server Protocol": "Swindon Web-Server-Protokoll (JSON-Kodierung)", + "Constrained Application Protocol": "Protokoll fia bschränkte Anwendungen (CoAP)", + "Softvelum WebSocket signaling protocol": "Softvelum WebSocket-Signalisierungsprotokoll", + "Cobra Real Time Messaging Protocol": "Cobra Echtzeit-Meldungsprotokoll", + "Declarative Resource Protocol": "Deklarativs Ressourcen-Protokoll", + "BACnet Secure Connect Hub Connection": "BACnet Secure Connect Hub-Vabindung", + "WebSocket Transport for JMAP": "WebSocket-Ibadrog fia JMAP (JSON Meta Application Protocol)", + "ITU-T T.140 Real-Time Text": "ITU-T T.140 Echtzeit-Text", + "Done.best IoT Protocol": "Done.best IoT-Protokoll", + "Collection Update": "Des Websocket-Subprotokoll fia Sammlungs-Aktualisiarunga", + "Text IRC Protocol": "Text-IRC-Protokoll", + "Binary IRC Protocol": "Binärs-IRC-Protokoll", + "Dark": "Dunkla", + "Theme - Heartbeat Bar": "Design - Herzschlag-Balkn", + "styleElapsedTime": "Vaganga Zeit untam Herzschlag-Balkn", + "styleElapsedTimeShowNoLine": "Ozoagn (ohne Linien)", + "styleElapsedTimeShowWithLine": "Ozoagn (mid Linien)", + "Normal": "Ganz normal", + "Bottom": "Untn", + "None": "Gar nix", + "Timezone": "Zeidzon", + "Search Engine Visibility": "Sichtboarkeit fia Suachmaschinan", + "Allow indexing": "Indexierung erlaubm", + "Current Password": "Aktuells Passwoat", + "Repeat New Password": "Neis Passwoat wiedahoin", + "Update Password": "Passwoat aktualisiern", + "Disable Auth": "Auth ausschoidn", + "Enable Auth": "Auth einschoidn", + "disableauth.message1": "Bist da ganz sicher, dass d' {disableAuth} wuist?", + "disable authentication": "Authentifizierung ausschoidn", + "Please use this option carefully!": "Pass bittschee gscheid auf mid dera Option!", + "where you intend to implement third-party authentication": "wo du a Drittanbieta-Authentifizierung eibaua wuist", + "Logout": "Ausloggn", + "Leave": "Valassn", + "I understand, please disable": "I hob's kapiad, bittschee ausschoidn", + "Confirm": "Bestätign", + "Tag with this name already exist.": "A Kennzeichnung mid dem Nama gibts scho.", + "Tag with this value already exist.": "A Kennzeichnung mid dem Wert gibts scho.", + "tagAlreadyOnMonitor": "De Kennzeichnung (Nam und Wert) is beim Monitor scho dabei oda werd grod dazuagfügt.", + "tagAlreadyStaged": "De Kennzeichnung (Nam und Wert) is fia den Durchgang scho vorgmerkt.", + "value (optional)": "Wert (optional)", + "Gray": "Grau", + "Red": "Rot", + "Orange": "Orange", + "Green": "Grea", + "Blue": "Blau", + "Indigo": "Indigo", + "Purple": "Lila", + "Pink": "Rosa", + "Search...": "Suacha…", + "Search monitored sites": "Überwachte Seitn suacha", + "webhookFormDataDesc": "{multipart} is guad fia PHP. Des JSON muaß mit {decodeFunction} ausglesn wern", + "Avg. Ping": "Durschn. Ping", + "Avg. Response": "Durschn. Antwortzeit", + "Entry Page": "Einstiegssaitn", + "statusPageNothing": "Do is nix, bittschee füg a Gruppn oda an Monitor hinzu.", + "statusPageRefreshIn": "Aktualisierung in: {0}", + "No Services": "Keine Dienste", + "All Systems Operational": "Olle Systeme laffan ohne Probleme", + "Partially Degraded Service": "Teilweis eigschränkta Dienst", + "Degraded Service": "Eigschränkta Dienst", + "Add Group": "Gruppn hinzufügn", + "Add a monitor": "Monitor hinzufügn", + "Edit Status Page": "Statusseitn bearbeitn", + "Status Page": "Statusseitn", + "Status Pages": "Statusseitn", + "webhookJsonDesc": "{0} is guad fia olle modernan HTTP-Server wie zum Beispui Express.js", + "templateMonitorJSON": "Objekt, des an Monitor bschreibt", + "templateLimitedToUpDownNotifications": "bloß fia AUF/O-Benachrichtigungen vafügbar", + "webhookAdditionalHeadersTitle": "Extra Kopfzeuin", + "webhookBodyPresetOption": "Vorgob - {0}", + "webhookBodyCustomOption": "Eigna Inhoit", + "Webhook URL": "Webhook-Url", + "Priority": "Wichtigkeit", + "emojiCheatSheet": "Emoji-Spickzedl: {0}", + "Read more": "Lies weida", + "appriseInstalled": "Apprise is scho drauf.", + "appriseNotInstalled": "Apprise is ned installiert. {0}", + "Method": "Art und Weis", + "Body": "Inhoit", + "PushUrl": "Push-Adress", + "BodyInvalidFormat": "Da Inhoit vo da Onfrog is koa gültigs JSON: ", + "descriptionHelpText": "Werd auf'm internen Dashboard ozoagt. Markdown is erlaubt und werd vor da Anzeige bereinigt (Leerzeichn und Einrückunga bleim dahoitn).", + "telegramProtectContentDescription": "Wannst des aktivierst, wern d'Nachrichtn vom Bot in Telegram vor'm Weiterleitn und Speichern gschützt.", + "Setup Notification": "Benachrichtigung earichdn", + "Light": "Hella", + "Path": "Pfad", + "templateHeartbeatJSON": "Objekt, des an Heartbeat bschreibt", + "templateMsg": "Benachrichtigungstext", + "Show Clickable Link Description": "Wann des ausgwählt is, ko jeda, der Zuagriff auf de Statusseitn hod, a de Monitor-URL seng.", + "wayToGetSevenIOApiKey": "Geh auf's Dashboard unta app.seven.io > Developer > API-Key > auf den greana \"Hinzufügn\"-Knopf drucka", + "wayToGetEvolutionUrlAndToken": "D'API-URL und den Schlüssel kriagst, wannst bei {0} in dein gwünschtn Kanal neigehst", + "wayToGetWhapiUrlAndToken": "D'API-URL und den Schlüssel kriagst, wannst bei {0} in dein gwünschtn Kanal neigehst", + "templateServiceName": "Dienstnam", + "templateStatus": "zustand", + "templateHostnameOrURL": "Hostnam oda URL", + "defaultFriendlyName": "Neia Monitor", + "Change Password": "Passwoat ändan", + "filterActive": "Aktiv", + "Inactive": "Inaktiv", + "Token": "Berechtigungs-Code", + "Show URI": "URI ozoagn", + "Tags": "Kennzeichnunga", + "Add New Tag": "Neie Kennzeichnung dazuafügn", + "Add New below or Select...": "Untn a neis dazuafügn oda auswähln…", + "filterActivePaused": "Pausiat", + "Active": "Aktiv", + "Custom": "Selba gmacht", + "Go to Dashboard": "Auf's Dashboard geh", + "defaultNotificationName": "Mei {notification} Alarm ({number})", + "here": "do", + "Post URL": "POST-Url", + "Required": "Erforderlich", + "Server URL": "Server-Adress", + "Headers": "Kopfzeuin", + "disableAPIKeyMsg": "Bist da ganz sicher, dass den API-Schlissl deaktivern wuist?", + "wayToGetPagerTreeIntegrationURL": "Soboidst de Uptime Kuma Integration in PagerTree oglegt host, kopier den \"Endpoint\". Olle Details findst unta {0}", + "deleteStatusPageMsg": "Bist da ganz sicher, dass de Statusseitn leschn wuist?", + "wayToGetClickSendSMSToken": "Den API-Nutzama und in API-Schlissl kriagst vo {here}.", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Dann suach da a Aktion aus, zum Beispui an Szenenwechsel, bei dem a RGB-Liacht auf Rot gschait werd.", + "New Password": "Neis Passwoat", + "now": "iatzad", + "time ago": "vor {0}", + "-year": "-Joahr", + "Accepted Status Codes": "Bassnde Codes", + "Save": "Speichern", + "Host URL": "Host-URL", + "ignoredTLSError": "TLS/SSL-Fehla san ignoriad worn", + "pushOptionalParams": "Optionale Parameta: {0}", + "programmingLanguages": "Programmiasprochn", + "Application Token": "Anwendungstoken", + "locally configured mail transfer agent": "lokal konfiguierda Mail-Ibadrog-Agent", + "ignoreTLSErrorGeneral": "TLS/SSL-Fehla fia d'Vabindung ignoria", + "pushViewCode": "Wia ma an Push-Monitor hernimmt? (Code oschaun)", + "Json Query Expression": "Json Abfrog-Ausdruck", + "Push URL": "Push-URL", + "needPushEvery": "Du soist de URL olle {0} Sekundn afruafa.", + "pushOthers": "Ondane", + "Auto": "Automatisch", + "successKeyword": "Des Stichwort fia \"Erfoig\"", + "records": "Datensätze", + "HTTP Options": "HTTP-Auswoimöglichkeiten", + "One record": "Ei Datensatz", + "Current User": "Da Benutza, der grod dro is", + "topic": "Des Thema", + "topicExplanation": "Des MQTT-Thema, des ma im Aug bhoidn wuin", + "mqttWebSocketPath": "Da MQTT-WebSocket-Pfad", + "mqttWebsocketPathExplanation": "Da Pfad fia d'WebSocket-Vabindungen iba MQTT (zum Beispui /mqtt)", + "mqttWebsocketPathInvalid": "Bittschee nimm a richte Form fia den WebSocket-Pfad", + "mqttHostnameTip": "Bittschee nimm des Format {hostnameFormat}", + "successKeywordExplanation": "Des MQTT-Wort, des ma ois Erfoig geitn lassn", + "recent": "Was grod erst war", + "Reset Token": "Des Token zum Zurücksetzn", + "Done": "Basst scho", + "Info": "Bscheid", + "Security": "Sicherheit", + "Steam API Key": "Da Steam-Schlissl fia d'Schnittsteijn", + "Shrink Database": "De Datenbank a wengal kloaner macha", + "Pick a RR-Type...": "Suach da an RR-typ aus…", + "Pick Accepted Status Codes...": "Suach da d'Statuscodes aus, de bassn…", + "Default": "So wia's oiwai is", + "Create Incident": "An Vorfoi eitrogn", + "Title": "Iberschrift", + "Content": "Des, wos drin steht", + "Style": "Wia's ausschaugt", + "info": "Bscheid", + "warning": "Obacht", + "danger": "Gfai", + "critical": "Brenzlig", + "Monitor History": "De Gschicht vom Monitor", + "clearDataOlderThan": "De Daten vo da Monitor-Gschicht fia {0} Dog aufhem.", + "PasswordsDoNotMatch": "De Kennwerter bassn ned zam.", + "resolverserverDescription": "Cloudflare is da Standard-Serva. Du konnst a Listn mit IP-Adressn oda Hostnama ogem, de mit Kommas drent san.", + "keywordDescription": "Suach des Stichwort im normalen HTML oder JSON. Bei da Suach werd zwischn Groß- und Kleinschreibung untaschiedn.", + "pushoverDesc2": "Wennst d'Meldungen an untaschiedliche Gerät verschicka wuist, dann füll des Feld fia de Gerät aus.", + "octopushLegacyHint": "Nimmst du de oide Version vo Octopush (2011-2020) her oder de neie?", + "groupOnesenderDesc": "Schaug, dass de GroupID passt. Wennst a Nachricht in a Gruppn schicka wuist, zum Beispui: 628123456789-342345", + "rabbitmqNodesDescription": "Gib de URL fia de RabbitMQ-Verwaltungsknotn ei, mit Protokoll und Port. Zum Beispui: {0}", + "dnsPortDescription": "DNS-Server-Anschluss. Standard is 53. Den Port konnst jedazeit ändern.", + "wayToGetHeiiOnCallDetails": "Wia ma d'Trigger-ID und de API-Schlissl kriagt, steht ois in da {documentation} drin", + "telegramTemplateFormatDescription": "Telegram erlaubt untaschiedliche Schreibweisn fia Nachrichten, schau bei Telegram {0} fia de ganz genauen Details nacha.", + "wayToGetClickSMSIRTemplateID": "De Vorlog muss a {uptkumaalert} Fejd drin ham. Du konnst {here} a neie Vorlog bastln.", + "Custom CSS": "Eigns CSS-Gwandl", + "error": "Do hod's wos", + "primary": "hauptsach", + "dark": "dunkl", + "Post": "Post", + "Please input title and content": "Bittschee gib a Überschrift und an Inhalt ei", + "Created": "Erstellt", + "Last Updated": "Zletzt hergricht", + "Switch to Light Theme": "Auf d'heije Ansicht umsteijn", + "Switch to Dark Theme": "Auf d'finstere Ansicht umsteijn", + "Show Tags": "De Pickerl zoagn", + "Hide Tags": "De Pickerl vostecka", + "Description": "Wos des eigntlich is", + "No monitors available.": "Es san koane Monitorn do.", + "Add one": "An neia dazua doa", + "No Monitors": "Koane Monitor do", + "Untitled Group": "A Gruppn ohne Nam", + "Services": "De Dienste", + "Discard": "Weg damit", + "Cancel": "Abbrechn", + "Installed": "Installiert", + "Not installed": "Ned installiert", + "Running": "Des lafft", + "Not running": "Des lafft ned", + "Add New Status Page": "A neie Statusseitn dazua doa", + "Slug": "Namens-Kurzl", + "Accept characters:": "De Zeichn san erlaubt:", + "wayToGetCloudflaredURL": "(Lad da Cloudflared vo {0} runter)", + "cloudflareWebsite": "Cloudflare-Netzseitn", + "Don't know how to get the token? Please read the guide:": "Woast ned, wia ma an Token kriagt? Bittschee lies da d'Anleitung durch:", + "Domain Names": "Internet-Adressn (Domains)", + "signedInDisp": "Ois {0} angmejdt", + "Show Powered By": "\"Gmocht vo\" zoagn", + "signedInDispDisabled": "D'Anmeldung is ausgschoidt.", + "RadiusSecret": "Radius-Gheimnis", + "deleteDockerHostMsg": "Bist da ganz sicher, dass den Docker-Host fia olle Monitore leschn wuist?", + "telegramSendSilentlyDescription": "Schickt de Nachricht ganz stad. De Leid kriagn a Meldung, aba ohne an Ton.", + "chatIDNotFound": "D'Chat-ID is ned gfundn worn; schreib bittschee zeascht amoi a Nachricht an den Bot", + "wayToCheckSignalURL": "Dast seng konnst, wia ma des einricht, schau da de Netzseitn o:", + "Proxy": "Proxy", + "twilloMessagingServiceSIDHelptext": "Gib dei Messaging Service SID do ei, wennst {twillo_messaging_service_help_link} hernimmsst, um d'Absender und d'Funktionen zum Verwalten", + "cellsyntOriginatortypeAlphanumeric": "A Buchstabm-Zoin-Folg (höchstns 11 Zeichn). Wer des kriagt, ko auf d'Nachricht ned antwortn.", + "cellsyntSplitLongMessages": "Lange Nachrichten in bis zu 6 Teile aufteijn. 153 x 6 = 918 Zeichn.", + "Invalid": "Des is a Schmarrn", + "User": "Benutza", + "Remove Token": "Den Token weghaun", + "Start": "Af geht's", + "No consecutive dashes": "Koane Bindestrich hintereinanda", + "startOrEndWithOnly": "Foing oder Aufhern deafs bloß mit {0}", + "statusPageSpecialSlugDesc": "Extra-Kurzl {0}: De Seitn werd zoagt, wenn ma gar nix eigebm hat", + "Next": "Weida", + "The slug is already taken. Please choose another slug.": "Des Kurzl gibt's scho. Suach da bittschee a anders aus.", + "No Proxy": "Koa Proxy", + "Authentication": "D'Anmeldung", + "HTTP Basic Auth": "HTTP Standard-Auth", + "New Status Page": "A neie Statusseitn", + "Page Not Found": "De Seitn findt ma ned", + "Reverse Proxy": "Reverse-Proxy", + "Backup": "Sicherheitskopie", + "About": "Iaba des Ganze", + "Message:": "Nachricht:", + "grpcMethodDescription": "Nam vo d'Methodn wern in camelCase umgwandelt, so wia sayHello, check, und so weida.", + "auto-select": "Automatisch aussuacha", + "Select": "Aussuacha", + "selectedMonitorCount": "{0} san ausgwählt", + "Check/Uncheck": "Ohackln oder Weglassn", + "Powered by": "Lafft mit", + "Customize": "So hiwiang wia mas braucht", + "Custom Footer": "De eigne Fuasszeijn", + "Proxies": "De Proxies", + "default": "Standard", + "enabled": "Eigschoidt", + "setAsDefault": "Ois Standard festleng", + "deleteProxyMsg": "Wuist den Proxy echt fia oisamt löschn?", + "proxyDescription": "Du muasst an Proxy scho am Monitor zuadeitn, sonst basst des ned.", + "Certificate Chain": "Da gane Rattnschwanz vo de Zertifikate", + "Valid": "Des is recht so", + "Stop": "Hoid, stop", + "HTTP Headers": "HTTP-Kopfzeijn", + "Trust Proxy": "Vertrau am Proxy", + "Other Software": "Andane Software", + "Valid To:": "Gültig bis:", + "Days Remaining:": "Tog, de no bleibm:", + "Issuer:": "Aussteller:", + "Fingerprint:": "Fingabdruck:", + "No status pages": "Koane Statusseitn do", + "For example: nginx, Apache and Traefik.": "Zum Beispui: nginx, Apache und Traefik.", + "Please read": "Bittschee lesn", + "Subject:": "Betreff:", + "Domain Name Expiry Notification": "Domain-Nam-Ablauf-Warnung", + "Add a new expiry notification day": "An neia Tog fia d'Ablauf-Meldung dazua doa", + "Remove the expiry notification": "Den Tog fia d'Ablauf-Meldung weghaun", + "Date Created": "Wann's gmocht worn is", + "Footer Text": "Da Text fia d'Fuasszeijn", + "Refresh Interval": "Wia oft nei glon werd", + "Refresh Interval Description": "De Statusseitn ladt se olle {0} Sekundn komplett nei", + "promosmsTypeEco": "SMS ECO - billig, aba lahm und oft iabalastet. Geht bloß fia Leid in Poln.", + "light": "heij", + "certHostnameMismatch": "Da Hostnama vom Zertifikat basst ned zur Monitor-URL.", + "Container Name / ID": "Container Nama / ID", + "Docker Host": "Docker-Host", + "Docker Hosts": "Docker-Hosts", + "Domain": "Domain", + "Workstation": "Oabatsplatz", + "Packet Size": "Paketgräß", + "Bot Token": "Bot-Token", + "wayToGetTelegramToken": "Da konst da a Token vo {0} hoin.", + "Chat ID": "Chat-ID", + "telegramMessageThreadID": "(Optional) Nochrichtn-Thread-ID", + "telegramSendSilently": "Stad schicka", + "wayToGetLineNotifyToken": "An Access-Token konst da vo {0} hoin", + "supportBaleChatID": "Unterstützt Direkt-Chat / Gruppn / Kanal Chat-ID", + "wayToGetBaleToken": "Da konst da a Token vo {0} hoin.", + "Home Assistant URL": "Home-Assistant URL", + "Long-Lived Access Token": "Langzeit-Zuagriffstoken", + "Notification Service": "Benachrichtigungs-Dienst", + "default: notify all devices": "Standard: Olle Gräta bscheid gem", + "Frontend Version do not match backend version!": "D'Frontend-Version basst ned zur Backend-Version zam!", + "lastDay1": "Letzda Dog im Monat", + "Event type:": "Ereignis-Typ:", + "Server Timezone": "Server-Zeitzone", + "telegramProtectContent": "Vorm Weidaschicka/Speichern schützn", + "Trigger type:": "Ausläsa-Typ:", + "endDateTime": "Enddatum/-zeid", + "warningTimezone": "Des nutzt d'Server-Zeitzone", + "weekdayShortTue": "Di", + "dayOfMonth": "Dog im Monat", + "RadiusSecretDescription": "Des gmoasame Gheimnis zwischn Client und Server", + "Coming Soon": "Kimmt boid", + "telegramUseTemplate": "A eigne Nochrichtnvorlog hernehma", + "telegramUseTemplateDescription": "Wenns eigschoid is, wead d'Nochricht mit ana eignen Vorlog vaschickt.", + "telegramServerUrl": "(Optional) Server-URL", + "supportTelegramChatID": "Unterstützt Direkt-Chat / Gruppn / Kanal Chat-ID", + "YOUR BOT TOKEN HERE": "DEIN BOT-TOKEN KIMMT DO NEI", + "disableCloudflaredNoAuthMsg": "Du bist im \"No Auth\"-Modus, do brauchts koa Passwort.", + "Automations can optionally be triggered in Home Assistant:": "Automatisierungen kinnan optional im Home Assistant ausglöst wern:", + "Frontend Version": "Frontend-Version", + "Optional": "Optional", + "backupRecommend": "Bittschee mach liaba direkt a Backup vom Volume oda vom Datnordna (./data/).", + "and": "und", + "or": "oda", + "sameAsServerTimezone": "Gleich wia d'Server-Zeitzone", + "startDateTime": "Startdatum/-zeid", + "weekdayShortSun": "So", + "dayOfWeek": "Wochadog", + "weekdayShortMon": "Mo", + "weekdayShortWed": "Mi", + "lastDay": "Letzda Dog", + "lastDay2": "Vorletzda Dog im Monat", + "lastDay3": "Drittletzda Dog im Monat", + "lastDay4": "Viertletzda Dog im Monat", + "No Maintenance": "Koa Wartung", + "maintenanceStatus-unknown": "Unbekannt", + "Display Timezone": "Zeitzone ozoang", + "statusPageMaintenanceEndDate": "Ende", + "IconUrl": "Icon-URL", + "pauseMaintenanceMsg": "Bist da sicha, dassd pausiern wuisd?", + "maintenanceStatus-under-maintenance": "Werd grod gwart", + "maintenanceStatus-inactive": "Inaktiv", + "maintenanceStatus-scheduled": "Eiplant", + "maintenanceStatus-ended": "Beendt", + "weekdayShortSat": "Sa", + "RadiusCalledStationIdDescription": "D'Kennung vo da ogruafna Station", + "RadiusCalledStationId": "D'Kennung vo da ogruafna Station", + "RadiusCallingStationIdDescription": "D'Kennung vom ogruafna Grät", + "RadiusCallingStationId": "D'Kennung vo da oruafandn Station", + "Certificate Expiry Notification": "Meldung, dass s'Zertifikat oblafft", + "API Username": "API-Benutzanama", + "API Key": "API-Schlissl", + "Show update if available": "Zoag s'Update o, wenns oans gibt", + "Also check beta release": "Schau a noch da Beta-Version", + "Using a Reverse Proxy?": "Vawendst an Reverse Proxy?", + "Check how to config it for WebSocket": "Schau noch, wiasd des fia WebSocket eistellst", + "Steam Game Server": "Steam Spui-Server", + "Most likely causes:": "De wahrscheinlichtsn Gründ:", + "The resource is no longer available.": "De Ressource is nimmer do.", + "There might be a typing error in the address.": "Es kunnt a Tippfehler in da Adress sei.", + "What you can try:": "Wosd probiern kannst:", + "Retype the address.": "Schreib d'Adress nomoi nei.", + "Go back to the previous page.": "Geh zruck zur vorign Seitn.", + "noDockerHostMsg": "Ned vafügbar. Richt zerscht amoi an Docker Host ei.", + "DockerHostRequired": "Bittschee stej an Docker Host fia den Monitor ei.", + "Connection String": "Vabindungszeichenfoig", + "Query": "Obfrog", + "settingsCertificateExpiry": "Oblauf vom TLS-Zertifikat", + "certificationExpiryDescription": "HTTPS-Monitore gem Bscheid, wenn s'TLS-Zertifikat oblafft in:", + "Setup Docker Host": "Docker Host eirichtn", + "Connection Type": "Vabindungstyp", + "Docker Daemon": "Docker-Daemon", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker-Container", + "cronExpression": "Cron-Ausdruck", + "cronSchedule": "Zeidplan: ", + "invalidCronExpression": "Unguiltiga Cron-Ausdruck: {0}", + "recurringInterval": "Intervall", + "Recurring": "Wiedakehrend", + "strategyManual": "Händisch Aktiv/Inaktiv", + "Enable DNS Cache": "(Vaoited) DNS-Cache fia HTTP(s)-Monitore eischoidn", + "Disable": "Ausschoidn", + "Enable": "Eischoidn", + "Examples": "Beispui", + "Event data:": "Ereignis-Datn:", + "weekdayShortThu": "Do", + "weekdayShortFri": "Fr", + "minimumIntervalWarning": "Abstände unter 20 Sekundn kanntn dazua fiahn, dass nimmer ois rund lafft.", + "loadingError": "De Daten kinnan grod ned g’hoit wern, probier’s bittschön später nomoi.", + "deleteNotificationMsg": "Bist da ganz sicher, dass’d de Benachrichtigung für olle Monitoren löschen wuist?", + "deleteGroupMsg": "Bist da ganz sicher, dass’d de Gruppn wirklich löschen wuist?", + "clearEventsMsg": "Bist da ganz sicher, dass’d olle Events für den Monitor löschen wuist?", + "clearHeartbeatsMsg": "Bist da ganz sicher, dass’d olle Heartbeats für den Monitor löschen wuist?", + "tokenValidSettingsMsg": "Da Token is guidig! Iatz koanst de 2FA-Eistellungen speichern.", + "recurringIntervalMessage": "Oamoi am Dog laffn lassn | Olle {0} Dog oamoi laffn lassn", + "confirmClearStatisticsMsg": "Bist da ganz sicher, dass’d wirklich de GANZN Statistiken löschen wuist?", + "notificationDescription": "D’Benachrichtigungen miaßn am Monitor zuagordnet sei, damit’s überhaupts hihaut.", + "backupDescription": "Du koanst a Backup vo olle Monitoren und Benachrichtigungen in a JSON-Datei macha.", + "octopushAPIKey": "Den „API-Key“ findst bei de HTTP-API-Zugangsdaten im Menü", + "wayToGetKookBotToken": "Leg a Anwendung o und hoi da dein Bot-Token unter {0}", + "signalImportant": "WICHTIG: Du deafst Gruppen und Nummern bei de Empfänger ned mischn!", + "smtpDkimDesc": "Schaug da am besten de Nodemailer DKIM {0} o, damit’st woast, wia ma’s hernimmt.", + "smspartnerApiurl": "Dein API-Key findst in deim Dashboard unter {0}", + "The phone number of the recipient in E.164 format.": "D’Nummer vom Empfänger im E.164-Format.", + "rabbitmqNodesInvalid": "Nimm her bittschön a ganz ausgschriebene URL (de mit „http“ o’fängt) für d’ RabbitMQ-Nodes.", + "pingNumericDescription": "Wennst des o’kreizlt hast, wern d’IP-Adressn ausgem statt de Host-Naman", + "pingIntervalAdjustedInfo": "Da Abstand werd automatisch an d’Ozahl vo de Packtl, an globaln Timeout und an Timeout pro Ping o’passt", + "customUrlDescription": "Des werd dann ois URL zum Oklicka hergnomma, statt dera vom Monitor.", + "wayToGetWahaApiKey": "Da API-Key is da Wert vo da WHATSAPP_API_KEY Environment-Variabln, de wo’st hergnumma hast, damit WAHA lafft.", + "Unable to get permission to notify": "Konn de Erlaubnis für d’Meldungen ned kriang (entweder obglehnt oder ignoriert).", + "wayToGetBitrix24Webhook": "Du koanst an Webhook anlegn, wennst di an de Schritt unter {0} hoitst", + "Number of retry attempts if webhook fails": "Wia oft’s nomoi probiert wern soi (olle 60 bis 180 Sekundn), wenn da Webhook fehlschlogt.", + "gtxMessagingToHint": "Internationales Format, mit am Plus „+“ ganz vorn dro ({e164}, {e212} oder {e214})", + "enableNSCD": "Schalt ’s NSCD (Name Service Cache Daemon) ei, damit de ganzn DNS-Ofragen gspeichert wern", + "needSignalAPI": "Du brauchst an Signal-Client mit ana REST-API.", + "wayToGetTeamsURL": "Du koanst unter {0} nachschaugn, wia ma a Webhook-URL o’legt.", + "wayToGetZohoCliqURL": "Unter {0} konnst di schlau macha, wia ma a Webhook-URL richtig o’legt.", + "deleteRemoteBrowserMessage": "Bist da ganz sicher, dass’d den Remote Browser für alle Monitoren löschen wuist?", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Absender-Telefonnummer / TPOA (Transmission Path Originating Address)", + "pingGlobalTimeoutDescription": "De ganze Zeit in Sekundn, bevor ’s Ping-Aufhern durt – ganz wurscht, wia vui Packtl scho gschickt wordn san", + "smsplanetApiDocs": "Gnauere Infos dazua, wia’st an d’API-Tokens kimmst, findst in {the_smsplanet_documentation}.", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Damit’st an Nextcloud Talk Bot installieren koanst, brauchst Admin-Rechte auf’m Server.", + "acceptedStatusCodesDescription": "Wähl de Status-Codes aus, de wo ois erfolgreiche Antwort guidn soin.", + "Single Maintenance Window": "Oinzlts Wartungsfensta", + "chromeExecutableAutoDetect": "Automatisch dakenna", + "Edit Maintenance": "Wartung bearban", + "Subprotocol": "Untaprotokoll", + "Duration (Minutes)": "Dauer (Minutn)", + "chromeExecutable": "Chrome/Chromium Ausfia-Datei", + "Maintenance Time Window of a Day": "Wartungszeitfenster vo am Dog", + "Effective Date Range": "Guitigkeits-Zeitraum (optional)", + "Schedule Maintenance": "Wartung eiblanan", + "Clone Maintenance": "Wartung klona", + "ariaPauseMaintenance": "Den Wartungs-Zeitplan pausiern", + "ariaResumeMaintenance": "Den Wartungs-Zeitplan wieda aufnehma", + "ariaCloneMaintenance": "A Kopie vo dem Wartungs-Zeitplan macha", + "plugin": "Plugin | Plugins", + "DateTime Range": "Datum- und Zeit-Bereich", + "ariaDeleteMaintenance": "Den Wartungs-Zeitplan leschn", + "install": "Installiern", + "installing": "Wird installierd", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "TLS-Fehler ignoriern", + "uninstall": "Deinstalliern", + "secureOptionNone": "Nix / STARTTLS (25, 587)", + "Date and Time": "Datum und Uhrzeit", + "uninstalling": "Wird deinstallierd", + "ariaEditMaintenance": "Den Wartungs-Zeitplan bearban", + "Clone Monitor": "Monitor klona", + "cloneOf": "Kopie vo {0}", + "Clone": "Klona", + "SMTP Security": "SMTP-Sicherheid", + "Ignore STARTTLS": "STARTTLS ignoriern", + "Use STARTTLS": "STARTTLS vawendn", + "notificationRegional": "Regionoi", + "confirmUninstallPlugin": "Bist da sicher, dass des Plugin deinstalliern wuist?", + "smtp": "Email (SMTP)", + "Use HTML for custom E-mail body": "HTML fia an eigna E-Mail-Text hernehma", + "Clear All Events": "Ois an Ereignissen rauslösch'n", + "emailTemplateLimitedToUpDownNotification": "Gibt's bloß fia UP/DOWN Lebanszeichn, sunst is's null", + "To Email": "An (E-Mail)", + "smtpBCC": "BCC (Blindkopie)", + "Channel access token": "Kanal-Zuagangs-Token", + "Channel access token (Long-lived)": "Kanal-Zuagangs-Token (der lang hoit)", + "Line Developers Console": "Line Developers Console", + "Your User ID": "Dei eigne Benutzer-ID", + "Messaging API": "Messaging-Schnittstej", + "Icon URL": "Bildl-URL", + "dataRetentionTimeError": "D'Zeit zum Aufbewahrn muaß 0 oda gressa sei", + "infiniteRetention": "Stell 0 ei, wennst es ewig aufheb'n wuist.", + "Could not clear events": "{failed}/{total} Ereignisse hamma ned wegputzn kinna", + "backupDescription2": "B'obacht: Da Verlaaf und de Ereignis-Daten san ned dabei.", + "octopushLogin": "\"Login\" vo de HTTP-API-Zugangsdaten im Kontroll-Zentrum", + "Leave blank to use a shared sender number.": "Lass leer, wennst a gemeinsames Absender-Numma hernehma wuist.", + "Server URL should not contain the nfty topic": "D'Server-URL deaf des nfty-Thema ned drin hab'n", + "ntfyPriorityHelptextAllEvents": "Alle Ereignisse wern mit da höchsten Priorität verschickt", + "smsplanetNeedToApproveName": "Muaß im Client-Panel erst no b'stätigt wern", + "e.g. {discordThreadID}": "wia zum Beispui {discordThreadID}", + "emailCustomisableContent": "Inhoit zum Seiba-Eistein", + "From Email": "Vo (E-Mail)", + "Discord Webhook URL": "Discord Webhook URL-Adress", + "Bot Display Name": "Discord Webhook-Adress", + "Select message type": "Wähl an Meldungs-Typ aus", + "Send to channel": "An an Kanal schicka", + "Recipients": "Empfänga", + "wsCodeDescription": "Wia des mit de Status-Codes genau is, schau da des {rfc6455} o", + "Subprotocol(s)": "Unter-Protokoll(e)", + "emailTemplateMsg": "De Meldung vo da Benachrichtigung", + "Access Token": "Zuagangs-Token", + "lineDevConsoleTo": "Line Developers Console – {0}", + "Basic Settings": "Grund-Eistellungen", + "pauseMonitorMsg": "Bist da sicher, dass d'pausiern wuist?", + "Hello @everyone is...": "Servus {'@'}everyone, grad is…", + "rrtypeDescription": "Suach da den RR-Typ aus, denst übawachn wuist", + "leave blank for default subject": "Lass leer, wennst an Standard-Betreff wuist", + "emailCustomBody": "Eigner Textinhait", + "leave blank for default body": "Lass leer fia den Standard-Text", + "forumPostName": "Nam vom Foren-Thema", + "threadForumPostID": "Thread / Foren-Post ID", + "emailCustomSubject": "Eigner Betreff", + "emailTemplateMonitorJSON": "A Objekt, des an Monitor b'schreibt", + "emailTemplateHeartbeatJSON": "A Objekt, des des Lebanszeichn b'schreibt", + "Create new forum post": "A neis Foren-Thema aufmachn", + "deleteMonitorMsg": "Bist da sicher, dass d'den Monitor leschn wuist?", + "No monitors found": "Koane Monitorn g'fundn.", + "Events cleared successfully": "Ois sauber rausglöscht.", + "twoFAVerifyLabel": "Gib bittschön dein Token fia d'2FA-Prüfung ei:", + "affectedMonitorsDescription": "Wähl de Monitorn aus, de bei da aktuellen Wartung betroffn san", + "affectedStatusPages": "Zeig de Wartungs-Meldung auf de ausgwähltn Status-Seitn o", + "octopushTypeLowCost": "Günstig (Langsam – werd ab und zua vom Betreiber blockiert)", + "whapiRecipient": "Telefonnumma / Kontakt-ID / Gruppen-ID", + "apiKeysDisabledMsg": "De API-Schlissl san ausgschoid, weil d'Anmeldung deaktiviert is.", + "enableGRPCTls": "gRPC-Anfragn mit TLS-Vabindung schicka lassn", + "clearAllEventsMsg": "Bist da ganz sicher, dass d'alle Ereignisse wegputzn wuist?", + "postToExistingThread": "In an vorhandnen Thread nei-schreim", + "Prefix Custom Message": "Eigner Text vornedroa", + "Number": "Numma", + "User ID": "Benutzer-ID", + "deleteMaintenanceMsg": "Bist da sicher, dass d'de Wartung leschn wuist?", + "confirmEnableTwoFAMsg": "Bist da sicher, dass d'2FA wirklich ausschoidn wuist?", + "confirmDisableTwoFAMsg": "Bist da sicher, dass d'2FA wirklich ausschoidn wuist?", + "smtpCC": "CC (Kopie)", + "sipsakPingWarning": "Damitst an \"SIP Options Ping\"-Monitor hernehma konst, muast Uptime Kuma ohne Docker installiern und dazua aa no an \"Sipsak\"-Client auf deim Server draufpacka.", + "checkPrice": "Schau da d'Preise fia {0} o:", + "Check octopush prices": "Schau da de Octopush-Preise {0} o.", + "apiCredentials": "API-Zugangsdatn", + "passwordNotMatchMsg": "De Passwörter bassn ned zam. Host di eppa vadippt?", + "atLeastOneMonitor": "Suach da gscheiderweis zumindest oan Monitor aus, dens dawischt hod", + "pushoversounds gamelan": "Gamelan", + "LunaSea Device ID": "LunaSea Geräte-ID", + "pushoversounds cosmic": "Weltraum-Gschiss", + "pushoversounds magic": "Zaubarei", + "pushoversounds siren": "Sirene", + "pushoversounds pianobar": "Klavier-Bar", + "pushoversounds spacealarm": "Weltraum-Alarm", + "pushoversounds alien": "Marsmanderl-Alarm (lang)", + "Template plain text instead of using cards": "Einfacher Text statt de Kartn hernehma", + "hostnameCannotBeIP": "A DNS-Hostname konn koane IP-Adress sei. Host eppa des Resolver-Feidl gmoant?", + "invalidHostnameOrIP": "Der Hostname oda d'IP basst ned. Des muass scho a gscheider FQDN sei. Sternerl (Wildcards) deafst ned hernehma. A Unterstrich is okay, oda a Punkt ganz am End.", + "wildcardOnlyForDNS": "Sternerl-Hostnaum gengan bloß bei de DNS-Monitor.", + "invalidDNSHostname": "Der Hostname oda d'IP basst ned. Des muass scho a gscheider FQDN sei. Sternerl (Wildcards) deafst hernehma. A Unterstrich is okay, oda a Punkt ganz am End.", + "invalidURL": "De URL is a Schmarrn", + "promosmsLogin": "API-Anmeldenaum", + "promosmsPassword": "API-Passwort", + "pushoversounds falling": "Obe-Fliang", + "pushoversounds incoming": "Do kimmt wos", + "pushoversounds cashregister": "Kass", + "pushoversounds classical": "Klassisch", + "pushoversounds tugboat": "Schlepper", + "pushoversounds echo": "Pushover-Hall (lang)", + "pushoversounds none": "Gar nix (stad)", + "pushyAPIKey": "Hoamlicher API-Schlissl", + "pushyToken": "Geräte-Markrl", + "apprise": "Apprise (Unterstützt mehr ois fufzig Nachrichtn-Dienste)", + "User Key": "Nutzer-Schlissl", + "Device": "Gerät", + "Message Title": "Nachrichtn-Titl", + "Notification Sound": "Benachrichtigungs-Ton", + "More info on:": "Mehr Infos gibt's da: {0}", + "octopushPhoneNumber": "Telefonnumma (internationale Form, z. B.: +33612345678)", + "pushoversounds climb": "Bergauf-Gstanzl (lang)", + "pushoversounds persistent": "Hartnäckig (lang)", + "SMS Type": "SMS-Sortn", + "octopushTypePremium": "Premium (Fix - des is gscheid fürn Alarm)", + "RSS Title": "RSS-Titl", + "Leave blank to use status page title": "Lass as einfach leer, wennst an Titl vo da Statusseitn hernehma wuist", + "pushoversounds pushover": "Pushover (Standard)", + "pushoversounds bike": "Radl", + "pushoversounds bugle": "Fanfarn", + "pushoversounds intermission": "Pause", + "pushoversounds mechanical": "Mechanisch", + "pushoversounds updown": "Auf und Obe (lang)", + "pushoversounds vibrate": "Bloß bittln", + "GoogleChat": "Google Chat (bloß für Google Workspace)", + "pushoverMessageTtl": "Nachrichtn-Lebnsdauer (Sekundn)", + "year": "Joah | Joah", + "endpoint": "Endpunkt", + "Guild ID": "Gilden-ID", + "Resolver Server(s)": "Resolver Server", + "notificationUniversal": "Universelle", + "notificationChatPlatforms": "Chat-Plattforma", + "domain_expiry_unsupported_invalid_domain": "Des, wasst ois \"{hostname}\" eigstellt hast, is koa gscheida Domain-Nama", + "domain_expiry_unsupported_public_suffix": "D' Domain \"{domain}\" hod koa guitige Public-Suffix ned", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" is z'kurz fia a Top-Level-Domain", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "D' Ibawochung, wann d' Domain oblafft, geht fia \".{publicSuffix}\" ned, weil bei da IANA koa RDAP-Service drin steht", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" is a IP-Adress. D' Ibawochung, wann d' Domain oblafft, geht bloß mit am richtigen Domain-Nama", + "domain_expiry_unsupported_monitor_type": "D' Ibawochung, wann d' Domain oblafft, geht bei dem Monitor-Typ ned", + "domain_expiry_unsupported_missing_target": "Fia den Monitor is koa guitige Domain und a koa Hostnama eigstellt", + "smseaglePriority": "Nachrichtn-Priorität (0-9, des hechste is 9)", + "smseagleRecipient": "Empfänga (mehrane miassn mit am Komma drent sei)", + "systemServiceDescriptionLinux": "Prüft, ob da Linux-systemd-Service {service_name} aktiv is", + "systemServiceDescriptionWindows": "Prüft, ob da Windows Service Manager {service_name} lafft", + "evolutionRecipient": "Telefonnumma / Kontakt-ID / Gruppen-ID", + "Apprise URL": "Apprise-URL", + "Read more:": "Mehr dazua lesn: {0}", + "Status:": "Status: {0}", + "Strategy": "Wia ma's macha", + "Free Mobile User Identifier": "Benutzer-ID vum Free-Mobile-User", + "matrixHomeserverURL": "Homeserver-URL (mit http(s):// und wennst mogst mit'm Port)", + "pushDeerServerDescription": "Lass leer, wennst den offizielln Serva hernehma mogst", + "ipFamilyDescriptionAutoSelect": "Nimmt des {happyEyeballs} her, zum Festleng vo da IP-Familie.", + "Proto Service Name": "Nama vum Proto-Service", + "Enable TLS": "TLS eischoidn", + "Lowcost": "Billi", + "Proto Content": "Inhoid vum Proto", + "Economy": "Spar-Modus", + "SendKey": "SendKey", + "SMSManager API Docs": "SMSManager API-Doku", + "expectedTlsAlertDescription": "Suach da den TLS-Alert aus, den da Server z'ruckgem soid. Nimm {code}, zum Prüfen, ob mTLS-Endpunkte Vabindunga ohne Client-Zertifikat obblocken. Schau unter {link} nacha, wennst as genauer wissn mogst.", + "notificationPushServices": "Push Service", + "notificationSmsServices": "SMS Service", + "notificationEmail": "Email", + "notificationIncidentManagement": "Störungs-Management", + "notificationHomeAutomation": "Dahoam-Automatisierung", + "notificationOther": "Andane Anbindunga", + "Gateway Type": "Gateway Typ", + "serwersmsGroupIdHelptext": "D' ID oda d' Gruppen-IDs aus'm Kunden-Panel. De Kennnumman konnst da iba d' Aktionsgruppen / Index einalon oda einfach beim Beabatn im Kunden-Panel kopiern.", + "You can divide numbers with": "Zoin ko ma teitln mit", + "Badge Link Generator Helptext": "Badge-Links gibt's fia olle Monitor, de wo bei de öffentlichn Statusseitn dabeisana. Wennst mehra wissn mogst, schau in d' {documentation} nei.", + "noGroupMonitorMsg": "Geht ned. Du muaßt zeascht an Gruppen-Monitor olyng.", + "Proto Method": "Methodn vum Proto-Service", + "Example:": "Beispui: {0}", + "Free Mobile API Key": "API-Key vum Free-Mobile-Account", + "high": "Narrisch hoch", + "Show this Maintenance Message on which Status Pages": "Auf wejche Statusseitn soi de Wartungsmeldung ozoagt wern", + "serwersmsSenderName": "SMS-Absendanama (iba's Kunden-Portal registriert)", + "Optional: The audience to request the JWT for": "Optional: De Audience, fia de ma des JWT ofordert", + "passwordTooWeak": "Dei Passwort is z'schwach. Da miassn Buachstobn und Zoin drin sei. Und es muaß mindsden 6 Zoachn lang sei.", + "ntfyCallHelptext": "Ruf o, wenn da Alarm losgeht. Schreib 'ja' her, wenn d' dei easchte bstätigte Numma nehma mogst, oda gib a ganz bestimmte Numma ei (z. B. +12223334444). Des geht aba bloß mit ntfy Pro und a bstätigtn Telefonnumma.", + "webhookPostMethodDesc": "POST is fia de meistn modernen HTTP-Serva guad.", + "saveErrorResponseForNotifications": "HTTP-Fehlermeldung fia Benachrichtigungen speichern", + "saveResponseForNotifications": "HTTP-Erfolgsmeldung fia Benachrichtigungen speichern", + "saveResponseDescription": "Speichert de HTTP-Antwort und stellt's fia dei Benachrichtigungs-Vorlag ois {templateVariable} zur Verfügung", + "responseMaxLength": "Maximale Antwort-Läng (Bytes)", + "responseMaxLengthDescription": "Maximale Greß vo de Antwort-Daten, de gspeichert wern soin. Bei 0 is' wurscht, do gibt's koa Limit. Wenn de Antwort z'groß is, werd's einfach obgschnittn. Standard is 1024 (1KB)", + "aliyun-template-requirements-and-parameters": "De aliyun SMS-Vorlag braucht unbedingt de Parameter: {parameters}", + "Huawei": "Huawei", + "threemaSenderIdentityFormat": "8 Zeichn, fangt normalerweis mit am * o", + "WeCom Bot Key": "WeCom-Bot-Schlissl", + "Setup Proxy": "Proxy eistelln", + "smseagleApiv1": "APIv1 (fia bestehende Projekte und damit ois beim Altn bleibm ko)", + "Base URL": "Basis-URL", + "HeadersInvalidFormatBecause": "De Request Headers san koa gscheids JSON, weil: {error}", + "BodyInvalidFormatBecause": "Der Request Body is koa gscheids JSON, weil: {error}", + "steamApiKeyDescriptionAt": "Damitst an Steam Game Server ibaprüfn konnst, brauchst an Steam Web-API Key. Den konnst da dahoitn unta {url}", + "checkPriceAt": "Schau da de Preise fia {service} am besten amoi unta {url} o", + "WeCom Mentioned Mobile List": "WeCom-Handynumman-Listn fias Markiern", + "WeCom Mentioned Mobile List Description": "Haug do de Telefonnumman nei, de markiert wern soin. Wenn's mehra san, trenn's mit am Komma. Mit {'@'}all kriagn olle an Hiweis, dass d'Hüttn brennt.", + "smspartnerSenderNameInfo": "Des miaßn zwischn 3 und 11 ganz normale Zeichn sei", + "smseagleComma": "Wenn's mehrane san, miaßn's mit am Komma trennt wern", + "Invalid mobile": "De Handynummer [{mobile}] is a Schmarrn, des basst so ned", + "halopsa_setup_step1": "Leg a \"Integration Runbook\" in HaloPSA o (Eistellunga → Integrationa → Integration Runbooks)", + "halopsa_setup_step3": "Kopier de Webhook-URL und haug’s obn in des Textfejd nei", + "For safety, must use secret key": "Sicherheit geht vor: Do muasst an Geheimschlissl hernemma", + "High": "Gscheid vui", + "smtpDkimSettings": "DKIM-Eistellunga", + "smtpDkimKeySelector": "Schlissl-Auswähler (Key Selector)", + "auto resolve": "Automatisch erledign", + "greater than or equal to": "gleiner oda gleich", + "Monitors": "{n} Aufbasser | {n} Aufbasser", + "Sets end time based on start time": "Setzt d'Endzeit so fest, wia's mit da Startzeit ausmacht is", + "Dingtalk User List": "User-ID-Listn", + "auto acknowledged": "Automatisch bstätigt", + "rabbitmqNodesRequired": "Suach da de Rechenknotn (Nodes) fia den Aufbasser aus.", + "unknownDays": "A paar Dog, koana woas genau wia vui", + "nostrRelaysHelp": "Pro Zeiln bloß oa Relay-URL", + "noMonitorsSelectedWarning": "Du planst grod a Wartung, de koan oanzign Aufbasser betrifft. Wuist des wirkli so macha?", + "noMonitorsOrStatusPagesSelectedError": "A Wartung ohne Aufbasser oda Status-Seitn haut ned hi, des konnst so ned speichern", + "Device Token": "Geräte-Token", + "Retry": "Nomoi vasuachn", + "Internal Room Id": "Interne Raum-ID", + "Go back to home page.": "Wieda zruck auf d'Startseitn.", + "halopsa_setup_step2": "Stell de \"Runbook Actions\" so ei, dass de Alarme aa verarbat’ wern (z. B. \"Ticket erstoin\")", + "halopsa_username_desc": "Benutzernam fia de Anmeldung am Halo PSA Webhook", + "halopsa_password_desc": "Passwort fia de Anmeldung am Halo PSA Webhook", + "serwersmsRecipientTypePhone": "API-Passwort", + "serwersmsRecipientTypeGroup": "Gruppn", + "Separate multiple email addresses with commas": "Mehrane Email-Adressn bitte mit am Komma trenna", + "Proxy Server": "Proxy-Server (da Mittlsmo)", + "SignName": "Absender-Nam (SignName)", + "Proxy Protocol": "Proxy-Protokoll (wia de Kistn hoid redt)", + "Invalid userId": "De User-ID [{userId}] is a Schmarrn, de gibt's ned", + "authUserInactiveOrDeleted": "Der Benutzer is nimmer aktiv oda scho glescht.", + "promosmsAllowLongSMS": "Lange SMS zualassn", + "aboutWebhooks": "Mehra Infos über Webhooks findst do: {0}", + "Icon Emoji": "Bildl-Emoji", + "systemServiceDescription": "Schau noch, ob da System-Dienst {service_name} grod aktiv is", + "brevoSeparateMultipleEmails": "Mehrane Email-Adressn bitte mit am Komma trenna", + "resendApiHelp": "Do konnst an API-Schlissl erstoin: {0}", + "smtpDkimHashAlgo": "Hash-Algorithmus (wennst magst)", + "serwersmsGroupId": "Gruppn ID", + "Bark API Version": "Bark API-Version", + "Bark Endpoint": "Bark Endpunkt", + "Bark Group": "Bark Gruppn", + "Bark Sound": "Bark Sound (wos fia a Gschepper)", + "Mention User List": "De User-ID-Listn markiern", + "Dingtalk Mobile List": "Handynumman-Listn", + "Mention group": "De Gruppn {group} markiern", + "Mention Mobile List": "De Handynumman-Listn markiern", + "domainExpiryDescription": "A Benachrichtigung raushaun, wenn de Domain-Nama oblaffn in:", + "Platform": "Plattform", + "Notify Channel": "Kanal benachrichtign", + "openModalTo": "Fensterl zum {0} aufmacha", + "Add a domain": "A Domain dazua-doa", + "Uptime Kuma URL": "An Kanal benachrichtign", + "setup a new monitor group": "A neie Aufbasser-Gruppn olegn", + "Actions": "Aktiona", + "selectedMonitorCountMsg": "ausgwählt: {n} | ausgwählt: {n}", + "selectAllMonitorsAria": "Alle Aufbasser auswähln", + "selectMonitorMsg": "Suach da de Aufbasser raus, mit dene du wos macha wuist", + "deselectAllMonitorsAria": "Olle Aufbasser wieda abwahlgn", + "OptionalParameters": "Extra-Parameter (Optional Parameters)", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Weng de Einschränkungen vom Provider eitn de extra Variabln auf dei eigne Kappe – ko sei, dass de Nachricht sonst ned ankimmt", + "SecretKey": "Geheimschlissl (SecretKey)", + "Topic": "Rubrik", + "Feishu WebHookUrl": "Feishu-WebHook-URL", + "smseagleContact": "Telefonbuach-Kontaktnam", + "halopsa_setup_step4": "Nimm de Standard-Anmeldung (Basic Authentication) und leg an Benutzernam und a Passwort o. Schreib oda kopier de Daten dann obn in de Test-Fejder nei", + "discordSuppressNotificationsHelptext": "Wenn des eigschalt is, wern de Nachrichtn zwar im Kanal anzoagt, aber de Leit kriagn koane Push-Meldungen oda Hiweise aufm Desktop.", + "Only retry if status code check fails": "Bloß wiedahoin, wenn da Status-Code nimma basst", + "retryOnlyOnStatusCodeFailureDescription": "Wennst des eischaltest, dann probiert a's bloß dann nomoi, wenn da HTTP-Status-Code in d'Binsn geht (z.B. wenn da Server eiganga is). Wenn da Status-Code basst, aber de JSON-Abfrag an Schmarrn liefert, dann werd da Monitor sofort ois \"Down\" markiert, ohne dass a's nomoi vasuacht.", + "bulkDeleteErrorMsg": "{n} Aufbasser ham se ned löschen lassn | {n} Aufbasser ham se ned löschen lassn", + "You can divide numbers with commas or semicolons": "Du konnst de Zahlen mit am {comma} oda am {semicolon} auseinanderhoidn", + "AccessKeyId": "AccessKey ID (Zugaungsschlissl-Numma)", + "aliyun-template-optional-parameters": "Extra-Parameter (wennst magst): {parameters}", + "WebHookUrl": "WebHook-URL", + "Mentioning": "Leit markiern (Mentioning)", + "Don't mention people": "Lass de Leit in Ruah und markier koan (Don't mention people)", + "Enter a list of userId": "Haug do a Listn mit de User-IDs nei", + "alertaApiKey": "API-Schlissl", + "Integration Key": "Integrations-Schlissl", + "alertaAlertState": "Alarm-Zustand", + "smseagleGroupV2": "Telefonbuach-Gruppn-ID(s)", + "smseagleDocs": "Schau in d'Dokumentation oda ob d'APIv2 scho hergeht: {0}", + "smseagleGroup": "Telefonbuach-Gruppnnam", + "Please enter a valid OID.": "Gib bittschön a gültige OID ei.", + "versionIs": "Version: {version}", + "lastUpdatedAt": "Z'letzt aktualisiert am: {date}", + "createdAt": "Erstellt am: {date}", + "logoutCurrentUser": "Ois {username} vaabschiedn", + "lastUpdatedAtFromNow": "Z'letzt aktualisiert am: {date} ({fromNow})", + "Certificate Chain:": "Zertifikats-Kettn:", + "dateCreatedAtFromNow": "Erstellt am: {date} ({fromNow})", + "Examples:": "Beispui: {0}", + "frontendVersionIs": "Frontend-Version: {version}", + "cronScheduleDescription": "Zeitplan: {description}", + "deleteMonitorsMsg": "Bist da ganz sicher, dass de ausgwähltn Aufbasser olle glöscht wern soin?", + "noMonitorsPausedMsg": "Koa Aufbasser pausiert (is eh koana glaufn)", + "pausedMonitorsMsg": "{n} Aufbasser pausiert | {n} Aufbasser pausiert", + "noMonitorsResumedMsg": "Koa Aufbasser wieda agworfa (war koana untabrecha)", + "resumedMonitorsMsg": "{n} Aufbasser wieda agworfa | {n} Aufbasser wieda agworfa", + "deletedMonitorsMsg": "{n} Aufbasser glöscht | {n} Aufbasser glöscht", + "cacheBusterParamDescription": "A zuafällig erstellter Parameter, damit da Zwischenspeicher (Cache) übaganga werd.", + "wayToGetThreemaGateway": "Du konnst di durt fias Threema Gateway oitradln {0}.", + "Custom sound to override default notification sound": "A eigna Sound, damit ma an andern Ton heart ois wia normal", + "Optional: Space separated list of scopes": "Optional: A Listn vo Bereiche (Scopes), mit Leerzeichn trennt", + "pingCountDescription": "Wia vui Paketl gschickt wern soin, bevor aufgheart werd", + "domain_expiry_unsupported_is_icann": "De Domain \"{domain}\" konn ma ned auf as Oblaufdatum ibapriafn, weil de Endung \".{publicSuffix}\" koa offiziellerICANN-Standard is", + "halopsa_webhook_url_desc": "Haug do de Webhook-URL vo deim Halo PSA Integration Runbook nei (Eistellunga > Integrationa > Eigene Integrationa > Integration Runbooks). Wähl \"Kon nur vo Halo und vo am öffentlichn Endpunkt gstart wern\" aus, wennst den Webhook olegst.", + "documentation": "Dokumentation", + "smtpDkimheaderFieldNames": "Header-Schlissl zum Signiern (wennst magst)", + "smtpDkimskipFields": "Header-Schlissl, de ned signiert wern soin (wennst magst)", + "Integration URL": "Integrations-URL", + "Auto resolve or acknowledged": "Automatisch erledign oda bestätign", + "smseagleContactV2": "Telefonbuach-Kontakt-ID(s)", + "Enter the list of nodes": "Schreib de Listn vo de RabbitMQ-Verwaltungsknotn (Nodes) do nei", + "screenshotDelayDescription": "Wennst magst, konnst a paar Millisekundn wartn, bevor as Buidl (Screenshot) gmocht werd. Maximal san {maxValueMs}ms drin (oiso de Hälfte vom Intervall).", + "screenshotDelayWarning": "Hechane Werte lassn den Browser länger offn. Des ko dazua fiahrn, dass da Arbatsspeicher (RAM) ziemlich voi werd, wenn vui Aufbasser gleichzeitig laffn.", + "Font Twemoji by Twitter licensed under": "Schriftort Twemoji vo Twitter, lizenziert unta", + "PhoneNumbers": "Telefonnumman", + "SecretAccessKey": "AccessKey Secret (Geheimschlissl)", + "alertaRecoverState": "Wieda-guat-Zustand", + "serwersmsAPIUser": "API-Benutzernam (inkl. \"webapi_\" am Anfang)", + "smseagleRecipientType": "Empfänger-Typ", + "FlashDuty Push URL Placeholder": "Kopier des vo da Alarm-Integrations-Seitn weg", + "successAuthChangePassword": "S'Passwort is erfolgreich aktualisiert worn.", + "Lost connection to the socket server.": "D'Vabindung zum Socket-Server is abgrissn.", + "onebotSafetyTips": "Aus Sicherheitsgrindn muaß a Access-Token festglegt wern", + "OneChatUserIdOrGroupId": "OneChat Benutzer-ID oda Gruppn-ID", + "Enter the list of brokers": "Schreib de Listn vo de Broker nei", + "Press Enter to add broker": "Pro Zeiln bloß oa Relay-URL", + "brevoApiHelp": "Do konnst an API-Schlissl erstoin: {0}", + "enableSSL": "SSL/TLS eischaltn", + "mariadbUseSSLHelptext": "Haug des nei, damitst a verschlisslte Leitung zu deina Datenbank host. Des brauchst für de meistn Cloud-Gschichtn eh unbedingt.", + "mariadbCaCertificateLabel": "CA Zertifikat", + "mariadbCaCertificateHelptext": "Klatsch do des CA-Zertifikat im PEM-Format nei, wennst da dein Zeigl söm unterschriebn host. Loß des Föid leer, wenn dei Datenbank a offiziells Zertifikat nutzt.", + "alertaApiEndpoint": "API-Schnittstelln", + "nostrRecipientsHelp": "npub-Format, bloß oana pro Zeiln", + "What is a Remote Browser?": "Was is'n a Remote-Browser?", + "days": "{n} Dog | {n} Dog", + "hours": "{n} Stund | {n} Stundn", + "minutes": "{n} Minutn | {n} Minutn", + "minuteShort": "{n} Min | {n} Min", + "years": "{n} Joahr | {n} Joahr", + "TemplateCode": "Vorlag-Code (TemplateCode)", + "Enter a list of mobile": "Haug do a Listn mit de Handynumman nei", + "Proxy server has authentication": "Da Proxy-Server braucht a Anmeldung", + "Remove domain": "Domain '{0}' nausschmeißen", + "Channel Name": "Kanal-Nam", + "Please set start time first": "Gib bittschön zerst de Startzeit o", + "smtpDkimDomain": "Domain-Nam", + "smtpDkimPrivateKey": "Privater Schlissl (Private Key)", + "do nothing": "Gar nix doa", + "alertaEnvironment": "System-Umfeld", + "smseagleTo": "Telefonnumman", + "twilioAuthToken": "Auth-Token / API-Schlissl-Gheimnis (Secret)", + "Cannot connect to the socket server.": "Mehrane Email-Adressn bitte mit am Komma trenna.", + "Enable Kafka Producer Auto Topic Creation": "Eischaltn, dass da Kafka-Producer automatisch Themen (Topics) erstoin deaf", + "Badge value (For Testing only.)": "Badge-Wert (bloß zum Testn)", + "less than or equal to": "gleiner oda gleich", + "Alphanumerical string and hyphens only": "Bloß Buachstobm, Zoin und Bindestriche san erlaubt", + "Copy the web app URL and paste it above": "Kopier d'URL und pappe s'obm nei", + "Failed to copy to clipboard": "Hot ned klappt mit da Zwischenablag", + "serwersmsRecipientType": "Empfängertyp", + "smseagleApiv2": "API v2", + "legacyOctopushEndpoint": "Oida Octopush Endpunkt", + "PushDeer Server": "PushDeer Server", + "Badge Warn Color": "Warnfarb", + "useRemoteBrowser": "Remote-Browser hernehma", + "Command": "Befehl", + "Alphanumeric (recommended)": "Buchstaben & Zahlen (besser)", + "Private Number": "Private Nummer", + "SIGNL4": "SIGNL4", + "Bubble": "Blaserl", + "Pop": "Pop-Musi", + "Umami": "Umami", + "account settings": "Konto-Einstellungen", + "Browser not supported": "Dein Browser mog nimmer", + "labelDomainExpiry": "Domain laft o.", + "labelDomainNameExpiryNotification": "Meldung wenn Domain o-laft", + "Halo PSA Webhook URL": "Halo PSA Webhook URL", + "Uptime Kuma": "Uptime Kuma", + "Details": "Details", + "GlobalpingDescription": "Globalping gibt da Zuagriff auf tausende Proben aus da Community, damitst Netzwerktests und Messungen macha konnst. Fia olle, de anonym untawegs san, is bei 250 Tests pro Stund Schluss. Wennst des Ganze auf 500 vadoppeln wuist, dann speicher dein Token in de {accountSettings}.", + "GlobalpingHostname": "A Messziel, des ma öffentlich erreichen ko. Meistens a Hostnam oda a IPv4/IPv6-Adress, je nachdem, wos ma grod messn wui.", + "GlobalpingIpFamilyInfo": "De IP-Version, de hergnumma wern soi. Geht bloß, wenn des Ziel a Hostnam is.", + "Pinned incidents are shown prominently on the status page": "Opegge Gschichten sigst glei ganz oben auf da Statusseitn", + "templateAvailableVariables": "Variablen de hernehma konnst", + "Please input content": "Schreib wos nei", + "Cloud ID": "Cloud ID", + "API Token": "API Token", + "See Jira Cloud Docs": "Schau in d'Jira Cloud Doku", + "disableSTARTTLSDescription": "Schalt des ei fia SMTP-Server, de koa STARTTLS ned kenna. Dann wern d'E-Mails ower iwa a unverschlüsselte Verbindung verschickt.", + "Google Apps Script Webhook URL": "Google Apps Script Webhook URL", + "Paste the script code (see below)": "Script-Code (unten) neikopiern", + "Click Deploy → New deployment → Web app": "Druck auf Bereitstellen → Neue Bereitstellung → Web-App", + "Google Apps Script Code": "Google Apps Script Code", + "Copy to Clipboard": "In d'Zwischenablag kopiern", + "smseagleMsgSms": "SMS", + "smseagleMsgTts": "Text-to-Speech", + "smseagleDuration": "Dauer", + "octopushEndpoint": "Octopush Endpunkt", + "onebotMessageType": "Nachrichtentyp", + "PushDeer Key": "PushDeer Key", + "Add API Key": "API Key dazuahama", + "apiKey-expired": "Oglaffn", + "apiKey-inactive": "Inaktiv", + "ntfyUsernameAndPassword": "Nutzer & Passwort", + "ntfyUseTemplateDescription": "Nimm a Vorlag anstatt ois händisch zum schreibn", + "ntfyCustomTitle": "Eigner Titl", + "ntfyCustomMessage": "Eigne Nachricht", + "twilioApiKey": "Twilio API Key", + "twilioFromNumber": "Vo da Nummer", + "twilioToNumber": "An de Nummer", + "Monitor Setting": "Monitor Einstellungen", + "Badge Color": "Badge Farb", + "Badge Label Prefix": "Beschriftungs-Präfix", + "Badge URL": "Badge URL", + "Group": "Gruppn", + "Monitor Group": "Monitor Gruppn", + "Enable Kafka SSL": "Kafka SSL eischalten", + "Remote Browser not found!": "Remote-Browser ned gfundn!", + "self-hosted container": "Eigner Container (self-hosted)", + "max 11 alphanumeric characters": "maximal 11 Zeichen", + "SNMP Version": "SNMP Version", + "threemaRecipientTypeIdentityFormat": "Threema ID Format", + "threemaApiAuthenticationSecret": "API Secret", + "not starts with": "fangt ned o mit", + "wayToGetWahaApiUrl": "Dei WAHA-Instanz-URL.", + "screenshot of the website": "Foto vo da Webseitn", + "Basic checkbox toggle button group": "Checkbox Gruppn", + "mtls-auth-server-cert-label": "Server Zertifikat", + "mtls-auth-server-cert-placeholder": "Zertifikat neibabba", + "mtls-auth-server-key-label": "Server Key", + "mtls-auth-server-ca-placeholder": "CA neibabba", + "avgPing": "Durchschnitt", + "maxPing": "Maximal", + "minPing": "Minimal", + "Setup Instructions": "Anleitung", + "imageResetConfirmation": "Buidl is wieda auf Standard zruckgsetzt", + "Incident description": "Beschreibung vom Vorfall", + "Halo PSA": "Halo PSA", + "Resolved": "Basst wieder", + "deleteIncidentMsg": "Wuist den Vorfall echt leschn?", + "threemaRecipientTypeIdentity": "Threema ID", + "slug is not found": "Den Slug gibts ned", + "Please input title": "Gib a Iwaschrift ei", + "Collapse All Groups": "Alle Gruppn zumacha", + "threemaSenderIdentity": "Absender ID", + "Disable STARTTLS": "STARTTLS ausschoitn", + "TLS Alert Spec": "TLS Warnung Spezifikation", + "Deploy a Google Apps Script as a web app and paste the URL here": "Mach a Web-App aus deim Script und pappe d'URL do nei", + "Quick Setup Guide": "Kurze Anleitung", + "Open your Google Spreadsheet": "Mach dei Google Spreadsheet auf", + "Go to Extensions → Apps Script": "Geh auf Erweiterungen → Apps Script", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Stell ei 'Ausführen ois: I' und 'Zugriff: Jeder'", + "Copied to clipboard!": "Is drin in da Zwischenablag!", + "Region": "Region", + "SSL/TLS": "SSL/TLS", + "aboutJiraCloudId": "Iwa d'Jira Cloud ID", + "see Jira Cloud Docs": "schau in d'Jira Cloud Doku", + "mariadbSocketPathDetectedHelptext": "I verbind mi grod mit da Datenbank, so wia's in da {0} Umgebungsvariabln drin steht.", + "serwersmsAPIPassword": "SMS-Passwort", + "serwersmsPhoneNumber": "Handynummer", + "smseagleToken": "SMSEagle Token", + "smseagleUrl": "SMSEagle URL", + "smseagleEncoding": "Kodierung", + "smseagleMsgType": "Nachrichtentyp", + "smseagleMsgRing": "Olaitn (Ring)", + "smseagleMsgTtsAdvanced": "Text-to-Speech (besser)", + "smseagleTtsModel": "TTS Modell", + "smseagleApiType": "API Typ", + "smspartnerPhoneNumber": "Handynummer", + "smspartnerSenderName": "Absender-Nam", + "Recipient Number": "Nummer vom Empfänger", + "From Name/Number": "Vo wem (Nam/Nummer)", + "Octopush API Version": "Octopush API Version", + "ntfy Topic": "ntfy Thema", + "onebotHttpAddress": "OneBot Adress", + "Google": "Google", + "onebotGroupMessage": "Gruppennachricht", + "onebotPrivateMessage": "Privatnachricht", + "onebotUserOrGroupId": "User oder Gruppen ID", + "Custom Monitor Type": "Eigner Monitortyp", + "Google Analytics ID": "Google Analytics ID", + "Analytics Type": "Analytics Typ", + "Analytics ID": "Analytics ID", + "Edit Tag": "Tag bearwan", + "Server Address": "Server Adress", + "Learn More": "Mehr wissn", + "Body Encoding": "Body Kodierung", + "Expiry": "Ablauf", + "Continue": "Weiter gehts", + "Add Another": "No oans dazu", + "Key Added": "Key is drin", + "No API Keys": "Koane API Keys do", + "apiKey-active": "Aktiv", + "Expires": "Laft o am", + "Generate": "Eizeign", + "pagertreeIntegrationUrl": "PagerTree URL", + "pagertreeUrgency": "Dringlichkeit", + "pagertreeSilent": "Stui", + "pagertreeLow": "Niedrig", + "pagertreeMedium": "Mittel", + "pagertreeHigh": "Hoch", + "pagertreeCritical": "Kritisch", + "pagertreeResolve": "Erledign", + "pagertreeDoNothing": "Nix doa", + "lunaseaDeviceID": "Geräte ID", + "lunaseaUserID": "User ID", + "ntfyAuthenticationMethod": "Anmeldung", + "ntfyPriorityDown": "Priorität wenns foid", + "ntfyCall": "Oruaf", + "ntfyUseTemplate": "Vorlag hernehma", + "OAuth2: Client Credentials": "OAuth2: Client Credentials", + "ntfyNotificationTemplateFallback": "Ersatz-Vorlag", + "Plausible": "Plausible", + "twilioAccountSID": "Twilio Account SID", + "twilioMessagingServiceSID": "Twilio Messaging Service SID", + "Show Clickable Link": "Link zum Drucka ozoign", + "Open Badge Link Generator": "Badge-Link Generator aufmacha", + "Badge Link Generator": "Badge-Link Generator", + "Badge Type": "Badge Typ", + "Badge Duration (in hours)": "Badge Dauer (in Stund)", + "Badge Label": "Badge Beschriftung", + "Badge Prefix": "Badge Präfix", + "Badge Suffix": "Badge Suffix", + "Badge Preview": "Vorschau", + "Badge Label Suffix": "Beschriftungs-Suffix", + "Badge Up Color": "Farb wenns laft", + "Badge Down Color": "Farb wenns foid", + "Badge Pending Color": "Farb beim Warten", + "Badge Maintenance Color": "Farb bei Wartung", + "Badge Warn Days": "Warnung in Tagen", + "Badge Down Days": "Fehler in Tagen", + "Badge Style": "Stil", + "monitorToastMessagesLabel": "Meldungen ozoign", + "toastErrorTimeout": "Fehler-Meldung Dauer", + "toastSuccessTimeout": "Erfolgs-Meldung Dauer", + "Kafka Brokers": "Kafka Brokers", + "Kafka Topic Name": "Kafka Thema", + "Kafka Producer Message": "Kafka Nachricht", + "Kafka SASL Options": "Kafka SASL Optionen", + "Mechanism": "Mechanismus", + "Pick a SASL Mechanism...": "Suach da an SASL Mechanismus aus…", + "AccessKey Id": "AccessKey Id", + "Secret AccessKey": "Secret AccessKey", + "Session Token": "Session Token", + "Request Body": "Request Body", + "HTTP Method": "HTTP Methode", + "FlashDuty Severity": "FlashDuty Schweregrad", + "FlashDuty Push URL": "FlashDuty Push URL", + "No incidents recorded": "Nix passiert bisher", + "Load More": "No mehr lodn", + "Loading...": "I lod grod...", + "Pin this incident": "De Gschicht oben opegga", + "Incident not found or access denied": "Nix gfundn oder du derfst da des ned oschaugn", + "Past Incidents": "Des is scho gwen", + "Incident title": "Iwaschrift", + "nostrRelays": "Nostr Relays", + "Edit Incident": "An Vorfall bearwan", + "Resolve": "Erledign", + "nostrSender": "Nostr Absender", + "nostrRecipients": "Nostr Empfänger", + "showCertificateExpiry": "Zertifikatsablauf ozoign", + "showOnlyLastHeartbeat": "Nua n'letztn Herzschlag ozoign", + "noOrBadCertificate": "Zertifikat is hi oder ned do", + "cacheBusterParam": "Cache-Buster Parameter", + "gamedigGuessPort": "Port ratn (GameDig)", + "Message format": "Nachrichtnformat", + "Send rich messages": "Scheene Nachrichtn schicka", + "Matomo": "Matomo", + "Bitrix24 Webhook URL": "Bitrix24 Webhook URL", + "Saved.": "Gspeichert.", + "authInvalidToken": "Token is nix wert.", + "authIncorrectCreds": "Anmeldung hod ned basst.", + "2faAlreadyEnabled": "2FA is scho o.", + "2faEnabled": "2FA is hiaz o.", + "2faDisabled": "2FA is hiaz aus.", + "successAdded": "Basst, hamma dazuado.", + "successResumed": "Geht wieda weiter.", + "successPaused": "Hamma hiaz pausiert.", + "successDeleted": "Hamma glöscht.", + "successEdited": "Hamma gändert.", + "successBackupRestored": "Backup is wieda do.", + "successDisabled": "Hamma ausgschoit.", + "successEnabled": "Hamma eigschait.", + "foundChromiumVersion": "Chromium Version gfundn", + "Remote Browsers": "Andere Browser (Remote)", + "Remote Browser": "Anderer Browser", + "Add a Remote Browser": "An Remote-Browser dazuado", + "Screenshot Delay": "Warten beim Foto", + "GrafanaOncallUrl": "Grafana Oncall URL", + "systemServiceName": "Dienst Nam", + "systemServiceExpectedOutput": "Wos mir erwarten", + "Browser Screenshot": "Browser Foto", + "senderSevenIO": "Absender (seven.io)", + "receiverSevenIO": "Empfänger (seven.io)", + "apiKeySevenIO": "API Key (seven.io)", + "API URL": "API URL", + "evolutionInstanceName": "Evolution Instanz", + "documentationOf": "Doku vo", + "To Phone Number": "An d'Handynummer", + "Close": "Zuamacha", + "smscTranslit": "SMSC Transliteration", + "Originator type": "Absendertyp", + "Telephone number": "Telefonsnummer", + "Originator": "Absender", + "Destination": "Ziel", + "Allow Long SMS": "Lange SMS erlaubn", + "max 15 digits": "maximal 15 Ziffern", + "Community String": "Community String", + "OID (Object Identifier)": "OID (Object Identifier)", + "snmpV3Username": "SNMP v3 Nutzer", + "Condition": "Bedingung", + "threemaRecipient": "Threema Empfänger", + "threemaRecipientType": "Threema Empfängertyp", + "threemaRecipientTypePhone": "Handynummer", + "threemaRecipientTypePhoneFormat": "Nummern-Format", + "threemaRecipientTypeEmail": "E-Mail", + "Host Onesender": "Onesender Host", + "Token Onesender": "Onesender Token", + "lunaseaTarget": "LunaSea Ziel", + "Recipient Type": "Empfängertyp", + "tagNotFound": "Tag ned gfundn.", + "mtls-auth-server-ca-label": "Server CA", + "Group ID": "Gruppen ID", + "Add Remote Browser": "Remote-Browser dazuado", + "New Group": "Neie Gruppn", + "Group Name": "Gruppn Nam", + "Authentication Method": "Art vo da Anmeldung", + "Authorization Header": "Authorization Header", + "Form Data Body": "Form Data Body", + "OAuth Token URL": "OAuth Token URL", + "Client ID": "Client ID", + "OAuth Scope": "OAuth Scope", + "OAuth Audience": "OAuth Audience", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL", + "Conditions": "Bedingungen", + "conditionAdd": "Bedingung dazuado", + "conditionDelete": "Bedingung weghama", + "conditionAddGroup": "Bedingungsgruppn dazuado", + "conditionDeleteGroup": "Bedingungsgruppn weghama", + "conditionValuePlaceholder": "Wert", + "equals": "is gleich", + "not equals": "is ned gleich", + "contains": "is drin", + "not contains": "is ned drin", + "starts with": "fangt o mit", + "ends with": "hert auf mit", + "not ends with": "hert ned auf mit", + "greater than": "mehr ois", + "record": "Eintrag", + "message": "Nachricht", + "json_value": "JSON Wert", + "Notification Channel": "Benachrichtigungskanal", + "Sound": "Tusch", + "Correct": "Richtig", + "Fail": "Hi", + "Harp": "Harfn", + "Reveal": "Aufdegga", + "Doorbell": "Haustür", + "Flute": "Flöitn", + "Money": "Gid", + "Scifi": "SciFi", + "Clear": "Sauber macha", + "Select All": "Ois auswähln", + "Deselect All": "Ois weg", + "Elevator": "Aufzug", + "Guitar": "Gitar", + "Time Sensitive (iOS Only)": "Eilig (nua iOS)", + "From": "Vo", + "Can be found on:": "Findst auf: {0}", + "RabbitMQ Nodes": "RabbitMQ Knoten", + "Press Enter to add node": "Enter drucka zum dazuado", + "RabbitMQ Username": "RabbitMQ Nutzer", + "RabbitMQ Password": "RabbitMQ Passwort", + "SendGrid API Key": "SendGrid API Key", + "brevoApiKey": "Brevo API Key", + "brevoFromEmail": "Brevo Absender E-Mail", + "brevoFromName": "Brevo Absender Nam", + "brevoLeaveBlankForDefaultName": "Leer lassn fia Standard-Nam", + "brevoToEmail": "Brevo Empfänger E-Mail", + "brevoCcEmail": "Brevo CC E-Mail", + "brevoBccEmail": "Brevo BCC E-Mail", + "brevoSubject": "Brevo Betreff", + "brevoLeaveBlankForDefaultSubject": "Leer lassn fia Standard-Betreff", + "resendApiKey": "Resend API Key", + "resendFromName": "Resend Absender Nam", + "resendFromEmail": "Resend Absender E-Mail", + "resendLeaveBlankForDefaultName": "Leer lassn fia Standard-Nam", + "resendLeaveBlankForDefaultSubject": "Leer lassn fia Standard-Betreff", + "resendToEmail": "Resend Empfänger E-Mail", + "resendSubject": "Resend Betreff", + "pingCountLabel": "Ping Anzahl", + "pingNumericLabel": "Ping Wert", + "pingGlobalTimeoutLabel": "Gesamt-Timeout", + "pingPerRequestTimeoutLabel": "Timeout pro Request", + "Custom URL": "Eigne URL", + "OneChatAccessToken": "OneChat Access Token", + "OneChatBotId": "OneChat Bot ID", + "wahaSession": "WAHA Session", + "YZJ Webhook URL": "YZJ Webhook URL", + "YZJ Robot Token": "YZJ Robot Token", + "Plain Text": "Nur Text", + "Message Template": "Nachrichtnvorlag", + "Template Format": "Vorlag-Format", + "smsplanetApiToken": "SMSPlanet Token", + "the smsplanet documentation": "d'SMSPlanet Doku", + "Phone numbers": "Handynummern", + "Sender name": "Absender Nam", + "Disable URL in Notification": "Link in Benachrichtigung weglaffn", + "Suppress Notifications": "Benachrichtigungen unterdrucka", + "Ip Family": "IP Familie", + "Happy Eyeballs algorithm": "Happy Eyeballs Algorithmus", + "Add Another Tag": "No a Tag dazuado", + "Staged Tags for Batch Add": "Tags fia d'Massenauswahl", + "Clear Form": "Formular laarmacha", + "Nextcloud host": "Nextcloud Host", + "Conversation token": "Gesprächs-Token", + "Bot secret": "Bot Gheimnis", + "Send UP silently": "Stui meldn wenns wieda geht", + "Send DOWN silently": "Stui meldn wenns hi is", + "Globalping - Access global monitoring probes": "Globalping - Weltweite Proben", + "Globalping API Token": "Globalping API Token", + "globalpingApiTokenDescription": "Hoi da dein Globalping API Token auf {0}.", + "GlobalpingLocation": "In des Feld fia'n Ort konnst Kontinente, Landa, Regionen, Städt, ASNs, ISPs oda Cloud-Regionen neischreibn. Du konnst de Filter mit {plus} kombiniern (z. B. {amazonPlusGermany} oda {comcastPlusCalifornia}). Wenn da d'Latenz wichtig is, dann schau, dass d'Region recht kloa gwählt is, damitst koane Sprünge drin host. {fullDocs}.", + "GlobalpingLocationDocs": "Globalping Ort-Doku", + "GlobalpingResolverInfo": "A IPv4/IPv6-Adress oda a vollständig qualifizierter Domainnam (FQDN). Standardmäßig werd da lokale Resolver vo da Probe hergnumma. Den Resolver-Server konnst owa jedazeit ändern.", + "Protocol": "Protokoll", + "Location": "Ort", + "Monitor Subtype": "Monitor Untertyp", + "Check for": "Suach noch", + "Maximum Retries": "Maximale Versuche", + "Template ID": "Vorlag ID", + "Recipient Numbers": "Empfängernummern", + "Allow Notifications": "Meldungen erlaubn", + "settingsDomainExpiry": "Domain-Ablauf Einstellungen", + "Expand All Groups": "Alle Gruppn aufmacha", + "Jira Service Management": "Jira Service Management", + "password": "Passwort", + "Basic radio toggle button group": "Radio Button Gruppn", + "mtls-auth-server-key-placeholder": "Key neibabba", + "Sort by status": "Nach Status", + "Sort by uptime": "Nach Uptime", + "Message Format": "Nachrichtnformat", + "PushDeer Server URL": "PushDeer Server URL", + "Service Name": "Dienst Nam", + "TLS Alerts": "TLS Warnungen", + "example": "Beispui", + "None (Successful Connection)": "Nix (Verbindung basst)", + "Result": "Ergebnis", + "SpugPush Template Code": "SpugPush Vorlagencode", + "API Keys": "API Keys", + "Expiry date": "Ablaufdatum", + "Don't expire": "Laft ned o", + "milliseconds": "Millisekundn", + "systemService": "System Dienst", + "systemServiceCommandHint": "Befehlshilf", + "Sort options": "Sortiern", + "Sort by name": "Nach Nam", + "Sort by certificate expiry": "Nach Zertifikats-Ende", + "Splunk Rest URL": "Splunk Rest URL", + "Severity": "Dringlichkeit", + "To Number": "An Nummer", + "GrafanaOncallURL": "Grafana Oncall URL", + "Never": "Niamois", + "Json Query": "JSON Abfrage", + "System Service": "System Dienst", + "playground": "Spuiwiesn", + "Check Type": "Check Typ", + "GRPC Options": "GRPC Optionen", + "Metadata": "Metadaten", + "End": "End", + "Endpoint": "Endpunkt", + "Expected TLS Alert": "TLS Warnung de ma erwarten", + "Analytics Script URL": "Analytics Script URL", + "Badge Label Color": "Beschriftungsfarb", + "Authorization Identity": "Identität", + "Client Secret": "Client Secret", + "No tags found.": "Koane Tags gfundn.", + "Arcade": "Arcade", + "less than": "weniger ois", + "Manual": "Händisch", + "Notifications Enabled": "Meldungen san o", + "username": "Nutzername", + "Clear current filters": "Filter rausdo", + "pause": "Pause" +} diff --git a/src/lang/be.json b/src/lang/be.json new file mode 100644 index 0000000..f05c6d1 --- /dev/null +++ b/src/lang/be.json @@ -0,0 +1,978 @@ +{ + "Edit": "Змяніць", + "-hour": "-гадзін", + "ignoreTLSErrorGeneral": "Ігнараваць памылку TLS/SSL для злучэння", + "pushOthers": "Іншыя", + "Yes": "Так", + "Show URI": "Паказаць URI", + "Tags": "Тэгі", + "Tag with this value already exist.": "Тэг з такім значэннем ужо існуе.", + "color": "Колер", + "value (optional)": "значэнне (неабавязкова)", + "Gray": "Шэры", + "Red": "Чырвоны", + "Orange": "Аранжавы", + "Search monitored sites": "Пошук адсочваемых сайтаў", + "Avg. Ping": "Сярэдні пінг", + "Body": "Цела", + "Headers": "Загалоўкі", + "Create Incident": "Стварыць інцыдэнт", + "Style": "Стыль", + "Proxies": "Проксі", + "default": "Па змаўчанні", + "enabled": "Уключана", + "setAsDefault": "Усталяваць па змаўчанні", + "Remove the expiry notification": "Выдаліць дату сканчэньня тэрміну дзеяння абвесткі", + "Refresh Interval": "Інтэрвал абнаўлення", + "Refresh Interval Description": "Старонка статусу будзе цалкам абнаўляць сайт кожныя {0} секунд", + "maintenanceStatus-ended": "Скончыўся(ліся)", + "Select message type": "Выберыце тып паведамлення", + "Create new forum post": "Стварыць новы пост", + "postToExistingThread": "Стварыць пост у гэтай галіне", + "forumPostName": "Назва паста", + "e.g. {discordThreadID}": "Напр. {discordThreadID}", + "Number": "Нумар", + "lineDevConsoleTo": "Кансоль распрацошчыкаў Line - {0}", + "recurringIntervalMessage": "Запускаць 1 раз кожны дзень | Запускаць 1 раз кожныя {0} дзён", + "affectedMonitorsDescription": "Выберыце маніторы, якія будуць затронутыя падчас тэхабслугоўвання", + "pushoversounds gamelan": "Гамелан", + "pushoversounds incoming": "Уваходны", + "pushoversounds climb": "Падым (доўгі)", + "wayToGetKookBotToken": "Стварыце праграму і атрымайце токен бота па адрасу {0}", + "Device": "Прылада", + "Huawei": "Huawei", + "Expiry date": "Дата сканчэння", + "Don't expire": "Не сканчаецца", + "Badge URL": "URL значка", + "nostrRelays": "Рэле Nostr", + "gamedigGuessPort": "Gamedig: Угадай порт", + "GrafanaOncallUrl": "URL-адрас Grafana Oncall", + "API URL": "API URL-адрас", + "Originator type": "Тып крыніцы", + "Destination": "Прызначэнне", + "languageName": "Беларуская", + "setupDatabaseChooseDatabase": "Якую базу даных вы хацелі б выкарыстоўваць?", + "setupDatabaseEmbeddedMariaDB": "Вам не трэба нічога наладжваць. У гэты Docker-вобраз аўтаматычна ўбудавана і наладжана MariaDB. Uptime Kuma будзе падключацца да гэтай базы даных праз unix-socket.", + "setupDatabaseMariaDB": "Падключыцца да знешняй базы даных MariaDB. Вам трэба задаць інфармацыю аб падлучэнні да базы даных.", + "setupDatabaseSQLite": "Просты файл базы даных, рэкамендуецца для невялікіх разгортванняў. Да версіі 2.0.0 Uptime Kuma выкарыстоўваў SQLite як базу даных па змаўчанні.", + "settingUpDatabaseMSG": "Настраиваем базу даных. Гэта можа заняць некаторы час, калі ласка, пачакайце.", + "dbName": "Назва базы даных", + "Settings": "Налады", + "Dashboard": "Панэль кіравання", + "Help": "Дапамога", + "New Update": "Даступна абнаўленне", + "Language": "Мова", + "Appearance": "Знешні выгляд", + "Theme": "Тэма", + "General": "Агульныя", + "Game": "Гульня", + "Primary Base URL": "Асноўны URL", + "Version": "Версія", + "Check Update On GitHub": "Праверыць абнаўленні ў GitHub", + "List": "Спіс", + "Home": "Галоўная", + "Add": "Дадаць", + "Add New Monitor": "Дадаць новы манітор", + "Quick Stats": "Статыстыка", + "Up": "Працуе", + "Down": "Не працуе", + "Pending": "У чаканні", + "statusMaintenance": "Тэхабслугоўванне", + "Maintenance": "Тэхабслугоўванне", + "Unknown": "Невядома", + "Cannot connect to the socket server": "Немагчыма падключыцца да сервера", + "Reconnecting...": "Падключэнне...", + "General Monitor Type": "Агульны Тып Манітора", + "Passive Monitor Type": "Пасіўны Тып Манітора", + "Specific Monitor Type": "Спецыфічны Тып Манітора", + "markdownSupported": "Падтрымліваецца сінтаксіс Markdown", + "pauseDashboardHome": "Паўза", + "Pause": "Паўза", + "Name": "Назва", + "Status": "Статус", + "DateTime": "Дата і час", + "Message": "Паведамленне", + "No important events": "Няма важных падзей", + "Resume": "Узнавіць", + "Delete": "Выдаліць", + "Current": "Бягучы", + "Uptime": "Час працы", + "Cert Exp.": "Сертыфікат сконч.", + "Monitor": "Манітор | Маніторы", + "day": "дзень | дзён", + "-day": "-дзён", + "hour": "гадзіна", + "Response": "Адказ", + "Ping": "Пінг", + "Monitor Type": "Тып манітора", + "Keyword": "Ключавое слова", + "Invert Keyword": "Інвертаваць ключавое слова", + "Friendly Name": "Назва", + "URL": "URL-спасылка", + "Hostname": "Адрас хоста", + "Expected Value": "Чаканае значэнне", + "Json Query": "JSON Запыт", + "Host URL": "URL Хоста", + "locally configured mail transfer agent": "Наладжаны лакальна агент перадачы паштовых паведамленняў", + "Port": "Порт", + "Heartbeat Interval": "Частата апытання", + "Request Timeout": "Тайм-Аут запыту", + "timeoutAfter": "Тайм-Аут праз {0} секундаў", + "Retries": "Спробы", + "Heartbeat Retry Interval": "Інтэрвал паўтору апытання", + "Resend Notification if Down X times consecutively": "Паўторная адпраўка абвесткі пры адключэнні некалькі раз", + "Advanced": "Дадаткова", + "checkEverySecond": "Праверка кожныя {0} секунд", + "retryCheckEverySecond": "Паўтараць кожныя {0} секунд", + "resendEveryXTimes": "Перасылаць кожныя {0} раз", + "resendDisabled": "Перасылка адключана", + "retriesDescription": "Максімальная колькасць спробаў перад адзнакай службы, як недаступная, і адпраўкай абвесткі", + "ignoreTLSError": "Ігнараваць памылкі TLS/SSL для HTTPS сайтаў", + "upsideDownModeDescription": "Змяніць статус службы на ПРАЦУЕ, калі яна даступная, а пазначаецца як НЕ ПРАЦУЕ.", + "maxRedirectDescription": "Максімальная колькасць перанакіраванняў. Пастаўце 0, каб адключыць перанакіраванні.", + "Upside Down Mode": "Рэжым змены статусу", + "Max. Redirects": "Макс. колькасць перанакіраванняў", + "Accepted Status Codes": "Дапушчальныя коды статуса", + "Push URL": "URL-спасылка пуш абвестак", + "needPushEvery": "Да гэтага URL неабходна звяртацца кожныя {0} секунд.", + "pushOptionalParams": "Неабавязковыя параметры: {0}", + "pushViewCode": "Як выкарыстоўваць манітор Push? (Паглядзець код)", + "programmingLanguages": "Мовы праграмавання", + "Save": "Захаваць", + "Notifications": "Апавяшчэнні", + "Not available, please setup.": "Апавяшчэнні недаступныя, патрабуецца налада.", + "Setup Notification": "Наладзіць апавяшчэнні", + "Light": "Светлая", + "Dark": "Цёмная", + "Auto": "Як у сістэме", + "Theme - Heartbeat Bar": "Тэма - радка частаты апытання", + "styleElapsedTime": "Мінулы час пад радком частаты апытання", + "styleElapsedTimeShowNoLine": "Паказаць (Без лініі)", + "styleElapsedTimeShowWithLine": "Паказаць (З лініяй)", + "Normal": "Звычайны", + "Bottom": "Унізе", + "None": "Адсутнічае", + "Timezone": "Часавы пояс TZ", + "Search Engine Visibility": "Бачнасць у пошукавых сістэмах", + "Allow indexing": "Дазволіць індэксацыю", + "Discourage search engines from indexing site": "Забараніць індэксацыю", + "Change Password": "Змяніць пароль", + "Current Password": "Бягучы пароль", + "New Password": "Новы пароль", + "Repeat New Password": "Паўтарыць новы пароль", + "Update Password": "Абнавіць пароль", + "Disable Auth": "Адключыць аўтарызацыю", + "Enable Auth": "Уключыць аўтарызацыю", + "disableauth.message1": "Вы ўпэўнены, што хочаце {disableAuth}?", + "disable authentication": "адключыць аўтарызацыю", + "disableauth.message2": "Гэта падыходзіць для {intendThirdPartyAuth} перад адкрыццём Uptime Kuma, такіх як Cloudflare Access, Authelia або іншыя.", + "where you intend to implement third-party authentication": "тых, у каго настроена старонняя сістэма аўтарызацыі", + "Please use this option carefully!": "Выкарыстоўвайце гэтую наладу асцярожна!", + "Logout": "Выйсці", + "Leave": "Пакінуць", + "I understand, please disable": "Я разумею, усё роўна адключыць", + "Confirm": "Пацвердзіць", + "No": "Не", + "Username": "Лагін", + "Password": "Пароль", + "Remember me": "Запомніць мяне", + "Login": "Уваход у сістэму", + "No Monitors, please": "Няма манітораў, калі ласка", + "add one": "дадаць", + "Notification Type": "Тып абвесткі", + "Email": "Электронная пошта", + "Test": "Тэст", + "Certificate Info": "Інфармацыя пра сертыфікат", + "Resolver Server": "DNS сервер", + "Resource Record Type": "Тып рэсурснай запісі", + "Last Result": "Апошні вынік", + "Create your admin account": "Стварыце акаўнт адміністратара", + "Repeat Password": "Паўтарыць пароль", + "Import Backup": "Імпартаваць Backup", + "Export Backup": "Спампаваць Backup", + "Export": "Экспарт", + "Import": "Імпарт", + "respTime": "Час адказу (мс)", + "notAvailableShort": "N/A", + "Default enabled": "Па змаўчанні ўключана", + "Apply on all existing monitors": "Ужыць да ўсіх існуючых манітораў", + "Create": "Стварыць", + "Clear Data": "Выдаліць даныя", + "Events": "Падзеі", + "Heartbeats": "Апытанні", + "Auto Get": "Аўта-атрыманне", + "Schedule maintenance": "Запланаваць тэхабслугоўванне", + "Affected Monitors": "Задзейнічаныя Маніторы", + "Pick Affected Monitors...": "Выберыце Задзейнічаныя Маніторы…", + "Start of maintenance": "Пачатак тэхабслугоўвання", + "All Status Pages": "Усе старонкі статусаў", + "Select status pages...": "Выберыце старонку статуса…", + "alertNoFile": "Выберыце файл для імпарту.", + "alertWrongFileType": "Выберыце JSON-файл.", + "Clear all statistics": "Ачысціць усю статыстыку", + "Skip existing": "Прапусціць існуючыя", + "Overwrite": "Перазапісаць", + "Options": "Опцыі", + "Keep both": "Пакінуць абодва", + "Verify Token": "Праверыць токен", + "Setup 2FA": "Налады 2FA", + "Enable 2FA": "Уключыць 2FA", + "Disable 2FA": "Адключыць 2FA", + "2FA Settings": "Налады 2FA", + "Two Factor Authentication": "Двухфактарная аўтэнтыфікацыя", + "filterActive": "Актыўны", + "filterActivePaused": "На паўзе", + "Active": "Актыўна", + "Inactive": "Неактыўна", + "Token": "Токен", + "Add New Tag": "Дадаць тэг", + "Add New below or Select...": "Дадаць новы або выбраць…", + "Tag with this name already exist.": "Тэг з такім імем ужо існуе.", + "Green": "Зялёны", + "Blue": "Сіні", + "Indigo": "Індыга", + "Purple": "Пурпуровы", + "Pink": "Ружовы", + "Custom": "Сваёродны", + "Search...": "Пошук…", + "Avg. Response": "Сярэдні адказ", + "Entry Page": "Галоўная", + "statusPageNothing": "Нічога няма, дадайце групу або манітор.", + "statusPageRefreshIn": "Абнаўленне праз: {0}", + "No Services": "Няма сэрвісаў", + "All Systems Operational": "Усе сістэмы працуюць", + "Partially Degraded Service": "Часткова працуючы сэрвіс", + "Degraded Service": "Пашкоджаная служба", + "Add Group": "Дадаць групу", + "Add a monitor": "Дадаць манітор", + "Edit Status Page": "Рэдагаваць старонку статусаў", + "Go to Dashboard": "Перайсці да панэлі кіравання", + "Status Page": "Старонка статуса", + "Status Pages": "Старонкі статуса", + "defaultNotificationName": "Абвесткі {notification} ({number})", + "here": "тут", + "Required": "Абавязкова", + "Post URL": "Post URL", + "Content Type": "Тып кантэнту", + "webhookJsonDesc": "{0} падыходзіць для любых сучасных HTTP-сервераў, напрыклад Express.js", + "webhookFormDataDesc": "{multipart} падыходзіць для PHP. JSON-вывад неабходна будзе апрацаваць з дапамогай {decodeFunction}", + "liquidIntroduction": "Шаблоннасьць дасягаецца з дапамогай мовы шаблонаў Liquid. Інструкцыі па выкарыстаньні прадстаўлены ў раздзеле {0}. Вось даступныя зменныя:", + "templateMsg": "паведамленне апавешчання", + "templateHeartbeatJSON": "аб'ект, які апісвае сігнал", + "templateMonitorJSON": "аб'ект, які апісвае манітор", + "templateLimitedToUpDownNotifications": "даступна толькі для апавешчанняў UP/DOWN", + "templateLimitedToUpDownCertNotifications": "даступна толькі для апавешчанняў UP/DOWN і аб заканчэньні тэрміну дзеяньня сертыфіката", + "webhookAdditionalHeadersTitle": "Дадатковыя Загалоўкі", + "webhookAdditionalHeadersDesc": "Устанаўлівае дадатковыя загалоўкі, якія адпраўляюцца з дапамогай вэб-хука. Кожны загаловак павінен быць вызначаны як JSON ключ/значэнне.", + "webhookBodyPresetOption": "Прэсет - {0}", + "webhookBodyCustomOption": "Карыстацкі аб'ект", + "Webhook URL": "URL вэбхука", + "Application Token": "Токен праграмы", + "Server URL": "URL сервера", + "Priority": "Прыярытэт", + "emojiCheatSheet": "Шпаргалка па Emoji: {0}", + "Read more": "Падрабязней", + "appriseInstalled": "Апавяшчэнне ўсталявана.", + "appriseNotInstalled": "Апавяшчэнне не ўсталявана. {0}", + "Method": "Метад", + "PushUrl": "URL пуша", + "HeadersInvalidFormat": "Загалоўкі запыту не з'яўляюцца валідным JSON: ", + "BodyInvalidFormat": "Цела запыту не з'яўляецца валідным JSON: ", + "Monitor History": "Гісторыя маніторынгу", + "clearDataOlderThan": "Захоўваць статыстыку за {0} дзён.", + "PasswordsDoNotMatch": "Паролі не супадаюць.", + "records": "запісы", + "One record": "Адзін запіс", + "steamApiKeyDescription": "Для маніторынгу гульнявога сервера Steam вам патрэбны Web-API ключ Steam. Зарэгістраваць яго можна тут: ", + "Current User": "Бягучы карыстальнік", + "topic": "Тэма", + "topicExplanation": "MQTT топік для маніторынгу", + "successKeyword": "Ключавое слова паспяховасці", + "successKeywordExplanation": "Ключавое слова MQTT, якое будзе лічыцца паспяховым", + "recent": "Апошняе", + "Reset Token": "Скід токена", + "Done": "Гатова", + "Info": "Інфа", + "Security": "Бяспека", + "Steam API Key": "Steam API-Ключ", + "Shrink Database": "Сціснуць базу даных", + "Pick a RR-Type...": "Выберыце RR-Тып…", + "Pick Accepted Status Codes...": "Выберыце прынятыя коды статуса…", + "Default": "Па змаўчанні", + "HTTP Options": "HTTP Опцыі", + "Title": "Назва інцыдэнту", + "Content": "Змест інцыдэнту", + "info": "ІНФА", + "warning": "УВАГА", + "danger": "ПАМЫЛКА", + "error": "памылка", + "critical": "крытычна", + "primary": "АСНОЎНЫ", + "light": "СВЕТЛЫ", + "dark": "ЦЁМНЫ", + "Post": "Апублікаваць", + "Please input title and content": "Калі ласка, увядзіце назву і змест", + "Created": "Створана", + "Last Updated": "Апошняе абнаўленне", + "Switch to Light Theme": "Светлая тэма", + "Switch to Dark Theme": "Цёмная тэма", + "Show Tags": "Паказаць тэгі", + "Hide Tags": "Схаваць тэгі", + "Description": "Апісанне", + "No monitors available.": "Няма даступных манітораў.", + "Add one": "Дадаць новы", + "No Monitors": "Маніторы адсутнічаюць", + "Untitled Group": "Група без назвы", + "Services": "Службы", + "Powered by": "Працуе на", + "Discard": "Скасаваць", + "Cancel": "Скасаваць", + "Select": "Выбраць", + "selectedMonitorCount": "Выбрана: {0}", + "Check/Uncheck": "Адзначыць/Зняць", + "shrinkDatabaseDescription": "Уключае VACUUM для базы даных SQLite. Калі ваша база даных была створана на версіі 1.10.0 і больш, AUTO_VACUUM ужо ўключаны і гэтае дзеянне не патрабуецца.", + "Customize": "Персаналізаваць", + "Custom Footer": "Карыстацкі footer", + "Custom CSS": "Карыстацкі CSS", + "enableProxyDescription": "Гэты проксі не будзе ўплываць на запыты манітора, пакуль ён не будзе актываваны. Вы можаце кантраляваць часовае адключэнне проксі для ўсіх манітораў праз статус актывацыі.", + "deleteStatusPageMsg": "Вы сапраўды хочаце выдаліць гэтую старонку статуса?", + "deleteProxyMsg": "Вы сапраўды хочаце выдаліць гэты проксі для ўсіх манітораў?", + "proxyDescription": "Проксі павінны быць прывязаныя да манітора, каб працаваць.", + "setAsDefaultProxyDescription": "Гэты проксі будзе па змаўчанні ўключаны для новых манітораў. Вы ўсё яшчэ можаце асобна адключаць проксі ў кожным маніторы.", + "Certificate Chain": "Ланцуг сертыфікатаў", + "Valid": "Дзейны", + "Invalid": "Нядзейсны", + "User": "Карыстальнік", + "Page Not Found": "Старонка не знойдзена", + "Installed": "Усталявана", + "Not installed": "Не ўсталявана", + "Running": "Працуе", + "Not running": "Не працуе", + "Remove Token": "Выдаліць токен", + "Start": "Пачаць", + "Stop": "Спыніць", + "Add New Status Page": "Дадаць старонку статуса", + "Slug": "Slug", + "Accept characters:": "Прымаць сімвалы:", + "startOrEndWithOnly": "Пачынаецца або заканчваецца толькі на {0}", + "No consecutive dashes": "Без паслядоўных тырэ", + "statusPageSpecialSlugDesc": "Спецыяльны значок {0}: гэтая старонка будзе адлюстроўвацца, калі значок не пазначаны", + "Next": "Далей", + "The slug is already taken. Please choose another slug.": "Гэты slug ужо заняты. Калі ласка, выберыце іншы slug.", + "No Proxy": "Без проксі", + "Authentication": "Аўтэнтыфікацыя", + "HTTP Basic Auth": "HTTP Аўтарызацыя", + "New Status Page": "Новая старонка статуса", + "Reverse Proxy": "Зваротны проксі", + "Backup": "Рэзервовая копія", + "About": "Аб праграме", + "wayToGetCloudflaredURL": "(Спампаваць cloudflared з {0})", + "cloudflareWebsite": "Вэб-сайт Cloudflare", + "Message:": "Паведамленне:", + "Don't know how to get the token? Please read the guide:": "Не ведаеце, як атрымаць токен? Калі ласка, прачытайце кіраўніцтва:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Бягучае злучэнне можа быць страчана, калі вы ў цяперашні час падключаецеся праз тунэль Cloudflare. Вы ўпэўнены, што хочаце яго спыніць? Увядзіце свой бягучы пароль, каб пацвердзіць гэта.", + "HTTP Headers": "Загалоўкі HTTP", + "Trust Proxy": "Давераны проксі", + "Other Software": "Іншае праграмнае забеспячэнне", + "For example: nginx, Apache and Traefik.": "Напрыклад: nginx, Apache і Traefik.", + "Please read": "Калі ласка, прачытайце", + "Subject:": "Тэма:", + "Valid To:": "Дзейсны да:", + "Days Remaining:": "Засталося дзён:", + "Issuer:": "Выдавец:", + "Fingerprint:": "Адбітак:", + "No status pages": "Няма старонак статуса", + "Domain Name Expiry Notification": "Абвестка пра сканчэнне тэрміну дзеяння даменнай назвы", + "Add a new expiry notification day": "Дадаць новы дзень абвесткі пра сканчэньне тэрміну дзеяння", + "Proxy": "Проксі", + "Date Created": "Дата стварэння", + "Footer Text": "Тэкст у ніжнім калонтытуле", + "Show Powered By": "Паказаць на чым створана", + "Domain Names": "Даменныя імёны", + "signedInDisp": "Вы ўвайшлі як {0}", + "signedInDispDisabled": "Аўтэнтыфікацыя адключана.", + "RadiusSecret": "Сакрэт Radius", + "RadiusSecretDescription": "Агульны сакрэт паміж кліентам і серверам", + "RadiusCalledStationId": "Ідэнтыфікатар вызываемай станцыі", + "RadiusCalledStationIdDescription": "Ідэнтыфікатар вызываемага прылады", + "RadiusCallingStationId": "Ідэнтыфікатар вызывальніка станцыі", + "RadiusCallingStationIdDescription": "Ідэнтыфікатар вызывальніка прылады", + "Certificate Expiry Notification": "Абвестка пра сканчэнне тэрміну дзеяння сертыфіката", + "API Username": "Імя карыстальніка API", + "API Key": "API ключ", + "Show update if available": "Паказваць даступныя абнаўленні", + "Also check beta release": "Праверыць абнаўленні для бета версій", + "Using a Reverse Proxy?": "Выкарыстоўваеце зваротны проксі?", + "Check how to config it for WebSocket": "Праверце, як наладзіць яго для WebSocket", + "Steam Game Server": "Гульнявы сервер Steam", + "Most likely causes:": "Найбольш верагодныя прычыны:", + "The resource is no longer available.": "Рэсурс больш не даступны.", + "There might be a typing error in the address.": "У адрасе можа быць памылка ў друку.", + "What you can try:": "Што вы можаце паспрабаваць:", + "Retype the address.": "Паўтарыце адрас.", + "Go back to the previous page.": "Вярнуцца на папярэднюю старонку.", + "Coming Soon": "Хутка", + "Connection String": "Радок падлучэння", + "Query": "Запыт", + "settingsCertificateExpiry": "Сканчэнне TLS сертыфіката", + "certificationExpiryDescription": "HTTPS Маніторы ініцыююць абвестку, калі срок дзеяння сертыфіката TLS скончыцца:", + "Setup Docker Host": "Налада Docker Host", + "Connection Type": "Тып злучэння", + "Docker Daemon": "Дэман Docker", + "noDockerHostMsg": "Не даступна. Спачатку наладзце хост Docker.", + "DockerHostRequired": "Усталюйце хост Docker для гэтага манітора.", + "deleteDockerHostMsg": "Вы сапраўды хочаце выдаліць гэты вузел docker для ўсіх манітораў?", + "socket": "Сокет", + "tcp": "TCP / HTTP", + "tailscalePingWarning": "Для таго, каб выкарыстоўваць манітор Tailscale Ping, неабходна ўсталяваць Uptime Kuma без Docker, а таксама ўсталяваць на сервер кліент Tailscale.", + "Docker Container": "Docker кантэйнер", + "Container Name / ID": "Назва кантэйнера / ID", + "Docker Host": "Хост Docker", + "Docker Hosts": "Хосты Docker", + "Domain": "Дамен", + "Workstation": "Рабочая станцыя", + "Packet Size": "Памер пакета", + "Bot Token": "Токен бота", + "wayToGetTelegramToken": "Вы можаце атрымаць токен тут - {0}.", + "Chat ID": "ID чата", + "telegramMessageThreadID": "(Неабавязкова) ID ланцуга паведамленняў", + "telegramMessageThreadIDDescription": "Неабавязковы ўнікальны ідэнтыфікатар для ланцуга паведамленняў (тэмы) форума; толькі для форумаў-супергруп", + "telegramSendSilently": "Адправіць без гуку", + "telegramSendSilentlyDescription": "Карыстальнікі атрымаюць абвестку без гуку.", + "telegramProtectContent": "Забараніць перасылку/захаванне", + "telegramProtectContentDescription": "Калі ўключана, паведамленні бота ў Telegram будуць забароненыя для перасылкі і захавання.", + "supportTelegramChatID": "Падтрымліваюцца ID чатаў, груп і каналаў", + "wayToGetTelegramChatID": "Вы можаце атрымаць ID вашага чата, адправіўшы паведамленне боту і перайсці па гэтаму URL для прагляду chat_id:", + "YOUR BOT TOKEN HERE": "ВАШ ТОКЕН БОТА ТУТ", + "chatIDNotFound": "ID чата не знойдзены; спачатку адпраўце паведамленне боту", + "disableCloudflaredNoAuthMsg": "Вы знаходзіцеся ў рэжыме без аўтарызацыі, пароль не патрабуецца.", + "trustProxyDescription": "Давяраць загалоўкам 'X-Forwarded-*'. Калі вы хочаце атрымаць правільны IP-адрас кліента, а ваш Uptime Kuma знаходзіцца пад Nginx або Apache, вам след включить гэты параметр.", + "wayToGetLineNotifyToken": "Вы можаце атрымаць токен доступу ў {0}", + "Examples": "Прыклады", + "Home Assistant URL": "URL-адрас Home Assistant", + "Long-Lived Access Token": "Токен доступу з доўгім тэрмінам службы", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Токен доступу з доўгім тэрмінам дзеяння можна стварыць, націснуўшы на імя вашага профілю (ўнізе злева) і пракруціўшы яго ўніз, потым націсніце Стварыць токен. ", + "Notification Service": "Служба абвестак", + "default: notify all devices": "па змаўчанні: апавяшчаць усе прылады", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Спіс службаў абвестак можна знайсці ў Home Assistant у раздзеле \"Інструменты распрацоўніка > Службы\", выканаўшы пошук па слове \"абвестка\", каб знайсці назву вашага прылады/тэлефона.", + "Automations can optionally be triggered in Home Assistant:": "Пры жаданні аўтаматызацыю можна актываваць у Home Assistant.:", + "Trigger type:": "Тып трыгера:", + "Event type:": "Тып падзеі:", + "Event data:": "даныя падзеі:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Затым выберыце дзеянне, напрыклад, пераключыце сцэну на чырвоны індыкатар RGB..", + "Frontend Version": "Версія інтэрфейса", + "Frontend Version do not match backend version!": "Версія інтэрфейса не адпавядае версіі сервернай часткі!", + "backupOutdatedWarning": "Састарэла: гэтая функцыя рэзервовага капіявання больш не падтрымліваецца. Праз даданыя шмат функцый, яна не можа стварыць або аднавіць поўную рэзервовую копію.", + "backupRecommend": "Зрабіце рэзервовую копію таму або папцы з данымі (./data/) напрамую.", + "Optional": "Неабавязкова", + "or": "або", + "sameAsServerTimezone": "Аналагічна часавому поясу сервера", + "startDateTime": "Пачатковая дата і час", + "endDateTime": "Канчатковая дата і час", + "cronExpression": "Выраз для Cron", + "cronSchedule": "Расклад: ", + "invalidCronExpression": "Няправільны выраз Cron: {0}", + "recurringInterval": "Інтэрвал", + "Recurring": "Паўторны", + "strategyManual": "Актыўны/Неактыўны Ручным спосабам", + "warningTimezone": "Выкарыстоўваецца часавы пояс сервера", + "weekdayShortMon": "Пн", + "weekdayShortTue": "Аўт", + "weekdayShortWed": "Ср", + "weekdayShortThu": "Чт", + "weekdayShortFri": "Пт", + "weekdayShortSat": "Сб", + "weekdayShortSun": "Нд", + "dayOfWeek": "Дзень тыдня", + "dayOfMonth": "Дзень месяца", + "lastDay": "Апошні дзень", + "lastDay1": "Апошні дзень месяца", + "lastDay2": "Другі апошні дзень месяца", + "lastDay3": "Трэці апошні дзень месяца", + "maintenanceStatus-scheduled": "Запланавана(ы)", + "maintenanceStatus-unknown": "Невядома", + "lastDay4": "Чацвёрты апошні дзень месяца", + "No Maintenance": "Няма тэхабслугоўванняў", + "pauseMaintenanceMsg": "Вы ўпэўненыя, што хочаце паставіць на паўзу?", + "maintenanceStatus-under-maintenance": "На тэхабслугоўванні", + "maintenanceStatus-inactive": "Неактыўны", + "Display Timezone": "Паказаць часавы пояс", + "Server Timezone": "Часавы пояс сервера", + "statusPageMaintenanceEndDate": "Канец", + "IconUrl": "URL значка", + "Enable DNS Cache": "(Састарэла) Уключыць DNS кэш для манітораў HTTP(S)", + "Enable": "Уключыць", + "Disable": "Адключыць", + "enableNSCD": "Уключыць NSCD (Name Service Cache Daemon) для кэшавання ўсіх DNS-запытаў", + "chromeExecutable": "Выканаўчы файл Chrome/Chromium", + "chromeExecutableAutoDetect": "Аўтавызначэнне", + "chromeExecutableDescription": "Для карыстальнікаў Docker, калі Chromium яшчэ не ўсталяваны, можа спатрэбіцца некалькі хвілін для ўсталявання і адлюстравання выніку тэставання. Ён займае 1 ГБ дыскавага прастору.", + "dnsCacheDescription": "Гэта можа не працаваць на некаторых IPv6 асяроддзях, адключыце гэта, калі ў вас узнікаюць праблемы.", + "Single Maintenance Window": "Адзінае акно тэхабслугоўвання", + "Maintenance Time Window of a Day": "Суточны інтэрвал для тэхабслугоўвання", + "Effective Date Range": "Даты дзеяння (Неабавязкова)", + "Schedule Maintenance": "Запланаваць тэхабслугоўванне", + "Edit Maintenance": "Рэдагаваць тэхабслугоўванне", + "Date and Time": "Дата і час", + "DateTime Range": "Дыяпазон даты і часу", + "loadingError": "Немагчыма атрымаць даныя, калі ласка паспрабуйце пазней.", + "plugin": "Плагін | Плагіны", + "install": "Усталяваць", + "installing": "Усталяваецца", + "uninstall": "Выдаліць", + "uninstalling": "Выдаляецца", + "confirmUninstallPlugin": "Вы ўпэўнены, што хочаце выдаліць гэты плагін?", + "notificationRegional": "Рэгіянальны", + "Clone Monitor": "Копія", + "Clone": "Кланаваць", + "cloneOf": "Копія {0}", + "smtp": "Email (SMTP)", + "secureOptionNone": "Няма / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ігнараваць памылкі TLS", + "From Email": "Ад каго", + "emailCustomisableContent": "Наладжвальны змест", + "smtpLiquidIntroduction": "Наступныя два поля з'яўляюцца шабланізаванымі з дапамогай мовы шаблонаў Liquid. Інструкцыі па іх выкарыстаньні прадстаўлены ў раздзеле {0}. Вось даступныя зменныя:", + "emailCustomSubject": "Свая тэма", + "leave blank for default subject": "пакіньце пустым для тэмы па змаўчаньні", + "emailCustomBody": "Карыстацкі аб'ект", + "leave blank for default body": "пакіньце пустым для аб'екта па змаўчаньні", + "emailTemplateServiceName": "Назва сэрвіса", + "emailTemplateHostnameOrURL": "Назва хоста або URL", + "emailTemplateStatus": "Статус", + "emailTemplateMonitorJSON": "аб'ект, які апісвае манітор", + "emailTemplateHeartbeatJSON": "аб'ект, які апісвае сігнал", + "emailTemplateMsg": "паведамленне апавешчання", + "emailTemplateLimitedToUpDownNotification": "даступны толькі для сігналаў UP/DOWN, у адваротным выпадку null", + "To Email": "Каму", + "smtpCC": "Копія", + "smtpBCC": "Схаваная копія", + "Discord Webhook URL": "Discord вэбхук URL", + "wayToGetDiscordURL": "Вы можаце стварыць яго ў наладах канала \"Налады -> Інтэграцыі -> Стварыць Вэбхук\"", + "Bot Display Name": "Адлюстраваная назва бота", + "Prefix Custom Message": "Свой прэфікс паведамлення", + "Hello @everyone is...": "Прывітанне {'@'}everyone гэта…", + "wayToGetTeamsURL": "Як стварыць URL вэбхука вы можаце даведацца тут - {0}.", + "wayToGetZohoCliqURL": "Вы можаце даведацца, як стварыць webhook URL тут {0}.", + "needSignalAPI": "Вам патрэбны кліент Signal з падтрымкай REST API.", + "Channel access token": "Токен доступу да канала", + "wayToCheckSignalURL": "Перайдзіце па гэтаму URL, каб даведацца, як наладзіць такі кліент:", + "Recipients": "Атрымальнікі", + "Access Token": "Токен доступу", + "Channel access token (Long-lived)": "Токен доступу да канала (даўгавечны)", + "Line Developers Console": "Кансоль распрацошчыкаў Line", + "Basic Settings": "Базавыя налады", + "User ID": "ID карыстальніка", + "Your User ID": "Ваш ідэнтыфікатар карыстальніка", + "Messaging API": "API паведамленняў", + "wayToGetLineChannelToken": "Спачатку зайдзіце ў {0}, стварыце правайдэра і канал (API паведамленняў), потым вы зможаце атрымаць токен доступу да канала і ID карыстальніка з вышэйзгаданых пунктаў меню.", + "Icon URL": "URL значка", + "aboutIconURL": "Вы можаце ўставіць спасылку на значок ў поле \"URL значка\" каб змяніць малюнак профілю па змаўчанні. Не выкарыстоўваецца, калі зададзена значок Emoji.", + "aboutMattermostChannelName": "Вы можаце перавызначыць канал па змаўчанні, у які вэбхук піша, уведаўшы імя канала ў поле \"Імя канала\". Гэта неабходна ўключыць у наладах вэбхука Mattermost. Напрыклад: #other-channel", + "dataRetentionTimeError": "Перыяд захавання павінен быць 0 або больш", + "infiniteRetention": "Выберыце 0 для бясконцага захавання.", + "confirmDeleteTagMsg": "Вы сапраўды хочаце выдаліць гэты тэг? Маніторы, звязаныя з гэтым тэгам не будуць выдаленыя.", + "enableGRPCTls": "Дазволіць адпраўляць gRPC запыт праз TLS злучэнне", + "grpcMethodDescription": "Імя метада пераўтвараецца ў фармат camelCase, напрыклад, sayHello, check і г.д.", + "acceptedStatusCodesDescription": "Выберыце коды статусаў для вызначэння даступнасці службы.", + "deleteMonitorMsg": "Вы сапраўды хочаце выдаліць гэты манітор?", + "deleteMaintenanceMsg": "Вы сапраўды хочаце выдаліць гэтае тэхабслугоўванне?", + "deleteNotificationMsg": "Вы сапраўды хочаце выдаліць гэтую абвестку для ўсіх манітораў?", + "dnsPortDescription": "Па змаўчанні порт DNS сервера - 53. Мы можаце змяніць яго ў любы час.", + "resolverserverDescription": "Cloudflare з'яўляецца серверам па змаўчанні. Вы заўсёды можаце змяніць гэты сервер.", + "rrtypeDescription": "Выберыце тып рэсурснага запісу, які вы хочаце адсочваць", + "pauseMonitorMsg": "Вы сапраўды хочаце прыпыніць?", + "enableDefaultNotificationDescription": "Для кожнага новага манітора гэта апавяшчэнне будзе ўключана па змаўчанні. Вы ўсё яшчэ можаце адключыць апавяшчэнні ў кожным маніторы асобна.", + "clearEventsMsg": "Вы сапраўды хочаце выдаліць усю статыстыку падзей гэтага манітора?", + "clearHeartbeatsMsg": "Вы сапраўды хочаце выдаліць усю статыстыку апытанняў гэтага манітора?", + "confirmClearStatisticsMsg": "Вы сапраўды хочаце выдаліць УСЮ статыстыку?", + "importHandleDescription": "Выберыце \"Прапусціць існуючыя\", калі вы хочаце прапусціць кожны манітор або апавяшчэнне з такой жа назвай. \"Перазапісаць\" выдаліць кожны існуючы манітор або апавяшчэнне і дадаць зноў. Варыянт \"Не правяраць\" прымусова адновіць усе маніторы і апавяшчэнні, нават калі яны ўжо існуюць.", + "twoFAVerifyLabel": "Увядзіце свой токен, каб праверыць працу 2FA:", + "tokenValidSettingsMsg": "Токен сапраўдны! Цяпер вы можаце захаваць налады 2FA.", + "confirmImportMsg": "Вы сапраўды хочаце аднавіць рэзервовую копію? Пераканайцеся, што вы выбралі правільны варыянт імпарту.", + "confirmEnableTwoFAMsg": "Вы сапраўды хочаце ўключыць 2FA?", + "confirmDisableTwoFAMsg": "Вы сапраўды хочаце адключыць 2FA?", + "affectedStatusPages": "Паказваць абвестку аб тэхабслугоўванні на выбраных старонках статуса", + "atLeastOneMonitor": "Выберыце больш за адзін затрагаваны манітор", + "passwordNotMatchMsg": "Уведзеныя паролі не супадаюць.", + "notificationDescription": "Прымацаваць абвесткі да манітораў.", + "keywordDescription": "Пошук слова ў чыстым HTML або ў JSON-адказе (адчувальны да рэгістра).", + "invertKeywordDescription": "Шукаць, каб ключавое слова адсутнічала, а не прысутнічала.", + "jsonQueryDescription": "Выконайце json-запыт да адказу і праверце наяўнасць чаканага значэння (вяртанае значэнне будзе пераўтворана ў радок для параўнання). Глядзіце {0} для атрымання дакументацыі па мове запытаў. А трэніравацца вы можаце {1}.", + "backupDescription": "Вы можаце захаваць рэзервовую копію ўсіх манітораў і апавешчанняў у выглядзе JSON-файла.", + "backupDescription2": "Важна: гісторыя і падзеі захаваныя не будуць.", + "backupDescription3": "Важныя даныя, такія як токены апавешчанняў, дадаюцца пры экспарце, таму захоўвайце файлы ў бяспечным месцы.", + "endpoint": "канчатковая кропка", + "octopushAPIKey": "\"{API key}\" з даных уліковых запісаў HTTP API ў панэлі кіравання", + "octopushLogin": "\"Login\" з даных уліковых запісаў HTTP API ў панэлі кіравання", + "promosmsLogin": "Лагін API", + "promosmsPassword": "Пароль API", + "pushoversounds pushover": "Pushover (па змаўчанні)", + "pushoversounds bike": "Веласіпед", + "pushoversounds bugle": "Горн", + "pushoversounds cashregister": "Касавы апарат", + "pushoversounds classical": "Класічны", + "pushoversounds cosmic": "Касмічны", + "pushoversounds falling": "Падаючы", + "pushoversounds intermission": "Антракт", + "pushoversounds magic": "Магія", + "pushoversounds mechanical": "Механічны", + "pushoversounds pianobar": "Піяна-бар", + "pushoversounds siren": "Сірэна", + "pushoversounds spacealarm": "Касмічная сігналізацыя", + "pushoversounds tugboat": "Буксір", + "pushoversounds alien": "Іншаплянетная трывога (доўгая)", + "pushoversounds persistent": "Настойлівы (доўгі)", + "pushoversounds echo": "Pushover Эха (доўгае)", + "pushoversounds updown": "Уверх уніз (доўгае)", + "pushoversounds vibrate": "Толькі вібрацыя", + "pushoversounds none": "Няма (ціха)", + "pushyAPIKey": "Сакрэтны ключ API", + "pushyToken": "Токен прылады", + "apprise": "Apprise (Падтрымка 50+ сэрвісаў абвестак)", + "GoogleChat": "Google Chat (толькі Google Workspace)", + "wayToGetKookGuildID": "Уключыце \"Рэжым распрацошчыка\" у наладах Kook, а затым націсніце правай кнопкай на гільдыю, каб скапіраваць яе ID", + "Guild ID": "Ідэнтыфікатар гільдыі", + "User Key": "Ключ карыстальніка", + "Message Title": "Загаловак паведамлення", + "Notification Sound": "Гук абвесткі", + "More info on:": "Больш інфармацыі на: {0}", + "pushoverDesc1": "Экстрэмальны прыярытэт (2) мае таймаўт паўтору па змаўчанні 30 секунд і сканчаецца праз 1 гадзіну.", + "pushoverDesc2": "Калі вы хочаце адпраўляць абвесткі розным прыладам, неабходна запоўніць поле Прылада.", + "pushoverMessageTtl": "TTL паведамлення (у секундах)", + "SMS Type": "Тып SMS", + "octopushTypePremium": "Преміум (Хуткі - рэкамендуецца для аляртаў)", + "octopushTypeLowCost": "Танны (Павольны - часам блакуецца аператарамі)", + "apiCredentials": "API рэквізіты", + "checkPrice": "Тарыфы {0}:", + "octopushLegacyHint": "Вы выкарыстоўваеце старую версію Octopush (2011-2020) ці новую?", + "Check octopush prices": "Тарыфы Octopush {0}.", + "octopushPhoneNumber": "Нумар тэлефона (міжнародны фармат напр. +48123456789) ", + "octopushSMSSender": "Імя адпраўніка SMS: 3-11 сімвалаў алфавіта, лічбаў і прабелаў (a-zA-Z0-9)", + "LunaSea Device ID": "ID прылады LunaSea", + "Apprise URL": "URL апавяшчэння", + "Example:": "Прыклад: {0}", + "Read more:": "Падрабязней: {0}", + "Status:": "Статус: {0}", + "Strategy": "Стратэгія", + "Free Mobile User Identifier": "Бясплатны мабільны ідэнтыфікатар карыстальніка", + "Free Mobile API Key": "API ключ Free Mobile", + "Enable TLS": "Уключыць TLS", + "Proto Service Name": "Назва службы Proto", + "Proto Method": "Метад Proto", + "Proto Content": "Змест Proto", + "Economy": "Эканомія", + "Lowcost": "Бюджэтны", + "high": "высокі", + "SendKey": "SendKey", + "SMSManager API Docs": "Дакументацыя да API SMSManager ", + "Gateway Type": "Тып шлюза", + "You can divide numbers with": "Вы можаце дзяліць лічбы з", + "Base URL": "Базавы URL", + "goAlertInfo": "GoAlert — гэта праграма з адкрытым зыходным кодам для складання раскладу выклікаў, аўтаматычнай эскаляцыі і абвестак (напрыклад, SMS або галасавых выклікаў). Аўтаматычна прывабляйце патрэбнага чалавека, патрэбным спосабам і ў патрэбны час! {0}", + "goAlertIntegrationKeyInfo": "Атрымаць агульны ключ інтэграцыі API для службы ў гэтым фармаце \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" звычайна значэнне параметра токена скапіяванага URL.", + "AccessKeyId": "ID ключа доступу", + "SecretAccessKey": "Сакрэтны ключ доступу", + "PhoneNumbers": "Нумары тэлефонаў", + "TemplateCode": "Код шаблону", + "SignName": "SignName", + "Sms template must contain parameters: ": "Шаблон SMS павінен змяшчаць параметры: ", + "Bark API Version": "Версія Bark API", + "Bark Endpoint": "Канчатковая кропка Bark", + "Bark Group": "Bark Group", + "Bark Sound": "Bark Sound", + "WebHookUrl": "WebHookUrl", + "SecretKey": "Сакрэтны Ключ", + "For safety, must use secret key": "Для бяспекі, неабходна выкарыстоўваць сакрэтны ключ", + "Mentioning": "Згадванне", + "Don't mention people": "Не згадваць людзей", + "Mention group": "Згадаць {group}", + "Device Token": "Токен прылады", + "Platform": "Платформа", + "High": "Высокі", + "Retry": "Паўторыць", + "Topic": "Тэма", + "WeCom Bot Key": "WeCom Bot Key", + "Setup Proxy": "Налада Проксі", + "Proxy Protocol": "Пратакол Проксі", + "Proxy Server": "Проксі", + "Proxy server has authentication": "Проксі мае аўтэнтыфікацыю", + "promosmsTypeEco": "SMS ECO - танкі і павольны, часта перагружаны. Толькі для атрымальнікаў з Польшчы.", + "promosmsTypeFlash": "SMS FLASH - паведамленні аўтаматычна з'яўляюцца на прыладзе атрымальніка. Толькі для атрымальнікаў з Польшчы.", + "promosmsTypeFull": "SMS FULL - прэміум-узровень SMS, можна выкарыстоўваць сваё імя адпраўніка (папярэдне зарэгістраваў яго). Надзейна для аляртаў.", + "promosmsTypeSpeed": "SMS SPEED - найвышэйшы прыярытэт у сістэме. Вельмі хутка і надзейна, але вельмі дорага (у два разы дорага, чым SMS FULL).", + "promosmsPhoneNumber": "Нумар тэлефона (для атрымальнікаў з Польшчы можна прапусціць код рэгіёна)", + "promosmsSMSSender": "Імя адпраўніка SMS: Зарэгістраванае або адно з імён па змаўчанні: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "promosmsAllowLongSMS": "Дазволіць доўгія SMS", + "Feishu WebHookUrl": "Feishu WebHookURL", + "matrixHomeserverURL": "URL сервера (разам з http(s):// і па жаданні порт)", + "Internal Room Id": "Унутраны ID пакою", + "matrixDesc1": "Унутраны ID пакою можна знайсці ў Падрабязнасцях у параметрах канала вашага кліента Matrix. Ён павінен выглядаць прыблізна так !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Рэкамендуецца стварыць новага карыстальніка і не выкарыстоўваць токен доступу асабістага карыстальніка Matrix, т.к. гэта ўяўляе за сабой поўны доступ да акаўнта і да пакояў, у якіх вы знаходзіцеся. Замест гэтага стварыце новага карыстальніка і запрасіце яго толькі ў той пакой, у якім вы хочаце атрымліваць абвесткі. Токен доступу можна атрымаць, выканаўшы каманду {0}", + "Channel Name": "Назва канала", + "Notify Channel": "Канал апавешчанняў", + "aboutNotifyChannel": "Апавяшчэнне аб канале выкліча настольнае або мабільнае апавяшчэнне для ўсіх удзельнікаў канала, незалежна ад таго, ці ўстаноўлена іх даступнасць як актыўная або адсутная.", + "Uptime Kuma URL": "Uptime Kuma URL", + "setup a new monitor group": "наладзіць новую групу манітораў", + "openModalTo": "адкрыць мадальнае акно {0}", + "Add a domain": "Дадаць дамен", + "Remove domain": "Выдаліць дамен '{0}'", + "Icon Emoji": "Emoji", + "signalImportant": "ВАЖНА: Нельга змешваць у Атрымальніках групы і нумары!", + "aboutWebhooks": "Больш інфармацыі аб вэбхуках: {0}", + "aboutChannelName": "Увядзіце назву канала ў поле {0} Назва канала, калі вы хочаце абысці канал вэбхука. Напрыклад: #other-channel", + "aboutKumaURL": "Калі поле Uptime Kuma URL у наладах застанецца пустым, па змаўчанні будзе выкарыстоўвацца спасылка на праект на GitHub.", + "smtpDkimSettings": "DKIM Налады", + "smtpDkimDesc": "Калі ласка, азнаёмцеся з {0} Nodemailer DKIM для выкарыстання.", + "documentation": "дакументацыяй", + "smtpDkimDomain": "Назва дамена", + "smtpDkimKeySelector": "Ключ", + "smtpDkimPrivateKey": "Прыватны ключ", + "smtpDkimHashAlgo": "Алгарытм хэша (неабавязкова)", + "smtpDkimheaderFieldNames": "Загаловак ключоў для подпісу (неабавязкова)", + "smtpDkimskipFields": "Загаловак ключоў не для подпісу (опцыянальна)", + "wayToGetPagerDutyKey": "Вы можаце гэта атрымаць, перайшоўшы ў Сервіс -> Каталог сервісаў -> (Выберыце сервіс) -> Інтэграцыі -> Дадаць інтэграцыю. Тут вы можаце шукаць «Events API V2». Падрабязней {0}", + "Integration Key": "Ключ інтэграцыі", + "Integration URL": "URL інтэграцыі", + "Auto resolve or acknowledged": "Аўтаматычнае развязванне або пацверджанне", + "do nothing": "нічога не рабіць", + "auto acknowledged": "аўтаматычна пацверджана", + "auto resolve": "аўтаматычна развязана", + "alertaApiEndpoint": "Канчатковая кропка API", + "alertaEnvironment": "Асяроддзе", + "alertaApiKey": "Ключ API", + "alertaAlertState": "Стан алярта", + "alertaRecoverState": "Стан аднаўлення", + "serwersmsAPIUser": "API Карыстальнік (уключаючы прэфікс webapi_)", + "serwersmsAPIPassword": "API Пароль", + "serwersmsPhoneNumber": "Нумар тэлефона", + "serwersmsSenderName": "SMS Імя адпраўніка (зарэгістравана праз карыстальніцкі партал)", + "smseagleTo": "Нумар(ы) тэлефона", + "smseagleGroup": "Назва(ы) групы тэлефоннай кнігі", + "smseagleContact": "Імёны кантактаў тэлефоннай кнігі", + "smseagleRecipientType": "Тып атрымальніка", + "smseagleRecipient": "Атрымальнік(і) (калі множнасць, павінны быць раздзеленыя коскай)", + "smseagleToken": "Токен доступу API", + "smseagleUrl": "URL вашага прылады SMSEagle", + "smseagleEncoding": "Адправіць у Unicode", + "smseaglePriority": "Прыярытэт паведамлення (0-9, па змаўчанні = 0)", + "Recipient Number": "Нумар атрымальніка", + "From Name/Number": "Імя/нумар адпраўніка", + "Leave blank to use a shared sender number.": "Пакіньце пустым, каб выкарыстоўваць агульны нумар адпраўніка.", + "Octopush API Version": "Версія API Octopush", + "Legacy Octopush-DM": "Састарэлы Octopush-DM", + "ntfy Topic": "Тэма ntfy", + "Server URL should not contain the nfty topic": "URL сервера не павінен утрымліваць тэму nfty", + "onebotHttpAddress": "HTTP-адрас OneBot", + "onebotMessageType": "Тып паведамлення OneBot", + "onebotGroupMessage": "Група", + "onebotPrivateMessage": "Асабістае", + "onebotUserOrGroupId": "ID групы/карыстальніка", + "onebotSafetyTips": "Для бяспекі неабходна ўсталяваць токен доступу", + "PushDeer Server": "Сервер PushDeer", + "pushDeerServerDescription": "Пакіньце пустым для выкарыстання афіцыйнага сервера", + "PushDeer Key": "Ключ PushDeer", + "wayToGetClickSendSMSToken": "Вы можаце атрымаць імя карыстальніка API і ключ API з {0} .", + "Custom Monitor Type": "Сваёродны тып манітора", + "Google Analytics ID": "ID Google Аналітыкі", + "Edit Tag": "Рэдагаваць тэг", + "Server Address": "Адрас сервера", + "Learn More": "Даведацца больш", + "Body Encoding": "Тып зместу запыту.(JSON або XML)", + "API Keys": "API Ключы", + "Expiry": "Сканчэнне", + "Continue": "Працягнуць", + "Add Another": "Дадаць яшчэ", + "Key Added": "Ключ дададзены", + "apiKeyAddedMsg": "Ваш ключ API дададзены. Звярніце ўвагу на гэтае паведамленне, так як яно адлюстроўваецца адзін раз.", + "Add API Key": "Дадаць API ключ", + "No API Keys": "Няма ключоў API", + "apiKey-active": "Актыўны", + "apiKey-expired": "Скончыўся", + "apiKey-inactive": "Неактыўны", + "Expires": "Сканчаецца", + "disableAPIKeyMsg": "Вы ўпэўнены, што хочаце адключыць гэты API ключ?", + "deleteAPIKeyMsg": "Вы ўпэўнены, што хочаце выдаліць гэты ключ API?", + "Generate": "Згенераваць", + "pagertreeIntegrationUrl": "URL-адрас інтэграцыі", + "pagertreeUrgency": "Тэрміновасць", + "pagertreeSilent": "Ціхі", + "pagertreeLow": "Нізкі", + "pagertreeMedium": "Сярэдні", + "pagertreeHigh": "Высокі", + "pagertreeCritical": "Крытычны", + "pagertreeResolve": "Аўтаматычнае развязванне", + "pagertreeDoNothing": "Нічога не рабіць", + "wayToGetPagerTreeIntegrationURL": "Пасля стварэння інтэграцыі Uptime Kuma ў PagerTree скапіруйце файл {Endpoint}. Гл. поўную інфармацыю {0}", + "lunaseaTarget": "Мэта", + "lunaseaDeviceID": "Ідэнтыфікатар прылады", + "lunaseaUserID": "Ідэнтыфікатар карыстальніка", + "ntfyAuthenticationMethod": "Метад уваходу", + "ntfyPriorityHelptextAllEvents": "Усе падзеі адпраўляюцца з максімальным прыярытэтам", + "ntfyPriorityHelptextAllExceptDown": "Усе падзеі адпраўляюцца з гэтым прыярытэтам, акрамя {0}-падзеяў, якія маюць прыярытэт {1}", + "ntfyUsernameAndPassword": "Лагін і пароль", + "twilioAccountSID": "SID уліковага запісу", + "twilioApiKey": "Ключ API (неабавязкова)", + "twilioAuthToken": "Токен аўтарызацыі / Сакрэтны API ключ", + "twilioFromNumber": "З нумара", + "twilioToNumber": "На нумар", + "Monitor Setting": "Налада манітора {0}", + "Show Clickable Link": "Паказаць націскальную спасылку", + "Show Clickable Link Description": "Калі пазначаны флажок, усе, хто мае доступ да гэтай старонкі стану, могуць мець доступ да URL-адрасу манітора.", + "Open Badge Generator": "Адкрыць генератар значкаў", + "Badge Generator": "Генератар значкоў для {0}", + "Badge Type": "Тып значка", + "Badge Duration (in hours)": "Тэрмін дзеяння значка (у гадзінах)", + "Badge Label": "Надпіс для значка", + "Badge Prefix": "Значэнне прэфікса значка", + "Badge Suffix": "Значэнне суфікса значка", + "Badge Label Color": "Колер надпісу значка", + "Badge Color": "Колер значка", + "Badge Label Prefix": "Прэфікс надпісу для значка", + "Badge Preview": "Папярэдні прагляд значка", + "Badge Label Suffix": "Суфікс надпісу для значка", + "Badge Up Color": "Колер значка для статусу \"Даступны\"", + "Badge Down Color": "Колер значка для статусу \"Недаступны\"", + "Badge Pending Color": "Колер значка для статусу \"Чаканне\"", + "Badge Maintenance Color": "Колер значка для статусу \"Тэхабслугоўванне\"", + "Badge Warn Color": "Колер значка для папярэджання", + "Badge Warn Days": "Значок для \"дзён папярэджання\"", + "Badge Down Days": "Значок для \"дзён недаступнасці\"", + "Badge Style": "Стыль значка", + "Badge value (For Testing only.)": "Значэнне значка (толькі для тэставання)", + "Group": "Група", + "Monitor Group": "Група манітораў", + "monitorToastMessagesLabel": "Апавяшчэнні", + "monitorToastMessagesDescription": "Паведамленні для манітораў знікаюць праз зададзены час у секундах. Значэнне -1 адключае тайм-аўт. Значэнне 0 адключае апавяшчэнні.", + "toastErrorTimeout": "Таймаут для апавешчанняў пра памылкі", + "toastSuccessTimeout": "Таймаут для апавешчанняў пра паспяховасьць", + "Enter the list of brokers": "Увядзіце спіс брокераў", + "Kafka Brokers": "Kafka Brokers", + "Press Enter to add broker": "Націсніце Enter, каб дадаць брокера", + "Kafka Topic Name": "Назва тэмы Kafka", + "Kafka Producer Message": "Паведамленне продюсера Kafka", + "Enable Kafka SSL": "Уключэнне пратаколу Kafka SSL", + "Enable Kafka Producer Auto Topic Creation": "Уключэнне аўтаматычнага стварэння тэм у Kafka Producer", + "Kafka SASL Options": "Параметры SASL у Kafka", + "Mechanism": "Механізм", + "Pick a SASL Mechanism...": "Выберыце механізм SASL…", + "Authorization Identity": "Аўтарызацыйная ідэнтычнасць", + "AccessKey Id": "AccessKey Id", + "Secret AccessKey": "Сакрэтны ключ доступу", + "Session Token": "Токен сеансу", + "noGroupMonitorMsg": "Не даступна. Спачатку стварыце групу манітораў.", + "Close": "Закрыць", + "Request Body": "Цела запыту", + "wayToGetFlashDutyKey": "Вы можаце перайсці на старонку \"Канал\" -> (Выберыце канал) -> \"Інтэграцыі\" -> \"Дадаць новую старонку інтэграцыі\", дадаць \"Карыстацкую падзею\", каб атрымаць push-адрас, скапіяваць ключ інтэграцыі ў адрас. Для атрымання дадатковай інфармацыі, калі ласка, наведайце", + "FlashDuty Severity": "Сур'ёзнасць", + "nostrRecipients": "Адкрытыя ключы атрымальнікаў (npub)", + "nostrRelaysHelp": "Адзін URL-адрас рэтрансляцыі ў кожным радку", + "nostrSender": "Закрыты ключ адпраўшчыка (nsec)", + "nostrRecipientsHelp": "фармат npub, па адным у радку", + "showCertificateExpiry": "Паказваць пратэрмінаваны сертыфікат", + "noOrBadCertificate": "Адсутнасць сертыфіката", + "gamedigGuessPortDescription": "Порт, які выкарыстоўваецца пратаколам Valve Server Query Protocol, можа адрознівацца ад порта кліента. Паспрабуйце гэта, калі манітор не можа падключыцца да сервера.", + "authUserInactiveOrDeleted": "Карыстальнік неактыўны або выдалены.", + "authInvalidToken": "Няправільны токен.", + "authIncorrectCreds": "Няправільнае імя карыстальніка або пароль.", + "2faAlreadyEnabled": "2FA ўжо ўключана.", + "2faEnabled": "2FA ўключана.", + "2faDisabled": "2FA адключана.", + "successAdded": "Паспяхова дададзена.", + "successResumed": "Паспяхова прадоўжана.", + "successPaused": "Паспяхова спынена.", + "successDeleted": "Паспяхова выдалена.", + "successEdited": "Паспяхова зменена.", + "successAuthChangePassword": "Пароль паспяхова абноўлены.", + "successBackupRestored": "Рэзервовая копія паспяхова адноўлена.", + "successDisabled": "Паспяхова адключана.", + "successEnabled": "Паспяхова ўключана.", + "tagNotFound": "Тэг не знойдзены.", + "foundChromiumVersion": "Выяўлены Chromium/Chrome. Версіі: {0}", + "Remote Browsers": "Аддаленыя браўзеры", + "Remote Browser": "Аддалены браўзер", + "Add a Remote Browser": "Дадаць аддалены браўзер", + "Remote Browser not found!": "Аддалены браўзер не знойдзены!", + "remoteBrowsersDescription": "Аддаленыя браўзеры — альтэрнатыва лакальнаму запуску Chromium. Усталюйце такі сервіс, як browserless.io, або падлучыцеся да свайго ўласнага", + "self-hosted container": "кантэйнер, які хостыцца самастойна", + "remoteBrowserToggle": "Па змаўчаньні Chromium працуе ўнутры кантэйнера Uptime Kuma. Вы можаце выкарыстоўваць аддалены браўзер, пераключыўшы гэты пераключальнік.", + "useRemoteBrowser": "Выкарыстоўваць удалены браўзер", + "deleteRemoteBrowserMessage": "Вы ўпэўнены, што хочаце выдаліць гэты удалены браўзер для ўсіх манітораў?", + "Browser Screenshot": "Скрыншот браўзера", + "wayToGetSevenIOApiKey": "Зайдзіце на панэль кіравання па адрасе app.seven.io > распрацоўшчык > {api key} > зялёная кнопка дадаць", + "senderSevenIO": "Адпраўляе нумар або імя", + "receiverSevenIO": "Нумар атрымання", + "apiKeySevenIO": "SevenIO {API Key}", + "receiverInfoSevenIO": "Калі нумар атрымальніка не знаходзіцца ў Германіі, то перад нумарам неабходна дадаць код краіны (напрыклад, для ЗША код краіны 1, тады выкарыстоўвайце 117612121212, замест 017612121212)", + "wayToGetWhapiUrlAndToken": "Вы можаце атрымаць {API URL} і токен, зайдзяўшы ў патрэбны вам канал з {0}", + "whapiRecipient": "Нумар тэлефона / ID кантакта / ID групы", + "documentationOf": "{0} Дакументацыя", + "What is a Remote Browser?": "Што такое аддалены браўзер?", + "wayToGetHeiiOnCallDetails": "Як атрымаць ID трыгера і {API Keys}, напісана ў {documentation}", + "callMeBotGet": "Тут вы можаце стварыць {endpoint} для {0}, {1} і {2}. Майце на ўвазе, што вы можаце атрымаць абмежаванне па хуткасці. Абмежаванні па хуткасці выглядаюць наступным чынам: {3}", + "gtxMessagingApiKeyHint": "Вы можаце знайсці свой {API key} на старонцы: Мае уліковыя запісы маршрутызацыі > Паказаць інфармацыю аб уліковым запісе > Уліковыя даныя API > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Нумар тэлефона / Адрас крыніцы шляху перадачы (АІПП)", + "To Phone Number": "На нумар тэлефона", + "gtxMessagingToHint": "Міжнародны фармат, з «+» ({e164}, {e212} або {e214})", + "Alphanumeric (recommended)": "Літарна-лічбавая (рэкамендуецца)", + "Telephone number": "Нумар тэлефона", + "cellsyntOriginatortypeAlphanumeric": "Літарна-лічбавы радок (не больш за 11 літарна-лічбавых сімвалаў). Атрымальнікі не могуць адказаць на гэта паведамленне.", + "cellsyntOriginatortypeNumeric": "Лічбавае значэнне (не больш за 15 лічбаў) з нумарам тэлефона ў міжнародным фармаце без 00 ў пачатку (напрыклад, нумар Вялікабрытаніі 07920 110 000 павінен быць заданы, як 447920110000). Атрымальнікі могуць адказаць на паведамленне.", + "Originator": "Крыніца", + "cellsyntOriginator": "Бачны на мабільным тэлефоне атрымальніка як адпраўшчыка паведамлення. Дапушчальныя значэнні і функцыя залежаць ад параметра {originatortype}.", + "cellsyntDestination": "Нумар тэлефона атрымальніка ў міжнародным фармаце з 00 ў пачатку, за якім следуе код краіны, напрыклад, 00447920110000 для нумара Вялікабрытаніі 07920 110 000 (не больш за 17 лічбаў у суме). Не больш за 25000 атрымальнікаў, раздзеленых коскамі, на адзін HTTP-запыт.", + "Allow Long SMS": "Дазволіць доўгія SMS", + "cellsyntSplitLongMessages": "Раздзеляць доўгія паведамленні на 6 частак. 153 x 6 = 918 сімвалаў.", + "max 15 digits": "макс. 15 лічбаў", + "max 11 alphanumeric characters": "максімум 11 літарна-лічбавых сімвалаў", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Увядзіце {Hostname} сервера, да якога вы хочаце падключыцца, або {localhost}, калі вы хочаце выкарыстоўваць {local_mta}", + "Saved.": "Захавана.", + "wayToWriteWhapiRecipient": "Нумар тэлефона з міжнародным прэфіксам, але без знака плюс у пачатку ({0}), ідэнтыфікатара кантакта ({1}) або ідэнтыфікатара групы ({2}).", + "gtxMessagingFromHint": "На мабільных тэлефонах атрымальнікі бачаць АІПП як адпраўшчыка паведамлення. Дапускаецца выкарыстаньне да 11 літарна-лічбавых сімвалаў, шорткода, мясцовага доўгага кода або міжнародных нумароў ({e164}, {e212} або {e214})", + "Send to channel": "Адправіць у канал", + "threadForumPostID": "Трэд / ID паста", + "whatHappensAtForumPost": "Стварыць новы пост на форуме. Гэта НЕ размяшчае паведамленні ў існуючым пасце. Для публікацыі ў існуючай публікацыі выкарыстоўвайце \"{option}\"", + "now": "зараз", + "-year": "-год", + "telegramServerUrl": "(Неабавязкова) URL сервера", + "telegramServerUrlDescription": "Каб зняць абмежаванні API бота Telegram або атрымаць доступ у заблакіраваных рэгіёнах (Кітай, Іран і інш.), націсніце {0} для атрымання дадатковай інфармацыі. Значэнне па змаўчанні: {1}", + "smspartnerPhoneNumber": "Нумар(ы) тэлефона", + "smspartnerSenderName": "Імя адпраўніка SMS", + "Command": "Каманда", + "mongodbCommandDescription": "Выканаць каманду MongoDB для базы даных. Інфармацыю пра даступныя каманды можна знайсці ў {documentation}", + "Community String": "Радок супольнасці", + "snmpCommunityStringHelptext": "Гэты радок выконвае функцыю пароля для аўтэнтыфікацыі і кантролю доступу да прылад з падтрымкай SNMP. Павінен адпавядаць канфігурацыі вашай SNMP-прылады.", + "OID (Object Identifier)": "OID (Ідэнтыфікатар аб’екта)", + "snmpOIDHelptext": "Увядзіце OID для датчыка або статусу, які вы хочаце маніторыць. Выкарыстоўвайце інструменты кіравання сеткай, такія як аглядальнікі MIB або праграмы SNMP, калі вы не ўпэўнены ў патрэбным OID.", + "SNMP Version": "Версія SNMP", + "Please enter a valid OID.": "Калі ласка, увядзіце карэктны OID.", + "wayToGetThreemaGateway": "Вы можаце зарэгістравацца для Threema Gateway {0}.", + "threemaRecipient": "Атрымальнік", + "templateServiceName": "назва сэрвісу", + "templateHostnameOrURL": "імя хоста або URL", + "templateStatus": "статус", + "telegramUseTemplate": "Выкарыстоўваць уласны шаблон паведамлення", + "telegramUseTemplateDescription": "Калі ўключана, паведамленне будзе адпраўлена з выкарыстаннем уласнага шаблона.", + "telegramTemplateFormatDescription": "Telegram дазваляе выкарыстоўваць розныя мовы разметкі для паведамленняў, падрабязнасці глядзіце ў Telegram {0}.", + "cacheBusterParamDescription": "Выпадкова згенераваны параметр для абыходу кэша.", + "Send rich messages": "Адправіць пашыраныя паведамленні", + "Bitrix24 Webhook URL": "URL вэбхука Bitrix24", + "wayToGetBitrix24Webhook": "Вы можаце стварыць вэбхук, выканаўшы крокі, апісаныя тут: {0}", + "bitrix24SupportUserID": "Увядзіце свой ідэнтыфікатар карыстальніка ў Bitrix24. Вы можаце даведацца ідэнтыфікатар па спасылцы, перайшоўшы ў профіль карыстальніка.", + "Condition": "Умова", + "aboutSlackUsername": "Змяняе адлюстраванае імя адпраўніка паведамлення. Калі вы хочаце згадваць кагосьці, уключыце гэта ў сяброўскае імя замест гэтага.", + "time ago": "{0} таму", + "Json Query Expression": "Выраз запыту JSON", + "and": "i", + "smspartnerApiurl": "Вы можаце знайсці свой ключ API на панэлі кіравання па адрасе {0}", + "smspartnerSenderNameInfo": "Павінна быць ад 3 да 11 звычайных сімвалаў", + "Message format": "Фармат паведамлення", + "ignoredTLSError": "Памылкі TLS/SSL былі праігнараваныя", + "shrinkDatabaseDescriptionSqlite": "Запусціць аперацыю {vacuum} для SQLite. {auto_vacuum} ужо ўключаны, але ён не дэфрагментуе базу даных і не перапакоўвае асобныя старонкі базы даных так, як гэта робіць каманда {vacuum}.", + "wayToGetDiscordThreadId": "Атрыманне ідэнтыфікатара тэмы або паведамлення на форуме аналагічнае атрыманню ідэнтыфікатара канала. Падрабязней пра тое, як атрымаць ідэнтыфікатары, чытайце тут {0}", + "smspartnerPhoneNumberHelptext": "Нумар павінен быць у міжнародным фармаце {0}, {1}. Некалькі нумароў павінны быць падзелены {2}", + "cacheBusterParam": "Дадайце параметр {0}", + "defaultFriendlyName": "Новы манiтор", + "Path": "Шлях", + "Add Tags": "Дадаць тэгi" +} diff --git a/src/lang/bg-BG.json b/src/lang/bg-BG.json new file mode 100644 index 0000000..86df987 --- /dev/null +++ b/src/lang/bg-BG.json @@ -0,0 +1,1703 @@ +{ + "languageName": "Български", + "checkEverySecond": "Ще се извършва на всеки {0} секунди", + "retryCheckEverySecond": "Ще се извършва на всеки {0} секунди", + "retriesDescription": "Максимален брой опити преди маркиране на услугата като недостъпна и изпращане на известие", + "ignoreTLSError": "Игнорирай TLS/SSL грешки за HTTPS уеб сайтове", + "upsideDownModeDescription": "Обръща статуса от достъпен на недостъпен. Ако услугата е достъпна, ще се вижда като НЕДОСТЪПНА.", + "maxRedirectDescription": "Максимален брой пренасочвания, които да бъдат следвани. Въведете 0 за да изключите пренасочване.", + "acceptedStatusCodesDescription": "Изберете статус кодове, които да се считат за успешен отговор.", + "passwordNotMatchMsg": "Повторената парола не съвпада.", + "notificationDescription": "Моля, задайте известието към монитор(и), за да функционира.", + "keywordDescription": "Търси ключова дума в чист HTML или JSON отговор - чувствителна е към регистъра.", + "pauseDashboardHome": "Пауза", + "deleteMonitorMsg": "Наистина ли желаете да изтриете този монитор?", + "deleteNotificationMsg": "Наистина ли желаете да изтриете това известие за всички монитори?", + "resolverserverDescription": "Cloudflare е сървърът по подразбиране. Можете да посочите списък с IP адреси или имена на хостове, разделени със запетаи.", + "rrtypeDescription": "Изберете ресурсния запис, който желаете да наблюдавате", + "pauseMonitorMsg": "Наистина ли желаете да поставите в режим пауза?", + "enableDefaultNotificationDescription": "За всеки нов монитор това известие ще бъде активирано по подразбиране. Можете да го изключите за всеки отделен монитор.", + "clearEventsMsg": "Сигурни ли сте, че желаете да изтриете всички събития за този монитор? Това перманентно премахва съпровождащите данни и не може да бъде отменено.", + "clearHeartbeatsMsg": "Сигурни ли сте, че желаете да изтриете всички записи на честотни събития за този монитор? Това перманентно премахва съпровождащите данни и не може да бъде отменено.", + "confirmClearStatisticsMsg": "Наистина ли желаете да изтриете всички статистически данни?", + "importHandleDescription": "Изберете 'Пропусни съществуващите', ако желаете да пропуснете всеки монитор или известие със същото име. 'Презапис' ще изтрие всеки съществуващ монитор и известие.", + "confirmImportMsg": "Сигурни ли сте, че желаете импортирането на архива? Моля, уверете се, че сте избрали правилната опция за импортиране.", + "twoFAVerifyLabel": "Моля, въведете вашия токен код, за да проверите дали 2FA работи:", + "tokenValidSettingsMsg": "Токен кодът е валиден! Вече можете да запазите настройките за 2FA.", + "confirmEnableTwoFAMsg": "Сигурни ли сте, че желаете да активирате 2FA?", + "confirmDisableTwoFAMsg": "Сигурни ли сте, че желаете да изключите 2FA?", + "Settings": "Настройки", + "Dashboard": "Табло", + "New Update": "Налична е актуализация", + "Language": "Език", + "Appearance": "Изглед", + "Theme": "Тема", + "General": "Общи", + "Version": "Версия", + "Check Update On GitHub": "Проверка за актуализация в GitHub", + "List": "Списък", + "Add": "Добави", + "Add New Monitor": "Добави монитор", + "Quick Stats": "Кратка статистика", + "Up": "Достъпен", + "Down": "Недостъпен", + "Pending": "Изчаква", + "Unknown": "Неизвестен", + "Pause": "Пауза", + "Name": "Име", + "Status": "Статус", + "DateTime": "Дата и час", + "Message": "Отговор", + "No important events": "Все още няма събития", + "Resume": "Възобнови", + "Edit": "Редактирай", + "Delete": "Изтрий", + "Current": "Текущ", + "Uptime": "Достъпност", + "Cert Exp.": "Вал. сертификат", + "day": "ден | дни", + "-day": "-дни", + "hour": "час | часове", + "-hour": "-часa", + "Response": "Отговор", + "Ping": "Пинг", + "Monitor Type": "Монитор тип", + "Keyword": "Ключова дума", + "Friendly Name": "Псевдоним", + "URL": "URL Адрес", + "Hostname": "Име на хост", + "Port": "Порт", + "Heartbeat Interval": "Честота на проверка", + "Retries": "Повторни опити", + "Heartbeat Retry Interval": "Честота на повторните опити", + "Advanced": "Разширени", + "Upside Down Mode": "Обърнат режим", + "Max. Redirects": "Макс. брой пренасочвания", + "Accepted Status Codes": "Допустими статус кодове", + "Save": "Запази", + "Notifications": "Известия", + "Not available, please setup.": "Не са налични. Моля, настройте.", + "Setup Notification": "Настройка на известие", + "Light": "Светла", + "Dark": "Тъмна", + "Auto": "Автоматично", + "Theme - Heartbeat Bar": "Тема - поле проверки", + "Normal": "Нормално", + "Bottom": "Долу", + "None": "Без", + "Timezone": "Часова зона", + "Search Engine Visibility": "Видимост за търсачки", + "Allow indexing": "Разреши индексиране", + "Discourage search engines from indexing site": "Не позволявай на търсачките да индексират този сайт", + "Change Password": "Промяна на парола", + "Current Password": "Текуща парола", + "New Password": "Нова парола", + "Repeat New Password": "Повторете новата парола", + "Update Password": "Актуализирай паролата", + "Disable Auth": "Изключи удостоверяване", + "Enable Auth": "Активирай удостоверяване", + "disableauth.message1": "Сигурни ли сте, че желаете да {disableAuth}?", + "disable authentication": "изключите удостоверяването", + "disableauth.message2": "Използва се в случаите, когато {intendThirdPartyAuth} преди Uptime Kuma, например Cloudflare Access, Authelia или друг механизъм за удостоверяване.", + "where you intend to implement third-party authentication": "има настроен алтернативен метод за удостоверяване", + "Please use this option carefully!": "Моля, използвайте с повишено внимание!", + "Logout": "Изход от профила", + "Leave": "Отказ", + "I understand, please disable": "Разбирам. Моля, изключи", + "Confirm": "Потвърдете", + "Yes": "Да", + "No": "Не", + "Username": "Потребител", + "Password": "Парола", + "Remember me": "Запомни ме", + "Login": "Вход", + "No Monitors, please": "Все още няма монитори. Моля,", + "add one": "добавете поне един.", + "Notification Type": "Тип известие", + "Email": "Имейл", + "Test": "Тест", + "Certificate Info": "Информация за сертификат", + "Resolver Server": "Преобразуващ (DNS) сървър", + "Resource Record Type": "Тип запис", + "Last Result": "Последен резултат", + "Create your admin account": "Създаване на администриращ акаунт", + "Repeat Password": "Повторете паролата", + "Import Backup": "Импорт на архив", + "Export Backup": "Експорт на архив", + "Export": "Експорт", + "Import": "Импорт", + "respTime": "Време за отговор (ms)", + "notAvailableShort": "Няма", + "Default enabled": "Активирано по подразбиране", + "Apply on all existing monitors": "Приложи върху всички съществуващи монитори", + "Create": "Създай", + "Clear Data": "Изтрий данни", + "Events": "Събития", + "Heartbeats": "Проверки", + "Auto Get": "Авт. попълване", + "backupDescription": "Можете да архивирате всички монитори и всички известия в JSON файл.", + "backupDescription2": "PS: Имайте предвид, че данните за история и събития няма да бъдат включени.", + "backupDescription3": "Чувствителни данни, като токен кодове за известия, се съдържат в експортирания файл. Моля, бъдете внимателни с неговото съхранение.", + "alertNoFile": "Моля, изберете файл за импортиране.", + "alertWrongFileType": "Моля, изберете JSON файл.", + "Clear all statistics": "Изтрий цялата статистика", + "Skip existing": "Пропусни съществуващите", + "Overwrite": "Презапиши", + "Options": "Опции", + "Keep both": "Запази двете", + "Verify Token": "Провери токен код", + "Setup 2FA": "Настройка на 2FA", + "Enable 2FA": "Активирай 2FA", + "Disable 2FA": "Деактивирай 2FA", + "2FA Settings": "Настройка за 2FA", + "Two Factor Authentication": "Двуфакторно удостоверяване", + "Active": "Активно", + "Inactive": "Неактивно", + "Token": "Токен код", + "Show URI": "Покажи URI", + "Tags": "Етикети", + "Add New below or Select...": "Добавете нов по-долу или изберете…", + "Tag with this name already exist.": "Етикет с това име вече съществува.", + "Tag with this value already exist.": "Етикет с тази стойност вече съществува.", + "color": "цвят", + "value (optional)": "стойност (по желание)", + "Gray": "Сиво", + "Red": "Червено", + "Orange": "Оранжево", + "Green": "Зелено", + "Blue": "Синьо", + "Indigo": "Индиго", + "Purple": "Лилаво", + "Pink": "Розово", + "Search...": "Търси…", + "Avg. Ping": "Ср. пинг", + "Avg. Response": "Ср. отговор", + "Entry Page": "Основна страница", + "statusPageNothing": "Все още няма нищо тук. Моля, добавете група или монитор.", + "No Services": "Няма Услуги", + "All Systems Operational": "Всички услуги са достъпни", + "Partially Degraded Service": "Част от услугите са недостъпни", + "Degraded Service": "Всички услуги са недостъпни", + "Add Group": "Добави група", + "Add a monitor": "Добави монитор", + "Edit Status Page": "Редактиране на статус страницата", + "Go to Dashboard": "Към Таблото", + "telegram": "Telegram", + "webhook": "Уеб кука", + "smtp": "Имейл (SMTP)", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Поддържа 50+ услуги за известяване)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "Status Page": "Статус страница", + "Status Pages": "Статус страници", + "Primary Base URL": "Базов URL адрес", + "Push URL": "Генериран Push URL адрес", + "needPushEvery": "Необходимо е да извършвате заявка към този URL адрес на всеки {0} секунди.", + "pushOptionalParams": "Допълнителни, но не задължителни параметри: {0}", + "defaultNotificationName": "Моето {notification} известие ({number})", + "here": "тук", + "Required": "Задължително поле", + "Bot Token": "Бот токен", + "wayToGetTelegramToken": "Можете да получите токен от {0}.", + "Chat ID": "Чат ID", + "supportTelegramChatID": "Поддържа Direct Chat / Group / Channel's Chat ID", + "wayToGetTelegramChatID": "Можете да получите вашето чат ID, като изпратите съобщение на бота, след което е нужно да посетите този URL адрес за да го видите:", + "YOUR BOT TOKEN HERE": "ВАШИЯТ БОТ ТОКЕН ТУК", + "chatIDNotFound": "Чат ID не е намерено. Моля, първо изпратете съобщение до този бот", + "Post URL": "Post URL адрес", + "Content Type": "Тип съдържание", + "webhookJsonDesc": "{0} е подходящ за всички съвременни http сървъри, като например express.js", + "webhookFormDataDesc": "{multipart} е подходящ за PHP, нужно е да анализирате json чрез {decodeFunction}", + "secureOptionNone": "Няма (25) / STARTTLS (587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Игнорирай TLS грешките", + "From Email": "От имейл адрес", + "emailCustomSubject": "Модифициране на тема", + "To Email": "Получател имейл адрес", + "smtpCC": "Явно копие до имейл адрес:", + "smtpBCC": "Скрито копие до имейл адрес:", + "Discord Webhook URL": "Discord URL адрес на уеб кука", + "wayToGetDiscordURL": "Можете да създадете, от меню \"Настройки на сървъра\" -> \"Интеграции\" -> \"Уеб куки\" -> \"Нова уеб кука\"", + "Bot Display Name": "Име на бота, което да се показва", + "Prefix Custom Message": "Модифицирано обръщение", + "Hello @everyone is...": "Здравейте, {'@'}everyone е…", + "Webhook URL": "Уеб кука URL адрес", + "wayToGetTeamsURL": "Можете да научите как се създава URL адрес за уеб кука {0}.", + "Number": "Номер", + "Recipients": "Получатели", + "needSignalAPI": "Необходимо е да разполагате със Signal клиент с REST API.", + "wayToCheckSignalURL": "Можете да посетите този URL адрес, ако се нуждаете от помощ при настройването:", + "signalImportant": "ВАЖНО: Не можете да смесвате \"Групи\" и \"Номера\" в поле \"Получатели\"!", + "Application Token": "Токен код за приложението", + "Server URL": "URL адрес на сървъра", + "Priority": "Приоритет", + "Icon Emoji": "Иконка Емотикон", + "Channel Name": "Канал име", + "Uptime Kuma URL": "Uptime Kuma URL адрес", + "aboutWebhooks": "Повече информация относно уеб куки на: {0}", + "aboutChannelName": "Въведете името на канала в поле {0} \"Канал име\", ако желаете да заобиколите канала от уеб куката. Например: #other-channel", + "aboutKumaURL": "Ако оставите празно полето \"Uptime Kuma URL адрес\", по подразбиране ще се използва GitHub страницата на проекта.", + "emojiCheatSheet": "Подсказки за емотикони: {0}", + "User Key": "Потребителски ключ", + "Device": "Устройство", + "Message Title": "Заглавие на съобщението", + "Notification Sound": "Звуков сигнал", + "More info on:": "Повече информация на: {0}", + "pushoverDesc1": "Приоритет Спешно (2) по подразбиране изчаква 30 секунди между повторните опити и изтича след 1 час.", + "pushoverDesc2": "Ако желаете да изпратите известия до различни устройства, попълнете полето Устройство.", + "SMS Type": "SMS тип", + "octopushTypePremium": "Премиум (Бърз - препоръчителен в случай на тревога)", + "octopushTypeLowCost": "Евтин (Бавен - понякога бива блокиран от оператора)", + "checkPrice": "Тарифни планове на {0}:", + "octopushLegacyHint": "Дали използвате съвместима версия на Octopush (2011-2020) или нова версия?", + "Check octopush prices": "Тарифни планове на octopush {0}.", + "octopushPhoneNumber": "Телефонен номер (в международен формат, например: +33612345678)", + "octopushSMSSender": "SMS подател Име: 3-11 знака - букви, цифри и интервал (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea ID на устройство", + "Apprise URL": "Apprise URL адрес", + "Example:": "Пример: {0}", + "Read more:": "Научете повече: {0}", + "Status:": "Статус: {0}", + "Read more": "Научете повече", + "appriseInstalled": "Apprise е инсталиран.", + "appriseNotInstalled": "Apprise не е инсталиран. {0}", + "Access Token": "Токен код за достъп", + "Channel access token": "Канал токен код", + "Line Developers Console": "Line - Конзола за разработчици", + "lineDevConsoleTo": "Line - Конзола за разработчици - {0}", + "Basic Settings": "Основни настройки", + "User ID": "Потребител ID", + "Messaging API": "API за съобщаване", + "wayToGetLineChannelToken": "Необходимо е първо да посетите {0}, за да създадете (Messaging API) за доставчик и канал, след което можете да вземете токен кода за канал и потребителско ID от споменатите по-горе елементи на менюто.", + "Icon URL": "URL адрес за иконка", + "aboutIconURL": "Можете да предоставите линк към картинка в поле \"URL Адрес за иконка\" за да отмените картинката на профила по подразбиране. Няма да се използва, ако вече сте настроили емотикон.", + "aboutMattermostChannelName": "Можете да замените канала по подразбиране, към който публикува уеб куката, като въведете името на канала в полето \"Канал име\". Трябва да бъде активирано в настройките за уеб кука на Mattermost. Например: #other-channel", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - евтин, но бавен. Често е претоварен. Само за получатели от Полша.", + "promosmsTypeFlash": "SMS FLASH - Съобщението автоматично се показва на устройството на получателя. Само за получатели от Полша.", + "promosmsTypeFull": "SMS FULL - Високо ниво на SMS услуга. Можете да използвате Вашето име като подател (Необходимо е първо да регистрирате името). Надежден метод за съобщения тип тревога.", + "promosmsTypeSpeed": "SMS SPEED - Най-висок приоритет в системата. Много бърза и надеждна, но същевременно скъпа услуга. (Около два пъти по-висока цена в сравнение с SMS FULL).", + "promosmsPhoneNumber": "Телефонен номер (за получатели от Полша, можете да пропуснете въвеждането на код за населено място)", + "promosmsSMSSender": "SMS Подател име: Предварително регистрирано име или някое от имената по подразбиране: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu URL адрес за уеб кука", + "matrixHomeserverURL": "Сървър URL адрес (започва с http(s):// и порт по желание)", + "Internal Room Id": "ID на вътрешна стая", + "matrixDesc1": "Можете да намерите \"ID на вътрешна стая\" в разширените настройки на стаята във вашия Matrix клиент. Примерен изглед: !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Силно препоръчваме да създадете НОВ потребител и да НЕ използвате токен кодът на вашия личен Matrix потребител, т.к. той позволява пълен достъп до вашия акаунт и всички стаи към които сте се присъединили. Вместо това създайте нов потребител и го поканете само в стаята, където желаете да получавате известията. Токен код за достъп ще получите изпълнявайки {0}", + "Method": "Метод", + "Body": "Съобщение", + "Headers": "Хедъри", + "PushUrl": "Push URL адрес", + "HeadersInvalidFormat": "Заявените хедъри не са валидни JSON: ", + "BodyInvalidFormat": "Заявеното съобщение не е валиден JSON: ", + "Monitor History": "История на мониторите", + "clearDataOlderThan": "Ще се съхранява за {0} дни.", + "records": "записа", + "One record": "Един запис", + "steamApiKeyDescription": "За да мониторирате Steam Game Server се нуждаете от Steam Web-API ключ. Можете да регистрирате Вашия API ключ тук: ", + "clicksendsms": "ClickSend SMS", + "apiCredentials": "API удостоверяване", + "PasswordsDoNotMatch": "Паролите не съвпадат.", + "Current User": "Текущ потребител", + "recent": "Скорошни", + "Done": "Готово", + "Info": "Информация", + "Security": "Сигурност", + "Steam API Key": "Steam API ключ", + "Shrink Database": "Редуцирай базата данни", + "Pick a RR-Type...": "Изберете вида на ресурсния запис за мониториране…", + "Pick Accepted Status Codes...": "Изберете статус кодове, които да се считат за успешен отговор…", + "Default": "По подразбиране", + "HTTP Options": "HTTP Опции", + "Create Incident": "Създаване на инцидент", + "Title": "Заглавие", + "Content": "Съдържание", + "Style": "Стил", + "info": "информация", + "warning": "предупреждение", + "danger": "опасност", + "primary": "основен", + "light": "светъл", + "dark": "тъмен", + "Post": "Публикувай", + "Please input title and content": "Моля, въведете заглавие и съдържание", + "Created": "Създаден", + "Last Updated": "Последно обновена", + "Unpin": "Откачи", + "Switch to Light Theme": "Превключи към светла тема", + "Switch to Dark Theme": "Превключи към тъмна тема", + "Show Tags": "Покажи етикети", + "Hide Tags": "Скрий етикети", + "Description": "Описание", + "No monitors available.": "Няма налични монитори.", + "Add one": "Добави един", + "No Monitors": "Няма монитори", + "Untitled Group": "Група без заглавие", + "Services": "Услуги", + "Discard": "Отмени", + "Cancel": "Отмени", + "Powered by": "Създадено чрез", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API Потребителско име (вкл. webapi_ prefix)", + "serwersmsAPIPassword": "API Парола", + "serwersmsPhoneNumber": "Телефон номер", + "serwersmsSenderName": "SMS Подател име (регистриран през клиентския портал)", + "stackfield": "Stackfield", + "smtpDkimSettings": "DKIM Настройки", + "smtpDkimDesc": "Моля, вижте {0} на Nodemailer DKIM за инструкции.", + "documentation": "документацията", + "smtpDkimDomain": "Домейн", + "smtpDkimKeySelector": "Селектор на ключ", + "smtpDkimPrivateKey": "Частен ключ", + "smtpDkimHashAlgo": "Хеш алгоритъм (по желание)", + "smtpDkimheaderFieldNames": "Хедър ключове за подписване (по желание)", + "smtpDkimskipFields": "Хедър ключове, които да не се подписват (по желание)", + "PushByTechulus": "Push от Techulus", + "GoogleChat": "Google Chat (Само за работното пространство на Google)", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "Крайна точка на API", + "alertaEnvironment": "Среда", + "alertaApiKey": "API Ключ", + "alertaAlertState": "Състояние на тревога", + "alertaRecoverState": "Състояние на възстановяване", + "deleteStatusPageMsg": "Сигурни ли сте, че желаете да изтриете тази статус страница?", + "Proxies": "Прокси", + "default": "По подразбиране", + "enabled": "Активирано", + "setAsDefault": "Зададен по подразбиране", + "deleteProxyMsg": "Сигурни ли сте, че желаете да изтриете това прокси за всички монитори?", + "proxyDescription": "За да функционират трябва да бъдат зададени към монитор.", + "enableProxyDescription": "Това прокси няма да има ефект върху заявките за мониторинг, докато не бъде активирано. Можете да контролирате временното деактивиране на проксито от всички монитори чрез статуса на активиране.", + "setAsDefaultProxyDescription": "Това прокси ще бъде активно по подразбиране за новите монитори. Можете да го изключвате отделно за всеки един монитор.", + "Certificate Chain": "Верига на сертификата", + "Valid": "Валиден", + "Invalid": "Невалиден", + "AccessKeyId": "ID на ключ за достъп", + "SecretAccessKey": "Тайна на ключа за достъп", + "PhoneNumbers": "Телефонни номера", + "TemplateCode": "Шаблон Код", + "SignName": "Знак име", + "Sms template must contain parameters: ": "SMS шаблонът трябва да съдържа следните параметри: ", + "Bark Endpoint": "Bark крайна точка", + "WebHookUrl": "URL адрес на уеб кука", + "SecretKey": "Таен ключ", + "For safety, must use secret key": "За сигурност, трябва да се използва таен ключ", + "Device Token": "Токен за устройство", + "Platform": "Платформа", + "Huawei": "Huawei", + "High": "Висок", + "Retry": "Повтори", + "Topic": "Тема", + "WeCom Bot Key": "WeCom бот ключ", + "Setup Proxy": "Настройка на прокси", + "Proxy Protocol": "Прокси протокол", + "Proxy Server": "Прокси сървър", + "Proxy server has authentication": "Прокси сървърът е с удостоверяване", + "User": "Потребител", + "Installed": "Инсталиран", + "Not installed": "Не е инсталиран", + "Running": "Работи", + "Not running": "Не работи", + "Remove Token": "Премахни токен", + "Start": "Стартирай", + "Stop": "Спри", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Добави нова статус страница", + "Slug": "Слъг", + "Accept characters:": "Приеми символи:", + "startOrEndWithOnly": "Започва или завършва само с {0}", + "No consecutive dashes": "Без последователни тирета", + "Next": "Следващ", + "The slug is already taken. Please choose another slug.": "Този слъг вече се използва. Моля изберете друг.", + "No Proxy": "Без прокси", + "Authentication": "Удостоверяване", + "HTTP Basic Auth": "HTTP основно удостоверяване", + "New Status Page": "Нова статус страница", + "Page Not Found": "Страницата не е открита", + "Reverse Proxy": "Ревърс прокси", + "Backup": "Архивиране", + "About": "Относно", + "wayToGetCloudflaredURL": "(Свалете \"cloudflared\" от {0})", + "cloudflareWebsite": "Cloudflare уеб сайт", + "Message:": "Съобщение:", + "Don't know how to get the token? Please read the guide:": "Не знаете как да вземете токен? Моля, прочетете ръководството:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Текущата връзка може да прекъсне ако в момента сте свързани чрез \"Cloudflare Tunnel\". Сигурни ли сте, че желаете да го спрете? Моля, въведете Вашата текуща парола за да потвърдите.", + "Other Software": "Друг софтуер", + "For example: nginx, Apache and Traefik.": "Например: Nginx, Apache и Traefik.", + "Please read": "Моля, прочетете", + "Subject:": "Субект:", + "Valid To:": "Валиден до:", + "Days Remaining:": "Оставащи дни:", + "Issuer:": "Издател:", + "Fingerprint:": "Пръстов отпечатък:", + "No status pages": "Няма статус страници", + "topic": "Тема", + "topicExplanation": "MQTT тема за мониториране", + "successMessage": "Съобщение при успех", + "successMessageExplanation": "MQTT съобщение, което ще бъде считано за успех", + "Customize": "Персонализирай", + "Custom Footer": "Персонализиран долен колонтитул", + "Custom CSS": "Потребителски CSS", + "Domain Name Expiry Notification": "Известие при изтичащ домейн", + "Proxy": "Прокси", + "Date Created": "Дата на създаване", + "onebotHttpAddress": "OneBot HTTP адрес", + "onebotMessageType": "OneBot тип съобщение", + "onebotGroupMessage": "Група", + "onebotPrivateMessage": "Лично", + "onebotUserOrGroupId": "Група/Потребител ID", + "onebotSafetyTips": "С цел безопасност трябва да зададете токен код за достъп", + "PushDeer Key": "PushDeer ключ", + "Footer Text": "Текст долен колонтитул", + "Show Powered By": "Покажи \"Създадено чрез\"", + "Domain Names": "Домейни", + "signedInDisp": "Вписан като {0}", + "signedInDispDisabled": "Удостоверяването е изключено.", + "Certificate Expiry Notification": "Известие за изтичане валидността на сертификата", + "API Username": "API Потребител", + "API Key": "API Ключ", + "Recipient Number": "Номер на получателя", + "From Name/Number": "От Име/Номер", + "Leave blank to use a shared sender number.": "Оставете празно, за да използвате споделен номер на подател.", + "Octopush API Version": "Octopush API версия", + "Legacy Octopush-DM": "Octopush-DM старa версия", + "endpoint": "крайна точка", + "octopushAPIKey": "\"API ключ\" от HTTP API удостоверяване в контролния панел", + "octopushLogin": "\"Вписване\" от HTTP API удостоверяване в контролния панел", + "promosmsLogin": "API Потребителско име", + "promosmsPassword": "API Парола", + "pushoversounds pushover": "Pushover (по подразбиране)", + "pushoversounds bike": "Велосипед", + "pushoversounds bugle": "Тромпет", + "pushoversounds cashregister": "Касов апарат", + "pushoversounds classical": "Класическа музика", + "pushoversounds cosmic": "Космически", + "pushoversounds falling": "Падащ", + "pushoversounds gamelan": "Гамелан", + "pushoversounds incoming": "Входящ", + "pushoversounds intermission": "Прекъсване", + "pushoversounds magic": "Магия", + "pushoversounds mechanical": "Механичен", + "pushoversounds pianobar": "Пиано бар", + "pushoversounds siren": "Сирена", + "pushoversounds spacealarm": "Космическа аларма", + "pushoversounds tugboat": "Буксир", + "pushoversounds alien": "Извънземна аларма (дълъг)", + "pushoversounds climb": "Изкачване (дълъг)", + "pushoversounds persistent": "Постоянен (дълъг)", + "pushoversounds echo": "Pushover ехо (дълъг)", + "pushoversounds updown": "Горе долу (дълъг)", + "pushoversounds vibrate": "Само вибрация", + "pushoversounds none": "Без (тих)", + "pushyAPIKey": "Таен API ключ", + "pushyToken": "Токен на устройство", + "Show update if available": "Покажи актуализация, ако е налична", + "Also check beta release": "Проверявай и за бета версии", + "Using a Reverse Proxy?": "Използвате ревърс прокси?", + "Check how to config it for WebSocket": "Проверете как да го конфигурирате за WebSocket", + "Steam Game Server": "Steam Game сървър", + "Most likely causes:": "Най-вероятни причини:", + "The resource is no longer available.": "Ресурсът вече не е наличен.", + "There might be a typing error in the address.": "Възможно е да е допусната грешка при изписването на адреса.", + "What you can try:": "Какво можете да опитате:", + "Retype the address.": "Повторно въвеждане на адреса.", + "Go back to the previous page.": "Да се върнете към предишната страница.", + "Coming Soon": "Очаквайте скоро", + "wayToGetClickSendSMSToken": "Можете да получите API потребителско име и API ключ от {here}.", + "dnsPortDescription": "DNS порт на сървъра. По подразбиране е 53. Можете да го промените по всяко време.", + "error": "грешка", + "critical": "критично", + "wayToGetPagerDutyKey": "Можете да го получите като посетите Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Тук трябва да потърсите \"Events API V2\". Повече информация {0}", + "Integration Key": "Ключ за интегриране", + "Integration URL": "URL адрес за интеграция", + "Auto resolve or acknowledged": "Автоматично разрешаване или потвърждаване", + "do nothing": "не прави нищо", + "auto acknowledged": "автоматично потвърждаване", + "auto resolve": "автоматично разрешаване", + "Connection String": "Стринг за връзка", + "Query": "Заявка", + "settingsCertificateExpiry": "Изтичане валидността на TLS сертификата", + "certificationExpiryDescription": "HTTPS мониторите ще задействат известие, ако е наличен изтичащ TLS сертификат, през следващите:", + "ntfy Topic": "ntfy Тема", + "Domain": "Домейн", + "Workstation": "Работна станция", + "disableCloudflaredNoAuthMsg": "Тъй като сте в режим \"No Auth mode\", парола не се изисква.", + "wayToGetLineNotifyToken": "Можете да получите токен код за достъп от {0}", + "resendEveryXTimes": "Изпращай повторно на всеки {0} пъти", + "resendDisabled": "Повторното изпращане е изключено", + "Resend Notification if Down X times consecutively": "Повторно изпращане на известие, ако е недостъпен X пъти последователно", + "Bark Group": "Bark група", + "Bark Sound": "Bark звук", + "HTTP Headers": "HTTP хедъри", + "Trust Proxy": "Доверено Proxy", + "HomeAssistant": "Home Assistant", + "RadiusSecret": "Radius таен код", + "RadiusSecretDescription": "Споделен таен код между клиент и сървър", + "RadiusCalledStationId": "Повиквана станция ID", + "RadiusCalledStationIdDescription": "Идентификатор на повикваното устройство", + "RadiusCallingStationId": "Повикваща станция ID", + "RadiusCallingStationIdDescription": "Идентификатор на повикващото устройство", + "Setup Docker Host": "Настройка на Docker хост", + "Connection Type": "Тип свързване", + "Docker Daemon": "Docker демон", + "deleteDockerHostMsg": "Сигурни ли сте, че желаете да изтриете този Docker хост за всички монитори?", + "socket": "Сокет", + "tcp": "TCP / HTTP", + "Docker Container": "Docker контейнер", + "Container Name / ID": "Име на контейнер / ID", + "Docker Host": "Docker хост", + "Docker Hosts": "Docker хостове", + "trustProxyDescription": "Доверяване на 'X-Forwarded-*' хедърите. В случай, че желаете да получавате реалния IP адрес на клиента и Uptime Kuma е зад системи като Nginx или Apache, трябва да разрешите тази опция.", + "Examples": "Примери", + "Home Assistant URL": "Home Assistant URL адрес", + "Long-Lived Access Token": "Long-Lived токен за достъп", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Long-Lived Access Token можете да създадете, като кликнете върху името на профила си (долу ляво) и превъртите до най-долу, след това кликнете върху Създаване на токен.", + "Notification Service": "Услуга за известяване", + "default: notify all devices": "по подразбиране: извести всички устройства", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Списък с услугите за известяване може да бъде намерен в Home Assistant под \"Developer Tools > Services\", там потърсете \"notification\", за да намерите името на вашето устройство/телефон.", + "Automations can optionally be triggered in Home Assistant:": "Автоматизациите могат да се задействат при нужда в Home Assistant:", + "Trigger type:": "Задействане тип:", + "Event type:": "Събитие тип:", + "Event data:": "Събитие данни:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "След което изберете действие, например да превключите сцената, където RGB светлината е червена.", + "Frontend Version": "Фронтенд версия", + "Frontend Version do not match backend version!": "Фронтенд версията не съвпада с Бекенд версията!", + "Base URL": "Базов URL адрес", + "goAlertInfo": "GoAlert е приложение с отворен код за планиране на повиквания, автоматизирани ескалации и известия (като SMS или гласови повиквания). Автоматично ангажирайте точния човек, по точния начин и в точното време! {0}", + "goAlertIntegrationKeyInfo": "Вземете общ API интеграционен ключ за услугата във формат \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" обикновено стойността на параметъра token на копирания URL адрес.", + "goAlert": "GoAlert", + "backupOutdatedWarning": "Отпаднала: Тъй като са добавени много функции, тази опция за архивиране не е достатъчно поддържана и не може да генерира или възстанови пълен архив.", + "backupRecommend": "Вместо това моля, архивирайте директно дяла или папката (./data/).", + "Maintenance": "Поддръжка", + "statusMaintenance": "Поддръжка", + "Schedule maintenance": "Планиране на поддръжка", + "Affected Monitors": "Засегнати монитори", + "Pick Affected Monitors...": "Изберете засегнатите монитори…", + "Start of maintenance": "Стартирай поддръжка", + "All Status Pages": "Всички статус страници", + "Select status pages...": "Изберете статус страници…", + "recurringIntervalMessage": "Изпълнявай ежедневно | Изпълнявай всеки {0} дни", + "affectedMonitorsDescription": "Изберете монитори, попадащи в обсега на текущата поддръжка", + "affectedStatusPages": "Покажи това съобщение за поддръжка на избрани статус страници", + "atLeastOneMonitor": "Изберете поне един засегнат монитор", + "deleteMaintenanceMsg": "Сигурни ли сте, че желаете да изтриете тази поддръжка?", + "Optional": "По желание", + "squadcast": "Squadcast", + "SendKey": "SendKey", + "SMSManager API Docs": "SMSManager API Документация", + "Gateway Type": "Тип на шлюза", + "SMSManager": "SMSManager", + "You can divide numbers with": "Можете да разделяте числата с", + "or": "или", + "recurringInterval": "Интервал", + "Recurring": "Повтаряне", + "strategyManual": "Активен/Неактивен ръчно", + "warningTimezone": "Използва се часовата зона на сървъра", + "weekdayShortMon": "Пон", + "weekdayShortTue": "Вт", + "weekdayShortWed": "Ср", + "weekdayShortThu": "Чет", + "weekdayShortFri": "Пет", + "weekdayShortSat": "Съб", + "weekdayShortSun": "Нед", + "dayOfWeek": "Ден", + "dayOfMonth": "Дата", + "lastDay": "Последен ден", + "lastDay1": "Последен ден от месеца", + "lastDay2": "2-ри последен ден на месеца", + "lastDay3": "3-ти последен ден на месеца", + "lastDay4": "4-ти последен ден на месеца", + "No Maintenance": "Няма поддръжка", + "pauseMaintenanceMsg": "Сигурни ли сте, че желаете да направите пауза?", + "maintenanceStatus-under-maintenance": "В режим поддръжка", + "maintenanceStatus-inactive": "Неактивна", + "maintenanceStatus-scheduled": "Планирана", + "maintenanceStatus-ended": "Приключена", + "maintenanceStatus-unknown": "Неизвестна", + "Display Timezone": "Покажи часова зона", + "Server Timezone": "Часова зона на сървъра", + "statusPageMaintenanceEndDate": "Край", + "enableGRPCTls": "Разреши изпращане на gRPC заявка с TLS връзка", + "grpcMethodDescription": "Името на метода се форматира в \"camelCase\", например sayHello, check, и т.н.", + "smseagle": "SMSEagle", + "smseagleTo": "Тел. номер(а)", + "smseagleGroup": "Име на група/и от тел. указател", + "smseagleContact": "Име(на) от тел. указател", + "smseagleRecipientType": "Получател тип", + "smseagleRecipient": "Получател(и) (при повече от един разделете със запетая)", + "smseagleToken": "API токен за достъп", + "smseagleUrl": "Вашият SMSEagle URL на устройството", + "smseagleEncoding": "Изпрати като Unicode (по подразбиране=GSM-7)", + "smseaglePriority": "Приоритет на съобщението (0-9, най-висок приоритет = 9)", + "IconUrl": "Икона URL адрес", + "webhookAdditionalHeadersTitle": "Допълнителни хедъри", + "webhookAdditionalHeadersDesc": "Задава допълнителни хедъри, изпратени с уеб куката. Всеки хедър трябва да бъде дефиниран като JSON ключ/стойност.", + "Enable DNS Cache": "(Отпаднала) Активирай DNS кеширане за HTTP(S) монитори", + "Enable": "Активирай", + "Disable": "Деактивирай", + "dnsCacheDescription": "Възможно е да не работи в IPv6 среда - деактивирайте, ако срещнете проблеми.", + "Single Maintenance Window": "Единичен времеви интервал за поддръжка", + "Maintenance Time Window of a Day": "Времеви интервал от деня за поддръжка", + "Effective Date Range": "Ефективен интервал от дни (по желание)", + "Schedule Maintenance": "Планирай поддръжка", + "Date and Time": "Дата и час", + "DateTime Range": "Изтрий времеви интервал", + "Strategy": "Стратегия", + "Free Mobile User Identifier": "Free Mobile потребителски идентификатор", + "Free Mobile API Key": "Free Mobile API ключ", + "Enable TLS": "Активирай TLS", + "Proto Service Name": "Proto име на услугата", + "Proto Method": "Proto метод", + "Proto Content": "Proto съдържание", + "Economy": "Икономичен", + "Lowcost": "Евтин", + "high": "висок", + "General Monitor Type": "Общ тип монитори", + "Passive Monitor Type": "Пасивен тип монитори", + "Specific Monitor Type": "Специфичен тип монитори", + "ZohoCliq": "ZohoCliq", + "wayToGetZohoCliqURL": "Можете да научите как се създава URL адрес за уеб кука {0}.", + "Kook": "Kook", + "wayToGetKookBotToken": "Създайте приложение и вземете вашия бот токен на {0}", + "wayToGetKookGuildID": "Превключете в 'Developer Mode' в 'Kook' настройките, след което десен клик върху 'guild' за да вземете неговото 'ID'", + "Guild ID": "Guild ID", + "Help": "Помощ", + "Game": "Игра", + "Custom": "Потребителски", + "infiniteRetention": "Задайте стойност 0 за безкрайно съхранение.", + "Monitor": "Монитор | Монитори", + "dataRetentionTimeError": "Периодът на съхранение трябва да е 0 или по-голям", + "confirmDeleteTagMsg": "Сигурни ли сте, че желаете да изтриете този етикет? Мониторите, свързани с него, няма да бъдат изтрити.", + "promosmsAllowLongSMS": "Позволи дълъг SMS", + "Packet Size": "Размер на пакет", + "Custom Monitor Type": "Потребителски тип монитор", + "loadingError": "Данните не могат да бъдат изтеглени. Моля, опитайте отново по-късно.", + "plugin": "Плъгин | Плъгини", + "install": "Инсталирай", + "installing": "Инсталиране", + "uninstall": "Деинсталирай", + "uninstalling": "Деинсталиране", + "confirmUninstallPlugin": "Сигурни ли сте, че желаете да деинсталирате този плъгин?", + "markdownSupported": "Поддържа се Markdown синтаксис. Ако използвате HTML, избягвайте началните интервали, за да предотвратите проблеми с форматирането.", + "Google Analytics ID": "Google Analytics ID", + "Edit Tag": "Редактиране на етикет", + "Learn More": "Научете повече", + "Server Address": "Сървър адрес", + "notificationRegional": "Регионални", + "Body Encoding": "Кодировка на тялото", + "telegramMessageThreadID": "(По избор) Thread ID на съобщението", + "telegramMessageThreadIDDescription": "Незадължителен уникален идентификатор за целевата нишка от съобщения (тема) на форума; само за форумни супергрупи", + "telegramProtectContent": "Защита на препращане/записване", + "telegramProtectContentDescription": "Ако е активирано, съобщенията от ботове в Telegram ще бъдат защитени от препращане и запазване.", + "telegramSendSilentlyDescription": "Изпраща съобщението тихо. Потребителите ще получат известие без звук.", + "telegramSendSilently": "Изпрати тихо", + "Clone Monitor": "Клониране на монитор", + "Clone": "Клонирай", + "cloneOf": "Клониран {0}", + "Expiry": "Валиден до", + "Expiry date": "Дата на изтичане", + "Add Another": "Добави друг", + "Key Added": "Ключът е добавен", + "Add API Key": "Добави API ключ", + "No API Keys": "Няма API ключове", + "apiKey-active": "Активен", + "Expires": "Изтича на", + "deleteAPIKeyMsg": "Сигурни ли сте, че желаете да изтриете този API ключ?", + "Generate": "Генерирай", + "API Keys": "API Ключове", + "Don't expire": "Не изтича", + "Continue": "Продължи", + "apiKeyAddedMsg": "Вашият API ключ е добавен. Моля, запишете го, тъй като той няма да бъде показан отново.", + "apiKey-expired": "Изтекъл", + "apiKey-inactive": "Неактивен", + "disableAPIKeyMsg": "Сигурни ли сте, че желаете да деактивирате този API ключ?", + "pagertreeUrgency": "Спешност", + "pagertreeSilent": "Тих", + "pagertreeLow": "Ниска", + "pagertreeHigh": "Висока", + "pagertreeResolve": "Автоматично разрешаване", + "pagertreeDoNothing": "Не прави нищо", + "wayToGetPagerTreeIntegrationURL": "След като създадете интеграция на Uptime Kuma в PagerTree, копирайте крайната точка. За пълни подробности вижте {0}", + "pagertreeIntegrationUrl": "URL Адрес за интеграция", + "pagertreeMedium": "Средна", + "pagertreeCritical": "Критична", + "Add New Tag": "Добави нов етикет", + "lunaseaTarget": "Цел", + "lunaseaDeviceID": "ID на устройството", + "lunaseaUserID": "ID на потребител", + "twilioAccountSID": "Профил SID", + "twilioAuthToken": "Удостоверяващ токен / Тайна на API ключа", + "twilioFromNumber": "От номер", + "twilioToNumber": "Към номер", + "sameAsServerTimezone": "Kато часовата зона на сървъра", + "startDateTime": "Старт Дата/Час", + "endDateTime": "Край Дата/Час", + "cronSchedule": "График: ", + "invalidCronExpression": "Невалиден \"Cron\" израз: {0}", + "cronExpression": "Израз тип \"Cron\"", + "statusPageRefreshIn": "Ще се обнови след: {0}", + "ntfyUsernameAndPassword": "Потребителско име и парола", + "ntfyAuthenticationMethod": "Метод за удостоверяване", + "pushoverMessageTtl": "TTL на съобщението (секунди)", + "Open Badge Generator": "Отвори генератора на баджове", + "Badge Generator": "Генератор на баджове на {0}", + "Badge Type": "Тип бадж", + "Badge Duration": "Продължителност на баджа", + "Badge Prefix": "Префикс за стйността на баджа", + "Badge Label Color": "Цвят на етикета на баджа", + "Badge Color": "Цвят на баджа", + "Badge Label Suffix": "Суфикс на етикета на значката", + "Badge Up Color": "Цвят на баджа за достъпен", + "Badge Down Color": "Цвят на баджа за недостъпен", + "Badge Maintenance Color": "Цвят на баджа за поддръжка", + "Badge Warn Color": "Цвят на баджа за предупреждение", + "Badge Warn Days": "Дни за показване на баджа", + "Badge Style": "Стил на баджа", + "Badge value (For Testing only.)": "Стойност на баджа (само за тест.)", + "Badge URL": "URL адрес на баджа", + "Monitor Setting": "Настройка на монитор {0}", + "Show Clickable Link": "Покажи връзка, която може да се кликне", + "Show Clickable Link Description": "Ако е отбелязано, всеки който има достъп до тази статус страница, ще може да достъпва мониторирания URL адрес.", + "Badge Label": "Етикет на баджа", + "Badge Suffix": "Суфикс за стойността на баджа", + "Badge Label Prefix": "Префикс на етикета на значката", + "Badge Pending Color": "Цвят на баджа за изчакващ", + "Badge Down Days": "Колко дни баджът да не се показва", + "Group": "Група", + "Monitor Group": "Монитор група", + "Cannot connect to the socket server": "Не може да се свърже със сокет сървъра", + "Reconnecting...": "Повторно свързване...", + "Edit Maintenance": "Редактиране на поддръжка", + "Home": "Главна страница", + "noGroupMonitorMsg": "Не е налично. Първо създайте групов монитор.", + "Close": "Затвори", + "nostrRelays": "Nostr релета", + "nostrRelaysHelp": "Един URL адрес за реле на ред", + "nostrSender": "Частен ключ на изпращача (nsec)", + "nostrRecipients": "Публични ключове на получатели (npub)", + "nostrRecipientsHelp": "npub формат, по един на ред", + "chromeExecutable": "Chrome/Chromium изпълним файл", + "chromeExecutableAutoDetect": "Автоматично откриване", + "chromeExecutableDescription": "За потребителите на Docker, ако Chromium все още не е инсталиран, инсталирането и показването на резултата от теста може да отнеме няколко минути. Заема 1GB дисково пространство.", + "Invert Keyword": "Обърнат режим за ключова дума", + "invertKeywordDescription": "При търсене ключовата дума трябва да отсъства, а не да присъства.", + "webhookBodyPresetOption": "Предварителна настройка - {0}", + "webhookBodyCustomOption": "Персонализирано тяло", + "webhookCustomBodyDesc": "Дефинирайте персонализирано HTTP тяло за заявката. Приемат се шаблонни променливи {msg}, {heartbeat}, {monitor}.", + "Request Body": "Тяло на заявката", + "twilioApiKey": "API ключ (по избор)", + "Expected Value": "Очаквана стойност", + "Json Query": "Заявка тип JSON", + "Badge Duration (in hours)": "Времетраене на баджа (в часове)", + "Badge Preview": "Преглед на баджа", + "Notify Channel": "Канал за известяване", + "aboutNotifyChannel": "Каналът за известяване ще задейства известие на настолен компютър или мобилно устройство за всички членове на канала, независимо дали тяхната наличност е в състояние активен или отсъстващ.", + "filterActive": "Активен", + "filterActivePaused": "На пауза", + "Kafka Brokers": "Kafka брокери", + "Enter the list of brokers": "Въведете списъка с брокери", + "Press Enter to add broker": "Натиснете Enter, за да добавите брокер", + "Kafka Topic Name": "Име на темата за Kafka", + "Enable Kafka SSL": "Активирай Kafka SSL", + "Enable Kafka Producer Auto Topic Creation": "Активирай автоматично създаване на темa в Kafka Producer", + "Kafka Producer Message": "Съобщение на Kafka Producer", + "Kafka SASL Options": "Опции на Kafka SASL", + "Mechanism": "Механизъм", + "Pick a SASL Mechanism...": "Изберете SASL механизъм…", + "Authorization Identity": "Идентичност за оторизиране", + "AccessKey Id": "AccessKey ID", + "Secret AccessKey": "Таен ключ за достъп", + "Session Token": "Токен за сесия", + "tailscalePingWarning": "За да използвате Tailscale Ping монитор, трябва да инсталирате Uptime Kuma без Docker и също така да инсталирате Tailscale клиент на вашия сървър.", + "Server URL should not contain the nfty topic": "URL адресът на сървъра не трябва да съдържа nfty темата", + "FlashDuty Severity": "Степен на тежест", + "showCertificateExpiry": "Показвай изтичащ сертификат", + "noOrBadCertificate": "Няма/лош сертификат", + "Select": "Избери", + "selectedMonitorCount": "Избрано: {0}", + "wayToGetFlashDutyKey": "За да интегрирате Uptime Kuma с Flashduty: Отидете на Канали > Изберете канал > Интеграции > Добавяне на нова интеграция, изберете Uptime Kuma и копирайте Push URL адреса.", + "PushDeer Server": "PushDeer сървър", + "pushDeerServerDescription": "Оставете празно, за да използвате официалния сървър", + "Check/Uncheck": "Постави/Премахни отметка", + "Request Timeout": "Време за изтичане на заявката", + "timeoutAfter": "Времето изтича след {0} секунди", + "styleElapsedTime": "Изминало време под лентата с проверки", + "styleElapsedTimeShowNoLine": "Покажи (без ред)", + "gamedigGuessPort": "Gamedig: Познай порт", + "gamedigGuessPortDescription": "Портът, използван от Valve Server Query Protocol, може да е различен от клиентския порт. Опитайте това, ако мониторът не може да се свърже с вашия сървър.", + "styleElapsedTimeShowWithLine": "Покажи (с ред)", + "enableNSCD": "Активирай NSCD (Name Service Cache Daemon) за кеширане на всички DNS заявки", + "dbName": "Име на базата данни", + "setupDatabaseChooseDatabase": "Коя база данни желаете да използвате?", + "Saved.": "Запазено.", + "toastErrorTimeout": "Време за изчакване на известията при грешка", + "toastSuccessTimeout": "Време за изчакване на известията при успех", + "monitorToastMessagesDescription": "Известието за състояние на монитора изчезва след определено време в секунди. Задаване на -1, деактивира времето за изчакване. Задаване на 0 деактивира тост известията.", + "monitorToastMessagesLabel": "Мониторинг на известията при промяна на състоянието", + "setupDatabaseEmbeddedMariaDB": "Не е нужно да настройвате нищо. Този Docker имидж автоматично е вградил и конфигурирал MariaDB за Вас. Uptime Kuma ще се свърже с тази база данни чрез Unix сокет.", + "setupDatabaseMariaDB": "Свързване към външна MariaDB база данни. Трябва да зададете информацията за връзка с базата данни.", + "setupDatabaseSQLite": "Обикновен файл с база данни, препоръчително при маломащабен тип внедрявания. Преди v2.0.0 Uptime Kuma използва SQLite като база данни по подразбиране.", + "Bark API Version": "Версия на Bark API", + "pushViewCode": "Как да използвате Push монитор? (Вижте кода)", + "pushOthers": "Други", + "programmingLanguages": "Програмни езици", + "authInvalidToken": "Невалиден токен.", + "authUserInactiveOrDeleted": "Потребителят е неактивен или изтрит.", + "authIncorrectCreds": "Неправилно потребителско име или парола.", + "2faAlreadyEnabled": "2FA вече е активирано.", + "2faEnabled": "2FA е активирано.", + "2faDisabled": "2FA е деактивирано.", + "successAdded": "Добавен успешно.", + "successPaused": "Успешно поставен на пауза.", + "successDeleted": "Успешно изтрит.", + "successEdited": "Успешно редактиран.", + "successBackupRestored": "Резервното копие е възстановено успешно.", + "successDisabled": "Успешно деактивиран.", + "successEnabled": "Успешно активиран.", + "tagNotFound": "Етикетът не е намерен.", + "successResumed": "Успешно възобновен.", + "successAuthChangePassword": "Паролата е актуализирана успешно.", + "foundChromiumVersion": "Намерен Chromium/Chrome. Версия: {0}", + "Reset Token": "Нулиране на токен код", + "leave blank for default subject": "оставете празно за използване на тема по подразбиране", + "emailCustomBody": "Персонализирано съдържание", + "emailCustomisableContent": "Съдържание подлежащо на персонализиране", + "leave blank for default body": "оставете празно за използване на съдържание по подразбиране", + "emailTemplateServiceName": "Име на услугата", + "emailTemplateHostnameOrURL": "Име на хост или URL адрес", + "emailTemplateStatus": "Статус", + "emailTemplateMonitorJSON": "обект описващ монитора", + "emailTemplateHeartbeatJSON": "обект описващ проверката", + "liquidIntroduction": "Шаблонността се постига чрез езика за шаблони Liquid. Моля, вижте {0} за инструкции за употреба.", + "templateLimitedToUpDownCertNotifications": "налично само за известия от тип Достъпен/Недостъпен/Изтичане вал. на сертификата", + "smtpLiquidIntroduction": "Следните две полета могат да бъдат създадени чрез шаблони, посредством езика \"Liquid templating \". Моля, вижте {0} относно инструкции за употреба. Наличните променливи са:", + "templateMsg": "съобщение на известието", + "templateHeartbeatJSON": "обект описващ проверките", + "templateMonitorJSON": "обект описващ монитора", + "templateLimitedToUpDownNotifications": "налично само за известия от тип Достъпен/Недостъпен", + "emailTemplateMsg": "съобщение на известието", + "emailTemplateLimitedToUpDownNotification": "налично само за проверки от тип Достъпен/Недостъпен, в противен случай null", + "GrafanaOncallUrl": "Grafana Oncall URL адрес", + "noDockerHostMsg": "Не е наличен. Първо настройте \"Docker\" хост.", + "DockerHostRequired": "Моля, задайте \"Docker\" хоста за този монитор.", + "Browser Screenshot": "Екранна снимка на браузър", + "remoteBrowserToggle": "По подразбиране Chromium работи в контейнера Uptime Kuma. Можете да използвате отдалечен браузър, като превключите този ключ.", + "remoteBrowsersDescription": "Отдалечените браузъри са алтернатива на локално стартиран Chromium. Настройва се с услуга като \"browserless.io\" или можете да свържете с Ваш собствен", + "Remove the expiry notification": "Премахни деня за известяване при изтичане", + "Add a new expiry notification day": "Добави нов ден за известяване при изтичане", + "setup a new monitor group": "настройка на нова група от монитори", + "openModalTo": "отвори модален прозорец към {0}", + "Add a domain": "Добави домейн", + "Remove domain": "Премахни домейн '{0}'", + "Remote Browsers": "Отдалечени браузъри", + "Remote Browser": "Отдалечен браузър", + "Add a Remote Browser": "Добави отдалечен браузър", + "Remote Browser not found!": "Отдалеченият браузър не е намерен!", + "self-hosted container": "самостоятелно хостван контейнер", + "useRemoteBrowser": "Използвай отдалечен браузър", + "deleteRemoteBrowserMessage": "Сигурни ли сте, че желаете да изтриете този отдалечен браузър за всички монитори?", + "successKeyword": "Ключова дума за успех", + "successKeywordExplanation": "MQTT ключова дума, която ще се счита за успех", + "ntfyPriorityHelptextAllExceptDown": "Всички събития се изпращат с този приоритет, с изключение на {0}-събития, които имат приоритет {1}", + "statusPageSpecialSlugDesc": "Специален слъг {0}: тази страница ще бъде показана, когато не е предоставен слъг", + "ntfyPriorityHelptextAllEvents": "Всички събития се изпращат с максимален приоритет", + "settingUpDatabaseMSG": "Настройка на базата данни. Може да отнеме известно време, моля, бъдете търпеливи.", + "Search monitored sites": "Търсене на мониторирани сайтове", + "What is a Remote Browser?": "Какво е отдалечен браузър?", + "Channel access token (Long-lived)": "Токен код за достъп до канал (дълготраен)", + "Your User ID": "Вашето потребителско ID", + "documentationOf": "{0} Документация", + "wayToGetHeiiOnCallDetails": "Как да получите Trigger ID и API ключове е обяснено в {documentation}", + "To Phone Number": "До телефонен номер", + "gtxMessagingToHint": "Международен формат, като първо въведете знака \"+\" ({e164}, {e212} or {e214})", + "gtxMessagingApiKeyHint": "Можете да намерите вашия API ключ в: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "От телефонен номер / Изходен Адрес на Пътя на Предаване (ИАПП)", + "gtxMessagingFromHint": "На мобилни телефони, вашите получатели виждат ИАПП показан като подател на съобщението. Позволени са до 11 буквено-цифрови знака, кратък код, местен дълъг код или международни номера ({e164}, {e212} or {e214})", + "Alphanumeric (recommended)": "Буквено-цифров (препоръчително)", + "Telephone number": "Телефонен номер", + "Originator type": "Тип автор", + "Originator": "Автор", + "cellsyntOriginator": "Вижда се на мобилния телефон на получателя като автор на съобщението. Позволените стойности и функция зависят от параметъра - тип автор.", + "Destination": "Дестинация", + "Allow Long SMS": "Разреши дълъг SMS", + "cellsyntSplitLongMessages": "Раздели дългите съобщения до 6 части. 153 x 6 = 918 знака.", + "max 15 digits": "Максимум 15 цифри", + "max 11 alphanumeric characters": "Максимум 11 буквено-цифрови знака", + "cellsyntOriginatortypeAlphanumeric": "Буквено-цифров низ (максимум 11 буквено-цифрови знака). Получателите не могат да отговорят на съобщението.", + "cellsyntOriginatortypeNumeric": "Числова стойност (максимум 15 цифри) с телефонен номер в международен формат, без предхождащо въвеждане на 00 (примерен номер в България 088 123 45 67 трябва да бъде зададен като 359881234567). Получателите могат да отговорят на съобщението.", + "cellsyntDestination": "Телефонен номер на получателя в международен формат, предхождан от 00, последвано от код на държавата, напр. 00359881234567 за номер в България 088 123 45 67 (максимум 17 цифри). Максимум 25 000 получатели, разделени със запетая, на HTTP заявка.", + "callMeBotGet": "Тук можете да генерирате крайна точка за {0}, {1} и {2}. Имайте предвид, че може да получите ограничения в опитите. Ограниченията в опитите изглеждат: {3}", + "wayToGetWhapiUrlAndToken": "Можете да получите URL адреса на API и токена, като влезете в желания от вас канал от {0}", + "whapiRecipient": "Телефонен номер / ID на контакт / ID на група", + "API URL": "URL адрес на API", + "wayToWriteWhapiRecipient": "Телефонният номер с международния префикс, но без знака плюс в началото ({0}), ID на контакта ({1}) или ID на групата ({2}).", + "locally configured mail transfer agent": "локално конфигуриран \"mail transfer agent\"", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Или въведете името на хоста на сървъра, към който желаете да се свържете, или {localhost}, ако възнамерявате да използвате {local_mta}", + "Mentioning": "Споменаване", + "Don't mention people": "Не споменавай хора", + "Mention group": "Споменавай {group}", + "wayToGetSevenIOApiKey": "Посетете таблото за управление в app.seven.io > developer > api key > зеленият бутон за добавяне", + "receiverSevenIO": "Номер на получател", + "senderSevenIO": "Номер или име на подател", + "receiverInfoSevenIO": "Ако номерът на получателя не се намира в Германия, трябва да добавите кода на държавата пред номера (напр. за код на държавата 1 от САЩ използвайте 117612121212 вместо 017612121212)", + "apiKeySevenIO": "SevenIO API ключ", + "Host URL": "Хост URL адрес", + "Command": "Команда", + "mongodbCommandDescription": "Изпълни MongoDB команда срещу базата данни. За информация относно наличните команди, вижте {documentation}", + "Bitrix24 Webhook URL": "Bitrix24 Webhook URL адрес", + "wayToGetBitrix24Webhook": "Можете да създадете webhook, като следвате стъпките на {0}", + "bitrix24SupportUserID": "Въведете Вашето потребителско ID в Bitrix24. Можете да го намерите от връзката, като отидете в профила на потребителя.", + "Refresh Interval": "Интервал на опресняване", + "Refresh Interval Description": "Статус страницата ще извършва пълно опресняване на всеки {0} секунди", + "e.g. {discordThreadID}": "напр. {discordThreadID}", + "whatHappensAtForumPost": "Създава нова публикация във форума. Това НЕ публикува съобщения в съществуваща публикация. За да публикувате в съществуваща публикация, използвайте \"{option}\"", + "wayToGetDiscordThreadId": "Получаването на ID на тема/публикация във форум е подобно на получаването на ID на канал. Прочетете повече за това как да получите ID-та {0}", + "Select message type": "Изберете тип съобщение", + "Send to channel": "Изпрати до канал", + "ignoreTLSErrorGeneral": "Игнорирай TLS/SSL грешка зa връзка", + "Create new forum post": "Създай нова публикация във форум", + "postToExistingThread": "Публикувай в съществуваща тема/публикация във форум", + "forumPostName": "Име на публикацията във форума", + "threadForumPostID": "ID на публикация в темата/форум", + "smspartnerApiurl": "Можете да намерите вашия API ключ в таблото на {0}", + "smspartnerPhoneNumber": "Телефон номер(а)", + "smspartnerPhoneNumberHelptext": "Номерът задължително е в международен формат {0}, {1}. Отделните номера трябва да бъдат разделени посредством {2}", + "smspartnerSenderName": "Име на изпращащия на SMS", + "smspartnerSenderNameInfo": "Трябва да е между 3..=11 стандартни знака", + "wayToGetThreemaGateway": "Можете да се регистрирате за Threema Gateway {0}.", + "threemaRecipient": "Получател", + "threemaRecipientType": "Тип получател", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypePhone": "Телефонен номер", + "threemaRecipientTypeEmail": "Имейл адрес", + "threemaSenderIdentity": "Gateway-ID", + "threemaSenderIdentityFormat": "8 знака, обикновено започва с *", + "threemaApiAuthenticationSecret": "Gateway-ID Тайна фраза", + "threemaRecipientTypeIdentityFormat": "8 знака", + "threemaRecipientTypePhoneFormat": "E.164, без водещ +", + "threemaBasicModeInfo": "Забележка: Тази интеграция използва Threema Gateway в основен режим (сървърно базирано криптиране). Допълнителни подробности можете да намерите {0}.", + "apiKeysDisabledMsg": "API ключовете са деактивирани, защото удостоверяването е деактивирано.", + "jsonQueryDescription": "Анализира и извлича конкретни данни от JSON отговора на сървъра, използвайки JSON заявка или чрез \"$\" за необработения отговор, ако не очаква JSON. След това резултатът се сравнява с очакваната стойност като низове. Вижте {0} за документация и използвайте {1}, за да експериментирате със заявки.", + "starts with": "започва с", + "less than or equal to": "по-малко или равно на", + "now": "сега", + "time ago": "преди {0}", + "-year": "-година", + "Json Query Expression": "Json израз на заявка", + "and": "и", + "cacheBusterParam": "Добави параметъра {0}", + "cacheBusterParamDescription": "Произволно генериран параметър за пропускане на кешове.", + "Community String": "Общностен низ", + "snmpCommunityStringHelptext": "Този низ функционира като парола за удостоверяване и контрол на достъпа до устройства с активиран SNMP. Сравнете го с конфигурацията на вашето SNMP устройство.", + "OID (Object Identifier)": "OID (Идентификатор на обект)", + "snmpOIDHelptext": "Въведете OID за сензора или състоянието, които искате да мониторирате. Използвайте инструменти за управление на мрежата като MIB браузъри или SNMP софтуер, ако не сте сигурни за OID.", + "SNMP Version": "SNMP Версия", + "Please enter a valid OID.": "Моля, въведете валиден OID.", + "Host Onesender": "Onesender хост", + "Token Onesender": "Onesender токен", + "Recipient Type": "Тип получател", + "Private Number": "Частен номер", + "privateOnesenderDesc": "Уверете се, че телефонният номер е валиден. За да изпратите съобщение на личен телефонен номер, напр.: 628123456789", + "groupOnesenderDesc": "Уверете се, че GroupID е валиден. За да изпратите съобщение в група, напр.: 628123456789-342345", + "Group ID": "ID на групата", + "wayToGetOnesenderUrlandToken": "Можете да получите URL адреса и токена, като посетите уебсайта на Onesender. Повече информация {0}", + "Add Remote Browser": "Добави отдалечен браузър", + "New Group": "Нова група", + "Group Name": "Име на групата", + "OAuth2: Client Credentials": "OAuth2: Идентификационни данни на клиента", + "Condition": "Условие", + "Authentication Method": "Метод за удостоверяване", + "Authorization Header": "Хедър за оторизация", + "Form Data Body": "Тяло на формата за данни", + "OAuth Token URL": "URL адрес на OAuth токена", + "Client ID": "ID на клиента", + "Client Secret": "Тайна на клиента", + "OAuth Scope": "Обхват на OAuth", + "Optional: Space separated list of scopes": "По избор: разделен с интервал списък с обхвати", + "Go back to home page.": "Обратно към началната страница.", + "No tags found.": "Няма намерени етикети.", + "Lost connection to the socket server.": "Изгубена връзка със сокет сървъра.", + "Cannot connect to the socket server.": "Не може да се свърже със сокет сървъра.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 URL адрес на уеб кука", + "signl4Docs": "Повече информация относно конфигуриране на SIGNL4 и получаване на URL адрес за уеб кука SIGNL4 в {0}.", + "Conditions": "Условия", + "conditionAdd": "Добави условие", + "conditionDelete": "Изтрий условие", + "conditionAddGroup": "Добави група", + "conditionDeleteGroup": "Изтрий група", + "conditionValuePlaceholder": "Стойност", + "contains": "съдържа", + "equals": "равно на", + "not equals": "не е равно на", + "not contains": "не съдържа", + "not starts with": "не започва с", + "ends with": "завършва с", + "not ends with": "не завършва с", + "less than": "по-малко от", + "greater than": "по-голямо от", + "greater than or equal to": "по-голямо или равно на", + "record": "запис", + "CurlDebugInfo": "За да отстраните грешки в монитора, можете или да поставите това в терминала на Вашата машина, или в терминала на машината, на която работи \"Uptime Kuma\" и да видите заявката.{newiline}Моля, вземете под внимание мрежовите разлики като {firewalls}, {dns_resolvers} или {docker_networks}.", + "shrinkDatabaseDescriptionSqlite": "Инициира {vacuum} за база данни тип SQLite. Функцията {auto_vacuum} вече е активирана, но това не дефрагментира базата данни, нито препакетира отделните страници на базата данни по начина, по който го прави командата {vacuum}.", + "ignoredTLSError": "TLS/SSL грешките са игнорирани", + "Debug": "Отстраняване на грешки", + "Copy": "Копирай", + "CopyToClipboardError": "Неуспешно копиране в клипборда: {error}", + "CopyToClipboardSuccess": "Копирано!", + "firewalls": "защитни стени", + "dns resolvers": "DNS преобразуватели", + "docker networks": "docker мрежи", + "CurlDebugInfoOAuth2CCUnsupported": "Пълният oauth клиентски идентификационен поток не се поддържа в {curl}.{newline}Моля, вземете токен на носител и го предайте чрез опцията {oauth2_bearer}.", + "CurlDebugInfoProxiesUnsupported": "Поддръжката на прокси в горната команда {curl} в момента не е внедрена.", + "Message format": "Формат на съобщението", + "Send rich messages": "Изпращай съобщения в \"rich\" формат", + "Sound": "Звук", + "Notification Channel": "Канал за известяване", + "Alphanumerical string and hyphens only": "Само буквено-цифров низ и тирета", + "Arcade": "Arcade", + "Correct": "Правилно", + "Fail": "Грешка", + "Harp": "Арфа", + "Reveal": "Разкрий", + "Bubble": "Балон", + "Doorbell": "Звънец на врата", + "Flute": "Флейта", + "Money": "Пари", + "Scifi": "Nаучна фантастика", + "Clear": "Премахни", + "Elevator": "Асансьор", + "Guitar": "Китара", + "Pop": "Поп", + "Custom sound to override default notification sound": "Персонализиран звук, заменящ звука за известяване по подразбиране", + "Time Sensitive (iOS Only)": "Зависещи от часа (само за iOS)", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Известията от типа \"Зависещи от часа\" ще бъдат доставени незабавно, дори ако устройството е в режим „Не безпокойте“.", + "From": "От", + "Can be found on:": "Можте да се откриете на: {0}", + "The phone number of the recipient in E.164 format.": "Телефонният номер на получателя във формат E.164.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Идентификационен номер на подателя на текста или телефонен номер във формат E.164, в случай, че желаете да получавате отговори.", + "rabbitmqNodesRequired": "Моля, задайте възлите за този монитор.", + "rabbitmqNodesInvalid": "Моля, използвайте пълния URL адрес (започващ с 'http') за RabbitMQ възли.", + "RabbitMQ Password": "RabbitMQ парола", + "RabbitMQ Username": "RabbitMQ потребител", + "SendGrid API Key": "SendGrid API ключ", + "Separate multiple email addresses with commas": "Разделяйте отделните имейл адреси със запетаи", + "RabbitMQ Nodes": "Възли за управление на RabbitMQ", + "rabbitmqNodesDescription": "Въведете URL адреса на възлите за управление на RabbitMQ, включително протокол и порт. Пример: {0}", + "rabbitmqHelpText": "За да използвате монитора, ще трябва да активирате добавката за управление във вашата настройка на RabbitMQ. За повече информация моля, вижте {rabitmq_documentation}.", + "aboutSlackUsername": "Променя показваното име на подателя на съобщението. Ако желаете да споменете някого, вместо това го включете в приятелското име.", + "YZJ Robot Token": "YZJ Robot токен код", + "YZJ Webhook URL": "YZJ Уеб кука URL адрес", + "templateServiceName": "име на услугата", + "templateHostnameOrURL": "име на хоста или URL адрес", + "Plain Text": "Обикновен текст", + "Message Template": "Шаблон за съобщение", + "templateStatus": "статус", + "telegramUseTemplate": "Използвай персонализиран шаблон за съобщение", + "telegramUseTemplateDescription": "Ако е активирано, съобщението ще бъде изпратено чрез персонализиран шаблон.", + "telegramTemplateFormatDescription": "Telegram позволява използването на различни \"markup\" езици за съобщенията. Вижте Telegram {0} за детайли.", + "Template Format": "Формат на шаблона", + "wayToGetWahaApiUrl": "Вашият WAHA URL адрес.", + "wahaSession": "Сесия", + "wahaChatId": "Чат ID (телефонен номер / ID на контакт / ID на група)", + "wayToGetWahaApiKey": "API ключът, е стойността на променливата WHATSAPP_API_KEY, която сте използвали за стартиране на WAHA.", + "wayToWriteWahaChatId": "Телефонният номер с международния префикс, но без знака плюс в началото ({0}), ID на контакта ({1}) или ID на групата ({2}). Известията се изпращат до това чат ID от WAHA сесия.", + "wayToGetWahaSession": "От тази сесия WAHA изпраща известия до чат ID. Можете да го намерите в таблото за управление на WAHA.", + "telegramServerUrlDescription": "За премахване на API бот ограниченията за Telegram или за получаване на достъп в блокирани зони (Китай, Иран и др.). За повече информация щракнете върху {0}. По подразбиране: {1}", + "telegramServerUrl": "(По избор) URL адрес на сървъра", + "Font Twemoji by Twitter licensed under": "Шрифт Twemoji от Twitter, лицензиран под", + "the smsplanet documentation": "документацията на smsplanet", + "Phone numbers": "Телефонни номера", + "Sender name": "Име на подател", + "smsplanetNeedToApproveName": "Трябва да бъде одобрен в клиентския панел", + "smsplanetApiToken": "Токен код за SMSPlanet API", + "smsplanetApiDocs": "Подробна информация, за получаване на API токен кодове, можете да намерите в {the_smsplanet_documentation}.", + "defaultFriendlyName": "Нов монитор", + "Use HTML for custom E-mail body": "Използвайте HTML за персонализирано тяло на имейл", + "smseagleGroupV2": "ID(та) на група от телефонния указател", + "smseagleContactV2": "ID(та) на контакти в телефонния указател", + "smseagleMsgTts": "Обаждане чрез Гласово синтезиран текст", + "smseagleTtsModel": "ID на модел за Гласово синтезиран текст", + "smseagleApiv2": "APIv2 (препоръчва се за нови интеграции)", + "SpugPush Template Code": "Код на шаблона", + "FlashDuty Push URL": "Push URL адрес", + "FlashDuty Push URL Placeholder": "Копирай от страницата за интегриране на предупреждения", + "pingCountLabel": "Максимален брой пакети", + "pingCountDescription": "Брой пакети за изпращане преди спиране", + "pingPerRequestTimeoutDescription": "Това е максималното време на изчакване (в секунди), преди да се приеме, че един пинг пакет е загубен", + "pingGlobalTimeoutDescription": "Общото време в секунди преди пинга да спре, независимо от изпратените пакети", + "pingIntervalAdjustedInfo": "Интервалът е коригиран въз основа на броя на пакетите, глобалното време за изчакване и времето за изчакване на пинг", + "smtpHelpText": "'SMTPS' тества дали SMTP/TLS работи; 'Игнорирай TLS' се свързва използвайки обикновен текст; 'STARTTLS' се свързва, като издава команда STARTTLS и проверява сертификата на сървъра. Нито един от тях не изпраща имейл.", + "smseagleMsgType": "Тип съобщение", + "smseagleMsgSms": "SMS съобщение (по подразбиране)", + "smseagleMsgRing": "Прозвъняване", + "smseagleMsgTtsAdvanced": "Разширено повикване чрез Гласово синтезиран текст", + "smseagleDuration": "Времетраене (в секунди)", + "smseagleApiType": "Версия на API", + "smseagleApiv1": "APIv1 (за съществуващи проекти и обратна съвместимост)", + "smseagleDocs": "Проверете документацията или наличността на APIv2: {0}", + "smseagleComma": "При няколко, трябва да бъдат разделени със запетая", + "pingNumericLabel": "Числен изход", + "pingNumericDescription": "Ако е отбелязано, ще се извеждат IP адреси вместо символни имена на хостове", + "pingGlobalTimeoutLabel": "Глобално време за изчакване", + "pingPerRequestTimeoutLabel": "Време за изчакване на пинг", + "Custom URL": "Персонализиран URL адрес", + "customUrlDescription": "Ще се използва като URL адрес, върху който може да се кликне, вместо този на монитора.", + "OneChatAccessToken": "Токен код за достъп до OneChat", + "OneChatUserIdOrGroupId": "OneChat ID на потребител или ID на група", + "OneChatBotId": "Бот ID на OneChat", + "Disable URL in Notification": "Деактивиране на URL адрес в известие", + "ntfyPriorityHelptextPriorityHigherThanDown": "Нормалният приоритет трябва да е по-висок от приоритета {0}. Приоритет {1} е по-висок от приоритета {2} на {0}", + "ntfyPriorityDown": "Приоритет за събития от тип Недостъпен", + "tagAlreadyOnMonitor": "Този етикет (име и стойност) вече е на монитора или чака добавяне.", + "tagNameExists": "Вече съществува системен етикет с това име. Изберете го от списъка или използвайте друго име.", + "Add Another Tag": "Добави друг етикет", + "Add Tags": "Добави етикети", + "tagAlreadyStaged": "Този етикет (име и стойност) вече е подготвен за тази група.", + "Staged Tags for Batch Add": "Подготвени етикети за групово добавяне", + "Clear Form": "Изчисти формата", + "pause": "Пауза", + "Happy Eyeballs algorithm": "Алгоритъм \"Happy Eyeballs\"", + "Ip Family": "IP Семейство", + "ipFamilyDescriptionAutoSelect": "Използва {happyEyeballs} за определяне на IP семейството.", + "Manual": "Ръчно", + "OAuth Audience": "OAuth аудитория", + "Optional: The audience to request the JWT for": "По желание: Аудиторията, за която да се поиска JWT", + "mqttWebSocketPath": "MQTT Уеб сокет път", + "mqttWebsocketPathInvalid": "Моля, използвайте валиден формат на пътя за Уеб сокет", + "Path": "Път", + "mqttWebsocketPathExplanation": "Уеб сокет път за MQTT през Уеб сокет връзки (напр. /mqtt)", + "mqttHostnameTip": "Моля, използвайте този формат {hostnameFormat}", + "issueWithGoogleChatOnAndroidHelptext": "Това позволява да се заобиколят грешки, като например {issuetackerURL}", + "Template plain text instead of using cards": "Шаблон с обикновен текст вместо използване на карти", + "Events cleared successfully": "Събитията са изчистени успешно.", + "No monitors found": "Не са намерени монитори.", + "Clear All Events": "Изчисти всички събития", + "clearAllEventsMsg": "Сигурни и сте, че желаете да изтриете всички събития за всички монитори? Това премахва перманентно съпровождащите данни и не може да бъде отменено.", + "Could not clear events": "Неуспешен опит за изчистване на {failed}/{total} събития", + "wayToWriteEvolutionRecipient": "Телефонният номер с международен префикс, но без знака плюс в началото ({0}), ID на контакта ({1}) или ID на на групата ({2}).", + "wayToGetEvolutionUrlAndToken": "Можете да получите API URL адреса, като влезете в желания от вас канал от {0}", + "evolutionRecipient": "Телефонен номер / ID на контакт / ID на група", + "evolutionInstanceName": "Име на инстанция", + "brevoApiKey": "Brevo API ключ", + "brevoApiHelp": "Създай API ключ тук: {0}", + "brevoFromEmail": "От имейл", + "brevoFromName": "От име", + "brevoToEmail": "До имейл", + "brevoCcEmail": "Имейл с явно копие", + "brevoBccEmail": "Имейл със скрито копие", + "brevoSubject": "Тема", + "brevoLeaveBlankForDefaultSubject": "оставете празно за тема по подразбиране", + "brevoLeaveBlankForDefaultName": "оставете празно за име по подразбиране", + "brevoSeparateMultipleEmails": "Разделяйте имейл адреси със запетаи", + "Nextcloud host": "Nextcloud хост", + "Conversation token": "Conversation токен", + "Bot secret": "Bot таен код", + "Send UP silently": "Безшумно известяване за Достъпен", + "Send DOWN silently": "Безшумно известяване за Недостъпен", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Инсталирането на Nextcloud Talk бот изисква администраторски достъп до сървъра.", + "auto-select": "Автоматичен избор", + "supportBaleChatID": "Поддръжка на директен чат / група / чат ID на канала", + "wayToGetBaleChatID": "Можете да получите вашия чат ID, като изпратите съобщение до бота и отидете на този URL адрес, за да видите chat_id:", + "wayToGetBaleToken": "Можете да получите токен код от {0}.", + "Mention Mobile List": "Списък със споменаващи мобилни устройства", + "Mention User List": "Списък със споменаващи потребителски ID-та", + "Dingtalk Mobile List": "Списък с мобилни устройства", + "Dingtalk User List": "Списък с потребителски ID-та", + "Enter a list of userId": "Въведете списък с потребителски ID-та", + "Enter a list of mobile": "Въведете списък с мобилни устройства", + "Invalid mobile": "Невалиден мобилен телефон [{mobile}]", + "Invalid userId": "Невалидно потребителско ID [{userId}]", + "Maximum Retries": "Максимален брой повторни опити", + "Number of retry attempts if webhook fails": "Брой повторни опити (на всеки 60-180 секунди), ако уеб куката се првали.", + "HTTP Method": "HTTP Метод", + "webhookPostMethodDesc": "POST е подходящ за повечето съвременни HTTP сървъри.", + "descriptionHelpText": "Показва се на вътрешното табло. Markdown е разрешен и се обработва (запазва интервалите и отстъпите) преди показване.", + "webhookGetMethodDesc": "GET изпраща данни като параметри на заявката и не позволява конфигуриране на тяло. Полезно за задействане на Uptime Kuma Push монитори.", + "deleteGroupMsg": "Сигурни ли сте, че желаете да изтриете тази група?", + "deleteChildrenMonitors": "Също така изтрий директните подмонитори и подподмонитори, ако има такива | Също така изтрий всички {count} директни подмонитори и техните подподмонитори, ако има такива", + "Clone Maintenance": "Клонирай поддръжката", + "ariaPauseMaintenance": "Пауза на този график за поддръжка", + "ariaResumeMaintenance": "Възстанови този график за поддръжка", + "ariaCloneMaintenance": "Създай копие на този график за поддръжка", + "ariaEditMaintenance": "Редактирай този график за поддръжка", + "ariaDeleteMaintenance": "Изтрий този график за поддръжка", + "Template ID": "ID на шаблона", + "wayToGetClickSMSIRTemplateID": "Вашият шаблон трябва да съдържа поле {uptkumaalert}. Можете да създадете нов шаблон {here}.", + "Recipient Numbers": "Номера на получател", + "twilioMessagingServiceSID": "SID на услугата за съобщения (по избор)", + "twilioApiKeyHelptext": "API ключът е по избор, но е препоръчителен. Можете да предоставите или Account SID и AuthToken от страницата на TwilioConsole, или Account SID и двойката Api Key и Api Key secret", + "twilloMessagingServiceSIDHelptext": "Въведете SID на вашата услуга за съобщения тук, ако използвате {twillo_messaging_service_help_link} за управление на податели и функции", + "showOnlyLastHeartbeat": "Показвай само последната проверка", + "Allow Notifications": "Разреши известяванията", + "Webpush Helptext": "Web push работи само със SSL (HTTPS) връзки. За iOS устройства уеб страницата трябва да бъде предварително добавена към началния екран.", + "Notifications Enabled": "Известяванията са активирани", + "Browser not supported": "Браузърът не се поддържа", + "Unable to get permission to notify": "Неуспешно получаване на разрешение за известяване (заявката е отказана или игнорирана).", + "ignoreSecWebsocketAcceptHeaderDescription": "Позволява на сървъра да не отговаря със Sec-WebSocket-Accept хедър, ако надстройката на websocket е успешна.", + "Network API for Notification Channel": "OMA RESTful мрежов API ключ за канал за известия", + "Extensible Messaging and Presence Protocol": "WebSocket транспорт за Extensible Messaging and Presence Protocol (XMPP)", + "Softvelum Low Delay Protocol": "Softvelum протокол с ниска степен закъснение", + "Declarative Resource Protocol": "Declarative Resource протокол", + "WebSocket Transport for JMAP": "WebSocket транспорт за JMAP (JSON Meta Application Protocol)", + "Ignore Sec-WebSocket-Accept header": "Игнорирай {0} хедър", + "wsSubprotocolDescription": "Въведете списък с подпротоколи, разделени със запетаи. За повече информация относно подпротоколите - моля, вижте {documentation}", + "WebSocket Application Messaging Protocol": "WAMP (The WebSocket Application Messaging Protocol)", + "Session Initiation Protocol": "WebSocket транспорт за SIP (Session Initiation Protocol)", + "Web Process Control Protocol": "Web Process Control Protocol (WPCP)", + "Advanced Message Queuing Protocol": "Advanced Message Queuing Protocol (AMQP) 1.0+", + "jsflow": "jsFlow pubsub/queue протокол", + "Reverse Web Process Control": "Reverse Web Process Control Protocol (RWPCP)", + "Smart Home IP": "SHIP - Smart Home IP", + "Miele Cloud Connect Protocol": "Miele Cloud Connect протокол", + "Push Channel Protocol": "Push Channel протокол", + "Message Session Relay Protocol": "WebSocket транспорт за MSRP (Message Session Relay Protocol)", + "Binary Floor Control Protocol": "WebSocket транспорт за BFCP (Binary Floor Control Protocol)", + "OPC UA Connection Protocol": "OPC UA протокол за свързване", + "OPC UA JSON Encoding": "OPC UA JSON кодировка", + "Swindon Web Server Protocol": "Swindon уеб сървърен протокол (JSON кодировка)", + "Broadband Forum User Services Platform": "USP (Broadband Forum User Services Platform)", + "Constrained Application Protocol": "Constrained Application Protocol (CoAP)", + "Softvelum WebSocket signaling protocol": "Softvelum WebSocket Signaling протокол", + "Cobra Real Time Messaging Protocol": "Cobra Real Time Messaging протокол", + "BACnet Secure Connect Hub Connection": "BACnet Secure Connect Hub свързване", + "BACnet Secure Connect Direct Connection": "BACnet Secure Connect директна свръзка", + "ITU-T T.140 Real-Time Text": "ITU-T T.140 текст в реално време", + "Done.best IoT Protocol": "Done.best IoT протокол", + "Collection Update": "The Collection Update Websocket подпротокол", + "Text IRC Protocol": "Text IRC протокол", + "Binary IRC Protocol": "Binary IRC протокол", + "Penguin Statistics Live Protocol v3": "Penguin Statistics Live протокол v3 (Protobuf кодировка)", + "minimumIntervalWarning": "Интервали под 20 секунди може да доведат до лоша производителност.", + "lowIntervalWarning": "Наистина ли желаете да зададете стойността на интервала под 20 секунди? Производителността може да се влоши, особено ако има голям брой монитори.", + "certHostnameMismatch": "Хоста от сертификата не съвпада с URL адреса на монитора.", + "settingsDomainExpiry": "Валидност на домейна", + "labelDomainExpiry": "Домейнът изт.", + "labelDomainNameExpiryNotification": "Известие при изтичане на домейн", + "domainExpiryDescription": "Изпрати известие в случай, че предстои изтичане на домейн до:", + "Select All": "Избери всички", + "Deselect All": "Премахни избор за всички", + "Subprotocol": "Подпротокол", + "Duration (Minutes)": "Продължителност (минути)", + "SMTP Security": "SMTP сигурност", + "Ignore STARTTLS": "Игнорирай STARTTLS", + "Use STARTTLS": "Използвай STARTTLS", + "Enter the list of nodes": "Въведете списъка с нодове за управление на RabbitMQ", + "Press Enter to add node": "Натиснете Enter, за да добавите нод", + "resendApiKey": "Изпрати отново API ключ", + "resendApiHelp": "Създай API ключ тук {0}", + "resendFromName": "От име", + "resendFromEmail": "От имейл", + "resendLeaveBlankForDefaultName": "оставете празно за име по подразбиране", + "resendToEmail": "До имейл", + "resendSubject": "Тема", + "Subprotocol(s)": "Подпротокол(и)", + "wsCodeDescription": "За повече информация относно кодовете за състояние, моля, вижте {rfc6455}", + "imageResetConfirmation": "Изображението се нулира до настройки по подразбиране", + "systemService": "Системна услуга", + "systemServiceName": "Име на услугата", + "systemServiceDescription": "Проверява дали системната услуга {service_name} е активна", + "systemServiceDescriptionLinux": "Проверява дали Linux systemd услугата {service_name} е активна", + "systemServiceDescriptionWindows": "Проверява дали Windows Service Manager {service_name} работи", + "systemServiceCommandHint": "Използвана команда: {command}", + "systemServiceExpectedOutput": "Очакван резултат: \"{0}\"", + "maxPing": "Максимален пинг", + "minPing": "Минимален пинг", + "avgPing": "Среден пинг", + "sipsakPingWarning": "За да използвате SIP Options Ping монитора, трябва да инсталирате Uptime Kuma без Docker, както и да инсталирате Sipsak клиента на вашия сървър.", + "serwersmsRecipientType": "Тип получател", + "serwersmsRecipientTypePhone": "Телефонен номер", + "serwersmsRecipientTypeGroup": "Група", + "serwersmsGroupId": "ID на групата", + "serwersmsGroupIdHelptext": "ID или ID-та в клиентския панел. Тези идентификатори могат да бъдат изтеглени с помощта на групи за действие / индекс или чрез копиране от групата за редактиране в клиентския панел.", + "invalidURL": "Невалиден URL адрес", + "hostnameCannotBeIP": "Името на DNS хоста не може да бъде IP адрес. Да не би да имате предвид, че желаете да използвате полето за преобразуване?", + "invalidHostnameOrIP": "Невалидно име на хост или IP адрес. Името на хоста трябва да е валидно FQDN. Не може да се използва заместващ символ. Може да съдържа долна черта или да завършва с точка.", + "invalidDNSHostname": "Невалидно име на хост. Името на хоста трябва да е валидно FQDN. Може да бъде заместващ знак, да съдържа долна черта или да завършва с точка.", + "wildcardOnlyForDNS": "Заместващите имена на хостове се поддържат само за DNS монитори.", + "RSS Title": "Заглавие на RSS", + "Leave blank to use status page title": "Оставете празно, за да използвате заглавието на статус страницата", + "notificationUniversal": "Универсален", + "notificationChatPlatforms": "Чат платформи", + "notificationPushServices": "Push услуги", + "notificationSmsServices": "SMS услуги", + "notificationEmail": "Имейл", + "notificationIncidentManagement": "Управление на инциденти", + "notificationHomeAutomation": "Домашна автоматизация", + "notificationOther": "Други интеграции", + "year": "година | години", + "Analytics Type": "Тип аналитики", + "Analytics ID": "ID за аналитики", + "Analytics Script URL": "URL адрес на скрипта за аналитики", + "screenshot of the website": "Екранна снимка на уебсайта", + "domain_expiry_unsupported_missing_target": "За този монитор не е конфигуриран валиден домейн или име на хост", + "domain_expiry_unsupported_monitor_type": "Мониториране за изтичащ домейн, не се поддържа за този тип монитор", + "domain_expiry_unsupported_invalid_domain": "Конфигурираната стойност \"{hostname}\" не е валидно име на домейн", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" е твърде кратко за име на домейн от първо ниво", + "Resolver Server(s)": "Сървър(и) за преобразуване", + "HeadersInvalidFormatBecause": "Хедърите на заявката не са валиден JSON, поради {error}", + "BodyInvalidFormatBecause": "Тялото на хедъра не е валиден JSON файл, поради {error}", + "steamApiKeyDescriptionAt": "За мониториране на Steam Game Server Ви е необходим Steam Web-API ключ. Можете да регистрирате Вашият API ключ на {url}", + "checkPriceAt": "Проверете цените за {service} на {url}", + "You can divide numbers with commas or semicolons": "Можете да разделяте числа с {comma} или {semicolon}", + "password": "Парола", + "halopsa_username_desc": "Потребител за удостоверяване с Halo PSA уеб кука", + "message": "съобщение", + "json_value": "JSON стойност", + "noMonitorsSelectedWarning": "Създавате режим на поддръжка, без включени монитори. Наистина ли желаете да продължите?", + "noMonitorsOrStatusPagesSelectedError": "Не може да се създаде поддръжка, без засегнати монитори или страници за състояние", + "Plausible": "Plausible", + "domain_expiry_unsupported_public_suffix": "Домейнът \"{domain}\" няма валиден публичен суфикс", + "Halo PSA Webhook URL": "URL адрес за Halo PSA уеб кука", + "Halo PSA": "Halo PSA", + "halopsa_webhook_url_desc": "Въведете URL адреса за уеб куката от Вашия Halo PSA Integration Runbook (Конфигурация > Интеграции > Персонализирани интеграции > Integration Runbooks). Изберете 'Може да се стартира само от Halo и от публична крайна точка', когато създавате уеб куката.", + "username": "Потребител", + "Badge Link Generator Helptext": "Бадж линковете са налични за всички монитори, присвоени на публични страници за състояние. За повече информация, моля, вижте {documentation}.", + "Open Badge Link Generator": "Отвори бадж линк генератора", + "Badge Link Generator": "Бадж линк генератор за {0}", + "Google": "Google", + "resendLeaveBlankForDefaultSubject": "Оставете празно за тема по подразбиране", + "OptionalParameters": "Незадължителни параметри", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Поради ограничения от оператора, активирайте незадължителните променливи с риск да не бъде доставено", + "aliyun-template-requirements-and-parameters": "Шаблонът за aliyun SMS трябва да съдържа параметри: {parameters}", + "aliyun-template-optional-parameters": "Незадължителни параметри: {parameters}", + "Matomo": "Matomo", + "Umami": "Umami", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" е IP адрес. Мониторирането за изтичащ домейна изисква име на домейн", + "halopsa_password_desc": "Парола за удостоверяване с Halo PSA уеб кука", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Мониторирането за изтичане на домейна не е налично за \".{publicSuffix}\", защото IANA не включва RDAP услуга", + "ntfyCall": "Телефонно обаждане", + "ntfyCallHelptext": "Инициирай телефонно обаждане, когато се задейства предупреждение. Задайте 'да', за да използвате първия си потвърден номер, или въведете конкретен телефонен номер (напр. +12223334444). Изисква ntfy Pro и потвърден телефонен номер.", + "Basic radio toggle button group": "Основна група превключватели тип радио бутони", + "GRPC Options": "GRPC опции", + "Splunk Rest URL": "Splunk Rest URL адрес", + "Message Format": "Формат на съобщението", + "Severity": "Тежест", + "Basic checkbox toggle button group": "Основна група превключватели тип квадратчета за маркиране", + "Setup Instructions": "Инструкции за настройка", + "smscTranslit": "smscTranslit", + "Region": "Регион", + "End": "Край", + "Show this Maintenance Message on which Status Pages": "Покажи това съобщение за поддръжка на кои страници за състояние", + "Metadata": "Метаданни", + "Endpoint": "Крайна точка", + "Details": "Детайли", + "None (Successful Connection)": "Няма (Успешна връзка)", + "halopsa_setup_step2": "Конфигуриране на \"runbook actions\" за обработка на предупреждения (напр. Създаване на \"Ticket\")", + "TLS Alerts": "TLS предупреждения", + "Expected TLS Alert": "Очаквано TLS предупреждение", + "expectedTlsAlertDescription": "Изберете TLS предупреждението, което очаквате сървърът да върне. Използвайте {code}, за да проверите дали крайните точки на mTLS отхвърлят връзки без клиентски сертификати. Вижте {link} за подробности.", + "mtls-auth-server-key-placeholder": "Тяло на ключа", + "To Number": "До номер", + "GrafanaOncallURL": "Grafana Oncall URL адрес", + "Never": "Никога", + "PushDeer Server URL": "PushDeer URL адрес на сървър", + "mtls-auth-server-cert-label": "Сертификат", + "halopsa_setup_step1": "Създаване на Integration Runbook в HaloPSA (Configuration → Integrations → Integration Runbooks)", + "mtls-auth-server-ca-label": "Сертифициращ орган (CA)", + "mtls-auth-server-ca-placeholder": "CA сървър", + "SSL/TLS": "SSL/TLS", + "Service Name": "Име на услугата", + "TLS Alert Spec": "RFC 8446", + "Check Type": "Тип проверка", + "playground": "тестова среда", + "Sort by certificate expiry": "Сортирай по дата вал. сертификат", + "System Service": "Системна услуга", + "enableSSL": "Активиране SSL/TLS", + "mariadbUseSSLHelptext": "Активира използването на криптирана връзка с базата данни. Изисква се за повечето облачни бази данни.", + "mariadbCaCertificateLabel": "CA Сертификат", + "mariadbCaCertificateHelptext": "За да използвате самоподписани сертификати, поставете CA сертификата в PEM формат. Оставете празно, ако вашата база данни използва сертификат, подписан от публичен CA.", + "mtls-auth-server-cert-placeholder": "Тяло на сертификата", + "mtls-auth-server-key-label": "Ключ", + "halopsa_setup_step3": "Копирайте URL адреса на уеб куката и го поставете над текстовото поле", + "halopsa_setup_step4": "Изберете основно удостоверяване и създайте потребителско име и парола. Въведете или поставете тези, потребителско име и парола, над тестовите полета", + "Clear current filters": "Изчисти текущите филтри", + "Sort options": "Опции за сортиране", + "Sort by status": "Сортирай по статус", + "Sort by name": "Сортирай по име", + "Sort by uptime": "Сортирай по достъпност", + "passwordTooWeak": "Паролата е твърде слаба. Трябва да съдържа букви и цифри и да бъде с с дължина поне 6 знака.", + "saveErrorResponseForNotifications": "Запази отговора за HTTP грешка за известия", + "saveResponseForNotifications": "Запази HTTP отговор за успех за известия", + "saveResponseDescription": "Съхранява HTTP отговора и го прави достъпен в шаблони за известия, като {templateVariable}", + "responseMaxLength": "Отговор - максимална дължина (байтове)", + "responseMaxLengthDescription": "Максимален размер на данни за отговор, които да се съхраняват. Задайте на 0 за неограничен размер. По-големите отговори ще бъдат отрязани. По подразбиране: 1024 (1KB)", + "unknownDays": "Неизвестен брой дни", + "Monitors": "{n} Монитор | {n} Монитори", + "Disable STARTTLS": "Деактивирай STARTTLS", + "disableSTARTTLSDescription": "Активирайте тази опция за SMTP сървъри, които не поддържат STARTTLS. Това ще изпраща имейли през некриптирана връзка.", + "Actions": "Действия", + "pausedMonitorsMsg": "На пауза {n} монитор | На пауза {n} монитора", + "versionIs": "Версия: {version}", + "logoutCurrentUser": "Изход {username}", + "createdAt": "Създаден: {date}", + "lastUpdatedAt": "Последно обновен: {date}", + "lastUpdatedAtFromNow": "Последно обновен: {date} ({fromNow})", + "frontendVersionIs": "Версия на фронтенда: {version}", + "Examples:": "Примери: {0}", + "cronScheduleDescription": "График: {description}", + "Only retry if status code check fails": "Повторен опит, само при кода за състояние - неуспешен", + "retryOnlyOnStatusCodeFailureDescription": "Ако е активирано, повторните опити ще се случват само когато проверката на HTTP кода за състояние е неуспешна (напр. сървърът не работи). Ако проверката на кода за състояние е успешна, но JSON заявката е неуспешна, мониторът ще бъде маркиран като неработещ незабавно без повторни опити.", + "selectedMonitorCountMsg": "избран: {n} | избрани: {n}", + "selectMonitorMsg": "Изберете монитори върху които да се приложат действията", + "selectAllMonitorsAria": "Избери всички монитори", + "deselectAllMonitorsAria": "Премахни избор на всички монитори", + "noMonitorsResumedMsg": "Няма възстановено монитори (нямаше неактивен)", + "resumedMonitorsMsg": "Възобновен {n} монитор | Възобновено {n} монитора", + "deletedMonitorsMsg": "Премахнат {n} монитор | Премахнати {n} монитора", + "noMonitorsPausedMsg": "Няма монитори, поставени на пауза (нямаше активен)", + "No incidents recorded": "Няма регистрирани инциденти", + "Load More": "Зареди още", + "Loading...": "Зареждане...", + "Pin this incident": "Закачи този инцидент", + "Incident description": "Описание на инцидента", + "Incident not found or access denied": "Инцидентът не е намерен или достъпът е отказан", + "Past Incidents": "Минали инциденти", + "Incident title": "Заглавие на инцидента", + "Pinned incidents are shown prominently on the status page": "Закачените инциденти се показват на видно място на статус страницата", + "Edit Incident": "Редактиране на инцидент", + "Resolve": "Разрешаване", + "Resolved": "Разрешен", + "deleteIncidentMsg": "Сигурни ли сте, че желаете да изтриете този инцидент?", + "Certificate Chain:": "Сертификат - верига:", + "Please input content": "Моля, въведете съдържани", + "Please input title": "Моля, въведете заглавие", + "deleteMonitorsMsg": "Сигурни ли сте, че желаете да изтриете избраните монитори?", + "days": "{n} ден | {n} дни", + "hours": "{n} час | {n} часа", + "minutes": "{n} минута | {n} минути", + "minuteShort": "{n} мин | {n} мин", + "years": "{n} година | {n} години", + "dateCreatedAtFromNow": "Дата на създаване: {date} ({fromNow})", + "snmpV3Username": "SNMPv3 Потребителско име", + "Google Apps Script Webhook URL": "URL адрес на уеб куката за Google Apps Script", + "Quick Setup Guide": "Ръководство за бърза настройка", + "Open your Google Spreadsheet": "Отворете Вашата Google Spreadsheet таблица", + "Go to Extensions → Apps Script": "Отидете на Extensions → Apps Script", + "Paste the script code (see below)": "Поставете кода на скрипта (вижте по-долу)", + "Click Deploy → New deployment → Web app": "Щтракнете върху Deploy → New deployment → Web app", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Задайте 'Execute as: Me' and 'Who has access: Anyone'", + "Google Apps Script Code": "Google Apps Script код", + "Copy to Clipboard": "Копирай в клипборда", + "Copied to clipboard!": "Копирано в клипборда!", + "Failed to copy to clipboard": "Неуспешно копиране в буферната памет", + "WeCom Mentioned Mobile List": "Списък мобилни, споменати в WeCom", + "Expand All Groups": "Разпъни всички групи", + "Collapse All Groups": "Свий всички групи", + "Deploy a Google Apps Script as a web app and paste the URL here": "Разгърнете Google Apps Script като уеб приложение и поставете URL адреса тук", + "Copy the web app URL and paste it above": "Копирайте URL адреса на уеб приложението и го поставете по-горе", + "WeCom Mentioned Mobile List Description": "Въведете телефонни номера, които да споменете. Разделете няколко номера със запетаи. Използвайте {'@'}all, за да споменете всички.", + "mariadbSocketPathDetectedHelptext": "Свързване с базата данни, както е посочено чрез променливата на средата {0}.", + "Suppress Notifications": "Заглуши известията", + "discordSuppressNotificationsHelptext": "Когато е активирано, съобщенията ще бъдат публикувани в канала, но няма да задействат push или известия на работния плот за получателите.", + "octopushEndpoint": "octopush (крайна точка адрес: {url})", + "milliseconds": "{n} милисекунда | {n} милисекунди", + "Screenshot Delay": "Забавяне при екранна снимка (изчаква {milliseconds})", + "slug is not found": "Слъгът не е намерен", + "bulkDeleteErrorMsg": "Неуспешно изтриване на {n} монитор | Неуспешно изтриване на {n} монитора", + "screenshotDelayWarning": "По-високите стойности поддържат браузъра отворен по-дълго време, което може да увеличи използването на памет при голям брой едновременни монитори.", + "screenshotDelayDescription": "По желание изчакайте толкова милисекунди, преди да направите екранна снимка. Максимум: {maxValueMs}ms (0.5 × interval).", + "domain_expiry_unsupported_is_icann": "Домейнът \"{domain}\" не е кандидат за наблюдение на изтичане на домейн, тъй като неговият публичен суфикс \".{publicSuffix}\" не се управлява от ICANN", + "Sets end time based on start time": "Задава крайния час въз основа на началния час", + "Please set start time first": "Моля, първо задайте начален час", + "legacyOctopushEndpoint": "Унаследен Octopush-DM (крайна точка адрес: {url})", + "Globalping API Token": "Globalping API токен", + "globalpingApiTokenDescription": "Вземете Вашия Globalping API токен на {0}.", + "GlobalpingHostname": "Публично достъпна цел на измерване. Обикновено име на хост или IPv4/IPv6 адрес, в зависимост от типа на измерването.", + "GlobalpingLocationDocs": "Пълна документация за въвеждане на местоположение", + "GlobalpingIpFamilyInfo": "IP версията, която да се използва. Разрешено е само ако целта е име на хост.", + "Protocol": "Протокол", + "account settings": "настройки на профила", + "Location": "Местоположение", + "Monitor Subtype": "Подтип на монитора", + "Check for": "Провери за", + "Globalping - Access global monitoring probes": "Globalping - Достъп до глобални системи за мониторинг", + "GlobalpingDescription": "Globalping предоставя достъп до хиляди системи за мониторинг, хоствани от общността, за провеждане на мрежови тестове и измервания. Зададен е лимит от 250 теста на час за всички анонимни потребители. За да удвоите лимита до 500 теста на час, моля, запазете вашия токен в {accountSettings}.", + "GlobalpingLocation": "Полето за местоположение приема континенти, държави, региони, градове, ASN, интернет доставчици или облачни региони. Можете, да комбинирате филтри с {plus} (напр. {amazonPlusGermany} или {comcastPlusCalifornia}). Ако латентността е важен показател, използвайте филтри, за да стесните местоположението до малък регион, за да избегнете пикове. {fullDocs}.", + "GlobalpingResolverInfo": "IPv4/IPv6 адрес или пълно квалифицирано име на домейн (FQDN). По подразбиране е използван локалният мрежов преобразувател на монитора. Можете да промените сървъра за преобразуване по всяко време.", + "discordMessageFormat": "Формат на съобщението", + "discordMessageFormatMinimalist": "Минималистичен (кратък статус)", + "discordMessageFormatCustom": "Персонализиран шаблон", + "discordUseMessageTemplate": "Използвай персонализиран шаблон за съобщение", + "discordMessageTemplate": "Шаблон за съобщение", + "Cloud ID": "Cloud ID", + "API Token": "API токен код", + "See Jira Cloud Docs": "Вижте документацията на Jira Cloud", + "slackIncludeGroupName": "Включи име за група монитори", + "slackIncludeGroupNameDescription": "Ако е активирано, пътят до групата монитори ще бъде включен в известията, за да се помогне за разграничаването на монитори с едно и също име в различните групи.", + "slackUseTemplate": "Използвайте персонализиран шаблон за съобщение", + "slackUseTemplateDescription": "Ако е активирано, съобщението ще бъде изпратено с помощта на персонализиран шаблон. Можете да използвате Liquid templating, за да включите информация за групата монитори чрез monitorJSON.path или monitorJSON.pathName.", + "see Jira Cloud Docs": "вижте документацията на Jira Cloud", + "aboutJiraCloudId": "Повече информация за Jira Cloud ID: {0}", + "ntfyUseTemplate": "Персонализиране на шаблони за известия", + "discordMessageFormatNormal": "Нормално (богат набор от вградени файлове)", + "discordUseMessageTemplateDescription": "Ако е активирано, съобщението ще бъде изпратено с помощта на персонализиран шаблон (LiquidJS). Оставете празно, за да използвате Uptime Kuma формата по подразбиране.", + "templateAvailableVariables": "Налични променливи", + "example": "Пример", + "Result": "Резултат", + "Jira Service Management": "Управление на услугите в Jira", + "ntfyUseTemplateDescription": "Активирайте това, за да персонализирате заглавията и съобщенията за известия, използвайки шаблони на LiquidJS", + "ntfyCustomTitle": "Шаблон за персонализирано заглавие", + "ntfyCustomMessage": "Шаблон за персонализирано съобщение", + "ntfyNotificationTemplateFallback": "Оставете празно, за да използвате Uptime Kuma формата по подразбиране", + "halopsa_field_message": "Пълно предупредително съобщение със състояние и подробности", + "halopsa_field_timestamp": "Времева щампа на събитието във формат ISO 8601", + "halopsa_id_usage_hint": "💡 Съвет: Използвайте \"monitor_id\" за надеждно съпоставяне на известия с тикети и \"heartbeat_id' за проследяване на историята на събитията", + "Webhook Payload Fields": "Полета за \"прикачен товар\" на уеб куката", + "halopsa_payload_desc": "Следните полета се изпращат към Вашата \"Halo PSA\" уеб кука:", + "halopsa_field_title": "Заглавие на предупреждението (винаги 'Uptime Kuma предупреждение')", + "halopsa_field_status": "Състояние на монитора: ДОСТЪПЕН, НЕДОСТЪПЕН, ИЗВЕСТИЕ или НЕИЗВЕСТНО", + "halopsa_field_monitor": "Име на монитора", + "halopsa_field_monitor_id": "Уникален идентификатор на монитор ('null' за тестови известия) - Използвайте това, за да съпоставите известията с тикетите", + "matrixUseTemplate": "Използвай персонализиран шаблон за съобщение", + "matrixUseTemplateDescription": "Ако е активирано, съобщението ще бъде изпратено с помощта на персонализиран шаблон.", + "halopsa_field_uptime_kuma_version": "Версията номер на Uptime Kuma", + "halopsa_setup_step5": "Конфигурирайте \"runbook\" да използва \"monitor_id\" за съпоставяне на предупреждения със съществуващи тикети", + "teltonikaUrl": "URL адрес на Вашето устройство Teltonika", + "teltonikaUnsafeTls": "Игнорирай валидирането на сертификата", + "teltonikaUsername": "Потребителско име за API", + "teltonikaPassword": "Парола за API", + "teltonikaPasswordHelptext": "Можете да дефинирате паролата на потребителя за API във вашия рутер Teltonika, напр. {0}", + "teltonikaModem": "ID на модем", + "teltonikaModemHelptext": "Идентификаторът на SMS модема трябва да бъде във формат {0}. Вижте https://developers.teltonika-networks.com/reference/ за насоки.", + "teltonikaPhoneNumber": "Телефонен номер", + "teltonikaPhoneNumberHelptext": "Номерът трябва да бъде в международен формат {0}, {1}. Разрешен е само един номер.", + "Teltonika SMS Gateway": "SMS шлюз на Teltonika", + "teltonikaVersionWarning": "Този доставчик на известия изисква Вашето устройство Teltonika да работи с RMS версия 7.14.0 или по-нова.", + "teltonikaUrlHelptext": "URL адресът трябва да бъде посочен като пълен произход, напр. {0} или {1}.", + "teltonikaUnsafeTlsDescription": "Изключване валидирането на TLS сертификати Ви излага на риск от атаки по средата (on-path, man-in-the-middle), което потенциално може да доведе до изтичане на данни и превземане на системи. Не изключвайте валидирането на сертификати, освен ако не приемате този вектор на атака. Препоръчваме използването на LetsEncrypt с автоматично подновяване.", + "teltonikaUsernameHelptext": "Препоръка: Създайте отделен акаунт, който е ограничен само до изпращане на SMS съобщения, и въведете потребителското му име тук", + "teamsEnableTags": "Включи етикети", + "teamsEnableTagsDescription": "Ако е активирано, съобщението ще включва етикетите на монитора.", + "RecordMatch": "Съвпадение в стойността на записа", + "RegexMatch": "Въведете регулярен израз, за да съответства на стойността на записа", + "GlobalpingMonitorDescription": "Globalping предоставя достъп до хиляди сонди, хоствани от общността, за провеждане на мрежови тестове и измервания. За всички анонимни потребители е определен лимит от 250 теста на час. За да удвоите ограничението до 500 на час, моля, запазете токена си в {accountSettings}. Вижте {docs} за повече информация.", + "certificateExpiryNotificationHelp": "Броят дни напред, може да се конфигурира в настройките.", + "domainExpiryNotificationHelp": "Броят дни, напред може да се конфигурира в настройките.", + "signalUseTemplate": "Използвай персонализиран шаблон за съобщение", + "signalUseTemplateDescription": "Ако е активирано, съобщението ще бъде изпратено с помощта на персонализиран шаблон. Можете да използвате Liquid templating, за да персонализирате формата на известията.", + "monitorTypeGameServer": "Игрови сървър", + "monitorTypeDatabase": "Mонитори за бази данни", + "monitorTypeSpecial": "Специални", + "360messengerGroupId": "360messenger ID на група", + "360messengerUseTemplate": "Използвай персонализиран шаблон за съобщение", + "360messengerGroupList": "WhatsApp групи", + "360messengerSelectGroupList": "Изберете група за добавяне", + "360messengerSelectedGroupID": "Избрани ID-та за група", + "360messengerEnableSendToGroup": "Активиране на изпращане до група(и) в WhatsApp", + "360messengerMessageTemplate": "Шаблон за съобщение", + "360messengerErrorNoApiKey": "Моля, първо въведете вашия 360messenger API ключ.", + "360messengerErrorNoGroups": "Не са намерени групи в WhatsApp за този акаунт.", + "360messengerErrorApi": "Не може да се зареди списъкът с групи в WhatsApp (Грешка {statusCode}: {message}).", + "360messengerErrorGeneric": "Не може да се зареди груповият списъкът в WhatsApp: {message}", + "360messengerAuthToken": "360messenger API ключ", + "360messengerRecipient": "Телефонен(ни) номер(а) на получателя", + "360messengerTemplate": "360messenger шаблон за съобщение", + "360messengerCustomMessageTemplate": "Шаблон за персонализирано съобщение", + "360messengerEnableCustomMessage": "Активирай персонализиран шаблон за съобщение вместо съобщението по подразбиране.", + "360messengerWayToGetUrlAndToken": "Можете да получите вашия API ключ за 360messenger от {0}.", + "360messengerWayToWriteRecipient": "Въведете един или повече телефонни номера в международен формат без водещ плюс (напр. {0}). Разделете отделните номера със запетая.", + "GlobalpingMultipleLocationsError": "Не се поддържат множество местоположения, моля, използвайте едно местоположение за всеки монитор.", + "GlobalpingLocationDescription": "Полето за местоположение приема континенти, държави, региони, градове, ASN, интернет доставчици или облачни региони. Можете да комбинирате филтри с {plus} (напр. {amazonPlusGermany} или {comcastPlusCalifornia}). Ако латентността е важен показател, използвайте филтри, за да стесните местоположението до малък регион, за да избегнете пикове, и за по-добра стабилност задайте филтъра {datacenter}. {fullDocs}.", + "fluxerMessageFormat": "Формат на съобщението", + "fluxerMessageFormatNormal": "Нормално (с вграден rich)", + "fluxerMessageFormatCustom": "Персонализиран шаблон", + "fluxerUseMessageTemplate": "Използвай персонализиран шаблон за съобщение", + "fluxerMessageTemplate": "Шаблон за съобщение", + "Fluxer Webhook URL": "Fluxer URL адрес за уебкука", + "fluxerMessageFormatMinimalist": "Минималистичен (кратък статус)", + "fluxerUseMessageTemplateDescription": "Ако е активирано, съобщението ще бъде изпратено с помощта на персонализиран шаблон (LiquidJS). Оставете празно, за да използвате Uptime Kuma формат, който е по подразбиране.", + "wayToGetFluxerURL": "Можете да получите, като отидете в настройките на целевия канал > Уеб куки > Създаване на уеб кука > Копиране на URL адрес на уеб кука.", + "Notification Action": "Действие при известяване", + "homeAssistantNotificationActionHelptext": "Списък с действия за известия, можете да намерите в Home Assistant - меню \"Settings > Developer Tools > Actions\". Потърсете \"notify\", за да намерите действията си. Въведете само частта след \"notify.\", например за действието \"notify.mobile_app_xyz\" въведете „mobile_app_xyz“. За вградени мобилни известия потърсете \"Send a notification via mobile_app_xyz\" (не \"Send a notification\").", + "wayToGetMaxToken": "Можете да получите \"MAX bot token\" и други подробности от {0}.", + "maxMessenger": "MAX messenger", + "maxApiUrlDescription": "Базов URL адрес на API за MAX messenger. По подразбиране: https://platform-api.max.ru", + "wayToGetMaxChatID": "Посочете идентификатора на чата в MAX, където трябва да се доставят съобщенията.", + "maxUseTemplate": "Използвай персонализиран шаблон за съобщение", + "maxUseTemplateDescription": "Ако е активирано, съобщението ще бъде изпратено с персонализиран шаблон.", + "maxTemplateFormatDescription": "MAX messenger поддържа форматиране на обикновен текст, Markdown и HTML.", + "oracledbConnectionString": "Oracle база данни: {connectionString}", + "telnyxApiKeyHelptext": "Вашият Telnyx V2 API ключ от Telnyx Portal под API Keys", + "telnyxApiKey": "API ключ (V2)", + "telnyxPhoneNumber": "От номер", + "telnyxPhoneNumberHelptext": "Вашият телефонен номер в Telnyx, от който да изпращате (трябва да е регистриран във вашия Telnyx акаунт, например +15551234567)", + "telnyxToNumber": "До номер", + "telnyxToNumberHelptext": "Телефонният номер на получателя за SMS известия (напр. +15559876543)", + "telnyxMessagingProfileId": "ID на профил за съобщения (по избор)", + "API Version": "API Версия", + "vkDontParseLinks": "Деактивиране на линк снипети", + "Peer ID": "Пиър ID", + "vkDontParseLinksDescription": "Ако е активирано, VK няма да генерира линк прегледи/снипети.", + "vkApiVersionDescription": "Версия на VK API, използвана за заявки. Оставете стойността по подразбиране, освен ако не се нуждаете конкретно от друга версия на VK API за съвместимост.", + "telnyxMessagingProfileIdHelptext": "По желание можете да свържете профил за съобщения Telnyx, за да контролирате настройките и функциите на подателя", + "vkPeerIdDescription": "Въведете целевия VK peer_id. Тази стойност се изпраща към API \"както е\".", + "evolutionCustomMessageTitle": "Персонализирано съобщение (по избор)", + "evolutionCustomMessageDesc": "Оставете празно, за да използвате съобщението по подразбиране.", + "WebSocket Options": "WebSocket опции", + "pingPacketSizeWarning": "Пакет с размер по-малък от 16 не продуцира \"round-trip\" време в Linux", + "wayToGetEgoSMSToken": "Можете да получите потребителското си име и парола за EgoSMS API от {тук}.", + "egosmsPhoneNumberDescription": "Номер на получател с код на държавата, без + или 00 (напр. 2567XXXXXXXX).", + "Bearer Token": "Bearer токен код", + "VKTeams Use Template": "Използвай персонализиран шаблон за съобщение", + "gamedigToken": "Токен код", + "gamedigTokenDescription": "По желание. Някои игрови сървъри изискват токен за удостоверяване.", + "VKTeams Bot Token Description": "Можете да разберете как да получите бот токен код и други подробности на {0}.", + "VKTeams Chat Id Description": "За потребителите това е техният имейл адрес. За групи и канали, вземете идентификатора от техните настройки и добавете {'@'}chat.agent", + "VKTeams Template Format Description": "За стилизиране на съобщения, VKTeams поддържа форматиране на обикновен текст, MarkdownV2 и HTML.", + "egosmsSenderDescription": "ID на подателя, показван на получателите (максимум 11 знака). По подразбиране е EGOSMS, ако е празно.", + "VKTeams Base Url Description": "Базов URL адрес за VKTeams API, може да се различава в локалното решение. По подразбиране: https://myteam.mail.ru", + "VKTeams Use Template Description": "Ако е активирано, съобщението ще бъде изпратено чрез персонализиран шаблон.", + "Connection Timeout": "Време за изчакване на свързването", + "smtpAdditionalHeadersTitle": "Допълнителни хедъри", + "smtpAdditionalHeadersDesc": "Моля, вижте \"Персонализирани хедъри на Nodemailer“ {0} за употреба.", + "Flowtriq Webhook URL": "URL адрес на уеб кука", + "Flowtriq API Key": "API ключ", + "flowtriqApiKeyDescription": "Допълнителен API ключ за удостоверяващ заявки за уеб кука към Flowtriq инстанцията.", + "wxPusherDownloadApp": "изтеглете приложението", + "wxPusherDocs": "официално ръководство", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) доставя чрез самостоятелното приложение WxPusher на 6 платформи — Android, iOS, HarmonyOS, Windows, macOS и Linux ({download}). Сканирайте QR кода по-долу, за да получите вашия SPT и сте готови за изпращане. Разделете отделните SPT със запетая (,) — вижте {docs} за повече информация.", + "Access Key": "Ключ за достъп", + "ooredooUsernameDescription": "Имейл адресът, който използвате за влизане в таблото за управление на Ooredoo bulk SMS dashboard.", + "ooredooAccessKeyDescription": "Намира се под \"Ключ за достъп до API\" на таблото ви за управление на Ooredoo SMS. Поставете го точно както е показано — той се кодира автоматично в base64 преди изпращане.", + "ooredooRecipientDescription": "Въведете едно или повече числа, разделени със запетая или интервал. Местните 7-цифрени номера автоматично получават префикс 960.", + "Leave blank to use the default Ooredoo endpoint.": "Оставете празно, за да използвате крайната точка Ooredoo по подразбиране.", + "plivoAuthID": "Auth ID", + "plivoAuthIDHelptext": "Вашият Plivo Auth ID от Plivo конзолата", + "plivoAuthTokenHelptext": "Вашият Plivo Auth Token от Plivo конзолата", + "plivoAuthToken": "Auth Token", + "plivoFromNumber": "От номер", + "plivoFromNumberHelptext": "Телефонният номер на Plivo, от който да се изпраща (напр. +15551234567). SMS може да използва и одобрено буквено-цифрово ID на подателя; гласовите повиквания изискват Plivo телефонен номер.", + "plivoToNumber": "До номер", + "plivoToNumberHelptext": "Телефонният номер на получателя за получаване на SMS известия (напр. +15559876543)", + "ooredooBearerTokenDescription": "Токенът, показан след \"Авторизиране: Носител\" в примерната команда cURL на вашето табло за управление на Ooredoo SMS.", + "Select PM2 process": "Избор на PM2 процес", + "PM2 Process": "PM2 Процес", + "Refresh": "Опресни", + "plivoMessageType": "Тип съобщение", + "plivoVoiceCall": "Гласово повикване", + "plivoAnswerUrl": "URL адрес на отговора", + "plivoAnswerUrlHelptext": "URL адрес, който връща Plivo XML за повикването. Текстът на предупреждението се добавя като параметър на заявката ?message=query parameter, така че крайната точка може да върне например {message}.", + "pm2Description": "Проверява дали PM2 процесът {service_name} е онлайн", + "pm2ExpectedStates": "Очаквано състояние: онлайн. Състоянията \"спряно\" и \"грешно\" се третират като \"недостъпно\".", + "ntpStratumThreshold": "Праг на стратума", + "ntpOffsetThreshold": "Праг на времево отместване (ms)", + "ntpOffsetThresholdHelp": "Маркирай като недостъпен, ако времевото отместване надвишава тази стойност в милисекунди.", + "ntpThresholdsTitle": "NTP прагове", + "ntpStratumThresholdHelp": "Маркирайте като надолу, ако стратумът е на или над тази стойност. Стратумът показва качеството на източника на време (1=GPS/атомен часовник, 16=несинхронизиран).", + "ntpDispersionThreshold": "Root Dispersion - праг (ms)", + "ntpDispersionThresholdHelp": "Маркира се като недостъпен, ако Root Dispersion, надвишава тази стойност. Стойността показва точността на часовника на сървъра." +} diff --git a/src/lang/bn.json b/src/lang/bn.json new file mode 100644 index 0000000..22deeb8 --- /dev/null +++ b/src/lang/bn.json @@ -0,0 +1,1180 @@ +{ + "setupDatabaseChooseDatabase": "আপনি কোন ডাটাবেজটি ব্যবহার করতে চান?", + "setupDatabaseEmbeddedMariaDB": "আপনাকে কিছু সেটআপ করার প্রয়োজন নেই। এই ডকার ইমেজে স্বয়ংক্রিয়ভাবে MariaDB এম্বেড এবং কনফিগার করা হয়েছে। Uptime Kuma এই ডাটাবেজের সাথে ইউনিক্স সকেটের মাধ্যমে সংযুক্ত হবে।", + "setupDatabaseMariaDB": "বহিরাগত MariaDB ডাটাবেজের সাথে সংযোগ করতে হবে। আপনাকে ডাটাবেজের সংযোগ তথ্য সেট করতে হবে।", + "Add": "সংযোগ করুন", + "dbName": "ডাটাবেজের নাম", + "languageName": "বাংলা", + "Settings": "সেটিংস", + "Dashboard": "ড্যাশবোর্ড", + "Help": "সাহায্য", + "New Update": "নতুন আপডেট", + "Language": "ভাষা", + "Version": "সংস্করণ", + "Check Update On GitHub": "GitHub-এ আপডেট চেক করুন", + "List": "তালিকা", + "General": "সাধারণ", + "Game": "খেলা", + "disable authentication": "প্রমাণীকরণ বন্ধ করুন", + "pauseDashboardHome": "থামুন", + "disableauth.message2": "এটি Uptime Kuma এর সামনে {intendThirdPartyAuth} এর মতো পরিস্থিতির জন্য ডিজাইন করা হয়েছে, যেমন Cloudflare Access, Authelia অথবা অন্যান্য প্রমাণীকরণ ব্যবস্থা।", + "I understand, please disable": "আমি বুঝতে পারছি, দয়া করে অক্ষম করুন", + "Certificate Info": "সার্টিফিকেট তথ্য", + "Create your admin account": "আপনার অ্যাডমিন অ্যাকাউন্ট তৈরি করুন", + "Import": "ইমপোর্ট", + "Default enabled": "ডিফল্ট সক্রিয়", + "Heartbeats": "হার্টবিট", + "Affected Monitors": "প্রভাবিত মনিটরগুলো", + "All Status Pages": "সমস্ত স্ট্যাটাস পেজ", + "alertNoFile": "অনুগ্রহ করে আমদানির জন্য একটি ফাইল নির্বাচন করুন।", + "alertWrongFileType": "অনুগ্রহ করে একটি JSON ফাইল নির্বাচন করুন।", + "Clear all statistics": "সব পরিসংখ্যান মুছে ফেলুন", + "Setup 2FA": "2FA সেট আপ করুন", + "Two Factor Authentication": "দুই ফ্যাক্টর প্রমাণীকরণ", + "Add New Tag": "নতুন ট্যাগ যোগ করুন", + "Tag with this name already exist.": "এই নামের ট্যাগ আগে থেকেই আছে।", + "Pink": "গোলাপী", + "Search monitored sites": "পর্যবেক্ষণ করা সাইট অনুসন্ধান", + "statusPageNothing": "এখানে কিছুই নেই, অনুগ্রহ করে একটি গ্রুপ বা মনিটর যোগ করুন।", + "Degraded Service": "অধঃপতন সেবা", + "Go to Dashboard": "ড্যাশবোর্ডে যান", + "defaultNotificationName": "আমার {বিজ্ঞপ্তি} সতর্কতা ({number})", + "webhookJsonDesc": "{0} যেকোনো আধুনিক HTTP সার্ভার যেমন Express.js-এর জন্য ভালো", + "liquidIntroduction": "টেম্পলেটবিলিটি লিকুইড টেমপ্লেটিং ভাষার মাধ্যমে অর্জন করা হয়। ব্যবহারের নির্দেশাবলীর জন্য অনুগ্রহ করে {0} দেখুন। এই উপলব্ধ ভেরিয়েবল হল:", + "Notifications": "নোটিফিকেশন", + "Setup Notification": "নোটিফিকেশন সেটআপ করুন", + "Light": "আলো", + "Auto": "স্বয়ংক্রিয়", + "Theme - Heartbeat Bar": "থিম - হার্টবিট বার", + "styleElapsedTime": "হার্টবিট বারের নিচে ব্যবহৃত সময়", + "styleElapsedTimeShowNoLine": "শোর করুন (কোনো লাইন নেই)", + "styleElapsedTimeShowWithLine": "শো করুন (লাইন সহ)", + "Normal": "স্বাভাবিক", + "Bottom": "নীচে", + "Timezone": "টাইমজোন", + "Search Engine Visibility": "সার্চ ইঞ্জিন দৃশ্যমানতা", + "Allow indexing": "ইন্ডেক্সিং অনুমোদন করুন", + "Discourage search engines from indexing site": "সার্চ ইঞ্জিনগুলোকে সাইটের ইন্ডেক্সিং থেকে বিরত রাখুন", + "Change Password": "পাসওয়ার্ড পরিবর্তন করুন", + "Current Password": "বর্তমান পাসওয়ার্ড", + "New Password": "নতুন পাসওয়ার্ড", + "Repeat New Password": "নতুন পাসওয়ার্ডটি আবার দিন", + "Update Password": "পাসওয়ার্ড আপডেট করুন", + "Enable Auth": "প্রমাণীকরণ চালু করুন", + "disableauth.message1": "আপনি কি সত্যিই {disableAuth} করতে চান?", + "where you intend to implement third-party authentication": "যেখানে আপনি তৃতীয় পক্ষের প্রমাণীকরণ বাস্তবায়ন করতে চান", + "Please use this option carefully!": "দয়া করে এই অপশনটি সাবধানে ব্যবহার করুন!", + "Logout": "লগ আউট", + "Leave": "লিভ", + "Confirm": "নিশ্চিত", + "Yes": "হ্যাঁ", + "No": "না", + "Username": "ইউজার নেম", + "Password": "পাসওয়ার্ড", + "Remember me": "আমাকে মনে রাখুন", + "Login": "লগ ইন", + "add one": "একটি যোগ করুন", + "Notification Type": "নোটিফিকেশনের ধরন", + "Email": "ইমেইল", + "Test": "পরীক্ষা", + "Resolver Server": "সমাধানকারী সার্ভার", + "Resource Record Type": "রিসোর্স রেকর্ড টাইপ", + "Last Result": "শেষ ফলাফল", + "Repeat Password": "পাসওয়ার্ড পুনরায় দিন", + "Import Backup": "ইমপোর্ট ব্যাকআপ", + "Export Backup": "এক্সপোর্ট ব্যাকআপ", + "Export": "এক্সপোর্ট", + "respTime": "প্রতিক্রিয়া সময় (ms)", + "notAvailableShort": "প্রযোজ্য নয়", + "Apply on all existing monitors": "সমস্ত বিদ্যমান মনিটরে প্রয়োগ করুন", + "Create": "তৈরি", + "Clear Data": "ডেটা সাফ", + "Events": "ইভেন্ট সমূহ", + "Auto Get": "অটো গ্রহণ", + "Schedule maintenance": "রক্ষণাবেক্ষণ সূচী করুন", + "Pick Affected Monitors...": "প্রভাবিত মনিটর নির্বাচন করুন…", + "Start of maintenance": "রক্ষণাবেক্ষণের শুরু", + "Select status pages...": "স্ট্যাটাস পেজগুলো নির্বাচন করুন…", + "Skip existing": "বিদ্যমান এড়িয়ে যান", + "Overwrite": "ওভাররাইট", + "Options": "অপশন", + "Keep both": "দুটোই রাখুন", + "Verify Token": "টোকেন যাচাই করুন", + "Enable 2FA": "2FA সক্ষম করুন", + "Disable 2FA": "2FA নিষ্ক্রিয় করুন", + "2FA Settings": "2FA সেটিংস", + "filterActive": "সক্রিয়", + "filterActivePaused": "বিরতি", + "Active": "সক্রিয়", + "Inactive": "নিষ্ক্রিয়", + "Token": "টোকেন", + "Show URI": "URI দেখান", + "Tags": "ট্যাগ", + "Add New below or Select...": "নীচে নতুন যোগ করুন বা নির্বাচন করুন…", + "Tag with this value already exist.": "এই মান সহ ট্যাগ ইতিমধ্যেই বিদ্যমান।", + "color": "রং", + "value (optional)": "মান (ঐচ্ছিক)", + "Red": "লাল", + "Orange": "কমলা", + "Green": "সবুজ", + "Blue": "নীল", + "Indigo": "নীল", + "Purple": "বেগুনি", + "Custom": "কাস্টম", + "Search...": "অনুসন্ধান…", + "Avg. Ping": "গড় পিং", + "Avg. Response": "গড় প্রতিক্রিয়া", + "Entry Page": "প্রবেশ পৃষ্ঠা", + "statusPageRefreshIn": "রিফ্রেশ হবে: {0}", + "No Services": "কোনো পরিষেবা নেই", + "All Systems Operational": "সমস্ত সিস্টেম অপারেশনাল", + "Partially Degraded Service": "আংশিকভাবে অবনমিত পরিষেবা", + "Add Group": "গ্রুপ যোগ করুন", + "Add a monitor": "একটি মনিটর যোগ করুন", + "Edit Status Page": "এডিট স্টেটাস পেজ", + "Status Page": "স্ট্যাটাস পেজ", + "Status Pages": "স্ট্যাটাস পেজ সমূহ", + "here": "এখানে", + "Required": "প্রয়োজন", + "Post URL": "পোস্ট URL", + "Content Type": "বিষয়বস্তুর প্রকার", + "webhookFormDataDesc": "{multipart} PHP এর জন্য ভালো। JSON-কে {decodeFunction} দিয়ে পার্স করতে হবে", + "templateMsg": "বিজ্ঞপ্তির বার্তা", + "templateHeartbeatJSON": "হৃদস্পন্দন বর্ণনাকারী বস্তু", + "templateMonitorJSON": "মনিটরের বর্ণনাকারী বস্তু", + "templateLimitedToUpDownNotifications": "শুধুমাত্র UP/DOWN বিজ্ঞপ্তির জন্য উপলব্ধ", + "Gray": "ধূসর", + "Add New Monitor": "নতুন আপটাইম মনিটর যোগ করুন", + "statusMaintenance": "রক্ষণাবেক্ষণ", + "Friendly Name": "বন্ধুত্বপূর্ণ নাম", + "URL": "ইউআরএল", + "settingUpDatabaseMSG": "ডাটাবেস সেটআপ করা হচ্ছে। এটি কিছুটা সময় নিতে পারে, অনুগ্রহ করে ধৈর্য ধরুন।", + "Response": "রেসপন্স", + "Ping": "পিং", + "Keyword": "কীওয়ার্ড", + "Invert Keyword": "ইনভার্ট কীওয়ার্ড", + "setupDatabaseSQLite": "একটি সাধারণ ডাটাবেস ফাইল, যা ছোট পরিসরের ডিপ্লয়মেন্টের জন্য সুপারিশ করা হয়। v2.0.0 এর পূর্বে, Uptime Kuma ডিফল্ট ডাটাবেস হিসেবে SQLite ব্যবহার করত।", + "Unknown": "অজানা", + "Cannot connect to the socket server": "সকেট সার্ভারের সাথে সংযোগ স্থাপন করা যাচ্ছে না", + "Reconnecting...": "পুনরায় সংযোগ স্থাপন করা হচ্ছে...", + "Passive Monitor Type": "নিষ্ক্রিয় মনিটরের ধরন", + "markdownSupported": "মার্কডাউন সিনট্যাক্স সাপোর্ট", + "Pause": "থামুন", + "-day": "-দিন", + "hour": "ঘন্টা", + "Host URL": "হোস্ট ইউআরএল", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "আপনি যে সার্ভারে সংযোগ করতে চান তার হোস্টনেম প্রবেশ করুন, অথবা যদি আপনি একটি {local_mta} ব্যবহার করার পরিকল্পনা করেন, তাহলে {localhost} লিখুন", + "ignoreTLSErrorGeneral": "সংযোগের জন্য TLS/SSL ত্রুটি উপেক্ষা করুন", + "Upside Down Mode": "আপসাইড ডাউন মোড", + "Pending": "প্রক্রিয়াধীন", + "Push URL": "পুশ URL", + "needPushEvery": "আপনাকে এই URL টি প্রতি {0} সেকেন্ডে কল করতে হবে।", + "Dark": "অন্ধকার", + "Up": "আপ", + "webhookAdditionalHeadersTitle": "অতিরিক্ত শিরোনাম", + "pushOthers": "অন্যান্য", + "programmingLanguages": "প্রোগ্রামিং ভাষাসমূহ", + "Not available, please setup.": "উপলব্ধ নয়, অনুগ্রহ করে সেটআপ করুন।", + "Down": "ডাউন", + "Monitor Type": "মনিটরের ধরন", + "Expected Value": "প্রত্যাশিত মান", + "Home": "হোম", + "Maintenance": "রক্ষণাবেক্ষণ", + "General Monitor Type": "সাধারণ মনিটরের ধরন", + "Specific Monitor Type": "নির্দিষ্ট মনিটরের ধরন", + "Monitor": "মনিটর | মনিটরগুলো", + "day": "দিন | দিনগুলো", + "-hour": "-ঘন্টা", + "Hostname": "হোস্টনেম", + "locally configured mail transfer agent": "লোকালি কনফিগারড মেইল ট্রান্সফার এজেন্ট", + "Port": "পোর্ট", + "Heartbeat Interval": "হার্টবিট ইন্টারভাল", + "Max. Redirects": "সর্বোচ্চ রিডাইরেক্ট", + "Primary Base URL": "প্রাথমিক বেস URL", + "Message": "বার্তা", + "Resume": "পুনরায় শুরু", + "Delete": "মুছে ফেলুন", + "now": "এখন", + "time ago": "{০} বয়স", + "-year": "-বছর", + "Json Query Expression": "JSON কোয়েরি এক্সপ্রেশন", + "timeoutAfter": "{0} সেকেন্ড পর টাইমআউট", + "Retries": "পুনরায় চেষ্টা করে", + "checkEverySecond": "প্রতিটি {0} সেকেন্ডে চেক করুন", + "retryCheckEverySecond": "প্রতিটি {0} সেকেন্ডে পুনরায় চেষ্টা করুন", + "resendEveryXTimes": "প্রতিটি {0} বার পুনরায় পাঠান", + "pushOptionalParams": "ঐচ্ছিক প্যারামিটারসমূহ: {0}", + "Save": "সংরক্ষণ", + "ignoredTLSError": "TLS/SSL ত্রুটিগুলি উপেক্ষা করা হয়েছে", + "Accepted Status Codes": "গ্রহণযোগ্য স্ট্যাটাস কোডসমূহ", + "Disable Auth": "প্রমাণীকরণ বন্ধ করুন", + "Theme": "থিম", + "Name": "নাম", + "Status": "অবস্থা", + "DateTime": "তারিখ ও সময়", + "No important events": "কোনো গুরুত্বপূর্ণ ইভেন্ট নেই", + "Edit": "সম্পাদনা", + "Current": "বর্তমান", + "Uptime": "আপটাইম", + "Request Timeout": "রিকোয়েস্ট টাইমআউট", + "Heartbeat Retry Interval": "হার্টবিট রিট্রাই ইন্টারভাল", + "Advanced": "অ্যাডভান্স", + "retriesDescription": "সার্ভিসটি ডাউন হিসেবে চিহ্নিত হওয়ার আগে এবং একটি নোটিফিকেশন পাঠানোর জন্য সর্বোচ্চ পুনরায় চেষ্টা করার সংখ্যা", + "upsideDownModeDescription": "স্ট্যাটাসটি উল্টে দিন। যদি পরিষেবাটি পৌঁছানো যায়, তাহলে এটি বন্ধ।", + "maxRedirectDescription": "অনুসরণ করার জন্য সর্বোচ্চ রিডাইরেক্ট সংখ্যা। রিডাইরেক্ট নিষ্ক্রিয় করতে 0 সেট করুন।", + "ignoreTLSError": "HTTPS ওয়েবসাইটগুলির জন্য TLS/SSL ত্রুটিগুলি উপেক্ষা করুন", + "pushViewCode": "পুশ মনিটর কীভাবে ব্যবহার করবেন? (কোড দেখুন)", + "Appearance": "দেখানোর ধরন", + "Quick Stats": "তাৎক্ষণিক পরিসংখ্যান", + "affectedMonitorsDescription": "বর্তমান রক্ষণাবেক্ষণে প্রভাবিত মনিটরগুলো নির্বাচন করুন", + "atLeastOneMonitor": "অন্তত একটি প্রভাবিত মনিটর নির্বাচন করুন", + "pushoversounds cashregister": "নগদ রেজিস্টার", + "pushoversounds incoming": "আগত", + "importHandleDescription": "আপনি যদি একই নামের প্রতিটি মনিটর অথবা নোটিফিকেশান এড়িয়ে যেতে চান তাহলে 'যেটা এখন আছে' সেটা এড়িয়ে যান । 'ওভাররাইট' প্রতিটি বিদ্যমান মনিটর এবং নোটিফিকেশান মুছে ফেলবে।", + "confirmImportMsg": "তুমি কি নিশ্চিত যে তুমি ব্যাকআপটি ইমপোর্ট করতে চাও? অনুগ্রহ করে যাচাই করো তুমি সঠিক ইমপোর্ট অপশনটি নির্বাচন করেছ কিনা।", + "twoFAVerifyLabel": "দয়া করে আপনার টোকেনটি প্রবেশ করুন ২-ধাপ যাচাইকরণের (2FA) জন্য:", + "tokenValidSettingsMsg": "টোকেনটি বৈধ! এখন আপনি ২-ধাপ যাচাইকরণের (2FA) সেটিংস সংরক্ষণ করতে পারেন।", + "confirmEnableTwoFAMsg": "তুমি কি নিশ্চিত যে তুমি ২-ধাপ যাচাইকরণ (2FA) চালু করতে চাও?", + "confirmDisableTwoFAMsg": "তুমি কি নিশ্চিত যে তুমি ২-ধাপ যাচাইকরণ (2FA) বন্ধ করতে চাও?", + "recurringIntervalMessage": "প্রতিদিন একবার চালান । প্রতি {0} দিনে একবার চালান", + "affectedStatusPages": "নির্বাচিত স্ট্যাটাস পেজগুলোতে এই রক্ষণাবেক্ষণ বার্তাটি দেখান", + "passwordNotMatchMsg": "পুনরায় দেওয়া পাসওয়ার্ডটি মিলছে না।", + "invertKeywordDescription": "কীওয়ার্ডটি উপস্থিত আছে কি না নয়, বরং অনুপস্থিত আছে কি না দেখুন।", + "jsonQueryDescription": "সার্ভারের JSON প্রতিক্রিয়া থেকে JSON কুয়েরি ব্যবহার করে নির্দিষ্ট ডেটা পার্স ও এক্সট্র্যাক্ট করুন; যদি JSON প্রত্যাশিত না হয়, তাহলে প্রতিক্রিয়ার জন্য “$” ব্যবহার করুন। প্রাপ্ত ফল এরপর প্রত্যাশিত মানের সাথে **স্ট্রিং** হিসেবে তুলনা করা হবে। ডকুমেন্টেশনের জন্য {0} দেখুন এবং কুয়েরি নিয়ে পরীক্ষা করতে {1} ব্যবহার করুন।", + "backupDescription": "আপনি সব মনিটর ও নোটিফিকেশনকে একটি JSON ফাইলে ব্যাকআপ নিতে পারেন।", + "backupDescription2": "নোট: ইতিহাস ও ইভেন্ট সম্পর্কিত তথ্য এতে অন্তর্ভুক্ত নয়।", + "backupDescription3": "নোটিফিকেশন টোকেনের মতো সংবেদনশীল ডেটা এক্সপোর্ট ফাইলে অন্তর্ভুক্ত থাকে; অনুগ্রহ করে এক্সপোর্টটি নিরাপদভাবে সংরক্ষণ করুন।", + "endpoint": "সংযোগ বিন্দু", + "octopushAPIKey": "কন্ট্রোল প্যানেলে থাকা HTTP API ক্রেডেনশিয়াল থেকে “API key” সংগ্রহ করুন", + "octopushLogin": "কন্ট্রোল প্যানেলে থাকা HTTP API ক্রেডেনশিয়াল থেকে “লগইন” তথ্য সংগ্রহ করুন", + "promosmsLogin": "API লগইন নাম", + "promosmsPassword": "API পাসওয়ার্ড", + "pushoversounds pushover": "Pushover (ডিফল্ট)", + "pushoversounds bike": "সাইকেল", + "pushoversounds bugle": "বিউগল", + "pushoversounds classical": "ক্লাসিক", + "pushoversounds cosmic": "মহাজাগতিক", + "pushoversounds falling": "পড়ে যাওয়া / পতনশীল", + "pushoversounds gamelan": "গামেলান", + "pushoversounds intermission": "বিরতি", + "pushoversounds magic": "ম্যাজিক", + "pushoversounds mechanical": "যান্ত্রিক", + "pushoversounds pianobar": "পিয়ানো বার", + "pushoversounds siren": "সাইরেন", + "pushoversounds spacealarm": "স্পেস অ্যালার্ম", + "pushoversounds tugboat": "টাগবোট", + "pushoversounds alien": "এলিয়েন সতর্কতা (দীর্ঘ)", + "pushoversounds climb": "আরোহণ (দীর্ঘ)", + "pushoversounds persistent": "স্থায়ী (দীর্ঘ)", + "pushoversounds echo": "Pushover ইকো (দীর্ঘ)", + "notificationDescription": "নোটিফিকেশনগুলো কার্যকর হতে হলে সেগুলোকে কোনো মনিটরের সাথে যুক্ত করতে হবে।", + "keywordDescription": "সাধারণ HTML বা JSON প্রতিক্রিয়ায় কীওয়ার্ড অনুসন্ধান করুন। এই অনুসন্ধানটি কেস-সেনসিটিভ।", + "pushoversounds updown": "উপরে নিচে", + "pushoversounds vibrate": "শুধু কম্পন", + "pushoversounds none": "নীরব (নিঃশব্দ)", + "pushyAPIKey": "গোপন API কী", + "pushyToken": "ডিভাইস টোকেন", + "Guild ID": "গিল্ড আইডি", + "User Key": "ইউজার কী", + "Device": "ডিভাইস", + "pushoverMessageTtl": "বার্তার TTL (সেকেন্ড)", + "Check octopush prices": "Octopush এর দাম যাচাই করুন {0}.", + "issueWithGoogleChatOnAndroidHelptext": "এটি {issuetackerURL}-এর মতো আপস্ট্রিম বাগগুলো এড়িয়ে যেতে সাহায্য করে", + "wayToGetKookBotToken": "অ্যাপ্লিকেশন তৈরি করুন এবং আপনার বট টোকেন {0} থেকে সংগ্রহ করুন", + "wayToGetKookGuildID": "Kook সেটিংসে ‘ডেভেলপার মোড’ চালু করুন, তারপর গিল্ডে ডান ক্লিক করে তার আইডি সংগ্রহ করুন", + "GoogleChat": "গুগল চ্যাট (শুধুমাত্র গুগল ওয়ার্কস্পেসের জন্য)", + "Template plain text instead of using cards": "কার্ডের পরিবর্তে সাধারণ টেক্সট টেমপ্লেট", + "apprise": "অ্যাপরাইজ (৫০টিরও বেশি নোটিফিকেশন পরিষেবা সমর্থন করে)", + "SMS Type": "এসএমএস ধরন", + "pushoverDesc2": "যদি আপনি বিভিন্ন ডিভাইসে নোটিফিকেশন পাঠাতে চান, তাহলে ডিভাইস ঘরটি পূরণ করুন।", + "Message Title": "বার্তার শিরোনাম", + "checkPrice": "{0}-এর দাম যাচাই করুন:", + "apiCredentials": "API শংসাপত্র", + "octopushLegacyHint": "আপনি কি Octopush-এর পুরোনো সংস্করণ (২০১১–২০২০) ব্যবহার করছেন, নাকি নতুন সংস্করণ?", + "Notification Sound": "নোটিফিকেশন সাউন্ড", + "More info on:": "আরও তথ্যের জন্য: {0}", + "pushoverDesc1": "জরুরি অগ্রাধিকার (২)-এর ক্ষেত্রে প্রতিটি পুনরায় চেষ্টার মধ্যে ডিফল্ট ৩০ সেকেন্ডের টাইমআউট থাকে এবং এটি ১ ঘণ্টা পরে মেয়াদোত্তীর্ণ হবে।", + "octopushPhoneNumber": "ফোন নম্বর (আন্তর্জাতিক ফরম্যাট, যেমন: +33612345678) ", + "octopushSMSSender": "এসএমএস প্রেরকের নাম: ৩–১১টি বর্ণ ও সংখ্যা (a-zA-Z0-9) এবং স্পেস ব্যবহারযোগ্য", + "octopushTypePremium": "প্রিমিয়াম (দ্রুত - সতর্কতার জন্য প্রস্তাবিত)", + "octopushTypeLowCost": "কম খরচ (ধীর - কখনও অপারেটর দ্বারা ব্লক করা হয়)", + "Path": "পথ", + "defaultFriendlyName": "নতুন মনিটর", + "Cert Exp.": "সার্টি এক্সপ্রেস.", + "Add one": "একটি যোগ করুন", + "Untitled Group": "শিরোনামহীন গ্রুপ", + "Services": "সেবা", + "Discard": "বাতিল করুন", + "Cancel": "বাতিল করুন", + "Installed": "ইনস্টল করা হয়েছে", + "Running": "চলছে", + "Not running": "চলছে না", + "Remove Token": "টোকেন সরান", + "Start": "শুরু করুন", + "Stop": "থামো", + "Slug": "স্লাগ", + "Accept characters:": "অক্ষর গ্রহণ করুন:", + "startOrEndWithOnly": "শুধুমাত্র {0} দিয়ে শুরু বা শেষ করুন", + "No consecutive dashes": "কোনও ধারাবাহিক ড্যাশ নেই", + "The slug is already taken. Please choose another slug.": "স্লাগটি ইতিমধ্যেই নেওয়া হয়েছে। অনুগ্রহ করে অন্য একটি স্লাগ বেছে নিন।", + "Authentication": "প্রমাণীকরণ", + "HTTP Basic Auth": "HTTP বেসিক প্রমাণীকরণ", + "New Status Page": "নতুন স্ট্যাটাস পৃষ্ঠা", + "Page Not Found": "পৃষ্ঠাটি খুঁজে পাওয়া যায়নি", + "Reverse Proxy": "বিপরীত প্রক্সি", + "Backup": "ব্যাকআপ", + "About": "সম্পর্কে", + "wayToGetCloudflaredURL": "({0} থেকে cloudflared ডাউনলোড করুন)", + "cloudflareWebsite": "ক্লাউডফ্লেয়ার ওয়েবসাইট", + "Message:": "বার্তা:", + "HTTP Headers": "HTTP হেডার", + "Please read": "অনুগ্রহ করে পড়ুন", + "Valid To:": "বৈধ:", + "Days Remaining:": "বাকি দিন:", + "Issuer:": "ইস্যুকারী:", + "Fingerprint:": "আঙুলের ছাপ:", + "No status pages": "কোনও স্ট্যাটাস পৃষ্ঠা নেই", + "Domain Name Expiry Notification": "ডোমেন নামের মেয়াদ শেষ হওয়ার বিজ্ঞপ্তি", + "Add a new expiry notification day": "একটি নতুন মেয়াদ শেষ হওয়ার বিজ্ঞপ্তির দিন যোগ করুন", + "Remove the expiry notification": "মেয়াদ শেষ হওয়ার বিজ্ঞপ্তির দিনটি সরিয়ে দিন", + "Proxy": "প্রক্সি", + "Date Created": "তৈরির তারিখ", + "Footer Text": "পাদলেখ টেক্সট", + "Refresh Interval": "রিফ্রেশ ব্যবধান", + "Show Powered By": "দ্বারা চালিত দেখান", + "Domain Names": "ডোমেইন নাম", + "signedInDisp": "{0} হিসেবে সাইন ইন করেছেন", + "signedInDispDisabled": "প্রমাণীকরণ অক্ষম।", + "RadiusSecret": "রেডিয়াস সিক্রেট", + "RadiusSecretDescription": "ক্লায়েন্ট এবং সার্ভারের মধ্যে ভাগ করা গোপনীয়তা", + "RadiusCalledStationId": "স্টেশন আইডি বলা হয়", + "RadiusCallingStationId": "কলিং স্টেশন আইডি", + "RadiusCallingStationIdDescription": "কলিং ডিভাইসের শনাক্তকারী", + "API Key": "এপিআই কী", + "Show update if available": "উপলব্ধ থাকলে আপডেট দেখান", + "Also check beta release": "বিটা রিলিজও পরীক্ষা করুন", + "Using a Reverse Proxy?": "রিভার্স প্রক্সি ব্যবহার করছেন?", + "What you can try:": "আপনি যা চেষ্টা করতে পারেন:", + "Go back to the previous page.": "আগের পৃষ্ঠায় ফিরে যান।", + "templateHostnameOrURL": "হোস্টনাম অথবা URL", + "Other Software": "অন্যান্য সফটওয়্যার", + "For example: nginx, Apache and Traefik.": "উদাহরণস্বরূপ: nginx, Apache এবং Traefik।", + "Subject:": "বিষয়:", + "Trust Proxy": "প্রক্সি বিশ্বাস করুন", + "API Username": "API ব্যবহারকারীর নাম", + "Most likely causes:": "সবচেয়ে সম্ভাব্য কারণ:", + "There might be a typing error in the address.": "ঠিকানায় টাইপিং ত্রুটি থাকতে পারে।", + "Steam Game Server": "স্টিম গেম সার্ভার", + "Retype the address.": "ঠিকানাটি পুনরায় টাইপ করুন।", + "Connection String": "সংযোগ স্ট্রিং", + "Query": "প্রশ্ন", + "certificationExpiryDescription": "TLS সার্টিফিকেটের মেয়াদ শেষ হলে HTTPS মনিটর বিজ্ঞপ্তি ট্রিগার করে:", + "Setup Docker Host": "ডকার হোস্ট সেট আপ করুন", + "Connection Type": "সংযোগের ধরণ", + "Container Name / ID": "কন্টেইনারের নাম / আইডি", + "None": "কেউ না", + "tagAlreadyStaged": "এই ট্যাগটি (নাম এবং মান) ইতিমধ্যেই এই ব্যাচের জন্য মঞ্চস্থ করা হয়েছে।", + "templateStatus": "অবস্থা", + "webhookBodyCustomOption": "কাস্টম বডি", + "Webhook URL": "ওয়েবহুক ইউআরএল", + "Application Token": "অ্যাপ্লিকেশন টোকেন", + "Server URL": "সার্ভার ইউআরএল", + "Method": "পদ্ধতি", + "Body": "শরীর", + "Headers": "শিরোনাম", + "BodyInvalidFormat": "অনুরোধের মূল অংশটি বৈধ JSON নয়: ", + "Monitor History": "মনিটরের ইতিহাস", + "PasswordsDoNotMatch": "পাসওয়ার্ড মিলছে না।", + "records": "রেকর্ড", + "One record": "একটি রেকর্ড", + "Current User": "বর্তমান ব্যবহারকারী", + "topic": "বিষয়", + "topicExplanation": "পর্যবেক্ষণের জন্য MQTT বিষয়", + "mqttWebSocketPath": "MQTT ওয়েবসকেট পাথ", + "mqttWebsocketPathInvalid": "অনুগ্রহ করে একটি বৈধ ওয়েবসকেট পাথ ফর্ম্যাট ব্যবহার করুন", + "successKeyword": "সাফল্যের মূলশব্দ", + "successKeywordExplanation": "MQTT কীওয়ার্ড যা সফল হিসাবে বিবেচিত হবে", + "recent": "সাম্প্রতিক", + "Reset Token": "টোকেন রিসেট করুন", + "Done": "সম্পন্ন", + "Info": "তথ্য", + "Security": "নিরাপত্তা", + "Steam API Key": "স্টিম এপিআই কী", + "Pick a RR-Type...": "একটি RR-টাইপ বেছে নিন…", + "Default": "ডিফল্ট", + "HTTP Options": "HTTP বিকল্প", + "Create Incident": "ঘটনা তৈরি করুন", + "Title": "শিরোনাম", + "Style": "স্টাইল", + "info": "তথ্য", + "warning": "সতর্কতা", + "danger": "বিপদ", + "error": "ত্রুটি", + "critical": "সমালোচনামূলক", + "light": "আলো", + "dark": "অন্ধকার", + "Post": "পোস্ট", + "Please input title and content": "অনুগ্রহ করে শিরোনাম এবং বিষয়বস্তু লিখুন", + "Created": "তৈরি করা হয়েছে", + "Switch to Dark Theme": "কালো থিমে স্যুইচ করুন", + "Show Tags": "ট্যাগ দেখান", + "Hide Tags": "ট্যাগ লুকান", + "templateServiceName": "পরিষেবার নাম", + "Pick Accepted Status Codes...": "গৃহীত স্ট্যাটাস কোডগুলি বেছে নিন…", + "No Monitors, please": "দয়া করে কোন মনিটর নেই", + "Add Tags": "ট্যাগ যোগ করুন", + "tagAlreadyOnMonitor": "এই ট্যাগটি (নাম এবং মান) ইতিমধ্যেই মনিটরে আছে অথবা সংযোজনের অপেক্ষায় আছে।", + "tagNameExists": "এই নামের একটি সিস্টেম ট্যাগ ইতিমধ্যেই বিদ্যমান। তালিকা থেকে এটি নির্বাচন করুন অথবা অন্য একটি নাম ব্যবহার করুন।", + "Add New Status Page": "নতুন স্ট্যাটাস পৃষ্ঠা যোগ করুন", + "webhookAdditionalHeadersDesc": "ওয়েবহুকের সাথে পাঠানো অতিরিক্ত হেডার সেট করে। প্রতিটি হেডারকে একটি JSON কী/মান হিসেবে সংজ্ঞায়িত করা উচিত।", + "HeadersInvalidFormat": "অনুরোধের শিরোনামগুলি বৈধ JSON নয়: ", + "clearDataOlderThan": "মনিটরের ইতিহাসের ডেটা {0} দিনের জন্য রাখুন।", + "Next": "পরবর্তী", + "steamApiKeyDescription": "স্টিম গেম সার্ভার পর্যবেক্ষণের জন্য আপনার একটি স্টিম ওয়েব-এপিআই কী প্রয়োজন। আপনি এখানে আপনার এপিআই কী নিবন্ধন করতে পারেন: ", + "mqttWebsocketPathExplanation": "ওয়েবসকেট সংযোগের মাধ্যমে MQTT-এর জন্য ওয়েবসকেট পাথ (যেমন, /mqtt)", + "Chat ID": "চ্যাট আইডি", + "mqttHostnameTip": "অনুগ্রহ করে এই ফর্ম্যাটটি {hostnameFormat} ব্যবহার করুন", + "Shrink Database": "ডাটাবেস সঙ্কুচিত করুন", + "shrinkDatabaseDescriptionSqlite": "SQLite এর জন্য ডাটাবেস {vacuum} ট্রিগার করুন। {auto_vacuum} ইতিমধ্যেই সক্রিয় আছে কিন্তু এটি ডাটাবেসকে ডিফ্র্যাগমেন্ট করে না বা {vacuum} কমান্ডের মতো পৃথক ডাটাবেস পৃষ্ঠাগুলিকে পুনরায় প্যাক করে না।", + "Content": "কন্টেন্ট", + "primary": "প্রাথমিক", + "Last Updated": "শেষ আপডেট করা হয়েছে", + "descriptionHelpText": "অভ্যন্তরীণ ড্যাশবোর্ডে দেখানো হয়েছে। প্রদর্শনের আগে মার্কডাউন অনুমোদিত এবং স্যানিটাইজ করা হয়েছে (স্থান এবং ইন্ডেন্টেশন সংরক্ষণ করে)।", + "No monitors available.": "কোনও মনিটর পাওয়া যাচ্ছে না।", + "Custom CSS": "কাস্টম সিএসএস", + "deleteStatusPageMsg": "আপনি কি নিশ্চিত যে এই স্ট্যাটাস পৃষ্ঠাটি মুছে ফেলতে চান?", + "deleteProxyMsg": "আপনি কি নিশ্চিত যে সকল মনিটরের জন্য এই প্রক্সিটি মুছে ফেলতে চান?", + "enableProxyDescription": "এই প্রক্সিটি সক্রিয় না হওয়া পর্যন্ত মনিটরের অনুরোধগুলিতে প্রভাব ফেলবে না। আপনি অ্যাক্টিভেশন স্ট্যাটাসের মাধ্যমে সমস্ত মনিটর থেকে অস্থায়ীভাবে প্রক্সিটি নিষ্ক্রিয় করতে পারেন।", + "Not installed": "ইনস্টল করা নেই", + "statusPageSpecialSlugDesc": "বিশেষ স্লাগ {0}: কোনও স্লাগ না থাকলে এই পৃষ্ঠাটি দেখানো হবে", + "Don't know how to get the token? Please read the guide:": "টোকেন কিভাবে পাবেন জানেন না? অনুগ্রহ করে নির্দেশিকাটি পড়ুন:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "আপনি যদি বর্তমানে Cloudflare Tunnel এর মাধ্যমে সংযোগ স্থাপন করেন তবে বর্তমান সংযোগটি বিচ্ছিন্ন হতে পারে। আপনি কি নিশ্চিতভাবে এটি বন্ধ করতে চান? এটি নিশ্চিত করতে আপনার বর্তমান পাসওয়ার্ডটি টাইপ করুন।", + "Refresh Interval Description": "স্ট্যাটাস পৃষ্ঠাটি প্রতি {0} সেকেন্ডে একটি সম্পূর্ণ সাইট রিফ্রেশ করবে", + "RadiusCalledStationIdDescription": "কল করা ডিভাইসের শনাক্তকারী", + "Certificate Expiry Notification": "সার্টিফিকেটের মেয়াদ শেষ হওয়ার বিজ্ঞপ্তি", + "Check how to config it for WebSocket": "রিভার্স প্রক্সি ব্যবহার করছেন", + "The resource is no longer available.": "রিসোর্সটি আর উপলব্ধ নেই।", + "settingsCertificateExpiry": "TLS সার্টিফিকেটের মেয়াদ শেষ", + "Docker Daemon": "ডকার ডেমন", + "DockerHostRequired": "এই মনিটরের জন্য ডকার হোস্ট সেট করুন।", + "deleteDockerHostMsg": "আপনি কি নিশ্চিত যে সমস্ত মনিটরের জন্য এই ডকার হোস্টটি মুছে ফেলতে চান?", + "tailscalePingWarning": "টেইলস্কেল পিং মনিটর ব্যবহার করার জন্য, আপনাকে ডকার ছাড়াই আপটাইম কুমা ইনস্টল করতে হবে এবং আপনার সার্ভারে টেইলস্কেল ক্লায়েন্টও ইনস্টল করতে হবে।", + "auto-select": "স্বয়ংক্রিয় নির্বাচন", + "Certificate Chain": "সার্টিফিকেট চেইন", + "Select": "নির্বাচন করুন", + "selectedMonitorCount": "নির্বাচিত: {0}", + "Check/Uncheck": "চেক/আনচেক করুন", + "Powered by": "দ্বারা চালিত", + "Customize": "কাস্টমাইজ করুন", + "Custom Footer": "কাস্টম ফুটার", + "Proxies": "প্রক্সি", + "default": "ডিফল্ট", + "enabled": "সক্ষম করা হয়েছে", + "setAsDefault": "ডিফল্ট হিসেবে সেট করুন", + "proxyDescription": "প্রক্সিগুলি কাজ করার জন্য একটি মনিটরে বরাদ্দ করতে হবে।", + "setAsDefaultProxyDescription": "নতুন মনিটরের জন্য এই প্রক্সিটি ডিফল্টরূপে সক্রিয় থাকবে। আপনি এখনও প্রতিটি মনিটরের জন্য আলাদাভাবে প্রক্সিটি অক্ষম করতে পারেন।", + "Valid": "বৈধ", + "Invalid": "অবৈধ", + "User": "ব্যবহারকারী", + "No Proxy": "কোনও প্রক্সি নেই", + "Coming Soon": "শীঘ্রই আসছে", + "noDockerHostMsg": "উপলব্ধ নেই। প্রথমে একটি ডকার হোস্ট সেট আপ করুন।", + "socket": "সকেট", + "tcp": "টিসিপি / এইচটিটিপি", + "Docker Container": "ডকার কন্টেইনার", + "Docker Host": "ডকার হোস্ট", + "Docker Hosts": "ডকার হোস্ট", + "Domain": "ডোমেইন", + "Workstation": "ওয়ার্কস্টেশন", + "Packet Size": "প্যাকেটের আকার", + "Bot Token": "বট টোকেন", + "wayToGetTelegramToken": "আপনি {0} থেকে একটি টোকেন পেতে পারেন।", + "telegramMessageThreadID": "(ঐচ্ছিক) মেসেজ থ্রেড আইডি", + "Switch to Light Theme": "সাদা থিমে স্যুইচ করুন", + "Description": "বিবরণ", + "No Monitors": "কোনও মনিটর নেই", + "templateLimitedToUpDownCertNotifications": "শুধুমাত্র UP/DOWN/সার্টিফিকেটের মেয়াদ শেষ হওয়ার বিজ্ঞপ্তির জন্য উপলব্ধ", + "webhookBodyPresetOption": "প্রিসেট - {0}", + "Priority": "অগ্রাধিকার", + "emojiCheatSheet": "ইমোজি চিট শিট: {0}", + "Read more": "আরও পড়ুন", + "appriseInstalled": "অ্যাপ্রিজ ইনস্টল করা আছে।", + "appriseNotInstalled": "অ্যাপ্রিজ ইনস্টল করা নেই। {0}", + "PushUrl": "পুশ ইউআরএল", + "Resend Notification if Down X times consecutively": "টানা X বার ডাউন হলে বিজ্ঞপ্তি পুনরায় পাঠান", + "resendDisabled": "পুনরায় পাঠানো বন্ধ আছে", + "telegramSendSilentlyDescription": "নীরবে বার্তা পাঠায়। ব্যবহারকারীরা কোনও শব্দ ছাড়াই একটি বিজ্ঞপ্তি পাবেন।", + "telegramProtectContent": "ফরোয়ার্ডিং/সঞ্চয় রক্ষা করুন", + "telegramProtectContentDescription": "যদি সক্ষম করা হয়, তাহলে টেলিগ্রামের বট বার্তাগুলি ফরোয়ার্ড এবং সংরক্ষণ থেকে সুরক্ষিত থাকবে।", + "telegramUseTemplate": "কাস্টম মেসেজ টেমপ্লেট ব্যবহার করুন", + "telegramUseTemplateDescription": "সক্রিয় থাকলে, একটি কাস্টম টেমপ্লেট ব্যবহার করে বার্তাটি পাঠানো হবে।", + "telegramTemplateFormatDescription": "টেলিগ্রাম বার্তাগুলির জন্য বিভিন্ন মার্কআপ ভাষা ব্যবহারের অনুমতি দেয়, নির্দিষ্ট বিবরণের জন্য টেলিগ্রাম {0} দেখুন।", + "telegramServerUrlDescription": "টেলিগ্রামের বট এপিআই সীমাবদ্ধতা তুলে নিতে অথবা অবরুদ্ধ এলাকায় (চীন, ইরান, ইত্যাদি) অ্যাক্সেস পেতে। আরও তথ্যের জন্য {0} এ ক্লিক করুন। ডিফল্ট: {1", + "disableCloudflaredNoAuthMsg": "আপনি No Auth মোডে আছেন, পাসওয়ার্ডের প্রয়োজন নেই।", + "wayToGetLineNotifyToken": "আপনি {0} থেকে একটি অ্যাক্সেস টোকেন পেতে পারেন", + "Examples": "উদাহরণ", + "Home Assistant URL": "হোম অ্যাসিস্ট্যান্ট ইউআরএল", + "Notification Service": "বিজ্ঞপ্তি পরিষেবা", + "cronExpression": "ক্রোন এক্সপ্রেশন", + "dayOfMonth": "মাসের দিন", + "lastDay": "শেষ দিন", + "lastDay1": "মাসের শেষ দিন", + "lastDay3": "মাসের ৩য় শেষ দিন", + "lastDay4": "মাসের ৪র্থ শেষ দিন", + "No Maintenance": "রক্ষণাবেক্ষণ নেই", + "pauseMaintenanceMsg": "তুমি কি নিশ্চিতভাবে বিরতি দিতে চাও?", + "maintenanceStatus-under-maintenance": "রক্ষণাবেক্ষণাধীন", + "maintenanceStatus-inactive": "নিষ্ক্রিয়", + "maintenanceStatus-scheduled": "নির্ধারিত", + "maintenanceStatus-ended": "শেষ হয়েছে", + "Display Timezone": "টাইমজোন প্রদর্শন করুন", + "Server Timezone": "সার্ভার টাইমজোন", + "statusPageMaintenanceEndDate": "শেষ", + "IconUrl": "আইকন URL", + "Enable": "সক্রিয় করুন", + "chromeExecutable": "ক্রোম/ক্রোমিয়াম এক্সিকিউটেবল", + "chromeExecutableAutoDetect": "স্বয়ংক্রিয় সনাক্তকরণ", + "dnsCacheDescription": "এটি কিছু IPv6 পরিবেশে কাজ নাও করতে পারে, যদি আপনার কোনও সমস্যা হয় তবে এটি অক্ষম করুন।", + "Clone": "ক্লোন", + "cloneOf": "{0} এর ক্লোন", + "emailCustomBody": "কাস্টম বডি", + "leave blank for default body": "ডিফল্ট বডির জন্য ফাঁকা রাখুন", + "emailTemplateHeartbeatJSON": "হৃদস্পন্দন বর্ণনাকারী বস্তু", + "emailTemplateMsg": "বিজ্ঞপ্তির বার্তা", + "Discord Webhook URL": "ডিসকর্ড ওয়েবহুক ইউআরএল", + "Bot Display Name": "বট প্রদর্শনের নাম", + "Prefix Custom Message": "প্রিফিক্স কাস্টম বার্তা", + "Hello @everyone is...": "হ্যালো {'@'}সবাই…", + "Select message type": "বার্তার ধরণ নির্বাচন করুন", + "Send to channel": "চ্যানেলে পাঠান", + "postToExistingThread": "বিদ্যমান থ্রেড / ফোরাম পোস্টে পোস্ট করুন", + "forumPostName": "ফোরাম পোস্টের নাম", + "threadForumPostID": "থ্রেড / ফোরাম পোস্ট আইডি", + "wayToGetDiscordThreadId": "একটি থ্রেড / ফোরাম পোস্ট আইডি পাওয়া চ্যানেল আইডি পাওয়ার মতোই। আইডি কীভাবে পাবেন সে সম্পর্কে আরও পড়ুন {0}", + "wayToGetZohoCliqURL": "আপনি কীভাবে একটি ওয়েবহুক URL {0} তৈরি করবেন তা শিখতে পারেন।", + "wayToCheckSignalURL": "কিভাবে সেট আপ করবেন তা দেখতে আপনি এই URL টি দেখতে পারেন:", + "Number": "সংখ্যা", + "Recipients": "প্রাপক", + "Channel access token": "চ্যানেল অ্যাক্সেস টোকেন", + "lineDevConsoleTo": "লাইন ডেভেলপার কনসোল - {0}", + "Basic Settings": "মৌলিক সেটিংস", + "User ID": "ব্যবহারকারী আইডি", + "Your User ID": "আপনার ব্যবহারকারী আইডি", + "Icon URL": "আইকন URL", + "aboutMattermostChannelName": "\"চ্যানেলের নাম\" ফিল্ডে চ্যানেলের নাম লিখে ওয়েবহুক যে ডিফল্ট চ্যানেলে পোস্ট করে, আপনি সেটিকে ওভাররাইড করতে পারেন। এটি ম্যাটারমোস্ট ওয়েবহুক সেটিংসে সক্রিয় করতে হবে। যেমন: #other-channel", + "infiniteRetention": "অসীম ধরে রাখার জন্য 0 তে সেট করুন।", + "enableGRPCTls": "TLS সংযোগের মাধ্যমে gRPC অনুরোধ পাঠানোর অনুমতি দিন", + "acceptedStatusCodesDescription": "সফল প্রতিক্রিয়া হিসেবে বিবেচিত স্ট্যাটাস কোডগুলি নির্বাচন করুন।", + "deleteMonitorMsg": "আপনি কি নিশ্চিত যে এই মনিটরটি মুছে ফেলতে চান?", + "telegramServerUrl": "(ঐচ্ছিক) সার্ভার ইউআরএল", + "chatIDNotFound": "চ্যাট আইডি খুঁজে পাওয়া যাচ্ছে না; অনুগ্রহ করে প্রথমে এই বটে একটি বার্তা পাঠান", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "\"ডেভেলপার টুলস > সার্ভিসেস\" এর অধীনে হোম অ্যাসিস্ট্যান্টে নোটিফিকেশন পরিষেবার একটি তালিকা পাওয়া যাবে এবং আপনার ডিভাইস/ফোনের নাম খুঁজে পেতে \"নোটিফিকেশন\" অনুসন্ধান করুন।", + "Automations can optionally be triggered in Home Assistant:": "হোম অ্যাসিস্ট্যান্টে ঐচ্ছিকভাবে অটোমেশন ট্রিগার করা যেতে পারে:", + "Trigger type:": "ট্রিগারের ধরণ:", + "Event type:": "ইভেন্টের ধরণ:", + "Event data:": "ইভেন্ট ডেটা:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "তারপর একটি অ্যাকশন বেছে নিন, উদাহরণস্বরূপ দৃশ্যটি এমন জায়গায় স্যুইচ করুন যেখানে একটি RGB আলো লাল।", + "Frontend Version": "ফ্রন্টএন্ড সংস্করণ", + "Frontend Version do not match backend version!": "ফ্রন্টএন্ড ভার্সন ব্যাকএন্ড ভার্সনের সাথে মেলে না!", + "backupRecommend": "অনুগ্রহ করে ভলিউম অথবা ডেটা ফোল্ডার (./data/) সরাসরি ব্যাকআপ করুন।", + "weekdayShortThu": "বৃহস্পতি", + "supportBaleChatID": "সরাসরি চ্যাট / গ্রুপ / চ্যানেলের চ্যাট আইডি সমর্থন করুন", + "wayToGetBaleChatID": "আপনি বটে একটি বার্তা পাঠিয়ে এবং chat_id দেখতে এই URL-এ গিয়ে আপনার চ্যাট আইডি পেতে পারেন:", + "wayToGetBaleToken": "আপনি {0} থেকে একটি টোকেন পেতে পারেন।", + "Long-Lived Access Token": "দীর্ঘজীবী অ্যাক্সেস টোকেন", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "আপনার প্রোফাইল নামের উপর (নীচে বাম দিকে) ক্লিক করে এবং নীচে স্ক্রোল করে তারপর Create Token ক্লিক করে দীর্ঘস্থায়ী অ্যাক্সেস টোকেন তৈরি করা যেতে পারে। ", + "default: notify all devices": "ডিফল্ট: সমস্ত ডিভাইসকে অবহিত করুন", + "weekdayShortSat": "শনি", + "weekdayShortSun": "রবি", + "dayOfWeek": "সপ্তাহের দিন", + "weekdayShortFri": "শুক্র", + "Maintenance Time Window of a Day": "দিনের রক্ষণাবেক্ষণের সময়সূচী", + "Effective Date Range": "কার্যকর তারিখের পরিসর (ঐচ্ছিক)", + "DateTime Range": "তারিখসময় পরিসীমা", + "plugin": "প্লাগইন | প্লাগইন", + "install": "ইনস্টল করুন", + "installing": "ইনস্টল করা হচ্ছে", + "uninstall": "আনইনস্টল করুন", + "uninstalling": "আনইনস্টল করা হচ্ছে", + "notificationRegional": "আঞ্চলিক", + "Clone Monitor": "ক্লোন মনিটর", + "smtp": "ইমেল (SMTP)", + "Use HTML for custom E-mail body": "কাস্টম ইমেল বডির জন্য HTML ব্যবহার করুন", + "secureOptionNone": "কোনটিই নয় / STARTTLS (25, 587)", + "secureOptionTLS": "টিএলএস (৪৬৫)", + "Ignore TLS Error": "TLS ত্রুটি উপেক্ষা করুন", + "From Email": "ইমেল থেকে", + "emailCustomisableContent": "কাস্টমাইজযোগ্য কন্টেন্ট", + "smtpLiquidIntroduction": "নিম্নলিখিত দুটি ক্ষেত্র তরল টেমপ্লেটিং ভাষার মাধ্যমে টেমপ্লেটযোগ্য। ব্যবহারের নির্দেশাবলীর জন্য অনুগ্রহ করে {0} দেখুন। এগুলি উপলব্ধ ভেরিয়েবল:", + "emailCustomSubject": "কাস্টম বিষয়", + "leave blank for default subject": "ডিফল্ট বিষয়ের জন্য ফাঁকা রাখুন", + "Channel access token (Long-lived)": "চ্যানেল অ্যাক্সেস টোকেন (দীর্ঘস্থায়ী)", + "and": "এবং", + "or": "অথবা", + "sameAsServerTimezone": "সার্ভার টাইমজোনের মতোই", + "Optional": "ঐচ্ছিক", + "Date and Time": "তারিখ এবং সময়", + "dataRetentionTimeError": "ধরে রাখার সময়কাল অবশ্যই ০ বা তার বেশি হতে হবে", + "telegramSendSilently": "নীরবে পাঠান", + "To Email": "ইমেল করতে", + "smtpCC": "সিসি", + "smtpBCC": "বিসিসি", + "telegramMessageThreadIDDescription": "ফোরামের টার্গেট মেসেজ থ্রেড (বিষয়) এর জন্য ঐচ্ছিক অনন্য শনাক্তকারী; শুধুমাত্র ফোরাম সুপারগ্রুপের জন্য", + "trustProxyDescription": "'X-Forwarded-*' হেডারগুলিতে বিশ্বাস করুন। যদি আপনি সঠিক ক্লায়েন্ট আইপি পেতে চান এবং আপনার আপটাইম কুমা Nginx বা Apache এর মতো প্রক্সির পিছনে থাকে, তাহলে আপনার এটি সক্ষম করা উচিত।", + "backupOutdatedWarning": "বন্ধ: যেহেতু অনেক বৈশিষ্ট্য যোগ করা হয়েছে এবং এই ব্যাকআপ বৈশিষ্ট্যটি কিছুটা রক্ষণাবেক্ষণ করা হয়নি, তাই এটি সম্পূর্ণ ব্যাকআপ তৈরি বা পুনরুদ্ধার করতে পারে না।", + "lastDay2": "মাসের দ্বিতীয় শেষ দিন", + "maintenanceStatus-unknown": "অজানা", + "Disable": "অক্ষম করুন", + "enableNSCD": "সকল DNS অনুরোধ ক্যাশ করার জন্য NSCD (Name Service Cache Daemon) সক্ষম করুন", + "chromeExecutableDescription": "ডকার ব্যবহারকারীদের জন্য, যদি Chromium এখনও ইনস্টল না করা থাকে, তাহলে পরীক্ষার ফলাফল ইনস্টল করতে এবং প্রদর্শন করতে কয়েক মিনিট সময় লাগতে পারে। এতে ১ গিগাবাইট ডিস্ক স্পেস লাগে।", + "Single Maintenance Window": "একক রক্ষণাবেক্ষণ উইন্ডো", + "Edit Maintenance": "রক্ষণাবেক্ষণ সম্পাদনা করুন", + "loadingError": "তথ্য আনা যাচ্ছে না, দয়া করে পরে আবার চেষ্টা করুন।", + "confirmUninstallPlugin": "আপনি কি নিশ্চিত যে এই প্লাগইনটি আনইনস্টল করতে চান?", + "emailTemplateMonitorJSON": "মনিটরের বর্ণনাকারী বস্তু", + "emailTemplateLimitedToUpDownNotification": "শুধুমাত্র উপরে/নিচে হৃদস্পন্দনের জন্য উপলব্ধ, অন্যথায় শূন্য", + "wayToGetDiscordURL": "আপনি সার্ভার সেটিংস -> ইন্টিগ্রেশন -> ওয়েবহুক দেখুন -> নতুন ওয়েবহুক-এ গিয়ে এটি পেতে পারেন", + "Create new forum post": "নতুন ফোরাম পোস্ট তৈরি করুন", + "e.g. {discordThreadID}": "যেমন {discordThreadID}", + "whatHappensAtForumPost": "একটি নতুন ফোরাম পোস্ট তৈরি করুন। এটি বিদ্যমান পোস্টে বার্তা পোস্ট করে না। বিদ্যমান পোস্টে পোস্ট করতে \"{option}\" ব্যবহার করুন", + "wayToGetTeamsURL": "আপনি কীভাবে একটি ওয়েবহুক URL {0} তৈরি করবেন তা শিখতে পারেন।", + "needSignalAPI": "আপনার REST API সহ একটি সিগন্যাল ক্লায়েন্ট থাকা দরকার।", + "Line Developers Console": "লাইন ডেভেলপার কনসোল", + "Messaging API": "মেসেজিং এপিআই", + "wayToGetLineChannelToken": "প্রথমে {0} অ্যাক্সেস করুন, একটি প্রোভাইডার এবং চ্যানেল (মেসেজিং API) তৈরি করুন, তারপর আপনি উপরে উল্লিখিত মেনু আইটেমগুলি থেকে চ্যানেল অ্যাক্সেস টোকেন এবং ব্যবহারকারী আইডি পেতে পারেন।", + "aboutIconURL": "ডিফল্ট প্রোফাইল ছবি ওভাররাইড করার জন্য আপনি \"আইকন URL\"-এ একটি ছবির লিঙ্ক দিতে পারেন। আইকন ইমোজি সেট করা থাকলে ব্যবহার করা হবে না।", + "confirmDeleteTagMsg": "আপনি কি নিশ্চিত যে আপনি এই ট্যাগটি মুছে ফেলতে চান? এই ট্যাগের সাথে সম্পর্কিত মনিটরগুলি মুছে ফেলা হবে না।", + "grpcMethodDescription": "পদ্ধতির নাম camelCase ফর্ম্যাটে রূপান্তর করা হয় যেমন sayHello, check, ইত্যাদি।", + "supportTelegramChatID": "সরাসরি চ্যাট / গ্রুপ / চ্যানেলের চ্যাট আইডি সমর্থন করুন", + "wayToGetTelegramChatID": "আপনি বটে একটি বার্তা পাঠিয়ে এবং chat_id দেখতে এই URL-এ গিয়ে আপনার চ্যাট আইডি পেতে পারেন:", + "YOUR BOT TOKEN HERE": "তোমার বট টোকেন এখানে", + "recurringInterval": "ব্যবধান", + "startDateTime": "শুরুর তারিখ/সময়", + "endDateTime": "শেষ তারিখ/সময়", + "cronSchedule": "সময়সূচী: ", + "invalidCronExpression": "অবৈধ ক্রোন এক্সপ্রেশন: {0}", + "Recurring": "পুনরাবৃত্তিমূলক", + "strategyManual": "সক্রিয়/নিষ্ক্রিয় ম্যানুয়ালি", + "warningTimezone": "এটি সার্ভারের টাইমজোন ব্যবহার করছে", + "weekdayShortMon": "সোম", + "weekdayShortTue": "মঙ্গল", + "weekdayShortWed": "বুধ", + "Schedule Maintenance": "সময়সূচী রক্ষণাবেক্ষণ", + "Access Token": "অ্যাক্সেস টোকেন", + "Enable DNS Cache": "(অবঞ্চিত) HTTP(গুলি) মনিটরের জন্য DNS ক্যাশে সক্ষম করুন", + "ipFamilyDescriptionAutoSelect": "আইপি ফ্যামিলি নির্ধারণের জন্য {happyEyeballs} ব্যবহার করে।", + "Ip Family": "আইপি পরিবার", + "HTTP Method": "HTTP পদ্ধতি", + "Saved.": "সংরক্ষিত।", + "authUserInactiveOrDeleted": "ব্যবহারকারী নিষ্ক্রিয় অথবা মুছে ফেলা হয়েছে।", + "authInvalidToken": "অবৈধ টোকেন।", + "authIncorrectCreds": "ভুল ব্যবহারকারীর নাম বা পাসওয়ার্ড।", + "2faAlreadyEnabled": "2FA ইতিমধ্যেই সক্রিয় আছে।", + "2faEnabled": "2FA সক্রিয়।", + "2faDisabled": "2FA অক্ষম।", + "successBackupRestored": "ব্যাকআপ সফলভাবে পুনরুদ্ধার করা হয়েছে।", + "successDisabled": "সফলভাবে অক্ষম করা হয়েছে।", + "tagNotFound": "ট্যাগ পাওয়া যায়নি।", + "foundChromiumVersion": "Chromium/Chrome পাওয়া গেছে। সংস্করণ: {0}", + "Remote Browsers": "রিমোট ব্রাউজার", + "evolutionInstanceName": "ইনস্ট্যান্স নাম", + "What is a Remote Browser?": "রিমোট ব্রাউজার কী?", + "documentationOf": "{0} ডকুমেন্টেশন", + "gtxMessagingApiKeyHint": "আপনি আপনার API কীটি এখানে খুঁজে পেতে পারেন: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "To Phone Number": "ফোন নম্বরে", + "gtxMessagingToHint": "আন্তর্জাতিক ফর্ম্যাট, যার আগে \"+\" ({e164}, {e212} বা {e214}) থাকবে", + "Alphanumeric (recommended)": "বর্ণসংখ্যাসূচক (প্রস্তাবিত)", + "Telephone number": "টেলিফোন নম্বর", + "cellsyntOriginatortypeAlphanumeric": "আলফানিউমেরিক স্ট্রিং (সর্বোচ্চ ১১টি বর্ণানুক্রমিক অক্ষর)। প্রাপকরা বার্তার উত্তর দিতে পারবেন না।", + "Originator": "প্রবর্তক", + "Destination": "গন্তব্য", + "Allow Long SMS": "দীর্ঘ এসএমএস মঞ্জুর করুন", + "cellsyntSplitLongMessages": "লম্বা বার্তাগুলিকে সর্বোচ্চ ৬টি ভাগে ভাগ করুন। ১৫৩ x ৬ = ৯১৮ অক্ষর।", + "max 15 digits": "সর্বোচ্চ ১৫টি সংখ্যা", + "max 11 alphanumeric characters": "সর্বোচ্চ ১১টি বর্ণসাংখ্যিক অক্ষর", + "Community String": "কমিউনিটি স্ট্রিং", + "OID (Object Identifier)": "OID (বস্তু শনাক্তকারী)", + "snmpOIDHelptext": "আপনি যে সেন্সর বা স্ট্যাটাসটি পর্যবেক্ষণ করতে চান তার OID লিখুন। OID সম্পর্কে নিশ্চিত না হলে MIB ব্রাউজার বা SNMP সফ্টওয়্যারের মতো নেটওয়ার্ক ম্যানেজমেন্ট টুল ব্যবহার করুন।", + "Condition": "অবস্থা", + "Please enter a valid OID.": "দয়া করে একটি বৈধ OID লিখুন।", + "apiKeysDisabledMsg": "প্রমাণীকরণ অক্ষম থাকার কারণে API কীগুলি অক্ষম করা হয়েছে।", + "Host Onesender": "হোস্ট ওয়ানসেন্ডার", + "Token Onesender": "টোকেন ওয়ানসেন্ডার", + "Private Number": "ব্যক্তিগত নম্বর", + "groupOnesenderDesc": "নিশ্চিত করুন যে GroupIDটি বৈধ। Group-এ বার্তা পাঠাতে, উদাহরণস্বরূপ: 628123456789-342345", + "Group ID": "গ্রুপ আইডি", + "New Group": "নতুন গ্রুপ", + "Authentication Method": "প্রমাণীকরণ পদ্ধতি", + "Form Data Body": "ফর্ম ডেটা বডি", + "OAuth Token URL": "OAuth টোকেন ইউআরএল", + "Client ID": "ক্লায়েন্ট আইডি", + "Client Secret": "ক্লায়েন্ট সিক্রেট", + "OAuth Scope": "OAuth স্কোপ", + "Go back to home page.": "হোম পেজে ফিরে যান।", + "Lost connection to the socket server.": "সকেট সার্ভারের সাথে সংযোগ বিচ্ছিন্ন।", + "SIGNL4": "SIGNL4 সম্পর্কে", + "Conditions": "শর্তাবলী", + "conditionAdd": "শর্ত যোগ করুন", + "not starts with": "দিয়ে শুরু হয় না", + "not ends with": "শেষ হয় না", + "less than": "এর চেয়ে কম", + "greater than": "এর চেয়ে বড়", + "Arcade": "আর্কেড", + "Correct": "সঠিক", + "Fail": "ব্যর্থ", + "Harp": "বীণা", + "Reveal": "প্রকাশ করুন", + "Bubble": "বুদ্বুদ", + "Doorbell": "ডোরবেল", + "Flute": "বাঁশি", + "Money": "টাকা", + "Time Sensitive (iOS Only)": "সময় সংবেদনশীল (শুধুমাত্র iOS)", + "From": "থেকে", + "Can be found on:": "এখানে পাওয়া যাবে: {0}", + "The phone number of the recipient in E.164 format.": "প্রাপকের ফোন নম্বর E.164 ফর্ম্যাটে।", + "Separate multiple email addresses with commas": "একাধিক ইমেল ঠিকানা কমা দিয়ে আলাদা করুন", + "pingIntervalAdjustedInfo": "প্যাকেট গণনা, গ্লোবাল টাইমআউট এবং প্রতি-পিং টাইমআউটের উপর ভিত্তি করে ব্যবধান সমন্বয় করা হয়েছে", + "OneChatBotId": "সবচেয়ে সক্রিয় বট", + "YZJ Robot Token": "YZJ রোবট টোকেন", + "Plain Text": "সরল লেখা", + "Message Template": "বার্তা টেমপ্লেট", + "smsplanetApiToken": "SMSPlanet API-এর জন্য টোকেন", + "smsplanetApiDocs": "API টোকেন প্রাপ্তির বিস্তারিত তথ্য {the_smsplanet_documentation}-এ পাওয়া যাবে।", + "smsplanetNeedToApproveName": "ক্লায়েন্ট প্যানেলে অনুমোদিত হতে হবে", + "Disable URL in Notification": "বিজ্ঞপ্তিতে URL অক্ষম করুন", + "Happy Eyeballs algorithm": "হ্যাপি আইবলস অ্যালগরিদম", + "openModalTo": "{0} তে মোডাল খুলুন", + "Add a domain": "একটি ডোমেন যোগ করুন", + "Remove domain": "'{0}' ডোমেনটি সরান", + "Icon Emoji": "আইকন ইমোজি", + "Authorization Header": "অনুমোদনের শিরোনাম", + "OAuth Audience": "OAuth শ্রোতা", + "Optional: The audience to request the JWT for": "ঐচ্ছিক: JWT-এর কাছে অনুরোধ করার জন্য দর্শকরা", + "SIGNL4 Webhook URL": "SIGNL4 ওয়েবহুক ইউআরএল", + "not equals": "সমান নয়", + "contains": "ধারণ করে", + "conditionValuePlaceholder": "মূল্য", + "Mentioning": "উল্লেখ করা", + "onebotMessageType": "ওয়ানবট মেসেজের ধরণ", + "senderSevenIO": "নম্বর বা নাম পাঠানো হচ্ছে", + "Originator type": "উৎপত্তিকারীর ধরণ", + "Font Twemoji by Twitter licensed under": "টুইটারের ফন্ট টুইমোজি লাইসেন্সপ্রাপ্ত", + "deleteMaintenanceMsg": "আপনি কি নিশ্চিত যে এই রক্ষণাবেক্ষণটি মুছে ফেলতে চান?", + "dnsPortDescription": "DNS সার্ভার পোর্ট। ডিফল্ট 53। আপনি যেকোনো সময় পোর্ট পরিবর্তন করতে পারেন।", + "resolverserverDescription": "ক্লাউডফ্লেয়ার হল ডিফল্ট সার্ভার। আপনি যেকোনো সময় রেজলভার সার্ভার পরিবর্তন করতে পারেন।", + "rrtypeDescription": "আপনি যে RR টাইপটি পর্যবেক্ষণ করতে চান তা নির্বাচন করুন", + "pauseMonitorMsg": "তুমি কি নিশ্চিতভাবে বিরতি দিতে চাও?", + "enableDefaultNotificationDescription": "নতুন মনিটরের জন্য এই বিজ্ঞপ্তিটি ডিফল্টরূপে সক্রিয় থাকবে। আপনি এখনও প্রতিটি মনিটরের জন্য আলাদাভাবে বিজ্ঞপ্তিটি অক্ষম করতে পারেন।", + "Clear All Events": "সকল ইভেন্ট সাফ করুন", + "clearAllEventsMsg": "আপনি কি নিশ্চিত যে সমস্ত ইভেন্ট মুছে ফেলতে চান?", + "Events cleared successfully": "ইভেন্টগুলি সফলভাবে সাফ করা হয়েছে।", + "No monitors found": "কোন মনিটর পাওয়া যায়নি।", + "Strategy": "কৌশল", + "Free Mobile User Identifier": "বিনামূল্যে মোবাইল ব্যবহারকারী শনাক্তকারী", + "Free Mobile API Key": "বিনামূল্যে মোবাইল API কী", + "Enable TLS": "TLS সক্ষম করুন", + "goAlertIntegrationKeyInfo": "পরিষেবার জন্য জেনেরিক API ইন্টিগ্রেশন কী এই ফর্ম্যাটে পান \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" সাধারণত কপি করা URL এর টোকেন প্যারামিটারের মান।", + "SecretAccessKey": "অ্যাক্সেসকি সিক্রেট", + "PhoneNumbers": "ফোন নম্বর", + "SignName": "সাইননাম", + "Bark API Version": "বার্ক এপিআই সংস্করণ", + "Bark Endpoint": "বার্ক এন্ডপয়েন্ট", + "For safety, must use secret key": "নিরাপত্তার জন্য, গোপন চাবি ব্যবহার করতে হবে", + "Mention group": "{গ্রুপ} উল্লেখ করুন", + "Mention Mobile List": "মোবাইল তালিকা উল্লেখ করুন", + "Dingtalk Mobile List": "মোবাইল তালিকা", + "Dingtalk User List": "ব্যবহারকারী আইডি তালিকা", + "Enter a list of userId": "ব্যবহারকারীর আইডির একটি তালিকা লিখুন", + "Invalid mobile": "অবৈধ মোবাইল [{মোবাইল}]", + "Invalid userId": "অবৈধ ব্যবহারকারী আইডি [{userId}]", + "Platform": "প্ল্যাটফর্ম", + "High": "উচ্চ", + "Retry": "পুনরায় চেষ্টা করুন", + "Topic": "বিষয়", + "WeCom Bot Key": "WeCom বট কী", + "Proxy server has authentication": "প্রক্সি সার্ভারের প্রমাণীকরণ আছে", + "aboutNotifyChannel": "নোটিফাই চ্যানেল চ্যানেলের সকল সদস্যের জন্য একটি ডেস্কটপ বা মোবাইল বিজ্ঞপ্তি ট্রিগার করবে, তাদের উপলব্ধতা সক্রিয় বা দূরে সেট করা হোক না কেন।", + "signalImportant": "গুরুত্বপূর্ণ: আপনি প্রাপকদের মধ্যে গ্রুপ এবং সংখ্যা মিশ্রিত করতে পারবেন না!", + "smtpDkimSettings": "DKIM সেটিংস", + "smtpDkimDesc": "ব্যবহারের জন্য অনুগ্রহ করে Nodemailer DKIM {0} দেখুন।", + "documentation": "ডকুমেন্টেশন", + "smtpDkimDomain": "ডোমেইন নাম", + "smtpDkimKeySelector": "কী নির্বাচক", + "smtpDkimPrivateKey": "ব্যক্তিগত চাবি", + "smtpDkimHashAlgo": "হ্যাশ অ্যালগরিদম (ঐচ্ছিক)", + "smtpDkimheaderFieldNames": "স্বাক্ষর করার জন্য হেডার কী (ঐচ্ছিক)", + "Integration Key": "ইন্টিগ্রেশন কী", + "do nothing": "কিছু করো না", + "auto acknowledged": "স্বয়ংক্রিয়ভাবে স্বীকৃত", + "alertaEnvironment": "পরিবেশ", + "alertaApiKey": "API কী", + "smseagleContact": "ফোনবুকের যোগাযোগের নাম(গুলি)", + "smseagleContactV2": "ফোনবুক যোগাযোগ আইডি(গুলি)", + "smseagleRecipientType": "প্রাপকের ধরণ", + "smseagleEncoding": "ইউনিকোড হিসেবে পাঠান (ডিফল্ট=GSM-7)", + "smspartnerSenderName": "এসএমএস প্রেরকের নাম", + "Recipient Number": "প্রাপকের নম্বর", + "From Name/Number": "নাম/নম্বর থেকে", + "Legacy Octopush-DM": "লিগ্যাসি অক্টোপাস-ডিএম", + "Server URL should not contain the nfty topic": "ntfy বিষয়", + "onebotHttpAddress": "ওয়ানবট HTTP ঠিকানা", + "onebotGroupMessage": "গ্রুপ", + "onebotPrivateMessage": "ব্যক্তিগত", + "onebotUserOrGroupId": "গ্রুপ/ব্যবহারকারী আইডি", + "PushDeer Server": "পুশডিয়ার সার্ভার", + "PushDeer Key": "পুশডিয়ার কী", + "wayToGetClickSendSMSToken": "আপনি {0} থেকে API ব্যবহারকারীর নাম এবং API কী পেতে পারেন।", + "Custom Monitor Type": "কাস্টম মনিটরের ধরণ", + "disableAPIKeyMsg": "আপনি কি নিশ্চিত যে আপনি এই API কীটি অক্ষম করতে চান?", + "Generate": "উৎপন্ন করুন", + "Badge Label Prefix": "ব্যাজ লেবেল উপসর্গ", + "Badge Up Color": "ব্যাজ আপ রঙ", + "Badge Pending Color": "ব্যাজ মুলতুবি রঙ", + "Badge Maintenance Color": "ব্যাজ রক্ষণাবেক্ষণের রঙ", + "Badge Warn Days": "ব্যাজ সতর্কীকরণ দিনগুলি", + "Badge Down Days": "ব্যাজ ডাউন ডেজ", + "Badge Style": "ব্যাজ স্টাইল", + "Badge URL": "ব্যাজ ইউআরএল", + "Group": "গ্রুপ", + "Monitor Group": "মনিটর গ্রুপ", + "monitorToastMessagesLabel": "টোস্ট বিজ্ঞপ্তিগুলি পর্যবেক্ষণ করুন", + "toastErrorTimeout": "ত্রুটি বিজ্ঞপ্তির জন্য সময়সীমা শেষ", + "toastSuccessTimeout": "সাফল্যের বিজ্ঞপ্তির সময়সীমা শেষ", + "Kafka Brokers": "কাফকা ব্রোকার্স", + "Enter the list of brokers": "ব্রোকারদের তালিকা লিখুন", + "Kafka SASL Options": "কাফকা SASL বিকল্পগুলি", + "FlashDuty Severity": "তীব্রতা", + "Mechanism": "প্রক্রিয়া", + "FlashDuty Push URL": "পুশ ইউআরএল", + "FlashDuty Push URL Placeholder": "সতর্কতামূলক ইন্টিগ্রেশন পৃষ্ঠা থেকে কপি করুন", + "AccessKey Id": "অ্যাক্সেস কী আইডি", + "Secret AccessKey": "গোপন অ্যাক্সেস কী", + "Session Token": "সেশন টোকেন", + "Close": "বন্ধ করা", + "cacheBusterParam": "{0} প্যারামিটার যোগ করুন", + "cacheBusterParamDescription": "ক্যাশে এড়িয়ে যাওয়ার জন্য এলোমেলোভাবে তৈরি প্যারামিটার।", + "gamedigGuessPort": "গেমডিগ: গেস পোর্ট", + "Message format": "বার্তার ফর্ম্যাট", + "successPaused": "সফলভাবে বিরতি দেওয়া হয়েছে।", + "successResumed": "সফলভাবে পুনরায় শুরু হয়েছে।", + "successEdited": "সফলভাবে সম্পাদিত।", + "Remote Browser not found!": "রিমোট ব্রাউজার খুঁজে পাওয়া যায়নি!", + "self-hosted container": "স্ব-হোস্টেড কন্টেইনার", + "useRemoteBrowser": "একটি রিমোট ব্রাউজার ব্যবহার করুন", + "GrafanaOncallUrl": "গ্রাফানা অনকল ইউআরএল", + "Browser Screenshot": "ব্রাউজারের স্ক্রিনশট", + "Command": "কমান্ড", + "wayToGetSevenIOApiKey": "app.seven.io > ডেভেলপার > api কী > সবুজ অ্যাড বোতামের অধীনে ড্যাশবোর্ডে যান", + "receiverSevenIO": "গ্রহণকারী নম্বর", + "apiKeySevenIO": "SevenIO API কী", + "wayToWriteWhapiRecipient": "আন্তর্জাতিক উপসর্গ সহ ফোন নম্বর, কিন্তু শুরুতে ({0}), যোগাযোগ আইডি ({1}) অথবা গ্রুপ আইডি ({2}) ছাড়া।", + "evolutionRecipient": "ফোন নম্বর / যোগাযোগ আইডি / গ্রুপ আইডি", + "threemaRecipientTypeIdentity": "থ্রিমা-আইডি", + "threemaRecipientTypeEmail": "ইমেইল ঠিকানা", + "threemaRecipientTypeIdentityFormat": "৮টি অক্ষর", + "threemaRecipientTypePhone": "ফোন নম্বর", + "threemaRecipientTypePhoneFormat": "E.164, লিডিং + ছাড়াই", + "threemaSenderIdentity": "গেটওয়ে-আইডি", + "threemaApiAuthenticationSecret": "গেটওয়ে-আইডি সিক্রেট", + "conditionDelete": "শর্ত মুছে ফেলুন", + "conditionAddGroup": "গ্রুপ যোগ করুন", + "signl4Docs": "SIGNL4 কীভাবে কনফিগার করবেন এবং SIGNL4 ওয়েবহুক URL কীভাবে পাবেন সে সম্পর্কে আরও তথ্য আপনি {0}-এ পেতে পারেন।", + "brevoApiHelp": "এখানে একটি API কী তৈরি করুন: {0}", + "brevoApiKey": "ব্রেভো এপিআই কী", + "brevoFromEmail": "ইমেল থেকে", + "brevoLeaveBlankForDefaultName": "ডিফল্ট নামের জন্য ফাঁকা রাখুন", + "brevoFromName": "নাম থেকে", + "Custom URL": "কাস্টম URL", + "the smsplanet documentation": "smsplanet ডকুমেন্টেশন", + "Phone numbers": "ফোন নম্বর", + "SNMP Version": "SNMP সংস্করণ", + "clearEventsMsg": "আপনি কি এই মনিটরের জন্য সমস্ত ইভেন্ট মুছে ফেলার বিষয়ে নিশ্চিত?", + "clearHeartbeatsMsg": "আপনি কি নিশ্চিত যে এই মনিটরের জন্য সমস্ত হার্টবিট মুছে ফেলতে চান?", + "confirmClearStatisticsMsg": "আপনি কি নিশ্চিত যে আপনি সমস্ত পরিসংখ্যান মুছে ফেলতে চান?", + "You can divide numbers with": "তুমি সংখ্যাগুলিকে ভাগ করতে পারো", + "Base URL": "বেস ইউআরএল", + "AccessKeyId": "অ্যাক্সেসকি আইডি", + "aboutKumaURL": "যদি আপনি Uptime Kuma URL ক্ষেত্রটি ফাঁকা রাখেন, তাহলে এটি Project GitHub পৃষ্ঠায় ডিফল্টভাবে প্রদর্শিত হবে।", + "pagertreeUrgency": "জরুরি অবস্থা", + "pagertreeSilent": "নীরব", + "pagertreeLow": "কম", + "pagertreeHigh": "উচ্চ", + "pagertreeCritical": "সমালোচনামূলক", + "pagertreeResolve": "অটো রিসলভ", + "pagertreeDoNothing": "কিছু করো না", + "lunaseaDeviceID": "ডিভাইস আইডি", + "lunaseaTarget": "লক্ষ্য", + "ntfyUsernameAndPassword": "ব্যবহারকারীর নাম এবং পাসওয়ার্ড", + "twilioAccountSID": "অ্যাকাউন্ট SID", + "twilioToNumber": "সংখ্যায়", + "Monitor Setting": "{0} এর মনিটর সেটিং", + "starts with": "দিয়ে শুরু হয়", + "Custom sound to override default notification sound": "ডিফল্ট বিজ্ঞপ্তি শব্দকে ওভাররাইড করার জন্য কাস্টম শব্দ", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "উত্তর পেতে চাইলে হয় একটি টেক্সট প্রেরক আইডি অথবা E.164 ফর্ম্যাটের একটি ফোন নম্বর।", + "RabbitMQ Nodes": "RabbitMQ ম্যানেজমেন্ট নোড", + "rabbitmqNodesDescription": "প্রোটোকল এবং পোর্ট সহ RabbitMQ ম্যানেজমেন্ট নোডের URL লিখুন। উদাহরণ: {0}", + "rabbitmqNodesRequired": "এই মনিটরের জন্য নোডগুলি সেট করুন।", + "equals": "সমান", + "not contains": "নেই", + "rabbitmqNodesInvalid": "RabbitMQ নোডের জন্য অনুগ্রহ করে একটি সম্পূর্ণ যোগ্যতাসম্পন্ন ('http' দিয়ে শুরু) URL ব্যবহার করুন।", + "RabbitMQ Username": "RabbitMQ ব্যবহারকারীর নাম", + "customUrlDescription": "মনিটরের URL এর পরিবর্তে ক্লিকযোগ্য URL হিসেবে ব্যবহার করা হবে।", + "OneChatAccessToken": "ওয়ানচ্যাট অ্যাক্সেস টোকেন", + "OneChatUserIdOrGroupId": "ওয়ানচ্যাট ব্যবহারকারী আইডি বা গ্রুপ আইডি", + "SecretKey": "সিক্রেটকি", + "Bark Sound": "বার্ক সাউন্ড", + "WebHookUrl": "ওয়েবহুকইউআরএল", + "Setup Proxy": "প্রক্সি সেট আপ করুন", + "Proxy Server": "প্রক্সি সার্ভার", + "ntfy Topic": "ntfy বিষয়", + "Badge Type": "ব্যাজের ধরণ", + "showCertificateExpiry": "সার্টিফিকেটের মেয়াদ দেখান", + "deleteNotificationMsg": "আপনি কি নিশ্চিত যে সমস্ত মনিটরের জন্য এই বিজ্ঞপ্তিটি মুছে ফেলতে চান?", + "API URL": "এপিআই ইউআরএল", + "Feishu WebHookUrl": "ফেইশু ওয়েবহুক ইউআরএল", + "matrixHomeserverURL": "হোমসার্ভার URL (http(s):// এবং ঐচ্ছিকভাবে পোর্ট সহ)", + "Internal Room Id": "অভ্যন্তরীণ কক্ষ আইডি", + "promosmsAllowLongSMS": "দীর্ঘ SMS-এর অনুমতি দিন", + "Uptime Kuma URL": "আপটাইম কুমা ইউআরএল", + "Integration URL": "ইন্টিগ্রেশন ইউআরএল", + "auto resolve": "স্বয়ংক্রিয় সমাধান", + "alertaRecoverState": "অবস্থা পুনরুদ্ধার করুন", + "serwersmsAPIPassword": "এপিআই পাসওয়ার্ড", + "serwersmsPhoneNumber": "ফোন নম্বর", + "smseagleTo": "ফোন নম্বর(গুলি)", + "smseagleGroup": "ফোনবুক গ্রুপের নাম(গুলি)", + "smseagleUrl": "API অ্যাক্সেস টোকেন", + "smseagleApiType": "এপিআই সংস্করণ", + "Octopush API Version": "অক্টোপাস এপিআই সংস্করণ", + "Edit Tag": "ট্যাগ সম্পাদনা করুন", + "Learn More": "আরও জানুন", + "Body Encoding": "বডি এনকোডিং", + "API Keys": "এপিআই কী", + "Expiry date": "মেয়াদ শেষ হওয়ার তারিখ", + "Continue": "চালিয়ে যান", + "Add Another": "আরেকটি যোগ করুন", + "Key Added": "কী যোগ করা হয়েছে", + "Don't expire": "মেয়াদ শেষ করবেন না", + "apiKey-active": "সক্রিয়", + "apiKey-expired": "মেয়াদোত্তীর্ণ", + "apiKey-inactive": "নিষ্ক্রিয়", + "Expires": "মেয়াদ শেষ", + "Add API Key": "API কী যোগ করুন", + "No API Keys": "কোনও API কী নেই", + "ntfyAuthenticationMethod": "প্রমাণীকরণ পদ্ধতি", + "lunaseaUserID": "ব্যবহারকারী আইডি", + "ntfyPriorityHelptextAllEvents": "সকল ইভেন্ট সর্বোচ্চ অগ্রাধিকার দিয়ে পাঠানো হয়।সকল ইভেন্ট সর্বোচ্চ অগ্রাধিকার দিয়ে পাঠানো হয়।সকল ইভেন্ট সর্বোচ্চ অগ্রাধিকার দিয়ে পাঠানো হয়", + "ntfyPriorityDown": "ডাউন-ইভেন্টের জন্য অগ্রাধিকার", + "ntfyPriorityHelptextPriorityHigherThanDown": "নিয়মিত অগ্রাধিকার {0} অগ্রাধিকারের চেয়ে বেশি হওয়া উচিত। অগ্রাধিকার {1}, {0} অগ্রাধিকার {2} এর চেয়ে বেশি", + "twilioApiKey": "এপিআই কী (ঐচ্ছিক)", + "twilioAuthToken": "Auth টোকেন / Api কী সিক্রেট", + "twilioFromNumber": "নম্বর থেকে", + "Show Clickable Link": "ক্লিকযোগ্য লিঙ্ক দেখান", + "Show Clickable Link Description": "যদি চেক করা থাকে, তাহলে এই স্ট্যাটাস পৃষ্ঠায় যাদের অ্যাক্সেস আছে তারা সকলেই মনিটরের URL অ্যাক্সেস করতে পারবেন।", + "Badge Label": "ব্যাজ লেবেল", + "Open Badge Generator": "ব্যাজ জেনারেটর খুলুন", + "Badge Generator": "{0} এর ব্যাজ জেনারেটর", + "Badge Duration (in hours)": "ব্যাজের সময়কাল (ঘণ্টায়)", + "Badge Prefix": "ব্যাজ মান উপসর্গ", + "Badge Suffix": "ব্যাজ মান প্রত্যয়", + "Badge Label Color": "ব্যাজ লেবেলের রঙ", + "Badge Color": "ব্যাজের রঙ", + "Badge Preview": "ব্যাজ প্রিভিউ", + "Badge Label Suffix": "ব্যাজ লেবেল প্রত্যয়", + "Badge Down Color": "ব্যাজ ডাউন কালার", + "Kafka Topic Name": "কাফকা বিষয়ের নাম", + "Enable Kafka SSL": "কাফকা SSL সক্ষম করুন", + "Enable Kafka Producer Auto Topic Creation": "কাফকা প্রযোজক অটো টপিক তৈরি সক্ষম করুন", + "nostrRelaysHelp": "প্রতি লাইনে একটি রিলে URL", + "nostrRelays": "আমাদের রিলে", + "nostrRecipients": "প্রাপকদের পাবলিক কী (npub)", + "Bitrix24 Webhook URL": "Bitrix24 ওয়েবহুক ইউআরএল", + "successAdded": "সফলভাবে যোগ করা হয়েছে।", + "threemaRecipient": "প্রাপক", + "Group Name": "গ্রুপের নাম", + "OAuth2: Client Credentials": "OAuth2: ক্লায়েন্ট শংসাপত্র", + "conditionDeleteGroup": "গ্রুপ মুছুন", + "record": "রেকর্ড", + "Sound": "শব্দ", + "Alphanumerical string and hyphens only": "শুধুমাত্র বর্ণানুক্রমিক স্ট্রিং এবং হাইফেন", + "Pop": "পপ", + "Template Format": "টেমপ্লেট ফর্ম্যাট", + "Request Body": "অনুরোধের মূল অংশ", + "smtpDkimskipFields": "স্বাক্ষর না করার জন্য হেডার কী (ঐচ্ছিক)", + "SpugPush Template Code": "টেমপ্লেট কোড", + "Clear": "পরিষ্কার", + "Elevator": "লিফট", + "pingCountLabel": "সর্বোচ্চ প্যাকেট", + "Could not clear events": "{failed}/{total}টি ইভেন্ট সাফ করা যায়নি", + "gamedigGuessPortDescription": "ভালভ সার্ভার কোয়েরি প্রোটোকল দ্বারা ব্যবহৃত পোর্ট ক্লায়েন্ট পোর্ট থেকে আলাদা হতে পারে। যদি মনিটর আপনার সার্ভারের সাথে সংযোগ করতে না পারে তবে এটি চেষ্টা করুন।", + "LunaSea Device ID": "লুনাসি ডিভাইস আইডি", + "Send rich messages": "সমৃদ্ধ বার্তা পাঠান", + "SMSManager API Docs": "SMSManager API ডক্স ", + "wayToGetBitrix24Webhook": "আপনি {0}-এ দেওয়া ধাপগুলি অনুসরণ করে একটি ওয়েবহুক তৈরি করতে পারেন", + "Gateway Type": "গেটওয়ের ধরণ", + "goAlertInfo": "GoAlert হল অন-কল শিডিউলিং, স্বয়ংক্রিয় এসকেলেশন এবং বিজ্ঞপ্তি (যেমন SMS বা ভয়েস কল) এর জন্য একটি ওপেন সোর্স অ্যাপ্লিকেশন। সঠিক ব্যক্তিকে, সঠিক উপায়ে এবং সঠিক সময়ে স্বয়ংক্রিয়ভাবে যুক্ত করুন! {0}", + "bitrix24SupportUserID": "Bitrix24 এ আপনার ব্যবহারকারী আইডি লিখুন। আপনি ব্যবহারকারীর প্রোফাইলে গিয়ে লিঙ্কটি থেকে আইডিটি খুঁজে পেতে পারেন।", + "TemplateCode": "টেমপ্লেটকোড", + "successDeleted": "সফলভাবে মুছে ফেলা হয়েছে।", + "Sms template must contain parameters: ": "এসএমএস টেমপ্লেটে অবশ্যই নিম্নলিখিত প্যারামিটার থাকতে হবে: ", + "successAuthChangePassword": "পাসওয়ার্ড সফলভাবে আপডেট করা হয়েছে।", + "Don't mention people": "মানুষের কথা উল্লেখ করো না", + "Mention User List": "ব্যবহারকারীর আইডি তালিকা উল্লেখ করুন", + "successEnabled": "সফলভাবে সক্ষম করা হয়েছে।", + "Enter a list of mobile": "মোবাইলের একটি তালিকা লিখুন", + "Remote Browser": "রিমোট ব্রাউজার", + "Device Token": "ডিভাইস টোকেন", + "Add a Remote Browser": "একটি রিমোট ব্রাউজার যোগ করুন", + "Huawei": "হুয়াওয়ে", + "remoteBrowsersDescription": "দূরবর্তী ব্রাউজারগুলি স্থানীয়ভাবে Chromium চালানোর বিকল্প। browserless.io এর মতো একটি পরিষেবা দিয়ে সেট আপ করুন অথবা আপনার নিজস্ব পরিষেবার সাথে সংযোগ করুন", + "Proxy Protocol": "প্রক্সি প্রোটোকল", + "remoteBrowserToggle": "ডিফল্টরূপে Chromium Uptime Kuma কন্টেইনারের ভিতরে চলে। আপনি এই সুইচটি টগল করে একটি রিমোট ব্রাউজার ব্যবহার করতে পারেন।", + "promosmsTypeEco": "SMS ECO - সস্তা কিন্তু ধীর এবং প্রায়শই অতিরিক্ত লোডযুক্ত। শুধুমাত্র পোলিশ গ্রাহকদের জন্য সীমাবদ্ধ।", + "deleteRemoteBrowserMessage": "আপনি কি নিশ্চিত যে সমস্ত মনিটরের জন্য এই রিমোট ব্রাউজারটি মুছে ফেলতে চান?", + "promosmsTypeFlash": "এসএমএস ফ্ল্যাশ - বার্তাটি স্বয়ংক্রিয়ভাবে প্রাপকের ডিভাইসে প্রদর্শিত হবে। শুধুমাত্র পোলিশ প্রাপকদের জন্য সীমাবদ্ধ।", + "mongodbCommandDescription": "ডাটাবেসের বিরুদ্ধে একটি MongoDB কমান্ড চালান। উপলব্ধ কমান্ড সম্পর্কে তথ্যের জন্য {ডকুমেন্টেশন} দেখুন", + "promosmsTypeFull": "এসএমএস ফুল - প্রিমিয়াম স্তরের এসএমএস, আপনি আপনার প্রেরকের নাম ব্যবহার করতে পারেন (প্রথমে আপনার নাম নিবন্ধন করতে হবে)। সতর্কতার জন্য নির্ভরযোগ্য।", + "receiverInfoSevenIO": "যদি রিসিভিং নম্বরটি জার্মানিতে অবস্থিত না হয়, তাহলে আপনাকে নম্বরের সামনে দেশের কোড যোগ করতে হবে (যেমন, মার্কিন যুক্তরাষ্ট্রের দেশের কোড ১ এর জন্য 017612121212 এর পরিবর্তে 1176121212 ব্যবহার করুন)", + "wayToGetWhapiUrlAndToken": "{0} থেকে আপনার পছন্দসই চ্যানেলে গিয়ে আপনি API URL এবং টোকেন পেতে পারেন", + "promosmsTypeSpeed": "এসএমএস স্পিড - সিস্টেমে সর্বোচ্চ অগ্রাধিকার। খুব দ্রুত এবং নির্ভরযোগ্য কিন্তু ব্যয়বহুল (এসএমএস এর সম্পূর্ণ মূল্যের প্রায় দ্বিগুণ)।", + "promosmsPhoneNumber": "ফোন নম্বর (পোলিশ প্রাপকের জন্য আপনি এরিয়া কোড এড়িয়ে যেতে পারেন)", + "whapiRecipient": "ফোন নম্বর / যোগাযোগ আইডি / গ্রুপ আইডি", + "promosmsSMSSender": "এসএমএস প্রেরকের নাম: পূর্ব-নিবন্ধিত নাম অথবা ডিফল্টগুলির মধ্যে একটি: ইনফোএসএমএস, এসএমএস তথ্য, ম্যাক্সএসএমএস, ইনফো, এসএমএস", + "wayToWriteEvolutionRecipient": "আন্তর্জাতিক উপসর্গ সহ ফোন নম্বর, কিন্তু শুরুতে ({0}), যোগাযোগ আইডি ({1}) অথবা গ্রুপ আইডি ({2}) ছাড়া।", + "matrixDesc1": "আপনার ম্যাট্রিক্স ক্লায়েন্টের রুম সেটিংসের অ্যাডভান্সড বিভাগে অনুসন্ধান করে আপনি অভ্যন্তরীণ রুম আইডি খুঁজে পেতে পারেন। এটি দেখতে !QMdRCpUIfLwsfjxye6:home.server এর মতো হওয়া উচিত।", + "wayToGetEvolutionUrlAndToken": "{0} থেকে আপনার পছন্দসই চ্যানেলে গিয়ে আপনি API URL এবং টোকেন পেতে পারেন", + "matrixDesc2": "আপনার নিজস্ব ম্যাট্রিক্স ব্যবহারকারীর অ্যাক্সেস টোকেন ব্যবহার না করে একটি নতুন ব্যবহারকারী তৈরি করার পরামর্শ দেওয়া হচ্ছে কারণ এটি আপনার অ্যাকাউন্ট এবং আপনি যে সমস্ত রুমে যোগদান করেছেন তাতে সম্পূর্ণ অ্যাক্সেস দেবে। পরিবর্তে, একটি নতুন ব্যবহারকারী তৈরি করুন এবং শুধুমাত্র সেই রুমে আমন্ত্রণ জানান যেখানে আপনি বিজ্ঞপ্তি পেতে চান। আপনি {0} চালিয়ে অ্যাক্সেস টোকেন পেতে পারেন", + "wayToGetHeiiOnCallDetails": "ট্রিগার আইডি এবং এপিআই কী কীভাবে পাবেন তা {ডকুমেন্টেশন}-এ ব্যাখ্যা করা হয়েছে", + "Notify Channel": "চ্যানেলটি জানান", + "callMeBotGet": "এখানে আপনি {0}, {1} এবং {2} এর জন্য একটি এন্ডপয়েন্ট তৈরি করতে পারেন। মনে রাখবেন যে আপনি সীমিত হার পেতে পারেন। হারের সীমাগুলি মনে হচ্ছে: {3}", + "aboutWebhooks": "ওয়েবহুক সম্পর্কে আরও তথ্য এখানে: {0}", + "From Phone Number / Transmission Path Originating Address (TPOA)": "ফোন নম্বর / ট্রান্সমিশন পাথ অরিজিনেটিং অ্যাড্রেস (TPOA) থেকে", + "aboutSlackUsername": "বার্তা প্রেরকের প্রদর্শন নাম পরিবর্তন করে। যদি আপনি কাউকে উল্লেখ করতে চান, তাহলে বন্ধুত্বপূর্ণ নামে এটি অন্তর্ভুক্ত করুন।", + "gtxMessagingFromHint": "মোবাইল ফোনে, আপনার প্রাপক বার্তার প্রেরক হিসেবে TPOA দেখতে পাবেন। অনুমোদিত সংখ্যা হল সর্বোচ্চ ১১টি বর্ণসাংখ্যিক অক্ষর, একটি শর্টকোড, স্থানীয় লংকোড অথবা আন্তর্জাতিক নম্বর ({e164}, {e212} অথবা {e214})", + "aboutChannelName": "ওয়েবহুক চ্যানেল বাইপাস করতে চাইলে {0} চ্যানেল নেম ফিল্ডে চ্যানেলের নাম লিখুন। যেমন: #other-channel", + "cellsyntOriginatortypeNumeric": "আন্তর্জাতিক ফর্ম্যাটে টেলিফোন নম্বর সহ সংখ্যাসূচক মান (সর্বোচ্চ ১৫ সংখ্যা) যার আগে 00 নেই (উদাহরণস্বরূপ UK নম্বর 07920 110 000 447920110000 হিসাবে সেট করা উচিত)। প্রাপকরা বার্তার উত্তর দিতে পারেন।", + "cellsyntOriginator": "বার্তার উৎস হিসেবে প্রাপকের মোবাইল ফোনে দৃশ্যমান। অনুমোদিত মান এবং কার্যকারিতা প্যারামিটার উৎসের ধরণ অনুসারে নির্ভর করে।", + "wayToGetPagerDutyKey": "আপনি এটি পরিষেবা -> পরিষেবা ডিরেক্টরি -> (একটি পরিষেবা নির্বাচন করুন) -> ইন্টিগ্রেশন -> ইন্টিগ্রেশন যোগ করুন এ গিয়ে পেতে পারেন। এখানে আপনি \"ইভেন্টস API V2\" অনুসন্ধান করতে পারেন। আরও তথ্য {0}", + "Auto resolve or acknowledged": "স্বয়ংক্রিয় সমাধান বা স্বীকৃত", + "cellsyntDestination": "প্রাপকের টেলিফোন নম্বর আন্তর্জাতিক ফর্ম্যাটে, যার আগে 00 থাকে এবং তারপর দেশের কোড থাকে, যেমন যুক্তরাজ্যের 07920 110 000 নম্বরের জন্য 00447920110000 (মোট সর্বোচ্চ 17 সংখ্যা)। প্রতি HTTP অনুরোধে সর্বাধিক 25000 কমা দ্বারা পৃথক প্রাপক।", + "alertaApiEndpoint": "এপিআই এন্ডপয়েন্ট", + "snmpCommunityStringHelptext": "এই স্ট্রিংটি SNMP-সক্ষম ডিভাইসগুলিতে অ্যাক্সেস প্রমাণীকরণ এবং নিয়ন্ত্রণ করার জন্য একটি পাসওয়ার্ড হিসাবে কাজ করে। এটি আপনার SNMP ডিভাইসের কনফিগারেশনের সাথে মিলিয়ে নিন।", + "alertaAlertState": "সতর্কতার অবস্থা", + "wayToGetThreemaGateway": "আপনি থ্রিমা গেটওয়ে {0}-এর জন্য নিবন্ধন করতে পারেন।", + "serwersmsAPIUser": "API ব্যবহারকারীর নাম (webapi_ উপসর্গ সহ)", + "threemaSenderIdentityFormat": "৮টি অক্ষর, সাধারণত * দিয়ে শুরু হয়", + "serwersmsSenderName": "এসএমএস প্রেরকের নাম (গ্রাহক পোর্টালের মাধ্যমে নিবন্ধিত)", + "threemaBasicModeInfo": "দ্রষ্টব্য: এই ইন্টিগ্রেশনটি বেসিক মোডে (সার্ভার-ভিত্তিক এনক্রিপশন) থ্রিমা গেটওয়ে ব্যবহার করে। আরও বিশদ বিবরণ {0}-এ পাওয়া যাবে।", + "smseagleGroupV2": "ফোনবুক গ্রুপ আইডি(গুলি)", + "Recipient Type": "প্রাপকের ধরণ", + "smseagleRecipient": "প্রাপক (একাধিক কমা দিয়ে আলাদা করতে হবে)", + "privateOnesenderDesc": "নিশ্চিত করুন যে নম্বরের ফোন নম্বরটি বৈধ। ব্যক্তিগত নম্বরের ফোন নম্বরে বার্তা পাঠাতে, যেমন: 628123456789", + "smseagleToken": "API অ্যাক্সেস টোকেন", + "smseagleTtsModel": "টেক্সট-টু-স্পিচ মডেল আইডি", + "smspartnerPhoneNumberHelptext": "সংখ্যাটি আন্তর্জাতিক ফর্ম্যাটে {0}, {1} হতে হবে। একাধিক সংখ্যাকে {2} দ্বারা পৃথক করতে হবে", + "wayToGetOnesenderUrlandToken": "আপনি Onesender ওয়েবসাইটে গিয়ে URL এবং টোকেন পেতে পারেন। আরও তথ্য {0}", + "smspartnerSenderNameInfo": "৩..=১১ টি নিয়মিত অক্ষরের মধ্যে হতে হবে", + "Add Remote Browser": "রিমোট ব্রাউজার যোগ করুন", + "Leave blank to use a shared sender number.": "একটি ভাগ করা প্রেরক নম্বর ব্যবহার করার জন্য ফাঁকা ছেড়ে দিন।", + "onebotSafetyTips": "নিরাপত্তার জন্য, অ্যাক্সেস টোকেন সেট করতে হবে", + "Optional: Space separated list of scopes": "ঐচ্ছিক: স্থান পৃথক করে স্কোপ তালিকা", + "pushDeerServerDescription": "অফিসিয়াল সার্ভার ব্যবহার করার জন্য ফাঁকা রাখুন", + "No tags found.": "কোন ট্যাগ পাওয়া যায়নি।", + "Google Analytics ID": "গুগল অ্যানালিটিক্স আইডি", + "Cannot connect to the socket server.": "সকেট সার্ভারের সাথে সংযোগ স্থাপন করা যাচ্ছে না।", + "Server Address": "সার্ভার ঠিকানা", + "ends with": "দিয়ে শেষ হয়", + "apiKeyAddedMsg": "আপনার API কী যোগ করা হয়েছে। দয়া করে এটি মনে রাখবেন কারণ এটি আর দেখানো হবে না।", + "less than or equal to": "এর চেয়ে কম বা সমান", + "deleteAPIKeyMsg": "আপনি কি নিশ্চিত যে আপনি এই API কীটি মুছে ফেলতে চান?", + "greater than or equal to": "এর চেয়ে বড় বা সমান", + "pagertreeIntegrationUrl": "ইন্টিগ্রেশন ইউআরএল", + "pagertreeMedium": "মাঝারি", + "Notification Channel": "বিজ্ঞপ্তি চ্যানেল", + "wayToGetPagerTreeIntegrationURL": "PagerTree-তে Uptime Kuma ইন্টিগ্রেশন তৈরি করার পর, Endpoint কপি করুন। সম্পূর্ণ বিবরণ দেখুন {0}", + "Scifi": "সাইফাই", + "ntfyPriorityHelptextAllExceptDown": "সমস্ত ইভেন্ট এই অগ্রাধিকার সহ পাঠানো হয়, {0}-ইভেন্ট ছাড়া, যার অগ্রাধিকার {1}", + "Guitar": "গিটার", + "Badge value (For Testing only.)": "ব্যাজ মান (শুধুমাত্র পরীক্ষার জন্য।)", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "ডিভাইসটি বিরক্ত করবেন না মোডে থাকলেও, সময় সংবেদনশীল বিজ্ঞপ্তিগুলি অবিলম্বে বিতরণ করা হবে।", + "monitorToastMessagesDescription": "মনিটরের জন্য টোস্ট বিজ্ঞপ্তিগুলি নির্দিষ্ট সময়ের পরে কয়েক সেকেন্ডে অদৃশ্য হয়ে যায়। -1 এ সেট করলে টাইমআউট বন্ধ হয়ে যায়। 0 এ সেট করলে টোস্ট বিজ্ঞপ্তিগুলি বন্ধ হয়ে যায়।", + "RabbitMQ Password": "RabbitMQ পাসওয়ার্ড", + "Press Enter to add broker": "ব্রোকার যোগ করতে এন্টার টিপুন", + "rabbitmqHelpText": "মনিটরটি ব্যবহার করার জন্য, আপনার RabbitMQ সেটআপে ম্যানেজমেন্ট প্লাগইন সক্ষম করতে হবে। আরও তথ্যের জন্য, অনুগ্রহ করে {rabitmq_documentation} দেখুন।", + "Kafka Producer Message": "কাফকা প্রযোজকের বার্তা", + "SendGrid API Key": "সেন্ডগ্রিড এপিআই কী", + "Pick a SASL Mechanism...": "একটি SASL মেকানিজম বেছে নিন…", + "wayToGetWahaSession": "এই সেশন থেকে WAHA চ্যাট আইডিতে বিজ্ঞপ্তি পাঠায়। আপনি এটি WAHA ড্যাশবোর্ডে খুঁজে পেতে পারেন।", + "noGroupMonitorMsg": "উপলব্ধ নেই। প্রথমে একটি গ্রুপ মনিটর তৈরি করুন।", + "webhookPostMethodDesc": "POST বেশিরভাগ আধুনিক HTTP সার্ভারের জন্য ভালো।", + "webhookGetMethodDesc": "GET কোয়েরি প্যারামিটার হিসেবে ডেটা পাঠায় এবং বডি কনফিগার করার অনুমতি দেয় না। আপটাইম কুমা পুশ মনিটর ট্রিগার করার জন্য কার্যকর।", + "wayToGetFlashDutyKey": "আপটাইম কুমাকে ফ্ল্যাশডিউটির সাথে ইন্টিগ্রেট করতে: চ্যানেল > একটি চ্যানেল নির্বাচন করুন > ইন্টিগ্রেশন > একটি নতুন ইন্টিগ্রেশন যোগ করুন এ যান, আপটাইম কুমা নির্বাচন করুন এবং পুশ ইউআরএলটি অনুলিপি করুন।", + "wayToWriteWahaChatId": "আন্তর্জাতিক উপসর্গ সহ ফোন নম্বর, কিন্তু শুরুতে প্লাস চিহ্ন ({0}), যোগাযোগ আইডি ({1}) অথবা গ্রুপ আইডি ({2}) ছাড়াই। WAHA সেশন থেকে এই চ্যাট আইডিতে বিজ্ঞপ্তি পাঠানো হয়।", + "nostrSender": "প্রেরকের ব্যক্তিগত কী (nsec)", + "Expiry": "মেয়াদ শেষ", + "nostrRecipientsHelp": "npub ফর্ম্যাট, প্রতি লাইনে একটি", + "noOrBadCertificate": "না/খারাপ সার্টিফিকেট", + "YZJ Webhook URL": "YZJ ওয়েবহুক ইউআরএল", + "Authorization Identity": "অনুমোদনের পরিচয়", + "Sender name": "প্রেরকের নাম", + "Apprise URL": "ইউআরএল জানান", + "Example:": "উদাহরণ: {0}", + "Read more:": "আরও পড়ুন: {0}", + "Status:": "স্থিতি: {0}", + "Proto Method": "প্রোটো পদ্ধতি", + "Proto Content": "প্রোটো কন্টেন্ট", + "Economy": "অর্থনীতি", + "Lowcost": "কম খরচে", + "high": "উচ্চ", + "Proto Service Name": "প্রোটো পরিষেবার নাম", + "SendKey": "সেন্ডকি", + "Channel Name": "চ্যানেলের নাম", + "setup a new monitor group": "একটি নতুন মনিটর গ্রুপ সেট আপ করুন", + "smseagleMsgTts": "টেক্সট-টু-স্পিচ কল", + "smseagleMsgTtsAdvanced": "টেক্সট-টু-স্পিচ উন্নত কল", + "smseagleDuration": "সময়কাল (সেকেন্ডে)", + "smseagleApiv1": "APIv1 (বিদ্যমান প্রকল্প এবং ব্যাকওয়ার্ড সামঞ্জস্যের জন্য)", + "smseagleApiv2": "APIv2 (নতুন ইন্টিগ্রেশনের জন্য প্রস্তাবিত)", + "smseagleDocs": "ডকুমেন্টেশন অথবা APIv2 এর প্রাপ্যতা পরীক্ষা করুন: {0}", + "smseagleComma": "একাধিককে কমা দিয়ে আলাদা করতে হবে", + "smspartnerApiurl": "আপনি আপনার ড্যাশবোর্ডে {0} ঠিকানায় আপনার API কী খুঁজে পেতে পারেন", + "smspartnerPhoneNumber": "ফোন নম্বর(গুলি)", + "smseaglePriority": "বার্তার অগ্রাধিকার (০-৯, সর্বোচ্চ অগ্রাধিকার = ৯)", + "smseagleMsgType": "বার্তার ধরণ", + "smseagleMsgSms": "এসএমএস বার্তা (ডিফল্ট)", + "smseagleMsgRing": "রিং কল", + "threemaRecipientType": "প্রাপকের ধরণ", + "brevoToEmail": "ইমেল করতে", + "brevoCcEmail": "সিসি ইমেল", + "brevoBccEmail": "বিসিসি ইমেল", + "brevoSeparateMultipleEmails": "একাধিক ইমেল ঠিকানা কমা দিয়ে আলাদা করুন", + "brevoSubject": "বিষয়", + "pingNumericLabel": "সংখ্যাসূচক আউটপুট", + "pingNumericDescription": "যদি চেক করা থাকে, তাহলে প্রতীকী হোস্টনামের পরিবর্তে আইপি ঠিকানাগুলি আউটপুট হবে", + "pingGlobalTimeoutLabel": "গ্লোবাল টাইমআউট", + "brevoLeaveBlankForDefaultSubject": "ডিফল্ট বিষয়ের জন্য ফাঁকা রাখুন", + "pingCountDescription": "থামার আগে পাঠানোর জন্য প্যাকেটের সংখ্যা", + "wahaSession": "অধিবেশন", + "wahaChatId": "চ্যাট আইডি (ফোন নম্বর / যোগাযোগ আইডি / গ্রুপ আইডি)", + "wayToGetWahaApiUrl": "আপনার WAHA ইনস্ট্যান্স URL।", + "pingGlobalTimeoutDescription": "পিং থামার আগে সেকেন্ডে মোট সময়, পাঠানো প্যাকেট নির্বিশেষে", + "pingPerRequestTimeoutLabel": "প্রতি-পিং টাইমআউট", + "pingPerRequestTimeoutDescription": "একটি একক পিং প্যাকেট হারিয়ে যাওয়ার আগে এটি সর্বোচ্চ অপেক্ষার সময় (সেকেন্ডে)", + "smtpHelpText": "'SMTPS' পরীক্ষা করে যে SMTP/TLS কাজ করছে; 'TLS উপেক্ষা করুন' প্লেইনটেক্সটের মাধ্যমে সংযোগ করে; 'STARTTLS' সংযোগ করে, একটি STARTTLS কমান্ড জারি করে এবং সার্ভার সার্টিফিকেট যাচাই করে। এর কোনটিই ইমেল পাঠায় না।", + "wayToGetWahaApiKey": "API কী হল WHATSAPP_API_KEY এনভায়রনমেন্ট ভেরিয়েবল মান যা আপনি WAHA চালানোর জন্য ব্যবহার করেছিলেন।", + "Number of retry attempts if webhook fails": "ওয়েবহুক ব্যর্থ হলে পুনরায় চেষ্টা করার সংখ্যা (প্রতি ৬০-১৮০ সেকেন্ডে)।", + "Manual": "ম্যানুয়াল", + "Add Another Tag": "আরেকটি ট্যাগ যোগ করুন", + "Staged Tags for Batch Add": "ব্যাচ অ্যাডের জন্য স্টেজড ট্যাগ", + "Clear Form": "ফর্ম পরিষ্কার করুন", + "Send UP silently": "চুপিচুপি পাঠান", + "Maximum Retries": "সর্বোচ্চ পুনঃপ্রচেষ্টা", + "Bark Group": "বার্ক গ্রুপ", + "Badge Warn Color": "ব্যাজ সতর্কতার রঙ", + "pause": "বিরতি", + "Conversation token": "কথোপকথনের টোকেন", + "Bot secret": "বট গোপন", + "Nextcloud host": "নেক্সটক্লাউড হোস্ট", + "Send DOWN silently": "নীরবে নিচে পাঠান", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "নেক্সটক্লাউড টক বট ইনস্টল করার জন্য সার্ভারে প্রশাসনিক অ্যাক্সেস প্রয়োজন।" +} diff --git a/src/lang/ca.json b/src/lang/ca.json new file mode 100644 index 0000000..95d4a32 --- /dev/null +++ b/src/lang/ca.json @@ -0,0 +1,1680 @@ +{ + "Settings": "Paràmetres", + "Dashboard": "Tauler", + "Help": "Ajuda", + "New Update": "Nova actualització", + "Language": "Idioma", + "Appearance": "Aparença", + "Theme": "Tema", + "General": "General", + "Game": "Joc", + "Version": "Versió", + "Check Update On GitHub": "Comprovar actualitzacions a GitHub", + "List": "Llista", + "Home": "Inici", + "Add": "Afegir", + "Add New Monitor": "Afegir nou monitor", + "Quick Stats": "Estadístiques ràpides", + "Up": "Funcional", + "Down": "Caigut", + "Pending": "Pendent", + "Maintenance": "Manteniment", + "Unknown": "Desconegut", + "Cannot connect to the socket server": "No es pot connectar al servidor socket", + "Reconnecting...": "S'està tornant a connectar...", + "languageName": "Català", + "Primary Base URL": "URL Base Primària", + "statusMaintenance": "Manteniment", + "setupDatabaseChooseDatabase": "Quina base de dades vols utilitzar?", + "setupDatabaseEmbeddedMariaDB": "No has de configurar res. Aquesta imatge de Docker ha configurat MariaDB automàticament. Uptime Kuma es conectarà a aquesta base de dades pel socket d'Unix.", + "setupDatabaseMariaDB": "Conectar a una base de MariaDB externa. Has de ficar les dades de la conexió de la base de dades.", + "setupDatabaseSQLite": "Un simple fitxer de base de dades, es recomana per desplegaments en petita escala. Abans de la v2.0.0, Uptime Kuma utilitzaba SQLite com la base de dades predeterminada.", + "sameAsServerTimezone": "La mateixa zona horaria que el servidor", + "settingUpDatabaseMSG": "Configuració de la base de dades. Pot trigar una estona, si us plau, tingueu paciència.", + "dbName": "Nom de la base de dades", + "Passive Monitor Type": "Tipus passiu de monitor", + "pauseDashboardHome": "Pausa", + "Pause": "Pausa", + "Name": "Nom", + "Status": "Estat", + "DateTime": "DataHora", + "Message": "Missatge", + "General Monitor Type": "Tipus general de monitor", + "Specific Monitor Type": "Tipus específic de monitor", + "Heartbeat Interval": "Interval de \"heartbeat\"", + "Request Timeout": "'Timeout' per petició", + "No important events": "Esdeveniments sense importància", + "Resume": "Resum", + "Edit": "Editar", + "Delete": "Eliminar", + "Current": "Actual", + "Uptime": "Temps actiu", + "Cert Exp.": "Caducitat del certificat.", + "Monitor": "Monitor | Monitors", + "day": "dia | dies", + "-day": "-dia", + "hour": "hora", + "-hour": "-hora", + "Response": "Resposta", + "Ping": "Ping", + "Monitor Type": "Tipus de monitor", + "Keyword": "Paraula clau", + "Invert Keyword": "Invertir paraula clau", + "Expected Value": "Valor esperat", + "Json Query": "Cerca Json", + "URL": "URL", + "Hostname": "Nom del servidor", + "Port": "Port", + "timeoutAfter": "¡Timeout' després de {0} segons", + "locally configured mail transfer agent": "Agent de transferència de correu configurat localment", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Introduïu el nom del servidor al qual voleu connectar-vos o {localhost} si voleu utilitzar un {local_mta}", + "Host URL": "URL de l'amfitrió", + "Friendly Name": "Nom senzill", + "markdownSupported": "Sintaxi de Markdown suportada. Si fas servir HTML, evita espais blancs a l'inici per prevenir problemes de format.", + "Retries": "Reintents", + "Advanced": "Avançat", + "ignoreTLSErrorGeneral": "Ignora errors TLS/SSL per connexió", + "maxRedirectDescription": "Nombre màxim de redireccions a seguir. Establiu a 0 per a desactivar les redireccions.", + "Upside Down Mode": "Mode al revés", + "Max. Redirects": "Redireccions Màx", + "Accepted Status Codes": "Codis d'Estat Acceptats", + "needPushEvery": "Hauries de cridar a aquesta URL cada {0} segons.", + "Heartbeat Retry Interval": "Reintent de l'interval de \"heartbeat\"", + "Resend Notification if Down X times consecutively": "Reenvia notificacions si Down X vegades consecutives", + "resendEveryXTimes": "Reenvia cada {0} vegades", + "retryCheckEverySecond": "Reintenta cada {0} segons", + "checkEverySecond": "Comprova cada {0} segons", + "resendDisabled": "Reenvia deshabilitat", + "retriesDescription": "Màxim d'intents abans de que el servei sigui marcat com a caigut i la notificació sigui enviada", + "ignoreTLSError": "Ignora errors de TLS/SSL per a pàgines web HTTPS", + "upsideDownModeDescription": "Canvia l'estat al revés. Si el servei és accessible, està CAIGUT.", + "Setup Notification": "Configurar Notificació", + "Allow indexing": "Permetre indexat", + "Discourage search engines from indexing site": "Desencoratjar als motors de cerca que indexin la pàgina", + "Current Password": "Contrasenya actual", + "Please use this option carefully!": "Per favor, empra aquesta opció amb cura !", + "disable authentication": "deshabilita autenticació", + "Partially Degraded Service": "Servei Parcialment Degradat", + "Degraded Service": "Servei Degradat", + "Add Group": "Afegir Grup", + "Add a monitor": "Afegir monitor", + "pushViewCode": "Com emprar el monitor de Push? (Veure Codi)", + "Notifications": "Notificacions", + "pushOthers": "Altres", + "programmingLanguages": "Llenguatges de programació", + "Dark": "Fosc", + "Remember me": "Recordar-me", + "Login": "Iniciar sessió", + "No Monitors, please": "Sense Monitors, per favor", + "notAvailableShort": "N/A", + "Two Factor Authentication": "Segon Factor d'Autenticació", + "Custom": "Personalitzat", + "Search...": "Cercar…", + "Search monitored sites": "Cercar llocs monitoritzats", + "Avg. Response": "Resp. Promig", + "Add New below or Select...": "Afegir nova o Seleccionar…", + "Tag with this name already exist.": "Aquesta etiqueta ja existeix.", + "color": "Color", + "Gray": "Gris", + "value (optional)": "valor (opcional)", + "Active": "Actiu", + "Push URL": "URL push", + "pushOptionalParams": "Paràmetres opcionals: {0}", + "Save": "Desa", + "Not available, please setup.": "No disponible, per favor configura-ho.", + "Light": "Clar", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Tema - Heartbet Bar", + "styleElapsedTime": "Temps transcorregut a la barra", + "styleElapsedTimeShowNoLine": "Mostrar (Fora Línia)", + "styleElapsedTimeShowWithLine": "Mostrar (Amb línia)", + "Normal": "Normal", + "Bottom": "Inferior", + "None": "Cap", + "Timezone": "Zona Horària", + "Search Engine Visibility": "Visibilitat motor de cerca", + "Change Password": "Canviar contrasenya", + "New Password": "Nova Contrasenya", + "Repeat New Password": "Repeteix Nova Contrasenya", + "Update Password": "Actualitzar Contrasenya", + "Disable Auth": "Deshabilita autenticació", + "Enable Auth": "Habilita Autenticació", + "disableauth.message1": "Estau segur que voleu {disableAuth}?", + "disableauth.message2": "Està dissenyat per a escenaris {intendThirdPartyAuth} davant Uptime Kuma, com ara Cloudflare Access, Authelia o altres mecanismes d'autenticació.", + "where you intend to implement third-party authentication": "on es vol implementar l'autenticació de tercers", + "Logout": "Tancar sessió", + "Leave": "Marxar", + "I understand, please disable": "Ho entenc, per favor deshabilita-ho", + "Confirm": "Confirma", + "Yes": "Si", + "No": "No", + "Username": "Nom d'usuari", + "Password": "Contrasenya", + "add one": "afegir un", + "Notification Type": "Tipus de notificació", + "Test": "Test", + "Certificate Info": "Informació del certificat", + "Resolver Server": "Servidor DNS", + "Resource Record Type": "Tipus de registre", + "Create your admin account": "Crear compte d'administració", + "Repeat Password": "Repeteix Contrasenya", + "Export Backup": "Exportar Còpia", + "Import Backup": "Importar Còpia", + "Export": "Exporta", + "Import": "Importa", + "respTime": "Temps Resp. (ms)", + "Default enabled": "Habilitat per defecte", + "Apply on all existing monitors": "Aplicar a tots els monitors existents", + "Create": "Crear", + "Clear Data": "Esborra dades", + "Events": "Events", + "Heartbeats": "Heartbeat", + "Auto Get": "Obtenir automàticament", + "Schedule maintenance": "Programar manteniment", + "Affected Monitors": "Monitors Afectats", + "Pick Affected Monitors...": "Seleccionar Monitors Afectats…", + "Start of maintenance": "Inici del manteniment", + "All Status Pages": "Totes les pàgines d'estat", + "Select status pages...": "Selecciona pàgines d'estat…", + "alertNoFile": "Per favor, selecciona fitxer a importar.", + "alertWrongFileType": "Selecciona un fitxer JSON.", + "Clear all statistics": "Esborra totes les Estadístiques", + "Skip existing": "Ometre existent", + "Overwrite": "Sobreescriu", + "Options": "Opcions", + "Keep both": "Manté ambdos", + "Verify Token": "Verificar token", + "Setup 2FA": "Configurar 2FA", + "Enable 2FA": "Habilitar 2FA", + "Disable 2FA": "Deshabilitar 2FA", + "2FA Settings": "Ajustaments 2FA", + "filterActive": "Actiu", + "filterActivePaused": "Pausat", + "Inactive": "Inactiu", + "Token": "Token", + "Show URI": "Mostrar URI", + "Tags": "Etiquetes", + "Red": "Vermell", + "Orange": "Taronja", + "Green": "Verd", + "Blue": "Blau", + "Indigo": "Morat", + "Purple": "Porpra", + "Pink": "Rosa", + "Avg. Ping": "Ping promig", + "Entry Page": "Pàgina d'entrada", + "statusPageNothing": "Res per aquí, per favor afegeix un grup o un monitor.", + "statusPageRefreshIn": "Refrescat en: {0]", + "No Services": "Sense Servei", + "All Systems Operational": "Tots els sistemes operatius", + "Edit Status Page": "Editar pàgina Estat", + "Go to Dashboard": "Anar al Panell", + "Status Page": "Pàgina d'Estat", + "Email": "Correu", + "Last Result": "Darrer Resultat", + "Add New Tag": "Afegir nova etiqueta", + "Tag with this value already exist.": "Ja existeix una etiqueta amb aquest valor.", + "defaultNotificationName": "La meva {notification} Alerta ({number})", + "Required": "Obligatori", + "Post URL": "Posar URL", + "Content Type": "Content Type", + "Json Query Expression": "Expressió de consulta Json", + "now": "ara", + "-year": "-any", + "Status Pages": "Pàgines d'estat", + "here": "aquí", + "time ago": "fa {0}", + "ignoredTLSError": "Errors TLS/SSL ignorats", + "webhookFormDataDesc": "{multipart} es bo per PHP. El JSON haurà d'analitzar-se amb {decodeFunction}", + "webhookJsonDesc": "{0} es bo per qualsevol servidor HTTP modern com Express.js", + "templateMsg": "missatge de la notificació", + "webhookAdditionalHeadersTitle": "Capçaleres addicionals", + "Application Token": "Testimoni d'aplicació", + "Server URL": "URL del servidor", + "Priority": "Prioritat", + "Webhook URL": "URL del Webhook", + "emojiCheatSheet": "Full de trampa d'emoji: {0}", + "Read more": "Llegeix més", + "appriseInstalled": "S'ha instal·lat l'apèndix.", + "templateHeartbeatJSON": "objecte que descriu el batec del cor", + "templateMonitorJSON": "objecte que descriu el monitor", + "templateLimitedToUpDownCertNotifications": "només disponible per a notificacions de venciment UP/DOWN/Certificate", + "templateLimitedToUpDownNotifications": "només disponible per a notificacions UP/DOWN", + "webhookAdditionalHeadersDesc": "Estableix les capçaleres addicionals enviades amb el webhook. Cada capçalera s'ha de definir com una clau/valor JSON.", + "webhookBodyPresetOption": "Predefinit - {0}", + "webhookBodyCustomOption": "Cos personalitzat", + "Headers": "Capçaleres", + "Monitor History": "Historial del monitor", + "PasswordsDoNotMatch": "Les contrasenyes no coincideixen.", + "records": "registres", + "One record": "Un registre", + "Current User": "Usuari actual", + "topic": "Tema", + "topicExplanation": "tema MQTT a monitorar", + "successKeyword": "Paraula clau d'èxit", + "successKeywordExplanation": "Paraula clau MQTT que es considerarà un èxit", + "recent": "Recent", + "Reset Token": "Restableix el testimoni", + "Done": "Fet", + "Info": "Info", + "Steam API Key": "Clau API de Steam", + "Shrink Database": "Redueix la base de dades", + "Pick a RR-Type...": "Trieu un tipus RR…", + "Default": "Per defecte", + "HTTP Options": "Opcions HTTP", + "Create Incident": "Crear Incident", + "Title": "Títol", + "Content": "Contingut", + "Style": "Estil", + "info": "info", + "warning": "avís", + "danger": "perill", + "primary": "primària", + "light": "lleuger", + "dark": "fosc", + "Post": "Post", + "Created": "Creat", + "Last Updated": "Darrera actualització", + "Switch to Light Theme": "Canvia a tema clar", + "Switch to Dark Theme": "Canviar a tema fosc", + "Show Tags": "Mostra les etiquetes", + "Hide Tags": "Amaga les etiquetes", + "Description": "Descripció", + "No monitors available.": "No hi ha monitors disponibles.", + "Add one": "Afegeix-ne un", + "No Monitors": "Sense monitors", + "Untitled Group": "Grup sense títol", + "Services": "Serveis", + "Discard": "Descarta", + "Cancel": "Canel·la", + "Select": "Selecciona", + "Check/Uncheck": "Comprova/desmarca", + "Powered by": "Funciona amb", + "Customize": "Personalitza", + "Custom Footer": "Peu de pàgina personalitzat", + "Custom CSS": "CSS personalitzat", + "default": "Per defecte", + "enabled": "Habilitat", + "setAsDefault": "Estableix com a predeterminat", + "deleteProxyMsg": "Esteu segur que voleu suprimir aquest servidor intermediari per a tots els monitors?", + "setAsDefaultProxyDescription": "Aquest servidor intermediari s'habilitarà de manera predeterminada per als monitors nous. Encara podeu desactivar el servidor intermediari per separat per a cada monitor.", + "Certificate Chain": "Cadena de certificats", + "Valid": "Vàlid", + "Invalid": "Invàlid", + "User": "Usuari", + "Installed": "Instal·lat", + "Not installed": "No instal·lat", + "Running": "En execució", + "Not running": "No en execució", + "Remove Token": "Elimina el testimoni", + "Start": "Inicia", + "Stop": "Aturar", + "Add New Status Page": "Afegeix una pàgina d'estat nova", + "Slug": "Àlies", + "Accept characters:": "Accepta caràcters:", + "startOrEndWithOnly": "Inicia o acaba només amb {0}", + "No consecutive dashes": "Sense guions consecutius", + "Next": "Següent", + "The slug is already taken. Please choose another slug.": "L'àlias ja està agafat. Si us plau, escolliu un altre àlies.", + "No Proxy": "Sense servidor intermediari", + "Proxies": "Servidors intermediaris", + "HTTP Basic Auth": "Autenticació bàsica HTTP", + "New Status Page": "Pàgina d'estat nova", + "Reverse Proxy": "Servidor intermediari invertit", + "Backup": "Còpia de seguretat", + "About": "Quant a", + "wayToGetCloudflaredURL": "(Descarrega de cloudfared des de {0})", + "cloudflareWebsite": "Lloc web de Cloudflare", + "Message:": "Missatge:", + "HTTP Headers": "Capçaleres HTTP", + "Trust Proxy": "Confia en el servidor intermediari", + "Other Software": "Un altre programari", + "For example: nginx, Apache and Traefik.": "Per exemple: nginx, Apache i Traefik.", + "Please read": "Llegiu", + "Subject:": "Assumpte:", + "Valid To:": "Vàlid per a:", + "Days Remaining:": "Dies restants:", + "Issuer:": "Emissor:", + "Fingerprint:": "Empremta digital:", + "No status pages": "Sense pàgines d'estat", + "Domain Name Expiry Notification": "Notificació de venciment del nom de domini", + "Add a new expiry notification day": "Notificació de venciment del nom de domini", + "Remove the expiry notification": "Elimina el dia de notificació de venciment", + "Proxy": "Servidor intermediari", + "Date Created": "Data de creació", + "Footer Text": "Text del peu de pàgina", + "Refresh Interval Description": "La pàgina d'estat farà una actualització completa del lloc cada {0} segons", + "Show Powered By": "Mostra Impulsat per", + "Domain Names": "Noms de domini", + "signedInDisp": "Sessió iniciada com a {0}", + "RadiusSecret": "Secret de Radius", + "RadiusSecretDescription": "Secret compartit entre client i servidor", + "RadiusCalledStationId": "Id de l'estació cridada", + "RadiusCallingStationId": "Id de l'estació de trucada", + "RadiusCallingStationIdDescription": "Identificador del dispositiu de crida", + "Certificate Expiry Notification": "Notificació de venciment del certificat", + "API Username": "Nom d'usuari API", + "API Key": "Clau API", + "Show update if available": "Mostra l'actualització si està disponible", + "Using a Reverse Proxy?": "Usar un servidor intermediari invers?", + "Check how to config it for WebSocket": "Comprova com configurar-lo per a WebSocket", + "Steam Game Server": "Servidor de jocs Steam", + "Most likely causes:": "Causes més probables:", + "There might be a typing error in the address.": "Pot haver-hi un error d'escriptura a l'adreça.", + "What you can try:": "Què podeu provar:", + "Retype the address.": "Torneu a teclejar l'adreça.", + "Go back to the previous page.": "Torna a la pàgina anterior.", + "Coming Soon": "Properament", + "Connection String": "Cadena de connexió", + "Query": "Consulta", + "settingsCertificateExpiry": "Caducitat del certificat TLS", + "Setup Docker Host": "Configura l'amfitrió Docker", + "Connection Type": "Tipus de connexió", + "Docker Daemon": "Dimoni Docker", + "noDockerHostMsg": "No disponible. Primer configureu un amfitrió Docker.", + "DockerHostRequired": "Establiu l'amfitrió Docker per a aquest monitor.", + "socket": "Sòcol", + "tcp": "TCP / HTTP", + "Docker Container": "Contenidor Docker", + "Container Name / ID": "Nom del contenidor / ID", + "Docker Host": "Amfitrió Docker", + "Docker Hosts": "Amfitrions Docker", + "Domain": "Domini", + "Workstation": "Estació de treball", + "Packet Size": "Mida del paquet", + "Bot Token": "Testimoni de bot", + "wayToGetTelegramToken": "Podeu obtenir un testimoni de {0}.", + "Chat ID": "ID de xat", + "telegramMessageThreadID": "(Opcional) ID del fil del missatge", + "telegramMessageThreadIDDescription": "Identificador únic opcional per al fil del missatge de destinació (tema) del fòrum; només per als supergrups del fòrum", + "telegramSendSilently": "Envia silenciosament", + "telegramProtectContent": "Protegeix la reenviament/desament", + "supportTelegramChatID": "Admet xat directe / grup / ID de xat del canal", + "YOUR BOT TOKEN HERE": "EL VOSTRE TESTIMONI DE BOT AQUÍ", + "chatIDNotFound": "No s'ha trobat l'ID del xat; primer envieu un missatge a aquest bot", + "disableCloudflaredNoAuthMsg": "Esteu en mode No Auth, no cal una contrasenya.", + "wayToGetLineNotifyToken": "Podeu obtenir un testimoni d'accés des de {0}", + "Examples": "Exemples", + "Home Assistant URL": "URL de l'assistent d'inici", + "Long-Lived Access Token": "Testimoni d'accés viu", + "Notification Service": "Servei de notificacions", + "Automations can optionally be triggered in Home Assistant:": "Les automatitzacions es poden activar opcionalment a l'assistent d'inici:", + "Trigger type:": "Tipus d'activador:", + "Event type:": "Tipus d'esdeveniment:", + "Event data:": "Dades de l'esdeveniment:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "A continuació, trieu una acció, per exemple, canvieu l'escena a on una llum RGB és vermella.", + "Frontend Version": "Versió del frontal", + "Frontend Version do not match backend version!": "La versió frontal no coincideix amb la versió del dorsal!", + "backupRecommend": "Si us plau, feu una còpia de seguretat del volum o de la carpeta de dades (./data/) directament.", + "Optional": "Opcional", + "and": "i", + "startDateTime": "Data/hora d'inici", + "endDateTime": "Data/hora final", + "cronExpression": "Expressió Cron", + "cronSchedule": "Planificació: ", + "invalidCronExpression": "Expressió Cron no vàlida: {0}", + "recurringInterval": "Interval", + "Recurring": "Recurrència", + "strategyManual": "Activa/Inactiva manualment", + "warningTimezone": "Està utilitzant la zona horària del servidor", + "weekdayShortMon": "Dill", + "weekdayShortTue": "Dim", + "weekdayShortWed": "Dim", + "weekdayShortThu": "Dij", + "weekdayShortFri": "Div", + "weekdayShortSat": "Diss", + "weekdayShortSun": "Dg", + "dayOfWeek": "Dia de la setmana", + "dayOfMonth": "Dia del mes", + "lastDay": "Últim dia", + "lastDay2": "2n últim dia del mes", + "lastDay3": "3r Darrer Dia del Mes", + "lastDay4": "4t Darrer Dia del Mes", + "No Maintenance": "Sense manteniment", + "maintenanceStatus-under-maintenance": "Sota manteniment", + "maintenanceStatus-inactive": "Inactiu", + "maintenanceStatus-scheduled": "Programat", + "maintenanceStatus-unknown": "Desconegut", + "Server Timezone": "Zona horària del servidor", + "statusPageMaintenanceEndDate": "Final", + "IconUrl": "URL de la icona", + "Enable DNS Cache": "(Obsolet) Habilita la memòria cau DNS per als monitors HTTP(s)", + "Enable": "Habilita", + "Disable": "Desactiva", + "chromeExecutable": "Executable Chrome/Chromium", + "chromeExecutableAutoDetect": "Detecció automàtica", + "dnsCacheDescription": "Pot ser que no funcioni treballant amb entorns IPv6, desactiva'l si detectes qualsevol problema.", + "Single Maintenance Window": "Finestra de Manteniment únic", + "Maintenance Time Window of a Day": "Finestra de Temps del manteniment d'un Dia", + "Effective Date Range": "Rang de data eficaç (Opcional)", + "Schedule Maintenance": "Programa de manteniment", + "Edit Maintenance": "Edita el manteniment", + "Date and Time": "Data i hora", + "DateTime Range": "Rang de data i temps", + "loadingError": "Impossible obtenir la data, si us plau prova-ho més endavant.", + "plugin": "Connector | Connectors", + "install": "Instal·la", + "installing": "Instal·lant", + "uninstall": "Desinstal·la", + "confirmUninstallPlugin": "Estàs segur de desinstal·lar aquest connector?", + "notificationRegional": "Local", + "Clone Monitor": "Clona el monitor", + "Clone": "Clona", + "cloneOf": "Clon de {0}", + "secureOptionNone": "Cap / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignora error TLS", + "From Email": "Des de Correu", + "emailCustomisableContent": "Contingut personalitzable", + "emailCustomSubject": "Tema personalitzable", + "leave blank for default subject": "deixar en blanc per tema per defecte", + "emailCustomBody": "Cos personalitzat", + "leave blank for default body": "deixa en blanc per un cos per defecte", + "emailTemplateServiceName": "Nom de servei", + "emailTemplateHostnameOrURL": "Nom de host o URL", + "emailTemplateStatus": "Estat", + "emailTemplateMonitorJSON": "objecte que descriu el monitor", + "emailTemplateHeartbeatJSON": "objecte que descriu el batec del cor", + "To Email": "Destí email", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "URL del Webhook de Discord", + "wayToGetDiscordURL": "Pots rebre aquest per anar a Paràmetres de Servidor -> Integracions -> Vista *Webhooks -> Nou *Webhook", + "Bot Display Name": "Nom de pantalla de bot", + "Prefix Custom Message": "Prefix de missatge personalitzat", + "Hello @everyone is...": "Hola {'@'} a tothom …", + "Send to channel": "Envia al canal", + "Create new forum post": "Crea una nova publicació", + "postToExistingThread": "Publica a un fil existent", + "forumPostName": "Nom de publicació de fòrum", + "threadForumPostID": "Fil / identificador de fòrum", + "e.g. {discordThreadID}": "exemple {discordThreadID}", + "wayToGetTeamsURL": "Pot aprendre com crear una URL de webhook {0}.", + "wayToGetZohoCliqURL": "Pot aprendre com crear una URL de webhook {0}.", + "needSignalAPI": "Necessites tenir una senyal de client amb REST API.", + "wayToCheckSignalURL": "Pot comprovar aquesta URL per veure com configurar:", + "Number": "Número", + "Recipients": "Receptors", + "Access Token": "Fitxa d'accés", + "Channel access token (Long-lived)": "Fitxa d'accés del canal (de llarga vida)", + "Line Developers Console": "Consola de Desenvolupadors de la línia", + "appriseNotInstalled": "L'apèndix no està instal·lat. {0}", + "Method": "Mètode", + "clearDataOlderThan": "Conserva les dades de l'historial del monitor durant {0} dies.", + "steamApiKeyDescription": "Per a monitoritzar un servidor de jocs de vapor, necessiteu una clau Steam Web-API. Podeu registrar la vostra clau API aquí: ", + "shrinkDatabaseDescriptionSqlite": "Activa la base de dades {vacuum} per a SQLite. {auto.vacuum} ja està activat, però això no desfragmenta la base de dades ni reempaqueta les pàgines individuals de la base de dades de la manera com ho fa l'ordre {vacuum}.", + "liquidIntroduction": "S'aconsegueix la flexibilitat mitjançant el llenguatge de templatació líquid. Consulteu el {0} per a les instruccions d'ús.", + "selectedMonitorCount": "Seleccionat: {0}", + "deleteStatusPageMsg": "Esteu segur que voleu suprimir aquesta pàgina d'estat?", + "proxyDescription": "Els intermediaris s'han d'assignar a un monitor perquè funcioni.", + "enableProxyDescription": "Aquest servidor intermediari no afectarà les sol·licituds del monitor fins que estigui activat. Podeu controlar temporalment desactivar el servidor intermediari de tots els monitors per l'estat d'activació.", + "statusPageSpecialSlugDesc": "Àlies especial {0}: aquesta pàgina es mostrarà quan no es proporcioni l'àlies", + "Authentication": "Autenticació", + "Page Not Found": "Pàgina no trobada", + "Don't know how to get the token? Please read the guide:": "No saps com aconseguir el testimoni? Si us plau, llegiu la guia:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "La connexió actual es pot perdre si esteu connectant a través del túnel Cloudflare. Segur que voleu aturar-ho? Escriviu la contrasenya actual per confirmar-la.", + "Refresh Interval": "Interval de refresc", + "signedInDispDisabled": "Autenticació desactivada.", + "RadiusCalledStationIdDescription": "Identificador del dispositiu anomenat", + "Also check beta release": "Comprova també la versió beta", + "The resource is no longer available.": "El recurs ja no està disponible.", + "certificationExpiryDescription": "Els monitors HTTPS activen la notificació quan el certificat TLS caduca a:", + "deleteDockerHostMsg": "Esteu segur que voleu suprimir aquest amfitrió de l'acoblador per a tots els monitors?", + "tailscalePingWarning": "Per utilitzar el monitor de Ping Tailscale, heu d'instal·lar el Kuma Uptime sense Docker i també instal·lar el client Tailscale al vostre servidor.", + "telegramSendSilentlyDescription": "Envia el missatge en silenci. Els usuaris rebran una notificació sense so.", + "telegramProtectContentDescription": "Si està activat, els missatges del bot del Telegram estaran protegits contra reenviaments i desaments.", + "wayToGetTelegramChatID": "Podeu obtenir el vostre ID de xat enviant un missatge al bot i anant a aquest URL per veure el xat id:", + "trustProxyDescription": "Confia en les capçaleres «X-Forwarded-*». Si voleu obtenir la IP del client correcta i el vostre Kuma Uptime està darrere d'un servidor intermediari com Nginx o Apache, hauríeu d'activar-ho.", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Es pot crear un testimoni d'accés de llarga durada fent clic al nom del vostre perfil (a baix a l'esquerra) i desplaçant-vos a la part inferior i després feu clic a Crea un testimoni. ", + "default: notify all devices": "per defecte: notifica tots els dispositius", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Podeu trobar una llista dels Serveis de Notificació a l'assistent d'inici a «Eines de revelador . Serveis» cerca «notificació» per trobar el nom del vostre dispositiu/telèfon.", + "backupOutdatedWarning": "Obsolet: Atès que s'han afegit moltes característiques i aquesta funció de còpia de seguretat és una mica inexistent, no pot generar o restaurar una còpia de seguretat completa.", + "lastDay1": "L'últim dia del mes", + "pauseMaintenanceMsg": "Segur que voleu fer una pausa?", + "maintenanceStatus-ended": "Finalitzat", + "Display Timezone": "Mostra la zona horària", + "enableNSCD": "Habilita NSCD (Dimoni de memòria cau de Servei del Nom ) per accelerar les sol·licituds de DNS", + "chromeExecutableDescription": "Per a usuaris de Docker, si el Chrome no està encara instal·lat, pot dur uns quants minuts per instal·lar i mostrar el resultat de prova. Duu 1*GB d'espai de disc.", + "uninstalling": "Desinstal·lant", + "smtp": "Correu electrònic (SMTP)", + "smtpLiquidIntroduction": "Els següents dos camps són personalitzables via \"Liquid templating Language\". Per favor refereix al {0} per a instruccions d'ús. Aquests són les variables disponibles:", + "emailTemplateMsg": "missatge de la notificació", + "emailTemplateLimitedToUpDownNotification": "només disponible per estats UP/Down, altrament null", + "Select message type": "Selecciona el tipus de missatge", + "whatHappensAtForumPost": "Crea una nova publicació de fòrum. Això no publica un missatge a un fòrum existent. Per publicar un fil existent utilitza \"{option}\"", + "Channel access token": "Fitxa d'accés del canal", + "Body": "Cos", + "or": "o", + "PushUrl": "URL de captura", + "HeadersInvalidFormat": "Les capçaleres de sol·licitud no són JSON vàlides: ", + "BodyInvalidFormat": "El cos de petició no és JSON vàlid: ", + "Security": "Seguretat", + "Pick Accepted Status Codes...": "Trieu els codis d'estat acceptats…", + "error": "error", + "critical": "crítica", + "Please input title and content": "Introduïu el títol i el contingut", + "telegramServerUrl": "(Opcional) Url del servidor", + "telegramServerUrlDescription": "Per saltar-se les limitacions del bot de Telegram o tenir accés a regions bloquejades (China, Iran, etc). Per a més informació fes click {0}. Per defecte {1}", + "templateServiceName": "Nom del servei", + "templateHostnameOrURL": "Adreça URL o nom del host", + "templateStatus": "Estat", + "telegramUseTemplate": "Fes servir una plantilla de missatge personalitzada", + "telegramUseTemplateDescription": "Si s'activa, el missatge s'enviarà fent servir una plantilla personalitzada.", + "telegramTemplateFormatDescription": "Telegram permet l'ús de diferents tipus de llenguatges de marcat, llegeix Telegram {0} per més detalls.", + "wayToGetDiscordThreadId": "Obtenir un identificador de publicació del fil/fòrum és semblant a obtenir un identificador de canal. Més informació sobre com obtenir identificadors {0}", + "lineDevConsoleTo": "Consola de desenvolupadors de linia - {0}", + "Basic Settings": "Configuracions bàsiques", + "User ID": "ID d'usuari", + "Your User ID": "El teu identificador d'usuari", + "Messaging API": "API de missatges", + "wayToGetLineChannelToken": "Primer accediu a {0}, creeu un proveïdor i un canal (API de missatgeria) i, a continuació, podeu obtenir el token d'accés al canal i l'identificador d'usuari dels elements del menú esmentats anteriorment.", + "Icon URL": "URL de la icona", + "aboutIconURL": "Pots donar un enllaç a la imatge a \"URL de la icona\" per sobreposar-la a la imatge de perfil pere defecte. No s'usarà si hi ha una icona d'emoji establerta.", + "tagAlreadyOnMonitor": "Aquesta etiqueta (nom i valor) ja hi és al monitor o està pendent d'afegir.", + "mqttWebsocketPathInvalid": "Si us plau, feu servir un format de directori de WebSocket vàlid", + "confirmDeleteTagMsg": "Estàs segur que vols eliminar aquesta etiqueta? Els monitors associats amb aquesta etiqueta no seran eliminats.", + "To Phone Number": "Al número de telèfon", + "Manual": "Manual", + "aboutMattermostChannelName": "Podeu substituir el canal predeterminat al qual publica el Webhook introduint el nom del canal al camp \"Nom del canal\". Això s'ha d'activar a la configuració de Mattermost Webhook. Ex: #altre-canal", + "enableGRPCTls": "Permet enviar una sol·licitud gRPC amb connexió TLS", + "deleteMaintenanceMsg": "N'esteu segur que voleu suprimir aquest manteniment?", + "dnsPortDescription": "Port del servidor DNS. El valor predeterminat és 53. Podeu canviar el port en qualsevol moment.", + "enableDefaultNotificationDescription": "Aquesta notificació s'habilitarà per defecte per als monitors nous. Encara podeu desactivar la notificació per separat per a cada monitor.", + "importHandleDescription": "Trieu \"Omet l'existent\" si voleu ometre tots els monitors o notificacions amb el mateix nom. \"Sobreescriure\" suprimirà tots els monitors i notificacions existents.", + "affectedStatusPages": "Mostra aquest missatge de manteniment a les pàgines d'estat seleccionades", + "Path": "Directori", + "mqttWebSocketPath": "Directori MQTT WebSocket", + "mqttHostnameTip": "Si us plau, feu servir aquest format {hostnameFormat}", + "mqttWebsocketPathExplanation": "Directori de WebSocket per a MQTT a través de connexions WebSocket (p. ex., /mqtt)", + "Use HTML for custom E-mail body": "Utilitzeu HTML per al cos personalitzat del correu electrònic", + "dataRetentionTimeError": "El període de retenció ha de ser 0 o superior", + "infiniteRetention": "Establiu-ho a 0 per a una retenció infinita.", + "grpcMethodDescription": "El nom del mètode es converteix en format camelCase, com ara sayHello, check, etc.", + "acceptedStatusCodesDescription": "Seleccioneu els codis d'estat que es considerin una resposta satisfactòria.", + "deleteMonitorMsg": "N'esteu segur que voleu suprimir aquest monitor?", + "deleteNotificationMsg": "Confirmes que vols suprimir aquesta notificació per a tots els monitors?", + "resolverserverDescription": "Cloudflare és el servidor predeterminat. Podeu canviar el servidor de resolució en qualsevol moment.", + "rrtypeDescription": "Seleccioneu el tipus de RR que voleu monitoritzar", + "pauseMonitorMsg": "Segur que vols pausar-ho?", + "clearEventsMsg": "Confirmes que vols suprimir tots els esdeveniments d'aquest monitor?", + "clearHeartbeatsMsg": "Confirmes que vols suprimir tots els batecs d'aquest monitor?", + "confirmClearStatisticsMsg": "Esteu segur que voleu suprimir TOTES les estadístiques?", + "confirmImportMsg": "N'esteu segur que voleu importar la còpia de seguretat? Verifiqueu que heu seleccionat l'opció d'importació correcta.", + "twoFAVerifyLabel": "Introduïu el vostre token per verificar 2FA:", + "tokenValidSettingsMsg": "El token és vàlid! Ara podeu desar la configuració de 2FA.", + "confirmEnableTwoFAMsg": "N'esteu segur que voleu activar 2FA?", + "confirmDisableTwoFAMsg": "Esteu segur que voleu deshabilitar 2FA?", + "recurringIntervalMessage": "Executar un cop cada dia | Executeu un cop cada {0} dies", + "affectedMonitorsDescription": "Seleccioneu els monitors afectats pel manteniment actual", + "atLeastOneMonitor": "Seleccioneu almenys un monitor afectat", + "passwordNotMatchMsg": "La contrasenya repetida no coincideix.", + "notificationDescription": "Les notificacions s'han d'assignar a un monitor perquè funcionin.", + "pushyToken": "Token de dispositiu", + "Add Tags": "Afegir etiquetes", + "tagAlreadyStaged": "Aquesta etiqueta (nom i valor) ja està preparada per a aquest lot.", + "tagNameExists": "Ja existeix una etiqueta del sistema amb aquest nom. Seleccioneu-la de la llista o utilitzeu un nom diferent.", + "high": "alt", + "defaultFriendlyName": "Nou monitor", + "descriptionHelpText": "Es mostra al tauler intern. S’admet l’ús de Markdown i s’aplica una neteja segura (es conserven els espais i la sagría) abans de mostrar-se.", + "Clear All Events": "Neteja tots els esdeveniments", + "Could not clear events": "No s'han pogut netejar {failed}/{total} esdeveniments", + "invertKeywordDescription": "Busca que la paraula clau no hi sigui present, en lloc de que hi sigui.", + "backupDescription": "Podeu fer una còpia de seguretat de tots els monitors i notificacions en un fitxer JSON.", + "backupDescription3": "Les dades sensibles, com els tokens de notificació, estan incloses al fitxer d’exportació. Assegura’t de desar-lo de manera segura.", + "octopushAPIKey": "Clau d’API obtinguda de les credencials HTTP al tauler de control.", + "octopushLogin": "Nom d’usuari de les credencials de l’API HTTP al tauler de control.", + "promosmsLogin": "Nom d’inici de sessió de l’API", + "promosmsPassword": "Contrasenya de l’API", + "supportBaleChatID": "Admet Xat Directe / Grups / ID de xat del canal", + "wayToGetBaleChatID": "Pots obtenir el teu ID de xat enviant un missatge al bot i anant a aquesta URL per veure el chat_id:", + "wayToGetBaleToken": "Pots obtenir un token des de {0}.", + "Events cleared successfully": "Els esdeveniments s'han esborrat correctament.", + "No monitors found": "No s'ha trobat cap monitor.", + "clearAllEventsMsg": "Esteu segur que voleu suprimir tots els esdeveniments?", + "endpoint": "endpoint", + "keywordDescription": "Cerca paraules clau en HTML pla o en resposta JSON. La cerca distingeix entre majúscules i minúscules.", + "jsonQueryDescription": "Analitza i extreu dades específiques de la resposta JSON del servidor mitjançant una JSON query, o bé utilitza “$” per mostrar la resposta en cru si no s’espera un JSON. El resultat es compara amb el valor esperat com a cadenes de text. Consulta {0} per veure la documentació i fes servir {1} per provar consultes.", + "auto-select": "Selecció Automàtica", + "backupDescription2": "Nota: les dades d’historial i d’esdeveniments no estan incloses.", + "ignoreSecWebsocketAcceptHeaderDescription": "Permet que el servidor no respongui amb la capçalera Sec-WebSocket-Accept si l'actualització de websocket té èxit.", + "Advanced Message Queuing Protocol": "Protocol avançat de cues de missatges (AMQP) 1.0+", + "Miele Cloud Connect Protocol": "Protocol de connexió al núvol de Miele", + "Ignore Sec-WebSocket-Accept header": "Ignora la capçalera de {0}", + "wsSubprotocolDescription": "Introdueix una llista de subprotocols separats per comes. Per obtenir més informació sobre els subprotocols, consulta la {documentació}.", + "WebSocket Application Messaging Protocol": "WAMP (Protocol de missatgeria d'aplicacions WebSocket)", + "Session Initiation Protocol": "Transport WebSocket per a SIP (Protocol d'inici de sessió)", + "Network API for Notification Channel": "API de xarxa RESTful OMA per al canal de notificació", + "Web Process Control Protocol": "Protocol de control de processos web (WPCP)", + "jsflow": "Protocol de pubsub/cua de jsFlow", + "Reverse Web Process Control": "Protocol de control de processos web invers (RWPCP)", + "Extensible Messaging and Presence Protocol": "Transport WebSocket per al protocol extensible de missatgeria i presència (XMPP)", + "Smart Home IP": "SHIP - IP de la llar intel·ligent", + "Push Channel Protocol": "Protocol de canal push", + "Message Session Relay Protocol": "Transport WebSocket per a MSRP (Protocol de retransmissió de sessions de missatges)", + "Binary Floor Control Protocol": "Transport WebSocket per a BFCP (Protocol de control de planta binària)", + "Monitors": "{n} monitor | {n} monitors", + "unknownDays": "Dies desconeguts", + "wsCodeDescription": "Per a més informació sobre els codis d'estat, consulteu {rfc6455}", + "Subprotocol(s)": "subprotocol(s)", + "versionIs": "Versió: {version}", + "Only retry if status code check fails": "Torna-ho a provar només si falla la comprovació del codi d'estat", + "retryOnlyOnStatusCodeFailureDescription": "Si està habilitat, només es tornaran a intentar les comprovacions quan falli la comprovació del codi d’estat HTTP (per exemple, si el servidor no respon). Si la comprovació del codi d’estat és correcta però la consulta JSON falla, el monitor es marcarà immediatament com a inoperatiu sense reintents.", + "No incidents recorded": "Sense incidències registrats", + "Load More": "Carrega més", + "Loading...": "Carregant...", + "Pin this incident": "Fixa aquesta incidència", + "enableSSL": "Activa SSL/TLS", + "mariadbUseSSLHelptext": "Activa per a fer servir una connexió encriptada a la base de dades. Necessari per la majoria de bases de dades al núvol.", + "mariadbCaCertificateLabel": "Certificat CA", + "mariadbCaCertificateHelptext": "Enganxa el certificat CA Cert en format PEM per fer servir certificats autosignats. Deixa per emplenar si la teva base de dades fa servir un certificat signat per un CA públic.", + "days": "{n} dia | {n} dies", + "hours": "{n} hora | {n} hores", + "minutes": "{n} minut | {n} minuts", + "minuteShort": "{n} min | {n} min", + "years": "{n} any | {n} anys", + "oracledbConnectionString": "Base de dades d'oracle: {connectionString}", + "monitorTypeGameServer": "Servidor de jocs", + "monitorTypeDatabase": "Tipus de monitor de la base de dades", + "monitorTypeSpecial": "Especial", + "Connection Timeout": "Temps d'espera de la connexió", + "saveResponseForNotifications": "Desa la resposta HTTP d'èxit per a les notificacions", + "saveErrorResponseForNotifications": "Desa la resposta d'error HTTP per a les notificacions", + "saveResponseDescription": "Emmagatzema la resposta HTTP i la posa a disposició de les plantilles de notificació com a {templateVariable}", + "responseMaxLength": "Longitud màxima de la resposta (bytes)", + "responseMaxLengthDescription": "Mida màxima de les dades de resposta a emmagatzemar. Establiu-ho a 0 per a il·limitat. Es truncaran les respostes més grans. Per defecte: 1024 (1KB)", + "logoutCurrentUser": "Tanca la sessió {username}", + "Resolver Server(s)": "Servidors de resolució", + "Incident description": "Descripció de l'incident", + "Incident not found or access denied": "Incident no trobat o accés denegat", + "Past Incidents": "Incidents passats", + "Incident title": "Títol de l'incident", + "Pinned incidents are shown prominently on the status page": "Les incidències fixades es mostren de manera destacada a la pàgina d'estat", + "Edit Incident": "Edita l'incident", + "templateAvailableVariables": "Variables disponibles", + "example": "Exemple", + "Result": "Resultat", + "HeadersInvalidFormatBecause": "Les capçaleres de sol·licitud no són JSON vàlides perquè {error}", + "BodyInvalidFormatBecause": "El cos de la sol·licitud no és JSON vàlid perquè {error}", + "steamApiKeyDescriptionAt": "Per a monitoritzar un servidor de jocs de Steam, necessiteu una clau Steam Web-API. Podeu registrar la vostra clau API a {url}", + "hostnameCannotBeIP": "El nom del servidor DNS no pot ser una IP. Volíeu utilitzar el camp resolver?", + "invalidHostnameOrIP": "El nom d'amfitrió o la IP no són vàlids. El nom d'amfitrió ha de ser un FQDN vàlid. No es pot utilitzar comodí. Pot tenir subratllat o acabar amb un punt.", + "invalidDNSHostname": "Nom d'amfitrió no vàlid. El nom d'amfitrió ha de ser un FQDN vàlid. Pot ser un comodí, tenir subratllat o acabar amb un punt.", + "wildcardOnlyForDNS": "Els noms d'amfitrió Wildcard només són compatibles amb monitors DNS.", + "invalidURL": "URL no vàlida", + "Resolve": "Resol", + "Resolved": "Resolt", + "createdAt": "Creat: {date}", + "lastUpdatedAt": "Darrera actualització: {date}", + "lastUpdatedAtFromNow": "Darrera actualització: {date} ({fromNow})", + "Actions": "Accions", + "selectedMonitorCountMsg": "seleccionat: {n} | seleccionat: {n}", + "selectMonitorMsg": "Seleccioneu els monitors per a realitzar accions", + "selectAllMonitorsAria": "Selecciona tots els monitors", + "deselectAllMonitorsAria": "Desselecciona tots els monitors", + "deleteIncidentMsg": "Esteu segur que voleu suprimir aquest incident?", + "Certificate Chain:": "Cadena de certificats:", + "slug is not found": "No s'ha trobat la lligadura", + "Bearer Token": "Bearer Token", + "Please input content": "Introduïu el contingut", + "Please input title": "Si us plau, introduïu el títol", + "dateCreatedAtFromNow": "Data de creació: {date} ({fromNow})", + "RSS Title": "Títol RSS", + "Leave blank to use status page title": "Deixa en blanc per utilitzar el títol de la pàgina d'estat", + "certificateExpiryNotificationHelp": "El nombre de dies d'antelació es pot configurar a la Configuració.", + "Cloud ID": "ID del núvol", + "API Token": "Token (fitxa) d'API", + "See Jira Cloud Docs": "Veure Jira Cloud Docs", + "certHostnameMismatch": "El nom del servidor del certificat no coincideix amb l'URL del monitor.", + "sipsakPingWarning": "Per utilitzar el monitor Ping Opcions del SIP, heu d'instal·lar el Kuma Uptime sense Docker i també instal·lar el client Sipsak al vostre servidor.", + "pingPacketSizeWarning": "Una mida de paquet menor de 16 no produeix temps d'anada i tornada a Linux", + "wayToGetMaxToken": "Podeu obtenir un testimoni de bot MAX i altres detalls de {0}.", + "maxMessenger": "Missatger màxim", + "matrixUseTemplate": "Fes servir una plantilla de missatge personalitzada", + "matrixUseTemplateDescription": "Si s'activa, el missatge s'enviarà fent servir una plantilla personalitzada.", + "maxApiUrlDescription": "URL de l'API base per al missatger MAX. Per defecte: https://platform-api.max.ru", + "wayToGetMaxChatID": "Especifiqueu l'identificador de xat al MAX on s'han de lliurar els missatges.", + "maxUseTemplate": "Fes servir una plantilla de missatge personalitzada", + "maxUseTemplateDescription": "Si s'activa, el missatge s'enviarà fent servir una plantilla personalitzada.", + "maxTemplateFormatDescription": "El missatger MAX admet text pla, Markdown i format HTML.", + "Examples:": "Exemples: {0}", + "Notification Action": "Acció de notificació", + "homeAssistantNotificationActionHelptext": "Es pot trobar una llista d'accions de notificació a l'assistent d'inici a «Configuració > Eines per a desenvolupadors > Accions». Cerqueu «notifica» per a trobar les vostres accions. Introduïu només la part després de «notify.», p. ex. per a l'acció «notify.mobile_app_xyz» introduïu «mobile_app_xyz». Per a les notificacions mòbils incorporades, cerqueu \"Envia una notificació a través de mobile_app_xyz\" (no \"Envia una notificació\").", + "frontendVersionIs": "Versió frontal: {version}", + "cronScheduleDescription": "Programació: {description}", + "Duration (Minutes)": "Durada (minuts)", + "Clone Maintenance": "Clona el manteniment", + "ariaPauseMaintenance": "Posa en pausa aquesta planificació de manteniment", + "ariaResumeMaintenance": "Reprèn aquest calendari de manteniment", + "ariaCloneMaintenance": "Crea una còpia d'aquesta planificació de manteniment", + "ariaEditMaintenance": "Edita aquesta planificació de manteniment", + "ariaDeleteMaintenance": "Suprimeix aquesta planificació de manteniment", + "notificationUniversal": "Universal", + "notificationChatPlatforms": "Plataformes de xat", + "notificationPushServices": "Serveis Push", + "notificationSmsServices": "Serveis SMS", + "notificationEmail": "Correu electrònic", + "notificationIncidentManagement": "Gestió d'incidents", + "notificationHomeAutomation": "Automatització d'inici", + "notificationOther": "Altres integracions", + "SMTP Security": "Seguretat SMTP", + "Ignore STARTTLS": "Ignora STARTTLS", + "Use STARTTLS": "Usa STARTTLS", + "Disable STARTTLS": "Desactiva STARTTLS", + "disableSTARTTLSDescription": "Activeu aquesta opció per als servidors SMTP que no suporten STARTTLS. Això enviarà correus electrònics a través d'una connexió no xifrada.", + "teamsEnableTags": "Inclou les etiquetes", + "teamsEnableTagsDescription": "Si està activat, el missatge inclourà les etiquetes del monitor.", + "API Version": "Versió API", + "deleteMonitorsMsg": "Segur que voleu suprimir els monitors seleccionats?", + "pausedMonitorsMsg": "S'ha pausat un monitor | S'han pausat {n} monitors", + "resumedMonitorsMsg": "S'ha reprès un monitor | S'han reprès {n} monitors", + "deletedMonitorsMsg": "S'ha suprimit un monitor | S'han suprimit {n} monitors", + "noMonitorsPausedMsg": "No hi ha cap monitor en pausa (cap estava actiu)", + "noMonitorsResumedMsg": "No s'ha reprès cap monitor (cap estava inactiu)", + "bulkDeleteErrorMsg": "No s'ha pogut suprimir el monitor | No s'han pogut suprimir {n} monitors", + "deleteGroupMsg": "Esteu segur que voleu suprimir aquest grup?", + "Status:": "Estat: {0}", + "alertaAlertState": "Estat d'alerta", + "alertaRecoverState": "Estat de recuperació", + "Sets end time based on start time": "Estableix l'hora de finalització basada en l'hora d'inici", + "Please set start time first": "Establiu primer l'hora d'inici", + "noMonitorsOrStatusPagesSelectedError": "No es pot crear un manteniment sense els monitors o pàgines d'estat afectades", + "Peer ID": "ID del contacte", + "pushoversounds bike": "Bicicleta", + "pushoversounds cashregister": "Registre d'efectiu", + "pushoversounds classical": "Clàssica", + "pushoversounds cosmic": "Còsmic", + "pushoversounds falling": "Caiguda", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Entrada", + "pushoversounds intermission": "Intermissió", + "pushoversounds magic": "Màgia", + "pushoversounds mechanical": "Mecànic", + "pushoversounds pianobar": "Barra de piano", + "pushoversounds siren": "Sirena", + "pushoversounds spacealarm": "Alarma espacial", + "Copy to Clipboard": "Copia al porta-retalls", + "Copied to clipboard!": "S'ha copiat al porta-retalls", + "Device": "Dispositiu", + "Notification Sound": "So de notificació", + "Strategy": "Estratègia", + "Base URL": "URL de base", + "Platform": "Plataforma", + "Topic": "Tema", + "Proxy Server": "Servidor intermediari", + "Channel Name": "Nom del canal", + "signalUseTemplate": "Fes servir una plantilla de missatge personalitzada", + "slackUseTemplate": "Fes servir una plantilla de missatge personalitzada", + "smtpAdditionalHeadersTitle": "Capçaleres addicionals", + "documentation": "documentació", + "smtpDkimPrivateKey": "Clau privada", + "alertaEnvironment": "Entorn", + "serwersmsAPIPassword": "Contrasenya de l’API", + "serwersmsPhoneNumber": "Número de telèfon", + "serwersmsRecipientTypePhone": "Número de telèfon", + "Server Address": "Adreça del servidor", + "Learn More": "Més informació", + "Expiry date": "Data de venciment", + "Continue": "Continua", + "Generate": "Genera", + "pagertreeCritical": "Crític", + "pagertreeDoNothing": "No facis res", + "lunaseaTarget": "Objectiu", + "lunaseaDeviceID": "ID del dispositiu", + "ntfyCall": "Trucada telefònica", + "plivoMessageType": "Tipus de missatge", + "plivoVoiceCall": "Trucada", + "Close": "Tanca", + "Flowtriq Webhook URL": "URL del Webhook", + "Command": "Ordre", + "Telephone number": "Número de telèfon", + "Condition": "Condició", + "threemaRecipient": "Destinatari", + "threemaRecipientTypePhone": "Número de telèfon", + "Group Name": "Nom del grup", + "Conditions": "Condicions", + "conditionAdd": "Afegir una condició", + "conditionValuePlaceholder": "Valor", + "equals": "és igual a", + "contains": "conté", + "starts with": "comença amb", + "ends with": "acaba amb", + "message": "missatge", + "Sound": "So", + "Correct": "Correcte", + "Fail": "Error", + "Reveal": "Mostra", + "Money": "Diners", + "Elevator": "Ascensor", + "Guitar": "Guitarra", + "Pop": "Pop", + "brevoFromEmail": "Des de Correu", + "brevoToEmail": "Destí email", + "brevoLeaveBlankForDefaultSubject": "deixa en blanc per un títol per defecte", + "resendFromEmail": "Des de correu electrònic", + "resendToEmail": "Correu electrònic de destinació", + "Custom URL": "URL personalitzat", + "wahaSession": "Sessió", + "openwaSession": "Sessió", + "360messengerMessageTemplate": "Plantilla del missatge", + "Sender name": "Nom del remitent", + "discordUseMessageTemplate": "Fes servir una plantilla de missatge personalitzada", + "fluxerUseMessageTemplate": "Fes servir una plantilla de missatge personalitzada", + "Protocol": "Protocol", + "Location": "Ubicació", + "labelDomainNameExpiryNotification": "Notificació de venciment del nom de domini", + "password": "Contrasenya", + "mtls-auth-server-key-label": "Clau", + "mtls-auth-server-ca-label": "CA", + "Sort by status": "Ordena per estat", + "Sort by name": "Ordena per nom", + "Region": "Regió", + "Never": "Mai", + "SSL/TLS": "SSL/TLS", + "Metadata": "Metadades", + "teltonikaPhoneNumber": "Número de telèfon", + "VKTeams Use Template": "Fes servir una plantilla de missatge personalitzada", + "VKTeams Use Template Description": "Si s'activa, el missatge s'enviarà fent servir una plantilla personalitzada.", + "deleteChildrenMonitors": "També esborra els monitors fills i descendents, si en tenen | També esborra tots els {count} monitors fills i descendents, si en tenen", + "noMonitorsSelectedWarning": "Estàs creant un manteniment sense cap monitor afectat. N'estàs segur que vols continuar?", + "vkApiVersionDescription": "Versió de l'API de VK utilitzada per a les sol·licituds. Deixeu el valor per defecte llevat que necessiteu específicament una altra versió de l'API de VK per compatibilitat.", + "vkDontParseLinks": "Desactiva els fragments d'enllaç", + "vkDontParseLinksDescription": "Si s'activa, VK no generarà vistes prèvies/fragments d'enllaç.", + "vkPeerIdDescription": "Introduïu el peer_id de destinació de VK. Aquest valor s'envia a l'API tal qual.", + "pushoversounds pushover": "Pushover (per defecte)", + "pushoversounds bugle": "Corneta", + "pushoversounds tugboat": "Remolcador", + "pushoversounds alien": "Alarma alienígena (llarga)", + "pushoversounds climb": "Ascens (llarga)", + "pushoversounds persistent": "Persistent (llarga)", + "pushoversounds echo": "Eco de Pushover (llarga)", + "pushoversounds updown": "Amunt i avall (llarga)", + "pushoversounds vibrate": "Només vibració", + "pushoversounds none": "Cap (silenci)", + "pushyAPIKey": "Clau API secreta", + "apprise": "Apprise (compatible amb més de 50 serveis de notificació)", + "GoogleChat": "Google Chat (només Google Workspace)", + "Google Apps Script Webhook URL": "URL del Webhook de Google Apps Script", + "Deploy a Google Apps Script as a web app and paste the URL here": "Desplega un Google Apps Script com a aplicació web i enganxa la URL aquí", + "Quick Setup Guide": "Guia de configuració ràpida", + "Open your Google Spreadsheet": "Obre el teu full de càlcul de Google", + "Go to Extensions → Apps Script": "Ves a Extensions → Apps Script", + "Paste the script code (see below)": "Enganxa el codi de l'script (vegeu més avall)", + "Click Deploy → New deployment → Web app": "Fes clic a Desplega → Nou desplegament → Aplicació web", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Estableix 'Executa com a: Jo' i 'Qui té accés: Qualsevol persona'", + "Copy the web app URL and paste it above": "Copia la URL de l'aplicació web i enganxa-la a dalt", + "Google Apps Script Code": "Codi de Google Apps Script", + "Failed to copy to clipboard": "No s'ha pogut copiar al porta-retalls", + "Template plain text instead of using cards": "Utilitza text pla en lloc de targetes", + "issueWithGoogleChatOnAndroidHelptext": "Això també permet evitar errors de codi font com {issuetackerURL}", + "wayToGetKookBotToken": "Crea una aplicació i obtén el testimoni del teu bot a {0}", + "wayToGetKookGuildID": "Activa el 'Mode desenvolupador' a la configuració de Kook i fes clic dret al gremi per obtenir el seu ID", + "Guild ID": "ID del gremi", + "User Key": "Clau d'usuari", + "Message Title": "Títol del missatge", + "More info on:": "Més informació a: {0}", + "pushoverDesc1": "La prioritat d'emergència (2) té un temps d'espera predeterminat de 30 segons entre reintents i caducarà després d'1 hora.", + "pushoverDesc2": "Si voleu enviar notificacions a diferents dispositius, ompliu el camp Dispositiu.", + "pushoverMessageTtl": "TTL del missatge (segons)", + "SMS Type": "Tipus d'SMS", + "octopushTypePremium": "Premium (Ràpid - recomanat per a alertes)", + "octopushTypeLowCost": "Baix cost (Lent - de vegades bloquejat per l'operador)", + "checkPriceAt": "Consulta els preus de {service} a {url}", + "apiCredentials": "Credencials de l'API", + "octopushLegacyHint": "Fas servir la versió antiga d'Octopush (2011-2020) o la nova versió?", + "Check octopush prices": "Consulta els preus d'octopush {0}.", + "octopushPhoneNumber": "Número de telèfon (format internacional, p. ex.: +33612345678)", + "octopushSMSSender": "Nom del remitent SMS: 3-11 caràcters alfanumèrics i espai (a-zA-Z0-9)", + "LunaSea Device ID": "ID de dispositiu LunaSea", + "Apprise URL": "URL d'Apprise", + "Example:": "Exemple: {0}", + "Read more:": "Llegeix més: {0}", + "Free Mobile User Identifier": "Identificador d'usuari de Free Mobile", + "Free Mobile API Key": "Clau API de Free Mobile", + "Enable TLS": "Activa TLS", + "Proto Service Name": "Nom del servei Proto", + "Proto Method": "Mètode Proto", + "Proto Content": "Contingut Proto", + "Economy": "Econòmic", + "Lowcost": "Baix cost", + "SendKey": "SendKey", + "SMSManager API Docs": "Documentació de l'API de SMSManager", + "Gateway Type": "Tipus de passarel·la", + "You can divide numbers with commas or semicolons": "Podeu separar els números amb {comma} o {semicolon}", + "goAlertInfo": "GoAlert és una aplicació de codi obert per a la planificació de guàrdies, escalats automatitzats i notificacions (com SMS o trucades de veu). Involucra automàticament la persona adequada, de la manera adequada i en el moment adequat! {0}", + "goAlertIntegrationKeyInfo": "Obteniu la clau d'integració genèrica de l'API per al servei en aquest format \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\", normalment el valor del paràmetre token de la URL copiada.", + "AccessKeyId": "ID d'AccessKey", + "SecretAccessKey": "Secret d'AccessKey", + "PhoneNumbers": "Números de telèfon", + "TemplateCode": "Codi de plantilla", + "SignName": "Nom de signatura", + "OptionalParameters": "Paràmetres opcionals", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "A causa de les restriccions de l'operadora, activeu les variables opcionals amb el risc de no lliurament", + "aliyun-template-requirements-and-parameters": "La plantilla d'SMS d'aliyun ha de contenir els paràmetres: {parameters}", + "aliyun-template-optional-parameters": "Paràmetres opcionals: {parameters}", + "Bark API Version": "Versió de l'API de Bark", + "Bark Endpoint": "Punt final de Bark", + "Bark Group": "Grup de Bark", + "Bark Sound": "So de Bark", + "WebHookUrl": "URL del Webhook", + "SecretKey": "Clau secreta", + "For safety, must use secret key": "Per seguretat, cal utilitzar una clau secreta", + "Mentioning": "Mencions", + "Don't mention people": "No mencionis ningú", + "Mention group": "Menciona {group}", + "Mention Mobile List": "Menciona la llista de mòbils", + "Mention User List": "Menciona la llista d'ID d'usuari", + "Dingtalk Mobile List": "Llista de mòbils", + "Dingtalk User List": "Llista d'ID d'usuari", + "Enter a list of userId": "Introduïu una llista d'userId", + "Enter a list of mobile": "Introduïu una llista de mòbils", + "Invalid mobile": "Mòbil no vàlid [{mobile}]", + "Invalid userId": "userId no vàlid [{userId}]", + "Device Token": "Testimoni del dispositiu", + "Huawei": "Huawei", + "High": "Alt", + "Retry": "Reintenta", + "WeCom Bot Key": "Clau del bot de WeCom", + "WeCom Mentioned Mobile List": "Llista de mòbils mencionats de WeCom", + "WeCom Mentioned Mobile List Description": "Introduïu els números de telèfon a mencionar. Separeu diversos números amb comes. Utilitzeu {'@'}all per mencionar-los tots.", + "Setup Proxy": "Configura el servidor intermediari", + "Proxy Protocol": "Protocol del servidor intermediari", + "Proxy server has authentication": "El servidor intermediari té autenticació", + "promosmsTypeEco": "SMS ECO - econòmic però lent i sovint sobrecarregat. Limitat només a destinataris polonesos.", + "promosmsTypeFlash": "SMS FLASH - El missatge es mostrarà automàticament al dispositiu del destinatari. Limitat només a destinataris polonesos.", + "promosmsTypeFull": "SMS FULL - Nivell premium d'SMS, podeu utilitzar el vostre nom de remitent (cal registrar-lo primer). Fiable per a alertes.", + "promosmsTypeSpeed": "SMS SPEED - Prioritat més alta del sistema. Molt ràpid i fiable però costós (aproximadament el doble del preu de l'SMS FULL).", + "promosmsPhoneNumber": "Número de telèfon (per a destinataris polonesos podeu ometre els prefixos)", + "promosmsSMSSender": "Nom del remitent SMS: nom preregistrat o un dels predeterminats: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "promosmsAllowLongSMS": "Permet SMS llargs", + "Feishu WebHookUrl": "URL del Webhook de Feishu", + "matrixHomeserverURL": "URL del servidor Matrix (amb http(s):// i, opcionalment, el port)", + "Internal Room Id": "ID intern de la sala", + "matrixDesc1": "Podeu trobar l'ID intern de la sala a la secció avançada de la configuració de la sala al vostre client Matrix. Hauria de tenir un aspecte com !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "És molt recomanable crear un usuari nou i no utilitzar el testimoni d'accés del vostre propi usuari de Matrix, ja que permetria accés total al vostre compte i a totes les sales a les quals us heu unit. En canvi, creeu un usuari nou i convideu-lo només a la sala on vulgueu rebre la notificació. Podeu obtenir el testimoni d'accés executant {0}", + "Notify Channel": "Notifica el canal", + "aboutNotifyChannel": "Notifica el canal activarà una notificació d'escriptori o mòbil per a tots els membres del canal, independentment que la seva disponibilitat estigui establerta com a activa o absent.", + "Uptime Kuma URL": "URL d'Uptime Kuma", + "setup a new monitor group": "configura un nou grup de monitors", + "openModalTo": "obre el diàleg per a {0}", + "Add a domain": "Afegeix un domini", + "Remove domain": "Elimina el domini '{0}'", + "Icon Emoji": "Emoji d'icona", + "signalImportant": "IMPORTANT: No podeu barrejar grups i números als destinataris!", + "signalUseTemplateDescription": "Si s'activa, el missatge s'enviarà utilitzant una plantilla personalitzada. Podeu utilitzar Liquid per personalitzar el format de la notificació.", + "aboutWebhooks": "Més informació sobre Webhooks a: {0}", + "aboutJiraCloudId": "Més informació sobre l'ID de Jira Cloud: {0}", + "see Jira Cloud Docs": "vegeu la documentació de Jira Cloud", + "Jira Service Management": "Jira Service Management", + "aboutSlackUsername": "Canvia el nom mostrat del remitent del missatge. Si voleu mencionar algú, incloeu-ho al nom senzill en el seu lloc.", + "slackIncludeGroupName": "Inclou el nom del grup de monitors", + "slackIncludeGroupNameDescription": "Si s'activa, el camí del grup de monitors s'inclourà a les notificacions per ajudar a distingir monitors amb el mateix nom entre diferents grups.", + "slackUseTemplateDescription": "Si s'activa, el missatge s'enviarà utilitzant una plantilla personalitzada. Podeu utilitzar Liquid per incloure informació del grup de monitors mitjançant monitorJSON.path o monitorJSON.pathName.", + "aboutChannelName": "Introduïu el nom del canal al camp Nom del canal de {0} si voleu evitar el canal del Webhook. Ex: #un-altre-canal", + "aboutKumaURL": "Si deixeu el camp URL d'Uptime Kuma en blanc, s'utilitzarà per defecte la pàgina de GitHub del projecte.", + "smtpAdditionalHeadersDesc": "Consulteu les capçaleres personalitzades de Nodemailer {0} per a l'ús.", + "smtpDkimSettings": "Configuració DKIM", + "smtpDkimDesc": "Consulteu el DKIM de Nodemailer {0} per a l'ús.", + "smtpDkimDomain": "Nom de domini", + "smtpDkimKeySelector": "Selector de clau", + "smtpDkimHashAlgo": "Algorisme de hash (opcional)", + "smtpDkimheaderFieldNames": "Claus de capçalera a signar (opcional)", + "smtpDkimskipFields": "Claus de capçalera a no signar (opcional)", + "wayToGetPagerDutyKey": "Podeu obtenir-ho anant a Servei -> Directori de serveis -> (Seleccioneu un servei) -> Integracions -> Afegeix integració. Aquí podeu cercar \"Events API V2\". Més informació {0}", + "Integration Key": "Clau d'integració", + "Integration URL": "URL d'integració", + "Auto resolve or acknowledged": "Resol o reconeix automàticament", + "do nothing": "no facis res", + "auto acknowledged": "reconeixement automàtic", + "auto resolve": "resolució automàtica", + "alertaApiEndpoint": "Punt final de l'API", + "alertaApiKey": "Clau API", + "serwersmsAPIUser": "Nom d'usuari de l'API (inclou el prefix webapi_)", + "serwersmsRecipientType": "Tipus de destinatari", + "serwersmsRecipientTypeGroup": "Grup", + "serwersmsGroupId": "ID de grup", + "serwersmsGroupIdHelptext": "ID o IDs de grup al Panell del client. Aquests identificadors es poden descarregar mitjançant els grups d'acció / índex o copiant-los des de l'edició del grup al Panell del client.", + "serwersmsSenderName": "Nom del remitent SMS (registrat mitjançant el portal del client)", + "smseagleTo": "Número(s) de telèfon", + "smseagleGroup": "Nom(s) de grup de l'agenda", + "smseagleContact": "Nom(s) de contacte de l'agenda", + "smseagleGroupV2": "ID(s) de grup de l'agenda", + "smseagleContactV2": "ID(s) de contacte de l'agenda", + "smseagleRecipientType": "Tipus de destinatari", + "smseagleRecipient": "Destinatari(s) (si n'hi ha diversos, separeu-los amb comes)", + "smseagleToken": "Testimoni d'accés de l'API", + "smseagleUrl": "URL del vostre dispositiu SMSEagle", + "smseagleEncoding": "Envia com a Unicode (per defecte=GSM-7)", + "smseaglePriority": "Prioritat del missatge (0-9, la prioritat més alta és 9)", + "smseagleMsgType": "Tipus de missatge", + "smseagleMsgSms": "Missatge SMS (per defecte)", + "smseagleMsgRing": "Trucada de timbre", + "smseagleMsgTts": "Trucada de text a veu", + "smseagleMsgTtsAdvanced": "Trucada avançada de text a veu", + "smseagleDuration": "Durada (en segons)", + "smseagleTtsModel": "ID del model de text a veu", + "smseagleApiType": "Versió de l'API", + "smseagleApiv1": "APIv1 (per a projectes existents i compatibilitat amb versions anteriors)", + "smseagleApiv2": "APIv2 (recomanat per a noves integracions)", + "smseagleDocs": "Comproveu la documentació o la disponibilitat d'APIv2: {0}", + "smseagleComma": "Si n'hi ha diversos, separeu-los amb comes", + "smspartnerApiurl": "Podeu trobar la vostra clau API al vostre tauler a {0}", + "smspartnerPhoneNumber": "Número(s) de telèfon", + "smspartnerPhoneNumberHelptext": "El número ha d'estar en format internacional {0}, {1}. Els diversos números s'han de separar amb {2}", + "smspartnerSenderName": "Nom del remitent SMS", + "smspartnerSenderNameInfo": "Ha de tenir entre 3 i 11 caràcters normals", + "Recipient Number": "Número del destinatari", + "Access Key": "Clau d'accés", + "ooredooUsernameDescription": "L'adreça de correu que utilitzeu per iniciar sessió al tauler d'SMS massius d'Ooredoo.", + "ooredooAccessKeyDescription": "Es troba a \"API Access Key\" al tauler d'SMS d'Ooredoo. Enganxeu-la exactament com es mostra: es codifica automàticament en base64 abans d'enviar-la.", + "ooredooBearerTokenDescription": "El testimoni que es mostra després de \"Authorization: Bearer\" a l'exemple de comanda cURL al vostre tauler d'SMS d'Ooredoo.", + "ooredooRecipientDescription": "Introduïu un o més números separats per una coma o espai. Els números locals de 7 dígits s'anteposen automàticament amb 960.", + "Leave blank to use the default Ooredoo endpoint.": "Deixeu-ho en blanc per utilitzar el punt final predeterminat d'Ooredoo.", + "From Name/Number": "Des de nom/número", + "Leave blank to use a shared sender number.": "Deixeu-ho en blanc per utilitzar un número de remitent compartit.", + "Octopush API Version": "Versió de l'API d'Octopush", + "octopushEndpoint": "octopush (punt final: {url})", + "legacyOctopushEndpoint": "Octopush-DM antic (punt final: {url})", + "ntfy Topic": "Tema de ntfy", + "Server URL should not contain the nfty topic": "La URL del servidor no ha de contenir el tema de ntfy", + "onebotHttpAddress": "Adreça HTTP de OneBot", + "onebotMessageType": "Tipus de missatge de OneBot", + "onebotGroupMessage": "Grup", + "onebotPrivateMessage": "Privat", + "onebotUserOrGroupId": "ID de grup/usuari", + "onebotSafetyTips": "Per seguretat, cal establir un testimoni d'accés", + "PushDeer Server": "Servidor PushDeer", + "pushDeerServerDescription": "Deixeu-ho en blanc per utilitzar el servidor oficial", + "PushDeer Key": "Clau PushDeer", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) es lliura a través de l'aplicació independent WxPusher en 6 plataformes — Android, iOS, HarmonyOS, Windows, macOS i Linux ({download}). Escanegeu el codi QR de sota per obtenir el vostre SPT i ja estareu llestos per enviar. Separeu diversos SPT amb una coma (,) — vegeu {docs} per a més informació.", + "wxPusherDownloadApp": "descarrega l'aplicació", + "wxPusherDocs": "guia oficial", + "SpugPush Template Code": "Codi de plantilla", + "wayToGetClickSendSMSToken": "Podeu obtenir el nom d'usuari de l'API i la clau de l'API des de {here}.", + "wayToGetEgoSMSToken": "Podeu obtenir el nom d'usuari i la contrasenya de l'API d'EgoSMS des de {here}.", + "egosmsSenderDescription": "ID del remitent mostrat als destinataris (màx. 11 caràcters). Per defecte EGOSMS si es deixa buit.", + "egosmsPhoneNumberDescription": "Número del destinatari amb el codi de país, sense + ni 00 (p. ex. 2567XXXXXXXX).", + "Hash Key": "Clau de hash", + "wayToGetBearSMSToken": "Podeu obtenir el nom d'usuari i la clau de hash de l'API de BearSMS des de {0}.", + "bearsmsPhoneNumberDescription": "Número del destinatari amb el codi de país, sense + ni 00 (p. ex. 9725XXXXXXXX).", + "bearsmsSenderDescription": "ID del remitent opcional mostrat als destinataris (màx. 11 caràcters). Ha d'estar aprovat al vostre compte de BearSMS, altrament s'utilitza el remitent predeterminat del compte.", + "Custom Monitor Type": "Tipus de monitor personalitzat", + "Google Analytics ID": "ID de Google Analytics", + "Analytics Type": "Tipus d'analítica", + "Analytics ID": "ID d'analítica", + "Analytics Script URL": "URL de l'script d'analítica", + "Edit Tag": "Edita l'etiqueta", + "WebSocket Options": "Opcions de WebSocket", + "Body Encoding": "Codificació del cos", + "API Keys": "Claus API", + "Expiry": "Caducitat", + "Don't expire": "No caduca", + "Add Another": "Afegeix-ne una altra", + "Key Added": "Clau afegida", + "apiKeyAddedMsg": "S'ha afegit la vostra clau API. Anoteu-la, ja que no es tornarà a mostrar.", + "Add API Key": "Afegeix una clau API", + "No API Keys": "Sense claus API", + "apiKey-active": "Activa", + "apiKey-expired": "Caducada", + "apiKey-inactive": "Inactiva", + "Expires": "Caduca", + "disableAPIKeyMsg": "Esteu segur que voleu desactivar aquesta clau API?", + "deleteAPIKeyMsg": "Esteu segur que voleu suprimir aquesta clau API?", + "pagertreeIntegrationUrl": "URL d'integració", + "pagertreeUrgency": "Urgència", + "pagertreeSilent": "Silenciós", + "pagertreeLow": "Baixa", + "pagertreeMedium": "Mitjana", + "pagertreeHigh": "Alta", + "pagertreeResolve": "Resolució automàtica", + "wayToGetPagerTreeIntegrationURL": "Després de crear la integració d'Uptime Kuma a PagerTree, copieu el punt final. Vegeu tots els detalls {0}", + "lunaseaUserID": "ID d'usuari", + "ntfyAuthenticationMethod": "Mètode d'autenticació", + "ntfyPriorityHelptextAllEvents": "Tots els esdeveniments s'envien amb la prioritat màxima", + "ntfyPriorityHelptextAllExceptDown": "Tots els esdeveniments s'envien amb aquesta prioritat, excepte els esdeveniments {0}, que tenen una prioritat de {1}", + "ntfyPriorityHelptextPriorityHigherThanDown": "La prioritat normal hauria de ser superior a la prioritat de {0}. La prioritat {1} és superior a la prioritat {0} {2}", + "ntfyPriorityDown": "Prioritat per als esdeveniments DOWN", + "ntfyUsernameAndPassword": "Nom d'usuari i contrasenya", + "ntfyCallHelptext": "Fa una trucada telefònica quan es dispara l'alerta. Establiu-ho a 'yes' per utilitzar el vostre primer número verificat, o introduïu un número de telèfon específic (p. ex. +12223334444). Requereix ntfy Pro i un número de telèfon verificat.", + "ntfyUseTemplate": "Personalitza les plantilles de notificació", + "ntfyUseTemplateDescription": "Activeu això per personalitzar els títols i missatges de les notificacions mitjançant plantilles LiquidJS", + "ntfyCustomTitle": "Plantilla de títol personalitzat", + "ntfyCustomMessage": "Plantilla de missatge personalitzat", + "ntfyNotificationTemplateFallback": "Deixeu-ho en blanc per utilitzar el format predeterminat d'Uptime Kuma", + "Device ID or Group ID": "ID de dispositiu o ID de grup", + "notifyAppDeviceIdDescription": "L'ID del dispositiu de l'aplicació Notify!, o un ID de grup per alertar tots els dispositius d'un grup alhora.", + "notifyAppSetup": "Obriu Notify! i cerqueu el vostre ID de dispositiu i testimoni a Configuració. No cal cap compte. Més informació a {0}.", + "telnyxApiKey": "Clau API (V2)", + "telnyxApiKeyHelptext": "La vostra clau API V2 de Telnyx del Portal de Telnyx a Claus API", + "telnyxPhoneNumber": "Número d'origen", + "telnyxPhoneNumberHelptext": "El vostre número de telèfon de Telnyx des del qual enviar (ha d'estar registrat al vostre compte de Telnyx, p. ex. +15551234567)", + "telnyxToNumber": "Número de destinació", + "telnyxToNumberHelptext": "El número de telèfon de destinació per rebre notificacions SMS (p. ex. +15559876543)", + "telnyxMessagingProfileId": "ID del perfil de missatgeria (opcional)", + "telnyxMessagingProfileIdHelptext": "Opcionalment, associeu un perfil de missatgeria de Telnyx per controlar la configuració i les funcions del remitent", + "plivoAuthID": "ID d'autenticació", + "plivoAuthIDHelptext": "El vostre ID d'autenticació de Plivo de la Consola de Plivo", + "plivoAuthToken": "Testimoni d'autenticació", + "plivoAuthTokenHelptext": "El vostre testimoni d'autenticació de Plivo de la Consola de Plivo", + "plivoFromNumber": "Número d'origen", + "plivoFromNumberHelptext": "El número de telèfon de Plivo des del qual enviar (p. ex. +15551234567). Els SMS també poden utilitzar un ID de remitent alfanumèric aprovat; les trucades de veu requereixen un número de telèfon de Plivo.", + "plivoToNumber": "Número de destinació", + "plivoToNumberHelptext": "El número de telèfon de destinació que rep l'SMS o la trucada de veu (p. ex. +15559876543)", + "plivoAnswerUrl": "URL de resposta", + "plivoAnswerUrlHelptext": "URL que retorna XML de Plivo per a la trucada. El text de l'alerta s'afegeix com a paràmetre de consulta ?message=, de manera que el vostre punt final pot retornar, per exemple, {message}.", + "twilioAccountSID": "SID del compte", + "twilioApiKey": "Clau API (opcional)", + "twilioApiKeyHelptext": "La clau API és opcional però recomanable. Podeu proporcionar el SID del compte i el testimoni d'autenticació de la pàgina de la Consola de Twilio, o el SID del compte i el parell de clau API i secret de clau API", + "twilioMessagingServiceSID": "SID del servei de missatgeria (opcional)", + "twilloMessagingServiceSIDHelptext": "Introduïu aquí el SID del vostre servei de missatgeria si utilitzeu {twillo_messaging_service_help_link} per gestionar remitents i funcions", + "twilioAuthToken": "Testimoni d'autenticació / secret de clau API", + "twilioFromNumber": "Número d'origen", + "twilioToNumber": "Número de destinació", + "Monitor Setting": "Configuració del monitor de {0}", + "Show Clickable Link": "Mostra un enllaç clicable", + "Show Clickable Link Description": "Si es marca, tothom qui tingui accés a aquesta pàgina d'estat podrà accedir a la URL del monitor.", + "Open Badge Link Generator": "Obre el generador d'enllaços de distintiu", + "Badge Link Generator": "Generador d'enllaços de distintiu de {0}", + "Badge Link Generator Helptext": "Els enllaços de distintiu estan disponibles per a tots els monitors assignats a pàgines d'estat públiques. Per a més informació, vegeu la {documentation}.", + "Badge Type": "Tipus de distintiu", + "Badge Duration (in hours)": "Durada del distintiu (en hores)", + "Badge Label": "Etiqueta del distintiu", + "Badge Prefix": "Prefix del valor del distintiu", + "Badge Suffix": "Sufix del valor del distintiu", + "Badge Label Color": "Color de l'etiqueta del distintiu", + "Badge Color": "Color del distintiu", + "Badge Label Prefix": "Prefix de l'etiqueta del distintiu", + "Badge Preview": "Vista prèvia del distintiu", + "Badge Label Suffix": "Sufix de l'etiqueta del distintiu", + "Badge Up Color": "Color del distintiu Actiu", + "Badge Down Color": "Color del distintiu Caigut", + "Badge Pending Color": "Color del distintiu Pendent", + "Badge Maintenance Color": "Color del distintiu Manteniment", + "Badge Warn Color": "Color del distintiu d'avís", + "Badge Warn Days": "Dies d'avís del distintiu", + "Badge Down Days": "Dies caigut del distintiu", + "Badge Style": "Estil del distintiu", + "Badge value (For Testing only.)": "Valor del distintiu (només per a proves)", + "Badge URL": "URL del distintiu", + "Group": "Grup", + "Monitor Group": "Grup de monitors", + "monitorToastMessagesLabel": "Notificacions emergents del monitor", + "monitorToastMessagesDescription": "Les notificacions emergents dels monitors desapareixen després d'un temps determinat en segons. Establiu-ho a -1 per desactivar el temps d'espera. Establiu-ho a 0 per desactivar les notificacions emergents.", + "toastErrorTimeout": "Temps d'espera per a notificacions d'error", + "toastSuccessTimeout": "Temps d'espera per a notificacions d'èxit", + "Kafka Brokers": "Brokers de Kafka", + "Enter the list of brokers": "Introduïu la llista de brokers", + "Press Enter to add broker": "Premeu Retorn per afegir un broker", + "Kafka Topic Name": "Nom del tema de Kafka", + "Kafka Producer Message": "Missatge del productor de Kafka", + "Enable Kafka SSL": "Activa SSL de Kafka", + "Enable Kafka Producer Auto Topic Creation": "Activa la creació automàtica de temes del productor de Kafka", + "Kafka SASL Options": "Opcions SASL de Kafka", + "Mechanism": "Mecanisme", + "Pick a SASL Mechanism...": "Trieu un mecanisme SASL…", + "Authorization Identity": "Identitat d'autorització", + "AccessKey Id": "ID d'AccessKey", + "Secret AccessKey": "AccessKey secreta", + "Session Token": "Testimoni de sessió", + "noGroupMonitorMsg": "No disponible. Primer creeu un monitor de grup.", + "Request Body": "Cos de la sol·licitud", + "HTTP Method": "Mètode HTTP", + "webhookPostMethodDesc": "POST és adequat per a la majoria de servidors HTTP moderns.", + "webhookGetMethodDesc": "GET envia les dades com a paràmetres de consulta i no permet configurar un cos. Útil per activar monitors Push d'Uptime Kuma.", + "wayToGetFlashDutyKey": "Per integrar Uptime Kuma amb Flashduty: aneu a Canals > Seleccioneu un canal > Integracions > Afegeix una nova integració, trieu Uptime Kuma i copieu la URL de push.", + "FlashDuty Severity": "Gravetat", + "FlashDuty Push URL": "URL de push", + "FlashDuty Push URL Placeholder": "Copieu-ho de la pàgina d'integració d'alertes", + "Flowtriq API Key": "Clau API", + "flowtriqApiKeyDescription": "Clau API opcional per autenticar les sol·licituds de webhook a la vostra instància de Flowtriq.", + "Pinglet Topic URL": "URL del tema", + "pingletTopicUrlDescription": "La URL de publicació del tema de Pinglet on enviar alertes, p. ex. https://app.pinglet.co.uk/your-namespace/alerts. El tema es crea a la primera publicació si encara no existeix.", + "nostrRelays": "Relés de Nostr", + "nostrRelaysHelp": "Una URL de relé per línia", + "nostrSender": "Clau privada del remitent (nsec)", + "nostrRecipients": "Claus públiques dels destinataris (npub)", + "nostrRecipientsHelp": "format npub, un per línia", + "showCertificateExpiry": "Mostra la caducitat del certificat", + "showOnlyLastHeartbeat": "Mostra només l'últim heartbeat", + "noOrBadCertificate": "Certificat inexistent o incorrecte", + "cacheBusterParam": "Afegeix el paràmetre {0}", + "cacheBusterParamDescription": "Paràmetre generat aleatòriament per evitar memòries cau.", + "gamedigToken": "Testimoni", + "gamedigTokenDescription": "Opcional. Alguns servidors de jocs requereixen un testimoni d'autenticació.", + "gamedigGuessPort": "Gamedig: endevina el port", + "gamedigGuessPortDescription": "El port utilitzat pel protocol de consulta de servidor de Valve pot ser diferent del port del client. Proveu-ho si el monitor no es pot connectar al vostre servidor.", + "Message format": "Format del missatge", + "Send rich messages": "Envia missatges enriquits", + "Bitrix24 Webhook URL": "URL del Webhook de Bitrix24", + "wayToGetBitrix24Webhook": "Podeu crear un webhook seguint els passos a {0}", + "bitrix24SupportUserID": "Introduïu el vostre ID d'usuari a Bitrix24. Podeu trobar l'ID a l'enllaç anant al perfil de l'usuari.", + "ClickUp API Token": "Testimoni de l'API de ClickUp", + "ClickUp API Token Description": "Podeu crear un testimoni d'accés personal a ClickUp anant a Configuració -> Integracions i ClickApps -> API de ClickUp -> Testimoni de l'API", + "ClickUp Workspace ID": "ID de l'espai de treball de ClickUp", + "ClickUp Workspace ID Description": "El podeu trobar a la URL del vostre espai de treball, just després d'app.clickup.com/, p. ex. app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ID del canal de ClickUp", + "ClickUp Channel ID Description": "El podeu trobar a la URL del canal de xat, just després de /chat/r/, p. ex. .../chat/r/abc123-4567.", + "Saved.": "Desat.", + "authUserInactiveOrDeleted": "L'usuari està inactiu o eliminat.", + "authInvalidToken": "Testimoni no vàlid.", + "authIncorrectCreds": "Nom d'usuari o contrasenya incorrectes.", + "2faAlreadyEnabled": "El 2FA ja està activat.", + "2faEnabled": "2FA activat.", + "2faDisabled": "2FA desactivat.", + "successAdded": "Afegit correctament.", + "successResumed": "Reprès correctament.", + "successPaused": "Pausat correctament.", + "successDeleted": "Suprimit correctament.", + "successEdited": "Editat correctament.", + "successAuthChangePassword": "La contrasenya s'ha actualitzat correctament.", + "successBackupRestored": "Còpia de seguretat restaurada correctament.", + "successDisabled": "Desactivat correctament.", + "successEnabled": "Activat correctament.", + "tagNotFound": "Etiqueta no trobada.", + "foundChromiumVersion": "S'ha trobat Chromium/Chrome. Versió: {0}", + "Remote Browsers": "Navegadors remots", + "Remote Browser": "Navegador remot", + "Add a Remote Browser": "Afegeix un navegador remot", + "Remote Browser not found!": "No s'ha trobat el navegador remot!", + "remoteBrowsersDescription": "Els navegadors remots són una alternativa a executar Chromium localment. Configureu-ho amb un servei com browserless.io o connecteu-vos al vostre propi", + "self-hosted container": "contenidor autoallotjat", + "remoteBrowserToggle": "Per defecte, Chromium s'executa dins del contenidor d'Uptime Kuma. Podeu utilitzar un navegador remot activant aquest interruptor.", + "useRemoteBrowser": "Utilitza un navegador remot", + "deleteRemoteBrowserMessage": "Esteu segur que voleu suprimir aquest navegador remot per a tots els monitors?", + "Screenshot Delay": "Retard de la captura de pantalla (espera {milliseconds})", + "milliseconds": "{n} mil·lisegon | {n} mil·lisegons", + "screenshotDelayDescription": "Opcionalment, espereu aquesta quantitat de mil·lisegons abans de fer la captura de pantalla. Màxim: {maxValueMs}ms (0,5 × interval).", + "screenshotDelayWarning": "Valors més alts mantenen el navegador obert més temps, cosa que pot augmentar l'ús de memòria amb molts monitors concurrents.", + "GrafanaOncallUrl": "URL de Grafana Oncall", + "systemService": "Servei del sistema", + "systemServiceName": "Nom del servei", + "systemServiceDescription": "Comprova si el servei del sistema {service_name} està actiu", + "systemServiceDescriptionLinux": "Comprova si el servei systemd de Linux {service_name} està actiu", + "systemServiceDescriptionWindows": "Comprova si el servei {service_name} del Gestor de serveis de Windows s'està executant", + "systemServiceCommandHint": "Ordre utilitzada: {command}", + "systemServiceExpectedOutput": "Sortida esperada: \"{0}\"", + "pm2Description": "Comprova si el procés PM2 {service_name} està en línia", + "pm2ExpectedStates": "Estat esperat: en línia. Els estats aturat i amb error es tracten com a CAIGUT.", + "Browser Screenshot": "Captura de pantalla del navegador", + "mongodbCommandDescription": "Executeu una ordre de MongoDB contra la base de dades. Per obtenir informació sobre les ordres disponibles, consulteu la {documentation}", + "wayToGetSevenIOApiKey": "Visiteu el tauler a app.seven.io > developer > api key > el botó verd d'afegir", + "senderSevenIO": "Número o nom de l'emissor", + "receiverSevenIO": "Número receptor", + "receiverInfoSevenIO": "Si el número receptor no es troba a Alemanya, heu d'afegir el codi de país davant del número (p. ex. per al codi de país 1 dels EUA, utilitzeu 117612121212 en lloc de 017612121212)", + "apiKeySevenIO": "Clau API de SevenIO", + "wayToWriteWhapiRecipient": "El número de telèfon amb el prefix internacional, però sense el signe de suma a l'inici ({0}), l'ID de contacte ({1}) o l'ID de grup ({2}).", + "wayToGetWhapiUrlAndToken": "Podeu obtenir la URL de l'API i el testimoni anant al canal desitjat des de {0}", + "whapiRecipient": "Número de telèfon / ID de contacte / ID de grup", + "API URL": "URL de l'API", + "wayToWriteEvolutionRecipient": "El número de telèfon amb el prefix internacional, però sense el signe de suma a l'inici ({0}), l'ID de contacte ({1}) o l'ID de grup ({2}).", + "wayToGetEvolutionUrlAndToken": "Podeu obtenir la URL de l'API i el testimoni anant al canal desitjat des de {0}", + "evolutionRecipient": "Número de telèfon / ID de contacte / ID de grup", + "evolutionInstanceName": "Nom de la instància", + "evolutionCustomMessageTitle": "Missatge personalitzat (opcional)", + "evolutionCustomMessageDesc": "Deixeu-ho buit per utilitzar el missatge predeterminat.", + "What is a Remote Browser?": "Què és un navegador remot?", + "wayToGetHeiiOnCallDetails": "Com obtenir l'ID de l'activador i les claus API s'explica a la {documentation}", + "documentationOf": "Documentació de {0}", + "callMeBotGet": "Aquí podeu generar un punt final per a {0}, {1} i {2}. Tingueu en compte que podríeu tenir un límit de sol·licituds. Els límits semblen ser: {3}", + "gtxMessagingApiKeyHint": "Podeu trobar la vostra clau API a: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Número de telèfon d'origen / Adreça d'origen de la ruta de transmissió (TPOA)", + "gtxMessagingFromHint": "Als telèfons mòbils, els destinataris veuen el TPOA mostrat com a remitent del missatge. Es permeten fins a 11 caràcters alfanumèrics, un codi curt, el codi llarg local o números internacionals ({e164}, {e212} o {e214})", + "gtxMessagingToHint": "Format internacional, amb \"+\" al davant ({e164}, {e212} o {e214})", + "Originator type": "Tipus d'originador", + "Alphanumeric (recommended)": "Alfanumèric (recomanat)", + "cellsyntOriginatortypeAlphanumeric": "Cadena alfanumèrica (màx. 11 caràcters alfanumèrics). Els destinataris no poden respondre al missatge.", + "cellsyntOriginatortypeNumeric": "Valor numèric (màx. 15 dígits) amb el número de telèfon en format internacional sense el 00 inicial (per exemple, el número del Regne Unit 07920 110 000 s'hauria d'establir com a 447920110000). Els destinataris poden respondre al missatge.", + "Originator": "Originador", + "cellsyntOriginator": "Visible al telèfon mòbil del destinatari com a originador del missatge. Els valors permesos i la funció depenen del paràmetre originatortype.", + "Destination": "Destinació", + "cellsyntDestination": "Número de telèfon del destinatari en format internacional amb el 00 inicial seguit del codi de país, p. ex. 00447920110000 per al número del Regne Unit 07920 110 000 (màx. 17 dígits en total). Màxim 25000 destinataris separats per comes per sol·licitud HTTP.", + "Allow Long SMS": "Permet SMS llargs", + "cellsyntSplitLongMessages": "Divideix els missatges llargs en fins a 6 parts. 153 x 6 = 918 caràcters.", + "max 15 digits": "màx. 15 dígits", + "max 11 alphanumeric characters": "màx. 11 caràcters alfanumèrics", + "Community String": "Cadena de comunitat", + "snmpCommunityStringHelptext": "Aquesta cadena funciona com una contrasenya per autenticar i controlar l'accés a dispositius habilitats amb SNMP. Feu-la coincidir amb la configuració del vostre dispositiu SNMP.", + "OID (Object Identifier)": "OID (identificador d'objecte)", + "snmpOIDHelptext": "Introduïu l'OID per al sensor o estat que voleu monitorar. Utilitzeu eines de gestió de xarxa com navegadors MIB o programari SNMP si no esteu segur de l'OID.", + "snmpV3Username": "Nom d'usuari SNMPv3", + "SNMP Version": "Versió SNMP", + "Please enter a valid OID.": "Introduïu un OID vàlid.", + "wayToGetThreemaGateway": "Us podeu registrar per a Threema Gateway {0}.", + "threemaRecipientType": "Tipus de destinatari", + "threemaRecipientTypeIdentity": "ID de Threema", + "threemaRecipientTypeIdentityFormat": "8 caràcters", + "threemaRecipientTypePhoneFormat": "E.164, sense el + inicial", + "threemaRecipientTypeEmail": "Adreça de correu", + "threemaSenderIdentity": "ID de passarel·la", + "threemaSenderIdentityFormat": "8 caràcters, normalment comença amb *", + "threemaApiAuthenticationSecret": "Secret de l'ID de passarel·la", + "threemaBasicModeInfo": "Nota: aquesta integració utilitza Threema Gateway en mode bàsic (xifratge basat en servidor). Podeu trobar més detalls a {0}.", + "apiKeysDisabledMsg": "Les claus API estan desactivades perquè l'autenticació està desactivada.", + "Host Onesender": "Amfitrió d'Onesender", + "Token Onesender": "Testimoni d'Onesender", + "Recipient Type": "Tipus de destinatari", + "Private Number": "Número privat", + "privateOnesenderDesc": "Assegureu-vos que el número de telèfon és vàlid. Per enviar un missatge a un número de telèfon privat, ex: 628123456789", + "groupOnesenderDesc": "Assegureu-vos que l'ID de grup és vàlid. Per enviar un missatge a un grup, ex: 628123456789-342345", + "Group ID": "ID de grup", + "wayToGetOnesenderUrlandToken": "Podeu obtenir la URL i el testimoni anant al lloc web d'Onesender. Més informació {0}", + "Add Remote Browser": "Afegeix un navegador remot", + "New Group": "Grup nou", + "OAuth2: Client Credentials": "OAuth2: credencials del client", + "Authentication Method": "Mètode d'autenticació", + "Authorization Header": "Capçalera d'autorització", + "Form Data Body": "Cos de dades de formulari", + "OAuth Token URL": "URL del testimoni OAuth", + "Client ID": "ID del client", + "Client Secret": "Secret del client", + "OAuth Scope": "Àmbit OAuth", + "OAuth Audience": "Audiència OAuth", + "Optional: The audience to request the JWT for": "Opcional: l'audiència per la qual sol·licitar el JWT", + "Optional: Space separated list of scopes": "Opcional: llista d'àmbits separats per espais", + "Go back to home page.": "Torna a la pàgina d'inici.", + "No tags found.": "No s'ha trobat cap etiqueta.", + "Lost connection to the socket server.": "S'ha perdut la connexió amb el servidor socket.", + "Cannot connect to the socket server.": "No es pot connectar al servidor socket.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "URL del Webhook de SIGNL4", + "signl4Docs": "Podeu trobar més informació sobre com configurar SIGNL4 i com obtenir la URL del webhook de SIGNL4 a {0}.", + "conditionDelete": "Elimina la condició", + "conditionAddGroup": "Afegeix un grup", + "conditionDeleteGroup": "Elimina el grup", + "not equals": "no és igual a", + "not contains": "no conté", + "not starts with": "no comença amb", + "not ends with": "no acaba amb", + "less than": "menor que", + "greater than": "major que", + "less than or equal to": "menor o igual a", + "greater than or equal to": "major o igual a", + "record": "registre", + "json_value": "valor JSON", + "Notification Channel": "Canal de notificació", + "Alphanumerical string and hyphens only": "Només cadena alfanumèrica i guions", + "Arcade": "Arcade", + "Harp": "Arpa", + "Bubble": "Bombolla", + "Doorbell": "Timbre", + "Flute": "Flauta", + "Scifi": "Ciència-ficció", + "Clear": "Neteja", + "Select All": "Selecciona-ho tot", + "Deselect All": "Desselecciona-ho tot", + "Custom sound to override default notification sound": "So personalitzat per substituir el so de notificació per defecte", + "Time Sensitive (iOS Only)": "Sensible al temps (només iOS)", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Les notificacions sensibles al temps es lliuraran immediatament, fins i tot si el dispositiu està en mode no molestar.", + "From": "Des de", + "Can be found on:": "Es pot trobar a: {0}", + "The phone number of the recipient in E.164 format.": "El número de telèfon del destinatari en format E.164.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Un ID de remitent de text o un número de telèfon en format E.164 si voleu poder rebre respostes.", + "RabbitMQ Nodes": "Nodes de gestió de RabbitMQ", + "Enter the list of nodes": "Introduïu la llista de nodes de gestió de RabbitMQ", + "Press Enter to add node": "Premeu Retorn per afegir un node", + "rabbitmqNodesDescription": "Introduïu la URL dels nodes de gestió de RabbitMQ, incloent-hi el protocol i el port. Exemple: {0}", + "rabbitmqNodesRequired": "Establiu els nodes per a aquest monitor.", + "rabbitmqNodesInvalid": "Utilitzeu una URL completa (que comenci amb 'http') per als nodes de RabbitMQ.", + "RabbitMQ Username": "Nom d'usuari de RabbitMQ", + "RabbitMQ Password": "Contrasenya de RabbitMQ", + "rabbitmqHelpText": "Per utilitzar el monitor, haureu d'activar el connector de gestió a la vostra configuració de RabbitMQ. Per a més informació, consulteu la {rabitmq_documentation}.", + "SendGrid API Key": "Clau API de SendGrid", + "turbosmtpConsumerKey": "Clau de consumidor", + "turbosmtpConsumerSecret": "Secret de consumidor", + "Separate multiple email addresses with commas": "Separeu diverses adreces de correu amb comes", + "brevoApiKey": "Clau API de Brevo", + "brevoApiHelp": "Creeu una clau API aquí: {0}", + "brevoFromName": "Nom del remitent", + "brevoLeaveBlankForDefaultName": "deixeu-ho en blanc per al nom predeterminat", + "brevoCcEmail": "Correu CC", + "brevoBccEmail": "Correu CCO", + "brevoSeparateMultipleEmails": "Separeu diverses adreces de correu amb comes", + "brevoSubject": "Assumpte", + "resendApiKey": "Clau API de Resend", + "resendApiHelp": "Creeu una clau API aquí {0}", + "resendFromName": "Nom del remitent", + "resendLeaveBlankForDefaultName": "deixeu-ho en blanc per al nom predeterminat", + "resendLeaveBlankForDefaultSubject": "Deixeu-ho en blanc per a l'assumpte predeterminat", + "resendSubject": "Assumpte", + "pingCountLabel": "Màxim de paquets", + "pingCountDescription": "Nombre de paquets a enviar abans d'aturar-se", + "pingNumericLabel": "Sortida numèrica", + "pingNumericDescription": "Si es marca, es mostraran adreces IP en lloc de noms d'amfitrió simbòlics", + "pingGlobalTimeoutLabel": "Temps d'espera global", + "pingGlobalTimeoutDescription": "Temps total en segons abans que el ping s'aturi, independentment dels paquets enviats", + "pingPerRequestTimeoutLabel": "Temps d'espera per ping", + "pingPerRequestTimeoutDescription": "Aquest és el temps màxim d'espera (en segons) abans de considerar perdut un sol paquet de ping", + "pingIntervalAdjustedInfo": "Interval ajustat en funció del nombre de paquets, el temps d'espera global i el temps d'espera per ping", + "smtpHelpText": "'SMTPS' comprova que SMTP/TLS funciona; 'Ignora TLS' es connecta en text pla; 'STARTTLS' es connecta, emet una ordre STARTTLS i verifica el certificat del servidor. Cap d'aquestes opcions envia un correu electrònic.", + "customUrlDescription": "S'utilitzarà com a URL clicable en lloc de la del monitor.", + "OneChatAccessToken": "Testimoni d'accés de OneChat", + "OneChatUserIdOrGroupId": "ID d'usuari o de grup de OneChat", + "OneChatBotId": "ID de bot de OneChat", + "wahaChatId": "ID de xat (número de telèfon / ID de contacte / ID de grup)", + "wayToGetWahaApiUrl": "La URL de la vostra instància WAHA.", + "wayToGetWahaApiKey": "La clau API és el valor de la variable d'entorn WHATSAPP_API_KEY que heu utilitzat per executar WAHA.", + "wayToGetWahaSession": "Des d'aquesta sessió, WAHA envia notificacions a l'ID de xat. La podeu trobar al tauler de WAHA.", + "wayToWriteWahaChatId": "El número de telèfon amb el prefix internacional, però sense el signe de suma a l'inici ({0}), l'ID de contacte ({1}) o l'ID de grup ({2}). Les notificacions s'envien a aquest ID de xat des de la sessió WAHA.", + "openwaChatId": "ID de xat (número de telèfon / ID de contacte / ID de grup)", + "wayToGetOpenwaApiUrl": "La URL de la vostra instància OpenWA.", + "wayToGetOpenwaApiKey": "Clau API utilitzada per a la vostra instància OpenWA.", + "wayToGetOpenwaSession": "ID de sessió utilitzat per OpenWA per enviar notificacions.", + "wayToWriteOpenwaChatId": "El número de telèfon amb el prefix internacional, però sense el signe de suma a l'inici ({0}), l'ID de contacte ({1}) o l'ID de grup ({2}). Podeu enviar a diversos ID de xat separant-los amb comes.", + "openwaCustomMessageTitle": "Missatge personalitzat (opcional)", + "openwaCustomMessageDesc": "Deixeu-ho buit per utilitzar el missatge predeterminat.", + "360messengerAuthToken": "Clau API de 360messenger", + "360messengerRecipient": "Número(s) de telèfon del destinatari", + "360messengerGroupId": "ID de grup de 360messenger", + "360messengerUseTemplate": "Utilitza una plantilla de missatge personalitzada", + "360messengerTemplate": "Plantilla de missatge de 360messenger", + "360messengerGroupList": "Grups de WhatsApp", + "360messengerSelectGroupList": "Seleccioneu un grup per afegir", + "360messengerSelectedGroupID": "ID(s) de grup seleccionats", + "360messengerEnableSendToGroup": "Activa l'enviament a grup(s) de WhatsApp", + "360messengerCustomMessageTemplate": "Plantilla de missatge personalitzada", + "360messengerEnableCustomMessage": "Activa una plantilla de missatge personalitzada en lloc del missatge predeterminat.", + "360messengerWayToGetUrlAndToken": "Podeu obtenir la vostra clau API de 360messenger a {0}.", + "360messengerWayToWriteRecipient": "Introduïu un o més números de telèfon en format internacional sense el signe de suma inicial (p. ex. {0}). Separeu diversos números amb comes.", + "360messengerErrorNoApiKey": "Introduïu primer la vostra clau API de 360messenger.", + "360messengerErrorNoGroups": "No s'ha trobat cap grup de WhatsApp per a aquest compte.", + "360messengerErrorApi": "No s'ha pogut carregar la llista de grups de WhatsApp (Error {statusCode}: {message}).", + "360messengerErrorGeneric": "No s'ha pogut carregar la llista de grups de WhatsApp: {message}", + "YZJ Webhook URL": "URL del Webhook de YZJ", + "YZJ Robot Token": "Testimoni del robot de YZJ", + "Plain Text": "Text pla", + "Message Template": "Plantilla de missatge", + "Template Format": "Format de la plantilla", + "Font Twemoji by Twitter licensed under": "Tipus de lletra Twemoji de Twitter amb llicència", + "smsplanetApiToken": "Testimoni per a l'API de SMSPlanet", + "smsplanetApiDocs": "Podeu trobar informació detallada sobre com obtenir testimonis d'API a {the_smsplanet_documentation}.", + "the smsplanet documentation": "la documentació de smsplanet", + "Phone numbers": "Números de telèfon", + "smsplanetNeedToApproveName": "Cal aprovar-lo al panell del client", + "Disable URL in Notification": "Desactiva la URL a la notificació", + "Suppress Notifications": "Suprimeix les notificacions", + "discordSuppressNotificationsHelptext": "Quan s'activa, els missatges es publicaran al canal però no activaran notificacions push o d'escriptori per als destinataris.", + "discordMessageFormat": "Format del missatge", + "discordMessageFormatNormal": "Normal (incrustacions riques)", + "discordMessageFormatMinimalist": "Minimalista (estat curt)", + "discordMessageFormatCustom": "Plantilla personalitzada", + "discordUseMessageTemplateDescription": "Si s'activa, el missatge s'enviarà utilitzant una plantilla personalitzada (LiquidJS). Deixeu-ho en blanc per utilitzar el format predeterminat d'Uptime Kuma.", + "discordMessageTemplate": "Plantilla de missatge", + "fluxerMessageFormat": "Format del missatge", + "fluxerMessageFormatNormal": "Normal (incrustacions riques)", + "fluxerMessageFormatMinimalist": "Minimalista (estat curt)", + "fluxerMessageFormatCustom": "Plantilla personalitzada", + "fluxerUseMessageTemplateDescription": "Si s'activa, el missatge s'enviarà utilitzant una plantilla personalitzada (LiquidJS). Deixeu-ho en blanc per utilitzar el format predeterminat d'Uptime Kuma.", + "fluxerMessageTemplate": "Plantilla de missatge", + "Fluxer Webhook URL": "URL del Webhook de Fluxer", + "wayToGetFluxerURL": "Podeu obtenir-ho anant a la configuració del canal de destinació > Webhooks > Crea un Webhook > Copia la URL del Webhook.", + "Ip Family": "Família d'IP", + "ipFamilyDescriptionAutoSelect": "Utilitza el {happyEyeballs} per determinar la família d'IP.", + "Happy Eyeballs algorithm": "Algorisme Happy Eyeballs", + "Add Another Tag": "Afegeix una altra etiqueta", + "Staged Tags for Batch Add": "Etiquetes preparades per a l'addició en lot", + "Clear Form": "Neteja el formulari", + "pause": "Pausa", + "Nextcloud host": "Amfitrió de Nextcloud", + "Conversation token": "Testimoni de conversa", + "Bot secret": "Secret del bot", + "Send UP silently": "Envia ACTIU en silenci", + "Send DOWN silently": "Envia CAIGUT en silenci", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Instal·lar un bot de Nextcloud Talk requereix accés administratiu al servidor.", + "Globalping - Access global monitoring probes": "Globalping - Accedeix a sondes de monitoratge globals", + "GlobalpingMonitorDescription": "Globalping proporciona accés a milers de sondes allotjades per la comunitat per executar proves i mesuraments de xarxa. S'estableix un límit de 250 proves per hora per a tots els usuaris anònims. Per duplicar el límit a 500 per hora, deseu el vostre testimoni a {accountSettings}. Consulteu {docs} per a més informació.", + "Globalping API Token": "Testimoni de l'API de Globalping", + "globalpingApiTokenDescription": "Obteniu el vostre testimoni de l'API de Globalping a {0}.", + "GlobalpingHostname": "Un objectiu de mesurament accessible públicament. Normalment un nom d'amfitrió o adreça IPv4/IPv6, depenent del tipus de mesurament.", + "GlobalpingLocationDescription": "El camp d'ubicació accepta continents, països, regions, ciutats, ASN, ISP o regions de núvol. Podeu combinar filtres amb {plus} (p. ex. {amazonPlusGermany} o {comcastPlusCalifornia}). Si la latència és una mètrica important, utilitzeu filtres per limitar la ubicació a una regió petita per evitar pics i, per a més estabilitat, establiu el filtre {datacenter}. {fullDocs}.", + "GlobalpingLocationDocs": "Documentació completa de l'entrada d'ubicació", + "GlobalpingMultipleLocationsError": "No s'admeten diverses ubicacions, utilitzeu una única ubicació per a cada monitor.", + "GlobalpingIpFamilyInfo": "La versió d'IP a utilitzar. Només es permet si l'objectiu és un nom d'amfitrió.", + "GlobalpingResolverInfo": "Adreça IPv4/IPv6 o un nom de domini totalment qualificat (FQDN). Per defecte, s'utilitza el resolutor de xarxa local de la sonda. Podeu canviar el servidor de resolució en qualsevol moment.", + "RecordMatch": "Coincidència del valor del registre", + "RegexMatch": "Introduïu una expressió regular per fer coincidir el valor del registre", + "account settings": "configuració del compte", + "Monitor Subtype": "Subtipus de monitor", + "Check for": "Comprova", + "Number of retry attempts if webhook fails": "Nombre d'intents de reintent (cada 60-180 segons) si el webhook falla.", + "Maximum Retries": "Reintents màxims", + "Template ID": "ID de plantilla", + "wayToGetClickSMSIRTemplateID": "La vostra plantilla ha de contenir un camp {uptkumaalert}. Podeu crear una nova plantilla {here}.", + "Recipient Numbers": "Números de destinatari", + "Notifications Enabled": "Notificacions activades", + "Allow Notifications": "Permet notificacions", + "Browser not supported": "Navegador no compatible", + "Unable to get permission to notify": "No s'ha pogut obtenir permís per notificar (sol·licitud denegada o ignorada).", + "Webpush Helptext": "Les notificacions push web només funcionen amb connexions SSL (HTTPS). Per a dispositius iOS, la pàgina web s'ha d'afegir prèviament a la pantalla d'inici.", + "settingsDomainExpiry": "Caducitat del domini", + "labelDomainExpiry": "Cad. del domini", + "domainExpiryNotificationHelp": "El nombre de dies d'antelació es pot configurar a Configuració.", + "domainExpiryDescription": "Activa la notificació quan el nom de domini caduqui en:", + "domain_expiry_unsupported_monitor_type": "El monitoratge de caducitat de domini no és compatible amb aquest tipus de monitor", + "domain_expiry_unsupported_missing_target": "No hi ha cap domini o nom d'amfitrió vàlid configurat per a aquest monitor", + "domain_expiry_unsupported_is_icann": "El domini \"{domain}\" no és candidat per al monitoratge de caducitat, perquè el seu sufix públic \".{publicSuffix}\" no està gestionat per ICANN", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "El monitoratge de caducitat de domini no està disponible per a \".{publicSuffix}\" perquè IANA no llista cap servei RDAP", + "minimumIntervalWarning": "Intervals inferiors a 20 segons poden provocar un rendiment deficient.", + "lowIntervalWarning": "Esteu segur que voleu establir el valor de l'interval per sota de 20 segons? El rendiment es pot degradar, especialment si hi ha un gran nombre de monitors.", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "URL del Webhook de Halo PSA", + "halopsa_webhook_url_desc": "Introduïu la URL del webhook del vostre Integration Runbook de Halo PSA (Configuration > Integrations > Custom Integrations > Integration Runbooks). Seleccioneu 'Can only be started from Halo and from a public endpoint' en crear el webhook.", + "username": "nom d'usuari", + "halopsa_username_desc": "Nom d'usuari per autenticar-se amb el webhook de Halo PSA", + "halopsa_password_desc": "Contrasenya per autenticar-se amb el webhook de Halo PSA", + "imageResetConfirmation": "Imatge restablerta als valors predeterminats", + "screenshot of the website": "captura de pantalla del lloc web", + "Basic checkbox toggle button group": "Grup de botons de casella de selecció bàsic", + "Basic radio toggle button group": "Grup de botons de ràdio bàsic", + "mtls-auth-server-cert-label": "Certificat", + "mtls-auth-server-cert-placeholder": "Cos del certificat", + "mtls-auth-server-key-placeholder": "Cos de la clau", + "mtls-auth-server-ca-placeholder": "CA del servidor", + "avgPing": "Ping mitjà", + "Uptime Kuma": "Uptime Kuma", + "maxPing": "Ping màxim", + "minPing": "Ping mínim", + "Setup Instructions": "Instruccions de configuració", + "halopsa_setup_step1": "Creeu un Integration Runbook a HaloPSA (Configuration → Integrations → Integration Runbooks)", + "halopsa_setup_step2": "Configureu les accions del runbook per processar alertes (p. ex., Crea un tiquet)", + "halopsa_setup_step3": "Copieu la URL del webhook i enganxeu-la al camp de text de dalt", + "halopsa_setup_step4": "Trieu Autenticació bàsica i creeu un nom d'usuari i una contrasenya. I escriviu o enganxeu aquest nom d'usuari i contrasenya als camps de dalt", + "Clear current filters": "Neteja els filtres actuals", + "Sort options": "Opcions d'ordenació", + "Sort by uptime": "Ordena per temps actiu", + "Sort by certificate expiry": "Ordena per caducitat del certificat", + "Splunk Rest URL": "URL REST de Splunk", + "Severity": "Gravetat", + "Message Format": "Format del missatge", + "smscTranslit": "smscTranslit", + "PushDeer Server URL": "URL del servidor PushDeer", + "To Number": "Número de destinació", + "GrafanaOncallURL": "URL de Grafana Oncall", + "PM2 Process": "Procés PM2", + "Refresh": "Actualitza", + "Select PM2 process": "Selecciona el procés PM2", + "playground": "espai de proves", + "Check Type": "Tipus de comprovació", + "Service Name": "Nom del servei", + "GRPC Options": "Opcions GRPC", + "End": "Final", + "Show this Maintenance Message on which Status Pages": "Mostra aquest missatge de manteniment a quines pàgines d'estat", + "Endpoint": "Punt final", + "Details": "Detalls", + "passwordTooWeak": "La contrasenya és massa feble. Hauria de contenir caràcters alfabètics i numèrics. Ha de tenir com a mínim 6 caràcters de longitud.", + "TLS Alerts": "Alertes TLS", + "Expected TLS Alert": "Alerta TLS esperada", + "None (Successful Connection)": "Cap (connexió correcta)", + "expectedTlsAlertDescription": "Seleccioneu l'alerta TLS que espereu que retorni el servidor. Utilitzeu {code} per verificar que els punts finals mTLS rebutgen connexions sense certificats de client. Vegeu {link} per a més detalls.", + "TLS Alert Spec": "RFC 8446", + "mariadbSocketPathDetectedHelptext": "Connectant a la base de dades tal com s'especifica mitjançant la variable d'entorn {0}.", + "Expand All Groups": "Expandeix tots els grups", + "Collapse All Groups": "Redueix tots els grups", + "Webhook Payload Fields": "Camps de dades del Webhook", + "halopsa_payload_desc": "Els següents camps s'envien al vostre webhook de Halo PSA:", + "halopsa_field_title": "Títol de l'alerta (sempre 'Uptime Kuma Alert')", + "halopsa_field_status": "Estat del monitor: UP, DOWN, NOTIFICATION, o UNKNOWN", + "halopsa_field_monitor": "Nom del monitor", + "halopsa_field_monitor_id": "Identificador únic del monitor (null per a notificacions de prova) - Utilitzeu-lo per fer coincidir alertes amb tiquets", + "halopsa_field_message": "Missatge complet de l'alerta amb estat i detalls", + "halopsa_field_timestamp": "Marca de temps de l'esdeveniment en format ISO 8601", + "halopsa_field_uptime_kuma_version": "Número de versió d'Uptime Kuma", + "halopsa_id_usage_hint": "💡 Consell: utilitzeu monitor_id per fer coincidir alertes amb tiquets de manera fiable, i heartbeat_id per fer seguiment de l'historial d'esdeveniments", + "halopsa_setup_step5": "Configureu el runbook per utilitzar monitor_id per fer coincidir alertes amb tiquets existents", + "Teltonika SMS Gateway": "Passarel·la SMS de Teltonika", + "teltonikaVersionWarning": "Aquest proveïdor de notificacions requereix que el vostre dispositiu Teltonika executi la versió RMS 7.14.0 o superior.", + "teltonikaUrl": "URL del vostre dispositiu Teltonika", + "teltonikaUrlHelptext": "La URL s'ha d'especificar com a origen complet, p. ex. {0}, o {1}.", + "teltonikaUnsafeTls": "Ignora la validació del certificat", + "teltonikaUnsafeTlsDescription": "Desactivar la validació del certificat TLS us exposa a atacs de camí (home-en-el-mig), que poden provocar fuites de dades i la presa de control dels sistemes. No desactiveu la validació del certificat llevat que accepteu aquest vector d'atac. Us recomanem utilitzar LetsEncrypt amb renovació automàtica.", + "teltonikaUsername": "Nom d'usuari de l'API", + "teltonikaUsernameHelptext": "Recomanació: creeu un compte separat restringit únicament a l'enviament de missatges SMS i introduïu-hi el seu nom d'usuari", + "teltonikaPassword": "Contrasenya de l'API", + "teltonikaPasswordHelptext": "Podeu definir la contrasenya de l'usuari de l'API al vostre router Teltonika, p. ex. {0}", + "teltonikaModem": "ID del mòdem", + "teltonikaModemHelptext": "L'ID del mòdem SMS ha de tenir el format {0}. Consulteu https://developers.teltonika-networks.com/reference/ per a més orientació.", + "teltonikaPhoneNumberHelptext": "El número ha d'estar en format internacional {0}, {1}. Només es permet un número.", + "ntpThresholdsTitle": "Llindars NTP", + "ntpStratumThreshold": "Llindar d'estrat", + "ntpStratumThresholdHelp": "Marca com a caigut si l'estrat és igual o superior a aquest valor. L'estrat indica la qualitat de la font horària (1=GPS/rellotge atòmic, 16=no sincronitzat).", + "ntpOffsetThreshold": "Llindar de desfasament horari (ms)", + "ntpOffsetThresholdHelp": "Marca com a caigut si el desfasament horari supera aquest valor en mil·lisegons.", + "ntpDispersionThreshold": "Llindar de dispersió arrel (ms)", + "ntpDispersionThresholdHelp": "Marca com a caigut si la dispersió arrel supera aquest valor. La dispersió indica la precisió del rellotge del servidor.", + "VKTeams Bot Token Description": "Podeu esbrinar com obtenir un testimoni de bot i altres detalls a {0}.", + "VKTeams Base Url Description": "URL base de l'API de VKTeams, pot variar en la solució local. Per defecte: https://myteam.mail.ru", + "VKTeams Chat Id Description": "Per als usuaris, és la seva adreça de correu electrònic. Per a grups i canals, obteniu l'ID de la seva configuració i afegiu-hi {'@'}chat.agent", + "VKTeams Template Format Description": "Per a l'estil del missatge, VKTeams admet text pla, MarkdownV2 i format HTML.", + "milkyHttpAddress": "Adreça HTTP de Milky (sense el sufix '/api')", + "milkyMessageType": "Tipus de missatge de Milky", + "milkyGroupMessage": "Grup", + "milkyPrivateMessage": "Privat", + "milkyUserOrGroupId": "ID de grup/usuari", + "milkySafetyTips": "Per seguretat, cal establir un testimoni d'accés", + "smsgatewayUrlHelptext": "La URL base de la vostra passarel·la SMS, sense un camí d'API.", + "smsgatewayTo": "Número(s) de telèfon", + "smsgatewayToHelptext": "Llista separada per comes de números de telèfon dels destinataris en format internacional (p. ex. +15551234567)", + "Client Key": "Clau del client", + "Channel": "Canal", + "SFTP Path to Check": "Camí SFTP a comprovar", + "sftpPathHelpText": "Opcional. Si s'especifica, el monitor verificarà que aquest camí existeix al servidor SFTP.", + "SSH Private Key": "Clau privada SSH", + "Passphrase": "Contrasenya", + "sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----", + "sshPrivateKeyHelpText": "Enganxa el contingut del fitxer de clau privada (p. ex. id_rsa o id_ed25519).", + "sshPassphraseHelpText": "Opcional. Només es requereix si la vostra clau privada està encriptada amb una contrasenya.", + "optional": "opcional", + "Line Number": "Número de línia", + "Amoot SMS recipient numbers help": "Introduïu un o més números mòbils separats per comes.", + "Use Pattern": "Usa el patró", + "Pattern Code ID": "ID del codi del patró", + "Amoot SMS pattern help": "Introduïu l'ID del codi del patró del tauler d'Amoot SMS. {0}", + "Create an Amoot SMS pattern": "Crea un patró d'Amoot SMS", + "Use Own Line for Pattern": "Usa una línia pròpia per al patró", + "Indigo URL": "URL d'Indigo", + "indigoUrlHelptext": "La vostra adreça de Reflector Indigo (https://NAME.indigodomo.net) o el Servidor Web Indigo a la vostra xarxa (https://IP:8176).", + "indigoIgnoreTlsErrorHelptext": "Es necessita per al Servidor Web Indigo de la vostra xarxa, que utilitza un certificat autosignat. No és necessari per al reflector.", + "indigoApiKeyHelptext": "Una clau API del vostre compte Indigo (Autoritzacions), o un secret local per al servidor web a la vostra xarxa.", + "Variable ID": "ID variable", + "indigoVariableIdHelptext": "El missatge d'alerta s'escriu a aquesta variable.", + "Action Group ID": "ID del grup d'acció", + "indigoActionGroupIdHelptext": "S'executa després que s'actualitzi la variable. Estableix una variable, un grup d'acció o tots dos. Copia els ID d'Indigo amb el clic dret . Copia l'ID." +} diff --git a/src/lang/ca@valencia.json b/src/lang/ca@valencia.json new file mode 100644 index 0000000..519f5bc --- /dev/null +++ b/src/lang/ca@valencia.json @@ -0,0 +1,12 @@ +{ + "settingUpDatabaseMSG": "Configurant la base de dades. Pot trigar una mica, si us plau, espereu.", + "dbName": "Nom de la Base de Dades", + "Settings": "Configuració", + "setupDatabaseChooseDatabase": "Quina base de dades vols fer servir?", + "setupDatabaseEmbeddedMariaDB": "No cal configurar rés. Aquesta imatge Docker té MariaDB inclosa. Uptime Kuma s'hi connectarà amb un socket Unix.", + "setupDatabaseMariaDB": "Connectar a una base de dades MariaDB externa. Cal configurar la informació de la connexió.", + "setupDatabaseSQLite": "Es recomana un sistema senzill de base de dades per desenvolupaments petits. Abans de la versió 2.0.0 es feia servir SQLite per defecte.", + "Dashboard": "Penell de control", + "Help": "Ajuda", + "New Update": "Nova actualització" +} diff --git a/src/lang/ckb.json b/src/lang/ckb.json new file mode 100644 index 0000000..9ed5cff --- /dev/null +++ b/src/lang/ckb.json @@ -0,0 +1,46 @@ +{ + "languageName": "کوردی", + "Settings": "ڕێکخستنەکان", + "Help": "یارمەتی", + "New Update": "وەشانی نوێ", + "Language": "زمان", + "Appearance": "ڕووکار", + "Theme": "شێوەی ڕووکار", + "General": "گشتی", + "Game": "یاری", + "Version": "وەشان", + "Check Update On GitHub": "سەیری وەشانی نوێ بکە لە Github", + "List": "لیست", + "Add": "زیادکردن", + "Quick Stats": "ئاماری خێرا", + "Up": "سەروو", + "Down": "خواروو", + "Pending": "هەڵپەسێردراو", + "statusMaintenance": "چاکردنەوە", + "Maintenance": "چاکردنەوە", + "Unknown": "نەزانراو", + "Passive Monitor Type": "جۆری مۆنیتەری پاسیڤ", + "Specific Monitor Type": "جۆری مۆنیتەری تایبەت", + "markdownSupported": "ڕستەسازی مارکداون پشتگیری دەکرێت", + "pauseDashboardHome": "وچان", + "Pause": "وچان", + "Name": "ناو", + "Status": "دۆخ", + "Message": "پەیام", + "No important events": "هیچ ڕووداوێکی گرنگ نییە", + "Resume": "‬دەستپێکردنەوە", + "Edit": "بژارکردن", + "Delete": "سڕینەوە", + "Uptime": "کاتی کارکردن", + "Cert Exp.": "بەسەرچوونی بڕوانامەی SSL.", + "day": "ڕۆژ | ڕۆژەکان", + "-day": "-ڕۆژ", + "hour": "کاتژمێر", + "Dashboard": "داشبۆرد", + "Primary Base URL": "بەستەری بنچینەیی سەرەکی", + "Add New Monitor": "مۆنیتەرێکی نوێ زیاد بکە", + "General Monitor Type": "جۆری مۆنیتەری گشتی", + "DateTime": "رێکەوت", + "Current": "هەنووکە", + "Monitor": "مۆنیتەر | مۆنیتەرەکان" +} diff --git a/src/lang/cs-CZ.json b/src/lang/cs-CZ.json new file mode 100644 index 0000000..e65e1a7 --- /dev/null +++ b/src/lang/cs-CZ.json @@ -0,0 +1,1719 @@ +{ + "languageName": "Čeština", + "checkEverySecond": "Kontrolovat každých {0} sekund", + "retryCheckEverySecond": "Opakovat každých {0} sekund", + "resendEveryXTimes": "Znovu zaslat {0}krát", + "resendDisabled": "Opakované zasílání je vypnuté", + "retriesDescription": "Maximální počet pokusů před označením služby jako nedostupné a odesláním oznámení", + "ignoreTLSError": "Ignorovat TLS/SSL chyby na HTTPS stránkách", + "upsideDownModeDescription": "Pomocí této možnosti změníte způsob vyhodnocování stavu. Pokud je služba dosažitelná, je NEDOSTUPNÁ.", + "maxRedirectDescription": "Maximální počet přesměrování, která se mají následovat. Nastavením hodnoty 0 zakážete přesměrování.", + "enableGRPCTls": "Umožnit odeslání gRPC žádosti během TLS spojení", + "grpcMethodDescription": "Název metody se převede do camelCase formátu jako je sayHello, check, aj.", + "acceptedStatusCodesDescription": "Vyberte stavové kódy, které jsou považovány za úspěšnou odpověď.", + "Maintenance": "Údržba", + "statusMaintenance": "V údržbě", + "Schedule maintenance": "Naplánovat údržbu", + "Affected Monitors": "Dotčené dohledy", + "Pick Affected Monitors...": "Vyberte dotčené dohledy…", + "Start of maintenance": "Zahájit údržbu", + "All Status Pages": "Všechny stavové stránky", + "Select status pages...": "Vyberte stavové stránky…", + "recurringIntervalMessage": "Spustit jednou každý den | Spustit jednou každých {0} dní", + "affectedMonitorsDescription": "Vyberte dohledy, které budou ovlivněny touto údržbou", + "affectedStatusPages": "Zobrazit tuto zprávu o údržbě na vybraných stavových stránkách", + "atLeastOneMonitor": "Vyberte alespoň jeden dotčený dohled", + "passwordNotMatchMsg": "Hesla se neshodují.", + "notificationDescription": "Aby oznámení fungovala, je nutné jej přiřadit k dohledu.", + "keywordDescription": "Vyhledat klíčové slovo v prosté odpovědi HTML nebo JSON. Při hledání se rozlišuje velikost písmen.", + "pauseDashboardHome": "Pauza", + "deleteMonitorMsg": "Opravdu chcete odstranit tento dohled?", + "deleteMaintenanceMsg": "Opravdu chcete odstranit tuto údržbu?", + "deleteNotificationMsg": "Opravdu chcete odstranit toto oznámení pro všechny dohledy?", + "dnsPortDescription": "Port DNS serveru. Standardně běží na portu 53. V případě potřeby jej můžete kdykoli změnit.", + "resolverserverDescription": "Cloudflare je výchozí server. V případě potřeby můžete zadat seznam IP adres nebo názvů hostitelů oddělených čárkami.", + "rrtypeDescription": "Vyberte typ záznamu o prostředku, který chcete monitorovat", + "pauseMonitorMsg": "Opravdu chcete dohled pozastavit?", + "enableDefaultNotificationDescription": "Toto oznámení bude standardně aktivní pro nové dohledy. V případě potřeby můžete oznámení stále zakázat na úrovni jednotlivých dohledů.", + "clearEventsMsg": "Opravdu chcete odstranit všechny události tohoto dohledu? Tím dojde k trvalému odstranění souvisejících dat zpráv a tento úkon nelze vrátit zpět.", + "clearHeartbeatsMsg": "Opravdu chcete smazat všechny záznamy životních signálů z tohoto dohledu? Tímto krokem dojde k trvalému odstranění všech údajů o životních signálech a nelze jej vrátit zpět.", + "confirmClearStatisticsMsg": "Opravdu chcete smazat VŠECHNY statistiky?", + "importHandleDescription": "Možnost 'Přeskočit existující' vyberte v případě, že chcete přeskočit všechny dohledy nebo oznámení se stejným názvem. Vybráním možnosti 'Přepsat' dojde k odstranění všech existujících dohledů a oznámení.", + "confirmImportMsg": "Opravdu chcete importovat zálohu? Prosím ověřte, zda jste vybrali správnou možnost importu.", + "twoFAVerifyLabel": "Prosím, zadejte svůj token pro ověření 2FA:", + "tokenValidSettingsMsg": "Token je platný! Nyní můžete uložit nastavení 2FA.", + "confirmEnableTwoFAMsg": "Opravdu chcete zapnout 2FA?", + "confirmDisableTwoFAMsg": "Opravdu chcete deaktivovat 2FA?", + "Settings": "Nastavení", + "Dashboard": "Nástěnka", + "New Update": "Nová aktualizace", + "Language": "Jazyk", + "Appearance": "Vzhled", + "Theme": "Motiv", + "General": "Obecné", + "Primary Base URL": "Primární URL adresa", + "Version": "Verze", + "Check Update On GitHub": "Zkontrolovat aktualizace na GitHubu", + "List": "Seznam", + "Add": "Přidat", + "Add New Monitor": "Přidat nový dohled", + "Quick Stats": "Rychlý přehled", + "Up": "Běží", + "Down": "Nedostupné", + "Pending": "Čekám", + "Unknown": "Neznámý", + "Pause": "Pauza", + "Name": "Název", + "Status": "Stav", + "DateTime": "Časové razítko", + "Message": "Zpráva", + "No important events": "Žádné důležité události", + "Resume": "Pokračovat", + "Edit": "Změnit", + "Delete": "Vymazat", + "Current": "Aktuální", + "Uptime": "Doba provozu", + "Cert Exp.": "Expirace Cert.", + "Monitor": "Dohled | Dohledů", + "day": "den | dny/í", + "-day": "-dní", + "hour": "hodina", + "-hour": "-hodin", + "Response": "Odpověď", + "Ping": "Ping", + "Monitor Type": "Typ dohledu", + "Keyword": "Klíčové slovo", + "Friendly Name": "Obecný název", + "URL": "URL", + "Hostname": "Adresa serveru", + "Port": "Port", + "Heartbeat Interval": "Heartbeat interval", + "Retries": "Počet pokusů", + "Heartbeat Retry Interval": "Interval opakování heartbeatu", + "Resend Notification if Down X times consecutively": "Zaslat oznámení znovu, pokud je služba nedostupná Xkrát za sebou", + "Advanced": "Rozšířené", + "Upside Down Mode": "Inverzní režim", + "Max. Redirects": "Max. přesměrování", + "Accepted Status Codes": "Akceptované stavové kódy", + "Push URL": "Push URL", + "needPushEvery": "Tuto URL adresu byste měli volat každých {0} sekund.", + "pushOptionalParams": "Volitelné parametry: {0}", + "Save": "Uložit", + "Notifications": "Oznámení", + "Not available, please setup.": "Není k dispozici, prosím nastavte.", + "Setup Notification": "Nastavení oznámení", + "Light": "Světlý", + "Dark": "Tmavý", + "Auto": "Automaticky", + "Theme - Heartbeat Bar": "Motiv – Heartbeat panel", + "Normal": "Normální", + "Bottom": "Dole", + "None": "Žádné", + "Timezone": "Časové pásmo", + "Search Engine Visibility": "Viditelnost pro vyhledávače", + "Allow indexing": "Povolit indexování", + "Discourage search engines from indexing site": "Zabránit vyhledávačům v indexování stránky", + "Change Password": "Změnit heslo", + "Current Password": "Aktuální heslo", + "New Password": "Nové heslo", + "Repeat New Password": "Znovu zadat nové heslo", + "Update Password": "Aktualizovat heslo", + "Disable Auth": "Deaktivovat ověřování", + "Enable Auth": "Povolit ověřování", + "disableauth.message1": "Opravdu chcete {disableAuth}?", + "disable authentication": "deaktivovat autentifikaci", + "disableauth.message2": "Tato možnost je určena pro případy, kdy {intendThirdPartyAuth} ještě před přístupem do Uptime Kuma, například prostřednictvím Cloudflare Access.", + "where you intend to implement third-party authentication": "máte autentifikaci zajištěnou třetí stranou", + "Please use this option carefully!": "Používejte ji prosím s rozmyslem!", + "Logout": "Odhlásit", + "Leave": "Odejít", + "I understand, please disable": "Rozumím, chci ji deaktivovat", + "Confirm": "Potvrzení", + "Yes": "Ano", + "No": "Ne", + "Username": "Uživatelské jméno", + "Password": "Heslo", + "Remember me": "Zapamatovat si mě", + "Login": "Přihlášení", + "No Monitors, please": "Žádné dohledy, prosím", + "add one": "začněte přidáním nového", + "Notification Type": "Typ oznámení", + "Email": "E-mail", + "Test": "Test", + "Certificate Info": "Informace o certifikátu", + "Resolver Server": "Server Resolveru", + "Resource Record Type": "Typ záznamu o prostředku", + "Last Result": "Poslední výsledek", + "Create your admin account": "Vytvořit účet administrátora", + "Repeat Password": "Znovu zadat heslo", + "Import Backup": "Importovat zálohu", + "Export Backup": "Exportovat zálohu", + "Export": "Exportovat", + "Import": "Importovat", + "respTime": "Doba odezvy (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Standardně povoleno", + "Apply on all existing monitors": "Použít pro všechny existující dohledy", + "Create": "Vytvořit", + "Clear Data": "Vymazat data", + "Events": "Události", + "Heartbeats": "Heartbeaty", + "Auto Get": "Získat automaticky", + "backupDescription": "Všechny dohledy a oznámení můžete zálohovat do souboru ve formátu JSON.", + "backupDescription2": "Poznámka: Nezahrnuje historii a data událostí.", + "backupDescription3": "Součástí exportovaného souboru jsou citlivá data jako tokeny oznámení; export si prosím bezpečně uložte.", + "alertNoFile": "Vyberte soubor, který chcete importovat.", + "alertWrongFileType": "Vyberte soubor ve formátu JSON.", + "Clear all statistics": "Vymazat všechny statistiky", + "Skip existing": "Přeskočit existující", + "Overwrite": "Přepsat", + "Options": "Možnosti", + "Keep both": "Ponechat obojí", + "Verify Token": "Ověřit token", + "Setup 2FA": "Nastavení 2FA", + "Enable 2FA": "Povolit 2FA", + "Disable 2FA": "Deaktivovat 2FA", + "2FA Settings": "Nastavení 2FA", + "Two Factor Authentication": "Dvoufaktorová autentifikace", + "Active": "Zapnuto", + "Inactive": "Neaktivní", + "Token": "Token", + "Show URI": "Zobrazit URI", + "Tags": "Štítky", + "Add New below or Select...": "Níže přidejte nový nebo vyberte existující…", + "Tag with this name already exist.": "Štítek s tímto názvem již existuje.", + "Tag with this value already exist.": "Štítek touto hodnotou již existuje.", + "color": "barva", + "value (optional)": "hodnota (volitelné)", + "Gray": "Šedá", + "Red": "Červená", + "Orange": "Oranžová", + "Green": "Zelená", + "Blue": "Modrá", + "Indigo": "Indigo", + "Purple": "Purpurová", + "Pink": "Růžová", + "Custom": "Vlastní", + "Search...": "Hledat…", + "Avg. Ping": "Průměr Ping", + "Avg. Response": "Průměr Odpověď", + "Entry Page": "Vstupní stránka", + "statusPageNothing": "Nic tady není, přidejte prosím skupinu nebo dohled.", + "No Services": "Žádné služby", + "All Systems Operational": "Všechny systémy běží", + "Partially Degraded Service": "Částečně zhoršená služba", + "Degraded Service": "Zhoršená služba", + "Add Group": "Přidat skupinu", + "Add a monitor": "Přidání dohledu", + "Edit Status Page": "Upravit stavovou stránku", + "Go to Dashboard": "Přejít na nástěnku", + "Status Page": "Stavová stránka", + "Status Pages": "Stavová stránka", + "defaultNotificationName": "Moje {notification} upozornění ({číslo})", + "here": "klikněte sem", + "Required": "Vyžadováno", + "telegram": "Telegram", + "ZohoCliq": "ZohoCliq", + "Bot Token": "Token bota", + "wayToGetTelegramToken": "Token můžete získat od {0}.", + "Chat ID": "ID chatu", + "supportTelegramChatID": "Podpora přímého chatu / skupiny / ID chatu kanálu", + "wayToGetTelegramChatID": "ID chatu můžete získat tak, že robotovi zašlete zprávu a přejdete na tuto adresu URL, kde zobrazíte chat_id:", + "YOUR BOT TOKEN HERE": "SEM ZADEJTE TOKEN VAŠEHO CHATBOTA", + "chatIDNotFound": "ID chatu nebylo nalezeno; nejprve tomuto robotovi zašlete zprávu", + "webhook": "Webhook", + "Post URL": "URL adresa příspěvku", + "Content Type": "Typ obsahu", + "webhookJsonDesc": "{0} je vhodný pro všechny moderní servery HTTP, jako je Express.js", + "webhookFormDataDesc": "{multipart} je vhodné pro PHP. JSON bude nutné analyzovat prostřednictvím {decodeFunction}", + "webhookAdditionalHeadersTitle": "Dodatečné hlavičky", + "webhookAdditionalHeadersDesc": "Nastavte dodatečné hlavičky, které se odešlou společně s webhookem. Každá hlavička by měla být definována jako klíč/hodnota v JSON.", + "smtp": "E-mail (SMTP)", + "secureOptionNone": "Žádné / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignorovat chybu TLS", + "From Email": "Odesílatel", + "emailCustomSubject": "Vlastní předmět", + "To Email": "Příjemce", + "smtpCC": "Kopie", + "smtpBCC": "Skrytá kopie", + "discord": "Discord", + "Discord Webhook URL": "URL Webhooku Discord", + "wayToGetDiscordURL": "Získáte tak, že přejdete do Nastavení serveru - > Integrace - > Zobrazi webhooky -> Nový webhook", + "Bot Display Name": "Zobrazované jméno robota", + "Prefix Custom Message": "Předpona vlastní zprávy", + "Hello @everyone is...": "Dobrý den, {'@'}všichni jsou…", + "teams": "Microsoft Teams", + "Webhook URL": "URL adresa webhooku", + "wayToGetTeamsURL": "Pro informace o tom, jak vytvořit URL adresu webhooku {0}.", + "wayToGetZohoCliqURL": "Pro informace o tom, jak vytvořit URL adresu webhooku {0}.", + "signal": "Signal", + "Number": "Číslo", + "Recipients": "Příjemci", + "needSignalAPI": "Musíte mít Signal klienta s REST API.", + "wayToCheckSignalURL": "Pro zobrazení instrukcí, jak službu nastavit, přejděte na následující adresu:", + "signalImportant": "Důležité: v seznamu příjemců není možné současně použít skupiny a čísla!", + "gotify": "Gotify", + "Application Token": "Token aplikace", + "Server URL": "URL adresa serveru", + "Priority": "Priorita", + "slack": "Slack", + "Icon Emoji": "Ikona smajlíka", + "Channel Name": "Název kanálu", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Více informací o Webhoocích naleznete na adrese: {0}", + "aboutChannelName": "Pro vynechání Webhook kanálu zadejte jeho název do pole Název kanálu {0}. Příklad: #jiny-kanal", + "aboutKumaURL": "Pokud ponecháte pole URL adresa Uptime Kuma prázdné, použije se domovská stránka GitHub projektu.", + "emojiCheatSheet": "Tahák smajlíků: {0}", + "rocket.chat": "Rocket.Chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "Push od Techulus", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (podpora více než 50 oznamovacích služeb)", + "GoogleChat": "Google Chat (pouze Google Workspace)", + "pushbullet": "Pushbullet", + "Kook": "Kook", + "wayToGetKookBotToken": "Aplikaci vytvoříte a token bota získáte na {0}", + "wayToGetKookGuildID": "V nastavení Kook aktivujte 'Vývojářský režim' a kliknutím pravým tlačítkem na guild získejte jeho ID", + "Guild ID": "Guild ID", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "Klíč uživatele", + "Device": "Zařízení", + "Message Title": "Nadpis zprávy", + "Notification Sound": "Zvuk oznámení", + "More info on:": "Více informací naleznete na adrese: {0}", + "pushoverDesc1": "Výchozí časový limit pro emergency prioritu (2) je 30 sekund mezi opakovanými pokusy a vyprší po 1 hodině.", + "pushoverDesc2": "Pokud chcete odesílat oznámení do různých zařízení, vyplňte pole Zařízení.", + "SMS Type": "Typ SMS", + "octopushTypePremium": "Premium (rychlé – doporučeno pro upozornění)", + "octopushTypeLowCost": "Nízké náklady (pomalé – někdy blokované operátorem)", + "checkPrice": "Ceny {0} zjistíte na adrese:", + "apiCredentials": "API přihlašovací údaje", + "octopushLegacyHint": "Používáte starší verzi Octopush (2011-2020) nebo novou verzi?", + "Check octopush prices": "Ceny octopush naleznete na adrese {0}.", + "octopushPhoneNumber": "Telefonní číslo (v mezinárodním formátu, např: +42012345678)", + "octopushSMSSender": "Odesílatel SMS: 3-11 alfanumerických znaků a mezera (a-zA-Z0-9)", + "LunaSea Device ID": "ID zařízení LunaSea", + "Apprise URL": "Apprise URL", + "Example:": "Příklad: {0}", + "Read more:": "Více informací: {0}", + "Status:": "Stav: {0}", + "Read more": "Více informací", + "appriseInstalled": "Apprise je nainstalován.", + "appriseNotInstalled": "Apprise není nainstalován. {0}", + "Access Token": "Přístupový token", + "Channel access token": "Přístupový token ke kanálu", + "Line Developers Console": "Konzole Line Developers", + "lineDevConsoleTo": "Konzole Line Developers - {0}", + "Basic Settings": "Obecné nastavení", + "User ID": "ID uživatele", + "Messaging API": "API pro zasílání zpráv", + "wayToGetLineChannelToken": "Nejprve otevřete {0}, vytvořte poskytovatele a kanál (Messaging API). Poté můžete získat přístupový token ke kanálu a ID uživatele, v sekci uvedené výše.", + "Icon URL": "URL adresa ikony", + "aboutIconURL": "Pro přepsání výchozího profilového obrázku můžete do pole \"URL adresa ikony\" zadat odkaz na obrázek. Nebude použito, pokud je nastavena ikona smajlíka.", + "aboutMattermostChannelName": "Výchozí kanál, do kterého jsou zasílány Webhook příspěvky, můžete přepsat zadáním názvu kanálu do pole \"Název kanálu\". Tato možnost musí být povolena v nastavení Mattermost Webhooku. Příklad: #jiny-kanal", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO – levné, ale pomalé a často přetížené. Omezeno pouze na polské příjemce.", + "promosmsTypeFlash": "SMS FLASH –zpráva se automaticky zobrazí na zařízení příjemce. Omezeno pouze na polské příjemce.", + "promosmsTypeFull": "SMS FULL – prémiová úroveň SMS. Můžete definovat odesílatele (vyžadována registrace jména). Spolehlivý pro výstrahy.", + "promosmsTypeSpeed": "SMS SPEED – nejvyšší priorita v systému. Velmi rychlé a spolehlivé, ale nákladné (přibližně dvojnásobek ceny SMS FULL).", + "promosmsPhoneNumber": "Telefonní číslo (polští příjemci mohou vynechat telefonní předvolbu)", + "promosmsSMSSender": "Odesílatel SMS: Předem zaregistrovaný název nebo jeden z výchozích: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "promosmsAllowLongSMS": "Povolit dlouhé SMS", + "Feishu WebHookUrl": "URL Webhooku Feishu", + "matrixHomeserverURL": "URL adresa domácího serveru (s http(s):// a volitelně portem)", + "Internal Room Id": "ID interní místnosti", + "matrixDesc1": "ID interní místnosti naleznete v Matrix klientovi v rozšířeném nastavení místnosti. Mělo by být ve tvaru !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Důrazně doporučujeme vytvořit nového uživatele a nepoužívat váš vlastní přístupový token uživatele Matrix. Pomocí něj je možné získat přístup k vašemu účtu a všem místnostem, ke kterým jste se připojili. Místo toho vytvořte nového uživatele a pozvěte jej pouze do místnosti, do které chcete oznámení dostávat. Přístupový token můžete získat spuštěním {0}", + "Method": "Metoda", + "Body": "Tělo", + "Headers": "Hlavičky", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "Hlavičky žádosti nejsou platný JSON: ", + "BodyInvalidFormat": "Text žádosti není platný JSON: ", + "Monitor History": "Historie dohledu", + "clearDataOlderThan": "Historie dohledu bude uchovávána po dobu {0} dní.", + "PasswordsDoNotMatch": "Hesla se neshodují.", + "records": "záznamů", + "One record": "Jeden záznam", + "steamApiKeyDescription": "Pro monitorování herního serveru ve službě Steam je nutné zadat Steam Web-API klíč. Svůj API klíč získáte na následující stránce: ", + "Current User": "Aktuálně přihlášený uživatel", + "topic": "Téma", + "topicExplanation": "MQTT téma, které chcete sledovat", + "successMessage": "Zpráva o úspěchu", + "successMessageExplanation": "MQTT zpráva považovaná za úspěšnou", + "recent": "Poslední", + "Done": "Hotovo", + "Info": "Informace", + "Security": "Bezpečnost", + "Steam API Key": "API klíč služby Steam", + "Shrink Database": "Zmenšit databázi", + "Pick a RR-Type...": "Vyberte typ RR záznamu…", + "Pick Accepted Status Codes...": "Vyberte stavové kódy, které chcete akceptovat…", + "Default": "Výchozí", + "HTTP Options": "Možnosti protokolu HTTP", + "Create Incident": "Vytvořit incident", + "Title": "Předmět", + "Content": "Obsah", + "Style": "Styl", + "info": "informace", + "warning": "upozornění", + "danger": "riziko", + "error": "chyba", + "critical": "kritické", + "primary": "primární", + "light": "světlý", + "dark": "tmavý", + "Post": "Publikovat", + "Please input title and content": "Zadejte prosím název a obsah", + "Created": "Vytvořen", + "Last Updated": "Poslední aktualizace", + "Unpin": "Odepnout", + "Switch to Light Theme": "Přepnout na světlý motiv", + "Switch to Dark Theme": "Přepnout na tmavý motiv", + "Show Tags": "Zobrazit štítky", + "Hide Tags": "Skrýt štítky", + "Description": "Popis", + "No monitors available.": "Není dostupný žádný dohled.", + "Add one": "Přidat jeden", + "No Monitors": "Žádný dohled", + "Untitled Group": "Skupina bez názvu", + "Services": "Služby", + "Discard": "Zahodit", + "Cancel": "Zrušit", + "Powered by": "Poskytuje", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API uživatelské jméno (včetně předpony webapi_)", + "serwersmsAPIPassword": "API heslo", + "serwersmsPhoneNumber": "Telefonní číslo", + "serwersmsSenderName": "Odesílatel SMS (registrováno prostřednictvím zákaznického portálu)", + "smseagle": "SMSEagle", + "smseagleTo": "Telefonní číslo(a)", + "smseagleGroup": "Název skupiny v adresáři", + "smseagleContact": "Název kontaktu v adresáři", + "smseagleRecipientType": "Typ příjemce", + "smseagleRecipient": "Příjemce(i) (více záznamů oddělte čárkou)", + "smseagleToken": "API přístupový token", + "smseagleUrl": "URL vašeho SMSEagle zařízení", + "smseagleEncoding": "Odeslat v Unicode (standardně=GSM-7)", + "smseaglePriority": "Priorita zprávy (0-9, nejvyšší priorita = 9)", + "stackfield": "Stackfield", + "Customize": "Přizpůsobit", + "Custom Footer": "Vlastní patička", + "Custom CSS": "Vlastní CSS", + "smtpDkimSettings": "Nastavení DKIM", + "smtpDkimDesc": "Informace o použití naleznete v {0} Nodemailer DKIM.", + "documentation": "dokumentaci", + "smtpDkimDomain": "Název domény", + "smtpDkimKeySelector": "Selektor klíče", + "smtpDkimPrivateKey": "Privátní klíč", + "smtpDkimHashAlgo": "Hashovací algoritmus (volitelné)", + "smtpDkimheaderFieldNames": "Podepisovat tyto hlavičky (volitelné)", + "smtpDkimskipFields": "Nepodepisovat tyto hlavičky (volitelné)", + "wayToGetPagerDutyKey": "Získat jej můžete v sekci Service -> Service Directory -> (vyberte službu) -> Integrations -> Add integration. Následně vyhledejte \"Events API V2\". Více informace naleznete na adrese {0}", + "Integration Key": "Integrační klíč", + "Integration URL": "Integrační URL", + "Auto resolve or acknowledged": "Automatické řešení nebo potvrzení", + "do nothing": "nedělat nic", + "auto acknowledged": "automaticky uznáno", + "auto resolve": "automatické řešení", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API koncový bod", + "alertaEnvironment": "Prostředí", + "alertaApiKey": "API klíč", + "alertaAlertState": "Stav upozornění", + "alertaRecoverState": "Stav obnovení", + "deleteStatusPageMsg": "Opravdu chcete odstranit tuto stavovou stránku?", + "Proxies": "Proxy", + "default": "Výchozí", + "enabled": "Zapnuto", + "setAsDefault": "Nastavit jako výchozí", + "deleteProxyMsg": "Opravdu chcete odstranit tuto proxy ze všech dohledů?", + "proxyDescription": "Pro zajištění funkčnosti musí být proxy přiřazena dohledům.", + "enableProxyDescription": "Tato proxy neovlivní žádosti dohledu do doby, než ji aktivujete. Změnou tohoto nastavení dočasně zakážete použití proxy ve všech dohledech.", + "setAsDefaultProxyDescription": "Tato proxy se použije pro všechny nové dohledy. V případě potřeby můžete její využívání zakázat v konkrétním dohledu.", + "Certificate Chain": "Řetězec certifikátu", + "Valid": "Platný", + "Invalid": "Neplatný", + "AccessKeyId": "ID přístupového klíče", + "SecretAccessKey": "Tajemství přístupového klíče", + "PhoneNumbers": "Telefonní čísla", + "TemplateCode": "TemplateCode", + "SignName": "SignName", + "Sms template must contain parameters: ": "Šablona SMS musí obsahovat parametry: ", + "Bark Endpoint": "Bark koncový bod", + "Bark Group": "Skupina Bark", + "Bark Sound": "Bark zvuk", + "WebHookUrl": "WebHookUrl", + "SecretKey": "Tajný klíč", + "For safety, must use secret key": "Z důvodu bezpečnosti použijte secret key", + "Device Token": "Token zařízení", + "Platform": "Platforma", + "Huawei": "Huawei", + "High": "Vysoký", + "Retry": "Opakovat", + "Topic": "Téma", + "WeCom Bot Key": "Klíč WeCom Bota", + "Setup Proxy": "Nastavit proxy", + "Proxy Protocol": "Protokol proxy", + "Proxy Server": "Proxy Server", + "Proxy server has authentication": "Proxy server vyžaduje ověření", + "User": "Uživatel", + "Installed": "Nainstalováno", + "Not installed": "Nenainstalováno", + "Running": "Běží", + "Not running": "Neběží", + "Remove Token": "Odstranit token", + "Start": "Spustit", + "Stop": "Zastavit", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Přidat novou stavovou stránku", + "Slug": "Slug", + "Accept characters:": "Přípustné znaky:", + "startOrEndWithOnly": "Počáteční a koncový znak může být pouze {0}", + "No consecutive dashes": "Nesmí se opakovat pomlčky", + "Next": "Další", + "The slug is already taken. Please choose another slug.": "Slug s tímto názvem již existuje. Prosím, zadejte jiný název.", + "No Proxy": "Žádná proxy", + "Authentication": "Ověření", + "HTTP Basic Auth": "HTTP Basic ověření", + "New Status Page": "Nová stavová stránka", + "Page Not Found": "Stránka nenalezena", + "Reverse Proxy": "Reverzní proxy", + "Backup": "Záloha", + "About": "O programu", + "wayToGetCloudflaredURL": "(Stáhnout cloudflared z {0})", + "cloudflareWebsite": "Webová stránka Cloudflare", + "Message:": "Zpráva:", + "Don't know how to get the token? Please read the guide:": "Nevíte jak získat? Prosím, přečtěte si tuto příručku:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Stávající připojení mohlo být ztraceno, pokud jste připojeni prostřednictvím Cloudflare tunelu. Opravdu jej chcete zastavit? Pro potvrzení zadejte své současné heslo.", + "HTTP Headers": "HTTP hlavičky", + "Trust Proxy": "Důvěryhodná proxy", + "Other Software": "Jiný software", + "For example: nginx, Apache and Traefik.": "Například nginx, Apache nebo Traefik.", + "Please read": "Prosím, přečtěte si informace na adrese", + "Subject:": "Předmět:", + "Valid To:": "Platnost do:", + "Days Remaining:": "Počet zbývajících dní:", + "Issuer:": "Vydavatel:", + "Fingerprint:": "Otisk:", + "No status pages": "Žádná stavová stránka", + "Domain Name Expiry Notification": "Oznámení na blížící se konec platnosti doménového jména", + "Proxy": "Proxy", + "Date Created": "Datum vytvoření", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "OneBot HTTP adresa", + "onebotMessageType": "Typ OneBot zprávy", + "onebotGroupMessage": "Skupinová", + "onebotPrivateMessage": "Soukromá", + "onebotUserOrGroupId": "ID skupiny/uživatele", + "onebotSafetyTips": "Z důvodu bezpečnosti je nutné zadat přístupový token", + "PushDeer Key": "PushDeer klíč", + "Footer Text": "Text v patičce", + "Show Powered By": "Zobrazit \"Poskytuje\"", + "Domain Names": "Doménová jména", + "signedInDisp": "Přihlášen jako {0}", + "signedInDispDisabled": "Ověření je vypnuté.", + "RadiusSecret": "Tajemství Radius", + "RadiusSecretDescription": "Sdílený tajný klíč mezi klientem a serverem", + "RadiusCalledStationId": "ID volaného zařízení", + "RadiusCalledStationIdDescription": "Identifikátor volaného zařízení", + "RadiusCallingStationId": "ID volajícího zařízení", + "RadiusCallingStationIdDescription": "Identifikátor volajícího zařízení", + "Certificate Expiry Notification": "Upozornění na blížící se konec platnosti certifikátu", + "API Username": "Uživatelské jméno API", + "API Key": "API klíč", + "Recipient Number": "Číslo příjemce", + "From Name/Number": "Jméno/číslo odesílatele", + "Leave blank to use a shared sender number.": "Ponechte prázdné, pokud chcete použít číslo sdíleného příjemce.", + "Octopush API Version": "Octopush API verze", + "Legacy Octopush-DM": "Legacy Octopush-DM", + "endpoint": "endpoint", + "octopushAPIKey": "\"API key\" ze sekce HTTP API credentials na nástěnce", + "octopushLogin": "\"Login\" ze sekce HTTP API credentials na nástěnce", + "promosmsLogin": "API Login Name", + "promosmsPassword": "API Password", + "pushoversounds pushover": "Pushover (výchozí)", + "pushoversounds bike": "Kolo", + "pushoversounds bugle": "Trumpeta", + "pushoversounds cashregister": "Pokladna", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Kosmický", + "pushoversounds falling": "Padající", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Příchozí", + "pushoversounds intermission": "Přestávka", + "pushoversounds magic": "Kouzlo", + "pushoversounds mechanical": "Mechanika", + "pushoversounds pianobar": "Barové piano", + "pushoversounds siren": "Siréna", + "pushoversounds spacealarm": "Vesmírný alarm", + "pushoversounds tugboat": "Remorkér", + "pushoversounds alien": "Mimozemský poplach (dlouhý)", + "pushoversounds climb": "Climb (dlouhý)", + "pushoversounds persistent": "Persistent (dlouhý)", + "pushoversounds echo": "Pushover Echo (dlouhý)", + "pushoversounds updown": "Up Down (dlouhý)", + "pushoversounds vibrate": "Pouze vibrace", + "pushoversounds none": "Žádný (ticho)", + "pushyAPIKey": "Tajný API klíč", + "pushyToken": "Token zařízení", + "Show update if available": "Upozornit na aktualizace, pokud jsou k dispozici", + "Also check beta release": "Kontrolovat také dostupnost beta verzí", + "Using a Reverse Proxy?": "Používáte reverzní proxy?", + "Check how to config it for WebSocket": "Zjistěte, jak ji nakonfigurovat pro WebSockety", + "Steam Game Server": "Herní server ve službě Steam", + "Most likely causes:": "Nejčastější důvody:", + "The resource is no longer available.": "Zdroj již není k dispozici.", + "There might be a typing error in the address.": "Při zadávání adresy jste udělali chybu.", + "What you can try:": "Co můžete vyzkoušet:", + "Retype the address.": "Znovu zadat adresu.", + "Go back to the previous page.": "Vrátit se na předchozí stránku.", + "Coming Soon": "Připravujeme", + "wayToGetClickSendSMSToken": "API Username a API Key získáte na adrese {here}.", + "Connection String": "Připojovací řetězec", + "Query": "Dotaz", + "settingsCertificateExpiry": "Platnost TLS certifikátu", + "certificationExpiryDescription": "HTTPS dohledy upozorní na vypršení platnosti certifikátu TLS nastavenou dobu dopředu:", + "Setup Docker Host": "Nastavit Docker hostitele", + "Connection Type": "Typ připojení", + "Docker Daemon": "Démon Dockeru", + "deleteDockerHostMsg": "Opravdu chcete odstranit tohoto docker hostitele ze všech dohledů?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker kontejner", + "Container Name / ID": "ID / název kontejneru", + "Docker Host": "Docker hostitel", + "Docker Hosts": "Docker hostitelé", + "ntfy Topic": "ntfy Téma", + "Domain": "Doména", + "Workstation": "Pracovní stanice", + "disableCloudflaredNoAuthMsg": "Používáte režim bez ověření, heslo není vyžadováno.", + "trustProxyDescription": "Důvěřovat 'X-Forwarded-*' hlavičkám. Pokud chcete získat správnou IP adresu klientů a vaše instance Uptime Kuma je za Nginx nebo Apache, měli byste tuto možnost zapnout.", + "wayToGetLineNotifyToken": "Přístupový token můžete získat na adrese {0}", + "Examples": "Příklady", + "Home Assistant URL": "URL Home Assistant", + "Long-Lived Access Token": "Dlouhodobý přístupový token", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Pro vytvoření dlouhodobého přístupový tokenu klikněte na název svého profilu (v levém dolním rohu) a následně v dolní části stránky klikněte na tlačítko Create Token.", + "Notification Service": "Oznamovací služba", + "default: notify all devices": "výchozí: upozornit všechny zařízení", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Seznam dostupných oznamovacích služeb naleznete v Home Assistant v sekci \"Developer Tools > Services\", kde vyhledejte \"notification\" pro zjištění názvu zařízení.", + "Automations can optionally be triggered in Home Assistant:": "Automatizaci můžete volitelně aktivovat prostřednictvím Home Assistant:", + "Trigger type:": "Typ podmínky spuštění:", + "Event type:": "Typ události:", + "Event data:": "Data události:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Následně vyberte akci, například přepnutí scény z RGB světla na červenou.", + "Frontend Version": "Verze frontendu", + "Frontend Version do not match backend version!": "Verze frontendu neodpovídá verzi backendu!", + "Base URL": "Primární URL adresa", + "goAlertInfo": "GoAlert je aplikace s otevřeným zdrojovým kódem pro plánování hovorů, automatické eskalace a upozornění (jako jsou SMS nebo hlasové hovory). Automaticky zapojte správnou osobu, správným způsobem a ve správný čas! {0}", + "goAlertIntegrationKeyInfo": "Obecný API integrační klíč pro danou službu ve formátu \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" se obvykle nachází ve zkopírované URL jako hodnota parametru token.", + "goAlert": "GoAlert", + "backupOutdatedWarning": "Zastaralé: Vzhledem k tomu, že bylo přidáno mnoho funkcí a tato funkce zálohování je poněkud neudržovaná, nemůže vytvářet nebo obnovit kompletní zálohu.", + "backupRecommend": "Prosím, zálohujte si ručně celý svazek nebo datovou složku (./data/).", + "Optional": "Volitelný", + "squadcast": "Squadcast", + "SendKey": "Klíč k odesílání", + "SMSManager API Docs": "Dokumentace API služby SMSManager", + "Gateway Type": "Typ brány", + "SMSManager": "SMSManager", + "You can divide numbers with": "Čísla můžete oddělit pomocí", + "or": "nebo", + "recurringInterval": "Interval", + "Recurring": "Opakující se", + "strategyManual": "Ruční spuštění/vypnutí", + "warningTimezone": "Používá se časové pásmo serveru", + "weekdayShortMon": "Po", + "weekdayShortTue": "Út", + "weekdayShortWed": "St", + "weekdayShortThu": "Čt", + "weekdayShortFri": "Pá", + "weekdayShortSat": "So", + "weekdayShortSun": "Ne", + "dayOfWeek": "Den v týdnu", + "dayOfMonth": "Den v měsíci", + "lastDay": "Poslední den", + "lastDay1": "Poslední den v měsíci", + "lastDay2": "2. poslední den v měsíci", + "lastDay3": "3. poslední den v měsíci", + "lastDay4": "4. poslední den v měsíci", + "No Maintenance": "Žádná údržba", + "pauseMaintenanceMsg": "Opravdu chcete údržbu pozastavit?", + "maintenanceStatus-under-maintenance": "Údržba", + "maintenanceStatus-inactive": "Neaktivní", + "maintenanceStatus-scheduled": "Naplánováno", + "maintenanceStatus-ended": "Ukončeno", + "maintenanceStatus-unknown": "Neznámý", + "Display Timezone": "Zobrazit časové pásmo", + "Server Timezone": "Časové pásmo serveru", + "statusPageMaintenanceEndDate": "Konec", + "IconUrl": "Adresa URL ikony", + "Enable DNS Cache": "(Zastaralé) Povolit DNS Cache pro HTTP(s) dohledy", + "Enable": "Povolit", + "Disable": "Zakázat", + "dnsCacheDescription": "V některých IPv6 prostředích nemusí fungovat. Pokud narazíte na nějaké problémy, tuto možnost vypněte.", + "Single Maintenance Window": "Konkrétní časové okno pro údržbu", + "Maintenance Time Window of a Day": "Časové okno pro údržbu v daný den", + "Effective Date Range": "Časové období (volitelné)", + "Schedule Maintenance": "Naplánovat údržbu", + "Date and Time": "Datum a čas", + "DateTime Range": "Rozsah data a času", + "Strategy": "Strategie", + "Free Mobile User Identifier": "Identifikátor uživatele Free Mobile", + "Free Mobile API Key": "API klíč Free Mobile", + "Enable TLS": "Povolit TLS", + "Proto Service Name": "Jméno Proto Service", + "Proto Method": "Proto metoda", + "Proto Content": "Proto obsah", + "Economy": "Úsporná", + "Lowcost": "Nízkonákladová", + "high": "vysoká", + "General Monitor Type": "Obecný typ dohledu", + "Passive Monitor Type": "Pasivní typ dohledu", + "Specific Monitor Type": "Konkrétní typ dohledu", + "dataRetentionTimeError": "Doba pro uchování musí být větší nebo rovna 0", + "infiniteRetention": "Pro nekonečný záznam zadejte 0.", + "confirmDeleteTagMsg": "Opravdu chcete odstranit tento štítek? Provedením této akce nedojde k odstranění dohledů, které jej mají přiřazeny.", + "Help": "Nápověda", + "Game": "Hra", + "Custom Monitor Type": "Vlastní typ dohledu", + "loadingError": "Nelze načíst data, zkuste to prosím později.", + "confirmUninstallPlugin": "Opravdu chcete tento zásuvný modul odinstalovat?", + "plugin": "Zásuvné moduly | Zásuvné moduly", + "install": "Instalace", + "installing": "Instaluji", + "uninstall": "Odinstalace", + "uninstalling": "Odinstalování", + "Packet Size": "Velikost paketu", + "markdownSupported": "Markdown syntaxe je podporována. Při použití HTML se vyhněte mezerám na začátku, aby nedocházelo k problémům s formátováním.", + "Google Analytics ID": "ID Google Analytics", + "Edit Tag": "Upravit štítek", + "Server Address": "Adresa serveru", + "Learn More": "Zjistěte více", + "notificationRegional": "Místní", + "telegramMessageThreadID": "(Nepovinné) ID vlákna zprávy", + "telegramMessageThreadIDDescription": "Nepovinný jedinečný identifikátor cílového vlákna zprávy (tématu) fóra; pouze pro nadskupiny fóra", + "telegramProtectContentDescription": "Pokud je tato funkce povolena, budou zprávy bota v aplikaci Telegram chráněny před přeposíláním a ukládáním.", + "Body Encoding": "Kódování těla zprávy", + "telegramProtectContent": "Ochrana přeposílání/ukládání", + "telegramSendSilently": "Odeslat potichu", + "telegramSendSilentlyDescription": "Zprávu odešle tiše. Uživatelé obdrží oznámení bez zvuku.", + "Clone": "Duplikovat", + "cloneOf": "Kopie {0}", + "Clone Monitor": "Duplikovat dohled", + "API Keys": "API klíče", + "Expiry": "Platnost", + "Don't expire": "Nevyprší", + "Continue": "Pokračovat", + "Add Another": "Přidat další", + "Key Added": "Klíč byl přidán", + "Expiry date": "Vyprší dne", + "No API Keys": "Žàdné API klíče", + "apiKey-active": "Aktivní", + "apiKey-expired": "Vypršel", + "Expires": "Vyprší", + "disableAPIKeyMsg": "Jste si jistý, že chcete deaktivovat tento API klíč?", + "Add API Key": "Přidat API klíč", + "apiKey-inactive": "Neaktivní", + "Generate": "Vygenerovat", + "apiKeyAddedMsg": "Váš klíč API byl přidán. Poznamenejte si jej, protože se již nebude zobrazovat.", + "deleteAPIKeyMsg": "Opravdu chcete tento klíč API odstranit?", + "pagertreeUrgency": "Urgence", + "pagertreeSilent": "Potichu", + "pagertreeLow": "Slabě", + "pagertreeCritical": "Kritické", + "pagertreeResolve": "Automatické řešení", + "pagertreeDoNothing": "Nedělej nic", + "pagertreeIntegrationUrl": "Integrační URL", + "pagertreeMedium": "Středně", + "pagertreeHigh": "Nahlas", + "wayToGetPagerTreeIntegrationURL": "Po vytvoření integrace Uptime Kuma v aplikaci PagerTree zkopírujte koncový bod. Více podrobností si zobrazíte na {0}", + "Add New Tag": "Přidat nový štítek", + "lunaseaTarget": "Cíl", + "lunaseaDeviceID": "ID zařízení", + "lunaseaUserID": "ID uživatele", + "statusPageRefreshIn": "Obnovení za: {0}", + "twilioAccountSID": "SID účtu", + "twilioFromNumber": "Číslo odesílatele", + "twilioToNumber": "Číslo příjemce", + "twilioAuthToken": "Autorizační token / Tajemství API klíče", + "sameAsServerTimezone": "Stejné jako časové pásmo serveru", + "cronExpression": "Cron výraz", + "cronSchedule": "Plán: ", + "invalidCronExpression": "Neplatný cron výraz: {0}", + "startDateTime": "Datum/čas začátku", + "endDateTime": "Datum/čas konce", + "ntfyAuthenticationMethod": "Způsob ověření", + "ntfyUsernameAndPassword": "Uživatelské jméno a heslo", + "pushoverMessageTtl": "Zpráva TTL (Sekund)", + "Show Clickable Link": "Zobrazit klikatelný odkaz", + "Show Clickable Link Description": "Pokud je zaškrtnuto, všichni, kdo mají přístup k této stavové stránce, mají přístup k adrese URL dohledu.", + "Open Badge Generator": "Otevřít generátor odznaků", + "Badge Type": "Typ odznaku", + "Badge Duration": "Platnost odznaku", + "Badge Label": "Štítek odznaku", + "Badge Prefix": "Prefix hodnoty odznaku", + "Monitor Setting": "Nastavení dohledu pro {0}", + "Badge Generator": "Generátor odznaků pro {0}", + "Badge Label Color": "Barva štítku odznaku", + "Badge Color": "Barva odznaku", + "Badge Style": "Styl odznaku", + "Badge Label Suffix": "Přípona štítku odznaku", + "Badge URL": "URL odznaku", + "Badge Suffix": "Přípona hodnoty odznaku", + "Badge Label Prefix": "Prefix štítku odznaku", + "Badge Up Color": "Barva odznaku při Běží", + "Badge Down Color": "Barva odznaku při Nedostupné", + "Badge Pending Color": "Barva odznaku při Pauze", + "Badge Maintenance Color": "Barva odznaku při Údržbě", + "Badge Warn Color": "Barva odznaku při Upozornění", + "Reconnecting...": "Obnovování spojení…", + "Cannot connect to the socket server": "Nelze se připojit k socketu serveru", + "Edit Maintenance": "Upravit Údržbu", + "Home": "Hlavní stránka", + "Badge Down Days": "Odznak nedostupných dní", + "Group": "Skupina", + "Monitor Group": "Sledovaná skupina", + "noGroupMonitorMsg": "Není k dispozici. Nejprve vytvořte skupinu dohledů.", + "Close": "Zavřít", + "Badge value (For Testing only.)": "Hodnota odznaku (pouze pro testování)", + "Badge Warn Days": "Odznak dní s upozorněním", + "nostrSender": "Privátní klíč odesílatele (nsec)", + "nostrRelaysHelp": "Jedno relay URL na řádku", + "nostrRecipients": "Privátní klíče příjemců (npub)", + "nostrRecipientsHelp": "formát npub, jeden na řádku", + "chromeExecutable": "Spustitelný soubor Chrome/Chromium", + "chromeExecutableAutoDetect": "Automatická detekce", + "chromeExecutableDescription": "Pokud uživatelé nástroje Docker ještě nemají nainstalovanou aplikaci Chromium, může instalace a zobrazení výsledku testu trvat několik minut. Zabere 1 GB místa na disku.", + "Invert Keyword": "Inverzní klíčové slovo", + "webhookBodyPresetOption": "Uložená hodnota - {0}", + "webhookBodyCustomOption": "Vlastní tělo", + "invertKeywordDescription": "Hledá se klíčové slovo, které je spíše nepřítomné než přítomné.", + "webhookCustomBodyDesc": "Nastaví vlastní tělo HTTP pro požadavek. Akceptovány jsou proměnné {msg}, {heartbeat}, {monitor}.", + "Request Body": "Tělo požadavku", + "twilioApiKey": "Klíč k API (volitelný)", + "Expected Value": "Očekávaná hodnota", + "Json Query": "Json dotaz", + "Badge Duration (in hours)": "Zobrazení odznaku (v hodinách)", + "Badge Preview": "Náhled odznaku", + "Notify Channel": "Upozornit kanál", + "aboutNotifyChannel": "Upozornění na kanál spustí upozornění na počítači nebo v mobilním telefonu pro všechny členy kanálu, ať už je jejich dostupnost nastavena na aktivní nebo na nepřítomnost.", + "filterActive": "Aktivní", + "filterActivePaused": "Pozastaveno", + "Enter the list of brokers": "Vytvořte seznam zprostředkovatelů", + "Press Enter to add broker": "Stiskem klávesy Enter přidáte zprostředkovatele", + "Kafka Topic Name": "Název Kafka vlákna", + "Enable Kafka SSL": "Zapnout Kafka SSL", + "Mechanism": "Mechanismus", + "Kafka Brokers": "Kafka zprostředkovatelé", + "Authorization Identity": "Autorizační identita", + "AccessKey Id": "AccessKey Id", + "Session Token": "Token relace", + "Pick a SASL Mechanism...": "Vyberte SASL mechanismus…", + "Secret AccessKey": "Heslo AccessKey", + "Server URL should not contain the nfty topic": "URL serveru by neměla obsahovat nfty vlákno", + "Kafka SASL Options": "Možnosti Kafka SASL", + "Enable Kafka Producer Auto Topic Creation": "Povolit Kafka zprostředkovateli automatické vytváření vláken", + "Kafka Producer Message": "Zpráva Kafka zprostředkovatele", + "tailscalePingWarning": "Abyste mohli používat Tailscale Ping monitor, je nutné Uptime Kuma nainstalovat mimo Docker, a dále na váš server nainstalovat Tailscale klienta.", + "Select": "Vybrat", + "selectedMonitorCount": "Vybráno: {0}", + "Check/Uncheck": "Vybrat/Zrušit výběr", + "showCertificateExpiry": "Zobrazit vypršení platnosti certifikátu", + "pushDeerServerDescription": "Chcete-li používat oficiální server, ponechte prázdné", + "noOrBadCertificate": "Žádný/Vadný certifikát", + "nostrRelays": "Nostr relay", + "FlashDuty Severity": "Závažnost", + "PushDeer Server": "Server PushDeer", + "wayToGetFlashDutyKey": "Pro integrování Uptime Kuma s Flashduty: přejděte do sekce Kanály > Vyberte kanál > Integrace > Přidat novou integraci\", vyberte možnost Uptime Kuma a zkopírujte Push adresu.", + "Request Timeout": "Časový limit požadavku", + "timeoutAfter": "Vypršení časového limitu po {0} sekundách", + "styleElapsedTime": "Čas uplynulý pod heartbeat ukazatelem", + "styleElapsedTimeShowWithLine": "Zobrazit (s linkou)", + "gamedigGuessPortDescription": "Port používaný protokolem Valve Server Query Protocol se může lišit od portu klienta. Pokud se monitor nemůže připojit k serveru, zkuste to.", + "styleElapsedTimeShowNoLine": "Zobrazit (bez linky)", + "gamedigGuessPort": "Gamedig: Port", + "Saved.": "Uloženo.", + "setupDatabaseChooseDatabase": "Kterou databázi byste chtěli používat?", + "setupDatabaseEmbeddedMariaDB": "Nemusíte nic nastavovat. Tento Docker obraz pro vás automaticky vložil a nakonfiguroval databázi MariaDB. Uptime Kuma se k této databázi připojí prostřednictvím unixového socketu.", + "setupDatabaseMariaDB": "Připojení k externí databázi MariaDB. Je třeba nastavit informace o připojení k databázi.", + "setupDatabaseSQLite": "Jednoduchý databázový soubor, doporučený pro malé instalace. Před verzí 2.0.0 používal Uptime Kuma jako výchozí databázi SQLite.", + "dbName": "Název databáze", + "enableNSCD": "Povolit NSCD (Name Service Cache Daemon) pro cachování všech DNS požadavků", + "toastErrorTimeout": "Časový limit pro oznámení o chybách", + "toastSuccessTimeout": "Časový limit pro oznámení o úspěchu", + "Bark API Version": "Verze API Bark", + "authInvalidToken": "Neplatný token.", + "2faAlreadyEnabled": "2FA je již aktivní.", + "2faEnabled": "2FA aktivní.", + "2faDisabled": "2FA neaktivní.", + "successResumed": "Úspěšně obnoveno.", + "successPaused": "Úspěšně pozastaveno.", + "successDeleted": "Úspěšně smazáno.", + "successEdited": "Úspěšně upraveno.", + "successBackupRestored": "Záloha byla úspěšně obnovena.", + "successDisabled": "Úspěšně zakázáno.", + "successEnabled": "Úspěšně povoleno.", + "tagNotFound": "Štítek nebyl nalezen.", + "foundChromiumVersion": "Nalezeno Chromium/Chrom. Verze: {0}", + "authUserInactiveOrDeleted": "Uživatel je neaktivní nebo smazaný.", + "authIncorrectCreds": "Nesprávné uživatelské jméno nebo heslo.", + "successAdded": "Úspěšně přidáno.", + "successAuthChangePassword": "Heslo bylo úspěšně aktualizováno.", + "pushOthers": "Ostatní", + "programmingLanguages": "Programovací jazyky", + "monitorToastMessagesLabel": "Upozornění Monitor Toast", + "monitorToastMessagesDescription": "Upozornění Toast zmizí po uplynutí nastaveného času. Časový limit vypnete nastavením -1. Upozornění vypnete nastavením 0.", + "pushViewCode": "Jak používat Push monitor? (Zobrazit kód)", + "liquidIntroduction": "Šablonovatelnost je dosažena pomocí šablonovacího jazyka Liquid. Pokyny k použití naleznete v {0}.", + "templateMsg": "zpráva upozornění", + "emailCustomisableContent": "Přizpůsobitelný obsah", + "GrafanaOncallUrl": "URL pro volání do Grafana", + "Reset Token": "Vynulovat token", + "noDockerHostMsg": "Není k dispozici. Nejprve nastavte hostitele Docker.", + "DockerHostRequired": "Nastavte prosím hostitele nástroje Docker pro tento dohled.", + "smtpLiquidIntroduction": "Následující dvě pole lze šablonovat pomocí jazyka Liquid. Pokyny k použití naleznete v {0}. Jedná se o dostupné proměnné:", + "leave blank for default subject": "ponechte prázdné pro výchozí předmět", + "emailCustomBody": "Vlastní tělo", + "leave blank for default body": "ponechte prázdné, pokud chcete použít výchozí tělo", + "emailTemplateServiceName": "Název služby", + "emailTemplateHostnameOrURL": "Název hostitele nebo adresa URL", + "emailTemplateStatus": "Stav", + "emailTemplateMsg": "text oznámení", + "templateHeartbeatJSON": "objekt popisující funkčnost systému (heartbeat)", + "templateLimitedToUpDownCertNotifications": "dostupné pouze pro oznámení Běží/neběží/stav certifikátu", + "emailTemplateMonitorJSON": "objekt popisující dohled", + "emailTemplateHeartbeatJSON": "objekt popisující heartbeat", + "emailTemplateLimitedToUpDownNotification": "dostupné pouze pro heatbeaty BĚŽÍ/NEBĚŽÍ, jinak null", + "templateMonitorJSON": "objekt popisující dohled", + "templateLimitedToUpDownNotifications": "dostupné pouze pro oznámení BĚŽÍ/NEBĚŽÍ", + "successKeyword": "Úspěch Klíčové slovo", + "Search monitored sites": "Hledat v monitorovaných umístěních", + "settingUpDatabaseMSG": "Nastavuje se databáze. Prosím buďte trpělivý, může to chvíli trvat.", + "successKeywordExplanation": "Klíčové slovo MQTT, které bude považováno za úspěch", + "Browser Screenshot": "Snímek obrazovky prohlížeče", + "setup a new monitor group": "nastavení nové skupiny dohledů", + "ntfyPriorityHelptextAllEvents": "Všechny události jsou odesílány s maximální prioritou", + "Remote Browser not found!": "Vzdálený prohlížeč nebyl nalezen!", + "Remove domain": "Odstranit doménu '{0}'", + "Add a Remote Browser": "Přidat vzdálený prohlížeč", + "self-hosted container": "samostatně hostovaný kontejner", + "useRemoteBrowser": "Použít Vzdálený prohlížeč", + "deleteRemoteBrowserMessage": "Opravdu chcete odstranit tento Vzdálený prohlížeč pro všechny dohledy?", + "openModalTo": "otevřít modální okno {0}", + "Add a domain": "Přidat doménu", + "Remote Browsers": "Vzdálené prohlížeče", + "Remote Browser": "Vzdálený prohlížeč", + "Add a new expiry notification day": "Přidání nového oznámení o vypršení platnosti", + "Remove the expiry notification": "Odstranit upozornění na vypršení", + "ntfyPriorityHelptextAllExceptDown": "Všechny události jsou odesílány s touto prioritou, kromě událostí {0}, které mají prioritu {1}", + "What is a Remote Browser?": "Co je to vzdálený prohlížeč?", + "Your User ID": "Vaše uživatelské ID", + "Channel access token (Long-lived)": "Přístupový token kanálu (s dlouhou životností)", + "wayToGetHeiiOnCallDetails": "Jak získat ID spouštěče a klíče API je vysvětleno v {documentation}", + "documentationOf": "{0} Dokumentace", + "gtxMessagingApiKeyHint": "Svůj klíč API najdete v menu: Moje směrovací účty > Zobrazit informace o účtu > Pověření API > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Z telefonního čísla / adresy původu přenosové cesty (TPOA)", + "To Phone Number": "Na telefonní číslo", + "gtxMessagingToHint": "Mezinárodní formát s počátečním \"+\" ({e164}, {e212} nebo {e214})", + "gtxMessagingFromHint": "V mobilních telefonech se příjemci zobrazí TPOA jako odesílatel zprávy. Povoleno je až 11 alfanumerických znaků, zkrácený kód, místní dlouhý kód nebo mezinárodní čísla ({e164}, {e212} nebo {e214})", + "cellsyntOriginatortypeAlphanumeric": "Alfanumerický řetězec (max. 11 alfanumerických znaků). Příjemci nemohou na zprávu odpovědět.", + "Originator type": "Typ původce", + "Alphanumeric (recommended)": "Alfanumerický (doporučeno)", + "Telephone number": "Telefonní číslo", + "cellsyntOriginatortypeNumeric": "Číselná hodnota (max. 15 číslic) s telefonním číslem v mezinárodním formátu bez počátečního 00 (například číslo pro UK 07920 110 000 by mělo být nastaveno jako 447920110000). Příjemci mohou na zprávu odpovědět.", + "Originator": "Původce", + "callMeBotGet": "Zde můžete vygenerovat koncový bod pro {0}, {1} a {2}. Mějte na paměti, že můžete být omezeni rychlostí. Omezení rychlosti vypadají takto: {3}", + "cellsyntOriginator": "Viditelný v mobilním telefonu příjemce jako původce zprávy. Přípustné hodnoty a funkce závisí na parametru originatortype.", + "Destination": "Cíl", + "cellsyntDestination": "Telefonní číslo příjemce v mezinárodním formátu s počátečním číslem 00 a kódem země, např. 00447920110000 pro číslo 07920 110 000 ve Spojeném království (celkem maximálně 17 číslic). Maximálně 25 000 příjemců oddělených čárkou na jeden požadavek HTTP.", + "whapiRecipient": "Telefonní číslo / ID kontaktu / ID skupiny", + "API URL": "API URL", + "Allow Long SMS": "Povolit dlouhé SMS", + "cellsyntSplitLongMessages": "Rozdělit dlouhé zprávy až na 6 částí. 153 x 6 = 918 znaků.", + "max 15 digits": "maximálně 15 číslic", + "max 11 alphanumeric characters": "maximálně 11 alfanumerických znaků", + "remoteBrowserToggle": "Ve výchozím nastavení běží Chromium uvnitř kontejneru Uptime Kuma. Přepnutím tohoto přepínače můžete použít vzdálený prohlížeč.", + "wayToGetWhapiUrlAndToken": "URL rozhraní API a token získáte tak, že přejdete do požadovaného kanálu z {0}", + "wayToWriteWhapiRecipient": "Telefonní číslo s mezinárodní předvolbou, ale bez znaménka plus na začátku ({0}), ID kontaktu ({1}) nebo ID skupiny ({2}).", + "remoteBrowsersDescription": "Vzdálené prohlížeče jsou alternativou k místnímu spuštění Chromu. Nastavte se pomocí služby, jako je browserless.io, nebo se připojte k vlastnímu", + "statusPageSpecialSlugDesc": "Speciální slug {0}: tato stránka se zobrazí, pokud není definován žádný slug", + "Mentioning": "Zmínky", + "wayToGetSevenIOApiKey": "Navštivte ovládací panel v části app.seven.io > developer > api key > zelené tlačítko přidat", + "senderSevenIO": "Číslo nebo jméno odesílatele", + "receiverSevenIO": "Přijímací číslo", + "apiKeySevenIO": "API klíč SevenIO", + "locally configured mail transfer agent": "místně nakonfigurovaný agent pro přenos pošty", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Zadejte název serveru, ke kterému se chcete připojit. Případně zadejte {localhost}, pokud chcete využít {local_mta}", + "receiverInfoSevenIO": "Pokud se přijímající číslo nenachází v Německu, musíte před číslo přidat kód země (např. pro kód země 420 z ČR použijte 420603603603 místo 603603603)", + "Command": "Příkaz", + "mongodbCommandDescription": "Spusťte příkaz MongoDB proti databázi. Informace o dostupných příkazech najdete v {dokumentaci}", + "ignoreTLSErrorGeneral": "Ignorování chyby TLS/SSL u připojení", + "smspartnerApiurl": "Svůj klíč API najdete v ovládacím panelu na adrese {0}", + "smspartnerPhoneNumber": "Telefonní číslo (čísla)", + "smspartnerPhoneNumberHelptext": "Číslo musí být v mezinárodním formátu {0}, {1}. Více čísel musí být odděleno znakem {2}", + "smspartnerSenderName": "Název odesílatele SMS", + "smspartnerSenderNameInfo": "Musí obsahovat 3..=11 běžných znaků", + "Bitrix24 Webhook URL": "Adresa URL webhooku služby Bitrix24", + "wayToGetBitrix24Webhook": "Webhook můžete vytvořit podle pokynů na adrese {0}", + "bitrix24SupportUserID": "Zadejte své uživatelské ID v Bitrix24. ID zjistíte z odkazu, který najdete v profilu uživatele.", + "Refresh Interval": "Interval obnovení", + "Refresh Interval Description": "Stavová stránka provede úplnou obnovu webu každých {0} sekund", + "Select message type": "Zvolte typ zprávy", + "Send to channel": "Poslat na kanál", + "Create new forum post": "Vytvořit nový příspěvek na fóru", + "postToExistingThread": "Příspěvek do existujícího vlákna / příspěvek na fóru", + "forumPostName": "Název příspěvku ve fóru", + "threadForumPostID": "Vlákno / ID příspěvku ve fóru", + "e.g. {discordThreadID}": "např. {discordThreadID}", + "whatHappensAtForumPost": "Vytvořte nový příspěvek ve fóru. Tímto způsobem NENÍ možné odesílat zprávy v existujících příspěvcích. Pro vložení příspěvku do existujícího příspěvku použijte \"{option}\"", + "wayToGetDiscordThreadId": "Získání ID vlákna / příspěvku ve fóru je podobné získání ID kanálu. Přečtěte si více o tom, jak získat id {0}", + "Don't mention people": "Nezmiňujte se o osobách", + "Mention group": "Zmínka o {skupině}", + "Host URL": "URL hosta", + "threemaRecipientType": "Typ příjemce", + "threemaRecipient": "Příjemce", + "threemaSenderIdentityFormat": "8 znaků, obvykle začíná na *", + "threemaRecipientTypeEmail": "Emailová adresa", + "threemaSenderIdentity": "ID brány", + "threemaApiAuthenticationSecret": "Tajný klíč ID brány", + "apiKeysDisabledMsg": "Klíče API jsou zakázány, protože je zakázáno ověřování.", + "threemaBasicModeInfo": "Poznámka: Tato integrace využívá bránu Threema v základním režimu (šifrování pomocí serveru). Další podrobnosti naleznete {0}.", + "threemaRecipientTypeIdentityFormat": "8 znaků", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypePhone": "Telefonní číslo", + "wayToGetThreemaGateway": "Můžete se zaregistrovat do služby brány Threema {0}.", + "privateOnesenderDesc": "Zkontrolujte, zda je telefonní číslo platné. Odeslání zprávy na soukromé telefonní číslo, např.: 628123456789", + "wayToGetOnesenderUrlandToken": "Adresu URL a token získáte na webových stránkách společnosti Onesender. Více informací {0}", + "now": "nyní", + "time ago": "před {0}", + "-year": "-rok", + "Json Query Expression": "Výraz dotazu v JSON", + "and": "a", + "cacheBusterParam": "Přidání parametru {0}", + "cacheBusterParamDescription": "Náhodně generovaný parametr pro vynechání mezipaměti.", + "OID (Object Identifier)": "OID (identifikátor objektu)", + "Condition": "Stav", + "SNMP Version": "Verze SNMP", + "Please enter a valid OID.": "Zadejte prosím platný identifikátor OID.", + "Recipient Type": "Typ příjemce", + "Private Number": "Soukromé číslo", + "Group ID": "ID skupiny", + "Add Remote Browser": "Přidat vzdálený prohlížeč", + "New Group": "Nová skupina", + "Group Name": "Název skupiny", + "OAuth2: Client Credentials": "OAuth2: přihlašovací údaje klienta", + "Authentication Method": "Metoda ověřování", + "Authorization Header": "Hlavička autorizace", + "Form Data Body": "Tělo formuláře s daty", + "threemaRecipientTypePhoneFormat": "E.164, bez počátečního +", + "jsonQueryDescription": "Pro zpracování a získání konkrétních dat z JSON odpovědi serveru použijte JSON dotaz - případně \"$\" pro zdrojovou (raw) odpověď, pokud neočekáváte JSON výstup. Výsledek bude následně porovnán jako řetězec vůči očekávaní hodnotě. Dokumentaci naleznete na {0} a pro testování dotazů můžete využít {1}.", + "shrinkDatabaseDescriptionSqlite": "Podmínka spuštění příkazu {vacuum} nad SQLite databází. Příkaz {auto_vacuum} je již zapnutý, ale nedochází k defragmentaci databáze ani k přebalení jednotlivých stránek databáze tak, jak to dělá příkaz {vacuum}.", + "Community String": "Řetězec komunity", + "Host Onesender": "Onesender hostitel", + "Token Onesender": "Onesender token", + "snmpOIDHelptext": "Zadejte OID senzoru nebo stavu, který chcete monitorovat. Pokud si nejste jisti identifikátorem OID, použijte nástroje pro správu sítě, jako jsou prohlížeče MIB nebo SNMP software.", + "snmpCommunityStringHelptext": "Tento řetězec slouží jako heslo pro ověřování a řízení přístupu k zařízením podporujícím protokol SNMP. Shodujte se s konfigurací zařízení SNMP.", + "record": "záznam", + "Go back to home page.": "Vrátit se domovskou stránku.", + "No tags found.": "Nenalezeny žádné štítky.", + "Lost connection to the socket server.": "Ztraceno socketové spojení se serverem.", + "Cannot connect to the socket server.": "Nelze navázat socketové spojení se serverem.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "URL adresa webhooku SIGNL4", + "signl4Docs": "Další informace související s konfigurací SIGNL4 a postup jak získat URL webhooku SIGNL4 naleznete na {0}.", + "Conditions": "Podmínky", + "conditionAdd": "Přidat podmínku", + "conditionDelete": "Vymazat podmínku", + "conditionAddGroup": "Přidat skupinu", + "conditionDeleteGroup": "Smazat skupinu", + "conditionValuePlaceholder": "Hodnota", + "equals": "rovná se", + "not equals": "nerovná se", + "contains": "obsahuje", + "not contains": "neobsahuje", + "starts with": "začíná na", + "not starts with": "nezačíná na", + "ends with": "končí na", + "not ends with": "nekončí na", + "less than": "menší než", + "greater than": "větší než", + "less than or equal to": "menší nebo rovno", + "greater than or equal to": "větší nebo rovno", + "groupOnesenderDesc": "Ujistěte se, že jste zadali platné GroupID. Pro odeslání zprávy do skupiny zadejte například 628123456789-342345", + "OAuth Token URL": "URL OAuth tokenu", + "Client ID": "ID klienta", + "Client Secret": "Tajemství klienta", + "OAuth Scope": "OAuth rozsah", + "Optional: Space separated list of scopes": "Volitelné: seznam rozsahů oddělte mezerami", + "Debug": "Ladění", + "CopyToClipboardError": "Nelze zkopírovat do schránky: {error}", + "ignoredTLSError": "Chyby TLS/SSL byly ignorovány", + "Copy": "Kopírovat", + "aboutSlackUsername": "Mění zobrazované jméno odesílatele zprávy. Pokud někoho chcete zmínit, použijte raději pole obecný název.", + "Message format": "Formát zprávy", + "Notification Channel": "Kanál notifikací", + "Alphanumerical string and hyphens only": "Pouze alfanumerické řetězce a pomlčky", + "Sound": "Zvuk", + "Custom sound to override default notification sound": "Použít vlastní zvuk místo výchozího zvuku upozornění", + "Correct": "Správně", + "Fail": "Chyba", + "Reveal": "Odhalení", + "Doorbell": "Zvonek", + "Flute": "Flétna", + "Money": "Peníze", + "Elevator": "Výtah", + "Guitar": "Kytara", + "Time Sensitive (iOS Only)": "Časově kritické (pouze pro iOS)", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Časově kritická upozornění budou doručena okamžitě, i když je zařízení v režimu nerušit.", + "From": "Od", + "Can be found on:": "Lze nalézt na: {0}", + "The phone number of the recipient in E.164 format.": "Telefonní číslo příjemce ve formátu E.164 (+420...).", + "RabbitMQ Nodes": "RabbitMQ uzly pro správu", + "rabbitmqNodesDescription": "URL RabbitMQ uzlů pro správu zadávejte včetně protokolu a portu. Příklad: {0}", + "rabbitmqNodesRequired": "Prosím, definujte uzly pro tento dohled.", + "rabbitmqNodesInvalid": "Prosím, použijte plně kvalifikované URL (začínající na 'http)' RabbitMQ uzlů.", + "RabbitMQ Username": "RabbitMQ uživatelské jméno", + "Send rich messages": "Poslat rozsáhlé zprávy", + "Arcade": "Arkáda", + "Harp": "Harfa", + "Bubble": "Bublinky", + "Scifi": "Scifi", + "Pop": "Prasknutí", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Pro zajištění příjmu odpovědí zadejte ID odesílatele textu nebo telefonní číslo ve formátu E.164.", + "RabbitMQ Password": "RabbitMQ heslo", + "rabbitmqHelpText": "Abyste mohli používat tento monitor, musíte v nastavení RabbitMQ povolit modul pro správu. Další informace naleznete na adrese {rabitmq_documentation}.", + "SendGrid API Key": "SendGrid API klíč", + "Separate multiple email addresses with commas": "Více e-mailových adres oddělte čárkami", + "Clear": "Odstranění", + "Ip Family": "Rodina IP protokolů", + "ipFamilyDescriptionAutoSelect": "Pro určení rodiny IP protokolů se využívá {happyEyeballs}.", + "Clear Form": "Vymazat formulář", + "Happy Eyeballs algorithm": "Happy Eyeballs algoritmus", + "pause": "Pauza", + "Manual": "Ručně", + "Plain Text": "Prostý text", + "pingGlobalTimeoutLabel": "Globální časový limit", + "pingGlobalTimeoutDescription": "Celková doba v sekundách před zastavením pingu, bez ohledu na počet odeslaných paketů", + "pingPerRequestTimeoutLabel": "Časový limit pingu", + "pingIntervalAdjustedInfo": "Interval upravený na základě počtu paketů, globálního časového limitu a časového limitu pro ping", + "Custom URL": "Vlastní URL", + "customUrlDescription": "Použije se jako hypertextový odkaz místo adresy monitoru.", + "OneChatAccessToken": "OneChat Přístupový token", + "OneChatUserIdOrGroupId": "OneChat ID uživatele nebo ID skupiny", + "OneChatBotId": "OneChat ID bota", + "wahaSession": "Relace", + "wahaChatId": "ID chatu (telefonní číslo / ID kontaktu / ID skupiny)", + "wayToGetWahaApiUrl": "URL vaší WAHA instance.", + "wayToGetWahaApiKey": "API klíč je hodnota proměnné prostředí WHATSAPP_API_KEY, kterou jste použili při spuštění WAHA.", + "Message Template": "Šablony zprávy", + "Template Format": "Formát zprávy", + "smsplanetApiDocs": "Detailní informace týkající se získání API tokenů naleznete v {the_smsplanet_documentation}.", + "the smsplanet documentation": "dokumentaci smsplanet", + "Phone numbers": "Telefonní čísla", + "Sender name": "Jméno odesílatele", + "smsplanetNeedToApproveName": "Je vyžadováno schválení v klientském portále", + "Disable URL in Notification": "Zakázat URL v oznámeních", + "Add Another Tag": "Přidat další štítek", + "Staged Tags for Batch Add": "Připravené štítky pro hromadné přidání", + "ntfyPriorityDown": "Priorita pro DOWN-události", + "pingNumericLabel": "Číselný výstup", + "Font Twemoji by Twitter licensed under": "Písmo Twemoji od Twitteru je pod licencí", + "smsplanetApiToken": "Token pro SMSPlanet API", + "tagAlreadyStaged": "Tento štítek (název a hodnota) je již pro tuto dávku připraven.", + "telegramUseTemplateDescription": "Po aktivování této možnosti bude při odeslání zprávy použita vlastní šablona.", + "telegramServerUrlDescription": "Pro zrušení omezení api botů Telegramu nebo získání přístupu v blokovaných oblastech (Čína, Írán, atp.). Pro více informací klikněte na {0}. Výchozí nastavení: {1}", + "Use HTML for custom E-mail body": "Použít HTML v těle vlastního e-mailu", + "ntfyPriorityHelptextPriorityHigherThanDown": "Pravidelná priorita by měla být vyšší než priorita {0}. Priorita {1} je vyšší než {0} s prioritou {2}", + "OAuth Audience": "Cílová skupina OAuth", + "Optional: The audience to request the JWT for": "Volitelné: Audience, pro které se má vyžádat JWT", + "pingCountDescription": "Počet paketů, které se před zastavením odešlou", + "pingNumericDescription": "Pokud je tato možnost aktivní, místo symbolických názvů hostitelů se zobrazí IP adresy", + "pingPerRequestTimeoutDescription": "Jedná se o maximální dobu čekání (v sekundách) před tím, než bude konkrétní ping paket považován za ztracený", + "smtpHelpText": "Pomocí možnosti 'SMTPS' se ověří funkčnost protokolu SMTP/TLS; výběrem ‚Ignorovat TLS‘ se naváže něšifrované spojení; po vybrání 'STARTTLS‘ se odešle příkaz STARTTLS a dojde k ověření certifikátu serveru. Ani jeden z těchto režimů neinicializuje odeslání e-mailu.", + "wayToGetWahaSession": "Z této relace WAHA odesílá oznámení do ID chatu. Najdete ho v nástěnce WAHA.", + "wayToWriteWahaChatId": "Telefonní číslo s mezinárodní předvolbou, ale bez znaménka plus na začátku ({0}), ID kontaktu ({1}) nebo ID skupiny ({2}). Oznámení jsou z relace WAHA odesílána na toto ID chatu.", + "telegramServerUrl": "(Volitelné) Adresa serveru", + "YZJ Robot Token": "Token robota YZJ", + "YZJ Webhook URL": "YZJ URL webhooku", + "Add Tags": "Přidat štítky", + "tagAlreadyOnMonitor": "Dohled již má přiřazen tento štítek (název a hodnota) nebo se čeká na jeho přidání.", + "tagNameExists": "Systémový štítek s tímto názvem již existuje. Vyberte jej ze seznamu nebo použijte jiný název.", + "telegramUseTemplate": "Použít vlastní šablonu zprávy", + "telegramTemplateFormatDescription": "Telegram umožňuje ve zprávách používat různé značkovací jazyky, více informací naleznete v Telegram {0}.", + "smseagleDocs": "Zkontrolujte dokumentaci nebo dostupnost APIv2: {0}", + "smseagleComma": "Více záznamů oddělte čárkou", + "SpugPush Template Code": "Kód šablony", + "FlashDuty Push URL": "Push URL", + "FlashDuty Push URL Placeholder": "Zkopírovat ze stránky integrace upozornění", + "pingCountLabel": "Max. počet paketů", + "templateServiceName": "název služby", + "templateHostnameOrURL": "název hostitele nebo URL", + "templateStatus": "stav", + "defaultFriendlyName": "Nový dohled", + "smseagleContactV2": "ID kontaktu z telefonního seznamu", + "smseagleGroupV2": "ID skupin(y) telefonního seznamu", + "smseagleMsgType": "Typ zprávy", + "smseagleMsgSms": "SMS zprávy (výchozí)", + "smseagleMsgRing": "Vyzvánění", + "smseagleMsgTts": "Hovor s převodem textu na řeč", + "smseagleMsgTtsAdvanced": "Rozšířený hovor s převodem textu na řeč", + "smseagleDuration": "Doba (v sekundách)", + "smseagleTtsModel": "ID modelu převodu textu na řeč", + "smseagleApiType": "Verze API", + "smseagleApiv1": "APIv1 (pro stávající projekty a zpětnou kompatibilitu)", + "smseagleApiv2": "APIv2 (doporučeno pro nové integrace)", + "Path": "Cesta", + "mqttWebsocketPathInvalid": "Použijte, prosím platný formát WebSocket cesty", + "mqttHostnameTip": "Použijte, prosím tento formát {hostnameFormat}", + "mqttWebSocketPath": "MQTT WebSocket cesta", + "mqttWebsocketPathExplanation": "WebSocket cesta pro MQTT prostřednictvím WebSocket spojení (např. /mqtt)", + "Template plain text instead of using cards": "Šablona prostého textu namísto použití karet", + "issueWithGoogleChatOnAndroidHelptext": "Tím se můžete rovněž vyhnout chybám v upstreamu jako je {issuetackerURL}", + "clearAllEventsMsg": "Opravdu chcete odstranit všechny události ze všech dohledů? Tím dojde k trvalému smazání souvisejících dat zpráv a tento úkon nelze vrátit zpět.", + "Events cleared successfully": "Události byly vymazány.", + "No monitors found": "Nebyl nalezen žádný dohled.", + "brevoApiHelp": "API klíč vytvoříte na: {0}", + "brevoFromEmail": "E-mail odesílatele", + "brevoFromName": "Jméno odesílatele", + "brevoLeaveBlankForDefaultName": "nechte prázdné pro výchozí název", + "brevoToEmail": "E-mail příjemce", + "brevoBccEmail": "Skrytá kopie", + "brevoCcEmail": "Kopie", + "brevoSubject": "Předmět", + "brevoLeaveBlankForDefaultSubject": "nechte prázdné pro výchozí předmět", + "Clear All Events": "Vymazat všechny události", + "Could not clear events": "Nepodařilo se vymazat {failed}/{total} událostí", + "brevoApiKey": "Brevo API klíč", + "brevoSeparateMultipleEmails": "Více e-mailových adres oddělte čárkami", + "wayToWriteEvolutionRecipient": "Telefonní číslo s mezinárodní předvolbou, ale bez znaménka plus na začátku ({0}), ID kontaktu ({1}) nebo ID skupiny ({2}).", + "evolutionRecipient": "Telefonní číslo / ID kontaktu / ID skupiny", + "evolutionInstanceName": "Název instance", + "wayToGetEvolutionUrlAndToken": "API URL a token získáte tak, že přejdete do požadovaného kanálu z {0}", + "Nextcloud host": "Nextcloud hostitel", + "Conversation token": "Token konverzace", + "Bot secret": "Heslo bota", + "Send UP silently": "UP odeslat tiše", + "Send DOWN silently": "DOWN odeslat tiše", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Instalace Nextcloud Talk bota vyžaduje administrátorský přístup k serveru.", + "Maximum Retries": "Max. počet pokusů", + "Number of retry attempts if webhook fails": "Počet pokusů o opakování (každých 60–180 sekund), pokud se webhook nepodaří odeslat.", + "wayToGetBaleToken": "Token můžete získat na adrese {0}.", + "wayToGetBaleChatID": "ID chatu získáte zasláním zprávy robotovi a přejdete na tuto URL adresu, na které se zobrazí chat_id:", + "supportBaleChatID": "Podpora přímého chatu / skupiny / ID chatu kanálu", + "Dingtalk Mobile List": "Seznam telefonních čísel", + "Dingtalk User List": "Seznam ID uživatelů", + "Mention Mobile List": "Seznam Mention telefonních čísel", + "Mention User List": "Seznam ID uživatelů Mention", + "Enter a list of userId": "Zadejte seznam userID", + "Enter a list of mobile": "Zadejte seznam telefonních čísel", + "Invalid mobile": "Neplatné telefonní číslo [{mobile}]", + "Invalid userId": "Neplatné userID [{userId}]", + "auto-select": "Automatický výběr", + "webhookPostMethodDesc": "POST je vhodný pro většinu moderních HTTP serverů.", + "descriptionHelpText": "Zobrazí se na interní nástěnce. Markdown je povolen a před zobrazením dojde k jeho očištění (zůstanou zachovány mezery a odsazení).", + "HTTP Method": "HTTP metoda", + "webhookGetMethodDesc": "GET odesílá data jako parametry dotazu a neumožňuje konfiguraci těla. Užitečné pro spouštění Uptime Kuma Push dohledů.", + "Recipient Numbers": "Počet příjemců", + "deleteGroupMsg": "Opravdu chcete smazat tuto skupinu?", + "deleteChildrenMonitors": "Tímto dojde rovněž k odstranění přímých potomků tohoto dohledu a jejich potomků, pokud nějaké mají | Tímto dojde rovněž k odstranění všech {count} přímých potomků tohoto dohledu a jejich potomků, pokud nějaké mají", + "Template ID": "ID šablony", + "wayToGetClickSMSIRTemplateID": "Vaše šablona musí obsahovat pole {uptkumaalert}. Novou šablonu můžete vytvořit {here}.", + "Clone Maintenance": "Duplikovat údržbu", + "ariaPauseMaintenance": "Pozastavit tento plán údržby", + "ariaResumeMaintenance": "Pokračovat v tomto plánu údržby", + "ariaCloneMaintenance": "Vytvořit kopii tohoto plánu údržby", + "ariaEditMaintenance": "Upravit tento plán údržby", + "ariaDeleteMaintenance": "Odstranit tento plán údržby", + "ignoreSecWebsocketAcceptHeaderDescription": "Při úspěšném websocket upgrade se po aktivování této možnosti se nebude v odpovědi používat hlavička Sec-WebSocket-Accept.", + "Ignore Sec-WebSocket-Accept header": "Ignorovat hlavičku {0}", + "Resolver Server(s)": "Resolver server(y)", + "saveResponseDescription": "Uloží HTTP odpověď a umožní ji použít v šablonách oznámení jako {templateVariable}", + "responseMaxLength": "Maximální délka odpovědi (v bajtech)", + "saveErrorResponseForNotifications": "Uložit chybovou odpověď HTTP pro oznámení", + "saveResponseForNotifications": "Uložit úspěšnou odpověď HTTP pro oznámení", + "responseMaxLengthDescription": "Maximální velikost dat odpovědi, která se mají uložit. Nastavte na 0 pro neomezenou velikost. Větší odpovědi budou zkráceny. Výchozí hodnota: 1024 (1 KB)", + "sipsakPingWarning": "Abyste mohli používat dohled SIP Options Ping, musíte si na server nainstalovat Uptime Kuma mimo Docker a také klienta Sipsak.", + "HeadersInvalidFormatBecause": "Hlavička požadavku není platný JSON. Důvod: {error}", + "BodyInvalidFormatBecause": "Tělo požadavku není platný JSON. Důvod: {error}", + "steamApiKeyDescriptionAt": "Pro monitorování herního serveru Steam potřebujete klíč Steam Web-API. Svůj klíč API si můžete zaregistrovat na adrese {url}", + "checkPriceAt": "Ceny {service} si zjistěte na {url}", + "Select All": "Vybrat vše", + "resendApiKey": "Znovu zaslat API klíč", + "unknownDays": "Neznámý počet dní", + "Monitors": "{n} dohled| {n} dohledy | {n} dohledů", + "serwersmsRecipientType": "Typ příjemce", + "serwersmsRecipientTypePhone": "Telefonní číslo", + "serwersmsRecipientTypeGroup": "Skupina", + "serwersmsGroupId": "ID skupiny", + "resendFromName": "Jméno odesílatele", + "noMonitorsSelectedWarning": "Vytváříte údržbu bez dotčených dohledů. Opravdu chcete pokračovat?", + "noMonitorsOrStatusPagesSelectedError": "Nelze vytvořit údržbu bez dotčených dohledů nebo stavových stránek", + "serwersmsGroupIdHelptext": "ID nebo ID skupiny v Zákaznickém panelu. Tyto identifikátory můžete stáhnout pomocí hromadných akcí / indexu nebo jejich zkopírováním při úpravě skupiny v Zákaznickém panelu.", + "systemService": "Systémová služba", + "systemServiceDescriptionLinux": "Zkontroluje, zda je Linuxová systemd služba {service_name} aktivní", + "systemServiceName": "Název služby", + "systemServiceDescriptionWindows": "Zkontroluje, zda Windows Service Manager {service_name} běží", + "message": "zpráva", + "Press Enter to add node": "Stiskněte Enter pro přidání uzlu", + "Enter the list of nodes": "Zadejte seznam RabbitMQ uzlů pro správu", + "resendApiHelp": "API klíč si vytvořte na {0}", + "Allow Notifications": "Povolit oznámení", + "Badge Link Generator": "{0} generátor odkazů odznaků", + "Open Badge Link Generator": "Otevřít generátor odkazů odznaků", + "Badge Link Generator Helptext": "Odkazy na odznaky jsou k dispozici pro všechny dohledy přiřazené veřejným stavovým stránkám. Další informace naleznete v {documentation}.", + "resendToEmail": "Komu", + "resendLeaveBlankForDefaultName": "nechte prázdné pro výchozí jméno", + "resendSubject": "Předmět", + "wsCodeDescription": "Další informace o stavových kódech naleznete v {rfc6455}", + "Subprotocol(s)": "Podprotokol(y)", + "twilloMessagingServiceSIDHelptext": "Pokud ke správě odesílatelů a funkcí používáte {twillo_messaging_service_help_link}, zadejte zde SID vaší služby pro zasílání zpráv", + "wsSubprotocolDescription": "Zadejte seznam podprotokolů oddělených čárkami. Další informace o podprotokolech naleznete v{documentation}", + "Browser not supported": "Nepodporovaný prohlížeč", + "invalidHostnameOrIP": "Neplatný název hostitele nebo IP adresa. Název hostitele musí být platné plně kvalifikovaný doménový název (FQDN). Nelze použít zástupné znaky. Může obsahovat podtržítko nebo končit tečkou.", + "invalidDNSHostname": "Neplatný název hostitele. Název hostitele musí být platné plně kvalifikovaný doménový název (FQDN). Nelze použít zástupné znaky. Může obsahovat podtržítko nebo končit tečkou.", + "hostnameCannotBeIP": "Název DNS hostitele nemůže být IP adresa. Měli jste v úmyslu použít pole resolver?", + "invalidURL": "Neplatná URL", + "wildcardOnlyForDNS": "Názvy hostitelů se zástupnými znaky jsou podporovány pouze pro monitory DNS.", + "Use STARTTLS": "Používat STARTTLS", + "Google": "Google", + "Plausible": "Plausible", + "RSS Title": "RSS titulek", + "Leave blank to use status page title": "Ponechte prázdné pro převzetí názvu stavové stránky", + "Duration (Minutes)": "Doba (v minutách)", + "notificationUniversal": "Univerzální", + "notificationChatPlatforms": "Chatovací platformy", + "notificationPushServices": "Push služby", + "SMTP Security": "SMTP zabezpečení", + "notificationOther": "Ostatní integrace", + "systemServiceCommandHint": "Použitý příkaz: {command}", + "twilioApiKeyHelptext": "API klíč je volitelný, ale doporučován. Můžete zadat buď SID účtu a autorizační token z TwilioConsole, nebo SID účtu a dvojici klíčů API a tajných klíčů API", + "twilioMessagingServiceSID": "SID služby pro zasílání zpráv (volitelné)", + "resendLeaveBlankForDefaultSubject": "Nechte prázdné pro výchozí předmět", + "OptionalParameters": "Volitelné parametry", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "S ohledem na omezení operátora zvyšuje použití volitelných proměnných riziko nedoručení", + "aliyun-template-requirements-and-parameters": "Šablona SMS zprávy Aliyun musí obsahovat tyto parametry: {parameters}", + "Umami": "Umami", + "Matomo": "Matomo", + "Suppress Notifications": "Potlačit oznámení", + "discordSuppressNotificationsHelptext": "Při zapnuté této možnosti se zprávy budou zasílat do kanálu, ale příjemcům se nezobrazí push oznámení ani oznámení na ploše.", + "Notifications Enabled": "Oznámení jsou zapnutá", + "Actions": "Akce", + "createdAt": "Vytvořeno: {date}", + "lastUpdatedAt": "Poslední aktualizace: {date}", + "logoutCurrentUser": "Odhlásit {username}", + "Certificate Chain:": "Řetězec certifikátu:", + "dateCreatedAtFromNow": "Datum vytvoření: {date} ({fromNow})", + "Examples:": "Příklady: {0}", + "certHostnameMismatch": "Název hostitele certifikátu neodpovídá URL dohledu.", + "cronScheduleDescription": "Plán: {description}", + "notificationSmsServices": "SMS služby", + "Ignore STARTTLS": "Ignorovat STARTTLS", + "bulkDeleteErrorMsg": "Nepodařilo se odstranit {n} dohled| Nepodařilo se odstranit {n} dohledy | Nepodařilo se odstranit {n} dohledů", + "versionIs": "Verze: {version}", + "lastUpdatedAtFromNow": "Poslední aktualizace: {date} ({fromNow})", + "selectedMonitorCountMsg": "vybráno: {n} | vybráno: {n}", + "selectMonitorMsg": "Vyberte dohledy, nad kterými chcete provést akci", + "frontendVersionIs": "Verze frontendu: {version}", + "deletedMonitorsMsg": "Odstraněn {n} dohled| Odstraněny {n} dohledy | Odstraněno {n} dohledů", + "octopushEndpoint": "octopush (koncový bod: {url})", + "legacyOctopushEndpoint": "Legacy Octopush-DM (koncový bod: {url})", + "Only retry if status code check fails": "Opakovat pouze v případě selhání kontroly stavového kódu", + "retryOnlyOnStatusCodeFailureDescription": "Při zapnutí této možnosti se opakované pokusy provedou pouze v při neúspěšné kontrole stavového kódu HTTP (např. server neběží). Pokud kontrola stavového kódu proběhne úspěšně, ale selže JSON dotaz, dohled bude okamžitě označen jako mimo provoz bez opakovaných pokusů.", + "selectAllMonitorsAria": "Vybrat všechny dohledy", + "deselectAllMonitorsAria": "Zrušit výběr všech dohledů", + "notificationEmail": "E-mail", + "notificationIncidentManagement": "Správa mimořádných údálostí", + "resumedMonitorsMsg": "Obnoven {n} dohled| Obnoveny {n} dohledy | Obnoveno {n} dohledů", + "You can divide numbers with commas or semicolons": "Čísla můžete oddělit pomocí {comma} nebo {semicolon}", + "showOnlyLastHeartbeat": "Zobrazit pouze poslední heartbeat", + "Analytics Type": "Typ analytiky", + "Analytics Script URL": "URL skriptu analytiky", + "Analytics ID": "ID analytiky", + "systemServiceExpectedOutput": "Očekávaný výstup: \"{0}\"", + "enableSSL": "Používat SSL/TLS", + "mariadbUseSSLHelptext": "Zapnutím této možnosti se pro připojení do vaší databáze použije šifrované spojení. Toto je vyžadováno pro většinu cloudových databází.", + "mariadbCaCertificateLabel": "Certifikát CA", + "mariadbCaCertificateHelptext": "Vložte certifikát CA ve formátu PEM, abyste mohli použít self-signed certifikáty / vydané vaší autoritou. Pokud vaše databáze používá certifikát podepsaný veřejnou certifikační autoritou, ponechte pole prázdné.", + "systemServiceDescription": "Zkontroluje, zda je systémová služba {service_name} aktivní", + "Deselect All": "Zrušit výběr", + "hours": "{n} hodina | {n} hodiny | {n} hodin", + "days": "{n} den | {n} dny | {n} dní", + "minutes": "{n} minuta | {n} minuty | {n} minut", + "minuteShort": "{n} min | {n} min", + "years": "{n} rok | {n} roky | {n} let", + "notificationHomeAutomation": "Domácí automatizace", + "deleteMonitorsMsg": "Opravdu chcete smazat vybrané dohledy?", + "pausedMonitorsMsg": "Pozastaven {n} dohled | Pozastaveny {n} dohledy | Pozastaveno {n} dohledů", + "Please set start time first": "Prosím, nejprve zadejte čas spuštění", + "noMonitorsPausedMsg": "Nebyly pozastaveny žádné dohledy (žádné nebyly aktivní)", + "noMonitorsResumedMsg": "Nebyly obnoveny žádné dohledy (žádné nebyly neaktivní)", + "Sets end time based on start time": "Nastaví čas ukončení na základě času spuštění", + "aliyun-template-optional-parameters": "Volitelné parametry: {parameters}", + "json_value": "JSON hodnota", + "resendFromEmail": "E-mailová adresa odesílatele", + "ntfyCall": "Telefonní hovor", + "ntfyCallHelptext": "Zavolat při aktivaci upozornění. Nastavte na hodnotu 'ano', pro použití prvního ověřeného čísla, nebo zadejte konkrétní telefonní číslo (např. +12223334444). Vyžaduje ntfy Pro a ověřené telefonní číslo.", + "domain_expiry_unsupported_monitor_type": "Sledování platnosti domény není podporované pro tento typ dohledu", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" je IP adresa. Pro sledování konce platnosti domény je nutné zadat doménový název domény", + "minimumIntervalWarning": "Intervaly nižší než 20 sekund mohou vést ke snížení výkonu.", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "URL Halo PSA webhooku", + "username": "Uživatelské jméno", + "halopsa_webhook_url_desc": "Zadejte URL adresu webhooku z vašeho Runbooku pro integraci Halo PSA (Konfigurace > Integrace > Vlastní integrace > Runbooky pro integraci). Při vytváření webhooku vyberte možnost 'Lze spustit pouze z Halo a z veřejného koncového bodu'.", + "halopsa_password_desc": "Heslo pro ověření Halo PSA webhooku", + "Webpush Helptext": "Web push funguje pouze při použití SSL (HTTPS) spojení. Na iOS zařízeních je nutné webovou stránku nejprve přidat na domovskou obrazovku.", + "labelDomainExpiry": "Expirace Domény", + "settingsDomainExpiry": "Platnost domény", + "labelDomainNameExpiryNotification": "Oznámení na blížící se konec platnosti doménového jména", + "domainExpiryDescription": "Upozornit, pokud platnost doménového jména končí za:", + "domain_expiry_unsupported_missing_target": "Pro tento dohled nebyl zadán platný název domény nebo název hostitele", + "password": "Heslo", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" je pro doménu nejvyšší úrovně (TLD) příliš krátké", + "lowIntervalWarning": "Opravdu chcete nastavit hodnotu intervalu pod 20 sekund? Může dojít ke snížení výkonu, zejména při velkém počtu dohledů.", + "imageResetConfirmation": "Obnovení výchozího nastavení obrázku", + "domain_expiry_unsupported_is_icann": "Doména „{domain}“ není kandidátem pro dohled nad vypršením platnosti domény, protože její veřejná přípona „.{publicSuffix}“ není spravována organizací ICANN", + "Unable to get permission to notify": "Nepodařilo se získat oprávnění pro odeslání upozornění (žádost byla buď zamítnuta, nebo ignorována).", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Sledování konce platnosti domény není dostupné pro \".{publicSuffix}\" domény, protože IANA neposkytuje žádnou RDAP službu", + "halopsa_username_desc": "Uživatelské jméno pro ověření Halo PSA webhooku", + "screenshot of the website": "Screenshot webové stránky", + "Endpoint": "Koncový bod", + "Setup Instructions": "Instrukce pro nastavení", + "halopsa_setup_step1": "Vytvořte integračního runbook v HaloPSA (Konfigurace → Integrace → Integrační runbooky)", + "halopsa_setup_step3": "Zkopírujte URL webhooku a vložte ji nad textové pole", + "halopsa_setup_step4": "Vyberte základní ověřování a vytvořte uživatelské jméno a heslo. Uživatelské jméno a heslo zadejte nebo vložte nad testovací pole", + "mtls-auth-server-cert-placeholder": "Tělo certifikátu", + "mtls-auth-server-key-label": "Klíč", + "Sort options": "Možnosti řazení", + "Splunk Rest URL": "URL Splunk Rest", + "Sort by certificate expiry": "Seřadit podle platnosti certifikátu", + "System Service": "Systémová služba", + "Basic radio toggle button group": "Skupina základních přepínačů", + "Basic checkbox toggle button group": "Skupina základních zaškrtávacích políček", + "mtls-auth-server-ca-placeholder": "Server CA", + "Clear current filters": "Zrušit aktuální filtry", + "Sort by status": "Seřadit podle stavu", + "Message Format": "Formát zprávy", + "PushDeer Server URL": "URL PushDeer serveru", + "To Number": "Komu (číslo)", + "GrafanaOncallURL": "URL Grafana Oncall", + "Metadata": "Metadata", + "Details": "Detaily", + "mtls-auth-server-ca-label": "CA", + "mtls-auth-server-key-placeholder": "Tělo klíče", + "Sort by name": "Seřadit podle názvu", + "TLS Alerts": "TLS upozornění", + "Expected TLS Alert": "Očekávané TLS upozornění", + "None (Successful Connection)": "Žádné (úspěšné spojení)", + "TLS Alert Spec": "RFC 8446", + "Never": "Nikdy", + "playground": "hřiště", + "Severity": "Závažnost", + "expectedTlsAlertDescription": "Vyberte upozornění TLS, která má server vracet. Pomocí kódu {code} ověřte, zda mTLS koncové body zamítají spojení bez klientských certifikátů. Podrobnosti naleznete na odkaze {link}.", + "Region": "Region", + "SSL/TLS": "SSL/TLS", + "Check Type": "Typ kontroly", + "GRPC Options": "Možnosti GRPC", + "Sort by uptime": "Seřadit podle doby běhu", + "Show this Maintenance Message on which Status Pages": "Zobrazit tuto zprávu o údržbě na následujících stavových stránkách", + "mtls-auth-server-cert-label": "Certifikát", + "smscTranslit": "smscTranslit", + "Service Name": "Název služby", + "End": "Konec", + "avgPing": "Prům. ping", + "maxPing": "Max. ping", + "minPing": "Min. ping", + "halopsa_setup_step2": "Nastavte akce runbooku pro zpracování upozornění (např. Vytvoření tiketu)", + "passwordTooWeak": "Heslo je příliš slabé. Mělo by obsahovat písmena a číslice. Musí být alespoň 6 znaků dlouhé.", + "snmpV3Username": "SNMPv3 uživatelské jméno", + "WeCom Mentioned Mobile List": "WeCom seznam mobilních zmínek", + "WeCom Mentioned Mobile List Description": "Zadejte telefonní čísla, která chcete zmínit. Více čísel oddělte čárkami. Pro zmínku všem použijte {'@'}all.", + "screenshotDelayWarning": "Při vyšších hodnotách bude prohlížeč otevřený déle, což může zvýšení spotřeby paměti při mnoha souběžných dohledech.", + "Screenshot Delay": "Prodleva před pořízením (počkat {milliseconds})", + "milliseconds": "{n} millisekunda | {n} millisekundy | {n} millisekund", + "screenshotDelayDescription": "Volitelně se může před pořízením snímku obrazovky počkat tento počet milisekund. Maximálně: {maxValueMs}ms (0,5 × interval).", + "Expand All Groups": "Rozbalit všechny skupiny", + "Collapse All Groups": "Sbalit všechny skupiny", + "mariadbSocketPathDetectedHelptext": "Připojování k databázi dle instrukcí definovaných v proměnném prostředí {0}.", + "Past Incidents": "Dřívější mimořádné události", + "Incident title": "Název mimořádné události", + "No incidents recorded": "Nebyly zaznamenány žádné mimořádné události", + "example": "Příklad", + "Copy to Clipboard": "Zkopírovat do schránky", + "Copied to clipboard!": "Zkopírováno do schránky!", + "Incident description": "Popis mimořádné události", + "Incident not found or access denied": "Mimořádná událost nenalezena nebo nemáte přístup", + "templateAvailableVariables": "Dostupné proměnné", + "Result": "Výsledek", + "Resolve": "Vyřešit", + "Resolved": "Vyřešeno", + "deleteIncidentMsg": "Opravdu chcete smazat tuto mimořádnou událost?", + "Please input content": "Vložte prosím obsah", + "Please input title": "Vložte prosím název", + "API Token": "API token", + "See Jira Cloud Docs": "Viz dokumentaci Jira Cloud", + "Disable STARTTLS": "Zakázat STARTTLS", + "disableSTARTTLSDescription": "Povolte tuto možnost, pokud váš SMTP server nepodporuje STARTTLS. E-maily se budou posílat přes nešifrované spojení.", + "teamsEnableTags": "Zahrnout štítky", + "teamsEnableTagsDescription": "Pokud povolíte tuto možnost, zpráva bude obsahovat štítky dohledů.", + "Google Apps Script Webhook URL": "URL webhooku Google Apps Script", + "see Jira Cloud Docs": "vizte dokumentaci Jira Cloud", + "Deploy a Google Apps Script as a web app and paste the URL here": "Nasaďte Google Apps Script jako webovou aplikaci a vložte jeho URL adresu zde", + "Open your Google Spreadsheet": "Otevřte svou Google Tabulku", + "Failed to copy to clipboard": "Nepodařilo se zkopírovat do schránky", + "matrixUseTemplate": "Použít vlastní šablonu zprávy", + "Load More": "Načíst další", + "Loading...": "Načítám...", + "Pin this incident": "Připnout tuto mimořádnou událost", + "Quick Setup Guide": "Stručný návod", + "Pinned incidents are shown prominently on the status page": "Připnuté mimořádné události jsou předním obsahem stavové stránky", + "Edit Incident": "Upravit mimořádnou událost", + "Paste the script code (see below)": "Vložte kód sem (vizte níže)", + "monitorTypeGameServer": "Herní server", + "slug is not found": "Slug nebyl nalezen", + "Cloud ID": "Cloud ID", + "certificateExpiryNotificationHelp": "Počet dní předem lze nastavit v nastavení.", + "aboutJiraCloudId": "Další informace o Jira Cloud ID: {0}", + "Click Deploy → New deployment → Web app": "Klikněte na Nasadit → Nové nasazení → Webová aplikace", + "matrixUseTemplateDescription": "Pokud je tato možnost povolena, bude zpráva odeslána pomocí vlastní šablony.", + "signalUseTemplateDescription": "Pokud je tato možnost povolena, bude zpráva odeslána pomocí vlastní šablony. K přizpůsobení formátu oznámení můžete použít šablony Liquid.", + "slackIncludeGroupName": "Zahrnout název skupiny dohledů", + "signalUseTemplate": "Použít vlastní šablonu zprávy", + "monitorTypeDatabase": "Typ monitorování databáze", + "monitorTypeSpecial": "Speciální", + "Google Apps Script Code": "Kód skriptu Google Apps", + "Jira Service Management": "Správa služeb Jira", + "Go to Extensions → Apps Script": "Přejdi do Rozšíření → Skript aplikací", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Nastavte „Spustit jako: Já“ a „Kdo má přístup: Kdokoli“", + "Copy the web app URL and paste it above": "Zkopírujte adresu URL webové aplikace a vložte ji výše", + "Teltonika SMS Gateway": "SMS brána Teltonika", + "teltonikaVersionWarning": "Tento poskytovatel oznámení vyžaduje, aby vaše zařízení Teltonika používalo RMS verze 7.14.0 nebo vyšší.", + "360messengerUseTemplate": "Použít vlastní šablonu zprávy", + "360messengerAuthToken": "Klíč API 360messenger", + "360messengerRecipient": "Číslo/čísla telefonu příjemce", + "360messengerGroupId": "360messenger ID skupiny", + "360messengerTemplate": "Šablona zprávy 360messenger", + "360messengerGroupList": "Skupiny WhatsApp", + "360messengerSelectGroupList": "Vyberte skupinu, kterou chcete přidat", + "360messengerSelectedGroupID": "Vybrané ID skupiny (skupin)", + "360messengerEnableSendToGroup": "Povolit odesílání do skupin WhatsApp", + "360messengerCustomMessageTemplate": "Vlastní šablona zprávy", + "360messengerMessageTemplate": "Šablona zprávy", + "360messengerWayToGetUrlAndToken": "Klíč API pro 360messenger získáte na adrese {0}.", + "360messengerWayToWriteRecipient": "Zadejte jedno nebo více telefonních čísel v mezinárodním formátu bez úvodního znaménka plus (např. {0}). Více čísel oddělte čárkami.", + "360messengerErrorNoApiKey": "Nejprve zadejte svůj klíč API 360messenger.", + "360messengerErrorNoGroups": "Pro tento účet nebyly nalezeny žádné skupiny WhatsApp.", + "360messengerErrorApi": "Nelze načíst seznam skupin WhatsApp (Chyba {statusCode}: {message}).", + "360messengerErrorGeneric": "Nelze načíst seznam skupin WhatsApp: {message}", + "teltonikaUsernameHelptext": "Doporučení: Vytvořte samostatný účet, který bude omezen pouze na odesílání SMS zpráv, a zadejte zde jeho uživatelské jméno", + "teltonikaPassword": "Heslo API", + "teltonikaPasswordHelptext": "Heslo uživatele API můžete definovat ve svém routeru Teltonika, např. {0}", + "teltonikaModem": "ID modemu", + "teltonikaPhoneNumber": "Telefonní číslo", + "discordMessageFormat": "Formát zprávy", + "discordMessageFormatNormal": "Normální (bohaté vkládání)", + "discordMessageFormatMinimalist": "Minimalistický (krátký stav)", + "discordMessageFormatCustom": "Vlastní šablona", + "discordUseMessageTemplateDescription": "Pokud je tato možnost povolena, bude zpráva odeslána pomocí vlastní šablony (LiquidJS). Nechte pole prázdné, pokud chcete použít výchozí formát Uptime Kuma.", + "discordMessageTemplate": "Šablona zprávy", + "discordUseMessageTemplate": "Použít vlastní šablonu zprávy", + "GlobalpingLocationDescription": "Do pole pro zadání umístění lze zadat kontinenty, země, regiony, města, ASN, ISP nebo cloudové regiony. Filtry lze kombinovat pomocí znaku {plus} (např. {amazonPlusGermany} nebo {comcastPlusCalifornia}). Pokud je latence důležitým měřítkem, použijte filtry k zúžení umístění na malý region, abyste se vyhnuli výkyvům, a pro lepší stabilitu nastavte filtr {datacenter}. {fullDocs}.", + "GlobalpingMultipleLocationsError": "Více umístění není podporováno, pro každý dohled použijte jedno umístění.", + "halopsa_payload_desc": "Následující pole jsou odeslána do vašeho webhooku Halo PSA:", + "halopsa_field_title": "Název upozornění (vždy „Uptime Kuma upozornění“)", + "halopsa_field_status": "Stav dohledu: AKTIVNÍ, NEAKTIVNÍ, OZNÁMENÍ nebo NEZNÁMÝ", + "halopsa_field_monitor": "Název dohledu", + "halopsa_field_message": "Úplná výstražná zpráva se stavem a podrobnostmi", + "halopsa_field_timestamp": "Časové razítko události ve formátu ISO 8601", + "teltonikaModemHelptext": "ID SMS modemu musí být ve formátu {0}. Pokyny najdete na stránce https://developers.teltonika-networks.com/reference/.", + "halopsa_field_uptime_kuma_version": "Číslo verze Uptime Kuma", + "360messengerEnableCustomMessage": "Povolit vlastní šablonu zprávy namísto výchozí zprávy.", + "GlobalpingMonitorDescription": "Globalping poskytuje přístup k tisícům komunitních sond pro provádění síťových testů a měření. Pro všechny anonymní uživatele je stanoven limit 250 testů za hodinu. Chcete-li tento limit zdvojnásobit na 500 testů za hodinu, uložte si svůj token v {accountSettings}. Další informace najdete v {docs}.", + "halopsa_field_monitor_id": "Jedinečný identifikátor dohledu (null pro testovací oznámení) – použijte k přiřazení upozornění k ticketům", + "globalpingApiTokenDescription": "Získejte svůj token API Globalping na {0}.", + "slackIncludeGroupNameDescription": "Pokud je tato možnost povolena, bude do oznámení zahrnuta cesta ke skupině dohledů, aby bylo možné rozlišit dohledy se stejným názvem v různých skupinách.", + "slackUseTemplate": "Použít vlastní šablonu zprávy", + "Webhook Payload Fields": "Pole datové části webhooku", + "slackUseTemplateDescription": "Pokud je tato možnost povolena, zpráva bude odeslána pomocí vlastní šablony. Pomocí šablon Liquid můžete zahrnout informace o skupině dohledů prostřednictvím monitorJSON.path nebo monitorJSON.pathName.", + "Globalping - Access global monitoring probes": "Globalping – přístup k globálním monitorovacím sondám", + "GlobalpingHostname": "Veřejně dostupný cíl měření. Obvykle se jedná o název hostitele nebo adresu IPv4/IPv6, v závislosti na typu měření.", + "account settings": "nastavení účtu", + "GlobalpingResolverInfo": "Adresa IPv4/IPv6 nebo plně kvalifikovaný název domény (FQDN). Výchozí nastavení je lokální síťový resolver sondy. Server resolveru můžete kdykoli změnit.", + "RecordMatch": "Porovnání rekordní hodnoty", + "RegexMatch": "Zadejte regulární výraz, který porovná hodnoty záznamu", + "Protocol": "Protokol", + "domainExpiryNotificationHelp": "Počet dní předem lze nastavit v nastavení.", + "halopsa_id_usage_hint": "💡 Tip: Použijte monitor_id pro spolehlivé přiřazení upozornění k ticketům a heartbeat_id pro sledování historie událostí", + "halopsa_setup_step5": "Nakonfigurujte runbook tak, aby používal monitor_id pro přiřazování výstrah k existujícím ticketům", + "teltonikaUrl": "URL vašeho zařízení Teltonika", + "teltonikaUrlHelptext": "URL by měla být zadána jako úplný původ, např. {0} nebo {1}.", + "teltonikaUnsafeTls": "Ignorovat ověření certifikátu", + "teltonikaUnsafeTlsDescription": "Vypnutím ověřování certifikátů TLS se vystavujete riziku útoků typu „man-in-the-middle“, které mohou vést k úniku dat a převzetí kontroly nad systémy. Ověřování certifikátů nevypínejte, pokud nechcete přijmout riziko tohoto typu útoku. Doporučujeme používat LetsEncrypt s automatickým obnovováním.", + "teltonikaUsername": "API uživatelské jméno", + "teltonikaPhoneNumberHelptext": "Číslo musí být v mezinárodním formátu {0}, {1}. Je povoleno pouze jedno číslo.", + "Globalping API Token": "Token API Globalping", + "GlobalpingLocationDocs": "Kompletní dokumentace k zadávání polohy", + "GlobalpingIpFamilyInfo": "Verze IP, která se má použít. Povoleno pouze v případě, že cílem je název hostitele.", + "Location": "Umístění", + "Monitor Subtype": "Podtyp dohledu", + "Check for": "Zkontrolujte", + "ntfyUseTemplate": "Přizpůsobit šablony oznámení", + "ntfyUseTemplateDescription": "Povolit tuto funkci pro přizpůsobení názvů a zpráv oznámení pomocí šablon LiquidJS", + "ntfyCustomTitle": "Vlastní šablona názvu", + "ntfyCustomMessage": "Šablona vlastní zprávy", + "ntfyNotificationTemplateFallback": "Nechte prázdné pole, pokud chcete použít výchozí formát Uptime Kuma", + "fluxerMessageFormat": "Formát zprávy", + "fluxerMessageFormatNormal": "Normální (bohaté vložení)", + "fluxerMessageFormatMinimalist": "Minimalistický (krátký stav)", + "fluxerMessageTemplate": "Šablona zprávy", + "Fluxer Webhook URL": "URL webhooku Fluxer", + "wayToGetFluxerURL": "Toto nastavení najdete v nastavení cílového kanálu > Webhooky > Vytvořit webhook > Kopírovat URL webhooku.", + "Notification Action": "Oznámení Akce", + "homeAssistantNotificationActionHelptext": "Seznam oznamovacích akcí najdete v Home Assistant v části „Nastavení > Nástroje pro vývojáře > Akce“. Vyhledejte „notify“, abyste našli své akce. Zadejte pouze část za „notify.“, např. pro akci „notify.mobile_app_xyz“ zadejte „mobile_app_xyz“. Pro vestavěná mobilní oznámení vyhledejte „Odeslat oznámení přes mobile_app_xyz“ (nikoli „Odeslat oznámení“).", + "fluxerMessageFormatCustom": "Vlastní šablona", + "fluxerUseMessageTemplate": "Použít vlastní šablonu zprávy", + "fluxerUseMessageTemplateDescription": "Pokud je tato možnost povolena, bude zpráva odeslána pomocí vlastní šablony (LiquidJS). Nechte pole prázdné, pokud chcete použít výchozí formát Uptime Kuma.", + "maxUseTemplateDescription": "Pokud je tato funkce zapnutá, zpráva bude odeslána pomocí vlastní šablony.", + "wayToGetMaxToken": "Token MAX bot a další podrobnosti najdete na {0}.", + "maxMessenger": "MAX messenger", + "maxApiUrlDescription": "Základní adresa URL rozhraní API pro MAX Messenger. Výchozí nastavení: https://platform-api.max.ru", + "wayToGetMaxChatID": "Zadejte identifikátor chatu v MAX, kam mají být zprávy doručeny.", + "maxUseTemplate": "Použít vlastní šablonu zprávy", + "maxTemplateFormatDescription": "Aplikace MAX Messenger podporuje formátování prostého textu, Markdownu a HTML.", + "oracledbConnectionString": "Databáze Oracle: {connectionString}", + "API Version": "Verze API", + "Peer ID": "ID uživatele", + "vkApiVersionDescription": "Verze rozhraní VK API používaná pro požadavky. Ponechte výchozí nastavení, pokud z důvodu kompatibility výslovně nepotřebujete jinou verzi rozhraní VK API.", + "vkDontParseLinks": "Zakázat úryvky odkazů", + "vkDontParseLinksDescription": "Pokud je tato funkce zapnutá, VK nebude generovat náhledy odkazů ani úryvky.", + "vkPeerIdDescription": "Zadejte ID cílového VK peer_id. Tato hodnota se odesílá do API beze změn.", + "telnyxApiKey": "API klíč (V2)", + "telnyxApiKeyHelptext": "Váš klíč API Telnyx V2 z portálu Telnyx v sekci Klíče API", + "telnyxPhoneNumber": "Z čísla", + "telnyxPhoneNumberHelptext": "Vaše telefonní číslo Telnyx, ze kterého chcete odesílat (musí být zaregistrováno ve vašem účtu Telnyx, např. +15551234567)", + "telnyxToNumber": "Na číslo", + "telnyxToNumberHelptext": "Číslo telefonu, na které mají být zasílány SMS zprávy (např. +15559876543)", + "telnyxMessagingProfileId": "ID profilu pro zasílání zpráv (volitelné)", + "telnyxMessagingProfileIdHelptext": "Volitelně můžete přiřadit profil zasílání zpráv Telnyx, abyste mohli spravovat nastavení a funkce odesílatele", + "evolutionCustomMessageDesc": "Nechte pole prázdné, chcete-li použít výchozí zprávu.", + "evolutionCustomMessageTitle": "Vlastní zpráva (volitelné)", + "WebSocket Options": "Možnosti WebSocketu", + "pingPacketSizeWarning": "Velikost paketu menší než 16 nevyvolává v systému Linux měření doby přenosu tam a zpět", + "VKTeams Use Template": "Použít vlastní šablonu zprávy", + "VKTeams Use Template Description": "Pokud je tato funkce zapnutá, zpráva bude odeslána pomocí vlastní šablony.", + "VKTeams Bot Token Description": "Jak získat token bota a další podrobnosti najdete na {0}.", + "VKTeams Base Url Description": "Základní adresa URL rozhraní API VKTeams; v případě řešení nasazeného na vlastním serveru se může lišit. Výchozí hodnota: https://myteam.mail.ru", + "VKTeams Chat Id Description": "U uživatelů se jedná o jejich e-mailovou adresu. U skupin a kanálů získejte ID z jejich nastavení a připojte k němu {‚@‘}chat.agent", + "VKTeams Template Format Description": "Pro formátování zpráv podporuje VKTeams formátování prostého textu, MarkdownV2 a HTML.", + "smtpAdditionalHeadersTitle": "Další záhlaví", + "egosmsPhoneNumberDescription": "Číslo příjemce s předvolbou země, bez znaménka + nebo 00 (např. 2567XXXXXXXX).", + "wayToGetEgoSMSToken": "Uživatelské jméno a heslo pro rozhraní API služby EgoSMS si můžete vyžádat {here}.", + "egosmsSenderDescription": "ID odesílatele zobrazené příjemcům (max. 11 znaků). Pokud je pole prázdné, je jako výchozí hodnota nastaveno „EGOSMS“.", + "smtpAdditionalHeadersDesc": "Informace o použití najdete v dokumentaci k vlastním hlavičkám Nodemaileru {0}.", + "Bearer Token": "Token na doručitele", + "gamedigToken": "Token", + "gamedigTokenDescription": "Volitelné. Některé herní servery vyžadují ověřovací token.", + "Connection Timeout": "Časový limit připojení", + "Rybbit": "Rybbit", + "Flowtriq Webhook URL": "URL webhooku", + "Flowtriq API Key": "API klíč", + "flowtriqApiKeyDescription": "Volitelný klíč API pro ověřování požadavků webhooků směřujících k vaší instanci Flowtriq.", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) funguje prostřednictvím samostatné aplikace WxPusher na 6 platformách – Android, iOS, HarmonyOS, Windows, macOS a Linux ({download}). Naskenujte níže uvedený QR kód, abyste získali svůj SPT, a můžete začít odesílat. Více SPT oddělte čárkou (,) – další informace najdete v {docs}.", + "wxPusherDownloadApp": "stáhnout aplikaci", + "wxPusherDocs": "oficiální průvodce", + "Access Key": "Přístupový klíč", + "ooredooUsernameDescription": "E-mailová adresa, kterou používáte k přihlášení do ovládacího panelu hromadných SMS od Ooredoo.", + "ooredooAccessKeyDescription": "Najdete jej v části „API přistupový klíč“ na vašem SMS panelu Ooredoo. Vložte jej přesně tak, jak je uvedeno – před odesláním se automaticky zakóduje pomocí kódování base64.", + "ooredooBearerTokenDescription": "Token zobrazený za „Oprávnění: Držitel“ ve vzorovém příkazu cURL na vašem SMS panelu Ooredoo.", + "Leave blank to use the default Ooredoo endpoint.": "Nechte pole prázdné, chcete-li použít výchozí koncový bod Ooredoo.", + "ooredooRecipientDescription": "Zadejte jedno nebo více čísel oddělených čárkou nebo mezerou. Před místní sedmimístná čísla se automaticky přidává předčíslí 960.", + "plivoAuthID": "Ověřovací ID", + "plivoAuthIDHelptext": "Vaše identifikační číslo Plivo Auth ID z konzole Plivo", + "plivoAuthToken": "Autentizační token", + "plivoAuthTokenHelptext": "Váš autentizační token Plivo z konzole Plivo", + "plivoFromNumber": "Z čísla", + "plivoFromNumberHelptext": "Telefonní číslo Plivo, ze kterého se má odesílat (např. +15551234567). U SMS lze také použít schválené alfanumerické ID odesílatele; hlasové hovory vyžadují telefonní číslo Plivo.", + "plivoToNumber": "Na číslo", + "plivoToNumberHelptext": "Číslo telefonu příjemce, na které je doručena SMS nebo hlasový hovor (např. +15559876543)", + "pm2Description": "Zkontroluje, zda je proces PM2 {service_name} online", + "Refresh": "Obnovit", + "Select PM2 process": "Vybrat proces PM2", + "pm2ExpectedStates": "Očekávaný stav: online. Stavy „zastaveno“ a „chyba“ se považují za DOLE.", + "PM2 Process": "Proces PM2", + "plivoAnswerUrl": "URL odpovědi", + "plivoMessageType": "Typ zprávy", + "plivoVoiceCall": "Hlasový hovor", + "plivoAnswerUrlHelptext": "URL adresa, která vrací XML data Plivo pro daný hovor. Text upozornění je připojen jako parametr dotazu ?message=, takže váš koncový bod může vrátit například {message}.", + "ntpThresholdsTitle": "Prahové hodnoty NTP", + "ntpStratumThreshold": "Prahová hodnota stratum", + "ntpOffsetThreshold": "Prahová hodnota časového posunu (ms)", + "ntpOffsetThresholdHelp": "Označit jako dole, pokud časový posun překročí tuto hodnotu v milisekundách.", + "ntpDispersionThreshold": "Prahová hodnota rozptylu root (ms)", + "ntpStratumThresholdHelp": "Označte jako „dole“, pokud je hodnota stratum rovna této hodnotě nebo vyšší. Stratum udává kvalitu časového zdroje (1 = GPS/atomové hodiny, 16 = nesynchronizováno).", + "ntpDispersionThresholdHelp": "Označte jako dole, pokud rozptyl root času překročí tuto hodnotu. Rozptyl udává přesnost hodin serveru.", + "Pinglet Topic URL": "URL tématu", + "pingletTopicUrlDescription": "URL pro publikování tématu Pinglet, na které se mají odesílat upozornění, např. https://app.pinglet.co.uk/your-namespace/alerts. Pokud téma ještě neexistuje, vytvoří se při prvním publikování.", + "openwaSession": "Relace", + "turbosmtpConsumerKey": "Spotřebitelský klíč", + "openwaChatId": "ID chatu (telefonní číslo / ID kontaktu / ID skupiny)", + "wayToGetOpenwaApiUrl": "URL vaší instance OpenWA.", + "turbosmtpConsumerSecret": "Spotřebitelské heslo(tajemství)", + "wayToGetOpenwaApiKey": "API klíč používaný pro vaši instanci OpenWA.", + "openwaCustomMessageTitle": "Vlastní zpráva (volitelné)", + "openwaCustomMessageDesc": "Nechte pole prázdné, chcete-li použít výchozí zprávu.", + "wayToWriteOpenwaChatId": "Telefonní číslo s mezinárodní předvolbou, ale bez znaménka plus na začátku ({0}), ID kontaktu ({1}) nebo ID skupiny ({2}). Zprávu můžete odeslat na více ID v chatu, a to tak, že je oddělíte čárkami.", + "Hash Key": "Hashový klíč", + "wayToGetBearSMSToken": "Své uživatelské jméno a hash klíč pro API BearSMS získáte na {0}.", + "bearsmsPhoneNumberDescription": "Číslo příjemce s předvolbou země, bez znaku + nebo 00 (např. 9725XXXXXXXX).", + "bearsmsSenderDescription": "Volitelný identifikátor odesílatele zobrazený příjemcům (max. 11 znaků). Musí být schválen ve vašem účtu BearSMS, jinak se použije výchozí odesílatel účtu.", + "wayToGetOpenwaSession": "ID relace, které OpenWA používá k odesílání oznámení.", + "ClickUp API Token": "ClickUp API Token", + "ClickUp API Token Description": "Osobní přístupový token si v ClickUp můžete vytvořit tak, že přejdete do části Nastavení -> Integrace a ClickApps -> API ClickUp -> API token", + "ClickUp Workspace ID": "ID pracovního prostoru ClickUp", + "ClickUp Workspace ID Description": "Najdete jej v adrese URL svého pracovního prostoru, hned za částí app.clickup.com/, např. app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ID kanálu ClickUp", + "ClickUp Channel ID Description": "Najdete ho v adrese kanálu chatu, hned za /chat/r/, např. .../chat/r/abc123-4567.", + "milkyHttpAddress": "Adresa Milky HTTP (bez koncovky „/api“)", + "milkyMessageType": "Typ zprávy „Milky“", + "milkyGroupMessage": "Skupina", + "milkyPrivateMessage": "Soukromé", + "milkyUserOrGroupId": "ID skupiny/uživatele", + "milkySafetyTips": "Z bezpečnostních důvodů je nutné nastavit přístupový token", + "Device ID or Group ID": "ID zařízení nebo ID skupiny", + "notifyAppDeviceIdDescription": "ID zařízení z aplikace Notify! nebo ID skupiny, pomocí kterého lze najednou upozornit všechna zařízení ve skupině.", + "notifyAppSetup": "Spusťte aplikaci Notify! a v části Nastavení vyhledejte ID zařízení a token. Není třeba mít žádný účet. Více informací najdete na {0}.", + "smsgatewayUrlHelptext": "Základní URL vaší SMS brány bez cesty k API.", + "smsgatewayTo": "Telefonní číslo (čísla)", + "smsgatewayToHelptext": "Seznam telefonních čísel příjemců oddělených čárkami v mezinárodním formátu (např. +15551234567)", + "Client Key": "Klíč klienta", + "Channel": "Kanál", + "SFTP Path to Check": "Cesta SFTP k ověření", + "sftpPathHelpText": "Volitelné. Pokud je tato možnost zadána, dohled ověří, zda tato cesta na serveru SFTP existuje.", + "SSH Private Key": "Soukromý klíč SSH", + "Passphrase": "Tajné heslo", + "sshPrivateKeyPlaceholder": "-----ZAČÁTEK SOUKROMÉHO KLÍČE OPENSSH-----\n...\n-----KONEC SOUKROMÉHO KLÍČE OPENSSH-----", + "sshPrivateKeyHelpText": "Vložte obsah souboru s vaším soukromým klíčem (např. id_rsa nebo id_ed25519).", + "sshPassphraseHelpText": "Volitelné. Vyžaduje se pouze v případě, že je váš soukromý klíč zašifrován heslem.", + "optional": "volitelné", + "Line Number": "Číslo řádku", + "Amoot SMS recipient numbers help": "Zadejte jedno nebo více mobilních čísel oddělených čárkami.", + "Use Pattern": "Vzor použití", + "Pattern Code ID": "ID kódu vzoru", + "Amoot SMS pattern help": "Zadejte identifikační kód vzoru z panelu SMS služby Amoot. {0}", + "Create an Amoot SMS pattern": "Vytvořit šablonu SMS v Amoot", + "Use Own Line for Pattern": "Použít vlastní šablonu", + "Indigo URL": "Indigo URL", + "indigoUrlHelptext": "Vaše adresa Indigo Reflector (https://JMENO.indigodomo.net) nebo webový server Indigo ve vaší síti (https://IP:8176).", + "indigoIgnoreTlsErrorHelptext": "Je to nutné pro webový server Indigo ve vaší síti, který používá certifikát s vlastním podpisem. Pro Reflector to není nutné.", + "indigoApiKeyHelptext": "API klíč z vašeho účtu Indigo (Autorizace) nebo lokální tajný klíč pro webový server ve vaší síti.", + "Variable ID": "ID proměnné", + "indigoVariableIdHelptext": "Do této proměnné se zapíše výstražná zpráva.", + "Action Group ID": "ID akční skupiny", + "indigoActionGroupIdHelptext": "Provede se po aktualizaci proměnné. Nastavte proměnnou, skupinu akcí nebo obojí. ID z Indiga zkopírujte kliknutím pravým tlačítkem myši → Kopírovat ID." +} diff --git a/src/lang/da-DK.json b/src/lang/da-DK.json new file mode 100644 index 0000000..07413dc --- /dev/null +++ b/src/lang/da-DK.json @@ -0,0 +1,824 @@ +{ + "languageName": "Dansk", + "Settings": "Indstillinger", + "Dashboard": "Betjeningspanel", + "New Update": "Opdatering tilgængelig", + "Language": "Sprog", + "Appearance": "Udseende", + "Theme": "Tema", + "General": "Generelt", + "Version": "Version", + "Check Update On GitHub": "Tjek efter opdateringer på GitHub", + "List": "Liste", + "Add": "Tilføj", + "Add New Monitor": "Tilføj ny overvågning", + "Quick Stats": "Oversigt", + "Up": "Aktiv", + "Down": "Inaktiv", + "Pending": "Afventer", + "Unknown": "Ukendt", + "Pause": "Stands", + "pauseDashboardHome": "Standset", + "Name": "Navn", + "Status": "Status", + "DateTime": "Dato / Tid", + "Message": "Beskeder", + "No important events": "Ingen vigtige begivenheder", + "Resume": "Fortsæt", + "Edit": "Redigér", + "Delete": "Slet", + "Current": "Aktuelt", + "Uptime": "Oppetid", + "Cert Exp.": "Certifikatets udløb.", + "day": "Dag | Dage", + "-day": "-Dage", + "hour": "Timer", + "-hour": "-Timer", + "checkEverySecond": "Tjek hvert {0} sekund", + "Response": "Respons", + "Ping": "Ping", + "Monitor Type": "Overvågningstype", + "Keyword": "Nøgleord", + "Friendly Name": "Visningsnavn", + "URL": "URL", + "Hostname": "Hostname", + "Port": "Port", + "Heartbeat Interval": "Hjerteslag interval", + "Retries": "Gentagelser", + "retriesDescription": "Maksimalt antal gentagelser, før tjenesten markeres som inaktiv og sender en meddelelse", + "Advanced": "Avanceret", + "ignoreTLSError": "Ignorér TLS/SSL fejl for HTTPS websteder", + "Upside Down Mode": "Omvendt tilstand", + "upsideDownModeDescription": "Håndter tilstanden omvendt. Hvis tjenesten er tilgængelig, vises den som inaktiv.", + "Max. Redirects": "Maks. Omdirigeringer", + "maxRedirectDescription": "Maksimalt antal omdirigeringer, der skal følges. Indstil til 0 for at deaktivere omdirigeringer.", + "Accepted Status Codes": "Tilladte HTTP-Statuskoder", + "acceptedStatusCodesDescription": "Vælg de statuskoder, der stadig skal vurderes som vellykkede.", + "Save": "Gem", + "Notifications": "Underretninger", + "Not available, please setup.": "Ikke tilgængelige, opsæt venligst.", + "Setup Notification": "Opsæt underretninger", + "Light": "Lys", + "Dark": "Mørk", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Tema - Tidslinje", + "Normal": "Normal", + "Bottom": "Bunden", + "None": "Ingen", + "Timezone": "Tidszone", + "Search Engine Visibility": "Søgemaskine synlighed", + "Allow indexing": "Tillad indeksering", + "Discourage search engines from indexing site": "Frabed søgemaskiner at indeksere webstedet", + "Change Password": "Skift adgangskode", + "Current Password": "Nuværende adgangskode", + "New Password": "Ny adgangskode", + "Repeat New Password": "Gentag den nye adgangskode", + "passwordNotMatchMsg": "Adgangskoderne er ikke ens.", + "Update Password": "Opdatér adgangskode", + "Disable Auth": "Deaktivér autentifikation", + "Enable Auth": "Aktivér autentifikation", + "Logout": "Log ud", + "notificationDescription": "Tildel underretninger til Overvåger(e), så denne funktion træder i kraft.", + "Leave": "Forlad", + "I understand, please disable": "Jeg er indforstået, deaktiver venligst", + "Confirm": "Bekræft", + "Yes": "Ja", + "No": "Nej", + "Username": "Brugernavn", + "Password": "Adgangskode", + "Remember me": "Husk mig", + "Login": "Log ind", + "No Monitors, please": "Ingen overvågninger", + "add one": "tilføj en", + "Notification Type": "Underretningstype", + "Email": "E-Mail", + "Test": "Test", + "Certificate Info": "Certifikatoplysninger", + "keywordDescription": "Søg efter et søgeord i almindelig HTML- eller JSON -output. Bemærk, at der skelnes mellem store og små bogstaver.", + "deleteMonitorMsg": "Er du sikker på, at du vil slette overvågeren?", + "deleteNotificationMsg": "Er du sikker på, at du vil slette denne underretning for alle overvågere?", + "resolverserverDescription": "Cloudflare er standardserveren, den kan til enhver tid ændres.", + "Resolver Server": "Navne-server", + "rrtypeDescription": "Vælg den type RR, du vil overvåge", + "Last Result": "Seneste resultat", + "pauseMonitorMsg": "Er du sikker på at du vil standse overvågningen?", + "Create your admin account": "Opret din administratorkonto", + "Repeat Password": "Gentag adgangskoden", + "Resource Record Type": "Resource Record Type", + "respTime": "Resp. Tid (ms)", + "notAvailableShort": "N/A", + "Create": "Opret", + "clearEventsMsg": "Er du sikker på vil slette alle events for denne Overvåger?", + "clearHeartbeatsMsg": "Er du sikker på vil slette alle hjerteslag for denne Overvåger?", + "confirmClearStatisticsMsg": "Vil du helt sikkert slette ALLE statistikker?", + "Clear Data": "Ryd Data", + "Events": "Events", + "Heartbeats": "Hjerteslag", + "Auto Get": "Auto-hent", + "enableDefaultNotificationDescription": "For hver ny overvåger aktiveres denne underretning som standard. Du kan stadig deaktivere underretningen separat for hver skærm.", + "Default enabled": "Standard aktiveret", + "Also apply to existing monitors": "Anvend også på eksisterende overvågere", + "Export": "Eksport", + "Import": "Import", + "backupDescription": "Du kan sikkerhedskopiere alle Overvågere og alle underretninger til en JSON-fil.", + "backupDescription2": "PS: Historik og hændelsesdata er ikke inkluderet.", + "backupDescription3": "Følsom data, f.eks. underretnings-tokener, er inkluderet i eksportfilen. Gem den sikkert.", + "alertNoFile": "Vælg en fil der skal importeres.", + "alertWrongFileType": "Vælg venligst en JSON-fil.", + "twoFAVerifyLabel": "Indtast venligst dit token for at bekræfte, at 2FA fungerer:", + "tokenValidSettingsMsg": "Token er gyldigt! Du kan nu gemme 2FA -indstillingerne.", + "confirmEnableTwoFAMsg": "Er du sikker på at du vil aktivere 2FA?", + "confirmDisableTwoFAMsg": "Er du sikker på at du vil deaktivere 2FA?", + "Apply on all existing monitors": "Anvend på alle eksisterende overvågere", + "Verify Token": "Bekræft nøgle", + "Setup 2FA": "Opsæt 2FA", + "Enable 2FA": "Aktiver 2FA", + "Disable 2FA": "Deaktiver 2FA", + "2FA Settings": "2FA Indstillinger", + "Two Factor Authentication": "To-Faktor Autentificering", + "Active": "Aktiv", + "Inactive": "Inaktiv", + "Token": "Token", + "Show URI": "Vis URI", + "Clear all statistics": "Ryd alle Statistikker", + "retryCheckEverySecond": "Prøv igen hvert {0} sekund", + "importHandleDescription": "Vælg 'Spring over eksisterende', hvis du vil springe over hver overvåger eller underretning med samme navn. 'Overskriv' sletter alle eksisterende overvågere og underretninger.", + "confirmImportMsg": "Er du sikker på at importere sikkerhedskopien? Sørg for, at du har valgt den rigtige importindstilling.", + "Heartbeat Retry Interval": "Hjerteslag gentagelsesinterval", + "Import Backup": "Importér Backup", + "Export Backup": "Eksportér Backup", + "Skip existing": "Spring over eksisterende", + "Overwrite": "Overskriv", + "Options": "Valgmuligheder", + "Keep both": "Behold begge", + "Tags": "Etiketter", + "Add New below or Select...": "Tilføj Ny nedenfor eller Vælg…", + "Tag with this name already exist.": "En etiket med dette navn findes allerede.", + "Tag with this value already exist.": "En etiket med denne værdi findes allerede.", + "color": "farve", + "value (optional)": "værdi (valgfri)", + "Gray": "Grå", + "Red": "Rød", + "Orange": "Orange", + "Green": "Grøn", + "Blue": "Blå", + "Indigo": "Indigo", + "Purple": "Lilla", + "Pink": "Pink", + "Search...": "Søg…", + "Avg. Ping": "Gns. ping", + "Avg. Response": "Gns. respons", + "Entry Page": "Entry Side", + "statusPageNothing": "Intet her, tilføj venligst en Gruppe eller en Overvåger.", + "No Services": "Ingen Tjenester", + "All Systems Operational": "Alle Systemer i Drift", + "Partially Degraded Service": "Delvist forringet service", + "Degraded Service": "Forringet service", + "Add Group": "Tilføj Gruppe", + "Add a monitor": "Tilføj en overvågning", + "Edit Status Page": "Redigér Statusside", + "Go to Dashboard": "Gå til Betjeningspanel", + "Status Page": "Statusside", + "Status Pages": "Statusside", + "telegram": "Telegram", + "webhook": "Webhook", + "smtp": "Email (SMTP)", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Understøtter 50+ Notifikationstjenester)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "Primary Base URL": "Primær Basis-URL", + "Push URL": "Push URL", + "needPushEvery": "Du bør kalde denne webadresse hvert {0} sekund.", + "pushOptionalParams": "Valgfrie parametre: {0}", + "defaultNotificationName": "Min {notification} Advarsel ({number})", + "here": "her", + "Required": "Påkrævet", + "Bot Token": "Bot Token", + "wayToGetTelegramToken": "Du kan få et token fra {0}.", + "Chat ID": "Chat ID", + "supportTelegramChatID": "Support Direct Chat / Group / Channel's Chat ID", + "wayToGetTelegramChatID": "Du kan få dit chat-ID ved at sende en besked til bot'en og gå til denne URL for at se chat_id'et:", + "YOUR BOT TOKEN HERE": "DIT BOT TOKEN HER", + "chatIDNotFound": "Chat-ID blev ikke fundet; send venligst en besked til denne bot først", + "Post URL": "Post URL", + "Content Type": "Indholdstype", + "webhookJsonDesc": "{0} er god til alle moderne HTTP-servere som f.eks Express.js", + "webhookFormDataDesc": "{multipart} er god til PHP. JSON'en skal parses med {decodeFunction}", + "secureOptionNone": "Ingen / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignorer TLS-fejl", + "From Email": "Afsender Email", + "emailCustomSubject": "Brugerdefineret Emne", + "To Email": "Modtager Email", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord Webhook URL", + "wayToGetDiscordURL": "Du kan få dette ved at gå til Serverindstillinger -> Integrationer -> Opret webhook", + "Bot Display Name": "Bot Visningsnavn", + "Prefix Custom Message": "Præfiks Brugerdefineret Besked", + "Hello @everyone is...": "Hej {'@'}alle er…", + "Webhook URL": "Webhook URL", + "wayToGetTeamsURL": "Du kan lære, hvordan du laver en webhook URL {0}.", + "Number": "Nummer", + "Recipients": "Modtagere", + "needSignalAPI": "Du skal have en Signal-klient med REST API.", + "wayToCheckSignalURL": "Du kan tjekke denne URL for at se, hvordan du konfigurerer en:", + "signalImportant": "VIGTIGT: Du kan ikke blande grupper og numre i modtagere!", + "Application Token": "Program Token", + "Server URL": "Server URL", + "Priority": "Prioritet", + "Icon Emoji": "Ikon Emoji", + "Channel Name": "Kanalnavn", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Mere info om Webhooks på: {0}", + "aboutChannelName": "Indtast kanalnavnet i {0} Kanalnavn feltet, hvis du vil omgå Webhook-kanalen. Eks: #anden-kanal", + "aboutKumaURL": "Hvis du efterlader Uptime Kuma URL-feltet tomt, vil det som standard gå til projektets GitHub-siden.", + "emojiCheatSheet": "Emoji cheat sheet: {0}", + "clicksendsms": "ClickSend SMS", + "User Key": "Bruger-Nøgle", + "Device": "Enhed", + "Message Title": "Besked Titel", + "Notification Sound": "Notifikationslyd", + "More info on:": "Mere info på: {0}", + "pushoverDesc1": "Nødprioritet (2) har som standard 30 sekunders timeout mellem genforsøg og udløber efter 1 time.", + "pushoverDesc2": "Hvis du vil sende meddelelser til forskellige enheder, skal du udfylde feltet Enhed.", + "SMS Type": "SMS Type", + "octopushTypePremium": "Premium (Hurtig - anbefales til advarsel)", + "octopushTypeLowCost": "Lavpris (Langsom - nogle gange blokeret af operatøren)", + "checkPrice": "Tjek {0} priser:", + "apiCredentials": "API legitimationsoplysninger", + "octopushLegacyHint": "Bruger du den ældre version af Octopush (2011-2020) eller den nye version?", + "Check octopush prices": "Tjek octopush priser {0}.", + "octopushPhoneNumber": "Telefonnummer (intl format, f.eks : +4512345678) ", + "octopushSMSSender": "SMS Afsender Navn : 3-11 alfanumeriske tegn og mellemrum (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Enhed-ID", + "Apprise URL": "Apprise URL", + "Example:": "Eksempel: {0}", + "Read more:": "Læs mere: {0}", + "Status:": "Status: {0}", + "Read more": "Læs mere", + "appriseInstalled": "Apprise er installeret.", + "appriseNotInstalled": "Apprise er ikke installeret. {0}", + "Access Token": "Adgangsnøgle", + "Channel access token": "Kanal adgangsnøgle", + "Line Developers Console": "Line Udviklerkonsol", + "lineDevConsoleTo": "Line Udviklerkonsol - {0}", + "Basic Settings": "Basisindstillinger", + "User ID": "Bruger-ID", + "Messaging API": "Besked API", + "wayToGetLineChannelToken": "Tilgå først {0}, opret en udbyder og kanal (Messaging API), så kan du få kanaladgangstoken'et og bruger-ID'et fra de ovennævnte menupunkter.", + "Icon URL": "Ikon URL", + "aboutIconURL": "Du kan angive et link til et billede i \"Ikon URL\" for at tilsidesætte standardprofilbilledet. Vil ikke blive brugt, hvis Ikon Emoji er angivet.", + "aboutMattermostChannelName": "Du kan tilsidesætte standardkanalen, som Webhoo'en sender til ved at indtaste kanalnavnet i feltet \"Kanalnavn\". Dette skal aktiveres i Mattermost Webhook-indstillingerne. Eks: #anden-kanal", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - billig, men langsom og ofte overbelastet. Begrænset kun til polske modtagere.", + "promosmsTypeFlash": "SMS FLASH - Beskeden vises automatisk på modtagerenheden. Begrænset kun til polske modtagere.", + "promosmsTypeFull": "SMS FULL - Premium-niveau af SMS, Du kan bruge dit \"Sender Name\" (Du skal først registrere navn). Pålidelig til advarsler.", + "promosmsTypeSpeed": "SMS SPEED - Højeste prioritet i systemet. Meget hurtig og pålidelig, men dyr (ca. to gange af SMS FULL pris).", + "promosmsPhoneNumber": "Telefonnummer (polske numre behøver ikke angive områdenumre)", + "promosmsSMSSender": "SMS Sender Name : Forudregistreret navn eller en af standarderne: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu WebHookURL", + "matrixHomeserverURL": "Hjemmeserver-URL (med http(s):// og eventuel port)", + "Internal Room Id": "Intern Rum-ID", + "matrixDesc1": "Du kan finde det interne rum-ID ved at se i det avancerede afsnit af rumindstillingerne i din Matrix-klient. Det skulle ligne !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Det anbefales stærkt, at du opretter en ny bruger og ikke bruger din egen Matrix-brugers adgangstoken, da det giver fuld adgang til din konto og alle de rum, du har tilsluttet dig. I stedet skal du oprette en ny bruger og kun invitere den til det rum, du vil modtage meddelelsen i. Du kan få adgangstokenet ved at køre {0}", + "Method": "Metode", + "Body": "Body", + "Headers": "Headers", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "\"request headers\"-erne er ikke gyldige JSON: ", + "BodyInvalidFormat": "\"request body\"-en er ikke gyldige JSON: ", + "Monitor History": "Overvågningshistorik", + "clearDataOlderThan": "Gem overvågningshistorikdata i {0} dage.", + "PasswordsDoNotMatch": "Adgangskoderne stemmer ikke overens.", + "records": "forekomster", + "One record": "Én forekomst", + "steamApiKeyDescription": "For at overvåge en Steam Game Server skal du bruge en Steam Web-API nøgle. Du kan registrere din API-nøgle her: ", + "Current User": "Nuværende Bruger", + "recent": "Seneste", + "Done": "Færdig", + "Info": "Info", + "Security": "Sikkerhed", + "Steam API Key": "Steam API-nøgle", + "Shrink Database": "Krymp Database", + "Pick a RR-Type...": "Vælg en RR-Type…", + "Pick Accepted Status Codes...": "Vælg accepterede statuskoder…", + "Default": "Standard", + "HTTP Options": "HTTP Valgmuligheder", + "Create Incident": "Opret Annoncering", + "Title": "Titel", + "Content": "Indhold", + "Style": "Type", + "info": "info", + "warning": "advarsel", + "danger": "fare", + "primary": "primær", + "light": "lys", + "dark": "mørk", + "Post": "Udgiv", + "Please input title and content": "Indtast venligst titel og indhold", + "Created": "Oprettet", + "Last Updated": "Sidst Opdateret", + "Unpin": "Frigør", + "Switch to Light Theme": "Skift til Lys Tema", + "Switch to Dark Theme": "Skift til Mørkt Tema", + "Show Tags": "Vis Etiketter", + "Hide Tags": "Skjul Etiketter", + "Description": "Beskrivelse", + "No monitors available.": "Ingen tilgængelige overvågninger.", + "Add one": "Tilføj en", + "No Monitors": "Ingen Overvågere", + "Untitled Group": "Unavngivet Gruppe", + "Services": "Tjenester", + "Discard": "Kassér", + "Cancel": "Annullér", + "Powered by": "Drevet af", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API Brugernavn (inkl. webapi_ prefix)", + "serwersmsAPIPassword": "API Adgangskode", + "serwersmsPhoneNumber": "Telefonnummer", + "serwersmsSenderName": "SMS Afsender Navn (registreret via kundeportal)", + "statusMaintenance": "Vedligeholdelse", + "Maintenance": "Vedligeholdelse", + "No Maintenance": "Ingen vedligeholdelse", + "Examples": "Eksempler", + "High": "Høj", + "Recipient Number": "Modtager Nummer", + "From Name/Number": "Fra Navn/Nummer", + "Help": "Hjælp", + "Please use this option carefully!": "Brug venligst denne funktion med forsigtighed!", + "successMessage": "Succesmeddelelse", + "error": "fejl", + "critical": "kritisk", + "Customize": "Tilpas", + "Custom Footer": "Brugerdefineret Footer", + "Custom CSS": "Brugerdefineret CSS", + "deleteStatusPageMsg": "Er du sikker på, at du vil slette denne statusside?", + "Proxies": "Proxies", + "default": "Standard", + "enabled": "Aktiveret", + "setAsDefault": "Indstil som standard", + "Certificate Chain": "Certificate Chain", + "Days Remaining:": "Dage tilbage:", + "No status pages": "Ingen statussider", + "Proxy": "Proxy", + "default: notify all devices": "standard: underretter alle enheder", + "Automations can optionally be triggered in Home Assistant:": "Automatiseringer kan valgfrit udløses i Home Assistant:", + "Trigger type:": "Trigger type:", + "Event type:": "Event type:", + "Event data:": "Event data:", + "Frontend Version": "Frontend Version", + "or": "eller", + "Notification Service": "Notifikationstjeneste", + "Domain": "Domæne", + "Google Analytics ID": "Google Analytics ID", + "Edit Tag": "Redigér etiket", + "Learn More": "Lær mere", + "Schedule maintenance": "Planlæg vedligeholdelse", + "Invalid": "Ugyldig", + "User": "Bruger", + "Installed": "Installeret", + "Not installed": "Ikke installeret", + "Running": "Kører", + "Not running": "Kører ikke", + "Remove Token": "Fjern Token", + "Start": "Start", + "Stop": "Stop", + "Add New Status Page": "Tilføj ny statusside", + "Next": "Næste", + "No Proxy": "Ingen proxy", + "New Status Page": "Ny statusside", + "Page Not Found": "Side blev ikke fundet", + "Reverse Proxy": "Reverse Proxy", + "Backup": "Backup", + "About": "Om", + "cloudflareWebsite": "Cloudflare hjemmeside", + "Message:": "Besked:", + "HTTP Headers": "HTTP Headers", + "Trust Proxy": "Trust Proxy", + "For example: nginx, Apache and Traefik.": "For eksempel: nginx, Apache og Traefik.", + "Please read": "Læs venligst", + "Show Powered By": "Vis Drevet af", + "Domain Names": "Domænenavne", + "signedInDisp": "Logget ind som {0}", + "Certificate Expiry Notification": "Meddelelse om udløbsdato for certifikatet", + "API Username": "API Brugernavn", + "API Key": "API Nøgle", + "Steam Game Server": "Steam Game Server", + "What you can try:": "Hvad du kan prøve:", + "Go back to the previous page.": "Gå tilbage til forrige side.", + "Coming Soon": "Kommer snart", + "settingsCertificateExpiry": "Udløb af TLS-certifikat", + "Setup Docker Host": "Opsæt Docker Host", + "Connection Type": "Forbindelsestype", + "Docker Daemon": "Docker Daemon", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker Container", + "Container Name / ID": "Container Navn / ID", + "Packet Size": "Pakke størrelse", + "Home Assistant URL": "Home Assistant URL", + "Frontend Version do not match backend version!": "Frontend versionen stemmer ikke overens med backend versionen!", + "Optional": "Valgfri", + "HomeAssistant": "Home Assistant", + "disableauth.message1": "Er du sikker på, at du vil {disableAuth}?", + "disable authentication": "deaktivere autentifikation", + "disableauth.message2": "Den er beregnet til scenarier {intendThirdPartyAuth} foran Uptime Kuma, f.eks. Cloudflare Access, Authelia eller andre godkendelsesmekanismer.", + "where you intend to implement third-party authentication": "hvor du har tænkt dig at implementere tredjepartsgodkendelse", + "deleteProxyMsg": "Er du sikker på, at du vil slette denne proxy for alle monitors?", + "Valid": "Gyldig", + "Don't know how to get the token? Please read the guide:": "Ved du ikke, hvordan du får fat i din Token? Læs venligst guiden:", + "Subject:": "Emne:", + "Footer Text": "Footer tekst", + "Using a Reverse Proxy?": "Bruger du en Reverse Proxy?", + "deleteDockerHostMsg": "Er du sikker på, at du vil slette denne docker host for alle monitors?", + "Docker Host": "Docker Host", + "Docker Hosts": "Docker Hosts", + "loadingError": "Kan ikke hente dataene, prøv igen senere.", + "Custom": "Brugerdefineret", + "Monitor": "Overvåger | Overvågere", + "Specific Monitor Type": "Specifik overvågningstype", + "topic": "Emne", + "Fingerprint:": "Fingerprint:", + "Issuer:": "Udsteder:", + "dayOfWeek": "Ugedag", + "dayOfMonth": "Dag i måneden", + "lastDay": "Sidste dag", + "lastDay1": "Sidste dag i måneden", + "weekdayShortThu": "Tor", + "weekdayShortFri": "Fre", + "weekdayShortSat": "Lør", + "weekdayShortSun": "Søn", + "weekdayShortWed": "Ons", + "lastDay2": "Anden sidste dag i måneden", + "lastDay3": "Tredje sidste dag i måneden", + "lastDay4": "Fjerde sidste dag i måneden", + "maintenanceStatus-under-maintenance": "Under vedligeholdelse", + "maintenanceStatus-inactive": "Inaktiv", + "maintenanceStatus-scheduled": "Planlagt", + "maintenanceStatus-ended": "Afsluttet", + "maintenanceStatus-unknown": "Ukendt", + "Display Timezone": "Vis tidszone", + "Server Timezone": "Serverens tidszone", + "IconUrl": "Ikon URL", + "Enable DNS Cache": "Aktivér DNS Cache", + "Enable": "Aktivér", + "Disable": "Deaktivér", + "dnsCacheDescription": "Det fungerer muligvis ikke i alle IPv6-miljøer, så deaktiver det, hvis du støder på problemer.", + "Maintenance Time Window of a Day": "Tidsvindue for vedligeholdelse af en dag", + "Schedule Maintenance": "Planlæg vedligeholdelse", + "Date and Time": "Dato og klokkeslæt", + "plugin": "Plugin | Plugins", + "install": "Installér", + "uninstall": "Afinstallér", + "uninstalling": "Afinstallerer", + "confirmUninstallPlugin": "Er du sikker på, at du vil afinstallere dette plugin?", + "installing": "Installerer", + "markdownSupported": "Markdown syntax understøttet", + "Affected Monitors": "Berørte overvågninger", + "All Status Pages": "Alle statussider", + "Pick Affected Monitors...": "Vælg berørte overvågninger…", + "Select status pages...": "Vælg statusside…", + "proxyDescription": "Proxyer skal være tilknyttet en monitor for at fungere.", + "Accept characters:": "Accepter tegn:", + "Authentication": "Godkendelse", + "wayToGetCloudflaredURL": "(Download cloudflared fra {0})", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Den aktuelle forbindelse kan gå tabt, hvis du er forbundet via Cloudflare Tunnel. Er du sikker på, at du vil stoppe det? Indtast din nuværende adgangskode for at bekræfte den.", + "Other Software": "Anden software", + "Date Created": "Dato oprettet", + "signedInDispDisabled": "Auth Deaktiveret.", + "certificationExpiryDescription": "HTTPS Monitors sender en notifikation, når TLS-certifikatet udløber om:", + "Also check beta release": "Se også betaudgivelsen", + "Show update if available": "Vis opdatering, hvis tilgængelig", + "wayToGetZohoCliqURL": "Du kan lære, hvordan du opretter et webhook URL {0}.", + "recurringInterval": "Interval", + "weekdayShortMon": "Man", + "weekdayShortTue": "Tir", + "dnsPortDescription": "DNS server port. Standardværdien er 53. Du kan altid ændre porten.", + "Valid To:": "Gyldig til:", + "Domain Name Expiry Notification": "Notifikation om udløb af domænenavn", + "Custom Monitor Type": "Brugerdefineret overvågningstype", + "API Keys": "API Nøgler", + "Don't expire": "Udløb aldrig", + "Continue": "Fortsæt", + "Add Another": "Tilføj en mere", + "Key Added": "Nøgle tilføjet", + "Add API Key": "Tilføj API Nøgle", + "No API Keys": "Ingen API nøgler", + "apiKey-active": "Aktiv", + "apiKey-expired": "Udløbet", + "apiKey-inactive": "Inaktiv", + "disableAPIKeyMsg": "Er du sikker på du vil deaktivere denne API nøgle?", + "Generate": "Generér", + "Game": "Spil", + "General Monitor Type": "Generel Overvågningstype", + "Clone Monitor": "Duplikér overvågning", + "Clone": "Duplikér", + "cloneOf": "Kopi af {0}", + "promosmsLogin": "API Login Navn", + "pushoversounds siren": "Sirene", + "pushoversounds none": "Ingen (lydløs)", + "smtpDkimSettings": "DKIM Indstillinger", + "documentation": "dokumentation", + "smtpDkimDomain": "Domænenavn", + "smtpDkimPrivateKey": "Privat nøgle", + "alertaApiEndpoint": "API Slutpunkt", + "alertaApiKey": "API Nøgle", + "smseagleEncoding": "Send som Unicode", + "onebotHttpAddress": "OneBot HTTP Adresse", + "onebotMessageType": "OneBot Meddelelse Type", + "onebotGroupMessage": "Gruppe", + "onebotPrivateMessage": "Privat", + "onebotUserOrGroupId": "Gruppe/Bruger ID", + "promosmsPassword": "API Adgangskode", + "recurringIntervalMessage": "Kør hver dag | Kør hver {0}. dag", + "smseagleTo": "Telefon numre", + "pagertreeIntegrationUrl": "Integration URL", + "pagertreeSilent": "Lydløs", + "pagertreeLow": "Lav", + "pagertreeMedium": "Mellem", + "pagertreeHigh": "Høj", + "pagertreeCritical": "Kritisk", + "pushoversounds vibrate": "Kun Vibration", + "Server Address": "Server Adresse", + "pauseMaintenanceMsg": "Er du sikker på du vil pause?", + "Recurring": "Tilbagevendende", + "Enable TLS": "Aktivér TLS", + "high": "høj", + "Base URL": "Base URL", + "Platform": "Platform", + "Huawei": "Huawei", + "Retry": "Forsøg igen", + "Topic": "Emne", + "Setup Proxy": "Opsæt Proxy", + "Proxy Server": "Proxy Server", + "wayToGetClickSendSMSToken": "Du kan få API brugernavn og API nøgle fra {0} .", + "PushDeer Key": "PushDeer Nøgle", + "The resource is no longer available.": "Denne ressource er ikke længere tilgængelig.", + "Proxy Protocol": "Proxy Protokol", + "Integration Key": "Integration Nøgle", + "Integration URL": "Integration URL", + "do nothing": "gør intet", + "Passive Monitor Type": "Passiv Overvågningstype", + "Most likely causes:": "Mest sandsynlige årsager:", + "statusPageMaintenanceEndDate": "Slut", + "pushoversounds magic": "Magisk", + "pushoversounds mechanical": "Mekanisk", + "pushyAPIKey": "Hemmelig API Nøgle", + "Expiry date": "Udløbsdato", + "Expires": "Udløber", + "deleteAPIKeyMsg": "Er du sikker på du vil slette denne API nøgle?", + "pagertreeDoNothing": "Gør intet", + "Start of maintenance": "Start på vedligeholdelse", + "Add New Tag": "Tilføj ny etiket", + "setupDatabaseChooseDatabase": "Hvilken database vil du gerne bruge?", + "Saved.": "Gemt.", + "authUserInactiveOrDeleted": "Denne bruger er inaktiv eller er blevet slettet.", + "webhookBodyPresetOption": "Forudindstilling - {0}", + "filterActive": "Aktiv", + "filterActivePaused": "På pause", + "Select": "Vælg", + "selectedMonitorCount": "Valgte: {0}", + "chromeExecutableAutoDetect": "Autodetektér", + "pushyToken": "Enhedsnøgle", + "You can divide numbers with": "Du kan dividere numre med", + "alertaEnvironment": "Miljø", + "promosmsAllowLongSMS": "Tillad lang SMS", + "smseagleToken": "API Adgangsnøgle", + "twilioFromNumber": "Fra nummer", + "twilioToNumber": "Til nummer", + "twilioApiKey": "API nøgle (valgfrit)", + "ntfyUsernameAndPassword": "Brugernavn og adgangskode", + "lunaseaUserID": "Bruger ID", + "lunaseaDeviceID": "Enheds ID", + "lunaseaTarget": "Mål", + "Expiry": "Udløber", + "pushDeerServerDescription": "Efterlad blank for at bruge den officielle server", + "Close": "Luk", + "Group": "Gruppe", + "Device Token": "Enhedsnøgle", + "dbName": "Database navn", + "Monitor Group": "Overvågningsgruppe", + "telegramSendSilently": "Send lydløst", + "smseagleRecipientType": "Modtager type", + "smseagleRecipient": "Modtager(e) (Adskil med komma)", + "statusPageRefreshIn": "Genopfrisk om: {0}", + "Home": "Hjem", + "tagNotFound": "Etiket blev ikke fundet.", + "pushOthers": "Andre", + "confirmDeleteTagMsg": "Er du sikker på at du vil slette denne etiket? Overvågninger med denne etiket vil ikke blive slettet.", + "resendEveryXTimes": "Gensend hver {0} gang", + "resendDisabled": "Gensendelse deaktiveret", + "Reconnecting...": "Genopretter forbindelse...", + "successPaused": "Standset med succes.", + "Reset Token": "Nulstil nøgle", + "Show Clickable Link": "Vis klikbart link", + "atLeastOneMonitor": "Vælg mindst én berørt overvågning", + "authInvalidToken": "Ugyldig nøgle.", + "authIncorrectCreds": "Brugernavn eller adgangskode er ikke korrekt.", + "2faAlreadyEnabled": "2FA er allerede aktiveret.", + "foundChromiumVersion": "Fandt Chromium/Chrome. Version: {0}", + "programmingLanguages": "Programmeringssprog", + "HTTP Basic Auth": "HTTP Basic Auth", + "strategyManual": "Aktiv/Inaktiv manuelt", + "Retype the address.": "Genindtast adressen.", + "deleteMaintenanceMsg": "Er du sikker på at du vil slette denne vedligeholdelse?", + "pushoversounds alien": "Alien Alarm (lang)", + "2faEnabled": "2FA aktiveret.", + "2faDisabled": "2FA deaktiveret.", + "successAdded": "Tilføjet med succes.", + "successResumed": "Genoptaget med succes.", + "successDeleted": "Slettet med succes.", + "successEdited": "Ændret med succes.", + "successDisabled": "Deaktiveret med succes.", + "successEnabled": "Aktiveret med succes.", + "Monitor Setting": "{0}'s overvågningsindstilling", + "Enable Kafka SSL": "Aktivér Kafka SSL", + "Kafka SASL Options": "Kafka SASL Indstillinger", + "successAuthChangePassword": "Adgangskoden er blevet opdateret med succes.", + "smseagleGroup": "Telefonbog gruppenavn(e)", + "smseagleContact": "Telefonbog kontaktnavn(e)", + "showCertificateExpiry": "Vis udløbsdato for certifikat", + "sameAsServerTimezone": "Samme som serverens tidszone", + "wayToGetLineNotifyToken": "Du kan få en adgangsnøgle fra {0}", + "telegramSendSilentlyDescription": "Send beskeden lydløst. Brugerne vil modtage en notifikation uden lyd.", + "pushoverMessageTtl": "Message TTL (Sekunder)", + "setupDatabaseEmbeddedMariaDB": "Du skal ikke opsætte noget. Docker billedet har integreret og konfigureret MariaDB automatisk. Uptime Kuma vil forbinde til databasen vha. en Unix socket.", + "setupDatabaseMariaDB": "Oprette forbindelse til en ekstern MariaDB database. Du skal indstille databasens forbindelsesinformation.", + "styleElapsedTimeShowWithLine": "Vis (med linjer)", + "styleElapsedTime": "Forløbet tid under heartbeat-bjælken", + "templateMsg": "besked fra notifikationen", + "templateHeartbeatJSON": "objekt, der beskriver heartbeat", + "templateMonitorJSON": "objekt, der beskriver monitoren", + "templateLimitedToUpDownNotifications": "kun tilgængelig for UP/DOWN notifikationer", + "webhookAdditionalHeadersTitle": "Yderligere Headers", + "webhookAdditionalHeadersDesc": "Angiver yderligere headers, der sendes med webhooken. Hver header skal defineres som en JSON-nøgle/værdi.", + "webhookBodyCustomOption": "Brugerdefineret Body", + "successKeyword": "Succesnøgleord", + "startOrEndWithOnly": "Kun start eller slut med {0}", + "setAsDefaultProxyDescription": "Denne proxy vil som standard være aktiveret for nye monitorer. Du kan stadig deaktivere proxyen individuelt for hver monitor.", + "No consecutive dashes": "Ingen på hinanden følgende bindestreger", + "statusPageSpecialSlugDesc": "Speciel slug {0}: Denne side vises, når der ikke angives en slug", + "Query": "Kø", + "Add a new expiry notification day": "Tilføj en ny udløbsnotifikationsdag", + "Remove the expiry notification": "Fjern udløbsnotifikationsdagen", + "Refresh Interval": "Opdateringsinterval", + "Refresh Interval Description": "Statussiden vil udføre en fuld opdatering af sitet hvert {0} sekunder", + "RadiusSecret": "Radius-hemmelighed", + "RadiusCalledStationId": "Called Station ID", + "RadiusCalledStationIdDescription": "Identifikator for den kaldte enhed", + "RadiusCallingStationId": "Calling Station Id", + "RadiusCallingStationIdDescription": "Identifikator for den kaldende enhed", + "Check how to config it for WebSocket": "Tjek, hvordan det konfigureres til WebSocket", + "Connection String": "Forbindelsesstreng", + "Workstation": "Arbejdsstation", + "telegramMessageThreadID": "(Valgfrit) Beskedtråd-ID", + "telegramMessageThreadIDDescription": "Valgfri unik identifikator for målbeskedtråden (emnet) i forummet; kun for forum-supergrupper", + "telegramProtectContent": "Beskyt videresendelse/gemning", + "telegramProtectContentDescription": "Hvis aktiveret, vil bot-beskeder i Telegram være beskyttet mod videresendelse og gemning.", + "disableCloudflaredNoAuthMsg": "Du er i No Auth-tilstand, en adgangskode er ikke påkrævet.", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "En liste over notifikationstjenester kan findes i Home Assistant under “Developer Tools > Services”. Søg efter “notification” for at finde navnet på din enhed/telefon.", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Vælg derefter en handling, for eksempel at skifte scenen til en, hvor et RGB-lys er rødt.", + "backupRecommend": "Sikkerhedskopier venligst volumen eller data-mappen (./data/) direkte i stedet.", + "and": "og", + "startDateTime": "Startdato/-tidspunkt", + "endDateTime": "Slutdato/-tidspunkt", + "cronExpression": "Cron-udtryk", + "cronSchedule": "Tidsplan: ", + "warningTimezone": "Den bruger serverens tidszone", + "enableNSCD": "Aktivér NSCD (Name Service Cache Daemon) for at cache alle DNS-forespørgsler", + "chromeExecutable": "Chrome/Chromium-eksekverbar fil", + "Single Maintenance Window": "Enkelt vedligeholdelsesvindue", + "Edit Maintenance": "Rediger vedligeholdelse", + "emailCustomisableContent": "Brugerdefinerbart indhold", + "leave blank for default subject": "lad stå tomt for standardemne", + "emailCustomBody": "Brugerdefineret body", + "leave blank for default body": "lad stå tomt for standard-body", + "emailTemplateServiceName": "Servicenavn", + "emailTemplateHostnameOrURL": "Værtsnavn eller URL", + "emailTemplateStatus": "Status", + "emailTemplateMonitorJSON": "objekt, der beskriver monitoren", + "emailTemplateHeartbeatJSON": "objekt, der beskriver heartbeat", + "emailTemplateMsg": "besked fra notifikationen", + "emailTemplateLimitedToUpDownNotification": "kun tilgængelig for UP/DOWN heartbeats, ellers null", + "Select message type": "Vælg beskedtype", + "Send to channel": "Send til kanal", + "Create new forum post": "Opret nyt forumpost", + "postToExistingThread": "Send til eksisterende tråd / forumpost", + "forumPostName": "Forumpost-navn", + "threadForumPostID": "Tråd- / Forumpost-ID", + "e.g. {discordThreadID}": "f.eks. {discordThreadID}", + "wayToGetDiscordThreadId": "At hente en tråd- / forumpost-ID ligner processen for at hente en kanal-ID. Læs mere om, hvordan du får IDs {0}", + "Channel access token (Long-lived)": "Kanaladgangstoken (langvarig)", + "Your User ID": "Din bruger-ID", + "dataRetentionTimeError": "Opbevaringsperioden skal være 0 eller højere", + "infiniteRetention": "Sæt til 0 for uendelig opbevaring.", + "enableGRPCTls": "Tillad afsendelse af gRPC-forespørgsel med TLS-forbindelse", + "affectedStatusPages": "Vis denne vedligeholdelsesbesked på udvalgte statussider", + "invertKeywordDescription": "Se efter, at nøgleordet mangler i stedet for at være til stede.", + "octopushAPIKey": "“API-nøgle” fra HTTP API-legitimationsoplysninger i kontrolpanelet", + "octopushLogin": "“Login” fra HTTP API-legitimationsoplysninger i kontrolpanelet", + "pushoversounds pushover": "Pushover (standard)", + "pushoversounds bike": "Cykel", + "pushoversounds bugle": "Signalhorn", + "pushoversounds cashregister": "Kasseapparat", + "pushoversounds cosmic": "Kosmisk", + "pushoversounds falling": "Faldende", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Indkommende", + "pushoversounds intermission": "Pause", + "pushoversounds spacealarm": "Rumalarm", + "pushoversounds tugboat": "Slæbebåd", + "pushoversounds climb": "Klatring (lang)", + "pushoversounds persistent": "Vedvarende (lang)", + "pushoversounds echo": "Pushover Echo (lang)", + "pushoversounds updown": "Op Ned (lang)", + "GoogleChat": "Google Chat (kun Google Workspace)", + "styleElapsedTimeShowNoLine": "Vis (ingen linjer)", + "Slug": "Slug", + "The slug is already taken. Please choose another slug.": "Denne slug er allerede i brug. Vælg venligst en anden.", + "There might be a typing error in the address.": "Der er muligvis en stavfejl i adressen.", + "Long-Lived Access Token": "Long-Lived Access Token", + "Search monitored sites": "Søg overvågede sites", + "liquidIntroduction": "Templatability opnås via Liquid-templeringssproget. Se venligst {0} for brugsanvisninger. Her er de tilgængelige variabler:", + "templateLimitedToUpDownCertNotifications": "kun tilgængelig for UP/DOWN/Certifikatudløb notifikationer", + "shrinkDatabaseDescriptionSqlite": "Udløs database-{vacuum} for SQLite. {auto_vacuum} er allerede aktiveret, men dette defragmenterer ikke databasen eller ompakker de enkelte database-sider på samme måde som {vacuum}-kommandoen gør.", + "Check/Uncheck": "Markér/Afjern markering", + "enableProxyDescription": "Denne proxy vil ikke påvirke monitor-forespørgsler, før den er aktiveret. Du kan midlertidigt deaktivere proxyen for alle monitorer via aktiveringsstatus.", + "RadiusSecretDescription": "Delt hemmelighed mellem klient og server", + "noDockerHostMsg": "Ikke tilgængelig. Opsæt en Docker-host først.", + "DockerHostRequired": "Angiv venligst Docker-hosten for denne monitor.", + "tailscalePingWarning": "For at bruge Tailscale Ping-monitoren skal du installere Uptime Kuma uden Docker og også installere Tailscale-klienten på din server.", + "trustProxyDescription": "Tillid til ‘X-Forwarded-*’ headers. Hvis du vil hente den korrekte klient-IP, og din Uptime Kuma er bag en proxy såsom Nginx eller Apache, bør du aktivere dette.", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "En langvarig adgangstoken kan oprettes ved at klikke på dit profilnavn (nederst til venstre), rulle ned til bunden og derefter klikke på “Opret token”. ", + "backupOutdatedWarning": "Forældet: Da mange funktioner er blevet tilføjet, og denne backup-funktion er lidt vedligeholdelsesfri, kan den ikke generere eller gendanne en komplet backup.", + "invalidCronExpression": "Ugyldigt Cron-udtryk: {0}", + "chromeExecutableDescription": "For Docker-brugere: Hvis Chromium endnu ikke er installeret, kan det tage et par minutter at installere og vise testresultatet. Det kræver 1 GB diskplads.", + "Effective Date Range": "Gyldig datointerval (Valgfrit)", + "DateTime Range": "Dato-/tidsinterval", + "notificationRegional": "Regional", + "smtpLiquidIntroduction": "De følgende to felter kan tilpasses via Liquid-templateringssproget. Se venligst {0} for brugsanvisninger. Her er de tilgængelige variabler:", + "whatHappensAtForumPost": "Opret en ny forumpost. Dette sender IKKE beskeder i en eksisterende post. For at sende i en eksisterende post, brug ”{option}”", + "grpcMethodDescription": "Metodenavnet konverteres til camelCase-format, såsom sayHello, check osv.", + "affectedMonitorsDescription": "Vælg de monitorer, der er påvirket af den aktuelle vedligeholdelse", + "jsonQueryDescription": "Parse og udtræk specifikke data fra serverens JSON-svar ved hjælp af en JSON-forespørgsel, eller brug ”$” for det rå svar, hvis JSON ikke forventes. Resultatet sammenlignes derefter med den forventede værdi som strenge. Se {0} for dokumentation, og brug {1} til at eksperimentere med forespørgsler.", + "pushoversounds classical": "Klassisk", + "pushoversounds pianobar": "Piano Bar", + "wayToGetKookBotToken": "Opret en applikation og få din bot-token på {0}", + "wayToGetKookGuildID": "Tænd for ‘Developer Mode’ i Kook-indstillingerne, og højreklik på guilden for at få dens ID", + "successKeywordExplanation": "MQTT-nøgleord, der vil blive betragtet som succes", + "endpoint": "endpoint", + "topicExplanation": "MQTT-emne til overvågning", + "settingUpDatabaseMSG": "Opsætter databasen. Det kan tage et stykke tid, så vær tålmodig.", + "now": "nu", + "Json Query Expression": "Json Query Expression", + "locally configured mail transfer agent": "lokalt konfigureret mail overførsels agent", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Indtast enten værtsnavnet på serveren du vil forbinde til eller {localhost} hvis du planlægger at bruge en {local_mta}", + "timeoutAfter": "Timeout efter {0} sekunder", + "ignoredTLSError": "TLS/SSL-fejl er blevet ignoreret", + "Invert Keyword": "Inverter Nøgleord", + "Resend Notification if Down X times consecutively": "Send meddelelse igen, hvis Ned X gange i træk", + "Expected Value": "Forventede Værdi", + "setupDatabaseSQLite": "En simpel database-fil, anbefalet til mindre implementeringer. Før v2.0.0, brugte Uptime Kuma SQLite som standard database.", + "ignoreTLSErrorGeneral": "Ignorér TLS/SSL-fejl for forbindelsen", + "time ago": "{0} siden", + "-year": "-år", + "Host URL": "Host URL", + "Request Timeout": "Anmod Timeout", + "Cannot connect to the socket server": "Kan ikke oprette forbindelse til socket serveren", + "pushViewCode": "Hvordan bruger man Push Monitor? (Se kode)", + "Path": "Sti", + "Add Tags": "Tilføj Tags", + "tagAlreadyOnMonitor": "Dette tag (navn og værdi) er allerde tilføjet til denne overvågning, eller afventer tilføjelse.", + "defaultFriendlyName": "Ny Overvågning", + "Events cleared successfully": "Begivenheder blev ryddet.", + "mqttHostnameTip": "Brug venligst dette format {hostnameFormat}", + "mqttWebSocketPath": "MQTT WebSocket Sti", + "telegramUseTemplateDescription": "Hvis aktiveret vil beskeden blive sendt med en brugerdefineret skabelon.", + "Add a domain": "Tilføj et domæne", + "mqttWebsocketPathExplanation": "WebSocket sti for MQTT over WebSocket forbindelser (f.eks. /mqtt)", + "telegramUseTemplate": "Brug brugerdefineret besked-skabelon", + "telegramServerUrl": "(Valgfrit) Server URL", + "Clear All Events": "Ryd alle begivenheder", + "clearAllEventsMsg": "Er du sikker på du vil slette alle begivenheder?", + "Remove domain": "Fjern domæne '{0}'", + "tagNameExists": "Et system tag med dette navn eksisterer allerede. Vælg det fra listen eller brug et andet navn.", + "year": "år", + "mariadbCaCertificateHelptext": "Indsæt CA certifikat i PEM format for at bruge et self-signed certifikat. Efterlad blank hvis din database bruger et certifikat signeret af en offentlig CA.", + "versionIs": "Version: {version}", + "enableSSL": "Slå SSL/TLS til", + "mariadbUseSSLHelptext": "Slå krypteret forbindelse til mod databasen. Påkrævet for de fleste cloud databaser.", + "mariadbCaCertificateLabel": "CA certifikat" +} diff --git a/src/lang/de-CH.json b/src/lang/de-CH.json new file mode 100644 index 0000000..0fc5d15 --- /dev/null +++ b/src/lang/de-CH.json @@ -0,0 +1,1291 @@ +{ + "languageName": "Deutsch (Schweiz)", + "Settings": "Einstellungen", + "Dashboard": "Überblick", + "New Update": "Update verfügbar", + "Language": "Sprache", + "Appearance": "Erscheinungsbild", + "Theme": "Erscheinungsbild", + "General": "Allgemein", + "Version": "Version", + "Check Update On GitHub": "Auf GitHub nach Updates suchen", + "List": "Liste", + "Home": "Home", + "Add": "Hinzufügen", + "Add New Monitor": "Neuen Monitor hinzufügen", + "Quick Stats": "Übersicht", + "Up": "Aktiv", + "Down": "Inaktiv", + "Pending": "Ausstehend", + "Unknown": "Unbekannt", + "Cannot connect to the socket server": "Es kann keine Verbindung zum Socket-Server hergestellt werden", + "Reconnecting...": "Die Verbindung wird wiederhergestellt...", + "Pause": "Pausieren", + "pauseDashboardHome": "Pausiert", + "Name": "Name", + "Status": "Status", + "DateTime": "Datum / Uhrzeit", + "Message": "Nachricht", + "No important events": "Keine wichtigen Ereignisse", + "Resume": "Fortsetzen", + "Edit": "Bearbeiten", + "Delete": "Löschen", + "Current": "Aktuell", + "Uptime": "Verfügbarkeit", + "Cert Exp.": "Zertifikatsablauf", + "day": "Tag | Tage", + "-day": "-Tage", + "hour": "Stunde", + "-hour": "-Stunden", + "checkEverySecond": "Überprüfe alle {0} Sekunden", + "Response": "Antwortzeit", + "Ping": "Ping", + "Monitor Type": "Monitor-Typ", + "Keyword": "Suchwort", + "Friendly Name": "Anzeigename", + "URL": "URL", + "Hostname": "Hostname", + "Port": "Port", + "Heartbeat Interval": "Prüfintervall", + "Retries": "Wiederholungen", + "retriesDescription": "Maximale Wiederholungen, bevor der Dienst als inaktiv markiert und eine Benachrichtigung gesendet wird", + "Advanced": "Erweitert", + "ignoreTLSError": "Ignoriere TLS-/SSL-Fehler von Webseiten", + "Upside Down Mode": "Umgekehrter Modus", + "upsideDownModeDescription": "Im umgekehrten Modus wird der Dienst als inaktiv angezeigt, wenn er erreichbar ist.", + "Max. Redirects": "Max. Weiterleitungen", + "maxRedirectDescription": "Maximale Anzahl von Weiterleitungen, denen gefolgt werden soll. Auf 0 setzen, um Weiterleitungen zu deaktivieren.", + "Accepted Status Codes": "Erlaubte HTTP-Statuscodes", + "acceptedStatusCodesDescription": "Statuscodes auswählen, die als erfolgreiche Verbindung gelten sollen.", + "Save": "Speichern", + "Notifications": "Benachrichtigungen", + "Not available, please setup.": "Nicht verfügbar, bitte einrichten.", + "Setup Notification": "Benachrichtigung einrichten", + "Light": "Hell", + "Dark": "Dunkel", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Erscheinungsbild - Zeitleiste", + "Normal": "Normal", + "Bottom": "Unten", + "None": "Keine", + "Timezone": "Zeitzone", + "Search Engine Visibility": "Sichtbarkeit für Suchmaschinen", + "Allow indexing": "Indizierung zulassen", + "Discourage search engines from indexing site": "Suchmaschinen darum bitten, die Seite nicht zu indizieren", + "Change Password": "Passwort ändern", + "Current Password": "Aktuelles Passwort", + "New Password": "Neues Passwort", + "Repeat New Password": "Neues Passwort wiederholen", + "passwordNotMatchMsg": "Passwörter stimmen nicht überein.", + "Update Password": "Passwort aktualisieren", + "Disable Auth": "Authentifizierung deaktivieren", + "Enable Auth": "Authentifizierung aktivieren", + "disableauth.message1": "Bist du sicher das du die {disableAuth} möchtest?", + "disable authentication": "Authentifizierung deaktivieren", + "disableauth.message2": "Dies ist für Szenarien gedacht, {intendThirdPartyAuth} vor Uptime Kuma geschaltet hat, wie z.B. Cloudflare Access, Authelia oder andere Authentifizierungsmechanismen.", + "where you intend to implement third-party authentication": "in denen man eine externe Authentifizierung", + "Please use this option carefully!": "Bitte mit Vorsicht nutzen!", + "Logout": "Ausloggen", + "notificationDescription": "Benachrichtigungen müssen einem Monitor zugewiesen werden, damit diese funktionieren.", + "Leave": "Verlassen", + "I understand, please disable": "Ich verstehe, bitte deaktivieren", + "Confirm": "Bestätigen", + "Yes": "Ja", + "No": "Nein", + "Username": "Benutzername", + "Password": "Passwort", + "Remember me": "Angemeldet bleiben", + "Login": "Einloggen", + "No Monitors, please": "Keine Monitore, bitte", + "add one": "hinzufügen", + "Notification Type": "Benachrichtigungsdienst", + "Email": "E-Mail", + "Test": "Test", + "Certificate Info": "Zertifikatsinformation", + "keywordDescription": "Ein Suchwort in der HTML- oder JSON-Ausgabe finden. Bitte beachte: es wird zwischen Gross-/Kleinschreibung unterschieden.", + "deleteMonitorMsg": "Bist du sicher, dass du den Monitor löschen möchtest?", + "deleteNotificationMsg": "Möchtest du diese Benachrichtigung wirklich für alle Monitore löschen?", + "resolverserverDescription": "Cloudflare ist als der Standardserver festgelegt. Dieser kann jederzeit geändert werden.", + "Resolver Server": "Auflösungsserver", + "rrtypeDescription": "Wähle den RR Typ aus, welchen du überwachen möchtest", + "Last Result": "Letztes Ergebnis", + "pauseMonitorMsg": "Bist du sicher, dass du den Monitor pausieren möchtest?", + "clearEventsMsg": "Bist du sicher, dass du alle Ereignisse für diesen Monitor löschen möchtest?", + "clearHeartbeatsMsg": "Bist du sicher, dass du alle Prüfintervalle für diesen Monitor löschen möchtest?", + "Clear Data": "Lösche Daten", + "Events": "Ereignisse", + "Heartbeats": "Prüfintervalle", + "confirmClearStatisticsMsg": "Bist du dir sicher, dass du ALLE Statistiken löschen möchtest?", + "Create your admin account": "Erstelle dein Admin-Konto", + "Repeat Password": "Passwort erneut eingeben", + "Resource Record Type": "Ressourcen Record Typ", + "Export": "Export", + "Import": "Import", + "respTime": "Antw.-Zeit (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Standardmässig aktiviert", + "Apply on all existing monitors": "Auf alle existierenden Monitore anwenden", + "enableDefaultNotificationDescription": "Für jeden neuen Monitor wird diese Benachrichtigung standardmässig aktiviert. Die Benachrichtigung kann weiterhin für jeden Monitor separat deaktiviert werden.", + "Create": "Erstellen", + "Auto Get": "Auto Get", + "backupDescription": "Es können alle Monitore und Benachrichtigungen in einer JSON-Datei gesichert werden.", + "backupDescription2": "PS: Verlaufs- und Ereignisdaten sind nicht enthalten.", + "backupDescription3": "Sensible Daten wie Benachrichtigungs-Token sind in der Exportdatei enthalten, bitte bewahre sie sorgfältig auf.", + "alertNoFile": "Bitte wähle eine Datei zum Importieren aus.", + "alertWrongFileType": "Bitte wähle eine JSON-Datei aus.", + "Clear all statistics": "Lösche alle Statistiken", + "importHandleDescription": "Wähle 'Vorhandene überspringen' aus, wenn jeder Monitor oder jede Benachrichtigung mit demselben Namen übersprungen werden soll. 'Überschreiben' löscht jeden vorhandenen Monitor sowie Benachrichtigungen.", + "Skip existing": "Vorhandene überspringen", + "Overwrite": "Überschreiben", + "Options": "Optionen", + "confirmImportMsg": "Möchtest du das Backup wirklich importieren? Bitte stelle sicher, dass die richtige Import-Option ausgewählt ist.", + "Keep both": "Beide behalten", + "twoFAVerifyLabel": "Bitte trage deinen Token ein, um zu verifizieren, dass 2FA funktioniert:", + "Verify Token": "Token verifizieren", + "Setup 2FA": "2FA einrichten", + "Enable 2FA": "2FA aktivieren", + "Disable 2FA": "2FA deaktivieren", + "2FA Settings": "2FA-Einstellungen", + "confirmEnableTwoFAMsg": "Bist du sicher, dass du 2FA aktivieren möchtest?", + "confirmDisableTwoFAMsg": "Bist du sicher, dass du 2FA deaktivieren möchtest?", + "tokenValidSettingsMsg": "Token gültig! Du kannst jetzt die 2FA-Einstellungen speichern.", + "Two Factor Authentication": "Zwei-Faktor-Authentifizierung", + "Active": "Aktiv", + "Inactive": "Inaktiv", + "Token": "Token", + "Show URI": "URI anzeigen", + "Tags": "Tags", + "Add New below or Select...": "Einen bestehenden Tag auswählen oder neuen hinzufügen…", + "Tag with this name already exist.": "Ein Tag mit diesem Namen existiert bereits.", + "Tag with this value already exist.": "Ein Tag mit diesem Wert existiert bereits.", + "color": "Farbe", + "value (optional)": "Wert (optional)", + "Gray": "Grau", + "Red": "Rot", + "Orange": "Orange", + "Green": "Grün", + "Blue": "Blau", + "Indigo": "Indigo", + "Purple": "Lila", + "Pink": "Pink", + "Search...": "Suchen…", + "Heartbeat Retry Interval": "Überprüfungsintervall", + "Resend Notification if Down X times consecutively": "Benachrichtigung erneut senden, wenn Inaktiv X mal hintereinander", + "retryCheckEverySecond": "Alle {0} Sekunden neu versuchen", + "resendEveryXTimes": "Erneut versenden alle {0} mal", + "resendDisabled": "Erneut versenden deaktiviert", + "Import Backup": "Backup importieren", + "Export Backup": "Backup exportieren", + "Avg. Ping": "Ping ø", + "Avg. Response": "Antwortzeit ø", + "Entry Page": "Einstiegsseite", + "statusPageNothing": "Noch ist hier nichts. Bitte füge eine Gruppe oder einen Monitor hinzu.", + "No Services": "Keine Dienste", + "All Systems Operational": "Alle Systeme betriebsbereit", + "Partially Degraded Service": "Teilweise beeinträchtigter Dienst", + "Degraded Service": "Eingeschränkter Dienst", + "Add Group": "Gruppe hinzufügen", + "Add a monitor": "Monitor hinzufügen", + "Edit Status Page": "Bearbeite Status-Seite", + "Go to Dashboard": "Gehe zum Dashboard", + "Status Page": "Status-Seite", + "Status Pages": "Status-Seiten", + "telegram": "Telegram", + "webhook": "Webhook", + "smtp": "E-Mail (SMTP)", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Unterstützung für 50+ Benachrichtigungsdienste)", + "GoogleChat": "Google Chat (nur Google Workspace)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "Primary Base URL": "Primär URL", + "Push URL": "Push URL", + "needPushEvery": "Du solltest diese URL alle {0} Sekunden aufrufen.", + "pushOptionalParams": "Optionale Parameter: {0}", + "defaultNotificationName": "Mein {notification} Alarm ({number})", + "here": "hier", + "Required": "Erforderlich", + "Bot Token": "Bot Token", + "wayToGetTelegramToken": "Hier kannst du einen Token erhalten {0}.", + "Chat ID": "Chat ID", + "supportTelegramChatID": "Unterstützt Direkt Chat / Gruppe / Kanal Chat-ID's", + "wayToGetTelegramChatID": "Du kannst die Chat-ID erhalten, indem du eine Nachricht an den Bot sendest und zu dieser URL gehst, um die chat_id zu sehen", + "YOUR BOT TOKEN HERE": "HIER DEIN BOT TOKEN", + "chatIDNotFound": "Chat-ID wurde nicht gefunden: bitte sende zuerst eine Nachricht an diesen Bot", + "Post URL": "Post URL", + "Content Type": "Content Type", + "webhookJsonDesc": "{0} ist gut für alle modernen HTTP-Server, wie z.B. Express.js, geeignet", + "webhookFormDataDesc": "{multipart} ist gut für PHP. Das JSON muss mit {decodeFunction} verarbeitet werden", + "secureOptionNone": "Keine / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "TLS-Fehler ignorieren", + "From Email": "Absender E-Mail", + "emailCustomSubject": "Benutzerdefinierter Betreff", + "To Email": "Empfänger E-Mail", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord Webhook URL", + "wayToGetDiscordURL": "Du kannst diese erhalten, indem du zu den Servereinstellungen gehst -> Notifikationen -> Webhooks -> Neuer Webhook", + "Bot Display Name": "Bot-Anzeigename", + "Prefix Custom Message": "Benutzerdefinierter Nachrichten Präfix", + "Hello @everyone is...": "Hallo {'@'}everyone ist…", + "Webhook URL": "Webhook URL", + "wayToGetTeamsURL": "Wie eine Webhook-URL erstellt werden kann, erfährst du {0}.", + "Number": "Nummer", + "Recipients": "Empfänger", + "needSignalAPI": "Es wird ein Signal Client mit REST-API benötigt.", + "wayToCheckSignalURL": "Du kannst diese URL aufrufen, um zu sehen, wie du eine einrichtest:", + "signalImportant": "WICHTIG: Gruppen und Nummern können in Empfängern nicht gemischt werden!", + "Application Token": "Anwendungstoken", + "Server URL": "Server URL", + "Priority": "Priorität", + "Icon Emoji": "Icon Emoji", + "Channel Name": "Kanalname", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Weitere Informationen zu Webhooks auf: {0}", + "aboutChannelName": "Gebe den Kanalnamen ein in {0} Feld Kanalname, falls du den Webhook-Kanal umgehen möchtest. Ex: #other-channel", + "aboutKumaURL": "Wenn das Feld für die Uptime Kuma URL leer gelassen wird, wird standardmässig die GitHub Projekt Seite verwendet.", + "emojiCheatSheet": "Emoji Cheat Sheet: {0}", + "User Key": "Benutzerschlüssel", + "Device": "Gerät", + "Message Title": "Nachrichtentitel", + "Notification Sound": "Benachrichtigungston", + "More info on:": "Mehr Infos auf: {0}", + "pushoverDesc1": "Notfallpriorität (2) hat standardmässig 30 Sekunden Auszeit zwischen den Versuchen und läuft nach 1 Stunde ab.", + "pushoverDesc2": "Fülle das Geräte Feld aus, wenn du Benachrichtigungen an verschiedene Geräte senden möchtest.", + "pushoverMessageTtl": "Message TTL (Sekunden)", + "SMS Type": "SMS Typ", + "octopushTypePremium": "Premium (Schnell - zur Benachrichtigung empfohlen)", + "octopushTypeLowCost": "Kostengünstig (Langsam - manchmal vom Betreiber gesperrt)", + "checkPrice": "Prüfe {0} Preise:", + "octopushLegacyHint": "Verwendest du die Legacy-Version von Octopush (2011-2020) oder die neue Version?", + "Check octopush prices": "Vergleiche die Oktopush Preise {0}.", + "octopushPhoneNumber": "Telefonnummer (Internationales Format, z.B : +49612345678) ", + "octopushSMSSender": "Name des SMS-Absenders : 3-11 alphanumerische Zeichen und Leerzeichen (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Geräte ID", + "Apprise URL": "Apprise URL", + "Example:": "Beispiel: {0}", + "Read more:": "Weiterlesen: {0}", + "Status:": "Status: {0}", + "Read more": "Weiterlesen", + "appriseInstalled": "Apprise ist installiert.", + "appriseNotInstalled": "Apprise ist nicht installiert. {0}", + "Access Token": "Access Token", + "Channel access token": "Kanalzugriffstoken", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console - {0}", + "Basic Settings": "Grundeinstellungen", + "User ID": "User ID", + "Messaging API": "Messaging API", + "wayToGetLineChannelToken": "Rufe zuerst {0} auf, erstelle dann einen Provider und Channel (Messaging API). Als nächstes kannst du den Channel access token und die User ID aus den oben genannten Menüpunkten abrufen.", + "Icon URL": "Icon URL", + "aboutIconURL": "Du kannst einen Link zu einem Bild in 'Icon URL' übergeben um das Standardprofilbild zu überschreiben. Wird nicht verwendet, wenn ein Icon Emoji gesetzt ist.", + "aboutMattermostChannelName": "Du kannst den Standardkanal, auf dem der Webhook gesendet wird überschreiben, indem der Kanalnamen in das Feld 'Channel Name' eingeben wird. Dies muss in den Mattermost Webhook-Einstellungen aktiviert werden. Ex: #other-channel", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - billig, aber langsam und oft überladen. Auf polnische Empfänger beschränkt.", + "promosmsTypeFlash": "SMS FLASH - Die Nachricht wird automatisch auf dem Empfängergerät angezeigt. Auf polnische Empfänger beschränkt.", + "promosmsTypeFull": "SMS FULL - Premium Stufe von SMS, es kann der Absendernamen verwendet werden (Der Name musst zuerst registriert werden). Zuverlässig für Warnungen.", + "promosmsTypeSpeed": "SMS SPEED - Höchste Priorität im System. Sehr schnell und zuverlässig, aber teuer (Ungefähr das doppelte von SMS FULL).", + "promosmsPhoneNumber": "Telefonnummer (für polnische Empfänger können die Vorwahlen übersprungen werden)", + "promosmsSMSSender": "Name des SMS-Absenders : vorregistrierter Name oder einer der Standardwerte: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu Webhook URL", + "matrixHomeserverURL": "Heimserver URL (mit http(s):// und optionalen Ports)", + "Internal Room Id": "Interne Raum-ID", + "matrixDesc1": "Die interne Raum-ID findest du im erweiterten Bereich der Raumeinstellungen im Matrix-Client. Es sollte aussehen wie z.B. !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Es wird dringend empfohlen einen neuen Benutzer anzulegen und nicht den Zugriffstoken deines eigenen Matrix-Benutzers zu verwenden. Anderenfalls ermöglicht es vollen Zugriff auf dein Konto und alle Räume, denen du beigetreten bist. Erstelle stattdessen einen neuen Benutzer und lade ihn nur in den Raum ein, in dem du die Benachrichtigung erhalten möchtest. Du kannst den Zugriffstoken erhalten, indem du Folgendes ausführst {0}", + "Method": "Methode", + "Body": "Body", + "Headers": "Headers", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "Der Header ist kein gültiges JSON: ", + "BodyInvalidFormat": "Der Body ist kein gültiges JSON: ", + "Monitor History": "Monitor Verlauf", + "clearDataOlderThan": "Bewahre die Aufzeichnungsdaten für {0} Tage auf.", + "PasswordsDoNotMatch": "Passwörter stimmen nicht überein.", + "records": "Einträge", + "One record": "Ein Eintrag", + "steamApiKeyDescription": "Um einen Steam Game Server zu überwachen, wird ein Steam Web-API-Schlüssel benötigt. Dieser kann hier registriert werden: ", + "Current User": "Aktueller Benutzer", + "recent": "Letzte", + "Done": "Fertig", + "Info": "Info", + "Security": "Sicherheit", + "Steam API Key": "Steam API Key", + "Shrink Database": "Datenbank verkleinern", + "Pick a RR-Type...": "Wähle ein RR-Typ aus…", + "Pick Accepted Status Codes...": "Wähle akzeptierte Statuscodes aus…", + "Default": "Standard", + "HTTP Options": "HTTP Optionen", + "Create Incident": "Vorfall erstellen", + "Title": "Titel", + "Content": "Inhalt", + "Style": "Stil", + "info": "info", + "warning": "warnung", + "danger": "gefahr", + "primary": "primär", + "light": "hell", + "dark": "dunkel", + "Post": "Veröffentlichen", + "Please input title and content": "Bitte Titel und Inhalt eingeben", + "Created": "Erstellt", + "Last Updated": "Zuletzt aktualisiert", + "Unpin": "Loslösen", + "Switch to Light Theme": "Zu hellem Thema wechseln", + "Switch to Dark Theme": "Zum dunklen Thema wechseln", + "Show Tags": "Tags anzeigen", + "Hide Tags": "Tags ausblenden", + "Description": "Beschreibung", + "No monitors available.": "Keine Monitore verfügbar.", + "Add one": "Hinzufügen", + "No Monitors": "Keine Monitore", + "Untitled Group": "Gruppe ohne Titel", + "Services": "Dienste", + "Discard": "Verwerfen", + "Cancel": "Abbrechen", + "Powered by": "Bereitgestellt mit", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API Benutzername (inkl. webapi_ prefix)", + "serwersmsAPIPassword": "API Passwort", + "serwersmsPhoneNumber": "Telefonnummer", + "serwersmsSenderName": "Name des SMS-Absenders (über Kundenportal registriert)", + "stackfield": "Stackfield", + "clicksendsms": "ClickSend SMS", + "apiCredentials": "API Zugangsdaten", + "smtpDkimSettings": "DKIM Einstellungen", + "smtpDkimDesc": "Details zur Konfiguration sind in der Nodemailer DKIM {0} zu finden.", + "documentation": "Dokumentation", + "smtpDkimDomain": "Domain Name", + "smtpDkimKeySelector": "Schlüssel Auswahl", + "smtpDkimPrivateKey": "Privater Schlüssel", + "smtpDkimHashAlgo": "Hash-Algorithmus (Optional)", + "smtpDkimheaderFieldNames": "Zu validierende Header-Schlüssel (optional)", + "smtpDkimskipFields": "Zu ignorierende Header Schlüssel (optional)", + "PushByTechulus": "Push by Techulus", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API Endpunkt", + "alertaEnvironment": "Umgebung", + "alertaApiKey": "API Schlüssel", + "alertaAlertState": "Alarmstatus", + "alertaRecoverState": "Wiederherstellungsstatus", + "deleteStatusPageMsg": "Bist du sicher, dass du diese Status-Seite löschen willst?", + "Proxies": "Proxys", + "default": "Standard", + "enabled": "Aktiviert", + "setAsDefault": "Als Standard setzen", + "deleteProxyMsg": "Bist du sicher, dass du diesen Proxy für alle Monitore löschen willst?", + "proxyDescription": "Proxys müssen einem Monitor zugewiesen werden, um zu funktionieren.", + "enableProxyDescription": "Dieser Proxy wird keinen Effekt auf Monitor-Anfragen haben, bis er aktiviert ist. Du kannst ihn temporär von allen Monitoren nach Aktivierungsstatus deaktivieren.", + "setAsDefaultProxyDescription": "Dieser Proxy wird standardmässig für alle neuen Monitore aktiviert sein. Du kannst den Proxy immer noch für jeden Monitor einzeln deaktivieren.", + "Certificate Chain": "Zertifikatskette", + "Valid": "Gültig", + "Invalid": "Ungültig", + "AccessKeyId": "AccessKey ID", + "SecretAccessKey": "Geheimer Zugriffsschlüssel", + "PhoneNumbers": "Telefonnummern", + "TemplateCode": "Vorlagencode", + "SignName": "Signaturname", + "Sms template must contain parameters: ": "SMS Vorlage muss folgende Parameter enthalten: ", + "Bark Endpoint": "Bark Endpunkt", + "WebHookUrl": "Webhook URL", + "SecretKey": "Geheimer Schlüssel", + "For safety, must use secret key": "Zur Sicherheit muss ein geheimer Schlüssel verwendet werden", + "Device Token": "Gerätetoken", + "Platform": "Platform", + "Huawei": "Huawei", + "High": "Hoch", + "Retry": "Wiederholungen", + "Topic": "Thema", + "WeCom Bot Key": "WeCom Bot Schlüssel", + "Setup Proxy": "Proxy einrichten", + "Proxy Protocol": "Proxy Protokoll", + "Proxy Server": "Proxy-Server", + "Proxy server has authentication": "Proxy-Server hat Authentifizierung", + "User": "Benutzer", + "Installed": "Installiert", + "Not installed": "Nicht installiert", + "Running": "Läuft", + "Not running": "Gestoppt", + "Remove Token": "Token entfernen", + "Start": "Start", + "Stop": "Stop", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Neue Status-Seite hinzufügen", + "Slug": "Slug", + "Accept characters:": "Akzeptierte Zeichen:", + "startOrEndWithOnly": "Nur mit {0} anfangen und enden", + "No consecutive dashes": "Keine aufeinanderfolgenden Bindestriche", + "Next": "Weiter", + "The slug is already taken. Please choose another slug.": "Der Slug ist bereits in Verwendung. Bitte wähle einen anderen.", + "No Proxy": "Kein Proxy", + "Authentication": "Authentifizierung", + "HTTP Basic Auth": "HTTP Basisauthentifizierung", + "New Status Page": "Neue Status-Seite", + "Page Not Found": "Seite nicht gefunden", + "Reverse Proxy": "Reverse Proxy", + "Backup": "Sicherung", + "About": "Über", + "wayToGetCloudflaredURL": "(Lade Cloudflare von {0} herunter)", + "cloudflareWebsite": "Cloudflare Webseite", + "Message:": "Nachricht:", + "Don't know how to get the token? Please read the guide:": "Du weisst nicht, wie man den Token bekommt? Lies die Anleitung dazu:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Die aktuelle Verbindung kann unterbrochen werden, wenn du aktuell über Cloudflare Tunnel verbunden bist. Bist du sicher, dass du es stoppen willst? Gib zur Bestätigung dein aktuelles Passwort ein.", + "Other Software": "Andere Software", + "For example: nginx, Apache and Traefik.": "Zum Beispiel: nginx, Apache und Traefik.", + "Please read": "Bitte lesen", + "Subject:": "Betreff:", + "Valid To:": "Gültig bis:", + "Days Remaining:": "Tage verbleibend:", + "Issuer:": "Aussteller:", + "Fingerprint:": "Fingerabdruck:", + "No status pages": "Keine Status-Seiten", + "Domain Name Expiry Notification": "Benachrichtigung bei Ablauf des Domainnamens", + "Customize": "Anpassen", + "Custom Footer": "Eigener Footer", + "Custom CSS": "Eigenes CSS", + "Footer Text": "Fusszeile", + "Show Powered By": "Zeige 'Bereitgestellt mit'", + "Date Created": "Erstellt am", + "Domain Names": "Domainnamen", + "signedInDisp": "Angemeldet als {0}", + "signedInDispDisabled": "Authentifizierung deaktiviert.", + "dnsPortDescription": "DNS Server Port. Standard ist 53. Der Port kann jederzeit geändert werden.", + "topic": "Thema", + "topicExplanation": "MQTT Thema für den monitor", + "successMessage": "Erfolgsnachricht", + "successMessageExplanation": "MQTT Nachricht, die als Erfolg angesehen wird", + "error": "Fehler", + "critical": "kritisch", + "wayToGetPagerDutyKey": "Dieser kann unter Service -> Service Directory -> (Select a service) -> Integrations -> Add integration gefunden werden. Hier muss nach \"Events API V2\" gesucht werden. Mehr informationen {0}", + "Integration Key": "Schlüssel der Integration", + "Integration URL": "URL der Integration", + "Auto resolve or acknowledged": "Automatisch lösen oder bestätigen", + "do nothing": "nichts tun", + "auto acknowledged": "automatisch bestätigen", + "auto resolve": "automatisch lösen", + "Bark Group": "Bark Gruppe", + "Bark Sound": "Bark Klang", + "HTTP Headers": "HTTP Kopfzeilen", + "Trust Proxy": "Vertrauenswürdiger Proxy", + "Proxy": "Proxy", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "OneBot HTTP Adresse", + "onebotMessageType": "OneBot Nachrichtentyp", + "onebotGroupMessage": "Gruppe", + "onebotPrivateMessage": "Privat", + "onebotUserOrGroupId": "Gruppe/Nutzer ID", + "onebotSafetyTips": "Zur Sicherheit ein access token setzen", + "PushDeer Key": "PushDeer Schlüssel", + "RadiusSecret": "Radius Geheimnis", + "RadiusSecretDescription": "Geteiltes Geheimnis zwischen Client und Server", + "RadiusCalledStationId": "ID der angesprochenen Station", + "RadiusCalledStationIdDescription": "Identifikation des angesprochenen Geräts", + "RadiusCallingStationId": "ID der ansprechenden Station", + "RadiusCallingStationIdDescription": "Identifikation des ansprechenden Geräts", + "Certificate Expiry Notification": "Benachrichtigung ablaufendes Zertifikat", + "API Username": "API Nutzername", + "API Key": "API Schlüssel", + "Recipient Number": "Empfängernummer", + "From Name/Number": "Von Name/Nummer", + "Leave blank to use a shared sender number.": "Leer lassen um eine geteilte Absendernummer zu nutzen.", + "Octopush API Version": "Octopush API Version", + "Legacy Octopush-DM": "Legacy Octopush-DM", + "endpoint": "Endpunkt", + "octopushAPIKey": "\"API Schlüssel\" der HTTP API Zugangsdaten im control panel", + "octopushLogin": "\"Login\" der HTTP API Zugangsdaten im control panel", + "promosmsLogin": "API Login Name", + "promosmsPassword": "API Password", + "pushoversounds pushover": "Pushover (Standard)", + "pushoversounds bike": "Fahrrad", + "pushoversounds bugle": "Signalhorn", + "pushoversounds cashregister": "Kasse", + "pushoversounds classical": "Klassisch", + "pushoversounds cosmic": "Kosmisch", + "pushoversounds falling": "Abfallend", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Eingang", + "pushoversounds intermission": "Pause", + "pushoversounds magic": "Magisch", + "pushoversounds mechanical": "Mechanisch", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Sirene", + "pushoversounds spacealarm": "Space Alarm", + "pushoversounds tugboat": "Schlepper Horn", + "pushoversounds alien": "Ausserirdisch (lang)", + "pushoversounds climb": "Ansteigende (lang)", + "pushoversounds persistent": "Hartnäckig (lang)", + "pushoversounds echo": "Pushover Echo (lang)", + "pushoversounds updown": "Auf und Ab (lang)", + "pushoversounds vibrate": "Nur vibrieren", + "pushoversounds none": "Nichts (Stille)", + "pushyAPIKey": "Geheimer API Schlüssel", + "pushyToken": "Gerätetoken", + "Show update if available": "Verfügbare Updates anzeigen", + "Also check beta release": "Auch nach beta Versionen schauen", + "Using a Reverse Proxy?": "Wird ein Reverse Proxy genutzt?", + "Check how to config it for WebSocket": "Prüfen, wie er für die Nutzung mit WebSocket konfiguriert wird", + "Steam Game Server": "Steam Spielserver", + "Most likely causes:": "Wahrscheinliche Ursachen:", + "The resource is no longer available.": "Die Quelle ist nicht mehr verfügbar.", + "There might be a typing error in the address.": "Es gibt einen Tippfehler in der Adresse.", + "What you can try:": "Was du versuchen kannst:", + "Retype the address.": "Schreibe die Adresse erneut.", + "Go back to the previous page.": "Gehe zur vorigen Seite.", + "Coming Soon": "Kommt bald", + "wayToGetClickSendSMSToken": "Du kannst einen API Nutzernamen und Schlüssel unter {here} erhalten.", + "Connection String": "Verbindungstext", + "Query": "Abfrage", + "settingsCertificateExpiry": "TLS Zertifikatsablauf", + "certificationExpiryDescription": "HTTPS Monitore senden eine Benachrichtigung, wenn das Zertifikat abläuft in:", + "Setup Docker Host": "Docker Host einrichten", + "Connection Type": "Verbindungstyp", + "Docker Daemon": "Docker Daemon", + "deleteDockerHostMsg": "Bist du sicher diesen docker host für alle Monitore zu löschen?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker Container", + "Container Name / ID": "Container Name / ID", + "Docker Host": "Docker Host", + "Docker Hosts": "Docker Hosts", + "ntfy Topic": "ntfy Thema", + "Domain": "Domain", + "Workstation": "Workstation", + "disableCloudflaredNoAuthMsg": "Du bist im nicht-authentifizieren Modus, ein Passwort wird nicht benötigt.", + "trustProxyDescription": "Vertraue 'X-Forwarded-*' headern. Wenn man die richtige Client IP erhalten möchte und Uptime Kuma hinter einem Proxy wie Nginx oder Apache läuft, sollte dies aktiviert werden.", + "wayToGetLineNotifyToken": "Du kannst hier ein Token erhalten: {0}", + "Examples": "Beispiele", + "Home Assistant URL": "Home Assistant URL", + "Long-Lived Access Token": "Lange gültiges Access Token", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Lange gültige Access Token können durch klicken auf den Profilnamen (unten links) und dann einen Klick auf Create Token am Ende erstellt werden. ", + "Notification Service": "Benachrichtigungsdienst", + "default: notify all devices": "standard: Alle Geräte benachrichtigen", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Eine Liste der Benachrichtigungsdienste kann im Home Assistant unter \"Developer Tools > Services\" gefunden werden, wnen man nach \"notification\" sucht um den Geräte-/Telefonnamen zu finden.", + "Automations can optionally be triggered in Home Assistant:": "Automatisierungen können optional im Home Assistant ausgelöst werden:", + "Trigger type:": "Auslöser:", + "Event type:": "Ereignistyp:", + "Event data:": "Ereignis daten:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Dann eine Aktion wählen, zum Beispiel eine Scene wählen in der ein RGB Licht rot ist.", + "Frontend Version": "Frontend Version", + "Frontend Version do not match backend version!": "Die Frontend Version stimmt nicht mit der backend version überein!", + "Maintenance": "Wartung", + "statusMaintenance": "Wartung", + "Schedule maintenance": "Geplante Wartung", + "Affected Monitors": "Betroffene Monitore", + "Pick Affected Monitors...": "Wähle betroffene Monitore…", + "Start of maintenance": "Beginn der Wartung", + "All Status Pages": "Alle Status Seiten", + "Select status pages...": "Wähle Status Seiten…", + "recurringIntervalMessage": "einmal pro Tag ausgeführt | Wird alle {0} Tage ausgführt", + "affectedMonitorsDescription": "Wähle alle Monitore die von der Wartung betroffen sind", + "affectedStatusPages": "Zeige diese Nachricht auf ausgewählten Status Seiten", + "atLeastOneMonitor": "Wähle mindestens einen Monitor", + "deleteMaintenanceMsg": "Möchtest du diese Wartung löschen?", + "Base URL": "Basis URL", + "goAlertInfo": "GoAlert ist eine Open-Source Applikation für Rufbereitschaftsplanung, automatische Eskalation und Benachrichtigung (z.B. SMS oder Telefonanrufe). Engagiere automatisch die richtige Person, auf die richtige Art und Weise und zum richtigen Zeitpunkt! {0}", + "goAlertIntegrationKeyInfo": "Bekommt einen generischen API Schlüssel in folgenden Format \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\". Normalerweise entspricht dies dem Wert des Token aus der URL.", + "goAlert": "GoAlert", + "backupOutdatedWarning": "Veraltet: Da viele Funktionen hinzugefügt wurden und die Backupfunktion nicht mehr gepflegt wird, kann keine vollständige Sicherung erstellt oder wiederhergestellt werden.", + "backupRecommend": "Bitte Backup das Volume oder den Ordner (./ data /) selbst.", + "Optional": "Optional", + "squadcast": "Squadcast", + "SendKey": "SendKey", + "SMSManager API Docs": "SMSManager API Dokumente ", + "Gateway Type": "Gateway Typ", + "SMSManager": "SMSManager", + "You can divide numbers with": "Du kannst Zahlen teilen mit", + "or": "oder", + "recurringInterval": "Intervall", + "Recurring": "Wiederkehrend", + "strategyManual": "Aktiv/Inaktiv Manuell", + "warningTimezone": "Es wird die Zeitzone des Servers genutzt", + "weekdayShortMon": "Mo", + "weekdayShortTue": "Di", + "weekdayShortWed": "Mi", + "weekdayShortThu": "Do", + "weekdayShortFri": "Fr", + "weekdayShortSat": "Sa", + "weekdayShortSun": "So", + "dayOfWeek": "Tag der Woche", + "dayOfMonth": "Tag im Monat", + "lastDay": "Letzter Tag", + "lastDay1": "Letzter Tag im Monat", + "lastDay2": "Vorletzer Tag im Monat", + "lastDay3": "3. letzter Tag im Monat", + "lastDay4": "4. letzter Tag im Monat", + "No Maintenance": "Keine Wartung", + "pauseMaintenanceMsg": "Möchtest du wirklich pausieren?", + "maintenanceStatus-under-maintenance": "In Wartung", + "maintenanceStatus-inactive": "Inaktiv", + "maintenanceStatus-scheduled": "Geplant", + "maintenanceStatus-ended": "Ende", + "maintenanceStatus-unknown": "Unbekannt", + "Display Timezone": "Zeitzone anzeigen", + "Server Timezone": "Server Zeitzone", + "telegramMessageThreadID": "(Optional) Nachrichten Thread ID", + "telegramMessageThreadIDDescription": "Optionale eindeutige Kennung für den Ziel-Thread (Thema) des Forums; nur für Forum-Supergroups", + "Enable": "Aktivieren", + "telegramProtectContent": "Schütze gegen Weiterleiten/Speichern der Nachricht", + "telegramProtectContentDescription": "Die Bot-Nachrichten in Telegram sind gegen Weiterleitung und Speichern geschützt.", + "Disable": "Deaktivieren", + "plugin": "Plugin | Plugins", + "installing": "Installiere", + "uninstall": "Deinstallieren", + "uninstalling": "Deinstalliere", + "confirmUninstallPlugin": "Möchtest du dieses Plugin wirklich deinstallieren?", + "notificationRegional": "Regional", + "Single Maintenance Window": "Einmaliges Wartungsfenster", + "dnsCacheDescription": "In einigen IPv6-Umgebungen funktioniert es möglicherweise nicht. Deaktiviere es, wenn Probleme auftreten.", + "Maintenance Time Window of a Day": "Wartungszeitfenster eines Tages", + "Effective Date Range": "Gültigkeitsbereich (Optional)", + "Schedule Maintenance": "Wartung planen", + "Date and Time": "Datum und Uhrzeit", + "DateTime Range": "Datums- und Zeitbereich", + "telegramSendSilently": "Stumm senden", + "telegramSendSilentlyDescription": "Sende die Nachricht stumm. Nutzer bekommen eine Benachrichtigung ohne Ton.", + "markdownSupported": "Markdown-Syntax unterstützt", + "webhookAdditionalHeadersTitle": "Zusätzliche Header", + "webhookAdditionalHeadersDesc": "Legt zusätzliche Kopfzeilen fest, die mit dem Webhook gesendet werden. Jede Kopfzeile sollte als JSON Schlüssel/Wert definiert werden.", + "Packet Size": "Paketgrösse", + "IconUrl": "Symbol URL", + "Enable DNS Cache": "(Veraltet) DNS-Cache für HTTP(s)-Monitore aktivieren", + "Help": "Hilfe", + "Game": "Spiel", + "General Monitor Type": "Allgemeiner Monitortyp", + "Passive Monitor Type": "Passiver Monitortyp", + "Specific Monitor Type": "Spezifischer Monitortyp", + "Monitor": "Überwachung | Monitore", + "Custom": "Benutzerdefiniert", + "statusPageMaintenanceEndDate": "Ende", + "loadingError": "Die Daten konnten nicht abgerufen werden, bitte später noch einmal versuchen.", + "install": "Installieren", + "Body Encoding": "Body Encoding", + "Custom Monitor Type": "Benutzerdefinierter Monitortyp", + "Expiry": "Ablauf", + "Expiry date": "Ablaufdatum", + "Don't expire": "Nicht ablaufen", + "Add Another": "Hinzufügen", + "Key Added": "Schlüssel hinzugefügt", + "apiKeyAddedMsg": "API Schlüssel wurde hinzugefügt. Bitte notiere den Schlüssel, da er nicht erneut angezeigt wird.", + "Add API Key": "API Schlüssel hinzufügen", + "No API Keys": "Kein API Schlüssel", + "apiKey-active": "Aktiv", + "apiKey-expired": "Abgelaufen", + "apiKey-inactive": "Inaktiv", + "Expires": "Läuft ab", + "disableAPIKeyMsg": "Bist du sicher, dass du diesen API Schlüssel deaktivieren willst?", + "deleteAPIKeyMsg": "Bist du sicher, dass du diesen API Schlüssel löschen willst?", + "Generate": "Generieren", + "infiniteRetention": "Für unendliche Speicherung auf 0 setzen.", + "dataRetentionTimeError": "Aufbewahrungsfrist muss grösser oder gleich 0 sein", + "Clone Monitor": "Monitor klonen", + "Clone": "Klonen", + "cloneOf": "Klon von {0}", + "wayToGetZohoCliqURL": "Wie eine Webhook URL erstellt werden kann, erfährst du {0}.", + "enableGRPCTls": "Senden von gRPC Anforderungen mit TLS Verbindung zulassen", + "grpcMethodDescription": "Der Name der Methode wird in das \"camelCase\" Format konvertiert (z.B. sayHello, check, etc.)", + "wayToGetKookGuildID": "Schalte den „Entwicklermodus“ in den Kook-Einstellungen ein und klicke mit der rechten Maustaste auf die Gilde, um die ID zu erhalten", + "Guild ID": "Gilde ID", + "Lowcost": "Kostengünstig", + "high": "hoch", + "Google Analytics ID": "Google Analytics ID", + "Enable TLS": "TLS aktivieren", + "Free Mobile API Key": "Kostenloser Mobile API Schlüssel", + "Proto Service Name": "Proto Dienst Name", + "Proto Method": "Proto Methode", + "Proto Content": "Proto Inhalt", + "Economy": "Economy", + "pagertreeIntegrationUrl": "Integrations-URL", + "pagertreeUrgency": "Dringlichkeit", + "pagertreeSilent": "Stumm", + "pagertreeLow": "Niedrig", + "pagertreeMedium": "Mittel", + "pagertreeHigh": "Hoch", + "pagertreeCritical": "Kritisch", + "pagertreeResolve": "Automatisch auflösen", + "pagertreeDoNothing": "Nichts tun", + "wayToGetPagerTreeIntegrationURL": "Nachdem du die Uptime Kuma Integration in PagerTree erstellt hast, kopiere den Endpunkt. Siehe details {0}", + "Server Address": "Serveradresse", + "Learn More": "Erfahre mehr", + "Edit Tag": "Tag editieren", + "promosmsAllowLongSMS": "Lange SMS erlauben", + "smseagleRecipientType": "Empfängertyp", + "smseagleToken": "API Zugriffstoken", + "smseagleTo": "Telefonnummer(n)", + "smseagleUrl": "Ihre SMSEagle Geräte URL", + "smseagleEncoding": "Als Unicode senden (Standard=GSM-7)", + "smseaglePriority": "Nachrichtenpriorität (0-9, höchste Priorität = 9)", + "smseagleContact": "Telefonbuch Kontaktname(n)", + "confirmDeleteTagMsg": "Möchtest du dieses Tag wirklich löschen? Monitore, die mit diesem Tag verknüpft sind, werden nicht gelöscht.", + "wayToGetKookBotToken": "Erstelle eine Anwendung und erhalte den Bot-Token unter {0}", + "Strategy": "Strategie", + "Free Mobile User Identifier": "Kostenlose mobile Benutzerkennung", + "smseagleGroup": "Telefonbuch Gruppenname(n)", + "smseagleRecipient": "Empfänger (mehrere müssen durch Komma getrennt werden)", + "API Keys": "API Schlüssel", + "Continue": "Weiter", + "Add New Tag": "Neuen Tag hinzufügen", + "lunaseaTarget": "Ziel", + "lunaseaDeviceID": "Geräte-ID", + "lunaseaUserID": "Benutzer-ID", + "ntfyAuthenticationMethod": "Authentifizierungsmethode", + "ntfyUsernameAndPassword": "Benutzername und Passwort", + "twilioAccountSID": "Account SID", + "twilioFromNumber": "Absender", + "twilioToNumber": "Empfänger", + "twilioAuthToken": "Auth Token / API Key Secret", + "statusPageRefreshIn": "Aktualisierung in: {0}", + "sameAsServerTimezone": "Gleiche Zeitzone wie Server", + "startDateTime": "Start Datum/Uhrzeit", + "endDateTime": "Ende Datum/Uhrzeit", + "cronExpression": "Cron-Ausdruck", + "cronSchedule": "Zeitplan: ", + "invalidCronExpression": "Ungültiger Cron-Ausdruck: {0}", + "Open Badge Generator": "Abzeichengenerator öffnen", + "Badge Generator": "{0}'s Abzeichen Generator", + "Badge Type": "Abzeichen Typ", + "Badge Duration": "Badge Dauer", + "Badge Label": "Abzeichen Label", + "Badge Prefix": "Abzeichen Wert Präfix", + "Badge Suffix": "Abzeichen Wert Suffix", + "Badge Label Color": "Abzeichen Label Farbe", + "Badge Color": "Abzeichen Farbe", + "Badge Label Prefix": "Abzeichen Label Präfix", + "Badge Up Color": "Abzeichen Up Farbe", + "Badge Maintenance Color": "Abzeichen Wartung Farbe", + "Badge Warn Color": "Abzeichen Warnung Farbe", + "Badge Warn Days": "Abzeichen Warnung Tage", + "Badge Style": "Abzeichen Stil", + "Badge URL": "Abzeichen URL", + "Badge Pending Color": "Abzeichen Pending Farbe", + "Badge Down Days": "Abzeichen Down Tage", + "Monitor Setting": "{0}'s Monitor Einstellung", + "Show Clickable Link": "Klickbaren Link anzeigen", + "Badge Label Suffix": "Abzeichen Label Suffix", + "Badge value (For Testing only.)": "Abzeichen Wert (nur für Tests)", + "Show Clickable Link Description": "Wenn diese Option aktiviert ist, kann jeder, der Zugriff auf diese Statusseite hat, auf die Monitor URL zugreifen.", + "Badge Down Color": "Abzeichen Down Farbe", + "Edit Maintenance": "Wartung bearbeiten", + "Group": "Gruppe", + "Monitor Group": "Monitor Gruppe", + "noGroupMonitorMsg": "Nicht verfügbar. Erstelle zunächst einen Gruppenmonitor.", + "Close": "Schliessen", + "chromeExecutableAutoDetect": "Automatische Erkennung", + "chromeExecutableDescription": "Für Docker-Benutzer, die Chromium noch nicht installiert haben, kann es ein paar Minuten dauern, bis es installiert ist und das Testergebnis angezeigt wird. Es benötigt 1 GB Speicherplatz.", + "chromeExecutable": "Chrome/Chromium Ausführbare Datei", + "Invert Keyword": "Schlüsselwort invertieren", + "webhookCustomBodyDesc": "Definiere einen benutzerdefinierten HTTP-Body für die Anfrage. Die Template-Variablen {msg}, {heartbeat} und {monitor} werden akzeptiert.", + "webhookBodyPresetOption": "Voreinstellung - {0}", + "webhookBodyCustomOption": "Benutzerdefinierter Body", + "invertKeywordDescription": "Achte darauf, dass das Schlüsselwort eher fehlt als vorhanden ist.", + "Request Body": "Anforderungstext", + "twilioApiKey": "API-Schlüssel (optional)", + "aboutNotifyChannel": "Notify Kanal löst eine Desktop- oder Mobilbenachrichtigung für alle Mitglieder des Kanals aus, unabhängig davon, ob deine Verfügbarkeit auf aktiv oder abwesend eingestellt ist.", + "Notify Channel": "Notify Kanal", + "Enter the list of brokers": "Gib die Liste der Broker ein", + "Kafka Topic Name": "Kafka Topic Name", + "Kafka Producer Message": "Kafka Producer Nachricht", + "Enable Kafka SSL": "Kafka SSL aktivieren", + "Enable Kafka Producer Auto Topic Creation": "Kafka Producer Auto Topic Creation aktivieren", + "Kafka SASL Options": "Kafka SASL Optionen", + "Mechanism": "Mechanismus", + "Pick a SASL Mechanism...": "Wähle ein SASL Mechanismus…", + "AccessKey Id": "AccessKey Id", + "Secret AccessKey": "Secret AccessKey", + "Session Token": "Sitzungs-Token", + "Kafka Brokers": "Kafka Brokers", + "Press Enter to add broker": "Drücke Enter um den Broker hinzuzufügen", + "Authorization Identity": "Authorization Identity", + "Expected Value": "Erwarteter Wert", + "Json Query": "Json-Abfrage", + "filterActive": "Aktiv", + "filterActivePaused": "Pausiert", + "Badge Duration (in hours)": "Abzeichen Dauer (in Stunden)", + "Badge Preview": "Abzeichen Vorschau", + "tailscalePingWarning": "Um den Tailscale Ping Monitor nutzen zu können, musst du Uptime Kuma ohne Docker installieren und den Tailscale Client auf dem Server installieren.", + "Server URL should not contain the nfty topic": "Die Server-URL sollte das nfty-Thema nicht enthalten", + "pushDeerServerDescription": "Leer lassen um den offiziellen Server zu verwenden", + "FlashDuty Severity": "Schweregrad", + "nostrSender": "Privater Schlüssel des Absenders (nsec)", + "nostrRecipientsHelp": "npub-Format, eine pro Zeile", + "noOrBadCertificate": "Kein/schlechtes Zertifikat", + "wayToGetFlashDutyKey": "Um Uptime Kuma mit Flashduty zu integrieren: Gehe zu Channels > Wähle einen Channel > Integrationen > Füge eine neue Integration hinzu, wähle Uptime Kuma und kopiere die Push URL.", + "nostrRelays": "Nostr Relays", + "nostrRelaysHelp": "Eine Relay-URL pro Zeile", + "nostrRecipients": "Öffentliche Schlüssel des Empfängers (npub)", + "gamedigGuessPort": "Gamedig: Vermuteter Port", + "Request Timeout": "Zeitüberschreitung der Anfrage", + "styleElapsedTimeShowNoLine": "Anzeigen (keine Zeile)", + "styleElapsedTimeShowWithLine": "Anzeigen (mit Zeile)", + "Select": "Auswählen", + "selectedMonitorCount": "Ausgewählt: {0}", + "PushDeer Server": "PushDeer Server", + "showCertificateExpiry": "Ablauf des Zertifikats anzeigen", + "gamedigGuessPortDescription": "Der vom Valve Server Query Protocol verwendete Port kann sich vom Port des Clients unterscheiden. Versuche dies, wenn der Monitor keine Verbindung zum Server herstellen kann.", + "timeoutAfter": "Zeitüberschreitung nach {0} Sekunden", + "styleElapsedTime": "Verstrichene Zeit unter der Prüfintervallleiste", + "Check/Uncheck": "Aktivieren/Deaktivieren", + "enableNSCD": "Aktiviere NSCD (Name Service Cache Daemon) zur Zwischenspeicherung aller DNS-Anfragen", + "setupDatabaseChooseDatabase": "Welche Datenbank möchtest du verwenden?", + "setupDatabaseEmbeddedMariaDB": "Du brauchst nichts einzustellen. Dieses Docker-Image hat automatisch eine MariaDB für dich eingerichtet und konfiguriert. Uptime Kuma wird sich mit dieser Datenbank über einen Unix-Socket verbinden.", + "dbName": "Datenbank Name", + "setupDatabaseMariaDB": "Mit externer MariaDB-Datenbank verbinden. Du musst die Verbindungsinformationen für die Datenbank festlegen.", + "setupDatabaseSQLite": "Eine einfache Datenbankdatei, empfohlen für kleinere Bereitstellungen. Vor v2.0.0 verwendete Uptime Kuma SQLite als Standarddatenbank.", + "Saved.": "Gespeichert.", + "monitorToastMessagesLabel": "Toast-Benachrichtigungen überwachen", + "toastSuccessTimeout": "Zeitüberschreitung für Erfolgsbenachrichtigungen", + "toastErrorTimeout": "Zeitüberschreitung für Fehlerbenachrichtigungen", + "monitorToastMessagesDescription": "Toast-Benachrichtigungen für Monitore verschwinden nach einer bestimmten Zeit in Sekunden. Auf -1 setzen, um die Zeitüberschreitung zu deaktivieren. Der Wert 0 deaktiviert die Toast-Benachrichtigungen.", + "Bark API Version": "Bark API Version", + "pushViewCode": "Wie verwendet man den Push-Monitor? (Code anzeigen)", + "pushOthers": "Sonstige", + "programmingLanguages": "Programmiersprachen", + "authInvalidToken": "Ungültiges Token.", + "authIncorrectCreds": "Falscher Benutzername oder falsches Passwort.", + "2faAlreadyEnabled": "2FA ist bereits aktiviert.", + "2faEnabled": "2FA ist aktiviert.", + "2faDisabled": "2FA ist deaktiviert.", + "successResumed": "Erfolgreich wiederaufgenommen.", + "successPaused": "Erfolgreich pausiert.", + "successDeleted": "Erfolgreich gelöscht.", + "successEdited": "Erfolgreich bearbeitet.", + "successBackupRestored": "Sicherung erfolgreich wiederhergestellt.", + "successEnabled": "Erfolgreich aktiviert.", + "tagNotFound": "Tag nicht gefunden.", + "foundChromiumVersion": "Gefunden Chromium/Chrome. Version: {0}", + "authUserInactiveOrDeleted": "Der Benutzer ist inaktiv oder gelöscht.", + "successAdded": "Erfolgreich hinzugefügt.", + "successAuthChangePassword": "Das Passwort wurde erfolgreich aktualisiert.", + "successDisabled": "Erfolgreich deaktiviert.", + "Reset Token": "Token zurücksetzen", + "leave blank for default subject": "leer lassen für Standard-Betreff", + "emailCustomBody": "Benutzerdefinierter Body", + "emailCustomisableContent": "Anpassbarer Inhalt", + "liquidIntroduction": "Die Templatierbarkeit wird durch die Liquid Templating Language erreicht. Hinweise zur Verwendung findest du auf {0}. Dies sind die verfügbaren Variablen:", + "templateLimitedToUpDownCertNotifications": "nur verfügbar für UP/DOWN/Zertifikat-Ablaufbenachrichtigungen", + "smtpLiquidIntroduction": "Die folgenden beiden Felder können mit der Liquid Templating Language angepasst werden. Eine Anleitung zur Verwendung findest du in der {0}. Dies sind die verfügbaren Variablen:", + "templateMsg": "Nachricht der Benachrichtigung", + "templateHeartbeatJSON": "Objekt, das den Heartbeat beschreibt", + "templateMonitorJSON": "Objekt zur Beschreibung des Monitors", + "templateLimitedToUpDownNotifications": "nur für UP/DOWN-Benachrichtigungen verfügbar", + "leave blank for default body": "leer lassen für Standard Body", + "emailTemplateServiceName": "Dienst Name", + "emailTemplateHostnameOrURL": "Hostname oder URL", + "emailTemplateStatus": "Status", + "emailTemplateMonitorJSON": "Objekt zur Beschreibung des Monitors", + "emailTemplateHeartbeatJSON": "Objekt, das den Heartbeat beschreibt", + "emailTemplateMsg": "Nachricht der Benachrichtigung", + "emailTemplateLimitedToUpDownNotification": "nur bei UP/DOWN-Heartbeats verfügbar, sonst null", + "GrafanaOncallUrl": "Grafana Oncall URL", + "noDockerHostMsg": "Nicht verfügbar. Richte zunächst einen Docker-Host ein.", + "DockerHostRequired": "Bitte Docker-Host für diesen Monitor festlegen.", + "Browser Screenshot": "Browser Screenshot", + "Add a new expiry notification day": "Neuen Tag für Auslaufbenachrichtigung hinzufügen", + "Remote Browsers": "Remote Browsers", + "Remote Browser": "Remote Browser", + "Add a Remote Browser": "Remote Browser hinzufügen", + "Remote Browser not found!": "Remote Browser nicht gefunden!", + "remoteBrowsersDescription": "Remote Browser sind eine Alternative zur lokalen Ausführung von Chromium. Einrichtung mit einem Dienst wie browserless.io oder verbinde mit deinem Eigenen.", + "self-hosted container": "Selbstverwalteter Container", + "remoteBrowserToggle": "Standardmässig läuft Chromium innerhalb des Uptime-Kuma Containers. Du kannst einen Remote-Browser verwenden, indem du diesen Schalter umlegst.", + "useRemoteBrowser": "Verwende einen Remote Browser", + "deleteRemoteBrowserMessage": "Bist du sicher, dass du diesen Remote Browser für alle Monitore löschen möchtest?", + "Remove the expiry notification": "Tag für Auslaufbenachrichtigung löschen", + "setup a new monitor group": "Neue Monitorgruppe einrichten", + "openModalTo": "Modal öffnen nach {0}", + "Add a domain": "Domain hinzufügen", + "Remove domain": "Domain '{0}' entfernen", + "successKeyword": "Erfolgsschlüsselwort", + "successKeywordExplanation": "MQTT-Schlüsselwort, das als Erfolg gewertet wird", + "ntfyPriorityHelptextAllExceptDown": "Alle Ereignisse werden mit dieser Priorität gesendet, ausser {0}-Ereignisse, die eine Priorität von {1} haben", + "settingUpDatabaseMSG": "Die Datenbank wird eingerichtet. Dies kann einen Moment dauern, bitte habe Geduld.", + "Search monitored sites": "Überwachte Seiten durchsuchen", + "statusPageSpecialSlugDesc": "Spezieller Slug {0}: diese Seite wird angezeigt, wenn kein Slug angegeben wird", + "ntfyPriorityHelptextAllEvents": "Alle Ereignisse werden mit der höchsten Priorität gesendet", + "What is a Remote Browser?": "Was ist ein Remote-Browser?", + "Channel access token (Long-lived)": "Channel-Token (langlebig)", + "Your User ID": "Deine Benutzer-ID", + "wayToGetHeiiOnCallDetails": "Wie man die Trigger-ID und API-Schlüssel erhält, wird in der {Dokumentation} erklärt", + "documentationOf": "{0} Dokumentation", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Von Telefonnummer / Transmission Path Originating Address (TPOA)", + "To Phone Number": "Zur Telefonnummer", + "gtxMessagingToHint": "Internationales Format, mit vorangestelltem \"+\" ({e164}, {e212} oder {e214})", + "gtxMessagingApiKeyHint": "Du findest deinen API-Schlüssel unter: Meine Routing-Konten > Kontoinformationen anzeigen > API-Anmeldeinformationen > REST-API (v2.x)", + "gtxMessagingFromHint": "Auf Mobiltelefonen sieht der Empfänger die TPOA als Absender der Nachricht. Erlaubt sind bis zu 11 alphanumerische Zeichen, eine Kurzwahlnummer, die lokale Langwahlnummer oder internationale Nummern ({e164}, {e212} oder {e214})", + "Telephone number": "Telefonnummer", + "Originator": "Absender", + "cellsyntOriginator": "Sichtbar auf dem Mobiltelefon des Empfängers als Absender der Nachricht. Zulässige Werte und Funktion hängen vom Parametertyp ab.", + "Destination": "Ziel", + "Allow Long SMS": "Lange SMS zulassen", + "cellsyntSplitLongMessages": "Teile lange Nachrichten in bis zu 6 Teile. 153 x 6 = 918 Zeichen.", + "max 15 digits": "max. 15 Ziffern", + "max 11 alphanumeric characters": "max. 11 alphanumerische Zeichen", + "Originator type": "Typ des Absenders", + "Alphanumeric (recommended)": "Alphanumerisch (empfohlen)", + "cellsyntOriginatortypeAlphanumeric": "Alphanumerische Zeichenfolge (max. 11 alphanumerische Zeichen). Die Empfänger können nicht auf die Nachricht antworten.", + "cellsyntOriginatortypeNumeric": "Numerischer Wert (max. 15 Stellen) mit Telefonnummer im internationalen Format ohne Vorwahl 00 (Beispiel UK-Nummer 07920 110 000 sollte auf 447920110000 gesetzt werden). Empfänger können auf die Nachricht antworten.", + "cellsyntDestination": "Telefonnummer des Empfängers im internationalen Format mit führender 00, gefolgt von der Landesvorwahl, z. B. 00447920110000 für die britische Nummer 07920 110 000 (insgesamt maximal 17 Ziffern). Maximal 25000 kommagetrennte Empfänger pro HTTP-Anfrage.", + "callMeBotGet": "Hier kannst du einen Endpunkt für {0}, {1} und {2} generieren. Denke daran, dass der Durchsatz möglicherweise begrenzt ist. Die Durchsatzbegrenzung ist: {3}", + "whapiRecipient": "Telefonnummer / Kontakt-ID / Gruppen-ID", + "API URL": "API URL", + "wayToWriteWhapiRecipient": "Die Rufnummer mit der internationalen Vorwahl, aber ohne das Pluszeichen am Anfang ({0}), die Kontakt-ID ({1}) oder die Gruppen-ID ({2}).", + "wayToGetWhapiUrlAndToken": "Du kannst die API-URL und das Token erhalten, indem du in deinen gewünschten Channel von {0} gehst", + "locally configured mail transfer agent": "Lokal konfigurierter Mail-Transfer-Agent", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Gib entweder den Hostnamen des Servers ein, mit dem eine Verbindung hergestellt werden soll, oder {localhost}, wenn ein {local_mta} verwendet werden soll.", + "Mentioning": "Erwähnung", + "Don't mention people": "Keine Personen erwähnen", + "Mention group": "Erwähne {group}", + "senderSevenIO": "Absendernummer oder Name", + "receiverSevenIO": "Empfangende Nummer", + "receiverInfoSevenIO": "Wenn sich die empfangende Nummer nicht in Deutschland befindet, muss der Nummer die Landesvorwahl vorangestellt werden (z.B. für die Landesvorwahl 1 aus den USA muss 117612121212 anstelle von 017612121212 verwendet werden)", + "apiKeySevenIO": "SevenIO API Schlüssel", + "wayToGetSevenIOApiKey": "Besuche das Dashboard unter app.seven.io > Entwickler > API Schlüssel > grüne Hinzufügen Schaltfläche", + "Host URL": "Host URL", + "whatHappensAtForumPost": "Erstelle einen neuen Forumsbeitrag. Dadurch werden KEINE Nachrichten in einem bestehenden Beitrag veröffentlicht. Um einen Beitrag in einem bestehenden Beitrag zu erstellen, verwende \"{option}\"", + "Command": "Befehl", + "Bitrix24 Webhook URL": "Bitrix24 Webhook URL", + "wayToGetBitrix24Webhook": "Du kannst einen Webhook erstellen, indem du die Schritte unter {0} befolgst", + "bitrix24SupportUserID": "Gib deine User-ID in Bitrix24 ein. Du kannst die ID über den Link erfahren, indem du auf das Profil des Users gehst.", + "mongodbCommandDescription": "Führe einen MongoDB Befehl gegen die Datenbank aus. Informationen zu den verfügbaren Befehlen findest du in der {documentation}", + "Refresh Interval": "Aktualisierungsintervall", + "Refresh Interval Description": "Die Statusseite wird alle {0} Sekunden neu geladen", + "Select message type": "Nachrichtentyp auswählen", + "Send to channel": "An Kanal senden", + "Create new forum post": "Neuen Forumsbeitrag erstellen", + "postToExistingThread": "An bestehendes Thema/Forumbeitrag senden", + "forumPostName": "Name des Forumsbeitrags", + "ignoreTLSErrorGeneral": "TLS/SSL-Fehler für Verbindung ignorieren", + "threadForumPostID": "Themen-/Forumbeitrags-ID", + "e.g. {discordThreadID}": "z.B. {discordThreadID}", + "wayToGetDiscordThreadId": "Das Abrufen einer Thread-/Forumspost-ID ist ähnlich wie das Abrufen einer Channel-ID. Lese mehr darüber, wie man IDs abruft {0}", + "smspartnerApiurl": "Den API-Schlüssel findest du im Dashboard unter {0}", + "smspartnerPhoneNumber": "Telefonnummer(n)", + "smspartnerSenderName": "SMS Absender Name", + "smspartnerSenderNameInfo": "Muss zwischen 3..=11 regulären Zeichen sein", + "smspartnerPhoneNumberHelptext": "Die Nummer muss das internationale Format {0}, {1} haben. Mehrere Nummern müssen durch {2} getrennt werden", + "threemaRecipient": "Empfänger", + "threemaRecipientType": "Empfänger Typ", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypePhone": "Telefonnummer", + "threemaRecipientTypePhoneFormat": "E.164, ohne führendes +", + "threemaRecipientTypeEmail": "E-Mail Adresse", + "threemaSenderIdentity": "Gateway-ID", + "threemaApiAuthenticationSecret": "Gateway-ID Schlüssel", + "wayToGetThreemaGateway": "Du kannst dich für Threema Gateway {0} registrieren.", + "threemaRecipientTypeIdentityFormat": "8 Zeichen", + "threemaSenderIdentityFormat": "8 Zeichen, beginnt normalerweise mit *", + "threemaBasicModeInfo": "Hinweis: Diese Integration verwendet Threema Gateway im Basismodus (serverbasierte Verschlüsselung). Weitere Details siehe {0}.", + "apiKeysDisabledMsg": "API-Schlüssel sind deaktiviert, da die Authentifizierung deaktiviert ist.", + "Json Query Expression": "Json Query Ausdrck", + "Cannot connect to the socket server.": "Es kann keine Verbindung zum Socket-Server hergestellt werden.", + "not ends with": "endet nicht mit", + "signl4Docs": "Weitere Informationen zur Konfiguration von SIGNL4 und zum Abrufen der SIGNL4-Webhook-URL siehe {0}.", + "now": "jetzt", + "time ago": "vor {0}", + "-year": "-Jahr", + "and": "und", + "jsonQueryDescription": "Parsen und Extrahieren spezifischer Daten aus der JSON-Antwort des Servers mittels JSON-Abfrage oder Verwendung von \"$\" für die rohe Antwort, wenn kein JSON erwartet wird. Das Ergebnis wird dann mit dem erwarteten Wert in Form von Strings verglichen. Siehe {0} für die Dokumentation und verwende {1}, um mit Abfragen zu experimentieren.", + "cacheBusterParamDescription": "Zufällig generierter Parameter um den Cache zu umgehen.", + "cacheBusterParam": "Den Parameter {0} hinzufügen", + "Community String": "Gemeinschaftliche Zeichenkette", + "snmpCommunityStringHelptext": "Diese Zeichenfolge dient als Passwort zur Authentifizierung und Kontrolle des Zugriffs auf SNMP-fähigen Geräten. Pass sie an die Konfiguration des SNMP-Geräts an.", + "OID (Object Identifier)": "OID (Objekt-Identifikator)", + "Condition": "Bedingung", + "SNMP Version": "SNMP Version", + "Please enter a valid OID.": "Gib eine gültige OID ein.", + "Host Onesender": "Host Onesender", + "Token Onesender": "Token Onesender", + "Recipient Type": "Empfänger Typ", + "Private Number": "Private Nummer", + "Group ID": "Gruppen ID", + "wayToGetOnesenderUrlandToken": "Du kannst die URL und den Token auf der Onesender-Website erhalten. Weitere Infos {0}", + "Add Remote Browser": "Remote-Browser hinzufügen", + "New Group": "Neue Gruppe", + "Group Name": "Gruppenname", + "OAuth2: Client Credentials": "OAuth2: Client-Anmeldeinformationen", + "snmpOIDHelptext": "Gib die OID für den zu überwachenden Sensor oder Status ein. Verwende Netzwerkverwaltungstools wie MIB-Browser oder SNMP-Software, wenn du bezüglich OID unsicher bist.", + "privateOnesenderDesc": "Stell sicher, dass die Telefonnummer gültig ist. Um Nachrichten an private Telefonnummer zu senden, z. B.: 628123456789", + "groupOnesenderDesc": "Stell sicher, dass die GroupID gültig ist. Um Nachricht an die Gruppe zu senden, z.B.: 628123456789-342345", + "Authentication Method": "Authentifizierungsmethode", + "Authorization Header": "Autorisierungs-Header", + "Form Data Body": "Formular Data Body", + "OAuth Token URL": "OAuth Token URL", + "Client ID": "Client ID", + "Client Secret": "Client Secret", + "OAuth Scope": "OAuth Scope", + "Optional: Space separated list of scopes": "Optional: Durch Leerzeichen getrennte Liste der Scopes", + "Go back to home page.": "Zurück zur Startseite.", + "No tags found.": "Keine Tags gefunden.", + "Lost connection to the socket server.": "Verbindung zum Socket-Server verloren.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL", + "Conditions": "Bedingungen", + "conditionAdd": "Bedingung hinzufügen", + "conditionDelete": "Bedingung löschen", + "conditionAddGroup": "Gruppe hinzufügen", + "conditionDeleteGroup": "Gruppe löschen", + "conditionValuePlaceholder": "Wert", + "equals": "ist gleich", + "not equals": "ist nicht gleich", + "contains": "enthält", + "not contains": "enthält nicht", + "starts with": "beginnt mit", + "not starts with": "beginnt nicht mit", + "ends with": "endet mit", + "less than": "weniger als", + "greater than": "mehr als", + "less than or equal to": "kleiner als oder gleich", + "greater than or equal to": "grösser als oder gleich", + "record": "Eintrag", + "shrinkDatabaseDescriptionSqlite": "Datenbank {vacuum} für SQLite auslösen. {auto_vacuum} ist bereits aktiviert, aber dies defragmentiert die Datenbank nicht und packt auch nicht einzelne Datenbankseiten neu, wie es der Befehl {vacuum} tut.", + "ignoredTLSError": "TLS/SSL-Fehler wurden ignoriert", + "Debug": "Debug", + "Copy": "Kopieren", + "CurlDebugInfoOAuth2CCUnsupported": "Der vollständige OAuth-Client-Credential-Flow wird in {curl} nicht unterstützt.{newline}Bitte besorge dir ein Bearer-Token und übergebe ihn über die {oauth2_bearer}-Option.", + "Money": "Geld", + "Custom sound to override default notification sound": "Benutzerdefinierter Ton, um den standardmässigen Benachrichtigungston zu ersetzen", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Entweder eine Text-Absender-ID oder eine Telefonnummer im E.164-Format, wenn du Antworten erhalten möchtest.", + "CopyToClipboardError": "Konnte nicht in die Zwischenablage kopiert werden: {error}", + "CopyToClipboardSuccess": "Kopiert!", + "CurlDebugInfo": "Um den Monitor zu debuggen, kannst du dies entweder in das Terminal deines eigenen Rechners oder in das Terminal der Maschine, auf der Uptime Kuma läuft, einfügen und überprüfen, was du anforderst.{newiline}Bitte beachte Netzwerkunterschiede wie {firewalls}, {dns_resolvers} oder {docker_networks}.", + "firewalls": "Firewalls", + "dns resolvers": "DNS-Resolver", + "docker networks": "Docker-Netzwerke", + "CurlDebugInfoProxiesUnsupported": "Die Unterstützung von Proxys im oben genannten {curl}-Befehl ist derzeit nicht implementiert.", + "Message format": "Nachrichtenformat", + "Send rich messages": "Sende Rich-Text-Nachrichten", + "Notification Channel": "Benachrichtigungskanal", + "Sound": "Benachrichtigungston", + "Alphanumerical string and hyphens only": "Nur alphanumerische Zeichen und Bindestriche", + "Correct": "Korrekt", + "Fail": "Fehlgeschlagen", + "Harp": "Harfe", + "Reveal": "Enthüllen", + "Bubble": "Blase", + "Doorbell": "Türklingel", + "Flute": "Flöte", + "Scifi": "Science Fiction", + "Clear": "Klar", + "Elevator": "Lift", + "Guitar": "Gitarre", + "Pop": "Pop", + "Time Sensitive (iOS Only)": "Zeitkritisch (nur iOS)", + "The phone number of the recipient in E.164 format.": "Die Telefonnummer des Empfängers im E.164-Format.", + "Can be found on:": "Ist zu finden auf: {0}", + "From": "Von", + "Arcade": "Spielhalle", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Zeitkritische Benachrichtigungen werden sofort zugestellt, auch wenn sich das Gerät im Nicht stören-Modus befindet.", + "RabbitMQ Nodes": "RabbitMQ-Verwaltungsknoten", + "rabbitmqNodesDescription": "Gib die URL für die RabbitMQ-Verwaltungsknoten einschliesslich Protokoll und Port ein. Beispiel: {0}", + "rabbitmqNodesRequired": "Setze die Knoten für diesen Monitor.", + "RabbitMQ Username": "RabbitMQ Benutzername", + "RabbitMQ Password": "RabbitMQ Passwort", + "SendGrid API Key": "SendGrid-API-Schlüssel", + "Separate multiple email addresses with commas": "Mehrere E-Mail-Adressen mit Kommas trennen", + "rabbitmqNodesInvalid": "Benutze eine vollständig qualifizierte URL (beginnend mit 'http') für RabbitMQ-Knoten.", + "rabbitmqHelpText": "Um den Monitor zu benutzen, musst du das Management Plugin in deinem RabbitMQ-Setup aktivieren. Weitere Informationen siehe {rabitmq_documentation}.", + "aboutSlackUsername": "Ändert den Anzeigenamen des Absenders. Wenn du jemanden erwähnen möchtest, füge ihn stattdessen in den Namen ein.", + "templateHostnameOrURL": "Hostname oder URL", + "telegramUseTemplate": "Benutzerdefinierte Nachrichtenvorlage verwenden", + "telegramUseTemplateDescription": "Wenn diese Option aktiviert ist, wird die Nachricht unter Verwendung einer benutzerdefinierten Vorlage gesendet.", + "templateServiceName": "Service-Name", + "YZJ Webhook URL": "YZJ Webhook URL", + "YZJ Robot Token": "YZJ Robot Token", + "templateStatus": "Status", + "telegramTemplateFormatDescription": "Telegram ermöglicht die Verwendung verschiedener Markup-Sprachen für Nachrichten, siehe Telegram {0} für spezifische Details.", + "Plain Text": "Nur Text", + "Message Template": "Nachrichtenvorlage", + "Template Format": "Vorlagenformat", + "wayToGetWahaApiUrl": "Die URL deiner WAHA-Instanz.", + "wayToGetWahaSession": "Von dieser Sitzung aus sendet WAHA Benachrichtigungen an die Chat-ID. Du kannst sie im WAHA Dashboard finden.", + "wahaSession": "Sitzung", + "wahaChatId": "Chat-ID (Telefonnummer / Kontakt-ID / Gruppen-ID)", + "wayToGetWahaApiKey": "API-Schlüssel ist der Wert der WHATSAPP_API_KEY-Umgebungsvariable, den du beim Ausführen von WAHA verwendet hast.", + "wayToWriteWahaChatId": "Die Telefonnummer mit internationaler Vorwahl, ohne den anfänglichen Pluszeichen ({0}), die Kontakt-ID ({1}) oder die Gruppen-ID ({2}). Die Benachrichtigungen werden an diese Chat-ID von der WAHA-Sitzung gesendet.", + "telegramServerUrl": "(Optional) Server URL", + "telegramServerUrlDescription": "Um die Telegram-Bot-API-Beschränkungen aufzuheben oder in gesperrten Gebieten (China, Iran usw.) Zugriff zu erhalten. Weitere Informationen findest du unter {0}. Standard: {1}", + "Font Twemoji by Twitter licensed under": "Schriftart Twemoji von Twitter lizenziert unter", + "smsplanetApiToken": "Token für die SMSPlanet API", + "smsplanetApiDocs": "Ausführliche Informationen zum Erhalt von API-Tokens findest du in {the_smsplanet_documentation}.", + "the smsplanet documentation": "die smsplanet Dokumentation", + "Phone numbers": "Telefonnummern", + "Sender name": "Absendername", + "smsplanetNeedToApproveName": "Muss im Kundenpanel genehmigt werden", + "Use HTML for custom E-mail body": "HTML für benutzerdefinierten E-Mail-Text verwenden", + "smseagleApiv2": "APIv2 (empfohlen für neue Integrationen)", + "SpugPush Template Code": "Vorlage Code", + "FlashDuty Push URL": "Push-URL", + "FlashDuty Push URL Placeholder": "Kopie von der Seite für die Integration von Warnmeldungen", + "pingCountDescription": "Anzahl der zu sendenden Pakete vor dem Anhalten", + "pingNumericDescription": "Wenn diese Option aktiviert ist, werden IP-Adressen anstelle von symbolischen Hostnamen ausgegeben", + "pingPerRequestTimeoutDescription": "Dies ist die maximale Wartezeit (in Sekunden), bevor ein einzelnes Ping-Paket als verloren gilt", + "smtpHelpText": "'SMTPS' testet, ob SMTP/TLS funktioniert; 'Ignoriere TLS' stellt eine Verbindung über Klartext her; 'STARTTLS' stellt eine Verbindung her, gibt einen STARTTLS-Befehl aus und überprüft das Serverzertifikat. Keiner dieser Befehle sendet eine E-Mail.", + "defaultFriendlyName": "Neuer Monitor", + "smseagleGroupV2": "Telefonbuchgruppen-ID(s)", + "smseagleContactV2": "Telefonbuch-Kontakt-ID(s)", + "smseagleMsgType": "Nachrichtentyp", + "smseagleMsgSms": "SMS-Nachricht (Standard)", + "smseagleMsgRing": "Ringruf", + "smseagleMsgTts": "Text-zu-Sprache-Anruf", + "smseagleMsgTtsAdvanced": "Text-zu-Sprache Erweiterter Anruf", + "smseagleDuration": "Dauer (in Sekunden)", + "smseagleTtsModel": "Text-zu-Sprache-Modell-ID", + "smseagleApiType": "API-Version", + "smseagleApiv1": "APIv1 (für bestehende Projekte und Abwärtskompatibilität)", + "smseagleDocs": "Dokumentation oder APIv2-Verfügbarkeit prüfen: {0}", + "smseagleComma": "Mehrere müssen durch Komma getrennt werden", + "pingCountLabel": "Maximale Pakete", + "pingNumericLabel": "Numerische Ausgabe", + "pingGlobalTimeoutLabel": "Globale Zeitüberschreitung", + "pingGlobalTimeoutDescription": "Gesamtzeit in Sekunden bis zum Ende des Pings, unabhängig von den gesendeten Paketen", + "pingPerRequestTimeoutLabel": "Zeitüberschreitung pro Ping", + "pingIntervalAdjustedInfo": "Das Intervall wird anhand der Paketanzahl, des globalen Timeouts und des Timeouts pro Ping angepasst", + "Custom URL": "Benutzerdefinierte URL", + "customUrlDescription": "Wird als klickbare URL anstelle der des Monitors verwendet.", + "OneChatAccessToken": "OneChat Access Token", + "OneChatUserIdOrGroupId": "OneChat Benutzer-ID oder Gruppen-ID", + "OneChatBotId": "OneChat Bot-ID", + "Disable URL in Notification": "URL in der Benachrichtigung deaktivieren", + "Ip Family": "IP-Familie", + "ipFamilyDescriptionAutoSelect": "Verwendet den {happyEyeballs} zur Bestimmung der IP-Familie.", + "Happy Eyeballs algorithm": "Happy Eyeballs Algorithmus", + "Add Another Tag": "Weiteres Tag hinzufügen", + "Manual": "Manuell", + "ntfyPriorityHelptextPriorityHigherThanDown": "Die reguläre Priorität sollte höher sein als die Priorität {0}. Priorität {1} ist höher als {0} Priorität {2}", + "ntfyPriorityDown": "Priorität für DOWN-Ereignisse", + "Add Tags": "Tags hinzufügen", + "tagAlreadyOnMonitor": "Dieses Tag (Name und Wert) befindet sich bereits auf dem Monitor oder muss noch hinzugefügt werden.", + "tagAlreadyStaged": "Dieses Tag (Name und Wert) wurde für dieses Batch bereits bereitgestellt.", + "tagNameExists": "Ein System-Tag mit diesem Namen existiert bereits. Wähle es aus der Liste aus oder verwende einen anderen Namen.", + "Clear Form": "Formular leeren", + "pause": "Pause", + "Staged Tags for Batch Add": "Bereitgestellte Tags für Batch-Hinzufügen", + "OAuth Audience": "OAuth Zielgruppe", + "Optional: The audience to request the JWT for": "Optional: Die Zielgruppe, für die das JWT angefordert werden soll", + "Path": "Pfad", + "mqttWebSocketPath": "MQTT WebSocket Pfad", + "mqttWebsocketPathExplanation": "WebSocket-Pfad für MQTT über WebSocket-Verbindungen (z. B. /mqtt)", + "mqttWebsocketPathInvalid": "Verwende ein gültiges WebSocket-Pfadformat", + "mqttHostnameTip": "Verwende dieses Format {hostnameFormat}", + "Template plain text instead of using cards": "Textvorlage anstelle von Karten", + "issueWithGoogleChatOnAndroidHelptext": "Dies ermöglicht auch die Umgehung von Fehlern im Vorfeld wie {issuetackerURL}", + "Clear All Events": "Alle Ereignisse löschen", + "clearAllEventsMsg": "Möchtest du wirklich alle Ereignisse löschen?", + "Events cleared successfully": "Ereignisse erfolgreich gelöscht.", + "No monitors found": "Keine Monitore gefunden.", + "Could not clear events": "{failed}/{total} Ereignisse konnten nicht gelöscht werden", + "wayToWriteEvolutionRecipient": "Die Telefonnummer mit der internationalen Vorwahl, jedoch ohne das Pluszeichen am Anfang ({0}), die Kontakt-ID ({1}) oder die Gruppen-ID ({2}).", + "wayToGetEvolutionUrlAndToken": "Du kannst die API-URL und das Token erhalten, indem du den gewünschten Kanal unter {0} abrufst.", + "evolutionRecipient": "Telefonnummer / Kontakt-ID / Gruppen-ID", + "evolutionInstanceName": "Instanzname", + "brevoApiKey": "Brevo API Schlüssel", + "brevoApiHelp": "Erstelle hier einen API-Schlüssel: {0}", + "brevoFromEmail": "Absender E-Mail", + "brevoFromName": "Absender Name", + "brevoLeaveBlankForDefaultName": "für Standardnamen leer lassen", + "brevoToEmail": "Empfänger E-Mail", + "brevoCcEmail": "CC E-Mail", + "brevoBccEmail": "BCC E-Mail", + "brevoSeparateMultipleEmails": "Mehrere E-Mail-Adressen durch Komma trennen", + "brevoSubject": "Betreff", + "brevoLeaveBlankForDefaultSubject": "für Standardbetreff leer lassen", + "Nextcloud host": "Nextcloud Host", + "Conversation token": "Gesprächs-Token", + "Bot secret": "Bot Secret", + "Send UP silently": "UP still senden", + "Send DOWN silently": "DOWN still senden", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Die Installation eines Nextcloud Talk-Bots erfordert Administratorrechte für den Server.", + "auto-select": "Automatische Auswahl", + "Dingtalk User List": "Benutzer-ID-Liste", + "Invalid userId": "Ungültige Benutzer-ID [{userId}]", + "supportBaleChatID": "Support-Direkt-Chat / Gruppe / Chat-ID des Kanals", + "wayToGetBaleChatID": "Du kannst deine Chat-ID erhalten, indem du dem Bot eine Nachricht sendest und dann diese URL aufrufst, um die chat_id anzusehen:", + "wayToGetBaleToken": "Du kannst ein Token von {0} erhalten.", + "Mention Mobile List": "Mobile Liste erwähnen", + "Mention User List": "Benutzer-ID-Liste erwähnen", + "Dingtalk Mobile List": "Liste der Mobilgeräte", + "Enter a list of userId": "Gib eine Liste von Benutzer-IDs ein", + "Enter a list of mobile": "Gib eine Liste von Mobilnummern ein", + "Invalid mobile": "Ungültige Mobilnummer [{mobile}]", + "Number of retry attempts if webhook fails": "Anzahl der Wiederholungsversuche (alle 60–180 Sekunden), wenn der Webhook fehlschlägt.", + "Maximum Retries": "Maximale Wiederholungsversuche", + "descriptionHelpText": "Wird auf dem internen Dashboard angezeigt. Markdown ist zulässig und wird vor der Anzeige bereinigt (Leerzeichen und Einrückungen bleiben erhalten).", + "HTTP Method": "HTTP-Methode", + "webhookPostMethodDesc": "POST eignet sich für die meisten modernen HTTP-Server.", + "webhookGetMethodDesc": "GET sendet Daten als Abfrageparameter und erlaubt keine Konfiguration eines Bodys. Nützlich zum Auslösen von Uptime Kuma Push-Überwachungen.", + "deleteGroupMsg": "Möchtest du diese Gruppe wirklich löschen?", + "deleteChildrenMonitors": "Lösche auch die direkten untergeordneten Monitore und deren untergeordnete Elemente, falls vorhanden | Lösche auch alle {count} direkten untergeordneten Monitore und deren untergeordnete Elemente, falls vorhanden", + "Clone Maintenance": "Wartung klonen", + "ariaCloneMaintenance": "Kopie des Wartungsplans erstellen", + "ariaEditMaintenance": "Wartungsplan bearbeiten", + "ariaDeleteMaintenance": "Wartungsplan löschen", + "ariaPauseMaintenance": "Wartungsplan pausieren", + "ariaResumeMaintenance": "Wartungsplan wieder aufnehmen", + "Template ID": "Vorlagen-ID", + "wayToGetClickSMSIRTemplateID": "Die Vorlage muss ein Feld {uptkumaalert} enthalten. Eine neue Vorlage kann {here} erstellt werden.", + "Recipient Numbers": "Empfängernummern", + "twilioApiKeyHelptext": "Der API-Schlüssel ist optional, wird jedoch empfohlen. Du kannst entweder die Konto-SID und den AuthToken von der TwilioConsole-Seite oder die Konto-SID und das Paar aus API-Schlüssel und API-Schlüssel-Geheimnis angeben.", + "twilioMessagingServiceSID": "Messaging-Dienst-SID (optional)", + "twilloMessagingServiceSIDHelptext": "Messaging-Service-SID eingeben, wenn {twillo_messaging_service_help_link} zur Verwaltung von Absendern und Funktionen verwendet wird.", + "showOnlyLastHeartbeat": "Nur letzten Heartbeat anzeigen", + "WebSocket Application Messaging Protocol": "WAMP (The WebSocket Application Messaging Protocol)", + "ignoreSecWebsocketAcceptHeaderDescription": "Ermöglicht es dem Server, nicht mit dem Sec-WebSocket-Accept-Header zu antworten, wenn das WebSocket-Upgrade erfolgreich ist.", + "Ignore Sec-WebSocket-Accept header": "Ignoriere {0} Header", + "wsSubprotocolDescription": "Kommagetrennte Liste von Subprotokollen eingeben. Weitere Informationen zu Subprotokollen findest du in der {documentation}", + "Session Initiation Protocol": "WebSocket-Transport für SIP (Session Initiation Protocol)", + "Network API for Notification Channel": "OMA RESTful-Netzwerk-API für Benachrichtigungskanal", + "Web Process Control Protocol": "Web Process Control Protocol (WPCP)", + "Advanced Message Queuing Protocol": "Advanced Message Queuing Protocol (AMQP) 1.0+", + "jsflow": "jsFlow pubsub/queue Protocol", + "Reverse Web Process Control": "Reverse Web Process Control Protocol (RWPCP)", + "Extensible Messaging and Presence Protocol": "WebSocket-Transport für das Extensible Messaging and Presence Protocol (XMPP)", + "Smart Home IP": "SHIP - Smart Home IP", + "Miele Cloud Connect Protocol": "Miele Cloud Connect Protokoll", + "Push Channel Protocol": "Push Channel Protokoll", + "Message Session Relay Protocol": "WebSocket Transport für MSRP (Message Session Relay Protocol)", + "Binary Floor Control Protocol": "WebSocket Transport für BFCP (Binary Floor Control Protocol)", + "Softvelum Low Delay Protocol": "Softvelum Low Delay Protokoll", + "OPC UA Connection Protocol": "OPC UA Connection Protokoll", + "OPC UA JSON Encoding": "OPC UA JSON Encoding", + "Swindon Web Server Protocol": "Swindon Web Server Protokoll (JSON encoding)", + "Broadband Forum User Services Platform": "USP (Broadband Forum User Services Platform)", + "Constrained Application Protocol": "Constrained Application Protokoll (CoAP)", + "Softvelum WebSocket signaling protocol": "Softvelum WebSocket Signaling Protokoll", + "Cobra Real Time Messaging Protocol": "Cobra Real Time Messaging Protokoll", + "Declarative Resource Protocol": "Declarative Resource Protokoll", + "BACnet Secure Connect Hub Connection": "BACnet Secure Connect Hub Connection", + "WebSocket Transport for JMAP": "WebSocket Transport für JMAP (JSON Meta Application Protocol)", + "BACnet Secure Connect Direct Connection": "BACnet Secure Connect Direct Connection", + "Notifications Enabled": "Benachrichtigungen aktiviert", + "Allow Notifications": "Benachrichtigungen zulassen", + "Browser not supported": "Browser wird nicht unterstützt", + "Unable to get permission to notify": "Keine Berechtigung für Benachrichtigungen (die Anfrage wurde abgelehnt oder ignoriert).", + "certHostnameMismatch": "Der Hostname des Zertifikats stimmt nicht mit der Monitor-URL überein.", + "ITU-T T.140 Real-Time Text": "ITU-T T.140 Real-Time Text", + "Done.best IoT Protocol": "Done.best IoT Protokoll", + "Collection Update": "The Collection Update Websocket Subprotokoll", + "Text IRC Protocol": "Text IRC Protokoll", + "Binary IRC Protocol": "Binary IRC Protokoll", + "Penguin Statistics Live Protocol v3": "Penguin Statistics Live Protokoll v3 (Protobuf encoding)", + "Webpush Helptext": "Web-Push funktioniert nur mit SSL-Verbindungen (HTTPS). Bei iOS-Geräten muss die Webseite zuvor zum Startbildschirm hinzugefügt werden.", + "minimumIntervalWarning": "Intervalle unter 20 Sekunden können zu einer schlechten Leistung führen.", + "lowIntervalWarning": "Möchtest du den Intervallwert wirklich auf unter 20 Sekunden einstellen? Dies kann zu Leistungseinbussen führen, insbesondere wenn eine grosse Anzahl von Monitoren vorhanden ist.", + "settingsDomainExpiry": "Ablauf der Domain", + "labelDomainExpiry": "Ablauf der Domain", + "labelDomainNameExpiryNotification": "Benachrichtigung zum Ablauf des Domainnamens", + "domainExpiryDescription": "Benachrichtigung auslösen, wenn Domainnamen abläuft. In:", + "wsCodeDescription": "Für mehr Informationen über die verfügbaren Statuscodes siehe {rfc6455}", + "Subprotocol(s)": "Unterprotokoll(e)", + "hostnameCannotBeIP": "Der DNS-Hostname darf keine IP-Adresse sein. Resolver-Feld verwenden?" +} diff --git a/src/lang/de-DE.json b/src/lang/de-DE.json new file mode 100644 index 0000000..3db5dd7 --- /dev/null +++ b/src/lang/de-DE.json @@ -0,0 +1,1620 @@ +{ + "languageName": "Deutsch", + "Settings": "Einstellungen", + "Dashboard": "Dashboard", + "New Update": "Neues Update", + "Language": "Sprache", + "Appearance": "Darstellung", + "Theme": "Design", + "General": "Allgemein", + "Version": "Version", + "Check Update On GitHub": "Auf GitHub nach Updates suchen", + "List": "Liste", + "Home": "Startseite", + "Add": "Hinzufügen", + "Add New Monitor": "Neuen Monitor hinzufügen", + "Quick Stats": "Schnellübersicht", + "Up": "Online", + "Down": "Offline", + "Pending": "Ausstehend", + "Unknown": "Unbekannt", + "Cannot connect to the socket server": "Verbindung zum Socket-Server nicht möglich", + "Reconnecting...": "Verbindung wird wiederhergestellt …", + "Pause": "Pausieren", + "pauseDashboardHome": "Pausieren", + "Name": "Name", + "Status": "Status", + "DateTime": "Datum/Uhrzeit", + "Message": "Nachricht", + "No important events": "Keine wichtigen Ereignisse", + "Resume": "Fortsetzen", + "Edit": "Bearbeiten", + "Delete": "Löschen", + "Current": "Aktuell", + "Uptime": "Verfügbarkeit", + "Cert Exp.": "Zert.-Ablauf", + "day": "Tag | Tage", + "-day": "-Tage", + "hour": "Stunde | Stunden", + "-hour": "-Stunden", + "checkEverySecond": "Alle {0} Sekunden prüfen", + "Response": "Antwort", + "Ping": "Ping", + "Monitor Type": "Monitortyp", + "Keyword": "Schlüsselwort", + "Friendly Name": "Anzeigename", + "URL": "URL", + "Hostname": "Hostname", + "Port": "Port", + "Heartbeat Interval": "Heartbeat-Intervall", + "Retries": "Wiederholungen", + "retriesDescription": "Maximale Wiederholungen, bevor der Dienst als ausgefallen markiert und eine Benachrichtigung gesendet wird", + "Advanced": "Erweitert", + "ignoreTLSError": "TLS/SSL-Fehler für HTTPS-Websites ignorieren", + "Upside Down Mode": "Umgekehrter Modus", + "upsideDownModeDescription": "Kehrt den Status um. Wenn der Dienst erreichbar ist, wird er als Offline angezeigt.", + "Max. Redirects": "Max. Weiterleitungen", + "maxRedirectDescription": "Maximale Anzahl der zu folgenden Weiterleitungen. Auf 0 setzen, um Weiterleitungen zu deaktivieren.", + "Accepted Status Codes": "Akzeptierte Statuscodes", + "acceptedStatusCodesDescription": "Wähle Statuscodes aus, die als erfolgreiche Antwort gewertet werden.", + "Save": "Speichern", + "Notifications": "Benachrichtigungen", + "Not available, please setup.": "Nicht verfügbar, bitte einrichten.", + "Setup Notification": "Benachrichtigung einrichten", + "Light": "Hell", + "Dark": "Dunkel", + "Auto": "Automatisch", + "Theme - Heartbeat Bar": "Design – Heartbeat-Leiste", + "Normal": "Normal", + "Bottom": "Unten", + "None": "Keine", + "Timezone": "Zeitzone", + "Search Engine Visibility": "Suchmaschinen-Sichtbarkeit", + "Allow indexing": "Indexierung erlauben", + "Discourage search engines from indexing site": "Suchmaschinen von der Indexierung abhalten", + "Change Password": "Passwort ändern", + "Current Password": "Aktuelles Passwort", + "New Password": "Neues Passwort", + "Repeat New Password": "Neues Passwort wiederholen", + "passwordNotMatchMsg": "Die Passwortwiederholung stimmt nicht überein.", + "Update Password": "Passwort ändern", + "Disable Auth": "Authentifizierung deaktivieren", + "Enable Auth": "Authentifizierung aktivieren", + "disableauth.message1": "Möchtest du wirklich {disableAuth}?", + "disable authentication": "Authentifizierung deaktivieren", + "disableauth.message2": "Diese Funktion ist für Szenarien gedacht, bei denen du {intendThirdPartyAuth} vor Uptime Kuma verwendest, z. B. Cloudflare Access, Authelia oder andere Authentifizierungsmechanismen.", + "where you intend to implement third-party authentication": "wo du Drittanbieter-Authentifizierung implementieren möchtest", + "Please use this option carefully!": "Bitte diese Option mit Bedacht verwenden!", + "Logout": "Abmelden", + "notificationDescription": "Benachrichtigungen müssen einem Monitor zugewiesen werden, um zu funktionieren.", + "Leave": "Verlassen", + "I understand, please disable": "Ich verstehe, bitte deaktivieren", + "Confirm": "Bestätigen", + "Yes": "Ja", + "No": "Nein", + "Username": "Benutzername", + "Password": "Passwort", + "Remember me": "Angemeldet bleiben", + "Login": "Anmelden", + "No Monitors, please": "Keine Monitore, bitte", + "add one": "hinzufügen", + "Notification Type": "Benachrichtigungstyp", + "Email": "E-Mail", + "Test": "Testen", + "Certificate Info": "Zertifikatsinformationen", + "keywordDescription": "Schlüsselwort im reinen HTML- oder JSON-Response suchen. Die Suche unterscheidet Groß-/Kleinschreibung.", + "deleteMonitorMsg": "Möchtest du diesen Monitor wirklich löschen?", + "deleteNotificationMsg": "Möchtest du diese Benachrichtigung wirklich für alle Monitore löschen?", + "resolverserverDescription": "Cloudflare ist der Standardserver. Du kannst eine kommagetrennte Liste von IP-Adressen oder Hostnamen angeben.", + "Resolver Server": "Resolver-Server", + "rrtypeDescription": "Wähle den RR-Typ, den du überwachen möchtest", + "Last Result": "Letztes Ergebnis", + "pauseMonitorMsg": "Bist du sicher, dass du pausieren möchtest?", + "clearEventsMsg": "Möchtest du wirklich alle Ereignisse für diesen Monitor löschen?", + "clearHeartbeatsMsg": "Möchtest du wirklich alle Heartbeats für diesen Monitor löschen?", + "Clear Data": "Daten löschen", + "Events": "Ereignisse", + "Heartbeats": "Heartbeats", + "confirmClearStatisticsMsg": "Möchtest du wirklich ALLE Statistiken löschen?", + "Create your admin account": "Erstelle dein Administrator-Konto", + "Repeat Password": "Passwort wiederholen", + "Resource Record Type": "Ressource-Record-Typ", + "Export": "Exportieren", + "Import": "Importieren", + "respTime": "Antw.-Zeit (ms)", + "notAvailableShort": "n. v.", + "Default enabled": "Standardmäßig aktiviert", + "Apply on all existing monitors": "Auf alle bestehenden Monitore anwenden", + "enableDefaultNotificationDescription": "Diese Benachrichtigung wird standardmäßig für neue Monitore aktiviert. Du kannst sie für jeden Monitor einzeln deaktivieren.", + "Create": "Erstellen", + "Auto Get": "Automatisch abrufen", + "backupDescription": "Du kannst alle Monitore und Benachrichtigungen in einer JSON-Datei sichern.", + "backupDescription2": "Hinweis: Verlaufs- und Ereignisdaten sind nicht enthalten.", + "backupDescription3": "Sensible Daten wie Benachrichtigungstoken sind in der Exportdatei enthalten. Bitte bewahre sie sicher auf.", + "alertNoFile": "Bitte wähle eine Datei zum Importieren aus.", + "alertWrongFileType": "Bitte wähle eine JSON-Datei aus.", + "Clear all statistics": "Alle Statistiken löschen", + "importHandleDescription": "Wähle \"Vorhandene überspringen\", um Monitore oder Benachrichtigungen mit gleichem Namen zu überspringen. \"Überschreiben\" löscht alle vorhandenen Monitore und Benachrichtigungen.", + "Skip existing": "Vorhandene überspringen", + "Overwrite": "Überschreiben", + "Options": "Optionen", + "confirmImportMsg": "Möchtest du die Sicherung wirklich importieren? Bitte überprüfe, ob du die richtige Importoption ausgewählt hast.", + "Keep both": "Beide behalten", + "twoFAVerifyLabel": "Bitte gib deinen Token ein, um 2FA zu verifizieren:", + "Verify Token": "Token verifizieren", + "Setup 2FA": "2FA einrichten", + "Enable 2FA": "2FA aktivieren", + "Disable 2FA": "2FA deaktivieren", + "2FA Settings": "2FA-Einstellungen", + "confirmEnableTwoFAMsg": "Möchtest du 2FA wirklich aktivieren?", + "confirmDisableTwoFAMsg": "Möchtest du 2FA wirklich deaktivieren?", + "tokenValidSettingsMsg": "Token ist gültig! Du kannst die 2FA-Einstellungen jetzt speichern.", + "Two Factor Authentication": "Zwei-Faktor-Authentifizierung", + "Active": "Aktiv", + "Inactive": "Inaktiv", + "Token": "Token", + "Show URI": "URI anzeigen", + "Tags": "Tags", + "Add New below or Select...": "Unten neu hinzufügen oder auswählen …", + "Tag with this name already exist.": "Ein Tag mit diesem Namen existiert bereits.", + "Tag with this value already exist.": "Ein Tag mit diesem Wert existiert bereits.", + "color": "Farbe", + "value (optional)": "Wert (optional)", + "Gray": "Grau", + "Red": "Rot", + "Orange": "Orange", + "Green": "Grün", + "Blue": "Blau", + "Indigo": "Indigo", + "Purple": "Lila", + "Pink": "Rosa", + "Search...": "Suchen …", + "Heartbeat Retry Interval": "Heartbeat-Wiederholungsintervall", + "Resend Notification if Down X times consecutively": "Benachrichtigung erneut senden nach X aufeinanderfolgenden Ausfällen", + "retryCheckEverySecond": "Alle {0} Sekunden wiederholen", + "resendEveryXTimes": "Alle {0} Mal erneut senden", + "resendDisabled": "Erneutes Senden deaktiviert", + "Import Backup": "Backup importieren", + "Export Backup": "Backup exportieren", + "Avg. Ping": "Ø Ping", + "Avg. Response": "Ø Antwortzeit", + "Entry Page": "Einstiegsseite", + "statusPageNothing": "Hier ist nichts. Bitte füge eine Gruppe oder einen Monitor hinzu.", + "No Services": "Keine Dienste", + "All Systems Operational": "Alle Systeme funktionsfähig", + "Partially Degraded Service": "Teilweise eingeschränkter Dienst", + "Degraded Service": "Eingeschränkter Dienst", + "Add Group": "Gruppe hinzufügen", + "Add a monitor": "Monitor hinzufügen", + "Edit Status Page": "Statusseite bearbeiten", + "Go to Dashboard": "Zum Dashboard", + "Status Page": "Statusseite", + "Status Pages": "Statusseiten", + "telegram": "Telegram", + "webhook": "Webhook", + "smtp": "E-Mail (SMTP)", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (unterstützt über 50 Benachrichtigungsdienste)", + "GoogleChat": "Google Chat (nur Google Workspace)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "Primary Base URL": "Primäre Basis-URL", + "Push URL": "Push-URL", + "needPushEvery": "Diese URL sollte alle {0} Sekunden aufgerufen werden.", + "pushOptionalParams": "Optionale Parameter: {0}", + "defaultNotificationName": "Meine {notification}-Benachrichtigung ({number})", + "here": "hier", + "Required": "Erforderlich", + "Bot Token": "Bot-Token", + "wayToGetTelegramToken": "Du erhältst einen Token von {0}.", + "Chat ID": "Chat-ID", + "supportTelegramChatID": "Unterstützt Direktchat / Gruppen / Kanal-Chat-ID", + "wayToGetTelegramChatID": "Du erhältst deine Chat-ID, indem du eine Nachricht an den Bot sendest und diese URL aufrufst, um die chat_id zu sehen:", + "YOUR BOT TOKEN HERE": "DEIN BOT-TOKEN HIER", + "chatIDNotFound": "Chat-ID nicht gefunden; Bitte sende zuerst eine Nachricht an diesen Bot", + "Post URL": "Post-URL", + "Content Type": "Inhaltstyp", + "webhookJsonDesc": "{0} eignet sich gut für moderne HTTP-Server wie Express.js", + "webhookFormDataDesc": "{multipart} eignet sich gut für PHP. Das JSON muss mit {decodeFunction} geparst werden", + "secureOptionNone": "Keine / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "TLS-Fehler ignorieren", + "From Email": "Absender-E-Mail", + "emailCustomSubject": "Benutzerdefinierter Betreff", + "To Email": "An E-Mail", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord-Webhook-URL", + "wayToGetDiscordURL": "Du erhältst diese, indem du zu Servereinstellungen → Integrationen → Webhooks anzeigen → Neuer Webhook gehst", + "Bot Display Name": "Bot-Anzeigename", + "Prefix Custom Message": "Benutzerdefinierte Nachricht voranstellen", + "Hello @everyone is...": "Hallo {'@'}everyone ist …", + "Webhook URL": "Webhook-URL", + "wayToGetTeamsURL": "Hier erfährst du, wie du eine Webhook-URL erstellst: {0}.", + "Number": "Nummer", + "Recipients": "Empfänger", + "needSignalAPI": "Du benötigst einen Signal-Client mit REST-API.", + "wayToCheckSignalURL": "Unter dieser URL erfährst du, wie du eine einrichtest:", + "signalImportant": "WICHTIG: Du kannst Gruppen und Nummern nicht als Empfänger mischen!", + "Application Token": "Anwendungstoken", + "Server URL": "Server-URL", + "Priority": "Priorität", + "Icon Emoji": "Icon-Emoji", + "Channel Name": "Kanalname", + "Uptime Kuma URL": "Uptime-Kuma-URL", + "aboutWebhooks": "Weitere Informationen zu Webhooks: {0}", + "aboutChannelName": "Gib den Kanalnamen im Feld \"{0} Kanalname\" ein, um den Webhook-Kanal zu umgehen. Beispiel: #anderer-kanal", + "aboutKumaURL": "Wenn du das Feld \"Uptime Kuma URL\" leer lässt, wird standardmäßig die GitHub-Projektseite verwendet.", + "emojiCheatSheet": "Emoji-Spickzettel: {0}", + "User Key": "Benutzerschlüssel", + "Device": "Gerät", + "Message Title": "Nachrichtentitel", + "Notification Sound": "Benachrichtigungston", + "More info on:": "Weitere Infos unter: {0}", + "pushoverDesc1": "Notfallpriorität (2) hat standardmäßig 30 Sekunden Wartezeit zwischen Wiederholungen und läuft nach 1 Stunde ab.", + "pushoverDesc2": "Wenn du Benachrichtigungen an verschiedene Geräte senden möchtest, fülle das Gerätefeld aus.", + "pushoverMessageTtl": "Nachrichten-TTL (Sekunden)", + "SMS Type": "SMS-Typ", + "octopushTypePremium": "Premium (schnell – empfohlen für Alarme)", + "octopushTypeLowCost": "Low Cost (langsam – wird manchmal vom Anbieter blockiert)", + "checkPrice": "Preise prüfen unter {0}:", + "octopushLegacyHint": "Verwendest du die alte Version von Octopush (2011–2020) oder die neue Version?", + "Check octopush prices": "Octopush-Preise prüfen: {0}.", + "octopushPhoneNumber": "Telefonnummer (internationales Format, z. B.: +49123456789)", + "octopushSMSSender": "SMS-Absendername: 3–11 alphanumerische Zeichen und Leerzeichen (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea-Geräte-ID", + "Apprise URL": "Apprise-URL", + "Example:": "Beispiel: {0}", + "Read more:": "Mehr erfahren: {0}", + "Status:": "Status: {0}", + "Read more": "Mehr erfahren", + "appriseInstalled": "Apprise ist installiert.", + "appriseNotInstalled": "Apprise ist nicht installiert. {0}", + "Access Token": "Zugriffstoken", + "Channel access token": "Kanal-Zugriffstoken", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console – {0}", + "Basic Settings": "Grundeinstellungen", + "User ID": "Benutzer-ID", + "Messaging API": "Messaging-API", + "wayToGetLineChannelToken": "Greife zuerst auf {0} zu, erstelle einen Provider und Kanal (Messaging API), dann erhältst du den Kanal-Zugriffstoken und die Benutzer-ID aus den oben genannten Menüpunkten.", + "Icon URL": "Icon-URL", + "aboutIconURL": "Du kannst unter \"Symbol-URL\" einen Link zu einem Bild angeben, um das Standard-Profilbild zu überschreiben. Wird nicht verwendet, wenn ein Symbol-Emoji gesetzt ist.", + "aboutMattermostChannelName": "Du kannst den Standardkanal des Webhooks überschreiben, indem du den Kanalnamen in das Feld \"Kanalname\" eingibst. Dies muss in den Mattermost-Webhook-Einstellungen aktiviert sein. Beispiel: #anderer-kanal", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO – günstig, aber langsam und oft überlastet. Nur für polnische Empfänger.", + "promosmsTypeFlash": "SMS FLASH – Nachricht wird automatisch auf dem Empfängergerät angezeigt. Nur für polnische Empfänger.", + "promosmsTypeFull": "SMS FULL – Premium-SMS, du kannst deinen Absendernamen verwenden (muss vorher registriert werden). Zuverlässig für Alarme.", + "promosmsTypeSpeed": "SMS SPEED – Höchste Priorität im System. Sehr schnell und zuverlässig, aber teuer (etwa doppelt so teuer wie SMS FULL).", + "promosmsPhoneNumber": "Telefonnummer (für polnische Empfänger kannst du Vorwahlen weglassen)", + "promosmsSMSSender": "SMS-Absendername: Vorregistrierter Name oder einer der Standards: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu-Webhook-URL", + "matrixHomeserverURL": "Homeserver-URL (mit http(s):// und optional Port)", + "Internal Room Id": "Interne Raum-ID", + "matrixDesc1": "Die interne Raum-ID findest du im erweiterten Bereich der Raumeinstellungen in deinem Matrix-Client. Sie sollte etwa so aussehen: !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Es wird dringend empfohlen, einen neuen Benutzer zu erstellen und nicht das Zugriffstoken deines eigenen Matrix-Benutzers zu verwenden, da dies vollen Zugriff auf dein Konto und alle beigetretenen Räume ermöglicht. Erstelle stattdessen einen neuen Benutzer und lade ihn nur in den Raum ein, in dem du Benachrichtigungen erhalten möchtest. Das Zugriffstoken erhältst du durch Ausführen von {0}", + "Method": "Methode", + "Body": "Inhalt", + "Headers": "Header", + "PushUrl": "Push-URL", + "HeadersInvalidFormat": "Die Anfrage-Header sind kein gültiges JSON: ", + "BodyInvalidFormat": "Der Anfrage-Body ist kein gültiges JSON: ", + "Monitor History": "Monitorverlauf", + "clearDataOlderThan": "Monitor-Verlaufsdaten {0} Tage aufbewahren.", + "PasswordsDoNotMatch": "Passwörter stimmen nicht überein.", + "records": "Einträge", + "One record": "Ein Eintrag", + "steamApiKeyDescription": "Um einen Steam-Spielserver zu überwachen, wird ein Steam-Web-API-Schlüssel benötigt. Diesen kannst du hier registrieren: ", + "Current User": "Aktueller Benutzer", + "recent": "Kürzlich", + "Done": "Fertig", + "Info": "Info", + "Security": "Sicherheit", + "Steam API Key": "Steam-API-Schlüssel", + "Shrink Database": "Datenbank verkleinern", + "Pick a RR-Type...": "RR-Typ auswählen …", + "Pick Accepted Status Codes...": "Akzeptierte Statuscodes auswählen …", + "Default": "Standard", + "HTTP Options": "HTTP-Optionen", + "Create Incident": "Vorfall erstellen", + "Title": "Titel", + "Content": "Inhalt", + "Style": "Stil", + "info": "Info", + "warning": "Warnung", + "danger": "Gefahr", + "primary": "primär", + "light": "hell", + "dark": "Dunkel", + "Post": "Post", + "Please input title and content": "Bitte Titel und Inhalt eingeben", + "Created": "Erstellt", + "Last Updated": "Zuletzt aktualisiert", + "Unpin": "Loslösen", + "Switch to Light Theme": "Zum hellen Design wechseln", + "Switch to Dark Theme": "Zum dunklen Design wechseln", + "Show Tags": "Tags anzeigen", + "Hide Tags": "Tags ausblenden", + "Description": "Beschreibung", + "No monitors available.": "Keine Monitore verfügbar.", + "Add one": "Hinzufügen", + "No Monitors": "Keine Monitore", + "Untitled Group": "Unbenannte Gruppe", + "Services": "Dienste", + "Discard": "Verwerfen", + "Cancel": "Abbrechen", + "Powered by": "Betrieben mit", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API-Benutzername (inkl. webapi_-Präfix)", + "serwersmsAPIPassword": "API-Passwort", + "serwersmsPhoneNumber": "Telefonnummer", + "serwersmsSenderName": "SMS-Absendername (über Kundenportal registriert)", + "stackfield": "Stackfield", + "clicksendsms": "ClickSend SMS", + "apiCredentials": "API-Zugangsdaten", + "smtpDkimSettings": "DKIM-Einstellungen", + "smtpDkimDesc": "Bitte lies die Nodemailer-DKIM-{0} für Verwendungshinweise.", + "documentation": "Dokumentation", + "smtpDkimDomain": "Domain-Name", + "smtpDkimKeySelector": "Key-Selector", + "smtpDkimPrivateKey": "Privater Schlüssel", + "smtpDkimHashAlgo": "Hash-Algorithmus (optional)", + "smtpDkimheaderFieldNames": "Zu signierende Header-Schlüssel (optional)", + "smtpDkimskipFields": "Nicht zu signierende Header-Schlüssel (optional)", + "PushByTechulus": "Push by Techulus", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API-Endpunkt", + "alertaEnvironment": "Umgebung", + "alertaApiKey": "API-Schlüssel", + "alertaAlertState": "Alarmzustand", + "alertaRecoverState": "Wiederherstellungszustand", + "deleteStatusPageMsg": "Möchtest du diese Statusseite wirklich löschen?", + "Proxies": "Proxys", + "default": "Standard", + "enabled": "Aktiviert", + "setAsDefault": "Als Standard festlegen", + "deleteProxyMsg": "Möchtest du diesen Proxy wirklich für alle Monitore löschen?", + "proxyDescription": "Proxys müssen einem Monitor zugewiesen werden, um zu funktionieren.", + "enableProxyDescription": "Dieser Proxy wirkt sich erst auf Monitor-Anfragen aus, wenn er aktiviert ist. Du kannst den Proxy über den Aktivierungsstatus vorübergehend für alle Monitore deaktivieren.", + "setAsDefaultProxyDescription": "Dieser Proxy wird standardmäßig für neue Monitore aktiviert. Du kannst den Proxy für jeden Monitor einzeln deaktivieren.", + "Certificate Chain": "Zertifikatskette", + "Valid": "Gültig", + "Invalid": "Ungültig", + "AccessKeyId": "AccessKey-ID", + "SecretAccessKey": "AccessKey Secret", + "PhoneNumbers": "Telefonnummern", + "TemplateCode": "TemplateCode", + "SignName": "SignName", + "Sms template must contain parameters: ": "SMS-Vorlage muss folgende Parameter enthalten: ", + "Bark Endpoint": "Bark-Endpunkt", + "WebHookUrl": "WebHookUrl", + "SecretKey": "SecretKey", + "For safety, must use secret key": "Aus Sicherheitsgründen muss ein geheimer Schlüssel verwendet werden", + "Device Token": "Geräte-Token", + "Platform": "Plattform", + "Huawei": "Huawei", + "High": "Hoch", + "Retry": "Wiederholen", + "Topic": "Thema", + "WeCom Bot Key": "WeCom-Bot-Schlüssel", + "Setup Proxy": "Proxy einrichten", + "Proxy Protocol": "Proxy-Protokoll", + "Proxy Server": "Proxy-Server", + "Proxy server has authentication": "Proxy-Server erfordert Authentifizierung", + "User": "Benutzer", + "Installed": "Installiert", + "Not installed": "Nicht installiert", + "Running": "Läuft", + "Not running": "Läuft nicht", + "Remove Token": "Token entfernen", + "Start": "Start", + "Stop": "Stoppen", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Neue Statusseite hinzufügen", + "Slug": "Slug", + "Accept characters:": "Erlaubte Zeichen:", + "startOrEndWithOnly": "Nur mit {0} beginnen oder enden", + "No consecutive dashes": "Keine aufeinanderfolgenden Bindestriche", + "Next": "Weiter", + "The slug is already taken. Please choose another slug.": "Der Slug ist bereits vergeben. Bitte wähle einen anderen Slug.", + "No Proxy": "Kein Proxy", + "Authentication": "Authentifizierung", + "HTTP Basic Auth": "HTTP-Basic-Auth", + "New Status Page": "Neue Statusseite", + "Page Not Found": "Seite nicht gefunden", + "Reverse Proxy": "Reverse-Proxy", + "Backup": "Sicherung", + "About": "Über", + "wayToGetCloudflaredURL": "(Cloudflared von {0} herunterladen)", + "cloudflareWebsite": "Cloudflare-Website", + "Message:": "Nachricht:", + "Don't know how to get the token? Please read the guide:": "Du weißt nicht, wie du den Token erhältst? Lies die Anleitung:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Die aktuelle Verbindung kann unterbrochen werden, wenn du dich über einen Cloudflare-Tunnel verbindest. Bist du sicher, dass du ihn stoppen möchtest? Gib dein aktuelles Passwort zur Bestätigung ein.", + "Other Software": "Andere Software", + "For example: nginx, Apache and Traefik.": "Zum Beispiel: nginx, Apache und Traefik.", + "Please read": "Bitte lesen", + "Subject:": "Betreff:", + "Valid To:": "Gültig bis:", + "Days Remaining:": "Verbleibende Tage:", + "Issuer:": "Aussteller:", + "Fingerprint:": "Fingerabdruck:", + "No status pages": "Keine Statusseiten", + "Domain Name Expiry Notification": "Benachrichtigung bei Domain-Ablauf", + "Customize": "Anpassen", + "Custom Footer": "Benutzerdefinierte Fußzeile", + "Custom CSS": "Benutzerdefiniertes CSS", + "Footer Text": "Fußzeilentext", + "Show Powered By": "\"Powered By\" anzeigen", + "Date Created": "Erstellt am", + "Domain Names": "Domainnamen", + "signedInDisp": "Angemeldet als {0}", + "signedInDispDisabled": "Authentifizierung deaktiviert.", + "dnsPortDescription": "DNS-Server-Port. Standardmäßig 53. Du kannst den Port jederzeit ändern.", + "topic": "Thema", + "topicExplanation": "Zu überwachendes MQTT-Topic", + "successMessage": "Erfolgsnachricht", + "successMessageExplanation": "MQTT Nachricht, die als Erfolg angesehen wird", + "error": "Fehler", + "critical": "Kritisch", + "wayToGetPagerDutyKey": "Du erhältst diesen unter Dienst → Dienstverzeichnis → (Dienst auswählen) → Integrationen → Integration hinzufügen. Dort kannst du nach \"Events API V2\" suchen. Mehr Infos {0}", + "Integration Key": "Integrationsschlüssel", + "Integration URL": "Integrations-URL", + "Auto resolve or acknowledged": "Automatisch beheben oder bestätigen", + "do nothing": "nichts tun", + "auto acknowledged": "automatisch bestätigt", + "auto resolve": "automatisch beheben", + "Bark Group": "Bark-Gruppe", + "Bark Sound": "Bark-Ton", + "HTTP Headers": "HTTP-Header", + "Trust Proxy": "Proxy vertrauen", + "Proxy": "Proxy", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "OneBot-HTTP-Adresse", + "onebotMessageType": "OneBot-Nachrichtentyp", + "onebotGroupMessage": "Gruppe", + "onebotPrivateMessage": "Privat", + "onebotUserOrGroupId": "Gruppen-/Benutzer-ID", + "onebotSafetyTips": "Aus Sicherheitsgründen muss ein Zugriffstoken gesetzt werden", + "PushDeer Key": "PushDeer-Schlüssel", + "RadiusSecret": "Radius-Secret", + "RadiusSecretDescription": "Gemeinsames Secret zwischen Client und Server", + "RadiusCalledStationId": "Called Station Id", + "RadiusCalledStationIdDescription": "Kennung des angerufenen Geräts", + "RadiusCallingStationId": "Calling Station Id", + "RadiusCallingStationIdDescription": "Kennung des anrufenden Geräts", + "Certificate Expiry Notification": "Benachrichtigung bei Zertifikatsablauf", + "API Username": "API-Benutzername", + "API Key": "API-Schlüssel", + "Recipient Number": "Empfängernummer", + "From Name/Number": "Absendername/-nummer", + "Leave blank to use a shared sender number.": "Leer lassen, um eine gemeinsame Absendernummer zu verwenden.", + "Octopush API Version": "Octopush-API-Version", + "Legacy Octopush-DM": "Legacy Octopush-DM", + "endpoint": "Endpunkt", + "octopushAPIKey": "\"API-Schlüssel\" aus den HTTP-API-Zugangsdaten im Control Panel", + "octopushLogin": "\"Login\" aus den HTTP-API-Zugangsdaten im Control Panel", + "promosmsLogin": "API-Anmeldename", + "promosmsPassword": "API-Passwort", + "pushoversounds pushover": "Pushover (Standard)", + "pushoversounds bike": "Fahrrad", + "pushoversounds bugle": "Signalhorn", + "pushoversounds cashregister": "Kasse", + "pushoversounds classical": "Klassisch", + "pushoversounds cosmic": "Kosmisch", + "pushoversounds falling": "Fallend", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Eingehend", + "pushoversounds intermission": "Pause", + "pushoversounds magic": "Magisch", + "pushoversounds mechanical": "Mechanisch", + "pushoversounds pianobar": "Piano-Bar", + "pushoversounds siren": "Sirene", + "pushoversounds spacealarm": "Weltraumalarm", + "pushoversounds tugboat": "Schlepper", + "pushoversounds alien": "Alien-Alarm (lang)", + "pushoversounds climb": "Aufstieg (lang)", + "pushoversounds persistent": "Anhaltend (lang)", + "pushoversounds echo": "Pushover-Echo (lang)", + "pushoversounds updown": "Auf und Ab (lang)", + "pushoversounds vibrate": "Nur Vibration", + "pushoversounds none": "Keine (lautlos)", + "pushyAPIKey": "Geheimer API-Schlüssel", + "pushyToken": "Geräte-Token", + "Show update if available": "Update anzeigen, wenn verfügbar", + "Also check beta release": "Auch Beta-Versionen prüfen", + "Using a Reverse Proxy?": "Verwendest du einen Reverse-Proxy?", + "Check how to config it for WebSocket": "Anleitung zur WebSocket-Konfiguration", + "Steam Game Server": "Steam-Spieleserver", + "Most likely causes:": "Wahrscheinliche Ursachen:", + "The resource is no longer available.": "Die Ressource ist nicht mehr verfügbar.", + "There might be a typing error in the address.": "Möglicherweise liegt ein Tippfehler in der Adresse vor.", + "What you can try:": "Was du versuchen kannst:", + "Retype the address.": "Adresse erneut eingeben.", + "Go back to the previous page.": "Zurück zur vorherigen Seite.", + "Coming Soon": "Demnächst verfügbar", + "wayToGetClickSendSMSToken": "Du erhältst API-Benutzername und API-Key {here}.", + "Connection String": "Verbindungszeichenfolge", + "Query": "Abfrage", + "settingsCertificateExpiry": "TLS-Zertifikatsablauf", + "certificationExpiryDescription": "HTTPS-Monitore lösen eine Benachrichtigung aus, wenn das TLS-Zertifikat abläuft in:", + "Setup Docker Host": "Docker-Host einrichten", + "Connection Type": "Verbindungstyp", + "Docker Daemon": "Docker-Daemon", + "deleteDockerHostMsg": "Möchtest du diesen Docker-Host wirklich für alle Monitore löschen?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker-Container", + "Container Name / ID": "Container-Name / ID", + "Docker Host": "Docker-Host", + "Docker Hosts": "Docker-Hosts", + "ntfy Topic": "ntfy-Topic", + "Domain": "Domain", + "Workstation": "Arbeitsstation", + "disableCloudflaredNoAuthMsg": "Du befindest dich im Modus ohne Authentifizierung, ein Passwort ist nicht erforderlich.", + "trustProxyDescription": "‚X-Forwarded-*'-Headern vertrauen. Wenn du die korrekte Client-IP erhalten möchtest und dein Uptime Kuma hinter einem Proxy wie Nginx oder Apache liegt, solltest du dies aktivieren.", + "wayToGetLineNotifyToken": "Du erhältst einen Zugriffstoken von {0}", + "Examples": "Beispiele", + "Home Assistant URL": "Home-Assistant-URL", + "Long-Lived Access Token": "Langlebiges Zugriffstoken", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Ein langlebiges Zugriffstoken kannst du erstellen, indem du auf deinen Profilnamen (unten links) klickst, nach unten scrollst und dann auf \"Token erstellen\" klickst.", + "Notification Service": "Benachrichtigungsdienst", + "default: notify all devices": "Standard: alle Geräte benachrichtigen", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Eine Liste der Benachrichtigungsdienste findest du in Home Assistant unter \"Entwicklerwerkzeuge > Dienste\". Suche nach \"notification\", um deinen Geräte-/Telefonnamen zu finden.", + "Automations can optionally be triggered in Home Assistant:": "Automatisierungen können optional in Home Assistant ausgelöst werden:", + "Trigger type:": "Auslösertyp:", + "Event type:": "Ereignistyp:", + "Event data:": "Ereignisdaten:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Wähle dann eine Aktion, z. B. eine Szene, bei der ein RGB-Licht rot leuchtet.", + "Frontend Version": "Frontend-Version", + "Frontend Version do not match backend version!": "Frontend-Version stimmt nicht mit Backend-Version überein!", + "Maintenance": "Wartung", + "statusMaintenance": "Wartung", + "Schedule maintenance": "Wartung planen", + "Affected Monitors": "Betroffene Monitore", + "Pick Affected Monitors...": "Betroffene Monitore auswählen …", + "Start of maintenance": "Wartungsbeginn", + "All Status Pages": "Alle Statusseiten", + "Select status pages...": "Statusseiten auswählen …", + "recurringIntervalMessage": "Einmal täglich ausführen | Alle {0} Tage ausführen", + "affectedMonitorsDescription": "Wähle Monitore aus, die von der aktuellen Wartung betroffen sind", + "affectedStatusPages": "Diese Wartungsmeldung auf ausgewählten Statusseiten anzeigen", + "atLeastOneMonitor": "Wähle mindestens einen betroffenen Monitor aus", + "deleteMaintenanceMsg": "Möchtest du diese Wartung wirklich löschen?", + "Base URL": "Basis-URL", + "goAlertInfo": "GoAlert ist eine Open-Source-Anwendung für Bereitschaftsplanung, automatische Eskalationen und Benachrichtigungen (wie SMS oder Sprachanrufe). Erreiche automatisch die richtige Person, auf die richtige Weise und zur richtigen Zeit! {0}", + "goAlertIntegrationKeyInfo": "Hole dir den generischen API-Integrationsschlüssel für den Dienst im Format \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\", normalerweise der Wert des Token-Parameters der kopierten URL.", + "goAlert": "GoAlert", + "backupOutdatedWarning": "Veraltet: Da viele Funktionen hinzugefügt wurden und diese Sicherungsfunktion kaum gewartet wird, kann sie keine vollständige Sicherung erstellen oder wiederherstellen.", + "backupRecommend": "Bitte sichere stattdessen das Volume oder den Datenordner (./data/) direkt.", + "Optional": "Optional", + "squadcast": "Squadcast", + "SendKey": "SendKey", + "SMSManager API Docs": "SMSManager-API-Dokumentation", + "Gateway Type": "Gateway-Typ", + "SMSManager": "SMSManager", + "You can divide numbers with": "Du kannst Nummern trennen mit", + "or": "oder", + "recurringInterval": "Intervall", + "Recurring": "Wiederkehrend", + "Single Maintenance Window": "Einmaliges Wartungsfenster", + "Maintenance Time Window of a Day": "Tägliches Wartungszeitfenster", + "Effective Date Range": "Gültigkeitszeitraum (optional)", + "strategyManual": "Manuell aktivieren/deaktivieren", + "warningTimezone": "Es wird die Zeitzone des Servers verwendet", + "weekdayShortMon": "Mo", + "weekdayShortTue": "Di", + "weekdayShortWed": "Mi", + "weekdayShortThu": "Do", + "weekdayShortFri": "Fr", + "weekdayShortSat": "Sa", + "weekdayShortSun": "So", + "dayOfWeek": "Wochentag", + "dayOfMonth": "Tag des Monats", + "lastDay": "Letzter Tag", + "lastDay1": "Letzter Tag des Monats", + "lastDay2": "Vorletzter Tag des Monats", + "lastDay3": "Drittletzter Tag des Monats", + "lastDay4": "Viertletzter Tag des Monats", + "No Maintenance": "Keine Wartung", + "Schedule Maintenance": "Wartung planen", + "Edit Maintenance": "Wartung bearbeiten", + "pauseMaintenanceMsg": "Bist du sicher, dass du pausieren möchtest?", + "maintenanceStatus-under-maintenance": "In Wartung", + "maintenanceStatus-inactive": "Inaktiv", + "maintenanceStatus-scheduled": "Geplant", + "maintenanceStatus-ended": "Beendet", + "maintenanceStatus-unknown": "Unbekannt", + "Display Timezone": "Angezeigte Zeitzone", + "Server Timezone": "Server-Zeitzone", + "Date and Time": "Datum und Uhrzeit", + "DateTime Range": "Datums-/Uhrzeitbereich", + "Strategy": "Strategie", + "statusPageMaintenanceEndDate": "Ende", + "Help": "Hilfe", + "Game": "Spiel", + "Custom": "Benutzerdefiniert", + "Enable DNS Cache": "(Veraltet) DNS-Cache für HTTP(s)-Monitore aktivieren", + "Enable": "Aktivieren", + "Disable": "Deaktivieren", + "Custom Monitor Type": "Benutzerdefinierter Monitortyp", + "webhookAdditionalHeadersDesc": "Legt zusätzliche Header fest, die mit dem Webhook gesendet werden. Jeder Header sollte als JSON-Schlüssel/Wert-Paar definiert werden.", + "dnsCacheDescription": "Funktioniert möglicherweise nicht in manchen IPv6-Umgebungen. Deaktiviere diese Option bei Problemen.", + "loadingError": "Daten konnten nicht abgerufen werden. Bitte versuche es später erneut.", + "confirmUninstallPlugin": "Möchtest du dieses Plugin wirklich deinstallieren?", + "grpcMethodDescription": "Der Methodenname wird in camelCase-Format konvertiert, z. B. sayHello, check usw.", + "Passive Monitor Type": "Passiver Monitortyp", + "Specific Monitor Type": "Spezifischer Monitortyp", + "webhookAdditionalHeadersTitle": "Zusätzliche Header", + "Packet Size": "Paketgröße", + "IconUrl": "Icon-URL", + "wayToGetZohoCliqURL": "Hier erfährst du, wie du eine Webhook-URL erstellst: {0}.", + "dataRetentionTimeError": "Der Aufbewahrungszeitraum muss 0 oder größer sein", + "infiniteRetention": "Auf 0 setzen für unbegrenzte Aufbewahrung.", + "confirmDeleteTagMsg": "Möchtest du diesen Tag wirklich löschen? Mit diesem Tag verknüpfte Monitore werden nicht gelöscht.", + "enableGRPCTls": "gRPC-Anfragen mit TLS-Verbindung erlauben", + "ZohoCliq": "ZohoCliq", + "Monitor": "Monitor | Monitore", + "plugin": "Plugin | Plugins", + "install": "Installieren", + "installing": "Wird installiert", + "uninstall": "Deinstallieren", + "uninstalling": "Wird deinstalliert", + "markdownSupported": "Markdown-Syntax wird unterstützt. Vermeide bei HTML führende Leerzeichen, um Formatierungsprobleme zu vermeiden.", + "wayToGetKookBotToken": "Erstelle eine Anwendung und hole dir deinen Bot-Token unter {0}", + "wayToGetKookGuildID": "Aktiviere den ‚Entwicklermodus' in den Kook-Einstellungen und klicke mit der rechten Maustaste auf die Gilde, um ihre ID zu erhalten", + "Guild ID": "Server-ID", + "Free Mobile User Identifier": "Free-Mobile-Benutzerkennung", + "Free Mobile API Key": "Free-Mobile-API-Schlüssel", + "Enable TLS": "TLS aktivieren", + "Proto Service Name": "Proto-Dienstname", + "Proto Method": "Proto-Methode", + "Proto Content": "Proto-Inhalt", + "Economy": "Sparsam", + "Lowcost": "Günstig", + "high": "hoch", + "promosmsAllowLongSMS": "Lange SMS erlauben", + "General Monitor Type": "Allgemeiner Monitortyp", + "smseagle": "SMSEagle", + "smseagleTo": "Telefonnummer(n)", + "smseagleGroup": "Telefonbuch-Gruppenname(n)", + "smseagleContact": "Telefonbuch-Kontaktname(n)", + "smseagleRecipientType": "Empfängertyp", + "smseagleRecipient": "Empfänger (mehrere mit Komma trennen)", + "smseagleToken": "API-Zugriffstoken", + "smseagleUrl": "URL deines SMSEagle-Geräts", + "Kook": "Kook", + "smseagleEncoding": "Als Unicode senden (Standard = GSM-7)", + "smseaglePriority": "Nachrichtenpriorität (0–9, höchste Priorität = 9)", + "Google Analytics ID": "Google-Analytics-ID", + "Edit Tag": "Tag bearbeiten", + "Server Address": "Serveradresse", + "Learn More": "Mehr erfahren", + "Body Encoding": "Inhaltscodierung", + "Add API Key": "API-Schlüssel hinzufügen", + "apiKey-active": "Aktiv", + "apiKey-expired": "Abgelaufen", + "apiKey-inactive": "Inaktiv", + "Expires": "Läuft ab", + "deleteAPIKeyMsg": "Möchtest du diesen API-Schlüssel wirklich löschen?", + "Generate": "Generieren", + "API Keys": "API-Schlüssel", + "Expiry": "Ablauf", + "Expiry date": "Ablaufdatum", + "Don't expire": "Nicht ablaufen lassen", + "Continue": "Weiter", + "Add Another": "Weiteren hinzufügen", + "Clone Monitor": "Monitor klonen", + "Clone": "Klonen", + "cloneOf": "Kopie von {0}", + "pagertreeIntegrationUrl": "Integrations-URL", + "pagertreeUrgency": "Dringlichkeit", + "pagertreeSilent": "Lautlos", + "pagertreeLow": "Niedrig", + "pagertreeMedium": "Mittel", + "pagertreeHigh": "Hoch", + "pagertreeCritical": "Kritisch", + "pagertreeResolve": "Automatisch auflösen", + "No API Keys": "Keine API-Schlüssel", + "disableAPIKeyMsg": "Möchtest du diesen API-Schlüssel wirklich deaktivieren?", + "pagertreeDoNothing": "Nichts tun", + "wayToGetPagerTreeIntegrationURL": "Nachdem du die Uptime-Kuma-Integration in PagerTree erstellt hast, kopiere den Endpoint. Vollständige Details {0}", + "telegramProtectContent": "Weiterleitung/Speichern schützen", + "telegramProtectContentDescription": "Wenn aktiviert, werden die Bot-Nachrichten in Telegram vor Weiterleitung und Speichern geschützt.", + "notificationRegional": "Regional", + "Key Added": "Schlüssel hinzugefügt", + "apiKeyAddedMsg": "Dein API-Schlüssel wurde hinzugefügt. Bitte notiere ihn, da er nicht erneut angezeigt wird.", + "telegramMessageThreadID": "(Optional) Nachrichten-Thread-ID", + "telegramMessageThreadIDDescription": "Optionale eindeutige Kennung für den Ziel-Nachrichten-Thread (Thema) des Forums; nur für Forum-Supergruppen", + "telegramSendSilently": "Lautlos senden", + "telegramSendSilentlyDescription": "Sendet die Nachricht lautlos. Benutzer erhalten eine Benachrichtigung ohne Ton.", + "Add New Tag": "Neuen Tag hinzufügen", + "lunaseaDeviceID": "Geräte-ID", + "lunaseaTarget": "Ziel", + "lunaseaUserID": "Benutzer-ID", + "ntfyAuthenticationMethod": "Authentifizierungsmethode", + "ntfyUsernameAndPassword": "Benutzername und Passwort", + "twilioAccountSID": "Account-SID", + "twilioFromNumber": "Von Nummer", + "twilioToNumber": "An Nummer", + "twilioAuthToken": "Auth-Token / API-Key-Secret", + "statusPageRefreshIn": "Aktualisierung in: {0}", + "sameAsServerTimezone": "Wie Server-Zeitzone", + "startDateTime": "Startdatum/-zeit", + "endDateTime": "Enddatum/-zeit", + "cronExpression": "Cron-Ausdruck", + "cronSchedule": "Zeitplan: ", + "invalidCronExpression": "Ungültiger Cron-Ausdruck: {0}", + "Show Clickable Link": "Klickbaren Link anzeigen", + "Open Badge Generator": "Badge-Generator öffnen", + "Badge Generator": "Badge-Generator für {0}", + "Badge Type": "Badge-Typ", + "Badge Duration": "Badge Dauer", + "Badge Label": "Badge-Beschriftung", + "Show Clickable Link Description": "Wenn aktiviert, können alle mit Zugriff auf diese Statusseite die Monitor-URL sehen.", + "Badge Label Color": "Badge-Beschriftungsfarbe", + "Badge Color": "Badge-Farbe", + "Badge Label Prefix": "Badge-Beschriftungspräfix", + "Badge Label Suffix": "Badge-Beschriftungssuffix", + "Badge Maintenance Color": "Badge-Farbe (Wartung)", + "Badge Warn Color": "Badge-Farbe (Warnung)", + "Badge Style": "Badge-Stil", + "Badge value (For Testing only.)": "Badge-Wert (nur zum Testen)", + "Badge URL": "Badge-URL", + "Badge Up Color": "Badge-Farbe (Online)", + "Badge Down Color": "Badge-Farbe (Offline)", + "Badge Pending Color": "Badge-Farbe (Ausstehend)", + "Badge Down Days": "Badge-Tage (Offline)", + "Monitor Setting": "Monitoreinstellungen für {0}", + "Badge Prefix": "Badge-Wertpräfix", + "Badge Suffix": "Badge-Wertsuffix", + "Badge Warn Days": "Badge-Tage (Warnung)", + "Group": "Gruppe", + "Monitor Group": "Monitorgruppe", + "noGroupMonitorMsg": "Nicht verfügbar. Erstelle zuerst einen Gruppenmonitor.", + "Close": "Schließen", + "chromeExecutableAutoDetect": "Automatisch erkennen", + "chromeExecutableDescription": "Für Docker-Benutzer: Falls Chromium noch nicht installiert ist, kann die Installation einige Minuten dauern, bevor das Testergebnis angezeigt wird. Es werden ca. 1 GB Speicherplatz benötigt.", + "chromeExecutable": "Chrome/Chromium-Programmdatei", + "Invert Keyword": "Schlüsselwort invertieren", + "invertKeywordDescription": "Nach Abwesenheit statt Vorhandensein des Schlüsselworts suchen.", + "webhookCustomBodyDesc": "Definiere einen benutzerdefinierten HTTP-Body für die Anfrage. Die Template-Variablen {msg}, {heartbeat} und {monitor} werden akzeptiert.", + "webhookBodyPresetOption": "Vorlage – {0}", + "webhookBodyCustomOption": "Benutzerdefinierter Body", + "Request Body": "Anfrage-Body", + "Badge Duration (in hours)": "Badge-Dauer (in Stunden)", + "Badge Preview": "Badge-Vorschau", + "twilioApiKey": "API-Key (optional)", + "Notify Channel": "Kanal benachrichtigen", + "Enter the list of brokers": "Liste der Broker eingeben", + "Kafka Topic Name": "Kafka-Topic-Name", + "Kafka Producer Message": "Kafka-Producer-Nachricht", + "Enable Kafka SSL": "Kafka-SSL aktivieren", + "Enable Kafka Producer Auto Topic Creation": "Automatische Kafka-Topic-Erstellung aktivieren", + "Kafka SASL Options": "Kafka-SASL-Optionen", + "Mechanism": "Mechanismus", + "Pick a SASL Mechanism...": "SASL-Mechanismus auswählen …", + "Authorization Identity": "Authorization-Identität", + "AccessKey Id": "AccessKey-ID", + "Secret AccessKey": "Geheimer AccessKey", + "Session Token": "Session-Token", + "aboutNotifyChannel": "\"Kanal benachrichtigen\" löst eine Desktop- oder Mobilbenachrichtigung für alle Kanalmitglieder aus, unabhängig davon, ob ihr Status auf aktiv oder abwesend steht.", + "Kafka Brokers": "Kafka-Broker", + "Press Enter to add broker": "Enter drücken, um Broker hinzuzufügen", + "filterActive": "Aktiv", + "filterActivePaused": "Pausiert", + "Expected Value": "Erwarteter Wert", + "Json Query": "JSON-Abfrage", + "tailscalePingWarning": "Um den Tailscale-Ping-Monitor zu verwenden, musst du Uptime Kuma ohne Docker installieren und außerdem den Tailscale-Client auf deinem Server installieren.", + "Server URL should not contain the nfty topic": "Server-URL sollte das ntfy-Topic nicht enthalten", + "pushDeerServerDescription": "Leer lassen, um den offiziellen Server zu verwenden", + "FlashDuty Severity": "Schweregrad", + "nostrRelays": "Nostr-Relays", + "gamedigGuessPort": "Gamedig: Port erraten", + "Request Timeout": "Anfrage-Timeout", + "styleElapsedTimeShowNoLine": "Anzeigen (ohne Linie)", + "Select": "Auswählen", + "selectedMonitorCount": "Ausgewählt: {0}", + "PushDeer Server": "PushDeer-Server", + "nostrRelaysHelp": "Eine Relay-URL pro Zeile", + "nostrSender": "Absender-privater-Schlüssel (nsec)", + "gamedigGuessPortDescription": "Der Port für das Valve Server Query Protocol kann vom Client-Port abweichen. Probiere dies aus, wenn der Monitor keine Verbindung zu deinem Server herstellen kann.", + "wayToGetFlashDutyKey": "Um Uptime Kuma mit Flashduty zu integrieren: Gehe zu Kanäle > Wähle einen Kanal > Integrationen > Neue Integration hinzufügen, wähle Uptime Kuma und kopiere die Push-URL.", + "timeoutAfter": "Zeitüberschreitung nach {0} Sekunden", + "styleElapsedTimeShowWithLine": "Anzeigen (mit Linie)", + "styleElapsedTime": "Verstrichene Zeit unter der Heartbeat-Leiste", + "Check/Uncheck": "Auswählen/Abwählen", + "nostrRecipients": "Empfänger-öffentliche-Schlüssel (npub)", + "nostrRecipientsHelp": "npub-Format, einer pro Zeile", + "showCertificateExpiry": "Zertifikatsablauf anzeigen", + "noOrBadCertificate": "Kein/ungültiges Zertifikat", + "enableNSCD": "NSCD (Name Service Cache Daemon) für das Caching aller DNS-Anfragen aktivieren", + "setupDatabaseChooseDatabase": "Welche Datenbank möchtest du verwenden?", + "setupDatabaseEmbeddedMariaDB": "Du musst nichts einstellen. Dieses Docker-Image enthält eine bereits konfigurierte MariaDB. Uptime Kuma verbindet sich über Unix-Socket mit dieser Datenbank.", + "dbName": "Datenbankname", + "setupDatabaseMariaDB": "Mit externer MariaDB-Datenbank verbinden. Du musst die Datenbankverbindungsinformationen eingeben.", + "setupDatabaseSQLite": "Eine einfache Datenbankdatei, empfohlen für kleine Installationen. Vor v2.0.0 verwendete Uptime Kuma SQLite als Standarddatenbank.", + "Saved.": "Gespeichert.", + "toastSuccessTimeout": "Anzeigedauer für Erfolgsmeldungen", + "toastErrorTimeout": "Anzeigedauer für Fehlermeldungen", + "monitorToastMessagesLabel": "Monitor-Toast-Benachrichtigungen", + "monitorToastMessagesDescription": "Toast-Benachrichtigungen für Monitore verschwinden nach der angegebenen Zeit in Sekunden. -1 deaktiviert das Timeout. 0 deaktiviert Toast-Benachrichtigungen.", + "Bark API Version": "Bark-API-Version", + "pushViewCode": "Wie verwendet man den Push-Monitor? (Code anzeigen)", + "pushOthers": "Sonstige", + "programmingLanguages": "Programmiersprachen", + "authInvalidToken": "Ungültiges Token.", + "authIncorrectCreds": "Falscher Benutzername oder falsches Passwort.", + "2faAlreadyEnabled": "2FA ist bereits aktiviert.", + "2faEnabled": "2FA aktiviert.", + "2faDisabled": "2FA deaktiviert.", + "successResumed": "Erfolgreich fortgesetzt.", + "successPaused": "Erfolgreich pausiert.", + "successDeleted": "Erfolgreich gelöscht.", + "successEdited": "Erfolgreich bearbeitet.", + "successBackupRestored": "Backup erfolgreich wiederhergestellt.", + "successDisabled": "Erfolgreich deaktiviert.", + "successEnabled": "Erfolgreich aktiviert.", + "tagNotFound": "Tag nicht gefunden.", + "authUserInactiveOrDeleted": "Der Benutzer ist inaktiv oder gelöscht.", + "successAdded": "Erfolgreich hinzugefügt.", + "successAuthChangePassword": "Passwort erfolgreich geändert.", + "foundChromiumVersion": "Chromium/Chrome gefunden. Version: {0}", + "Reset Token": "Token zurücksetzen", + "leave blank for default subject": "Leer lassen für Standard-Betreff", + "emailCustomBody": "Benutzerdefinierter Inhalt", + "leave blank for default body": "Leer lassen für Standard-Nachrichtentext", + "emailTemplateServiceName": "Dienst Name", + "emailTemplateStatus": "Status", + "emailTemplateMsg": "Nachrichtentext der Benachrichtigung", + "liquidIntroduction": "Template-Funktionalität wird über die Liquid-Templating-Sprache bereitgestellt. Eine Anleitung findest du in der {0}.", + "emailCustomisableContent": "Anpassbarer Inhalt", + "smtpLiquidIntroduction": "Die folgenden zwei Felder können mit der Liquid-Template-Sprache angepasst werden. Siehe {0} für Anweisungen. Dies sind die verfügbaren Variablen:", + "emailTemplateHostnameOrURL": "Hostname oder URL", + "emailTemplateLimitedToUpDownNotification": "nur für Online/Offline-Heartbeats verfügbar, sonst null", + "templateMsg": "Nachrichtentext der Benachrichtigung", + "templateHeartbeatJSON": "Objekt, das den Heartbeat beschreibt", + "templateMonitorJSON": "Objekt, das den Monitor beschreibt", + "templateLimitedToUpDownCertNotifications": "nur für Online/Offline/Zertifikatsablauf-Benachrichtigungen verfügbar", + "templateLimitedToUpDownNotifications": "nur für Online/Offline-Benachrichtigungen verfügbar", + "emailTemplateMonitorJSON": "Objekt, das den Monitor beschreibt", + "emailTemplateHeartbeatJSON": "Objekt, das den Heartbeat beschreibt", + "GrafanaOncallUrl": "Grafana-Oncall-URL", + "noDockerHostMsg": "Nicht verfügbar. Richte zuerst einen Docker-Host ein.", + "DockerHostRequired": "Bitte lege einen Docker-Host für diesen Monitor fest.", + "Browser Screenshot": "Browser-Screenshot", + "setup a new monitor group": "Neue Monitorgruppe einrichten", + "Add a new expiry notification day": "Neuen Ablauf-Benachrichtigungstag hinzufügen", + "Remote Browsers": "Remote-Browser", + "Remote Browser": "Remote-Browser", + "Add a Remote Browser": "Remote-Browser hinzufügen", + "Remote Browser not found!": "Remote-Browser nicht gefunden!", + "remoteBrowsersDescription": "Remote-Browser sind eine Alternative zur lokalen Chromium-Ausführung. Nutze einen Dienst wie browserless.io oder verbinde dich mit deinem eigenen", + "self-hosted container": "Selbstgehosteter Container", + "remoteBrowserToggle": "Standardmäßig läuft Chromium im Uptime-Kuma-Container. Du kannst einen Remote-Browser verwenden, indem du diesen Schalter aktivierst.", + "useRemoteBrowser": "Remote-Browser verwenden", + "deleteRemoteBrowserMessage": "Möchtest du diesen Remote-Browser wirklich für alle Monitore löschen?", + "Remove the expiry notification": "Ablaufbenachrichtigungstag entfernen", + "openModalTo": "Modal öffnen für {0}", + "Add a domain": "Domain hinzufügen", + "Remove domain": "Domain \"{0}\" entfernen", + "successKeyword": "Erfolgs-Schlüsselwort", + "successKeywordExplanation": "MQTT-Schlüsselwort, das als Erfolg gewertet wird", + "settingUpDatabaseMSG": "Datenbank wird eingerichtet. Dies kann eine Weile dauern, bitte hab Geduld.", + "Search monitored sites": "Überwachte Seiten durchsuchen", + "statusPageSpecialSlugDesc": "Spezieller Slug {0}: Diese Seite wird angezeigt, wenn kein Slug angegeben ist", + "ntfyPriorityHelptextAllEvents": "Alle Ereignisse werden mit der höchsten Priorität gesendet", + "ntfyPriorityHelptextAllExceptDown": "Alle Ereignisse werden mit dieser Priorität gesendet, außer {0}-Ereignisse, die Priorität {1} haben", + "What is a Remote Browser?": "Was ist ein Remote-Browser?", + "Channel access token (Long-lived)": "Kanal-Zugriffstoken (langlebig)", + "Your User ID": "Deine Benutzer-ID", + "wayToGetHeiiOnCallDetails": "Wie du die Trigger-ID und API-Keys erhältst, wird in der {documentation} erklärt", + "documentationOf": "{0}-Dokumentation", + "gtxMessagingToHint": "Internationales Format mit führendem \"+\" ({e164}, {e212} oder {e214})", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Absender-Telefonnummer / Transmission Path Originating Address (TPOA)", + "To Phone Number": "An Telefonnummer", + "gtxMessagingApiKeyHint": "Du findest deinen API-Schlüssel unter: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "gtxMessagingFromHint": "Auf Mobiltelefonen wird dem Empfänger die TPOA als Absender angezeigt. Erlaubt sind bis zu 11 alphanumerische Zeichen, ein Shortcode, die lokale Langwahlnummer oder internationale Nummern ({e164}, {e212} oder {e214})", + "Alphanumeric (recommended)": "Alphanumerisch (empfohlen)", + "Telephone number": "Telefonnummer", + "Originator": "Absender", + "cellsyntOriginator": "Wird auf dem Mobiltelefon des Empfängers als Absender der Nachricht angezeigt. Erlaubte Werte und Funktionen hängen vom Parameter originatortype ab.", + "Destination": "Ziel", + "Allow Long SMS": "Lange SMS erlauben", + "cellsyntSplitLongMessages": "Lange Nachrichten in bis zu 6 Teile aufteilen. 153 × 6 = 918 Zeichen.", + "max 15 digits": "max. 15 Ziffern", + "max 11 alphanumeric characters": "max. 11 alphanumerische Zeichen", + "Originator type": "Absendertyp", + "cellsyntOriginatortypeAlphanumeric": "Alphanumerische Zeichenkette (max. 11 alphanumerische Zeichen). Empfänger können nicht auf die Nachricht antworten.", + "cellsyntOriginatortypeNumeric": "Numerischer Wert (max. 15 Ziffern) mit Telefonnummer im internationalen Format ohne führende 00 (Beispiel: deutsche Nummer 0170 12345678 als 4917012345678). Empfänger können auf die Nachricht antworten.", + "cellsyntDestination": "Telefonnummer des Empfängers im internationalen Format mit führender 00 gefolgt von der Ländervorwahl, z. B. 004917012345678 für die deutsche Nummer 0170 12345678 (max. 17 Ziffern insgesamt). Max. 25.000 kommagetrennte Empfänger pro HTTP-Anfrage.", + "callMeBotGet": "Hier kannst du einen Endpunkt für {0}, {1} und {2} generieren. Beachte, dass Ratenlimits gelten können. Die Ratenlimits scheinen zu sein: {3}", + "wayToGetWhapiUrlAndToken": "Du erhältst die API-URL und den Token, indem du in {0} zu deinem gewünschten Kanal gehst", + "whapiRecipient": "Telefonnummer / Kontakt-ID / Gruppen-ID", + "API URL": "API-URL", + "wayToWriteWhapiRecipient": "Die Telefonnummer mit internationaler Vorwahl, aber ohne das Pluszeichen am Anfang ({0}), die Kontakt-ID ({1}) oder die Gruppen-ID ({2}).", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Gib entweder den Hostnamen des Servers ein, mit dem du dich verbinden möchtest, oder {localhost}, wenn du einen {local_mta} verwenden möchtest", + "locally configured mail transfer agent": "Lokal konfigurierter Mail-Transfer-Agent", + "Mentioning": "Erwähnungen", + "Don't mention people": "Personen nicht erwähnen", + "Mention group": "{group} erwähnen", + "senderSevenIO": "Absendernummer oder -name", + "receiverSevenIO": "Empfängernummer", + "apiKeySevenIO": "SevenIO-API-Schlüssel", + "wayToGetSevenIOApiKey": "Besuche das Dashboard unter app.seven.io > Entwickler > API-Schlüssel > grüner Hinzufügen-Button", + "receiverInfoSevenIO": "Wenn sich die Empfängernummer nicht in Deutschland befindet, musst du die Landesvorwahl voranstellen (z. B. für die US-Landesvorwahl 1 verwende 117612121212 statt 017612121212)", + "Host URL": "Host-URL", + "whatHappensAtForumPost": "Erstellt einen neuen Forenbeitrag. Dies postet NICHT in bestehende Beiträge. Um in bestehende Beiträge zu posten, verwende \"{option}\"", + "Bitrix24 Webhook URL": "Bitrix24-Webhook-URL", + "wayToGetBitrix24Webhook": "Du kannst einen Webhook erstellen, indem du den Schritten unter {0} folgst", + "bitrix24SupportUserID": "Gib deine Benutzer-ID in Bitrix24 ein. Du findest die ID im Link, wenn du das Benutzerprofil aufrufst.", + "Command": "Befehl", + "mongodbCommandDescription": "Führe einen MongoDB-Befehl auf der Datenbank aus. Informationen zu verfügbaren Befehlen findest du in der {documentation}", + "Refresh Interval": "Aktualisierungsintervall", + "Refresh Interval Description": "Die Statusseite wird alle {0} Sekunden vollständig neu geladen", + "Select message type": "Nachrichtentyp auswählen", + "Send to channel": "An Kanal senden", + "ignoreTLSErrorGeneral": "TLS/SSL-Fehler für Verbindung ignorieren", + "Create new forum post": "Neuen Forenbeitrag erstellen", + "postToExistingThread": "In vorhandenen Thread/Forumsbeitrag posten", + "forumPostName": "Forenbeitragsname", + "threadForumPostID": "Thread- / Forenbeitrags-ID", + "e.g. {discordThreadID}": "z. B. {discordThreadID}", + "wayToGetDiscordThreadId": "Das Abrufen einer Thread-/Forenbeitrags-ID funktioniert ähnlich wie bei einer Kanal-ID. Mehr über das Abrufen von IDs erfährst du {0}", + "smspartnerPhoneNumber": "Telefonnummer(n)", + "smspartnerSenderName": "SMS-Absendername", + "smspartnerSenderNameInfo": "Muss zwischen 3 und 11 reguläre Zeichen haben", + "smspartnerApiurl": "Deinen API-Schlüssel findest du in deinem Dashboard unter {0}", + "smspartnerPhoneNumberHelptext": "Die Nummer muss im internationalen Format sein: {0}, {1}. Mehrere Nummern müssen mit {2} getrennt werden", + "threemaRecipient": "Empfänger", + "threemaRecipientType": "Empfängertyp", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypePhone": "Telefonnummer", + "threemaRecipientTypePhoneFormat": "E.164, ohne führendes +", + "threemaRecipientTypeEmail": "E-Mail-Adresse", + "threemaSenderIdentity": "Gateway-ID", + "threemaApiAuthenticationSecret": "Gateway-ID-Geheimnis", + "wayToGetThreemaGateway": "Du kannst dich für Threema Gateway {0} registrieren.", + "threemaRecipientTypeIdentityFormat": "8 Zeichen", + "threemaSenderIdentityFormat": "8 Zeichen, beginnt üblicherweise mit *", + "threemaBasicModeInfo": "Hinweis: Diese Integration verwendet Threema Gateway im Basismodus (serverbasierte Verschlüsselung). Weitere Details findest du {0}.", + "apiKeysDisabledMsg": "API-Schlüssel sind deaktiviert, da die Authentifizierung deaktiviert ist.", + "wayToGetOnesenderUrlandToken": "Du erhältst URL und Token auf der Onesender-Website. Mehr Infos {0}", + "Lost connection to the socket server.": "Verbindung zum Socket-Server verloren.", + "conditionDeleteGroup": "Gruppe löschen", + "greater than": "größer als", + "snmpOIDHelptext": "Gib die OID für den Sensor oder Status ein, den du überwachen möchtest. Nutze Netzwerk-Management-Tools wie MIB-Browser oder SNMP-Software, wenn du dir bei der OID unsicher bist.", + "signl4Docs": "Weitere Informationen zur Konfiguration von SIGNL4 und zum Erhalt der SIGNL4-Webhook-URL findest du in der {0}.", + "now": "jetzt", + "time ago": "vor {0}", + "Json Query Expression": "JSON-Abfrageausdruck", + "-year": "-Jahr", + "and": "und", + "jsonQueryDescription": "Analysiere und extrahiere bestimmte Daten aus der JSON-Antwort des Servers mittels JSON-Abfrage oder verwende \"$\" für die Rohantwort, falls kein JSON erwartet wird. Das Ergebnis wird dann als String mit dem erwarteten Wert verglichen. Dokumentation unter {0}, zum Ausprobieren von Abfragen siehe {1}.", + "cacheBusterParamDescription": "Zufällig generierter Parameter zum Umgehen von Caches.", + "cacheBusterParam": "Parameter {0} hinzufügen", + "Community String": "Community-String", + "snmpCommunityStringHelptext": "Dieser String dient als Passwort zur Authentifizierung und Zugriffskontrolle auf SNMP-fähige Geräte. Gleiche ihn mit der Konfiguration deines SNMP-Geräts ab.", + "OID (Object Identifier)": "OID (Objektkennung)", + "Condition": "Bedingung", + "SNMP Version": "SNMP-Version", + "Please enter a valid OID.": "Bitte eine gültige OID eingeben.", + "Host Onesender": "Onesender-Host", + "Token Onesender": "Onesender-Token", + "Recipient Type": "Empfängertyp", + "Private Number": "Private Nummer", + "Group ID": "Gruppen-ID", + "privateOnesenderDesc": "Stelle sicher, dass die Telefonnummer gültig ist. Um Nachrichten an private Nummern zu senden, z. B.: 49123456789", + "groupOnesenderDesc": "Stelle sicher, dass die Gruppen-ID gültig ist. Um eine Nachricht an eine Gruppe zu senden, z. B.: 628123456789-342345", + "Add Remote Browser": "Remote-Browser hinzufügen", + "New Group": "Neue Gruppe", + "Group Name": "Gruppenname", + "OAuth2: Client Credentials": "OAuth2: Client Credentials", + "Authentication Method": "Authentifizierungsmethode", + "Authorization Header": "Authorization-Header", + "Form Data Body": "Formulardaten-Inhalt", + "OAuth Token URL": "OAuth-Token-URL", + "Client ID": "Client-ID", + "Client Secret": "Client-Geheimnis", + "OAuth Scope": "OAuth-Scope", + "Optional: Space separated list of scopes": "Optional: Durch Leerzeichen getrennte Liste von Scopes", + "Go back to home page.": "Zurück zur Startseite.", + "No tags found.": "Keine Tags gefunden.", + "Cannot connect to the socket server.": "Verbindung zum Socket-Server nicht möglich.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4-Webhook-URL", + "Conditions": "Bedingungen", + "conditionAdd": "Bedingung hinzufügen", + "conditionDelete": "Bedingung löschen", + "conditionAddGroup": "Gruppe hinzufügen", + "conditionValuePlaceholder": "Wert", + "equals": "ist gleich", + "not equals": "ungleich", + "contains": "enthält", + "not contains": "enthält nicht", + "starts with": "beginnt mit", + "not starts with": "beginnt nicht mit", + "ends with": "endet mit", + "not ends with": "endet nicht mit", + "less than": "kleiner als", + "less than or equal to": "kleiner oder gleich", + "greater than or equal to": "größer oder gleich", + "record": "Eintrag", + "shrinkDatabaseDescriptionSqlite": "Löst {vacuum} für SQLite aus. {auto_vacuum} ist bereits aktiviert, defragmentiert aber nicht die Datenbank oder packt Datenbankseiten neu wie der {vacuum}-Befehl.", + "ignoredTLSError": "TLS/SSL-Fehler wurden ignoriert", + "Message format": "Nachrichtenformat", + "Notification Channel": "Benachrichtigungskanal", + "Custom sound to override default notification sound": "Benutzerdefinierter Ton, um den Standardbenachrichtigungston zu überschreiben", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Zeitkritische Benachrichtigungen werden sofort zugestellt, auch wenn sich das Gerät im \"Nicht stören\"-Modus befindet.", + "Debug": "Debug", + "Copy": "Kopieren", + "CopyToClipboardError": "Konnte nicht in die Zwischenablage kopiert werden: {error}", + "CopyToClipboardSuccess": "Kopiert!", + "CurlDebugInfo": "Um den Monitor zu debuggen, kannst du dies entweder in das Terminal deines eigenen Rechners oder in das Terminal der Maschine, auf der Uptime Kuma läuft, einfügen und überprüfen, was du anforderst.{newiline}Bitte beachte Netzwerkunterschiede wie {firewalls}, {dns_resolvers} oder {docker_networks}.", + "firewalls": "Firewalls", + "dns resolvers": "DNS-Resolver", + "docker networks": "Docker-Netzwerke", + "CurlDebugInfoOAuth2CCUnsupported": "Der vollständige OAuth-Client-Credential-Flow wird in {curl} nicht unterstützt.{newline}Bitte besorge dir ein Bearer-Token und übergebe ihn über die {oauth2_bearer}-Option.", + "CurlDebugInfoProxiesUnsupported": "Die Unterstützung von Proxys im oben genannten {curl}-Befehl ist derzeit nicht implementiert.", + "Alphanumerical string and hyphens only": "Nur alphanumerische Zeichen und Bindestriche", + "Correct": "Korrekt", + "Harp": "Harfe", + "Doorbell": "Türklingel", + "Flute": "Flöte", + "Money": "Geld", + "Scifi": "Sci-Fi", + "Elevator": "Aufzug", + "Guitar": "Gitarre", + "Sound": "Ton", + "Time Sensitive (iOS Only)": "Zeitkritisch (nur iOS)", + "From": "Von", + "Can be found on:": "Zu finden unter: {0}", + "The phone number of the recipient in E.164 format.": "Die Telefonnummer des Empfängers im E.164-Format.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Entweder eine Text-Absenderkennung oder eine Telefonnummer im E.164-Format, wenn du Antworten empfangen möchtest.", + "Send rich messages": "Rich Messages senden", + "Fail": "Fehlgeschlagen", + "Reveal": "Anzeigen", + "Bubble": "Bubble", + "Clear": "Leeren", + "Pop": "Pop", + "Arcade": "Arcade", + "rabbitmqNodesRequired": "Bitte lege die Knoten für diesen Monitor fest.", + "RabbitMQ Username": "RabbitMQ-Benutzername", + "RabbitMQ Password": "RabbitMQ-Passwort", + "SendGrid API Key": "SendGrid-API-Schlüssel", + "Separate multiple email addresses with commas": "Mehrere E-Mail-Adressen mit Kommas trennen", + "RabbitMQ Nodes": "RabbitMQ-Management-Knoten", + "rabbitmqNodesDescription": "Gib die URL der RabbitMQ-Management-Knoten inklusive Protokoll und Port ein. Beispiel: {0}", + "rabbitmqNodesInvalid": "Bitte verwende eine vollständige URL (beginnend mit 'http') für RabbitMQ-Knoten.", + "rabbitmqHelpText": "Um den Monitor zu verwenden, musst du das Management-Plugin in deiner RabbitMQ-Installation aktivieren. Weitere Informationen findest du in der {rabitmq_documentation}.", + "aboutSlackUsername": "Ändert den Anzeigenamen des Absenders. Wenn du jemanden erwähnen möchtest, füge es stattdessen im Anzeigenamen ein.", + "templateHostnameOrURL": "Hostname oder URL", + "telegramUseTemplate": "Benutzerdefinierte Nachrichtenvorlage verwenden", + "telegramTemplateFormatDescription": "Telegram erlaubt verschiedene Auszeichnungssprachen für Nachrichten, siehe Telegram {0} für Details.", + "Plain Text": "Klartext", + "templateServiceName": "Dienstname", + "YZJ Webhook URL": "YZJ-Webhook-URL", + "YZJ Robot Token": "YZJ-Robot-Token", + "templateStatus": "Status", + "telegramUseTemplateDescription": "Wenn aktiviert, wird die Nachricht mit einer benutzerdefinierten Vorlage gesendet.", + "Message Template": "Nachrichtenvorlage", + "Template Format": "Vorlagenformat", + "wayToGetWahaApiUrl": "Die URL deiner WAHA-Instanz.", + "wahaSession": "Sitzung", + "wahaChatId": "Chat-ID (Telefonnummer / Kontakt-ID / Gruppen-ID)", + "wayToGetWahaApiKey": "Der API-Key ist der Wert der Umgebungsvariable WHATSAPP_API_KEY, den du zum Ausführen von WAHA verwendet hast.", + "wayToGetWahaSession": "Von dieser Sitzung sendet WAHA Benachrichtigungen an die Chat-ID. Du findest sie im WAHA-Dashboard.", + "wayToWriteWahaChatId": "Die Telefonnummer mit internationaler Vorwahl, aber ohne das Pluszeichen am Anfang ({0}), die Kontakt-ID ({1}) oder die Gruppen-ID ({2}). Benachrichtigungen werden von der WAHA-Sitzung an diese Chat-ID gesendet.", + "telegramServerUrlDescription": "Um Telegram-Bot-API-Einschränkungen zu umgehen oder Zugang in blockierten Gebieten (China, Iran usw.) zu erhalten. Für weitere Informationen klicke {0}. Standard: {1}", + "telegramServerUrl": "(Optional) Server-URL", + "Font Twemoji by Twitter licensed under": "Schriftart Twemoji von Twitter lizenziert unter", + "the smsplanet documentation": "die smsplanet-Dokumentation", + "Phone numbers": "Telefonnummern", + "Sender name": "Absendername", + "smsplanetNeedToApproveName": "Muss im Kundenportal genehmigt werden", + "smsplanetApiToken": "Token für die SMSPlanet-API", + "smsplanetApiDocs": "Detaillierte Informationen zum Erhalt von API-Tokens findest du in der {the_smsplanet_documentation}.", + "defaultFriendlyName": "Neuer Monitor", + "Use HTML for custom E-mail body": "HTML für benutzerdefinierten E-Mail-Inhalt verwenden", + "smseagleApiv1": "APIv1 (für bestehende Projekte und Abwärtskompatibilität)", + "SpugPush Template Code": "Vorlagencode", + "FlashDuty Push URL": "Push-URL", + "pingCountLabel": "Max. Pakete", + "pingNumericDescription": "Wenn aktiviert, werden IP-Adressen statt symbolischer Hostnamen ausgegeben", + "pingPerRequestTimeoutDescription": "Maximale Wartezeit (in Sekunden), bevor ein einzelnes Ping-Paket als verloren gilt", + "smtpHelpText": "\"SMTPS\" testet, ob SMTP/TLS funktioniert; \"TLS ignorieren\" verbindet über Klartext; \"STARTTLS\" verbindet, sendet einen STARTTLS-Befehl und verifiziert das Serverzertifikat. Keine dieser Optionen sendet eine E-Mail.", + "OneChatUserIdOrGroupId": "OneChat-Benutzer-ID oder Gruppen-ID", + "Disable URL in Notification": "URL in Benachrichtigung deaktivieren", + "smseagleGroupV2": "Telefonbuch-Gruppen-ID(s)", + "smseagleContactV2": "Telefonbuch-Kontakt-ID(s)", + "smseagleMsgType": "Nachrichtentyp", + "smseagleMsgSms": "SMS-Nachricht (Standard)", + "smseagleMsgRing": "Klingelanruf", + "smseagleMsgTts": "Text-to-Speech-Anruf", + "smseagleMsgTtsAdvanced": "Text-to-Speech-Anruf (erweitert)", + "smseagleDuration": "Dauer (in Sekunden)", + "smseagleTtsModel": "Text-to-Speech-Modell-ID", + "smseagleApiType": "API-Version", + "smseagleApiv2": "APIv2 (empfohlen für neue Integrationen)", + "smseagleDocs": "Dokumentation oder APIv2-Verfügbarkeit prüfen: {0}", + "smseagleComma": "Mehrere müssen mit Komma getrennt werden", + "FlashDuty Push URL Placeholder": "Von der Alarmintegrations-Seite kopieren", + "pingCountDescription": "Anzahl der zu sendenden Pakete vor dem Stoppen", + "pingNumericLabel": "Numerische Ausgabe", + "pingGlobalTimeoutLabel": "Globales Timeout", + "pingGlobalTimeoutDescription": "Gesamtzeit in Sekunden, bevor Ping stoppt, unabhängig von gesendeten Paketen", + "pingPerRequestTimeoutLabel": "Einzelping-Timeout", + "pingIntervalAdjustedInfo": "Intervall wurde basierend auf Paketanzahl, globalem Timeout und Einzelping-Timeout angepasst", + "Custom URL": "Benutzerdefinierte URL", + "customUrlDescription": "Wird als anklickbare URL anstelle der Monitor-URL verwendet.", + "OneChatAccessToken": "OneChat-Zugriffstoken", + "OneChatBotId": "OneChat-Bot-ID", + "Ip Family": "IP-Familie", + "Happy Eyeballs algorithm": "Happy-Eyeballs-Algorithmus", + "ipFamilyDescriptionAutoSelect": "Verwendet den {happyEyeballs} zur Bestimmung der IP-Familie.", + "Manual": "Manuell", + "ntfyPriorityHelptextPriorityHigherThanDown": "Die reguläre Priorität sollte höher sein als die {0}-Priorität. Priorität {1} ist höher als {0}-Priorität {2}", + "ntfyPriorityDown": "Priorität für Offline-Ereignisse", + "Add Tags": "Tags hinzufügen", + "tagAlreadyOnMonitor": "Dieser Tag (Name und Wert) ist bereits am Monitor vorhanden oder wird gerade hinzugefügt.", + "tagAlreadyStaged": "Dieser Tag (Name und Wert) ist bereits für diesen Stapel vorgemerkt.", + "tagNameExists": "Ein System-Tag mit diesem Namen existiert bereits. Wähle ihn aus der Liste oder verwende einen anderen Namen.", + "Clear Form": "Formular leeren", + "Add Another Tag": "Weiteren Tag hinzufügen", + "Staged Tags for Batch Add": "Vorbereitete Tags für Stapelhinzufügung", + "pause": "Pausieren", + "OAuth Audience": "OAuth-Audience", + "Optional: The audience to request the JWT for": "Optional: Die Audience, für die das JWT angefordert werden soll", + "mqttWebsocketPathInvalid": "Bitte ein gültiges WebSocket-Pfadformat verwenden", + "Path": "Pfad", + "mqttWebSocketPath": "MQTT-WebSocket-Pfad", + "mqttWebsocketPathExplanation": "WebSocket-Pfad für MQTT-über-WebSocket-Verbindungen (z. B. /mqtt)", + "mqttHostnameTip": "Bitte dieses Format verwenden: {hostnameFormat}", + "Template plain text instead of using cards": "Klartext-Vorlage statt Karten verwenden", + "issueWithGoogleChatOnAndroidHelptext": "Dies hilft auch, Upstream-Bugs wie {issuetackerURL} zu umgehen", + "clearAllEventsMsg": "Möchtest du wirklich alle Ereignisse löschen?", + "Events cleared successfully": "Ereignisse erfolgreich gelöscht.", + "No monitors found": "Keine Monitore gefunden.", + "Could not clear events": "{failed}/{total} Ereignisse konnten nicht gelöscht werden", + "Clear All Events": "Alle Ereignisse löschen", + "wayToWriteEvolutionRecipient": "Die Telefonnummer mit internationaler Vorwahl, aber ohne das Pluszeichen am Anfang ({0}), die Kontakt-ID ({1}) oder die Gruppen-ID ({2}).", + "wayToGetEvolutionUrlAndToken": "Du erhältst die API-URL und den Token, indem du in {0} zu deinem gewünschten Kanal gehst", + "evolutionRecipient": "Telefonnummer / Kontakt-ID / Gruppen-ID", + "evolutionInstanceName": "Instanzname", + "brevoApiKey": "Brevo-API-Schlüssel", + "brevoApiHelp": "API-Schlüssel hier erstellen: {0}", + "brevoFromEmail": "Absender-E-Mail", + "brevoFromName": "Absendername", + "brevoLeaveBlankForDefaultName": "Für Standardnamen leer lassen", + "brevoToEmail": "Empfänger-E-Mail", + "brevoCcEmail": "CC-E-Mail", + "brevoBccEmail": "BCC-E-Mail", + "brevoSeparateMultipleEmails": "Mehrere E-Mail-Adressen durch Kommas trennen", + "brevoSubject": "Betreff", + "brevoLeaveBlankForDefaultSubject": "Für Standardbetreff leer lassen", + "Conversation token": "Konversationstoken", + "Bot secret": "Bot-Geheimnis", + "Send DOWN silently": "Offline lautlos senden", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Die Installation eines Nextcloud-Talk-Bots erfordert Administratorzugriff auf den Server.", + "Nextcloud host": "Nextcloud-Host", + "Send UP silently": "Online lautlos senden", + "auto-select": "Automatische Auswahl", + "supportBaleChatID": "Unterstützt Direktchat / Gruppen / Kanal-Chat-ID", + "wayToGetBaleChatID": "Du erhältst deine Chat-ID, indem du eine Nachricht an den Bot sendest und diese URL aufrufst, um die chat_id zu sehen:", + "wayToGetBaleToken": "Du erhältst einen Token von {0}.", + "Mention Mobile List": "Mobilnummernliste erwähnen", + "Mention User List": "Benutzer-ID-Liste erwähnen", + "Dingtalk Mobile List": "Mobilnummernliste", + "Dingtalk User List": "Benutzer-ID-Liste", + "Enter a list of userId": "Liste der Benutzer-IDs eingeben", + "Enter a list of mobile": "Liste der Mobilnummern eingeben", + "Invalid mobile": "Ungültige Mobilnummer [{mobile}]", + "Invalid userId": "Ungültige Benutzer-ID [{userId}]", + "Number of retry attempts if webhook fails": "Anzahl der Wiederholungsversuche (alle 60–180 Sek.) bei Webhook-Fehlern.", + "Maximum Retries": "Maximale Wiederholungen", + "webhookPostMethodDesc": "POST eignet sich gut für die meisten modernen HTTP-Server.", + "descriptionHelpText": "Wird im internen Dashboard angezeigt. Markdown ist erlaubt und wird vor der Anzeige bereinigt (Leerzeichen und Einrückungen bleiben erhalten).", + "HTTP Method": "HTTP-Methode", + "webhookGetMethodDesc": "GET sendet Daten als Query-Parameter und erlaubt keine Body-Konfiguration. Nützlich zum Auslösen von Uptime-Kuma-Push-Monitoren.", + "deleteGroupMsg": "Möchtest du diese Gruppe wirklich löschen?", + "deleteChildrenMonitors": "Auch den direkten untergeordneten Monitor und dessen Untergeordnete löschen, falls vorhanden | Auch alle {count} direkten untergeordneten Monitore und deren Untergeordnete löschen, falls vorhanden", + "Clone Maintenance": "Wartung klonen", + "ariaCloneMaintenance": "Eine Kopie dieses Wartungsplans erstellen", + "ariaEditMaintenance": "Diesen Wartungsplan bearbeiten", + "ariaDeleteMaintenance": "Diesen Wartungsplan löschen", + "ariaPauseMaintenance": "Diesen Wartungsplan pausieren", + "ariaResumeMaintenance": "Diesen Wartungsplan fortsetzen", + "Template ID": "Vorlagen-ID", + "wayToGetClickSMSIRTemplateID": "Deine Vorlage muss ein {uptkumaalert}-Feld enthalten. Du kannst eine neue Vorlage {here} erstellen.", + "Recipient Numbers": "Empfängernummern", + "twilioApiKeyHelptext": "Der API-Key ist optional, aber empfohlen. Du kannst entweder Account-SID und AuthToken von der Twilio-Konsole oder Account-SID zusammen mit API-Key und API-Key-Secret angeben", + "twilioMessagingServiceSID": "Messaging-Service-SID (optional)", + "twilloMessagingServiceSIDHelptext": "Gib hier deine Messaging-Service-SID ein, wenn du den {twillo_messaging_service_help_link} zur Verwaltung von Absendern und Funktionen verwendest", + "showOnlyLastHeartbeat": "Nur letzten Heartbeat anzeigen", + "Softvelum WebSocket signaling protocol": "Softvelum WebSocket Signaling Protocol", + "ignoreSecWebsocketAcceptHeaderDescription": "Erlaubt dem Server, nicht mit dem Sec-WebSocket-Accept-Header zu antworten, wenn das WebSocket-Upgrade erfolgreich ist.", + "Ignore Sec-WebSocket-Accept header": "{0}-Header ignorieren", + "wsSubprotocolDescription": "Gib eine durch Kommas getrennte Liste von Subprotokollen ein. Für weitere Informationen zu Subprotokollen siehe die {documentation}", + "Session Initiation Protocol": "WebSocket-Transport für SIP (Session Initiation Protocol)", + "WebSocket Application Messaging Protocol": "WAMP (WebSocket Application Messaging Protocol)", + "Network API for Notification Channel": "OMA RESTful Network API für Benachrichtigungskanal", + "Web Process Control Protocol": "Web Process Control Protocol (WPCP)", + "Advanced Message Queuing Protocol": "Advanced Message Queuing Protocol (AMQP) 1.0+", + "jsflow": "jsFlow pubsub/queue Protocol", + "Reverse Web Process Control": "Reverse Web Process Control Protocol (RWPCP)", + "Extensible Messaging and Presence Protocol": "WebSocket-Transport für XMPP (Extensible Messaging and Presence Protocol)", + "Smart Home IP": "SHIP – Smart Home IP", + "Miele Cloud Connect Protocol": "Miele Cloud Connect Protocol", + "Push Channel Protocol": "Push Channel Protocol", + "Message Session Relay Protocol": "WebSocket-Transport für MSRP (Message Session Relay Protocol)", + "Binary Floor Control Protocol": "WebSocket-Transport für BFCP (Binary Floor Control Protocol)", + "Softvelum Low Delay Protocol": "Softvelum Low Delay Protocol", + "OPC UA Connection Protocol": "OPC UA Connection Protocol", + "OPC UA JSON Encoding": "OPC UA JSON Encoding", + "Swindon Web Server Protocol": "Swindon Web Server Protocol (JSON-Kodierung)", + "Broadband Forum User Services Platform": "USP (Broadband Forum User Services Platform)", + "Constrained Application Protocol": "Constrained Application Protocol (CoAP)", + "Cobra Real Time Messaging Protocol": "Cobra Real Time Messaging Protocol", + "Declarative Resource Protocol": "Declarative Resource Protocol", + "BACnet Secure Connect Hub Connection": "BACnet Secure Connect Hub Connection", + "BACnet Secure Connect Direct Connection": "BACnet Secure Connect Direct Connection", + "WebSocket Transport for JMAP": "WebSocket-Transport für JMAP (JSON Meta Application Protocol)", + "Notifications Enabled": "Benachrichtigungen aktiviert", + "Allow Notifications": "Benachrichtigungen erlauben", + "Browser not supported": "Browser nicht unterstützt", + "Unable to get permission to notify": "Benachrichtigungsberechtigung konnte nicht eingeholt werden (Anfrage wurde abgelehnt oder ignoriert).", + "Webpush Helptext": "Web-Push funktioniert nur mit SSL-Verbindungen (HTTPS). Bei iOS-Geräten muss die Webseite vorher zum Startbildschirm hinzugefügt werden.", + "certHostnameMismatch": "Der Zertifikats-Hostname stimmt nicht mit der Monitor-URL überein.", + "ITU-T T.140 Real-Time Text": "ITU-T T.140 Real-Time Text", + "Done.best IoT Protocol": "Done.best IoT Protocol", + "Collection Update": "Collection Update WebSocket Subprotocol", + "Text IRC Protocol": "Text IRC Protocol", + "Binary IRC Protocol": "Binary IRC Protocol", + "Penguin Statistics Live Protocol v3": "Penguin Statistics Live Protocol v3 (Protobuf-Kodierung)", + "minimumIntervalWarning": "Intervalle unter 20 Sekunden können zu Leistungseinbußen führen.", + "lowIntervalWarning": "Bist du sicher, dass du das Intervall auf unter 20 Sekunden setzen möchtest? Bei vielen Monitoren kann dies die Performance beeinträchtigen.", + "labelDomainExpiry": "Domain-Ablauf", + "domainExpiryDescription": "Benachrichtigung auslösen, wenn die Domain abläuft in:", + "settingsDomainExpiry": "Domain-Ablauf", + "labelDomainNameExpiryNotification": "Benachrichtigung bei Domain-Ablauf", + "Deselect All": "Alle abwählen", + "Select All": "Alle auswählen", + "resendFromName": "Absendername", + "resendFromEmail": "Absender-E-Mail", + "resendLeaveBlankForDefaultName": "Leer lassen für Standardnamen", + "resendApiKey": "Resend-API-Schlüssel", + "resendApiHelp": "API-Schlüssel hier erstellen: {0}", + "wsCodeDescription": "Für weitere Informationen zu Statuscodes siehe {rfc6455}", + "Subprotocol(s)": "Subprotokoll(e)", + "Duration (Minutes)": "Dauer (Minuten)", + "systemService": "Systemdienst", + "systemServiceName": "Dienstname", + "systemServiceDescription": "Prüft, ob der Systemdienst {service_name} aktiv ist", + "systemServiceDescriptionLinux": "Prüft, ob der Linux-systemd-Dienst {service_name} aktiv ist", + "systemServiceCommandHint": "Verwendeter Befehl: {command}", + "systemServiceExpectedOutput": "Erwartete Ausgabe: \"{0}\"", + "systemServiceDescriptionWindows": "Prüft, ob der Windows-Dienst {service_name} ausgeführt wird", + "resendToEmail": "Empfänger-E-Mail", + "resendSubject": "Betreff", + "imageResetConfirmation": "Bild auf Standard zurückgesetzt", + "Ignore STARTTLS": "STARTTLS ignorieren", + "Use STARTTLS": "STARTTLS verwenden", + "Enter the list of nodes": "Liste der RabbitMQ-Management-Knoten eingeben", + "Press Enter to add node": "Enter drücken, um Node hinzuzufügen", + "SMTP Security": "SMTP-Sicherheit", + "hostnameCannotBeIP": "Der DNS-Hostname darf keine IP-Adresse sein. Meintest du das Resolver-Feld?", + "sipsakPingWarning": "Um den SIP Options Ping-Monitor zu verwenden, musst du Uptime Kuma ohne Docker installieren und den Sipsak-Client auf deinem Server installieren.", + "year": "Jahr | Jahre", + "resendLeaveBlankForDefaultSubject": "Leer lassen für Standardbetreff", + "RSS Title": "RSS-Titel", + "Leave blank to use status page title": "Leer lassen, um den Titel der Statusseite zu verwenden", + "mtls-auth-server-cert-label": "Zertifikat", + "invalidHostnameOrIP": "Ungültiger Hostname oder IP. Der Hostname muss ein gültiger FQDN sein. Wildcards sind nicht erlaubt. Unterstriche oder abschließende Punkte sind möglich.", + "invalidDNSHostname": "Ungültiger Hostname. Der Hostname muss ein gültiger FQDN sein. Kann ein Wildcard sein, Unterstriche enthalten oder mit einem Punkt enden.", + "wildcardOnlyForDNS": "Wildcard-Hostnamen werden nur für DNS-Monitore unterstützt.", + "invalidURL": "Ungültige URL", + "Google": "Google", + "Plausible": "Plausible", + "Matomo": "Matomo", + "Umami": "Umami", + "maxPing": "Max. Ping", + "avgPing": "Ø Ping", + "minPing": "Min. Ping", + "Analytics ID": "Analytics-ID", + "Analytics Type": "Analytics-Typ", + "Analytics Script URL": "Analytics-Script-URL", + "mtls-auth-server-key-placeholder": "Schlüsselinhalt", + "mtls-auth-server-ca-placeholder": "Server-CA", + "mtls-auth-server-ca-label": "CA", + "mtls-auth-server-cert-placeholder": "Zertifikatsinhalt", + "mtls-auth-server-key-label": "Schlüssel", + "Sort options": "Sortieroptionen", + "Clear current filters": "Aktuelle Filter zurücksetzen", + "Region": "Region", + "smscTranslit": "smscTranslit", + "HeadersInvalidFormatBecause": "Die Anfrage-Header sind kein gültiges JSON, weil {error}", + "BodyInvalidFormatBecause": "Der Anfrage-Inhalt ist kein gültiges JSON: {error}", + "notificationUniversal": "Universal", + "notificationChatPlatforms": "Chat-Plattformen", + "notificationPushServices": "Push-Dienste", + "notificationSmsServices": "SMS-Dienste", + "notificationEmail": "E-Mail", + "enableSSL": "SSL/TLS aktivieren", + "mariadbUseSSLHelptext": "Aktivieren, um eine verschlüsselte Verbindung zur Datenbank zu verwenden. Bei den meisten Cloud-Datenbanken erforderlich.", + "mariadbCaCertificateLabel": "CA-Zertifikat", + "mariadbCaCertificateHelptext": "Füge das CA-Zertifikat im PEM-Format ein, um es mit selbstsignierten Zertifikaten zu verwenden. Leer lassen, wenn deine Datenbank ein von einer öffentlichen CA signiertes Zertifikat verwendet.", + "steamApiKeyDescriptionAt": "Um einen Steam-Spieleserver zu überwachen, benötigst du einen Steam-Web-API-Schlüssel. Du kannst deinen API-Schlüssel unter {url} registrieren", + "domain_expiry_unsupported_monitor_type": "Die Ablaufüberwachung wird für diesen Monitortyp nicht unterstützt", + "domain_expiry_unsupported_missing_target": "Für diesen Monitor ist keine gültige Domain oder kein gültiger Hostname konfiguriert", + "Resolver Server(s)": "Resolver-Server", + "saveResponseDescription": "Speichert die HTTP-Antwort und stellt sie als {templateVariable} in Benachrichtigungsvorlagen zur Verfügung", + "responseMaxLength": "Maximale Antwortlänge (Bytes)", + "saveResponseForNotifications": "HTTP-Erfolgsantwort für Benachrichtigungen speichern", + "saveErrorResponseForNotifications": "HTTP-Fehlerantwort für Benachrichtigungen speichern", + "responseMaxLengthDescription": "Maximale Größe der zu speichernden Antwortdaten. 0 für unbegrenzt. Größere Antworten werden gekürzt. Standard: 1024 (1 KB)", + "message": "Nachricht", + "unknownDays": "Unbekannte Tage", + "Monitors": "{n} Monitor | {n} Monitore", + "Sets end time based on start time": "Endzeit basierend auf Startzeit festlegen", + "Please set start time first": "Bitte zuerst Startzeit festlegen", + "You can divide numbers with commas or semicolons": "Du kannst Zahlen mit {comma} oder {semicolon} trennen", + "serwersmsRecipientType": "Empfängertyp", + "serwersmsRecipientTypePhone": "Telefonnummer", + "noMonitorsOrStatusPagesSelectedError": "Wartung kann nicht ohne betroffene Monitore oder Statusseiten erstellt werden", + "noMonitorsSelectedWarning": "Du erstellst eine Wartung ohne betroffene Monitore. Bist du sicher, dass du fortfahren möchtest?", + "json_value": "JSON-Wert", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" ist zu kurz für eine Top-Level-Domain", + "Badge Link Generator Helptext": "Badge-Links sind für alle Monitore verfügbar, die öffentlichen Statusseiten zugewiesen sind. Weitere Informationen findest du in der {documentation}.", + "Badge Link Generator": "{0} Badge-Link-Generator", + "Open Badge Link Generator": "Badge-Link-Generator öffnen", + "notificationOther": "Andere Integrationen", + "OptionalParameters": "Optionale Parameter", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Aufgrund von Providerbeschränkungen optionale Variablen aktivieren (Zustellung nicht garantiert)", + "aliyun-template-requirements-and-parameters": "Die Aliyun-SMS-Vorlage muss folgende Parameter enthalten: {parameters}", + "Suppress Notifications": "Benachrichtigungen unterdrücken", + "versionIs": "Version: {version}", + "logoutCurrentUser": "{username} abmelden", + "createdAt": "Erstellt: {date}", + "lastUpdatedAt": "Zuletzt aktualisiert: {date}", + "frontendVersionIs": "Frontend-Version: {version}", + "dateCreatedAtFromNow": "Erstelldatum: {date} ({fromNow})", + "cronScheduleDescription": "Zeitplan: {description}", + "Examples:": "Beispiele: {0}", + "legacyOctopushEndpoint": "Legacy Octopush-DM (Endpunkt: {url})", + "octopushEndpoint": "octopush (Endpunkt: {url})", + "Actions": "Aktionen", + "noMonitorsResumedMsg": "Keine Monitore fortgesetzt (keine waren inaktiv)", + "noMonitorsPausedMsg": "Keine Monitore pausiert (keine waren aktiv)", + "aliyun-template-optional-parameters": "Optionale Parameter: {parameters}", + "discordSuppressNotificationsHelptext": "Wenn aktiviert, werden Nachrichten im Kanal gepostet, lösen aber keine Push- oder Desktop-Benachrichtigungen für Empfänger aus.", + "bulkDeleteErrorMsg": "Fehler beim Löschen von {n} Monitor | Fehler beim Löschen von {n} Monitoren", + "Only retry if status code check fails": "Nur wiederholen, wenn Statuscode-Prüfung fehlschlägt", + "retryOnlyOnStatusCodeFailureDescription": "Wenn aktiviert, erfolgen Wiederholungen nur bei fehlgeschlagener HTTP-Statuscode-Prüfung (z. B. Server nicht erreichbar). Wenn die Statuscode-Prüfung erfolgreich ist, aber die JSON-Abfrage fehlschlägt, wird der Monitor sofort ohne Wiederholungen als ausgefallen markiert.", + "selectAllMonitorsAria": "Alle Monitore auswählen", + "selectedMonitorCountMsg": "ausgewählt: {n} | ausgewählt: {n}", + "selectMonitorMsg": "Monitore auswählen, um Aktionen auszuführen", + "deselectAllMonitorsAria": "Alle Monitore abwählen", + "pausedMonitorsMsg": "{n} Monitor pausiert | {n} Monitore pausiert", + "deleteMonitorsMsg": "Möchtest du die ausgewählten Monitore wirklich löschen?", + "deletedMonitorsMsg": "{n} Monitor gelöscht | {n} Monitore gelöscht", + "resumedMonitorsMsg": "{n} Monitor fortgesetzt | {n} Monitore fortgesetzt", + "days": "{n} Tag | {n} Tage", + "hours": "{n} Stunde | {n} Stunden", + "minutes": "{n} Minute | {n} Minuten", + "minuteShort": "{n} Min. | {n} Min.", + "years": "{n} Jahr | {n} Jahre", + "lastUpdatedAtFromNow": "Zuletzt aktualisiert: {date} ({fromNow})", + "Certificate Chain:": "Zertifikatskette:", + "checkPriceAt": "{service}-Preise prüfen unter {url}", + "serwersmsRecipientTypeGroup": "Gruppe", + "serwersmsGroupId": "Gruppen-ID", + "ntfyCall": "Telefonanruf", + "ntfyCallHelptext": "Bei Alarm einen Telefonanruf tätigen. \"yes\" eingeben, um die erste verifizierte Nummer zu verwenden, oder eine bestimmte Telefonnummer eingeben (z. B. +49123456789). Erfordert ntfy Pro und eine verifizierte Telefonnummer.", + "halopsa_username_desc": "Benutzername für die Authentifizierung mit dem Halo-PSA-Webhook", + "System Service": "Systemdienst", + "screenshot of the website": "Screenshot der Website", + "username": "Benutzername", + "password": "Passwort", + "halopsa_password_desc": "Passwort für die Authentifizierung mit dem Halo-PSA-Webhook", + "Basic checkbox toggle button group": "Einfache Checkbox-Schaltflächengruppe", + "Sort by certificate expiry": "Nach Zertifikatsablauf sortieren", + "halopsa_setup_step2": "Konfiguriere Runbook-Aktionen zur Verarbeitung von Alarmen (z. B. Ticket erstellen)", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" ist eine IP-Adresse. Die Ablaufüberwachung erfordert einen Domainnamen", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Die Ablaufüberwachung ist für \".{publicSuffix}\" nicht verfügbar, da kein RDAP-Dienst bei der IANA gelistet ist", + "Setup Instructions": "Einrichtungsanleitung", + "halopsa_setup_step1": "Erstelle ein Integrations-Runbook in HaloPSA (Konfiguration → Integrationen → Integrations-Runbooks)", + "Splunk Rest URL": "Splunk-REST-URL", + "Severity": "Schweregrad", + "SSL/TLS": "SSL/TLS", + "TLS Alerts": "TLS-Warnungen", + "Expected TLS Alert": "Erwarteter TLS-Alert", + "None (Successful Connection)": "Keine (erfolgreiche Verbindung)", + "expectedTlsAlertDescription": "Wähle den TLS-Alert aus, den der Server zurückgeben soll. Verwende {code}, um zu prüfen, ob mTLS-Endpunkte Verbindungen ohne Client-Zertifikate ablehnen. Details unter {link}.", + "TLS Alert Spec": "RFC 8446", + "Sort by status": "Nach Status sortieren", + "Message Format": "Nachrichtenformat", + "To Number": "An Nummer", + "PushDeer Server URL": "PushDeer-Server-URL", + "Service Name": "Dienstname", + "GRPC Options": "gRPC-Optionen", + "End": "Ende", + "Endpoint": "Endpunkt", + "Details": "Details", + "domain_expiry_unsupported_is_icann": "Die Domain \"{domain}\" kann nicht auf Ablauf überwacht werden, da ihr öffentlicher Suffix \".{publicSuffix}\" nicht ICANN-verwaltet ist", + "halopsa_setup_step3": "Kopiere die Webhook-URL und füge sie oben ein", + "Screenshot Delay": "Screenshot-Verzögerung (wartet {milliseconds})", + "milliseconds": "{n} Millisekunde | {n} Millisekunden", + "screenshotDelayDescription": "Optional so viele Millisekunden vor dem Screenshot warten. Maximum: {maxValueMs} ms (0,5 × Intervall).", + "screenshotDelayWarning": "Höhere Werte halten den Browser länger offen, was bei vielen gleichzeitigen Monitoren den Speicherverbrauch erhöhen kann.", + "Basic radio toggle button group": "Einfache Radio-Schaltflächengruppe", + "Sort by name": "Nach Name sortieren", + "GrafanaOncallURL": "Grafana-Oncall-URL", + "Never": "Nie", + "Metadata": "Metadaten", + "Sort by uptime": "Nach Verfügbarkeit sortieren", + "Show this Maintenance Message on which Status Pages": "Auf welchen Statusseiten soll diese Wartungsmeldung angezeigt werden", + "passwordTooWeak": "Passwort ist zu schwach. Es sollte Buchstaben und Zahlen enthalten und mindestens 6 Zeichen lang sein.", + "halopsa_setup_step4": "Wähle Basic-Authentifizierung und erstelle Benutzername und Passwort. Trage diese dann oben ein", + "No incidents recorded": "Keine Vorfälle verzeichnet", + "Load More": "Mehr laden", + "Loading...": "Wird geladen …", + "Pin this incident": "Diesen Vorfall anheften", + "Incident description": "Vorfallbeschreibung", + "Past Incidents": "Vergangene Vorfälle", + "snmpV3Username": "SNMPv3-Benutzername", + "WeCom Mentioned Mobile List": "WeCom – Erwähnte Mobilnummern", + "WeCom Mentioned Mobile List Description": "Gib Telefonnummern ein, die erwähnt werden sollen. Trenne mehrere Nummern mit Kommas. Verwende {'@'}all, um alle zu erwähnen.", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "Halo-PSA-Webhook-URL", + "serwersmsGroupIdHelptext": "ID oder Gruppen-IDs aus dem Kundenportal. Diese Kennungen können über die Aktion groups/index heruntergeladen oder aus der Gruppenbearbeitung im Kundenportal kopiert werden.", + "notificationIncidentManagement": "Vorfallsmanagement", + "Incident title": "Name des Vorfalls", + "Edit Incident": "Vorfall bearbeiten", + "Pinned incidents are shown prominently on the status page": "Angeheftete Vorfälle werden prominent auf der Statusseite angezeigt", + "Resolve": "Beheben", + "Resolved": "Behoben", + "Google Apps Script Webhook URL": "Google-Apps-Script-Webhook-URL", + "Deploy a Google Apps Script as a web app and paste the URL here": "Stelle ein Google Apps Script als Web-App bereit und füge die URL hier ein", + "Quick Setup Guide": "Schnellinstallationsanleitung", + "Open your Google Spreadsheet": "Öffne deine Google-Tabelle", + "Go to Extensions → Apps Script": "Gehe zu Erweiterungen → Apps Script", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Setze \"Ausführen als: Ich\" und \"Wer hat Zugriff: Jeder\"", + "Copy the web app URL and paste it above": "Kopiere die Webanwendungs URL und füge sie oben ein", + "Failed to copy to clipboard": "Kopieren in die Zwischenablage fehlgeschlagen", + "Expand All Groups": "Alle Gruppen aufklappen", + "Paste the script code (see below)": "Skriptcode einfügen (siehe unten)", + "Click Deploy → New deployment → Web app": "Klicke Deploy → Neues deployment → Webanwendung", + "Google Apps Script Code": "Google-Apps-Script-Code", + "Copy to Clipboard": "In Zwischenablage kopieren", + "mariadbSocketPathDetectedHelptext": "Verbindung zur Datenbank wie in der Umgebungsvariable {0} spezifiziert.", + "Copied to clipboard!": "In die Zwischenablage kopiert!", + "halopsa_webhook_url_desc": "Gib die Webhook-URL aus deinem Halo PSA-Integrations-Runbook ein (Konfiguration > Integrationen > Benutzerdefinierte Integrationen > Integrations-Runbooks). Wähle beim Erstellen des Webhooks die Option \"Kann nur von Halo und von einem öffentlichen Endpunkt aus gestartet werden\".", + "disableSTARTTLSDescription": "Aktiviere diese Option für SMTP-Server, die STARTTLS nicht unterstützen. E-Mails werden dann über eine unverschlüsselte Verbindung gesendet.", + "Disable STARTTLS": "STARTTLS deaktivieren", + "playground": "Playground", + "Collapse All Groups": "Alle Gruppen einklappen", + "Check Type": "Prüfungstyp", + "notificationHomeAutomation": "Heimautomatisierung", + "Incident not found or access denied": "Vorfall nicht gefunden oder Zugriff verweigert", + "deleteIncidentMsg": "Möchtest du diesen Vorfall wirklich löschen?", + "slug is not found": "Slug nicht gefunden", + "Please input content": "Bitte Inhalt eingeben", + "Please input title": "Bitte Titel eingeben", + "example": "Beispiel", + "Cloud ID": "Cloud-ID", + "API Token": "API-Token", + "aboutJiraCloudId": "Weitere Informationen zur Jira Cloud-ID: {0}", + "Globalping - Access global monitoring probes": "Globalping – Zugriff auf globale Monitoring-Sonden", + "GlobalpingDescription": "Globalping bietet Zugriff auf Tausende von Community-gehosteten Sonden für Netzwerktests und -messungen. Für anonyme Benutzer gilt ein Limit von 250 Tests pro Stunde. Um das Limit auf 500 pro Stunde zu verdoppeln, speichere deinen Token in den {accountSettings}.", + "Globalping API Token": "Globalping-API-Token", + "globalpingApiTokenDescription": "Hol dir deinen Globalping-API-Token unter {0}.", + "GlobalpingHostname": "Ein öffentlich erreichbares Messziel. Typischerweise ein Hostname oder eine IPv4/IPv6-Adresse, je nach Messtyp.", + "GlobalpingLocation": "Das Standortfeld akzeptiert Kontinente, Länder, Regionen, Städte, ASNs, ISPs oder Cloud-Regionen. Du kannst Filter mit {plus} kombinieren (z. B. {amazonPlusGermany} oder {comcastPlusCalifornia}). Wenn Latenz wichtig ist, grenze den Standort auf eine kleine Region ein, um Spitzen zu vermeiden. {fullDocs}.", + "GlobalpingLocationDocs": "Vollständige Dokumentation zur Standorteingabe", + "GlobalpingIpFamilyInfo": "Die zu verwendende IP-Version. Nur erlaubt, wenn das Ziel ein Hostname ist.", + "GlobalpingResolverInfo": "IPv4/IPv6-Adresse oder ein vollqualifizierter Domainname (FQDN). Standardmäßig der lokale Netzwerk-Resolver der Sonde. Du kannst den Resolver-Server jederzeit ändern.", + "account settings": "Kontoeinstellungen", + "Check for": "Prüfen auf", + "templateAvailableVariables": "Verfügbare Variablen", + "Result": "Ergebnis", + "See Jira Cloud Docs": "Siehe Jira Cloud-Dokumentation", + "see Jira Cloud Docs": "siehe Jira Cloud-Dokumentation", + "Jira Service Management": "Jira Service Management", + "ntfyUseTemplate": "Benachrichtigungsvorlagen anpassen", + "ntfyUseTemplateDescription": "Aktivieren, um Benachrichtigungstitel und -nachrichten mit LiquidJS-Templating anzupassen", + "ntfyCustomTitle": "Benutzerdefinierte Titelvorlage", + "ntfyCustomMessage": "Benutzerdefinierte Nachrichtenvorlage", + "ntfyNotificationTemplateFallback": "Leer lassen, um das Standard-Uptime-Kuma-Format zu verwenden", + "Protocol": "Protokoll", + "Location": "Standort", + "Monitor Subtype": "Monitor-Untertyp", + "discordMessageFormat": "Nachrichtenformat", + "discordMessageFormatNormal": "Normal (Rich Embeds)", + "discordUseMessageTemplate": "Benutzerdefinierte Nachrichtenvorlage verwenden", + "discordMessageTemplate": "Nachrichtenvorlage", + "slackIncludeGroupName": "Monitorgruppenname einbeziehen", + "slackUseTemplate": "Benutzerdefinierte Nachrichtenvorlage verwenden", + "slackIncludeGroupNameDescription": "Wenn aktiviert, wird der Monitorgruppenpfad in Benachrichtigungen eingefügt, um Monitore mit gleichem Namen in verschiedenen Gruppen zu unterscheiden.", + "slackUseTemplateDescription": "Wenn aktiviert, wird die Nachricht mit einer benutzerdefinierten Vorlage gesendet. Du kannst Liquid-Templating verwenden, um Monitorgruppen-Informationen über monitorJSON.path oder monitorJSON.pathName einzubinden.", + "discordMessageFormatMinimalist": "Minimalistisch (Kurzstatus)", + "discordMessageFormatCustom": "Benutzerdefinierte Vorlage", + "discordUseMessageTemplateDescription": "Wenn aktiviert, wird die Nachricht mit einer benutzerdefinierten Vorlage (LiquidJS) gesendet. Leer lassen, um das Standard-Format von Uptime Kuma zu verwenden.", + "Webhook Payload Fields": "Webhook-Payload-Felder", + "halopsa_payload_desc": "Die folgenden Felder werden an deinen Halo-PSA-Webhook gesendet:", + "halopsa_field_title": "Alarmtitel (immer „Uptime Kuma Alert\")", + "halopsa_field_status": "Monitorstatus: UP, DOWN, NOTIFICATION oder UNKNOWN", + "halopsa_field_monitor": "Name des Monitors", + "halopsa_field_monitor_id": "Eindeutige Monitor-ID (null bei Testbenachrichtigungen) – Verwende diese, um Alarme Tickets zuzuordnen", + "halopsa_field_message": "Vollständige Alarmnachricht mit Status und Details", + "halopsa_field_timestamp": "Ereignis-Zeitstempel im ISO-8601-Format", + "halopsa_field_uptime_kuma_version": "Uptime-Kuma-Versionsnummer", + "halopsa_id_usage_hint": "💡 Tipp: Verwende monitor_id, um Alarme zuverlässig Tickets zuzuordnen, und heartbeat_id, um den Ereignisverlauf nachzuverfolgen", + "halopsa_setup_step5": "Runbook so konfigurieren, dass monitor_id zur Zuordnung von Alarmen zu bestehenden Tickets verwendet wird", + "matrixUseTemplate": "Benutzerdefinierte Nachrichtenvorlage verwenden", + "matrixUseTemplateDescription": "Wenn aktiviert, wird die Nachricht unter Verwendung einer benutzerdefinierten Vorlage gesendet.", + "teamsEnableTagsDescription": "Wenn diese Option aktiviert ist, enthält die Nachricht die Monitor-Tags.", + "teamsEnableTags": "Mit Tags", + "teltonikaUrl": "Deine Teltonika Geräte-URL", + "teltonikaUnsafeTls": "Zertifikatsvalidierung ignorieren", + "teltonikaUrlHelptext": "Die URL muss als vollständiger Origin definiert sein (z. B. {0} oder {1}).", + "teltonikaUsername": "API Benutzername", + "teltonikaUsernameHelptext": "Empfehlung: Erstelle ein separates Konto, das nur für den Versand von SMS-Nachichten berechtigt ist, und gib den Benutzernamen hier ein", + "teltonikaPassword": "API Passwort", + "teltonikaPasswordHelptext": "Du kannst das Passwort des API-Benutzers im Teltonika-Router festlegen, z. B. {0}", + "teltonikaModem": "Modem-ID", + "teltonikaPhoneNumber": "Telefonnummer", + "teltonikaPhoneNumberHelptext": "Die Nummer muss im internationalen Format ({0}, {1}) vorliegen. Nur eine Nummer ist erlaubt.", + "Teltonika SMS Gateway": "Teltonika SMS-Gateway", + "teltonikaVersionWarning": "Dieser Benachrichtigungsanbieter erfordert, dass auf deinem Teltonika-Gerät RMS-Version 7.14.0 oder höher installiert ist.", + "teltonikaUnsafeTlsDescription": "Die Deaktivierung der TLS Zertifikatsprüfung setzt dich möglichen On-Path-Angriffen (Man in the Middle-Attacken) aus, welche zu Datenlecks und Systemübernahmen führen können. Deaktiviere diese Prüfung nur, wenn du den Angriffsvektor akzeptierst. Wir empfehlen die Verwendung von Let's Encrypt mit automatischer Verlängerung.", + "teltonikaModemHelptext": "Die ID des SMS-Modems muss das Format {0} haben. Weitere Informationen kannst du unter https://developers.teltonika-networks.com/reference/ finden.", + "RecordMatch": "Eintragswert-Übereinstimmung", + "RegexMatch": "Gib einen regulären Ausdruck ein, der mit dem Eintragswert übereinstimmt", + "GlobalpingMonitorDescription": "Globalping bietet Zugriff auf Tausende von Community-gehosteten Sonden, um Netzwerktests und -messungen durchzuführen. Für alle anonymen Benutzer gilt eine Begrenzung von 250 Tests pro Stunde. Um die Begrenzung auf 500 pro Stunde zu verdoppeln, speichere bitte dein Token in {accountSettings}. Weitere Informationen findest du in den {docs}.", + "certificateExpiryNotificationHelp": "Die Anzahl der Tage im Voraus kann in den Einstellungen konfiguriert werden.", + "domainExpiryNotificationHelp": "Die Anzahl der Tage im Voraus kann in Einstellungen konfiguriert werden.", + "signalUseTemplate": "Benutzerdefinierte Nachrichtenvorlage verwenden", + "signalUseTemplateDescription": "Wenn diese Option aktiviert ist, wird die Nachricht mit einer benutzerdefinierten Vorlage versendet. Du kannst Liquid-Templating verwenden, um das Benachrichtigungsformat anzupassen.", + "monitorTypeGameServer": "Spieleserver", + "monitorTypeDatabase": "Datenbankmonitor-Typ", + "monitorTypeSpecial": "Speziell", + "360messengerUseTemplate": "Benutze eine Benutzerdefinierte Nachrichten Vorlage", + "360messengerTemplate": "360messenger Nachrichten Vorlage", + "360messengerGroupList": "WhatsApp Gruppen", + "360messengerSelectGroupList": "Wähle eine Gruppe zum hinzufügen aus", + "360messengerCustomMessageTemplate": "Benutzerdefinierte Nachrichten Vorlage", + "360messengerMessageTemplate": "Nachrichtenvorlage", + "360messengerErrorNoApiKey": "Bitte gebe deinen 360messenger API-Schlüssel zuerst ein.", + "360messengerAuthToken": "360messenger API-Schlüssel", + "360messengerGroupId": "360messenger Gruppen ID", + "fluxerMessageFormatCustom": "Benutzerdefinierte Vorlage", + "fluxerMessageFormatMinimalist": "Minimalistisch (kurzer Status)", + "Notification Action": "Benachrichtigungsaktion", + "homeAssistantNotificationActionHelptext": "Eine Liste an Benachrichtigungsaktionen können in Home Assistant unter „Einstellungen > Entwicklerwerkzeuge > Aktionen“ gefunden werden. Suche nach „notify“, um deine Aktionen zu finden. Gib nur den Teil nach „notify.“ ein, z. B. für die Aktion „notify.mobile_app_xyz“ gib „mobile_app_xyz“ ein. Bei integrierten mobilen Benachrichtigungen suche nach „Send a notification via mobile_app_xyz“ (nicht „Send a notification“).", + "oracledbConnectionString": "Oracle Datenbank: {connectionString}", + "Connection Timeout": "Zeitüberschreitung bei der Verbindung", + "Bearer Token": "Bearer Token", + "pingPacketSizeWarning": "Eine Paketgröße unter 16 führt unter Linux nicht zur Ermittlung von Round-Trip-Zeiten", + "maxUseTemplate": "Benutzerdefinierte Nachrichtenvorlage verwenden", + "maxUseTemplateDescription": "Wenn diese Option aktiviert ist, wird die Nachricht anhand einer benutzerdefinierten Vorlage versendet.", + "API Version": "API-Version", + "Peer ID": "Peer-ID", + "wxPusherDownloadApp": "App herunterladen", + "Channel": "Kanal", + "maxMessenger": "MAX Messenger", + "maxApiUrlDescription": "Basis-API-URL für MAX Messenger. Standard: https://platform-api.max.ru", + "Client Key": "Client Key", + "wayToGetMaxToken": "Sie können einen MAX-Bot-Token und weitere Details von {0} erhalten.", + "Indigo URL": "Indigo URL" +} diff --git a/src/lang/el-GR.json b/src/lang/el-GR.json new file mode 100644 index 0000000..907282f --- /dev/null +++ b/src/lang/el-GR.json @@ -0,0 +1,821 @@ +{ + "languageName": "Ελληνικά", + "checkEverySecond": "Έλεγχος κάθε {0} δευτερόλεπτα", + "retryCheckEverySecond": "Επανάληψη κάθε {0} δευτερόλεπτα", + "resendEveryXTimes": "Επανάληψη αποστολής ειδοποίησης κάθε {0} φορές", + "resendDisabled": "Η επανάληψη αποστολής ειδοποίησης είναι απενεργοποιημένη", + "retriesDescription": "Μέγιστες επαναλήψεις προτού η υπηρεσία επισημανθεί ως κατω και σταλεί μια ειδοποίηση", + "ignoreTLSError": "Παράβλεψη σφαλμάτων TLS/SSL για ιστότοπους HTTPS", + "upsideDownModeDescription": "Αναποδογυρίστε την κατάσταση. Εάν η υπηρεσία είναι προσβάσιμη, είναι ΚΑΤΩ.", + "maxRedirectDescription": "Μέγιστος αριθμός redirect που θα ακολουθήσουν. Ρυθμίστε το 0 για να απενεργοποιήσετε τα redirect.", + "acceptedStatusCodesDescription": "Επιλέξτε κωδικούς κατάστασης που θεωρούνται επιτυχή.", + "passwordNotMatchMsg": "Ο κωδικός δεν ταιριάζει.", + "notificationDescription": "Οι ειδοποιήσεις πρέπει να εκχωρηθούν σε μια παρακολούθηση για να λειτουργήσουν.", + "keywordDescription": "Αναζήτηση λέξης-κλειδιού σε απλή απόκριση HTML ή JSON. Η αναζήτηση είναι διάκριση πεζών-κεφαλαίων.", + "pauseDashboardHome": "Παύση", + "deleteMonitorMsg": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν την παρακολούθηση;", + "deleteNotificationMsg": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν την ειδοποίηση για όλες τις παρακολούθησης?", + "dnsPortDescription": "Θύρα διακομιστή DNS. Προεπιλογή σε 53. Μπορείτε να αλλάξετε τη θύρα ανά πάσα στιγμή.", + "resolverserverDescription": "Το Cloudflare είναι ο προεπιλεγμένος διακομιστής. Μπορείτε να αλλάξετε τον διακομιστή επίλυσης ανά πάσα στιγμήhe default server. You can change the resolver server anytime.", + "rrtypeDescription": "Επιλέξτε τον τύπο RR που θέλετε να παρακολουθήσετε", + "pauseMonitorMsg": "Είστε βέβαιοι ότι θέλετε να κάνετε παύση;", + "enableDefaultNotificationDescription": "Αυτή η ειδοποίηση θα είναι ενεργοποιημένη από προεπιλογή για νέες παρακολούθησης. Μπορείτε ακόμα να απενεργοποιήσετε την ειδοποίηση ξεχωριστά για κάθε παρακολούθηση.", + "clearEventsMsg": "Είστε βέβαιοι ότι θέλετε να διαγράψετε όλα τα συμβάντα για αυτήν την παρακολούθηση;", + "clearHeartbeatsMsg": "Είστε βέβαιοι ότι θέλετε να διαγράψετε όλους τους καρδιακούς παλμούς για αυτήν την παρακολούθηση;", + "confirmClearStatisticsMsg": "Είστε βέβαιοι ότι θέλετε να διαγράψετε ΟΛΑ τα στατιστικά στοιχεία;?", + "importHandleDescription": "Επιλέξτε «Παράλειψη υπάρχοντος» εάν θέλετε να παραλείψετε κάθε παρακολούθηση ή ειδοποίηση με το ίδιο όνομα. Το 'Overwrite' θα διαγράψει κάθε υπάρχουσα παρακολούθηση και ειδοποίηση.", + "confirmImportMsg": "Είστε βέβαιοι ότι θέλετε να εισαγάγετε το αντίγραφο ασφαλείας; Επαληθεύστε ότι έχετε επιλέξει τη σωστή επιλογή.", + "twoFAVerifyLabel": "Εισαγάγετε το 2FA κωδικό για να επαληθεύσετε:", + "tokenValidSettingsMsg": "Ο κωδικός 2FA είναι έγκυρος! Τώρα μπορείτε να αποθηκεύσετε τις ρυθμίσεις 2FA.", + "confirmEnableTwoFAMsg": "Είστε βέβαιοι ότι θέλετε να ενεργοποιήσετε το 2FA;", + "confirmDisableTwoFAMsg": "Είστε βέβαιοι ότι θέλετε να απενεργοποιήσετε το 2FA;", + "Settings": "Ρυθμίσεις", + "Dashboard": "Πίνακας", + "New Update": "Νέα ενημέρωση", + "Language": "Γλώσσα", + "Appearance": "Εμφάνιση", + "Theme": "Θέμα", + "General": "Γενικά", + "Primary Base URL": "Κύρια βασική διεύθυνση URL", + "Version": "Έκδοση", + "Check Update On GitHub": "Ελέγξτε για Ενημέρωση στο GitHub", + "List": "Λίστα", + "Add": "Προσθήκη", + "Add New Monitor": "Προσθήκη νέας παρακολούθησης", + "Quick Stats": "Γρήγορα στατιστικά", + "Up": "Πάνω", + "Down": "Κάτω", + "Pending": "Εκκρεμεί", + "Unknown": "Άγνωστο", + "Pause": "Παύση", + "Name": "Ονομα", + "Status": "Κατάσταση", + "DateTime": "ΗμερομηνίαΏρα", + "Message": "Μήνυμα", + "No important events": "Δεν υπάρχουν σημαντικά γεγονότα", + "Resume": "Συνέχιση", + "Edit": "Επεξεργασία", + "Delete": "Διαγράφη", + "Current": "Τωρινό", + "Uptime": "Χρόνος λειτουργίας", + "Cert Exp.": "Λήξη Πιστοπ.", + "day": "ημέρα | ημέρες", + "-day": "-ημέρα", + "hour": "ώρα", + "-hour": "-ώρα", + "Response": "Απάντηση", + "Ping": "Ping", + "Monitor Type": "Τύπος παρακολούθησης", + "Keyword": "Λέξη-κλειδί", + "Friendly Name": "Φιλικό όνομα", + "URL": "URL", + "Hostname": "Hostname", + "Port": "Port", + "Heartbeat Interval": "Διάστημα καρδιακών παλμών", + "Retries": "Επαναλήψεις", + "Heartbeat Retry Interval": "Διάστημα επανάληψης παλμών καρδιάς", + "Resend Notification if Down X times consecutively": "Αποστολή νέας ειδοποίησης εάν κατω X φορές κατά συνέχεια", + "Advanced": "Προχωρημένα", + "Upside Down Mode": "Ανάποδη λειτουργία", + "Max. Redirects": "Μέγιστη. Ανακατευθύνσεις", + "Accepted Status Codes": "Αποδεκτοί Κωδικοί Κατάστασης", + "Push URL": "Push URL", + "needPushEvery": "Θα πρέπει να καλείτε αυτήν τη διεύθυνση URL κάθε {0} δευτερόλεπτα.", + "pushOptionalParams": "Προαιρετικές παράμετροι: {0}", + "Save": "Αποθηκεύση", + "Notifications": "Ειδοποιήσεις", + "Not available, please setup.": "Μη διαθέσιμο, παρακαλώ αρχικοποιήστε.", + "Setup Notification": "Αρχικοποίηση ειδοποίησης", + "Light": "Φωτεινό", + "Dark": "Σκοτεινό", + "Auto": "Αυτόματο", + "Theme - Heartbeat Bar": "Θέμα - Μπάρα καρδιακών παλμών", + "Normal": "Κανονικό", + "Bottom": "Κάτω μέρος", + "None": "Τίποτα", + "Timezone": "Ζώνη ώρας", + "Search Engine Visibility": "Ορατότητα μηχανών αναζήτησης", + "Allow indexing": "Να επιτρέπεται η ευρετηρίαση", + "Discourage search engines from indexing site": "Αποθαρρύνετε τις μηχανές αναζήτησης από την ευρετηρίαση ιστότοπου", + "Change Password": "Αλλαγή κωδικού πρόσβασης", + "Current Password": "Τρέχων κωδικός πρόσβασης", + "New Password": "Νέος κωδικός πρόσβασης", + "Repeat New Password": "Επαναλάβετε τον νέο κωδικό πρόσβασης", + "Update Password": "Ενημέρωση κωδικού πρόσβασης", + "Disable Auth": "Απενεργοποίηση ελέγχου ταυτότητας", + "Enable Auth": "Ενεργοποίηση ελέγχου ταυτότητας", + "disableauth.message1": "Είστε βέβαιοι ότι θέλετε να {disableAuth};", + "disable authentication": "απενεργοποιήσετε τον έλεγχο ταυτότητας", + "disableauth.message2": "Έχει σχεδιαστεί για σενάρια {intendThirdPartyAuth} μπροστά από το Uptime Kuma, όπως το Cloudflare Access, Authelia ή άλλους μηχανισμούς ελέγχου ταυτότητας.", + "where you intend to implement third-party authentication": "όπου σκοπεύετε να εφαρμόσετε έλεγχο ταυτότητας τρίτου μέρους", + "Please use this option carefully!": "Χρησιμοποιήστε αυτή την επιλογή προσεκτικά!", + "Logout": "Αποσύνδεση", + "Leave": "Φύγετε", + "I understand, please disable": "Καταλαβαίνω, απενεργοποιήστε", + "Confirm": "Επιβεβαίωση", + "Yes": "Ναί", + "No": "Οχι", + "Username": "Όνομα χρήστη", + "Password": "Κωδικός πρόσβασης", + "Remember me": "Θυμήσου με", + "Login": "Σύνδεση", + "No Monitors, please": "Δεν υπάρχουν παρακολούθησης παρακαλώ", + "add one": "προσθέστε ένα", + "Notification Type": "Είδος ειδοποίησης", + "Email": "Email", + "Test": "Δοκιμή", + "Certificate Info": "Πληροφορίες πιστοποιητικού", + "Resolver Server": "Διακομιστής επίλυσης", + "Resource Record Type": "Τύπος εγγραφής πόρων", + "Last Result": "Τελευταίο Αποτέλεσμα", + "Create your admin account": "Δημιουργήστε τον λογαριασμό διαχειριστή σας", + "Repeat Password": "Επαναλάβετε τον κωδικό πρόσβασης", + "Import Backup": "Εισαγωγή αντιγράφων ασφαλείας", + "Export Backup": "Εξαγωγή αντιγράφων ασφαλείας", + "Export": "Εξαγωγή", + "Import": "Εισαγωγή", + "respTime": "Χρόν. Aπό (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Προεπιλογή ενεργοποιημένη", + "Apply on all existing monitors": "Εφαρμόστε σε όλες τις υπάρχουσες παρακολούθησης", + "Create": "Δημιουργία", + "Clear Data": "Καθαρισμός δεδομένων", + "Events": "Γεγονότα", + "Heartbeats": "Παλμοι καρδιας", + "Auto Get": "Αυτόματη λήψη", + "backupDescription": "Μπορείτε να δημιουργήσετε αντίγραφα ασφαλείας γία ολλες της παρακολούθησης και ειδοποιήσης σε ένα αρχείο JSON.", + "backupDescription2": "Σημείωση: δεν περιλαμβάνονται δεδομένα ιστορικού και συμβάντων.", + "backupDescription3": "Στο αρχείο εξαγωγής περιλαμβάνονται ευαίσθητα δεδομένα, όπως token ειδοποιήσεων. Aποθηκεύστε την εξαγωγή με ασφάλεια.", + "alertNoFile": "Επιλέξτε ένα αρχείο για εισαγωγή.", + "alertWrongFileType": "Επιλέξτε ένα αρχείο JSON.", + "Clear all statistics": "Εκκαθάριση όλων των στατιστικών", + "Skip existing": "Παράβλεψη υπάρχοντος", + "Overwrite": "Αντικατάσταση", + "Options": "Επιλογές", + "Keep both": "Κράτα και τα δύο", + "Verify Token": "Επαλήθευση Token", + "Setup 2FA": "Ρύθμιση 2FA", + "Enable 2FA": "Ενεργοποίηση 2FA", + "Disable 2FA": "Απενεργοποίηση 2FA", + "2FA Settings": "Ρυθμίσεις 2FA", + "Two Factor Authentication": "Έλεγχος ταυτότητας δύο παραγόντων", + "Active": "Ενεργός", + "Inactive": "Ανενεργό", + "Token": "Token", + "Show URI": "Εμφάνιση URI", + "Tags": "Ετικέτες", + "Add New below or Select...": "Προσθήκη νέου παρακάτω ή Επιλέξτε…", + "Tag with this name already exist.": "Υπάρχει ήδη η ετικέτα με αυτό το όνομα.", + "Tag with this value already exist.": "Υπάρχει ήδη ετικέτα με αυτό το value.", + "color": "χρώμα", + "value (optional)": "τιμή (προαιρετικό)", + "Gray": "Γκρί", + "Red": "Κόκκινο", + "Orange": "Πορτοκάλι", + "Green": "Πράσινο", + "Blue": "Μπλε", + "Indigo": "Indigo", + "Purple": "Μωβ", + "Pink": "Ροζ", + "Search...": "Αναζήτηση…", + "Avg. Ping": "Μέσo.Ping", + "Avg. Response": "Μέσo. Aπάντηση", + "Entry Page": "Σελίδα εισαγωγής", + "statusPageNothing": "Δεν υπάρχει τίποτα εδώ, προσθέστε μια ομάδα ή μια παρακολούθηση.", + "No Services": "Δεν υπάρχουν υπηρεσίες", + "All Systems Operational": "Όλα τα συστήματα λειτουργούν", + "Partially Degraded Service": "Μερικώς υποβαθμισμένη υπηρεσία", + "Degraded Service": "Υποβαθμισμένη υπηρεσία", + "Add Group": "Προσθήκη γρουπ", + "Add a monitor": "Προσθήκη παρακολούθησης", + "Edit Status Page": "Επεξεργασία σελίδας κατάστασης", + "Go to Dashboard": "Μεταβείτε στον Πίνακα ελέγχου", + "Status Page": "Σελίδα κατάστασης", + "Status Pages": "Σελίδες κατάστασης", + "defaultNotificationName": "Η ειδοποίηση μου {notification} ({number})", + "here": "εδώ", + "Required": "Απαιτείται", + "telegram": "Telegram", + "ZohoCliq": "ZohoCliq", + "Bot Token": "Διακριτικό Bot", + "wayToGetTelegramToken": "Μπορείτε να πάρετε ένα διακριτικό από {0}.", + "Chat ID": "Chat ID", + "supportTelegramChatID": "Support Direct Chat / Group / Channel's Chat ID", + "wayToGetTelegramChatID": "Μπορείτε να λάβετε το αναγνωριστικό συνομιλίας σας στέλνοντας ένα μήνυμα στο bot και μεταβαίνοντας σε αυτήν τη διεύθυνση URL για να προβάλετε το chat_id:", + "YOUR BOT TOKEN HERE": "ΤΟ BOT ΣΑΣ ΔΙΑΚΡΙΤΙΚΌ ΕΔΩ", + "chatIDNotFound": "Το Chat ID δεν βρέθηκε. Στείλτε πρώτα ένα μήνυμα σε αυτό το bot", + "webhook": "Webhook", + "Post URL": "Post URL", + "Content Type": "Τύπος περιεχομένου", + "webhookJsonDesc": "{0} είναι καλό για οποιονδήποτε σύγχρονο διακομιστή HTTP όπως το Express.js", + "webhookFormDataDesc": "{multipart} είναι καλό για την PHP. Το JSON θα πρέπει να αναλυθεί με {decodeFunction}", + "smtp": "Email (SMTP)", + "secureOptionNone": "None / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Παράβλεψη σφάλματος TLS", + "From Email": "Από Email", + "emailCustomSubject": "Προσαρμοσμένο θέμα", + "To Email": "Προς Email", + "smtpCC": "CC", + "smtpBCC": "BCC", + "discord": "Discord", + "Discord Webhook URL": "Discord Webhook URL", + "wayToGetDiscordURL": "Μπορείτε να το αποκτήσετε μεταβαίνοντας στις Ρυθμίσεις διακομιστή -> Ενσωματώσεις -> Προβολή των Webhooks -> Νέο Webhook", + "Bot Display Name": "Εμφανιζόμενο όνομα bot", + "Prefix Custom Message": "Προσαρμοσμένο μήνυμα", + "Hello @everyone is...": "Γεια {'@'}everyone είναι…", + "teams": "Microsoft Teams", + "Webhook URL": "Webhook URL", + "wayToGetTeamsURL": "Μπορείτε να μάθετε πώς να δημιουργείτε μια διεύθυνση URL webhook {0}.", + "wayToGetZohoCliqURL": "Μπορείτε να μάθετε πώς να δημιουργείτε μια διεύθυνση URL webhook {0}.", + "signal": "Signal", + "Number": "Αριθμός", + "Recipients": "Αποδέκτες", + "needSignalAPI": "Πρέπει να έχετε ένα signal client με REST API..", + "wayToCheckSignalURL": "Μπορείτε να ελέγξετε αυτό το URL για να δείτε πώς να ρυθμίσετε ένα:", + "signalImportant": "ΣΗΜΑΝΤΙΚΟ: Δεν μπορείτε να συνδυάσετε ομάδες και αριθμούς στους παραλήπτες!", + "gotify": "Gotify", + "Application Token": "Token εφαρμογής", + "Server URL": "URL διακομιστή", + "Priority": "Προτεραιότητα", + "slack": "Slack", + "Icon Emoji": "Εικονίδιο Emoji", + "Channel Name": "Όνομα καναλιού", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Περισσότερες πληροφορίες σχετικά με τα Webhooks στο: {0}", + "aboutChannelName": "Εισαγάγετε το όνομα του καναλιού στο {0} Όνομα καναλιού εάν θέλετε να παρακάμψετε το κανάλι Webhook. Π.χ.: #other-channel", + "aboutKumaURL": "Εάν αφήσετε κενό το πεδίο URL Uptime Kuma, θα είναι προεπιλεγμένο στη σελίδα Project GitHub..", + "emojiCheatSheet": "Φύλλο εξαπάτησης emoji: {0}", + "rocket.chat": "Rocket.Chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "Push by Techulus", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Support 50+ Notification services)", + "GoogleChat": "Google Chat (Google Workspace only)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "Κλειδί χρήστη", + "Device": "Συσκευή", + "Message Title": "Τίτλος μηνύματος", + "Notification Sound": "Ήχος ειδοποίησης", + "More info on:": "Περισσότερες πληροφορίες στο: {0}", + "pushoverDesc1": "Η προτεραιότητα έκτακτης ανάγκης (2) έχει προεπιλεγμένο χρονικό όριο 30 δευτερολέπτων μεταξύ των επαναλήψεων και θα λήξει μετά από 1 ώρα.", + "pushoverDesc2": "Εάν θέλετε να στέλνετε ειδοποιήσεις σε διαφορετικές συσκευές, συμπληρώστε το πεδίο Συσκευή.", + "SMS Type": "Τύπος SMS", + "octopushTypePremium": "Premium (Γρήγορη - συνιστάται για ειδοποίηση)", + "octopushTypeLowCost": "Χαμηλό κόστος (Αργό - μερικές φορές μπλοκάρεται από τον χειριστή)", + "checkPrice": "Ελέγξτε τις τιμές {0}:", + "apiCredentials": "API credentials", + "octopushLegacyHint": "Χρησιμοποιείτε την παλαιού τύπου έκδοση του Octopush (2011-2020) ή τη νέα έκδοση;", + "Check octopush prices": "Ελέγξτε τις τιμές OctoPush {0}.", + "octopushPhoneNumber": "Αριθμός τηλεφώνου (διεθνής μορφή, π.χ.: +30694345678) ", + "octopushSMSSender": "Όνομα αποστολέα SMS: 3-11 αλφαριθμητικοί χαρακτήρες και διάστημα (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Device ID", + "Apprise URL": "Apprise URL", + "Example:": "Παράδειγμα: {0}", + "Read more:": "Διαβάστε περισσότερα: {0}", + "Status:": "Κατάσταση: {0}", + "Read more": "Διαβάστε περισσότερα", + "appriseInstalled": "Το Apprise έχει εγκατασταθεί.", + "appriseNotInstalled": "Το Apprise δεν έχει εγκατασταθεί. {0}", + "Access Token": "Access Token", + "Channel access token": "Channel Access Token", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console - {0}", + "Basic Settings": "Βασικές ρυθμίσεις", + "User ID": "User ID", + "Messaging API": "Messaging API", + "wayToGetLineChannelToken": "Πρώτα αποκτήστε πρόσβαση στο {0}, δημιουργήστε έναν πάροχο και ένα κανάλι (Messanging API) και, στη συνέχεια, μπορείτε να λάβετε το channel access token και το user ID από τα παραπάνω στοιχεία μενού.", + "Icon URL": "Διεύθυνση URL εικονιδίου", + "aboutIconURL": "Μπορείτε να παρέχετε έναν σύνδεσμο προς μια εικόνα στο \"Icon URL\" για να παρακάμψετε την προεπιλεγμένη εικόνα προφίλ. Δεν θα χρησιμοποιηθεί εάν έχει οριστεί το εικονίδιο Emoji.", + "aboutMattermostChannelName": "Μπορείτε να παρακάμψετε το προεπιλεγμένο κανάλι στο οποίο δημοσιεύει το Webhook εισάγοντας το όνομα του καναλιού στο πεδίο \"Όνομα καναλιού\". Αυτό πρέπει να ενεργοποιηθεί στις ρυθμίσεις του Mattermost Webhook. Π.χ.: #other-channel", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - φθηνό αλλά αργό και συχνά υπερφορτωμένο. Περιορίζεται μόνο σε Πολωνούς παραλήπτες.", + "promosmsTypeFlash": "SMS FLASH - Το μήνυμα θα εμφανίζεται αυτόματα στη συσκευή του παραλήπτη. Περιορίζεται μόνο σε Πολωνούς παραλήπτες.", + "promosmsTypeFull": "SMS FULL - Premium επίπεδο SMS, Μπορείτε να χρησιμοποιήσετε το Όνομα Αποστολέα σας (Πρέπει πρώτα να καταχωρήσετε το όνομα). Αξιόπιστο για ειδοποιήσεις.", + "promosmsTypeSpeed": "SMS SPEED - Υψηλότερη προτεραιότητα στο σύστημα. Πολύ γρήγορο και αξιόπιστο αλλά ακριβό (περίπου διπλάσια τιμή SMS FULL).", + "promosmsPhoneNumber": "Αριθμός τηλεφώνου (για πολωνούς παραλήπτες Μπορείτε να παραλείψετε τους κωδικούς περιοχής)", + "promosmsSMSSender": "Όνομα αποστολέα SMS: Προεγγεγραμμένο όνομα ή ένα από τα προεπιλεγμένα: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu WebHookURL", + "matrixHomeserverURL": "Homeserver URL (με http(s):// και προαιρετικά θύρα)", + "Internal Room Id": "Internal Room ID", + "matrixDesc1": "Μπορείτε να βρείτε το internal room ID ανατρέχοντας στην ενότητα για προχωρημένους των ρυθμίσεων δωματίου στο πρόγραμμα-πελάτη Matrix. Θα πρέπει να μοιάζει με !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Συνιστάται ανεπιφύλακτα να δημιουργήσετε έναν νέο χρήστη και να μην χρησιμοποιήσετε το διακριτικό πρόσβασης του χρήστη Matrix, καθώς θα επιτρέψει την πλήρη πρόσβαση στον λογαριασμό σας και σε όλα τα δωμάτια στα οποία συμμετέχετε. Αντίθετα, δημιουργήστε έναν νέο χρήστη και προσκαλέστε τον μόνο στο δωμάτιο στο οποίο θέλετε να λαμβάνετε την ειδοποίηση. Μπορείτε να λάβετε το access token εκτελώντας {0}", + "Method": "Μέθοδος", + "Body": "Σώμα", + "Headers": "Κεφαλίδες", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "Οι κεφαλίδες του αιτήματος δεν αποτελούν έγκυρο JSON: ", + "BodyInvalidFormat": "Το περιεχόμενο/σώμα του αιτήματος δεν αποτελεί έγκυρο JSON: ", + "Monitor History": "Ιστορικο Παρακολούθησης", + "clearDataOlderThan": "Διατηρήστε τα δεδομένα ιστορικού παρακολούθησης για {0} ημέρες.", + "PasswordsDoNotMatch": "Οι κωδικοί πρόσβασης δεν ταιριάζουν.", + "records": "εγγραφές", + "One record": "Μία εγγραφή", + "steamApiKeyDescription": "Για την παρακολούθηση ενός διακομιστή παιχνιδιών Steam χρειάζεστε ένα κλειδί Steam Web-API. Μπορείτε να καταχωρήσετε το κλειδί API σας εδώ: ", + "Current User": "Τρέχων χρήστης", + "topic": "Θέμα", + "topicExplanation": "Θέμα MQTT προς παρακολούθηση", + "successMessage": "Μήνυμα επιτυχίας", + "successMessageExplanation": "Μήνυμα MQTT που θα θεωρηθεί επιτυχές", + "recent": "Πρόσφατος", + "Done": "Ολοκληρώθηκε", + "Info": "Πληροφορίες", + "Security": "Ασφάλεια", + "Steam API Key": "Steam API Key", + "Shrink Database": "Συρρίκνωση βάσης δεδομένων", + "Pick a RR-Type...": "Επιλέξτε έναν τύπο RR…", + "Pick Accepted Status Codes...": "Επιλέξτε Αποδεκτούς κωδικούς κατάστασης…", + "Default": "Προκαθορισμένο", + "HTTP Options": "Επιλογές HTTP", + "Create Incident": "Δημιουργία περιστατικού", + "Title": "Τίτλος", + "Content": "Περιεχόμενο", + "Style": "Στυλ", + "info": "πληροφορίες", + "warning": "προειδοποίηση", + "danger": "κίνδυνος", + "error": "σφάλμα", + "critical": "κριτικό", + "primary": "κύριο", + "light": "φωτεινό", + "dark": "σκοτεινό", + "Post": "Δημοσίευση", + "Please input title and content": "Παρακαλούμε εισαγάγετε τίτλο και περιεχόμενο", + "Created": "Δημιουργήθηκε", + "Last Updated": "Τελευταία ενημέρωση", + "Unpin": "Ξεκαρφιτσώστε", + "Switch to Light Theme": "Μετάβαση σε Ανιχτό θέμα", + "Switch to Dark Theme": "Μετάβαση σε Σκούρο θέμα", + "Show Tags": "Εμφάνιση ετικετών", + "Hide Tags": "Απόκρυψη ετικετών", + "Description": "Περιγραφή", + "No monitors available.": "Δεν υπάρχουν διαθέσιμες παρακολουθήσεις.", + "Add one": "Προσθέστε ένα", + "No Monitors": "Χωρίς παρακολουθήσεις", + "Untitled Group": "Ομάδα χωρίς τίτλο", + "Services": "Υπηρεσίες", + "Discard": "Απορρίψει", + "Cancel": "Ακυρο", + "Powered by": "Με την υποστήριξη του", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API Username (incl. webapi_ prefix)", + "serwersmsAPIPassword": "API κωδικός πρόσβασης", + "serwersmsPhoneNumber": "Αριθμός τηλεφώνου", + "serwersmsSenderName": "Όνομα αποστολέα SMS (καταχωρήθηκε μέσω της πύλης πελατών)", + "stackfield": "Stackfield", + "Customize": "Προσαρμογή", + "Custom Footer": "Προσαρμογή Footer", + "Custom CSS": "Προσαρμογή CSS", + "smtpDkimSettings": "Ρυθμίσεις DKIM", + "smtpDkimDesc": "Ανατρέξτε στο Nodemailer DKIM {0} για χρήση.", + "documentation": "documentation", + "smtpDkimDomain": "Domain Name", + "smtpDkimKeySelector": "Key Selector", + "smtpDkimPrivateKey": "Private Key", + "smtpDkimHashAlgo": "Hash Algorithm (Optional)", + "smtpDkimheaderFieldNames": "Header Keys to sign (Optional)", + "smtpDkimskipFields": "Header Keys not to sign (Optional)", + "wayToGetPagerDutyKey": "Μπορείτε να το λάβετε μεταβαίνοντας στο Service -> Service Directory -> (Επιλέξτε μια υπηρεσία) -> Integrations -> Add integration. Εδώ μπορείτε να κάνετε αναζήτηση για \"Events API V2\". Περισσότερες πληροφορίες {0}", + "Integration Key": "Integration Key", + "Integration URL": "Integration URL", + "Auto resolve or acknowledged": "Αυτόματη επίλυση ή αναγνώριση", + "do nothing": "μην κάνεις τίποτα", + "auto acknowledged": "αυτόματη αναγνώριση", + "auto resolve": "αυτόματη επίλυση", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API Endpoint", + "alertaEnvironment": "Environment", + "alertaApiKey": "API Key", + "alertaAlertState": "Alert State", + "alertaRecoverState": "Recover State", + "deleteStatusPageMsg": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν τη σελίδα κατάστασης?", + "Proxies": "Proxies", + "default": "Προκαθορισμένο", + "enabled": "Ενεργοποιημένο", + "setAsDefault": "Ορίσετε ως προεπιλογή", + "deleteProxyMsg": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το proxy για όλες τις παρακολουθήσεις;", + "proxyDescription": "Πρέπει να εκχωρηθούν proxies σε μια οθπαρακολουθή για να λειτουργήσουν..", + "enableProxyDescription": "Το proxy δεν θα επηρεάσει τα αιτήματα της παρακολουθήσεις μέχρι να ενεργοποιηθεί. Μπορείτε να ελέγξετε την προσωρινή απενεργοποίηση του proxy από όλες τις παρακολουθήσεις βάσει κατάστασης ενεργοποίησης.", + "setAsDefaultProxyDescription": "Αυτός το proxy θα είναι ενεργοποιημένο από προεπιλογή για νέες παρακολουθήσεις. Μπορείτε ακόμα να απενεργοποιήσετε το proxy ξεχωριστά για κάθε οθόνη.", + "Certificate Chain": "Αλυσίδα Πιστοποιητικών", + "Valid": "Εγκυρο", + "Invalid": "Μη έγκυρο", + "AccessKeyId": "AccessKey ID", + "SecretAccessKey": "AccessKey Secret", + "PhoneNumbers": "PhoneNumbers", + "TemplateCode": "TemplateCode", + "SignName": "SignName", + "Sms template must contain parameters: ": "Το πρότυπο SMS πρέπει να περιέχει παραμέτρους: ", + "Bark Endpoint": "Bark Endpoint", + "Bark Group": "Bark Ομάδα", + "Bark Sound": "Bark Ήχος", + "WebHookUrl": "WebHookUrl", + "SecretKey": "SecretKey", + "For safety, must use secret key": "Για ασφάλεια, πρέπει να χρησιμοποιήσετε secret key", + "Device Token": "Device Token", + "Platform": "Platform", + "Huawei": "Huawei", + "High": "High", + "Retry": "Ξαναδοκιμάσετε", + "Topic": "Θέμα", + "WeCom Bot Key": "WeCom Bot Key", + "Setup Proxy": "Ρύθμιση Proxy", + "Proxy Protocol": "Πρωτόκολλο Proxy", + "Proxy Server": "Proxy Server", + "Proxy server has authentication": "Το Proxy διαθέτει έλεγχο ταυτότητας", + "User": "Χρήστης", + "Installed": "Εγκατεστημένο", + "Not installed": "Μη εγκατεστημενο", + "Running": "Τρέχη", + "Not running": "Δεν τρεχη", + "Remove Token": "Κατάργηση Token", + "Start": "Αρχή", + "Stop": "Στάση", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Προσθήκη νέας σελίδας κατάστασης", + "Slug": "Slug", + "Accept characters:": "Αποδοχή χαρακτήρων:", + "startOrEndWithOnly": "Ξεκινήστε ή τελειώστε μόνο με {0}", + "No consecutive dashes": "Χωρίς διαδοχικές παύλες", + "Next": "Επόμενο", + "The slug is already taken. Please choose another slug.": "Ο slug έχει ήδη πιαστεί. Επιλέξτε άλλο slug.", + "No Proxy": "Οχι Proxy", + "Authentication": "Authentication", + "HTTP Basic Auth": "HTTP Basic Auth", + "New Status Page": "Νέας Σελίδα κατάστασης", + "Page Not Found": "Η σελίδα δεν βρέθηκε", + "Reverse Proxy": "Αντίστροφο Proxy", + "Backup": "Αντιγράφων ασφαλείας", + "About": "Σχετικά με το Uptime Kuma", + "wayToGetCloudflaredURL": "(Λήψη cloudflared από {0})", + "cloudflareWebsite": "Ιστοσελίδα Cloudflare", + "Message:": "Μήνυμα:", + "Don't know how to get the token? Please read the guide:": "Δεν ξέρετε πώς να αποκτήσετε το token; Διαβάστε τον οδηγό:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Η τρέχουσα σύνδεση μπορεί να χαθεί εάν αυτή τη στιγμή συνδέεστε μέσω του Cloudflare Tunnel. Θέλετε σίγουρα να το σταματήσετε; Πληκτρολογήστε τον τρέχοντα κωδικό πρόσβασής σας για να τον επιβεβαιώσετε.", + "HTTP Headers": "Κεφαλίδες HTTP", + "Trust Proxy": "Εμπιστοσύνη του Proxy", + "Other Software": "Other Software", + "For example: nginx, Apache and Traefik.": "Για παράδειγμα: nginx, Apache και Traefik.", + "Please read": "Παρακαλώ διαβάστε", + "Subject:": "Θέμα:", + "Valid To:": "Εγκυρο για:", + "Days Remaining:": "Ημέρες που απομένουν:", + "Issuer:": "Εκδότης:", + "Fingerprint:": "Δακτυλικό αποτύπωμα:", + "No status pages": "Δεν υπάρχουν σελίδες κατάστασης", + "Domain Name Expiry Notification": "Ειδοποίηση λήξης ονόματος τομέα", + "Proxy": "Proxy", + "Date Created": "Ημερομηνία Δημιουργίας", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "OneBot HTTP Address", + "onebotMessageType": "OneBot Message Type", + "onebotGroupMessage": "Group", + "onebotPrivateMessage": "Private", + "onebotUserOrGroupId": "Group/User ID", + "onebotSafetyTips": "Για ασφάλεια, πρέπει να ορίσετε το acess token", + "PushDeer Key": "PushDeer Key", + "Footer Text": "Κείμενο υποσέλιδου", + "Show Powered By": "Εμφάνιση Powered By", + "Domain Names": "Ονόματα Τομέα", + "signedInDisp": "Συνδεθήκατε ως {0}", + "signedInDispDisabled": "Εξουσιοδότηση είναι απενεργοποιημένη.", + "RadiusSecret": "Radius Secret", + "RadiusSecretDescription": "Shared Secret μεταξύ client και το server", + "RadiusCalledStationId": "Called Station Id", + "RadiusCalledStationIdDescription": "Identifier της καλούμενης συσκευής", + "RadiusCallingStationId": "Calling Station Id", + "RadiusCallingStationIdDescription": "Identifier oτης συσκευής κλήσης", + "Certificate Expiry Notification": "Ειδοποίηση Λήξης Πιστοποιητικού", + "API Username": "API Username", + "API Key": "API Key", + "Recipient Number": "Αριθμός Παραλήπτη", + "From Name/Number": "Από Όνομα/Αριθμός", + "Leave blank to use a shared sender number.": "Αφήστε το κενό για να χρησιμοποιήσετε έναν κοινόχρηστο αριθμό αποστολέα.", + "Octopush API Version": "Octopush API Version", + "Legacy Octopush-DM": "Legacy Octopush-DM", + "endpoint": "endpoint", + "octopushAPIKey": "\"API key\" από το HTTP API credentials στον πίνακα ελέγχου", + "octopushLogin": "\"Login\" από το HTTP API credentials στον πίνακα ελέγχου", + "promosmsLogin": "API Login Name", + "promosmsPassword": "API Password", + "pushoversounds pushover": "Pushover (default)", + "pushoversounds bike": "Bike", + "pushoversounds bugle": "Bugle", + "pushoversounds cashregister": "Cash Register", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Cosmic", + "pushoversounds falling": "Falling", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Incoming", + "pushoversounds intermission": "Intermission", + "pushoversounds magic": "Magic", + "pushoversounds mechanical": "Mechanical", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Siren", + "pushoversounds spacealarm": "Space Alarm", + "pushoversounds tugboat": "Tug Boat", + "pushoversounds alien": "Alien Alarm (long)", + "pushoversounds climb": "Climb (long)", + "pushoversounds persistent": "Persistent (long)", + "pushoversounds echo": "Pushover Echo (long)", + "pushoversounds updown": "Up Down (long)", + "pushoversounds vibrate": "Vibrate Only", + "pushoversounds none": "None (silent)", + "pushyAPIKey": "Μυστικό API Key", + "pushyToken": "Τoken Συσκευής", + "Show update if available": "Εμφάνιση ενημέρωσης εάν είναι διαθέσιμη", + "Also check beta release": "Ελέγξτε επίσης την έκδοση beta", + "Using a Reverse Proxy?": "Χρησιμοποιείτε reverse proxy;", + "Check how to config it for WebSocket": "Ελέγξτε πώς να το ρυθμίσετε για το WebSocket", + "Steam Game Server": "Διακομιστής παιχνιδιών Steam", + "Most likely causes:": "Πιο πιθανές αιτίες:", + "The resource is no longer available.": "Ο πόρος δεν είναι πλέον διαθέσιμος.", + "There might be a typing error in the address.": "Μπορεί να υπάρχει σφάλμα πληκτρολόγησης στη διεύθυνση.", + "What you can try:": "Τι μπορείτε να δοκιμάσετε:", + "Retype the address.": "Πληκτρολογήστε ξανά τη διεύθυνση.", + "Go back to the previous page.": "Επιστρέψτε στην προηγούμενη σελίδα.", + "Coming Soon": "Ερχεται σύντομα", + "wayToGetClickSendSMSToken": "Μπορείτε να πάρετε το API Username και API Key απο {0} .", + "Connection String": "Connection String", + "Query": "Query", + "settingsCertificateExpiry": "Λήξη πιστοποιητικού TLS", + "certificationExpiryDescription": "Οι παρακολουθήσεις HTTPS ενεργοποιούν ειδοποίηση όταν λήξει το πιστοποιητικό TLS σε:", + "Setup Docker Host": "Αρχικοποίηση Docker Host", + "Connection Type": "Τύπος σύνδεσης", + "Docker Daemon": "Docker Daemon", + "deleteDockerHostMsg": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτόν τον κεντρικό υπολογιστή βάσης για όλες τις παρακολουθήσεις;", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker Container", + "Container Name / ID": "Container Name / ID", + "Docker Host": "Docker Host", + "Docker Hosts": "Docker Hosts", + "ntfy Topic": "ntfy Topic", + "Domain": "Domain", + "Workstation": "Workstation", + "disableCloudflaredNoAuthMsg": "Βρίσκεστε σε λειτουργία No Auth, δεν απαιτείται κωδικός πρόσβασης.", + "trustProxyDescription": "Εμπιστευτείτε τις κεφαλίδες 'X-Forwarded-*'. Εάν θέλετε να λάβετε τη σωστή IP πελάτη και το Uptime Kuma σας βρίσκεται πίσω κάποιος proxy όπως το Nginx ή το Apache, θα πρέπει να το ενεργοποιήσετε.", + "wayToGetLineNotifyToken": "Μπορείτε να λάβετε ένα access token από το {0}", + "Examples": "Παραδείγματα", + "Home Assistant URL": "Home Assistant URL", + "Long-Lived Access Token": "Μακράς-Διάρκειας Κλειδί Τόκεν", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Long-Lived Access Token μπορεί να δημιουργηθεί κάνοντας κλικ στο όνομα του προφίλ σας (κάτω αριστερά) και κάνοντας κύλιση προς τα κάτω και, στη συνέχεια, κάντε κλικ στο Create Token. ", + "Notification Service": "Υπηρεσία ειδοποιήσεων", + "default: notify all devices": "προεπιλογή: ειδοποίηση όλων των συσκευών", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Μπορείτε να βρείτε μια λίστα με τις Υπηρεσίες ειδοποιήσεων στον Home assistant στην περιοχή \"Developer Tools > Services\" αναζήτηση για \"notification\" για να βρείτε το όνομα της συσκευής/τηλεφώνου σας.", + "Automations can optionally be triggered in Home Assistant:": "Οι αυτοματισμοί μπορούν προαιρετικά να ενεργοποιηθούν στο Home Assistant:", + "Trigger type:": "Τύπος ενεργοποίησης:", + "Event type:": "Τύπος συμβάντος:", + "Event data:": "Δεδομένα συμβάντος:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Στη συνέχεια, επιλέξτε μια ενέργεια, για παράδειγμα αλλάξτε τη σκηνή στο σημείο όπου ένα φως RGB είναι κόκκινο.", + "Frontend Version": "Έκδοση Frontend", + "Frontend Version do not match backend version!": "Η Frontend έκδοση δεν ταιριάζει με την έκδοση backend!", + "Base URL": "Βασική διεύθυνση URL", + "goAlertInfo": "Το GoAlert είναι μια εφαρμογή ανοιχτού κώδικα για προγραμματισμό κλήσεων, αυτοματοποιημένες κλιμακώσεις και ειδοποιήσεις (όπως SMS ή φωνητικές κλήσεις). Αλληλεπιδράστε αυτόματα με το σωστό άτομο, με τον σωστό τρόπο και τη σωστή στιγμή! {0}", + "goAlertIntegrationKeyInfo": "Λάβετε το generic API integration key για την υπηρεσία σε αυτήν τη μορφή \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" συνήθως την τιμή της παραμέτρου διακριτικού της αντιγραμμένης διεύθυνσης URL.", + "goAlert": "GoAlert", + "backupOutdatedWarning": "Καταργήθηκε: Επειδή προστέθηκαν πολλές δυνατότητες και αυτή η δυνατότητα δημιουργίας αντιγράφων ασφαλείας δεν διατηρείται, δεν μπορεί να δημιουργήσει ή να επαναφέρει ένα πλήρες αντίγραφο ασφαλείας.", + "backupRecommend": "Παρακαλούμε δημιουργήστε αντίγραφα ασφαλείας του volume ή του φακέλου δεδομένων (./data/) απευθείας.", + "Maintenance": "Συντήρηση", + "General Monitor Type": "Επόπτης Γενικού Τύπου", + "maintenanceStatus-under-maintenance": "Υπό Συντήρηση", + "dnsCacheDescription": "Μπορεί να μη λειτουργεί σε κάποια IPv6 περιβάλλοντα, απενεργοποιήστε αν συναντήσετε προβλήματα.", + "uninstalling": "Γίνεται απεγκατάσταση", + "confirmUninstallPlugin": "Θέλετε σίγουρα να απεγκαταστήσετε αυτό το πρόσθετο;", + "smseagle": "SMSEagle", + "smseagleRecipientType": "Τύπος παραλήπτη", + "smseagleUrl": "Το URL της SMSEagle συσκευής σας", + "Start of maintenance": "Έναρξη συντήρησης", + "All Status Pages": "Όλες οι Σελίδες Κατάστασης", + "Select status pages...": "Επιλέξτε σελίδες κατάστασης…", + "Optional": "Προαιρετικό", + "weekdayShortMon": "Δευ", + "weekdayShortTue": "Τρι", + "weekdayShortWed": "Τετ", + "weekdayShortThu": "Πεμ", + "weekdayShortFri": "Παρ", + "weekdayShortSat": "Σαβ", + "Help": "Βοήθεια", + "Game": "Παιχνίδι", + "Specific Monitor Type": "Επόπτης Συγκεκριμένου Τύπου", + "Passive Monitor Type": "Επόπτης Παθητικού Τύπου", + "Monitor": "Επόπτης | Επόπτες", + "Schedule maintenance": "Προγραμματισμός συντήρησης", + "Affected Monitors": "Επηρεαζόμενοι Επόπτες", + "Pick Affected Monitors...": "Διαλέξτε Επηρεαζόμενους Επόπτες…", + "webhookAdditionalHeadersTitle": "Επιπρόσθετες Κεφαλίδες", + "webhookAdditionalHeadersDesc": "Ορίζει επιπρόσθετες κεφαλίδες που θα σταλθούν με το webhook. Κάθε κεφαλίδα πρέπει να ορίζεται ώς JSON ζεύγος κλειδιού/τιμής.", + "weekdayShortSun": "Κυρ", + "dayOfWeek": "Ημέρα της Εβδομάδας", + "dayOfMonth": "Ημέρα του Μήνα", + "lastDay1": "Τελευταία Μέρα του Μήνα", + "lastDay2": "2η Τελευταία Μέρα του Μήνα", + "lastDay3": "3η Τελευταία Μέρα του Μήνα", + "lastDay4": "4η Τελευταία Μέρα του Μήνα", + "lastDay": "Τελευταία Μέρα", + "No Maintenance": "Όχι Συντήρηση", + "pauseMaintenanceMsg": "Είστε σίγουροι για την παύση;", + "maintenanceStatus-inactive": "Ανενεργό", + "maintenanceStatus-scheduled": "Προγραμματισμένο", + "maintenanceStatus-ended": "Ολοκληρωμένο", + "maintenanceStatus-unknown": "Άγνωστο", + "Display Timezone": "Προβολή Ζώνης Ώρας", + "Server Timezone": "Ζώνη Ώρας του Server", + "statusPageMaintenanceEndDate": "Λήξη", + "Custom": "Προσαρμοσμένο", + "Economy": "Οικονομία", + "loadingError": "Αδύνατη συλλογή δεδομένων, προσπαθήστε ξανά αργότερα.", + "SendKey": "SendKey", + "SMSManager API Docs": "API βιβλιογραφία του SMSManager ", + "Kook": "Kook", + "statusMaintenance": "Συντήρηση", + "markdownSupported": "Υποστηρίζεται η σύνταξη Markdown. Εάν χρησιμοποιείτε HTML, αποφύγετε τα κενά διαστήματα στην αρχή για να αποτρέψετε προβλήματα μορφοποίησης.", + "Packet Size": "Μέγεθος Πακέτου", + "or": "ή", + "recurringInterval": "Χρονικό Διάστημα", + "Recurring": "Επαναλαμβανόμενο", + "strategyManual": "Ενεργό/Ανενεργό Χειροκίνητα", + "warningTimezone": "Χρησιμοποιεί την ζώνη ώρας του server", + "squadcast": "Squadcast", + "IconUrl": "URL εικονιδίου", + "Enable DNS Cache": "(Καταργήθηκε) Ενεργοποίηση DNS Cache για οθόνες HTTP(s)", + "Enable": "Ενεργοποίηση", + "Disable": "Απενεργοποίηση", + "Single Maintenance Window": "Μονό Παράθυρο Συντήρησης", + "Maintenance Time Window of a Day": "Ημερίσιο πρόγραμμα Συντήρησης", + "Effective Date Range": "Ημερομηνιακό Διάστημα Εφαρμογής (Προαιρετικό)", + "Schedule Maintenance": "Προγραμματισμός Συντήρησης", + "Date and Time": "Ημερομηνία και Ώρα", + "DateTime Range": "Ημερομηνιακό Πλαίσιο", + "plugin": "Πρόσθετο | Πρόσθετα", + "install": "Εγκατάσταση", + "installing": "Γίνεται εγκατάσταση", + "uninstall": "Απεγκατάσταση", + "dataRetentionTimeError": "Η περίοδος διατήρησης πρέπει να είναι 0 ή μεγαλύτερο", + "infiniteRetention": "Ορίστε 0 για μόνιμη διατήρηση.", + "confirmDeleteTagMsg": "Θέλετε σίγουρα να διαγράψετε αυτήν την ετικέτα; Οι επόπτες που σχετίζονται με αυτήν την ετικέτα δεν θα διαγραφούν.", + "enableGRPCTls": "Επιτρέψτε την αποστολή gRPC αιτημάτων μέσω TLS συνδέσεων", + "grpcMethodDescription": "Το όνομα της μεθόδου μετατρέπεται σε cammelCase μορφή όπως π.χ. sayHello, check, κλπ.", + "deleteMaintenanceMsg": "Θέλετε σίγουρα να διαγράψετε αυτή την συντήρηση;", + "recurringIntervalMessage": "Εκτέλεση μια φορά την ημέρα | Εκτέλεση μία φορά ανά {0} ημέρες", + "affectedMonitorsDescription": "Επιλέξτε τους επόπτες που επηρεάζονται από την τωρινή συντήρηση", + "affectedStatusPages": "Προβολή αυτού του μηνύματος συντήρησης σε επιλεγμένες σελίδες κατάστασης", + "atLeastOneMonitor": "Επιλέξτε τουλάχιστον έναν επηρεασμένο επόπτη", + "wayToGetKookBotToken": "Δημιουργήστε εφαρμογή και πάρτε το bot token στο {0}", + "wayToGetKookGuildID": "Ενεργοποιήστε την 'Λειτουργία Προγραμματιστή' στις ρυθμίσεις Kook, και κάντε δεξί κλικ στο guild για να πάρετε το ID του", + "Guild ID": "Guild ID", + "Strategy": "Στρατηγική", + "Enable TLS": "Ενεργοποίηση TLS", + "Proto Service Name": "Όνομα Υπηρεσίας Proto", + "Proto Method": "Μέθοδος Proto", + "Proto Content": "Περιεχόμενο Proto", + "Lowcost": "Χαμηλό κόστος", + "high": "υψηλό", + "Gateway Type": "Τύπος Πύλης", + "SMSManager": "SMSManager", + "You can divide numbers with": "Μπορείτε να διαιρέσετε αριθμούς με", + "promosmsAllowLongSMS": "Επέτρεψε SMS μεγάλου μεγέθους", + "smseagleTo": "Αριθμός(οί) τηλεφώνου", + "smseagleGroup": "Όνομα/Ονόματα γκρουπ καταλόγων", + "smseagleContact": "Όνομα/Ονόματα επαφών καταλόγου", + "smseagleRecipient": "Παραλήπτης(ες) (πολλαπλοί πρέπει να διαχωρίζονται με κόμμα)", + "smseagleToken": "API Κλειδί τόκεν", + "smseagleEncoding": "Αποστολή ως Unicode", + "Custom Monitor Type": "Προσαρμοσμένος Τύπος Επόπτη", + "Edit Tag": "Επεξεργασία Ετικέτας", + "Server Address": "Διεύθυνση Διακομιστή", + "Learn More": "Μάθετε περισσότερα", + "Free Mobile User Identifier": "Free Mobile User Identifier", + "Free Mobile API Key": "Free Mobile API Key", + "smseaglePriority": "Προτεραιότητα μηνύματος (0-9, προεπιλογή = 0)", + "statusPageRefreshIn": "Ανανέωση σε {0}", + "Add New Tag": "Πρόσθεσε νέα ετικέτα", + "setupDatabaseMariaDB": "Συνδεθείτε με εξωτερική βάση δεδομένων MariaDB. Θα πρέπει να ορίσετε τα στοιχεία σύνδεσης της βάσης δεδομένων.", + "setupDatabaseChooseDatabase": "Ποια βάση δεδομένων θέλετε να χρησιμοποιήσετε;", + "setupDatabaseEmbeddedMariaDB": "Δεν χρειάζεται να ορίσετε τίποτε. Το Docker Image έχει ενθέσειι και διαμορφώσει μια βάση δεδομένων MariaDB για εσάς αυτόματα. Το Uptime Kuma θα συνδεθεί με αυτήν την βάση δεδομένων μέσω unix socket.", + "setupDatabaseSQLite": "Ένα απλό αρχείο βάσης δεδομένων, προτεινόμενο για εγκαταστάσεις μικρής κλίμακας. Πρίν από την έκδοση 2.0.0, το Uptime Kuma χρησιμοποιούσε το SQLite ως την προεπιλεγμένη βάση δεδομένων.", + "Cannot connect to the socket server": "Δεν είναι δυνατή η σύνδεση με τον διακομιστή socket", + "Reconnecting...": "Επανασύνδεση...", + "Home": "Αρχική", + "settingUpDatabaseMSG": "Ρύθμιση της βάσης δεδομένων. Μπορεί να διαρκέσει λίγη ώρα, παρακαλώ κάνετε υπομονή.", + "dbName": "Όνομα βάσης δεδομένων", + "Invert Keyword": "Αντιστροφή Λέξης-Κλειδιού", + "Expected Value": "Αναμενόμενη Τιμή", + "Json Query": "Ερώτημα Json", + "Add Tags": "Πρόσθεσε ετικέτες", + "tagAlreadyOnMonitor": "Η ετικέτα (όνομα και τιμή) υπάρχει ήδη στην Οθόνη ή αναμένει να προστεθεί.", + "tagNameExists": "Μια ετικέτα συστήματος υπάρχει ήδη με αυτό το όνομα. Επιλέξτε την από τη λίστα ή χρησιμοποιήστε διαφορετικό όνομα.", + "liquidIntroduction": "H ικανότητα προσαρμογής σε πρότυπο επιτυγχάνεται με τη γλώσσα προτύπων Liquid. Παρακαλώ βρείτε οδηγίες χρήσης στο {0}. Οι διαθέσιμες μεταβλητές είναι:", + "mqttWebsocketPathExplanation": "WebSocket μονοπάτι για MQTT πάνω σε συνδέσεις WebSocket (π.χ., /mqtt)", + "successKeyword": "Λέξη-κλειδί σε περίπτωση επιτυχίας", + "successKeywordExplanation": "MQTT Λέξη-κλειδί που θα θεωρηθεί επιτυχία", + "statusPageSpecialSlugDesc": "Ειδικό slug {0}: η σελίδα δεν θα φανεί αν δεν παρέχεται κανένα slug", + "Add a new expiry notification day": "Προσθέστε νέα μέρα ειδοποίησης λήξης", + "noDockerHostMsg": "Μη διαθέσιμο. Πρώτα αρχικοποιήστε εναν Docker Host.", + "tailscalePingWarning": "Προκειμένου να χρησιμοποιήσετε την οθόνη Tailscale Ping, χρειάζεται να εγκαταστήσετε το Uptime Kuma χωρίς Docker και επίσης εναν Tailscale client στον server σας.", + "telegramProtectContentDescription": "Αν είναι ενεργοποιημένο, τα μηνύματα από μποτς στο Telegram θα προστατεύονται από προώθηση και αποθήκευση.", + "telegramUseTemplateDescription": "Αν είναι ενεργοποιημένο, το μηνυμα θα σταλεί με προσαρμοσμένο πρότυπο.", + "telegramServerUrlDescription": "Για να υπερβείτε τους περιορισμούς του Bot API του Telegram ή να αποκτήσετε πρόσβαση σε αποκλεισμένες περιοχές (Κίνα, Ιραν, κλπ). Για περισσότερες πληροφορίες πατήστε {0}. Προεπιλογή: {1}", + "invalidCronExpression": "Μη έγκυρη έκφραση Cron: {0}", + "chromeExecutableDescription": "Για χρήστες Docker, αν το Chromium δεν είναι ακόμη εγκατεστημένο, μπορεί να πάρει μερικά λεπτά να εγκατασταθεί και να εμφανίσει το αποτέλεσμα της δοκιμής. Παίρνει 1GB χώρου στο δίσκο.", + "telegramServerUrl": "(Προαιρετικό) Server Url", + "Path": "Μονοπάτι", + "styleElapsedTimeShowWithLine": "Εμφάνισε (Με Γραμμή)", + "filterActive": "Ενεργό", + "filterActivePaused": "Σε Διακοπή", + "templateMsg": "μήνυμα ειδοποίησης", + "templateHeartbeatJSON": "Αντικείμενο που περιγράφει το heartbeat", + "templateMonitorJSON": "Αντικείμενο που περιγράφει την οθόνη", + "templateLimitedToUpDownCertNotifications": "διαθέσιμο μόνο για ειδοποιήσεις τύπου UP/DOWN/Certificate expiry", + "templateLimitedToUpDownNotifications": "Διαθέσιμο μόνο για ειδοποιήσεις τύπου UP/DOWN", + "mqttWebSocketPath": "Μονοπάτι MQTT WebSocket", + "mqttWebsocketPathInvalid": "Παρακαλώ χρησιμοποιείστε έγκυρη μορφή μονοπατιού WebSocket", + "mqttHostnameTip": "Παρακαλώ χρησιμοποιείστε τη μορφή {hostnameFormat}", + "Select": "Επιλέξτε", + "selectedMonitorCount": "Επιλεγμένο: {0}", + "Check/Uncheck": "Επιλέξτε/Απεπιλέξτε", + "Remove the expiry notification": "Αφαιρέστε την ημέρα ειδοποίησης λήξης", + "Refresh Interval": "Ανανεώστε το μεσοδιάστημα", + "Refresh Interval Description": "Η σελίδα κατάστασης θα ανανεώνεται κάθε {0} δευτερόλεπτα", + "DockerHostRequired": "Παρακαλώ ορίστε τον Docker Host για αυτή την οθόνη.", + "telegramMessageThreadID": "(Προαιρετικό) Αναγνωριστικό Νήματος Μηνύματος", + "telegramMessageThreadIDDescription": "Προαιρετικό μοναδικό αναγνωριστικό για τον νήμα (θέμα) που αναφέρεστε στο forum, μόνο για υπερσύνολα του forum", + "telegramSendSilently": "Στείλτε αθόρυβα", + "telegramProtectContent": "Προστατέψτε Προώθηση/Αποθήκευση", + "telegramUseTemplate": "Χρησιμοποιήστε προσαρμοσμένο πρότυπο μηνύματος", + "telegramTemplateFormatDescription": "Το Telegram επιτρέπει τη χρήση διαφορετικών γλωσσών markup για μηνύματα, βλέπε Telegram {0} για συγκεκριμένες λεπτομέρειες.", + "sameAsServerTimezone": "Ίδιο με τη ζώνη ώρας του Server", + "startDateTime": "Ημερομηνία/Ώρα έναρξης", + "endDateTime": "Ημερομηνία/Ώρα λήξης", + "cronExpression": "Έκφραση Cron", + "cronSchedule": "Πρόγραμμα: ", + "enableNSCD": "Ενεργοποιήστε NSCD (Name Service Cache Daemon) για προσωρινή αποθήκευση όλων των αιτημάτων DNS", + "chromeExecutable": "Εκτελέσιμο Chrome/Chromium", + "chromeExecutableAutoDetect": "Αυτόματος Εντοπισμός", + "Edit Maintenance": "Επεξεργασία Συντήρησης", + "tagAlreadyStaged": "Η ετικέτα (όνομα και τιμή) είναι ήδη σε κατάσταση staged για αυτό το batch.", + "Reset Token": "Επαναφορά Token", + "defaultFriendlyName": "Νέα Οθόνη", + "styleElapsedTimeShowNoLine": "Εμφάνισε (Χωρίς Γραμμή)", + "telegramSendSilentlyDescription": "Στέλνει το μήνυμα αθόρυβα. Οι χρήστες θα λάβουν ειδοποίηση χωρίς ήχο.", + "templateServiceName": "όνομα υπηρεσίας", + "templateHostnameOrURL": "hostname ή URL", + "templateStatus": "κατάσταση", + "webhookBodyPresetOption": "Πρότυπο - {0}", + "webhookBodyCustomOption": "Προσαρμοσμένο Σώμα", + "and": "και", + "Search monitored sites": "Αναζητήστε παρακολουθούμενα sites", + "shrinkDatabaseDescriptionSqlite": "Πυροδοτήστε τη βαση δεδομένων {vacuum} για SQLite. Το {auto_vacuum} είναι ήδη ενεργοποιημένο αλλα αυτό δεν ανασυγκροτεί τη βάση ούτε επανενώνει μεμονωμένες σελίδες στη βάση όπως το κάνει η εντολή {vacuum}.", + "Host URL": "Host URL", + "locally configured mail transfer agent": "τοπικά παραμετροποιημένος πράκτορας μεταφοράς αλληλογραφίας", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Εισάγετε είτε το hostname του server που θέλετε να συνδεθείτε ή {localhost} αν σκοπεύετε να χρησιμοποιήσετε την επιλογή {local_mta}", + "now": "τώρα", + "ignoredTLSError": "Έχουν αγνοηθεί τα σφάλματα TLS/SSL", + "ignoreTLSErrorGeneral": "Παράβλεψη σφάλματος TLS/SSL για σύνδεση", + "programmingLanguages": "Γλώσσες Προγραμματισμού", + "styleElapsedTime": "Παρελθόν χρόνος υπό την μπάρα heartbeat", + "pushOthers": "Άλλα", + "time ago": "πριν από {0}", + "-year": "-έτος", + "Json Query Expression": "Έκφραση Json Query", + "Request Timeout": "Timeout Αιτήματος", + "timeoutAfter": "Λήξη χρόνου ύστερα από {0} δευτερόλεπτα", + "pushViewCode": "Πώς να χρησιμοποιήσω Οθόνη Push (βλέπε κώδικα)", + "mariadbCaCertificateLabel": "Πιστοποιητικά CA", + "mariadbUseSSLHelptext": "Ενεργοποιήστε τη χρήση κρυπτογραφημένης σύνδεσης με τη βάση δεδομένων σας. Απαιτείται για τις περισσότερες βάσεις δεδομένων cloud.", + "No incidents recorded": "Δεν καταγράφηκαν περιστατικά", + "Load More": "Φόρτωση περισσότερων", + "Loading...": "Φόρτωση...", + "unknownDays": "Άγνωστες ημέρες", + "Monitors": "{n} Οθόνη | {n} Οθόνες", + "versionIs": "Έκδοση: {version}", + "monitorTypeGameServer": "Διακομιστής παιχνιδιών", + "monitorTypeDatabase": "Τύπος παρακολούθησης βάσης δεδομένων", + "Pin this incident": "Καρφίτσωμα αυτού του περιστατικού", + "days": "{n} ημέρα | {n} ημέρες", + "mariadbCaCertificateHelptext": "Επικολλήστε το πιστοποιητικό CA σε μορφή PEM για χρήση με αυτο-υπογεγραμμένα πιστοποιητικά. Αφήστε το κενό εάν η βάση δεδομένων σας χρησιμοποιεί πιστοποιητικό υπογεγραμμένο από δημόσια CA.", + "hours": "{n} ώρες| {n} ώρες", + "Ignore Sec-WebSocket-Accept header": "Αγνοήστε την κεφαλίδα {0}", + "wsSubprotocolDescription": "Εισαγάγετε μια λίστα υποπρωτοκόλλων διαχωρισμένων με κόμματα. Για περισσότερες πληροφορίες σχετικά με τα υποπρωτόκολλα, συμβουλευτείτε το {documentation}", + "Subprotocol(s)": "Υποπρωτόκολλο(-α)", + "saveResponseForNotifications": "Αποθήκευση απάντησης επιτυχίας HTTP για ειδοποιήσεις", + "wsCodeDescription": "Για περισσότερες πληροφορίες σχετικά με τους κωδικούς κατάστασης, παρακαλούμε συμβουλευτείτε το {rfc6455}", + "retryOnlyOnStatusCodeFailureDescription": "Εάν η λειτουργία είναι ενεργοποιημένη, οι επαναλήψεις θα πραγματοποιούνται μόνο όταν αποτυγχάνει ο έλεγχος του κωδικού κατάστασης HTTP (π.χ. ο διακομιστής είναι εκτός λειτουργίας). Εάν ο έλεγχος του κωδικού κατάστασης είναι επιτυχής αλλά το ερώτημα JSON αποτύχει, το στοιχείο παρακολούθησης θα επισημανθεί άμεσα ως μη λειτουργικό, χωρίς να γίνουν επαναλήψεις.", + "Only retry if status code check fails": "Δοκιμάστε ξανά μόνο αν ο έλεγχος κατάστασης κώδικα έχει αποτύχει", + "monitorTypeSpecial": "Ειδικά", + "minuteShort": "{n} λεπ | {n} λεπ", + "years": "{n} χρόνια | {n} χρόνια", + "Connection Timeout": "Λήξη χρονικού ορίου αιτήματος", + "enableSSL": "Ενεργοποίηση SSL/TLS", + "saveErrorResponseForNotifications": "Αποθήκευση απάντησης σφάλματος HTTP για ειδοποιήσεις", + "minutes": "{n} λεπτά| {n} λεπτά", + "ignoreSecWebsocketAcceptHeaderDescription": "Επιτρέπει στον διακομιστή να μην απαντήσει με την κεφαλίδα Sec-WebSocket-Accept, εάν η αναβάθμιση σε WebSocket ολοκληρωθεί με επιτυχία.", + "oracledbConnectionString": "Βάση Δεδομένων Oracle: {connectionString}" +} diff --git a/src/lang/en.json b/src/lang/en.json new file mode 100644 index 0000000..afb0a2b --- /dev/null +++ b/src/lang/en.json @@ -0,0 +1,1641 @@ +{ + "languageName": "English", + "setupDatabaseChooseDatabase": "Which database would you like to use?", + "setupDatabaseEmbeddedMariaDB": "You don't need to set anything. This Docker image has embedded and configured MariaDB for you automatically. Uptime Kuma will connect to this database via Unix socket.", + "setupDatabaseMariaDB": "Connect to an external MariaDB database. You need to set the database connection information.", + "setupDatabaseSQLite": "A simple database file, recommended for small-scale deployments. Prior to v2.0.0, Uptime Kuma used SQLite as the default database.", + "settingUpDatabaseMSG": "Setting up the database. It may take a while, please be patient.", + "dbName": "Database Name", + "oracledbConnectionString": "Oracle Database: {connectionString}", + "enableSSL": "Enable SSL/TLS", + "mariadbUseSSLHelptext": "Enable to use a encrypted connection to your database. Required for most cloud databases.", + "mariadbCaCertificateLabel": "CA Certificate", + "mariadbCaCertificateHelptext": "Paste the CA Cert in PEM format to use with self-signed certificates. Leave blank if your database uses a certificate signed by a public CA.", + "Settings": "Settings", + "Dashboard": "Dashboard", + "Help": "Help", + "New Update": "New Update", + "Language": "Language", + "Appearance": "Appearance", + "Theme": "Theme", + "General": "General", + "Game": "Game", + "Primary Base URL": "Primary Base URL", + "versionIs": "Version: {version}", + "Check Update On GitHub": "Check Update On GitHub", + "List": "List", + "Home": "Home", + "Add": "Add", + "Add New Monitor": "Add New Monitor", + "Quick Stats": "Quick Stats", + "Up": "Up", + "Down": "Down", + "Pending": "Pending", + "statusMaintenance": "Maintenance", + "Maintenance": "Maintenance", + "Unknown": "Unknown", + "unknownDays": "Unknown days", + "Cannot connect to the socket server": "Cannot connect to the socket server", + "Reconnecting...": "Reconnecting...", + "General Monitor Type": "General Monitor Type", + "Passive Monitor Type": "Passive Monitor Type", + "Specific Monitor Type": "Specific Monitor Type", + "monitorTypeGameServer": "Game Server", + "monitorTypeDatabase": "Database Monitor Type", + "monitorTypeSpecial": "Special", + "markdownSupported": "Markdown syntax supported. If using HTML, avoid leading spaces to prevent formatting issues.", + "pauseDashboardHome": "Pause", + "Pause": "Pause", + "Name": "Name", + "Status": "Status", + "DateTime": "DateTime", + "Message": "Message", + "No incidents recorded": "No incidents recorded", + "Load More": "Load More", + "Loading...": "Loading...", + "No important events": "No important events", + "Resume": "Resume", + "Edit": "Edit", + "Delete": "Delete", + "Current": "Current", + "Uptime": "Uptime", + "Cert Exp.": "Cert Exp.", + "Monitors": "{n} Monitor | {n} Monitors", + "now": "now", + "time ago": "{0} ago", + "days": "{n} day | {n} days", + "hours": "{n} hour | {n} hours", + "minutes": "{n} minute | {n} minutes", + "minuteShort": "{n} min | {n} min", + "years": "{n} year | {n} years", + "Response": "Response", + "Pin this incident": "Pin this incident", + "Ping": "Ping", + "Monitor Type": "Monitor Type", + "Keyword": "Keyword", + "Invert Keyword": "Invert Keyword", + "Expected Value": "Expected Value", + "Json Query Expression": "Json Query Expression", + "Friendly Name": "Friendly Name", + "defaultFriendlyName": "New Monitor", + "URL": "URL", + "Hostname": "Hostname", + "Host URL": "Host URL", + "locally configured mail transfer agent": "locally configured mail transfer agent", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Either enter the hostname of the server you want to connect to, or {localhost} if you intend to use a {local_mta}", + "Port": "Port", + "Path": "Path", + "Heartbeat Interval": "Heartbeat Interval", + "Request Timeout": "Request Timeout", + "Connection Timeout": "Connection Timeout", + "timeoutAfter": "Timeout after {0} seconds", + "Retries": "Retries", + "Heartbeat Retry Interval": "Heartbeat Retry Interval", + "Resend Notification if Down X times consecutively": "Resend Notification if Down X times consecutively", + "Advanced": "Advanced", + "checkEverySecond": "Check every {0} seconds", + "retryCheckEverySecond": "Retry every {0} seconds", + "resendEveryXTimes": "Resend every {0} times", + "resendDisabled": "Resend disabled", + "retriesDescription": "Maximum retries before the service is marked as down and a notification is sent", + "Only retry if status code check fails": "Only retry if status code check fails", + "retryOnlyOnStatusCodeFailureDescription": "If enabled, retries will only occur when the HTTP status code check fails (e.g., server is down). If the status code check passes but the JSON query fails, the monitor will be marked as down immediately without retries.", + "ignoredTLSError": "TLS/SSL errors have been ignored", + "ignoreTLSError": "Ignore TLS/SSL errors for HTTPS websites", + "ignoreTLSErrorGeneral": "Ignore TLS/SSL error for connection", + "upsideDownModeDescription": "Flip the status upside down. If the service is reachable, it is DOWN.", + "ignoreSecWebsocketAcceptHeaderDescription": "Allows the server to not reply with Sec-WebSocket-Accept header, if the websocket upgrade succeeds.", + "Ignore Sec-WebSocket-Accept header": "Ignore {0} header", + "wsSubprotocolDescription": "Enter a comma delimited list of subprotocols. For more information on subprotocols, please consult the {documentation}", + "wsCodeDescription": "For more information on status codes, please consult {rfc6455}", + "Subprotocol(s)": "Subprotocol(s)", + "maxRedirectDescription": "Maximum number of redirects to follow. Set to 0 to disable redirects.", + "Upside Down Mode": "Upside Down Mode", + "Max. Redirects": "Max. Redirects", + "saveResponseForNotifications": "Save HTTP Success Response for Notifications", + "saveErrorResponseForNotifications": "Save HTTP Error Response for Notifications", + "saveResponseDescription": "Stores the HTTP response and makes it available to notification templates as {templateVariable}", + "responseMaxLength": "Response Max Length (bytes)", + "responseMaxLengthDescription": "Maximum size of response data to store. Set to 0 for unlimited. Larger responses will be truncated. Default: 1024 (1KB)", + "Accepted Status Codes": "Accepted Status Codes", + "Push URL": "Push URL", + "needPushEvery": "You should call this URL every {0} seconds.", + "pushOptionalParams": "Optional parameters: {0}", + "pushViewCode": "How to use Push monitor? (View Code)", + "pushOthers": "Others", + "programmingLanguages": "Programming Languages", + "Save": "Save", + "Notifications": "Notifications", + "Not available, please setup.": "Not available, please set up.", + "Setup Notification": "Set Up Notification", + "Light": "Light", + "Dark": "Dark", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Theme - Heartbeat Bar", + "styleElapsedTime": "Elapsed time under the heartbeat bar", + "styleElapsedTimeShowNoLine": "Show (No Line)", + "styleElapsedTimeShowWithLine": "Show (With Line)", + "Normal": "Normal", + "Bottom": "Bottom", + "None": "None", + "Timezone": "Timezone", + "Search Engine Visibility": "Search Engine Visibility", + "Allow indexing": "Allow indexing", + "Discourage search engines from indexing site": "Discourage search engines from indexing site", + "Change Password": "Change Password", + "Current Password": "Current Password", + "New Password": "New Password", + "Repeat New Password": "Repeat New Password", + "Update Password": "Update Password", + "Disable Auth": "Disable Auth", + "Enable Auth": "Enable Auth", + "disableauth.message1": "Are you sure want to {disableAuth}?", + "disable authentication": "disable authentication", + "disableauth.message2": "It is designed for scenarios {intendThirdPartyAuth} in front of Uptime Kuma such as Cloudflare Access, Authelia or other authentication mechanisms.", + "where you intend to implement third-party authentication": "where you intend to implement third-party authentication", + "Please use this option carefully!": "Please use this option carefully!", + "Logout": "Log out", + "logoutCurrentUser": "Log out {username}", + "Leave": "Leave", + "I understand, please disable": "I understand, please disable", + "Confirm": "Confirm", + "Yes": "Yes", + "No": "No", + "Username": "Username", + "Password": "Password", + "Remember me": "Remember me", + "Login": "Log in", + "No Monitors, please": "No Monitors, please", + "add one": "add one", + "Notification Type": "Notification Type", + "Email": "Email", + "Test": "Test", + "Certificate Info": "Certificate Info", + "Resolver Server(s)": "Resolver Server(s)", + "Resource Record Type": "Resource Record Type", + "Last Result": "Last Result", + "Create your admin account": "Create your admin account", + "Repeat Password": "Repeat Password", + "Incident description": "Incident description", + "Incident not found or access denied": "Incident not found or access denied", + "Past Incidents": "Past Incidents", + "Incident title": "Incident title", + "Import Backup": "Import Backup", + "Export Backup": "Export Backup", + "Export": "Export", + "Import": "Import", + "respTime": "Resp. Time (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Default enabled", + "Apply on all existing monitors": "Apply on all existing monitors", + "Create": "Create", + "Clear Data": "Clear Data", + "Events": "Events", + "Heartbeats": "Heartbeats", + "Auto Get": "Auto Get", + "Schedule maintenance": "Schedule maintenance", + "Affected Monitors": "Affected Monitors", + "Pick Affected Monitors...": "Pick Affected Monitors…", + "Start of maintenance": "Start of maintenance", + "All Status Pages": "All Status Pages", + "Select status pages...": "Select status pages…", + "alertNoFile": "Please select a file to import.", + "alertWrongFileType": "Please select a JSON file.", + "Clear all statistics": "Clear all Statistics", + "Skip existing": "Skip existing", + "Overwrite": "Overwrite", + "Options": "Options", + "Keep both": "Keep both", + "Verify Token": "Verify Token", + "Setup 2FA": "Set Up 2FA", + "Enable 2FA": "Enable 2FA", + "Disable 2FA": "Disable 2FA", + "2FA Settings": "2FA Settings", + "Two Factor Authentication": "Two Factor Authentication", + "filterActive": "Active", + "filterActivePaused": "Paused", + "Active": "Active", + "Inactive": "Inactive", + "Token": "Token", + "Show URI": "Show URI", + "Tags": "Tags", + "Add New Tag": "Add New Tag", + "Add Tags": "Add Tags", + "Add New below or Select...": "Add New below or Select…", + "Tag with this name already exist.": "Tag with this name already exists.", + "Tag with this value already exist.": "Tag with this value already exists.", + "tagAlreadyOnMonitor": "This tag (name and value) is already on the monitor or pending addition.", + "tagAlreadyStaged": "This tag (name and value) is already staged for this batch.", + "tagNameExists": "A system tag with this name already exists. Select it from the list or use a different name.", + "color": "Color", + "value (optional)": "value (optional)", + "Gray": "Gray", + "Red": "Red", + "Orange": "Orange", + "Green": "Green", + "Blue": "Blue", + "Indigo": "Indigo", + "Purple": "Purple", + "Pinned incidents are shown prominently on the status page": "Pinned incidents are shown prominently on the status page", + "Pink": "Pink", + "Custom": "Custom", + "Search...": "Search…", + "Search monitored sites": "Search monitored sites", + "Avg. Ping": "Avg. Ping", + "Avg. Response": "Avg. Response", + "Entry Page": "Entry Page", + "statusPageNothing": "Nothing here, please add a group or a monitor.", + "statusPageRefreshIn": "Refresh in: {0}", + "No Services": "No Services", + "All Systems Operational": "All Systems Operational", + "Partially Degraded Service": "Partially Degraded Service", + "Degraded Service": "Degraded Service", + "Add Group": "Add Group", + "Add a monitor": "Add a monitor", + "Edit Incident": "Edit Incident", + "Edit Status Page": "Edit Status Page", + "Go to Dashboard": "Go to Dashboard", + "Status Page": "Status Page", + "Status Pages": "Status Pages", + "defaultNotificationName": "My {notification} Alert ({number})", + "here": "here", + "Required": "Required", + "Post URL": "Post URL", + "Content Type": "Content Type", + "webhookJsonDesc": "{0} is good for any modern HTTP servers such as Express.js", + "webhookFormDataDesc": "{multipart} is good for PHP. The JSON will need to be parsed with {decodeFunction}", + "liquidIntroduction": "Templatability is achieved via the Liquid templating language. Please refer to the {0} for usage instructions.", + "templateAvailableVariables": "Available variables", + "example": "Example", + "Result": "Result", + "templateMsg": "message of the notification", + "templateHeartbeatJSON": "object describing the heartbeat", + "templateMonitorJSON": "object describing the monitor", + "templateLimitedToUpDownCertNotifications": "only available for UP/DOWN/Certificate expiry notifications", + "templateLimitedToUpDownNotifications": "only available for UP/DOWN notifications", + "templateServiceName": "service name", + "templateHostnameOrURL": "hostname or URL", + "templateStatus": "status", + "webhookAdditionalHeadersTitle": "Additional Headers", + "webhookAdditionalHeadersDesc": "Sets additional headers sent with the webhook. Each header should be defined as a JSON key/value.", + "webhookBodyPresetOption": "Preset - {0}", + "webhookBodyCustomOption": "Custom Body", + "Webhook URL": "Webhook URL", + "Application Token": "Application Token", + "Server URL": "Server URL", + "Priority": "Priority", + "emojiCheatSheet": "Emoji cheat sheet: {0}", + "Read more": "Read more", + "appriseInstalled": "Apprise is installed.", + "appriseNotInstalled": "Apprise is not installed. {0}", + "Method": "Method", + "Body": "Body", + "Headers": "Headers", + "PushUrl": "Push URL", + "HeadersInvalidFormatBecause": "The request headers are not valid JSON because {error}", + "BodyInvalidFormatBecause": "The request body is not valid JSON because {error}", + "Monitor History": "Monitor History", + "clearDataOlderThan": "Keep monitor history data for {0} days.", + "PasswordsDoNotMatch": "Passwords do not match.", + "records": "records", + "One record": "One record", + "steamApiKeyDescriptionAt": "For monitoring a Steam Game Server you need a Steam Web-API key. You can register your API key at {url}", + "topic": "Topic", + "topicExplanation": "MQTT topic to monitor", + "mqttWebSocketPath": "MQTT WebSocket Path", + "mqttWebsocketPathExplanation": "WebSocket path for MQTT over WebSocket connections (e.g., /mqtt)", + "mqttWebsocketPathInvalid": "Please use a valid WebSocket Path format", + "mqttHostnameTip": "Please use this format {hostnameFormat}", + "hostnameCannotBeIP": "DNS hostname cannot be an IP. Did you mean to use the resolver field?", + "invalidHostnameOrIP": "Invalid hostname or IP. Hostname must be a valid FQDN. Cannot use wildcard. Can have underscore, or end with a dot.", + "invalidDNSHostname": "Invalid hostname. Hostname must be a valid FQDN. Can be a wildcard, have underscore or end with a dot.", + "wildcardOnlyForDNS": "Wildcard hostnames are only supported for DNS monitors.", + "invalidURL": "Invalid URL", + "successKeyword": "Success Keyword", + "successKeywordExplanation": "MQTT Keyword that will be considered as success", + "recent": "Recent", + "Resolve": "Resolve", + "Resolved": "Resolved", + "Reset Token": "Reset Token", + "Done": "Done", + "Info": "Info", + "Security": "Security", + "Steam API Key": "Steam API Key", + "Shrink Database": "Shrink Database", + "shrinkDatabaseDescriptionSqlite": "Trigger database {vacuum} for SQLite. {auto_vacuum} is already enabled but this does not defragment the database nor repack individual database pages the way that the {vacuum} command does.", + "Pick a RR-Type...": "Pick a RR-Type…", + "Pick Accepted Status Codes...": "Pick Accepted Status Codes…", + "Default": "Default", + "HTTP Options": "HTTP Options", + "Create Incident": "Create Incident", + "Title": "Title", + "Content": "Content", + "Style": "Style", + "info": "info", + "warning": "warning", + "danger": "danger", + "error": "error", + "critical": "critical", + "primary": "primary", + "light": "light", + "dark": "dark", + "Post": "Post", + "Please input title and content": "Please input title and content", + "createdAt": "Created: {date}", + "lastUpdatedAt": "Last Updated: {date}", + "lastUpdatedAtFromNow": "Last Updated: {date} ({fromNow})", + "Switch to Light Theme": "Switch to Light Theme", + "Switch to Dark Theme": "Switch to Dark Theme", + "Show Tags": "Show Tags", + "Hide Tags": "Hide Tags", + "Description": "Description", + "descriptionHelpText": "Shown on the internal dashboard. Markdown is allowed and sanitized (preserves spaces and indentation) before display.", + "No monitors available.": "No monitors available.", + "Add one": "Add one", + "No Monitors": "No Monitors", + "Untitled Group": "Untitled Group", + "Services": "Services", + "Discard": "Discard", + "Cancel": "Cancel", + "auto-select": "Auto Select", + "Select": "Select", + "Actions": "Actions", + "selectedMonitorCountMsg": "selected: {n} | selected: {n}", + "selectMonitorMsg": "Select monitors to perform actions", + "selectAllMonitorsAria": "Select all monitors", + "deselectAllMonitorsAria": "Deselect all monitors", + "Check/Uncheck": "Check/Uncheck", + "Powered by": "Powered by", + "Customize": "Customize", + "Custom Footer": "Custom Footer", + "Custom CSS": "Custom CSS", + "deleteIncidentMsg": "Are you sure you want to delete this incident?", + "deleteStatusPageMsg": "Are you sure want to delete this status page?", + "Proxies": "Proxies", + "default": "Default", + "enabled": "Enabled", + "setAsDefault": "Set As Default", + "deleteProxyMsg": "Are you sure want to delete this proxy for all monitors?", + "proxyDescription": "Proxies must be assigned to a monitor to function.", + "enableProxyDescription": "This proxy will not effect on monitor requests until it is activated. You can control temporarily disable the proxy from all monitors by activation status.", + "setAsDefaultProxyDescription": "This proxy will be enabled by default for new monitors. You can still disable the proxy separately for each monitor.", + "Certificate Chain:": "Certificate Chain:", + "Valid": "Valid", + "Invalid": "Invalid", + "User": "User", + "Installed": "Installed", + "Not installed": "Not installed", + "Running": "Running", + "Not running": "Not running", + "Remove Token": "Remove Token", + "Start": "Start", + "Stop": "Stop", + "Add New Status Page": "Add New Status Page", + "Slug": "Slug", + "slug is not found": "Slug is not found", + "Accept characters:": "Accept characters:", + "startOrEndWithOnly": "Start or end with {0} only", + "No consecutive dashes": "No consecutive dashes", + "statusPageSpecialSlugDesc": "Special slug {0}: this page will be shown when no slug is provided", + "Next": "Next", + "The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.", + "No Proxy": "No Proxy", + "Authentication": "Authentication", + "HTTP Basic Auth": "HTTP Basic Auth", + "Bearer Token": "Bearer Token", + "New Status Page": "New Status Page", + "Page Not Found": "Page Not Found", + "Reverse Proxy": "Reverse Proxy", + "Backup": "Backup", + "About": "About", + "wayToGetCloudflaredURL": "(Download cloudflared from {0})", + "cloudflareWebsite": "Cloudflare Website", + "Message:": "Message:", + "Don't know how to get the token? Please read the guide:": "Don't know how to get the token? Please read the guide:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.", + "HTTP Headers": "HTTP Headers", + "Trust Proxy": "Trust Proxy", + "Other Software": "Other Software", + "For example: nginx, Apache and Traefik.": "For example: nginx, Apache and Traefik.", + "Please input content": "Please input content", + "Please input title": "Please input title", + "Please read": "Please read", + "Subject:": "Subject:", + "Valid To:": "Valid To:", + "Days Remaining:": "Days Remaining:", + "Issuer:": "Issuer:", + "Fingerprint:": "Fingerprint:", + "No status pages": "No status pages", + "Domain Name Expiry Notification": "Domain Name Expiry Notification", + "Add a new expiry notification day": "Add a new expiry notification day", + "Remove the expiry notification": "Remove the expiry notification day", + "Proxy": "Proxy", + "dateCreatedAtFromNow": "Date Created: {date} ({fromNow})", + "Footer Text": "Footer Text", + "RSS Title": "RSS Title", + "Leave blank to use status page title": "Leave blank to use status page title", + "Refresh Interval": "Refresh Interval", + "Refresh Interval Description": "The status page will do a full site refresh every {0} seconds", + "Show Powered By": "Show Powered By", + "Domain Names": "Domain Names", + "signedInDisp": "Signed in as {0}", + "signedInDispDisabled": "Auth Disabled.", + "RadiusSecret": "Radius Secret", + "RadiusSecretDescription": "Shared Secret between client and server", + "RadiusCalledStationId": "Called Station Id", + "RadiusCalledStationIdDescription": "Identifier of the called device", + "RadiusCallingStationId": "Calling Station Id", + "RadiusCallingStationIdDescription": "Identifier of the calling device", + "Certificate Expiry Notification": "Certificate Expiry Notification", + "certificateExpiryNotificationHelp": "The number of days in advance can be configured in the Settings.", + "Cloud ID": "Cloud ID", + "API Username": "API Username", + "API Key": "API Key", + "API Token": "API Token", + "Client Key": "Client Key", + "Channel": "Channel", + "See Jira Cloud Docs": "See Jira Cloud Docs", + "Show update if available": "Show update if available", + "Also check beta release": "Also check beta release", + "Using a Reverse Proxy?": "Using a Reverse Proxy?", + "Check how to config it for WebSocket": "Check how to config it for WebSocket", + "Steam Game Server": "Steam Game Server", + "Most likely causes:": "Most likely causes:", + "The resource is no longer available.": "The resource is no longer available.", + "There might be a typing error in the address.": "There might be a typing error in the address.", + "What you can try:": "What you can try:", + "Retype the address.": "Retype the address.", + "Go back to the previous page.": "Go back to the previous page.", + "Coming Soon": "Coming Soon", + "Connection String": "Connection String", + "Query": "Query", + "settingsCertificateExpiry": "TLS Certificate Expiry", + "certificationExpiryDescription": "HTTPS Monitors trigger notification when TLS certificate expires in:", + "certHostnameMismatch": "Certificate hostname does not match the monitor URL.", + "Setup Docker Host": "Set Up Docker Host", + "Connection Type": "Connection Type", + "Docker Daemon": "Docker Daemon", + "noDockerHostMsg": "Not Available. Set Up a Docker Host First.", + "DockerHostRequired": "Please set the Docker Host for this monitor.", + "deleteDockerHostMsg": "Are you sure want to delete this docker host for all monitors?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "tailscalePingWarning": "In order to use the Tailscale Ping monitor, you need to install Uptime Kuma without Docker and also install Tailscale client on your server.", + "sipsakPingWarning": "In order to use the SIP Options Ping monitor, you need to install Uptime Kuma without Docker and also install Sipsak client on your server.", + "Docker Container": "Docker Container", + "Container Name / ID": "Container Name / ID", + "Docker Host": "Docker Host", + "Docker Hosts": "Docker Hosts", + "Domain": "Domain", + "Workstation": "Workstation", + "Packet Size": "Packet Size", + "pingPacketSizeWarning": "A packet size smaller than 16 does not produce round-trip times on Linux", + "Bot Token": "Bot Token", + "wayToGetTelegramToken": "You can get a token from {0}.", + "wayToGetMaxToken": "You can get a MAX bot token and other details from {0}.", + "maxMessenger": "MAX messenger", + "Chat ID": "Chat ID", + "telegramMessageThreadID": "(Optional) Message Thread ID", + "telegramMessageThreadIDDescription": "Optional Unique identifier for the target message thread (topic) of the forum; for forum supergroups only", + "telegramSendSilently": "Send Silently", + "telegramSendSilentlyDescription": "Sends the message silently. Users will receive a notification with no sound.", + "telegramProtectContent": "Protect Forwarding/Saving", + "telegramProtectContentDescription": "If enabled, the bot messages in Telegram will be protected from forwarding and saving.", + "telegramUseTemplate": "Use custom message template", + "telegramUseTemplateDescription": "If enabled, the message will be sent using a custom template.", + "matrixUseTemplate": "Use custom message template", + "matrixUseTemplateDescription": "If enabled, the message will be sent using a custom template.", + "telegramTemplateFormatDescription": "Telegram allows using different markup languages for messages, see Telegram {0} for specifc details.", + "supportTelegramChatID": "Support Direct Chat / Group / Channel's Chat ID", + "wayToGetTelegramChatID": "You can get your chat ID by sending a message to the bot and going to this URL to view the chat_id:", + "maxApiUrlDescription": "Base API URL for MAX messenger. Default: https://platform-api.max.ru", + "wayToGetMaxChatID": "Specify the chat identifier in MAX where messages should be delivered.", + "maxUseTemplate": "Use custom message template", + "maxUseTemplateDescription": "If enabled, the message will be sent using a custom template.", + "maxTemplateFormatDescription": "MAX messenger supports plain text, Markdown and HTML formatting.", + "telegramServerUrl": "(Optional) Server Url", + "telegramServerUrlDescription": "To lift Telegram's bot api limitations or gain access in blocked areas (China, Iran, etc). For more information click {0}. Default: {1}", + "YOUR BOT TOKEN HERE": "YOUR BOT TOKEN HERE", + "chatIDNotFound": "Chat ID is not found; please send a message to this bot first", + "disableCloudflaredNoAuthMsg": "You are in No Auth mode, a password is not required.", + "trustProxyDescription": "Trust 'X-Forwarded-*' headers. If you want to get the correct client IP and your Uptime Kuma is behind a proxy such as Nginx or Apache, you should enable this.", + "wayToGetLineNotifyToken": "You can get an access token from {0}", + "Examples:": "Examples: {0}", + "supportBaleChatID": "Support Direct Chat / Group / Channel's Chat ID", + "wayToGetBaleChatID": "You can get your chat ID by sending a message to the bot and going to this URL to view the chat_id:", + "wayToGetBaleToken": "You can get a token from {0}.", + "Home Assistant URL": "Home Assistant URL", + "Long-Lived Access Token": "Long-Lived Access Token", + "Indigo URL": "Indigo URL", + "indigoUrlHelptext": "Your Indigo Reflector address (https://NAME.indigodomo.net) or the Indigo Web Server on your network (https://IP:8176).", + "indigoIgnoreTlsErrorHelptext": "Needed for the Indigo Web Server on your network, which uses a self-signed certificate. Not needed for the Reflector.", + "indigoApiKeyHelptext": "An API key from your Indigo Account (Authorizations), or a local secret for the Web Server on your network.", + "Variable ID": "Variable ID", + "indigoVariableIdHelptext": "The alert message is written to this variable.", + "Action Group ID": "Action Group ID", + "indigoActionGroupIdHelptext": "Executed after the variable is updated. Set a variable, an action group, or both. Copy IDs from Indigo with right-click → Copy ID.", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token.", + "Notification Action": "Notification Action", + "default: notify all devices": "default: notify all devices", + "homeAssistantNotificationActionHelptext": "A list of Notification Actions can be found in Home Assistant under \"Settings > Developer Tools > Actions\". Search for \"notify\" to find your actions. Enter only the part after \"notify.\", e.g. for the action \"notify.mobile_app_xyz\" enter \"mobile_app_xyz\". For built-in mobile notifications, look for \"Send a notification via mobile_app_xyz\" (not \"Send a notification\").", + "Automations can optionally be triggered in Home Assistant:": "Automations can optionally be triggered in Home Assistant:", + "Trigger type:": "Trigger type:", + "Event type:": "Event type:", + "Event data:": "Event data:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Then choose an action, for example switch the scene to where an RGB light is red.", + "frontendVersionIs": "Frontend Version: {version}", + "Frontend Version do not match backend version!": "Frontend Version do not match backend version!", + "backupOutdatedWarning": "Deprecated: Since a lot of features were added and this backup feature is a bit unmaintained, it cannot generate or restore a complete backup.", + "backupRecommend": "Please backup the volume or the data folder (./data/) directly instead.", + "Optional": "Optional", + "and": "and", + "or": "or", + "sameAsServerTimezone": "Same as Server Timezone", + "startDateTime": "Start Date/Time", + "endDateTime": "End Date/Time", + "cronExpression": "Cron Expression", + "cronScheduleDescription": "Schedule: {description}", + "Duration (Minutes)": "Duration (Minutes)", + "invalidCronExpression": "Invalid Cron Expression: {0}", + "recurringInterval": "Interval", + "Recurring": "Recurring", + "strategyManual": "Active/Inactive Manually", + "warningTimezone": "It is using the server's timezone", + "weekdayShortMon": "Mon", + "weekdayShortTue": "Tue", + "weekdayShortWed": "Wed", + "weekdayShortThu": "Thu", + "weekdayShortFri": "Fri", + "weekdayShortSat": "Sat", + "weekdayShortSun": "Sun", + "dayOfWeek": "Day of Week", + "dayOfMonth": "Day of Month", + "lastDay": "Last Day", + "lastDay1": "Last Day of Month", + "lastDay2": "2nd Last Day of Month", + "lastDay3": "3rd Last Day of Month", + "lastDay4": "4th Last Day of Month", + "No Maintenance": "No Maintenance", + "pauseMaintenanceMsg": "Are you sure want to pause?", + "maintenanceStatus-under-maintenance": "Under Maintenance", + "maintenanceStatus-inactive": "Inactive", + "maintenanceStatus-scheduled": "Scheduled", + "maintenanceStatus-ended": "Ended", + "maintenanceStatus-unknown": "Unknown", + "Display Timezone": "Display Timezone", + "Server Timezone": "Server Timezone", + "statusPageMaintenanceEndDate": "End", + "IconUrl": "Icon URL", + "Enable DNS Cache": "(Deprecated) Enable DNS Cache for HTTP(s) monitors", + "Enable": "Enable", + "Disable": "Disable", + "enableNSCD": "Enable NSCD (Name Service Cache Daemon) for caching all DNS requests", + "chromeExecutable": "Chrome/Chromium Executable", + "chromeExecutableAutoDetect": "Auto Detect", + "chromeExecutableDescription": "For Docker users, if Chromium is not yet installed, it may take a few minutes to install and display the test result. It takes 1GB of disk space.", + "dnsCacheDescription": "It may be not working in some IPv6 environments, disable it if you encounter any issues.", + "Single Maintenance Window": "Single Maintenance Window", + "Maintenance Time Window of a Day": "Maintenance Time Window of a Day", + "Effective Date Range": "Effective Date Range (Optional)", + "Schedule Maintenance": "Schedule Maintenance", + "Edit Maintenance": "Edit Maintenance", + "Clone Maintenance": "Clone Maintenance", + "ariaPauseMaintenance": "Pause this maintenance schedule", + "ariaResumeMaintenance": "Resume this maintenance schedule", + "ariaCloneMaintenance": "Create a copy of this maintenance schedule", + "ariaEditMaintenance": "Edit this maintenance schedule", + "ariaDeleteMaintenance": "Delete this maintenance schedule", + "Date and Time": "Date and Time", + "DateTime Range": "DateTime Range", + "loadingError": "Cannot fetch the data, please try again later.", + "plugin": "Plugin | Plugins", + "install": "Install", + "installing": "Installing", + "uninstall": "Uninstall", + "uninstalling": "Uninstalling", + "confirmUninstallPlugin": "Are you sure want to uninstall this plugin?", + "notificationRegional": "Regional", + "notificationUniversal": "Universal", + "notificationChatPlatforms": "Chat Platforms", + "notificationPushServices": "Push Services", + "notificationSmsServices": "SMS Services", + "notificationEmail": "Email", + "notificationIncidentManagement": "Incident Management", + "notificationHomeAutomation": "Home Automation", + "notificationOther": "Other Integrations", + "Clone Monitor": "Clone Monitor", + "Clone": "Clone", + "cloneOf": "Clone of {0}", + "smtp": "Email (SMTP)", + "SMTP Security": "SMTP Security", + "Ignore STARTTLS": "Ignore STARTTLS", + "Use STARTTLS": "Use STARTTLS", + "Use HTML for custom E-mail body": "Use HTML for custom E-mail body", + "secureOptionNone": "None / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignore TLS Error", + "Disable STARTTLS": "Disable STARTTLS", + "disableSTARTTLSDescription": "Enable this option for SMTP servers that do not support STARTTLS. This will send emails over an unencrypted connection.", + "From Email": "From Email", + "emailCustomisableContent": "Customisable content", + "smtpLiquidIntroduction": "The following two fields are templatable via the Liquid templating Language. Please refer to the {0} for usage instructions. These are the available variables:", + "emailCustomSubject": "Custom Subject", + "leave blank for default subject": "leave blank for default subject", + "emailCustomBody": "Custom Body", + "leave blank for default body": "leave blank for default body", + "emailTemplateMonitorJSON": "object describing the monitor", + "emailTemplateHeartbeatJSON": "object describing the heartbeat", + "emailTemplateMsg": "message of the notification", + "emailTemplateLimitedToUpDownNotification": "only available for UP/DOWN heartbeats, otherwise null", + "To Email": "To Email", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord Webhook URL", + "wayToGetDiscordURL": "You can get this by going to Server Settings -> Integrations -> View Webhooks -> New Webhook", + "Bot Display Name": "Bot Display Name", + "Prefix Custom Message": "Prefix Custom Message", + "Hello @everyone is...": "Hello {'@'}everyone is…", + "Select message type": "Select message type", + "Send to channel": "Send to channel", + "Create new forum post": "Create new forum post", + "postToExistingThread": "Post to existing thread / forum post", + "forumPostName": "Forum post name", + "threadForumPostID": "Thread / Forum post ID", + "e.g. {discordThreadID}": "e.g. {discordThreadID}", + "whatHappensAtForumPost": "Create a new forum post. This does NOT post messages in existing post. To post in existing post use \"{option}\"", + "wayToGetDiscordThreadId": "Getting a thread / forum post id is similar to getting a channel id. Read more about how to get ids {0}", + "wayToGetTeamsURL": "You can learn how to create a webhook URL {0}.", + "teamsEnableTags": "Include tags", + "teamsEnableTagsDescription": "If enabled, the message will include the monitor tags.", + "wayToGetZohoCliqURL": "You can learn how to create a webhook URL {0}.", + "needSignalAPI": "You need to have a signal client with REST API.", + "wayToCheckSignalURL": "You can check this URL to view how to set one up:", + "Number": "Number", + "Recipients": "Recipients", + "Access Token": "Access Token", + "API Version": "API Version", + "Channel access token": "Channel access token", + "Channel access token (Long-lived)": "Channel access token (Long-lived)", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console - {0}", + "Basic Settings": "Basic Settings", + "User ID": "User ID", + "Your User ID": "Your user ID", + "Messaging API": "Messaging API", + "wayToGetLineChannelToken": "First access the {0}, create a provider and channel (Messaging API), then you can get the channel access token and user ID from the above mentioned menu items.", + "Icon URL": "Icon URL", + "aboutIconURL": "You can provide a link to a picture in \"Icon URL\" to override the default profile picture. Will not be used if Icon Emoji is set.", + "aboutMattermostChannelName": "You can override the default channel that the Webhook posts to by entering the channel name into \"Channel Name\" field. This needs to be enabled in the Mattermost Webhook settings. Ex: #other-channel", + "dataRetentionTimeError": "Retention period must be 0 or greater", + "infiniteRetention": "Set to 0 for infinite retention.", + "confirmDeleteTagMsg": "Are you sure you want to delete this tag? Monitors associated with this tag will not be deleted.", + "enableGRPCTls": "Allow to send gRPC request with TLS connection", + "grpcMethodDescription": "Method name is convert to camelCase format such as sayHello, check, etc.", + "acceptedStatusCodesDescription": "Select status codes which are considered as a successful response.", + "deleteMonitorMsg": "Are you sure want to delete this monitor?", + "deleteMonitorsMsg": "Are you sure you want to delete the selected monitors?", + "pausedMonitorsMsg": "Paused {n} monitor | Paused {n} monitors", + "resumedMonitorsMsg": "Resumed {n} monitor | Resumed {n} monitors", + "deletedMonitorsMsg": "Deleted {n} monitor | Deleted {n} monitors", + "noMonitorsPausedMsg": "No monitors paused (none were active)", + "noMonitorsResumedMsg": "No monitors resumed (none were inactive)", + "bulkDeleteErrorMsg": "Failed to delete {n} monitor | Failed to delete {n} monitors", + "deleteGroupMsg": "Are you sure you want to delete this group?", + "deleteChildrenMonitors": "Also delete the direct child monitors and its children if it has any | Also delete all {count} direct child monitors and their children if they have any", + "deleteMaintenanceMsg": "Are you sure want to delete this maintenance?", + "deleteNotificationMsg": "Are you sure want to delete this notification for all monitors?", + "dnsPortDescription": "DNS server port. Defaults to 53. You can change the port at any time.", + "resolverserverDescription": "Cloudflare is the default server. You can specify a comma delimited list of IP addresses or hostnames.", + "rrtypeDescription": "Select the RR type you want to monitor", + "pauseMonitorMsg": "Are you sure want to pause?", + "enableDefaultNotificationDescription": "This notification will be enabled by default for new monitors. You can still disable the notification separately for each monitor.", + "Clear All Events": "Clear All Events", + "clearAllEventsMsg": "Are you sure you want to delete all events for all monitors? This permanently removes associated message data and cannot be undone.", + "Events cleared successfully": "Events cleared successfully.", + "No monitors found": "No monitors found.", + "Could not clear events": "Could not clear {failed}/{total} events", + "clearEventsMsg": "Are you sure you want to delete all events for this monitor? This permanently removes associated message data and cannot be undone.", + "clearHeartbeatsMsg": "Are you sure you want to delete all heartbeats for this monitor? This permanently removes all heartbeat data and cannot be undone.", + "confirmClearStatisticsMsg": "Are you sure you want to delete ALL statistics?", + "importHandleDescription": "Choose 'Skip existing' if you want to skip every monitor or notification with the same name. 'Overwrite' will delete every existing monitor and notification.", + "confirmImportMsg": "Are you sure you want to import the backup? Please verify you've selected the correct import option.", + "twoFAVerifyLabel": "Please enter your token to verify 2FA:", + "tokenValidSettingsMsg": "Token is valid! You can now save the 2FA settings.", + "confirmEnableTwoFAMsg": "Are you sure you want to enable 2FA?", + "confirmDisableTwoFAMsg": "Are you sure you want to disable 2FA?", + "recurringIntervalMessage": "Run once every day | Run once every {0} days", + "affectedMonitorsDescription": "Select monitors that are affected by current maintenance", + "affectedStatusPages": "Show this maintenance message on selected status pages", + "Sets end time based on start time": "Sets end time based on start time", + "Please set start time first": "Please set start time first", + "noMonitorsSelectedWarning": "You are creating a maintenance without any affected monitors. Are you sure you want to continue?", + "noMonitorsOrStatusPagesSelectedError": "Cannot create maintenance without affected monitors or status pages", + "passwordNotMatchMsg": "The repeat password does not match.", + "notificationDescription": "Notifications must be assigned to a monitor to function.", + "Peer ID": "Peer ID", + "vkApiVersionDescription": "VK API version used for requests. Leave the default unless you specifically need another VK API version for compatibility.", + "vkDontParseLinks": "Disable link snippets", + "vkDontParseLinksDescription": "If enabled, VK will not generate link previews/snippets.", + "vkPeerIdDescription": "Enter the target VK peer_id. This value is sent to the API as-is.", + "keywordDescription": "Search keyword in plain HTML or JSON response. The search is case-sensitive.", + "invertKeywordDescription": "Look for the keyword to be absent rather than present.", + "jsonQueryDescription": "Parse and extract specific data from the server's JSON response using JSON query or use \"$\" for the raw response, if not expecting JSON. The result is then compared to the expected value, as strings. See {0} for documentation and use {1} to experiment with queries.", + "backupDescription": "You can backup all monitors and notifications into a JSON file.", + "backupDescription2": "Note: history and event data is not included.", + "backupDescription3": "Sensitive data such as notification tokens are included in the export file; please store export securely.", + "octopushAPIKey": "\"API key\" from HTTP API credentials in control panel", + "octopushLogin": "\"Login\" from HTTP API credentials in control panel", + "promosmsLogin": "API Login Name", + "promosmsPassword": "API Password", + "pushoversounds pushover": "Pushover (default)", + "pushoversounds bike": "Bike", + "pushoversounds bugle": "Bugle", + "pushoversounds cashregister": "Cash Register", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Cosmic", + "pushoversounds falling": "Falling", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Incoming", + "pushoversounds intermission": "Intermission", + "pushoversounds magic": "Magic", + "pushoversounds mechanical": "Mechanical", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Siren", + "pushoversounds spacealarm": "Space Alarm", + "pushoversounds tugboat": "Tug Boat", + "pushoversounds alien": "Alien Alarm (long)", + "pushoversounds climb": "Climb (long)", + "pushoversounds persistent": "Persistent (long)", + "pushoversounds echo": "Pushover Echo (long)", + "pushoversounds updown": "Up Down (long)", + "pushoversounds vibrate": "Vibrate Only", + "pushoversounds none": "None (silent)", + "pushyAPIKey": "Secret API Key", + "pushyToken": "Device token", + "apprise": "Apprise (Support 50+ Notification services)", + "GoogleChat": "Google Chat (Google Workspace only)", + "Google Apps Script Webhook URL": "Google Apps Script Webhook URL", + "Deploy a Google Apps Script as a web app and paste the URL here": "Deploy a Google Apps Script as a web app and paste the URL here", + "Quick Setup Guide": "Quick Setup Guide", + "Open your Google Spreadsheet": "Open your Google Spreadsheet", + "Go to Extensions → Apps Script": "Go to Extensions → Apps Script", + "Paste the script code (see below)": "Paste the script code (see below)", + "Click Deploy → New deployment → Web app": "Click Deploy → New deployment → Web app", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Set 'Execute as: Me' and 'Who has access: Anyone'", + "Copy the web app URL and paste it above": "Copy the web app URL and paste it above", + "Google Apps Script Code": "Google Apps Script Code", + "Copy to Clipboard": "Copy to Clipboard", + "Copied to clipboard!": "Copied to clipboard!", + "Failed to copy to clipboard": "Failed to copy to clipboard", + "Template plain text instead of using cards": "Template plain text instead of using cards", + "issueWithGoogleChatOnAndroidHelptext": "This also allows to get around bugs upstream like {issuetackerURL}", + "wayToGetKookBotToken": "Create application and get your bot token at {0}", + "wayToGetKookGuildID": "Switch on 'Developer Mode' in Kook setting, and right click the guild to get its ID", + "Guild ID": "Guild ID", + "User Key": "User Key", + "Device": "Device", + "Message Title": "Message Title", + "Notification Sound": "Notification Sound", + "More info on:": "More info on: {0}", + "pushoverDesc1": "Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour.", + "pushoverDesc2": "If you want to send notifications to different devices, fill out Device field.", + "pushoverMessageTtl": "Message TTL (Seconds)", + "SMS Type": "SMS Type", + "octopushTypePremium": "Premium (Fast - recommended for alerting)", + "octopushTypeLowCost": "Low Cost (Slow - sometimes blocked by operator)", + "checkPriceAt": "Check {service} prices at {url}", + "apiCredentials": "API credentials", + "octopushLegacyHint": "Do you use the legacy version of Octopush (2011-2020) or the new version?", + "Check octopush prices": "Check octopush prices {0}.", + "octopushPhoneNumber": "Phone number (intl format, eg : +33612345678)", + "octopushSMSSender": "SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Device ID", + "Apprise URL": "Apprise URL", + "Example:": "Example: {0}", + "Read more:": "Read more: {0}", + "Status:": "Status: {0}", + "Strategy": "Strategy", + "Free Mobile User Identifier": "Free Mobile User Identifier", + "Free Mobile API Key": "Free Mobile API Key", + "Enable TLS": "Enable TLS", + "Proto Service Name": "Proto Service Name", + "Proto Method": "Proto Method", + "Proto Content": "Proto Content", + "Economy": "Economy", + "Lowcost": "Lowcost", + "high": "high", + "SendKey": "SendKey", + "SMSManager API Docs": "SMSManager API Docs", + "Gateway Type": "Gateway Type", + "You can divide numbers with commas or semicolons": "You can divide numbers with {comma} or {semicolon}", + "Base URL": "Base URL", + "goAlertInfo": "GoAlert is a An open source application for on-call scheduling, automated escalations and notifications (like SMS or voice calls). Automatically engage the right person, the right way, and at the right time! {0}", + "goAlertIntegrationKeyInfo": "Get generic API integration key for the service in this format \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" usually the value of token parameter of copied URL.", + "AccessKeyId": "AccessKey ID", + "SecretAccessKey": "AccessKey Secret", + "PhoneNumbers": "PhoneNumbers", + "TemplateCode": "TemplateCode", + "SignName": "SignName", + "OptionalParameters": "Optional Parameters", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Due to carrier restrictions, enable optional variables at the risk of non-delivery", + "aliyun-template-requirements-and-parameters": "The aliyun SMS template must contain parameters: {parameters}", + "aliyun-template-optional-parameters": "Optional parameters: {parameters}", + "Bark API Version": "Bark API Version", + "Bark Endpoint": "Bark Endpoint", + "Bark Group": "Bark Group", + "Bark Sound": "Bark Sound", + "WebHookUrl": "WebHookUrl", + "SecretKey": "SecretKey", + "For safety, must use secret key": "For safety, must use secret key", + "Mentioning": "Mentioning", + "Don't mention people": "Don't mention people", + "Mention group": "Mention {group}", + "Mention Mobile List": "Mention mobile list", + "Mention User List": "Mention user id list", + "Dingtalk Mobile List": "Mobile list", + "Dingtalk User List": "User ID list", + "Enter a list of userId": "Enter a list of userId", + "Enter a list of mobile": "Enter a list of mobile", + "Invalid mobile": "Invalid mobile [{mobile}]", + "Invalid userId": "Invalid userId [{userId}]", + "Device Token": "Device Token", + "Platform": "Platform", + "Huawei": "Huawei", + "High": "High", + "Retry": "Retry", + "Topic": "Topic", + "WeCom Bot Key": "WeCom Bot Key", + "WeCom Mentioned Mobile List": "WeCom Mentioned Mobile List", + "WeCom Mentioned Mobile List Description": "Enter phone numbers to mention. Separate multiple numbers with commas. Use {'@'}all to mention everyone.", + "Setup Proxy": "Set Up Proxy", + "Proxy Protocol": "Proxy Protocol", + "Proxy Server": "Proxy Server", + "Proxy server has authentication": "Proxy server has authentication", + "promosmsTypeEco": "SMS ECO - cheap but slow and often overloaded. Limited only to Polish recipients.", + "promosmsTypeFlash": "SMS FLASH - Message will automatically show on recipient device. Limited only to Polish recipients.", + "promosmsTypeFull": "SMS FULL - Premium tier of SMS, You can use your Sender Name (You need to register name first). Reliable for alerts.", + "promosmsTypeSpeed": "SMS SPEED - Highest priority in system. Very quick and reliable but costly (about twice of SMS FULL price).", + "promosmsPhoneNumber": "Phone number (for Polish recipient You can skip area codes)", + "promosmsSMSSender": "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "promosmsAllowLongSMS": "Allow long SMS", + "Feishu WebHookUrl": "Feishu WebHookURL", + "matrixHomeserverURL": "Homeserver URL (with http(s):// and optionally port)", + "Internal Room Id": "Internal Room ID", + "matrixDesc1": "You can find the internal room ID by looking in the advanced section of the room settings in your Matrix client. It should look like !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "It is highly recommended you create a new user and do not use your own Matrix user's access token as it will allow full access to your account and all the rooms you joined. Instead, create a new user and only invite it to the room that you want to receive the notification in. You can get the access token by running {0}", + "Channel Name": "Channel Name", + "Notify Channel": "Notify Channel", + "aboutNotifyChannel": "Notify channel will trigger a desktop or mobile notification for all members of the channel, whether their availability is set to active or away.", + "Uptime Kuma URL": "Uptime Kuma URL", + "setup a new monitor group": "set up a new monitor group", + "openModalTo": "open modal to {0}", + "Add a domain": "Add a domain", + "Remove domain": "Remove domain '{0}'", + "Icon Emoji": "Icon Emoji", + "signalImportant": "IMPORTANT: You cannot mix groups and numbers in recipients!", + "signalUseTemplate": "Use custom message template", + "signalUseTemplateDescription": "If enabled, the message will be sent using a custom template. You can use Liquid templating to customize the notification format.", + "aboutWebhooks": "More info about Webhooks on: {0}", + "aboutJiraCloudId": "More info about Jira Cloud ID: {0}", + "see Jira Cloud Docs": "see Jira Cloud Docs", + "Jira Service Management": "Jira Service Management", + "aboutSlackUsername": "Changes the display name of the message sender. If you want to mention someone, include it in the friendly name instead.", + "slackIncludeGroupName": "Include monitor group name", + "slackIncludeGroupNameDescription": "If enabled, the monitor group path will be included in notifications to help distinguish monitors with the same name across different groups.", + "slackUseTemplate": "Use custom message template", + "slackUseTemplateDescription": "If enabled, the message will be sent using a custom template. You can use Liquid templating to include monitor group information via monitorJSON.path or monitorJSON.pathName.", + "aboutChannelName": "Enter the channel name on {0} Channel Name field if you want to bypass the Webhook channel. Ex: #other-channel", + "aboutKumaURL": "If you leave the Uptime Kuma URL field blank, it will default to the Project GitHub page.", + "smtpAdditionalHeadersTitle": "Additional Headers", + "smtpAdditionalHeadersDesc": "Please refer to the Nodemailer Custom Headers {0} for usage.", + "smtpDkimSettings": "DKIM Settings", + "smtpDkimDesc": "Please refer to the Nodemailer DKIM {0} for usage.", + "documentation": "documentation", + "smtpDkimDomain": "Domain Name", + "smtpDkimKeySelector": "Key Selector", + "smtpDkimPrivateKey": "Private Key", + "smtpDkimHashAlgo": "Hash Algorithm (Optional)", + "smtpDkimheaderFieldNames": "Header Keys to sign (Optional)", + "smtpDkimskipFields": "Header Keys not to sign (Optional)", + "wayToGetPagerDutyKey": "You can get this by going to Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Here you can search for \"Events API V2\". More info {0}", + "Integration Key": "Integration Key", + "Integration URL": "Integration URL", + "Auto resolve or acknowledged": "Auto resolve or acknowledged", + "do nothing": "do nothing", + "auto acknowledged": "auto acknowledged", + "auto resolve": "auto resolve", + "alertaApiEndpoint": "API Endpoint", + "alertaEnvironment": "Environment", + "alertaApiKey": "API Key", + "alertaAlertState": "Alert State", + "alertaRecoverState": "Recover State", + "serwersmsAPIUser": "API Username (incl. webapi_ prefix)", + "serwersmsAPIPassword": "API Password", + "serwersmsPhoneNumber": "Phone number", + "serwersmsRecipientType": "Recipient type", + "serwersmsRecipientTypePhone": "Phone number", + "serwersmsRecipientTypeGroup": "Group", + "serwersmsGroupId": "Group ID", + "serwersmsGroupIdHelptext": "ID or group IDs in the Customer Panel. These identifiers can be downloaded using action groups / index or by copying them from the editing group in the Customer Panel.", + "serwersmsSenderName": "SMS Sender Name (registered via customer portal)", + "smseagleTo": "Phone number(s)", + "smseagleGroup": "Phonebook group name(s)", + "smseagleContact": "Phonebook contact name(s)", + "smseagleGroupV2": "Phonebook group ID(s)", + "smseagleContactV2": "Phonebook contact ID(s)", + "smseagleRecipientType": "Recipient type", + "smseagleRecipient": "Recipient(s) (multiple must be separated with comma)", + "smseagleToken": "API Access token", + "smseagleUrl": "Your SMSEagle device URL", + "smseagleEncoding": "Send as Unicode (default=GSM-7)", + "smseaglePriority": "Message priority (0-9, highest priority = 9)", + "smseagleMsgType": "Message type", + "smseagleMsgSms": "Sms message (default)", + "smseagleMsgRing": "Ring call", + "smseagleMsgTts": "Text-to-speech call", + "smseagleMsgTtsAdvanced": "Text-to-speech Advanced call", + "smseagleDuration": "Duration (in seconds)", + "smseagleTtsModel": "Text-to-speech model ID", + "smseagleApiType": "API version", + "smseagleApiv1": "APIv1 (for existing projects and backward compatibility)", + "smseagleApiv2": "APIv2 (recommended for new integrations)", + "smseagleDocs": "Check documentation or APIv2 availability: {0}", + "smseagleComma": "Multiple must be separated with comma", + "smspartnerApiurl": "You can find your API key in your dashboard at {0}", + "smspartnerPhoneNumber": "Phone number(s)", + "smspartnerPhoneNumberHelptext": "The number must be in the international format {0}, {1}. Multiple numbers must be separated by {2}", + "smspartnerSenderName": "SMS Sender Name", + "smspartnerSenderNameInfo": "Must be between 3..=11 regular characters", + "Recipient Number": "Recipient Number", + "Access Key": "Access Key", + "ooredooUsernameDescription": "The email address you use to sign in to the Ooredoo bulk SMS dashboard.", + "ooredooAccessKeyDescription": "Found under \"API Access Key\" on your Ooredoo SMS dashboard. Paste it exactly as shown — it is base64-encoded automatically before sending.", + "ooredooBearerTokenDescription": "The token shown after \"Authorization: Bearer\" in the sample cURL command on your Ooredoo SMS dashboard.", + "ooredooRecipientDescription": "Enter one or more numbers separated by a comma or space. Local 7-digit numbers are automatically prefixed with 960.", + "Leave blank to use the default Ooredoo endpoint.": "Leave blank to use the default Ooredoo endpoint.", + "From Name/Number": "From Name/Number", + "Leave blank to use a shared sender number.": "Leave blank to use a shared sender number.", + "Octopush API Version": "Octopush API Version", + "octopushEndpoint": "octopush (endpoint: {url})", + "legacyOctopushEndpoint": "Legacy Octopush-DM (endpoint: {url})", + "ntfy Topic": "ntfy Topic", + "Server URL should not contain the nfty topic": "Server URL should not contain the nfty topic", + "onebotHttpAddress": "OneBot HTTP Address", + "onebotMessageType": "OneBot Message Type", + "onebotGroupMessage": "Group", + "onebotPrivateMessage": "Private", + "onebotUserOrGroupId": "Group/User ID", + "onebotSafetyTips": "For safety, must set access token", + "PushDeer Server": "PushDeer Server", + "pushDeerServerDescription": "Leave blank to use the official server", + "PushDeer Key": "PushDeer Key", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) delivers through the standalone WxPusher app across 6 platforms — Android, iOS, HarmonyOS, Windows, macOS and Linux ({download}). Scan the QR code below to get your SPT and you are ready to send. Separate multiple SPTs with a comma (,) — see the {docs} for more.", + "wxPusherDownloadApp": "download the app", + "wxPusherDocs": "official guide", + "SpugPush Template Code": "Template Code", + "wayToGetClickSendSMSToken": "You can get API Username and API Key from {here}.", + "wayToGetEgoSMSToken": "You can get your EgoSMS API username and password from {here}.", + "egosmsSenderDescription": "Sender ID shown to recipients (max 11 characters). Defaults to EGOSMS if empty.", + "egosmsPhoneNumberDescription": "Recipient number with country code, without + or 00 (e.g. 2567XXXXXXXX).", + "Hash Key": "Hash Key", + "wayToGetBearSMSToken": "You can get your BearSMS API username and hash key from {0}.", + "bearsmsPhoneNumberDescription": "Recipient number with country code, without + or 00 (e.g. 9725XXXXXXXX).", + "bearsmsSenderDescription": "Optional sender ID shown to recipients (max 11 characters). It must be approved in your BearSMS account, otherwise the account's default sender is used.", + "Custom Monitor Type": "Custom Monitor Type", + "Google Analytics ID": "Google Analytics ID", + "Analytics Type": "Analytics Type", + "Analytics ID": "Analytics ID", + "Analytics Script URL": "Analytics Script URL", + "Edit Tag": "Edit Tag", + "WebSocket Options": "WebSocket Options", + "Server Address": "Server Address", + "Learn More": "Learn More", + "Body Encoding": "Body Encoding", + "API Keys": "API Keys", + "Expiry": "Expiry", + "Expiry date": "Expiry date", + "Don't expire": "Don't expire", + "Continue": "Continue", + "Add Another": "Add Another", + "Key Added": "Key Added", + "apiKeyAddedMsg": "Your API key has been added. Please make a note of it as it will not be shown again.", + "Add API Key": "Add API Key", + "No API Keys": "No API Keys", + "apiKey-active": "Active", + "apiKey-expired": "Expired", + "apiKey-inactive": "Inactive", + "Expires": "Expires", + "disableAPIKeyMsg": "Are you sure you want to disable this API key?", + "deleteAPIKeyMsg": "Are you sure you want to delete this API key?", + "Generate": "Generate", + "pagertreeIntegrationUrl": "Integration URL", + "pagertreeUrgency": "Urgency", + "pagertreeSilent": "Silent", + "pagertreeLow": "Low", + "pagertreeMedium": "Medium", + "pagertreeHigh": "High", + "pagertreeCritical": "Critical", + "pagertreeResolve": "Auto Resolve", + "pagertreeDoNothing": "Do Nothing", + "wayToGetPagerTreeIntegrationURL": "After creating the Uptime Kuma integration in PagerTree, copy the Endpoint. See full details {0}", + "lunaseaTarget": "Target", + "lunaseaDeviceID": "Device ID", + "lunaseaUserID": "User ID", + "ntfyAuthenticationMethod": "Authentication Method", + "ntfyPriorityHelptextAllEvents": "All events are sent with the maximum priority", + "ntfyPriorityHelptextAllExceptDown": "All events are sent with this priority, except {0}-events, which have a priority of {1}", + "ntfyPriorityHelptextPriorityHigherThanDown": "Regular priority should be higher than {0} priority. Priority {1} is higher than {0} priority {2}", + "ntfyPriorityDown": "Priority for DOWN-events", + "ntfyUsernameAndPassword": "Username and Password", + "ntfyCall": "Phone Call", + "ntfyCallHelptext": "Make a phone call when alert fires. Set to 'yes' to use your first verified number, or enter a specific phone number (e.g. +12223334444). Requires ntfy Pro and a verified phone number.", + "ntfyUseTemplate": "Customize notification templates", + "ntfyUseTemplateDescription": "Enable this to customize notification titles and messages using LiquidJS templating", + "ntfyCustomTitle": "Custom Title Template", + "ntfyCustomMessage": "Custom Message Template", + "ntfyNotificationTemplateFallback": "Leave blank to use the default Uptime Kuma format", + "Device ID or Group ID": "Device ID or Group ID", + "notifyAppDeviceIdDescription": "The Device ID from the Notify! app, or a Group ID to alert every device in a group at once.", + "notifyAppSetup": "Open Notify! and find your Device ID and token under Settings. No account is required. Learn more at {0}.", + "telnyxApiKey": "API Key (V2)", + "telnyxApiKeyHelptext": "Your Telnyx V2 API key from the Telnyx Portal under API Keys", + "telnyxPhoneNumber": "From Number", + "telnyxPhoneNumberHelptext": "Your Telnyx phone number to send from (must be registered in your Telnyx account, e.g. +15551234567)", + "telnyxToNumber": "To Number", + "telnyxToNumberHelptext": "The destination phone number to receive SMS notifications (e.g. +15559876543)", + "telnyxMessagingProfileId": "Messaging Profile ID (optional)", + "telnyxMessagingProfileIdHelptext": "Optionally associate a Telnyx Messaging Profile to control sender settings and features", + "plivoAuthID": "Auth ID", + "plivoAuthIDHelptext": "Your Plivo Auth ID from the Plivo Console", + "plivoAuthToken": "Auth Token", + "plivoAuthTokenHelptext": "Your Plivo Auth Token from the Plivo Console", + "plivoFromNumber": "From Number", + "plivoFromNumberHelptext": "The Plivo phone number to send from (e.g. +15551234567). SMS may also use an approved alphanumeric sender ID; voice calls require a Plivo phone number.", + "plivoToNumber": "To Number", + "plivoToNumberHelptext": "The destination phone number that receives the SMS or voice call (e.g. +15559876543)", + "plivoMessageType": "Message Type", + "plivoVoiceCall": "Voice Call", + "plivoAnswerUrl": "Answer URL", + "plivoAnswerUrlHelptext": "URL that returns Plivo XML for the call. The alert text is appended as a ?message= query parameter, so your endpoint can return e.g. {message}.", + "twilioAccountSID": "Account SID", + "twilioApiKey": "Api Key (optional)", + "twilioApiKeyHelptext": "The API key is optional but recommended. You can provide either Account SID and AuthToken from the may TwilioConsole page or Account SID and the pair of Api Key and Api Key secret", + "twilioMessagingServiceSID": "Messaging Service SID (optional)", + "twilloMessagingServiceSIDHelptext": "Enter your Messaging Service SID here if using {twillo_messaging_service_help_link} to manage senders and features", + "twilioAuthToken": "Auth Token / Api Key Secret", + "twilioFromNumber": "From Number", + "twilioToNumber": "To Number", + "Monitor Setting": "{0}'s Monitor Setting", + "Show Clickable Link": "Show Clickable Link", + "Show Clickable Link Description": "If checked everyone who have access to this status page can have access to monitor URL.", + "Open Badge Link Generator": "Open Badge Link Generator", + "Badge Link Generator": "{0}'s Badge Link Generator", + "Badge Link Generator Helptext": "Badge links are available for all monitors assigned to public status pages. For more information, please see the {documentation}.", + "Badge Type": "Badge Type", + "Badge Duration (in hours)": "Badge Duration (in hours)", + "Badge Label": "Badge Label", + "Badge Prefix": "Badge Value Prefix", + "Badge Suffix": "Badge Value Suffix", + "Badge Label Color": "Badge Label Color", + "Badge Color": "Badge Color", + "Badge Label Prefix": "Badge Label Prefix", + "Badge Preview": "Badge Preview", + "Badge Label Suffix": "Badge Label Suffix", + "Badge Up Color": "Badge Up Color", + "Badge Down Color": "Badge Down Color", + "Badge Pending Color": "Badge Pending Color", + "Badge Maintenance Color": "Badge Maintenance Color", + "Badge Warn Color": "Badge Warn Color", + "Badge Warn Days": "Badge Warn Days", + "Badge Down Days": "Badge Down Days", + "Badge Style": "Badge Style", + "Badge value (For Testing only.)": "Badge value (For Testing only.)", + "Badge URL": "Badge URL", + "Group": "Group", + "Monitor Group": "Monitor Group", + "monitorToastMessagesLabel": "Monitor Toast notifications", + "monitorToastMessagesDescription": "Toast notifications for monitors disappear after given time in seconds. Set to -1 disables timeout. Set to 0 disables toast notifications.", + "toastErrorTimeout": "Timeout for Error Notifications", + "toastSuccessTimeout": "Timeout for Success Notifications", + "Kafka Brokers": "Kafka Brokers", + "Enter the list of brokers": "Enter the list of brokers", + "Press Enter to add broker": "Press Enter to add broker", + "Kafka Topic Name": "Kafka Topic Name", + "Kafka Producer Message": "Kafka Producer Message", + "Enable Kafka SSL": "Enable Kafka SSL", + "Enable Kafka Producer Auto Topic Creation": "Enable Kafka Producer Auto Topic Creation", + "Kafka SASL Options": "Kafka SASL Options", + "Mechanism": "Mechanism", + "Pick a SASL Mechanism...": "Pick a SASL Mechanism…", + "Authorization Identity": "Authorization Identity", + "AccessKey Id": "AccessKey Id", + "Secret AccessKey": "Secret AccessKey", + "Session Token": "Session Token", + "noGroupMonitorMsg": "Not Available. Create a Group Monitor First.", + "Close": "Close", + "Request Body": "Request Body", + "HTTP Method": "HTTP Method", + "webhookPostMethodDesc": "POST is good for most modern HTTP servers.", + "webhookGetMethodDesc": "GET sends data as query parameters and does not allow configuring a body. Useful for triggering Uptime Kuma Push monitors.", + "wayToGetFlashDutyKey": "To integrate Uptime Kuma with Flashduty: Go to Channels > Select a channel > Integrations > Add a new integration, choose Uptime Kuma, and copy the Push URL.", + "FlashDuty Severity": "Severity", + "FlashDuty Push URL": "Push URL", + "FlashDuty Push URL Placeholder": "Copy from the alerting integration page", + "Flowtriq Webhook URL": "Webhook URL", + "Flowtriq API Key": "API Key", + "flowtriqApiKeyDescription": "Optional API key for authenticating webhook requests to your Flowtriq instance.", + "Pinglet Topic URL": "Topic URL", + "pingletTopicUrlDescription": "The publish URL of the Pinglet topic to send alerts to, e.g. https://app.pinglet.co.uk/your-namespace/alerts. The topic is created on first publish if it doesn't exist yet.", + "nostrRelays": "Nostr relays", + "nostrRelaysHelp": "One relay URL per line", + "nostrSender": "Sender Private Key (nsec)", + "nostrRecipients": "Recipients Public Keys (npub)", + "nostrRecipientsHelp": "npub format, one per line", + "showCertificateExpiry": "Show Certificate Expiry", + "showOnlyLastHeartbeat": "Show Only Last Heartbeat", + "noOrBadCertificate": "No/Bad Certificate", + "cacheBusterParam": "Add the {0} parameter", + "cacheBusterParamDescription": "Randomly generated parameter to skip caches.", + "gamedigToken": "Token", + "gamedigTokenDescription": "Optional. Some game servers require an authentication token.", + "gamedigGuessPort": "Gamedig: Guess Port", + "gamedigGuessPortDescription": "The port used by Valve Server Query Protocol may be different from the client port. Try this if the monitor cannot connect to your server.", + "Message format": "Message format", + "Send rich messages": "Send rich messages", + "Bitrix24 Webhook URL": "Bitrix24 Webhook URL", + "wayToGetBitrix24Webhook": "You can create a webhook by following the steps at {0}", + "bitrix24SupportUserID": "Enter your user ID in Bitrix24. You can find out the ID from the link by going to the user's profile.", + "ClickUp API Token": "ClickUp API Token", + "ClickUp API Token Description": "You can create a personal access token in ClickUp by going to Settings -> Integrations & ClickApps -> ClickUp API -> API Token", + "ClickUp Workspace ID": "ClickUp Workspace ID", + "ClickUp Workspace ID Description": "You can find it in your workspace's URL, right after app.clickup.com/, e.g. app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ClickUp Channel ID", + "ClickUp Channel ID Description": "You can find it in the Chat channel's URL, right after /chat/r/, e.g. .../chat/r/abc123-4567.", + "Saved.": "Saved.", + "authUserInactiveOrDeleted": "The user is inactive or deleted.", + "authInvalidToken": "Invalid Token.", + "authIncorrectCreds": "Incorrect username or password.", + "2faAlreadyEnabled": "2FA is already enabled.", + "2faEnabled": "2FA Enabled.", + "2faDisabled": "2FA Disabled.", + "successAdded": "Added Successfully.", + "successResumed": "Resumed Successfully.", + "successPaused": "Paused Successfully.", + "successDeleted": "Deleted Successfully.", + "successEdited": "Edited Successfully.", + "successAuthChangePassword": "Password has been updated successfully.", + "successBackupRestored": "Backup successfully restored.", + "successDisabled": "Disabled Successfully.", + "successEnabled": "Enabled Successfully.", + "tagNotFound": "Tag not found.", + "foundChromiumVersion": "Found Chromium/Chrome. Version: {0}", + "Remote Browsers": "Remote Browsers", + "Remote Browser": "Remote Browser", + "Add a Remote Browser": "Add a Remote Browser", + "Remote Browser not found!": "Remote Browser not found!", + "remoteBrowsersDescription": "Remote Browsers are an alternative to running Chromium locally. Set up with a service like browserless.io or connect to your own", + "self-hosted container": "self-hosted container", + "remoteBrowserToggle": "By default Chromium runs inside the Uptime Kuma container. You can use a remote browser by toggling this switch.", + "useRemoteBrowser": "Use a Remote Browser", + "deleteRemoteBrowserMessage": "Are you sure want to delete this Remote Browser for all monitors?", + "Screenshot Delay": "Screenshot Delay (waits {milliseconds})", + "milliseconds": "{n} millisecond | {n} milliseconds", + "screenshotDelayDescription": "Optionally wait this many milliseconds before taking the screenshot. Maximum: {maxValueMs}ms (0.5 × interval).", + "screenshotDelayWarning": "Higher values keep the browser open longer, which may increase memory usage with many concurrent monitors.", + "GrafanaOncallUrl": "Grafana Oncall URL", + "systemService": "System Service", + "systemServiceName": "Service Name", + "systemServiceDescription": "Checks if system service {service_name} is active", + "systemServiceDescriptionLinux": "Checks if Linux systemd service {service_name} is active", + "systemServiceDescriptionWindows": "Checks if Windows Service Manager {service_name} is running", + "systemServiceCommandHint": "Command used: {command}", + "systemServiceExpectedOutput": "Expected Output: \"{0}\"", + "pm2Description": "Checks if PM2 process {service_name} is online", + "pm2ExpectedStates": "Expected state: online. States stopped and errored are treated as DOWN.", + "Browser Screenshot": "Browser Screenshot", + "Command": "Command", + "mongodbCommandDescription": "Run a MongoDB command against the database. For information about the available commands check out the {documentation}", + "wayToGetSevenIOApiKey": "Visit the dashboard under app.seven.io > developer > api key > the green add button", + "senderSevenIO": "Sending number or name", + "receiverSevenIO": "Receiving number", + "receiverInfoSevenIO": "If the receiving number is not located in Germany, you have to add the country code in front of the number (e.g. for the country code 1 from the US use 117612121212 instead of 017612121212)", + "apiKeySevenIO": "SevenIO API Key", + "wayToWriteWhapiRecipient": "The phone number with the international prefix, but without the plus sign at the start ({0}), the Contact ID ({1}) or the Group ID ({2}).", + "wayToGetWhapiUrlAndToken": "You can get the API URL and the token by going into your desired channel from {0}", + "whapiRecipient": "Phone Number / Contact ID / Group ID", + "API URL": "API URL", + "wayToWriteEvolutionRecipient": "The phone number with the international prefix, but without the plus sign at the start ({0}), the Contact ID ({1}) or the Group ID ({2}).", + "wayToGetEvolutionUrlAndToken": "You can get the API URL and the token by going into your desired channel from {0}", + "evolutionRecipient": "Phone Number / Contact ID / Group ID", + "evolutionInstanceName": "Instance Name", + "evolutionCustomMessageTitle": "Custom Message (optional)", + "evolutionCustomMessageDesc": "Leave empty to use the default message.", + "What is a Remote Browser?": "What is a Remote Browser?", + "wayToGetHeiiOnCallDetails": "How to get the Trigger ID and API Keys is explained in the {documentation}", + "documentationOf": "{0} Documentation", + "callMeBotGet": "Here you can generate an endpoint for {0}, {1} and {2}. Keep in mind that you might get rate limited. The ratelimits appear to be: {3}", + "gtxMessagingApiKeyHint": "You can find your API key at: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "From Phone Number / Transmission Path Originating Address (TPOA)", + "gtxMessagingFromHint": "On mobile phones, your recipients sees the TPOA displayed as the sender of the message. Allowed are up to 11 alphanumeric characters, a shortcode, the local longcode or international numbers ({e164}, {e212} or {e214})", + "To Phone Number": "To Phone Number", + "gtxMessagingToHint": "International format, with leading \"+\" ({e164}, {e212} or {e214})", + "Originator type": "Originator type", + "Alphanumeric (recommended)": "Alphanumeric (recommended)", + "Telephone number": "Telephone number", + "cellsyntOriginatortypeAlphanumeric": "Alphanumeric string (max 11 alphanumeric characters). Recipients can not reply to the message.", + "cellsyntOriginatortypeNumeric": "Numeric value (max 15 digits) with telephone number on international format without leading 00 (example UK number 07920 110 000 should be set as 447920110000). Recipients can reply to the message.", + "Originator": "Originator", + "cellsyntOriginator": "Visible on recipient's mobile phone as originator of the message. Allowed values and function depends on parameter originatortype.", + "Destination": "Destination", + "cellsyntDestination": "Recipient's telephone number using international format with leading 00 followed by country code, e.g. 00447920110000 for the UK number 07920 110 000 (max 17 digits in total). Max 25000 comma separated recipients per HTTP request.", + "Allow Long SMS": "Allow Long SMS", + "cellsyntSplitLongMessages": "Split long messages into up to 6 parts. 153 x 6 = 918 characters.", + "max 15 digits": "max 15 digits", + "max 11 alphanumeric characters": "max 11 alphanumeric characters", + "Community String": "Community String", + "snmpCommunityStringHelptext": "This string functions as a password to authenticate and control access to SNMP-enabled devices. Match it with your SNMP device's configuration.", + "OID (Object Identifier)": "OID (Object Identifier)", + "snmpOIDHelptext": "Enter the OID for the sensor or status you want to monitor. Use network management tools like MIB browsers or SNMP software if you're unsure about the OID.", + "snmpV3Username": "SNMPv3 Username", + "Condition": "Condition", + "SNMP Version": "SNMP Version", + "Please enter a valid OID.": "Please enter a valid OID.", + "wayToGetThreemaGateway": "You can register for Threema Gateway {0}.", + "threemaRecipient": "Recipient", + "threemaRecipientType": "Recipient Type", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypeIdentityFormat": "8 characters", + "threemaRecipientTypePhone": "Phone Number", + "threemaRecipientTypePhoneFormat": "E.164, without leading +", + "threemaRecipientTypeEmail": "Email Address", + "threemaSenderIdentity": "Gateway-ID", + "threemaSenderIdentityFormat": "8 characters, usually starts with *", + "threemaApiAuthenticationSecret": "Gateway-ID Secret", + "threemaBasicModeInfo": "Note: This integration uses Threema Gateway in basic mode (server-based encryption). Further details can be found {0}.", + "apiKeysDisabledMsg": "API keys are disabled because authentication is disabled.", + "Host Onesender": "Host Onesender", + "Token Onesender": "Token Onesender", + "Recipient Type": "Recipient Type", + "Private Number": "Private Number", + "privateOnesenderDesc": "Make sure the number phone is valid. To send message into private number phone, ex: 628123456789", + "groupOnesenderDesc": "Make sure the GroupID is valid. To send message into Group, ex: 628123456789-342345", + "Group ID": "Group ID", + "wayToGetOnesenderUrlandToken": "You can get the URL and Token by going to the Onesender website. More info {0}", + "Add Remote Browser": "Add Remote Browser", + "New Group": "New Group", + "Group Name": "Group Name", + "OAuth2: Client Credentials": "OAuth2: Client Credentials", + "Authentication Method": "Authentication Method", + "Authorization Header": "Authorization Header", + "Form Data Body": "Form Data Body", + "OAuth Token URL": "OAuth Token URL", + "Client ID": "Client ID", + "Client Secret": "Client Secret", + "OAuth Scope": "OAuth Scope", + "OAuth Audience": "OAuth Audience", + "Optional: The audience to request the JWT for": "Optional: The audience to request the JWT for", + "Optional: Space separated list of scopes": "Optional: Space separated list of scopes", + "Go back to home page.": "Go back to home page.", + "No tags found.": "No tags found.", + "Lost connection to the socket server.": "Lost connection to the socket server.", + "Cannot connect to the socket server.": "Cannot connect to the socket server.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL", + "signl4Docs": "You can find more information about how to configure SIGNL4 and how to obtain the SIGNL4 webhook URL in the {0}.", + "Conditions": "Conditions", + "conditionAdd": "Add Condition", + "conditionDelete": "Delete Condition", + "conditionAddGroup": "Add Group", + "conditionDeleteGroup": "Delete Group", + "conditionValuePlaceholder": "Value", + "equals": "equals", + "not equals": "not equals", + "contains": "contains", + "not contains": "not contains", + "starts with": "starts with", + "not starts with": "not starts with", + "ends with": "ends with", + "not ends with": "not ends with", + "less than": "less than", + "greater than": "greater than", + "less than or equal to": "less than or equal to", + "greater than or equal to": "greater than or equal to", + "record": "record", + "message": "message", + "json_value": "JSON value", + "Notification Channel": "Notification Channel", + "Sound": "Sound", + "Alphanumerical string and hyphens only": "Alphanumerical string and hyphens only", + "Arcade": "Arcade", + "Correct": "Correct", + "Fail": "Fail", + "Harp": "Harp", + "Reveal": "Reveal", + "Bubble": "Bubble", + "Doorbell": "Doorbell", + "Flute": "Flute", + "Money": "Money", + "Scifi": "Scifi", + "Clear": "Clear", + "Select All": "Select All", + "Deselect All": "Deselect All", + "Elevator": "Elevator", + "Guitar": "Guitar", + "Pop": "Pop", + "Custom sound to override default notification sound": "Custom sound to override default notification sound", + "Time Sensitive (iOS Only)": "Time Sensitive (iOS Only)", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.", + "From": "From", + "Can be found on:": "Can be found on: {0}", + "The phone number of the recipient in E.164 format.": "The phone number of the recipient in E.164 format.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.", + "RabbitMQ Nodes": "RabbitMQ Management Nodes", + "Enter the list of nodes": "Enter the list of RabbitMQ management nodes", + "Press Enter to add node": "Press Enter to add node", + "rabbitmqNodesDescription": "Enter the URL for the RabbitMQ management nodes including protocol and port. Example: {0}", + "rabbitmqNodesRequired": "Please set the nodes for this monitor.", + "rabbitmqNodesInvalid": "Please use a fully qualified (starting with 'http') URL for RabbitMQ nodes.", + "RabbitMQ Username": "RabbitMQ Username", + "RabbitMQ Password": "RabbitMQ Password", + "rabbitmqHelpText": "To use the monitor, you will need to enable the Management Plugin in your RabbitMQ setup. For more information, please consult the {rabitmq_documentation}.", + "SendGrid API Key": "SendGrid API Key", + "turbosmtpConsumerKey": "Consumer Key", + "turbosmtpConsumerSecret": "Consumer Secret", + "Separate multiple email addresses with commas": "Separate multiple email addresses with commas", + "brevoApiKey": "Brevo API Key", + "brevoApiHelp": "Create an API key here: {0}", + "brevoFromEmail": "From Email", + "brevoFromName": "From Name", + "brevoLeaveBlankForDefaultName": "leave blank for default name", + "brevoToEmail": "To Email", + "brevoCcEmail": "CC Email", + "brevoBccEmail": "BCC Email", + "brevoSeparateMultipleEmails": "Separate multiple email addresses with commas", + "brevoSubject": "Subject", + "brevoLeaveBlankForDefaultSubject": "leave blank for default subject", + "resendApiKey": "Resend API Key", + "resendApiHelp": "Create an api key here {0}", + "resendFromName": "From Name", + "resendFromEmail": "From Email", + "resendLeaveBlankForDefaultName": "leave blank for default name", + "resendLeaveBlankForDefaultSubject": "Leave blank for default subject", + "resendToEmail": "To Email", + "resendSubject": "Subject", + "pingCountLabel": "Max Packets", + "pingCountDescription": "Number of packets to send before stopping", + "pingNumericLabel": "Numeric Output", + "pingNumericDescription": "If checked, IP addresses will be output instead of symbolic hostnames", + "pingGlobalTimeoutLabel": "Global Timeout", + "pingGlobalTimeoutDescription": "Total time in seconds before ping stops, regardless of packets sent", + "pingPerRequestTimeoutLabel": "Per-Ping Timeout", + "pingPerRequestTimeoutDescription": "This is the maximum waiting time (in seconds) before considering a single ping packet lost", + "pingIntervalAdjustedInfo": "Interval adjusted based on packet count, global timeout and per-ping timeout", + "smtpHelpText": "'SMTPS' tests that SMTP/TLS is working; 'Ignore TLS' connects over plaintext; 'STARTTLS' connects, issues a STARTTLS command and verifies the server certificate. None of these send an email.", + "SFTP Path to Check": "SFTP Path to Check", + "sftpPathHelpText": "Optional. If specified, the monitor will verify that this path exists on the SFTP server.", + "SSH Private Key": "SSH Private Key", + "Passphrase": "Passphrase", + "sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----", + "sshPrivateKeyHelpText": "Paste the contents of your private key file (e.g. id_rsa or id_ed25519).", + "sshPassphraseHelpText": "Optional. Required only if your private key is encrypted with a passphrase.", + "optional": "optional", + "Custom URL": "Custom URL", + "customUrlDescription": "Will be used as the clickable URL instead of the monitor's one.", + "OneChatAccessToken": "OneChat Access Token", + "OneChatUserIdOrGroupId": "OneChat User ID or Group ID", + "OneChatBotId": "OneChat Bot ID", + "wahaSession": "Session", + "wahaChatId": "Chat ID (Phone Number / Contact ID / Group ID)", + "wayToGetWahaApiUrl": "Your WAHA Instance URL.", + "wayToGetWahaApiKey": "API Key is WHATSAPP_API_KEY environment variable value you used to run WAHA.", + "wayToGetWahaSession": "From this session WAHA sends notifications to Chat ID. You can find it in WAHA Dashboard.", + "wayToWriteWahaChatId": "The phone number with the international prefix, but without the plus sign at the start ({0}), the Contact ID ({1}) or the Group ID ({2}). Notifications are sent to this Chat ID from WAHA Session.", + "openwaSession": "Session", + "openwaChatId": "Chat ID (Phone Number / Contact ID / Group ID)", + "wayToGetOpenwaApiUrl": "Your OpenWA Instance URL.", + "wayToGetOpenwaApiKey": "API key used for your OpenWA instance.", + "wayToGetOpenwaSession": "Session ID used by OpenWA to send notifications.", + "wayToWriteOpenwaChatId": "The phone number with the international prefix, but without the plus sign at the start ({0}), the Contact ID ({1}) or the Group ID ({2}). You can send to multiple Chat IDs by separating them with commas.", + "openwaCustomMessageTitle": "Custom Message (optional)", + "openwaCustomMessageDesc": "Leave empty to use the default message.", + "360messengerAuthToken": "360messenger API Key", + "360messengerRecipient": "Recipient phone number(s)", + "360messengerGroupId": "360messenger Group ID", + "360messengerUseTemplate": "Use a custom message template", + "360messengerTemplate": "360messenger Message Template", + "360messengerGroupList": "WhatsApp groups", + "360messengerSelectGroupList": "Select a group to add", + "360messengerSelectedGroupID": "Selected Group ID(s)", + "360messengerEnableSendToGroup": "Enable sending to WhatsApp group(s)", + "360messengerCustomMessageTemplate": "Custom message template", + "360messengerEnableCustomMessage": "Enable a custom message template instead of the default message.", + "360messengerMessageTemplate": "Message template", + "360messengerWayToGetUrlAndToken": "You can get your 360messenger API key from {0}.", + "360messengerWayToWriteRecipient": "Enter one or more phone numbers in international format without a leading plus (e.g. {0}). Separate multiple numbers with commas.", + "360messengerErrorNoApiKey": "Please enter your 360messenger API key first.", + "360messengerErrorNoGroups": "No WhatsApp groups were found for this account.", + "360messengerErrorApi": "Unable to load the WhatsApp group list (Error {statusCode}: {message}).", + "360messengerErrorGeneric": "Unable to load the WhatsApp group list: {message}", + "YZJ Webhook URL": "YZJ Webhook URL", + "YZJ Robot Token": "YZJ Robot token", + "Plain Text": "Plain Text", + "Message Template": "Message Template", + "Template Format": "Template Format", + "Font Twemoji by Twitter licensed under": "Font Twemoji by Twitter licensed under", + "smsplanetApiToken": "Token for the SMSPlanet API", + "smsplanetApiDocs": "Detailed information on obtaining API tokens can be found in {the_smsplanet_documentation}.", + "the smsplanet documentation": "the smsplanet documentation", + "Phone numbers": "Phone numbers", + "Sender name": "Sender name", + "smsplanetNeedToApproveName": "Needs to be approved in the client panel", + "Disable URL in Notification": "Disable URL in Notification", + "Suppress Notifications": "Suppress Notifications", + "discordSuppressNotificationsHelptext": "When enabled, messages will be posted to the channel but won't trigger push or desktop notifications for recipients.", + "discordMessageFormat": "Message Format", + "discordMessageFormatNormal": "Normal (rich embeds)", + "discordMessageFormatMinimalist": "Minimalist (short status)", + "discordMessageFormatCustom": "Custom template", + "discordUseMessageTemplate": "Use custom message template", + "discordUseMessageTemplateDescription": "If enabled, the message will be sent using a custom template (LiquidJS). Leave blank to use the default Uptime Kuma format.", + "discordMessageTemplate": "Message Template", + "fluxerMessageFormat": "Message Format", + "fluxerMessageFormatNormal": "Normal (rich embeds)", + "fluxerMessageFormatMinimalist": "Minimalist (short status)", + "fluxerMessageFormatCustom": "Custom template", + "fluxerUseMessageTemplate": "Use custom message template", + "fluxerUseMessageTemplateDescription": "If enabled, the message will be sent using a custom template (LiquidJS). Leave blank to use the default Uptime Kuma format.", + "fluxerMessageTemplate": "Message Template", + "Fluxer Webhook URL": "Fluxer Webhook URL", + "wayToGetFluxerURL": "You can get this by going to the target channel's settings > Webhooks > Create Webhook > Copy Webhook URL.", + "Ip Family": "IP Family", + "ipFamilyDescriptionAutoSelect": "Uses the {happyEyeballs} for determining the IP family.", + "Happy Eyeballs algorithm": "Happy Eyeballs algorithm", + "Add Another Tag": "Add Another Tag", + "Staged Tags for Batch Add": "Staged Tags for Batch Add", + "Clear Form": "Clear Form", + "pause": "Pause", + "Manual": "Manual", + "Nextcloud host": "Nextcloud host", + "Conversation token": "Conversation token", + "Bot secret": "Bot secret", + "Send UP silently": "Send UP silently", + "Send DOWN silently": "Send DOWN silently", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Installing a Nextcloud Talk bot requires administrative access to the server.", + "Globalping - Access global monitoring probes": "Globalping - Access global monitoring probes", + "GlobalpingMonitorDescription": "Globalping provides access to thousands of community hosted probes to run network tests and measurements. A limit of 250 tests per hour is set for all anonymous users. To double the limit to 500 per hour please save your token in {accountSettings}. Check the {docs} for more information.", + "Globalping API Token": "Globalping API Token", + "globalpingApiTokenDescription": "Get your Globalping API Token at {0}.", + "GlobalpingHostname": "A publicly reachable measurement target. Typically a hostname or IPv4/IPv6 address, depending on the measurement type.", + "GlobalpingLocationDescription": "The location field accepts continents, countries, regions, cities, ASNs, ISPs, or cloud regions. You can combine filters with {plus} (e.g {amazonPlusGermany} or {comcastPlusCalifornia}). If latency is an important metric, use filters to narrow down the location to a small region to avoid spikes and for better stability set the {datacenter} filter. {fullDocs}.", + "GlobalpingLocationDocs": "Full location input documentation", + "GlobalpingMultipleLocationsError": "Multiple locations are not supported, please use a single location for each monitor.", + "GlobalpingIpFamilyInfo": "The IP version to use. Only allowed if the target is a hostname.", + "GlobalpingResolverInfo": "IPv4/IPv6 address or a fully Qualified Domain Name (FQDN). Defaults to the probe's local network resolver. You can change the resolver server anytime.", + "RecordMatch": "Record value match", + "RegexMatch": "Enter a regex to match the record value", + "Resolver Server": "Resolver Server", + "Protocol": "Protocol", + "account settings": "account settings", + "Location": "Location", + "Monitor Subtype": "Monitor Subtype", + "Check for": "Check for", + "Number of retry attempts if webhook fails": "Number of retry attempts (every 60-180 seconds) if the webhook fails.", + "Maximum Retries": "Maximum Retries", + "Template ID": "Template ID", + "wayToGetClickSMSIRTemplateID": "Your template must contain an {uptkumaalert} field. You can create a new template {here}.", + "Recipient Numbers": "Recipient Numbers", + "Notifications Enabled": "Notifications Enabled", + "Allow Notifications": "Allow Notifications", + "Browser not supported": "Browser not supported", + "Unable to get permission to notify": "Unable to get permission to notify (request either denied or ignored).", + "Webpush Helptext": "Web push only works with SSL (HTTPS) connections. For iOS devices, webpage must be added to homescreen beforehand.", + "settingsDomainExpiry": "Domain Expiry", + "labelDomainExpiry": "Domain Exp.", + "labelDomainNameExpiryNotification": "Domain Name Expiry Notification", + "domainExpiryNotificationHelp": "The number of days in advance can be configured in Settings.", + "domainExpiryDescription": "Trigger notification when domain names expires in:", + "domain_expiry_unsupported_monitor_type": "Domain expiry monitoring is not supported for this monitor type", + "domain_expiry_unsupported_missing_target": "No valid domain or hostname is configured for this monitor", + "domain_expiry_unsupported_is_icann": "The domain \"{domain}\" is not a candidate for domain expiry monitoring, because its public suffix \".{publicSuffix}\" is not managed by ICANN", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Domain expiry monitoring is not available for \".{publicSuffix}\" because no RDAP service is listed by IANA", + "minimumIntervalWarning": "Intervals below 20 seconds may result in poor performance.", + "lowIntervalWarning": "Are you sure want to set the interval value below 20 seconds? Performance may be degraded, particularly if there are a large number of monitors.", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "Halo PSA Webhook URL", + "halopsa_webhook_url_desc": "Enter the webhook URL from your Halo PSA Integration Runbook (Configuration > Integrations > Custom Integrations > Integration Runbooks). Select 'Can only be started from Halo and from a public endpoint' when creating the webhook.", + "username": "Username", + "password": "Password", + "halopsa_username_desc": "Username for authenticating with Halo PSA webhook", + "halopsa_password_desc": "Password for authenticating with Halo PSA webhook", + "imageResetConfirmation": "Image reset to default", + "screenshot of the website": "Screenshot of the website", + "Basic checkbox toggle button group": "Basic checkbox toggle button group", + "Basic radio toggle button group": "Basic radio toggle button group", + "mtls-auth-server-cert-label": "Cert", + "mtls-auth-server-cert-placeholder": "Cert body", + "mtls-auth-server-key-label": "Key", + "mtls-auth-server-key-placeholder": "Key body", + "mtls-auth-server-ca-label": "CA", + "mtls-auth-server-ca-placeholder": "Server CA", + "avgPing": "Avg Ping", + "Uptime Kuma": "Uptime Kuma", + "maxPing": "Max Ping", + "minPing": "Min Ping", + "Setup Instructions": "Setup Instructions", + "halopsa_setup_step1": "Create an Integration Runbook in HaloPSA (Configuration → Integrations → Integration Runbooks)", + "halopsa_setup_step2": "Configure runbook actions to process alerts (e.g., Create Ticket)", + "halopsa_setup_step3": "Copy the Webhook URL and paste it above text field", + "halopsa_setup_step4": "Choose basic Authentication and create username and password. And type or paste those username and password above test fileds", + "Clear current filters": "Clear current filters", + "Sort options": "Sort options", + "Sort by status": "Sort by status", + "Sort by name": "Sort by name", + "Sort by uptime": "Sort by uptime", + "Sort by certificate expiry": "Sort by certificate expiry", + "Splunk Rest URL": "Splunk Rest URL", + "Severity": "Severity", + "Message Format": "Message Format", + "smscTranslit": "smscTranslit", + "Region": "Region", + "PushDeer Server URL": "PushDeer Server URL", + "To Number": "To Number", + "GrafanaOncallURL": "Grafana Oncall URL", + "Never": "Never", + "Json Query": "Json Query", + "PM2 Process": "PM2 Process", + "Refresh": "Refresh", + "Select PM2 process": "Select PM2 process", + "SSL/TLS": "SSL/TLS", + "playground": "playground", + "Check Type": "Check Type", + "Service Name": "Service Name", + "GRPC Options": "GRPC Options", + "Metadata": "Metadata", + "End": "End", + "Show this Maintenance Message on which Status Pages": "Show this Maintenance Message on which Status Pages", + "Endpoint": "Endpoint", + "Details": "Details", + "passwordTooWeak": "Password is too weak. It should contain alphabetic and numeric characters. It must be at least 6 characters in length.", + "TLS Alerts": "TLS Alerts", + "Expected TLS Alert": "Expected TLS Alert", + "None (Successful Connection)": "None (Successful Connection)", + "expectedTlsAlertDescription": "Select the TLS alert you expect the server to return. Use {code} to verify mTLS endpoints reject connections without client certificates. See {link} for details.", + "TLS Alert Spec": "RFC 8446", + "mariadbSocketPathDetectedHelptext": "Connecting to the database as specified via the {0} environment variable.", + "Expand All Groups": "Expand All Groups", + "Collapse All Groups": "Collapse All Groups", + "Webhook Payload Fields": "Webhook Payload Fields", + "halopsa_payload_desc": "The following fields are sent to your Halo PSA webhook:", + "halopsa_field_title": "Alert title (always 'Uptime Kuma Alert')", + "halopsa_field_status": "Monitor status: UP, DOWN, NOTIFICATION, or UNKNOWN", + "halopsa_field_monitor": "Name of the monitor", + "halopsa_field_monitor_id": "Unique monitor identifier (null for test notifications) - Use this to match alerts to tickets", + "halopsa_field_message": "Full alert message with status and details", + "halopsa_field_timestamp": "Event timestamp in ISO 8601 format", + "halopsa_field_uptime_kuma_version": "Uptime Kuma version number", + "halopsa_id_usage_hint": "💡 Tip: Use monitor_id to reliably match alerts to tickets, and heartbeat_id to track event history", + "halopsa_setup_step5": "Configure runbook to use monitor_id for matching alerts to existing tickets", + "Teltonika SMS Gateway": "Teltonika SMS Gateway", + "teltonikaVersionWarning": "This notification provider requires that your Teltonika device runs RMS version 7.14.0, or higher.", + "teltonikaUrl": "Your Teltonika device URL", + "teltonikaUrlHelptext": "URL should be specified as full origin, e.g. {0}, or {1}.", + "teltonikaUnsafeTls": "Ignore certificate validation", + "teltonikaUnsafeTlsDescription": "Turning off TLS certificate validation opens you up to on-path (man-in-the-middle) attacks, potentially leading to data leaks and systems take-over. Do not turn off certificate validation unless you accept this attack vector. We recomend using LetsEncrypt with automatic renewal.", + "teltonikaUsername": "API username", + "teltonikaUsernameHelptext": "Recommendation: Create a separate account which is restricted to only sending SMS messages and enter its username here", + "teltonikaPassword": "API password", + "teltonikaPasswordHelptext": "You can define the API user's password in your Teltonika router, e.g. {0}", + "teltonikaModem": "Modem Id", + "teltonikaModemHelptext": "The id of the SMS modem, must be in the format {0}. Refer to https://developers.teltonika-networks.com/reference/ for guidance.", + "teltonikaPhoneNumber": "Phone number", + "teltonikaPhoneNumberHelptext": "The number must be in the international format {0}, {1}. Only one number is allowed.", + "ntpThresholdsTitle": "NTP Thresholds", + "ntpStratumThreshold": "Stratum Threshold", + "ntpStratumThresholdHelp": "Mark as down if stratum is at or above this value. Stratum indicates time source quality (1=GPS/atomic clock, 16=unsynchronized).", + "ntpOffsetThreshold": "Time Offset Threshold (ms)", + "ntpOffsetThresholdHelp": "Mark as down if time offset exceeds this value in milliseconds.", + "ntpDispersionThreshold": "Root Dispersion Threshold (ms)", + "ntpDispersionThresholdHelp": "Mark as down if root dispersion exceeds this value. Dispersion indicates the server's clock accuracy.", + "VKTeams Bot Token Description": "You can find out how to get a bot token and other details at {0}.", + "VKTeams Base Url Description": "VKTeams base API URL, may differ in On-Premise solution. Default: https://myteam.mail.ru", + "VKTeams Chat Id Description": "For users, this is their email address. For groups and channels, get the ID from their settings and append {'@'}chat.agent", + "VKTeams Use Template": "Use custom message template", + "VKTeams Use Template Description": "If enabled, the message will be sent using a custom template.", + "VKTeams Template Format Description": "For message styling, VKTeams supports plain text, MarkdownV2, and HTML formatting.", + "milkyHttpAddress": "Milky HTTP Address(Without the '/api' suffix)", + "milkyMessageType": "Milky Message Type", + "milkyGroupMessage": "Group", + "milkyPrivateMessage": "Private", + "milkyUserOrGroupId": "Group/User ID", + "milkySafetyTips": "For safety, must set access token", + "smsgatewayUrlHelptext": "The base URL of your SMS Gateway, without an API path.", + "smsgatewayTo": "Phone Number(s)", + "smsgatewayToHelptext": "Comma-separated list of recipient phone numbers in international format (e.g. +15551234567)", + "Line Number": "Line Number", + "Amoot SMS recipient numbers help": "Enter one or more mobile numbers separated by commas.", + "Use Pattern": "Use Pattern", + "Pattern Code ID": "Pattern Code ID", + "Amoot SMS pattern help": "Enter the Pattern Code ID from your Amoot SMS panel. {0}", + "Create an Amoot SMS pattern": "Create an Amoot SMS pattern", + "Use Own Line for Pattern": "Use Own Line for Pattern" +} diff --git a/src/lang/en_GB.json b/src/lang/en_GB.json new file mode 100644 index 0000000..88571c6 --- /dev/null +++ b/src/lang/en_GB.json @@ -0,0 +1,1646 @@ +{ + "setupDatabaseMariaDB": "Connect to an external MariaDB database. You need to set the database connection information.", + "settingUpDatabaseMSG": "Setting up the database. It may take a while. Please be patient.", + "dbName": "Database Name", + "enableSSL": "Enable SSL/TLS", + "mariadbCaCertificateLabel": "CA Certificate", + "mariadbCaCertificateHelptext": "Paste the CA Cert in PEM format to use with self-signed certificates. Leave blank if your database uses a certificate signed by a public CA.", + "Dashboard": "Dashboard", + "Help": "Help", + "New Update": "New Update", + "Language": "Language", + "Appearance": "Appearance", + "Theme": "Theme", + "Primary Base URL": "Primary Base URL", + "versionIs": "Version: {version}", + "List": "List", + "Home": "Home", + "Add New Monitor": "Add New Monitor", + "Quick Stats": "Quick Stats", + "Up": "Up", + "Down": "Down", + "Pending": "Pending", + "statusMaintenance": "Maintenance", + "Maintenance": "Maintenance", + "Unknown": "Unknown", + "unknownDays": "Unknown days", + "Reconnecting...": "Reconnecting...", + "General Monitor Type": "General Monitor Type", + "monitorTypeGameServer": "Game Server", + "monitorTypeDatabase": "Database Monitor Type", + "pauseDashboardHome": "Pause", + "Pause": "Pause", + "Name": "Name", + "Status": "Status", + "DateTime": "DateTime", + "Message": "Message", + "No incidents recorded": "No incidents recorded", + "Load More": "Load More", + "Loading...": "Loading...", + "No important events": "No important events", + "Resume": "Resume", + "Delete": "Delete", + "Current": "Current", + "Uptime": "Uptime", + "Cert Exp.": "Cert Exp.", + "Monitors": "{n} Monitor | {n} Monitors", + "now": "now", + "time ago": "{0} ago", + "days": "{n} day | {n} days", + "hours": "{n} hour | {n} hours", + "minutes": "{n} minute | {n} minutes", + "minuteShort": "{n} min | {n} min", + "years": "{n} year | {n} years", + "Response": "Response", + "Pin this incident": "Pin this incident", + "Ping": "Ping", + "Monitor Type": "Monitor Type", + "Keyword": "Keyword", + "Invert Keyword": "Invert Keyword", + "Expected Value": "Expected Value", + "Json Query Expression": "Json Query Expression", + "defaultFriendlyName": "New Monitor", + "URL": "URL", + "Hostname": "Hostname", + "Host URL": "Host URL", + "Port": "Port", + "Heartbeat Interval": "Heartbeat Interval", + "Request Timeout": "Request Timeout", + "timeoutAfter": "Timeout after {0} seconds", + "Retries": "Retries", + "Resend Notification if Down X times consecutively": "Resend Notification if Down X times consecutively", + "Advanced": "Advanced", + "retryCheckEverySecond": "Retry every {0} seconds", + "resendDisabled": "Resend disabled", + "retriesDescription": "Maximum retries before the service is marked as down, and a notification is sent", + "ignoreTLSErrorGeneral": "Ignore TLS/SSL error for the connection", + "upsideDownModeDescription": "Flip the status upside down. If the service is reachable, it is DOWN.", + "Ignore Sec-WebSocket-Accept header": "Ignore {0} header", + "wsSubprotocolDescription": "Enter a comma-delimited list of subprotocols. For more information on subprotocols, please consult the {documentation}", + "wsCodeDescription": "For more information on status codes, please consult {rfc6455}", + "Subprotocol(s)": "Subprotocol(s)", + "Upside Down Mode": "Upside Down Mode", + "Max. Redirects": "Max. Redirects", + "saveResponseForNotifications": "Save HTTP Success Response for Notifications", + "saveResponseDescription": "Stores the HTTP response and makes it available to notification templates as {templateVariable}", + "responseMaxLength": "Response Max Length (bytes)", + "Accepted Status Codes": "Accepted Status Codes", + "Push URL": "Push URL", + "needPushEvery": "You should call this URL every {0} seconds.", + "pushOptionalParams": "Optional parameters: {0}", + "pushViewCode": "How to use Push monitor? (View Code)", + "pushOthers": "Others", + "programmingLanguages": "Programming Languages", + "Save": "Save", + "Notifications": "Notifications", + "Setup Notification": "Set Up Notification", + "Light": "Light", + "Dark": "Dark", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Theme - Heartbeat Bar", + "styleElapsedTimeShowNoLine": "Show (No Line)", + "styleElapsedTimeShowWithLine": "Show (With Line)", + "Normal": "Normal", + "Bottom": "Bottom", + "None": "None", + "Timezone": "Timezone", + "Allow indexing": "Allow indexing", + "Discourage search engines from indexing site": "Discourage search engines from indexing the site", + "Change Password": "Change Password", + "Current Password": "Current Password", + "Update Password": "Update Password", + "Disable Auth": "Disable Auth", + "Enable Auth": "Enable Auth", + "disable authentication": "disable authentication", + "disableauth.message2": "It is designed for scenarios {intendThirdPartyAuth} in front of Uptime Kuma, such as Cloudflare Access, Authelia or other authentication mechanisms.", + "where you intend to implement third-party authentication": "where you intend to implement third-party authentication", + "Please use this option carefully!": "Please use this option carefully!", + "Logout": "Log out", + "logoutCurrentUser": "Log out {username}", + "Leave": "Leave", + "Confirm": "Confirm", + "Yes": "Yes", + "No": "No", + "Username": "Username", + "Password": "Password", + "Remember me": "Remember me", + "Login": "Log in", + "add one": "add one", + "Notification Type": "Notification Type", + "Email": "Email", + "Test": "Test", + "Certificate Info": "Certificate Info", + "Resolver Server(s)": "Resolver Server(s)", + "Resource Record Type": "Resource Record Type", + "Last Result": "Last Result", + "Create your admin account": "Create your admin account", + "Repeat Password": "Repeat Password", + "Incident description": "Incident description", + "Incident not found or access denied": "Incident not found or access denied", + "Past Incidents": "Past Incidents", + "Incident title": "Incident title", + "Import Backup": "Import Backup", + "Export Backup": "Export Backup", + "Export": "Export", + "Import": "Import", + "respTime": "Resp. Time (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Default enabled", + "Apply on all existing monitors": "Apply on all existing monitors", + "Create": "Create", + "Clear Data": "Clear Data", + "Events": "Events", + "Heartbeats": "Heartbeats", + "Auto Get": "Auto Get", + "Schedule maintenance": "Schedule maintenance", + "Affected Monitors": "Affected Monitors", + "Pick Affected Monitors...": "Pick Affected Monitors…", + "Start of maintenance": "Start of maintenance", + "All Status Pages": "All Status Pages", + "alertNoFile": "Please select a file to import.", + "Clear all statistics": "Clear all Statistics", + "Skip existing": "Skip existing", + "Overwrite": "Overwrite", + "Options": "Options", + "Setup 2FA": "Set Up 2FA", + "Enable 2FA": "Enable 2FA", + "Disable 2FA": "Disable 2FA", + "2FA Settings": "2FA Settings", + "Keep both": "Keep both", + "languageName": "English", + "Verify Token": "Verify Token", + "setupDatabaseChooseDatabase": "Which database would you like to use?", + "setupDatabaseEmbeddedMariaDB": "You don't need to set anything. This Docker image has embedded and configured MariaDB for you automatically. Uptime Kuma will connect to this database via Unix socket.", + "setupDatabaseSQLite": "A simple database file, recommended for small-scale deployments. Before v2.0.0, Uptime Kuma used SQLite as the default database.", + "mariadbUseSSLHelptext": "Enable an encrypted connection to your database. Required for most cloud databases.", + "Settings": "Settings", + "General": "General", + "Game": "Game", + "Check Update On GitHub": "Check Update On GitHub", + "Add": "Add", + "Cannot connect to the socket server": "Cannot connect to the socket server", + "Passive Monitor Type": "Passive Monitor Type", + "Specific Monitor Type": "Specific Monitor Type", + "monitorTypeSpecial": "Special", + "markdownSupported": "Markdown syntax is supported. If using HTML, avoid leading spaces to prevent formatting issues.", + "Edit": "Edit", + "Friendly Name": "Friendly Name", + "locally configured mail transfer agent": "locally configured mail transfer agent", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Either enter the hostname of the server you want to connect to, or {localhost} if you intend to use a {local_mta}", + "Path": "Path", + "Heartbeat Retry Interval": "Heartbeat Retry Interval", + "checkEverySecond": "Check every {0} seconds", + "resendEveryXTimes": "Resend every {0} times", + "Only retry if status code check fails": "Only retry if the status code check fails", + "retryOnlyOnStatusCodeFailureDescription": "If enabled, retries will only occur when the HTTP status code check fails (e.g., server is down). If the status code check passes but the JSON query fails, the monitor will be marked as down immediately without retries.", + "ignoredTLSError": "TLS/SSL errors have been ignored", + "ignoreTLSError": "Ignore TLS/SSL errors for HTTPS websites", + "ignoreSecWebsocketAcceptHeaderDescription": "Allows the server to not reply with Sec-WebSocket-Accept header if the WebSocket upgrade succeeds.", + "maxRedirectDescription": "Maximum number of redirects to follow. Set to 0 to disable redirects.", + "saveErrorResponseForNotifications": "Save HTTP Error Response for Notifications", + "responseMaxLengthDescription": "Maximum size of response data to store. Set to 0 for unlimited. Larger responses will be truncated. Default: 1024 (1KB)", + "Not available, please setup.": "Not available, please set up.", + "styleElapsedTime": "Elapsed time under the heartbeat bar", + "Search Engine Visibility": "Search Engine Visibility", + "New Password": "New Password", + "Repeat New Password": "Repeat New Password", + "disableauth.message1": "Are you sure you want to {disableAuth}?", + "I understand, please disable": "I understand, please disable", + "No Monitors, please": "No Monitors, please", + "Select status pages...": "Select status pages…", + "alertWrongFileType": "Please select a JSON file.", + "Two Factor Authentication": "Two Factor Authentication", + "filterActive": "Active", + "filterActivePaused": "Paused", + "Active": "Active", + "Inactive": "Inactive", + "Token": "Token", + "Show URI": "Show URI", + "Tags": "Tags", + "Add New Tag": "Add New Tag", + "Add Tags": "Add Tags", + "Add New below or Select...": "Add New below or Select…", + "Tag with this name already exist.": "Tag with this name already exists.", + "Tag with this value already exist.": "Tag with this value already exists.", + "tagAlreadyOnMonitor": "This tag (name and value) is already on the monitor or pending addition.", + "tagAlreadyStaged": "This tag (name and value) is already staged for this batch.", + "Red": "Red", + "Orange": "Orange", + "tagNameExists": "A system tag with this name already exists. Select it from the list or use a different name.", + "color": "Colour", + "value (optional)": "value (optional)", + "Gray": "Grey", + "Green": "Green", + "Blue": "Blue", + "Indigo": "Indigo", + "Purple": "Purple", + "Pink": "Pink", + "Entry Page": "Entry Page", + "Search monitored sites": "Search monitored sites", + "Avg. Ping": "Avg. Ping", + "Avg. Response": "Avg. Response", + "statusPageNothing": "Nothing here, please add a group or a monitor.", + "oracledbConnectionString": "Oracle Database: {connectionString}", + "Pinned incidents are shown prominently on the status page": "Pinned incidents are shown prominently on the status page", + "Custom": "Custom", + "Search...": "Search…", + "statusPageRefreshIn": "Refresh in: {0}", + "No Services": "No Services", + "Partially Degraded Service": "Partially Degraded Service", + "Degraded Service": "Degraded Service", + "Add a monitor": "Add a monitor", + "Add Group": "Add Group", + "Edit Incident": "Edit Incident", + "Edit Status Page": "Edit Status Page", + "Go to Dashboard": "Go to Dashboard", + "Status Page": "Status Page", + "Status Pages": "Status Pages", + "defaultNotificationName": "My {notification} Alert ({number})", + "here": "here", + "Required": "Required", + "Post URL": "Post URL", + "All Systems Operational": "All Systems Operational", + "Content Type": "Content Type", + "webhookJsonDesc": "{0} is good for any modern HTTP server, such as Express.js", + "webhookFormDataDesc": "{multipart} is good for PHP. The JSON will need to be parsed with {decodeFunction}", + "example": "Example", + "liquidIntroduction": "Templatability is achieved via the Liquid templating language. Please refer to the {0} for usage instructions.", + "templateAvailableVariables": "Available variables", + "Result": "Result", + "templateMsg": "message of the notification", + "templateLimitedToUpDownCertNotifications": "only available for UP/DOWN/Certificate expiry notifications", + "templateLimitedToUpDownNotifications": "only available for UP/DOWN notifications", + "templateServiceName": "service name", + "templateHostnameOrURL": "hostname or URL", + "templateStatus": "status", + "templateHeartbeatJSON": "object describing the heartbeat", + "templateMonitorJSON": "object describing the monitor", + "webhookAdditionalHeadersDesc": "Sets additional headers sent with the webhook. Each header should be defined as a JSON key/value.", + "webhookAdditionalHeadersTitle": "Additional Headers", + "webhookBodyCustomOption": "Custom Body", + "Webhook URL": "Webhook URL", + "webhookBodyPresetOption": "Preset - {0}", + "emojiCheatSheet": "Emoji cheat sheet: {0}", + "Read more": "Read more", + "appriseInstalled": "Apprise is installed.", + "appriseNotInstalled": "Apprise is not installed. {0}", + "Application Token": "Application Token", + "Server URL": "Server URL", + "Priority": "Priority", + "Method": "Method", + "Body": "Body", + "Headers": "Headers", + "PushUrl": "Push URL", + "HeadersInvalidFormatBecause": "The request headers are not valid JSON because {error}", + "BodyInvalidFormatBecause": "The request body is not valid JSON because {error}", + "Monitor History": "Monitor History", + "clearDataOlderThan": "Keep monitor history data for {0} days.", + "PasswordsDoNotMatch": "Passwords do not match.", + "records": "records", + "One record": "One record", + "steamApiKeyDescriptionAt": "For monitoring a Steam Game Server, you need a Steam Web API key. You can register your API key at {url}", + "topic": "Topic", + "topicExplanation": "MQTT topic to monitor", + "mqttWebSocketPath": "MQTT WebSocket Path", + "mqttWebsocketPathExplanation": "WebSocket path for MQTT over WebSocket connections (e.g., /mqtt)", + "mqttWebsocketPathInvalid": "Please use a valid WebSocket Path format", + "mqttHostnameTip": "Please use this format {hostnameFormat}", + "invalidDNSHostname": "Invalid hostname. Hostname must be a valid FQDN. Can be a wildcard, have an underscore or end with a dot.", + "hostnameCannotBeIP": "DNS hostname cannot be an IP. Did you mean to use the resolver field?", + "invalidHostnameOrIP": "Invalid hostname or IP. Hostname must be a valid FQDN. Cannot use wildcard. Can have an underscore, or end with a dot.", + "wildcardOnlyForDNS": "Wildcard hostnames are only supported for DNS monitors.", + "invalidURL": "Invalid URL", + "telnyxApiKey": "API Key (V2)", + "telnyxApiKeyHelptext": "Your Telnyx V2 API key from the Telnyx Portal under API Keys", + "telnyxPhoneNumber": "From Number", + "Authorization Identity": "Authorisation Identity", + "AccessKey Id": "AccessKey Id", + "senderSevenIO": "Sending number or name", + "evolutionRecipient": "Phone Number / Contact ID / Group ID", + "To Phone Number": "To Phone Number", + "snmpOIDHelptext": "Enter the OID for the sensor or status you want to monitor. Use network management tools like MIB browsers or SNMP software if you're unsure about the OID.", + "snmpV3Username": "SNMPv3 Username", + "OAuth Scope": "OAuth Scope", + "OAuth Audience": "OAuth Audience", + "brevoBccEmail": "BCC Email", + "pingCountLabel": "Max Packets", + "username": "Username", + "halopsa_setup_step5": "Configure runbook to use monitor_id for matching alerts to existing tickets", + "wayToGetMaxToken": "You can get a MAX bot token and other details from {0}.", + "maxMessenger": "MAX messenger", + "maxApiUrlDescription": "Base API URL for MAX messenger. Default: https://platform-api.max.ru", + "wayToGetMaxChatID": "Specify the chat identifier in MAX where messages should be delivered.", + "recurringInterval": "Interval", + "weekdayShortSat": "Sat", + "No Maintenance": "No Maintenance", + "chromeExecutable": "Chrome/Chromium Executable", + "emailTemplateHeartbeatJSON": "object describing the heartbeat", + "emailTemplateMsg": "message of the notification", + "emailTemplateLimitedToUpDownNotification": "only available for UP/DOWN heartbeats, otherwise null", + "Prefix Custom Message": "Prefix Custom Message", + "Click Deploy → New deployment → Web app": "Click Deploy → New deployment → Web app", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Set 'Execute as: Me' and 'Who has access: Anyone'", + "Message Title": "Message Title", + "AccessKeyId": "AccessKey ID", + "smseagleRecipient": "Recipient(s) (multiple must be separated with comma)", + "smseagleMsgSms": "Sms message (default)", + "Server URL should not contain the nfty topic": "Server URL should not contain the nfty topic", + "FlashDuty Push URL": "Push URL", + "FlashDuty Push URL Placeholder": "Copy from the alerting integration page", + "rabbitmqNodesInvalid": "Please use a fully qualified (starting with 'http') URL for RabbitMQ nodes.", + "RabbitMQ Username": "RabbitMQ Username", + "brevoFromName": "From Name", + "resendToEmail": "To Email", + "resendSubject": "Subject", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Installing a Nextcloud Talk bot requires administrative access to the server.", + "Globalping - Access global monitoring probes": "Globalping - Access global monitoring probes", + "Endpoint": "Endpoint", + "API Version": "API Version", + "clearEventsMsg": "Are you sure you want to delete all events for this monitor? This permanently removes associated message data and cannot be undone.", + "infiniteRetention": "Set to 0 for infinite retention.", + "clearHeartbeatsMsg": "Are you sure you want to delete all heartbeats for this monitor? This permanently removes all heartbeat data and cannot be undone.", + "confirmClearStatisticsMsg": "Are you sure you want to delete ALL statistics?", + "Peer ID": "Peer ID", + "vkPeerIdDescription": "Enter the target VK peer_id. This value is sent to the API as-is.", + "Read more:": "Read more: {0}", + "Bark Endpoint": "Bark Endpoint", + "do nothing": "do nothing", + "smseagleDuration": "Duration (in seconds)", + "onebotHttpAddress": "OneBot HTTP Address", + "onebotMessageType": "OneBot Message Type", + "Analytics Type": "Analytics Type", + "Analytics ID": "Analytics ID", + "ntfyCall": "Phone Call", + "ntfyCallHelptext": "Make a phone call when alert fires. Set to 'yes' to use your first verified number, or enter a specific phone number (e.g. +12223334444). Requires ntfy Pro and a verified phone number.", + "ntfyUseTemplateDescription": "Enable this to customise notification titles and messages using LiquidJS templating", + "ntfyCustomMessage": "Custom Message Template", + "ntfyNotificationTemplateFallback": "Leave blank to use the default Uptime Kuma format", + "twilioAccountSID": "Account SID", + "lunaseaUserID": "User ID", + "ntfyAuthenticationMethod": "Authentication Method", + "twilioApiKeyHelptext": "The API key is optional but recommended. You can provide either Account SID and AuthToken from the may TwilioConsole page or Account SID and the pair of API Key and API Key secret", + "twilioMessagingServiceSID": "Messaging Service SID (optional)", + "Badge Up Color": "Badge Up Colour", + "Badge Down Color": "Badge Down Colour", + "Badge Pending Color": "Badge Pending Colour", + "Badge Maintenance Color": "Badge Maintenance Colour", + "Badge Warn Color": "Badge Warn Colour", + "monitorToastMessagesDescription": "Toast notifications for monitors disappear after given time in seconds. Set to -1 disables timeout. Set to 0 disables toast notifications.", + "Kafka Producer Message": "Kafka Producer Message", + "Enable Kafka SSL": "Enable Kafka SSL", + "Enable Kafka Producer Auto Topic Creation": "Enable Kafka Producer Auto Topic Creation", + "cacheBusterParamDescription": "Randomly generated parameter to skip caches.", + "gamedigGuessPortDescription": "The port used by Valve Server Query Protocol may be different from the client port. Try this if the monitor cannot connect to your server.", + "Message format": "Message format", + "successBackupRestored": "Backup successfully restored.", + "successEnabled": "Enabled Successfully.", + "Screenshot Delay": "Screenshot Delay (waits {milliseconds})", + "milliseconds": "{n} millisecond | {n} milliseconds", + "What is a Remote Browser?": "What is a Remote Browser?", + "wayToGetHeiiOnCallDetails": "How to get the Trigger ID and API Keys is explained in the {documentation}", + "gtxMessagingApiKeyHint": "You can find your API key at: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "gtxMessagingFromHint": "On mobile phones, your recipients sees the TPOA displayed as the sender of the message. Allowed are up to 11 alphanumeric characters, a shortcode, the local longcode or international numbers ({e164}, {e212} or {e214})", + "threemaSenderIdentityFormat": "8 characters, usually starts with *", + "threemaApiAuthenticationSecret": "Gateway-ID Secret", + "threemaBasicModeInfo": "Note: This integration uses Threema Gateway in basic mode (server-based encryption). Further details can be found {0}.", + "apiKeysDisabledMsg": "API keys are disabled because authentication is disabled.", + "Host Onesender": "Host Onesender", + "Group ID": "Group ID", + "signl4Docs": "You can find more information about how to configure SIGNL4 and how to obtain the SIGNL4 webhook URL in the {0}.", + "Conditions": "Conditions", + "conditionAdd": "Add Condition", + "message": "message", + "json_value": "JSON value", + "Notification Channel": "Notification Channel", + "Sound": "Sound", + "Alphanumerical string and hyphens only": "Alphanumerical string and hyphens only", + "Arcade": "Arcade", + "Bubble": "Bubble", + "Money": "Money", + "Scifi": "Scifi", + "Clear": "Clear", + "Select All": "Select All", + "Time Sensitive (iOS Only)": "Time Sensitive (iOS Only)", + "From": "From", + "Can be found on:": "Can be found on: {0}", + "customUrlDescription": "Will be used as the clickable URL instead of the monitor's one.", + "360messengerWayToGetUrlAndToken": "You can get your 360messenger API key from {0}.", + "Matomo": "Matomo", + "Umami": "Umami", + "Disable URL in Notification": "Disable URL in Notification", + "fluxerMessageTemplate": "Message Template", + "Fluxer Webhook URL": "Fluxer Webhook URL", + "wayToGetFluxerURL": "You can get this by going to the target channel's settings > Webhooks > Create Webhook > Copy Webhook URL.", + "Ip Family": "IP Family", + "Manual": "Manual", + "GlobalpingMonitorDescription": "Globalping provides access to thousands of community hosted probes to run network tests and measurements. A limit of 250 tests per hour is set for all anonymous users. To double the limit to 500 per hour please save your token in {accountSettings}. Check the {docs} for more information.", + "GlobalpingHostname": "A publicly reachable measurement target. Typically a hostname or IPv4/IPv6 address, depending on the measurement type.", + "RecordMatch": "Record value match", + "RegexMatch": "Enter a regex to match the record value", + "GlobalpingLocationDescription": "The location field accepts continents, countries, regions, cities, ASNs, ISPs, or cloud regions. You can combine filters with {plus} (e.g {amazonPlusGermany} or {comcastPlusCalifornia}). If latency is an important metric, use filters to narrow down the location to a small region to avoid spikes and for better stability set the {datacenter} filter. {fullDocs}.", + "Check for": "Check for", + "Number of retry attempts if webhook fails": "Number of retry attempts (every 60-180 seconds) if the webhook fails.", + "Maximum Retries": "Maximum Retries", + "Template ID": "Template ID", + "wayToGetClickSMSIRTemplateID": "Your template must contain an {uptkumaalert} field. You can create a new template {here}.", + "Browser not supported": "Browser not supported", + "Unable to get permission to notify": "Unable to get permission to notify (request either denied or ignored).", + "Webpush Helptext": "Web push only works with SSL (HTTPS) connections. For iOS devices, webpage must be added to homescreen beforehand.", + "settingsDomainExpiry": "Domain Expiry", + "labelDomainExpiry": "Domain Exp.", + "labelDomainNameExpiryNotification": "Domain Name Expiry Notification", + "domain_expiry_unsupported_monitor_type": "Domain expiry monitoring is not supported for this monitor type", + "domain_expiry_unsupported_missing_target": "No valid domain or hostname is configured for this monitor", + "domainExpiryDescription": "Trigger notification when domain names expires in:", + "halopsa_webhook_url_desc": "Enter the webhook URL from your Halo PSA Integration Runbook (Configuration > Integrations > Custom Integrations > Integration Runbooks). Select 'Can only be started from Halo and from a public endpoint' when creating the webhook.", + "password": "Password", + "halopsa_username_desc": "Username for authenticating with Halo PSA webhook", + "halopsa_setup_step3": "Copy the Webhook URL and paste it above text field", + "Region": "Region", + "PushDeer Server URL": "PushDeer Server URL", + "Json Query": "Json Query", + "System Service": "System Service", + "SSL/TLS": "SSL/TLS", + "playground": "playground", + "Check Type": "Check Type", + "Service Name": "Service Name", + "Metadata": "Metadata", + "End": "End", + "Teltonika SMS Gateway": "Teltonika SMS Gateway", + "evolutionCustomMessageTitle": "Custom Message (optional)", + "evolutionCustomMessageDesc": "Leave empty to use the default message.", + "gtxMessagingToHint": "International format, with leading \"+\" ({e164}, {e212} or {e214})", + "Originator type": "Originator type", + "Alphanumeric (recommended)": "Alphanumeric (recommended)", + "Telephone number": "Telephone number", + "cellsyntOriginatortypeAlphanumeric": "Alphanumeric string (max 11 alphanumeric characters). Recipients can not reply to the message.", + "cellsyntSplitLongMessages": "Split long messages into up to 6 parts. 153 x 6 = 918 characters.", + "max 15 digits": "max 15 digits", + "wayToGetThreemaGateway": "You can register for Threema Gateway {0}.", + "threemaRecipient": "Recipient", + "threemaRecipientTypeEmail": "Email Address", + "threemaSenderIdentity": "Gateway-ID", + "Fail": "Fail", + "Harp": "Harp", + "Reveal": "Reveal", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.", + "The phone number of the recipient in E.164 format.": "The phone number of the recipient in E.164 format.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.", + "brevoLeaveBlankForDefaultName": "leave blank for default name", + "brevoToEmail": "To Email", + "brevoCcEmail": "CC Email", + "pingPerRequestTimeoutDescription": "This is the maximum waiting time (in seconds) before considering a single ping packet lost", + "pingIntervalAdjustedInfo": "Interval adjusted based on packet count, global timeout and per-ping timeout", + "wayToWriteWahaChatId": "The phone number with the international prefix, but without the plus sign at the start ({0}), the Contact ID ({1}) or the Group ID ({2}). Notifications are sent to this Chat ID from WAHA Session.", + "360messengerAuthToken": "360messenger API Key", + "360messengerEnableCustomMessage": "Enable a custom message template instead of the default message.", + "360messengerMessageTemplate": "Message template", + "360messengerErrorApi": "Unable to load the WhatsApp group list (Error {statusCode}: {message}).", + "360messengerErrorGeneric": "Unable to load the WhatsApp group list: {message}", + "smsplanetApiToken": "Token for the SMSPlanet API", + "discordMessageFormatMinimalist": "Minimalist (short status)", + "Nextcloud host": "Nextcloud host", + "Bot secret": "Bot secret", + "Send UP silently": "Send UP silently", + "Send DOWN silently": "Send DOWN silently", + "GlobalpingResolverInfo": "IPv4/IPv6 address or a Fully Qualified Domain Name (FQDN). Defaults to the probe's local network resolver. You can change the resolver server anytime.", + "Recipient Numbers": "Recipient Numbers", + "Notifications Enabled": "Notifications Enabled", + "Halo PSA Webhook URL": "Halo PSA Webhook URL", + "Clear current filters": "Clear current filters", + "Sort options": "Sort options", + "smscTranslit": "smscTranslit", + "GRPC Options": "GRPC Options", + "expectedTlsAlertDescription": "Select the TLS alert you expect the server to return. Use {code} to verify mTLS endpoints reject connections without client certificates. See {link} for details.", + "mariadbSocketPathDetectedHelptext": "Connecting to the database as specified via the {0} environment variable.", + "halopsa_field_monitor": "Name of the monitor", + "halopsa_field_monitor_id": "Unique monitor identifier (null for test notifications) - Use this to match alerts to tickets", + "halopsa_field_message": "Full alert message with status and details", + "teltonikaPhoneNumber": "Phone number", + "Security": "Security", + "Steam API Key": "Steam API Key", + "danger": "danger", + "error": "error", + "critical": "critical", + "primary": "primary", + "lastUpdatedAtFromNow": "Last Updated: {date} ({fromNow})", + "Switch to Light Theme": "Switch to Light Theme", + "Switch to Dark Theme": "Switch to Dark Theme", + "descriptionHelpText": "Shown on the internal dashboard. Markdown is allowed and sanitised (preserves spaces and indentation) before display.", + "Add one": "Add one", + "No Monitors": "No Monitors", + "selectedMonitorCountMsg": "selected: {n} | selected: {n}", + "selectMonitorMsg": "Select monitors to perform actions", + "Custom CSS": "Custom CSS", + "Invalid": "Invalid", + "User": "User", + "API Username": "API Username", + "API Key": "API Key", + "API Token": "API Token", + "See Jira Cloud Docs": "See Jira Cloud Docs", + "Retype the address.": "Retype the address.", + "Go back to the previous page.": "Go back to the previous page.", + "deleteDockerHostMsg": "Are you sure want to delete this docker host for all monitors?", + "matrixUseTemplateDescription": "If enabled, the message will be sent using a custom template.", + "telegramTemplateFormatDescription": "Telegram allows using different markup languages for messages, see Telegram {0} for specifc details.", + "supportTelegramChatID": "Support Direct Chat / Group / Channel's Chat ID", + "maxUseTemplate": "Use custom message template", + "maxUseTemplateDescription": "If enabled, the message will be sent using a custom template.", + "maxTemplateFormatDescription": "MAX messenger supports plain text, Markdown and HTML formatting.", + "trustProxyDescription": "Trust 'X-Forwarded-*' headers. If you want to get the correct client IP and your Uptime Kuma is behind a proxy such as Nginx or Apache, you should enable this.", + "wayToGetLineNotifyToken": "You can get an access token from {0}", + "homeAssistantNotificationActionHelptext": "A list of Notification Actions can be found in Home Assistant under \"Settings > Developer Tools > Actions\". Search for \"notify\" to find your actions. Enter only the part after \"notify.\", e.g. for the action \"notify.mobile_app_xyz\" enter \"mobile_app_xyz\". For built-in mobile notifications, look for \"Send a notification via mobile_app_xyz\" (not \"Send a notification\").", + "Duration (Minutes)": "Duration (Minutes)", + "invalidCronExpression": "Invalid Cron Expression: {0}", + "Recurring": "Recurring", + "strategyManual": "Active/Inactive Manually", + "weekdayShortTue": "Tue", + "Long-Lived Access Token": "Long-Lived Access Token", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token.", + "weekdayShortWed": "Wed", + "weekdayShortThu": "Thu", + "weekdayShortFri": "Fri", + "lastDay4": "4th Last Day of Month", + "maintenanceStatus-ended": "Ended", + "maintenanceStatus-unknown": "Unknown", + "Display Timezone": "Display time zone", + "Server Timezone": "Server time zone", + "statusPageMaintenanceEndDate": "End", + "enableNSCD": "Enable NSCD (Name Service Cache Daemon) for caching all DNS requests", + "chromeExecutableAutoDetect": "Auto Detect", + "Single Maintenance Window": "Single Maintenance Window", + "DateTime Range": "DateTime Range", + "loadingError": "Cannot fetch the data, please try again later.", + "plugin": "Plugin | Plugins", + "notificationEmail": "Email", + "notificationIncidentManagement": "Incident Management", + "notificationHomeAutomation": "Home Automation", + "notificationOther": "Other Integrations", + "Clone Monitor": "Clone Monitor", + "Ignore STARTTLS": "Ignore STARTTLS", + "Use STARTTLS": "Use STARTTLS", + "Use HTML for custom E-mail body": "Use HTML for custom E-mail body", + "secureOptionNone": "None / STARTTLS (25, 587)", + "emailCustomisableContent": "Customisable content", + "Hello @everyone is...": "Hello {'@'}everyone is…", + "Select message type": "Select message type", + "wayToGetTeamsURL": "You can learn how to create a webhook URL {0}.", + "teamsEnableTags": "Include tags", + "teamsEnableTagsDescription": "If enabled, the message will include the monitor tags.", + "Number": "Number", + "User ID": "User ID", + "Your User ID": "Your user ID", + "Messaging API": "Messaging API", + "wayToGetLineChannelToken": "First access the {0}, create a provider and channel (Messaging API), then you can get the channel access token and user ID from the above mentioned menu items.", + "aboutIconURL": "You can provide a link to a picture in \"Icon URL\" to override the default profile picture. Will not be used if Icon Emoji is set.", + "aboutMattermostChannelName": "You can override the default channel that the Webhook posts to by entering the channel name into \"Channel Name\" field. This needs to be enabled in the Mattermost Webhook settings. Ex: #other-channel", + "noMonitorsResumedMsg": "No monitors resumed (none were inactive)", + "deleteMaintenanceMsg": "Are you sure want to delete this maintenance?", + "deleteNotificationMsg": "Are you sure want to delete this notification for all monitors?", + "dnsPortDescription": "DNS server port. Defaults to 53. You can change the port at any time.", + "importHandleDescription": "Choose 'Skip existing' if you want to skip every monitor or notification with the same name. 'Overwrite' will delete every existing monitor and notification.", + "twoFAVerifyLabel": "Please enter your token to verify 2FA:", + "affectedStatusPages": "Show this maintenance message on selected status pages", + "Sets end time based on start time": "Sets end time based on start time", + "Please set start time first": "Please set start time first", + "octopushAPIKey": "\"API key\" from HTTP API credentials in control panel", + "noMonitorsSelectedWarning": "You are creating a maintenance without any affected monitors. Are you sure you want to continue?", + "noMonitorsOrStatusPagesSelectedError": "Cannot create maintenance without affected monitors or status pages", + "passwordNotMatchMsg": "The repeat password does not match.", + "notificationDescription": "Notifications must be assigned to a monitor to function.", + "vkApiVersionDescription": "VK API version used for requests. Leave the default unless you specifically need another VK API version for compatibility.", + "vkDontParseLinks": "Disable link snippets", + "vkDontParseLinksDescription": "If enabled, VK will not generate link previews/snippets.", + "keywordDescription": "Search keyword in plain HTML or JSON response. The search is case-sensitive.", + "invertKeywordDescription": "Look for the keyword to be absent rather than present.", + "pushoversounds bike": "Bike", + "pushoversounds bugle": "Bugle", + "pushoversounds cashregister": "Cash Register", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Cosmic", + "pushoversounds falling": "Falling", + "pushoversounds gamelan": "Gamelan", + "pushoversounds spacealarm": "Space Alarm", + "pushoversounds tugboat": "Tug Boat", + "pushoversounds alien": "Alien Alarm (long)", + "pushoversounds climb": "Climb (long)", + "pushyToken": "Device token", + "apprise": "Apprise (Support 50+ Notification services)", + "GoogleChat": "Google Chat (Google Workspace only)", + "Paste the script code (see below)": "Paste the script code (see below)", + "issueWithGoogleChatOnAndroidHelptext": "This also allows to get around bugs upstream like {issuetackerURL}", + "wayToGetKookBotToken": "Create application and get your bot token at {0}", + "Notification Sound": "Notification Sound", + "More info on:": "More info on: {0}", + "pushoverDesc1": "Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour.", + "apiCredentials": "API credentials", + "Apprise URL": "Apprise URL", + "Example:": "Example: {0}", + "Status:": "Status: {0}", + "Proto Service Name": "Proto Service Name", + "Proto Method": "Proto Method", + "Proto Content": "Proto Content", + "Economy": "Economy", + "Retry": "Retry", + "Topic": "Topic", + "goAlertInfo": "GoAlert is a An open source application for on-call scheduling, automated escalations and notifications (like SMS or voice calls). Automatically engage the right person, the right way, and at the right time! {0}", + "PhoneNumbers": "PhoneNumbers", + "TemplateCode": "TemplateCode", + "SignName": "SignName", + "aliyun-template-requirements-and-parameters": "The aliyun SMS template must contain parameters: {parameters}", + "aliyun-template-optional-parameters": "Optional parameters: {parameters}", + "Bark Group": "Bark Group", + "Bark Sound": "Bark Sound", + "WebHookUrl": "WebHookUrl", + "SecretKey": "SecretKey", + "For safety, must use secret key": "For safety, must use secret key", + "Mentioning": "Mentioning", + "WeCom Bot Key": "WeCom Bot Key", + "WeCom Mentioned Mobile List": "WeCom Mentioned Mobile List", + "WeCom Mentioned Mobile List Description": "Enter phone numbers to mention. Separate multiple numbers with commas. Use {'@'}all to mention everyone.", + "Setup Proxy": "Set Up Proxy", + "Proxy Protocol": "Proxy Protocol", + "Proxy Server": "Proxy Server", + "promosmsTypeSpeed": "SMS SPEED - Highest priority in system. Very quick and reliable but costly (about twice of SMS FULL price).", + "promosmsPhoneNumber": "Phone number (for Polish recipient You can skip area codes)", + "promosmsSMSSender": "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Internal Room Id": "Internal Room ID", + "matrixDesc2": "It is highly recommended you create a new user and do not use your own Matrix user's access token as it will allow full access to your account and all the rooms you joined. Instead, create a new user and only invite it to the room that you want to receive the notification in. You can get the access token by running {0}", + "Channel Name": "Channel Name", + "Notify Channel": "Notify Channel", + "aboutNotifyChannel": "Notify channel will trigger a desktop or mobile notification for all members of the channel, whether their availability is set to active or away.", + "Uptime Kuma URL": "Uptime Kuma URL", + "setup a new monitor group": "set up a new monitor group", + "signalImportant": "IMPORTANT: You cannot mix groups and numbers in recipients!", + "signalUseTemplate": "Use custom message template", + "signalUseTemplateDescription": "If enabled, the message will be sent using a custom template. You can use Liquid templating to customise the notification format.", + "aboutWebhooks": "More info about Webhooks on: {0}", + "aboutKumaURL": "If you leave the Uptime Kuma URL field blank, it will default to the Project GitHub page.", + "smtpDkimHashAlgo": "Hash Algorithm (Optional)", + "alertaEnvironment": "Environment", + "smseagleRecipientType": "Recipient type", + "smtpDkimheaderFieldNames": "Header Keys to sign (Optional)", + "auto acknowledged": "auto acknowledged", + "auto resolve": "auto resolve", + "alertaApiEndpoint": "API Endpoint", + "smseagleMsgRing": "Ring call", + "smseagleMsgTts": "Text-to-speech call", + "smseagleMsgTtsAdvanced": "Text-to-speech Advanced call", + "smseagleTtsModel": "Text-to-speech model ID", + "smseagleApiType": "API version", + "smseagleApiv1": "APIv1 (for existing projects and backward compatibility)", + "smseagleComma": "Multiple must be separated with comma", + "smspartnerPhoneNumberHelptext": "The number must be in the international format {0}, {1}. Multiple numbers must be separated by {2}", + "smspartnerSenderName": "SMS Sender Name", + "smspartnerApiurl": "You can find your API key in your dashboard at {0}", + "smspartnerPhoneNumber": "Phone number(s)", + "smspartnerSenderNameInfo": "Must be between 3..=11 regular characters", + "Recipient Number": "Recipient Number", + "From Name/Number": "From Name/Number", + "octopushEndpoint": "octopush (endpoint: {url})", + "legacyOctopushEndpoint": "Legacy Octopush-DM (endpoint: {url})", + "ntfy Topic": "ntfy Topic", + "deleteAPIKeyMsg": "Are you sure you want to delete this API key?", + "Leave blank to use a shared sender number.": "Leave blank to use a shared sender number.", + "Octopush API Version": "Octopush API Version", + "pagertreeSilent": "Silent", + "pagertreeLow": "Low", + "pagertreeMedium": "Medium", + "pagertreeHigh": "High", + "pagertreeCritical": "Critical", + "pagertreeResolve": "Auto Resolve", + "wayToGetPagerTreeIntegrationURL": "After creating the Uptime Kuma integration in PagerTree, copy the Endpoint. See full details {0}", + "lunaseaTarget": "Target", + "lunaseaDeviceID": "Device ID", + "pagertreeDoNothing": "Do Nothing", + "telnyxPhoneNumberHelptext": "Your Telnyx phone number to send from (must be registered in your Telnyx account, e.g. +15551234567)", + "telnyxToNumber": "To Number", + "telnyxToNumberHelptext": "The destination phone number to receive SMS notifications (e.g. +15559876543)", + "telnyxMessagingProfileId": "Messaging Profile ID (optional)", + "telnyxMessagingProfileIdHelptext": "Optionally associate a Telnyx Messaging Profile to control sender settings and features", + "twilioApiKey": "API Key (optional)", + "Open Badge Link Generator": "Open Badge Link Generator", + "Badge Label": "Badge Label", + "Badge Prefix": "Badge Value Prefix", + "Badge Suffix": "Badge Value Suffix", + "Badge Color": "Badge Colour", + "Badge Label Prefix": "Badge Label Prefix", + "Badge Preview": "Badge Preview", + "Badge Label Suffix": "Badge Label Suffix", + "Badge Warn Days": "Badge Warn Days", + "wayToGetFlashDutyKey": "To integrate Uptime Kuma with Flashduty: Go to Channels > Select a channel > Integrations > Add a new integration, choose Uptime Kuma, and copy the Push URL.", + "gamedigGuessPort": "Gamedig: Guess Port", + "Badge Down Days": "Badge Down Days", + "monitorToastMessagesLabel": "Monitor Toast notifications", + "Secret AccessKey": "Secret AccessKey", + "webhookGetMethodDesc": "GET sends data as query parameters and does not allow configuring a body. Useful for triggering Uptime Kuma Push monitors.", + "successPaused": "Paused Successfully.", + "tagNotFound": "Tag not found.", + "foundChromiumVersion": "Found Chromium/Chrome. Version: {0}", + "Remote Browsers": "Remote Browsers", + "Remote Browser": "Remote Browser", + "Add a Remote Browser": "Add a Remote Browser", + "remoteBrowserToggle": "By default Chromium runs inside the Uptime Kuma container. You can use a remote browser by toggling this switch.", + "screenshotDelayDescription": "Optionally wait this many milliseconds before taking the screenshot. Maximum: {maxValueMs}ms (0.5 × interval).", + "deleteRemoteBrowserMessage": "Are you sure want to delete this Remote Browser for all monitors?", + "systemServiceDescriptionLinux": "Checks if Linux systemd service {service_name} is active", + "systemServiceDescriptionWindows": "Checks if Windows Service Manager {service_name} is running", + "systemServiceCommandHint": "Command used: {command}", + "systemServiceExpectedOutput": "Expected Output: \"{0}\"", + "Browser Screenshot": "Browser Screenshot", + "Command": "Command", + "mongodbCommandDescription": "Run a MongoDB command against the database. For information about the available commands check out the {documentation}", + "wayToGetSevenIOApiKey": "Visit the dashboard under app.seven.io > developer > API key > the green add button", + "wayToGetEvolutionUrlAndToken": "You can get the API URL and the token by going into your desired channel from {0}", + "OID (Object Identifier)": "OID (Object Identifier)", + "Condition": "Condition", + "cellsyntDestination": "Recipient's telephone number using international format with leading 00 followed by country code, e.g. 00447920110000 for the UK number 07920 110 000 (max 17 digits in total). Max 25000 comma separated recipients per HTTP request.", + "SNMP Version": "SNMP Version", + "Please enter a valid OID.": "Please enter a valid OID.", + "Optional: The audience to request the JWT for": "Optional: The audience to request the JWT for", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL", + "conditionDelete": "Delete Condition", + "not equals": "not equals", + "less than": "less than", + "greater than": "greater than", + "less than or equal to": "less than or equal to", + "greater than or equal to": "greater than or equal to", + "record": "record", + "Correct": "Correct", + "brevoFromEmail": "From Email", + "OneChatUserIdOrGroupId": "OneChat User ID or Group ID", + "wayToGetWahaSession": "From this session WAHA sends notifications to Chat ID. You can find it in WAHA Dashboard.", + "pause": "Pause", + "Conversation token": "Conversation token", + "smtpHelpText": "'SMTPS' tests that SMTP/TLS is working; 'Ignore TLS' connects over plaintext; 'STARTTLS' connects, issues a STARTTLS command and verifies the server certificate. None of these send an email.", + "lowIntervalWarning": "Are you sure want to set the interval value below 20 seconds? Performance may be degraded, particularly if there are a large number of monitors.", + "halopsa_password_desc": "Password for authenticating with Halo PSA webhook", + "halopsa_setup_step2": "Configure runbook actions to process alerts (e.g., Create Ticket)", + "halopsa_setup_step4": "Choose basic Authentication and create username and password. And type or paste those username and password above test fileds", + "teltonikaModemHelptext": "The id of the SMS modem, must be in the format {0}. Refer to https://developers.teltonika-networks.com/reference/ for guidance.", + "Resolve": "Resolve", + "Resolved": "Resolved", + "Reset Token": "Reset Token", + "Done": "Done", + "Shrink Database": "Shrink Database", + "shrinkDatabaseDescriptionSqlite": "Trigger database {vacuum} for SQLite. {auto_vacuum} is already enabled but this does not defragment the database nor repack individual database pages the way that the {vacuum} command does.", + "Pick a RR-Type...": "Pick a RR-Type…", + "Pick Accepted Status Codes...": "Pick Accepted Status Codes…", + "Default": "Default", + "HTTP Options": "HTTP Options", + "Create Incident": "Create Incident", + "Title": "Title", + "Content": "Content", + "Style": "Style", + "info": "info", + "warning": "warning", + "light": "light", + "dark": "dark", + "Post": "Post", + "Please input title and content": "Please input title and content", + "createdAt": "Created: {date}", + "lastUpdatedAt": "Last Updated: {date}", + "Show Tags": "Show Tags", + "Hide Tags": "Hide Tags", + "Description": "Description", + "No monitors available.": "No monitors available.", + "Services": "Services", + "Discard": "Discard", + "Cancel": "Cancel", + "Untitled Group": "Untitled Group", + "auto-select": "Auto Select", + "Select": "Select", + "Actions": "Actions", + "selectAllMonitorsAria": "Select all monitors", + "deselectAllMonitorsAria": "Deselect all monitors", + "Check/Uncheck": "Check/Uncheck", + "Powered by": "Powered by", + "Customize": "Customise", + "Custom Footer": "Custom Footer", + "Proxies": "Proxies", + "default": "Default", + "enabled": "Enabled", + "setAsDefault": "Set As Default", + "deleteProxyMsg": "Are you sure want to delete this proxy for all monitors?", + "setAsDefaultProxyDescription": "This proxy will be enabled by default for new monitors. You can still disable the proxy separately for each monitor.", + "enableProxyDescription": "This proxy will not effect on monitor requests until it is activated. You can control temporarily disable the proxy from all monitors by activation status.", + "Certificate Chain:": "Certificate Chain:", + "Valid": "Valid", + "Installed": "Installed", + "Not installed": "Not installed", + "Running": "Running", + "Not running": "Not running", + "Remove Token": "Remove Token", + "Stop": "Stop", + "Add New Status Page": "Add New Status Page", + "Slug": "Slug", + "Start": "Start", + "slug is not found": "Slug is not found", + "Accept characters:": "Accept characters:", + "startOrEndWithOnly": "Start or end with {0} only", + "No consecutive dashes": "No consecutive dashes", + "statusPageSpecialSlugDesc": "Special slug {0}: this page will be shown when no slug is provided", + "Next": "Next", + "The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.", + "No Proxy": "No Proxy", + "Authentication": "Authentication", + "HTTP Basic Auth": "HTTP Basic Auth", + "New Status Page": "New Status Page", + "Page Not Found": "Page Not Found", + "Reverse Proxy": "Reverse Proxy", + "Backup": "Backup", + "About": "About", + "wayToGetCloudflaredURL": "(Download cloudflared from {0})", + "cloudflareWebsite": "Cloudflare Website", + "Message:": "Message:", + "Don't know how to get the token? Please read the guide:": "Don't know how to get the token? Please read the guide:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.", + "HTTP Headers": "HTTP Headers", + "Trust Proxy": "Trust Proxy", + "Other Software": "Other Software", + "Please read": "Please read", + "Subject:": "Subject:", + "Valid To:": "Valid To:", + "Days Remaining:": "Days Remaining:", + "Issuer:": "Issuer:", + "Fingerprint:": "Fingerprint:", + "No status pages": "No status pages", + "Add a new expiry notification day": "Add a new expiry notification day", + "Remove the expiry notification": "Remove the expiry notification day", + "Proxy": "Proxy", + "dateCreatedAtFromNow": "Date Created: {date} ({fromNow})", + "Footer Text": "Footer Text", + "signedInDisp": "Signed in as {0}", + "RadiusSecret": "Radius Secret", + "signedInDispDisabled": "Auth Disabled.", + "RadiusSecretDescription": "Shared Secret between client and server", + "RadiusCalledStationId": "Called Station Id", + "RadiusCalledStationIdDescription": "Identifier of the called device", + "RadiusCallingStationId": "Calling Station Id", + "RadiusCallingStationIdDescription": "Identifier of the calling device", + "Certificate Expiry Notification": "Certificate Expiry Notification", + "certificateExpiryNotificationHelp": "The number of days in advance can be configured in the Settings.", + "Cloud ID": "Cloud ID", + "Steam Game Server": "Steam Game Server", + "Using a Reverse Proxy?": "Using a Reverse Proxy?", + "Check how to config it for WebSocket": "Check how to config it for WebSocket", + "Most likely causes:": "Most likely causes:", + "What you can try:": "What you can try:", + "Coming Soon": "Coming Soon", + "Connection String": "Connection String", + "Query": "Query", + "settingsCertificateExpiry": "TLS Certificate Expiry", + "certificationExpiryDescription": "HTTPS Monitors trigger notification when TLS certificate expires in:", + "certHostnameMismatch": "Certificate hostname does not match the monitor URL.", + "Setup Docker Host": "Set Up Docker Host", + "Connection Type": "Connection Type", + "Docker Daemon": "Docker Daemon", + "noDockerHostMsg": "Not Available. Set Up a Docker Host First.", + "DockerHostRequired": "Please set the Docker Host for this monitor.", + "tcp": "TCP / HTTP", + "tailscalePingWarning": "In order to use the Tailscale Ping monitor, you need to install Uptime Kuma without Docker and also install Tailscale client on your server.", + "sipsakPingWarning": "In order to use the SIP Options Ping monitor, you need to install Uptime Kuma without Docker and also install Sipsak client on your server.", + "Docker Container": "Docker Container", + "socket": "Socket", + "Container Name / ID": "Container Name / ID", + "Docker Host": "Docker Host", + "Docker Hosts": "Docker Hosts", + "Domain": "Domain", + "Workstation": "Workstation", + "Packet Size": "Packet Size", + "Bot Token": "Bot Token", + "wayToGetTelegramToken": "You can get a token from {0}.", + "Chat ID": "Chat ID", + "telegramMessageThreadID": "(Optional) Message Thread ID", + "telegramMessageThreadIDDescription": "Optional Unique identifier for the target message thread (topic) of the forum; for forum supergroups only", + "telegramSendSilently": "Send Silently", + "telegramSendSilentlyDescription": "Sends the message silently. Users will receive a notification with no sound.", + "telegramProtectContent": "Protect Forwarding/Saving", + "telegramProtectContentDescription": "If enabled, the bot messages in Telegram will be protected from forwarding and saving.", + "telegramUseTemplate": "Use custom message template", + "telegramUseTemplateDescription": "If enabled, the message will be sent using a custom template.", + "matrixUseTemplate": "Use custom message template", + "telegramServerUrlDescription": "To lift Telegram's bot API limitations or gain access in blocked areas (China, Iran, etc). For more information click {0}. Default: {1}", + "YOUR BOT TOKEN HERE": "YOUR BOT TOKEN HERE", + "chatIDNotFound": "Chat ID is not found; please send a message to this bot first", + "disableCloudflaredNoAuthMsg": "You are in No Auth mode, a password is not required.", + "Automations can optionally be triggered in Home Assistant:": "Automations can optionally be triggered in Home Assistant:", + "Trigger type:": "Trigger type:", + "Event type:": "Event type:", + "Event data:": "Event data:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Then choose an action, for example switch the scene to where an RGB light is red.", + "frontendVersionIs": "Frontend Version: {version}", + "endDateTime": "End Date/Time", + "cronExpression": "Cron Expression", + "cronScheduleDescription": "Schedule: {description}", + "warningTimezone": "It is using the server's time zone", + "weekdayShortMon": "Mon", + "weekdayShortSun": "Sun", + "dayOfWeek": "Day of Week", + "dayOfMonth": "Day of Month", + "lastDay": "Last Day", + "lastDay1": "Last Day of Month", + "lastDay2": "2nd Last Day of Month", + "lastDay3": "3rd Last Day of Month", + "Enable DNS Cache": "(Deprecated) Enable DNS Cache for HTTP(s) monitors", + "Enable": "Enable", + "Disable": "Disable", + "Effective Date Range": "Effective Date Range (Optional)", + "Schedule Maintenance": "Schedule Maintenance", + "Edit Maintenance": "Edit Maintenance", + "Clone Maintenance": "Clone Maintenance", + "ariaPauseMaintenance": "Pause this maintenance schedule", + "ariaResumeMaintenance": "Resume this maintenance schedule", + "notificationPushServices": "Push Services", + "notificationSmsServices": "SMS Services", + "Disable STARTTLS": "Disable STARTTLS", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignore TLS Error", + "disableSTARTTLSDescription": "Enable this option for SMTP servers that do not support STARTTLS. This will send emails over an unencrypted connection.", + "From Email": "From Email", + "smtpLiquidIntroduction": "The following two fields are templatable via the Liquid templating Language. Please refer to the {0} for usage instructions. These are the available variables:", + "emailCustomSubject": "Custom Subject", + "leave blank for default subject": "leave blank for default subject", + "emailCustomBody": "Custom Body", + "leave blank for default body": "leave blank for default body", + "emailTemplateMonitorJSON": "object describing the monitor", + "wayToGetDiscordURL": "You can get this by going to Server Settings -> Integrations -> View Webhooks -> New Webhook", + "Bot Display Name": "Bot Display Name", + "postToExistingThread": "Post to existing thread / forum post", + "forumPostName": "Forum post name", + "threadForumPostID": "Thread / Forum post ID", + "e.g. {discordThreadID}": "e.g. {discordThreadID}", + "whatHappensAtForumPost": "Create a new forum post. This does NOT post messages in existing post. To post in existing post use \"{option}\"", + "wayToGetDiscordThreadId": "Getting a thread / forum post id is similar to getting a channel id. Read more about how to get ids {0}", + "Create new forum post": "Create new forum post", + "Recipients": "Recipients", + "Access Token": "Access Token", + "Channel access token": "Channel access token", + "Channel access token (Long-lived)": "Channel access token (Long-lived)", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console - {0}", + "Basic Settings": "Basic Settings", + "resumedMonitorsMsg": "Resumed {n} monitor | Resumed {n} monitors", + "deletedMonitorsMsg": "Deleted {n} monitor | Deleted {n} monitors", + "noMonitorsPausedMsg": "No monitors paused (none were active)", + "Clear All Events": "Clear All Events", + "resolverserverDescription": "Cloudflare is the default server. You can specify a comma delimited list of IP addresses or hostnames.", + "rrtypeDescription": "Select the RR type you want to monitor", + "pauseMonitorMsg": "Are you sure want to pause?", + "enableDefaultNotificationDescription": "This notification will be enabled by default for new monitors. You can still disable the notification separately for each monitor.", + "clearAllEventsMsg": "Are you sure you want to delete all events for all monitors? This permanently removes associated message data and cannot be undone.", + "tokenValidSettingsMsg": "Token is valid! You can now save the 2FA settings.", + "confirmEnableTwoFAMsg": "Are you sure you want to enable 2FA?", + "confirmDisableTwoFAMsg": "Are you sure you want to disable 2FA?", + "affectedMonitorsDescription": "Select monitors that are affected by current maintenance", + "recurringIntervalMessage": "Run once every day | Run once every {0} days", + "backupDescription": "You can backup all monitors and notifications into a JSON file.", + "jsonQueryDescription": "Parse and extract specific data from the server's JSON response using JSON query or use \"$\" for the raw response, if not expecting JSON. The result is then compared to the expected value, as strings. See {0} for documentation and use {1} to experiment with queries.", + "backupDescription2": "Note: history and event data is not included.", + "backupDescription3": "Sensitive data such as notification tokens are included in the export file; please store export securely.", + "pushoversounds incoming": "Incoming", + "pushoversounds intermission": "Intermission", + "pushoversounds magic": "Magic", + "pushoversounds mechanical": "Mechanical", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Siren", + "pushoversounds persistent": "Persistent (long)", + "pushoversounds updown": "Up Down (long)", + "pushoversounds echo": "Pushover Echo (long)", + "pushoversounds vibrate": "Vibrate Only", + "pushoversounds none": "None (silent)", + "pushyAPIKey": "Secret API Key", + "Google Apps Script Webhook URL": "Google Apps Script Webhook URL", + "Deploy a Google Apps Script as a web app and paste the URL here": "Deploy a Google Apps Script as a web app and paste the URL here", + "Quick Setup Guide": "Quick Setup Guide", + "Open your Google Spreadsheet": "Open your Google Spreadsheet", + "Go to Extensions → Apps Script": "Go to Extensions → Apps Script", + "Device": "Device", + "pushoverDesc2": "If you want to send notifications to different devices, fill out Device field.", + "pushoverMessageTtl": "Message TTL (Seconds)", + "SMS Type": "SMS Type", + "octopushTypePremium": "Premium (Fast - recommended for alerting)", + "octopushTypeLowCost": "Low Cost (Slow - sometimes blocked by operator)", + "checkPriceAt": "Check {service} prices at {url}", + "Check octopush prices": "Check octopush prices {0}.", + "octopushLegacyHint": "Do you use the legacy version of Octopush (2011-2020) or the new version?", + "octopushPhoneNumber": "Phone number (intl format, eg : +33612345678)", + "octopushSMSSender": "SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Device ID", + "Strategy": "Strategy", + "Free Mobile User Identifier": "Free Mobile User Identifier", + "Free Mobile API Key": "Free Mobile API Key", + "Enable TLS": "Enable TLS", + "SMSManager API Docs": "SMSManager API Docs", + "Gateway Type": "Gateway Type", + "You can divide numbers with commas or semicolons": "You can divide numbers with {comma} or {semicolon}", + "Base URL": "Base URL", + "OptionalParameters": "Optional Parameters", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Due to carrier restrictions, enable optional variables at the risk of non-delivery", + "Bark API Version": "Bark API Version", + "Enter a list of mobile": "Enter a list of mobile", + "Invalid mobile": "Invalid mobile [{mobile}]", + "Invalid userId": "Invalid userId [{userId}]", + "Device Token": "Device Token", + "Platform": "Platform", + "Huawei": "Huawei", + "promosmsTypeFull": "SMS FULL - Premium tier of SMS, You can use your Sender Name (You need to register name first). Reliable for alerts.", + "promosmsAllowLongSMS": "Allow long SMS", + "Feishu WebHookUrl": "Feishu WebHookURL", + "matrixHomeserverURL": "Homeserver URL (with http(s):// and optionally port)", + "openModalTo": "open modal to {0}", + "Add a domain": "Add a domain", + "Remove domain": "Remove domain '{0}'", + "Icon Emoji": "Icon Emoji", + "smtpDkimDesc": "Please refer to the Nodemailer DKIM {0} for usage.", + "documentation": "documentation", + "smtpDkimDomain": "Domain Name", + "smtpDkimKeySelector": "Key Selector", + "smtpDkimPrivateKey": "Private Key", + "smtpDkimskipFields": "Header Keys not to sign (Optional)", + "Integration Key": "Integration Key", + "smseagleTo": "Phone number(s)", + "smseagleGroup": "Phonebook group name(s)", + "smseagleContact": "Phonebook contact name(s)", + "smseagleGroupV2": "Phonebook group ID(s)", + "smseagleContactV2": "Phonebook contact ID(s)", + "smseagleApiv2": "APIv2 (recommended for new integrations)", + "smseagleDocs": "Check documentation or APIv2 availability: {0}", + "pushDeerServerDescription": "Leave blank to use the official server", + "PushDeer Key": "PushDeer Key", + "SpugPush Template Code": "Template Code", + "wayToGetClickSendSMSToken": "You can get API Username and API Key from {here}.", + "Custom Monitor Type": "Custom Monitor Type", + "Google Analytics ID": "Google Analytics ID", + "No API Keys": "No API Keys", + "apiKey-active": "Active", + "apiKey-expired": "Expired", + "apiKey-inactive": "Inactive", + "Expires": "Expires", + "disableAPIKeyMsg": "Are you sure you want to disable this API key?", + "pagertreeUrgency": "Urgency", + "Generate": "Generate", + "pagertreeIntegrationUrl": "Integration URL", + "ntfyPriorityHelptextAllEvents": "All events are sent with the maximum priority", + "ntfyPriorityHelptextAllExceptDown": "All events are sent with this priority, except {0}-events, which have a priority of {1}", + "ntfyPriorityHelptextPriorityHigherThanDown": "Regular priority should be higher than {0} priority. Priority {1} is higher than {0} priority {2}", + "ntfyPriorityDown": "Priority for DOWN-events", + "ntfyUsernameAndPassword": "Username and Password", + "ntfyUseTemplate": "Customise notification templates", + "ntfyCustomTitle": "Custom Title Template", + "Badge Link Generator": "{0}'s Badge Link Generator", + "Badge Link Generator Helptext": "Badge links are available for all monitors assigned to public status pages. For more information, please see the {documentation}.", + "Badge Type": "Badge Type", + "Badge Duration (in hours)": "Badge Duration (in hours)", + "Badge Style": "Badge Style", + "Badge value (For Testing only.)": "Badge value (For Testing only.)", + "Badge URL": "Badge URL", + "Group": "Group", + "Monitor Group": "Monitor Group", + "toastErrorTimeout": "Timeout for Error Notifications", + "toastSuccessTimeout": "Timeout for Success Notifications", + "Kafka Brokers": "Kafka Brokers", + "Enter the list of brokers": "Enter the list of brokers", + "Press Enter to add broker": "Press Enter to add broker", + "Kafka Topic Name": "Kafka Topic Name", + "Session Token": "Session Token", + "noGroupMonitorMsg": "Not Available. Create a Group Monitor First.", + "Close": "Close", + "Request Body": "Request Body", + "HTTP Method": "HTTP Method", + "webhookPostMethodDesc": "POST is good for most modern HTTP servers.", + "FlashDuty Severity": "Severity", + "nostrSender": "Sender Private Key (nsec)", + "nostrRecipients": "Recipients Public Keys (npub)", + "nostrRecipientsHelp": "npub format, one per line", + "showCertificateExpiry": "Show Certificate Expiry", + "showOnlyLastHeartbeat": "Show Only Last Heartbeat", + "noOrBadCertificate": "No/Bad Certificate", + "cacheBusterParam": "Add the {0} parameter", + "authUserInactiveOrDeleted": "The user is inactive or deleted.", + "authInvalidToken": "Invalid Token.", + "authIncorrectCreds": "Incorrect username or password.", + "2faAlreadyEnabled": "2FA is already enabled.", + "2faEnabled": "2FA Enabled.", + "2faDisabled": "2FA Disabled.", + "successAdded": "Added Successfully.", + "successResumed": "Resumed Successfully.", + "successAuthChangePassword": "Password has been updated successfully.", + "successDisabled": "Disabled Successfully.", + "useRemoteBrowser": "Use a Remote Browser", + "GrafanaOncallUrl": "Grafana Oncall URL", + "screenshotDelayWarning": "Higher values keep the browser open longer, which may increase memory usage with many concurrent monitors.", + "systemService": "System Service", + "documentationOf": "{0} Documentation", + "callMeBotGet": "Here you can generate an endpoint for {0}, {1} and {2}. Keep in mind that you might get rate limited. The ratelimits appear to be: {3}", + "From Phone Number / Transmission Path Originating Address (TPOA)": "From Phone Number / Transmission Path Originating Address (TPOA)", + "max 11 alphanumeric characters": "max 11 alphanumeric characters", + "Community String": "Community String", + "snmpCommunityStringHelptext": "This string functions as a password to authenticate and control access to SNMP-enabled devices. Match it with your SNMP device's configuration.", + "Recipient Type": "Recipient Type", + "Private Number": "Private Number", + "privateOnesenderDesc": "Make sure the number phone is valid. To send message into private number phone, ex: 628123456789", + "contains": "contains", + "not contains": "not contains", + "starts with": "starts with", + "not starts with": "not starts with", + "ends with": "ends with", + "not ends with": "not ends with", + "Deselect All": "Deselect All", + "Elevator": "Elevator", + "Guitar": "Guitar", + "Pop": "Pop", + "Custom sound to override default notification sound": "Custom sound to override default notification sound", + "Press Enter to add node": "Press Enter to add node", + "rabbitmqNodesDescription": "Enter the URL for the RabbitMQ management nodes including protocol and port. Example: {0}", + "rabbitmqNodesRequired": "Please set the nodes for this monitor.", + "rabbitmqHelpText": "To use the monitor, you will need to enable the Management Plugin in your RabbitMQ setup. For more information, please consult the {rabitmq_documentation}.", + "SendGrid API Key": "SendGrid API Key", + "Separate multiple email addresses with commas": "Separate multiple email addresses with commas", + "brevoApiKey": "Brevo API Key", + "brevoApiHelp": "Create an API key here: {0}", + "resendApiHelp": "Create an API key here {0}", + "resendFromName": "From Name", + "resendFromEmail": "From Email", + "resendLeaveBlankForDefaultName": "leave blank for default name", + "resendLeaveBlankForDefaultSubject": "Leave blank for default subject", + "pingCountDescription": "Number of packets to send before stopping", + "pingNumericLabel": "Numeric Output", + "pingNumericDescription": "If checked, IP addresses will be output instead of symbolic hostnames", + "pingGlobalTimeoutLabel": "Global Timeout", + "pingPerRequestTimeoutLabel": "Per-Ping Timeout", + "wahaChatId": "Chat ID (Phone Number / Contact ID / Group ID)", + "wayToGetWahaApiUrl": "Your WAHA Instance URL.", + "wayToGetWahaApiKey": "API Key is WHATSAPP_API_KEY environment variable value you used to run WAHA.", + "YZJ Webhook URL": "YZJ Webhook URL", + "YZJ Robot Token": "YZJ Robot token", + "Plain Text": "Plain Text", + "Message Template": "Message Template", + "Template Format": "Template Format", + "Font Twemoji by Twitter licensed under": "Font Twemoji by Twitter licensed under", + "smsplanetNeedToApproveName": "Needs to be approved in the client panel", + "Google": "Google", + "Plausible": "Plausible", + "fluxerUseMessageTemplate": "Use custom message template", + "fluxerUseMessageTemplateDescription": "If enabled, the message will be sent using a custom template (LiquidJS). Leave blank to use the default Uptime Kuma format.", + "Globalping API Token": "Globalping API Token", + "globalpingApiTokenDescription": "Get your Globalping API Token at {0}.", + "GlobalpingLocationDocs": "Full location input documentation", + "GlobalpingMultipleLocationsError": "Multiple locations are not supported, please use a single location for each monitor.", + "GlobalpingIpFamilyInfo": "The IP version to use. Only allowed if the target is a hostname.", + "Protocol": "Protocol", + "Resolver Server": "Resolver Server", + "account settings": "account settings", + "Location": "Location", + "Monitor Subtype": "Monitor Subtype", + "domainExpiryNotificationHelp": "The number of days in advance can be configured in Settings.", + "mtls-auth-server-key-label": "Key", + "mtls-auth-server-key-placeholder": "Key body", + "mtls-auth-server-ca-label": "CA", + "mtls-auth-server-ca-placeholder": "Server CA", + "Never": "Never", + "passwordTooWeak": "Password is too weak. It should contain alphabetic and numeric characters. It must be at least 6 characters in length.", + "TLS Alerts": "TLS Alerts", + "Expected TLS Alert": "Expected TLS Alert", + "None (Successful Connection)": "None (Successful Connection)", + "TLS Alert Spec": "RFC 8446", + "halopsa_field_title": "Alert title (always 'Uptime Kuma Alert')", + "halopsa_field_status": "Monitor status: UP, DOWN, NOTIFICATION, or UNKNOWN", + "Webhook Payload Fields": "Webhook Payload Fields", + "halopsa_payload_desc": "The following fields are sent to your Halo PSA webhook:", + "halopsa_field_timestamp": "Event timestamp in ISO 8601 format", + "halopsa_field_uptime_kuma_version": "Uptime Kuma version number", + "halopsa_id_usage_hint": "💡 Tip: Use monitor_id to reliably match alerts to tickets, and heartbeat_id to track event history", + "teltonikaUsername": "API username", + "teltonikaUsernameHelptext": "Recommendation: Create a separate account which is restricted to only sending SMS messages and enter its username here", + "teltonikaPassword": "API password", + "teltonikaPasswordHelptext": "You can define the API user's password in your Teltonika router, e.g. {0}", + "teltonikaModem": "Modem Id", + "teltonikaPhoneNumberHelptext": "The number must be in the international format {0}, {1}. Only one number is allowed.", + "Notification Action": "Notification Action", + "successKeyword": "Success Keyword", + "successKeywordExplanation": "MQTT Keyword that will be considered as success", + "recent": "Recent", + "Info": "Info", + "deleteIncidentMsg": "Are you sure you want to delete this incident?", + "deleteStatusPageMsg": "Are you sure want to delete this status page?", + "proxyDescription": "Proxies must be assigned to a monitor to function.", + "For example: nginx, Apache and Traefik.": "For example: nginx, Apache and Traefik.", + "Please input content": "Please input content", + "Please input title": "Please input title", + "Domain Name Expiry Notification": "Domain Name Expiry Notification", + "RSS Title": "RSS Title", + "Leave blank to use status page title": "Leave blank to use status page title", + "Refresh Interval": "Refresh Interval", + "Refresh Interval Description": "The status page will do a full site refresh every {0} seconds", + "Show Powered By": "Show Powered By", + "Domain Names": "Domain Names", + "Show update if available": "Show update if available", + "Also check beta release": "Also check beta release", + "The resource is no longer available.": "The resource is no longer available.", + "There might be a typing error in the address.": "There might be a typing error in the address.", + "wayToGetTelegramChatID": "You can get your chat ID by sending a message to the bot and going to this URL to view the chat_id:", + "telegramServerUrl": "(Optional) Server URL", + "Examples:": "Examples: {0}", + "supportBaleChatID": "Support Direct Chat / Group / Channel's Chat ID", + "wayToGetBaleChatID": "You can get your chat ID by sending a message to the bot and going to this URL to view the chat_id:", + "wayToGetBaleToken": "You can get a token from {0}.", + "Home Assistant URL": "Home Assistant URL", + "backupOutdatedWarning": "Deprecated: Since a lot of features were added and this backup feature is a bit unmaintained, it cannot generate or restore a complete backup.", + "backupRecommend": "Please backup the volume or the data folder (./data/) directly instead.", + "default: notify all devices": "default: notify all devices", + "Frontend Version do not match backend version!": "Frontend Version do not match backend version!", + "Optional": "Optional", + "and": "and", + "or": "or", + "sameAsServerTimezone": "Same as Server Timezone", + "startDateTime": "Start Date/Time", + "pauseMaintenanceMsg": "Are you sure want to pause?", + "maintenanceStatus-under-maintenance": "Under Maintenance", + "maintenanceStatus-inactive": "Inactive", + "maintenanceStatus-scheduled": "Scheduled", + "IconUrl": "Icon URL", + "chromeExecutableDescription": "For Docker users, if Chromium is not yet installed, it may take a few minutes to install and display the test result. It takes 1GB of disk space.", + "dnsCacheDescription": "It may be not working in some IPv6 environments, disable it if you encounter any issues.", + "Maintenance Time Window of a Day": "Maintenance Time Window of a Day", + "ariaCloneMaintenance": "Create a copy of this maintenance schedule", + "ariaEditMaintenance": "Edit this maintenance schedule", + "ariaDeleteMaintenance": "Delete this maintenance schedule", + "Date and Time": "Date and Time", + "install": "Install", + "installing": "Installing", + "uninstall": "Uninstall", + "uninstalling": "Uninstalling", + "confirmUninstallPlugin": "Are you sure want to uninstall this plugin?", + "notificationRegional": "Regional", + "notificationUniversal": "Universal", + "notificationChatPlatforms": "Chat Platforms", + "Clone": "Clone", + "cloneOf": "Clone of {0}", + "smtp": "Email (SMTP)", + "SMTP Security": "SMTP Security", + "To Email": "To Email", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord Webhook URL", + "Send to channel": "Send to channel", + "wayToGetZohoCliqURL": "You can learn how to create a webhook URL {0}.", + "needSignalAPI": "You need to have a signal client with REST API.", + "wayToCheckSignalURL": "You can check this URL to view how to set one up:", + "Icon URL": "Icon URL", + "dataRetentionTimeError": "Retention period must be 0 or greater", + "confirmDeleteTagMsg": "Are you sure you want to delete this tag? Monitors associated with this tag will not be deleted.", + "enableGRPCTls": "Allow to send gRPC request with TLS connection", + "grpcMethodDescription": "Method name is convert to camelCase format such as sayHello, check, etc.", + "acceptedStatusCodesDescription": "Select status codes which are considered as a successful response.", + "deleteMonitorMsg": "Are you sure want to delete this monitor?", + "deleteMonitorsMsg": "Are you sure you want to delete the selected monitors?", + "pausedMonitorsMsg": "Paused {n} monitor | Paused {n} monitors", + "bulkDeleteErrorMsg": "Failed to delete {n} monitor | Failed to delete {n} monitors", + "deleteGroupMsg": "Are you sure you want to delete this group?", + "deleteChildrenMonitors": "Also delete the direct child monitors and its children if it has any | Also delete all {count} direct child monitors and their children if they have any", + "Events cleared successfully": "Events cleared successfully.", + "No monitors found": "No monitors found.", + "Could not clear events": "Could not clear {failed}/{total} events", + "confirmImportMsg": "Are you sure you want to import the backup? Please verify you've selected the correct import option.", + "octopushLogin": "\"Login\" from HTTP API credentials in control panel", + "promosmsLogin": "API Login Name", + "promosmsPassword": "API Password", + "pushoversounds pushover": "Pushover (default)", + "Copy the web app URL and paste it above": "Copy the web app URL and paste it above", + "Google Apps Script Code": "Google Apps Script Code", + "Copy to Clipboard": "Copy to Clipboard", + "Copied to clipboard!": "Copied to clipboard!", + "Failed to copy to clipboard": "Failed to copy to clipboard", + "Template plain text instead of using cards": "Template plain text instead of using cards", + "wayToGetKookGuildID": "Switch on 'Developer Mode' in Kook setting, and right click the guild to get its ID", + "Guild ID": "Guild ID", + "User Key": "User Key", + "Lowcost": "Lowcost", + "high": "high", + "SendKey": "SendKey", + "goAlertIntegrationKeyInfo": "Get generic API integration key for the service in this format \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" usually the value of token parameter of copied URL.", + "SecretAccessKey": "AccessKey Secret", + "Mention Mobile List": "Mention mobile list", + "Mention User List": "Mention user id list", + "Don't mention people": "Don't mention people", + "Mention group": "Mention {group}", + "Dingtalk Mobile List": "Mobile list", + "Dingtalk User List": "User ID list", + "Enter a list of userId": "Enter a list of userId", + "High": "High", + "promosmsTypeFlash": "SMS FLASH - Message will automatically show on recipient device. Limited only to Polish recipients.", + "matrixDesc1": "You can find the internal room ID by looking in the advanced section of the room settings in your Matrix client. It should look like !QMdRCpUIfLwsfjxye6:home.server.", + "Proxy server has authentication": "Proxy server has authentication", + "promosmsTypeEco": "SMS ECO - cheap but slow and often overloaded. Limited only to Polish recipients.", + "aboutJiraCloudId": "More info about Jira Cloud ID: {0}", + "see Jira Cloud Docs": "see Jira Cloud Docs", + "Jira Service Management": "Jira Service Management", + "aboutSlackUsername": "Changes the display name of the message sender. If you want to mention someone, include it in the friendly name instead.", + "slackIncludeGroupName": "Include monitor group name", + "slackIncludeGroupNameDescription": "If enabled, the monitor group path will be included in notifications to help distinguish monitors with the same name across different groups.", + "slackUseTemplate": "Use custom message template", + "slackUseTemplateDescription": "If enabled, the message will be sent using a custom template. You can use Liquid templating to include monitor group information via monitorJSON.path or monitorJSON.pathName.", + "aboutChannelName": "Enter the channel name on {0} Channel Name field if you want to bypass the Webhook channel. Ex: #other-channel", + "smtpDkimSettings": "DKIM Settings", + "wayToGetPagerDutyKey": "You can get this by going to Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Here you can search for \"Events API V2\". More info {0}", + "Integration URL": "Integration URL", + "Auto resolve or acknowledged": "Auto resolve or acknowledged", + "alertaApiKey": "API Key", + "alertaAlertState": "Alert State", + "alertaRecoverState": "Recover State", + "serwersmsAPIUser": "API Username (incl. webapi_ prefix)", + "serwersmsAPIPassword": "API Password", + "serwersmsPhoneNumber": "Phone number", + "serwersmsRecipientType": "Recipient type", + "serwersmsRecipientTypePhone": "Phone number", + "serwersmsRecipientTypeGroup": "Group", + "serwersmsGroupId": "Group ID", + "smseagleEncoding": "Send as Unicode (default=GSM-7)", + "smseaglePriority": "Message priority (0-9, highest priority = 9)", + "smseagleMsgType": "Message type", + "serwersmsGroupIdHelptext": "ID or group IDs in the Customer Panel. These identifiers can be downloaded using action groups / index or by copying them from the editing group in the Customer Panel.", + "serwersmsSenderName": "SMS Sender Name (registered via customer portal)", + "smseagleToken": "API Access token", + "smseagleUrl": "Your SMSEagle device URL", + "onebotGroupMessage": "Group", + "onebotPrivateMessage": "Private", + "onebotUserOrGroupId": "Group/User ID", + "onebotSafetyTips": "For safety, must set access token", + "PushDeer Server": "PushDeer Server", + "Analytics Script URL": "Analytics Script URL", + "Edit Tag": "Edit Tag", + "Server Address": "Server Address", + "Learn More": "Learn More", + "Body Encoding": "Body Encoding", + "API Keys": "API Keys", + "Expiry": "Expiry", + "Expiry date": "Expiry date", + "Don't expire": "Don't expire", + "Continue": "Continue", + "Add Another": "Add Another", + "Key Added": "Key Added", + "apiKeyAddedMsg": "Your API key has been added. Please make a note of it as it will not be shown again.", + "Add API Key": "Add API Key", + "twilloMessagingServiceSIDHelptext": "Enter your Messaging Service SID here if using {twillo_messaging_service_help_link} to manage senders and features", + "twilioAuthToken": "Auth Token / API Key Secret", + "twilioFromNumber": "From Number", + "twilioToNumber": "To Number", + "Monitor Setting": "{0}'s Monitor Setting", + "Show Clickable Link": "Show Clickable Link", + "Show Clickable Link Description": "If checked everyone who have access to this status page can have access to monitor URL.", + "Badge Label Color": "Badge Label Colour", + "Kafka SASL Options": "Kafka SASL Options", + "Mechanism": "Mechanism", + "Pick a SASL Mechanism...": "Pick a SASL Mechanism…", + "nostrRelays": "Nostr relays", + "nostrRelaysHelp": "One relay URL per line", + "Send rich messages": "Send rich messages", + "Bitrix24 Webhook URL": "Bitrix24 Webhook URL", + "wayToGetBitrix24Webhook": "You can create a webhook by following the steps at {0}", + "bitrix24SupportUserID": "Enter your user ID in Bitrix24. You can find out the ID from the link by going to the user's profile.", + "Saved.": "Saved.", + "successDeleted": "Deleted Successfully.", + "successEdited": "Edited Successfully.", + "Remote Browser not found!": "Remote Browser not found!", + "remoteBrowsersDescription": "Remote Browsers are an alternative to running Chromium locally. Set up with a service like browserless.io or connect to your own", + "self-hosted container": "self-hosted container", + "systemServiceName": "Service Name", + "systemServiceDescription": "Checks if system service {service_name} is active", + "receiverSevenIO": "Receiving number", + "receiverInfoSevenIO": "If the receiving number is not located in Germany, you have to add the country code in front of the number (e.g. for the country code 1 from the US use 117612121212 instead of 017612121212)", + "apiKeySevenIO": "SevenIO API Key", + "wayToWriteWhapiRecipient": "The phone number with the international prefix, but without the plus sign at the start ({0}), the Contact ID ({1}) or the Group ID ({2}).", + "wayToGetWhapiUrlAndToken": "You can get the API URL and the token by going into your desired channel from {0}", + "whapiRecipient": "Phone Number / Contact ID / Group ID", + "API URL": "API URL", + "wayToWriteEvolutionRecipient": "The phone number with the international prefix, but without the plus sign at the start ({0}), the Contact ID ({1}) or the Group ID ({2}).", + "evolutionInstanceName": "Instance Name", + "cellsyntOriginatortypeNumeric": "Numeric value (max 15 digits) with telephone number on international format without leading 00 (example UK number 07920 110 000 should be set as 447920110000). Recipients can reply to the message.", + "Originator": "Originator", + "cellsyntOriginator": "Visible on recipient's mobile phone as originator of the message. Allowed values and function depends on parameter originatortype.", + "Destination": "Destination", + "Allow Long SMS": "Allow Long SMS", + "threemaRecipientType": "Recipient Type", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypeIdentityFormat": "8 characters", + "threemaRecipientTypePhone": "Phone Number", + "threemaRecipientTypePhoneFormat": "E.164, without leading +", + "Token Onesender": "Token Onesender", + "groupOnesenderDesc": "Make sure the GroupID is valid. To send message into Group, ex: 628123456789-342345", + "wayToGetOnesenderUrlandToken": "You can get the URL and Token by going to the Onesender website. More info {0}", + "Add Remote Browser": "Add Remote Browser", + "New Group": "New Group", + "Group Name": "Group Name", + "OAuth2: Client Credentials": "OAuth2: Client Credentials", + "Authentication Method": "Authentication Method", + "Authorization Header": "Authorization Header", + "Form Data Body": "Form Data Body", + "OAuth Token URL": "OAuth Token URL", + "Client ID": "Client ID", + "Client Secret": "Client Secret", + "Optional: Space separated list of scopes": "Optional: Space separated list of scopes", + "Go back to home page.": "Go back to home page.", + "No tags found.": "No tags found.", + "Lost connection to the socket server.": "Lost connection to the socket server.", + "Cannot connect to the socket server.": "Cannot connect to the socket server.", + "SIGNL4": "SIGNL4", + "conditionAddGroup": "Add Group", + "conditionDeleteGroup": "Delete Group", + "conditionValuePlaceholder": "Value", + "equals": "equals", + "Doorbell": "Doorbell", + "Flute": "Flute", + "RabbitMQ Nodes": "RabbitMQ Management Nodes", + "Enter the list of nodes": "Enter the list of RabbitMQ management nodes", + "RabbitMQ Password": "RabbitMQ Password", + "brevoSubject": "Subject", + "brevoSeparateMultipleEmails": "Separate multiple email addresses with commas", + "brevoLeaveBlankForDefaultSubject": "leave blank for default subject", + "resendApiKey": "Resend API Key", + "pingGlobalTimeoutDescription": "Total time in seconds before ping stops, regardless of packets sent", + "Custom URL": "Custom URL", + "OneChatAccessToken": "OneChat Access Token", + "360messengerSelectGroupList": "Select a group to add", + "OneChatBotId": "OneChat Bot ID", + "wahaSession": "Session", + "360messengerRecipient": "Recipient phone number(s)", + "360messengerGroupId": "360messenger Group ID", + "360messengerUseTemplate": "Use a custom message template", + "360messengerTemplate": "360messenger Message Template", + "360messengerGroupList": "WhatsApp groups", + "360messengerSelectedGroupID": "Selected Group ID(s)", + "360messengerEnableSendToGroup": "Enable sending to WhatsApp group(s)", + "360messengerCustomMessageTemplate": "Custom message template", + "360messengerWayToWriteRecipient": "Enter one or more phone numbers in international format without a leading plus (e.g. {0}). Separate multiple numbers with commas.", + "360messengerErrorNoApiKey": "Please enter your 360messenger API key first.", + "360messengerErrorNoGroups": "No WhatsApp groups were found for this account.", + "smsplanetApiDocs": "Detailed information on obtaining API tokens can be found in {the_smsplanet_documentation}.", + "Suppress Notifications": "Suppress Notifications", + "the smsplanet documentation": "the smsplanet documentation", + "Phone numbers": "Phone numbers", + "Sender name": "Sender name", + "discordSuppressNotificationsHelptext": "When enabled, messages will be posted to the channel but won't trigger push or desktop notifications for recipients.", + "discordMessageFormat": "Message Format", + "discordMessageFormatNormal": "Normal (rich embeds)", + "discordMessageFormatCustom": "Custom template", + "discordUseMessageTemplate": "Use custom message template", + "discordUseMessageTemplateDescription": "If enabled, the message will be sent using a custom template (LiquidJS). Leave blank to use the default Uptime Kuma format.", + "discordMessageTemplate": "Message Template", + "fluxerMessageFormat": "Message Format", + "fluxerMessageFormatCustom": "Custom template", + "ipFamilyDescriptionAutoSelect": "Uses the {happyEyeballs} for determining the IP family.", + "Happy Eyeballs algorithm": "Happy Eyeballs algorithm", + "Add Another Tag": "Add Another Tag", + "fluxerMessageFormatNormal": "Normal (rich embeds)", + "fluxerMessageFormatMinimalist": "Minimalist (short status)", + "Staged Tags for Batch Add": "Staged Tags for Batch Add", + "Clear Form": "Clear Form", + "Allow Notifications": "Allow Notifications", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Domain expiry monitoring is not available for \".{publicSuffix}\" because no RDAP service is listed by IANA", + "minimumIntervalWarning": "Intervals below 20 seconds may result in poor performance.", + "Halo PSA": "Halo PSA", + "domain_expiry_unsupported_is_icann": "The domain \"{domain}\" is not a candidate for domain expiry monitoring, because its public suffix \".{publicSuffix}\" is not managed by ICANN", + "imageResetConfirmation": "Image reset to default", + "screenshot of the website": "Screenshot of the website", + "Basic checkbox toggle button group": "Basic checkbox toggle button group", + "Basic radio toggle button group": "Basic radio toggle button group", + "mtls-auth-server-cert-label": "Cert", + "mtls-auth-server-cert-placeholder": "Cert body", + "avgPing": "Avg Ping", + "Uptime Kuma": "Uptime Kuma", + "maxPing": "Max Ping", + "minPing": "Min Ping", + "Setup Instructions": "Setup Instructions", + "halopsa_setup_step1": "Create an Integration Runbook in HaloPSA (Configuration → Integrations → Integration Runbooks)", + "Sort by uptime": "Sort by uptime", + "Sort by certificate expiry": "Sort by certificate expiry", + "Splunk Rest URL": "Splunk Rest URL", + "Severity": "Severity", + "Message Format": "Message Format", + "Sort by status": "Sort by status", + "Sort by name": "Sort by name", + "To Number": "To Number", + "GrafanaOncallURL": "Grafana Oncall URL", + "Show this Maintenance Message on which Status Pages": "Show this Maintenance Message on which Status Pages", + "Details": "Details", + "Expand All Groups": "Expand All Groups", + "Collapse All Groups": "Collapse All Groups", + "teltonikaVersionWarning": "This notification provider requires that your Teltonika device runs RMS version 7.14.0, or higher.", + "teltonikaUrl": "Your Teltonika device URL", + "teltonikaUrlHelptext": "URL should be specified as full origin, e.g. {0}, or {1}.", + "teltonikaUnsafeTls": "Ignore certificate validation", + "teltonikaUnsafeTlsDescription": "Turning off TLS certificate validation opens you up to on-path (man-in-the-middle) attacks, potentially leading to data leaks and systems take-over. Do not turn off certificate validation unless you accept this attack vector. We recomend using LetsEncrypt with automatic renewal.", + "WebSocket Options": "WebSocket Options", + "pingPacketSizeWarning": "A packet size smaller than 16 does not produce round-trip times on Linux", + "wayToGetEgoSMSToken": "You can get your EgoSMS API username and password from {here}.", + "egosmsSenderDescription": "Sender ID shown to recipients (max 11 characters). Defaults to EGOSMS if empty.", + "egosmsPhoneNumberDescription": "Recipient number with country code, without + or 00 (e.g. 2567XXXXXXXX).", + "VKTeams Chat Id Description": "For users, this is their email address. For groups and channels, get the ID from their settings and append {'@'}chat.agent", + "VKTeams Bot Token Description": "You can find out how to get a bot token and other details at {0}.", + "VKTeams Base Url Description": "VKTeams base API URL may differ in the on-premises solution. Default: https://myteam.mail.ru", + "VKTeams Use Template": "Use custom message template", + "Bearer Token": "Bearer Token", + "gamedigToken": "Token", + "gamedigTokenDescription": "Optional. Some game servers require an authentication token.", + "VKTeams Use Template Description": "If enabled, the message will be sent using a custom template.", + "VKTeams Template Format Description": "For message styling, VKTeams supports plain text, MarkdownV2, and HTML formatting.", + "Connection Timeout": "Connection Timeout", + "smtpAdditionalHeadersTitle": "Additional Headers", + "smtpAdditionalHeadersDesc": "Please refer to the Nodemailer Custom Headers {0} for usage.", + "Flowtriq Webhook URL": "Webhook URL", + "Flowtriq API Key": "API Key", + "flowtriqApiKeyDescription": "Optional API key for authenticating webhook requests to your Flowtriq instance.", + "wxPusherDownloadApp": "download the app", + "wxPusherDocs": "official guide", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) delivers through the standalone WxPusher app across 6 platforms — Android, iOS, HarmonyOS, Windows, macOS and Linux ({download}). Scan the QR code below to get your SPT and you are ready to send. Separate multiple SPTs with a comma (,) — see the {docs} for more.", + "Access Key": "Access Key", + "ooredooUsernameDescription": "The email address you use to sign in to the Ooredoo bulk SMS dashboard.", + "ooredooAccessKeyDescription": "Found under \"API Access Key\" on your Ooredoo SMS dashboard. Paste it exactly as shown — it is base64-encoded automatically before sending.", + "ooredooBearerTokenDescription": "The token shown after \"Authorization: Bearer\" in the sample cURL command on your Ooredoo SMS dashboard.", + "ooredooRecipientDescription": "Enter one or more numbers separated by a comma or space. Local 7-digit numbers are automatically prefixed with 960.", + "Leave blank to use the default Ooredoo endpoint.": "Leave blank to use the default Ooredoo endpoint.", + "plivoAuthID": "Auth ID", + "plivoAuthIDHelptext": "Your Plivo Auth ID from the Plivo Console", + "plivoAuthToken": "Auth Token", + "plivoAuthTokenHelptext": "Your Plivo Auth Token from the Plivo Console", + "plivoFromNumber": "From Number", + "plivoFromNumberHelptext": "The Plivo phone number to send from (e.g. +15551234567). SMS may also use an approved alphanumeric sender ID; voice calls require a Plivo phone number.", + "plivoToNumber": "To Number", + "plivoToNumberHelptext": "The destination phone number that receives the SMS or voice call (e.g. +15559876543)", + "pm2Description": "Checks if PM2 process {service_name} is online", + "pm2ExpectedStates": "Expected state: online. States stopped and errored are treated as DOWN.", + "PM2 Process": "PM2 Process", + "Refresh": "Refresh", + "Select PM2 process": "Select PM2 process", + "plivoMessageType": "Message Type", + "plivoVoiceCall": "Voice Call", + "plivoAnswerUrl": "Answer URL", + "plivoAnswerUrlHelptext": "URL that returns Plivo XML for the call. The alert text is appended as a ?message= query parameter, so your endpoint can return e.g. {message}.", + "ntpStratumThreshold": "Stratum Threshold", + "ntpOffsetThreshold": "Time Offset Threshold (ms)", + "ntpOffsetThresholdHelp": "Mark as down if time offset exceeds this value in milliseconds.", + "ntpDispersionThreshold": "Root Dispersion Threshold (ms)", + "ntpThresholdsTitle": "NTP Thresholds", + "ntpStratumThresholdHelp": "Mark as down if stratum is at or above this value. Stratum indicates time source quality (1=GPS/atomic clock, 16=unsynchronised).", + "ntpDispersionThresholdHelp": "Mark as down if root dispersion exceeds this value. Dispersion indicates the server's clock accuracy.", + "Client Key": "Client Key", + "Channel": "Channel", + "Indigo URL": "Indigo URL", + "indigoUrlHelptext": "Your Indigo Reflector address (https://NAME.indigodomo.net) or the Indigo Web Server on your network (https://IP:8176).", + "indigoIgnoreTlsErrorHelptext": "Needed for the Indigo Web Server on your network, which uses a self-signed certificate. Not needed for the Reflector.", + "indigoApiKeyHelptext": "An API key from your Indigo Account (Authorisations), or a local secret for the Web Server on your network.", + "Variable ID": "Variable ID", + "indigoVariableIdHelptext": "The alert message is written to this variable.", + "Action Group ID": "Action Group ID", + "indigoActionGroupIdHelptext": "Executed after the variable is updated. Set a variable, an action group, or both. Copy IDs from Indigo with right-click → Copy ID.", + "Hash Key": "Hash Key", + "wayToGetBearSMSToken": "You can get your BearSMS API username and hash key from {0}.", + "bearsmsPhoneNumberDescription": "Recipient number with country code, without + or 00 (e.g. 9725XXXXXXXX).", + "bearsmsSenderDescription": "Optional sender ID shown to recipients (max 11 characters). It must be approved in your BearSMS account, otherwise the account's default sender is used.", + "Device ID or Group ID": "Device ID or Group ID", + "notifyAppDeviceIdDescription": "The Device ID from the Notify! app, or a Group ID to alert every device in a group at once.", + "notifyAppSetup": "Open Notify! and find your Device ID and token under Settings. No account is required. Learn more at {0}.", + "Pinglet Topic URL": "Topic URL", + "pingletTopicUrlDescription": "The publish URL of the Pinglet topic to send alerts to, e.g. https://app.pinglet.co.uk/your-namespace/alerts. The topic is created on first publish if it doesn't exist yet.", + "ClickUp API Token": "ClickUp API Token", + "ClickUp API Token Description": "You can create a personal access token in ClickUp by going to Settings -> Integrations & ClickApps -> ClickUp API -> API Token", + "ClickUp Workspace ID": "ClickUp Workspace ID", + "ClickUp Workspace ID Description": "You can find it in your workspace's URL, right after app.clickup.com/, e.g. app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ClickUp Channel ID", + "ClickUp Channel ID Description": "You can find it in the Chat channel's URL, right after /chat/r/, e.g. .../chat/r/abc123-4567.", + "turbosmtpConsumerKey": "Consumer Key", + "turbosmtpConsumerSecret": "Consumer Secret", + "SFTP Path to Check": "SFTP Path to Check", + "sftpPathHelpText": "Optional. If specified, the monitor will verify that this path exists on the SFTP server.", + "SSH Private Key": "SSH Private Key", + "Passphrase": "Passphrase", + "sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----", + "sshPrivateKeyHelpText": "Paste the contents of your private key file (e.g. id_rsa or id_ed25519).", + "sshPassphraseHelpText": "Optional. Required only if your private key is encrypted with a passphrase.", + "optional": "optional", + "openwaSession": "Session", + "openwaChatId": "Chat ID (Phone Number / Contact ID / Group ID)", + "wayToGetOpenwaApiUrl": "Your OpenWA Instance URL.", + "wayToGetOpenwaApiKey": "API key used for your OpenWA instance.", + "wayToGetOpenwaSession": "Session ID used by OpenWA to send notifications.", + "wayToWriteOpenwaChatId": "The phone number with the international prefix, but without the plus sign at the start ({0}), the Contact ID ({1}) or the Group ID ({2}). You can send to multiple Chat IDs by separating them with commas.", + "openwaCustomMessageTitle": "Custom Message (optional)", + "openwaCustomMessageDesc": "Leave empty to use the default message.", + "milkyHttpAddress": "Milky HTTP Address(Without the '/api' suffix)", + "milkyMessageType": "Milky Message Type", + "milkyGroupMessage": "Group", + "milkyPrivateMessage": "Private", + "milkyUserOrGroupId": "Group/User ID", + "milkySafetyTips": "For safety, must set access token", + "smsgatewayUrlHelptext": "The base URL of your SMS Gateway, without an API path.", + "smsgatewayTo": "Phone Number(s)", + "smsgatewayToHelptext": "Comma-separated list of recipient phone numbers in international format (e.g. +15551234567)", + "Line Number": "Line Number", + "Amoot SMS recipient numbers help": "Enter one or more mobile numbers separated by commas.", + "Use Pattern": "Use Pattern", + "Pattern Code ID": "Pattern Code ID", + "Amoot SMS pattern help": "Enter the Pattern Code ID from your Amoot SMS panel. {0}", + "Create an Amoot SMS pattern": "Create an Amoot SMS pattern", + "Use Own Line for Pattern": "Use Own Line for Pattern" +} diff --git a/src/lang/enm.json b/src/lang/enm.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/lang/enm.json @@ -0,0 +1 @@ +{} diff --git a/src/lang/es-ES.json b/src/lang/es-ES.json new file mode 100644 index 0000000..0eaf820 --- /dev/null +++ b/src/lang/es-ES.json @@ -0,0 +1,1463 @@ +{ + "languageName": "Español", + "checkEverySecond": "Comprobar cada {0} segundos", + "retriesDescription": "Número máximo de intentos antes de que el servicio se marque como CAÍDO y una notificación sea enviada", + "ignoreTLSError": "Ignorar errores TLS/SSL para sitios web HTTPS", + "upsideDownModeDescription": "Invertir el estado. Si el servicio es alcanzable, está CAÍDO.", + "maxRedirectDescription": "Número máximo de direcciones a seguir. Establecer a 0 para deshabilitar.", + "acceptedStatusCodesDescription": "Seleccionar los códigos de estado que se consideran como respuesta exitosa.", + "passwordNotMatchMsg": "La contraseña repetida no coincide.", + "notificationDescription": "Por favor asigna una notificación a el/los monitor(es) para hacerlos funcional(es).", + "keywordDescription": "Buscar palabra clave en HTML plano o respuesta JSON. La búsqueda es sensible a mayúsculas.", + "pauseDashboardHome": "Pausar", + "deleteMonitorMsg": "¿Seguro que quieres eliminar este monitor?", + "deleteNotificationMsg": "¿Seguro que quieres eliminar esta notificación para todos los monitores?", + "resolverserverDescription": "Cloudflare es el servidor por defecto. Puedes especificar una lista de direcciones IP o nombres de host separados por comas.", + "rrtypeDescription": "Selecciona el tipo de registro que quieres monitorizar", + "pauseMonitorMsg": "¿Seguro que quieres pausar?", + "Settings": "Ajustes", + "Dashboard": "Panel", + "New Update": "Nueva actualización", + "Language": "Idioma", + "Appearance": "Apariencia", + "Theme": "Tema", + "General": "General", + "Version": "Versión", + "Check Update On GitHub": "Comprobar actualizaciones en GitHub", + "List": "Lista", + "Add": "Añadir", + "Add New Monitor": "Añadir nuevo monitor", + "Quick Stats": "Estadísticas rápidas", + "Up": "Funcional", + "Down": "Caído", + "Pending": "Pendiente", + "Unknown": "Desconocido", + "Pause": "Pausar", + "Name": "Nombre", + "Status": "Estado", + "DateTime": "Fecha y hora", + "Message": "Mensaje", + "No important events": "No hay eventos importantes", + "Resume": "Reanudar", + "Edit": "Editar", + "Delete": "Eliminar", + "Current": "Actual", + "Uptime": "Tiempo activo", + "Cert Exp.": "Caducidad cert.", + "day": "día | días", + "-day": "-día", + "hour": "hora | horas", + "-hour": "-hora", + "Response": "Respuesta", + "Ping": "Ping", + "Monitor Type": "Tipo de monitor", + "Keyword": "Palabra clave", + "Friendly Name": "Nombre sencillo", + "URL": "URL", + "Hostname": "Nombre del host", + "Port": "Puerto", + "Heartbeat Interval": "Intervalo de latido", + "Retries": "Reintentos", + "Advanced": "Avanzado", + "Upside Down Mode": "Modo invertido", + "Max. Redirects": "Redirecciones máximas", + "Accepted Status Codes": "Códigos de estado aceptados", + "Save": "Guardar", + "Notifications": "Notificaciones", + "Not available, please setup.": "No disponible, por favor configúralo.", + "Setup Notification": "Configurar notificación", + "Light": "Claro", + "Dark": "Oscuro", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Tema - Barra de intervalo de latido", + "Normal": "Normal", + "Bottom": "Abajo", + "None": "Ninguno", + "Timezone": "Zona horaria", + "Search Engine Visibility": "Visibilidad motor de búsqueda", + "Allow indexing": "Permitir indexación", + "Discourage search engines from indexing site": "Disuadir a los motores de búsqueda de indexar el sitio", + "Change Password": "Cambiar contraseña", + "Current Password": "Contraseña actual", + "New Password": "Nueva contraseña", + "Repeat New Password": "Repetir nueva contraseña", + "Update Password": "Actualizar contraseña", + "Disable Auth": "Deshabilitar autenticación", + "Enable Auth": "Habilitar autenticación", + "disableauth.message1": "Seguro que deseas {disableAuth}?", + "disable authentication": "deshabilitar la autenticación", + "disableauth.message2": "Es para {intendThirdPartyAuth} ante Uptime Kuma como por ejemplo Cloudflare Access.", + "where you intend to implement third-party authentication": "quien implementa autenticación de terceros", + "Please use this option carefully!": "¡Utilice esta opción con cuidado!", + "Logout": "Cerrar sesión", + "Leave": "Salir", + "I understand, please disable": "Entiendo, por favor deshabilitar", + "Confirm": "Confirmar", + "Yes": "Sí", + "No": "No", + "Username": "Usuario", + "Password": "Contraseña", + "Remember me": "Recordarme", + "Login": "Acceso", + "No Monitors, please": "Sin monitores, por favor", + "add one": "añade uno", + "Notification Type": "Tipo de notificación", + "Email": "Email", + "Test": "Test", + "Certificate Info": "Información del certificado", + "Resolver Server": "Servidor de resolución", + "Resource Record Type": "Tipo de registro", + "Last Result": "Último resultado", + "Create your admin account": "Crea tu cuenta de administrador", + "Repeat Password": "Repetir contraseña", + "respTime": "Tiempo de resp. (ms)", + "notAvailableShort": "N/A", + "Create": "Crear", + "clearEventsMsg": "¿Estás seguro de que deseas eliminar todos los eventos de este monitor? Esto permanentemente remueve los datos asociados de los mensajes y no se puede deshacer", + "clearHeartbeatsMsg": "¿Estás seguro de que deseas eliminar todos los latidos de este monitor? Esto permanentemente remueve los datos asociados de los latidos y no se puede deshacer.", + "confirmClearStatisticsMsg": "¿Estás seguro de que deseas eliminar TODAS las estadísticas?", + "Clear Data": "Borrar datos", + "Events": "Eventos", + "Heartbeats": "Latidos", + "Auto Get": "Obtener automáticamente", + "enableDefaultNotificationDescription": "Para cada nuevo monitor, esta notificación estará habilitada de forma predeterminada. Aún puedes deshabilitar la notificación por separado para cada monitor.", + "Default enabled": "Habilitado por defecto", + "Also apply to existing monitors": "También se aplica a monitores existentes", + "Export": "Exportar", + "Import": "Importar", + "backupDescription": "Puedes hacer una copia de seguridad de todos los monitores y todas las notificaciones en un archivo JSON.", + "backupDescription2": "PD: el historial y los datos de eventos no están incluidos.", + "backupDescription3": "Los datos confidenciales, como los tokens de notificación, se incluyen en el archivo de exportación. Guárdalo con cuidado.", + "alertNoFile": "Selecciona un archivo para importar.", + "alertWrongFileType": "Selecciona un archivo JSON.", + "twoFAVerifyLabel": "Ingresa tu token para verificar que 2FA está funcionando:", + "tokenValidSettingsMsg": "¡El token es válido! Ahora puedes guardar la configuración de 2FA.", + "confirmEnableTwoFAMsg": "¿Estás seguro de que quieres habilitar 2FA?", + "confirmDisableTwoFAMsg": "¿Estás seguro de que quieres desactivar 2FA?", + "Apply on all existing monitors": "Aplicar en todos los monitores existentes", + "Verify Token": "Verificar token", + "Setup 2FA": "Configurar 2FA", + "Enable 2FA": "Habilitar 2FA", + "Disable 2FA": "Desactivar 2FA", + "2FA Settings": "Ajustes 2FA", + "Two Factor Authentication": "Autenticación de dos factores", + "Active": "Activo", + "Inactive": "Inactivo", + "Token": "Token", + "Show URI": "Mostrar URI", + "Clear all statistics": "Borrar todas las estadísticas", + "retryCheckEverySecond": "Reintentar cada {0} segundos", + "importHandleDescription": "Elige 'Omitir existente' si deseas omitir todos los monitores o notificaciones con el mismo nombre. 'Sobrescribir' eliminará todos los monitores y notificaciones existentes.", + "confirmImportMsg": "¿Estás seguro de importar la copia de seguridad? Asegúrate de haber seleccionado la opción de importación correcta.", + "Heartbeat Retry Interval": "Intervalo de reintento de latido", + "Import Backup": "Importar copia de seguridad", + "Export Backup": "Exportar copia de seguridad", + "Skip existing": "Omitir existente", + "Overwrite": "Sobrescribir", + "Options": "Opciones", + "Keep both": "Mantener ambos", + "Tags": "Etiquetas", + "Add New below or Select...": "Agregar nuevo a continuación o seleccionar…", + "Tag with this name already exist.": "Una etiqueta con este nombre ya existe.", + "Tag with this value already exist.": "Una etiqueta con este valor ya existe.", + "color": "Color", + "value (optional)": "valor (opcional)", + "Gray": "Gris", + "Red": "Rojo", + "Orange": "Naranja", + "Green": "Verde", + "Blue": "Azul", + "Indigo": "Índigo", + "Purple": "Morado", + "Pink": "Rosa", + "Search...": "Buscar…", + "Avg. Ping": "Ping promedio", + "Avg. Response": "Respuesta promedio", + "Entry Page": "Página de entrada", + "statusPageNothing": "No hay nada aquí, agrega un grupo o un monitor.", + "No Services": "Sin servicio", + "All Systems Operational": "Todos los sistemas están operativos", + "Partially Degraded Service": "Servicio parcialmente degradado", + "Degraded Service": "Servicio degradado", + "Add Group": "Agregar grupo", + "Add a monitor": "Agregar un monitor", + "Edit Status Page": "Editar página de estado", + "Go to Dashboard": "Ir al panel de control", + "Status Page": "Página de estado", + "Status Pages": "Páginas de estado", + "telegram": "Telegram", + "webhook": "Webhook", + "smtp": "Email (SMTP)", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Señal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Admite más de 50 servicios de notificación)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "Monitor History": "Historial de monitor", + "clearDataOlderThan": "Mantener los datos del historial del monitor durante {0} días.", + "records": "registros", + "One record": "Un registro", + "steamApiKeyDescription": "Para monitorear un servidor de juegos de Steam, necesitas una clave Steam Web-API. Puedes registrar tu clave API aquí: ", + "Custom Monitor Type": "Monitor Tipo Personalizado", + "Primary Base URL": "URL Base Primaria", + "Passive Monitor Type": "Monitor Tipo Pasivo", + "pushOptionalParams": "Parámetros opcionales: {0}", + "Schedule maintenance": "Programar mantenimiento", + "Pick Affected Monitors...": "Seleccionar Monitores Afectados…", + "Start of maintenance": "Inicio del mantenimiento", + "All Status Pages": "Todas las Páginas de Estado", + "Select status pages...": "Seleccionar páginas de estado…", + "Style": "Estilo", + "info": "información", + "warning": "advertencia", + "danger": "peligro", + "critical": "crítico", + "primary": "primario", + "Content": "Contenido", + "recent": "Reciente", + "Done": "Terminado", + "Create Incident": "Crear Incidente", + "Title": "Título", + "Info": "Información", + "Security": "Seguridad", + "Current User": "Usuario Actual", + "topic": "Asunto", + "Shrink Database": "Reducir Base de Datos", + "dark": "oscuro", + "light": "claro", + "Last Updated": "Última Actualización", + "Show Tags": "Mostrar Etiquetas", + "Switch to Light Theme": "Cambiar a Tema Claro", + "Add one": "Añadir uno", + "Description": "Descripción", + "Cancel": "Cancelar", + "No Monitors": "Sin Monitores", + "Untitled Group": "Grupo sin título", + "Services": "Servicios", + "Discard": "Descartar", + "Add New Status Page": "Añadir Nueva Página de Estado", + "Start": "Iniciar", + "Stop": "Parar", + "Remove Token": "Eliminar Token", + "Powered by": "Potenciado por", + "Customize": "Personalizar", + "Custom Footer": "Pie Personalizado", + "Custom CSS": "CSS Personalizado", + "Backup": "Respaldo", + "Go back to the previous page.": "Volver a la página anterior.", + "Query": "Consulta", + "Examples": "Ejemplos", + "weekdayShortMon": "Lun", + "weekdayShortWed": "Mie", + "weekdayShortSat": "Sab", + "Ignore TLS Error": "Ignorar Error TLS", + "secureOptionNone": "Ninguno / STARTTLS (25, 587)", + "Schedule Maintenance": "Programar Mantenimiento", + "Date and Time": "Fecha y Hora", + "Enable": "Habilitar", + "Disable": "Deshabilitar", + "maintenanceStatus-inactive": "Inactivo", + "maintenanceStatus-scheduled": "Programado", + "maintenanceStatus-unknown": "Desconocido", + "Display Timezone": "Mostrar Zona Horaria", + "Server Timezone": "Servidor de Zona Horaria", + "statusPageMaintenanceEndDate": "Finaliza", + "Enable DNS Cache": "(Obsoleto) Habilitar caché DNS para monitores HTTP(s)", + "No Maintenance": "Sin Mantenimiento", + "weekdayShortSun": "Dom", + "dayOfWeek": "Día de la Semana", + "dayOfMonth": "Día del Mes", + "lastDay": "Último día", + "lastDay1": "Último Día del Mes", + "pauseMaintenanceMsg": "¿Seguro que quiere pausar?", + "maintenanceStatus-under-maintenance": "En Mantenimiento", + "DateTime Range": "Rango de Fecha y Hora", + "infiniteRetention": "Poner a 0 para retención infinita.", + "confirmDeleteTagMsg": "¿Estas seguro que quieres eliminar esta etiqueta? Los monitores asociados a esta etiqueta no serán eliminados.", + "Example:": "Ejemplo: {0}", + "Strategy": "Estrategia", + "Read more:": "Leer más: {0}", + "onebotGroupMessage": "Grupo", + "Affected Monitors": "Monitores Afectados", + "Custom": "Personalizado", + "Headers": "Encabezados", + "PhoneNumbers": "Números de Teléfono", + "No monitors available.": "Sin monitores disponibles.", + "error": "error", + "deleteProxyMsg": "¿Seguro que quieres eliminar este proxy para todos los monitores?", + "Hide Tags": "Ocultar Etiquetas", + "Created": "Creado", + "Switch to Dark Theme": "Cambiar a Tema Oscuro", + "More info on:": "Más información en: {0}", + "weekdayShortTue": "Mar", + "PasswordsDoNotMatch": "Las contraseñas no coinciden.", + "statusMaintenance": "Mantenimiento", + "Maintenance": "Mantenimiento", + "General Monitor Type": "Monitor Tipo General", + "Specific Monitor Type": "Monitor Tipo Específico", + "Monitor": "Monitor | Monitores", + "Resend Notification if Down X times consecutively": "Reenviar Notificación si Caído X veces consecutivamente", + "resendEveryXTimes": "Reenviar cada {0} veces", + "resendDisabled": "Reenvío deshabilitado", + "needPushEvery": "Debe llamar a esta URL cada {0} segundos.", + "here": "aquí", + "Content Type": "Tipo de Contenido", + "Required": "Requerido", + "defaultNotificationName": "Mi {notification} Alerta ({number})", + "Server URL": "URL del servidor", + "Priority": "Prioridad", + "Read more": "Leer más", + "Body": "Cuerpo", + "webhookAdditionalHeadersTitle": "Encabezados Adicionales", + "Method": "Método", + "Default": "Predeterminado", + "uninstalling": "Desinstalando", + "install": "Instalar", + "installing": "Instalando", + "uninstall": "Desinstalar", + "confirmUninstallPlugin": "¿Estas seguro que quieres desinstalar este complemento?", + "Recipients": "Destinatarios", + "User ID": "ID de Usuario", + "deleteMaintenanceMsg": "¿Seguro que quieres eliminar este mantenimiento?", + "promosmsLogin": "Nombre de inicio de sesión de la API", + "SMS Type": "Tipo de SMS", + "Device": "Dispositivo", + "Message Title": "Título del Mensaje", + "Notification Sound": "Sonido de Notificación", + "documentation": "documentación", + "onebotUserOrGroupId": "Grupo/ID de Usuario", + "Game": "Juego", + "or": "ó", + "Status:": "Estado: {0}", + "Help": "Ayuda", + "HTTP Options": "Opciones HTTP", + "weekdayShortThu": "Jue", + "weekdayShortFri": "Vie", + "maintenanceStatus-ended": "Finalizado", + "BodyInvalidFormat": "El cuerpo de la solicitud no es JSON válido: ", + "topicExplanation": "Tema MQTT para monitorear", + "successMessageExplanation": "Mensaje MQTT que será considerado como éxito", + "Steam API Key": "API Key de Steam", + "Please input title and content": "Por favor introduzca título y contenido", + "Footer Text": "Texto del Pié", + "Show Powered By": "Mostrar Potenciado Por", + "Domain Names": "Nombres de Dominio", + "signedInDisp": "Iniciada sesión como {0}", + "RadiusSecretDescription": "Secreto Compartido entre cliente y servidor", + "RadiusCalledStationId": "Id de la Estación Llamada", + "RadiusCalledStationIdDescription": "Identificador del dispositivo llamado", + "RadiusCallingStationId": "Id de Estación Llamante", + "Certificate Expiry Notification": "Notificación de Caducidad del Certificado", + "API Username": "Nombre Usuario API", + "API Key": "Clave API", + "Show update if available": "Mostrar actualización si está disponible", + "Also check beta release": "Comprobar también lanzamientos beta", + "Steam Game Server": "Servidor de Juegos de Steam", + "Most likely causes:": "Las causas más probables:", + "There might be a typing error in the address.": "Debe haber un error de escritura en la dirección.", + "What you can try:": "Lo que puedes intentar:", + "Retype the address.": "Reescriba la dirección.", + "Coming Soon": "Próximamente", + "Connection String": "Cadena de Conexión", + "settingsCertificateExpiry": "Caducidad del certificado TLS", + "certificationExpiryDescription": "Los monitores HTTPS activan una notificación cuando el certificado TLS caduca en:", + "Setup Docker Host": "Configurar Host de Docker", + "Connection Type": "Tipo de Conexión", + "Docker Daemon": "Demonio Docker", + "deleteDockerHostMsg": "¿Estas seguro que quieres eliminar este host de docker para todos los monitores?", + "Date Created": "Fecha de Creación", + "signedInDispDisabled": "Autenticación Deshabilitada.", + "RadiusCallingStationIdDescription": "Identificador del dispositivo llamante", + "Using a Reverse Proxy?": "¿Usando un Proxy Inverso?", + "Check how to config it for WebSocket": "Compruebe cómo configurarlo para WebSocket", + "The resource is no longer available.": "El recurso ya no está disponible.", + "Push URL": "URL Push", + "Webhook URL": "URL Webhook", + "Application Token": "Token de Aplicación", + "appriseNotInstalled": "Apprise no está instalado. {0}", + "PushUrl": "URL Push", + "HeadersInvalidFormat": "Los encabezados de solicitud no son JSON válido: ", + "Post URL": "URL Post", + "emojiCheatSheet": "Hoja de trucos Emoji: {0}", + "webhookJsonDesc": "{0} es bueno para cualquier servidor HTTP moderno como Express.js", + "webhookFormDataDesc": "{multipart} es bueno para PHP. El JSON deberá analizarse con {decodeFunction}", + "webhookAdditionalHeadersDesc": "Establece encabezados adicionales enviados con el webhook. Cada cabecera debe definirse como una clave/valor JSON.", + "appriseInstalled": "Apprise está instalado.", + "successMessage": "Mensaje de éxito", + "Pick Accepted Status Codes...": "Seleccione Códigos de Estado Aceptados…", + "Post": "Post", + "deleteStatusPageMsg": "¿Estas seguro que quieres eliminar esta página de estado?", + "default": "Predeterminado", + "enabled": "Habilitado", + "setAsDefault": "Establecer Por Defecto", + "proxyDescription": "Proxies deben ser asignados a un monitor para que funcionen.", + "warningTimezone": "Está usando la zona horaria del servidor", + "trustProxyDescription": "Confiar en los encabezados 'X-Forwarded-*'. Si desea obtener la IP de cliente correcta y su Uptime Kuma está detrás de un proxy como Nginx o Apache, debe habilitar esto.", + "enableProxyDescription": "Este proxy no afectará las solicitudes de monitoreo hasta que se active. Puede controlar deshabilitar temporalmente el proxy de todos los monitores por estado de activación.", + "setAsDefaultProxyDescription": "Este proxy estará habilitado de forma predeterminada para los nuevos monitores. Todavía puede deshabilitar el proxy por separado para cada monitor.", + "Certificate Chain": "Cadena de certificado", + "Valid": "Válido", + "Invalid": "Inválido", + "User": "Usuario", + "Installed": "Instalado", + "Not installed": "No instalado", + "Running": "Funcionando", + "Not running": "No funcionando", + "Slug": "Slug", + "Accept characters:": "Aceptar caracteres:", + "Proxies": "Proxys", + "startOrEndWithOnly": "Empezar o terminar sólo con {0}", + "No consecutive dashes": "Sin guiones consecutivos", + "Next": "Siguiente", + "The slug is already taken. Please choose another slug.": "Este slug ya está en uso. Por favor, elige otro slug.", + "No Proxy": "Sin Proxy", + "Authentication": "Autenticación", + "HTTP Basic Auth": "Autenticación básica HTTP", + "New Status Page": "Nueva Página de Estado", + "Page Not Found": "Página No Encontrada", + "Reverse Proxy": "Proxy Inverso", + "About": "Acerca de", + "wayToGetCloudflaredURL": "(Descargar cloudflared de {0})", + "cloudflareWebsite": "Web de Cloudflare", + "Message:": "Mensaje:", + "Don't know how to get the token? Please read the guide:": "No sabes como obtener el token? Por favor, lee la guía:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "La conexión actual puede perderse si actualmente se está conectando a través del Tunel Cloudflare. ¿Seguro que quieres detenerlo? Escriba su contraseña actual para confirmarlo.", + "HTTP Headers": "Encabezados HTTP", + "Trust Proxy": "Proxy de Confianza", + "Other Software": "Otro Software", + "For example: nginx, Apache and Traefik.": "Por ejemplo: nginx, Apache y Traefik.", + "Please read": "Por favor lee", + "Subject:": "Asunto:", + "Valid To:": "Válido Para:", + "Days Remaining:": "Días Restantes:", + "Issuer:": "Emisor:", + "Fingerprint:": "Huella:", + "No status pages": "Sin páginas de estado", + "Domain Name Expiry Notification": "Notificación de Caducidad de Nombre de Dominio", + "Proxy": "Proxy", + "RadiusSecret": "Secreto de Radius", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Contenedor de Docker", + "Container Name / ID": "Nombre / ID de Contenedor", + "Docker Host": "Host Docker", + "Docker Hosts": "Hosts Docker", + "Domain": "Dominio", + "Workstation": "Puesto de Trabajo", + "Packet Size": "Tamaño del Paquete", + "ZohoCliq": "ZohoCliq", + "Bot Token": "Token de Bot", + "wayToGetTelegramToken": "Puedes conseguir un token desde {0}.", + "Chat ID": "ID de Chat", + "supportTelegramChatID": "Chat Directo de Soporte / Grupo / ID de Chat del Canal", + "wayToGetTelegramChatID": "Puedes obtener tu ID de chat enviando un mensaje al bot y visitando esta URL para ver el chat_id:", + "YOUR BOT TOKEN HERE": "TU TOKEN DE BOT AQUÍ", + "chatIDNotFound": "ID de Chat no encontrada; por favor, primero envía un mensaje a este bot", + "disableCloudflaredNoAuthMsg": "Estas en modo No Autenticado, no es necesaria una contraseña.", + "wayToGetLineNotifyToken": "Puede obtener un token de acceso desde {0}", + "Home Assistant URL": "URL de Asistente de Hogar", + "Long-Lived Access Token": "Token de acceso de larga duración", + "Notification Service": "Servicio de Notificaciones", + "default: notify all devices": "predeterminado: notificar todos los dispositivos", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Puede encontrar una lista de Servicios de notificación en Asistente de Hogar en \"Herramientas para desarrolladores > Servicios\", busque \"notificación\" para encontrar el nombre de su dispositivo/teléfono.", + "Automations can optionally be triggered in Home Assistant:": "Las automatizaciones se pueden activar opcionalmente en Asistente de Hogar:", + "Trigger type:": "Tipo de disparador:", + "Event type:": "Tipo de Evento:", + "Event data:": "Datos del Evento:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Luego elija una acción, por ejemplo, cambie la escena a donde una luz RGB es roja.", + "Frontend Version": "Versión de Interfaz", + "Frontend Version do not match backend version!": "La Versión de Interfaz no coincide con la versión backend!", + "backupRecommend": "Por favor, haz copia de seguridad del volumen o el archivo de datos (./data/) directamente en su lugar.", + "recurringInterval": "Intervalo", + "Recurring": "Periódico", + "strategyManual": "Activo/Inactivo Manualmente", + "lastDay2": "Penúltimo Día del Mes", + "lastDay3": "Antepenúltimo día del mes", + "lastDay4": "Trasantepenúltimo Día del Mes", + "IconUrl": "URL de Icono", + "dnsCacheDescription": "Es posible que no funcione en algunos entornos IPv6; desactívelo si encuentra algún problema.", + "Single Maintenance Window": "Ventana de Mantenimiento Único", + "secureOptionTLS": "TLS (465)", + "aboutMattermostChannelName": "Puedes sobreescribir el canal por defecto en el cual el Webhook publica introduciendo el nombre del canal en el campo \"Nombre del Canal\". Esto tiene que estar habilitado en la configuración de Mattermost Webhook. Ejemplo: #otro-canal", + "dataRetentionTimeError": "El periodo de retención debe ser 0 o mayor", + "Enable TLS": "Habilita TLS", + "Lowcost": "Bajo coste", + "You can divide numbers with": "Puedes dividir números con", + "Base URL": "URL Base", + "Proto Service Name": "Nombre de Servicio Proto", + "Proto Method": "Método Proto", + "Proto Content": "Contenido Proto", + "Economy": "Económico", + "Platform": "Plataforma", + "onebotPrivateMessage": "Privado", + "onebotMessageType": "Tipo de Mensaje OneBot", + "smseagleRecipientType": "Tipo de destinatario", + "smseagleRecipient": "Destinatario(s) (multiples deben separarse por comas)", + "smseagleEncoding": "Enviar como Unicode (por defecto=GSM-7)", + "smseaglePriority": "Prioridad del mensaje (0-9, mas alta prioridad = 9)", + "stackfield": "Stackfield", + "Leave blank to use a shared sender number.": "Dejar en blanco para usar un número de remitente compartido.", + "Octopush API Version": "Versión API Octopush", + "From Name/Number": "De Nombre/Número", + "Recipient Number": "Número de Destinatario", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "El token de acceso de larga duración puede crearse haciendo clic en el nombre de su perfil (abajo a la izquierda), desplazándose hasta la parte inferior y luego haciendo clic en Crear token.", + "backupOutdatedWarning": "Obsoleto: dado que se agregaron muchas funciones y esta función de copia de seguridad no se mantiene desde hace un tiempo, no puede generar ni restaurar una copia de seguridad completa.", + "Optional": "Opcional", + "loadingError": "No se pueden obtener los datos, inténtelo de nuevo más tarde.", + "pushoverDesc2": "Si quieres enviar notificaciones a diferentes dispositivos, rellena el campo Dispositivo.", + "octopushLegacyHint": "Utilizas la versión anterior de Octopush (2011-2020) o la nueva versión?", + "Sms template must contain parameters: ": "La plantilla SMS debería contener parámetros: ", + "For safety, must use secret key": "Por seguridad, deberías usar key secreta", + "signalImportant": "IMPORTANTE: No puedes mezclar grupos y números en destinatarios!", + "aboutWebhooks": "Más información sobre Webhooks en: {0}", + "smtpDkimHashAlgo": "Algoritmo Hash (Opcional)", + "promosmsPhoneNumber": "Número de teléfono (para destinatarios Polacos puedes omitir los códigos de área)", + "promosmsTypeFlash": "SMS FLASH - Mensaje se mostrará automáticamente en el dispositivo del destinatario. Limitado sólo a destinatarios Polacos.", + "promosmsSMSSender": "Nombre de Remitente SMS: Nombre pre-registrado o uno de los predeterminados: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "matrixDesc1": "Puedes encontrar la ID de sala interna mirando en la sección avanzado de los ajustes de sala en tu cliente Matrix. Debería ser algo como !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Es altamente recomendable crear un nuevo usuario y no usar el token de acceso propio de tu usuario porque otorgaría acceso completo a tu cuenta y todas las salas que hayas entrado. En su lugar, crea un usuario nuevo e invítalo a la sala donde quieres recibir las notificaciones. Puedes obtener el token de acceso ejecutando {0}", + "plugin": "Complemento | Complementos", + "From Email": "Desde el Email", + "emailCustomSubject": "Asunto Personalizado", + "To Email": "Al Email", + "smtpCC": "CC", + "smtpBCC": "CCO", + "Discord Webhook URL": "URL Webhook de Discord", + "wayToGetDiscordURL": "Puede obtener esto yendo a Configuración del servidor -> Integraciones -> Ver Webhooks -> Crear Webhook", + "Bot Display Name": "Nombre para mostrar del Bot", + "Hello @everyone is...": "Hola {'@'}todos están…", + "wayToGetTeamsURL": "Puedes aprender cómo crear una URL webhook {0}.", + "wayToGetZohoCliqURL": "Puedes aprender cómo crear una URL webhook {0}.", + "needSignalAPI": "Necesitas tener un cliente de señal con API REST.", + "wayToCheckSignalURL": "Puedes revisar esta URL para ver cómo configurar uno:", + "Number": "Número", + "Access Token": "Token de Acceso", + "Channel access token": "Token de acceso al canal", + "Line Developers Console": "Consola de Desarrolladores de Line", + "lineDevConsoleTo": "Consola de Desarrolladores de Line - {0}", + "Basic Settings": "Configuración Básica", + "Messaging API": "API de Mensajería", + "wayToGetLineChannelToken": "Primero accede al {0}, crea un proveedor y un canal (API de Mensajería), entonces puedes obtener el token de acceso al cana y el ID de usuario de los elementos de menú anteriormente mencionados.", + "Icon URL": "URL de Icono", + "aboutIconURL": "Puede proporcionar un enlace a una imagen en \"URL de icono\" para anular la imagen de perfil predeterminada. No se utilizará si se establece Icono Emoji.", + "enableGRPCTls": "Permite enviar solicitudes gRPC con conexión TLS", + "grpcMethodDescription": "El nombre del método es convertido al formato camelCase tal como sayHello, check, etc.", + "dnsPortDescription": "Puerto servidor DNS. Por defecto al 53. Puedes cambiar el puerto en cualquier momento.", + "recurringIntervalMessage": "Ejecutar una vez al día | Ejecutar una vez cada {0} días", + "affectedMonitorsDescription": "Selecciona los monitores que se ven afectados por el mantenimiento actual", + "affectedStatusPages": "Muestra este mensaje de mantenimiento en las páginas de estado seleccionadas", + "atLeastOneMonitor": "Selecciona al menos un monitor afectado", + "endpoint": "punto final", + "promosmsPassword": "Contraseña API", + "pushoversounds pushover": "Pushover (predeterminado)", + "pushoversounds bike": "Bicicleta", + "pushoversounds bugle": "Trompeta", + "pushoversounds cashregister": "Caja Registradora", + "pushoversounds classical": "Clásica", + "pushoversounds cosmic": "Cósmico", + "pushoversounds falling": "Descendente", + "pushoversounds gamelan": "Gamelán", + "pushoversounds incoming": "Entrante", + "pushoversounds intermission": "Intermedio", + "pushoversounds magic": "Mágico", + "pushoversounds mechanical": "Mecánica", + "pushoversounds pianobar": "Bar Piano", + "pushoversounds siren": "Sirena", + "pushoversounds spacealarm": "Alarma Espacial", + "pushoversounds tugboat": "Remolcador", + "pushoversounds alien": "Alarma Alienígena (largo)", + "pushoversounds climb": "Escalada (largo)", + "pushoversounds persistent": "Persistente (largo)", + "pushoversounds echo": "Pushover Eco (largo)", + "pushoversounds updown": "Arriba Abajo (largo)", + "pushoversounds vibrate": "Sólo Vibración", + "pushoversounds none": "Ninguno (silencio)", + "pushyAPIKey": "Key de Api Secreta", + "pushyToken": "Token de Dispositivo", + "PushByTechulus": "Push con Techulus", + "clicksendsms": "SMS con ClickSend", + "GoogleChat": "Chat de Google (sólo Google Workspace)", + "Kook": "Kook", + "wayToGetKookBotToken": "Crea aplicación y obtén tu token de bot en {0}", + "wayToGetKookGuildID": "Activa 'Modo Desarrollador' en los ajustes de Kook, y haz click derecho en el grupo para obtener su ID", + "Guild ID": "ID de grupo", + "User Key": "Key de Usuario", + "octopushTypePremium": "Premium (Rápido - recomendado para alertas)", + "octopushTypeLowCost": "Bajo Coste (Lento - algunas veces bloqueado por operador)", + "checkPrice": "Consultar {0} precios:", + "apiCredentials": "Credenciales de API", + "Check octopush prices": "Consulta los precios de octopush {0}.", + "octopushPhoneNumber": "Número de teléfono (en formato internacional, ejemplo: +33612345678)", + "octopushSMSSender": "Nombre de Remitente del SMS: 3-11 caracteres alfanuméricos y espacio (a-zA-Z0-9)", + "LunaSea Device ID": "ID Dispositivo LunaSea", + "goAlert": "GoAlert", + "pushoverDesc1": "La prioridad Emergencia (2) tiene predeterminado un tiempo muerto entre reintentos de 30 segundos y expirará después de 1 hora.", + "AccessKeyId": "ID de Key de Acceso", + "SecretAccessKey": "Secrreto de Key de Acceso", + "TemplateCode": "Código de Plantilla", + "Bark Group": "Grupo de Bark", + "Bark Sound": "Sonido de Bark", + "SecretKey": "Clave Secreta", + "Huawei": "Huawei", + "Retry": "Reintentar", + "Proxy Server": "Servidor Proxy", + "Proxy Protocol": "Protocolo Proxy", + "Setup Proxy": "Configurar Proxy", + "Proxy server has authentication": "El servidor Proxy tiene autenticación", + "promosmsAllowLongSMS": "Permitir SMS largo", + "Uptime Kuma URL": "URL de Uptime Kuma", + "Icon Emoji": "Icono Emoji", + "aboutKumaURL": "Si dejas vacío el campo URL Uptime Kuma, predeterminará la página GitHub del Proyecto.", + "smtpDkimSettings": "Ajustes DKIM", + "smtpDkimDomain": "Nombre de Dominio", + "smtpDkimKeySelector": "Selector de Clave", + "smtpDkimPrivateKey": "Clave Privada", + "Integration Key": "Key de Integración", + "Integration URL": "URL de Integración", + "Device Token": "Token de Dispositivo", + "WeCom Bot Key": "Clave de Bot WeCom", + "promosmsTypeEco": "SMS ECO - barato pero lento y a veces sobrecargado. Limitado sólo a destinatarios Polacos.", + "promosmsTypeSpeed": "SMS SPEED - La mayor prioridad en el sistema. Muy rápido y confiable pero costoso (alrededor del doble del precio de SMS FULL).", + "matrixHomeserverURL": "URL Servidor Casero (con http(s):// y opcionalmente el puerto)", + "Internal Room Id": "ID de Sala Interna", + "Channel Name": "Nombre del canal", + "aboutChannelName": "Introduce el nombre del canal en {0} campo Nombre del Canal si quieres evitar el canal Webhook. Ejemplo: #otro-canal", + "smtpDkimDesc": "Por favor, remitir a DKIM Nodemailer {0} para descubrir como se usa.", + "smtpDkimheaderFieldNames": "Keys de encabezado para firmar (Opcional)", + "smtpDkimskipFields": "Keys de encabezado para no firmar (Opcional)", + "Auto resolve or acknowledged": "Resolución automática o reconocida", + "promosmsTypeFull": "SMS FULL - Nivel Premium de SMS, puedes usar tu Nombre de Remitente (Tienes que registrarlo primero). Confiable para alertas.", + "do nothing": "no hacer nada", + "alerta": "Alerta", + "serwersmsAPIPassword": "Contraseña de API", + "serwersmsPhoneNumber": "Número de teléfono", + "smseagle": "SMSEagle", + "smseagleTo": "Número(s) de teléfono", + "serwersmsSenderName": "Nombre de remitente de SMS (registrado a través del portal de cliente)", + "auto resolve": "resolución automática", + "auto acknowledged": "Auto reconocida", + "alertaEnvironment": "Entorno", + "PushDeer Key": "Key de PushDeer", + "onebotSafetyTips": "Por seguridad, deberías colocara el token de acceso", + "wayToGetClickSendSMSToken": "Puedes obtener Usuario de API y llave de API {aquí}.", + "Apprise URL": "URL Apprise", + "gorush": "Gorush", + "squadcast": "Squadcast", + "Maintenance Time Window of a Day": "Ventana de tiempo de mantenimiento de un día", + "Effective Date Range": "Rango de Fecha Efectivo(Opcional)", + "Free Mobile User Identifier": "Identificador de Usuario de Free Mobile", + "Gateway Type": "Tipo de puerta de enlace", + "SMSManager": "SMSManager", + "goAlertInfo": "GoAlert es una aplicación de código abierto para la programación de guardias, escaladas automatizadas y notificaciones (como SMS o llamadas de voz). ¡Involucre automáticamente a la persona adecuada, de la manera correcta y en el momento adecuado! {0}", + "Free Mobile API Key": "Clave API de Free Mobile", + "high": "alto", + "SMSManager API Docs": "Documentación API de SMSManager", + "smseagleContact": "Nombre(s) de contacto en la guía telefónica", + "smseagleToken": "Token de Acceso a la API", + "smseagleUrl": "URL del dispositivo SMSEagle", + "Legacy Octopush-DM": "Octopush-DM (legacy)", + "HomeAssistant": "Home Assistant", + "goAlertIntegrationKeyInfo": "Obtenga la clave de integración API genérica para el servicio en este formato \"aaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\", generalmente el valor del parámetro token de la URL copiada.", + "Topic": "Tema", + "matrix": "Matrix", + "Feishu WebHookUrl": "WebHookURL de Feishu", + "wayToGetPagerDutyKey": "Puede obtener esto yendo a Servicio -> Directorio de servicios -> (Seleccione un servicio) -> Integraciones -> Agregar integración. Aquí puede buscar \"API de eventos V2\". Más información {0}", + "alertaApiKey": "Clave API", + "alertaAlertState": "Estado de Alerta", + "alertaRecoverState": "Estado de Recuperación", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "Nombre de usuario de API (inc. webapi_ prefix)", + "smseagleGroup": "Nombre(s) de grupo(s) de Guía Telefónica", + "Unpin": "Dejar de Fijar", + "Prefix Custom Message": "Prefijo personalizado", + "markdownSupported": "Sintaxis de Markdown soportada. Si estas usando HTML, evita espacios al principio para prevenir problemas de formato.", + "Server Address": "Dirección del Servidor", + "Learn More": "Aprende Más", + "Pick a RR-Type...": "Seleccione un Tipo RR…", + "onebotHttpAddress": "Dirección HTTP OneBot", + "SendKey": "Clave de Envío", + "octopushAPIKey": "\"Clave API\" desde credenciales API HTTP en panel de control", + "octopushLogin": "\"Inicio de sesión\" desde credenciales API HTTP en panel de control", + "ntfy Topic": "Tema ntfy", + "Google Analytics ID": "ID Analíticas de Google", + "Edit Tag": "Editar Etiqueta", + "SignName": "Firma", + "Bark Endpoint": "Endpoint Bark", + "WebHookUrl": "URL del WebHook", + "High": "Alto", + "alertaApiEndpoint": "Endpoint API", + "Body Encoding": "Codificación del cuerpo", + "Expiry date": "Fecha de vencimiento", + "Expiry": "Expiración", + "API Keys": "Claves API", + "Key Added": "Clave añadida", + "Add Another": "Agregar otro/a", + "Continue": "Continuar", + "Don't expire": "No caduca", + "apiKey-inactive": "Inactivo", + "apiKey-expired": "Expirado", + "apiKey-active": "Activo", + "No API Keys": "No hay claves API", + "Add API Key": "Añadir clave API", + "apiKeyAddedMsg": "Su clave API ha sido añadida. Anótala, ya que no se volverá a mostrar.", + "Clone": "Clonar", + "cloneOf": "Clon de {0}", + "pagertreeDoNothing": "No hacer nada", + "pagertreeResolve": "Resolución automática", + "pagertreeCritical": "Crítico/a", + "pagertreeHigh": "Alto", + "pagertreeMedium": "Medio", + "pagertreeLow": "Bajo", + "pagertreeSilent": "Silencio", + "pagertreeUrgency": "Urgencia", + "pagertreeIntegrationUrl": "URL de integración", + "lunaseaTarget": "Objetivo", + "wayToGetPagerTreeIntegrationURL": "Después de crear la integración Uptime Kuma en PagerTree, copie el Endpoint. Ver todos los detalles {0}", + "Generate": "Generar", + "deleteAPIKeyMsg": "¿Está seguro de que desea eliminar esta clave API?", + "telegramMessageThreadID": "(Opcional) ID del hilo de mensajes", + "telegramMessageThreadIDDescription": "Opcional Identificador único para el hilo de mensajes de destino (asunto) del foro; solo para supergrupos de foros", + "telegramProtectContent": "Proteger Forwarding/Saving", + "telegramProtectContentDescription": "Si se activa, los mensajes del bot en Telegram estarán protegidos contra el reenvío y el guardado.", + "notificationRegional": "Regionales", + "Clone Monitor": "Clonar Monitor", + "telegramSendSilently": "Enviar en silencio", + "telegramSendSilentlyDescription": "Envía el mensaje en silencio. Los usuarios recibirán una notificación sin sonido.", + "Add New Tag": "Añadir nueva etiqueta", + "lunaseaUserID": "ID Usuario", + "lunaseaDeviceID": "ID Dispositivo", + "disableAPIKeyMsg": "¿Está seguro de que desea desactivar esta clave API?", + "Expires": "Expira", + "twilioAccountSID": "SID de Cuenta", + "twilioFromNumber": "Desde el numero", + "twilioToNumber": "Hasta el numero", + "startDateTime": "Fecha/Hora Inicio", + "sameAsServerTimezone": "Igual a Zona horaria del Servidor", + "endDateTime": "Fecha/Hora Fin", + "cronExpression": "Expresión Cron", + "cronSchedule": "Cronograma: ", + "invalidCronExpression": "Expresión Cron invalida:{0}", + "statusPageRefreshIn": "Reinicio en: {0}", + "twilioAuthToken": "Token de autenticación / Secreto de clave API", + "ntfyUsernameAndPassword": "Nombre de Usuario y Contraseña", + "ntfyAuthenticationMethod": "Método de Autenticación", + "Cannot connect to the socket server": "No se puede conectar al servidor socket", + "Reconnecting...": "Reconectando...", + "Select": "Seleccionar", + "chromeExecutableAutoDetect": "Auto Detectar", + "Edit Maintenance": "Editar mantenimiento", + "pushoverMessageTtl": "Mensaje TTL (segundos)", + "Notify Channel": "Canal de notificación", + "Show Clickable Link Description": "Si está marcado, todos los que tienen acceso a esta página de estado pueden tener acceso a la URL del monitor.", + "webhookBodyCustomOption": "Cuerpo Personalizado", + "selectedMonitorCount": "Seleccionado: {0}", + "Check/Uncheck": "Marcar/Desmarcar", + "Invert Keyword": "Invertir palabra clave", + "filterActive": "Activo", + "filterActivePaused": "Pausado", + "Home": "Inicio", + "Expected Value": "Valor esperado", + "Json Query": "Consulta Json", + "invertKeywordDescription": "Comprobar si la palabra clave está ausente en vez de presente.", + "enableNSCD": "Habilitar NSCD (Demonio de Caché de Servicio de Nombres) para almacenar en caché todas las solicitudes DNS", + "Request Timeout": "Tiempo de espera máximo de petición", + "timeoutAfter": "Expirar después de {0} segundos", + "chromeExecutableDescription": "Para usuarios de Docker, si Chromium no está instalado, puede que tarde unos minutos en ser instalado y mostrar el resultado de la prueba. Usa 1GB de espacio.", + "chromeExecutable": "Ejecutable de Chrome/Chromium", + "Monitor Setting": "Ajustes del monitor de {0}", + "Show Clickable Link": "Mostrar enlace clickeable", + "Open Badge Generator": "Abrir generador de insignias", + "Badge Generator": "Generador de insignias de {0}", + "Badge Type": "Tipo de insignia", + "Badge Label": "Etiqueta de la insignia", + "Badge Label Color": "Color de la etiqueta de la insignia", + "Badge Color": "Color de la insignia", + "Badge Label Prefix": "Prefijo de la etiqueta de insignia", + "Badge Preview": "Vista previa de la insignia", + "Badge Up Color": "Color de la insignia superior", + "Badge Down Color": "Color de la insignia inferior", + "Badge Pending Color": "Color de la insignia pendiente", + "Badge Maintenance Color": "Color de mantenimiento de la insignia", + "Badge Warn Days": "Días de advertencia de insignia", + "Badge Down Days": "Días sin insignia", + "Badge Style": "Estilo de insignia", + "Badge URL": "URL de la insignia", + "Group": "Grupo", + "Monitor Group": "Grupo de Monitoreo", + "Kafka Brokers": "Brokers de Kafka", + "Enter the list of brokers": "Ingrese a la lista de brokers", + "Press Enter to add broker": "Presione Enter para agregar un broker", + "Kafka Topic Name": "Nombre del tema Kafka", + "Kafka Producer Message": "Mensaje del Productor de Kafka", + "Enable Kafka SSL": "Habilitar Kafka SSL", + "Kafka SASL Options": "Opciones de Kafka SASL", + "Mechanism": "Mecanismo", + "Pick a SASL Mechanism...": "Elija un mecanismo SASL…", + "Authorization Identity": "Identidad de autorización", + "AccessKey Id": "ID de clave de acceso", + "Secret AccessKey": "Secreto de la clave de acceso", + "Session Token": "Token de sesión", + "Close": "Cerrar", + "Request Body": "Cuerpo de solicitud", + "FlashDuty Severity": "Gravedad", + "nostrRelays": "Relays de Nostr", + "nostrRelaysHelp": "Una URL de retransmisión por línea", + "nostrSender": "Clave Privada del remitente (nsec)", + "nostrRecipients": "Claves públicas de destinatarios (npub)", + "nostrRecipientsHelp": "formato npub, uno por línea", + "showCertificateExpiry": "Mostrar caducidad del certificado", + "noOrBadCertificate": "Certificado Nulo/Incorrecto", + "aboutNotifyChannel": "Notificar canal activará una notificación de escritorio o móvil para todos los miembros del canal, ya sea que su disponibilidad esté activa o ausente.", + "Server URL should not contain the nfty topic": "La URL del servidor no puede incluír el tópico de ntfy", + "PushDeer Server": "Servidor PushDeer", + "pushDeerServerDescription": "Dejar en blanco para usar el servidor oficial", + "Badge Duration (in hours)": "Duración de la insignia (en horas)", + "Badge Prefix": "Prefijo del valor de la insignia", + "Badge Suffix": "Sufijo del valor de la insignia", + "Badge Label Suffix": "Sufijo de la etiqueta de insignia", + "Badge Warn Color": "Color de advertencia de insignia", + "Badge value (For Testing only.)": "Valor de la insignia (Solo para pruebas.)", + "Enable Kafka Producer Auto Topic Creation": "Habilitar la Creación Automática de Temas del Productor de Kafka", + "noGroupMonitorMsg": "No disponible. Cree primero un monitor de grupo.", + "wayToGetFlashDutyKey": "Para integrar Uptime Kuma con Flashduty: Ir a Canal -> (Seleccionar un Canal) -> Integraciones -> Agregar una nueva integración, elegir 'Uptime Kuma' y copia la URL Push.", + "gamedigGuessPort": "Gamedig: Adivinar el puerto", + "gamedigGuessPortDescription": "El puerto utilizado por Valve Server Query Protocol puede ser diferente del puerto del cliente. Pruebe esto si el monitor no puede conectarse a su servidor.", + "twilioApiKey": "Clave de la API (opcional)", + "styleElapsedTime": "Tiempo transcurrido en la barra de latidos", + "styleElapsedTimeShowNoLine": "Mostrar (sin línea)", + "styleElapsedTimeShowWithLine": "Mostrar (Con línea)", + "webhookCustomBodyDesc": "Define un cuerpo HTTP personalizado para la petición. Las variables que puedes usar como plantillas son {msg}, {heartbeat}, y {monitor}.", + "webhookBodyPresetOption": "Preajuste - {0}", + "tailscalePingWarning": "Para utilizar el monitor Tailscale Ping, debe instalar Uptime Kuma sin Docker y también instalar el cliente Tailscale en su servidor.", + "Bark API Version": "Versión de la API Bark", + "monitorToastMessagesDescription": "Las notificaciones Toast para monitores desaparecen después de un tiempo dado en segundos. Establecer a -1 desactiva el tiempo de espera. Si se establece en 0, se desactivan las notificaciones.", + "Saved.": "Guardado.", + "monitorToastMessagesLabel": "Monitorizar las notificaciones Toast", + "toastSuccessTimeout": "Tiempo de espera para notificaciones de éxito", + "toastErrorTimeout": "Tiempo de espera para notificaciones de error", + "setupDatabaseChooseDatabase": "¿Qué base de datos te gustaría usar?", + "setupDatabaseEmbeddedMariaDB": "No necesitas configurar nada. Esta imagen de Docker tiene incorporado y configurado MariaDB para ti automáticamente. Uptime Kuma se conectará a esta base de datos a través de un socket Unix.", + "setupDatabaseMariaDB": "Conectarse a una base de datos MariaDB externa. Debe configurar la información de conexión a la base de datos.", + "setupDatabaseSQLite": "Un archivo de base de datos simple, recomendado para despliegues a pequeña escala. Antes de la versión 2.0.0, Uptime Kuma utilizaba SQLite como base de datos predeterminada.", + "dbName": "Nombre de la Base de Datos", + "authInvalidToken": "Token inválido.", + "authIncorrectCreds": "Nombre de usuario o contraseña incorrectos.", + "2faEnabled": "2FA habilitado.", + "2faDisabled": "2FA deshabilitado.", + "liquidIntroduction": "El plantillaje se logra a través del lenguaje de plantillas Liquid. Consulte {0} para obtener instrucciones de uso.", + "templateLimitedToUpDownCertNotifications": "solo disponible para notificaciones FUNCIONAL/CAÍDO/Caducidad de certificado", + "emailTemplateMsg": "mensaje de la notificación", + "emailTemplateLimitedToUpDownNotification": "sólo disponible para latidos FUNCIONAL/CAÍDO, de lo contrario nulo", + "setup a new monitor group": "configurar un nuevo grupo de monitores", + "authUserInactiveOrDeleted": "El usuario está inactivo o eliminado.", + "2faAlreadyEnabled": "2FA ya está habilitado.", + "remoteBrowsersDescription": "Los navegadores remotos son una alternativa a ejecutar Chromium localmente. Configúralos con un servicio como browserless.io o conéctalos a tu propio servidor", + "successKeyword": "Palabra clave de éxito", + "successKeywordExplanation": "MQTT Palabra clave que se considerará como éxito", + "Remove the expiry notification": "Eliminar la notificación de vencimiento", + "Browser Screenshot": "Captura de pantalla del navegador", + "emailCustomisableContent": "Contenido personalizable", + "smtpLiquidIntroduction": "Los dos campos siguientes pueden crear plantillas mediante el lenguaje de plantillas Liquid. Consulte {0} para obtener instrucciones de uso. Estas son las variables disponibles:", + "leave blank for default subject": "dejar en blanco para el asunto predeterminado", + "emailCustomBody": "Cuerpo personalizado", + "successAuthChangePassword": "La contraseña se ha actualizado correctamente.", + "successDeleted": "Borrado exitosamente.", + "successEdited": "Editado con éxito.", + "successDisabled": "Deshabilitado con éxito.", + "GrafanaOncallUrl": "URL de llamada de Grafana", + "Reset Token": "Restablecer Token", + "Remote Browsers": "Navegadores remotos", + "Remote Browser": "Navegador remoto", + "Add a Remote Browser": "Añadir un navegador remoto", + "noDockerHostMsg": "No disponible. Configure primero un host Docker.", + "DockerHostRequired": "Establezca el host Docker para este monitor.", + "successAdded": "Agregado exitosamente.", + "successResumed": "Reanudado con éxito.", + "successPaused": "Pausado con éxito.", + "successBackupRestored": "Copia de seguridad restaurada correctamente.", + "successEnabled": "Habilitado exitosamente.", + "tagNotFound": "Etiqueta no encontrada.", + "foundChromiumVersion": "Se encontró Chromium/Chrome. Versión: {0}", + "pushViewCode": "¿Cómo utilizar el monitor Push? (Ver código)", + "pushOthers": "Otros", + "programmingLanguages": "Lenguajes de Programación", + "templateMsg": "mensaje de la notificación", + "templateMonitorJSON": "objeto que describe el monitor", + "templateLimitedToUpDownNotifications": "sólo disponible para notificaciones FUNCIONAL/CAIDO", + "Add a new expiry notification day": "Añadir una nueva notificación de vencimiento", + "leave blank for default body": "dejar en blanco para el cuerpo predeterminado", + "emailTemplateServiceName": "Nombre del Servicio", + "emailTemplateHostnameOrURL": "Nombre del Host o URL", + "emailTemplateStatus": "Estado", + "emailTemplateMonitorJSON": "objeto que describe el monitor", + "openModalTo": "abrir modal a {0}", + "Add a domain": "Añadir un dominio", + "Remove domain": "Eliminar dominio '{0}'", + "Remote Browser not found!": "¡Navegador remoto no encontrado!", + "self-hosted container": "contenedor autohospedado", + "remoteBrowserToggle": "Por defecto Chromium se ejecuta dentro del contenedor Uptime Kuma. Puedes usar un navegador remoto activando este interruptor.", + "useRemoteBrowser": "Utilice un navegador remoto", + "deleteRemoteBrowserMessage": "¿Está seguro de que desea eliminar este Navegador Remoto para todos los monitores?", + "settingUpDatabaseMSG": "Configurando la base de datos. Puede tomar un tiempo, sea paciente, por favor.", + "Search monitored sites": "Buscar sitios monitoreados", + "statusPageSpecialSlugDesc": "Identificador único especial {0}: esta página será mostrada cuando no se proporcione ningún identificador único", + "emailTemplateHeartbeatJSON": "objeto que describe el latido", + "ntfyPriorityHelptextAllEvents": "Todos los eventos se envían con la máxima prioridad", + "ntfyPriorityHelptextAllExceptDown": "Todos los eventos son enviados con esta prioridad, excepto los eventos {0}, que tienen una prioridad de {1}", + "templateHeartbeatJSON": "objeto que describe el latido", + "What is a Remote Browser?": "¿Qué es un Navegador Remoto?", + "Your User ID": "Su ID de usuario", + "Alphanumeric (recommended)": "Alfanumérico (recomendado)", + "Destination": "Destino", + "wayToGetWhapiUrlAndToken": "Puedes obtener la URL de la API y el token accediendo al canal que desee desde {0}", + "API URL": "API URL", + "Allow Long SMS": "Permitir SMS largo", + "cellsyntDestination": "Número de teléfono del destinatario en formato internacional precedido de 00, seguido del código de país, ej. 00447920110000 para el número del Reino Unido 07920 110 000 (máximo 17 dígitos en total). Máximo 25.000 destinatarios, separados por comas, por solicitud HTTP.", + "Refresh Interval Description": "La página de estado se refrescará cada {0} segundos", + "Refresh Interval": "Intervalo de refresco", + "ignoreTLSErrorGeneral": "Ignorar errores SSL/TLS durante la conexión", + "documentationOf": "Documentación de {0}", + "wayToGetHeiiOnCallDetails": "Cómo obtener el Trigger ID y las API Keys se explica en la {documentación}", + "Command": "Comando", + "wayToGetThreemaGateway": "Puedes registrarte para Threema Gateway {0}.", + "threemaRecipient": "Destinatario", + "threemaRecipientType": "Tipo de Destinatario", + "threemaRecipientTypeIdentity": "ID de Threema", + "threemaRecipientTypeIdentityFormat": "8 caracteres", + "threemaRecipientTypePhone": "Número de Teléfono", + "threemaRecipientTypeEmail": "Dirección de Correo Electrónico", + "threemaSenderIdentity": "ID de Gateway", + "threemaSenderIdentityFormat": "8 caracteres, generalmente comienza con *", + "Host URL": "URL del anfitrión", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Ingresa el nombre del host del servidor al que deseas conectarte o {localhost} si deseas usar un {local_mta}", + "smspartnerPhoneNumberHelptext": "El número debe estar en el formato internacional {0}, {1}. Múltiples números deben estar separados por {2}", + "smspartnerSenderName": "Nombre del emisor del SMS", + "smspartnerApiurl": "Puedes encontrar tu clave API en tu panel de control en {0}", + "smspartnerPhoneNumber": "Número(s) de teléfono", + "max 11 alphanumeric characters": "máximo 11 caracteres alfanuméricos", + "gtxMessagingFromHint": "En teléfonos móviles, tus destinatarios ven el TPOA mostrado como el remitente del mensaje. Se permiten hasta 11 caracteres alfanuméricos, un código corto, el código largo local o números internacionales ({e164}, {e212} o {e214})", + "cellsyntOriginatortypeAlphanumeric": "Cadena alfanumérica (máximo 11 caracteres alfanuméricos). Los destinatarios no pueden responder al mensaje.", + "cellsyntOriginatortypeNumeric": "Valor numérico (máximo 15 dígitos) con el número de teléfono en formato internacional sin el prefijo 00 (por ejemplo, el número del Reino Unido 07920 110 000 debe establecerse como 447920110000). Los destinatarios pueden responder al mensaje.", + "Originator type": "Tipo de originador", + "Telephone number": "Número de teléfono", + "Mentioning": "Mencionando", + "Don't mention people": "No mencionar personas", + "Mention group": "Mencionar a {group}", + "Bitrix24 Webhook URL": "URL del Webhook de Bitrix24", + "wayToGetBitrix24Webhook": "Puedes crear un webhook siguiendo los pasos en {0}", + "gtxMessagingApiKeyHint": "Puedes encontrar tu clave API en: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "Originator": "Originador", + "bitrix24SupportUserID": "Ingresa tu ID de usuario en Bitrix24. Puedes encontrar el ID en el enlace al ir al perfil del usuario.", + "wayToWriteWhapiRecipient": "El número de teléfono con el prefijo internacional, pero sin el signo más al inicio ({0}), el ID de Contacto ({1}) o el ID de Grupo ({2}).", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Número de Teléfono del Remitente / Dirección de Origen de la Ruta de Transmisión (TPOA)", + "To Phone Number": "Al Número de Teléfono", + "Select message type": "Seleccionar el tipo de mensaje", + "Send to channel": "Enviar al canal", + "max 15 digits": "máximo 15 dígitos", + "mongodbCommandDescription": "Ejecuta un comando de MongoDB contra la base de datos. Para obtener información sobre los comandos disponibles, consulta la {documentación}", + "whapiRecipient": "Número de teléfono / ID del contacto / ID del grupo", + "cellsyntSplitLongMessages": "Divide mensajes largos en hasta 6 partes. 153 x 6 = 918 caracteres.", + "receiverSevenIO": "Número receptor", + "apiKeySevenIO": "Clave API de SevenIO", + "wayToGetSevenIOApiKey": "Visita el panel de control en app.seven.io > developer > api key > el botón verde de agregar", + "senderSevenIO": "Número o nombre del remitente", + "gtxMessagingToHint": "Formato internacional, con el signo \"+\" al inicio ({e164}, {e212} o {e214})", + "locally configured mail transfer agent": "agente de transferencia de correo configurado localmente", + "wayToGetDiscordThreadId": "Obtener un ID de hilo / publicación en el foro es similar a obtener un ID de canal. Lee más sobre cómo obtener IDs {0}", + "smspartnerSenderNameInfo": "Debe ser entre 3..=11 carácteres regulares", + "receiverInfoSevenIO": "Si el número receptor no está ubicado en Alemania, debes agregar el código de país delante del número (por ejemplo, para el código de país 1 de EE. UU. usa 117612121212 en lugar de 017612121212)", + "callMeBotGet": "Aquí puedes generar un endpoint para {0}, {1} y {2}. Ten en cuenta que podrías recibir limitaciones de tasa. Las limitaciones de tasa parecen ser: {3}", + "cellsyntOriginator": "Visible en el teléfono móvil del destinatario como originador del mensaje. Los valores permitidos y la función dependen del parámetro originatortype.", + "threemaRecipientTypePhoneFormat": "E.164, sin el signo + al inicio", + "threemaApiAuthenticationSecret": "Clave Secreta del Gateway-ID", + "threemaBasicModeInfo": "Nota: Esta integración utiliza Threema Gateway en modo básico (encriptación basada en servidor). Puedes encontrar más detalles en {0}.", + "apiKeysDisabledMsg": "Las claves API están desactivadas porque la autenticación está desactivada.", + "Channel access token (Long-lived)": "Token de acceso al canal (Larga duración)", + "Create new forum post": "Crear un nuevo post en el foro", + "postToExistingThread": "Publicar en hilo / publicación existente", + "forumPostName": "Nombre de la publicación en el foro", + "threadForumPostID": "ID del hilo / publicación en el foro", + "e.g. {discordThreadID}": "por ejemplo, {discordThreadID}", + "whatHappensAtForumPost": "Crear una nueva publicación en el foro. Esto NO publica mensajes en una publicación existente. Para publicar en una publicación existente usa \"{option}\"", + "jsonQueryDescription": "Analice y extraiga datos específicos de la respuesta JSON del servidor mediante una consulta JSON o utilice \"$\" para la respuesta sin formato, si no espera JSON. Luego, el resultado se compara con el valor esperado, como cadenas. Consulte {0} para obtener documentación y use {1} para experimentar con consultas.", + "aboutSlackUsername": "Cambia el nombre que se muestra del remitente del mensaje. Si quieres mencionar a alguien, inclúyelo en el nombre descriptivo.", + "cacheBusterParam": "Añade el parámetro {0}", + "cacheBusterParamDescription": "Parámetro generado aleatoriamente para omitir cachés.", + "Community String": "Cadena comunitaria", + "snmpCommunityStringHelptext": "Esta cadena funciona como contraseña para autenticar y controlar el acceso a dispositivos habilitados para SNMP. Compárela con la configuración de su dispositivo SNMP.", + "privateOnesenderDesc": "Asegúrese de que el número de teléfono sea válido. Para enviar un mensaje a un número de teléfono privado, por ejemplo: 628123456789", + "wayToGetOnesenderUrlandToken": "Puedes obtener la URL y el token en el sitio web de Onesender. Más información {0}", + "Optional: Space separated list of scopes": "Opcional: Lista de ámbitos separados por espacios", + "No tags found.": "No se han encontrado etiquetas.", + "signl4Docs": "Puede encontrar más información sobre cómo configurar SIGNL4 y cómo obtener la URL del webhook de SIGNL4 en {0}.", + "shrinkDatabaseDescriptionSqlite": "Desencadena la base de datos {vacuum} para SQLite. {auto_vacuum} ya está habilitado, pero esto no desfragmenta la base de datos ni reempaqueta páginas de base de datos individuales como lo hace el comando {vacuum}.", + "and": "y", + "Message format": "Formato del mensaje", + "Send rich messages": "Enviar mensajes enriquecidos", + "OID (Object Identifier)": "(Identificador de Objeto) OID", + "snmpOIDHelptext": "Ingrese el OID del sensor o el estado que desea monitorear. Use herramientas de administración de red como navegadores MIB o software SNMP si no está seguro acerca del OID.", + "Condition": "Condición", + "SNMP Version": "Versión SNMP", + "Please enter a valid OID.": "Por favor escribe un OID válido.", + "Host Onesender": "Host de Onesender", + "Token Onesender": "Token de Onesender", + "Recipient Type": "Tipo de Receptor", + "Private Number": "Número Privado", + "groupOnesenderDesc": "Asegúrese de que el ID del grupo sea válido. Para enviar un mensaje al grupo, por ejemplo: 628123456789-342345", + "Group ID": "ID del grupo", + "Add Remote Browser": "Agregar navegador remoto", + "New Group": "Nuevo grupo", + "Group Name": "Nombre del grupo", + "OAuth2: Client Credentials": "OAuth2: Credenciales del Cliente", + "Authentication Method": "Método de autentificación", + "Authorization Header": "Cabecera de Autorización", + "Form Data Body": "Cuerpo de datos del formulario", + "OAuth Token URL": "OAuth Token URL", + "Client ID": "ID del Cliente", + "Client Secret": "Secreto de Cliente", + "OAuth Scope": "Alcance OAuth", + "Go back to home page.": "Volver a la página de inicio.", + "Lost connection to the socket server.": "Se perdió la conexión con el servidor de socket.", + "Cannot connect to the socket server.": "No se puede conectar al servidor de socket.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "URL de SIGNL4 WebhooK", + "Conditions": "Condiciones", + "conditionAdd": "Añadir condición", + "now": "ahora", + "time ago": "hace {0}", + "-year": "-año", + "Json Query Expression": "Expresión de consulta Json", + "ignoredTLSError": "Se han ignorado errores TLS/SSL", + "conditionDelete": "Borrar condición", + "conditionAddGroup": "Añadir grupo", + "conditionDeleteGroup": "Borrar grupo", + "conditionValuePlaceholder": "Valor", + "equals": "igual", + "not equals": "no es igual", + "contains": "contiene", + "not contains": "no contiene", + "starts with": "empieza por", + "not starts with": "no empieza por", + "ends with": "termina con", + "not ends with": "No termina en", + "less than": "menor que", + "greater than": "mayor que", + "less than or equal to": "Menor o igual a", + "greater than or equal to": "Mayor o igual a", + "Notification Channel": "Canal de notificación", + "Sound": "Sonido", + "record": "Registro", + "RabbitMQ Username": "Usuario RabbitMQ", + "RabbitMQ Password": "Contraseña RabbitMQ", + "RabbitMQ Nodes": "Nodos de Gestión RabbitMQ", + "rabbitmqNodesRequired": "Por favor, configure los nodos para este monitor.", + "rabbitmqNodesInvalid": "Por favor, utilice una URL totalmente cualificada (comenzando por 'http') para los nodos RabbitMQ.", + "Separate multiple email addresses with commas": "Separa las múltiples direcciones de correo con comas", + "rabbitmqHelpText": "Para utilizar el monitor, necesitará habilitar el Plugin de Gestión en su configuración RabbitMQ. Para más información, por favor consulte {rabitmq_documentation}.", + "SendGrid API Key": "Clave de la API de SendGrid", + "rabbitmqNodesDescription": "Introduzca la URL para los nodos de gestión de RabbitMQ incluyendo el protocolo y el puerto. Ejemplo: {0}", + "Money": "Dinero", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "O bien un texto de ID de origen o un número de teléfono en formato E.164 si quiere ser capaz de recibir respuestas.", + "Harp": "Arpa", + "Scifi": "Ciencia Ficción", + "Alphanumerical string and hyphens only": "Sólo cadenas alfanuméricas y guiones", + "Time Sensitive (iOS Only)": "Sensible a la Hora (Solo iOS)", + "Doorbell": "Timbre", + "Flute": "Flauta", + "Guitar": "Guitarra", + "Correct": "Correcto", + "Bubble": "Burbuja", + "Elevator": "Ascensor", + "Fail": "Fallo", + "Reveal": "Mostrar", + "Pop": "Pop", + "From": "De", + "Clear": "Limpiar", + "Can be found on:": "Se puede encontrar en: {0}", + "Custom sound to override default notification sound": "Sonidos personalizados prevalecen sobre los sonidos por defecto de las notificaciones", + "The phone number of the recipient in E.164 format.": "El número de teléfono del receptor en formato E.164.", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Las notificaciones sensibles en el tiempo se enviarán inmediatamente, incluso si el dispositivo está en modo no molestar.", + "Arcade": "Arcade", + "defaultFriendlyName": "Nuevo Monitor", + "telegramUseTemplate": "Usar una plantilla de mensaje personalizado", + "telegramUseTemplateDescription": "Si esta habilitado, el mensaje sera enviado usando una plantilla personalizada.", + "telegramServerUrlDescription": "Para aumentar las limitaciones de la API de bots de Telegram o ganar acceso en áreas bloqueadas (China, Irán, etc). Para mas información click {0}. Por defecto: {1}", + "smseagleApiType": "Versión de la API", + "telegramServerUrl": "(Opcional) URL del servidor", + "tagAlreadyOnMonitor": "Esta etiqueta (nombre y valor) ya están en el monitor o están pendientes de ser agregadas.", + "tagAlreadyStaged": "Esta etiqueta (nombre y valor) ya esta montada para este lote.", + "Add Tags": "Agregar etiquetas", + "tagNameExists": "Una etiqueta de sistema con este nombre ya existe. Seleccionala de la lista o usa un nombre diferente.", + "telegramTemplateFormatDescription": "Telegram permite usar diferentes lenguajes de marcado para los mensajes, ver telegram {0} para detalles específicos.", + "Use HTML for custom E-mail body": "Usar HTML para un cuerpo de E-mail personalizado", + "smseagleMsgType": "Tipo de mensaje", + "smseagleMsgSms": "Mensaje por SMS (por defecto)", + "smseagleMsgTts": "Llamada Voz-a-texto", + "smseagleDuration": "Duración (en segundos)", + "smseagleApiv1": "APIv1 (para proyectos existentes y retrocompatibilidad)", + "smseagleApiv2": "APIv2 (recomendada para nuevas integraciones)", + "templateServiceName": "nombre de servicio", + "templateHostnameOrURL": "Nombre de host o URL", + "templateStatus": "estado", + "mqttWebsocketPathInvalid": "Por favor, utiliza un formato válido de ruta WebSocket", + "smseagleDocs": "Consulte la documentación o la disponibilidad de APIv2: {0}", + "SpugPush Template Code": "Plantilla de código", + "FlashDuty Push URL Placeholder": "Copiar de la página de la página de integración de alerta", + "pingNumericLabel": "Salida numérica", + "pingGlobalTimeoutDescription": "Tiempo total en segundos antes de que termine el ping, independientemente de los paquetes enviados", + "customUrlDescription": "Se utilizará como URL en la que se puede hacer clic en lugar de la del monitor.", + "pingNumericDescription": "Si se marca, se mostrarán las direcciones IP en vez de los nombres de host simbólicos", + "pingGlobalTimeoutLabel": "Tiempo de espera global", + "pingPerRequestTimeoutLabel": "Tiempo de espera por ping", + "Custom URL": "URL personalizada", + "OneChatUserIdOrGroupId": "ID de usuario o ID de grupo de OneChat", + "OneChatAccessToken": "Token de acceso de OneChat", + "wayToGetWahaApiUrl": "La URL de tu instancia WAHA.", + "wahaSession": "Sesión", + "pingPerRequestTimeoutDescription": "Este es el tiempo de espera máximo (en segundos) antes de considerar que se ha perdido un paquete", + "OneChatBotId": "ID de bot de OneChat", + "mqttHostnameTip": "Por favor, utiliza este formato {hostnameFormat}", + "smseagleComma": "Multiple debe separarse con comas", + "pingCountLabel": "Paquetes máximos", + "pingCountDescription": "Número de paquetes a enviar antes de parar", + "ntfyPriorityDown": "Prioridad para eventos de caída", + "pause": "Pausar", + "wayToGetWahaSession": "Desde esta sesión, WAHA envía notificaciones a Chat ID. Puedes encontrarlo en el Panel de WAHA.", + "wayToWriteWahaChatId": "El número de teléfono con el prefijo internacional, pero sin el signo más al principio ({0}), el ID de contacto ({1}) o el ID de grupo ({2}). Las notificaciones se envían a este ID de chat desde la sesión de WAHA.", + "YZJ Webhook URL": "URL del webhook de YZJ", + "Add Another Tag": "Añadir otra etiqueta", + "Manual": "Manual", + "wahaChatId": "ID del chat (Número de teléfono / ID de contacto / ID de grupo)", + "smtpHelpText": "\"SMTPS\" comprueba el funcionamiento de SMTP/TLS; \"Ignorar TLS\" se conecta mediante texto plano; \"STARTTLS\" se conecta, emite un comando STARTTLS y verifica el certificado del servidor. Ninguno de estos envía un correo electrónico.", + "Plain Text": "Texto plano", + "Message Template": "Plantilla de mensaje", + "Template Format": "Formato de plantilla", + "wayToGetWahaApiKey": "La clave API es el valor de la variable de entorno WHATSAPP_API_KEY que utilizabas para ejecutar WAHA.", + "smsplanetApiDocs": "Puede encontrar información detallada sobre la obtención de tokens API en {the_smsplanet_documentation}.", + "the smsplanet documentation": "Documentación de smsplanet", + "Phone numbers": "Números de teléfono", + "Sender name": "Nombre del emisor", + "smsplanetNeedToApproveName": "Necesita ser aprobado en el panel de cliente", + "smsplanetApiToken": "Token para la API de SMSPlanet", + "Disable URL in Notification": "Deshabilitar la URL en la notificación", + "YZJ Robot Token": "Token del robot de YZJ", + "pingIntervalAdjustedInfo": "Intervalo ajustado en función del recuento de paquetes, el tiempo de espera global y el tiempo de espera por ping", + "Clear Form": "Limpiar formulario", + "Ip Family": "Familia de IP", + "ipFamilyDescriptionAutoSelect": "Utiliza {happyEyeballs} para determinar la familia de IP.", + "Happy Eyeballs algorithm": "Algoritmo Happy Eyeballs", + "Path": "Ruta", + "mqttWebSocketPath": "Ruta de MQTT WebSocket", + "smseagleMsgRing": "Llamada", + "Clear All Events": "Limpiar todos los eventos", + "clearAllEventsMsg": "¿Seguro deseas eliminar todos los eventos? para todos los monitores? Esto remueve permanentemente los datos de los mensajes asociados y no se puede deshacer.", + "Events cleared successfully": "Eventos limpiados exitosamente.", + "No monitors found": "No se encontraron monitores.", + "Could not clear events": "No se pudieron limpiar {failed}/{total} eventos", + "smseagleGroupV2": "ID(s) de grupo(s) de Guía Telefónica", + "mqttWebsocketPathExplanation": "Ruta del WebSocker para MQTT sobre conexión WebSocker (ejemplo, /mqtt)", + "OAuth Audience": "Audiencia OAuth", + "Template plain text instead of using cards": "Plantilla de texto plano en vez de utilizar tarjetas", + "issueWithGoogleChatOnAndroidHelptext": "Esto también permite evitar errores anteriores como {issuetackerURL}", + "wayToGetBaleChatID": "Puedes obtener tu ID de chat enviando un mensaje al bot y yendo a esta URL para ver el chat_id:", + "smseagleContactV2": "ID(s) de contacto(s) de Guía Telefónica", + "smseagleMsgTtsAdvanced": "Llamada avanzada de texto a voz", + "wayToWriteEvolutionRecipient": "El número de teléfono con el prefijo internacional, pero sin el signo más al principio ({0}), el ID de contacto ({1}) o el ID de grupo ({2}).", + "brevoApiHelp": "Crea una clave API aquí: {0}", + "brevoLeaveBlankForDefaultName": "deja en blanco para el nombre por defecto", + "brevoLeaveBlankForDefaultSubject": "Deja en blanco para sujeto por defecto", + "Send DOWN silently": "Envía DOWN silenciosamente", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "La instalación de un bot de Nextcloud Talk requiere acceso administrativo al servidor.", + "Font Twemoji by Twitter licensed under": "Fuente Twemoji de Twitter con licencia", + "Optional: The audience to request the JWT for": "Opcional: La audiencia para la que se solicitará el JWT", + "brevoApiKey": "Clave API de Brevo", + "supportBaleChatID": "Soporte de chat directo/grupo/ID de chat del canal", + "wayToGetBaleToken": "Puedes obtener un token de {0}.", + "Mention Mobile List": "Mencionar lista de móviles", + "Mention User List": "Mencionar la lista de id de usuario", + "Dingtalk Mobile List": "Lista de móviles", + "Dingtalk User List": "Lista de ID de usuario", + "Enter a list of userId": "Ingresa una lista de userId", + "Enter a list of mobile": "Ingresa una lista de móviles", + "Invalid mobile": "Móvil inválido [{mobile}]", + "Invalid userId": "UserId inválido [{userId}]", + "smseagleTtsModel": "ID del modelo de texto a voz", + "ntfyPriorityHelptextPriorityHigherThanDown": "La prioridad regular debe ser mayor que la prioridad {0}. La prioridad {1} es mayor que la prioridad {0} {2}", + "FlashDuty Push URL": "URL push", + "brevoFromEmail": "Desde el correo electrónico", + "brevoFromName": "De Nombre", + "brevoToEmail": "Para el correo electrónico", + "brevoCcEmail": "Correo electrónico CC", + "brevoBccEmail": "Correo electrónico BCC", + "brevoSeparateMultipleEmails": "Separa múltiples direcciones de correo con comas", + "brevoSubject": "Sujeto", + "Staged Tags for Batch Add": "Etiquetas preparadas para agregar por lotes", + "Nextcloud host": "Servidor Nextcloud", + "Conversation token": "Token de conversación", + "Bot secret": "Secreto del Bot", + "Send UP silently": "Envía UP silenciosamente", + "wayToGetEvolutionUrlAndToken": "Puede obtener la URL de la API y el token ingresando al canal deseado desde {0}", + "evolutionRecipient": "Número de teléfono / ID de contacto / ID de grupo", + "evolutionInstanceName": "Nombre de instancia", + "auto-select": "Selección automática", + "Number of retry attempts if webhook fails": "Número de intentos de reintento (cada 60–180 segundos) si el webhook falla.", + "Maximum Retries": "Máximo de reintentos", + "sipsakPingWarning": "Para poder utilizar el monitor de SIP Options Ping, necesitas instalar Uptime Kuma sin Docker e instalar el cliente Sipsak en tu servidor.", + "Plausible": "Admisible", + "Select All": "Seleccionar todo", + "Deselect All": "Desmarcar todo", + "HTTP Method": "Método HTTP", + "webhookPostMethodDesc": "POST es adecuado para la mayoría de servidores HTTP.", + "resendApiKey": "Reenviar la llave API", + "deleteGroupMsg": "¿Está seguro que quiere eliminar este grupo?", + "settingsDomainExpiry": "Expiración de dominio", + "labelDomainExpiry": "Exp. de dominio", + "message": "mensaje", + "domainExpiryDescription": "Lanzar notificación cuando el nombre de dominio expire en:", + "year": "año | años", + "descriptionHelpText": "Mostrar en el panel de control interno. Se permite markdown limpio (conserva el espacio y la sangría) antes de mostrarse.", + "json_value": "Valor JSON", + "Press Enter to add node": "Pulsa Enter para añadir el nodo", + "resendApiHelp": "Crear una llave de API aquí {0}", + "systemServiceDescription": "Comprueba si el servicio del sistema {service_name} está activo", + "systemServiceDescriptionLinux": "Comprueba si el servicio systemd {service_name} de Linux está activo", + "resendSubject": "Asunto", + "wsCodeDescription": "Para más información acerca de los códigos de estado, por favor consulta {rfc6455}", + "Subprotocol(s)": "Subprotocolo(s)", + "certHostnameMismatch": "El nombre de host del certificado no coincide con la URL del monitor.", + "twilioMessagingServiceSID": "SID del servicio de Mensajería (opcional)", + "resendFromEmail": "Correo electrónico del remitente", + "resendLeaveBlankForDefaultSubject": "Deja en blanco para utilizar el asunto por defecto", + "ignoreSecWebsocketAcceptHeaderDescription": "En caso de que la actualización del websocket sea satisfactoria, permite al servidor no responder con la cabecera Sec-WebSocket-Accept.", + "Ignore Sec-WebSocket-Accept header": "Ignorar la cabecera {0}", + "wsSubprotocolDescription": "Introduce una lista de subprotocolos separados por comas. Para más información sobre subprotocolos, por favor, consulta la {documentation}", + "RSS Title": "Título RSS", + "Leave blank to use status page title": "Dejar en blanco para utilizar el título de la página de estado", + "wayToGetClickSMSIRTemplateID": "Tu plantilla debe contener un campo {uptkumaalert}. Puedes crear una nueva plantilla {aquí}.", + "Webpush Helptext": "El push web solo funciona con conexiones SSL (HTTPS). Para dispositivos iOS, la página web debe añadirse a la página principal de antemano.", + "notificationUniversal": "Universal", + "notificationChatPlatforms": "Plataformas de chat", + "notificationPushServices": "Servicios Push", + "notificationSmsServices": "Servicios de SMS", + "notificationEmail": "Correo electrónico", + "notificationIncidentManagement": "Gestión de incidentes", + "notificationHomeAutomation": "Automatización del hogar", + "notificationOther": "Otras integraciones", + "serwersmsRecipientType": "Tipo de recipiente", + "serwersmsRecipientTypePhone": "Número de teléfono", + "serwersmsRecipientTypeGroup": "Grupo", + "serwersmsGroupId": "ID de grupo", + "serwersmsGroupIdHelptext": "ID o grupo de IDs en el panel del cliente. Estos identificadores pueden descargarse utilizando grupos de acción / índices o copiándolos desde el grupo de edición en el panel de cliente.", + "systemServiceDescriptionWindows": "Comprueba si el gestor de servicios {service_name} de Windows está ejecutándose", + "invalidURL": "URL no válida", + "Clone Maintenance": "Clonar Mantenimiento", + "ariaPauseMaintenance": "Pausar este cronograma de mantenimiento", + "systemServiceName": "Nombre del servicio", + "systemService": "Servicio del sistema", + "systemServiceCommandHint": "Comando utilizado: {command}", + "systemServiceExpectedOutput": "Salida espera: \"{0}\"", + "Enter the list of nodes": "Introduce la lista de gestión de nodos de RabbitMQ", + "resendFromName": "Nombre del remitente", + "resendLeaveBlankForDefaultName": "dejar en blanco para utilizar el nombre por defecto", + "resendToEmail": "Correo electrónico del receptor", + "Template ID": "ID de la plantilla", + "Notifications Enabled": "Notificaciones activadas", + "Allow Notifications": "Permitir notificaciones", + "Browser not supported": "Navegador no permitido", + "labelDomainNameExpiryNotification": "Notificación de expiración de dominio", + "Duration (Minutes)": "Duración (Minutos)", + "ariaResumeMaintenance": "Reanudar este cronograma de mantenimiento", + "ariaCloneMaintenance": "Crear una copia de este cronograma de mantenimiento", + "ariaEditMaintenance": "Editar este cronograma de mantenimiento", + "ariaDeleteMaintenance": "Eliminar este cronograma de mantenimiento", + "SMTP Security": "Seguridad SMTP", + "Ignore STARTTLS": "Ignorar STARTTLS", + "Use STARTTLS": "Utilizar STARTTLS", + "twilloMessagingServiceSIDHelptext": "Ingrese el SID del Servicio de Mensajería aquí si está usando {twillo_messaging_service_help_link} para administrar los remitentes y características", + "webhookGetMethodDesc": "GET envía los datos como parámetros de consulta y no permite la configuración del cuerpo de la consulta. Útil para disparar monitores PUSH de Uptime Kuma.", + "showOnlyLastHeartbeat": "Mostrar solo el último latido", + "Analytics Type": "Tipo de analíticas", + "Google": "Google", + "ntfyCall": "Llamada de teléfono", + "ntfyCallHelptext": "Hace una llamada de teléfono cuando se dispara la alerta. Márcalo como 'sí' para utilizar tu primer número verificado o introduce un número de teléfono específico (p.e +12223334444). Se necesita ntfy Pro y un número de teléfono verificado.", + "Unable to get permission to notify": "No se ha podido obtener el permiso para notificar (la petición ha sido denegada o ignorada).", + "You can divide numbers with commas or semicolons": "Puedes dividir números con {comma} o {semicolon}", + "HeadersInvalidFormatBecause": "Los cabezales de la petición no son un JSON válido porque {error}", + "BodyInvalidFormatBecause": "El cuerpo de la petición no es un JSON válido porque {error}", + "steamApiKeyDescriptionAt": "Para monitorear un servidor de juegos de Steam necesitas una clave de la API de Steam Web. Puedes registrar tu clave de la API en {url}", + "checkPriceAt": "Comprueba los precios de {service} en {url}", + "noMonitorsOrStatusPagesSelectedError": "No se puede crear un mantenimiento sin monitores afectados o páginas de estado", + "noMonitorsSelectedWarning": "Estás creando un mantenimiento sin ningún monitor afectado. ¿Estás seguro de que deseas continuar?", + "deleteChildrenMonitors": "Tambien elimina los monitores hijos directos y sus descendientes si los tuvieran | Tambien elimina todos los {count} monitores hijos directos y sus descendientes si los tuvieran", + "OptionalParameters": "Parámetros Opcionales", + "aliyun-template-requirements-and-parameters": "La plantilla de SMS de aliyun debe de contener los siguientes parámetros: {parameters}", + "aliyun-template-optional-parameters": "Parámetros opcionales: {parameters}", + "invalidHostnameOrIP": "Nombre de host o IP inválidos. El nombre del host debe ser un FQDN válido. No se pueden usar comodines. Pueden tener barra-bajas, o terminar con un punto.", + "invalidDNSHostname": "Nombre de host inválido. El nombre del host debe ser un FQDN válido. Puede ser un comodín, tener barra-baja o terminar con un punto.", + "wildcardOnlyForDNS": "Los nombres de host comodín sólo son soportados por los monitores DNS.", + "Analytics ID": "ID de Analytics", + "Analytics Script URL": "Script URL de Analytics", + "enableSSL": "Habilitar SSL/TLS", + "mariadbCaCertificateLabel": "Certificado CA", + "unknownDays": "Días desconocidos", + "No incidents recorded": "No se registraron incidentes", + "Load More": "Cargar más", + "mariadbUseSSLHelptext": "Habilita el uso de una conexión cifrada a tu base de datos. Requerido para la mayoría de las bases de datos en la nube.", + "mariadbCaCertificateHelptext": "Pegue el certificado CA en formato PEM para utilizarlo con certificados autofirmados. Déjelo en blanco si su base de datos utiliza un certificado firmado por una CA pública.", + "versionIs": "Versión: {version}", + "Loading...": "Cargando...", + "days": "{n} día | {n} días", + "hours": "{n} hora | {n} horas", + "minutes": "{n} minuto | {n} minutos", + "minuteShort": "{n} min | {n} min", + "years": "{n} año | {n} años", + "Pin this incident": "Fijar este incidente", + "saveResponseForNotifications": "Guardar respuesta HTTP correcta para notificaciones", + "saveErrorResponseForNotifications": "Guardar respuesta HTTP errónea para notificaciones", + "saveResponseDescription": "Almacena la respuesta HTTP y la pone a disposición de las plantillas de notificación como {templateVariable}", + "responseMaxLength": "Longitud máxima de la respuesta (bytes)", + "Only retry if status code check fails": "Reintentar solo si la comprobación del código de estado falla", + "retryOnlyOnStatusCodeFailureDescription": "Si está habilitado, los reintentos solo se realizarán cuando falle la comprobación del código de estado HTTP (por ejemplo, si el servidor está caído). Si la comprobación del código de estado es correcta pero falla la consulta JSON, el monitor se marcará como inactivo inmediatamente, sin reintentos.", + "responseMaxLengthDescription": "Tamaño máximo de los datos de la respuesta que se van a almacenar. Establezca 0 para ilimitado. Las respuestas más grandes se truncarán. Valor por defecto: 1024 (1 KB)", + "logoutCurrentUser": "Cerrar sesión de {username}", + "Incident description": "Descripción del incidente", + "twilioApiKeyHelptext": "La llave de la API es opcional pero recomendada. Puede proporcionar el SID de la cuenta y el Token de Autorizacion desde la consola de Twilio o el SID de la cuenta y la llave de la API junto con el secreto de la llave de la API", + "monitorTypeGameServer": "Servidor de Juego", + "monitorTypeDatabase": "Tipo de Monitor de Base de Datos", + "monitorTypeSpecial": "Especial", + "Recipient Numbers": "Números de destinatarios", + "Incident not found or access denied": "No se encontró incidente o acceso denegado", + "Past Incidents": "Incidentes pasados", + "Incident title": "Título del incidente", + "example": "Ejemplo", + "Result": "Resultado", + "lastUpdatedAt": "Última actualización: {date}", + "Actions": "Acciones", + "selectAllMonitorsAria": "Seleccionar todos los monitores", + "deselectAllMonitorsAria": "Deseleccionar todos los monitores", + "lastUpdatedAtFromNow": "Última actualización: {date} ({fromNow})", + "See Jira Cloud Docs": "Ver la documentación de Jura Cloud", + "Cloud ID": "ID de nube", + "API Token": "Token de API", + "templateAvailableVariables": "Variables disponibles", + "selectMonitorMsg": "Selecciona monitores para realizar acciones", + "Examples:": "Ejemplos: {0}", + "Pinned incidents are shown prominently on the status page": "Incidentes marcados se muestran prominentemente en la pagina de estado", + "Edit Incident": "Editar incidente", + "Please input title": "Por favor ingresa título", + "Resolve": "Resolver", + "Resolved": "Resuelto", + "createdAt": "Creado: {date}", + "deleteIncidentMsg": "Estas seguro que quieres eliminar este incidente?", + "Certificate Chain:": "Cadena de certificado:", + "Please input content": "Por favor ingresa contenido", + "dateCreatedAtFromNow": "Fecha de creación: {date} ({fromNow})", + "360messengerAuthToken": "Clave de la API 360messenger", + "360messengerGroupId": "ID de grupo 360messenger", + "360messengerGroupList": "Grupos de WhatsApp", + "ntfyUseTemplateDescription": "Habilite esta opción para personalizar los títulos y mensajes de notificación mediante plantillas LiquidJS", + "ntfyCustomTitle": "Plantilla de título personalizado", + "ntfyCustomMessage": "Plantilla de mensaje personalizado", + "ntfyNotificationTemplateFallback": "Dejar en blanco para utilizar el formato predeterminado de Uptime Kuma", + "Screenshot Delay": "Retraso de captura de pantalla (espera {milliseconds})", + "milliseconds": "{n} milisegundos | {n} milisegundos", + "snmpV3Username": "Nombre de usuario SNMPv3", + "ntfyUseTemplate": "Personalizar plantillas de notificación", + "Monitors": "{n} Monitor | {n} Monitores", + "oracledbConnectionString": "Base de datos Oracle: {connectionString}", + "wayToGetMaxToken": "Puede obtener un token de bot MAX y otros detalles en {0}.", + "selectedMonitorCountMsg": "seleccionado: {n} | seleccionados: {n}", + "maxUseTemplate": "Usar plantilla de mensaje personalizada", + "certificateExpiryNotificationHelp": "El número de días de antelación se puede configurar en los Ajustes.", + "Bearer Token": "Token", + "matrixUseTemplate": "Usar plantilla de mensaje personalizada", + "matrixUseTemplateDescription": "Si está activo, el mensaje se enviará utilizando una plantilla personalizada.", + "Connection Timeout": "Tiempo de espera de conexión", + "slug is not found": "No se ha encontrador el “slug”", + "pingPacketSizeWarning": "Un tamaño de paquete menor que 16 no produce tiempos de ida y vuelta en Linux", + "hostnameCannotBeIP": "El nombre del host del DNS no puede ser una IP. Te referías a un campo resolver?", + "smtpAdditionalHeadersDesc": "Por favor, consulte los encabezados personalizados de Nodemailer {0} para conocer su uso.", + "smtpAdditionalHeadersTitle": "Encabezados Adicionales", + "wayToGetEgoSMSToken": "Puedes obtener tu nombre de usuario y contraseña de la API de EgoSMS {aquí}.", + "egosmsSenderDescription": "ID del remitente que se muestra a los destinatarios (máximo 11 caracteres). Si se deja vacío, se utiliza EGOSMS por defecto.", + "egosmsPhoneNumberDescription": "Número del destinatario con código de país, sin + ni 00 (p. ej., 2567XXXXXXXX).", + "maxMessenger": "MAX messenger", + "maxApiUrlDescription": "URL base de la API para MAX messenger. Por defecto: Default: https://platform-api.max.ru", + "Resolver Server(s)": "Servidor(es) de Resolución", + "Hash Key": "Clave hash", + "wayToGetBearSMSToken": "Puedes obtener tu nombre de usuario y clave hash de la API de BearSMS en {0}.", + "bearsmsPhoneNumberDescription": "Número del destinatario con código de país, sin + ni 00 (p. ej., 9725XXXXXXXX).", + "bearsmsSenderDescription": "Identificador del remitente opcional que se muestra a los destinatarios (máximo 11 caracteres). Debe estar aprobado en su cuenta de BearSMS; de lo contrario, se utilizará el remitente predeterminado de la cuenta.", + "cronScheduleDescription": "Cronograma: {description}", + "frontendVersionIs": "Versión de la Interfaz: [version]", + "disableSTARTTLSDescription": "Habilite esta opción para servidores SMTP que no soportan STARTTLS. Esto enviará correos a través de una conexión no cifrada.", + "API Version": "Versión de la API", + "wxPusherDownloadApp": "descargar la applicación", + "wxPusherDocs": "guía oficial", + "wayToGetMaxChatID": "Especifica el identificador de chat en MAX a donde los mensajes deberían ser entregados.", + "maxUseTemplateDescription": "Si está habilitado, el mensaje será enviado usando la plantilla personalizada.", + "maxTemplateFormatDescription": "MAX messenger soporta formato de texto plano, Markdown y HTML.", + "WeCom Mentioned Mobile List Description": "Introduce los números de teléfono que quieras mencionar. Separa los números múltiples con comas. Usa {'@'}all para mencionar a todos.", + "pausedMonitorsMsg": "Pausado {n} monitor | Pausados {n} monitors", + "resumedMonitorsMsg": "Reanudado {n} monitor | Reanudados {n} monitores", + "deletedMonitorsMsg": "Eliminado {n} monitor | Eliminados {n} monitores", + "Access Key": "Clave de acceso", + "ooredooUsernameDescription": "La dirección de correo electrónico que utiliza para iniciar sesión en el panel de control de SMS masivos de Ooredoo.", + "ooredooAccessKeyDescription": "Lo encontrarás en la sección \"API Access Key\" de tu panel de control de SMS de Ooredoo. Pégalo exactamente como aparece; se codifica automáticamente en base64 antes de enviarse.", + "ooredooBearerTokenDescription": "El token que aparece después de \"Authorization: Bearer\" en el comando cURL de ejemplo de tu panel de control de SMS de Ooredoo.", + "ooredooRecipientDescription": "Ingrese uno o varios números separados por comas o espacios. A los números locales de 7 dígitos se les antepone automáticamente el prefijo 960.", + "Leave blank to use the default Ooredoo endpoint.": "Déjelo en blanco para utilizar el punto de conexión predeterminado de Ooredoo.", + "Google Apps Script Webhook URL": "URL del WebHook del Google App Script", + "Go to Extensions → Apps Script": "Ir a Extensiones → Apps Script", + "teamsEnableTagsDescription": "Si está habilitado, el mensaje incluirá las etiquetas del monitor.", + "teamsEnableTags": "Incluir etiquetas", + "Please set start time first": "Por favor, define el tiempo de inicio primero", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Configure 'Ejecutar como: Yo' y 'Quién tiene acceso: Cualquier persona'", + "Copy to Clipboard": "Copiar al Portapapeles", + "Copied to clipboard!": "¡Copiado al Portapapeles!", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Debido a restricciones del carrier, habilite variables opcionales bajo el riesgo de que no se realice la entrega", + "WeCom Mentioned Mobile List": "Lista de menciones móviles de WeCom", + "aboutJiraCloudId": "Más información sobre el ID de Jira Cloud: {0}", + "homeAssistantNotificationActionHelptext": "Una lista de acciones de notificación puede ser encontrada en Home Assistant en \"Settings > Developer Tools > Actions\". Busque \"notify\" para encontrar sus acciones. Introduzca solamente la parte después de \"notify.\", e.g. para la acción \"notify.mobile_app_xyz\" introduzca \"mobile_app_xyz\". Para notificaciones mobiles incorporadas (built-in), busque \"Send a notification via mobile_app_xyz\" (en vez de \"Send a notification\").", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) funciona a través de la aplicación independiente WxPusher en 6 plataformas: Android, iOS, HarmonyOS, Windows, macOS y Linux ({descargar}). Escanea el código QR que aparece a continuación para obtener tu SPT y estarás listo para enviar mensajes. Separa varios SPT con una coma (,); consulta la {documentación} para más información.", + "slackIncludeGroupName": "Incluir el nombre del grupo de monitores", + "slackUseTemplateDescription": "Si está habilitada, la notificación se enviará utilizando una plantilla personalizada. Puede utilizar plantillas Liquid para incluir información del grupo de monitores mediante monitorJSON.path o monitorJSON.pathName.", + "signalUseTemplate": "Usar plantilla de mensaje personalizada", + "signalUseTemplateDescription": "Si está habilitado, el mensaje se enviará utilizando una plantilla personalizada. Puede utilizar plantillas Liquid para personalizar el formato de la notificación.", + "Notification Action": "Acción de Notificación", + "Disable STARTTLS": "Deshabilitar STARTTLS", + "Click Deploy → New deployment → Web app": "Haz clic en Implementar → Nueva implementación → Aplicación web", + "octopushEndpoint": "octopush (punto de conexión: {url})", + "Sets end time based on start time": "Definir el tiempo de finalización en base al tiempo de inicio", + "vkApiVersionDescription": "Versión VK API usada para las solicitudes. Deje el valor por defecto a menos que necesite otra versión de la VK API por compatibilidad.", + "Peer ID": "Identificador (ID) del Par (Peer)", + "vkDontParseLinks": "Habilitar fragmentos (snippets) de enlaces", + "vkDontParseLinksDescription": "Si está habilitado, VK no generará prevista de enlaces/fragmentos (link/snippets).", + "vkPeerIdDescription": "Introduzca el VK peer_id de destino. Este valor se envia a la API sin modificaciones.", + "legacyOctopushEndpoint": "Legacy Octopush-DM (punto de conexión: {url})", + "Quick Setup Guide": "Guía Rápida de Configuración", + "Open your Google Spreadsheet": "Abrir la Hoja de Cálculo (Google Spreadsheet)", + "Deploy a Google Apps Script as a web app and paste the URL here": "Despliegue un Google App Script como una aplicación Web e introduzca la URL aquí", + "Copy the web app URL and paste it above": "Copia la URL de la aplicación web y péguela arriba", + "Google Apps Script Code": "Código de Google Apps Script", + "Failed to copy to clipboard": "Fallo al copiar al portapapeles", + "Paste the script code (see below)": "Introduzca el código del script (mire abajo)", + "see Jira Cloud Docs": "Consulta la documentación de Jira Cloud", + "Jira Service Management": "Gestión de servicios de Jira", + "slackIncludeGroupNameDescription": "Si está habilitada, la ruta del grupo de monitores se incluirá en las notificaciones para ayudar a distinguir los monitores con el mismo nombre en diferentes grupos.", + "slackUseTemplate": "Usar plantilla de mensaje personalizada", + "deleteMonitorsMsg": "Está seguro de eliminar los monitores seleccionados?", + "noMonitorsResumedMsg": "No hay monitores reanudados (ninguno estaba inactivo)", + "noMonitorsPausedMsg": "No hay monitores pausados (ninguno estaba activo)", + "bulkDeleteErrorMsg": "Fallo al eliminar {n} monitor | Fallo al eliminar {n} monitores" +} diff --git a/src/lang/et-EE.json b/src/lang/et-EE.json new file mode 100644 index 0000000..aa519ec --- /dev/null +++ b/src/lang/et-EE.json @@ -0,0 +1,317 @@ +{ + "languageName": "eesti", + "retryCheckEverySecond": "Kontrolli {0} sekundilise vahega", + "retriesDescription": "Mitu korda tuleb kontrollida, mille järel märkida 'maas' ja saata välja teavitus", + "ignoreTLSError": "Eira TLS/SSL viga HTTPS veebisaitidel", + "upsideDownModeDescription": "Käitle teenuse saadavust rikkena, teenuse kättesaamatust töötavaks.", + "maxRedirectDescription": "Suurim arv ümbersuunamisi, millele järgida. 0 ei luba ühtegi.", + "acceptedStatusCodesDescription": "Vali välja HTTP koodid, mida arvestada kõlblikuks.", + "passwordNotMatchMsg": "Salasõnad ei kattu.", + "notificationDescription": "Teavitusteenuse kasutamiseks seo see seirega.", + "keywordDescription": "Jälgi võtmesõna HTML või JSON vastustes. (tõstutundlik)", + "pauseDashboardHome": "Seisatud", + "deleteMonitorMsg": "Kas soovid eemaldada seire?", + "deleteNotificationMsg": "Kas soovid eemaldada selle teavitusteenuse kõikidelt seiretelt?", + "resolverserverDescription": "Cloudflare on vaikimisi pöördserver.", + "rrtypeDescription": "Vali kirje tüüp, mida soovid jälgida.", + "pauseMonitorMsg": "Kas soovid peatada seire?", + "Settings": "Seaded", + "Status Page": "Ülevaade", + "Status Pages": "Ülevaated", + "Dashboard": "Töölaud", + "New Update": "Uuem tarkvara versioon on saadaval", + "Language": "Keel", + "Appearance": "Välimus", + "Theme": "Teema", + "General": "Üldine", + "Version": "Versioon", + "Check Update On GitHub": "Otsi uuendusi GitHub'ist", + "List": "Nimekiri", + "Add": "Lisa", + "Add New Monitor": "Lisa seire", + "Add a monitor": "Lisa seire", + "Quick Stats": "Ülevaade", + "Up": "Töökorras", + "Down": "Rikkis", + "Pending": "Määramisel", + "Unknown": "Kahtlast", + "Pause": "Seiska", + "Name": "Nimi", + "Status": "Olek", + "DateTime": "Kuupäev", + "Message": "Tulemus", + "No important events": "Märkimisväärsed juhtumid puuduvad", + "Resume": "Taasta", + "Edit": "Muuda", + "Delete": "Eemalda", + "Current": "Hetkeseisund", + "Uptime": "Eluiga", + "Cert Exp.": "Sert. aegumine", + "day": "päev | päeva", + "-day": "-päev", + "hour": "tund", + "-hour": "-tund", + "Response": "Reaktsiooniaeg", + "Ping": "Ping", + "Monitor Type": "Seire tüüp", + "Keyword": "Võtmesõna", + "Friendly Name": "Sõbralik nimi", + "URL": "URL", + "Hostname": "Hostname", + "Port": "Port", + "Heartbeat Interval": "Tukse sagedus", + "Retries": "Korduskatsed", + "Advanced": "Rohkem", + "Upside Down Mode": "Tagurpidi seire", + "Max. Redirects": "Max. ümbersuunamine", + "Accepted Status Codes": "Kõlblikud HTTP koodid", + "Save": "Salvesta", + "Notifications": "Teavitused", + "Not available, please setup.": "Ühtegi teavitusteenust pole saadaval.", + "Setup Notification": "Lisa teavitusteenus", + "Light": "hele", + "Dark": "tume", + "Auto": "automaatne", + "Theme - Heartbeat Bar": "Teemasäte — tuksete riba", + "Normal": "tavaline", + "Bottom": "all", + "None": "puudub", + "Timezone": "Ajatsoon", + "Search Engine Visibility": "Otsimootorite ligipääs", + "Allow indexing": "Luba indekseerimine", + "Discourage search engines from indexing site": "Keela selle saidi indekseerimine otsimootorite poolt", + "Change Password": "Muuda parooli", + "Current Password": "Praegune parool", + "New Password": "Uus parool", + "Repeat New Password": "Korda uut parooli", + "Update Password": "Uuenda parooli", + "Disable Auth": "Lülita autentimine välja", + "Enable Auth": "Lülita autentimine sisse", + "disableauth.message1": "Kas soovid {disableAuth}?", + "disable authentication": "lülitada autentimise välja", + "disableauth.message2": "Kastuamiseks {intendThirdPartyAuth}, näiteks Cloudflare Access.", + "where you intend to implement third-party authentication": "välise autentimispakkujaga", + "Please use this option carefully!": "Palun kasuta seda valikut vastutustundlikult!", + "Logout": "Logi välja", + "Leave": "Lahku", + "I understand, please disable": "Olen tutvunud riskidega, lülita välja", + "Confirm": "Kinnita", + "Yes": "Jah", + "No": "Ei", + "Username": "Kasutajanimi", + "Password": "Parool", + "Remember me": "Mäleta mind", + "Login": "Logi sisse", + "No Monitors, please": "Seired puuduvad, palun", + "add one": "Lisa esimene", + "Notification Type": "Teavituse tüüp", + "Email": "e-posti aadress", + "Test": "Saada prooviteavitus", + "Certificate Info": "Sertifikaadi teave", + "Resolver Server": "Server, mis vastab DNS päringutele.", + "Resource Record Type": "DNS kirje tüüp", + "Last Result": "Viimane", + "Create your admin account": "Admininstraatori konto loomine", + "Repeat Password": "korda salasõna", + "respTime": "Reageerimisaeg (ms)", + "notAvailableShort": "N/A", + "enableDefaultNotificationDescription": "Kõik järgnevalt lisatud seired kasutavad seda teavitusteenuset. Seiretelt võib teavitusteenuse ühekaupa eemaldada.", + "clearEventsMsg": "Kas soovid seire kõik sündmused kustutada?", + "clearHeartbeatsMsg": "Kas soovid seire kõik tuksed kustutada?", + "confirmClearStatisticsMsg": "Kas soovid TERVE ajaloo kustutada?", + "Export": "Eksport", + "Import": "Import", + "Default enabled": "Kasuta vaikimisi", + "Apply on all existing monitors": "Kõik praegused seired hakkavad kasutama seda teavitusteenust", + "Create": "Loo konto", + "Clear Data": "Eemalda andmed", + "Events": "Sündmused", + "Heartbeats": "Tuksed", + "Auto Get": "Hangi automaatselt", + "backupDescription": "Varunda kõik seired ja teavitused JSON faili.", + "backupDescription2": "PS: Varukoopia EI sisalda seirete ajalugu ja sündmustikku.", + "backupDescription3": "Varukoopiad sisaldavad teavitusteenusete pääsuvõtmeid.", + "alertNoFile": "Palun lisa fail, mida importida.", + "alertWrongFileType": "Palun lisa JSON-formaadis fail.", + "twoFAVerifyLabel": "2FA kinnitamiseks sisesta pääsukood", + "tokenValidSettingsMsg": "Kood õige. Akna võib sulgeda.", + "confirmEnableTwoFAMsg": "Kas soovid 2FA sisse lülitada?", + "confirmDisableTwoFAMsg": "Kas soovid 2FA välja lülitada?", + "Verify Token": "Kontrolli", + "Setup 2FA": "Kaksikautentimise seadistamine", + "Enable 2FA": "Seadista 2FA", + "Disable 2FA": "Lülita 2FA välja", + "2FA Settings": "2FA seaded", + "Two Factor Authentication": "Kaksikautentimine", + "Active": "Aktiivne", + "Inactive": "Mitteaktiivne", + "Token": "Kaksikautentimise kood", + "Show URI": "Näita URId", + "Clear all statistics": "Tühjenda ajalugu", + "importHandleDescription": "'kombineeri' täiendab varukoopiast ja kirjutab üle samanimelised seireid ja teavitusteenused; 'lisa praegustele' jätab olemasolevad puutumata; 'asenda' kustutab ja asendab kõik seired ja teavitusteenused.", + "confirmImportMsg": "Käkerdistest hoidumiseks lae enne taastamist alla uus varukoopia. Kas soovid taastada üles laetud?", + "Heartbeat Retry Interval": "Korduskatsete intervall", + "Import Backup": "Varukoopia importimine", + "Export Backup": "Varukoopia eksportimine", + "Skip existing": "lisa praegustele", + "Overwrite": "Asenda", + "Options": "Mestimisviis", + "Keep both": "Kombineeri", + "Tags": "Sildid", + "Add New below or Select...": "Leia või lisa all uus…", + "Tag with this name already exist.": "Selle nimega silt on juba olemas.", + "Tag with this value already exist.": "Selle väärtusega silt on juba olemas.", + "color": "värvus", + "value (optional)": "väärtus (fakultatiivne)", + "Gray": "hall", + "Red": "punane", + "Orange": "oranž", + "Green": "roheline", + "Blue": "sinine", + "Indigo": "indigo", + "Purple": "lilla", + "Pink": "roosa", + "Search...": "Otsi…", + "Avg. Ping": "Keskmine ping", + "Avg. Response": "Keskmine reaktsiooniaeg", + "Entry Page": "Avaleht", + "statusPageNothing": "Kippu ega kõppu; siia saab lisada seireid või -gruppe.", + "No Services": "Teenused puuduvad", + "All Systems Operational": "Kõik töökorras", + "Partially Degraded Service": "Teenuse töö osaliselt häiritud", + "Degraded Service": "Teenuse töö häiritud", + "Add Group": "Lisa grupp", + "Edit Status Page": "Muuda lehte", + "Go to Dashboard": "Töölauale", + "checkEverySecond": "Kontrolli peale tõrget {0} sekundilise vahega", + "telegram": "Telegram", + "webhook": "Webhook", + "smtp": "elektronpost (SMTP)", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (vahendab üle 65 teavitusteenust)", + "pushbullet": "Pushbullet", + "line": "LINE", + "mattermost": "Mattermost", + "alerta": "Alerta", + "alertaApiEndpoint": "API otsik", + "alertaEnvironment": "Keskkond", + "alertaApiKey": "API võti", + "alertaAlertState": "Häireseisund", + "alertaRecoverState": "Taasta algolek", + "Game": "Mäng", + "Primary Base URL": "Peamine baas URL", + "Passive Monitor Type": "Passiivne monitori tüüp", + "Specific Monitor Type": "Spetsiifiline monitori tüüp", + "resendDisabled": "Uuesti saatmine keelatud", + "Push URL": "Lükka URL", + "needPushEvery": "Sa peaksid kutsuma seda URL-i iga {0} sekundi tagant.", + "pushOptionalParams": "Valikulised parameetrid: {0}", + "Schedule maintenance": "Planeeri hooldus", + "All Status Pages": "Kõik staatuse lehed", + "Select status pages...": "Vali staatuse lehed…", + "Custom": "Kohandatud", + "here": "siin", + "Required": "Nõutud", + "Post URL": "Postita URL", + "Affected Monitors": "Mõjutatud monitorid", + "Pick Affected Monitors...": "Vali mõjutatud monitorid…", + "Start of maintenance": "Hoolduse algus", + "Content Type": "Sisu tüüp", + "webhookJsonDesc": "{0} on hea iga modernse HTTP serveri jaoks nagu Express.js", + "webhookAdditionalHeadersTitle": "Täiendavad päised", + "setAsDefault": "Lisa vaikimisi", + "deleteProxyMsg": "Kas Sa oled kindel, et soovid kustutada seda puhverserverit kõkidel monitoridel?", + "proxyDescription": "Puhverserverid tuleb lisada monitorile selle töötamiseks.", + "setAsDefaultProxyDescription": "See puhverserver aktiveeritakse vaikimisi uutel monitoridel. Sa saad keelata seda puhverserverit igal monitoril eraldi.", + "Certificate Chain": "Sertifikaadi kett", + "Valid": "Kehtiv", + "Invalid": "Kehtetu", + "User": "Kasutaja", + "Installed": "Paigaldatud", + "Not installed": "Ei ole installeeritud", + "Running": "Töötab", + "resendEveryXTimes": "Saada uuesti {0} korda", + "statusMaintenance": "Hooldus", + "Server URL": "Serveri URL", + "Priority": "Tähtsus", + "emojiCheatSheet": "Emotikoni spikker: {0}", + "appriseInstalled": "Apprise on installitud.", + "appriseNotInstalled": "Apprise ei ole installitud. {0}", + "Method": "Meetod", + "Body": "Keha", + "Headers": "Päis", + "PushUrl": "Lükka URL", + "Monitor History": "Monitori ajalugu", + "PasswordsDoNotMatch": "Paroolid ei ühti.", + "Current User": "Praegune kasutaja", + "topic": "Teema", + "successMessage": "Edukas sõnum", + "recent": "Hiljutine", + "Info": "Info", + "Security": "Turvalisus", + "Steam API Key": "Steam API võti", + "Pick a RR-Type...": "Vali RR-tüüp…", + "Default": "Vaikimisi", + "HTTP Options": "HTTP valikud", + "Create Incident": "Loo intsident", + "Title": "Pealkiri", + "Content": "Sisu", + "Style": "Stiil", + "info": "info", + "warning": "hoiatus", + "danger": "oht", + "error": "viga", + "critical": "kriitiline", + "primary": "peamine", + "dark": "tume", + "light": "hele", + "Post": "Postita", + "Please input title and content": "Palun lisa pealkiri ja sisu", + "Created": "Loodud", + "Last Updated": "Viimati uuendatud", + "Unpin": "Vabastada", + "Switch to Dark Theme": "Vaheta tumedale teemale", + "Hide Tags": "Peida tagid", + "Show Tags": "Näita tagid", + "Description": "Kirjeldus", + "No monitors available.": "Ühtegi monitori ei ole saadaval.", + "Add one": "Lisa üks", + "No Monitors": "Ei ole monitore", + "Untitled Group": "Nimetamata grupp", + "Services": "Teenused", + "Cancel": "Tühista", + "Customize": "Kohanda", + "Custom Footer": "Kohandatud jalus", + "Custom CSS": "Kohandatud CSS", + "Proxies": "Puhverserverid", + "default": "Vaikimisi", + "enabled": "Lubatud", + "Not running": "Ei tööta", + "Start": "Alusta", + "Stop": "Peata", + "Add New Status Page": "Lisa uus staatuse leht", + "Shrink Database": "Vähenda andmebaasi", + "Help": "Abi", + "Maintenance": "Hooldus", + "General Monitor Type": "Üldine monitori tüüp", + "webhookAdditionalHeadersDesc": "Lisab täiendavad päised saadetud webhookiga.", + "Read more": "Loe rohkem", + "clearDataOlderThan": "Hoia monitori ajalugu alles {0} päeva.", + "steamApiKeyDescription": "Steam Game Serveri monitoorimiseks on vaja sul Steam Web-API võtit. Sa saad registreerida enda API võtme siin: ", + "Done": "Tehtud", + "Pick Accepted Status Codes...": "Vali vastu võetud staatuse koodid…", + "Switch to Light Theme": "Vaheta heledale teemale", + "Discard": "Loobu", + "deleteStatusPageMsg": "Kas Sa oled kindel, et soovid kustutada seda staatuse lehte?", + "Resend Notification if Down X times consecutively": "Saada teavitus uuesti kui monitor on rikkis X korda järjest" +} diff --git a/src/lang/eu.json b/src/lang/eu.json new file mode 100644 index 0000000..045ed8b --- /dev/null +++ b/src/lang/eu.json @@ -0,0 +1,752 @@ +{ + "languageName": "Euskara", + "checkEverySecond": "Egiaztatu {0} segunduro", + "retryCheckEverySecond": "Errepikatu {0} segunduro", + "retriesDescription": "Zerbitzua erorita markatu eta jakinarazpena bidali aurretik egindako saiakera kopuru maximoa", + "ignoreTLSError": "Ezikusiarena egin TLS/SSL erroreei HTTPS webguneetan", + "upsideDownModeDescription": "Alderantzizkatu erortze egoera. Zerbitzua martxan badago, ERORITA markatuko du.", + "maxRedirectDescription": "Jarraitu beharreko berbideratze kopuru maximoa. Jarri 0 berbideratzeak desgaitzeko.", + "acceptedStatusCodesDescription": "Hautatu erantzun ona kontsideratzen diren egoera kodeak.", + "passwordNotMatchMsg": "Errepikatutako pasahitza ez dator bat.", + "notificationDescription": "Jakinarazpenak monitorizazio funtzio bati asignatu behar zaizkio.", + "keywordDescription": "Bilatu gako-hitza HTML edo JSON erantzunean. Bilaketan maiuskulak kontuan hartzen dira.", + "pauseDashboardHome": "Gelditu", + "deleteMonitorMsg": "Ziur zaude monitorizazio hau ezabatu nahi duzula?", + "deleteNotificationMsg": "Ziur zaude jakinarazpen hau monitorizazio guztientzat ezabatu nahi duzula?", + "dnsPortDescription": "DNS zerbitzari portua. Defektuz 53. Nahi duzunean aldatu dezakezu portua.", + "resolverserverDescription": "Cloudflare zerbitzari lehenetsia da. Edozein unetan alda dezakezu ebazteko zerbitzaria.", + "rrtypeDescription": "Hautatu kontrolatu nahi duzun RR mota", + "enableDefaultNotificationDescription": "Jakinarazpen hau monitore berrientzat gaituko da defektuz. Baina monitorizazio bakoitzarentzat jakinarazpena desgaitu dezakezu.", + "pauseMonitorMsg": "Ziur zaude gelditu egin nahi duzula?", + "clearEventsMsg": "Ziur zaude monitorizazio honen gertaera guztiak ezabatu nahi dituzula?", + "clearHeartbeatsMsg": "Ziur zaude monitorizazio honen pultsu guztiak ezabatu nahi dituzula?", + "confirmClearStatisticsMsg": "Ziur zaude estatistika GUZTIAK ezabatu nahi dituzula?", + "importHandleDescription": "Aukeratu 'existitzen bada', izen bereko monitore edo jakinarazpen bakoitza saltatu nahi baduzu. Lehendik dauden kontrol eta jakinarazpen guztiak ezabatuko ditu 'Gainidatzi' aukerak.", + "confirmImportMsg": "Ziur zaude segurtasun-kopia inportatu nahi duzula? Egiaztatu inportatzeko aukera zuzena hautatu duzula.", + "twoFAVerifyLabel": "Sartu zure tokena 2FA egiaztatzeko:", + "tokenValidSettingsMsg": "Tokenak balio du! Orain 2FA konfigurazioa gorde dezakezu.", + "confirmEnableTwoFAMsg": "Ziur zaude 2FA gaitu nahi duzula?", + "confirmDisableTwoFAMsg": "Ziur zaude 2FA desgaitu nahi duzula?", + "Settings": "Ezarpenak", + "Dashboard": "Arbela", + "New Update": "Eguneraketa berria", + "Language": "Hizkuntza", + "Appearance": "Itxura", + "Theme": "Gaia", + "General": "Orokorra", + "Primary Base URL": "Oinarrizkoa URL", + "Version": "Bertsioa", + "Check Update On GitHub": "Egiaztatu eguneraketa GitHuben", + "List": "Zerrenda", + "Add": "Gehitu", + "Add New Monitor": "Gehitu monitorizazio berria", + "Quick Stats": "Estatistika azkarrak", + "Up": "Erabilgarri", + "Down": "Erorita", + "Pending": "Zain", + "Unknown": "Ezezaguna", + "Pause": "Gelditu", + "Name": "Izena", + "Status": "Egoera", + "DateTime": "Data eta ordua", + "Message": "Mezua", + "No important events": "Gertaera garrantzitsurik ez", + "Resume": "Jarraitu", + "Edit": "Editatu", + "Delete": "Ezabatu", + "Current": "Unekoa", + "Uptime": "Martxan", + "Cert Exp.": "Ziurtagiri iraun.", + "day": "egun | egun", + "-day": "-egun", + "hour": "ordua", + "-hour": "-ordu", + "Response": "Erantzuna", + "Ping": "Ping", + "Monitor Type": "Monitorizazio mota", + "Keyword": "Gakohitza", + "Friendly Name": "Izen xumea", + "URL": "URLa", + "Hostname": "Ostalari izena", + "Port": "Portua", + "Heartbeat Interval": "Pultsu interbaloak", + "Retries": "Errepikapenak", + "Heartbeat Retry Interval": "Pultsu errepikatze interbaloak", + "Advanced": "Aurreratua", + "Upside Down Mode": "Alderantzizkako modua", + "Max. Redirects": "Birbideratze max.", + "Accepted Status Codes": "Onartutako egoera kodeak", + "Push URL": "Push URLa", + "needPushEvery": "URL hau {0} segunduro deitu beharko zenuke.", + "pushOptionalParams": "Hautazko parametroak: {0}", + "Save": "Gorde", + "Notifications": "Jakinarazpenak", + "Not available, please setup.": "Ez dago eskuragarri, ezarri mesedez.", + "Setup Notification": "Ezarri jakinarazpenak", + "Light": "Argia", + "Dark": "Iluna", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Gaia - Pultsu barra", + "Normal": "Normala", + "Bottom": "Behean", + "None": "Bat ere ez", + "Timezone": "Timezone", + "Search Engine Visibility": "Bilatzaile ikurgarritasuna", + "Allow indexing": "Onartu indexatzea", + "Discourage search engines from indexing site": "Discourage search engines from indexing site", + "Change Password": "Aldatu pasahitza", + "Current Password": "Uneko pasahitza", + "New Password": "Pasahitz berria", + "Repeat New Password": "Errepikatu pasahitz berria", + "Update Password": "Eguneratu pasahitza", + "Disable Auth": "Desgaitu Auth", + "Enable Auth": "Gaitu Auth", + "disableauth.message1": "Ziur zaude {disableAuth} nahi duzula?", + "disable authentication": "autentifikazioa desgaitu", + "disableauth.message2": "Egoera jakin batzuetarako diseinatuta dago, Uptime Kumaren {intendThirdPartyAuth} (Cloudflare Access, Authelia edo beste autentifikazio-mekanismo batzuk).", + "where you intend to implement third-party authentication": "aurrean hirugarrengo autentifikazio batzuek jartzeko", + "Please use this option carefully!": "Mesedez, kontuz erabili aukera hau!", + "Logout": "Itxi saioa", + "Leave": "Utzi", + "I understand, please disable": "Ulertzen dut, mesedez desgaitu", + "Confirm": "Baieztatu", + "Yes": "Bai", + "No": "Ez", + "Username": "Erabiltzailea", + "Password": "Pasahitza", + "Remember me": "Gogora nazazu", + "Login": "Hasi saioa", + "No Monitors, please": "Monitorizaziorik ez, mesedez", + "add one": "gehitu bat", + "Notification Type": "Jakinarazpen mota", + "Email": "Emaila", + "Test": "Testa", + "Certificate Info": "Ziurtagiri informazioa", + "Resolver Server": "Ebazpen-zerbitzaria", + "Resource Record Type": "Baliabideen erregistro mota", + "Last Result": "Azken emaitza", + "Create your admin account": "Sortu zure admin kontua", + "Repeat Password": "Errepikatu pasahitza", + "Import Backup": "segurtasun-kopia inportatu", + "Export Backup": "segurtasun-kopia esportatu", + "Export": "Esportatu", + "Import": "Inportatu", + "respTime": "Erantz. denbora (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Lehenetsia gaituta", + "Apply on all existing monitors": "Aplikatu existitzen diren monitorizazio guztietan", + "Create": "Sortu", + "Clear Data": "Garbitu datuak", + "Events": "Gertaerak", + "Heartbeats": "Pultsuak", + "Auto Get": "Auto Get", + "backupDescription": "Monitore eta jakinarazpen guztien segurtasun-kopiak egin ditzakezu JSON fitxategi batean.", + "backupDescription2": "Oharra: ez dira historia eta gertaeren datuak sartzen.", + "backupDescription3": "Datu sentikorrak, hala nola jakinarazpen tokenak, esportazio-fitxategian sartzen dira; mesedez, gorde esportazioa modu seguruan.", + "alertNoFile": "Mesedez hautatu inportatzeko fitxategia.", + "alertWrongFileType": "Mesedez hautatu JSON fitxategia.", + "Clear all statistics": "Garbitu estatistika guztiak", + "Skip existing": "Saltatu existitzen bada", + "Overwrite": "Gainidatzi", + "Options": "Aukerak", + "Keep both": "Biak mantendu", + "Verify Token": "Egiaztatu Tokena", + "Setup 2FA": "Ezarri 2FA", + "Enable 2FA": "Gaitu 2FA", + "Disable 2FA": "Desgaitu 2FA", + "2FA Settings": "2FA ezarpenak", + "Two Factor Authentication": "Bi aldetako autentifikazioa (2FA)", + "Active": "Aktibo", + "Inactive": "Inaktibo", + "Token": "Tokena", + "Show URI": "Erakutsi URIa", + "Tags": "Etiketak", + "Add New below or Select...": "Gehitu beste bat behean edo hautatu…", + "Tag with this name already exist.": "Izen hau duen etiketa dagoeneko badago.", + "Tag with this value already exist.": "Balio hau duen etiketa dagoeneko badago.", + "color": "Kolorea", + "value (optional)": "balioa (hautazkoa)", + "Gray": "Grisa", + "Red": "Gorria", + "Orange": "Laranja", + "Green": "Berdea", + "Blue": "Urdina", + "Indigo": "Indigo", + "Purple": "Morea", + "Pink": "Arrosa", + "Search...": "Bilatu…", + "Avg. Ping": "Batazbesteko Pinga", + "Avg. Response": "Batazbesteko erantzuna", + "Entry Page": "Sarrera orria", + "statusPageNothing": "Ezer ere ez hemen, mesedez gehitu taldea edo monitorizazioa.", + "No Services": "Zerbitzurik ez", + "All Systems Operational": "Sistema guztiak martxan", + "Partially Degraded Service": "Zerbitzu partzialki degradatua", + "Degraded Service": "Zerbitzu degradatua", + "Add Group": "Gehitu taldea", + "Add a monitor": "Gehitu monitorizazioa", + "Edit Status Page": "Editatu egoera orria", + "Go to Dashboard": "Joan arbelera", + "Status Page": "Egoera orria", + "Status Pages": "Egoera orriak", + "defaultNotificationName": "Nire {notification} Alerta ({number})", + "here": "hemen", + "Required": "Beharrezkoa", + "telegram": "Telegram", + "ZohoCliq": "ZohoCliq", + "Bot Token": "Bot Tokena", + "wayToGetTelegramToken": "You can get a token from {0}.", + "Chat ID": "Txat IDa", + "supportTelegramChatID": "Support Direct Chat / Group / Channel's Chat ID", + "wayToGetTelegramChatID": "You can get your chat ID by sending a message to the bot and going to this URL to view the chat_id:", + "YOUR BOT TOKEN HERE": "YOUR BOT TOKEN HERE", + "chatIDNotFound": "Chat ID is not found; please send a message to this bot first", + "webhook": "Webhook", + "Post URL": "Bidalketa URLa", + "Content Type": "Eduki mota", + "webhookJsonDesc": "{0} is good for any modern HTTP servers such as Express.js", + "webhookFormDataDesc": "{multipart} is good for PHP. The JSON will need to be parsed with {decodeFunction}", + "smtp": "Emaila (SMTP)", + "secureOptionNone": "Bat ere ez / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignore TLS Error", + "From Email": "Email honetatik", + "emailCustomSubject": "Pertsonalizatutako gaia", + "To Email": "Email honetara", + "smtpCC": "CC", + "smtpBCC": "BCC", + "discord": "Discord", + "Discord Webhook URL": "Discord Webhook URLa", + "wayToGetDiscordURL": "Hona joanda lortu dezakezu: Server Settings -> Integrations -> View Webhooks -> New Webhook", + "Bot Display Name": "Botaren erakusteko izena", + "Prefix Custom Message": "Prefix Custom Message", + "Hello @everyone is...": "Kaixo {'@'}edonor da…", + "teams": "Microsoft Teams", + "Webhook URL": "Webhook URLa", + "wayToGetTeamsURL": "You can learn how to create a webhook URL {0}.", + "wayToGetZohoCliqURL": "You can learn how to create a webhook URL {0}.", + "signal": "Signal", + "Number": "Zenbakia", + "Recipients": "Hartzaileak", + "needSignalAPI": "REST APIarekin signal bezeroa eduki behar duzu.", + "wayToCheckSignalURL": "You can check this URL to view how to set one up:", + "signalImportant": "IMPORTANT: You cannot mix groups and numbers in recipients!", + "gotify": "Gotify", + "Application Token": "Aplikazio tokena", + "Server URL": "Zerbitzari URLa", + "Priority": "Lehentasuna", + "slack": "Slack", + "Icon Emoji": "Emoji ikonoa", + "Channel Name": "Kanalaren izena", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "More info about Webhooks on: {0}", + "aboutChannelName": "Enter the channel name on {0} Channel Name field if you want to bypass the Webhook channel. Ex: #other-channel", + "aboutKumaURL": "If you leave the Uptime Kuma URL field blank, it will default to the Project GitHub page.", + "emojiCheatSheet": "Emoji cheat sheet: {0}", + "rocket.chat": "Rocket.Chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "Push by Techulus", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Support 50+ Notification services)", + "GoogleChat": "Google Chat (Google Workspace only)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "Erabiltzaile gakoa", + "Device": "Gailua", + "Message Title": "Mezuaren izenburua", + "Notification Sound": "Jakinarazpen soinua", + "More info on:": "More info on: {0}", + "pushoverDesc1": "Emergency priority (2) has default 30 second timeout between retries and will expire after 1 hour.", + "pushoverDesc2": "If you want to send notifications to different devices, fill out Device field.", + "SMS Type": "SMS mota", + "octopushTypePremium": "Premium (Fast - recommended for alerting)", + "octopushTypeLowCost": "Low Cost (Slow - sometimes blocked by operator)", + "checkPrice": "Check {0} prices:", + "apiCredentials": "API credentials", + "octopushLegacyHint": "Do you use the legacy version of Octopush (2011-2020) or the new version?", + "Check octopush prices": "Check octopush prices {0}.", + "octopushPhoneNumber": "Phone number (intl format, eg : +33612345678) ", + "octopushSMSSender": "SMS Sender Name : 3-11 alphanumeric characters and space (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Device ID", + "Apprise URL": "Apprise URL", + "Example:": "Adibidez: {0}", + "Read more:": "Irakurri gehiago: {0}", + "Status:": "Egoera: {0}", + "Read more": "Irakurri gehiago", + "appriseInstalled": "Apprise instalatuta.", + "appriseNotInstalled": "Apprise ez dago instalatuta. {0}", + "Access Token": "Access Tokena", + "Channel access token": "Kanalaren access tokena", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console - {0}", + "Basic Settings": "Oinarrizko ezarpenak", + "User ID": "Erabiltzaile ID", + "Messaging API": "Messaging API", + "wayToGetLineChannelToken": "First access the {0}, create a provider and channel (Messaging API), then you can get the channel access token and user ID from the above mentioned menu items.", + "Icon URL": "Ikono URL", + "aboutIconURL": "You can provide a link to a picture in \"Icon URL\" to override the default profile picture. Will not be used if Icon Emoji is set.", + "aboutMattermostChannelName": "You can override the default channel that the Webhook posts to by entering the channel name into \"Channel Name\" field. This needs to be enabled in the Mattermost Webhook settings. Ex: #other-channel", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - cheap but slow and often overloaded. Limited only to Polish recipients.", + "promosmsTypeFlash": "SMS FLASH - Message will automatically show on recipient device. Limited only to Polish recipients.", + "promosmsTypeFull": "SMS FULL - Premium tier of SMS, You can use your Sender Name (You need to register name first). Reliable for alerts.", + "promosmsTypeSpeed": "SMS SPEED - Highest priority in system. Very quick and reliable but costly (about twice of SMS FULL price).", + "promosmsPhoneNumber": "Phone number (for Polish recipient You can skip area codes)", + "promosmsSMSSender": "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu WebHookURL", + "matrixHomeserverURL": "Hasiera zerbitzari URL (with http(s):// and optionally port)", + "Internal Room Id": "Internal Room ID", + "matrixDesc1": "You can find the internal room ID by looking in the advanced section of the room settings in your Matrix client. It should look like !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "It is highly recommended you create a new user and do not use your own Matrix user's access token as it will allow full access to your account and all the rooms you joined. Instead, create a new user and only invite it to the room that you want to receive the notification in. You can get the access token by running {0}", + "Method": "Metodoa", + "Body": "Gorputza", + "Headers": "Goiburuak", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "The request headers are not valid JSON: ", + "BodyInvalidFormat": "The request body is not valid JSON: ", + "Monitor History": "Monitorizazio Historia", + "clearDataOlderThan": "Keep monitor history data for {0} days.", + "PasswordsDoNotMatch": "Pasahitzak ez datoz bat.", + "records": "records", + "One record": "One record", + "steamApiKeyDescription": "For monitoring a Steam Game Server you need a Steam Web-API key. You can register your API key here: ", + "Current User": "Uneko erabiltzailea", + "topic": "Gaia", + "topicExplanation": "MQTT topic to monitor", + "successMessage": "Arrakasta mezua", + "successMessageExplanation": "MQTT message that will be considered as success", + "recent": "Duela gutxikoa", + "Done": "Egina", + "Info": "Info", + "Security": "Segurtasuna", + "Steam API Key": "Steam API Giltza", + "Shrink Database": "Shrink Datubasea", + "Pick a RR-Type...": "Pick a RR-Type…", + "Pick Accepted Status Codes...": "Hautatu onartutako egoera kodeak…", + "Default": "Lehenetsia", + "HTTP Options": "HTTP Aukerak", + "Create Incident": "Sortu inzidentzia", + "Title": "Titulua", + "Content": "Edukia", + "Style": "Estiloa", + "info": "info", + "warning": "kontuz", + "danger": "arriskua", + "error": "errorea", + "critical": "kritikoa", + "primary": "oinarrizkoa", + "light": "argia", + "dark": "iluna", + "Post": "Post", + "Please input title and content": "Mesedez sartu titulua eta edukia", + "Created": "Sortuta", + "Last Updated": "Azken eguneratzea", + "Unpin": "Unpin", + "Switch to Light Theme": "Aldatu gai argira", + "Switch to Dark Theme": "Aldatu gai ilunera", + "Show Tags": "Erakutsi etiketak", + "Hide Tags": "Ezkutatu etiketak", + "Description": "Deskribapena", + "No monitors available.": "Monitorizaziorik eskuragarri ez.", + "Add one": "Gehitu bat", + "No Monitors": "Monitorizaziorik ez", + "Untitled Group": "Titulurik gabeko taldea", + "Services": "Zerbitzuak", + "Discard": "Baztertu", + "Cancel": "Ezeztatu", + "Powered by": "Honekin egina:", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API erabiltzailea (webapi_ aurre-hizkia barne)", + "serwersmsAPIPassword": "API pasahitza", + "serwersmsPhoneNumber": "Telefono zenbakia", + "serwersmsSenderName": "SMS bidaltzaile izena (registered via customer portal)", + "stackfield": "Stackfield", + "Customize": "Pertsonalizatu", + "Custom Footer": "Oin pertsonalizatua", + "Custom CSS": "CSS pertsonalizatua", + "smtpDkimSettings": "DKIM ezarpenak", + "smtpDkimDesc": "Please refer to the Nodemailer DKIM {0} for usage.", + "documentation": "dokumentazioa", + "smtpDkimDomain": "Domeinu izena", + "smtpDkimKeySelector": "Gako hautatzailea", + "smtpDkimPrivateKey": "Gako pribatua", + "smtpDkimHashAlgo": "Hash algoritmoa (hautazkoa)", + "smtpDkimheaderFieldNames": "Header Keys to sign (Optional)", + "smtpDkimskipFields": "Header Keys not to sign (Optional)", + "wayToGetPagerDutyKey": "You can get this by going to Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Here you can search for \"Events API V2\". More info {0}", + "Integration Key": "Integration Key", + "Integration URL": "Integrazio URLa", + "Auto resolve or acknowledged": "Auto resolve or acknowledged", + "do nothing": "ez egin ezer", + "auto acknowledged": "auto acknowledged", + "auto resolve": "auto resolve", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API Endpoint", + "alertaEnvironment": "Ingurunea", + "alertaApiKey": "API Key", + "alertaAlertState": "Alerta egoera", + "alertaRecoverState": "Berreskuratze egoera", + "deleteStatusPageMsg": "Ziur zaude egoera orri hau ezabatu nahi duzula?", + "Proxies": "Proxiak", + "default": "Lehenetsia", + "enabled": "Gaituta", + "setAsDefault": "Ezarri lehenetsitzat", + "deleteProxyMsg": "Are you sure want to delete this proxy for all monitors?", + "proxyDescription": "Proxies must be assigned to a monitor to function.", + "enableProxyDescription": "This proxy will not effect on monitor requests until it is activated. You can control temporarily disable the proxy from all monitors by activation status.", + "setAsDefaultProxyDescription": "This proxy will be enabled by default for new monitors. You can still disable the proxy separately for each monitor.", + "Certificate Chain": "Zertifikatu katea", + "Valid": "Baliozkoa", + "Invalid": "Baliogabea", + "AccessKeyId": "AccessKey ID", + "SecretAccessKey": "AccessKey Secret", + "PhoneNumbers": "TelefonoZenbakiak", + "TemplateCode": "TemplateCode", + "SignName": "SignName", + "Sms template must contain parameters: ": "Sms txantiloiak parametroak eduki behar ditu: ", + "Bark Endpoint": "Bark Endpoint", + "WebHookUrl": "WebHookUrl", + "SecretKey": "SecretKey", + "For safety, must use secret key": "For safety, must use secret key", + "Device Token": "Gailu tokena", + "Platform": "Plataforma", + "Huawei": "Huawei", + "High": "Altua", + "Retry": "Errepikatu", + "Topic": "Gaia", + "WeCom Bot Key": "WeCom Bot Key", + "Setup Proxy": "Ezarri Proxya", + "Proxy Protocol": "Proxy protokoloa", + "Proxy Server": "Proxy zerbitzaria", + "Proxy server has authentication": "Proxy zerbitzariak autentifikazioa dauka", + "User": "Erabiltzailea", + "Installed": "Instalatuta", + "Not installed": "Instalatu gabe", + "Running": "Martxan", + "Not running": "Ez martxan", + "Remove Token": "Ezabatu Tokena", + "Start": "Hasi", + "Stop": "Gelditu", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Gehitu egoera orri berria", + "Slug": "Sluga", + "Accept characters:": "Onartu karaktereak:", + "startOrEndWithOnly": "Start or end with {0} only", + "No consecutive dashes": "No consecutive dashes", + "Next": "Hurrengoa", + "The slug is already taken. Please choose another slug.": "Sluga dagoeneko hartuta dago. Mesedez beste bat hautatu.", + "No Proxy": "Proxyrik ez", + "Authentication": "Authentication", + "HTTP Basic Auth": "HTTP oinarrizko Auth", + "New Status Page": "Egoera orri berria", + "Page Not Found": "Orria ez da aurkitu", + "Reverse Proxy": "Alderantzizkako Proxya", + "Backup": "Backup", + "About": "Honi buruz", + "wayToGetCloudflaredURL": "(Download cloudflared from {0})", + "cloudflareWebsite": "Cloudflare webgunea", + "Message:": "Mezua:", + "Don't know how to get the token? Please read the guide:": "Don't know how to get the token? Please read the guide:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.", + "Other Software": "Beste softwarea", + "For example: nginx, Apache and Traefik.": "Adibidez: nginx, Apache and Traefik.", + "Please read": "Mesedez irakurri", + "Subject:": "Gaia:", + "Valid To:": "Balio-epea:", + "Days Remaining:": "Egun faltan:", + "Issuer:": "Issuer:", + "Fingerprint:": "Hatzmarka:", + "No status pages": "Egoera orririk ez", + "Domain Name Expiry Notification": "Domeinu izen iraungitze jakinarazpena", + "Proxy": "Proxya", + "Date Created": "Data sortuta", + "onebotHttpAddress": "OneBot HTTP helbidea", + "onebotMessageType": "OneBot mezu mota", + "onebotGroupMessage": "Taldea", + "onebotPrivateMessage": "Pribatua", + "onebotUserOrGroupId": "Talde/Erabiltzaile IDa", + "onebotSafetyTips": "For safety, must set access token", + "PushDeer Key": "PushDeer Key", + "Footer Text": "Oineko testua", + "Show Powered By": "Erakutsi Honekin egina:", + "Domain Names": "Domeinu izenak", + "signedInDisp": "{0} bezala logeatua", + "signedInDispDisabled": "Auth desgaituta.", + "Certificate Expiry Notification": "Zertifikatu iraungitze jakinarazpena", + "API Username": "API Erabiltzailea", + "API Key": "API Gakoa", + "Recipient Number": "Recipient Number", + "From Name/Number": "From Name/Number", + "Leave blank to use a shared sender number.": "Leave blank to use a shared sender number.", + "Octopush API Version": "Octopush API Version", + "Legacy Octopush-DM": "Legacy Octopush-DM", + "endpoint": "endpoint", + "octopushAPIKey": "\"API key\" from HTTP API credentials in control panel", + "octopushLogin": "\"Login\" from HTTP API credentials in control panel", + "promosmsLogin": "API Saio haste izena", + "promosmsPassword": "API Pasahitza", + "pushoversounds pushover": "Pushover (defektuz)", + "pushoversounds bike": "Bizikleta", + "pushoversounds bugle": "Bugle", + "pushoversounds cashregister": "Cash Register", + "pushoversounds classical": "Klasikoa", + "pushoversounds cosmic": "Kosmikoa", + "pushoversounds falling": "Erortzen", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Incoming", + "pushoversounds intermission": "Intermission", + "pushoversounds magic": "Magia", + "pushoversounds mechanical": "Mekanikoa", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Sirena", + "pushoversounds spacealarm": "Espazio Alarma", + "pushoversounds tugboat": "Tug Boat", + "pushoversounds alien": "Alien Alarm (long)", + "pushoversounds climb": "Climb (long)", + "pushoversounds persistent": "Persistent (long)", + "pushoversounds echo": "Pushover Echo (long)", + "pushoversounds updown": "Up Down (long)", + "pushoversounds vibrate": "Bibrazioa soilik", + "pushoversounds none": "Bat ere ez (isilik)", + "pushyAPIKey": "Secret API giltza", + "pushyToken": "Gailu tokena", + "Show update if available": "Erakutsi eguneratzea eskuragarri badago", + "Also check beta release": "Beta bertsioak ere egiaztatu", + "Using a Reverse Proxy?": "Proxy alderantzizkako zerbitzaria erabiltzen?", + "Check how to config it for WebSocket": "Check how to config it for WebSocket", + "Steam Game Server": "Steam joko zerbitzaria", + "Most likely causes:": "Arrazoi probableenak:", + "The resource is no longer available.": "Baliabidea ez dago erabilgarri.", + "There might be a typing error in the address.": "Idazketa-akats bat egon daiteke helbidean.", + "What you can try:": "Probatu dezakezuna:", + "Retype the address.": "Berridatzi helbidea.", + "Go back to the previous page.": "Itzuli aurreko orrialdera.", + "Coming Soon": "Laster", + "wayToGetClickSendSMSToken": "API erabiltzailea and API giltza hemendik lortu ditzakezu: {0} .", + "Connection String": "Konexio katea", + "Query": "Kontsulta", + "settingsCertificateExpiry": "TLS irungitze zertifikatua", + "certificationExpiryDescription": "HTTPS Monitorizazio jakinarazpena martxan jarri TLS zertifikatua iraungitzeko hau falta denean:", + "ntfy Topic": "ntfy Topic", + "Domain": "Domeinua", + "Workstation": "Lan gunea", + "disableCloudflaredNoAuthMsg": "Ez Auth moduan zaude, pasahitza ez da beharrezkoa.", + "maintenanceStatus-ended": "Bukatuta", + "maintenanceStatus-unknown": "Ezezaguna", + "Enable": "Gaitu", + "Strategy": "Estrategia", + "General Monitor Type": "Monitorizazio mota orokorra", + "Select status pages...": "Hautatu egoera orriak…", + "Server Address": "Zerbitzari helbidea", + "Learn More": "Ikasi gehiago", + "weekdayShortTue": "Ast", + "weekdayShortWed": "Asz", + "Disable": "Desgaitu", + "warningTimezone": "Zerbitzariaren orduzona erabiltzen ari da", + "weekdayShortThu": "Og", + "weekdayShortMon": "Asl", + "Base URL": "Oinarri URLa", + "high": "altua", + "Economy": "Ekonomia", + "Help": "Laguntza", + "Game": "Jokoa", + "statusMaintenance": "Mantenuan", + "Maintenance": "Mantenua", + "Passive Monitor Type": "Monitorizazio mota pasiboa", + "Specific Monitor Type": "Zehaztutako monitorizazio mota", + "markdownSupported": "Markdown sintaxia onartzen du", + "Monitor": "Monitorizazio | Monitorizazioak", + "resendDisabled": "Berbidaltzea desgaituta", + "weekdayShortFri": "Ost", + "weekdayShortSat": "Lar", + "weekdayShortSun": "Iga", + "dayOfWeek": "Asteko eguna", + "dayOfMonth": "Hilabeteko eguna", + "lastDay": "Azken eguna", + "lastDay1": "Hilabeteko azken eguna", + "Resend Notification if Down X times consecutively": "Bidali jakinarazpena X aldiz jarraian erortzen bada", + "Add New Tag": "Gehitu etiketa berria", + "Schedule maintenance": "Programatu mantenua", + "Start of maintenance": "Mantenuaren hasiera", + "All Status Pages": "Egoera orrialde guztiak", + "Custom": "Pertsonalizatua", + "showCertificateExpiry": "Erakutsi ziurtagiriaren iraungitzea", + "Close": "Itxi", + "Request Body": "Eskaera gorputza", + "Mechanism": "Mekanismoa", + "Home": "Hasiera", + "filterActive": "Aktibo", + "filterActivePaused": "Pausatua", + "Expected Value": "Esperotako balioa", + "statusPageRefreshIn": "{0} barru freskatuko da.", + "now": "orain", + "time ago": "duela {0}", + "-year": "-urte", + "styleElapsedTimeShowNoLine": "Erakutsi (Lerrorik ez)", + "styleElapsedTimeShowWithLine": "Erakutsi (Lerroarekin)", + "Select": "Hautatu", + "Docker Container": "Docker edukiontzia", + "Container Name / ID": "Edukiontzi izena / ID", + "Packet Size": "Pakete tamaina", + "telegramSendSilently": "Bidali isilik", + "Examples": "Adibideak", + "default: notify all devices": "Defektuz: jakinarazi gailu guztiak", + "Event type:": "Ekintza mota:", + "Event data:": "Ekintza mota:", + "Optional": "Hautazkoa", + "and": "eta", + "sameAsServerTimezone": "Zerbitzariaren ordu-zona bera", + "endDateTime": "Bukaera data/ordua", + "cronExpression": "Cron espresioa", + "cronSchedule": "Programatu: ", + "invalidCronExpression": "Cron espresio baliogabea: {0}", + "recurringInterval": "Tartea", + "No Maintenance": "Mantenurik ez", + "maintenanceStatus-under-maintenance": "Mantenuan", + "maintenanceStatus-inactive": "Aktibatu gabe", + "maintenanceStatus-scheduled": "Programatuta", + "Display Timezone": "Erakutsi ordu zona", + "statusPageMaintenanceEndDate": "Bukatu", + "IconUrl": "Ikono URLa", + "chromeExecutableAutoDetect": "Auto detektatu", + "Schedule Maintenance": "Programatu mantenua", + "Edit Maintenance": "Editatu mantenua", + "Date and Time": "Data eta ordua", + "plugin": "Plugin | Pluginak", + "installing": "Instalatzen", + "uninstalling": "Desinstalatzen", + "confirmUninstallPlugin": "Ziur zaude plugin hau desinstalatu nahi duzula?", + "Clone": "Klonatu", + "cloneOf": "{0}-(a)ren klona", + "emailTemplateStatus": "Egoera", + "emailTemplateMsg": "jakinarazpenaren mezua", + "Select message type": "Hautatu mezu mota", + "Send to channel": "Bidali kanalera", + "Enable TLS": "Gaitu TLS", + "webhookAdditionalHeadersTitle": "Goiburu gehigarriak", + "Reset Token": "Berrezarri tokena", + "selectedMonitorCount": "Hautatuta: {0}", + "HTTP Headers": "HTTP goiburuak", + "Refresh Interval": "Eguneratze tartea", + "tcp": "TCP / HTTP", + "Notification Service": "Jakinarazpen zerbitzua", + "or": "edo", + "startDateTime": "Hasiera data/ordua", + "pauseMaintenanceMsg": "Ziur zaude gelditu nahi duzula?", + "install": "Instalatu", + "uninstall": "Desinstalatu", + "emailTemplateServiceName": "Zerbitzu izena", + "Your User ID": "Zure erabiltzaile IDa", + "Connection Type": "Konexio mota", + "pushOthers": "Besteak", + "programmingLanguages": "Programatze lengoaiak", + "dbName": "Datubase izena", + "resendEveryXTimes": "Berbidali {0} aldiz", + "Reconnecting...": "Birkonektatzen...", + "setupDatabaseChooseDatabase": "Zein datubase erabili nahi duzu?", + "Recurring": "Errepikakorra", + "Mentioning": "Aipatzen", + "Condition": "Baldintza", + "RabbitMQ Username": "RabbitMQ erabiltzailea", + "Expiry": "Iraungitzea", + "pagertreeSilent": "Isildu", + "Group": "Taldea", + "Saved.": "Gordeta.", + "Conditions": "Baldintzak", + "smseagleTo": "Telefono zenbakia(k)", + "Enable Kafka SSL": "Gaitu Kafka SSL", + "Continue": "Jarraitu", + "apiKey-active": "Aktibo", + "apiKey-expired": "Iraungita", + "apiKey-inactive": "Ez-aktibo", + "Expires": "Iraungitzen da", + "Generate": "Sortu", + "pagertreeUrgency": "Larritasuna", + "pagertreeLow": "Baxua", + "pagertreeHigh": "Altua", + "pagertreeCritical": "Larria", + "Destination": "Helburua", + "conditionValuePlaceholder": "Balioa", + "RabbitMQ Password": "RabbitMQ pasahitza", + "Correct": "Zuzena", + "Fail": "Akatsa", + "Elevator": "Igogailua", + "Guitar": "Gitarra", + "pagertreeMedium": "Ertaina", + "Host URL": "Host URLa", + "Affected Monitors": "Kaltetutako monitoreak", + "Pick Affected Monitors...": "Hautatu kaltetutako monitoreak…", + "deleteMaintenanceMsg": "Ziur zaude mantentze lan hau ezabatu nahi duzula?", + "smseagleEncoding": "Unicode gisa bidali (default=GSM-7)", + "Add Another": "Gehitu beste bat", + "Google Analytics ID": "Google Analytics IDa", + "Edit Tag": "Editatu etiketa", + "PushDeer Server": "PushDeer zerbitzaria", + "Show Clickable Link": "Erakutsi lotura klikagarria", + "twilioToNumber": "zenbakira", + "twilioFromNumber": "zenbakitik", + "ntfyUsernameAndPassword": "Erabiltzailea eta pasahitza", + "lunaseaUserID": "Erabiltzaile IDa", + "pagertreeDoNothing": "Ez egin ezer", + "Separate multiple email addresses with commas": "Banatu email helbideak koma ikurrekin", + "settingUpDatabaseMSG": "Datubasea ezartzen. Denbora pixka bat iraun dezake, pazientzia eduki.", + "pause": "Gelditu", + "Ip Family": "IP familia", + "Clear Form": "Garbitu formularioa", + "Manual": "Eskuzkoa", + "templateMsg": "Jakinarazpenaren mezua", + "infiniteRetention": "Ezarri 0 atxikipen infinitua lortzeko.", + "strategyManual": "Aktibatu/desaktibatu eskuz", + "Home Assistant URL": "Home Assistant URLa", + "lastDay4": "Hilabeteko azken 4. eguna", + "smseagleApiType": "API bertsioa", + "defaultFriendlyName": "Monitorizazio berria", + "threemaRecipientTypeIdentityFormat": "8 karaktere", + "threemaRecipientTypePhone": "Telefono zenbakia", + "Group ID": "Talde IDa", + "Custom URL": "URL pertsonalizatua", + "Plain Text": "Testu laua", + "telegramServerUrl": "(Hautazkoa) Zerbitzari Url-a", + "Notify Channel": "Jakinarazpen kanala", + "Add Tags": "Gehitu etiketak", + "Search monitored sites": "Bilatu monitorizatutako guneak", + "lastDay2": "Hilabeteko azken 2. eguna", + "lastDay3": "Hilabeteko azken 3. eguna", + "Server Timezone": "Zerbitzari ordu zona", + "chromeExecutable": "Chrome/Chromium exekutagarria", + "Clone Monitor": "Klonatu monitorizazioa", + "Add a domain": "Gehitu domeinu bat", + "smseagleDuration": "Iraupena (segundotan)", + "smseagleMsgType": "Mezu mota", + "smseagleMsgSms": "Sms mezua (defektuzkoa)", + "ntfyAuthenticationMethod": "Autentifikazio metodoa", + "lunaseaDeviceID": "Gailu IDa", + "threemaRecipientTypeEmail": "Email helbidea", + "New Group": "Talde berria", + "Group Name": "Talde izena", + "OAuth2: Client Credentials": "OAuth2: bezero kredentzialak", + "Authentication Method": "Autentifikazio metodoa", + "Client ID": "Bezero IDa", + "Client Secret": "Bezero gakoa", + "Go back to home page.": "Itzuli hasiera ordura.", + "No tags found.": "Ez da etiketarik aurkitu.", + "Disable URL in Notification": "Desgaitu URLa jakunarazpenean", + "Sender name": "Bidaltzaile izena", + "Phone numbers": "Telefono zenbakiak", + "Message Template": "Mezu txantiloia", + "Template Format": "Txantiloi formatua", + "Add Another Tag": "Gehitu beste etiketa", + "templateServiceName": "Zerbitzu izena", + "templateStatus": "egoera", + "webhookBodyCustomOption": "Pertsonalizatutako mezu gorputza", + "telegramSendSilentlyDescription": "Bidali mezuak isilik. Erabiltzaileak soinurik gabeko jakinarazpena jasoko du.", + "setupDatabaseMariaDB": "Konektatu kanpo MariaDB datu base batekin. Datu base konexioaren informazioa ezarri behar da.", + "Json Query Expression": "Json Query espresioa", + "ignoredTLSError": "TLS/SSL erroreei ezikusiarena eginda" +} diff --git a/src/lang/fa.json b/src/lang/fa.json new file mode 100644 index 0000000..bb172aa --- /dev/null +++ b/src/lang/fa.json @@ -0,0 +1,1300 @@ +{ + "languageName": "فارسی", + "checkEverySecond": "بررسی هر {0} ثانیه", + "retryCheckEverySecond": "تکرار مجدد هر {0} ثانیه", + "retriesDescription": "بیشینهٔ تلاش‌های مجدد پیش از آنکه خدمت، از کار افتاده نشان‌دار شود و اعلانی ارسال گردد.", + "ignoreTLSError": "بی‌خیال ارور TLS/SSL برای سایت‌های HTTPS", + "upsideDownModeDescription": "نتیجه وضعیت را برعکس کن، اگر خدمت در دسترس بود فرض کن که پایین است.", + "maxRedirectDescription": "حداکثر تعداد تغییرمسیرهایی که باید دنبال شوند. برای غیرفعال کردن تغییرمسیرها، مقدار آن را روی 0 تنظیم کنید.", + "acceptedStatusCodesDescription": "لطفا HTTP Status Code هایی که میخواهید به عنوان پاسخ موفقیت آمیز در نظر گرفته شود را انتخاب کنید.", + "passwordNotMatchMsg": "تکرار گذرواژه مطابقت ندارد.", + "notificationDescription": "اعلان‌ها برای عملکرد صحیح باید به یک پایشگر اختصاص داده شوند.", + "keywordDescription": "در نتیجه درخواست (اهمیتی ندارد پاسخ JSON است یا HTML) بدنبال این کلمه بگرد (حساس به کوچک/بزرگ بودن حروف).", + "pauseDashboardHome": "مکث", + "deleteMonitorMsg": "آیا از برداشتن این پایشگر مطمئن هستید؟", + "deleteNotificationMsg": "آیا مطمئن هستید که می‌خواهید این اطلاعیه را برای همهٔ پایشگرها بردارید؟", + "resolverserverDescription": "کلادفلر کارساز پیش‌فرض است. شما می‌توانید فهرستی از نشانی‌های آی‌پی یا نام‌های میزبان (hostname) را که با , از هم جدا شده‌اند، مشخص کنید.", + "rrtypeDescription": "لطفا نوع Resource Record را انتخاب کنید", + "pauseMonitorMsg": "آیا مطمئن هستید که میخواهید این مانیتور را متوقف کنید ؟", + "enableDefaultNotificationDescription": "این اعلان به‌طور پیش‌فرض برای پایشگرهای جدید فعال خواهد بود. با این حال، همچنان می‌توانید این اعلان را به‌صورت جداگانه برای هر پایشگر غیرفعال کنید.", + "clearEventsMsg": "آیا از اینکه تمامی تاریخچهٔ رویداد‌های این پایشگر برداشته شود مطمئن هستید؟", + "clearHeartbeatsMsg": "آیا از اینکه تاریخچه تمامی ضربان قلب های این مانیتور حذف شود مطمئن هستید؟", + "confirmClearStatisticsMsg": "آیا از حذف تمامی آمار و ارقام مطمئن هستید؟", + "importHandleDescription": "اگر که میخواهید بیخیال مانیتورها و یا سرویس‌های اطلاع‌رسانی که با نام مشابه از قبل موجود هستند شوید، گزینه 'بی‌خیال موارد ..' را انتخاب کنید. توجه کنید که گزینه 'بازنویسی' تمامی موارد موجود با نام مشابه را از بین خواهد برد.", + "confirmImportMsg": "آیا از بازگردانی بک آپ مطمئن هستید؟ لطفا از اینکه نوع بازگردانی درستی را انتخاب کرده‌اید اطمینان حاصل کنید.", + "twoFAVerifyLabel": "لطفا جهت اطمینان از عملکرد احراز هویت دو مرحله‌ای توکن خود را وارد کنید:", + "tokenValidSettingsMsg": "توکن شما معتبر است، هم اکنون میتوانید احراز هویت دو مرحله‌ای را فعال کنید.", + "confirmEnableTwoFAMsg": "آیا از فعال سازی احراز هویت دو مرحله‌ای مطمئن هستید؟", + "confirmDisableTwoFAMsg": "آیا از غیرفعال سازی احراز هویت دومرحله‌ای مطمئن هستید؟", + "Settings": "تنظیمات", + "Dashboard": "پیشخوان", + "New Update": "به‌روز‌رسانی جدید", + "Language": "زبان", + "Appearance": "ظاهر", + "Theme": "زمینه", + "General": "عمومی", + "Version": "نسخه", + "Check Update On GitHub": "بررسی به‌روز‌رسانی بر روی گیت‌هاب", + "List": "سیاهه", + "Add": "افزودن", + "Add New Monitor": "افزودن پایشگر جدید", + "Quick Stats": "آمار اجمالی", + "Up": "فعال", + "Down": "غیرفعال", + "Pending": "در انتظار تایید", + "Unknown": "نامشخص", + "Pause": "توقف", + "Name": "نام", + "Status": "وضعیت", + "DateTime": "تاریخ و زمان", + "Message": "پیام", + "No important events": "رخداد جدیدی موجود نیست", + "Resume": "ادامه", + "Edit": "ویرایش", + "Delete": "حذف", + "Current": "فعلی", + "Uptime": "زمان در دسترس‌بودن", + "Cert Exp.": "انقضای گواهی‌نامه", + "day": "روز", + "-day": "-روز", + "hour": "ساعت", + "-hour": "-ساعت", + "Response": "پاسخ", + "Ping": "پینگ", + "Monitor Type": "نوع پایشگر", + "Keyword": "کلمه کلیدی", + "Friendly Name": "عنوان", + "URL": "نشانی", + "Hostname": "نام میزبان", + "Port": "درگاه", + "Heartbeat Interval": "فاصله هر Heartbeat", + "Retries": "تلاش مجدد", + "Heartbeat Retry Interval": "فاصله تلاش مجدد برایHeartbeat", + "Advanced": "پیشرفته", + "Upside Down Mode": "حالت بر عکس", + "Max. Redirects": "حداکثر تعداد ری‌دایرکت", + "Accepted Status Codes": "کدهای وضعیت پذیرفته‌شده", + "Save": "ذخیره", + "Notifications": "اطلاع‌رسانی‌ها", + "Not available, please setup.": "هیچ موردی موجود نیست، اولین مورد را راه‌اندازی کنید.", + "Setup Notification": "راه اندازی اطلاع‌رسانی‌", + "Light": "روشن", + "Dark": "تاریک", + "Auto": "اتوماتیک", + "Theme - Heartbeat Bar": "ظاهر نوار Heartbeat", + "Normal": "معمولی", + "Bottom": "پایین", + "None": "هیچ کدام", + "Timezone": "موقعیت زمانی", + "Search Engine Visibility": "قابلیت دسترسی برای موتورهای جستجو", + "Allow indexing": "اجازه ایندکس شدن در موتور های جستجو را بده", + "Discourage search engines from indexing site": "به موتورهای جستجو اجازه ایندکس کردن این سامانه را نده", + "Change Password": "تغییر گذرواژه", + "Current Password": "گذرواژه فعلی", + "New Password": "گذرواژهٔ جدید", + "Repeat New Password": "تکرار گذرواژهٔ جدید", + "Update Password": "به‌روز‌رسانی گذرواژه", + "Disable Auth": "غیر فعال سازی تایید هویت", + "Enable Auth": "فعال سازی تایید هویت", + "disableauth.message1": "آیا مطمئن هستید که میخواهید {disableAuth}?", + "disable authentication": "احراز هویت را غیر فعال کنید", + "disableauth.message2": "این ویژگی برای کسانی است که {intendThirdPartyAuth}، مانند Cloudflare Access.", + "where you intend to implement third-party authentication": "لایه امنیتی شخص ثالث دیگر بر روی این آدرس فعال کرده‌اند", + "Please use this option carefully!": "لطفا از این امکان با دقت استفاده کنید!", + "Logout": "خروج", + "Leave": "منصرف شدم", + "I understand, please disable": "متوجه هستم، غیرفعال کن", + "Confirm": "تایید", + "Yes": "بلی", + "No": "خیر", + "Username": "نام‌کاربری", + "Password": "گذرواژه", + "Remember me": "به خاطر بسپار", + "Login": "ورود", + "No Monitors, please": "هیچ پایشگری موجود نیست، لطفاً", + "add one": "یک مورد اضافه کنید", + "Notification Type": "نوع اطلاع‌رسانی", + "Email": "ایمیل", + "Test": "تست", + "Certificate Info": "اطلاعات سرتیفیکت", + "Resolver Server": "سرور Resolver", + "Resource Record Type": "نوع رکورد (Resource Record Type)", + "Last Result": "آخرین نتیجه", + "Create your admin account": "ایجاد حساب کاربری مدیر", + "Repeat Password": "تکرار گذرواژه", + "Import Backup": "بازگردانی فایل پشتیبان", + "Export Backup": "ذخیره فایل پشتیبان", + "Export": "استخراج اطلاعات", + "Import": "ورود اطلاعات", + "respTime": "زمان پاسخگویی (میلی‌ثانیه)", + "notAvailableShort": "ناموجود", + "Default enabled": "به صورت پیش‌فرض فعال باشد", + "Apply on all existing monitors": "بر روی تمامی پایشگرهای فعلی اعمال شود", + "Create": "ایجاد", + "Clear Data": "پاکسازی داده‌ها", + "Events": "رخداد‌ها", + "Heartbeats": "ضربان قلب", + "Auto Get": "Auto Get", + "backupDescription": "شما می‌توانید تمامی پایشگرها و تنظیمات آگاهی‌ها را در قالب یک فایل JSON دریافت کنید.", + "backupDescription2": "البته تاریخچه رخدادها دراین فایل قرار نخواهند داشت.", + "backupDescription3": "توجه داشته باشید که تمامی اطلاعات حساس شما مانند توکن‌ها نیز در این فایل وجود خواهد داشت ، پس از این فایل به خوبی مراقبت کنید.", + "alertNoFile": "لطفا یک فایل برای «ورود اطلاعات» انتخاب کنید..", + "alertWrongFileType": "یک فایل JSON انتخاب کنید.", + "Clear all statistics": "پاکسازی تمامی آمار و ارقام", + "Skip existing": "بی‌خیال مواردی که از قبل موجود است", + "Overwrite": "بازنویسی", + "Options": "تنظیمات", + "Keep both": "هر دو را نگه‌ دار", + "Verify Token": "تایید توکن", + "Setup 2FA": "تنظیمات احراز دو مرحله‌ای", + "Enable 2FA": "فعال سازی احراز 2 مرحله‌ای", + "Disable 2FA": "غیر فعال کردن احراز 2 مرحله‌ای", + "2FA Settings": "تنظیمات احراز 2 مرحله‌ای", + "Two Factor Authentication": "احراز هویت دومرحله‌ای", + "Active": "فعال", + "Inactive": "غیرفعال", + "Token": "توکن", + "Show URI": "نمایش آدرس (URI)", + "Tags": "برچسب‌ها", + "Add New below or Select...": "یک مورد جدید اضافه کنید و یا از لیست انتخاب کنید…", + "Tag with this name already exist.": "یک برچسب با این «نام» از قبل وجود دارد.", + "Tag with this value already exist.": "یک برچسب با این «مقدار» از قبل وجود دارد.", + "color": "رنگ", + "value (optional)": "مقدار (اختیاری)", + "Gray": "خاکستری", + "Red": "قرمز", + "Orange": "نارنجی", + "Green": "سبز", + "Blue": "آبی", + "Indigo": "نیلی", + "Purple": "بنفش", + "Pink": "صورتی", + "Search...": "جستجو …", + "Avg. Ping": "متوسط پینگ", + "Avg. Response": "متوسط زمان پاسخ", + "Entry Page": "صفحه ورودی", + "statusPageNothing": "چیزی اینجا نیست، لطفا یک گروه و یا یک مانیتور اضافه کنید.", + "No Services": "بدون خدمات", + "All Systems Operational": "همهٔ سامانه‌ها فعال هستند", + "Partially Degraded Service": "افت نسبی کیفیت خدمت", + "Degraded Service": "افت کیفیت خدمت", + "Add Group": "اضافه کردن گروه", + "Add a monitor": "افزودن پایشگر", + "Edit Status Page": "ویرایش صفحه وضعیت", + "Status Page": "صفحه وضعیت", + "Status Pages": "صفحه وضعیت", + "Go to Dashboard": "رفتن به پیشخوان", + "Uptime Kuma": "پایشگر کوما", + "records": "مورد", + "One record": "یک مورد", + "Info": "اطلاعات", + "Powered by": "نیرو گرفته از", + "apprise": "Apprise (پشتیبانی از 50+ خدمات اعلان)", + "Monitor": "مانیتور | مانتیور ها", + "Help": "کمک", + "Game": "بازی", + "Primary Base URL": "نشانی پایهٔ اصلی", + "Passive Monitor Type": "پایش غیرفعال", + "Specific Monitor Type": "پایش اختصاصی", + "statusMaintenance": "در دست تعمیر", + "Maintenance": "در حال تعمیر", + "General Monitor Type": "پایش عمومی", + "markdownSupported": "شیوه نگارشی مارک‌داون پشتیبانی می‌شود", + "Body Encoding": "انکودینگ محتوا", + "twilioFromNumber": "از شماره", + "twilioToNumber": "به شماره", + "Resend Notification if Down X times consecutively": "اگر X بار متوالی غیرفعال بود، مجددا اطلاع بده", + "successMessageExplanation": "پیام MQTT موفقیت آمیز به نظر نمیرسد", + "Create Incident": "یک حادثه را اطلاع دهید", + "Switch to Light Theme": "تغییر به حالت روشن", + "No monitors available.": "هیچ پایشگری در دسترس نیست.", + "deleteProxyMsg": "آیا مطمئن هستید که می‌خواهید پیشکار را برای همهٔ پایشگرها غیرفعال کنید؟", + "enableProxyDescription": "این پروکسی تا زمانی که فعال نشود روی درخواست های مانیتور اثری نخواهد داشت. می‌توانید با توجه به وضعیت فعال‌سازی، پروکسی را از همه مانیتورها به طور موقت غیرفعال کنید.", + "supportTelegramChatID": "پشتیبانی از چت مستقیم / گروه / کانال", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "توکن دسترسی طولانی مدت (Long-Lived Access Token) را می توان با کلیک بر روی نام پروفایل خود (پایین سمت چپ) و اسکرول کردن به پایین و سپس روی Create Token ایجاد کرد. ", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "فهرستی از سرویس‌های اعلان را می‌توانید در هوم اسیستنت در قسمت «ابزارهای برنامه‌نویس > خدمات» برای «اعلان» جستجو کنید تا نام دستگاه/تلفن خود را پیدا کنید.", + "lastDay4": "چهارمین روز آخر ماه", + "dnsCacheDescription": "ممکن است در برخی از محیط های IPv6 کار نکند، اگر با مشکلی مواجه شدید آن را غیرفعال کنید.", + "Maintenance Time Window of a Day": "صفحه نگه داری در روز", + "Messaging API": "API پیام (Messaging API)", + "wayToGetLineChannelToken": "ابتدا به {0} دسترسی پیدا کنید، یک ارائه دهنده و کانال ایجاد کنید (API پیام)، سپس می توانید رمز توکن کانال و آیدی کاربری را از آیتم های منوی ذکر شده در بالا دریافت کنید.", + "aboutMattermostChannelName": "می‌توانید با وارد کردن نام کانال در قسمت «نام کانال»، کانال پیش‌فرضی را که وب هوک به آن پست می‌کند لغو کنید. این باید در تنظیمات Mattermost Webhook فعال شود. مثال: #other-channel", + "dnsPortDescription": "درگاه کارساز ساناد. پیش فرض ۵۳. می‌توانید این عدد را در هر زمانی عوض کنید.", + "affectedStatusPages": "نمایش این پیام تعمیر و نگه داری در صفحات استاتوس انتخاب شده", + "octopushSMSSender": "نام فرستنده پیامک: 3-11 الفبای انگلیسی، حروف و فاصله (a-zA-Z0-9)", + "Lowcost": "کم هزینه", + "You can divide numbers with": "می توانید اعداد را با آن تقسیم کنید", + "goAlertInfo": "GoAlert یک برنامه اوپن سورس برای زمان‌بندی تماس، افزایش خودکار و اعلان‌ها (مانند پیامک یا تماس‌های صوتی) است. به طور خودکار شخص مناسب، راه درست و در زمان مناسب را درگیر کنید! {0}", + "API Keys": "کلید های API", + "Expiry": "انقضا", + "Expiry date": "انقضا در تاریخ", + "Don't expire": "بدون انقضا (منقضی نمی شود)", + "For safety, must use secret key": "برای امنیت، میببایستی از SecretKey استفاده کنید", + "promosmsTypeFlash": "SMS FLASH - پیام به طور خودکار در دستگاه گیرنده نشان داده می شود. فقط به گیرندگان لهستانی محدود می شود.", + "promosmsTypeFull": "SMS FULL - پیامک پریموم، می توانید از نام فرستنده خود استفاده کنید (ابتدا باید نام خود را ثبت کنید). قابل اعتماد برای هشدار.", + "matrixHomeserverURL": "URL هوم سرور (با http(s):// و پورت اختیاری)", + "matrixDesc1": "با مراجعه به بخش پیشرفته تنظیمات اتاق در کلاینت Matrix خود می توانید آیدی داخلی اتاق را بیابید. باید شبیه \"!QMdRCpUIfLwsfjxye6:home.server\" باشد.", + "wayToGetPagerDutyKey": "با رفتن به Service -> Service Directory -> (Select a Service) -> Integrations -> Add integration می توانید این مورد را دریافت کنید. در اینجا می توانید \"Events API V2\" را جستجو کنید. اطلاعات بیشتر در {0}", + "smseagleRecipientType": "نوع گیرنده", + "smseagleEncoding": "ارسال به صورت یونیکد‌ (پیشفرض=GSM-7)", + "Leave blank to use a shared sender number.": "برای استفاده از شماره فرستنده مشترک، آن را خالی بگذارید.", + "onebotSafetyTips": "برای امنیت، میبایستی توکن دسترسی اضافه کنید", + "Custom Monitor Type": "نوع پایشگر سفارشی", + "apiKeyAddedMsg": "کلید API شما اضافه شده است. لطفاً آن را یادداشت کنید زیرا دیگر نمایش داده نخواهد شد.", + "deleteAPIKeyMsg": "آیا مطمئن هستید که می خواهید این کلید API را غیرفعال کنید؟", + "twilioAccountSID": "SID حساب", + "twilioAuthToken": "توکن اعتبارسنجی / کلید مخفی API", + "appriseNotInstalled": "Apprise نصب نشده است. {0}", + "trustProxyDescription": "سرآیندهای `X-Forwarded-*` را معتبر بدانید. اگر می‌خواهید نشانی IP درست کارخواه را دریافت کنید و پایشگرکوما پشت پیشکاری مانند Nginx یا Apache قرار دارد، این گزینه را فعال کنید.", + "matrixDesc2": "اکیداً توصیه می‌شود که یک کاربر جدید ایجاد کنید و از رمز دسترسی کاربر Matrix خود استفاده نکنید زیرا امکان دسترسی کامل به حساب شما و تمام اتاق‌هایی را که به آنها ملحق شده‌اید می‌دهد. در عوض، یک کاربر جدید ایجاد کنید و فقط او را به اتاقی دعوت کنید که می‌خواهید اعلان را دریافت کنید. می‌توانید با اجرای {0} توکن دسترسی را دریافت کنید", + "Certificate Chain": "زنجیره گواهی (Certificate Chain)", + "telegramMessageThreadID": "(اختیاری) آیدی Thread پیام", + "telegramMessageThreadIDDescription": "(اختیاری) شناسه منحصر به فرد برای موضوع پیام هدف در انجمن. فقط برای سوپر گروه های انجمن", + "Channel Name": "نام کانال", + "auto acknowledged": "تصدیق خودکار", + "needPushEvery": "باید هر {0} ثانیه یک‌بار این نشانی اینترنتی را فراخوانی کنید.", + "pushOptionalParams": "پارامترهای اختیاری: {0}", + "Affected Monitors": "پایشگرهای تحت تأثیر", + "Pick Affected Monitors...": "پایشگرهای تحت‌تأثیر را انتخاب کنید…", + "Start of maintenance": "زمان شروع نگهداری", + "All Status Pages": "همه صفحات مشاهده وضعیت", + "Select status pages...": "انتخاب صفحه مشاهده وضعیت…", + "here": "اینجا", + "Required": "اجباری", + "Post URL": "URL بعدی", + "defaultNotificationName": "هشدار {notification} در ({number})", + "Add one": "اضافه کردن", + "Page Not Found": "صفحه درخواستی پیدا نشد", + "Reverse Proxy": "پیشکار معکوس", + "Backup": "پشتیبان گیری", + "API Key": "کلید API", + "Show update if available": "نمایش بروز رسانی اگر موجود بود", + "Check how to config it for WebSocket": "بررسی چگونگی پیکربندی برای وب سوکت", + "Steam Game Server": "سرور گیم استیم", + "Most likely causes:": "به احتمال زیاد بخاطر:", + "The resource is no longer available.": "منبع دیگر در دسترس نیست.", + "Docker Container": "کانتینر داکر", + "Container Name / ID": "نام / آیدی کانتینر", + "Docker Host": "هاست داکر", + "Docker Hosts": "هاست های داکر", + "Domain": "دامنه", + "Clone Monitor": "تکثیر", + "Clone": "تکثیر", + "cloneOf": "تکثیر {0}", + "Prefix Custom Message": "پیشوند پیام سفارشی", + "enableGRPCTls": "امکان ارسال درخواست gRPC با اتصال TLS", + "pushoversounds classical": "کلاسیک", + "smtpDkimSettings": "تنظیمات DKIM", + "aboutChannelName": "اگر می‌خواهید کانال وب هوک را دور بزنید، نام کانال را در قسمت {0} نام کانال وارد کنید. مثال: #other-channel", + "aboutKumaURL": "در صورت خالی‌ماندنِ قسمت نشانی مربوط به پایشگر کوما، صفحهٔ گیت‌هاب پروژه به‌عنوان مقدار پیش‌فرض در نظر گرفته می‌شود.", + "smtpDkimDesc": "لطفاً برای استفاده به Nodemailer DKIM {0} مراجعه کنید.", + "alertaApiEndpoint": "اند پوینت API", + "serwersmsAPIUser": "نام کاربری API (شامل پیشوند webapi_)", + "serwersmsAPIPassword": "رمز عبور API", + "serwersmsPhoneNumber": "شماره موبایل", + "serwersmsSenderName": "نام فرستنده پیامک (ثبت شده از طریق پورتال مشتری)", + "alertaRecoverState": "حالت ریکاور (Recover State)", + "smseagleToken": "توکن دسترسی API", + "Google Analytics ID": "آیدی گوگل آنالیتیکس", + "pagertreeLow": "کم", + "pagertreeMedium": "متوسط", + "pagertreeHigh": "زیاد", + "pagertreeCritical": "حساس - خیلی مهم", + "pagertreeIntegrationUrl": "URL یکپارچه سازی", + "pagertreeUrgency": "اهمیت", + "pagertreeSilent": "بی صدا", + "pagertreeResolve": "Resolve اتوماتیک", + "pagertreeDoNothing": "هیچ کاری نکن", + "wayToGetPagerTreeIntegrationURL": "پس از ایجاد یکپارچه‌سازی پایشگر کوما در پیرتیوب، قطهٔ پایانی را رونوشت کنید. توضیحات کامل را در {0} ببینید.", + "telegramProtectContent": "محافظت از ارسال/ذخیره", + "telegramProtectContentDescription": "در صورت فعال بودن، پیام‌های ربات در تلگرام از ارسال و ذخیره محافظت می‌شوند.", + "wayToGetTelegramChatID": "برای مشاهده chat_id می توانید شناسه چت خود را با ارسال یک پیام به ربات و رفتن به این URL دریافت کنید:", + "YOUR BOT TOKEN HERE": "شناسه ربات خود را اینجا وارد کنید", + "chatIDNotFound": "شناسه چت یافت نشد. لطفا ابتدا به ربات پیام دهید", + "disableCloudflaredNoAuthMsg": "شما در حالت بدون احراز هویت هستید، گذرواژه در این حالت لازم نیست.", + "Trigger type:": "نوع راه اندازی:", + "DateTime Range": "محدوده تاریخ", + "loadingError": "نمی توان داده ها را دریافت کرد، لطفاً بعداً دوباره امتحان کنید.", + "High": "زیاد", + "Retry": "تلاش مجدد", + "Topic": "موضوع", + "Integration Key": "کلید یکپارچه سازی", + "Edit Tag": "ویرایش تگ", + "Server Address": "آدرس سرور", + "Learn More": "بیشتر بدانید", + "Customize": "شخصی سازی", + "Custom Footer": "پاورقی سفارشی", + "No Proxy": "بدون پیشکار", + "Authentication": "اعتبارسنجی", + "steamApiKeyDescription": "برای مانیتورینگ یک سرور استیم،‌ شما نیاز به یک \"Steam Web-API key\" دارید. برای دریافت کلید میتوانید از اینجا اقدام کنید: ", + "No Monitors": "بدون پایشگر", + "Untitled Group": "دسته بنده نشده", + "Services": "خدمات", + "Discard": "دست کشیدن", + "Cancel": "انصراف", + "About": "درباره", + "wayToGetCloudflaredURL": "(دریافت Cloudflared از {0})", + "cloudflareWebsite": "وب سایت کلادفلر", + "Message:": "پیام:", + "HTTP Headers": "هدر های HTTP", + "Bot Token": "توکن بات", + "SecretKey": "کلید محرمانه (SecretKey)", + "telegramSendSilently": "ارسال بی صدا", + "telegramSendSilentlyDescription": "پیام را بی صدا ارسال کن. در این حالت کاربران یک اعلان بدون صدا دریافت خواهند کرد.", + "install": "نصب", + "Icon URL": "URL آیکون", + "Steam API Key": "کلید API استیم", + "Security": "امنیت", + "light": "روشن", + "Query": "کوئری", + "Effective Date Range": "محدوده تاریخ مورد تاثیر (اختیاری)", + "statusPageRefreshIn": "تا بارگذاری مجدد:‌ {0}", + "Content Type": "نوع محتوا (Content Type)", + "Server URL": "آدرس سرور", + "Priority": "اولویت", + "emojiCheatSheet": "چیت شیت ایموجی ها:‌ {0}", + "Read more": "بیشتر بدانید", + "webhookJsonDesc": "{0} برای هر HTTP سرور جدیدی مانند Express.js مناسب است", + "Method": "متد", + "Headers": "سرآیندها", + "PushUrl": "نشانی پوش", + "HeadersInvalidFormat": "هدر ریکوئست یک JSON درست نیست: ", + "BodyInvalidFormat": "هدر ریکوئست یک JSON درست نیست: ", + "Monitor History": "تاریخچهٔ پایشگر", + "clearDataOlderThan": "تاریخچه پایشگر را برای {0} روز نگه دار.", + "PasswordsDoNotMatch": "گذرواژهٔ وارد شده درست نیست.", + "topic": "موضوع", + "topicExplanation": "موضوع MQTT برای پایشگر", + "successMessage": "پیام موفقیت آمیز", + "recent": "اخیر", + "Done": "انجام شده", + "Shrink Database": "فشرده سازی دیتابیس", + "Pick a RR-Type...": "یک تایپ RR انتخاب کنید…", + "Pick Accepted Status Codes...": "یک استاتوس کد قابل قبول انتخاب کنید…", + "Default": "پیش فرض", + "HTTP Options": "آپشن های HTTP", + "Title": "عنوان", + "Content": "محتوا", + "primary": "اولیه", + "dark": "تیره", + "Post": "اطلاع بده", + "Please input title and content": "لطفا یک عنوان و محتوا وارد کنید", + "Created": "ساخته شده در", + "Last Updated": "ویرایش شده در", + "Unpin": "برداشتن", + "Switch to Dark Theme": "تغییر به حالت تیره", + "Show Tags": "نمایش تگ ها", + "Hide Tags": "مخفی سازی تگ ها", + "Description": "توضیحات", + "Custom CSS": "CSS اختصاصی", + "deleteStatusPageMsg": "آیا بابت حذف این استاتوس پیچ مطمئن هستید؟", + "Proxies": "پیشکارها", + "appriseInstalled": "Apprise نصب شده است.", + "Body": "متن", + "Start": "شروع", + "Stop": "توقف", + "Add New Status Page": "افزودن صفحه استاتوس جدید", + "Slug": "لینک", + "Accept characters:": "کاراکتر های مورد تایید:", + "startOrEndWithOnly": "شروع یا پایان فقط با {0}", + "No consecutive dashes": "بدون خط تیره متوالی", + "Next": "بعدی", + "The slug is already taken. Please choose another slug.": "این لینک قبلا گرفته شده است. لطفا لینک دیگری را انتخاب کنید.", + "New Status Page": "صفحه استاتوس جدید", + "Don't know how to get the token? Please read the guide:": "نمی دانید توکن خود را چگونه دریافت کنید؟ لطفا این راهنما را بخوانید:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "اگر در حال حاضر از طریق تونل به کلادفلر متصل می شوید، ممکن است اتصال فعلی قطع شود. آیا مطمئن هستید که می خواهید کلادفلر را متوقف کنید؟ رمز عبور خود را برای تایید این دستور تایپ کنید.", + "Trust Proxy": "پیشکار مورد اعتماد", + "Other Software": "برنامه های دیگر", + "For example: nginx, Apache and Traefik.": "برای مثال: Nginx ،Apache و Traefik.", + "signedInDispDisabled": "اعتبارسنجی غیرفعال شده است.", + "RadiusCallingStationIdDescription": "شناسه دستگاه تماس گیرنده", + "Certificate Expiry Notification": "اطلاعیه انقضای گواهی", + "RadiusSecret": "کلید Radius", + "API Username": "نام‌کاربری API", + "Also check beta release": "همچنین برای نسخه های بتا نیز جستجو کن", + "Using a Reverse Proxy?": "استفاده از پیشکار معکوس؟", + "There might be a typing error in the address.": "ممکن است یک خطای تایپ در آدرس وجود داشته باشد.", + "What you can try:": "آنچه می توانید امتحان کنید:", + "Go back to the previous page.": "بازگشت به صفحه قبلی.", + "Coming Soon": "به زودی", + "Connection String": "رشته اتصال‌ (Connection String)", + "settingsCertificateExpiry": "انقضای گواهی TLS", + "certificationExpiryDescription": "مانیتور های HTTPS راه اندازی میشود زمانی که گواهی TLS منقضی شود در:", + "Retype the address.": "آدرس را دوباره تایپ کنید.", + "Setup Docker Host": "راه اندازی هاست داکر", + "Connection Type": "نوع اتصال", + "Docker Daemon": "Daemon داکر", + "deleteDockerHostMsg": "آیا مطمئن هستید که می خواهید این هاست داکر را برای همه مانیتورها حذف کنید؟", + "Workstation": "محل کار (Workstation)", + "Packet Size": "سایز پکت", + "wayToGetTelegramToken": "شما میتوانید توکن خود را از {0} دریافت کنید.", + "Chat ID": "آیدی چت", + "wayToGetLineNotifyToken": "می‌توانید یک توکن جهت دسترسی از {0} دریافت کنید", + "Examples": "مثال ها", + "Home Assistant URL": "URL هوم اسیستنت شما", + "Long-Lived Access Token": "توکن دسترسی طولانی مدت", + "Notification Service": "سرویس اطلاع رسانی", + "default: notify all devices": "پیش فرض: اطلاع به همه دستگاه ها", + "Automations can optionally be triggered in Home Assistant:": "اتوماسیون ها می توانند به صورت اختیاری در هوم اسیستنت فعال شوند:", + "Event type:": "نوع ایونت:", + "Event data:": "نوع دیتا:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "سپس یک عمل را انتخاب کنید، برای مثال صحنه را به جایی که نور RGB قرمز است تغییر دهید.", + "Optional": "اختیاری", + "recurringInterval": "وقفه", + "Recurring": "مکرر", + "strategyManual": "فعال/غیرفعال سازی به صورت دستی", + "warningTimezone": "این از منطقه زمانی سرور استفاده می کند", + "weekdayShortMon": "دوشنبه", + "weekdayShortTue": "سه شنبه", + "weekdayShortWed": "چهارشنبه", + "weekdayShortThu": "پنجشنبه", + "weekdayShortFri": "جمعه", + "weekdayShortSat": "شنبه", + "weekdayShortSun": "یکشنبه", + "dayOfWeek": "روز های هفته", + "dayOfMonth": "روز های ماه", + "lastDay": "روز آخر", + "lastDay1": "روز آخر ماه", + "lastDay2": "دومین روز آخر ماه", + "lastDay3": "سومین روز آخر ماه", + "Enable": "فعال سازی", + "Single Maintenance Window": "تعمیر و نگه داری تک صفحه", + "Schedule Maintenance": "زمانبندی تعمیر و نگهداری", + "Date and Time": "زمان و تاریخ", + "plugin": "پلاگین | پلاگین ها", + "installing": "در حال نصب", + "uninstall": "حذف از نصب", + "uninstalling": "درحال حذف", + "confirmUninstallPlugin": "آیا مطمئن هستید که می خواهید این پلاگین را حذف از نصب کنید؟", + "notificationRegional": "منطقه ای", + "secureOptionNone": "None / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "خطای TLS را نادیده بگیر", + "From Email": "از ایمیل", + "emailCustomSubject": "موضوع سفارشی", + "To Email": "به ایمیل", + "smtpBCC": "BCC", + "Discord Webhook URL": "URL وب هوک دیسکورد", + "Bot Display Name": "نام نمایشی ربات", + "Hello @everyone is...": "سلام {'@'} همه…", + "wayToGetTeamsURL": "می‌توانید نحوه ایجاد وب هوک را در {0} بیاموزید.", + "wayToGetZohoCliqURL": "می‌توانید نحوه ایجاد وب هوک را در {0} بیاموزید.", + "needSignalAPI": "شما باید یک Signal Client با REST API داشته باشید.", + "wayToCheckSignalURL": "برای مشاهده نحوه تنظیم آن می توانید این URL را بررسی کنید:", + "Number": "عدد", + "Recipients": "گیرندگان", + "Channel access token": "توکن دسترسی به کانال", + "Line Developers Console": "کنسول توسعه دهندگان لاین (Line Developers Console)", + "lineDevConsoleTo": "کنسول توسعه دهندگان لاین (Line Developers Console) - {0}", + "Basic Settings": "تنظیمات پایه", + "User ID": "آیدی کاربر", + "aboutIconURL": "می‌توانید پیوندی به یک عکس در \"URL آیکون \" ارائه دهید تا عکس نمایه پیش‌فرض را لغو کنید. اگر نماد Emoji تنظیم شده باشد، این مورد استفاده نخواهد شد.", + "dataRetentionTimeError": "دوره نگهداری باید 0 یا بیشتر باشد", + "wayToGetDiscordURL": "شما می توانید این را با رفتن به تنظیمات سرور -> ادغام -> مشاهده وب هوک -> وب هوک جدید (Settings -> Integrations -> View Webhooks -> New Webhook) دریافت کنید", + "infiniteRetention": "برای دوره بی نهایت 0 را وارد تنظیم کنید.", + "confirmDeleteTagMsg": "آیا مطمئن هستید که می خواهید این تگ را حذف کنید؟ مانیتورهای مرتبط با این تگ حذف نخواهند شد.", + "grpcMethodDescription": "نام روش تبدیل به فرمت camelCase مانند sayHello، check و غیره.", + "deleteMaintenanceMsg": "آیا مطمئن هستید که می خواهید این تعمیر و نگهداری را حذف کنید؟", + "recurringIntervalMessage": "یکبار اجرا برای هر روز | یکبار اجرا در هر {0} روز", + "affectedMonitorsDescription": "مانیتورهایی را انتخاب کنید که تحت تأثیر تعمیر و نگهداری فعلی هستند", + "atLeastOneMonitor": "حداقل یک مانیتور مورد تاثیر را انتخاب کنید", + "octopushAPIKey": "\"کلید API\" از اعتبارنامه های HTTP API در کنترل پنل", + "octopushLogin": "\"ورود\" از اعتبار HTTP API در کنترل پنل", + "promosmsLogin": "نام ورود API", + "pushoversounds cashregister": "صندوق فروش", + "pushoversounds falling": "رها کردن", + "pushoversounds incoming": "ورودی", + "pushoversounds intermission": "وقفه", + "pushoversounds magic": "سحر آمیز", + "pushoversounds mechanical": "مکانیکی", + "pushoversounds pianobar": "پیانو بار", + "pushoversounds siren": "آژیر", + "pushoversounds spacealarm": "هشدار فضایی", + "pushoversounds gamelan": "گیم لن (Gamelan)", + "Current User": "کاربر فعلی", + "pushoversounds none": "بی صدا", + "pushoversounds tugboat": "قایق یدک کش", + "pushoversounds alien": "هشدار بیگانه (طولانی)", + "pushoversounds climb": "صعود (طولانی)", + "pushoversounds persistent": "پایدار (طولانی)", + "pushoversounds echo": "اکو (طولانی)", + "pushoversounds updown": "بالا پایین (طولانی)", + "pushoversounds vibrate": "فقط ویبره", + "pushyToken": "توکن دستگاه", + "GoogleChat": "Google Chat (فقط Google Workspace)", + "wayToGetKookBotToken": "یک برنامه ایجاد کنید و توکن ربات خود را از {0} دریافت کنید", + "User Key": "کلید کاربر", + "Message Title": "عنوان پیام", + "Notification Sound": "صدای اعلان", + "More info on:": "اطلاعات بیشتر در مورد: {0}", + "pushoverDesc1": "اولویت اضطراری (2) دارای وقفه پیش‌فرض 30 ثانیه بین تلاش‌های مجدد است و پس از 1 ساعت منقضی می‌شود.", + "pushoverDesc2": "اگر می‌خواهید اعلان‌ها را به دستگاه‌های مختلف ارسال کنید، قسمت دستگاه را پر کنید.", + "pushyAPIKey": "کلید Secret API", + "wayToGetKookGuildID": "«حالت توسعه‌دهنده» را در تنظیمات کوک روشن کنید و روی انجمن کلیک راست کنید تا شناسه آن را دریافت کنید", + "Guild ID": "گیلد آیدی (Guild ID)", + "SMS Type": "نوع پیامک", + "octopushTypePremium": "پرمیوم (سریع - پیشنهاد شده برای هشدار ها)", + "octopushTypeLowCost": "کم هزینه (آهسته - گاهی اوقات توسط اپراتور مسدود می شود)", + "checkPrice": "بررسی قیمت‌های {0} :", + "apiCredentials": "اطلاعات API", + "octopushLegacyHint": "آیا از نسخه قدیمی Octopush (1387-1400) استفاده می کنید یا از نسخه جدید؟", + "octopushPhoneNumber": "شماره تلفن (حالت بین المللی مانند 989121234567+) ", + "LunaSea Device ID": "شناسه دستگاه LunaSea", + "Apprise URL": "آدرس Apprise", + "Example:": "مثال: {0}", + "Read more:": "بیشتر بخوانید: {0}", + "Free Mobile User Identifier": "شناسه کاربری Free Mobile", + "Free Mobile API Key": "کلید API در Free Mobile", + "Enable TLS": "فعال کردن TLS", + "Proto Service Name": "نام خدمت پروتو", + "Proto Method": "متد Proto", + "Proto Content": "محتوای Proto", + "Economy": "اقتصاد", + "high": "زیاد", + "SMSManager API Docs": "مستندات SMSManager API ", + "Gateway Type": "نوع Gateway", + "Base URL": "URL پایه", + "goAlertIntegrationKeyInfo": "کلید ادغام API عمومی را برای سرویس در این قالب دریافت کنید \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeee\" معمولاً مقدار پارامتر توکن URL کپی شده است.", + "AccessKeyId": "آیدی AccessKey", + "PhoneNumbers": "شماره های موبایل", + "TemplateCode": "کد تمپلیت", + "Sms template must contain parameters: ": "قالب پیامک باید دارای پارامترهای زیر باشد: ", + "Bark Endpoint": "اند پوینت Bark", + "Bark Group": "گروه Bark", + "Bark Sound": "صدای Bark", + "WebHookUrl": "آدرس وب هوک", + "Device Token": "توکن دستگاه", + "Platform": "پلتفرم", + "Check octopush prices": "بررسی قیمت های octopush {0}.", + "SendKey": "کلید ارسال (SendKey)", + "SecretAccessKey": "کلید دسترسی مخفی (AccessKey Secret)", + "SignName": "نام امضا (SignName)", + "Huawei": "هواوی", + "WeCom Bot Key": "کلید ربات WeCom", + "Setup Proxy": "تنظیم پیشکار", + "Proxy Protocol": "شیوه‌نامهٔ پیشکار", + "Proxy Server": "کارساز پیشکار", + "promosmsTypeEco": "SMS ECO - ارزان اما کند و اغلب بارگذاری شده است. فقط به گیرندگان لهستانی محدود می شود.", + "promosmsTypeSpeed": "سرعت پیامک — بالاترین اولویت در سامانه. بسیار سریع و قابل‌اعتماد، اما پرهزینه است (حدود دو برابر قیمت پیامک کامل).", + "promosmsPhoneNumber": "شماره تلفن (برای گیرنده لهستانی می توانید کدهای منطقه را نادیده بگیرید)", + "promosmsSMSSender": "نام فرستنده پیامک: نام از پیش ثبت شده یا یکی از پیش فرض ها: InfoSMS، SMS Info، MaxSMS، INFO، SMS", + "promosmsAllowLongSMS": "اجازه برای پیامک طولانی", + "Feishu WebHookUrl": "آدرس وب هوک Feishu", + "Internal Room Id": "آیدی اتاق داخلی", + "Uptime Kuma URL": "نشانی پایشگر کوما", + "signalImportant": "مهم: شما نمی توانید گروه ها و اعداد را در گیرندگان ترکیب کنید!", + "aboutWebhooks": "اطلاعات بیشتر درباره وب هوک در: {0}", + "documentation": "مستندات", + "smtpDkimDomain": "نام دامنه", + "smtpDkimHashAlgo": "الگوریتم رمزگذاری (اختیاری)", + "smtpDkimheaderFieldNames": "کلیدهای هدر برای امضا (اختیاری)", + "smtpDkimskipFields": "کلیدهای هدر برای عدم امضا (اختیاری)", + "Integration URL": "URL یکپارچه سازی", + "smtpDkimKeySelector": "انتخابگر کلید (SecretKey)", + "smtpDkimPrivateKey": "کلید محرمانه (Private Key)", + "socket": "سوکت", + "do nothing": "هیچ کاری نکن", + "auto resolve": "حل خودکار", + "alertaEnvironment": "محیط", + "alertaApiKey": "کلید API", + "alertaAlertState": "وضعیت هشدار", + "smseagleTo": "شماره تلفن(ها)", + "smseagleGroup": "نام(های) گروه دفترچه تلفن", + "smseagleContact": "نام(های) تماس دفترچه تلفن", + "smseagleRecipient": "گیرنده(های) (چند مورد باید با کاما از هم جدا شوند)", + "smseagleUrl": "URL دستگاه SMSEagle شما", + "smseaglePriority": "اولویت پیام (9-0, بالاترین اولویت = 9)", + "Recipient Number": "شماره گیرنده", + "From Name/Number": "از نام/شماره", + "Octopush API Version": "نسخه Octopush API", + "ntfy Topic": "موضوع ntfy", + "onebotHttpAddress": "آدرس HTTP OneBot", + "onebotMessageType": "نوع پیام OneBot", + "onebotGroupMessage": "گروه", + "onebotPrivateMessage": "خصوصی", + "onebotUserOrGroupId": "آیدی گروه/کاربر", + "PushDeer Key": "کلید PushDeer", + "wayToGetClickSendSMSToken": "می‌توانید نام کاربری و کلید API را از {0} دریافت کنید.", + "Continue": "ادامه", + "Add Another": "افزودن یکی دیگر", + "Key Added": "کلید API اضافه شد", + "Add API Key": "افزودن کلید API", + "No API Keys": "بدون کلید API", + "apiKey-active": "فعال", + "apiKey-expired": "منقضی شده", + "apiKey-inactive": "غیرفعال", + "Expires": "انقضا", + "disableAPIKeyMsg": "آیا مطمئن هستید که می خواهید این کلید API را غیرفعال کنید؟", + "Generate": "ایجاد یک کلید API جدید", + "lunaseaTarget": "هدف", + "lunaseaDeviceID": "شناسه افزاره", + "lunaseaUserID": "شناسه کاربر", + "Auto resolve or acknowledged": "حل خودکار یا اعلام اطلاع یافته (Auto resolve or acknowledged)", + "Legacy Octopush-DM": "(Legacy Octopush-DM)", + "smtpCC": "ارسال نسخه به", + "promosmsPassword": "رمز عبور API", + "pushoversounds pushover": "Pushover (پیش‌فرض)", + "pushoversounds bike": "دوچرخه", + "pushoversounds bugle": "بوق", + "pushoversounds cosmic": "کیهانی", + "resendEveryXTimes": "پیام را هر {0} بار دوباره ارسال کن", + "resendDisabled": "ارسال مجدد غیرفعال است", + "Push URL": "نشانی پوش", + "Schedule maintenance": "زمانبندی نگهداری (غیرفعال سازی دستی)", + "webhookFormDataDesc": "{multipart} برای PHP مناسب است. آرایه JSON نیاز است تا به این شکل باز شود {decodeFunction}", + "webhookAdditionalHeadersTitle": "هدرهای اضافی", + "webhookAdditionalHeadersDesc": "تنظیم هدر های اضافی که نیاز است با وب هوک ارسال شود. هر هدر باید به کیلد/مقدار JSON تعریف شده باشد.", + "Webhook URL": "آدرس وب هوک", + "Application Token": "توکن اپلیکیشن", + "Style": "حالت ها", + "info": "اطلاعات", + "warning": "هشدار", + "danger": "خطر", + "error": "خطا", + "critical": "اهمیت ویژه", + "HTTP Basic Auth": "حالت پایه احراز هویت (HTTP Basic Auth)", + "RadiusSecretDescription": "اشتراک گذاری Secret بین کاربر و سرور", + "RadiusCalledStationId": "نام Station Id", + "RadiusCalledStationIdDescription": "شناسه دستگاه فراخوانی شده", + "RadiusCallingStationId": "آیدی ایستگاه تماس (Calling Station Id)", + "tcp": "TCP / HTTP", + "Frontend Version": "نسخه فرانت اند", + "Frontend Version do not match backend version!": "نسخه فرانت اند با نسخه بک اند مطابقت ندارد!", + "backupOutdatedWarning": "منسوخ شده: از آنجایی که بسیاری از ویژگی ها اضافه شده اند و این ویژگی پشتیبان گیری کمی حفظ نشده است، نمی تواند یک نسخه پشتیبان کامل ایجاد یا بازیابی شود.", + "backupRecommend": "لطفاً مستقیماً از Volume یا پوشه داده (./data/) نسخه پشتیبان تهیه کنید.", + "No Maintenance": "بدون تعمیر و نگهداری", + "pauseMaintenanceMsg": "آیا مطمئن هستید که می خواهید توقف کنید؟", + "maintenanceStatus-under-maintenance": "تحت تعمیر و نگهداری", + "maintenanceStatus-inactive": "غیرفعال", + "maintenanceStatus-scheduled": "برنامه ریزی شده", + "maintenanceStatus-ended": "پایان یافته", + "maintenanceStatus-unknown": "ناشناخته", + "Display Timezone": "منطقه زمانی برای نمایش", + "Server Timezone": "منطقه زمانی در سرور", + "statusPageMaintenanceEndDate": "پایان", + "IconUrl": "URL آیکون", + "Enable DNS Cache": "(منسوخ شده) فعال‌سازی انبارهٔ ساناد برای پایشکرهای HTTP", + "Access Token": "توکن دسترسی", + "smtp": "ایمیل (SMTP)", + "Device": "دستگاه", + "Proxy server has authentication": "کارساز پیشکار دارای احراز هویت است", + "Add New Tag": "اضافه کردن تگ جدید", + "Custom": "غیره", + "default": "پیش فرض", + "enabled": "فعال", + "setAsDefault": "ذخیره به عنوان پیش فرض", + "proxyDescription": "پیشکاری برای راه اندازی این پایشگر اجباری است.", + "setAsDefaultProxyDescription": "این پروکسی به طور پیش فرض برای مانیتورهای جدید فعال می شود. همچنان می توانید پروکسی را به طور جداگانه برای هر مانیتور غیرفعال کنید.", + "Valid": "درست", + "Invalid": "نادرست", + "User": "کاربر", + "Installed": "نصب شده", + "Not installed": "نصب نشده", + "Running": "در حال اجرا", + "Not running": "اجرا نشده", + "Remove Token": "حذف توکن", + "Please read": "لطفا بخوانید", + "Subject:": "موضوع:", + "Valid To:": "معتبر تا:", + "Days Remaining:": "روز های باقی مانده:", + "Fingerprint:": "اثرانگشت (Fingerprint):", + "No status pages": "بدون صفحات استاتوس", + "Domain Name Expiry Notification": "اعلان انقضای نام دامنه", + "Issuer:": "صادرکننده:", + "Date Created": "ایجاد شده در", + "Footer Text": "متن فوتر", + "Show Powered By": "نمایش قدرت گرفته از آپ‌تایم کوما", + "Domain Names": "نام دامنه ها", + "Proxy": "پیشکار", + "signedInDisp": "وارد شده به عنوان {0}", + "or": "یا", + "Disable": "غیرفعال سازی", + "endpoint": "نقطه پایانی", + "Status:": "وضعیت: {0}", + "Strategy": "استراتژی", + "Icon Emoji": "ایموجی آیکون", + "sameAsServerTimezone": "مشابه با منطقه زمانی سرور", + "startDateTime": "ساعت/روز شروع", + "endDateTime": "ساعت/روز پایان", + "cronSchedule": "برنامه زمانی: ", + "invalidCronExpression": "حالت کرون نامعتبر است: {0}", + "cronExpression": "حالت کرون", + "ntfyAuthenticationMethod": "روش اعتبارسنجی", + "ntfyUsernameAndPassword": "نام‌کاربری و گذرواژه", + "pushoverMessageTtl": "TTL پیام (ثانیه)", + "Show Clickable Link": "نمایش لینک های قابل کلیک", + "Open Badge Generator": "باز کردن نشان ساز (Badge Generator)", + "Badge Generator": "نشان ساز (Badge Generator) {0}", + "Badge Type": "نوع نشان", + "Badge Duration": "مدت نشان", + "Badge Label": "برچسب نشان", + "Badge Prefix": "مقدار پیشوند نشان", + "Badge Suffix": "مقدار پسوند نشان", + "Badge Label Color": "رنگ برچسب نشان", + "Badge Color": "رنگ نشان", + "Badge Label Prefix": "پیشوند برچسب نشان", + "Badge Label Suffix": "پسوند برچسب نشان", + "Badge Down Color": "رنگ نشان زمانی که مانیتور دچار قطعی و Down شده است", + "Badge Maintenance Color": "رنگ نشان برای زمانی که پایشگر در حالت نگهداری است", + "Badge Warn Color": "رنگ نشان زمانی که مانیتور در حالت هشدار است", + "Badge Down Days": "روز هایی که مانیتور دچار قطعی شده است", + "Badge Style": "حالت نشان", + "Badge value (For Testing only.)": "مقدار نشان (فقط برای تست.)", + "Badge URL": "آدرس نشان", + "Monitor Setting": "تنظیمات مانتیور {0}", + "Show Clickable Link Description": "اگر انتخاب شود، همه کسانی که به این صفحه وضعیت دسترسی دارند میتوانند به صفحه مانیتور نیز دسترسی داشته باشند.", + "Badge Up Color": "رنگ نشان زمانی که مانیتور بدون مشکل و بالا است", + "Badge Pending Color": "رنگ نشان زمانی که مانیتور در حال انتظار است", + "Badge Warn Days": "روزهایی که پایشگر در حالت هشدار است", + "noGroupMonitorMsg": "موجود نیست. ابتدا یک گروه پایشگر جدید ایجاد کنید.", + "Home": "خانه", + "Edit Maintenance": "ویرایش تعمیر و نگهداری", + "Cannot connect to the socket server": "نمی‌توان به کارساز سوکت متصل شد", + "Reconnecting...": "ارتباط مجدد...", + "Monitor Group": "گروه پایشگر", + "Group": "گروه", + "Close": "بستن", + "Request Timeout": "پایان‌ زمانِ درخواست", + "filterActive": "فعال", + "webhookCustomBodyDesc": "یک بدنه HTTP سفارشی برای ریکوئست تعریف کنید. متغیر های قابل استفاده: {msg}, {heartbeat}, {monitor}.", + "tailscalePingWarning": "برای استفاده از Tailscale Ping monitor، شما باید آپتایم کوما را بدون استفاده از داکر و همچنین Tailscale client را نیز بر روی سرور خود نصب داشته باشید.", + "Enter the list of brokers": "لیست بروکر هارا وارد کنید", + "Enable Kafka Producer Auto Topic Creation": "فعال سازی ایجاپ موضوع اتوماتیک تهیه کننده", + "Secret AccessKey": "کلید محرمانه AccessKey", + "wayToGetFlashDutyKey": "برای یکپارچه‌سازی Uptime Kuma با Flashduty: به بخش کانال‌ها بروید > یک کانال را انتخاب کنید > یکپارچه‌سازی‌ها > افزودن یک یکپارچه‌سازی جدید، گزینه Uptime Kuma را انتخاب کنید و آدرس Push را کپی کنید.", + "showCertificateExpiry": "نمایش زمان به پایان رسیدن اعتبار سرتیفیکیت", + "gamedigGuessPortDescription": "پورت مورد استفاده توسط پروتکل Query Valve Server ممکن است با پورت مشتری متفاوت باشد. اگر مانیتور نمی تواند به سرور شما متصل شود، این را امتحان کنید.", + "invertKeywordDescription": "دنبال کلمات کلیدی ناموجود باشید تا آنهایی که موجود است.", + "Notify Channel": "آگاه کردن کانال", + "timeoutAfter": "پایان‌زمانِ بعد از {0} ثانیه", + "FlashDuty Severity": "شدت", + "nostrRelays": "ریلی Nostr", + "nostrRelaysHelp": "یک آدرس ریلی به ازای هر خط", + "nostrSender": "کلید محرمانه فرسته (nsec)", + "gamedigGuessPort": "Gamedig: پورت پرسش", + "styleElapsedTime": "زمان سپری شده زیر heartbeat bar", + "styleElapsedTimeShowNoLine": "نمایش (بدون خط)", + "styleElapsedTimeShowWithLine": "نمایش (با خط)", + "filterActivePaused": "متوقف شده", + "webhookBodyPresetOption": "از پیش تعیین شده - {0}", + "webhookBodyCustomOption": "بدنه سفارشی", + "selectedMonitorCount": "انتخاب شده:‌ {0}", + "Check/Uncheck": "انتخاب/لغو انتخاب", + "chromeExecutable": "فایل اجرایی کروم/کرومیوم", + "chromeExecutableAutoDetect": "تشخیص خودکار", + "chromeExecutableDescription": "برای کاربران داکر، اگر کرومیوم را هنوز نصب نکردید، ممکن است چند دقیقه زمان ببرد تا کرومیوم نصب و نتایج نشان داده شود. همچنین ممکن است تا 1 گیگابایت فضا بر روی سرور نیاز باشد.", + "Select": "انتخاب", + "aboutNotifyChannel": "آگاه کردن کانال یک نوتیفیکیشن دسکتاپ یا تلفن همراه را برای همه اعضای کانال راه‌اندازی می‌کند، خواه در دسترس بودن آنها فعال باشد یا غیرفعال.", + "Server URL should not contain the nfty topic": "URL سرور نباید حاوی موضوع nfty باشد", + "PushDeer Server": "سرور PushDeer", + "pushDeerServerDescription": "برای استفاده از سرور رسمی خالی بگذارید", + "twilioApiKey": "کلید API‌ (اختیاری)", + "Badge Duration (in hours)": "مدت نشان (در ساعت)", + "Badge Preview": "پیش نمایش نشان", + "Kafka Brokers": "بروکر Kafka", + "Press Enter to add broker": "Enter را برای افزودن بروکر جدید فشار دهید", + "Kafka Topic Name": "عنوان موضوع", + "Kafka Producer Message": "پیام تهیه کننده", + "Enable Kafka SSL": "فعال سازی SSL", + "Kafka SASL Options": "گزینه های SASL", + "Mechanism": "مکانیزم", + "Pick a SASL Mechanism...": "یک مکانیزم SASL انتخاب کنید…", + "Authorization Identity": "اطلاعات اعتبارسنجی", + "AccessKey Id": "آیدی AccessKey", + "Session Token": "توکن سشن", + "Request Body": "بدنه پیام", + "nostrRecipients": "کلید عمومی گیرنده (npub)", + "nostrRecipientsHelp": "فرمت npub، یکی به ازای هر خط", + "noOrBadCertificate": "بدون سرتیفیکت یا بد", + "Invert Keyword": "کلمه کلیدی معکوس", + "Expected Value": "مقدار مورد انتظار", + "Json Query": "کوئری جیسون", + "Saved.": "ذخیره شده.", + "setupDatabaseChooseDatabase": "از چه پایگاه‌داده‌ای می‌خواهید استفاده کنید؟", + "setupDatabaseEmbeddedMariaDB": "شما نیازی نیست چیزی را تنظیم کنید . این تصویر بارگنج، MariaDB را به طور خودکار برای شما جاسازی و پیکربندی کرده است. مورد کوما از طریق سوکت یونیکس به این پایگاه‌داده متصل می شود.", + "setupDatabaseSQLite": "یک پروندهٔ پایگاه‌داده‌ ساده که برای استقرار در مقیاس کوچک توصیه می‌شود. قبل از نسخه 2.0.0، Uptime Kuma از SQLite به عنوان پایگاه‌داده‌ پیش‌فرض استفاده می‌کرد.", + "enableNSCD": "فعال سازی NSCD (Name Service Cache Daemon) برای کش کردن تمام ریکوئست های DNS", + "setupDatabaseMariaDB": "به یک پایگاه‌داده‌ خارجی MariaDB متصل شوید. شما باید اطلاعات اتصال پایگاه‌داده را تنظیم کنید.", + "dbName": "نام پایگاه‌داده", + "Bark API Version": "نسخه API Bark", + "monitorToastMessagesDescription": "پیام های Toasy برای مانیتورها پس از زمان معین در چند ثانیه ناپدید می‌شوند. با تنظیم آن بر روی -1 باعث غیرفعال شدن مهلت زمانی می شود. تنظیم بر روی 0 پیام های Toastرا غیرفعال می‌کند.", + "monitorToastMessagesLabel": "پیام های Monitor Toast", + "toastErrorTimeout": "تایم اوت برای پیام های خطا", + "toastSuccessTimeout": "تایم اوت برای پیام های موفقیت آمیز", + "authInvalidToken": "توکن نامعتبر است.", + "authIncorrectCreds": "نام‌کاربری یا گذرواژه اشتباه است.", + "2faEnabled": "تایید دو مرحله ای فعال است.", + "2faDisabled": "تایید دو مرحله ای غیرفعال است.", + "successPaused": "با موفقیت متوقف شد.", + "successDeleted": "با موفقیت حذف شد.", + "successEdited": "با موفقیت ویرایش شد.", + "successBackupRestored": "بکاپ با موفقیت بازیابی شد.", + "successDisabled": "با موفقیت غیرفعال شد.", + "successEnabled": "با موفقیت فعال شد.", + "tagNotFound": "برچسب پیدا نشد.", + "foundChromiumVersion": "کرومیوم/کروم پیدا شد. نسخه: {0}", + "pushViewCode": "چگونه از پوش مانیتور استفاده شود؟ (نگاه به کد)", + "programmingLanguages": "زبان های برنامه نویسی", + "authUserInactiveOrDeleted": "کاربر غیرفعال یا حذف شده است.", + "2faAlreadyEnabled": "تایید دومرحله ای قبلاً فعال شده است.", + "successAdded": "با موفقیت اضافه شد.", + "successResumed": "با موفقیت از سر گرفته شد.", + "successAuthChangePassword": "رمز عبور با موفقیت به روز رسانی شد.", + "pushOthers": "غیره", + "successKeyword": "کلیدواژه موفقیت", + "Reset Token": "توکن بازنشانی", + "settingUpDatabaseMSG": "در حال تنظیم بانک داده. ممکن است مقداری طول بکشد، لطفا صبور باشید.", + "noDockerHostMsg": "در دسترس نیست. ابتدا میزبان داکر را تنظیم کنید.", + "DockerHostRequired": "لطفا میزبان داکر این ناظر را مشخص کنید.", + "Search monitored sites": "جستجو در سایت های نظارت شده", + "successKeywordExplanation": "کلیدواژه MQTT که به عنوان موفق تلقی خواهد شد", + "Remove the expiry notification": "حذف تاریخ انقضا اطلاع رسانی", + "emailCustomisableContent": "محتوای قابل سفارشی کردن", + "leave blank for default subject": "برای موضوع پیشفرض خالی بگذارید", + "emailCustomBody": "بدنه سفارشی", + "leave blank for default body": "برای بدنه پیشفرض خالی بگذارید", + "emailTemplateServiceName": "نام سرویس", + "emailTemplateHostnameOrURL": "نام میزبان یا URL", + "emailTemplateStatus": "وضعیت", + "templateMsg": "پیام اعلان", + "templateMonitorJSON": "شی توصیف کننده ناظر", + "Add a new expiry notification day": "تنظیم تاریخ انقضا اطلاع رسانی جدید", + "ntfyPriorityHelptextAllExceptDown": "همه ایونت ها با این اولویت ارسال می شوند، به جز {0}-ایونت، که دارای اولویت {1} هستند", + "Browser Screenshot": "اسکرین شات مرورگر", + "GrafanaOncallUrl": "لینک در دسترس گرافانا (Grafana)", + "What is a Remote Browser?": "مرورگر ریموت چیست؟", + "Remote Browsers": "مرورگرهای ریموت", + "Remote Browser": "مرورگر ریموت", + "Add a Remote Browser": "اضافه کردن مرورگر ریموت", + "Remote Browser not found!": "مرورگر ریموت پیدا نشد!", + "remoteBrowsersDescription": "مرورگرهای ریموت جایگزینی برای اجرای لوکالی Chromium هستند.انها را میتوانید با سرویسی مانند browserless.io راه اندازی کنید یا به سرویس خود متصل شوید", + "self-hosted container": "کانتیر سلف-هاست شده", + "remoteBrowserToggle": "به‌طور پیش‌فرض Chromium در کانتینر Uptime Kuma اجرا می‌شود. با تغییر دادن این سوئیچ می توانید از یک مرورگر ریموت استفاده کنید.", + "useRemoteBrowser": "استفاده از یک مرورگر ریموت", + "deleteRemoteBrowserMessage": "آیا مطمئن هستید که می خواهید این مرورگر ریموت را برای همه مانیتورها حذف کنید؟", + "statusPageSpecialSlugDesc": "پسوند اختصاصی {0}: این صفحه زمانی نشان داده می شود که هیچ پسوندی ارائه نشود", + "smtpLiquidIntroduction": "دو فیلد زیر از طریق Liquid templating Language قابل قالب بندی هستند. لطفاً برای نحوه استفاده به {0} مراجعه کنید. متغیرهای موجو:", + "emailTemplateMonitorJSON": "شیء توصیف کننده پایشگر(JSON)", + "emailTemplateHeartbeatJSON": "شیء توصیف کننده Heartbeat", + "emailTemplateMsg": "پیام اطلاع رسانی", + "emailTemplateLimitedToUpDownNotification": "فقط برای heartbeat UP/DOWN در دسترس است، در غیر این صورت NULL است", + "liquidIntroduction": "قالب‌ پذیری از طریق زبان قالب‌سازی مایع قابل انجام است. لطفاً برای دیدن نحوه استفاده به {0} مراجعه کنید. متغیرهای موجود:", + "templateHeartbeatJSON": "شیئی که heartbeat را توصیف می کند (JSON)", + "templateLimitedToUpDownCertNotifications": "فقط برای پیام های UP/DOWN/انقضای گواهی (Certificate Expiry) موجود است", + "templateLimitedToUpDownNotifications": "فقط برای پیام های UP/DOWN موجود است", + "setup a new monitor group": "ایجاد یک گروه پایشگر", + "openModalTo": "باز کردن مدال برای {0}", + "Add a domain": "اضافه کردن دامین", + "Remove domain": "حذف دامین '{0}'", + "ntfyPriorityHelptextAllEvents": "تمامی ایونت ها با حداکثر اولویت ارسال می شوند", + "documentationOf": "کلید مطلب ها", + "wayToGetHeiiOnCallDetails": "روش دریافت ایدی کلید و کلید ای پی آی درمطلب توضیح داده شده است", + "Channel access token (Long-lived)": "توکن دسترسی به کانال", + "Your User ID": "آیدی حساب کاربری شما", + "ignoreTLSErrorGeneral": "نادیده گرفتن ارور TLS/SSL برای برقراری ارتباط", + "Originator": "ایجاد کننده", + "cellsyntOriginatortypeNumeric": "مقدار عددی (حداکثر 15 رقم) با شماره تلفن در قالب بین المللی بدون 00 اول (به عنوان مثال شماره 07920 110 000 بریتانیا باید به عنوان 447920110000 تنظیم شود). گیرندگان می توانند به پیام پاسخ دهند.", + "threemaRecipient": "گیرنده", + "threemaRecipientType": "نوع گیرنده", + "threemaRecipientTypeIdentity": "شناسه Threema", + "threemaRecipientTypeIdentityFormat": "8 کاراکتر", + "threemaRecipientTypePhone": "شماره موبایل", + "threemaRecipientTypePhoneFormat": "E.164، بدون پیشوند +", + "threemaRecipientTypeEmail": "آدرس ایمیل", + "threemaSenderIdentity": "شناسه ورودی", + "threemaApiAuthenticationSecret": "کلید شناسه ورودی", + "threemaBasicModeInfo": "توجه: این ادغام از ورودی Threema در حالت اولیه (رمزگذاری مبتنی بر سرور) استفاده می کند. جزئیات بیشتر را می توان در {0} یافت.", + "smspartnerApiurl": "شما می‌توانید کلید API را در داشبورد خود در {0} پیدا کنید", + "smspartnerPhoneNumber": "شماره موبایل(ها)", + "Host URL": "نشانی میزبان", + "gtxMessagingApiKeyHint": "شما می‌توانید کلید API خود را در این آدرس پیدا کنید: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "Bitrix24 Webhook URL": "آدرس وب هوک Bitrix24", + "wayToGetBitrix24Webhook": "شما می توانید با دنبال کردن مراحل در {0} یک وب هوک ایجاد کنید", + "whapiRecipient": "شماره تلفن / شناسه ارتباط / شناسه گروه", + "gtxMessagingToHint": "قالب بین‌المللی، با پیشوند \"+\" ({e164}، {e212} یا {e214})", + "Mentioning": "منشن کردن", + "Send to channel": "ارسال به کانال", + "forumPostName": "نام پست انجمن", + "threadForumPostID": "شناسه پست رشته / انجمن", + "e.g. {discordThreadID}": "به عنوان مثال: {discordThreadID}", + "wayToGetDiscordThreadId": "دریافت شناسه پست / انجمن شبیه به دریافت شناسه کانال است. درباره نحوه دریافت شناسه بیشتر بخوانید {0}", + "senderSevenIO": "شماره یا نام ارسال کننده", + "receiverSevenIO": "شماره گیرنده", + "Don't mention people": "این آدم هارا منشن نکن", + "Mention group": "منشن {group}", + "smspartnerSenderName": "نام ارسال کننده اس ام اس", + "apiKeySevenIO": "کلید SevenIO API", + "API URL": "آدرس API", + "callMeBotGet": "اینجا شما می توانید یک نقطه پایانی برای {0}، {1} و {2} ایجاد کنید. به خاطر داشته باشید که ممکن است ریت لیمیت (Rate Limit) دریافت کنید. به نظر می رسد ریت لیمیت ها عبارتند از: {3}", + "To Phone Number": "به شماره موبایل", + "Originator type": "نوع Originator", + "Destination": "مقصد", + "Allow Long SMS": "اجازه ارسال پیام بلند", + "max 15 digits": "حداکثر 15 عدد", + "max 11 alphanumeric characters": "حداکثر 11 کاراکتر الفبا و اعداد", + "locally configured mail transfer agent": "نماینده انتقال نامه به صورت محلی", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "یا آدرس هاست سروری که میخواهید به آن متصل شوید را وارد کنید یا {localhost} اگر میخواهید از یک {local_mta} استفاده کنید", + "Refresh Interval": "وقفه بین بارگزاری مجدد", + "Refresh Interval Description": "صفحه وضعیت هر {0} ثانیه یک بار سایت را به‌روزرسانی می‌کند", + "Select message type": "انتخاب نوع پیام", + "Create new forum post": "ایحاد یک پست جدید در انجمن", + "postToExistingThread": "پست در یک رشته / انجمن موجود", + "whatHappensAtForumPost": "ایحاد یک پست جدید در انجمن. این گزینه در یک پست موجود پیامی را ارسال نمی کند. برای پست در یک پیام موجود از گزینه\"{option}\" استفاده کنید", + "smspartnerPhoneNumberHelptext": "شماره باید در قالب بین المللی {0}، {1} باشد. شماره ها باید با {2} از هم جدا شوند", + "smspartnerSenderNameInfo": "باید بین 3..=11 کاراکتر معمولی باشد", + "bitrix24SupportUserID": "لطفا شناسه کاربری خود را در Bitrix24 وارد کنید. با رفتن به پروفایل کاربر می توانید شناسه را از لینک دریافت کنید.", + "Command": "دستور", + "mongodbCommandDescription": "یک دستور MongoDB را در پایگاه داده اجرا کنید. برای اطلاعات در مورد دستورات موجود، {documentation} را بررسی کنید", + "wayToGetSevenIOApiKey": "دز داشبورد به آدرس app.seven.io > developer > api key بر روی دکمه سبز اضافه کلیک کنید", + "receiverInfoSevenIO": "اگر شماره دریافت کننده در آلمان نیست، شما باید کد کشور را در مقابل شماره اضافه کنید (به عنوان مثال برای کد کشور 1 از ایالات متحده به جای 017612121212 از 117612121212 استفاده کنید)", + "wayToWriteWhapiRecipient": "شماره تلفن با پیشوند بین‌المللی، اما بدون علامت مثبت در شروع ({0})، شناسه تماس ({1}) یا شناسه گروه ({2}).", + "wayToGetWhapiUrlAndToken": "شما با رفتن به کانال مورد نظر خود از {0} می توانید URL API و توکن را دریافت کنید", + "gtxMessagingFromHint": "در موبایل ها، گیرندگان شما TPOA را که به عنوان فرستنده پیام نمایش داده می شود، می بینند. حداکثر 11 کاراکتر (فقط الفبا و اعداد)، یک کد کوتاه، کد طولانی محلی یا اعداد بین‌المللی مجاز هستند ({e164}، {e212} یا {e214})", + "cellsyntOriginator": "روی تلفن همراه گیرنده به عنوان Originator پیام قابل مشاهده است. مقادیر و تابع مجاز به پارامتر مبدا بستگی دارد.", + "cellsyntDestination": "شماره تلفن گیرنده با استفاده از فرمت بین‌المللی با 00 اصلی و به دنبال آن کد کشور، به عنوان مثال. 00447920110000 برای بریتانیا شماره 07920 110 000 (حداکثر 17 رقم در کل). حداکثر 25000 گیرنده جدا شده با کاما در هر درخواست HTTP.", + "cellsyntSplitLongMessages": "پیام های طولانی را به حداکثر 6 قسمت تقسیم کنید. 153 x 6 = 918 کاراکتر.", + "wayToGetThreemaGateway": "شما می توانید برای Threema Gateway {0} ثبت نام کنید.", + "threemaSenderIdentityFormat": "8 کاراکتر، معمولا با * (ستاره) شروع می شود", + "apiKeysDisabledMsg": "کلیدهای API غیرفعال هستند زیرا احراز هویت غیرفعال است.", + "From Phone Number / Transmission Path Originating Address (TPOA)": "از شماره تلفن / آدرس مبدأ مسیر انتقال (TPOA)", + "Alphanumeric (recommended)": "الفبا و اعداد (پیشنهاد شده)", + "Telephone number": "شماره تلفن", + "cellsyntOriginatortypeAlphanumeric": "الفبا و اعداد (حداکثر 11 کاراکتر). گیرندگان نمی توانند به پیام پاسخ دهند.", + "cacheBusterParam": "افزودن پارامتر {0}", + "Private Number": "شماره شخصی", + "jsonQueryDescription": "با استفاده از JSON query پاسخ JSON سرور را تحلیل و استخراج کنید یا از «$» برای پاسخ خام استفاده کنید، در صورتی که JSON نباشد. سپس نتیجه با مقدار مورد نظر به عنوان رشته مقایسه می‌شود. برای مستندات به {0} بروید و از {1} برای آزمایش کوئری‌ها استفاده کنید.", + "now": "الان", + "-year": "-سال", + "time ago": "{0} پیش", + "and": "و", + "Condition": "شرط", + "SNMP Version": "نسخه SNMP", + "privateOnesenderDesc": "مطمئن شوید که شماره تلفن درست است. برای ارسال پیام به شماره شخصی، مثل: 09123456789", + "Group ID": "شناسه گروه", + "Add Remote Browser": "افزودن مرورگر ریموت", + "New Group": "گروه جدید", + "Group Name": "نام گروه", + "Authentication Method": "روش احراز هویت", + "Client ID": "شناسه کلاینت", + "Client Secret": "رمز کلاینت", + "Go back to home page.": "بازگشت به صفحه اصلی.", + "No tags found.": "برچسبی یافت نشد.", + "Lost connection to the socket server.": "اتصال به سوکت سرور از دست رفت.", + "Cannot connect to the socket server.": "نمی‌توان به سوکت سرور متصل شد.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "پیوند وب‌هوک SIGNL4", + "Conditions": "شرایط", + "conditionAdd": "افزودن شرط", + "conditionDelete": "حذف شرط", + "conditionAddGroup": "افزودن گروه", + "conditionDeleteGroup": "حذف گروه", + "conditionValuePlaceholder": "مقدار", + "equals": "برابر", + "not equals": "نابرابر", + "contains": "شامل", + "starts with": "شروع با", + "not starts with": "عدم شروع با", + "not contains": "شامل نباشد", + "ends with": "پایان با", + "not ends with": "عدم پایان با", + "less than": "کمتر از", + "greater than": "بیشتر از", + "less than or equal to": "کمتر یا مساوی با", + "greater than or equal to": "بیشتر یا مساوی با", + "record": "رکورد", + "snmpCommunityStringHelptext": "این رشته به عنوان پسورد برای احراز هویت و کنترل دسترسی به دستگاه‌های آماده SNMP استفاده می‌شود. این پسورد را با توجه به تنظیمات دستگاه SNMP خود مطابقت دهید.", + "snmpOIDHelptext": "OID برای سنسور یا وضعیتی که می‌خواهید مانیتور کنید را وارد کنید. اگر درباره مقدار OID اطمینان ندارید از ابزارهای مدیریت شبکه‌ای مانند MIB یا SNMP استفاده کنید.", + "Json Query Expression": "عبارت کوئری JSON", + "cacheBusterParamDescription": "پارامتر تولید شده رندوم برای رد کردن کش.", + "Community String": "رشته انجمن", + "OID (Object Identifier)": "OID (شناسه شئ)", + "Please enter a valid OID.": "لطفا یک OID معتبر وارد کنید.", + "Host Onesender": "هاست Onesender", + "Token Onesender": "توکن Onesender", + "Recipient Type": "نوع دریافت کننده", + "groupOnesenderDesc": "از معتبر بودن GroupID اطمینان حاصل کنید. برای ارسال پیام در گروه، برای مثال: ‭628123456789-342345", + "wayToGetOnesenderUrlandToken": "برای دریافت URL و توکن می‌توانید به وبسایت Onesender مراجعه گنید. اطلاعات بیشتر {0}", + "OAuth2: Client Credentials": "OAuth2: گواهی مشتری", + "Authorization Header": "هدر Authorization", + "Form Data Body": "از اطلاعات بدنه", + "OAuth Token URL": "URL توکن OAuth", + "OAuth Scope": "اسکوپ OAuth", + "Optional: Space separated list of scopes": "اختیاری: لیست جدا شده با فاصله از اسکوپ‌ها", + "signl4Docs": "شما می‌توانید اطلاعات بیشتر در رابطه با نحوه تنظیم SIGNL4 و به دست آوردن URL وب‌هوک SIGNL4 را در {0} پیدا کنید.", + "ignoredTLSError": "خطاهای TLS/SSL نادیده گرفته میشود", + "Bubble": "حباب", + "Message format": "ساختار پیام", + "Notification Channel": "کانال اطلاع‌رسانی", + "Doorbell": "زنگ", + "The phone number of the recipient in E.164 format.": "شماره تلفن گیرنده به فرمت E.164.", + "Sound": "صدا", + "Correct": "درست", + "Reveal": "نمایش", + "Flute": "فلوت", + "Money": "پول", + "Scifi": "علمی‌تخیلی", + "Elevator": "آسانسور", + "Guitar": "گیتار", + "Pop": "پاپ", + "From": "از", + "telegramServerUrl": "آدرس سرور (اختیاری)", + "telegramServerUrlDescription": "برای کاهش محدودیت‌های بات تلگرام یا دسترسی در مناطقی که تلگرام فیلتر شده است (مثل ایران یا چین و ...). برای اطلاعات بیشتر {0} را ببینید. مقدار پیشفرض: {1}", + "Alphanumerical string and hyphens only": "فقط حروف الفبا، اعداد و -", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "اعلان‌های حساس به زمان در لحظه ارسال خواهند شد، حتی اگر دستگاه در حالت بدون مزاحمت قرار داشته باشد.", + "rabbitmqNodesRequired": "لطفا node های این مانیتور را تنظیم کنید.", + "RabbitMQ Password": "گذرواژه RabbitMQ", + "RabbitMQ Nodes": "node های مدیریت RabbitMQ", + "rabbitmqHelpText": "برای پایش، لازم است افزونه مدیریت (Management) در RabbitMQ را فعال کنید. برای اطلاعات بیشتر به {rabitmq_documentation} مراجعه کنید.", + "wayToWriteWahaChatId": "شماره موبایل در قالب بین‌المللی و بدون علامت مثبت ابتدایی ({0})، شناسه مخاطب ({1}) یا شناسه گروه ({2}). اعلان‌ها از نشست WAHA به این شناسه گفتگو ارسال خواهند شد.", + "wahaSession": "نشست", + "wahaChatId": "شناسه گفتگو (شماره موبایل / شناسه مخاطب / شناسه گروه)", + "wayToGetWahaSession": "با این نشست WAHA اعلان‌ها را به شناسه گفتگو ارسال میکند. قابل مشاهده در پنل کاربری WAHA.", + "Message Template": "قالب پیام", + "Template Format": "فرمت قالب", + "YZJ Webhook URL": "آدرس وب‌هوک YZJ", + "Fail": "شکست", + "Custom sound to override default notification sound": "نوای دلخواه به جای نوای پیشفرض اعلان", + "Time Sensitive (iOS Only)": "حساس به زمان (فقط iOS)", + "Can be found on:": "در {0} پیدا میشود", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "شناسه ارسال کننده متنی و در صورتی که میخواهید پاسخ‌ها را دریافت کنید، شماره موبایل در قالب E.164.", + "rabbitmqNodesDescription": "آدرس گره‌های مدیریت RabbitMQ را به همراه پروتکل و شماره پورت وارد کنید. مثال: {0}", + "RabbitMQ Username": "نام‌کاربری RabbitMQ", + "Separate multiple email addresses with commas": "آدرس‌های ایمیل را با استفاده از ویرگول انگلیسی یا کاما جدا کنید", + "Plain Text": "متن ساده", + "aboutSlackUsername": "نام نمایشی ارسال کننده پیام را تغییر میدهد. اگر میخواهید شخصی را نام ببرید، در قسمت نام دوستانه بنویسید.", + "Clear": "پاک‌سازی", + "templateServiceName": "نام خدمت", + "templateHostnameOrURL": "آدرس یا نام میزبان", + "templateStatus": "وضعیت", + "telegramUseTemplate": "استفاده از قالب پیام دلخواه", + "telegramUseTemplateDescription": "در صورت فعال‌سازی، پیام با قالب دلخواه ارسال خواهد شد.", + "telegramTemplateFormatDescription": "در تلگرام امکان استفاده از زبان‌های نشانه‌گذاری مختلفی وجود دارد، برای جزئیات بیشتر {0} را ببینید.", + "defaultFriendlyName": "پایشگر جدید", + "shrinkDatabaseDescriptionSqlite": "اجرای دستور {vacuum} برای پایگاه داده SQLite. گزینه {auto_vacuum} به‌صورت پیش‌فرض فعال است، اما مانند {vacuum} عمل یکپارچه‌سازی و بازآرایی صفحات پایگاه داده را انجام نمی‌دهد.", + "Use HTML for custom E-mail body": "استفاده از HTML برای محتوای سفارشی ایمیل", + "smseagleGroupV2": "شناسه(های) گروه دفترچه تلفن", + "smseagleTtsModel": "شناسه مدل تبدیل متن به گفتار", + "smseagleApiv2": "APIv2 (پیشنهاد شده برای یکپارچه‌سازی‌های جدید)", + "rabbitmqNodesInvalid": "لطفاً از یک آدرس URL کامل (که با 'http' شروع می‌شود) برای node های RabbitMQ استفاده کنید.", + "pingNumericDescription": "اگر فعال شود، آدرس‌های IP به‌جای نام‌های نمادین هاست نمایش داده خواهند شد", + "pingPerRequestTimeoutDescription": "این بیشترین زمان انتظار (بر حسب ثانیه) است پیش از آن‌که یک پکت پینگ به‌عنوان گم‌شده (packet lost) در نظر گرفته شود", + "smtpHelpText": "'SMTPS' بررسی می‌کند که SMTP/TLS به‌درستی کار می‌کند؛ 'Ignore TLS' اتصال را به‌صورت متن ساده (plaintext) برقرار می‌کند؛ 'STARTTLS' ابتدا اتصال برقرار کرده، سپس فرمان STARTTLS را ارسال می‌کند و سرتیفیکت سرور را بررسی می‌نماید. هیچ‌یک از این حالت‌ها ایمیلی ارسال نمی‌کنند.", + "smseagleContactV2": "شناسه(های) مخاطب دفترچه تلفن", + "smseagleMsgType": "نوع پیام", + "smseagleMsgSms": "پیامک (پیش‌فرض)", + "smseagleMsgRing": "شماره تماس", + "smseagleMsgTtsAdvanced": "تماس پیشرفته با تبدیل متن به گفتار", + "smseagleMsgTts": "تماس با تبدیل متن به گفتار", + "smseagleDuration": "مدت زمان (بر حسب ثانیه)", + "smseagleApiType": "نسخه API", + "smseagleApiv1": "APIv1 (برای پروژه‌های موجود و سازگاری با نسخه‌های قبلی)", + "smseagleDocs": "بررسی مستندات یا در دسترس بودن APIv2: ‏{0}", + "smseagleComma": "چند مقدار باید با کاما (,) از هم جدا شوند", + "SpugPush Template Code": "کد قالب", + "FlashDuty Push URL": "آدرس Push", + "FlashDuty Push URL Placeholder": "از صفحه یکپارچه‌سازی هشدارها کپی کنید", + "Send rich messages": "ارسال rich messages", + "Arcade": "Arcade (آرکید)", + "Harp": "Harp (چنگ)", + "pingCountLabel": "حداکثر پکت ها", + "pingCountDescription": "تعداد پکت هایی که قبل از توقف باید ارسال شوند", + "pingNumericLabel": "خروجی عددی", + "pingGlobalTimeoutLabel": "تایم اوت کلی", + "pingGlobalTimeoutDescription": "زمان کلی به ثانیه قبل از توقف پینگ، صرف‌نظر از تعداد پکت های ارسال‌شده", + "pingPerRequestTimeoutLabel": "تایم اوت برای هر پینگ", + "pingIntervalAdjustedInfo": "فاصله زمانی بر اساس تعداد پکت ها، تایم اوت کلی و تایم اوت برای هر پینگ تنظیم می‌شود", + "Custom URL": "آدرس سفارشی", + "customUrlDescription": "به‌ جای نشانی پایشگر، به‌عنوان پیوندقابل کلیک استفاده خواهد شد.", + "OneChatAccessToken": "توکن دسترسی OneChat", + "OneChatUserIdOrGroupId": "شناسه کاربر یا شناسه گروه OneChat", + "OneChatBotId": "شناسه بات OneChat", + "YZJ Robot Token": "توکن ربات YZJ", + "Disable URL in Notification": "غیرفعال‌سازی URL در اعلان", + "Font Twemoji by Twitter licensed under": "فونت Twemoji ساخت Twitter تحت لایسنس زیر منتشر شده است", + "smsplanetApiDocs": "اطلاعات دقیق درباره دریافت توکن‌های API را می‌توانید در {the_smsplanet_documentation} بیابید.", + "the smsplanet documentation": "مستندات SMSPlanet", + "Phone numbers": "شماره های تماس", + "Sender name": "نام ارسال کننده", + "smsplanetNeedToApproveName": "نیاز به تأیید در پنل کاربری دارد", + "smsplanetApiToken": "توکن برای API سرویس SMSPlanet", + "wayToGetWahaApiKey": "کلید API همان مقدار environment variable WHATSAPP_API_KEY است که هنگام اجرای WAHA استفاده کرده‌اید.", + "wayToGetWahaApiUrl": "آدرس URL اینستنس WAHA شما.", + "SendGrid API Key": "کلید API سرویس SendGrid", + "Add Tags": "افزودن برچسب ها", + "tagAlreadyOnMonitor": "این برچسب (نام و مقدار) قبلاً روی مانیتور قرار دارد یا در حال اضافه شدن است.", + "tagNameExists": "برچسب سیستمی با این نام از قبل وجود دارد. آن را از فهرست انتخاب کنید یا نام دیگری به‌کار ببرید.", + "Staged Tags for Batch Add": "برچسب‌های آماده برای افزودن به مجموعه", + "ntfyPriorityHelptextPriorityHigherThanDown": "اولویت عادی باید بالاتر از اولویت {0} باشد. اولویت {1} بالاتر از اولویت {0} اولویت {2} است", + "ntfyPriorityDown": "اولویت برای ایونت های DOWN", + "tagAlreadyStaged": "این برچسب (نام و مقدار) قبلاً برای این مجموعه آماده شده است.", + "Add Another Tag": "افزودن برچسب دیگر", + "Clear Form": "پاک کردن فرم", + "pause": "توقف", + "Happy Eyeballs algorithm": "الگوریتم Happy Eyeballs", + "Ip Family": "خانواده IP", + "ipFamilyDescriptionAutoSelect": "از {happyEyeballs} برای تعیین خانواده IP استفاده می‌کند.", + "Manual": "راهنما", + "Maximum Retries": "حداکثر تعداد تلاش مجدد", + "Number of retry attempts if webhook fails": "تعداد تلاش‌های مجدد (هر ۶۰ تا ۱۸۰ ثانیه) در صورت عدم موفقیت وبهوک.", + "HTTP Method": "متد HTTP", + "webhookPostMethodDesc": "متد POST برای بیشتر سرورهای HTTP مدرن مناسب است.", + "brevoToEmail": "به ایمیل", + "brevoBccEmail": "ایمیل رونوشت مخفی (BCC Email)", + "brevoSubject": "موضوع", + "brevoLeaveBlankForDefaultSubject": "برای موضوع پیشفرض خالی بگذارید", + "brevoApiKey": "کلید API بریوو (Brevo API Key)", + "Bot secret": "سیکرت بات", + "wayToGetBaleToken": "شما میتوانید از {0} یک توکن دریافت کنید.", + "mqttWebsocketPathInvalid": "لطفا از یک فرمت وب ساکت درست استفاده کنید", + "mqttHostnameTip": "لطفا از این فرمت استفاده کنید: {hostnameFormat}", + "clearAllEventsMsg": "آیا از پاک کردن تمامی رویداد ها مطمعنی؟", + "No monitors found": "هیچ پایشگری پیدا نشد.", + "Could not clear events": "نتوانستیم {failed}/{total} رویداد را پاک کنیم", + "Template plain text instead of using cards": "استفاده از متن ساده به‌جای کارت‌ها در قالب", + "issueWithGoogleChatOnAndroidHelptext": "این همچنین امکان دور زدن باگ‌های بالادست مانند {issuetackerURL} را فراهم می‌کند", + "Mention User List": "ذکر فهرست شناسه کاربر (User ID List)", + "Dingtalk Mobile List": "لیست موبایل", + "Dingtalk User List": "لیست شناسه کاربر (User ID)", + "Enter a list of userId": "یک فهرست از شناسه‌های کاربری (userId) وارد کنید", + "Invalid mobile": "شماره موبایل نامعتبر [{mobile}]", + "Invalid userId": "شناسه‌کاربری نامعتبر [{userId}]", + "OAuth Audience": "مخاطب اوآوث (OAuth Audience)", + "brevoApiHelp": "ایجاد یک کلید API در اینجا: {0}", + "brevoFromEmail": "ایمیل فرستنده (From Email)", + "brevoFromName": "از اسم", + "brevoLeaveBlankForDefaultName": "برای نام پیشفرض خالی بگذارید", + "Send UP silently": "ارسال UP به صورت بی صدا", + "Send DOWN silently": "ارسال DOWN به صورت بی صدا", + "Path": "مسیر", + "mqttWebSocketPath": "مسیر وب ساکت MQTT", + "mqttWebsocketPathExplanation": "مسیر وب ساکت برای MQTT روی اتصالات وب ساکت (مثال:‌/mqtt)", + "supportBaleChatID": "چت مستقیم پشتیبانی / گروه / چت آیدی چنل", + "Clear All Events": "پاک‌سازی تمامی رویداد‌ها", + "Events cleared successfully": "تمامی رویدادها با موفقیت پاک‌سازی شدند.", + "descriptionHelpText": "در داشبورد داخلی نمایش داده می‌شود. مارک‌داون مجاز است و پیش از نمایش، پاک‌سازی می‌شود (فاصله‌ها و تورفتگی‌ها را حفظ می‌کند).", + "wayToGetBaleChatID": "می‌توانید با فرستادن یک پیام به ربات و رفتن به این آدرس، شناسه چت (chat_id) خود را مشاهده کنید:", + "Mention Mobile List": "ذکر فهرست موبایل", + "Enter a list of mobile": "یک فهرست از شماره‌های موبایل وارد کنید", + "webhookGetMethodDesc": "متد GET داده‌ها را به‌صورت پارامترهای کوئری ارسال می‌کند و اجازه پیکربندی بدنه (body) را نمی‌دهد. برای فعال‌سازی مانیتورهای Uptime Kuma Push مفید است.", + "brevoCcEmail": "ایمیل رونوشت (CC Email)", + "brevoSeparateMultipleEmails": "برای چندین آدرس ایمیل، آن‌ها را با کاما جدا کنید", + "Nextcloud host": "هاست Nextcloud", + "Conversation token": "توکن گفتوگو (Conversation token)", + "wayToWriteEvolutionRecipient": "شماره تلفن با پیش‌شماره بین‌المللی، اما بدون علامت بعلاوه (+) در ابتدای آن ({0})، شناسه مخاطب ({1}) یا شناسه گروه ({2}).", + "evolutionRecipient": "شماره تلفن / شناسه مخاطب / شناسه گروه", + "evolutionInstanceName": "نام نمونه (Instance Name)", + "wayToGetEvolutionUrlAndToken": "می‌توانید آدرس API و توکن را با ورود به کانال موردنظر خود از {0} دریافت کنید", + "auto-select": "انتخاب خودکار", + "Optional: The audience to request the JWT for": "اختیاری: مخاطبی که برای آن JWT درخواست می‌شود", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "نصب یک بات Nextcloud Talk نیاز به دسترسی مدیریتی به سرور دارد.", + "Clone Maintenance": "کپی‌کردن نگهداری", + "ariaPauseMaintenance": "توقف این برنامه نگهداری", + "ariaResumeMaintenance": "ادامه این برنامه نگهداری", + "Template ID": "کد قالب", + "wayToGetClickSMSIRTemplateID": "قالب شما باید یک فیلد {uptkumaalert} داشته باشد. از {here} میتوانید یک قالب جدید ایجاد کنید.", + "Recipient Numbers": "شماره مشترکین", + "Metadata": "فراداده", + "End": "پایان", + "Pin this incident": "این رخداد را سنجاق کن", + "Loading...": "در حال بارگیری...", + "Load More": "بارگیری بیشتر", + "days": "{n} روز | {n} روز", + "hours": "{n} ساعت | {n} ساعت", + "minutes": "{n} دقیقه | {n} دقیقه", + "Monitors": "‏ {n} پایشگر| {n} پایشگرها", + "versionIs": "نگارش: {version}", + "monitorTypeGameServer": "کارساز بازی", + "monitorTypeDatabase": "پایش پایگاه‌داده", + "monitorTypeSpecial": "ویژه", + "notificationSmsServices": "پیامک خدمات", + "notificationPushServices": "خدمات پوش", + "unknownDays": "روزهای نامعلوم", + "enableSSL": "به کار گیری SSL/TLS", + "oracledbConnectionString": "پایگاه‌داده اوراکل: {connectionString}", + "No incidents recorded": "هنوز هیچ حادثه‌ای ثبت نشده است", + "years": "{n} سال | {n} سال", + "logoutCurrentUser": "از {username} خارج شو", + "minuteShort": "{n} دقیقه| {n} دقیقه", + "Jira Service Management": "مدیریت خدمات جیرا", + "twilioMessagingServiceSID": "شناسه سرویس پیام‌رسانی (اختیاری)", + "systemService": "خدمت سامانه", + "systemServiceName": "نام خدمت", + "Service Name": "نام خدمت", + "systemServiceDescription": "بررسی می‌کند که آیا خدمت سامانهٔ {service_name} فعال است", + "systemServiceDescriptionLinux": "بررسی می‌کند که آیا خدمت systemd لینوکس با نام {service_name} فعال است", + "systemServiceDescriptionWindows": "بررسی می‌کند که آیا خدمت {service_name} در مدیر خدمات ویندوز در حال اجراست", + "systemServiceCommandHint": "فرمان استفاده‌شده: {command}", + "systemServiceExpectedOutput": "خروجی موردانتظار: «{0}»", + "teltonikaUnsafeTlsDescription": "غیرفعال‌کردن اعتبارسنجی گواهی TLS شما را در معرض حملهٔ واسط (فرد میانی) قرار می‌دهد و ممکن است به افشای داده‌ها یا تسلط مهاجم بر سامانه‌ها بینجامد. اعتبارسنجی گواهی را فقط زمانی غیرفعال کنید که خطر این نوع حمله را می‌پذیرید. توصیه می‌کنیم از **Let’s Encrypt** با تمدید خودکار استفاده کنید.", + "mariadbUseSSLHelptext": "برای استفاده از اتصال رمزگذاری‌شده به پایگاه داده فعال کنید. این گزینه برای بیشتر پایگاه‌های دادهٔ ابری لازم است.", + "mariadbCaCertificateLabel": "گواهینامه‌های CA", + "mariadbCaCertificateHelptext": "برای استفاده از گواهی‌های خودامضا، گواهی مرجع صدور گواهی (CA) را با قالب PEM جای‌گذاری کنید. اگر پایگاه داده‌تان از گواهی صادرشده توسط یک مرجع صدور گواهی عمومی استفاده می‌کند، این بخش را خالی بگذارید.", + "Connection Timeout": "وقفه زمانی اتصال", + "retryOnlyOnStatusCodeFailureDescription": "اگر این گزینه فعال باشد، تلاش مجدد فقط زمانی انجام می‌شود که بررسی کد وضعیت HTTP ناموفق باشد (برای نمونه، کارساز از دسترس خارج شده باشد). اگر بررسی کد وضعیت موفق باشد، اما پرس‌وجوی JSON ناموفق شود، پایشگر بی‌درنگ ازکارافتاده علامت‌گذاری می‌شود و تلاش مجددی انجام نمی‌شود.", + "ignoreSecWebsocketAcceptHeaderDescription": "اگر ارتقای اتصال وب‌سوکت موفق باشد، به کارساز اجازه می‌دهد سرآیند `Sec-WebSocket-Accept` را در پاسخ ارسال نکند.", + "wsCodeDescription": "برای اطلاعات بیشتر دربارهٔ کدهای وضعیت، به {rfc6455} مراجعه کنید", + "Subprotocol(s)": "زیرشیوه‌نامه(s)", + "responseMaxLengthDescription": "بیشترین اندازهٔ دادهٔ پاسخ که ذخیره می‌شود. برای نامحدودکردن اندازه، مقدار را روی 0 بگذارید. پاسخ‌های بزرگ‌تر کوتاه‌سازی می‌شوند. مقدار پیش‌فرض: ۱۰۲۴ (۱ کیلوبایت)", + "Pinned incidents are shown prominently on the status page": "رخدادهای سنجاق‌شده در صفحهٔ وضعیت برجسته‌تر نمایش داده می‌شوند", + "Edit Incident": "ویرایش حادثه", + "templateAvailableVariables": "متغیرهای موجود", + "example": "مثال", + "Result": "نتیجه", + "HeadersInvalidFormatBecause": "سرآیندهای درخواست به‌دلیل {error}، JSON معتبر نیستند", + "BodyInvalidFormatBecause": "بدنهٔ درخواست به‌دلیلِ {error}، JSON معتبر نیست", + "steamApiKeyDescriptionAt": "برای پایش کارساز بازی استیم، به کلید API وب Steam نیاز دارید. می‌توانید کلید API خود را در {url} ثبت کنید", + "createdAt": "تاریخ ایجاد: {date}", + "lastUpdatedAt": "آخرین به‌روزرسانی: {date}", + "lastUpdatedAtFromNow": "آخرین به‌روزرسانی: {date} ({fromNow})", + "Actions": "اقدامات", + "selectedMonitorCountMsg": "انتخاب‌شده: {n} | انتخاب‌شده: {n}", + "selectMonitorMsg": "برای انجام عملیات، پایشگرها را انتخاب کنید", + "selectAllMonitorsAria": "انتخاب همهٔ پایشگرها", + "deselectAllMonitorsAria": "برداشتن همهٔ پایشگرها", + "deleteIncidentMsg": "آیا مطمئن هستید که می‌خواهید این رویداد را بردارید؟", + "expectedTlsAlertDescription": "هشدار TLS موردانتظار از کارساز را انتخاب کنید. برای بررسی اینکه نقاط پایانی mTLS اتصال‌های فاقد گواهی‌نامهٔ کارخواه را رد می‌کنند، از {code} استفاده کنید. برای جزئیات بیشتر، به {link} مراجعه کنید.", + "TLS Alert Spec": "RFC 8446", + "mariadbSocketPathDetectedHelptext": "اتصال به پایگاه داده با مشخصاتی که از طریق متغیر محیطی {0} تعیین شده است.", + "Expand All Groups": "گسترش همه گروه‌ها", + "Collapse All Groups": "بستن همه گروه‌ها", + "Webhook Payload Fields": "فیلدهای محتوای وب‌هوک", + "halopsa_payload_desc": "فیلدهای زیر به وب‌هوک Halo PSA شما ارسال می‌شوند:", + "halopsa_field_title": "عنوان هشدار (همیشه «Uptime Kuma Alert»)", + "halopsa_field_status": "وضعیت پایش: UP، DOWN، NOTIFICATION یا UNKNOWN", + "halopsa_field_monitor": "نام پایشگر", + "halopsa_field_monitor_id": "شناسهٔ یکتای پایشگر (برای اعلان‌های آزمایشی `null` است) — از این شناسه برای پیوند دادن هشدارها به پشتیبانی‌ها استفاده کنید", + "halopsa_field_message": "متن کامل هشدار، شامل وضعیت و جزئیات", + "halopsa_field_timestamp": "مهر زمانی رویداد با فرمت ISO 8601", + "halopsa_field_uptime_kuma_version": "شماره نسخه پایشگرکوما", + "halopsa_id_usage_hint": "💡 نکته: برای پیوند مطمئن هشدارها به پشتیبانی‌ها از `monitor_id` و برای پیگیری تاریخچهٔ رویدادها از `heartbeat_id` استفاده کنید", + "halopsa_setup_step5": "ران‌بوک را طوری پیکربندی کنید که برای تطبیق هشدارها با پشتیبانی‌های موجود از `monitor_id` استفاده کند", + "Teltonika SMS Gateway": "درگاه پیامک Teltonika", + "teltonikaVersionWarning": "این ارائه‌دهندهٔ اعلان مستلزم آن است که دستگاه Teltonika شما از نسخهٔ 7.14.0 یا بالاتر RMS استفاده کند.", + "teltonikaUrl": "نشانی دستگاه Teltonika شما", + "teltonikaUrlHelptext": "نشانی باید به‌صورت مبدأ کامل وارد شود؛ برای مثال: {0} یا {1}.", + "teltonikaUnsafeTls": "اعتبارسنجی گواهی‌نامه را نادیده بگیرید", + "teltonikaUsername": "نام‌کاربری API", + "teltonikaUsernameHelptext": "پیشنهاد: حساب کاربری جداگانه‌ای بسازید که فقط مجاز به ارسال پیامک باشد و نام کاربری آن را اینجا وارد کنید", + "teltonikaPassword": "گذرواژهٔ API", + "teltonikaPasswordHelptext": "می‌توانید گذرواژهٔ کاربر API را در روتر Teltonika خود تعیین کنید؛ برای مثال: {0}", + "teltonikaModem": "شناسه مودم", + "teltonikaModemHelptext": "شناسهٔ مودم پیامک؛ باید در قالب {0} باشد. برای راهنمایی به https://developers.teltonika-networks.com/reference/ مراجعه کنید.", + "teltonikaPhoneNumber": "شمارهٔ تلفن", + "teltonikaPhoneNumberHelptext": "شماره باید با قالب بین‌المللی {0} یا {1} وارد شود. فقط واردکردن یک شماره مجاز است.", + "ntpThresholdsTitle": "آستانه‌های NTP", + "ntpStratumThreshold": "آستانهٔ استراتوم", + "ntpStratumThresholdHelp": "اگر مقدار استراتوم برابر یا بیشتر از این مقدار بود، وضعیت را قطع‌شده علامت بزنید. استراتوم کیفیت منبع زمان را نشان می‌دهد (۱ = GPS/ساعت اتمی، ۱۶ = همگام‌نشده).", + "ntpOffsetThreshold": "آستانهٔ اختلاف زمانی (میلی‌ثانیه)", + "ntpOffsetThresholdHelp": "اگر اختلاف زمانی برحسب میلی‌ثانیه از این مقدار بیشتر شد، وضعیت را قطع‌شده علامت بزنید.", + "ntpDispersionThreshold": "آستانه پراکندگی ریشه (میلی‌ثانیه)", + "milkyMessageType": "نوع پیام Milky نوع پیام Milky", + "milkyGroupMessage": "گروه", + "milkyPrivateMessage": "خصوصی", + "milkyUserOrGroupId": "گروه/شناسه کاربر", + "milkySafetyTips": "برای حفظ امنیت، باید توکن دسترسی تنظیم شود", + "smsgatewayTo": "شماره تلفن (ها)", + "smsgatewayToHelptext": "فهرستی از شماره‌تلفن‌های گیرندگان با فرمت بین‌المللی (مثلاً 15551234567+) که با , از هم جدا شده‌اند", + "Line Number": "شماره سطر", + "Use Pattern": "الگوی استفاده", + "Use Own Line for Pattern": "برای الگو از خط اختصاصی استفاده کنید" +} diff --git a/src/lang/fi.json b/src/lang/fi.json new file mode 100644 index 0000000..34a9ab5 --- /dev/null +++ b/src/lang/fi.json @@ -0,0 +1,1217 @@ +{ + "Dashboard": "Kojetaulu", + "Help": "Apua", + "New Update": "Uusi päivitys", + "Language": "Kieli", + "Appearance": "Ulkonäkö", + "Theme": "Teema", + "General": "Yleinen", + "Game": "Peli", + "Version": "Versio", + "List": "Lista", + "Add": "Lisää", + "Add New Monitor": "Lisää uusi seurain", + "Quick Stats": "Pikakatsaus", + "Up": "Toimii", + "Down": "Alas", + "Pending": "Odottaa", + "statusMaintenance": "Ylläpito", + "Maintenance": "Ylläpito", + "Unknown": "Tuntematon", + "General Monitor Type": "Yleinen seuraintyyppi", + "Passive Monitor Type": "Passiivinen seuraintyyppi", + "markdownSupported": "Markdown-syntaksi tuettu. Jos käytät HTMLää, vältä välilyöntejä tekstin alussa, välttääksesi muotoilu-ongelmat.", + "pauseDashboardHome": "Tauko", + "Pause": "Tauko", + "Name": "Nimi", + "Status": "Tila", + "DateTime": "Päivämäärä", + "Message": "Viesti", + "No important events": "Ei tärkeitä tapahtumia", + "Resume": "Jatka", + "Edit": "Muokkaa", + "Delete": "Poista", + "Current": "Nykyinen", + "Uptime": "Päälläoloaika", + "Monitor": "Seurain | Seuraimet", + "day": "Päivä | Päivää", + "-day": "-päivä(ä)", + "hour": "tunti", + "-hour": "-tunti(a)", + "Response": "Vastaus", + "Ping": "Ping", + "Monitor Type": "Seuraintyyppi", + "Keyword": "Avainsana", + "URL": "URL", + "Hostname": "Isäntänimi", + "Heartbeat Interval": "Sydämensykkeiden aikaväli", + "Retries": "Uusintayrityksiä", + "Resend Notification if Down X times consequently": "Uudelleen lähetä ilmoitus jos ei vastausta X määrä peräkkäin", + "Advanced": "Edistynyt", + "checkEverySecond": "Tarkista joka {0} sekunti", + "retryCheckEverySecond": "Yritä uudelleen joka {0} sekunti", + "resendEveryXTimes": "Lähetä uudelleen joka {0} kerta", + "ignoreTLSError": "Älä huomioi TLS/SSL virheitä HTTPS nettisivuilla", + "upsideDownModeDescription": "Käännä tila vastakkaiseksi. Jos palvelu on saavutettavissa, merkitse se SAMMUNEEKSI.", + "maxRedirectDescription": "Enimmäismäärä seurattavia uudelleenohjauksia. Aseta arvoksi 0 estääksesi uudelleenohjaukset.", + "Upside Down Mode": "Käänteinen tila", + "Max. Redirects": "Uudelleenohjauksia enintään", + "Accepted Status Codes": "Sallitut tilakoodit", + "Push URL": "Push-ilmoitus URL", + "Save": "Tallenna", + "Notifications": "Ilmoitukset", + "Setup Notification": "Määritä ilmoitukset", + "Light": "Vaalea", + "Dark": "Tumma", + "Auto": "Automaattinen", + "Theme - Heartbeat Bar": "Teema - Sydämensykepalkki", + "Normal": "Normaali", + "Bottom": "Alhaalla", + "None": "Ei mitään", + "Timezone": "Aikavyöhyke", + "Search Engine Visibility": "Hakukonenäkyvyys", + "Allow indexing": "Salli indeksointi", + "Change Password": "Vaihda salasana", + "Current Password": "Nykyinen salasana", + "New Password": "Uusi salasana", + "Repeat New Password": "Toista uusi salasana", + "Update Password": "Päivitä salasana", + "Disable Auth": "Poista todennus käytöstä", + "Enable Auth": "Ota todennus käyttöön", + "Logout": "Kirjaudu ulos", + "Leave": "Poistu", + "I understand, please disable": "Ymmärrän, poista käytöstä", + "Confirm": "Hyväksy", + "Yes": "Kyllä", + "No": "Ei", + "Username": "Käyttäjänimi", + "Password": "Salasana", + "Login": "Kirjaudu", + "add one": "lisää", + "Notification Type": "Ilmoitustyyppi", + "Email": "Sähköposti", + "Test": "Testi", + "Certificate Info": "Varmenteen tiedot", + "Settings": "Asetukset", + "Check Update On GitHub": "Tarkista päivitys GitHub:ssa", + "Specific Monitor Type": "Tietty seuraintyyppi", + "Cert Exp.": "Varmenne vanh.", + "Friendly Name": "Ystävällinen nimi", + "Port": "Portti", + "Heartbeat Retry Interval": "Sydämensykkeen uudelleenyritysten aikaväli", + "resendDisabled": "Uudelleenlähetys poissa käytöstä", + "retriesDescription": "Enimmäismäärä uudelleenyrityksiä ennen kuin palvelu merkitään sammuneeksi ja ilmoitus lähetetään", + "Discourage search engines from indexing site": "Estä hakukoneita indeksoimasta sivua", + "Please use this option carefully!": "Käytä tätä vaihtoehtoa varoen!", + "Remember me": "Muista minut", + "languageName": "Suomi", + "Primary Base URL": "Ensisijainen perus-URL", + "pushOptionalParams": "Valinnaiset parametrit: {0}", + "Not available, please setup.": "Ei saatavilla, määritä ensin.", + "needPushEvery": "Sinun tulee kutsua tätä URLia joka {0} sekunti.", + "disableauth.message1": "Oletko varma että haluat {disableAuth}?", + "disable authentication": "poistaa todennuksen käytöstä", + "disableauth.message2": "Se on suunniteltu tilanteisiin {intendThirdPartyAuth} Uptime Kuma:n edessä, kuten Cloudflare Access, Authelia tai jotain muuta todennus mekanismia.", + "where you intend to implement third-party authentication": "jossa aiot käyttää kolmannen osapuolen todennnusta", + "No Monitors, please": "Ei seuraimia, ole hyvä", + "Resolver Server": "Nimipalvelin", + "Resource Record Type": "Tietueen tyyppi (RR)", + "Last Result": "Viimeisin tulos", + "Create your admin account": "Luo ylläpitäjän käyttäjätili", + "Repeat Password": "Toista salasana", + "Import Backup": "Tuo varmuuskopio", + "Export Backup": "Vie varmuuskopio", + "Export": "Vie", + "Import": "Tuo", + "respTime": "Vast. aika (ms)", + "notAvailableShort": "Ei käytössä", + "Default enabled": "Oletuksena käytössä", + "Apply on all existing monitors": "Aseta jokaiselle olemassa olevalle seuraimelle", + "Create": "Luo", + "Clear Data": "Tyhjennä tiedot", + "Events": "Tapahtumat", + "Heartbeats": "Sydämensyke", + "Auto Get": "Automaattinen haku", + "Schedule maintenance": "Aikatauluta huolto", + "Affected Monitors": "Vaikutetut seuraimet", + "Pick Affected Monitors...": "Poimi vaikutetut seuraimet…", + "Start of maintenance": "Huollon aloitus", + "All Status Pages": "Kaikki tilasivut", + "Select status pages...": "Valitse tilasivu…", + "alertNoFile": "Valitse tuotava tiedosto.", + "alertWrongFileType": "Valitse JSON-tiedosto.", + "Clear all statistics": "Tyhjennä kaikki tilastot", + "Skip existing": "Ohita olemassa oleva", + "Overwrite": "Päällekirjoita", + "Options": "Vaihtoehdot", + "Keep both": "Pidä molemmat", + "Verify Token": "Vahvista tunnus", + "Setup 2FA": "Määritä 2FA", + "Enable 2FA": "Ota 2FA käyttöön", + "Disable 2FA": "Poista 2FA käytöstä", + "2FA Settings": "2FA-asetukset", + "Two Factor Authentication": "Kaksivaiheinen tunnistautuminen (2FA)", + "Active": "Aktiivinen", + "Token": "Token", + "Show URI": "Näytä URI", + "Tags": "Tunnisteet", + "Tag with this name already exist.": "Tunniste tällä nimellä on jo olemassa.", + "Tag with this value already exist.": "Tunniste tällä arvolla on jo olemassa.", + "color": "Väri", + "value (optional)": "arvo (valinnainen)", + "Gray": "Harmaa", + "Red": "Punainen", + "Orange": "Oranssi", + "Green": "Vihreä", + "Indigo": "Indigo", + "Purple": "Purppura", + "Pink": "Vaaleanpunainen", + "Custom": "Mukautettu", + "Search...": "Etsi…", + "Avg. Ping": "Kesk.arv. viive", + "Entry Page": "Sisääntulosivu", + "statusPageNothing": "Täällä ei ole mitään. Lisää ryhmä tai seurain.", + "No Services": "Ei palveluita", + "Partially Degraded Service": "Osittain heikentynyt palvelu", + "Degraded Service": "Heikentynyt palvelu", + "Add Group": "Lisää ryhmä", + "Add a monitor": "Lisää seurain", + "Edit Status Page": "Muokkaa tilasivua", + "Go to Dashboard": "Mene kojelaudalle", + "Status Page": "Tilasivu", + "Status Pages": "Tilasivut", + "here": "täällä", + "Required": "Vaadittu", + "webhook": "Webhookki", + "Post URL": "Lähetys-URL", + "Content Type": "Sisältötyyppi", + "webhookJsonDesc": "{0} soveltuu mille tahansa modernille HTTP-palvelimille kuten Express.js", + "webhookAdditionalHeadersTitle": "Lisäotsakkeet", + "webhookAdditionalHeadersDesc": "Asettaa lisäotsakkeet, jotka lähetetään webhookille. Jokainen lisäotsake tulisi määritellä JSON-avaimena ja -arvona.", + "Webhook URL": "Webhookin URL", + "Application Token": "Sovellus-token", + "Server URL": "Palvelimen URL", + "Priority": "Prioriteetti", + "emojiCheatSheet": "Emoji lunttilappu: {0}", + "Read more": "Lue lisää", + "appriseInstalled": "Apprise on asennettu.", + "appriseNotInstalled": "Apprisea ei ole asennettu. {0}", + "Method": "Menetelmä", + "Body": "Runko", + "Headers": "Otsikot", + "PushUrl": "Push-ilmoitus URL", + "BodyInvalidFormat": "Pyynnön sisältörunko ei ole kelvollisessa JSON-muodossa: ", + "Monitor History": "Seurannan historia", + "PasswordsDoNotMatch": "Salasanat eivät täsmää.", + "records": "tallenteet", + "One record": "Yksi tallenne", + "Current User": "Nykyinen käyttäjä", + "topic": "Aihe", + "topicExplanation": "MQTT seurattava aihe", + "successMessage": "Onnistumis viesti", + "successMessageExplanation": "MQTT-viesti, jota pidetään onnistuneena", + "recent": "Viimeaikainen", + "Done": "Tehty", + "Info": "Tiedot", + "Security": "Turvallisuus", + "Steam API Key": "Steam API-avain", + "Shrink Database": "Pienennä tietokanta", + "Pick a RR-Type...": "Valitse tietuetyyppi (RR)…", + "Pick Accepted Status Codes...": "Valitse hyväksytyt tilakoodit…", + "Default": "Oletus", + "HTTP Options": "HTTP-valinnat", + "Create Incident": "Luo tapaus", + "Title": "Otsikko", + "Content": "Sisältö", + "Style": "Tyyli", + "info": "tiedote", + "warning": "varoitus", + "danger": "vaara", + "error": "virhe", + "critical": "kriittinen", + "primary": "ensisijainen", + "dark": "tumma", + "Post": "Lähetä", + "Please input title and content": "Syötä otsikko ja sisältö", + "Created": "Luotu", + "Last Updated": "Viimeksi päivitetty", + "Unpin": "Irroita", + "Switch to Dark Theme": "Vaihda tummaan teemaan", + "Show Tags": "Näytä tunnisteet", + "Hide Tags": "Piilota tunnisteet", + "Description": "Kuvaus", + "No monitors available.": "Ei seuraimia saatavilla.", + "Add one": "Lisää yksi", + "No Monitors": "Ei seuraimia", + "Untitled Group": "Nimetön ryhmä", + "Services": "Palvelut", + "Discard": "Peruuta", + "Cancel": "Peruuttaa", + "Customize": "Mukauta", + "Custom Footer": "Mukautettu alatunniste", + "Custom CSS": "Mukautettu CSS", + "deleteStatusPageMsg": "Haluatko varmasti poistaa tämän tilasivun?", + "Proxies": "Välityspalvelimet", + "default": "Oletus", + "enabled": "Käytössä", + "setAsDefault": "Aseta oletukseksi", + "deleteProxyMsg": "Haluatko varmasti poistaa tämän välityspalvelimen kaikista seuraimista?", + "proxyDescription": "Välityspalvelimet on määritettävä seuraimelle toimiakseen.", + "enableProxyDescription": "Tämä välityspalvelin ei vaikuta valvontapyyntöihin ennen kuin se on aktivoitu. Voit hallita välityspalvelimen väliaikaista poistamista käytöstä kaikista seuraimista aktivointitilan perusteella.", + "setAsDefaultProxyDescription": "Tämä välityspalvelin on oletuksena käytössä uusissa seuraimissa. Voit silti poistaa välityspalvelimen käytöstä erikseen jokaisesta seuraimesta.", + "Certificate Chain": "Varmenneketju", + "Valid": "Voimassa oleva", + "Invalid": "Pätemätön", + "User": "Käyttäjä", + "Installed": "Asennettu", + "Not installed": "Ei asennettu", + "Running": "Käynnissä", + "Not running": "Ei käynnissä", + "Remove Token": "Poista token", + "Start": "Käynnistä", + "Stop": "Pysäytä", + "Add New Status Page": "Lisää uusi tilasivu", + "Slug": "Slug", + "startOrEndWithOnly": "Aloita tai lopeta vain {0}", + "No consecutive dashes": "Ei peräkkäisiä viivoja", + "Next": "Seuraava", + "No Proxy": "Ei välityspalvelinta", + "Authentication": "Todennus", + "HTTP Basic Auth": "HTTP-perustodennus", + "Page Not Found": "Sivua ei löydetty", + "Reverse Proxy": "Käänteinen välityspalvelin", + "Backup": "Varmuuskopio", + "About": "Tietoja", + "cloudflareWebsite": "Cloudflare verkkosivusto", + "Message:": "Viesti:", + "Don't know how to get the token? Please read the guide:": "Etkö tiedä kuinka saada tunnus? Ole hyvä ja lue opas:", + "HTTP Headers": "HTTP-otsikot", + "Trust Proxy": "Luota välityspalvelimeen", + "Other Software": "Muut ohjelmistot", + "For example: nginx, Apache and Traefik.": "Esimerkiksi: nginx, Apache ja Traefik.", + "Please read": "Ole hyvä ja lue", + "Subject:": "Aihe:", + "Valid To:": "Voimassa:", + "Days Remaining:": "Päiviä jäljellä:", + "Issuer:": "Myöntäjä:", + "Fingerprint:": "Sormenjälki:", + "No status pages": "Ei tilasivuja", + "Domain Name Expiry Notification": "Verkkotunnuksen vanhenemisilmoitus", + "Proxy": "Välityspalvelin", + "Date Created": "Luomispäivämäärä", + "Footer Text": "Alatunnisteen teksti", + "Show Powered By": "Näytä \"Voimanlähteenä\"", + "Domain Names": "Verkkotunnukset", + "signedInDisp": "Kirjautunut sisään käyttäjänä {0}", + "signedInDispDisabled": "Todennus poistettu käytöstä.", + "RadiusSecretDescription": "Asiakkaan ja palvelimen välinen yhteinen salaisuus", + "RadiusCalledStationIdDescription": "Kutsutun laitteen tunniste", + "RadiusCallingStationId": "Kutsuaseman tunnus", + "Certificate Expiry Notification": "Varmenteen vanhenemisilmoitus", + "API Username": "API-käyttäjänimi", + "API Key": "API-avain", + "Show update if available": "Näytä päivitys, jos saatavilla", + "Also check beta release": "Tarkista myös beta-versio", + "Using a Reverse Proxy?": "Käytätkö käänteistä välityspalvelinta?", + "The slug is already taken. Please choose another slug.": "Slug on jo otettu. Ole hyvä ja valitse toinen slug.", + "RadiusSecret": "Radius-salaisuus", + "RadiusCalledStationId": "Kutsuttu aseman tunnus", + "Steam Game Server": "Steam-pelipalvelin", + "Most likely causes:": "Todennäköisimmät syyt:", + "The resource is no longer available.": "Resurssi ei ole enää saatavilla.", + "There might be a typing error in the address.": "Osoitteessa saattaa olla kirjoitusvirhe.", + "What you can try:": "Mitä voit kokeilla:", + "Retype the address.": "Kirjoita osoite uudelleen.", + "Go back to the previous page.": "Palaa edelliselle sivulle.", + "Coming Soon": "Tulossa pian", + "Connection String": "Yhteysmerkkijono", + "Query": "Tiedustelu (Query)", + "settingsCertificateExpiry": "TLS-varmenteen vanheneminen", + "certificationExpiryDescription": "HTTPS-seuraimet käynnistävät ilmoituksen, kun TLS-varmenne vanhenee:", + "Setup Docker Host": "Määritä Docker-isäntä", + "Connection Type": "Yhteystyyppi", + "tcp": "TCP / HTTP", + "Docker Container": "Docker-kontti", + "Container Name / ID": "Säilön nimi/tunnus", + "Docker Host": "Docker-isäntä", + "Docker Hosts": "Docker-isännät", + "Domain": "Verkkotunnus", + "Workstation": "Työasema", + "socket": "Socket", + "Packet Size": "Paketin koko", + "telegram": "Telegram", + "ZohoCliq": "ZohoCliq", + "Bot Token": "Botin tokeni", + "wayToGetTelegramToken": "Voit saada tunnuksen osoitteesta {0}.", + "Chat ID": "Chat-tunnus", + "wayToGetTelegramChatID": "Saat chat-tunnuksesi lähettämällä viestin botille ja siirtymällä tähän URL-osoitteeseen nähdäksesi chat_id:", + "YOUR BOT TOKEN HERE": "BOTIN TOKEN TÄHÄN", + "chatIDNotFound": "Chat ID:tä ei löydy; lähetä ensin viesti tälle botille", + "disableCloudflaredNoAuthMsg": "Olet No Auth -tilassa, salasanaa ei tarvita.", + "trustProxyDescription": "Luota \"X-Forwarded-*\"-otsikoihin. Jos haluat saada oikean asiakas-IP:n ja Uptime Kumasi on välityspalvelimen, kuten Nginx tai Apache, takana, sinun tulee ottaa tämä käyttöön.", + "wayToGetLineNotifyToken": "Voit saada käyttötunnuksen osoitteesta {0}", + "Examples": "Esimerkkejä", + "Home Assistant URL": "Home Assistantin URL-osoite", + "Long-Lived Access Token": "Pitkäikäinen pääsytunnus", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Pitkäkestoisen käyttöoikeustunnuksen voi luoda napsauttamalla vasemmassa alakulmassa olevaa profiilinimeäsi, vierittämällä sivun loppuun ja valitsemalla sitten Luo tunnus.", + "Notification Service": "Ilmoituspalvelu", + "default: notify all devices": "oletus: ilmoita kaikille laitteille", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Luettelo ilmoituspalveluista löytyy Home Assistantin kohdasta \"Kehittäjätyökalut > Palvelut\". Hae hakusanalla \"ilmoitus\" löytääksesi laitteesi/puhelimesi nimen.", + "Automations can optionally be triggered in Home Assistant:": "Automaatiot voidaan vaihtoehtoisesti laukaista Home Assistantissa:", + "Trigger type:": "Triggerin tyyppi:", + "Event type:": "Tapahtumatyyppi:", + "Frontend Version": "Käyttöliittymän versio", + "Frontend Version do not match backend version!": "Käyttöliittymän versio ei vastaa taustaversiota!", + "backupRecommend": "Varmuuskopioi asema tai tietokansio (./data/) suoraan sen sijaan.", + "Optional": "Valinnainen", + "squadcast": "Squadcast", + "or": "tai", + "recurringInterval": "Aikaväli", + "Recurring": "Toistuva", + "strategyManual": "Aktiivinen/ei-aktiivinen manuaalisesti", + "warningTimezone": "Se käyttää palvelimen aikavyöhykettä", + "weekdayShortMon": "Ma", + "weekdayShortTue": "Ti", + "weekdayShortWed": "Ke", + "weekdayShortThu": "To", + "weekdayShortFri": "Pe", + "weekdayShortSat": "La", + "weekdayShortSun": "Su", + "dayOfWeek": "Viikonpäivä", + "dayOfMonth": "Kuukauden päivä", + "lastDay": "Viimeinen päivä", + "lastDay1": "Kuukauden viimeinen päivä", + "lastDay2": "Kuukauden toiseksi viimeinen päivä", + "lastDay3": "Kuukauden 3. viimeinen päivä", + "No Maintenance": "Ei huoltoa", + "pauseMaintenanceMsg": "Haluatko varmasti keskeyttää?", + "maintenanceStatus-under-maintenance": "Huollossa", + "maintenanceStatus-inactive": "Ei-aktiivinen", + "maintenanceStatus-scheduled": "Aikataulutettu", + "maintenanceStatus-ended": "Päättyi", + "maintenanceStatus-unknown": "Tuntematon", + "Display Timezone": "Käyttöliittymän aikavyöhyke", + "Server Timezone": "Palvelimen aikavyöhyke", + "statusPageMaintenanceEndDate": "Loppu", + "Enable": "Ota käyttöön", + "Disable": "Poista käytöstä", + "Single Maintenance Window": "Yksi huoltoikkuna", + "Maintenance Time Window of a Day": "Päivän huoltoaikaikkuna", + "Effective Date Range": "Voimassa oleva ajanjakso (valinnainen)", + "Schedule Maintenance": "Ajoita huolto", + "Date and Time": "Päivämäärä ja aika", + "DateTime Range": "Päivämäärä- ja aika-alue", + "loadingError": "Tietoja ei voi noutaa, yritä myöhemmin uudelleen.", + "plugin": "Lisäosa | Lisäosat", + "install": "Asenna", + "installing": "Asennetaan", + "uninstall": "Poista asennus", + "uninstalling": "Poistetaan asennusta", + "smtp": "Sähköposti (SMTP)", + "secureOptionNone": "Ei mitään / STARTTLS (25 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ohita TLS-virhe", + "From Email": "Sähköpostista", + "emailCustomSubject": "Mukautettu aihe", + "To Email": "Sähköpostiin", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord Webhookin URL-osoite", + "Bot Display Name": "Botin näyttönimi", + "Prefix Custom Message": "Mukautetun viestin etuliite", + "Hello @everyone is...": "Hei {'@'}kaikki ovat…", + "wayToGetTeamsURL": "Voit oppia luomaan webhookin URL-osoitteen {0}.", + "wayToGetZohoCliqURL": "Voit oppia luomaan webhookin URL-osoitteen {0}.", + "wayToCheckSignalURL": "Voit tarkistaa tämän URL-osoitteen nähdäksesi, kuinka se määritetään:", + "Number": "Numero", + "Recipients": "Vastaanottajat", + "Access Token": "Käyttöoikeustunnus", + "Channel access token": "Kanavan käyttöoikeustunnus", + "Basic Settings": "Perusasetukset", + "User ID": "Käyttäjätunnus", + "Messaging API": "Viestintä-API", + "Line Developers Console": "Line Kehittäjän Konsoli", + "lineDevConsoleTo": "Line Kehittäjän Konsoli - {0}", + "dataRetentionTimeError": "Säilytysajan on oltava 0 tai suurempi", + "infiniteRetention": "Aseta arvoksi 0, jos haluat ikuisen säilytyksen.", + "confirmDeleteTagMsg": "Haluatko varmasti poistaa tämän tunnisteen? Tähän tunnisteeseen liittyviä näyttöjä ei poisteta.", + "enableGRPCTls": "Salli lähettää gRPC-pyyntö TLS-yhteydellä", + "grpcMethodDescription": "Menetelmän nimi muunnetaan camelCase-muotoon, kuten sayHello, check jne.", + "acceptedStatusCodesDescription": "Valitse tilakoodit, jotka katsotaan onnistuneeksi vastaukseksi.", + "deleteMonitorMsg": "Haluatko varmasti poistaa tämän seuraimen?", + "deleteMaintenanceMsg": "Haluatko varmasti poistaa tämän huollon?", + "deleteNotificationMsg": "Haluatko varmasti poistaa tämän ilmoituksen kaikista seuraimista?", + "dnsPortDescription": "DNS-palvelimen portti. Oletusarvo on 53. Voit vaihtaa porttia milloin tahansa.", + "rrtypeDescription": "Valitse valvottava RR-tyyppi", + "pauseMonitorMsg": "Haluatko varmasti keskeyttää?", + "clearHeartbeatsMsg": "Haluatko varmasti poistaa kaikki tämän monitorin syketiedot? Tämä poistaa kaikki syketiedot pysyvästi, eikä toimintoa voi perua.", + "confirmImportMsg": "Haluatko varmasti tuoda varmuuskopion? Varmista, että olet valinnut oikean tuontivaihtoehdon.", + "twoFAVerifyLabel": "Anna tunnuksesi vahvistaaksesi 2FA:", + "tokenValidSettingsMsg": "Tunnus on voimassa! Voit nyt tallentaa 2FA-asetukset.", + "confirmEnableTwoFAMsg": "Haluatko varmasti ottaa 2FA:n käyttöön?", + "confirmDisableTwoFAMsg": "Haluatko varmasti poistaa 2FA:n käytöstä?", + "recurringIntervalMessage": "Juokse kerran päivässä | Suorita kerran {0} päivässä", + "affectedMonitorsDescription": "Valitse seuraimet, joihin nykyinen huolto vaikuttaa", + "affectedStatusPages": "Näytä tämä huoltoviesti valituilla tilasivuilla", + "atLeastOneMonitor": "Valitse vähintään yksi seurain, johon vaikuttaa", + "notificationDescription": "Ilmoitukset on määritettävä seuraimelle toimiakseen.", + "keywordDescription": "Hae avainsanaa tavallisessa HTML- tai JSON-vastauksessa. Haussa kirjainkoolla on merkitystä.", + "backupDescription": "Voit varmuuskopioida kaikki näytöt ja ilmoitukset JSON-tiedostoon.", + "backupDescription2": "Huomaa: historia- ja tapahtumatiedot eivät sisälly.", + "octopushLogin": "\"Kirjaudu\" ohjauspaneelin HTTP API -tunnistetiedoista", + "promosmsLogin": "API-kirjautumisnimi", + "promosmsPassword": "API-salasana", + "pushoversounds pushover": "Pushover (oletus)", + "pushoversounds bike": "Pyörä", + "pushoversounds bugle": "Merkkitorvi", + "pushoversounds cashregister": "Kassakone", + "pushoversounds classical": "Klassinen", + "pushoversounds cosmic": "Kosminen", + "pushoversounds falling": "Putoaminen", + "pushoversounds gamelan": "Gamelan-soitin", + "pushoversounds incoming": "Saapuva", + "pushoversounds intermission": "Väliaika", + "pushoversounds magic": "Taika", + "pushoversounds mechanical": "Mekaaninen", + "pushoversounds pianobar": "Pianobaari", + "pushoversounds spacealarm": "Avaruushälytys", + "pushoversounds tugboat": "Hinaaja", + "pushoversounds alien": "Avaruusoliohälytys (pitkä)", + "pushoversounds climb": "Kiipeily (pitkä)", + "pushoversounds persistent": "Pysyvä (pitkä)", + "pushoversounds updown": "Toimii Ei toimi (pitkä)", + "pushoversounds vibrate": "Vain värinä", + "pushoversounds none": "Ei mitään (hiljainen)", + "pushyAPIKey": "Salainen API-avain", + "pushyToken": "Laitteen tunnus", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.Chat", + "pushy": "Päällekäyvä", + "PushByTechulus": "Techuluksen työntö", + "octopush": "Mustekala", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "GoogleChat": "Google Chat (vain Google Workspace)", + "Kook": "Kook", + "wayToGetKookGuildID": "Ota 'Kehittäjätila' käyttöön Kook-asetuksissa ja napsauta kiltaa hiiren kakkospainikkeella saadaksesi sen tunnuksen", + "Guild ID": "Killan tunnus", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "Käyttäjäavain", + "Device": "Laite", + "Message Title": "Viestin otsikko", + "More info on:": "Lisätietoja: {0}", + "pushoverDesc2": "Jos haluat lähettää ilmoituksia eri laitteille, täytä Laite-kenttä.", + "SMS Type": "SMS-tyyppi", + "octopushTypePremium": "Premium (nopea - suositellaan hälytykseen)", + "octopushTypeLowCost": "Alhaiset kustannukset (hidas - joskus operaattori estää)", + "checkPrice": "Tarkista kohteen {0} hinnat:", + "apiCredentials": "API-tunnistetiedot", + "Check octopush prices": "Tarkista Octopush-hinnat {0}.", + "octopushPhoneNumber": "Puhelinnumero (Kansainvälinen muoto, esim.: +33612345678)", + "octopushSMSSender": "Tekstiviestin lähettäjän nimi: 3-11 aakkosnumeerista merkkiä ja välilyönti (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea laitetunnus", + "Apprise URL": "Apprise URL-osoite", + "Example:": "Esimerkki: {0}", + "Read more:": "Lue lisää: {0}", + "Status:": "Tila: {0}", + "Strategy": "strategia", + "Free Mobile User Identifier": "Ilmainen mobiilikäyttäjätunnus", + "Enable TLS": "Ota TLS käyttöön", + "Proto Service Name": "Proto-palvelun nimi", + "Proto Method": "Proto-menetelmä", + "Proto Content": "Proto-sisältö", + "Economy": "Talous", + "Lowcost": "Halpa", + "high": "korkea", + "SendKey": "Lähetysavain (SendKey)", + "SMSManager API Docs": "SMSManager API dokumentaatio", + "Gateway Type": "Yhdyskäytävän tyyppi", + "SMSManager": "SMSManager", + "Base URL": "Perus-URL", + "goAlertIntegrationKeyInfo": "Hanki yleinen API-integrointiavain palvelulle tässä muodossa \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" yleensä kopioidun URL-osoitteen tunnusparametrin arvona.", + "goAlert": "GoAlert", + "AccessKeyId": "Pääsyavaimen tunnus (AccessKey ID)", + "SecretAccessKey": "Pääsyavaimen salaisuus (AccessKey Secret)", + "PhoneNumbers": "Puhelinnumerot (PhoneNumbers)", + "TemplateCode": "Mallikoodi (TemplateCode)", + "SignName": "Allekirjoitusnimi (SignName)", + "Sms template must contain parameters: ": "Tekstiviestimallin tulee sisältää parametrit: ", + "Bark Group": "Bark ryhmä", + "Bark Sound": "Bark ääni", + "WebHookUrl": "Webhookin URL-osoite (WebHookUrl)", + "SecretKey": "Salainen avain (SecretKey)", + "For safety, must use secret key": "Turvallisuuden vuoksi on käytettävä salaista avainta", + "Device Token": "Laitteen tunnus", + "Platform": "Alusta", + "Bark Endpoint": "Bark päätepiste", + "Huawei": "Huawei-merkki", + "High": "Korkea", + "Topic": "Aihe", + "WeCom Bot Key": "WeCom-bottiavain", + "Setup Proxy": "Määritä välityspalvelin", + "Proxy Protocol": "Välityspalvelinprotokolla", + "Proxy Server": "Välityspalvelin", + "matrix": "Matriisi", + "promosmsTypeFlash": "SMS FLASH - Viesti näkyy automaattisesti vastaanottajan laitteessa. Rajoitettu vain puolalaisille vastaanottajille.", + "promosmsTypeSpeed": "SMS SPEED - Järjestelmän korkein prioriteetti. Erittäin nopea ja luotettava, mutta kallis (noin kaksi kertaa SMS TÄYSI hinta).", + "promosmsPhoneNumber": "Puhelinnumero (puolalaiselle vastaanottajalle voit ohittaa suuntanumerot)", + "promosmsSMSSender": "Tekstiviestin lähettäjän nimi: Esirekisteröity nimi tai jokin oletusasetuksista: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "promosmsAllowLongSMS": "Salli pitkät tekstiviestit", + "Feishu WebHookUrl": "Feishu WebHookURL-osoite", + "Internal Room Id": "Huoneen sisäinen tunnus", + "Channel Name": "Kanavan nimi", + "Uptime Kuma URL": "Uptime Kuma URL-osoite", + "Icon Emoji": "Ikoni Emoji", + "signalImportant": "TÄRKEÄÄ: Et voi sekoittaa ryhmiä ja numeroita vastaanottajissa!", + "aboutWebhooks": "Lisätietoja Webhookeista osoitteessa: {0}", + "aboutChannelName": "Kirjoita kanavan nimi {0} Kanavan nimi -kenttään, jos haluat ohittaa Webhook-kanavan. Esimerkki: #muu-kanava", + "aboutKumaURL": "Jos jätät Uptime Kuma URL -kentän tyhjäksi, se on oletuksena Project GitHub -sivu.", + "smtpDkimSettings": "DKIM-asetukset", + "smtpDkimDesc": "Katso Nodemailer DKIM {0} -sovelluksen käytöstä.", + "documentation": "dokumentointi", + "smtpDkimDomain": "Verkkotunnus", + "smtpDkimKeySelector": "Näppäinvalitsin", + "smtpDkimPrivateKey": "Yksityinen avain", + "smtpDkimHashAlgo": "Hash-algoritmi (valinnainen)", + "smtpDkimheaderFieldNames": "Allekirjoitettavat otsikkoavaimet (valinnainen)", + "smtpDkimskipFields": "Otsikkonäppäimet, joita ei allekirjoiteta (valinnainen)", + "Integration Key": "Integrointiavain", + "Integration URL": "Integroinnin URL-osoite", + "Auto resolve or acknowledged": "Automaattinen ratkaisu tai kuittaus", + "do nothing": "Älä tee mitään", + "auto acknowledged": "automaattisesti kuitattu", + "auto resolve": "automaattinen ratkaisu", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API-päätepiste", + "alertaEnvironment": "Ympäristö", + "alertaApiKey": "API-avain", + "alertaRecoverState": "Palautustila", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API-käyttäjänimi (sis. webapi_-etuliite)", + "serwersmsAPIPassword": "API-salasana", + "serwersmsPhoneNumber": "Puhelinnumero", + "smseagle": "SMSEagle", + "smseagleTo": "Puhelinnumero(t)", + "smseagleGroup": "Puhelinmuistioryhmän nimi/nimet", + "smseagleContact": "Puhelinmuistion yhteyshenkilön nimet", + "smseagleRecipientType": "Vastaanottajan tyyppi", + "smseagleRecipient": "Vastaanottaja(t) (jos useita, ne on erotettava pilkulla)", + "smseagleToken": "API-käyttöoikeustunnus", + "smseagleUrl": "SMSEagle-laitteesi URL-osoite", + "smseagleEncoding": "Lähetä Unicodena (normaalisti=GSM-7)", + "smseaglePriority": "Viestin prioriteetti (0-9, korkein oletus = 0)", + "stackfield": "Stackfield", + "Recipient Number": "Vastaanottajan numero", + "From Name/Number": "Nimestä/numerosta", + "Leave blank to use a shared sender number.": "Jätä tyhjäksi, jos haluat käyttää jaettua lähettäjän numeroa.", + "Octopush API Version": "Octopush API -versio", + "Legacy Octopush-DM": "Vanha Octopush-DM", + "ntfy Topic": "ntfy-aihe", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "OneBot HTTP-osoite", + "onebotGroupMessage": "Ryhmä", + "onebotPrivateMessage": "Yksityinen", + "onebotUserOrGroupId": "Ryhmä/käyttäjätunnus", + "onebotSafetyTips": "Käyttöoikeustunnus on asetettava turvallisuuden vuoksi", + "PushDeer Key": "PushDeer-avain", + "wayToGetClickSendSMSToken": "Voit saada API-käyttäjänimen ja API-avaimen osoitteesta {0}.", + "Custom Monitor Type": "Mukautettu seuraintyyppi", + "Google Analytics ID": "Google Analytics -tunnus", + "Edit Tag": "Muokkaa tunnistetta", + "Server Address": "Palvelimen osoite", + "Learn More": "Lisätietoja", + "Inactive": "Ei-aktiivinen", + "Add New below or Select...": "Lisää uusi alapuolelle tai valitse…", + "Blue": "Sininen", + "Avg. Response": "Kesk.arv. vastaus", + "All Systems Operational": "Kaikki järjestelmät toiminnassa", + "defaultNotificationName": "Minun {notification} Hälytys ({number})", + "webhookFormDataDesc": "{multipart} soveltuu PHP:lle. JSON pitää tulkita {decodeFunction} avulla", + "HeadersInvalidFormat": "Pyynnön otsikot eivät ole kelvollisessa JSON-muodossa: ", + "clearDataOlderThan": "Säilytä seuraimen historiatiedot {0} päivän ajan.", + "steamApiKeyDescription": "Steam-pelipalvelimen valvontaa varten tarvitset Steam Web API-avaimen. Voit rekisteröidä API-avaimesi täällä: ", + "light": "vaalea", + "Switch to Light Theme": "Vaihda vaaleaan teemaan", + "Powered by": "Voimanlähteenä", + "Accept characters:": "Hyväksy merkit:", + "New Status Page": "Uusi tilasivu", + "wayToGetCloudflaredURL": "(Lataa cloudflared osoitteesta {0})", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Nykyinen yhteys saattaa katketa, jos muodostat parhaillaan yhteyttä Cloudflare-tunnelin kautta. Haluatko varmasti lopettaa sen? Vahvista se kirjoittamalla nykyinen salasanasi.", + "RadiusCallingStationIdDescription": "Kutsu laitteen tunniste", + "Check how to config it for WebSocket": "Tarkista, kuinka se määritetään WebSocketille", + "Docker Daemon": "Docker-daemon", + "deleteDockerHostMsg": "Haluatko varmasti poistaa tämän docker-isännän kaikista seuraimista?", + "supportTelegramChatID": "Tukee suoraa chattia / ryhmää / kanavan chat-tunnusta", + "Event data:": "Tapahtuman tiedot:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Valitse sitten toiminto, esimerkiksi vaihda kohtaus sellaiseen, jossa RGB-valo on punainen.", + "backupOutdatedWarning": "Vanhentunut: Koska monia ominaisuuksia lisättiin ja tätä varmuuskopiointitoimintoa ei ole ylläpidetty, se ei voi luoda tai palauttaa täydellistä varmuuskopiota.", + "lastDay4": "Kuukauden 4. viimeinen päivä", + "IconUrl": "Kuvakkeen URL-osoite", + "Enable DNS Cache": "(Vanhentunut) Ota DNS-välimuisti käyttöön HTTP(s)-seuraimille", + "dnsCacheDescription": "Se ei ehkä toimi joissakin IPv6-ympäristöissä, poista se käytöstä, jos kohtaat ongelmia.", + "confirmUninstallPlugin": "Haluatko varmasti poistaa tämän laajennuksen?", + "wayToGetDiscordURL": "Saat tämän siirtymällä kohtaan Palvelinasetukset -> Integraatiot -> Näytä Webhookit -> Uusi Webhook", + "needSignalAPI": "Sinulla on oltava signaaliasiakas, jossa on REST API.", + "wayToGetLineChannelToken": "Avaa ensin {0}, luo palveluntarjoaja ja kanava (Messaging API), sitten saat kanavan käyttö tokenin ja käyttäjätunnuksen yllä mainituista valikon kohdista.", + "Icon URL": "Kuvakkeen URL-osoite", + "aboutIconURL": "Voit ohittaa oletusprofiilikuvan antamalla linkin kuvaan kohdassa \"kuvakeen URL\". Ei käytetä, jos kuvake emoji on asetettu.", + "aboutMattermostChannelName": "Voit ohittaa oletuskanavan, jolle Webhook lähettää viestejä, kirjoittamalla kanavan nimen Kanavan nimi -kenttään. Tämä on otettava käyttöön Mattermost Webhook -asetuksissa. Esimerkki: #muu-kanava", + "resolverserverDescription": "Cloudflare on oletuspalvelin. Voit vaihtaa ratkaisijapalvelinta milloin tahansa.", + "enableDefaultNotificationDescription": "Tämä ilmoitus on oletuksena käytössä uusissa seuraimissa. Voit silti poistaa ilmoituksen käytöstä erikseen jokaiselta seuraimelta.", + "clearEventsMsg": "Haluatko varmasti poistaa kaikki tämän monitorin tapahtumat? Tämä poistaa niihin liittyvät viestitiedot pysyvästi, eikä toimintoa voi perua.", + "confirmClearStatisticsMsg": "Haluatko varmasti poistaa KAIKKI tilastot?", + "importHandleDescription": "Valitse \"Ohita olemassa oleva\", jos haluat ohittaa jokaisen samannimisen seuraimen tai ilmoituksen. \"Korvaa\" poistaa kaikki olemassa olevat seuraimet ja ilmoitukset.", + "passwordNotMatchMsg": "Toistettu salasana ei täsmää.", + "backupDescription3": "Arkaluonteiset tiedot, kuten ilmoitustunnukset, sisältyvät vientitiedostoon. Säilytä vienti turvallisesti.", + "endpoint": "päätepiste", + "octopushAPIKey": "\"API-avain\" ohjauspaneelin HTTP API -tunnistetiedoista", + "pushoversounds siren": "Sireeni", + "pushoversounds echo": "Pushover-kaiku (pitkä)", + "pushover": "Ylityöntö", + "apprise": "Apprise (tukee yli 50 ilmoituspalvelua)", + "pushbullet": "Pushbullet", + "wayToGetKookBotToken": "Luo sovellus ja hanki bot-tunnus osoitteessa {0}", + "Notification Sound": "Ilmoitusääni", + "pushoverDesc1": "Hätäprioriteetilla (2) on oletusarvoisesti 30 sekunnin aikakatkaisu uudelleenyritysten välillä, ja se vanhenee 1 tunnin kuluttua.", + "octopushLegacyHint": "Käytätkö Octopushin (2011-2020) vanhaa versiota vai uutta versiota?", + "Free Mobile API Key": "Ilmainen mobiilisovellusliittymäavain", + "You can divide numbers with": "Voit jakaa numerot", + "goAlertInfo": "GoAlert on avoimen lähdekoodin sovellus päivystykseen, automatisoituihin eskalaatioihin ja ilmoituksiin (kuten tekstiviestit tai äänipuhelut). Ota automaattisesti mukaan oikea henkilö, oikealla tavalla ja oikeaan aikaan! {0}", + "Retry": "Yritä uudelleen", + "Proxy server has authentication": "Välityspalvelimella on todennus", + "promosmsTypeEco": "SMS ECO - halpa mutta hidas ja usein ylikuormitettu. Rajoitettu vain puolalaisille vastaanottajille.", + "promosmsTypeFull": "SMS FULL - Premium-tason tekstiviestit, voit käyttää lähettäjän nimeäsi (sinun on rekisteröitävä nimi ensin). Luotettava hälytyksiä varten.", + "matrixHomeserverURL": "Kotipalvelimen URL-osoite (http(s):// ja valinnaisesti portti)", + "matrixDesc1": "Löydät sisäisen huonetunnuksen katsomalla Matrix-asiakasohjelman huoneasetusten lisäosaa. Sen pitäisi näyttää tältä: !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "On erittäin suositeltavaa, että luot uuden käyttäjän etkä käytä omaa Matrix-käyttäjätunnustasi, koska se antaa täyden pääsyn tilillesi ja kaikkiin huoneisiin, joihin liityit. Luo sen sijaan uusi käyttäjä ja kutsu se vain siihen huoneeseen, josta haluat saada ilmoituksen. Saat käyttöoikeustunnuksen suorittamalla {0}", + "wayToGetPagerDutyKey": "Saat tämän siirtymällä kohtaan Palvelu -> Palveluhakemisto -> (Valitse palvelu) -> Integraatiot -> Lisää integraatio. Täältä voit etsiä \"Events API V2\". Lisätietoja {0}", + "alertaAlertState": "Varoitustila", + "serwersmsSenderName": "Tekstiviestin lähettäjän nimi (rekisteröity asiakasportaalin kautta)", + "onebotMessageType": "OneBot-viestityyppi", + "pushViewCode": "Kuinka käyttää Push-seurainta? (Näytä Koodi)", + "settingUpDatabaseMSG": "Tietokannan asennus käynnissä. Tämä voi kestää hetken, ole kärsivällinen.", + "setupDatabaseChooseDatabase": "Mitä tietokantaa haluat käyttää?", + "setupDatabaseEmbeddedMariaDB": "Sinun ei tarvitse asettaa mitään. Tämä Docker-kuva on sisäänrakentanut ja konfiguroinut MariaDB:n sinulle automaattisesti. Uptime Kuma yhdistää tähän tietokantaan Unix-socketin kautta.", + "setupDatabaseMariaDB": "Yhdistä ulkoiseen MariaDB-tietokantaan. Sinun on asetettava tietokantayhteyden tiedot.", + "setupDatabaseSQLite": "Yksinkertainen tietokantatiedosto, suositeltu pienimuotoisiin käyttöönottoihin. Ennen versiota 2.0.0 Uptime Kuma käytti SQLitea oletustietokantana.", + "dbName": "Tietokannan nimi", + "pushOthers": "Muut", + "programmingLanguages": "Ohjelmointikielet", + "Home": "Koti", + "Cannot connect to the socket server": "Ei voi yhdistää socket-palvelimeen", + "Reconnecting...": "Yhdistetään uudelleen...", + "Invert Keyword": "Käänteinen Avainsana", + "Request Timeout": "Pyynnön aikakatkaisu", + "timeoutAfter": "Aikakatkaisu {0} sekunnin jälkeen", + "Resend Notification if Down X times consecutively": "Lähetä ilmoitus uudelleen, jos alhaalla X kertaa peräkkäin", + "Expected Value": "Odotettu arvo", + "Json Query": "JSON-kysely", + "wayToGetHeiiOnCallDetails": "Trigger ID:n ja API-avainten hankinta selitetään ohjeistuksessa {documentation}", + "documentationOf": "{0} Dokumentaatio", + "Enter the list of brokers": "Syötä lista välittäjistä", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Joko palvelimen osoite johon haluat yhdistää tai {localhost} jos aiot käyttää {local_mta}", + "successKeyword": "Onnistumisen avainsana", + "tailscalePingWarning": "Käyttääksesi Tailscale Ping-seurainta, sinun täytyy asentaa Uptime Kuma ilman Dockeria ja asentaa Tailscale-asiakasohjelma palvelimellesi.", + "cronExpression": "Cron-määrittely", + "Remove domain": "Poista verkkotunnus '{0}'", + "ntfyPriorityHelptextAllEvents": "Kaikki tapahtumat lähetetään korkeimmalla prioriteetilla", + "Press Enter to add broker": "Paina Enter lisätäksesi välittäjän", + "wayToGetFlashDutyKey": "Integroi Uptime Kuma Flashdutyyn seuraavasti: siirry kohtaan Channels > valitse kanava > Integrations > Add a new integration, valitse Uptime Kuma ja kopioi Push URL -osoite.", + "Saved.": "Tallennettu.", + "receiverInfoSevenIO": "Jos vastaanottajan numero ei ole Saksassa, sinun täytyy lisätä maakoodi numeron eteen (esim. Yhdysvaltojen maakoodille 1 käytä muotoa 117612121212 - ei muotoa 017612121212)", + "gtxMessagingFromHint": "Matkapuhelimissa vastaanottajat näkevät TPOA:n viestin lähettäjänä. Sallittu on korkeintaan 11 aakkosnumeerista merkkiä, lyhytkoodi, paikallinen pitkä koodi tai kansainväliset numerot ({e164}, {e212} or {e214})", + "monitorToastMessagesLabel": "Seuraimen ponnahdusilmoitukset", + "Browser Screenshot": "Selaimen ruutukaappaus", + "Mentioning": "Mainitaan", + "Don't mention people": "Älä mainitse ihmisiä", + "Mention group": "Mainitse {group}", + "API Keys": "API-avaimet", + "Expiry": "Vanhenee", + "Expiry date": "Vanhenemispäivä", + "Don't expire": "Ei vanhene", + "Continue": "Jatka", + "Add Another": "Lisää toinen", + "apiKey-expired": "Vanhentunut", + "apiKey-inactive": "Ei-aktiivinen", + "Expires": "Vanhenee", + "Badge Down Color": "Badgen alhaalla-väri", + "Badge Pending Color": "Badgen odottaa-väri", + "Originator type": "Lähettäjän tyyppi", + "cellsyntDestination": "Vastaanottajan puhelinnumero kansainvälisessä muodossa: etuliitteenä 00 + maakoodi, esim. 00447920110000 numerolle 07920 110 000 (enint. 17 numeroa). Enintään 25000 pilkulla erotettua vastaanottajaa yhtä HTTP-kyselyä kohti.", + "cellsyntOriginatortypeAlphanumeric": "Aakkosnumeerinen merkkijono (enint. 11 merkkiä). Vastaanottajat eivät voi vastata viestiin.", + "cellsyntOriginatortypeNumeric": "Lukuarvo (enint. 15 numeroa). Puhelinnumero kansainvälisessä formaatissa ilman 00-etuliitettä (esim. UK puhelinnumero 07920 110 000 tulisi syöttää 447920110000). Vastaanottajat eivät voi vastata viestiin.", + "callMeBotGet": "Täällä voit luoda päätepisteen {0}, {1} ja {2}. Muistathan, että palvelu saattaa rajoittavan pääsyäsi. Rajoite näyttäisi olevan: {3}", + "cellsyntOriginator": "Näkyy vastaanottajan matkapuhelimessa viestin lähettäjänä. Sallitut arvot ja toiminta riippuvat lähettäjän tyypistä.", + "Allow Long SMS": "Salli pitkä SMS", + "max 15 digits": "Enint. 15 numeroa", + "Host URL": "Isäntä-URL", + "liquidIntroduction": "Mallipohjien käyttö toteutetaan Liquid-mallipohjakielellä. Katso käyttöohjeet kohdasta {0}.", + "templateMsg": "ilmoituksen viesti", + "templateHeartbeatJSON": "sydämensyke-objekti", + "templateMonitorJSON": "seurain-objekti", + "templateLimitedToUpDownNotifications": "saatavilla vain TOIMII/EI TOIMI -ilmoituksille", + "templateLimitedToUpDownCertNotifications": "saatavilla vain TOIMII/EI TOIMI/Varmenteen vanheneminen -ilmoituksille", + "successKeywordExplanation": "MQTT-avainsana joka tulkitaan onnistumiseksi", + "Check/Uncheck": "Valitse/Poista valinta", + "statusPageSpecialSlugDesc": "Erityinen slug {0}: tätä sivua ei näytetä kun slugia ei ole määritelty", + "Add a new expiry notification day": "Lisää uusi vanhenemismuistutuspäivä", + "Remove the expiry notification": "Poista vanhenemismuistutuspäivä", + "telegramMessageThreadID": "(Valinnainen) Viestilangan ID", + "telegramSendSilently": "Lähetä hiljaisesti", + "telegramSendSilentlyDescription": "Lähettää viestin hiljaisesti. Käyttäjät saavat ilmoituksen ilman äänimerkkiä.", + "telegramProtectContentDescription": "Jos käytössä, botin viestit Telegramissa suojataan uudelleenlähetykseltä ja tallennukselta.", + "telegramProtectContent": "Suojaa uudelleenlähetykseltä ja tallennukselta", + "telegramMessageThreadIDDescription": "Valinnainen yksilöllinen tunnus keskustelualueen viestilangalle (ketju); vain keskustelualueen superryhmille", + "sameAsServerTimezone": "Sama kuin palvelimen aikavyöhyke", + "startDateTime": "Aloitusajankohta", + "endDateTime": "Päättymisajankohta", + "enableNSCD": "Ota käyttöön NSCD (Name Service Cache Daemon) kaikkien DNS-kyselyiden välimuistitusta varten", + "chromeExecutable": "Chrome/Chromium -sovellus", + "chromeExecutableAutoDetect": "Tunnista automaattisesti", + "chromeExecutableDescription": "Docker käyttäjillä, jos Chromium ei ole vielä asennettu, saattaa kulua muutamia minuutteja asennukseen ennen testituloksen näyttämistä. Levytilaa vaaditaan 1 Gt.", + "Edit Maintenance": "Muokkaa Huoltoa", + "smtpLiquidIntroduction": "Kaksi seuraavaa kenttää ovat muokattavissa Liquid template-kielellä. Katso osoitteesta {0} lisäohjeita. Käytettävissä olevat muuttujat:", + "Clone Monitor": "Monista seurain", + "leave blank for default subject": "jätä tyhjäksi käyttääksesi oletusaihetta", + "emailCustomBody": "Mukautettu sisältörunko", + "leave blank for default body": "jätä tyhjäksi käyttääksesi oletussisältörunkoa", + "emailTemplateServiceName": "Palvelun nimi", + "emailTemplateHostnameOrURL": "Isäntänimi tai URL", + "emailTemplateStatus": "Tila", + "emailTemplateMonitorJSON": "seurain-objekti", + "emailTemplateHeartbeatJSON": "sydämensyke-objekti", + "emailTemplateMsg": "ilmoitusviesti", + "emailTemplateLimitedToUpDownNotification": "saatavilla vain TOIMII/EI TOIMI sydämensykkeille, muulloin null", + "Your User ID": "Käyttäjätunnuksesi", + "invertKeywordDescription": "Etsi puuttuvaa avainsanaa.", + "Bark API Version": "Bark API-versio", + "Notify Channel": "Ilmoitus kanavalle", + "aboutNotifyChannel": "Ilmoitus kanavalle antaa työpöytä- tai mobiili-ilmoituksen kaikille kanavan jäsenille; riippumatta ovatko he paikalla vai poissa.", + "setup a new monitor group": "luo uusi seurainryhmä", + "openModalTo": "avaa ikkuna kohteeseen {0}", + "Server URL should not contain the nfty topic": "Palvelimen URL ei saa sisältää nfty-aihetta", + "PushDeer Server": "PushDeer -palvelin", + "pushDeerServerDescription": "Jätä tyhjäksi käyttääksesi virallista palvelinta", + "Add API Key": "Lisää API-avain", + "No API Keys": "Ei API-avaimia", + "apiKeyAddedMsg": "API-avaimesi on lisätty. Laitathan sen muistiin, sillä sitä ei näytetä uudelleen.", + "Body Encoding": "Sisällön muoto (encoding)", + "Key Added": "Avain lisätty", + "apiKey-active": "Aktiivinen", + "disableAPIKeyMsg": "Haluatko varmasti kytkeä tämän API-avaimen pois käytöstä?", + "deleteAPIKeyMsg": "Haluatko varmasti poistaa tämän API-avaimen?", + "Generate": "Luo", + "pagertreeIntegrationUrl": "Integrointi-URL", + "pagertreeUrgency": "Kiireellisyysaste", + "pagertreeSilent": "Hiljainen", + "pagertreeLow": "Alhainen", + "pagertreeMedium": "Keskitaso", + "pagertreeHigh": "Korkea", + "pagertreeCritical": "Kriittinen", + "pagertreeDoNothing": "Älä tee mitään", + "pagertreeResolve": "Ratkaise automaattisesti", + "wayToGetPagerTreeIntegrationURL": "Kun olet luonut PagerTree Uptime Kuma -integraation, kopioi endpoint-URL. Katso lisätietoja: {0}", + "lunaseaTarget": "Kohde", + "lunaseaDeviceID": "Laitetunnus", + "lunaseaUserID": "Käyttäjätunnus", + "ntfyAuthenticationMethod": "Tunnistautumismenetelmä", + "ntfyPriorityHelptextAllExceptDown": "Kaikki tapahtumat lähetetään tällä prioriteetilla, paitsi {0}-tapahtumat, joilla on prioriteetti {1}", + "twilioApiKey": "API-avain (valinnainen)", + "twilioAuthToken": "Tunnistautumis-token / API-avaimen salaisuus", + "twilioFromNumber": "Numerosta", + "Badge Prefix": "Badgen arvon etuliite", + "Badge Suffix": "Badgen arvon pääte", + "Badge Label Color": "Badgen tekstin väri", + "Badge Color": "Badgen väri", + "Badge Label Prefix": "Badgen tekstin etuliite", + "Badge Warn Color": "Badgen varoitusväri", + "Badge Maintenance Color": "Badgen huoltoväri", + "Authorization Identity": "Todennuksen Identititeetti", + "Badge Down Days": "Badgen alhaalla-päivät", + "Badge Style": "Badgen tyyli", + "Badge URL": "Badgen URL", + "monitorToastMessagesDescription": "Ponnahdusilmoitukset seuraimille katoavat annetun sekuntimäärän kuluttua. Arvo -1 kytkee ajastuksen pois. Aseta arvoksi 0 estääksesi ponnahdusilmoitukset.", + "toastErrorTimeout": "Virheilmoitusten kesto", + "Enable Kafka Producer Auto Topic Creation": "Käytä Kafka Producerin automaattista aiheen luontia", + "Kafka SASL Options": "Kafka SASL-valinnat", + "Mechanism": "Mekanismi", + "AccessKey Id": "AccessKey-tunnus", + "Secret AccessKey": "Salainen AccessKey", + "Session Token": "Istunnon token", + "noGroupMonitorMsg": "Ei saatavilla. Luo seurainryhmä ensin.", + "Close": "Sulje", + "Request Body": "Pyynnön sisältörunko", + "FlashDuty Severity": "Vakavuus", + "nostrRelays": "Nostr-releet", + "nostrSender": "Lähettäjän yksityisavain (nsec)", + "nostrRecipients": "Vastaanottajan julkiset avaimet (npub)", + "nostrRecipientsHelp": "npub-formaatissa, yksi per rivi", + "showCertificateExpiry": "Näytä varmenteen vanheneminen", + "noOrBadCertificate": "Puuttuva tai virheellinen varmenne", + "gamedigGuessPort": "Gamedig: Arvaa portti", + "gamedigGuessPortDescription": "Valve Server Query Protocol -portti voi olla eri kuin asiakasportti. Kokeile tätä jos seurain ei voi yhdistää palvelimeesi.", + "authUserInactiveOrDeleted": "Käyttäjä on ei ole aktiivinen tai on poistettu.", + "authInvalidToken": "Virheellinen token.", + "successAdded": "Lisätty onnistuneesti.", + "authIncorrectCreds": "Virheellinen käyttäjänimi tai salasana.", + "2faDisabled": "2FA poistettu käytöstä.", + "2faEnabled": "2FA käytössä.", + "2faAlreadyEnabled": "2FA on jo käytössä.", + "successDeleted": "Poistettu onnistuneesti.", + "successEdited": "Muokattu onnistuneesti.", + "successAuthChangePassword": "Salasana on päivitetty onnistuneesti.", + "successBackupRestored": "Varmuuskopio on palautettu onnistuneesti.", + "successDisabled": "Kytketty pois päältä onnistuneesti.", + "successEnabled": "Kytketty päälle onnistuneesti.", + "useRemoteBrowser": "Käytä etäselainta", + "deleteRemoteBrowserMessage": "Oletko varma että haluat poistaa tämän etäselaimen kaikilta seuraimilta?", + "wayToWriteWhapiRecipient": "Puhelinnumero kansainvälisellä etuliitteellä, mutta ilman plus-merkkiä alussa ({0}), kontaktin tunnusta ({1}) tai ryhmän tunnusta ({2}).", + "Alphanumeric (recommended)": "Aakkosnumeerinen (suositeltu)", + "wayToGetWhapiUrlAndToken": "Voit saada API URLin ja token-avaimen käymällä haluamallasi kanavalla osoitteessa {0}", + "whapiRecipient": "Puhelinnumero / Kontaktin tunnus / Ryhmän tunnus", + "API URL": "API-URL", + "gtxMessagingApiKeyHint": "Löydät API-avaimesi täältä: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Lähettäjän puhelinnumero / Transmission Path Originating Address (TPOA)", + "To Phone Number": "Vastaanottajan puhelinnumero", + "cellsyntSplitLongMessages": "Jaa pitkät viestit korkeintaan kuuteen osaan. 153 x 6 = 918 merkkiä.", + "max 11 alphanumeric characters": "Enint. 11 aakkosnumeerista merkkiä", + "GrafanaOncallUrl": "Grafana Oncall-URL", + "emailCustomisableContent": "Mukautettava sisältö", + "Pick a SASL Mechanism...": "Valitse SASL-mekanismi…", + "Group": "Ryhmä", + "Monitor Group": "Seurainryhmä", + "What is a Remote Browser?": "Mikä on etäselain?", + "Badge Warn Days": "Badgen varoitus-päivät", + "Remote Browsers": "Etäselaimet", + "self-hosted container": "Itse ylläpidetty kontti", + "remoteBrowserToggle": "Oletuksena Chromium ajetaan Uptime Kuma -kontin sisällä. Voit käyttää etäselainta kytkemällä tämän valinnan.", + "Remote Browser": "Etäselain", + "Add a Remote Browser": "Lisää etäselain", + "Remote Browser not found!": "Etäselainta ei löytynyt!", + "remoteBrowsersDescription": "Etäselaimet ovat vaihtoehto paikalliselle Chromiumille. Käytä palvelua kuten browserless.io tai yhdistä oma etäselaimesi", + "Reset Token": "Nollaa token", + "notificationRegional": "Alueellinen", + "twilioToNumber": "Numeroon", + "Monitor Setting": "{0}'s seurainasetukset", + "Badge Duration (in hours)": "Badgen kesto (tunneissa)", + "Badge Label": "Badgen teksti", + "Show Clickable Link Description": "Jos valittu, jokainen jolla on pääsy tälle tilasivulle saavat pääsyn seuraimen URL-osoitteeseen.", + "Show Clickable Link": "Näytä klikattava linkki", + "Open Badge Generator": "Avaa Badge-generaattori", + "Badge Generator": "{0} Badge-generaattori", + "Badge Type": "Badgen tyyppi", + "Badge Preview": "Badgen esikatselu", + "Badge Label Suffix": "Badgen tekstin pääte", + "Badge Up Color": "Badgen ylhäällä-väri", + "tagNotFound": "Tunnistetta ei löydy.", + "foundChromiumVersion": "Löydettiin Chromium/Chrome. Versio: {0}", + "noDockerHostMsg": "Ei saatavilla. Asenna Docker-isäntä ensin.", + "Badge value (For Testing only.)": "Badgen arvo (Vain testausta varten.)", + "toastSuccessTimeout": "Onnistumisilmoitusten kesto", + "wayToGetSevenIOApiKey": "Vieraile hallintasivustolla app.seven.io > developer > api key ja paina vihreää 'add' -painiketta", + "senderSevenIO": "Lähettävä numero tai nimi", + "receiverSevenIO": "Vastaanottajan numero", + "apiKeySevenIO": "SevenIO API-avain", + "Telephone number": "Puhelinnumero", + "gtxMessagingToHint": "Kansainvälisessä muodossa, etuliitteenä \"+\" ({e164}, {e212} or {e214})", + "Originator": "Lähettäjä", + "Destination": "Vastaanottaja", + "locally configured mail transfer agent": "Paikallisesti asetettu postiagentti (MTA)", + "Search monitored sites": "Etsi valvotuista sivustoista", + "DockerHostRequired": "Aseta Docker-isäntä tälle seuraimelle.", + "Clone": "Monista", + "cloneOf": "Monistettu {0}", + "pushoverMessageTtl": "Viestin TTL (Sekuntia)", + "Add a domain": "Lisää verkkotunnus", + "ntfyUsernameAndPassword": "Käyttäjänimi ja salasana", + "twilioAccountSID": "Tilin SID", + "successPaused": "Laitettu tauolle onnistuneesti.", + "successResumed": "Jatkettu onnistuneesti.", + "Channel access token (Long-lived)": "Kanavan pääsyavain (pitkäaikainen)", + "Kafka Brokers": "Kafla-välittäjät", + "Kafka Producer Message": "Kafka Producer -viesti", + "Enable Kafka SSL": "Käytä Kafka SSL:ää", + "cronSchedule": "Aikataulu: ", + "Kafka Topic Name": "Kafka aihenimi", + "nostrRelaysHelp": "Yksi rele-URL riviä kohden", + "styleElapsedTime": "Kulunut aika sydämensykepalkin alla", + "styleElapsedTimeShowNoLine": "Näytä (Ei viivaa)", + "styleElapsedTimeShowWithLine": "Näytä (Viivan kanssa)", + "filterActive": "Aktiivinen", + "filterActivePaused": "Tauolla", + "statusPageRefreshIn": "Päivitetään: {0}", + "webhookBodyPresetOption": "Esivalinta - {0}", + "Select": "Valitse", + "selectedMonitorCount": "Valittu: {0}", + "Add New Tag": "Lisää uusi tunniste", + "webhookBodyCustomOption": "Mukautettu sisältörunko", + "invalidCronExpression": "Virheellinen Cron-määrittely: {0}", + "wayToGetDiscordThreadId": "Langan / keskustelualueketjun ID haetaan samalla tavalla kuin kanavan ID. Lue lisää ID:n hakemisesta {0}", + "mongodbCommandDescription": "Aja MongoDB-komento tietokannassa. Lisätietoja saatavilla olevista komennoista saat osoitteesta {documentation}", + "Bitrix24 Webhook URL": "Bitrix24 Webhookin URL", + "wayToGetBitrix24Webhook": "Voit luoda webhookin seuraamalla ohjeita osoitteessa {0}", + "bitrix24SupportUserID": "Syötä Bitrix24 käyttäjä-ID:si. Löydät käyttäjä-ID:n linkistä vierailemalla käyttäjän profiilissa.", + "Refresh Interval": "Päivityksen aikaväli", + "Refresh Interval Description": "Tilasivu latautuu uudelleen joka {0} sekunti", + "forumPostName": "Keskustelualueketjun nimi", + "ignoreTLSErrorGeneral": "Ohita yhteyden TLS/SSL-virheet", + "Select message type": "Valitse viestityyppi", + "Send to channel": "Lähetä kanavalle", + "postToExistingThread": "Lähetä jo olemassaolevaan lankaan / keskustelualueen ketjuun", + "Create new forum post": "Luo uusi ketju keskustelualueelle", + "threadForumPostID": "Langan / Keskustelualueketjun ID", + "e.g. {discordThreadID}": "esim. {discordThreadID}", + "whatHappensAtForumPost": "Luo uusi keskustelualueketju. Tämä EI jatka edellistä ketjua. Lähettääksesi olemassaolevaan ketjuun käytä toimintoa \"{option}\"", + "Command": "Komento", + "smspartnerApiurl": "Löydät API-avaimesi kojelaudalta osoitteesta {0}", + "smspartnerPhoneNumber": "Puhelinnumero(t)", + "smspartnerPhoneNumberHelptext": "Numeron täytyy olla kansainvälisessä muodossa {0},{1}. Jos numeroita on useita, ne täytyy erottaa merkillä {2}", + "smspartnerSenderName": "SMS-lähettäjän nimi", + "smspartnerSenderNameInfo": "Täytyy sisältää 3..=11 tavallista merkkiä", + "threemaRecipient": "Vastaanottaja", + "threemaRecipientType": "Vastaanottajan tyyppi", + "threemaRecipientTypeIdentityFormat": "8 merkkiä", + "threemaRecipientTypePhone": "Puhelinnumero", + "threemaRecipientTypePhoneFormat": "E.164, ilman etuliitettä +", + "threemaRecipientTypeEmail": "Sähköpostiosoite", + "threemaSenderIdentity": "Yhdyskäytävän tunnus (Gateway-ID)", + "threemaApiAuthenticationSecret": "Gateway-ID salaisuus", + "wayToGetThreemaGateway": "Voit rekisteröityä Threema Gateway:n käyttäjäksi {0}.", + "threemaRecipientTypeIdentity": "Threema-tunnus (Threema-ID)", + "threemaSenderIdentityFormat": "8 merkkiä, alkaa useimmiten merkillä *", + "threemaBasicModeInfo": "Huomio: Tämä integraatio käyttää Threema Gateway:tä perustilassa (palvelinpohjainen salaus). Lisätietoja löytyy {0}.", + "apiKeysDisabledMsg": "API-avaimet eivät ole käytössä koska tunnistautuminen ei ole käytössä.", + "snmpCommunityStringHelptext": "Tämä merkkijono toimii salasanana ja pääsyoikeutena SNMP-laitteisiin. Aseta se samaksi kuin SNMP-laitteen asetuksissa.", + "privateOnesenderDesc": "Varmista, että puhelinnumero on kelvollinen. Lähettääksesi viestin yksityiseen numeroon esim. 628123456789", + "Authorization Header": "Tunnistautumisen otsikko (Authorization Header)", + "Optional: Space separated list of scopes": "Valinnainen: Luettelo näkyvyysalueista (scope) välilyönnillä erotettuna", + "jsonQueryDescription": "Valitse sisältö palvelimen JSON-vastauksesta käyttämällä JSON-kyselyä tai käytä alkuperäistä sisältöä merkillä \"$\", jos palvelimen vastaus ei ole JSON-muodossa. Tämän jälkeen sisältöä verrataan odotettuun arvoon (merkkijonona). Katso kyselykielen ohjeita osoitteesta {0}. Leikkikenttä löytyy osoitteesta {1}.", + "now": "nyt", + "time ago": "{0} sitten", + "-year": "-vuosi", + "Json Query Expression": "Json-kyselylauseke", + "and": "ja", + "cacheBusterParam": "Lisää parametri {0}", + "cacheBusterParamDescription": "Satunnaisesti luotu parametri välimuistien ohittamiseksi.", + "Community String": "Yhteisömerkkijono", + "OID (Object Identifier)": "OID (Objektin tunniste)", + "snmpOIDHelptext": "Syötä OID anturille tai tilalle jota haluat valvoa. Käytä verkonvalvontatyökaluja kuten MIB-selaimia tai SNMP-ohjelmistoja jos olet epävarma OID:n valinnasta.", + "Condition": "Ehto", + "SNMP Version": "SNMP-versio", + "Please enter a valid OID.": "Ole hyvä ja syötä kelvollinen OID.", + "Host Onesender": "Onesender-isäntä", + "Token Onesender": "Onesender-tokeni", + "Recipient Type": "Vastaanottajan tyyppi", + "Private Number": "Yksityinen numero", + "Group ID": "Ryhmän tunnus (Group ID)", + "groupOnesenderDesc": "Varmista, että ryhmän tunnus (Group ID) on kelvollinen. Lähettääksesi viestin ryhmälle, esim. 628123456789-342345", + "wayToGetOnesenderUrlandToken": "Saat URL-osoitteen ja tokenin vierailemalla OneSenderin verkkosivulla. Lisätietoja osoitteesta {0}", + "Add Remote Browser": "Lisää etäselain", + "New Group": "Uusi ryhmä", + "Group Name": "Ryhmän nimi", + "OAuth2: Client Credentials": "OAuth2: Asiakkaan tunnukset", + "Authentication Method": "Tunnistustapa", + "Form Data Body": "Lomakkeen tietosisältö (Form data body)", + "OAuth Token URL": "OAuth tokenin URL", + "Client ID": "Asiakkaan ID", + "Client Secret": "Asiakkaan salaisuus", + "OAuth Scope": "OAuth-näkyvyysalue (scope)", + "Go back to home page.": "Palaa etusivulle.", + "No tags found.": "Tunnisteita ei löytynyt.", + "Lost connection to the socket server.": "Menetettiin yhteys Socket-palvelimeen.", + "Cannot connect to the socket server.": "Socket-palvelimeen ei voi yhdistää.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhookin URL", + "signl4Docs": "Löydät lisätietoja SIGNL4-asetuksista ja webhook URLin hankinnasta osoitteesta {0}.", + "Conditions": "Ehdot", + "conditionAdd": "Lisää ehto", + "conditionDelete": "Poista ehto", + "conditionAddGroup": "Lisää ryhmä", + "conditionDeleteGroup": "Poista ryhmä", + "conditionValuePlaceholder": "Arvo", + "contains": "sisältää", + "not contains": "ei sisällä", + "not equals": "ei yhtä suuri kuin", + "equals": "yhtä suuri kuin", + "starts with": "alkaa", + "not starts with": "ei ala", + "ends with": "päättyy", + "not ends with": "ei pääty", + "less than": "vähemmän kuin", + "greater than": "enemmän kuin", + "less than or equal to": "vähemmän tai yhtä paljon kuin", + "greater than or equal to": "enemmän tai yhtä paljon kuin", + "record": "tietue", + "Notification Channel": "Ilmoituskanava", + "Custom sound to override default notification sound": "Mukautettu ääni oletusäänen sijaan", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Aikaherkät ilmoitukset toimitetaan välittömästi vaikka laite olisi Älä Häiritse-tilassa.", + "ignoredTLSError": "TLS/SSL-virheitä ei huomioida", + "Debug": "Debug", + "Copy": "Kopioi", + "CopyToClipboardError": "Leikepöydälle kopiointi ei onnistunut: {error}", + "CopyToClipboardSuccess": "Kopioitu!", + "CurlDebugInfo": "Debugataksesi seurainta, voit joko kopioida tämän oman tietokoneesi komentoriville tai sen tietokoneen komentoriville jossa uptime kuma on käynissä nähdäksesi pyyntösi.{newiline}Otathan huomioon verkkoympäristöjen erot kuten {firewalls}, {dns_resolvers} tai {docker_networks}.", + "firewalls": "palomuurit", + "dns resolvers": "DNS-palvelimet", + "docker networks": "Docker-verkot", + "CurlDebugInfoOAuth2CCUnsupported": "{curl} ei tue täydellistä oauth-asiakkaan kirjautumistietojen välittämistä.{newline}Hanki bearer token -tunnus ja välitä se {oauth2_bearer}-vaihtoehdon kautta.", + "CurlDebugInfoProxiesUnsupported": "Välityspalvelimen tukea yllä olevassa {curl}-komennossa ei ole tällä hetkellä toteutettu.", + "shrinkDatabaseDescriptionSqlite": "Käynnistä tietokannan {vacuum} SQLitelle. {auto_vacuum} on jo käytössä, mutta se ei eheytä tietokantaa tai uudelleenpakkaa yksittäisiä tietokantasivuja samoin kuin {vacuum}.", + "Message format": "Viestin muoto", + "Send rich messages": "Lähetä monipuolisia viestejä (rich messages)", + "Sound": "Ääni", + "Alphanumerical string and hyphens only": "Vain aakkosnumeerinen merkkijono ja yhdysmerkit", + "Arcade": "Pelihalli", + "Harp": "Harppu", + "Fail": "Virhe", + "Correct": "Onnistuminen", + "Reveal": "Ilmestys", + "Bubble": "Kupla", + "Doorbell": "Ovikello", + "Flute": "Huilu", + "Money": "Raha", + "Clear": "Kirkas", + "Elevator": "Hissi", + "Guitar": "Kitara", + "Time Sensitive (iOS Only)": "Aikaherkkä (vain iOS)", + "From": "Lähettäjä", + "Can be found on:": "Löydettävissä: {0}", + "The phone number of the recipient in E.164 format.": "Vastaanottajan puhelinnumero E.164-muodossa.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Joko lähettäjätunnus (sender ID) tai puhelinnumero E-164-muodossa jos haluat pystyä vastaanottamaan vastausviestejä.", + "Scifi": "Tieteisseikkailu", + "Pop": "Poksahdus", + "rabbitmqNodesRequired": "Aseta tämän seuraimen solmut.", + "rabbitmqNodesInvalid": "Käytä RabbitMQ-solmuille täysin hyväksyttyä ('http' alkavaa) URL-osoitetta.", + "RabbitMQ Username": "RabbitMQ Käyttäjänimi", + "RabbitMQ Password": "RabbitMQ Salasana", + "SendGrid API Key": "SendGrid API-avain", + "Separate multiple email addresses with commas": "Erottele useammat sähköpostiosoitteet pilkuilla", + "RabbitMQ Nodes": "RabbitMQ-hallintasolmut", + "rabbitmqNodesDescription": "Anna URL RabbitMQ-hallintasolmuille sisältäen protokollan ja portin. Esimerkki: {0}", + "rabbitmqHelpText": "Jotta voit käyttää seurainta, sinun on otettava hallintalaajennus käyttöön RabbitMQ-asetuksissa. Lisätietoja saat osoitteesta {rabitmq_documentation}.", + "aboutSlackUsername": "Muuttaa viestin lähettäjän näyttönimeä. Jos haluat mainita jonkun, lisää se ystävälliseen nimeen.", + "Font Twemoji by Twitter licensed under": "Twemoji-fontti (Twitter) on lisensoitu seuraavalla lisenssillä", + "wayToGetWahaSession": "Tästä istunnosta WAHA lähettää ilmoituksia Chat ID:hen. Löydät sen WAHA kojelaudasta.", + "wayToWriteWahaChatId": "Puhelinnumero kansainvälisellä etuliitteellä, mutta ilman plusmerkkiä alussa ({0}), yhteystietotunnusta ({1}) tai ryhmätunnusta ({2}). Ilmoitukset lähetetään tähän chat-tunnukseen WAHA-istunnosta.", + "wahaSession": "Istunto", + "wahaChatId": "Viesti ID (Puhelinnumero / Yhteystieto ID / Ryhmä ID)", + "Template Format": "Malli Muotoilu", + "wayToGetWahaApiUrl": "Sinun WAHA instanssin URL.", + "YZJ Webhook URL": "YZJ Webhook URL-osoite", + "telegramServerUrl": "(Valinnainen) Palvelin Url", + "telegramServerUrlDescription": "Telegramin bot-api-rajoitusten poistamiseksi tai pääsyn saamiseksi estetyille alueille (Kiina, Iran jne.). Saat lisätietoja napsauttamalla {0}. Oletus: {1}", + "Message Template": "Viesti Malli", + "YZJ Robot Token": "YZJ Robotti tokeni", + "wayToGetWahaApiKey": "API Key on WHATSAPP_API_KEY ympäristömuuttujan arvo, jota käytit WAHA käynnistämiseen.", + "Plain Text": "Pelkkää tekstiä", + "templateServiceName": "palvelin nimi", + "templateHostnameOrURL": "isäntänimi tai URL", + "templateStatus": "tila", + "telegramUseTemplate": "Käytä mukautettua viesti mallia", + "telegramUseTemplateDescription": "Jos aktivoitu, viesti lähetetään käyttämällä mukautettua mallia.", + "telegramTemplateFormatDescription": "Telegram sallii erilaisten merkintäkielien käytön viesteissä, katso Telegram {0} tarkempia tietoja.", + "smsplanetApiToken": "SMSPlanet API:n tunnus", + "smsplanetApiDocs": "Yksityiskohtaiset tiedot API-tunnusten hankkimisesta löytyvät osoitteesta {the_smsplanet_documentation}.", + "the smsplanet documentation": "smsplanetin dokumentaatio", + "Phone numbers": "Puhelinnumerot", + "Sender name": "Lähettäjän nimi", + "smsplanetNeedToApproveName": "On hyväksyttävä asiakaspaneelissa", + "Use HTML for custom E-mail body": "Käytä HTML-muotoilua mukautetussa sähköpostiviestissä", + "smseagleDocs": "Tarkista dokumentaatio APIv2 saatavuudelle: {0}", + "defaultFriendlyName": "Uusi monitori", + "smseagleMsgType": "Viestin tyyppi", + "smseagleMsgSms": "SMS viesti (oletus)", + "smseagleMsgRing": "Ring puhelu", + "smseagleMsgTts": "Teksti puheeksi puhelu", + "smseagleMsgTtsAdvanced": "Teksti puheeksi edistynyt puhelu", + "smseagleDuration": "Aika (sekuntteina)", + "smseagleTtsModel": "Teksti puheeksi mallin tunniste ID", + "pingCountLabel": "Suurin määrä paketeille", + "Add Tags": "Lisää tageja", + "tagAlreadyOnMonitor": "Tämä tagi (nimi ja arvo) on jo monitorissa tai odottamassa lisäystä.", + "tagAlreadyStaged": "Tämä tagi (nimi ja arvo) on jo lavastettu tälle erälle.", + "tagNameExists": "Systeemin tagi tällä nimellä on jo olemassa. Valitse se listasta tai käytä eri nimeä.", + "smseagleGroupV2": "Puhelinluettelon ryhmän tunnukset", + "smseagleContactV2": "Puhelinluettelon yhteydenotto ID:t", + "smseagleApiType": "API versio", + "smseagleApiv1": "APIv1 (olemassa oleville projekteille ja taaksepäin yhteensopivuudelle)", + "smseagleApiv2": "APIv2 (suositeltu uusimmille lisäosille)", + "smseagleComma": "Useat on erotettava toisistaan pilkulla", + "SpugPush Template Code": "Mallipohjan koodi", + "ntfyPriorityHelptextPriorityHigherThanDown": "Normaalin prioriteetin tulisi olla korkeampi kuin {0} prioriteetti. Prioriteetti {1} on korkeampi kuin {0} prioriteetti {2}", + "ntfyPriorityDown": "DOWN-tapahtumien prioriteetti", + "FlashDuty Push URL": "Pushaamiseen URL-osoite", + "FlashDuty Push URL Placeholder": "Kopioi hälytysintegraatiosivulta", + "pingCountDescription": "Lähetettävien pakettien määrä ennen lopettamista", + "versionIs": "Versio: {version}", + "enableSSL": "SSL/TLS Päälle", + "monitorTypeDatabase": "Tietokannan seuraimen tyyppi", + "monitorTypeSpecial": "Erikoinen", + "monitorTypeGameServer": "Pelipalvelin", + "snmpV3Username": "SNMPv3 Käyttäjänimi", + "message": "viesti", + "json_value": "JSON arvo", + "OAuth Audience": "OAuth-yleisö", + "Path": "Polku", + "years": "{n} vuosi | {n} vuotta", + "Pin this incident": "Kiinnitä tämä tiedote", + "No incidents recorded": "Ei kirjattuja tapauksia", + "days": "{n} päivä | {n} päivää", + "Load More": "Lataa lisää", + "Loading...": "Ladataan...", + "mariadbCaCertificateLabel": "CA-varmenteet", + "Monitors": "{n} Seurain | {n} Seurainta", + "hours": "{n} tunti | {n} tuntia", + "minutes": "{n} minuutti | {n} minuuttia", + "minuteShort": "{n} min | {n} min", + "saveErrorResponseForNotifications": "Tallenna HTTP-virhevastaus ilmoituksia varten", + "saveResponseDescription": "Tallentaa HTTP-vastauksen ja tuo sen ilmoitusmallien käytettäväksi muuttujana {templateVariable}", + "saveResponseForNotifications": "Tallenna onnistunut HTTP-vastaus ilmoituksia varten", + "invalidURL": "Virheellinen URL", + "wildcardOnlyForDNS": "Jokerimerkkejä sisältäviä isäntänimiä tuetaan vain DNS-monitoreissa.", + "ooredooAccessKeyDescription": "Löydät tämän Ooredoo SMS -hallintapaneelista kohdasta ”API Access Key”. Liitä se täsmälleen siinä muodossa kuin se näkyy — se Base64-koodataan automaattisesti ennen lähettämistä.", + "ooredooBearerTokenDescription": "Ooredoo SMS -hallintapaneelin cURL-esimerkkikomennossa tekstin ”Authorization: Bearer” jälkeen näkyvä tunniste.", + "pingNumericLabel": "Numeerinen tuloste", + "Only retry if status code check fails": "Yritä uudelleen vain, jos tilakoodin tarkistus epäonnistuu", + "Connection Timeout": "Yhteyden aikakatkaisu", + "ignoreSecWebsocketAcceptHeaderDescription": "Sallii sen, ettei palvelin palauta Sec-WebSocket-Accept-otsaketta, jos WebSocket-yhteyden päivitys onnistuu.", + "Subprotocol(s)": "Aliprotokolla(t)", + "responseMaxLength": "Vastauksen enimmäispituus (tavuina)", + "responseMaxLengthDescription": "Tallennettavien vastaustietojen enimmäiskoko. Arvo 0 tarkoittaa rajoittamatonta kokoa. Tätä suuremmat vastaukset katkaistaan. Oletusarvo: 1024 tavua (1 kt)", + "logoutCurrentUser": "Kirjaa {username} ulos", + "Resolver Server(s)": "DNS-selvityspalvelin/-palvelimet", + "Incident description": "Häiriön kuvaus", + "Incident not found or access denied": "Häiriötä ei löytynyt tai käyttöoikeus puuttuu", + "Past Incidents": "Aiemmat häiriöt", + "Incident title": "Häiriön otsikko", + "Edit Incident": "Muokkaa häiriötä", + "HeadersInvalidFormatBecause": "Pyyntöotsakkeet eivät ole kelvollista JSON-muotoa, koska {error}", + "BodyInvalidFormatBecause": "Pyynnön runko ei ole kelvollista JSON-muotoa, koska {error}", + "mariadbUseSSLHelptext": "Ota käyttöön salattu yhteys tietokantaan. Tämä vaaditaan useimmissa pilvitietokannoissa.", + "retryOnlyOnStatusCodeFailureDescription": "Jos tämä asetus on käytössä, uudelleenyrityksiä tehdään vain, kun HTTP-tilakoodin tarkistus epäonnistuu (esimerkiksi palvelin ei ole käytettävissä). Jos tilakoodin tarkistus onnistuu mutta JSON-kysely epäonnistuu, monitori merkitään välittömästi toimimattomaksi ilman uudelleenyrityksiä.", + "Pinned incidents are shown prominently on the status page": "Kiinnitetyt häiriöt näytetään näkyvästi tilasivulla", + "templateAvailableVariables": "Käytettävissä olevat muuttujat", + "hostnameCannotBeIP": "DNS-isäntänimi ei voi olla IP-osoite. Tarkoititko käyttää DNS-selvityspalvelinkenttää?", + "oracledbConnectionString": "Oracle tietokanta: {connectionString}", + "unknownDays": "Tuntemattomat päivät", + "mariadbCaCertificateHelptext": "Liitä CA-varmenne PEM-muodossa, jos käytät itse allekirjoitettuja varmenteita. Jätä kenttä tyhjäksi, jos tietokantasi käyttää julkisen varmentajan allekirjoittamaa varmennetta.", + "wsSubprotocolDescription": "Syötä pilkuilla eroteltu luettelo aliprotokollista. Lisätietoja aliprotokollista löydät {documentation}", + "wsCodeDescription": "Tarvitessasi lisätietoja tilakoodeista katso {rfc6455}", + "Ignore Sec-WebSocket-Accept header": "Ohita {0}-otsake", + "example": "Esimerkki", + "Result": "Tulos", + "invalidDNSHostname": "Virheellinen isäntänimi. Isäntänimen on oltava kelvollinen FQDN-nimi. Se voi sisältää jokerimerkin tai alaviivan tai päättyä pisteeseen.", + "mqttWebSocketPath": "MQTT WebSocketin polku", + "mqttHostnameTip": "Ole hyvä, ja käytä seuraavaa muotoa: {hostnameFormat}", + "steamApiKeyDescriptionAt": "Steam-pelipalvelimen valvontaan tarvitaan Steam Web API -avain. Voit rekisteröidä API-avaimesi osoitteessa {url}", + "mqttWebsocketPathExplanation": "WebSocket-polku MQTT:n WebSocket-yhteyksille (esim. /mqtt)", + "mqttWebsocketPathInvalid": "Käytä kelvollista WebSocket-polun muotoa", + "invalidHostnameOrIP": "Virheellinen isäntänimi tai IP-osoite. Isäntänimen on oltava kelvollinen FQDN-nimi. Jokerimerkkejä ei voi käyttää. Nimi voi sisältää alaviivan tai päättyä pisteeseen." +} diff --git a/src/lang/fr-FR.json b/src/lang/fr-FR.json new file mode 100644 index 0000000..d6f4fcd --- /dev/null +++ b/src/lang/fr-FR.json @@ -0,0 +1,1763 @@ +{ + "languageName": "Français", + "checkEverySecond": "Vérifier toutes les {0} secondes", + "retryCheckEverySecond": "Réessayer toutes les {0} secondes", + "resendEveryXTimes": "Renvoyez toutes les {0} fois", + "resendDisabled": "Renvoi désactivé", + "retriesDescription": "Nombre de tentatives avant que le service ne soit déclaré hors ligne et qu'une notification soit envoyée", + "ignoreTLSError": "Ignorer les erreurs liées au certificat SSL/TLS", + "upsideDownModeDescription": "Si le service est en ligne, il sera alors noté hors ligne et vice-versa.", + "maxRedirectDescription": "Nombre maximal de redirections avant que le service ne soit marqué comme hors ligne.", + "enableGRPCTls": "Autoriser l'envoi d'une requête gRPC avec une connexion TLS", + "grpcMethodDescription": "Le nom de la méthode est converti au format CamelCase tel que sayHello, check, etc.", + "acceptedStatusCodesDescription": "Codes HTTP qui considèrent le service comme étant disponible.", + "Maintenance": "Maintenance", + "statusMaintenance": "Maintenance", + "Schedule maintenance": "Planifier la maintenance", + "Affected Monitors": "Sondes concernées", + "Pick Affected Monitors...": "Sélectionner les sondes concernées…", + "Start of maintenance": "Début de la maintenance", + "All Status Pages": "Toutes les pages d'état", + "Select status pages...": "Sélectionner les pages d'état…", + "recurringIntervalMessage": "Exécuter une fois par jour | Exécuter une fois tous les {0} jours", + "affectedMonitorsDescription": "Sélectionnez les sondes concernées par la maintenance en cours", + "affectedStatusPages": "Afficher ce message de maintenance sur les pages d'état sélectionnées", + "atLeastOneMonitor": "Sélectionnez au moins une sonde concernée", + "passwordNotMatchMsg": "Les mots de passe ne correspondent pas.", + "notificationDescription": "Une fois ajoutée, vous devez l'activer manuellement dans les paramètres de vos hôtes.", + "keywordDescription": "Le mot clé sera recherché dans la réponse HTML/JSON reçue du site internet.", + "pauseDashboardHome": "En pause", + "deleteMonitorMsg": "Êtes-vous sûr de vouloir supprimer cette sonde ?", + "deleteMaintenanceMsg": "Voulez-vous vraiment supprimer cette maintenance ?", + "deleteNotificationMsg": "Êtes-vous sûr de vouloir supprimer ce type de notification pour toutes les sondes ?", + "dnsPortDescription": "Port du serveur DNS. La valeur par défaut est 53. Vous pouvez modifier le port à tout moment.", + "resolverserverDescription": "Cloudflare est le serveur par défaut. Vous pouvez modifier le serveur de résolution à tout moment en spécifiant une liste d'adresses IP ou de noms d'hôtes séparés par des virgules.", + "rrtypeDescription": "Veuillez sélectionner un type d'enregistrement DNS", + "pauseMonitorMsg": "Êtes-vous sûr de vouloir mettre en pause cette sonde ?", + "enableDefaultNotificationDescription": "Pour chaque nouvelle sonde, cette notification sera activée par défaut. Vous pouvez toujours désactiver la notification séparément pour chaque sonde.", + "clearEventsMsg": "Êtes-vous sûr de vouloir supprimer tous les événements de cette sonde ? Cette action supprime définitivement les données de messages associées et ne peut être annulée.", + "clearHeartbeatsMsg": "Êtes-vous sûr de vouloir supprimer toutes les données de fréquence cardiaque de cette sonde ? Cette action efface définitivement toutes les données et ne peut être annulée.", + "confirmClearStatisticsMsg": "Êtes-vous sûr de vouloir supprimer toutes les statistiques ?", + "importHandleDescription": "Choisissez « Ignorer l'existant » si vous voulez ignorer chaque sonde ou notification portant le même nom. L'option « Écraser » supprime toutes les sondes et notifications existantes.", + "confirmImportMsg": "Êtes-vous sûr de vouloir importer la sauvegarde ? Veuillez vous assurer que vous avez sélectionné la bonne option d'importation.", + "twoFAVerifyLabel": "Veuillez saisir votre jeton pour vérifier que le système 2FA fonctionne :", + "tokenValidSettingsMsg": "Le jeton est valide. Vous pouvez maintenant sauvegarder les paramètres de double authentification (2FA).", + "confirmEnableTwoFAMsg": "Êtes-vous sûr de vouloir activer la double authentification (2FA) ?", + "confirmDisableTwoFAMsg": "Êtes-vous sûr de vouloir désactiver la double authentification (2FA) ?", + "Settings": "Paramètres", + "Dashboard": "Tableau de bord", + "New Update": "Mise à jour disponible", + "Language": "Langue", + "Appearance": "Apparence", + "Theme": "Thème", + "General": "Général", + "Primary Base URL": "URL principale", + "Version": "Version", + "Check Update On GitHub": "Consulter les mises à jour sur GitHub", + "List": "Lister", + "Add": "Ajouter", + "Add New Monitor": "Ajouter une nouvelle sonde", + "Quick Stats": "Résumé", + "Up": "En ligne", + "Down": "Hors ligne", + "Pending": "En attente", + "Unknown": "Inconnu", + "Pause": "En pause", + "Name": "Nom", + "Status": "État", + "DateTime": "Heure", + "Message": "Messages", + "No important events": "Aucun évènement important", + "Resume": "Reprendre", + "Edit": "Modifier", + "Delete": "Supprimer", + "Current": "Actuellement", + "Uptime": "Disponibilité", + "Cert Exp.": "Expiration Cert SSL.", + "day": "jour | jours", + "-day": "-jours", + "hour": "heure | heures", + "-hour": "-heures", + "Response": "Temps de réponse", + "Ping": "Ping", + "Monitor Type": "Type de sonde", + "Keyword": "Mot-clé", + "Friendly Name": "Nom d'affichage", + "URL": "URL", + "Hostname": "Nom d'hôte / adresse IP", + "Port": "Port", + "Heartbeat Interval": "Intervalle de vérification", + "Retries": "Tentatives", + "Heartbeat Retry Interval": "Intervalle entre chaque nouvelle tentative", + "Resend Notification if Down X times consecutively": "Renvoyer la notification si hors ligne X fois consécutivement", + "Advanced": "Avancé", + "Upside Down Mode": "Mode inversé", + "Max. Redirects": "Nombre maximum de redirections", + "Accepted Status Codes": "Codes HTTP acceptés", + "Push URL": "Push URL", + "needPushEvery": "Vous devez appeler cette URL toutes les {0} secondes.", + "pushOptionalParams": "Paramètres facultatifs : {0}", + "Save": "Sauvegarder", + "Notifications": "Notifications", + "Not available, please setup.": "Non disponible, merci de le configurer.", + "Setup Notification": "Créer une notification", + "Light": "Clair", + "Dark": "Sombre", + "Auto": "Automatique", + "Theme - Heartbeat Bar": "Thème - barres d'état", + "Normal": "Normal", + "Bottom": "En dessous", + "None": "Aucun", + "Timezone": "Fuseau horaire", + "Search Engine Visibility": "Visibilité par les moteurs de recherche", + "Allow indexing": "Autoriser l'indexation", + "Discourage search engines from indexing site": "Refuser l'indexation", + "Change Password": "Changer le mot de passe", + "Current Password": "Mot de passe actuel", + "New Password": "Nouveau mot de passe", + "Repeat New Password": "Répéter votre nouveau mot de passe", + "Update Password": "Mettre à jour le mot de passe", + "Disable Auth": "Désactiver l'authentification", + "Enable Auth": "Activer l'authentification", + "disableauth.message1": "Voulez-vous vraiment {disableAuth} ?", + "disable authentication": "désactiver l'authentification", + "disableauth.message2": "Cette fonctionnalité est conçue pour les scénarios {intendThirdPartyAuth} devant Uptime Kuma, comme Cloudflare Access, Authelia ou d'autres mécanismes d'authentification.", + "where you intend to implement third-party authentication": "où vous avez l'intention d'implémenter une authentification tierce", + "Please use this option carefully!": "Veuillez utiliser cette option avec précaution !", + "Logout": "Déconnexion", + "Leave": "Quitter", + "I understand, please disable": "Je comprends, désactivez-la", + "Confirm": "Confirmer", + "Yes": "Oui", + "No": "Non", + "Username": "Nom d'utilisateur", + "Password": "Mot de passe", + "Remember me": "Se souvenir de moi", + "Login": "Connexion", + "No Monitors, please": "Pas de sondes, veuillez", + "add one": "en ajouter une", + "Notification Type": "Type de notification", + "Email": "Courriel", + "Test": "Tester", + "Certificate Info": "Informations sur le certificat SSL", + "Resolver Server": "Serveur DNS utilisé", + "Resource Record Type": "Type d'enregistrement DNS recherché", + "Last Result": "Dernier résultat", + "Create your admin account": "Créer votre compte administrateur", + "Repeat Password": "Répéter le mot de passe", + "Import Backup": "Importation de la sauvegarde", + "Export Backup": "Exportation de la sauvegarde", + "Export": "Exporter", + "Import": "Importer", + "respTime": "Temps de réponse (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Activé par défaut", + "Apply on all existing monitors": "Appliquer sur toutes les sondes existantes", + "Create": "Créer", + "Clear Data": "Effacer les données", + "Events": "Événements", + "Heartbeats": "Vérifications", + "Auto Get": "Récupérer automatiquement", + "backupDescription": "Vous pouvez sauvegarder toutes les sondes et toutes les notifications dans un fichier JSON.", + "backupDescription2": "PS : Les données relatives à l'historique et aux événements ne sont pas incluses.", + "backupDescription3": "Les données sensibles telles que les jetons de notification sont incluses dans le fichier d'exportation, veuillez les conserver soigneusement.", + "alertNoFile": "Veuillez sélectionner un fichier à importer.", + "alertWrongFileType": "Veuillez sélectionner un fichier JSON à importer.", + "Clear all statistics": "Effacer toutes les statistiques", + "Skip existing": "Sauter l'existant", + "Overwrite": "Écraser", + "Options": "Options", + "Keep both": "Garder les deux", + "Verify Token": "Vérifier le jeton", + "Setup 2FA": "Configurer la double authentification (2FA)", + "Enable 2FA": "Activer la double authentification (2FA)", + "Disable 2FA": "Désactiver la double authentification (2FA)", + "2FA Settings": "Paramètres de la double authentification (2FA)", + "Two Factor Authentication": "Double authentification", + "Active": "Actif", + "Inactive": "Inactif", + "Token": "Jeton", + "Show URI": "Afficher l'URI", + "Tags": "Étiquettes", + "Add New below or Select...": "Ajoutez-en un en dessous ou sélectionnez-le ici…", + "Tag with this name already exist.": "Une étiquette portant ce nom existe déjà.", + "Tag with this value already exist.": "Une étiquette avec cette valeur existe déjà.", + "color": "Couleur", + "value (optional)": "Valeur (facultatif)", + "Gray": "Gris", + "Red": "Rouge", + "Orange": "Orange", + "Green": "Vert", + "Blue": "Bleu", + "Indigo": "Indigo", + "Purple": "Violet", + "Pink": "Rose", + "Search...": "Rechercher…", + "Avg. Ping": "Ping moyen", + "Avg. Response": "Réponse moyenne", + "Entry Page": "Page d'accueil", + "statusPageNothing": "Rien ici, veuillez ajouter un groupe ou une sonde.", + "No Services": "Aucun service", + "All Systems Operational": "Tous les systèmes sont opérationnels", + "Partially Degraded Service": "Service partiellement dégradé", + "Degraded Service": "Service dégradé", + "Add Group": "Ajouter un groupe", + "Add a monitor": "Ajouter une sonde", + "Edit Status Page": "Modifier la page d’état", + "Go to Dashboard": "Accéder au tableau de bord", + "Status Page": "Page d’état", + "Status Pages": "Pages d’état", + "defaultNotificationName": "Ma notification {notification} numéro ({number})", + "here": "ici", + "Required": "Requis", + "telegram": "Telegram", + "ZohoCliq": "ZohoCliq", + "Bot Token": "Jeton du robot", + "wayToGetTelegramToken": "Vous pouvez obtenir un token depuis {0}.", + "Chat ID": "Chat ID", + "supportTelegramChatID": "Prend en charge les messages privés / messages de groupe / l'ID d'un salon", + "wayToGetTelegramChatID": "Vous pouvez obtenir le Chat ID en envoyant un message avec le robot puis en récupérant l'URL pour voir l'ID du salon :", + "YOUR BOT TOKEN HERE": "VOTRE JETON ROBOT ICI", + "chatIDNotFound": "ID du salon introuvable, envoyez un message via le robot avant", + "webhook": "Webhook", + "Post URL": "Post URL", + "Content Type": "Type de contenu", + "webhookJsonDesc": "{0} est bien pour tous les serveurs HTTP modernes comme Express.js", + "webhookFormDataDesc": "{multipart} est bien pour du PHP. Le JSON aura besoin d'être parsé avec {decodeFunction}", + "webhookAdditionalHeadersTitle": "En-têtes supplémentaires", + "webhookAdditionalHeadersDesc": "Définit des en-têtes supplémentaires envoyés avec le webhook. Chaque en-tête doit être défini comme une clé/valeur JSON.", + "smtp": "Courriel (SMTP)", + "secureOptionNone": "Aucun / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignorer les erreurs TLS", + "From Email": "Depuis l'adresse", + "emailCustomSubject": "Objet personnalisé", + "To Email": "Vers l'adresse", + "smtpCC": "CC", + "smtpBCC": "CCI", + "discord": "Discord", + "Discord Webhook URL": "URL vers le webhook Discord", + "wayToGetDiscordURL": "Vous pouvez l'obtenir en allant dans « Paramètres du serveur » -> « Intégrations » -> « Consulter les webhooks» -> « Nouveau Webhook »", + "Bot Display Name": "Nom du robot (affiché)", + "Prefix Custom Message": "Préfixe du message personnalisé", + "Hello @everyone is...": "Bonjour {'@'}everyone il…", + "teams": "Microsoft Teams", + "Webhook URL": "URL vers le webhook", + "wayToGetTeamsURL": "Vous pouvez apprendre comment créer une URL Webhook {0}.", + "wayToGetZohoCliqURL": "Vous pouvez apprendre comment créer une URL Webhook {0}.", + "signal": "Signal", + "Number": "Numéro", + "Recipients": "Destinataires", + "needSignalAPI": "Vous avez besoin d'un client Signal avec l'API REST.", + "wayToCheckSignalURL": "Vous pouvez regarder l'URL suivante pour savoir comment la mettre en place :", + "signalImportant": "IMPORTANT : Vous ne pouvez pas mixer les groupes et les numéros en destinataires !", + "gotify": "Gotify", + "Application Token": "Jeton d'application", + "Server URL": "URL du serveur", + "Priority": "Priorité", + "slack": "Slack", + "Icon Emoji": "Icon Emoji", + "Channel Name": "Nom du salon", + "Uptime Kuma URL": "URL vers Uptime Kuma", + "aboutWebhooks": "Plus d'informations sur les webhooks ici : {0}", + "aboutChannelName": "Mettez le nom du salon dans {0} dans « Nom du salon » si vous voulez contourner le salon webhook. Ex. : #autre-salon", + "aboutKumaURL": "Si vous laissez l'URL d'Uptime Kuma vierge, elle redirigera vers la page du projet GitHub.", + "emojiCheatSheet": "Aide sur les émojis : {0}", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "Push par Techulus", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (prend en charge plus de 50 services de notification)", + "GoogleChat": "Google Chat (Google Workspace uniquement)", + "pushbullet": "Pushbullet", + "Kook": "Kook", + "wayToGetKookBotToken": "Créez une application et récupérer le jeton de robot à l'addresse {0}", + "wayToGetKookGuildID": "Passez en « mode développeur » dans les paramètres de Kook, et cliquez droit sur le Guild pour obtenir son identifiant", + "Guild ID": "Identifiant de Guild", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "Clé d'utilisateur", + "Device": "Appareil", + "Message Title": "Titre du message", + "Notification Sound": "Son de notification", + "More info on:": "Plus d'informations sur : {0}", + "pushoverDesc1": "Priorité d'urgence (2) a un délai par défaut de 30 secondes entre les tentatives et expire après une heure.", + "pushoverDesc2": "Si vous voulez envoyer des notifications sur différents appareils, remplissez le champ « Appareil ».", + "SMS Type": "Type de SMS", + "octopushTypePremium": "Premium (rapide - recommandé pour les alertes)", + "octopushTypeLowCost": "Économique (lent, bloqué de temps en temps par l'opérateur)", + "checkPrice": "Vérification {0} tarifs :", + "apiCredentials": "Identifiants de l'API", + "octopushLegacyHint": "Voulez-vous utiliser l'ancienne version d'Octopush (2011-2020) ou la nouvelle version ?", + "Check octopush prices": "Vérifier les prix d'Octopush {0}.", + "octopushPhoneNumber": "Numéro de téléphone (format international, ex. : +33612345678)", + "octopushSMSSender": "Nom de l'expéditeur : 3-11 caractères alphanumériques avec espace (a-zA-Z0-9)", + "LunaSea Device ID": "Identifiant d'appareil LunaSea", + "Apprise URL": "URL d'Apprise", + "Example:": "Exemple : {0}", + "Read more:": "En savoir plus : {0}", + "Status:": "État : {0}", + "Read more": "En savoir plus", + "appriseInstalled": "Apprise est installé.", + "appriseNotInstalled": "Apprise n'est pas installé. {0}", + "Access Token": "Jeton d'accès", + "Channel access token": "Jeton d'accès au canal", + "Line Developers Console": "Console développeurs Line", + "lineDevConsoleTo": "Console développeurs Line - {0}", + "Basic Settings": "Paramètres de base", + "User ID": "Identifiant utilisateur", + "Messaging API": "API de messagerie", + "wayToGetLineChannelToken": "Premièrement accédez à {0}, créez un provider et définissez un type de salon à «Messaging API». Vous obtiendrez alors le jeton d'accès du salon et l'identifiant utilisateur demandés.", + "Icon URL": "URL vers l'icône", + "aboutIconURL": "Vous pouvez mettre un lien vers une image dans « URL vers l'icône » pour remplacer l'image de profil par défaut. Elle ne sera utilisé que si « Icône émoji » n'est pas défini.", + "aboutMattermostChannelName": "Vous pouvez remplacer le salon par défaut que le webhook utilise en mettant le nom du salon dans le champ « Nom du salon ». Vous aurez besoin de l'activer depuis les paramètres de Mattermost. Ex. : #autre-salon", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - Bon marché mais lent et souvent surchargé. Limité uniquement aux destinataires polonais.", + "promosmsTypeFlash": "SMS FLASH - Le message sera automatiquement affiché sur l'appareil du destinataire. Limité uniquement aux destinataires Polonais.", + "promosmsTypeFull": "SMS FULL - Version premium des SMS. Vous pouvez mettre le nom de l'expéditeur (vous devez l'enregistrer au préalable). Fiable pour les alertes.", + "promosmsTypeSpeed": "SMS SPEED - Priorité élevée pour le système. Très rapide et fiable mais coûteux (environ le double du prix d'un SMS FULL).", + "promosmsPhoneNumber": "Numéro de téléphone (pour les destinataires polonais, vous pouvez ignorer l'indicatif international)", + "promosmsSMSSender": "Nom de l'expéditeur du SMS : Nom pré-enregistré ou l'un de base : InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu WebHookURL", + "matrixHomeserverURL": "L'URL du serveur (avec http(s):// et le port de manière facultative)", + "Internal Room Id": "ID de la salle interne", + "matrixDesc1": "Vous pouvez trouver l'ID de salle interne en regardant dans la section avancée des paramètres dans le client Matrix. C'est censé ressembler à !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Il est fortement recommandé de créer un nouvel utilisateur et de ne pas utiliser le jeton d'accès de votre propre utilisateur Matrix, car il vous donnera un accès complet à votre compte et à toutes les salles que vous avez rejointes. Pour cela, créez un nouvel utilisateur et invitez-le uniquement dans la salle dans laquelle vous souhaitez recevoir la notification. Vous pouvez obtenir le jeton d'accès en exécutant {0}", + "Method": "Méthode", + "Body": "Corps", + "Headers": "En-têtes", + "PushUrl": "URL Push", + "HeadersInvalidFormat": "Les en-têtes de la requête ne sont pas dans un format JSON valide : ", + "BodyInvalidFormat": "Le corps de la requête n'est pas dans un format JSON valide : ", + "Monitor History": "Historique de la sonde", + "clearDataOlderThan": "Conserver l'historique des données de la sonde durant {0} jours.", + "PasswordsDoNotMatch": "Les mots de passe ne correspondent pas.", + "records": "enregistrements", + "One record": "Un enregistrement", + "steamApiKeyDescription": "Pour surveiller un serveur Steam, vous avez besoin d'une clé Steam Web-API. Vous pouvez enregistrer votre clé ici : ", + "Current User": "Utilisateur actuel", + "topic": "Sujet", + "topicExplanation": "Topic MQTT à surveiller", + "successMessage": "Message de réussite", + "successMessageExplanation": "Message MQTT qui sera considéré comme un succès", + "recent": "Récent", + "Done": "Fait", + "Info": "Info", + "Security": "Sécurité", + "Steam API Key": "Clé d'API Steam", + "Shrink Database": "Réduire la base de données", + "Pick a RR-Type...": "Choisissez un type d'enregistrement…", + "Pick Accepted Status Codes...": "Choisissez les codes d’état acceptés…", + "Default": "Défaut", + "HTTP Options": "Options HTTP", + "Create Incident": "Créer un incident", + "Title": "Titre", + "Content": "Contenu", + "Style": "Style", + "info": "Info", + "warning": "Attention", + "danger": "Danger", + "error": "Erreur", + "critical": "Critique", + "primary": "Primaire", + "light": "Blanc", + "dark": "Noir", + "Post": "Post", + "Please input title and content": "Veuillez saisir le titre et le contenu", + "Created": "Créé", + "Last Updated": "Dernière mise à jour", + "Unpin": "Retirer", + "Switch to Light Theme": "Passer au thème clair", + "Switch to Dark Theme": "Passer au thème sombre", + "Show Tags": "Afficher les étiquettes", + "Hide Tags": "Masquer les étiquettes", + "Description": "Description", + "No monitors available.": "Aucune sonde disponible.", + "Add one": "En rajouter une", + "No Monitors": "Aucune sonde", + "Untitled Group": "Groupe sans titre", + "Services": "Services", + "Discard": "Abandonner", + "Cancel": "Annuler", + "Powered by": "Propulsé par", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "Nom d'utilisateur de l'API (incl. webapi_ prefix)", + "serwersmsAPIPassword": "Mot de passe API", + "serwersmsPhoneNumber": "Numéro de téléphone", + "serwersmsSenderName": "Nom de l'expéditeur du SMS (enregistré via le portail client)", + "smseagle": "SMSEagle", + "smseagleTo": "Numéro(s) de téléphone", + "smseagleGroup": "Nom(s) de groupe(s) de répertoire", + "smseagleContact": "Nom(s) de contact du répertoire", + "smseagleRecipientType": "Type de destinataire", + "smseagleRecipient": "Destinataire(s) (les multiples doivent être séparés par une virgule)", + "smseagleToken": "Jeton d'accès à l'API", + "smseagleUrl": "L'URL de votre appareil SMSEagle", + "smseagleEncoding": "Envoyer en Unicode (default=GSM-7)", + "smseaglePriority": "Priorité du message (0 à 9, la priorité la plus élevée = 9)", + "stackfield": "Stackfield", + "Customize": "Personnaliser", + "Custom Footer": "Pied de page personnalisé", + "Custom CSS": "CSS personnalisé", + "smtpDkimSettings": "Paramètres DKIM", + "smtpDkimDesc": "Veuillez vous référer au Nodemailer DKIM {0} pour l'utilisation.", + "documentation": "documentation", + "smtpDkimDomain": "Nom de domaine", + "smtpDkimKeySelector": "Sélecteur de clé", + "smtpDkimPrivateKey": "Clé privée", + "smtpDkimHashAlgo": "Algorithme de hachage (facultatif)", + "smtpDkimheaderFieldNames": "Clés d'en-tête à signer (facultatif)", + "smtpDkimskipFields": "Clés d'en-tête à ne pas signer (facultatif)", + "wayToGetPagerDutyKey": "Vous pouvez l'obtenir en allant dans Service -> Annuaire des services -> (sélectionner un service) -> Intégrations -> Ajouter une intégration. Ici, vous pouvez rechercher \"Events API V2\". Plus d'infos {0}", + "Integration Key": "Clé d'intégration", + "Integration URL": "URL d'intégration", + "Auto resolve or acknowledged": "Résolution automatique ou accusé de réception", + "do nothing": "ne fais rien", + "auto acknowledged": "accusé de réception automatique", + "auto resolve": "résolution automatique", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "Endpoint API", + "alertaEnvironment": "Environnement", + "alertaApiKey": "Clé de l'API", + "alertaAlertState": "État de l'alerte", + "alertaRecoverState": "État de récupération", + "deleteStatusPageMsg": "Voulez-vous vraiment supprimer cette page d'état ?", + "Proxies": "Proxies", + "default": "Défaut", + "enabled": "Activé", + "setAsDefault": "Définir par défaut", + "deleteProxyMsg": "Voulez-vous vraiment supprimer ce proxy pour toutes les sondes ?", + "proxyDescription": "Les proxies doivent être affectés à une sonde pour fonctionner.", + "enableProxyDescription": "Ce proxy n'aura pas d'effet sur les demandes de sonde tant qu'il n'est pas activé. Vous pouvez contrôler la désactivation temporaire du proxy de toutes les sondes en fonction de l'état d'activation.", + "setAsDefaultProxyDescription": "Ce proxy sera activé par défaut pour les nouvelles sondes. Vous pouvez toujours désactiver le proxy séparément pour chaque sonde.", + "Certificate Chain": "Chaîne de certificats", + "Valid": "Valide", + "Invalid": "Non valide", + "AccessKeyId": "ID de clé d'accès", + "SecretAccessKey": "Clé secrète d'accès", + "PhoneNumbers": "Numéros de téléphone", + "TemplateCode": "Modèle de code", + "SignName": "Signature", + "Sms template must contain parameters: ": "Le modèle de SMS doit contenir des paramètres : ", + "Bark Endpoint": "Endpoint Bark", + "Bark Group": "Groupe Bark", + "Bark Sound": "Son Bark", + "WebHookUrl": "WebHookUrl", + "SecretKey": "Clé secrète", + "For safety, must use secret key": "Par sécurité, utilisation obligatoire de la clé secrète", + "Device Token": "Jeton d'appareil", + "Platform": "Plateforme", + "Huawei": "Huawei", + "High": "Haute", + "Retry": "Recommencez", + "Topic": "Sujet", + "WeCom Bot Key": "Clé de robot WeCom", + "Setup Proxy": "Configurer le proxy", + "Proxy Protocol": "Protocole proxy", + "Proxy Server": "Serveur proxy", + "Proxy server has authentication": "Une authentification est nécessaire pour le serveur proxy", + "User": "Utilisateur", + "Installed": "Installé", + "Not installed": "Non installé", + "Running": "En cours", + "Not running": "Ne fonctionne pas", + "Remove Token": "Supprimer le jeton", + "Start": "Démarrer", + "Stop": "Arrêter", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Ajouter une page d’état", + "Slug": "Chemin", + "Accept characters:": "Caractères acceptés :", + "startOrEndWithOnly": "Commence uniquement par {0}", + "No consecutive dashes": "Pas de double tirets", + "Next": "Continuer", + "The slug is already taken. Please choose another slug.": "Un chemin existe déjà. Veuillez en choisir un autre.", + "No Proxy": "Pas de proxy", + "Authentication": "Authentification", + "HTTP Basic Auth": "Authentification de base HTTP", + "New Status Page": "Nouvelle page d’état", + "Page Not Found": "Page non trouvée", + "Reverse Proxy": "Proxy inverse", + "Backup": "Sauvegarde", + "About": "À propos", + "wayToGetCloudflaredURL": "(télécharger cloudflared depuis {0})", + "cloudflareWebsite": "Site web de Cloudflare", + "Message:": "Message :", + "Don't know how to get the token? Please read the guide:": "Vous ne savez pas comment obtenir le jeton ? Lisez le guide :", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "La connexion actuelle peut être perdue si vous vous connectez actuellement via un tunnel Cloudflare. Êtes-vous sûr de vouloir l'arrêter ? Tapez votre mot de passe actuel pour le confirmer.", + "HTTP Headers": "En-têtes HTTP", + "Trust Proxy": "Proxy de confiance", + "Other Software": "Autres logiciels", + "For example: nginx, Apache and Traefik.": "Par exemple : nginx, Apache et Traefik.", + "Please read": "Veuillez lire", + "Subject:": "Objet :", + "Valid To:": "Valable jusqu'au :", + "Days Remaining:": "Jours restants :", + "Issuer:": "Émetteur :", + "Fingerprint:": "Empreinte :", + "No status pages": "Aucune page d’état", + "Domain Name Expiry Notification": "Notification d'expiration du nom de domaine", + "Proxy": "Proxy", + "Date Created": "Date de création", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "Adresse HTTP OneBot", + "onebotMessageType": "Type de message OneBot", + "onebotGroupMessage": "Groupe", + "onebotPrivateMessage": "Privé", + "onebotUserOrGroupId": "ID de groupe/utilisateur", + "onebotSafetyTips": "Pour des raisons de sécurité, vous devez définir un jeton d'accès", + "PushDeer Key": "Clé PushDeer", + "Footer Text": "Texte de pied de page", + "Show Powered By": "Afficher « Propulsé par »", + "Domain Names": "Noms de domaine", + "signedInDisp": "Connecté en tant que {0}", + "signedInDispDisabled": "Authentification désactivée.", + "RadiusSecret": "Secret Radius", + "RadiusSecretDescription": "Secret partagé entre le client et le serveur", + "RadiusCalledStationId": "Identifiant de la station appelée", + "RadiusCalledStationIdDescription": "Identifiant de l'appareil appelé", + "RadiusCallingStationId": "Identifiant de la station appelante", + "RadiusCallingStationIdDescription": "Identifiant de l'appareil appelant", + "Certificate Expiry Notification": "Notification d'expiration du certificat", + "API Username": "Nom d'utilisateur de l'API", + "API Key": "Clé API", + "Recipient Number": "Numéro du destinataire", + "From Name/Number": "De nom/numéro", + "Leave blank to use a shared sender number.": "Laisser vide pour utiliser un numéro d'expéditeur partagé.", + "Octopush API Version": "Version de l'API Octopush", + "Legacy Octopush-DM": "Ancien Octopush-DM", + "endpoint": "endpoint", + "octopushAPIKey": "\"Clé API\" à partir des informations d'identification de l'API HTTP dans le panneau de configuration", + "octopushLogin": "\"Identifiant\" à partir des informations d'identification de l'API HTTP dans le panneau de configuration", + "promosmsLogin": "Nom de connexion API", + "promosmsPassword": "Mot de passe API", + "pushoversounds pushover": "Pushover (par défaut)", + "pushoversounds bike": "Vélo", + "pushoversounds bugle": "Clairon", + "pushoversounds cashregister": "Caisse enregistreuse", + "pushoversounds classical": "Classique", + "pushoversounds cosmic": "Cosmique", + "pushoversounds falling": "Chute", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Arrivée", + "pushoversounds intermission": "Intermission", + "pushoversounds magic": "Magique", + "pushoversounds mechanical": "Mécanique", + "pushoversounds pianobar": "Piano-bar", + "pushoversounds siren": "Sirène", + "pushoversounds spacealarm": "Alarme spatiale", + "pushoversounds tugboat": "Remorqueur", + "pushoversounds alien": "Alarme alienne (version longue)", + "pushoversounds climb": "Escalade (version longue)", + "pushoversounds persistent": "Persistent (version longue)", + "pushoversounds echo": "Pushover Echo (version longue)", + "pushoversounds updown": "Up Down (version longue)", + "pushoversounds vibrate": "Vibration seulement", + "pushoversounds none": "Aucun (silencieux)", + "pushyAPIKey": "Clé API secrète", + "pushyToken": "Jeton d'appareil", + "Show update if available": "Afficher la mise à jour si disponible", + "Also check beta release": "Vérifiez également la version bêta", + "Using a Reverse Proxy?": "Utiliser un proxy inverse ?", + "Check how to config it for WebSocket": "Vérifier comment le configurer pour WebSocket", + "Steam Game Server": "Serveur de jeu Steam", + "Most likely causes:": "Causes les plus probables :", + "The resource is no longer available.": "La ressource n'est plus disponible.", + "There might be a typing error in the address.": "Il se peut qu'il y ait une erreur de frappe dans l'adresse.", + "What you can try:": "Ce que vous pouvez essayer :", + "Retype the address.": "Retaper l'adresse.", + "Go back to the previous page.": "Retourner à la page précédente.", + "Coming Soon": "Prochainement", + "wayToGetClickSendSMSToken": "Vous pouvez obtenir le nom d’utilisateur API et la clé API depuis {here}.", + "Connection String": "Chaîne de connexion", + "Query": "Requête", + "settingsCertificateExpiry": "Expiration du certificat TLS", + "certificationExpiryDescription": "Les sondes HTTPS émettent une notification lorsque le certificat TLS expire dans :", + "Setup Docker Host": "Configurer l'hôte Docker", + "Connection Type": "Type de connexion", + "Docker Daemon": "Deamon Docker", + "deleteDockerHostMsg": "Voulez-vous vraiment supprimer cet hôte Docker pour toutes les sondes ?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Conteneur Docker", + "Container Name / ID": "Nom / ID du conteneur", + "Docker Host": "Hôte Docker", + "Docker Hosts": "Hôtes Docker", + "ntfy Topic": "Topic ntfy", + "Domain": "Domaine", + "Workstation": "Poste de travail", + "disableCloudflaredNoAuthMsg": "Vous êtes en mode No Auth, un mot de passe n'est pas nécessaire.", + "trustProxyDescription": "Faire confiance aux en-têtes 'X-Forwarded-*'. Si vous souhaitez obtenir la bonne adresse IP client et que votre Uptime Kuma se situe derrière un proxy (comme nginx ou Apache), vous devez l'activer.", + "wayToGetLineNotifyToken": "Vous pouvez obtenir un jeton d'accès auprès de {0}", + "Examples": "Exemples", + "Home Assistant URL": "URL vers Home Assistant", + "Long-Lived Access Token": "Jeton d'accès de longue durée", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Un jeton d'accès de longue durée peut être créé en cliquant sur le nom de votre profil (en bas à gauche) et en faisant défiler vers le bas, puis cliquez sur Créer un jeton.", + "Notification Service": "Service de notifications", + "default: notify all devices": "par défaut : notifier tous les appareils", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Une liste des services de notification peut être trouvée dans Home Assistant sous \"Outils de développement > Services\" recherchez \"notification\" pour trouver le nom de votre appareil/téléphone.", + "Automations can optionally be triggered in Home Assistant:": "Les automatisations peuvent éventuellement être déclenchées dans Home Assistant :", + "Trigger type:": "Type de déclencheur :", + "Event type:": "Type d'événement :", + "Event data:": "Données d'événement :", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Ensuite, choisissez une action, par exemple basculer la scène là où une lumière RVB est rouge.", + "Frontend Version": "Version frontend", + "Frontend Version do not match backend version!": "La version frontend ne correspond pas à la version backend !", + "Base URL": "URL de base", + "goAlertInfo": "GoAlert est une application open source pour la planification des appels, les escalades automatisées et les notifications (comme les SMS ou les appels vocaux). Impliquez automatiquement la bonne personne, de la bonne manière et au bon moment ! {0}", + "goAlertIntegrationKeyInfo": "Obtenez la clé d'intégration d'API générique pour le service dans ce format \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" généralement la valeur du paramètre de jeton de l'URL copiée.", + "goAlert": "GoAlert", + "backupOutdatedWarning": "Obsolète : étant donné que de nombreuses fonctionnalités ont été ajoutées et que cette fonctionnalité de sauvegarde est non maintenue, elle ne peut pas générer ou restaurer une sauvegarde complète.", + "backupRecommend": "Veuillez sauvegarder le volume ou le dossier de données (./data/) directement à la place.", + "Optional": "Optionnel", + "squadcast": "Squadcast", + "SendKey": "SendKey", + "SMSManager API Docs": "Documentations de l'API SMSManager", + "Gateway Type": "Type de passerelle", + "SMSManager": "SMSManager", + "You can divide numbers with": "Vous pouvez diviser des nombres avec", + "or": "ou", + "recurringInterval": "Intervalle", + "Recurring": "Récurrent", + "strategyManual": "Activer/désactiver manuellement", + "warningTimezone": "Utilisation du fuseau horaire du serveur", + "weekdayShortMon": "Lun", + "weekdayShortTue": "Mar", + "weekdayShortWed": "Mer", + "weekdayShortThu": "Jeu", + "weekdayShortFri": "Ven", + "weekdayShortSat": "Sam", + "weekdayShortSun": "Dim", + "dayOfWeek": "Jour de la semaine", + "dayOfMonth": "Jour du mois", + "lastDay": "Dernier jour", + "lastDay1": "Dernier jour du mois", + "lastDay2": "Avant-dernier jour du mois", + "lastDay3": "3ème dernier jour du mois", + "lastDay4": "4ème dernier jour du mois", + "No Maintenance": "Aucune maintenance", + "pauseMaintenanceMsg": "Voulez-vous vraiment mettre en pause ?", + "maintenanceStatus-under-maintenance": "En maintenance", + "maintenanceStatus-inactive": "Inactif", + "maintenanceStatus-scheduled": "Programmé", + "maintenanceStatus-ended": "Terminé", + "maintenanceStatus-unknown": "Inconnue", + "Display Timezone": "Afficher le fuseau horaire", + "Server Timezone": "Fuseau horaire du serveur", + "statusPageMaintenanceEndDate": "Fin", + "IconUrl": "URL vers l'icône", + "Enable DNS Cache": "(Obsolète) Activer le cache DNS pour les sondes HTTP(s)", + "Enable": "Activer", + "Disable": "Désactiver", + "dnsCacheDescription": "Il peut ne pas fonctionner dans certains environnements IPv6, désactivez-le si vous rencontrez des problèmes.", + "Single Maintenance Window": "Créneau de maintenance unique", + "Maintenance Time Window of a Day": "Créneau de la maintenance", + "Effective Date Range": "Plage de dates d'effet (facultatif)", + "Schedule Maintenance": "Créer une maintenance", + "Date and Time": "Date et heure", + "DateTime Range": "Plage de dates et d'heures", + "Strategy": "Stratégie", + "Free Mobile User Identifier": "Identifiant d'utilisateur Free Mobile", + "Free Mobile API Key": "Clé d'API Free Mobile", + "Enable TLS": "Activer le TLS", + "Proto Service Name": "Nom du service proto", + "Proto Method": "Méthode Proto", + "Proto Content": "Contenu proto", + "Economy": "Économique", + "Lowcost": "Faible coût", + "high": "Haute", + "General Monitor Type": "Type de sonde générale", + "Passive Monitor Type": "Type de sonde passive", + "Specific Monitor Type": "Type de sonde spécifique", + "dataRetentionTimeError": "La durée de conservation doit être supérieure ou égale à 0", + "infiniteRetention": "Définissez la valeur à 0 pour une durée de conservation infinie.", + "Monitor": "Sonde | Sondes", + "Custom": "Personnalisé", + "confirmDeleteTagMsg": "Voulez-vous vraiment supprimer cette étiquette ? Les sondes associées ne seront pas supprimées.", + "promosmsAllowLongSMS": "Autoriser les longs SMS", + "Help": "Aide", + "Game": "Jeux", + "Packet Size": "Taille du paquet", + "loadingError": "Impossible de récupérer les données, veuillez réessayer plus tard.", + "plugin": "Plugin | Plugins", + "install": "Installer", + "installing": "Installation", + "uninstall": "Désinstaller", + "uninstalling": "Désinstallation", + "confirmUninstallPlugin": "Voulez-vous vraiment désinstaller ce plugin ?", + "Custom Monitor Type": "Type de sonde personnalisé", + "markdownSupported": "Syntaxe Markdown supportée. Si vous utilisez du HTML, évitez les espaces en début de ligne afin d'éviter les problèmes de mise en forme.", + "Google Analytics ID": "Identifiant Google Analytics", + "Server Address": "Adresse du serveur", + "Learn More": "En savoir plus", + "Edit Tag": "Modifier l'étiquette", + "Body Encoding": "Encodage du corps", + "telegramMessageThreadID": "(Facultatif) ID du fil de message", + "telegramMessageThreadIDDescription": "(Facultatif) Identifiant unique pour le fil de discussion ciblé (sujet) du forum ; pour les supergroupes du forum uniquement", + "telegramProtectContent": "Protéger le transfert/l'enregistrement", + "telegramProtectContentDescription": "S'il est activé, les messages du robot dans Telegram seront protégés contre le transfert et l'enregistrement.", + "telegramSendSilently": "Envoyer silencieusement", + "telegramSendSilentlyDescription": "Envoie le message silencieusement. Les utilisateurs recevront une notification sans son.", + "notificationRegional": "Régional", + "Clone Monitor": "Cloner la sonde", + "Clone": "Cloner", + "cloneOf": "Clone de {0}", + "Expiry date": "Date d'expiration", + "Add Another": "Ajouter un autre", + "Key Added": "Clé ajoutée", + "Expiry": "Expiration", + "Continue": "Continuer", + "Add API Key": "Ajouter une clé API", + "No API Keys": "Aucune clé API", + "apiKey-active": "Active", + "apiKey-expired": "Expirée", + "apiKey-inactive": "Inactif", + "Expires": "Expire", + "disableAPIKeyMsg": "Voulez-vous vraiment désactiver cette clé API ?", + "deleteAPIKeyMsg": "Voulez-vous vraiment supprimer cette clé API ?", + "Generate": "Générer", + "API Keys": "Clés API", + "apiKeyAddedMsg": "Votre clé API a été ajoutée. Veuillez la noter car elle ne pourra plus être affichée.", + "Don't expire": "N'expire pas", + "pagertreeUrgency": "Urgence", + "pagertreeSilent": "Silencieux", + "pagertreeLow": "Faible", + "pagertreeMedium": "Moyen", + "pagertreeHigh": "Haut", + "pagertreeResolve": "Résolution automatique", + "pagertreeDoNothing": "Ne fais rien", + "pagertreeIntegrationUrl": "URL d'intégration", + "pagertreeCritical": "Critique", + "wayToGetPagerTreeIntegrationURL": "Après avoir créé l'intégration Uptime Kuma dans PagerTree, copiez le fichier Endpoint. Voir tous les détails {0}", + "lunaseaDeviceID": "Identifiant de l'appareil", + "lunaseaUserID": "Identifiant de l'utilisateur", + "Add New Tag": "Ajouter une étiquette", + "lunaseaTarget": "Cible", + "statusPageRefreshIn": "Actualisation dans : {0}", + "twilioFromNumber": "Du Nombre", + "twilioToNumber": "Au Nombre", + "twilioAccountSID": "ID du compte", + "twilioAuthToken": "Jeton d'authentification / Clé secrète de l'API", + "sameAsServerTimezone": "Identique au fuseau horaire du serveur", + "startDateTime": "Date/heure de début", + "endDateTime": "Date/heure de fin", + "cronExpression": "Expression cron", + "cronSchedule": "Calendrier : ", + "invalidCronExpression": "Expression Cron non valide : {0}", + "ntfyUsernameAndPassword": "Nom d'utilisateur et mot de passe", + "ntfyAuthenticationMethod": "Méthode d'authentification", + "pushoverMessageTtl": "TTL Message (Secondes)", + "Show Clickable Link": "Afficher le lien cliquable", + "Show Clickable Link Description": "Si cette case est cochée, tous ceux qui ont accès à cette page d'état peuvent accéder à l'URL de la sonde.", + "Open Badge Generator": "Ouvrir le générateur de badges", + "Badge Type": "Type de badge", + "Badge Duration": "Durée du badge", + "Badge Prefix": "Préfixe de la valeur du badge", + "Badge Suffix": "Suffixe de la valeur du badge", + "Badge Label Color": "Couleur de l'étiquette du badge", + "Badge Color": "Couleur du badge", + "Badge Label Prefix": "Préfixe d'étiquette de badge", + "Badge Label Suffix": "Suffixe d'étiquette de badge", + "Badge Up Color": "Couleur du badge en ligne", + "Badge Down Color": "Couleur du badge hors ligne", + "Badge Pending Color": "Couleur du badge en attente", + "Badge Maintenance Color": "Couleur du badge maintenance", + "Badge Warn Color": "Couleur du badge d'avertissement", + "Badge Warn Days": "Jours d'avertissement de badge", + "Badge Style": "Style de badge", + "Badge value (For Testing only.)": "Valeur du badge (Pour les tests uniquement.)", + "Monitor Setting": "Réglage de la sonde {0}", + "Badge Generator": "Générateur de badges {0}", + "Badge Label": "Étiquette de badge", + "Badge URL": "URL du badge", + "Cannot connect to the socket server": "Impossible de se connecter au serveur de socket", + "Reconnecting...": "Reconnexion...", + "Edit Maintenance": "Modifier la maintenance", + "Monitor Group": "Groupe de sonde | Groupe de sondes", + "Badge Down Days": "Badge hors ligne", + "Group": "Groupe", + "Home": "Accueil", + "noGroupMonitorMsg": "Pas disponible. Créez d'abord une sonde de groupe.", + "Close": "Fermer", + "chromeExecutableDescription": "Pour les utilisateurs sous Docker, si Chromium n'est pas encore installé, quelques minutes seront nécessaires pour installer et afficher le résultat du test. Cela peut prendre 1 Go d'espace disque.", + "chromeExecutableAutoDetect": "Auto-détecter", + "chromeExecutable": "Exécutable Chrome/Chromium", + "Invert Keyword": "Inverser le mot-clé", + "invertKeywordDescription": "Recherchez le mot-clé absent plutôt que présent.", + "webhookCustomBodyDesc": "Définissez un corps HTTP personnalisé pour la requête. Les variables de modèle {msg}, {heartbeat}, {monitor} sont acceptées.", + "webhookBodyCustomOption": "Corps personnalisé", + "webhookBodyPresetOption": "Préréglages - {0}", + "Request Body": "Corps de la requête", + "twilioApiKey": "Clé API (facultatif)", + "Expected Value": "Valeur attendue", + "Json Query": "Requête Json", + "Badge Duration (in hours)": "Durée du badge (en heures)", + "Badge Preview": "Aperçu du badge", + "aboutNotifyChannel": "Notifier le canal déclenchera une notification de bureau ou mobile pour tous les membres du canal, que leur disponibilité soit active ou absente.", + "Notify Channel": "Notifier le canal", + "filterActive": "Actif", + "filterActivePaused": "En pause", + "Enter the list of brokers": "Entrez la liste des courtiers", + "Press Enter to add broker": "Appuyez sur Entrée pour ajouter un courtier", + "Kafka Topic Name": "Nom du sujet Kafka", + "Enable Kafka SSL": "Activer Kafka SSL", + "Kafka SASL Options": "Options de Kafka SAS", + "Mechanism": "Mécanisme", + "Pick a SASL Mechanism...": "Choisissez un mécanisme SASL…", + "Authorization Identity": "Identité d'autorisation", + "AccessKey Id": "ID de la clé d'accès", + "Secret AccessKey": "Clé d'accès secrète", + "Session Token": "Jeton de session", + "Kafka Brokers": "Courtiers Kafka", + "Kafka Producer Message": "Message du producteur Kafka", + "Enable Kafka Producer Auto Topic Creation": "Activer la création automatique de rubrique Kafka", + "tailscalePingWarning": "Afin d'utiliser la sonde Tailscale Ping, vous devez installer Uptime Kuma sans Docker et également installer le client Tailscale sur votre serveur.", + "Server URL should not contain the nfty topic": "L'URL du serveur ne doit pas contenir le sujet nfty", + "Select": "Sélectionner", + "selectedMonitorCount": "Sélectionné : {0}", + "Check/Uncheck": "Cocher/décocher", + "nostrRelaysHelp": "Une URL relais par ligne", + "nostrRecipients": "Clés publiques des bénéficiaires (npub)", + "nostrSender": "Émetteur clé privée (nsec)", + "nostrRecipientsHelp": "Format npub, un par ligne", + "nostrRelays": "Relais Nostr", + "PushDeer Server": "Serveur PushDeer", + "showCertificateExpiry": "Afficher l'expiration du certificat", + "noOrBadCertificate": "Pas/Mauvais certificat", + "pushDeerServerDescription": "Laissez le champ vide pour utiliser le serveur officiel", + "FlashDuty Severity": "Gravité", + "wayToGetFlashDutyKey": "Pour intégrer Uptime Kuma avec Flashduty : allez dans Canaux > sélectionnez un canal > Intégrations > Ajouter une nouvelle intégration, choisissez Uptime Kuma, puis copiez l’URL Push.", + "Request Timeout": "Délai d'expiration de la demande", + "timeoutAfter": "Délai dépassé après {0} secondes", + "gamedigGuessPort": "Gamedig : Devinez le port", + "gamedigGuessPortDescription": "Le port utilisé par Valve Server Query Protocol peut être différent du port client. Essayez ceci si la sonde ne peut pas se connecter à votre serveur.", + "styleElapsedTimeShowNoLine": "Afficher (pas de ligne)", + "styleElapsedTimeShowWithLine": "Afficher (avec ligne)", + "styleElapsedTime": "Temps écoulé sous la barre d'état", + "enableNSCD": "Activer NSCD (Name Service Cache Daemon) pour mettre en cache toutes les demandes DNS", + "setupDatabaseChooseDatabase": "Quelle base de données souhaitez-vous utiliser ?", + "setupDatabaseEmbeddedMariaDB": "Vous n’avez rien besoin de configurer. Cette image Docker inclut et configure automatiquement MariaDB pour vous. Uptime Kuma se connectera à cette base de données via un socket Unix.", + "setupDatabaseSQLite": "Un fichier de base de données simple, recommandé pour les déploiements à petite échelle. Avant la v2.0.0, Uptime Kuma utilisait SQLite comme base de données par défaut.", + "setupDatabaseMariaDB": "Connectez-vous à une base de données MariaDB externe. Vous devez définir les informations de connexion à la base de données.", + "dbName": "Nom de la base de données", + "Saved.": "Enregistré.", + "toastErrorTimeout": "Délai d'attente pour les notifications d'erreur", + "toastSuccessTimeout": "Délai d'attente pour les notifications de réussite", + "monitorToastMessagesLabel": "Surveiller les notifications Toast", + "monitorToastMessagesDescription": "Les notifications Toast pour les sondes disparaissent après un délai donné en secondes. La valeur -1 désactive le délai d'attente. La valeur 0 désactive les notifications toast.", + "Bark API Version": "Version de l'API Bark", + "pushViewCode": "Comment utiliser une sonde type « Push » (voir le code)", + "pushOthers": "Autres", + "programmingLanguages": "Langages de programmation", + "authInvalidToken": "Jeton invalide.", + "authIncorrectCreds": "Nom d'utilisateur ou mot de passe incorrects.", + "2faAlreadyEnabled": "L'authentification à deux facteurs (2FA) est déjà activée.", + "2faDisabled": "Authentification à deux facteurs (2FA) désactivée.", + "successAdded": "Ajouté avec succès.", + "successEdited": "Modifié avec succès.", + "successBackupRestored": "Sauvegarde restaurée avec succès.", + "successDisabled": "Désactivé avec succès.", + "successEnabled": "Activé avec succès.", + "tagNotFound": "Étiquette non trouvée.", + "foundChromiumVersion": "Version de Chromium/Chrome trouvée : {0}", + "successResumed": "Reprise avec succès.", + "successPaused": "Mis en pause avec succès.", + "authUserInactiveOrDeleted": "L'utilisateur est inactif ou a été supprimé.", + "2faEnabled": "Authentification à deux facteurs (2FA) activée.", + "successDeleted": "Supprimé avec succès.", + "successAuthChangePassword": "Le mot de passe a bien été mis à jour.", + "Reset Token": "Réinitialiser le jeton", + "liquidIntroduction": "La possibilité de créer des modèles est obtenue via le langage de modèles Liquid. Veuillez vous référer au {0} pour les instructions d'utilisation.", + "emailCustomisableContent": "Contenu personnalisable", + "smtpLiquidIntroduction": "Les deux champs suivants peuvent être modélisés via le langage de modèles Liquid. Veuillez vous référer au {0} pour les instructions d'utilisation. Voici les variables disponibles :", + "leave blank for default subject": "laisser vide pour le sujet par défaut", + "emailCustomBody": "Corps personnalisé", + "leave blank for default body": "laisser vide pour le corps par défaut", + "emailTemplateServiceName": "Nom du service", + "emailTemplateHostnameOrURL": "Nom d'hôte ou URL", + "emailTemplateStatus": "Statut", + "emailTemplateMonitorJSON": "objet décrivant la sonde", + "templateMsg": "message de notification", + "templateHeartbeatJSON": "objet décrivant la vérification", + "templateMonitorJSON": "objet décrivant la sonde", + "templateLimitedToUpDownCertNotifications": "disponible uniquement pour les notifications En ligne/Hors ligne/expiration du certificat", + "templateLimitedToUpDownNotifications": "disponible uniquement pour les notifications en ligne/hors ligne", + "emailTemplateMsg": "message de notification", + "emailTemplateLimitedToUpDownNotification": "disponible uniquement pour les vérifications en ligne/hors ligne, sinon nul", + "emailTemplateHeartbeatJSON": "objet décrivant la vérification", + "GrafanaOncallUrl": "URL vers Grafana Oncall", + "noDockerHostMsg": "Pas disponible. Configurez d'abord un hôte Docker.", + "DockerHostRequired": "Veuillez définir l'hôte Docker pour cette sonde.", + "Browser Screenshot": "Capture d'écran du navigateur", + "setup a new monitor group": "configurer un nouveau groupe de sondes", + "remoteBrowsersDescription": "Les navigateurs distants sont une alternative à l'exécution de Chromium localement. Configurez avec un service comme browserless.io ou connectez-vous au vôtre", + "deleteRemoteBrowserMessage": "Êtes-vous sûr de vouloir supprimer ce navigateur distant pour toutes les sondes ?", + "Add a new expiry notification day": "Ajouter un nouveau jour de notification d'expiration", + "Remove the expiry notification": "Supprimer le jour de notification d'expiration", + "openModalTo": "Ouvrir la fenêtre modale pour {0}", + "Add a domain": "Ajouter un domaine", + "Remove domain": "Supprimer le domaine '{0}'", + "Remote Browsers": "Navigateurs distants", + "Remote Browser": "Navigateur distant", + "Add a Remote Browser": "Ajouter un navigateur distant", + "Remote Browser not found!": "Navigateur distant introuvable !", + "self-hosted container": "conteneur auto-hébergé", + "remoteBrowserToggle": "Par défaut, Chromium s'exécute dans le conteneur Uptime Kuma. Vous pouvez utiliser un navigateur distant en activant ce commutateur.", + "useRemoteBrowser": "Utiliser un navigateur distant", + "successKeyword": "Mot-clé de réussite", + "successKeywordExplanation": "Mot clé MQTT qui sera considéré comme un succès", + "Search monitored sites": "Rechercher des sites surveillés", + "settingUpDatabaseMSG": "Mise en place de la base de données. Cela peut prendre un certain temps, veuillez patienter.", + "ntfyPriorityHelptextAllEvents": "Tous les événements sont envoyés avec la priorité maximale", + "ntfyPriorityHelptextAllExceptDown": "Tous les événements sont envoyés avec cette priorité, à l'exception des événements {0}, qui ont une priorité de {1}", + "statusPageSpecialSlugDesc": "Chemin spécial {0} : cette page sera affichée en l'absence de chemin", + "What is a Remote Browser?": "Qu'est-ce qu'un navigateur distant ?", + "Channel access token (Long-lived)": "Jeton d'accès au canal (longue durée)", + "Your User ID": "Votre identifiant", + "documentationOf": "{0} Documentation", + "wayToGetHeiiOnCallDetails": "Comment obtenir l'ID de déclencheur et les clés API d'après la {documentation}", + "From Phone Number / Transmission Path Originating Address (TPOA)": "À partir du numéro de téléphone / Adresse d'origine du chemin de transmission (TPOA)", + "To Phone Number": "Au numéro de téléphone", + "gtxMessagingToHint": "Format international, avec \"+\" ({e164}, {e212} ou {e214})", + "gtxMessagingApiKeyHint": "Vous pouvez trouver votre clé API à l'adresse suivante : Mes comptes de routage > Afficher les informations du compte > Informations d'identification de l'API > API REST (v2.x)", + "gtxMessagingFromHint": "Sur les téléphones mobiles, vos destinataires voient le TPOA affiché comme l'expéditeur du message. Sont autorisés jusqu'à 11 caractères alphanumériques, un shortcode, le longcode local ou les numéros internationaux ({e164}, {e212} ou {e214})", + "Telephone number": "Numéro de téléphone", + "Originator": "Auteur", + "cellsyntOriginator": "Visible sur le téléphone mobile du destinataire en tant qu'expéditeur du message. Les valeurs autorisées et la fonction dépendent du type d'auteur du paramètre.", + "Destination": "Destination", + "Allow Long SMS": "Autoriser les SMS longs", + "cellsyntSplitLongMessages": "Divisez les longs messages en 6 parties maximum. 153 x 6 = 918 caractères.", + "max 15 digits": "maximum 15 chiffres", + "max 11 alphanumeric characters": "maximum 11 caractères alphanumériques", + "Originator type": "Type d'auteur", + "Alphanumeric (recommended)": "Alphanumérique (recommandé)", + "cellsyntOriginatortypeAlphanumeric": "Chaîne alphanumérique (max 11 caractères alphanumériques). Les destinataires ne peuvent pas répondre au message.", + "cellsyntOriginatortypeNumeric": "Valeur numérique (maximum 15 chiffres) avec numéro de téléphone au format international sans 00 en tête (par exemple, le numéro britannique 07920 110 000 doit être défini comme 447920110000). Les destinataires peuvent répondre au message.", + "cellsyntDestination": "Numéro de téléphone du destinataire au format international commençant par 00 suivi de l'indicatif du pays, par ex. 00447920110000 pour le numéro britannique 07920 110 000 (max 17 chiffres au total). Max 25 000 destinataires séparés par des virgules par requête HTTP.", + "callMeBotGet": "Ici, vous pouvez générer un point de terminaison pour {0}, {1} et {2}. Gardez à l’esprit que votre tarif peut être limité. Les limites de débit semblent être : {3}", + "wayToGetWhapiUrlAndToken": "Vous pouvez obtenir l'URL de l'API et le jeton en allant dans votre canal souhaité à partir de {0}", + "whapiRecipient": "Numéro de téléphone / ID de contact / ID de groupe", + "API URL": "URL de l'API", + "wayToWriteWhapiRecipient": "Le numéro de téléphone avec le préfixe international, mais sans le signe plus au début ({0}), l'ID de contact ({1}) ou l'ID de groupe ({2}).", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Saisissez soit le nom d’hôte du serveur auquel vous souhaitez vous connecter, soit {localhost} si vous comptez utiliser un {local_mta}", + "locally configured mail transfer agent": "Agent de transfert de courrier configuré localement", + "Don't mention people": "Ne mentionne pas les personnes", + "Mention group": "Mentionne {groupe}", + "Mentioning": "Mentionner", + "senderSevenIO": "Numéro ou nom d'envoi", + "receiverSevenIO": "Numéro de réception", + "apiKeySevenIO": "Clé API SevenIO", + "wayToGetSevenIOApiKey": "Visitez le tableau de bord sous app.seven.io > développeur > clé api > le bouton d'ajout vert", + "receiverInfoSevenIO": "Si le numéro de réception n'est pas situé en Allemagne, vous devez ajouter l'indicatif du pays devant le numéro (par exemple, pour l'indicatif de pays 1 des États-Unis, utilisez 117612121212 au lieu de 017612121212)", + "Host URL": "URL de l'hôte", + "Select message type": "Sélectionnez le type de message", + "Create new forum post": "Créer un nouveau message sur le forum", + "postToExistingThread": "Publier sur le fil de discussion / forum existant", + "forumPostName": "Nom du post sur le forum", + "threadForumPostID": "ID du fil / ID du post sur le forum", + "whatHappensAtForumPost": "Créer un nouveau post sur le forum. Cela ne publie PAS de messages dans un post existant. Pour publier dans un post existant, utilisez \"{option}\"", + "wayToGetDiscordThreadId": "Obtenir un ID de fil / post sur le forum est similaire à obtenir un ID de canal. Pour en savoir plus sur la manière d'obtenir des IDs, lisez {0}", + "Send to channel": "Envoyer au canal", + "e.g. {discordThreadID}": "e.g. {discordThreadID}", + "Command": "Commande", + "mongodbCommandDescription": "Exécutez une commande MongoDB sur la base de données. Pour plus d'informations sur les commandes disponibles, consultez la {documentation}", + "Refresh Interval": "Intervalle de rafraîchissement", + "Refresh Interval Description": "La page d’état effectuera un rafraîchissement complet du site toutes les {0} secondes", + "ignoreTLSErrorGeneral": "Ignorer l'erreur TLS/SSL pour la connexion", + "Bitrix24 Webhook URL": "URL du Webhook Bitrix24", + "wayToGetBitrix24Webhook": "Vous pouvez créer un webhook en suivant les étapes de {0}", + "bitrix24SupportUserID": "Entrez votre identifiant d'utilisateur dans Bitrix24. Vous pouvez trouver l'ID à partir du lien en allant sur le profil de l'utilisateur.", + "smspartnerPhoneNumber": "Numéro(s) de téléphone", + "smspartnerPhoneNumberHelptext": "Le numéro doit être au format international {0}, {1}. Plusieurs numéros doivent être séparés par {2}", + "smspartnerSenderName": "Nom de l'expéditeur SMS", + "smspartnerSenderNameInfo": "Doit être compris entre 3 et 11 caractères réguliers", + "smspartnerApiurl": "Vous pouvez trouver votre clé API dans votre tableau de bord à {0}", + "threemaRecipient": "Destinataire", + "threemaRecipientType": "Type de destinataire", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypeIdentityFormat": "8 caractères", + "threemaRecipientTypePhone": "Numéro de téléphone", + "threemaRecipientTypePhoneFormat": "E.164, sans le signe + initial", + "threemaRecipientTypeEmail": "Adresse email", + "threemaSenderIdentity": "Identifiant de passerelle", + "threemaSenderIdentityFormat": "8 caractères, commence généralement par *", + "threemaApiAuthenticationSecret": "Secret de l'identifiant de la passerelle", + "wayToGetThreemaGateway": "Vous pouvez vous inscrire à Threema Gateway {0}.", + "threemaBasicModeInfo": "Note : Cette intégration utilise Threema Gateway en mode basique (chiffrement basé sur le serveur). Vous pouvez trouver plus de détails {0}.", + "apiKeysDisabledMsg": "Les clés API sont désactivées car l'authentification est désactivée.", + "cacheBusterParam": "Ajoutez le paramètre {0}", + "snmpCommunityStringHelptext": "Cette chaîne fonctionne comme un mot de passe pour authentifier et contrôler l'accès aux appareils compatibles SNMP. Faites-le correspondre à la configuration de votre périphérique SNMP.", + "privateOnesenderDesc": "Assurez-vous que le numéro de téléphone est valide. Pour envoyer un message vers un numéro de téléphone privé, ex : 628123456789", + "groupOnesenderDesc": "Assurez-vous que le GroupID est valide. Pour envoyer un message dans le groupe, ex : 628123456789-342345", + "Optional: Space separated list of scopes": "Facultatif : liste des étendues séparées par des espaces", + "jsonQueryDescription": "Analyser et extraire des données spécifiques de la réponse JSON du serveur en utilisant une requête JSON ou utiliser « $ » pour la réponse brute, si un JSON n'est pas attendu. Le résultat est ensuite comparé à la valeur attendue, en tant que chaînes de caractères. Consultez {0} pour la documentation et utilisez {1} pour expérimenter avec des requêtes.", + "now": "Maintenant", + "time ago": "il y a {0}", + "-year": "-année", + "Json Query Expression": "Expression de requête JSON", + "and": "et", + "cacheBusterParamDescription": "Paramètre généré aléatoirement pour ignorer les caches.", + "Community String": "Chaîne de communauté", + "OID (Object Identifier)": "OID (identifiant d'objet)", + "snmpOIDHelptext": "Entrez l'OID du capteur ou de l'état que vous souhaitez surveiller. Utilisez des outils de gestion de réseau tels que les navigateurs MIB ou le logiciel SNMP si vous n'êtes pas sûr de l'OID.", + "Condition": "Condition", + "SNMP Version": "Version SNMP", + "Please enter a valid OID.": "Veuillez saisir un OID valide.", + "Host Onesender": "Héberger un expéditeur", + "Token Onesender": "Jeton Onesender", + "Recipient Type": "Type de destinataire", + "Private Number": "Numéro privé", + "Group ID": "Identifiant du groupe", + "wayToGetOnesenderUrlandToken": "Vous pouvez obtenir l'URL et le jeton en accédant au site Web Onesender. Plus d'informations {0}", + "Add Remote Browser": "Ajouter un navigateur distant", + "New Group": "Nouveau groupe", + "Group Name": "Nom du groupe", + "OAuth2: Client Credentials": "OAuth2 : informations d'identification du client", + "Authentication Method": "Méthode d'authentification", + "Authorization Header": "En-tête d'autorisation", + "Form Data Body": "Corps des données du formulaire", + "OAuth Token URL": "URL du jeton OAuth", + "Client ID": "Identifiant client", + "Client Secret": "Secret client", + "OAuth Scope": "Portée OAuth", + "Go back to home page.": "Retournez à la page d'accueil.", + "No tags found.": "Aucune étiquettes trouvée.", + "Lost connection to the socket server.": "Connexion au serveur de socket perdue.", + "Cannot connect to the socket server.": "Impossible de se connecter au serveur de socket.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "URL du webhook SIGNL4", + "signl4Docs": "Vous pouvez trouver plus d'informations sur la façon de configurer SIGNL4 et sur la façon d'obtenir l'URL du webhook SIGNL4 dans le {0}.", + "Conditions": "Conditions", + "conditionAdd": "Ajouter une condition", + "conditionDelete": "Supprimer la condition", + "conditionAddGroup": "Ajouter un groupe", + "conditionDeleteGroup": "Supprimer le groupe", + "conditionValuePlaceholder": "Valeur", + "equals": "est égal", + "not equals": "pas égal", + "contains": "contient", + "not contains": "ne contient pas", + "starts with": "commence par", + "not starts with": "ne commence pas par", + "ends with": "se termine par", + "not ends with": "ne se termine pas par", + "less than": "moins que", + "greater than": "supérieur à", + "less than or equal to": "inférieur ou égal à", + "greater than or equal to": "supérieur ou égal à", + "record": "enregistrer", + "shrinkDatabaseDescriptionSqlite": "Déclencher la commande {vacuum} pour la base de données SQLite. {auto_vacuum} est déjà activé, mais cela ne défragmente pas la base de données ni ne réorganise les pages individuelles de la base de données de la même manière que la commande {vacuum}.", + "ignoredTLSError": "Les erreurs TLS/SSL ont été ignorées", + "CopyToClipboardError": "Impossible de copier dans le presse-papiers : {error}", + "CurlDebugInfo": "Pour déboguer la sonde, vous pouvez soit le coller dans le terminal de votre propre machine, soit dans le terminal de la machine sur lequel kuma de disponibilité s'exécute et voir ce qui vous est demandé. {newiline} Veuillez être conscient des différences de réseau telles que {firewalls}, {dns_resolvers}. ou {docker_networks}.", + "Notification Channel": "Canal de notification", + "Alphanumerical string and hyphens only": "Chaîne alphanumérique et tirets uniquement", + "Custom sound to override default notification sound": "Son personnalisé pour remplacer le son de notification par défaut", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Les notifications urgentes seront envoyées immédiatement, même si l'appareil est en mode \"Ne pas déranger\".", + "Debug": "Déboguer", + "Copy": "Copie", + "CopyToClipboardSuccess": "Copié !", + "firewalls": "pare-feu", + "dns resolvers": "résolveurs DNS", + "docker networks": "réseaux dockers", + "CurlDebugInfoOAuth2CCUnsupported": "Le flux complet des informations d'identification du client OAuth n'est pas pris en charge dans {curl}. {newline}Veuillez obtenir un jeton d'accès bearer token et le transmettre via l'option {oauth2_bearer}.", + "CurlDebugInfoProxiesUnsupported": "La prise en charge du proxy dans la commande {curl} ci-dessus n'est actuellement pas implémentée.", + "Message format": "Format de message", + "Send rich messages": "Envoyer des messages enrichis", + "Sound": "Son", + "Arcade": "Arcade", + "Correct": "Correcte", + "Fail": "Échouer", + "Harp": "Harpe", + "Reveal": "Révéler", + "Bubble": "Bulle", + "Doorbell": "Sonnette", + "Flute": "Flûte", + "Money": "Argent", + "Scifi": "Science-fiction", + "Clear": "Effacer", + "Elevator": "Ascenseur", + "Guitar": "Guitare", + "Pop": "Pop", + "Time Sensitive (iOS Only)": "Sensible au temps (iOS uniquement)", + "From": "De", + "Can be found on:": "Disponible sur : {0}", + "The phone number of the recipient in E.164 format.": "Le numéro de téléphone du destinataire au format E.164.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Soit un identifiant d'expéditeur de texte, soit un numéro de téléphone au format E.164 si vous souhaitez pouvoir recevoir des réponses.", + "RabbitMQ Nodes": "Nœuds de gestion RabbitMQ", + "rabbitmqNodesDescription": "Entrez l'URL des nœuds de gestion RabbitMQ, y compris le protocole et le port. Exemple : {0}", + "rabbitmqNodesRequired": "Veuillez définir les nœuds pour cette sonde.", + "rabbitmqNodesInvalid": "Veuillez utiliser une URL complète (commençant par « http ») pour les nœuds RabbitMQ.", + "RabbitMQ Username": "Nom d'utilisateur RabbitMQ", + "RabbitMQ Password": "Mot de passe RabbitMQ", + "rabbitmqHelpText": "Pour utiliser la sonde, vous devrez activer le plug-in de gestion dans votre configuration RabbitMQ. Pour plus d'informations, veuillez consulter la {rabitmq_documentation}.", + "SendGrid API Key": "Clé API SendGrid", + "Separate multiple email addresses with commas": "Séparez plusieurs adresses e-mail par des virgules", + "aboutSlackUsername": "Modifie le nom d'affichage de l'expéditeur du message. Si vous souhaitez mentionner quelqu’un, incluez-le plutôt dans le nom convivial.", + "templateHostnameOrURL": "Nom d'hôte ou URL", + "telegramUseTemplate": "Utiliser un modèle de message personnalisé", + "telegramTemplateFormatDescription": "Telegram permet d'utiliser différents langages de balisage pour les messages, voir Telegram {0} pour plus de détails.", + "Plain Text": "Texte brut", + "YZJ Webhook URL": "URL du webhook YZJ", + "YZJ Robot Token": "Jeton robot YZJ", + "templateServiceName": "Nom du service", + "templateStatus": "Status", + "telegramUseTemplateDescription": "Si cette option est activée, le message sera envoyé à l'aide d'un modèle personnalisé.", + "Message Template": "Modèle de message", + "Template Format": "Format du modèle", + "wayToGetWahaApiUrl": "L’URL de votre instance WAHA.", + "wayToGetWahaSession": "À partir de cette session, WAHA envoie des notifications à l'ID de discussion. Vous pouvez le trouver dans le tableau de bord WAHA.", + "wahaSession": "Session", + "wahaChatId": "ID de discussion (numéro de téléphone / ID de contact / ID de groupe)", + "wayToGetWahaApiKey": "La clé API est la valeur de la variable d'environnement WHATSAPP_API_KEY que vous avez utilisée pour exécuter WAHA.", + "wayToWriteWahaChatId": "Le numéro de téléphone avec le préfixe international, mais sans le signe plus ({0}), l'identifiant de contact ({1}) ni l'identifiant de groupe ({2}). Les notifications sont envoyées à cet identifiant de chat depuis la session WAHA.", + "telegramServerUrlDescription": "Pour lever les limitations de l’API des bots Telegram ou accéder aux zones bloquées (Chine, Iran, etc.). Pour plus d’informations, cliquez sur {0}. Par défaut : {1}", + "telegramServerUrl": "(Facultatif) URL du serveur", + "Font Twemoji by Twitter licensed under": "La police Twemoji de Twitter est sous licence", + "the smsplanet documentation": "la documentation de smsplanet", + "Phone numbers": "Numéros de téléphone", + "Sender name": "Nom de l'expéditeur", + "smsplanetNeedToApproveName": "Doit être approuvé dans le panneau client", + "smsplanetApiToken": "Jeton pour l'API SMSPlanet", + "smsplanetApiDocs": "Des informations détaillées sur l'obtention de jetons API peuvent être trouvées dans {the_smsplanet_documentation}.", + "pingNumericDescription": "Si coché, les adresses IP seront affichées à la place des noms d’hôtes symboliques", + "pingPerRequestTimeoutDescription": "C’est le temps d’attente maximal (en secondes) avant de considérer un paquet ping comme perdu", + "smtpHelpText": "'SMTPS' teste que SMTP/TLS fonctionne ; 'Ignorer TLS' se connecte en clair ; 'STARTTLS' se connecte, envoie une commande STARTTLS et vérifie le certificat du serveur. Aucun de ces tests n’envoie d’e-mail.", + "Disable URL in Notification": "Désactiver l’URL dans la notification", + "defaultFriendlyName": "Nouvelle sonde", + "Use HTML for custom E-mail body": "Utiliser du HTML pour le corps personnalisé de l'e-mail", + "smseagleGroupV2": "ID(s) de groupe du carnet d’adresses", + "smseagleContactV2": "ID(s) de contact du carnet d’adresses", + "smseagleMsgType": "Type de message", + "smseagleMsgSms": "Message SMS (par défaut)", + "smseagleMsgRing": "Appel sonore", + "smseagleMsgTts": "Appel avec synthèse vocale", + "smseagleMsgTtsAdvanced": "Appel texte vers parole (avancé)", + "smseagleDuration": "Durée (en secondes)", + "smseagleTtsModel": "ID du modèle de synthèse vocale", + "smseagleApiType": "Version de l’API", + "smseagleApiv1": "API v1 (pour les projets existants et la compatibilité ascendante)", + "smseagleApiv2": "API v2 (recommandée pour les nouvelles intégrations)", + "smseagleDocs": "Vérifiez la documentation ou la disponibilité de l’API v2 : {0}", + "smseagleComma": "Les multiples doivent être séparés par une virgule", + "SpugPush Template Code": "Code du modèle", + "FlashDuty Push URL": "URL de notification", + "FlashDuty Push URL Placeholder": "Copier depuis la page d’intégration des alertes", + "pingCountLabel": "Nombre maximal de paquets", + "pingCountDescription": "Nombre de paquets à envoyer avant d’arrêter", + "pingNumericLabel": "Sortie numérique", + "pingGlobalTimeoutLabel": "Délai d’attente global", + "pingGlobalTimeoutDescription": "Durée totale en secondes avant l’arrêt du ping, quel que soit le nombre de paquets envoyés", + "pingPerRequestTimeoutLabel": "Délai d’attente par ping", + "pingIntervalAdjustedInfo": "Intervalle ajusté en fonction du nombre de paquets, du délai d’attente global et du délai d’attente par ping", + "Custom URL": "URL personnalisée", + "customUrlDescription": "Sera utilisée comme URL cliquable à la place de celle du moniteur.", + "OneChatAccessToken": "Jeton d’accès OneChat", + "OneChatUserIdOrGroupId": "ID utilisateur ou ID de groupe OneChat", + "OneChatBotId": "ID du bot OneChat", + "ntfyPriorityHelptextPriorityHigherThanDown": "La priorité normale devrait être plus élevée que la priorité {0}. La priorité {1} est plus élevée que la priorité {0} {2}", + "ntfyPriorityDown": "Priorité pour les événements DOWN", + "Add Tags": "Ajouter des étiquettes", + "tagAlreadyOnMonitor": "Cette étiquette (nom et valeur) est déjà présente sur la sonde ou en attente d'ajout.", + "tagAlreadyStaged": "Cette étiquette (nom et valeur) est déjà préparée pour ce lot.", + "tagNameExists": "Une étiquette système portant ce nom existe déjà. Sélectionnez là dans la liste ou utilisez un autre nom.", + "Add Another Tag": "Ajouter une autre étiquette", + "Staged Tags for Batch Add": "Étiquettes préparées pour l'ajout en lot", + "Clear Form": "Réinitialiser le formulaire", + "pause": "Pause", + "Happy Eyeballs algorithm": "Algorithme Happy Eyeballs", + "Manual": "Manuel", + "Ip Family": "Famille d'adresses IP", + "ipFamilyDescriptionAutoSelect": "Utilise le {happyEyeballs} pour déterminer la famille d'adresses IP.", + "OAuth Audience": "Audience OAuth", + "Optional: The audience to request the JWT for": "Optionnel : Le public pour lequel demander le JWT", + "Path": "Chemin", + "mqttWebSocketPath": "Chemin WebSocket MQTT", + "mqttWebsocketPathExplanation": "Chemin WebSocket pour MQTT sur les connexions WebSocket (par exemple, /mqtt)", + "mqttWebsocketPathInvalid": "Veuillez utiliser un format de chemin WebSocket valide", + "mqttHostnameTip": "Veuillez utiliser ce format {hostnameFormat}", + "Template plain text instead of using cards": "Modèle en texte brut au lieu d'utiliser des cartes", + "issueWithGoogleChatOnAndroidHelptext": "Cela permet également de contourner les bugs en amont comme {issuetackerURL}", + "clearAllEventsMsg": "Êtes-vous sûr de vouloir supprimer tous les événements pour toutes les sondes ? Cette action supprime définitivement les données de messages associées et ne peut être annulée.", + "Clear All Events": "Effacer tous les événements", + "Events cleared successfully": "Événements effacés avec succès.", + "No monitors found": "Aucun sonde trouvé.", + "Could not clear events": "Impossible d’effacer {failed}/{total} événements", + "wayToWriteEvolutionRecipient": "Le numéro de téléphone avec l’indicatif international, mais sans le signe plus au début ({0}), l’ID de contact ({1}) ou l’ID de groupe ({2}).", + "wayToGetEvolutionUrlAndToken": "Vous pouvez obtenir l’URL de l’API et le jeton en accédant à votre canal souhaité depuis {0}", + "evolutionRecipient": "Numéro de téléphone / ID de contact / ID de groupe", + "evolutionInstanceName": "Nom de l’instance", + "brevoApiHelp": "Créez une clé API ici : {0}", + "brevoFromEmail": "Adresse e-mail d’expédition", + "brevoFromName": "Nom de l’expéditeur", + "brevoToEmail": "Adresse e-mail du destinataire", + "brevoBccEmail": "E-mail en copie cachée (BCC)", + "brevoSubject": "Objet", + "brevoLeaveBlankForDefaultSubject": "laisser vide pour l’objet par défaut", + "brevoApiKey": "Clé API Brevo", + "brevoLeaveBlankForDefaultName": "laisser vide pour garder le nom par défaut", + "brevoCcEmail": "E-mail en copie (CC)", + "brevoSeparateMultipleEmails": "Séparez plusieurs adresses e-mail par des virgules", + "Bot secret": "Secret du bot", + "Send DOWN silently": "Envoyer un DOWN silencieusement", + "Nextcloud host": "Hôte Nextcloud", + "Conversation token": "Jeton de conversation", + "Send UP silently": "Envoyer un UP silencieusement", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "L’installation d’un bot Nextcloud Talk nécessite un accès administratif au serveur.", + "auto-select": "Sélection automatique", + "Enter a list of userId": "Entrez une liste d’IDs utilisateur", + "supportBaleChatID": "Prend en charge l’ID de chat direct / de groupe / de canal", + "wayToGetBaleChatID": "Vous pouvez obtenir votre ID de chat en envoyant un message au bot, puis en accédant à cette URL pour voir le chat_id :", + "wayToGetBaleToken": "Vous pouvez obtenir un jeton depuis {0}.", + "Mention Mobile List": "Liste des mentions mobiles", + "Mention User List": "Liste des IDs des utilisateurs à mentionner", + "Dingtalk Mobile List": "Liste des mobiles", + "Dingtalk User List": "Liste des IDs utilisateurs", + "Enter a list of mobile": "Entrez une liste de numéros de téléphone", + "Invalid mobile": "Numéro de téléphone invalide [{mobile}]", + "Invalid userId": "ID utilisateur invalide [{userId}]", + "Maximum Retries": "Nombre maximal de tentatives", + "Number of retry attempts if webhook fails": "Nombre de tentatives de réessai (toutes les 60 à 180 secondes) en cas d’échec du webhook.", + "HTTP Method": "Méthode HTTP", + "webhookPostMethodDesc": "POST convient à la plupart des serveurs HTTP modernes.", + "webhookGetMethodDesc": "GET envoie les données sous forme de paramètres de requête et ne permet pas de configurer un corps. Utile pour déclencher les sondes Push d’Uptime Kuma.", + "descriptionHelpText": "Affiché sur le tableau de bord interne. Le Markdown est autorisé et assaini (les espaces et l’indentation sont conservés) avant l’affichage.", + "Clone Maintenance": "Cloner la maintenance", + "ariaPauseMaintenance": "Mettre en pause ce programme de maintenance", + "ariaResumeMaintenance": "Reprendre ce programme de maintenance", + "ariaCloneMaintenance": "Créer une copie de ce programme de maintenance", + "ariaEditMaintenance": "Modifier ce programme de maintenance", + "ariaDeleteMaintenance": "Supprimer ce programme de maintenance", + "deleteChildrenMonitors": "Supprimer également les sondes enfants directs et leurs sous-éléments s’il y en a | Supprimer également les {count} sondes enfants directs et leurs sous-éléments s’ils en ont", + "deleteGroupMsg": "Êtes-vous sûr de vouloir supprimer ce groupe ?", + "Template ID": "ID du modèle", + "wayToGetClickSMSIRTemplateID": "Votre modèle doit contenir un champ {uptkumaalert}. Vous pouvez créer un nouveau modèle {here}.", + "Recipient Numbers": "Numéros des destinataires", + "twilioMessagingServiceSID": "SID du service de messagerie (optionnel)", + "twilloMessagingServiceSIDHelptext": "Saisissez votre SID de service de messagerie ici si vous utilisez {twillo_messaging_service_help_link} pour gérer les expéditeurs et les fonctionnalités", + "twilioApiKeyHelptext": "La clé API est facultative mais recommandée. Vous pouvez fournir soit le SID de compte et l’AuthToken depuis la page principale de la Twilio Console, soit le SID de compte et le couple clé API / secret de clé API", + "Swindon Web Server Protocol": "Protocole de serveur Web Swindon (encodage JSON)", + "Cobra Real Time Messaging Protocol": "Protocole de messagerie en temps réel Cobra", + "WebSocket Transport for JMAP": "Transport WebSocket pour JMAP (JSON Meta Application Protocol)", + "Collection Update": "Sous-protocole WebSocket de mise à jour de collection", + "Penguin Statistics Live Protocol v3": "Protocole en direct Penguin Statistics v3 (encodage Protobuf)", + "certHostnameMismatch": "Le nom d’hôte du certificat ne correspond pas à l’URL de la sonde.", + "Notifications Enabled": "Notifications activées", + "Allow Notifications": "Autoriser les notifications", + "Browser not supported": "Navigateur non pris en charge", + "Webpush Helptext": "Les notifications push Web ne fonctionnent qu’avec des connexions SSL (HTTPS). Pour les appareils iOS, la page web doit être ajoutée à l’écran d’accueil au préalable.", + "ignoreSecWebsocketAcceptHeaderDescription": "Permet au serveur de ne pas répondre avec l’en-tête Sec-WebSocket-Accept si la mise à niveau WebSocket réussit.", + "Ignore Sec-WebSocket-Accept header": "Ignorer l’en-tête {0}", + "wsSubprotocolDescription": "Saisissez une liste de sous-protocoles séparés par des virgules. Pour plus d'informations sur les sous-protocoles, veuillez consulter la {documentation}", + "WebSocket Application Messaging Protocol": "WAMP (le protocole de messagerie applicative WebSocket)", + "Session Initiation Protocol": "Transport WebSocket pour SIP (Session Initiation Protocol)", + "Network API for Notification Channel": "API réseau RESTful OMA pour le canal de notification", + "Web Process Control Protocol": "Protocole de contrôle des processus Web (WPCP)", + "Advanced Message Queuing Protocol": "Protocole de file d’attente de messages avancé (AMQP) 1.0+)", + "jsflow": "Protocole jsFlow pubsub/queue", + "Reverse Web Process Control": "Protocole de contrôle des processus Web inversé (RWPCP)", + "Extensible Messaging and Presence Protocol": "Transport WebSocket pour le protocole extensible de messagerie et de présence (XMPP)", + "Smart Home IP": "SHIP - IP pour maison intelligente", + "Miele Cloud Connect Protocol": "Protocole de connexion Miele Cloud", + "Push Channel Protocol": "Protocole de canal Push", + "Message Session Relay Protocol": "Transport WebSocket pour MSRP (Message Session Relay Protocol)", + "Binary Floor Control Protocol": "Transport WebSocket pour BFCP (Binary Floor Control Protocol)", + "Softvelum Low Delay Protocol": "Protocole Softvelum à faible latence", + "OPC UA Connection Protocol": "Protocole de connexion OPC UA", + "OPC UA JSON Encoding": "Encodage JSON OPC UA", + "Broadband Forum User Services Platform": "USP (User Services Platform du Broadband Forum)", + "Constrained Application Protocol": "Protocole d’application contraint (CoAP)", + "Softvelum WebSocket signaling protocol": "Protocole de signalisation Softvelum WebSocket", + "Declarative Resource Protocol": "Protocole de ressources déclaratives", + "BACnet Secure Connect Hub Connection": "Connexion au hub BACnet Secure Connect", + "BACnet Secure Connect Direct Connection": "Connexion directe BACnet Secure Connect", + "ITU-T T.140 Real-Time Text": "Texte en temps réel ITU-T T.140", + "Done.best IoT Protocol": "Done. Meilleur protocole IoT", + "Text IRC Protocol": "Protocole IRC textuel", + "Binary IRC Protocol": "Protocole IRC binaire", + "Unable to get permission to notify": "Impossible d’obtenir l’autorisation de notification (la demande a été refusée ou ignorée).", + "showOnlyLastHeartbeat": "Afficher uniquement la dernière vérification", + "minimumIntervalWarning": "Des intervalles inférieurs à 20 secondes peuvent entraîner de mauvaises performances.", + "lowIntervalWarning": "Êtes-vous sûr de vouloir définir l’intervalle en dessous de 20 secondes ? Les performances pourraient être dégradées, en particulier s’il y a un grand nombre de sondes.", + "domainExpiryDescription": "Déclencher une notification lorsque le nom de domaine expire dans :", + "settingsDomainExpiry": "Expiration du domaine", + "labelDomainExpiry": "Expiration du domaine.", + "labelDomainNameExpiryNotification": "Notification d’expiration du nom de domaine", + "Subprotocol": "Sous-protocole", + "Duration (Minutes)": "Durée (minutes)", + "SMTP Security": "Sécurité SMTP", + "Ignore STARTTLS": "Ignorer STARTTLS", + "Use STARTTLS": "Utilisez STARTTLS", + "Enter the list of nodes": "Saisissez la liste des nœuds de gestion RabbitMQ", + "Press Enter to add node": "Appuyez sur Entrée pour ajouter un nœud", + "Deselect All": "Désélectionner tout", + "Select All": "Tout sélectionner", + "resendApiKey": "Renvoyer la clé API", + "resendFromName": "Du nom", + "resendFromEmail": "Depuis un e-mail", + "resendLeaveBlankForDefaultName": "laisser vide pour le nom par défaut", + "resendToEmail": "Envoyer par e-mail", + "resendSubject": "Sujet", + "resendApiHelp": "Créez une clé API ici {0}", + "wsCodeDescription": "Pour plus d’informations sur les codes d’état, veuillez consulter {rfc6455}", + "Subprotocol(s)": "Sous-protocole(s)", + "imageResetConfirmation": "Image réinitialisée par défaut", + "systemService": "Service système", + "systemServiceName": "Nom du service", + "systemServiceDescriptionLinux": "Vérifie si le service systemd Linux {service_name} est actif", + "systemServiceDescriptionWindows": "Vérifie si le Gestionnaire de services Windows {service_name} est en cours d'exécution", + "systemServiceDescription": "Vérifie si le service système {service_name} est actif", + "systemServiceCommandHint": "Commande utilisée : {commande}", + "systemServiceExpectedOutput": "Résultat attendu : \"{0}\"", + "maxPing": "Ping maximum", + "minPing": "Ping minimum", + "avgPing": "Ping moyen", + "Analytics Type": "Type d'analyse", + "Analytics ID": "ID d'analyse", + "Analytics Script URL": "URL du script d'analyse", + "Google": "Google", + "Plausible": "Plausible", + "Matomo": "Matomo", + "Umami": "Umami", + "hostnameCannotBeIP": "Un nom d'hôte DNS ne peut pas être une adresse IP. Vouliez-vous utiliser le champ du résolveur ?", + "invalidHostnameOrIP": "Nom d'hôte ou adresse IP invalide. Le nom d'hôte doit être un nom de domaine pleinement qualifié (FQDN) valide. Les caractères génériques ne sont pas autorisés. Le nom d'hôte peut contenir un trait de soulignement ou se terminer par un point.", + "invalidDNSHostname": "Nom d'hôte invalide. Le nom d'hôte doit être un nom de domaine pleinement qualifié (FQDN) valide. Il peut contenir un caractère générique, un trait de soulignement ou se terminer par un point.", + "wildcardOnlyForDNS": "Les noms d'hôtes génériques ne sont pris en charge que pour les sondes DNS.", + "invalidURL": "URL invalide", + "sipsakPingWarning": "Pour utiliser la sonde de ping SIP Options, vous devez installer Uptime Kuma sans Docker et installer le client Sipsak sur votre serveur.", + "year": "année | années", + "RSS Title": "Titre RSS", + "Leave blank to use status page title": "Laisser vide pour utiliser le titre de la page d'état", + "message": "message", + "json_value": "Valeur JSON", + "Basic checkbox toggle button group": "Groupe de boutons à cocher basiques", + "Sort by status": "Trier par état", + "serwersmsRecipientType": "Type de destinataire", + "serwersmsRecipientTypePhone": "Numéro de téléphone", + "serwersmsRecipientTypeGroup": "Groupe", + "serwersmsGroupId": "Identifiant du groupe", + "serwersmsGroupIdHelptext": "Identifiants (ID ou ID de groupe) dans le panneau client. Ces identifiants peuvent être téléchargés via les groupes d'actions / index ou en les copiant depuis le groupe d'édition du panneau client.", + "smscTranslit": "smscTranslit", + "System Service": "Service système", + "SSL/TLS": "SSL/TLS", + "Open Badge Link Generator": "Ouvre le générateur de lien badge", + "Badge Link Generator": "Générateur de liens de badges {0}", + "Badge Link Generator Helptext": "Des liens de badge sont disponibles pour tous les sondes assignés aux pages d’état publiques. Pour plus d’informations, veuillez consulter la {documentation}.", + "mtls-auth-server-cert-placeholder": "Organisme de certification", + "mtls-auth-server-cert-label": "Certificat", + "mtls-auth-server-key-label": "Clé", + "mtls-auth-server-key-placeholder": "Corps de la clé", + "Sort options": "Options de tri", + "Sort by uptime": "Trier par disponibilité", + "Clear current filters": "Effacer les filtres actuels", + "Sort by name": "Trier par nom", + "Severity": "Gravité", + "Region": "Région", + "PushDeer Server URL": "URL du serveur PushDeer", + "To Number": "Numéro du destinataire", + "Never": "Jamais", + "Check Type": "Type de vérification", + "Service Name": "Nom du service", + "GRPC Options": "Options GRPC", + "End": "Fin", + "playground": "Bac à sable", + "Metadata": "Métadonnées", + "Endpoint": "Point de terminaison", + "Details": "Détails", + "None (Successful Connection)": "Aucun (Connexion réussie)", + "TLS Alerts": "Alertes TLS", + "Expected TLS Alert": "Alerte TLS attendue", + "TLS Alert Spec": "RFC 8446", + "expectedTlsAlertDescription": "Sélectionnez l'alerte TLS que vous prévoyez de recevoir du serveur. Utilisez {code} pour vérifier que les points de terminaison mTLS refusent les connexions sans certificat client. Consultez {link} pour plus d'informations.", + "resendLeaveBlankForDefaultSubject": "Laisser vide pour le sujet par défaut", + "notificationUniversal": "Universel", + "notificationChatPlatforms": "Plateformes de discussion", + "notificationPushServices": "Services push", + "notificationSmsServices": "Services SMS", + "notificationEmail": "E-mail", + "notificationIncidentManagement": "Gestion des incidents", + "notificationHomeAutomation": "Domotique", + "notificationOther": "Autres intégrations", + "mtls-auth-server-ca-label": "Autorité de certification (CA)", + "mtls-auth-server-ca-placeholder": "Autorité de certification du serveur", + "screenshot of the website": "Capture d'écran du site web", + "Basic radio toggle button group": "groupe de boutons radio de base", + "Sort by certificate expiry": "Trier par date d'expiration du certificat", + "Splunk Rest URL": "URL REST Splunk", + "Message Format": "Format des messages", + "GrafanaOncallURL": "URL de Grafana Oncall", + "Show this Maintenance Message on which Status Pages": "Afficher ce message de maintenance sur quelles pages d’état", + "passwordTooWeak": "Le mot de passe est trop faible. Il doit contenir des caractères alphabétiques et numériques et comporter au moins 6 caractères.", + "domain_expiry_unsupported_missing_target": "Aucun domaine ou nom d'hôte valide n'est configuré pour cette sonde", + "domain_expiry_unsupported_monitor_type": "La surveillance de l'expiration des domaines n'est pas prise en charge pour ce type de sonde", + "domain_expiry_unsupported_invalid_domain": "La valeur configurée « {hostname} » n'est pas un nom de domaine valide", + "Resolver Server(s)": "Serveur(s) de résolution", + "HeadersInvalidFormatBecause": "Les en-têtes de requête ne sont pas du JSON valide car {error}", + "BodyInvalidFormatBecause": "Le corps de la requête n'est pas un JSON valide car {error}", + "steamApiKeyDescriptionAt": "Pour surveiller un serveur de jeu Steam, vous avez besoin d'une clé API Web Steam. Vous pouvez enregistrer votre clé API à l'adresse {url}", + "checkPriceAt": "Consultez les prix de {service} sur {url}", + "halopsa_webhook_url_desc": "Saisissez l'URL du webhook depuis votre manuel d'intégration Halo PSA (Configuration > Intégrations > Intégrations personnalisées > Manuels d'intégration). Lors de la création du webhook, sélectionnez « Ne peut être lancé que depuis Halo et depuis un point de terminaison public ».", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "URL du webhook Halo PSA", + "username": "Nom d'utilisateur", + "password": "Mot de passe", + "halopsa_username_desc": "Nom d'utilisateur pour l'authentification auprès du webhook Halo PSA", + "halopsa_setup_step1": "Créer un manuel d'exécution d'intégration dans HaloPSA (Configuration → Intégrations → Manuels d'exécution d'intégration)", + "halopsa_setup_step2": "Configurez les actions du runbook pour traiter les alertes (par exemple, créer un ticket)", + "halopsa_setup_step3": "Copiez l'URL du webhook et collez-la au-dessus du champ de texte", + "halopsa_setup_step4": "Choisissez l'authentification de base et créez un nom d'utilisateur et un mot de passe. Saisissez ou collez ensuite ces informations dans les champs de test ci-dessus", + "noMonitorsSelectedWarning": "Vous lancez une maintenance sans aucune sonde concerné. Êtes-vous sûr de vouloir continuer ?", + "noMonitorsOrStatusPagesSelectedError": "Impossible de créer une page de maintenance sans sonde ou pages d'état concernés", + "You can divide numbers with commas or semicolons": "Vous pouvez séparer les nombres avec {virgule} ou {point-virgule}", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" est trop court pour un domaine de premier niveau", + "domain_expiry_unsupported_public_suffix": "Le domaine « {domain} » ne possède pas de suffixe public valide", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" est une adresse IP. La surveillance de l'expiration des domaines nécessite un nom de domaine", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "La surveillance de l'expiration des domaines n'est pas disponible pour \".{publicSuffix}\" car aucun service RDAP n'est répertorié par l'IANA", + "halopsa_password_desc": "Mot de passe pour l'authentification auprès du webhook Halo PSA", + "Setup Instructions": "Instructions de configuration", + "OptionalParameters": "Paramètres facultatifs", + "aliyun-template-requirements-and-parameters": "Le modèle de SMS Aliyun doit contenir les paramètres suivants : {paramètres}", + "aliyun-template-optional-parameters": "Paramètres optionnels : {parameters}", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "En raison des restrictions des transporteurs, l’activation des variables optionnelles se fait au risque d’une non-livraison", + "ntfyCall": "Appel téléphonique", + "ntfyCallHelptext": "Lancez un appel en cas d'alerte incendie. Choisissez « oui » pour utiliser votre premier numéro vérifié ou saisissez un numéro spécifique (par exemple : +12223334444). Nécessite NTFY Pro et un numéro de téléphone vérifié.", + "enableSSL": "Activer SSL/TLS", + "mariadbUseSSLHelptext": "Activez cette option pour utiliser une connexion chiffrée à votre base de données. Requis pour la plupart des bases de données cloud.", + "mariadbCaCertificateLabel": "Certificat d'autorité de certification", + "mariadbCaCertificateHelptext": "Coller le certificat d'autorité dans le format PEM avec les certificats auto-signés. Laisser vide si votre base de données utilise un certificat signé par une autorité de certification publique.", + "saveResponseForNotifications": "Enregistrer la réponse HTTP de succès pour les notifications", + "saveErrorResponseForNotifications": "Enregistrer la réponse d'erreur HTTP pour les notifications", + "saveResponseDescription": "Stocke la réponse HTTP et la met à disposition des modèles de notification en tant que {templateVariable}", + "responseMaxLength": "Longueur maximale de la réponse (octets)", + "responseMaxLengthDescription": "Taille maximale des données de réponse à stocker. Indiquez 0 pour une taille illimitée. Les réponses plus volumineuses seront tronquées. Valeur par défaut : 1024 (1 Ko)", + "Actions": "Actions", + "selectAllMonitorsAria": "Sélectionner toutes les sondes", + "deselectAllMonitorsAria": "Désélectionnez toutes les sondes", + "selectedMonitorCountMsg": "sélectionné : {n} | sélectionné : {n}", + "selectMonitorMsg": "Sélectionnez les sondes pour effectuer les actions", + "deleteMonitorsMsg": "Êtes-vous sûr de vouloir supprimer les sondes sélectionnés ?", + "pausedMonitorsMsg": "Sonde {n} en pause | Sondes {n} en pause", + "resumedMonitorsMsg": "Surveillance reprise pour {n} sonde | Surveillance reprise pour {n} sondes", + "noMonitorsResumedMsg": "Aucune sonde reprise (aucune n’était inactive)", + "bulkDeleteErrorMsg": "Échec de la suppression de {n} sonde | Échec de la suppression de {n} sondes", + "Only retry if status code check fails": "Réessayer uniquement si la vérification du code d’état échoue", + "retryOnlyOnStatusCodeFailureDescription": "Si cette option est activée, les nouvelles tentatives n'auront lieu qu'en cas d'échec du contrôle du code d'état HTTP (par exemple, si le serveur est hors service). Si le contrôle du code d'état réussit mais que la requête JSON échoue, le système sera immédiatement considéré comme hors service, sans nouvelle tentative.", + "noMonitorsPausedMsg": "Aucune sonde mise en pause (aucune n’était active)", + "deletedMonitorsMsg": "Sonde supprimée : {n} | Sondes supprimées : {n}", + "snmpV3Username": "Nom d'utilisateur SNMPv3", + "WeCom Mentioned Mobile List": "Liste des téléphones mobiles mentionnés sur WeCom", + "Expand All Groups": "Développer tous les groupes", + "WeCom Mentioned Mobile List Description": "Saisissez les numéros de téléphone des personnes à mentionner. Séparez les numéros par des virgules. Utilisez {'@'}all pour mentionner tout le monde.", + "mariadbSocketPathDetectedHelptext": "Connexion à la base de données comme spécifié via la variable d'environnement {0}.", + "Collapse All Groups": "Réduire tous les groupes", + "unknownDays": "Jours inconnus", + "Monitors": "{n} sonde | {n} sondes", + "lastUpdatedAtFromNow": "Dernière mise à jour : {date} ({fromNow})", + "Suppress Notifications": "Supprimer les notifications", + "discordSuppressNotificationsHelptext": "Lorsqu'elle est activée, les messages seront publiés sur le canal mais ne déclencheront pas de notifications push ou de notifications de bureau pour les destinataires.", + "domain_expiry_unsupported_is_icann": "Le domaine « {domain} » n'est pas éligible à la surveillance de l'expiration des domaines, car son suffixe public « .{publicSuffix} » n'est pas géré par l'ICANN", + "versionIs": "Version : {version}", + "logoutCurrentUser": "Déconnexion {username}", + "createdAt": "Créé le : {date}", + "lastUpdatedAt": "Dernière mise à jour : {date}", + "Certificate Chain:": "Chaîne de certificats :", + "Examples:": "Exemples : {0}", + "frontendVersionIs": "Version du frontend : {version}", + "cronScheduleDescription": "Horaire : {description}", + "legacyOctopushEndpoint": "Octopush-DM hérité (point de terminaison : {url})", + "octopushEndpoint": "octopush (point de terminaison : {url})", + "milliseconds": "{n} millisecondes | {n} millisecondes", + "screenshotDelayDescription": "Attendez éventuellement ce nombre de millisecondes avant de prendre la capture d'écran. Maximum : {maxValueMs}ms (0,5 × intervalle).", + "Screenshot Delay": "Délai de capture d’écran (attente de {milliseconds} ms)", + "screenshotDelayWarning": "Des valeurs plus élevées maintiennent le navigateur ouvert plus longtemps, ce qui peut augmenter l’utilisation de la mémoire avec de nombreuses sondes simultanées.", + "days": "{n} jour | {n} jours", + "hours": "{n} heure | {n} heures", + "minutes": "{n} minute | {n} minutes", + "minuteShort": "{n} min | {n} min", + "years": "{n} année | {n} années", + "dateCreatedAtFromNow": "Date de création : {date} ({fromNow})", + "Sets end time based on start time": "Définit l'heure de fin en fonction de l'heure de début", + "Please set start time first": "Veuillez d'abord définir l'heure de début", + "Google Apps Script Webhook URL": "URL du webhook Google Apps Script", + "Deploy a Google Apps Script as a web app and paste the URL here": "Déployez un script Google Apps Script en tant qu'application Web et collez l'URL ici", + "Quick Setup Guide": "Guide d'installation rapide", + "Open your Google Spreadsheet": "Ouvrez votre feuille de calcul Google", + "Go to Extensions → Apps Script": "Accédez à Extensions → Apps Script", + "Paste the script code (see below)": "Collez le code du script (voir ci-dessous)", + "Click Deploy → New deployment → Web app": "Cliquez sur Déployer → Nouveau déploiement → Application Web", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Définir « Exécuter en tant que : Moi » et « Qui a accès : Tout le monde »", + "Google Apps Script Code": "Code Google Apps Script", + "Copy to Clipboard": "Copier dans le Presse-papiers", + "Copied to clipboard!": "Copié dans le presse-papiers !", + "Failed to copy to clipboard": "Impossible de copier dans le presse-papiers", + "Copy the web app URL and paste it above": "Copiez l'URL de l'application web et collez-la ci-dessus", + "disableSTARTTLSDescription": "Activez cette option pour les serveurs SMTP qui ne prennent pas en charge STARTTLS. Les e-mails seront alors envoyés via une connexion non chiffrée.", + "Disable STARTTLS": "Désactiver STARTTLS", + "No incidents recorded": "Aucun incident enregistré", + "Load More": "Charger plus", + "Loading...": "Chargement...", + "Pin this incident": "Épingler cet incident", + "Past Incidents": "Incidents passés", + "Incident title": "Titre de l'incident", + "Incident description": "Description de l'incident", + "Incident not found or access denied": "Incident introuvable ou accès refusé", + "Pinned incidents are shown prominently on the status page": "Les incidents épinglés sont affichés en évidence sur la page d'état", + "Edit Incident": "Modifier l'incident", + "Resolve": "Résoudre", + "Resolved": "Résolu", + "deleteIncidentMsg": "Êtes-vous sûr de vouloir supprimer cet incident ?", + "slug is not found": "Chemin introuvable", + "Please input content": "Veuillez saisir le contenu", + "Please input title": "Veuillez saisir le titre", + "Globalping API Token": "Globalping Token API", + "GlobalpingLocationDocs": "Documentation complète sur la saisie de l'emplacement", + "GlobalpingIpFamilyInfo": "Version IP à utiliser. Autorisée uniquement si la cible est un nom d'hôte.", + "Protocol": "Protocole", + "account settings": "paramètres du compte", + "Location": "Emplacement", + "Monitor Subtype": "Sous-type de moniteur", + "GlobalpingDescription": "Globalping donne accès à des milliers de sondes hébergées par la communauté pour effectuer des tests et des mesures réseau. Une limite de 250 tests par heure est fixée pour tous les utilisateurs anonymes. Pour doubler cette limite à 500 tests par heure, veuillez enregistrer votre token dans {accountSettings}.", + "Globalping - Access global monitoring probes": "Globalping - Accédez aux sondes de surveillance globales", + "GlobalpingLocation": "Le champ « Localisation » accepte les continents, les pays, les régions, les villes, les ASN, les FAI ou les régions cloud. Vous pouvez combiner des filtres avec {plus} (par exemple, {amazonPlusGermany} ou {comcastPlusCalifornia}). Si la latence est un critère important, utilisez des filtres pour restreindre la localisation à une petite région afin d'éviter les pics de latence. {fullDocs}.", + "GlobalpingResolverInfo": "Adresse IPv4/IPv6 ou nom de domaine pleinement qualifié (FQDN). Par défaut, le résolveur réseau local de la sonde est utilisé. Vous pouvez modifier le serveur de résolution à tout moment.", + "templateAvailableVariables": "Variables disponibles", + "example": "Exemple", + "Result": "Résultat", + "Cloud ID": "Identifiant cloud", + "API Token": "Jeton API", + "See Jira Cloud Docs": "Consultez la documentation de Jira Cloud", + "aboutJiraCloudId": "Plus d'informations sur l'identifiant Jira Cloud : {0}", + "see Jira Cloud Docs": "Consultez la documentation de Jira Cloud", + "ntfyUseTemplate": "Personnaliser les modèles de notification", + "ntfyUseTemplateDescription": "Activez cette option pour personnaliser les titres et les messages des notifications à l'aide des modèles LiquidJS", + "ntfyCustomTitle": "Modèle de titre personnalisé", + "ntfyCustomMessage": "Modèle de message personnalisé", + "ntfyNotificationTemplateFallback": "Laisser vide pour utiliser le format Uptime Kuma par défaut", + "globalpingApiTokenDescription": "Obtenez votre jeton d'API Globalping à {0}.", + "GlobalpingHostname": "Une cible de mesure accessible publiquement. Généralement un nom d'hôte ou une adresse IPv4/IPv6, selon le type de mesure.", + "Check for": "Vérifiez", + "Jira Service Management": "Gestion des services Jira", + "discordMessageFormatNormal": "Normal (embeds enrichis)", + "discordMessageFormatMinimalist": "Minimaliste (statut court)", + "discordMessageFormatCustom": "Modèle personnalisé", + "discordMessageTemplate": "Modèle de message", + "slackIncludeGroupName": "Inclure le nom du groupe de sondes", + "slackIncludeGroupNameDescription": "Si activé, le chemin du groupe de sondes sera inclus dans les notifications afin d’aider à distinguer les sondes portant le même nom dans des groupes différents.", + "slackUseTemplate": "Utiliser un modèle de message personnalisé", + "slackUseTemplateDescription": "Si activé, le message sera envoyé à l’aide d’un modèle personnalisé. Vous pouvez utiliser le templating Liquid pour inclure les informations du groupe de sondes via monitorJSON.path ou monitorJSON.pathName.", + "discordMessageFormat": "Format du message", + "discordUseMessageTemplate": "Utiliser un modèle de message personnalisé", + "discordUseMessageTemplateDescription": "Si cette option est activée, le message sera envoyé à l'aide d'un modèle personnalisé (LiquidJS). Laissez ce champ vide pour utiliser le format Uptime Kuma par défaut.", + "halopsa_field_title": "Titre de l'alerte (toujours « Alerte Uptime Kuma »)", + "halopsa_field_monitor": "Nom de la sonde", + "halopsa_field_message": "Message d’alerte complet avec état et détails", + "halopsa_field_timestamp": "Horodatage de l'événement au format ISO 8601", + "halopsa_field_uptime_kuma_version": "Numéro de version Uptime Kuma", + "halopsa_setup_step5": "Configurez le runbook pour utiliser monitor_id afin d'associer les alertes aux tickets existants", + "Webhook Payload Fields": "Champs de charge utile du webhook", + "halopsa_payload_desc": "Les champs suivants sont envoyés à votre webhook Halo PSA :", + "halopsa_field_status": "État de la sonde : UP, DOWN, NOTIFICATION ou UNKNOWN", + "halopsa_field_monitor_id": "Identifiant unique de la sonde (null pour les notifications de test) – À utiliser pour faire correspondre les alertes aux tickets", + "halopsa_id_usage_hint": "💡 Conseil : Utilisez monitor_id pour associer les alertes aux tickets de manière fiable, et heartbeat_id pour suivre l’historique des événements", + "Teltonika SMS Gateway": "Passerelle SMS Teltonika", + "teltonikaVersionWarning": "Ce fournisseur de notifications exige que votre appareil Teltonika exécute la version RMS 7.14.0 ou supérieure.", + "teltonikaUrl": "URL de votre appareil Teltonika", + "RecordMatch": "Correspondance de la valeur d'enregistrement", + "certificateExpiryNotificationHelp": "Le nombre de jours à l'avance peut être configuré dans les Paramètres.", + "teltonikaUrlHelptext": "L'URL doit être spécifiée comme origine complète, par exemple {0} ou {1}.", + "teltonikaUnsafeTls": "Ignorer la validation du certificat", + "teltonikaUnsafeTlsDescription": "Désactiver la validation des certificats TLS vous expose à des attaques de type « homme du milieu » (attaques en cours), pouvant entraîner des fuites de données et la prise de contrôle de vos systèmes. Ne désactivez la validation des certificats que si vous acceptez ce risque d'attaque. Nous vous recommandons d'utiliser Let's Encrypt avec renouvellement automatique.", + "teltonikaUsername": "Nom d'utilisateur de l'API", + "teltonikaUsernameHelptext": "Recommandation : Créez un compte distinct, limité à l’envoi de SMS, et saisissez son nom d’utilisateur ici", + "teltonikaPassword": "Mot de passe API", + "teltonikaPasswordHelptext": "Vous pouvez définir le mot de passe de l'utilisateur API dans votre routeur Teltonika, par exemple {0}", + "teltonikaModem": "Identifiant du modem", + "teltonikaModemHelptext": "L'identifiant du modem SMS doit être au format {0}. Consultez https://developers.teltonika-networks.com/reference/ pour plus d'informations.", + "teltonikaPhoneNumber": "Numéro de téléphone", + "teltonikaPhoneNumberHelptext": "Le numéro doit être au format international {0}, {1}. Un seul numéro est autorisé.", + "teamsEnableTags": "Inclure des balises", + "teamsEnableTagsDescription": "Si activé, le message inclura les tags de la sonde.", + "GlobalpingMonitorDescription": "Globalping donne accès à des milliers de sondes hébergées par la communauté pour effectuer des tests et des mesures réseau. Une limite de 250 tests par heure est fixée pour tous les utilisateurs anonymes. Pour doubler cette limite à 500 tests par heure, veuillez enregistrer votre jeton dans {accountSettings}. Consultez la {docs} pour plus d'informations.", + "RegexMatch": "Saisissez une expression régulière pour faire correspondre la valeur de l'enregistrement", + "domainExpiryNotificationHelp": "Le nombre de jours à l'avance peut être configuré dans les Paramètres.", + "matrixUseTemplate": "Utiliser un modèle de message personnalisé", + "matrixUseTemplateDescription": "Si cette option est activée, le message sera envoyé à l'aide d'un modèle personnalisé.", + "signalUseTemplate": "Utiliser un modèle de message personnalisé", + "signalUseTemplateDescription": "Si cette option est activée, le message sera envoyé à l'aide d'un modèle personnalisé. Vous pouvez utiliser les modèles Liquid pour personnaliser le format de la notification.", + "360messengerAuthToken": "Clé API 360messenger", + "360messengerRecipient": "Numéro(s) de téléphone du destinataire", + "360messengerGroupId": "ID du groupe 360messenger", + "360messengerUseTemplate": "Utilisez un modèle de message personnalisé", + "360messengerTemplate": "Modèle de message 360messenger", + "360messengerGroupList": "Groupes WhatsApp", + "360messengerSelectGroupList": "Sélectionnez un groupe à ajouter", + "360messengerEnableSendToGroup": "Activer l'envoi aux groupes WhatsApp", + "360messengerCustomMessageTemplate": "Modèle de message personnalisé", + "360messengerMessageTemplate": "Modèle de message", + "360messengerWayToGetUrlAndToken": "Vous pouvez obtenir votre clé API 360messenger à partir de {0}.", + "360messengerErrorNoApiKey": "Veuillez d'abord saisir votre clé API 360messenger.", + "360messengerErrorNoGroups": "Aucun groupe WhatsApp n'a été trouvé pour ce compte.", + "360messengerErrorApi": "Impossible de charger la liste des groupes WhatsApp (Erreur {statusCode}  : {message}).", + "360messengerErrorGeneric": "Impossible de charger la liste des groupes WhatsApp : {message}", + "GlobalpingLocationDescription": "Le champ « Emplacement » accepte les continents, les pays, les régions, les villes, les ASN, les FAI ou les régions cloud. Vous pouvez combiner des filtres avec {plus} (par exemple, {amazonPlusGermany} ou {comcastPlusCalifornia}). Si la latence est un critère important, utilisez des filtres pour restreindre l'emplacement à une petite région afin d'éviter les pics de latence et, pour une meilleure stabilité, activez le filtre {datacenter}. {fullDocs}.", + "GlobalpingMultipleLocationsError": "La prise en charge de plusieurs emplacements n'est pas assurée ; veuillez utiliser un seul emplacement par sonde.", + "360messengerSelectedGroupID": "Identifiant(s) du groupe sélectionné(s)", + "360messengerEnableCustomMessage": "Activez un modèle de message personnalisé au lieu du message par défaut.", + "360messengerWayToWriteRecipient": "Saisissez un ou plusieurs numéros de téléphone au format international sans signe plus (par exemple : {0}). Séparez les numéros par des virgules.", + "monitorTypeSpecial": "Spécial", + "monitorTypeGameServer": "Serveur de jeu", + "monitorTypeDatabase": "Sonde de Type base de données", + "fluxerMessageFormatNormal": "Normal (intégrations riches)", + "fluxerUseMessageTemplate": "Utiliser un modèle de message personnalisé", + "Fluxer Webhook URL": "URL du Webhook de Fluxer", + "wayToGetFluxerURL": "Vous pouvez obtenir cela en allant dans les paramètres du canal cible > Webhooks > Créer un webhook > Copier l’URL du webhook.", + "fluxerMessageFormat": "Format du message", + "fluxerMessageFormatCustom": "Modèle personnalisé", + "fluxerMessageTemplate": "Modèle de message", + "fluxerMessageFormatMinimalist": "Minimalist (statut court", + "fluxerUseMessageTemplateDescription": "Si activé, le message sera envoyé en utilisant un modèle personnalisé (LiquidJS). Laissez vide pour utiliser le format Uptime Kuma par défaut.", + "Notification Action": "Action de notification", + "homeAssistantNotificationActionHelptext": "Une liste des actions de notification peut être trouvée dans l’assistant familial sous « Paramètres > Outils de développement > Actions ». Recherchez « notifier » pour trouver vos actions. Entrez uniquement la partie après \"notify.\", par exemple pour l’action \"notify.mobile_app_xyz\" entrez \"mobile_app_xyz\". Pour les notifications mobiles intégrées, recherchez \"Envoyer une notification via mobile_app_xyz\" (et non \"Envoyer une notification\").", + "wayToGetMaxToken": "Vous pouvez obtenir un jeton de bot MAX et d’autres détails auprès de {0}.", + "maxApiUrlDescription": "URL de l'API de base pour MAX Messenger. Par défaut : https://platform-api.max.ru", + "maxTemplateFormatDescription": "MAX messenger prend en charge le texte brut, le Markdown et le formatage HTML.", + "maxMessenger": "MAX messenger", + "wayToGetMaxChatID": "Spécifiez l’identifiant de chat dans MAX où les messages doivent être livrés.", + "maxUseTemplate": "Utiliser un modèle de message personnalisé", + "maxUseTemplateDescription": "Si activé, le message sera envoyé en utilisant un modèle personnalisé.", + "oracledbConnectionString": "Base de données Oracle : {connectionString}", + "API Version": "version de l'API", + "Peer ID": "ID de pair", + "vkApiVersionDescription": "Version de l’API VK utilisée pour les demandes. Laissez la valeur par défaut sauf si vous avez spécifiquement besoin d’une autre version de l’API VK pour la compatibilité.", + "vkDontParseLinks": "Désactiver les extraits de lien", + "vkDontParseLinksDescription": "Si activé, VK ne générera pas d’aperçus/extraits de liens.", + "vkPeerIdDescription": "Entrez le peer_id de la cible VK. Cette valeur est envoyée à l’API telle quelle.", + "telnyxApiKey": "Clé API (V2)", + "telnyxApiKeyHelptext": "Votre clé API Telnyx V2 du portail Telnyx sous Clés API", + "telnyxPhoneNumber": "Du numéro", + "telnyxPhoneNumberHelptext": "Votre numéro de téléphone Telnyx à envoyer depuis (doit être enregistré dans votre compte Telnyx, par exemple +15551234567", + "telnyxToNumber": "Au numéro", + "telnyxToNumberHelptext": "Le numéro de téléphone de destination pour recevoir des notifications par SMS (par exemple, +15559876543", + "telnyxMessagingProfileId": "ID de profil de messagerie (facultatif)", + "telnyxMessagingProfileIdHelptext": "Associez facultativement un profil de messagerie Telnyx pour contrôler les paramètres et les fonctionnalités de l’expéditeur", + "evolutionCustomMessageTitle": "Message personnalisé (facultatif)", + "evolutionCustomMessageDesc": "Laissez vide pour utiliser le message par défaut.", + "WebSocket Options": "Options du WebSocket", + "pingPacketSizeWarning": "Une taille de paquet inférieure à 16 n'entraîne pas de temps d'aller-retour sous Linux", + "VKTeams Bot Token Description": "Vous pouvez découvrir comment obtenir un jeton de robot et d’autres informations à l’adresse {0}.", + "VKTeams Chat Id Description": "Pour les utilisateurs, il s’agit de leur adresse e-mail. Pour les groupes et les canaux, récupérez l’ID dans leurs paramètres et ajoutez {'@'}chat.agent", + "VKTeams Use Template": "Utiliser un modèle de message personnalisé", + "VKTeams Template Format Description": "Pour le style de message, VKTeams prend en charge le texte brut, MarkdownV2 et la mise en forme HTML.", + "VKTeams Base Url Description": "URL d’API de base VKTeams, peut différer selon la solution sur site. Valeur par défaut : https://myteam.mail.ru", + "VKTeams Use Template Description": "Si activé, le message sera envoyé en utilisant un modèle personnalisé.", + "wayToGetEgoSMSToken": "Vous pouvez obtenir votre nom d’utilisateur et mot de passe API EgoSMS à partir de {here}.", + "egosmsSenderDescription": "ID de l’expéditeur affiché aux destinataires (11 caractères maximum). Si vide, utilise EGOSMS par défaut.", + "egosmsPhoneNumberDescription": "Numéro de destinataire avec code pays, sans + ou 00 (par exemple 2567XXXXXXXX).", + "gamedigToken": "Jeton", + "gamedigTokenDescription": "Facultatif. Certains serveurs de jeu nécessitent un jeton d’authentification.", + "Bearer Token": "Jeton Bearer", + "Connection Timeout": "Délai de connexion dépassé", + "smtpAdditionalHeadersTitle": "en-têtes supplémentaires", + "smtpAdditionalHeadersDesc": "Veuillez vous référer aux en-têtes personnalisés Nodemailer {0} pour l’utilisation.", + "Rybbit": "Rybbit", + "Flowtriq Webhook URL": "URL du webhook", + "Flowtriq API Key": "Clé API", + "flowtriqApiKeyDescription": "Clé API facultative pour l'authentification des requêtes de webhook vers votre instance Flowtriq.", + "wxPusherDownloadApp": "Download the app", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) fournit via l’application autonome WxPusher sur 6 plateformes - Android, iOS, HarmonyOS, Windows, macOS et Linux ({download}). Scannez le code QR ci-dessous pour obtenir votre SPT et vous êtes prêt à l’envoyer. Séparez les SPT multiples par une virgule (,) – voir les {docs} pour plus d’informations.", + "wxPusherDocs": "Guide officiel", + "Access Key": "clé d'accès", + "ooredooUsernameDescription": "L’adresse e-mail que vous utilisez pour vous connecter au tableau de bord SMS en masse Ooredoo.", + "ooredooAccessKeyDescription": "Trouvé sous « API Access Key » sur votre tableau de bord SMS Ooredoo. Collez-le exactement comme indiqué - il est encodé en base64 automatiquement avant l’envoi.", + "ooredooBearerTokenDescription": "Le jeton affiché après « Authorization : Bearer » dans l’exemple de commande cURL sur votre tableau de bord SMS Ooredoo.", + "ooredooRecipientDescription": "Saisissez un ou plusieurs nombres séparés par une virgule ou un espace. Les numéros locaux à 7 chiffres sont automatiquement préfixés par 960.", + "Leave blank to use the default Ooredoo endpoint.": "Laissez vide pour utiliser le point de terminaison par défaut d’Ooredoo.", + "plivoAuthIDHelptext": "Votre ID d’authentification Plivo depuis la console Plivo", + "plivoAuthToken": "Jeton d'authentification", + "plivoAuthTokenHelptext": "Votre jeton d’authentification Plivo depuis la console Plivo", + "plivoAuthID": "ID d'authentification", + "plivoFromNumberHelptext": "Le numéro de téléphone Plivo à envoyer depuis (par exemple, +15551234567). SMS peut également utiliser un identifiant d’expéditeur alphanumérique approuvé ; les appels vocaux nécessitent un numéro de téléphone Plivo.", + "plivoToNumber": "Vers le numéro", + "plivoToNumberHelptext": "Le numéro de téléphone de destination qui reçoit l’appel SMS ou vocal (par exemple, +15559876543", + "plivoFromNumber": "Numéro d'origine", + "pm2Description": "Vérifie si le processus PM2 {service_name} est en ligne", + "PM2 Process": "Processus PM2", + "Refresh": "Actualiser", + "Select PM2 process": "Sélectionner le processus PM2", + "pm2ExpectedStates": "État attendu : en ligne. Les états arrêtés et erronés sont traités comme hors ligne.", + "plivoMessageType": "Type de message", + "plivoVoiceCall": "Appel vocal", + "plivoAnswerUrl": "URL de réponse", + "plivoAnswerUrlHelptext": "URL qui renvoie Plivo XML pour l’appel. Le texte de l’alerte est ajouté en tant que ?message= paramètre de requête, qui permet à votre point de terminaison de renvoyer par exemple {message}.", + "ntpStratumThreshold": "Seuil de strate", + "ntpOffsetThreshold": "Seuil de décalage temporel (ms)", + "ntpOffsetThresholdHelp": "Marquer comme hors ligne si le décalage temporel dépasse cette valeur en millisecondes.", + "ntpDispersionThreshold": "Seuil de dispersion des racines (ms", + "ntpDispersionThresholdHelp": "Marquez comme hors ligne si la dispersion racine dépasse cette valeur. La dispersion indique la précision de l’horloge du serveur.", + "ntpThresholdsTitle": "Seuils NTP", + "ntpStratumThresholdHelp": "Marquez comme bas si la strate est à cette valeur ou supérieure. La strate indique la qualité de la source de temps (1=GPS/horloge atomique, 16=non synchronisé).", + "pingletTopicUrlDescription": "L’URL de publication de la rubrique Pinglet à laquelle envoyer des alertes, par exemple https://app.pinglet.co.uk/your-namespace/alerts. La rubrique est créée lors de sa première publication si elle n’existe pas encore.", + "Pinglet Topic URL": "URL de rubrique", + "openwaChatId": "ID Chat (numéro de téléphone / ID de contact / ID de groupe)", + "openwaSession": "Session", + "wayToGetOpenwaApiUrl": "Votre URL d’instance OpenWA.", + "wayToWriteOpenwaChatId": "Le numéro de téléphone avec le préfixe international, mais sans le signe plus au début ({0}), l’ID du contact ({1}) ou l’ID du groupe ({2}). Vous pouvez envoyer à plusieurs ID de chat en les séparant par des virgules.", + "wayToGetOpenwaSession": "ID de session utilisé par OpenWA pour envoyer des notifications.", + "openwaCustomMessageTitle": "Message personnalisé (facultatif)", + "openwaCustomMessageDesc": "Laissez vide pour utiliser le message par défaut.", + "turbosmtpConsumerKey": "Clé du client", + "wayToGetOpenwaApiKey": "Clé API utilisée pour votre instance OpenWA.", + "turbosmtpConsumerSecret": "secret du client", + "wayToGetBearSMSToken": "Vous pouvez obtenir votre nom d’utilisateur API BearSMS et votre clé de hachage depuis {0}.", + "bearsmsPhoneNumberDescription": "Numéro de destinataire avec code pays, sans + ou 00 (par exemple 9725XXXXXXXX).", + "Hash Key": "Clé de hachage", + "bearsmsSenderDescription": "ID d’expéditeur facultatif affiché aux destinataires (11 caractères maximum). Il doit être approuvé dans votre compte BearSMS, sinon l’expéditeur par défaut du compte est utilisé.", + "ClickUp API Token": "Jeton d’API ClickUp", + "ClickUp API Token Description": "Vous pouvez créer un jeton d’accès personnel dans ClickUp en allant dans Paramètres -> Intégrations et ClickApps -> API ClickUp -> Jeton d’API", + "ClickUp Workspace ID": "ID d’espace de travail ClickUp", + "ClickUp Workspace ID Description": "Vous pouvez le trouver dans l’URL de votre espace de travail, juste après app.clickup.com/, par exemple app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ID de canal ClickUp", + "ClickUp Channel ID Description": "Vous pouvez le trouver dans l’URL du canal de chat, juste après /chat/r/, par exemple.../chat/r/abc123-4567.", + "milkyMessageType": "Type de message Milky", + "milkyGroupMessage": "Groupe", + "milkyPrivateMessage": "privé", + "milkySafetyTips": "Pour des raisons de sécurité, le jeton d'accès doit être défini", + "milkyHttpAddress": "Adresse HTTP Milky (sans le suffixe '/api')", + "milkyUserOrGroupId": "ID de groupe/utilisateur", + "smsgatewayUrlHelptext": "URL de base de votre passerelle SMS, sans chemin d’accès API.", + "smsgatewayTo": "Numéro(s) de téléphone", + "smsgatewayToHelptext": "Liste de numéros de téléphone des destinataires séparés par une virgule au format international (par exemple +15551234567)", + "Device ID or Group ID": "ID d’appareil ou ID de groupe", + "notifyAppDeviceIdDescription": "L’ID d’appareil de l’application Notify!, ou un ID de groupe pour alerter tous les appareils d’un groupe en même temps.", + "notifyAppSetup": "Ouvrez Notify ! et trouvez votre ID d’appareil et jeton sous Paramètres. Aucun compte n’est requis. Pour en savoir plus, rendez-vous sur {0}.", + "Client Key": "Clé du client", + "Channel": "Canal", + "SFTP Path to Check": "Chemin SFTP à vérifier", + "sftpPathHelpText": "Facultatif. Si spécifié, le moniteur vérifiera que ce chemin existe sur le serveur SFTP.", + "SSH Private Key": "Clé privée SSH", + "Passphrase": "Phrase secrète", + "sshPrivateKeyHelpText": "Collez le contenu de votre fichier de clé privée (par exemple id_rsa ou id_ed25519).", + "sshPassphraseHelpText": "Facultatif. Requis uniquement si votre clé privée est cryptée avec une phrase de passe.", + "optional": "optionnel", + "Line Number": "Numéro de ligne", + "Amoot SMS recipient numbers help": "Saisissez un ou plusieurs numéros de portable séparés par des virgules.", + "Use Pattern": "Profil d'utilisation", + "Pattern Code ID": "Code d'identification du motif", + "Amoot SMS pattern help": "Saisissez l’ID du code de motif depuis votre panneau Amoot SMS. {0}", + "Create an Amoot SMS pattern": "Créer un modèle de SMS Amoot", + "Use Own Line for Pattern": "Utiliser sa propre ligne pour le modèle", + "sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----", + "Indigo URL": "URL Indigo", + "indigoUrlHelptext": "Votre adresse Indigo Reflector (https://NAME.indigodomo.net) ou le serveur Web Indigo sur votre réseau (https://IP:8176).", + "indigoIgnoreTlsErrorHelptext": "Requis pour le serveur Web Indigo sur votre réseau, qui utilise un certificat auto-signé. Non requis pour le réflecteur.", + "indigoApiKeyHelptext": "Une clé API de votre compte Indigo (autorisations), ou un secret local pour le serveur Web sur votre réseau.", + "Variable ID": "ID de variable", + "indigoVariableIdHelptext": "Le message d’alerte est écrit dans cette variable.", + "Action Group ID": "ID de groupe d’actions", + "indigoActionGroupIdHelptext": "Exécuté après la mise à jour de la variable. Définissez une variable, un groupe d’actions ou les deux. Copiez les ID depuis Indigo en cliquant avec le bouton droit → Copier l’ID." +} diff --git a/src/lang/ga.json b/src/lang/ga.json new file mode 100644 index 0000000..20592cb --- /dev/null +++ b/src/lang/ga.json @@ -0,0 +1,1709 @@ +{ + "setupDatabaseMariaDB": "Ceangail le bunachar sonraí MariaDB seachtrach. Ní mór duit eolas ceangailte na bunachar sonraí a shocrú.", + "settingUpDatabaseMSG": "Ag socrú an bhunachair sonraí. D'fhéadfadh sé tamaill, biodh foighne agat, le do thoil.", + "dbName": "Ainm an Bhunachair Sonraí", + "Settings": "Socruithe", + "Dashboard": "Deais", + "Help": "Cabhair", + "New Update": "Nuashonrú Nua", + "Language": "Teanga", + "Appearance": "Dealramh", + "Theme": "Téama", + "General": "Ginearálta", + "Game": "Cluiche", + "Primary Base URL": "Bonn URL Príomhach", + "Version": "Leagan", + "List": "Liosta", + "Home": "Abhaile", + "Add": "Cui", + "Add New Monitor": "Cuir Monatóir Nua leis", + "Quick Stats": "Staitisticí Tapa", + "Up": "Suas", + "Down": "Síos", + "Pending": "Ar feitheamh", + "Maintenance": "Cothabháil", + "Unknown": "Anaithnid", + "Cannot connect to the socket server": "Ní féidir ceangal a dhéanamh leis an freastalaí soicéad", + "Passive Monitor Type": "Cineál Monatóir Éighníomhach", + "Specific Monitor Type": "Cineál Sonrach Monatóir", + "markdownSupported": "Tacaítear le comhréir marcála síos. Má tá HTML in úsáid agat, seachain spásanna tosaigh chun fadhbanna formáidithe a chosc.", + "pauseDashboardHome": "Sos", + "Pause": "Sos", + "Name": "Ainm", + "Status": "Stádas", + "DateTime": "Dáta Am", + "Message": "Teachtaireacht", + "Resume": "Lean ar aghaidh", + "Edit": "Cuir in Eagar", + "Delete": "Scrios", + "Current": "Reatha", + "Monitor": "Monatóir | Monatóirí", + "day": "lá | laethanta", + "-day": "-lá", + "hour": "uair | uaireanta", + "-hour": "-uair", + "Response": "Freagra", + "Ping": "Ping", + "Uptime": "Am Ata ar Fáil", + "Cert Exp.": "Teastas in éag.", + "Keyword": "Eochairfhocal", + "Invert Keyword": "Inbhéartaigh Eochairfhocal", + "Expected Value": "Luach Meastaithe", + "Json Query": "Ceist Json", + "Friendly Name": "Ainm Cairdiúil", + "URL": "URL", + "Hostname": "Ainm Óstach", + "Port": "Port", + "Request Timeout": "Iarr am istigh", + "timeoutAfter": "Am istigh tar éis {0} soicind", + "Retries": "Atriall", + "languageName": "Gaeilge", + "setupDatabaseChooseDatabase": "Cén bunachar sonraí is mian leat a úsáid?", + "setupDatabaseSQLite": "Comhad bunachar sonraí simplí, molta do sholáthairí beaga scála. Roimh v2.0.0, úsáid Uptime Kuma SQLite mar an bunachar sonraí réamhshocraithe.", + "setupDatabaseEmbeddedMariaDB": "Ní gá duit aon rud a shocrú. Tá MariaDB leabaithe agus cumraithe go huathoibríoch ag an íomhá Docker seo duit. Ceanglóidh Uptime Kuma leis an mbunachar sonraí seo trí soicéad Unix.", + "Check Update On GitHub": "Seiceáil an Nuashonrú ar GitHub", + "statusMaintenance": "Cothabháil", + "Reconnecting...": "Ag athcheangal…", + "General Monitor Type": "Cineál Monatóir Ginearálta", + "No important events": "Gan imeachtaí tábhachtacha", + "Monitor Type": "Cineal Monatóir", + "Heartbeat Interval": "Buille Croí Eatramh", + "Heartbeat Retry Interval": "Eatramh Athtriail Buille Croí", + "Resend Notification if Down X times consecutively": "Seol Fógra arís má tá Down X uair i ndiaidh a chéile", + "Advanced": "Ardleibhéal", + "checkEverySecond": "Seiceáil gach {0} soicind", + "retryCheckEverySecond": "Atriail gach {0} soicind", + "resendEveryXTimes": "Seol ar ais gach {0} uair eile", + "resendDisabled": "Seol ar ais dhíchumasaithe", + "retriesDescription": "Uasmhéid triailí sula ndéantar an tseirbhís a mharcáil mar síos agus go seoltar fógra", + "ignoreTLSError": "Déan neamhaird de earráidí TLS/SSL do shuíomhanna Gréasáin HTTPS", + "upsideDownModeDescription": "Smeach an stádas bun os cionn. Má tá an tseirbhís inrochtana, tá sé SÍOS.", + "maxRedirectDescription": "Uasmhéid na n-athreoraigh le leanúint. Socraigh go 0 chun athreoraigh a dhíchumasú.", + "Upside Down Mode": "Mód bunoscionn", + "Max. Redirects": "Uasmhéid Athreoraíochtaí", + "Push URL": "Brúigh URL", + "pushOptionalParams": "Paraiméadair roghnacha: {0}", + "Accepted Status Codes": "Códanna Stádais a nGlactar leo", + "needPushEvery": "Ba cheart duit an URL seo a ghairm gach {0} soicind.", + "pushOthers": "Eile", + "programmingLanguages": "Teangacha Programála", + "Save": "Sábháil", + "Notifications": "Fógraí", + "Setup Notification": "Socrú Fógra", + "Light": "Solas", + "Dark": "Dorcha", + "Auto": "Uathoibríoch", + "Theme - Heartbeat Bar": "Téama - Barra Buille Croí", + "styleElapsedTime": "Am caite faoi bharra an bhuille croí", + "styleElapsedTimeShowNoLine": "Taispeáin (Gan Líne)", + "Normal": "Gnáth", + "Bottom": "Bun", + "None": "Gan rud", + "Timezone": "Crios ama", + "Disable Auth": "Auth dhíchumasú", + "Enable Auth": "Cumasaigh Auth", + "Change Password": "Athraigh do Phasfhocal", + "Current Password": "Pasfhocal Reatha", + "New Password": "Pasfhocal Nua", + "Repeat New Password": "Déan Pasfhocal Nua arís", + "Search Engine Visibility": "Infheictheacht Inneall Cuardaigh", + "Allow indexing": "Ceadaigh innéacsú", + "Please use this option carefully!": "Bain úsáid as an rogha seo go cúramach, le do thoil!", + "Logout": "Logáil Amach", + "Leave": "Fág", + "I understand, please disable": "Tuigim, le do thoil, múch", + "Confirm": "Deimhnigh", + "Yes": "Tá", + "No": "Níl", + "Username": "Ainm úsáideora", + "Password": "Pasfhocal", + "Remember me": "Cuimhnigh orm", + "Login": "Logáil isteach", + "where you intend to implement third-party authentication": "áit a bhfuil sé ar intinn agat fíordheimhniú tríú páirtí a chur i bhfeidhm", + "disableauth.message1": "An bhfuil tú cinnte gur mhaith leat {disableAuth}?", + "disable authentication": "fíordheimhniú a dhíchumasú", + "No Monitors, please": "Gan Monatóirí, le do thoil", + "add one": "Cuir ceann leis", + "Notification Type": "Cineál Fógra", + "Email": "Ríomhphost", + "Test": "Scrúdú", + "Certificate Info": "Eolas Teastais", + "Resolver Server": "Freastalaí Resolver", + "Resource Record Type": "Cineál Taifead Acmhainn", + "Last Result": "Toradh Deireanach", + "Create your admin account": "Cruthaigh do chuntas riaracháin", + "Repeat Password": "Athscríobh an Pasfhocal", + "Import Backup": "Iompórtáil Cúltaca", + "Export Backup": "Easpórtáil Cúltaca", + "Export": "Easpórtáil", + "Import": "Iompórtáil", + "respTime": "Am freagartha (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Réamhshocrú cumasaithe", + "Apply on all existing monitors": "Déan iarratas ar gach monatóir atá ann cheana féin", + "Create": "Cruthaigh", + "Events": "Imeachtaí", + "Heartbeats": "Buillí croí", + "Auto Get": "Faigh Uathoibríoch", + "Affected Monitors": "Monatóirí Impiachtaithe", + "Pick Affected Monitors...": "Roghnaigh na Monatóirí Impiachtaithe…", + "Start of maintenance": "Tús na Cothabhála", + "All Status Pages": "Gach Leathanach Stádais", + "alertNoFile": "Roghnaigh comhad le haghaidh iompórtála, le do thoil.", + "alertWrongFileType": "Roghnaigh comhad JSON, le do thoil.", + "Clear all statistics": "Glan na Staitistici uile", + "Skip existing": "Scipeáil atá ann cheana", + "Overwrite": "Forscríobh", + "Options": "Roghanna", + "Verify Token": "Dearbhaigh an Tócan", + "Setup 2FA": "Socraigh 2FA", + "Enable 2FA": "Cumasaigh 2FA", + "Disable 2FA": "Díchumasigh 2FA", + "2FA Settings": "Socruithe 2FA", + "filterActive": "Gníomhach", + "filterActivePaused": "Sos", + "Active": "Gníomhach", + "Inactive": "Neamhghníomhach", + "Token": "Tócan", + "Show URI": "Taispeáin URI", + "Tags": "Clibanna", + "Add New Tag": "Cuir Clib Nua", + "Add New below or Select...": "Cuir Nua leis thíos nó Roghnaigh…", + "Tag with this value already exist.": "Tá clib leis an luach seo ann cheana féin.", + "color": "Dath", + "value (optional)": "luach (roghnach)", + "Gray": "Liath", + "Red": "Dearg", + "Orange": "Oráiste", + "Green": "Glas", + "Blue": "Gorm", + "Indigo": "Indeagó", + "Custom": "Gnás", + "Search...": "Cuardach…", + "Search monitored sites": "Cuardaigh suíomhanna monatóireachta", + "Avg. Ping": "Meán Ping", + "Avg. Response": "Meán Freagra", + "statusPageNothing": "Ní dhéanfaidh aon ní anseo, cuir grúpa nó monatóir leis.", + "statusPageRefreshIn": "Athnuaigh i: {0}", + "No Services": "Uimh Seirbhísí", + "Degraded Service": "Seirbhís Díghrádaithe", + "Add Group": "Cuir Grúpa", + "Add a monitor": "Cuir monatóir leis", + "Edit Status Page": "Cuir Leathanach Stádais in Eagar", + "Go to Dashboard": "Téigh go dtí an Deais", + "Status Page": "Leathanach Stádais", + "Status Pages": "Leathanaigh Stádais", + "here": "anseo", + "Required": "Ag teastáil", + "Post URL": "Post URL", + "Content Type": "Cineál Ábhar", + "webhookJsonDesc": "Tá {0} go maith d’aon fhreastalaithe HTTP nua-aimseartha ar nós Express.js", + "templateMsg": "teachtaireacht an fhógra", + "templateHeartbeatJSON": "réad ag cur síos ar bhuille an chroí", + "templateMonitorJSON": "réad ag cur síos ar an monatóir", + "templateLimitedToUpDownCertNotifications": "ar fáil ach amháin le haghaidh fógraí éaga SUAS/SÍOS/Teastas", + "templateLimitedToUpDownNotifications": "ar fáil ach amháin le fógraí SUAS/SÍOS", + "webhookAdditionalHeadersTitle": "Ceanntásca Breise", + "webhookBodyPresetOption": "Réamhshocrú - {0}", + "webhookBodyCustomOption": "Comhlacht Saincheaptha", + "Webhook URL": "URL Crúca Gréasáin", + "Application Token": "Tócan Feidhmchláir", + "Server URL": "Fhreastalaí URL", + "Priority": "Tosaíocht", + "emojiCheatSheet": "Bileog cheat Emoji: {0}", + "Read more": "Leigh Nios mo", + "appriseInstalled": "Tá Apprise suiteáilte.", + "appriseNotInstalled": "Níl Apprise suiteáilte. {0}", + "Method": "Modh", + "Body": "Corp", + "Headers": "Ceanntásca", + "PushUrl": "Brúigh URL", + "BodyInvalidFormat": "Ní JSON bailí an comhlacht iarratais: ", + "Monitor History": "Stair an Mhiontóra", + "clearDataOlderThan": "Keep monitoring history data for {0} days.", + "PasswordsDoNotMatch": "Ní hionann pasfhocail.", + "records": "taifid", + "One record": "Taifead amháin", + "Current User": "Úsáideoir Reatha", + "topic": "Topaic", + "topicExplanation": "Ábhar MQTT le monatóireacht", + "successKeyword": "Keyword Success", + "successKeywordExplanation": "MQTT Keyword to be considered a success", + "recent": "Le déanaí", + "Reset Token": "Athshocraigh Tócan", + "Done": "Déanta", + "Info": "Eolas", + "Security": "Slándáil", + "Steam API Key": "Eochair Steam API", + "Pick a RR-Type...": "Roghnaigh Cineál RR…", + "Pick Accepted Status Codes...": "Roghnaigh Cóid Stádais a nGlactar leo…", + "Default": "Réamhshocrú", + "HTTP Options": "Roghanna HTTP", + "Create Incident": "Cruthaigh Teagmhas", + "Title": "Teideal", + "Content": "Ábhar", + "Style": "Stíl", + "info": "eolas", + "warning": "rabhadh", + "danger": "contúirt", + "error": "earráid", + "critical": "criticiúil", + "primary": "príomha", + "light": "solas", + "dark": "dorcha", + "Post": "Post", + "Please input title and content": "Cuir isteach teideal agus ábhar", + "Created": "Cruthaithe", + "Last Updated": "Nuashonraithe Deiridh", + "Unpin": "Díbhútaigh", + "Switch to Light Theme": "Athraigh go Téama Solais", + "Switch to Dark Theme": "Athraigh go Téama Dorcha", + "Show Tags": "Taispeáin Clibeanna", + "Hide Tags": "Folaigh Clibeanna", + "Description": "Cur síos", + "Add one": "Cuir ceann leis", + "No Monitors": "Uimh Monatóirí", + "Untitled Group": "Grúpa Gan Teideal", + "Services": "Seirbhísí", + "Discard": "Caith amach", + "Cancel": "Cealaigh", + "Select": "Roghnaigh", + "selectedMonitorCount": "Roghnaithe: {0}", + "Check/Uncheck": "Seiceáil/Díthiceáil", + "Powered by": "Cumhachtaithe ag", + "Customize": "Saincheap", + "Custom Footer": "Buntásc an Chustaim", + "Custom CSS": "CSS saincheaptha", + "default": "Réamhshocrú", + "enabled": "Cumasaithe", + "setAsDefault": "Socraigh Mar Réamhshocrú", + "Proxies": "Seachfhreastalaithe", + "proxyDescription": "Ní mór seachvótálaithe a shannadh do mhonatóir chun feidhmiú.", + "setAsDefaultProxyDescription": "Cumasófar an seachfhreastalaí seo de réir réamhshocraithe le haghaidh monatóirí nua. Is féidir leat an seachfhreastalaí a dhíchumasú fós ar leithligh do gach monatóir.", + "Certificate Chain": "Slabhra Teastais", + "Valid": "Bailí", + "Invalid": "Neamhbhailí", + "User": "Úsáideoir", + "Installed": "Suiteáilte", + "Not installed": "Gan suiteáilte", + "Not running": "Gan rith", + "Running": "Ag Rith", + "Remove Token": "Bain Tócan", + "Start": "Tosaigh", + "Stop": "Stad", + "Add New Status Page": "Cuir Leathanach Stádais Nua leis", + "Slug": "Sluga", + "Accept characters:": "Glac le carachtair:", + "startOrEndWithOnly": "Tosaigh nó críoch le {0} amháin", + "statusPageSpecialSlugDesc": "Sluga speisialta {0}: taispeánfar an leathanach seo nuair nach gcuirtear aon sluga ar fáil", + "Next": "Ar aghaidh", + "No Proxy": "Gan Seachfhreastalaí", + "Authentication": "Fíordheimhniú", + "HTTP Basic Auth": "Fíordheimhniú Bunúsach HTTP", + "New Status Page": "Leathanach Stádais Nua", + "Page Not Found": "Ní bhfuarthas an leathanach", + "Reverse Proxy": "Seachfhreastalaí Droim ar Ais", + "Backup": "Cúltaca", + "About": "Faoi", + "wayToGetCloudflaredURL": "(Íoslódáil cloudflared ó {0})", + "cloudflareWebsite": "Láithreán gréasáin Cloudflare", + "HTTP Headers": "Ceanntásca HTTP", + "Trust Proxy": "Iontaobhas Seachfhreastalaí", + "Other Software": "Bogearraí Eile", + "For example: nginx, Apache and Traefik.": "Mar shampla: nginx, Apache agus Traefik.", + "Please read": "Léigh le do thoil", + "Subject:": "Ábhar:", + "Valid To:": "Bailí do:", + "Days Remaining:": "Laethanta fágtha:", + "Issuer:": "Eisitheoir:", + "Fingerprint:": "Fingerprint:", + "No status pages": "Gan leathanaigh stádais", + "Domain Name Expiry Notification": "Fógra Éaga Ainm Fearainn", + "Add a new expiry notification day": "Cuir lá fógra éaga nua leis", + "Remove the expiry notification": "Bain an lá fógra éaga", + "Proxy": "Seachfhreastalaí", + "Date Created": "Dáta Cruthaithe", + "Footer Text": "Téacs Buntásc", + "Show Powered By": "Taispeáin Cumhachtaithe ag", + "Domain Names": "Ainmneacha Fearainn", + "signedInDisp": "Sínithe isteach mar {0}", + "signedInDispDisabled": "Auth Díchumasaithe.", + "RadiusSecret": "Rún Radius", + "RadiusCalledStationId": "Stáisiún Id a thugtar air", + "RadiusCallingStationId": "Ag glaoch ar Aitheantas an Stáisiúin", + "RadiusCallingStationIdDescription": "Aitheantóir an ghléis ghlaoigh", + "Certificate Expiry Notification": "Fógra Éaga Teastais", + "API Username": "Ainm Úsáideora API", + "API Key": "Eochair API", + "Show update if available": "Taispeáin nuashonrú má tá sé ar fáil", + "Using a Reverse Proxy?": "Ag Úsáid Inléasaithe Dearcadh?", + "Check how to config it for WebSocket": "Seiceáil conas é a chumrú le haghaidh WebSocket", + "Steam Game Server": "Freastalaí Cluiche Steam", + "Most likely causes:": "Na cúiseanna is dócha:", + "There might be a typing error in the address.": "Seans go bhfuil earráid chlóscríobh sa seoladh.", + "What you can try:": "Cad is féidir leat triail a bhaint as:", + "Retype the address.": "Athchlóscríobh an seoladh.", + "Go back to the previous page.": "Téigh ar ais go dtí an leathanach roimhe seo.", + "Coming Soon": "Ag teacht go luath", + "Connection String": "Teaghrán Ceangail", + "Query": "Ceist", + "certificationExpiryDescription": "Spreagann Monatóirí HTTPS fógra nuair a théann teastas TLS in éag i:", + "Setup Docker Host": "Socraigh Suas Óstach Docker", + "Connection Type": "Cineál Ceangail", + "Docker Daemon": "Deamhan Docker", + "DockerHostRequired": "Socraigh an tÓstríomhaire Docker don mhonatóir seo.", + "deleteDockerHostMsg": "An bhfuil tú cinnte gur mian leat an t-óstach docker seo a scriosadh do gach monatóir?", + "socket": "Soicéad", + "tcp": "TCP / HTTP", + "Container Name / ID": "Ainm Coimeádán / ID", + "Docker Host": "Óstach Docker", + "Domain": "Fearann", + "Workstation": "Stáisiún Oibre", + "Packet Size": "Méid an Phaicéid", + "Bot Token": "Tócan Bot", + "wayToGetTelegramToken": "Is féidir leat tócan a fháil ó {0}.", + "Chat ID": "Aitheantas Comhrá", + "telegramSendSilently": "Seol Go Ciúin", + "telegramSendSilentlyDescription": "Seolann an teachtaireacht go ciúin. Gheobhaidh úsáideoirí fógra gan aon fhuaim.", + "telegramProtectContent": "Cosain Cur ar Aghaidh/Sábháil", + "telegramProtectContentDescription": "Má tá sé cumasaithe, déanfar na teachtaireachtaí bot in Telegram a chosaint ó chur ar aghaidh agus a shábháil.", + "supportTelegramChatID": "Tacaigh le hAitheantas Comhrá Díreach / Grúpa / Cainéal", + "YOUR BOT TOKEN HERE": "DO CHOMHARTHA BOT ANSEO", + "chatIDNotFound": "Ní aimsítear ID Comhrá; seol teachtaireacht chuig an bot seo ar dtús", + "disableCloudflaredNoAuthMsg": "Tá tú i mód Gan Údar, níl pasfhocal ag teastáil.", + "wayToGetLineNotifyToken": "Is féidir leat comhartha rochtana a fháil ó {0}", + "pushViewCode": "Conas úsáid a bhaint as monatóir Brúigh? (Amharc ar an gCod)", + "Not available, please setup.": "Níl sé ar fáil, socraigh le do thoil.", + "styleElapsedTimeShowWithLine": "Taispeáin (Le Líne)", + "Update Password": "Nuashonraigh Pasfhocal", + "Discourage search engines from indexing site": "Déan innill chuardaigh a dhíspreagadh ón suíomh innéacsaithe", + "disableauth.message2": "Tá sé deartha do chásanna {intendThirdPartyAuth} os comhair Uptime Kuma cosúil le Cloudflare Access, Authelia, nó meicníochtaí fíordheimhnithe eile.", + "Clear Data": "Glan Sonraí", + "Schedule maintenance": "Cothabháil sceideal", + "Select status pages...": "Roghnaigh leathanaigh stádais…", + "Keep both": "Coinnigh an dá rud", + "Two Factor Authentication": "Fíordheimhniú Dhá Fhachtóir", + "Tag with this name already exist.": "Tá clib leis an ainm seo ann cheana féin.", + "Purple": "Corcra", + "Pink": "Bándearg", + "Entry Page": "Leathanach Iontráil", + "All Systems Operational": "Gach Córas Oibriúcháin", + "Partially Degraded Service": "Seirbhís Díghrádaithe i bPáirt", + "defaultNotificationName": "Mo {notification} Fhógra ({number})", + "webhookFormDataDesc": "Tá {multipart} go maith do PHP. Beidh gá an JSON a pharsáil le {decodeFunction}", + "liquidIntroduction": "Baintear amach inúsáidteacht teimpléadaithe tríd an teanga teimpléadaithe Liquid. Féach ar an {0} le haghaidh treoracha úsáide.", + "webhookAdditionalHeadersDesc": "Socraítear ceanntásca breise a sheoltar leis an gcuaille gréasáin. Ba cheart gach ceanntásc a shainiú mar eochair/luach JSON.", + "HeadersInvalidFormat": "Níl na ceanntásca iarratais bailí JSON: ", + "steamApiKeyDescription": "Chun monatóireacht a dhéanamh ar Fhreastalaí Cluiche Gaile is gá duit eochair Steam Web-API. Is féidir leat d’eochair API a chlárú anseo: ", + "Shrink Database": "Laghdaigh Bunachar Sonraí", + "No monitors available.": "Níl monatóirí ar fáil.", + "deleteStatusPageMsg": "An bhfuil tú cinnte gur mhaith leat an leathanach stádais seo a scriosadh?", + "deleteProxyMsg": "An bhfuil tú cinnte gur mhaith leat an seachfhreastalaí seo a scriosadh do gach monatóir?", + "enableProxyDescription": "Ní bheidh éifeacht ag an seachfhreastalaí seo ar iarratais ar mhonatóir go dtí go ngníomhófar é. Is féidir leat an seachfhreastalaí a dhíchumasú go sealadach ó gach monatóir de réir stádas gníomhachtaithe.", + "No consecutive dashes": "Uimh dashes as a chéile", + "The slug is already taken. Please choose another slug.": "Tá an sluga glactha cheana féin. Roghnaigh sluga eile le do thoil.", + "Message:": "Teachtaireacht:", + "Don't know how to get the token? Please read the guide:": "Níl a fhios agat conas an tócan a fháil? Léigh an treoir le do thoil:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Seans go gcaillfear an nasc reatha más rud é go bhfuil tú ag nascadh trí Thollán Cloudflare faoi láthair. An bhfuil tú cinnte gur mhaith leat é a stopadh? Clóscríobh do phasfhocal reatha chun é a dhearbhú.", + "RadiusSecretDescription": "Rún Comhroinnte idir an cliant agus an freastalaí", + "RadiusCalledStationIdDescription": "Aitheantóir na feiste ar a dtugtar", + "Also check beta release": "Seiceáil freisin scaoileadh béite", + "The resource is no longer available.": "Níl an acmhainn ar fáil a thuilleadh.", + "settingsCertificateExpiry": "Éaga Teastas TLS", + "noDockerHostMsg": "Níl sé ar fáil. Socraigh Óstach Docker ar dtús.", + "tailscalePingWarning": "D'fhonn an monatóir Tailscale Ping a úsáid, ní mór duit Uptime Kuma gan Docker a shuiteáil agus cliant Tailscale a shuiteáil ar do fhreastalaí freisin.", + "Docker Container": "Coimeádán Docker", + "Docker Hosts": "Óstáin Docker", + "telegramMessageThreadID": "(Roghnach) ID Snáithe Teachtaireacht", + "telegramMessageThreadIDDescription": "Roghnach Aitheantóir uathúil don snáithe sprice teachtaireachta (ábhar) an fhóraim; d'ollghrúpaí fóraim amháin", + "wayToGetTelegramChatID": "Is féidir leat d'aitheantas comhrá a fháil trí theachtaireacht a sheoladh chuig an bot agus dul chuig an URL seo chun an chat_id a fheiceáil:", + "trustProxyDescription": "Cuir muinín i gceanntásca 'X-Ar Aghaidh-*'. Más mian leat an IP cleint ceart a fháil agus má tá do Uptime Kuma taobh thiar de sheachfhreastalaí cosúil le Nginx nó Apache, ba cheart duit é seo a chumasú.", + "Examples": "Samplaí", + "Home Assistant URL": "URL an Home Assistant", + "Long-Lived Access Token": "Tócan Rochtana Fadsaoil", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Is féidir Comhartha Rochtana Fadsaolach a chruthú trí chliceáil ar d’ainm próifíle (bun ar chlé) agus scrollú go dtí an bun agus ansin cliceáil ar Chruthaigh Comhartha.", + "Notification Service": "Seirbhís Fógra", + "default: notify all devices": "réamhshocraithe: cuir gach feiste ar an eolas", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Is féidir liosta de Sheirbhísí Fógra a fháil i Home Assistant faoi \"Uirlisí Forbróra > Seirbhísí\" cuardach \"fógra\" chun ainm do ghléis/teileafóin a aimsiú.", + "Automations can optionally be triggered in Home Assistant:": "Is féidir uathoibriú a chur i ngníomh go roghnach in Home Assistant:", + "Trigger type:": "Cineál Tionscadal Truicear:", + "Event type:": "Cineál imeachta:", + "Event data:": "Sonraí imeachta:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Ansin roghnaigh gníomh, mar shampla athraigh an radharc go dtí an áit a bhfuil solas RGB dearg.", + "Frontend Version": "Leagan Frontend", + "Frontend Version do not match backend version!": "Ní hionann Leagan Frontend leis an leagan inneall!", + "backupOutdatedWarning": "Gan úsáid: Mar gur cuireadh go leor gnéithe nua leis agus mar go bhfuil an gné chúltaca seo neamhcheadaithe go leor, ní féidir leis cúltaca iomlán a ghiniúint ná a thabhairt ar ais.", + "backupRecommend": "Le do thoil cúltaca an toirt nó an fillteán sonraí (./data/) go díreach ina ionad.", + "Optional": "Roghnach", + "or": "nó", + "sameAsServerTimezone": "Mar an gcéanna le Crios Ama an Fhreastalaí", + "endDateTime": "Dáta/Am Deiridh", + "cronExpression": "Slonn Cron", + "cronSchedule": "Sceideal: ", + "invalidCronExpression": "Slonn Cron Neamhbhailí: {0}", + "recurringInterval": "Eatramh", + "Recurring": "Athfhillteach", + "warningTimezone": "Tá sé ag baint úsáide as crios ama an fhreastalaí", + "weekdayShortMon": "Luan", + "weekdayShortTue": "Már", + "weekdayShortWed": "Céa", + "weekdayShortThu": "Déa", + "weekdayShortFri": "Aoí", + "weekdayShortSat": "Sát", + "weekdayShortSun": "Dom", + "No Maintenance": "Gan Cothabháil", + "lastDay1": "Lá deiridh den Mhí", + "dayOfWeek": "Lá den Seachtaine", + "lastDay2": "2ú Lá Deireanach den Mhí", + "lastDay3": "3ú Lá Deireanach den Mhí", + "lastDay": "Lá Deiridh", + "pauseMaintenanceMsg": "An bhfuil tú cinnte gur mhaith leat sos?", + "maintenanceStatus-under-maintenance": "Faoi Chothabháil", + "maintenanceStatus-inactive": "Neamhghníomhach", + "maintenanceStatus-ended": "Dar Críoch", + "maintenanceStatus-unknown": "Anaithnid", + "Display Timezone": "Crios Ama Taispeána", + "Server Timezone": "Crios Ama Freastalaí", + "statusPageMaintenanceEndDate": "Deireadh", + "IconUrl": "URL deilbhín", + "Enable": "Cumasaigh", + "Disable": "Díchumasaigh", + "enableNSCD": "Cumasaigh NSCD (Deamhan Taisce Ainm na Seirbhíse) chun gach iarratas DNS a thaisceadh", + "chromeExecutable": "Chrome/Chromium Inrite", + "chromeExecutableAutoDetect": "Braith Uathoibríoch", + "dnsCacheDescription": "B'fhéidir nach bhfuil sé ag obair i roinnt timpeallachtaí IPv6, é a dhíchumasú má thagann tú ar aon fhadhbanna.", + "Single Maintenance Window": "Fuinneog Chothabhála Aonair", + "Maintenance Time Window of a Day": "Fuinneog Am Cothabhála Lá", + "Effective Date Range": "Raon Dáta Éifeachtach (Roghnach)", + "Schedule Maintenance": "Cothabháil Sceideal", + "Edit Maintenance": "Cuir Cothabháil in Eagar", + "DateTime Range": "Raon DátaTime", + "install": "Suiteáil", + "installing": "Ag Suiteáil", + "uninstall": "Dhíshuiteáil", + "uninstalling": "Ag Dhíshuiteáil", + "confirmUninstallPlugin": "An bhfuil tú cinnte gur mhaith leat an breiseán seo a dhíshuiteáil?", + "plugin": "Breiseán | Breiseáni", + "notificationRegional": "Réigiúnach", + "Clone": "Clón", + "cloneOf": "Clón de {0}", + "secureOptionNone": "Ceann ar bith / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Déan neamhaird de Earráid TLS", + "From Email": "Ó Ríomhphost", + "emailCustomSubject": "Ábhar an Chustaim", + "leave blank for default subject": "fág bán don ábhar réamhshocraithe", + "emailCustomBody": "Comhlacht Saincheaptha", + "leave blank for default body": "fág bán don chomhlacht réamhshocraithe", + "emailTemplateServiceName": "Ainm Seirbhíse", + "emailTemplateHostnameOrURL": "Óstainm nó URL", + "emailTemplateStatus": "Stádas", + "emailTemplateMsg": "teachtaireacht an fhógra", + "emailTemplateLimitedToUpDownNotification": "ar fáil ach amháin le haghaidh buillí croí SUAS/SÍOS, ar shlí eile ar neamhní", + "emailTemplateMonitorJSON": "réad a mhíníonn an monatóir", + "emailTemplateHeartbeatJSON": "réad a mhíníonn bhuille an chroí", + "To Email": "Chun Ríomhphost", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord Crúca Gréasáin URL", + "Bot Display Name": "Ainm Taispeána Bot", + "Prefix Custom Message": "Réimír Teachtaireacht Chustaim", + "Hello @everyone is...": "Dia duit {'@'}tá gach duine…", + "wayToGetTeamsURL": "Is féidir leat foghlaim conas URL crúca gréasáin {0} a chruthú.", + "wayToGetZohoCliqURL": "Is féidir leat foghlaim conas URL crúca gréasáin {0} a chruthú.", + "wayToCheckSignalURL": "Is féidir leat an URL seo a sheiceáil chun féachaint ar conas ceann a shocrú:", + "Number": "Uimhir", + "Recipients": "Faighteoirí", + "Access Token": "Tócan Rochtana", + "Channel access token": "Tócan rochtana cainéal", + "Line Developers Console": "Consól Forbróirí Líne", + "Basic Settings": "Socruithe Bunúsacha", + "User ID": "ID Úsáideora", + "Messaging API": "API Teachtaireachtaí", + "Icon URL": "URL Deilbhín", + "aboutIconURL": "Is féidir leat nasc chuig pictiúr a sholáthar i \"URL Deilbhín\" chun an pictiúr próifíle réamhshocraithe a shárú. Ní úsáidfear é má tá Icon Emoji socraithe.", + "confirmDeleteTagMsg": "An bhfuil tú cinnte gur mhaith leat an chlib seo a scriosadh? Ní scriosfar monatóirí a bhaineann leis an gclib seo.", + "enableGRPCTls": "Ceadaigh iarratas gRPC a sheoladh le ceangal TLS", + "acceptedStatusCodesDescription": "Roghnaigh cóid stádais a mheastar mar fhreagra rathúil.", + "deleteMonitorMsg": "An bhfuil tú cinnte gur mhaith leat an monatóir seo a scriosadh?", + "deleteMaintenanceMsg": "An bhfuil tú cinnte gur mhaith leat an cothabháil seo a scriosadh?", + "dnsPortDescription": "Port freastalaí DNS. Réamhshocrú go 53. Is féidir leat an port a athrú am ar bith.", + "rrtypeDescription": "Roghnaigh an cineál RR is mian leat chun monatóireacht a dhéanamh", + "pauseMonitorMsg": "An bhfuil tú cinnte gur mhaith leat sos?", + "clearEventsMsg": "An bhfuil tú cinnte gur mian leat gach imeacht don mhonatóir seo a scriosadh? Baintear sonraí teachtaireachta gaolmhara leis seo go buan agus ní féidir é a chealú.", + "clearHeartbeatsMsg": "An bhfuil tú cinnte gur mian leat gach buille croí a scriosadh don mhonatóir seo? Baintear gach sonraí buille croí leis seo go buan agus ní féidir é a chealú.", + "confirmClearStatisticsMsg": "An bhfuil tú cinnte gur mian leat GACH staitisticí a scriosadh?", + "confirmImportMsg": "An bhfuil tú cinnte gur mhaith leat an cúltaca a iompórtáil? Cinntigh gur roghnaigh tú an rogha iompórtála ceart.", + "twoFAVerifyLabel": "Cuir isteach do tócan chun 2FA a fhíorú:", + "tokenValidSettingsMsg": "Tá tócan bailí! Is féidir leat na socruithe 2FA a shábháil anois.", + "confirmEnableTwoFAMsg": "An bhfuil tú cinnte gur mhaith leat 2FA a chumasú?", + "recurringIntervalMessage": "Rith uair amháin gach lá | Rith uair amháin gach {0} lá", + "affectedMonitorsDescription": "Roghnaigh monatóirí a bhfuil tionchar ag cothabháil reatha orthu", + "atLeastOneMonitor": "Roghnaigh monatóir difear amháin ar a laghad", + "passwordNotMatchMsg": "Ní hionann an pasfhocal athfhillteach.", + "notificationDescription": "Ní mór fógraí a shannadh do mhonatóir chun feidhmiú.", + "invertKeywordDescription": "Cuardaigh an eochairfhocal a bheith as láthair seachas i láthair.", + "backupDescription3": "Tá sonraí íogaire amhail comharthaí fógra san áireamh sa chomhad easpórtála; stóráil an t-easpórtáil go slán.", + "endpoint": "críochphointe", + "octopushLogin": "\"Logáil isteach\" ó HTTP API dintiúir sa phainéal rialú", + "promosmsLogin": "Ainm Logála Isteach API", + "promosmsPassword": "Pasfhocal API", + "pushoversounds pushover": "Brúite (réamhshocraithe)", + "pushoversounds bike": "Rothar", + "pushoversounds bugle": "buabhall", + "pushoversounds cashregister": "Clár Airgid", + "pushoversounds classical": "Clasaiceach", + "pushoversounds cosmic": "Cósmach", + "pushoversounds falling": "Titim", + "pushoversounds gamelan": "Ceol Traidisiúnta Indinéise", + "pushoversounds incoming": "Teachtaireacht Teacht", + "pushoversounds magic": "Draíocht", + "pushoversounds mechanical": "Meicniúil", + "pushoversounds pianobar": "Báir Pianó", + "pushoversounds siren": "Síorán", + "pushoversounds spacealarm": "Aláram Spáis", + "pushoversounds tugboat": "Bád Tuga", + "pushoversounds alien": "Aláram Eachtráin (fad)", + "pushoversounds climb": "Dreapadh (fad)", + "pushoversounds persistent": "Dianseasmhach (fad)", + "pushoversounds updown": "Suas Síos (fad)", + "pushoversounds vibrate": "Vibráil Amháin", + "pushoversounds none": "Aon Cheann (ciúin)", + "pushyAPIKey": "Eochair Rúnda API", + "pushyToken": "Tócan gléas", + "apprise": "Apprise (Tacaíocht le 50+ seirbhís fógra)", + "wayToGetKookBotToken": "Cruthaigh feidhmchlár agus faigh do tócan bot ag {0}", + "Guild ID": "ID Guild", + "User Key": "Eochair Úsáideora", + "Device": "Gléas", + "Message Title": "Teideal na Teachtaireachta", + "More info on:": "Tuilleadh eolais ar: {0}", + "pushoverDesc2": "Más mian leat fógraí a sheoladh chuig gléasanna éagsúla, líon amach Gléas réimse.", + "pushoverMessageTtl": "Teachtaireacht TTL (Soicindí)", + "SMS Type": "Cineál SMS", + "octopushTypePremium": "Préimh (Gasta - molta le haghaidh foláirimh)", + "octopushTypeLowCost": "Costas Íseal (Mall - uaireanta bac ag oibreoir)", + "checkPrice": "Seiceáil {0} praghsanna:", + "apiCredentials": "Dintiúir API", + "Check octopush prices": "Seiceáil praghsanna ochtapas {0}.", + "octopushPhoneNumber": "Uimhir theileafóin (formáid idirnáisiúnta, m.sh.: +33612345678)", + "octopushSMSSender": "Ainm Seoltóra SMS : 3-11 carachtar alfa-uimhriúil agus spás (a-zA-Z0-9)", + "LunaSea Device ID": "Aitheantas Gléas LunaSea", + "Apprise URL": "URL Apprise", + "Example:": "Sampla: {0}", + "Read more:": "Léigh tuilleadh: {0}", + "Strategy": "Straitéis", + "Free Mobile User Identifier": "Aitheantóir soghluaiste saor in aisce", + "Free Mobile API Key": "Eochair Soghluaiste API Saor in Aisce", + "Enable TLS": "Cumasaigh TLS", + "Proto Method": "Modh Prótacal", + "Proto Content": "Ábhar Prótacal", + "Economy": "Geilleagar", + "Lowcost": "Chostas Íseal", + "high": "ard", + "SendKey": "Seol Eochair", + "SMSManager API Docs": "Doiciméid API SMSManager", + "Gateway Type": "Cineál Geata", + "Base URL": "Bun URL", + "goAlertIntegrationKeyInfo": "Faigh eochair chomhtháthaithe cineálach API don tseirbhís san fhormáid seo \"aaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" luach paraiméadar comharthaíochta an URL cóipeáilte de ghnáth.", + "AccessKeyId": "Aitheantas Eochair Rochtana", + "SecretAccessKey": "Rúin AccessKey", + "PhoneNumbers": "Uimhreacha Teileafóin", + "Huawei": "Huawei", + "TemplateCode": "Cód Teimpléad", + "SignName": "Ainm Comhartha", + "Bark API Version": "Leagan Bark API", + "Bark Endpoint": "Críochphointe Coirt", + "Bark Group": "Grúpa Bark", + "Bark Sound": "Fuaim Coirt", + "WebHookUrl": "URL Crúca Gréasáin", + "SecretKey": "Eochair Rúnda", + "For safety, must use secret key": "Ar mhaithe le sábháilteacht, ní mór eochair rúnda a úsáid", + "Device Token": "Tócan Gléas", + "Platform": "Ardán", + "High": "Ard", + "Retry": "Bain triail eile as", + "Topic": "Topaic", + "Setup Proxy": "Socrú Seachfhreastalaí", + "Proxy Protocol": "Prótacal Seachfhreastalaí", + "Proxy Server": "Seachfhreastalaí", + "promosmsTypeEco": "SMS ECO - chostas iseal ach mall agus ró-ualach go minic. Teoranta do fhaighteoirí Polainnis amháin.", + "promosmsTypeFull": "SMS IOMLÁN - Sraith préimhe de SMS, Is féidir leat d'Ainm Seoltóra a úsáid (Ní mór duit ainm a chlárú ar dtús). Iontaofa le haghaidh foláirimh.", + "promosmsPhoneNumber": "Uimhir theileafóin (d’fhaighteoir Polannach Is féidir leat cóid limistéir a scipeáil)", + "promosmsSMSSender": "Ainm Seoltóra SMS : Ainm réamhchláraithe nó ceann de na réamhshocruithe: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "promosmsAllowLongSMS": "Ceadaigh SMS fada", + "Feishu WebHookUrl": "URL Crúca Gréasáin Feishu", + "matrixHomeserverURL": "URL an fhreastalaí baile (le http(s):// agus go roghnach port)", + "Internal Room Id": "Aitheantas Seomra Inmheánach", + "Channel Name": "Ainm Cainéal", + "Notify Channel": "Cainéal a chur ar an eolas", + "Uptime Kuma URL": "URL Uptime Kuma", + "setup a new monitor group": "grúpa monatóireachta nua a bhunú", + "openModalTo": "módúil a oscailt go {0}", + "Add a domain": "Cuir fearann leis", + "Remove domain": "Bain an fearann '{0}'", + "Icon Emoji": "Deilbhín Emoji", + "signalImportant": "TÁBHACHTACH: Ní féidir leat grúpaí agus uimhreacha i faighteoirí a mheascadh!", + "aboutWebhooks": "Tuilleadh eolais faoi Crúca Gréasáin ar: {0}", + "aboutChannelName": "Cuir isteach ainm an chainéil ar an réimse Ainm Cainéal {0} más mian leat an cainéal Crúca Gréasáin a sheachbhóthar. Mar shampla: #anotherchannel", + "smtpDkimSettings": "Socruithe DKIM", + "smtpDkimDesc": "Déan tagairt don Nodemailer DKIM {0} le haghaidh úsáide.", + "documentation": "doiciméadú", + "smtpDkimKeySelector": "Roghnóir Eochracha", + "smtpDkimPrivateKey": "Eochair Phríobháideach", + "smtpDkimHashAlgo": "Algartam Hash (Roghnach)", + "smtpDkimskipFields": "Eochracha Ceanntásca gan síniú (Roghnach)", + "startDateTime": "Dáta/Am Tosaithe", + "strategyManual": "Gníomhach/Neamhghníomhach de Láimh", + "dayOfMonth": "Lá den Mhí", + "lastDay4": "4ú Lá Deireanach den Mhí", + "maintenanceStatus-scheduled": "Sceidealta", + "Enable DNS Cache": "(Ligthe i léig) Cumasaigh Taisce DNS le haghaidh monatóirí HTTP(s)", + "chromeExecutableDescription": "I gcás úsáideoirí Docker, mura bhfuil Chromium suiteáilte fós, féadfaidh sé cúpla nóiméad a ghlacadh chun toradh na tástála a shuiteáil agus a thaispeáint. Tógann sé 1GB de spás diosca.", + "Date and Time": "Dáta agus Am", + "loadingError": "Ní féidir na sonraí a fháil, bain triail eile as ar ball.", + "Clone Monitor": "Monatóir Clón", + "smtp": "Ríomhphost (SMTP)", + "emailCustomisableContent": "Ábhar inoiriúnaithe", + "smtpLiquidIntroduction": "Tá an dá réimse seo a leanas inphléite trí Theanga Teimpléadúcháin Liquid. Féach ar an {0} le haghaidh treoracha úsáide. Seo iad na hathróga atá ar fáil:", + "wayToGetDiscordURL": "Is féidir leat é seo a fháil ach dul go Socruithe Freastalaí -> Comhtháthaithe -> Féach ar Crúca Gréasáin -> Nua Crúca Gréasán", + "needSignalAPI": "Ní mór duit cliant comhartha a bheith agat le REST API.", + "lineDevConsoleTo": "Consól Forbróirí Líne - {0}", + "wayToGetLineChannelToken": "Déan rochtain ar {0} ar dtús, cruthaigh soláthraí agus cainéal (API Teachtaireachtaí), ansin is féidir leat an comhartha rochtana cainéal agus an t-aitheantas úsáideora a fháil ó na míreanna roghchláir thuasluaite.", + "aboutMattermostChannelName": "Is féidir leat an cainéal réamhshocraithe a phostálann an Crúca Gréasáin dó a shárú trí ainm an chainéil a chur isteach sa réimse \"Ainm na Cainéal\". Ní mór é seo a chumasú i socruithe Mattermost Crúca Gréasáin. Mar shampla: #other-channel", + "dataRetentionTimeError": "Ní mór don tréimhse choinneála a bheith 0 nó níos mó", + "infiniteRetention": "Socraigh go 0 le haghaidh coinneála gan teorainn.", + "grpcMethodDescription": "Tiontaítear ainm an mhodha go formáid camelCase mar sayHello, seiceáil, etc.", + "deleteNotificationMsg": "An bhfuil tú cinnte gur mhaith leat an fógra seo a scriosadh do gach monatóir?", + "resolverserverDescription": "Is é Cloudflare an freastalaí réamhshocraithe. Is féidir leat liosta de sheoltaí IP nó d'ainmneacha óstacha atá scartha le camóga a shonrú.", + "enableDefaultNotificationDescription": "Cumasófar an fógra seo de réir réamhshocraithe le haghaidh monatóirí nua. Is féidir leat an fógra a dhíchumasú fós ar leithligh do gach monatóir.", + "importHandleDescription": "Roghnaigh 'Scipeáil atá ann cheana féin' más mian leat scipeáil a dhéanamh ar gach monatóir nó fógra leis an ainm céanna. Scriosfaidh ‘Forscríobh’ gach monatóir agus fógra atá ann cheana.", + "confirmDisableTwoFAMsg": "An bhfuil tú cinnte gur mhaith leat 2FA a dhíchumasú?", + "affectedStatusPages": "Taispeáin an teachtaireacht cothabhála seo ar leathanaigh stádais roghnaithe", + "keywordDescription": "Cuardaigh eochairfhocal i ngnáthfhreagra HTML nó JSON. Tá an cuardach cás-íogair.", + "backupDescription": "Is féidir leat gach monatóir agus fógra a chúltaca isteach i gcomhad JSON.", + "backupDescription2": "Nóta: níl sonraí staire agus imeachta san áireamh.", + "octopushAPIKey": "\"Eochair API\" ó dhintiúir API HTTP sa phainéal rialaithe", + "pushoversounds intermission": "Idirsheasúr", + "pushoversounds echo": "Pushover Macalla (fad)", + "GoogleChat": "Google Chat (Google Workspace amháin)", + "wayToGetKookGuildID": "Cuir 'Mód Forbróra' ar siúl i suíomh Kook, agus cliceáil ar dheis ar an guild chun a ID a fháil", + "Notification Sound": "Fuaim Fógra", + "pushoverDesc1": "Tá teorainn ama réamhshocraithe 30 soicind ag tosaíocht éigeandála (2) idir na hathiarratais agus rachaidh sé in éag tar éis 1 uair an chloig.", + "octopushLegacyHint": "An úsáideann tú an leagan oidhreachta de Octopush (2011-2020) nó an leagan nua?", + "Status:": "Stádas: {0}", + "Proto Service Name": "Ainm Seirbhíse Proto", + "You can divide numbers with": "Is féidir leat uimhreacha a roinnt le", + "goAlertInfo": "Is feidhmchlár foinse oscailte é GoAlert le haghaidh sceidealaithe ar ghlao-dhualgas, arduithe uathoibrithe agus fógraí (amhail SMS nó glaonna gutha). Téigh i dteagmháil go huathoibríoch leis an duine ceart, ar an mbealach ceart, agus ag an am ceart! {0}", + "Sms template must contain parameters: ": "Ní mór paraiméadair a bheith sa teimpléad sms: ", + "WeCom Bot Key": "Eochair do WeCom Bot", + "Proxy server has authentication": "Tá fíordheimhniú ag an seachfhreastalaí", + "promosmsTypeFlash": "SMS FLASH - Taispeánfar an teachtaireacht go huathoibríoch ar an ngléas faighteora. Teoranta do fhaighteoirí Polainnis amháin.", + "promosmsTypeSpeed": "SMS LUAS - An tosaíocht is airde sa chóras. An-tapa agus iontaofa ach costasach (thart ar dhá uair de phraghas IOMLÁN SMS).", + "matrixDesc1": "Is féidir leat an t-aitheantas seomra inmheánach a fháil ach breathnú ar an rannán ardleibhéil de shocruithe an tseomra i do chliant Maitrís. Ba cheart go mbeadh cuma air !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Moltar go mór duit úsáideoir nua a chruthú agus ná húsáid do chomhartha rochtana úsáideora Matrix féin mar go gceadóidh sé rochtain iomlán ar do chuntas agus ar na seomraí go léir a ndeachaigh tú isteach ann. Ina áit sin, cruthaigh úsáideoir nua agus gan ach tabhair cuireadh dó go dtí an seomra inar mian leat an fógra a fháil. Is féidir leat an comhartha rochtana a fháil trí {0} a rith", + "aboutNotifyChannel": "Spreagfaidh an cainéal fógra deisce nó soghluaiste do gach ball den chineál, cibé infhaighteacht atá socraithe acu go gníomhach nó as oifig.", + "aboutKumaURL": "Má fhágann tú an réimse URL Uptime Kuma bán, beidh sé réamhshocraithe chuig an leathanach Project GitHub.", + "smtpDkimDomain": "Ainm Fearainn", + "smtpDkimheaderFieldNames": "Eochracha Ceanntásc le síniú (Roghnach)", + "wayToGetPagerDutyKey": "Is féidir leat é seo a fháil ach dul chuig Seirbhís -> Eolaire Seirbhíse -> (Roghnaigh seirbhís) -> Comhtháthaithe -> Cuir comhtháthú leis. Anseo is féidir leat cuardach a dhéanamh ar \"Imeachtaí API V2\". Tuilleadh eolais {0}", + "Integration Key": "Eochair Chomhtháthaithe", + "Integration URL": "URL Comhtháthaithe", + "Auto resolve or acknowledged": "Réiteach uathoibríoch nó admháil", + "do nothing": "ná déan faic", + "auto acknowledged": "auto admháil", + "auto resolve": "réiteach uathoibríoch", + "alertaApiEndpoint": "Críochphointe API", + "alertaEnvironment": "Timpeallacht", + "alertaApiKey": "Eochair API", + "alertaAlertState": "Stát Foláirimh", + "alertaRecoverState": "Aisghabháil Stáit", + "serwersmsAPIUser": "Ainm Úsáideora API (lena n-áirítear réimír webapi_)", + "serwersmsAPIPassword": "Pasfhocal API", + "serwersmsPhoneNumber": "Uimhir teileafón", + "smseagleTo": "Uimhir(eacha) gutháin", + "smseagleGroup": "Ainm(neacha) an ghrúpa ghutháin", + "smseagleContact": "Ainm(neacha) teagmhála an leabhair ghutháin", + "smseagleRecipientType": "Cineál faighteora", + "smseagleRecipient": "Faighteoir(í) (ní mór an iliomad a bheith scartha le camóg)", + "smseagleToken": "Comhartha rochtana API", + "smseagleUrl": "URL do ghléis SMSEagle", + "smseaglePriority": "Tosaíocht teachtaireachta (0-9, an tosaíocht is airde = 9)", + "Recipient Number": "Uimhir Faighteoir", + "From Name/Number": "Ó Ainm/Uimhir", + "Octopush API Version": "Leagan API Octopush", + "Legacy Octopush-DM": "Oidhreacht Octopush-DM", + "ntfy Topic": "ntfy Topaic", + "Server URL should not contain the nfty topic": "Níor cheart go mbeadh an topaic nfty i URL an fhreastalaí", + "onebotHttpAddress": "Seoladh HTTP OneBot", + "onebotMessageType": "Cineál Teachtaireachta OneBot", + "onebotPrivateMessage": "Príobháideach", + "onebotSafetyTips": "Ar mhaithe le sábháilteacht, ní mór comhartha rochtana a shocrú", + "PushDeer Server": "Freastalaí PushDeer", + "PushDeer Key": "Eochair PushDeer", + "wayToGetClickSendSMSToken": "Is féidir leat Ainm Úsáideora API agus Eochair API a fháil ó {here}.", + "Custom Monitor Type": "Cineál Monatóir an Chustaim", + "Google Analytics ID": "Aitheantas Google Analytics", + "Edit Tag": "Cuir Clib in Eagar", + "Server Address": "Seoladh an fhreastalaí", + "Learn More": "Foghlaim níos mó", + "API Keys": "Eochracha API", + "Expiry": "Éaga", + "Expiry date": "Dáta éaga", + "Don't expire": "Ná éag", + "Continue": "Leanúint ar aghaidh", + "Add Another": "Cuir Eile leis", + "Key Added": "Cuireadh eochair leis", + "Add API Key": "Cuir Eochair API leis", + "No API Keys": "Gan Eochracha API", + "apiKey-active": "Gníomhach", + "apiKey-expired": "Imithe in Éag", + "apiKey-inactive": "Neamhghníomhach", + "Expires": "In éag", + "disableAPIKeyMsg": "An bhfuil tú cinnte gur mhaith leat an eochair API seo a dhíchumasú?", + "Generate": "Gin", + "pagertreeIntegrationUrl": "URL Comhtháthaithe", + "pagertreeUrgency": "Práinne", + "pagertreeSilent": "Ciúin", + "pagertreeLow": "Íseal", + "pagertreeMedium": "Mheán", + "pagertreeHigh": "Ard", + "pagertreeCritical": "Criticiúil", + "pagertreeResolve": "Uathréiteach", + "pagertreeDoNothing": "Ná Déan Faic", + "lunaseaDeviceID": "Aitheantas an Ghléis", + "lunaseaUserID": "ID Úsáideora", + "ntfyAuthenticationMethod": "Modh Fíordheimhnithe", + "ntfyPriorityHelptextAllExceptDown": "Seoltar gach imeacht leis an tosaíocht seo, seachas {0}-imeachtaí, a bhfuil tosaíocht de {1} acu", + "ntfyUsernameAndPassword": "Ainm Úsáideora agus Pasfhocal", + "twilioAccountSID": "SID cuntais", + "twilioApiKey": "Eochair Api (roghnach)", + "twilioAuthToken": "Auth Tócan / Eochair API Rúnda", + "twilioFromNumber": "Ó Uimhir", + "Show Clickable Link": "Taispeáin Nasc Inchliceáilte", + "Open Badge Generator": "Oscail Gineadóir Suaitheantais", + "Badge Generator": "Gineadóir Suaitheantais {0}", + "Badge Type": "Cineál Suaitheantais", + "Badge Duration (in hours)": "Fad Suaitheantais (in uaireanta)", + "Badge Label": "Lipéad Suaitheantais", + "Badge Prefix": "Réimír Luach Suaitheanta", + "Badge Suffix": "Iarmhír Luach Suaitheanta", + "Badge Label Color": "Dath Lipéad Suaitheantais", + "Badge Color": "Dath Suaitheantas", + "Badge Label Prefix": "Réimír Lipéad Suaitheantais", + "Badge Preview": "Réamhamharc Suaitheantais", + "Badge Label Suffix": "Iarmhír Lipéad Suaitheantais", + "Badge Down Color": "Suaitheantas Síos Dath", + "Badge Pending Color": "Suaitheantas ar Feitheamh Dath", + "Badge Maintenance Color": "Dath Cothabhála Suaitheantais", + "Badge Warn Color": "Dath Rabhadh Suaitheantas", + "Badge Warn Days": "Laethanta Rabhaidh Suaitheanta", + "Badge Style": "Stíl Suaitheantas", + "Badge value (For Testing only.)": "Luach suaitheantais (Le haghaidh Tástála amháin.)", + "Badge URL": "URL Suaitheantais", + "Group": "Grúpa", + "Monitor Group": "Grúpa Monatóireachta", + "toastErrorTimeout": "Teorainn Ama le haghaidh Fógraí Earráide", + "toastSuccessTimeout": "Teorainn Ama le haghaidh Fógraí Ratha", + "Kafka Brokers": "Bróicéirí Kafka", + "Enter the list of brokers": "Cuir isteach an liosta bróicéirí", + "Press Enter to add broker": "Brúigh Enter chun bróicéir a chur leis", + "Kafka Topic Name": "Ainm an Ábhair Kafka", + "Kafka Producer Message": "Teachtaireacht Léiritheoir Kafka", + "Enable Kafka SSL": "Cumasaigh Kafka SSL", + "Kafka SASL Options": "Roghanna Kafka SASL", + "Mechanism": "Meicníocht", + "Pick a SASL Mechanism...": "Roghnaigh Meicníocht SASL…", + "Authorization Identity": "Aitheantas Údaraithe", + "AccessKey Id": "Aitheantas AccessKey", + "Secret AccessKey": "Eochair Rochtana Rúnda", + "Session Token": "Comhartha Seisiúin", + "Close": "Dún", + "Request Body": "Comhlacht Iarratas", + "FlashDuty Severity": "Déine", + "nostrRelays": "Athsheachadáin Nostr", + "nostrRelaysHelp": "URL sealaíochta amháin in aghaidh an líne", + "nostrSender": "Eochair Phríobháideach an Seoltóra (nsec)", + "nostrRecipients": "Eochracha Poiblí Faighteoirí (npub)", + "nostrRecipientsHelp": "formáid npub, ceann in aghaidh an líne", + "showCertificateExpiry": "Taispeáin Éaga Teastais", + "noOrBadCertificate": "Níl/Drochteastas", + "gamedigGuessPort": "Gamedig: Port a Fháil Amach", + "Saved.": "Shábháil.", + "authUserInactiveOrDeleted": "Tá an t-úsáideoir neamhghníomhach nó scriostar é.", + "authInvalidToken": "Tócan Neamhbhailí.", + "authIncorrectCreds": "Ainm úsáideora nó pasfhocal mícheart.", + "2faAlreadyEnabled": "Tá 2FA cumasaithe cheana féin.", + "2faEnabled": "2FA Cumasaithe.", + "2faDisabled": "2FA faoi Mhíchumas.", + "successAdded": "Curtha leis go rathúil.", + "successResumed": "Tá an próiseas ar ais ar bun go rathúil.", + "successPaused": "Curtha ar sos go rathúil.", + "successDeleted": "Scriosta go rathúil.", + "successEdited": "Tá an t-eagarthóireacht athruithe go rathúil.", + "successAuthChangePassword": "Tá an pasfhocal nuashonraithe go rathúil.", + "successDisabled": "Díchumasaithe go rathúil.", + "successEnabled": "Cumasaithe go rathúil.", + "tagNotFound": "Clib gan aimsiú.", + "foundChromiumVersion": "Aimsíodh Cróimiam/Chrome. Leagan: {0}", + "Remote Browsers": "Brabhsálaithe cianda", + "Remote Browser": "Brabhsálaí cianda", + "Add a Remote Browser": "Cuir Brabhsálaí Cianda leis", + "Remote Browser not found!": "Brabhsálaí cianda gan aimsiú!", + "remoteBrowsersDescription": "Is rogha eile iad Brabhsálaithe Cian chun Cróimiam a rith go háitiúil. Socraigh le seirbhís cosúil le browserless.io nó ceangail le do cheann féin", + "self-hosted container": "coimeádán féin-óstach", + "remoteBrowserToggle": "De réir réamhshocraithe ritheann Cróimiam taobh istigh den choimeádán Uptime Kuma. Is féidir leat cianbhrabhsálaí a úsáid tríd an lasc seo a scoránú.", + "useRemoteBrowser": "Úsáid Brabhsálaí Cianda", + "deleteRemoteBrowserMessage": "An bhfuil tú cinnte gur mhaith leat an Cianbhrabhsálaí seo a scriosadh do gach monatóir?", + "GrafanaOncallUrl": "URL Grafana Oncall", + "Browser Screenshot": "Scáileán Brabhsálaí", + "What is a Remote Browser?": "Cad is Brabhsálaí Cianda ann?", + "serwersmsSenderName": "Ainm Seoltóra SMS (cláraithe trí thairseach custaiméirí)", + "smseagleEncoding": "Seol mar Unicode (réamhshocrú=GSM-7)", + "Leave blank to use a shared sender number.": "Fág bán chun uimhir seoltóra roinnte a úsáid.", + "onebotGroupMessage": "Grúpa", + "onebotUserOrGroupId": "Aitheantas Grúpa/Úsáideora", + "pushDeerServerDescription": "Fág bán chun an freastalaí oifigiúil a úsáid", + "Body Encoding": "Ionchódú Coirp", + "apiKeyAddedMsg": "Cuireadh d'eochair API leis. Déan nóta de le do thoil mar ní thaispeánfar arís é.", + "deleteAPIKeyMsg": "An bhfuil tú cinnte gur mhaith leat an eochair API seo a scriosadh?", + "wayToGetPagerTreeIntegrationURL": "Tar éis comhtháthú Uptime Kuma a chruthú i PagerTree, cóipeáil an Endpoint. Féach ar na sonraí iomlána {0}", + "lunaseaTarget": "Sprioc", + "ntfyPriorityHelptextAllEvents": "Seoltar gach imeacht leis an tosaíocht uasta", + "twilioToNumber": "A Uimhir", + "Monitor Setting": "Socrú Monatóir {0}", + "Show Clickable Link Description": "Má dhéantar é a sheiceáil is féidir le gach duine a bhfuil rochtain acu ar an leathanach stádais seo rochtain a fháil ar an monatóireacht a dhéanamh ar URL.", + "Badge Up Color": "Suaitheantas Suas Dath", + "Badge Down Days": "Laethanta Dúin Suaitheanta", + "monitorToastMessagesLabel": "Monatóireacht a dhéanamh ar fhógraí Tósta", + "monitorToastMessagesDescription": "Imíonn fógraí tósta le haghaidh monatóirí tar éis am tugtha i soicindí. Díchumasaítear am istigh le socrú go -1. Díchumasaigh Socrú go 0 fógraí tósta.", + "Enable Kafka Producer Auto Topic Creation": "Cumasaigh Cruthú Uath-Ábhair Táirgeora Kafka", + "noGroupMonitorMsg": "Níl sé ar fáil. Cruthaigh Monatóir Grúpa ar dtús.", + "wayToGetFlashDutyKey": "Chun Uptime Kuma a chomhtháthú le Flashduty: Téigh go Cainéil > Roghnaigh cainéal > Comhtháthúcháin > Cuir comhtháthú nua leis, roghnaigh Uptime Kuma, agus cóipeáil an URL Push.", + "gamedigGuessPortDescription": "Féadfaidh an calafort a úsáideann Prótacal Iarratas Freastalaí Comhla a bheith difriúil ó phort an chliaint. Bain triail as seo mura bhfuil an monatóir in ann ceangal le do fhreastalaí.", + "successBackupRestored": "Tá an cúltaca athchóirithe go rathúil.", + "Host URL": "URL Óstach", + "senderSevenIO": "Sraithnú uimhir nó ainm a sheoladh", + "receiverInfoSevenIO": "Má bhíonn an uimhir atá á fháil ann nach bhfuil i nGearmáin, caithfidh tú an cód tíre a chur leis romhainn na huimhreacha (m.sh. le haghaidh cód tíre 1 ón RA, úsáid 117612121212 in ionad 017612121212)", + "apiKeySevenIO": "Eochair API SevenIO", + "locally configured mail transfer agent": "feidhmchlár aistrúcháin phoist cumraithe go háitiúil", + "wayToGetDiscordThreadId": "Tá sé cosúil le haitheantas poist snáithe / fóraim a fháil agus aitheantas cainéil a fháil. Léigh tuilleadh faoi conas aitheantais a fháil {0}", + "wayToGetHeiiOnCallDetails": "Mínítear sa {documentation} conas an Trigger ID agus Eochracha API a fháil", + "documentationOf": "{0} Doiciméadúchán", + "gtxMessagingApiKeyHint": "Is féidir leat d’eochair API a aimsiú ag: Mo Chuntais Ródúcháin > Taispeáin Eolas Cuntais > Dintiúir API > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Ón Uimhir Theileafóin / Seoladh Bunaidh Conair Tarchuir (TPOA)", + "Command": "Ordú", + "mongodbCommandDescription": "Rith ordú MongoDB i gcoinne an bhunachair sonraí. Le haghaidh faisnéise faoi na horduithe atá ar fáil féach ar an {documentation}", + "whapiRecipient": "Uimhir Theileafóin / Aitheantas Teagmhála / ID Grúpa", + "API URL": "URL API", + "callMeBotGet": "Anseo is féidir leat críochphointe a ghiniúint le haghaidh {0}, {1} agus {2}. Coinnigh i gcuimhne go mb'fhéidir go mbeadh an ráta teoranta agat. Is cosúil gurb iad na teorainneacha ráta: {3}", + "To Phone Number": "A chuig Uimhir Theileafóin", + "Mentioning": "Ag lua", + "Don't mention people": "Ná luaigh daoine", + "Mention group": "Luaigh {group}", + "Allow Long SMS": "Ceadaigh SMS Fada", + "Bitrix24 Webhook URL": "URL Webbhook Bitrix24", + "wayToGetBitrix24Webhook": "Is féidir leat cuaille gréasáin a chruthú trí na céimeanna ag {0} a leanúint", + "bitrix24SupportUserID": "Cuir isteach d’aitheantas úsáideora in Bitrix24. Is féidir leat an ID a fháil amach ón nasc ach dul chuig próifíl an úsáideora.", + "Originator": "Tionscnóir", + "wayToWriteWhapiRecipient": "An uimhir theileafóin leis an réimír idirnáisiúnta, ach gan an comhartha móide ag an tús ({0}), an ID Teagmhála ({1}) nó an Grúpa ID ({2}).", + "wayToGetWhapiUrlAndToken": "Is féidir leat an URL API agus an comhartha a fháil ach dul isteach sa chainéal atá uait ó {0}", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Cuir isteach ainm óstach an fhreastalaí ar mhaith leat ceangal leis, nó {localhost} más mian leat {local_mta} a úsáid", + "Refresh Interval": "Eatramh Athnuaigh", + "Refresh Interval Description": "Déanfaidh an leathanach stádais athnuachan iomlán ar an suíomh gach {0} soicind", + "whatHappensAtForumPost": "Cruthaigh post fóraim nua. NÍ phostann sé seo teachtaireachtaí sa phostáil reatha. Chun postáil sa phostáil reatha úsáid \"{option}\"", + "cellsyntOriginatortypeAlphanumeric": "Teaghrán alfa-uimhriúil (uasmhéid 11 carachtar alfa-uimhriúil). Ní féidir leis na faighteoirí freagra a thabhairt ar an teachtaireacht.", + "gtxMessagingFromHint": "Ar fhóin phóca, feiceann d’fhaighteoirí an TPOA ar taispeáint mar sheoltóir na teachtaireachta. Tá suas le 11 gcarachtar alfa-uimhriúla ceadaithe, gearrchód, an longchód áitiúil nó uimhreacha idirnáisiúnta ({e164}, {e212} nó {e214})", + "gtxMessagingToHint": "Formáid idirnáisiúnta, le \"+\" chun tosaigh ({e164}, {e212} nó {e214})", + "Originator type": "Cineál tionscnóra", + "Select message type": "Roghnaigh cineál teachtaireachta", + "wayToGetSevenIOApiKey": "Tabhair cuairt ar an painéal faoi app.seven.io > forbróir > eochair api > an cnaipe cuir glas", + "max 11 alphanumeric characters": "11 carachtar alfa-uimhriúil ar a mhéad", + "Your User ID": "D'aitheantas úsáideora", + "cellsyntOriginatortypeNumeric": "Luach uimhriúil (15 dhigit ar a mhéad) le huimhir theileafóin ar an bhformáid idirnáisiúnta gan 00 a threorú (mar shampla ba cheart uimhir RA 07920 110 000 a shocrú mar 447920110000). Is féidir le faighteoirí freagra a thabhairt ar an teachtaireacht.", + "cellsyntOriginator": "Le feiceáil ar ghuthán póca an fhaighteora mar thionscnóir na teachtaireachta. Braitheann luachanna agus feidhm cheadaithe ar chineál tionscnóra paraiméadar.", + "cellsyntDestination": "Uimhir theileafóin an fhaighteora ag baint úsáide as formáid idirnáisiúnta le 00 chun tosaigh agus cód tíre ina dhiaidh, e.g. 00447920110000 don uimhir RA 07920 110 000 (uasmhéid 17 ndigit san iomlán). Uasmhéid 25000 faighteoir scartha le camóg in aghaidh an iarratais HTTP.", + "Destination": "Ceann Scríbe", + "Channel access token (Long-lived)": "Comhartha rochtana cainéal (fadsaolach)", + "ignoreTLSErrorGeneral": "Déan neamhaird de earráid TLS/SSL le haghaidh ceangail", + "Send to channel": "Seol chuig cainéal", + "Create new forum post": "Cruthaigh post fóraim nua", + "postToExistingThread": "Cuir chuig an bpost snáithe / fóram atá ann cheana féin", + "forumPostName": "Ainm post an Fhóraim", + "threadForumPostID": "Snáithe / ID post an Fhóraim", + "e.g. {discordThreadID}": "e.g. {discordThreadID}", + "receiverSevenIO": "Uimhir glactha", + "Alphanumeric (recommended)": "Alfa-uimhriúil (molta)", + "Telephone number": "Uimhir theileafóin", + "cellsyntSplitLongMessages": "Roinn teachtaireachtaí fada suas le 6 chuid. 153 x 6 = 918 carachtar.", + "max 15 digits": "15 dhigit ar a mhéad", + "threemaRecipient": "Faighteoir", + "threemaRecipientType": "Cineál Faighteoir", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypeIdentityFormat": "8 gcarachtar", + "threemaRecipientTypePhone": "Uimhir teileafón", + "threemaRecipientTypePhoneFormat": "E.164, gan tosaigh +", + "threemaRecipientTypeEmail": "Seoladh ríomhphoist", + "threemaSenderIdentity": "Geata-ID", + "threemaApiAuthenticationSecret": "Rún Geata-ID", + "smspartnerPhoneNumber": "Uimhir(neacha) fóin", + "smspartnerSenderName": "Ainm Seoltóra SMS", + "smspartnerSenderNameInfo": "Caithfidh sé a bheith idir 3..=11 charachtar rialta", + "apiKeysDisabledMsg": "Tá eochracha API díchumasaithe toisc go bhfuil fíordheimhniú díchumasaithe.", + "smspartnerApiurl": "Is féidir leat d'eochair API a aimsiú ar do dheais ag {0}", + "smspartnerPhoneNumberHelptext": "Caithfidh an uimhir a bheith san fhormáid idirnáisiúnta {0}, {1}. Ní mór uimhreacha iolracha a dheighilt le {2}", + "wayToGetThreemaGateway": "Is féidir leat clárú le haghaidh Threema Gateway {0}.", + "threemaSenderIdentityFormat": "8 gcarachtar, a thosaíonn le * de ghnáth", + "threemaBasicModeInfo": "Nóta: Úsáideann an comhtháthú seo Threema Gateway i mód bunúsach (criptiúchán freastalaí-bhunaithe). Is féidir tuilleadh sonraí a fháil {0}.", + "jsonQueryDescription": "Parsáil agus bain sonraí ar leith ó fhreagra JSON an fhreastalaí ag baint úsáide as ceist JSON nó úsáid \"$\" don fhreagra amh, mura bhfuil tú ag súil le JSON. Cuirtear an toradh i gcomparáid ansin leis an luach ionchais, mar teaghráin. Féach ar {0} le haghaidh doiciméadú agus úsáid {1} chun triail a bhaint as fiosruithe.", + "snmpCommunityStringHelptext": "Feidhmíonn an teaghrán seo mar phasfhocal chun rochtain ar ghléasanna SNMP-chumasaithe a fhíordheimhniú agus a rialú. Meaitseáil sé le cumraíocht do ghléis SNMP.", + "snmpOIDHelptext": "Cuir isteach an OID don braiteoir nó don stádas ar mhaith leat monatóireacht a dhéanamh air. Úsáid uirlisí bainistíochta líonra ar nós brabhsálaithe MIB nó bogearraí SNMP mura bhfuil tú cinnte faoin OID.", + "wayToGetOnesenderUrlandToken": "Is féidir leat an URL agus Token a fháil ach dul chuig láithreán gréasáin Onesender. Tuilleadh eolais {0}", + "Lost connection to the socket server.": "Ceangal caillte leis an bhfreastalaí soicéad.", + "signl4Docs": "Is féidir leat tuilleadh faisnéise a fháil faoi conas SIGNL4 a chumrú agus conas an URL SIGNL4 a fháil sa {0}.", + "not starts with": "ní thosaíonn le", + "greater than or equal to": "níos mó ná nó cothrom le", + "now": "anois", + "time ago": "{0} ó shin", + "-year": "-bliain", + "Json Query Expression": "Léiriú Ceist Json", + "and": "agus", + "cacheBusterParam": "Cuir an {0} paraiméadar leis", + "cacheBusterParamDescription": "Paraiméadar ginte go randamach chun scipeáil a dhéanamh ar thaisce.", + "Community String": "Teaghrán Pobail", + "OID (Object Identifier)": "OID (Aitheantóir Oibiachta)", + "Condition": "Coinníoll", + "SNMP Version": "Leagan SNMP", + "Please enter a valid OID.": "Cuir isteach OID bailí.", + "Host Onesender": "Óstach Onesender", + "Token Onesender": "Licín Onesender", + "Recipient Type": "Cineál Faighteoir", + "Private Number": "Uimhir Phríobháideach", + "privateOnesenderDesc": "Cinntigh go bhfuil an uimhir theileafóin bailí. Chun teachtaireacht a sheoladh chuig uimhir ghutháin phríobháideach, sean: 628123456789", + "groupOnesenderDesc": "Cinntigh go bhfuil an GroupID bailí. Chun teachtaireacht a sheoladh chuig an nGrúpa, sean: 628123456789-342345", + "Group ID": "ID grúpa", + "Add Remote Browser": "Cuir Brabhsálaí Cianda leis", + "New Group": "Grúpa Nua", + "Group Name": "Ainm an Ghrúpa", + "OAuth2: Client Credentials": "OAuth2: Dintiúir Cliant", + "Authentication Method": "Modh Fíordheimhnithe", + "Authorization Header": "Ceanntásc Údaraithe", + "Form Data Body": "Comhlacht Sonraí Foirm", + "OAuth Token URL": "URL OAuth Token", + "Client ID": "ID Cliant", + "Client Secret": "Rúnda Cliant", + "OAuth Scope": "OAuth Scóip", + "Optional: Space separated list of scopes": "Roghnach: Liosta scóip spásscartha", + "Go back to home page.": "Téigh ar ais go dtí an leathanach baile.", + "No tags found.": "Níor aimsíodh clibeanna.", + "Cannot connect to the socket server.": "Ní féidir ceangal leis an bhfreastalaí soicéad.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "URL SIGNL4 Cabhrán Gréasáin", + "Conditions": "Coinníollacha", + "conditionAdd": "Cuir Coinníoll leis", + "conditionDelete": "Scrios Coinníoll", + "conditionAddGroup": "Cuir Grúpa leis", + "conditionDeleteGroup": "Scrios Grúpa", + "conditionValuePlaceholder": "Luach", + "equals": "comhionann", + "not equals": "ní ionann", + "contains": "ina bhfuil", + "not contains": "nach bhfuil", + "starts with": "thosaíonn le", + "ends with": "chríochnaíonn le", + "not ends with": "ní chríochnaíonn le", + "less than": "níos lú ná", + "greater than": "níos mó ná", + "less than or equal to": "níos lú ná nó cothrom le", + "record": "taifead", + "shrinkDatabaseDescriptionSqlite": "Bunachar sonraí truicear {vacuum} le haghaidh SQLite. Tá {auto_vacuum} cumasaithe cheana féin ach ní dhéanann sé seo scoilt ar an mbunachar sonraí ná athphacáil leathanaigh aonair an bhunachair sonraí mar a dhéanann an t-ordú {vacuum}.", + "aboutSlackUsername": "Athraítear ainm taispeána sheoltóir na teachtaireachta. Más mian leat duine éigin a lua, cuir san ainm cairdiúil é ina ionad sin.", + "Custom sound to override default notification sound": "Fuaim shaincheaptha chun fuaim fógra réamhshocraithe a shárú", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Déanfar fógraí atá íogair ó thaobh ama a sheachadadh láithreach, fiú má tá an gléas i mód ná cuir isteach.", + "rabbitmqNodesInvalid": "Bain úsáid as URL láncháilithe (ag tosú le 'http') le haghaidh nóid RabbitMQ.", + "rabbitmqHelpText": "Chun an monatóir a úsáid, beidh ort an Breiseán Bainistíochta a chumasú i do chumrú RabbitMQ. Le haghaidh tuilleadh faisnéise, féach ar an {rabitmq_documentation}.", + "Pop": "Popcheol", + "Time Sensitive (iOS Only)": "Am-íogair (iOS amháin)", + "From": "Ó", + "Can be found on:": "Is féidir é a fháil ar: {0}", + "The phone number of the recipient in E.164 format.": "Uimhir theileafóin an fhaighteora san fhormáid E.164.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Aitheantas seoltóir téacs nó uimhir theileafóin i bhformáid E.164 más mian leat a bheith in ann freagraí a fháil.", + "RabbitMQ Nodes": "Nóid Bainistíochta RabbitMQ", + "rabbitmqNodesDescription": "Cuir isteach an URL do na nóid bhainistíochta RabbitMQ lena n-áirítear prótacal agus port. Sampla: {0}", + "rabbitmqNodesRequired": "Socraigh na nóid don mhonatóir seo le do thoil.", + "RabbitMQ Username": "Ainm Úsáideora RabbitMQ", + "RabbitMQ Password": "RabbitMQ Pasfhocal", + "SendGrid API Key": "Eochair API SendGrid", + "Separate multiple email addresses with commas": "Scar seoltaí ríomhphoist iolracha le camóga", + "ignoredTLSError": "Níor tugadh aird ar earráidí TLS/SSL", + "Message format": "Formáid teachtaireachta", + "Send rich messages": "Seol teachtaireachtaí saibhir", + "Notification Channel": "Cainéal Fógraí", + "Sound": "Fuaim", + "Alphanumerical string and hyphens only": "Teaghrán alfa-uimhriúil agus fleiscíní amháin", + "Arcade": "Stuara", + "Correct": "Ceart", + "Fail": "Teip", + "Harp": "Cláirseach", + "Reveal": "Nocht", + "Bubble": "Mboilgeog", + "Doorbell": "Cloigín an dorais", + "Flute": "Fliúit", + "Money": "Airgead", + "Clear": "Glan", + "Elevator": "Ardaitheoir", + "Guitar": "Giotár", + "Scifi": "Ficsean eolaíochta", + "ipFamilyDescriptionAutoSelect": "Úsáideann sé {happyEyeballs} chun an teaghlach IP a chinneadh.", + "Happy Eyeballs algorithm": "Algartam Súile Sona", + "Ip Family": "Teaghlach IP", + "Manual": "Lámhleabhar", + "OAuth Audience": "Lucht Féachana OAuth", + "pingGlobalTimeoutLabel": "Am Teorann Domhanda", + "pingPerRequestTimeoutLabel": "Am Teorann In aghaidh an Phing", + "pingPerRequestTimeoutDescription": "Seo an t-am feithimh uasta (i soicindí) sula measfar go bhfuil paicéad ping amháin caillte", + "pingIntervalAdjustedInfo": "Eatramh coigeartaithe bunaithe ar chomhaireamh paicéad, am scoir domhanda agus am scoir in aghaidh an phing", + "Custom URL": "URL Saincheaptha", + "customUrlDescription": "Úsáidfear é mar an URL inchliceáilte in ionad URL an mhonatóra.", + "OneChatBotId": "Aitheantas Bot OneChat", + "Plain Text": "Téacs Gnáth", + "Disable URL in Notification": "Díchumasaigh URL san Fhógra", + "Add Another Tag": "Cuir Clib Eile leis", + "Staged Tags for Batch Add": "Clibeanna Céimnithe le haghaidh Cuir Baisc leis", + "Clear Form": "Foirm Glan", + "pause": "Sos", + "pingCountDescription": "Líon na bpacáistí le seoladh sula stopann tú", + "smsplanetApiDocs": "Is féidir faisnéis mhionsonraithe maidir le comharthaí API a fháil a fháil i {the_smsplanet_documentation}.", + "the smsplanet documentation": "an doiciméadú smsplanet", + "Phone numbers": "Uimhreacha gutháin", + "Sender name": "Ainm an tseoltóra", + "smsplanetApiToken": "Comhartha don SMSPlanet API", + "defaultFriendlyName": "Monatóir Nua", + "telegramUseTemplate": "Úsáid teimpléad teachtaireachta saincheaptha", + "telegramUseTemplateDescription": "Má tá sé cumasaithe, seolfar an teachtaireacht ag baint úsáide as teimpléad saincheaptha.", + "telegramServerUrlDescription": "Chun teorainneacha bot api Telegram a ardú nó rochtain a fháil i gceantair atá blocáilte (an tSín, an Iaráin, srl.). Le haghaidh tuilleadh eolais cliceáil {0}. Réamhshocrú: {1}", + "Use HTML for custom E-mail body": "Úsáid HTML le haghaidh corp saincheaptha ríomhphoist", + "smseagleGroupV2": "Aitheantóirí grúpa leabhar teileafóin", + "smseagleMsgType": "Cineál teachtaireachta", + "smseagleMsgTts": "Glao téacs-go-hurlabhra", + "smseagleApiv1": "APIv1 (do thionscadail atá ann cheana féin agus comhoiriúnacht siar)", + "Optional: The audience to request the JWT for": "Roghnach: An lucht féachana ar a n-iarrfar an JWT", + "pingCountLabel": "Uasmhéid Pacáistí", + "pingNumericDescription": "Más seiceáilte é, aschuirfear seoltaí IP in ionad ainmneacha óstach siombalacha", + "pingGlobalTimeoutDescription": "Am iomlán i soicindí sula stopann an ping, beag beann ar na paicéid a seoladh", + "smtpHelpText": "Déanann 'SMTPS' tástáil an bhfuil SMTP/TLS ag obair; ceanglaíonn 'Neamhaird a dhéanamh de TLS' thar théacs simplí; ceanglaíonn 'STARTTLS', eisíonn sé ordú STARTTLS agus fíoraíonn sé teastas an fhreastalaí. Ní sheolann aon cheann díobh seo ríomhphost.", + "OneChatAccessToken": "Comhartha Rochtana OneChat", + "OneChatUserIdOrGroupId": "Aitheantas Úsáideora OneChat nó Aitheantas Grúpa", + "wayToGetWahaSession": "Is é Eochair API luach athróg comhshaoil WHATSAPP_API_KEY a d’úsáid tú chun WAHA a rith.", + "wayToWriteWahaChatId": "An uimhir theileafóin leis an réimír idirnáisiúnta, ach gan an comhartha móide ag an tús ({0}), an ID Teagmhála ({1}) ná an ID Grúpa ({2}). Seoltar fógraí chuig an ID Comhrá seo ó Sheisiún WAHA.", + "Font Twemoji by Twitter licensed under": "Cló Twemoji le Twitter ceadúnaithe faoi", + "smsplanetNeedToApproveName": "Ní mór é a cheadú sa phainéal cliant", + "wahaSession": "Seisiún", + "wahaChatId": "ID Comhrá (Uimhir Theileafóin / ID Teagmhála / ID Grúpa)", + "wayToGetWahaApiUrl": "URL d’Áisnéis WAHA.", + "wayToGetWahaApiKey": "Is é Eochair API luach athróg comhshaoil WHATSAPP_API_KEY a d'úsáid tú chun WAHA a rith.", + "ntfyPriorityHelptextPriorityHigherThanDown": "Ba chóir go mbeadh an tosaíocht rialta níos airde ná tosaíocht {0}. Tá tosaíocht {1} níos airde ná tosaíocht {0} {2}", + "ntfyPriorityDown": "Tosaíocht do imeachtaí SÍOS", + "Message Template": "Teimpléad Teachtaireachta", + "Template Format": "Formáid Teimpléid", + "YZJ Robot Token": "Comhartha robot YZJ", + "YZJ Webhook URL": "URL Crúca Gréasán YZJ", + "Add Tags": "Cuir Clibeanna leis", + "tagAlreadyOnMonitor": "Tá an clib seo (ainm agus luach) ar an monatóir cheana féin nó á cur leis ar feitheamh.", + "tagAlreadyStaged": "Tá an clib seo (ainm agus luach) curtha i láthair cheana féin don bhaisc seo.", + "tagNameExists": "Tá clib chórais leis an ainm seo ann cheana féin. Roghnaigh é ón liosta nó bain úsáid as ainm eile.", + "templateStatus": "stádas", + "telegramTemplateFormatDescription": "Ceadaíonn Telegram teangacha marcála éagsúla a úsáid le haghaidh teachtaireachtaí, féach Telegram {0} le haghaidh sonraí sonracha.", + "telegramServerUrl": "(Roghnach) URL an Fhreastalaí", + "smseagleContactV2": "Aitheantóirí teagmhála an leabhair teileafóin", + "smseagleMsgSms": "Teachtaireacht SMS (réamhshocraithe)", + "smseagleMsgRing": "Glaoigh glaoch", + "smseagleMsgTtsAdvanced": "Glao ardleibhéil téacs-go-hurlabhra", + "smseagleDuration": "Fad (i soicindí)", + "smseagleTtsModel": "Aitheantas samhail téacs-go-hurlabhra", + "smseagleApiType": "Leagan API", + "smseagleApiv2": "APIv2 (molta le haghaidh comhtháthúcháin nua)", + "smseagleDocs": "Seiceáil an doiciméadacht nó infhaighteacht APIv2: {0}", + "smseagleComma": "Ní mór ilchodanna a dheighilt le camóg", + "SpugPush Template Code": "Cód Teimpléid", + "FlashDuty Push URL": "Brúigh URL", + "FlashDuty Push URL Placeholder": "Cóipeáil ón leathanach comhtháthaithe foláirimh", + "pingNumericLabel": "Aschur Uimhriúil", + "templateServiceName": "ainm na seirbhíse", + "templateHostnameOrURL": "ainm óstach nó URL", + "Events cleared successfully": "Glanadh na himeachtaí go rathúil.", + "No monitors found": "Níor aimsíodh aon mhonatóirí.", + "Could not clear events": "Níorbh fhéidir {failed}/{total} imeachtaí a ghlanadh", + "Path": "Cosán", + "mqttWebsocketPathExplanation": "Cosán WebSocket le haghaidh MQTT thar naisc WebSocket (m.sh., /mqtt)", + "mqttHostnameTip": "Bain úsáid as an bhformáid seo {hostnameFormat} le do thoil", + "mqttWebSocketPath": "Cosán Soicéad Gréasáin MQTT", + "mqttWebsocketPathInvalid": "Úsáid formáid bhailí Cosáin WebSocket le do thoil", + "Clear All Events": "Glan Gach Imeacht", + "clearAllEventsMsg": "An bhfuil tú cinnte gur mian leat gach imeacht a scriosadh do gach monatóir? Baintear sonraí teachtaireachta gaolmhara leis seo go buan agus ní féidir é a chealú.", + "Template plain text instead of using cards": "Téacs simplí teimpléid in ionad cártaí a úsáid", + "issueWithGoogleChatOnAndroidHelptext": "Ligeann sé seo freisin dul timpeall ar fhabhtanna suas an sruth cosúil le {issuetackerURL}", + "supportBaleChatID": "Tacaíocht Comhrá Díreach / Grúpa / ID Comhrá an Chainéil", + "wayToGetBaleChatID": "Is féidir leat d’ID comhrá a fháil trí theachtaireacht a sheoladh chuig an bot agus dul chuig an URL seo chun an chat_id a fheiceáil:", + "Mention Mobile List": "Luaigh liosta soghluaiste", + "Mention User List": "Luaigh liosta aitheantais úsáideora", + "Invalid mobile": "Fón póca neamhbhailí [{mobile}]", + "brevoApiKey": "Eochair API Brevo", + "brevoLeaveBlankForDefaultName": "fág bán don ainm réamhshocraithe", + "brevoToEmail": "Chuig Ríomhphost", + "brevoSeparateMultipleEmails": "Deighil seoltaí ríomhphoist iolracha le camóga", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Éilíonn suiteáil bot Nextcloud Talk rochtain riaracháin ar an bhfreastalaí.", + "brevoApiHelp": "Cruthaigh eochair API anseo: {0}", + "brevoFromEmail": "Ó Ríomhphost", + "brevoFromName": "Ó Ainm", + "Nextcloud host": "Óstach Nextcloud", + "Conversation token": "Comhartha comhrá", + "Bot secret": "Rún an bhota", + "wayToGetBaleToken": "Is féidir leat comhartha a fháil ó {0}.", + "Dingtalk Mobile List": "Liosta soghluaiste", + "Dingtalk User List": "Liosta aitheantais úsáideora", + "Enter a list of userId": "Cuir isteach liosta d'AitheantasÚsáideora", + "Enter a list of mobile": "Cuir isteach liosta de shoghluaisteáin", + "Invalid userId": "Aitheantas úsáideora neamhbhailí [{userId}]", + "wayToWriteEvolutionRecipient": "An uimhir theileafóin leis an réimír idirnáisiúnta, ach gan an comhartha móide ag an tús ({0}), an ID Teagmhála ({1}) ná an ID Grúpa ({2}).", + "wayToGetEvolutionUrlAndToken": "Is féidir leat URL an API agus an comhartha a fháil trí dhul isteach sa chainéal atá uait ó {0}", + "evolutionRecipient": "Uimhir Theileafóin / Aitheantas Teagmhála / Aitheantas Grúpa", + "evolutionInstanceName": "Ainm an Cháis", + "brevoCcEmail": "Ríomhphost CC", + "brevoBccEmail": "Ríomhphost BCC", + "brevoSubject": "Ábhar", + "brevoLeaveBlankForDefaultSubject": "fág bán don ábhar réamhshocraithe", + "Send UP silently": "Seol SUAS go ciúin", + "Send DOWN silently": "Seol SÍOS go ciúin", + "auto-select": "Roghnaigh Uathoibríoch", + "HTTP Method": "Modh HTTP", + "Maximum Retries": "Uasmhéid Athiarracht", + "ariaPauseMaintenance": "Cuir an sceideal cothabhála seo ar sos", + "ariaDeleteMaintenance": "Scrios an sceideal cothabhála seo", + "deleteGroupMsg": "An bhfuil tú cinnte gur mian leat an grúpa seo a scriosadh?", + "deleteChildrenMonitors": "Scrios na monatóirí díreacha leanaí agus a leanaí freisin más ann dóibh | Scrios gach {count} monatóir díreach leanaí agus a leanaí freisin más ann dóibh", + "webhookPostMethodDesc": "Tá POST maith don chuid is mó de na freastalaithe HTTP nua-aimseartha.", + "webhookGetMethodDesc": "Seolann GET sonraí mar pharaiméadair fiosrúcháin agus ní cheadaíonn sé corp a chumrú. Úsáideach chun monatóirí Uptime Kuma Push a spreagadh.", + "Number of retry attempts if webhook fails": "Líon na n-iarrachtaí athdhéanta (gach 60-180 soicind) má theipeann ar an webhook.", + "descriptionHelpText": "Taispeántar ar an deais inmheánach é. Ceadaítear marcáil anuas agus déantar é a shláintiú (coinnítear spásanna agus eangú) roimh an taispeántas.", + "Clone Maintenance": "Cothabháil Clónála", + "ariaResumeMaintenance": "Atosaigh an sceideal cothabhála seo", + "ariaCloneMaintenance": "Cruthaigh cóip den sceideal cothabhála seo", + "ariaEditMaintenance": "Cuir an sceideal cothabhála seo in eagar", + "Template ID": "Aitheantas Teimpléid", + "Recipient Numbers": "Uimhreacha Faighteoirí", + "wayToGetClickSMSIRTemplateID": "Ní mór réimse {uptkumaalert} a bheith i do theimpléad. Is féidir leat teimpléad nua a chruthú {here}.", + "ignoreSecWebsocketAcceptHeaderDescription": "Ceadaíonn sé seo don fhreastalaí gan freagra a thabhairt leis an gceanntásc Sec-WebSocket-Accept, má éiríonn leis an uasghrádú websocket.", + "Ignore Sec-WebSocket-Accept header": "Déan neamhaird den cheanntásc {0}", + "wsSubprotocolDescription": "Cuir isteach liosta fophrótacal atá scartha le camóga. Le haghaidh tuilleadh eolais faoi fhophrótacail, féach ar an {documentation}", + "WebSocket Application Messaging Protocol": "WAMP (Prótacal Teachtaireachtaí Feidhmchláir WebSocket)", + "Session Initiation Protocol": "Iompar Soicéad Gréasáin le haghaidh SIP (Prótacal Tionscnaimh Seisiúin)", + "Network API for Notification Channel": "API Líonra RESTful OMA le haghaidh Cainéal Fógra", + "Web Process Control Protocol": "Prótacal Rialaithe Próisis Gréasáin (WPCP)", + "Advanced Message Queuing Protocol": "Prótacal Ciúála Teachtaireachtaí Ardleibhéil (AMQP) 1.0+", + "jsflow": "Prótacal pubsub/queue jsFlow", + "Reverse Web Process Control": "Prótacal Rialaithe Próisis Gréasáin Droim ar Ais (RWPCP)", + "Extensible Messaging and Presence Protocol": "Iompar WebSocket don Phrótacal Teachtaireachtaí agus Láithreachta Inleathnaithe (XMPP)", + "Smart Home IP": "LOING - IP Baile Cliste", + "Miele Cloud Connect Protocol": "Prótacal Miele Cloud Connect", + "Push Channel Protocol": "Prótacal Brúigh Cainéal", + "Message Session Relay Protocol": "Iompar WebSocket le haghaidh MSRP (Prótacal Athsheolta Seisiúin Teachtaireachtaí)", + "Binary Floor Control Protocol": "Iompar Soicéad Gréasáin le haghaidh BFCP (Prótacal Rialaithe Urláir Dénártha)", + "Softvelum Low Delay Protocol": "Prótacal Moille Íseal Softvelum", + "OPC UA Connection Protocol": "Prótacal Ceangail OPC UA", + "OPC UA JSON Encoding": "Ionchódú OPC UA JSON", + "Swindon Web Server Protocol": "Prótacal Freastalaí Gréasáin Swindon (ionchódú JSON)", + "Broadband Forum User Services Platform": "USP (Ardán Seirbhísí Úsáideoirí Fóram Leathanbhanda)", + "Constrained Application Protocol": "Prótacal Iarratais Srianta (CoAP)", + "Softvelum WebSocket signaling protocol": "Prótacal Comharthaíochta Softvelum WebSocket", + "Cobra Real Time Messaging Protocol": "Prótacal Teachtaireachtaí Fíor-Ama Cobra", + "Declarative Resource Protocol": "Prótacal Acmhainní Dearbhaithe", + "BACnet Secure Connect Hub Connection": "Nasc Mol Slán BACnet", + "Allow Notifications": "Ceadaigh Fógraí", + "Notifications Enabled": "Fógraí Cumasaithe", + "Browser not supported": "Ní thacaítear le brabhsálaí", + "certHostnameMismatch": "Ní hionann ainm óstach an deimhnithe agus URL an mhonatóra.", + "twilioMessagingServiceSID": "SID Seirbhíse Teachtaireachtaí (roghnach)", + "twilloMessagingServiceSIDHelptext": "Cuir isteach d’Aitheantas Sóisialta Seirbhíse Teachtaireachtaí anseo má tá tú ag úsáid {twillo_messaging_service_help_link} chun seoltóirí agus gnéithe a bhainistiú", + "twilioApiKeyHelptext": "Tá an eochair API roghnach ach moltar í. Is féidir leat SID an Chuntais agus AuthToken a sholáthar ó leathanach TwilioConsole nó SID an Chuntais agus an péire Eochair API agus rún Eochair API", + "BACnet Secure Connect Direct Connection": "Ceangal Díreach Slán BACnet", + "WebSocket Transport for JMAP": "Iompar WebSocket do JMAP (Prótacal Meitea-Fheidhmchláir JSON)", + "ITU-T T.140 Real-Time Text": "Téacs Fíor-Ama ITU-T T.140", + "Done.best IoT Protocol": "Prótacal Idirlín na Rudaí Done.best", + "Collection Update": "Fophrótacal Gréasáin Nuashonraithe an Bhailiúcháin", + "Text IRC Protocol": "Prótacal IRC Téacs", + "Binary IRC Protocol": "Prótacal IRC Dénártha", + "Penguin Statistics Live Protocol v3": "Prótacal Beo Staitisticí Penguin v3 (ionchódú Protobuf)", + "Unable to get permission to notify": "Ní féidir cead a fháil chun fógra a thabhairt (diúltaíodh nó neamhaird déanta ar an iarratas).", + "Webpush Helptext": "Ní oibríonn brú gréasáin ach le naisc SSL (HTTPS). I gcás gléasanna iOS, ní mór leathanach gréasáin a chur leis an scáileán baile roimh ré.", + "showOnlyLastHeartbeat": "Taispeáin an Buille Croí Deireanach Amháin", + "minimumIntervalWarning": "D’fhéadfadh eatraimh faoi bhun 20 soicind drochfheidhmíocht a bheith mar thoradh air.", + "lowIntervalWarning": "An bhfuil tú cinnte gur mian leat an luach eatraimh a shocrú faoi bhun 20 soicind? D’fhéadfadh an fheidhmíocht dul in olcas, go háirithe má tá líon mór monatóirí ann.", + "Deselect All": "Díroghnaigh Gach Rud", + "Select All": "Roghnaigh Uile", + "Enter the list of nodes": "Cuir isteach liosta na nóid bainistíochta RabbitMQ", + "resendApiHelp": "Cruthaigh eochair API anseo {0}", + "resendApiKey": "Athsheol Eochair API", + "resendFromName": "Ó Ainm", + "resendFromEmail": "Ó Ríomhphost", + "resendLeaveBlankForDefaultName": "fág bán don ainm réamhshocraithe", + "resendSubject": "Ábhar", + "labelDomainExpiry": "Dul in Éag Fearainn", + "settingsDomainExpiry": "Dáta Éaga Fearainn", + "imageResetConfirmation": "Athshocraigh an íomhá go dtí an réamhshocrú", + "wsCodeDescription": "Chun tuilleadh eolais a fháil faoi chóid stádais, féach ar {rfc6455}", + "Subprotocol(s)": "Fophrótacal(anna)", + "Duration (Minutes)": "Fad (Nóiméid)", + "SMTP Security": "Fad (Nóiméid)", + "Ignore STARTTLS": "Déan neamhaird de STARTTLS", + "systemServiceCommandHint": "Ordú a úsáideadh: {command}", + "systemService": "Seirbhís Córais", + "systemServiceName": "Ainm na Seirbhíse", + "systemServiceDescription": "Seiceálann sé an bhfuil seirbhís chórais {service_name} gníomhach", + "systemServiceDescriptionLinux": "Seiceálann sé an bhfuil seirbhís chóras Linux {service_name} gníomhach", + "systemServiceDescriptionWindows": "Seiceálann sé an bhfuil Bainisteoir Seirbhísí Windows {service_name} ag rith", + "labelDomainNameExpiryNotification": "Fógra maidir le Dul in Éag Ainm Fearainn", + "avgPing": "Meán-Phing", + "minPing": "Íosmhéid Ping", + "maxPing": "Uasmhéid Ping", + "Analytics Type": "Cineál Anailísíochta", + "Analytics ID": "Aitheantas Anailísíochta", + "Analytics Script URL": "URL Script Anailísíochta", + "systemServiceExpectedOutput": "Aschur Ionchais: \"{0}\"", + "resendToEmail": "Chuig Ríomhphost", + "Google": "Google", + "Plausible": "Inchreidte", + "Matomo": "Matomo", + "Umami": "Umami", + "domainExpiryDescription": "Cuir fógra ar fáil nuair a théann ainmneacha fearainn in éag i:", + "Use STARTTLS": "Úsáid STARTTLS", + "Press Enter to add node": "Brúigh Iontráil chun nód a chur leis", + "sipsakPingWarning": "Chun monatóir SIP Options Ping a úsáid, ní mór duit Uptime Kuma a shuiteáil gan Docker agus cliant Sipsak a shuiteáil ar do fhreastalaí freisin.", + "message": "teachtaireacht", + "json_value": "Luach JSON", + "PushDeer Server URL": "URL Freastalaí PushDeer", + "Open Badge Link Generator": "Gineadóir Nasc Suaitheantais Oscailte", + "Badge Link Generator": "Gineadóir Nasc Suaitheantais {0}", + "Badge Link Generator Helptext": "Tá naisc suaitheantais ar fáil do gach monatóir atá sannta do leathanaigh stádais phoiblí. Le haghaidh tuilleadh eolais, féach ar an {documentation}.", + "To Number": "Go Uimhir", + "Show this Maintenance Message on which Status Pages": "Taispeáin an Teachtaireacht Chothabhála seo ar a bhfuil Leathanaigh Stádais", + "year": "bliain | blianta", + "screenshot of the website": "Scáileán den suíomh Gréasáin", + "Basic checkbox toggle button group": "Grúpa cnaipe scoránaigh bunúsach seiceála", + "Basic radio toggle button group": "Grúpa cnaipe scoránaigh raidió bunúsach", + "Region": "Réigiún", + "resendLeaveBlankForDefaultSubject": "Fág bán don ábhar réamhshocraithe", + "mtls-auth-server-cert-label": "Teastas", + "mtls-auth-server-key-placeholder": "Corp eochair", + "mtls-auth-server-cert-placeholder": "Comhlacht deimhnithe", + "mtls-auth-server-ca-label": "CA", + "mtls-auth-server-ca-placeholder": "CA Freastalaí", + "mtls-auth-server-key-label": "Eochair", + "Uptime Kuma": "Uptime Kuma", + "Clear current filters": "Glan na scagairí reatha", + "Sort by certificate expiry": "Sórtáil de réir éaga an deimhnithe", + "SMSManager": "Bainisteoir SMS", + "Sort options": "Roghanna sórtála", + "Splunk Rest URL": "URL Scíthe Splunk", + "Severity": "Déine", + "Sort by status": "Sórtáil de réir stádais", + "Sort by name": "Sórtáil de réir ainm", + "Sort by uptime": "Sórtáil de réir ama oibriúcháin", + "Message Format": "Formáid Teachtaireachta", + "GrafanaOncallURL": "URL Glaoigh ar Grafana", + "Never": "Choíche", + "System Service": "Seirbhís Córais", + "SSL/TLS": "SSL/TLS", + "playground": "clós súgartha", + "Check Type": "Cineál Seiceála", + "Service Name": "Ainm na Seirbhíse", + "GRPC Options": "Roghanna GRPC", + "Metadata": "Meiteashonraí", + "End": "Deireadh", + "Endpoint": "Críochphointe", + "Details": "Sonraí", + "RSS Title": "Teideal RSS", + "Leave blank to use status page title": "Fág bán chun teideal an leathanaigh stádais a úsáid", + "smscTranslit": "smscTranslit", + "promosms": "promosms", + "hostnameCannotBeIP": "Ní féidir le hainm óstach DNS a bheith ina IP. An raibh sé i gceist agat an réimse réiteora a úsáid?", + "invalidHostnameOrIP": "Ainm óstach nó IP neamhbhailí. Ní mór don ainm óstach a bheith ina FQDN bailí. Ní féidir fiáin-chárta a úsáid. Féadfar fo-líne a bheith ann, nó críochnú le ponc.", + "invalidDNSHostname": "Ainm óstach neamhbhailí. Ní mór don ainm óstach a bheith ina FQDN bailí. Is féidir é a bheith ina fhiáin-chárta, fo-líne a bheith ann nó críochnú le ponc.", + "wildcardOnlyForDNS": "Ní thacaítear le hainmneacha óstach fiáine ach le haghaidh monatóirí DNS.", + "invalidURL": "URL neamhbhailí", + "domain_expiry_unsupported_monitor_type": "Ní thacaítear le monatóireacht ar dhul in éag fearainn don chineál seo monatóireachta", + "domain_expiry_unsupported_missing_target": "Níl aon fhearann ná ainm óstach bailí cumraithe don mhonatóir seo", + "snmpV3Username": "Ainm úsáideora SNMPv3", + "Resolver Server(s)": "Freastalaí(í) Réitigh", + "HeadersInvalidFormatBecause": "Níl ceanntásca na n-iarratas bailí mar gheall ar {error}", + "WeCom Mentioned Mobile List": "Liosta Soghluaiste Luaigh WeCom", + "WeCom Mentioned Mobile List Description": "Cuir isteach uimhreacha gutháin le lua. Deighil uimhreacha iolracha le camóga. Úsáid {'@'}all chun gach duine a lua.", + "BodyInvalidFormatBecause": "Níl corp an iarratais bailí mar gheall ar {error}", + "saveResponseForNotifications": "Sábháil Freagra Rathúil HTTP le haghaidh Fógraí", + "saveErrorResponseForNotifications": "Sábháil Freagra Earráide HTTP le haghaidh Fógraí", + "saveResponseDescription": "Stórálann sé an freagra HTTP agus cuireann sé ar fáil é do theimpléid fógra mar {templateVariable}", + "responseMaxLength": "Fad Uasta Freagra (bearta)", + "responseMaxLengthDescription": "Uasmhéid sonraí freagartha le stóráil. Socraigh go 0 le haghaidh neamhtheoranta. Gearrfar freagraí níos mó. Réamhshocrú: 1024 (1KB)", + "steamApiKeyDescriptionAt": "Chun monatóireacht a dhéanamh ar Fhreastalaí Cluichí Steam, teastaíonn eochair Steam Web-API uait. Is féidir leat d’eochair API a chlárú ag {url}", + "pausedMonitorsMsg": "Monatóir {n} ar sos | Monatóirí {n} ar sos", + "resumedMonitorsMsg": "Monatóir {n} atosaithe | Monatóirí {n} atosaithe", + "You can divide numbers with commas or semicolons": "Is féidir leat uimhreacha a roinnt le {comma} nó {semicolon}", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "URL Gréasáin Hook PSA Halo", + "halopsa_webhook_url_desc": "Cuir isteach URL an webhook ó do Halo PSA Integration Runbook (Cumraíocht > Comhtháthúcháin > Comhtháthúcháin Saincheaptha > Leabhair Rith Comhtháthaithe). Roghnaigh 'Ní féidir é a thosú ach ó Halo agus ó chríochphointe poiblí' agus an webhook á chruthú.", + "Expand All Groups": "Leathnaigh Gach Grúpa", + "Collapse All Groups": "Laghdaigh Gach Grúpa", + "mariadbSocketPathDetectedHelptext": "Ag ceangal leis an mbunachar sonraí mar atá sonraithe tríd an athróg timpeallachta {0}.", + "unknownDays": "Laethanta anaithnide", + "Monitors": "Monatóir {n} | Monatóirí {n}", + "username": "Ainm úsáideora", + "password": "Pasfhocal", + "serwersmsGroupId": "Aitheantas Grúpa", + "serwersmsRecipientType": "Cineál faighteora", + "serwersmsRecipientTypeGroup": "Grúpa", + "serwersmsRecipientTypePhone": "Uimhir ghuthán", + "noMonitorsOrStatusPagesSelectedError": "Ní féidir cothabháil a chruthú gan monatóirí nó leathanaigh stádais lena mbaineann", + "noMonitorsSelectedWarning": "Tá tú ag cruthú cothabhála gan aon mhonatóirí atá buailte. An bhfuil tú cinnte gur mhaith leat leanúint ar aghaidh?", + "serwersmsGroupIdHelptext": "Aitheantóirí aitheantais nó grúpa sa Phainéal Custaiméirí. Is féidir na haitheantóirí seo a íoslódáil ag baint úsáide as grúpaí gníomhaíochta / innéacs nó trína gcóipeáil ón ngrúpa eagarthóireachta sa Phainéal Custaiméirí.", + "domain_expiry_public_suffix_too_short": "Tá \".{publicSuffix}\" ró-ghearr le haghaidh fearann barrleibhéil", + "domain_expiry_unsupported_is_ip": "Is seoladh IP é \"{hostname}\". Éilíonn monatóireacht ar dhul in éag fearainn ainm fearainn", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Níl monatóireacht ar dhul in éag fearainn ar fáil do \".{publicSuffix}\" mar nach bhfuil aon tseirbhís RDAP liostaithe ag IANA", + "Disable STARTTLS": "Díchumasaigh STARTTLS", + "disableSTARTTLSDescription": "Cumasaigh an rogha seo do fhreastalaithe SMTP nach dtacaíonn le STARTTLS. Seolfar ríomhphoist thar nasc neamhchriptithe leis seo.", + "Setup Instructions": "Treoracha Socraithe", + "TLS Alerts": "Foláirimh TLS", + "Expected TLS Alert": "Foláireamh TLS a bhíothas ag súil leis", + "None (Successful Connection)": "Gan aon cheann (Ceangal Rathúil)", + "TLS Alert Spec": "RFC 8446", + "Suppress Notifications": "Fógraí a Chosc", + "discordSuppressNotificationsHelptext": "Nuair a bheidh sé cumasaithe, cuirfear teachtaireachtaí chuig an gcainéal ach ní spreagfar fógraí brú ná fógraí deisce do fhaighteoirí.", + "domain_expiry_unsupported_is_icann": "Ní iarrthóir é an fearann \"{domain}\" le haghaidh monatóireachta ar dhul in éag fearainn, toisc nach bhfuil a iarmhír phoiblí \".{publicSuffix}\" á bhainistiú ag ICANN", + "notificationUniversal": "Uilíoch", + "notificationChatPlatforms": "Ardáin Comhrá", + "notificationPushServices": "Seirbhísí Brúigh", + "notificationSmsServices": "Seirbhísí SMS", + "notificationEmail": "Ríomhphost", + "notificationHomeAutomation": "Uathoibriú Baile", + "notificationOther": "Comhtháthúcháin Eile", + "OptionalParameters": "Paraiméadair Roghnacha", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Mar gheall ar shrianta iompróra, cumasaigh athróga roghnacha ar an mbaol neamhsheachadta", + "aliyun-template-requirements-and-parameters": "Ní mór na paraiméadair seo a leanas a bheith sa teimpléad SMS aliyun: {parameters}", + "aliyun-template-optional-parameters": "Paraiméadair roghnacha: {parameters}", + "versionIs": "Leagan: {version}", + "logoutCurrentUser": "Logáil amach {username}", + "createdAt": "Cruthaithe: {date}", + "frontendVersionIs": "Leagan Tosaigh: {version}", + "Certificate Chain:": "Slabhra Teastais:", + "dateCreatedAtFromNow": "Dáta Cruthaithe: {date} ({fromNow})", + "Examples:": "Samplaí: {0}", + "octopushEndpoint": "ochtapas (deireadhphointe: {url})", + "halopsa_setup_step1": "Cruthaigh Leabhar Rith Comhtháthaithe in HaloPSA (Cumraíocht → Comhtháthúcháin → Leabhair Rith Comhtháthaithe)", + "milliseconds": "{n} milleasoicind | {n} milleasoicind", + "Screenshot Delay": "Moill ar Ghlacadh Scáileáin (fanacht {milliseconds})", + "screenshotDelayDescription": "Is féidir fanacht an méid seo milleasoicind sula nglactar an scáileán. Uasmhéid: {maxValueMs}ms (eatramh 0.5 ×).", + "screenshotDelayWarning": "Coinníonn luachanna níos airde an brabhsálaí ar oscailt ar feadh tréimhse níos faide, rud a d'fhéadfadh úsáid na cuimhne a mhéadú le go leor monatóirí comhuaineacha.", + "halopsa_setup_step2": "Cumraigh gníomhartha runbook chun foláirimh a phróiseáil (m.sh., Ticéad a Chruthú)", + "halopsa_setup_step3": "Cóipeáil URL an Webhook agus greamaigh é os cionn an réimse téacs", + "Actions": "Gníomhartha", + "selectedMonitorCountMsg": "roghnaithe: {n} | roghnaithe: {n}", + "selectMonitorMsg": "Roghnaigh monatóirí chun gníomhartha a dhéanamh", + "notificationIncidentManagement": "Bainistíocht Teagmhas", + "deletedMonitorsMsg": "Scriosadh {n} monatóir | Scriosadh {n} monatóirí", + "noMonitorsPausedMsg": "Níl aon mhonatóirí curtha ar sos (ní raibh aon cheann gníomhach)", + "noMonitorsResumedMsg": "Níor atosaíodh aon mhonatóirí (ní raibh aon cheann acu neamhghníomhach)", + "halopsa_setup_step4": "Roghnaigh Fíordheimhniú Bunúsach agus cruthaigh ainm úsáideora agus pasfhocal. Agus clóscríobh nó greamaigh an t-ainm úsáideora agus an pasfhocal sin os cionn na gcomhad tástála", + "Only retry if status code check fails": "Déan iarracht eile ach amháin má theipeann ar an seiceáil cód stádais", + "retryOnlyOnStatusCodeFailureDescription": "Má tá sé cumasaithe, ní dhéanfar athiarracht ach amháin nuair a theipeann ar an seiceáil cód stádais HTTP (m.sh., má tá an freastalaí síos). Má éiríonn leis an tseiceáil cód stádais ach má theipeann ar an gceist JSON, marcálfar an monatóir mar síos láithreach gan athiarracht.", + "selectAllMonitorsAria": "Roghnaigh gach monatóir", + "deselectAllMonitorsAria": "Díroghnaigh gach monatóir", + "checkPriceAt": "Seiceáil praghsanna {service} ag {url}", + "No incidents recorded": "Níor taifeadadh aon eachtraí", + "Loading...": "Ag lódáil...", + "Pin this incident": "Bioráin an eachtra seo", + "Incident description": "Cur síos ar an teagmhas", + "Incident not found or access denied": "Níor aimsíodh an teagmhas nó diúltaíodh rochtain", + "Past Incidents": "Teagmhais san Am atá Caite", + "Load More": "Luchtaigh Tuilleadh", + "Incident title": "Teideal an teagmhais", + "Pinned incidents are shown prominently on the status page": "Taispeántar teagmhais phionáilte go feiceálach ar an leathanach stádais", + "Edit Incident": "Cuir Teagmhas in Eagar", + "Resolve": "Réitigh", + "Resolved": "Réitithe", + "lastUpdatedAt": "Nuashonraithe Deiridh: {date}", + "lastUpdatedAtFromNow": "Nuashonraithe Deiridh: {date} ({fromNow})", + "deleteIncidentMsg": "An bhfuil tú cinnte gur mian leat an eachtra seo a scriosadh?", + "slug is not found": "Ní bhfuarthas sluga", + "Please input content": "Cuir isteach ábhar le do thoil", + "Please input title": "Cuir isteach teideal le do thoil", + "cronScheduleDescription": "Sceideal: {description}", + "bulkDeleteErrorMsg": "Theip ar {n} monatóir a scriosadh | Theip ar {n} monatóirí a scriosadh", + "legacyOctopushEndpoint": "Oidhreacht Octopush-DM (críochphointe: {url})", + "halopsa_username_desc": "Ainm úsáideora le haghaidh fíordheimhniú le webhook Halo PSA", + "halopsa_password_desc": "Pasfhocal le haghaidh fíordheimhniú le crúca gréasáin Halo PSA", + "passwordTooWeak": "Tá an focal faire ró-lag. Ba chóir go mbeadh carachtair aibítreacha agus uimhriúla ann. Ní mór go mbeadh sé 6 charachtar ar fhad ar a laghad.", + "ntfyCall": "Glao Gutháin", + "ntfyCallHelptext": "Cuir glaoch gutháin nuair a chuirtear foláireamh ar siúl. Socraigh go 'tá' chun d'uimhir fhíoraithe tosaigh a úsáid, nó cuir isteach uimhir guthán shonrach (e.g. +12223334444). Éilíonn sé ntfy Pro agus uimhir ghuthán fhíoraithe.", + "enableSSL": "Cumasaigh SSL/TLS", + "mariadbUseSSLHelptext": "Cumasaigh úsáid nasc criptithe le do bhunachar sonraí. Riachtanach don chuid is mó de na bunachair sonraí scamall.", + "mariadbCaCertificateLabel": "Teastas CA", + "mariadbCaCertificateHelptext": "Greamaigh an Teastas CA i bhformáid PEM le húsáid le teastais féinshínithe. Fág bán é má úsáideann do bhunachar sonraí teastas atá sínithe ag CA poiblí.", + "days": "{n} lá | {n} lá", + "hours": "{n} uair | {n} uair an chloig", + "minutes": "{n} nóiméad | {n} nóiméid", + "minuteShort": "{n} nóim| {n} nóim", + "years": "{n} bliain | {n} blianta", + "deleteMonitorsMsg": "An bhfuil tú cinnte gur mian leat na monatóirí roghnaithe a scriosadh?", + "Sets end time based on start time": "Socraíonn sé am críochnaithe bunaithe ar am tosaithe", + "Please set start time first": "Socraigh an t-am tosaithe ar dtús le do thoil", + "expectedTlsAlertDescription": "Roghnaigh an foláireamh TLS a bhfuil súil agat go dtabharfaidh an freastalaí ar ais é. Úsáid {code} chun a fhíorú go ndiúltaíonn críochphointí mTLS do naisc gan deimhnithe cliaint. Féach {link} le haghaidh sonraí.", + "Teltonika SMS Gateway": "Geata SMS Teltonika", + "teltonikaVersionWarning": "Éilíonn an soláthraí fógraí seo go ritheann do ghléas Teltonika leagan 7.14.0 de RMS, nó níos airde.", + "teltonikaUrl": "URL do ghléis Teltonika", + "teltonikaUrlHelptext": "Ba chóir URL a shonrú mar bhunadh iomlán, m.sh. {0}, nó {1}.", + "teltonikaUnsafeTls": "Déan neamhaird den bhailíochtú teastais", + "teltonikaUsername": "Ainm úsáideora API", + "discordMessageFormatMinimalist": "Íostach (stádas gearr)", + "discordMessageFormatCustom": "Teimpléad saincheaptha", + "discordUseMessageTemplate": "Úsáid teimpléad teachtaireachta saincheaptha", + "RecordMatch": "Meaitseáil luach taifead", + "RegexMatch": "Cuir isteach regex a mheaitseálann luach an taifid", + "account settings": "socruithe cuntais", + "Location": "Suíomh", + "Check for": "Seiceáil le haghaidh", + "templateAvailableVariables": "Athróga atá ar fáil", + "example": "Sampla", + "Result": "Toradh", + "See Jira Cloud Docs": "Féach ar Dhoiciméid Jira Cloud", + "API Token": "Comhartha API", + "Cloud ID": "Aitheantas Néil", + "Click Deploy → New deployment → Web app": "Cliceáil Imscaradh → Imscaradh nua → Aip ghréasáin", + "Go to Extensions → Apps Script": "Téigh go dtí Síneadh → Script na nAipeanna", + "Paste the script code (see below)": "Greamaigh an cód scripte (féach thíos)", + "Google Apps Script Code": "Cód Scripte Google Apps", + "aboutJiraCloudId": "Tuilleadh eolais faoi Jira Cloud ID: {0}", + "see Jira Cloud Docs": "féach ar Dhoiciméid Jira Cloud", + "ntfyUseTemplate": "Saincheap teimpléid fógra", + "certificateExpiryNotificationHelp": "Is féidir líon na laethanta roimh ré a chumrú sna Socruithe.", + "Deploy a Google Apps Script as a web app and paste the URL here": "Imscaradh Script Google Apps mar aip ghréasáin agus greamaigh an URL anseo", + "Quick Setup Guide": "Treoir Socraithe Tapa", + "Copy to Clipboard": "Cóipeáil chuig an nGearrthaisce", + "Copied to clipboard!": "Cóipeáilte chuig an ghearrthaisce!", + "Failed to copy to clipboard": "Theip ar chóipeáil chuig an ghearrthaisce", + "slackIncludeGroupName": "Cuir ainm an ghrúpa monatóireachta san áireamh", + "ntfyCustomTitle": "Teimpléad Teidil Saincheaptha", + "ntfyCustomMessage": "Teimpléad Teachtaireachta Saincheaptha", + "ntfyNotificationTemplateFallback": "Fág bán chun an fhormáid réamhshocraithe Uptime Kuma a úsáid", + "discordUseMessageTemplateDescription": "Má tá sé cumasaithe, seolfar an teachtaireacht ag baint úsáide as teimpléad saincheaptha (LiquidJS). Fág bán é chun an fhormáid réamhshocraithe Uptime Kuma a úsáid.", + "discordMessageTemplate": "Teimpléad Teachtaireachta", + "Globalping - Access global monitoring probes": "Globalping - Rochtain ar bhrabhsálaithe monatóireachta domhanda", + "Globalping API Token": "Comhartha API Globalping", + "globalpingApiTokenDescription": "Faigh do Chomhartha API Globalping ag {0}.", + "GlobalpingHostname": "Sprioc tomhais atá inrochtana go poiblí. De ghnáth ainm óstach nó seoladh IPv4/IPv6, ag brath ar an gcineál tomhais.", + "domainExpiryNotificationHelp": "Is féidir líon na laethanta roimh ré a chumrú sna Socruithe.", + "halopsa_setup_step5": "Cumraigh runbook chun monitor_id a úsáid chun foláirimh a mheaitseáil le ticéid atá ann cheana féin", + "teltonikaUsernameHelptext": "Moladh: Cruthaigh cuntas ar leithligh atá teoranta do theachtaireachtaí SMS amháin agus cuir isteach a ainm úsáideora anseo", + "teltonikaPassword": "Pasfhocal API", + "teltonikaPasswordHelptext": "Is féidir leat pasfhocal úsáideora API a shainiú i do ródaire Teltonika, m.sh. {0}", + "teltonikaModem": "Aitheantas Móideim", + "teltonikaModemHelptext": "Caithfidh aitheantas an mhóideim SMS a bheith san fhormáid {0}. Féach https://developers.teltonika-networks.com/reference/ le haghaidh treorach.", + "Webhook Payload Fields": "Réimsí Ualaigh Webhook", + "halopsa_payload_desc": "Seoltar na réimsí seo a leanas chuig do chrúca gréasáin Halo PSA:", + "halopsa_field_title": "Teideal an fholáirimh (i gcónaí 'Foláireamh Kuma Am Ar Fáil')", + "halopsa_field_status": "Stádas monatóireachta: SUAS, SÍOS, FÓGRA, nó ANAITHNITHE", + "halopsa_field_monitor": "Ainm an mhonatóra", + "halopsa_field_message": "Teachtaireacht foláirimh iomlán le stádas agus sonraí", + "halopsa_field_timestamp": "Stampa ama imeachta i bhformáid ISO 8601", + "teamsEnableTags": "Cuir clibeanna san áireamh", + "teamsEnableTagsDescription": "Má tá sé cumasaithe, beidh na clibeanna monatóireachta san áireamh sa teachtaireacht.", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Socraigh 'Forghníomhaigh mar: Mise' agus 'Cé a bhfuil rochtain aige: Aon duine'", + "ntfyUseTemplateDescription": "Cumasaigh seo chun teidil agus teachtaireachtaí fógraí a shaincheapadh ag baint úsáide as teimpléadú LiquidJS", + "discordMessageFormat": "Formáid Teachtaireachta", + "discordMessageFormatNormal": "Gnáth (leabaithe saibhre)", + "GlobalpingMonitorDescription": "Cuireann Globalping rochtain ar fáil ar na mílte braiteoir pobail chun tástálacha agus tomhais líonra a reáchtáil. Tá teorainn 250 tástáil san uair socraithe do gach úsáideoir gan ainm. Chun an teorainn a dhúbailt go 500 san uair, sábháil do chomhartha i {accountSettings}. Seiceáil na {docs} le haghaidh tuilleadh eolais.", + "GlobalpingLocation": "Glacann an réimse suímh le hilchríocha, tíortha, réigiúin, cathracha, ASNanna, ISPanna, nó réigiúin scamall. Is féidir leat scagairí a chomhcheangal le {plus} (m.sh. {amazonPlusGermany} nó {comcastPlusCalifornia}). Más méadracht thábhachtach í an mhoill, bain úsáid as scagairí chun an suíomh a chúngú síos go réigiún beag chun borradh a sheachaint. {fullDocs}.", + "Monitor Subtype": "Fochineál Monatóra", + "halopsa_field_monitor_id": "Aitheantóir uathúil monatóireachta (nialasach le haghaidh fógraí tástála) - Úsáid é seo chun foláirimh a mheaitseáil le ticéid", + "halopsa_field_uptime_kuma_version": "Uimhir leagan Kuma Am Ar Fáil", + "halopsa_id_usage_hint": "💡 Leid: Bain úsáid as monitor_id chun foláirimh a mheaitseáil go hiontaofa le ticéid, agus heartbeat_id chun stair imeachtaí a rianú", + "teltonikaUnsafeTlsDescription": "Má mhúchann tú bailíochtú teastais TLS, osclaítear tú suas le hionsaithe ar an gcosán (fear-sa-lár), rud a d'fhéadfadh sceitheanna sonraí agus glacadh seilbhe córas a bheith mar thoradh air. Ná múch bailíochtú teastais mura nglacann tú leis an veicteoir ionsaithe seo. Molaimid LetsEncrypt a úsáid le hathnuachan uathoibríoch.", + "teltonikaPhoneNumber": "Uimhir Ghutháin", + "teltonikaPhoneNumberHelptext": "Ní mór don uimhir a bheith san fhormáid idirnáisiúnta {0}, {1}. Ní cheadaítear ach uimhir amháin.", + "matrixUseTemplate": "Úsáid teimpléad teachtaireachta saincheaptha", + "matrixUseTemplateDescription": "Má tá sé cumasaithe, seolfar an teachtaireacht ag baint úsáide as teimpléad saincheaptha.", + "Open your Google Spreadsheet": "Oscail do Scarbhileog Google", + "Copy the web app URL and paste it above": "Cóipeáil URL an aip ghréasáin agus greamaigh thuas é", + "slackIncludeGroupNameDescription": "Má chuirtear ar chumas é, cuirfear cosán an ghrúpa monatóirí san áireamh sna fógraí chun cabhrú le monatóirí leis an ainm céanna a idirdhealú i ngrúpaí éagsúla.", + "slackUseTemplate": "Úsáid teimpléad teachtaireachta saincheaptha", + "slackUseTemplateDescription": "Má tá sé cumasaithe, seolfar an teachtaireacht ag baint úsáide as teimpléad saincheaptha. Is féidir leat teimpléadú Liquid a úsáid chun faisnéis ghrúpa monatóireachta a áireamh trí monitorJSON.path nó monitorJSON.pathName.", + "Protocol": "Prótacal", + "GlobalpingLocationDocs": "Doiciméadacht iomlán ionchuir suímh", + "GlobalpingIpFamilyInfo": "An leagan IP le húsáid. Ní cheadaítear é seo ach amháin má tá an sprioc ina hainm óstach.", + "GlobalpingResolverInfo": "Seoladh IPv4/IPv6 nó Ainm Fearainn Cáilithe go Lán (FQDN). Is é an réamhshocrú ná réiteoir líonra áitiúil an tóireadóra. Is féidir leat an freastalaí réiteora a athrú am ar bith.", + "Jira Service Management": "Bainistíocht Seirbhíse Jira", + "Google Apps Script Webhook URL": "URL Gréasáin-chrúca Script Google Apps", + "360messengerEnableSendToGroup": "Cumasaigh seoltaí chuig grúpa(í) WhatsApp", + "360messengerAuthToken": "Eochair API 360messenger", + "360messengerRecipient": "Uimhir(í) theileafóin an fhaighteora", + "360messengerGroupId": "Aitheantas Grúpa 360messenger", + "360messengerUseTemplate": "Úsáid teimpléad teachtaireachta saincheaptha", + "360messengerTemplate": "Teimpléad Teachtaireachta 360messenger", + "360messengerGroupList": "Grúpaí WhatsApp", + "360messengerSelectGroupList": "Roghnaigh grúpa le cur leis", + "360messengerSelectedGroupID": "Aitheantais Ghrúpa Roghnaithe", + "360messengerCustomMessageTemplate": "Teimpléad teachtaireachta saincheaptha", + "360messengerEnableCustomMessage": "Cumasaigh teimpléad teachtaireachta saincheaptha in ionad an teachtaireachta réamhshocraithe.", + "360messengerMessageTemplate": "Teimpléad teachtaireachta", + "360messengerWayToGetUrlAndToken": "Is féidir leat d'eochair API 360messenger a fháil ó {0}.", + "360messengerErrorNoApiKey": "Cuir isteach d’eochair API 360messenger ar dtús.", + "360messengerErrorNoGroups": "Ní bhfuarthas aon ghrúpaí WhatsApp don chuntas seo.", + "360messengerErrorApi": "Ní féidir liosta na ngrúpaí WhatsApp a luchtú (Earráid {statusCode}: {message}).", + "360messengerErrorGeneric": "Ní féidir an liosta grúpa WhatsApp a luchtú: {message}", + "GlobalpingLocationDescription": "Glacann an réimse suímh le hilchríocha, tíortha, réigiúin, cathracha, ASNanna, ISPanna, nó réigiúin scamall. Is féidir leat scagairí a chomhcheangal le {plus} (m.sh. {amazonPlusGermany} nó {comcastPlusCalifornia}). Más méadracht thábhachtach í an mhoill, bain úsáid as scagairí chun an suíomh a chúngú síos go réigiún beag chun spící a sheachaint agus socraigh an scagaire {datacenter} ar mhaithe le cobhsaíocht níos fearr. {fullDocs}.", + "GlobalpingMultipleLocationsError": "Ní thacaítear le hilshuíomhanna, bain úsáid as suíomh amháin do gach monatóir le do thoil.", + "360messengerWayToWriteRecipient": "Cuir isteach uimhir theileafóin amháin nó níos mó i bhformáid idirnáisiúnta gan móide tosaigh (m.sh. {0}). Scar uimhreacha iolracha le camóga.", + "signalUseTemplate": "Úsáid teimpléad teachtaireachta saincheaptha", + "signalUseTemplateDescription": "Má tá sé cumasaithe, seolfar an teachtaireacht ag baint úsáide as teimpléad saincheaptha. Is féidir leat teimpléadú Liquid a úsáid chun formáid an fhógra a shaincheapadh.", + "monitorTypeGameServer": "Freastalaí Cluiche", + "monitorTypeDatabase": "Cineál Monatóra Bunachar Sonraí", + "monitorTypeSpecial": "Speisialta", + "fluxerMessageFormatNormal": "Gnáth (leabaithe saibhre)", + "fluxerMessageFormatMinimalist": "Íostach (stádas gearr)", + "fluxerMessageFormatCustom": "Teimpléad saincheaptha", + "fluxerMessageTemplate": "Teimpléad Teachtaireachta", + "Notification Action": "Gníomh Fógra", + "homeAssistantNotificationActionHelptext": "Is féidir liosta de Ghníomhartha Fógra a fháil i Home Assistant faoi \"Socruithe > Uirlisí Forbróra > Gníomhartha\". Cuardaigh \"fógra\" chun do ghníomhartha a aimsiú. Ná cuir isteach ach an chuid i ndiaidh \"fógra.\", m.sh. don ghníomh \"fógra.mobile_app_xyz\" cuir isteach \"mobile_app_xyz\". Le haghaidh fógraí soghluaiste ionsuite, lorg \"Seol fógra trí mobile_app_xyz\" (ní \"Seol fógra\").", + "fluxerMessageFormat": "Formáid Teachtaireachta", + "fluxerUseMessageTemplate": "Úsáid teimpléad teachtaireachta saincheaptha", + "fluxerUseMessageTemplateDescription": "Má tá sé cumasaithe, seolfar an teachtaireacht ag baint úsáide as teimpléad saincheaptha (LiquidJS). Fág bán é chun an fhormáid réamhshocraithe Uptime Kuma a úsáid.", + "Fluxer Webhook URL": "URL Gréasáin Fluxer", + "wayToGetFluxerURL": "Is féidir leat é seo a fháil trí dhul chuig socruithe an chainéil sprice > Webhooks > Cruthaigh Webhook > Cóipeáil URL Webhook.", + "wayToGetMaxToken": "Is féidir leat comhartha bot MAX agus sonraí eile a fháil ó {0}.", + "maxMessenger": "Teachtaire MAX", + "maxApiUrlDescription": "URL API bunúsach do theachtaire MAX. Réamhshocrú: https://platform-api.max.ru", + "wayToGetMaxChatID": "Sonraigh an t-aitheantóir comhrá i MAX inar cheart teachtaireachtaí a sheachadadh.", + "maxUseTemplate": "Úsáid teimpléad teachtaireachta saincheaptha", + "maxUseTemplateDescription": "Má tá sé cumasaithe, seolfar an teachtaireacht ag baint úsáide as teimpléad saincheaptha.", + "maxTemplateFormatDescription": "Tacaíonn MAX messenger le téacs simplí, Markdown agus formáidiú HTML.", + "API Version": "Leagan API", + "Peer ID": "Aitheantas Piaraí", + "vkApiVersionDescription": "Leagan API VK a úsáidtear le haghaidh iarratais. Fág an réamhshocrú mura bhfuil leagan eile de VK API ag teastáil uait go sonrach ar mhaithe le comhoiriúnacht.", + "vkDontParseLinks": "Díchumasaigh blúiríní nasc", + "vkDontParseLinksDescription": "Mura bhfuil sé cumasaithe, ní ghinfidh VK réamhamhairc/sleachta nasc.", + "vkPeerIdDescription": "Cuir isteach an VK peer_id sprice. Seoltar an luach seo chuig an API mar atá.", + "oracledbConnectionString": "Bunachar Sonraí Oracle: {connectionString}", + "telnyxApiKey": "Eochair API (V2)", + "telnyxApiKeyHelptext": "D'eochair API Telnyx V2 ón Tairseach Telnyx faoi Eochracha API", + "telnyxPhoneNumber": "Ó Uimhir", + "telnyxToNumber": "Chuig Uimhir", + "telnyxToNumberHelptext": "An uimhir theileafóin ceann scríbe chun fógraí SMS a fháil (m.sh. +15559876543)", + "telnyxMessagingProfileId": "Aitheantas Próifíle Teachtaireachtaí (roghnach)", + "telnyxMessagingProfileIdHelptext": "Is féidir Próifíl Teachtaireachtaí Telnyx a cheangal go roghnach chun socruithe agus gnéithe an tseoltóra a rialú", + "evolutionCustomMessageTitle": "Teachtaireacht Saincheaptha (roghnach)", + "evolutionCustomMessageDesc": "Fág folamh chun an teachtaireacht réamhshocraithe a úsáid.", + "telnyxPhoneNumberHelptext": "D'uimhir theileafóin Telnyx le seoladh uaithi (ní mór í a bheith cláraithe i do chuntas Telnyx, m.sh. +15551234567)", + "WebSocket Options": "Roghanna Soicéad Gréasáin", + "pingPacketSizeWarning": "Ní chruthaíonn méid paicéid níos lú ná 16 amanna turas cruinn ar Linux", + "VKTeams Bot Token Description": "Is féidir leat tuilleadh eolais a fháil faoi chomhartha bot a fháil agus sonraí eile ag {0}.", + "VKTeams Base Url Description": "URL API bonn VKTeams, d'fhéadfadh sé a bheith difriúil sa réiteach ar an láthair. Réamhshocrú: https://myteam.mail.ru", + "VKTeams Use Template": "Úsáid teimpléad teachtaireachta saincheaptha", + "VKTeams Use Template Description": "Má tá sé cumasaithe, seolfar an teachtaireacht ag baint úsáide as teimpléad saincheaptha.", + "VKTeams Chat Id Description": "I gcás úsáideoirí, seo a seoladh ríomhphoist. I gcás grúpaí agus cainéil, faigh an ID óna socruithe agus cuir {'@'}chat.agent leis", + "VKTeams Template Format Description": "Chun stíliú teachtaireachtaí, tacaíonn VKTeams le téacs simplí, MarkdownV2, agus formáidiú HTML.", + "smtpAdditionalHeadersTitle": "Ceanntásca Breise", + "smtpAdditionalHeadersDesc": "Féach ar Cheanntásca Saincheaptha Nodemailer {0} le haghaidh úsáide.", + "wayToGetEgoSMSToken": "Is féidir leat d’ainm úsáideora agus do phasfhocal EgoSMS API a fháil ó {here}.", + "egosmsPhoneNumberDescription": "Uimhir an fhaighteora leis an gcód tíre, gan + ná 00 (m.sh. 2567XXXXXXXX).", + "egosmsSenderDescription": "Aitheantas an tseoltóra a thaispeántar do fhaighteoirí (uasmhéid 11 charachtar). Úsáidtear EGOSMS mar réamhshocrú má tá sé folamh.", + "Rybbit": "Rybbit", + "gamedigToken": "Comhartha", + "gamedigTokenDescription": "Roghnach. Éilíonn roinnt freastalaithe cluichí comhartha fíordheimhnithe.", + "Bearer Token": "Comhartha Iompróra", + "Connection Timeout": "Am Teorann Ceangail", + "Flowtriq Webhook URL": "URL Crúca Gréasáin", + "Flowtriq API Key": "Eochair API", + "wxPusherSptDescription": "Seachadann WxPusher Simple Push (SPT) tríd an aip neamhspleách WxPusher thar 6 ardán — Android, iOS, HarmonyOS, Windows, macOS agus Linux ({download}). Scan an cód QR thíos chun do SPT a fháil agus tá tú réidh le seoladh. Deighil il-SPTanna le camóg (,) — féach na {doiciméid} le haghaidh tuilleadh eolais.", + "wxPusherDownloadApp": "íoslódáil an aip", + "wxPusherDocs": "treoir oifigiúil", + "flowtriqApiKeyDescription": "Eochair API roghnach chun iarratais webhook a fhíordheimhniú chuig d'eispéireas Flowtriq.", + "Access Key": "Eochair Rochtana", + "ooredooUsernameDescription": "An seoladh ríomhphoist a úsáideann tú chun síniú isteach i bpainéal mórchóir SMS Ooredoo.", + "ooredooAccessKeyDescription": "Le fáil faoi \"Eochair Rochtana API\" ar do phainéal SMS Ooredoo. Greamaigh é díreach mar a thaispeántar — déantar é a ionchódú go huathoibríoch le base64 sula seoltar é.", + "ooredooBearerTokenDescription": "An comhartha a thaispeántar i ndiaidh \"Údarú: Iompróir\" san ordú samplach cURL ar do phainéal SMS Ooredoo.", + "ooredooRecipientDescription": "Cuir isteach uimhir amháin nó níos mó atá scartha le camóg nó spás. Cuirtear 960 roimh réimír uimhreacha 7 ndigit áitiúla go huathoibríoch.", + "Leave blank to use the default Ooredoo endpoint.": "Fág bán chun críochphointe réamhshocraithe Ooredoo a úsáid.", + "plivoAuthIDHelptext": "D'ID Údaraithe Plivo ón gConsól Plivo", + "plivoAuthID": "Aitheantas Údaraithe", + "plivoAuthToken": "Comhartha Údaraithe", + "plivoAuthTokenHelptext": "Do Chomhartha Údaraithe Plivo ón gConsól Plivo", + "plivoFromNumber": "Ó Uimhir", + "plivoFromNumberHelptext": "An uimhir theileafóin Plivo le seoladh uaithi (m.sh. +15551234567). Féadfaidh SMS aitheantas seolta alfa-uimhriúil ceadaithe a úsáid freisin; teastaíonn uimhir theileafóin Plivo le haghaidh glaonna gutha.", + "plivoToNumber": "Chuig Uimhir", + "plivoToNumberHelptext": "An uimhir theileafóin ceann scríbe a fhaigheann an SMS nó an glao gutha (m.sh. +15559876543)", + "pm2Description": "Seiceálann sé an bhfuil próiseas PM2 {service_name} ar líne", + "PM2 Process": "Próiseas PM2", + "plivoMessageType": "Cineál Teachtaireachta", + "plivoVoiceCall": "Glao Gutha", + "plivoAnswerUrl": "URL an Fhreagra", + "pm2ExpectedStates": "Stádas ionchais: ar líne. Meastar go bhfuil staideanna stoptha agus earráide SÍOS.", + "Refresh": "Athnuachan", + "Select PM2 process": "Roghnaigh próiseas PM2", + "ntpStratumThreshold": "Tairseach Stratum", + "ntpOffsetThreshold": "Tairseach Fritháireamh Ama (ms)", + "ntpOffsetThresholdHelp": "Marcáil mar síos má sháraíonn an fritháireamh ama an luach seo i milleasoicindí.", + "ntpDispersionThreshold": "Tairseach Scaipthe Fréimhe (ms)", + "plivoAnswerUrlHelptext": "URL a thugann Plivo XML ar ais don ghlao. Cuirtear an téacs foláirimh leis mar pharaiméadar fiosrúcháin ?message=, ionas gur féidir le do chríochphointe m.sh. {message} a thabhairt ar ais.", + "ntpThresholdsTitle": "Tairseacha NTP", + "ntpStratumThresholdHelp": "Marcáil mar síos má tá an stratum ag an luach seo nó os a chionn. Léiríonn an stratum cáilíocht foinse ama (1=Clog adamhach/GPS, 16=neamhshioncrónaithe).", + "ntpDispersionThresholdHelp": "Marcáil mar síos má sháraíonn scaipeadh fréimhe an luach seo. Léiríonn scaipeadh cruinneas cloig an fhreastalaí.", + "Hash Key": "Eochair Hais", + "wayToGetBearSMSToken": "Is féidir leat d'ainm úsáideora agus d'eochair haise BearSMS API a fháil ó {0}.", + "bearsmsPhoneNumberDescription": "Uimhir an fhaighteora leis an gcód tíre, gan + ná 00 (m.sh. 9725XXXXXXXX).", + "bearsmsSenderDescription": "Aitheantas seoltóra roghnach a thaispeántar do fhaighteoirí (uasmhéid 11 charachtar). Ní mór é a cheadú i do chuntas BearSMS, nó úsáidfear seoltóir réamhshocraithe an chuntais.", + "Pinglet Topic URL": "URL na topaice", + "pingletTopicUrlDescription": "URL foilsithe an ábhair Pinglet le foláirimh a sheoladh chuige, m.sh. https://app.pinglet.co.uk/your-namespace/alerts. Cruthaítear an topaic ar an gcéad fhoilsiú mura bhfuil sé ann fós.", + "ClickUp API Token": "Comhartha API ClickUp", + "ClickUp API Token Description": "Is féidir leat comhartha rochtana pearsanta a chruthú i ClickUp trí dhul chuig Socruithe -> Comhtháthúcháin & ClickApps -> ClickUp API -> Comhartha API", + "ClickUp Workspace ID": "Aitheantas Spás Oibre ClickUp", + "ClickUp Workspace ID Description": "Is féidir leat é a fháil i URL do spás oibre, díreach i ndiaidh app.clickup.com/, m.sh. app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ID Cainéal ClickUp", + "ClickUp Channel ID Description": "Is féidir leat é a fháil i URL an chainéil Comhrá, díreach i ndiaidh /chatr/, m.sh. .../chatr/abc 123-4567.", + "turbosmtpConsumerKey": "Eochair Tomhaltóra", + "turbosmtpConsumerSecret": "Rún Tomhaltóra", + "openwaSession": "Seisiún", + "openwaChatId": "Aitheantas Comhrá (Uimhir Theileafóin / Aitheantas Teagmhála / Aitheantas Grúpa)", + "wayToGetOpenwaApiUrl": "URL d’Áscáin OpenWA.", + "wayToGetOpenwaApiKey": "Eochair API a úsáidtear do d’eispéireas OpenWA.", + "wayToGetOpenwaSession": "Aitheantas seisiúin a úsáideann OpenWA chun fógraí a sheoladh.", + "wayToWriteOpenwaChatId": "An uimhir theileafóin leis an réimír idirnáisiúnta, ach gan an comhartha móide ag an tús ({0}), an ID Teagmhála ({1}) ná an ID Grúpa ({2}). Is féidir leat seoladh chuig il-IDanna Comhrá trí iad a dheighilt le camóga.", + "openwaCustomMessageTitle": "Teachtaireacht Saincheaptha (roghnach)", + "openwaCustomMessageDesc": "Fág folamh chun an teachtaireacht réamhshocraithe a úsáid.", + "milkyHttpAddress": "Seoladh HTTP Milky (Gan an iarmhír '/api')", + "milkyMessageType": "Cineál Teachtaireachta Milky", + "milkyGroupMessage": "Grúpa", + "milkyPrivateMessage": "Príobháideach", + "milkyUserOrGroupId": "Aitheantas Grúpa/Úsáideora", + "milkySafetyTips": "Ar mhaithe le sábháilteacht, ní mór comhartha rochtana a shocrú", + "smsgatewayUrlHelptext": "Bun-URL do Gheata SMS, gan cosán API.", + "smsgatewayTo": "Uimhir(í) Teileafóin", + "smsgatewayToHelptext": "Liosta uimhreacha gutháin faighteoirí i bhformáid idirnáisiúnta, scartha le camóga (m.sh. +15551234567)", + "Client Key": "Eochair Chliaint", + "Channel": "Cainéal", + "Device ID or Group ID": "Aitheantas Gléis nó Aitheantas Grúpa", + "notifyAppDeviceIdDescription": "An ID Gléas ón aip Notify!, nó ID Grúpa chun gach gléas sa ghrúpa a chur ar an eolas ag an am céanna.", + "notifyAppSetup": "Oscail Fógra! agus faigh d’Aitheantas Gléis agus do chomhartha faoi Shocruithe. Níl aon chuntas ag teastáil. Faigh tuilleadh eolais ag {0}.", + "SFTP Path to Check": "Cosán SFTP le Seiceáil", + "sftpPathHelpText": "Roghnach. Má shonraítear é, fíoróidh an monatóir go bhfuil an cosán seo ann ar an bhfreastalaí SFTP.", + "SSH Private Key": "Eochair Phríobháideach SSH", + "Passphrase": "Pasfrása", + "sshPrivateKeyPlaceholder": "-----TOSAIGH EOCHAIR PHRÍOBHÁIDEACH OPENSSH-----\n...\n-----DEIREADH EOCHAIR PHRÍOBHÁIDEACH OPENSSH-----", + "sshPrivateKeyHelpText": "Greamaigh ábhar do chomhaid eochrach phríobháidigh (e.g. id_rsa nó id_ed25519).", + "sshPassphraseHelpText": "Roghnach. Ní gá ach amháin má tá d’eochair phríobháideach criptithe le frása faire.", + "optional": "roghnach", + "Line Number": "Uimhir Líne", + "Amoot SMS recipient numbers help": "Cuir isteach uimhir shoghluaiste amháin nó níos mó scartha le camóga.", + "Use Pattern": "Úsáid Patrún", + "Pattern Code ID": "Aitheantas Cód Patrúin", + "Amoot SMS pattern help": "Cuir isteach an Cód Patrún ID ó do phainéal SMS Amoot. {0}", + "Create an Amoot SMS pattern": "Cruthaigh patrún SMS Amoot", + "Use Own Line for Pattern": "Úsáid do Líne Féin le haghaidh Patrúin" +} diff --git a/src/lang/gl.json b/src/lang/gl.json new file mode 100644 index 0000000..b8d611b --- /dev/null +++ b/src/lang/gl.json @@ -0,0 +1,39 @@ +{ + "Settings": "Axustes", + "Dashboard": "Panel", + "Help": "Axuda", + "General": "Xeral", + "List": "Lista", + "Home": "Casa", + "Add": "Engadir", + "Up": "Arriba", + "Pending": "Pendente", + "statusMaintenance": "Mantemento", + "Maintenance": "Mantemento", + "Unknown": "Descoñecido", + "Reconnecting...": "Reconectando...", + "pauseDashboardHome": "Pausa", + "Pause": "Pausa", + "Name": "Nome", + "Status": "Estado", + "DateTime": "DataHora", + "Message": "Mensaxe", + "languageName": "Galego", + "Down": "Abaixo", + "mariadbCaCertificateLabel": "Certificado CA", + "mariadbCaCertificateHelptext": "Pega o certificado da CA en formato PEM para usalo con certificados autofirmados. Deixa en branco se a túa base de datos usa un certificado asinado por unha CA pública.", + "setupDatabaseChooseDatabase": "Que base de datos te gustaría usar?", + "setupDatabaseEmbeddedMariaDB": "Non precisas configurar nada. Esta imaxe de Docker ten incorporado e configurado MariaDB para ti automáticamente. Uptime Kuma conectarase a esta base de datos a través dun socket Unix.", + "setupDatabaseMariaDB": "Conectarse a unha base de datos MariaDB externa. Debes configurar a información de conexión á base de datos.", + "setupDatabaseSQLite": "Un arquivo de base de datos sinxelo, recomendado para despregues a pequena escala. Antes da versión 2.0.0, Uptime Kuma usaba SQLite como base de datos por defecto.", + "settingUpDatabaseMSG": "Configurando a base de datos. Pode levar un tempo, sexa paciente.", + "dbName": "Nome da base de datos", + "enableSSL": "Habilitar SSL/TLS", + "New Update": "Nova actualización", + "Appearance": "Aparencia", + "Primary Base URL": "URL base principal", + "Language": "Idioma", + "mariadbUseSSLHelptext": "Habilita o uso de una conexión cifrada a túa base de datos. Requerido para a maioría das bases de datos na nube.", + "oracledbConnectionString": "Base de datos Oracle: {connectionString}", + "Theme": "Tema" +} diff --git a/src/lang/he-IL.json b/src/lang/he-IL.json new file mode 100644 index 0000000..fc79889 --- /dev/null +++ b/src/lang/he-IL.json @@ -0,0 +1,780 @@ +{ + "languageName": "אל תתרגם את languageName ישירות. זה השם של השפה שלך. לדוגמא: אנגלית לאנגלית, 简体中文 לסינית (פשוטה)", + "checkEverySecond": "בדיקה כל {0} שניות", + "retryCheckEverySecond": "ניסיון חוזר כל {0} שניות", + "resendEveryXTimes": "שליחה שוב כל {0} פעמים", + "resendDisabled": "שליחה מחדש מושבתת", + "retriesDescription": "מקסימום ניסיונות חוזרים לפני שהשירות יסומן כלא פעיל ותשלח התראה", + "ignoreTLSError": "התעלם משגיאת TLS/SSL עבור אתרי HTTPS", + "upsideDownModeDescription": "הפוך את יעד הסטטוס. אם ניתן להגיע לשירות, הוא ייחשב כלא פעיל.", + "maxRedirectDescription": "המספר המרבי של הפניות מחדש למעקב. ערך 0 ישבית הפניות מחדש לגמרי.", + "enableGRPCTls": "אפשר לשלוח בקשת gRPC עם חיבור TLS", + "grpcMethodDescription": "שם השיטה מומר לפורמט cammelCase כגון sayHello, check וכו.", + "acceptedStatusCodesDescription": "בחר קודי סטטוס שנחשבים לתגובה מוצלחת.", + "Maintenance": "תחזוקה", + "statusMaintenance": "תחזוקה", + "Schedule maintenance": "תחזוקה מתוכננת", + "Affected Monitors": "מוניטורים מושפעים", + "Pick Affected Monitors...": "בחר המוניטריים המושפעים…", + "Start of maintenance": "תחילת תחזוקה", + "All Status Pages": "כל דפי הסטטוס", + "Select status pages...": "בחר דפי סטטוס…", + "recurringIntervalMessage": "רוץ פעם ביום | הפעל אחת ל-{0} ימים", + "affectedMonitorsDescription": "בחר מוניטורים שמושפעים מהתחזוקה הנוכחית", + "affectedStatusPages": "הצג הודעת תחזוקה זו בדפי סטטוס שנבחרו", + "atLeastOneMonitor": "בחר לפחות מוניטור אחד מושפע", + "passwordNotMatchMsg": "הסיסמאות שהזנתם אינן תואמות.", + "notificationDescription": "יש להקצות התראות למוניטור כדי שהן יעבדו.", + "keywordDescription": "חפש מילת מפתח בתגובת HTML או JSON רגילה. החיפוש תלוי רישיות.", + "pauseDashboardHome": "עצור", + "deleteMonitorMsg": "האם אתה בטוח שברצונך למחוק את המוניטור הזה?", + "deleteMaintenanceMsg": "האם אתה בטוח שברצונך למחוק את התחזוקה הזו?", + "deleteNotificationMsg": "האם אתה בטוח שברצונך למחוק את ההודעה הזו עבור כל מוניטרים?", + "dnsPortDescription": "יציאת שרת DNS. ברירת המחדל היא 53. אתה יכול לשנות את היציאה בכל עת.", + "resolverserverDescription": "Cloudflare הוא שרת ברירת המחדל. אתה יכול לשנות את שרת הפותר בכל עת.", + "rrtypeDescription": "בחר את סוג ה-RR שברצונך לפקח עליו", + "pauseMonitorMsg": "האם אתה בטוח רוצה להשהות?", + "enableDefaultNotificationDescription": "הודעה זו תופעל כברירת מחדל עבור מוניטרים חדשים. אתה עדיין יכול להשבית את ההודעה בנפרד עבור כל מוניטור.", + "clearEventsMsg": "האם אתה בטוח שברצונך למחוק את כל האירועים עבור המוניטור הזה?", + "clearHeartbeatsMsg": "האם אתה בטוח שברצונך למחוק את כל פעימות הלב עבור המוניטור הזה?", + "confirmClearStatisticsMsg": "האם אתה בטוח שברצונך למחוק את כל הנתונים הסטטיסטיים?", + "importHandleDescription": "בחר 'דלג על קיים' אם ברצונך לדלג על כל מוניטור או התראה באותו שם. 'החלף' ימחק כל מוניטור והתראה קיימים.", + "confirmImportMsg": "האם אתה בטוח שברצונך לייבא את הגיבוי? אנא ודא שבחרת באפשרות הייבוא הנכונה.", + "twoFAVerifyLabel": "אנא הזן את האסימון שלך כדי לאמת מערכת אדוש:", + "tokenValidSettingsMsg": "האסימון תקף! כעת אתה יכול לשמור את הגדרות האדוש.", + "confirmEnableTwoFAMsg": "האם אתה בטוח שברצונך להפעיל את מערכת אדוש?", + "confirmDisableTwoFAMsg": "האם אתם בטוחים שאתם רוצים להשבית אימות 2FA (אימות כפול)?", + "Settings": "הגדרות", + "Dashboard": "לוח בקרה", + "New Update": "עדכון חדש", + "Language": "שפה", + "Appearance": "תצוגה", + "Theme": "ערכת נושא", + "General": "כללי", + "Primary Base URL": "כתובת האתר הראשית", + "Version": "גרסה", + "Check Update On GitHub": "בדיקה לעדכונים מ-GitHub", + "List": "רשימה", + "Add": "הוספה", + "Add New Monitor": "הוספת מוניטור חדש", + "Quick Stats": "סטטיסטיקות בקצרה", + "Up": "זמין", + "Down": "לא זמין", + "Pending": "ממתין", + "Unknown": "לא יודע", + "Pause": "עצירה", + "Name": "שם", + "Status": "סטטוס", + "DateTime": "תאריך שעה", + "Message": "הודעה", + "No important events": "אין אירועים חשובים", + "Resume": "הפעלה", + "Edit": "עריכה", + "Delete": "מחיקה", + "Current": "נוכחי", + "Uptime": "זמן פעילות", + "Cert Exp.": "תפוגת תעודה.", + "day": "יום | ימים", + "-day": "-יום", + "hour": "שעה", + "-hour": "-שעה", + "Response": "תגובה", + "Ping": "פינג", + "Monitor Type": "סוג מוניטור", + "Keyword": "מילת מפתח", + "Friendly Name": "שם תצוגה", + "URL": "כתובת אתר", + "Hostname": "שם המארח", + "Port": "פורט", + "Heartbeat Interval": "מרווח פעימות (בין קריאה לקריאה)", + "Retries": "נסיונות חוזרים", + "Heartbeat Retry Interval": "מרווח נסיונות חוזר של פעימות", + "Resend Notification if Down X times consecutively": "שליחת הודעה שוב אם לא פעיל X פעמים ברציפות", + "Advanced": "מתקדם", + "Upside Down Mode": "מצב הפוך", + "Max. Redirects": "מקסימום הפניות מחדש", + "Accepted Status Codes": "קודי סטטוס מאושרים", + "Push URL": "כתובת URL ל-Push", + "needPushEvery": "יש לקרוא לכתובת הזו כל {0} שניות.", + "pushOptionalParams": "פרמטרים אופציונליים: {0}", + "Save": "שמירה", + "Notifications": "התראות", + "Not available, please setup.": "לא זמין, נא להגדיר.", + "Setup Notification": "הגדרת התראה", + "Light": "בהירה", + "Dark": "כהה", + "Auto": "אוטומטית", + "Theme - Heartbeat Bar": "ערכת נושא - Heartbeat Bar", + "Normal": "רגיל", + "Bottom": "בתחתית האתר", + "None": "ללא", + "Timezone": "אזור זמן", + "Search Engine Visibility": "נראות במנועי חיפוש", + "Allow indexing": "לאפשר הוספה לאינדקס", + "Discourage search engines from indexing site": "לעודד מנועי חיפוש שלא לאנדקס את האתר", + "Change Password": "שינוי סיסמה", + "Current Password": "סיסמה נוכחית", + "New Password": "סיסמה חדשה", + "Repeat New Password": "חזרה על הסיסמה חדשה", + "Update Password": "עידכון סיסמה", + "Disable Auth": "השבתת התחברות", + "Enable Auth": "הפעלת התחברות", + "disableauth.message1": "האם אתם בטוחים שברצונכם{disableAuth}?", + "disable authentication": "להשבית את האבטחה", + "disableauth.message2": "אופציה זו מיועדת לתרחישים {intendThirdPartyAuth} מול Uptime Kuma כגון Cloudflare Access, Authelia או מנגנוני אימות אחרים.", + "where you intend to implement third-party authentication": "בהם בכוונתכם ליישם אימות של צד שלישי", + "Please use this option carefully!": "יש להשתמש באפשרות זו בזהירות!", + "Logout": "התנתקות", + "Leave": "יציאה", + "I understand, please disable": "מובן, אני רוצה להשבית", + "Confirm": "אישור", + "Yes": "כן", + "No": "לא", + "Username": "שם משתמש", + "Password": "סיסמה", + "Remember me": "זכור אותי", + "Login": "התחברות", + "No Monitors, please": "בלי מוניטורים, בבקשה", + "add one": "להוסיף אחד", + "Notification Type": "סוג התראה", + "Email": "אימייל", + "Test": "Test", + "Certificate Info": "פרטי תעודת אבטחה", + "Resolver Server": "שרת פותר", + "Resource Record Type": "סוג רשומת משאבים", + "Last Result": "תוצאה אחרונה", + "Create your admin account": "צור את חשבון הניהול שלך", + "Repeat Password": "חזור על הסיסמה", + "Import Backup": "ייבוא גיבוי", + "Export Backup": "ייצוא גיבוי", + "Export": "ייצוא", + "Import": "ייבוא", + "respTime": "רפ. זמן (ms)", + "notAvailableShort": "N/A", + "Default enabled": "ברירת המחדל מופעלת", + "Apply on all existing monitors": "החל על כל המסכים הקיימים", + "Create": "ליצור", + "Clear Data": "נקה נתונים", + "Events": "אירועים", + "Heartbeats": "פעימות לב", + "Auto Get": "קבל אוטומטי", + "backupDescription": "אתה יכול לגבות את כל המסכים וההתראות לקובץ JSON.", + "backupDescription2": "הערה: היסטוריה ונתוני אירועים אינם כלולים.", + "backupDescription3": "נתונים רגישים כגון אסימוני הודעה כלולים בקובץ הייצוא; נא לאחסן יצוא בצורה מאובטחת.", + "alertNoFile": "אנא בחר קובץ לייבוא.", + "alertWrongFileType": "אנא בחר קובץ JSON.", + "Clear all statistics": "נקה את כל הנתונים הסטטיסטיים", + "Skip existing": "דילוג על הקיים", + "Overwrite": "החלף", + "Options": "אפשרויות", + "Keep both": "שמור את שניהם", + "Verify Token": "אמת את האסימון", + "Setup 2FA": "הגדרת מערכת אדוש", + "Enable 2FA": "הפעלת אדוש", + "Disable 2FA": "כיבוי אדוש", + "2FA Settings": "הגדרות אדוש", + "Two Factor Authentication": "אימות דו-שלבי (מערכת אדוש)", + "Active": "מופעל", + "Inactive": "קבוי", + "Token": "אסימון", + "Show URI": "הצג URI", + "Tags": "תגים", + "Add New below or Select...": "הוסף חדש למטה או בחר…", + "Tag with this name already exist.": "תג בשם זה כבר קיים.", + "Tag with this value already exist.": "תג עם ערך זה כבר קיים.", + "color": "צבע", + "value (optional)": "ערך (אופציונלי)", + "Gray": "אפור", + "Red": "אדום", + "Orange": "כתום", + "Green": "ירוק", + "Blue": "כחול", + "Indigo": "כחול כהה", + "Purple": "סגול", + "Pink": "כתום", + "Search...": "לחפש…", + "Avg. Ping": "פינג ממוצע", + "Avg. Response": "ממוצע תגובה", + "Entry Page": "דף כניסה", + "statusPageNothing": "אין כאן שום דבר, בבקשה הוסף קבוצה או מוניטור.", + "No Services": "אין שירותים", + "All Systems Operational": "כל המערכות עובדות", + "Partially Degraded Service": "שירות פגום חלקית", + "Degraded Service": "שירות פגום", + "Add Group": "הוסף קבוצה", + "Add a monitor": "הוסף מוניטור", + "Edit Status Page": "ערוך דף סטטוס", + "Go to Dashboard": "מעבר לפאנל", + "Status Page": "דף סטטוס", + "Status Pages": "דפי סטטוס", + "defaultNotificationName": "התראת {notification} שלי ({number})", + "here": "פה", + "Required": "נדרש", + "telegram": "טלגרם", + "Bot Token": "אסימון בוט", + "wayToGetTelegramToken": "אתה יכול לקבל אסימון מ-{0}.", + "Chat ID": "מזהה צ'אט", + "supportTelegramChatID": "תמיכה בצ'אט ישיר / קבוצה / מזהה הצ'אט של הערוץ", + "wayToGetTelegramChatID": "אתה יכול לקבל את מזהה הצ'אט שלך על ידי שליחת הודעה לבוט ומעבר לכתובת האתר הזו כדי להציג את ה-chat_id:", + "YOUR BOT TOKEN HERE": "אסימון הבוט שלך כאן", + "chatIDNotFound": "מזהה צ'אט לא נמצא; אנא שלח הודעה לבוט זה תחילה", + "webhook": "Webhook", + "Post URL": "כתובת אתר של פוסט", + "Content Type": "סוג התוכן", + "webhookJsonDesc": "{0} מתאים לכל שרתי HTTP מודרניים כגון Express.js", + "webhookFormDataDesc": "{multipart} טוב ל-PHP. יהיה צורך לנתח את ה-JSON באמצעות {decodeFunction}", + "webhookAdditionalHeadersTitle": "כותרות נוספות", + "webhookAdditionalHeadersDesc": "מגדיר כותרות נוספות שנשלחות עם ה-webhook. כל כותרת צריכה להיות מוגדרת כמפתח/ערך JSON.", + "smtp": "אימייל (SMTP)", + "secureOptionNone": "None / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "התעלם משגיאת TLS", + "From Email": "אמייל שולח", + "emailCustomSubject": "נושא מותאם אישית", + "To Email": "למייל", + "smtpCC": "עותק", + "smtpBCC": "עותק מוסתר", + "discord": "דיסקורד", + "Discord Webhook URL": "כתובת אתר של Discord Webhook", + "wayToGetDiscordURL": "אתה יכול לקבל זאת על ידי מעבר להגדרות שרת -> אינטגרציות -> יצירת Webhook", + "Bot Display Name": "שם תצוגה של בוט", + "Prefix Custom Message": "קידומת הודעה מותאמת אישית", + "Hello @everyone is...": "שלום {'@'}כולם…", + "teams": "Microsoft Teams", + "Webhook URL": "כתובת האתר של Webhook", + "wayToGetTeamsURL": "אתה יכול ללמוד כיצד ליצור כתובת אתר ל-webhook {0}.", + "signal": "אוֹת", + "Number": "מספר", + "Recipients": "נמענים", + "needSignalAPI": "אתה צריך שיהיה לך לקוח איתות עם REST API.", + "wayToCheckSignalURL": "אתה יכול לבדוק את כתובת האתר הזו כדי לראות כיצד להגדיר אחת:", + "signalImportant": "חשוב: לא ניתן לערבב קבוצות ומספרים בנמענים!", + "gotify": "Gotify", + "Application Token": "אסימון אפליקציה", + "Server URL": "כתובת האתר של השרת", + "Priority": "עדיפות", + "slack": "Slack", + "Icon Emoji": "אייקון אימוג'י", + "Channel Name": "שם הערוץ", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "מידע נוסף על Webhooks ב: {0}", + "aboutChannelName": "הזן את שם הערוץ בשדה {0} שם ערוץ אם ברצונך לעקוף את ערוץ Webhook. לדוגמה: #ערוץ אחר", + "aboutKumaURL": "אם תשאיר את השדה Uptime Kuma URL ריק, הוא יעבור כברירת מחדל לעמוד Project GitHub.", + "emojiCheatSheet": "גיליון הונאה של אמוג'י: {0}", + "rocket.chat": "Rocket.Chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "Push by Techulus", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (תומך ב-50+ שירותי התראות)", + "GoogleChat": "Google Chat (Google Workspace בלבד)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "מפתח משתמש", + "Device": "התקן", + "Message Title": "כותרת ההודעה", + "Notification Sound": "צליל התראה", + "More info on:": "מידע נוסף על: {0}", + "pushoverDesc1": "לעדיפות חירום (2) יש פסק זמן של 30 שניות ברירת מחדל בין ניסיונות חוזרים, והיא תפוג לאחר שעה.", + "pushoverDesc2": "אם ברצונך לשלוח התראות למכשירים שונים, מלא את שדה התקן.", + "SMS Type": "סוג SMS", + "octopushTypePremium": "פרימיום (מהיר - מומלץ להתראה)", + "octopushTypeLowCost": "עלות נמוכה (איטית - לפעמים חסומה על ידי המפעיל)", + "checkPrice": "בדוק מחירים של {0}:", + "apiCredentials": "אישורי API", + "octopushLegacyHint": "האם אתה משתמש בגרסה הישנה של Octopush (2011-2020) או בגרסה החדשה?", + "Check octopush prices": "בדוק מחירי תמנון {0}.", + "octopushPhoneNumber": "מספר טלפון (בפורמט בינלאומי, למשל: +972501234567) ", + "octopushSMSSender": "שם שולח SMS: 3-11 תווים אלפאנומריים ורווח (a-zA-Z0-9)", + "LunaSea Device ID": "מזהה מכשיר LunaSea", + "Apprise URL": "כתובת URL ל-Apprize", + "Example:": "דוגמה: {0}", + "Read more:": "קרא עוד: {0}", + "Status:": "סטטוס: {0}", + "Read more": "קרא עוד", + "appriseInstalled": "Apprise מותקן.", + "appriseNotInstalled": "Apprise אינו מותקן. {0}", + "Access Token": "אסימון גישה", + "Channel access token": "אסימון גישה לערוץ", + "Line Developers Console": "קונסולת מפתחים", + "lineDevConsoleTo": "קו מפתחי קונסולת - {0}", + "Basic Settings": "הגדרות בסיסיות", + "User ID": "תעודת זהות של משתמש", + "Messaging API": "API להודעות", + "wayToGetLineChannelToken": "תחילה גש ל-{0}, צור ספק וערוץ (Messaging API), לאחר מכן תוכל לקבל את אסימון הגישה לערוץ ומזהה המשתמש מפריטי התפריט שהוזכרו לעיל.", + "Icon URL": "כתובת אתר של סמל", + "aboutIconURL": "אתה יכול לספק קישור לתמונה ב\"כתובת URL של סמל\" כדי לעקוף את תמונת הפרופיל המוגדרת כברירת מחדל. לא ישמש אם Icon Emoji מוגדר.", + "aboutMattermostChannelName": "אתה יכול לעקוף את ערוץ ברירת המחדל שאליו ה-Webhook מפרסם על ידי הזנת שם הערוץ בשדה \"שם ערוץ\". זה צריך להיות מופעל בהגדרות Mattermos Webhook. לדוגמה: #ערוץ אחר", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - זול אך איטי ולעיתים עמוס מדי. מוגבל רק לנמענים פולנים.", + "promosmsTypeFlash": "SMS FLASH - ההודעה תוצג אוטומטית במכשיר הנמען. מוגבל לנמענים פולנים בלבד.", + "promosmsTypeFull": "SMS FULL - שכבת פרימיום של SMS, אתה יכול להשתמש בשם השולח שלך (עליך לרשום את השם תחילה). אמין להתראות.", + "promosmsTypeSpeed": "SMS SPEED - העדיפות הגבוהה ביותר במערכת. מאוד מהיר ואמין אבל יקר (בערך פי שניים ממחיר מלא של SMS).", + "promosmsPhoneNumber": "מספר טלפון (לנמען פולני ניתן לדלג על אזורי חיוג)", + "promosmsSMSSender": "שם שולח SMS: שם רשום מראש או אחת מברירות המחדל: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "כתובת WebHook עבור Feishu", + "matrixHomeserverURL": "כתובת האתר של שרת הבית (עם http(s):// ויציאה אופציונלית)", + "Internal Room Id": "מזהה חדר פנימי", + "matrixDesc1": "אתה יכול למצוא את מזהה החדר הפנימי על ידי עיון בחלק המתקדם של לקוח Matrix שלך בהגדרות החדר. זה צריך להיראות כמו !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "מומלץ מאוד ליצור משתמש חדש ולא להשתמש באסימון הגישה של משתמש מטריקס משלך שכן הוא יאפשר גישה מלאה לחשבון שלך ולכל החדרים שהצטרפת אליהם. במקום זאת, צור משתמש חדש והזמן אותו רק לחדר שבו תרצה לקבל את ההתראה. תוכל לקבל את אסימון הגישה על ידי הפעלת {0}", + "Method": "שיטה", + "Body": "תוכן (Body)", + "Headers": "האדרים (Headers)", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "ההאדרים בבקשה אינם בתחביר JSON תקני: ", + "BodyInvalidFormat": "גוף הבקשה (Body) אינו JSON תקני: ", + "Monitor History": "מעקב אחר היסטוריה", + "clearDataOlderThan": "שמור את נתוני היסטוריית הצג למשך {0} ימים.", + "PasswordsDoNotMatch": "סיסמאות לא תואמות.", + "records": "רשומות", + "One record": "שיא אחד", + "steamApiKeyDescription": "לניטור שרת משחקי Steam יש צורך במפתח Steam Web-API. ניתן לרשום ולקבל את מפתח ה-API כאן: ", + "Current User": "משתמש נוכחי", + "topic": "נושא", + "topicExplanation": "נושא MQTT למעקב", + "successMessage": "הודעת הצלחה", + "successMessageExplanation": "הודעת MQTT שתיחשב כהצלחה", + "recent": "לאחרונה", + "Done": "בוצע", + "Info": "מידע", + "Security": "אבטחה", + "Steam API Key": "מפתח API Steam", + "Shrink Database": "מסד נתונים מכווץ", + "Pick a RR-Type...": "בחר סוג RR…", + "Pick Accepted Status Codes...": "בחר קודי סטטוס מקובלים…", + "Default": "בְּרִירַת מֶחדָל", + "HTTP Options": "אפשרויות HTTP", + "Create Incident": "ליצור אירוע", + "Title": "כותרת", + "Content": "תוֹכֶן", + "Style": "Style", + "info": "מידע", + "warning": "אַזהָרָה", + "danger": "סַכָּנָה", + "error": "שְׁגִיאָה", + "critical": "קריטי", + "primary": "יְסוֹדִי", + "light": "אוֹר", + "dark": "אפל", + "Post": "הודעה", + "Please input title and content": "אנא הזן כותרת ותוכן", + "Created": "נוצר", + "Last Updated": "עודכן לאחרונה", + "Unpin": "ענן חוף", + "Switch to Light Theme": "לעבור לנושא האור", + "Switch to Dark Theme": "לעבור לנושא אפל", + "Show Tags": "הצגת תגיות", + "Hide Tags": "הסתר תגיות", + "Description": "תיאור", + "No monitors available.": "אין צגים זמינים.", + "Add one": "הוסף אחד", + "No Monitors": "אין צגים", + "Untitled Group": "קבוצה ללא כותרת", + "Services": "שירותים", + "Discard": "להשליך", + "Cancel": "לְבַטֵל", + "Powered by": "פועל על", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "שם משתמש ל-API (כולל webapi_prefix)", + "serwersmsAPIPassword": "סיסמת API", + "serwersmsPhoneNumber": "מספר טלפון", + "serwersmsSenderName": "שם שולח SMS (רשום באמצעות פורטל לקוחות)", + "smseagle": "SMSEagle", + "smseagleTo": "מספרי טלפון)", + "smseagleGroup": "שם קבוצת ספר טלפונים", + "smseagleContact": "שם איש קשר בספר הטלפונים", + "smseagleRecipientType": "Rסוג הנמען", + "smseagleRecipient": "נמענים (ים) (יש להפריד בין מרובים לפסיק)", + "smseagleToken": "API Access Token", + "smseagleUrl": "כתובת האתר של מכשיר ה- SMSeagege שלך", + "smseagleEncoding": "שלח כ- Unicode", + "smseaglePriority": "עדיפות הודעה (0-9, ברירת מחדל = 0)", + "stackfield": "סטאקפילד", + "Customize": "התאמה אישית", + "Custom Footer": "כותרת תחתונה מותאמת אישית", + "Custom CSS": "CSS מותאם אישית", + "smtpDkimSettings": "הגדרות DKIM", + "smtpDkimDesc": "אנא עיין ב- NodeMailer DKIM {0} לשימוש.", + "documentation": "ווקיפדיית מדריכים", + "smtpDkimDomain": "שם דומיין", + "smtpDkimKeySelector": "בורר מפתח", + "smtpDkimPrivateKey": "טוראי של פרטיy", + "smtpDkimHashAlgo": "אלגוריתם hash (אופציונלי)", + "smtpDkimheaderFieldNames": "מפתחות כותרת לחתום (אופציונלי)", + "smtpDkimskipFields": "מפתחות כותרת לא לחתום (אופציונלי)", + "wayToGetPagerDutyKey": "אתה יכול להשיג זאת על ידי מעבר לשירות -> ספריית שירות -> (בחר שירות) -> אינטגרציות -> הוסף אינטגרציה.כאן תוכלו לחפש \"אירועים API v2 \".מידע נוסף {0}", + "Integration Key": "מפתח אינטגרציה", + "Integration URL": "URL אינטגרציה", + "Auto resolve or acknowledged": "פיתרון אוטומטי או הודה", + "do nothing": "לעשות כלום", + "auto acknowledged": "Auto הודה", + "auto resolve": "פתרון אוטומטי", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "נקודת קצה של API", + "alertaEnvironment": "סביבה", + "alertaApiKey": "מפתח API", + "alertaAlertState": "מצב התראה", + "alertaRecoverState": "לשחזר מדינה", + "deleteStatusPageMsg": "האם אתה בטוח רוצה למחוק את דף הסטטוס הזה?", + "Proxies": "מתווכים", + "default": "בְּרִירַת מֶחדָל", + "enabled": "מופעל", + "setAsDefault": "נקבע כברירת מחדל", + "deleteProxyMsg": "האם אתה בטוח רוצה למחוק את הפרוקסי הזה לכל המסכים?", + "proxyDescription": "מתווכים חייבים להיות משויכים למוניטור בשביל שיעבדו.", + "enableProxyDescription": "פרוקסי זה לא ישפיע על בקשות צג עד שהוא יופעל.אתה יכול לשלוט באופן זמני להשבית את ה- Proxy מכל המסכים לפי מצב ההפעלה.", + "setAsDefaultProxyDescription": "פרוקסי זה יופעל כברירת מחדל עבור צגים חדשים.אתה עדיין יכול להשבית את ה- Proxy בנפרד עבור כל צג.", + "Certificate Chain": "שרשרת אישורים", + "Valid": "תָקֵף", + "Invalid": "לא חוקי", + "AccessKeyId": "מזהה AccessKey", + "SecretAccessKey": "גישהלמפתחסוד", + "PhoneNumbers": "מספר טלפוןs", + "TemplateCode": "מזהה TemplateCode", + "SignName": "מזהה SignName", + "Sms template must contain parameters: ": "תבנית SMS חייבת להכיל פרמטרים: ", + "Bark Endpoint": "כתובת יעד של Bark", + "Bark Group": "קבוצת Bark", + "Bark Sound": "צליל Bark", + "WebHookUrl": "כתובת ל-WebHookUrl", + "SecretKey": "מפתח סודי", + "For safety, must use secret key": "לבטיחות, חייב להשתמש במפתח סודיy", + "Device Token": "אסימון מכשיר", + "Platform": "פּלַטפוֹרמָה", + "Huawei": "huawei", + "High": "High", + "Retry": "נסה שוב", + "Topic": "נוֹשֵׂא", + "WeCom Bot Key": "מפתח בוט ל-WeCom", + "Setup Proxy": "הגדרת פרוקסי", + "Proxy Protocol": "פרוטוקול פרוקסי", + "Proxy Server": "שרת פרוקסי", + "Proxy server has authentication": "לשרת ה- Proxy יש אימות", + "User": "מִשׁתַמֵשׁ", + "Installed": "מוּתקָן", + "Not installed": "לא מותקן", + "Running": "רץ", + "Not running": "לא רץ", + "Remove Token": "הסר אסימון", + "Start": "הַתחָלָה", + "Stop": "תפסיק", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "הוסף דף סטטוס חדש", + "Slug": "Slug", + "Accept characters:": "קבל תווים:", + "startOrEndWithOnly": "התחל או סוף עם {0} בלבד", + "No consecutive dashes": "אין מקפים רצופים", + "Next": "הַבָּא", + "The slug is already taken. Please choose another slug.": "השבלול כבר נלקח.אנא בחר שבלול נוסף.", + "No Proxy": "אין פרוקסי", + "Authentication": "אבטחה", + "HTTP Basic Auth": "HTTP בסיסי Auth", + "New Status Page": "דף סטטוס חדש", + "Page Not Found": "הדף לא נמצא", + "Reverse Proxy": "פרוקסי הפוך", + "Backup": "גיבוי", + "About": "אודות", + "wayToGetCloudflaredURL": "(הורד את CloudFlared מ- {0})", + "cloudflareWebsite": "אתר CloudFlare", + "Message:": "הוֹדָעָה:", + "Don't know how to get the token? Please read the guide:": "לא יודע איך להשיג את האסימון?אנא קרא את המדריך:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "החיבור הנוכחי עשוי ללכת לאיבוד אם אתה מתחבר כרגע באמצעות מנהרת CloudFlare.האם אתה בטוח רוצה לעצור את זה?הקלד את הסיסמה הנוכחית שלך כדי לאשר אותה.", + "HTTP Headers": "כותרות HTTP", + "Trust Proxy": "אמון בפרוקסי", + "Other Software": "תוכנה אחרת", + "For example: nginx, Apache and Traefik.": "למשל: Nginx, Apache ו- Traefik.", + "Please read": "בבקשה תקרא", + "Subject:": "נושא:", + "Valid To:": "תקף ל:", + "Days Remaining:": "ימים שנותרו:", + "Issuer:": "המנפיק:", + "Fingerprint:": "טביעת אצבע:", + "No status pages": "אין דפי סטטוס", + "Domain Name Expiry Notification": "הודעה על תום שם תחום", + "Proxy": "פרוקסי", + "Date Created": "תאריך יצירה", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "כתובת HTTP של OneBot", + "onebotMessageType": "סוג ההודעה OneBot", + "onebotGroupMessage": "קְבוּצָה", + "onebotPrivateMessage": "פְּרָטִי", + "onebotUserOrGroupId": "מזהה קבוצה/משתמש", + "onebotSafetyTips": "לבטיחות, חובה לקבוע טוקן גישה", + "PushDeer Key": "מפתח PushDeer", + "Footer Text": "טקסט לתחתית האתר (פוטר)", + "Show Powered By": "הצגת \"מופעל ע\"י\"", + "Domain Names": "שמות דומיין", + "signedInDisp": "חתום כ- {0}", + "signedInDispDisabled": "Auth מושבת.", + "RadiusSecret": "רדיוס סוד", + "RadiusSecretDescription": "סוד משותף בין לקוח לשרת", + "RadiusCalledStationId": "נקרא מזהה תחנה", + "RadiusCalledStationIdDescription": "מזהה המכשיר שנקרא", + "RadiusCallingStationId": "מזהה תחנת הקריאה", + "RadiusCallingStationIdDescription": "מזהה של מכשיר הנקרא", + "Certificate Expiry Notification": "הודעת תפוגה של אישור", + "API Username": "שם משתמש API", + "API Key": "מפתח API", + "Recipient Number": "מספר הנמען", + "From Name/Number": "משם/מספר", + "Leave blank to use a shared sender number.": "השאר ריק כדי להשתמש במספר שולח משותף.", + "Octopush API Version": "גרסת API של תמנון", + "Legacy Octopush-DM": "שיטת DM (מיושנת)", + "endpoint": "נקודת קצה", + "octopushAPIKey": "\"מפתח API \" מתוך תעודות API של HTTP בלוח הבקרה", + "octopushLogin": "\"כניסה \" מתעודות API של HTTP בלוח הבקרה", + "promosmsLogin": "שם כניסה של API", + "promosmsPassword": "סיסמת API", + "pushoversounds pushover": "Pushover (ברירת מחדל)", + "pushoversounds bike": "אופניים", + "pushoversounds bugle": "חֲצוֹצְרָה", + "pushoversounds cashregister": "קופה רושמת", + "pushoversounds classical": "קלַאסִי", + "pushoversounds cosmic": "קוֹסמִי", + "pushoversounds falling": "נופל", + "pushoversounds gamelan": "gamelan", + "pushoversounds incoming": "נִכנָס", + "pushoversounds intermission": "הפוגה", + "pushoversounds magic": "קֶסֶם", + "pushoversounds mechanical": "מֵכָנִי", + "pushoversounds pianobar": "בר פסנתר", + "pushoversounds siren": "סִירֶנָה", + "pushoversounds spacealarm": "אזעקת חלל", + "pushoversounds tugboat": "סירת משיכה", + "pushoversounds alien": "אזעקת חייזרים (ארוכה)", + "pushoversounds climb": "לטפס (ארוך)", + "pushoversounds persistent": "מתמיד (ארוך)", + "pushoversounds echo": "הד Pushover (ארוך)", + "pushoversounds updown": "למעלה (ארוך)", + "pushoversounds vibrate": "לרטוט בלבד", + "pushoversounds none": "אף אחד (שקט)", + "pushyAPIKey": "מפתח API סודי", + "pushyToken": "אסימון מכשיר", + "Show update if available": "הצג עדכון אם זמין", + "Also check beta release": "בדוק גם את שחרור הבטא", + "Using a Reverse Proxy?": "באמצעות פרוקסי הפוך?", + "Check how to config it for WebSocket": "בדוק כיצד להגדיר אותו ל- WebSocket", + "Steam Game Server": "שרת משחק קיטור", + "Most likely causes:": "ככל הנראה גורם:", + "The resource is no longer available.": "המשאב כבר לא זמין.", + "There might be a typing error in the address.": "יתכן שיש שגיאת הקלדה בכתובת.", + "What you can try:": "מה שאתה יכול לנסות:", + "Retype the address.": "הקלד מחדש את הכתובת.", + "Go back to the previous page.": "חזור לדף הקודם.", + "Coming Soon": "בקרוב", + "wayToGetClickSendSMSToken": "אתה יכול לקבל שם משתמש API ומפתח API מ- {0}.", + "Connection String": "מחרוזת חיבור", + "Query": "שאילתא", + "settingsCertificateExpiry": "תפוגת תעודת TLS", + "certificationExpiryDescription": "HTTPS עוקב אחר התראה על התראה כאשר תעודת TLS פגה ב:", + "Setup Docker Host": "הגדרת מארח Docker", + "Connection Type": "סוג חיבור", + "Docker Daemon": "שירות Docker", + "deleteDockerHostMsg": "האם אתה בטוח רוצה למחוק את המארח של Docker לכל המוניטורים?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "מיכל Docker", + "Container Name / ID": "שם מכולה / מזהה", + "Docker Host": "מארח דוקר", + "Docker Hosts": "מארחי Docker", + "ntfy Topic": "נושא ntfy", + "Domain": "תְחוּם", + "Workstation": "עמדת עבודה", + "disableCloudflaredNoAuthMsg": "אתה לא נמצא במצב AUTH, אין צורך בסיסמה.", + "trustProxyDescription": "סמוך על הכותרות 'X-Forwarded-*'. אם אתה רוצה לקבל את כתובת ה-IP של הלקוח וה- Uptime Kuma שלך עומד מאחורי פרוקסי כגון Nginx או Apache, עליך להפעיל זאת.", + "wayToGetLineNotifyToken": "אתה יכול לקבל אסימון גישה מ- {0}", + "Examples": "דוגמאות", + "Home Assistant URL": "כתובת URL עוזרת ביתית", + "Long-Lived Access Token": "אסימון גישה ארוכת שנים", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "ניתן ליצור אסימון גישה לאורך זמן על ידי לחיצה על שם הפרופיל שלך (שמאל למטה) וגלילה לתחתית ואז לחץ על צור אסימון. ", + "Notification Service": "Notification Service", + "default: notify all devices": "ברירת מחדל: התראה לכלל המכשירים", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "רשימה של שירותי הודעה ניתן למצוא בעוזר הבית תחת \"כלי מפתחים> שירותים \" חפש \"הודעה \" כדי למצוא את שם המכשיר/טלפון שלך.", + "Automations can optionally be triggered in Home Assistant:": "אוטומציות יכולות להיות מופעלות באופן אופציונלי לעוזר הבית:", + "Trigger type:": "סוג ההדק:", + "Event type:": "סוג אירוע:", + "Event data:": "נתוני אירועים:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "ואז בחר פעולה, למשל העבר את הסצינה למקום בו אור RGB הוא אדום.", + "Frontend Version": "גרסת Frontend", + "Frontend Version do not match backend version!": "גרסת Frontend לא תואמת את גרסת Backend!", + "Base URL": "כתובת בסיס", + "goAlertInfo": "SAETRERT הוא יישום קוד פתוח לתזמון שיחה, הסלמות והודעות אוטומטיות (כמו SMS או שיחות קוליות).לעסוק אוטומטית את האדם הנכון, בדרך הנכונה ובזמן הנכון!{0}", + "goAlertIntegrationKeyInfo": "קבל מפתח אינטגרציה של API גנרי לשירות בפורמט זה \"AAAAAAAA-BBB-CCCC-DDDD-EEEEEEEEEEE \" בדרך כלל הערך של פרמטר האסימון של URL שהועתק.", + "goAlert": "GoAlert", + "backupOutdatedWarning": "הוצא משימוש: מכיוון שנוספו הרבה תכונות ותכונת הגיבוי הזו קצת לא מתוחזקת, היא לא יכולה ליצור או לשחזר גיבוי מלא.", + "backupRecommend": "אנא גבה את עוצמת הקול או את תיקיית הנתונים (./data/) ישירות במקום.", + "Optional": "אופציונאלי", + "squadcast": "Squadcast", + "SendKey": "מזהה SendKey", + "SMSManager API Docs": "מסמכי API של SmsManager ", + "Gateway Type": "סוג שער", + "SMSManager": "SMSManager", + "You can divide numbers with": "אתה יכול לחלק מספרים עם", + "or": "אוֹ", + "recurringInterval": "הפסקה", + "Recurring": "מחזורי", + "strategyManual": "פעיל/לא פעיל באופן ידני", + "warningTimezone": "זה משתמש באזור הזמן של השרת", + "weekdayShortMon": "שני", + "weekdayShortTue": "שלישי", + "weekdayShortWed": "רביעי", + "weekdayShortThu": "חמישי", + "weekdayShortFri": "שישי", + "weekdayShortSat": "שבת", + "weekdayShortSun": "ראשון", + "dayOfWeek": "יום בשבוע", + "dayOfMonth": "יום בחודש", + "lastDay": "יום אחרון", + "lastDay1": "היום האחרון של החודש", + "lastDay2": "יום שני האחרון של החודש", + "lastDay3": "יום 3 האחרון של החודש", + "lastDay4": "היום הרביעי האחרון בחודש", + "No Maintenance": "אין תחזוקה", + "pauseMaintenanceMsg": "האם אתה בטוח רוצה להשהות?", + "maintenanceStatus-under-maintenance": "מתבצעות עבודות תחזוקה", + "maintenanceStatus-inactive": "לא פעיל", + "maintenanceStatus-scheduled": "מתוזמן", + "maintenanceStatus-ended": "הסתיים", + "maintenanceStatus-unknown": "לא ידוע", + "Display Timezone": "הצג אזור זמן", + "Server Timezone": "אזור זמן של שרת", + "statusPageMaintenanceEndDate": "סוך", + "IconUrl": "קישור לתמונת אייקון", + "Enable DNS Cache": "(הוצא משימוש) הפעל מטמון DNS עבור צגי HTTP(ים).", + "Enable": "הפעל", + "Disable": "השבת", + "dnsCacheDescription": "ייתכן שהוא לא עובד בסביבות IPv6 מסוימות, השבת אותו אם אתה נתקל בבעיות כלשהן.", + "Single Maintenance Window": "חלון תחזוקה בודד", + "Maintenance Time Window of a Day": "חלון זמן תחזוקה ביום", + "Effective Date Range": "טווח תאריכים אפקטיבי", + "Schedule Maintenance": "לוח זמנים לתחזוקה", + "Date and Time": "תאריך ושעה", + "DateTime Range": "טווח תאריכים וזמן", + "Strategy": "אסטרטגיה", + "Free Mobile User Identifier": "מזהה משתמש נייד בחינם", + "Free Mobile API Key": "מפתח API חינם לנייד", + "Enable TLS": "אפשר TLS", + "Proto Service Name": "שם שירות פרוטו", + "Proto Method": "שיטת פרוטו", + "Proto Content": "תוכן פרוטו", + "Economy": "חיסכון", + "Lowcost": "זול", + "high": "גבוהה", + "General Monitor Type": "סוג מוניטור כללי", + "Passive Monitor Type": "סוג מוניטור פסיבי", + "Specific Monitor Type": "סוג מוניטור ספציפי", + "Custom Monitor Type": "סוג צג מותאם אישית", + "Monitor": "מוניטור | מוניטרים", + "promosmsAllowLongSMS": "אפשר SMS ארוך", + "loadingError": "לא ניתן לאחזר את הנתונים, אנא נסה שוב מאוחר יותר.", + "plugin": "תוסף | תוספים", + "install": "להתקין", + "installing": "מתקין", + "uninstall": "הסר את ההתקנה", + "uninstalling": "מסיר התקנה", + "confirmUninstallPlugin": "האם אתה בטוח שברצונך להסיר את התוסף הזה?", + "Help": "עזרה", + "Game": "משחק", + "Packet Size": "גודל חבילה", + "markdownSupported": "תחביר Markdown נתמך", + "Custom": "מותאם אישית", + "ZohoCliq": "זוהו קליק", + "wayToGetZohoCliqURL": "אתה יכול ללמוד כיצד ליצור כתובת אתר ל-webhook {0}.", + "dataRetentionTimeError": "תקופת השמירה חייבת להיות 0 או יותר", + "infiniteRetention": "הגדר ל-0 לשמירה אינסופית.", + "confirmDeleteTagMsg": "האם אתה בטוח שברצונך למחוק תג זה? צגים המשויכים לתג זה לא יימחקו.", + "Kook": "קוק", + "wayToGetKookBotToken": "צור יישום וקבל את אסימון הבוט שלך ב-{0}", + "wayToGetKookGuildID": "הפעל את 'מצב מפתח' בהגדרת קוק, ולחץ לחיצה ימנית על הגילדה כדי לקבל את המזהה שלה", + "Guild ID": "מזהה גילד", + "Body Encoding": "קידוד Body", + "API Keys": "מפתחות API", + "Expiry": "תפוגה", + "Don't expire": "תוקף שלא פג", + "Continue": "להמשיך", + "Add Another": "להוסיף עוד", + "Key Added": "המפתח נוסף", + "Add API Key": "הוספת מפתח API", + "No API Keys": "אין מפתחות API", + "apiKey-active": "פעיל", + "apiKey-expired": "פג תוקף", + "apiKey-inactive": "לא פעיל", + "Expires": "תוקף", + "deleteAPIKeyMsg": "האם אתם בטוחים שאתם רוצים למחוק מפתח API זה?", + "Generate": "ייצור", + "telegramMessageThreadID": "מזהה ID לאשכול (אופציונאלי)", + "telegramMessageThreadIDDescription": "מזהה ייחודי אופציונאלי להזנת ההודעה לאשכול (הנושא) המתאים. נועד לסופר-קבוצות בלבד", + "telegramProtectContentDescription": "אם מופעל , הודעות הבוט ב-Telegram יהיו מוגנים מהעברה ושמירה שלהם.", + "Clone Monitor": "שכפול של Monitor", + "Expiry date": "תאריך תפוגה", + "apiKeyAddedMsg": "מפתח ה-API שלך נוסף. בבקשה רשמו אותו עבורכם כיוון שהוא לא יופיע שנית.", + "disableAPIKeyMsg": "האם אתם בטוחים שאתם רוצים להשבית מפתח API זה?", + "Clone": "שכפול", + "cloneOf": "שכפול של {0}", + "Google Analytics ID": "מזהה ID של Google Analytics", + "telegramProtectContent": "הגנת העברה \\ שמירה", + "notificationRegional": "לפי איזור", + "Server Address": "כתובת השרת", + "Edit Tag": "עריכת תגית", + "Learn More": "לקריאה נוספת", + "telegramSendSilently": "שליחה שקטה", + "telegramSendSilentlyDescription": "שליחת הודעות שקטה. משתמשים יקבלו ההתראה ללא צליל.", + "Add New Tag": "הוסף תג חדש", + "Home": "ראשי", + "sameAsServerTimezone": "אותו איזור זמן כמו השרת", + "cronSchedule": "לו\"ז: ", + "twilioToNumber": "למספר", + "startDateTime": "תאריך\\זמן התחלה", + "pagertreeSilent": "שקט", + "Reconnecting...": "מתחבר מחדש...", + "statusPageRefreshIn": "רענון תוך: {0}", + "Edit Maintenance": "ערוך תחזוקה", + "pagertreeUrgency": "דחיפות", + "pagertreeLow": "נמוכה", + "pagertreeMedium": "בינונית", + "pagertreeHigh": "גבוהה", + "pagertreeCritical": "קריטי", + "pagertreeResolve": "הגדרה אוטומטית", + "ntfyUsernameAndPassword": "שם משתמש וסיסמא", + "Cannot connect to the socket server": "לא ניתן להתחבר לשקע השרת", + "pushOthers": "אחרים", + "styleElapsedTime": "הזמן שחלף מתחת לסרגל פעימות הלב", + "Select": "בחר", + "Check/Uncheck": "סמן/בטל סימון", + "tailscalePingWarning": "על מנת להשתמש בצג Tailscale Ping, עליך להתקין את Uptime Kuma ללא Docker וגם להתקין את לקוח Tailscale בשרת שלך.", + "pushViewCode": "כיצד להשתמש ב-Push Monitor? (הצג קוד)", + "Reset Token": "אפס את האסימון", + "noDockerHostMsg": "לא זמין. תחילה הגדר מארח Docker.", + "DockerHostRequired": "אנא הגדר את Docker Host עבור צג זה.", + "endDateTime": "תאריך / זמן סיום", + "setupDatabaseChooseDatabase": "באיזה מסד נתונים אתה רוצה להשתמש?", + "setupDatabaseEmbeddedMariaDB": "אתה לא צריך להגדיר כלום. תמונת docker זו הטמיעה והגדירה עבורך MariaDB באופן אוטומטי. Uptime Kuma יתחבר למסד נתונים זה באמצעות שקע יוניקס.", + "setupDatabaseMariaDB": "התחבר למסד נתונים חיצוני של MariaDB. עליך להגדיר את פרטי חיבור מסד הנתונים.", + "setupDatabaseSQLite": "קובץ מסד נתונים פשוט, מומלץ לפריסות בקנה מידה קטן. לפני v2.0.0, Uptime Kuma השתמש ב-SQLite כמסד הנתונים המוגדר כברירת מחדל.", + "dbName": "שם בסיס הנתונים", + "programmingLanguages": "שפות תכנות", + "styleElapsedTimeShowNoLine": "הצג (ללא קו)", + "styleElapsedTimeShowWithLine": "הצג (עם קו)", + "liquidIntroduction": "יכולת התבניות מושגת באמצעות שפת התבניות Liquid. עיין ב-{0} להוראות שימוש. אלו הם המשתנים הזמינים:", + "templateMsg": "הודעת ההודעה", + "templateHeartbeatJSON": "אובייקט המתאר את פעימות הלב", + "templateMonitorJSON": "אובייקט המתאר את הצג", + "templateLimitedToUpDownCertNotifications": "זמין רק עבור הודעות מעלה/מטה/תפוגה של אישור", + "templateLimitedToUpDownNotifications": "זמין רק להתראות למעלה/מטה", + "webhookBodyPresetOption": "תבנית - {0}", + "webhookBodyCustomOption": "גוף מותאם אישית", + "selectedMonitorCount": "נבחר: {0}", + "filterActivePaused": "הופסק", + "invalidCronExpression": "ביטוי קרון לא חוקי: {0}", + "Request Timeout": "בקש פסק זמן", + "timeoutAfter": "פסק זמן לאחר {0} שניות", + "Invert Keyword": "הפוך מילת מפתח", + "filterActive": "פעיל", + "Expected Value": "ערך צפוי", + "Json Query": "שאילתת Json" +} diff --git a/src/lang/he.json b/src/lang/he.json new file mode 100644 index 0000000..5687158 --- /dev/null +++ b/src/lang/he.json @@ -0,0 +1,5 @@ +{ + "Help": "עזרה", + "languageName": "עברית", + "setupDatabaseChooseDatabase": "באיזה מסד נתונים אתה רוצה להשתמש?" +} diff --git a/src/lang/hi.json b/src/lang/hi.json new file mode 100644 index 0000000..68d983a --- /dev/null +++ b/src/lang/hi.json @@ -0,0 +1,131 @@ +{ + "Dashboard": "डैशबोर्ड", + "Help": "मदद", + "New Update": "नया अपडेट", + "Language": "भाषा", + "Appearance": "अपीयरेंस", + "Theme": "थीम", + "Game": "गेम", + "languageName": "हिंदी", + "Settings": "सेटिंग्स", + "General": "जनरल", + "List": "सूची", + "Add": "जोड़ें", + "Add New Monitor": "नया मॉनिटर जोड़ें", + "Pending": "लंबित", + "statusMaintenance": "रखरखाव", + "Maintenance": "रखरखाव", + "Unknown": "अज्ञात", + "Cannot connect to the socket server": "सॉकेट सर्वर से कनेक्ट नहीं हो सकता", + "pauseDashboardHome": "विराम", + "Resume": "फिर से शुरू करें", + "Delete": "हटाएं", + "Current": "मौजूदा", + "Up": "चालू", + "General Monitor Type": "सामान्य मॉनिटर प्रकार", + "Specific Monitor Type": "विशिष्ट मॉनिटर प्रकार", + "Pause": "विराम", + "Name": "नाम", + "Message": "संदेश", + "No important events": "कोई महत्वपूर्ण घटनाक्रम नहीं", + "Edit": "परिवर्तन", + "Ping": "पिंग", + "Monitor Type": "मॉनिटर प्रकार", + "Keyword": "कीवर्ड", + "Friendly Name": "दोस्ताना नाम", + "Version": "संस्करण", + "Home": "घर", + "Quick Stats": "शीघ्र आँकड़े", + "Reconnecting...": "पुनः कनेक्ट किया जा रहा है...", + "Down": "बंद", + "Passive Monitor Type": "निष्क्रिय मॉनिटर प्रकार", + "Status": "स्थिति", + "showCertificateExpiry": "प्रमाणपत्र समाप्ति दिखाएँ", + "setupDatabaseEmbeddedMariaDB": "आपको कुछ भी सेट करने की जरूरत नहीं है. इस डॉकर छवि ने आपके लिए मारियाडीबी को स्वचालित रूप से एम्बेड और कॉन्फ़िगर किया है। अपटाइम कुमा यूनिक्स सॉकेट के माध्यम से इस डेटाबेस से कनेक्ट होगा।", + "setupDatabaseChooseDatabase": "आप कौन सा डेटाबेस प्रयोग करना चाहते हैं ?", + "setupDatabaseMariaDB": "किसी बाहरी मारियाडीबी डेटाबेस से कनेक्ट करें। आपको डेटाबेस कनेक्शन जानकारी सेट करने की आवश्यकता है।", + "setupDatabaseSQLite": "एक सरल डेटाबेस फ़ाइल, जो छोटे पैमाने पर तैनाती के लिए अनुशंसित है। V2.0.0 से पहले, अपटाइम कुमा ने डिफ़ॉल्ट डेटाबेस के रूप में SQLite का उपयोग किया था।", + "settingUpDatabaseMSG": "डेटाबेस की स्थापना. इसमें थोड़ा समय लग सकता है, कृपया धैर्य रखें।", + "markdownSupported": "मार्कडाउन सिंटैक्स समर्थित", + "time ago": "समय पहले", + "day": "दिन", + "Primary Base URL": "प्राथमिक आधार यूआरएल", + "-hour": "-घंटा", + "-day": "-दिन", + "hour": "घंटे", + "DateTime": "दिनांक समय", + "Uptime": "उपरिकाल", + "Cert Exp.": "प्रमाणपत्र अनुभव.", + "dbName": "डेटाबेस का नाम", + "now": "अभी", + "-year": "-वर्ष", + "Not available, please setup.": "उपलब्ध नहीं है, कृपया सेट अप करें।", + "Auto": "स्वतः", + "styleElapsedTime": "हार्टबीट बार के नीचे बीता हुआ समय", + "Setup Notification": "सूचना सेट अप करें", + "Light": "प्रकाश", + "Dark": "अंधकार", + "Theme - Heartbeat Bar": "थीम - हार्टबीट बार", + "Host URL": "होस्ट यूआरएल", + "Request Timeout": "अनुरोध समय सीमा", + "Accepted Status Codes": "स्वीकृत स्थिति कोड", + "Response": "प्रतिक्रिया", + "locally configured mail transfer agent": "स्थानीय रूप से कॉन्फ़िगर किया गया मेल ट्रांसफर एजेंट", + "Heartbeat Interval": "हार्टबीट अंतराल", + "Retries": "पुनः प्रयासों की संख्या", + "Heartbeat Retry Interval": "हार्टबीट पुनः प्रयास अंतराल", + "Port": "पोर्ट", + "checkEverySecond": "हर {0} सेकंड में जांच करें", + "retryCheckEverySecond": "हर {0} सेकंड में पुनः प्रयास करें", + "ignoreTLSErrorGeneral": "कनेक्शन के लिए TLS/SSL त्रुटि को अनदेखा करें", + "needPushEvery": "आपको हर {0} सेकंड में इस URL को कॉल करना चाहिए।", + "pushOptionalParams": "वैकल्पिक पैरामीटर: {0}", + "Save": "सहेजें", + "retriesDescription": "सेवा को डाउन के रूप में चिह्नित करने और सूचना भेजने से पहले अधिकतम पुनः प्रयासों की संख्या", + "Notifications": "सूचनाएँ", + "Check Update On GitHub": "गिटहब पर अपडेट जांचें", + "timeoutAfter": "{0} सेकंड के बाद समय सीमा समाप्त", + "upsideDownModeDescription": "स्थिति को उल्टा करें। यदि सेवा पहुंच योग्य है, तो यह DOWN है।", + "Upside Down Mode": "उल्टा मोड", + "Max. Redirects": "अधिकतम पुनर्निर्देश", + "Push URL": "पुश यूआरएल", + "Monitor": "मॉनिटर | मॉनिटर्स", + "maxRedirectDescription": "अनुसरण करने के लिए अधिकतम पुनर्निर्देशनों की संख्या। पुनर्निर्देशनों को अक्षम करने के लिए 0 पर सेट करें।", + "Advanced": "उन्नत", + "Invert Keyword": "कीवर्ड उलटें", + "Json Query Expression": "JSON क्वेरी अभिव्यक्ति", + "URL": "यूआरएल", + "Hostname": "होस्टनेम", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "या तो उस सर्वर का होस्टनेम दर्ज करें जिससे आप कनेक्ट करना चाहते हैं या {localhost} यदि आप {local_mta} का उपयोग करना चाहते हैं", + "Resend Notification if Down X times consecutively": "यदि लगातार X बार डाउन हो, तो पुनः सूचना भेजें", + "resendEveryXTimes": "हर {0} बार में पुनः भेजें", + "resendDisabled": "पुनः भेजना निष्क्रिय किया गया है", + "ignoredTLSError": "TLS/SSL त्रुटियों को अनदेखा किया गया है", + "pushOthers": "अन्य", + "programmingLanguages": "प्रोग्रामिंग भाषाएँ", + "styleElapsedTimeShowNoLine": "दिखाएँ (कोई रेखा नहीं)", + "Expected Value": "अपेक्षित मान", + "ignoreTLSError": "HTTPS वेबसाइटों के लिए TLS/SSL त्रुटियों को अनदेखा करें", + "pushViewCode": "पुश मॉनिटर का उपयोग कैसे करें? (कोड देखें)", + "Timezone": "समय क्षेत्र", + "Bottom": "नीचे", + "Current Password": "वर्तमान पासवर्ड", + "Repeat New Password": "नया पासवर्ड दोहराएँ", + "None": "कुछ नहीं", + "New Password": "नया पासवर्ड", + "Update Password": "पासवर्ड अपडेट करें", + "Change Password": "पासवर्ड बदलें", + "Normal": "सामान्य", + "enableSSL": "SSL/TLS सक्रिय करें", + "mariadbUseSSLHelptext": "अपने डेटाबेस से एन्क्रिप्टेड कनेक्शन का उपयोग करने के लिए इसे चालू करें। अधिकांश क्लाउड डेटाबेस के लिए ज़रूरी है।", + "mariadbCaCertificateLabel": "CA सर्टिफिकेट", + "No incidents recorded": "कोई घटना दर्ज नहीं की गई", + "unknownDays": "अनजाने दिन", + "mariadbCaCertificateHelptext": "CA सर्टिफिकेट को PEM format में पेस्ट करें, ताकि स्व-हस्ताक्षरित सर्टिफिकेट के साथ इस्तेमाल किया जा सके। अगर आपका डेटाबेस पब्लिक CA द्वारा हस्ताक्षरित सर्टिफिकेट का उपयोग करता है तो खाली छोड़ दें।", + "versionIs": "संस्करण: {version}", + "monitorTypeGameServer": "गेम सर्वर", + "monitorTypeSpecial": "विशेष", + "Load More": "और दिखायें", + "monitorTypeDatabase": "डेटाबेस निगरानी प्रकार", + "oracledbConnectionString": "Oracle डेटाबेस: {connectionString}" +} diff --git a/src/lang/hr-HR.json b/src/lang/hr-HR.json new file mode 100644 index 0000000..033cc34 --- /dev/null +++ b/src/lang/hr-HR.json @@ -0,0 +1,1625 @@ +{ + "languageName": "Hrvatski", + "checkEverySecond": "Provjera svake {0} sekunde", + "retryCheckEverySecond": "Ponovni pokušaj svake {0} sekunde", + "retriesDescription": "Broj ponovnih pokušaja prije nego će se servis označiti kao nedostupan te poslati obavijest", + "ignoreTLSError": "Ignoriraj TLS/SSL pogreške za HTTPS web stranice", + "upsideDownModeDescription": "Preokreni logiku statusa. Ako se primi pozitivan odgovor, smatra se da je usluga nedostupna.", + "maxRedirectDescription": "Maksimalan broj preusmjeravanja. Postaviti na 0 kako bi se preusmjeravanja onemogućila.", + "acceptedStatusCodesDescription": "Odaberite statusne kodove koji se smatraju uspješnim odgovorom.", + "passwordNotMatchMsg": "Lozinke se ne poklapaju.", + "notificationDescription": "Obavijesti će funkcionirati samo ako su dodijeljene monitoru.", + "keywordDescription": "Ključna riječ za pretragu, u obliku običnog HTML-a ili u JSON formatu. Pretraga je osjetljiva na velika i mala slova.", + "deleteMonitorMsg": "Jeste li sigurni da želite izbrisati monitor?", + "deleteNotificationMsg": "Jeste li sigurni da želite izbrisati ovu obavijest za sve monitore?", + "resolverserverDescription": "Cloudflare je zadani DNS poslužitelj. Možete zadati više poslužitelja kao popis IP adresa odvojenih zarezom.", + "rrtypeDescription": "Odaberite vrstu DNS zapisa o resursu kojeg želite pratiti", + "pauseMonitorMsg": "Jeste li sigurni da želite pauzirati?", + "enableDefaultNotificationDescription": "Ova će obavijesti biti omogućena za sve nove monitore. Možete ju ručno onemogućiti za pojedini monitor.", + "clearEventsMsg": "Jeste li sigurni da želite izbrisati sve zapise o događajima za ovaj monitor? Ovo će trajno ukloniti podatke o porukama i ne može se poništiti.", + "clearHeartbeatsMsg": "Jeste li sigurni da želite izbrisati sve zapise o provjerama za ovaj monitor? Ovo će trajno ukloniti podatke o provjerama i ne može se poništiti.", + "confirmClearStatisticsMsg": "Jeste li sigurni da želite izbrisati SVE statistike?", + "importHandleDescription": "Odaberite opciju \"Preskoči postojeće\" ako želite preskočiti uvoz postojećih monitora i obavijesti ako dođe do poklapanja u imenu. Opcija \"Prepiši\" će izbrisati postojeće monitore i obavijesti.", + "confirmImportMsg": "Jeste li sigurni da želite pokrenuti uvoz? Provjerite jeste li odabrali ispravnu opciju uvoza.", + "twoFAVerifyLabel": "Unesite svoj 2FA token:", + "tokenValidSettingsMsg": "Token je važeći! Sada možete spremiti postavke dvofaktorske autentikacije.", + "confirmEnableTwoFAMsg": "Želite li omogućiti dvofaktorsku autentikaciju?", + "confirmDisableTwoFAMsg": "Jeste li sigurni da želite onemogućiti dvofaktorsku autentikaciju?", + "Settings": "Postavke", + "Dashboard": "Kontrolna ploča", + "New Update": "Novo ažuriranje", + "Language": "Jezik", + "Appearance": "Izgled", + "Theme": "Tema", + "General": "Općenito", + "Primary Base URL": "Osnovni URL", + "Version": "Inačica", + "Check Update On GitHub": "Provjeri dostupnost nove inačice na GitHubu", + "List": "Popis", + "Add": "Dodaj", + "Add New Monitor": "Dodaj novi Monitor", + "Quick Stats": "Statistika", + "Up": "Dostupno", + "Down": "Nedostupno", + "Pending": "U tijeku", + "Unknown": "Nepoznato", + "pauseDashboardHome": "Pauzirano", + "Name": "Naziv", + "Status": "Status", + "DateTime": "Vremenska oznaka", + "Message": "Izvještaj", + "No important events": "Nema važnih događaja", + "Pause": "Pauziraj", + "Resume": "Nastavi", + "Edit": "Uredi", + "Delete": "Obriši", + "Current": "Trenutno", + "Uptime": "Dostupnost", + "Cert Exp.": "Istek cert.", + "day": "dan | dana", + "-day": "-dnevno", + "hour": "sat(i)", + "-hour": "-satno", + "Response": "Odgovor", + "Ping": "Odziv", + "Monitor Type": "Vrsta Monitora", + "Keyword": "Ključna riječ", + "Friendly Name": "Prilagođen naziv", + "URL": "URL", + "Hostname": "Naziv hosta", + "Port": "Port", + "Heartbeat Interval": "Interval provjere", + "Retries": "Broj ponovnih pokušaja", + "Heartbeat Retry Interval": "Interval ponovnih pokušaja", + "Advanced": "Napredne postavke", + "Upside Down Mode": "Obrnuti način", + "Max. Redirects": "Maksimalan broj preusmjeravanja", + "Accepted Status Codes": "Prihvaćeni statusni kodovi", + "Push URL": "Push URL", + "needPushEvery": "Potrebno je slati zahtjeve na URL svakih {0} sekundi.", + "pushOptionalParams": "Neobavezni parametri: {0}", + "Save": "Spremi", + "Notifications": "Obavijesti", + "Not available, please setup.": "Nije dostupno, potrebno je dodati novu stavku.", + "Setup Notification": "Dodaj obavijest", + "Light": "Svijetli način", + "Dark": "Tamni način", + "Auto": "Automatski", + "Theme - Heartbeat Bar": "Tema za traku dostupnosti", + "Normal": "Normalno", + "Bottom": "Ispod", + "None": "Isključeno", + "Timezone": "Vremenska zona", + "Search Engine Visibility": "Vidljivost tražilicama", + "Allow indexing": "Dopusti indeksiranje", + "Discourage search engines from indexing site": "Sprječavanje indeksiranja", + "Change Password": "Promjena lozinke", + "Current Password": "Trenutna lozinka", + "New Password": "Nova lozinka", + "Repeat New Password": "Potvrdite novu lozinku", + "Update Password": "Spremi novu lozinku", + "Disable Auth": "Onemogući autentikaciju", + "Enable Auth": "Omogući autentikaciju", + "disableauth.message1": "Jeste li sigurni da želite {disableAuth}?", + "disable authentication": "isključiti autentikaciju", + "disableauth.message2": "To je za {intendThirdPartyAuth} ispred Uptime Kume, poput usluge Cloudflare Access.", + "where you intend to implement third-party authentication": "korisnike koji imaju vanjsku autentikaciju stranice", + "Please use this option carefully!": "Pažljivo koristite ovu opciju!", + "Logout": "Odjava", + "Leave": "Poništi", + "I understand, please disable": "Razumijem, svejedno onemogući", + "Confirm": "Potvrda", + "Yes": "Da", + "No": "Ne", + "Username": "Korisničko ime", + "Password": "Lozinka", + "Remember me": "Zapamti me", + "Login": "Prijava", + "No Monitors, please": "Nema monitora,", + "add one": "dodaj jedan", + "Notification Type": "Tip obavijesti", + "Email": "E-pošta", + "Test": "Testiraj", + "Certificate Info": "Informacije o certifikatu", + "Resolver Server": "DNS poslužitelj", + "Resource Record Type": "Vrsta DNS zapisa", + "Last Result": "Posljednji rezultat", + "Create your admin account": "Stvori administratorski račun", + "Repeat Password": "Potvrda lozinke", + "Import Backup": "Uvoz sigurnosne kopije", + "Export Backup": "Izvoz sigurnosne kopije", + "Export": "Izvoz", + "Import": "Uvoz", + "respTime": "Vrijeme odgovora (ms)", + "notAvailableShort": "ne postoji", + "Default enabled": "Omogući za nove monitore", + "Apply on all existing monitors": "Primijeni na postojeće monitore", + "Create": "Kreiraj", + "Clear Data": "Obriši podatke", + "Events": "Događaji", + "Heartbeats": "Provjere", + "Auto Get": "Automatski dohvat", + "backupDescription": "Moguće je napraviti sigurnosnu kopiju svih monitora i obavijesti koja će biti spremljena kao JSON datoteka.", + "backupDescription2": "Napomena: povijest i podaci o događajima nisu uključeni u sigurnosnu kopiju.", + "backupDescription3": "Osjetljivi podaci poput tokena za obavijesti uključeni su u sigurnosnu kopiju. Zato je potrebno čuvati izvoz na sigurnom mjestu.", + "alertNoFile": "Datoteka za uvoz nije odabrana.", + "alertWrongFileType": "Datoteka za uvoz nije u JSON formatu.", + "Clear all statistics": "Obriši sve statistike", + "Skip existing": "Preskoči postojeće", + "Overwrite": "Prepiši", + "Options": "Opcije", + "Keep both": "Zadrži sve", + "Verify Token": "Provjeri Token", + "Setup 2FA": "Postavi dvofaktorsku autentikaciju", + "Enable 2FA": "Omogući dvofaktorsku autentikaciju", + "Disable 2FA": "Onemogući dvofaktorsku autentikaciju", + "2FA Settings": "Postavke 2FA", + "Two Factor Authentication": "Dvofaktorska autentikacija", + "Active": "Aktivna", + "Inactive": "Neaktivno", + "Token": "Token", + "Show URI": "Pokaži URI", + "Tags": "Oznake", + "Add New below or Select...": "Dodajte novu oznaku ispod ili odaberite…", + "Tag with this name already exist.": "Oznaka s tim nazivom već postoji.", + "Tag with this value already exist.": "Oznaka s tom vrijednošću već postoji.", + "color": "Boja", + "value (optional)": "Vrijednost (neobavezno)", + "Gray": "Siva", + "Red": "Crvena", + "Orange": "Narančasta", + "Green": "Zelena", + "Blue": "Plava", + "Indigo": "Indigo", + "Purple": "Ljubičasta", + "Pink": "Ružičasta", + "Search...": "Pretraga…", + "Avg. Ping": "Prosječni odziv", + "Avg. Response": "Prosječni odgovor", + "Entry Page": "Početna stranica", + "statusPageNothing": "Ovdje nema ničega, dodajte grupu ili monitor.", + "No Services": "Nema usluga", + "All Systems Operational": "Svi sustavi su operativni", + "Partially Degraded Service": "Usluga djelomično nedostupna", + "Degraded Service": "Usluga nedostupna", + "Add Group": "Dodaj grupu", + "Add a monitor": "Dodaj monitor", + "Edit Status Page": "Uredi Statusnu stranicu", + "Go to Dashboard": "Na Kontrolnu ploču", + "Status Page": "Statusna stranica", + "Status Pages": "Statusne stranice", + "defaultNotificationName": "Moja {number}. {notification} obavijest", + "here": "ovdje", + "Required": "Potrebno", + "telegram": "Telegram", + "Bot Token": "Token bota", + "wayToGetTelegramToken": "Token možete nabaviti preko {0}.", + "Chat ID": "Identifikator razgovora", + "supportTelegramChatID": "Podržani su identifikatori izravnih razgovora, grupa i kanala", + "wayToGetTelegramChatID": "Identifikator razgovora možete saznati tako da botu pošaljete poruku te odete na ovaj URL:", + "YOUR BOT TOKEN HERE": "TOKEN BOTA STAVITI OVDJE", + "chatIDNotFound": "Identifikator razgovora nije pronađen; prvo morate poslati poruku botu", + "webhook": "Webhook", + "Post URL": "URL Post zahtjeva", + "Content Type": "Tip sadržaja (Content Type)", + "webhookJsonDesc": "{0} je dobra opcija za moderne HTTP poslužitelje poput Express.js-a", + "webhookFormDataDesc": "{multipart} je moguća alternativa za PHP, samo je potrebno parsirati JSON koristeći {decodeFunction}", + "smtp": "E-pošta (SMTP)", + "secureOptionNone": "Bez sigurnosti / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignoriraj greške TLS-a", + "From Email": "Adresa za \"From\" polje", + "emailCustomSubject": "Prilagođeno \"Subject\" polje", + "To Email": "Odredišne adrese e-pošte", + "smtpCC": "Cc", + "smtpBCC": "Bcc", + "discord": "Discord", + "Discord Webhook URL": "URL Discord webhooka", + "wayToGetDiscordURL": "Ovo možete dobiti tako da odete na Postavke servera -> Integracije -> Pogledaj webhookove -> Novi webhook", + "Bot Display Name": "Nadimak Bota unutar servera", + "Prefix Custom Message": "Prefiks prilagođene poruke", + "Hello @everyone is...": "Pozdrav {'@'}everyone…", + "teams": "Microsoft Teams", + "Webhook URL": "URL webhooka", + "wayToGetTeamsURL": "Više informacija o Teams webhookovima možete pročitati {0}.", + "signal": "Signal", + "Number": "Broj", + "Recipients": "Primatelji", + "needSignalAPI": "Potreban je klijent s REST sučeljem.", + "wayToCheckSignalURL": "Više informacija o postavljanju Signal klijenta:", + "signalImportant": "VAŽNO: Grupe i brojevi se ne mogu istovremeno koristiti kao primatelji!", + "gotify": "Gotify", + "Application Token": "Token Aplikacije", + "Server URL": "URL poslužitelja", + "Priority": "Prioritet", + "slack": "Slack", + "Icon Emoji": "Emotikon", + "Channel Name": "Naziv kanala", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Dodatne informacije o webhookovima su dostupne na: {0}", + "aboutChannelName": "Unesite ime {0} kanala u polju Naziv kanala ako želite zaobići webhook kanal. Primjerice: #neki-kanal", + "aboutKumaURL": "Ako je polje \"Uptime Kuma URL\" prazno, koristi se zadana vrijednost koja vodi na GitHub stranicu projekta.", + "emojiCheatSheet": "Popis emotikona: {0}", + "rocket.chat": "Rocket.Chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Podržava preko 50 usluga za obavijesti)", + "pushbullet": "Pushbullet", + "line": "LINE", + "mattermost": "Mattermost", + "User Key": "Korisnički ključ", + "Device": "Uređaji", + "Message Title": "Naslov poruke", + "Notification Sound": "Zvuk obavijesti", + "More info on:": "Više informacija na: {0}", + "pushoverDesc1": "Hitni prioritet (2) ima zadani istek vremena od 30 sekundi između ponovnih pokušaja te će isteći nakon 1 sata.", + "pushoverDesc2": "Ako želite slati obavijesti na više uređaja, ispunite polje \"Uređaji\".", + "SMS Type": "Tip SMS-a", + "octopushTypePremium": "Premium (Brzo - preporučeno za obavijesti)", + "octopushTypeLowCost": "Low Cost (Sporo - mobilni operateri ponekad blokiraju ove poruke)", + "checkPrice": "Provjerite {0} cijene:", + "apiCredentials": "Vjerodajnice za API", + "octopushLegacyHint": "Koristite li staru inačicu usluge Octopush (2011-2020) ili noviju inačicu?", + "Check octopush prices": "Provjerite cijene usluge Octopush {0}.", + "octopushPhoneNumber": "Telefonski broj (međunarodni format, primjerice: +38512345678)", + "octopushSMSSender": "Naziv SMS pošiljatelja : 3-11 alfanumeričkih znakova i razmak (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea identifikator uređaja", + "Apprise URL": "URL usluge Apprise", + "Example:": "Primjerice: {0}", + "Read more:": "Pročitajte više: {0}", + "Status:": "Status: {0}", + "Read more": "Pročitaj više", + "appriseInstalled": "Apprise je instaliran.", + "appriseNotInstalled": "Apprise nije instaliran. {0}", + "Access Token": "Pristupni token", + "Channel access token": "Token za pristup kanalu", + "Line Developers Console": "LINE razvojnoj konzoli", + "lineDevConsoleTo": "LINE razvojna konzola - {0}", + "Basic Settings": "Osnovne Postavke", + "User ID": "Korisnički identifikator", + "Messaging API": "API za razmjenu poruka", + "wayToGetLineChannelToken": "Prvo, pristupite {0}, kreirajte pružatelja usluga te kanal (API za razmjenu poruka), zatim možete dobiti token za pristup kanalu te identifikator korisnika za polja iznad.", + "Icon URL": "URL slike", + "aboutIconURL": "Možete postaviti poveznicu na sliku u polju \"URL slike\" kako biste spriječili korištenje zadane slike. Ovo se polje neće koristiti ako je postavljeno polje \"Emotikon\".", + "aboutMattermostChannelName": "Možete promijeniti kanal u kojeg webhook šalje tako da ispunite polje \"Naziv kanala\". Ta opcija mora biti omogućena unutar Mattermost postavki za webhook. Primjerice: #neki-kanal", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - jeftina, ali spora opcija koja je često preopterećena. Ograničeno samo na primatelje unutar Poljske.", + "promosmsTypeFlash": "SMS FLASH - Poruka se automatski pojavljuje na uređaju primatelja. Ograničeno samo na primatelje unutar Poljske.", + "promosmsTypeFull": "SMS FULL - Premium razina usluge, dozvoljava postavljanje naziva SMS pošiljatelja (Naziv mora biti registriran). Usluga pouzdana za obavijesti.", + "promosmsTypeSpeed": "SMS SPEED - Usluga najvećeg prioriteta. Brza i pouzdana, ali skupa (otprilike dvostruko skuplja od cijene usluge SMS FULL).", + "promosmsPhoneNumber": "Telefonski broj (za primatelje unutar Poljske nije potrebno navoditi pozivni broj države)", + "promosmsSMSSender": "Naziv SMS pošiljatelja: Registriran naziv ili jedan od zadanih: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu URL webhooka", + "matrixHomeserverURL": "URL Matrix homeservera (uključujući http(s):// te port, ako je potrebno)", + "Internal Room Id": "Interni ID sobe", + "matrixDesc1": "Interni ID sobe se može pronaći u naprednim postavkama sobe unutar Matrix klijenta. ID sobe nalikuje idućem zapisu: !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Preporučuje se stvaranje novog korisnika te suzdržavanje od korištenja pristupnog tokena vlastitog Matrix korisnika. Novog korisnika potrebno je dodati u sobe u kojima želite primati obavijesti. Pristupni token možete dobiti pokretanjem naredbe {0}", + "Method": "Metoda", + "Body": "Tijelo", + "Headers": "Zaglavlja", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "Zaglavlja nisu nije valjani JSON: ", + "BodyInvalidFormat": "Tijelo zahtjeva nije valjani JSON: ", + "Monitor History": "Povijest monitora", + "clearDataOlderThan": "Podaci o povijesti monitora čuvaju se {0} dana.", + "PasswordsDoNotMatch": "Lozinke se ne poklapaju.", + "records": "zapisa", + "One record": "Jedan zapis", + "Showing {from} to {to} of {count} records": "Prikaz zapisa {from}-{to} od sveukupno {count}", + "steamApiKeyDescription": "Za praćenje Steam poslužitelja za igru, potrebno je imati Steam Web-API ključ. Možete registrirati vlastiti ključ ovdje: ", + "Current User": "Trenutni korisnik", + "recent": "Nedavno", + "Done": "Gotovo", + "Info": "Informacije", + "Security": "Sigurnost", + "Shrink Database": "Smanji bazu podataka", + "Pick a RR-Type...": "Odaberite vrstu DNS zapisa od navedenih…", + "Pick Accepted Status Codes...": "Odaberite HTTP statusne kodove koji će biti prihvaćeni…", + "Steam API Key": "Steam API ključ", + "Default": "Zadano", + "HTTP Options": "HTTP Postavke", + "Create Incident": "Novi izvještaj o incidentu", + "Title": "Naslov", + "Content": "Sadržaj", + "Style": "Stil", + "info": "informacija", + "warning": "upozorenje", + "danger": "opasnost", + "primary": "primarno", + "light": "svijetlo", + "dark": "tamno", + "Post": "Objavi", + "Created": "Stvoreno", + "Last Updated": "Uređeno", + "Please input title and content": "Naslov i sadržaj ne mogu biti prazni", + "Unpin": "Ukloni", + "Switch to Light Theme": "Prebaci na svijetli način", + "Switch to Dark Theme": "Prebaci na tamni način", + "Show Tags": "Pokaži oznake", + "Hide Tags": "Sakrij oznake", + "Description": "Opis", + "No monitors available.": "Nema dostupnih monitora.", + "Add one": "Stvori jednog", + "No Monitors": "Bez monitora", + "Untitled Group": "Bezimena grupa", + "Services": "Usluge", + "Discard": "Odbaci", + "Cancel": "Otkaži", + "Powered by": "Pokreće", + "Saved.": "Spremljeno.", + "PushByTechulus": "Push by Techulus", + "GoogleChat": "Google Chat (preko platforme Google Workspace)", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API korisničko ime (uključujući webapi_ prefiks)", + "serwersmsAPIPassword": "API lozinka", + "serwersmsPhoneNumber": "Broj telefona", + "serwersmsSenderName": "Ime SMS pošiljatelja (registrirano preko korisničkog portala)", + "stackfield": "Stackfield", + "smtpDkimSettings": "DKIM postavke", + "smtpDkimDesc": "Za više informacija, postoji Nodemailer DKIM {0}.", + "documentation": "dokumentacija", + "smtpDkimDomain": "Domena", + "smtpDkimKeySelector": "Odabir ključa", + "smtpDkimPrivateKey": "Privatni ključ", + "smtpDkimHashAlgo": "Hash algoritam (neobavezno)", + "smtpDkimheaderFieldNames": "Ključevi zaglavlja za potpis (neobavezno)", + "smtpDkimskipFields": "Ključevi zaglavlja koji se neće potpisati (neobavezno)", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "Krajnja točka API-ja (Endpoint)", + "alertaEnvironment": "Okruženje (Environment)", + "alertaApiKey": "API ključ", + "alertaAlertState": "Stanje upozorenja", + "alertaRecoverState": "Stanje oporavka", + "deleteStatusPageMsg": "Sigurno želite obrisati ovu statusnu stranicu?", + "resendEveryXTimes": "Ponovno pošalji svakih {0} puta", + "resendDisabled": "Ponovno slanje je onemogućeno", + "dnsPortDescription": "Port DNS poslužitelja. Zadana vrijednost je 53. Moguće je promijeniti ga u svakom trenutku.", + "Resend Notification if Down X times consecutively": "Ponovno pošalji obavijest ako je usluga nedostupna više puta zaredom", + "topic": "Tema", + "topicExplanation": "MQTT tema koja će se monitorirati", + "successMessage": "Poruka o uspjehu", + "successMessageExplanation": "MQTT poruka koja se smatra uspješnom", + "error": "greška", + "critical": "kritično", + "Customize": "Prilagodi", + "Custom Footer": "Prilagođeno podnožje", + "Custom CSS": "Prilagođeni CSS", + "wayToGetPagerDutyKey": "Ključ možete dobiti odlaskom na \"Service -> Service Directory -> (Odabrani servis) -> Integrations -> Add integration\". Ovdje pretražite za \"Events API V2\". Više informacija {0}", + "Integration Key": "Ključ integracije", + "Integration URL": "URL integracije", + "Auto resolve or acknowledged": "Automatsko razrješavanje i priznavanje", + "do nothing": "Ne radi ništa", + "auto acknowledged": "Automatsko priznavanje", + "auto resolve": "Automatsko razrješavanje", + "Proxies": "Proxy poslužitelji", + "default": "Zadano", + "enabled": "Omogućeno", + "setAsDefault": "Postavi kao zadano", + "deleteProxyMsg": "Sigurno želite obrisati ovaj proxy za sve monitore?", + "proxyDescription": "Proxy poslužitelji moraju biti dodijeljni monitoru kako bi funkcionirali.", + "enableProxyDescription": "Onemogućeni proxy poslužitelj neće imati učinak na zahtjeve monitora. Možete privremeno onemogućiti proxy poslužitelja za sve monitore.", + "setAsDefaultProxyDescription": "Ovaj proxy poslužitelj bit će odmah omogućen za nove monitore. I dalje ga možete onemogućiti za svaki monitor zasebno.", + "Certificate Chain": "Lanac certifikata", + "Valid": "Važeći", + "Invalid": "Nevažeći", + "AccessKeyId": "AccessKey identifikator", + "SecretAccessKey": "AccessKey tajni ključ", + "PhoneNumbers": "Telefonski brojevi", + "TemplateCode": "Predložak koda", + "SignName": "Potpis", + "Sms template must contain parameters: ": "SMS predložak mora sadržavati parametre: ", + "Bark Endpoint": "Bark krajnja točka (endpoint)", + "Bark Group": "Bark grupa", + "Bark Sound": "Bark zvuk", + "WebHookUrl": "URL webhooka", + "SecretKey": "Tajni ključ", + "For safety, must use secret key": "Korištenje tajnog ključa je obavezno", + "Device Token": "Token uređaja", + "Platform": "Platforma", + "Huawei": "Huawei", + "High": "Visoko", + "Retry": "Ponovnih pokušaja", + "Topic": "Tema", + "WeCom Bot Key": "WeCom ključ Bota", + "Setup Proxy": "Dodaj proxy poslužitelj", + "Proxy Protocol": "Protokol", + "Proxy Server": "Proxy poslužitelj", + "Proxy server has authentication": "Proxy poslužitelj ima autentikaciju", + "User": "Korisnik", + "Installed": "Instalirano", + "Not installed": "Nije instalirano", + "Running": "Pokrenuto", + "Not running": "Nije pokrenuto", + "Remove Token": "Ukloni Token", + "Start": "Pokreni", + "Stop": "Zaustavi", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Dodaj novu statusnu stranicu", + "Slug": "Slug", + "Accept characters:": "Dozvoljeni znakovi:", + "startOrEndWithOnly": "Započinje ili završava znakovima {0}", + "No consecutive dashes": "Bez uzastopnih povlaka", + "Next": "Sljedeće", + "The slug is already taken. Please choose another slug.": "Slug je zauzet. Odaberite novi slug.", + "No Proxy": "Bez proxy poslužitelja", + "Authentication": "Autentikacija", + "HTTP Basic Auth": "HTTP \"Basic\" autentifikacija", + "New Status Page": "Dodaj statusnu stranicu", + "Page Not Found": "Stranica nije pronađena", + "Reverse Proxy": "Reverzni proxy", + "Backup": "Sigurnosno kopiranje", + "About": "O Uptime Kumi", + "wayToGetCloudflaredURL": "(Preuzmite cloudflared s {0})", + "cloudflareWebsite": "Cloudflare web stranice", + "Message:": "Poruka:", + "Don't know how to get the token? Please read the guide:": "Ne znate kako doći do tokena? Pročitajte vodič:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Trenutna veza možda bude prekinuta jer se koristi Cloudflare tuneliranje. Sigurno želite zaustaviti? Unesite lozinku za potvrdu.", + "HTTP Headers": "HTTP zaglavlja", + "Trust Proxy": "Vjeruj proxy poslužitelju", + "Other Software": "Ostali programi", + "For example: nginx, Apache and Traefik.": "Primjerice: nginx, Apache ili Traefik.", + "Please read": "Molimo pročitajte", + "Subject:": "Predmet:", + "Valid To:": "Valjano do:", + "Days Remaining:": "Preostalo dana:", + "Issuer:": "Izdavatelj:", + "Fingerprint:": "Fingerprint:", + "No status pages": "Nema statusnih stranica", + "Domain Name Expiry Notification": "Obavijest za istek domena", + "Proxy": "Proxy", + "Date Created": "Datum stvaranja", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "OneBot HTTP adresa", + "onebotMessageType": "OneBot tip poruke", + "onebotGroupMessage": "Grupna", + "onebotPrivateMessage": "Privatna", + "onebotUserOrGroupId": "ID korisnika/grupe", + "onebotSafetyTips": "Pristupni token mora biti postavljen", + "PushDeer Key": "PushDeer ključ", + "Footer Text": "Tekst podnožja", + "Show Powered By": "Pokaži natpis 'Pokreće...'", + "Domain Names": "Domene", + "signedInDisp": "Prijavljeni ste kao {0}", + "signedInDispDisabled": "Autentikacija onemogućena.", + "RadiusSecret": "Radius Tajna", + "RadiusSecretDescription": "Dijeljena Tajna između klijenta i poslužitelja", + "RadiusCalledStationId": "Called Station ID", + "RadiusCalledStationIdDescription": "Identifikator pozivne stanice", + "RadiusCallingStationId": "Calling Station ID", + "RadiusCallingStationIdDescription": "Identifikator pozivajuće stanice", + "Certificate Expiry Notification": "Obavijest za istek certifikata", + "API Username": "API korisničko ime", + "API Key": "API ključ", + "Recipient Number": "Broj primatelja", + "From Name/Number": "Naziv/broj pošiljatelja", + "Leave blank to use a shared sender number.": "Ostaviti prazno za korištenje dijeljenog broja pošiljatelja.", + "Octopush API Version": "Verzija Octopush API-ja", + "Legacy Octopush-DM": "Zastarijela Octopush-DM", + "endpoint": "krajnja točka (endpoint)", + "octopushAPIKey": "\"API ključ\" iz HTTP API postavki na upravljačkoj ploči", + "octopushLogin": "\"Korisničko ime\" iz HTTP API postavki", + "promosmsLogin": "API korisničko ime", + "promosmsPassword": "API lozinka", + "pushoversounds pushover": "Pushover (zadano)", + "pushoversounds bike": "Bicikl", + "pushoversounds bugle": "Truba", + "pushoversounds cashregister": "Blagajna", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Kozmički", + "pushoversounds falling": "Padanje", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Dolazno", + "pushoversounds intermission": "Intermisija", + "pushoversounds magic": "Čarolija", + "pushoversounds mechanical": "Mehanički", + "pushoversounds pianobar": "Bar s klavirom", + "pushoversounds siren": "Sirena", + "pushoversounds spacealarm": "Svemirski alarm", + "pushoversounds tugboat": "Remorker", + "pushoversounds alien": "Vanzemaljski alarm (dugačko)", + "pushoversounds climb": "Penjanje (dugačko)", + "pushoversounds persistent": "Uporno (dugačko)", + "pushoversounds echo": "Pushover jeka (dugačko)", + "pushoversounds updown": "Gore-dolje (dugačko)", + "pushoversounds vibrate": "Samo vibracija", + "pushoversounds none": "Utišano (bez zvuka)", + "pushyAPIKey": "Tajni API ključ", + "pushyToken": "Token uređaja", + "Show update if available": "Pokaži moguću nadogradnju", + "Also check beta release": "Provjeravaj i za beta izdanja", + "Using a Reverse Proxy?": "Koristi li se reverzni proxy?", + "Check how to config it for WebSocket": "Provjerite kako se konfigurira za WebSocket protokol", + "Steam Game Server": "Steam poslužitelj igre", + "Most likely causes:": "Najvjerojatniji uzroci:", + "The resource is no longer available.": "Resurs više nije dostupan.", + "There might be a typing error in the address.": "Možda je nastala greška pri upisu adrese.", + "What you can try:": "Što možete pokušati:", + "Retype the address.": "Ponovno napišite adresu.", + "Go back to the previous page.": "Vratite se na prethodnu stranicu.", + "Coming Soon": "Dolazi uskoro", + "wayToGetClickSendSMSToken": "Možete dobiti API korisničko ime i API ključ sa {here}.", + "Connection String": "Tekst veze", + "Query": "Upit", + "settingsCertificateExpiry": "TLS istek certifikata", + "certificationExpiryDescription": "HTTPS monitori će obavijesiti kada je istek TLS certifikata za:", + "Setup Docker Host": "Dodaj Docker hosta", + "Connection Type": "Tip veze", + "Docker Daemon": "Docker daemon", + "deleteDockerHostMsg": "Sigurno želite izbrisati ovog Docker hosta za sve monitore?", + "socket": "Docker socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker kontejner", + "Container Name / ID": "Naziv / identifikator kontejnera", + "Docker Host": "Docker host", + "Docker Hosts": "Docker hostovi", + "ntfy Topic": "ntfy tema", + "Domain": "Domena", + "Workstation": "Radna stanica", + "disableCloudflaredNoAuthMsg": "Lozinka nije nužna dok je isključena autentikacija.", + "trustProxyDescription": "Vjeruj 'X-Forwarded-*' zaglavljima. Ako želite dobiti ispravnu IP adresu klijenta i Uptime Kuma je iza reverznog proxy poslužitelja, trebate omogućiti ovo.", + "wayToGetLineNotifyToken": "Možete dobiti pristupni token sa {0}", + "Examples": "Primjeri", + "Home Assistant URL": "URL Home Assistanta", + "Long-Lived Access Token": "Dugotrajni pristupni token", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Dugotrajni pristupni token može se kreirati klikom na korisničko ime (dolje lijevo) u Home Assistantu, pomicanjem do dna, te klikom na 'Create Token'.", + "Notification Service": "Notification Service", + "default: notify all devices": "zadano ponašanje: obavijesti sve uređaje", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Popis servisa za obavijesti u Home Assistantu nalaze se pod \"Developer Tools > Services\" te pretražiti \"notification\".", + "Automations can optionally be triggered in Home Assistant:": "Automacije se mogu okinuti u Home Assistantu:", + "Trigger type:": "Tip triggera:", + "Event type:": "Tip eventa:", + "Event data:": "Podaci eventa:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Potrebno je i odabrati akciju za izvođenje na Home Assistantu.", + "Frontend Version": "Inačica sučelja", + "Frontend Version do not match backend version!": "Inačica sučelja ne odgovara poslužitelju!", + "monitorToastMessagesLabel": "Skočne obavijesti Monitora", + "toastSuccessTimeout": "Vrijeme isteka skočnih obavijesti o uspjehu", + "toastErrorTimeout": "Vrijeme isteka skočnih obavijesti o pogrešci", + "Enter the list of brokers": "Upišite popis brokera", + "Press Enter to add broker": "Pritisnite Enter za dodavanje brokera", + "Kafka Topic Name": "Naziv Kafka teme", + "Kafka Producer Message": "Poruka Kafka producera", + "Enable Kafka SSL": "Omogući SSL", + "Enable Kafka Producer Auto Topic Creation": "Omogući automatsku izradu teme za Kafka producera", + "Kafka SASL Options": "Kafka SASL opcije", + "Mechanism": "Mehanizam", + "Pick a SASL Mechanism...": "Odaberite SASL mehanizam…", + "AccessKey Id": "ID pristupnog ključa", + "Secret AccessKey": "Tajni pristupni ključ", + "Session Token": "Token sesije", + "Schedule maintenance": "Zakaži održavanje", + "Select status pages...": "Odaberi statusne stranice…", + "webhookAdditionalHeadersTitle": "Dodatna zaglavlja", + "webhookAdditionalHeadersDesc": "Postavlja dodatna polja zaglavlja koja se šalju s webhookom. Svako zaglavlje treba se definirati kao JSON par ključ-vrijednost.", + "Packet Size": "Veličina paketa", + "backupRecommend": "Umjesto toga napravite ručnu sigurnosnu kopiju cijelog volumena ili izravno direktorija s podacima (./data/).", + "No Maintenance": "Ne postoje zakazana održavanja", + "Server Timezone": "Vremenska zona poslužitelja", + "dnsCacheDescription": "Možda ne radi kako spada u nekim IPv6 okruženjima. Onemogućite ako naiđete na probleme.", + "Select": "Odaberi", + "tailscalePingWarning": "Kako biste koristili Tailscale Ping monitor, trebate instalirati Uptime Kuma bez Dockera te također instalirati Tailscale klijent na svoj poslužitelj.", + "telegramProtectContentDescription": "Ako je omogućeno, poruke bota će biti zaštićene od prosljeđivanja i spremanja.", + "enableNSCD": "Omogući NSCD (Name Service Cache Daemon) za sve DNS zahtjeve", + "chromeExecutableDescription": "Za korisnike Dockera, ako Chromium još nije instaliran, instalacija i prikaz rezultata testa može potrajati nekoliko minuta. Zauzima 1 GB prostora na disku.", + "grpcMethodDescription": "Naziv metode automatski se pretvara u camelCase format. Primjerice, \"say hello\" će se pretvoriti u \"sayHello\".", + "wayToGetKookBotToken": "Kreirajte aplikaciju i preuzmite token svog bota na {0}", + "wayToGetKookGuildID": "Uključite 'Developer Mode' za Kook u postavkama i desnom tipkom miša kliknite na guild kako biste dobili njegov ID", + "Lowcost": "Niskotarifni", + "SendKey": "Ključ za slanje (SendKey)", + "You can divide numbers with": "Možete odvojiti brojeve pomoću", + "goAlertInfo": "GoAlert je aplikacija otvorenog koda za zakazivanje poziva, automatiziranu eskalaciju i slanje obavijesti (poput SMS-a ili glasovnih poziva). Automatski obavijestite pravu osobu, na pravi način i u pravo vrijeme! {0}", + "smseagleTo": "Broj(evi) telefona", + "smseagleGroup": "Nazivi grupa telefonskog imenika", + "smseagleRecipient": "Primatelj(i) (višestruke vrijednosti moraju se odvojiti zarezom)", + "pushDeerServerDescription": "Ostavite prazno za korištenje službenog poslužitelja", + "Edit Tag": "Uredi oznaku", + "Expiry date": "Datum isteka", + "Schedule Maintenance": "Zakazivanje održavanja", + "Edit Maintenance": "Uređivanje održavanja", + "uninstall": "Deinstaliraj", + "uninstalling": "Deinstalacija", + "Badge Type": "Tip značke", + "apiKeyAddedMsg": "Vaš API ključ je dodan. Spremite ga sada jer se više neće prikazivati.", + "pagertreeDoNothing": "Ne čini ništa", + "wayToGetPagerTreeIntegrationURL": "Nakon što stvorite Uptime Kuma integraciju u aplikaciji PagerTree, kopirajte krajnju točku (endpoint). Pogledajte sve pojedinosti {0}", + "Show Clickable Link Description": "Ako je označeno, svi koji imaju pristup ovoj statusnoj stranici mogu imati pristup URL-u Monitora.", + "monitorToastMessagesDescription": "Skočne obavijesti za monitore nestaju nakon zadanog vremena u sekundama. Vrijednost od -1 onemogućuje vremensko ograničenje, 0 onemogućuje skočne obavijesti.", + "Authorization Identity": "Identitet autorizacije", + "weekdayShortThu": "Čet", + "setupDatabaseChooseDatabase": "Koju bazu podataka želite koristiti?", + "setupDatabaseEmbeddedMariaDB": "Ne morate ništa dodatno postavljati. Ovaj Docker image ima ugrađenu i konfiguriranu MariaDB bazu podataka za Vas. Uptime Kuma će se spojiti na ovu bazu preko UNIX socketa.", + "setupDatabaseMariaDB": "Spojite vanjsku MariaDB bazu podataka. Morate unijeti informacije o konekciji prema bazi.", + "setupDatabaseSQLite": "Jednostavna datoteka s bazom podataka, preporuča se samo za manje implementacije. Prije inačice v2.0.0, Uptime Kuma je koristila SQLite kao zadanu bazu podataka.", + "dbName": "Naziv baze podataka", + "Start of maintenance": "Početak održavanja", + "All Status Pages": "Sve statusne stranice", + "Affected Monitors": "Zahvaćeni Monitori", + "Pick Affected Monitors...": "Odaberi zahvaćene Monitore…", + "filterActivePaused": "Pauzirano", + "Add New Tag": "Dodaj novu oznaku", + "statusPageRefreshIn": "Osvježavanje za: {0}", + "webhookCustomBodyDesc": "Prilagodite tijelo HTTP zahtjeva. Dozvoljene varijable predloška: {msg}, {heartbeat}, {monitor}.", + "webhookBodyPresetOption": "Unaprijed postavljeno - {0}", + "webhookBodyCustomOption": "Prilagođeno tijelo zahtjeva", + "selectedMonitorCount": "Odabrano: {0}", + "Check/Uncheck": "Označi/odznači", + "telegramMessageThreadID": "(Neobvezno) Identifikator dretve poruka", + "telegramMessageThreadIDDescription": "Neobavezni jedinstveni identifikator za dretvu poruka (temu) foruma; samo za forumske supergrupe", + "telegramSendSilently": "Pošalji nečujno", + "telegramSendSilentlyDescription": "Šalje poruku nečujno. Primatelji će dobiti obavijest bez zvuka.", + "telegramProtectContent": "Zaštiti od prosljeđivanja i spremanja", + "Optional": "Neobavezno", + "or": "ili", + "weekdayShortTue": "Uto", + "weekdayShortWed": "Sri", + "weekdayShortFri": "Pet", + "weekdayShortSat": "Sub", + "dayOfWeek": "Dan u tjednu", + "dayOfMonth": "Dan u mjesecu", + "lastDay": "Posljednji dan", + "lastDay1": "Posljednji dan mjeseca", + "lastDay2": "Pretposljednji dan mjeseca", + "lastDay3": "Treći do posljednjeg dana u mjesecu", + "lastDay4": "Četvrti do posljednjeg dana u mjesecu", + "pauseMaintenanceMsg": "Jeste li sigurni da želite pauzirati?", + "maintenanceStatus-under-maintenance": "Održavanje u tijeku", + "maintenanceStatus-inactive": "Neaktivno", + "maintenanceStatus-scheduled": "Zakazano", + "maintenanceStatus-ended": "Završeno", + "maintenanceStatus-unknown": "Nepoznato", + "Display Timezone": "Prikaži vremensku zonu", + "statusPageMaintenanceEndDate": "Kraj", + "IconUrl": "URL ikone", + "Enable": "Omogući", + "Disable": "Onemogući", + "sameAsServerTimezone": "Ista kao i vremenska zona poslužitelja", + "chromeExecutable": "Izvršna datoteka za Chrome/Chromium", + "chromeExecutableAutoDetect": "Automatska detekcija", + "Single Maintenance Window": "Jednokratno održavanje", + "Maintenance Time Window of a Day": "Vrijeme održavanja u danu", + "Effective Date Range": "Efektivan raspon datuma (neobavezno)", + "Clone": "Kloniraj", + "dataRetentionTimeError": "Razdoblje zadržavanja mora biti 0 ili veće", + "infiniteRetention": "Postavite na 0 za beskonačno zadržavanje.", + "confirmDeleteTagMsg": "Jeste li sigurni da želite izbrisati ovu oznaku? Monitori povezani s ovom oznakom neće biti izbrisani.", + "enableGRPCTls": "Omogući sigurno slanje gRPC zahtjeva koristeći TLS", + "deleteMaintenanceMsg": "Jeste li sigurni da želite izbrisati ovo održavanje?", + "Guild ID": "Identifikator za guild", + "pushoverMessageTtl": "Vrijeme isteka poruke (u sekundama)", + "Proto Method": "Metoda poziva", + "Proto Content": "Proto sadržaj", + "Economy": "Ekonomski", + "high": "visoko", + "SMSManager API Docs": "Dokumentacija SMSManager API-ja", + "Gateway Type": "Tip poveznika (gateway)", + "Base URL": "Osnovni URL", + "goAlertIntegrationKeyInfo": "Nabavite generički integracijski API ključ za ovu uslugu u formatu \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\", koji je obično vrijednost token parametra kopiranog URL-a.", + "aboutNotifyChannel": "Ova opcija će poslati mobilnu ili desktop obavijest za sve članove kanala, bez obzira na to jesu li trenutno dostupni ili ne.", + "smseagleContact": "Nazivi kontakata telefonskog imenika", + "smseagleRecipientType": "Tip primatelja", + "smseagleToken": "Token za pristup API-ju", + "smseagleUrl": "URL Vašeg SMSEagle uređaja", + "smseagleEncoding": "Pošalji kao Unicode (zadano=GSM-7)", + "smseaglePriority": "Prioritet poruke (0-9, gdje je 9 najveći prioritet)", + "Server URL should not contain the nfty topic": "URL poslužitelja ne smije sadržavati temu nfty", + "PushDeer Server": "PushDeer poslužitelj", + "Custom Monitor Type": "Prilagođeni tip Monitora", + "Google Analytics ID": "Google Analytics identifikator", + "Server Address": "Adresa poslužitelja", + "Learn More": "Saznaj više", + "Body Encoding": "Vrsta sadržaja u tijelu zahtjeva", + "API Keys": "API ključevi", + "Expiry": "Istek", + "Don't expire": "Bez isteka", + "Continue": "Nastavi", + "Add Another": "Dodaj još jedan", + "Key Added": "Ključ dodan", + "Add API Key": "Dodaj API ključ", + "No API Keys": "Nema dodanih API ključeva", + "apiKey-active": "Aktivan", + "apiKey-expired": "Istekao", + "apiKey-inactive": "Neaktivan", + "Expires": "Ističe", + "disableAPIKeyMsg": "Jeste li sigurni da želite onemogućiti ovaj API ključ?", + "Generate": "Generiraj", + "pagertreeIntegrationUrl": "URL integracije", + "pagertreeUrgency": "Hitnost", + "pagertreeSilent": "Utišano", + "pagertreeLow": "Niska", + "pagertreeMedium": "Srednja", + "pagertreeHigh": "Visoka", + "pagertreeCritical": "Kritična", + "pagertreeResolve": "Automatsko rješavanje", + "lunaseaTarget": "Cilj", + "lunaseaDeviceID": "ID uređaja", + "lunaseaUserID": "Korisnički ID", + "ntfyAuthenticationMethod": "Metoda provjere autentičnosti", + "ntfyUsernameAndPassword": "Korisničko ime i lozinka", + "twilioAccountSID": "SID korisničkog računa", + "twilioAuthToken": "Token za autentifikaciju / tajni API ključ", + "twilioFromNumber": "Broj pošiljatelja", + "twilioToNumber": "Broj primatelja", + "Show Clickable Link": "Pokaži poveznicu", + "Open Badge Generator": "Otvori generator znački", + "Badge Duration (in hours)": "Trajanje značke (u satima)", + "Badge Prefix": "Prefiks vrijednosti značke", + "Badge Suffix": "Sufiks vrijednosti značke", + "Badge Label": "Natpis značke", + "Badge Label Color": "Boja natpisa značke", + "Badge Color": "Boja značke", + "Badge Label Prefix": "Prefiks natpisa značke", + "Badge Preview": "Pretpregled značke", + "Badge Label Suffix": "Sufiks natpisa značke", + "Badge Up Color": "Boja značke za dostupnost", + "Badge Down Color": "Boja značke za nedostupnost", + "Badge Pending Color": "Boja značke za monitore u tijeku", + "Badge Maintenance Color": "Boja značke za monitore u održavanju", + "Badge Warn Color": "Boja značke za upozorenje", + "Badge Warn Days": "Dani značke za upozorenje", + "Badge Down Days": "Dani značke za nedostupnost", + "Badge value (For Testing only.)": "Vrijednost značke (samo za testiranje)", + "Badge URL": "URL značke", + "Group": "Grupa", + "Monitor Group": "Grupa Monitora", + "Badge Style": "Stil značke", + "Custom": "Prilagođeno", + "styleElapsedTime": "Vremenske oznake ispod trake dostupnosti", + "styleElapsedTimeShowNoLine": "Pokaži (bez linije)", + "styleElapsedTimeShowWithLine": "Pokaži (s linijom)", + "recurringInterval": "Periodično", + "Recurring": "Ponavljajući", + "strategyManual": "Ručno aktivno/neaktivno", + "warningTimezone": "Koristi vremensku zonu poslužitelja", + "weekdayShortMon": "Pon", + "weekdayShortSun": "Ned", + "startDateTime": "Vrijeme početka", + "endDateTime": "Vrijeme završetka", + "cronExpression": "Cron izraz", + "cronSchedule": "Raspored: ", + "invalidCronExpression": "Nevaljali Cron izraz: {0}", + "Date and Time": "Datum i vrijeme", + "DateTime Range": "Vremenski raspon", + "loadingError": "Nije moguće dohvatiti podatke, pokušajte ponovno kasnije.", + "plugin": "Dodatak | Dodaci", + "install": "Instaliraj", + "installing": "Instaliranje", + "confirmUninstallPlugin": "Jeste li sigurni da želite deinstalirati ovaj dodatak?", + "notificationRegional": "Specifično za regiju", + "Clone Monitor": "Kloniraj Monitor", + "cloneOf": "Klon monitora {0}", + "wayToGetZohoCliqURL": "Možete naučiti kako kreirati URL za webhook {0}.", + "affectedMonitorsDescription": "Odaberite monitore koji će biti zahvaćeni održavanjem", + "recurringIntervalMessage": "Pokreni jednom svaki dan | Pokreni jednom svakih {0} dana", + "affectedStatusPages": "Prikazuje poruku o održavanju na odabranim statusnim stranicama", + "atLeastOneMonitor": "Odaberite barem jedan zahvaćeni Monitor", + "invertKeywordDescription": "Postavi da ključna riječ mora biti odsutna umjesto prisutna.", + "Strategy": "Strategija", + "Free Mobile User Identifier": "Besplatni mobilni korisnički identifikator", + "Free Mobile API Key": "Besplatni mobilni ključ za API", + "Enable TLS": "Omogući TLS", + "Proto Service Name": "Naziv servisa", + "promosmsAllowLongSMS": "Dozvoli dugačke SMS-ove", + "Notify Channel": "Obavijesti cijeli kanal", + "Request Timeout": "Vrijeme isteka zahtjeva", + "timeoutAfter": "Istek nakon {0} sekundi", + "backupOutdatedWarning": "Zastarjelo: Budući da je dodano puno značajki, a ova je pomalo neodržavana, ne može generirati niti vratiti potpunu sigurnosnu kopiju.", + "Enable DNS Cache": "(Zastarijelo) Omogući DNS privremenu memoriju (cache) za HTTP(s) monitore", + "Home": "Početna", + "deleteAPIKeyMsg": "Jeste li sigurni da želite obrisati ovaj API ključ?", + "twilioApiKey": "API ključ (neobavezno)", + "Kafka Brokers": "Kafka brokeri", + "Game": "Igra", + "Passive Monitor Type": "Pasivni tip Monitora", + "markdownSupported": "Podržana je Markdown sintaksa. Ako koristite HTML, izbjegavajte početne razmake kako biste spriječili probleme s formatiranjem.", + "statusMaintenance": "Održavanje", + "General Monitor Type": "Općeniti tip Monitora", + "Maintenance": "Održavanje", + "Specific Monitor Type": "Posebni tip Monitora", + "Monitor": "Monitor | Monitori", + "Invert Keyword": "Obrni ključnu riječ", + "filterActive": "Aktivnost", + "Cannot connect to the socket server": "Nije moguće spojiti se na socket poslužitelj", + "Reconnecting...": "Ponovno povezivanje...", + "Expected Value": "Očekivana vrijednost", + "Json Query": "JSON upit", + "Help": "Pomoć", + "noGroupMonitorMsg": "Nije dostupno. Prvo kreirajte grupu Monitora.", + "Close": "Zatvori", + "Request Body": "Tijelo zahtjeva", + "wayToGetFlashDutyKey": "Za integraciju Uptime Kume s Flashdutyjem: Idite na Channels > Select a channel > Integrations > Add a new integration, odaberite Uptime Kuma i kopirajte push adresu.", + "FlashDuty Severity": "Stupanj ozbiljnosti", + "nostrRelays": "Nostr releji", + "nostrRelaysHelp": "Jedan URL releja po liniji", + "nostrSender": "Privatni ključ pošiljatelja (nsec)", + "nostrRecipients": "Javni ključevi primatelja (npub)", + "nostrRecipientsHelp": "U formatu npub, jedan ključ po liniji", + "showCertificateExpiry": "Pokaži istek certifikata", + "noOrBadCertificate": "Nepostojeći ili nevaljali certifikat", + "gamedigGuessPort": "Gamedig: Pogodi vrijednost porta", + "gamedigGuessPortDescription": "Port koji koristi Valve Server Query Protocol može se razlikovati od klijentskog porta. Pokušajte uključiti ovu opciju ako Monitor ne može uspostaviti vezu s vašim poslužiteljem.", + "Monitor Setting": "Postavka monitora korisnika {0}", + "Badge Generator": "Generator znački korisnika {0}", + "Bark API Version": "Verzija Bark API-ja", + "authInvalidToken": "Nevažeći token.", + "authIncorrectCreds": "Pogrešno korisničko ime ili lozinka.", + "2faAlreadyEnabled": "Dvofaktorska autentifikacija je već omogućena.", + "2faDisabled": "Dvofaktorska autentifikacija je onemogućena.", + "successAdded": "Uspješno dodano.", + "successPaused": "Uspješno pauzirano.", + "successEdited": "Uspješno uređeno.", + "successAuthChangePassword": "Lozinka je uspješno ažurirana.", + "successBackupRestored": "Sigurnosna kopija je uspješno vraćena.", + "successDisabled": "Uspješno onemogućeno.", + "successEnabled": "Uspješno omogućeno.", + "foundChromiumVersion": "Pronađen program Chromium ili Chrome. Inačica: {0}", + "pushViewCode": "Kako koristiti Monitor Push? (Prikaži kôd)", + "programmingLanguages": "Programski jezici", + "authUserInactiveOrDeleted": "Korisnik je neaktivan ili obrisan.", + "2faEnabled": "Dvofaktorska autentifikacija je omogućena.", + "successResumed": "Uspješno nastavljeno.", + "successDeleted": "Uspješno obrisano.", + "tagNotFound": "Oznaka nije pronađena.", + "pushOthers": "Ostali", + "Reset Token": "Poništi token", + "GrafanaOncallUrl": "URL za Grafana OnCall", + "liquidIntroduction": "Mogućnost korištenja predložaka postignuto je putem jezika Liquid. Pogledajte upute korištenja na {0}.", + "templateLimitedToUpDownCertNotifications": "dostupno samo za obavijesti dostupnosti te isteka certifikata", + "emailCustomisableContent": "Prilagodljiv sadržaj", + "smtpLiquidIntroduction": "Sljedeća dva polja mogu se izraditi putem Liquid jezika za predloške. Upute za korištenje dostupne su na {0}. Ovo su dostupne varijable:", + "leave blank for default subject": "ostavite prazno za zadano polje predmeta", + "emailCustomBody": "Prilagođeno tijelo", + "leave blank for default body": "ostavite prazno za zadani sadržaj tijela", + "emailTemplateServiceName": "Naziv servisa", + "emailTemplateHostnameOrURL": "Naziv domaćina ili URL", + "emailTemplateStatus": "Status", + "emailTemplateMonitorJSON": "objekt koji opisuje Monitor", + "emailTemplateHeartbeatJSON": "objekt koji opisuje provjeru", + "emailTemplateMsg": "poruka obavijesti", + "emailTemplateLimitedToUpDownNotification": "dostupno samo za provjere dostupnosti, inače je null", + "templateMsg": "poruka obavijesti", + "templateHeartbeatJSON": "predmet koji opisuje provjeru", + "templateMonitorJSON": "objekt koji opisuje Monitor", + "templateLimitedToUpDownNotifications": "dostupno samo za obavijesti dostupnosti", + "noDockerHostMsg": "Nije dostupno. Morate postaviti Docker hosta.", + "DockerHostRequired": "Postavite Docker hosta za ovaj Monitor.", + "Browser Screenshot": "Snimka zaslona preglednika", + "successKeyword": "Ključna riječ za uspjeh", + "successKeywordExplanation": "MQTT ključna riječ koja označava uspješan odgovor", + "remoteBrowserToggle": "Chromium se pokreće unutar Uptime Kuma kontejnera. Možete koristiti udaljeni preglednik uključivanjem ove opcije.", + "deleteRemoteBrowserMessage": "Jeste li sigurni da želite ukloniti ovaj udaljeni preglednik za sve Monitore?", + "Remote Browsers": "Udaljeni preglednici", + "settingUpDatabaseMSG": "Postavljanje baze podataka u tijeku. Ovaj postupak može potrajati, hvala na strpljenju.", + "ntfyPriorityHelptextAllEvents": "Svi događaji šalju se s maksimalnim prioritetom", + "ntfyPriorityHelptextAllExceptDown": "Svi događaji šalju se ovim prioritetom, osim {0} događaja, koji imaju prioritet {1}", + "Search monitored sites": "Pretraži monitorirane stranice", + "statusPageSpecialSlugDesc": "Poseban slug {0}: ova stranica će se pokazati kada nijedan slug nije naveden", + "openModalTo": "Otvori modal do {0}", + "Add a domain": "Dodaj domenu", + "setup a new monitor group": "Postavljanje nove grupe Monitora", + "Remove domain": "Ukloni domenu '{0}'", + "Remote Browser": "Udaljeni preglednik", + "Add a Remote Browser": "Dodaj udaljeni preglednik", + "Remote Browser not found!": "Udaljeni preglednik nije pronađen!", + "remoteBrowsersDescription": "Udaljeni preglenici alternativa su lokalnom pokretanju Chromiuma. Postavite uslugu poput browserless.io ili spojite vlastiti preglednik", + "self-hosted container": "kontejner koji je self-hosted", + "useRemoteBrowser": "Korištenje udaljenog preglednika", + "Add a new expiry notification day": "Dodaj novi dan za istek obavijesti", + "Remove the expiry notification": "Ukloni dan za istek obavijesti", + "What is a Remote Browser?": "Što je udaljeni preglednik?", + "documentationOf": "{0} dokumentacija", + "wayToGetHeiiOnCallDetails": "Postupak pribavljanja identifikatora okidača i API ključeva objašnjeno je u {documentation}", + "gtxMessagingApiKeyHint": "Svoj API ključ možete pronaći odlaskom na postavku: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "To Phone Number": "Telefonski broj primatelja", + "Originator type": "Izvorišni tip", + "Alphanumeric (recommended)": "Alfanumerički (preporučeno)", + "Destination": "Odredište", + "callMeBotGet": "Ovdje se može generirati krajnja točka za {0}, {1} i {2}. Imajte na umu da biste mogli biti ograničeni tarifom. Čini se da je trenutna tarifa: {3}", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Telefonski broj pošiljatelja / Izvorišna adresa prijenosnog puta (TPOA)", + "whapiRecipient": "Broj telefona / Identifikator kontakta / Identifikator grupe", + "API URL": "URL API-ja", + "gtxMessagingToHint": "Međunarodni format, s početnim plusom (\"+\") ({e164}, {e212} ili {e214})", + "Telephone number": "Telefonski broj", + "cellsyntOriginatortypeAlphanumeric": "Alfanumerički niz znakova (maksimalne duljine 11). Primatelji ne mogu odgovoriti na poruku.", + "cellsyntOriginatortypeNumeric": "Brojčana vrijednost (maksimalno 15 znamenki) telefonskog broja u međunarodnom formatu bez \"00\" na početku (primjerice, broj iz UK-a 07920-110-000 trebao bi biti postavljen kao 447920110000). Primatelji mogu odgovoriti na poruku.", + "Originator": "Izvorište", + "Allow Long SMS": "Dozvoli dugačke SMS poruke", + "cellsyntSplitLongMessages": "Podijeli dugačke poruke na više dijelova, do maksimalnih 6. Maksimalan broj znakova tada je 153 x 6 = 918.", + "max 15 digits": "maks. 15 znamenki", + "max 11 alphanumeric characters": "maks. 11 alfanumeričkih znakova", + "wayToWriteWhapiRecipient": "Telefonski broj s međunarodnim prefiksom, ali bez znaka plus na početku ({0}), identifikatora kontakta ({1}) ili identifikatora grupe ({2}).", + "wayToGetWhapiUrlAndToken": "Moguće je pribaviti token te URL API-ja odlaskom na željeni kanal s {0}", + "gtxMessagingFromHint": "Na mobilnim telefonima, vaši primatelji vide TPOA prikazanu kao pošiljatelja poruke. Dopušteno je do 11 alfanumeričkih znakova, kratki kod, lokalni dugi kod ili međunarodni brojevi ({e164}, {e212} ili {e214})", + "cellsyntOriginator": "Vidljivo na mobilnom telefonu primatelja kao autor poruke. Dopuštene vrijednosti i funkcija ovise o vrsti izvorišta.", + "cellsyntDestination": "Telefonski broj primatelja u međunarodnom formatu s početnim 00 iza kojeg slijedi pozivni broj države (maksimalno 17 znamenki). Primjerice, za broj iz UK-a 07920-110-000 vrijednost mora biti 00447920110000 . Maksimalno 25.000 primatelja odvojenih zarezom po HTTP zahtjevu.", + "Channel access token (Long-lived)": "Pristupni token za kanal (dugovječni)", + "Your User ID": "Vaš korisnički identifikator (ID)", + "wayToGetSevenIOApiKey": "Posjetite nadzornu ploču odlaskom na app.seven.io > Developer > API Key i dodajte novi ključ koristeći zeleni gumb za dodavanje", + "Command": "Naredba", + "mongodbCommandDescription": "Pokreni MongoDB naredbu na bazi podataka. Za informacije o dostupnim naredbama posjetite {documentation}", + "Mentioning": "Spominjanje", + "Don't mention people": "Ne spominji ljude", + "Bitrix24 Webhook URL": "URL webhooka za Bitrix24", + "wayToGetBitrix24Webhook": "Možete napraviti webhook prateći upute na {0}", + "bitrix24SupportUserID": "Unesite svoj korisnički identifikator u Bitrix24. Možete ga dobiti iz URL-a odlaskom na vlastiti profil.", + "apiKeySevenIO": "SevenIO API ključ", + "Host URL": "URL hosta", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Unesite adresu poslužitelja na koju se želite spojiti ili {localhost} ako planirate koristiti {local_mta}", + "Select message type": "Odaberite tip poruke", + "postToExistingThread": "Pošalji u postojeću temu / forumsku raspravu", + "Send to channel": "Slanje u kanal", + "Create new forum post": "Stvori novu forumsku objavu", + "whatHappensAtForumPost": "Stvori novu forumsku raspravu. Ova opcija ne dozvoljava objavu poruka u postojeću raspravu, za to je potrebno koristiti \"{option}\"", + "wayToGetDiscordThreadId": "Pronalaženje indentifikatora forumske rasprave ili objave slično je kao i za indentifikator kanala. Više o tome: {0}", + "Refresh Interval": "Interval osvježavanja", + "Refresh Interval Description": "Statusna stranica će napraviti cjelovito osvježavanje svake/ih {0} sekunde/i", + "locally configured mail transfer agent": "agent prijenosa e-pošte postavljen lokalno", + "ignoreTLSErrorGeneral": "Ignoriraj greške TLS-a/SSL-a prilikom spajanja", + "forumPostName": "Naziv forumske objave", + "e.g. {discordThreadID}": "npr. {discordThreadID}", + "threadForumPostID": "Identifikator forumske rasprave ili objave", + "Mention group": "Spomeni {group}", + "senderSevenIO": "Broj ili naziv pošiljatelja", + "receiverSevenIO": "Broj primatelja", + "receiverInfoSevenIO": "Ako broj primatelja nije registriran u Njemačkoj, potrebno je uključiti i pozivni broj države (primjerice, umjesto 023123123 potrebno je koristiti 36523123123 ako je riječ o hrvatskom broju)", + "threemaRecipient": "Primatelj", + "threemaRecipientType": "Tip pošiljatelja", + "threemaRecipientTypeIdentity": "Threema ID", + "threemaRecipientTypeIdentityFormat": "8 znakova", + "threemaRecipientTypeEmail": "Adresa e-pošte", + "threemaSenderIdentity": "ID pristupnika", + "threemaSenderIdentityFormat": "8 znakova, obično počinje znakom *", + "threemaApiAuthenticationSecret": "Tajna pristupnika", + "smspartnerApiurl": "Možete pronaći svoj API ključ na nadzornoj ploči: {0}", + "smspartnerPhoneNumber": "Broj(evi) telefona", + "smspartnerPhoneNumberHelptext": "Broj mora biti u međunarodnom formatu {0}, {1}. Višestruki unosi moraju biti odvojeni znakom {2}", + "smspartnerSenderName": "Naziv SMS pošiljatelja", + "apiKeysDisabledMsg": "API ključevi su onemogućeni jer je provjera autentičnosti onemogućena.", + "wayToGetThreemaGateway": "Možete se registrirati za uslugu Threema Gateway {0}.", + "threemaRecipientTypePhone": "Telefonski broj", + "threemaRecipientTypePhoneFormat": "E.164, bez vodećeg znaka +", + "threemaBasicModeInfo": "Napomena: Ova integracija koristi Threema Gateway u osnovnom načinu rada (enkripcija temeljena na poslužitelju). Dodatne pojedinosti možete pronaći na {0}.", + "smspartnerSenderNameInfo": "Mora biti između 3 i 11 znakova", + "shrinkDatabaseDescriptionSqlite": "Pokreni {vacuum} baze podataka za SQLite. Opcija {auto_vacuum} je već omogućena, ali to ne defragmentira bazu podataka niti ponovno pakira pojedinačne stranice baze podataka na način na koji to radi naredba {vacuum}.", + "ignoredTLSError": "TLS/SSL greške se ignoriraju", + "cacheBusterParam": "Dodaj parametar {0}", + "cacheBusterParamDescription": "Nasumično generirani parametar, za preskakanje predmemorije.", + "snmpCommunityStringHelptext": "Ovaj niz funkcionira kao lozinka za provjeru autentičnosti i kontrolu pristupa uređajima s omogućenim SNMP-om. Uskladite ga sa svojom konfiguracijom SNMP uređaja.", + "privateOnesenderDesc": "Provjerite je li broj telefona valjan. Za slanje poruke na privatni telefonski broj, npr. 628123456789", + "Go back to home page.": "Vratite se na početnu stranicu.", + "signl4Docs": "Više informacija o tome kako konfigurirati SIGNL4 i kako dobiti SIGNL4 URL webhooka možete pronaći na {0}.", + "not starts with": "ne počinje s", + "less than or equal to": "manje od ili jednako", + "Doorbell": "Zvono na vratima", + "Custom sound to override default notification sound": "Prilagođeni zvuk za zamjenu zadanog zvuka obavijesti", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Vremenski osjetljive obavijesti bit će isporučene odmah, čak i ako je uređaj u načinu rada bez ometanja.", + "now": "sada", + "time ago": "prije {0}", + "-year": "-godišnje", + "Json Query Expression": "Upit u JSON obliku", + "Community String": "Zajednički niz teksta", + "conditionAddGroup": "Dodaj grupu", + "conditionDeleteGroup": "Obriši grupu", + "Debug": "Otklanjanje grešaka", + "Copy": "Kopirati", + "CopyToClipboardError": "Greška pri kopiranju u međuspremnik: {error}", + "CopyToClipboardSuccess": "Kopirano!", + "dns resolvers": "DNS razrješivači", + "firewalls": "vatrozidi", + "CurlDebugInfo": "Za otklanjanje grešaka u Monitoru, možete zalijepiti ovo u terminal na vlastitom računalu ili računalu na kojem se Uptime Kuma pokreće, kako biste isprobali Vaš mrežni zahtjev.{newiline}Budite svjesni mrežnih razlika koje mogu činiti {firewalls}, {dns_resolvers} ili {docker_networks}.", + "docker networks": "Dockerove mreže", + "CurlDebugInfoOAuth2CCUnsupported": "Potpuni tok vjerodajnica klijenta OAuth nije podržan u {curl}.{newline}Nabavite token nositelja i proslijedite ga koristeći opciju {oauth2_bearer}.", + "CurlDebugInfoProxiesUnsupported": "Proxy podrška u gornjoj naredbi {curl} trenutno nije implementirana.", + "and": "i", + "Message format": "Format poruke", + "Send rich messages": "Slanje poruka s obogaćenim tekstom", + "OID (Object Identifier)": "OID (Identifikator objekta)", + "snmpOIDHelptext": "Unesite OID za senzor ili status kojeg želite monitorirati. Koristite alate za upravljanje mrežom poput MIB preglednika ili SNMP programa ako niste sigurni koja je vrijednost OID-a.", + "Condition": "Uvjet", + "SNMP Version": "Inačica SNMP-a", + "Please enter a valid OID.": "Unesite važeći OID.", + "Recipient Type": "Tip primatelja", + "Private Number": "Privatni broj", + "wayToGetOnesenderUrlandToken": "URL i token možete dobiti odlaskom na OneSender web stranicu. Više informacija na {0}", + "Token Onesender": "OneSender Token", + "Host Onesender": "Adresa OneSender hosta", + "Group ID": "Identifikator Grupe", + "groupOnesenderDesc": "Provjerite je li Identifikator Grupe valjan. Za slanje poruke na Grupu, npr. 628123456789-342345", + "Add Remote Browser": "Dodaj udaljeni preglednik", + "New Group": "Nova grupa", + "Group Name": "Naziv grupe", + "OAuth2: Client Credentials": "OAuth2: vjerodajnice klijenta", + "Authentication Method": "Metoda provjere autentičnosti", + "Authorization Header": "Zaglavlje autorizacije", + "Form Data Body": "Tijelo podataka obrasca", + "OAuth Token URL": "URL OAuth tokena", + "Client ID": "Klijentski identifikator", + "Client Secret": "Klijentska tajna", + "OAuth Scope": "OAuth opseg", + "Optional: Space separated list of scopes": "Neobavezno: popis opsega odvojen razmakom", + "No tags found.": "Nema pronađenih oznaka.", + "Lost connection to the socket server.": "Izgubljena veza sa socket poslužiteljem.", + "Cannot connect to the socket server.": "Nije moguće spojiti se na socket poslužitelj.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 URL webhooka", + "Conditions": "Uvjeti", + "conditionAdd": "Dodaj uvjet", + "conditionDelete": "Obriši uvjet", + "conditionValuePlaceholder": "Vrijednost", + "equals": "je jednako", + "not equals": "nije jednako", + "contains": "sadržava", + "not contains": "ne sadržava", + "starts with": "počinje s", + "ends with": "završava s", + "not ends with": "ne završava s", + "less than": "manje od", + "greater than": "veće od", + "greater than or equal to": "veće od ili jednako", + "record": "zapis", + "Notification Channel": "Kanal obavijesti", + "Sound": "Zvuk", + "Alphanumerical string and hyphens only": "Samo alfanumerički niz i crtice", + "Arcade": "Arkadno", + "Fail": "Neuspjeh", + "Correct": "Ispravno", + "Harp": "Harfa", + "Reveal": "Otkrivanje", + "Bubble": "Mjehurić", + "Flute": "Flauta", + "Money": "Novac", + "Scifi": "Znanstvena fantastika", + "Clear": "Čisto", + "Elevator": "Dizalo", + "Guitar": "Gitara", + "Pop": "Pop", + "Time Sensitive (iOS Only)": "Vremenski osjetljivo (samo iOS)", + "From": "Od", + "Can be found on:": "Može se pronaći na: {0}", + "The phone number of the recipient in E.164 format.": "Telefonski broj primatelja u formatu E.164.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Ili identifikator pošiljatelja tekstualne poruke ili telefonski broj u formatu E.164 ako želite primati odgovore.", + "jsonQueryDescription": "Izvršite JSON upit nad primljenim odgovorom i provjerite očekivanu povratnu vrijednost. Koristite \"$\" za zahtjeve u kojima ne očekujete JSON odgovor. Povratna vrijednost će se za usporedbu pretvoriti u niz znakova (string). Pogledajte stranicu {0} za dokumentaciju o jeziku upita. Testno okruženje možete pronaći na {1}.", + "rabbitmqNodesRequired": "Postavite čvorove za ovaj Monitor.", + "rabbitmqNodesInvalid": "Koristite potpuni URL (onaj koji počinje s 'http') za RabbitMQ čvorove.", + "RabbitMQ Username": "RabbitMQ korisničko ime", + "RabbitMQ Password": "RabbitMQ lozinka", + "SendGrid API Key": "SendGrid API ključ", + "Separate multiple email addresses with commas": "Više adresa e-pošte potrebno je odvojiti zarezima", + "RabbitMQ Nodes": "RabbitMQ upravljački čvorovi", + "rabbitmqNodesDescription": "Unesite URL za upravljačke čvorove RabbitMQ uključujući protokol i port. Primjer: {0}", + "rabbitmqHelpText": "Za korištenje ovog Monitora morat ćete omogućiti dodatak \"Management Plugin\" u svom RabbitMQ-u. Za više informacija pogledajte {rabitmq_documentation}.", + "aboutSlackUsername": "Mijenja ime pošiljatelja vidljivo svima ostalima.", + "templateServiceName": "naziv servisa", + "telegramUseTemplate": "Koristi prilagođeni predložak poruke", + "telegramTemplateFormatDescription": "Telegram dozvoljava korištenje različitih markup jezika za formatiranje poruka, pogledajte {0} za više detalja.", + "YZJ Robot Token": "YZJ token robota", + "YZJ Webhook URL": "YZJ URL webhooka", + "templateHostnameOrURL": "naziv hosta ili URL", + "templateStatus": "status", + "telegramUseTemplateDescription": "Ako je omogućeno, poruka će biti poslana koristeći prilagođeni predložak.", + "Plain Text": "Obični tekst", + "Message Template": "Predložak poruke", + "Template Format": "Format predloška", + "wahaSession": "Sjednica", + "wahaChatId": "Identifikator razgovora (telefonski broj / ID kontakta / ID grupe)", + "wayToGetWahaApiUrl": "URL instance WAHA.", + "wayToGetWahaApiKey": "API ključ je vrijednost varijable okruženja WHATSAPP_API_KEY koju ste koristili za pokretanje servisa WAHA.", + "wayToGetWahaSession": "Iz ove sjednice WAHA šalje obavijesti na identifikator razgovora. Može se pronaći na WAHA nadzornoj ploči.", + "wayToWriteWahaChatId": "Telefonski broj s međunarodnim prefiksom, ali bez znaka plus na početku ({0}), identifikator kontakta ({1}) ili identifikator grupe ({2}). Obavijesti se šalju na ovaj identifikator chata iz WAHA sesije.", + "telegramServerUrl": "(Neobvezno) URL Poslužitelja", + "telegramServerUrlDescription": "Za ukidanje ograničenja API-ja za botove Telegrama ili dobivanje pristupa u blokiranim područjima (Kina, Iran, itd.). Za više informacija kliknite {0}. Zadano: {1}", + "Font Twemoji by Twitter licensed under": "Font Twemoji tvrtke X je pod licencom", + "the smsplanet documentation": "dokumentaciji usluge SMSPLANET", + "Phone numbers": "Brojevi telefona", + "Sender name": "Naziv pošiljatelja", + "smsplanetNeedToApproveName": "Potrebno je odobrenje u klijentskoj nadzornoj ploči", + "smsplanetApiToken": "Token za pristup SMSPLANET API-ju", + "smsplanetApiDocs": "Detaljne informacije o dobivanju tokena za API možete pronaći u {the_smsplanet_documentation}.", + "Happy Eyeballs algorithm": "algoritam Happy Eyeballs", + "Add Another Tag": "Dodaj novu oznaku", + "Staged Tags for Batch Add": "Pripremljene oznake za skupno dodavanje", + "pause": "Pauziraj", + "Use HTML for custom E-mail body": "Koristiti HTML za prilagođeno tijelo e-maila", + "smseagleGroupV2": "Identifikator(i) grupe telefonskog imenika", + "smseagleMsgType": "Tip poruke", + "smseagleMsgTtsAdvanced": "Napredni poziv s pretvaranjem teksta u govor", + "smseagleApiv1": "APIv1 (za postojeće projekte i povratnu kompatibilnost)", + "pingGlobalTimeoutDescription": "Ukupno vrijeme trajanja slanja (u sekundama), bez obzira na broj paketa", + "smtpHelpText": "'SMTPS' opcija provjerava radi li SMTP/TLS; 'Ignore TLS' se povezuje koristeći običan tekst; 'STARTTLS' dovodi do izdavanja naredbe STARTTLS i provjere certifikata poslužitelja. Ništa od ovoga ne šalje e-poštu.", + "Ip Family": "IP verzija", + "ipFamilyDescriptionAutoSelect": "Koristi {happyEyeballs} za određivanje IP verzija.", + "Clear Form": "Očisti formu", + "defaultFriendlyName": "Novi Monitor", + "ntfyPriorityHelptextPriorityHigherThanDown": "Regularni prioritet treba biti veći od prioriteta {0}. Prioritet {1} je veći od prioriteta {0} {2}", + "ntfyPriorityDown": "Prioritet za incidente ispada", + "pingPerRequestTimeoutDescription": "Maksimalno vrijeme čekanja (u sekundama) prije nego što se pojedini paket smatra izgubljenim", + "Add Tags": "Dodaj oznake", + "tagAlreadyOnMonitor": "Ova oznaka (naziv i vrijednost) već je dodana na Monitor ili je njeno dodavanje na čekanju.", + "tagNameExists": "Već postoji sistemska oznaka s ovim nazivom. Odaberite ju iz popisa ili koristite drugi naziv.", + "tagAlreadyStaged": "Ova oznaka (naziv i vrijednost) već je pripremljena za ovaj skup Monitora.", + "smseagleContactV2": "Identifikator(i) kontakta telefonskog imenika", + "smseagleMsgSms": "SMS poruka (zadano)", + "smseagleMsgRing": "Poziv sa zvonom", + "smseagleMsgTts": "Poziv s pretvaranjem teksta u govor", + "smseagleDuration": "Trajanje (u sekundama)", + "smseagleTtsModel": "Identifikator modela za pretvaranje teksta u govor", + "smseagleApiType": "Inačica API-ja", + "smseagleApiv2": "APIv2 (preporučeno za nove integracije)", + "smseagleDocs": "Provjeriti dokumentaciju i dostupnost verzije APIv2: {0}", + "smseagleComma": "Višestruki izbori moraju se odvojiti zarezom", + "SpugPush Template Code": "Kôd predloška", + "FlashDuty Push URL": "Push adresa", + "FlashDuty Push URL Placeholder": "Kopirati sa stranice za integraciju", + "pingCountLabel": "Maks. paketa", + "pingCountDescription": "Ukupan broj paketa koji će se poslati", + "pingNumericLabel": "Brojčani ispis", + "pingNumericDescription": "Ako je odabrano, bit će ispisane IP adrese umjesto naziva hostova", + "pingGlobalTimeoutLabel": "Globalno vrijeme čekanja", + "pingPerRequestTimeoutLabel": "Vrijeme čekanja za jedan ping", + "pingIntervalAdjustedInfo": "Interval koji se prilagođava broju paketa, globalnom vremenu čekanja i vremenu čekanja jednog pinga", + "Custom URL": "Prilagođena adresa", + "customUrlDescription": "Koristit će se kao adresa na koju monitor vodi, umjesto adrese Monitora.", + "OneChatAccessToken": "OneChat pristupni token", + "OneChatUserIdOrGroupId": "OneChat identifikator korisnika ili grupe", + "OneChatBotId": "OneChat identifikator bota", + "Disable URL in Notification": "Onemogući URL u obavijesti", + "Manual": "Ručno", + "OAuth Audience": "OAuth publika", + "Optional: The audience to request the JWT for": "Neobavezno: Publika za koju se traži JWT", + "mqttWebSocketPath": "Putanja MQTT WebSocketa", + "mqttHostnameTip": "Koristiti ovaj format {hostnameFormat}", + "Path": "Putanja", + "mqttWebsocketPathExplanation": "Putanja WebSocketa za MQTT preko WebSocket veza (npr. /mqtt)", + "mqttWebsocketPathInvalid": "Koristiti valjani format putanje WebSocketa", + "Template plain text instead of using cards": "Predložak običnim tekstom umjesto korištenja kartica", + "issueWithGoogleChatOnAndroidHelptext": "Ovo omogućuje izbjegavanje grešaka poput {issuetackerURL}", + "clearAllEventsMsg": "Jeste li sigurni da želite obrisati sve događaje sa svih Monitora? Ovo će trajno ukloniti podatke o porukama i ne može se poništiti.", + "No monitors found": "Nema pronađenih monitora.", + "Could not clear events": "Nije bilo moguće obrisati {failed} od {total} događaja", + "Clear All Events": "Očisti sve događaje", + "Events cleared successfully": "Događaji su uspješno obrisani.", + "wayToWriteEvolutionRecipient": "Telefonski broj s međunarodnim prefiksom, ali bez znaka plusa na početku ({0}), identifikator kontakta ({1}) ili identifikator grupe ({2}).", + "wayToGetEvolutionUrlAndToken": "URL za API te token možete dobiti tako da otvorite željeni kanal preko {0}", + "evolutionRecipient": "Telefonski broj / identifikator kontakta / identifikator grupe", + "evolutionInstanceName": "Naziv instance", + "brevoApiHelp": "Kreiranje API ključa: {0}", + "brevoFromName": "Naziv pošiljatelja", + "brevoCcEmail": "Adrese za Cc", + "brevoBccEmail": "Adrese za Bcc", + "brevoToEmail": "Adresa primatelja", + "brevoFromEmail": "Adresa pošiljatelja", + "brevoSeparateMultipleEmails": "odvojiti adrese zarezima", + "brevoSubject": "Predmet e-pošte", + "brevoApiKey": "Brevo API ključ", + "brevoLeaveBlankForDefaultName": "ostaviti prazno za zadani naziv", + "brevoLeaveBlankForDefaultSubject": "ostaviti prazno za korištenje zadang teksta predmeta", + "Nextcloud host": "Nextcloud ime hosta", + "Conversation token": "Token razgovora", + "Bot secret": "Tajna bota", + "Send UP silently": "Pošalji UP utišano", + "auto-select": "Automatski odabir", + "Send DOWN silently": "Pošalji DOWN utišano", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Instaliranje Nextcloud Talk bota zahtijeva administratorski pristup poslužitelju.", + "Number of retry attempts if webhook fails": "Broj ponovnih pokušaja (svakih 60-180 sekundi) ako webhook ne uspije.", + "Maximum Retries": "Maksimalan broj ponovnih pokušaja", + "HTTP Method": "HTTP metoda", + "webhookPostMethodDesc": "POST je dobar za većinu modernih HTTP poslužitelja.", + "Invalid userId": "Nevažeći korisnički ID [{userId}]", + "supportBaleChatID": "Podržan ID izravnoga razgovora / grupe / kanala", + "wayToGetBaleChatID": "Svoj ID razgovora (chat_id) možete dobiti slanjem poruke botu i odlaskom na ovaj URL:", + "webhookGetMethodDesc": "GET šalje podatke kao parametre upita i ne dopušta konfiguriranje tijela zahtjeva. Korisno za okidanje Uptime Kuma Push monitora.", + "descriptionHelpText": "Prikazuje se na internoj nadzornoj ploči. Markdown je dopušten i sanitiziran (čuva razmake i uvlačenja) prije prikazivanja.", + "wayToGetBaleToken": "Token možete dobiti na {0}.", + "Mention Mobile List": "Popis mobilnih uređaja za Mention", + "Mention User List": "Popis korisničkih ID-jeva za Mention", + "Dingtalk Mobile List": "Popis mobilnih uređaja", + "Dingtalk User List": "Popis korisničkih ID-jeva", + "Enter a list of userId": "Unesite popis korisničkih ID-jeva", + "Enter a list of mobile": "Unesite popis mobilnih uređaja", + "Invalid mobile": "Nevažeći mobitel [{mobile}]", + "Recipient Numbers": "Brojevi primatelja", + "deleteChildrenMonitors": "Također izbriši podređene monitore i njihovu djecu ako takvi postoje | Također izbriši sve {count} podređene monitore i njihovu djecu ako takvi postoje", + "Template ID": "Identifikator predloška", + "wayToGetClickSMSIRTemplateID": "Predložak mora sadržavati polje {uptkumaalert}. Novi predložak može se stvoriti {ovdje}.", + "deleteGroupMsg": "Jeste li sigurni da želite izbrisati ovu grupu?", + "Clone Maintenance": "Kloniraj održavanje", + "ariaPauseMaintenance": "Pauziraj ovaj raspored održavanja", + "ariaResumeMaintenance": "Pokreni ovaj raspored održavanja", + "ariaCloneMaintenance": "Stvori kopiju ovog rasporeda održavanja", + "ariaEditMaintenance": "Uredi ovaj raspored održavanja", + "ariaDeleteMaintenance": "Obriši ovaj raspored održavanja", + "twilioApiKeyHelptext": "API ključ je neobvezan, ali se preporučuje. Možete navesti ili SID računa i AuthToken s Twilio konzole ili SID računa te API ključ s tajnom", + "twilioMessagingServiceSID": "SID usluge za slanje poruka (neobvezno)", + "twilloMessagingServiceSIDHelptext": "Ovdje unesite SID svoje usluge za slanje poruka ako koristite {twillo_messaging_service_help_link} za upravljanje pošiljateljima i ostalim značajkama", + "enableSSL": "Omogući SSL/TLS", + "mariadbUseSSLHelptext": "Omogućiti za korištenje šifrirane veze do bate. Postavka je nužna za većinu baza u oblaku.", + "mariadbCaCertificateLabel": "CA certifikat", + "teltonikaUsername": "Korisničko ime za API", + "teltonikaUsernameHelptext": "Preporuka: izradite zaseban račun koji je ograničen samo na slanje SMS poruka i ovdje unesite njegovo korisničko ime", + "Teltonika SMS Gateway": "Teltonika SMS pristupnik", + "teltonikaVersionWarning": "Ovaj pružatelj obavijesti zahtijeva da vaš Teltonika uređaj koristi RMS verziju 7.14.0 ili noviju.", + "teltonikaUrl": "URL vašeg Teltonika uređaja", + "teltonikaUrlHelptext": "URL treba biti naveden kao puni origin, npr. {0} ili {1}.", + "teltonikaUnsafeTlsDescription": "Isključivanjem provjere valjanosti TLS certifikata otvarate se za man-in-the-middle napade, što potencijalno može dovesti do curenja podataka i ostalih napada. Ne isključujte provjeru valjanosti certifikata osim ako prihvaćate ovaj rizik.", + "teltonikaPassword": "Lozinka za API", + "teltonikaUnsafeTls": "Zanemari provjeru valjanosti certifikata", + "Show this Maintenance Message on which Status Pages": "Na kojim statusnim stranicama prikazati ovu poruku o održavanju", + "Expand All Groups": "Proširi sve grupe", + "discordMessageFormat": "Format poruke", + "GlobalpingLocationDocs": "Dokumentacija za unos lokacije", + "labelDomainNameExpiryNotification": "Obavijest o isteku domene", + "minimumIntervalWarning": "Intervali kraći od 20 sekundi mogu dovesti do loših performansi.", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "URL webhooka za Halo PSA", + "username": "Korisničko ime", + "halopsa_setup_step3": "Kopirajte URL webhooka i zalijepite ga u polje iznad", + "Clear current filters": "Obriši trenutne filtere", + "Sort by status": "Sortiraj po statusu", + "Sort by name": "Sortiraj po imenu", + "Sort by uptime": "Sortiraj po vremenu rada", + "Severity": "Ozbiljnost", + "SSL/TLS": "SSL/TLS", + "slackIncludeGroupName": "Uključi naziv grupe monitora", + "unknownDays": "Nepoznat broj dana", + "No incidents recorded": "Nema zabilježenih incidenata", + "Load More": "Učitaj više", + "Loading...": "Učitavanje...", + "Monitors": "{n} Monitor | {n} Monitora", + "days": "{n} dan | {n} dana", + "minutes": "{n} minuta | {n} minuta", + "minuteShort": "{n} min | {n} min", + "years": "{n} godina | {n} godina", + "Pin this incident": "Prikvači ovaj incident", + "ignoreSecWebsocketAcceptHeaderDescription": "Omogućuje poslužitelju da ne odgovori sa zaglavljem Sec-WebSocket-Accept ako nadogradnja websocketa uspije.", + "Ignore Sec-WebSocket-Accept header": "Ignoriraj {0} zaglavlje", + "wsSubprotocolDescription": "Unesite popis podprotokola odvojen zarezima. Za više informacija o podprotokolima pogledajte {documentation}", + "wsCodeDescription": "Za više informacija o statusnim kodovima, pogledajte {rfc6455}", + "Subprotocol(s)": "Podprotokol(i)", + "saveResponseForNotifications": "Spremi uspješni HTTP odgovor za obavijesti", + "saveErrorResponseForNotifications": "Spremi HTTP odgovor s greškom za obavijesti", + "responseMaxLength": "Maksimalna duljina odgovora (u oktetima)", + "responseMaxLengthDescription": "Maksimalna veličina podataka odgovora za pohranu. Postavite na 0 za neograničenu veličinu. Veći odgovori bit će skraćeni. Zadano: 1024 (1 KiB)", + "Incident not found or access denied": "Incident nije pronađen ili je pristup odbijen", + "Incident title": "Naslov incidenta", + "Resolver Server(s)": "DNS poslužitelj", + "Edit Incident": "Uredi incident", + "example": "Primjer", + "Result": "Rezultat", + "BodyInvalidFormatBecause": "Tijelo zahtjeva nije valjani JSON. Opis pogreške: {error}", + "invalidDNSHostname": "Nevažeći naziv hosta. Naziv hosta mora biti valjani FQDN. Može sadržavati zamjenski znak (engl. wildcard), imati podcrtu ili završavati točkom.", + "wildcardOnlyForDNS": "Nazivi hostova sa zamjenskim znakom (engl. wildcard) podržani su samo za DNS monitore.", + "invalidURL": "Nevažeći URL", + "Resolve": "Razriješi", + "Resolved": "Razriješeno", + "createdAt": "Stvoreno: {date}", + "lastUpdatedAt": "Zadnje ažurirano: {date}", + "lastUpdatedAtFromNow": "Zadnje ažurirano: {date} ({fromNow})", + "Actions": "Akcije", + "selectedMonitorCountMsg": "odabrano: {n} | odabrano: {n}", + "selectMonitorMsg": "Odaberite Monitore za izvođenje akcija", + "deselectAllMonitorsAria": "Poništi odabir svih Monitora", + "deleteIncidentMsg": "Jeste li sigurni da želite izbrisati ovaj incident?", + "slug is not found": "Slug nije pronađen", + "Please input title": "Unesite naslov", + "dateCreatedAtFromNow": "Stvoreno: {date} ({fromNow})", + "RSS Title": "Naslov RSS-a", + "Cloud ID": "Cloud ID", + "frontendVersionIs": "Frontend inačica: {version}", + "Examples:": "Primjeri: {0}", + "Duration (Minutes)": "Trajanje (u minutama)", + "cronScheduleDescription": "Raspored: {description}", + "notificationChatPlatforms": "Platforme za chat", + "notificationPushServices": "Push usluge", + "notificationSmsServices": "SMS usluge", + "notificationEmail": "E-pošta", + "Please set start time first": "Prvo postavite vrijeme početka", + "Quick Setup Guide": "Vodič za brzo postavljanje", + "Open your Google Spreadsheet": "Otvorite svoju Google proračunsku tablicu", + "checkPriceAt": "Provjerite cijene {service} na {url}", + "OptionalParameters": "Neobavezni parametri", + "Screenshot Delay": "Odgoda snimke zaslona (čekanje {milliseconds})", + "snmpV3Username": "SNMPv3 korisničko ime", + "Select All": "Odaberi sve", + "Deselect All": "Poništi odabir svega", + "mtls-auth-server-cert-label": "Certifikat", + "TLS Alert Spec": "RFC 8446", + "mariadbCaCertificateHelptext": "Zalijepite CA certifikat u PEM formatu za korištenje sa samopotpisanim certifikatima. Ostavite prazno ako vaša baza podataka koristi certifikat potpisan od strane javnog CA.", + "GlobalpingResolverInfo": "IPv4/IPv6 adresa ili potpuno kvalificirani naziv domene (FQDN). Zadano je lokalni DNS posljužitelj sonde. DNS poslužitelja možete promijeniti u bilo kojem trenutku.", + "hours": "{n} sat | {n} sati", + "RecordMatch": "Podudaranje vrijednosti zapisa", + "Only retry if status code check fails": "Ponovno pokušati samo ako provjera statusnog koda ne uspije", + "imageResetConfirmation": "Slika vraćena na zadane vrijednosti", + "retryOnlyOnStatusCodeFailureDescription": "Ako je omogućeno, ponovni pokušaji će se dogoditi samo kada provjera HTTP statusnog koda ne uspije (npr. poslužitelj je u kvaru). Ako provjera statusnog koda prođe, ali JSON upit ne uspije, Monitor će odmah biti označen kao nedostupan bez ponovnih pokušaja.", + "halopsa_setup_step4": "Odaberite Basic autentifikaciju i kreirajte korisničko ime i lozinku. Zatim upišite ili zalijepite korisničko ime i lozinku u polja iznad", + "saveResponseDescription": "Pohranjuje HTTP odgovor i čini ga dostupnim preko predložaka unutar obavijesti kao {templateVariable}", + "logoutCurrentUser": "Odjava {username}", + "Incident description": "Opis incidenta", + "Past Incidents": "Prošli incidenti", + "Pinned incidents are shown prominently on the status page": "Prikvačeni incidenti istaknuti su na statusnoj stranici", + "templateAvailableVariables": "Dostupne varijable", + "HeadersInvalidFormatBecause": "Zaglavlja zahtjeva nisu valjani JSON. Opis pogreške: {error}", + "steamApiKeyDescriptionAt": "Za monitoriranje Steam poslužitelja za igre potreban vam je Steam Web-API ključ. Svoj API ključ možete registrirati na {url}", + "hostnameCannotBeIP": "DNS naziv hosta ne može biti IP adresa. Jeste li htjeli koristiti polje DNS poslužitelja?", + "invalidHostnameOrIP": "Nevažeći naziv hosta ili IP adresa. Naziv hosta mora biti valjani FQDN. Ne može se koristiti zamjenski znak. Može sadržavati podcrtu ili završavati točkom.", + "selectAllMonitorsAria": "Odaberi sve Monitore", + "Certificate Chain:": "Lanac certifikata:", + "Please input content": "Unesite sadržaj", + "Leave blank to use status page title": "Ostaviti prazno za korištenje naslova statusne stranice", + "disableSTARTTLSDescription": "Odaberite ovu opciju za SMTP poslužitelje koji ne podržavaju STARTTLS. Ovo će slati e-poruke putem nešifrirane veze.", + "Sets end time based on start time": "Postavlja vrijeme završetka na temelju vremena početka", + "noMonitorsSelectedWarning": "Izrađujete održavanje bez ikakvih zahvaćenih monitora. Jeste li sigurni da želite nastaviti?", + "Google Apps Script Webhook URL": "URL webhooka za Google Apps Script", + "Deploy a Google Apps Script as a web app and paste the URL here": "Implementirajte Google Apps Script kao web aplikaciju i zalijepite URL ovdje", + "Copy the web app URL and paste it above": "Kopirajte URL web aplikacije i zalijepite ga iznad", + "You can divide numbers with commas or semicolons": "Brojeve možete odvojiti s {comma} ili {semicolon}", + "aliyun-template-requirements-and-parameters": "Predložak za Aliyun SMS mora sadržavati parametre: {parameters}", + "WeCom Mentioned Mobile List": "WeCom popis mobilnih uređaja za spominjanje", + "WeCom Mentioned Mobile List Description": "Unesite telefonske brojeve koje želite spomenuti. Odvojite više brojeva zarezima. Koristite {'@'}all da biste spomenuli sve.", + "Analytics Type": "Tip analitika", + "ntfyCallHelptext": "Broj se poziva kada se oglasi upozorenje. Postavite na 'da' kako biste koristili svoj prvi potvrđeni broj ili unesite određeni telefonski broj (npr. +12223334444). Zahtijeva ntfy Pro i potvrđeni telefonski broj.", + "API Token": "Token za API", + "See Jira Cloud Docs": "Pogledajte Jira Cloud dokumentaciju", + "certificateExpiryNotificationHelp": "Broj dana može se konfigurirati u postavkama.", + "certHostnameMismatch": "Naziv hosta u certifikatu ne odgovara URL-u Monitora.", + "sipsakPingWarning": "Za korištenje Monitora SIP opcija, Uptime Kuma mora biti instalirana bez Dockera sa Sipsak klijentom na istom poslužitelju.", + "notificationIncidentManagement": "Upravljanje incidentima", + "notificationHomeAutomation": "Automatizacija doma", + "notificationOther": "Ostale integracije", + "SMTP Security": "SMTP sigurnost", + "Ignore STARTTLS": "Ignoriraj STARTTLS", + "Use STARTTLS": "Korsti STARTTLS", + "deleteMonitorsMsg": "Jeste li sigurni da želite izbrisati odabrane Monitore?", + "deletedMonitorsMsg": "Obrisan {n} monitor | Obrisano {n} monitora", + "noMonitorsPausedMsg": "Nijedan Monitor nije pauziran (nijedan nije bio aktivan)", + "noMonitorsResumedMsg": "Nijedan Monitor nije pokrenut (svi su već aktivni)", + "bulkDeleteErrorMsg": "Brisanje {n} monitora nije uspjelo | Brisanje {n} monitora nije uspjelo", + "noMonitorsOrStatusPagesSelectedError": "Nije moguće kreirati održavanje bez zahvaćenih monitora ili stranica sa statusom", + "Paste the script code (see below)": "Zalijepite kod skripte (vidi dolje)", + "Go to Extensions → Apps Script": "Idite na Extensions → Apps Script", + "Click Deploy → New deployment → Web app": "Kliknite na Deploy → New deployment → Web app", + "Google Apps Script Code": "Kôd skripte za Google Apps", + "Copy to Clipboard": "Kopiraj u međuspremnik", + "Copied to clipboard!": "Kopirano u međuspremnik!", + "Failed to copy to clipboard": "Kopiranje u međuspremnik nije uspjelo", + "Webhook Payload Fields": "Polja za sadržaj webhooka", + "halopsa_payload_desc": "Sljedeća polja šalju se vašem Halo PSA webhooku:", + "halopsa_field_monitor": "Naziv Monitora", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Zbog ograničenja operatera, opcionalne varijable omogućujete prihvaćajući rizik neisporuke", + "aliyun-template-optional-parameters": "Neobavezni parametri: {parameters}", + "halopsa_field_title": "Naslov upozorenja (uvijek 'Uptime Kuma Alert')", + "slackUseTemplate": "Koristi prilagođeni predložak poruke", + "aboutJiraCloudId": "Više informacija o Jira Cloud ID-u: {0}", + "see Jira Cloud Docs": "pogledati Jira Cloud dokumentaciju", + "slackIncludeGroupNameDescription": "Ako je omogućeno, grupa Monitora bit će uključena u obavijesti. Tako se mogu razlikovati Monitori s istim nazivom, ali različitim grupama.", + "slackUseTemplateDescription": "Ako je omogućeno, poruka će biti poslana s prilagođenim predloškom. Možete koristiti Liquid predloške za dodavanje informacija o grupi monitora koristeći monitorJSON.path ili monitorJSON.pathName.", + "serwersmsRecipientType": "Tip primatelja", + "serwersmsRecipientTypePhone": "Telefonski broj", + "Analytics Script URL": "URL skripte za analitiku", + "screenshotDelayDescription": "Po želji čeka ovoliko milisekundi prije snimanja zaslona. Maksimalno: {maxValueMs} ms (0,5 × interval).", + "resendLeaveBlankForDefaultSubject": "ostaviti prazno za zadani predmet", + "resendToEmail": "Adresa e-pošte primatelja", + "resendSubject": "Predmet", + "halopsa_setup_step1": "Izradite Integration Runbook u HaloPSA-u (Configuration → Integrations → Integration Runbooks)", + "Sort options": "Opcije sortiranja", + "End": "Kraj", + "teltonikaModem": "Identifikator modema", + "teltonikaPhoneNumber": "Telefonski broj", + "halopsa_setup_step5": "Konfigurirajte runbook za korištenje monitor_id-a za usklađivanje upozorenja s postojećim ticketima", + "teltonikaModemHelptext": "Identifikator SMS modema mora biti u formatu {0}. Za upute pogledajte https://developers.teltonika-networks.com/reference/.", + "teamsEnableTags": "Uključi oznake", + "teamsEnableTagsDescription": "Ako je omogućeno, poruka će sadržavati oznake Monitora.", + "pausedMonitorsMsg": "Pauziran {n} monitor | Pauzirano {n} monitora", + "resumedMonitorsMsg": "Pokrenut {n} monitor | Pokrenuto {n} monitora", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Postavite 'Execute as: Me' te 'Who has access: Anyone'", + "notificationUniversal": "Univerzalna", + "versionIs": "Inačica: {version}", + "mtls-auth-server-ca-label": "CA", + "smscTranslit": "SMSC Translit", + "matrixUseTemplateDescription": "Ako je omogućeno, poruka će biti poslana pomoću prilagođenog predloška.", + "matrixUseTemplate": "Koristi prilagođeni predložak poruke", + "serwersmsRecipientTypeGroup": "Grupa", + "serwersmsGroupId": "Identifikator grupe", + "serwersmsGroupIdHelptext": "Identifikatori korisnika ili grupa u Kontrolnoj ploči. Ove identifikatore možete preuzeti pomoću grupa akcija / indeksa ili kopiranjem iz grupe za uređivanje u Kontrolnoj ploči.", + "signalUseTemplate": "Koristi prilagođeni predložak poruke", + "signalUseTemplateDescription": "Ako je omogućeno, poruka će biti poslana s prilagođenim predloškom. Možete koristiti Liquid predloške za prilagodbu poruke.", + "octopushEndpoint": "octopush (krajnja točka: {url})", + "legacyOctopushEndpoint": "Zastarijeli Octopush-DM (krajnja točka: {url})", + "Analytics ID": "Identifikator analitika", + "ntfyCall": "Telefonski poziv", + "ntfyUseTemplate": "Prilagodite predloške obavijesti", + "ntfyUseTemplateDescription": "Omogućite ovo za prilagodbu naslova i poruka obavijesti pomoću LiquidJS predložaka", + "ntfyCustomTitle": "Predložak naslova", + "ntfyCustomMessage": "Predložak poruke", + "ntfyNotificationTemplateFallback": "Ostaviti prazno za korištenje zadanog formata Uptime Kume", + "Badge Link Generator": "Generator poveznica za značke korisnika {0}", + "Open Badge Link Generator": "Otvoriti generator poveznica za značke", + "Badge Link Generator Helptext": "Poveznice za značke dostupne su svim Monitorima dodijeljenim javnim statusnim stranicama. Za više informacija pogledajte {documentation}.", + "showOnlyLastHeartbeat": "Prikaži samo posljednjiu provjeru", + "milliseconds": "{n} milisekunda | {n} milisekundi", + "screenshotDelayWarning": "Veće vrijednosti drže preglednik otvorenim dulje, što može povećati potrošnju memorije ako postoji mnogo istovremenih monitora.", + "systemService": "Sistemski servis", + "systemServiceName": "Naziv servisa", + "systemServiceDescription": "Provjerava je li sistemski servis {service_name} aktivan", + "systemServiceDescriptionLinux": "Provjerava je li Linux systemd servis {service_name} aktivan", + "systemServiceDescriptionWindows": "Provjerava je li pokrenut Windows servis {service_name}", + "systemServiceCommandHint": "Korištena naredba: {command}", + "systemServiceExpectedOutput": "Očekivani izlaz: \"{0}\"", + "message": "poruka", + "json_value": "JSON vrijednost", + "Enter the list of nodes": "Unesite popis upravljačkih čvorova RabbitMQ-a", + "Press Enter to add node": "Pritisnuti Enter za dodavanje čvora", + "resendApiKey": "Ponovno pošalji API ključ", + "resendApiHelp": "Ovdje stvorite API ključ: {0}", + "resendFromName": "Naziv pošiljatelja", + "resendFromEmail": "Adresa e-pošte pošiljatelja", + "resendLeaveBlankForDefaultName": "ostaviti prazno za zadano ime", + "discordUseMessageTemplate": "Koristi prilagođeni predložak poruke", + "Google": "Google", + "Plausible": "Plausible", + "Matomo": "Matomo", + "Umami": "Umami", + "Suppress Notifications": "Utišaj obavijesti", + "discordSuppressNotificationsHelptext": "Kada je omogućeno, poruke će biti objavljene na kanalu, ali neće pokretati push ili desktop obavijesti za primatelje.", + "discordMessageFormatNormal": "Normalni (bogati ugrađeni sadržaji)", + "discordMessageFormatMinimalist": "Minimalistički (kratki status)", + "discordMessageFormatCustom": "Prilagođeni predložak", + "discordUseMessageTemplateDescription": "Ako je omogućeno, poruka će biti poslana s prilagođenim predloškom (LiquidJS). Ostavite prazno za korištenje zadanog Uptime Kuma formata.", + "discordMessageTemplate": "Predložak poruke", + "Globalping - Access global monitoring probes": "Globalping - Pristup globalnim sondama za praćenje", + "GlobalpingMonitorDescription": "Globalping omogućuje pristup tisućama sondi za pokretanje mrežnih testova i mjerenja koje održava zajednica. Za sve anonimne korisnike postavljeno je ograničenje od 250 testova na sat. Da biste udvostručili to ograničenje, spremite svoj token u {accountSettings}. Za više informacija pogledajte {docs}.", + "Globalping API Token": "Token za Globalping API", + "globalpingApiTokenDescription": "Preuzmite svoj token za Globalping API na {0}.", + "GlobalpingHostname": "Javno dostupna ciljna točka mjerenja. Obično naziv hosta ili IPv4/IPv6 adresa, ovisno o vrsti mjerenja.", + "GlobalpingLocation": "Polje lokacije prihvaća kontinente, zemlje, regije, gradove, ASN-ove, ISP-ove ili regije u oblaku. Možete kombinirati filtre s {plus} (npr. {amazonPlusGermany} ili {comcastPlusCalifornia}). Ako je latencija važna metrika, upotrijebite filtre za sužavanje lokacije na malu regiju kako biste izbjegli nagli rast prometa. {fullDocs}.", + "GlobalpingIpFamilyInfo": "IP inačica koja će se koristiti. Dopušten unos samo ako je naziv hosta krajnji cilj.", + "RegexMatch": "Unesite regularni izraz koji odgovara vrijednosti zapisa", + "Protocol": "Protokol", + "account settings": "postavke računa", + "Location": "Lokacija", + "Monitor Subtype": "Podtip Monitora", + "Check for": "Provjeriti za", + "Notifications Enabled": "Obavijesti omogućene", + "Allow Notifications": "Dozvoli obavijesti", + "Browser not supported": "Preglednik nije podržan", + "Unable to get permission to notify": "Nije moguće dobiti dopuštenje za obavijesti (zahtjev je odbijen ili ignoriran).", + "Webpush Helptext": "Web push radi samo s SSL (HTTPS) vezama. Za iOS uređaje, web stranicu je potrebno prethodno dodati na početni zaslon.", + "settingsDomainExpiry": "Istek domene", + "labelDomainExpiry": "Istek domene", + "domainExpiryNotificationHelp": "Broj dana može se konfigurirati u postavkama.", + "domainExpiryDescription": "Pošalji obavijest kada domena ističe za:", + "domain_expiry_unsupported_monitor_type": "Praćenje isteka domene nije podržano za ovu vrstu Monitora", + "domain_expiry_unsupported_missing_target": "Za ovaj Monitor nije konfigurirana valjana domena ili naziv hosta", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" je prekratak za vršnu domenu", + "domain_expiry_unsupported_is_icann": "Domena \"{domain}\" nije kandidat za praćenje isteka domene jer ICANN ne upravlja njezinim javnim sufiksom \".{publicSuffix}\"", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" je IP adresa. Za praćenje isteka domene potreban je naziv domene", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Praćenje isteka domene nije dostupno za \".{publicSuffix}\" jer IANA ne navodi nijednu RDAP uslugu", + "lowIntervalWarning": "Jeste li sigurni da želite postaviti vrijednost intervala ispod 20 sekundi? Performanse se mogu smanjiti, posebice ako postoji veliki broj Monitora.", + "halopsa_webhook_url_desc": "Unesite URL webhooka iz svog Halo PSA Integration Runbooka (Configuration > Integrations > Custom Integrations > Integration Runbooks). Prilikom izrade webhooka odaberite 'Can only be started from Halo and from a public endpoint'.", + "password": "Lozinka", + "halopsa_username_desc": "Korisničko ime za autentifikaciju s Halo PSA webhookom", + "halopsa_password_desc": "Lozinka za autentifikaciju s Halo PSA webhookom", + "screenshot of the website": "Snimka zaslona web-mjesta", + "mtls-auth-server-cert-placeholder": "Tijelo certifikata", + "mtls-auth-server-key-label": "Ključ", + "mtls-auth-server-key-placeholder": "Tijelo ključa", + "mtls-auth-server-ca-placeholder": "CA poslužitelja", + "avgPing": "Prosj. ping", + "Basic checkbox toggle button group": "Osnovna grupa za checkbox gumbe", + "maxPing": "Maks. ping", + "minPing": "Min. ping", + "Setup Instructions": "Upute za postavljanje", + "halopsa_setup_step2": "Konfigurirajte radnje runbooka za obradu upozorenja (npr. izrada ticketa)", + "Sort by certificate expiry": "Sortiraj po isteku certifikata", + "Splunk Rest URL": "Splunk REST URL", + "Message Format": "Format poruke", + "Region": "Regija", + "PushDeer Server URL": "URL PushDeer poslužitelja", + "To Number": "Broj primatelja", + "GrafanaOncallURL": "URL za Grafana on-call", + "Never": "Nikad", + "System Service": "Sistemski servis", + "playground": "igralište", + "Check Type": "Tip provjere", + "Service Name": "Naziv servisa", + "GRPC Options": "Opcije za GRPC", + "Metadata": "Metapodaci", + "Endpoint": "Krajnja točka", + "Details": "Detalji", + "passwordTooWeak": "Lozinka je preslaba. Trebala bi sadržavati slova i brojke. Mora biti dugačka najmanje 6 znakova.", + "TLS Alerts": "TLS upozorenja", + "expectedTlsAlertDescription": "Odaberite TLS grešku koje očekujete da će poslužitelj vratiti. Upotrijebite {code} za provjeru odbijaju li mTLS krajnje točke veze bez klijentskih certifikata. Za detalje pogledajte {link}.", + "Expected TLS Alert": "Očekivana TLS greška", + "None (Successful Connection)": "Nikakva (uspješna veza)", + "mariadbSocketPathDetectedHelptext": "Povezivanje s bazom podataka kako je navedeno varijablom okruženja {0}.", + "Collapse All Groups": "Sažmi sve grupe", + "halopsa_field_status": "Status Monitora: UP, DOWN, NOTIFICATION, ili UNKNOWN", + "halopsa_field_monitor_id": "Jedinstveni identifikator Monitora (null za testne obavijesti)", + "halopsa_field_message": "Cjelovita poruka upozorenja sa statusom i detaljima", + "halopsa_field_timestamp": "Vremenska oznaka događaja u ISO 8601 formatu", + "halopsa_field_uptime_kuma_version": "Inačica Uptime Kume", + "halopsa_id_usage_hint": "💡 Savjet: Koristite \"monitor_id\" za povezivanje upozorenja s ticketima i \"heartbeat_id\" za praćenje povijesti događaja", + "teltonikaPasswordHelptext": "Možete definirati lozinku korisnika API-ja u svom Teltonika routeru, npr. {0}", + "teltonikaPhoneNumberHelptext": "Broj mora biti u međunarodnom formatu {0}, {1}. Dopušten je samo jedan broj.", + "Jira Service Management": "Jira Service Management", + "Disable STARTTLS": "Onemogući STARTTLS", + "Basic radio toggle button group": "Osnovna grupa za radio gumbe", + "360messengerAuthToken": "360messenger API ključ", + "360messengerRecipient": "Telefonski broj(evi) primatelja", + "360messengerGroupId": "360messenger identifikator grupe", + "360messengerUseTemplate": "Koristiti prilagođeni predložak poruke", + "360messengerTemplate": "Predložak poruke za uslugu 360messenger", + "360messengerGroupList": "WhatsApp grupe", + "360messengerSelectGroupList": "Odaberite grupu koju ćete dodati", + "360messengerSelectedGroupID": "Odabrani identifikator(i) grupe", + "360messengerEnableSendToGroup": "Omogući slanje u WhatsApp grupu/e", + "360messengerCustomMessageTemplate": "Prilagođeni predložak poruke", + "360messengerMessageTemplate": "Predložak poruke", + "360messengerWayToGetUrlAndToken": "API ključ za 360messenger možete dobiti na {0}.", + "360messengerErrorNoApiKey": "Prvo unesite API ključ za 360messenger.", + "360messengerErrorNoGroups": "Za ovaj račun nisu pronađene WhatsApp grupe.", + "360messengerErrorApi": "Nije moguće učitati popis WhatsApp grupa (Pogreška {statusCode}: {message}).", + "360messengerErrorGeneric": "Nije moguće učitati popis WhatsApp grupa: {message}", + "GlobalpingMultipleLocationsError": "Više lokacija nije podržano, koristite jednu lokaciju za svaki Monitor.", + "GlobalpingLocationDescription": "Polje lokacije prihvaća kontinente, zemlje, regije, gradove, ASN-ove, ISP-ove ili regije u oblaku. Možete kombinirati filtere s {plus} (npr. {amazonPlusGermany} ili {comcastPlusCalifornia}). Ako je latencija važna metrika, upotrijebite filtere za sužavanje lokacije na malu regiju kako biste izbjegli nagli rast prometa, a za bolju stabilnost postavite filter {datacenter}. {fullDocs}.", + "fluxerMessageFormatNormal": "Normalno (bogati ugrađeni sadržaji)", + "fluxerMessageFormatMinimalist": "Minimalistički (kratki status)", + "fluxerUseMessageTemplate": "Koristiti prilagođeni predložak poruke", + "Fluxer Webhook URL": "Fluxer URL webhooka", + "Notification Action": "Radnja obavijesti", + "360messengerEnableCustomMessage": "Omogućiti prilagođeni predložak poruke umjesto zadane poruke.", + "homeAssistantNotificationActionHelptext": "Popis radnji obavijesti možete pronaći u Home Assistantu pod \"Settings > Developer Tools > Actions\". Potražite \"notify\" kako biste pronašli svoje radnje. Unesite samo dio nakon \"notify.\", npr. za radnju \"notify.mobile_app_xyz\" unesite \"mobile_app_xyz\". Za ugrađene mobilne obavijesti potražite \"Send a notification via mobile_app_xyz\" (ne \"Send a notification\").", + "360messengerWayToWriteRecipient": "Unesite jedan ili više telefonskih brojeva u međunarodnom formatu bez vodećeg znaka plus (npr. {0}). Odvojite više brojeva zarezima.", + "fluxerMessageFormat": "Format poruke", + "fluxerMessageFormatCustom": "Prilagođeni predložak", + "fluxerUseMessageTemplateDescription": "Ako je omogućeno, poruka će biti poslana pomoću prilagođenog predloška (LiquidJS). Ostavite prazno za korištenje zadanog Uptime Kuma formata.", + "fluxerMessageTemplate": "Predložak poruke", + "wayToGetFluxerURL": "To možete dobiti tako da odete na postavke odredišnog kanala > Webhooks > Create Webhook > Copy Webhook URL.", + "monitorTypeGameServer": "Poslužitelj za igru", + "monitorTypeDatabase": "Monitor za baze podataka", + "monitorTypeSpecial": "Posebni", + "oracledbConnectionString": "Oracle baza podataka: {connectionString}", + "wayToGetMaxToken": "Za dohvat MAX bot tokena i više detalja pristupite stranici {0}.", + "maxMessenger": "MAX aplikacija za razmjenu poruka", + "maxApiUrlDescription": "Osnovni URL API-ja za MAX messenger. Zadano: https://platform-api.max.ru", + "wayToGetMaxChatID": "Navedite identifikator chata u MAX-u gdje bi se poruke trebale isporučivati.", + "maxUseTemplate": "Koristi prilagođeni predložak poruke", + "maxUseTemplateDescription": "Ako je omogućeno, poruka će biti poslana koristeći prilagođeni predložak.", + "maxTemplateFormatDescription": "MAX messenger podržava formatiranje koristeći običan tekst, Markdown i HTML.", + "API Version": "Inačica API-ja", + "Peer ID": "Identifikator peera", + "vkApiVersionDescription": "Verzija VK API-ja koja se koristi za zahtjeve. Ostavite zadanu vrijednost osim ako vam nije nužna druga verzija VK API-ja radi kompatibilnosti.", + "vkDontParseLinks": "Onemogući prikazivanje isječaka iz poveznica", + "vkDontParseLinksDescription": "Ako je omogućeno, VK neće generirati pretpreglede/isječke poveznica.", + "vkPeerIdDescription": "Unesite VK peer_id krajnjeg odredišta. Ova vrijednost se nepromjenjena šalje API-ju.", + "telnyxApiKey": "API ključ (V2)", + "telnyxApiKeyHelptext": "Vaš Telnyx V2 API ključ s Telnyx portala", + "telnyxPhoneNumber": "Broj pošiljatelja", + "telnyxPhoneNumberHelptext": "Vaš Telnyx telefonski broj za slanje (mora biti registriran na vašem Telnyx računu, npr. +15551234567)", + "telnyxToNumber": "Broj primatelja", + "telnyxToNumberHelptext": "Odredišni telefonski broj za primanje SMS obavijesti (npr. +15559876543)", + "telnyxMessagingProfileId": "ID profila za poruke (neobavezno)", + "telnyxMessagingProfileIdHelptext": "Po želji povežite Telnyx profil za kontrolu postavki i značajki pošiljatelja", + "evolutionCustomMessageTitle": "Prilagođena poruka (neobvezno)", + "evolutionCustomMessageDesc": "Ostavite prazno za korištenje zadane poruke.", + "VKTeams Base Url Description": "Osnovni URL API-ja VKTeamsa može se razlikovati ako ga sami pokrećete. Zadana vrijednost je https://myteam.mail.ru", + "VKTeams Use Template": "Koristi prilagođeni predložak poruke", + "VKTeams Use Template Description": "Ako je omogućeno, poruka će biti poslana koristeći prilagođeni predložak.", + "VKTeams Template Format Description": "Za stiliziranje poruka, VKTeams podržava obični tekst, formatiranje MarkdownV2 i HTML.", + "VKTeams Bot Token Description": "Kako dobiti bot token i ostale detalje možete pronaći na {0}.", + "VKTeams Chat Id Description": "Za korisnike, ovo je njihova adresa e-pošte. Za grupe i kanale, preuzmite ID iz njihovih postavki i dodajte {'@'}chat.agent", + "WebSocket Options": "Opcije WebSocketa", + "pingPacketSizeWarning": "Veličina paketa manja od 16 na Linuxu ne stvara povratno putovanje", + "smtpAdditionalHeadersTitle": "Dodatna zaglavlja", + "smtpAdditionalHeadersDesc": "Za korištenje pogledajte prilagođena zaglavlja Nodemailera {0}.", + "wayToGetEgoSMSToken": "Svoje korisničko ime i lozinku za EgoSMS API možete dobiti {ovdje}.", + "egosmsSenderDescription": "ID pošiljatelja prikazan primateljima (najviše 11 znakova). Zadana vrijednost je EGOSMS.", + "egosmsPhoneNumberDescription": "Broj primatelja s pozivnim brojem države, bez + ili 00 (npr. 3857XXXXXXXX).", + "gamedigToken": "Token", + "gamedigTokenDescription": "Neobvezno. Neki poslužitelji igara zahtijevaju token za autentifikaciju.", + "Bearer Token": "Token nositelja", + "Connection Timeout": "Vrijeme isteka konekcije" +} diff --git a/src/lang/hu.json b/src/lang/hu.json new file mode 100644 index 0000000..a112492 --- /dev/null +++ b/src/lang/hu.json @@ -0,0 +1,1167 @@ +{ + "languageName": "Magyar", + "checkEverySecond": "Ellenőrzés {0} másodpercenként", + "retryCheckEverySecond": "Újrapróbálkozás minden {0} másodpercenként", + "retriesDescription": "Maximális próbálkozás mielőtt a szolgáltatás 'Leállt' jelölést kap és értesítés kerül kiküldésre", + "ignoreTLSError": "TLS/SSL hibák figyelmen kívül hagyása HTTPS weboldalaknál", + "upsideDownModeDescription": "Az állapot megfordítása. Ha a szolgáltatás elérhető, akkor lesz leállt állapotú.", + "maxRedirectDescription": "Az átirányítások maximális száma. állítsa 0-ra az átirányítás tiltásához.", + "acceptedStatusCodesDescription": "Válassza ki az állapot kódokat amelyek sikeres válasznak fognak számítani.", + "passwordNotMatchMsg": "A megismételt jelszó nem egyezik.", + "notificationDescription": "Kérem, rendeljen egy értesítést a figyeléshez, hogy működjön.", + "keywordDescription": "Kulcsszó keresése a HTML-ben vagy a JSON válaszban. A keresés kis-nagybetű érzékeny.", + "pauseDashboardHome": "Szünetel", + "deleteMonitorMsg": "Biztos, hogy törölni akarja ezt a figyelőt?", + "deleteNotificationMsg": "Biztos, hogy törölni akarja ezt az értesítést az összes figyelőnél?", + "resolverserverDescription": "A Cloudflare az alapértelmezett szerver, bármikor meg tudja változtatni a resolver server-t.", + "rrtypeDescription": "Válassza ki az RR-típust a figyelőhöz", + "pauseMonitorMsg": "Biztos, hogy szüneteltetni akarja?", + "enableDefaultNotificationDescription": "Minden új figyelőhöz ez az értesítés engedélyezett lesz alapértelmezetten. Kikapcsolhatja az értesítést külön minden figyelőnél.", + "clearEventsMsg": "Biztos, hogy törölni akar miden eseményt ennél a figyelnél?", + "clearHeartbeatsMsg": "Biztos, hogy törölni akar minden életjelet ennél a figyelőnél?", + "confirmClearStatisticsMsg": "Biztos, hogy törölni akar MINDEN statisztikát?", + "importHandleDescription": "Válassza a 'Meglévő kihagyását', ha ki szeretné hagyni az azonos nevő figyelőket vagy értesítésket. A 'Felülírás' törölni fog minden meglévő figyelőt és értesítést.", + "confirmImportMsg": "Biztos, hogy importálja a mentést? Győződjön meg róla, hogy jól választotta ki az importálás opciót.", + "twoFAVerifyLabel": "Kérem add meg a token-t a 2FA ellenőrzéséhez:", + "tokenValidSettingsMsg": "A token érvényes! El tudja menteni a 2FA beállításait.", + "confirmEnableTwoFAMsg": "Biztosan engedélyezi a 2FA-t?", + "confirmDisableTwoFAMsg": "Biztosan letiltja a 2FA-t?", + "Settings": "Beállítások", + "Dashboard": "Irányítópult", + "New Update": "Új frissítés", + "Language": "Nyelv", + "Appearance": "Megjelenés", + "Theme": "Téma", + "General": "Általános", + "Version": "Verzió", + "Check Update On GitHub": "Frissítések keresése a GitHub-on", + "List": "Lista", + "Add": "Hozzáadás", + "Add New Monitor": "Új monitor hozzáadása", + "Quick Stats": "Gyors statisztikák", + "Up": "Működik", + "Down": "Leállt", + "Pending": "Függőben", + "Unknown": "Ismeretlen", + "Pause": "Szünet", + "Name": "Név", + "Status": "Állapot", + "DateTime": "Időpont", + "Message": "Üzenet", + "No important events": "Nincs fontos esemény", + "Resume": "Folytatás", + "Edit": "Szerkesztés", + "Delete": "Törlés", + "Current": "Aktuális", + "Uptime": "Uptime", + "Cert Exp.": "Tanúsítvány Lejárat", + "day": "nap", + "-day": "-nap", + "hour": "óra", + "-hour": "- óra", + "Response": "Válasz", + "Ping": "Ping", + "Monitor Type": "Monitor típusa", + "Keyword": "Kulcsszó", + "Friendly Name": "Rövid név", + "URL": "URL", + "Hostname": "Hosztnév", + "Port": "Port", + "Heartbeat Interval": "Életjel időköz", + "Retries": "Újrapróbálkozás", + "Heartbeat Retry Interval": "Életjel újrapróbálkozások időköze", + "Advanced": "Haladó", + "Upside Down Mode": "Fordított mód", + "Max. Redirects": "Max. átirányítás", + "Accepted Status Codes": "Elfogadott állapot kódok", + "Save": "Mentés", + "Notifications": "Értesítések", + "Not available, please setup.": "Nem elérhető, állítsa be.", + "Setup Notification": "Értesítés beállítása", + "Light": "Világos", + "Dark": "Sötét", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Téma - Életjel sáv", + "Normal": "Normál", + "Bottom": "Nyomógomb", + "None": "Nincs", + "Timezone": "Időzóna", + "Search Engine Visibility": "Látható a keresőmotoroknak", + "Allow indexing": "Indexelés engedélyezése", + "Discourage search engines from indexing site": "Keresőmotorok elriasztása az oldal indexelésétől", + "Change Password": "Jelszó változtatása", + "Current Password": "Jelenlegi jelszó", + "New Password": "Új jelszó", + "Repeat New Password": "Ismételje meg az új jelszót", + "Update Password": "Jelszó módosítása", + "Disable Auth": "Hitelesítés tiltása", + "Enable Auth": "Hitelesítés engedélyezése", + "disableauth.message1": "Biztos benne, hogy {disableAuth}?", + "disable authentication": "kikapcsolja a hitelesítést", + "disableauth.message2": "Akkor érdemes, ha {intendThirdPartyAuth} az Uptime Kuma-t megelőzően mint a Cloudflare Access.", + "where you intend to implement third-party authentication": "van 3rd-party hitelesítés", + "Please use this option carefully!": "Használja megfontoltan!", + "Logout": "Kijelentkezés", + "Leave": "Elhagy", + "I understand, please disable": "Megértettem, kérem tiltsa le", + "Confirm": "Megerősítés", + "Yes": "Igen", + "No": "Nem", + "Username": "Felhasználónév", + "Password": "Jelszó", + "Remember me": "Emlékezzen rám", + "Login": "Bejelentkezés", + "No Monitors, please": "Nincsenek megfigyelők, kérem", + "add one": "adjon hozzá egyet", + "Notification Type": "Értesítés típusa", + "Email": "Email", + "Test": "Teszt", + "Certificate Info": "Tanúsítvány információk", + "Resolver Server": "DNS szerver", + "Resource Record Type": "Resource Record típusa", + "Last Result": "Utolsó eredmény", + "Create your admin account": "Hozza létre az adminisztrátor felhasználót", + "Repeat Password": "Jelszó ismétlése", + "Import Backup": "Mentés importálása", + "Export Backup": "Mentés exportálása", + "Export": "Exportálás", + "Import": "Importálás", + "respTime": "Válaszidő (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Alapértelmezetten engedélyezett", + "Apply on all existing monitors": "Alkalmazza az összes monitorra", + "Create": "Létrehozás", + "Clear Data": "Adatok törlése", + "Events": "Események", + "Heartbeats": "Életjelek", + "Auto Get": "Auto lekérdezés", + "backupDescription": "Mentheti az összes figyelőt és értesítést egy JSON fájlba.", + "backupDescription2": "Megj: Történeti és esemény adatokat nem tartalmaz.", + "backupDescription3": "Érzékeny adatok, pl. értesítés tokenek is vannak az export fájlban. Figyeljen erre!", + "alertNoFile": "Válaszzon ki egy fájlt az importáláshoz.", + "alertWrongFileType": "Válasszon egy JSON fájlt.", + "Clear all statistics": "Összes statisztika törlése", + "Skip existing": "Meglévő kihagyása", + "Overwrite": "Felülírás", + "Options": "Opciók", + "Keep both": "Mindegyiket tartsa meg", + "Verify Token": "Token ellenőrzése", + "Setup 2FA": "2FA beállítása", + "Enable 2FA": "2FA engedélyezése", + "Disable 2FA": "2FA tiltása", + "2FA Settings": "2FA beállítások", + "Two Factor Authentication": "Kétfaktoros hitelesítés", + "Active": "Aktív", + "Inactive": "Inaktív", + "Token": "Token", + "Show URI": "URI megmutatása", + "Tags": "Címkék", + "Add New below or Select...": "Új hozzáadása alább vagy Válasszon…", + "Tag with this name already exist.": "Ilyen nevű címke már létezik.", + "Tag with this value already exist.": "Ilyen értékű címke már létezik.", + "color": "szín", + "value (optional)": "érték (opcionális)", + "Gray": "Szürke", + "Red": "Piros", + "Orange": "Narancs", + "Green": "Zöld", + "Blue": "Kék", + "Indigo": "Indigó", + "Purple": "Lila", + "Pink": "Rózsaszín", + "Search...": "Keresés…", + "Avg. Ping": "Átl. ping", + "Avg. Response": "Átl. válasz", + "Entry Page": "Nyitólap", + "statusPageNothing": "Semmi nincs itt. Adjon hozzá egy vagy több figyelőt.", + "No Services": "Nincsenek szolgáltatások", + "All Systems Operational": "Minden rendszer működik", + "Partially Degraded Service": "Részlegesen leállt szolgáltatás", + "Degraded Service": "Leállt szolgáltatás", + "Add Group": "Csoport hozzáadása", + "Add a monitor": "Figyelő hozzáadása", + "Edit Status Page": "Státusz oldal szerkesztése", + "Go to Dashboard": "Irányítópulthoz", + "telegram": "Telegram", + "webhook": "Webhook", + "smtp": "Email (SMTP)", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.Chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (50+ értesítési szolgáltatás)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "Status Page": "Státusz oldal", + "Status Pages": "Státusz oldalak", + "Primary Base URL": "Elsődleges URL", + "Push URL": "Meghívandó URL", + "needPushEvery": "Ezt az URL-t kell meghívni minden {0} másodpercben.", + "pushOptionalParams": "Opcionális paraméterek: {0}", + "defaultNotificationName": "{notification} értesítésem ({number})", + "here": "itt", + "Required": "Kötelező", + "Bot Token": "BOT token", + "wayToGetTelegramToken": "Innen kaphat token-t: {0}.", + "Chat ID": "Csevegés ID", + "supportTelegramChatID": "Támogatja a közvetlen csevegést, csoportnak küldést és csatona ID-t is", + "wayToGetTelegramChatID": "A csevegés ID-t szerezhet ha küld egy üzenetet a bot-nak és ellátogat erre az URL-re, ahol láthatja a chat_id-t:", + "YOUR BOT TOKEN HERE": "AZ ÖN BOT TOKENJE ITT", + "chatIDNotFound": "Csevegés ID nem található, küldjön egy első üzenetet a bot-nak", + "Post URL": "Cél URL (Post)", + "Content Type": "Tartalom típus (Content Type)", + "webhookJsonDesc": "{0} ideális a moderh HTTP szerverekhez, mint az Express.js", + "webhookFormDataDesc": "{multipart} ideális a PHP-hez. A JSON értelmezhető ezzel: {decodeFunction}", + "secureOptionNone": "Nincs / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "TLS hiba figyelmen kívül hagyása", + "From Email": "Feladó email", + "emailCustomSubject": "Egyedi tárgy", + "To Email": "Cél email", + "smtpCC": "Másolat", + "smtpBCC": "Titkos másolat", + "Discord Webhook URL": "Discord cím (webhook URL)", + "wayToGetDiscordURL": "Ezt itt szerezhetsz: Server Settings -> Integrations -> View Webhooks -> New Webhook", + "Bot Display Name": "Bot megjelenő neve", + "Prefix Custom Message": "Egyedi előtét üzenet", + "Hello @everyone is...": "Hello {'@'}everyone …", + "Webhook URL": "Cím (webhook URL)", + "wayToGetTeamsURL": "Itt megnézheti, hogy kell ilyen URL-t készíteni: {0}.", + "Number": "Szám", + "Recipients": "Címzettek", + "needSignalAPI": "Egy Signal kliensre van szüksége, amihez REST API tartozik.", + "wayToCheckSignalURL": "Itt megnézheti, hogy hozhat létre egyet:", + "signalImportant": "FONTOS! Nem keverheti a csoportokat és számokat a címzetteknél!", + "Application Token": "Alkalmazás token", + "Server URL": "Szerver URL", + "Priority": "Prioritás", + "Icon Emoji": "Emoji ikonok", + "Channel Name": "Csatorna neve", + "Uptime Kuma URL": "Uptime Kuma cím", + "aboutWebhooks": "Webhook-okról több info: {0}", + "aboutChannelName": "Adja meg a {0} csatorna nevét ha szeretné elkerülni a webhook-ot. Pl: #masik-csatorna", + "aboutKumaURL": "Ha üresen hagyja a Uptime Kuma cím mezőt, akkor a projekt GitHub oldala lesz az alapértelmezett.", + "emojiCheatSheet": "Emoji csalás: {0}", + "clicksendsms": "ClickSend SMS", + "User Key": "Felhasználói Kulcs", + "Device": "Eszköz", + "Message Title": "Üzenet Címe", + "Notification Sound": "Értesítési Hang", + "More info on:": "További információ: {0}", + "pushoverDesc1": "A vészhelyzeti prioritásnak (2) 30 másodperc az újrapróbálkozási alapértéke és egy óra után lejár.", + "pushoverDesc2": "Ha különböző eszközökre szeretne értesítést küldeni, töltse ki az Eszköz mezőt.", + "SMS Type": "SMS típusa", + "octopushTypePremium": "Prémium (Gyors - riasztáshoz ajánlott)", + "octopushTypeLowCost": "Alacsony költség (lassú - néha az üzemeltető blokkolja)", + "checkPrice": "Ellenőrizze {0} árat:", + "apiCredentials": "API kulcsok", + "octopushLegacyHint": "Az Octopush régi (2011-2020) verzióját használod vagy az újat?", + "Check octopush prices": "Csekkold az octopush {0} árakat.", + "octopushPhoneNumber": "Telefonszám (nemz. formátum, pl : +36705554433) ", + "octopushSMSSender": "SMS küldő neve : 3-11 betű/szám (a-zA-Z0-9) vagy szóköz", + "LunaSea Device ID": "LunaSea eszköz ID", + "Apprise URL": "Apprise cím (URL)", + "Example:": "Például: {0}", + "Read more:": "Itt olvashat róla: {0}", + "Status:": "Állapot: {0}", + "Read more": "Tovább olvasom", + "appriseInstalled": "Apprise telepítve.", + "appriseNotInstalled": "Apprise nincs telepítve. {0}", + "Access Token": "Elérési token", + "Channel access token": "Csatorna elérési token", + "Line Developers Console": "Line Developers konzol", + "lineDevConsoleTo": "Line Developers konzol - {0}", + "Basic Settings": "Alap beállítások", + "User ID": "Felhasználó ID", + "Messaging API": "Üzenet API", + "wayToGetLineChannelToken": "{0} első eléréséhez készítsen egy Provider-t és csatornát (Messaging API), utána kaphatja meg a csatorna elérési token-t és felhasználó ID-t az alábbi menüpontban.", + "Icon URL": "Ikon cím (URL)", + "aboutIconURL": "Megadhat egy webcímet az Ikon cím mezőben, ezzel felülírva az alapértelmezet képet. Nem kerül felhasználásra, ha az Emoji-k be vannak állítva.", + "aboutMattermostChannelName": "Felülírhatja az alapértelmezett csatornát, ahova a webhook az adatokat küldi. Ehhez töltse ki a \"Csatorna neve\" mezőt (pl: #egyeb-csatorna). A Mattermost webhook beállításaiban további engedélyek szükségesek", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - olcsó, de lassú, gyakran túlterhelt. Csak lengyel címzettekhez.", + "promosmsTypeFlash": "SMS FLASH - Az üzenet automatikusan megjelenik a fogadó eszközön. Csak lengyel címzettekhez.", + "promosmsTypeFull": "SMS FULL - Prémium szintje az SMS-nek. Megadható a feladó neve, de előtte jóváhagyás szükséges. Ideális értesítésekhez.", + "promosmsTypeSpeed": "SMS SPEED - A legmagasabb prioritás a rendszerben. Nagyon gyors és pontos, de költséges (kb. duplája a hagyományos SMS-nek).", + "promosmsPhoneNumber": "Telefonszám (lengyel címzett esetén az országkód elhagyható)", + "promosmsSMSSender": "SMS feladónév: Előre beállított név vagy az alábbiak egyike: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu webhook cím (URL)", + "matrixHomeserverURL": "Homeserver cím (URL http(s):// előtaggal és opcionálisan port-tal)", + "Internal Room Id": "Belső Szoba ID", + "matrixDesc1": "A belső szoba ID-t a szpbák speciális beállítások között találja meg a Matrix kliens programban. Így kell kinéznie: !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Erősen ajánlott készíteni egy új felhasználót és nem a teljes joggal rendelkező felhasználót használni. Az új felhasználó létrehozása után csak azokba a szobákba kell megjhívni a felhasználót, ahol értesítéseket szeretne kapni. Ezzel a művelettel lehet elérési token-t kérni: {0}", + "Method": "Metódus", + "Body": "Törzs", + "Headers": "Fejlécek", + "PushUrl": "Push cím (URL)", + "HeadersInvalidFormat": "A kérés fejléc nem egy valós JSON: ", + "BodyInvalidFormat": "A kérés törzse nem egy valós JSON: ", + "Monitor History": "Vizsgálatok előzményei", + "clearDataOlderThan": "Előzmények megtartása {0} napig.", + "PasswordsDoNotMatch": "Jelszó nem egyezik.", + "records": "sorok", + "One record": "Egy sor", + "steamApiKeyDescription": "Steam Game Server ellenőrzéséhez szükséges egy Steam Web-API kulcs. Itt létrehozhat egy API kulcsot: ", + "Current User": "Felhasználó", + "recent": "Legújabb", + "Done": "Kész", + "Info": "Infó", + "Security": "Biztonság", + "Steam API Key": "Steam API kulcs", + "Shrink Database": "Adatbázis tömörítése", + "Pick a RR-Type...": "Válassz egy RR-típust…", + "Pick Accepted Status Codes...": "Válassz Elfogadható Állapot Kódokat…", + "Default": "Alapértelmezett", + "HTTP Options": "HTTP beállítások", + "Create Incident": "Incidens létrehozása", + "Title": "Cím", + "Content": "Tartalom", + "Style": "Stílus", + "info": "info", + "warning": "figyelmeztetés", + "danger": "veszély", + "primary": "elsődleges", + "light": "világos", + "dark": "sötét", + "Post": "Bejegyzés", + "Please input title and content": "Adjon meg címet és tartalmat", + "Created": "Létrehozva", + "Last Updated": "Utoljára Módosítva", + "Unpin": "Leválaszt", + "Switch to Light Theme": "Világos témára váltás", + "Switch to Dark Theme": "Sötét témára váltás", + "Show Tags": "Címkék mutatása", + "Hide Tags": "Címkék elrejtése", + "Description": "Leírás", + "No monitors available.": "Nincs még figyelő beállítva.", + "Add one": "Adjon hozzá egyet", + "No Monitors": "Nincsenek megfigyelők", + "Untitled Group": "Névtelen csoport", + "Services": "Szolgáltatások", + "Discard": "Elvet", + "Cancel": "Mégsem", + "Powered by": "A megoldást szállítja az", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API felhasználónév (webapi_ előtaggal együtt)", + "serwersmsAPIPassword": "API jelszó", + "serwersmsPhoneNumber": "Telefonszám", + "serwersmsSenderName": "SMS feladó neve (regisztrált név az oldalon)", + "GoogleChat": "Google Chat (csak Google Workspace)", + "stackfield": "Stackfield", + "smtpDkimSettings": "DKIM beállítások", + "smtpDkimDesc": "Nézze meg a Nodemailer DKIM {0} használati szabályokat.", + "documentation": "dokumentáció", + "smtpDkimDomain": "Domain név", + "smtpDkimKeySelector": "Kulcs választó", + "smtpDkimPrivateKey": "Privát kulcs", + "smtpDkimHashAlgo": "Hash algoritmus (nem kötelező)", + "smtpDkimheaderFieldNames": "Fejléc kulcsok a bejelentkezéshez (nem kötelező)", + "smtpDkimskipFields": "Fejléc kulcsok egyéb esetben (nem kötelező)", + "PushByTechulus": "Techulus push", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API végpont", + "alertaEnvironment": "Környezet", + "alertaApiKey": "API kulcs", + "alertaAlertState": "Figyelmeztetési állapot", + "alertaRecoverState": "Visszaállási állapot", + "deleteStatusPageMsg": "Biztos, hogy törölni akarja a státusz oldalt?", + "Start of maintenance": "Karbantartás kezdete", + "successMessageExplanation": "MQTT üzenet, amely sikeresnek minősül", + "weekdayShortFri": "Pé", + "lastDay2": "A hónap 2. utolsó napja", + "maintenanceStatus-under-maintenance": "Karbantartás alatt", + "dnsCacheDescription": "Előfordulhat, hogy bizonyos IPv6-környezetekben nem működik, tiltsa le, ha problémákat tapasztal.", + "Add New Status Page": "Új állapotoldal hozzáadása", + "The resource is no longer available.": "Az erőforrás már nem elérhető.", + "Show update if available": "Frissítés megjelenítése, ha elérhető", + "weekdayShortMon": "Hé", + "weekdayShortTue": "Ke", + "weekdayShortWed": "Sze", + "weekdayShortThu": "Csüt", + "weekdayShortSat": "Szo", + "weekdayShortSun": "Vas", + "dayOfWeek": "A hét napja", + "dayOfMonth": "A hónap napja", + "lastDay": "Utolsó nap", + "lastDay3": "A hónap 3. utolsó napja", + "lastDay4": "A hónap 4. utolsó napja", + "No Maintenance": "Nincs karbantartás", + "pauseMaintenanceMsg": "Biztosan szüneteltetni akarja?", + "maintenanceStatus-inactive": "Inaktív", + "maintenanceStatus-scheduled": "Ütemezett", + "maintenanceStatus-ended": "Végzett", + "maintenanceStatus-unknown": "Ismeretlen", + "Display Timezone": "Időzóna megjelenítése", + "Server Timezone": "Szerver időzóna", + "statusPageMaintenanceEndDate": "Vége", + "Enable DNS Cache": "(Idejétmúlt) DNS gyorsítótár engedélyezése a HTTP(s) monitorok számára", + "Enable": "Engedélyezze", + "Disable": "Letiltás", + "Affected Monitors": "Érintett monitorok", + "Packet Size": "Csomag mérete", + "IconUrl": "Ikon URL", + "successMessage": "Siker Üzenet", + "lastDay1": "A hónap utolsó napja", + "Guild ID": "Guild ID", + "Help": "Segítség", + "statusMaintenance": "Karbantartás", + "Maintenance": "Karbantartás", + "Game": "Játék", + "markdownSupported": "Markdown szintaxis támogatott", + "Pick Affected Monitors...": "Érintett monitor(ok) kiválasztása…", + "All Status Pages": "Összes státusz oldal", + "topic": "Téma", + "topicExplanation": "MQTT téma a monitorhoz", + "webhookAdditionalHeadersDesc": "További fejléceket állít be, amelyeket a webhookkal együtt küldünk. Minden egyes fejlécet JSON kulcs/érték formában kell definiálni.", + "error": "hiba", + "critical": "kritikus", + "Customize": "Testreszab", + "Custom Footer": "Egyedi Lábléc", + "Custom CSS": "Egyedi CSS", + "Proxies": "Proxyk", + "default": "Alapértelmezett", + "enabled": "Bekapcsolva", + "Certificate Chain": "Tanúsítvány Lánc", + "Valid": "Érvényes", + "Invalid": "Érvénytelen", + "User": "Felhasználó", + "Installed": "Telepítve", + "Not installed": "Nincs telepítve", + "Running": "Fut", + "Not running": "Nem fut", + "Remove Token": "Token Eltávolítása", + "Start": "Inditás", + "Stop": "Megállítás", + "Slug": "Slug", + "Accept characters:": "Karakterek engedélyezése:", + "Next": "Következő", + "The slug is already taken. Please choose another slug.": "Ez a slug már használatban van. Kérlek válassz másikat.", + "No Proxy": "Nincs Proxy", + "HTTP Basic Auth": "HTTP Alap Hitelesítés", + "New Status Page": "Új Állapot Oldal", + "Page Not Found": "Oldal Nem Található", + "Reverse Proxy": "Reverse Proxy", + "Backup": "Mentés", + "cloudflareWebsite": "Cloudflare Weboldal", + "Message:": "Üzenet:", + "Trust Proxy": "Trust Proxy", + "Other Software": "Egyébb Szoftware", + "Please read": "Olvasd el", + "Subject:": "Tárgy:", + "Valid To:": "Érvényes eddig:", + "Issuer:": "Kiállitó:", + "Fingerprint:": "Ujjlenyomat:", + "No status pages": "Nincsenek állapot oldalak", + "Domain Name Expiry Notification": "Domain Név Lejárás Értesítés", + "Date Created": "Létrehozva Ekkor", + "Footer Text": "Lábléc Szöveg", + "affectedMonitorsDescription": "Válaszd ki azokat a monitorokat amelyek a karbantartásban érintettek", + "affectedStatusPages": "Mutasd ezt az üzenetet a kiválasztott állapot oldalakon", + "atLeastOneMonitor": "Válassz legalább egy érintett monitort", + "endpoint": "végpont", + "promosmsLogin": "API Bejelentkezési Név", + "pushoversounds classical": "Klasszikus", + "pushoversounds cosmic": "Kozmikus", + "pushoversounds falling": "Esés", + "pushoversounds gamelan": "Gamelán", + "pushoversounds incoming": "Érkező", + "pushoversounds bike": "Bicikli", + "pushoversounds bugle": "Kürt", + "pushoversounds cashregister": "Pénztárgép", + "pushoversounds intermission": "Intermission", + "pushoversounds magic": "Varázslat", + "pushoversounds mechanical": "Mechanikus", + "pushoversounds pianobar": "Zongora", + "pushoversounds siren": "Sziréna", + "pushoversounds spacealarm": "Térriasztó", + "pushoversounds tugboat": "Vontatóhajó", + "pushoversounds alien": "Űrlény Riasztó (hosszú)", + "pushoversounds climb": "Mászás (hosszú)", + "pushoversounds persistent": "Állandó (hosszú)", + "pushoversounds echo": "Pushover Visszhang (hosszú)", + "pushoversounds updown": "Fent Lent (hosszú)", + "pushoversounds vibrate": "Csak Rezgés", + "pushoversounds none": "Egyik se (néma)", + "pushyAPIKey": "Titkos API Kulcs", + "pushyToken": "Eszköz token", + "Kook": "Kook", + "Free Mobile API Key": "Ingyenes Mobil API Kulcs", + "Enable TLS": "TLS bekapcsolása", + "proxyDescription": "A proxyk egy monitorhoz kell legyenek rendelve hogy működjenek.", + "Server Address": "Szerver Cím", + "resendEveryXTimes": "Újraküldés minden {0} időnként", + "resendDisabled": "Újraküldés kikapcsolva", + "Resend Notification if Down X times consequently": "Értesítés Újraküldése ha X-szer nem válaszol", + "Monitor": "Monitor | Monitorok", + "setAsDefault": "Beállítás Alapértelmezetnek", + "Proxy": "Proxy", + "Strategy": "Stratégia", + "Free Mobile User Identifier": "Ingyenes Mobil Felhasználó Azonosító", + "Schedule maintenance": "Karbantartás ütemezése", + "Select status pages...": "Státusz oldal kiválasztása…", + "Custom": "Egyedi", + "webhookAdditionalHeadersTitle": "További Fejlécek", + "deleteProxyMsg": "Biztos hogy kitörlöd ezt a proxy-t az összes monitorok-tól?", + "HTTP Headers": "HTTP Fejlécek", + "For example: nginx, Apache and Traefik.": "Például: nginx, Apache vagy Traefik.", + "dnsPortDescription": "DNS szerver portja. Alapéretelmezett az 53. Bármikor megváltoztathatja.", + "promosmsPassword": "API Jelszó", + "wayToGetKookBotToken": "Hozz létre egy app-ot és szerezz egy tokent itt: {0}", + "wayToGetKookGuildID": "Válts át 'Developer Mode'-ra a Kook beállításoknál majd jobb klikkelve a guildra megtalálod az ID-jét", + "Resend Notification if Down X times consecutively": "Értesítés Újraküldése ha X-szer nem válaszol", + "Authentication": "Hitelesítés", + "Passive Monitor Type": "Passzív monitor típus", + "General Monitor Type": "Általános monitor típus", + "Specific Monitor Type": "Specifikus monitor típus", + "Reconnecting...": "Újracsatlakozás...", + "Saved.": "Elmentve.", + "authUserInactiveOrDeleted": "A felhasználó inaktív vagy nem létezik.", + "authIncorrectCreds": "Helytelen felhasználónév vagy jelszó.", + "2faAlreadyEnabled": "A kétlépcsős azonosítás már be van kapcsolva.", + "successAdded": "Sikeres hozzáadás.", + "2faDisabled": "Kétlépcsős azonosítás kikapcsolva.", + "successAuthChangePassword": "Sikeres jelszó módosítás.", + "successBackupRestored": "Sikeres mentés visszaállítás.", + "statusPageRefreshIn": "Frissítés {0} múlva", + "Retry": "Újrapróbálkozás", + "Close": "Bezárás", + "Request Body": "Kérés törzs", + "successResumed": "Sikeres folytatás.", + "successDeleted": "Sikeres törlés.", + "Query": "Kérés", + "settingsCertificateExpiry": "TLS tanúsítvány lejárat", + "chromeExecutableAutoDetect": "Automatikus felismerés", + "emailTemplateStatus": "Státusz", + "deleteMaintenanceMsg": "Biztosan törölni szeretné ezt a karbantartást?", + "apiKeyAddedMsg": "Az ön API kulcsa létrejött. Kérjük jegyezze fel, mert nem lesz a felületen elérhető a jövőben.", + "Expires": "Lejár", + "disableAPIKeyMsg": "Biztosan le fel szeretné függeszteni ezt az API kulcsot?", + "Key Added": "Kulcs létrehozva", + "selectedMonitorCount": "Kiválasztva: {0}", + "sameAsServerTimezone": "A szerver időzónájával megegyező", + "setupDatabaseChooseDatabase": "Melyik adatbázist szeretné használni?", + "setupDatabaseEmbeddedMariaDB": "Nem kell semmit beállítani. Ez a Docker imagefájl automatikusan beágyazott és konfigurált egy MariaDB-t a számodra. Az Uptime Kuma Unix socket-en keresztül fog csatlakozni ehhez az adatbázishoz.", + "setupDatabaseMariaDB": "Kapcsolódás egy külső MariaDB adatbázishoz. Meg kell adnia az adatbázis kapcsolat információit.", + "setupDatabaseSQLite": "Egy egyszerű adatbázisfájl, amely kis méretű telepítésekhez ajánlott. A v2.0.0 előtt az Uptime Kuma az SQLite-ot használta alapértelmezett adatbázisként.", + "dbName": "Adazbázis neve", + "FlashDuty Severity": "Súlyosság", + "lunaseaDeviceID": "Eszköz azonosító", + "lunaseaUserID": "Felhasználó azonosító", + "successPaused": "Sikeres szüneteltetés.", + "successEdited": "Sikeres szerkesztés.", + "tagNotFound": "Nem található címke.", + "foundChromiumVersion": "Chromium/Chrome találat. Verzió: {0}", + "Examples": "Példák", + "Home Assistant URL": "Home Assistant URL", + "Add API Key": "API kulcs hozzáadása", + "apiKey-expired": "Lejárt", + "apiKey-inactive": "Inaktív", + "apiKey-active": "Aktív", + "Request Timeout": "A kérés időtúllépés miatt megszakadt", + "timeoutAfter": "Időtúllépés {0} másodperc után", + "Json Query": "Json lekérdezés", + "Home": "Kezdőlap", + "Cannot connect to the socket server": "Nem lehet csatlakozni a socket szerverhez", + "Expected Value": "Várt érték", + "successKeyword": "Siker kulcsszó", + "pushViewCode": "Hogyan kell használni a Push monitort? (Kód megtekintése)", + "setAsDefaultProxyDescription": "Ez a proxy alapértelmezés szerint engedélyezve lesz az új megfigyelők esetében. A proxy továbbra is letiltható minden egyes monitor esetében külön-külön.", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Az aktuális kapcsolat elveszhet, ha jelenleg a Cloudflare Tunnelen keresztül csatlakozik. Biztos, hogy le akarja állítani? Írja be az aktuális jelszavát a megerősítéshez.", + "telegramSendSilentlyDescription": "Az üzenetet némán küldi el. A felhasználók hang nélküli értesítést kapnak.", + "trustProxyDescription": "Bízz az 'X-Forwarded-*' fejlécekben. Ha a helyes ügyfél IP-t szeretnél kapni, és az Uptime Kuma egy proxy, például Nginx vagy Apache mögött van, akkor ezt engedélyezni kell.", + "liquidIntroduction": "Az ütemezhetőséget a Liquid templating nyelv segítségével érhetjük el. A használati utasításokat lásd a {0} oldalon. Ezek a rendelkezésre álló változók:", + "successKeywordExplanation": "MQTT kulcsszó, amelyet sikerként fogunk figyelembe venni", + "warningTimezone": "A szerver időzónáját használja", + "recurringIntervalMessage": "Futtatás minden nap egyszer | Futtatás {0} naponként egyszer", + "goAlertIntegrationKeyInfo": "A szolgáltatás általános API-integrációs kulcsának lekérdezése ebben a formátumban: \"aaaaaaaa-bbbb-cccccc-dddd-eeeeeeeeeeeeee\" általában a másolt URL token paraméterének értéke.", + "wayToGetPagerDutyKey": "Ezt a Szolgáltatás -> Szolgáltatásjegyzék -> (Szolgáltatás kiválasztása) -> Integrációk -> Integráció hozzáadása menüpontban érheti el. Itt kereshet rá az \"Events API V2\" kifejezésre. További információ {0}", + "backupOutdatedWarning": "Megszűnt: Ez a biztonsági mentési funkció nem karbantartott, ezért nem tud teljes biztonsági mentést létrehozni vagy visszaállítani.", + "SecretKey": "TitkosKulcs", + "Reset Token": "Token visszaállítása", + "Select": "Kiválasztás", + "Check/Uncheck": "Megjelölni/feloldani", + "Steam Game Server": "Steam játék szerver", + "Optional": "Opcionális", + "settingUpDatabaseMSG": "Az adatbázis beállítása. Ez eltarthat egy ideig, kérlek légy türelemmel.", + "webhookBodyPresetOption": "Előbeállítás - {0}", + "startOrEndWithOnly": "Csak {0}-val kezdődő vagy végződő", + "PushDeer Key": "PushDeer kulcs", + "wayToGetClickSendSMSToken": "Az API felhasználónevet és az API kulcsot a {here} oldalon kaphatja meg.", + "pushOthers": "Egyebek", + "programmingLanguages": "Programozási Nyelvek", + "Docker Container": "Docker Konténer", + "Device Token": "Eszköz Token", + "filterActive": "Aktív", + "filterActivePaused": "Szünetel", + "Add New Tag": "Új címke hozzáadása", + "webhookBodyCustomOption": "Egyedi törzs", + "Search monitored sites": "Monitorozott oldalak keresése", + "templateMsg": "az értesítés üzenete", + "templateHeartbeatJSON": "a szívverést leíró objektum", + "templateMonitorJSON": "a monitort leíró objektum", + "templateLimitedToUpDownNotifications": "csak a FEL/LE értesítéseknél érhető el", + "templateLimitedToUpDownCertNotifications": "csak az FEL/LE/Bizonyítvány lejáratáról szóló értesítések esetében érhető el", + "Retype the address.": "Ismételje meg a címet.", + "enableProxyDescription": "Ez a proxy nem lesz hatással a monitorkérelmekre, amíg nem aktiválódik. Az aktiválás állapotával ideiglenesen letilthatja a proxy-t az összes monitorról.", + "No consecutive dashes": "Egymást követő kötőjelek nélkül", + "wayToGetCloudflaredURL": "(Letöltés cloudflaretől {0})", + "Don't know how to get the token? Please read the guide:": "Nem tudod, hogy hogyan szerezd meg a tokent? Olvasd el az útmutatót:", + "About": "Rólunk", + "Days Remaining:": "Nap hátramaradt:", + "Certificate Expiry Notification": "Tanúsítvány lejáratáról szóló értesítés", + "API Username": "API Felhasználónév", + "API Key": "API Kulcs", + "Also check beta release": "Ellenőrizd a béta kiadást is", + "Using a Reverse Proxy?": "Fordított proxy használata?", + "Check how to config it for WebSocket": "Ellenőrizd, hogyan kell konfigurálni a WebSocket számára", + "Most likely causes:": "Legvalószínűbb okok:", + "There might be a typing error in the address.": "Lehet, hogy gépelési hiba van a címben.", + "What you can try:": "Amit kipróbálhatsz:", + "Go back to the previous page.": "Térj vissza az előző oldalra.", + "Coming Soon": "Hamarosan", + "Connection String": "Csatlakozási karakterlánc", + "statusPageSpecialSlugDesc": "Speciális slug {0}: ez az oldal akkor jelenik meg, ha nincs slug megadva", + "certificationExpiryDescription": "A HTTPS-monitorok értesítést váltanak ki, ha a TLS-tanúsítvány lejár:", + "Setup Docker Host": "Docker Gazda beállítása", + "Connection Type": "Csatlakozás Típusa", + "Docker Daemon": "Docker Daemon", + "deleteDockerHostMsg": "Biztos, hogy törölni akarod ezt a docker hostot az összes monitorról?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Container Name / ID": "Konténer neve / Azonosító", + "Docker Hosts": "Docket Hosztok", + "Domain": "Domain", + "Docker Host": "Docket Hoszt", + "noDockerHostMsg": "Nem elérhető. Először állítson be egy Docker Gazdát.", + "DockerHostRequired": "Kérlek állítsd be a Docker Hosztot ehhez a monitorhoz.", + "tailscalePingWarning": "A Tailscale Ping monitor használatához telepítenie kell az Uptime Kuma-t Docker nélkül, és telepíteni kell a Tailscale klienst is a szerverre.", + "Workstation": "Munkaállomás", + "telegramMessageThreadID": "(Választható) Üzenetszál azonosítója", + "telegramMessageThreadIDDescription": "Választható Egyedi azonosítója a fórum célüzenetszálának (témájának); csak a fórum szupercsoportok esetében", + "telegramSendSilently": "Csendben küldeni", + "telegramProtectContent": "Védi továbbítást/mentést", + "telegramProtectContentDescription": "Ha engedélyezve van, a bot üzenetei a Telegramban védve lesznek a továbbítástól és a mentéstől.", + "Trigger type:": "Kioldó típusa:", + "Event data:": "Egyemény adata:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Ezután válasz ki egy műveletet, például állítsa a jelenetet úgy, hogy egy RGB fény piros legyen.", + "Frontend Version": "Frontend verzió", + "Frontend Version do not match backend version!": "Frontend verzió nem egyezik a backend verzióval!", + "backupRecommend": "Helyette készíts biztonsági mentést a kötetről vagy az adatmappáról (./data/) közvetlenül.", + "recurringInterval": "Intervallum", + "Recurring": "Ismétlődő", + "strategyManual": "Aktív/inaktív manuálisan", + "endDateTime": "Befejezés dátuma/időpontja", + "startDateTime": "Kezdés dátuma/időpontja", + "cronExpression": "Cron kifejezés", + "cronSchedule": "Időzítő: ", + "invalidCronExpression": "Érvénytelen Cron kifejezés: {0}", + "chromeExecutable": "Chrome/Chromium futtatható", + "Single Maintenance Window": "Egyetlen karbantartási ablak", + "Maintenance Time Window of a Day": "Egy nap karbantartási időablak", + "Effective Date Range": "Hatályos dátumtartomány (opcionális)", + "Schedule Maintenance": "Karbantartás ütemezése", + "DateTime Range": "DátumIdő tartomány", + "wayToGetZohoCliqURL": "Megtudhatja, hogy hogyan hozz létre egy webhook URL-címet {0}.", + "chromeExecutableDescription": "A Docker-felhasználók számára, ha a Chromium még nincs telepítve, a telepítés és a teszteredmény megjelenítése néhány percet vehet igénybe. 1 GB lemezterületet foglal.", + "loadingError": "Nem sikerült lekérni az adatokat, kérjük, próbálja meg később újra.", + "cloneOf": "{0} duplikációja", + "dataRetentionTimeError": "A megőrzési időszaknak 0-nak vagy annál nagyobbnak kell lennie", + "infiniteRetention": "Végtelen visszatartás esetén állítsa 0-ra.", + "confirmDeleteTagMsg": "Biztos, hogy törölni szeretné ezt a címkét? Az ehhez a címkéhez tartozó monitorok nem kerülnek törlésre.", + "enableGRPCTls": "Engedélyezi a gRPC-kérelem küldését TLS-kapcsolattal", + "octopushAPIKey": "\"API-kulcs\" a HTTP API hitelesítő adatokból a vezérlőpultban", + "octopushLogin": "\"Bejelentkezés\" a HTTP API hitelesítő adatokból a vezérlőpultban", + "pushoversounds pushover": "Pushover (alapértelmezett)", + "Proto Service Name": "Proto szolgáltatás neve", + "Proto Method": "Proto módszer", + "Proto Content": "Proto tartalom", + "Economy": "Gazdaság", + "Lowcost": "Alacsonyár", + "SendKey": "KulcsKüldés", + "SMSManager API Docs": "SMSManager API dokumentáció ", + "Gateway Type": "Átjáró típusa", + "high": "magas", + "Remove domain": "'{0}' domain eltávolítása", + "You can divide numbers with": "Számokat oszthatunk a", + "Base URL": "Bázis URL", + "goAlertInfo": "A GoAlert egy nyílt forráskódú alkalmazás az ügyeleti időbeosztáshoz, az automatikus eszkalációhoz és értesítésekhez (például SMS vagy hanghívás). Automatikusan hívja be a megfelelő személyt, a megfelelő módon és a megfelelő időben! {0}", + "AccessKeyId": "Hozzáférési Kulcs Azonosítója", + "PhoneNumbers": "Telefonszámok", + "TemplateCode": "SablonKód", + "SignName": "TáblaNév", + "Sms template must contain parameters: ": "Az sms-sablonnak paramétereket kell tartalmaznia: ", + "Bark Endpoint": "Bark végpont", + "Bark Group": "Bark csoport", + "Bark Sound": "Bark hang", + "WebHookUrl": "WebHookUrl", + "High": "Magas", + "Bark API Version": "Bark API Verzió", + "promosmsAllowLongSMS": "Hosszú üzenet engedélyezése", + "Huawei": "Huawei", + "Integration Key": "Integrációs kulcs", + "Integration URL": "Integrációs URL", + "Auto resolve or acknowledged": "Automatikus feloldás vagy nyugtázás", + "do nothing": "ne csináljon semmit", + "auto acknowledged": "automatikus visszaigazolás", + "auto resolve": "automatikus feloldás", + "smseagleTo": "Telefonszám(ok)", + "smseagleContact": "Telefonkönyv kapcsolattartó neve(i)", + "onebotHttpAddress": "OneBot HTTP cím", + "onebotMessageType": "OneBot HTTP cím", + "onebotPrivateMessage": "Privát", + "onebotUserOrGroupId": "Csoport/Felhasználó azonosító", + "onebotSafetyTips": "A biztonság érdekében be kell állítani a hozzáférési jelszót", + "Continue": "Folytatás", + "Google Analytics ID": "Google Analitika Azonosító", + "RadiusCalledStationIdDescription": "A hívott eszköz azonosítója", + "RadiusCallingStationId": "Hívó állomás azonosítója", + "RadiusCallingStationIdDescription": "A hívó eszköz azonosítója", + "Domain Names": "Domain nevek", + "signedInDisp": "Bejelentkezve, mint {0}", + "signedInDispDisabled": "Belépés letiltva.", + "Add a new expiry notification day": "Új lejárati értesítési nap hozzáadása", + "Remove the expiry notification": "A lejáratról szóló értesítés napjának eltávolítása", + "Show Powered By": "Jelenjen meg a \"Szolgáltatva által\"t", + "RadiusSecret": "Titkos sugár", + "RadiusSecretDescription": "Megosztott titok az ügyfél és a szerver között", + "RadiusCalledStationId": "Hívott állomás azonosítója", + "Date and Time": "Dátum és idő", + "enableNSCD": "Az NSCD (Name Service Cache Daemon) engedélyezése az összes DNS-kérés gyorsítótárazásához", + "Edit Maintenance": "Karbantartás szerkesztése", + "smseagleGroup": "Telefonkönyv csoport neve(i)", + "styleElapsedTime": "Az eltelt idő a heartbeat sáv alatt", + "styleElapsedTimeShowNoLine": "Megjelenítés (nincs sor)", + "styleElapsedTimeShowWithLine": "Megjelenítés (vonallal)", + "disableCloudflaredNoAuthMsg": "No Auth módban vagy, jelszóra nincs szükség.", + "wayToGetLineNotifyToken": "Hozzáférési jelszót kaphat {0}-tól/-től", + "Long-Lived Access Token": "Hosszú élettartamú hozzáférési token", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "A hosszú élettartamú hozzáférési token létrehozásához kattintson a profilja nevére (balra lent), görgessen az aljára, majd kattintson a Token létrehozása gombra. ", + "Notification Service": "Értesítési szolgáltatás", + "default: notify all devices": "alapértelmezett: minden eszköz értesítése", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Az Értesítési szolgáltatások listája megtalálható a Home Assistantban a \"Fejlesztői eszközök > Szolgáltatások\" menüpont alatt, ahol az \"értesítés\" kifejezésre keresve megtalálja a készülék/telefon nevét.", + "Automations can optionally be triggered in Home Assistant:": "Az automatizálás opcionálisan elindítható a Home Assistantban:", + "Event type:": "Esemény típusa:", + "Topic": "Téma", + "WeCom Bot Key": "WeCom Bot Kulcs", + "Setup Proxy": "Proxy beállítása", + "Proxy Protocol": "Proxy Protokol", + "Proxy Server": "Proxy szerver", + "Proxy server has authentication": "A proxy szerver hitelesítéssel rendelkezik", + "smseagleRecipientType": "Címzett típusa", + "smseagleRecipient": "Címzett(ek) (több címzettet vesszővel kell elválasztani)", + "smseagleToken": "API hozzáférési jelszó", + "smseagleUrl": "Az Ön SMSEagle eszközének URL címe", + "smseagleEncoding": "Unicode-ként küldés (alapértelmezett=GSM-7)", + "smseaglePriority": "Üzenet prioritása (0-9, legmagasabb prioritás = 9)", + "Recipient Number": "Fogadó telefonszáma", + "From Name/Number": "A névtől/számtól", + "Leave blank to use a shared sender number.": "Hagyd üresen, ha megosztott feladószámot szeretnél használni.", + "Octopush API Version": "Octopush API verzió", + "Legacy Octopush-DM": "Octopush-DM örökség", + "ntfy Topic": "ntfy Téma", + "onebotGroupMessage": "Csoport", + "SecretAccessKey": "Titkos Hozzáférési Kulcs", + "Invert Keyword": "Kulcsszó invertálása", + "or": "vagy", + "For safety, must use secret key": "Biztonsági okoból kötelező a titkos kulcs használata", + "emailCustomisableContent": "Testreszabható tartalom", + "leave blank for default subject": "hagyja üresen az alapértelmezett email tárgyát", + "smtpLiquidIntroduction": "A következő két mező a Liquid sablonozási nyelven keresztül szerkeszthető. Kérjük, olvassa el a következőt: {0} a használati utasításokért. Ezek az elérhető változók:", + "Learn More": "Tudjon meg többet", + "Notify Channel": "Csatorna értesítése", + "install": "Telepítés", + "installing": "Telepítés", + "grpcMethodDescription": "A metódus neve camelCase formátumba konvertálva, például sayHello, check stb.", + "Custom Monitor Type": "Egyedi monitor típus", + "uninstall": "Eltávolítás", + "confirmUninstallPlugin": "Biztosan eltávolítja ezt a bővítményt?", + "notificationRegional": "Regionális", + "uninstalling": "Eltávolítás", + "Clone Monitor": "Monitor Duplikálása", + "Clone": "Duplikáció", + "emailCustomBody": "Egyedi email törzs", + "leave blank for default body": "hagyja üresen az alapértelmezett email törzsét", + "emailTemplateServiceName": "Szolgáltatás neve", + "emailTemplateHostnameOrURL": "Hosztnév vagy URL", + "emailTemplateMonitorJSON": "a monitort leíró objektum", + "emailTemplateHeartbeatJSON": "a szívverést leíró objektum", + "emailTemplateMsg": "az értesítés üzenete", + "emailTemplateLimitedToUpDownNotification": "csak FEL/LE szívverés esetén érhető el, egyébként null érték", + "pushoverMessageTtl": "TTL üzenet (másodperc)", + "Platform": "Platform", + "aboutNotifyChannel": "A Csatorna értesítése opció, értesítést fog küldeni a csatorna összes tagjának, függetlenül a tagok elérhetőségétől.", + "setup a new monitor group": "állítson be egy új monitorcsoportot", + "openModalTo": "felugró ablak megnyitása ide: {0}", + "Add a domain": "Adjon hozzá egy domaint", + "Server URL should not contain the nfty topic": "A szerver URL-je nem tartalmazhatja az nfty témát", + "PushDeer Server": "PushDeer Szerver", + "pushDeerServerDescription": "Hagyja üresen a hivatalos szerver használatához", + "Edit Tag": "Címke szerkesztése", + "plugin": "Bővítmény | Bővítmények", + "Expiry date": "Lejárati dátum", + "pagertreeDoNothing": "Ne csináljon semmit", + "ntfyUsernameAndPassword": "Felhasználónév és jelszó", + "Body Encoding": "Törzs kódolás", + "API Keys": "API Kulcsok", + "Expiry": "Lejár", + "Don't expire": "Nem jár le", + "Add Another": "Másik hozzáadása", + "twilioApiKey": "API-kulcs (opcionális)", + "Monitor Setting": "{0} monitor beállítása", + "pagertreeResolve": "Automatikus megoldás", + "wayToGetPagerTreeIntegrationURL": "Miután létrehozta az Uptime Kuma integrációt a PagerTree-ben, másolja át a végpontot. A részletek megtekintése {0}", + "lunaseaTarget": "Cél", + "ntfyPriorityHelptextAllEvents": "Minden esemény maximális prioritással került elküldésre", + "ntfyPriorityHelptextAllExceptDown": "Minden esemény ezzel a prioritással került elküldésre, kivéve a {0} események, amelyek prioritása {1}", + "twilioAccountSID": "Fiók SID", + "Mechanism": "Mechanizmus", + "No API Keys": "Nincsenek API kulcsok", + "Monitor Group": "Monitor csoport", + "ntfyAuthenticationMethod": "Hitelesítési metódus", + "Host URL": "Gazda URL", + "Select message type": "Válasszon üzenet típust", + "Send to channel": "Küldés erre a csatornára", + "Create new forum post": "Új fórum üzenet készítése", + "Refresh Interval Description": "Az állapot megfigyelő {0} másodpercenként teljesen újra frissíti a weboldalt", + "Refresh Interval": "Frissítési intervallum", + "postToExistingThread": "Létező szál / fórum -ra küldés", + "forumPostName": "Fórum üzenet neve", + "threadForumPostID": "Szál / Fórum üzenet ID", + "e.g. {discordThreadID}": "például. {discordThreadID}", + "locally configured mail transfer agent": "helyileg beállított email továbbító", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Adja meg a csatlakoztatni kívánt szerver gazdagép nevét vagy {localhost}, ha a {local_mta}-t szeretné használni", + "ignoreTLSErrorGeneral": "Mellőzze a TLS/SSL hibákat a csatlakozáshoz", + "threemaRecipientTypeIdentityFormat": "8 karakter", + "threemaRecipientTypeEmail": "Email cím", + "threemaSenderIdentity": "Gateway-azonosító", + "threemaRecipientTypeIdentity": "Threema-azonosító", + "threemaSenderIdentityFormat": "8 karakter, általában *-al kezdődik", + "threemaRecipientTypePhone": "Telefonszám", + "max 15 digits": "max 15 karakter", + "cellsyntDestination": "A címzett telefonszáma nemzetközi formátumban megadva. A kezdő 00-t követően az országkód, pl. 003612127654 egy magyarországi 0612127654 szám esetében (max 17 karakter összesen). HTTP lekérdezésenként max 25000, vesszővel elválaszott címzett.", + "Telephone number": "Telefonszám", + "Allow Long SMS": "Hosszú SMS engedélyezve", + "now": "most", + "Channel access token (Long-lived)": "Csatorna-hozzáférési token (Hosszú-élettartamú)", + "Mentioning": "Megemlítés", + "Don't mention people": "Ne említsen embereket", + "aboutSlackUsername": "Megváltoztatja az üzenet feladójának megjelenített nevét. Ha valakit meg akar említeni, helyette írja be a barátságos névbe.", + "smspartnerApiurl": "Az API-kulcsot az irányítópulton találja {0}", + "smspartnerPhoneNumberHelptext": "A számnak nemzetközi formátumúnak kell lennie {0}, {1}. Több szám elválasztása esetén jelölje {2}", + "toastSuccessTimeout": "Sikerértesítések időkorlátja", + "and": "és", + "Your User ID": "Felhasználói azonosítód", + "Mention group": "Említ {csoport}", + "smspartnerPhoneNumber": "Telefonszám(ok)", + "smspartnerSenderName": "SMS feladó neve", + "Generate": "Generálás", + "pagertreeIntegrationUrl": "Integrációs URL", + "pagertreeUrgency": "Sürgősség", + "pagertreeSilent": "Csendes", + "pagertreeLow": "Alacsony", + "pagertreeMedium": "Közepes", + "pagertreeCritical": "Kritikus", + "twilioAuthToken": "Hitelesítési Token / API Kulcs", + "Show Clickable Link": "Kattintható link megjelenítése", + "Group": "Csoport", + "monitorToastMessagesLabel": "Toast értesítések figyelése", + "toastErrorTimeout": "Hibaértesítések időkorlátja", + "Json Query Expression": "Json lekérdezés", + "ignoredTLSError": "TLS/SSL hibák figyelmen kívül hagyva", + "time ago": "előtt", + "-year": "- év", + "deleteAPIKeyMsg": "Biztosan törölni szeretné ezt az API kulcsot?", + "RabbitMQ Nodes": "RabbitMQ menedszer csomópontok", + "jsonQueryDescription": "Végezzen JSON-lekérdezést a válasz alapján, és ellenőrizze a várt értéket (a visszatérési értéket a rendszer karakterlánccá alakítja az összehasonlításhoz). Nézze meg a {0} webhelyet a lekérdezés paramétereivel kapcsolatos dokumentációért. A test környezet itt található: {1}.", + "Authorization Identity": "Jogosultság Identitás", + "noGroupMonitorMsg": "Nem lehetséges. Először létre kell hozni egy monitorozandó csoportot.", + "wayToGetFlashDutyKey": "Az Uptime Kuma Flashduty-val való integrálásához: Lépjen a Csatornák > Válasszon ki egy csatornát > Integrációk > Új integráció hozzáadása menüpontra, válassza az Uptime Kuma lehetőséget, és másolja ki a Push URL-t.", + "gamedigGuessPortDescription": "A Valve Server Query Protocol által használt port eltérhet az kliens portjától. Próbáld ki ezt, ha a monitor nem tud csatlakozni a kiszolgálóhoz.", + "remoteBrowsersDescription": "A távoli böngészők a Chromium helyi futtatásának alternatívája. Állítsa be egy olyan szolgáltatással, mint a browserless.io, vagy csatlakozzon a sajátjával", + "mongodbCommandDescription": "Egy utasítás futtatásának kérése a MongoDB adatbázishoz. A rendelkezésre álló parancsokról a {documentation}", + "receiverInfoSevenIO": "Ha a fogadó szám nem Németországban van, akkor a szám elé kell írni az országkódot (pl. az USA-ból érkező 1-es országkódhoz a 017612121212 helyett a 117612121212 címet kell használni)", + "conditionAddGroup": "Csoport hozzáadása", + "less than or equal to": "kisebb vagy egyenlő, mint", + "Alphanumerical string and hyphens only": "Csak alfanumerikus karakterlánc és kötőjelek", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Időérzékeny értesítések azonnal kézbesítve lesznek, még akkor is ha az eszköz ne zavarjanak üzemmódban van.", + "successEnabled": "Sikeresen engedélyezve.", + "smspartnerSenderNameInfo": "3..=11 általános karakternek kell lennie", + "Show Clickable Link Description": "Ha be van jelölve, mindenkinek akinek hozzáférése van ehhez a státusz oldalhoz, hozzáférése van a monitor URL-hez is.", + "Enter the list of brokers": "Adja meg a brókerek listáját", + "Press Enter to add broker": "Bróker hozzáadásához nyomja meg az ENTER billentyűt", + "Enable Kafka SSL": "Kafka SSL engedélyezése", + "Enable Kafka Producer Auto Topic Creation": "Kafka Producer automatikus téma létrehozásának engedélyezése", + "Kafka Producer Message": "Kafka Producer üzenet", + "Kafka SASL Options": "Kafka SASL opciók", + "Pick a SASL Mechanism...": "Válassz egy SASL mechanizmus-t…", + "AccessKey Id": "Hozzáférési Kulcs ID", + "Secret AccessKey": "Titkos Hozzáférési Kulcs", + "Session Token": "Munkamenet-azonosító", + "nostrRelaysHelp": "Soronként egy közvetítő URL", + "nostrSender": "Küldő privát kulcsa (nsec)", + "nostrRecipients": "Címzettek nyilvános kulcsai (npub)", + "nostrRecipientsHelp": "npub formátum, soronként egy", + "showCertificateExpiry": "Tanúsítvány lejáratának megjelenítése", + "cacheBusterParam": "Adja hozzá a {0} paramétert", + "cacheBusterParamDescription": "Véletlenszerűen generált paraméter a gyorsítótárak kihagyásához.", + "Message format": "Üzenet formátuma", + "Send rich messages": "RCS üzenetek küldése", + "wayToGetBitrix24Webhook": "Webhookot a következő lépésekkel hozhat létre {0}", + "nostrRelays": "Nostr közvetítők", + "bitrix24SupportUserID": "Adja meg felhasználói azonosítóját a Bitrix24-ben. Az azonosítót a felhasználó profiljába lépve a linkről tudhatja meg.", + "authInvalidToken": "Érvénytelen token.", + "2faEnabled": "Kétlépcsős azonosítás engedélyezve.", + "successDisabled": "Sikeresen letiltva.", + "Remote Browsers": "Távoli böngészők", + "Remote Browser": "Távoli böngésző", + "Add a Remote Browser": "Távoli böngésző hozzáadása", + "Remote Browser not found!": "Távoli böngésző nem található!", + "self-hosted container": "Helyi futtatású konténer", + "useRemoteBrowser": "Távoli böngésző használata", + "deleteRemoteBrowserMessage": "Biztos-e Ön benne, hogy törölni akarja ezt a távoli böngészőt az összes monitorozandó számára?", + "GrafanaOncallUrl": "Grafana Hívás URL", + "Browser Screenshot": "Böngésző képernyőkép", + "Command": "Utasítás", + "wayToGetSevenIOApiKey": "Látogasson el a műszerfalra az app.seven.io > developer > api key > a zöld hozzáadás gombra", + "senderSevenIO": "Szám vagy név küldése", + "receiverSevenIO": "Fogadó telefonszáma", + "apiKeySevenIO": "SevenIO API kulcs", + "wayToWriteWhapiRecipient": "A telefonszám a nemzetközi előtaggal, de az elején lévő pluszjel nélkül ({0}), a kapcsolattartó azonosítója ({1}) vagy a csoport azonosítója ({2}).", + "Separate multiple email addresses with commas": "Több e-mail cím elválasztása vesszővel", + "conditionDeleteGroup": "Csoport törlése", + "conditionValuePlaceholder": "Érték", + "equals": "egyenlő", + "not equals": "nem egyenlő", + "contains": "tartalmaz", + "not contains": "nem tartalmaz", + "ends with": "végződik a", + "not ends with": "nem végződik a", + "greater than": "nagyobb, mint", + "less than": "kisebb, mint", + "greater than or equal to": "nagyobb vagy egyenlő, mint", + "record": "sor", + "Notification Channel": "Értesítési csatorna", + "Sound": "Hang", + "Arcade": "Árkád", + "Correct": "Helyes", + "Harp": "Hárfa", + "Reveal": "Felfed", + "Bubble": "Buborék", + "Doorbell": "Ajtócsengő", + "Flute": "Fuvola", + "Money": "Pénz", + "Scifi": "Tudományos-fantasztikus", + "Clear": "Törlés", + "Elevator": "Felvonó", + "Guitar": "Gitár", + "Time Sensitive (iOS Only)": "Időérzékeny (Csak iOS)", + "From": "Tól", + "Can be found on:": "Megtalálható: {0}", + "The phone number of the recipient in E.164 format.": "A címzett telefonszáma E.164-es formában.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Vagy egy feladó azonosító, vagy egy telefonszám E.164 formátumban kell lennie, ha válaszokat szeretne fogadni.", + "RabbitMQ Username": "RabbitMQ felhaszálónév", + "RabbitMQ Password": "RabbitMQ jelszó", + "SendGrid API Key": "SendGrid API kulcs", + "pagertreeHigh": "Magas", + "noOrBadCertificate": "Nincs/rossz tanúsítvány", + "whatHappensAtForumPost": "Új fórumbejegyzés létrehozása. NEM küldi el a meglévő hozzászólásokhoz. Meglévő hozzászólásokhoz az \"{option}\" használatával lehet hozzászólni", + "snmpCommunityStringHelptext": "Ez a karakterlánc jelszóként szolgál az SNMP-kompatibilis eszközök hitelesítésére és a hozzáférés ellenőrzésére. Egyeztesse az SNMP-eszköz konfigurációjával.", + "snmpOIDHelptext": "Adja meg a megfigyelni kívánt érzékelő vagy állapot OID azonosítóját. Ha nem biztos az OID-ben, használjon hálózatirányítási eszközöket, például MIB-böngészőket vagy SNMP-szoftvereket.", + "privateOnesenderDesc": "Győződjön meg róla, hogy a telefonszám érvényes. Üzenet küldése privát telefonszámra, például: 628123456789", + "Authorization Header": "Hitelesítési Fejléc", + "wayToGetDiscordThreadId": "Szál / fórum bejegyzés ID lekérése hasonló, a csatorna ID lekéréséhez. Itt olvashatsz tovább az ID-k lekérésől{0}", + "Badge Type": "Jelvény típusa", + "Badge Duration (in hours)": "Jelvény Időtartam (órákban)", + "Badge Label": "Jelvény Címke", + "Badge Prefix": "Jelvény Érték Előtag", + "Badge Suffix": "Jelvény Érték Utótag", + "Badge Label Color": "Jelvény Címke Szín", + "Badge Color": "Jelvény Szín", + "Badge Label Prefix": "Jelvény Címke Előtag", + "Badge Preview": "Jelvény Előnézet", + "Badge Label Suffix": "Jelvény Címke Utótag", + "Badge Up Color": "Jelvény Online Szín", + "Badge Down Color": "Jelvény Offline Szín", + "Badge Pending Color": "Jelvény Folyamatban Levő Szín", + "Badge Maintenance Color": "Jelvény Karbantartás Szín", + "Badge Warn Color": "Jelvény Figyelmeztetés Szín", + "Badge Warn Days": "Jelvény Figyelmeztetés Napok", + "Badge Down Days": "Jelvény Offline Napok", + "Badge value (For Testing only.)": "Jelvény érték (Csak tesztelés számára.)", + "Badge URL": "Jelvény URL", + "Kafka Brokers": "Kafka brókerek", + "Kafka Topic Name": "Kafka Téma Név", + "wayToGetWhapiUrlAndToken": "Az API URL-t és a tokent lekérheted a kívánt csatornára belépve a {0}", + "API URL": "API URL", + "What is a Remote Browser?": "Mi az a távol böngésző?", + "gtxMessagingApiKeyHint": "Az API kulcsokat megtalálhatod: Útvonalválasztási fiókom > Fiók megtekintése > API hitelesítőadatok > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Telefonszámból / Átviteli útvonal kiindulási címe (TPOA)", + "gtxMessagingFromHint": "Mobiltelefonokon a címzettek az üzenet feladójaként a TPOA-t látják. Legfeljebb 11 alfanumerikus karakter, egy rövidkód, a helyi hosszúkód vagy nemzetközi számok ({e164}, {e212} vagy {e214}) engedélyezettek", + "To Phone Number": "Telefonszámmá", + "gtxMessagingToHint": "Nemzetközi formátum, vezető \"+\" jel és ({e164}, {e212} vagy {e214})", + "Alphanumeric (recommended)": "Alfanumerikus (ajánlott)", + "cellsyntOriginatortypeAlphanumeric": "Alfanumerikus karakterlánc (legfeljebb 11 alfanumerikus karakter). A címzettek nem tudnak válaszolni az üzenetre.", + "Originator": "Kezdeményező", + "cellsyntOriginator": "Látható a címzett mobiltelefonján az üzenet küldőjeként. A megengedett értékek és a funkció az kezdeményező-típus(originatortype) paramétertől függ.", + "Destination": "Cél", + "cellsyntSplitLongMessages": "Hosszú üzenetek tördelése legfeljebb 6 részre. 153 x 6 = 918 karakter.", + "max 11 alphanumeric characters": "legfeljebb 11 alfanumerikus karakter", + "Community String": "Közösségi Karakterlánc", + "OID (Object Identifier)": "OID (Objektum azonosító)", + "Condition": "Feltétel", + "SNMP Version": "SNMP Verzió", + "Please enter a valid OID.": "Kérem adjon meg egy helyes OID-t.", + "wayToGetThreemaGateway": "Regisztrálhat a Threema Gatewayre {0}.", + "threemaRecipient": "Címzett", + "threemaRecipientTypePhoneFormat": "E.164, vezető \"+\" nélkül", + "threemaApiAuthenticationSecret": "Gateway-ID Kulcs", + "threemaBasicModeInfo": "Megjegyzés: Ez az integráció a Threema Gateway-t alapmódban használja (szerver alapú titkosítás). További részletek {0} találhatók.", + "apiKeysDisabledMsg": "Az API-kulcsok le vannak tiltva, mivel a hitelesítés le van tiltva.", + "Host Onesender": "Onesender futtató gép", + "Token Onesender": "Onesender Token", + "Recipient Type": "Címzett Típusa", + "Private Number": "Privát Telefonszám", + "groupOnesenderDesc": "Győződjön meg róla, hogy a GroupID érvényes. Üzenet küldése a csoportba, például: 628123456789-342345", + "Group ID": "Group ID", + "wayToGetOnesenderUrlandToken": "Az URL-t és a Tokent az Onesender weboldalán keresztül kaphatja meg. További információ {0}", + "Add Remote Browser": "Távoli böngésző hozzáadása", + "New Group": "Új csoport", + "Group Name": "Csoport név", + "OAuth2: Client Credentials": "OAuth2: Ügyfél hitelesítő adatok", + "Authentication Method": "Hitelesítési metódus", + "Form Data Body": "Adat törzsből", + "OAuth Token URL": "OAuth Token URL", + "Client ID": "Ügyfél Azonosító", + "Client Secret": "Ügyfél Kulcs", + "OAuth Scope": "OAuth hatókör", + "Badge Style": "Jelvény Stílus", + "whapiRecipient": "Telefonszám / Kontakt ID / Csoport ID", + "documentationOf": "{0} Dokumentáció", + "threemaRecipientType": "Címzett Típusa", + "rabbitmqNodesRequired": "Állítsa be a csomópontokat ehhez a figyelőhöz.", + "rabbitmqNodesDescription": "Adja meg a RabbitMQ menedszer csomópontok URL-jét beleértve a protokollt és a port számát is. Példa: {0}", + "shrinkDatabaseDescriptionSqlite": "SQLite adatbázis {vacuum} indítása. Az {auto_vacuum} már engedélyezve van, de ez nem defragmentálja az adatbázist, és nem csomagolja újra az egyes adatbázisoldalakat, ahogyan a {vacuum} parancs teszi.", + "invertKeywordDescription": "Keresse meg, hogy a kulcsszó inkább hiányzik-e, mint jelen van.", + "No tags found.": "Nem található címkék.", + "twilioToNumber": "Címzett szám", + "twilioFromNumber": "Feladó szám", + "Open Badge Generator": "Nyílt jelvény generátor", + "Badge Generator": "{0} jelvény generátora", + "monitorToastMessagesDescription": "A figyelőktől érkező Toast értesítések eltűnnek megadott másodpercen belül. Eltűnés kikapcsolásához állítsd \"-1\"-re. Toast értesítések kikapcsolásához pedig \"0\"-ra.", + "gamedigGuessPort": "Gamedig: Port", + "remoteBrowserToggle": "Alapértelmezetten Chromium böngésző fut az Uptime Kuma konténerben. Távoli böngésző használatához aktiválja a kapcsolót.", + "callMeBotGet": "Itt létre tud hozni egy végpontot {0}, {1} és {2} számára. Tartsa észben, lehet le lesz korlátozva. A korlátozások a következők: {3}", + "Originator type": "A kezdeményező típusa", + "cellsyntOriginatortypeNumeric": "Numerikus érték (legfeljebb 15 számjegy) nemzetközi formátumú telefonszámmal, 00 előtag nélkül (például a 06-20-534-6789 magyar számot 36205346789-ként kell beállítani). A címzettek válaszolhatnak az üzenetre.", + "Optional: Space separated list of scopes": "Opcionális: A hatókörök (scopes) listája szóközzel elválasztva", + "Go back to home page.": "Vissza a fő oldalra.", + "Lost connection to the socket server.": "Kapcsolat megszakadt a socket szerverrel.", + "Cannot connect to the socket server.": "Nem képes kapcsolódni a socket szerverhez.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL", + "Conditions": "Feltételek", + "conditionAdd": "Feltétel hozzáadása", + "conditionDelete": "Feltétel törlése", + "signl4Docs": "A SIGNL4 konfigurálásával és a SIGNL4 webhook URL-címének beszerzésével kapcsolatos további információkat itt talál: {0}.", + "starts with": "kezdődik", + "not starts with": "nem kezdődik", + "Custom sound to override default notification sound": "Eredeti értesítési hang felülírása egyedi hanggal", + "rabbitmqNodesInvalid": "Kérjük, használjon teljesen minősített (\"http\"-vel kezdődő) URL-t a RabbitMQ csomópontokhoz.", + "rabbitmqHelpText": "Figyelő használatához, engedélyeznie kell a Management Plugin-t a RabbitMQ beállításai között. További információért, tekintse meg {rabitmq_documentation} dokumentumot.", + "Fail": "Hiba", + "Pop": "Megjelen", + "Bitrix24 Webhook URL": "Bitrix24 Webhook URL", + "wayToGetHeiiOnCallDetails": "A Trigger ID és az API kulcsok megszerzésének módja a {dokumentáció}", + "telegramServerUrl": "(Választható) Szerver Url", + "telegramServerUrlDescription": "A Telegram bot api korlátozásainak feloldása vagy hozzáférés a blokkolt területekhez (Kína, Irán stb.). További információért kattintson a {0} gombra. Alapértelmezett: {1}", + "wahaSession": "Munkamenet", + "wahaChatId": "Beszélgetés azonosító (Telefonszám / Kontakt azonosító / Csoport azonosító)", + "wayToGetWahaApiUrl": "WAHA példányod URL-je.", + "wayToGetWahaApiKey": "Az API-kulcs a WHATSAPP_API_KEY környezeti változó értéke, amelyet a WAHA futtatásához használt.", + "wayToWriteWahaChatId": "A telefonszám nemzetközi előtaggal, de az elején lévő pluszjel ({0}), a kapcsolattartó azonosítója ({1}) vagy a csoportazonosító ({2}) nélkül. A WAHA Session értesítéseket küld erre a beszélgetési azonosítóra.", + "Plain Text": "Sima Szöveg", + "Message Template": "Üzenet Sablon", + "Template Format": "Sablon Formátum", + "wayToGetWahaSession": "A munkamenetből WAHA küld egy értesítést a Beszélgetés azonosítóra. Az értesítést megtalálhatod a WAHA műszerfalon.", + "YZJ Webhook URL": "YZJ Webhook URL", + "YZJ Robot Token": "YZJ Robot token", + "templateServiceName": "szolgáltatás név", + "templateHostnameOrURL": "kiszolgáló név vagy URL", + "templateStatus": "státusz", + "telegramUseTemplate": "Egyéni üzenetsablon használata", + "telegramUseTemplateDescription": "Ha engedélyezve van, az üzenet egy egyéni sablon szerint lesz elküldve.", + "telegramTemplateFormatDescription": "Telegram különböző jelölőnyelvek használatát engedi, további információkért lásd {0}.", + "webhookPostMethodDesc": "POST jó modern HTTP szerverek számára.", + "HTTP Method": "HTTP metódus", + "webhookGetMethodDesc": "A GET lekérdezési paraméterként küldi az adatokat, és nem teszi lehetővé a törzs konfigurálását. Hasznos az Uptime Kuma Push monitorok elindításához.", + "defaultFriendlyName": "Új Monitor", + "smseagleContactV2": "Telefonkönyv kontakt ID-k", + "deleteGroupMsg": "Biztosan törölni szeretné ezt a csoportot?", + "deleteChildrenMonitors": "Törölje a közvetlen alárendelt monitorokat és azok gyermekeit is, ha vannak ilyenek | Törölje az összes {count} közvetlen alárendelt monitort és azok gyermekeit is, ha vannak ilyenek", + "Events cleared successfully": "Események sikeresen törölve.", + "No monitors found": "Nem találhatóak monitor-ok.", + "Could not clear events": "Nem sikerült törölni {failed}/{total} eseményt", + "Mention User List": "Felhasználói azonosító lista említése", + "Dingtalk Mobile List": "Mobilszám lista", + "Dingtalk User List": "Felhasználói ID lista", + "Enter a list of userId": "Adjon meg egy felhasználói ID listát", + "Invalid mobile": "Érvénytelen mobilszám [{mobile}]", + "Invalid userId": "Érvénytelen felhasználói ID [{userId}]", + "smseagleMsgType": "Üzenet típusa", + "smseagleMsgTtsAdvanced": "Text-to-speech haladó hívás", + "smseagleTtsModel": "Text-to-speech modell ID", + "smseagleApiType": "API verzió", + "smseagleApiv1": "APIv1 (meglevő projektek és visszafelé kompatibilitás miatt)", + "smseagleApiv2": "APIv2 (új integrációk számára ajánlott)", + "smseagleDuration": "Időtartam (másodpercben)", + "supportBaleChatID": "Közvetlen csevegés / csoport / csatorna csevegés ID támogatása", + "wayToGetBaleChatID": "A chat azonosítódat úgy szerezheted meg, hogy üzenetet küldesz a bot-nak, és erre az URL-re kattintva megtekinted a chat_id-t:", + "wayToGetBaleToken": "Token-t szerezhetsz innen {0}.", + "smseagleGroupV2": "Telefonkönyv csoport ID-k", + "smseagleMsgSms": "SMS üzenet (alapértelmezett)", + "smseagleMsgRing": "Csörgés", + "smseagleMsgTts": "Text-to-speech hívás", + "smseagleComma": "Több felsorolása esetén vesszővel kell elválasztani", + "Path": "Útvonal", + "mqttWebSocketPath": "MQTT WebSocket Útvonal", + "mqttWebsocketPathExplanation": "WebSocket útvonal az MQTT-hez WebSocket kapcsolatokon keresztül (pl. /mqtt)", + "mqttWebsocketPathInvalid": "Kérem egy érvényes WebSocket útvonal formátumot használjon", + "mqttHostnameTip": "Kérjük ezt a formátumot használja {hostnameFormat}", + "FlashDuty Push URL Placeholder": "Másolás a riasztási integrációs oldalról", + "twilioApiKeyHelptext": "Az API-kulcs megadása opcionális, de ajánlott. Megadhatja a TwilioConsole oldalról származó fiók SID-jét és AuthToken-jét, vagy a fiók SID-jét és az API-kulcs, valamint az API-kulcs titkos kódjának párosát", + "twilioMessagingServiceSID": "Üzenetküldő szolgáltatás SID (opcionális)", + "twilloMessagingServiceSIDHelptext": "Add meg az üzenetküldő szolgáltatás SID azonosítóját, ha a {twillo_messaging_service_help_link} szolgáltatást használod a küldők és funkciók kezeléséhez", + "Add Tags": "Címkék hozzáadása", + "tagAlreadyStaged": "Ez a címke (név és érték) már elő van készítve ehhez a köteghez.", + "tagNameExists": "Egy rendszer címke már létezik ezzel a névvel. Válassza ki a listából vagy használjon egy másik nevet.", + "tagAlreadyOnMonitor": "Ez a címke (név és érték) már szerepel a monitor-on vagy hozzáadás alatt áll.", + "Mention Mobile List": "Mobilszám lista említése", + "Enter a list of mobile": "Adjon meg egy mobilszám listát", + "smseagleDocs": "Ellenőrizze a dokumentációt vagy az APIv2 elérhetőségét: {0}", + "Template plain text instead of using cards": "Sima szöveg sablon használata kártyák helyett", + "issueWithGoogleChatOnAndroidHelptext": "Ez lehetővé teszi a hibák, például a {issuetackerURL} megkerülését is", + "descriptionHelpText": "A belső irányítópulton látható. A Markdown engedélyezett és a megjelenítés előtt szanitizálva van (megőrzi a szóközöket és a behúzást).", + "ntfyPriorityHelptextPriorityHigherThanDown": "A normál prioritásnak magasabbnak kell lennie, mint a(z) {0} prioritás. A(z) {1} prioritás magasabb, mint a(z) {0} prioritás {2}", + "auto-select": "Automatikus kiválasztás", + "Clone Maintenance": "Karbantartás klónozása", + "ariaPauseMaintenance": "Karbantartási ütemterv szüneteltetése", + "ariaResumeMaintenance": "Karbantartási ütemterv folytatása", + "ariaCloneMaintenance": "Másolat készítése a karbantartási ütemtervről", + "ariaEditMaintenance": "Szerkessze ezt a karbantartási ütemtervet", + "ariaDeleteMaintenance": "Törölje ezt a karbantartási ütemtervet", + "ntfyPriorityDown": "DOWN-események prioritása", + "Use HTML for custom E-mail body": "HTML használata egyedi E-mail törzshöz", + "FlashDuty Push URL": "Push URL", + "Clear All Events": "Összes esemény törlése", + "clearAllEventsMsg": "Biztosan törölni szeretné az összes eseményt?", + "SpugPush Template Code": "Sablon kód" +} diff --git a/src/lang/id-ID.json b/src/lang/id-ID.json new file mode 100644 index 0000000..83783d1 --- /dev/null +++ b/src/lang/id-ID.json @@ -0,0 +1,1463 @@ +{ + "languageName": "Bahasa Indonesia (Indonesian)", + "checkEverySecond": "Cek setiap {0} detik", + "retryCheckEverySecond": "Coba lagi setiap {0} detik", + "resendEveryXTimes": "Kirim ulang setiap {0} kali", + "resendDisabled": "Kirim ulang dinonaktifkan", + "retriesDescription": "Percobaan ulang maksimum sebelum layanan ditandai sebagai tidak aktif dan pemberitahuan dikirim", + "ignoreTLSError": "Abaikan kesalahan TLS/SSL untuk situs web HTTPS", + "upsideDownModeDescription": "Balikkan status menjadi terbalik. Jika layanan dapat dijangkau, statusnya adalah TIDAK AKTIF.", + "maxRedirectDescription": "Jumlah maksimum pengalihan yang harus diikuti. Atur ke 0 untuk menonaktifkan pengalihan.", + "acceptedStatusCodesDescription": "Pilih kode status yang dianggap sebagai tanggapan yang berhasil.", + "passwordNotMatchMsg": "Kata sandi kedua tidak cocok.", + "notificationDescription": "Harap atur notifikasi ke monitor agar berfungsi.", + "keywordDescription": "Kata kunci pencarian dalam HTML biasa atau respons JSON. Pencarian bersifat peka terhadap huruf besar/kecil.", + "pauseDashboardHome": "Dijeda", + "deleteMonitorMsg": "Anda yakin ingin menghapus monitor ini?", + "deleteNotificationMsg": "Anda yakin ingin menghapus notifikasi untuk semua monitor?", + "dnsPortDescription": "Port server DNS. Bawaan menggunakan 53. Anda dapat mengubah port kapan saja.", + "resolverserverDescription": "Cloudflare adalah server baku. Anda dapat menentukan suatu daftar dipisah koma dari alamat IP atau nama host.", + "rrtypeDescription": "Pilih RR Type yang mau Anda monitor", + "pauseMonitorMsg": "Anda yakin ingin menjeda?", + "enableDefaultNotificationDescription": "Untuk setiap monitor baru, notifikasi ini akan diaktifkan secara bawaan. Anda masih dapat menonaktifkan notifikasi secara terpisah untuk setiap monitor.", + "clearEventsMsg": "Anda yakin ingin menghapus semua kejadian di monitor ini?", + "clearHeartbeatsMsg": "Anda yakin ingin menghapus semua heartbeat di monitor ini?", + "confirmClearStatisticsMsg": "Anda yakin ingin menghapus semua statistik?", + "importHandleDescription": "Pilih 'Lewati yang ada' jika Anda ingin melewati setiap monitor atau notifikasi dengan nama yang sama. 'Timpa' akan menghapus setiap monitor dan notifikasi yang ada.", + "confirmImportMsg": "Apakah Anda yakin untuk mengimpor cadangan? Pastikan Anda telah memilih opsi impor yang tepat.", + "twoFAVerifyLabel": "Masukkan token Anda untuk memverifikasi 2FA:", + "tokenValidSettingsMsg": "Token benar! Anda sekarang dapat menyimpan pengaturan 2FA.", + "confirmEnableTwoFAMsg": "Apakah Anda yakin ingin mengaktifkan 2FA?", + "confirmDisableTwoFAMsg": "Apakah Anda yakin ingin menonaktifkan 2FA?", + "Settings": "Pengaturan", + "Dashboard": "Dasbor", + "New Update": "Pemutakhiran Baru", + "Language": "Bahasa", + "Appearance": "Penampilan", + "Theme": "Tema", + "General": "Umum", + "Primary Base URL": "Basis URL Utama", + "Version": "Versi", + "Check Update On GitHub": "Cek Pembaruan di GitHub", + "List": "Daftar", + "Add": "Tambah", + "Add New Monitor": "Tambahkan Monitor Baru", + "Quick Stats": "Statistik Singkat", + "Up": "Hidup", + "Down": "Mati", + "Pending": "Tertunda", + "Unknown": "Tidak diketahui", + "Pause": "Dijeda", + "Name": "Nama", + "Status": "Status", + "DateTime": "Tanggal/Waktu", + "Message": "Pesan", + "No important events": "Tidak ada peristiwa penting", + "Resume": "Lanjutkan", + "Edit": "Ubah", + "Delete": "Hapus", + "Current": "Saat ini", + "Uptime": "Waktu aktif", + "Cert Exp.": "Sertifikat Kedaluwarsa.", + "day": "hari", + "-day": "-hari", + "hour": "jam | jam", + "-hour": "-jam", + "Response": "Respon", + "Ping": "Ping", + "Monitor Type": "Tipe Monitor", + "Keyword": "Kata Kunci", + "Friendly Name": "Nama Yang Bersahabat", + "URL": "URL", + "Hostname": "Nama Host", + "Port": "Port", + "Heartbeat Interval": "Interval Heartbeat", + "Retries": "Mencoba lagi", + "Heartbeat Retry Interval": "Interval Pengulangan Heartbeat", + "Resend Notification if Down X times consecutively": "Kirim Ulang Pemberitahuan jika Tidak Aktif X kali berturut-turut", + "Advanced": "Tingkat Lanjut", + "Upside Down Mode": "Mode Terbalik", + "Max. Redirects": "Pengalihan Maksimum", + "Accepted Status Codes": "Kode Status yang Diterima", + "Push URL": "Push URL", + "needPushEvery": "Anda harus memanggil URL berikut setiap {0} detik..", + "pushOptionalParams": "Parameter tambahan: {0}", + "Save": "Simpan", + "Notifications": "Notifikasi", + "Not available, please setup.": "Tidak tersedia, silakan atur.", + "Setup Notification": "Atur Pemberitahuan", + "Light": "Terang", + "Dark": "Gelap", + "Auto": "Otomatis", + "Theme - Heartbeat Bar": "Tema - Heartbeat Bar", + "Normal": "Normal", + "Bottom": "Bawah", + "None": "Tidak ada", + "Timezone": "Zona Waktu", + "Search Engine Visibility": "Visibilitas Mesin Pencari", + "Allow indexing": "Izinkan pengindeksan", + "Discourage search engines from indexing site": "Mencegah mesin pencari untuk mengindeks situs", + "Change Password": "Ubah Kata Sandi", + "Current Password": "Kata Sandi Saat Ini", + "New Password": "Kata Sandi Baru", + "Repeat New Password": "Ulangi Kata Sandi Baru", + "Update Password": "Perbarui Kata Sandi", + "Disable Auth": "Nonaktifkan Autentikasi", + "Enable Auth": "Aktifkan Autentikasi", + "disableauth.message1": "Apakah Anda yakin ingin {disableAuth}?", + "disable authentication": "menonaktifkan autentikasi", + "disableauth.message2": "Ini dirancang untuk skenario {intendThirdPartyAuth} di depan Uptime Kuma seperti Cloudflare Access, Authelia, atau mekanisme autentikasi lainnya.", + "where you intend to implement third-party authentication": "di mana Anda bermaksud menerapkan autentikasi pihak ketiga", + "Please use this option carefully!": "Harap gunakan opsi ini dengan hati-hati!", + "Logout": "Keluar", + "Leave": "Tinggalkan", + "I understand, please disable": "Saya mengerti, tolong nonaktifkan", + "Confirm": "Konfirmasi", + "Yes": "Ya", + "No": "Tidak", + "Username": "Nama Pengguna", + "Password": "Kata Sandi", + "Remember me": "Ingat saya", + "Login": "Masuk", + "No Monitors, please": "Tidak ada monitor, silakan", + "add one": "tambahkan satu", + "Notification Type": "Tipe Pemberitahuan", + "Email": "Surel", + "Test": "Tes", + "Certificate Info": "Info Sertifikat", + "Resolver Server": "Server Penyelesai", + "Resource Record Type": "Jenis Rekam Sumber Daya", + "Last Result": "Hasil Terakhir", + "Create your admin account": "Buat akun admin Anda", + "Repeat Password": "Ulangi Kata Sandi", + "Import Backup": "Impor Cadangan", + "Export Backup": "Ekspor Cadangan", + "Export": "Ekspor", + "Import": "Impor", + "respTime": "Waktu Respons (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Diaktifkan secara default", + "Apply on all existing monitors": "Terapkan pada semua monitor yang ada", + "Create": "Buat", + "Clear Data": "Bersihkan Data", + "Events": "Peristiwa", + "Heartbeats": "Heartbeat", + "Auto Get": "Ambil Otomatis", + "backupDescription": "Anda dapat mencadangkan semua monitor dan semua notifikasi ke dalam berkas JSON.", + "backupDescription2": "Catatan: Data sejarah dan peristiwa tidak disertakan.", + "backupDescription3": "Data sensitif seperti notifikasi token disertakan dalam berkas ekspor, harap simpan dengan hati-hati.", + "alertNoFile": "Silakan pilih berkas yang akan diimpor.", + "alertWrongFileType": "Silakan pilih berkas JSON.", + "Clear all statistics": "Hapus semua statistik", + "Skip existing": "Lewati yang sudah ada", + "Overwrite": "Timpa", + "Options": "Opsi", + "Keep both": "Simpan keduanya", + "Verify Token": "Verifikasi Token", + "Setup 2FA": "Siapkan 2FA", + "Enable 2FA": "Aktifkan 2FA", + "Disable 2FA": "Nonaktifkan 2FA", + "2FA Settings": "Pengaturan 2FA", + "Two Factor Authentication": "Autentikasi Dua Faktor", + "Active": "Aktif", + "Inactive": "Tidak Aktif", + "Token": "Token", + "Show URI": "Lihat URI", + "Tags": "Tanda", + "Add New below or Select...": "Tambahkan Baru di Bawah ini atau Pilih…", + "Tag with this name already exist.": "Tanda dengan nama ini sudah ada.", + "Tag with this value already exist.": "Tanda dengan nilai ini sudah ada.", + "color": "Warna", + "value (optional)": "nilai (opsional)", + "Gray": "Abu-Abu", + "Red": "Merah", + "Orange": "Jingga", + "Green": "Hijau", + "Blue": "Biru", + "Indigo": "Biru Tua", + "Purple": "Ungu", + "Pink": "Merah Muda", + "Search...": "Cari…", + "Avg. Ping": "Rata-rata Ping", + "Avg. Response": "Rata-rata Tanggapan", + "Entry Page": "Beranda", + "statusPageNothing": "Tidak ada di sini, silakan tambahkan grup atau monitor.", + "No Services": "Tidak ada Layanan", + "All Systems Operational": "Semua Sistem Berfungsi", + "Partially Degraded Service": "Layanan Terdegradasi Sebagian", + "Degraded Service": "Layanan Terdegradasi", + "Add Group": "Tambahkan Grup", + "Add a monitor": "Tambahkan monitor", + "Edit Status Page": "Edit Halaman Status", + "Go to Dashboard": "Pergi ke Dasbor", + "Status Page": "Halaman Status", + "Status Pages": "Halaman Status", + "defaultNotificationName": "{notification} saya Peringatan ({number})", + "here": "di sini", + "Required": "Wajib", + "telegram": "Telegram", + "Bot Token": "Token Bot", + "wayToGetTelegramToken": "Anda dapat mendapatkan token dari {0}.", + "Chat ID": "Chat ID", + "supportTelegramChatID": "Mendukung Obrolan Langsung / Grup / Channel Chat ID", + "wayToGetTelegramChatID": "Anda bisa mendapatkan chat id Anda dengan mengirim pesan ke bot dan pergi ke url ini untuk melihat chat_id:", + "YOUR BOT TOKEN HERE": "TOKEN BOT ANDA DI SINI", + "chatIDNotFound": "Chat ID tidak ditemukan, tolong kirim pesan ke bot ini dulu", + "webhook": "Webhook", + "Post URL": "Post URL", + "Content Type": "Tipe Konten", + "webhookJsonDesc": "{0} bagus untuk peladen http modern seperti express.js", + "webhookFormDataDesc": "{multipart} bagus untuk PHP, Anda hanya perlu mengurai json dengan {decodeFunction}", + "smtp": "Surel (SMTP)", + "secureOptionNone": "Nihil / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Abaikan Kesalahan TLS", + "From Email": "Dari Email", + "emailCustomSubject": "Subjek", + "To Email": "Ke Email", + "smtpCC": "CC", + "smtpBCC": "BCC", + "discord": "Discord", + "Discord Webhook URL": "URL Webhook Discord", + "wayToGetDiscordURL": "Anda bisa mendapatkan ini dengan pergi ke Server Pengaturan -> Integrasi -> Lihat Webhooks -> Buat Webhook", + "Bot Display Name": "Nama Bot", + "Prefix Custom Message": "Awalan Pesan", + "Hello @everyone is...": "Halo {'@'}everyone adalah…", + "teams": "Microsoft Teams", + "Webhook URL": "URL Webhook", + "wayToGetTeamsURL": "Anda dapat mempelajari cara membuat URL webhook {0}.", + "signal": "Sinyal", + "Number": "Nomer", + "Recipients": "Penerima", + "needSignalAPI": "Anda harus memiliki klien sinyal dengan REST API.", + "wayToCheckSignalURL": "Anda dapat memeriksa URL ini untuk melihat cara menyiapkannya:", + "signalImportant": "PENTING: Anda tidak dapat mencampur grup dan nomor di penerima!", + "gotify": "Gotify", + "Application Token": "Token Aplikasi", + "Server URL": "URL Server", + "Priority": "Prioritas", + "slack": "Slack", + "Icon Emoji": "Ikon Emoji", + "Channel Name": "Nama Saluran", + "Uptime Kuma URL": "URL Uptime Kuma", + "aboutWebhooks": "Info lain tentang webhook: {0}", + "aboutChannelName": "Masukan nama saluran di {0} Kolom Nama Saluran jika Anda ingin melewati saluran webhook. Contoh: #saluran-lain", + "aboutKumaURL": "Jika Anda membiarkan bidang URL Uptime Kuma kosong, itu akan menjadi bawaan ke halaman Proyek Github.", + "emojiCheatSheet": "Lembar contekan emoji: {0}", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "Push by Techulus", + "octopush": "Octopush", + "promosms": "promosms", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Mendukung 50+ layanan notifikasi)", + "GoogleChat": "Google Chat (hanya Google Workspace)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "Kunci Pengguna", + "Device": "Perangkat", + "Message Title": "Judul Pesan", + "Notification Sound": "Suara Nofifikasi", + "More info on:": "Info lebih lanjut tentang: {0}", + "pushoverDesc1": "Prioritas darurat (2) memiliki batas waktu bawaan 30 detik antara percobaan ulang dan akan kadaluwarsa setelah 1 jam.", + "pushoverDesc2": "Jika Anda ingin mengirim pemberitahuan ke perangkat yang berbeda, isi kolom Perangkat.", + "SMS Type": "Tipe SMS", + "octopushTypePremium": "Premium (Cepat - direkomendasikan untuk mengingatkan)", + "octopushTypeLowCost": "Low Cost (Lambat, terkadang diblokir oleh operator)", + "checkPrice": "Cek harga {0}:", + "apiCredentials": "Kredensial API", + "octopushLegacyHint": "Apakah Anda menggunakan Octopush versi lama (2011-2020) atau versi baru?", + "Check octopush prices": "Cek harga octopush {0}.", + "octopushPhoneNumber": "Nomer Telpon/HP (format internasional, contoh : +33612345678) ", + "octopushSMSSender": "Nama Pengirim SMS : 3-11 karakter alfanumerik dan spasi (a-zA-Z0-9)", + "LunaSea Device ID": "ID Perangkat LunaSea", + "Apprise URL": "Apprise URL", + "Example:": "Contoh: {0}", + "Read more:": "Baca lebih lanjut: {0}", + "Status:": "Status: {0}", + "Read more": "Baca lebih lanjut", + "appriseInstalled": "Apprise diinstall.", + "appriseNotInstalled": "Apprise tidak diinstall. {0}", + "Access Token": "Token Akses", + "Channel access token": "Token akses saluran", + "Line Developers Console": "Konsol Pengembang Line", + "lineDevConsoleTo": "Konsol Pengembang Line - {0}", + "Basic Settings": "Pengaturan Dasar", + "User ID": "ID User", + "Messaging API": "API Messaging", + "wayToGetLineChannelToken": "Pertama akses {0}, buat penyedia dan saluran (Messaging API), lalu Anda bisa mendapatkan token akses saluran dan id pengguna dari item menu yang disebutkan di atas.", + "Icon URL": "URL Ikon", + "aboutIconURL": "Anda dapat memberikan tautan ke gambar di \"Icon URL\" untuk mengganti gambar profil bawaan. Tidak akan digunakan jika Ikon Emoji diset.", + "aboutMattermostChannelName": "Anda dapat mengganti saluran bawaan tujuan posting webhook dengan memasukkan nama saluran ke dalam Kolom \"Channel Name\". Ini perlu diaktifkan di pengaturan webhook Mattermost. contoh: #other-channel", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - murah tapi lambat dan sering kelebihan beban. Terbatas hanya untuk penerima Polandia.", + "promosmsTypeFlash": "SMS FLASH - Pesan akan otomatis muncul di perangkat penerima. Terbatas hanya untuk penerima Polandia.", + "promosmsTypeFull": "SMS FULL - Tingkat Premium SMS, Anda dapat menggunakan Nama Pengirim Anda (Nama Anda harus didaftarkan terlebih dahulu). Dapat diandalkan untuk peringatan.", + "promosmsTypeSpeed": "SMS SPEED - Prioritas tertinggi dalam sistem. Sangat cepat dan dapat diandalkan tetapi mahal (sekitar dua kali lipat dari harga SMS FULL).", + "promosmsPhoneNumber": "Nomor telepon (untuk penerima Polandia Anda dapat melewati kode area)", + "promosmsSMSSender": "Nama Pengirim SMS : Nama pra-registrasi atau salah satu bawaan: InfoSMS, Info SMS, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu WebHookUrl", + "matrixHomeserverURL": "Homeserver URL (dengan http(s):// dan port tambahan)", + "Internal Room Id": "ID Ruang Internal", + "matrixDesc1": "Kamu dapat menemukan Internal Room ID dengan melihat di bagian konfigurasi ruang di Matrix. Seharusnya berbentuk seperti !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Sangat direkomendasikan kepada Anda untuk membuat akun baru dan jangan menggunakan token atas akun terkini yang memiliki token akses secara penuh terhadap akun dan seluruh ruang yang terdaftar. Alih - alih, buat akun baru dan undang akun tsb ke ruang tempat anda ingin menerima notifikasi. Untuk mendapatkan token akses anda dapat menjalankan {0}", + "Method": "Metode", + "Body": "Body", + "Headers": "Headers", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "Request Headers memiliki format JSON yang tidak sesuai: ", + "BodyInvalidFormat": "Request Body memiliki format JSON yang tidak sesuai: ", + "Monitor History": "Riyawat Monitor", + "clearDataOlderThan": "Simpan data riwayat monitoring selama {0} hari.", + "PasswordsDoNotMatch": "Password tidak sama.", + "records": "catatan", + "One record": "Satu catatan", + "steamApiKeyDescription": "Untuk monitoring Steam Game Server Anda membutuhkan kunci Steam Web-API. Anda dapat mendaftarkan Kunci API Anda melalui: ", + "Current User": "Pengguna Saat Ini", + "topic": "Topik", + "topicExplanation": "MQTT topik untuk dimonitor", + "successMessage": "Pesan Berhasil", + "successMessageExplanation": "Pesan MQTT yang akan dianggap berhasil", + "recent": "Baru saja", + "Done": "Selesai", + "Info": "Info", + "Security": "Keamanan", + "Steam API Key": "Kunci API Steam", + "Shrink Database": "Kecilkan Database", + "Pick a RR-Type...": "Pilih RR-Type…", + "Pick Accepted Status Codes...": "Pilih Kode Status yang Diterima…", + "Default": "Default", + "HTTP Options": "Opsi HTTP", + "Create Incident": "Buat Incident", + "Title": "Judul", + "Content": "Konten", + "Style": "Gaya", + "info": "info", + "warning": "peringatan", + "danger": "bahaya", + "error": "kesalahan", + "critical": "kritis", + "primary": "primer", + "light": "terang", + "dark": "gelap", + "Post": "Post", + "Please input title and content": "Masukkan judul dan konten", + "Created": "Dibuat", + "Last Updated": "Terakhir Diperbarui", + "Unpin": "Lepaskan Semat", + "Switch to Light Theme": "Ubah ke Tema Terang", + "Switch to Dark Theme": "Ubah ke Tema Gelap", + "Show Tags": "Tampilkan Tags", + "Hide Tags": "Sembunyikan Tags", + "Description": "Deskripsi", + "No monitors available.": "Tidak ada monitor yang tersedia.", + "Add one": "Tambahkan", + "No Monitors": "Tidak ada monitor", + "Untitled Group": "Group Tanpa Judul", + "Services": "Layanan", + "Discard": "Buang", + "Cancel": "Batal", + "Powered by": "Dipersembahkan oleh", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "Nama Pengguna API ( termamsuk awalan webapi_ )", + "serwersmsAPIPassword": "Kata Sandi API", + "serwersmsPhoneNumber": "Nomor Telepon", + "serwersmsSenderName": "Nama Pengirim SMS (didaftarkan melalui portal pelanggan)", + "stackfield": "Stackfield", + "Customize": "Kustomisasi", + "Custom Footer": "Tambahan Footer", + "Custom CSS": "Tambahan CSS", + "smtpDkimSettings": "Pengaturan DKIM", + "smtpDkimDesc": "Silakan merujuk ke Nodemailer DKIM {0} untuk penggunaan.", + "documentation": "dokumentasi", + "smtpDkimDomain": "Nama Domain", + "smtpDkimKeySelector": "Selektor Kunci", + "smtpDkimPrivateKey": "Kunci Pribadi", + "smtpDkimHashAlgo": "Algoritma Hash (Opsional)", + "smtpDkimheaderFieldNames": "Header Keys untuk ditambahkan (Optional)", + "smtpDkimskipFields": "Header Keys not untuk ditambahkan (Optional)", + "wayToGetPagerDutyKey": "Anda dapat menambahkan melalui Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Lalu Anda dapat menjadi dengan kata kunci \"Events API V2\". Informasi tambahan {0}", + "Integration Key": "Kunci Integrasi", + "Integration URL": "URL Integrasi", + "Auto resolve or acknowledged": "Penyelesaian otomatis atau diakui", + "do nothing": "tidak melakukan apapun", + "auto acknowledged": "otomatis diakui", + "auto resolve": "otomatis terselesaikan", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API Endpoint", + "alertaEnvironment": "Lingkungan", + "alertaApiKey": "Kunci API", + "alertaAlertState": "Status Siaga", + "alertaRecoverState": "Status Pemulihan", + "deleteStatusPageMsg": "Apakah Anda yakin untuk menghapus halaman status berikut?", + "Proxies": "Proxy", + "default": "Bawaan", + "enabled": "Diaktifkan", + "setAsDefault": "Tetapkan sebagai bawaan", + "deleteProxyMsg": "Apakah Anda yakin ingin menghapus proxy berikut untuk seluruh monitor?", + "proxyDescription": "Proxy harus ditambahkan ke monitor agar berfungsi.", + "enableProxyDescription": "Proxy berikut tidak akan berdampak ke monitor hingga diaktifkan. Anda dapat mengontrol menonaktifkan sementara proxy dari semua monitor dengan status aktivasi.", + "setAsDefaultProxyDescription": "Proxy berikut akan diaktifkan sebagai bawaan untuk monitor baru. Anda masih dapat menonaktifkan proxy secara terpisah untuk setiap monitor.", + "Certificate Chain": "Rantai Sertifikat", + "Valid": "Berlaku", + "Invalid": "Tidak Valid", + "AccessKeyId": "AccessKey ID", + "SecretAccessKey": "AccessKey Secret", + "PhoneNumbers": "Nomor Telepon", + "TemplateCode": "Kode Template", + "SignName": "Nama Tanda", + "Sms template must contain parameters: ": "Template SMS harus berisi parameter: ", + "Bark Endpoint": "Bark Endpoint", + "Bark Group": "Grup Bark", + "Bark Sound": "Suara Bark", + "WebHookUrl": "WebHookUrl", + "SecretKey": "SecretKey", + "For safety, must use secret key": "Untuk keamaan Anda harus menggunakan kunci rahasia", + "Device Token": "Token Perangkat", + "Platform": "Platform", + "Huawei": "Huawei", + "High": "Tinggi", + "Retry": "Ulang", + "Topic": "Topik", + "WeCom Bot Key": "Kunci WeCom Bot", + "Setup Proxy": "Siapkan Proxy", + "Proxy Protocol": "Protokol Proxy", + "Proxy Server": "Server Proxy", + "Proxy server has authentication": "Server Proxy memiliki autentikasi", + "User": "Pengguna", + "Installed": "Terpasang", + "Not installed": "Tidak terpasang", + "Running": "Berjalan", + "Not running": "Tidak berjalan", + "Remove Token": "Hapus Token", + "Start": "Mulai", + "Stop": "Berhenti", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Tambahkan Halaman Status Baru", + "Slug": "Slug", + "Accept characters:": "Terima karakter:", + "startOrEndWithOnly": "Mulai atau akhiri hanya dengan {0}", + "No consecutive dashes": "Tanda hubung tidak berurutan", + "Next": "Selanjutnya", + "The slug is already taken. Please choose another slug.": "Slug telah digunakan. Silakan pilih slug lain.", + "No Proxy": "Tidak ada Proxy", + "Authentication": "Autentikasi", + "HTTP Basic Auth": "Autentikasi Dasar HTTP", + "New Status Page": "Halaman Status Baru", + "Page Not Found": "Halaman Tidak Ditemukan", + "Reverse Proxy": "Proxy Terbalik", + "Backup": "Cadangan", + "About": "Tentang", + "wayToGetCloudflaredURL": "(Unduh cloudflared dari {0})", + "cloudflareWebsite": "Situs Cloudflare", + "Message:": "Pesan:", + "Don't know how to get the token? Please read the guide:": "Tidak tahu cara mendapatkan token? Silakan baca panduannya:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Koneksi saat ini mungkin hilang jika Anda saat ini terhubung melalui Cloudflare Tunel. Apakah Anda yakin ingin menghentikannya? Ketik kata sandi Anda saat ini untuk mengonfirmasinya.", + "HTTP Headers": "Header HTTP", + "Trust Proxy": "Proxy Terpercaya", + "Other Software": "Perangkat Lunak lainnya", + "For example: nginx, Apache and Traefik.": "Sebagai contoh: nginx, Apache and Traefik.", + "Please read": "Harap dibaca", + "Subject:": "Subjek:", + "Valid To:": "Berlaku Untuk:", + "Days Remaining:": "Hari Tersisa:", + "Issuer:": "Penerbit:", + "Fingerprint:": "Sidik jari:", + "No status pages": "Tidak ada halaman status", + "Domain Name Expiry Notification": "Pemberitahuan Kedaluwarsa Nama Domain", + "Proxy": "Proxy", + "Date Created": "Tanggal Dibuat", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "Alamat HTTP OneBot", + "onebotMessageType": "Jenis Pesan OneBot", + "onebotGroupMessage": "Grup", + "onebotPrivateMessage": "Pribadi", + "onebotUserOrGroupId": "Grup/Pengguna ID", + "onebotSafetyTips": "Untuk keamanan, harus mengatur token akses", + "PushDeer Key": "Kunci PushDeer", + "Footer Text": "Tulisan Footer", + "Show Powered By": "Tampilkan Dipersembahkan oleh", + "Domain Names": "Nama Domain", + "signedInDisp": "Masuk sebagai {0}", + "signedInDispDisabled": "Autentikasi dinonaktifkan.", + "RadiusSecret": "Radius Secret", + "RadiusSecretDescription": "Shared Secret antara klien dan server", + "RadiusCalledStationId": "Called Station Id", + "RadiusCalledStationIdDescription": "Pengenal perangkat yang dipanggil", + "RadiusCallingStationId": "Memanggil Station Id", + "RadiusCallingStationIdDescription": "Pengenal perangkat panggilan", + "Certificate Expiry Notification": "Pemberitahuan Kedaluwarsa Sertifikat", + "API Username": "Nama Pengguna API", + "API Key": "Kunci API", + "Recipient Number": "Nomor Penerima", + "From Name/Number": "Dari Nama/Nomor", + "Leave blank to use a shared sender number.": "Biarkan kosong untuk menggunakan nomor pengirim bersama.", + "Octopush API Version": "Versi API Octopush", + "Legacy Octopush-DM": "Legacy Octopush-DM", + "endpoint": "endpoint", + "octopushAPIKey": "\"API key\" dari kredensial HTTP API di panel kontrol", + "octopushLogin": "\"Login\" dari kredensial HTTP API di panel kontrol", + "promosmsLogin": "Nama Masuk API", + "promosmsPassword": "Kata Sandi API", + "pushoversounds pushover": "Pushover (bawaan)", + "pushoversounds bike": "Sepeda", + "pushoversounds bugle": "Terompet", + "pushoversounds cashregister": "Mesin Uang", + "pushoversounds classical": "Klasik", + "pushoversounds cosmic": "Kosmik", + "pushoversounds falling": "Jatuh", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Masuk", + "pushoversounds intermission": "Jeda", + "pushoversounds magic": "Magic", + "pushoversounds mechanical": "Mekanis", + "pushoversounds pianobar": "Bilah Piano", + "pushoversounds siren": "Sirene", + "pushoversounds spacealarm": "Alarm Luar Angkasa", + "pushoversounds tugboat": "Kapal Tunda", + "pushoversounds alien": "Alien Alarm (panjang)", + "pushoversounds climb": "Mendaki (long)", + "pushoversounds persistent": "Terus menerus (panjang)", + "pushoversounds echo": "Pushover Echo (panjang)", + "pushoversounds updown": "Up Down (panjang)", + "pushoversounds vibrate": "Hanya Bergetar", + "pushoversounds none": "Nihil (hening)", + "pushyAPIKey": "Kunci Rahasia API", + "pushyToken": "Token perangkat", + "Show update if available": "Tampilkan pembaruan jika tersedia", + "Also check beta release": "Periksa juga rilis beta", + "Using a Reverse Proxy?": "Menggunakan Proxy Terbalik?", + "Check how to config it for WebSocket": "Periksa cara mengonfigurasinya untuk A WebSocket", + "Steam Game Server": "Server Game Steam", + "Most likely causes:": "Kemungkinan besar penyebabnya:", + "The resource is no longer available.": "Sumber daya tidak lagi tersedia.", + "There might be a typing error in the address.": "Mungkin ada kesalahan pengetikan di alamat.", + "What you can try:": "Apa yang dapat kamu coba:", + "Retype the address.": "Ketik ulang alamat.", + "Go back to the previous page.": "Kembali ke halaman sebelumnya.", + "Coming Soon": "Segera", + "wayToGetClickSendSMSToken": "Anda bisa mendapatkan Nama Pengguna API dan Kunci API dari {here} .", + "Connection String": "String Koneksi", + "Query": "Query", + "settingsCertificateExpiry": "Sertifikat TLS Kadaluarsa", + "certificationExpiryDescription": "Monitor HTTPS memicu pemberitahuan saat sertifikat TLS kedaluwarsa dalam:", + "Setup Docker Host": "Siapkan Host Docker", + "Connection Type": "Jenis Koneksi", + "Docker Daemon": "Docker Daemon", + "deleteDockerHostMsg": "Apakah Anda yakin ingin menghapus host docker berikut untuk semua monitor?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Kontainer Docker", + "Container Name / ID": "Nama / ID Container", + "Docker Host": "Host Docker", + "Docker Hosts": "Hosts Docker", + "ntfy Topic": "Topik ntfy", + "Domain": "Domain", + "Workstation": "Workstation", + "disableCloudflaredNoAuthMsg": "Anda berada dalam mode Tanpa Autentikasi, kata sandi tidak diperlukan.", + "trustProxyDescription": "Trust 'X-Forwarded-*' headers. Jika Anda ingin mendapatkan IP klien yang benar dan Uptime Kuma Anda dibalik proxy seperti Nginx or Apache, Anda harus mengaktifkan ini.", + "wayToGetLineNotifyToken": "Anda bisa mendapatkan token akses dari {0}", + "Examples": "Contoh", + "Home Assistant URL": "URL Home Asisten", + "Long-Lived Access Token": "Token Akses Berumur Panjang", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Token Akses Berumur Panjang dapat dibuat dengan mengklik nama profil Anda (kiri bawah) dan menggulir ke bawah lalu klik Buat Token. ", + "Notification Service": "Layanan Pemberitahuan", + "default: notify all devices": "bawaan: notifikasi seluruh perangkat", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Daftar Layanan Pemberitahuan dapat ditemukan di Home Assistant pada \"Developer Tools > Services\" cari \"notification\" lalu cari nama perangkat Anda.", + "Automations can optionally be triggered in Home Assistant:": "Otomatisasi dapat dipicu secara opsional di Home Assistant:", + "Trigger type:": "Tipe Trigger/Pemicu:", + "Event type:": "Tipe event:", + "Event data:": "Data event:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Kemudian pilih tindakan, misalnya alihkan ke tempat dimana lampu RGB berwarna merah.", + "Frontend Version": "Versi Frontend", + "Frontend Version do not match backend version!": "Versi Frontend tidak sama dengan versi backend!", + "Base URL": "URL Dasar", + "goAlertInfo": "GoAlert adalah aplikasi open source untuk penjadwalan panggilan, eskalasi otomatis dan pemberitahuan (seperti SMS atau panggilan suara). Secara otomatis melibatkan orang yang tepat, dengan cara yang benar, dan pada waktu yang tepat! {0}", + "goAlertIntegrationKeyInfo": "Dapatkan kunci integrasi API generik untuk layanan dalam format ini \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" biasanya nilai parameter token dari URL yang disalin.", + "goAlert": "GoAlert", + "backupOutdatedWarning": "Tidak digunakan lagi: Karena banyak fitur ditambahkan dan fitur pencadangan ini agak tidak terpelihara, fitur ini tidak dapat menghasilkan atau memulihkan cadangan lengkap.", + "backupRecommend": "Silakan backup volume atau folder (./data/) secara langsung.", + "Help": "Bantuan", + "Game": "Permainan", + "markdownSupported": "Sintaks markdown didukung", + "statusMaintenance": "Pemeliharaan", + "Maintenance": "Pemeliharaan", + "General Monitor Type": "Tipe Monitor Umum", + "Passive Monitor Type": "Tipe Monitor Pasif", + "Specific Monitor Type": "Tipe Monitor Spesifik", + "Monitor": "Monitor", + "Guild ID": "ID Guild", + "twilioAccountSID": "SID akun", + "twilioAuthToken": "Token Autentikasi (API Key)", + "ntfyAuthenticationMethod": "Metode Autentikasi", + "ntfyUsernameAndPassword": "Nama pengguna dan kata sandi", + "Add Another": "Tambah Lainnya", + "Key Added": "Kunci Ditambahkan", + "Google Analytics ID": "ID Google Analytics", + "pagertreeIntegrationUrl": "URL integrasi", + "pagertreeUrgency": "Darurat", + "Home": "Beranda", + "startDateTime": "Tanggal/Waktu Mulai", + "Recurring": "Berulang", + "strategyManual": "Aktif/TidakAktif Secara Manual", + "infiniteRetention": "Setel ke 0 untuk retensi tak terbatas.", + "enableGRPCTls": "Izinkan untuk mengirim permintaan gRPC dengan koneksi TLS", + "grpcMethodDescription": "Nama metode dikonversi ke format camelCase seperti sayHello, check, dll.", + "deleteMaintenanceMsg": "Anda yakin ingin menghapus pemeliharaan ini?", + "Free Mobile API Key": "Kunci API Seluler Gratis", + "Enable TLS": "Aktifkan TLS", + "Proto Method": "Metode Proto", + "Proto Content": "Konten Proto", + "Economy": "Ekonomi", + "Free Mobile User Identifier": "Pengidentifikasi Pengguna Seluler", + "Proto Service Name": "Nama Layanan Proto", + "SMSManager API Docs": "Dokumen API SMSManager", + "Expiry date": "Tanggal kadaluarsa", + "No API Keys": "Tidak ada Kunci API", + "Expires": "Berakhir", + "pagertreeCritical": "Penting", + "pagertreeResolve": "Penyelesaian Otomatis", + "lunaseaDeviceID": "ID perangkat", + "lunaseaUserID": "ID Pengguna", + "twilioFromNumber": "Dari Nomor", + "twilioToNumber": "Ke Nomor", + "Badge Generator": "Pembuat Lencana {0}", + "Badge Duration": "Durasi Lencana", + "Badge Label": "Label Lencana", + "Badge Prefix": "Prefiks Lencana", + "Badge Suffix": "Suffix Lencana", + "Badge Label Color": "Warna Label Lencana", + "Badge Color": "Warna Lencana", + "Badge Label Prefix": "Prefiks Label Lencana", + "telegramSendSilently": "Kirim Secara Senyap", + "Invert Keyword": "Balikkan Kata Kunci", + "Pick Affected Monitors...": "Pilih Monitor yang Terpengaruh…", + "Badge Label Suffix": "Suffix Label Lencana", + "statusPageMaintenanceEndDate": "Selesai", + "Add API Key": "Tambahkan Kunci API", + "apiKey-expired": "Kedaluwarsa", + "apiKey-active": "Aktif", + "apiKey-inactive": "Tidak aktif", + "Monitor Setting": "Pengaturan Pemantauan {0}", + "Show Clickable Link": "Tampilkan Tautan yang Dapat Diklik", + "Badge Type": "Tipe lencana", + "confirmDeleteTagMsg": "Yakin ingin menghapus tag ini? Monitor yang terkait dengan tag ini tidak akan dihapus.", + "Gateway Type": "Tipe Gateway", + "Don't expire": "Jangan sampai kadaluarsa", + "apiKeyAddedMsg": "Kunci API Anda telah ditambahkan. Mohon dicatat karena tidak akan ditampilkan lagi.", + "disableAPIKeyMsg": "Yakin ingin menonaktifkan kunci API ini?", + "pagertreeSilent": "Bisu", + "pagertreeLow": "Rendah", + "pagertreeDoNothing": "Jangan Lakukan Apa-apa", + "wayToGetPagerTreeIntegrationURL": "Setelah membuat integrasi Uptime Kuma di PagerTree, salin Endpoint. Lihat detail lengkap {0}", + "lunaseaTarget": "Sasaran", + "Show Clickable Link Description": "Jika dicentang, setiap orang yang memiliki akses ke halaman status ini dapat memiliki akses ke URL monitor.", + "Open Badge Generator": "Buka Pembuat Lencana", + "Cannot connect to the socket server": "Tidak dapat terhubung ke server soket", + "Reconnecting...": "Menghubungkan ulang...", + "deleteAPIKeyMsg": "Apakah Anda yakin ingin menghapus kunci API ini?", + "Generate": "Hasilkan", + "pagertreeMedium": "Sedang", + "pagertreeHigh": "Tinggi", + "Group": "Grup", + "Body Encoding": "Body Encoding", + "Add New Tag": "Tambahkan Tanda Baru", + "chromeExecutableDescription": "Untuk pengguna Docker, jika Chromium belum diinstal, mungkin perlu waktu beberapa menit untuk menginstal dan menampilkan hasil pengujian. Dibutuhkan 1GB ruang penyimpanan.", + "recurringIntervalMessage": "Jalankan sekali setiap hari | Jalankan sekali setiap {0} hari", + "wayToGetKookBotToken": "Buat aplikasi dan dapatkan token bot Anda di {0}", + "Custom Monitor Type": "Tipe Monitor Khusus", + "API Keys": "Kunci API", + "Expiry": "Kadaluarsa", + "noGroupMonitorMsg": "Tidak tersedia. Buat Monitor Grup Terlebih Dahulu.", + "Close": "Tutup", + "telegramMessageThreadID": "(Opsional) ID Pesan", + "Date and Time": "Tanggal dan Waktu", + "Single Maintenance Window": "Jendela Pemeliharaan Tunggal", + "wayToGetZohoCliqURL": "Anda dapat mempelajari cara membuat URL webhook {0}.", + "dayOfWeek": "Hari dalam Seminggu", + "dayOfMonth": "Hari dalam Bulan", + "lastDay": "Hari Terakhir", + "Clone Monitor": "Klon Monitor", + "Clone": "Klon", + "Server Address": "Alamat server", + "Edit Tag": "Sunting Tag", + "smseagleTo": "Nomor telepon", + "maintenanceStatus-under-maintenance": "Dalam perbaikan", + "webhookAdditionalHeadersDesc": "Menetapkan header tambahan yang dikirim dengan webhook. Setiap header harus didefinisikan sebagai kunci/nilai JSON.", + "webhookCustomBodyDesc": "Tentukan Body HTTP khusus untuk permintaan tersebut. Variabel template {msg}, {heartbeat}, {monitor} yang diterima.", + "webhookBodyPresetOption": "Prasetel - {0}", + "webhookBodyCustomOption": "Body Kustom", + "Packet Size": "Ukuran Paket", + "telegramMessageThreadIDDescription": "Pengidentifikasi unik Opsional untuk pesan target (topik) forum; untuk forum supergrup saja", + "telegramProtectContent": "Lindungi Forwarding/Saving", + "or": "atau", + "sameAsServerTimezone": "Sama seperti Zona Waktu Server", + "endDateTime": "Tanggal/Waktu Berakhir", + "cronExpression": "Ekspresi Cron", + "cronSchedule": "Jadwal: ", + "invalidCronExpression": "Ekspresi Cron Tidak Valid: {0}", + "recurringInterval": "Selang waktu", + "warningTimezone": "Itu menggunakan zona waktu server", + "weekdayShortMon": "Sen", + "weekdayShortTue": "Sel", + "weekdayShortWed": "Rab", + "weekdayShortThu": "Kam", + "weekdayShortFri": "Jum", + "weekdayShortSat": "Sab", + "weekdayShortSun": "Mgu", + "lastDay1": "Hari Terakhir dalam Sebulan", + "lastDay2": "Hari ke-2 Terakhir Bulan", + "lastDay3": "Hari ke-3 Terakhir Bulan", + "lastDay4": "Hari ke-4 Terakhir Bulan", + "No Maintenance": "Tidak Ada Pemeliharaan", + "pauseMaintenanceMsg": "Anda yakin ingin menjeda?", + "maintenanceStatus-inactive": "Tidak aktif", + "Display Timezone": "Zona Waktu Tampilan", + "IconUrl": "URL ikon", + "Enable DNS Cache": "(Tidak berlaku lagi) Aktifkan Cache DNS untuk monitor HTTP(s)", + "Enable": "Aktifkan", + "Disable": "Nonaktifkan", + "affectedStatusPages": "Tampilkan pesan pemeliharaan ini pada halaman status yang dipilih", + "invertKeywordDescription": "Carilah kata kunci untuk menjadi tidak ada daripada hadir.", + "wayToGetKookGuildID": "Aktifkan 'Mode Pengembang' di pengaturan Kook, dan klik kanan guild untuk mendapatkan ID-nya", + "Strategy": "Strategi", + "high": "tinggi", + "SendKey": "SendKey", + "Lowcost": "Biaya rendah", + "smseagleContact": "Nama kontak buku telepon", + "smseagleRecipient": "Penerima (jika banyak harus dipisahkan dengan koma)", + "smseagleEncoding": "Kirim sebagai Unicode (default: GSM-7)", + "smseaglePriority": "Prioritas Pesan (0-9, prioritas tertinggi = 9)", + "Learn More": "Pelajari lebih lanjut", + "Badge Up Color": "Warna atas Lencana", + "Badge Maintenance Color": "Warna Lencana Pemeliharaan", + "Badge Warn Color": "Warna Lencana Peringatan", + "Request Body": "Permintaan Body", + "uninstalling": "Menghapus instalasi", + "notificationRegional": "Regional", + "atLeastOneMonitor": "Pilih setidaknya satu monitor yang terpengaruh", + "pushoverMessageTtl": "TTL pesan (Detik)", + "smseagleGroup": "Nama grup buku telepon", + "smseagleRecipientType": "Tipe Penerima", + "smseagleToken": "Token Akses API", + "smseagleUrl": "URL perangkat SMSEagle Anda", + "Schedule maintenance": "Jadwalkan Pemeliharaan", + "Affected Monitors": "Monitor yang Terpengaruh", + "Start of maintenance": "Mulai pemeliharaan", + "All Status Pages": "Semua Halaman Status", + "Select status pages...": "Pilih halaman status…", + "Custom": "Khusus", + "Optional": "Opsional", + "dnsCacheDescription": "Ini mungkin tidak berfungsi di beberapa lingkungan IPv6, nonaktifkan jika Anda mengalami masalah.", + "Maintenance Time Window of a Day": "Jendela Waktu Perawatan dalam Sehari", + "Effective Date Range": "Rentang Tanggal Efektif (Opsional)", + "Schedule Maintenance": "Jadwal Pemeliharaan", + "Badge Down Color": "Warna bawah Lencana", + "Badge Warn Days": "Hari Lencana Peringatan", + "statusPageRefreshIn": "Muat ulang dalam: {0}", + "webhookAdditionalHeadersTitle": "Header Tambahan", + "maintenanceStatus-unknown": "Tidak dikenal", + "Server Timezone": "Zona Waktu Server", + "maintenanceStatus-scheduled": "Dijadwalkan", + "maintenanceStatus-ended": "Berakhir", + "dataRetentionTimeError": "Periode retensi harus 0 atau lebih besar", + "chromeExecutable": "Chrome/Chromium yang Dapat di Eksekusi", + "chromeExecutableAutoDetect": "Deteksi otomatis", + "Edit Maintenance": "Sunting Pemeliharaan", + "DateTime Range": "Rentang Tanggal dan Waktu", + "loadingError": "Tidak dapat mengambil data, harap coba lagi nanti.", + "installing": "Memasang", + "uninstall": "Copot pemasangan", + "confirmUninstallPlugin": "Anda yakin ingin mencopot pemasangan plugin ini?", + "cloneOf": "Klon dari {0}", + "affectedMonitorsDescription": "Pilih monitor yang terpengaruh oleh pemeliharaan saat ini", + "You can divide numbers with": "Anda dapat membagi angka dengan", + "Continue": "Lanjutkan", + "Badge Style": "Gaya Lencana", + "Badge value (For Testing only.)": "Nilai lencana (Hanya untuk Pengujian.)", + "Badge URL": "URL lencana", + "Badge Down Days": "Hari Penghentian Lencana", + "telegramSendSilentlyDescription": "Mengirim pesan secara senyap. Pengguna akan menerima notifikasi tanpa suara.", + "telegramProtectContentDescription": "Jika diaktifkan, pesan bot di Telegram akan dilindungi dari forwarding dan saving.", + "plugin": "Pengaya | Plugin", + "install": "Pasang", + "promosmsAllowLongSMS": "Izinkan SMS panjang", + "Badge Pending Color": "Warna Lencana Tertunda", + "Monitor Group": "Monitor Grup", + "Expected Value": "Nilai Yang Diharapkan", + "Json Query": "Kueri JSON", + "setupDatabaseSQLite": "File database sederhana, direkomendasikan untuk penerapan skala kecil. Sebelum v2.0.0, Uptime Kuma menggunakan SQLite sebagai database default.", + "setupDatabaseMariaDB": "Hubungkan ke database MariaDB eksternal. Anda perlu mengatur informasi koneksi database.", + "setupDatabaseEmbeddedMariaDB": "Anda tidak perlu mengatur apa pun. Docker Image ini telah disemati MariaDB dan dikonfigurasi untuk Anda secara otomatis. Uptime Kuma akan terhubung ke database ini melalui soket Unix.", + "setupDatabaseChooseDatabase": "Database mana yang ingin Anda gunakan?", + "dbName": "Nama Database", + "pushViewCode": "Bagaimana cara menggunakan Push monitor? (Lihat Kode)", + "monitorToastMessagesLabel": "Pantau notifikasi Toast", + "toastErrorTimeout": "Batas Waktu untuk Pemberitahuan Kesalahan", + "filterActive": "Aktif", + "Select": "Pilih", + "tailscalePingWarning": "Untuk menggunakan monitor Tailscale Ping, Anda perlu menginstal Uptime Kuma tanpa Docker dan juga menginstal klien Tailscale di server Anda.", + "emailTemplateLimitedToUpDownNotification": "hanya tersedia untuk detak jantung ATAS/BAWAH, jika tidak, null", + "aboutNotifyChannel": "Beritahu saluran akan memicu pemberitahuan desktop atau seluler untuk semua anggota saluran, baik ketersediaannya diatur ke aktif atau tidak.", + "monitorToastMessagesDescription": "Notifikasi Toast untuk monitor hilang setelah waktu tertentu dalam hitungan detik. Setel ke -1 akan menonaktifkan batas waktu. Setel ke 0 akan menonaktifkan notifikasi Toast.", + "wayToGetFlashDutyKey": "Untuk integrasi Uptime Kuma dengan Flashduty: Buka Channels > Pilih channel > Integrations > Add a new integration, pilih Uptime Kuma, dan copy Push URL-nya.", + "Saved.": "Tersimpan.", + "authUserInactiveOrDeleted": "Pengguna tidak aktif atau dihapus.", + "authInvalidToken": "Token Tidak Valid.", + "authIncorrectCreds": "Username atau kata sandi salah.", + "2faAlreadyEnabled": "2FA sudah diaktifkan.", + "2faEnabled": "2FA Diaktifkan.", + "2faDisabled": "2FA Dinonaktifkan.", + "successResumed": "Berhasil Dilanjutkan.", + "successPaused": "Berhasil Dijeda.", + "successDeleted": "Berhasil Dihapus.", + "successEdited": "Berhasil Diedit.", + "successAuthChangePassword": "Kata sandi telah berhasil diperbarui.", + "successBackupRestored": "Cadangan berhasil dipulihkan.", + "successDisabled": "Berhasil Dinonaktifkan.", + "successEnabled": "Berhasil Diaktifkan.", + "GrafanaOncallUrl": "URL Oncall Grafana", + "emailCustomisableContent": "Konten yang dapat disesuaikan", + "leave blank for default subject": "biarkan kosong untuk subjek bawaan", + "Bark API Version": "Versi Bark API", + "Reset Token": "Setel Ulang Token", + "noDockerHostMsg": "Tidak Tersedia. Siapkan Host Docker Terlebih Dahulu.", + "DockerHostRequired": "Silakan atur Docker Host untuk monitor ini.", + "enableNSCD": "Aktifkan NSCD (Name Service Cache Daemon) untuk menyimpan semua permintaan DNS", + "smtpLiquidIntroduction": "Dua bidang berikut dapat dibuat templatnya melalui Bahasa templating Liquid. Silakan lihat {0} untuk petunjuk penggunaan. Berikut ini adalah variabel yang tersedia:", + "leave blank for default body": "biarkan kosong untuk isi bawaan", + "emailTemplateServiceName": "Nama Layanan", + "emailTemplateHostnameOrURL": "Nama host atau URL", + "emailTemplateStatus": "Status", + "emailTemplateMonitorJSON": "objek yang menggambarkan monitor", + "emailTemplateHeartbeatJSON": "objek yang menggambarkan heartbeat", + "emailTemplateMsg": "pesan pemberitahuan", + "emailCustomBody": "Kustomisasi Body", + "Notify Channel": "Beritahu Saluran", + "Server URL should not contain the nfty topic": "URL server tidak boleh berisi topik nfty", + "PushDeer Server": "Server PushDeer", + "pushDeerServerDescription": "Biarkan kosong untuk menggunakan server resmi", + "twilioApiKey": "Kunci API (opsional)", + "Badge Duration (in hours)": "Durasi Badge (dalam jam)", + "Badge Preview": "Pratinjau Lencana", + "toastSuccessTimeout": "Batas Waktu untuk Pemberitahuan Sukses", + "Kafka Brokers": "Kafka Broker", + "Enter the list of brokers": "Masukkan daftar broker", + "Press Enter to add broker": "Tekan Enter untuk menambahkan broker", + "Kafka Topic Name": "Nama Topik Kafka", + "Kafka Producer Message": "Pesan Produser Kafka", + "Enable Kafka SSL": "Aktifkan SSL Kafka", + "Enable Kafka Producer Auto Topic Creation": "Aktifkan Pembuatan Topik Otomatis Produser Kafka", + "Mechanism": "Mekanisme", + "FlashDuty Severity": "Severity", + "nostrRelaysHelp": "Satu URL relai per baris", + "nostrSender": "Kunci Pribadi Pengirim (ndetik)", + "nostrRecipients": "Kunci Publik Penerima (npub)", + "nostrRecipientsHelp": "format npub, satu per baris", + "showCertificateExpiry": "Tampilkan Sertifikat Kedaluwarsa", + "noOrBadCertificate": "Tidak Ada/Sertifikat Salah", + "gamedigGuessPort": "Gamedig: Guess Port", + "nostrRelays": "Relai nostr", + "gamedigGuessPortDescription": "Port yang digunakan oleh Valve Server Query Protocol mungkin berbeda dari port klien. Coba ini jika monitor tidak dapat terhubung ke server Anda.", + "successAdded": "Berhasil Ditambahkan.", + "tagNotFound": "Tag tidak ditemukan.", + "foundChromiumVersion": "Ditemukan Chromium/Chrome. Versi: {0}", + "Kafka SASL Options": "Opsi Kafka SASL", + "Pick a SASL Mechanism...": "Pilih Mekanisme SASL…", + "AccessKey Id": "Id AccessKey", + "Secret AccessKey": "Secret AccessKey", + "Session Token": "Sesi Token", + "pushOthers": "Lainnya", + "programmingLanguages": "Bahasa Pemrograman", + "filterActivePaused": "Dijeda", + "templateMsg": "pesan pemberitahuan", + "templateHeartbeatJSON": "objek yang menggambarkan heartbeat", + "templateMonitorJSON": "objek yang menggambarkan monitor", + "templateLimitedToUpDownCertNotifications": "hanya tersedia untuk notifikasi NYALA/MATI/Sertifikat yang kedaluwarsa", + "templateLimitedToUpDownNotifications": "hanya tersedia untuk notifikasi NYALA/MATI", + "liquidIntroduction": "Kemampuan templat dicapai melalui bahasa templating Liquid. Silakan lihat {0} untuk petunjuk penggunaan. Ini adalah variabel yang tersedia:", + "selectedMonitorCount": "Terpilih: {0}", + "Check/Uncheck": "Centang/Hapus centang", + "Authorization Identity": "Identitas Otorisasi", + "styleElapsedTime": "Waktu yang telah berlalu di bawah bilah heartbeat", + "styleElapsedTimeShowNoLine": "Tampilkan (Tanpa Garis)", + "styleElapsedTimeShowWithLine": "Tampilkan (Dengan Garis)", + "Request Timeout": "Batas Waktu Permintaan", + "timeoutAfter": "Waktu habis setelah {0} detik", + "successKeywordExplanation": "Kata Kunci MQTT yang akan dianggap sukses", + "Remove the expiry notification": "Hapus hari pemberitahuan kedaluwarsa", + "Browser Screenshot": "Tangkapan Layar Browser", + "Remote Browsers": "Peramban Jarak Jauh", + "Remote Browser": "Peramban Jarak Jauh", + "Add a Remote Browser": "Tambahkan Remote Browser", + "Remote Browser not found!": "Remote Browser tidak ditemukan!", + "remoteBrowsersDescription": "Remote Browsers adalah cara alternatif untuk menjalankan Chromium secara lokal. Siapkan dengan layanan seperti browserless.io atau sambungkan ke layanan Anda sendiri", + "self-hosted container": "kontainer self-hosted", + "remoteBrowserToggle": "Secara default, Chromium berjalan di dalam kontainer Uptime Kuma. Anda dapat menggunakan remote browser dengan menekan tombol ini.", + "useRemoteBrowser": "Gunakan Remote Browser", + "deleteRemoteBrowserMessage": "Apakah Anda yakin ingin menghapus Remote Browser ini untuk semua monitor?", + "Add a new expiry notification day": "Tambahkan hari pemberitahuan kedaluwarsa baru", + "setup a new monitor group": "menyiapkan grup monitor baru", + "openModalTo": "buka modal ke {0}", + "Add a domain": "Tambahkan domain", + "Remove domain": "Hapus domain '{0}'", + "successKeyword": "Kata Kunci Sukses", + "ntfyPriorityHelptextAllExceptDown": "Semua peristiwa dikirim dengan prioritas ini, kecuali peristiwa {0}, yang memiliki prioritas {1}", + "statusPageSpecialSlugDesc": "Tanda khusus {0}: halaman ini akan ditampilkan jika tidak ada tanda khusus yang disediakan", + "settingUpDatabaseMSG": "Menyiapkan database. Mungkin perlu waktu beberapa saat, harap bersabar.", + "Search monitored sites": "Mencari situs yang dipantau", + "ntfyPriorityHelptextAllEvents": "Semua peristiwa dikirim dengan prioritas maksimum", + "What is a Remote Browser?": "Apa itu Remote Browser?", + "wayToGetHeiiOnCallDetails": "Cara mendapatkan ID Pemicu dan Kunci API dijelaskan di {documentation}", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Dari Nomor Telepon / Transmission Path Originating Address (TPOA)", + "gtxMessagingFromHint": "Penerima pesan akan melihat TPOA yang ditampilkan sebagai pengirim pesan. Diperbolehkan maksimal 11 karakter alfanumerik, kode pendek, kode panjang lokal atau internasional ({e164}, {e212} or {e214})", + "To Phone Number": "Untuk Nomor Telepon", + "gtxMessagingToHint": "Format internasional, dimulai dengan tanda plus \"+\" ({e164}, {e212} or {e214})", + "gtxMessagingApiKeyHint": "Anda dapat menemukan Kunci API Anda di: Perutean Akun Saya > Lihat Informasi Akun > Kredensial API > REST API (v2.x)", + "Originator type": "Tipe Originator", + "Alphanumeric (recommended)": "Alfanumerik (direkomendasikan)", + "Telephone number": "Nomor telepon", + "cellsyntOriginatortypeAlphanumeric": "String alfanumerik (maks 11 karakter). Penerima tidak dapat membalas pesan tersebut.", + "cellsyntOriginatortypeNumeric": "Nilai numerik (maks 15 digit) dengan nomor telepon dalam format internasional tanpa awalan 00 (contoh nomor Inggris 07920 110 000 harus diisi sebagai 447920110000). Penerima dapat membalas pesan tersebut.", + "cellsyntDestination": "Nomor telepon penerima dalam format internasional yang diawali angka 00, diikuti dengan kode negara, contoh : 00447920110000 untuk nomor Inggris 07920 110 000 (maks total 17 digit). Maksimum 25.000 penerima yang dipisahkan dengan tanda koma per permintaan HTTP.", + "Allow Long SMS": "Izinkan SMS Panjang", + "max 15 digits": "maks 15 digit", + "callMeBotGet": "Anda dapat membuat endpoint untuk {0}, {1} dan {2}. Ingatlah bahwa Anda mungkin akan terkena batas tarif. Batas tarif saat ini adalah: {3}", + "Originator": "Originator", + "cellsyntOriginator": "Terlihat di ponsel penerima sebagai pencetus pesan. Nilai dan fungsi yang diizinkan bergantung pada parameter tipe pencetus.", + "Destination": "Tujuan", + "wayToGetWhapiUrlAndToken": "Anda dapat menggunakan URL API dan token dengan bergabung ke saluran yang Anda inginkan dari {0}", + "whapiRecipient": "Nomor Telepon / Kontak ID / Grup ID", + "API URL": "URL API", + "cellsyntSplitLongMessages": "Pisahkan pesan panjang ke dalam 6 bagian. 153 x 6 = 918 karakter.", + "max 11 alphanumeric characters": "maksimal 11 karakter alfanumerik", + "wayToWriteWhapiRecipient": "Nomor telepon dalam format internasional, tanpa tanda tambah ({0}), Kontak ID ({1}) atau Grup ID ({2}).", + "documentationOf": "{0} Dokumentasi", + "Channel access token (Long-lived)": "Token akses saluran (durasi panjang)", + "Your User ID": "User ID Anda", + "mongodbCommandDescription": "Jalankan perintah MongoDB terhadap database. Untuk informasi tentang perintah yang tersedia, lihat {documentation}", + "Command": "Perintah", + "threemaRecipient": "Penerima", + "threemaRecipientType": "Tipe Penerima", + "threemaRecipientTypeIdentity": "Threem-ID", + "threemaRecipientTypeIdentityFormat": "8 karakter", + "threemaRecipientTypePhone": "Nomor Telepon", + "threemaRecipientTypePhoneFormat": "E.164, tanpa awalan +", + "threemaRecipientTypeEmail": "Alamat Email", + "threemaSenderIdentity": "Gateway-ID", + "threemaApiAuthenticationSecret": "Gateway-ID Secret", + "smspartnerPhoneNumber": "Nomor telepon", + "smspartnerSenderName": "Nama Pengirim SMS", + "Mentioning": "Menyebut", + "Don't mention people": "Jangan menyebut orang", + "bitrix24SupportUserID": "Masukkan ID pengguna Anda di Bitrix24. Anda dapat mengetahui ID dari tautan dengan membuka profil pengguna.", + "Bitrix24 Webhook URL": "Bitrix24 Webhook URL", + "wayToGetBitrix24Webhook": "Anda dapat membuat webhook dengan mengikuti langkah-langkah di {0}", + "receiverSevenIO": "Menerima nomor", + "receiverInfoSevenIO": "Jika nomor penerima tidak berlokasi di Jerman, Anda harus menambahkan kode negara di depan nomor tersebut (misalnya untuk kode negara 1 dari AS gunakan 117612121212, bukan 017612121212)", + "Host URL": "URL Host", + "senderSevenIO": "Mengirim nomor atau nama", + "apiKeysDisabledMsg": "Kunci API dinonaktifkan karena autentikasi dinonaktifkan.", + "forumPostName": "Nama postingan forum", + "threadForumPostID": "Thread / ID postingan Forum", + "e.g. {discordThreadID}": "mis. {discordThreadID}", + "Select message type": "Pilih jenis pesan", + "Send to channel": "Kirim ke saluran", + "Create new forum post": "Buat postingan forum baru", + "postToExistingThread": "Posting ke thread/postingan forum yang ada", + "whatHappensAtForumPost": "Buat postingan forum baru. Ini TIDAK mempublikasikan pesan di postingan yang sudah ada. Untuk mempublikasikan di postingan yang sudah ada gunakan '{option}'", + "smspartnerApiurl": "Anda dapat menemukan kunci API di dasbor Anda di {0}", + "smspartnerPhoneNumberHelptext": "Nomor harus dalam format internasional {0}, {1}. Beberapa angka harus dipisahkan dengan {2}", + "smspartnerSenderNameInfo": "Harus di antara 3..=11 karakter biasa", + "wayToGetSevenIOApiKey": "Kunjungi dasbor di bawah app.seven.io > pengembang > api key > tombol yang berwarna hijau", + "wayToGetThreemaGateway": "Anda dapat mendaftar ke Threema Gateway {0}.", + "threemaSenderIdentityFormat": "8 karakter, biasanya diawali dengan *", + "threemaBasicModeInfo": "Catatan: Integrasi ini menggunakan Threema Gateway dalam mode dasar (enkripsi berbasis server). Detail lebih lanjut dapat ditemukan {0}.", + "wayToGetDiscordThreadId": "Mendapatkan id postingan thread/forum mirip dengan mendapatkan id saluran. Baca selengkapnya tentang cara mendapatkan ID {0}", + "Mention group": "Sebutkan {group}", + "Refresh Interval Description": "Laman status akan melakukan penyegaran situs secara penuh setiap {0} detik", + "Refresh Interval": "Interval muat ulang", + "apiKeySevenIO": "Kunci API SevenIO", + "locally configured mail transfer agent": "transfer email agent yang dikonfigurasi secara lokal", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Masukkan nama host server yang ingin Anda sambungkan atau {localhost} jika Anda ingin menggunakan {local_mta}", + "ignoreTLSErrorGeneral": "Abaikan kesalahan TLS/SSL untuk koneksi", + "jsonQueryDescription": "Mengurai dan mengekstrak data spesifik dari respons JSON server menggunakan query JSON atau menggunakan “$” untuk respons mentah, jika tidak menginginkan JSON. Hasilnya kemudian dibandingkan dengan nilai yang diharapkan, sebagai string. Lihat {0} untuk dokumentasi dan gunakan {1} untuk bereksperimen dengan query.", + "now": "sekarang", + "time ago": "{0} yang lalu", + "-year": "-tahun", + "Json Query Expression": "Ekspresi Kueri Json", + "and": "dan", + "cacheBusterParam": "Tambahkan parameter {0}", + "cacheBusterParamDescription": "Parameter yang dihasilkan secara acak untuk melewati cache.", + "Community String": "Community String", + "snmpCommunityStringHelptext": "String ini berfungsi sebagai kata sandi untuk autentikasi dan mengontrol akses ke perangkat SNMP. Cocokkan dengan konfigurasi perangkat SNMP Anda.", + "OID (Object Identifier)": "OID (Object Identifier)", + "snmpOIDHelptext": "Masukkan OID untuk sensor atau status yang ingin Anda pantau. Gunakan alat manajemen jaringan seperti browser MIB atau perangkat lunak SNMP jika Anda tidak yakin tentang OID.", + "signl4Docs": "Anda dapat menemukan informasi selengkapnya tentang cara mengonfigurasi SIGNL4 dan cara mendapatkan URL webhook SIGNL4 di {0}.", + "greater than or equal to": "lebih dari atau sama dengan", + "wayToGetOnesenderUrlandToken": "Anda bisa mendapatkan URL dan Token dengan mengunjungi situs Onesender. Informasi lebih lanjut {0}", + "Condition": "Kondisi", + "SNMP Version": "Versi SNMP", + "Please enter a valid OID.": "Silakan masukkan OID yang tepat.", + "Host Onesender": "Host Onesender", + "Token Onesender": "Token Onesender", + "Recipient Type": "Tipe Penerima", + "Private Number": "Nomor Pribadi", + "privateOnesenderDesc": "Pastikan nomor telepon valid. Untuk mengirim pesan ke nomor telepon pribadi, contoh: 628123456789", + "Authorization Header": "Authorization Header", + "Form Data Body": "Form Data Body", + "OAuth Token URL": "OAuth Token URL", + "Client ID": "ID Klien", + "Client Secret": "Client Secret", + "OAuth Scope": "OAuth Scope", + "Optional: Space separated list of scopes": "Opsional: Daftar cakupan yang dipisahkan spasi", + "Go back to home page.": "Kembali ke halaman beranda.", + "No tags found.": "Tidak ada tag yang ditemukan.", + "Lost connection to the socket server.": "Kehilangan koneksi ke server soket.", + "Cannot connect to the socket server.": "Tidak dapat terhubung ke server soket.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL", + "Conditions": "Kondisi", + "conditionAdd": "Tambahkan Kondisi", + "conditionDelete": "Menghapus Kondisi", + "conditionAddGroup": "Tambahkan Grup", + "conditionDeleteGroup": "Menghapus Grup", + "conditionValuePlaceholder": "Nilai", + "equals": "sama dengan", + "not equals": "tidak sama dengan", + "contains": "berisi", + "not contains": "tidak berisi", + "starts with": "dimulai dengan", + "not starts with": "tidak dimulai dengan", + "ends with": "diakhiri dengan", + "not ends with": "tidak diakhiri dengan", + "less than": "kurang dari", + "greater than": "lebih dari", + "less than or equal to": "kurang dari atau sama dengan", + "record": "rekaman", + "groupOnesenderDesc": "Pastikan GroupIDnya valid. Untuk mengirim pesan ke Grup, misal: 628123456789-342345", + "Group ID": "ID Grup", + "Add Remote Browser": "Tambahkan Remote Browser", + "New Group": "Grup Baru", + "Group Name": "Nama Grup", + "OAuth2: Client Credentials": "OAuth2: Kredensial Klien", + "Authentication Method": "Metode Autentikasi", + "shrinkDatabaseDescriptionSqlite": "Memicu pangkalan data {vacuum} untuk SQLite. {auto_vacuum} sudah diaktifkan, tetapi tidak mendefragmentasi pangkalan data atau mengemas ulang halaman individual dari pangkalan data seperti yang dilakukan oleh perintah {vacuum}.", + "ignoredTLSError": "Galat TLS/SSL sudah diabaikan", + "Debug": "Awakutu", + "Copy": "Salin", + "CopyToClipboardError": "Tidak bisa menyalin ke papan klip: {galat}", + "CopyToClipboardSuccess": "Tersalin!", + "CurlDebugInfo": "Untuk pengawakutuan monitor, Anda bisa menempelkan ini ke terminal mesin Anda sendiri atau ke terminal mesin di mana uptime kuma sedang berjalan dan melihat apa yang Anda harapkan. Mohon perhatikan perbedaan jaringan seperti {firewalls}, {dns_resolvers}, atau {docker_networks}.", + "aboutSlackUsername": "Mengubah nama tampilan pengirim pesan. Jika Anda ingin menyebut seseorang, sertakan dalam nama yang mudah diingat.", + "Message format": "Format pesan", + "Sound": "Suara", + "Alphanumerical string and hyphens only": "Hanya string alfanumerik dan tanda hubung", + "Elevator": "Pengundak", + "Custom sound to override default notification sound": "Suara khusus untuk mengganti suara notifikasi bawaan", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Pemberitahuan yang sensitif terhadap waktu akan segera dikirimkan, meskipun perangkat dalam mode jangan ganggu.", + "RabbitMQ Nodes": "RabbitMQ Management Nodes", + "rabbitmqNodesDescription": "Masukkan URL untuk node manajemen RabbitMQ termasuk protokol dan port. Contoh: {0}", + "RabbitMQ Password": "Kata kunci RabbitMQ", + "rabbitmqHelpText": "Untuk menggunakan monitor, Anda perlu mengaktifkan Plugin Manajemen di pengaturan RabbitMQ Anda. Untuk informasi lebih lanjut, silakan baca {rabitmq_documentation}.", + "rabbitmqNodesRequired": "Harap atur node untuk monitor ini.", + "rabbitmqNodesInvalid": "Harap gunakan URL yang memenuhi syarat (dimulai dengan 'http') untuk node RabbitMQ.", + "RabbitMQ Username": "Nama pengguna RabbitMQ", + "Notification Channel": "Notifikasi Kanal", + "Send rich messages": "Kirim rich messages", + "Arcade": "Arkade", + "Correct": "Benar", + "Fail": "Gagal", + "Harp": "Harpa", + "Reveal": "Mengungkap", + "Bubble": "Gelembung", + "Doorbell": "Bel pintu", + "Flute": "Seruling", + "Money": "Uang", + "Scifi": "Fiksi ilmiah", + "Clear": "Jernih", + "Guitar": "Gitar", + "Pop": "Letupan", + "Time Sensitive (iOS Only)": "Sensitif terhadap Waktu (Khusus iOS)", + "From": "Dari", + "Can be found on:": "Bisa ditemukan di: {0}", + "The phone number of the recipient in E.164 format.": "Nomor telepon penerima dalam format E.164.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Antara ID pengirim teks atau nomor telepon dalam format E.164 jika Anda ingin menerima balasan.", + "SendGrid API Key": "Kunci API SendGrid", + "Separate multiple email addresses with commas": "Pisahkan beberapa alamat email dengan koma", + "telegramUseTemplate": "Gunakan template pesan kustom", + "telegramTemplateFormatDescription": "Telegram mengizinkan penggunaan berbagai bahasa markup untuk pesan, lihat {0} dari Telegram untuk detail lebih lanjut.", + "telegramServerUrlDescription": "Untuk menghapus batasan API bot Telegram atau mendapatkan akses di area yang diblokir (Seperti: China, Iran, dll.). Untuk informasi selengkapnya, klik {0}. Default: {1}", + "Use HTML for custom E-mail body": "Gunakan HTML untuk body E-mail kustom", + "Template plain text instead of using cards": "Templat teks biasa (alih-alih kartu)", + "issueWithGoogleChatOnAndroidHelptext": "Ini juga memungkinkan untuk menyiasati bug dari upstream, seperti pada {issuetackerURL}", + "smseagleMsgTtsAdvanced": "Panggilan teks-ke-ucapan (Lanjutan)", + "smseagleDocs": "Dokumentasi dan Ketersediaan APIv2: {0}", + "SpugPush Template Code": "Kode Templat", + "ntfyPriorityHelptextPriorityHigherThanDown": "Prioritas reguler harus lebih tinggi daripada prioritas {0}. Prioritas {1} lebih tinggi daripada {0} prioritas {2}", + "defaultFriendlyName": "Monitor Baru", + "telegramServerUrl": "(Opsional) Url Server", + "Path": "Path", + "mqttWebSocketPath": "Path WebSocket MQTT", + "mqttWebsocketPathExplanation": "Path WebSocket untuk MQTT melalui koneksi WebSocket (e.g., /mqtt)", + "mqttWebsocketPathInvalid": "Harap gunakan format Path WebSocket yang valid", + "mqttHostnameTip": "Harap gunakan format ini {hostnameFormat}", + "smseagleComma": "Gunakan koma untuk memisahkan beberapa nilai", + "Add Tags": "Tambahkan Tanda", + "tagAlreadyOnMonitor": "Tanda ini (name and value) sudah ada di monitor atau penambahan tertunda.", + "tagAlreadyStaged": "Tanda ini (name and value) sudah disiapkan untuk batch ini.", + "tagNameExists": "Tanda sistem dengan nama ini sudah ada. Silakan pilih dari daftar atau gunakan nama yang berbeda.", + "smseagleGroupV2": "ID Grup Kontak", + "smseagleContactV2": "ID kontak buku telepon", + "smseagleMsgType": "Tipe pesan", + "smseagleMsgSms": "Pesan SMS (default)", + "smseagleMsgRing": "Panggilan telepon", + "smseagleMsgTts": "Panggilan teks-ke-ucapan", + "smseagleDuration": "Durasi (dalam detik)", + "smseagleTtsModel": "ID model teks-ke-ucapan", + "smseagleApiType": "Versi API", + "smseagleApiv1": "APIv1 (untuk proyek yang sudah ada & kompatibilitas mundur)", + "smseagleApiv2": "APIv2 (direkomendasikan untuk integrasi baru)", + "ntfyPriorityDown": "Prioritas untuk kejadian DOWN", + "FlashDuty Push URL": "URL Push", + "FlashDuty Push URL Placeholder": "Salin dari halaman integrasi peringatan", + "templateServiceName": "nama layanan", + "templateHostnameOrURL": "nama host atau URL", + "templateStatus": "status", + "telegramUseTemplateDescription": "Jika diaktifkan, pesan akan dikirim menggunakan template pesan kustom.", + "wayToWriteEvolutionRecipient": "Nomor telepon dengan kode negara, namun tanpa tanda plus (+) di awal ({0}), ID Kontak ({1}) atau ID Grup ({2}).", + "Clear All Events": "Hapus Semua Kejadian", + "clearAllEventsMsg": "Anda yakin ingin menghapus semua kejadian?", + "Events cleared successfully": "Semua kejadian berhasil dibersihkan.", + "No monitors found": "Tidak ada monitor yang ditemukan.", + "Could not clear events": "Gagal membersihkan {failed}/{total} event", + "wayToGetEvolutionUrlAndToken": "Anda bisa mendapatkan URL API dan token dengan membuka channel yang Anda inginkan di {0}", + "evolutionRecipient": "Nomor Telepon / ID Kontak / ID Grup", + "evolutionInstanceName": "Nama Instans", + "Phone numbers": "Nomor telepon", + "Sender name": "Nama pengirim", + "auto-select": "Pilih Otomatis", + "ignoreSecWebsocketAcceptHeaderDescription": "Izinkan server untuk tidak menjawab dengan header Sec-WebSocket-Accept, bila peningkatan websocket sukses.", + "Ignore Sec-WebSocket-Accept header": "Abaikan header {0}", + "wsSubprotocolDescription": "Masukkan daftar yang dipisah koma dari sub protokol. Untuk informasi lebih lanjut tentang sub protokol, silakan lihat {documentation}", + "WebSocket Application Messaging Protocol": "WAMP (WebSocket Application Messaging Protocol)", + "Session Initiation Protocol": "Transport WebSocket bagi SIP (Session Initiation Protocol)", + "Subprotocol": "Sub protokol", + "wsCodeDescription": "Untuk informasi lebih lanjut tentang kode status, silakan lihat {rfc6455}", + "Subprotocol(s)": "Sub protokol", + "supportBaleChatID": "Mendukung ID Obrolan Kanal / Grup / Obrolan Langsung", + "wayToGetBaleChatID": "Anda bisa mendapatkan id obrolan Anda dengan mengirim pesan ke bot dan pergi ke URL ini untuk melihat chat_id:", + "wayToGetBaleToken": "Anda bisa memperoleh token dari {0}.", + "ariaResumeMaintenance": "Lanjutkan jadwal pemeliharaan ini", + "ariaCloneMaintenance": "Buat suatu salinan dari jadwal pemeliharaan ini", + "ariaEditMaintenance": "Sunting jadwal pemeliharaan ini", + "SMTP Security": "Keamanan SMTP", + "certHostnameMismatch": "Nama host sertifikat tidak cocok dengan URL monitor.", + "deleteGroupMsg": "Anda yakin ingin menghapus grup ini?", + "descriptionHelpText": "Ditampilkan pada dasbor internal. Markdown diizinkan dan disanitasi (mempertahankan spasi dan indentasi) sebelum ditampilkan.", + "Clone Maintenance": "Klon Pemeliharaan", + "ariaPauseMaintenance": "Jedakan jadwal pemeliharaan ini", + "Duration (Minutes)": "Durasi (Menit)", + "ariaDeleteMaintenance": "Hapus jadwal pemeliharaan ini", + "Ignore STARTTLS": "Abaikan STARTTLS", + "Use STARTTLS": "Pakai STARTTLS", + "Manual": "Manual", + "Select All": "Pilih Semua", + "OAuth Audience": "Audiens OAuth", + "sipsakPingWarning": "Untuk menggunakan monitor Ping Opsi SIP, Anda perlu menginstal Uptime Kuma tanpa Docker dan juga menginstal klien Sipsak di server Anda.", + "webhookPostMethodDesc": "Metode POST cocok untuk sebagian besar server HTTP modern.", + "brevoApiHelp": "Buat kunci API di sini: {0}", + "brevoToEmail": "Email Ke", + "resendLeaveBlankForDefaultName": "biarkan kosong untuk nama default", + "pingNumericLabel": "Keluaran Numerik", + "pingGlobalTimeoutLabel": "Tenggat Waktu Global", + "Umami": "Umami", + "resendApiKey": "Kirim Ulang Kunci API", + "HTTP Method": "Metode HTTP", + "invalidDNSHostname": "Nama host tidak valid. Nama host harus berupa FQDN yang valid. Dapat berupa karakter wildcard, diawali dengan garis bawah, atau diakhiri dengan titik.", + "invalidURL": "URL Tidak Valid", + "deleteChildrenMonitors": "Hapus juga monitor anak langsung dan anak-anaknya jika ada | Hapus juga semua {count} monitor anak langsung dan anak-anaknya jika ada", + "webhookGetMethodDesc": "GET mengirimkan data sebagai parameter kueri dan tidak mengizinkan mengonfigurasi body. Berguna untuk memicu monitor Push Uptime Kuma.", + "resendApiHelp": "Buat kunci API di sini {0}", + "resendFromName": "Nama Pengirim", + "resendFromEmail": "Email Pengirim", + "resendToEmail": "Email Ke", + "pingCountDescription": "Banyaknya paket yang akan dikirim sebelum berhenti", + "pingNumericDescription": "Jika dicentang, alamat IP akan ditampilkan sebagai pengganti nama host simbolik", + "pingPerRequestTimeoutLabel": "Tenggat Waktu Per-Ping", + "pingIntervalAdjustedInfo": "Interval disesuaikan berdasarkan cacah paket, tenggat waktu global, dan tenggat waktu per-ping", + "message": "pesan", + "json_value": "Nilai JSON", + "year": "tahun | tahun", + "Custom URL": "URL Ubahan", + "Google": "Google", + "Plausible": "Plausible", + "mtls-auth-server-ca-label": "CA", + "mtls-auth-server-ca-placeholder": "Server CA", + "resendLeaveBlankForDefaultSubject": "Biarkan kosong untuk subjek default", + "wayToGetWahaApiKey": "Kunci API adalah nilai variabel lingkungan WHATSAPP_API_KEY yang Anda gunakan untuk menjalankan WAHA.", + "Matomo": "Matomo", + "systemService": "Layanan Sistem", + "systemServiceName": "Nama Layanan", + "systemServiceDescription": "Memeriksa apakah layanan sistem {service_name} aktif", + "systemServiceDescriptionLinux": "Memeriksa apakah layanan systemd Linux {service_name} aktif", + "systemServiceDescriptionWindows": "Memeriksa apakah Windows Service Manager {service_name} sedang berjalan", + "Deselect All": "Pilih Tak Satu Pun", + "pingGlobalTimeoutDescription": "Total waktu dalam detik sebelum ping berhenti, terlepas dari paket yang dikirim", + "OneChatAccessToken": "Token Akses OneChat", + "twilioApiKeyHelptext": "Kunci API bersifat opsional tetapi disarankan. Anda dapat memberikan SID Account dan AuthToken dari halaman TwilioConsole atau SID Account dan pasangan Api Key dan Api Key secret", + "twilioMessagingServiceSID": "SID Layanan Pesan (opsional)", + "pingPerRequestTimeoutDescription": "Ini adalah waktu tunggu maksimum (dalam detik) sebelum menganggap satu paket ping hilang", + "customUrlDescription": "Akan digunakan sebagai URL yang dapat diklik, bukan URL monitor.", + "twilloMessagingServiceSIDHelptext": "Masukkan SID Layanan Pesan Anda di sini jika menggunakan {twillo_messaging_service_help_link} untuk mengelola pengirim dan fitur", + "systemServiceExpectedOutput": "Keluaran yang diharapkan: \"{0}\"", + "Optional: The audience to request the JWT for": "Opsional: Audiens bagi permintaan JWT", + "brevoApiKey": "Kunci API Brevo", + "brevoFromEmail": "Email Pengirim", + "brevoFromName": "Nama Pengirim", + "brevoLeaveBlankForDefaultName": "biarkan kosong untuk nama default", + "brevoCcEmail": "Email CC", + "resendSubject": "Subjek", + "pingCountLabel": "Paket Maks", + "smtpHelpText": "'SMTPS' menguji apakah SMTP/TLS berfungsi; 'Ignore TLS' terhubung melalui teks polos; 'STARTTLS' menyambung, mengeluarkan perintah STARTTLS, dan memverifikasi sertifikat server. Tak satu pun dari opsi ini mengirim email.", + "pause": "Jeda", + "showOnlyLastHeartbeat": "Tampilkan Hanya Heartbeat Terakhir", + "Analytics Type": "Jenis Analitik", + "Analytics ID": "ID Analitik", + "Analytics Script URL": "URL Skrip Analitik", + "hostnameCannotBeIP": "Nama host DNS tidak boleh berupa IP. Apakah Anda bermaksud menggunakan kolom resolver?", + "invalidHostnameOrIP": "Nama host atau IP tidak valid. Nama host harus berupa FQDN yang valid. Tidak dapat menggunakan wildcard. Dapat mengandung garis bawah, atau diakhiri dengan titik.", + "wildcardOnlyForDNS": "Nama host wildcard hanya didukung untuk monitor DNS.", + "systemServiceCommandHint": "Perintah yang digunakan: {command}", + "Invalid userId": "userId tidak valid [{userId}]", + "Mention Mobile List": "Sebutkan daftar seluler", + "Mention User List": "Sebutkan daftar id pengguna", + "Dingtalk Mobile List": "Daftar seluler", + "Dingtalk User List": "Daftar ID pengguna", + "Enter a list of userId": "Masukkan daftar userId", + "Enter a list of mobile": "Masukkan daftar ponsel", + "Invalid mobile": "Nomor ponsel tidak valid [{mobile}]", + "Enter the list of nodes": "Masukkan daftar node manajemen RabbitMQ", + "Press Enter to add node": "Tekan Enter untuk menambahkan node", + "brevoSeparateMultipleEmails": "Pisahkan beberapa alamat email dengan koma", + "brevoSubject": "Subjek", + "brevoBccEmail": "Email BCC", + "brevoLeaveBlankForDefaultSubject": "biarkan kosong untuk subjek default", + "OneChatBotId": "ID Bot OneChat", + "wahaSession": "Sesi", + "wahaChatId": "ID Obrolan (Nomor Telepon / ID Kontak / ID Grup)", + "wayToGetWahaApiUrl": "URL Instans WAHA Anda.", + "OneChatUserIdOrGroupId": "ID Pengguna atau ID Grup OneChat", + "Notifications Enabled": "Notifikasi Diaktifkan", + "Allow Notifications": "Izinkan Notifikasi", + "Number of retry attempts if webhook fails": "Cacah percobaan ulang (setiap 60-180 detik) jika webhook gagal.", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Memasang bot Nextcloud Talk memerlukan akses administrator ke server.", + "Disable URL in Notification": "Nonaktifkan URL di Notifikasi", + "ipFamilyDescriptionAutoSelect": "Menggunakan {happyEyeballs} untuk menentukan keluarga IP.", + "Staged Tags for Batch Add": "Tag Bertahap untuk Penambahan Massal", + "Maximum Retries": "Cacah Percobaan Maksimum", + "minimumIntervalWarning": "Interval di bawah 20 detik dapat mengakibatkan kinerja yang buruk.", + "lowIntervalWarning": "Anda yakin ingin mengatur nilai interval di bawah 20 detik? Kinerja mungkin akan menurun, terutama jika terdapat banyak monitor.", + "Basic checkbox toggle button group": "Grup tombol jungkit kotak centang dasar", + "Splunk Rest URL": "URL Rest Splunk", + "wayToGetClickSMSIRTemplateID": "Templat Anda harus berisi bidang {uptkumaalert}. Anda dapat membuat templat baru {here}.", + "Template ID": "ID Templat", + "settingsDomainExpiry": "Kedaluwarsa Domain", + "labelDomainExpiry": "Ked. Domain", + "Recipient Numbers": "Nomor Penerima", + "Open Badge Link Generator": "Buka Generator Tautan Lencana", + "Badge Link Generator": "Generator Tautan Lencana {0}", + "Badge Link Generator Helptext": "Tautan lencana tersedia untuk semua monitor yang ditugaskan ke halaman status publik. Untuk informasi lebih lanjut, silakan lihat {documentation}.", + "Happy Eyeballs algorithm": "Algoritma Happy Eyeballs", + "labelDomainNameExpiryNotification": "Pemberitahuan Kedaluwarsa Nama Domain", + "Webpush Helptext": "Pemberitahuan push web hanya berfungsi dengan koneksi SSL (HTTPS). Untuk perangkat iOS, halaman web harus ditambahkan ke layar beranda terlebih dahulu.", + "domainExpiryDescription": "Aktifkan notifikasi saat nama domain kedaluwarsa dalam:", + "Severity": "Keparahan", + "SMSManager": "SMSManager", + "Message Format": "Format Pesan", + "System Service": "Layanan Sistem", + "YZJ Webhook URL": "URL Webhook YZJ", + "imageResetConfirmation": "Gambar diatur ulang ke default", + "Nextcloud host": "Penyedia layanan Nextcloud", + "Conversation token": "Token percakapan", + "Clear Form": "Bersihkan Formulir", + "Font Twemoji by Twitter licensed under": "Fonta Twemoji oleh Twitter berlisensi di bawah", + "Ip Family": "Keluarga IP", + "Add Another Tag": "Tambahkan Tag Lainnya", + "Send UP silently": "Kirim NYALA diam-diam", + "Send DOWN silently": "Kirim MATI diam-diam", + "Bot secret": "Rahasia bot", + "Browser not supported": "Peramban tidak didukung", + "Unable to get permission to notify": "Tidak bisa memperoleh izin untuk memberi tahu (permintaan ditolak atau diabaikan).", + "screenshot of the website": "Tangkapan layar situs web", + "mtls-auth-server-cert-placeholder": "Badan sert", + "mtls-auth-server-key-label": "Kunci", + "Basic radio toggle button group": "Grup tombol jungkit radio dasar", + "mtls-auth-server-key-placeholder": "Badan kunci", + "smscTranslit": "smscTranslit", + "SSL/TLS": "SSL/TLS", + "playground": "tempat bermain", + "Check Type": "Jenis Cek", + "mtls-auth-server-cert-label": "Sert", + "Endpoint": "Titik akhir", + "Show this Maintenance Message on which Status Pages": "Tampilkan Pesan Pemeliharaan ini di Halaman Status mana", + "Service Name": "Nama Layanan", + "GRPC Options": "Opsi GRPC", + "Metadata": "Metadata", + "End": "Akhir", + "Details": "Detail", + "smsplanetApiToken": "Token untuk API SMSPlanet", + "wayToGetWahaSession": "Dari sesi ini, WAHA mengirimkan notifikasi ke ID Obrolan. Anda dapat menemukannya di Dasbor WAHA.", + "wayToWriteWahaChatId": "Nomor telepon dengan awalan internasional, tetapi tanpa tanda plus di awal ({0}), ID Kontak ({1}) atau ID Grup ({2}). Notifikasi dikirim ke ID Obrolan ini dari Sesi WAHA.", + "Template Format": "Format Templat", + "smsplanetApiDocs": "Informasi detail mengenai cara mendapatkan token API dapat ditemukan di {the_smsplanet_documentation}.", + "the smsplanet documentation": "dokumentasi smsplanet", + "smsplanetNeedToApproveName": "Perlu disetujui di panel klien", + "RSS Title": "Judul RSS", + "Leave blank to use status page title": "Biarkan kosong untuk menggunakan judul halaman status", + "Clear current filters": "Hapus filter saat ini", + "minPing": "Ping Min", + "Sort options": "Opsi pengurutan", + "Sort by status": "Urutkan berdasarkan status", + "Sort by name": "Urutkan berdasarkan nama", + "Sort by uptime": "Urutkan berdasarkan waktu aktif", + "Sort by certificate expiry": "Urutkan berdasarkan tanggal kedaluwarsa sertifikat", + "Region": "Wilayah", + "PushDeer Server URL": "URL Server PushDeer", + "To Number": "Nomor Ke", + "GrafanaOncallURL": "URL OnCall Grafana", + "Never": "Tidak Pernah", + "Plain Text": "Teks Polos", + "Message Template": "Templat Pesan", + "avgPing": "Ping Rerata", + "maxPing": "Ping Maks", + "YZJ Robot Token": "Token Robot YZJ", + "Actions": "Aksi", + "selectedMonitorCountMsg": "dipilih: {n} | dipilih: {n}", + "selectMonitorMsg": "Pilih monitor untuk melakukan aksi", + "selectAllMonitorsAria": "Pilih semua monitor", + "deselectAllMonitorsAria": "Hapus pilihan semua monitor", + "deleteMonitorsMsg": "Anda yakin hendak menghapus monitor yang dipilih?", + "pausedMonitorsMsg": "Dijeda {n} monitor | Dijeda {n} monitor", + "resumedMonitorsMsg": "Dilanjutkan {n} monitor | Dilanjutkan {n} monitor", + "Resolver Server(s)": "Server Resolver", + "HeadersInvalidFormatBecause": "Header permintaan bukan JSON yang valid karena {error}", + "BodyInvalidFormatBecause": "Body permintaan bukan JSON yang valid karena {error}", + "steamApiKeyDescriptionAt": "Untuk pemantauan suatu Server Game Steam, Anda perlu kunci Web-API Steam. Anda dapat mendaftarkan kunci API Anda pada {url}", + "saveErrorResponseForNotifications": "Simpan Respon Galat HTTP bagi Pemberitahuan", + "saveResponseForNotifications": "Simpan Respon Sukses HTTP bagi Pemberitahuan", + "saveResponseDescription": "Menyimpan respon HTTP dan membuatnya tersedia bagi templat pemberitahuan sebagai {templateVariable}", + "responseMaxLength": "Panjang Maks Respon (byte)", + "responseMaxLengthDescription": "Ukuran maksimum data respon yang akan disimpan. Atur ke 0 untuk tak terbatas. Respon yang lebih besar akan dipotong. Baku: 1024 (1KB)", + "deletedMonitorsMsg": "Dihapus {n} monitor | Dihapus {n} monitor", + "noMonitorsPausedMsg": "Tidak ada monitor yang dijeda (tidak ada yang aktif)", + "noMonitorsResumedMsg": "Tidak ada monitor yang dilanjutkan (tidak ada yang tak aktif)", + "bulkDeleteErrorMsg": "Gagal menghapus {n} monitor | Gagal menghapus {n} monitor", + "notificationPushServices": "Layanan Push", + "notificationUniversal": "Universal", + "notificationChatPlatforms": "Platform Obrolan", + "notificationSmsServices": "Layanan SMS", + "notificationEmail": "Surel", + "notificationOther": "Integrasi Lain", + "notificationIncidentManagement": "Manajemen Insiden", + "notificationHomeAutomation": "Otomasi Rumah", + "Only retry if status code check fails": "Hanya coba lagi bila pemeriksaan kode status gagal", + "retryOnlyOnStatusCodeFailureDescription": "Bila difungsikan, coba ulang hanya akan terjadi ketika pemeriksaan kode status HTTP gagal (mis., server mati). Bila pemeriksaan kode status lolos tapi query JSON gagal, monitor akan ditandai sebagai mati seketika tanpa coba ulang.", + "enableSSL": "Fungsikan SSL/TLS", + "mariadbUseSSLHelptext": "Fungsikan untuk memakai koneksi terenkripsi ke basis data Anda. Diperlukan bagi kebanyakan basis data cloud.", + "mariadbCaCertificateLabel": "Sertifikat CA", + "mariadbCaCertificateHelptext": "Tempelkan Sert CA dalam format PEM untuk memakai sertifikat self-signed. Biarkan kosong bila basis data Anda memakai suatu sertifikat yang ditandatangani oleh suatu CA publik.", + "aliyun-template-optional-parameters": "Parameter opsional: {parameters}", + "serwersmsRecipientTypeGroup": "Grup", + "serwersmsRecipientType": "Tipe penerima", + "serwersmsRecipientTypePhone": "Nomor telepon", + "serwersmsGroupId": "ID Grup", + "noMonitorsOrStatusPagesSelectedError": "Tidak bisa membuat pemeliharaan tanpa halaman status atau monitor yang terpengaruh", + "noMonitorsSelectedWarning": "Anda membuat suatu pemeliharaan tanpa monitor apa pun yang terpengaruh. Anda yakin ingin melanjutkan?", + "aliyun-template-requirements-and-parameters": "Templat SMS aliyun harus memuat parameter: {parameters}", + "OptionalParameters": "Parameter Opsional", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Karena pembatasan carrier, fungsikan variabel opsional dengan risiko tidak terkirim", + "checkPriceAt": "Periksa harga {service} pada {url}", + "You can divide numbers with commas or semicolons": "Anda dapat membagi bilangan dengan {comma} atau {semicolon}", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" terlalu pendek bagi suatu domain tingkat puncak", + "domain_expiry_unsupported_public_suffix": "Domain \"{domain}\" tidak punya akhiran publik yang valid", + "domain_expiry_unsupported_invalid_domain": "Nilai \"{hostname}\" yang dikonfigurasi bukanlah suatu nama domain yang valid", + "Halo PSA Webhook URL": "URL Webhook Halo PSA", + "Halo PSA": "Halo PSA", + "domain_expiry_unsupported_missing_target": "Tidak ada domain atau nama host valid yang dikonfigurasi bagi monitor ini", + "serwersmsGroupIdHelptext": "ID atau ID grup dalam Panel Pelanggan. Identifier ini dapat diunduh melalui aksi grup / indeks atau dengan menyalin mereka dari menyunting grup dalam Panel Pelanggan.", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" adalah suatu alamat IP. Pemantauan kedaluwarsa domain memerlukan suatu nama domain", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Pemantauan kedaluwarsa domain tidak tersedia bagi \".{publicSuffix}\" karena tidak ada layanan RDAP yang dicantumkan oleh IANA", + "domain_expiry_unsupported_monitor_type": "Pemantauan kedaluwarsa domain tidak didukung bagi tipe monitor ini", + "ntfyCall": "Panggilan Telepon", + "ntfyCallHelptext": "Buat suatu panggilan telepon ketika peringatan terpicu. Atur ke 'ya' untuk memakai nomor terverifikasi pertama Anda, atau masukkan suatu nomor telepon tertentu (mis. +6281122334455). Memerlukan ntfy Pro dan suatu nomor telepon terverifikasi.", + "Setup Instructions": "Petunjuk Penyiapan", + "halopsa_username_desc": "Nama pengguna untuk mengotentikasi webhook Halo PSA", + "username": "Nama Pengguna", + "password": "Kata Sandi", + "halopsa_password_desc": "Kata sandi untuk mengotentikasi webhook Halo PSA", + "halopsa_setup_step2": "Konfigurasikan aksi runbook untuk memroses peringatan (mis. Buat Tiket)", + "halopsa_setup_step3": "Salin URL Webhook dan tempelkan itu ke bidang teks di atas", + "halopsa_setup_step1": "Buat suatu Integration Runbook dalam HaloPSA(Configuration → Integrations → Integration Runbooks)", + "expectedTlsAlertDescription": "Pilih peringatan TLS yang Anda harap dikembalikan oleh server. Gunakan {code} untuk memverifikasi titik akhir mTLS menolak koneksi tanpa sertifikat klien. Lihat {link} untuk rinciannya.", + "TLS Alerts": "Peringatan TLS", + "Expected TLS Alert": "Peringatan TLS yang Diharapkan", + "None (Successful Connection)": "Nihil (Koneksi Sukses)", + "halopsa_setup_step4": "Pilih Otentikasi dasar dan buat nama pengguna dan kata sandi. Dan ketikkan atau tempelkan nama pengguna dan kata sandi di atas bidang text", + "halopsa_webhook_url_desc": "Masukkan URL webhook dari Halo PSA Integration Runbook Anda(Configuration > Integrations > Custom Integrations > Integration Runbooks). Pilih 'Can only be started from Halo and from a public endpoint' ketika membuat webook.", + "passwordTooWeak": "Kata sandi terlalu lemah. Itu mesti mengandung karakter alfabet dan numerik. Panjangnya mesti setidaknya 6 karakter.", + "TLS Alert Spec": "RFC 8446", + "Google Apps Script Webhook URL": "URL Webhook Google Apps Script", + "unknownDays": "Hari tidak diketahui", + "Monitors": "{n} Monitor | {n} Monitor", + "Disable STARTTLS": "Nonaktifkan STARTTLS", + "disableSTARTTLSDescription": "Aktifkan opsi ini untuk server SMTP yang tidak mendukung STARTTLS. Surel akan dikirimkan melalui koneksi yang tidak terenkripsi.", + "versionIs": "Versi: {version}", + "logoutCurrentUser": "Keluar {username}", + "Examples:": "Contoh: {0}", + "Certificate Chain:": "Rantai Sertifikat:", + "dateCreatedAtFromNow": "Tanggal dibuat: {date} ({fromNow})", + "lastUpdatedAtFromNow": "Terakhir Diperbarui: {date} ({fromNow})", + "frontendVersionIs": "Versi Frontend: {version}", + "cronScheduleDescription": "Jadwal: {description}", + "No incidents recorded": "Tidak ada insiden terekam", + "Load More": "Muat lebih banyak", + "Loading...": "Memuat...", + "Pin this incident": "Sematkan insiden ini", + "Incident description": "Deskripsi insiden", + "Incident not found or access denied": "Insiden tidak ditemukan atau akses ditolak", + "Past Incidents": "Insiden yang lalu", + "Incident title": "Judul insiden", + "Pinned incidents are shown prominently on the status page": "Insiden yang disematkan ditampilkan dengan jelas pada halaman status", + "Edit Incident": "Ubah insiden", + "Resolve": "Atasi", + "Resolved": "Teratasi", + "slug is not found": "Slug tidak ditemukan", + "Please input content": "Mohon masukan konten", + "deleteIncidentMsg": "Apakah anda yakin akan menghapus insiden ini?", + "Please input title": "Mohon masukkan judul", + "days": "{n} hari | {n} hari", + "hours": "{n} jam | {n} jam", + "minutes": "{n} menit | {n} menit", + "minuteShort": "{n} mnt | {n} mnt", + "years": "{n} tahun | {n} tahun", + "createdAt": "Dibuat: {date}", + "lastUpdatedAt": "Terakhir Diperbarui: {date}", + "Sets end time based on start time": "Atur waktu akhir berdasar waktu mulai", + "Please set start time first": "Mohon atur waktu mulai terlebih dahulu", + "example": "Contoh", + "Result": "Hasil", + "Cloud ID": "ID Cloud", + "API Token": "Token API", + "See Jira Cloud Docs": "Lihat Dokumentasi Jira Cloud", + "templateAvailableVariables": "Variable Tersedia", + "pingPacketSizeWarning": "Ukuran paket yang lebih kecil dari 16 tidak menghasilkan waktu bolak-balik di Linux", + "monitorTypeGameServer": "Server Game", + "monitorTypeDatabase": "Tipe Database Monitor", + "monitorTypeSpecial": "Spesial", + "oracledbConnectionString": "Database Oracle: {connectionString}" +} diff --git a/src/lang/it-IT.json b/src/lang/it-IT.json new file mode 100644 index 0000000..dc3df79 --- /dev/null +++ b/src/lang/it-IT.json @@ -0,0 +1,1736 @@ +{ + "languageName": "Italiano (Italian)", + "checkEverySecond": "controlla ogni {0} secondi", + "retryCheckEverySecond": "Riprova ogni {0} secondi", + "retriesDescription": "Tentativi prima che il servizio venga marcato come \"SPENTO\" e che una notifica venga inviata", + "ignoreTLSError": "Ignora gli errori TLS/SSL per i siti HTTPS", + "upsideDownModeDescription": "Se il servizio risulta raggiungibile viene marcato come \"SPENTO\".", + "maxRedirectDescription": "Numero massimo di redirezionamenti consentito. Per disabilitare, impostare \"0\".", + "acceptedStatusCodesDescription": "Elenco di codici di stato HTTP che sono considerati validi.", + "passwordNotMatchMsg": "La password non corrisponde.", + "notificationDescription": "Per funzionare, le notifiche devono essere assegnate ad un monitoraggio.", + "keywordDescription": "Cerca la parola chiave nella risposta in HTML o JSON. Distingue tra maiuscole e minuscole.", + "pauseDashboardHome": "In Pausa", + "deleteMonitorMsg": "Sei sicuro di voler eliminare questo monitoraggio?", + "deleteNotificationMsg": "Sei sicuro di voler eliminare questa notifica per tutti gli oggetti monitorati?", + "resolverserverDescription": "Cloudflare è il server predefinito ma è possibile cambiare il server DNS.", + "rrtypeDescription": "Sceglie il tipo di RR che si vuole monitorare", + "pauseMonitorMsg": "Sei sicuro di voler mettere in pausa?", + "enableDefaultNotificationDescription": "Per ogni nuovo monitoraggio questa notifica sarà abilitata in modo predefinito. È comunque possibile disabilitare la notifica singolarmente.", + "clearEventsMsg": "Sei sicuro di voler eliminare tutti gli eventi per questo monitoraggio? Questa operazione rimuove definitivamente i dati dei messaggi associati e non può essere annullata.", + "clearHeartbeatsMsg": "Sei sicuro di voler eliminare tutti i dati relativi ai battiti cardiaci di questo monitoraggio? Questa operazione elimina definitivamente tutti i dati relativi ai battiti cardiaci e non può essere annullata.", + "confirmClearStatisticsMsg": "Sei sicuro di voler eliminare TUTTE le statistiche?", + "importHandleDescription": "Seleziona \"Ignora esistenti\" se vuoi ignorare tutti i monitoraggi o le notifiche con lo stesso nome. \"Sovrascrivi\" eliminerà tutti i monitoraggi e le notifiche esistenti.", + "confirmImportMsg": "Sei sicuro di voler importare il backup? Controlla di aver selezionato l'opzione corretta di importazione.", + "twoFAVerifyLabel": "Digita il token per verificare che l'autenticazione a due fattori funzioni correttamente:", + "tokenValidSettingsMsg": "Il token è valido! È ora possibile salvare le impostazioni.", + "confirmEnableTwoFAMsg": "Sei sicuro di voler abilitare l'autenticazione a due fattori?", + "confirmDisableTwoFAMsg": "Sei sicuro di voler disabilitare l'autenticazione a due fattori?", + "Settings": "Impostazioni", + "Dashboard": "Cruscotto", + "New Update": "Nuovo aggiornamento disponibile", + "Language": "Lingua", + "Appearance": "Aspetto", + "Theme": "Tema", + "General": "Generale", + "Primary Base URL": "URL base primario", + "Version": "Versione", + "Check Update On GitHub": "Controlla aggiornamenti su GitHub", + "List": "Lista", + "Add": "Aggiungi", + "Add New Monitor": "Aggiungi nuovo monitoraggio", + "Quick Stats": "Statistiche rapide", + "Up": "Operativo", + "Down": "Spento", + "Pending": "In attesa", + "Unknown": "Sconosciuti", + "Pause": "Metti in pausa", + "Name": "Nome", + "Status": "Stato", + "DateTime": "Data e Ora", + "Message": "Messaggio", + "No important events": "Nessun evento importante", + "Resume": "Riprendi", + "Edit": "Modifica", + "Delete": "Elimina", + "Current": "Corrente", + "Uptime": "Tempo di attività", + "Cert Exp.": "Scadenza Certificato.", + "day": "giorno | giorni", + "-day": "-giorni", + "hour": "ora", + "-hour": "-ora", + "Response": "Risposta", + "Ping": "Ping", + "Monitor Type": "Modalità di monitoraggio", + "Keyword": "Parola chiave", + "Friendly Name": "Nome", + "URL": "URL", + "Hostname": "Nome sistema", + "Port": "Porta", + "Heartbeat Interval": "Intervallo di controllo", + "Retries": "Tentativi", + "Heartbeat Retry Interval": "Intervallo tra i tentativi di controllo", + "Advanced": "Avanzate", + "Upside Down Mode": "Modalità inversa", + "Max. Redirects": "Reindirizzamenti massimi", + "Accepted Status Codes": "Codici di stato accettati", + "Push URL": "Push URL", + "needPushEvery": "Notificare questo URL ogni {0} secondi.", + "pushOptionalParams": "Parametri aggiuntivi: {0}", + "Save": "Salva", + "Notifications": "Notifiche", + "Not available, please setup.": "Non disponibile, richiesta configurazione manuale.", + "Setup Notification": "Configura le notifiche", + "Light": "Chiaro", + "Dark": "Scuro", + "Auto": "Automatico", + "Theme - Heartbeat Bar": "Tema (barra di stato)", + "Normal": "Normale", + "Bottom": "Sotto", + "None": "Nessuna", + "Timezone": "Fuso Orario", + "Search Engine Visibility": "Visibilità ai motori di ricerca", + "Allow indexing": "Consenti l'indicizzazione", + "Discourage search engines from indexing site": "Evita l'indicizzazione ai motori di ricerca", + "Change Password": "Cambia password", + "Current Password": "Password corrente", + "New Password": "Nuova password", + "Repeat New Password": "Ripeti nuova password", + "Update Password": "Modifica password", + "Disable Auth": "Disabilita autenticazione", + "Enable Auth": "Abilita autenticazione", + "disableauth.message1": "{disableAuth}?", + "disable authentication": "Disabilita l'autenticazione", + "disableauth.message2": "{intendThirdPartyAuth} prima di Uptime Kuma, come ad esempio Cloudflare Access.", + "where you intend to implement third-party authentication": "Questa opzione è per chi possiede un sistema di autenticazione gestito da terze parti", + "Please use this option carefully!": "Utilizzare con attenzione!", + "Logout": "Disconnetti", + "Leave": "Annulla", + "I understand, please disable": "Lo capisco, disabilita l'autenticazione", + "Confirm": "Conferma", + "Yes": "Sì", + "No": "No", + "Username": "Nome utente", + "Password": "Password", + "Remember me": "Ricorda credenziali", + "Login": "Accesso", + "No Monitors, please": "Nessun monitoraggio, per favore", + "add one": "aggiungine uno", + "Notification Type": "Servizio di notifica", + "Email": "E-mail", + "Test": "Fai una prova", + "Certificate Info": "Informazioni sul certificato", + "Resolver Server": "Server DNS", + "Resource Record Type": "Tipo di Resource Record", + "Last Result": "Ultimo risultato", + "Create your admin account": "Crea l'account amministratore", + "Repeat Password": "Ripeti password", + "Import Backup": "Importa backup", + "Export Backup": "Esporta backup", + "Export": "Esporta", + "Import": "Importa", + "respTime": "Tempo di risposta (ms)", + "notAvailableShort": "N/D", + "Default enabled": "Abilitato in modo predefinito", + "Apply on all existing monitors": "Applica su tutti i monitoraggi", + "Create": "Crea", + "Clear Data": "Cancella dati", + "Events": "Eventi", + "Heartbeats": "Controlli", + "Auto Get": "Rileva", + "backupDescription": "È possibile fare il backup di tutti i monitoraggi e di tutte le notifiche in un file JSON.", + "backupDescription2": "NOTA: lo storico e i dati relativi agli eventi non saranno inclusi nel backup.", + "backupDescription3": "Dati sensibili come i token di autenticazione saranno inclusi nel backup, custodisci il file in un luogo sicuro.", + "alertNoFile": "Selezionare il file da importare.", + "alertWrongFileType": "Selezionare un file JSON.", + "Clear all statistics": "Cancella tutte le statistiche", + "Skip existing": "Ignora esistenti", + "Overwrite": "Sovrascrivi", + "Options": "Opzioni", + "Keep both": "Mantieni entrambi", + "Verify Token": "Verifica token", + "Setup 2FA": "Configurazione 2FA", + "Enable 2FA": "Abilita 2FA", + "Disable 2FA": "Disabilita 2FA", + "2FA Settings": "Gestisci 2FA", + "Two Factor Authentication": "Autenticazione a due fattori (2FA)", + "Active": "Attivata", + "Inactive": "Disattivata", + "Token": "Token", + "Show URI": "Mostra URI", + "Tags": "Etichette", + "Add New below or Select...": "Aggiungi oppure scegli…", + "Tag with this name already exist.": "Esiste già un'etichetta con questo nome.", + "Tag with this value already exist.": "Esiste già un'etichetta con questo valore.", + "color": "colore", + "value (optional)": "descrizione (opzionale)", + "Gray": "Grigio", + "Red": "Rosso", + "Orange": "Arancione", + "Green": "Verde", + "Blue": "Blu", + "Indigo": "Indaco", + "Purple": "Viola", + "Pink": "Rosa", + "Search...": "Cerca…", + "Avg. Ping": "Tempo medio di risposta al ping", + "Avg. Response": "Tempo medio di risposta", + "Entry Page": "Pagina Principale", + "statusPageNothing": "Non c'è nulla qui, aggiungi un gruppo oppure un monitoraggio.", + "No Services": "Nessun servizio", + "All Systems Operational": "Tutti i sistemi sono operativi", + "Partially Degraded Service": "Servizio parzialmente degradato", + "Degraded Service": "Servizio degradato", + "Add Group": "Aggiungi gruppo", + "Add a monitor": "Aggiungi monitoraggio", + "Edit Status Page": "Modifica pagina di stato", + "Go to Dashboard": "Vai al Cruscotto", + "Status Page": "Pagina di stato", + "Status Pages": "Pagine di stato", + "defaultNotificationName": "Notifica {notification} ({number})", + "here": "qui", + "Required": "Obbligatorio", + "telegram": "Telegram", + "Bot Token": "Token del bot", + "wayToGetTelegramToken": "Puoi ottenere il token da {0}.", + "Chat ID": "ID Chat", + "supportTelegramChatID": "Supporta ID di chat private, gruppi e canali", + "wayToGetTelegramChatID": "È possibile ricereve l'ID chat mandando un messaggio al bot e poi andando in questo URL per visualizzare il chat_id:", + "YOUR BOT TOKEN HERE": "QUI IL TOKEN DEL BOT", + "chatIDNotFound": "Non trovo l'ID chat. Prima bisogna mandare un messaggio al bot", + "webhook": "Webhook", + "Post URL": "Post URL", + "Content Type": "Content Type", + "webhookJsonDesc": "{0} va bene per qualsiasi server HTTP moderno ad esempio express.js", + "webhookFormDataDesc": "{multipart} va bene per PHP, c'è solo bisogno di analizzare il JSON con {decodeFunction}", + "smtp": "E-mail (SMTP)", + "secureOptionNone": "Nessuno / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignora gli errori TLS", + "From Email": "Mittente", + "emailCustomSubject": "Oggetto personalizzato", + "To Email": "Destinatario", + "smtpCC": "CC", + "smtpBCC": "CCn", + "discord": "Discord", + "Discord Webhook URL": "URL Webhook di Discord", + "wayToGetDiscordURL": "Puoi ottenerlo andando su Impostazioni server -> Integrazioni -> Visualizza webhook -> Nuovo webhook", + "Bot Display Name": "Nome del Bot", + "Prefix Custom Message": "Prefisso per il messaggio personalizzato", + "Hello @everyone is...": "Ciao a {'@'}everyone …", + "teams": "Microsoft Teams", + "Webhook URL": "URL Webhook", + "wayToGetTeamsURL": "È possibile imparare a creare un URL Webhook {0}.", + "signal": "Signal", + "Number": "Numero", + "Recipients": "Destinatari", + "needSignalAPI": "È necessario avere un client Signal con le API REST.", + "wayToCheckSignalURL": "Controllare questo url per capire come impostarne uno:", + "signalImportant": "IMPORTANTE: Non è possibile mischiare gruppi e numeri all'interno dei destinatari!", + "gotify": "Gotify", + "Application Token": "Token Applicazione", + "Server URL": "URL Server", + "Priority": "Priorità", + "slack": "Slack", + "Icon Emoji": "Icona Emoji", + "Channel Name": "Nome Canale", + "Uptime Kuma URL": "Indirizzo Uptime Kuma", + "aboutWebhooks": "Maggiori informazioni riguardo ai webhooks su: {0}", + "aboutChannelName": "Inserire il nome del canale nel campo \"Nome Canale\" {0} se si vuole bypassare il canale webhook. Ad esempio: #altro-canale", + "aboutKumaURL": "Se si lascia bianco il campo Indirizzo Uptime Kuma, la pagina GitHub sarà il valore predefinito.", + "emojiCheatSheet": "Lista Emoji: {0}", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Supporta più di 50 servizi di notifica)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "Chiave Utente", + "Device": "Dispositivo", + "Message Title": "Titolo Messaggio", + "Notification Sound": "Suono di Notifica", + "More info on:": "Maggiori informazioni su: {0}", + "pushoverDesc1": "Priorità di Emergenza (2) ha 30 secondi di scadenza tra un tentativo e l'altro e scadrà dopo un'ora.", + "pushoverDesc2": "Se si vuole inviare la notifica a dispositivi differenti, riempire il campo Dispositivi.", + "SMS Type": "Tipo di SMS", + "octopushTypePremium": "Premium (Veloce - raccomandato per allertare)", + "octopushTypeLowCost": "A Basso Costo (Lento - talvolta bloccato dall'operatore)", + "checkPrice": "Controlla {0} prezzi:", + "apiCredentials": "Credenziali API", + "octopushLegacyHint": "Si vuole utilizzare la vecchia versione (2011-2020) oppure la nuova versione di Octopush?", + "Check octopush prices": "Controlla i prezzi di Octopush {0}.", + "octopushPhoneNumber": "Numero di telefono (formato internazionale. Esempio: +33612345678)", + "octopushSMSSender": "Nome del mittente: 3-11 caratteri alfanumerici e spazi (a-zA-Z0-9)", + "LunaSea Device ID": "ID dispositivo LunaSea", + "Apprise URL": "URL Apprise", + "Example:": "Esempio: {0}", + "Read more:": "Maggiori informazioni: {0}", + "Status:": "Stato: {0}", + "Read more": "Maggiori informazioni", + "appriseInstalled": "Apprise è installato.", + "appriseNotInstalled": "Apprise non è installato. {0}", + "Access Token": "Token di accesso", + "Channel access token": "Token di accesso al canale", + "Line Developers Console": "Console sviluppatori Line", + "lineDevConsoleTo": "Console sviluppatori Line - {0}", + "Basic Settings": "Impostazioni Base", + "User ID": "ID Utente", + "Messaging API": "API di Messaggistica", + "wayToGetLineChannelToken": "Prima accedi a {0}, crea un provider e un canale (API di Messaggistica), dopodiché puoi avere il token di accesso e l'id utente dal menù sopra.", + "Icon URL": "URL Icona", + "aboutIconURL": "È possibile impostare un collegamento ad una immagine in \"URL Icona\" per modificare l'immagine del profilo predefinito. Non verrà utilizzata se è impostata l'Icona Emoji.", + "aboutMattermostChannelName": "È possibile modificare il canale predefinito che dove il webhook manda messaggi immettendo il nome del canale nel campo \"Nome Canale\". Questo va abilitato nelle impostazioni webhook di Mattermost webhook. P.E.: #altro-canale", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - economico, ma lento e spesso sovraccarico. Limitato solamente a destinatari Polacchi.", + "promosmsTypeFlash": "SMS FLASH - Il messaggio sarà automaticamente mostrato sul dispositivo dei destinatari. Limitato solo a destinatari Polacchi.", + "promosmsTypeFull": "SMS FULL - Premium, È possibile utilizzare il proprio nome come mittente (è necessario prima registrare il nome). Affidabile per gli allarmi.", + "promosmsTypeSpeed": "SMS SPEED - Maggior priorità. Rapido, affidabile, ma costoso (costa il doppio di SMS FULL).", + "promosmsPhoneNumber": "Numero di Telefono (per destinatari Polacchi si può omettere il codice area)", + "promosmsSMSSender": "Mittente SMS : Nome preregistrato oppure uno dei seguenti: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "URL WebHook di Feishu", + "matrixHomeserverURL": "URL Server (con http(s):// e opzionalmente la porta)", + "Internal Room Id": "ID Stanza Interna", + "matrixDesc1": "È possibile recuperare l'ID della stanza all'interno delle impostazioni avanzate della stanza nel client Matrix. Dovrebbe essere simile a !QMdRCpUIfLwsfjxye6:server.di.casa.", + "matrixDesc2": "È altamente raccomandata la creazione di un nuovo utente e di non utilizare il proprio token di accesso Matrix poiché darà pieno controllo al proprio account e a tutte le stanze in cui si ha accesso. Piuttosto, si crei un nuovo utente per invitarlo nella stanza dove si vuole ricevere le notifiche. Si può accedere al token eseguendo {0}", + "Method": "Metodo", + "Body": "Corpo", + "Headers": "Intestazioni", + "PushUrl": "URL di Push", + "HeadersInvalidFormat": "L'intestazione di richiesta non è un JSON valido: ", + "BodyInvalidFormat": "Il corpo di richiesta non è un JSON valido: ", + "Monitor History": "Storico monitoraggio", + "clearDataOlderThan": "Mantieni lo storico per {0} giorni.", + "PasswordsDoNotMatch": "Le password non corrispondono.", + "records": "valori", + "One record": "Un record", + "steamApiKeyDescription": "Per monitorare un server di gioco Steam è necessaria una chiave Web-API di Steam. È possibile registrarne una qui: ", + "Current User": "Utente corrente", + "recent": "Recenti", + "Done": "Fatto", + "Info": "Info", + "Security": "Sicurezza", + "Steam API Key": "Chiave API di Steam", + "Shrink Database": "Comprimi database", + "Pick a RR-Type...": "Scegli un tipo di RR…", + "Pick Accepted Status Codes...": "Scegli i codici di Stato Accettati…", + "Default": "Predefinito", + "HTTP Options": "Opzioni HTTP", + "Create Incident": "Segnala incidente", + "Title": "Titolo", + "Content": "Contenuto", + "Style": "Stile", + "info": "informativo", + "warning": "attenzione", + "danger": "critico", + "primary": "predefinito", + "light": "chiaro", + "dark": "scuro", + "Post": "Posta", + "Please input title and content": "Inserire il titolo e il contenuto", + "Created": "Creato", + "Last Updated": "Ultima modifica", + "Unpin": "Rimuovi", + "Switch to Light Theme": "Utilizza il tema chiaro", + "Switch to Dark Theme": "Utilizza il tema scuro", + "Show Tags": "Mostra etichette", + "Hide Tags": "Nascondi etichette", + "Description": "Descrizione", + "No monitors available.": "Nessun monitoraggio disponibile.", + "Add one": "Aggiungine uno", + "No Monitors": "Nessun monitoraggio presente", + "Untitled Group": "Gruppo senza titolo", + "Services": "Servizi", + "Discard": "Scarta modifiche", + "Cancel": "Annulla", + "Powered by": "Offerto da", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "Nome utente API (incl. prefisso webapi_)", + "serwersmsAPIPassword": "Password API", + "serwersmsPhoneNumber": "Numero di Telefono", + "serwersmsSenderName": "Nome del mittente SMS (registrato via portale cliente)", + "stackfield": "Stackfield", + "smtpDkimSettings": "Impostazioni DKIM", + "smtpDkimDesc": "Fare riferimento a Nodemailer DKIM {0} per l'utilizzo.", + "documentation": "documentazione", + "smtpDkimDomain": "Dominio", + "smtpDkimKeySelector": "Selettore Chiave", + "smtpDkimPrivateKey": "Chiave Privata", + "smtpDkimHashAlgo": "Algoritmo di hashing (opzionale)", + "smtpDkimheaderFieldNames": "Campi Intestazione da firmare (opzionale)", + "smtpDkimskipFields": "Campi Intestazione da non firmare (opzionale)", + "GoogleChat": "Google Chat (solo per Google Workspace)", + "Help": "Aiuto", + "Maintenance": "Manutenzione", + "statusMaintenance": "In manutenzione", + "General Monitor Type": "Monitoraggio Generico", + "Game": "Gioco", + "Passive Monitor Type": "Monitoraggio Passivo", + "Specific Monitor Type": "Monitoraggio Specifico", + "Monitor": "Monitoraggio | Monitoraggi", + "Topic": "Argomento", + "markdownSupported": "Sintassi Markdown supportata. Se utilizzi HTML, evita spazi iniziali per prevenire problemi di formattazione.", + "Proxy Server": "Server Proxy", + "Select status pages...": "Seleziona pagine di stato…", + "Schedule maintenance": "Pianifica manutenzione", + "Start of maintenance": "Inizio della manutenzione", + "All Status Pages": "Tutte le pagine di stato", + "webhookAdditionalHeadersTitle": "Headers aggiuntivi", + "resendEveryXTimes": "Reinvia ogni {0} volte", + "resendDisabled": "Reinvio disabilitato", + "Resend Notification if Down X times consequently": "Reinvia la notifica se Down X volte di seguito", + "Add New Status Page": "Aggiungi nuova pagina di stato", + "webhookAdditionalHeadersDesc": "Imposta intestazioni aggiuntive inviate con il webhook. Ogni intestazione deve essere definita come chiave/valore JSON.", + "topicExplanation": "Argomento MQTT da monitorare", + "successMessage": "Messaggio con successo", + "successMessageExplanation": "Messaggio MQTT considerato come successo", + "error": "errore", + "critical": "critico", + "Customize": "Personalizza", + "Custom Footer": "Piè di pagina personalizzato", + "Custom CSS": "CSS personalizzato", + "deleteStatusPageMsg": "Confermi l'eliminazione di questa pagina di stato?", + "default": "Predefinito", + "enabled": "Abilitato", + "setAsDefault": "Imposta come predefinito", + "deleteProxyMsg": "Confermi l'eliminazione di questo proxy per tutti i monitoraggi?", + "proxyDescription": "I proxy devono essere assegnati ad un monitoraggio per essere operativi.", + "setAsDefaultProxyDescription": "Questo proxy sarà abilitato come predefinito per tutti i nuovi monitoraggi. E' possibile disabilitare il proxy in modo indipendente per ogni singolo monitoraggio.", + "Certificate Chain": "Catena di certificati", + "Invalid": "Non valido", + "User": "Utente", + "Installed": "Installato", + "Not installed": "Non installato", + "Running": "In esecuzione", + "Not running": "Fermo", + "Remove Token": "Rimuovere token", + "Start": "Avvio", + "Next": "Prossimo", + "No Proxy": "Nessun proxy", + "Authentication": "Autenticazione", + "New Status Page": "Nuova pagina di stato", + "Page Not Found": "Pagina non trovata", + "Affected Monitors": "Monitoraggi interessati", + "Pick Affected Monitors...": "Seleziona i monitoraggi interessati…", + "Valid": "Valido", + "Certificate Expiry Notification": "Notifica scadenza certificato", + "styleElapsedTimeShowWithLine": "Mostra (con linea)", + "webhookBodyPresetOption": "Predefinito - {0}", + "webhookBodyCustomOption": "Corpo personalizzato", + "topic": "Argomento", + "selectedMonitorCount": "Selezionato: {0}", + "Check/Uncheck": "Seleziona/Deseleziona", + "Proxies": "Proxy", + "Stop": "Fermare", + "startOrEndWithOnly": "Inizia o termina solo con {0}", + "No consecutive dashes": "Nessun trattino consecutivo", + "HTTP Basic Auth": "Autenticazione di base HTTP", + "Reverse Proxy": "Proxy inverso", + "Backup": "Backup", + "About": "Info", + "wayToGetCloudflaredURL": "(Scarica cloudflared da {0})", + "cloudflareWebsite": "Sito web di Cloudflare", + "Message:": "Messaggio:", + "Don't know how to get the token? Please read the guide:": "Non sai come ottenere il token? Per favore leggi la guida:", + "Trust Proxy": "Proxy di fiducia", + "Other Software": "Altro Software", + "For example: nginx, Apache and Traefik.": "Ad esempio: nginx, Apache e Traefik.", + "Please read": "Per favore leggi", + "Subject:": "Soggetto:", + "Valid To:": "Valido per:", + "Days Remaining:": "Giorni rimanenti:", + "Issuer:": "Emittente:", + "Fingerprint:": "Impronta digitale:", + "No status pages": "Nessuna pagina di stato", + "Domain Name Expiry Notification": "Notifica di scadenza del nome di dominio", + "Date Created": "Data di creazione", + "Slug": "Slug", + "Show Powered By": "Mostra \"Offerto da\"", + "Domain Names": "Nomi di dominio", + "signedInDispDisabled": "Autenticazione disabilitata.", + "RadiusSecret": "Segreto di Radius", + "RadiusCalledStationId": "Identificativo della stazione chiamata", + "RadiusCallingStationId": "Id stazione chiamante", + "RadiusCallingStationIdDescription": "Identificativo del dispositivo chiamante", + "API Username": "Nome utente dell'API", + "API Key": "Chiave API", + "Show update if available": "Mostra aggiornamento se disponibile", + "RadiusSecretDescription": "Segreto condiviso tra client e server", + "Also check beta release": "Controlla anche la versione beta", + "Check how to config it for WebSocket": "Controlla come configurarlo per WebSocket", + "Steam Game Server": "Server di gioco Steam", + "Most likely causes:": "Cause più probabili:", + "The resource is no longer available.": "La risorsa non è più disponibile.", + "What you can try:": "Cosa puoi provare:", + "Retype the address.": "Ridigita l'indirizzo.", + "Go back to the previous page.": "Torna alla pagina precedente.", + "Coming Soon": "Prossimamente", + "Connection String": "Stringa di connessione", + "Query": "Richiesta", + "settingsCertificateExpiry": "Scadenza certificato TLS", + "deleteDockerHostMsg": "Sei sicuro di voler eliminare questo sistema docker per tutti i monitoraggi?", + "tcp": "TCP / HTTP", + "Docker Container": "Contenitore Docker", + "Container Name / ID": "Nome/ID contenitore", + "Docker Host": "Sistema Docker", + "Docker Hosts": "Sistema Docker", + "Domain": "Dominio", + "Workstation": "Postazione di lavoro", + "Packet Size": "Dimensione del pacchetto", + "Setup Docker Host": "Configura il sistema Docker", + "telegramSendSilently": "Invia silenziosamente", + "telegramSendSilentlyDescription": "Invia il messaggio in silenzio. Gli utenti riceveranno una notifica senza audio.", + "telegramProtectContent": "Proteggi inoltro/salvataggio", + "disableCloudflaredNoAuthMsg": "Sei in modalità No Auth, non è richiesta una password.", + "wayToGetLineNotifyToken": "Puoi ottenere un token di accesso da {0}", + "Examples": "Esempi", + "Long-Lived Access Token": "Token di accesso di lunga durata", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Il token di accesso di lunga durata può essere creato facendo clic sul nome del tuo profilo (in basso a sinistra) e scorrendo verso il basso, quindi fai clic su Crea token.", + "Notification Service": "Servizio di notifica", + "default: notify all devices": "predefinito: notifica a tutti i dispositivi", + "Automations can optionally be triggered in Home Assistant:": "Le automazioni possono essere facoltativamente attivate in Home Assistant:", + "Trigger type:": "Tipo di attivazione:", + "Event type:": "Tipo di evento:", + "Event data:": "Dati dell'evento:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Quindi scegli un'azione, ad esempio cambia la scena in cui una luce RGB è rossa.", + "Frontend Version": "Versione front-end", + "Frontend Version do not match backend version!": "La versione del frontend non corrisponde alla versione del backend!", + "backupOutdatedWarning": "Deprecato: poiché sono state aggiunte molte funzionalità e questa funzionalità di backup è un po' trascurata, non può generare o ripristinare un backup completo.", + "backupRecommend": "Esegui invece il backup diretto del volume o della cartella dei dati (./data/).", + "Optional": "Opzionale", + "sameAsServerTimezone": "Uguale al fuso orario del server", + "startDateTime": "Data/ora di inizio", + "endDateTime": "Data/ora di fine", + "cronExpression": "Espressione Cron", + "cronSchedule": "Programma: ", + "recurringInterval": "Intervallo", + "Recurring": "Ricorrente", + "strategyManual": "Attivo/Inattivo manualmente", + "warningTimezone": "Sta usando il fuso orario del server", + "weekdayShortMon": "Lun", + "weekdayShortTue": "Mar", + "weekdayShortWed": "Mer", + "weekdayShortThu": "Gio", + "weekdayShortSat": "Sab", + "weekdayShortSun": "Dom", + "dayOfWeek": "Giorno della settimana", + "dayOfMonth": "Giorno del mese", + "lastDay": "Ultimo giorno", + "lastDay1": "Ultimo giorno del mese", + "lastDay3": "3° ultimo giorno del mese", + "lastDay4": "4° ultimo giorno del mese", + "No Maintenance": "Nessuna manutenzione", + "pauseMaintenanceMsg": "Sei sicuro di voler mettere in pausa?", + "maintenanceStatus-inactive": "Inattivo", + "maintenanceStatus-scheduled": "Programmato", + "maintenanceStatus-ended": "Conclusa", + "maintenanceStatus-unknown": "Sconosciuto", + "Display Timezone": "Mostra fuso orario", + "Server Timezone": "Fuso orario del server", + "statusPageMaintenanceEndDate": "Fine", + "IconUrl": "URL dell'icona", + "Enable DNS Cache": "(Obsoleto) Abilita la cache DNS per i monitoraggi HTTP(s)", + "Enable": "Abilitare", + "Disable": "Disattivare", + "chromeExecutableAutoDetect": "Trova automaticamente", + "dnsCacheDescription": "Potrebbe non funzionare in alcuni ambienti IPv6, disabilitalo in caso di problemi.", + "Single Maintenance Window": "Singola finestra di manutenzione", + "Maintenance Time Window of a Day": "Finestra temporale di manutenzione di un giorno", + "Effective Date Range": "Intervallo di date effettivo (facoltativo)", + "Schedule Maintenance": "Pianificare la manutenzione", + "Edit Maintenance": "Modifica Manutenzione", + "Date and Time": "Data e ora", + "DateTime Range": "Intervallo DataOra", + "loadingError": "Impossibile recuperare i dati, riprova più tardi.", + "plugin": "Plug-in | Plugin", + "install": "Installare", + "installing": "Installazione", + "uninstall": "Disinstalla", + "confirmUninstallPlugin": "Sei sicuro di voler disinstallare questo plugin?", + "notificationRegional": "Regionale", + "Clone": "Duplica", + "cloneOf": "Clone di {0}", + "wayToGetZohoCliqURL": "Puoi scoprire come creare un URL webhook {0}.", + "dataRetentionTimeError": "Il periodo di conservazione deve essere pari o superiore a 0", + "infiniteRetention": "Impostare su 0 per la conservazione infinita.", + "enableGRPCTls": "Consenti l'invio di richieste gRPC con connessione TLS", + "grpcMethodDescription": "Il nome del metodo viene convertito nel formato camelCase come sayHello, check, ecc.", + "styleElapsedTimeShowNoLine": "Mostra (nessuna riga)", + "Add New Tag": "Aggiungi nuova etichetta", + "webhookCustomBodyDesc": "Definire un corpo HTTP personalizzato per la richiesta. Le variabili modello {msg}, {heartbeat}, {monitor} sono accettate.", + "Select": "Selezionare", + "Accept characters:": "Accetta caratteri:", + "The slug is already taken. Please choose another slug.": "La lumaca è già slug. Scegli un'altra slug.", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "La connessione corrente potrebbe andare persa se ti stai connettendo tramite Cloudflare Tunnel. Sei sicuro di volerlo fermare? Digita la tua password attuale per confermarla.", + "Footer Text": "Testo piè di pagina", + "signedInDisp": "Accesso eseguito come {0}", + "RadiusCalledStationIdDescription": "Identificativo del dispositivo chiamato", + "Clone Monitor": "Clona Monitoraggio", + "styleElapsedTime": "Tempo trascorso sotto la barra del battito cardiaco", + "enableProxyDescription": "Questo proxy non avrà effetto sulle richieste di monitoraggio fino a quando non viene attivato. È possibile controllare la disabilitazione temporanea del proxy da tutti i monitoraggi in base allo stato di attivazione.", + "Using a Reverse Proxy?": "Utilizzare un proxy inverso?", + "There might be a typing error in the address.": "Potrebbe esserci un errore di battitura nell'indirizzo.", + "certificationExpiryDescription": "HTTPS monitora la notifica di attivazione quando il certificato TLS scade tra:", + "tailscalePingWarning": "Per utilizzare il monitoraggio Tailscale Ping, è necessario installare Uptime Kuma senza Docker e installare anche il client Tailscale sul server.", + "telegramMessageThreadID": "(Facoltativo) ID thread messaggio", + "telegramMessageThreadIDDescription": "Facoltativo Identificatore univoco per il thread del messaggio di destinazione (argomento) del forum; solo per i supergruppi del forum", + "telegramProtectContentDescription": "Se abilitato, i messaggi del bot in Telegram saranno protetti dall'inoltro e dal salvataggio.", + "trustProxyDescription": "Fidati delle intestazioni 'X-Forwarded-*'. Se vuoi ottenere l'IP client corretto e il tuo Uptime Kuma è dietro un proxy come Nginx o Apache, dovresti abilitarlo.", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Un elenco di servizi di notifica è disponibile in Home Assistant in \"Strumenti per sviluppatori> Servizi\" cerca \"notifica\" per trovare il nome del tuo dispositivo/telefono.", + "invalidCronExpression": "Espressione Cron non valida: {0}", + "lastDay2": "2° ultimo giorno del mese", + "maintenanceStatus-under-maintenance": "In manutenzione", + "chromeExecutable": "Eseguibile Chrome/Chromium", + "chromeExecutableDescription": "Per gli utenti Docker, se Chromium non è ancora installato, potrebbero essere necessari alcuni minuti per l'installazione e la visualizzazione del risultato del test. Richiede 1 GB di spazio su disco.", + "uninstalling": "Disinstallazione", + "confirmDeleteTagMsg": "Sei sicuro di voler eliminare questa etichetta? I monitoraggi associati a questa etichetta non saranno eliminati.", + "Request Timeout": "Richiedi timeout", + "timeoutAfter": "Timeout dopo {0} secondi", + "Resend Notification if Down X times consecutively": "Invia di nuovo la notifica se spento X volte consecutivamente", + "Proxy": "Proxy", + "or": "o", + "statusPageRefreshIn": "Aggiorna tra: {0}", + "HTTP Headers": "Intestazioni HTTP", + "Custom": "Personalizzato", + "Connection Type": "Tipo di connessione", + "Docker Daemon": "Deamon Docker", + "socket": "Socket", + "Home Assistant URL": "URL dell'assistente domestico", + "weekdayShortFri": "Ven", + "Home": "Accoglienza", + "Invert Keyword": "Inverti parola chiave", + "filterActive": "Attiva", + "filterActivePaused": "In pausa", + "Cannot connect to the socket server": "Impossibile connettersi al server socket", + "Reconnecting...": "Riconnessione...", + "Expected Value": "Valore atteso", + "Json Query": "Query Json", + "deleteMaintenanceMsg": "Sei sicuro di voler eliminare questa attività di manutenzione?", + "dnsPortDescription": "Porta server DNS. Default 53. Puoi cambiare questa porta in ogni momento.", + "setupDatabaseChooseDatabase": "Quale database vuoi usare?", + "setupDatabaseMariaDB": "Connettiti ad un database (MariaDB) esterno. Dovrai impostare i parametri di connessione.", + "setupDatabaseSQLite": "Un semplice file di database, consigliato per distribuzioni su piccola scala. Prima della versione 2.0.0, Uptime Kuma utilizzava SQLite come database predefinito.", + "Reset Token": "Reimposta Token", + "enableNSCD": "Abilita NSCD (Name Service Cache Daemon) per abilitare la cache su tutte le richieste DNS", + "recurringIntervalMessage": "Esegui una volta al giorno | Esegui una volta ogni {0} giorni", + "affectedMonitorsDescription": "Seleziona i monitoraggi che sono influenzati da questa manutenzione", + "For safety, must use secret key": "Per sicurezza, devi usare una chiave segreta", + "Proxy server has authentication": "Il server Proxy ha una autenticazione", + "smseaglePriority": "Priorità messaggio (0-9, massima priorità = 9)", + "pushViewCode": "Come usare il monitoraggio Push? (visualizza codice)", + "affectedStatusPages": "Mostra il messaggio di manutenzione sulle pagine di stato selezionate", + "atLeastOneMonitor": "Seleziona almeno un monitoraggio", + "endpoint": "endpoint", + "pushyAPIKey": "Segreto chiave API", + "pushyToken": "Token dispositivo", + "wayToGetKookBotToken": "Crea un applicazione e prendi nota del tuo token su {0}", + "Device Token": "Token dispositivo", + "Platform": "Piattaforma", + "Huawei": "Huawei", + "High": "Alto", + "Retry": "Riprova", + "WeCom Bot Key": "Chiave WeCom Bot", + "Proxy Protocol": "Protocollo Proxy", + "Setup Proxy": "Configura proxy", + "Notify Channel": "Canale di notifica", + "Integration Key": "Chiave di integrazione", + "Integration URL": "URL integrazione", + "do nothing": "non fare nulla", + "auto resolve": "risolvi automaticamente", + "alertaApiEndpoint": "Endpoint API", + "alertaEnvironment": "Ambiente", + "alertaApiKey": "Chiave API", + "alertaAlertState": "Stato Alert", + "smseagleTo": "Numer(i) di telefono", + "smseagleRecipientType": "Tipo destinatario", + "smseagleToken": "Token di accesso API", + "smseagleEncoding": "Invia come Unicode (predefinito=GSM-7)", + "Recipient Number": "Numero destinatario", + "From Name/Number": "Da Nome/Numero", + "Leave blank to use a shared sender number.": "Lascia vuoto per usare un numero condiviso.", + "onebotGroupMessage": "Gruppo", + "onebotUserOrGroupId": "Gruppo/ID utente", + "Badge Color": "Colore insegna", + "Monitor Group": "Gruppo monitoraggio", + "setupDatabaseEmbeddedMariaDB": "Non devi impostare niente. Questa immagine Docker include un database MariaDB già configurato e pronto all'uso. Uptime Kuma si collegherà a questo database attraverso il socket Unix.", + "dbName": "Nome Database", + "Group": "Gruppo", + "pushOthers": "Altro", + "programmingLanguages": "Linguaggi di programmazione", + "promosmsAllowLongSMS": "Permetti SMS lunghi", + "liquidIntroduction": "La modellizzabilità è ottenuta attraverso il linguaggio di modellazione Liquid. Consulta {0} per le istruzioni d'uso. Queste sono le variabili disponibili.", + "templateMsg": "Messaggio della notifica", + "Search monitored sites": "Ricerca tra i siti monitorati", + "settingUpDatabaseMSG": "Configurazione del database in corso. Potrebbe richiedere un po' di tempo...", + "templateHeartbeatJSON": "oggetto che descrive il controllo", + "templateLimitedToUpDownNotifications": "disponibile solo per le notifiche ATTIVO/SPENTO", + "Add a new expiry notification day": "Aggiungi un nuovo giorno di notifica di scadenza", + "DockerHostRequired": "Per favore, imposta il sistema Docker per questo monitoraggio.", + "smtpLiquidIntroduction": "I due campi seguenti sono adattabili tramite il linguaggio di modellazione Liquid. Consulta {0} per le istruzioni d'uso. Queste sono le variabili disponibili:", + "emailCustomisableContent": "Contenuto personalizzabile", + "noDockerHostMsg": "Non disponibile. Configura prima un sistema Docker.", + "Remove the expiry notification": "Rimuovi il giorno di notifica di scadenza", + "emailTemplateHeartbeatJSON": "oggetto che descrive il controllo", + "leave blank for default subject": "Lasciare vuoto per oggetto predefinito", + "emailCustomBody": "Messaggio personalizzato", + "Select message type": "Seleziona il tipo di messaggio", + "Send to channel": "Invia al canale", + "Create new forum post": "Crea un nuovo post sul forum", + "postToExistingThread": "Scrivi in una conversazione / post esistente", + "forumPostName": "Nome forum del post", + "threadForumPostID": "ID della conversazione / Forum del post", + "e.g. {discordThreadID}": "es. {discordThreadID}", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Inserisci il nome di sistema del server a cui ti vuoi connettere, o {localhost} se vuoi usare {local_mta}", + "templateLimitedToUpDownCertNotifications": "disponibile solo per le notifiche ATTIVO/SPENTO/Scadenza certificato", + "emailTemplateMsg": "Messaggio della notifica", + "emailTemplateLimitedToUpDownNotification": "disponibile solo per controlli ATTIVO/SPENTO, altrimenti nullo", + "Refresh Interval": "Intervallo di aggiornamento", + "Refresh Interval Description": "La pagina di stato si aggiornerà completamente ogni {0} secondi", + "emailTemplateServiceName": "Nome servizio", + "leave blank for default body": "Lasciare vuoto per messaggio predefinito", + "emailTemplateHostnameOrURL": "Nome host o URL", + "emailTemplateStatus": "Stato", + "ignoreTLSErrorGeneral": "Ignora gli errori TLS/SSL per la connessione", + "statusPageSpecialSlugDesc": "Slug speciale {0}: questa pagina verrà mostrata quando non viene fornito alcuno slug", + "successKeywordExplanation": "Parola chiave MQTT che verrà considerata come corretta", + "signl4Docs": "Puoi trovare maggiori informazioni su come configurare SIGNL4 e su come ottenere l'URL del webhook SIGNL4 in {0}.", + "contains": "contiene", + "not contains": "non contiene", + "starts with": "inizia con", + "equals": "uguale", + "not equals": "non uguale", + "less than": "meno di", + "greater than": "maggiore di", + "successKeyword": "Parola chiave corretta", + "templateMonitorJSON": "oggetto che descrive il monitoraggio", + "Cannot connect to the socket server.": "Impossibile connettersi al server socket.", + "wayToGetDiscordThreadId": "Ottenere l'ID di un thread/post del forum è simile a ottenere un ID di canale. Scopri di più su come ottenere gli ID {0}", + "pushoversounds pushover": "Pushover (predefinito)", + "pushoversounds none": "Nessuno (silenzioso)", + "Don't mention people": "Non menzionare le persone", + "Mention group": "Menzionare {group}", + "Host URL": "URL del sistema", + "emailTemplateMonitorJSON": "oggetto che descrive il monitoraggio", + "Channel access token (Long-lived)": "Token di accesso al canale (di lunga durata)", + "Your User ID": "Il tuo ID utente", + "pushoverMessageTtl": "Messaggio TTL (secondi)", + "SendKey": "SendKey", + "Sms template must contain parameters: ": "Il modello SMS deve contenere i parametri: ", + "Bark API Version": "Versione API Bark", + "Bark Group": "Gruppo Bark", + "Bark Endpoint": "Endpoint Bark", + "Bark Sound": "Suono Bark", + "aboutNotifyChannel": "Notify channel attiverà una notifica desktop o mobile per tutti i membri del canale, indipendentemente dal fatto che la loro disponibilità sia impostata su attivo o assente.", + "promosmsPassword": "Password API", + "You can divide numbers with": "Puoi dividere i numeri con", + "Base URL": "URL base", + "-year": "-anno", + "now": "ora", + "time ago": "fa", + "Json Query Expression": "Espressione di query JSON", + "and": "e", + "whatHappensAtForumPost": "Crea un nuovo post sul forum. Questo NON pubblica messaggi in post esistenti. Per pubblicare in un post esistente utilizzare \"{option}\"", + "invertKeywordDescription": "Cerca la parola chiave essere assente anziché presente.", + "octopushAPIKey": "\"Chiave API\" dalle credenziali API HTTP nel pannello di controllo", + "Enable TLS": "Abilita TLS", + "ignoredTLSError": "Ignora errori TLS/SSL", + "templateHostnameOrURL": "nome sistema o URL", + "templateStatus": "stato", + "templateServiceName": "nome del servizio", + "locally configured mail transfer agent": "agente mail configurato localmente", + "shrinkDatabaseDescriptionSqlite": "Attiva il database {vacuum} per SQLite. {auto_vacuum} è già abilitato, ma non deframmenta il database né ricomprime le singole pagine del database come fa il comando {vacuum}.", + "pushoversounds cashregister": "Registratore di cassa", + "Strategy": "Strategia", + "Add a domain": "Aggiungi un dominio", + "telegramServerUrl": "(Facoltativo) URL del Server", + "pushoversounds magic": "Magico", + "pushoversounds mechanical": "Meccanico", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Sirena", + "pushoversounds spacealarm": "Allarme spaziale", + "pushoversounds alien": "Allarme Alieno (lungo)", + "Remove domain": "Rimuovi il dominio '{0}'", + "Edit Tag": "Modifica etichetta", + "Server Address": "Indirizzo del Server", + "Expiry": "Scadenza", + "telegramUseTemplateDescription": "Se abilitato, il messaggio sarà spedito usando il modello personalizzato.", + "high": "alto", + "jsonQueryDescription": "Analizza ed estrai dati specifici dalla risposta JSON del server utilizzando una query JSON oppure usa \"$\" per la risposta grezza, se non ti aspetti JSON. Il risultato viene quindi confrontato con il valore previsto, sotto forma di stringhe. Consulta {0} per la documentazione e usa {1} per sperimentare con le query.", + "Free Mobile User Identifier": "Identificatore utente mobile gratuito", + "telegramServerUrlDescription": "Per rimuovere le limitazioni dell'API bot di Telegram o ottenere l'accesso in aree bloccate (Cina, Iran, ecc.), clicca su {0} per maggiori informazioni. Predefinito: {1}", + "octopushLogin": "\"Accedi\" dalle credenziali API HTTP nel pannello di controllo", + "promosmsLogin": "Nome di accesso API", + "pushoversounds bike": "Bicicletta", + "pushoversounds bugle": "Tromba", + "pushoversounds classical": "Classico", + "pushoversounds cosmic": "Cosmico", + "pushoversounds incoming": "In arrivo", + "pushoversounds intermission": "Intervallo", + "pushoversounds tugboat": "Rimorchiatore", + "pushoversounds climb": "Salita (lunga)", + "pushoversounds persistent": "Persistente (lungo)", + "pushoversounds vibrate": "Solo vibrazione", + "wayToGetKookGuildID": "Attiva la 'Modalità sviluppatore' nelle impostazioni di Kook e fai clic con il pulsante destro del mouse sul gruppo per ottenere il suo ID", + "Guild ID": "ID gruppo", + "Free Mobile API Key": "Chiave API mobile gratuita", + "telegramUseTemplate": "Usa un modello di messaggio personalizzato", + "telegramTemplateFormatDescription": "Telegram permette l'utilizzo di diversi linguaggi di markup, vedi Telegram {0} per maggiori dettagli.", + "Add Tags": "Aggiungi Etichette", + "Mentioning": "Menzione", + "Clear All Events": "Cancella tutti gli eventi", + "clearAllEventsMsg": "Sei sicuro di voler eliminare tutti gli eventi per tutti i monitoraggio? Questa operazione rimuove definitivamente i dati dei messaggi associati e non può essere annullata.", + "Events cleared successfully": "Eventi cancellati con successo.", + "No monitors found": "Nessun monitoraggio trovato.", + "Could not clear events": "Impossibile cancellare {falliti} di {totali} eventi", + "Proto Method": "Metodo Proto", + "Proto Content": "Contenuto Proto", + "Economy": "Economico", + "Lowcost": "Basso costo", + "SMSManager API Docs": "Documentazione API di SMSManager", + "Gateway Type": "Tipo di gateway", + "goAlertIntegrationKeyInfo": "Ottieni la chiave di integrazione API generica per il servizio in questo formato \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\", solitamente il valore del parametro token dell'URL copiato.", + "mqttWebsocketPathInvalid": "Per favore utilizza un formato di percorso WebSocket valido", + "mqttHostnameTip": "Per favore utilizza questo formato {hostnameFormat}", + "pushoversounds echo": "Pushover Echo (lungo)", + "pushoversounds updown": "Attivo Spento (lungo)", + "defaultFriendlyName": "Nuovo Monitoraggio", + "Path": "Percorso", + "mqttWebSocketPath": "Percorso WebSocket MQTT", + "mqttWebsocketPathExplanation": "Percorso WebSocket per MQTT su connessioni WebSocket (ad esempio, /mqtt)", + "Use HTML for custom E-mail body": "Utilizza HTML per il contenuto personalizzato dell'email", + "pushoversounds falling": "Cadente", + "pushoversounds gamelan": "Gamelan", + "Template plain text instead of using cards": "Utilizza testo semplice invece di utilizzare le schede", + "issueWithGoogleChatOnAndroidHelptext": "Ciò consente anche di aggirare bug a monte come {issuetackerURL}", + "Proto Service Name": "Nome servizio Proto", + "goAlertInfo": "GoAlert è un'applicazione open source per la pianificazione delle chiamate in reperibilità, l'escalation automatica e le notifiche (come SMS o chiamate vocali). Coinvolgi automaticamente la persona giusta, nel modo giusto e al momento giusto! {0}", + "AccessKeyId": "ID AccessKey", + "SecretKey": "Chiave segreta", + "tagAlreadyOnMonitor": "Questa etichetta (nome e valore) è già presente sul monitoraggio o è in attesa di aggiunta.", + "tagAlreadyStaged": "Questa etichetta (nome e valore) è già pronta per questo batch.", + "tagNameExists": "Esiste già un'etichetta di sistema con questo nome. Selezionala dall'elenco o usa un nome diverso.", + "SecretAccessKey": "Segreto di AccessKey", + "PhoneNumbers": "Numeri di telefono", + "TemplateCode": "Codice modello", + "SignName": "Firma", + "WebHookUrl": "URL del WebHook", + "Add Another Tag": "Aggiungi altra etichetta", + "aboutSlackUsername": "Modifica il nome visualizzato del mittente del messaggio. Se vuoi menzionare qualcuno, includilo nel nome descrittivo.", + "Body Encoding": "Codifica del corpo", + "disableAPIKeyMsg": "Sei sicuro di voler disabilitare questa chiave API?", + "Badge Pending Color": "Colore insegna In attesa", + "monitorToastMessagesDescription": "Le notifiche toast per i monitoraggi scompaiono dopo un determinato periodo di tempo in secondi. Impostando -1 si disabilita la scadenza. Impostando 0 si disabilitano le notifiche toast.", + "gamedigGuessPort": "Gamedig: indovina la porta", + "gamedigGuessPortDescription": "La porta utilizzata dal protocollo di query del server Valve potrebbe essere diversa dalla porta del client. Prova questa soluzione se il monitoraggio non riesce a connettersi al server.", + "gtxMessagingFromHint": "Sui telefoni cellulari, i destinatari vedranno il TPOA visualizzato come mittente del messaggio. Sono consentiti fino a 11 caratteri alfanumerici, un codice breve, il codice lungo locale o numeri internazionali ({e164}, {e212} o {e214})", + "cellsyntOriginatortypeNumeric": "Valore numerico (massimo 15 cifre) con numero di telefono in formato internazionale senza 00 iniziale (ad esempio, il numero del Regno Unito 07920 110 000 dovrebbe essere impostato come 447920110000). I destinatari possono rispondere al messaggio.", + "max 15 digits": "massimo 15 cifre", + "apiKeysDisabledMsg": "Le chiavi API sono disabilitate perché l'autenticazione è disabilitata.", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Le notifiche urgenti verranno recapitate immediatamente, anche se il dispositivo è in modalità Non disturbare.", + "Monitor Setting": "Impostazione del monitoraggio di {0}", + "Show Clickable Link Description": "Se selezionato, chiunque abbia accesso a questa pagina di stato potrà accedere all'URL del monitoraggio.", + "monitorToastMessagesLabel": "Monitora le notifiche Toast", + "noGroupMonitorMsg": "Non disponibile. Crea prima un monitoraggio di gruppo.", + "customUrlDescription": "Verrà utilizzato come URL cliccabile al posto di quello del monitoraggio.", + "twilioAuthToken": "Token di autenticazione / Chiave API segreta", + "authUserInactiveOrDeleted": "L'utente è inattivo o eliminato.", + "pingNumericDescription": "Se selezionato, verranno visualizzati gli indirizzi IP al posto dei nomi di sistema simbolici", + "ntfyPriorityHelptextPriorityHigherThanDown": "La priorità normale dovrebbe essere superiore alla priorità {0}. La priorità {1} è superiore alla priorità {0} {2}", + "Badge Maintenance Color": "Colore insegna Manutenzione", + "wayToGetPagerDutyKey": "Puoi ottenerlo andando su Servizio -> Directory Servizi -> (Seleziona un servizio) -> Integrazioni -> Aggiungi integrazione. Qui puoi cercare \"Eventi API V2\". Ulteriori informazioni {0}", + "smspartnerApiurl": "Puoi trovare la tua chiave API nel Cruscotto all'indirizzo {0}", + "smspartnerPhoneNumberHelptext": "Il numero deve essere nel formato internazionale {0}, {1}. I numeri multipli devono essere separati da {2}", + "onebotSafetyTips": "Per sicurezza, è necessario impostare il token di accesso", + "Continue": "Continua", + "Learn More": "Maggiori info", + "ntfyPriorityDown": "Priorità per gli eventi SPENTO", + "gtxMessagingToHint": "Formato internazionale, con \"+\" iniziale ({e164}, {e212} o {e214})", + "setup a new monitor group": "impostare un nuovo gruppo di monitoraggio", + "openModalTo": "apri modale a {0}", + "Auto resolve or acknowledged": "Risoluzione automatica o confermata", + "auto acknowledged": "riconosciuto automaticamente", + "alertaRecoverState": "Stato ripristino", + "smseagleApiv2": "APIv2 (consigliata per nuove integrazioni)", + "smseagleDocs": "Verifica la documentazione o la disponibilità dell'APIv2: {0}", + "smseagleComma": "I multipli devono essere separati da virgola", + "smspartnerPhoneNumber": "Numero/i di telefono", + "pagertreeMedium": "Medio", + "Custom Monitor Type": "Tipo di monitoraggio personalizzato", + "SpugPush Template Code": "Codice modello", + "deleteAPIKeyMsg": "Sei sicuro di voler eliminare questa chiave API?", + "smspartnerSenderName": "Nome del mittente dell'SMS", + "smspartnerSenderNameInfo": "Deve essere compreso tra 3..=11 caratteri regolari", + "Octopush API Version": "Versione API Octopush", + "Legacy Octopush-DM": "Octopush-DM (legacy)", + "ntfy Topic": "Argomento ntfy", + "Server URL should not contain the nfty topic": "L'URL del server non deve contenere l'argomento nfty", + "onebotHttpAddress": "Indirizzo HTTP OneBot", + "onebotMessageType": "Tipo di messaggio OneBot", + "onebotPrivateMessage": "Privato", + "PushDeer Server": "Server PushDeer", + "pushDeerServerDescription": "Lascia vuoto per utilizzare il server ufficiale", + "PushDeer Key": "Chiave PushDeer", + "wayToGetClickSendSMSToken": "Puoi ottenere il nome utente API e la chiave API da {here}.", + "Google Analytics ID": "ID di Google Analytics", + "API Keys": "Chiavi API", + "Expiry date": "Data di scadenza", + "Don't expire": "Nessuna scadenza", + "Open Badge Generator": "Apri generatore di insegne", + "Badge Generator": "Generatore di insegne di {0}", + "Badge Duration (in hours)": "Durata insegna (in ore)", + "Badge Type": "Tipo insegna", + "Badge Label": "Etichetta insegna", + "Badge Prefix": "Prefisso valore insegna", + "Badge Suffix": "Suffisso valore insegna", + "Badge Label Color": "Colore etichetta insegna", + "Badge value (For Testing only.)": "Valore insegna (solo per prova.)", + "Secret AccessKey": "Chiave di accesso segreta", + "successEdited": "Modificato con successo.", + "remoteBrowserToggle": "Per impostazione predefinita, Chromium viene eseguito all'interno del contenitore Uptime Kuma. È possibile utilizzare un browser remoto attivando questa opzione.", + "tagNotFound": "Etichetta non trovata.", + "successDeleted": "Eliminato con successo.", + "deleteRemoteBrowserMessage": "Sei sicuro di eliminare questo Browser Remoto per tutti i monitoraggi?", + "wayToGetSevenIOApiKey": "Visita il Cruscotto in app.seven.io > sviluppatore > chiave API > pulsante verde Aggiungi", + "conditionAdd": "Aggiungi condizione", + "smseagleGroup": "Nome(i) del gruppo della rubrica", + "smseagleContact": "Nome/i del contatto della rubrica", + "Badge Preview": "Anteprima insegna", + "Badge Label Suffix": "Suffisso etichetta insegna", + "Badge Up Color": "Colore insegna Attivo", + "Badge Warn Color": "Colore insegna Avviso", + "Badge Down Days": "Giorni insegna Spento", + "Badge Warn Days": "Giorni insegna Avviso", + "Badge Style": "Stile insegna", + "Badge URL": "URL insegna", + "Badge Down Color": "Colore insegna Spento", + "Badge Label Prefix": "Prefisso etichetta insegna", + "senderSevenIO": "Invio del numero o del nome", + "threemaRecipientTypePhoneFormat": "E.164, senza + iniziale", + "threemaApiAuthenticationSecret": "Segreto di Gateway-ID", + "OAuth Audience": "Pubblico OAuth", + "Optional: The audience to request the JWT for": "Facoltativo: il pubblico a cui richiedere il JWT", + "rabbitmqNodesRequired": "Impostare i nodi per questo monitoraggio.", + "rabbitmqHelpText": "Per utilizzare il monitoraggio, è necessario abilitare il plugin di gestione nella configurazione di RabbitMQ. Per ulteriori informazioni, consultare la documentazione di RabbitMQ.", + "Client Secret": "Segreto del client", + "Custom sound to override default notification sound": "Suono personalizzato per sovrascrivere il suono di notifica predefinito", + "No tags found.": "Etichette non trovate.", + "Template Format": "Formato modello", + "Message Template": "Modello messaggio", + "Staged Tags for Batch Add": "Etichette preparate per aggiunta batch", + "wayToGetWahaSession": "Da questa sessione WAHA invia notifiche all'ID chat. Puoi trovarlo nel Cruscotto WAHA.", + "smseagleMsgSms": "Messaggio SMS (predefinito)", + "smseagleGroupV2": "ID gruppo rubrica", + "smseagleContactV2": "ID contatto rubrica", + "smseagleRecipient": "Destinatario/i (i destinatari multipli devono essere separati da virgola)", + "smseagleUrl": "URL del tuo dispositivo SMSEagle", + "smseagleMsgType": "Tipo di messaggio", + "smseagleMsgRing": "Chiamata", + "smseagleMsgTts": "Chiamata da testo a voce", + "smseagleMsgTtsAdvanced": "Chiamata avanzata da testo a voce", + "smseagleDuration": "Durata (in secondi)", + "smseagleTtsModel": "ID modello di sintesi vocale", + "smseagleApiType": "Versione API", + "smseagleApiv1": "APIv1 (per progetti esistenti e compatibilità con le versioni precedenti)", + "cellsyntDestination": "Numero di telefono del destinatario in formato internazionale con 00 iniziale seguito dal prefisso internazionale, ad esempio 00447920110000 per il numero del Regno Unito 07920 110 000 (massimo 17 cifre in totale). Massimo 25.000 destinatari separati da virgole per richiesta HTTP.", + "snmpOIDHelptext": "Inserisci l'OID del sensore o dello stato che desideri monitorare. In caso di dubbi sull'OID, utilizza strumenti di gestione della rete come browser MIB o software SNMP.", + "Condition": "Condizione", + "conditionDelete": "Elimina condizione", + "conditionAddGroup": "Aggiungi gruppo", + "conditionDeleteGroup": "Elimina gruppo", + "conditionValuePlaceholder": "Valore", + "rabbitmqNodesDescription": "Inserisci l'URL per i nodi di gestione RabbitMQ, inclusi protocollo e porta. Esempio: {0}", + "Conditions": "Condizioni", + "ntfyPriorityHelptextAllExceptDown": "Tutti gli eventi vengono inviati con questa priorità, eccetto gli eventi {0}, che hanno una priorità di {1}", + "Add Another": "Aggiungi un altro", + "Key Added": "Chiave aggiunta", + "apiKeyAddedMsg": "La tua chiave API è stata aggiunta. Prendine nota perché non verrà più visualizzata.", + "Add API Key": "Aggiungi chiave API", + "No API Keys": "Nessuna chiave API", + "apiKey-active": "Attiva", + "apiKey-expired": "Scaduta", + "apiKey-inactive": "Inattiva", + "Expires": "Scade", + "Generate": "Genera", + "pagertreeIntegrationUrl": "URL di integrazione", + "pagertreeUrgency": "Urgenza", + "pagertreeSilent": "Silenziosa", + "pagertreeLow": "Bassa", + "pagertreeHigh": "Alta", + "pagertreeCritical": "Critica", + "pagertreeResolve": "Risoluzione automatica", + "pagertreeDoNothing": "Non fare nulla", + "wayToGetPagerTreeIntegrationURL": "Dopo aver creato l'integrazione Uptime Kuma in PagerTree, copia l'Endpoint. Vedi tutti i dettagli {0}", + "lunaseaTarget": "Obiettivo", + "lunaseaDeviceID": "ID dispositivo", + "lunaseaUserID": "ID utente", + "ntfyAuthenticationMethod": "Metodo di autenticazione", + "ntfyUsernameAndPassword": "Nome utente e password", + "twilioAccountSID": "SID account", + "twilioApiKey": "Chiave API (facoltativa)", + "Show Clickable Link": "Mostra collegamento cliccabile", + "toastErrorTimeout": "Scadenza per le notifiche di errore", + "toastSuccessTimeout": "Scadenza per le notifiche di successo", + "Kafka Brokers": "Broker Kafka", + "ntfyPriorityHelptextAllEvents": "Tutti gli eventi vengono inviati con la massima priorità", + "RabbitMQ Nodes": "Nodi di gestione RabbitMQ", + "Ip Family": "Famiglia IP", + "Manual": "Manuale", + "Font Twemoji by Twitter licensed under": "Font Twemoji di Twitter concesso in licenza sotto", + "smsplanetApiToken": "Token per l'API SMSPlanet", + "wahaSession": "Sessione", + "brevoApiHelp": "Crea una chiave API qui: {0}", + "brevoFromEmail": "Da email", + "brevoFromName": "Da nome", + "brevoToEmail": "A email", + "brevoCcEmail": "Email in CC", + "brevoBccEmail": "Email in CCN", + "brevoSubject": "Oggetto", + "brevoLeaveBlankForDefaultSubject": "lasciare vuoto per l'oggetto predefinito", + "pingNumericLabel": "Uscita numerica", + "pingPerRequestTimeoutLabel": "Scadenza per ping", + "OneChatAccessToken": "Token di accesso OneChat", + "OneChatUserIdOrGroupId": "ID utente o ID gruppo OneChat", + "OneChatBotId": "ID bot OneChat", + "wahaChatId": "ID chat (numero di telefono / ID contatto / ID gruppo)", + "wayToGetWahaApiUrl": "URL istanza WAHA.", + "YZJ Webhook URL": "URL del webhook YZJ", + "YZJ Robot Token": "Token robot YZJ", + "Plain Text": "Testo normale", + "smsplanetApiDocs": "Informazioni dettagliate su come ottenere i token API sono disponibili in {the_smsplanet_documentation}.", + "the smsplanet documentation": "la documentazione di smsplanet", + "Phone numbers": "Numeri di telefono", + "Sender name": "Nome del mittente", + "smsplanetNeedToApproveName": "Deve essere approvato nel pannello del cliente", + "Disable URL in Notification": "Disabilita URL nelle notifiche", + "ipFamilyDescriptionAutoSelect": "Utilizza {happyEyeballs} per determinare la famiglia IP.", + "Happy Eyeballs algorithm": "Algoritmo Happy Eyeballs", + "Clear Form": "Pulire modulo", + "pause": "Pausa", + "Alphanumeric (recommended)": "Alfanumerico (consigliato)", + "OAuth Token URL": "URL del token OAuth", + "twilioFromNumber": "Numero mittente", + "twilioToNumber": "Numero destinatario", + "Enter the list of brokers": "Inserisci l'elenco dei broker", + "Press Enter to add broker": "Premi Invio per aggiungere il broker", + "Kafka Topic Name": "Nome argomento Kafka", + "Enable Kafka SSL": "Abilita Kafka SSL", + "Kafka Producer Message": "Messaggio del Produttore di Kafka", + "Kafka SASL Options": "Opzioni SASL di Kafka", + "Pick a SASL Mechanism...": "Scegli un meccanismo SASL…", + "Authorization Identity": "Identità di autorizzazione", + "AccessKey Id": "ID chiave di accesso", + "Session Token": "Token di sessione", + "Request Body": "Corpo della richiesta", + "FlashDuty Severity": "Gravità", + "FlashDuty Push URL": "URL di invio", + "nostrRecipientsHelp": "formato npub, uno per riga", + "showCertificateExpiry": "Mostra scadenza certificato", + "noOrBadCertificate": "Certificato nullo/non valido", + "cacheBusterParam": "Aggiungi il parametro {0}", + "cacheBusterParamDescription": "Parametro generato casualmente per evitare la cache.", + "Message format": "Formato del messaggio", + "Send rich messages": "Invia messaggi ricchi", + "Bitrix24 Webhook URL": "URL del webhook di Bitrix24", + "wayToGetBitrix24Webhook": "Puoi creare un webhook seguendo i passaggi in {0}", + "Saved.": "Salvato.", + "authInvalidToken": "Token non valido.", + "authIncorrectCreds": "Nome utente o password errati.", + "2faEnabled": "2FA abilitata.", + "2faDisabled": "2FA disabilitata.", + "successAdded": "Aggiunta con successo.", + "successResumed": "Ripresa con successo.", + "successPaused": "Sospesa correttamente.", + "successAuthChangePassword": "La password è stata aggiornata con successo.", + "successBackupRestored": "Backup ripristinato con successo.", + "successDisabled": "Disabilitato con successo.", + "successEnabled": "Abilitato con successo.", + "foundChromiumVersion": "Trovato Chromium/Chrome. Versione: {0}", + "Remote Browsers": "Browser remoti", + "Remote Browser": "Browser remoto", + "Add a Remote Browser": "Aggiungi un browser remoto", + "Remote Browser not found!": "Browser remoto non trovato!", + "remoteBrowsersDescription": "I browser remoti sono un'alternativa all'esecuzione locale di Chromium. Puoi configurarli con un servizio come browserless.io o connetterti al tuo", + "self-hosted container": "contenitore auto-ospitato", + "useRemoteBrowser": "Utilizza un browser remoto", + "GrafanaOncallUrl": "URL di Grafana Oncall", + "Browser Screenshot": "Cattura schermata del browser", + "Command": "Comando", + "mongodbCommandDescription": "Esegui un comando MongoDB sul database. Per informazioni sui comandi disponibili, consultare la {documentazione}", + "cellsyntOriginatortypeAlphanumeric": "Stringa alfanumerica (massimo 11 caratteri alfanumerici). I destinatari non possono rispondere al messaggio.", + "threemaSenderIdentity": "ID gateway", + "Recipient Type": "Tipo di destinatario", + "Private Number": "Numero privato", + "Go back to home page.": "Torna alla pagina principale.", + "SIGNL4": "SIGNL4", + "Correct": "Corretto", + "Enable Kafka Producer Auto Topic Creation": "Abilita la creazione automatica degli argomenti del Produttore di Kafka", + "wayToGetFlashDutyKey": "Per integrare Uptime Kuma con Flashduty: vai su Canali > Seleziona un canale > Integrazioni > Aggiungi una nuova integrazione, scegli Uptime Kuma e copia l'URL di invio.", + "FlashDuty Push URL Placeholder": "Copia dalla pagina di integrazione degli avvisi", + "nostrRecipients": "Chiavi pubbliche dei destinatari (npub)", + "bitrix24SupportUserID": "Inserisci il tuo ID utente in Bitrix24. Puoi trovare l'ID tramite il collegamento presente nel profilo dell'utente.", + "2faAlreadyEnabled": "2FA già abilitata.", + "Group Name": "Nome del gruppo", + "OAuth2: Client Credentials": "OAuth2: credenziali del client", + "Form Data Body": "Corpo dei dati del modulo", + "Optional: Space separated list of scopes": "Facoltativo: elenco di ambiti separati da spazi", + "SIGNL4 Webhook URL": "URL del webhook SIGNL4", + "Arcade": "Sala giochi", + "brevoApiKey": "Chiave API Brevo", + "brevoLeaveBlankForDefaultName": "lasciare vuoto per il nome predefinito", + "brevoSeparateMultipleEmails": "Separa più indirizzi email con virgole", + "pingGlobalTimeoutDescription": "Tempo totale in secondi prima che il ping si interrompa, indipendentemente dai pacchetti inviati", + "wayToGetWahaApiKey": "La chiave API è il valore della variabile d'ambiente WHATSAPP_API_KEY utilizzata per eseguire WAHA.", + "wayToWriteWahaChatId": "Il numero di telefono con il prefisso internazionale, ma senza il segno più all'inizio ({0}), l'ID contatto ({1}) o l'ID gruppo ({2}). Le notifiche vengono inviate a questo ID chat dalla sessione WAHA.", + "nostrRelaysHelp": "Un URL inoltro per riga", + "nostrRelays": "Inoltri di Nostr", + "nostrSender": "Chiave privata del mittente (nsec)", + "wayToWriteWhapiRecipient": "Il numero di telefono con il prefisso internazionale, ma senza il segno più all'inizio ({0}), l'ID contatto ({1}) o l'ID gruppo ({2}).", + "wayToGetWhapiUrlAndToken": "Puoi ottenere l'URL API e il token accedendo al canale desiderato da {0}", + "whapiRecipient": "Numero di telefono / ID contatto / ID gruppo", + "API URL": "URL dell'API", + "receiverSevenIO": "Numero di ricezione", + "receiverInfoSevenIO": "Se il numero di destinazione non si trova in Germania, è necessario aggiungere il prefisso internazionale prima del numero (ad esempio, per il prefisso internazionale 1 dagli Stati Uniti utilizzare 117612121212 invece di 017612121212)", + "apiKeySevenIO": "Chiave API SevenIO", + "greater than or equal to": "maggiore o uguale a", + "wayToWriteEvolutionRecipient": "Il numero di telefono con il prefisso internazionale, ma senza il segno più all'inizio ({0}), l'ID contatto ({1}) o l'ID gruppo ({2}).", + "wayToGetEvolutionUrlAndToken": "Puoi ottenere l'URL API e il token accedendo al canale desiderato da {0}", + "evolutionRecipient": "Numero di telefono / ID contatto / ID gruppo", + "evolutionInstanceName": "Nome istanza", + "What is a Remote Browser?": "Che cos'è un browser remoto?", + "wayToGetHeiiOnCallDetails": "Come ottenere l'ID trigger e le chiavi API è spiegato nella {documentazione}", + "documentationOf": "{0} Documentazione", + "callMeBotGet": "Qui puoi generare un endpoint per {0}, {1} e {2}. Tieni presente che potresti ottenere una limitazione della velocità. I limiti di velocità sembrano essere: {3}", + "gtxMessagingApiKeyHint": "Puoi trovare la tua chiave API in: I miei account di routing > Mostra informazioni account > Credenziali API > API REST (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Da Numero di telefono / Indirizzo di origine del percorso di trasmissione (TPOA)", + "To Phone Number": "Al numero di telefono", + "Telephone number": "Numero di telefono", + "Originator": "Originatore", + "cellsyntOriginator": "Visibile sul cellulare del destinatario in quanto mittente del messaggio. I valori consentiti e la funzione dipendono dal parametro originatortype.", + "threemaRecipientTypeEmail": "Indirizzo email", + "threemaSenderIdentityFormat": "8 caratteri, di solito inizia con *", + "threemaBasicModeInfo": "Nota: questa integrazione utilizza Threema Gateway in modalità base (crittografia basata sul server). Ulteriori dettagli sono disponibili {0}.", + "Host Onesender": "Sistema Onesender", + "Token Onesender": "Token Onesender", + "privateOnesenderDesc": "Assicurati che il numero di telefono sia valido. Per inviare un messaggio a un numero di telefono privato, ad esempio: 628123456789", + "groupOnesenderDesc": "Assicurati che l'ID del Group sia valido. Per inviare un messaggio al gruppo, ad esempio: 628123456789-342345", + "Group ID": "ID gruppo", + "wayToGetOnesenderUrlandToken": "Puoi ottenere l'URL e il token andando sul sito web di Onesender. Ulteriori informazioni {0}", + "Add Remote Browser": "Aggiungi browser remoto", + "New Group": "Nuovo gruppo", + "Authentication Method": "Metodo di autenticazione", + "Authorization Header": "Intestazione di autorizzazione", + "OAuth Scope": "Ambito OAuth", + "Lost connection to the socket server.": "Connessione al server socket persa.", + "Fail": "Fallo", + "Harp": "Arpa", + "Reveal": "Svela", + "Bubble": "Bolla", + "Doorbell": "Campanello", + "Flute": "Flauto", + "Money": "Soldi", + "Scifi": "Fantascienza", + "Clear": "Pulire", + "Elevator": "Ascensore", + "Guitar": "Chitarra", + "Pop": "Pop", + "Time Sensitive (iOS Only)": "Sensibile al tempo (solo iOS)", + "From": "Da", + "Can be found on:": "Può essere trovato su: {0}", + "The phone number of the recipient in E.164 format.": "Il numero di telefono del destinatario nel formato E.164.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Se vuoi ricevere risposte, indica l'ID del mittente del messaggio oppure un numero di telefono in formato E.164.", + "rabbitmqNodesInvalid": "Utilizza un URL completamente qualificato (che inizi con 'http') per i nodi RabbitMQ.", + "RabbitMQ Username": "Nome utente RabbitMQ", + "RabbitMQ Password": "Password RabbitMQ", + "SendGrid API Key": "Chiave API SendGrid", + "Separate multiple email addresses with commas": "Separa più indirizzi email con virgole", + "pingCountLabel": "Pacchetti massimi", + "pingCountDescription": "Numero di pacchetti da inviare prima di fermarsi", + "pingGlobalTimeoutLabel": "Scadenza globale", + "pingPerRequestTimeoutDescription": "Questo è il tempo massimo di attesa (in secondi) prima di considerare perso un singolo pacchetto ping", + "pingIntervalAdjustedInfo": "Intervallo regolato in base al conteggio dei pacchetti, alla scadenza globale e alla scadenza per ping", + "smtpHelpText": "'SMTPS' verifica il funzionamento di SMTP/TLS; 'Ignora TLS' si connette tramite testo normale; 'STARTTLS' si connette, invia un comando STARTTLS e verifica il certificato del server. Nessuno di questi invia un'email.", + "Custom URL": "URL personalizzato", + "Community String": "Stringa della comunità", + "snmpCommunityStringHelptext": "Questa stringa funge da password per autenticare e controllare l'accesso ai dispositivi abilitati SNMP. Abbinala alla configurazione del tuo dispositivo SNMP.", + "OID (Object Identifier)": "OID (identificatore dell'oggetto)", + "Destination": "Destinazione", + "Allow Long SMS": "Consenti SMS lunghi", + "cellsyntSplitLongMessages": "Dividi i messaggi lunghi in un massimo di 6 parti. 153 x 6 = 918 caratteri.", + "max 11 alphanumeric characters": "massimo 11 caratteri alfanumerici", + "SNMP Version": "Versione SNMP", + "Please enter a valid OID.": "Inserisci un OID valido.", + "wayToGetThreemaGateway": "Puoi registrarti per Threema Gateway {0}.", + "threemaRecipient": "Destinatario", + "threemaRecipientType": "Tipo di destinatario", + "threemaRecipientTypeIdentity": "ID Threema", + "threemaRecipientTypeIdentityFormat": "8 caratteri", + "threemaRecipientTypePhone": "Numero di telefono", + "not starts with": "non inizia con", + "ends with": "finisce con", + "not ends with": "non finisce con", + "less than or equal to": "minore o uguale a", + "record": "registro", + "Notification Channel": "Canale di notifica", + "Sound": "Suono", + "Alphanumerical string and hyphens only": "Solo stringhe alfanumeriche e trattini", + "Close": "Chiudi", + "Client ID": "ID cliente", + "Originator type": "Tipo di originatore", + "Mechanism": "Meccanismo", + "Bot secret": "Segreto del bot", + "Send UP silently": "Invia SU silenziosamente", + "Send DOWN silently": "Invia GIU' silenziosamente", + "Nextcloud host": "Sistema Nextcloud", + "Conversation token": "Token di conversazione", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Per installare un bot Nextcloud Talk è necessario disporre dell'accesso amministrativo al server.", + "auto-select": "Selezione automatica", + "supportBaleChatID": "Supporto Chat Diretta / Gruppo / ID Chat del Canale", + "wayToGetBaleChatID": "Puoi ottenere il tuo ID chat inviando un messaggio al bot e andando a questo URL per visualizzare la chat_id:", + "wayToGetBaleToken": "Puoi ottenere un token da {0}.", + "Mention Mobile List": "Menziona l'elenco dei cellulari", + "Mention User List": "Menziona l'elenco degli ID utente", + "Dingtalk Mobile List": "Elenco dei cellulari", + "Dingtalk User List": "Elenco ID utente", + "Enter a list of userId": "Inserisci un elenco di userId", + "Invalid mobile": "Cellulare non valido [{mobile}]", + "Enter a list of mobile": "Inserisci un elenco di cellulari", + "Invalid userId": "ID utente non valido [{userId}]", + "Maximum Retries": "Tentativi massimi", + "Number of retry attempts if webhook fails": "Numero di tentativi di ripetizione (ogni 60-180 secondi) se il webhook fallisce.", + "HTTP Method": "Metodo HTTP", + "webhookPostMethodDesc": "POST è adatto alla maggior parte dei server HTTP moderni.", + "webhookGetMethodDesc": "GET invia dati come parametri di query e non consente la configurazione di un corpo. Utile per attivare i monitoraggi Push di Uptime Kuma.", + "descriptionHelpText": "Mostrato sul cruscotto interno. La formattazione è consentita e adattata (conserva spazi e rientri) prima della visualizzazione.", + "deleteChildrenMonitors": "Elimina anche i monitoraggi figlio diretti e i relativi figli, se presenti | Elimina anche tutti i {count} monitoraggi figlio diretti e i relativi figli, se presenti", + "Template ID": "ID modello", + "wayToGetClickSMSIRTemplateID": "Il tuo modello deve contenere un campo {uptkumaalert}. Puoi creare un nuovo modello {qui}.", + "deleteGroupMsg": "Sei sicuro di voler eliminare questo gruppo?", + "Clone Maintenance": "Clona manutenzione", + "ariaPauseMaintenance": "Metti in pausa questa pianifica di manutenzione", + "ariaResumeMaintenance": "Riprendi questa pianifica di manutenzione", + "ariaCloneMaintenance": "Crea una copia di questa pianifica di manutenzione", + "ariaEditMaintenance": "Modifica questa pianifica di manutenzione", + "ariaDeleteMaintenance": "Elimina questa pianifica di manutenzione", + "Recipient Numbers": "Numeri dei destinatari", + "twilioApiKeyHelptext": "La chiave API è facoltativa ma consigliata. È possibile fornire l'Account SID e l'AuthToken dalla pagina TwilioConsole oppure l'Account SID e la coppia chiave Api e chiave segreta Api", + "twilioMessagingServiceSID": "SID del servizio di messaggistica (facoltativo)", + "twilloMessagingServiceSIDHelptext": "Inserisci qui il SID del tuo servizio di messaggistica se utilizzi {twillo_messaging_service_help_link} per gestire mittenti e funzionalità", + "Allow Notifications": "Consenti notifiche", + "Browser not supported": "Browser non supportato", + "showOnlyLastHeartbeat": "Mostra solo l'ultimo segnale di vita", + "Notifications Enabled": "Notifiche abilitate", + "Unable to get permission to notify": "Impossibile ottenere l'autorizzazione per la notifica (richiesta negata o ignorata).", + "Webpush Helptext": "La funzionalità Web push funziona solo con connessioni SSL (HTTPS). Per i dispositivi iOS, la pagina web deve essere aggiunta in anticipo alla schermata iniziale.", + "Binary Floor Control Protocol": "Trasporto WebSocket per BFCP (Binary Floor Control Protocol)", + "OPC UA Connection Protocol": "Protocollo di connessione OPC UA", + "Constrained Application Protocol": "Protocollo applicativo vincolato (CoAP)", + "WebSocket Transport for JMAP": "Trasporto WebSocket per JMAP (JSON Meta Application Protocol)", + "Penguin Statistics Live Protocol v3": "Protocollo Penguin Statistics Live v3 (codifica Protobuf)", + "certHostnameMismatch": "Il nome sistema del certificato non corrisponde all'URL del monitoraggio.", + "minimumIntervalWarning": "Intervalli inferiori a 20 secondi potrebbero causare scarse prestazioni.", + "lowIntervalWarning": "Vuoi davvero impostare un valore di intervallo inferiore a 20 secondi? Le prestazioni potrebbero risultare ridotte, soprattutto se sono presenti molti monitoraggi.", + "ignoreSecWebsocketAcceptHeaderDescription": "Permetti al server di non rispondere con l'intestazione Sec-WebSocket-Accept, se l'aggiornamento del websocket riesce.", + "Ignore Sec-WebSocket-Accept header": "Ignora l'intestazione {0}", + "wsSubprotocolDescription": "Per maggiori informazioni sui sottoprotocolli, consultare la {documentazione}", + "WebSocket Application Messaging Protocol": "WAMP (protocollo di messaggistica delle applicazioni WebSocket)", + "Session Initiation Protocol": "Trasporto WebSocket per SIP (Session Initiation Protocol)", + "Network API for Notification Channel": "API di rete RESTful OMA per canale di notifica", + "Web Process Control Protocol": "Protocollo di controllo dei processi Web (WPCP)", + "Advanced Message Queuing Protocol": "Protocollo avanzato di accodamento messaggi (AMQP) 1.0+", + "jsflow": "Protocollo jsFlow pubsub/queue", + "Reverse Web Process Control": "Protocollo di controllo del processo Web inverso (RWPCP)", + "Extensible Messaging and Presence Protocol": "Trasporto WebSocket per il protocollo Extensible Messaging and Presence (XMPP)", + "Smart Home IP": "SHIP - Smart Home IP", + "Miele Cloud Connect Protocol": "Protocollo Miele Cloud Connect", + "Push Channel Protocol": "Protocollo Push Channel", + "Message Session Relay Protocol": "Trasporto WebSocket per MSRP (Message Session Relay Protocol)", + "Softvelum Low Delay Protocol": "Protocollo Softvelum a basso ritardo", + "OPC UA JSON Encoding": "Codifica JSON OPC UA", + "Swindon Web Server Protocol": "Protocollo del server Web Swindon (codifica JSON)", + "Broadband Forum User Services Platform": "USP (piattaforma di servizi per gli utenti del forum a banda larga)", + "Softvelum WebSocket signaling protocol": "Protocollo di segnalazione WebSocket Softvelum", + "Cobra Real Time Messaging Protocol": "Protocollo di messaggistica in tempo reale Cobra", + "Declarative Resource Protocol": "Protocollo di risorse dichiarative", + "BACnet Secure Connect Hub Connection": "Connessione hub BACnet Secure Connect", + "BACnet Secure Connect Direct Connection": "Connessione diretta BACnet Secure Connect", + "ITU-T T.140 Real-Time Text": "Testo in tempo reale ITU-T T.140", + "Done.best IoT Protocol": "Fatto. Il miglior protocollo IoT", + "Collection Update": "Sottoprotocollo Websocket di aggiornamento della raccolta", + "Text IRC Protocol": "Protocollo IRC di testo", + "Binary IRC Protocol": "Protocollo IRC binario", + "labelDomainExpiry": "Scadenza dominio", + "labelDomainNameExpiryNotification": "Notifica scadenza del nome di dominio", + "settingsDomainExpiry": "Scadenza del dominio", + "domainExpiryDescription": "Attiva la notifica quando i nomi di dominio scadono in:", + "Select All": "Seleziona tutto", + "Deselect All": "Deseleziona tutto", + "Subprotocol": "Sottoprotocollo", + "Duration (Minutes)": "Durata (minuti)", + "SMTP Security": "Sicurezza SMTP", + "Ignore STARTTLS": "Ignora STARTTLS", + "Use STARTTLS": "Utilizza STARTTLS", + "Enter the list of nodes": "Inserisci l'elenco dei nodi di gestione RabbitMQ", + "Press Enter to add node": "Premi Invio per aggiungere il nodo", + "enableSSL": "Abilita SSL/TLS", + "mariadbUseSSLHelptext": "Abilita per usare una connessione criptata per il tuo database. Richiesto dalla maggior parte dei database cloud.", + "mariadbCaCertificateLabel": "Certificato CA", + "Google": "Google", + "RecordMatch": "Corrispondenza del valore del record", + "Sort by status": "Ordina per stato", + "teltonikaVersionWarning": "Questo provider di notifiche richiede che il tuo dispositivo Teltonika esegua RMS versione 7.14.0 o superiore.", + "discordMessageFormatNormal": "Normale (embed ricchi)", + "discordMessageFormat": "Formato Messaggio", + "halopsa_setup_step3": "Copia l’URL del webhook e incollalo nel campo di testo sopra", + "Region": "Regione", + "slackIncludeGroupName": "Includi il nome del gruppo di monitor", + "360messengerAuthToken": "API Key 360messenger", + "360messengerRecipient": "Numero/i di telefono del destinatario", + "360messengerGroupId": "ID Gruppo 360messenger", + "360messengerUseTemplate": "Usa un modello di messaggio personalizzato", + "360messengerTemplate": "Modello di messaggio 360messenger", + "360messengerGroupList": "Gruppi WhatsApp", + "360messengerSelectGroupList": "Seleziona un gruppo da aggiungere", + "360messengerSelectedGroupID": "Seleziona ID Gruppo/i", + "360messengerEnableSendToGroup": "Abilita l’invio a gruppo/i WhatsApp", + "360messengerCustomMessageTemplate": "Modello di messaggio personalizzato", + "360messengerEnableCustomMessage": "Abilita un modello di messaggio personalizzato invece del messaggio predefinito.", + "360messengerMessageTemplate": "Modello di messaggio", + "360messengerWayToGetUrlAndToken": "Puoi ottenere la tua API Key di 360messenger da {0}.", + "360messengerErrorNoApiKey": "Per favore inserisci la tua API Key di 360messenger.", + "360messengerErrorNoGroups": "Nessun gruppo WhatsApp trovato per questo account.", + "360messengerErrorApi": "Impossibile caricare l’elenco dei gruppi WhatsApp (Errore {statusCode}: {message}).", + "360messengerErrorGeneric": "Impossibile caricare la lista di gruppi Whatsapp: {message}", + "discordMessageFormatMinimalist": "Minimalista (stato corto)", + "discordUseMessageTemplate": "Usa un modello di messaggio personalizzato", + "mtls-auth-server-cert-label": "Certificato", + "mtls-auth-server-cert-placeholder": "Contenuto del certificato", + "avgPing": "Ping medio", + "Uptime Kuma": "Uptime Kuma", + "maxPing": "Ping massimo", + "minPing": "Ping minimo", + "Setup Instructions": "Istruzioni configurazione", + "halopsa_setup_step2": "Configura le azioni del runbook per elaborare gli avvisi (ad es. Crea ticket)", + "halopsa_setup_step4": "Scegli Basic Auth e crea nome utente e password. Quindi digita o incolla quel nome utente e quella password nei campi di test sopra", + "Clear current filters": "Pulisci filtri attuali", + "Sort options": "Opzioni di ordinamento", + "Sort by name": "Ordina per nome", + "Sort by certificate expiry": "Ordina per scadenza certificato", + "Splunk Rest URL": "URL Rest Splunk", + "Severity": "Gravità", + "Message Format": "Formato del Messaggio", + "smscTranslit": "smscTranslit", + "PushDeer Server URL": "URL Server PushDeer", + "To Number": "Numero destinatario", + "GrafanaOncallURL": "URL Oncall Grafana", + "System Service": "Servizio di sistema", + "SSL/TLS": "SSL/TLS", + "playground": "playground", + "Check Type": "Tipo di controllo", + "Service Name": "Nome del servizio", + "Details": "Dettagli", + "passwordTooWeak": "La password è troppo debole. Dovrebbe contenere caratteri alfabetici e numeri. Deve essere lunga almeno 6 caratteri.", + "teltonikaUrl": "URL del tuo dispositivo Teltonika", + "GRPC Options": "Opzioni GRPC", + "Metadata": "Metadati", + "End": "Fine", + "Endpoint": "Endpoint", + "teltonikaUsernameHelptext": "Raccomandazione: crea un account separato limitato solo all’invio di messaggi SMS e inserisci qui il suo nome utente", + "teltonikaUnsafeTlsDescription": "Disattivare la validazione del certificato TLS espone a attacchi on-path (man-in-the-middle), con il rischio di perdita di dati e compromissione dei sistemi. Non disattivare la validazione del certificato a meno che tu non accetti questo vettore di attacco. Consigliamo di usare Let’s Encrypt con rinnovo automatico.", + "teltonikaPassword": "Password API", + "teltonikaModem": "ID Modem", + "teltonikaPhoneNumber": "Numero di cellulare", + "teltonikaModemHelptext": "L’ID del modem SMS deve essere nel formato {0}. Consulta https://developers.teltonika-networks.com/reference/ per indicazioni.", + "Plausible": "Plausible", + "TLS Alert Spec": "RFC 8446", + "Umami": "Umami", + "Load More": "Carica altri", + "Loading...": "Caricamento...", + "hours": "{n} ora | {n} ore", + "minutes": "{n} minuto | {n} minuti", + "Monitors": "{n} monitor | {n} monitor", + "mariadbCaCertificateHelptext": "Incolla il certificato CA in formato PEM da utilizzare con certificati autofirmati. Lascia vuoto se il database usa un certificato firmato da una CA pubblica.", + "unknownDays": "Giorni indefiniti", + "No incidents recorded": "Nessun incidente registrato", + "days": "{n} giorno | {n} giorni", + "responseMaxLength": "Lunghezza massima risposta (bytes)", + "responseMaxLengthDescription": "Dimensione massima della risposta da memorizzare. Imposta 0 per illimitata. Le risposte più grandi verranno troncate. Predefinito: 1024 (1 KB)", + "wsCodeDescription": "Per più informazioni sui codici di stato, per favore consulta {rfc6455}", + "Subprotocol(s)": "Sottoprotocollo/i", + "saveResponseForNotifications": "Salva la risposta HTTP con esito positivo per le notifiche", + "saveErrorResponseForNotifications": "Salva la risposta HTTP con errore per le notifiche", + "saveResponseDescription": "Memorizza la risposta HTTP e la rende disponibile ai modelli di notifica come {templateVariable}", + "logoutCurrentUser": "Consenti l'indicizzazione", + "Resolver Server(s)": "Server resolver", + "GlobalpingMultipleLocationsError": "Le posizioni multiple non sono supportate, utilizza una singola posizione per ogni monitor.", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Il monitoraggio della scadenza del dominio non è disponibile per \".{publicSuffix}\" perché nessun servizio RDAP è elencato da IANA", + "imageResetConfirmation": "Immagine ripristinata al valore predefinito", + "Basic radio toggle button group": "Gruppo di pulsanti a selezione singola di base", + "Incident description": "Descrizione incidente", + "mariadbSocketPathDetectedHelptext": "Connessione al database come specificato tramite la variabile d’ambiente {0}.", + "teltonikaUrlHelptext": "L'URL dovrebbe essere specificato come origine completa, esempio: {0} o {1}.", + "teltonikaUnsafeTls": "Ignora validazione del certificato", + "Incident not found or access denied": "Incidente non trovato o accesso negato", + "Past Incidents": "Incidenti passati", + "Incident title": "Titolo incidente", + "GlobalpingLocationDescription": "Il campo posizione accetta continenti, paesi, regioni, città, ASN, ISP o regioni cloud. Puoi combinare i filtri con {plus} (ad es. {amazonPlusGermany} o {comcastPlusCalifornia}). Se la latenza è una metrica importante, usa i filtri per restringere la posizione a una piccola area per evitare picchi e, per una migliore stabilità, imposta il filtro {datacenter}. {fullDocs}.", + "Pinned incidents are shown prominently on the status page": "Gli incidenti fissati vengono mostrati in evidenza nella pagina di stato", + "Edit Incident": "Modifica incidente", + "templateAvailableVariables": "Variabili disponibili", + "example": "Esempio", + "Result": "Risultato", + "BodyInvalidFormatBecause": "Il corpo della richiesta non è in formato JSON valido: {error}", + "HeadersInvalidFormatBecause": "Le intestazioni della richiesta non sono in formato JSON valido: {error}", + "steamApiKeyDescriptionAt": "Per monitorare un server di gioco Steam è necessaria una chiave Steam Web API. Puoi registrare la tua chiave API su {url}", + "hostnameCannotBeIP": "L’hostname DNS non può essere un indirizzo IP. Forse volevi usare il campo resolver?", + "invalidDNSHostname": "Hostname non valido. L’hostname deve essere un FQDN valido. Può essere wildcard, contenere underscore oppure terminare con un punto.", + "lastUpdatedAt": "Ultimo aggiornamento: {date}", + "lastUpdatedAtFromNow": "Ultimo aggiornamento: {date} ({fromNow}", + "createdAt": "Creato il: {data}", + "selectMonitorMsg": "Seleziona i monitor su cui eseguire azioni", + "selectAllMonitorsAria": "Seleziona tutti i monitors", + "deselectAllMonitorsAria": "Deseleziona tutti i monitors", + "deleteIncidentMsg": "Sei sicuro di voler eliminare questo incidente?", + "Certificate Chain:": "Catena dei certificati:", + "selectedMonitorCountMsg": "selezionato: {n} | selezionati: {n}", + "Actions": "Azioni", + "slug is not found": "Slug non trovato", + "RSS Title": "Titolo RSS", + "Please input content": "Inserisci un contenuto", + "Please input title": "Inserisci un titolo", + "dateCreatedAtFromNow": "Data di creazione: {date} ({fromNow})", + "Leave blank to use status page title": "Lascia vuoto per usare il titolo della pagina di stato", + "Cloud ID": "ID Cloud", + "sipsakPingWarning": "Per utilizzare il monitor SIP Options Ping, devi installare Uptime Kuma senza Docker e installare anche il client Sipsak sul tuo server.", + "Examples:": "Esempi: {0}", + "frontendVersionIs": "Versione Frontend: {version}", + "cronScheduleDescription": "Pianificazione: {description}", + "notificationChatPlatforms": "Piattaforme di chat", + "notificationPushServices": "Servizi Push", + "notificationSmsServices": "Servizi SMS", + "deletedMonitorsMsg": "{n} monitor eliminato | {n} monitor eliminati", + "noMonitorsPausedMsg": "Nessun monitor messo in pausa (nessuno era attivo)", + "deleteMonitorsMsg": "Sei sicuro di voler eliminare i monitor selezionati?", + "pausedMonitorsMsg": "{n} monitor in pausa | {n} monitor in pausa", + "resumedMonitorsMsg": "{n} monitor riattivato | {n} monitor riattivati", + "Sets end time based on start time": "Imposta l’ora di fine in base all’ora di inizio", + "Please set start time first": "Perfavore imposta prima il tempo d'inizio", + "noMonitorsOrStatusPagesSelectedError": "Impossibile creare una manutenzione senza monitor interessati o pagine di stato", + "Click Deploy → New deployment → Web app": "Fai clic su Distribuisci → Nuovo deployment → App web", + "You can divide numbers with commas or semicolons": "Puoi separare i numeri con {comma} o {semicolon}", + "OptionalParameters": "Parametri opzionali", + "WeCom Mentioned Mobile List": "Elenco dei numeri di cellulare menzionati in WeCom", + "serwersmsRecipientType": "Tipo di destinatario", + "Analytics Script URL": "URL Script Analytics", + "octopushEndpoint": "octopush (endpoint: {url})", + "legacyOctopushEndpoint": "Octopush-DM legacy (endpoint: {url})", + "Analytics Type": "Tipo di Analytics", + "Analytics ID": "ID Analytics", + "ntfyCall": "Chiamata", + "ntfyCallHelptext": "Effettua una chiamata telefonica quando scatta l’avviso. Imposta su “yes” per usare il primo numero verificato, oppure inserisci un numero di telefono specifico (ad esempio +12223334444). Richiede ntfy Pro e un numero di telefono verificato.", + "Screenshot Delay": "Ritardo dello screenshot (attende {milliseconds})", + "snmpV3Username": "Username SNMPv3", + "json_value": "Valore JSON", + "resendApiHelp": "Crea la tua API Key qui {0}", + "resendApiKey": "API Key Resend", + "Suppress Notifications": "Sopprimi le notifiche", + "discordSuppressNotificationsHelptext": "Se abilitato, i messaggi verranno pubblicati nel canale ma non attiveranno notifiche push o desktop per i destinatari.", + "domain_expiry_unsupported_missing_target": "Nessun dominio o hostname valido è configurato per questo monitor", + "Never": "Mai", + "Webhook Payload Fields": "Campi del payload del webhook", + "halopsa_payload_desc": "I seguenti campi verranno inviati al tuo Webhook Halo PSA:", + "certificateExpiryNotificationHelp": "Il numero di giorni di anticipo può essere configurato nelle impostazioni.", + "notificationHomeAutomation": "Domotica", + "notificationOther": "Altre Integrazioni", + "Disable STARTTLS": "Disattiva STARTTLS", + "disableSTARTTLSDescription": "Abilita questa opzione per i server SMTP che non supportano STARTTLS. Le email verranno inviate tramite una connessione non crittografata.", + "noMonitorsResumedMsg": "Nessun monitor riattivato (nessuno era inattivo)", + "slackIncludeGroupNameDescription": "Se abilitato, il percorso del gruppo di monitor verrà incluso nelle notifiche per distinguere i monitor con lo stesso nome presenti in gruppi diversi.", + "see Jira Cloud Docs": "Guarda la documentazione di Jira Cloud", + "ntfyUseTemplate": "Personalizza i modelli di notifica", + "ntfyUseTemplateDescription": "Abilita questa opzione per personalizzare titoli e messaggi delle notifiche utilizzando il templating LiquidJS", + "ntfyCustomTitle": "Modello personalizzato del titolo", + "ntfyCustomMessage": "Modello personalizzato del messaggio", + "Globalping API Token": "Token API Globalping", + "GlobalpingLocationDocs": "Documentazione completa sull’input della posizione", + "username": "Nome utente", + "mtls-auth-server-ca-placeholder": "Server dell'autorità del certificato", + "halopsa_field_monitor_id": "Identificatore univoco del monitor (null per notifiche di test) - Usalo per associare gli avvisi ai ticket", + "halopsa_field_message": "Messaggio completo dell’avviso con stato e dettagli", + "halopsa_field_title": "Titolo avviso (sempre 'Uptime Kuma Alert')", + "teltonikaPasswordHelptext": "Puoi definire la password dell’utente API nel tuo router Teltonika, esempio: {0}", + "teltonikaPhoneNumberHelptext": "Il numero deve essere nel formato internazionale {0}, {1}. È consentito un solo numero.", + "bulkDeleteErrorMsg": "Impossibile eliminare {n} monitor | Impossibile eliminare {n} monitor", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Imposta “Esegui come: Me” e “Chi ha accesso: Chiunque”", + "Google Apps Script Code": "Codice Google Apps Script", + "Copy to Clipboard": "Copia negli appunti", + "Sort by uptime": "Ordina per uptime", + "fluxerMessageFormatMinimalist": "Minimalista (stato corto)", + "fluxerMessageFormatCustom": "Modello personalizzato", + "fluxerMessageTemplate": "Modello del Messaggio", + "Fluxer Webhook URL": "URL del Webhook Fluxer", + "wayToGetFluxerURL": "Puoi ottenerlo andando nelle impostazioni del canale di destinazione > Webhook > Crea Webhook > Copia URL Webhook.", + "domainExpiryNotificationHelp": "Il numero di giorni in anticipo può essere configurato nelle impostazioni.", + "Matomo": "Matomo", + "Notification Action": "Azione di notifica", + "versionIs": "Versione: {version}", + "invalidHostnameOrIP": "Hostname o IP non valido. L’hostname deve essere un FQDN valido. Non è possibile usare wildcard. Può contenere underscore oppure terminare con un punto.", + "homeAssistantNotificationActionHelptext": "L’elenco delle azioni di notifica è disponibile in Home Assistant in “Impostazioni > Strumenti per sviluppatori > Azioni”. Cerca “notify” per trovare le tue azioni. Inserisci solo la parte successiva a “notify.”, ad esempio per l’azione “notify.mobile_app_xyz” inserisci “mobile_app_xyz”. Per le notifiche mobile integrate, cerca “Invia una notifica tramite mobile_app_xyz” (non “Invia una notifica”).", + "notificationUniversal": "Universale", + "teamsEnableTags": "Includi tags", + "teamsEnableTagsDescription": "Se attivato, il messaggio includerà le tags del monitor.", + "Copy the web app URL and paste it above": "Copia l’URL dell’app web e incollalo qui sopra", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "A causa delle restrizioni dell’operatore, abilita le variabili opzionali a rischio di mancata consegna", + "WeCom Mentioned Mobile List Description": "Inserisci i numeri di telefono da menzionare. Separa più numeri con virgole. Usa {'@'}all per menzionare tutti.", + "serwersmsRecipientTypePhone": "Numero di cellulare", + "Open Badge Link Generator": "Apri il generatore di link Open Badge", + "Badge Link Generator": "Generatore di link per badge di {0}", + "Badge Link Generator Helptext": "I link dei badge sono disponibili per tutti i monitor assegnati alle pagine di stato pubbliche. Per maggiori informazioni, consulta la {documentation}.", + "systemServiceDescriptionWindows": "Verifica se il servizio {service_name} nel Gestore servizi di Windows è in esecuzione", + "360messengerWayToWriteRecipient": "Inserisci uno o più numeri di telefono in formato internazionale senza il segno più iniziale (ad es. {0}). Separa più numeri con virgole.", + "fluxerMessageFormat": "Formato del Messaggio", + "fluxerMessageFormatNormal": "Normale (embed ricchi)", + "fluxerUseMessageTemplate": "Usa un modello di messaggio personalizzato", + "fluxerUseMessageTemplateDescription": "Se abilitato, il messaggio verrà inviato utilizzando un modello personalizzato (LiquidJS). Lascia vuoto per usare il formato predefinito di Uptime Kuma.", + "Globalping - Access global monitoring probes": "Globalping - Accedi alle sonde di monitoraggio globali", + "GlobalpingMonitorDescription": "Globalping fornisce accesso a migliaia di sonde ospitate dalla community per eseguire test e misurazioni di rete. Per tutti gli utenti anonimi è impostato un limite di 250 test all’ora. Per raddoppiare il limite a 500 all’ora salva il tuo token in {accountSettings}. Consulta la {docs} per maggiori informazioni.", + "GlobalpingHostname": "Un obiettivo di misurazione raggiungibile pubblicamente. In genere un hostname o un indirizzo IPv4/IPv6, a seconda del tipo di misurazione.", + "matrixUseTemplate": "Usa un modello di messaggio personalizzato", + "matrixUseTemplateDescription": "Se abilitato, il messaggio verrà inviato utilizzando un modello personalizzato.", + "screenshotDelayWarning": "Valori più alti mantengono il browser aperto più a lungo, il che può aumentare l’uso di memoria con molti monitor simultanei.", + "resendLeaveBlankForDefaultName": "Lascia vuoto per il nome predefinito", + "resendLeaveBlankForDefaultSubject": "Lascia vuoto per l’oggetto predefinito", + "resendToEmail": "Email destinatario", + "resendSubject": "Oggetto", + "GlobalpingResolverInfo": "Indirizzo IPv4/IPv6 o un nome di dominio completamente qualificato (FQDN). Per impostazione predefinita usa il resolver di rete locale della sonda. Puoi cambiare il server resolver in qualsiasi momento.", + "Protocol": "Protocollo", + "Monitor Subtype": "Sottotipo Monitor", + "Check for": "Controlla per", + "Halo PSA": "Halo PSA", + "halopsa_webhook_url_desc": "Inserisci l’URL del webhook dal tuo Runbook di integrazione Halo PSA (Configurazione > Integrazioni > Integrazioni personalizzate > Runbook di integrazione). Seleziona “Can only be started from Halo and from a public endpoint” quando crei il webhook.", + "password": "Password", + "halopsa_username_desc": "Username per autenticarsi con il webhook Halo PSA", + "halopsa_password_desc": "Password per autenticarsi con il webhook Halo PSA", + "halopsa_id_usage_hint": "💡 Suggerimento: usa monitor_id per associare in modo affidabile gli avvisi ai ticket e heartbeat_id per tracciare la cronologia degli eventi", + "halopsa_setup_step5": "Configura il runbook per usare monitor_id per associare gli avvisi ai ticket esistenti", + "message": "messaggio", + "signalUseTemplate": "Usa un modello di messaggio personalizzato", + "signalUseTemplateDescription": "Se abilitato, il messaggio verrà inviato utilizzando un modello personalizzato. Puoi usare il templating Liquid per personalizzare il formato della notifica.", + "serwersmsRecipientTypeGroup": "Gruppo", + "serwersmsGroupId": "ID Gruppo", + "serwersmsGroupIdHelptext": "ID o ID dei gruppi nel Pannello Cliente. Questi identificatori possono essere scaricati utilizzando action groups / index oppure copiati dal gruppo in modifica nel Pannello Cliente.", + "domain_expiry_unsupported_is_icann": "Il dominio \"{domain}\" non è idoneo per il monitoraggio della scadenza del dominio, perché il suo suffisso pubblico \".{publicSuffix}\" non è gestito da ICANN", + "GlobalpingIpFamilyInfo": "La versione IP da utilizzare. Consentita solo se il target è un hostname.", + "RegexMatch": "Inserisci una regex per verificare la corrispondenza con il valore del record", + "domain_expiry_unsupported_monitor_type": "Il monitoraggio della scadenza del dominio non è supportato per questo tipo di monitor", + "screenshot of the website": "Screenshot del sito web", + "Basic checkbox toggle button group": "Gruppo di pulsanti a selezione multipla di base", + "mtls-auth-server-key-label": "Chiave", + "mtls-auth-server-key-placeholder": "Contenuto della chiave", + "mtls-auth-server-ca-label": "Autorità del Certificato", + "halopsa_setup_step1": "Crea un Runbook di integrazione in HaloPSA (Configurazione → Integrazioni → Runbook di integrazione)", + "Halo PSA Webhook URL": "URL Webhook Halo PSA", + "halopsa_field_monitor": "Nome del monitor", + "halopsa_field_timestamp": "Timestamp dell'evento nel formato ISO 8601", + "halopsa_field_uptime_kuma_version": "Numero di versione di Uptime Kuma", + "Teltonika SMS Gateway": "Gateway SMS Teltonika", + "teltonikaUsername": "Username API", + "TLS Alerts": "Avvisi TLS", + "Expected TLS Alert": "Avviso TLS previsto", + "Expand All Groups": "Espandi tutti i gruppi", + "halopsa_field_status": "Stato del monitor: UP, DOWN, NOTIFICATION o UNKNOWN", + "account settings": "Impostazioni account", + "Jira Service Management": "Jira Service Management", + "monitorTypeGameServer": "Server di gioco", + "monitorTypeDatabase": "Tipo di monitoraggio del database", + "monitorTypeSpecial": "Speciale", + "minuteShort": "{n} min | {n} min", + "years": "{n} anno | {n} anni", + "Pin this incident": "Fissa questo incidente", + "Only retry if status code check fails": "Riprova solo se il controllo del codice di stato fallisce", + "retryOnlyOnStatusCodeFailureDescription": "Se abilitato, i tentativi verranno effettuati solo quando fallisce il controllo del codice di stato HTTP (ad esempio, se il server non è raggiungibile). Se il controllo del codice di stato ha esito positivo ma la query JSON fallisce, il monitor verrà contrassegnato immediatamente come non disponibile, senza tentativi di nuovo invio.", + "wildcardOnlyForDNS": "Gli hostname wildcard sono supportati solo per i monitor DNS.", + "invalidURL": "URL Invalido", + "Resolve": "Risolvi", + "Resolved": "Risolto", + "notificationEmail": "Email", + "notificationIncidentManagement": "Gestione Incidente", + "noMonitorsSelectedWarning": "Stai creando una manutenzione senza alcun monitor interessato. Sei sicuro di voler continuare?", + "Google Apps Script Webhook URL": "URL del webhook di Google Apps Script", + "Deploy a Google Apps Script as a web app and paste the URL here": "Distribuisci uno script Google Apps Script come app web e incolla qui l’URL", + "Quick Setup Guide": "Guida rapida alla configurazione", + "Open your Google Spreadsheet": "Apri il tuo Spreadsheet Google", + "Go to Extensions → Apps Script": "Vai su Estensioni → Apps Script", + "Paste the script code (see below)": "Incolla il codice dello script (vedi sotto)", + "Copied to clipboard!": "Copiato negli appunti!", + "Failed to copy to clipboard": "Errore nel copiare negli appunti", + "checkPriceAt": "Controlla i prezzi di {service} su {url}", + "aliyun-template-requirements-and-parameters": "Il modello SMS di Aliyun deve contenere i parametri: {parameters}", + "aliyun-template-optional-parameters": "Parametri Opzionali: {parameters}", + "aboutJiraCloudId": "Altre informazioni su Jira Cloud ID: {0}", + "slackUseTemplate": "Usa un modello di messaggio personalizzato", + "slackUseTemplateDescription": "Se abilitato, il messaggio verrà inviato utilizzando un modello personalizzato. Puoi usare il templating Liquid per includere le informazioni del gruppo di monitor tramite monitorJSON.path o monitorJSON.pathName.", + "ntfyNotificationTemplateFallback": "Lascia vuoto per usare il formato predefinito di Uptime Kuma", + "milliseconds": "{n} millisecondo | {n} millisecondi", + "screenshotDelayDescription": "Attendi facoltativamente questo numero di millisecondi prima di acquisire lo screenshot. Massimo: {maxValueMs} ms (0,5 × intervallo).", + "systemServiceName": "Nome del servizio", + "systemServiceDescription": "Verifica se il servizio di sistema {service_name} è attivo", + "systemServiceDescriptionLinux": "Verifica se il servizio systemd di Linux {service_name} è attivo", + "systemServiceCommandHint": "Comando usato: {command}", + "systemServiceExpectedOutput": "Output Atteso: \"{0}\"", + "resendFromName": "Nome mittente", + "resendFromEmail": "Email mittente", + "discordMessageFormatCustom": "Modello personalizzato", + "discordMessageTemplate": "Modello del Messaggio", + "discordUseMessageTemplateDescription": "Se abilitato, il messaggio verrà inviato utilizzando un modello personalizzato (LiquidJS). Lascia vuoto per usare il formato predefinito di Uptime Kuma.", + "Location": "Luogo", + "API Token": "Token API", + "See Jira Cloud Docs": "Guarda la documentazione Jira Cloud", + "globalpingApiTokenDescription": "Ottieni il tuo token API Globalping qui: {0}.", + "Collapse All Groups": "Collassa tutti i gruppi", + "None (Successful Connection)": "Nessuno (Connessione riuscita)", + "expectedTlsAlertDescription": "Seleziona l’avviso TLS che ti aspetti venga restituito dal server. Usa {code} per verificare che gli endpoint mTLS rifiutino connessioni senza certificati client. Vedi {link} per i dettagli.", + "systemService": "Servizio di sistema", + "Show this Maintenance Message on which Status Pages": "Mostra questo messaggio di manutenzione su quali pagine di stato", + "telnyxApiKey": "API Key (V2)", + "telnyxApiKeyHelptext": "La tua chiave API Telnyx V2 dal Portale Telnyx, nella sezione API Keys", + "telnyxPhoneNumber": "Numero mittente", + "telnyxPhoneNumberHelptext": "Il tuo numero Telnyx da cui inviare i messaggi (deve essere registrato nel tuo account Telnyx, es. +15551234567)", + "telnyxToNumber": "Numero destinatario", + "telnyxMessagingProfileId": "ID del profilo di messaggistica (opzionale)", + "telnyxMessagingProfileIdHelptext": "Opzionalmente, associa un Profilo di Messaggistica Telnyx per gestire le impostazioni e le funzionalità del mittente", + "wayToGetMaxToken": "Puoi ottenere il token del bot MAX e altri dettagli da {0}.", + "maxApiUrlDescription": "URL base dell’API per MAX Messenger. Predefinito: https://platform-api.max.ru", + "wayToGetMaxChatID": "Specifica l’identificativo della chat in MAX Messenger dove i messaggi devono essere inviati.", + "maxUseTemplate": "Usa un modello di messaggio personalizzato", + "maxUseTemplateDescription": "Se abilitato, il messaggio verrà inviato utilizzando un modello personalizzato.", + "maxTemplateFormatDescription": "MAX Messenger supporta testo semplice, formattazione Markdown e HTML.", + "maxMessenger": "Socket", + "evolutionCustomMessageTitle": "Messaggio personalizzato (opzionale)", + "evolutionCustomMessageDesc": "Lascia vuoto per usare il messaggio predefinito.", + "telnyxToNumberHelptext": "Il numero di telefono destinatario che riceverà le notifiche SMS (es. +15559876543)", + "API Version": "Versione API", + "Peer ID": "ID Peer", + "vkApiVersionDescription": "Versione dell’API VK utilizzata per le richieste. Lasciare il valore predefinito a meno che non sia necessaria una versione diversa dell’API VK per compatibilità.", + "vkDontParseLinks": "Disabilita anteprime dei link", + "vkDontParseLinksDescription": "Se abilitato, VK non genererà anteprime/link snippet.", + "vkPeerIdDescription": "Inserisci il peer_id di destinazione di VK. Questo valore viene inviato all’API così com’è.", + "oracledbConnectionString": "Database Oracle: {connectionString}", + "WebSocket Options": "Opzioni WebSocket", + "pingPacketSizeWarning": "Una dimensione del pacchetto inferiore a 16 non produce tempi di andata e ritorno su Linux", + "VKTeams Use Template": "Usa un modello di messaggio personalizzato", + "VKTeams Use Template Description": "Se abilitato, il messaggio sarà spedito usando il template personalizzato.", + "VKTeams Template Format Description": "Per la formattazione dei messaggi, VKTeams supporta testo semplice, MarkdownV2 e HTML.", + "VKTeams Bot Token Description": "Puoi scoprire come ottenere un token bot e altri dettagli su {0}.", + "VKTeams Base Url Description": "URL API di base di VKTeams, potrebbe essere diverso nelle soluzioni on-premise. Predefinito: https://myteam.mail.ru", + "VKTeams Chat Id Description": "Per gli utenti, questo è il loro indirizzo email. Per gruppi e canali, recupera l'ID dalle loro impostazioni e aggiungi {'@'}chat.agent", + "wayToGetEgoSMSToken": "Puoi ottenere il tuo nome utente e la password dell'API EgoSMS da {qui}.", + "egosmsSenderDescription": "ID del mittente visualizzato ai destinatari (massimo 11 caratteri). Se vuoto, il valore predefinito è EGOSMS.", + "egosmsPhoneNumberDescription": "Numero del destinatario con prefisso internazionale, senza + o 00 (ad esempio 2567XXXXXXXX).", + "gamedigToken": "Token", + "gamedigTokenDescription": "Opzionale. Alcuni server di gioco richiedono un token di autenticazione.", + "Bearer Token": "Token Bearer", + "Connection Timeout": "Scadenza connessione", + "smtpAdditionalHeadersTitle": "Intestazioni aggiuntive", + "smtpAdditionalHeadersDesc": "Per l'utilizzo, fare riferimento alle intestazioni personalizzate di Nodemailer {0}.", + "Rybbit": "Rybbit", + "Flowtriq Webhook URL": "URL Webhook", + "Flowtriq API Key": "Chiave API", + "flowtriqApiKeyDescription": "Chiave API opzionale per autenticare le richieste webhook alla tua istanza di Flowtriq.", + "wxPusherDownloadApp": "scarica l'app", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) è disponibile tramite l'app WxPusher standalone su 6 piattaforme: Android, iOS, HarmonyOS, Windows, macOS e Linux ({download}). Scansiona il codice QR qui sotto per ottenere il tuo SPT e sei pronto per inviare. Separa più SPT con una virgola (,) — consulta la {docs} per maggiori informazioni.", + "wxPusherDocs": "guida ufficiale", + "Access Key": "Chiave di accesso", + "ooredooAccessKeyDescription": "La trovi sotto \"Chiave di accesso API\" nel cruscotto di Ooredoo SMS. Incollala esattamente come mostrato: verrà codificata automaticamente in base64 prima dell'invio.", + "ooredooUsernameDescription": "L'indirizzo email che utilizzi per accedere al cruscotto di Ooredoo per l'invio di SMS di massa.", + "ooredooBearerTokenDescription": "Il token visualizzato dopo \"Authorizzazione: Bearer\" nell'esempio di comando cURL sul cruscotto di Ooredoo SMS.", + "ooredooRecipientDescription": "Inserisci uno o più numeri separati da una virgola o da uno spazio. Ai numeri locali di 7 cifre viene automaticamente aggiunto il prefisso 960.", + "Leave blank to use the default Ooredoo endpoint.": "Lascia vuoto per utilizzare l'endpoint Ooredoo predefinito.", + "Hash Key": "Chiave hash", + "wayToGetBearSMSToken": "Puoi ottenere il tuo nome utente API BearSMS e la chiave hash da {0}.", + "bearsmsPhoneNumberDescription": "Numero del destinatario con prefisso internazionale, senza + o 00 (ad esempio 9725XXXXXXXX).", + "bearsmsSenderDescription": "ID mittente facoltativo, visibile ai destinatari (max 11 caratteri). Deve essere approvato nel tuo account BearSMS, altrimenti verrà utilizzato l'ID mittente predefinito dell'account.", + "plivoAuthID": "ID di autorizzazione", + "plivoAuthIDHelptext": "Il tuo ID di autenticazione Plivo dalla console Plivo", + "plivoAuthToken": "Token di autenticazione", + "plivoAuthTokenHelptext": "Il tuo token di autenticazione Plivo dalla console Plivo", + "plivoFromNumber": "Numero mittente", + "plivoFromNumberHelptext": "Il numero di telefono Plivo da cui inviare (es. +15551234567). Gli SMS possono anche utilizzare un ID mittente alfanumerico approvato; le chiamate vocali richiedono un numero di telefono Plivo.", + "plivoToNumber": "Numero destinatario", + "plivoToNumberHelptext": "Il numero di telefono di destinazione che riceve l'SMS o la chiamata vocale (es. +15559876543)", + "plivoMessageType": "Tipo di messaggio", + "plivoVoiceCall": "Chiamata vocale", + "plivoAnswerUrl": "URL di risposta", + "plivoAnswerUrlHelptext": "URL che restituisce il file XML di Plivo per la chiamata. Il testo dell'avviso viene aggiunto come parametro di query ?message=, quindi il tuo dispositivo può restituire, ad esempio, {message}.", + "Pinglet Topic URL": "URL dell'argomento", + "pingletTopicUrlDescription": "L'URL di pubblicazione dell'argomento Pinglet a cui inviare gli avvisi, ad esempio https://app.pinglet.co.uk/your-namespace/alerts. L'argomento viene creato al primo aggiornamento se non esiste già.", + "ClickUp API Token": "Token API di ClickUp", + "ClickUp API Token Description": "È possibile creare un token di accesso personale in ClickUp andando su Impostazioni -> Integrazioni e ClickApps -> API di ClickUp -> Token API", + "ClickUp Workspace ID": "ID dell'area di lavoro ClickUp", + "ClickUp Workspace ID Description": "Lo puoi trovare nell'URL della tua area di lavoro, subito dopo app.clickup.com/, ad esempio app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ID del canale ClickUp", + "ClickUp Channel ID Description": "Lo puoi trovare nell'URL del canale di chat, subito dopo /chatr/, ad esempio .../chatr/abc 123-4567.", + "pm2Description": "Verifica se il processo PM2 {service_name} è online", + "pm2ExpectedStates": "Stato previsto: online. Gli stati di arresto e di errore vengono trattati come GIU'.", + "turbosmtpConsumerKey": "Chiave del client", + "turbosmtpConsumerSecret": "Segreto del client", + "openwaSession": "Sessione", + "openwaChatId": "ID chat (numero di telefono / ID contatto / ID gruppo)", + "wayToGetOpenwaApiUrl": "URL della tua istanza OpenWA.", + "wayToGetOpenwaApiKey": "Chiave API utilizzata per la tua istanza di OpenWA.", + "wayToGetOpenwaSession": "ID di sessione utilizzato da OpenWA per inviare notifiche.", + "wayToWriteOpenwaChatId": "Il numero di telefono con il prefisso internazionale, ma senza il segno più all'inizio ({0}), l'ID contatto ({1}) o l'ID gruppo ({2}). È possibile inviare a più ID chat separandoli con delle virgole.", + "openwaCustomMessageTitle": "Messaggio personalizzato (opzionale)", + "openwaCustomMessageDesc": "Lascia vuoto per usare il messaggio predefinito.", + "PM2 Process": "Processo PM2", + "Refresh": "Aggiorna", + "Select PM2 process": "Selezionare il processo PM2", + "ntpThresholdsTitle": "Soglie di NTP", + "ntpStratumThreshold": "Soglia dello strato", + "ntpStratumThresholdHelp": "Contrassegna come non disponibile se lo strato è pari o superiore a questo valore. Lo strato indica la qualità della sorgente oraria (1=GPS/orologio atomico, 16=non sincronizzato).", + "ntpOffsetThreshold": "Soglia di scostamento temporale (ms)", + "ntpOffsetThresholdHelp": "Contrassegna come non disponibile se lo scostamento temporale supera questo valore in millisecondi.", + "ntpDispersionThreshold": "Soglia di dispersione delle radici (ms)", + "ntpDispersionThresholdHelp": "Contrassegna come non disponibile se la dispersione della radice supera questo valore. La dispersione indica la precisione dell'orologio del server.", + "milkyHttpAddress": "Indirizzo HTTP di Milky (senza il suffisso '/api')", + "milkyMessageType": "Tipo di messaggio Milky", + "milkyGroupMessage": "Gruppo", + "milkyPrivateMessage": "Privato", + "milkyUserOrGroupId": "Gruppo/ID utente", + "milkySafetyTips": "Per sicurezza, è necessario impostare il token di accesso", + "smsgatewayUrlHelptext": "L'URL di base del tuo gateway SMS, senza percorso API.", + "smsgatewayTo": "Numer(i) di telefono", + "smsgatewayToHelptext": "Elenco di numeri di telefono dei destinatari separati da virgole, in formato internazionale (ad esempio +15551234567)", + "Device ID or Group ID": "ID dispositivo o ID gruppo", + "notifyAppDeviceIdDescription": "ID del dispositivo dall'app Notify! oppure un ID di gruppo per inviare una notifica a tutti i dispositivi di un gruppo contemporaneamente.", + "notifyAppSetup": "Apri Notify! e trova l'ID del dispositivo e il token in Impostazioni. Non è necessario alcun account. Scopri di più su {0}.", + "Client Key": "Chiave client", + "Channel": "Canale", + "SFTP Path to Check": "Percorso SFTP da verificare", + "sftpPathHelpText": "Facoltativo. Se specificato, il monitoraggio verificherà che questo percorso esista sul server SFTP.", + "SSH Private Key": "Chiave privata SSH", + "Passphrase": "Frase d'accesso", + "sshPrivateKeyPlaceholder": "-----INIZIO CHIAVE PRIVATA OPENSSH-----\n...\n-----FINE CHIAVE PRIVATA OPENSSH-----", + "sshPrivateKeyHelpText": "Incolla il contenuto del file della tua chiave privata (ad es. id_rsa o id_ed25519).", + "sshPassphraseHelpText": "Facoltativo. Richiesto solo se la chiave privata è crittografata con una frase d'accesso.", + "optional": "facoltativo", + "Line Number": "Numero di riga", + "Amoot SMS recipient numbers help": "Inserisci uno o più numeri di cellulare separati da virgole.", + "Use Pattern": "Modello di utilizzo", + "Pattern Code ID": "Codice identificativo del modello", + "Amoot SMS pattern help": "Inserisci l'ID del codice modello dal tuo pannello SMS Amoot. {0}", + "Create an Amoot SMS pattern": "Crea un modello di SMS Amoot", + "Use Own Line for Pattern": "Utilizza una linea propria per il modello", + "Indigo URL": "URL Indigo", + "indigoUrlHelptext": "Il tuo indirizzo Indigo Reflector (https://NAME.indigodomo.net) o l'Indigo Web Server sulla tua rete (https://IP:8176).", + "indigoIgnoreTlsErrorHelptext": "Necessario per il server web Indigo sulla tua rete, che utilizza un certificato autofirmato. Non richiesto per il Reflector.", + "indigoApiKeyHelptext": "Una chiave API del tuo account Indigo (Autorizzazioni) oppure un segreto locale per il server web sulla tua rete.", + "Variable ID": "ID variabile", + "indigoVariableIdHelptext": "Il messaggio di avviso viene scritto in questa variabile.", + "Action Group ID": "ID gruppo di azioni", + "indigoActionGroupIdHelptext": "Viene eseguito dopo l'aggiornamento della variabile. Imposta una variabile, un gruppo di azioni o entrambi. Copia gli ID da Indigo usando il tasto destro del mouse → Copia ID." +} diff --git a/src/lang/ja.json b/src/lang/ja.json new file mode 100644 index 0000000..4b88dfc --- /dev/null +++ b/src/lang/ja.json @@ -0,0 +1,1304 @@ +{ + "languageName": "日本語", + "checkEverySecond": "{0}秒ごとにチェックします", + "retriesDescription": "サービスが完全に停止したと判断し、通知を送信する前に再接続を試みる最大回数", + "ignoreTLSError": "HTTPS ウェブサイトの TLS/SSL エラーを無視する", + "upsideDownModeDescription": "稼働ステータスを反転して扱います。サービスに接続可能な場合は、停止として扱います。", + "maxRedirectDescription": "必要な場合にリダイレクトする最大回数です。リダイレクトを無効にしたい場合は、0に設定してください。", + "acceptedStatusCodesDescription": "成功した応答とみなされるステータスコードを選択する。", + "passwordNotMatchMsg": "繰り返しのパスワードが一致しません。", + "notificationDescription": "監視を機能させるには、監視に通知を割り当ててください。", + "keywordDescription": "プレーンHTMLまたはJSON応答でキーワードを検索し、大文字と小文字を区別します。", + "pauseDashboardHome": "一時停止", + "deleteMonitorMsg": "この監視を削除してよろしいですか?", + "deleteNotificationMsg": "全ての監視のこの通知を削除してよろしいですか?", + "resolverserverDescription": "Cloudflareがデフォルトのサーバーですが、いつでもリゾルバサーバーを変更できます。", + "rrtypeDescription": "監視するRRタイプを選択します", + "pauseMonitorMsg": "一時停止しますか?", + "Settings": "設定", + "Dashboard": "ダッシュボード", + "New Update": "新しいアップデート", + "Language": "言語", + "Appearance": "外観", + "Theme": "テーマ", + "General": "全般", + "Version": "バージョン", + "Check Update On GitHub": "GitHubでアップデートを確認する", + "List": "一覧", + "Add": "追加", + "Add New Monitor": "監視の追加", + "Quick Stats": "統計", + "Up": "正常", + "Down": "異常", + "Pending": "待機中", + "Unknown": "不明", + "Pause": "一時停止", + "Name": "名前", + "Status": "ステータス", + "DateTime": "日時", + "Message": "メッセージ", + "No important events": "重要なイベントなし", + "Resume": "再開", + "Edit": "編集", + "Delete": "削除", + "Current": "現在", + "Uptime": "稼働時間", + "Cert Exp.": "証明書有効期限", + "day": "日 | 日間", + "-day": "-日", + "hour": "時間", + "-hour": "時間", + "Response": "レスポンス", + "Ping": "Ping", + "Monitor Type": "監視タイプ", + "Keyword": "キーワード", + "Friendly Name": "モニター表示名", + "URL": "URL", + "Hostname": "ホスト名", + "Port": "ポート", + "Heartbeat Interval": "監視間隔", + "Retries": "再試行回数", + "Advanced": "詳細設定", + "Upside Down Mode": "反転モード", + "Max. Redirects": "最大リダイレクト数", + "Accepted Status Codes": "正常なステータスコード", + "Save": "保存", + "Notifications": "通知", + "Not available, please setup.": "利用できません。設定が必要です。", + "Setup Notification": "通知設定", + "Light": "ライト", + "Dark": "ダーク", + "Auto": "自動", + "Theme - Heartbeat Bar": "テーマ - 監視バー", + "Normal": "通常", + "Bottom": "下部", + "None": "なし", + "Timezone": "タイムゾーン", + "Search Engine Visibility": "検索エンジンでの表示", + "Allow indexing": "インデックス作成を許可する", + "Discourage search engines from indexing site": "検索エンジンにインデックスさせないようにする", + "Change Password": "パスワード変更", + "Current Password": "現在のパスワード", + "New Password": "新しいパスワード", + "Repeat New Password": "確認のため新しいパスワードをもう一度", + "Update Password": "パスワードの更新", + "Disable Auth": "認証の無効化", + "Enable Auth": "認証の有効化", + "Logout": "ログアウト", + "Leave": "作業を中止する", + "I understand, please disable": "理解した上で無効化する", + "Confirm": "確認", + "Yes": "はい", + "No": "いいえ", + "Username": "ユーザー名", + "Password": "パスワード", + "Remember me": "ログインしたままにする", + "Login": "ログイン", + "No Monitors, please": "監視がありません", + "add one": "add one", + "Notification Type": "通知タイプ", + "Email": "Eメール", + "Test": "テスト", + "Certificate Info": "証明書情報", + "Resolver Server": "問い合わせ先DNSサーバ", + "Resource Record Type": "DNSレコード設定", + "Last Result": "最終結果", + "Create your admin account": "Adminアカウントの作成", + "Repeat Password": "パスワード確認", + "respTime": "応答時間 (ms)", + "notAvailableShort": "N/A", + "Create": "作成", + "clearEventsMsg": "このモニターのすべてのイベントを削除してもよろしいですか?これにより、関連するメッセージデータが完全に削除され、元に戻すことはできません。", + "clearHeartbeatsMsg": "このモニターのすべてのハートビートデータを削除してもよろしいですか?これにより、すべてのハートビートデータが完全に削除され、元に戻すことはできません。", + "confirmClearStatisticsMsg": "すべての統計を削除してもよろしいですか?", + "Clear Data": "データを削除", + "Events": "統計", + "Heartbeats": "異常記録", + "Auto Get": "自動取得", + "enableDefaultNotificationDescription": "監視を作成するごとに、この通知方法はデフォルトで有効になります。監視ごとに通知を無効にすることもできます。", + "Default enabled": "デフォルトで有効にする", + "Also apply to existing monitors": "既存のモニターにも適用する", + "Export": "エクスポート", + "Import": "インポート", + "backupDescription": "すべての監視と通知設定をJSONファイルとしてバックアップすることができます。", + "backupDescription2": "※ 履歴と統計のデータはバックアップされません。", + "backupDescription3": "通知に使用するトークンなどの機密データも含まれています。注意して扱ってください。", + "alertNoFile": "インポートするファイルを選択してください。", + "alertWrongFileType": "JSONファイルを選択してください。", + "twoFAVerifyLabel": "トークンを入力して、2段階認証を有効にします。", + "tokenValidSettingsMsg": "トークンの確認が完了しました! 「保存」をしてください。", + "confirmEnableTwoFAMsg": "2段階認証を「有効」にします。よろしいですか?", + "confirmDisableTwoFAMsg": "2段階認証を「無効」にします。よろしいですか?", + "Apply on all existing monitors": "既存のすべてのモニターに適用する", + "Verify Token": "認証する", + "Setup 2FA": "二要素認証 (2FA) の設定", + "Enable 2FA": "2段階認証を有効にする", + "Disable 2FA": "2段階認証を無効にする", + "2FA Settings": "2段階認証の設定", + "Two Factor Authentication": "2段階認証", + "Clear all statistics": "すべての記録を削除", + "retryCheckEverySecond": "{0} 秒ごとにリトライします", + "importHandleDescription": "同じ名前のすべての監視または通知方法を上書きしない場合は、「既存のをスキップ」を選択します。 「上書きする」は、既存のすべてのモニターと通知を削除します。", + "confirmImportMsg": "バックアップをインポートしてもよろしいですか?希望するオプションを選択してください。", + "Heartbeat Retry Interval": "異常検知後の再試行間隔", + "Import Backup": "バックアップのインポート", + "Export Backup": "バックアップのエクスポート", + "Skip existing": "既存のをスキップする", + "Overwrite": "上書きする", + "Options": "オプション", + "Keep both": "どちらも保持する", + "Tags": "タグ", + "Add New below or Select...": "新規追加または選択…", + "Tag with this name already exist.": "この名前のタグはすでに存在しています。", + "Tag with this value already exist.": "この値のタグはすでに存在しています。", + "color": "色", + "value (optional)": "値 (optional)", + "Search...": "検索…", + "Avg. Ping": "平均Ping時間", + "Avg. Response": "平均応答時間", + "Entry Page": "エントリーページ", + "statusPageNothing": "ここには何もありません。グループまたは監視を追加してください。", + "No Services": "サービス無し", + "All Systems Operational": "すべてのサービスが稼働中", + "Partially Degraded Service": "部分的にサービスが停止中", + "Degraded Service": "サービスが停止中", + "Add Group": "グループの追加", + "Add a monitor": "監視の追加", + "Edit Status Page": "ステータスページ編集", + "Go to Dashboard": "ダッシュボード", + "Status Page": "ステータスページ", + "Status Pages": "ステータスページ", + "Shrink Database": "データベースの縮小", + "Start": "始める", + "Retry": "リトライ", + "Please read": "次のリンクを参考にしてください", + "Orange": "橙", + "Gateway Type": "ゲートウェイの種類", + "Game": "ゲーム", + "Help": "ヘルプ", + "Maintenance": "メンテナンス", + "resendDisabled": "再送信不可", + "Schedule maintenance": "メンテナンスのスケジュール", + "Affected Monitors": "影響を受けるモニター", + "Pick Affected Monitors...": "影響を受けるモニターを選択…", + "Start of maintenance": "メンテナンス開始", + "General Monitor Type": "汎用モニタータイプ", + "resendEveryXTimes": "{0}回ごとに再送信", + "markdownSupported": "マークダウン構文がサポートされています。", + "All Status Pages": "すべてのステータス ページ", + "Monitor": "モニター |モニター", + "Resend Notification if Down X times consequently": "ダウンX回連続で通知再送", + "Push URL": "プッシュ URL", + "needPushEvery": "{0} 秒ごとにこの URL を呼び出す必要があります。", + "pushOptionalParams": "オプションのパラメーター: {0}", + "disableauth.message1": "{disableAuth}にしてもよろしいですか?", + "disable authentication": "認証を無効", + "disableauth.message2": "これは、Cloudflare Access、Authelia、またはその他の認証メカニズムなど、Uptime Kuma の前に{intendThirdPartyAuth}。", + "where you intend to implement third-party authentication": "サードパーティ認証を実装するシナリオ向けに設計されています", + "Please use this option carefully!": "このオプションは慎重に使用してください!", + "Primary Base URL": "プライマリ ベース URL", + "statusMaintenance": "メンテナンス", + "Passive Monitor Type": "パッシブモニタータイプ", + "Specific Monitor Type": "特定のモニターの種類", + "Security": "セキュリティ", + "Steam API Key": "Steam API Key", + "Default": "デフォルト", + "Title": "タイトル", + "No status pages": "ステータスページがありません", + "Proxy": "プロキシ", + "Date Created": "作成日", + "Content Type": "コンテンツタイプ", + "webhookAdditionalHeadersTitle": "追加ヘッダー", + "Server URL": "Server URL", + "Priority": "優先度", + "Read more": "続きを読む", + "Show Tags": "タグを表示", + "Switch to Dark Theme": "ダークテーマに切り替える", + "Token": "Token", + "Show URI": "URIを表示する", + "Gray": "灰色", + "Red": "赤色", + "Green": "緑色", + "Blue": "青色", + "Indigo": "藍色", + "Purple": "紫色", + "Pink": "ピンク", + "Required": "必須", + "Select status pages...": "ステータスページを選択してください…", + "webhookAdditionalHeadersDesc": "Webhook で送信される追加ヘッダーを設定します。各ヘッダーは JSON の key/value 型で定義されている必要があります。", + "Webhook URL": "Webhook URL", + "Application Token": "アプリケーショントークン", + "steamApiKeyDescription": "Steam Game Server を監視するためには、Steam Web-API キーが必要です。APIキーの登録はこちらから行えます。 ", + "Monitor History": "監視履歴", + "clearDataOlderThan": "監視履歴データを {0} 日間、保持します。", + "PasswordsDoNotMatch": "パスワードが一致していません。", + "Current User": "現在のユーザー", + "topic": "トピック", + "Info": "Info", + "Create Incident": "インシデントを作成", + "Content": "内容", + "Please input title and content": "タイトルと内容を入力してください", + "Last Updated": "最終アップデート日時", + "Unpin": "ピンを外す", + "Switch to Light Theme": "ライトテーマに切り替える", + "Hide Tags": "タグを隠す", + "Description": "メモ", + "Untitled Group": "名前の無いグループ", + "Services": "サービス", + "Discard": "破棄", + "Cancel": "キャンセル", + "Powered by": "Powered by", + "Customize": "カスタマイズ", + "Custom Footer": "カスタムフッター", + "Custom CSS": "カスタム CSS", + "deleteStatusPageMsg": "本当にこのステータスページを削除しますか?", + "Proxies": "プロキシ", + "default": "デフォルト", + "enabled": "有効", + "setAsDefault": "デフォルトに設定する", + "deleteProxyMsg": "本当にすべてのモニターからこのプロキシを削除しますか?", + "proxyDescription": "プロキシはモニターに割り当てられていないと機能しません。", + "setAsDefaultProxyDescription": "このプロキシは、新しいモニターに対してデフォルトで有効になっています。モニターごとに個別にプロキシを無効にすることができます。", + "Remove Token": "Tokenを削除", + "Stop": "停止", + "Add New Status Page": "新しいステータスページを追加", + "Next": "次へ", + "No Proxy": "プロキシなし", + "Authentication": "認証", + "HTTP Basic Auth": "HTTPベーシック認証", + "New Status Page": "新しいステータスページ", + "Page Not Found": "ページが見つかりません", + "Reverse Proxy": "リバースプロキシ", + "Backup": "バックアップ", + "About": "情報", + "cloudflareWebsite": "Cloudflare Website", + "Don't know how to get the token? Please read the guide:": "トークンの取得方法が分かりませんか?ガイドをお読みください。", + "Custom": "カスタム", + "Created": "作成日時", + "Resend Notification if Down X times consecutively": "X回連続でダウンしたら通知を再送する", + "webhookJsonDesc": "{0}はExpress.jsのような最新のHTTPサーバに適しています", + "webhookFormDataDesc": "{multipart}はPHPに適しています。このJSONは{decodeFunction}でデコードする必要があります", + "appriseInstalled": "Appriseはインストール済みです。", + "emojiCheatSheet": "絵文字一覧: {0}", + "Inactive": "無効", + "defaultNotificationName": "{notification} 通知 ({number})", + "Post URL": "Post URL", + "Active": "有効", + "Pick a RR-Type...": "RR-Typeを選択…", + "Pick Accepted Status Codes...": "正常なステータスコードを選択…", + "appriseNotInstalled": "Apprise はインストールされていません。{0}", + "Method": "方式", + "Body": "ボディ", + "Headers": "ヘッダー", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "リクエストヘッダーのJSONが無効です: ", + "BodyInvalidFormat": "リクエストボディのJSONが無効です: ", + "records": "レコード", + "One record": "レコード", + "topicExplanation": "監視するMQTTトピック", + "successMessage": "成功メッセージ", + "successMessageExplanation": "成功したとみなされるMQTTメッセージ", + "recent": "直近", + "Done": "完了", + "HTTP Options": "HTTPオプション", + "Style": "スタイル", + "info": "情報", + "warning": "警告", + "Valid To:": "有効期限:", + "Days Remaining:": "残りの有効日数:", + "Issuer:": "発行者:", + "Fingerprint:": "フィンガープリント:", + "Domain Name Expiry Notification": "ドメイン名有効期限通知", + "Footer Text": "フッター文章", + "Show Powered By": "Powered Byを表示", + "Domain Names": "ドメイン名", + "signedInDisp": "{0}としてログイン中", + "RadiusSecret": "Radius シークレット", + "RadiusSecretDescription": "クライアントとサーバー間の共有シークレット", + "RadiusCalledStationId": "Called-Station-Id", + "RadiusCallingStationId": "Calling-Station-Id", + "Subject:": "サブジェクト:", + "trustProxyDescription": "ヘッダー「X-Forwarded-*」を信頼します。Uptime Kumaがリバースプロキシの中にあり、正しいクライアントIPを取得する場合は、有効化してください。", + "Home Assistant URL": "ホームアシスタントURL", + "Examples": "例", + "telegramMessageThreadID": "(オプション) メッセージスレッドID", + "wayToGetLineNotifyToken": "{0}からアクセストークンを入手できます", + "Packet Size": "パケットサイズ", + "Bot Token": "ボットトークン", + "Chat ID": "チャットID", + "critical": "致命的エラー", + "signedInDispDisabled": "認証が無効化されています。", + "RadiusCalledStationIdDescription": "着信側の識別子", + "telegramProtectContent": "転送や保存を制限", + "YOUR BOT TOKEN HERE": "入手したボットトークン", + "API Key": "APIキー", + "Show update if available": "アップデートがあれば表示する", + "Using a Reverse Proxy?": "リバースプロキシを使用中ですか?", + "Go back to the previous page.": "前のページに戻る", + "Coming Soon": "近日公開予定", + "Workstation": "ワークステーション", + "wayToGetTelegramToken": "{0}からトークンを入手できます。", + "telegramMessageThreadIDDescription": "オプションとしてフォーラムのスレッド(話題)のIDを指定してメッセージを送信することができます。スーパーグループでのみ利用できます", + "telegramProtectContentDescription": "有効な場合、Telegram上のボットのメッセージの転送や保存が制限されます。", + "danger": "危険", + "error": "エラー", + "primary": "プライマリー", + "light": "ライト", + "dark": "ダーク", + "Post": "Post", + "No monitors available.": "監視対象がありません。", + "Add one": "追加", + "No Monitors": "監視対象なし", + "enableProxyDescription": "このプロキシは有効化されない限り、監視リクエストには影響しません。無効化した場合、一時的にプロキシをすべての監視から無効化することができます。", + "Certificate Chain": "証明書チェーン", + "Valid": "有効", + "Invalid": "無効", + "User": "ユーザー", + "Installed": "インストール済み", + "Not installed": "未インストール", + "Running": "稼働中", + "Not running": "停止中", + "Slug": "スラッグ", + "Accept characters:": "使用可能な文字:", + "startOrEndWithOnly": "{0}のみ最初と最後の文字として使用可能", + "No consecutive dashes": "連続したダッシュ記号は使用不可", + "The slug is already taken. Please choose another slug.": "指定されたスラグは既に使用されています。別のスラグを使用してください。", + "wayToGetCloudflaredURL": "({0}からcloudflaredをダウンロード)", + "Message:": "メッセージ:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Cloudflare Tunnelを経由した接続が遮断される可能性があります。停止してもよろしいですか? 確認のため、現在のパスワードを入力してください。", + "HTTP Headers": "HTTPヘッダー", + "Trust Proxy": "信頼できるプロキシ", + "Other Software": "その他のソフトウェア", + "For example: nginx, Apache and Traefik.": "例: nginxやApache、Traefikなど", + "RadiusCallingStationIdDescription": "発信側の識別子", + "Certificate Expiry Notification": "証明書有効期限通知", + "API Username": "APIユーザー名", + "Also check beta release": "ベータ版も表示する", + "Check how to config it for WebSocket": "WebSocketの設定方法について", + "Steam Game Server": "Steamゲームサーバー", + "Most likely causes:": "最も考えられる原因:", + "There might be a typing error in the address.": "アドレスの入力ミスの可能性があります。", + "What you can try:": "対応方法:", + "Retype the address.": "アドレスを入力し直してください。", + "The resource is no longer available.": "存在しないページです。", + "Connection String": "接続文字列", + "Query": "クエリ", + "settingsCertificateExpiry": "TLS証明書の有効期限", + "certificationExpiryDescription": "HTTPS監視のTLS証明書が以下の期限を迎えたときに通知を送信します。", + "Setup Docker Host": "Docker ホストを設定", + "Connection Type": "接続タイプ", + "Docker Daemon": "Docker デーモン", + "deleteDockerHostMsg": "すべての監視のDocker ホストを削除してもよろしいですか?", + "socket": "ソケット", + "tcp": "TCP / HTTP", + "Docker Container": "Docker コンテナー", + "Container Name / ID": "コンテナ名 / ID", + "Docker Host": "Docker ホスト", + "Docker Hosts": "Docker ホスト", + "Domain": "ドメイン", + "telegramSendSilently": "通知せずに送信", + "telegramSendSilentlyDescription": "通知せずにメッセージを送信します。通知音がなりません。", + "supportTelegramChatID": "チャットやグループ、チャンネルのチャットIDに対応", + "wayToGetTelegramChatID": "ボットにメッセージを送信し、以下のURLを開くとチャットIDのchat_idを入手できます。", + "chatIDNotFound": "チャットIDが存在しません。最初にメッセージをボットに送信してください", + "disableCloudflaredNoAuthMsg": "認証が無効化されているため、パスワードは必要ありません。", + "API Keys": "APIキー管理", + "Expiry": "期限切れ", + "Expiry date": "有効期限", + "No API Keys": "API Keyがありません", + "deleteAPIKeyMsg": "本当にこのAPIキーを削除しますか?", + "Generate": "生成", + "pauseMaintenanceMsg": "本当に一時停止しますか?", + "maintenanceStatus-under-maintenance": "メンテナンス中", + "secureOptionNone": "None / STARTTLS (25, 587)", + "smtp": "Email (SMTP)", + "Bot Display Name": "BOTの表示名", + "Prefix Custom Message": "メッセージの先頭に送信する文章", + "endpoint": "エンドポイント", + "Proxy Protocol": "Proxy Protocol", + "Google Analytics ID": "Google アナリティクス ID", + "Frontend Version do not match backend version!": "フロントエンドとバックエンドのバージョンが一致しません!", + "or": "または", + "Frontend Version": "フロントエンドのバージョン", + "promosmsPassword": "APIパスワード", + "Notification Sound": "通知音", + "Clone Monitor": "監視の複製", + "Clone": "複製", + "cloneOf": "{0} の複製", + "Hello @everyone is...": "Hello {'@'}everyone is…", + "Icon URL": "アイコンURL", + "affectedStatusPages": "メンテナンスメッセージを選択したステータスページに表示する", + "GoogleChat": "Google Chat (Google Workspace 限定)", + "pushyToken": "デバイストークン", + "SMS Type": "SMSタイプ", + "Proxy Server": "Proxy Server", + "smtpDkimDomain": "ドメイン名", + "Uptime Kuma URL": "Uptime Kuma URL", + "Channel Name": "チャンネル名", + "smtpDkimSettings": "DKIM設定", + "Server Address": "サーバーアドレス", + "strategyManual": "手動で有効/無効を切り替える", + "warningTimezone": "サーバーのタイムゾーンを使用します", + "weekdayShortMon": "月", + "weekdayShortTue": "火", + "weekdayShortWed": "水", + "weekdayShortFri": "金", + "weekdayShortSat": "土", + "weekdayShortSun": "日", + "dayOfWeek": "曜日ごと", + "dayOfMonth": "日にちごと", + "maintenanceStatus-inactive": "無効", + "maintenanceStatus-scheduled": "スケジュール済み", + "maintenanceStatus-ended": "終了済み", + "maintenanceStatus-unknown": "不明", + "Server Timezone": "サーバータイムゾーン", + "IconUrl": "アイコンURL", + "Enable DNS Cache": "【廃止】HTTP(s)モニターに対してDNSキャッシュを有効にする", + "Enable": "有効", + "Disable": "無効", + "Schedule Maintenance": "スケジュールメンテナンス", + "loadingError": "データを取得できません。後でもう一度試してください。", + "uninstall": "アンインストール", + "installing": "インストール中", + "Ignore TLS Error": "TLS エラーを無視", + "smtpCC": "CC", + "secureOptionTLS": "TLS (465)", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord ウェブフック URL", + "wayToCheckSignalURL": "こちらから設定方法を確認できます:", + "Number": "Number", + "Line Developers Console": "LINE Developersコンソール", + "Access Token": "アクセストークン", + "Channel access token": "チャンネルアクセストークン", + "enableGRPCTls": "TLS接続でgRPCリクエストを送信できるようにする", + "Messaging API": "Messaging API", + "dnsPortDescription": "DNSサーバーポートのデフォルトは53です。ポートはいつでも変更可能です。", + "Device": "デバイス", + "Event type:": "イベントタイプ:", + "here": "こちら", + "weekdayShortThu": "木", + "plugin": "プラグイン", + "No Maintenance": "メンテナンスはありません", + "dnsCacheDescription": "一部のIPv6環境では動作しない場合があります。問題が発生した場合は無効にしてください。", + "uninstalling": "アンインストール中", + "confirmUninstallPlugin": "本当にこのプラグインをアンインストールしますか?", + "wayToGetDiscordURL": "サーバー設定 -> 連携サービス -> ウェブフックを確認 -> 新しいウェブフック から新たに取得できます", + "wayToGetTeamsURL": "Webhook URLの作成方法については、{0}をご覧ください。", + "wayToGetZohoCliqURL": "Webhook URLの作成方法については、{0}をご覧ください。", + "confirmDeleteTagMsg": "このタグを本当に削除してよろしいですか?このタグが付けられたモニターは削除されません。", + "deleteMaintenanceMsg": "このメンテナンスを本当に削除していいですか?", + "promosmsLogin": "APIログイン名", + "pushyAPIKey": "シークレットAPI Key", + "Message Title": "メッセージタイトル", + "Setup Proxy": "プロキシを設定する", + "Proxy server has authentication": "プロキシサーバーは認証が必要", + "Edit Tag": "タグを編集", + "Add API Key": "API Keyを追加", + "Expires": "有効期限", + "disableAPIKeyMsg": "本当にこのAPIキーを無効化しますか?", + "install": "インストール", + "affectedMonitorsDescription": "メンテナンスによって影響を受けるモニターを選択してください", + "default: notify all devices": "デフォルト:すべてのデバイスに通知する", + "Trigger type:": "トリガータイプ:", + "Event data:": "イベントデータ:", + "backupOutdatedWarning": "非推奨: 多くの機能に変更があり、バックアップ機能の開発が一部滞っているため、完全なバックアップの作成や復元ができません。", + "backupRecommend": "代わりにボリュームまたはデータフォルダ(./data/)を直接バックアップしてください。", + "recurringInterval": "インターバル", + "Recurring": "繰り返し", + "lineDevConsoleTo": "LINE Developersコンソール - {0}", + "Basic Settings": "基本設定", + "User ID": "User ID", + "Huawei": "Huawei", + "Device Token": "デバイストークン", + "recurringIntervalMessage": "毎日1回実行する|{0} 日に1回実行する", + "Add New Tag": "新しいタグを追加", + "statusPageMaintenanceEndDate": "終了日", + "Body Encoding": "ボディエンコード", + "Learn More": "さらに詳しく", + "infiniteRetention": "保持期間を無制限にしたい場合は、0に設定してください。", + "Display Timezone": "表示タイムゾーン", + "startDateTime": "開始日時", + "User Key": "ユーザーキー", + "SecretKey": "シークレットキー", + "Home": "ホーム", + "webhookBodyCustomOption": "カスタムbody", + "octopushPhoneNumber": "電話番号 (初期フォーマット, 例: +33612345678)", + "Topic": "トピック", + "pushoverMessageTtl": "メッセージTTL(秒)", + "apiCredentials": "API認証情報", + "Economy": "エコノミー", + "statusPageRefreshIn": "{0}後に再読み込みします", + "filterActivePaused": "停止中", + "filterActive": "有効", + "Example:": "例: {0}", + "Read more:": "さらに: {0}", + "Status:": "ステータス: {0}", + "Enable TLS": "TLS 有効", + "AccessKeyId": "アクセスキーID", + "SecretAccessKey": "アクセスキーシークレット", + "PhoneNumbers": "携帯電話番号", + "Date and Time": "日時", + "chromeExecutableAutoDetect": "自動検出", + "More info on:": "詳細はこちら: {0}", + "Cannot connect to the socket server": "ソケットサーバーに接続できません", + "Reconnecting...": "再接続中...", + "endDateTime": "終了日時", + "cronSchedule": "スケジュール ", + "Edit Maintenance": "メンテナンスの編集", + "WebHookUrl": "ウェブフックUrl", + "Notification Service": "通知サービス", + "atLeastOneMonitor": "最低一つは影響を受けるモニターを選択してください", + "Json Query": "Jsonクエリ", + "octopushSMSSender": "SMS送信者名:3~11文字の英数字とスペース(a~zA~z0~9)", + "Lowcost": "低コスト", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "通知サービスの一覧からデバイス名を見つけるには、Home Assistantの「開発者ツール > サービス」から通知で検索してください。", + "Notify Channel": "チャンネル通知", + "Icon Emoji": "絵文字アイコン", + "setupDatabaseChooseDatabase": "どのデータベースを使いたいですか?", + "setupDatabaseEmbeddedMariaDB": "何も設定する必要はありません。この Docker イメージは設定済みの MariaDB が組み込まれています。Uptime Kuma はこのデータベースに unix ソケットを通じて接続します。", + "setupDatabaseSQLite": "小規模な開発のために推奨される小さなデータベースファイルです。v2.0.0 以前は、Uptime Kuma は SQLite を標準のデータベースとして使用していました。", + "tailscalePingWarning": "Tailscale Ping モニターを使用するためには、Uptime Kuma を Docker を利用せずインストールし、そのうえで Tailscale Client をサーバーにインストールしてください。", + "invertKeywordDescription": "キーワードが含まれているものではなく、含まれないものを探します。", + "setupDatabaseMariaDB": "外部の MariaDB データベースに接続するためには、データベースの接続情報を設定する必要があります。", + "dbName": "データベース名", + "Request Timeout": "リクエストタイムアウト", + "timeoutAfter": "{0} 秒後にタイムアウト", + "selectedMonitorCount": "選択済み: {0}", + "Long-Lived Access Token": "長期アクセストークン", + "Invert Keyword": "「含まない」キーワード", + "Expected Value": "期待値", + "dataRetentionTimeError": "保持期間は 0 か、それ以上である必要があります", + "settingUpDatabaseMSG": "データベースをセットアップしています。しばらくお待ちください。", + "Check/Uncheck": "選択あり/なし", + "Alphanumeric (recommended)": "半角英数字(推奨)", + "GrafanaOncallUrl": "Grafana オンコール URL", + "Command": "コマンド", + "Browser Screenshot": "ブラウザ・スクリーンショット", + "mongodbCommandDescription": "データベースに対して MongoDB コマンドを実行します。使用できるコマンドについては {documentation} を参照ください", + "2faDisabled": "二要素認証 (2FA) を無効にしました。", + "Telephone number": "電話番号", + "remoteBrowsersDescription": "リモートブラウザは Chromium を実行するための代替手段です。ローカルで実行する代わりに、browserless.io のようなサービスや、独自のサービスに接続します", + "max 15 digits": "最大15桁", + "What is a Remote Browser?": "リモート・ブラウザとは何ですか?", + "Remote Browsers": "リモート・ブラウザ", + "Remote Browser": "リモート・ブラウザ", + "wayToGetSevenIOApiKey": "app.seven.io > developer > api key > 緑色の追加ボタンの下にあるダッシュボードをご覧ください", + "senderSevenIO": "番号もしくは名前を送信しています", + "receiverSevenIO": "番号を受信しています", + "Search monitored sites": "監視対象サイトの検索", + "liquidIntroduction": "テンプレート機能は、Liquidテンプレート言語によって実現されています。使用方法については、{0}をご参照ください。", + "templateMsg": "通知の内容", + "templateHeartbeatJSON": "死活監視 (heartbeat) を記述している JSON オブジェクト", + "templateMonitorJSON": "監視対象", + "Remove the expiry notification": "有効期限の通知日を削除する", + "authInvalidToken": "無効なトークンです。", + "authIncorrectCreds": "無効なユーザー名かパスワードです。", + "self-hosted container": "セルフ・ホストのコンテナ", + "useRemoteBrowser": "リモート・ブラウザを使う", + "Add a Remote Browser": "リモート・ブラウザを追加する", + "remoteBrowserToggle": "デフォルトでは、Chromium は Uptime Kuma コンテナ内で実行されます。このスイッチを切り替えることで、リモート・ブラウザを使用できます。", + "Remote Browser not found!": "リモート・ブラウザがありません!", + "Select message type": "メッセージ・タイプを選択してください", + "Send to channel": "チャンネルに送信", + "Create new forum post": "新規フォーラムに投稿", + "needSignalAPI": "REST API に対応したシグナリング用のクライアントが必要です。", + "aboutIconURL": "「アイコン URL」に画像へのリンクを指定することで、デフォルトのプロフィール画像を上書きすることができます。「アイコン絵文字」が指定されている場合は使用されません。", + "octopushAPIKey": "コントロールパネルの HTTP API 認証情報にある「API キー」", + "2faEnabled": "二要素認証 (2FA) を有効にしました。", + "successAdded": "正常に追加されました。", + "successResumed": "正常に再開しました。", + "2faAlreadyEnabled": "二要素認証 (2FA) は、すでに有効になっています。", + "successDeleted": "正常に削除しました。", + "successPaused": "正常に一時停止しました。", + "successAuthChangePassword": "正常にパスワードを更新しました。", + "successEdited": "正常に編集できました。", + "successBackupRestored": "正常にバックアップから復元しました。", + "successEnabled": "正常に有効化しました。", + "tagNotFound": "タグが見つかりませんでした。", + "foundChromiumVersion": "Chromium/Chrome を見つけました。 Version: {0}", + "Select": "選択", + "Add a new expiry notification day": "有効期限の通知日を追加する", + "emailCustomSubject": "カスタム件名", + "emailTemplateMsg": "通知の内容", + "To Email": "Email へ", + "emailTemplateLimitedToUpDownNotification": "UP/DOWN 死活監視 (heartbeat) の場合のみ有効", + "deleteRemoteBrowserMessage": "本当にこのリモートブラウザをすべての監視から削除しますか?", + "Refresh Interval": "更新間隔", + "Refresh Interval Description": "ステータスページは {0} 秒ごとに全面更新されます", + "wayToGetLineChannelToken": "まず {0} にアクセスし、プロバイダーとチャネル(Messaging API)を作成します。次に、上記のメニューからアクセストークンとチャネルのユーザー ID を取得します。", + "aboutMattermostChannelName": "「チャンネル名」フィールドにチャンネル名を入力することで、Webhook から送信されるデフォルトのチャンネル名を上書きできます。これは Mattermost の Webhook 設定で有効にしておく必要があります。例 #other-channel", + "Saved.": "保存しました。", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "接続するサーバーのホスト名を入力するか、{local_mta} を使用する場合は {localhost} を入力します", + "e.g. {discordThreadID}": "例 {discordThreadID}", + "postToExistingThread": "既存のスレッド/フォーラムに投稿", + "forumPostName": "投稿先のフォーラム名", + "threadForumPostID": "投稿先のスレッド/フォーラム ID", + "whatHappensAtForumPost": "新規フォーラム投稿。既存の投稿がある場合は投稿しません。既存の投稿の中に投稿を作成するには「{option}」を使用してください", + "emailTemplateHeartbeatJSON": "死活監視 (heartbeat) を記述している JSON オブジェクト", + "leave blank for default subject": "空の場合はデフォルトの件名を使用します", + "cellsyntSplitLongMessages": "長文メッセージは最大 6 つに分割してください。153 x 6 = 918文字です。", + "Allow Long SMS": "ロングコード SMS を許可する", + "cellsyntDestination": "受信者の電話番号。00 に続けて国番号を付ける国際形式である必要があります。たとえば、英国の番号 07920 110 000 の場合は 00447920110000(合計17桁まで)になります。HTTP リクエストごとに指定できる受信者数は、カンマ区切りで最大 25,000 件です。", + "emailTemplateMonitorJSON": "監視対象を記述している JSON オブジェクト", + "successDisabled": "正常に無効化しました。", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "長期有効なアクセストークンは、プロフィール名(左下)をクリックし、画面を下までスクロールして「トークンの作成」をクリックすることで作成できます。", + "Effective Date Range": "有効範囲(オプション)", + "DateTime Range": "日付範囲", + "Maintenance Time Window of a Day": "今日のメンテナンス時間のウィンドウ", + "octopushLogin": "コントロールパネルの HTTP API 認証情報にある「ログイン」", + "notificationRegional": "リージョナル(一部の地域のみ)", + "pushOthers": "その他", + "Host URL": "ホストURL", + "enableNSCD": "NSCD (Name Service Cache Daemon) を有効にして、すべての DNS クエリをキャッシュする", + "successKeywordExplanation": "成功としてカウントされる MQTT のキーワード", + "DockerHostRequired": "この監視の Docker ホストを設定してください。", + "noDockerHostMsg": "利用できません。Docker ホストを先にセットアップしてください。", + "styleElapsedTime": "経過時間を監視バーの下に表示", + "styleElapsedTimeShowNoLine": "表示(改行なし)", + "styleElapsedTimeShowWithLine": "表示(改行あり)", + "lastDay": "最終日", + "lastDay1": "月末", + "lastDay2": "月末の 2 日前", + "lastDay3": "月末の 3 日前", + "lastDay4": "月末の 4 日前", + "chromeExecutable": "Chrome/Chromium の実行ファイル", + "chromeExecutableDescription": "Docker を使用しており、Chromium がインストールされていない場合、インストールしてからテスト結果の表示するのに数分かかることがあります。1 GB の容量を必要とします。", + "jsonQueryDescription": "サーバーのJSONレスポンスから特定のデータを抽出するにはJSONクエリを使用してください。\"$\"で生のレスポンスが取得できます。抽出結果は文字列として期待結果と比較されます。ドキュメントについては{0}を参照し、クエリの検証には{1}を使用してください。", + "leave blank for default body": "空の場合はデフォルトの本文を使用します", + "apprise": "Apprise (50以上の通知サービスをサポートしています)", + "Apprise URL": "AppriseのURL", + "emailTemplateHostnameOrURL": "ホスト名/URL", + "emailCustomisableContent": "カスタマイズ可能なコンテンツ", + "emailCustomBody": "カスタム本文", + "emailTemplateServiceName": "サービス名", + "smtpLiquidIntroduction": "次の 2 つのテンプレート・フィールドは Liquid テンプレート言語で記述できます。これらの使い方は {0} にあります。以下は利用可能な変数です:", + "emailTemplateStatus": "ステータス", + "now": "現在", + "time ago": "{0}前", + "-year": "年", + "Json Query Expression": "Jsonクエリ表現", + "ignoredTLSError": "TLS/SSLエラーは無視されました", + "locally configured mail transfer agent": "独自設定されたメール転送エージェント", + "ignoreTLSErrorGeneral": "接続時のTLS/SSLエラーを無視する", + "successKeyword": "成功時のキーワード", + "pushViewCode": "Push モニターの使い方(コードを見る)", + "Reset Token": "トークンのリセット", + "templateLimitedToUpDownCertNotifications": "監視対象の UP/DOWN と証明書の有効期限通知でのみ利用可能", + "templateLimitedToUpDownNotifications": "監視対象の UP/DOWN の通知でのみ利用可能", + "webhookBodyPresetOption": "プリセット - {0}", + "Optional": "オプション", + "and": "かつ", + "From Email": "送信元メールアドレス", + "CurlDebugInfoProxiesUnsupported": "上記コマンド {curl} のProxyサポートは現在、実装されていません。", + "Your User ID": "あなたのユーザーID", + "programmingLanguages": "プログラミング言語", + "Debug": "デバッグ", + "Copy": "コピー", + "CopyToClipboardError": "クリップボードにコピーできません: {error}", + "CopyToClipboardSuccess": "コピーしました!", + "firewalls": "ファイアウォール", + "dns resolvers": "DNSリゾルバ", + "docker networks": "Dockerネットワーク", + "sameAsServerTimezone": "サーバーのタイムゾーンと同じ", + "cronExpression": "cron表記", + "invalidCronExpression": "不正なcron表記です: {0}", + "Single Maintenance Window": "単体メンテナンス・ウィンドウ", + "shrinkDatabaseDescriptionSqlite": "SQLiteデータベースで{vacuum}をトリガーしてください。{auto_vacuum}は既に有効化されていますが、これは{vacuum}コマンドが行うようなデータベースのデフラグメントや個々のデータベースページの再パックを行いません。", + "statusPageSpecialSlugDesc": "特別なスラッグ{0}:スラッグが指定されていない場合にこのページが表示されます", + "Automations can optionally be triggered in Home Assistant:": "オートメーションは Home Assistant を使って動作させることもできます:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "次にアクションを選択します。例えば、RGB ライトが赤になるようにシーンを切り替えるなどです。", + "wayToGetDiscordThreadId": "スレッド/フォーラム投稿IDを取得する方法は、チャンネルIDを取得する方法に似ています。IDの取得方法について詳しくはこちらをご覧ください{0}", + "Recipients": "受信者", + "Channel access token (Long-lived)": "チャンネルアクセストークン(長期)", + "grpcMethodDescription": "メソッド名はsayHello、checkのようにキャメルケースに変換されます。", + "rabbitmqNodesRequired": "このモニターのノードを設定してください。", + "rabbitmqNodesInvalid": "RabbitMQノードには完全修飾('http'で始まる)URLを使用してください。", + "RabbitMQ Username": "RabbitMQユーザー名", + "RabbitMQ Password": "RabbitMQパスワード", + "SendGrid API Key": "SendGrid APIキー", + "pushoversounds pushover": "Pushover (デフォルト)", + "pushoversounds gamelan": "Gamelan", + "pushoversounds persistent": "Persistent (long)", + "wayToGetKookBotToken": "アプリケーション作成とボットトークンの取得はこちら: {0}", + "pushoverDesc1": "緊急優先度(2)を設定すると、リトライ中のタイムアウトはデフォルトで30秒に設定されます。また、1時間が経過すると再試行は行われなくなります。", + "octopushTypeLowCost": "Low Cost (低速 - 時々オペレーターにブロックされることがあります)", + "octopushLegacyHint": "Octopushの旧バージョン(2011-2020)を使用していますか?それとも新バージョンを使用していますか?", + "You can divide numbers with": "数字を次のキーワードで分割できます", + "goAlertInfo": "GoAlertはオンコールスケジューリング、自動エスカレーション、通知(SMSや音声通話など)を行うオープンソースのアプリケーションです。適切な人物を、適切な方法で、適切なタイミングで自動的に招集できます! {0}", + "Mention group": "メンション {group}", + "matrixDesc2": "新しいユーザーを作成し、自分のMatrixユーザーのアクセストークンを使用しないことを強く推奨します。アクセストークンを使用すると、アカウント自体や参加している全てのルームにフルアクセスできてしまいます。代わりに、新しいユーザーを作成して通知を受け取りたいルームにのみ招待してください。アクセストークンは{0}を実行すると取得できます。", + "aboutSlackUsername": "メッセージ送信者の表示名を変更します。誰かにメンションしたい場合は、そのメンションをモニター表示名に含めてください。", + "aboutChannelName": "Webhookチャンネルをバイパスしたい場合は「チャンネル名」フィールドにWebhookチャンネル名を入力してください。例:#other-channel", + "wayToGetPagerDutyKey": "キーを取得するには、サービス -> サービスディレクトリ -> (サービスを選択) -> インテグレーション -> インテグレーション と進み、「Events API V2」で検索してください。詳細情報はこちら: {0}", + "smseagleRecipient": "受信者(複数の場合はカンマで区切る)", + "smspartnerPhoneNumberHelptext": "番号は次のような国際形式で入力してください: {0}, {1}。複数の場合は{2}で区切ってください。", + "Server URL should not contain the nfty topic": "サーバーURLはntfyトピックを含むべきではありません", + "onebotSafetyTips": "安全のためにアクセストークンを設定してください", + "PushDeer Server": "PushDeerサーバー", + "pushDeerServerDescription": "公式サーバーを使用する場合は空白のままにしてください", + "apiKeyAddedMsg": "APIキーが追加されました。APIキーは再表示できないため、メモなどに保存してください。", + "wayToGetPagerTreeIntegrationURL": "PagerTreeでUptime Kumaインテグレーションを作成後、エンドポイントをコピーしてください。詳細は {0}", + "twilioAuthToken": "認証トークン/APIキーシークレット", + "Show Clickable Link Description": "チェックを入れると、このステータスページにアクセスできる全ての人がモニターURLにアクセスできるようになります。", + "Open Badge Generator": "オープンバッジの生成", + "Badge Generator": "{0}のバッジ生成", + "Badge Label Color": "バッジラベルの色", + "Badge Up Color": "Upを示すバッジの色", + "Badge Maintenance Color": "Maintenanceを示すバッジの色", + "monitorToastMessagesLabel": "モニターのトースト通知", + "monitorToastMessagesDescription": "モニターのトースト通知は、指定された秒数後に消えます。-1に設定するとタイムアウトが無効になり、0に設定するとトースト通知が無効になります。", + "Pick a SASL Mechanism...": "SASLメカニズムを選択してください…", + "noGroupMonitorMsg": "利用できません。先にグループモニターを作成してください。", + "wayToGetFlashDutyKey": "UptimeKumaとFlashdutyを統合するには: Channels > Select a channel > Integrations > Add a new integrationでUptime Kumaを選択し、Push URLをコピーしてください。", + "cacheBusterParamDescription": "キャッシュをスキップするためにランダム生成したパラメータ。", + "gamedigGuessPortDescription": "Valve Server Query Protocolで使用されるポートはクライアントポートとは異なる場合があります。モニターがサーバーに接続できない場合は、この設定を試してください。", + "receiverInfoSevenIO": "宛先の電話番号がドイツ国内のものではない場合は、番号の前に国番号を付ける必要があります(例:国番号が1の米国への場合は、017612121212ではなく117612121212と入力してください)", + "wayToWriteWhapiRecipient": "電話番号には国コードが必要ですが、先頭のプラス記号は不要です({0})。連絡先IDの場合({1})、グループIDの場合({2})。", + "callMeBotGet": "{0}、{1}、および{2}のエンドポイントを生成できます。レート制限がかかる可能性があるため注意してください。レート制限は次のようになります: {3}", + "gtxMessagingFromHint": "携帯電話の受信者にはメッセージの送信者としてTPOAが表示されます。最大11文字の英数字、ショートコード、ローカルロングコード、または国際電話番号({e164}、{e212}、または{e214})が使用できます", + "cellsyntOriginatortypeNumeric": "数値(最大15桁)で、先頭に00を付けない国際フォーマットの電話番号(例:イギリスの番号07920 110 000は447920110000として設定する必要があります)。受信者はメッセージに返信できます。", + "snmpCommunityStringHelptext": "この文字列はSNMP対応デバイスへの認証とアクセス制御用のパスワードとして機能します。SNMPデバイスの文字列設定と一致させてください。", + "snmpOIDHelptext": "センサーや監視したいステータス用のOIDを入力してください。OIDが不明な場合はMIBブラウザやSNMPソフトウェアなどのネットワーク管理ツールを使用してください。", + "threemaRecipientTypePhoneFormat": "E.164形式、先頭の+無し", + "threemaBasicModeInfo": "注記: このインテグレーションは、ベーシックモード(サーバーベースの暗号化)のThreema Gatewayを使用します。詳細は{0}。", + "privateOnesenderDesc": "電話番号が有効であることを確認してください。プライベート電話番号にメッセージを送信するには次のように入力してください。例:628123456789", + "groupOnesenderDesc": "グループIDが有効であることを確認してください。グループIDにメッセージを送信するには次のように入力してください。例:628123456789-342345", + "Lost connection to the socket server.": "ソケットサーバーへのコネクションが切れました。", + "signl4Docs": "SIGNL4の設定方法やSIGNL4のWebhook URLを取得する方法の詳細は{0}で確認できます。", + "equals": "一致", + "contains": "含む", + "less than or equal to": "以下", + "Doorbell": "Doorbell", + "Custom sound to override default notification sound": "デフォルトの通知音を上書きするカスタムサウンド", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "即時通知はデバイスが「おやすみモード」の場合でも通知されます。", + "RabbitMQ Nodes": "RabbitMQ管理ノード", + "rabbitmqNodesDescription": "RabbitMQ管理ノードのURLを、プロトコルとポートを含めて入力してください。例: {0}", + "rabbitmqHelpText": "モニターを使用するには、RabbitMQの設定で管理プラグインを有効にする必要があります。詳細はこちら: {rabitmq_documentation}。", + "pushoversounds bike": "Bike", + "pushoversounds bugle": "Bugle", + "pushoversounds cashregister": "Cash Register", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Cosmic", + "pushoversounds falling": "Falling", + "pushoversounds incoming": "Incoming", + "pushoversounds magic": "Magic", + "pushoversounds mechanical": "Mechanical", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Siren", + "pushoversounds spacealarm": "Space Alarm", + "pushoversounds tugboat": "Tug Boat", + "pushoversounds alien": "Alien Alarm (long)", + "pushoversounds climb": "Climb (long)", + "pushoversounds echo": "Pushover Echo (long)", + "pushoversounds updown": "Up Down (long)", + "pushoversounds vibrate": "Vibrate Only", + "pushoversounds none": "None (silent)", + "wayToGetKookGuildID": "Kookの設定で「開発者モード」をオンにし、ギルドを右クリックしてIDを取得します", + "Guild ID": "ギルドID", + "pushoverDesc2": "別デバイスに通知を送信したい場合、「デバイス」フィールドに入力してください。", + "octopushTypePremium": "Premium (高速 - アラート用に推奨)", + "checkPrice": "{0}の料金:", + "Check octopush prices": "octopushの価格を確認する {0}。", + "LunaSea Device ID": "LunaSeaデバイスID", + "Strategy": "戦略", + "Free Mobile User Identifier": "Free MobileユーザID", + "Free Mobile API Key": "Free Mobile APIキー", + "Proto Service Name": "Protoサービス名", + "Proto Method": "Protoメソッド", + "Proto Content": "Protoコンテンツ", + "high": "高", + "SendKey": "SendKey", + "SMSManager API Docs": "SMSManager APIドキュメント", + "Base URL": "ベースURL", + "goAlertIntegrationKeyInfo": "サービスの汎用APIインテグレーションキーを「aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee」の形式で取得してください。通常、URLのトークンパラメータの値です。", + "TemplateCode": "テンプレートコード", + "SignName": "署名", + "Sms template must contain parameters: ": "SMSテンプレートは次のパラメータを含む必要があります: ", + "Bark API Version": "Bark APIバージョン", + "Bark Endpoint": "Barkエンドポイント", + "Bark Group": "Barkグループ", + "For safety, must use secret key": "安全のためにシークレットキーを使用する必要があります", + "Mentioning": "メンション", + "Don't mention people": "メンションしない", + "Platform": "プラットフォーム", + "High": "高", + "promosmsTypeEco": "SMS ECO - 安価ですが低速で混雑 する場合があります。使用はポーランド在住の受信者に限られます。", + "promosmsTypeFlash": "SMS FLASH - メッセージは受信者のデバイスに自動的に表示されます。使用はポーランド在住の受信者に限られます。", + "promosmsTypeFull": "SMS FULL - SMSのプレミアム版です。送信者名を使用できます(名前を先に登録する必要があります)。アラート用途として信頼性があります。", + "promosmsTypeSpeed": "SMS SPEED - システム内の最も高い優先度を持ちます。非常に迅速で信頼性がありますが、コストは高くなります(SMS FULLの約2倍の料金)。", + "promosmsPhoneNumber": "電話番号 (Polishの受信者であればエリアコードを省略できます)", + "promosmsSMSSender": "SMS送信者名: 事前登録済みの名前、または次の既定値のいずれか(InfoSMS, SMS Info, MaxSMS, INFO, SMS)", + "Feishu WebHookUrl": "Feishu Webhook URL", + "matrixHomeserverURL": "ホームサーバーURL (http(s)://で始まり、オプションでポート番号を含む)", + "Internal Room Id": "内部ルームID", + "matrixDesc1": "内部ルームIDはルーム設定の詳細セクションでを確認でき、「!QMdRCpUIfLwsfjxye6:home.server」のような形式になっています。", + "aboutNotifyChannel": "チャンネル通知はメンバーの状態に関わらず、チャンネルの全てのメンバーに通知を送信します。", + "setup a new monitor group": "新しいモニターグループをセットアップ", + "openModalTo": "{0}へのモーダルを開く", + "Add a domain": "ドメインを追加", + "Remove domain": "ドメインの削除 '{0}'", + "signalImportant": "重要: 受信者にグループと番号を混在させることはできません!", + "aboutWebhooks": "Webhookに関する追加の情報はこちら: {0}", + "WeCom Bot Key": "WeCom Botキー", + "promosmsAllowLongSMS": "長いSMSメッセージを許可", + "alertaApiKey": "APIキー", + "Group": "グループ", + "smtpDkimDesc": "使用方法はNodemailer DKIMの{0}を参照してください。", + "documentation": "ドキュメント", + "smtpDkimKeySelector": "キーセレクター", + "smtpDkimPrivateKey": "プライベートキー", + "smtpDkimheaderFieldNames": "署名するヘッダーキー(オプション)", + "Integration Key": "Integrationキー", + "Integration URL": "Integration URL", + "Auto resolve or acknowledged": "Auto resolve または acknowledged", + "do nothing": "何もしない", + "auto acknowledged": "自動確認済み", + "auto resolve": "自動解決済み", + "alertaApiEndpoint": "APIエンドポイント", + "alertaEnvironment": "環境", + "alertaAlertState": "アラート状態", + "alertaRecoverState": "リカバリー状態", + "serwersmsAPIUser": "APIユーザ名 (webapi_ プレフィックスを含む)", + "serwersmsAPIPassword": "APIパスワード", + "serwersmsPhoneNumber": "電話番号", + "serwersmsSenderName": "SMS送信者名(カスタマーポータル経由で登録された)", + "smseagleTo": "電話番号", + "smseagleGroup": "電話帳グループ名", + "smseagleContact": "電話帳連絡先名", + "smseagleRecipientType": "受信者タイプ", + "smseagleToken": "APIアクセストークン", + "smseagleUrl": "SMSEagleデバイスURL", + "smseagleEncoding": "Unicodeで送信 (default=GSM-7)", + "smseaglePriority": "メッセージ優先度 (0-9, 最大優先度 = 9)", + "smspartnerApiurl": "APIキーはダッシュボードから確認できます: {0}", + "smspartnerPhoneNumber": "電話番号", + "smspartnerSenderName": "SMS送信者名", + "smspartnerSenderNameInfo": "3文字以上11文字以下の通常文字", + "Recipient Number": "受信者番号", + "From Name/Number": "送信者名/送信元番号", + "Leave blank to use a shared sender number.": "共有送信番号を使用する場合は空白のままにしてください。", + "Octopush API Version": "Octopush APIバージョン", + "Legacy Octopush-DM": "旧Octopush-DM", + "ntfy Topic": "ntfyトピック", + "onebotHttpAddress": "OneBot HTTPアドレス", + "onebotMessageType": "OneBotメッセージタイプ", + "onebotGroupMessage": "グループ", + "onebotPrivateMessage": "プライベート", + "onebotUserOrGroupId": "グループ/ユーザーID", + "PushDeer Key": "PushDeerキー", + "wayToGetClickSendSMSToken": "APIユーザー名とAPIキーは{0}から取得できます。", + "Custom Monitor Type": "カスタムモニタータイプ", + "Don't expire": "期限をつけない", + "Continue": "続ける", + "Add Another": "追加", + "apiKey-active": "アクティブ", + "apiKey-expired": "期限切れ", + "apiKey-inactive": "インアクティブ", + "pagertreeIntegrationUrl": "インテグレーションURL", + "pagertreeUrgency": "緊急度", + "pagertreeSilent": "サイレント", + "pagertreeLow": "低", + "pagertreeMedium": "中", + "pagertreeHigh": "高", + "pagertreeCritical": "クリティカル", + "pagertreeResolve": "自動解決", + "pagertreeDoNothing": "何もしない", + "lunaseaTarget": "ターゲット", + "lunaseaDeviceID": "デバイスID", + "lunaseaUserID": "ユーザーID", + "ntfyAuthenticationMethod": "認証方法", + "ntfyPriorityHelptextAllEvents": "全てのイベントは最大の優先度で送信されます", + "ntfyPriorityHelptextAllExceptDown": "優先度が{1}に設定された{0}イベントを除く、全てのイベントは設定された優先度で送信されます。", + "ntfyUsernameAndPassword": "ユーザー名とパスワード", + "twilioAccountSID": "アカウントSID", + "twilioApiKey": "APIキー(オプション)", + "twilioFromNumber": "送信元番号", + "twilioToNumber": "送信先番号", + "Monitor Setting": "{0}のモニター設定", + "Show Clickable Link": "クリックできるリンクを表示", + "Badge Type": "バッジタイプ", + "Badge Duration (in hours)": "バッジ間隔(時間単位)", + "Badge Label": "バッジラベル", + "Badge Prefix": "バッジのプレフィックス", + "Badge Suffix": "バッジのサフィックス", + "Badge Color": "バッジの色", + "Badge Label Prefix": "バッジラベルのプリフィックス", + "Badge Preview": "バッジのプレビュー", + "Badge Label Suffix": "バッジラベルのサフィックス", + "Badge Pending Color": "Pendingを示すバッジの色", + "Badge Down Color": "Downを示すバッジの色", + "Badge Warn Color": "Warnを示すバッジの色", + "Badge Warn Days": "バッジをWarn表示にするまでの日数", + "Badge Down Days": "バッジをDown表示にするまでの日数", + "Badge Style": "バッジスタイル", + "Badge value (For Testing only.)": "バッジの値(テスト用)", + "Monitor Group": "モニターグループ", + "toastErrorTimeout": "エラー通知のタイムアウト", + "toastSuccessTimeout": "成功通知のタイムアウト", + "Kafka Brokers": "Kafkaブローカー", + "Enter the list of brokers": "ブローカーのリストを入力してください", + "Press Enter to add broker": "エンターを押してブローカーを追加してください", + "Kafka Topic Name": "Kafkaトピック名", + "Kafka Producer Message": "Kafka Producerのメッセージ", + "Enable Kafka SSL": "Kafka SSLを有効にする", + "Enable Kafka Producer Auto Topic Creation": "Kafka Producerのトピック自動生成を有効にする", + "Kafka SASL Options": "Kafka SASLオプション", + "Mechanism": "メカニズム", + "Authorization Identity": "認証ID", + "AccessKey Id": "アクセスキーID", + "Secret AccessKey": "シークレットアクセスキー", + "Session Token": "セッショントークン", + "Close": "閉じる", + "Request Body": "リクエストボディ", + "FlashDuty Severity": "重要度", + "nostrRelays": "Nostrリレー", + "nostrRelaysHelp": "1行につき1つのリレーURL", + "nostrSender": "送信者プライベートキー(nsec)", + "nostrRecipients": "受信者パブリックキー(npub)", + "nostrRecipientsHelp": "npubフォーマット、1行ごと", + "showCertificateExpiry": "証明書の有効期限を表示", + "noOrBadCertificate": "無効/不正証明書", + "cacheBusterParam": "{0}パラメータを追加する", + "gamedigGuessPort": "Gamedig: ポートを推測", + "Message format": "メッセージフォーマット", + "Send rich messages": "リッチメッセージを送信", + "Bitrix24 Webhook URL": "Bitrix24 Webhook URL", + "wayToGetBitrix24Webhook": "Webhookは次の手順で作成できます {0}", + "bitrix24SupportUserID": "Bitrix24のユーザIDを入力してください。ユーザIDはユーザプロファイルのリンクから取得できます。", + "authUserInactiveOrDeleted": "そのユーザーは非アクティブまたは削除されています。", + "apiKeySevenIO": "SevenIO APIキー", + "wayToGetWhapiUrlAndToken": "{0}から目的のチャンネルに入ることでAPI URLとトークンを取得できます", + "whapiRecipient": "電話番号/連絡先ID/グループID", + "API URL": "API URL", + "wayToGetHeiiOnCallDetails": "Trigger IDとAPIキーの取得方法はこちら: {documentation}", + "documentationOf": "{0}のドキュメント", + "gtxMessagingApiKeyHint": "APIキーは次のページで確認できます: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "送信元番号/Transmission Path Originating Address (TPOA)", + "To Phone Number": "送信先電話番号", + "gtxMessagingToHint": "先頭に「+」を付けた国際フォーマット({e164}、{e212}、または{e214})", + "Originator type": "発信者タイプ", + "cellsyntOriginatortypeAlphanumeric": "英数字の文字列(最大11文字の英数字)。受信者はメッセージに返信できません。", + "Originator": "発信者", + "cellsyntOriginator": "受信者の携帯電話にメッセージの発信者として表示されます。許可される値と機能はoriginatortypeパラメータに依存します。", + "Destination": "送信先", + "Community String": "コミュニティー文字列", + "OID (Object Identifier)": "OID (Object ID)", + "max 11 alphanumeric characters": "最大11文字の英数字", + "SNMP Version": "SNMPバージョン", + "Please enter a valid OID.": "有効なOIDを入力してください。", + "wayToGetThreemaGateway": "Threema Gateway用に登録可能です: {0}。", + "threemaRecipient": "受信者", + "threemaRecipientType": "受信タイプ", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypeIdentityFormat": "8文字", + "threemaRecipientTypePhone": "電話番号", + "threemaRecipientTypeEmail": "メールアドレス", + "threemaSenderIdentity": "Gateway-ID", + "threemaSenderIdentityFormat": "8文字、通常 * で始まります", + "threemaApiAuthenticationSecret": "Gateway-IDシークレット", + "apiKeysDisabledMsg": "認証が無効になっているため、APIキーは無効です。", + "Host Onesender": "Onesenderホスト", + "Token Onesender": "Onesenderトークン", + "Recipient Type": "受信タイプ", + "Private Number": "プライベート電話番号", + "Group ID": "グループID", + "wayToGetOnesenderUrlandToken": "OnesenderのWebサイトからURLとトークンを取得できます。詳細は{0}", + "Add Remote Browser": "リモートブラウザを追加", + "New Group": "新規グループ", + "Group Name": "グループ名", + "OAuth2: Client Credentials": "OAuth2: クライアントクレデンシャル", + "Authentication Method": "認証方法", + "Authorization Header": "認証ヘッダー", + "Form Data Body": "フォームデータボディ", + "OAuth Token URL": "OAuthトークンURL", + "Client ID": "クライントID", + "Client Secret": "クライアントシークレット", + "OAuth Scope": "OAuthスコープ", + "Optional: Space separated list of scopes": "オプション:スペースで区切られたスコープのリスト", + "Go back to home page.": "ホームページに戻る", + "No tags found.": "タグが見つかりません。", + "Cannot connect to the socket server.": "ソケットサーバに接続できません。", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL", + "conditionAddGroup": "グループを追加", + "conditionDeleteGroup": "グループを削除", + "conditionValuePlaceholder": "値", + "Condition": "条件", + "Conditions": "条件", + "conditionAdd": "条件を追加", + "conditionDelete": "条件を削除", + "not equals": "不一致", + "not contains": "含まない", + "starts with": "から始まる", + "not starts with": "から始まらない", + "ends with": "で終わる", + "not ends with": "で終わらない", + "less than": "未満", + "greater than": "より大きい", + "greater than or equal to": "以上", + "record": "レコード", + "Notification Channel": "通知チャンネル", + "Sound": "サウンド", + "Alphanumerical string and hyphens only": "英数字とハイフンのみ", + "Arcade": "Arcade", + "Correct": "Correct", + "Fail": "Fail", + "Harp": "Harp", + "Reveal": "Reveal", + "Bubble": "Bubble", + "Flute": "Flute", + "Money": "Money", + "Scifi": "Scifi", + "Clear": "Clear", + "Elevator": "Elevator", + "Guitar": "Guitar", + "Pop": "Pop", + "Time Sensitive (iOS Only)": "即時通知(iOSのみ)", + "From": "送信元", + "Can be found on:": "次の場所にあります: {0}", + "The phone number of the recipient in E.164 format.": "受信者の電話番号はE.164形式で入力してください。", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "返信を受け取るためには、テキスト送信者IDまたはE.164形式の電話番号のいずれかが必要です。", + "Separate multiple email addresses with commas": "メールアドレスが複数の場合はカンマで区切ってください", + "aboutKumaURL": "Uptime KumaのURLフィールドを空白のままにすると、URLフィールドにはデフォルトでプロジェクトのGitHubページが設定されます。", + "smtpDkimHashAlgo": "ハッシュアルゴリズム(オプション )", + "smtpDkimskipFields": "署名しないヘッダーキー(オプション)", + "Key Added": "追加キー", + "Bark Sound": "Bark通知音", + "Badge URL": "バッジURL", + "pushoversounds intermission": "Intermission", + "telegramServerUrl": "(任意)サーバーUrl", + "telegramServerUrlDescription": "Telegramのボットapiの制限を解除したり、ブロックされた地域(中国、イランなど)でアクセスする。詳しくは {0} をクリックしてください。デフォルト: {1}", + "wayToWriteWahaChatId": "電話番号の先頭にプラス記号を付けない国際電話番号({0})、コンタクトID ({1})、またはグループID ({2}) 。WAHAセッションからこのチャットIDに通知が送信されます。", + "wahaSession": "セッション", + "wahaChatId": "チャットID(電話番号/連絡先ID/グループID)", + "wayToGetWahaApiUrl": "WAHAインスタンスのURL。", + "wayToGetWahaApiKey": "APIキーはWAHAを実行するために使用したWHATSAPP_API_KEY環境変数の値です。", + "wayToGetWahaSession": "このセッションから WAHA はチャット ID に通知を送信します。WAHAダッシュボードで確認できます。", + "YZJ Webhook URL": "YZJ ウェブフック URL", + "YZJ Robot Token": "YZJ ロボットトークン", + "Plain Text": "平文", + "Message Template": "メッセージテンプレート", + "Template Format": "テンプレート形式", + "templateServiceName": "サービス名", + "templateHostnameOrURL": "ホスト名またはURL", + "templateStatus": "ステータス", + "telegramUseTemplate": "カスタムメッセージテンプレートを使用", + "telegramUseTemplateDescription": "有効にすると、メッセージはカスタムテンプレートを使って送信されます。", + "telegramTemplateFormatDescription": "Telegramではメッセージに異なるマークアップ言語を使用することができます。詳細はTelegram {0} を参照してください。", + "Font Twemoji by Twitter licensed under": "TwemojiフォントはTwitterライセンス下でライセンスされています", + "the smsplanet documentation": "smsplanetドキュメント", + "Phone numbers": "携帯電話番号", + "Sender name": "送信者名", + "smsplanetNeedToApproveName": "クライアントパネルでの承認が必要", + "smsplanetApiToken": "SMSPlanet APIのトークン", + "smsplanetApiDocs": "APIトークンの取得に関する詳細な情報は、{the_smsplanet_documentation}にあります。", + "Happy Eyeballs algorithm": "Happy Eyeballs アルゴリズム", + "Ip Family": "IPファミリー", + "ipFamilyDescriptionAutoSelect": "IPファミリーの決定に {happyEyeballs} を使用する。", + "Manual": "マニュアル", + "pingNumericLabel": "数値出力", + "pingGlobalTimeoutLabel": "グローバルタイムアウト", + "pingGlobalTimeoutDescription": "送信されたパケットに関係なく、pingが停止するまでの合計時間(秒)", + "pingPerRequestTimeoutLabel": "Pingごとのタイムアウト", + "pingIntervalAdjustedInfo": "パケット数、グローバルタイムアウト、Pingごとのタイムアウトに基づいて間隔を調整", + "OneChatUserIdOrGroupId": "OneChat ユーザーIDまたはグループID", + "OneChatBotId": "OneChat ボットID", + "Add Another Tag": "その他のタグを追加", + "Staged Tags for Batch Add": "一括追加用ステージタグ", + "Clear Form": "フォームをクリア", + "pause": "一時停止", + "tagNameExists": "この名前のシステムタグは既に存在します。リストから選択するか、別の名前を使用してください。", + "smseagleDocs": "ドキュメントまたはAPIv2の可用性をチェックする: {0}", + "OAuth Audience": "OAuth オーディエンス", + "Optional: The audience to request the JWT for": "オプション: JWTを要求するオーディエンス", + "pingCountDescription": "停止前に送信するパケット数", + "pingNumericDescription": "チェックした場合、シンボリックホスト名の代わりにIPアドレスが出力されます", + "pingPerRequestTimeoutDescription": "これは、1つのpingパケットが失われたとみなすまでの最大待機時間(秒)です", + "Disable URL in Notification": "通知のURLを無効にする", + "defaultFriendlyName": "新しい監視", + "smseagleGroupV2": "電話帳グループID", + "smseagleContactV2": "電話帳連絡ID", + "ntfyPriorityHelptextPriorityHigherThanDown": "通常の優先度は {0} 優先度より高い必要があります。優先度 {1} は優先度 {0} 優先度 {2} よりも高い", + "ntfyPriorityDown": "ダウンイベントの優先順位", + "pingCountLabel": "最大パケット", + "Add Tags": "タグを追加", + "tagAlreadyOnMonitor": "このタグ(名前と値)はモニター上に既にあるか、追加待ちです。", + "tagAlreadyStaged": "このタグ(名前と値)は、このバッチに対して既にステージングされています。", + "Use HTML for custom E-mail body": "カスタムEメール本文にHTMLを使用", + "smseagleMsgType": "メッセージタイプ", + "smseagleMsgSms": "SMS メッセージ (デフォルト)", + "smseagleMsgRing": "呼び出し音", + "smseagleMsgTts": "音声合成通話", + "smseagleMsgTtsAdvanced": "音声合成高度通話", + "smseagleDuration": "継続時間(秒)", + "smseagleTtsModel": "音声合成モデルID", + "smseagleApiType": "API バージョン", + "smseagleApiv1": "APIv1 (既存プロジェクトおよび下位互換性用)", + "smseagleApiv2": "APIv2 (新規統合に推奨)", + "smseagleComma": "複数指定する場合はコンマで区切ってください", + "SpugPush Template Code": "テンプレートコード", + "FlashDuty Push URL": "Push URL", + "FlashDuty Push URL Placeholder": "アラート統合ページからコピー", + "smtpHelpText": "SMTPSは『SMTP/TLSが機能しているかテスト』Ignore TLSは『プレーンテキストで接続』STARTTLSは『接続し、STARTTLSコマンドを発行し、サーバー証明書を検証』いずれもメールを送信しない。", + "Custom URL": "カスタムURL", + "customUrlDescription": "モニターのURLの代わりにクリック可能なURLとして使用されます。", + "OneChatAccessToken": "OneChat アクセストークン", + "mqttWebSocketPath": "MQTT WebSocket パス", + "mqttWebsocketPathInvalid": "正しい WebSocket パス形式を入力してください", + "mqttHostnameTip": "次のフォーマットを使用してください {hostnameFormat}", + "HTTP Method": "HTTP メソッド", + "Clear All Events": "すべてのイベントをクリア", + "clearAllEventsMsg": "すべてのモニターのイベントをすべて削除してもよろしいですか?これにより、関連するメッセージデータが完全に削除され、元に戻すことはできません。", + "No monitors found": "モニターはありません。", + "wayToGetBaleToken": "{0} からトークンを入手できます。", + "Path": "パス", + "Events cleared successfully": "イベントが正常にクリアされました。", + "Could not clear events": "{total} 個中 {failed} 個のイベントをクリアできませんでした", + "webhookPostMethodDesc": "モダンな HTTP サーバーでは POST が最もおすすめです。", + "webhookGetMethodDesc": "GET はデータをクエリパラメータとして送信し、Body の設定は許可しません。Uptime Kuma Push モニターのトリガーとして便利です。", + "evolutionInstanceName": "インスタンス名", + "descriptionHelpText": "内部ダッシュボードに表示されます。Markdownを使用できますが、表示前にサニタイズ処理(スペースとインデントは保持)されます。", + "auto-select": "自動選択", + "saveResponseDescription": "HTTPレスポンスを保存し、通知テンプレートで {templateVariable} として利用できるようにします", + "steamApiKeyDescriptionAt": "Steamゲームサーバーを監視するには、Steam Web APIキーが必要です。APIキーは {url} で登録できます", + "homeAssistantNotificationActionHelptext": "通知アクションの一覧は、Home Assistantの「設定 > 開発者ツール > アクション」で確認できます。「notify」で検索すると、該当するアクションが見つかります。「notify.」以降の部分のみを入力してください。例えば、アクション「notify.mobile_app_xyz」の場合は「mobile_app_xyz」と入力します。組み込みのモバイル通知については、「Send a notification」ではなく、「Send a notification via mobile_app_xyz」を探してください。", + "invalidHostnameOrIP": "ホスト名またはIPアドレスが無効です。ホスト名は有効なFQDNである必要があります。ワイルドカードは使用できません。アンダースコアを含めることや、末尾にドットを付けることは可能です。", + "wildcardOnlyForDNS": "ワイルドカードホスト名は、DNSモニターでのみサポートされています。", + "responseMaxLength": "応答の最大長(バイト)", + "wayToGetMaxToken": "MAXボットトークンやその他の詳細については、{0}から入手できます。", + "maxMessenger": "MAX メッセンジャー", + "maxApiUrlDescription": "MAX メッセンジャー のベース API URL。デフォルト: https://platform-api.max.ru", + "wayToGetMaxChatID": "メッセージを配信するMAX内のチャット識別子を指定してください。", + "maxUseTemplate": "カスタムメッセージテンプレートを使用する", + "maxTemplateFormatDescription": "MAXメッセンジャーは、プレーンテキスト、Markdown、およびHTML形式に対応しています。", + "ariaEditMaintenance": "このメンテナンススケジュールを編集する", + "lowIntervalWarning": "間隔を 20 秒未満に設定してもよろしいですか? 特にモニターの数が多い場合は、パフォーマンスが低下する可能性があります。", + "mariadbCaCertificateLabel": "CA 証明書", + "mariadbCaCertificateHelptext": "自己署名証明書を使用する場合は、PEM形式のCA証明書を貼り付けてください。データベースでパブリックCAによって署名された証明書を使用している場合は、空欄のままにしてください。", + "enableSSL": "SSL/TLSを有効にする", + "mariadbUseSSLHelptext": "データベースへの暗号化接続を有効にします。ほとんどのクラウドデータベースで必須です。", + "days": "{n} 日 | {n} 日", + "unknownDays": "不明な日", + "No incidents recorded": "インシデント記録はありません", + "Load More": "さらに読み込む", + "Loading...": "読み込み中...", + "Monitors": "{n} モニター | {n} モニター", + "logoutCurrentUser": "ログアウト {username}", + "Incident not found or access denied": "インシデントが見つからないかアクセスを拒否されました", + "Incident description": "インシデントの概要", + "Pinned incidents are shown prominently on the status page": "固定されたインシデントは、ステータスページで目立つように表示されます", + "Edit Incident": "インシデントを編集", + "Result": "結果", + "HeadersInvalidFormatBecause": "リクエストヘッダーは、{error} のため有効な JSON ではありません", + "BodyInvalidFormatBecause": "リクエスト本文は、{error} のため有効な JSON ではありません", + "mqttWebsocketPathExplanation": "MQTT を WebSocket 接続で使用するための WebSocket パス(例: /mqtt)", + "Resolve": "解決", + "Resolved": "解決済み", + "createdAt": "作成日: {date}", + "lastUpdatedAt": "最終更新: {date}", + "selectMonitorMsg": "操作を実行するモニターを選択してください", + "selectAllMonitorsAria": "全てのモニターを選択", + "deselectAllMonitorsAria": "全てのモニターの選択を解除する", + "Actions": "アクション", + "selectedMonitorCountMsg": "選択済み: {n} | 選択済み: {n}", + "Certificate Chain:": "証明書チェーン:", + "slug is not found": "スラグが見つかりません", + "Please input title": "タイトルを入力してください", + "dateCreatedAtFromNow": "作成日: {date} ({fromNow})", + "Leave blank to use status page title": "ステータスページのタイトルを使用する場合は、空欄のままにしてください", + "Please input content": "内容を入力してください", + "Examples:": "例: {0}", + "supportBaleChatID": "ダイレクトチャット/グループ/チャンネルのチャットIDに対応", + "wayToGetBaleChatID": "ボットにメッセージを送信し、こちらのURLにアクセスしてchat_idを確認することで、チャットIDを取得できます:", + "frontendVersionIs": "フロントエンドバージョン: {version}", + "cronScheduleDescription": "スケジュール: {description}", + "Duration (Minutes)": "期間 (Minutes)", + "ariaResumeMaintenance": "このメンテナンススケジュールを再開する", + "Clone Maintenance": "メンテンスを複製", + "ariaPauseMaintenance": "このメンテナンススケジュールを一時停止する", + "ariaCloneMaintenance": "このメンテナンススケジュールのコピーを作成する", + "certificateExpiryNotificationHelp": "設定で、何日前に通知するか設定できます。", + "API Token": "API トークン", + "RSS Title": "RSSタイトル", + "versionIs": "バージョン: {version}", + "Notification Action": "通知アクション", + "matrixUseTemplate": "カスタムメッセージテンプレートを使用する", + "matrixUseTemplateDescription": "有効にすると、メッセージはカスタムテンプレートを使用して送信されます。", + "example": "例", + "ariaDeleteMaintenance": "このメンテナンススケジュールを削除する", + "pingPacketSizeWarning": "Linuxでは、パケットサイズが16未満の場合、往復遅延時間が算出されません", + "maxUseTemplateDescription": "有効にすると、メッセージはカスタムテンプレートを使用して送信されます。", + "Only retry if status code check fails": "ステータスコードの確認に失敗した場合にのみ再試行する", + "ignoreSecWebsocketAcceptHeaderDescription": "WebSocketのアップグレードが成功した場合、サーバーがSec-WebSocket-Acceptヘッダーを含めないで応答できるようにします。", + "Ignore Sec-WebSocket-Accept header": "{0} ヘッダーを無視する", + "monitorTypeGameServer": "ゲームサーバー", + "monitorTypeDatabase": "データベース監視タイプ", + "monitorTypeSpecial": "スペシャル", + "Pin this incident": "このインシデントをピン留めする", + "retryOnlyOnStatusCodeFailureDescription": "この設定を有効にすると、HTTPステータスコードの確認に失敗した場合(例:サーバーがダウンしている場合)にのみ再試行が行われます。ステータスコードの確認は成功したがJSONクエリが失敗した場合は、再試行を行わずに、そのモニターは直ちにダウン状態としてマークされます。", + "wsSubprotocolDescription": "サブプロトコルのリストをカンマ区切りで入力してください。サブプロトコルに関する詳細については、 {documentation} をご参照ください", + "oracledbConnectionString": "Oracle Database: {connectionString}", + "hours": "{n}時間 | {n} 時間", + "minutes": "{n} 分 | {n} 分", + "minuteShort": "{n} 分 | {n} 分", + "years": "{n} 年 | {n} 年", + "wsCodeDescription": "ステータスコードに関する詳細については、 {rfc6455} を参照してください", + "Subprotocol(s)": "サブプロトコル", + "saveResponseForNotifications": "通知用にHTTPの成功レスポンスを保存する", + "saveErrorResponseForNotifications": "通知用にHTTPエラーレスポンスを保存する", + "responseMaxLengthDescription": "保存するレスポンスデータの最大サイズ。無制限にする場合は 0 に設定してください。これを超えるレスポンスは切り捨てられます。デフォルト: 1024 (1KB)", + "Resolver Server(s)": "リゾルバサーバー", + "Past Incidents": "過去のインシデント", + "Incident title": "インシデントタイトル", + "templateAvailableVariables": "利用可能な変数", + "hostnameCannotBeIP": "DNSのホスト名はIPアドレスにすることはできません。もしかして「リゾルバ」フィールドを使用しようとしたのでしょうか?", + "invalidDNSHostname": "ホスト名が不正です。ホスト名は有効なFQDNである必要があります。ワイルドカードを使用したり、アンダースコアを含めたり、末尾にドットを付けたりすることができます。", + "invalidURL": "無効なURL", + "lastUpdatedAtFromNow": "最終更新: {date} ({fromNow})", + "deleteIncidentMsg": "このインシデントを削除してもよろしいですか?", + "Cloud ID": "Cloud ID", + "See Jira Cloud Docs": "Jira Cloud ドキュメントを参照", + "certHostnameMismatch": "証明書のホスト名がモニターのURLと一致しません。", + "sipsakPingWarning": "SIP Options Pingモニターを使用するには、Dockerを使用せずにUptime Kumaをインストールし、さらにサーバーにSipsakクライアントをインストールする必要があります。", + "Metadata": "メタデータ", + "notificationHomeAutomation": "ホームオートメーション", + "notificationOther": "その他", + "Click Deploy → New deployment → Web app": "デプロイ→新しいデプロイ→ウェブアプリを選択", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "「次のユーザーとして実行」を「自分」、「アクセスできるユーザー」を「全員」に設定", + "Paste the script code (see below)": "下記のスクリプトコードを貼り付けてください", + "Template plain text instead of using cards": "カードの代わりにテンプレートのプレーンテキストを使用", + "issueWithGoogleChatOnAndroidHelptext": "これにより以下のようなバグを回避することが可能になります。{issuetackerURL}", + "Failed to copy to clipboard": "クリップボードへのコピーに失敗しました", + "Quick Setup Guide": "クイックセットアップガイド", + "checkPriceAt": "{service}の価格はこちら: {url}", + "GRPC Options": "GRPCオプション", + "Bearer Token": "Bearer Token", + "Ignore STARTTLS": "STARTTLSを無視する", + "Use STARTTLS": "STARTTLSを使用", + "notificationChatPlatforms": "チャットプラットフォーム", + "notificationPushServices": "プッシュ通知サービス", + "notificationEmail": "Email", + "notificationSmsServices": "SMSサービス", + "notificationIncidentManagement": "インシデント管理", + "teamsEnableTags": "タグを含める", + "Google Apps Script Webhook URL": "Google Apps Script Webhook URL", + "Copied to clipboard!": "クリップボードにコピーしました!", + "notificationUniversal": "汎用", + "Copy to Clipboard": "クリップボードにコピー", + "Connection Timeout": "コネクションタイムアウト", + "SMTP Security": "SMTPセキュリティ", + "Deploy a Google Apps Script as a web app and paste the URL here": "Google Apps Scriptをウェブアプリとしてデプロイし、URLをここにペーストしてください", + "Open your Google Spreadsheet": "Googleスプレッドシートを開きます", + "Go to Extensions → Apps Script": "拡張機能→Apps Scriptを選択", + "Google Apps Script Code": "Google Apps Scriptコード", + "Copy the web app URL and paste it above": "ウェブアプリのURLをコピーして上に貼り付けてください", + "Disable STARTTLS": "STARTTLSを無効化", + "disableSTARTTLSDescription": "STARTTLSをサポートしていないSMTPサーバーでは、このオプションを設定してください。これにより、暗号化されていないコネクションでメールが送信されます。", + "Endpoint": "エンドポイント", + "deleteMonitorsMsg": "選択した監視を削除してよろしいですか?", + "Channel": "チャンネル", + "Client Key": "Clientキー", + "Action Group ID": "アクショングループID", + "Indigo URL": "インディゴURL", + "Variable ID": "変数ID" +} diff --git a/src/lang/ka.json b/src/lang/ka.json new file mode 100644 index 0000000..f06ea5c --- /dev/null +++ b/src/lang/ka.json @@ -0,0 +1,626 @@ +{ + "Dashboard": "საინფორმაციო დაფა", + "Help": "დახმარება", + "New Update": "ახალი განახლება", + "Language": "ენა", + "Appearance": "გარეგნობა", + "Theme": "თემა", + "Game": "თამაში", + "Version": "ვერსია", + "Quick Stats": "სწრაფი სტატისტიკა", + "Up": "ჩართ", + "Pending": "რიგშია", + "languageName": "ქართული", + "Settings": "მორგება", + "General": "ზოგადი", + "Check Update On GitHub": "განახლების შემოწმება GitHub-ზე", + "List": "სია", + "Add": "დამატება", + "Add New Monitor": "ახალი მონიტორის დამატება", + "Down": "გამორთ", + "setupDatabaseChooseDatabase": "რომელი მონაცემთა ბაზის გამოყენება გსურთ?", + "here": "აქ", + "Required": "აუცილებელია", + "high": "მაღალი", + "greater than": "მეტი, ვიდრე", + "less than or equal to": "ნაკლები ან ტოლი", + "greater than or equal to": "მეტი ან ტოლი", + "record": "ჩანაწერი", + "Notification Channel": "გაფრთხილების არხი", + "Sound": "ხმა", + "less than": "ნაკლები, ვიდრე", + "-hour": "-საათი", + "Friendly Name": "მეგობრული სახელი", + "hour": "საათი", + "-year": "-წელი", + "Response": "პასუხი", + "Ping": "პინგი", + "Monitor Type": "მონიტორის ტიპი", + "Keyword": "საკვანძო სიტყვა", + "Unknown": "უცნობი", + "dbName": "მონაცემთა ბაზის სახელი", + "Home": "საწყისი", + "Setup Notification": "შეტყობინების მორგება", + "Notification Type": "გაფრთხილების ტიპი", + "Schedule maintenance": "რემონტის დაგეგმვა", + "Notifications": "გაფრთხილებები", + "Search Engine Visibility": "საძიებო სისტემებში ხილვადობა", + "Discourage search engines from indexing site": "საძიებო სისტემებისთვის საიტის დაინდექსების აკრძალვა", + "Entry Page": "საწყისი გვერდი", + "Monitor History": "მონიტორის ისტორია", + "telegramSendSilentlyDescription": "აგზავნის შეტყობინებას ჩუმად. მომხმარებელი შეტყობინებას ხმის გარეშე მიიღებს.", + "telegramProtectContentDescription": "თუ ჩართულია Telegram-ში ბოტის შეტყობინებები დაცული იქნება გადამისამართებისა და შენახვისაგან.", + "Body Encoding": "სხეულის კოდირება", + "Expires": "ვადა", + "Auto Get": "ავტომატური მიღება", + "Display Timezone": "დროის სარტყლის ჩვენება", + "notificationRegional": "რეგიონალური", + "Clone Monitor": "მონიტორის ასლის შექმნა", + "Clone": "ასლი", + "cloneOf": "{0}-ის ასლი", + "API Keys": "API-ის გასაღებები", + "Add API Key": "API-ის გასაღების დამატება", + "supportTelegramChatID": "პირდაპირი ჩატის / ჯგუფის / არხის ჩატის ID-ის მხარდაჭერა", + "Powered by": "მხარდაჭერილია", + "Proxies": "პროქსიები", + "Reverse Proxy": "უკუპროქსი", + "Bot Token": "ბოტის ტოკენი", + "Chat ID": "ჩატის ID", + "telegramMessageThreadID": "(არასავალდებულო) შეტყობინების ნაკადის ID", + "telegramProtectContent": "გადამისამართებისგან/შენახვისგან დაცვა", + "Save": "შენახვა", + "setupDatabaseMariaDB": "გარე MariaDB მონაცემთა ბაზასთან კავშირი. საჭიროა მონაცემთა ბაზასთან დასაკავშირებელი ინფორმაციის შევსება.", + "settingUpDatabaseMSG": "მიმდინარეობს მონაცემთა ბაზის მორგება. ამას, შეიძლება, გარკვეული დრო დასჭირდეს. გამოიჩინეთ მოთმინება.", + "Cannot connect to the socket server": "სოკეტის სერვერთან დაკავშირება შეუძლებელია", + "Reconnecting...": "თავიდან დაკავშირება...", + "Primary Base URL": "ძირითადი საბაზისო URL", + "Maintenance": "რემონტი", + "statusMaintenance": "რემონტი", + "Auto": "ავტო", + "None": "არცერთი", + "Timezone": "დროის სარტყელი", + "Leave": "გასვლა", + "Confirm": "დადასტურება", + "Yes": "დიახ", + "No": "არა", + "Password": "პაროლი", + "Email": "ელფოსტა", + "Test": "ტესტი", + "Export": "გატანა", + "Import": "შემოტანა", + "Create": "შექმნა", + "Events": "მოვლენები", + "Heartbeats": "გულისცემები", + "Options": "მორგება", + "Token": "ტოკენი", + "Tags": "ჭდეები", + "color": "ფერი", + "Light": "ღია", + "Dark": "მუქი", + "Normal": "ნორმალური", + "Bottom": "ქვედა", + "Username": "მომხმარებლის სახელი", + "notAvailableShort": "N/A", + "Active": "აქტიური", + "Inactive": "არააქტიური", + "Status": "სტატუსი", + "DateTime": "დრო და თარიღი", + "Message": "შეტყობინება", + "-day": "-დღე", + "Advanced": "დამატებით", + "Overwrite": "თავზე გადაწერა", + "pauseDashboardHome": "პაუზა", + "Pause": "პაუზა", + "Name": "სახელი", + "Resume": "გაგრძელება", + "Edit": "ჩასწორება", + "Delete": "წაშლა", + "Current": "მიმდინარე", + "Uptime": "ჩართულობის დრო", + "URL": "URL", + "Hostname": "ჰოსტის სახელი", + "Port": "პორტი", + "Retries": "თავიდან ცდები", + "Green": "მწვანე", + "Blue": "ლურჯი", + "Indigo": "ინდიგო", + "Purple": "იისფერი", + "Custom": "მომხმარებლის", + "topic": "თემა", + "Done": "მზადაა", + "Info": "ინფორმაცია", + "Security": "უსაფრთხოება", + "Title": "სათაური", + "Content": "შემცველობა", + "Style": "სტილი", + "info": "ინფორმაცია", + "warning": "გაფრთხილება", + "error": "შეცდომა", + "critical": "კრიტიკული", + "primary": "ძირითადი", + "light": "ღია", + "dark": "მუქი", + "Post": "დაპოსტვა", + "Created": "შექმნილია", + "Description": "აღწერა", + "Domain": "დომენი", + "Optional": "არასავალდებულო", + "Recipients": "მიმღებები", + "pushoversounds intermission": "დროებით შესვენება", + "Proxy": "პროქსი", + "Search...": "ძებნა…", + "pushoversounds classical": "კლასიკური", + "pushoversounds cosmic": "კოსმოსური", + "pushoversounds falling": "ვარდება", + "pushoversounds gamelan": "გამელანი", + "pushoversounds incoming": "შემომავალი", + "pushoversounds magic": "მაგია", + "pushoversounds mechanical": "მექანიკური", + "pushoversounds siren": "სირენა", + "Strategy": "სტრატეგია", + "Economy": "ეკონომია", + "WebHookUrl": "WebHookUrl", + "SecretKey": "საიდუმლოგასაღები", + "Retry": "თავიდან ცდა", + "Platform": "პლატფორმა", + "Huawei": "Huawei", + "High": "მაღალი", + "Topic": "თემა", + "onebotGroupMessage": "ჯგუფი", + "onebotPrivateMessage": "პირადი", + "Continue": "გაგრძელება", + "apiKey-expired": "ვადაამოწურულია", + "apiKey-inactive": "არააქტიური", + "Generate": "გენერაცია", + "pagertreeUrgency": "სასწრაფოობა", + "pagertreeSilent": "ჩუმი", + "pagertreeLow": "დაბალი", + "pagertreeMedium": "საშუალო", + "pagertreeHigh": "მაღალი", + "pagertreeCritical": "კრიტიკული", + "lunaseaTarget": "სამიზნე", + "endpoint": "ბოლოწერტილი", + "Number": "ნომერი", + "SignName": "ხელმოწერისსახელი", + "TemplateCode": "შაბლონისკოდი", + "maintenanceStatus-scheduled": "დაგეგმილი", + "maintenanceStatus-ended": "დასრულდა", + "maintenanceStatus-unknown": "უცნობი", + "statusPageMaintenanceEndDate": "დასრულება", + "maintenanceStatus-inactive": "არააქტიური", + "Priority": "პრიორიტეტი", + "Method": "მეთოდი", + "Headers": "თავსართები", + "Body": "სხეული", + "records": "ჩანაწერი", + "recent": "უახლესი", + "Default": "ნაგულისხმევი", + "danger": "საშიშროება", + "Fingerprint:": "ანაბეჭდი:", + "weekdayShortThu": "ხუთშ", + "smtpCC": "CC", + "documentation": "დოკუმენტაცია", + "Services": "სერვისები", + "Discard": "მოცილება", + "Cancel": "გაუქმება", + "Valid": "სწორია", + "Invalid": "არასწორია", + "User": "მომხმარებელი", + "Installed": "დაყენებულია", + "Running": "გაშვებულია", + "Start": "გაშვება", + "Stop": "გაჩერება", + "Slug": "Slug", + "Next": "შემდეგი", + "Authentication": "ავთენტიკაცია", + "Backup": "მარქაფი", + "About": "შესახებ", + "Message:": "შეტყობინება:", + "Subject:": "თემა:", + "Issuer:": "გამომცემელი:", + "socket": "სოკეტი", + "Workstation": "სამუშაო სადგური", + "Examples": "მაგალითები", + "recurringInterval": "ინტერვალი", + "Recurring": "გამეორებადი", + "weekdayShortMon": "ორშ", + "weekdayShortTue": "სამშ", + "weekdayShortWed": "ოთხშ", + "weekdayShortFri": "პარ", + "weekdayShortSat": "შაბ", + "weekdayShortSun": "კვი", + "Enable": "ჩართვა", + "Disable": "გამორთვა", + "cronSchedule": "გეგმა: ", + "alertaEnvironment": "გარემო", + "apiKey-active": "აქტიური", + "Customize": "მორგება", + "default": "ნაგულისხმევი", + "enabled": "ჩართულია", + "uninstall": "წაშლა", + "uninstalling": "იშლება", + "or": "ან", + "smtpBCC": "BCC", + "install": "დაყენება", + "installing": "მიმდინარეობს დაყენება", + "pushoversounds bike": "მოტოციკლეტი", + "Lowcost": "იაფი", + "SendKey": "გაგზავნის გასაღები", + "PhoneNumbers": "ტელეფონისნომრები", + "pushoversounds bugle": "ბუკი", + "Device": "მოწყობილობა", + "Expiry": "ვადა", + "Query": "მოთხოვნა", + "Gray": "ნაცრისფერი", + "Red": "წითელი", + "Orange": "ნარინჯისფერი", + "Pink": "ვარდისფერი", + "filterActive": "აქტიური", + "filterActivePaused": "შეჩერებული", + "Mentioning": "ხსენება", + "Originator": "წყარო", + "Destination": "სამიზნე", + "Close": "დახურვა", + "Group": "ჯგუფი", + "Mechanism": "მექანიზმი", + "FlashDuty Severity": "სიმძიმე", + "threemaRecipientTypeIdentity": "Threema-ID", + "Command": "ბრძანება", + "threemaRecipient": "მიმღები", + "Saved.": "შენახულია.", + "Select": "არჩევა", + "Check/Uncheck": "მონიშვნა/მოხსნა", + "pushOthers": "სხვები", + "and": "და", + "Money": "ფული", + "Scifi": "სამეცნიერო ფანტასტიკა", + "Path": "ბილიკი", + "Condition": "პირობა", + "threemaSenderIdentity": "ნაგულისხმევი რაუტერის-ID", + "Conditions": "პირობები", + "conditionValuePlaceholder": "მნიშვნელობა", + "Pop": "პოპი", + "wahaSession": "სესია", + "brevoSubject": "თემა", + "pause": "შეჩერება", + "Manual": "ხელით", + "equals": "უდრის", + "Correct": "გასწორება", + "Harp": "არფა", + "Reveal": "გამოჩენა", + "Flute": "ფლეიტა", + "Fail": "ჩავარდნა", + "SIGNL4": "SIGNL4", + "From": "საიდან", + "contains": "შეიცავს", + "Arcade": "არკადა", + "Doorbell": "კარის ზარი", + "Clear": "გასუფთავება", + "Elevator": "ლიფტი", + "Guitar": "გიტარა", + "templateStatus": "სტატუსი", + "Bubble": "ბუშტი", + "now": "ახლა", + "resendDisabled": "თავიდან გაგზავნა გამორთულია", + "Cert Exp.": "სერტ. ვადა.", + "Max. Redirects": "მაქს. გადამისამართებები", + "Heartbeat Interval": "გულისცემის შუალედი", + "Allow indexing": "დაინდექსების დაშვება", + "Change Password": "პაროლის შეცვლა", + "Import Backup": "მარქაფის შემოტანა", + "Export Backup": "მარქაფის გატანა", + "Repeat Password": "გაიმეორეთ პაროლი", + "Clear Data": "მონაცემების გასუფთავება", + "Affected Monitors": "გავლენის ქვეშ მოყოლილი მონიტორები", + "No Services": "სერვისების გარეშე", + "Webhook URL": "ვებჰუკის URL", + "Application Token": "აპლიკაციის ტოკენი", + "Server URL": "სერვერის URL", + "Read more": "მეტის წაკთხვა", + "PushUrl": "Push-URL", + "Not installed": "დაყენებული არაა", + "Not running": "გაშვებული არაა", + "Remove Token": "ტოკენის წაშლა", + "Accept characters:": "დაშვებული სიმბოლოები:", + "No Proxy": "პროქსის გარეშე", + "cloudflareWebsite": "Cloudflare-ის ვებსაიტი", + "Other Software": "სხვა პროგრამები", + "Footer Text": "ქვედა კოლონტიტულის ტექსტი", + "Docker Container": "Docker-ის კონტეინერი", + "telegramSendSilently": "ჩუმად გაგზავნა", + "Trigger type:": "ტრიგერის ტიპი:", + "Event type:": "მოვლენის ტიპი:", + "Event data:": "მოვლენის მონაცემები:", + "Frontend Version": "წინაბოლოს ვერსია", + "Logout": "გასვლა", + "One record": "ერთი ჩანაწერი", + "Current User": "მიმდინარე მომხმარებელი", + "Hide Tags": "ჭდეების დამალვა", + "Last Updated": "ბოლო განახლება", + "Show Tags": "ჭდეების ჩვენება", + "Coming Soon": "სულ მალე", + "Proxy Server": "პროქსი სერვერი", + "Legacy Octopush-DM": "მოძველებული Octopush-DM", + "HTTP Options": "HTTP-ის პარამეტრები", + "Custom Footer": "მომხმარებლის ქვედა კოლონტიტული", + "API Username": "API-ის მომხმარებლის სახელი", + "pushoversounds cashregister": "სალაროს აპარატი", + "Current Password": "მიმდინარე პაროლი", + "New Password": "ახალი პაროლი", + "Update Password": "პაროლის განახლება", + "Disable Auth": "ავთენტიკაციის გამორთვა", + "Date Created": "შექმნის თარიღი", + "ntfy Topic": "ntfy-ის თემა", + "Avg. Ping": "საშ. პინგი", + "Channel Name": "არხის სახელი", + "Proto Method": "მეთოდი Proto", + "Proto Content": "Proto-ის შემცველობა", + "Bark Sound": "Bark-ის ხმა", + "Feishu WebHookUrl": "Feishu-WebHookURL", + "smtpDkimSettings": "DKIM-ის მორგება", + "Integration Key": "ინტეგრაციის გასაღები", + "auto acknowledged": "ავტომიღება", + "auto resolve": "ავტოამოხსნა", + "pagertreeIntegrationUrl": "ინტეგრაციის URL", + "pagertreeDoNothing": "არაფრის გაკეთება", + "pagertreeResolve": "ავტოამოხსნა", + "serwersmsPhoneNumber": "ტელეფონის ნომერი", + "smseagleTo": "ტელეფონის ნომერი", + "Enable TLS": "TLS-ის ჩართვა", + "Server Address": "სერვერის მისამართი", + "Server Timezone": "სერვერის დროის სარტყელი", + "DateTime Range": "დროისა და თარიღის შუალედი", + "emailCustomSubject": "მომხმარებლის თემა", + "Apprise URL": "Apprise-ის URL", + "Example:": "მაგალითი: {0}", + "alertaRecoverState": "აღდგენის მდგომარეობა", + "Docker Daemon": "Docker-ის დემონი", + "Docker Host": "Docker-ის ჰოსტი", + "Docker Hosts": "Docker-ის ჰოსტები", + "Packet Size": "პაკეტის ზომა", + "strategyManual": "აქტიური/არააქტიური ხელით", + "No Maintenance": "რემონტის გარეშე", + "maintenanceStatus-under-maintenance": "მიმდინარეობს რემონტი", + "IconUrl": "ხატულას URL", + "promosmsPassword": "API-ის პაროლი", + "pushoversounds persistent": "მუდმივი (გრძელი)", + "pushoversounds vibrate": "მხოლოდ ვიბრაცია", + "pushoversounds none": "არცერთი (ჩუმი)", + "apiCredentials": "API-ის ავტორიზაციის დეტალები", + "Status:": "სტატუსი: {0}", + "Device Token": "მოწყობილობის ტოკენი", + "Proxy Protocol": "პროქსის პროტოკოლი", + "do nothing": "არაფრის გაკეთება", + "alertaApiKey": "API-ის გასაღები", + "Add Another": "კიდევ ერთის დამატება", + "pushoversounds spacealarm": "კოსმოსური სიგნალიზაცია", + "pushoversounds tugboat": "ბუქსირი გემი", + "smtpDkimDomain": "დომენის სახელი", + "smtpDkimKeySelector": "გასაღების ამრჩევი", + "smtpDkimPrivateKey": "პირადი გასაღები", + "Integration URL": "ინტეგრაციის URL", + "Add one": "დამატება", + "alertaApiEndpoint": "API-ის ბოლოწერტილი", + "webhookAdditionalHeadersTitle": "დამატებითი თავსართები", + "PushDeer Key": "PushDeer-ის გასაღები", + "From Name/Number": "სახელიდან/ნომრიდან", + "Subprotocol": "ქვეპროტოკოლი", + "Remember me": "დამიმახსოვრე", + "Login": "შესვლა", + "add one": "დამატება", + "Certificate Info": "სერტიფიკატის ინფორმაცია", + "Resolver Server": "ამომხსნელი სერვერი", + "Last Result": "ბოლო შედეგი", + "Default enabled": "ჩართულია ნაგულისხმევად", + "Keep both": "ორივეს შენარჩუნება", + "Disable 2FA": "2FA-ის გამორთვა", + "2FA Settings": "2FA-ის მორგება", + "Show URI": "URI-ის ჩვენება", + "Avg. Response": "საშ. პასუხი", + "Degraded Service": "დეგრადირებული სერვისი", + "Add Group": "ჯგუფის დამატება", + "Status Page": "სტატუსის გვერდი", + "Status Pages": "სტატუსის გვერდები", + "Post URL": "Post-URL", + "Content Type": "შემცველობის ტიპი", + "Shrink Database": "მონაცემთა ბაზის დაპატარავება", + "Create Incident": "ინციდენტის შექმნა", + "No Monitors": "მონიტორების გარეშე", + "Untitled Group": "უსახელო ჯგუფი", + "Custom CSS": "მომხმარებლის CSS", + "Certificate Chain": "სერტიფიკატების ჯაჭვი", + "HTTP Headers": "HTTP თავსართები", + "Trust Proxy": "სანდო პროქსი", + "Please read": "წაიკითხეთ", + "Valid To:": "ვარგისობის ვადა:", + "Days Remaining:": "დარჩენილი დღეები:", + "Domain Names": "დომენის სახელები", + "signedInDispDisabled": "ავთენტიკაცია გამორთულია.", + "RadiusSecret": "Radius-ის საიდუმლო", + "API Key": "API გასაღები", + "Connection String": "დაკავშირების სტრიქონი", + "Connection Type": "კავშირის ტიპი", + "Notification Service": "გაფრთხილების სერვისი", + "lastDay": "ბოლო დღე", + "Schedule Maintenance": "რემონტის დაგეგმვა", + "smtp": "ელფოსტა (SMTP)", + "secureOptionTLS": "TLS (465)", + "From Email": "ელფოსტიდან", + "To Email": "ელფოსტაზე", + "Access Token": "წვდომის ტოკენი", + "Basic Settings": "ძირითადი პარამეტრები", + "User ID": "მომხმარებლის ID", + "Messaging API": "ჩატის API", + "Icon URL": "ხატულას URL", + "pushoversounds pushover": "Pushover (ნაგულისხმევი)", + "pushoversounds pianobar": "ბარის პიანინო", + "pushoversounds climb": "ცოცვა (გრძელი)", + "pushyToken": "მოწყობილობა გაფუჭებულია", + "Guild ID": "გილდიის ID", + "User Key": "მომხმარებლის გასაღები", + "Message Title": "შეტყობინების სათაური", + "Notification Sound": "გაფრთხილების ხმა", + "SMS Type": "SMS-ის ტიპი", + "Gateway Type": "ნაგულისხმევი რაუტერის ტიპი", + "Base URL": "საბაზისო URL", + "AccessKeyId": "წვდომის გასაღების ID", + "SecretAccessKey": "წვდომის გასაღების საიდუმლო", + "Bark Endpoint": "Bark ბოლოწერტილი", + "Bark Group": "Bark-ის ჯგუფი", + "alertaAlertState": "განგაშის მდგომარეობა", + "serwersmsAPIPassword": "API-ის პაროლი", + "smseagleRecipientType": "მიმღების ტიპი", + "Recipient Number": "მიმღების ნომერი", + "onebotUserOrGroupId": "ჯგუფის/მომხმარებლის ID", + "Edit Tag": "ჭდის ჩასწორება", + "Learn More": "გაიგეთ მეტი", + "Expiry date": "ვადა", + "Don't expire": "ვადა არ ამოიწურება", + "Key Added": "გასაღები დაემატა", + "Enable Auth": "ავთენტიკაციის ჩართვა", + "value (optional)": "მნიშვნელობა (არასავალდებულო)", + "Push URL": "Push-URL", + "Skip existing": "არსებულის გამოტოვება", + "Verify Token": "ტოკენის გადამოწმება", + "Enable 2FA": "2FA-ის ჩართვა", + "Icon Emoji": "ხატულას ემოჯი", + "webhookBodyCustomOption": "მომხმარებლის ობიექტი", + "Refresh Interval": "განახლების შუალედი", + "startDateTime": "დაწყების თარიღი/დრო", + "endDateTime": "დასრულების თარიღი/დრო", + "cronExpression": "Cron-ის გამოსახულება", + "Badge Style": "გულსაკიდის სტილი", + "FlashDuty Push URL": "Push-URL", + "Remote Browsers": "დაშორებული ბრაუზერები", + "threemaRecipientTypeIdentityFormat": "8 სიმბოლო", + "pingGlobalTimeoutLabel": "გლობალური მოლოდინის ვადა", + "Badge Label": "გულსაკიდის ჭდე", + "Kafka Brokers": "Kafka-ის ბროკერები", + "Secret AccessKey": "საიდუმლო წვდომის გასაღები", + "authInvalidToken": "არასწორი ტოკენი.", + "2faDisabled": "2FA გამორთულია.", + "successPaused": "წარმატებით შეჩერდა.", + "successDisabled": "წარმატებით გაითიშა.", + "receiverSevenIO": "მიმღები ნომერი", + "Community String": "საზოგადოების სტრიქონი", + "Client Secret": "კლიენტის საიდუმლო", + "OAuth Audience": "OAuth-ის აუდიტორია", + "Template ID": "ნიმუშის ID", + "HTTP Method": "HTTP-ის მეთოდი", + "nostrRelays": "Nostr-ის რელეები", + "settingsDomainExpiry": "დომენის ვადა", + "labelDomainExpiry": "დომენის ვადა.", + "Alphanumeric (recommended)": "ალფარიცხვითი (რეკომენდებულია)", + "Originator type": "ორიგინატორის ტიპი", + "New Group": "ახალი ჯგუფი", + "Group Name": "ჯგუფის სახელი", + "not contains": "არ შეიცავს", + "starts with": "იწყება", + "Recipient Numbers": "მიმღების ნომრები", + "disable authentication": "ავთენტიკაციის გათიშვა", + "defaultFriendlyName": "ახალი მონიტორი", + "pingPerRequestTimeoutLabel": "მოლოდინის ვადა თითოეული პინგისთვის", + "Notify Channel": "გაფრთხილების არხი", + "lunaseaDeviceID": "მოწყობილობის ID", + "lunaseaUserID": "მომხმარებლის ID", + "ntfyAuthenticationMethod": "ავთენტიკაციის მეთოდი", + "Remote Browser": "დაშორებული ბრაუზერი", + "Authorization Header": "ავტორიზაციის თავსართი", + "Nextcloud host": "Nextcloud-ის ჰოსტი", + "Edit Maintenance": "რემონტის ჩასწორება", + "emailCustomBody": "მომხმარებლის ობიექტი", + "Request Body": "მოთხოვნის სხეული", + "pingCountLabel": "მაქს. პაკეტები", + "pingNumericLabel": "რიცხვითი გამოტანა", + "conditionDelete": "პირობის წაშლა", + "conditionDeleteGroup": "ჯგუფის წაშლა", + "brevoFromName": "სახელიდან", + "brevoToEmail": "ელფოსტაზე", + "brevoCcEmail": "CC ელფოსტა", + "brevoBccEmail": "BCC ელფოსტა", + "Phone numbers": "ტელეფონის ნომრები", + "Sender name": "გამომგზავნის სახელი", + "Ip Family": "IP-ის ოჯახი", + "Message Template": "შეტყობინების ნიმუში", + "Template Format": "ნიმუშის ფორმატი", + "Maximum Retries": "მაქს. თავიდან ცდები", + "Notifications Enabled": "გაფრთხილებები ჩართულია", + "Allow Notifications": "გაფრთხილებების დაშვება", + "Monitor Group": "მონიტორების ჯგუფი", + "noOrBadCertificate": "სერტიფიკატი არ არსებობს/დაზიანებულია", + "Recipient Type": "მიმღების ტიპი", + "Private Number": "პირადი ნომერი", + "Group ID": "ჯგუფის ID", + "RabbitMQ Username": "RabbitMQ-ის მომხმარებლის სახელი", + "brevoFromEmail": "ელფოსტიდან", + "Add Tags": "ჭდეების დამატება", + "2faEnabled": "2FA ჩართულია.", + "RabbitMQ Password": "RabbitMQ-ის პაროლი", + "smseagleMsgRing": "ტელეფონზე დარეკვა", + "smseagleMsgTts": "ზარი ტექსტის წარმოთქმით", + "smseagleApiType": "API-ის ვერსია", + "documentationOf": "{0}-ის დოკუმენტაცია", + "threemaRecipientTypeEmail": "ელფოსტის მისამართი", + "conditionAddGroup": "ჯგუფის დამატება", + "not equals": "არ უდრის", + "twilioAccountSID": "ანგარიშის SID", + "Custom URL": "მომხმარებლის URL", + "templateServiceName": "სერვისის სახელი", + "Browser Screenshot": "ბრაუზერის ეკრანის ანაბეჭდი", + "Plain Text": "უბრალო ტექსტი", + "emailCustomisableContent": "მორგებადი შემცველობა", + "selectedMonitorCount": "არჩეულია: {0}", + "Reset Token": "ტოკენის ჩამოყრა", + "successKeyword": "წარმატების საკვანძო სიტყვა", + "auto-select": "ავტოარჩევა", + "chromeExecutable": "Chrome/Chromium-ის გამშვები ფაილი", + "chromeExecutableAutoDetect": "ავტოდადგენა", + "Duration (Minutes)": "ხანგრძლივობა (წთ)", + "e.g. {discordThreadID}": "მაგ: {discordThreadID}", + "Clone Maintenance": "რემონტის დაკლონვა", + "Mention group": "{group}-ის ხსენება", + "smspartnerPhoneNumber": "ტელეფონის ნომერი", + "smseagleMsgType": "შეტყობინების ტიპი", + "Dingtalk Mobile List": "მობილურების სია", + "PushDeer Server": "PushDeer-ის სერვერი", + "SpugPush Template Code": "ნიმუშის კოდი", + "twilioFromNumber": "ნომრიდან", + "twilioToNumber": "ნომერზე", + "Badge Type": "გულსაკიდის ტიპი", + "Badge Color": "გულსაკიდის ფერი", + "Badge URL": "გულსაკიდის URL", + "Badge Preview": "გულსაკიდის მინიატურა", + "Authorization Identity": "ავტორიზაციის იდენტიფიკატორი", + "AccessKey Id": "წვდომის გასაღების Id", + "Session Token": "სესიის ტოკენი", + "successAdded": "წარმატებით დაემატა.", + "successResumed": "წარმატებით გაგრძელდა.", + "successDeleted": "წარმატებით წაიშალა.", + "successEdited": "წარმატებით ჩასწორდა.", + "successEnabled": "წარმატებით ჩაირთო.", + "self-hosted container": "თვითდაჰოსტილი კონტეინერი", + "Telephone number": "ტელეფონის ნომერი", + "API URL": "API-ის URL", + "Message format": "შეტყობინების ფორმატი", + "evolutionInstanceName": "გაშვებული ასლის სახელი", + "threemaRecipientType": "მიმღების ტიპი", + "threemaRecipientTypePhone": "ტელეფონის ნომერი", + "threemaApiAuthenticationSecret": "Gateway-ID-ის საიდუმლო", + "SNMP Version": "SNMP-ის ვერსია", + "Host Onesender": "Onesender-ის ჰოსტი", + "Token Onesender": "Onesender-ის ტოკენი", + "Authentication Method": "ავთენტიკაციის მეთოდი", + "Client ID": "კლიენტის ID", + "OAuth Scope": "OAuth-ის შუალედი", + "conditionAdd": "პირობის დამატება", + "ends with": "მთავრდება", + "Clear Form": "ფორმის გასუფთავება", + "Conversation token": "საუბრის ტოკენი", + "Bot secret": "ბოტის საიდუმლო", + "Expected Value": "მოსალოდნელი მნიშვნელობა", + "Request Timeout": "მოთხოვნის მოლოდინის ვადა ამოიწურა", + "Host URL": "ჰოსტის URL", + "Invert Keyword": "საკვანძო სიტყვის ინვერსია", + "time ago": "{0}-ის წინ", + "programmingLanguages": "პროგრამირების ენები" +} diff --git a/src/lang/ko-KR.json b/src/lang/ko-KR.json new file mode 100644 index 0000000..fad6c4b --- /dev/null +++ b/src/lang/ko-KR.json @@ -0,0 +1,984 @@ +{ + "languageName": "한국어", + "checkEverySecond": "{0}초마다 확인", + "retryCheckEverySecond": "{0}초마다 재시도", + "retriesDescription": "서비스가 다운된 것으로 간주하고 알림을 보내기 전까지의 최대 재시도 횟수", + "ignoreTLSError": "HTTPS 웹사이트에서 TLS/SSL 오류 무시", + "upsideDownModeDescription": "상태를 반대로 표시합니다. 서비스에 연결 가능하면 '다운'으로 간주됩니다.", + "maxRedirectDescription": "최대 리디렉션 허용 횟수. 0으로 설정하면 리디렉션을 사용하지 않습니다.", + "acceptedStatusCodesDescription": "응답 성공으로 간주할 상태 코드를 정해요.", + "passwordNotMatchMsg": "비밀번호 재입력이 일치하지 않아요.", + "notificationDescription": "알림이 동작하려면 기존 모니터에 할당되어야 합니다.", + "keywordDescription": "HTML 이나 JSON에서 대소문자를 구분해 키워드를 검색해요.", + "pauseDashboardHome": "정지", + "deleteMonitorMsg": "이 모니터를 삭제하시겠습니까?", + "deleteNotificationMsg": "이 알림을 모든 모니터에서 삭제하시겠습니까?", + "resolverserverDescription": "Cloudflare가 기본 서버예요, 원한다면 언제나 다른 Resolver 서버로 변경할 수 있어요.", + "rrtypeDescription": "모니터링할 RR Type을 선택하세요.", + "pauseMonitorMsg": "이 모니터를 일시 정지하시겠습니까?", + "enableDefaultNotificationDescription": "새 모니터에 이 알림을 기본적으로 활성화합니다. 개별 모니터에 대해 알림을 비활성화할 수 있습니다.", + "clearEventsMsg": "이 모니터의 모든 이벤트를 삭제하시겠습니까?", + "clearHeartbeatsMsg": "이 모니터의 모든 하트비트를 삭제하시겠습니까?", + "confirmClearStatisticsMsg": "정말 모든 통계를 삭제할까요?", + "importHandleDescription": "이름이 같은 모니터나 알림을 건너뛰려면 '기존 항목 건너뛰기'를 선택하세요. '덮어쓰기'를 선택한 경우 존재하는 기존 모니터와 알림을 모두 삭제합니다.", + "confirmImportMsg": "정말 백업을 가져올까요? 가져오기 옵션을 제대로 설정했는지 다시 확인해주세요.", + "twoFAVerifyLabel": "토큰을 입력해 2단계 인증이 작동하는지 확인해주세요", + "tokenValidSettingsMsg": "토큰이 유효해요! 이제 2단계 인증 설정을 저장할 수 있어요.", + "confirmEnableTwoFAMsg": "정말 2단계 인증을 활성화할까요?", + "confirmDisableTwoFAMsg": "정말 2단계 인증을 비활성화할까요?", + "Settings": "설정", + "Dashboard": "대시보드", + "New Update": "새 업데이트", + "Language": "언어", + "Appearance": "모양", + "Theme": "테마", + "General": "일반", + "Version": "버전", + "Check Update On GitHub": "Github에서 업데이트 확인", + "List": "목록", + "Add": "추가", + "Add New Monitor": "새 모니터 추가", + "Quick Stats": "요약", + "Up": "온라인", + "Down": "오프라인", + "Pending": "대기 중", + "Unknown": "알 수 없음", + "Pause": "일시 정지", + "Name": "이름", + "Status": "상태", + "DateTime": "날짜", + "Message": "메시지", + "No important events": "중요 이벤트 없음", + "Resume": "재개", + "Edit": "편집", + "Delete": "삭제", + "Current": "현재", + "Uptime": "업타임", + "Cert Exp.": "인증서 만료", + "day": "일", + "-day": "일", + "hour": "시간", + "-hour": "시간", + "Response": "응답", + "Ping": "핑", + "Monitor Type": "모니터 타입", + "Keyword": "키워드", + "Friendly Name": "별명", + "URL": "URL", + "Hostname": "호스트명", + "Port": "포트", + "Heartbeat Interval": "하트비트 주기", + "Retries": "재시도", + "Heartbeat Retry Interval": "하트비트 재시도 주기", + "Advanced": "고급", + "Upside Down Mode": "상태 반전 모드", + "Max. Redirects": "최대 리다이렉트", + "Accepted Status Codes": "허용된 상태 코드", + "Save": "저장", + "Notifications": "알림", + "Not available, please setup.": "아직 사용할 수 없습니다. 설정이 필요합니다.", + "Setup Notification": "알림 설정", + "Light": "라이트", + "Dark": "다크", + "Auto": "자동", + "Theme - Heartbeat Bar": "테마 - 하트비트 바", + "Normal": "기본값", + "Bottom": "하단", + "None": "없음", + "Timezone": "시간대", + "Search Engine Visibility": "검색 엔진 노출", + "Allow indexing": "인덱싱 허용", + "Discourage search engines from indexing site": "검색 엔진의 인덱싱을 허용하지 않음", + "Change Password": "비밀번호 변경", + "Current Password": "현재 비밀번호", + "New Password": "새 비밀번호", + "Repeat New Password": "새 비밀번호 확인", + "Update Password": "비밀번호 변경", + "Disable Auth": "인증 비활성화", + "Enable Auth": "인증 활성화", + "disableauth.message1": "{disableAuth}하시겠습니까?", + "disable authentication": "인증을 비활성화", + "disableauth.message2": "이 기능은 Uptime Kuma 앞단에 Cloudflare Access, Authelia 등의 {intendThirdPartyAuth}을 위해 설계되었습니다.", + "where you intend to implement third-party authentication": "서드 파티 인증을 구현하는 상황", + "Please use this option carefully!": "신중하게 사용하세요!", + "Logout": "로그아웃", + "Leave": "취소", + "I understand, please disable": "이해했습니다. 비활성화합니다.", + "Confirm": "확인", + "Yes": "예", + "No": "아니요", + "Username": "사용자명", + "Password": "비밀번호", + "Remember me": "로그인 상태 유지", + "Login": "로그인", + "No Monitors, please": "등록된 모니터가 없습니다.", + "add one": "추가하기", + "Notification Type": "알림 종류", + "Email": "이메일", + "Test": "테스트", + "Certificate Info": "인증서 정보", + "Resolver Server": "Resolver 서버", + "Resource Record Type": "리소스 레코드 유형", + "Last Result": "최근 결과", + "Create your admin account": "관리자 계정 생성", + "Repeat Password": "비밀번호 확인", + "Import Backup": "백업 가져오기", + "Export Backup": "백업 내보내기", + "Export": "내보내기", + "Import": "가져오기", + "respTime": "응답 시간 (ms)", + "notAvailableShort": "N/A", + "Default enabled": "기본적으로 활성화", + "Apply on all existing monitors": "기존 모니터에 모두 적용", + "Create": "생성", + "Clear Data": "데이터 삭제", + "Events": "이벤트", + "Heartbeats": "하트비트", + "Auto Get": "Auto Get", + "backupDescription": "모든 모니터와 알림을 JSON 파일에 백업할 수 있습니다.", + "backupDescription2": "히스토리와 이벤트 데이터는 포함되어 있지 않아요.", + "backupDescription3": "알림 토큰과 같은 보안 데이터가 내보내기 파일에 포함되어 있으므로 관리에 주의해주세요.", + "alertNoFile": "가져올 파일을 선택하세요.", + "alertWrongFileType": "JSON 파일을 선택하세요.", + "Clear all statistics": "모든 통계 삭제", + "Skip existing": "기존 항목 건너뛰기", + "Overwrite": "덮어쓰기", + "Options": "옵션", + "Keep both": "모두 보존", + "Verify Token": "토큰 검증", + "Setup 2FA": "2단계 인증 설정", + "Enable 2FA": "2단계 인증 활성화", + "Disable 2FA": "2단계 인증 비활성화", + "2FA Settings": "2단계 인증 설정", + "Two Factor Authentication": "2단계 인증", + "Active": "활성화", + "Inactive": "비활성화", + "Token": "토큰", + "Show URI": "URI 표시", + "Tags": "태그", + "Add New below or Select...": "아래에서 선택하거나 추가…", + "Tag with this name already exist.": "동일한 이름의 태그가 이미 존재합니다.", + "Tag with this value already exist.": "동일한 값의 태그가 이미 존재합니다.", + "color": "색상", + "value (optional)": "값 (선택)", + "Gray": "회색", + "Red": "빨강", + "Orange": "주황", + "Green": "초록", + "Blue": "파랑", + "Indigo": "인디고", + "Purple": "보라", + "Pink": "핑크", + "Search...": "검색…", + "Avg. Ping": "평균 핑", + "Avg. Response": "평균 응답", + "Entry Page": "첫 페이지", + "statusPageNothing": "아무것도 없습니다. 새 그룹이나 모니터를 추가하세요.", + "No Services": "서비스 없음", + "All Systems Operational": "모든 시스템 작동 중", + "Partially Degraded Service": "일부 서비스 불안정", + "Degraded Service": "서비스 불안정", + "Add Group": "그룹 추가", + "Add a monitor": "모니터 추가", + "Edit Status Page": "상태 페이지 편집", + "Go to Dashboard": "대시보드로", + "Status Page": "상태 페이지", + "Status Pages": "상태 페이지", + "defaultNotificationName": "내 {notification} 알림 ({number})", + "here": "여기", + "Required": "필수", + "telegram": "Telegram", + "Bot Token": "봇 토큰", + "wayToGetTelegramToken": "토큰은 여기서 얻을 수 있어요: {0}.", + "Chat ID": "채팅 ID", + "supportTelegramChatID": "개인 채팅 / 그룹 / 채널의 ID를 지원해요", + "wayToGetTelegramChatID": "봇에 메시지를 보내 채팅 ID를 얻고 밑에 URL로 이동해 chat_id를 볼 수 있어요", + "YOUR BOT TOKEN HERE": "봇 토큰", + "chatIDNotFound": "채팅 ID를 찾을 수 없어요. 먼저 봇에게 메시지를 보내주세요", + "webhook": "Webhook", + "Post URL": "Post URL", + "Content Type": "Content Type", + "webhookJsonDesc": "{0}은(는) Express.js와 같은 모던 HTTP 서버에 적합합니다.", + "webhookFormDataDesc": "{multipart}는 PHP에 적합합니다. JSON은 {decodeFunction}을 사용해 파싱해야 합니다.", + "smtp": "Email (SMTP)", + "secureOptionNone": "없음 / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "TLS 에러 무시하기", + "From Email": "보내는 이메일", + "To Email": "받는 이메일", + "smtpCC": "참조", + "smtpBCC": "숨은 참조", + "discord": "Discord", + "Discord Webhook URL": "Discord 웹훅 URL", + "wayToGetDiscordURL": "서버 설정 -> 연동 -> 웹훅 보기 -> 새 웹훅 에서 얻을 수 있어요", + "Bot Display Name": "표시 이름", + "Prefix Custom Message": "접두사 메시지", + "Hello @everyone is...": "{'@'}everyone 서버 상태 알림이에요…", + "teams": "Microsoft Teams", + "Webhook URL": "Webhook URL", + "wayToGetTeamsURL": "{0}에서 Webhook을 어떻게 만드는지 알아보세요.", + "signal": "Signal", + "Number": "숫자", + "Recipients": "받는 사람", + "needSignalAPI": "REST API를 사용하는 Signal 클라이언트가 있어야 해요.", + "wayToCheckSignalURL": "밑에 URL을 확인해 URL 설정 방법을 볼 수 있어요:", + "signalImportant": "중요: 수신자 그룹과 숫자는 섞을 수 없습니다!", + "gotify": "Gotify", + "Application Token": "애플리케이션 토큰", + "Server URL": "서버 URL", + "Priority": "우선 순위", + "slack": "Slack", + "Icon Emoji": "아이콘 이모지", + "Channel Name": "채널명", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Webhook에 대한 자세한 내용: {0}", + "aboutChannelName": "Webhook 채널을 바이패스하려면 {0}에 채널 이름을 입력하세요. 예: #기타-채널", + "aboutKumaURL": "Uptime Kuma URL 필드를 공백으로 두면 기본적으로 Github Project 페이지로 설정합니다,", + "emojiCheatSheet": "이모지 목록: {0}", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (50개 이상 알림 서비스)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "유저 키", + "Device": "디바이스", + "Message Title": "메시지 제목", + "Notification Sound": "알림음", + "More info on:": "자세한 정보: {0}", + "pushoverDesc1": "긴급 우선 순위 (2)는 재시도 사이에 기본적으로 30초의 타임아웃이 있고, 1시간 후에 만료되어요.", + "pushoverDesc2": "다른 장치에 알림을 보내려면 장치칸을 입력해주세요.", + "SMS Type": "SMS 종류", + "octopushTypePremium": "프리미엄 (빠름) - 알림 기능에 적합해요)", + "octopushTypeLowCost": "저렴한 요금 (느림) - 가끔 차단될 수 있어요)", + "Check octopush prices": "{0}에서 Octopush 가격을 확인할 수 있어요.", + "octopushPhoneNumber": "휴대전화 번호 (intl format, 예시: +821023456789) ", + "octopushSMSSender": "보내는 사람 이름 : 3-11개의 영숫자 및 여백공간 (a-z, A-Z, 0-9)", + "LunaSea Device ID": "LunaSea 장치 ID", + "Apprise URL": "Apprise URL", + "Example:": "예: {0}", + "Read more:": "더 보기: {0}", + "Status:": "상태: {0}", + "Read more": "더보기", + "appriseInstalled": "Apprise가 설치되어 있습니다.", + "appriseNotInstalled": "Apprise가 설치되지 않았습니다. {0}", + "Access Token": "액세스 토큰", + "Channel access token": "채널 액세스 토큰", + "Line Developers Console": "Line 개발자 콘솔", + "lineDevConsoleTo": "Line 개발자 콘솔 - {0}", + "Basic Settings": "기본 설정 메뉴", + "User ID": "사용자 ID", + "Messaging API": "Messaging API 메뉴", + "wayToGetLineChannelToken": "먼저 {0}에 액세스하고, 공급자 및 채널 (Messaging API)을 만든 다음, 각 메뉴 밑에 언급된 메뉴에서 채널 액세스 토큰과 사용자 ID를 얻을 수 있어요.", + "Icon URL": "아이콘 URL", + "aboutIconURL": "\"아이콘 URL\"에 사진 링크를 입력해 프로필 사진을 설정할 수 있어요. 아이콘 이모지가 설정되어 있으면 적용되지 않을 거예요.", + "aboutMattermostChannelName": "채널 이름을 입력하면 Webhook이 게시할 기본 채널을 재설정할 수 있어요. 이 설정은 Mattermost 웹훅 설정에서 활성화해야 해요. 예: #기타-채널", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - 저렴하지만 느리고 가끔 과부하에 걸려요. 폴란드 수신자만 사용할 수 있어요.", + "promosmsTypeFlash": "SMS FLASH - 메시지가 받는 사람 장치에 자동으로 표시되어요. 폴란드 수신자만 사용할 수 있어요.", + "promosmsTypeFull": "SMS FULL - SMS 프리미엄 티어, 보내는 사람 이름을 먼저 등록해야 해요. 알림 기능에 적합해요.", + "promosmsTypeSpeed": "SMS SPEED - 시스템에서 가장 높은 우선순위예요. 매우 빠르고 신뢰할 수 있지만 비용이 많이 들어요 (SMS 전체 가격의 약 두 배).", + "promosmsPhoneNumber": "전화 번호 (폴란드 수신자라면 지역번호를 적지 않아도 되어요.)", + "promosmsSMSSender": "SMS 보내는 사람 이름 : 미리 등록된 이름 혹은 기본값 중 하나예요: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Primary Base URL": "주 베이스 URL", + "Push URL": "Push URL", + "needPushEvery": "이 URL을 {0}초 마다 호출할 수 있습니다.", + "pushOptionalParams": "추가 파라미터: {0}", + "emailCustomSubject": "커스텀 주제", + "clicksendsms": "ClickSend SMS", + "checkPrice": "{0} 가격 확인:", + "apiCredentials": "API 인증정보", + "octopushLegacyHint": "Octopush 레거시 버전 (2011-2020) 을 사용하시나요? 아니면 새 버전을 사용하시나요?", + "Feishu WebHookUrl": "Feishu WebHookURL", + "matrixHomeserverURL": "Homeserver URL (http(s):// 와 함께 적어주세요. 그리고 포트 번호는 선택적 입니다.)", + "Internal Room Id": "내부 방 ID", + "matrixDesc1": "Matrix 클라이언트 방 설정의 고급 섹션에서 내부 방 ID를 찾을 수 있어요. 내부 방 ID는 이렇게 생겼답니다: !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "개인 Matrix 사용자 계정의 액세스 토큰을 사용하는 것은 계정 전체와 참여 중인 모든 방에 완전한 접근 권한을 부여하게 되므로 권장되지 않습니다. 대신 새로운 사용자를 생성한 후 알림을 받을 방에만 초대하는 것을 권장합니다. 액세스 토큰은 {0} 명령어를 실행하여 얻을 수 있습니다.", + "Method": "Method", + "Body": "Body", + "Headers": "헤더", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "요청 헤더의 JSON 형식이 올바르지 않음: ", + "BodyInvalidFormat": "요청 본문의 JSON 형식이 올바르지 않음: ", + "Monitor History": "모니터 기록", + "clearDataOlderThan": "모니터 기록을 {0}일간 저장합니다.", + "PasswordsDoNotMatch": "비밀번호가 일치하지 않습니다.", + "records": "레코드", + "One record": "One record", + "steamApiKeyDescription": "Steam 게임 서버를 모니터링하려면 Steam Web-API 키가 필요합니다. 여기서 API 키를 등록하세요: ", + "Current User": "현재 사용자", + "recent": "최근", + "Done": "완료", + "Info": "정보", + "Security": "보안", + "Steam API Key": "Steam API 키", + "Shrink Database": "데이터베이스 축소", + "Pick a RR-Type...": "RR-Type 선택…", + "Pick Accepted Status Codes...": "성공 상태 코드 선택…", + "Default": "기본", + "HTTP Options": "HTTP 옵션", + "Create Incident": "인시던트 생성", + "Title": "제목", + "Content": "내용", + "Style": "스타일", + "info": "정보", + "warning": "경고", + "danger": "위험", + "primary": "기본", + "light": "라이트", + "dark": "다크", + "Post": "게시", + "Please input title and content": "제목 및 내용을 입력하세요.", + "Created": "생성일", + "Last Updated": "최근 수정", + "Unpin": "제거", + "Switch to Light Theme": "라이트 테마로 전환", + "Switch to Dark Theme": "다크 테마로 전환", + "Show Tags": "태그 보기", + "Hide Tags": "태그 숨기기", + "Description": "설명", + "No monitors available.": "사용 가능한 모니터가 없습니다.", + "Add one": "추가하기", + "No Monitors": "모니터 없음", + "Untitled Group": "제목 없는 그룹", + "Services": "서비스", + "Discard": "취소", + "Cancel": "닫기", + "Powered by": "Powered by", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API 사용자명 (webapi_ 접두사 포함)", + "serwersmsAPIPassword": "API 비밀번호", + "serwersmsPhoneNumber": "휴대폰 번호", + "serwersmsSenderName": "SMS 발신자명 (customer portal로 가입된 정보)", + "stackfield": "Stackfield", + "dnsPortDescription": "DNS 서버 포트, 기본값은 53 이에요. 포트는 언제나 변경할 수 있어요.", + "PushByTechulus": "Push by Techulus", + "GoogleChat": "Google Chat (Google Workspace only)", + "topic": "Topic", + "topicExplanation": "모니터링할 MQTT 토픽", + "successMessage": "성공 메시지", + "successMessageExplanation": "성공으로 간주되는 MQTT 메시지", + "error": "오류", + "critical": "중대", + "Customize": "사용자화", + "Custom Footer": "사용자 지정 푸터", + "Custom CSS": "사용자 지정 CSS", + "smtpDkimSettings": "DKIM 설정", + "smtpDkimDesc": "사용 방법은 Nodemailer DKIM {0}을(를) 참조하세요.", + "documentation": "문서", + "smtpDkimDomain": "도메인 이름", + "smtpDkimKeySelector": "Key Selector", + "smtpDkimPrivateKey": "비밀 키", + "smtpDkimHashAlgo": "해시 알고리즘 (선택)", + "smtpDkimheaderFieldNames": "서명할 헤더 키 (선택)", + "smtpDkimskipFields": "서명하지 않을 헤더 키 (선택)", + "wayToGetPagerDutyKey": "\"Events API V2\"는 Service -> Service Directory -> (서비스 선택) -> Integrations -> Add integration. 에서 찾을 수 있습니다. 자세한 내용 {0}", + "Integration Key": "Integration 키", + "Integration URL": "Integration URL", + "Auto resolve or acknowledged": "자동 해결 또는 승인", + "do nothing": "아무것도 하지 않기", + "auto acknowledged": "자동 승인 (acknowledged)", + "auto resolve": "자동 해결 (resolve)", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API 엔드포인트", + "alertaEnvironment": "환경", + "alertaApiKey": "API 키", + "alertaAlertState": "알림 상태", + "alertaRecoverState": "복구 상태", + "deleteStatusPageMsg": "이 상태 페이지를 삭제하시겠습니까?", + "Proxies": "프록시", + "default": "기본", + "enabled": "활성화", + "setAsDefault": "기본으로 설정", + "deleteProxyMsg": "이 프록시를 모든 모니터에서 삭제하시겠습니까?", + "proxyDescription": "프록시가 작동하려면 모니터에 할당되어야 합니다.", + "enableProxyDescription": "이 프록시는 활성화될 때까지 모니터의 요청에 미치지 않습니다. 활성화 상태를 통해 모든 모니터에서 프록시를 일시 정지할 수 있습니다.", + "setAsDefaultProxyDescription": "새 모니터에 이 프록시를 기본적으로 활성화합니다. 개별 모니터에 대해 프록시를 비활성화할 수 있습니다.", + "Certificate Chain": "인증서 체인", + "Valid": "유효함", + "Invalid": "유효하지 않음", + "AccessKeyId": "AccessKey ID", + "SecretAccessKey": "AccessKey Secret", + "PhoneNumbers": "휴대전화 번호", + "TemplateCode": "템플릿 코드", + "SignName": "SignName", + "Sms template must contain parameters: ": "SMS 템플릿은 다음과 같은 파라미터가 포함되어야 해요: ", + "Bark Endpoint": "Bark Endpoint", + "WebHookUrl": "웹훅 URL", + "SecretKey": "Secret Key", + "For safety, must use secret key": "안전을 위해 꼭 Secret Key를 사용하세요.", + "Device Token": "기기 Token", + "Platform": "플랫폼", + "Huawei": "Huawei", + "High": "High", + "Retry": "재시도", + "Topic": "Topic", + "WeCom Bot Key": "WeCom Bot Key", + "Setup Proxy": "프록시 설정", + "Proxy Protocol": "프록시 프로토콜", + "Proxy Server": "프록시 서버", + "Proxy server has authentication": "프록시 서버에 인증 절차가 있음", + "User": "사용자", + "Installed": "설치됨", + "Not installed": "설치되지 않음", + "Running": "작동 중", + "Not running": "작동 중이 아님", + "Remove Token": "토큰 제거", + "Start": "시작", + "Stop": "정지", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "새 상태 페이지 추가", + "Slug": "Slug", + "Accept characters:": "허용되는 문자열:", + "startOrEndWithOnly": "{0}로 시작하거나 끝나야 합니다.", + "No consecutive dashes": "연속되는 대시는 허용되지 않아요", + "Next": "다음", + "The slug is already taken. Please choose another slug.": "이미 존재하는 주소에요. 다른 주소를 사용해 주세요.", + "No Proxy": "프록시 없음", + "Authentication": "인증", + "HTTP Basic Auth": "HTTP 인증", + "New Status Page": "새로운 상태 페이지", + "Page Not Found": "페이지를 찾을 수 없어요", + "Reverse Proxy": "리버스 프록시", + "Backup": "백업", + "About": "정보", + "wayToGetCloudflaredURL": "({0}에서 Cloudflare 다운로드 하기)", + "cloudflareWebsite": "Cloudflare 웹사이트", + "Message:": "메시지:", + "Don't know how to get the token? Please read the guide:": "토큰을 얻는 방법은 이 가이드를 확인해주세요:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Cloudflare Tunnel를 연결하면 현재 연결이 끊길 수 있어요. 정말 중지할까요? 비밀번호를 입력해 확인하세요.", + "Other Software": "다른 소프트웨어", + "For example: nginx, Apache and Traefik.": "nginx, Apache, Traefik 등을 사용할 수 있어요.", + "Please read": "이 문서를 참조하세요:", + "Subject:": "발급 대상:", + "Valid To:": "유효 기간:", + "Days Remaining:": "남은 일수:", + "Issuer:": "발급 기관:", + "Fingerprint:": "Fingerprint:", + "No status pages": "상태 페이지 없음", + "Domain Name Expiry Notification": "도메인 이름 만료 알림", + "Proxy": "프록시", + "Date Created": "생성된 날짜", + "onebotHttpAddress": "OneBot HTTP 주소", + "onebotMessageType": "OneBot 메시지 종류", + "onebotGroupMessage": "그룹 메시지", + "onebotPrivateMessage": "개인 메시지", + "onebotUserOrGroupId": "그룹/사용자 ID", + "onebotSafetyTips": "보안을 위해 Access 토큰을 설정하세요.", + "PushDeer Key": "PushDeer 키", + "Footer Text": "Footer 문구", + "Show Powered By": "Powered By 문구 표시하기", + "Domain Names": "도메인 이름", + "signedInDisp": "{0} 로그인됨", + "signedInDispDisabled": "인증 비활성화됨.", + "Certificate Expiry Notification": "인증서 만료 알림", + "API Username": "API 사용자 이름", + "API Key": "API 키", + "Recipient Number": "수신자 번호", + "From Name/Number": "발신자 이름/번호", + "Leave blank to use a shared sender number.": "공유 발신 번호를 사용하려면 공백으로 두세요.", + "Octopush API Version": "Octopush API 버전", + "Legacy Octopush-DM": "레거시 Octopush-DM", + "endpoint": "endpoint", + "octopushAPIKey": "제어판 HTTP API credentials 에서 \"API key\"", + "octopushLogin": "제어판 HTTP API credentials 에서 \"Login\"", + "promosmsLogin": "API 로그인 이름", + "promosmsPassword": "API 비밀번호", + "pushoversounds pushover": "Pushover (기본)", + "pushoversounds bike": "Bike", + "pushoversounds bugle": "Bugle", + "pushoversounds cashregister": "Cash Register", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Cosmic", + "pushoversounds falling": "Falling", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Incoming", + "pushoversounds intermission": "Intermission", + "pushoversounds magic": "Magic", + "pushoversounds mechanical": "Mechanical", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Siren", + "pushoversounds spacealarm": "Space Alarm", + "pushoversounds tugboat": "Tug Boat", + "pushoversounds alien": "Alien Alarm (long)", + "pushoversounds climb": "Climb (long)", + "pushoversounds persistent": "Persistent (long)", + "pushoversounds echo": "Pushover Echo (long)", + "pushoversounds updown": "Up Down (long)", + "pushoversounds vibrate": "진동만", + "pushoversounds none": "없음 (무음)", + "pushyAPIKey": "비밀 API 키", + "pushyToken": "기기 토큰", + "Show update if available": "사용 가능한 경우에 업데이트 표시", + "Also check beta release": "베타 릴리즈 확인", + "Using a Reverse Proxy?": "리버스 프록시를 사용하시나요?", + "Check how to config it for WebSocket": "웹소켓 대한 설정 방법", + "Steam Game Server": "스팀 게임 서버", + "Most likely causes:": "원인:", + "The resource is no longer available.": "더 이상 사용할 수 없어요...", + "There might be a typing error in the address.": "주소에 오탈자가 있을 수 있어요.", + "What you can try:": "해결 방법:", + "Retype the address.": "주소 다시 입력하기.", + "Go back to the previous page.": "이전 페이지로 돌아가기.", + "Coming Soon": "Coming Soon", + "wayToGetClickSendSMSToken": "{0}에서 API 사용자명과 키를 얻을 수 있습니다.", + "Custom Monitor Type": "커스텀 모니터", + "deleteDockerHostMsg": "이 Docker 호스트를 모든 모니터에서 삭제하시겠습니까?", + "trustProxyDescription": "'X-Forwarded-*' 헤더를 신뢰해요. 올바른 클라이언트 IP를 얻어야하고Uptime Kuma가 Nginx나 Apache 같은 프록시 뒤에 있다면 이 기능을 활성화해야 해요.", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "프로필 이름(왼쪽 아래)을 클릭하고 아래로 스크롤한 다음 토큰 만들기를 클릭하여 장기 액세스 토큰을 만들 수 있어요. ", + "Then choose an action, for example switch the scene to where an RGB light is red.": "그런 다음 동작을 선택해요, 예를 들어 장면을 RGB 조명이 빨간색인 곳으로 전환해요.", + "backupOutdatedWarning": "Deprecated: 많은 기능이 추가되었고 이 백업 기능은 유지 관리되지 않아 전체 백업을 생성하거나 복원할 수 없어요.", + "lastDay3": "매월 세 번째 마지막 날", + "maintenanceStatus-under-maintenance": "점검 중", + "dnsCacheDescription": "일부 IPv6 환경에서는 작동하지 않을 수 있어요. 문제가 발생하면 비활성화하세요.", + "dataRetentionTimeError": "저장 기간은 0 이상이어야 해요", + "wayToGetKookGuildID": "Kook 설정에서 'Developer Mode'를 활성화하고 길드를 우클릭해 ID를 얻어요", + "You can divide numbers with": "다음과 같이 숫자를 구분할 수 있어요:", + "goAlertInfo": "GoAlert는 온콜 스케줄링, 자동 에스컬레이션 및 알림(SMS 또는 음성 통화와 같은)을 위한 오픈 소스 응용 프로그램이에요. 올바른 사람, 올바른 방법, 적절한 시간에 자동으로 참여하세요! {0}", + "smseagle": "SMSEagle", + "smseagleTo": "휴대폰 번호", + "smseagleRecipient": "수신자 (여러 명인 경우 쉼표로 구분)", + "Maintenance": "점검", + "statusMaintenance": "점검 중", + "resendEveryXTimes": "{0}번마다 재전송", + "resendDisabled": "재전송하지 않음", + "loadingError": "데이터를 가져올 수 없어요, 나중에 다시 시도하세요.", + "plugin": "플러그인", + "install": "설치", + "installing": "설치 중", + "uninstall": "삭제", + "uninstalling": "삭제 중", + "confirmUninstallPlugin": "정말 이 플러그인을 삭제할까요?", + "Guild ID": "길드 ID", + "Strategy": "계획", + "Free Mobile User Identifier": "무료 모바일 사용자 식별", + "Free Mobile API Key": "무료 모바일 API 키", + "Enable TLS": "TLS 활성화", + "Proto Service Name": "Proto 서비스 이름", + "Proto Method": "Proto 메서드", + "Proto Content": "Proto Content", + "Economy": "경제적", + "high": "높음", + "Lowcost": "저비용", + "SendKey": "SendKey", + "SMSManager API Docs": "SMSManager API 문서 ", + "Gateway Type": "게이트웨이 종류", + "SMSManager": "SMSManager", + "Base URL": "베이스 URL", + "goAlertIntegrationKeyInfo": "일반적으로 복사된 URL의 토큰 매개 변수 값을 \"aaaaaa-bbb-ccccc-ddd-eeeeeeeee\" 형식으로 서비스에 대한 일반 API 통합 키를 가져와요.", + "goAlert": "GoAlert", + "Bark Group": "Bark 그룹", + "Bark Sound": "Bark 소리", + "promosmsAllowLongSMS": "긴 SMS 허용", + "smseagleGroup": "연락처 그룹명 목록", + "smseagleContact": "연락처 이름 목록", + "smseagleRecipientType": "수신자 종류", + "smseagleToken": "API 액세스 토큰", + "smseagleUrl": "SMSEagle 기기 URL", + "smseagleEncoding": "유니코드로 전송 (기본값 = GSM-7)", + "smseaglePriority": "메시지 우선 순위 (0-9, 기본값= 0)", + "ntfy Topic": "ntfy 토픽", + "HomeAssistant": "홈 어시스턴트", + "RadiusSecretDescription": "클라이언트와 서버 간의 비밀 키", + "RadiusSecret": "Radius 비밀 키", + "RadiusCalledStationId": "접속 스테이션의 Id", + "RadiusCalledStationIdDescription": "접속 스테이션의 식별자", + "RadiusCallingStationId": "접속 요청 스테이션의 Id", + "RadiusCallingStationIdDescription": "접속 요청 스테이션의 식별자", + "timeoutAfter": "{0}초 후 타임아웃", + "Request Timeout": "요청 타임아웃", + "Query": "쿼리", + "settingsCertificateExpiry": "TLS 인증서 만료", + "certificationExpiryDescription": "TLS 인증서가 설정된 기간 내에 만료될 경우, HTTPS 모니터가 알림을 전송합니다:", + "Setup Docker Host": "도커 호스트 설정", + "Docker Daemon": "도커 데몬", + "socket": "소켓", + "tcp": "TCP / HTTP", + "Docker Container": "도커 컨테이너", + "Container Name / ID": "컨테이너 이름 / ID", + "Docker Host": "도커 호스트", + "Docker Hosts": "도커 호스트", + "Domain": "도메인", + "Connection String": "연결 문자열", + "Workstation": "워크스테이션", + "Packet Size": "패킷 크기", + "ZohoCliq": "ZohoCliq", + "disableCloudflaredNoAuthMsg": "인증 없음 모드이므로 암호가 필요하지 않아요.", + "wayToGetLineNotifyToken": "토큰은 여기서 얻을 수 있어요: {0}", + "Examples": "예시", + "Home Assistant URL": "홈 어시스턴트 URL", + "Long-Lived Access Token": "장기 엑세스 토큰", + "Notification Service": "알림 서비스", + "default: notify all devices": "기본값: 모든 장치에 알림", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "알림 서비스 목록은 홈 어시스턴트의 \"개발자 도구 > 서비스\"에서 \"알림\" 검색해 장치/전화 이름을 찾을 수 있어요.", + "Automations can optionally be triggered in Home Assistant:": "자동화는 Home Assistant에서 선택적으로 트리거될 수 있어요:", + "Connection Type": "연결 종류", + "Trigger type:": "트리거 종류:", + "Event type:": "이벤트 종류:", + "Event data:": "이벤트 데이터:", + "Frontend Version": "프론트엔드 버전", + "Frontend Version do not match backend version!": "프론트엔드 버전이 백엔드 버전과 일치하지 않아요!", + "confirmDeleteTagMsg": "이 태그를 삭제하시겠습니까? 이 태그와 연결된 모니터는 삭제되지 않습니다.", + "infiniteRetention": "무한히 저장하려면 0으로 설정하세요.", + "backupRecommend": "대신 볼륨 또는 데이터 폴더 (./data/) 를 직접 백업하세요.", + "Optional": "선택", + "squadcast": "Squadcast", + "or": "혹은", + "recurringInterval": "반복 - 주기적", + "Recurring": "반복", + "strategyManual": "직접 활성/비활성화 하기", + "warningTimezone": "서버 표준 시간대를 사용해요", + "weekdayShortMon": "월", + "weekdayShortTue": "화", + "weekdayShortWed": "수", + "weekdayShortThu": "목", + "weekdayShortFri": "금", + "weekdayShortSat": "토", + "weekdayShortSun": "일", + "dayOfWeek": "요일", + "dayOfMonth": "날짜", + "lastDay": "마지막 날", + "lastDay1": "매월 마지막 날", + "lastDay2": "매월 마지막 날", + "lastDay4": "매월 네 번째 마지막 날", + "No Maintenance": "점검 일정이 없어요", + "pauseMaintenanceMsg": "정말 일시 정지 할까요?", + "maintenanceStatus-inactive": "비활성화", + "maintenanceStatus-scheduled": "예약됨", + "maintenanceStatus-ended": "완료됨", + "maintenanceStatus-unknown": "알 수 없음", + "Server Timezone": "서버 시간대", + "Display Timezone": "표시 시간대", + "statusPageMaintenanceEndDate": "종료", + "IconUrl": "아이콘 URL", + "Enable DNS Cache": "(사용되지 않음) HTTP(S) 모니터를 위한 DNS 캐시 활성화", + "Enable": "활성화", + "Disable": "비활성화", + "Single Maintenance Window": "단일 점검", + "Maintenance Time Window of a Day": "점검 시간", + "Effective Date Range": "유효 날짜 범위 (옵션)", + "Schedule Maintenance": "점검 예약하기", + "Date and Time": "날짜 및 시간", + "DateTime Range": "날짜 시간 범위", + "wayToGetZohoCliqURL": "{0}에서 Webhook을 어떻게 만드는지 배울 수 있어요.", + "enableGRPCTls": "TLS 연결 gRPC 요청 전송 허용", + "grpcMethodDescription": "메서드 이름은 sayHello, check와 같은 카멜 케이스로 변환되어요.", + "deleteMaintenanceMsg": "정말 이 점검을 삭제할까요?", + "recurringIntervalMessage": "매일 한 번 실행 | {0}일마다 한 번 실행", + "affectedMonitorsDescription": "현재 유지보수에 영향을 받는 모니터를 선택하세요.", + "affectedStatusPages": "점검 메시지를 표시할 상태 페이지 선택하기", + "Kook": "Kook", + "atLeastOneMonitor": "적어도 1개 이상의 모니터를 선택하세요.", + "wayToGetKookBotToken": "{0} 에서 애플리케이션을 만들고 봇 토큰을 얻어요", + "Help": "도움말", + "Game": "게임", + "General Monitor Type": "일반 모니터 유형", + "Passive Monitor Type": "수동 모니터 유형", + "Specific Monitor Type": "특정 모니터 유형", + "Monitor": "모니터", + "Resend Notification if Down X times consecutively": "연속적인 다운으로 판단해 알림을 재전송할 기준 횟수", + "Schedule maintenance": "유지보수 예약", + "Affected Monitors": "영향을 받는 모니터", + "Pick Affected Monitors...": "영향을 받는 모니터 선택…", + "Start of maintenance": "점검 시작", + "All Status Pages": "모든 상태 페이지", + "Select status pages...": "상태 페이지 선택…", + "Custom": "사용자 지정", + "webhookAdditionalHeadersTitle": "추가 헤더", + "webhookAdditionalHeadersDesc": "Webhook과 함께 전송되는 추가 헤더를 설정합니다. 각각의 헤더는 JSON 키/값으로 이루어져야 합니다.", + "HTTP Headers": "HTTP 헤더", + "Trust Proxy": "프록시 신뢰", + "API Keys": "API 키", + "markdownSupported": "마크다운 문법 사용 가능", + "telegramMessageThreadID": "(선택) 메시지 스레드 ID", + "Clone": "복제", + "cloneOf": "{0}의 복제본", + "Clone Monitor": "모니터 복제", + "telegramProtectContent": "포워딩/저장 보호", + "telegramProtectContentDescription": "활성화 할경우 텔레그램 봇 메시지는 포워딩 및 저장으로부터 보호됩니다.", + "telegramSendSilentlyDescription": "조용히 메시지를 보냅니다. 사용자들은 무음으로 알림을 받습니다.", + "telegramSendSilently": "무음 알림", + "Add New Tag": "새 태그 추가", + "Edit Tag": "태그 편집", + "Server Address": "서버 주소", + "Learn More": "자세히 알아보기", + "Continue": "계속", + "Key Added": "키 추가됨", + "No API Keys": "API 키 없음", + "disableAPIKeyMsg": "이 API 키를 비활성화하시겠습니까?", + "deleteAPIKeyMsg": "이 API 키를 삭제하시겠습니까?", + "Generate": "생성", + "Body Encoding": "본문(Body) 인코딩", + "Expiry": "만료", + "Expiry date": "만료일", + "Don't expire": "만료되지 않음", + "notificationRegional": "지역별", + "Google Analytics ID": "Google 애널리틱스 ID", + "Add API Key": "API 키 추가", + "apiKeyAddedMsg": "API 키가 추가되었습니다. 다시 표시되지 않으므로 메모해 두세요.", + "pagertreeCritical": "긴급", + "apiKey-active": "활성", + "lunaseaUserID": "사용자 ID", + "apiKey-expired": "만료됨", + "Expires": "만료", + "twilioAuthToken": "인증 토큰 / API 키 시크릿", + "twilioFromNumber": "발신 번호", + "twilioToNumber": "번호에서", + "twilioAccountSID": "계정 SID", + "pagertreeUrgency": "긴급", + "sameAsServerTimezone": "서버 시간대로 설정하기", + "startDateTime": "시작 시간", + "endDateTime": "종료 시간", + "cronExpression": "Cron 값", + "cronSchedule": "스케줄: ", + "invalidCronExpression": "알수없는 Cron 값입니다: {0}", + "Add Another": "다른 항목 추가", + "apiKey-inactive": "비활성화", + "pagertreeIntegrationUrl": "Integration URL", + "pagertreeLow": "낮음", + "pagertreeMedium": "중간", + "pagertreeHigh": "높음", + "pagertreeResolve": "자동으로 해결", + "pagertreeDoNothing": "아무것도 하지 않음", + "wayToGetPagerTreeIntegrationURL": "PagerTree에서 Uptime Kuma 통합을 생성한 후 엔드포인트를 복사합니다. 세부 정보 보기 {0}", + "lunaseaTarget": "대상", + "lunaseaDeviceID": "디바이스 ID", + "statusPageRefreshIn": "{0} 후 새로고침", + "telegramMessageThreadIDDescription": "포럼의 대상 메시지 쓰레드(주제)에 대한 선택적 고유 식별인, 포럼 관리자 그룹에만 해당", + "pagertreeSilent": "없음", + "setupDatabaseChooseDatabase": "어떤 데이터베이스를 사용하시겠습니까?", + "setupDatabaseEmbeddedMariaDB": "추가 설정이 필요하지 않습니다. 이 도커 이미지에는 MariaDB가 자동으로 포함 및 구성되어 있으며, Uptime Kuma는 유닉스 소켓을 통해 데이터베이스에 연결합니다.", + "setupDatabaseMariaDB": "외부 MariaDB 데이터베이스에 연결합니다. 데이터베이스 연결 정보를 설정해야 합니다.", + "setupDatabaseSQLite": "소규모 배포에 권장되는 간단한 데이터베이스 파일입니다. Uptime Kuma는 v2.0.0 이전까지 SQLite를 기본 데이터베이스로 사용했습니다.", + "dbName": "데이터베이스 이름", + "filterActive": "활성", + "filterActivePaused": "일시 정지", + "Home": "홈", + "Cannot connect to the socket server": "소켓 서버에 연결할 수 없습니다.", + "Reconnecting...": "다시 연결하는 중...", + "Json Query": "JSON 쿼리", + "settingUpDatabaseMSG": "데이터베이스를 설정하는 중입니다. 시간이 걸릴 수 있으니 잠시만 기다려 주세요.", + "enableNSCD": "모든 DNS 요청을 캐싱하기 위해 NSCD (Name Service Cache Daemon) 활성화", + "pushOthers": "기타", + "programmingLanguages": "프로그래밍 언어", + "Select": "선택", + "Edit Maintenance": "점검 수정하기", + "styleElapsedTime": "하트비트 바 아래 표시되는 경과 시간", + "styleElapsedTimeShowNoLine": "표시 (선 없음)", + "styleElapsedTimeShowWithLine": "표시 (선 있음)", + "chromeExecutable": "Chrome/Chromium 실행 파일", + "chromeExecutableAutoDetect": "자동 감지", + "Invert Keyword": "키워드 반전", + "Expected Value": "기댓값", + "Add a domain": "도메인 추가", + "Remove domain": "도메인 '{0}' 제거", + "Monitor Group": "모니터 그룹", + "Monitor Setting": "{0}의 모니터 설정", + "now": "지금", + "time ago": "{0} 전", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "연결하려는 서버의 호스트 이름을 입력하거나, {local_mta}를 사용하려는 경우 {localhost}를 입력합니다", + "-year": "년", + "Json Query Expression": "Json 쿼리 표현식", + "Host URL": "호스트 URL", + "locally configured mail transfer agent": "로컬에 구성된 메일 전송 에이전트", + "ignoreTLSErrorGeneral": "연결 중 TLS/SSL 오류 무시", + "ignoredTLSError": "TLS/SSL 오류가 무시되었습니다", + "liquidIntroduction": "템플릿은 Liquid 템플릿 언어를 통해 생성됩니다. 사용법은 {0}을 참조하세요. 사용 가능한 변수는 다음과 같습니다:", + "templateMsg": "알림 메시지", + "templateLimitedToUpDownCertNotifications": "온라인/오프라인/인증서 만료 알림에만 사용 가능", + "templateLimitedToUpDownNotifications": "온라인/오프라인 알림에만 사용 가능", + "webhookBodyPresetOption": "사전 설정 - {0}", + "successKeyword": "성공 키워드", + "successKeywordExplanation": "성공으로 간주할 MQTT 키워드", + "Reset Token": "토큰 초기화", + "Check/Uncheck": "체크/체크 해제", + "pushViewCode": "푸시 모니터는 어떻게 사용하나요? (코드 보기)", + "Search monitored sites": "모니터링 중인 사이트 검색", + "templateHeartbeatJSON": "하트비트를 설명하는 오브젝트", + "shrinkDatabaseDescriptionSqlite": "SQLite 데이터베이스에 대해 {vacuum}을(를) 트리거합니다. {auto_vacuum}이 이미 활성화되어 있지만, 이는 데이터베이스를 조각 모음하거나 {vacuum} 명령어처럼 개별 데이터베이스 페이지를 다시 정리하지는 않습니다.", + "statusPageSpecialSlugDesc": "특별한 주소 {0}: 아무런 주소도 입력되지 않으면 이 페이지가 보여요", + "Add a new expiry notification day": "새 만료 알림 날짜 추가", + "Refresh Interval Description": "이 상태 페이지는 {0}초마다 완전 새로고침(F5) 돼요", + "telegramServerUrlDescription": "텔레그램 봇 API의 제한을 해제하거나, 차단된 지역(중국, 이란 등)에서 액세스하려면 {0}을 클릭하세요. 기본값: {1}", + "chromeExecutableDescription": "Docker 사용자의 경우, Chromium이 아직 설치되지 않았다면 이를 설치하고 테스트 결과를 표시하는 데 몇 분이 걸릴 수 있어요. 1GB의 디스크 공간을 사용해요.", + "templateMonitorJSON": "모니터를 설명하는 오브젝트", + "webhookBodyCustomOption": "사용자 지정 본문 (Body)", + "telegramServerUrl": "(선택) 서버 URL", + "and": "그리고", + "emailCustomisableContent": "사용자 지정 가능한 콘텐츠", + "smtpLiquidIntroduction": "다음 두 개 필드는 Liquid 템플릿 언어를 통해 템플릿화할 수 있습니다. 사용 지침은 {0}을 참조하세요. 사용 가능한 변수는 다음과 같습니다:", + "leave blank for default subject": "기본값을 사용하려면 비워두세요", + "emailCustomBody": "커스텀 Body", + "leave blank for default body": "기본값을 사용하려면 비워두세요", + "templateServiceName": "서비스명", + "templateHostnameOrURL": "호스트명 또는 URL", + "templateStatus": "상태", + "selectedMonitorCount": "선택됨: {0}", + "Remove the expiry notification": "만료 알림 날짜 제거", + "Refresh Interval": "새로고침 주기", + "noDockerHostMsg": "사용할 수 없습니다. 먼저 도커 호스트를 설정하세요.", + "DockerHostRequired": "이 모니터를 위한 Docker 호스트를 설정해 주세요.", + "tailscalePingWarning": "Tailscale Ping 모니터를 사용하려면 Docker를 사용하지 않고 Uptime Kuma를 설치해야 하며, 서버에 Tailscale 클라이언트도 설치해야 합니다.", + "telegramUseTemplate": "커스텀 메시지 템플릿 사용", + "telegramUseTemplateDescription": "활성화하면 메시지를 보낼 때 커스텀 템플릿을 사용해요.", + "telegramTemplateFormatDescription": "텔레그램은 메시지에 다양한 마크업 언어를 사용할 수 있어요. 자세한 내용은 텔레그램 {0}을 참조하세요.", + "RabbitMQ Username": "RabbitMQ 사용자명", + "RabbitMQ Password": "RabbitMQ 비밀번호", + "wahaSession": "세션", + "emailTemplateMsg": "알림 메시지", + "Select message type": "메시지 유형 선택", + "Send to channel": "채널로 전송", + "Create new forum post": "새 포럼 게시물 만들기", + "Your User ID": "사용자 ID", + "emailTemplateMonitorJSON": "모니터를 설명하는 객체", + "postToExistingThread": "기존 스레드/포럼 게시물에 게시", + "forumPostName": "포럼 게시물 이름", + "threadForumPostID": "스레드 / 포럼 게시물 ID", + "e.g. {discordThreadID}": "예: {discordThreadID}", + "whatHappensAtForumPost": "새 포럼 게시물을 만드세요. 기존 게시물에는 메시지가 게시되지 않습니다. 기존 게시물에 게시하려면 \"{option}\"을 사용하세요", + "wayToGetDiscordThreadId": "스레드/포럼 게시물 ID를 얻는 것은 채널 ID를 얻는 것과 비슷합니다. ID를 얻는 방법에 대해 자세히 알아보세요. {0}", + "Channel access token (Long-lived)": "채널 액세스 토큰(장기)", + "invertKeywordDescription": "키워드가 존재하지 않는지 살펴보세요.", + "emailTemplateLimitedToUpDownNotification": "UP/DOWN 하트비트에만 사용 가능, 그렇지 않으면 null", + "tagAlreadyOnMonitor": "이 태그 (이름 및 값)은 이미 모니터에 있거나 추가 보류 중입니다.", + "tagNameExists": "이 이름을 가진 시스템 태그는 이미 존재합니다. 목록에서 선택하거나 다른 이름을 사용하세요.", + "Use HTML for custom E-mail body": "맞춤형 이메일 본문에 HTML을 사용하세요", + "jsonQueryDescription": "서버의 JSON 응답에서 JSON 쿼리를 사용하거나, JSON이 아닐 경우 원시 응답을 위해 \"$\"를 사용하여 특정 데이터를 파싱하고 추출합니다. 추출된 결과는 문자열로 기대값과 비교됩니다. 문서는 {0}을 참조하고, 쿼리 실험은 {1}을 사용하세요.", + "Don't mention people": "사람들을 멘션하지 마세요", + "Notify Channel": "알림 채널", + "aboutNotifyChannel": "채널 알림은 해당 채널의 모든 구성원에게 데스크톱 또는 모바일 알림을 전송하며, 이들은 상태가 활성으로 설정되어 있든 자리 비움으로 설정되어 있든 관계없이 알림을 받습니다.", + "smseagleMsgTtsAdvanced": "고급 텍스트 음성 변환 통화", + "smseagleApiv2": "APIv2 (새로운 연동에 권장됨)", + "smspartnerApiurl": "{0}에 있는 대시보드에서 API 키를 확인할 수 있습니다", + "Server URL should not contain the nfty topic": "서버 URL에 nfty 토픽을 포함해서는 안 됨", + "defaultFriendlyName": "새로운 모니터", + "smspartnerPhoneNumber": "전화번호(들)", + "Add Tags": "태그 추가", + "tagAlreadyStaged": "이 태그 (이름과 값)은 이미 이 배치에 대해 단계적으로 설정되어 있습니다.", + "emailTemplateHeartbeatJSON": "하트 비트를 설명하는 객체", + "pushoverMessageTtl": "메시지 TTL(초)", + "Bark API Version": "Bark API 버전", + "Mentioning": "멘션하기", + "Mention group": "{group}을(를) 멘션", + "setup a new monitor group": "새 모니터 그룹 설정", + "openModalTo": "{0}을(를) 위한 모달 열기", + "aboutSlackUsername": "메시지 발신자의 표시 이름을 변경합니다. 누군가를 언급하려면, 친숙한 이름(friendly name)에 포함하세요.", + "smseagleGroupV2": "전화번호부 그룹 ID(들)", + "smseagleContactV2": "전화번호부 연락처 ID(들)", + "smseagleMsgType": "메시지 타입", + "smseagleMsgSms": "SMS 메시지 (기본값)", + "smseagleMsgRing": "통화 울리기", + "smseagleMsgTts": "텍스트 음성 변환 통화", + "smseagleDuration": "지속 시간 (초)", + "smseagleTtsModel": "텍스트 음성 변환 모델 ID", + "smseagleApiType": "API 버전", + "smseagleApiv1": "APIv1 (기존 프로젝트 및 하위 호환용)", + "smseagleDocs": "문서 또는 APIv2 사용 가능 여부를 확인하세요: {0}", + "smseagleComma": "여러 개를 입력할 경우 쉼표로 구분해야 합니다", + "smspartnerPhoneNumberHelptext": "번호는 국제 형식 {0}, {1}이어야 하며 여러 번호는 {2}로 구분해야 합니다", + "smspartnerSenderName": "SMS 발신자 이름", + "smspartnerSenderNameInfo": "3자 이상 11자 이하의 일반 문자여야 함", + "PushDeer Server": "PushDeer 서버", + "pushDeerServerDescription": "비워 두면 공식 서버를 사용함", + "SpugPush Template Code": "템플릿 코드", + "ntfyAuthenticationMethod": "인증 방법", + "ntfyPriorityHelptextAllEvents": "모든 이벤트는 최대 우선순위로 전송됨", + "twilioApiKey": "Api 키 (선택)", + "ntfyPriorityHelptextAllExceptDown": "우선 순위가 {1}인 {0}-이벤트를 제외한 모든 이벤트가 이 우선순위로 전송됩니다", + "ntfyUsernameAndPassword": "사용자명과 비밀번호", + "Show Clickable Link": "클릭 가능한 링크 표시", + "mqttHostnameTip": "이 형식을 소용해주세요 {hostnameFormat}", + "Clear All Events": "모든 이벤트 지우기", + "clearAllEventsMsg": "모든 이벤트를 지우시겠습니까?", + "No monitors found": "모니터를 찾지 못했습니다.", + "Could not clear events": "{failed}/{total} 이벤트를 지우지 못했습니다", + "ntfyPriorityHelptextPriorityHigherThanDown": "일반 우선순위는 {0} 우선순위보다 높아야 합니다. 우선순위 {1}은 {0} 우선순위 {2}보다 높습니다", + "ntfyPriorityDown": "DOWN 이벤트에 대한 우선순위", + "Show Clickable Link Description": "이 상태 페이지에 접근 권한이 있는 모든 사용자가 모니터링 URL에 접근할 수 있습니다.", + "Badge Duration (in hours)": "배지 유효 기간 (시간)", + "Badge Label": "배지 라벨", + "Open Badge Generator": "오픈 배지 생성기", + "Badge Label Color": "배지 라벨 색상", + "Badge Color": "배지 색상", + "Badge Preview": "배지 미리보기", + "Badge Pending Color": "보류 배지 색상", + "Badge Down Color": "다운 배지 색상", + "Badge Up Color": "업 배지 색상", + "Badge Maintenance Color": "유지보수 배지 색상", + "Path": "경로", + "mqttWebSocketPath": "MQTT 웹 소켓 경로", + "mqttWebsocketPathExplanation": "웹 소켓을 통한 MQTT 연결용 웹 소켓 경로 (예: /mqtt)", + "mqttWebsocketPathInvalid": "유효한 웹 소켓 경로 형식을 사용하세요", + "Events cleared successfully": "모든 이벤트 삭제 완료.", + "Badge Warn Color": "경고 배지 색상", + "Template plain text instead of using cards": "카드 대신 템플릿 일반 텍스트 사용", + "Badge Type": "배지 종류", + "Badge Generator": "{0}의 배지 생성기", + "Badge Style": "배지 스타일", + "Badge value (For Testing only.)": "배지 값 (테스트용)", + "Badge URL": "배지 URL", + "Group": "그룹", + "monitorToastMessagesLabel": "모니터 토스트 알림", + "monitorToastMessagesDescription": "모니터용 토스트 알림은 지정된 초 단위 시간이 지나면 사라집니다. -1로 설정하면 시간 초과를 비활성화합니다. 0으로 설정하면 토스트 알림을 비활성화합니다.", + "supportBaleChatID": "개인 채팅 / 그룹 / 채널의 ID를 지원해요", + "wayToGetBaleChatID": "봇에 메시지를 보내 채팅 ID를 얻고 밑에 URL로 이동해 chat_id를 볼 수 있어요:", + "wayToGetBaleToken": "토큰은 여기서 얻을 수 있어요: {0}.", + "auto-select": "자동 선택", + "issueWithGoogleChatOnAndroidHelptext": "이를 통해 {issuetackerURL}과 같은 상류 버그를 우회할 수도 있습니다", + "wayToGetHeiiOnCallDetails": "트리거 ID와 API 키를 얻는 방법은 {documentation}에 설명되어 있습니다", + "authIncorrectCreds": "사용자명 혹은 비밀번호가 올바르지 않습니다.", + "successDeleted": "삭제되었습니다.", + "foundChromiumVersion": "Chromium/Chrome 확인됨. 버전 {0}", + "2faAlreadyEnabled": "2FA가 이미 활성화되어 있습니다.", + "authInvalidToken": "유효하지 않은 토큰입니다.", + "Close": "닫기", + "Dingtalk User List": "사용자 ID 목록", + "Enter a list of userId": "userId 목록을 입력하세요", + "Enter a list of mobile": "전화번호 목록을 입력하세요", + "Invalid userId": "유효하지 않은 userId: [{userId}]", + "Mention User List": "사용자 ID 목록을 멘션", + "Invalid mobile": "유효하지 않은 전화번호: [{mobile}]", + "Dingtalk Mobile List": "전화번호 목록", + "Mention Mobile List": "전화번호 목록을 멘션", + "FlashDuty Push URL": "푸시 URL", + "Bitrix24 Webhook URL": "Bitrix24 Webhook URL", + "wayToGetSevenIOApiKey": "app.seven.io > 개발자 > API 키 > 녹색 추가 버튼 아래 대시보드를 방문하세요", + "2faEnabled": "2FA가 활성화되었습니다.", + "2faDisabled": "2FA가 비활성화되었습니다.", + "wayToGetBitrix24Webhook": "{0}의 단계를 따라 webhook을 생성할 수 있습니다", + "successResumed": "성공적으로 재개되었습니다.", + "successPaused": "성공적으로 일시 중지되었습니다.", + "successEdited": "수정되었습니다.", + "successAdded": "추가되었습니다.", + "successAuthChangePassword": "비밀번호가 성공적으로 업데이트되었습니다.", + "Telephone number": "전화번호", + "SNMP Version": "SNMP 버전", + "Originator": "발신자", + "cellsyntOriginator": "수신자의 휴대폰에 메시지 발신자로 표시됩니다. 허용되는 값과 기능은 originatortype 매개변수에 따라 다릅니다.", + "Message Template": "메세지 템플릿", + "descriptionHelpText": "내부 대시보드에 표시됩니다. 마크다운 사용이 허용되며, 표시 전에 정제 처리됩니다(공백과 들여쓰기 유지).", + "mariadbUseSSLHelptext": "연결 암호화를 활성화합니다. 대부분의 클라우드 데이터베이스에 필요합니다.", + "enableSSL": "SSL/TLS 활성화", + "mariadbCaCertificateLabel": "CA 인증서", + "mariadbCaCertificateHelptext": "자체 서명한 인증서를 사용하려는 경우 PEM 형식의 CA인증서를 붙여넣으세요. 공개 인증 기관에 의해 서명된 인증서를 사용하는 경우에는 빈 칸으로 남겨두세요.", + "years": "{n} 년 | {n} 년", + "Pin this incident": "장애 고정하기", + "Only retry if status code check fails": "상태 코드 확인이 실패했을 때만 재시도", + "retryOnlyOnStatusCodeFailureDescription": "활성화한 경우, HTTP 상태 코드 확인이 실패한 경우(예: 서버가 내려갔을 때)에만 재시도합니다. 상태 코드 확인에 성공했지만 JSON 쿼리가 실패한 경우에는 재시도 없이 바로 내려간 상태로 표시됩니다.", + "versionIs": "버전: {version}", + "Connection Timeout": "연결 시간초과", + "monitorTypeGameServer": "게임 서버", + "monitorTypeDatabase": "데이터베이스 모니터 유형", + "minutes": "{n} 분 | {n} 분", + "minuteShort": "{n} 분 | {n} 분", + "No incidents recorded": "장애 이력 없음", + "Loading...": "불러오는 중...", + "Monitors": "{n} 모니터 | {n} 모니터", + "days": "{n} 일 | {n} 일", + "hours": "{n} 시간 | {n} 시간" +} diff --git a/src/lang/lb.json b/src/lang/lb.json new file mode 100644 index 0000000..f7184b9 --- /dev/null +++ b/src/lang/lb.json @@ -0,0 +1,3 @@ +{ + "languageName": "Lëtzebuergesch" +} diff --git a/src/lang/lt.json b/src/lang/lt.json new file mode 100644 index 0000000..3d74cd1 --- /dev/null +++ b/src/lang/lt.json @@ -0,0 +1,1682 @@ +{ + "languageName": "Lietuvių", + "settingUpDatabaseMSG": "Duomenų bazės nustatymas vykdomas. Tai gali užtrukti, prašome palaukti.", + "Settings": "Parametrai", + "Dashboard": "Informacijos suvestinė", + "Help": "Pagalba", + "New Update": "Naujas atnaujinimas", + "Language": "Kalba", + "Appearance": "Išvaizda", + "General": "Bendra", + "Version": "Versija", + "List": "Sąrašas", + "Home": "Pradžia", + "Add": "Pridėti", + "Add New Monitor": "Pridėti naują stebėjimą", + "Quick Stats": "Greita statistika", + "Up": "Veikia", + "statusMaintenance": "Profilaktikos darbai", + "Unknown": "Nežinomas", + "Cannot connect to the socket server": "Nepavyksta prisijungti prie lizdo serverio", + "Reconnecting...": "Atkuriamas ryšys...", + "General Monitor Type": "Bendras stebėjimo tipas", + "Passive Monitor Type": "Pasyvus stebėjimo tipas", + "markdownSupported": "Palaikoma Markdown sintaksė. Jei naudojamas HTML, venkite tarpų priekyje siekiant išvengti formatavimo problemų.", + "Pause": "Pauzė", + "Name": "Pavadinimas", + "Status": "Būsena", + "DateTime": "Data ir laikas", + "Message": "Žinutė", + "Resume": "Tęsti", + "Edit": "Redaguoti", + "Delete": "Ištrinti", + "Current": "Dabartinis", + "Uptime": "Veikimo trukmė", + "Cert Exp.": "Sertifikato baig.", + "day": "diena | dienos(-ų)", + "-day": "-dienų", + "hour": "valanda | valandos", + "-hour": "-valandos", + "Ping": "Ping", + "Monitor Type": "Stebėjimo tipas", + "Keyword": "Raktažodis", + "Invert Keyword": "Invertuoti raktažodį", + "Json Query": "Json užklausa", + "Friendly Name": "Draugiškas pavadinimas", + "URL": "URL adresas", + "locally configured mail transfer agent": "lokaliai sukonfigūruotas pašto perdavimo agentas", + "Request Timeout": "Užklausos trukmės limitas", + "timeoutAfter": "Trukmės limitas po {0} sekundžių", + "Retries": "Pakartotiniai bandymai", + "Heartbeat Retry Interval": "Patikros signalo kartojimo intervalas", + "Advanced": "Išplėstinis", + "checkEverySecond": "Tikrinti kas {0} sekundžių", + "retryCheckEverySecond": "Pakartotinai bandyti kas {0} sekundžių", + "resendEveryXTimes": "Pakartotinai siųsti kas {0} kartų", + "resendDisabled": "Pakartotinis siuntimas išjungtas", + "ignoreTLSError": "TLS/SSL klaidų ignoravimas HTTPS svetainėse", + "ignoreTLSErrorGeneral": "Ignoruoti TLS/SSL ryšio klaidą", + "upsideDownModeDescription": "Apversti būseną aukštyn kojomis. Jei paslauga yra pasiekiama, ji NEVEIKIA.", + "Upside Down Mode": "Apverstas režimas", + "Max. Redirects": "Maksimalūs peradresavimai", + "pushOptionalParams": "Neprivalomi parametrai: {0}", + "pushViewCode": "Kaip naudoti Push stebėjimą? (Žiūrėti kodą)", + "pushOthers": "Kiti", + "programmingLanguages": "Programavimo kalbos", + "Save": "Išsaugoti", + "Notifications": "Pranešimai", + "Not available, please setup.": "Nepasiekiama, prašome nustatyti.", + "Setup Notification": "Nustatyti pranešimą", + "Light": "Šviesus", + "Theme - Heartbeat Bar": "Tema - Heartbeat Bar", + "styleElapsedTime": "Praėjęs laikas po patikros signalo juosta", + "styleElapsedTimeShowNoLine": "Rodyti (be linijos)", + "styleElapsedTimeShowWithLine": "Rodyti (su linija)", + "Normal": "Normalus", + "Bottom": "Apačia", + "None": "Nėra", + "Search Engine Visibility": "Matomumas paieškos sistemose", + "Allow indexing": "Leisti indeksuoti", + "Change Password": "Keisti slaptažodį", + "Current Password": "Dabartinis slaptažodis", + "New Password": "Naujas slaptažodis", + "Repeat New Password": "Naujas slaptažodis", + "Disable Auth": "Išjungti autentifikavimą", + "Enable Auth": "Įjungti autentifikavimą", + "disableauth.message1": "Ar tikrai norite {disableAuth}?", + "setupDatabaseChooseDatabase": "Kurią duomenų bazę norėtumėte naudoti?", + "setupDatabaseMariaDB": "Prisijunkite prie išorinės MariaDB duomenų bazės. Reikia nurodyti duomenų bazės prisijungimo informaciją.", + "setupDatabaseSQLite": "Paprastas duomenų bazės failas, rekomenduojamas mažoms diegimo aplinkoms. Iki v2.0.0 Uptime Kuma pagal numatytuosius nustatymus naudojo SQLite.", + "setupDatabaseEmbeddedMariaDB": "Nieko nustatyti nereikia. Ši Docker versija automatiškai įdiegia ir sukonfigūruoja MariaDB. Uptime Kuma prisijungs prie šios duomenų bazės per Unix socket.", + "dbName": "Duomenų bazės pavadinimas", + "Theme": "Tema", + "Game": "Žaidimas", + "Primary Base URL": "Pradinis bazės URL adresas", + "Check Update On GitHub": "Patikrinkite atnaujinimą GitHub", + "Down": "Neveikia", + "Pending": "Laukiama", + "Maintenance": "Profilaktikos darbai", + "Specific Monitor Type": "Specifinis stebėjimo tipas", + "pauseDashboardHome": "Pauzė", + "No important events": "Jokių svarbių įvykių", + "Monitor": "Stebėjimas | Stebėjimai", + "Response": "Atsakymas", + "Expected Value": "Tikėtina vertė", + "Hostname": "Pagrindinio kompiuterio vardas", + "Host URL": "Priimančiojo URL adresas", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Įveskite serverio, prie kurio norite prisijungti, pagrindinio kompiuterio pavadinimą (hostname) arba {localhost}, jei ketinate naudoti {local_mta}", + "Port": "Prievadas", + "Heartbeat Interval": "Patikros signalo intervalas", + "Resend Notification if Down X times consecutively": "Pakartotinis pranešimo siuntimas, jei neveikia X kartų iš eilės", + "retriesDescription": "Didžiausias pakartotinių bandymų skaičius, kol paslauga pažymėta, kaip neveikianti, ir išsiųstas pranešimas", + "maxRedirectDescription": "Didžiausias galimų nukreipimų skaičius. Nustatykite 0, jei norite išjungti nukreipimus.", + "Accepted Status Codes": "Priimtini būsenos kodai", + "Push URL": "Push URL adresas", + "needPushEvery": "Šį URL adresą turėtumėte iškviesti kas {0} sekundžių.", + "Dark": "Tamsus", + "Auto": "Automatinis", + "Timezone": "Laiko juosta", + "Discourage search engines from indexing site": "Neskatinti paieškos sistemų indeksuoti svetainę", + "Update Password": "Atnaujinti slaptažodį", + "disable authentication": "išjungti autentifikavimą", + "where you intend to implement third-party authentication": "kur ketinate įdiegti trečiosios šalies autentifikavimą", + "Please use this option carefully!": "Atsargiai naudokite šią parinktį!", + "Logout": "Atsijungti", + "Leave": "Palikti", + "Confirm": "Patvirtinti", + "Yes": "Taip", + "No": "Ne", + "Password": "Slaptažodis", + "Remember me": "Prisiminti mane", + "Login": "Prisijungti", + "add one": "pridėti vieną", + "Notification Type": "Pranešimo tipas", + "Email": "El. paštas", + "Test": "Testas", + "Resolver Server": "Resolverio serveris", + "Last Result": "Paskutinis rezultatas", + "Create your admin account": "Susikurkite administratoriaus paskyrą", + "Repeat Password": "Pakartoti slaptažodį", + "Export Backup": "Eksportuoti atsarginę kopiją", + "Export": "Eksportuoti", + "Import": "Importuoti", + "respTime": "Reakcijos laikas (ms)", + "notAvailableShort": "N/P", + "Create": "Sukurti", + "Clear Data": "Išvalyti duomenis", + "Events": "Įvykiai", + "Heartbeats": "Patikros signalai", + "Affected Monitors": "Paveikti stebėjimai", + "Pick Affected Monitors...": "Pasirinkite paveiktus stebėjimus…", + "All Status Pages": "Visi būsenų puslapiai", + "alertWrongFileType": "Pasirinkite JSON failą.", + "Clear all statistics": "Pravalyti visą statistiką", + "Overwrite": "Perrašyti", + "Options": "Parinktys", + "Keep both": "Palikti abu", + "Verify Token": "Paikrinti žetoną", + "Enable 2FA": "Įjungti 2FA", + "Disable 2FA": "Išjungti 2FA", + "2FA Settings": "2FA nustatymai", + "filterActive": "Aktyvus", + "filterActivePaused": "Sustabdytas", + "Active": "Aktyvus", + "Inactive": "Neaktyvus", + "Token": "Prieigos raktas", + "Show URI": "Rodyti URL", + "Tags": "Žymės", + "Add New Tag": "Pridėti naują žymą", + "Add New below or Select...": "Pridėti naują žemiau arba pasirinkti…", + "color": "Spalva", + "value (optional)": "reikšmė (pasirinktinai)", + "Gray": "Pilka", + "Red": "Raudona", + "Green": "Žalia", + "Blue": "Mėlyna", + "Indigo": "Indigo", + "Purple": "Violetinė", + "Custom": "Pasirinktina", + "Search monitored sites": "Stebimų svetainių paieška", + "Avg. Ping": "Vidutinis Ping", + "Entry Page": "Puslapis įėjus", + "statusPageRefreshIn": "Perkrauti per: {0}", + "No Services": "Paslaugų nėra", + "All Systems Operational": "Visos sistemos veikia", + "Partially Degraded Service": "Dalinai pablogėjusi paslauga", + "Degraded Service": "Pablogėjusi paslauga", + "Add Group": "Pridėti grupę", + "Add a monitor": "Pridėti stebėjimą", + "Edit Status Page": "Redaguoti būsenos puslapį", + "Status Page": "Būsenos puslapis", + "here": "čia", + "Required": "Privaloma", + "Post URL": "Įrašo URL", + "Content Type": "Turinio tipas", + "disableauth.message2": "Jis skirtas scenarijams {intendThirdPartyAuth} priešais Uptime Kuma, pavyzdžiui, Cloudflare Access, Authelia ar kitus autentifikavimo mechanizmus.", + "I understand, please disable": "Aš suprantu, prašau išjungti", + "Username": "Vartotojo vardas", + "No Monitors, please": "Prašome jokių stebėjimų", + "Certificate Info": "Sertifikato informacija", + "Resource Record Type": "Išteklių įrašo tipas", + "Import Backup": "Importuoti atsarginę kopiją", + "Default enabled": "Pagal nutylėjimą įjungta", + "Apply on all existing monitors": "Taikyti visiems esamiems stebėjimams", + "Auto Get": "Automatinis gavimas", + "Schedule maintenance": "Planuoti profilaktikos darbus", + "Start of maintenance": "Profilaktikos pradžia", + "Select status pages...": "Pasirinkite būsenos puslapius…", + "alertNoFile": "Pasirinkite importuojamą failą.", + "Skip existing": "Praleisti esamus", + "Setup 2FA": "Nustatyti 2FA", + "Two Factor Authentication": "Dviejų veiksnių autentifikavimas", + "Tag with this name already exist.": "Žyma tokiu pavadinimu jau egzistuoja.", + "Tag with this value already exist.": "Žyma tokia reikšme jau egzistuoja.", + "Orange": "Oranžinė", + "Pink": "Rožinė", + "Search...": "Ieškoti…", + "Avg. Response": "Vidutinė reakcija", + "statusPageNothing": "Čia nieko nėra, pridėkite grupę arba stebėjimą.", + "Go to Dashboard": "Eiti į skydelį", + "Status Pages": "Būsenos puslapiai", + "defaultNotificationName": "Mano {notification} pranešimas ({number})", + "Server URL": "Serverio URL", + "Method": "Metodas", + "appriseInstalled": "Apprise yra įdiegtas.", + "Current User": "Dabartinis vartotojas", + "Style": "Stilius", + "Switch to Light Theme": "Pereiti į šviesią temą", + "Cancel": "Atšaukti", + "Learn More": "Sužinoti Daugiau", + "Expiry": "Galiojimas", + "apiKeyAddedMsg": "Jūsų API raktas buvo pridėtas. Įsidėmėkite jį, nes jis daugiau nebus rodomas.", + "or": "arba", + "No Maintenance": "Nėra planuojamų darbų", + "Priority": "Prioritetas", + "appriseNotInstalled": "Apprise yra neįdiegtas. {0}", + "Body": "Turinys", + "PasswordsDoNotMatch": "Slaptažodžiai nesutampa.", + "records": "įrašai", + "One record": "Vienas įrašas", + "topic": "Tema", + "Info": "Informacija", + "Security": "Sauga", + "Steam API Key": "Steam API raktas", + "Shrink Database": "Sumažinti duomenų bazę", + "Default": "Numatytas", + "HTTP Options": "HTTP Nustatymai", + "Create Incident": "Sukurti Incidentą", + "Title": "Pavadinimas", + "Content": "Turinys", + "warning": "įspėjimas", + "info": "informacija", + "error": "klaida", + "primary": "pagrindinė", + "light": "šviesi", + "dark": "tamsi", + "Created": "Sukurta", + "Last Updated": "Atnaujinta", + "Switch to Dark Theme": "Pereiti į tamsią temą", + "Discard": "Atmesti", + "Select": "Pasirinkti", + "selectedMonitorCount": "Pasirinkta: {0}", + "recurringInterval": "Intervalas", + "Recurring": "Pasikartojantis", + "statusPageMaintenanceEndDate": "Pabaiga", + "pauseMaintenanceMsg": "Ar tikrai norite sustabdyti?", + "maintenanceStatus-under-maintenance": "Vykdomi profilaktikos darbai", + "maintenanceStatus-inactive": "Neaktyvus", + "maintenanceStatus-scheduled": "Suplanuotas", + "maintenanceStatus-ended": "Pabaigtas", + "maintenanceStatus-unknown": "Nežinoma", + "Body Encoding": "Turinio kodavimas", + "API Keys": "API Raktai", + "Expiry date": "Galiojimo data", + "Don't expire": "Nesibaigia galiojimo laikas", + "Continue": "Tęsti", + "Add Another": "Pridėti Kitą", + "Key Added": "Raktas Pridėtas", + "Add API Key": "Pridėti API Raktą", + "No API Keys": "Nėra API Raktų", + "apiKey-active": "Aktyvus", + "apiKey-expired": "Pasibaigęs", + "apiKey-inactive": "Neaktyvus", + "Expires": "Galioja", + "disableAPIKeyMsg": "Ar tikrai norite išjungti šį API raktą?", + "deleteAPIKeyMsg": "Ar tikrai norite ištrinti šį API raktą?", + "Generate": "Generuoti", + "now": "dabar", + "time ago": "prieš {0}", + "-year": "-metai", + "ignoredTLSError": "TLS/SSL klaidos buvo ignoruotos", + "Headers": "Antraštės", + "HeadersInvalidFormat": "Užklausos antraštės neteisingai pateiktos JSON formatu: ", + "shrinkDatabaseDescriptionSqlite": "Įvykdykite SQLite duomenų bazės {vacuum} komandą. {auto_vacuum} jau įjungtas, tačiau jis neperorganizuoja duomenų bazės ir nesupakuoja atskirų puslapių taip, kaip tai daro {vacuum} komanda.", + "Please read": "Prašome perskaityti", + "RabbitMQ Username": "RabbitMQ vartotojo vardas", + "RabbitMQ Password": "RabbitMQ slaptažodis", + "Telephone number": "Telefono numeris", + "chromeExecutableAutoDetect": "Aptikti automatiškai", + "Proxy Server": "Tarpinis serveris", + "Check/Uncheck": "Žymėti/atžymėti", + "PushDeer Server": "PushDeer serveris", + "Money": "Pinigai", + "confirmUninstallPlugin": "Ar tikrai norite pašalinti šį įskiepį?", + "Enable DNS Cache": "(Nebenaudojama) Įjungti DNS talpyklavimą HTTP(s) stebėjimams", + "Not running": "Nepaleistas", + "Reverse Proxy": "Atvirkštinis tarpinis serveris", + "Date Created": "Sukūrimo data", + "Refresh Interval Description": "Būsenos puslapis padarys pilną puslapio perkrovimą kas {0} sekundes(-ių)", + "API Key": "API raktas", + "emailTemplateMsg": "pranešimo tekstas", + "wayToGetDiscordURL": "Tai galite rasti nuėję į „Server Settings -> Integrations -> View Webhooks -> New Webhook“", + "dnsPortDescription": "DNS serverio prievadas. Numatytoji reikšmė – 53. Prievadą galite bet kada pakeisti.", + "pushoversounds cashregister": "Kasos aparatas", + "pushoversounds gamelan": "Gamelanas", + "apiCredentials": "API kredencialai", + "promosmsAllowLongSMS": "Leisti ilgas SMS žinutes", + "Uptime Kuma URL": "Uptime Kuma URL adresas", + "Add a domain": "Pridėti naują domeną", + "serwersmsAPIPassword": "API slaptažodis", + "Browser Screenshot": "Naršyklės ekrano nuotrauka", + "gtxMessagingToHint": "Nacionalinis formatas, prasideda su „+“ ({e164}, {e212} arba {e214})", + "Private Number": "Privatus numeris", + "signedInDisp": "Prisijungta kaip {0}", + "wayToGetTelegramToken": "Jūs galite gauti prieigos raktą iš {0}.", + "Send to channel": "Siųsti į kanalą", + "e.g. {discordThreadID}": "pvz. {discordThreadID}", + "Notification Sound": "Pranešimo garsas", + "For safety, must use secret key": "Saugumo sumetimais būtina naudoti slaptąjį raktą", + "Group": "Grupė", + "Mechanism": "Mechanizmas", + "Group Name": "Grupės pavadinimas", + "clearDataOlderThan": "Saugote stebėjimo istorijos duomenis {0} dienų.", + "infiniteRetention": "Nustatykite 0, kad būtų begalinis saugojimas.", + "backupDescription3": "Jautrūs duomenys, pvz., pranešimams naudojami prieigos raktai, yra įtraukti į eksporto failą, todėl laikykite jį saugiai.", + "chromeExecutable": "Chrome/Chromium vykdomoji programa", + "certificationExpiryDescription": "HTTPS stebėjimai sukelia pranešimą, kai baigiasi TLS sertifikato galiojimo laikas:", + "trustProxyDescription": "Pasitikėkite „X-Forwarded-*“ antraštėmis. Jei norite gauti teisingą kliento IP adresą, o jūsų Uptime Kuma yra už tarpinio serverio, pavyzdžiui, Nginx arba Apache, turėtumėte įjungti šią funkciją.", + "Valid": "Galiojantis", + "No consecutive dashes": "Be iš eilės einančių brūkšnelių", + "statusPageSpecialSlugDesc": "Speciali nuorodos dalis {0}: šis puslapis bus rodomas, jei nuorodos dalis nenurodyta", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Esamas ryšys gali būti prarastas, jei šiuo metu jungiatės per Cloudflare tunelį. Ar tikrai norite jį sustabdyti? Įveskite savo dabartinį slaptažodį, kad patvirtintumėte.", + "RadiusCallingStationIdDescription": "Skambinančio įrenginio identifikatorius", + "There might be a typing error in the address.": "Adrese gali būti įvedimo klaida.", + "DockerHostRequired": "Prašome nustatyti Docker serverį šiam stebėjimui.", + "tailscalePingWarning": "Norėdami naudoti Tailscale Ping stebėjimą, turite įdiegti Uptime Kuma be Docker, taip pat įdiegti Tailscale klientą savo serveryje.", + "telegramServerUrlDescription": "Norėdami pašalinti Telegram bot API apribojimus arba gauti prieigą į blokuotas sritis (Kinija, Iranas ir kt.), spustelėkite {0} dėl daugiau informacijos. Pagal nutylėjimą: {1}", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Ilgalaikį prieigos raktą galite sukurti paspaudę savo profilio pavadinimą (apačioje kairėje), slinkę į apačią ir paspaudę „Sukurti raktą“.", + "backupOutdatedWarning": "Pasenęs: Kadangi buvo pridėta daug naujų funkcijų ir šis atsarginių kopijų funkcionalumas nėra pilnai palaikomas, jis negali sukurti ar atkurti pilnos atsarginės kopijos.", + "chromeExecutableDescription": "Docker naudotojams, jei Chromium dar nėra įdiegtas, įdiegimas ir testo rezultatų rodymas gali užtrukti kelias minutes. Tai užima 1GB disko vietos.", + "smtpLiquidIntroduction": "Žemiau pateikti du laukai gali būti šablonizuojami naudojant Liquid šablonų kalbą. Prašome peržiūrėti {0} naudojimo instrukcijas. Štai prieinami kintamieji:", + "whatHappensAtForumPost": "Sukurti naują forumo įrašą. Tai NE skelbia žinučių esamame įraše. Norėdami skelbti esamame įraše, naudokite „{option}“", + "wayToCheckSignalURL": "Galite patikrinti šią nuorodą, kad pamatytumėte, kaip ją nustatyti:", + "wayToGetLineChannelToken": "Pirmiausia pasiekite {0}, sukurkite tiekėją ir kanalą (Žinučių API), tada iš aukščiau nurodytų meniu punktų galite gauti kanalo prieigos raktą ir vartotojo ID.", + "aboutMattermostChannelName": "Galite pakeisti numatytąjį kanalą, į kurį siunčiami pranešimai per Webhook, įvesdami kanalo pavadinimą lauke „Kanalo pavadinimas“. Tai turi būti įjungta Mattermost Webhook nustatymuose. Pvz.: #kitas-kanalas", + "resolverserverDescription": "Cloudflare yra numatytasis serveris. Galite nurodyti kableliais atskirtą IP adresų arba kompiuterių vardų sąrašą.", + "enableDefaultNotificationDescription": "Šis pranešimas bus įjungtas numatytai naujiems stebėjimams. Vis dėlto galite jį atjungti kiekvienam stebėjimui atskirai.", + "confirmImportMsg": "Ar tikrai norite importuoti atsarginę kopiją? Prašome patvirtinti, kad pasirinkote teisingą importo parinktį.", + "keywordDescription": "Ieškoti raktažodžio paprastame HTML arba JSON atsakyme. Paieška yra jautri didžiosioms ir mažosioms raidėms.", + "jsonQueryDescription": "Išanalizuokite ir ištraukite konkrečius duomenis iš serverio JSON atsakymo, naudodami JSON užklausą arba naudokite \"$\" neapdorotam atsakymui, jei nesitikite JSON formato. Rezultatas vėliau lyginamas su tikėtina verte, kaip eilutėmis. Žr. {0} dokumentacijai ir naudokite {1} eksperimentavimui su užklausomis.", + "pushoversounds alien": "Svetimo signalo (ilgas)", + "apprise": "Apprise (palaiko daugiau nei 50 pranešimų paslaugų)", + "wayToGetKookGuildID": "Įjunkite „Kūrėjo režimą“ Kook nustatymuose ir dešiniuoju pelės mygtuku spustelėkite gildiją, kad gautumėte jos ID", + "octopushTypePremium": "Premium (greitas – rekomenduojamas įspėjimams)", + "octopushPhoneNumber": "Telefono numeris (tarptautiniu formatu, pvz.: +33612345678)", + "SendGrid API Key": "SendGrid API raktas", + "conditionValuePlaceholder": "Reikšmė", + "Proto Service Name": "Proto paslaugos pavadinimas", + "goAlertInfo": "GoAlert yra atviro kodo programa, skirta nuolatinio budėjimo tvarkaraščio sudarymui, automatizuotam eskalavimui ir pranešimams (pvz., SMS arba balso skambučiams). Automatiškai susiekite tinkamą asmenį, tinkamu būdu ir tinkamu laiku! {0}", + "SecretKey": "SlaptasRaktas", + "promosmsTypeEco": "SMS ECO - pigus, bet lėtas ir dažnai perkrautas. Ribota tik Lenkijos gavėjams.", + "promosmsTypeFull": "SMS FULL - Aukštesnės klasės SMS paslauga. Galite naudoti savo siuntėjo vardą (pirmiausia reikia registruoti vardą). Patikima pranešimams.", + "promosmsSMSSender": "SMS siuntėjo vardas: iš anksto užregistruotas vardas arba vienas iš numatytųjų: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "matrixDesc2": "Labai rekomenduojama sukurti naują vartotoją ir nenaudoti savo esamo Matrix vartotojo prieigos rakto, nes jis suteiks pilną prieigą prie jūsų paskyros ir visų kambarių, į kuriuos esate įstoję. Vietoj to, sukurkite naują vartotoją ir pakvieskite jį tik į tą kambarį, kuriame norite gauti pranešimus. Prieigos raktą galite gauti vykdydami {0}", + "Notify Channel": "Pranešimų kanalas", + "aboutNotifyChannel": "Pranešimų kanalas iššauks stacionaraus kompiuterio ir mobiliojo pranešimus visiems kanalo nariams, nepriklausomai nuo to, ar jie pažymėti kaip aktyvūs, ar ne.", + "wayToGetPagerDutyKey": "Gali gauti eidamas į Paslaugos → Paslaugų katalogas → (Pasirink paslaugą) → Integracijos → Pridėti integraciją. Čia gali ieškoti „Events API V2“. Daugiau informacijos {0}", + "smseagleContact": "Telefonų knygos kontakto vardas (-ai)", + "smseaglePriority": "Žinutės prioritetas (0–9, aukščiausias prioritetas = 9)", + "smspartnerPhoneNumberHelptext": "Numeris turi būti tarptautiniu formatu {0}, {1}. Daugelius numerius atskirk {2}", + "Server URL should not contain the nfty topic": "Serverio URL neturėtų turėti ntfy temos", + "wayToGetPagerTreeIntegrationURL": "Sukūrus Uptime Kuma integraciją PagerTree, nukopijuok Endpoint adresą. Išsamiau {0}", + "ntfyPriorityHelptextAllExceptDown": "Visi įvykiai siunčiami su šiuo prioritetu, išskyrus {0} įvykius, kurių prioritetas yra {1}", + "Badge Prefix": "Ženklo reikšmės prefiksas", + "Badge Label Suffix": "Ženklo etiketės sufiksas", + "Badge Maintenance Color": "Profilaktikos būsenos ženklo spalva", + "monitorToastMessagesDescription": "Iššokančios žinutės stebėjimams išnyksta po nurodyto laiko sekundėmis. Nustatykite -1, kad išjungtumėte laiko limitą, arba 0, kad išjungtumėte iššokančias žinutes.", + "Enable Kafka Producer Auto Topic Creation": "Įjungti automatinį Kafka gamintojo temų kūrimą", + "noGroupMonitorMsg": "Neprieinama. Pirmiausia sukurkite grupės stebėjimą.", + "wayToGetFlashDutyKey": "Norėdami integruoti Uptime Kuma su Flashduty: eikite į Channels > pasirinkite kanalą > Integrations > Add a new integration, pasirinkite Uptime Kuma ir nukopijuokite Push URL.", + "Plain Text": "Paprastas tekstas", + "Message Template": "Žinutės šablonas", + "Template Format": "Šablono formatas", + "webhookJsonDesc": "{0} tinka visiems šiuolaikiniams HTTP serveriams, tokiems kaip Express.js", + "webhookFormDataDesc": "{multipart} tinka PHP. JSON reikės analizuoti naudojant {decodeFunction}", + "webhookBodyPresetOption": "Išankstinis nustatymas - {0}", + "Application Token": "Programos prieigos raktas", + "templateMsg": "pranešimo tekstas", + "PushUrl": "Siuntimo URL", + "BodyInvalidFormat": "Užklausos turinys neteisingai pateiktas JSON formatu: ", + "Reset Token": "Atstatyti prieigos raktą", + "Done": "Baigta", + "recent": "Naujausi", + "critical": "kritinė", + "Pick Accepted Status Codes...": "Pasirinkite priimtinus būsenos kodus…", + "Pick a RR-Type...": "Pasirinkite RR tipą…", + "successKeywordExplanation": "MQTT raktažodis, kuris bus laikomas sėkmingu", + "successKeyword": "Sėkmės raktažodis", + "topicExplanation": "Stebimas MQTT kanalas", + "steamApiKeyDescription": "Steam žaidimų serverio stebėjimui reikalingas Steam Web-API raktas. Užregistruoti savo API raktą galite čia: ", + "Monitor History": "Stebėjimo istorija", + "Post": "Paskelbti", + "danger": "pavojus", + "and": "ir", + "Proxy Protocol": "Tarpinio serverio protokolas", + "YZJ Webhook URL": "YZJ Webhook URL adresas", + "YZJ Robot Token": "YZJ Robot prieigos raktas", + "telegramServerUrl": "(Pasirinktinai) Serverio URL", + "dayOfWeek": "Savaitės diena", + "dayOfMonth": "Mėnesio diena", + "cronSchedule": "Tvarkaraštis: ", + "cronExpression": "Cron išraiška", + "weekdayShortMon": "Pir", + "weekdayShortTue": "Ant", + "weekdayShortWed": "Tre", + "weekdayShortThu": "Ket", + "weekdayShortFri": "Pen", + "weekdayShortSat": "Šeš", + "weekdayShortSun": "Sek", + "invalidCronExpression": "Neteisinga Cron išraiška: {0}", + "lastDay": "Paskutinė diena", + "lastDay1": "Paskutinė mėnesio diena", + "YOUR BOT TOKEN HERE": "JŪSŲ BOTO PRIEIGOS RAKTAS ČIA", + "default: notify all devices": "numatyta: pranešti visiems įrenginiams", + "wayToGetLineNotifyToken": "Jūs galite gauti prieigos raktą iš {0}", + "startDateTime": "Pradžios data/laikas", + "endDateTime": "Pabaigos data/laikas", + "chatIDNotFound": "Nepavyko rasti pokalbio ID; pirmiausia išsiųskite žinutę šiam botui", + "disableCloudflaredNoAuthMsg": "Jūs esate režime be autentifikacijos, slaptažodis nereikalingas.", + "Long-Lived Access Token": "Ilgalaikis prieigos raktas", + "Notification Service": "Pranešimų paslauga", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Pranešimų paslaugų sąrašą rasite Home Assistant skiltyje „Kūrėjo įrankiai > Paslaugos“, ieškodami žodžio „notification“, kad rastumėte savo įrenginio/telefono pavadinimą.", + "Home Assistant URL": "Namų Asistento URL", + "Automations can optionally be triggered in Home Assistant:": "Automatizacijas galima pasirinktinai paleisti Home Assistant sistemoje:", + "Trigger type:": "Paleidimo tipas:", + "Event type:": "Įvykio tipas:", + "Event data:": "Įvykio duomenys:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Tada pasirinkite veiksmą, pavyzdžiui, pakeiskite sceną į tą, kur RGB šviesa yra raudona.", + "Frontend Version": "Priekinės dalies versija", + "Frontend Version do not match backend version!": "Priekinės dalies versija nesutampa su galinės dalies versija!", + "backupRecommend": "Prašome atsarginę kopiją daryti tiesiogiai iš skaidinio arba duomenų aplanko (./data/).", + "Optional": "Pasirinktinai", + "sameAsServerTimezone": "Tokia pati kaip serverio laiko juosta", + "strategyManual": "Rankiniu būdu aktyvuojamas/neaktyvuojamas", + "warningTimezone": "Naudojama serverio laiko juosta", + "install": "Įdiegti", + "installing": "diegiama", + "uninstall": "Pašalinti", + "uninstalling": "Šalinama", + "lastDay2": "Antra paskutinė mėnesio diena", + "lastDay3": "Trečia paskutinė mėnesio diena", + "lastDay4": "Ketvirta paskutinė mėnesio diena", + "Display Timezone": "Rodoma laiko zona", + "Server Timezone": "Serverio laiko zona", + "IconUrl": "Piktogramos URL", + "Enable": "Įjungti", + "Disable": "Išjungti", + "plugin": "Įskiepis | Įskiepiai", + "Clone": "Klonuoti", + "cloneOf": "{0} klonas", + "smtp": "El. paštas (SMTP)", + "Schedule Maintenance": "Suplanuoti profilaktikos darbus", + "Edit Maintenance": "Redaguoti profilaktikos darbus", + "Date and Time": "Data ir laikas", + "Clone Monitor": "Kopijuoti stebėjimą", + "notificationRegional": "Regioninis", + "enableNSCD": "Įjungti NSCD (Name Service Cache Daemon) visų DNS užklausų talpyklavimui", + "dnsCacheDescription": "Gali neveikti kai kuriose IPv6 aplinkose, išjunkite, jei susiduriate su problemomis.", + "Single Maintenance Window": "Vienas profilaktikos darbų langas", + "Maintenance Time Window of a Day": "Profilaktikos laiko langas per dieną", + "Effective Date Range": "Galiojimo datos intervalas (pasirinktinai)", + "DateTime Range": "Datos ir laiko intervalas", + "loadingError": "Nepavyko gauti duomenų, prašome bandyti vėliau.", + "secureOptionNone": "Nėra / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "From Email": "Siuntėjo el. paštas", + "Ignore TLS Error": "Ignoruoti TLS klaidą", + "leave blank for default subject": "palikite tuščią numatytai temai", + "emailCustomisableContent": "Pritaikomas turinys", + "emailCustomSubject": "Pasirinktina tema", + "emailCustomBody": "Pasirinktinas turinys", + "leave blank for default body": "Palikite tuščią, jei norite naudoti numatytąjį turinį", + "emailTemplateMonitorJSON": "objektas, apibūdinantis stebėjimą", + "To Email": "Gavėjo el. paštas", + "Bot Display Name": "Bot'o rodomas vardas", + "emailTemplateHeartbeatJSON": "objektas, apibūdinantis patikros signalą", + "emailTemplateLimitedToUpDownNotification": "Prieinamas tik VEIKIA/NEVEIKIA patikros signalams, kitu atveju – null", + "smtpCC": "CC", + "Discord Webhook URL": "Discord Webhook URL adresas", + "Prefix Custom Message": "Priešdėlis pasirinktinam pranešimui", + "Hello @everyone is...": "Sveiki, {'@'}everyone yra…", + "Select message type": "Pasirinkite pranešimo tipą", + "postToExistingThread": "Skelbti esamoje temoje / forumo įraše", + "Create new forum post": "Sukurti naują forumo įrašą", + "forumPostName": "Forumo įrašo pavadinimas", + "threadForumPostID": "Temos / forumo įrašo ID", + "wayToGetDiscordThreadId": "Gauti temos / forumo įrašo ID yra panašu į kanalo ID gavimą. Plačiau apie ID gavimą rasite {0}", + "wayToGetTeamsURL": "Galite sužinoti, kaip sukurti webhook URL {0}.", + "Access Token": "Prieigos raktas", + "Channel access token": "Kanalo prieigos raktas", + "Icon URL": "Piktogramos URL", + "Basic Settings": "Baziniai nustatymai", + "User ID": "Vartotojo ID", + "Your User ID": "Jūsų vartotojo ID", + "deleteMonitorMsg": "Ar tikrai norite ištrinti šį stebėjimą?", + "wayToGetZohoCliqURL": "Galite sužinoti, kaip sukurti webhook URL {0}.", + "needSignalAPI": "Turite turėti Signal klientą su REST API.", + "Number": "Numeris", + "Recipients": "Gavėjai", + "Channel access token (Long-lived)": "Kanalo prieigos raktas (ilgalaikis)", + "Line Developers Console": "Line kūrėjų konsolė", + "lineDevConsoleTo": "Line kūrėjų konsolė - {0}", + "Messaging API": "Žinučių API", + "aboutIconURL": "Galite nurodyti nuorodą į paveikslėlį lauke „Piktogramos URL“, kad pakeistumėte numatytąjį profilio paveikslėlį. Jei nustatytas piktogramos jaustukas, jis nebus naudojamas.", + "dataRetentionTimeError": "Laikymo periodas turi būti 0 arba didesnis", + "confirmDeleteTagMsg": "Ar tikrai norite ištrinti šią žymą? Stebėjimai, susieti su šia žyma, nebus ištrinti.", + "enableGRPCTls": "Leisti siųsti gRPC užklausas per TLS ryšį", + "grpcMethodDescription": "Metodo pavadinimas konvertuojamas į camelCase formatą, pvz., sakytiLabas, tikrinti ir pan.", + "acceptedStatusCodesDescription": "Pasirinkite būsenos kodus, kurie laikomi sėkmingu atsaku.", + "twoFAVerifyLabel": "Įveskite savo prieigos kodą 2FA patvirtinti:", + "confirmEnableTwoFAMsg": "Ar Jūs norite įjungti 2FA?", + "confirmDisableTwoFAMsg": "Ar Jūs norite išjungti 2FA?", + "promosmsPassword": "API slaptažodis", + "promosmsLogin": "API prisijungimo vardas", + "pushoversounds bike": "Dviratis", + "pushoversounds classical": "Klasika", + "backupDescription": "Jūs galite pasidaryti visų stebėjimų ir pranešimų kopijas JSON formatu.", + "notificationDescription": "Kad pranešimai veiktų, jie turi būti priskirti stebėjimui.", + "deleteMaintenanceMsg": "Ar tikrai norite ištrinti šiuos profilaktikos darbus?", + "deleteNotificationMsg": "Ar tikrai norite ištrinti šį pranešimą visiems stebėjimams?", + "rrtypeDescription": "Pasirinkite RR tipą, kurį norite stebėti", + "pauseMonitorMsg": "Ar tikrai norite pristabdyti?", + "clearEventsMsg": "Ar tikrai norite ištrinti visus šio stebėjimo įvykius? Tai visam laikui pašalins susijusius pranešimų duomenis ir šio veiksmo nebus galima atšaukti.", + "clearHeartbeatsMsg": "Ar tikrai norite ištrinti visus šio stebėjimo būsenos patikrinimus? Tai visam laikui pašalins visus būsenos patikrinimų duomenis ir šio veiksmo nebus galima atšaukti.", + "confirmClearStatisticsMsg": "Ar tikrai norite ištrinti VISAS statistikas?", + "importHandleDescription": "Pasirinkite „Praleisti esamus“, jei norite praleisti kiekvieną stebėjimą ar pranešimą su tuo pačiu pavadinimu. „Perrašyti“ ištrins visus esamus stebėjimus ir pranešimus.", + "tokenValidSettingsMsg": "Prieigos kodas yra galiojantis! Dabar galite išsaugoti 2FA nustatymus.", + "recurringIntervalMessage": "Vykdyti kartą per dieną | Vykdyti kartą kas {0} dienas", + "affectedMonitorsDescription": "Pasirinkite stebėjimus, kuriuos paveikė dabartiniai profilaktikos darbai", + "affectedStatusPages": "Rodyti šį profilaktikos pranešimą pasirinktuose būsenos puslapiuose", + "atLeastOneMonitor": "Pasirinkite bent vieną paveiktą stebėjimą", + "passwordNotMatchMsg": "Pakartotas slaptažodis nesutampa.", + "backupDescription2": "Pastaba: istorija ir įvykių duomenys neįtraukti.", + "endpoint": "galinis taškas", + "octopushAPIKey": "„API key“ iš HTTP API prisijungimo duomenų valdymo skydelyje", + "octopushLogin": "„Login“ iš HTTP API prisijungimo duomenų valdymo skydelyje", + "pushoversounds pushover": "Pushover (numatyta)", + "pushoversounds bugle": "Trimitas", + "pushoversounds cosmic": "Kosminis", + "pushoversounds falling": "Kritantis", + "pushoversounds siren": "Sirena", + "Device": "Įrenginys", + "SMS Type": "SMS tipas", + "Strategy": "Strategija", + "Status:": "Būsena: {0}", + "Enable TLS": "Įjungti TLS", + "pushyToken": "Įrenginio prieigos raktas", + "User Key": "Vartotojo raktas", + "Message Title": "Žinutės tema", + "Apprise URL": "Apprise URL adresas", + "pushoversounds incoming": "Gaunamas", + "pushoversounds intermission": "Pertrauka", + "pushoversounds magic": "Magija", + "pushoversounds mechanical": "Mechaninis", + "pushoversounds pianobar": "Pianino klavišas", + "pushoversounds spacealarm": "Erdvinis signalas", + "pushoversounds tugboat": "Traukimo laivas", + "pushoversounds persistent": "Pastovus (ilgas)", + "pushoversounds echo": "Pushover Echo (ilgas)", + "pushoversounds updown": "Up Down (ilgas)", + "pushoversounds vibrate": "Tik vibracija", + "pushoversounds none": "Nėra (tylus)", + "pushyAPIKey": "Slaptas API raktas", + "GoogleChat": "Google Chat (tik Google Workspace)", + "wayToGetKookBotToken": "Sukurkite programą ir gaukite savo bot'o raktą adresu {0}", + "Guild ID": "Gildijos ID", + "More info on:": "Daugiau informacijos: {0}", + "pushoverDesc1": "Avarinis prioritetas (2) turi numatytą 30 sekundžių tarp bandymų laiko limitą ir pasibaigs po 1 valandos.", + "pushoverDesc2": "Jei norite siųsti pranešimus skirtingiems įrenginiams, užpildykite lauką „Įrenginys“.", + "pushoverMessageTtl": "Žinutės TTL (sekundės)", + "octopushTypeLowCost": "Low Cost (lėtas – kartais blokuoja operatorius)", + "checkPrice": "Patikrinkite {0} kainas:", + "octopushLegacyHint": "Ar naudojate senąją Octopush versiją (2011-2020) ar naująją?", + "Check octopush prices": "Patikrinkite Octopush kainas {0}.", + "octopushSMSSender": "SMS siuntėjo vardas: 3-11 simbolių (raidės, skaičiai ir tarpas) (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea įrenginio ID", + "Example:": "Pavyzdys: {0}", + "Read more:": "Plačiau: {0}", + "Free Mobile User Identifier": "Free Mobile vartotojo identifikatorius", + "Free Mobile API Key": "Free Mobile API raktas", + "Proto Method": "Proto metodas", + "Proto Content": "Proto turinys", + "Lowcost": "Mažai kainuojantis", + "Economy": "Ekonomiškas", + "Retry": "Pakartojimų skaičius", + "Topic": "Tema", + "Platform": "Platforma", + "Channel Name": "Kanalo pavadinimas", + "WebHookUrl": "WebHook URL", + "Don't mention people": "Neminėti žmonių", + "Mention group": "Paminėti {group}", + "Device Token": "Įrenginio prieigos raktas", + "Huawei": "Huawei", + "Proxy server has authentication": "Tarpinis serveris reikalauja autentifikacijos", + "Setup Proxy": "Nustatyti tarpinį serverį", + "Base URL": "Bazinis URL", + "high": "aukštas", + "SMSManager API Docs": "SMSManager API dokumentacija", + "SendKey": "SiuntimoRaktas", + "Gateway Type": "Šliuzo tipas", + "You can divide numbers with": "Galite atskirti skaičius su", + "goAlertIntegrationKeyInfo": "Gaukite bendrą API integravimo raktą paslaugai šiuo formatu „aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee”, dažniausiai tai bus prieigos rakto parametro reikšmė nukopijuotame URL.", + "AccessKeyId": "Prieigos rakto ID", + "PhoneNumbers": "TelefonoNumeriai", + "TemplateCode": "SablonoKodas", + "SecretAccessKey": "Prieigos rakto slaptasis raktas", + "SignName": "ParasoVardas", + "Sms template must contain parameters: ": "SMS šablone turi būti nurodyti parametrai: ", + "Bark API Version": "Bark API versija", + "Bark Endpoint": "Bark endpointas", + "Bark Group": "Bark grupė", + "Bark Sound": "Bark garsas", + "Mentioning": "Paminėjimas", + "High": "Aukštas", + "WeCom Bot Key": "WeCom bot'o raktas", + "promosmsTypeFlash": "SMS FLASH - Žinutė automatiškai bus rodoma gavėjo įrenginyje. Ribota tik Lenkijos gavėjams.", + "promosmsTypeSpeed": "SMS SPEED - Aukščiausias sistemos prioritetas. Labai greitas ir patikimas, tačiau brangesnis (apie du kartus kainuoja nei SMS FULL).", + "promosmsPhoneNumber": "Telefono numeris (Lenkijos gavėjui galite praleisti vietinį kodą)", + "Feishu WebHookUrl": "Feishu WebHook URL", + "matrixHomeserverURL": "Namų serverio URL (su http(s):// ir, jei reikia, prievadu)", + "Internal Room Id": "Vidinio kambario ID", + "matrixDesc1": "Vidinį kambario ID rasite pažangiojoje kambario nustatymų skiltyje savo Matrix kliente. Jis turėtų atrodyti panašiai: !QMdRCpUIfLwsfjxye6:home.server.", + "Remove domain": "Ištrinti domeną '{0}'", + "Icon Emoji": "Piktogramos jaustukas", + "smtpDkimDomain": "Domeno vardas", + "smtpDkimPrivateKey": "Privatus raktas", + "do nothing": "nieko nedaryti", + "alertaApiKey": "API raktas", + "serwersmsPhoneNumber": "Telefono numeris", + "smseagleTo": "Telefono numeris(-iai)", + "smspartnerSenderName": "SMS siuntėjo vardas", + "Recipient Number": "Gavėjo numeris", + "From Name/Number": "Siuntėjo vardas/numeris", + "smseagleRecipientType": "Gavėjo tipas", + "setup a new monitor group": "sukurti naują stebėjimo grupę", + "openModalTo": "Atidaryti modalą {0}", + "signalImportant": "SVARBU: Negalite maišyti grupių ir numerių gavėjų!", + "aboutWebhooks": "Išsamiau apie Webhooks: {0}", + "smtpDkimKeySelector": "Raktų parinkiklis", + "smtpDkimHashAlgo": "Maišos algoritmas (pasirinktinai)", + "smtpDkimskipFields": "Nepasirašyti antraščių raktai (pasirinktinai)", + "smtpDkimheaderFieldNames": "Pasirašyti antraščių raktai (pasirinktinai)", + "Integration Key": "Integracijos raktas", + "Integration URL": "Integracijos URL", + "Auto resolve or acknowledged": "Automatiškai išspręsta arba patvirtinta", + "auto acknowledged": "automatiškai patvirtinta", + "auto resolve": "automatiškai išspręsta", + "alertaApiEndpoint": "API galinis taškas", + "alertaEnvironment": "Aplinka", + "alertaAlertState": "Pranešimo būsena", + "alertaRecoverState": "Atkūrimo būsena", + "serwersmsAPIUser": "API vartotojo vardas (įskaitant webapi_ prefiksą)", + "serwersmsSenderName": "SMS siuntėjo vardas (užregistruotas per klientų portalą)", + "smseagleGroup": "Telefonų knygos grupės pavadinimas (-ai)", + "smseagleRecipient": "Gavėjas (-ai) (daugelius atskirk kableliais)", + "smseagleToken": "API prieigos raktas", + "smseagleUrl": "Jūsų SMSEagle įrenginio URL", + "smseagleEncoding": "Siųsti Unicode formatu (numatyta GSM-7)", + "smspartnerApiurl": "Savo API raktą rasite savo skydelyje adresu {0}", + "smspartnerPhoneNumber": "Telefono numeris (-ai)", + "smspartnerSenderNameInfo": "Turi būti tarp 3..=11 įprastų simbolių", + "Leave blank to use a shared sender number.": "Palikite tuščią, kad naudotumėte bendrinamą siuntėjo numerį.", + "Octopush API Version": "Octopush API versija", + "Legacy Octopush-DM": "Senasis Octopush-DM", + "ntfyUsernameAndPassword": "Vartotojo vardas ir slaptažodis", + "twilioFromNumber": "Siuntėjo numeris", + "twilioToNumber": "Gavėjo numeris", + "ntfy Topic": "ntfy Tema", + "onebotHttpAddress": "OneBot HTTP adresas", + "onebotMessageType": "OneBot žinutės tipas", + "onebotGroupMessage": "Grupė", + "onebotPrivateMessage": "Privati", + "onebotUserOrGroupId": "Grupės/Vartotojo ID", + "onebotSafetyTips": "Saugumo tikslais būtina nustatyti prieigos raktą", + "pushDeerServerDescription": "Palikite tuščią, kad naudotumėte oficialų serverį", + "lunaseaDeviceID": "Įrenginio ID", + "lunaseaUserID": "Vartotojo ID", + "ntfyAuthenticationMethod": "Autentifikacijos metodas", + "ntfyPriorityHelptextAllEvents": "Visi įvykiai siunčiami su aukščiausiu prioritetu", + "twilioAccountSID": "Paskyros SID", + "twilioApiKey": "API raktas (nebūtina)", + "twilioAuthToken": "Autorizacijos raktas / API slaptas raktas", + "Monitor Setting": "{0} stebėjimo nustatymai", + "Show Clickable Link": "Rodyti paspaudžiamą nuorodą", + "Show Clickable Link Description": "Jei pažymėta, visi, turintys prieigą prie šio būsenos puslapio, gali pasiekti stebėjimo URL.", + "Open Badge Generator": "Atidaryti ženklo generatorių", + "Badge Generator": "{0} ženklo generatorius", + "Badge Type": "Ženklo tipas", + "Badge Duration (in hours)": "Ženklo trukmė (valandomis)", + "Badge Label": "Ženklo etiketė", + "Badge Suffix": "Ženklo sufiksas", + "Badge Label Color": "Ženklo etiketės spalva", + "Badge Color": "Ženklo spalva", + "Close": "Uždaryti", + "Message format": "Pranešimo formatas", + "Monitor Group": "Stebėjimo grupė", + "Badge Label Prefix": "Ženklo etiketės prefiksas", + "Badge Preview": "Ženklo peržiūra", + "Badge Up Color": "Veikiančio ženklo spalva", + "Badge Down Color": "Neveikiančio ženklo spalva", + "Badge Pending Color": "Laukiamos būsenos ženklo spalva", + "Badge Warn Color": "Įspėjimo ženklo spalva", + "Badge Warn Days": "Įspėjimo dienų skaičius", + "Badge Down Days": "Neveikiančių dienų skaičius", + "Badge Style": "Ženklo stilius", + "Badge value (For Testing only.)": "Ženklo reikšmė (tik testavimui)", + "Badge URL": "Ženklo URL", + "monitorToastMessagesLabel": "Stebėjimo iššokančios žinutės", + "toastErrorTimeout": "Klaidų pranešimų laiko limitas", + "toastSuccessTimeout": "Sėkmingų pranešimų laiko limitas", + "Kafka Brokers": "Kafka brokeriai", + "Enter the list of brokers": "Įveskite brokerių sąrašą", + "Press Enter to add broker": "Paspauskite Enter, kad pridėtumėte brokerį", + "Kafka Topic Name": "Kafka temos pavadinimas", + "Kafka Producer Message": "Kafka gamintojo žinutė", + "Enable Kafka SSL": "Įjungti Kafka SSL", + "Kafka SASL Options": "Kafka SASL parinktys", + "Pick a SASL Mechanism...": "Pasirinkite SASL mechanizmą…", + "AccessKey Id": "Prieigos rakto ID", + "Secret AccessKey": "Prieigos rakto slaptasis raktas", + "Session Token": "Sesijos prieigos raktas", + "Request Body": "Užklausos turinys", + "FlashDuty Severity": "Svarba", + "nostrRelays": "Nostr tarpininkai", + "Saved.": "Išsaugota.", + "authInvalidToken": "Neteisingas prieigos raktas.", + "2faAlreadyEnabled": "2FA jau yra įjungtas.", + "2faEnabled": "2FA įjungtas.", + "2faDisabled": "2FA išjungtas.", + "successEdited": "Sėkmingai redaguota.", + "successDeleted": "Sėkmingai ištrinta.", + "successResumed": "Sėkmingai pratęsta.", + "successAdded": "Sėkmingai pridėta.", + "successAuthChangePassword": "Slaptažodis sėkmingai atnaujintas.", + "successBackupRestored": "Sėkmingai atstatytos atsarginės kopijos.", + "successDisabled": "Sėkmingai įšjungta.", + "successEnabled": "Sėkmingai įjungta.", + "tagNotFound": "Žymė nerasta.", + "Remote Browsers": "Nuotolinės naršyklės", + "Remote Browser": "Nuotolinė naršyklė", + "Add a Remote Browser": "Pridėti nuotolinę naršyklę", + "Remote Browser not found!": "Nuotolinė naršyklė nerasta!", + "useRemoteBrowser": "Naudoti nuotolinę naršyklę", + "Command": "Komanda", + "API URL": "API URL", + "What is a Remote Browser?": "Kas yra nuotolinė naršyklė?", + "successPaused": "Sėkmingai pristabdyta.", + "foundChromiumVersion": "Rasta Chromium/Chrome. Versija: {0}", + "documentationOf": "{0} dokumentacija", + "threemaRecipientTypeIdentityFormat": "8 simboliai", + "threemaRecipientTypePhone": "Telefono numeris", + "To Phone Number": "Gavėjo telefono numeris", + "threemaRecipientTypeEmail": "El. pašto adresas", + "threemaRecipient": "Gavėjas", + "threemaRecipientType": "Gavėjo tipas", + "Allow Long SMS": "Leisti ilgas SMS žinutes", + "max 15 digits": "maks. 15 skaitmenų", + "threemaRecipientTypeIdentity": "Threema ID", + "No tags found.": "Žymių nerasta.", + "Go back to home page.": "Grįžkite į pagrindinį puslapį.", + "Recipient Type": "Gavėjo tipas", + "New Group": "Nauja grupė", + "Add Remote Browser": "Pridėti nuotolinę naršyklę", + "Group ID": "Grupės ID", + "OAuth2: Client Credentials": "OAuth2: kliento kredencialai", + "Authentication Method": "Autentifikavimo metodas", + "Authorization Header": "Autorizacijos antraštė", + "Sound": "Garsas", + "Client ID": "Kliento ID", + "Guitar": "Gitara", + "From": "Nuo", + "smtpDkimDesc": "Prašome kreiptis į Nodemailer DKIM {0} naudojimo instrukcijoms.", + "smtpDkimSettings": "DKIM nustatymai", + "documentation": "dokumentacija", + "aboutSlackUsername": "Keičia rodomą pranešimo siuntėjo vardą. Jei norite ką nors paminėti, įtraukite tai į draugišką pavadinimą.", + "aboutChannelName": "Įveskite kanalo pavadinimą {0} lauke „Kanalo pavadinimas“, jei norite apeiti Webhook kanalą. Pvz.: #kitas-kanalas", + "aboutKumaURL": "Jei paliksite Uptime Kuma URL lauką tuščią, jis bus automatiškai nustatytas kaip Projekto GitHub puslapis.", + "PushDeer Key": "PushDeer raktas", + "wayToGetClickSendSMSToken": "API naudotojo vardą ir API raktą galite gauti čia: {0}.", + "Custom Monitor Type": "Pasirinktinis stebėjimo tipas", + "lunaseaTarget": "Tikslas", + "Edit Tag": "Redaguoti žymę", + "Server Address": "Serverio adresas", + "Google Analytics ID": "Google Analitikos ID", + "pagertreeUrgency": "Svarba", + "pagertreeCritical": "Kritinė", + "pagertreeHigh": "Aukšta", + "pagertreeMedium": "Vidutinė", + "pagertreeLow": "Maža", + "pagertreeIntegrationUrl": "Integracijos URL", + "pagertreeSilent": "Tyli", + "pagertreeDoNothing": "Nieko nedaryti", + "pagertreeResolve": "Automatiškai išspręsti", + "templateHostnameOrURL": "pagrindinio kompiuterio vardas arba nuoroda", + "templateStatus": "būsena", + "emojiCheatSheet": "Jaustukų kodų lentelė: {0}", + "Read more": "Skaityti daugiau", + "default": "Numatytas", + "No Monitors": "Nėra stebėjimų", + "Please input title and content": "Prašome įvesti pavadinimą ir turinį", + "Show Tags": "Rodyti žymes", + "Hide Tags": "Paslėpti žymes", + "No monitors available.": "Nėra galimų stebėjimų.", + "setAsDefault": "Nustatyti kaip numatytą", + "Customize": "Tinkinti", + "proxyDescription": "Kad tarpiniai serveriai veiktų, jie turi būti priskirti stebėjimui.", + "enableProxyDescription": "Šis tarpinis serveris neturės poveikio stebėjimo užklausoms, kol nebus aktyvuotas. Galite laikinai išjungti tarpinį serverį nuo visų stebėjimų pagal aktyvavimo būseną.", + "setAsDefaultProxyDescription": "Šis tarpinis serveris bus įjungtas pagal numatytuosius nustatymus naujiems stebėjimams. Vis dar galite išjungti tarpinį serverį kiekvienam stebėjimui atskirai.", + "templateServiceName": "paslaugos pavadinimas", + "Description": "Aprašymas", + "Add one": "Pridėti vieną", + "Untitled Group": "Grupė be pavadinimo", + "Services": "Paslaugos", + "Powered by": "Sukūrė", + "Custom Footer": "Pasirinktinė apačia", + "Custom CSS": "Pasirinktinis CSS", + "deleteStatusPageMsg": "Ar tikrai norite ištrinti šį būsenos puslapį?", + "Proxies": "Tarpiniai serveriai", + "enabled": "Įjungta", + "deleteProxyMsg": "Ar tikrai norite ištrinti šį tarpinį serverį visiems stebėjimams?", + "Installed": "Įdiegta", + "Not installed": "Neįdiegta", + "Running": "Paleistas", + "Remove Token": "Pašalinti prieigos raktą", + "Start": "Paleisti", + "Stop": "Sustabdyti", + "Next": "Toliau", + "Authentication": "Autentifikacija", + "Page Not Found": "Puslapis nerastas", + "About": "Apie", + "Message:": "Žinutė:", + "HTTP Headers": "HTTP antraštės", + "For example: nginx, Apache and Traefik.": "Pavyzdžiui: nginx, Apache ir Traefik.", + "Subject:": "Tema:", + "Refresh Interval": "Atnaujinimo intervalas", + "Add New Status Page": "Pridėti naują būsenos puslapį", + "Backup": "Padaryti atsargines kopijas", + "Domain Names": "Domenų vardai", + "Footer Text": "Apačios tekstas", + "wayToGetCloudflaredURL": "(Atsisiųskite cloudflared iš {0})", + "cloudflareWebsite": "Cloudflare svetainė", + "User": "Vartotojas", + "No Proxy": "Nėra tarpinio serverio", + "Trust Proxy": "Pasitikėti šiuo tarpiniu serveriu", + "Proxy": "Tarpinis serveris", + "Certificate Chain": "Sertifikato grandinė", + "Invalid": "Negaliojantis", + "Slug": "Nuorodos dalis", + "Accept characters:": "Priimami simboliai:", + "startOrEndWithOnly": "Pradėti arba baigti tik su {0}", + "The slug is already taken. Please choose another slug.": "Ši nuorodos dalis jau užimta. Prašome pasirinkti kitą.", + "HTTP Basic Auth": "HTTP bazinis autentifikavimas", + "New Status Page": "Naujas būsenos puslapis", + "Don't know how to get the token? Please read the guide:": "Nežinote, kaip gauti prieigos raktą? Perskaitykite vadovą:", + "Other Software": "Kitos programinės įrangos", + "Show Powered By": "Rodyti kas sukūrė", + "Valid To:": "Galioja iki:", + "Days Remaining:": "Liko dienų:", + "Issuer:": "Išdavėjas:", + "Fingerprint:": "Pirštų antspaudas:", + "No status pages": "Nėra būsenos puslapių", + "Domain Name Expiry Notification": "Domeno vardo galiojimo pabaigos pranešimas", + "Add a new expiry notification day": "Pridėti naują galiojimo pabaigos pranešimo dieną", + "Remove the expiry notification": "Pašalinti galiojimo pabaigos pranešimą", + "Steam Game Server": "Steam žaidimų serveris", + "Coming Soon": "Jau greitai", + "Query": "Užklausa", + "tcp": "TCP / HTTP", + "Packet Size": "Paketo dydis", + "Chat ID": "Pokalbio ID", + "API Username": "API vartotojo vardas", + "socket": "Lizdas", + "Go back to the previous page.": "Grįžkite į ankstesnį puslapį.", + "Domain": "Domenas", + "Bot Token": "Bot'o prieigos raktas", + "Show update if available": "Rodyti galimus atnaujinimus", + "Also check beta release": "Taip pat patikrinti beta išleidimus", + "settingsCertificateExpiry": "TLS sertifikato galiojimas", + "Using a Reverse Proxy?": "Naudojate atvirkštinį tarpinį serverį?", + "signedInDispDisabled": "Autentifikavimas išjungtas.", + "RadiusSecret": "RADIUS slaptažodis", + "RadiusSecretDescription": "Bendras slaptažodis tarp kliento ir serverio", + "RadiusCalledStationId": "RADIUS iškviesto stoties ID", + "RadiusCalledStationIdDescription": "Iškviesto įrenginio identifikatorius", + "RadiusCallingStationId": "RADIUS skambinančios stoties ID", + "Certificate Expiry Notification": "Sertifikato galiojimo pabaigos pranešimas", + "Check how to config it for WebSocket": "Patikrinkite, kaip sukonfigūruoti per WebSocket", + "Most likely causes:": "Greičiausios priežastys:", + "The resource is no longer available.": "Resursas nebeprieinamas.", + "What you can try:": "Ką galite pabandyti:", + "Retype the address.": "Įveskite adresą iš naujo.", + "Connection String": "Ryšio eilutė", + "Setup Docker Host": "Nustatyti Docker serverį", + "Connection Type": "Ryšio tipas", + "Docker Daemon": "Docker tarnyba", + "noDockerHostMsg": "Nepasiekiama. Pirmiausia nustatykite Docker serverį.", + "deleteDockerHostMsg": "Ar tikrai norite ištrinti šį Docker serverį iš visų stebėjimų?", + "Docker Container": "Docker konteineris", + "Container Name / ID": "Konteinerio vardas / ID", + "Docker Host": "Docker serveris", + "Docker Hosts": "Docker serveriai", + "Workstation": "Darbinė stotis", + "telegramMessageThreadID": "(Pasirinktinai) Žinučių temos ID", + "telegramMessageThreadIDDescription": "Pasirinktinai unikalus identifikatorius tikslinei žinučių temai (forumo temai), skirtas tik forumo supergrupėms", + "telegramSendSilently": "Siųsti tyliai", + "telegramSendSilentlyDescription": "Siunčia žinutę tyliai. Vartotojai gaus pranešimą be garso.", + "Examples": "Pavyzdžiai", + "telegramProtectContent": "Apsaugoti nuo persiuntimo/išsaugojimo", + "telegramProtectContentDescription": "Jei įjungta, Telegram bot'o žinutės bus apsaugotos nuo persiuntimo ir išsaugojimo.", + "telegramUseTemplate": "Naudoti pasirinktinį žinutės šabloną", + "telegramUseTemplateDescription": "Jei įjungta, žinutė bus išsiųsta naudojant pasirinktą šabloną.", + "telegramTemplateFormatDescription": "Telegram leidžia naudoti skirtingas žymėjimo kalbas žinutėms, žr. Telegram {0} dėl išsamesnės informacijos.", + "supportTelegramChatID": "Palaikymas tiesioginiam pokalbiui / grupei / kanalui pokalbio ID", + "wayToGetTelegramChatID": "Savo pokalbio ID galite gauti išsiuntę žinutę botui ir apsilankę šioje nuorodoje, kur pamatysite chat_id:", + "smtpBCC": "BCC", + "pushoversounds climb": "Klimbas (ilgas)", + "Authorization Identity": "Autorizacijos identifikatorius", + "liquidIntroduction": "Šablonavimo galimybės pasiekiamos naudojant Liquid šablonavimo kalbą. Naudojimo instrukcijas rasite {0}.", + "webhookAdditionalHeadersDesc": "Nustato papildomas antraštes, siunčiamas su webhook. Kiekviena antraštė turi būti apibrėžta kaip JSON raktas/reikšmė.", + "templateMonitorJSON": "objektas, apibūdinantis stebėjimą", + "templateLimitedToUpDownNotifications": "prieinama tik VEIKIA/NEVEIKIA pranešimams", + "Json Query Expression": "Json užklausos išraiška", + "webhookAdditionalHeadersTitle": "Papildomos antraštės", + "webhookBodyCustomOption": "Tinkintas turinys", + "Webhook URL": "Webhook URL adresas", + "templateHeartbeatJSON": "objektas, apibūdinantis patikros signalą", + "templateLimitedToUpDownCertNotifications": "prieinama tik VEIKIA/NEVEIKIA/Sertifikato galiojimo pranešimams", + "authIncorrectCreds": "Neteisingas vartotojo vardas arba slaptažodis.", + "nostrRelaysHelp": "Vienas tarpininko URL po vieną eilutėje", + "nostrSender": "Siuntėjo privatus raktas (nsec)", + "nostrRecipients": "Gavėjų viešieji raktai (npub)", + "nostrRecipientsHelp": "npub formatu, po vieną eilutėje", + "showCertificateExpiry": "Rodyti sertifikato galiojimo pabaigą", + "noOrBadCertificate": "Nėra / netinkamas sertifikatas", + "cacheBusterParam": "Pridėti {0} parametrą", + "cacheBusterParamDescription": "Atsitiktinai sugeneruotas parametras, skirtas apeiti talpyklas.", + "gamedigGuessPortDescription": "Valve Server Query Protocol naudojamas prievadas gali skirtis nuo kliento prievado. Pabandykite šį variantą, jei monitorius negali prisijungti prie jūsų serverio.", + "Send rich messages": "Siųsti išsamius pranešimus", + "Bitrix24 Webhook URL": "Bitrix24 Webhook URL adresas", + "authUserInactiveOrDeleted": "Vartotojas neaktyvus arba ištrintas.", + "rabbitmqNodesRequired": "Prašome nustatyti mazgus šiam stebėjimui.", + "RabbitMQ Nodes": "RabbitMQ valdymo mazgai", + "rabbitmqNodesDescription": "Įveskite URL adresą RabbitMQ valdymo mazgams, įskaitant protokolą ir prievadą. Pavyzdys: {0}", + "rabbitmqNodesInvalid": "Prašome naudoti pilnai kvalifikuotą (prasidedantį „http“) URL adresą RabbitMQ mazgams.", + "cellsyntDestination": "Gavėjo telefono numeris, naudojant tarptautinį formatą su pradine 00 ir šalies kodu, pvz., 00447920110000 JK numeriui 07920 110 000 (iš viso iki 17 skaitmenų). Viena HTTP užklausa gali turėti iki 25000 gavėjų, atskirtų kableliais.", + "threemaBasicModeInfo": "Pastaba: Ši integracija naudoja Threema Gateway baziniame režime (serverio pagrindu šifravimas). Išsamesnę informaciją rasite {0}.", + "Custom sound to override default notification sound": "Individualus garsas, pakeičiantis numatytąjį pranešimų garsą", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Naudokite teksto siuntėjo ID arba telefono numerį E.164 formatu, jei norite gauti atsakymus.", + "wahaSession": "Sesija", + "wahaChatId": "Pokalbio ID (telefono numeris / adresato ID / grupės ID)", + "wayToGetWahaApiUrl": "Jūsų WAHA instancijos URL.", + "wayToGetWahaApiKey": "API raktas yra WHATSAPP_API_KEY aplinkos kintamojo reikšmė, kurią naudojote paleidžiant WAHA.", + "wayToGetWahaSession": "Iš šios sesijos WAHA siunčia pranešimus į pokalbio ID. Jį rasite WAHA skydelyje.", + "wayToWriteWahaChatId": "Telefono numeris su tarptautiniu prefiksu, bet be pliuso ženklo pradžioje ({0}), adresato ID ({1}) arba grupės ID ({2}). Pranešimai siunčiami į šį pokalbio ID iš WAHA sesijos.", + "OAuth Token URL": "OAuth prieigos rakto URL", + "invertKeywordDescription": "Ieškokite raktažodžio, kuris yra ankščiau panaudotas, o ne esantis.", + "gamedigGuessPort": "Gamedig: atspėk prievadą", + "wayToGetBitrix24Webhook": "Sukurkite webhook, sekdami nurodymus adresu {0}", + "bitrix24SupportUserID": "Įveskite savo vartotojo ID Bitrix24. ID galite rasti nuorodoje, eidami į vartotojo profilį.", + "remoteBrowsersDescription": "Nuotolinės naršyklės yra alternatyva Chromium paleidimui vietoje. Nustatykite su tokia paslauga kaip browserless.io arba prisijunkite prie savo", + "self-hosted container": "savarankiškai talpinamas konteineris", + "remoteBrowserToggle": "Numatytuoju atveju Chromium veikia Uptime Kuma konteineryje. Galite naudoti nuotolinę naršyklę, persijungę šį jungiklį.", + "deleteRemoteBrowserMessage": "Ar tikrai norite ištrinti šią nuotolinę naršyklę visiems stebėjimams?", + "GrafanaOncallUrl": "Grafana Oncall URL adresas", + "mongodbCommandDescription": "Paleiskite MongoDB komandą prieš duomenų bazę. Daugiau informacijos apie galimas komandas rasite {documentation}", + "wayToGetSevenIOApiKey": "Aplankykite valdymo skydelį adresu app.seven.io > developer > api key > žalias pridėjimo mygtukas", + "senderSevenIO": "Siuntimo numeris arba pavadinimas", + "receiverSevenIO": "Priėmimo numeris", + "receiverInfoSevenIO": "Jei priėmimo numeris nėra Vokietijoje, turite pridėti šalies kodą numerio pradžioje (pvz., JAV šalies kodui 1 naudokite 117612121212 vietoje 017612121212)", + "apiKeySevenIO": "SevenIO API raktas", + "wayToWriteWhapiRecipient": "Telefono numeris su tarptautiniu prefiksu, bet be pliuso ženklas pradžioje ({0}), adresato ID ({1}) arba grupės ID ({2}).", + "wayToGetWhapiUrlAndToken": "API URL ir prieigos raktą galite gauti eidami į norimą kanalą iš {0}", + "whapiRecipient": "Telefono numeris / Adresato ID / Grupės ID", + "wayToGetHeiiOnCallDetails": "Kaip gauti Trigger ID ir API raktus, paaiškinta {documentation}", + "callMeBotGet": "Čia galite sugeneruoti pabaigos tašką {0}, {1} ir {2}. Atkreipkite dėmesį, kad galite susidurti su apribojimais. Apribojimai atrodo tokie: {3}", + "gtxMessagingApiKeyHint": "Savo API raktą rasite: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Iš telefono numerio / Perdavimo kelio kilmės adresas (TPOA)", + "gtxMessagingFromHint": "Mobiliaisiais telefonais jūsų gavėjai mato TPOA kaip pranešimo siuntėją. Leidžiama iki 11 simbolių su raidėmis/skaičiais, trumpasis kodas, vietinis ilgas numeris arba tarptautiniai numeriai ({e164}, {e212} arba {e214})", + "Originator type": "Siuntėjo tipas", + "cellsyntOriginatortypeAlphanumeric": "Raidinė skaitmeninė eilutė (maks. 11 simbolių). Gavėjai negali atsakyti į pranešimą.", + "Alphanumeric (recommended)": "Raidinis skaitmeninis (rekomenduojama)", + "cellsyntOriginatortypeNumeric": "Skaitinė reikšmė (maks. 15 skaitmenų) su tarptautiniu telefono numerio formatu be pradinio 00 (pvz., JK numeris 07920 110 000 turi būti įrašytas kaip 447920110000). Gavėjai gali atsakyti į pranešimą.", + "Originator": "Siuntėjas", + "cellsyntOriginator": "Rodoma gavėjo mobiliajame telefone kaip pranešimo siuntėjas. Leidžiamos reikšmės ir funkcija priklauso nuo parametro originatortype.", + "Destination": "Gavėjas", + "cellsyntSplitLongMessages": "Ilgi pranešimai suskaidomi į iki 6 dalių. 153 x 6 = 918 simbolių.", + "max 11 alphanumeric characters": "maks. 11 simbolių (raidės, skaičiai)", + "Community String": "Bendruomenės eilutė", + "snmpCommunityStringHelptext": "Ši eilutė veikia kaip slaptažodis, skirtas autentifikuoti ir valdyti prieigą prie SNMP palaikomų įrenginių. Suderinkite ją su savo SNMP įrenginio konfigūracija.", + "OID (Object Identifier)": "OID (objekto identifikatorius)", + "snmpOIDHelptext": "Įveskite OID sensoriui ar būsenai, kuriuos norite stebėti. Jei nesate tikri dėl OID, naudokite tinklo valdymo įrankius, tokius kaip MIB naršyklės ar SNMP programinė įranga.", + "Condition": "Sąlyga", + "SNMP Version": "SNMP versija", + "Please enter a valid OID.": "Prašome įvesti galiojantį OID.", + "wayToGetThreemaGateway": "Galite užsiregistruoti Threema Gateway {0}.", + "threemaRecipientTypePhoneFormat": "E.164, be pradinio +", + "threemaSenderIdentity": "Gateway ID", + "threemaSenderIdentityFormat": "8 simboliai, paprastai prasideda ženklu *", + "threemaApiAuthenticationSecret": "Gateway-ID slaptažodis", + "apiKeysDisabledMsg": "API raktai išjungti, nes autentifikacija išjungta.", + "Host Onesender": "Onesender serveris", + "Token Onesender": "Onesender prieigos raktas", + "privateOnesenderDesc": "Įsitikinkite, kad telefono numeris yra tinkamas. Norėdami išsiųsti žinutę į privatų telefono numerį, pvz.: 628123456789", + "groupOnesenderDesc": "Įsitikinkite, kad GroupID yra tinkamas. Norėdami išsiųsti žinutę į grupę, pvz.: 628123456789-342345", + "wayToGetOnesenderUrlandToken": "URL ir prieigos raktą galite gauti apsilankę Onesender svetainėje. Daugiau informacijos {0}", + "Flute": "Fleita", + "Harp": "Arfa", + "Doorbell": "Durų skambutis", + "Elevator": "Liftas", + "Form Data Body": "Formos duomenų turinys", + "Client Secret": "Kliento slaptažodis", + "OAuth Scope": "OAuth apimtis", + "Optional: Space separated list of scopes": "Pasirenkama: tarpu atskirtų apimčių sąrašas", + "Lost connection to the socket server.": "Prarastas ryšys su lizdo serveriu.", + "Cannot connect to the socket server.": "Nepavyksta prisijungti prie lizdo serverio.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL adresas", + "signl4Docs": "Daugiau informacijos apie SIGNL4 konfigūravimą ir kaip gauti SIGNL4 webhook URL rasite {0}.", + "Conditions": "Sąlygos", + "conditionAdd": "Pridėti sąlygą", + "conditionDelete": "Ištrinti sąlygą", + "conditionAddGroup": "Pridėti grupę", + "conditionDeleteGroup": "Ištrinti grupę", + "not contains": "neturi", + "starts with": "prasideda su", + "not starts with": "neprasideda su", + "ends with": "baigiasi su", + "not ends with": "nebaigiasi su", + "less than": "mažiau nei", + "greater than": "daugiau nei", + "less than or equal to": "mažiau arba lygu", + "greater than or equal to": "daugiau arba lygu", + "record": "įrašas", + "Notification Channel": "Pranešimų kanalas", + "Alphanumerical string and hyphens only": "Tik raidinė skaitinė eilutė ir brūkšneliai", + "Arcade": "Arkada", + "Fail": "Nepavyko", + "Correct": "Teisingai", + "Reveal": "Atskleisti", + "Bubble": "Burbulas", + "Scifi": "Mokslinis fantastinis", + "Clear": "Išvalyti", + "Pop": "Pop", + "Time Sensitive (iOS Only)": "Laiko jautrūs (tik iOS)", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Laiko jautrūs pranešimai bus pristatyti iš karto, net jei įrenginys yra „Netrukdyti“ režime.", + "Can be found on:": "Galima rasti: {0}", + "The phone number of the recipient in E.164 format.": "Gavėjo telefono numeris E.164 formatu.", + "rabbitmqHelpText": "Norėdami naudoti stebėjimą, turėsite įjungti valdymo įskiepį savo RabbitMQ sąrankoje. Išsamesnės informacijos rasite {rabitmq_documentation}.", + "Separate multiple email addresses with commas": "Atskirkite kelis el. pašto adresus kableliais", + "equals": "lygu", + "not equals": "nelygu", + "contains": "turi", + "Path": "Kelias", + "mqttWebSocketPath": "MQTT WebSocket kelias", + "mqttWebsocketPathExplanation": "WebSocket kelias MQTT jungtims per WebSocket (pvz., /mqtt)", + "mqttWebsocketPathInvalid": "Naudokite tinkamą WebSocket kelio formatą", + "mqttHostnameTip": "Naudokite šį formatą {hostnameFormat}", + "smseagleApiType": "API versija", + "smseagleApiv1": "APIv1 (esamiems projektams ir atgaliniam suderinamumui)", + "smseagleApiv2": "APIv2 (rekomenduojama naujoms integracijoms)", + "smseagleComma": "Keli elementai turi būti atskirti kableliu", + "Use HTML for custom E-mail body": "Naudokite HTML tinkintam el. laiško turiniui", + "smseagleTtsModel": "Teksto į kalbą modelio ID", + "smseagleDocs": "Patikrinkite dokumentaciją arba APIv2 prieinamumą: {0}", + "SpugPush Template Code": "Šabloninis kodas", + "ntfyPriorityHelptextPriorityHigherThanDown": "Įprastas prioritetas turėtų būti aukštesnis už {0} prioritetą. Prioritetas {1} yra aukštesnis už {0} prioritetą {2}", + "defaultFriendlyName": "Naujas stebėjimas", + "Clear All Events": "Išvalyti visus įvykius", + "clearAllEventsMsg": "Ar tikrai norite ištrinti visus įvykius iš visų stebėjimų? Tai visam laikui pašalins susijusius pranešimų duomenis ir šio veiksmo nebus galima atšaukti.", + "Events cleared successfully": "Įvykiai sėkmingai išvalyti.", + "No monitors found": "Stebėjimų nerasta.", + "Could not clear events": "Nepavyko pašalinti {failed}/{total} įvykių", + "smseagleGroupV2": "Telefono knygos grupės ID(s)", + "smseagleContactV2": "Telefono knygos kontakto ID", + "smseagleMsgSms": "Sms žinutė (numatyta)", + "smseagleDuration": "Trukmė (sekundės)", + "Add Tags": "Pridėti žymes", + "tagAlreadyOnMonitor": "Ši žymė (pavadinimas ir reikšmė) jau yra stebima arba laukia pridėjimo.", + "tagAlreadyStaged": "Ši žymė (pavadinimas ir reikšmė) jau laukia vykdymo šioje partijoje.", + "tagNameExists": "Sisteminė žymė su tokiu pavadinimu jau egzistuoja. Pasirinkite ją iš sąrašo arba naudokite kitą pavadinimą.", + "smseagleMsgType": "Žinutes tipas", + "issueWithGoogleChatOnAndroidHelptext": "Tai taip pat leidžia apeiti aukščiau esančias klaidas, pvz., {issuetackerURL}", + "WebSocket Transport for JMAP": "WebSocket perdavimo kanalas JMAP (JSON metataikomųjų programų protokolui)", + "Enter a list of userId": "Įveskite naudotojų ID sąrašą", + "Enter a list of mobile": "Įveskite mobiliųjų įrenginių sąrašą", + "smseagleMsgRing": "Skambučio signalas", + "twilloMessagingServiceSIDHelptext": "Įveskite savo pranešimų paslaugos SID čia, jei naudojate {twillo_messaging_service_help_link} siuntėjams ir funkcijoms valdyti", + "ignoreSecWebsocketAcceptHeaderDescription": "Leidžia serveriui neatsakyti su Sec-WebSocket-Accept antrašte, jei „websocket“ atnaujinimas pavyksta.", + "Ignore Sec-WebSocket-Accept header": "Ignoruoti {0} antraštę", + "wsSubprotocolDescription": "Įveskite kableliais atskirtą subprotokolų sąrašą. Daugiau informacijos apie subprotokolus rasite {dokumentacijoje}", + "WebSocket Application Messaging Protocol": "WAMP (WebSocket programų pranešimų perdavimo protokolas)", + "Session Initiation Protocol": "WebSocket perdavimo kanalas SIP (sesijos inicijavimo protokolui)", + "Network API for Notification Channel": "OMA RESTful tinklo API pranešimų kanalui", + "Web Process Control Protocol": "Žiniatinklio procesų valdymo protokolas (WPCP)", + "Advanced Message Queuing Protocol": "Išplėstinis pranešimų eilių protokolas (AMQP) 1.0+", + "jsflow": "jsFlow publikavimo/prenumeratos (pubsub) ir eilių protokolas", + "Reverse Web Process Control": "Atvirkštinis žiniatinklio procesų valdymo protokolas (RWPCP)", + "Extensible Messaging and Presence Protocol": "WebSocket perdavimo kanalas išplečiamajam pranešimų ir būsenos protokolui (XMPP)", + "Smart Home IP": "SHIP – išmaniųjų namų IP protokolas", + "Miele Cloud Connect Protocol": "Miele debesijos jungties protokolas", + "Push Channel Protocol": "Pranešimų siuntimo kanalo protokolas", + "Message Session Relay Protocol": "WebSocket perdavimo kanalas MSRP (pranešimų sesijos perdavimo protokolui)", + "Binary Floor Control Protocol": "WebSocket perdavimo kanalas BFCP (dvejetainiam Floor valdymo protokolui)", + "Softvelum Low Delay Protocol": "Softvelum mažos delsos protokolas", + "OPC UA Connection Protocol": "OPC UA ryšio protokolas", + "OPC UA JSON Encoding": "OPC UA JSON kodavimas", + "Swindon Web Server Protocol": "OPC UA JSON kodavimas", + "Broadband Forum User Services Platform": "OPC UA JSON kodavimas", + "Constrained Application Protocol": "Apribotų išteklių taikomųjų programų protokolas (CoAP)", + "Softvelum WebSocket signaling protocol": "Softvelum WebSocket Signalo protokolas", + "Cobra Real Time Messaging Protocol": "Cobra realaus laiko pranešimų protokolas", + "Declarative Resource Protocol": "Deklaratyviųjų išteklių protokolas", + "BACnet Secure Connect Hub Connection": "BACnet Secure Connect mazgo (hub) ryšys", + "BACnet Secure Connect Direct Connection": "BACnet Secure Connect tiesioginis ryšys", + "ITU-T T.140 Real-Time Text": "ITU-T T.140 Realaus laiko tekstas", + "Done.best IoT Protocol": "Done.best IoT protokolas", + "Collection Update": "Kolekcijos atnaujinimo WebSocket subprotokolas", + "Text IRC Protocol": "Tekstinis IRC protokolas", + "Binary IRC Protocol": "Dvejetainis IRC protokolas", + "Penguin Statistics Live Protocol v3": "Penguin Statistics Live protokolas v3 (Protobuf kodavimas)", + "Invalid userId": "Neteisingas naudotojo ID [{userId}]", + "HTTP Method": "HTTP metodas", + "deleteGroupMsg": "Ar tikrai norite ištrinti šią grupę?", + "webhookGetMethodDesc": "GET siunčia duomenis kaip užklausos parametrus ir neleidžia konfigūruoti turinio. Naudinga, kai reikia suaktyvinti „Uptime Kuma“ Push stebėjimo elementus.", + "wayToGetBaleToken": "Galite gauti prieigos raktą iš {0}.", + "supportBaleChatID": "Palaiko tiesioginio pokalbio, grupės arba kanalo pokalbio ID", + "wayToGetBaleChatID": "Galite gauti savo pokalbio ID išsiųsdami pranešimą botui ir apsilankę šiame URL, kad peržiūrėtumėte **chat_id**:", + "certHostnameMismatch": "Sertifikato pagrindinio kompiuterio (hostname) pavadinimas nesutampa su stebimos (monitor) URL nuoroda.", + "deleteChildrenMonitors": "Taip pat ištrinti tiesioginį antrinį stebėjimą ir jo antrinius įrašus, jei tokių yra | Taip pat ištrinti visus {count} tiesioginius antrinius stebėjimus ir jų antrinius įrašus, jei tokių yra", + "Invalid mobile": "Neteisingas mobiliojo įrenginio numeris [{mobile}]", + "ntfyPriorityDown": "Prioritetas „DOWN“ įvykiams", + "twilioApiKeyHelptext": "API raktas neprivalomas, bet rekomenduojamas. Galite pateikti Account SID ir AuthToken iš Twilio Console puslapio arba Account SID su API raktu ir API rakto slaptu kodu", + "twilioMessagingServiceSID": "Pranešimų paslaugos SID (neprivaloma)", + "webhookPostMethodDesc": "POST tinka daugumai šiuolaikinių HTTP serverių.", + "Template plain text instead of using cards": "Naudoti paprastą teksto šabloną vietoje kortelių", + "descriptionHelpText": "Rodoma vidinėje informacijos suvestinėje. Leidžiamas „Markdown“ formatas, kuris prieš rodymą išvalomas (išlaikomi tarpai ir įtrauka).", + "Mention Mobile List": "Paminėti mobiliųjų įrenginių sąrašą", + "Mention User List": "Paminėti naudotojų ID sąrašą", + "Dingtalk Mobile List": "Mobiliųjų įrenginių sąrašas", + "Dingtalk User List": "Naudotojų ID sąrašas", + "Clone Maintenance": "Kopijuoti profilaktikos darbus", + "ariaPauseMaintenance": "Pristabdyti šį profilaktikos darbų tvarkaraštį", + "ariaResumeMaintenance": "Tęsti šį profilaktikos darbų tvarkaraštį", + "ariaCloneMaintenance": "Sukurti šio profilaktikos darbų tvarkaraščio kopiją", + "ariaEditMaintenance": "Redaguoti šį profilaktikos darbų tvarkaraštį", + "ariaDeleteMaintenance": "Ištrinti šį profilaktikos darbų tvarkaraštį", + "smseagleMsgTts": "Text-to-speech skambutis", + "auto-select": "Automatinis pasirinkimas", + "smseagleMsgTtsAdvanced": "Išplėstinis teksto į kalbą skambutis", + "Ip Family": "IP Šeima", + "ipFamilyDescriptionAutoSelect": "Naudoja {happyEyeballs}, kad nustatytų IP šeimą.", + "Notifications Enabled": "Pranešimai įjungti", + "Allow Notifications": "Leisti pranešimus", + "Browser not supported": "Naršyklė nepalaikoma", + "Unable to get permission to notify": "Nepavyko gauti leidimo siųsti pranešimus (užklausa atmesta arba ignoruota).", + "Maximum Retries": "Didžiausias pakartojimų skaičius", + "wayToWriteEvolutionRecipient": "Telefono numeris su tarptautiniu prefiksu, bet be pliuso ženklo pradžioje ({0}), kontaktinio asmens ID ({1}) arba grupės ID ({2}).", + "smsplanetApiDocs": "Išsami informacija apie API prieigos raktų gavimą pateikta {the_smsplanet_documentation}.", + "Manual": "Rankinis", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Norint įdiegti „Nextcloud Talk“ botą, reikia administratoriaus prieigos prie serverio.", + "Webpush Helptext": "Žiniatinklio pranešimai („Web Push“) veikia tik su SSL (HTTPS) ryšiais. iOS įrenginiuose tinklalapį reikia iš anksto pridėti prie pradžios ekrano.", + "Number of retry attempts if webhook fails": "Bandymų pakartoti skaičius (kas 60–180 sekundžių), jei webhook užklausa nepavyksta.", + "pingPerRequestTimeoutLabel": "Laiko limitas vienam ping siuntimui", + "Recipient Numbers": "Gavėjų numeriai", + "Optional: The audience to request the JWT for": "Neprivaloma: auditorija, kuriai prašomas JWT", + "OAuth Audience": "OAuth Auditorija", + "pingCountDescription": "Paketų skaičius, kuriuos siųsti prieš sustojant", + "smsplanetApiToken": "Prieigos raktas SMSPlanet API paslaugai", + "Template ID": "Šablono ID", + "wayToGetClickSMSIRTemplateID": "Jūsų šablone turi būti laukas {uptkumaalert}. Naują šabloną galite sukurti {here}.", + "evolutionRecipient": "Telefono numeris / Kontaktinio asmens ID / Grupės ID", + "wayToGetEvolutionUrlAndToken": "API URL ir prieigos raktą galite gauti atsidarę norimą kanalą per {0}", + "evolutionInstanceName": "Egzemplioriaus Pavadinimas", + "brevoCcEmail": "Kopijos (CC) el. paštas", + "brevoSeparateMultipleEmails": "Kelis el. pašto adresus atskirkite kableliais", + "brevoBccEmail": "Nematomos kopijos (BCC) el. paštas", + "brevoSubject": "Tema", + "pingNumericLabel": "Skaitmeninė išvestis", + "pingNumericDescription": "Pažymėjus bus išvedami IP adresai, o ne simboliniai mazgų pavadinimai", + "pingGlobalTimeoutDescription": "Bendras laikas sekundėmis, po kurio ping sustos, neatsižvelgiant į išsiųstų paketų skaičių", + "customUrlDescription": "Bus naudojamas kaip paspaudžiamas URL, vietoj stebėjimo elemento URL.", + "Add Another Tag": "Pridėti kitą žymą", + "pause": "Pristabdyti", + "brevoApiKey": "Brevo API Raktas", + "brevoApiHelp": "Sukurkite API raktą čia: {0}", + "brevoFromEmail": "Siuntėjo el. paštas", + "pingIntervalAdjustedInfo": "Intervalas koreguojamas atsižvelgiant į paketų skaičių, bendrą laiko limitą ir vieno ping siuntimo laiko limitą", + "brevoFromName": "Siuntėjo vardas", + "brevoLeaveBlankForDefaultName": "Palikite tuščią, kad būtų naudojamas numatytasis vardas", + "Nextcloud host": "Nextcloud serveris", + "Bot secret": "Boto slaptas raktas", + "pingGlobalTimeoutLabel": "Bendras laiko limitas", + "OneChatBotId": "OneChat boto ID", + "brevoLeaveBlankForDefaultSubject": "Palikite tuščią, kad būtų naudojama numatytoji tema", + "Clear Form": "Išvalyti formą", + "FlashDuty Push URL": "Siuntimo URL", + "brevoToEmail": "Gavėjo el. paštas", + "pingPerRequestTimeoutDescription": "Didžiausias laukimo laikas (sekundėmis) prieš laikant vieną ping paketą prarastu", + "OneChatAccessToken": "OneChat Prieigos Tokenas", + "OneChatUserIdOrGroupId": "OneChat naudotojo ID arba grupės ID", + "the smsplanet documentation": "SMSPlanet dokumentacijoje", + "Phone numbers": "Telefono numeriai", + "Sender name": "Siuntėjo vardas", + "smsplanetNeedToApproveName": "Turi būti patvirtintas kliento skydelyje", + "Disable URL in Notification": "Išjungti URL pranešime", + "Happy Eyeballs algorithm": "Happy Eyeballs algoritmas", + "Staged Tags for Batch Add": "Paruoštos žymos masiniam pridėjimui", + "Conversation token": "Pokalbio prieigos raktas", + "Send UP silently": "Siųsti UP tyliai", + "Send DOWN silently": "Siųsti DOWN tyliai", + "pingCountLabel": "Maksimalus paketų skaičius", + "smtpHelpText": "„SMTPS“ tikrina, ar SMTP/TLS veikia; „Ignore TLS“ jungiasi nešifruotu ryšiu; „STARTTLS“ jungiasi, siunčia STARTTLS komandą ir patikrina serverio sertifikatą. Nė vienas iš šių režimų nesiunčia el. laiško.", + "Custom URL": "Pasirinktinis URL", + "Font Twemoji by Twitter licensed under": "Šriftas „Twemoji“, sukurtas „Twitter“, licencijuotas pagal", + "showOnlyLastHeartbeat": "Rodyti tik paskutinį heartbeat", + "FlashDuty Push URL Placeholder": "Nukopijuokite iš įspėjimų integracijos puslapio", + "resendApiHelp": "Sukurti api raktą čia {0}", + "resendApiKey": "Resend API raktas", + "resendToEmail": "Gavėjo el. paštas", + "serwersmsRecipientTypePhone": "Telefono numeris", + "resendFromName": "Siuntėjo vardas", + "resendFromEmail": "Siuntėjo Email", + "resendLeaveBlankForDefaultName": "Palikite tuščią, jei norite naudoti numatytąjį vardą", + "resendLeaveBlankForDefaultSubject": "Palikite tuščią, jei norite naudoti numatytąją temą", + "resendSubject": "Tema", + "Resolver Server(s)": "Resolver serveriai", + "BodyInvalidFormatBecause": "Užklausos kūnas nėra tinkamas JSON, nes {error}", + "steamApiKeyDescriptionAt": "Norint stebėti „Steam“ žaidimų serverį, reikalingas „Steam Web API“ raktas. Jį galite užregistruoti adresu {url}", + "checkPriceAt": "{service} kainas galite patikrinti adresu {url}", + "You can divide numbers with commas or semicolons": "Skaičius galite atskirti naudodami {comma} arba {semicolon}", + "HeadersInvalidFormatBecause": "Užklausos antraštės nėra galiojantis JSON formatas, nes {error}", + "sipsakPingWarning": "Norint naudoti SIP Options Ping stebėsenos monitorių, reikia įdiegti „Uptime Kuma“ be „Docker“ ir taip pat savo serveryje įdiegti „Sipsak“ klientą.", + "Ignore STARTTLS": "Ignoruoti STARTTLS", + "Use STARTTLS": "Naudoti STARTTLS", + "serwersmsRecipientType": "Gavėjo tipas", + "serwersmsRecipientTypeGroup": "Grupė", + "serwersmsGroupId": "Grupės ID", + "wsCodeDescription": "Daugiau informacijos apie būsenos kodus rasite {rfc6455}", + "Subprotocol(s)": "Subprotokolai", + "RSS Title": "RSS Tema", + "Leave blank to use status page title": "Palikite tuščią, jei norite naudoti būsenos puslapio pavadinimą", + "year": "metai | metai", + "notificationUniversal": "Universalus", + "notificationChatPlatforms": "Pokalbių platformos", + "notificationPushServices": "Push pranešimų paslaugos", + "notificationSmsServices": "SMS Paslaugos", + "notificationEmail": "El. Paštas", + "notificationIncidentManagement": "Incidentų valdymas", + "notificationHomeAutomation": "Namų automatizavimas", + "notificationOther": "Kitos integracijos", + "SMTP Security": "SMTP Sauga", + "hostnameCannotBeIP": "DNS vardas negali būti IP adresas. Ar turėjote omenyje rezoliavimo lauką?", + "invalidHostnameOrIP": "Neteisingas kompiuterio vardas arba IP adresas. Kompiuterio vardas turi būti galiojantis FQDN. Pakaitos simbolių (wildcard) naudoti negalima. Gali būti naudojamas pabraukimo simbolis (_) arba vardas gali baigtis tašku.", + "invalidDNSHostname": "Neteisingas kompiuterio vardas. Kompiuterio vardas turi būti galiojantis FQDN. Gali būti naudojami pakaitos simboliai (wildcard), pabraukimo simbolis (_) arba vardas gali baigtis tašku.", + "wildcardOnlyForDNS": "Pakaitos simbolius (wildcard) turintys kompiuterių vardai palaikomi tik DNS stebėsenos monitoriuose.", + "invalidURL": "Neteisingas URL", + "Duration (Minutes)": "Trukmė (Minutės)", + "Deselect All": "Atžymėti viską", + "Select All": "Pažymėti viską", + "message": "žinutė", + "json_value": "JSON reikšmė", + "serwersmsGroupIdHelptext": "ID arba grupių ID Kliento skydelyje. Šiuos identifikatorius galima atsisiųsti naudojant veiksmą groups / index arba nukopijuoti juos iš grupės redagavimo lango Kliento skydelyje.", + "Open Badge Link Generator": "Atvirojo ženklelio nuorodos generatorius", + "Badge Link Generator": "{0} ženklelio nuorodos generatorius", + "Badge Link Generator Helptext": "Ženklelių nuorodos yra prieinamos visiems monitoriams, priskirtiems viešiems būsenos puslapiams. Daugiau informacijos rasite {documentation}.", + "systemService": "Sistemos paslauga", + "systemServiceName": "Paslaugos Pavadinimas", + "systemServiceDescription": "Tikrina, ar sistemos paslauga {service_name} yra aktyvi", + "systemServiceDescriptionLinux": "Tikrina, ar Linux systemd paslauga {service_name} yra aktyvi", + "systemServiceCommandHint": "Naudota komanda: {command}", + "systemServiceExpectedOutput": "Tikėtina išvestis: \"{0}\"", + "Analytics Type": "Analitikos Tipas", + "Analytics ID": "Analitikos ID", + "Analytics Script URL": "Analitikos Skripto URL", + "ntfyCallHelptext": "Kai suveikia įspėjimas, atlikti telefono skambutį. Nustatykite 'taip', jei norite naudoti pirmąjį patvirtintą numerį, arba įveskite konkretų telefono numerį (pvz., +12223334444). Reikalinga ntfy Pro prenumerata ir patvirtintas telefono numeris.", + "systemServiceDescriptionWindows": "Tikrina, ar Windows Service Manager paslauga {service_name} veikia", + "ntfyCall": "Skambutis Telefonu", + "responseMaxLengthDescription": "Didžiausias saugomų atsako duomenų dydis. Nustatykite 0, jei norite neriboti. Didesni atsakai bus sutrumpinti. Numatytoji reikšmė: 1024 (1 KB)", + "homeAssistantNotificationActionHelptext": "Pranešimų veiksmų sąrašą rasite Home Assistant skiltyje „Settings > Developer Tools > Actions“. Ieškokite „notify“, kad rastumėte savo veiksmus. Įveskite tik dalį po „notify.“, pvz., veiksmui „notify.mobile_app_xyz“ įveskite „mobile_app_xyz“. Integruotiems mobiliesiems pranešimams ieškokite „Send a notification via mobile_app_xyz“ (ne „Send a notification“).", + "Failed to copy to clipboard": "Nepavyko nukopijuoti į iškarpinę", + "aliyun-template-optional-parameters": "Pasirenkami parametrai: {parameters}", + "teamsEnableTags": "Įtraukti žymes", + "teamsEnableTagsDescription": "Jei įjungta, pranešime bus stebėjimo žymės.", + "wayToGetMaxToken": "MAX boto prieigos raktą ir kitą informaciją galite gauti iš {0}.", + "maxMessenger": "MAX pranešimų sistema", + "maxApiUrlDescription": "Bazinis MAX pranešimų sistemos API URL. Numatytoji reikšmė: https://platform-api.max.ru", + "wayToGetMaxChatID": "Nurodykite MAX pokalbio identifikatorių, į kurį turi būti pristatomos žinutės.", + "maxUseTemplate": "Naudoti pasirinktinį žinutės šabloną", + "maxUseTemplateDescription": "Jei įjungta, žinutė bus siunčiama naudojant pasirinktinį šabloną.", + "maxTemplateFormatDescription": "MAX pranešimų sistema palaiko paprastą tekstą, Markdown ir HTML formatavimą.", + "Sets end time based on start time": "Nustato pabaigos laiką pagal pradžios laiką", + "Please set start time first": "Pirmiausia nustatykite pradžios laiką", + "Copy to Clipboard": "Kopijuoti į iškarpinę", + "Copied to clipboard!": "Nukopijuota į iškarpinę!", + "Go to Extensions → Apps Script": "Eikite į Extensions → Apps Script", + "Google Apps Script Code": "Google Apps Script kodas", + "aliyun-template-requirements-and-parameters": "Aliyun SMS šablone turi būti parametrai: {parameters}", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Dėl operatorių apribojimų pasirenkamus kintamuosius įjunkite tik suprasdami, kad žinutė gali būti nepristatyta", + "enableSSL": "Įjungti SSL/TLS", + "mariadbUseSSLHelptext": "Įjunkite, jei norite naudoti šifruotą ryšį su savo duomenų baze. Reikalinga daugumai debesijos duomenų bazių.", + "mariadbCaCertificateLabel": "CA sertifikatas", + "mariadbCaCertificateHelptext": "Įklijuokite CA sertifikatą PEM formatu, jei naudojate savarankiškai pasirašytus sertifikatus. Palikite tuščią, jei jūsų duomenų bazė naudoja viešos CA pasirašytą sertifikatą.", + "No incidents recorded": "Incidentų neužfiksuota", + "Load More": "Įkelti daugiau", + "Loading...": "Kraunama...", + "Monitors": "{n} stebėjimas | {n} stebėjimai", + "days": "{n} diena | {n} dienos", + "unknownDays": "Nežinomas dienų skaičius", + "hours": "{n} valanda | {n} valandos", + "minutes": "{n} minutė | {n} minutės", + "minuteShort": "{n} min. | {n} min.", + "saveResponseForNotifications": "Išsaugoti sėkmingą HTTP atsaką pranešimams", + "saveErrorResponseForNotifications": "Išsaugoti HTTP klaidos atsaką pranešimams", + "saveResponseDescription": "Išsaugo HTTP atsaką ir padaro jį prieinamą pranešimų šablonams kaip {templateVariable}", + "responseMaxLength": "Maksimalus atsako ilgis (baitais)", + "logoutCurrentUser": "Atsijungti {username}", + "Incident not found or access denied": "Incidentas nerastas arba prieiga uždrausta", + "Past Incidents": "Ankstesni incidentai", + "Incident title": "Incidento pavadinimas", + "Incident description": "Incidento aprašymas", + "Pinned incidents are shown prominently on the status page": "Prisegti incidentai aiškiai parodomi būsenos puslapyje", + "templateAvailableVariables": "Galimi kintamieji", + "example": "Pavyzdys", + "Edit Incident": "Redaguoti incidentą", + "Result": "Rezultatas", + "Resolve": "Pažymėti kaip išspręstą", + "Resolved": "Išspręsta", + "lastUpdatedAt": "Paskutinį kartą atnaujinta: {date}", + "lastUpdatedAtFromNow": "Paskutinį kartą atnaujinta: {date} ({fromNow})", + "Actions": "Veiksmai", + "selectedMonitorCountMsg": "pasirinkta: {n} | pasirinkta: {n}", + "selectMonitorMsg": "Pasirinkite stebėjimus veiksmams atlikti", + "selectAllMonitorsAria": "Pasirinkti visus stebėjimus", + "deselectAllMonitorsAria": "Atžymėti visus stebėjimus", + "deleteIncidentMsg": "Ar tikrai norite ištrinti šį incidentą?", + "Certificate Chain:": "Sertifikatų grandinė:", + "Please input content": "Įveskite turinį", + "slug is not found": "Nuorodos dalis nerasta", + "dateCreatedAtFromNow": "Sukūrimo data: {date} ({fromNow})", + "Please input title": "Įveskite pavadinimą", + "Cloud ID": "Debesijos ID", + "API Token": "API prieigos raktas", + "cronScheduleDescription": "Tvarkaraštis: {description}", + "disableSTARTTLSDescription": "Įjunkite šią parinktį SMTP serveriams, kurie nepalaiko STARTTLS. El. laiškai bus siunčiami nešifruotu ryšiu.", + "Disable STARTTLS": "Išjungti STARTTLS", + "Google Apps Script Webhook URL": "Google Apps Script Webhook URL adresas", + "Deploy a Google Apps Script as a web app and paste the URL here": "Paskelbkite Google Apps Script kaip žiniatinklio programą ir įklijuokite URL čia", + "OptionalParameters": "Pasirenkami parametrai", + "See Jira Cloud Docs": "Žr. Jira Cloud dokumentaciją", + "certificateExpiryNotificationHelp": "Dienų skaičių iki galiojimo pabaigos galima nustatyti parametruose.", + "API Version": "API versija", + "deleteMonitorsMsg": "Ar tikrai norite ištrinti pasirinktus stebėjimus?", + "pausedMonitorsMsg": "Pristabdytas {n} stebėjimas | Pristabdyti {n} stebėjimai", + "noMonitorsSelectedWarning": "Kuriate profilaktikos darbų įrašą nepasirinkę jokių paveiktų stebėjimų. Ar tikrai norite tęsti?", + "noMonitorsOrStatusPagesSelectedError": "Negalima sukurti profilaktikos darbų įrašo be paveiktų stebėjimų arba būsenos puslapių", + "Peer ID": "Peer ID", + "vkApiVersionDescription": "VK API versija, naudojama užklausoms. Palikite numatytąją, nebent dėl suderinamumo jums reikia kitos VK API versijos.", + "vkDontParseLinks": "Išjungti nuorodų peržiūras", + "vkDontParseLinksDescription": "Jei įjungta, VK negeneruos nuorodų peržiūrų / fragmentų.", + "vkPeerIdDescription": "Įveskite tikslinį VK peer_id. Ši reikšmė bus perduota API tokia, kokia yra.", + "Quick Setup Guide": "Greito nustatymo gidas", + "Open your Google Spreadsheet": "Atidarykite savo Google skaičiuoklę", + "versionIs": "Versija: {version}", + "createdAt": "Sukurta: {date}", + "Examples:": "Pavyzdžiai: {0}", + "Notification Action": "Pranešimo veiksmas", + "matrixUseTemplate": "Naudoti pasirinktinį žinutės šabloną", + "matrixUseTemplateDescription": "Jei įjungta, žinutė bus siunčiama naudojant pasirinktinį šabloną.", + "resumedMonitorsMsg": "Atnaujintas {n} stebėjimas | Atnaujinti {n} stebėjimai", + "noMonitorsPausedMsg": "Nė vienas stebėjimas nepristabdytas (nebuvo aktyvių)", + "noMonitorsResumedMsg": "Nė vienas stebėjimas neatnaujintas (nebuvo neaktyvių)", + "bulkDeleteErrorMsg": "Nepavyko ištrinti {n} stebėjimo | Nepavyko ištrinti {n} stebėjimų", + "deletedMonitorsMsg": "Ištrintas {n} stebėjimas | Ištrinti {n} stebėjimai", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Nustatykite „Execute as: Me“ ir „Who has access: Anyone“", + "Paste the script code (see below)": "Įklijuokite scenarijaus kodą (žr. žemiau)", + "Click Deploy → New deployment → Web app": "Spustelėkite Deploy → New deployment → Web app", + "Copy the web app URL and paste it above": "Nukopijuokite žiniatinklio programos URL ir įklijuokite jį aukščiau", + "monitorTypeGameServer": "Žaidimų serveris", + "monitorTypeDatabase": "Duomenų bazės stebėjimo tipas", + "monitorTypeSpecial": "Specialus", + "frontendVersionIs": "Sąsajos versija: {version}", + "oracledbConnectionString": "Oracle duomenų bazė: {connectionString}", + "years": "{n} metai | {n} metai", + "Pin this incident": "Prisegti šį incidentą", + "Only retry if status code check fails": "Kartoti tik jei būsenos kodo patikra nepavyksta", + "retryOnlyOnStatusCodeFailureDescription": "Jei įjungta, pakartotiniai bandymai bus vykdomi tik tada, kai HTTP būsenos kodo patikra nepavyksta (pvz., serveris neveikia). Jei būsenos kodo patikra sėkminga, bet JSON užklausa nepavyksta, monitorius bus pažymėtas kaip neveikiantis iš karto, be pakartotinių bandymų.", + "slackUseTemplate": "Naudoti pasirinktinį žinutės šabloną", + "Basic checkbox toggle button group": "Paprasta žymimųjų langelių perjungimo mygtukų grupė", + "Sort by status": "Rikiuoti pagal būseną", + "Sort by uptime": "Rikiuoti pagal veikimo laiką", + "Message Format": "Žinutės formatas", + "Region": "Regionas", + "PushDeer Server URL": "PushDeer serverio URL", + "To Number": "Gavėjo numeris", + "GrafanaOncallURL": "Grafana Oncall URL adresas", + "Never": "Niekada", + "Endpoint": "Galinis taškas", + "Details": "Išsami informacija", + "Teltonika SMS Gateway": "Teltonika SMS šliuzas", + "username": "Vartotojo vardas", + "password": "Slaptažodis", + "Expected TLS Alert": "Tikėtinas TLS įspėjimas", + "360messengerAuthToken": "360messenger API raktas", + "360messengerRecipient": "Gavėjo telefono numeris(-iai)", + "360messengerGroupId": "360messenger grupės ID", + "360messengerTemplate": "360messenger žinutės šablonas", + "360messengerGroupList": "WhatsApp grupės", + "360messengerSelectGroupList": "Pasirinkite grupę pridėti", + "360messengerEnableCustomMessage": "Įjungti pasirinktinį žinutės šabloną vietoje numatytosios žinutės.", + "slackIncludeGroupName": "Įtraukti stebėjimų grupės pavadinimą", + "360messengerWayToGetUrlAndToken": "Savo 360messenger API raktą galite gauti iš {0}.", + "360messengerErrorNoGroups": "Šiai paskyrai WhatsApp grupių nerasta.", + "GlobalpingMonitorDescription": "Globalping suteikia prieigą prie tūkstančių bendruomenės talpinamų zondų tinklo testams ir matavimams vykdyti. Anoniminiams naudotojams taikomas 250 testų per valandą limitas. Norėdami padidinti limitą iki 500 per valandą, išsaugokite savo prieigos raktą {accountSettings}. Daugiau informacijos rasite {docs}.", + "Protocol": "Protokolas", + "minimumIntervalWarning": "Intervalai mažesni nei 20 sekundžių gali pabloginti našumą.", + "Basic radio toggle button group": "Paprasta radijo perjungimo mygtukų grupė", + "Google": "Google", + "GlobalpingLocationDescription": "Vietos laukas priima žemynus, šalis, regionus, miestus, ASN, IPT arba debesijos regionus. Filtrus galite derinti naudodami {plus} (pvz. {amazonPlusGermany} arba {comcastPlusCalifornia}). Jei delsa yra svarbi metrika, naudokite filtrus, kad susiaurintumėte vietą iki mažo regiono ir išvengtumėte šuolių; geresniam stabilumui nustatykite {datacenter} filtrą. {fullDocs}.", + "GlobalpingMultipleLocationsError": "Kelios vietos nepalaikomos, naudokite vieną vietą kiekvienam monitoriui.", + "Collapse All Groups": "Sutraukti visas grupes", + "360messengerUseTemplate": "Naudoti pasirinktinį žinutės šabloną", + "360messengerSelectedGroupID": "Pasirinktos grupės ID", + "360messengerErrorGeneric": "Nepavyko įkelti WhatsApp grupių sąrašo: {message}", + "Umami": "Umami", + "account settings": "paskyros nustatymuose", + "Location": "Vieta", + "Monitor Subtype": "Monitoriaus potipis", + "WeCom Mentioned Mobile List Description": "Įveskite paminėtinus telefono numerius. Keliems numeriams atskirti naudokite kablelius. Naudokite {'@'}all, jei norite paminėti visus.", + "aboutJiraCloudId": "Daugiau informacijos apie Jira Cloud ID: {0}", + "see Jira Cloud Docs": "žr. Jira Cloud dokumentaciją", + "octopushEndpoint": "octopush (galinis taškas: {url})", + "legacyOctopushEndpoint": "Senasis Octopush-DM (galinis taškas: {url})", + "Screenshot Delay": "Ekrano nuotraukos delsimas (laukia {milliseconds})", + "milliseconds": "{n} milisekundė | {n} milisekundės", + "screenshotDelayDescription": "Pasirinktinai palaukite tiek milisekundžių prieš darant ekrano nuotrauką. Maksimumas: {maxValueMs} ms (0.5 × intervalas).", + "screenshotDelayWarning": "Didesnės reikšmės ilgiau laiko naršyklę atidarytą, todėl naudojant daug vienu metu veikiančių monitorių gali padidėti atminties sąnaudos.", + "snmpV3Username": "SNMPv3 vartotojo vardas", + "Enter the list of nodes": "Įveskite RabbitMQ valdymo mazgų sąrašą", + "Press Enter to add node": "Paspauskite Enter, kad pridėtumėte mazgą", + "Suppress Notifications": "Slopinti pranešimus", + "discordSuppressNotificationsHelptext": "Jei įjungta, žinutės bus skelbiamos kanale, bet nesukels gavėjų push ar darbalaukio pranešimų.", + "Globalping - Access global monitoring probes": "Globalping - prieiga prie pasaulinių stebėjimo zondų", + "Webhook Payload Fields": "Webhook duomenų laukai", + "Plausible": "Plausible", + "discordMessageTemplate": "Žinutės šablonas", + "fluxerMessageFormat": "Pranešimo formatas", + "fluxerMessageFormatNormal": "Įprastas (turtingi įterpiniai)", + "fluxerMessageTemplate": "Žinutės šablonas", + "WeCom Mentioned Mobile List": "WeCom paminėtų mobiliųjų numerių sąrašas", + "slackIncludeGroupNameDescription": "Jei įjungta, stebėjimų grupės kelias bus įtrauktas į pranešimus, kad būtų lengviau atskirti vienodai pavadintus stebėjimus skirtingose grupėse.", + "slackUseTemplateDescription": "Jei įjungta, žinutė bus siunčiama naudojant pasirinktinį šabloną. Galite naudoti Liquid šablonus ir įtraukti grupės informaciją per monitorJSON.path arba monitorJSON.pathName.", + "ntfyUseTemplate": "Pritaikyti pranešimų šablonus", + "ntfyUseTemplateDescription": "Įjunkite tai, jei norite pritaikyti pranešimų pavadinimus ir žinutes naudodami LiquidJS šablonus", + "ntfyCustomTitle": "Pasirinktinis pavadinimo šablonas", + "ntfyCustomMessage": "Pasirinktinis žinutės šablonas", + "ntfyNotificationTemplateFallback": "Palikite tuščią, jei norite naudoti numatytąjį Uptime Kuma formatą", + "360messengerEnableSendToGroup": "Įjungti siuntimą į WhatsApp grupę(-es)", + "360messengerCustomMessageTemplate": "Pasirinktinis žinutės šablonas", + "360messengerMessageTemplate": "Žinutės šablonas", + "360messengerWayToWriteRecipient": "Įveskite vieną ar daugiau telefono numerių tarptautiniu formatu be pradžioje esančio pliuso ženklo (pvz. {0}). Keliems numeriams atskirti naudokite kablelius.", + "360messengerErrorNoApiKey": "Pirmiausia įveskite savo 360messenger API raktą.", + "360messengerErrorApi": "Nepavyko įkelti WhatsApp grupių sąrašo (klaida {statusCode}: {message}).", + "discordMessageFormatNormal": "Įprastas (turtingi įterpiniai)", + "discordMessageFormatMinimalist": "Minimalistinis (trumpa būsena)", + "fluxerMessageFormatMinimalist": "Minimalistinis (trumpa būsena)", + "fluxerMessageFormatCustom": "Pasirinktinis šablonas", + "fluxerUseMessageTemplate": "Naudoti pasirinktinį žinutės šabloną", + "fluxerUseMessageTemplateDescription": "Jei įjungta, žinutė bus siunčiama naudojant pasirinktinį šabloną (LiquidJS). Palikite tuščią, jei norite naudoti numatytąjį Uptime Kuma formatą.", + "Fluxer Webhook URL": "Fluxer Webhook URL adresas", + "wayToGetFluxerURL": "Jį galite gauti nuėję į tikslinio kanalo nustatymus > Webhooks > Create Webhook > Copy Webhook URL.", + "RegexMatch": "Įveskite reguliariąją išraišką įrašo reikšmei tikrinti", + "lowIntervalWarning": "Ar tikrai norite nustatyti mažesnį nei 20 sekundžių intervalą? Našumas gali suprastėti, ypač jei monitorių yra daug.", + "Sort options": "Rikiavimo parinktys", + "Severity": "Svarbumas", + "Check Type": "Patikros tipas", + "Metadata": "Metaduomenys", + "End": "Pabaiga", + "TLS Alerts": "TLS įspėjimai", + "Expand All Groups": "Išskleisti visas grupes", + "SSL/TLS": "SSL/TLS", + "Globalping API Token": "Globalping API prieigos raktas", + "globalpingApiTokenDescription": "Savo Globalping API prieigos raktą gaukite adresu {0}.", + "GlobalpingHostname": "Viešai pasiekiamas matavimo tikslas. Paprastai tai yra kompiuterio vardas arba IPv4/IPv6 adresas, priklausomai nuo matavimo tipo.", + "GlobalpingLocationDocs": "Visa vietos įvesties dokumentacija", + "signalUseTemplate": "Naudoti pasirinktinį žinutės šabloną", + "signalUseTemplateDescription": "Jei įjungta, žinutė bus siunčiama naudojant pasirinktinį šabloną. Galite naudoti Liquid šablonus pranešimo formatui pritaikyti.", + "Matomo": "Matomo", + "discordMessageFormat": "Pranešimo formatas", + "discordMessageFormatCustom": "Pasirinktinis šablonas", + "discordUseMessageTemplate": "Naudoti pasirinktinį žinutės šabloną", + "discordUseMessageTemplateDescription": "Jei įjungta, žinutė bus siunčiama naudojant pasirinktinį šabloną (LiquidJS). Palikite tuščią, jei norite naudoti numatytąjį Uptime Kuma formatą.", + "GlobalpingIpFamilyInfo": "Naudotina IP versija. Leidžiama tik jei tikslas yra kompiuterio vardas.", + "GlobalpingResolverInfo": "IPv4/IPv6 adresas arba pilnai kvalifikuotas domeno vardas (FQDN). Numatytoji reikšmė - zondo vietinio tinklo resolveris. Resolverio serverį galite pakeisti bet kada.", + "RecordMatch": "Įrašo reikšmės atitikimas", + "Check for": "Tikrinti", + "halopsa_webhook_url_desc": "Įveskite Webhook URL iš savo Halo PSA Integration Runbook (Configuration > Integrations > Custom Integrations > Integration Runbooks). Kurdami webhook pasirinkite „Can only be started from Halo and from a public endpoint“.", + "imageResetConfirmation": "Paveikslėlis atkurtas į numatytąjį", + "avgPing": "Vid. ping", + "maxPing": "Maks. ping", + "minPing": "Min. ping", + "Setup Instructions": "Nustatymo instrukcijos", + "Sort by name": "Rikiuoti pagal pavadinimą", + "Sort by certificate expiry": "Rikiuoti pagal sertifikato galiojimo pabaigą", + "Splunk Rest URL": "Splunk REST URL", + "Jira Service Management": "Jira paslaugų valdymas", + "Uptime Kuma": "Uptime Kuma", + "Service Name": "Paslaugos pavadinimas", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "Halo PSA Webhook URL adresas", + "halopsa_username_desc": "Vartotojo vardas autentifikacijai su Halo PSA webhook", + "halopsa_password_desc": "Slaptažodis autentifikacijai su Halo PSA webhook", + "screenshot of the website": "Svetainės ekrano nuotrauka", + "Clear current filters": "Išvalyti dabartinius filtrus", + "None (Successful Connection)": "Nėra (sėkmingas prisijungimas)", + "System Service": "Sistemos paslauga", + "domain_expiry_unsupported_is_icann": "Domenas „{domain}“ netinka domeno galiojimo stebėjimui, nes jo viešasis sufiksas \".{publicSuffix}\" nėra valdomas ICANN", + "passwordTooWeak": "Slaptažodis per silpnas. Jame turi būti raidžių ir skaičių. Jis turi būti bent 6 simbolių ilgio.", + "expectedTlsAlertDescription": "Pasirinkite TLS įspėjimą, kurį tikitės, kad serveris grąžins. Naudokite {code}, kad patikrintumėte, ar mTLS galiniai taškai atmeta ryšius be kliento sertifikatų. Daugiau informacijos rasite {link}.", + "halopsa_field_status": "Monitoriaus būsena: UP, DOWN, NOTIFICATION arba UNKNOWN", + "halopsa_field_monitor_id": "Unikalus stebėjimo identifikatorius (testiniams pranešimams null) - naudokite jį įspėjimams susieti su bilietais", + "halopsa_field_message": "Pilna įspėjimo žinutė su būsena ir informacija", + "teltonikaUrl": "Jūsų Teltonika įrenginio URL", + "teltonikaModem": "Modemo ID", + "teltonikaModemHelptext": "SMS modemo ID turi būti formato {0}. Daugiau informacijos rasite https://developers.teltonika-networks.com/reference/.", + "teltonikaPhoneNumber": "Telefono numeris", + "teltonikaVersionWarning": "Šiam pranešimų teikėjui reikia, kad jūsų Teltonika įrenginyje veiktų RMS 7.14.0 arba naujesnė versija.", + "Show this Maintenance Message on which Status Pages": "Kuriuose būsenos puslapiuose rodyti šį profilaktikos darbų pranešimą", + "labelDomainExpiry": "Domeno galioj. pab.", + "halopsa_field_timestamp": "Įvykio laiko žyma ISO 8601 formatu", + "halopsa_field_uptime_kuma_version": "Uptime Kuma versijos numeris", + "mtls-auth-server-cert-placeholder": "Sertifikato turinys", + "halopsa_field_monitor": "Stebėjimo pavadinimas", + "domainExpiryNotificationHelp": "Dienų skaičių iki galiojimo pabaigos galima nustatyti parametruose.", + "domainExpiryDescription": "Siųsti pranešimą, kai domeno vardo galiojimas baigsis po:", + "domain_expiry_unsupported_monitor_type": "Domeno galiojimo pabaigos stebėjimas šiam stebėjimo tipui nepalaikomas", + "domain_expiry_unsupported_missing_target": "Šiam monitoriui nenustatytas galiojantis domenas arba kompiuterio vardas", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Domeno galiojimo stebėjimas „.{publicSuffix}“ zonai negalimas, nes IANA nenurodė RDAP paslaugos", + "teltonikaUrlHelptext": "URL turi būti nurodytas kaip pilna kilmė, pvz. {0} arba {1}.", + "teltonikaUnsafeTls": "Ignoruoti sertifikato tikrinimą", + "teltonikaPhoneNumberHelptext": "Numeris turi būti tarptautiniu formatu {0}, {1}. Leidžiamas tik vienas numeris.", + "settingsDomainExpiry": "Domeno galiojimo pabaiga", + "TLS Alert Spec": "TLS įspėjimų specifikacija", + "labelDomainNameExpiryNotification": "Pranešimas apie domeno vardo galiojimo pabaigą", + "halopsa_setup_step4": "Pasirinkite basic Authentication ir sukurkite vartotojo vardą bei slaptažodį. Tada įveskite arba įklijuokite juos į aukščiau esančius testinius laukus", + "playground": "bandymų aplinka", + "halopsa_id_usage_hint": "Patarimas: naudokite monitor_id patikimam įspėjimų susiejimui su bilietais, o heartbeat_id - įvykių istorijai sekti", + "halopsa_setup_step5": "Sukonfigūruokite runbook taip, kad esamiems bilietams atitikti būtų naudojamas monitor_id", + "teltonikaUnsafeTlsDescription": "Išjungus TLS sertifikato tikrinimą, atsiveria galimybė tarpininko (man-in-the-middle) atakoms, kurios gali sukelti duomenų nutekėjimą ar sistemų perėmimą. Neišjunkite sertifikato tikrinimo, nebent sąmoningai priimate šią riziką. Rekomenduojame naudoti Let's Encrypt su automatiniu atnaujinimu.", + "teltonikaUsername": "API vartotojo vardas", + "mtls-auth-server-ca-label": "CA", + "smscTranslit": "smscTranslit", + "mtls-auth-server-cert-label": "Sertifikatas", + "halopsa_setup_step1": "Sukurkite Integration Runbook HaloPSA sistemoje (Configuration → Integrations → Integration Runbooks)", + "halopsa_setup_step2": "Sukonfigūruokite runbook veiksmus įspėjimams apdoroti (pvz., Create Ticket)", + "halopsa_setup_step3": "Nukopijuokite Webhook URL ir įklijuokite jį į aukščiau esantį teksto lauką", + "mtls-auth-server-key-placeholder": "Rakto turinys", + "mtls-auth-server-ca-placeholder": "Serverio CA", + "teltonikaUsernameHelptext": "Rekomendacija: sukurkite atskirą paskyrą, apribotą tik SMS žinučių siuntimui, ir čia įveskite jos vartotojo vardą", + "teltonikaPassword": "API slaptažodis", + "teltonikaPasswordHelptext": "API naudotojo slaptažodį galite nustatyti savo Teltonika maršrutizatoriuje, pvz. {0}", + "GRPC Options": "GRPC parinktys", + "mariadbSocketPathDetectedHelptext": "Jungiamasi prie duomenų bazės taip, kaip nurodyta per {0} aplinkos kintamąjį.", + "halopsa_payload_desc": "Į jūsų Halo PSA webhook siunčiami šie laukai:", + "halopsa_field_title": "Įspėjimo pavadinimas (visada „Uptime Kuma Alert“)", + "mtls-auth-server-key-label": "Raktas", + "Connection Timeout": "Prisijungimo laukimo laikas", + "Bearer Token": "Bearer prieigos raktas", + "pingPacketSizeWarning": "Mažesnis nei 16 baitų paketo dydis Linux sistemoje nepateikia delsos matavimo.", + "smtpAdditionalHeadersTitle": "Papildomos antraštės", + "smtpAdditionalHeadersDesc": "Naudojimo instrukcijas rasite Nodemailer tinkintų antraščių {0} dokumentacijoje.", + "Access Key": "Prieigos raktas", + "ooredooUsernameDescription": "El. pašto adresas, kurį naudojate prisijungdami prie Ooredoo masinių SMS žinučių valdymo skydelio.", + "ooredooAccessKeyDescription": "Jį rasite savo Ooredoo SMS valdymo skydelyje, skiltyje „API prieigos raktas“. Įklijuokite tiksliai taip, kaip parodyta – prieš siunčiant jis automatiškai užkoduojamas naudojant Base64.", + "ooredooBearerTokenDescription": "Žetonas, rodomas po „Authorization: Bearer“ pavyzdinėje cURL komandoje jūsų Ooredoo SMS valdymo skydelyje.", + "ooredooRecipientDescription": "Įveskite vieną ar daugiau numerių, atskirdami juos kableliu arba tarpu. Vietiniams 7 skaitmenų numeriams automatiškai pridedamas prefiksas 960.", + "Leave blank to use the default Ooredoo endpoint.": "Palikite tuščią, kad būtų naudojamas numatytasis Ooredoo galinis taškas.", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) siunčia pranešimus per atskirą WxPusher programėlę 6 platformose – Android, iOS, HarmonyOS, Windows, macOS ir Linux ({download}). Nuskaitykite toliau esantį QR kodą, kad gautumėte savo SPT, ir galėsite siųsti pranešimus. Kelis SPT atskirkite kableliu (,). Daugiau informacijos rasite {docs}.", + "wxPusherDownloadApp": "atsisiųsti programėlę", + "wxPusherDocs": "oficialus gidas", + "wayToGetEgoSMSToken": "Savo EgoSMS API naudotojo vardą ir slaptažodį galite gauti {čia}.", + "egosmsSenderDescription": "Gavėjams rodomas siuntėjo ID (ne daugiau kaip 11 simbolių). Jei laukelis tuščias, naudojamas EGOSMS.", + "egosmsPhoneNumberDescription": "Gavėjo numeris su šalies kodu, be + arba 00 (pvz., 2567XXXXXXXX).", + "Hash Key": "Maišos raktas", + "wayToGetBearSMSToken": "Savo BearSMS API naudotojo vardą ir maišos raktą galite gauti {0}.", + "bearsmsPhoneNumberDescription": "Gavėjo numeris su šalies kodu, be + arba 00 (pvz., 9725XXXXXXXX).", + "bearsmsSenderDescription": "Pasirenkamas siuntėjo ID, rodomas gavėjams (ne daugiau kaip 11 simbolių). Jis turi būti patvirtintas jūsų BearSMS paskyroje, kitu atveju naudojamas numatytasis paskyros siuntėjas.", + "WebSocket Options": "WebSocket Nustatymai", + "Device ID or Group ID": "Įrenginio ID arba Grupės ID", + "notifyAppDeviceIdDescription": "Įrenginio ID iš Notify! programėlės arba Grupės ID, kad vienu metu būtų siunčiamas įspėjimas į visus grupės įrenginius.", + "notifyAppSetup": "Atidarykite Notify! programėlę ir nustatymuose raskite savo Įrenginio ID bei žetoną. Paskyra nereikalinga. Daugiau informacijos rasite čia: {0}.", + "telnyxApiKey": "API Raktas (V2)", + "telnyxApiKeyHelptext": "Jūsų Telnyx V2 API raktas, kurį rasite Telnyx portale, skiltyje API raktai", + "telnyxPhoneNumber": "Siuntėjo numeris", + "telnyxPhoneNumberHelptext": "Jūsų Telnyx telefono numeris, iš kurio siunčiamos žinutės (turi būti užregistruotas jūsų Telnyx paskyroje, pvz., +15551234567)", + "telnyxToNumber": "Gavėjo numeris", + "telnyxToNumberHelptext": "Gavėjo telefono numeris, kuriuo bus siunčiami SMS pranešimai (pvz., +15559876543)", + "telnyxMessagingProfileId": "Pranešimų profilio ID (neprivaloma)", + "telnyxMessagingProfileIdHelptext": "Pasirinktinai susiekite Telnyx pranešimų profilį, kad galėtumėte valdyti siuntėjo nustatymus ir funkcijas", + "plivoAuthID": "Autentifikavimo ID", + "plivoAuthIDHelptext": "Jūsų Plivo autentifikavimo ID iš Plivo valdymo skydelio", + "plivoAuthToken": "Autentifikavimo prieigos raktas", + "plivoAuthTokenHelptext": "Jūsų Plivo autentifikavimo prieigos raktas iš Plivo valdymo skydelio", + "plivoFromNumber": "Siuntėjo numeris", + "plivoFromNumberHelptext": "Plivo telefono numeris, iš kurio bus siunčiami pranešimai (pvz., +15551234567). SMS žinutėms taip pat gali būti naudojamas patvirtintas raidinis ir skaitinis siuntėjo ID; balso skambučiams reikalingas Plivo telefono numeris.", + "plivoToNumber": "Gavėjo numeris", + "plivoToNumberHelptext": "Gavėjo telefono numeris, kuriuo bus gaunamos SMS žinutės arba balso skambučiai (pvz., +15559876543)", + "plivoMessageType": "Žinutės tipas", + "plivoVoiceCall": "Balso skambutis", + "plivoAnswerUrl": "Atsakymo URL", + "plivoAnswerUrlHelptext": "URL, kuriuo grąžinamas Plivo XML skambučiui. Įspėjimo tekstas pridedamas kaip ?message= užklausos parametras, todėl jūsų galinis taškas gali grąžinti, pvz., {message}.", + "Flowtriq Webhook URL": "Webhook URL", + "Flowtriq API Key": "API Raktas", + "flowtriqApiKeyDescription": "Pasirenkamas API raktas, skirtas jūsų Flowtriq egzemplioriaus webhook užklausoms autentifikuoti.", + "Pinglet Topic URL": "Temos URL", + "pingletTopicUrlDescription": "Pinglet temos publikavimo URL, į kurį siunčiami įspėjimai, pvz., https://app.pinglet.co.uk/your-namespace/alerts. Jei temos dar nėra, ji sukuriama pirmo publikavimo metu.", + "gamedigToken": "Prieigos raktas", + "gamedigTokenDescription": "Neprivaloma. Kai kuriems žaidimų serveriams reikalingas autentifikavimo prieigos raktas.", + "ClickUp API Token": "ClickUp API prieigos raktas", + "ClickUp API Token Description": "Asmeninį prieigos raktą ClickUp galite sukurti nuėję į Nustatymai → Integracijos ir ClickApps → ClickUp API → API prieigos raktas", + "ClickUp Workspace ID": "ClickUp darbo srities ID", + "ClickUp Workspace ID Description": "Jį rasite savo darbo srities URL, iškart po app.clickup.com/, pvz., app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ClickUp kanalo ID", + "ClickUp Channel ID Description": "Jį rasite pokalbio kanalo URL, iškart po /chat/r/, pvz., .../chat/r/abc123-4567.", + "pm2Description": "Patikrina, ar PM2 procesas {service_name} veikia", + "pm2ExpectedStates": "Tikėtina būsena: veikia. Sustabdytos ir klaidos būsenos laikomos NEVEIKIANČIOMIS.", + "evolutionCustomMessageTitle": "Pasirinktinis pranešimas (neprivaloma)", + "evolutionCustomMessageDesc": "Palikite tuščią, kad būtų naudojamas numatytasis pranešimas.", + "turbosmtpConsumerKey": "Vartotojo raktas", + "turbosmtpConsumerSecret": "Vartotojo rakto paslaptis", + "openwaSession": "Sesija", + "openwaChatId": "Pokalbio ID (telefono numeris / adresato ID / grupės ID)", + "wayToGetOpenwaApiUrl": "Jūsų WAHA instancijos URL.", + "wayToGetOpenwaApiKey": "API raktas, naudojamas jūsų OpenWA egzemplioriui.", + "wayToGetOpenwaSession": "OpenWA naudojamas seanso ID pranešimams siųsti.", + "wayToWriteOpenwaChatId": "Telefono numeris su tarptautiniu kodu, bet be pliuso ženklo pradžioje ({0}), kontakto ID ({1}) arba grupės ID ({2}). Galite siųsti keliems pokalbių ID, atskirdami juos kableliais.", + "openwaCustomMessageTitle": "Pasirinktinis pranešimas (neprivaloma)", + "openwaCustomMessageDesc": "Palikite tuščią, kad būtų naudojamas numatytasis pranešimas.", + "PM2 Process": "PM2 Procesas", + "Refresh": "Atnaujinti", + "Select PM2 process": "Pasirinkti PM2 procesą", + "ntpThresholdsTitle": "NTP Ribos", + "ntpStratumThreshold": "Stratum Riba", + "ntpStratumThresholdHelp": "Pažymėti kaip neveikiantį, jei stratum yra lygus arba didesnis už šią reikšmę. Stratum nurodo laiko šaltinio kokybę (1 = GPS / atominis laikrodis, 16 = nesinchronizuotas).", + "ntpOffsetThreshold": "Laiko Poslinkio Riba (ms)", + "ntpOffsetThresholdHelp": "Pažymėti kaip neveikiantį, jei laiko poslinkis viršija šią reikšmę milisekundėmis.", + "ntpDispersionThreshold": "Pagrindinės dispersijos riba (ms)", + "ntpDispersionThresholdHelp": "Pažymėti kaip neveikiantį, jei pagrindinė dispersija viršija šią reikšmę. Dispersija nurodo serverio laikrodžio tikslumą.", + "VKTeams Bot Token Description": "Kaip gauti roboto prieigos raktą ir kitą informaciją, rasite čia: {0}.", + "VKTeams Base Url Description": "VKTeams pagrindinės API URL. Gali skirtis On-Premise sprendime. Numatytoji: https://myteam.mail.ru", + "VKTeams Chat Id Description": "Naudotojams – jų el. pašto adresas. Grupėms ir kanalams – nustatymuose nurodytas ID, prie kurio pridėkite {'@'}chat.agent", + "VKTeams Use Template": "Naudoti pasirinktinį žinutės šabloną", + "VKTeams Use Template Description": "Jei įjungta, pranešimas bus siunčiamas naudojant pasirinktinį šabloną.", + "VKTeams Template Format Description": "Pranešimų formatavimui VKTeams palaiko paprastą tekstą, MarkdownV2 ir HTML formatavimą.", + "milkyHttpAddress": "Milky HTTP adresas (be '/api' galūnės)", + "milkyMessageType": "Milky Pranešimo Tipas", + "milkyGroupMessage": "Grupė", + "milkyPrivateMessage": "Privati", + "milkyUserOrGroupId": "Grupės/Vartotojo ID", + "milkySafetyTips": "Saugumo tikslais būtina nustatyti prieigos raktą", + "smsgatewayUrlHelptext": "Jūsų SMS šliuzo bazinis URL be API kelio.", + "smsgatewayTo": "Telefono numeris (-iai)", + "smsgatewayToHelptext": "Kableliais atskirtas gavėjų telefono numerių sąrašas tarptautiniu formatu (pvz., +15551234567)" +} diff --git a/src/lang/lv.json b/src/lang/lv.json new file mode 100644 index 0000000..6724e5e --- /dev/null +++ b/src/lang/lv.json @@ -0,0 +1,101 @@ +{ + "languageName": "Latviešu", + "setupDatabaseChooseDatabase": "Kuru datubāzi izmantosiet?", + "setupDatabaseEmbeddedMariaDB": "Jums nav nekas jādara. Docker imidžā ir iebūvēta un automātiski konfigurēta MariaDB datubāze. Uptime Kuma pieslēgsies šai datubāzei izmantojot unix soketu.", + "setupDatabaseSQLite": "Vienkāršs datu bāzes fails, iesakāms maza izmēra risinājumiem. Pirms versijas v2.0.0 SQLite bija noklusējuma datubāze.", + "setupDatabaseMariaDB": "Pieslēgties ārējai MariaDB datubāzei. Jums būs jākonfigurē datubāzes pieslēgšanās informācija.", + "Name": "Nosaukums", + "Ping": "Ping", + "Dashboard": "Panelis", + "dbName": "Datubāzes nosaukums", + "enableSSL": "Iespējot SSL/TLS", + "Settings": "Iestatījumi", + "Help": "Palīdzība", + "New Update": "Jauns atjauninājums", + "Language": "Valoda", + "Appearance": "Izskats", + "Theme": "Tēma", + "General": "Vispārīgi", + "Game": "Spēle", + "mariadbCaCertificateLabel": "CA sertifikāts", + "Primary Base URL": "Galvenais bāzes URL", + "Check Update On GitHub": "Pārbaudīt atjauninājumu GitHub", + "List": "Saraksts", + "Home": "Sākums", + "Add": "Pievienot", + "Add New Monitor": "Pievienot jaunu monitoru", + "Quick Stats": "Ātrā statistika", + "Down": "Nedarbojas", + "Pending": "Rindā", + "statusMaintenance": "Tehniskā apkope", + "Maintenance": "Tehniskā apkope", + "Unknown": "Nezināms", + "unknownDays": "Nezināms dienu skaits", + "Cannot connect to the socket server": "Nevar izveidot savienojumu ar soketa serveri", + "Reconnecting...": "Savienojas...", + "General Monitor Type": "Vispārīgais monitora veids", + "pauseDashboardHome": "Pauze", + "Pause": "Pauze", + "DateTime": "Datums un laiks", + "Specific Monitor Type": "Specifiskais monitora veids", + "settingUpDatabaseMSG": "Tiek uzstādīta datubāze. Uzgaidiet, lūdzu, tas var prasīt laiku.", + "mariadbUseSSLHelptext": "Iespējot šifrētu savienojumu ar jūsu datubāzi. Nepieciešams lielākajai daļai mākoņdatubāžu.", + "mariadbCaCertificateHelptext": "Ielīmējiet CA sertifikātu PEM formātā, lai to izmantotu ar pašparakstītiem sertifikātiem. Atstājiet tukšu, ja jūsu datubāze izmanto publiskas sertifikācijas iestādes parakstītu sertifikātu.", + "Passive Monitor Type": "Pasīvais monitora veids", + "markdownSupported": "Tiek atbalstīta Markdown sintakse. Ja izmantojat HTML, izvairieties no atstarpēm rindas sākumā, lai novērstu formatēšanas problēmas.", + "versionIs": "Versija: {version}", + "monitorTypeGameServer": "Spēļu serveris", + "monitorTypeDatabase": "Datubāzes monitora veids", + "monitorTypeSpecial": "Specifisks", + "Message": "Ziņa", + "No incidents recorded": "Nav reģistrētu incidentu", + "Load More": "Ielādēt vairāk", + "Loading...": "Ielāde...", + "No important events": "Nav svarīgu notikumu", + "Resume": "Turpināt", + "Edit": "Labot", + "Delete": "Dzēst", + "Current": "Pašreizējais", + "Uptime": "Darbības laiks", + "Cert Exp.": "Sert. term.", + "Monitors": "{n} monitors | {n} monitori", + "now": "tagad", + "time ago": "pirms {0}", + "days": "{n} diena | {n} dienas", + "hours": "{n} stunda | {n} stundas", + "minutes": "{n} minūte | {n} minūtes", + "minuteShort": "{n} minūte | {n} minūtes", + "years": "{n} gads| {n} gadi", + "Response": "Atbilde", + "Pin this incident": "Piespraust šo incidentu", + "Monitor Type": "Monitora tips", + "Up": "Darbojas", + "Status": "Status", + "URL": "URL", + "Hostname": "Hostvārds", + "Port": "Ports", + "Path": "Ceļš", + "Heartbeat Interval": "Sirdsdarbības intervāls", + "Request Timeout": "Pieprasījuma laika limits", + "timeoutAfter": "Pēc {0} sekundēm tiks pārtraukta darbība", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Ievadiet vai nu tā servera nosaukumu, ar kuru vēlaties izveidot savienojumu, vai arī {localhost}, ja plānojat izmantot {local_mta}", + "Friendly Name": "Viegli atpazīstams nosaukums", + "Json Query Expression": "JSON vaicājuma izteiksme", + "defaultFriendlyName": "Jauns monitors", + "Host URL": "Mājaslapas URL", + "locally configured mail transfer agent": "vietējā līmenī konfigurēts pasta pārraides aģents", + "Retries": "Atkārtotas mēģinājums", + "Heartbeat Retry Interval": "Sirdsdarbības atkārtojuma intervāls", + "Resend Notification if Down X times consecutively": "Nosūtīt atkārtotu paziņojumu, ja sistēma ir nedarbīga X reizes pēc kārtas", + "Advanced": "Papildu", + "checkEverySecond": "Pārbaudīt ik pēc {0} sekundēm", + "retryCheckEverySecond": "Mēģināt atkārtot ik pēc {0} sekundēm", + "resendDisabled": "Atkārtota nosūtīšana ir atspējota", + "oracledbConnectionString": "Oracle datubāze: {connectionString}", + "Keyword": "Atslēgvārds", + "Invert Keyword": "Apgriezt atslēgvārdu", + "Expected Value": "Paredzamā vērtība", + "resendEveryXTimes": "Nosūtīt atkārtoti ik pēc {0} reizēm", + "retriesDescription": "Maksimālais atkārtoto mēģinājumu skaits, pirms pakalpojums tiek atzīmēts kā nedarbojošs un tiek nosūtīts paziņojums", + "Only retry if status code check fails": "Atkārtot mēģinājumu tikai tad, ja statusa koda pārbaude neizdodas" +} diff --git a/src/lang/lzh.json b/src/lang/lzh.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/lang/lzh.json @@ -0,0 +1 @@ +{} diff --git a/src/lang/mk.json b/src/lang/mk.json new file mode 100644 index 0000000..251463f --- /dev/null +++ b/src/lang/mk.json @@ -0,0 +1,92 @@ +{ + "setupDatabaseMariaDB": "Поврзи надворешна MariaDB датабаза. Мораш да ги внесеш податоците за најава од датабазата.", + "dbName": "Име од Датабаза", + "Settings": "Поставки", + "Dashboard": "Контролна табла", + "Help": "Помош", + "Language": "Јазик", + "Theme": "Тема", + "General": "Генерално", + "Game": "Игра", + "Version": "Верзија", + "Check Update On GitHub": "Проверете го ажурирањето на GitHub", + "List": "Листа", + "Home": "Дома", + "Add": "Додај", + "Add New Monitor": "Додај нов монитор", + "Quick Stats": "Брзи статистика", + "Up": "Горе", + "Down": "Доле", + "Pending": "Во очекување", + "Maintenance": "Одржување", + "Unknown": "Непознато", + "Reconnecting...": "Повторно поврзување...", + "Specific Monitor Type": "Специфичен тип на монитор", + "pauseDashboardHome": "Пауза", + "Name": "Име", + "Status": "Статус", + "Message": "Порака", + "No important events": "Нема важни настани", + "Resume": "Продолжи", + "Edit": "Измени", + "Current": "Сегашен", + "Cert Exp.": "Истек Серт.", + "Monitor": "Монитор | Монитори", + "day": "ден | денови", + "-day": "-ден", + "hour": "час", + "-hour": "-час", + "Ping": "Пинг", + "Monitor Type": "Мониторски тип", + "Invert Keyword": "Инвертирајте клучни зборови", + "Expected Value": "Очекувана Вредност", + "Json Query": "Json Query", + "URL": "УРЛ", + "Hostname": "Hostname", + "Port": "Порт", + "Heartbeat Interval": "Интервал на отчукување на срцето", + "Request Timeout": "Истекување на барањето", + "Retries": "Обиди", + "Advanced": "Напредно", + "checkEverySecond": "Провери секоја {0} секунда", + "retryCheckEverySecond": "Повтори секои {0} секунди", + "resendEveryXTimes": "Препрати на секои {0} пати", + "resendDisabled": "Повторно испраќање е оневозможено", + "ignoreTLSError": "Игнорирај TLS/SSL errors за HTTPS websites", + "ignoreTLSErrorGeneral": "Игнорирај TLS/SSL грешки при конекција", + "upsideDownModeDescription": "Превртете го статусот наопаку. Ако услугата е достапна, таа е ДОЛУ.", + "Upside Down Mode": "Режим наопаку", + "Max. Redirects": "Макс. Пренасочувања", + "Accepted Status Codes": "Прифатливи Статус Кодови", + "Push URL": "Push URL", + "pushOptionalParams": "Опционални параметри: {0}", + "languageName": "Македонски", + "setupDatabaseChooseDatabase": "Која датабаза би сакал да ја користиш?", + "setupDatabaseEmbeddedMariaDB": "Не треба ништо да поставувате. Оваа докер-слика автоматски ја вгради и конфигурираше MariaDB за вас. Uptime Kuma ќе се поврзе со оваа база на податоци преку уникс приклучок.", + "setupDatabaseSQLite": "Едноставна датотека со база на податоци, препорачана за распоредувања во мали размери. Пред верзијата 2.0.0, Uptime Kuma користеше SQLite како стандардна база на податоци.", + "settingUpDatabaseMSG": "Ја сетираме Датабазата за вас, ве молам почекајте.", + "New Update": "Ново ажурирање", + "Appearance": "Изглед", + "Primary Base URL": "Примарна основна URL", + "statusMaintenance": "Одржување", + "Cannot connect to the socket server": "Не може да се поврзе со сокет-серверот", + "General Monitor Type": "Општ тип на монитор", + "Passive Monitor Type": "Пасивен тип на монитор", + "markdownSupported": "Поддржана е синтакса Markdown", + "Pause": "Пауза", + "DateTime": "ДатаВреме", + "Delete": "Избриши", + "Uptime": "Време на работа", + "Response": "Одговор", + "Keyword": "Клучен збор", + "Friendly Name": "Пријателско Име", + "Host URL": "УРЛ адреса од сервер", + "locally configured mail transfer agent": "локално конфигуриран агент за пренос на пошта", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Или внесете го името на домаќинот на серверот на кој сакате да се поврзете или {localhost} ако имате намера да користите {local_mta}", + "timeoutAfter": "Истекување на времето по {0} секунди", + "Heartbeat Retry Interval": "Интервал на повторно обиди за отчукување на срцето", + "Resend Notification if Down X times consecutively": "Повторно испратете известување ако е долу X пати последователно", + "retriesDescription": "Максимално повторување пред услугата да биде означена како неискористена и да се испрати известување", + "maxRedirectDescription": "Максимален број пренасочувања што треба да се следат. Поставете на 0 за да ги оневозможите пренасочувањата.", + "needPushEvery": "Треба да ја повикувате оваа URL на секои {0} секунди." +} diff --git a/src/lang/ml.json b/src/lang/ml.json new file mode 100644 index 0000000..82fba9e --- /dev/null +++ b/src/lang/ml.json @@ -0,0 +1,52 @@ +{ + "Settings": "ക്രമീകരണം", + "Help": "സഹായം", + "New Update": "പുതിയ നവീകരണം", + "Language": "ഭാഷ", + "Appearance": "കാണപ്പെടുക", + "Theme": "ദൃശ്യക്രമീകരണം", + "General": "പൊതുവായത്", + "Version": "പതിപ്പ്", + "List": "പട്ടിക", + "Add": "ചേർക്കുക", + "Add New Monitor": "പുതിയ മോണിറ്റർ ചേർക്കുക", + "Quick Stats": "വേഗത്തിൽ ഇപ്പോളത്തെ അവസ്ഥ നോക്കുക", + "Up": "മുകളിൽ", + "Down": "താഴെ", + "statusMaintenance": "പരിപാലനം", + "Maintenance": "പരിപാലനം", + "Unknown": "അജ്ഞാതം", + "Passive Monitor Type": "പാർശമായ തിര നോട്ടം", + "Specific Monitor Type": "പ്രത്യേക തിര നോട്ടം", + "languageName": "മലയാളം", + "Dashboard": "നിയന്ത്രണോപകരണ സജ്ജീകരണം", + "Game": "കളികൾ", + "Check Update On GitHub": "പുതിയ മാറ്റങ്ങൾക്കായി GitHub നോക്കുക", + "Pending": "തീരുമാനം പ്രതീക്ഷിച്ചിരിക്കുന്ന", + "General Monitor Type": "പൊതുവരായ തിര നോട്ടം", + "settingUpDatabaseMSG": "ഡാറ്റാബേസ് സജ്ജീകരിച്ചുകൊണ്ടിരിക്കുന്നു . ദയവായി കുറച്ച് സമയം കാത്തിരിക്കുക.", + "setupDatabaseChooseDatabase": "ഏത് ഡാറ്റാബേസാണ് നിങ്ങൾ ഉപയോഗിക്കാൻ ആഗ്രഹിക്കുന്നത്?", + "dbName": "ഡാറ്റാബേസിന്റെ പേര്", + "Reconnecting...": "വീണ്ടും ബന്ധപ്പെടാൻ ശ്രമിക്കുന്നു...", + "Cannot connect to the socket server": "സോക്കറ്റ് സെർവറുമായി ബന്ധപ്പെടാൻ സാധിക്കുന്നില്ല", + "Name": "നാമം", + "hour": "മണിക്കൂർ", + "-hour": "-മണിക്കൂർ", + "Response": "ഉത്തരം", + "Request Timeout": "പ്രതികരണം ലഭിക്കാനുള്ള സമയപരിധി അവസാനിച്ചിരിക്കുന്നു", + "timeoutAfter": "സമയപരിധി {0} നിമിഷങ്ങൾക്കകം അവസാനിക്കും", + "Retries": "വീണ്ടും ശ്രമിക്കൽ", + "Heartbeat Retry Interval": "ഹൃദയമിടിപ്പ് പുനരവലോകന ഇടവേള", + "Resend Notification if Down X times consecutively": "തുടർച്ചയായി X തവണ പ്രവർത്തനം തടസ്സപ്പെട്ടാൽ അറിയിപ്പ് വീണ്ടും അയയ്ക്കുക", + "Advanced": "പുരോഗമിച്ച", + "checkEverySecond": "ഓരോ {0} നിമിഷങ്ങൾക്ക് ശേഷവും പരിശോധിക്കുക", + "retryCheckEverySecond": "ഓരോ {0} നിമിഷങ്ങൾക്ക് ശേഷവും വീണ്ടും ശ്രമിക്കുക", + "resendEveryXTimes": "ഓരോ {0} പ്രാവശ്യവും വീണ്ടും അയയ്ക്കുക", + "resendDisabled": "വീണ്ടും അയയ്ക്കൽ നിരോധിച്ചിരിക്കുന്നു", + "Notifications": "അറിയിപ്പുകൾ", + "Not available, please setup.": "ലഭ്യമല്ല, ദയവായി സജ്ജീകരിക്കുക.", + "Setup Notification": "വിജ്ഞാപനം സജ്ജമാക്കുക", + "Light": "വെളിച്ചം", + "Dark": "ഇരുട്ട്", + "Auto": "സ്വയം" +} diff --git a/src/lang/mr.json b/src/lang/mr.json new file mode 100644 index 0000000..78d1511 --- /dev/null +++ b/src/lang/mr.json @@ -0,0 +1,114 @@ +{ + "languageName": "मराठी", + "setupDatabaseChooseDatabase": "तुम्हाला कोणता डेटाबेस वापरायचा आहे?", + "setupDatabaseEmbeddedMariaDB": "तुम्हाला काहीही सेट करण्याची गरज नाही. या डॉकर इमेजमध्ये मारीया डीबी आपोआप एम्बेडेड आणि कॉन्फिगर केलेले आहे. अपटाईम कुमाच्या युनिक्स सॉकेटद्वारे या डेटाबेसशी कनेक्ट होईल.", + "setupDatabaseMariaDB": "एखाद्या बाह्य मारीया डीबी डेटाबेसशी कनेक्ट करा. तुम्हाला डेटाबेस कनेक्शनची माहिती सेट करणे आवश्यक आहे.", + "setupDatabaseSQLite": "एक साधी डेटाबेस फाइल, जी लहान प्रमाणातील उपयोगांसाठी शिफारस केलेली आहे. v2.0.0 च्या आधी, अपटाइम कुमा ने एसक्यु लाईट ला डीफॉल्ट डेटाबेस म्हणून वापरले होते.", + "settingUpDatabaseMSG": "डेटाबेस सेट अप करत आहे. यास थोडा वेळ लागू शकतो, कृपया धीर धरा.", + "dbName": "डेटाबेसचे नाव", + "oracledbConnectionString": "ओरॅकल डेटाबेस: {connectionString}", + "enableSSL": "SSL/TLS सक्षम करा", + "mariadbUseSSLHelptext": "तुमच्या डेटाबेससाठी एनक्रिप्टेड कनेक्शन वापरण्याची सुविधा सक्षम करा. बहुतेक क्लाउड डेटाबेसेससाठी हे आवश्यक आहे.", + "mariadbCaCertificateLabel": "सीए प्रमाणपत्र", + "mariadbCaCertificateHelptext": "स्वतः स्वाक्षरी केलेल्या (self-signed) प्रमाणपत्रांचा वापर करण्यासाठी PEM स्वरूपात CA प्रमाणपत्र येथे पेस्ट करा. जर तुमचा डेटाबेस सार्वजनिक CA (Certificate Authority) द्वारे स्वाक्षरी केलेले प्रमाणपत्र वापरत असेल, तर हे रकाने रिकामे ठेवा.", + "Settings": "सेटिंग्ज", + "Dashboard": "डॅशबोर्ड", + "Help": "मदत", + "New Update": "नवीन अपडेट", + "Language": "भाषा", + "Appearance": "स्वरूप", + "Theme": "रंगसंगती", + "General": "सामान्य", + "Game": "खेळ", + "Primary Base URL": "मुख्य बेस युआरएल", + "versionIs": "आवृत्ती: {version}", + "Check Update On GitHub": "गिटहबवर अपडेट तपासा", + "List": "यादी", + "Home": "मुख्यपृष्ठ", + "Add": "जोडा", + "Add New Monitor": "नवीन मॉनिटर जोडा", + "Quick Stats": "झटपट आकडेवारी", + "Up": "वर", + "Down": "खाली", + "Pending": "प्रलंबित", + "Maintenance": "देखभाल", + "Unknown": "अज्ञात", + "unknownDays": "अज्ञात दिवस", + "Cannot connect to the socket server": "सॉकेट सर्व्हरशी कनेक्ट करू शकत नाही", + "Reconnecting...": "पुन्हा जोडत आहे...", + "General Monitor Type": "सामान्य मॉनिटर प्रकार", + "teltonikaPhoneNumber": "दूरध्वनी क्रमांक", + "teltonikaPhoneNumberHelptext": "नंबर आंतरराष्ट्रीय स्वरूपात असले पाहिजे {0}, {1}. फक्त एकच नंबर अनुमत आहे.", + "ntpThresholdsTitle": "एनटीपी थ्रेशोल्ड्स", + "ntpStratumThreshold": "स्ट्रॅटम थ्रेशोल्ड", + "milkyGroupMessage": "समूह", + "milkyPrivateMessage": "खाजगी", + "milkyUserOrGroupId": "ग्रुप/युजर आयडी", + "Use Own Line for Pattern": "पॅटर्नसाठी स्वतःची ओळ वापरा", + "successAdded": "यशस्वीरित्या जोडले.", + "successResumed": "यशस्वीरित्या पुनरारंभ केला.", + "successPaused": "यशस्वीरीत्या तात्पुरते थांबवले.", + "successDeleted": "यशस्वीरित्या हटवले.", + "systemServiceExpectedOutput": "अपेक्षित आउटपुट: \"{0}\"", + "statusMaintenance": "देखभाल", + "Pause": "थांबवा", + "Name": "नाव", + "Status": "स्थिती", + "DateTime": "दिनांक आणि वेळ", + "Message": "संदेश", + "No incidents recorded": "कोणत्याही घटना नोंदवल्या गेलेल्या नाहीत", + "Load More": "अधिक दाखवा", + "Loading...": "लोड होत आहे...", + "No important events": "कोणत्याही महत्त्वाच्या घटना नाहीत", + "Resume": "पूर्ववत सुरू करा", + "Edit": "संपादित करा", + "Passive Monitor Type": "निष्क्रिय मॉनिटर प्रकार", + "Specific Monitor Type": "विशिष्ट मॉनिटर प्रकार", + "monitorTypeGameServer": "गेम सर्व्हर", + "monitorTypeDatabase": "डेटाबेस मॉनिटर प्रकार", + "monitorTypeSpecial": "विशेष", + "markdownSupported": "मार्कडाउन सिंटॅक्स समर्थित आहे. एचटीएमएल वापरत असल्यास, फॉरमॅटिंगच्या समस्या टाळण्यासाठी सुरुवातीला मोकळी जागा देणे टाळा.", + "pauseDashboardHome": "विराम", + "Delete": "हटवा", + "Current": "चालू", + "Uptime": "अपटाइम", + "now": "सध्या", + "Blue": "निळा", + "Indigo": "गडद निळा", + "Purple": "जांभळा", + "Pinned incidents are shown prominently on the status page": "पिन केलेले (निवडक) इन्सिडेंट्स स्टेटस पेजवर ठळकपणे दाखवले जातात", + "Pink": "गुलाबी", + "Custom": "कस्टम", + "Cert Exp.": "सर्टिफिकेट एक्स्पायरी.", + "Monitors": "{n} मॉनिटर | {n} मॉनिटर्स", + "time ago": "{0} पूर्वी", + "days": "{n} दिवस | {n} दिवस", + "hours": "{n} तास | {n} तास", + "minutes": "{n} मिनिट | {n} मिनिटे", + "minuteShort": "", + "years": "{n} वर्ष | {n} वर्षे", + "Response": "प्रतिसाद", + "Pin this incident": "हा इन्सिडेंट पिन करा", + "Ping": "पिंग", + "Monitor Type": "मॉनिटर टाईप", + "Keyword": "कीवर्ड", + "Invert Keyword": "इनव्हर्ट कीवर्ड", + "Expected Value": "अपेक्षित मूल्य", + "Json Query Expression": "जेसन क्वेरी एक्प्रेशन", + "Friendly Name": "फ्रेंडली नेम", + "defaultFriendlyName": "नवीन मॉनिटर", + "URL": "युआरएल", + "Hostname": "होस्टनेम", + "Host URL": "होस्ट युआरएल", + "locally configured mail transfer agent": "स्थानिक स्तरावर कॉन्फिगर केलेला मेल ट्रान्सफर एजंट", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "एकतर तुम्हाला ज्या सर्व्हरशी कनेक्ट करायचे आहे त्याचे होस्टनेम प्रविष्ट करा, किंवा जर तुम्हाला {local_mta} वापरण्याचा विचार असेल तर {localhost} प्रविष्ट करा", + "Port": "पोर्ट", + "ignoreSecWebsocketAcceptHeaderDescription": "वेबसॉकेट अपग्रेड यशस्वी झाल्यास, सर्व्हरला Sec-WebSocket-Accept हेडरसह उत्तर न देण्याची परवानगी देते.", + "wsSubprotocolDescription": "सबप्रोटोकॉल्सची कॉमा-डेलिमिटेड यादी प्रविष्ट करा. सबप्रोटोकॉल्सबद्दल अधिक माहितीसाठी, कृपया {documentation} पहा", + "wsCodeDescription": "स्टेटस कोड्सबद्दल अधिक माहितीसाठी, कृपया {rfc6455} पहा", + "maxPing": "कमाल पिंग", + "minPing": "किमान पिंग", + "Setup Instructions": "सेटअप मार्गदर्शक", + "halopsa_setup_step1": "इंटिग्रेशन रनबुक तयार करा (कॉन्फिगरेशन → इंटिग्रेशन्स → इंटिग्रेशन रनबुक्स)", + "halopsa_setup_step2": "अलर्ट्स प्रोसेस करण्यासाठी रनबुक ॲक्शन्स कॉन्फिगर करा (उदा. तिकीट तयार करा)" +} diff --git a/src/lang/ms.json b/src/lang/ms.json new file mode 100644 index 0000000..02ce100 --- /dev/null +++ b/src/lang/ms.json @@ -0,0 +1,90 @@ +{ + "Help": "Bantuan", + "New Update": "Kemaskini baharu", + "Appearance": "Penampilan", + "Theme": "Tema", + "General": "Umum", + "Game": "Permainan", + "Primary Base URL": "URL Pangkalan Utama", + "Version": "Versi", + "Add": "Tambah", + "Quick Stats": "Statistik Pantas", + "Up": "Atas", + "Down": "Bawah", + "Pending": "Dalam Proses", + "statusMaintenance": "Penyelenggaraan", + "Maintenance": "Penyelenggaraan", + "Unknown": "Tidak Diketahui", + "General Monitor Type": "Jenis Monitor Umum", + "Check Update On GitHub": "Semak kemas kini di GitHub", + "List": "Senarai", + "Specific Monitor Type": "Jenis Monitor Spesifik", + "markdownSupported": "Sintaks markdown disokong", + "languageName": "Bahasa inggeris", + "Dashboard": "Papan pemuka", + "Language": "Bahasa", + "Add New Monitor": "Tambah Monitor Baharu", + "Passive Monitor Type": "Jenis Monitor Pasif", + "No Services": "Tiada Servis", + "Add a monitor": "Tambah Monitor", + "High": "Tinggi", + "Normal": "Biasa", + "Bottom": "Bawah", + "None": "None", + "All Systems Operational": "Semua Sistem Baik", + "Partially Degraded Service": "Perkhidmatan Separa Tergendala", + "Degraded Service": "Perkhidmatan Tergendala", + "Add Group": "Tambah Kumpulan", + "Edit Status Page": "Ubah Laman Status", + "Go to Dashboard": "Ke Dashboard", + "Status Page": "Laman Status", + "Status Pages": "Laman Status", + "Avg. Ping": "Ping Purata", + "Avg. Response": "Respons Purata", + "color": "Warna", + "Active": "Aktif", + "Inactive": "Nyahaktif", + "Blue": "Biru", + "Red": "MErah", + "Content Type": "Jenis Content", + "Home": "Laman Utama", + "Settings": "Tetapan", + "Save": "Simpan", + "Cannot connect to the socket server": "Tidak dapat disambungkan kepada pelayan soket", + "Resume": "Sambung", + "Current": "Terkini", + "Uptime": "Uptime", + "Cert Exp.": "Tamat Sijil", + "now": "sekarang", + "setupDatabaseMariaDB": "Sambungan kepada pangkalan data MariaDB secara luaran. Anda perlu tetapkan maklumat sambungan pangkalan data.", + "hour": "jam", + "Ping": "Ping", + "settingUpDatabaseMSG": "Pangkalan data sedang ditetapkan. Sila tunggu sebentar.", + "Reconnecting...": "Penyambungan...", + "Message": "Mesej", + "No important events": "Tiada info penting", + "Edit": "Sunting", + "Delete": "Padam", + "Monitor": "Monitor | Monitors", + "time ago": "{0} yang lepas", + "day": "hari | hari", + "-day": "-hari", + "-year": "-tahun", + "Pause": "Rehat", + "Status": "Status", + "DateTime": "TarikhMasa", + "dbName": "Nama Pangkalan Data", + "-hour": "-jam", + "Response": "Tindakbalas", + "Monitor Type": "Jenis Monitor", + "Keyword": "Katakunci", + "pauseDashboardHome": "Rehat", + "Name": "Nama", + "setupDatabaseChooseDatabase": "Pangkalan Data yang mana hendak digunakan ?", + "Host URL": "URL Host", + "URL": "URL", + "Expected Value": "Nilai Sepatutnya", + "Friendly Name": "Nama Mudah", + "Hostname": "Nama Host", + "setupDatabaseEmbeddedMariaDB": "Kamu tidak perlu" +} diff --git a/src/lang/my.json b/src/lang/my.json new file mode 100644 index 0000000..d893a04 --- /dev/null +++ b/src/lang/my.json @@ -0,0 +1,95 @@ +{ + "languageName": "အင်္ဂလိပ်ဘာသာ", + "Settings": "ပြင်ဆင်ချက်များ", + "Help": "အကူအညီ", + "New Update": "အသစ်ထွက်ရှိခြင်း", + "Language": "ဘာသာစကား", + "Appearance": "သွင်ပြင်လက္ခဏာ", + "Theme": "သဏ္ဍာန်", + "General": "အထွေထွေ", + "Primary Base URL": "ဦးစားပေးအင်တာနက်လိပ်စာ", + "Version": "စနစ်အဆင့်", + "List": "စာရင်း", + "Home": "ပင်မစာမျက်နှာ", + "Dashboard": "ခြုံငုံသုံးသပ်ချက်စာမျက်နှာ", + "Add": "အသစ်ပေါင်းထည့်မည်", + "Quick Stats": "စာရင်းအားခြုံငုံကြည့်ရှုခြင်း", + "Up": "ချိတ်ဆက်မှုအောင်မြင်နေသည်", + "Maintenance": "ပြုပြင်နေသည်", + "Unknown": "အမည်မသိအကြောင်းအရာ", + "Reconnecting...": "ပြန်လည်ချိတ်ဆက်နေပါသည်...", + "General Monitor Type": "အထွေထွေစောင့်ကြည့်မှုပုံစံ", + "markdownSupported": "Markdown syntax အားထောက်ပံ့သည်", + "pauseDashboardHome": "ခေတ္တရပ်တန့်မည်", + "Pause": "ခေတ္တရပ်တန့်မည်", + "Name": "အမ်", + "Status": "အခြေအနေများ", + "DateTime": "နေ့ရက်နှင့်အချိန်", + "Message": "စာတို", + "No important events": "အရေးမကြီးသောဖြစ်ရပ်များ", + "Game": "ဂိမ်း", + "Check Update On GitHub": "အသစ်ထွက်ရှိမှုအား GitHub တွင် စစ်ဆေးရန်", + "Add New Monitor": "စောင့်ကြည့်မှုအသစ်ပေါင်းထည့်မည်", + "Down": "ကွန်ရက်ပြတ်တောက်နေသည်", + "Pending": "ဆိုင်းငံ့ဆဲ", + "statusMaintenance": "ပြုပြင်ထိန်းသိမ်းခြင်း", + "Cannot connect to the socket server": "Why Socket Server အားချိတ်ဆက်မှုမအောင်မြင်ပါ", + "Passive Monitor Type": "Passive စောင့်ကြည့်မှုပုံစံ", + "Specific Monitor Type": "သီးခြားစောင့်ကြည့်မှုပုံစံ", + "Resume": "ဆက်သွားမည်", + "Edit": "ပြောင်းလဲမည်", + "Delete": "ဖျက်သိမ်းမည်", + "Current": "ယခုလက်ရှိ", + "Uptime": "ကွန်ယက်ချိတ်ဆက်မှုကြာချိန်", + "Cert Exp.": "ဆာတီဖီကိတ်ကုန်ဆုံးချိန်", + "Monitor": "စောင့်ကြည့်မှု | စောင့်ကြည့်မှုများ", + "day": "နေ့ရက် | ရက်များ", + "-day": "-နေ့ရက်", + "hour": "နာရီ", + "-hour": "-နာရီ", + "Response": "တုံ့ပြန်မှု", + "Monitor Type": "စောင့်ကြည့်မှုပုံစံ", + "Keyword": "စာသား", + "Invert Keyword": "စာသားပြောင်းပြန်", + "Friendly Name": "မှတ်သားရန်လွယ်ကူသည့်အမည်", + "URL": "အင်တာနက်လိပ်စာ", + "Hostname": "စောင့်ကြည့်မှုအမည်", + "Port": "အပေါက်", + "Advanced": "အဆင့်မြင့်ပြင်ဆင်မှုများ", + "checkEverySecond": "{0} စက္ကန့်တိုင်း‌စောင့်ကြည့်မည်", + "retryCheckEverySecond": "{0} စက္ကန့်တိုင်း‌ထပ်မံကြိုးစားမည်", + "resendEveryXTimes": "{0} စက္ကန့်တိုင်း‌ထပ်မံပေးပို့မည်", + "resendDisabled": "ပိတ်ဆို့ထားခြင်းအားပေးပို့မ်", + "ignoreTLSError": "HTTPS ဝက်ဘ်ဆိုဒ်များအတွက် TLS/SSL အပြစ်အား‌လျစ်လျှူရှုမည်", + "Save": "သိမ်းဆည်းမည်", + "Notifications": "သတိပေးချက်များ", + "Not available, please setup.": "လတ်တလောမရရှိနိုင်ပါ၊ ကျေးဇူးပြု၍ပြင်ဆင်ပါ", + "Setup Notification": "ပြင်ဆင်ခြင်းသတိပေးချက်", + "Light": "အလင်း", + "Dark": "အမှောင်", + "Auto": "အလိုအလျောက်", + "Theme - Heartbeat Bar": "သဏ္ဍာန်-ကွန်ယက်တိုင်းတာနှုန်းပေတံ", + "Normal": "ပုံမှန်", + "Bottom": "အောက်ဆုံး", + "None": "ဘာမှမရှိပါ", + "Timezone": "အချိန်ဇုန်", + "Allow indexing": "အစီအစဉ်ချခြင်းကိုခွင့်ပြုရန်", + "Change Password": "စကားဝှက်ပြောင်းမည်", + "Current Password": "လက်ရှိစကားဝှက်", + "New Password": "စကားဝှက်အသစ်", + "Repeat New Password": "စကားဝှက်အသစ်အားထပ်မံရိုက်ထည့်ပါ", + "Update Password": "စကားဝှက်အားပြင်ဆင်မည်", + "Disable Auth": "ဝင်ရောက်မှုထပ်မံစစ်ဆေးခြင်းအားဖျက်သိမ်းမည်", + "Enable Auth": "ဝင်ရောက်မှုထပ်မံစစ်ဆေးခြင်းအားအတည်ပြုမည်", + "disableauth.message1": "ဝင်ရောက်မှုထပ်မံစစ်ဆေးခြင်းကိုဖျက်သိမ်းရန်သေချာပါသလား?", + "Ping": "ချိတ်ဆက်မှုတိုင်းတာခြင်း", + "Expected Value": "မျှော်လင့်ထားသည့်တန်ဖိုး", + "Heartbeat Interval": "ကွန်ယက်ချိတ်ဆက်နိုင်မှုတိုင်းတာခြင်း အချိန်ကွာဟချက်", + "Retries": "ထပ်မံကြိုးစားမှုများ", + "Heartbeat Retry Interval": "ကွန်ယက်ချိတ်ဆက်နိုင်မှုတိုင်းတာခြင်း ထပ်မံကြိုးစားခြင်း အချိန်ကွာဟချက်", + "Resend Notification if Down X times consecutively": "ကွန်ယက်ချိတ်ဆက်မှု X အကြိမ်ထိ ဆက်တိုက်ကျနေပါက သတိပေးချက်ထပ်မံပေးပို့ရန်", + "retriesDescription": "ဝန်ဆောင်မှုကွန်ယက်ပြတ်တောက်နေ၍ သတိပေးချက်ပေးပို့ပြီး အများဆုံးထပ်မံကြိုးစားနိုင်မှု", + "Search Engine Visibility": "ရှာဖွေမှုအင်ဂျင်များ၏မြင်နိုင်စွမ်း", + "setupDatabaseEmbeddedMariaDB": "You don't need to set anything. This docker image has embedded and configured MariaDB for you automatically. Uptime Kuma will connect to this database via unix socket.", + "setupDatabaseChooseDatabase": "English" +} diff --git a/src/lang/nb-NO.json b/src/lang/nb-NO.json new file mode 100644 index 0000000..fd468d7 --- /dev/null +++ b/src/lang/nb-NO.json @@ -0,0 +1,1553 @@ +{ + "languageName": "Norsk", + "checkEverySecond": "Sjekk hvert {0} sekund", + "retryCheckEverySecond": "Prøv igjen hvert {0} sekund", + "retriesDescription": "Maksimalt antall forsøk før tjenesten blir merket som nede og et varsel sendes", + "ignoreTLSError": "Ignorer TLS/SSL-feil for HTTPS-nettsteder", + "upsideDownModeDescription": "Snu statusen opp ned. Hvis tjenesten er tilgjengelig, er den NEDE.", + "maxRedirectDescription": "Maksimalt antall viderekoblinger å følge. Sett til 0 for å deaktivere viderekoblinger.", + "acceptedStatusCodesDescription": "Velg statuskoder som anses som en vellykket respons.", + "passwordNotMatchMsg": "Passordene stemmer ikke overens.", + "notificationDescription": "Varsler må tilordnes en overvåkning for å fungere.", + "keywordDescription": "Søk etter nøkkelord i ren HTML eller JSON. Søket skiller mellom store og små bokstaver.", + "pauseDashboardHome": "Pause", + "deleteMonitorMsg": "Er du sikker på at du vil slette denne overvåkningen?", + "deleteNotificationMsg": "Er du sikker på at du vil slette dette varselet for alle overvåkningene?", + "resolverserverDescription": "Cloudflare er standardserver. Du kan spesifisere en kommaseparert liste over IP-adresser eller vertsnavn.", + "rrtypeDescription": "Velg RR-typen du vil overvåke", + "pauseMonitorMsg": "Er du sikker på at du vil sette på pause?", + "enableDefaultNotificationDescription": "For hver ny overvåkning vil denne varslingen være aktivert som standard. Du kan fortsatt deaktivere varselet separat for hver overvåkning.", + "clearEventsMsg": "Er du sikker på at du vil slette alle hendelser for denne overvåkeren? Dette vil permanent fjerne tilknyttede meldingsdata og kan ikke angres.", + "clearHeartbeatsMsg": "Er du sikker på at du vil slette alle hjerteslag for denne overvåkeren? Dette vil permanent fjerne alle hjerteslagsdata og kan ikke angres.", + "confirmClearStatisticsMsg": "Er du sikker på at du vil slette ALL statistikk?", + "importHandleDescription": "Velg 'Hopp over eksisterende' hvis du vil hoppe over hver overvåkning eller varsel med samme navn. 'Overskriv' sletter alle eksisterende overvåkninger og varsler.", + "confirmImportMsg": "Er du sikker på at du vil importere denne sikkerhetskopien? Sørg for at du har valgt riktig importalternativ.", + "twoFAVerifyLabel": "Skriv inn tokenet ditt for å bekrefte at 2FA fungerer:", + "tokenValidSettingsMsg": "Token er gyldig! Du kan nå lagre 2FA-innstillingene.", + "confirmEnableTwoFAMsg": "Er du sikker på at du vil aktivere 2FA?", + "confirmDisableTwoFAMsg": "Er du sikker på at du vil deaktivere 2FA?", + "Settings": "Innstillinger", + "Dashboard": "Dashbord", + "New Update": "Ny oppdatering", + "Language": "Språk", + "Appearance": "Utseende", + "Theme": "Tema", + "General": "Generelt", + "Version": "Versjon", + "Check Update On GitHub": "Sjekk oppdatering på GitHub", + "List": "Liste", + "Add": "Legg til", + "Add New Monitor": "Legg til ny overvåkning", + "Quick Stats": "Statistikk", + "Up": "Oppe", + "Down": "Nede", + "Pending": "Avventer", + "Unknown": "Ukjent", + "Pause": "Pause", + "Name": "Navn", + "Status": "Status", + "DateTime": "Dato/tid", + "Message": "Melding", + "No important events": "Ingen viktige hendelser", + "Resume": "Gjenoppta", + "Edit": "Rediger", + "Delete": "Slett", + "Current": "Nåværende", + "Uptime": "Oppetid", + "Cert Exp.": "Sertifikat utløper.", + "day": "dag | dager", + "-day": "-dag", + "hour": "time", + "-hour": "-time", + "Response": "Respons", + "Ping": "Ping", + "Monitor Type": "Overvåkningstype", + "Keyword": "Stikkord", + "Friendly Name": "Lett gjenkjennelig navn", + "URL": "URL", + "Hostname": "Vertsnavn", + "Port": "Port", + "Heartbeat Interval": "Hjerteslagsintervall", + "Retries": "Forsøk", + "Heartbeat Retry Interval": "Hjerteslagsforsøkintervall", + "Advanced": "Avansert", + "Upside Down Mode": "Opp-ned-modus", + "Max. Redirects": "Maks. viderekoblinger", + "Accepted Status Codes": "Godkjente statuskoder", + "Save": "Lagre", + "Notifications": "Varsler", + "Not available, please setup.": "Ikke tilgjengelig, venligst sett opp.", + "Setup Notification": "Sett opp varsel", + "Light": "Lys", + "Dark": "Mørk", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Tema - Hjerteslagslinje", + "Normal": "Normal", + "Bottom": "Bunn", + "None": "Ingen", + "Timezone": "Tidssone", + "Search Engine Visibility": "Søkemotor-synlighet", + "Allow indexing": "Tillat indeksering", + "Discourage search engines from indexing site": "Fraråd søkemotorer fra å indeksere nettstedet", + "Change Password": "Endre passord", + "Current Password": "Nåværende passord", + "New Password": "Nytt passord", + "Repeat New Password": "Gjenta nytt passord", + "Update Password": "Oppdater passord", + "Disable Auth": "Deaktiver autentisering", + "Enable Auth": "Aktiver autentisering", + "disableauth.message1": "Er du sikker på at du vil {disableAuth}?", + "disable authentication": "deaktiver autentisering", + "disableauth.message2": "Dette er tiltenkt scenarier {intendThirdPartyAuth} foran Uptime Kuma, for eksempel Cloudflare Access, Authelia eller andre autentiseringsmekanismer.", + "where you intend to implement third-party authentication": "hvor du planlegger å implementere tredjeparts-autentifikasjon", + "Please use this option carefully!": "Bruk dette valget med forsiktighet!", + "Logout": "Logg ut", + "Leave": "Forlat", + "I understand, please disable": "Jeg forstår, vennligst deaktiver", + "Confirm": "Bekreft", + "Yes": "Ja", + "No": "Nei", + "Username": "Brukernavn", + "Password": "Passord", + "Remember me": "Husk meg", + "Login": "Logg inn", + "No Monitors, please": "Ingen overvåkning, takk", + "add one": "legg til en", + "Notification Type": "Meldingstype", + "Email": "E-post", + "Test": "Test", + "Certificate Info": "Sertifikatinformasjon", + "Resolver Server": "DNS-server", + "Resource Record Type": "DNS-posttype", + "Last Result": "Siste resultat", + "Create your admin account": "Opprett en administratorkonto", + "Repeat Password": "Gjenta passord", + "Import Backup": "Importer sikkerhetskopi", + "Export Backup": "Eksporter sikkerhetskopi", + "Export": "Eksporter", + "Import": "Importer", + "respTime": "Svartid (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Standard aktivert", + "Apply on all existing monitors": "Bruk for alle eksisterende overvåkninger", + "Create": "Opprett", + "Clear Data": "Slett data", + "Events": "Hendelser", + "Heartbeats": "Hjerteslag", + "Auto Get": "Auto-hent", + "backupDescription": "Du kan sikkerhetskopiere alle overvåkninger og alle varsler til en JSON-fil.", + "backupDescription2": "PS: Historikk og hendelsesdata er ikke inkludert.", + "backupDescription3": "Følsomme data som varslingstokener er inkludert i eksportfilen. Vennligst oppbevar dem sikkert.", + "alertNoFile": "Velg en fil som skal importeres.", + "alertWrongFileType": "Velg en JSON-fil.", + "Clear all statistics": "Fjern all statistikk", + "Skip existing": "Hopp over eksisterende", + "Overwrite": "Overskriv", + "Options": "Alternativer", + "Keep both": "Behold begge", + "Verify Token": "Bekreft token", + "Setup 2FA": "Sett opp 2FA", + "Enable 2FA": "Aktiver 2FA", + "Disable 2FA": "Deaktiver 2FA", + "2FA Settings": "2FA-innstillinger", + "Two Factor Authentication": "To-faktor-autentisering", + "Active": "Aktiv", + "Inactive": "Inaktiv", + "Token": "Token", + "Show URI": "Vis URI", + "Tags": "Etiketter", + "Add New below or Select...": "Legg til nytt nedenfor, eller Velg…", + "Tag with this name already exist.": "Etikett med dette navnet eksisterer allerede.", + "Tag with this value already exist.": "Etikett med denne verdien eksisterer allerede.", + "color": "farge", + "value (optional)": "verdi (valgfritt)", + "Gray": "Grå", + "Red": "Rød", + "Orange": "Oransje", + "Green": "Grønn", + "Blue": "Blå", + "Indigo": "Indigo", + "Purple": "Lilla", + "Pink": "Rosa", + "Search...": "Søk…", + "Avg. Ping": "Gj.sn. ping", + "Avg. Response": "Gj.sn. respons", + "Entry Page": "Oppføringsside", + "statusPageNothing": "Ingenting her, vennligst legg til en gruppe eller en overvåkning.", + "No Services": "Ingen tjenester", + "All Systems Operational": "Alle systemer i drift", + "Partially Degraded Service": "Delvis degradert tjeneste", + "Degraded Service": "Degradert tjeneste", + "Add Group": "Legg til gruppe", + "Add a monitor": "Legg til en overvåkning", + "Edit Status Page": "Rediger statusside", + "Go to Dashboard": "Gå til Dashboard", + "Status Page": "Statusside", + "Status Pages": "Statusside", + "defaultNotificationName": "Min {notification} varsling ({number})", + "here": "her", + "Required": "Obligatorisk", + "telegram": "Telegram", + "Bot Token": "Bot-token", + "wayToGetTelegramToken": "Du kan få et token fra {0}.", + "Chat ID": "Chat ID", + "supportTelegramChatID": "Support Direkte Chat / Gruppe/ Kanalchat ID", + "wayToGetTelegramChatID": "Du kan få chat-ID-en din ved å sende en melding til boten og gå til denne nettadressen for å se chat_id:", + "YOUR BOT TOKEN HERE": "DITT BOT TOKEN HER", + "chatIDNotFound": "Chat-ID ble ikke funnet. Send en melding til denne boten først", + "webhook": "Webhook", + "Post URL": "Post-URL", + "Content Type": "Innholdstype", + "webhookJsonDesc": "{0} er bra for alle moderne HTTP-servere som express.js", + "webhookFormDataDesc": "{multipart} er bra for PHP. JSON trenger å bli analysert med {decodeFunction}", + "smtp": "E-post (SMTP)", + "secureOptionNone": "None / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignorer TLS feilmelding", + "From Email": "Fra E-post", + "To Email": "Til E-post", + "smtpCC": "CC", + "smtpBCC": "BCC", + "discord": "Discord", + "Discord Webhook URL": "Discord Webhook URL", + "wayToGetDiscordURL": "Du kan få denne ved å gå til Serverinnstillinger -> Integrasjoner -> Vis webhooks -> Opprett en Webhook", + "Bot Display Name": "Bot Visningsnavn", + "Prefix Custom Message": "Prefiks tilpasset melding", + "Hello @everyone is...": "Hei {'@'}everyone det er…", + "teams": "Microsoft Teams", + "Webhook URL": "Webhook-URL", + "wayToGetTeamsURL": "Du kan lære hvordan du oppretter en webhook-URL {0}.", + "signal": "Signal", + "Number": "Nummer", + "Recipients": "Mottakere", + "needSignalAPI": "Du må ha en Signal-klient med REST API.", + "wayToCheckSignalURL": "Du kan sjekke denne nettadressen for å se hvordan du konfigurerer en:", + "signalImportant": "VIKTIG: Du kan ikke blande grupper og nummere i mottakere!", + "gotify": "Gotify", + "Application Token": "Applikasjonstoken", + "Server URL": "Server-URL", + "Priority": "Prioritet", + "slack": "Slack", + "Icon Emoji": "Icon Emoji", + "Channel Name": "Kanal navn", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Mer informasjon om webhooks på: {0}", + "aboutChannelName": "Skriv inn kanalnavnet på {0} Kanalnavn-feltet hvis du vil omgå webhook-kanalen. Eks: #other-channel", + "aboutKumaURL": "Hvis du lar Uptime Kuma URL feltet være blank, den blir som standard til Github-siden for dette prosjektet.", + "emojiCheatSheet": "Emoji-jukselapp: {0}", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Støtter 50+ varslingssystemer)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "Bruker-nøkkel", + "Device": "Enhet", + "Message Title": "Meldingstittel", + "Notification Sound": "Notifikasjonslyd", + "More info on:": "Mer info på: {0}", + "pushoverDesc1": "Nødsprioritet (2) har en standard 30 sekunders tidsavbrudd mellom forsøk og vil utløpe etter 1 time.", + "pushoverDesc2": "Hvis du vil sende varsler til forskjellige enheteter, fyll ut Enhet-feltet.", + "SMS Type": "SMS Type", + "octopushTypePremium": "Premium (Raskt - anbefalt for varsling)", + "octopushTypeLowCost": "Lav kostnad (Sakte, noen ganger blokkert av leverandør)", + "Check octopush prices": "Sjekk octopush priser {0}.", + "octopushPhoneNumber": "Telefonnummer (internasjonalt format, f.eks.: +33612345678)", + "octopushSMSSender": "SMS Avsendernavn : 3-11 alphanumeriske tegn og mellomrom (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Enhet ID", + "Apprise URL": "Apprise URL", + "Example:": "Eksempel: {0}", + "Read more:": "Les mer: {0}", + "Status:": "Status: {0}", + "Read more": "Les mer", + "appriseInstalled": "Apprise er installert.", + "appriseNotInstalled": "Apprise er ikke installert. {0}", + "Access Token": "Tilgangs-Token", + "Channel access token": "Kanal tilgangs-token", + "Line Developers Console": "Line Utviklserskonsoll", + "lineDevConsoleTo": "Line Utviklserskonsoll - {0}", + "Basic Settings": "Grunnleggende instillinger", + "User ID": "Bruker-ID", + "Messaging API": "Meldings-API", + "wayToGetLineChannelToken": "Først, få tilgang til {0}, lag en leverandør og kanal (Meldings-API), deretter kan du hente kanaltilgangs-token og bruker id fra menu-valgene nevnt over.", + "Icon URL": "Ikon URL", + "aboutIconURL": "Du kan gi en link til et bilde i \"Ikon URL\" for å overskrive det standard profilbildet. Vil ikke bli brukt hvis Ikon Emoji ikke er satt.", + "aboutMattermostChannelName": "Du kan overskrive standardkanalen som webhook-en poster i ved å skrive enn kanalnavnet i \"Kanalnavn\" feltet. Dette må være skrudd på i Mattermost webhook-instillingene. Eks: #other-channel", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - billig, men treg og ofte overbelastet. Begrenset til bare polske mottakere.", + "promosmsTypeFlash": "SMS FLASH - Melding vil automatisk vises på mottakker-enhet. Begrenset til bare polske mottakere.", + "promosmsTypeFull": "SMS FULL - Premuimnivå SMS. Du kan bruke dit avsendernavn (Du må registerere et navn først). Pålitelig for alle varslinger.", + "promosmsTypeSpeed": "SMS SPEED - Høyest prioritet i systemet.Veldig rask på pålitelig, men dyrt (omtrent det dobbeltet av SMS FULL pris).", + "promosmsPhoneNumber": "Telefonnummber (for polske mottakere. Du trenger ikke områdekode.)", + "promosmsSMSSender": "SMS Avsendernavn : Forhåndsregistert navn eller en av standardnavnene: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Help": "Hjelp", + "Game": "Spill", + "statusMaintenance": "Vedlikehold", + "Maintenance": "Vedlikehold", + "Passive Monitor Type": "Passiv monitortype", + "Specific Monitor Type": "Spesifikk monitortype", + "General Monitor Type": "Generell monitortype", + "markdownSupported": "Markdown-syntaks støttes. Hvis du bruker HTML, unngå innledende mellomrom for å forhindre formateringsproblemer.", + "Resend Notification if Down X times consecutively": "Send varsel på nytt dersom nede X antall ganger på rad", + "Saved.": "Lagret.", + "programmingLanguages": "Programmeringsspråk", + "steamApiKeyDescription": "For å overvåke en Steam-spillserver trenger du en Steam Web-API-nøkkel. Du kan registrere API-nøkkelen din her: ", + "recent": "Nylig", + "Pick Accepted Status Codes...": "Velg akseptere statuskoder…", + "Description": "Beskrivelse", + "signedInDisp": "Logget inn som {0}", + "2faEnabled": "2FA aktivert.", + "authUserInactiveOrDeleted": "Denne brukeren er inaktiv eller slettet.", + "High": "Høy", + "smtpDkimSettings": "DKIM Innstillinger", + "settingUpDatabaseMSG": "Setter opp databasen. Dette kan ta sin tid, vær tålmodig.", + "Schedule maintenance": "Planlegg vedlikehold", + "Affected Monitors": "Berørte monitorer", + "Start of maintenance": "Vedlikeholdsstart", + "All Status Pages": "Alle statussider", + "Select status pages...": "Velg statussider…", + "filterActive": "Aktiv", + "Custom": "Egendefinert", + "statusPageRefreshIn": "Oppfrisk om: {0}", + "templateMsg": "meldingen i varselet", + "templateLimitedToUpDownCertNotifications": "kun tilgjengelig for OPP/NED/sertifikatutløpsvarsler", + "templateLimitedToUpDownNotifications": "kun tilgjengelig for OPP/NED varsler", + "Method": "Metode", + "PasswordsDoNotMatch": "Passordene stemmer ikke overens.", + "Default": "Standard", + "HTTP Options": "HTTP valg", + "primary": "primær", + "Title": "Tittel", + "light": "lys", + "dark": "mørk", + "Customize": "Tilpass", + "Content": "Innhold", + "Style": "Stil", + "info": "info", + "warning": "advarsel", + "Custom Footer": "Egendefinert bunntekst", + "danger": "fare", + "error": "feil", + "critical": "kritisk", + "deleteStatusPageMsg": "Er du sikker på at du vil slette denne status siden?", + "Proxies": "Proxyer", + "default": "Standard", + "enabled": "Aktivert", + "setAsDefault": "Sett som standard", + "Optional": "Valgfri", + "sameAsServerTimezone": "Samme som server tidssone", + "startDateTime": "Start dag/tid", + "weekdayShortMon": "Man", + "weekdayShortTue": "Tirs", + "recurringInterval": "Intervall", + "Recurring": "Gjentakende", + "affectedStatusPages": "Vis denne vedlikeholdsmeldingen på valgte statussider", + "Economy": "Økonomi", + "Lowcost": "Lavkost", + "Base URL": "Grunnleggende URL", + "PhoneNumbers": "Telefonnummer", + "Retry": "Prøv igjen", + "Topic": "Emne", + "Add a domain": "Legg til domene", + "documentation": "dokumentasjon", + "smtpDkimDomain": "domenenavn", + "Expiry": "Utløp", + "dnsPortDescription": "DNS-serverport. Standard er 53. Du kan endre porten når som helst.", + "deleteMaintenanceMsg": "Er du sikker på at du vil slette vedlikeholdsperioden?", + "do nothing": "gjør ingenting", + "alertaApiEndpoint": "API endepunkt", + "alertaEnvironment": "Miljø", + "alertaApiKey": "API nøkkel", + "serwersmsPhoneNumber": "telefonnummer", + "successAuthChangePassword": "Passordet har blitt oppdatert.", + "recurringIntervalMessage": "Kjør en gang daglig | Kjør en gang hver {0} dag", + "tcp": "TCP / HTTP", + "setupDatabaseChooseDatabase": "Hvilken database vil du bruke?", + "resendDisabled": "Sende på nytt deaktivert", + "setupDatabaseMariaDB": "Koble til en ekstern MariaDB-database. Du må angi tilkoblingsinformasjon for databasen.", + "setupDatabaseSQLite": "En enkel databasefil, anbefalt for småskala-installasjoner. I versjoner tidligere enn v2.0.0 brukte Uptime Kuma SQLite som standarddatabase.", + "needPushEvery": "Du skal forsøke å nå denne URLen hvert {0] sekund(er).", + "dbName": "Databasenavn", + "resendEveryXTimes": "Send på nytt hver {0} gang(er)", + "Push URL": "Push-URL", + "pushOptionalParams": "Valgfrie parametere: {0}", + "pushOthers": "Andre", + "styleElapsedTimeShowNoLine": "Vis (Ingen linje)", + "styleElapsedTimeShowWithLine": "Vis (Med linje)", + "endDateTime": "Slutt dag/tid", + "Current User": "Nåværende bruker", + "Reset Token": "Nullstill token", + "Done": "Ferdig", + "Info": "Info", + "Security": "Sikkerhet", + "Steam API Key": "Steam API nøkkel", + "Shrink Database": "Forminsk database", + "Create Incident": "Opprett hendelse", + "Please input title and content": "Vennligst skriv inn tittel og innhold", + "Created": "Opprettet", + "Last Updated": "Sist oppdatert", + "Unpin": "Avmerk", + "Switch to Light Theme": "Bytt til Lyst tema", + "Switch to Dark Theme": "Bytt til Mørkt tema", + "Show Tags": "Vis tagger", + "Hide Tags": "Skjul tagger", + "Untitled Group": "Tittelløs gruppe", + "Services": "Tjenester", + "Discard": "Forkast", + "Cancel": "Avbryt", + "Select": "Velg", + "selectedMonitorCount": "Valgt: {0}", + "Powered by": "Drevet av", + "Custom CSS": "Egendefinert CSS", + "About": "Om", + "signedInDispDisabled": "Autentisering deaktivert.", + "Coming Soon": "Kommer snart", + "Connection String": "Tilkoblingsstreng", + "Connection Type": "Tilkoblingstype", + "Docker Daemon": "Docker-daemon", + "Domain": "Domene", + "Workstation": "Arbeidsstasjon", + "Packet Size": "Pakke størrelse", + "Examples": "Eksempler", + "weekdayShortWed": "Ons", + "weekdayShortThu": "Tors", + "weekdayShortFri": "Fre", + "weekdayShortSat": "Lør", + "weekdayShortSun": "Søn", + "Home Assistant URL": "Home Assistant URL", + "Notification Service": "Notifikasjons tjeneste", + "default: notify all devices": "standard:varsle alle enheter", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "En liste over varslingstjenester finner du i Home Assistant under «Utviklerverktøy > Tjenester»-søk etter «varsling» for å finne navnet på enheten/telefonen din.", + "Event data:": "Hendelsesdata:", + "Schedule Maintenance": "Planlegg vedlikehold", + "Edit Maintenance": "Endre vedlikehold", + "Date and Time": "Dato og tid", + "2faDisabled": "2FA deaktivert.", + "Query": "Spørring", + "Domain Names": "Domenenavn", + "Trigger type:": "Trigger type:", + "Event type:": "Hendelsestype:", + "wayToGetClickSendSMSToken": "Du kan få API-brukernavn og API-nøkkel fra {here}.", + "Expiry date": "Utløpsdato", + "Don't expire": "Ikke utløp", + "Continue": "Fortsett", + "Add Another": "Legg til en annen", + "Huawei": "Huawei", + "Request Timeout": "Tidsavbrudd på forespørsel", + "timeoutAfter": "Timeout etter {0} sekunder", + "or": "eller", + "filterActivePaused": "Pauset", + "Add New Tag": "Legg til ny etikett", + "Certificate Chain": "Sertifikatkjede", + "Valid": "Gyldig", + "Invalid": "Ugyldig", + "User": "Bruker", + "Installed": "Installert", + "Not installed": "Ikke installert", + "Remove Token": "Fjern nøkkel", + "Start": "Start", + "Add New Status Page": "Legg til ny status side", + "Accept characters:": "Akseptere karakterer:", + "Next": "Neste", + "Authentication": "Autentisering", + "New Status Page": "Ny status side", + "Page Not Found": "Siden ble ikke funnet", + "Backup": "Sikkerhetskopi", + "cloudflareWebsite": "Cloudflare sin side", + "Other Software": "Annen programvare", + "Valid To:": "Gyldig til:", + "Days Remaining:": "Gjenværende dager:", + "Issuer:": "Utsteder:", + "Fingerprint:": "Fingeravtrykk:", + "No status pages": "Ingen status sider", + "Date Created": "Dato opprettet", + "Message:": "Melding:", + "API Username": "API brukernavn", + "API Key": "API nøkkel", + "Show update if available": "Vis oppdatering hvis en er tilgjengelig", + "Also check beta release": "Sjekk også beta utgaven", + "Steam Game Server": "Steam Spill server", + "The resource is no longer available.": "Denne ressursen er ikke lengere tilgjengelig.", + "Most likely causes:": "Mest sannsynlige årsaker:", + "dayOfWeek": "Ukedag", + "lastDay": "Siste dag", + "No Maintenance": "Ingen vedlikehold", + "pauseMaintenanceMsg": "Er du sikker på at du vil pause?", + "maintenanceStatus-under-maintenance": "Under vedlikehold", + "maintenanceStatus-inactive": "Inaktiv", + "maintenanceStatus-scheduled": "Planlagt", + "maintenanceStatus-ended": "Avsluttet", + "maintenanceStatus-unknown": "Ukjent", + "Display Timezone": "Vis tidssone", + "Server Timezone": "Server tidssone", + "statusPageMaintenanceEndDate": "Slutt", + "IconUrl": "Ikon URL", + "dayOfMonth": "Dag i måneden", + "lastDay1": "Siste dag i måneden", + "Disable": "Deaktiver", + "serwersmsAPIPassword": "API Passord", + "Post": "Post", + "Stop": "Stopp", + "high": "høy", + "Expected Value": "Forventet verdi", + "Json Query": "Json Spørring", + "Primary Base URL": "Primær base-url", + "topic": "Emne", + "Add one": "Legg til en", + "Please read": "Vennligst les", + "Subject:": "Emne:", + "Home": "Hjem", + "Invert Keyword": "Inverter nøkkelord", + "Cannot connect to the socket server": "Kan ikke koble til socket-server", + "Reconnecting...": "Kobler til på nytt...", + "ntfyAuthenticationMethod": "Autentiserings metode", + "No API Keys": "Ingen API nøkler", + "apiKey-active": "Aktiv", + "Expires": "Utløper", + "Key Added": "Nøkkel lagt til", + "apiKeyAddedMsg": "API-nøkkelen din er lagt til. Vennligst noter det ned siden den ikke vises igjen.", + "Add API Key": "Legg til API nøkkel", + "disableAPIKeyMsg": "Er du sikker på at du vil deaktivere denne API nøkkelen?", + "deleteAPIKeyMsg": "Er du sikker på at du vil slette denne API nøkkelen?", + "ntfyPriorityHelptextAllEvents": "Alle hendelser blir sendt med høyeste prioritet", + "ntfyUsernameAndPassword": "Brukernavn og passord", + "pagertreeLow": "Lav", + "pagertreeMedium": "Medium", + "Container Name / ID": "Containernavn / ID", + "Docker Host": "Docker-vert", + "disableCloudflaredNoAuthMsg": "Du er i No Auth-modus, et passord er ikke nødvendig.", + "apiKey-inactive": "Inaktiv", + "lunaseaTarget": "Mål", + "Go back to the previous page.": "Gå tilbake til forrige side.", + "settingsCertificateExpiry": "TLS Sertifikat utløp", + "What you can try:": "Hva du kan prøve:", + "Retype the address.": "Skriv adressen på nytt.", + "Docker Hosts": "Docker-verter", + "wayToGetLineNotifyToken": "Du kan få en tilgangstoken fra {0}", + "Long-Lived Access Token": "Langlevd tilgangstoken", + "Generate": "Generer", + "pagertreeSilent": "Stille", + "pagertreeHigh": "Høy", + "pagertreeCritical": "Kritisk", + "pagertreeDoNothing": "Gjør ingenting", + "lunaseaDeviceID": "Enhets ID", + "lunaseaUserID": "Bruker ID", + "twilioAccountSID": "Konto SID", + "twilioApiKey": "Api nøkkel (valgfri)", + "twilioFromNumber": "Fra nummer", + "twilioToNumber": "Til nummer", + "Badge Color": "Merkefarge", + "Badge Preview": "Merke fremvisning", + "Badge URL": "Merke URL", + "Group": "Gruppe", + "Mechanism": "Mekanisme", + "Close": "Lukk", + "2faAlreadyEnabled": "2FA er allerede aktivert.", + "onebotSafetyTips": "For sikkerhetsskyld, må en aksess token settes", + "successDisabled": "Deaktivert.", + "successEnabled": "Aktivert.", + "FlashDuty Severity": "Alvorlighet", + "apiKey-expired": "Utløpt", + "confirmUninstallPlugin": "Er du sikker på at du vil avinstallere valgte plugin?", + "notificationRegional": "Regional", + "emailTemplateServiceName": "Tjenestenavn", + "emailTemplateHostnameOrURL": "Hostnavn eller URL", + "emailTemplateStatus": "Status", + "uninstall": "Avinstaller", + "uninstalling": "Avinstallerer", + "There might be a typing error in the address.": "Det kan være en skrivefeil i adressen.", + "setupDatabaseEmbeddedMariaDB": "Du trenger ikke å konfigurere noe. Dette Docker-imaget har innebygd og ferdig konfigurert MariaDB for deg automatisk. Uptime Kuma vil koble til denne databasen via Unix-socket.", + "liquidIntroduction": "Malmuligheter oppnås ved hjelp av Liquid-malspråket. Se {0} for bruksanvisning.", + "webhookAdditionalHeadersDesc": "Angir ekstra headere sendt sammen med webhooken. Hver header bør defineres som et JSON nøkkel/verdi-par.", + "HeadersInvalidFormat": "Request headernene er ikke gyldig JSON: ", + "successKeywordExplanation": "MQTT-nøkkelord som vil anses som gyldig", + "Pick Affected Monitors...": "Velg berørte overvåkere…", + "Search monitored sites": "Søk på overvåkede nettsteder", + "templateHeartbeatJSON": "objekt som beskriver hjerteslaget", + "templateMonitorJSON": "objekt som beskriver monitoren", + "webhookAdditionalHeadersTitle": "Ekstra headere", + "webhookBodyPresetOption": "Forhåndsvalg - {0}", + "webhookBodyCustomOption": "Egendefinert brødtekst", + "Body": "Brødtekst", + "Headers": "Headere", + "PushUrl": "Push-URL", + "BodyInvalidFormat": "Teksten i forespørselen er ikke gyldig JSON: ", + "Monitor History": "Overvåkningshistorikk", + "clearDataOlderThan": "Behold overvåkningsdata i {0} dager.", + "records": "oppføringer", + "One record": "En oppføring", + "topicExplanation": "MQTT-emne å overvåke", + "successKeyword": "Nøkkelord for gyldig respons", + "now": "nå", + "time ago": "{0} siden", + "Json Query Expression": "Json-spørring", + "locally configured mail transfer agent": "lokalt konfigurert mailoverføringsagent", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Skriv inn vertsnavnet til serveren du vil koble til, eller {localhost} hvis du har tenkt å bruke en {local_mta}", + "ignoreTLSErrorGeneral": "Ignorer TLS/SSL-feil for tilkobling", + "ignoredTLSError": "TLS/SSL-feil har blitt ignorert", + "styleElapsedTime": "Medgått tid udner hjerteslagslinjen", + "Host URL": "VertsURL", + "Monitor": "Monitor | Monitorer", + "-year": "-år", + "pushViewCode": "Hvordan bruke Push-overvåkning? (Se kode)", + "Path": "Sti", + "mqttWebSocketPath": "MQTT WebSocket Sti", + "mqttWebsocketPathExplanation": "WebSocket stifor MQTT over WebSocket forbindelser(feks., /mqtt)", + "Pick a RR-Type...": "Velg en RR-Type…", + "deleteProxyMsg": "Er du sikker på at du vil sltte thenne proxyen for alle monitorer?", + "Show Powered By": "Vis Powered By", + "Trust Proxy": "Stol på proxy", + "defaultFriendlyName": "Ny", + "No Proxy": "Ingen Proxy", + "HTTP Basic Auth": "HTTP Basic Autentisering", + "Reverse Proxy": "Omvendt proxy", + "HTTP Headers": "HTTP-headere", + "For example: nginx, Apache and Traefik.": "Foreksempel: nginx, Apache and Traefik.", + "Domain Name Expiry Notification": "Domenenavn utløps varsel", + "Add a new expiry notification day": "Legg til en utløsdato", + "Remove the expiry notification": "Fjern utlpsdatao", + "Proxy": "Proxy", + "Footer Text": "Bunntekst", + "Refresh Interval": "Oppfriskningsinterval", + "Add Tags": "Legg til etiketter", + "No monitors available.": "Ingen monitorer er tilgjengelig.", + "templateServiceName": "tjenestenavn", + "templateHostnameOrURL": "hostname og URL", + "templateStatus": "status", + "No Monitors": "Ingen overvåkere", + "supportBaleChatID": "Støtter direkte chat / gruppe / kanalens chat-ID", + "wayToGetBaleChatID": "Du kan få chat-ID-en din ved å sende en melding til boten og gå til denne URL-en for å se chat_id-en:", + "wayToGetBaleToken": "Du kan få et token fra {0}.", + "and": "og", + "chromeExecutable": "Chrome/Chromium-kjørbar fil", + "chromeExecutableAutoDetect": "Automatisk oppdaging", + "dataRetentionTimeError": "Bevaringsperioden må være 0 eller høyere", + "mqttWebsocketPathInvalid": "Vennligst bruk et gyldig format for WebSocket-sti", + "mqttHostnameTip": "Vennligst bruk dette formatet {hostnameFormat}", + "shrinkDatabaseDescriptionSqlite": "Utløs databasekommandoen {vacuum} for SQLite. {auto_vacuum} er allerede aktivert, men dette defragmenterer ikke databasen eller ompakker individuelle database­sider slik {vacuum}-kommandoen gjør.", + "Enable DNS Cache": "(Utdatert) Aktiver DNS-buffer for HTTP(s)-overvåkere", + "Enable": "Aktiver", + "descriptionHelpText": "Vises på det interne dashbordet. Markdown er tillatt og renses (bevarer mellomrom og innrykk) før visning.", + "proxyDescription": "Proxyer må tilordnes en overvåking for å fungere.", + "enableProxyDescription": "Denne proxyen vil ikke påvirke overvåkingsforespørsler før den er aktivert. Du kan midlertidig deaktivere proxyen for alle overvåkninger ved å endre aktiveringsstatus.", + "setAsDefaultProxyDescription": "Denne proxyen vil være aktivert som standard for nye overvåkninger. Du kan fortsatt deaktivere proxyen separat for hver overvåking.", + "Running": "Kjører", + "Not running": "Kjører ikke", + "The slug is already taken. Please choose another slug.": "Slug-en er allerede tatt. Vennligst velg en annen slug.", + "Refresh Interval Description": "Statussiden vil utføre en full oppdatering av nettstedet hvert {0} sekund", + "Using a Reverse Proxy?": "Bruker du en omvendt proxy?", + "Check how to config it for WebSocket": "Sjekk hvordan du konfigurerer den for WebSocket", + "socket": "Sokkel", + "tailscalePingWarning": "For å bruke Tailscale Ping-overvåkeren må du installere Uptime Kuma uten Docker og også installere Tailscale-klienten på serveren din.", + "Docker Container": "Docker-beholder", + "telegramSendSilently": "Send stille", + "telegramProtectContentDescription": "Hvis aktivert, vil bot-meldinger i Telegram beskyttes mot videresending og lagring.", + "trustProxyDescription": "Stol på ‘X-Forwarded-*’-headere. Hvis du vil få riktig klient-IP og din Uptime Kuma er bak en proxy som Nginx eller Apache, bør du aktivere dette.", + "Automations can optionally be triggered in Home Assistant:": "Automatiseringer kan valgfritt utløses i Home Assistant:", + "Frontend Version": "Frontend-versjon", + "Frontend Version do not match backend version!": "Frontend-versjonen samsvarer ikke med backend-versjonen!", + "cronExpression": "Cron-uttrykk", + "cronSchedule": "Tidsplan: ", + "strategyManual": "Aktiv/Inaktiv manuelt", + "warningTimezone": "Den bruker serverens tidssone", + "lastDay2": "Nest siste dag i måneden", + "dnsCacheDescription": "Det kan hende det ikke fungerer i enkelte IPv6-miljøer, deaktiver det hvis du opplever problemer.", + "Single Maintenance Window": "Enkelt vedlikeholdsvindu", + "Maintenance Time Window of a Day": "Vedlikeholdstidsvindu for en dag", + "Effective Date Range": "Gyldig datoperiode (valgfritt)", + "loadingError": "Kan ikke hente data, vennligst prøv igjen senere.", + "plugin": "Programtillegg | Programtillegg", + "install": "Installer", + "installing": "Installerer", + "Clone": "Klon", + "cloneOf": "Kopi av {0}", + "emailCustomisableContent": "Tilpassbart innhold", + "emailCustomSubject": "Egendefinert emne", + "leave blank for default subject": "la stå tomt for standardemne", + "emailCustomBody": "Egendefinert brødtekst", + "leave blank for default body": "la stå tomt for standardtekst", + "emailTemplateMonitorJSON": "objekt som beskriver overvåkeren", + "emailTemplateHeartbeatJSON": "objekt som beskriver pulssignalet", + "emailTemplateMsg": "melding fra varslingen", + "emailTemplateLimitedToUpDownNotification": "kun tilgjengelig for OPP/NED-pulssignaler, ellers null", + "Send to channel": "Send til kanal", + "postToExistingThread": "Post til eksisterende tråd / foruminnlegg", + "forumPostName": "Navn på foruminnlegg", + "Your User ID": "Din bruker-ID", + "infiniteRetention": "Sett til 0 for ubegrenset bevaring.", + "confirmDeleteTagMsg": "Er du sikker på at du vil slette denne taggen? Overvåkere som er tilknyttet denne taggen vil ikke bli slettet.", + "enableGRPCTls": "Tillat å sende gRPC-forespørsel med TLS-tilkobling", + "telegramServerUrl": "(Valgfritt) Server-URL", + "telegramServerUrlDescription": "For å fjerne Telegrams bot-API-begrensninger eller få tilgang i blokkerte områder (Kina, Iran, osv.). For mer informasjon klikk {0}. Standard: {1}", + "Slug": "Slug", + "startOrEndWithOnly": "Start eller slutt kun med {0}", + "No consecutive dashes": "Ingen påfølgende bindestreker", + "statusPageSpecialSlugDesc": "Spesiell slug {0}: denne siden vises når ingen slug er angitt", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Den nåværende tilkoblingen kan gå tapt hvis du for øyeblikket er koblet til via Cloudflare Tunnel. Er du sikker på at du vil stoppe den? Skriv inn ditt nåværende passord for å bekrefte.", + "RadiusCallingStationIdDescription": "Identifikator for den oppringende enheten", + "telegramMessageThreadID": "(Valgfritt) Meldings-tråd-ID", + "telegramMessageThreadIDDescription": "Valgfri unik identifikator for målmeldings-tråden (emnet) i forumet; kun for forum-supergrupper", + "telegramTemplateFormatDescription": "Telegram tillater bruk av forskjellige oppmerkingsspråk for meldinger, se Telegram {0} for spesifikke detaljer.", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Velg deretter en handling, for eksempel å bytte scenen til der et RGB-lys er rødt.", + "backupOutdatedWarning": "Utdatert: Siden mange funksjoner har blitt lagt til og denne sikkerhetskopifunksjonen er lite vedlikeholdt, kan den ikke generere eller gjenopprette en fullstendig sikkerhetskopi.", + "invalidCronExpression": "Ugyldig Cron-uttrykk: {0}", + "enableNSCD": "Aktiver NSCD (Name Service Cache Daemon) for å bufre alle DNS-forespørsler", + "chromeExecutableDescription": "For Docker-brukere, hvis Chromium ikke er installert ennå, kan det ta noen minutter å installere og vise testresultatet. Det krever 1 GB diskplass.", + "Clone Monitor": "Klon overvåker", + "Use HTML for custom E-mail body": "Bruk HTML for egendefinert e-posttekst", + "smtpLiquidIntroduction": "De to følgende feltene kan brukes som maler via Liquid-malspråket. Se {0} for bruksinstruksjoner. Dette er de tilgjengelige variablene:", + "Create new forum post": "Opprett nytt foruminnlegg", + "wayToGetDiscordThreadId": "Å hente en tråd- / foruminnlegg-ID ligner på å hente en kanal-ID. Les mer om hvordan du får ID-er {0}", + "wayToGetCloudflaredURL": "(Last ned cloudflared fra {0})", + "Don't know how to get the token? Please read the guide:": "Vet du ikke hvordan du får tokenet? Vennligst les guiden:", + "RadiusSecret": "Radius-hemmelighet", + "RadiusSecretDescription": "Delt hemmelighet mellom klient og server", + "RadiusCalledStationId": "Kalt stasjons-ID", + "RadiusCalledStationIdDescription": "Identifikator for den oppringte enheten", + "RadiusCallingStationId": "Oppringende stasjons-ID", + "telegramUseTemplate": "Bruk egendefinert meldingsmal", + "telegramUseTemplateDescription": "Hvis aktivert, vil meldingen bli sendt ved bruk av en egendefinert mal.", + "backupRecommend": "Vennligst sikkerhetskopier volumet eller datamappen (./data/) direkte i stedet.", + "lastDay3": "Tredje siste dag i måneden", + "lastDay4": "Fjerde siste dag i måneden", + "Channel access token (Long-lived)": "Kanaltilgangstoken (langvarig)", + "tagAlreadyOnMonitor": "Denne taggen (navn og verdi) er allerede på overvåkningen eller venter på å bli lagt til.", + "tagAlreadyStaged": "Denne taggen (navn og verdi) er allerede klargjort for denne batchen.", + "tagNameExists": "Et systemmerke med dette navnet finnes allerede. Velg det fra listen, eller bruk et annet navn.", + "Check/Uncheck": "Merk/Fjern merking", + "Certificate Expiry Notification": "Varsel om sertifikatutløp", + "deleteDockerHostMsg": "Er du sikker på at du vil slette denne Docker-verten for alle overvåkere?", + "Select message type": "Velg meldingstype", + "auto-select": "Automatisk valg", + "certificationExpiryDescription": "HTTPS-overvåkere utløser varsling når TLS-sertifikatet utløper om:", + "Setup Docker Host": "Sett opp Docker-vert", + "noDockerHostMsg": "Ikke tilgjengelig. Sett opp en Docker-vert først.", + "DockerHostRequired": "Vennligst angi Docker-verten for denne overvåkeren.", + "telegramSendSilentlyDescription": "Sender meldingen stille. Brukere vil motta et varsel uten lyd.", + "telegramProtectContent": "Beskytt videresending/lagring", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Langtids-tilgangstoken kan opprettes ved å klikke på profilnavnet ditt (nederst til venstre), bla til bunnen og deretter klikke på Opprett token.", + "DateTime Range": "Dato- og tidsområde", + "threadForumPostID": "Tråd- / foruminnlegg-ID", + "e.g. {discordThreadID}": "f.eks. {discordThreadID}", + "whatHappensAtForumPost": "Opprett et nytt foruminnlegg. Dette legger IKKE ut meldinger i et eksisterende innlegg. For å poste i et eksisterende innlegg, bruk \"{option}\"", + "wayToGetZohoCliqURL": "Du kan lære hvordan du oppretter en webhook-URL {0}.", + "days": "{n} dag | {n} dager", + "hours": "{n} time | {n} timer", + "minutes": "{n} minutt | {n} minutter", + "years": "{n} år | {n} år", + "enableSSL": "Aktiver SSL/TLS", + "mariadbUseSSLHelptext": "Aktiver for å bruke en kryptert tilkobling til databasen din. Påkrevd for de fleste skydatabaser.", + "mariadbCaCertificateLabel": "CA-sertifikat", + "Ignore Sec-WebSocket-Accept header": "Ignorer {0}-header", + "wsSubprotocolDescription": "Skriv inn en kommaseparert liste over underprotokoller. For mer informasjon om underprotokoller, se {documentation}", + "minuteShort": "{n} min | {n} min", + "Monitors": "{n} overvåking | {n} overvåkninger", + "mariadbCaCertificateHelptext": "Lim inn CA-sertifikatet i PEM-format for bruk med selvsignerte sertifikater. La stå tomt hvis databasen din bruker et sertifikat signert av en offentlig CA.", + "unknownDays": "Ukjente dager", + "ignoreSecWebsocketAcceptHeaderDescription": "Tillater at serveren ikke svarer med Sec-WebSocket-Accept-header, hvis WebSocket-oppgraderingen lykkes.", + "saveResponseForNotifications": "Lagre HTTP-suksessrespons for varsler", + "saveErrorResponseForNotifications": "Lagre HTTP-feilrespons for varsler", + "saveResponseDescription": "Lagrer HTTP-responsen og gjør den tilgjengelig for varslingsmaler som {templateVariable}", + "wsCodeDescription": "For mer informasjon om statuskoder, se {rfc6455}", + "Subprotocol(s)": "Underprotokoll(er)", + "versionIs": "Versjon: {version}", + "Only retry if status code check fails": "Prøv kun på nytt hvis statuskodekontrollen mislykkes", + "retryOnlyOnStatusCodeFailureDescription": "Hvis aktivert, vil gjenforsøk kun skje når kontrollen av HTTP-statuskode mislykkes (f.eks. serveren er nede). Hvis statuskodekontrollen lykkes, men JSON-spørringen mislykkes, vil overvåkingen bli markert som nede umiddelbart uten gjenforsøk.", + "Load More": "Last inn mer", + "Loading...": "Laster...", + "Pin this incident": "Fest denne hendelsen", + "No incidents recorded": "Ingen hendelser registrert", + "Resolver Server(s)": "Resolver-server(e)", + "steamApiKeyDescriptionAt": "For å overvåke en Steam-spillserver trenger du en Steam Web-API-nøkkel. Du kan registrere API-nøkkelen din på {url}", + "invalidURL": "Ugyldig URL", + "Use STARTTLS": "Bruk STARTTLS", + "selectAllMonitorsAria": "Velg alle overvåkere", + "Actions": "Handlinger", + "deselectAllMonitorsAria": "Fjern markering fra alle overvåkere", + "grpcMethodDescription": "Metodenavnet skal konverteres til camelCase-format, for eksempel sayHello, check, osv.", + "responseMaxLengthDescription": "Maksimal størrelse på responsdata som skal lagres. Sett til 0 for ubegrenset. Større responser vil bli forkortet. Standard: 1024 (1 KB)", + "deleteMonitorsMsg": "Er du sikker på at du vil slette de valgte overvåkere?", + "responseMaxLength": "Maks responslengde (byte)", + "selectedMonitorCountMsg": "valgt: {n} | valgt: {n}", + "selectMonitorMsg": "Velg overvåkere for å utføre handlinger", + "Ignore STARTTLS": "Ignorer STARTTLS", + "deletedMonitorsMsg": "Slettet {n} overvåker | Slettet {n} overvåkere", + "HeadersInvalidFormatBecause": "Forespørselsheaderne er ikke gyldig JSON på grunn av {error}", + "BodyInvalidFormatBecause": "Forespørselsinnholdet er ikke gyldig JSON på grunn av {error}", + "Leave blank to use status page title": "La stå tomt for å bruke statussidens tittel", + "SMTP Security": "SMTP-sikkerhet", + "pausedMonitorsMsg": "Pause {n} overvåker | Pause {n} overvåkere", + "Disable STARTTLS": "Deaktiver STARTTLS", + "disableSTARTTLSDescription": "Aktiver dette alternativet for SMTP-servere som ikke støtter STARTTLS. Dette vil sende e-poster over en ukryptert tilkobling.", + "resumedMonitorsMsg": "Gjenopptatt {n} overvåker | Gjenopptatt {n} overvåkere", + "certHostnameMismatch": "Sertifikatets vertsnavn samsvarer ikke med overvåkings-URLen.", + "RSS Title": "RSS-tittel", + "lastUpdatedAt": "Sist oppdatert: {date}", + "logoutCurrentUser": "Logg ut {username}", + "createdAt": "Opprettet: {date}", + "lastUpdatedAtFromNow": "Sist oppdatert: {date} ({fromNow})", + "Certificate Chain:": "Sertifikatkjede:", + "dateCreatedAtFromNow": "Opprettet dato: {date} ({fromNow})", + "frontendVersionIs": "Frontend-versjon: {version}", + "Examples:": "Eksempler: {0}", + "cronScheduleDescription": "Plan: {description}", + "ariaDeleteMaintenance": "Slett denne vedlikeholdsplanen", + "sipsakPingWarning": "For å bruke SIP Options Ping-overvåkingen, må du installere Uptime Kuma uten Docker og også installere Sipsak-klienten på serveren din.", + "Duration (Minutes)": "Varighet (minutter)", + "notificationUniversal": "Universell", + "notificationChatPlatforms": "Chat-plattformer", + "notificationPushServices": "Push-tjenester", + "notificationSmsServices": "SMS-tjenester", + "notificationEmail": "E-post", + "notificationIncidentManagement": "Hendelseshåndtering", + "notificationHomeAutomation": "Hjemmeautomatisering", + "notificationOther": "Andre integrasjoner", + "invalidHostnameOrIP": "Ugyldig vertsnavn eller IP. Vertsnavnet må være et gyldig FQDN. Kan ikke bruke jokertegn. Kan inneholde understrek eller slutte med punktum.", + "invalidDNSHostname": "Ugyldig vertsnavn. Vertsnavnet må være et gyldig FQDN. Kan være et jokertegn, inneholde understrek eller slutte med punktum.", + "wildcardOnlyForDNS": "Jokertegn-vertsnavn støttes kun for DNS-overvåkere.", + "hostnameCannotBeIP": "DNS-vertsnavn kan ikke være en IP-adresse. Mente du å bruke resolver-feltet?", + "ariaEditMaintenance": "Rediger denne vedlikeholdsplanen", + "Clone Maintenance": "Klon vedlikehold", + "ariaPauseMaintenance": "Sett dette vedlikeholdsplanen på pause", + "ariaResumeMaintenance": "Gjenoppta denne vedlikeholdsplanen", + "ariaCloneMaintenance": "Opprett en kopi av denne vedlikeholdsplanen", + "Incident description": "Hendelsesbeskrivelse", + "Incident not found or access denied": "Hendelse ikke funnet eller tilgang nektet", + "Past Incidents": "Tidligere hendelser", + "Incident title": "Hendelsestittel", + "Pinned incidents are shown prominently on the status page": "Festede hendelser vises tydelig på status-siden", + "Edit Incident": "Rediger hendelse", + "Resolve": "Løs", + "Resolved": "Løst", + "deleteIncidentMsg": "Er du sikker på at du vil slette denne hendelsen?", + "slug is not found": "Slug finnes ikke", + "Please input content": "Vennligst skriv inn innhold", + "Please input title": "Vennligst skriv inn tittel", + "Sets end time based on start time": "Setter sluttid basert på starttid", + "Please set start time first": "Vennligst sett starttid først", + "promosmsLogin": "API-påloggingsnavn", + "pushoversounds cashregister": "Kassesystem", + "octopushLogin": "«Pålogging» fra HTTP API-legitimasjon i kontrollpanelet", + "pushoversounds classical": "Klassisk", + "pushoversounds siren": "Sirene", + "pushyAPIKey": "Hemmelig API-nøkkel", + "aboutSlackUsername": "Endrer visningsnavnet til meldingsavsenderen. Hvis du vil nevne noen, inkluder det i det vennlige navnet i stedet.", + "Proxy server has authentication": "Proxy-serveren krever autentisering", + "Google Apps Script Webhook URL": "Google Apps Script Webhook-URL", + "Deploy a Google Apps Script as a web app and paste the URL here": "Distribuer et Google Apps Script som et nettprogram og lim inn URL-en her", + "Quick Setup Guide": "Rask oppsettsveiledning", + "Open your Google Spreadsheet": "Åpne Google-regnearket ditt", + "Go to Extensions → Apps Script": "Gå til Utvidelser → Apps Script", + "Paste the script code (see below)": "Lim inn skriptkoden (se nedenfor)", + "Click Deploy → New deployment → Web app": "Klikk Distribuer → Ny distribusjon → Nettapp", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Sett 'Utfør som: Meg' og 'Hvem har tilgang: Alle'", + "Copy the web app URL and paste it above": "Kopier nettapp-URL-en og lim den inn ovenfor", + "Google Apps Script Code": "Google Apps Script-kode", + "Copy to Clipboard": "Kopier til utklippstavle", + "Copied to clipboard!": "Kopiert til utklippstavle!", + "Failed to copy to clipboard": "Kunne ikke kopiere til utklippstavle", + "WeCom Mentioned Mobile List": "WeCom nevnte mobilnummerliste", + "WeCom Mentioned Mobile List Description": "Skriv inn telefonnumre som skal nevnes. Skill flere numre med komma. Bruk {'@'}all for å nevne alle.", + "noMonitorsPausedMsg": "Ingen overvåkere er satt på pause (ingen var aktive)", + "noMonitorsResumedMsg": "Ingen overvåkere har gjenopptatt (ingen var inaktive)", + "wayToGetKookGuildID": "Slå på «Utviklermodus» i Kook-innstillingene, og høyreklikk på serveren for å få ID-en dens", + "Guild ID": "Server-ID", + "checkPriceAt": "Sjekk {service}-priser på {url}", + "Proxy Server": "Proxy-server", + "openModalTo": "Åpne modal for {0}", + "smtpDkimDesc": "Se Nodemailer DKIM {0} for bruk.", + "Bark Endpoint": "Bark-endepunkt", + "invertKeywordDescription": "Se etter at nøkkelordet ikke er til stede i stedet for at det er til stede.", + "Free Mobile User Identifier": "Free Mobile-bruker-ID", + "Proto Service Name": "Proto-tjenestenavn", + "Proto Method": "Proto-metode", + "Enable TLS": "Aktiver TLS", + "Mention Mobile List": "Nevn mobilnummerliste", + "Could not clear events": "Kunne ikke fjerne {failed}/{total} hendelser", + "noMonitorsSelectedWarning": "Du oppretter et vedlikehold uten noen berørte overvåkere. Er du sikker på at du vil fortsette?", + "Free Mobile API Key": "Free Mobile API-nøkkel", + "Mention User List": "Nevn bruker-ID-liste", + "Dingtalk Mobile List": "Mobilnummerliste", + "Notify Channel": "Varslingskanal", + "Bark Group": "Bark-gruppe", + "bulkDeleteErrorMsg": "Kunne ikke slette {n} overvåker | Kunne ikke slette {n} overvåkere", + "affectedMonitorsDescription": "Velg overvåkere som påvirkes av nåværende vedlikehold", + "No monitors found": "Ingen overvåkere funnet.", + "pushoversounds climb": "Klatring (lang)", + "pushoversounds persistent": "Vedvarende (lang)", + "pushyToken": "Enhetstoken", + "aliyun-template-optional-parameters": "Valgfrie parametere: {parameters}", + "deleteGroupMsg": "Er du sikker på at du vil slette denne gruppen?", + "octopushAPIKey": "«API-nøkkel» fra HTTP API-legitimasjon i kontrollpanelet", + "pushoversounds none": "Ingen (stille)", + "Feishu WebHookUrl": "Feishu WebHook-URL", + "noMonitorsOrStatusPagesSelectedError": "Kan ikke opprette vedlikehold uten berørte overvåkere eller status-sider", + "jsonQueryDescription": "Analyser og hent ut spesifikke data fra serverens JSON-respons ved å bruke JSON-spørring, eller bruk \"$\" for råresponsen hvis du ikke forventer JSON. Resultatet sammenlignes deretter med forventet verdi som tekst. Se {0} for dokumentasjon og bruk {1} for å eksperimentere med spørringer.", + "pushoversounds alien": "Alienalarm (lang)", + "apiCredentials": "API-legitimasjon", + "Strategy": "Strategi", + "Proto Content": "Proto-innhold", + "You can divide numbers with commas or semicolons": "Du kan dele tall med {comma} eller {semicolon}", + "promosmsAllowLongSMS": "Tillat lange SMS", + "matrixDesc2": "Det anbefales sterkt å opprette en ny bruker og ikke bruke tilgangstokenet til din egen Matrix-bruker, siden dette gir full tilgang til kontoen din og alle rommene du har blitt med i. Opprett i stedet en ny bruker og inviter den kun til rommet der du ønsker å motta varsler. Du kan hente tilgangstokenet ved å kjøre {0}", + "Don't mention people": "Ikke nevn personer", + "Clear All Events": "Fjern alle hendelser", + "clearAllEventsMsg": "Er du sikker på at du vil slette alle hendelser for alle overvåkere? Dette vil permanent fjerne tilknyttede meldingsdata og kan ikke angres.", + "pushoversounds pushover": "Pushover (standard)", + "promosmsPassword": "API-passord", + "Mention group": "Nevn {group}", + "pushoverMessageTtl": "Meldingens TTL (sekunder)", + "aliyun-template-requirements-and-parameters": "Aliyun SMS-malen må inneholde parametrene: {parameters}", + "pushoversounds spacealarm": "Romalarm", + "matrixHomeserverURL": "Homeserver-URL (med http(s):// og valgfritt portnummer)", + "deleteChildrenMonitors": "Slett også de direkte underordnede overvåkerne og deres underordnede hvis de har noen | Slett også alle {count} direkte underordnede overvåkerne og deres underordnede hvis de har noen", + "Gateway Type": "Gateway-type", + "goAlertIntegrationKeyInfo": "Få generisk API-integrasjonsnøkkel for tjenesten i dette formatet \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\", vanligvis verdien av token-parameteren i den kopierte URL-en.", + "SecretAccessKey": "AccessKey-hemmelighet", + "SignName": "Signaturnavn", + "WebHookUrl": "WebHook-URL", + "Enter a list of mobile": "Skriv inn en liste med mobilnumre", + "SecretKey": "Hemmelig nøkkel", + "Enter a list of userId": "Skriv inn en liste med bruker-ID-er", + "Bark Sound": "Bark-lyd", + "pushoversounds falling": "Fallende", + "pushoversounds gamelan": "Gamelan", + "pushoversounds cosmic": "Kosmisk", + "pushoversounds incoming": "Innkommende", + "SendKey": "SendNøkkel", + "pushoversounds bike": "Sykkel", + "pushoversounds bugle": "Trompet", + "GoogleChat": "Google Chat (kun Google Workspace)", + "pushoversounds vibrate": "Kun vibrasjon", + "pushoversounds updown": "Opp Ned (lang)", + "octopushLegacyHint": "Bruker du den eldre versjonen av Octopush (2011–2020) eller den nye versjonen?", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "På grunn av operatørbegrensninger, aktiver valgfrie variabler på egen risiko for manglende levering", + "OptionalParameters": "Valgfrie parametere", + "matrixDesc1": "Du kan finne den interne rom-ID-en ved å se i avansert-seksjonen i rominnstillingene i Matrix-klienten din. Den skal se slik ut: !QMdRCpUIfLwsfjxye6:home.server.", + "aboutNotifyChannel": "Varslingskanalen vil utløse et skrivebords- eller mobilvarsel for alle medlemmer av kanalen, uavhengig av om tilgjengeligheten deres er satt til aktiv eller borte.", + "setup a new monitor group": "Opprett en ny overvåkingsgruppe", + "pushoversounds magic": "Magi", + "pushoversounds intermission": "Pause", + "Template plain text instead of using cards": "Bruk mal som ren tekst i stedet for kort", + "Events cleared successfully": "Hendelser fjernet vellykket.", + "pushoversounds mechanical": "Mekanisk", + "pushoversounds pianobar": "Pianobar", + "pushoversounds tugboat": "Dampskip", + "Bark API Version": "Bark API-versjon", + "Invalid mobile": "Ugyldig mobilnummer [{mobile}]", + "Invalid userId": "Ugyldig bruker-ID [{userId}]", + "Device Token": "Enhetstoken", + "pushoversounds echo": "Pushover-ekko (lang)", + "SMSManager API Docs": "SMSManager API-dokumentasjon", + "Dingtalk User List": "Bruker-ID-liste", + "Setup Proxy": "Sett opp proxy", + "Proxy Protocol": "Proxy-protokoll", + "WeCom Bot Key": "WeCom Bot-nøkkel", + "Remove domain": "Fjern domenet '{0}'", + "AccessKeyId": "AccessKey-ID", + "goAlertInfo": "GoAlert er en åpen kildekode-applikasjon for vaktplanlegging, automatiske eskaleringer og varsler (som SMS eller taleanrop). Engasjer automatisk riktig person, på riktig måte, og til rett tid! {0}", + "smtpDkimKeySelector": "Nøkkelvelger", + "issueWithGoogleChatOnAndroidHelptext": "Dette gjør det også mulig å omgå feil oppstrøms, som {issuetackerURL}", + "Mentioning": "Nevner", + "wayToGetKookBotToken": "Opprett en applikasjon og få bot-tokenet ditt på {0}", + "For safety, must use secret key": "For sikkerhetens skyld må du bruke hemmelig nøkkel", + "Platform": "Plattform", + "Internal Room Id": "Intern rom-ID", + "TemplateCode": "MalKode", + "auto resolve": "automatisk løst", + "pushDeerServerDescription": "La stå tomt for å bruke den offisielle serveren", + "ntfyPriorityHelptextAllExceptDown": "Alle hendelser sendes med denne prioriteten, unntatt {0}-hendelser som har prioritet {1}", + "Badge Label Suffix": "Merkeetikett-suffiks", + "wahaSession": "Sesjon", + "minPing": "Min ping", + "Sort by name": "Sorter etter navn", + "Notifications Enabled": "Varsler aktivert", + "Number of retry attempts if webhook fails": "Antall forsøk (hver 60-180 sekunder) hvis webhook feiler.", + "domain_expiry_unsupported_monitor_type": "Domeneutløp støttes ikke for denne monitortypen", + "snmpV3Username": "SNMPv3-brukernavn", + "minimumIntervalWarning": "Intervaller under 20 sekunder kan gi dårlig ytelse.", + "smsplanetNeedToApproveName": "Må godkjennes i kundeportalen", + "lowIntervalWarning": "Er du sikker på at du vil sette intervall under 20 sekunder? Ytelsen kan bli dårlig, spesielt med mange monitorer.", + "smseagleDocs": "Sjekk dokumentasjonen eller tilgjengelighet for APIv2: {0}", + "ntfyPriorityHelptextPriorityHigherThanDown": "Regulær prioritet bør være høyere enn {0}-prioritet. Prioritet {1} er høyere enn {0}-prioritet {2}", + "pagertreeIntegrationUrl": "Integrasjons-URL", + "Kafka SASL Options": "Kafka SASL-alternativer", + "Pick a SASL Mechanism...": "Velg en SASL-mekanisme…", + "receiverSevenIO": "Mottakende nummer", + "gtxMessagingFromHint": "På mobiler ser mottakere TPOA som avsender. Tillatt er opptil 11 alfanumeriske tegn, en kortkode, lokal longcode eller internasjonale numre ({e164}, {e212} eller {e214})", + "Originator type": "Avsendertype", + "Alphanumeric (recommended)": "Alfanumerisk (anbefalt)", + "Originator": "Avsender", + "Destination": "Destinasjon", + "Community String": "Community-streng", + "Client ID": "Klient-ID", + "OAuth Audience": "OAuth-målgruppe", + "Optional: The audience to request the JWT for": "Valgfritt: Målgruppe det skal forespørres JWT for", + "Custom URL": "Egendefinert URL", + "OneChatAccessToken": "OneChat-tilgangstoken", + "Authentication Method": "Autentiseringsmetode", + "RabbitMQ Password": "RabbitMQ-passord", + "Sender name": "Avsendernavn", + "Halo PSA": "Halo PSA", + "serwersmsRecipientTypeGroup": "Gruppe", + "serwersmsGroupId": "Gruppe-ID", + "remoteBrowsersDescription": "Fjernlesere er et alternativ til å kjøre Chromium lokalt. Sett opp med en tjeneste som browserless.io eller koble til din egen", + "GrafanaOncallUrl": "Grafana Oncall-URL", + "The phone number of the recipient in E.164 format.": "Mottakerens telefonnummer i E.164-format.", + "RabbitMQ Username": "RabbitMQ-brukernavn", + "Allow Notifications": "Tillat varsler", + "Browser not supported": "Nettleser støttes ikke", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Domeneutløp er ikke tilgjengelig for \".{publicSuffix}\" fordi ingen RDAP-tjeneste er listet av IANA", + "Basic radio toggle button group": "Grunnleggende radioknapp-brytergruppe", + "mtls-auth-server-ca-label": "CA", + "Splunk Rest URL": "Splunk REST-URL", + "From Name/Number": "Fra-navn/-nummer", + "systemServiceDescription": "Sjekker om systemtjenesten {service_name} er aktiv", + "systemServiceDescriptionLinux": "Sjekker om Linux systemd-tjenesten {service_name} er aktiv", + "cellsyntDestination": "Mottakers telefonnummer i internasjonalt format med ledende 00 og landkode, f.eks. 00447920110000 for britisk nummer 07920 110 000 (maks 17 tegn totalt). Maks 25000 mottakere per HTTP-forespørsel, skillt med komma.", + "threemaBasicModeInfo": "Merk: Denne integrasjonen bruker Threema Gateway i basic mode (serverbasert kryptering). Mer info finnes {0}.", + "conditionValuePlaceholder": "Verdi", + "not contains": "inneholder ikke", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Enten et tekst-avsender-ID eller et telefonnummer i E.164-format hvis du vil kunne motta svar.", + "rabbitmqNodesRequired": "Vennligst angi noder for denne monitoren.", + "SendGrid API Key": "SendGrid API-nøkkel", + "brevoFromEmail": "Fra e-post", + "pingNumericDescription": "Hvis avkrysset brukes IP-adresser i stedet for symbolske vertsnavn", + "OneChatUserIdOrGroupId": "OneChat bruker-ID eller gruppe-ID", + "wahaChatId": "Chat-ID (telefonnummer / kontakt-ID / gruppe-ID)", + "wayToGetWahaApiKey": "API-nøkkelen er verdien av WHATSAPP_API_KEY du brukte for å starte WAHA.", + "smsplanetApiDocs": "Detaljert info om API-tokens finnes i {the_smsplanet_documentation}.", + "the smsplanet documentation": "smsplanet-dokumentasjonen", + "Phone numbers": "Telefonnumre", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" er for kort til å være et toppnivådomene", + "Halo PSA Webhook URL": "Halo PSA Webhook-URL", + "username": "Brukernavn", + "Setup Instructions": "Oppsettinstruksjoner", + "halopsa_setup_step1": "Opprett et Integration Runbook i HaloPSA (Configuration → Integrations → Integration Runbooks)", + "halopsa_setup_step2": "Konfigurer runbook-aksjoner for å behandle varsler (f.eks. opprette sak)", + "onebotHttpAddress": "OneBot HTTP-adresse", + "resendApiKey": "Resend API-nøkkel", + "resendApiHelp": "Opprett API-nøkkel her {0}", + "resendToEmail": "Til e-post", + "resendSubject": "Emne", + "pingNumericLabel": "Numerisk output", + "Remote Browser": "Fjernleser", + "settingsDomainExpiry": "Domeneutløp", + "Expand All Groups": "Utvid alle grupper", + "smseagleApiType": "API-versjon", + "SpugPush Template Code": "Maldkode", + "remoteBrowserToggle": "Som standard kjører Chromium inne i Uptime Kuma-containere. Du kan bruke en fjernleser ved å slå på denne bryteren.", + "rabbitmqNodesDescription": "Angi URL for RabbitMQ management-noder inkludert protokoll og port. Eksempel: {0}", + "brevoApiHelp": "Opprett API-nøkkel her: {0}", + "smseagleUrl": "URL til SMSEagle-enheten", + "smseagleEncoding": "Send som Unicode (standard = GSM-7)", + "smseaglePriority": "Meldingsprioritet (0-9, høyest = 9)", + "smseagleMsgType": "Meldingstype", + "smseagleMsgRing": "Ring-anrop", + "smseagleMsgSms": "SMS-melding (standard)", + "nostrRelays": "Nostr-reléer", + "nostrRecipientsHelp": "npub-format, én per linje", + "Manual": "Manuell", + "pingCountLabel": "Maks pakker", + "Integration Key": "Integrasjonsnøkkel", + "ntfyCall": "Telefonsamtale", + "noOrBadCertificate": "Ingen/dårlig sertifikat", + "cacheBusterParam": "Legg til parameteren {0}", + "systemServiceDescriptionWindows": "Sjekker om Windows Service Manager {service_name} kjører", + "mongodbCommandDescription": "Kjør en MongoDB-kommando mot databasen. For informasjon om tilgjengelige kommandoer se {documentation}", + "Add Remote Browser": "Legg til fjernleser", + "OAuth2: Client Credentials": "OAuth2: Klientlegitimasjon", + "SIGNL4": "SIGNL4", + "json_value": "JSON-verdi", + "Arcade": "Arcade", + "Bubble": "Boble", + "Pop": "Pop", + "Can be found on:": "Finnes på: {0}", + "rabbitmqNodesInvalid": "Bruk en fullstendig (starter med 'http') URL for RabbitMQ-noder.", + "brevoToEmail": "Til e-post", + "resendFromName": "Fra navn", + "resendLeaveBlankForDefaultSubject": "La stå tomt for standardemne", + "Font Twemoji by Twitter licensed under": "Font Twemoji av Twitter lisensiert under", + "Umami": "Umami", + "password": "Passord", + "None (Successful Connection)": "Ingen (vellykket tilkobling)", + "Globalping - Access global monitoring probes": "Globalping - tilgang til globale måleprober", + "Globalping API Token": "Globalping API-token", + "globalpingApiTokenDescription": "Hent Globalping API-token på {0}.", + "GlobalpingHostname": "Et offentlig tilgjengelig målingsmål. Vanligvis et vertsnavn eller IPv4/IPv6-adresse avhengig av måletype.", + "GlobalpingLocationDocs": "Full dokumentasjon for lokasjonsfeltet", + "Protocol": "Protokoll", + "labelDomainExpiry": "Domeneutl.", + "Collapse All Groups": "Skjul alle grupper", + "systemServiceCommandHint": "Kommando brukt: {command}", + "threemaRecipientType": "Mottakertype", + "threemaRecipientTypeIdentityFormat": "8 tegn", + "Message format": "Meldingsformat", + "auto acknowledged": "automatisk erkjent", + "smseagleTtsModel": "Tekst-til-tale-modell-ID", + "Telephone number": "Telefonnummer", + "threemaApiAuthenticationSecret": "Gateway-ID-hemmelighet", + "Host Onesender": "Onesender-vert", + "Staged Tags for Batch Add": "Kølagte tags for masse-tillegg", + "Clear Form": "Tøm skjema", + "Nextcloud host": "Nextcloud-vert", + "Region": "Region", + "PushDeer Server URL": "PushDeer server-URL", + "Show this Maintenance Message on which Status Pages": "Vis denne vedlikeholds-meldingen på hvilke status-sider", + "gamedigGuessPort": "Gamedig: Gjett port", + "Correct": "Korrekt", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" er en IP-adresse. Domeneutløp krever et domenenavn", + "smseagleGroup": "Telefonbok-gruppenavn", + "smseagleGroupV2": "Telefonbok-gruppe-ID", + "smseagleContactV2": "Telefonbok-kontakt-ID", + "TLS Alerts": "TLS-varsler", + "brevoApiKey": "Brevo API-nøkkel", + "wayToGetPagerDutyKey": "Du finner dette ved å gå til Service -> Service Directory -> (velg en tjeneste) -> Integrations -> Add integration. Her kan du søke etter \"Events API V2\". Mer info {0}", + "Integration URL": "Integrasjons-URL", + "alertaAlertState": "Varseltilstand", + "alertaRecoverState": "Gjenopprettingstilstand", + "serwersmsAPIUser": "API-brukernavn (inkl. webapi_-prefiks)", + "serwersmsRecipientType": "Mottakertype", + "serwersmsGroupIdHelptext": "ID eller gruppe-ID-er i kundeportalen. Disse kan lastes ned via action groups / index eller kopieres fra gruppe-redigering i kundeportalen.", + "smseagleRecipient": "Mottaker(e) (skill flere med komma)", + "smseagleComma": "Flere må skilles med komma", + "smspartnerSenderNameInfo": "Må være mellom 3..=11 vanlige tegn", + "Server URL should not contain the nfty topic": "Server-URL-en skal ikke inneholde ntfy-emnet", + "onebotMessageType": "OneBot-meldingstype", + "onebotGroupMessage": "Gruppe", + "Open Badge Link Generator": "Åpne merke-lenke-generator", + "onebotPrivateMessage": "Privat", + "PushDeer Key": "PushDeer-nøkkel", + "ntfyCallHelptext": "Ring når varselet utløses. Sett til 'yes' for å bruke første verifiserte nummer, eller skriv inn et spesifikt nummer (f.eks. +12223334444). Krever ntfy Pro og et verifisert telefonnummer.", + "Badge Link Generator": "{0} sin merke-lenke-generator", + "Badge Label Prefix": "Merkeetikett-prefiks", + "monitorToastMessagesLabel": "Monitor-toastvarsler", + "Badge Down Days": "Nedetidsdager for merke", + "Badge Style": "Merkestil", + "Badge value (For Testing only.)": "Merkeverdi (kun testing)", + "toastErrorTimeout": "Tidsavbrudd for feilmeldinger", + "Enable Kafka Producer Auto Topic Creation": "Aktiver automatisk emneopprettelse for Kafka-produsent", + "AccessKey Id": "AccessKey-ID", + "nostrRelaysHelp": "Én relé-URL per linje", + "Secret AccessKey": "Hemmelig AccessKey", + "Session Token": "Økttoken", + "cacheBusterParamDescription": "Tilfeldig generert parameter for å hoppe over cache.", + "gamedigGuessPortDescription": "Porten som brukes av Valve Server Query Protocol kan være forskjellig fra klientporten. Prøv dette hvis monitoren ikke kan koble til serveren.", + "Send rich messages": "Send rike meldinger", + "Bitrix24 Webhook URL": "Bitrix24 Webhook-URL", + "bitrix24SupportUserID": "Oppgi bruker-ID-en din i Bitrix24. Du finner ID-en i lenken når du går til brukerens profil.", + "authInvalidToken": "Ugyldig token.", + "authIncorrectCreds": "Feil brukernavn eller passord.", + "successResumed": "Gjenopptatt.", + "successEdited": "Redigert.", + "successAdded": "Lagt til.", + "tagNotFound": "Tag ikke funnet.", + "foundChromiumVersion": "Fant Chromium/Chrome. Versjon: {0}", + "Remote Browsers": "Fjernlesere", + "useRemoteBrowser": "Bruk fjernleser", + "systemServiceExpectedOutput": "Forventet output: \"{0}\"", + "Browser Screenshot": "Nettleser-skjermbilde", + "wayToGetWhapiUrlAndToken": "Du får API-URL og token ved å gå til ønsket kanal fra {0}", + "wayToWriteEvolutionRecipient": "Telefonnummer med internasjonalt prefiks uten pluss i starten ({0}), kontakt-ID ({1}) eller gruppe-ID ({2}).", + "wayToGetEvolutionUrlAndToken": "Du får API-URL og token ved å gå til ønsket kanal fra {0}", + "wayToGetHeiiOnCallDetails": "Hvordan hente Trigger ID og API Keys forklares i {documentation}", + "gtxMessagingApiKeyHint": "Du finner API-nøkkelen under: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Fra telefonnummer / Transmission Path Originating Address (TPOA)", + "cellsyntOriginatortypeAlphanumeric": "Alfanumerisk streng (maks 11 tegn). Mottakere kan ikke svare.", + "cellsyntOriginatortypeNumeric": "Numerisk verdi (maks 15 sifre) med telefonnummer i internasjonalt format uten ledende 00 (f.eks. britisk 07920 110 000 skal settes som 447920110000). Mottakere kan svare.", + "cellsyntOriginator": "Vises på mottakerens telefon som avsender. Tillatte verdier og funksjon avhenger av originatortype.", + "cellsyntSplitLongMessages": "Del lange meldinger i opptil 6 deler. 153 x 6 = 918 tegn.", + "max 15 digits": "maks 15 sifre", + "max 11 alphanumeric characters": "maks 11 alfanumeriske tegn", + "snmpCommunityStringHelptext": "Denne strengen fungerer som passord for å autentisere og styre tilgang til SNMP-enheter. Den må samsvare med konfigurasjonen på SNMP-enheten.", + "SNMP Version": "SNMP-versjon", + "threemaRecipientTypeEmail": "E-postadresse", + "apiKeysDisabledMsg": "API-nøkler er deaktivert fordi autentisering er slått av.", + "Token Onesender": "Onesender-token", + "Private Number": "Privat nummer", + "privateOnesenderDesc": "Sørg for at telefonnummeret er gyldig. For å sende til privat nummer, f.eks.: 628123456789", + "groupOnesenderDesc": "Sørg for at GroupID er gyldig. For å sende til gruppe, f.eks.: 628123456789-342345", + "wayToGetOnesenderUrlandToken": "Du får URL og token på Onesender-nettstedet. Mer info {0}", + "Client Secret": "Klient-hemmelighet", + "OAuth Scope": "OAuth-omfang", + "SIGNL4 Webhook URL": "SIGNL4 Webhook-URL", + "equals": "er lik", + "not equals": "er ikke lik", + "message": "melding", + "Notification Channel": "Varslingskanal", + "Sound": "Lyd", + "Fail": "Feil", + "Select All": "Velg alle", + "Elevator": "Heis", + "Custom sound to override default notification sound": "Egendefinert lyd som overstyrer standard varslingslyd", + "Time Sensitive (iOS Only)": "Tidskritisk (kun iOS)", + "Press Enter to add node": "Trykk Enter for å legge til node", + "rabbitmqHelpText": "For å bruke monitoren må du aktivere Management Plugin i RabbitMQ-oppsettet. Se {rabitmq_documentation}.", + "smtpHelpText": "'SMTPS' tester at SMTP/TLS fungerer; 'Ignore TLS' kobler i klartekst; 'STARTTLS' kobler, sender STARTTLS og verifiserer sertifikatet. Ingen av dem sender e-post.", + "OneChatBotId": "OneChat Bot-ID", + "wayToGetWahaApiUrl": "WAHA-instansens URL.", + "Happy Eyeballs algorithm": "Happy Eyeballs-algoritmen", + "Add Another Tag": "Legg til enda en tag", + "GlobalpingDescription": "Globalping gir tilgang til tusenvis av felleshoste prober for å kjøre nettverkstester og målinger. Grense på 250 tester per time for anonyme brukere. Lagre tokenet ditt i {accountSettings} for å doble grensen til 500 per time.", + "GlobalpingLocation": "Plasseringsfeltet godtar kontinenter, land, regioner, byer, ASN-er, ISP-er eller skyregioner. Du kan kombinere filtre med {plus} (f.eks. {amazonPlusGermany} eller {comcastPlusCalifornia}). Hvis latens er viktig, snevr inn til et lite område for å unngå topper. {fullDocs}.", + "GlobalpingIpFamilyInfo": "IP-versjonen som skal brukes. Bare tillatt hvis målet er et vertsnavn.", + "GlobalpingResolverInfo": "IPv4/IPv6-adresse eller FQDN. Standard er probeens lokale resolver. Du kan endre resolver når som helst.", + "account settings": "kontoinnstillinger", + "Location": "Plassering", + "Monitor Subtype": "Monitor-undertype", + "Check for": "Se etter", + "Template ID": "Mal-ID", + "Maximum Retries": "Maks antall forsøk", + "Recipient Numbers": "Mottakernumre", + "Webpush Helptext": "Web push fungerer kun med SSL (HTTPS). For iOS må nettsiden legges til hjemskjermen først.", + "labelDomainNameExpiryNotification": "Varsel om domeneutløp", + "halopsa_password_desc": "Passord for autentisering mot Halo PSA-webhook", + "halopsa_webhook_url_desc": "Skriv inn webhook-URL fra Halo PSA Integration Runbook (Configuration > Integrations > Custom Integrations > Integration Runbooks). Velg 'Can only be started from Halo and from a public endpoint' når du lager webhooken.", + "Expected TLS Alert": "Forventet TLS-varsel", + "mariadbSocketPathDetectedHelptext": "Kobler til databasen som angitt via miljøvariabelen {0}.", + "Edit Tag": "Rediger tag", + "Server Address": "Serveradresse", + "gtxMessagingToHint": "Internasjonalt format med ledende \"+\" ({e164}, {e212} eller {e214})", + "serwersmsRecipientTypePhone": "Telefonnummer", + "smseagleApiv1": "APIv1 (for eksisterende prosjekter og bakoverkompatibilitet)", + "smseagleApiv2": "APIv2 (anbefales for nye integrasjoner)", + "smspartnerPhoneNumberHelptext": "Nummeret må være i internasjonalt format {0}, {1}. Flere nummer må skilles med {2}", + "Octopush API Version": "Octopush API-versjon", + "ntfy Topic": "ntfy-emne", + "onebotUserOrGroupId": "Gruppe-/bruker-ID", + "PushDeer Server": "PushDeer-server", + "Learn More": "Lær mer", + "Body Encoding": "Body-koding", + "Badge Link Generator Helptext": "Merke-lenker er tilgjengelige for alle monitorer tilknyttet offentlige status-sider. For mer info, se {documentation}.", + "Monitor Group": "Monitorgruppe", + "Kafka Topic Name": "Kafka-emnenavn", + "Command": "Kommando", + "wayToWriteWhapiRecipient": "Telefonnummer med internasjonalt prefiks uten pluss i starten ({0}), kontakt-ID ({1}) eller gruppe-ID ({2}).", + "threemaRecipientTypePhone": "Telefonnummer", + "Form Data Body": "Skjemabody", + "Deselect All": "Fjern alle", + "brevoSeparateMultipleEmails": "Skill flere e-poster med komma", + "From": "Fra", + "brevoSubject": "Emne", + "Template Format": "Malformat", + "pause": "Pause", + "halopsa_username_desc": "Brukernavn for autentisering mot Halo PSA-webhook", + "mtls-auth-server-key-label": "Nøkkel", + "mtls-auth-server-key-placeholder": "Nøkkel-body", + "halopsa_setup_step4": "Velg Basic Authentication og lag brukernavn og passord. Skriv eller lim inn disse i feltene over", + "Message Format": "Meldingsformat", + "Badge Maintenance Color": "Merke farge - vedlikehold", + "Badge Warn Days": "Advarselsdager for merke", + "Alphanumerical string and hyphens only": "Kun alfanumerisk streng og bindestreker", + "Harp": "Harpe", + "Doorbell": "Dørklokke", + "mtls-auth-server-cert-placeholder": "Sertifikat-body", + "Clear current filters": "Fjern gjeldende filtre", + "Sort options": "Sorteringsvalg", + "Sort by status": "Sorter etter status", + "To Number": "Til nummer", + "GrafanaOncallURL": "Grafana Oncall-URL", + "Never": "Aldri", + "System Service": "Systemtjeneste", + "SSL/TLS": "SSL/TLS", + "Metadata": "Metadata", + "End": "Slutt", + "Check Type": "Sjekk-type", + "Service Name": "Tjenestenavn", + "GRPC Options": "gRPC-alternativer", + "Details": "Detaljer", + "Suppress Notifications": "Undertrykk varsler", + "discordSuppressNotificationsHelptext": "Når aktivert postes meldinger i kanalen men utløser ikke push- eller skrivebordsvarsler.", + "domain_expiry_unsupported_missing_target": "Ingen gyldig domene eller vertsnavn er konfigurert for denne monitoren", + "mtls-auth-server-cert-label": "Sertifikat", + "twilioApiKeyHelptext": "API-nøkkelen er valgfri men anbefalt. Du kan oppgi enten Account SID og AuthToken fra hovedsiden i TwilioConsole eller Account SID og et par med API Key og API Key secret", + "twilioMessagingServiceSID": "Messaging Service SID (valgfritt)", + "twilioAuthToken": "Auth Token / API Key Secret", + "twilloMessagingServiceSIDHelptext": "Skriv Messaging Service SID her hvis du bruker {twillo_messaging_service_help_link} til å administrere avsendere og funksjoner", + "Badge Warn Color": "Merke farge - advarsel", + "successBackupRestored": "Backup gjenopprettet.", + "domain_expiry_unsupported_is_icann": "Domenet \"{domain}\" er ikke egnet for utløpsovervåking fordi public suffix \".{publicSuffix}\" ikke er ICANN", + "mtls-auth-server-ca-placeholder": "Server-CA", + "halopsa_setup_step3": "Kopier webhook-URL-en og lim den inn i feltet over", + "smsplanetApiToken": "Token for SMSPlanet API", + "systemService": "Systemtjeneste", + "systemServiceName": "Tjenestenavn", + "threemaSenderIdentityFormat": "8 tegn, starter vanligvis med *", + "New Group": "Ny gruppe", + "RabbitMQ Nodes": "RabbitMQ Management-noder", + "Enter the list of nodes": "Skriv inn listen over RabbitMQ management-noder", + "brevoFromName": "Fra navn", + "brevoLeaveBlankForDefaultName": "la stå tomt for standardnavn", + "pingGlobalTimeoutLabel": "Global timeout", + "wayToGetWahaSession": "WAHA sender varsler til denne Chat-ID-en fra valgt sesjon. Du finner den i WAHA Dashboard.", + "YZJ Webhook URL": "YZJ Webhook-URL", + "ntfyPriorityDown": "Prioritet for NED-hendelser", + "Badge Pending Color": "Merke farge - venter", + "Optional: Space separated list of scopes": "Valgfritt: Mellomrom-separert liste med scopes", + "Go back to home page.": "Gå tilbake til startsiden.", + "Conditions": "Betingelser", + "starts with": "starter med", + "not starts with": "starter ikke med", + "Group Name": "Gruppenavn", + "YZJ Robot Token": "YZJ Robot-token", + "Google": "Google", + "Ip Family": "IP-familie", + "ipFamilyDescriptionAutoSelect": "Bruker {happyEyeballs} for å bestemme IP-familie.", + "Screenshot Delay": "Skjermbildeforsinkelse (venter {milliseconds})", + "milliseconds": "{n} millisekund | {n} millisekunder", + "screenshotDelayDescription": "Vent eventuelt så mange millisekunder før skjermbilde tas. Maks: {maxValueMs}ms (0,5 × intervall).", + "screenshotDelayWarning": "Høyere verdier holder nettleseren åpen lengre, som kan øke minnebruken med mange samtidige monitorer.", + "documentationOf": "{0}-dokumentasjon", + "wayToGetThreemaGateway": "Du kan registrere deg for Threema Gateway {0}.", + "evolutionRecipient": "Telefonnummer / kontakt-ID / gruppe-ID", + "threemaRecipientTypePhoneFormat": "E.164, uten ledende +", + "not ends with": "slutter ikke med", + "Guitar": "Gitar", + "Separate multiple email addresses with commas": "Skill flere e-postadresser med komma", + "octopushEndpoint": "octopush (endepunkt: {url})", + "legacyOctopushEndpoint": "Legacy Octopush-DM (endepunkt: {url})", + "Monitor Setting": "{0} sin overvåkingsinnstilling", + "wayToGetPagerTreeIntegrationURL": "Etter å ha opprettet Uptime Kuma-integrasjonen i PagerTree, kopier Endpoint. Se full info {0}", + "Show Clickable Link Description": "Hvis avkrysset kan alle med tilgang til denne status-siden se monitorens URL.", + "smtpDkimskipFields": "Header-nøkler som ikke skal signeres (valgfritt)", + "Flute": "Fløyte", + "Money": "Penger", + "Scifi": "Sci-fi", + "Clear": "Tøm", + "resendFromEmail": "Fra e-post", + "brevoCcEmail": "CC e-post", + "domainExpiryDescription": "Send varsling når domenet utløper om:", + "screenshot of the website": "Skjermbilde av nettstedet", + "Basic checkbox toggle button group": "Grunnleggende avmerkingsboks-brytergruppe", + "Uptime Kuma": "Uptime Kuma", + "Auto resolve or acknowledged": "Automatisk løst eller erkjent", + "Sort by uptime": "Sorter etter oppetid", + "Sort by certificate expiry": "Sorter etter sertifikatutløp", + "Severity": "Alvorlighetsgrad", + "playground": "lekeplass", + "maxPing": "Maks ping", + "avgPing": "Snitt ping", + "smseagleTo": "Telefonnummer", + "smseagleContact": "Telefonbok-kontaktnavn", + "apiKeySevenIO": "SevenIO API-nøkkel", + "threemaSenderIdentity": "Gateway-ID", + "Message Template": "Meldingsmal", + "smseagleRecipientType": "Mottakertype", + "smseagleToken": "API-tilgangstoken", + "Custom Monitor Type": "Egendefinert overvåkningstype", + "monitorToastMessagesDescription": "Toastvarsler for monitorer forsvinner etter oppgitt tid i sekunder. Sett til -1 for å deaktivere timeout. Sett til 0 for å slå av toastvarsler.", + "toastSuccessTimeout": "Tidsavbrudd for suksessmeldinger", + "Kafka Brokers": "Kafka-meglere", + "wayToGetFlashDutyKey": "For å integrere Uptime Kuma med Flashduty: Gå til Channels > Select a channel > Integrations > Add a new integration, velg Uptime Kuma og kopier Push URL.", + "FlashDuty Push URL": "Push-URL", + "webhookGetMethodDesc": "GET sender data som spørringsparametere og tillater ikke body. Nyttig for å trigge Uptime Kuma Push-monitorer.", + "nostrSender": "Avsender privat nøkkel (nsec)", + "wayToWriteWahaChatId": "Telefonnummer med internasjonalt prefiks uten pluss ({0}), kontakt-ID ({1}) eller gruppe-ID ({2}). Varsler sendes til denne Chat-ID-en fra WAHA-sesjonen.", + "resendLeaveBlankForDefaultName": "la stå tomt for standardnavn", + "showOnlyLastHeartbeat": "Vis kun siste heartbeat", + "Reveal": "Vis", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Tidskritiske varsler leveres umiddelbart, selv om enheten er i Ikke forstyrr-modus.", + "Google Analytics ID": "Google Analytics-ID", + "smtpDkimHashAlgo": "Hash-algoritme (valgfritt)", + "smtpDkimheaderFieldNames": "Header-nøkler som skal signeres (valgfritt)", + "serwersmsSenderName": "SMS-avsendernavn (registrert via kundeportalen)", + "smseagleMsgTts": "Tekst-til-tale-anrop", + "smseagleMsgTtsAdvanced": "Tekst-til-tale avansert anrop", + "smseagleDuration": "Varighet (i sekunder)", + "smspartnerApiurl": "Du finner API-nøkkelen på dashbordet ditt på {0}", + "smspartnerPhoneNumber": "Telefonnummer", + "smspartnerSenderName": "SMS-avsendernavn", + "pagertreeUrgency": "Hast", + "pagertreeResolve": "Løs automatisk", + "Badge Down Color": "Merke farge - nede", + "Enter the list of brokers": "Skriv inn listen over meglere", + "Press Enter to add broker": "Trykk Enter for å legge til megler", + "Kafka Producer Message": "Kafka-produsentmelding", + "Authorization Identity": "Autorisasjonsidentitet", + "FlashDuty Push URL Placeholder": "Kopier fra varslingsintegrasjonssiden", + "showCertificateExpiry": "Vis sertifikatutløp", + "successPaused": "Pauset.", + "signl4Docs": "Du finner mer info om hvordan du konfigurerer SIGNL4 og får webhook-URL i {0}.", + "Please enter a valid OID.": "Angi en gyldig OID.", + "Recipient Type": "Mottakertype", + "conditionAdd": "Legg til betingelse", + "Authorization Header": "Autorisasjonsheader", + "Cannot connect to the socket server.": "Kan ikke koble til socket-serveren.", + "conditionDelete": "Slett betingelse", + "Plain Text": "Ren tekst", + "Plausible": "Sannsynlig", + "Matomo": "Matomo", + "Disable URL in Notification": "Skru av URL i varsling", + "wayToGetClickSMSIRTemplateID": "Malen må inneholde et {uptkumaalert}-felt. Du kan lage en ny mal {here}.", + "Unable to get permission to notify": "Kunne ikke få tillatelse til å varsle (forespørsel avslått eller ignorert).", + "expectedTlsAlertDescription": "Velg TLS-varselet du forventer at serveren returnerer. Bruk {code} for å bekrefte at mTLS-endepunkter avviser tilkoblinger uten klientsertifikat. Se {link} for detaljer.", + "TLS Alert Spec": "RFC 8446", + "Analytics ID": "Analyse-ID", + "Analytics Script URL": "Analyse-skript-URL", + "Analytics Type": "Analyse-type", + "Badge Up Color": "Merke farge - oppe", + "evolutionInstanceName": "Instansnavn", + "threemaRecipient": "Mottaker", + "threemaRecipientTypeIdentity": "Threema-ID", + "contains": "inneholder", + "Send DOWN silently": "Send NED lydløst", + "Conversation token": "Samtaletoken", + "Bot secret": "Bot-hemmelighet", + "Send UP silently": "Send OPP lydløst", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Installasjon av Nextcloud Talk-bot krever administrativ tilgang til serveren.", + "Recipient Number": "Mottakernummer", + "successDeleted": "Slettet.", + "brevoBccEmail": "BCC e-post", + "brevoLeaveBlankForDefaultSubject": "la stå tomt for standardemne", + "pingCountDescription": "Antall pakker som skal sendes før stopp", + "passwordTooWeak": "Passordet er for svakt. Det bør inneholde bokstaver og tall og være minst 6 tegn.", + "smtpDkimPrivateKey": "Privat nøkkel", + "API Keys": "API-nøkler", + "Add a Remote Browser": "Legg til fjernleser", + "Remote Browser not found!": "Fjernleser ikke funnet!", + "self-hosted container": "selvhostet container", + "deleteRemoteBrowserMessage": "Er du sikker på at du vil slette denne fjernleseren for alle monitorer?", + "OAuth Token URL": "OAuth-token-URL", + "conditionAddGroup": "Legg til gruppe", + "conditionDeleteGroup": "Slett gruppe", + "smscTranslit": "smscTranslit", + "Endpoint": "Endepunkt", + "OID (Object Identifier)": "OID (objektidentifikator)", + "snmpOIDHelptext": "Skriv inn OID for sensoren eller statusen du vil overvåke. Bruk MIB-browsere eller SNMP-programvare hvis du er usikker på OID.", + "Condition": "Betingelse", + "Lost connection to the socket server.": "Mistet forbindelsen til socket-serveren.", + "No tags found.": "Ingen tags funnet.", + "callMeBotGet": "Her kan du generere et endepunkt for {0}, {1} og {2}. Husk at du kan bli rate-begrenset. Ratebegrensningene ser ut til å være: {3}", + "To Phone Number": "Til telefonnummer", + "Leave blank to use a shared sender number.": "La stå tomt for å bruke et delt avsendernummer.", + "nostrRecipients": "Mottakere offentlige nøkler (npub)", + "wayToGetBitrix24Webhook": "Du kan opprette en webhook ved å følge stegene på {0}", + "ends with": "slutter med", + "less than": "mindre enn", + "greater than": "større enn", + "less than or equal to": "mindre enn eller lik", + "greater than or equal to": "større enn eller lik", + "imageResetConfirmation": "Bilde tilbakestilt til standard", + "record": "post", + "pingGlobalTimeoutDescription": "Total tid i sekunder før ping stopper uansett antall pakker", + "pingPerRequestTimeoutLabel": "Timeout per ping", + "pingIntervalAdjustedInfo": "Intervall justert basert på pakkeantall, global timeout og per-ping-timeout", + "Show Clickable Link": "Vis klikkbar lenke", + "Badge Type": "Merketype", + "Badge Duration (in hours)": "Merkevarighet (i timer)", + "Badge Label": "Merkeetikett", + "Badge Prefix": "Merkeverdi-prefiks", + "Badge Suffix": "Merkeverdi-suffiks", + "Badge Label Color": "Merkeetikett-farge", + "Enable Kafka SSL": "Aktiver Kafka SSL", + "noGroupMonitorMsg": "Ikke tilgjengelig. Opprett en gruppeovervåker først.", + "Request Body": "Forespørsels-body", + "HTTP Method": "HTTP-metode", + "webhookPostMethodDesc": "POST passer for de fleste moderne HTTP-servere.", + "wayToGetSevenIOApiKey": "Besøk dashbordet under app.seven.io > developer > api key > den grønne pluss-knappen", + "senderSevenIO": "Avsendende nummer eller navn", + "receiverInfoSevenIO": "Hvis mottakernummeret ikke er i Tyskland må du legge til landskoden foran nummeret (f.eks. for landskode 1 i USA bruk 117612121212 i stedet for 017612121212)", + "whapiRecipient": "Telefonnummer / kontakt-ID / gruppe-ID", + "API URL": "API-URL", + "What is a Remote Browser?": "Hva er en fjernleser?", + "Allow Long SMS": "Tillat lange SMS", + "Group ID": "Gruppe-ID", + "pingPerRequestTimeoutDescription": "Maks ventetid (i sekunder) før en enkelt ping-pakke anses tapt", + "customUrlDescription": "Brukes som klikkbar URL i stedet for monitorens egen.", + "templateAvailableVariables": "Tilgjengelige variabler", + "example": "Eksempel", + "Result": "Resultat", + "ntfyUseTemplate": "Tilpass varslingsmaler", + "ntfyUseTemplateDescription": "Aktiver dette for å tilpasse varslingstitler og -meldinger ved bruk av LiquidJS-maler", + "ntfyCustomTitle": "Egendefinert tittelmal", + "ntfyCustomMessage": "Egendefinert meldingsmal", + "ntfyNotificationTemplateFallback": "La stå tomt for å bruke standard Uptime Kuma-format", + "discordMessageFormatNormal": "Normal (rike innebygginger)", + "discordMessageFormatMinimalist": "Minimalistisk (kort status)", + "discordMessageFormatCustom": "Egendefinert mal", + "discordUseMessageTemplate": "Bruk egendefinert meldingsmal", + "discordMessageTemplate": "Meldingsmal", + "slackIncludeGroupName": "Inkluder navn på overvåkingsgruppe", + "slackUseTemplate": "Bruk egendefinert meldingsmal", + "slackUseTemplateDescription": "Hvis aktivert, vil meldingen bli sendt med en egendefinert mal. Du kan bruke Liquid-templating for å inkludere informasjon om overvåkingsgruppen via monitorJSON.sti eller monitorJSON.pathName.", + "slackIncludeGroupNameDescription": "Hvis aktivert, vil banen til overvåkingsgruppen bli inkludert i varsler for å hjelpe med å skille mellom overvåkere med samme navn i ulike grupper.", + "discordMessageFormat": "Meldingsformat", + "discordUseMessageTemplateDescription": "Hvis aktivert, vil meldingen bli sendt med en egendefinert mal (LiquidJS). La stå tomt for å bruke standardformatet til Uptime Kuma.", + "Cloud ID": "Sky-ID", + "API Token": "API-token", + "See Jira Cloud Docs": "Se Jira Cloud-dokumentasjon", + "aboutJiraCloudId": "Mer info om Jira Cloud-ID: {0}", + "see Jira Cloud Docs": "se Jira Cloud-dokumentasjon", + "Jira Service Management": "Jira Tjenesteadministrasjon", + "halopsa_field_title": "Varseltittel (alltid «Uptime Kuma Alert»)", + "halopsa_field_status": "Overvåkingsstatus: OPPE, NEDE, VARSLING eller UKJENT", + "halopsa_field_monitor": "Navn på overvåkingen", + "halopsa_field_message": "Fullt varselmelding med status og detaljer", + "halopsa_field_timestamp": "Hendelsestidspunkt i ISO 8601-format", + "halopsa_field_uptime_kuma_version": "Uptime Kuma-versjonsnummer", + "Webhook Payload Fields": "Webhook Payload Felter", + "halopsa_payload_desc": "Følgende felter sendes til Halo PSA-webhooken din:", + "halopsa_field_monitor_id": "Unik overvåkings-ID (null for testvarsler) – Bruk dette for å koble varsler til saker", + "halopsa_id_usage_hint": "💡 Tips: Bruk monitor_id for å pålitelig koble varsler til saker, og heartbeat_id for å spore hendelseshistorikk", + "halopsa_setup_step5": "Konfigurer runbooken til å bruke monitor_id for å matche varsler med eksisterende saker", + "matrixUseTemplate": "Bruk egendefinert meldingsmal", + "matrixUseTemplateDescription": "Hvis aktivert, vil meldingen bli sendt ved hjelp av en egendefinert mal.", + "teamsEnableTags": "Inkluder tagger", + "teamsEnableTagsDescription": "Hvis aktivert, vil meldingen inkludere overvokere-tagger.", + "signalUseTemplate": "Bruk egendefinert meldingsmal", + "signalUseTemplateDescription": "Hvis aktivert, vil meldingen bli sendt med en egendefinert mal. Du kan bruke Liquid-templating for å tilpasse varslingsformatet.", + "telnyxApiKey": "API-nøkkel (V2)", + "telnyxApiKeyHelptext": "Din Telnyx V2 API-nøkkel fra Telnyx-portalen under API-nøkler", + "telnyxPhoneNumber": "Fra nummer", + "telnyxPhoneNumberHelptext": "Ditt Telnyx-telefonnummer å sende fra (må være registrert i Telnyx-kontoen din, f.eks. +15551234567)", + "telnyxToNumber": "Til nummer", + "360messengerAuthToken": "360messenger API-nøkkel", + "evolutionCustomMessageTitle": "Egendefinert melding (valgfritt)", + "evolutionCustomMessageDesc": "La stå tom for å bruke standardmeldingen.", + "wayToGetMaxToken": "Du kan få en MAX-bot-token og andre detaljer fra {0}.", + "maxMessenger": "MAX-meldingsapp", + "maxApiUrlDescription": "Base-API-URL for MAX-meldingsapp. Standard: https://platform-api.max.ru", + "wayToGetMaxChatID": "Angi chat-identifikatoren i MAX der meldinger skal leveres.", + "maxUseTemplate": "Bruk egendefinert meldingsmal", + "maxUseTemplateDescription": "Hvis aktivert, vil meldingen bli sendt ved hjelp av en egendefinert mal.", + "maxTemplateFormatDescription": "MAX-meldingsappen støtter ren tekst, Markdown og HTML-formatering.", + "360messengerUseTemplate": "Bruk en egendefinert meldingsmal", + "360messengerTemplate": "360messenger meldingsmal", + "360messengerGroupList": "WhatsApp-grupper", + "360messengerRecipient": "Mottakerens telefonnummer(e)", + "360messengerGroupId": "360messenger gruppe-ID", + "certificateExpiryNotificationHelp": "Antall dager i forveien kan konfigureres i innstillingene.", + "API Version": "API-versjon", + "Peer ID": "Peer ID", + "vkApiVersionDescription": "VK API-versjon som brukes for forespørsler. La standardverdien stå hvis du ikke spesifikt trenger en annen VK API-versjon for kompatibilitet.", + "vkDontParseLinks": "Deaktiver lenkeforhåndsvisninger", + "vkDontParseLinksDescription": "Hvis aktivert, vil VK ikke generere forhåndsvisninger/snutter for lenker.", + "vkPeerIdDescription": "Angi mål-VK peer_id. Denne verdien sendes til API-et slik den er.", + "WebSocket Options": "WebSocket-alternativer", + "Notification Action": "Varslingshandling", + "homeAssistantNotificationActionHelptext": "En liste over varslingshandlinger finnes i Home Assistant under «Innstillinger > Utviklerverktøy > Handlinger». Søk etter «notify» for å finne handlingene dine. Skriv bare delen etter «notify.», for eksempel: for handlingen «notify.mobile_app_xyz» skriver du «mobile_app_xyz». For innebygde mobilvarsler, se etter «Send et varsel via mobile_app_xyz» (ikke «Send et varsel»).", + "pingPacketSizeWarning": "En pakkestørrelse mindre enn 16 gir ikke rundturstider på Linux", + "telnyxToNumberHelptext": "Telefonnummeret som SMS-varsler skal sendes til (f.eks. +15559876543)", + "telnyxMessagingProfileId": "Meldingsprofil-ID (valgfritt)", + "telnyxMessagingProfileIdHelptext": "Valgfritt: knytt en Telnyx meldingsprofil for å styre avsenderinnstillinger og funksjoner", + "monitorTypeGameServer": "Spillserver", + "monitorTypeDatabase": "Databasemonitortype", + "monitorTypeSpecial": "Spesiell", + "oracledbConnectionString": "Oracle Database: {connectionString}", + "360messengerMessageTemplate": "Meldingsmal", + "360messengerEnableCustomMessage": "Aktiver en tilpasset meldingsmal i stedet for standardmeldingen.", + "360messengerErrorGeneric": "Kan ikke laste inn WhatsApp gruppelisten: {message}", + "360messengerSelectGroupList": "Velg en gruppe å legge til", + "360messengerSelectedGroupID": "Velg Gruppe ID'er", + "360messengerEnableSendToGroup": "Aktiver sending til WhatsApp-gruppe(r)", + "360messengerCustomMessageTemplate": "Tilpasset meldingsmal", + "360messengerErrorNoApiKey": "Vennligst skriv inn 360messenger API-nøkkelen din først.", + "360messengerWayToWriteRecipient": "Skriv inn ett eller flere telefonnumre i internasjonalt format uten plusstegn foran (f.eks. {0}). Skill flere tall med komma.", + "360messengerWayToGetUrlAndToken": "Du kan få 360messenger API-nøkkelen din fra {0}.", + "360messengerErrorNoGroups": "Ingen WhatsApp grupper ble funnet for denne kontoen.", + "360messengerErrorApi": "Kan ikke laste inn WhatsApp gruppelisten (Feil {statusCode}: {message})." +} diff --git a/src/lang/ne.json b/src/lang/ne.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/lang/ne.json @@ -0,0 +1 @@ +{} diff --git a/src/lang/nl-NL.json b/src/lang/nl-NL.json new file mode 100644 index 0000000..57b986c --- /dev/null +++ b/src/lang/nl-NL.json @@ -0,0 +1,1617 @@ +{ + "languageName": "Nederlands", + "checkEverySecond": "Controleer elke {0} seconden", + "retriesDescription": "Maximum aantal nieuwe pogingen voordat de service wordt gemarkeerd als niet beschikbaar en er een melding wordt verzonden", + "ignoreTLSError": "Negeer TLS/SSL-fout voor HTTPS-websites", + "upsideDownModeDescription": "Draai de status om. Als de service bereikbaar is, zal OFFLINE getoond worden.", + "maxRedirectDescription": "Maximaal aantal te volgen omleidingen. Stel in op 0 om omleidingen uit te schakelen.", + "acceptedStatusCodesDescription": "Selecteer statuscodes die als een succesvol antwoord worden beschouwd.", + "passwordNotMatchMsg": "Het herhaalwachtwoord komt niet overeen.", + "notificationDescription": "Wijs a.u.b. een melding toe aan de monitor(s) om het te laten werken.", + "keywordDescription": "Zoek trefwoord in gewone html of JSON-response en het is hoofdlettergevoelig.", + "pauseDashboardHome": "Gepauzeerd", + "deleteMonitorMsg": "Weet u zeker dat u deze monitor wilt verwijderen?", + "deleteNotificationMsg": "Weet u zeker dat u deze melding voor alle monitoren wilt verwijderen?", + "resolverserverDescription": "Cloudflare is de standaardserver, u kunt de resolver server op elk moment wijzigen.", + "rrtypeDescription": "Selecteer het RR-type dat u wilt monitoren", + "pauseMonitorMsg": "Weet je zeker dat je wilt pauzeren?", + "enableDefaultNotificationDescription": "Voor elke nieuwe monitor wordt deze melding standaard ingeschakeld. U kunt de melding nog steeds afzonderlijk uitschakelen voor elke monitor.", + "clearEventsMsg": "Weet je zeker dat je alle evenementen voor deze monitor wilt verwijderen? Dit verwijderd de data van geassocieerde berichten permanent en kan niet worden teruggedraaid.", + "clearHeartbeatsMsg": "Weet je zeker dat je alle heartbeats voor deze monitor wilt verwijderen? Dit verwijderd alle heartbeat data permanent en kan niet worden teruggedraaid.", + "confirmClearStatisticsMsg": "Weet u zeker dat u alle statistieken wilt verwijderen?", + "twoFAVerifyLabel": "Voer uw 2FA controle token in voor verificatie:", + "tokenValidSettingsMsg": "Token is geldig! U kunt nu de 2FA-instellingen opslaan.", + "confirmEnableTwoFAMsg": "Weet je zeker dat je 2FA wilt inschakelen?", + "confirmDisableTwoFAMsg": "Weet je zeker dat je 2FA wilt uitschakelen?", + "Settings": "Instellingen", + "Dashboard": "Dashboard", + "New Update": "Nieuwe update", + "Language": "Taal", + "Appearance": "Weergave", + "Theme": "Thema", + "General": "Algemeen", + "Version": "Versie", + "Check Update On GitHub": "Zoek naar updates op GitHub", + "List": "Lijst", + "Add": "Toevoegen", + "Add New Monitor": "Nieuwe monitor toevoegen", + "Quick Stats": "Snelle statistieken", + "Up": "Online", + "Down": "Offline", + "Pending": "In afwachting", + "Unknown": "Onbekend", + "Pause": "Pauze", + "Name": "Naam", + "Status": "Status", + "DateTime": "Datum Tijd", + "Message": "Bericht", + "No important events": "Geen belangrijke gebeurtenissen", + "Resume": "Hervatten", + "Edit": "Wijzigen", + "Delete": "Verwijderen", + "Current": "Huidig", + "Uptime": "Uptime", + "Cert Exp.": "Cert. verl.", + "day": "dag | dagen", + "-day": "-dag", + "hour": "uur", + "-hour": "-uur", + "Response": "Antwoord", + "Ping": "Ping", + "Monitor Type": "Monitortype", + "Keyword": "Trefwoord", + "Friendly Name": "Vriendelijke naam", + "URL": "URL", + "Hostname": "Hostnaam", + "Port": "Poort", + "Heartbeat Interval": "Hartslaginterval", + "Retries": "Pogingen", + "Advanced": "Geavanceerd", + "Upside Down Mode": "Ondersteboven modus", + "Max. Redirects": "Max. Omleidingen", + "Accepted Status Codes": "Geaccepteerde statuscodes", + "Save": "Opslaan", + "Notifications": "Meldingen", + "Not available, please setup.": "Niet beschikbaar, stel a.u.b. in.", + "Setup Notification": "Melding instellen", + "Light": "Licht", + "Dark": "Donker", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Thema - Hartslagbalk", + "Normal": "Normaal", + "Bottom": "Onderkant", + "None": "Geen", + "Timezone": "Tijdzone", + "Search Engine Visibility": "Zichtbaarheid voor zoekmachines", + "Allow indexing": "Indexering toestaan", + "Discourage search engines from indexing site": "Ontmoedig zoekmachines om de site te indexeren", + "Change Password": "Verander wachtwoord", + "Current Password": "Huidig wachtwoord", + "New Password": "Nieuw wachtwoord", + "Repeat New Password": "Herhaal nieuw wachtwoord", + "Update Password": "Vernieuw wachtwoord", + "Disable Auth": "Authenticatie uitschakelen", + "Enable Auth": "Authenticatie inschakelen", + "disableauth.message1": "Weet je zeker dat je {disableAuth}?", + "disable authentication": "authenticatie wilt uitschakelen", + "disableauth.message2": "Er zijn omstandigheden waarbij je {intendThirdPartyAuth} voor Uptime Kuma, zoals Cloudflare Access, Authelia of andere authenticatiemechanismen.", + "where you intend to implement third-party authentication": "authenticatie door derden wilt implementeren", + "Please use this option carefully!": "Gebruik deze optie zorgvuldig!", + "Logout": "Uitloggen", + "Leave": "Vertrekken", + "I understand, please disable": "Ik begrijp het, schakel a.u.b. uit", + "Confirm": "Bevestigen", + "Yes": "Ja", + "No": "Nee", + "Username": "Gebruikersnaam", + "Password": "Wachtwoord", + "Remember me": "Wachtwoord onthouden", + "Login": "Inloggen", + "No Monitors, please": "Geen monitoren, alstublieft", + "add one": "voeg een toe", + "Notification Type": "Melding type", + "Email": "E-mail", + "Test": "Testen", + "Certificate Info": "Certificaat informatie", + "Resolver Server": "Resolver Server", + "Resource Record Type": "Type bronrecord", + "Last Result": "Laatste resultaat", + "Create your admin account": "Maak uw beheerdersaccount aan", + "Repeat Password": "Herhaal wachtwoord", + "Export": "Exporteren", + "Import": "Importeren", + "respTime": "reactietijd (ms)", + "notAvailableShort": "N.v.t.", + "Default enabled": "Standaard ingeschakeld", + "Apply on all existing monitors": "Pas toe op alle bestaande monitors", + "Create": "Aanmaken", + "Clear Data": "Data wissen", + "Events": "Gebeurtenissen", + "Heartbeats": "Hartslagen", + "Auto Get": "Auto Get", + "backupDescription": "U kunt een back-up maken van alle monitoren en alle meldingen in een JSON-bestand.", + "backupDescription2": "PS: Geschiedenis- en gebeurtenisgegevens zijn niet inbegrepen.", + "backupDescription3": "Gevoelige gegevens zoals melding tokens zijn opgenomen in het exportbestand, houd het veilig opgeslagen.", + "alertNoFile": "Selecteer een bestand om te importeren.", + "alertWrongFileType": "Selecteer een JSON-bestand.", + "Verify Token": "Controleer token", + "Setup 2FA": "Stel 2FA in", + "Enable 2FA": "Schakel 2FA in", + "Disable 2FA": "Schakel 2FA uit", + "2FA Settings": "2FA-instellingen", + "Two Factor Authentication": "Twee Factor Authenticatie", + "Active": "Actief", + "Inactive": "Inactief", + "Also apply to existing monitors": "Voeg ook toe aan bestaande monitors", + "Token": "Token", + "Show URI": "Toon URI", + "Clear all statistics": "Wis alle statistieken", + "retryCheckEverySecond": "Probeer elke {0} seconden", + "importHandleDescription": "Kies 'Sla bestaande over' als je elke monitor of melding met dezelfde naam wilt overslaan. Kies 'Overschrijf' als je elke monitor of notificatie wilt verwijderen.", + "confirmImportMsg": "Weet je zeker dat je dit bestand wilt importeren? Controleer of je de correcte importeer optie hebt geselecteerd.", + "Heartbeat Retry Interval": "Heartbeat opnieuw proberen interval", + "Import Backup": "Importeer Backup", + "Export Backup": "Exporteer Backup", + "Skip existing": "Sla bestaande over", + "Overwrite": "Overschrijf", + "Options": "Opties", + "Keep both": "Bewaar beide", + "Tags": "Labels", + "Add New below or Select...": "Voeg nieuwe toe of selecteer…", + "Tag with this name already exist.": "Label met deze naam bestaat al.", + "Tag with this value already exist.": "Label met deze waarde bestaat al.", + "color": "Kleur", + "value (optional)": "waarde (optioneel)", + "Gray": "Grijs", + "Red": "Rood", + "Orange": "Oranje", + "Green": "Groen", + "Blue": "Blauw", + "Indigo": "Indigo", + "Purple": "Paars", + "Pink": "Roze", + "Search...": "Zoeken…", + "Avg. Ping": "Gemiddelde ping", + "Avg. Response": "Gemiddelde response", + "Entry Page": "Opstartpagina", + "statusPageNothing": "Niets hier, voeg een groep of monitor toe.", + "No Services": "Geen diensten", + "All Systems Operational": "Alle systemen operationeel", + "Partially Degraded Service": "Gedeeltelijk verminderde prestaties", + "Degraded Service": "Verminderde prestaties", + "Add Group": "Voeg groep toe", + "Add a monitor": "Voeg monitor toe", + "Edit Status Page": "Wijzig status pagina", + "Go to Dashboard": "Ga naar Dashboard", + "Status Page": "Status Pagina", + "Status Pages": "Status Pagina", + "telegram": "Telegram", + "webhook": "Webhook", + "smtp": "Email (SMTP)", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Ondersteunt 50+ notificatie-diensten)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "Method": "Methode", + "Body": "Inhoud", + "Headers": "Headers", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "The request headers is geen geldige JSON: ", + "BodyInvalidFormat": "De request body is geen geldige JSON: ", + "Primary Base URL": "Hoofd Basis URL", + "Push URL": "Push URL", + "needPushEvery": "Je moet deze URL elke {0} seconden aanroepen.", + "pushOptionalParams": "Optionele parameters: {0}", + "defaultNotificationName": "Mijn {notification} Alert ({number})", + "here": "hier", + "Required": "Verplicht", + "Bot Token": "Bot token", + "wayToGetTelegramToken": "Je kunt een token krijgen van {0}.", + "Chat ID": "Chat ID", + "supportTelegramChatID": "Ondersteuning Directe Chat / Groep / Kanaal Chat ID", + "wayToGetTelegramChatID": "Je kunt je CHAT ID krijgen door een bericht te sturen naar de bot en naar deze URL te gaan om het chat_id te bekijken:", + "YOUR BOT TOKEN HERE": "DE BOT TOKEN HIER", + "chatIDNotFound": "Chat ID is niet gevonden; stuur eerst een bericht naar de bot", + "Post URL": "Post URL", + "Content Type": "Content Type", + "webhookJsonDesc": "{0} is goed voor een moderne HTTP server zoals Express.js", + "webhookFormDataDesc": "{multipart} is goed voor PHP. De JSON moet worden ontleed met {decodeFunction}", + "secureOptionNone": "Geen / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Negeer TLS Error", + "From Email": "Van Email", + "emailCustomSubject": "Aangepast Onderwerp", + "To Email": "Naar Email", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord Webhook URL", + "wayToGetDiscordURL": "Je kunt dit krijgen door te gaan naar Server Instellingen -> Integraties -> Bekijk webhooks -> Nieuwe webhook", + "Bot Display Name": "Bot Weergave Naam", + "Prefix Custom Message": "Prefix Aangepast Bericht", + "Hello @everyone is...": "Hallo {'@'}iedereen is…", + "Webhook URL": "URL webhook", + "wayToGetTeamsURL": "Je kunt hier leren hoe je een webhook URL kunt maken {0}.", + "Number": "Nummer", + "Recipients": "Ontvangers", + "needSignalAPI": "Je moet een signal client met REST API hebben.", + "wayToCheckSignalURL": "Je kunt op deze URL zien hoe je een kunt instellen:", + "signalImportant": "BELANGRIJK: Je kunt groepen en nummers niet mengen in ontvangers!", + "Application Token": "Applicatie Token", + "Server URL": "Server URL", + "Priority": "Prioriteit", + "Icon Emoji": "Icoon Emoji", + "Channel Name": "Kanaal Naam", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Meer info over Webhooks op: {0}", + "aboutChannelName": "Voer de kanaal naam in op {0} Kanaal naam veld als je het Webhook kanaal wilt omzeilen. Bijv: #other-channel", + "aboutKumaURL": "Als je de Uptime Kuma URL veld leeg laat, wordt standaard het GitHub project pagina weergegeven.", + "emojiCheatSheet": "Emoji spiekbrief: {0}", + "PushByTechulus": "Push door Techulus", + "clicksendsms": "ClickSend SMS", + "GoogleChat": "Google Chat (Google Workspace alleen)", + "User Key": "Gebruikers sleutel", + "Device": "Apparaat", + "Message Title": "Bericht Titel", + "Notification Sound": "Notificatie Geluid", + "More info on:": "Meer info op: {0}", + "pushoverDesc1": "Nood prioriteit (2) heeft standaard een 30 seconden timeout tussen pogingen en verloopt na 1 uur.", + "pushoverDesc2": "Vul het appraat veld in als je notificaties naar andere apparaten wilt versturen.", + "SMS Type": "SMS Type", + "octopushTypePremium": "Premium (Snel - aangeraden voor te alarmeren)", + "octopushTypeLowCost": "Low Cost (Langzaam - wordt soms geblokkeerd door operator)", + "checkPrice": "Controleer {0} prijzen:", + "apiCredentials": "API referenties", + "octopushLegacyHint": "Wil je de legacy versie van Octopush (2011-2020) gebruiken of de nieuwe versie?", + "Check octopush prices": "Controleer Octopush prijzen {0}.", + "octopushPhoneNumber": "Telefoonnummer (Int. formaat, bijv: +33612345678)", + "octopushSMSSender": "SMS zender naam : 3-11 alfanumerieke karakters en spatie (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Apparaat ID", + "Apprise URL": "Apprise URL", + "Example:": "Voorbeeld: {0}", + "Read more:": "Lees meer: {0}", + "Status:": "Status: {0}", + "Read more": "Lees meer", + "appriseInstalled": "Apprise is geïnstalleerd.", + "appriseNotInstalled": "Apprise is niet geïnstalleerd. {0}", + "Access Token": "Access Token", + "Channel access token": "Kanaal access token", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console - {0}", + "Basic Settings": "Basis Instellingen", + "User ID": "Gebruiker ID", + "Messaging API": "Berichten API", + "wayToGetLineChannelToken": "Begin met {0} te openen, creëer een provider en kanaal (Messaging API), dan kun je de kanaal access token en gebruikers ID van de hierboven genoemde menu items krijgen.", + "Icon URL": "Icoon URL", + "aboutIconURL": "Je kunt een link om de standaard profiel afbeelding te overschrijving in \"Icoon URL\" meegeven. Dit wordt niet gebruikt als Icon Emoji is ingesteld.", + "aboutMattermostChannelName": "Je kunt het standaard kanaal dat de Webhook plaatst overschijven door de kanaal naam in te vullen in het \"Channel Name\" veld. Dit moet worden ingeschakeld in de Mattermost Webhook instellingen. Bv. #ander-kanaal", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - Goedkoop maar langzaam en vaak overbelast. Gelimiteerd tot Poolse ontvangers.", + "promosmsTypeFlash": "SMS FLASH - Berichten worden automatisch weergegeven op het apparaat van de ontvanger. Gelimiteerd tot Poolse ontvangers.", + "promosmsTypeFull": "SMS FULL - Premium tier van SMS, je kunt de ontvanger naam gebruiken (Je moet eerst de naam registreren). Betrouwbaar voor alarmeringen.", + "promosmsTypeSpeed": "SMS SPEED - Hoogste prioriteit in systeem. Is veel sneller en betrouwbaarder maar kost meer (ongeveer twee keer zoveel als volle SMS prijs).", + "promosmsPhoneNumber": "Telefoon nummer (voor Poolse ontvangers. Je kunt gebieds codes overslaan)", + "promosmsSMSSender": "SMS Ontvanger naam : Voor geregistreerde naam of een van de standaarden: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu Webhook URL", + "matrixHomeserverURL": "Homeserver URL (met http(s):// en optioneel poort)", + "Internal Room Id": "Interne Room ID", + "matrixDesc1": "Je kunt de interne room ID vinden door in de geavanceerde sectie van de room instellingen in je Matrix client te kijken. Het zou moeten uitzien als !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Het wordt ten zeerste aanbevolen om een nieuwe gebruiker aan te maken en niet de access token van je account te gebruiken, aangezien dit volledige toegang geeft tot je account en alle kamers waar je lid van bent. Maak in plaats daarvan een nieuwe gebruiker aan en nodig deze alleen uit voor de ruimte waarin je de melding wilt ontvangen. Je kunt de access token krijgen door het volgende uit te voeren {0}", + "Monitor History": "Monitor Geschiedenis", + "clearDataOlderThan": "Bewaar monitor geschiedenis voor {0} dagen.", + "PasswordsDoNotMatch": "Wachtwoorden komen niet overeen.", + "records": "gegevens", + "One record": "Een record", + "steamApiKeyDescription": "Om een Steam Game Server te monitoren heb je een Steam Web-API key nodig. Je kunt hier je API key registreren: ", + "Current User": "Huidge Gebruiker", + "topic": "Onderwerp", + "topicExplanation": "MQTT onderwerp om te monitoren", + "successMessage": "Succesbericht", + "successMessageExplanation": "MQTT bericht dat als succes wordt beschouwd", + "recent": "Recent", + "Done": "Klaar", + "Info": "Info", + "Security": "Beveiliging", + "Steam API Key": "Steam API Sleutel", + "Shrink Database": "Verklein Database", + "Pick a RR-Type...": "Kies een RR-Type…", + "Pick Accepted Status Codes...": "Kies geaccepteerde Status Codes…", + "Default": "Standaard", + "HTTP Options": "HTTP Opties", + "Create Incident": "Creëer Incident", + "Title": "Titel", + "Content": "Content", + "Style": "Stijl", + "info": "info", + "warning": "waarschuwing", + "danger": "gevaar", + "primary": "primair", + "light": "licht", + "dark": "donker", + "Post": "Post", + "Please input title and content": "Voer alstublieft titel en content in", + "Created": "Gemaakt", + "Last Updated": "Laatst Bijgewerkt", + "Unpin": "Losmaken", + "Switch to Light Theme": "Wissel naar Licht Thema", + "Switch to Dark Theme": "Wissel naar Donker Thema", + "Show Tags": "Toon Labels", + "Hide Tags": "Verberg Labels", + "Description": "Beschrijving", + "No monitors available.": "Geen monitors beschikbaar.", + "Add one": "Voeg een toe", + "No Monitors": "Geen Monitors", + "Untitled Group": "Naamloze Groep", + "Services": "Diensten", + "Discard": "Weggooien", + "Cancel": "Annuleren", + "Powered by": "Mogelijk gemaakt door", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API Gebruikersnaam (incl. webapi_ prefix)", + "serwersmsAPIPassword": "API Wachtwoord", + "serwersmsPhoneNumber": "Telefoon nummer", + "serwersmsSenderName": "SMS Zender Naam (geregistreerd via klant portaal)", + "stackfield": "Stackfield", + "Customize": "Aanpassen", + "Custom Footer": "Aangepaste Footer", + "Custom CSS": "Aangepaste CSS", + "smtpDkimSettings": "DKIM Instellingen", + "smtpDkimDesc": "Refereer alsjeblieft naar Nodemailer DKIM {0} voor gebruik.", + "documentation": "documentatie", + "smtpDkimDomain": "Domein Naam", + "smtpDkimKeySelector": "Sleutel Kiezer", + "smtpDkimPrivateKey": "Prive Sleutel", + "smtpDkimHashAlgo": "Hash Algoritme (Optioneel)", + "smtpDkimheaderFieldNames": "Header sleutels om te ondertekenen (Optioneel)", + "smtpDkimskipFields": "Header sleutels niet om te ondertekenen (Optioneel)", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API Eindpunt", + "alertaEnvironment": "Omgeving", + "alertaApiKey": "API Sleutel", + "alertaAlertState": "Alert Staat", + "alertaRecoverState": "Herstel Staat", + "deleteStatusPageMsg": "Weet je zeker je deze status pagina wilt verwijderen?", + "Proxies": "Proxies", + "default": "Standaard", + "enabled": "Ingeschakeld", + "setAsDefault": "Stel in als standaard", + "deleteProxyMsg": "Weet je zeker dat je deze proxy wilt verwijderen voor alle monitors?", + "proxyDescription": "Proxies moeten worden toegewezen aan een monitor om te functioneren.", + "enableProxyDescription": "Deze proxy heeft geen effect op monitor verzoeken totdat het is geactiveerd. Je kunt tijdelijk de proxy uitschakelen voor alle monitors voor activatie status.", + "setAsDefaultProxyDescription": "Deze proxy wordt standaard aangezet voor alle nieuwe monitors. Je kunt nog steeds de proxy apart uitschakelen voor elke monitor.", + "Certificate Chain": "Certificaatketen", + "Valid": "Geldig", + "Invalid": "Ongeldig", + "AccessKeyId": "AccessKey ID", + "SecretAccessKey": "AccessKey Secret", + "PhoneNumbers": "TelefoonNummers", + "TemplateCode": "TemplateCode", + "SignName": "SignName", + "Sms template must contain parameters: ": "Sms sjabloon moet de volgende parameters bevatten: ", + "Bark Endpoint": "Bark Endpoint", + "WebHookUrl": "Webhook URL", + "SecretKey": "SecretKey", + "For safety, must use secret key": "Voor de veiligheid moet je de secret key gebruiken", + "Device Token": "Apparaat Token", + "Platform": "Platform", + "Huawei": "Huawei", + "High": "Hoog", + "Retry": "Opnieuw", + "Topic": "Onderwerp", + "WeCom Bot Key": "WeCom Bot Sleutel", + "Setup Proxy": "Stel Proxy in", + "Proxy Protocol": "Proxy Protocol", + "Proxy Server": "Proxy Server", + "Proxy server has authentication": "Proxy server heeft authenticatie", + "User": "Gebruiker", + "Installed": "Geïnstalleerd", + "Not installed": "Niet geïnstalleerd", + "Running": "Actief", + "Not running": "Niet actief", + "Remove Token": "Verwijder Token", + "Start": "Start", + "Stop": "Stop", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Voeg nieuwe status pagina toe", + "Slug": "Slug", + "Accept characters:": "Geaccepteerde tekens:", + "startOrEndWithOnly": "Start of eindig alleen met {0}", + "No consecutive dashes": "Geen opeenvolgende streepjes", + "Next": "Volgende", + "The slug is already taken. Please choose another slug.": "De slug is al in gebruik. Kies een andere slug.", + "No Proxy": "Geen Proxy", + "HTTP Basic Auth": "HTTP Basis Authenticatie", + "New Status Page": "Nieuwe Status Pagina", + "Page Not Found": "Pagina Niet gevonden", + "Reverse Proxy": "Reverse Proxy", + "Backup": "Backup", + "About": "Over", + "wayToGetCloudflaredURL": "(Download cloudflared van {0})", + "cloudflareWebsite": "Cloudflare website", + "Message:": "Bericht:", + "Don't know how to get the token? Please read the guide:": "Lees de uitleg als je niet weet hoe je een token krijgt:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "De huidge verbinding kan worden verbroken als je momenteel bent verbonden met Cloudflare Tunnel. Weet je zeker dat je het wilt stoppen? Typ je huidige wachtwoord om het te bevestigen.", + "Other Software": "Andere Software", + "For example: nginx, Apache and Traefik.": "Bijvoorbeeld: nginx, Apache and Traefik.", + "Please read": "Lees alstublieft", + "Subject:": "Onderwerp:", + "Valid To:": "Geldig Tot:", + "Days Remaining:": "Dagen Resterend:", + "Issuer:": "Uitgever:", + "Fingerprint:": "Vingerafruk:", + "No status pages": "Geen status pagina's", + "Proxy": "Proxy", + "Date Created": "Datum Aangemaakt", + "onebotHttpAddress": "OneBot HTTP Adres", + "onebotMessageType": "OneBot Bericht Type", + "onebotGroupMessage": "Groep", + "onebotPrivateMessage": "Privé", + "onebotUserOrGroupId": "Groep/Gebruiker ID", + "onebotSafetyTips": "Voor de veiligheid moet een toegangssleutel worden ingesteld", + "PushDeer Key": "PushDeer Sleutel", + "Footer Text": "Footer Tekst", + "Show Powered By": "Laat \"Mogeljik gemaakt door\" zien", + "Domain Names": "Domein Namen", + "pushoversounds pushover": "Pushover (standaard)", + "pushoversounds bike": "Fiets", + "pushoversounds bugle": "Trompet", + "pushoversounds cashregister": "Kassa", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Buitenaards", + "pushoversounds falling": "Vallend", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Inkomend", + "pushoversounds intermission": "Pauze", + "pushoversounds magic": "Magie", + "pushoversounds mechanical": "Mechanisch", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Sirene", + "pushoversounds spacealarm": "Ruimte Alarm", + "pushoversounds tugboat": "Tug Boat", + "pushoversounds alien": "Alien Alarm (long)", + "pushoversounds climb": "Climb (long)", + "pushoversounds persistent": "Aanhoudend (lang)", + "pushoversounds echo": "Pushover Echo (long)", + "pushoversounds updown": "Up Down (long)", + "pushoversounds vibrate": "Alleen trillen", + "pushoversounds none": "Geen (stil)", + "dnsPortDescription": "DNS-serverpoort. Standaard ingesteld op 53. Je kunt de poort op elk moment wijzigen.", + "error": "fout", + "critical": "kritisch", + "wayToGetPagerDutyKey": "Je kunt dit krijgen door naar Service -> Service Directory -> (Selecteer een service) -> Integraties -> Integratie toevoegen te gaan. Hier kunt u zoeken naar \"Events API V2\". Meer informatie {0}", + "Integration Key": "Integratie-key", + "Integration URL": "Integratie-URL", + "Auto resolve or acknowledged": "Automatisch oplossen of bevestigen", + "do nothing": "niets doen", + "auto acknowledged": "automatisch bevestigen", + "auto resolve": "automatisch oplossen", + "Authentication": "Authenticatie", + "signedInDisp": "Aangemeld als {0}", + "signedInDispDisabled": "Authenticatie uitgeschakeld.", + "Certificate Expiry Notification": "Melding over verlopen certificaat", + "Recipient Number": "Nummer ontvanger", + "From Name/Number": "Van naam/nummer", + "Leave blank to use a shared sender number.": "Laat leeg om een gedeeld afzendernummer te gebruiken.", + "endpoint": "endpoint", + "pushyAPIKey": "Secret API Key", + "pushyToken": "Apparaat token", + "Show update if available": "Update weergeven indien beschikbaar", + "Also check beta release": "Controleer ook de bètaversies", + "Using a Reverse Proxy?": "Een reverse proxy gebruiken?", + "Check how to config it for WebSocket": "Controleer hoe je het configureert voor een WebSocket", + "Steam Game Server": "Steam gameserver", + "Most likely causes:": "Meest waarschijnlijke oorzaken:", + "The resource is no longer available.": "De paginabron is niet langer beschikbaar.", + "There might be a typing error in the address.": "Er zit een typefout in het de URL.", + "What you can try:": "Wat je kan proberen:", + "Retype the address.": "De URL controleren en/of opnieuw typen.", + "Go back to the previous page.": "Terug naar de vorige pagina.", + "Coming Soon": "Binnenkort beschikbaar", + "wayToGetClickSendSMSToken": "Je kunt een API gebruikersnaam en API sleutel verkrijgen van {here}.", + "Connection String": "Connectie-string", + "Query": "Query", + "settingsCertificateExpiry": "TLS Certificaat verloopdatum", + "certificationExpiryDescription": "Stuur een melding bij het verlopen van het HTTPS TLS certificaat in:", + "ntfy Topic": "ntfy Topic", + "Domain": "Domein", + "Workstation": "Werkstation", + "disableCloudflaredNoAuthMsg": "De \"Geen authenticatie\" modus staat aan, wachtwoord is niet vereist.", + "backupOutdatedWarning": "Deprecated: Er zijn een hoop nieuwe functies toegevoegd en daarom is de backup functie niet onderhouden, het is op dit moment niet mogelijk om een volledige backup te maken en te herstellen.", + "RadiusSecret": "Radius Geheim", + "RadiusSecretDescription": "Shared Secret tussen client en server", + "API Key": "API Sleutel", + "Connection Type": "Verbindingstype", + "Docker Daemon": "Docker daemon", + "Trust Proxy": "Trust Proxy", + "Setup Docker Host": "Stel Docker Host in", + "tcp": "TCP / HTTP", + "Optional": "Optioneel", + "socket": "Socket", + "Docker Container": "Docker container", + "Container Name / ID": "Container Naam / ID", + "Docker Host": "Docker host", + "Docker Hosts": "Docker hosts", + "Packet Size": "Packet Grootte", + "wayToGetLineNotifyToken": "Je kan een Access Token van {0} krijgen", + "Examples": "Voorbeelden", + "Home Assistant URL": "Home Assistant URL", + "default: notify all devices": "Standaard: stuur melding naar alle apparaten", + "Automations can optionally be triggered in Home Assistant:": "Automations kunnen optioneel worden getriggerd in Home Assistant:", + "Event data:": "Event data:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Kies een actie, bijvoorbeeld het activeren van een scene.", + "Frontend Version": "Frontend Versie", + "Frontend Version do not match backend version!": "Frontend versie komt niet overeen niet met de backend versie!", + "backupRecommend": "In plaats daarvan, maak een backup van je Docker volume of de data map (./data/).", + "squadcast": "Squadcast", + "or": "of", + "recurringInterval": "Interval", + "Recurring": "Terugkerend", + "strategyManual": "Actief/Inactief handmatig", + "warningTimezone": "De tijdzone van de server wordt gebruikt", + "weekdayShortMon": "ma", + "weekdayShortTue": "di", + "weekdayShortWed": "wo", + "weekdayShortThu": "do", + "weekdayShortFri": "vr", + "weekdayShortSat": "za", + "weekdayShortSun": "zo", + "dayOfWeek": "Dag van de week", + "dayOfMonth": "Dag van de maand", + "lastDay": "Laatste dag", + "lastDay2": "1 na laatste dag van de maand", + "lastDay4": "3 na laatste dag van de maand", + "No Maintenance": "Geen onderhoud", + "pauseMaintenanceMsg": "Weet je zeker dat je wilt pauzeren?", + "maintenanceStatus-under-maintenance": "In onderhoud", + "maintenanceStatus-inactive": "Inactief", + "maintenanceStatus-scheduled": "Ingepland", + "maintenanceStatus-ended": "Beëindigd", + "Display Timezone": "Toon tijdzone", + "Server Timezone": "Server tijdzone", + "statusPageMaintenanceEndDate": "Einde", + "IconUrl": "Icoon URL", + "Enable DNS Cache": "(Verouderd) Schakel DNS-cache in voor HTTP(s)-monitors", + "Enable": "Inschakelen", + "Disable": "Uitschakelen", + "Single Maintenance Window": "Enkel onderhoudsperiode", + "Effective Date Range": "Effectieve periode (Optioneel)", + "Schedule Maintenance": "Onderhoud inplannen", + "Date and Time": "Datum en tijd", + "DateTime Range": "Datum en tijd periode", + "wayToGetZohoCliqURL": "Via deze link kun je uitvinden hoe je een webhook URL maakt {0}.", + "dataRetentionTimeError": "Bewaarperiode moet 0 of groter zijn", + "infiniteRetention": "Stel in op 0 voor oneindige bewaarperiode.", + "enableGRPCTls": "Toestaan om gRPC aanvragen te sturen over TLS verbinding", + "deleteMaintenanceMsg": "Weet je zeker dat je dit onderhoud wilt verwijderen?", + "recurringIntervalMessage": "1 keer per dag uitvoeren | 1 keer per elke {0} dagen uitvoeren", + "affectedStatusPages": "Toon het onderhoudsbericht op de geselecteerde status pagina's", + "promosmsPassword": "API Wachtwoord", + "Kook": "Kook", + "high": "hoog", + "Base URL": "Base URL", + "goAlert": "GoAlert", + "Octopush API Version": "Octopush API versie", + "HomeAssistant": "Home Assistant", + "affectedMonitorsDescription": "Selecteer de monitors die zullen worden aangetast door het huidige onderhoud", + "Custom": "Aangepast", + "Affected Monitors": "Aangetaste monitors", + "Resend Notification if Down X times consequently": "Verzend offline melding X keer opnieuw bij blijvend offline", + "Monitor": "Monitor | Monitoren", + "Start of maintenance": "Start van onderhoud", + "All Status Pages": "Alle status pagina's", + "Select status pages...": "Selecteer status pagina's…", + "API Username": "API Gebruikersnaam", + "Trigger type:": "Trigger type:", + "Event type:": "Event type:", + "Guild ID": "Guild ID", + "uninstalling": "Aan het verwijderen", + "Lowcost": "Lowcost", + "Economy": "Economy", + "webhookAdditionalHeadersTitle": "Extra Headers", + "webhookAdditionalHeadersDesc": "Voegt extra headers toe die meegestuurd worden met de webhook. Iedere header moet een JSON key/value zijn.", + "Help": "Hulp", + "Game": "Spel", + "statusMaintenance": "Onderhoud", + "Maintenance": "Onderhoud", + "Passive Monitor Type": "Passief Monitor Type", + "Pick Affected Monitors...": "Kies aangetaste monitors…", + "Specific Monitor Type": "Specifiek Monitor Type", + "promosmsLogin": "API Login naam", + "Schedule maintenance": "Onderhoud inplannen", + "resendEveryXTimes": "Verstuur elke {0} keer opnieuw", + "resendDisabled": "Opnieuw versturen uitgeschakeld", + "General Monitor Type": "Algemeen Monitor Type", + "Notification Service": "Melding diensten", + "uninstall": "Verwijderen", + "HTTP Headers": "HTTP Headers", + "Domain Name Expiry Notification": "Domeinnaam verlopen melding", + "deleteDockerHostMsg": "Weet je zeker dat je deze Docker host wilt verwijderen voor alle monitors?", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Een lijst van melding diensten kan worden gevonden in Home Assistant onder \"Developer Tools > Services\" en zoek voor \"notification\" om je apparaat/telefoon naam te vinden.", + "lastDay1": "Laatste dag van de maand", + "lastDay3": "2 na laatste dag van de maand", + "maintenanceStatus-unknown": "Onbekend", + "dnsCacheDescription": "Het werkt niet in sommige IPv6 omgevingen, schakel het uit als je problemen ervaart.", + "confirmDeleteTagMsg": "Weet je zeker dat je dit label wilt verwijderen? Monitors die gekoppeld zijn aan dit label worden niet verwijderd.", + "atLeastOneMonitor": "Selecteer tenminste 1 aangetaste monitor", + "Enable TLS": "TLS inschakelen", + "smseagle": "SMSEagle", + "smseagleTo": "Telefoonnummer(s)", + "Custom Monitor Type": "Eigen Monitor Type", + "trustProxyDescription": "'X-Forwarded-*' headers vertrouwen. Als je de correcte client IP wilt krijgen en de Uptime Kuma installatie is achter een proxy zoals Nginx of Apache, schakel dan dit in.", + "RadiusCalledStationId": "Genoemde stations ID", + "RadiusCalledStationIdDescription": "Identificatie van het genoemde apparaat", + "RadiusCallingStationId": "Calling stations-ID", + "ZohoCliq": "ZohoCliq", + "Long-Lived Access Token": "Long-Lived Access Token", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Long-Lived Access Token kan aangemaakt worden via je profiel naam (linksonder) en door naar beneden te scrollen en te klikken op Token Aanmaken.", + "Maintenance Time Window of a Day": "Onderhoud tijdsvak van een dag", + "octopushAPIKey": "\"API key\" van HTTP API inloggegevens van het controle paneel", + "octopushLogin": "\"Login\" van HTTP API inloggegevens controle paneel", + "grpcMethodDescription": "Methodenaam moet in camelCase formaat zijn zoals zegHallo, check, etc.", + "wayToGetKookBotToken": "Maak een applicatie en haal je bot token op bij {0}", + "wayToGetKookGuildID": "Switch naar 'Developer Mode' in de Kook instellingen, en klik met de rechter muisknop op de guild om de ID op te halen", + "Strategy": "Strategie", + "Free Mobile User Identifier": "Free Mobile gebruikers ID", + "Free Mobile API Key": "Free Mobile API Sleutel", + "Proto Service Name": "Proto service naam", + "Proto Method": "Proto methode", + "Proto Content": "Proto inhoud", + "SendKey": "SendKey", + "SMSManager API Docs": "SMSManager API documentatie", + "Gateway Type": "Gateway-type", + "SMSManager": "SMSManager", + "You can divide numbers with": "Je kunt nummers delen met", + "Bark Group": "Bark Groep", + "Bark Sound": "Bark Geluid", + "promosmsAllowLongSMS": "Sta lange SMS toe", + "smseagleRecipientType": "Ontvanger type", + "smseagleRecipient": "Ontvanger(s) (gescheiden met comma)", + "smseagleToken": "API access token", + "smseagleEncoding": "Stuur als Unicode", + "smseaglePriority": "Bericht prioriteit (0-9, standaard = 0)", + "Legacy Octopush-DM": "Legacy Octopush-DM", + "smseagleGroup": "Telefoonboek groep namen", + "Google Analytics ID": "Google Analytics ID", + "Edit Tag": "Tag bewerken", + "Server Address": "Server Adres", + "Learn More": "Meer leren", + "RadiusCallingStationIdDescription": "Identificatie van het bellende device", + "plugin": "Plugin | Plugins", + "installing": "Installeren", + "install": "Installeer", + "confirmUninstallPlugin": "Weet je zeker dat je deze plugin wilt verwijderen?", + "smseagleUrl": "SMSEagle apparaat URL", + "markdownSupported": "Markdown syntax ondersteund. Gebruik je HTML, vermijd dan spaties aan het begin om opmaakproblemen te voorkomen.", + "Resend Notification if Down X times consecutively": "Melding x keer opnieuw sturen als monitor offline is", + "loadingError": "Kan de data niet ophalen, probeer het later opnieuw.", + "smseagleContact": "Telefoonboek contact namen", + "apiKey-active": "Actief", + "apiKey-expired": "Verlopen", + "pagertreeLow": "Laag", + "pagertreeHigh": "Hoog", + "Clone": "Dupliceer", + "cloneOf": "Duplicaat van {0}", + "Add New Tag": "Voeg nieuw label toe", + "Body Encoding": "Body Encoding", + "twilioAuthToken": "Auth Token / Api Sleutel Secret", + "twilioAccountSID": "Account SID", + "Badge Preview": "Badge voorbeeld", + "ntfyAuthenticationMethod": "Authenticatiemethode", + "ntfyUsernameAndPassword": "Gebruikersnaam en Wachtwoord", + "twilioApiKey": "Api Sleutel (optioneel)", + "Badge Label Prefix": "Badge Label Voorvoegsel", + "Badge Label Suffix": "Badge label achtervoegsel", + "API Keys": "API Sleutels", + "Expiry": "Verval", + "noGroupMonitorMsg": "Niet beschikbaar. Creëer eerst een Groep Monitor.", + "Notify Channel": "Notify Channel", + "Expiry date": "Vervaldatum", + "Key Added": "Sleutel toegevoegd", + "Badge value (For Testing only.)": "Badgewaarde (Alleen voor testen)", + "aboutNotifyChannel": "Notify channel activeert een melding op bureaublad of mobiel voor alle leden van de channel, ongeacht of hun beschikbaarheid is ingesteld op actief of afwezig.", + "apiKey-inactive": "Inactief", + "disableAPIKeyMsg": "Weet je zeker dat je deze API-sleutel wilt uitschakelen?", + "Show Clickable Link Description": "Als deze optie is aangevinkt, heeft iedereen die toegang heeft tot deze statuspagina toegang tot de monitor URL.", + "Badge Duration (in hours)": "Duur badge (in uren)", + "Badge Maintenance Color": "Badge Onderhoud Kleur", + "Badge URL": "Badge URL", + "Close": "Sluit", + "Request Body": "Request Body", + "pagertreeIntegrationUrl": "Integratie URL", + "pagertreeUrgency": "Urgentie", + "pagertreeSilent": "Stil", + "telegramMessageThreadID": "(Optioneel) Berichtthread-ID", + "Clone Monitor": "Kloon Monitor", + "Expires": "Vervalt", + "webhookCustomBodyDesc": "Definieer een aangepaste HTTP Body voor de request. Template variabelen {msg}, {heartbeat}, {monitor} worden geaccepteerd.", + "webhookBodyPresetOption": "Voorinstelling - {0}", + "webhookBodyCustomOption": "Aangepaste Body", + "notificationRegional": "Regionaal", + "No API Keys": "Geen API Sleutels", + "apiKeyAddedMsg": "Je API-sleutel is toegevoegd. Noteer deze, want hij wordt niet meer weergegeven.", + "Add API Key": "Voeg API Sleutel toe", + "telegramSendSilently": "Stil sturen", + "telegramSendSilentlyDescription": "Stille verzending van het bericht. Gebruikers ontvangen een melding zonder geluid.", + "Home": "Home", + "Don't expire": "Verval nooit", + "Continue": "Ga verder", + "Add Another": "Nog een toevoegen", + "lunaseaTarget": "Doel", + "lunaseaDeviceID": "Apparaat ID", + "lunaseaUserID": "Gebruiker ID", + "Badge Color": "Badge kleur", + "wayToGetPagerTreeIntegrationURL": "Nadat je de Uptime Kuma-integratie in PagerTree hebt gemaakt, kopieert je het eindpunt. Bekijk alle details {0}", + "Badge Warn Color": "Badge Waarschuwing Kleur", + "Invert Keyword": "Trefwoord omkeren", + "filterActive": "Actief", + "filterActivePaused": "Gepauzeerd", + "statusPageRefreshIn": "Ververs in: {0}", + "telegramMessageThreadIDDescription": "Optioneel Unieke identifier voor de doelthread (onderwerp) van het forum; alleen voor forumsupergroepen", + "telegramProtectContentDescription": "Indien ingeschakeld, worden de botberichten in Telegram beschermd tegen doorsturen en opslaan.", + "telegramProtectContent": "Bescherm doorsturen/opslaan", + "sameAsServerTimezone": "Hetzelfde als Server Tijdzone", + "startDateTime": "Begindatum/-tijd", + "endDateTime": "Einddatum/-tijd", + "cronExpression": "Cron-expressie", + "cronSchedule": "Schema: ", + "invalidCronExpression": "Ongeldige Cron-expressie: {0}", + "chromeExecutableDescription": "Voor Docker-gebruikers, als Chromium nog niet is geïnstalleerd, kan het een paar minuten duren om te installeren en het testresultaat weer te geven. Het neemt 1GB schijfruimte in beslag.", + "invertKeywordDescription": "Kijk of het trefwoord afwezig is in plaats van aanwezig.", + "pushoverMessageTtl": "Bericht TTL (seconden)", + "goAlertInfo": "GoAlert is een open source applicatie voor het plannen van aanwezigheidsdiensten, geautomatiseerde escalaties en meldingen (zoals SMS of spraakoproepen). Schakel automatisch de juiste persoon in, op de juiste manier en op het juiste moment! {0}", + "goAlertIntegrationKeyInfo": "Verkrijg generieke API-integratiesleutel voor de service in dit formaat \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" meestal de waarde van tokenparameter van gekopieerde URL.", + "deleteAPIKeyMsg": "Weet je zeker dat je deze API-sleutel wilt verwijderen?", + "Generate": "Genereer", + "pagertreeMedium": "Medium", + "pagertreeCritical": "Kritisch", + "pagertreeResolve": "Automatisch oplossen", + "pagertreeDoNothing": "Doe niks", + "twilioFromNumber": "Van Nummer", + "twilioToNumber": "Naar Nummer", + "Monitor Setting": "{0}'s Monitor Instelling", + "Show Clickable Link": "Laat klikbare link zien", + "Open Badge Generator": "Open Badge Generator", + "Badge Generator": "{0}'s Badge Generator", + "Badge Type": "Badge type", + "Badge Up Color": "Badge Online Kleur", + "Badge Down Color": "Badge Offline Kleur", + "Badge Warn Days": "Badge Waarschuwing dagen", + "Badge Down Days": "Badge Offline dagen", + "Badge Style": "Badge stijl", + "chromeExecutable": "Chrome/Chromium Uitvoerbaar bestand", + "chromeExecutableAutoDetect": "Automatisch detecteren", + "Edit Maintenance": "Onderhoud bewerken", + "Badge Label": "Badge Label", + "Badge Label Color": "Badge label kleur", + "Badge Prefix": "Badge Waarde Voorvoegsel", + "Badge Pending Color": "Badge In Afwachting Kleur", + "Badge Suffix": "Badge waarde achtervoegsel", + "Group": "Groep", + "Monitor Group": "Monitor Groep", + "Cannot connect to the socket server": "Kan geen verbinding maken met de socket-server", + "Reconnecting...": "Opnieuw verbinden...", + "Expected Value": "Verwachte waarde", + "Json Query": "Json zoekopdracht", + "pushViewCode": "Hoe gebruik je Push monitor?(View Code)", + "setupDatabaseChooseDatabase": "Welke database wil je gebruiken?", + "setupDatabaseEmbeddedMariaDB": "Je hoeft niks in te stellen. Deze Docker image heeft een ingebouwde en geconfigureerde MariaDB instantie. Uptime Kuma verbindt met deze database via een Unix socket.", + "setupDatabaseMariaDB": "Verbind met een externe MariaDB database. Je moet de database-connectie informatie instellen.", + "setupDatabaseSQLite": "Een simpel database bestand, aanbevolen voor kleinschalige implementaties. Voor v2.0.0, maakte Uptime Kuma standaard gebruik van een SQLite database.", + "dbName": "Database naam", + "Request Timeout": "Verzoek Timeout", + "pushOthers": "Anderen", + "programmingLanguages": "Programmeertalen", + "styleElapsedTime": "Verstreken tijd onder de hartslagbalk", + "styleElapsedTimeShowNoLine": "Laat zien (Geen lijn)", + "styleElapsedTimeShowWithLine": "Laat zien (Met lijn)", + "timeoutAfter": "Timeout na {0} seconden", + "Select": "Selecteer", + "Server URL should not contain the nfty topic": "De server-URL mag het nfty-onderwerp niet bevatten", + "templateMsg": "bericht van de melding", + "liquidIntroduction": "Templatabiliteit wordt bereikt via de Liquid-templatingtaal. Raadpleeg de {0} voor gebruiksinstructies.", + "emailTemplateLimitedToUpDownNotification": "alleen beschikbaar voor UP/DOWN heartbeats, anders null", + "enableNSCD": "Schakel NSCD (Name Service Cache Daemon) in voor het cachen van alle DNS-verzoeken", + "smtpLiquidIntroduction": "De volgende twee velden zijn in te stellen via de Liquid-templatingtaal. Raadpleeg de {0} voor gebruiksinstructies. Dit zijn de beschikbare variabelen:", + "emailCustomisableContent": "Aanpasbare inhoud", + "Reset Token": "Hersteltoken", + "noDockerHostMsg": "Niet beschikbaar. Stel eerst een Docker host in.", + "DockerHostRequired": "Stel de Docker host voor deze monitor in.", + "leave blank for default subject": "laat leeg voor standaard onderwerp", + "emailCustomBody": "Aangepaste inhoud", + "leave blank for default body": "laat leeg voor standaard inhoud", + "emailTemplateServiceName": "Service naam", + "emailTemplateHostnameOrURL": "Hostnaam of URL", + "emailTemplateStatus": "Status", + "emailTemplateMonitorJSON": "object dat de monitor beschrijft", + "emailTemplateHeartbeatJSON": "object dat de heartbeat beschrijft", + "emailTemplateMsg": "meldingsbericht", + "PushDeer Server": "PushDeer-server", + "Kafka Brokers": "Kafka brokers", + "Enter the list of brokers": "Voer de lijst met brokers in", + "monitorToastMessagesLabel": "Monitor toast-meldingen", + "monitorToastMessagesDescription": "Toast-meldingen voor monitors verdwijnen na het opgegeven aantal meldingen. Zet dit op -1 om het verdwijnen uit te zetten. Zet op 0 om meldingen helemaal uit te zetten.", + "toastErrorTimeout": "Timeout voor foutmeldingen", + "toastSuccessTimeout": "Timeout voor succesmeldingen", + "Press Enter to add broker": "Druk op enter om een broker toe te voegen", + "Bark API Version": "Bark API-versie", + "templateHeartbeatJSON": "object dat de heartbeat beschrijft", + "templateMonitorJSON": "object dat de monitor beschrijft", + "templateLimitedToUpDownCertNotifications": "alleen beschikbaar voor UP/DOWN/Certificaatverloop notificaties", + "templateLimitedToUpDownNotifications": "alleen beschikbaar voor UP/DOWN notificaties", + "pushDeerServerDescription": "Laat dit veld leeg om de officiële server te gebruiken", + "Check/Uncheck": "Vink/Ontvink", + "tailscalePingWarning": "Om de Tailscale Ping-monitor te kunnen gebruiken, moet u Uptime Kuma zonder Docker installeren en ook de Tailscale-client op uw server installeren.", + "selectedMonitorCount": "Geselecteerd: {0}", + "wayToGetFlashDutyKey": "Ga naar Kanaal -> (Kies een kanaal) -> Integraties -> Voeg een nieuwe integratie toe, voeg een nieuwe 'Uptime Kuma' toe om een push-adres te verkrijgen, kopieer de integratiesleutel in het adres-veld.", + "gamedigGuessPortDescription": "De poort die wordt gebruikt door het Valve Server Query Protocol kan verschillen van de clientpoort. Probeer dit als de monitor geen verbinding kan maken met je server.", + "authUserInactiveOrDeleted": "De gebruiker is inactief of verwijderd.", + "authInvalidToken": "Ongeldig token.", + "authIncorrectCreds": "Ongeldige gebruikersnaam of wachtwoord.", + "2faAlreadyEnabled": "2FA is al ingeschakeld.", + "2faEnabled": "2FA Ingeschakeld.", + "2faDisabled": "2FA Uitgeschakeld.", + "successAdded": "Succesvol toegevoegd.", + "successResumed": "Succesvol doorgegaan.", + "successPaused": "Succesvol gepauzeerd.", + "successDeleted": "Succesvol verwijderd.", + "successEdited": "Succesvol bewerkt.", + "GrafanaOncallUrl": "Grafana Oncall URL", + "Kafka SASL Options": "Kafka SASL opties", + "Pick a SASL Mechanism...": "Kies een SASL mechanisme…", + "Enable Kafka SSL": "Kafka SSL inschakelen", + "Kafka Topic Name": "Kafka Topicnaam", + "Kafka Producer Message": "Kafka Producer bericht", + "Enable Kafka Producer Auto Topic Creation": "Kafka Producer automatische topic aanmaak inschakelen", + "Mechanism": "Mechanisme", + "Authorization Identity": "Autorisatie-identiteit", + "AccessKey Id": "AccessKey Id", + "Secret AccessKey": "Geheim AccessKey", + "Session Token": "Sessietoken", + "FlashDuty Severity": "Hevigheid", + "nostrRelays": "Nostr relays", + "nostrRelaysHelp": "Een relay-URL per regel", + "nostrSender": "Private key van de afzender (nsec)", + "nostrRecipients": "Publieke sleutels van de ontvangers (npub)", + "nostrRecipientsHelp": "npub formaat, een per regel", + "showCertificateExpiry": "Toon certificaat verloopdatum", + "noOrBadCertificate": "Geen/Fout certificaat", + "gamedigGuessPort": "Gamedig: Guess poort", + "Saved.": "Opgeslagen.", + "successAuthChangePassword": "Wachtwoord is succesvol gewijzigd.", + "successBackupRestored": "Backup succesvol teruggezet.", + "successDisabled": "Succesvol uitgeschakeld.", + "successEnabled": "Succesvol ingeschakeld.", + "tagNotFound": "Tag niet gevonden.", + "foundChromiumVersion": "Chromium/Chrome gevonden. Versie {0}", + "Browser Screenshot": "Browser Screenshot", + "successKeyword": "Succes trefwoord", + "successKeywordExplanation": "MQTT Keyword dat als succes wordt gezien", + "Add a new expiry notification day": "Voeg een nieuwe dag voor een vervaldatummelding toe", + "Remove the expiry notification": "Verwijder de melding voor de vervaldatum", + "Remote Browser": "Remote Browser", + "Add a Remote Browser": "Voeg een Remote Browser toe", + "Remote Browsers": "Remote Browsers", + "self-hosted container": "Self-hosted container", + "useRemoteBrowser": "Gebruik een Remote Browser", + "Remote Browser not found!": "Remote Browser niet gevonden!", + "remoteBrowsersDescription": "Remote Browsers zijn een alternatief voor het lokaal draaien van Chromium. Stel in via een service zoals browserless.io of verbind je eigen", + "deleteRemoteBrowserMessage": "Weet je zeker dat je deze Remote Browser voor alle monitors wilt verwijderen?", + "remoteBrowserToggle": "Chromium draait standaard in de Uptime Kuma container. Door dit aan te zetten kun je een remote browser gebruiken.", + "setup a new monitor group": "Stel een nieuwe monitorgroep in", + "Add a domain": "Voeg een domein toe", + "Remove domain": "Verwijder domein '{0}'", + "openModalTo": "Modal openen naar {0}", + "settingUpDatabaseMSG": "Database wordt ingesteld. Dit kan even duren, dus wees geduldig.", + "Search monitored sites": "Zoek naar gemonitorde sites", + "statusPageSpecialSlugDesc": "Speciale padnaam {0}: deze pagina wordt getoond als er geen padnaam wordt opgegeven", + "ntfyPriorityHelptextAllEvents": "Alle meldingen worden verzonden met de hoogste prioriteit", + "ntfyPriorityHelptextAllExceptDown": "Alle meldingen worden verzonden met deze prioriteit, behalve {0}-meldingen, die hebben een prioriteit van {1}", + "What is a Remote Browser?": "Wat is een remote webbrowser?", + "Your User ID": "Jouw gebruikers ID", + "Channel access token (Long-lived)": "Kanaal access token (blijvend)", + "documentationOf": "{0} Documentatie", + "wayToGetHeiiOnCallDetails": "Hoe de Trigger ID en de API Keys verkregen kunnen worden is terug te lezen in de {documentation}", + "To Phone Number": "Naar telefoonnummer", + "Telephone number": "Telefoonnummer", + "max 15 digits": "max 15 cijfers", + "max 11 alphanumeric characters": "max 11 alfanumerieke tekens", + "Allow Long SMS": "Lange SMS toestaan", + "Destination": "Bestemming", + "whapiRecipient": "Telefoonnummer / Contact-ID / Groeps-ID", + "API URL": "API URL", + "wayToGetWhapiUrlAndToken": "Je kunt de API-URL en de token verkrijgen door naar het gewenste kanaal te gaan vanaf {0}", + "gtxMessagingFromHint": "Op mobiele telefoons, de ontvangers zien het TPOA als de afzender van het bericht. Toegestaan zijn maximaal 11 alfanumerieke karakters, een shortcode, het lokale longcode of internationale nummers ({e164}, {e212} of {e214})", + "gtxMessagingToHint": "Internationaal formaat, met \"+\" aan het begin ({e164}, {e212} of {e214})", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Van telefoonnummer / Transmission Path Originating Address (TPOA)", + "gtxMessagingApiKeyHint": "Je kan je API key vinden bij: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "Alphanumeric (recommended)": "Alfanumeriek (aanbevolen)", + "cellsyntOriginatortypeAlphanumeric": "Alfanumerieke reeks (max. 11 alfanumerieke karakters). Ontvangers kunnen niet op het bericht reageren.", + "cellsyntOriginatortypeNumeric": "Numerieke waarde (max. 15 cijfers) met telefoonnummer met landcode zonder + aan het begin (een Nederlands nummer 06 12 34 56 78 moet bijvoorbeeld worden ingesteld als 31612345678). Ontvangers kunnen op het bericht reageren.", + "Originator type": "Afzender type", + "cellsyntSplitLongMessages": "Scheid lange berichten op in 6 delen. 153 x 6 = 918 karakters.", + "callMeBotGet": "Hier kan je een eindpunt genereren voor {0}, {1} en {2}. Houd er wel rekening mee dat er een maximaal aantal aanvragen zijn. Het maximum lijkt te zijn: {3}", + "cellsyntOriginator": "Zichtbaar op de mobiele telefoon van de ontvanger als afzender van het bericht. Toegestane waarden en functie zijn afhankelijk van het parameter-originatortype.", + "Originator": "Afzender", + "cellsyntDestination": "Ontvanger's telefoonnummer met landcode zonder + aan het begin (een Nederlands nummer 06 12 34 56 78 moet bijvoorbeeld worden ingesteld als 31612345678). Maximaal 25.000 door komma's gescheiden ontvangers per HTTP-verzoek.", + "wayToWriteWhapiRecipient": "Het telefoonnummer met landcode, maar zonder + aan het begin ({0}), het contact ID ({1}) of groep ID ({2}).", + "locally configured mail transfer agent": "lokaal geconfigureerde mail agent", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Geef de hostnaam van de server waar je naar wilt verbinden op of {localhost} als je een {local-mta} wilt gebruiken", + "Don't mention people": "Vermeld geen mensen", + "wayToGetSevenIOApiKey": "Bezoek het dashboard onder app.seven.io > developer > api key > de groene knop", + "senderSevenIO": "Verzenden van nummer of naam", + "Host URL": "Host URL", + "Mention group": "Vermeld {group}", + "Mentioning": "Vermelden", + "receiverSevenIO": "Nummer ontvangen", + "receiverInfoSevenIO": "Als het ontvangende nummer zich niet in Duitsland bevindt, moet u de landcode vóór het nummer zetten (bijvoorbeeld voor de landcode 1 uit de VS gebruikt u 117612121212 in plaats van 017612121212)", + "apiKeySevenIO": "SevenIO API Sleutel", + "wayToGetThreemaGateway": "Je kunt je registreren voor Threema Gateway {0}.", + "threemaRecipient": "Ontvanger", + "threemaRecipientType": "Type ontvanger", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypeIdentityFormat": "8 Karakters", + "threemaRecipientTypePhone": "Telefoonnummer", + "threemaRecipientTypePhoneFormat": "E.164, zonder voorgaande +", + "threemaSenderIdentityFormat": "8 karakters, begint normaal met een *", + "smspartnerPhoneNumber": "Telefoonnummer(s)", + "smspartnerSenderNameInfo": "Moet tussen 3..=11 reguliere karakters zijn", + "Bitrix24 Webhook URL": "Bitrix24 webhook URL", + "wayToGetBitrix24Webhook": "Je kunt een webhook maken door de stappen bij {0} te volgen", + "bitrix24SupportUserID": "Voer je gebruikers ID van Bitrix25 in. Je kunt dit achterhalen uit de link naar je gebruikersprofiel.", + "apiKeysDisabledMsg": "API-Sleutels zijn uitgeschakeld omdat authenticatie niet is ingeschakeld.", + "smspartnerApiurl": "Je kunt je API-sleutel vinden in je dashboard bij {0}", + "smspartnerPhoneNumberHelptext": "Het nummer moet in het internationale format {0}, {1} zijn. Meerdere nummers moeten gescheiden zijn door {2}", + "Command": "Commando", + "mongodbCommandDescription": "Draai een MongoDB commando tegen de database. Voor meer informatie over beschikbare commando's, raadpleeg de {documentation}", + "threemaBasicModeInfo": "Notitie: Deze integratie gebruikt Threema Gateway in de basis modus (server gebaseerde versleuteling). Meer details vind je hier {0}.", + "Select message type": "Selecteer bericht type", + "Send to channel": "Stuur naar kanaal", + "Create new forum post": "Maak nieuw forum bericht", + "Refresh Interval": "Ververs interval", + "Refresh Interval Description": "De status pagina zal elke {0} seconden een volledige website verversing doen", + "ignoreTLSErrorGeneral": "Negeer TLS/SSL-fout voor verbinding", + "forumPostName": "Forum bericht naam", + "threadForumPostID": "Thread / Forum bericht ID", + "e.g. {discordThreadID}": "bijv. {discordThreadID}", + "whatHappensAtForumPost": "Maak een nieuw forum bericht. Dit stuurt GEEN berichten in bestaande forum berichten. Om naar een bestaand forum een nieuw bericht te sturen, gebruik \"{option}\"", + "postToExistingThread": "Plaats op bestaande thread / forum bericht", + "wayToGetDiscordThreadId": "Verkrijgen van een thread / forum bericht id is vergelijkbaar met het verkrijgen van een kanaal id. Lees meer over het verkrijgen van ids {0}", + "threemaRecipientTypeEmail": "E-mailadres", + "threemaSenderIdentity": "Gateway ID", + "threemaApiAuthenticationSecret": "Gateway ID Geheim", + "smspartnerSenderName": "SMS afzender naam", + "and": "en", + "snmpCommunityStringHelptext": "Deze string fungeert als een wachtwoord om toegang tot SNMP-apparaten te verifiëren en te beheren. Match het met de configuratie van uw SNMP-apparaat.", + "groupOnesenderDesc": "Zorg ervoor dat de GroupID juist is. Om een bericht naar een groep te sturen, bijvoorbeeld: 628123456789-342345", + "privateOnesenderDesc": "Zorg ervoor dat het telefoonnummer juist is. Om een bericht te sturen naar een privénummer, bijvoorbeeld: 628123456789", + "now": "nu", + "time ago": "{0} geleden", + "-year": "-jaar", + "cacheBusterParamDescription": "Willekeurig gegenereerde parameter om caches over te slaan.", + "OID (Object Identifier)": "OID (Object indentificatie)", + "snmpOIDHelptext": "Voer de OID in voor de sensor of status die u wilt monitoren. Gebruik netwerkbeheertools zoals MIB-browsers of SNMP-software als u niet zeker bent over de OID.", + "Condition": "Conditie", + "SNMP Version": "SNMP Versie", + "Please enter a valid OID.": "Voer a.u.b. een geldige OID in.", + "Host Onesender": "Host Onesender", + "Token Onesender": "Token Onesender", + "Recipient Type": "Ontvanger Type", + "Private Number": "Privénummer", + "Group ID": "Groep ID", + "wayToGetOnesenderUrlandToken": "U kunt de URL en Token krijgen door naar de Onesender website te gaan. Meer informatie {0}", + "Add Remote Browser": "Externe browser toevoegen", + "New Group": "Nieuwe Groep", + "Group Name": "Groeps naam", + "OAuth2: Client Credentials": "OAuth2: Client referenties", + "Authentication Method": "Authenticatie methode", + "Authorization Header": "Autorisatie Header", + "OAuth Token URL": "OAuth Token URL", + "Client ID": "Client ID", + "Client Secret": "Client geheim", + "Go back to home page.": "Ga terug naar de home pagina.", + "No tags found.": "Geen tags gevonden.", + "Lost connection to the socket server.": "Verbinding met de socketserver verloren.", + "Cannot connect to the socket server.": "Kan geen verbinding maken met de socketserver.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL", + "signl4Docs": "Meer informatie over het configureren van SIGNL4 en het verkrijgen van de SIGNL4-webhook-URL kunt u hier vinden {0}.", + "Conditions": "Voorwaarden", + "conditionAdd": "Voorwaarde toevoegen", + "conditionDelete": "Voorwaarde verweideren", + "conditionAddGroup": "Groep toevoegen", + "conditionDeleteGroup": "Groep verweideren", + "conditionValuePlaceholder": "Waarde", + "contains": "bevat", + "not contains": "bevat niet", + "starts with": "start met", + "not starts with": "start niet met", + "ends with": "eindigt met", + "not ends with": "eindigt niet met", + "less than": "minder dan", + "greater than": "groter dan", + "record": "dossier", + "jsonQueryDescription": "Parseer en haal specifieke gegevens uit de JSON-respons van de server met behulp van JSON-query of gebruik \"$\" voor de onbewerkte respons, als u geen JSON verwacht. Het resultaat wordt vervolgens vergeleken met de verwachte waarde, als strings. Zie {0} voor documentatie en gebruik {1} om te experimenteren met query's.", + "rabbitmqNodesDescription": "Voer het URL voor de RabbitMQ beheer nodes inclusief protocol en poort in. Bijvoorbeeld: {0}", + "rabbitmqNodesRequired": "Aub stel de knooppunten voor deze monitor in.", + "rabbitmqNodesInvalid": "Gebruik een volledig gekwalificeerde (beginnend met 'http') URL voor de RabbitMQ nodes.", + "RabbitMQ Username": "RabbitMQ gebruikersnaam", + "RabbitMQ Password": "RabbitMQ wachtwoord", + "rabbitmqHelpText": "Om gebruik te maken van de monitor moet je de Management Plugin in de RabbitMQ setup aanzetten. Voor meer informatie zie de {rabitmq_documentatie}.", + "SendGrid API Key": "SendGrid API sleutel", + "Separate multiple email addresses with commas": "Splits meerdere emailadressen met kommas", + "RabbitMQ Nodes": "RabbitMQ beheer Nodes", + "shrinkDatabaseDescriptionSqlite": "Trigger database {vacuum} voor SQLite. {auto_vacuum} is al aangezet maar dit defragmenteert de database niet en zal individuele database pagina's niet opnieuw organiseren op de manier dat het {vacuum} commando dat doet.", + "aboutSlackUsername": "Verandert de weergavenaam van de afzender. Als je iemand wil vermelden, voeg dit dan aan de vriendelijke naam toe.", + "cacheBusterParam": "Voeg de {0} parameter", + "Form Data Body": "Formulier Gegevens Body", + "Optional: Space separated list of scopes": "Optioneel: door spaties gescheiden lijst met scopes", + "Alphanumerical string and hyphens only": "Alleen alfanumerieke tekens en streepjes", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Tijdsgevoelige meldingen worden meteen afgeleverd, zelfs als het apparaat in niet storen modus staat.", + "Message format": "Bericht opmaak", + "Send rich messages": "Verstuur berichten met opmaak", + "OAuth Scope": "OAuth bereik", + "equals": "hetzelfde als", + "not equals": "niet gelijk aan", + "less than or equal to": "minder dan of gelijk aan", + "greater than or equal to": "meer dan of gelijk aan", + "Notification Channel": "Notificatie kanaal", + "Sound": "Geluid", + "Arcade": "Spelletjes", + "Correct": "Goed", + "Fail": "Mislukt", + "Harp": "Harp", + "Reveal": "Laat zien", + "Bubble": "Bubbel", + "Doorbell": "Deurbel", + "Flute": "Fluit", + "Money": "Geld", + "Scifi": "Science fiction", + "Guitar": "Gitaar", + "Custom sound to override default notification sound": "Aangepast geluid om het standaard geluid te vervangen", + "Time Sensitive (iOS Only)": "Tijdsgevoelig (alleen voor iOs)", + "From": "Van", + "Can be found on:": "Kan gevonden worden op: {0}", + "The phone number of the recipient in E.164 format.": "Het telefoonnummer van de ontvanger in E.164 formaat.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Ofwel een sms zender ID of een telefoonnummer in E.164 formaat als je reacties wil ontvangen.", + "Clear": "Helder", + "Elevator": "Lift", + "Pop": "Pop", + "Community String": "Gemeenschapsreeks", + "Json Query Expression": "Json-queryexpressie", + "ignoredTLSError": "TLS/SSL fouten zijn genegeerd", + "telegramServerUrl": "(Optioneel) Server Url", + "telegramServerUrlDescription": "Om de beperkingen van Telegram's bot api op te heffen of toegang te krijgen in geblokkeerde gebieden (China, Iran, enz.). Klik voor meer informatie op {0}. Standaard: {1}", + "wahaSession": "Sessie", + "wahaChatId": "Chat-ID (telefoonnummer / contact-ID / groeps-ID)", + "wayToGetWahaApiUrl": "Je WAHA Instance URL.", + "wayToGetWahaApiKey": "API Key is de WHATSAPP_API_KEY omgevingsvariabele die je hebt gebruikt om WAHA uit te voeren.", + "wayToGetWahaSession": "Vanaf deze sessie stuurt WAHA meldingen naar Chat ID. Je kunt deze vinden in WAHA Dashboard.", + "wayToWriteWahaChatId": "Het telefoonnummer met het internationale voorvoegsel, maar zonder het plusteken aan het begin ({0}), de contact-ID ({1}) of de groeps-ID ({2}). Vanuit WAHA Sessie worden meldingen naar deze Chat-ID verzonden.", + "YZJ Robot Token": "YZJ Robot token", + "Plain Text": "Platte tekst", + "Message Template": "Bericht Sjabloon", + "YZJ Webhook URL": "YZJ Webhook URL", + "Template Format": "Sjabloonformaat", + "templateServiceName": "service naam", + "templateHostnameOrURL": "hostnaam of url", + "templateStatus": "status", + "telegramUseTemplate": "Gebruik aangepaste bericht sjabloon", + "telegramTemplateFormatDescription": "Telegram staat het gebruik van verschillende opmaaktalen voor berichten toe, zie Telegram {0} voor specifieke details.", + "telegramUseTemplateDescription": "Indien ingeschakeld, wordt het bericht verzonden met een aangepaste sjabloon.", + "Font Twemoji by Twitter licensed under": "Lettertype Twemoji van Twitter gelicentieerd onder", + "the smsplanet documentation": "de smsplanet documentatie", + "Phone numbers": "Telefoonnummers", + "Sender name": "Naam afzender", + "smsplanetNeedToApproveName": "Moet worden goedgekeurd in het clientpaneel", + "smsplanetApiToken": "Token voor de SMSPlanet API", + "smsplanetApiDocs": "Gedetailleerde informatie over het verkrijgen van API-tokens vindt u op {the_smsplanet_documentation}.", + "defaultFriendlyName": "Nieuwe monitor", + "Add Tags": "Labels toevoegen", + "tagAlreadyOnMonitor": "Dit label (naam en waarde) is al op de monitor gekoppeld of in afwachting van koppelen.", + "mqttWebsocketPathExplanation": "WebSocket pad voor MQTT via WebSocket verbindingen (bijv., /mqtt)", + "mqttWebsocketPathInvalid": "Gebruik een geldig WebSocket pad formaat alsjeblieft", + "mqttHostnameTip": "Gebruik dit formaat alsjeblieft {hostnaamFormat}", + "Path": "Pad", + "tagNameExists": "Een systeemtag met de deze naam bestaat al. Selecteer er een van de lijst of gebruik een andere naam.", + "Notifications Enabled": "Notificaties ingeschakeld", + "Allow Notifications": "Notificaties toestaan", + "Browser not supported": "Browser niet ondersteund", + "Custom URL": "Eigen URL", + "Maximum Retries": "Maximaal aantal pogingen", + "Number of retry attempts if webhook fails": "Aantal pogingen (elke 60-180 seconden) wanneer de webhook mislukt.", + "minimumIntervalWarning": "Intervallen onder de 20 seconden kunnen resulteren in slechte performance.", + "Select All": "Selecteer alle", + "Deselect All": "Deselecteer alle", + "Unable to get permission to notify": "Niet mogelijk om toestemming tot notificaties te verkrijgen (verzoek geweigerd of genegeerd).", + "brevoFromEmail": "Van Email", + "brevoSeparateMultipleEmails": "Scheid meerdere email-adressen met komma", + "pingNumericDescription": "Indien aangevinkt, worden IP-adressen weergegeven in plaats van symbolische hostnamen", + "pingGlobalTimeoutLabel": "Globale timeout", + "pingGlobalTimeoutDescription": "Totale tijd in seconden voordat pingen stopt, ongeacht aantal verstuurde pakketten", + "deleteGroupMsg": "Weet je zeker dat je deze groep wilt verwijderen?", + "deleteChildrenMonitors": "Verwijder de onderliggende monitoren en kinderen indien aanwezig | Verwijder ook alle {count} directe kinderen en hun kinderen als er enige zijn", + "supportBaleChatID": "Support directe chat / groep / kanaal chat ID", + "wayToGetBaleChatID": "Je kan een chat ID verkrijgen door een bericht te sturen naar de bot en naar deze URL te gaan:", + "brevoSubject": "Onderwerp", + "wayToGetClickSMSIRTemplateID": "Je sjabloon moet een {uptkumaalert} veld bevatten. Je kan een nieuwe sjabloon {here} aanmaken.", + "Webpush Helptext": "Web push werkt alleen met SSL- (HTTPS-)verbindingen. Voor iOS-apparaten moet de webpagina vooraf aan het beginscherm zijn toegevoegd.", + "lowIntervalWarning": "Weet je zeker dat je de intervalwaarde onder de 20 seconden wilt instellen? De prestaties kunnen hierdoor verslechteren, vooral als er een groot aantal monitors is.", + "FlashDuty Push URL Placeholder": "Kopieer van de alert-integratiepagina", + "smseagleApiv2": "APIv2 (aanbevolen voor nieuwe integraties)", + "smseagleDocs": "Controleer documentatie of APIv2 beschikbaarheid: {0}", + "SpugPush Template Code": "Template code", + "labelDomainExpiry": "Domein verval.", + "labelDomainNameExpiryNotification": "Domeinnaam verval notificatie", + "settingsDomainExpiry": "Domein vervaldatum", + "domainExpiryDescription": "Trigger notificatie wanneer de domeinnaam vervalt binnen:", + "smseagleComma": "Meerdere waarden moeten gescheiden worden met een komma", + "Recipient Numbers": "Nummers ontvangers", + "ignoreSecWebsocketAcceptHeaderDescription": "Staat toe dat de server geen Sec-WebSocket-Accept-header terugstuurt als de WebSocket-upgrade succesvol is.", + "Ignore Sec-WebSocket-Accept header": "Negeer {0} header", + "WebSocket Application Messaging Protocol": "WAMP (The WebSocket Application Messaging Protocol)", + "Session Initiation Protocol": "WebSocket-transport voor SIP (Session Initiation Protocol)", + "Network API for Notification Channel": "OMA RESTful-netwerk-API voor Notification Channel", + "Web Process Control Protocol": "Web Process Control Protocol (WPCP)", + "Advanced Message Queuing Protocol": "Advanced Message Queuing Protocol (AMQP) 1.0+", + "jsflow": "jsFlow pubsub/queue-protocol", + "Reverse Web Process Control": "Reverse Web Process Control Protocol (RWPCP)", + "Extensible Messaging and Presence Protocol": "WebSocket-transport voor het Extensible Messaging and Presence Protocol (XMPP)", + "Smart Home IP": "SHIP - Smart Home IP", + "Miele Cloud Connect Protocol": "Miele Cloud Connect Protocol", + "Push Channel Protocol": "Push Channel Protocol", + "wsSubprotocolDescription": "Voor een kommagescheiden lijst in van subprotocollen. Voor meer informatie over subprotocollen raadpleeg je de {documentation}", + "Softvelum WebSocket signaling protocol": "Softvelum WebSocket Signaling Protocol", + "Message Session Relay Protocol": "WebSocket Transport voor MSRP (Message Session Relay Protocol)", + "Binary Floor Control Protocol": "WebSocket Transport voor BFCP (Binary Floor Control Protocol)", + "Softvelum Low Delay Protocol": "Softvelum Low Delay Protocol", + "OPC UA Connection Protocol": "OPC UA Connection Protocol", + "OPC UA JSON Encoding": "OPC UA JSON codering", + "Swindon Web Server Protocol": "Swindon Web Server Protocol (JSON codering)", + "Broadband Forum User Services Platform": "USP (Breedband Forum User Services Platform)", + "Constrained Application Protocol": "Constrained Application Protocol (CoAP)", + "Cobra Real Time Messaging Protocol": "Cobra Real Time Messaging Protocol", + "Declarative Resource Protocol": "Declarative Resource Protocol", + "BACnet Secure Connect Hub Connection": "BACnet Secure Connect Hub Connectie", + "BACnet Secure Connect Direct Connection": "BACnet Secure Connect Directe connectie", + "Penguin Statistics Live Protocol v3": "Penguin Statistics Live Protocol v3 (Protobuf encoding)", + "tagAlreadyStaged": "Deze tag (naam en waarde) is al gekozen voor deze batch.", + "pingIntervalAdjustedInfo": "Het interval wordt aangepast op basis van het aantal pakketten, de globale time-out en de time-out per ping", + "smtpHelpText": "‘SMTPS’ test of SMTP/TLS werkt; ‘Ignore TLS’ maakt verbinding via platte tekst; ‘STARTTLS’ maakt verbinding, geeft een STARTTLS-commando en verifieert het servercertificaat. Geen van deze opties verstuurt een e-mail.", + "Happy Eyeballs algorithm": "Happy Eyeballs algoritme", + "Add Another Tag": "Nog een tag toevoegen", + "Clear Form": "Formulier wissen", + "Template ID": "Sjabloon ID", + "ipFamilyDescriptionAutoSelect": "Gebruikt {happyEyeballs} voor het vaststellen van de IP familie.", + "descriptionHelpText": "Getoond op het interne dashboard. Markdown is toegestaan en wordt gezuiverd voor weergave.", + "HTTP Method": "HTTP methode", + "webhookPostMethodDesc": "POST is goed voor de meeste moderne HTTP servers.", + "evolutionRecipient": "Telefoonnummer / Contact ID / Group ID", + "brevoFromName": "Van Naam", + "brevoCcEmail": "CC email", + "brevoBccEmail": "BCC email", + "brevoLeaveBlankForDefaultName": "laat leeg voor de standaard naam", + "brevoToEmail": "Naar Email", + "brevoLeaveBlankForDefaultSubject": "laat leeg voor standaard onderwerp", + "pingCountDescription": "Aantal pakketten om te sturen voordat gestopt wordt", + "ntfyPriorityDown": "Prioriteit voor DOWN-events", + "Send UP silently": "Stuur UP stil", + "Nextcloud host": "Nextcloud host", + "Conversation token": "Conversatie token", + "Bot secret": "Bot geheim", + "Mention Mobile List": "Mobiele lijst noemen", + "Mention User List": "Gebruikers-id lijst", + "Invalid mobile": "Ongeldige mobiel [{mobile}]", + "Invalid userId": "Ongeldig userId [{userId}]", + "smseagleMsgTtsAdvanced": "Tekst-naar-spraak geavanceerd gesprek", + "smseagleDuration": "Duur (in seconden)", + "brevoApiKey": "Brevo API Sleutel", + "pingNumericLabel": "Numerieke output", + "Disable URL in Notification": "URL in notificatie uitschakelen", + "pause": "Pauzeer", + "Manual": "Handmatig", + "Send DOWN silently": "Stuur DOWN stil", + "Staged Tags for Batch Add": "Gefaseerde tags voor batchtoevoeging", + "twilioApiKeyHelptext": "De API sleutel is optioneel maar aanbevolen. Je kan een account SID en AuthToken van de TwilioConsole pagina geven of een Account SID en paar van Api Key en Api Secret", + "FlashDuty Push URL": "Push URL", + "OAuth Audience": "OAuth publiek", + "certHostnameMismatch": "Hostnaam op certificaat komt niet overeen met de monitor-URL.", + "Dingtalk Mobile List": "Mobiel lijst", + "Dingtalk User List": "Gebruikers-id lijst", + "Enter a list of userId": "Voer een lijst van userId in", + "Enter a list of mobile": "Voer een lijst van mobiel in", + "smseagleMsgType": "Berichttype", + "smseagleMsgSms": "Sms bericht (standaard)", + "smseagleMsgRing": "Oproep", + "smseagleMsgTts": "Tekst-naar-spraak gesprek", + "smseagleTtsModel": "Tekst-naar-spraak model ID", + "smseagleApiType": "API versie", + "smseagleApiv1": "APIv1 (voor bestaande projecten)", + "ntfyPriorityHelptextPriorityHigherThanDown": "Reguliere prioriteit moet hoger zijn dan {0}. Prioriteit {1} is hoger dan {2}", + "twilioMessagingServiceSID": "Berichtenservice SID (optioneel)", + "twilloMessagingServiceSIDHelptext": "Voer hier je Messaging Service SID in als je {twillo_messaging_service_help_link} gebruikt om afzenders en functies te beheren", + "webhookGetMethodDesc": "GET verstuurt gegevens als queryparameters en staat niet toe om een body te configureren. Handig voor het activeren van Uptime Kuma Push-monitors.", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Installeren van een Nextcloud Talk bot vereist administrator-toegang op de server.", + "pingPerRequestTimeoutLabel": "Per-ping timeout", + "pingPerRequestTimeoutDescription": "Dit is de maximale wachttijd (in seconden) voordat een afzonderlijk pingpakket als verloren wordt beschouwd", + "showOnlyLastHeartbeat": "Toon alleen laatste heartbeat", + "OneChatBotId": "OneChat Bot id", + "customUrlDescription": "Wordt gebruikt als klikbare URL in plaats van de monitor URL.", + "OneChatAccessToken": "OneChat toegangstoken", + "OneChatUserIdOrGroupId": "OneChat User ID of Group ID", + "Ip Family": "IP familie", + "Template plain text instead of using cards": "Sjabloon platte tekst van gebruikte kaarten", + "Clone Maintenance": "Kloon onderhoud", + "ariaPauseMaintenance": "Pauzeer dit onderhoud", + "ariaResumeMaintenance": "Hervat dit onderhoud", + "ariaCloneMaintenance": "Maak een kopie van dit onderhoudsvenster", + "ariaEditMaintenance": "Bewerk dit onderhoudsvenster", + "ariaDeleteMaintenance": "Verwijder dit onderhoudsvenster", + "Clear All Events": "Wis alle evenementen", + "clearAllEventsMsg": "Weet je zeker dat je alle evenementen wilt verwijderen voor alle monitors? Dit verwijderd de data van geassocieerde berichten permanent en kan niet worden teruggedraaid.", + "Could not clear events": "Kon {failed}/{total} evenementen niet verwijderen", + "issueWithGoogleChatOnAndroidHelptext": "Dit geeft ook de mogelijkheid om upstream bugs als {issuetrackerURL} te passeren", + "auto-select": "Automatisch selecteren", + "smseagleGroupV2": "Telefoonboek groep ID(s)", + "smseagleContactV2": "Telefoonboek contact ID(s)", + "pingCountLabel": "Maximaal pakketten", + "Subprotocol": "Subprotocol", + "WebSocket Transport for JMAP": "WebSocket Transport voor JMAP (JSON Meta Application Protocol)", + "ITU-T T.140 Real-Time Text": "ITU-T T.140 Real-Time Text", + "Done.best IoT Protocol": "Done.best IoT Protocol", + "Text IRC Protocol": "Text IRC Protocol", + "Binary IRC Protocol": "Binair IRC Protocol", + "Collection Update": "Het Collection Update WebSocket-subprotocol", + "wayToGetBaleToken": "Je kan een token opvragen via {0}.", + "Duration (Minutes)": "Duur (minuten)", + "SMTP Security": "SMTP beveiliging", + "Ignore STARTTLS": "Negeer STARTTLS", + "Use STARTTLS": "Gebruik STARTTLS", + "Events cleared successfully": "Evenementen succesvol verwijderd.", + "No monitors found": "Geen monitors gevonden.", + "wayToWriteEvolutionRecipient": "Het telefoonnummer met het internationale voorvoegsel, maar zonder de plus aan het begin ({0}), het Contact ID ({1}) of het groeps-ID ({2}).", + "wayToGetEvolutionUrlAndToken": "Je kan een API URL en token verkrijgen door naar het gewenste kanaal te gaan van {0}", + "evolutionInstanceName": "Instantienaam", + "Optional: The audience to request the JWT for": "Optioneel: het publiek om de JWT voor aan te vragen", + "Enter the list of nodes": "Voer de lijst met RabbitMQ-beheer­nodes in", + "Press Enter to add node": "Druk op Enter om node toe te voegen", + "brevoApiHelp": "Maak hier een API sleutel: {0}", + "mqttWebSocketPath": "Pad voor MQTT WebSocket", + "Use HTML for custom E-mail body": "Gebruik HTML voor een aangepaste E-mailtekst", + "resendApiKey": "Verstuur API sleutel opnieuw", + "resendApiHelp": "Maak een api sleutel hier {0}", + "resendFromName": "Van naam", + "resendFromEmail": "Van email", + "resendLeaveBlankForDefaultName": "laat leeg voor de standaard naam", + "resendToEmail": "Naar email", + "resendSubject": "Onderwerp", + "imageResetConfirmation": "Afbeelding terugzetten naar standaard", + "wsCodeDescription": "Voor meer informatie over statuscodes, raadpleeg {rfc6455}", + "Subprotocol(s)": "Subprotocol(len)", + "systemService": "Systeemservice", + "systemServiceName": "Service naam", + "systemServiceDescription": "Controleert of systeem service {service_name} actief is", + "systemServiceDescriptionLinux": "Controleert of Linux systemd service {service_name} actief is", + "systemServiceDescriptionWindows": "Controleert of Windows Service Manager {service_name} actief is", + "systemServiceCommandHint": "Gebruikte commando: {command}", + "systemServiceExpectedOutput": "Verwachte output: \"{0}\"", + "days": "{n} dag| {n} dagen", + "hours": "{n} uur| {n} uren", + "minutes": "{n} minuut| {n} minuten", + "minuteShort": "{n} min | {n} min", + "years": "{n} jaar| {n} jaren", + "HeadersInvalidFormatBecause": "De verzoekheaders zijn geen geldige JSON omdat {error}", + "BodyInvalidFormatBecause": "Het verzoek body zijn geen geldige JSON omdat {error}", + "enableSSL": "Schakel SSL/TLS in", + "mariadbUseSSLHelptext": "Schakel in om een versleutelde verbinding met je database te gebruiken. Vereist voor de meeste clouddatabases.", + "mariadbCaCertificateLabel": "CA-certificaat", + "mariadbCaCertificateHelptext": "Plak het CA-certificaat in PEM-formaat om deze te gebruiken met zelfondertekende certificaten. Laat dit veld leeg als je database een certificaat gebruikt dat is ondertekend door een openbare CA.", + "saveResponseForNotifications": "HTTP-succesrespons voor meldingen opslaan", + "unknownDays": "Onbekende dagen", + "Monitors": "{n} Monitor | {n} Monitoren", + "saveErrorResponseForNotifications": "HTTP-foutmelding voor meldingen opslaan", + "saveResponseDescription": "Slaat het HTTP-antwoord op en maakt het beschikbaar voor meldingssjablonen als {templateVariable}", + "responseMaxLength": "Antwoord maximale lengte (bytes)", + "versionIs": "Versie: {version}", + "logoutCurrentUser": "Log uit {username}", + "hostnameCannotBeIP": "De DNS-hostnaam kan geen IP-adres zijn. Wilt u het veld ‘resolver’ gebruiken?", + "Only retry if status code check fails": "Alleen opnieuw proberen als de statuscode-controle mislukt", + "invalidURL": "Ongeldige URL", + "No incidents recorded": "Geen incidenten geregistreerd", + "Load More": "Meer laden", + "Loading...": "Laden...", + "Pin this incident": "Incident vastzetten", + "retryOnlyOnStatusCodeFailureDescription": "Indien ingeschakeld zal een nieuwe poging alleen plaatsvinden als de HTTP statuscode faalt. Als de statuscode geldig is, maar de JSON query faalt, zal de monitor als offline gemarkeerd worden zonder nieuwe pogingen.", + "Incident description": "Omschrijving incident", + "Past Incidents": "Afgelopen incidenten", + "Incident title": "Incident titel", + "Edit Incident": "Incident bewerken", + "templateAvailableVariables": "Beschikbare variabelen", + "example": "Voorbeeld", + "Result": "Resultaat", + "slackIncludeGroupName": "Voeg monitor groepsnaam in", + "slackIncludeGroupNameDescription": "Indien ingeschakeld, het pad van de monitor groep zal toegevoegd worden in notificaties om meer onderscheid te bieden in monitoren met dezelfde naam in verschillende groepen.", + "slackUseTemplate": "Gebruik eigen berichttemplate", + "slackUseTemplateDescription": "Indien ingeschakeld zal het bericht met een eigen template verstuurd worden. Je kan liquid templating gebruiken om monitor groepsinformatie in te voegen via monitorJSON.path of monitorJSON.pathName.", + "responseMaxLengthDescription": "Maximale grootte van responsdata om op te slaan. Stel in op 0 om geen limiet te zetten. Grotere responses worden afgekapt. Standaard: 1024 (1KB)", + "Resolver Server(s)": "Resolver server(s)", + "Incident not found or access denied": "Incident niet gevonden of toegang geweigerd", + "Pinned incidents are shown prominently on the status page": "Gepinde incidenten worden prominent getoond op de statuspagina", + "steamApiKeyDescriptionAt": "Voor het monitoren van een Steam gameserver heb je een Steam Web-API sleutel nodig. Je kan een API sleutel registreren op {url}", + "invalidHostnameOrIP": "Ongeldige hostnaam of IP. Hostnaam moet een geldige FQDN zijn. Wildcards niet toegestaan. Mag underscores bevatten of eindigen met een punt.", + "invalidDNSHostname": "Ongeldige hostnaam. Hostnaam moet een geldige FQDN zijn. Wildcards niet toegestaan. Mag underscores bevatten of eindigen met een punt.", + "wildcardOnlyForDNS": "Wildcard hostnamen zijn alleen ondersteund voor DNS monitors.", + "Resolved": "Opgelost", + "createdAt": "Gecreëerd: {date}", + "lastUpdatedAt": "Laatst bijgewerkt: {date}", + "lastUpdatedAtFromNow": "Laatst aangepast: {date} ({fromNow})", + "Actions": "Acties", + "selectedMonitorCountMsg": "geselecteerd: {n} | geselecteerd: {n}", + "selectMonitorMsg": "Selecteer monitors om acties uit te voeren", + "selectAllMonitorsAria": "Selecteer alle monitors", + "deselectAllMonitorsAria": "Deselecteer alle monitors", + "deleteIncidentMsg": "Weet je zeker dat je dit incident wilt verwijderen?", + "slug is not found": "Afkorting is niet gevonden", + "Please input content": "Voer content in", + "Please input title": "Voer een titel in", + "dateCreatedAtFromNow": "Datum aangemaakt: {date} ({fromNow})", + "RSS Title": "RSS titel", + "Leave blank to use status page title": "Laat leeg om titel van de statuspagina te gebruiken", + "Cloud ID": "Cloud ID", + "API Token": "API sleutel", + "See Jira Cloud Docs": "Zie Jira cloud docs", + "sipsakPingWarning": "Om de SIP Options Ping monitor te gebruiken, moet je Uptime Kuma zonder docker installeren en ook een Sipsak client op je server installeren.", + "Examples:": "Voorbeelden: {0}", + "frontendVersionIs": "Frontend versie: {version}", + "cronScheduleDescription": "Planning: {description}", + "notificationChatPlatforms": "Chat platforms", + "notificationPushServices": "Push diensten", + "notificationSmsServices": "SMS diensten", + "notificationEmail": "E-mail", + "notificationIncidentManagement": "Incident management", + "notificationHomeAutomation": "Home automation", + "notificationOther": "Andere integraties", + "Disable STARTTLS": "STARTTLS uitschakelen", + "disableSTARTTLSDescription": "Schakel deze optie in voor SMTP servers die geen STARTTLS ondersteunen. Dit zal emails onversleuteld versturen.", + "deleteMonitorsMsg": "Weet je zeker dat je de geselecteerde monitors wilt verwijderen?", + "pausedMonitorsMsg": "Gepauzeerd {n} monitor | Gepauzeerd {n} monitors", + "resumedMonitorsMsg": "Hervat {n} monitor | Hervat {n} monitors", + "deletedMonitorsMsg": "Verwijderd {n} monitor | Verwijderd {n} monitors", + "noMonitorsPausedMsg": "Geen monitor(s) gepauzeerd (er waren er geen actief)", + "noMonitorsResumedMsg": "Geen monitors hervat (er waren er geen gepauzeerd)", + "bulkDeleteErrorMsg": "Verwijderen van {n} monitor mislukt | Verwijderen van {n} monitors mislukt", + "Sets end time based on start time": "Stel eindtijd in op basis van starttijd", + "Please set start time first": "Stel starttijd eerst in", + "noMonitorsSelectedWarning": "Je maakt een onderhoudsvenster aan zonder getroffen monitors. Weet je zeker dat je wilt doorgaan?", + "noMonitorsOrStatusPagesSelectedError": "Kan geen onderhoud aanmaken zonder geraakte monitors of statuspagina's", + "Deploy a Google Apps Script as a web app and paste the URL here": "Publiceer een Google Apps script als web app en plak de URL hier", + "Quick Setup Guide": "Snelstartgids", + "Open your Google Spreadsheet": "Open je Google Spreadsheet", + "Go to Extensions → Apps Script": "Ga naar extensies → Apps Script", + "Paste the script code (see below)": "Plak de script code (zie onderstaand)", + "Click Deploy → New deployment → Web app": "Klik op Deploy → New deployment → Web app", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Stel 'Uitvoeren als: mij' en 'Wie heeft toegang: Iedereen' in", + "Copy the web app URL and paste it above": "Kopieer de web app URL en plak deze hierboven", + "Google Apps Script Code": "Google apps script code", + "Copy to Clipboard": "Kopieer naar klembord", + "Copied to clipboard!": "Gekopieerd naar klembord!", + "Failed to copy to clipboard": "Kopieren naar klembord mislukt", + "checkPriceAt": "Bekijk {service} prijzen op {url}", + "You can divide numbers with commas or semicolons": "Je kan getallen scheiden met {comma} of {semicolon}", + "OptionalParameters": "Optionele parameters", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Door beperkingen van de provider, schakel optionele variabelen in met het risico niet af te leveren", + "aliyun-template-requirements-and-parameters": "De aliyun SMS template moet parameters bevatten: {parameters}", + "aliyun-template-optional-parameters": "Optionele parameters: {parameters}", + "WeCom Mentioned Mobile List": "WeCom mobiel lijst", + "WeCom Mentioned Mobile List Description": "Voor het telefoonnummer in om te noemen. Scheid meerdere nummers met een komma. Gebruik {'@'}all om iedereen te noemen.", + "aboutJiraCloudId": "Meer informatie over Jira Cloud ID: {0}", + "see Jira Cloud Docs": "zie Jira Cloud documentatie", + "serwersmsRecipientType": "Ontvanger type", + "serwersmsRecipientTypePhone": "Telefonnnummer", + "serwersmsRecipientTypeGroup": "Groep", + "serwersmsGroupId": "Groep ID", + "serwersmsGroupIdHelptext": "ID of groep ID in het klantenpanel. Deze ID nummers kunnen gedownload worden met actie groepen / index of door ze te kopieren van het bewerk groep deel in het klantenpanel.", + "octopushEndpoint": "octopush (endpoint: {url})", + "legacyOctopushEndpoint": "Legacy Octopush-DM (endpoint: {url})", + "Analytics Type": "Analytics type", + "Analytics ID": "Analytics ID", + "message": "Bericht", + "Sort by uptime": "Sorteer op uptime", + "notificationUniversal": "Universeel", + "Resolve": "Oplossen", + "Jira Service Management": "Jira Service management", + "Certificate Chain:": "Certificaatketen:", + "Google Apps Script Webhook URL": "Google Apps script webhook URL", + "Show this Maintenance Message on which Status Pages": "Toon dit onderhoudsbericht op deze statuspagina's", + "discordMessageFormat": "Berichtformaat", + "discordMessageFormatMinimalist": "Minimalistisch (korte status)", + "discordMessageFormatCustom": "Aangepaste sjabloon", + "discordUseMessageTemplate": "Gebruik een aangepast berichtsjabloon", + "discordMessageTemplate": "Berichtsjabloon", + "Basic radio toggle button group": "Basis radioknoppen groep", + "halopsa_setup_step2": "Configureer runbook acties om meldingen te verwerken (bijv. Maak ticket)", + "halopsa_setup_step3": "Kopieer de webhook URL en plak deze in het bovenstaande tekstveld", + "Clear current filters": "Wis huidige filters", + "Sort options": "Sorteer opties", + "System Service": "Systeemservice", + "SSL/TLS": "SSL/TLS", + "mtls-auth-server-key-label": "Sleutel", + "mtls-auth-server-key-placeholder": "Sleutel inhoud", + "mtls-auth-server-ca-placeholder": "Server CA", + "avgPing": "Gemiddelde ping", + "maxPing": "Max ping", + "minPing": "Min ping", + "Setup Instructions": "Instel-instructies", + "playground": "playground", + "Check Type": "Controleer type", + "Service Name": "Service naam", + "GRPC Options": "GRPC opties", + "Metadata": "Metagegevens", + "End": "Eind", + "passwordTooWeak": "Wachtwoord is te zwak. Het moet minimaal alfanumerieke karakters bevatten en minstens 6 karakters lang zijn.", + "TLS Alerts": "TLS waarschuwingen", + "Expected TLS Alert": "Verwachtte TLS melding", + "None (Successful Connection)": "Geen (succesvolle verbinding)", + "mariadbSocketPathDetectedHelptext": "Verbinding maken met de database zoals opgegeven via de omgevingsvariabele {0}.", + "Expand All Groups": "Alle groepen uitklappen", + "Halo PSA": "Halo PSA", + "halopsa_payload_desc": "De volgende velden zijn verstuurd naar je Halo PSA webhook:", + "halopsa_field_title": "Melding titel (altijd 'Uptime Kuma Alert')", + "halopsa_field_monitor": "Naam van de monitor", + "halopsa_field_monitor_id": "Uniek monitor ID (null voor test meldingen) - gebruik dit om te matchen met tickets", + "halopsa_field_message": "Volledige meldtekst met status en details", + "halopsa_field_timestamp": "Event tijdstempel in ISO 8601 formaat", + "Umami": "Umami", + "Basic checkbox toggle button group": "Basis selectievakje schakelen knopgroep", + "Webhook Payload Fields": "Webhook payload velden", + "discordMessageFormatNormal": "Normaal (rijke insluitingen)", + "mtls-auth-server-cert-label": "Certificaat", + "halopsa_field_status": "Monitor status: UP, DOWN, NOTIFICATION, of UNKNOWN", + "discordUseMessageTemplateDescription": "Indien ingeschakeld, wordt het bericht verzonden met behulp van een aangepaste sjabloon (LiquidJS). Laat dit veld leeg om het standaard Uptime Kuma-formaat te gebruiken.", + "mtls-auth-server-cert-placeholder": "Certificaat body", + "halopsa_field_uptime_kuma_version": "Uptime Kuma versienummer", + "halopsa_setup_step1": "Maak een integratie runbook in HaloPSA (Configuration → Integrations → Integration Runbooks)", + "halopsa_id_usage_hint": "💡 Tip: Gebruik monitor_id om waarschuwingen betrouwbaar te koppelen aan tickets, en heartbeat_id om de gebeurtenisgeschiedenis bij te houden", + "halopsa_setup_step4": "Kies basis authenticatie en maak een gebruikersnaam en wachtwoord. En typ of plak de gebruikersnaam en wachtwoord in bovenstaande velden", + "halopsa_setup_step5": "Configureer runbook om monitor_id te gebruiken voor het matchen van waarschuwingen met bestaande tickets", + "GrafanaOncallURL": "Grafana Oncall URL", + "TLS Alert Spec": "RFC 8446", + "smscTranslit": "smscTranslit", + "Region": "Regio", + "Analytics Script URL": "Analytics-script-URL", + "Badge Link Generator Helptext": "Badge links zijn beschikbaar voor alle monitors toegewezen aan openbare statuspagina's. Voor meer informatie, zie de {documentation}.", + "ntfyCall": "Telefoongesprek", + "ntfyCallHelptext": "Maak een telefoongesprek wanneer een alert triggert. Stel in op 'Ja' om je eerst geverifieerde nummer te gebruiken, of voer een specifiek telefoonnummer in. Vereist een geverifieerd telefoonnummer.", + "ntfyUseTemplate": "Notificatie-templates aanpassen", + "ntfyUseTemplateDescription": "Schakel in om notificatie-titels en berichten aan te passen met LiquidJS templating", + "ntfyCustomTitle": "Eigen titel template", + "ntfyCustomMessage": "Eigen berichttemplate", + "ntfyNotificationTemplateFallback": "Laat leeg om het standaard Uptime Kuma format te gebruiken", + "Open Badge Link Generator": "Open badge link generator", + "Badge Link Generator": "{0}'s badge link generator", + "Screenshot Delay": "Screenshot vertraging (wacht {milliseconds})", + "milliseconds": "{n} millisecond | {n} milliseconden", + "screenshotDelayDescription": "Wacht eventueel zoveel milliseconden voordat u de schermafbeelding maakt. Maximaal: {maxValueMs} ms (0,5 × interval).", + "screenshotDelayWarning": "Hogere waarden zorgen ervoor dat de browser langer open blijft, waardoor het geheugengebruik bij veel gelijktijdige monitoren kan toenemen.", + "snmpV3Username": "SNMPv3 gebruikersnaam", + "json_value": "JSON waarde", + "resendLeaveBlankForDefaultSubject": "Laat dit leeg voor het standaardonderwerp", + "GlobalpingLocationDocs": "Volledige documentatie voor locatie-invoer", + "Globalping - Access global monitoring probes": "Globalping - Toegang tot wereldwijde monitoringsondes", + "GlobalpingDescription": "Globalping biedt toegang tot duizenden door de gemeenschap gehoste tests om netwerktests en metingen uit te voeren. Voor alle anonieme gebruikers is een limiet van 250 tests per uur ingesteld. Als u de limiet wilt verdubbelen naar 500 per uur, slaat u uw token op in {accountSettings}.", + "Globalping API Token": "Globalping API token", + "globalpingApiTokenDescription": "Haal uw Globalping API-token op bij {0}.", + "GlobalpingHostname": "Een publiek bereikbaar meetdoel. Meestal een hostnaam of IPv4/IPv6-adres, afhankelijk van het meettype.", + "GlobalpingLocation": "Het locatieveld accepteert continenten, landen, regio's, steden, ASN's, ISP's of cloudregio's. Je kunt filters combineren met {plus} (bijvoorbeeld {amazonPlusGermany} of {comcastPlusCalifornia}). Als latentie een belangrijke statistiek is, gebruikt u filters om de locatie te beperken tot een kleine regio om pieken te voorkomen. {fullDocs}.", + "Suppress Notifications": "Meldingen onderdrukken", + "discordSuppressNotificationsHelptext": "Indien ingeschakeld, worden berichten op het kanaal geplaatst, maar worden er geen push- of bureaubladmeldingen voor ontvangers geactiveerd.", + "Monitor Subtype": "Monitor subtype", + "Check for": "Controleer op", + "domain_expiry_unsupported_monitor_type": "Domein verloop monitoring is niet ondersteund voor dit monitor-type", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" is te kort voor een topleveldomein", + "domain_expiry_unsupported_is_icann": "Het domein \"{domain}\" komt niet in aanmerking voor monitoring van domeinverval, omdat het openbare achtervoegsel \".{publicSuffix}\" geen ICANN is", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" is een IP-adres. Voor het monitoren van het verlopen van domeinen is een domeinnaam vereist", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Controle van domeinverval is niet beschikbaar voor \".{publicSuffix}\" omdat er geen RDAP-service wordt vermeld door IANA", + "GlobalpingIpFamilyInfo": "De IP-versie die moet worden gebruikt. Alleen toegestaan als het doel een hostnaam is.", + "GlobalpingResolverInfo": "IPv4/IPv6-adres of een volledig gekwalificeerde domeinnaam (FQDN). Standaard ingesteld op de lokale netwerkresolver van de probe. U kunt de resolutieserver op elk gewenst moment wijzigen.", + "Location": "Locatie", + "Halo PSA Webhook URL": "Halo PSA webhook URL", + "halopsa_webhook_url_desc": "Voer de webhook-URL uit uw Halo PSA Integration Runbook in (Configuratie > Integraties > Aangepaste integraties > Integratie Runbooks). Selecteer 'Kan alleen worden gestart vanuit Halo en vanaf een openbaar eindpunt' bij het maken van de webhook.", + "username": "Gebruikersnaam", + "password": "Wachtwoord", + "halopsa_username_desc": "Gebruikersnaam voor authenticatie bij Halo PSA webhook", + "halopsa_password_desc": "Wachtwoord voor authenticatie bij Halo PSA webhook", + "screenshot of the website": "Screenshot van de website", + "PushDeer Server URL": "PushDeer server URL", + "To Number": "Naar nummer", + "Never": "Nooit", + "Sort by status": "Sorteer op status", + "Sort by name": "Sorteer op naam", + "Sort by certificate expiry": "Sorteer op certificaat verlooptijd", + "Splunk Rest URL": "Splunk rest URL", + "Severity": "Ernst", + "Message Format": "Berichtformaat", + "Google": "Google", + "Plausible": "Plausibel", + "Details": "Details", + "Matomo": "Matomo", + "mtls-auth-server-ca-label": "CA", + "account settings": "account instellingen", + "Collapse All Groups": "Alle groepen inklappen", + "expectedTlsAlertDescription": "Selecteer de TLS-waarschuwing waarvan u verwacht dat de server deze retourneert. Gebruik {code} om te verifiëren dat mTLS-eindpunten verbindingen weigeren zonder clientcertificaten. Zie {link} voor details.", + "Protocol": "Protocol", + "domain_expiry_unsupported_missing_target": "Geen geldige domeinnaam of hostnaam is ingesteld voor deze monitor", + "Endpoint": "Endpoint", + "signalUseTemplate": "Gebruik een persoonlijk berichtensjabloon", + "signalUseTemplateDescription": "Indien ingeschakeld, wordt het bericht verzonden met een persoonlijk sjabloon. U kunt Liquid-templating gebruiken om het meldingsformaat aan te passen.", + "360messengerAuthToken": "360messenger API Sleutel", + "360messengerRecipient": "Telefoonnummer (s) van de ontvanger", + "360messengerGroupId": "360messenger Groep ID", + "360messengerUseTemplate": "Gebruik een persoonlijk berichtensjabloon", + "360messengerTemplate": "360messenger berichtensjabloon", + "360messengerGroupList": "WhatsApp groepen", + "360messengerSelectGroupList": "Selecteer een groep om toe te voegen", + "360messengerSelectedGroupID": "Geselecteerde Groep ID(s)", + "360messengerEnableSendToGroup": "Schakel versturen naar WhatsApp groep(en) in", + "360messengerCustomMessageTemplate": "Persoonlijk berichtensjabloon", + "360messengerEnableCustomMessage": "Schakel een persoonlijk berichtensjabloon in i.p.v. het standaardbericht.", + "360messengerMessageTemplate": "Berichtensjabloon", + "teamsEnableTags": "Inclusief tags", + "teamsEnableTagsDescription": "Indien ingeschakeld zal het bericht de monitortags bevatten.", + "certificateExpiryNotificationHelp": "De hoeveelheid dagen op voorhand kan in de instellingen worden geconfigureerd.", + "matrixUseTemplate": "Gebruik een persoonlijk berichtensjabloon", + "matrixUseTemplateDescription": "Indien ingeschakeld, wordt het bericht verzonden met een persoonlijk sjabloon.", + "monitorTypeGameServer": "Spelserver", + "monitorTypeDatabase": "Databank Monitor Type", + "monitorTypeSpecial": "Speciaal", + "telnyxApiKey": "API sleutel (V2)", + "telnyxApiKeyHelptext": "Je Telnyx V2 API sleutel van het Telnyx Portal onder API Keys", + "wayToGetMaxToken": "Je kunt een MAX bot token en andere details krijgen van {0}.", + "maxMessenger": "MAX messenger", + "WebSocket Options": "WebSocket opties", + "API Version": "API versie", + "Peer ID": "Peer ID", + "maxApiUrlDescription": "Basis API URL voor MAX messenger. Standaard: https://platform-api.max.ru", + "maxUseTemplate": "Gebruik aangepaste template voor bericht", + "maxTemplateFormatDescription": "MAX messenger ondersteunt platte tekst, Markdown en HTML opmaak.", + "vkApiVersionDescription": "VK API versie die gebruikt wordt voor verzoeken. Behoud de standaardinstelling tenzij je specifiek een andere VK API versie nodig hebt voor compatibiliteit.", + "vkDontParseLinks": "Schakel linkfragmenten uit", + "oracledbConnectionString": "Oracle Database: {connectionString}", + "Connection Timeout": "Verbindingstime-out", + "Bearer Token": "Bearer Token", + "pingPacketSizeWarning": "Een pakketgrootte kleiner dan 16 levert geen round-triptijden op onder Linux", + "maxUseTemplateDescription": "Indien ingeschakeld, wordt het bericht verzonden met een aangepaste sjabloon.", + "Notification Action": "Notificatie Actie", + "vkDontParseLinksDescription": "Indien ingeschakeld, zal VK geen link previews/snippets genereren.", + "vkPeerIdDescription": "Voer het doel-VK peer_id in. Deze waarde wordt ongewijzigd naar de API verzonden.", + "smtpAdditionalHeadersTitle": "Aanvullende headers", + "smtpAdditionalHeadersDesc": "Zie de Nodemailer Custom Headers {0} voor het gebruik.", + "Access Key": "Toegangssleutel", + "ooredooUsernameDescription": "Het email adres dat je gebruikt om in te loggen op het Ooredoo bulk SMS dashboard.", + "ooredooAccessKeyDescription": "Te vinden onder \"API Access Key\" in jouw Ooredoo SMS dashboard. Plak het exact zoals weergegeven — deze wordt voor verzenden automatisch base64 gecodeerd.", + "ooredooBearerTokenDescription": "De token weergegeven na \"Authorization: Bearer\" in het voorbeeld cURL commando in jouw Ooredoo SMS dashboard.", + "ooredooRecipientDescription": "Voer één of meer nummers in gescheiden door een komma of spatie. Lokale 7-cijferige nummers worden automatisch voorgegaan door 960.", + "Leave blank to use the default Ooredoo endpoint.": "Laat leeg om de standaard Ooredoo endpoint te gebruiken.", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) levert via de standalone WxPusher-app op 6 platforms — Android, iOS, HarmonyOS, Windows, macOS en Linux ({download}). Scan de onderstaande QR-code om je SPT te ontvangen en je bent klaar om te verzenden. Scheid meerdere SPT's met een komma (,) — zie de {docs} voor meer informatie.", + "wxPusherDownloadApp": "download de app", + "wxPusherDocs": "officiële handleiding", + "wayToGetEgoSMSToken": "Je kan jouw EgoSMS API gebruikersnaam en wachtwoord {hier}.", + "egosmsSenderDescription": "Verzend ID weergegeven aan ontvangers (max 11 karakters). Standaard EGOSMS wanneer leeg gelaten.", + "egosmsPhoneNumberDescription": "Ontvangers nummer met landscode, zonder + of 00 (vb. 2567XXXXXXXX).", + "Hash Key": "Hash-sleutel", + "Client Key": "Cliënt sleutel", + "Channel": "Kanaal", + "wayToGetMaxChatID": "Specificeer de chat-ID in MAX waarnaar berichten moeten worden verzonden.", + "homeAssistantNotificationActionHelptext": "Een lijst van notificatie acties kunnen in home Assistent gevonden worden onder \"Instellingen > ontwikkelaar hulpmiddelen > acties\" zoek voor melding om uw actie te vinden. voer alleen het gedeelte na \"melding.\" e.g. voor de actie \"notify.mobile_app_xyz\" voer in \"mobile_app_xyz\". voor ingebouwde mobile notificaties, kijk voor \"Verstuur een notificatie via mobile_app_xyz\" (niet \"stuur een notificatie\").", + "wayToGetBearSMSToken": "U kunt een BearSMS API gebruikernaam en hash sleutel ophalen van {0}." +} diff --git a/src/lang/pa.json b/src/lang/pa.json new file mode 100644 index 0000000..8b1ce7f --- /dev/null +++ b/src/lang/pa.json @@ -0,0 +1,7 @@ +{ + "languageName": "پنجابی", + "setupDatabaseChooseDatabase": "ਤੁਸੀਂ ਕਿਹੜਾ ਡੇਟਾਬੇਸ ਵਰਤਣਾ ਚਾਹੁੰਦੇ ਹੋ?", + "setupDatabaseSQLite": "ਇੱਕ ਸਧਾਰਨ ਡੇਟਾਬੇਸ ਫਾਈਲ, ਛੋਟੇ ਪੈਮਾਨੇ ਦੀਆਂ ਤੈਨਾਤੀਆਂ ਲਈ ਸਿਫਾਰਸ਼ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। v2.0.0 ਤੋਂ ਪਹਿਲਾਂ, ਅਪਟਾਈਮ ਕੁਮਾ ਨੇ ਡਿਫੌਲਟ ਡੇਟਾਬੇਸ ਵਜੋਂ SQLite ਦੀ ਵਰਤੋਂ ਕੀਤੀ।", + "setupDatabaseEmbeddedMariaDB": "ਤੁਹਾਨੂੰ ਕੁਝ ਵੀ ਸੈੱਟ ਕਰਨ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ। ਇਸ ਡੌਕਰ ਚਿੱਤਰ ਨੇ ਤੁਹਾਡੇ ਲਈ ਮਾਰੀਆਡੀਬੀ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ 'ਤੇ ਏਮਬੇਡ ਅਤੇ ਕੌਂਫਿਗਰ ਕੀਤਾ ਹੈ। ਅਪਟਾਈਮ ਕੁਮਾ ਯੂਨਿਕਸ ਸਾਕਟ ਦੁਆਰਾ ਇਸ ਡੇਟਾਬੇਸ ਨਾਲ ਜੁੜ ਜਾਵੇਗਾ।", + "setupDatabaseMariaDB": "ਇੱਕ ਬਾਹਰੀ ਮਾਰੀਆਡੀਬੀ ਡੇਟਾਬੇਸ ਨਾਲ ਜੁੜੋ। ਤੁਹਾਨੂੰ ਡਾਟਾਬੇਸ ਕੁਨੈਕਸ਼ਨ ਜਾਣਕਾਰੀ ਨੂੰ ਸੈੱਟ ਕਰਨ ਦੀ ਲੋੜ ਹੈ." +} diff --git a/src/lang/pa_PK.json b/src/lang/pa_PK.json new file mode 100644 index 0000000..5f9ab79 --- /dev/null +++ b/src/lang/pa_PK.json @@ -0,0 +1,12 @@ +{ + "setupDatabaseMariaDB": "ਇੱਕ ਬਾਹਰੀ ਮਾਰੀਆਡੀਬੀ ਡੇਟਾਬੇਸ ਨਾਲ ਜੁੜੋ। ਤੁਹਾਨੂੰ ਡਾਟਾਬੇਸ ਕੁਨੈਕਸ਼ਨ ਜਾਣਕਾਰੀ ਸੈੱਟ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।", + "languageName": "ਅੰਗਰੇਜ਼ੀ", + "setupDatabaseChooseDatabase": "ਤੁਸੀਂ ਕਿਹੜਾ ਡੇਟਾਬੇਸ ਵਰਤਣਾ ਚਾਹੁੰਦੇ ਹੋ?", + "setupDatabaseEmbeddedMariaDB": "ਤੁਹਾਨੂੰ ਕੁਝ ਵੀ ਸੈੱਟ ਕਰਨ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ। ਇਸ ਡੌਕਰ ਚਿੱਤਰ ਨੇ ਤੁਹਾਡੇ ਲਈ ਮਾਰੀਆਡੀਬੀ ਨੂੰ ਸਵੈਚਲਿਤ ਤੌਰ 'ਤੇ ਏਮਬੇਡ ਅਤੇ ਕੌਂਫਿਗਰ ਕੀਤਾ ਹੈ। ਅਪਟਾਈਮ ਕੁਮਾ ਯੂਨਿਕਸ ਸਾਕਟ ਦੁਆਰਾ ਇਸ ਡੇਟਾਬੇਸ ਨਾਲ ਜੁੜ ਜਾਵੇਗਾ।", + "dbName": "ਡਾਟਾਬੇਸ ਦਾ ਨਾਮ", + "Settings": "ਸੈਟਿੰਗਾਂ", + "Dashboard": "ਡੈਸ਼ਬੋਰਡ", + "Help": "ਮਦਦ ਕਰੋ", + "New Update": "ਨਵਾਂ ਅੱਪਡੇਟ", + "setupDatabaseSQLite": "ਇੱਕ ਸਧਾਰਨ ਡੇਟਾਬੇਸ ਫਾਈਲ, ਛੋਟੇ ਪੈਮਾਨੇ ਦੀਆਂ ਤੈਨਾਤੀਆਂ ਲਈ ਸਿਫਾਰਸ਼ ਕੀਤੀ ਜਾਂਦੀ ਹੈ। v2.0.0 ਤੋਂ ਪਹਿਲਾਂ, ਅਪਟਾਈਮ ਕੁਮਾ ਨੇ ਡਿਫੌਲਟ ਡੇਟਾਬੇਸ ਵਜੋਂ SQLite ਦੀ ਵਰਤੋਂ ਕੀਤੀ।" +} diff --git a/src/lang/pl.json b/src/lang/pl.json new file mode 100644 index 0000000..52b607d --- /dev/null +++ b/src/lang/pl.json @@ -0,0 +1,1529 @@ +{ + "languageName": "Polski", + "checkEverySecond": "Sprawdzaj co {0} sekund", + "retryCheckEverySecond": "Ponawiaj co {0} sekund", + "retriesDescription": "Maksymalna liczba powtórzeń, zanim usługa zostanie oznaczona jako niedostępna i zostanie wysłane powiadomienie", + "ignoreTLSError": "Ignoruj błędy TLS/SSL dla stron HTTPS", + "upsideDownModeDescription": "Odwróć status do góry nogami. Jeśli usługa jest osiągalna, to jest oznaczona jako niedostępna.", + "maxRedirectDescription": "Maksymalna liczba przekierowań do wykonania. Ustaw na 0, aby wyłączyć przekierowania.", + "acceptedStatusCodesDescription": "Wybierz kody stanu, które są uważane za prawidłową odpowiedź.", + "passwordNotMatchMsg": "Powtórzone hasło nie pasuje.", + "notificationDescription": "Proszę przypisać powiadomienie do monitora(ów), aby działało.", + "keywordDescription": "Wyszukiwanie słów kluczowych w zwykłym html lub odpowiedzi JSON. Wielkość liter ma znaczenie.", + "pauseDashboardHome": "Wstrzymane", + "deleteMonitorMsg": "Czy na pewno chcesz usunąć ten monitor?", + "deleteNotificationMsg": "Czy na pewno chcesz usunąć to powiadomienie dla wszystkich monitorów?", + "resolverserverDescription": "Cloudflare jest domyślnym serwerem, możesz zmienić serwer resolver w każdej chwili.", + "rrtypeDescription": "Wybierz rodzaj rekordu, który chcesz monitorować", + "pauseMonitorMsg": "Czy na pewno chcesz wstrzymać monitorowanie?", + "enableDefaultNotificationDescription": "Dla każdego nowego monitora to powiadomienie będzie domyślnie włączone. Nadal możesz wyłączyć powiadomienia osobno dla każdego monitora.", + "clearEventsMsg": "Jesteś pewien, że chcesz wyczyścić wszystkie zdarzenia dla tego monitora? Ta akcja permanentnie usunie wszystkie powiązane dane wiadomości i nie może zostać cofnięta.", + "clearHeartbeatsMsg": "Jesteś pewien, że chcesz wyczyścić historię bicia serca dla tego monitora? Ta akcja permanentnie usunie wszystkie dane bicia serca i nie może zostać cofnięta.", + "confirmClearStatisticsMsg": "Jesteś pewien, że chcesz usunąć WSZYSTKIE statystyki?", + "importHandleDescription": "Wybierz 'Pomiń istniejące', jeśli chcesz pominąć każdy monitor lub powiadomienie o tej samej nazwie. 'Nadpisz' spowoduje usunięcie każdego istniejącego monitora i powiadomienia.", + "confirmImportMsg": "Czy na pewno chcesz zaimportować kopię zapasową? Upewnij się, że wybrałeś właściwą opcję importu.", + "twoFAVerifyLabel": "Proszę, podaj swój token 2FA, aby sprawdzić, czy 2FA działa:", + "tokenValidSettingsMsg": "Token jest prawidłowy! Teraz możesz zapisać ustawienia 2FA.", + "confirmEnableTwoFAMsg": "Jesteś pewien, że chcesz włączyć 2FA?", + "confirmDisableTwoFAMsg": "Jesteś pewien, że chcesz wyłączyć 2FA?", + "Settings": "Ustawienia", + "Dashboard": "Panel", + "New Update": "Nowa aktualizacja", + "Language": "Język", + "Appearance": "Wygląd", + "Theme": "Motyw", + "General": "Ogólne", + "Version": "Wersja", + "Check Update On GitHub": "Sprawdź aktualizację na GitHub", + "List": "Lista", + "Add": "Dodaj", + "Add New Monitor": "Dodaj monitor", + "Quick Stats": "Szybki podgląd statystyk", + "Up": "Online", + "Down": "Offline", + "Pending": "Oczekuje", + "Unknown": "Nieznane", + "Pause": "Wstrzymaj", + "Name": "Nazwa", + "Status": "Status", + "DateTime": "Data i godzina", + "Message": "Wiadomość", + "No important events": "Brak ważnych wydarzeń", + "Resume": "Wznów", + "Edit": "Edytuj", + "Delete": "Usuń", + "Current": "Aktualny", + "Uptime": "Czas pracy", + "Cert Exp.": "Data ważności certyfikatu.", + "day": "dzień | dni", + "-day": "dni", + "hour": "godzina", + "-hour": "godzin", + "Response": "Odpowiedź", + "Ping": "Ping", + "Monitor Type": "Rodzaj monitora", + "Keyword": "Słowo kluczowe", + "Friendly Name": "Przyjazna nazwa", + "URL": "URL", + "Hostname": "Nazwa hosta", + "Port": "Port", + "Heartbeat Interval": "Częstotliwość bicia serca", + "Retries": "Prób", + "Heartbeat Retry Interval": "Częstotliwość ponawiania bicia serca", + "Advanced": "Zaawansowane", + "Upside Down Mode": "Tryb odwrócony", + "Max. Redirects": "Maks. przekierowań", + "Accepted Status Codes": "Akceptowane kody statusu", + "Save": "Zapisz", + "Notifications": "Powiadomienia", + "Not available, please setup.": "Niedostępne, proszę skonfigurować.", + "Setup Notification": "Skonfiguruj powiadomienie", + "Light": "Jasny", + "Dark": "Ciemny", + "Auto": "Automatyczny", + "Theme - Heartbeat Bar": "Motyw - pasek bicia serca", + "Normal": "Domyślne", + "Bottom": "Na dole", + "None": "Brak", + "Timezone": "Strefa czasowa", + "Search Engine Visibility": "Widoczność w wyszukiwarce", + "Allow indexing": "Zezwól na indeksowanie", + "Discourage search engines from indexing site": "Zniechęcaj wyszukiwarki do indeksowania strony", + "Change Password": "Zmień hasło", + "Current Password": "Aktualne hasło", + "New Password": "Nowe hasło", + "Repeat New Password": "Powtórz nowe hasło", + "Update Password": "Zaktualizuj hasło", + "Disable Auth": "Wyłącz autoryzację", + "Enable Auth": "Włącz autoryzację", + "disableauth.message1": "Czy na pewno chcesz {disableAuth}?", + "disable authentication": "wyłączyć autoryzację", + "disableauth.message2": "Jest przeznaczony dla {intendThirdPartyAuth} przed Uptime Kuma, taką jak Cloudflare Access.", + "where you intend to implement third-party authentication": "kogoś, kto ma autoryzację zewnętrzną", + "Please use this option carefully!": "Proszę używać tej opcji ostrożnie!", + "Logout": "Wyloguj", + "Leave": "Zostaw", + "I understand, please disable": "Rozumiem, proszę wyłączyć", + "Confirm": "Potwierdź", + "Yes": "Tak", + "No": "Nie", + "Username": "Nazwa użytkownika", + "Password": "Hasło", + "Remember me": "Zapamiętaj mnie", + "Login": "Zaloguj", + "No Monitors, please": "Brak monitorów, proszę", + "add one": "dodać jeden", + "Notification Type": "Rodzaj powiadomienia", + "Email": "E-mail", + "Test": "Test", + "Certificate Info": "Informacje o certyfikacie", + "Resolver Server": "Serwer rozwiązywania nazw", + "Resource Record Type": "Typ rekordu zasobów", + "Last Result": "Ostatni wynik", + "Create your admin account": "Utwórz swoje konto administratora", + "Repeat Password": "Powtórz hasło", + "Import Backup": "Importuj kopię zapasową", + "Export Backup": "Eksportuj kopię zapasową", + "Export": "Eksportuj", + "Import": "Importuj", + "respTime": "Czas odp. (ms)", + "notAvailableShort": "N/D", + "Default enabled": "Włącz domyślnie", + "Apply on all existing monitors": "Zastosuj do istniejących monitorów", + "Create": "Stwórz", + "Clear Data": "Usuń dane", + "Events": "Zdarzenia", + "Heartbeats": "Bicia serca", + "Auto Get": "Wykryj", + "backupDescription": "Możesz wykonać kopię zapasową wszystkich monitorów i wszystkich powiadomień do pliku JSON.", + "backupDescription2": "PS: Historia i dane zdarzeń nie są uwzględniane.", + "backupDescription3": "Poufne dane, takie jak tokeny powiadomień, są zawarte w pliku eksportu, prosimy o ostrożne przechowywanie.", + "alertNoFile": "Wybierz plik do importu.", + "alertWrongFileType": "Proszę wybrać plik JSON.", + "Clear all statistics": "Wyczyść wszystkie statystyki", + "Skip existing": "Pomiń istniejące", + "Overwrite": "Nadpisz", + "Options": "Opcje", + "Keep both": "Zachowaj oba", + "Verify Token": "Zweryfikuj token", + "Setup 2FA": "Konfiguracja 2FA", + "Enable 2FA": "Włącz 2FA", + "Disable 2FA": "Wyłącz 2FA", + "2FA Settings": "Ustawienia 2FA", + "Two Factor Authentication": "Uwierzytelnienie dwuskładnikowe", + "Active": "Włączone", + "Inactive": "Wyłączone", + "Token": "Token", + "Show URI": "Pokaż URI", + "Tags": "Tagi", + "Add New below or Select...": "Dodaj nowy poniżej lub wybierz…", + "Tag with this name already exist.": "Tag o tej nazwie już istnieje.", + "Tag with this value already exist.": "Tag o tej wartości już istnieje.", + "color": "kolor", + "value (optional)": "wartość (opcjonalnie)", + "Gray": "Szary", + "Red": "Czerwony", + "Orange": "Pomarańczowy", + "Green": "Zielony", + "Blue": "Niebieski", + "Indigo": "Indygo", + "Purple": "Fioletowy", + "Pink": "Różowy", + "Search...": "Szukaj…", + "Avg. Ping": "Średni ping", + "Avg. Response": "Średnia odpowiedź", + "Entry Page": "Strona startowa", + "statusPageNothing": "Nic tu nie ma, dodaj grupę lub monitor.", + "No Services": "Brak usług", + "All Systems Operational": "Wszystkie systemy działają poprawnie", + "Partially Degraded Service": "Część usług nie działa", + "Degraded Service": "Usługa nie działa", + "Add Group": "Dodaj grupę", + "Add a monitor": "Dodaj monitor", + "Edit Status Page": "Edytuj stronę statusu", + "Go to Dashboard": "Idź do panelu", + "Status Page": "Strona statusu", + "Status Pages": "Strony statusów", + "defaultNotificationName": "Moje powiadomienie {notification} ({number})", + "here": "tutaj", + "Required": "Wymagane", + "telegram": "Telegram", + "Bot Token": "Token bota", + "wayToGetTelegramToken": "Token można uzyskać z {0}.", + "Chat ID": "Identyfikator czatu", + "supportTelegramChatID": "Czat wsparcia technicznego / Bezpośrednia rozmowa / Czat grupowy", + "wayToGetTelegramChatID": "Możesz uzyskać swój identyfikator czatu, wysyłając wiadomość do bota i przechodząc pod ten adres URL, aby wyświetlić identyfikator czatu:", + "YOUR BOT TOKEN HERE": "TWÓJ TOKEN BOTA", + "chatIDNotFound": "Identyfikator czatu nie znaleziony, najpierw napisz do bota", + "webhook": "Webhook", + "Post URL": "Adres URL", + "Content Type": "Rodzaj danych", + "webhookJsonDesc": "{0} jest dobry w przypadku serwerów HTTP, takich jak express.js", + "webhookFormDataDesc": "{multipart} jest dobry dla PHP, musisz jedynie przetworzyć dane przez {decodeFunction}", + "smtp": "Email (SMTP)", + "secureOptionNone": "Brak / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Zignoruj błędy TLS", + "From Email": "Nadawca (OD)", + "To Email": "Odbiorca (DO)", + "smtpCC": "DW", + "smtpBCC": "UDW", + "discord": "Discord", + "Discord Webhook URL": "URL webhook Discorda", + "wayToGetDiscordURL": "Możesz go uzyskać, przechodząc do Ustawienia serwera -> Integracje -> Webhooki -> Tworzenie webhooka", + "Bot Display Name": "Wyświetlana nazwa bota", + "Prefix Custom Message": "Własny początek wiadomości", + "Hello @everyone is...": "Hej {'@'}everyone…", + "teams": "Microsoft Teams", + "Webhook URL": "URL webhooka", + "wayToGetTeamsURL": "Możesz dowiedzieć się, jak utworzyć adres url webhooka {0}.", + "signal": "Signal", + "Number": "Numer", + "Recipients": "Odbiorcy", + "needSignalAPI": "Musisz mieć klienta Signal z REST API.", + "wayToCheckSignalURL": "W celu dowiedzenia się, jak go skonfigurować, odwiedź poniższy link:", + "signalImportant": "UWAGA: Nie można mieszać nazw grup i numerów odbiorców!", + "gotify": "Gotify", + "Application Token": "Token aplikacji", + "Server URL": "Server URL", + "Priority": "Priorytet", + "slack": "Slack", + "Icon Emoji": "Ikona emoji", + "Channel Name": "Nazwa kanału", + "Uptime Kuma URL": "Adres Uptime Kuma", + "aboutWebhooks": "Więcej informacji na temat webhooków: {0}", + "aboutChannelName": "Podaj nazwę kanału {0} w polu Nazwa kanału, jeśli chcesz pominąć kanał webhooka. Np.: #inny-kanal", + "aboutKumaURL": "Jeśli pozostawisz pole Adres Uptime Kuma puste, domyślnie będzie to strona projektu na GitHub.", + "emojiCheatSheet": "Ściąga emoji: {0}", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "Push od Techulus", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (obsługuje 50+ usług powiadomień)", + "GoogleChat": "Google Chat (wyłącznie Google Workspace)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "Klucz użytkownika", + "Device": "Urządzenie", + "Message Title": "Tytuł wiadomości", + "Notification Sound": "Dźwięk powiadomienia", + "More info on:": "Więcej informacji na: {0}", + "pushoverDesc1": "Priorytet awaryjny (2) ma domyślny 30-sekundowy limit czasu między kolejnymi próbami i wygaśnie po 1 godzinie.", + "pushoverDesc2": "Jeśli chcesz wysyłać powiadomienia na różne urządzenia, wypełnij pole Urządzenie.", + "SMS Type": "Rodzaj SMS", + "octopushTypePremium": "Premium (szybki - rekomendowany dla powiadomień)", + "octopushTypeLowCost": "Low Cost (wolny, czasami blokowany przez operatorów)", + "Check octopush prices": "Sprawdź ceny Octopush {0}.", + "octopushPhoneNumber": "Numer telefonu (format międzynarodowy np.: +33612345678)", + "octopushSMSSender": "Nadawca SMS: 3-11 znaków alfanumerycznych i spacji (a-zA-Z0-9)", + "LunaSea Device ID": "Identyfikator urządzenia LunaSea", + "Apprise URL": "URL Apprise", + "Example:": "Przykład: {0}", + "Read more:": "Czytaj dalej: {0}", + "Status:": "Status: {0}", + "Read more": "Czytaj dalej", + "appriseInstalled": "Apprise jest zainstalowane.", + "appriseNotInstalled": "Apprise nie zostało zainstalowane. {0}", + "Access Token": "Token dostępu", + "Channel access token": "Token dostępu kanału", + "Line Developers Console": "Konsola deweloperska Line", + "lineDevConsoleTo": "Konsola deweloperska Line - {0}", + "Basic Settings": "Ustawienia ogólne", + "User ID": "Identyfikator użytkownika", + "Messaging API": "API wiadomości", + "wayToGetLineChannelToken": "Najpierw uzyskaj dostęp do {0}, utwórz dostawcę i kanał (Messaging API), a następnie możesz uzyskać token dostępu do kanału i identyfikator użytkownika z wyżej wymienionych pozycji menu.", + "Icon URL": "Adres Ikony", + "aboutIconURL": "Możesz podać link do zdjęcia w \"Adres URL ikony\", aby zastąpić domyślne zdjęcie profilowe. Nie będzie używany, jeśli ustawiona jest ikona emoji.", + "aboutMattermostChannelName": "Możesz zastąpić domyślny kanał, na którym publikowane są posty webhooka, wpisując nazwę kanału w polu \"Nazwa kanału\". Należy to włączyć w ustawieniach webhooka Mattermost. Np.: #inny-kanał", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - tanie, lecz wolne. Dostępne tylko w Polsce.", + "promosmsTypeFlash": "SMS FLASH - wiadomość automatycznie wyświetli się na urządzeniu. Dostępne tylko w Polsce.", + "promosmsTypeFull": "SMS FULL - szybkie i dostępne międzynarodowo. Wersja premium usługi, która pozwala min. ustawić własną nazwę nadawcy.", + "promosmsTypeSpeed": "SMS SPEED - wysyłka priorytetowa, ma wszystkie zalety SMS FULL.", + "promosmsPhoneNumber": "Numer odbiorcy", + "promosmsSMSSender": "Nadawca SMS (wcześniej zatwierdzone nazwy z panelu PromoSMS)", + "promosmsAllowLongSMS": "Zezwól na długie SMSy", + "Primary Base URL": "Główny URL", + "Push URL": "Push URL", + "needPushEvery": "Powinieneś wywoływać ten URL co {0} sekund.", + "pushOptionalParams": "Parametry opcjonalne: {0}", + "emailCustomSubject": "Niestandardowy temat", + "checkPrice": "Sprawdź ceny {0}:", + "octopushLegacyHint": "Czy używasz starszej wersji Octopush (2011-2020) czy nowej wersji?", + "Feishu WebHookUrl": "Adres webhooka Feishu", + "matrixHomeserverURL": "Adres URL serwera domowego (z http(s):// i opcjonalnie port)", + "Internal Room Id": "Wewnętrzne ID pokoju", + "matrixDesc1": "Możesz znaleźć wewnętrzne ID pokoju, patrząc w zaawansowanej sekcji ustawień pokoju w twoim kliencie Matrix. Powinien on wyglądać jak !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Jest wysoce zalecane, abyś stworzył nowego użytkownika i nie używał tokena dostępu swojego użytkownika Matrix, ponieważ pozwoli on na pełny dostęp do twojego konta i wszystkich pokoi, do których dołączyłeś. Zamiast tego, utwórz nowego użytkownika i zaproś go tylko do pokoju, w którym chcesz otrzymywać powiadomienia. Możesz uzyskać token dostępu przez uruchomienie {0}", + "Method": "Metoda", + "Body": "Treść", + "Headers": "Nagłówki", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "Nagłówki żądania nie są w poprawnym formacie JSON: ", + "BodyInvalidFormat": "Treść żądania nie jest w poprawnym formacie JSON: ", + "Monitor History": "Historia monitorów", + "clearDataOlderThan": "Przechowuj dane dotyczące historii monitorowania {0} dni.", + "PasswordsDoNotMatch": "Hasła nie pasują.", + "records": "rekordy", + "One record": "Jeden rekord", + "steamApiKeyDescription": "Do monitorowania serwera gier Steam potrzebny jest klucz Steam Web-API. Możesz zarejestrować swój klucz API tutaj: ", + "Current User": "Aktualny użytkownik", + "topic": "Temat", + "topicExplanation": "Temat MQTT do monitorowania", + "successMessage": "Komunikat o powodzeniu", + "successMessageExplanation": "Komunikat MQTT, który zostanie uznany za powodzenie", + "recent": "Ostatnie", + "Done": "Zrobione", + "Info": "Info", + "Security": "Bezpieczeństwo", + "Steam API Key": "Klucz Steam API", + "Shrink Database": "Zmniejsz bazę danych", + "Pick a RR-Type...": "Wybierz typ RR…", + "Pick Accepted Status Codes...": "Wybierz akceptowalne kody statusu…", + "Default": "Domyślnie", + "HTTP Options": "Opcje HTTP", + "Create Incident": "Stwórz incydent", + "Title": "Tytuł", + "Content": "Treść", + "Style": "Styl", + "info": "info", + "warning": "ostrzeżenie", + "danger": "niebezpieczeństwo", + "primary": "podstawowy", + "light": "jasny", + "dark": "ciemny", + "Post": "Wyślij", + "Please input title and content": "Podaj tytuł i treść", + "Created": "Stworzony", + "Last Updated": "Ostatnio zaktualizowany", + "Unpin": "Odepnij", + "Switch to Light Theme": "Przełącz na jasny motyw", + "Switch to Dark Theme": "Przełącz na ciemny motyw", + "Show Tags": "Pokaż tagi", + "Hide Tags": "Ukryj tagi", + "Description": "Opis", + "No monitors available.": "Brak dostępnych monitorów.", + "Add one": "Dodaj jeden", + "No Monitors": "Brak monitorów", + "Untitled Group": "Nienazwana grupa", + "Services": "Usługi", + "Discard": "Odrzuć", + "Cancel": "Anuluj", + "Powered by": "Napędzane przez", + "clicksendsms": "ClickSend SMS", + "apiCredentials": "Poświadczenia API", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "Nazwa użytkownika API (z prefiksem webapi_)", + "serwersmsAPIPassword": "Hasło API", + "serwersmsPhoneNumber": "Numer telefonu", + "serwersmsSenderName": "Nazwa nadawcy (zatwierdzona w panelu klienta)", + "smseagle": "SMSEagle", + "smseagleTo": "Numer/y telefonu", + "smseagleGroup": "Grupa/y z Książki adresowej", + "smseagleContact": "Kontakt/y z Książki adresowej", + "smseagleRecipientType": "Typ odbiorcy", + "smseagleRecipient": "Odbiorca/y (wiele musi być oddzielone przecinkami)", + "smseagleToken": "Klucz dostępu API", + "smseagleUrl": "URL Twojego urządzenia SMSEagle", + "smseagleEncoding": "Wyślij jako Unicode (domyślnie=GSM-7)", + "smseaglePriority": "Priorytet wiadomości (0-9, najwyższy priorytet = 9)", + "stackfield": "Stackfield", + "Customize": "Dostosuj", + "Custom Footer": "Niestandardowa stopka", + "Custom CSS": "Niestandardowy CSS", + "smtpDkimSettings": "Ustawienia DKIM", + "smtpDkimDesc": "Zapoznaj się z Nodemailer DKIM {0}, aby dowiedzieć się więcej.", + "documentation": "dokumentacja", + "smtpDkimDomain": "Nazwa domeny", + "smtpDkimKeySelector": "Selektor klucza", + "smtpDkimPrivateKey": "Klucz prywatny", + "smtpDkimHashAlgo": "Algorytm haszujący (opcjonalne)", + "smtpDkimheaderFieldNames": "Klucze nagłówka do podpisu (opcjonalne)", + "smtpDkimskipFields": "Klucze nagłówka do pominięcia (opcjonalne)", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "Punkt końcowy API", + "alertaEnvironment": "Środowisko", + "alertaApiKey": "Klucz API", + "alertaAlertState": "Stan alarmowy", + "alertaRecoverState": "Stan odzyskania", + "deleteStatusPageMsg": "Jesteś pewien, że chcesz usunąć tę stronę statusów?", + "Proxies": "Proxy", + "default": "Domyślny", + "enabled": "Włączony", + "setAsDefault": "Ustaw jako domyślny", + "deleteProxyMsg": "Jesteś pewien, że chcesz usunąć proxy ze wszystkich monitorów?", + "proxyDescription": "Proxy muszą być przypisane do monitora, aby działały.", + "enableProxyDescription": "Ten serwer proxy nie będzie miał wpływu na żądania monitorów, dopóki nie zostanie aktywowany. Możesz kontrolować tymczasowe wyłączenie serwera proxy ze wszystkich monitorów za pomocą statusu aktywacji.", + "setAsDefaultProxyDescription": "Ten serwer proxy będzie domyślnie włączony dla nowych monitorów. Można go jednak wyłączyć osobno dla każdego monitora.", + "Certificate Chain": "Łańcuch certyfikatów", + "Valid": "Ważny", + "Invalid": "Nieważny", + "AccessKeyId": "ID klucza dostępu", + "SecretAccessKey": "AccessKey Sekret", + "PhoneNumbers": "Numery telefonów", + "TemplateCode": "Kod szablonu", + "SignName": "Podpis", + "Sms template must contain parameters: ": "Szablon sms musi posiadać parametry: ", + "Bark Endpoint": "Punkt końcowy Bark", + "WebHookUrl": "Adres webhooka", + "SecretKey": "Tajny klucz", + "For safety, must use secret key": "Ze względów bezpieczeństwa musisz użyć tajnego klucza", + "Device Token": "Token urządzenia", + "Platform": "Platforma", + "Huawei": "Huawei", + "High": "Wysoki", + "Retry": "Ponów", + "Topic": "Temat", + "WeCom Bot Key": "Klucz bota WeCom", + "Setup Proxy": "Skonfiguruj proxy", + "Proxy Protocol": "Protokół proxy", + "Proxy Server": "Serwer proxy", + "Proxy server has authentication": "Serwer proxy ma autoryzację", + "User": "Użytkownik", + "Installed": "Zainstalowany", + "Not installed": "Nie zainstalowany", + "Running": "Działa", + "Not running": "Nie działa", + "Remove Token": "Usuń token", + "Start": "Start", + "Stop": "Stop", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Dodaj nową stronę statusów", + "Slug": "Symbol", + "Accept characters:": "Dozwolone znaki:", + "startOrEndWithOnly": "Zaczynające się i kończące wyłącznie {0} znakami", + "No consecutive dashes": "Bez powtarzających się myślników", + "Next": "Dalej", + "The slug is already taken. Please choose another slug.": "Ten symbol jest już zajęty. Proszę, wybierz inny.", + "No Proxy": "Bez proxy", + "Authentication": "Uwierzytelnianie", + "HTTP Basic Auth": "Podstawowa autoryzacja HTTP", + "New Status Page": "Nowa strona statusu", + "Page Not Found": "Strona nie została znaleziona", + "Reverse Proxy": "Zwrotny serwer proxy", + "Backup": "Backup", + "About": "O skrypcie", + "wayToGetCloudflaredURL": "(Pobierz cloudflared z {0})", + "cloudflareWebsite": "strona Cloudflare", + "Message:": "Wiadomość:", + "Don't know how to get the token? Please read the guide:": "Nie wiesz, jak uzyskać token? Przeczytaj proszę poradnik:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Bieżące połączenie może zostać utracone, jeśli aktualnie łączysz się przez tunel Cloudflare. Czy na pewno chcesz to przerwać? Wpisz swoje aktualne hasło, aby je potwierdzić.", + "Other Software": "Inne oprogramowanie", + "For example: nginx, Apache and Traefik.": "Na przykład: nginx, Apache i Traefik.", + "Please read": "Przeczytaj proszę", + "Subject:": "Temat:", + "Valid To:": "Ważny do:", + "Days Remaining:": "Pozostało dni:", + "Issuer:": "Wydawca:", + "Fingerprint:": "Odcisk palca:", + "No status pages": "Brak stron statusów", + "Domain Name Expiry Notification": "Powiadomienie o wygasaniu domeny", + "Proxy": "Proxy", + "Date Created": "Data stworzenia", + "onebotHttpAddress": "Adres HTTP OneBot", + "onebotMessageType": "Rodzaj wiadomości OneBot", + "onebotGroupMessage": "Grupowa", + "onebotPrivateMessage": "Prywatna", + "onebotUserOrGroupId": "ID Grupy/Użytkownika", + "onebotSafetyTips": "Ze względów bezpieczeństwa musisz ustawić token dostępu", + "PushDeer Key": "Klucz PushDeer", + "Footer Text": "Treść stopki", + "Show Powered By": "Pokaż co napędza stronę", + "Domain Names": "Domeny", + "signedInDisp": "Zalogowany jako {0}", + "signedInDispDisabled": "Autoryzacja wyłączona.", + "resendEveryXTimes": "Wysyłaj ponownie co {0} razy", + "resendDisabled": "Ponowne wysyłanie jest wyłączone", + "Maintenance": "Konserwacja", + "statusMaintenance": "Konserwacja", + "Schedule maintenance": "Planowanie konserwacji", + "Affected Monitors": "Monitory dotknięte problemem", + "Pick Affected Monitors...": "Wybierz monitory, których to dotyczy…", + "Start of maintenance": "Rozpoczęcie konserwacji", + "All Status Pages": "Wszystkie strony statusu", + "Select status pages...": "Wybierz strony statusu…", + "recurringIntervalMessage": "Uruchom raz dziennie | Uruchom raz na {0} dni", + "affectedMonitorsDescription": "Wybierz monitory, których dotyczy bieżąca konserwacja", + "affectedStatusPages": "Pokaż ten komunikat o konserwacji na wybranych stronach statusu", + "atLeastOneMonitor": "Wybierz co najmniej jeden monitor, którego dotyczy problem", + "deleteMaintenanceMsg": "Czy na pewno chcesz usunąć tę konserwację?", + "dnsPortDescription": "Port serwera DNS. Domyślnie 53. Możesz zmienić port w dowolnym momencie.", + "Resend Notification if Down X times consecutively": "Wyślij ponownie powiadomienie, jeśli nie działa X razy z rzędu", + "error": "błąd", + "critical": "krytyczny", + "wayToGetPagerDutyKey": "Możesz to uzyskać, przechodząc do Service -> Service Directory -> (wybierz usługę) -> Integrations -> Add integration. Tutaj możesz wyszukać \"Events API V2\". Więcej informacji {0}", + "Integration Key": "Klucz integracji", + "Integration URL": "Adres URL integracji", + "Auto resolve or acknowledged": "Automatycznie rozwiązany lub potwierdzony", + "do nothing": "nie rób nic", + "auto acknowledged": "auto potwierdzony", + "auto resolve": "automatycznie rozwiązany", + "Bark Group": "Grupa Bark", + "Bark Sound": "Dźwięk Bark", + "HTTP Headers": "Nagłówki HTTP", + "Trust Proxy": "Ufaj proxy", + "HomeAssistant": "Asystent domowy", + "RadiusSecret": "Sekretny klucz Radius", + "RadiusSecretDescription": "Współdzielony sekretny klucz pomiędzy klientem a serwerem", + "RadiusCalledStationId": "Id stacji wywoływanej", + "RadiusCalledStationIdDescription": "Identyfikator wywoływanego urządzenia", + "RadiusCallingStationId": "Id stacji wywoławczej", + "RadiusCallingStationIdDescription": "Identyfikator urządzenia wywołującego", + "Certificate Expiry Notification": "Powiadomienie o wygaśnięciu certyfikatu", + "API Username": "Nazwa użytkownika API", + "API Key": "Klucz API", + "Recipient Number": "Numer odbiorcy", + "From Name/Number": "Od nazwa/numer", + "Leave blank to use a shared sender number.": "Pozostaw puste, aby użyć wspólnego numeru nadawcy.", + "Octopush API Version": "Wersja API Octopush", + "Legacy Octopush-DM": "Starsze Octopush-DM", + "endpoint": "punkt końcowy", + "octopushAPIKey": "\"API key\" z poświadczeń HTTP API w panelu sterowania", + "octopushLogin": "\"Login\" z poświadczeń HTTP API w panelu sterowania", + "promosmsLogin": "Nazwa logowania API", + "promosmsPassword": "Hasło API", + "pushoversounds pushover": "Pushover (domyślny)", + "pushoversounds bike": "Rower", + "pushoversounds bugle": "chrząszcz", + "pushoversounds cashregister": "Kasa fiskalna", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Kosmiczny", + "pushoversounds falling": "Spadek", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Przychodzące", + "pushoversounds intermission": "Przerwa", + "pushoversounds magic": "Magia", + "pushoversounds mechanical": "Mechaniczny", + "pushoversounds pianobar": "fortepianowy klawisz", + "pushoversounds siren": "Syrena", + "pushoversounds spacealarm": "Alarm kosmiczny", + "pushoversounds tugboat": "Holownik", + "pushoversounds alien": "Alien Alarm (długie)", + "pushoversounds climb": "Climb (długie)", + "pushoversounds persistent": "Persistent (długie)", + "pushoversounds echo": "Pushover Echo (długie)", + "pushoversounds updown": "Up Down (długie)", + "pushoversounds vibrate": "Tylko wibracje", + "pushoversounds none": "Brak (cisza)", + "pushyAPIKey": "Tajny klucz API", + "pushyToken": "Token urządzenia", + "Show update if available": "Pokaż aktualizację, jeśli jest dostępna", + "Also check beta release": "Sprawdź również wydanie beta", + "Using a Reverse Proxy?": "Używasz odwróconego proxy?", + "Check how to config it for WebSocket": "Sprawdź jak go skonfigurować dla WebSocket", + "Steam Game Server": "Serwer gry Steam", + "Most likely causes:": "Najbardziej prawdopodobne przyczyny:", + "The resource is no longer available.": "Zasób nie jest już dostępny.", + "There might be a typing error in the address.": "W adresie może być błąd w pisowni.", + "What you can try:": "Co możesz spróbować:", + "Retype the address.": "Ponownie wpisz adres.", + "Go back to the previous page.": "Wróć do poprzedniej strony.", + "Coming Soon": "Wkrótce", + "wayToGetClickSendSMSToken": "Możesz uzyskać nazwę użytkownika API i klucz API z {0}.", + "Connection String": "Ciąg połączenia", + "Query": "Zapytanie", + "settingsCertificateExpiry": "Wygaśnięcie certyfikatu TLS", + "certificationExpiryDescription": "Monitory HTTPS uruchamiają powiadomienia o wygaśnięciu certyfikatu TLS w:", + "Setup Docker Host": "Konfiguracja hosta Docker", + "Connection Type": "Typ połączenia", + "Docker Daemon": "Demon Dockera", + "deleteDockerHostMsg": "Czy na pewno chcesz usunąć ten host Dockera dla wszystkich monitorów?", + "socket": "Gniazdo", + "tcp": "TCP / HTTP", + "Docker Container": "Kontener Dockera", + "Container Name / ID": "Nazwa kontenera / ID", + "Docker Host": "Host Dockera", + "Docker Hosts": "Hosty Dockera", + "ntfy Topic": "Temat ntfy", + "Domain": "Domena", + "Workstation": "Stacja robocza", + "disableCloudflaredNoAuthMsg": "Jesteś w trybie No Auth, hasło nie jest wymagane.", + "trustProxyDescription": "Zaufaj nagłówkom 'X-Forwarded-*'. Jeśli chcesz uzyskać poprawne IP klienta, a twój Uptime Kuma jest za proxy, takim jak Nginx lub Apache, powinieneś to włączyć.", + "wayToGetLineNotifyToken": "Możesz uzyskać token dostępu z {0}", + "Examples": "Przykłady", + "Home Assistant URL": "URL Home Assistant", + "Long-Lived Access Token": "Długotrwały token dostępu", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Długotrwały token dostępu można utworzyć klikając na nazwę swojego profilu (na dole po lewej stronie) i przewijając do dołu, a następnie klikając Create Token.", + "Notification Service": "Usługa powiadamiania", + "default: notify all devices": "domyślnie: powiadamiaj wszystkie urządzenia", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Listę usług powiadamiania można znaleźć w Home Assistant pod \"Developer Tools > Services\" wyszukaj \"notification\", aby znaleźć nazwę swojego urządzenia/telefonu.", + "Automations can optionally be triggered in Home Assistant:": "Automaty mogą być opcjonalnie uruchamiane w Home Assistant:", + "Trigger type:": "Typ wyzwalacza:", + "Event type:": "Typ zdarzenia:", + "Event data:": "Dane o zdarzeniu:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Następnie wybierz akcję, na przykład przełącz scenę na taką, w której światło RGB jest czerwone.", + "Frontend Version": "Wersja frontu", + "Frontend Version do not match backend version!": "Wersja frontu nie pasuje do wersji backendu!", + "Base URL": "Bazowy adres URL", + "goAlertInfo": "GoAlert to aplikacja open source do planowania, automatycznych eskalacji i powiadomień (jak SMS lub połączenia głosowe). Automatycznie angażuj właściwą osobę, we właściwy sposób i we właściwym czasie! {0}", + "goAlertIntegrationKeyInfo": "Pobierz generyczny klucz integracyjny API dla usługi, którego wartość skopiowanego tokena URL jest zwykle w formacie \"aaaaaaaa-bbb-cccc-dddd-eeeeee\".", + "goAlert": "GoAlert", + "backupOutdatedWarning": "Przestarzałe: ponieważ dodano wiele funkcji i funkcja tworzenia kopii zapasowych nie jest wystarczająco utrzymywana, nie może generować ani przywracać pełnej kopii zapasowej.", + "backupRecommend": "Zamiast tego należy wykonać bezpośrednią kopię zapasową woluminu lub folderu danych (./data/).", + "Optional": "Opcjonalne", + "squadcast": "Squadcast", + "SendKey": "Przycisk Wyślij", + "SMSManager API Docs": "Dokumentacja API SMSManager", + "Gateway Type": "Typ bramy", + "SMSManager": "SMSManager", + "You can divide numbers with": "Możesz dzielić liczby przez", + "or": "lub", + "recurringInterval": "odstęp czasu", + "Recurring": "Powtarzający się", + "strategyManual": "Aktywowany/dezaktywowany ręcznie", + "warningTimezone": "Używa strefy czasowej serwera", + "weekdayShortMon": "pon", + "weekdayShortTue": "wt", + "weekdayShortWed": "śr", + "weekdayShortThu": "czw", + "weekdayShortFri": "pt", + "weekdayShortSat": "sob", + "weekdayShortSun": "niedz", + "dayOfWeek": "dzień tygodnia", + "dayOfMonth": "dzień miesiąca", + "lastDay": "Ostatni dzień", + "lastDay1": "Ostatni dzień miesiąca", + "lastDay2": "2. ostatni dzień miesiąca", + "lastDay3": "3. ostatni dzień miesiąca", + "lastDay4": "4. ostatni dzień miesiąca", + "No Maintenance": "Brak konserwacji", + "pauseMaintenanceMsg": "Jesteś pewien, że chcesz zatrzymać?", + "maintenanceStatus-under-maintenance": "Podczas konserwacji", + "maintenanceStatus-inactive": "Nieaktywny", + "maintenanceStatus-scheduled": "Zaplanowany", + "maintenanceStatus-ended": "Zakończony", + "maintenanceStatus-unknown": "Nieznany", + "Display Timezone": "Wyświetlana strefa czasowa", + "Server Timezone": "Strefa czasowa serwera", + "statusPageMaintenanceEndDate": "Koniec", + "Help": "Pomoc", + "Passive Monitor Type": "Pasywny typ monitora", + "Enable": "Włącz", + "confirmDeleteTagMsg": "Czy na pewno chcesz usunąć ten tag? Monitory powiązane z tym tagiem nie zostaną usunięte.", + "Kook": "Kook", + "Enable TLS": "Włącz TLS", + "webhookAdditionalHeadersDesc": "Ustawia dodatkowe nagłówki wysyłane z webhookiem. Każdy nagłówek powinien być zdefiniowany jako klucz/wartość JSON.", + "dnsCacheDescription": "Może nie działać w niektórych środowiskach IPv6, wyłącz ją, jeśli napotkasz jakiekolwiek problemy.", + "wayToGetKookBotToken": "Utwórz aplikację i uzyskaj swój token bota na {0}", + "wayToGetKookGuildID": "Włącz 'Developer Mode' w ustawieniach Kook'a i kliknij prawym przyciskiem myszy na gildię, aby uzyskać jej ID", + "Game": "Gra", + "Specific Monitor Type": "Konkretny typ monitora", + "Monitor": "Monitor | Monitory", + "webhookAdditionalHeadersTitle": "Dodatkowe nagłówki", + "Custom": "Własny", + "ZohoCliq": "ZohoCliq", + "Disable": "Wyłącz", + "Date and Time": "Data i czas", + "IconUrl": "URL ikony", + "Enable DNS Cache": "(Przestarzałe) Włącz pamięć podręczną DNS dla monitorów HTTP", + "Single Maintenance Window": "Pojedyncze okno konserwacji", + "Effective Date Range": "Zakres dat obowiązywania (opcjonalnie)", + "Schedule Maintenance": "Planowanie konserwacji", + "DateTime Range": "Zakres czasowy", + "Maintenance Time Window of a Day": "Okno czasowe konserwacji na dzień", + "wayToGetZohoCliqURL": "Możesz dowiedzieć się, jak utworzyć adres URL webhook {0}.", + "dataRetentionTimeError": "Okres przechowywania musi wynosić 0 lub więcej", + "infiniteRetention": "Ustaw na 0, aby uzyskać nieskończony okres przechowywania.", + "enableGRPCTls": "Zezwalaj na wysyłanie żądania gRPC z połączeniem TLS", + "grpcMethodDescription": "Nazwa metody jest konwertowana na format camelCase, taki jak sayHello, check, itp.", + "Guild ID": "ID gildii", + "Proto Content": "Zawartość Proto", + "Proto Method": "Metoda Proto", + "Proto Service Name": "Nazwa usługi Proto", + "Economy": "Ekonomia", + "Strategy": "Strategia", + "Free Mobile User Identifier": "Darmowy mobilny identyfikator użytkownika", + "Free Mobile API Key": "Darmowy mobilny klucz API", + "Lowcost": "Tani", + "high": "wysoki", + "General Monitor Type": "Ogólny typ monitora", + "Packet Size": "Rozmiar pakietu", + "uninstalling": "Odinstalowywanie", + "loadingError": "Nie można pobrać danych, proszę spróbować ponownie później.", + "plugin": "Plugin | Pluginy", + "install": "Instaluj", + "installing": "Instalowanie", + "uninstall": "Odinstaluj", + "confirmUninstallPlugin": "Czy na pewno chcesz odinstalować tę wtyczkę?", + "Custom Monitor Type": "Własny typ monitora", + "markdownSupported": "Obsługiwana składnia Markdown. W przypadku korzystania z HTML należy unikać spacji na początku, aby zapobiec problemom z formatowaniem.", + "Google Analytics ID": "Identyfikator Google Analytics", + "Edit Tag": "Edytuj Tag", + "Server Address": "Adres Serwera", + "Learn More": "Dowiedz się więcej", + "Body Encoding": "Kodowanie treści", + "Expiry": "Wygasa", + "Expiry date": "Data wygaśnięcia", + "Don't expire": "Nie wygaszaj", + "Continue": "Kontynuuj", + "Add Another": "Dodaj kolejne", + "Add API Key": "Dodaj klucz API", + "No API Keys": "Brak kluczy API", + "apiKey-active": "Aktywny", + "apiKey-expired": "Wygasły", + "apiKey-inactive": "Nieaktywny", + "Expires": "Wygasa", + "Generate": "Generuj", + "disableAPIKeyMsg": "Czy na pewno chcesz wyłączyć ten klucz API?", + "deleteAPIKeyMsg": "Czy na pewno chcesz usunąć ten klucz API?", + "pagertreeIntegrationUrl": "URL integracji", + "pagertreeUrgency": "Pilność", + "pagertreeSilent": "Cichy", + "pagertreeLow": "Niski", + "pagertreeMedium": "Średni", + "pagertreeHigh": "Wysoki", + "pagertreeCritical": "Krytyczny", + "pagertreeResolve": "Automatyczne rozwiązywanie", + "Clone Monitor": "Klonuj monitor", + "Clone": "Klonuj", + "cloneOf": "Klon {0}", + "API Keys": "Klucze API", + "Key Added": "Klucz dodany", + "pagertreeDoNothing": "Nie rób nic", + "wayToGetPagerTreeIntegrationURL": "Po utworzeniu integracji Uptime Kuma w PagerTree, należy skopiować Endpoint. Zobacz pełne szczegóły {0}", + "notificationRegional": "Regionalne", + "twilioFromNumber": "Z numeru", + "twilioToNumber": "Do numeru", + "lunaseaTarget": "Cel", + "twilioAccountSID": "SID konta", + "twilioAuthToken": "Token autoryzacji / klucz Api Secret", + "apiKeyAddedMsg": "Twój klucz API został dodany. Prosimy o zanotowanie go, ponieważ nie będzie on już więcej pokazywany.", + "telegramMessageThreadID": "(Opcjonalne) ID wątku wiadomości", + "telegramMessageThreadIDDescription": "Opcjonalny Unikalny identyfikator dla docelowego wątku wiadomości (tematu) forum; tylko dla supergrup forum", + "telegramProtectContent": "Ochrona przekazywania/zapisywania", + "telegramProtectContentDescription": "Jeśli włączona, wiadomości bota w Telegramie będą chronione przed przekazywaniem i zapisywaniem.", + "telegramSendSilently": "Wyślij po cichu", + "telegramSendSilentlyDescription": "Wysyła wiadomość po cichu. Użytkownicy otrzymają powiadomienie bez dźwięku.", + "statusPageRefreshIn": "Odświeżenie w ciągu: {0}", + "lunaseaDeviceID": "ID urządzenia", + "lunaseaUserID": "ID użytkownika", + "Add New Tag": "Dodaj nowy tag", + "startDateTime": "Data/godzina rozpoczęcia", + "cronSchedule": "Harmonogram: ", + "invalidCronExpression": "Nieprawidłowe sformułowanie Cron: {0}", + "sameAsServerTimezone": "Tak jak strefa czasowa serwera", + "endDateTime": "Data/godzina zakończenia", + "cronExpression": "Wyrażenie Cron", + "ntfyAuthenticationMethod": "Metoda uwierzytelniania", + "ntfyUsernameAndPassword": "Nazwa użytkownika i hasło", + "noGroupMonitorMsg": "Niedostępna. Stwórz najpierw grupę monitorów.", + "Close": "Zamknij", + "pushoverMessageTtl": "TTL wiadomości (sekundy)", + "Home": "Strona główna", + "Group": "Grupa", + "Monitor Group": "Grupa monitora", + "Reconnecting...": "Ponowne łączenie...", + "Cannot connect to the socket server": "Nie można połączyć się z serwerem gniazda", + "Badge Label Suffix": "Sufiks etykiety odznaki", + "Badge Warn Color": "Kolor ostrzeżenia odznaki", + "Badge Style": "Styl odznaki", + "Badge value (For Testing only.)": "Wartość odznaki (tylko do testów.)", + "Show Clickable Link": "Pokaż klikalne łącze", + "Show Clickable Link Description": "Jeśli to pole jest zaznaczone, każdy, kto ma dostęp do tej strony stanu, może mieć dostęp do adresu URL monitora.", + "Open Badge Generator": "Generator odznak Open Badge", + "Badge Generator": "{0} Generator odznak", + "Monitor Setting": "{0} Ustawienia monitora", + "Badge Duration": "Czas trwania odznaki", + "Badge Label": "Etykieta odznaki", + "Badge Suffix": "Sufiks wartości odznaki", + "chromeExecutable": "Plik wykonywalny Chrome/Chromium", + "chromeExecutableAutoDetect": "Wykryj automatycznie", + "chromeExecutableDescription": "W przypadku użytkowników Dockera, jeśli Chromium nie jest jeszcze zainstalowany, instalacja i wyświetlenie wyniku testu może zająć kilka minut. Zajmuje 1 GB miejsca na dysku.", + "Edit Maintenance": "Edycja konserwacji", + "Badge Type": "Typ odznaki", + "Badge Prefix": "Prefiks wartości odznaki", + "Badge Color": "Kolor odznaki", + "Badge Label Color": "Kolor etykiety Odznaki", + "Badge Label Prefix": "Prefiks etykiety identyfikatora", + "Badge Pending Color": "Oczekujący kolor odznaki", + "Badge Maintenance Color": "Kolor utrzymania odznaki", + "Badge URL": "Adres URL odznaki", + "gamedigGuessPort": "Gamedig: Port Guess", + "filterActive": "Aktywny", + "webhookCustomBodyDesc": "Definiuje niestandardową treść HTTP dla żądania. Akceptowane są zmienne szablonowe {msg}, {heartbeat}, {monitor}.", + "Select": "Wybierz", + "aboutNotifyChannel": "Powiadomienie kanału wywoła powiadomienie na komputerze lub urządzeniu mobilnym dla wszystkich członków kanału, niezależnie od tego, czy ich dostępność jest ustawiona jako aktywna, czy nie.", + "twilioApiKey": "Klucz API (opcjonalnie)", + "styleElapsedTime": "Czas, który upłynął pod paskiem bicia serca", + "tailscalePingWarning": "Aby korzystać z monitora Tailscale Ping, należy zainstalować Uptime Kuma bez Dockera, a także zainstalować klienta Tailscale na serwerze.", + "invertKeywordDescription": "Słowo kluczowe powinno być raczej nieobecne niż obecne.", + "Server URL should not contain the nfty topic": "Adres URL serwera nie powinien zawierać tematu nfty", + "Badge Duration (in hours)": "Czas trwania odznaki (w godzinach)", + "Enter the list of brokers": "Wprowadź listę brokerów", + "Enable Kafka Producer Auto Topic Creation": "Włącz automatyczne tworzenie tematów w producencie Kafka", + "showCertificateExpiry": "Pokaż wygaśnięcie certyfikatu", + "gamedigGuessPortDescription": "Port używany przez Valve Server Query Protocol może różnić się od portu klienta. Spróbuj tego, jeśli monitor nie może połączyć się z serwerem.", + "Secret AccessKey": "Tajny klucz AccessKey", + "wayToGetFlashDutyKey": "Aby zintegrować Uptime Kuma z Flashduty: Przejdź do Kanały > Wybierz kanał > Integracje > Dodaj nową integrację, wybierz Uptime Kuma i skopiuj adres URL Push.", + "Badge Down Color": "Kolor odznaki Offline", + "Notify Channel": "Powiadom kanał", + "Request Timeout": "Limit czasu żądania", + "timeoutAfter": "Przekroczenie limitu czasu po {0} sekundach", + "styleElapsedTimeShowNoLine": "Pokaż (bez linii)", + "styleElapsedTimeShowWithLine": "Pokaż (z linią)", + "filterActivePaused": "Wstrzymany", + "webhookBodyCustomOption": "Niestandardowa treść", + "webhookBodyPresetOption": "Wstępne ustawienie - {0}", + "selectedMonitorCount": "Wybrano: {0}", + "Check/Uncheck": "Zaznacz/Odznacz", + "PushDeer Server": "Serwer PushDeer", + "pushDeerServerDescription": "Pozostaw puste, aby korzystać z oficjalnego serwera", + "Badge Preview": "Podgląd odznaki", + "Kafka Brokers": "Broker Kafka", + "Press Enter to add broker": "Naciśnij Enter, aby dodać brokera", + "Kafka Topic Name": "Nazwa tematu Kafka", + "Kafka Producer Message": "Wiadomość producenta Kafka", + "Enable Kafka SSL": "Włącz Kafka SSL", + "Kafka SASL Options": "Opcje SASL Kafki", + "Mechanism": "Mechanizm", + "Pick a SASL Mechanism...": "Wybierz mechanizm SASL…", + "nostrRelaysHelp": "Jeden adres URL przekaźnika w wierszu", + "nostrRecipients": "Klucze publiczne odbiorców (npub)", + "nostrRecipientsHelp": "format npub, po jednym w wierszu", + "Authorization Identity": "Identyfikacja autoryzacji", + "AccessKey Id": "Identyfikator klucza AccessKey", + "Session Token": "Token sesji", + "Request Body": "Treść żądania", + "FlashDuty Severity": "Poziom istotności", + "nostrRelays": "Przekaźniki Nostr", + "nostrSender": "Prywatny klucz nadawcy (nsec)", + "Badge Up Color": "Kolor odznaki Online", + "Badge Down Days": "Odznaka dni offline", + "Badge Warn Days": "Odznaka dni z ostrzeżeniem", + "noOrBadCertificate": "Brak/zły certyfikat", + "Invert Keyword": "Odwróć słowo kluczowe", + "Expected Value": "Oczekiwana wartość", + "Json Query": "Zapytanie Json", + "enableNSCD": "Włącz NSCD (Name Service Cache Daemon) do buforowania wszystkich żądań DNS", + "Saved.": "Zapisano.", + "setupDatabaseChooseDatabase": "Której bazy danych chcesz używać?", + "setupDatabaseEmbeddedMariaDB": "Nie musisz niczego ustawiać. Ten obraz Docker automatycznie osadził i skonfigurował MariaDB. Uptime Kuma połączy się z tą bazą danych za pośrednictwem gniazda Unix.", + "setupDatabaseMariaDB": "Połącz z zewnętrzną bazą danych MariaDB. Należy ustawić informacje o połączeniu z bazą danych.", + "setupDatabaseSQLite": "Prosty plik bazy danych, zalecany do wdrożeń na małą skalę. Przed wersją 2.0.0 Uptime Kuma używała SQLite jako domyślnej bazy danych.", + "dbName": "Nazwa bazy danych", + "toastErrorTimeout": "Limit czasu dla powiadomień o błędach", + "monitorToastMessagesLabel": "Powiadomienie Toast Monitora", + "monitorToastMessagesDescription": "Powiadomienia toast dla monitorów znikają po określonym czasie w sekundach. Ustawienie -1 wyłącza limit czasu. Ustawienie 0 wyłącza powiadomienia toast.", + "authInvalidToken": "Nieprawidłowy token.", + "authIncorrectCreds": "Nieprawidłowa nazwa użytkownika lub hasło.", + "2faAlreadyEnabled": "2FA jest już włączone.", + "2faEnabled": "2FA włączone.", + "2faDisabled": "2FA wyłączone.", + "successAdded": "Pomyślnie dodano.", + "successPaused": "Wstrzymano pomyślnie.", + "successDeleted": "Usunięto pomyślnie.", + "successEdited": "Edytowano pomyślnie.", + "successAuthChangePassword": "Hasło zostało pomyślnie zaktualizowane.", + "successDisabled": "Wyłączono pomyślnie.", + "successEnabled": "Włączono pomyślnie.", + "tagNotFound": "Nie znaleziono tagu.", + "foundChromiumVersion": "Znaleziono Chromium/Chrome. Wersja: {0}", + "authUserInactiveOrDeleted": "Użytkownik jest nieaktywny lub usunięty.", + "successResumed": "Wznowiono pomyślnie.", + "successBackupRestored": "Kopia zapasowa została pomyślnie przywrócona.", + "pushViewCode": "Jak korzystać z monitora Push? (wyświetl kod)", + "Bark API Version": "Wersja API Bark", + "pushOthers": "Inne", + "programmingLanguages": "Języki programowania", + "toastSuccessTimeout": "Limit czasu dla powiadomień o powodzeniu", + "Reset Token": "Zresetuj token", + "liquidIntroduction": "Szablony są tworzone za pomocą języka szablonów Liquid. Instrukcje dotyczące użytkowania można znaleźć w {0}.", + "templateLimitedToUpDownCertNotifications": "dostępne tylko dla powiadomień UP/DOWN/wygaśnięcia certyfikatu", + "emailTemplateLimitedToUpDownNotification": "dostępne tylko dla uderzeń serca UP/DOWN, w przeciwnym razie null", + "emailCustomisableContent": "Personalizowana zawartość", + "smtpLiquidIntroduction": "Poniższe dwa pola można szablonować za pomocą języka szablonów Liquid. Instrukcje użytkowania znajdują się w {0}. Są to dostępne zmienne:", + "leave blank for default subject": "pozostaw puste dla domyślnego tematu", + "emailCustomBody": "Niestandardowa treść wiadomości e-mail", + "leave blank for default body": "pozostaw puste dla domyślnej treści", + "emailTemplateServiceName": "Nazwa usługi", + "emailTemplateHostnameOrURL": "Nazwa hosta lub adres URL", + "emailTemplateStatus": "Status", + "emailTemplateMonitorJSON": "obiekt opisujący monitor", + "emailTemplateHeartbeatJSON": "obiekt opisujący bicie serca", + "emailTemplateMsg": "wiadomość powiadomienia", + "templateMsg": "wiadomość powiadomienia", + "templateHeartbeatJSON": "obiekt opisujący bicie serca", + "templateMonitorJSON": "obiekt opisujący monitor", + "templateLimitedToUpDownNotifications": "dostępne tylko dla powiadomień UP/DOWN", + "Browser Screenshot": "Zrzut ekranu przeglądarki", + "noDockerHostMsg": "Niedostępne. Najpierw skonfiguruj host Dockera.", + "DockerHostRequired": "Ustaw host Dockera dla tego monitora.", + "successKeyword": "Słowo kluczowe sukcesu", + "successKeywordExplanation": "Słowo kluczowe MQTT, które zostanie uznane za sukces", + "ntfyPriorityHelptextAllExceptDown": "Wszystkie zdarzenia są wysyłane z tym priorytetem, z wyjątkiem zdarzeń {0}, które mają priorytet {1}", + "remoteBrowsersDescription": "Przeglądarki zdalne są alternatywą dla lokalnego uruchamiania Chromium. Skonfiguruj za pomocą usługi takiej jak browserless.io lub połącz z własną", + "deleteRemoteBrowserMessage": "Czy na pewno chcesz usunąć tę zdalną przeglądarkę dla wszystkich monitorów?", + "GrafanaOncallUrl": "Adres URL Grafana Oncall", + "Remote Browsers": "Zdalne przeglądarki", + "self-hosted container": "kontener hostowany samodzielnie", + "settingUpDatabaseMSG": "Konfigurowanie bazy danych. Może to zająć chwilę, bądź cierpliwy.", + "statusPageSpecialSlugDesc": "Specjalny slug {0}: ta strona zostanie wyświetlona, jeśli nie podano żadnego slugu", + "Search monitored sites": "Wyszukiwanie monitorowanych witryn", + "setup a new monitor group": "konfiguracja nowej grupy monitorów", + "openModalTo": "otwórz modal na {0}", + "Add a domain": "Dodaj domenę", + "Remove domain": "Usuń domenę '{0}'", + "ntfyPriorityHelptextAllEvents": "Wszystkie zdarzenia są wysyłane z maksymalnym priorytetem", + "Remote Browser": "Zdalna przeglądarka", + "Add a Remote Browser": "Dodaj zdalną przeglądarkę", + "Remote Browser not found!": "Nie znaleziono zdalnej przeglądarki!", + "remoteBrowserToggle": "Domyślnie Chromium działa wewnątrz kontenera Uptime Kuma. Możesz użyć zdalnej przeglądarki, przełączając ten przełącznik.", + "useRemoteBrowser": "Używaj zdalnej przeglądarki", + "Add a new expiry notification day": "Dodaj nowy dzień wygaśnięcia powiadomienia", + "Remove the expiry notification": "Usunąć dzień powiadomienia o wygaśnięciu", + "What is a Remote Browser?": "Co to jest zdalna przeglądarka?", + "Mentioning": "Oznaczenia", + "Don't mention people": "Nie oznaczaj nikogo", + "Mention group": "Oznacz {group}", + "Channel access token (Long-lived)": "Token dostępu do kanału (długotrwały)", + "Your User ID": "Twój identyfikator użytkownika", + "locally configured mail transfer agent": "lokalnie skonfigurowany serwer poczty", + "documentationOf": "{0} Dokumentacja", + "wayToGetHeiiOnCallDetails": "Jak uzyskać identyfikator wyzwalacza i klucze API wyjaśniono w {dokumentacji}", + "Select message type": "Wybierz typ wiadomości", + "Create new forum post": "Utwórz nowy post na forum", + "whatHappensAtForumPost": "Utwórz nowy post na forum. NIE powoduje to opublikowania wiadomości w istniejącym poście. Aby opublikować wiadomość w istniejącym poście, użyj \"{option}\"", + "callMeBotGet": "Tutaj możesz wygenerować punkt końcowy dla {0}, {1} i {2}. Należy pamiętać, że mogą wystąpić ograniczenia szybkości. Limity szybkości wydają się być następujące: {3}", + "Command": "Polecenie", + "mongodbCommandDescription": "Uruchom polecenie MongoDB względem bazy danych. Aby uzyskać informacje na temat dostępnych poleceń, sprawdź {documentation}", + "max 11 alphanumeric characters": "maksymalnie 11 znaków alfanumerycznych", + "Originator type": "Typ nadawcy", + "Alphanumeric (recommended)": "Alfanumeryczne (zalecane)", + "smspartnerApiurl": "Klucz API można znaleźć na pulpicie nawigacyjnym pod adresem {0}", + "smspartnerPhoneNumber": "Numer(y) telefonu(ów)", + "smspartnerPhoneNumberHelptext": "Numer musi być w międzynarodowym formacie {0}, {1}. Kilka numerów musi być oddzielonych znakiem {2}", + "smspartnerSenderName": "Nazwa nadawcy wiadomości SMS", + "smspartnerSenderNameInfo": "Musi zawierać między 3..=11 zwykłych znaków", + "Telephone number": "Nr telefonu", + "To Phone Number": "Na numer telefonu", + "Destination": "Miejsce docelowe", + "Bitrix24 Webhook URL": "Adres URL usługi Bitrix24 Webhook", + "wayToGetBitrix24Webhook": "Webhook można utworzyć, wykonując czynności opisane na stronie {0}", + "gtxMessagingToHint": "Format międzynarodowy, z początkowym \"+\" ({e164}, {e212} lub {e214})", + "wayToGetSevenIOApiKey": "Odwiedź pulpit nawigacyjny pod adresem app.seven.io > deweloper > klucz api > zielony przycisk dodawania", + "cellsyntOriginator": "Widoczny na telefonie komórkowym odbiorcy jako inicjator wiadomości. Dozwolone wartości i funkcja zależą od parametru originatortype.", + "cellsyntSplitLongMessages": "Dzielenie długich wiadomości na maksymalnie 6 części. 153 x 6 = 918 znaków.", + "max 15 digits": "maksymalnie 15 cyfr", + "bitrix24SupportUserID": "Wprowadź swój identyfikator użytkownika w Bitrix24. Identyfikator można znaleźć w linku, przechodząc do profilu użytkownika.", + "gtxMessagingApiKeyHint": "Klucz API można znaleźć na stronie: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "cellsyntOriginatortypeAlphanumeric": "Ciąg alfanumeryczny (maksymalnie 11 znaków alfanumerycznych). Odbiorcy nie mogą odpowiedzieć na wiadomość.", + "Send to channel": "Wyślij do kanału", + "postToExistingThread": "Opublikuj w istniejącym wątku / poście na forum", + "forumPostName": "Nazwa posta na forum", + "threadForumPostID": "Identyfikator wątku/postu na forum", + "e.g. {discordThreadID}": "np. {discordThreadID}", + "wayToGetDiscordThreadId": "Uzyskiwanie identyfikatora wątku / postu na forum jest podobne do uzyskiwania identyfikatora kanału. Przeczytaj więcej o tym, jak uzyskać identyfikatory {0}", + "cellsyntDestination": "Numer telefonu odbiorcy w formacie międzynarodowym z początkowym 00, po którym następuje numer kierunkowy kraju, np. 00447920110000 dla numeru w Wielkiej Brytanii 07920 110 000 (łącznie maksymalnie 17 cyfr). Maksymalnie 25000 odbiorców oddzielonych przecinkami na żądanie HTTP.", + "Allow Long SMS": "Zezwalaj na długie wiadomości SMS", + "Host URL": "Adres URL hosta", + "Refresh Interval": "Częstotliwość odświeżania", + "Refresh Interval Description": "Strona stanu będzie odświeżać całą witrynę co {0} sekund", + "ignoreTLSErrorGeneral": "Ignorowanie błędu TLS/SSL dla połączenia", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Wprowadź nazwę hosta serwera, z którym chcesz się połączyć lub {localhost}, jeśli zamierzasz użyć {local_mta}", + "senderSevenIO": "Wysyłany numer lub nazwa", + "receiverSevenIO": "Numer odbiorcy", + "receiverInfoSevenIO": "Jeśli numer odbiorczy nie znajduje się w Niemczech, należy dodać kod kraju przed numerem (np. dla kodu kraju 1 z USA należy użyć 117612121212 zamiast 017612121212)", + "apiKeySevenIO": "Klucz API SevenIO", + "wayToWriteWhapiRecipient": "Numer telefonu z prefiksem międzynarodowym, ale bez znaku plus na początku ({0}), identyfikator kontaktu ({1}) lub identyfikator grupy ({2}).", + "wayToGetWhapiUrlAndToken": "Adres URL API i token można uzyskać, przechodząc do żądanego kanału z {0}", + "Originator": "Nadawca", + "whapiRecipient": "Nr telefonu / ID kontaktu / ID grupy", + "API URL": "URL dla API", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Od numeru telefonu / adresu początkowego ścieżki transmisji (TPOA)", + "gtxMessagingFromHint": "W telefonach komórkowych odbiorcy widzą TPOA jako nadawcę wiadomości. Dozwolone jest do 11 znaków alfanumerycznych, krótki kod, lokalny długi kod lub numery międzynarodowe ({e164}, {e212} lub {e214})", + "cellsyntOriginatortypeNumeric": "Wartość numeryczna (maks. 15 cyfr) z numerem telefonu w formacie międzynarodowym bez początkowego 00 (np. brytyjski numer 07920 110 000 powinien być ustawiony jako 447920110000). Odbiorcy mogą odpowiedzieć na wiadomość.", + "threemaRecipient": "Odbiorca", + "threemaRecipientType": "Typ odbiorcy", + "threemaRecipientTypeIdentity": "Threema ID", + "threemaRecipientTypeIdentityFormat": "8 znaków", + "threemaRecipientTypePhone": "Numer telefonu", + "threemaRecipientTypePhoneFormat": "E.164, bez znaku + na początku", + "threemaRecipientTypeEmail": "Adres e-mail", + "threemaSenderIdentity": "Gateway ID", + "threemaApiAuthenticationSecret": "Hasło Gateway ID", + "wayToGetThreemaGateway": "Możesz zarejestrować się w Threema Gateway {0}.", + "threemaSenderIdentityFormat": "8 znaków, zwykle zaczyna się od *", + "threemaBasicModeInfo": "Uwaga: Ta integracja korzysta z Threema Gateway w trybie podstawowym (szyfrowanie po stronie serwera). Więcej szczegółów można znaleźć {0}.", + "apiKeysDisabledMsg": "Klucze API są wyłączone, ponieważ wyłączone jest uwierzytelnianie.", + "-year": "-rok", + "and": "i", + "now": "teraz", + "cacheBusterParam": "Dodaj parametr {0}", + "CopyToClipboardError": "Błąd podczas kopiowania do schowka: {error}", + "CurlDebugInfo": "Aby zdebugować monitor, możesz wkleić ten ciąg do terminala na swojej maszynie lub na maszynie gdzie uptime kuma jest uruchomina aby zobaczyć co żądasz.{newiline} Miej na uwadzę różnice sieciowe takich jak {firewalls}, {dns_resolvers} lub {docker_networks}.", + "shrinkDatabaseDescriptionSqlite": "Zażądaj {vacuum} bazy dla SQLite. {auto_vacuum} jest już włączone jednak nie defragmentuje to bazy ani nie przepakowuje indywidualnych stron bazy w taki sam sposób jak robi to polecenie {vacuum}.", + "cacheBusterParamDescription": "Losowo wygenerowany parametr w celu pominięcia pamięci podręcznej.", + "Group Name": "Nazwa grupy", + "OAuth2: Client Credentials": "OAuth2: Poświadczenia klienta", + "signl4Docs": "Aby zdobyć więcej informacji jak skonfigurować SIGNL4 i jak zdobyć odnośnik webhooka SIGNL4 udaj się do {0}.", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Czasowo wrażliwe powiadomienia zostaną dostarczone natychmiastowo, nawet jeśli urzadzenie jest w trybie nie przeszkadzać.", + "time ago": "{0} temu", + "Json Query Expression": "Wyrażenie zapytania JSON", + "Condition": "Warunek", + "snmpCommunityStringHelptext": "Ten ciąg funkcjonuje jako hasło do uwierzytelnienia i kontroli dostęp do urządzeń SNMP. Dopasuj go do konfiguracji urządzenia SNMP.", + "SNMP Version": "Wersja SNMP", + "OID (Object Identifier)": "OID (Identyfikator obiektu)", + "snmpOIDHelptext": "Wprowadź OID sensora lub statusu który chcesz monitorować. Użyj narzędzia do zarządzania siecią jak przeglądarki MIB lub oprogramowanie SNMP jeśli nie masz pewności co do OID.", + "Please enter a valid OID.": "Wprowadź poprawny OID.", + "Host Onesender": "Serwer Onesender", + "Token Onesender": "Token Onesender", + "Recipient Type": "Typ odbiorcy", + "Go back to home page.": "Powróć do strony domowej.", + "No tags found.": "Nie znaleziono etykiet.", + "Authorization Header": "Nagłówek autoryzacji", + "Form Data Body": "Treść danych formularza", + "OAuth Token URL": "Odnośnik tokena OAuth", + "Client ID": "Identyfikator klienta", + "Client Secret": "Sekret klienta", + "OAuth Scope": "Zakres OAuth", + "Optional: Space separated list of scopes": "Opcjonalne: Oddzielona spacją lista zakresów", + "SIGNL4 Webhook URL": "Odnośnik webhooka SIGNL4", + "Lost connection to the socket server.": "Utracono połączenie z serwerem gniazd.", + "Cannot connect to the socket server.": "Nie można połączyć się z serwerem gniazd.", + "SIGNL4": "SIGNL4", + "less than": "mniej niż", + "Conditions": "Warunek", + "conditionAdd": "Dodaj warunek", + "conditionDelete": "Usuń warunek", + "conditionAddGroup": "Dodaj grupę", + "conditionDeleteGroup": "Usuń grupę", + "conditionValuePlaceholder": "Wartość", + "equals": "równa się", + "not equals": "nie jest równe", + "contains": "zawiera", + "not contains": "nie zawiera", + "starts with": "zaczyna się od", + "not starts with": "nie zaczyna się od", + "ends with": "kończy się", + "ignoredTLSError": "Błędy TLS/SSL zostały zignorowane", + "Debug": "Debuguj", + "Copy": "Kopiuj", + "CopyToClipboardSuccess": "Skopiowano!", + "firewalls": "zapory sieciowe", + "dns resolvers": "serwery rozwiązywania nazw domenowych", + "docker networks": "sieci docker", + "CurlDebugInfoOAuth2CCUnsupported": "Pełen ciąg poświadczeń klienta oauth nie jest obsługiwany w {curl}.{newline}Zdobądź bearer token i przekaż go przez opcję {oauth2_bearer}.", + "CurlDebugInfoProxiesUnsupported": "Obsługa proxy w powyższym poleceniu {curl} nie jest aktualnie zaimplementowana.", + "Message format": "Format wiadomości", + "Send rich messages": "Wysyłaj rozbudowane wiadomości", + "Community String": "Ciąg community", + "Private Number": "Numer prywatny", + "groupOnesenderDesc": "Upewnij się, że GroupID jest poprawne. Aby wysłać wiadomość do grupy, np.: 628123456789-342345", + "privateOnesenderDesc": "Upewnij się, że numer telefonu jest poprawny. Aby wysłać wiadomość do prywatnego numeru, np.: 628123456789", + "Group ID": "ID grupy", + "wayToGetOnesenderUrlandToken": "Aby zdobyć odnośnik i token udaj się na stronę Onesender. Więcej informacji {0}", + "Add Remote Browser": "Dodaj zdalną przeglądarkę", + "New Group": "Nowa grupa", + "Authentication Method": "Metoda uwierzytelnienia", + "not ends with": "nie kończy się", + "greater than": "więcej niż", + "less than or equal to": "mniej lub równe", + "greater than or equal to": "więcej lub równe", + "record": "rekord", + "Notification Channel": "Kanał powiadomień", + "Sound": "Dźwięk", + "Alphanumerical string and hyphens only": "Tylko ciąg alfanumeryczny i dywiz (kreska)", + "Time Sensitive (iOS Only)": "Czasowo wrażliwe (tylko iOS)", + "From": "Od", + "Can be found on:": "Może być znalezione w: {0}", + "The phone number of the recipient in E.164 format.": "Numer telefonu odbiorcy w formacie E.164.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Identyfikator wysyłającego wiadomość lub numer telefonu w formacie E.164 jeśli chcesz otrzymywać odpowiedzi.", + "jsonQueryDescription": "Przetwórz i wyciągnij specyficzne dane z odpowiedzi JSON serwera używając zapytania JSON lub użyj \"$\" dla nieprzetworzonej odpowiedzi, jeśli nie spodziewasz się formatu JSON. Wynik jest następnie porównywany, jako ciągi, do spodziewanej wartości. Po dokumetnację zobacz {0} lub użyj {1} aby poeksperymentować z zapytaniami.", + "Custom sound to override default notification sound": "Własny dzwięk nadpisujący domyślny dzwięk powiadomień", + "Arcade": "Arcade", + "Correct": "Poprawne", + "Fail": "Niepowodzenie", + "Harp": "Harfa", + "Reveal": "Odkryj", + "Bubble": "Bubble", + "Doorbell": "Dzwonek do drzwi", + "Flute": "Flet", + "Money": "Pieniądze", + "Scifi": "Scifi", + "Clear": "Wyczyść", + "Elevator": "Winda", + "Guitar": "Gitara", + "Pop": "Pop", + "RabbitMQ Nodes": "Węzły zarządzania RabbitMQ", + "rabbitmqNodesDescription": "Wprowadź adres URL węzłów zarządzania RabbitMQ, w tym protokół i port. Przykład: {0}", + "aboutSlackUsername": "Zmienia wyświetlaną nazwę nadawcy wiadomości. Jeśli chcesz o kimś wspomnieć, umieść go w przyjaznej nazwie.", + "rabbitmqNodesRequired": "Proszę ustawić węzły dla tego monitora.", + "rabbitmqNodesInvalid": "W przypadku węzłów RabbitMQ należy używać w pełni kwalifikowanych (zaczynających się od „http”) adresów URL.", + "rabbitmqHelpText": "Aby korzystać z monitora, należy włączyć wtyczkę Management Plugin w konfiguracji RabbitMQ. Więcej informacji można znaleźć w {rabitmq_documentation}.", + "RabbitMQ Username": "Nazwa użytkownika RabbitMQ", + "RabbitMQ Password": "Hasło RabbitMQ", + "SendGrid API Key": "Klucz API SendGrid", + "Separate multiple email addresses with commas": "Oddziel wiele adresów e-mail przecinkami", + "templateServiceName": "service name", + "telegramServerUrlDescription": "Aby znieść ograniczenia api bota Telegrama lub uzyskać dostęp w zablokowanych obszarach (Chiny, Iran itp.). Aby uzyskać więcej informacji, kliknij {0}. Domyślnie: {1}", + "wayToGetWahaSession": "Z tej sesji WAHA wysyła powiadomienia do Chat ID. Można go znaleźć w WAHA Dashboard.", + "wayToWriteWahaChatId": "Numer telefonu z prefiksem międzynarodowym, ale bez znaku plus na początku ({0}), identyfikator kontaktu ({1}) lub identyfikator grupy ({2}). Powiadomienia są wysyłane do tego identyfikatora czatu z sesji WAHA.", + "wahaSession": "Sesja", + "wahaChatId": "Identyfikator czatu (numer telefonu / identyfikator kontaktu / identyfikator grupy)", + "wayToGetWahaApiUrl": "Adres URL instancji WAHA.", + "wayToGetWahaApiKey": "Klucz API to wartość zmiennej środowiskowej WHATSAPP_API_KEY użytej do uruchomienia WAHA.", + "YZJ Robot Token": "Token robota YZJ", + "YZJ Webhook URL": "Adres URL usługi YZJ Webhook", + "telegramServerUrl": "(Opcjonalnie) Adres URL serwera", + "Plain Text": "Zwykły tekst", + "Message Template": "Szablon wiadomości", + "Template Format": "Format szablonu", + "templateHostnameOrURL": "nazwa hosta lub adres URL", + "templateStatus": "status", + "telegramUseTemplate": "Użyj niestandardowego szablonu wiadomości", + "telegramUseTemplateDescription": "Jeśli opcja ta jest włączona, wiadomość zostanie wysłana przy użyciu niestandardowego szablonu.", + "telegramTemplateFormatDescription": "Telegram pozwala na używanie różnych języków znaczników dla wiadomości, zobacz Telegram {0}, aby uzyskać szczegółowe informacje.", + "Font Twemoji by Twitter licensed under": "Czcionka Twemoji autorstwa Twitter na licencji", + "smsplanetApiToken": "Token dla API SMSPlanet", + "smsplanetApiDocs": "Szczegółowe informacje na temat uzyskiwania tokenów API można znaleźć w {the_smsplanet_documentation}.", + "the smsplanet documentation": "dokumentacja smsplanet", + "Phone numbers": "Numery telefonów", + "Sender name": "Nazwa nadawcy", + "smsplanetNeedToApproveName": "Wymaga zatwierdzenia w panelu klienta", + "Staged Tags for Batch Add": "Tagi etapowe dla dodawania zbiorczego", + "Happy Eyeballs algorithm": "Algorytm Happy Eyeballs", + "smseagleApiv1": "APIv1 (dla istniejących projektów i kompatybilności wstecznej)", + "ntfyPriorityDown": "Priorytet dla zdarzeń DOWN", + "mqttWebsocketPathExplanation": "Ścieżka WebSocket dla połączeń MQTT przez WebSocket (np. /mqtt)", + "supportBaleChatID": "Pomoc techniczna Czat bezpośredni / Grupa / Identyfikator czatu kanału", + "wayToGetBaleChatID": "Możesz uzyskać swój identyfikator czatu, wysyłając wiadomość do bota i przechodząc do tego adresu URL, aby wyświetlić identyfikator czatu:", + "OAuth Audience": "Odbiorcy OAuth", + "Ip Family": "Rodzina IP", + "Conversation token": "Token konwersacji", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Instalacja bota Nextcloud Talk wymaga uprawnień administratora serwera.", + "Manual": "Ręczny", + "clearAllEventsMsg": "Czy na pewno chcesz usunąć wszystkie wydarzenia dla wszystkich monitorów? Ta akcja permanentnie usunie wszystkie powiązane dane wiadomości i nie może zostać cofnięta.", + "Clear All Events": "Wyczyść wszystkie wydarzenia", + "smseagleMsgType": "Typ wiadomości", + "smseagleMsgSms": "Wiadomość SMS (domyślnie)", + "smseagleMsgRing": "Dzwonek", + "smseagleMsgTts": "Połączenie z funkcją zamiany tekstu na mowę", + "smseagleMsgTtsAdvanced": "Zaawansowana funkcja zamiany tekstu na mowę", + "smseagleTtsModel": "Identyfikator modelu zamiany tekstu na mowę", + "smseagleApiType": "Wersja API", + "smseagleApiv2": "APIv2 (zalecane dla nowych integracji)", + "smseagleDocs": "Sprawdź dokumentację lub dostępność APIv2: {0}", + "smseagleComma": "Wiele wartości należy oddzielić przecinkami", + "pingCountLabel": "Maksymalna liczba pakietów", + "pingGlobalTimeoutDescription": "Całkowity czas w sekundach przed zatrzymaniem pingowania, niezależnie od wysłanych pakietów", + "pingPerRequestTimeoutLabel": "Limit czasu na ping", + "pingPerRequestTimeoutDescription": "Jest to maksymalny czas oczekiwania (w sekundach) przed uznaniem pojedynczego pakietu ping za utracony", + "Path": "Ścieżka", + "mqttWebSocketPath": "Ścieżka MQTT WebSocket", + "mqttWebsocketPathInvalid": "Użyj prawidłowego formatu ścieżki WebSocket", + "mqttHostnameTip": "Użyj tego formatu {hostnameFormat}", + "brevoApiKey": "Klucz API Brevo", + "brevoApiHelp": "Utwórz klucz API tutaj: {0}", + "brevoToEmail": "Na adres e-mail", + "Clear Form": "Wyczyść formularz", + "wayToGetBaleToken": "Możesz otrzymać token z {0}.", + "Use HTML for custom E-mail body": "Użyj HTML do tworzenia niestandardowej treści wiadomości e-mail", + "Events cleared successfully": "Wydarzenia zostały pomyślnie usunięte.", + "No monitors found": "Nie znaleziono monitorów.", + "Could not clear events": "Nie można usunąć {failed}/{total} zdarzeń", + "Mention Mobile List": "Lista urządzeń mobilnych Mention", + "Mention User List": "Lista identyfikatorów użytkowników Mention", + "Dingtalk Mobile List": "Lista urządzeń mobilnych", + "Dingtalk User List": "Lista identyfikatorów użytkowników", + "Enter a list of userId": "Wprowadź listę identyfikatorów użytkowników", + "Enter a list of mobile": "Wprowadź listę numerów telefonów komórkowych", + "Invalid mobile": "Nieprawidłowy numer telefonu komórkowego [{mobile}]", + "Invalid userId": "Nieprawidłowy identyfikator użytkownika [{userId}]", + "smseagleContactV2": "Identyfikatory kontaktów w książce telefonicznej", + "smseagleDuration": "Czas trwania (w sekundach)", + "SpugPush Template Code": "Kod szablonu", + "ntfyPriorityHelptextPriorityHigherThanDown": "Priorytet regularny powinien być wyższy niż priorytet {0}. Priorytet {1} jest wyższy niż priorytet {0} i priorytet {2}", + "FlashDuty Push URL": "Adres URL Push", + "FlashDuty Push URL Placeholder": "Kopiuj ze strony integracji alertów", + "Optional: The audience to request the JWT for": "Opcjonalnie: odbiorca żądający JWT dla", + "brevoFromEmail": "Z e-maila", + "brevoFromName": "Od nazwy", + "brevoLeaveBlankForDefaultName": "pozostaw puste pole, aby użyć nazwy domyślnej", + "brevoCcEmail": "E-mail CC", + "brevoBccEmail": "E-mail BCC", + "brevoSeparateMultipleEmails": "Oddziel wiele adresów e-mail przecinkami", + "brevoSubject": "Temat", + "brevoLeaveBlankForDefaultSubject": "pozostaw puste pole dla domyślnego tematu", + "pingCountDescription": "Liczba pakietów do wysłania przed zatrzymaniem", + "pingNumericLabel": "Wyjście numeryczne", + "pingNumericDescription": "Jeśli opcja jest zaznaczona, zamiast symbolicznych nazw hostów będą wyświetlane adresy IP", + "pingGlobalTimeoutLabel": "Globalny limit czasu", + "pingIntervalAdjustedInfo": "Interwał dostosowany na podstawie liczby pakietów, globalnego limitu czasu i limitu czasu na ping", + "smtpHelpText": "„SMTPS” sprawdza, czy SMTP/TLS działa; „Ignore TLS” łączy się za pomocą zwykłego tekstu; „STARTTLS” łączy się, wysyła polecenie STARTTLS i weryfikuje certyfikat serwera. Żadna z tych opcji nie wysyła wiadomości e-mail.", + "Custom URL": "Niestandardowy adres URL", + "customUrlDescription": "Będzie używany jako klikalny adres URL zamiast adresu monitora.", + "OneChatAccessToken": "Token dostępu OneChat", + "OneChatUserIdOrGroupId": "Identyfikator użytkownika OneChat lub identyfikator grupy", + "OneChatBotId": "Identyfikator bota OneChat", + "Disable URL in Notification": "Wyłącz adres URL w powiadomieniu", + "Add Another Tag": "Dodaj kolejny tag", + "pause": "Pauza", + "Nextcloud host": "Host Nextcloud", + "Bot secret": "Sekret bota", + "Send UP silently": "Wyślij UP po cichu", + "Send DOWN silently": "Wyślij DOWN po cichu", + "wayToWriteEvolutionRecipient": "Numer telefonu z prefiksem międzynarodowym, ale bez znaku plusa na początku ({0}), identyfikator kontaktu ({1}) lub identyfikator grupy ({2}).", + "wayToGetEvolutionUrlAndToken": "Adres URL interfejsu API i token można uzyskać, przechodząc do wybranego kanału z {0}", + "evolutionRecipient": "Numer telefonu / Identyfikator kontaktu / Identyfikator grupy", + "evolutionInstanceName": "Nazwa instancji", + "Template plain text instead of using cards": "Szablon zwykłego tekstu zamiast używania kart", + "defaultFriendlyName": "Nowy monitor", + "smseagleGroupV2": "Identyfikatory grup w książce telefonicznej", + "Add Tags": "Dodaj tagi", + "tagAlreadyOnMonitor": "Ten tag (nazwa i wartość) jest już dodany do monitora lub oczekuje na dodanie.", + "tagAlreadyStaged": "Ten tag (nazwa i wartość) jest już przygotowany dla tej partii.", + "tagNameExists": "Tag systemowy o tej nazwie już istnieje. Wybierz go z listy lub użyj innej nazwy.", + "auto-select": "Wybór automatyczny", + "issueWithGoogleChatOnAndroidHelptext": "Pozwala to również ominąć błędy upstream, takie jak {issuetackerURL}", + "ipFamilyDescriptionAutoSelect": "Używa {happyEyeballs} do określania rodziny adresów IP.", + "Ignore Sec-WebSocket-Accept header": "Ignoruj nagłówek {0}", + "Web Process Control Protocol": "Web Process Control Protocol (WPCP)", + "Advanced Message Queuing Protocol": "Advanced Message Queuing Protocol (AMQP) 1.0+", + "WebSocket Application Messaging Protocol": "WAMP (The WebSocket Application Messaging Protocol)", + "Reverse Web Process Control": "Reverse Web Process Control Protocol (RWPCP)", + "Show this Maintenance Message on which Status Pages": "Pokaż tę wiadomość dotyczącą konserwacji na stronach statusu", + "GlobalpingLocationDocs": "Pełna dokumentacja wprowadzania lokalizacji", + "GlobalpingIpFamilyInfo": "Wersja IP, której należy użyć. Dozwolona tylko wtedy, gdy celem jest nazwa hosta.", + "GlobalpingResolverInfo": "Adres IPv4/IPv6 lub w pełni kwalifikowana nazwa domeny (FQDN). Domyślnie jest to lokalny serwer rozpoznawania nazw sieciowych sondy. Serwer rozpoznawania nazw sieciowych można zmienić w dowolnym momencie.", + "Protocol": "Protokół", + "account settings": "ustawienia konta", + "discordMessageFormatNormal": "Normalny (bogate osadzenia)", + "discordMessageFormatMinimalist": "Minimalistyczny (krótki status)", + "discordMessageFormatCustom": "Szablon niestandardowy", + "discordUseMessageTemplate": "Użyj niestandardowego szablonu wiadomości", + "discordMessageTemplate": "Szablon wiadomości", + "Location": "Lokalizacja", + "Monitor Subtype": "Podtyp monitora", + "Check for": "Sprawdź, czy", + "Number of retry attempts if webhook fails": "Liczba prób ponownych (co 60–180 sekund) w przypadku niepowodzenia webhooka.", + "Maximum Retries": "Maksymalna liczba ponownych prób", + "wayToGetClickSMSIRTemplateID": "Twój szablon musi zawierać pole {uptkumaalert}. Nowy szablon można utworzyć {here}.", + "Notifications Enabled": "Powiadomienia włączone", + "Allow Notifications": "Zezwól na powiadomienia", + "Browser not supported": "Przeglądarka nie jest obsługiwana", + "Unable to get permission to notify": "Nie można uzyskać pozwolenia na powiadomienie (żądanie zostało odrzucone lub zignorowane).", + "Webpush Helptext": "Powiadomienia push działają tylko w przypadku połączeń SSL (HTTPS). W przypadku urządzeń z systemem iOS strona internetowa musi zostać wcześniej dodana do ekranu głównego.", + "labelDomainNameExpiryNotification": "Powiadomienie o wygaśnięciu nazwy domeny", + "domainExpiryDescription": "Wyzwalaj powiadomienie, gdy nazwy domen wygasną za:", + "domain_expiry_unsupported_monitor_type": "Monitorowanie wygaśnięcia domeny nie jest obsługiwane dla tego typu monitora", + "minimumIntervalWarning": "Interwały poniżej 20 sekund mogą skutkować słabą wydajnością.", + "password": "Hasło", + "imageResetConfirmation": "Obraz przywrócony do ustawień domyślnych", + "screenshot of the website": "Zrzut ekranu strony internetowej", + "mtls-auth-server-key-label": "Klucz", + "Basic checkbox toggle button group": "Podstawowa grupa przycisków przełączających z polem wyboru", + "Basic radio toggle button group": "Podstawowa grupa przycisków przełączających radio", + "avgPing": "Średni ping", + "minPing": "Minimalny ping", + "Sort by certificate expiry": "Sortuj według daty wygaśnięcia certyfikatu", + "Message Format": "Format wiadomości", + "Region": "Region", + "To Number": "Do numeru", + "Never": "Nigdy", + "System Service": "Usługa systemowa", + "Check Type": "Sprawdź typ", + "GRPC Options": "Opcje GRPC", + "Metadata": "Metadane", + "Endpoint": "Punkt końcowy", + "TLS Alerts": "Alerty TLS", + "PushDeer Server URL": "Adres URL serwera PushDeer", + "Expected TLS Alert": "Oczekiwany alert TLS", + "expectedTlsAlertDescription": "Wybierz alert TLS, którego zwrotu oczekujesz od serwera. Użyj {code}, aby sprawdzić, czy punkty końcowe mTLS odrzucają połączenia bez certyfikatów klienta. Szczegółowe informacje znajdziesz pod adresem {link}.", + "slackIncludeGroupName": "Dołącz nazwę grupy monitorów", + "slackIncludeGroupNameDescription": "Jeśli opcja jest włączona, ścieżka grupy monitorów zostanie uwzględniona w powiadomieniach, aby ułatwić rozróżnienie monitorów o tej samej nazwie w różnych grupach.", + "slackUseTemplate": "Użyj niestandardowego szablonu wiadomości", + "slackUseTemplateDescription": "Jeśli opcja jest włączona, wiadomość zostanie wysłana przy użyciu niestandardowego szablonu. Możesz użyć szablonu Liquid, aby dołączyć informacje o grupie monitorów poprzez monitorJSON.path lub monitorJSON.pathName.", + "domain_expiry_unsupported_missing_target": "Nie skonfigurowano prawidłowej domeny ani nazwy hosta dla tego monitora", + "Halo PSA": "Halo PSA", + "halopsa_username_desc": "Nazwa użytkownika do uwierzytelniania za pomocą webhooka Halo PSA", + "maxPing": "Maksymalny ping", + "Severity": "Ważność", + "Service Name": "Nazwa usługi", + "End": "Koniec", + "None (Successful Connection)": "Brak (połączenie nawiązane pomyślnie)", + "Expand All Groups": "Rozwiń wszystkie grupy", + "mariadbSocketPathDetectedHelptext": "Łączenie się z bazą danych zgodnie z ustawieniami zmiennej środowiskowej {0}.", + "halopsa_field_uptime_kuma_version": "Numer wersji Uptime Kuma", + "halopsa_field_monitor": "Nazwa monitora", + "halopsa_field_monitor_id": "Unikalny identyfikator monitora (null dla powiadomień testowych) — użyj tej opcji, aby dopasować alerty do zgłoszeń", + "halopsa_field_message": "Pełny komunikat alarmowy ze statusem i szczegółami", + "halopsa_field_timestamp": "Znacznik czasu zdarzenia w formacie ISO 8601", + "halopsa_id_usage_hint": "💡 Wskazówka: Użyj monitor_id, aby niezawodnie dopasować alerty do zgłoszeń, oraz heartbeat_id, aby śledzić historię zdarzeń", + "halopsa_setup_step5": "Skonfiguruj runbook, aby używał monitor_id do dopasowywania alertów do istniejących zgłoszeń", + "discordMessageFormat": "Format wiadomości", + "smscTranslit": "smscTranslit", + "GlobalpingLocation": "W polu lokalizacji można podawać kontynenty, kraje, regiony, miasta, numery ASN, dostawców usług internetowych lub regiony chmury. Filtry można łączyć za pomocą znaku {plus} (np. {amazonPlusGermany} lub {comcastPlusCalifornia}). Jeśli opóźnienie jest ważnym wskaźnikiem, należy użyć filtrów, aby zawęzić lokalizację do niewielkiego regionu, aby uniknąć skoków. {fullDocs}.", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" to adres IP. Monitorowanie wygaśnięcia domeny wymaga nazwy domeny", + "passwordTooWeak": "Hasło jest zbyt słabe. Powinno zawierać znaki alfabetyczne i cyfry. Musi mieć co najmniej 6 znaków.", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Monitorowanie wygaśnięcia domeny nie jest dostępne dla \".{publicSuffix}\", ponieważ IANA nie wymienia żadnej usługi RDAP", + "TLS Alert Spec": "RFC 8446", + "lowIntervalWarning": "Czy na pewno chcesz ustawić wartość interwału poniżej 20 sekund? Może to spowodować spadek wydajności, zwłaszcza w przypadku dużej liczby monitorów.", + "discordUseMessageTemplateDescription": "Jeśli opcja jest włączona, wiadomość zostanie wysłana przy użyciu niestandardowego szablonu (LiquidJS). Pozostaw puste pole, aby użyć domyślnego formatu Uptime Kuma.", + "mtls-auth-server-ca-placeholder": "Serwer CA", + "Setup Instructions": "Instrukcja konfiguracji", + "wsSubprotocolDescription": "Wprowadź listę podprotokołów oddzielonych przecinkami. Więcej informacji na temat podprotokołów można znaleźć w {documentation}", + "responseMaxLengthDescription": "Maksymalny rozmiar danych odpowiedzi do przechowywania. Ustaw wartość 0, aby uzyskać nieograniczoną wielkość. Większe odpowiedzi zostaną skrócone. Domyślnie: 1024 (1 KB)", + "enableSSL": "Włącz SSL/TLS", + "mariadbCaCertificateLabel": "Certyfikat CA", + "mariadbUseSSLHelptext": "Włącz możliwość korzystania z szyfrowanego połączenia z bazą danych. Wymagane w przypadku większości baz danych w chmurze.", + "mariadbCaCertificateHelptext": "Wklej certyfikat CA w formacie PEM, aby używać certyfikatów z podpisem własnym. Pozostaw puste pole, jeśli Twoja baza danych korzysta z certyfikatu podpisanego przez publiczny urząd certyfikacji.", + "Loading...": "Ładowanie...", + "days": "{n} dzień | {n} dni", + "Monitors": "{n} Monitor | {n} Monitorów", + "hours": "{n} godzina | {n} godzin", + "minutes": "{n} minuta | {n} minut", + "minuteShort": "{n} min | {n} min", + "years": "{n} rok | {n} lat", + "unknownDays": "Nieznane dni", + "No incidents recorded": "Nie odnotowano żadnych incydentów", + "Pin this incident": "Zapisz ten incydent", + "Only retry if status code check fails": "Ponów próbę tylko w przypadku niepowodzenia sprawdzania kodu statusu", + "retryOnlyOnStatusCodeFailureDescription": "Jeśli opcja jest włączona, ponowne próby będą podejmowane tylko w przypadku niepowodzenia sprawdzania kodu statusu HTTP (np. serwer nie działa). Jeśli sprawdzanie kodu statusu zakończy się powodzeniem, ale zapytanie JSON zakończy się niepowodzeniem, monitor zostanie natychmiast oznaczony jako nieaktywny bez ponownych prób.", + "ignoreSecWebsocketAcceptHeaderDescription": "Pozwala serwerowi nie odpowiadać nagłówkiem Sec-WebSocket-Accept, jeśli aktualizacja websocket zakończy się powodzeniem.", + "wsCodeDescription": "Więcej informacji na temat kodów statusu można znaleźć w {rfc6455}", + "Subprotocol(s)": "Podprotokoły", + "saveResponseForNotifications": "Zapisz odpowiedź HTTP sukcesu dla powiadomień", + "saveErrorResponseForNotifications": "Zapisz odpowiedź błędu HTTP dla powiadomień", + "saveResponseDescription": "Przechowuje odpowiedź HTTP i udostępnia ją szablonom powiadomień jako {templateVariable}", + "responseMaxLength": "Maksymalna długość odpowiedzi (bajty)", + "logoutCurrentUser": "Wyloguj {username}", + "Resolver Server(s)": "Serwer(y) resolver", + "Past Incidents": "Dotychczasowe incydenty", + "Incident title": "Tytuł incydentu", + "Incident not found or access denied": "Nie znaleziono indycentu lub odmowa dostępu", + "Incident description": "Opis incydentu", + "Pinned incidents are shown prominently on the status page": "Przypięte incydenty są widocznie wyświetlane na stronie statusu", + "templateAvailableVariables": "Dostępne zmienne", + "example": "Przykład", + "Result": "Wynik", + "Edit Incident": "Edytuj zdarzenie", + "BodyInvalidFormatBecause": "Treść żądania nie jest prawidłowym formatem JSON, ponieważ {error}", + "steamApiKeyDescriptionAt": "Aby monitorować serwer gier Steam, potrzebujesz klucza API Steam Web. Klucz API można zarejestrować pod adresem {url}", + "hostnameCannotBeIP": "Nazwa hosta DNS nie może być adresem IP. Czy chciałeś użyć pola resolver?", + "invalidHostnameOrIP": "Nieprawidłowa nazwa hosta lub adres IP. Nazwa hosta musi być prawidłową nazwą FQDN. Nie można używać symboli wieloznacznych. Może zawierać znak podkreślenia lub kończyć się kropką.", + "invalidDNSHostname": "Nieprawidłowa nazwa hosta. Nazwa hosta musi być prawidłową nazwą FQDN. Może zawierać symbole wieloznaczne, podkreślenia lub kończyć się kropką.", + "wildcardOnlyForDNS": "Nazwy hostów z symbolami wieloznacznymi są obsługiwane tylko w przypadku monitorów DNS.", + "invalidURL": "Nieprawidłowy adres URL", + "Resolve": "Rozwiązywanie", + "Resolved": "Rozwiązano", + "createdAt": "Utworzono: {date}", + "lastUpdatedAt": "Ostatnia aktualizacja: {date}", + "lastUpdatedAtFromNow": "Ostatnia aktualizacja: {date} ({fromNow})", + "descriptionHelpText": "Wyświetlane na wewnętrznym pulpicie nawigacyjnym. Markdown jest dozwolony i oczyszczany (zachowuje spacje i wcięcia) przed wyświetleniem.", + "Actions": "Akcje", + "selectedMonitorCountMsg": "wybrane: {n} | wybrane: {n}", + "selectMonitorMsg": "Wybierz monitory, aby wykonać czynności", + "selectAllMonitorsAria": "Wybierz wszystkie monitory", + "deselectAllMonitorsAria": "Odznacz wszystkie monitory", + "deleteIncidentMsg": "Czy na pewno chcesz usunąć to zdarzenie?", + "Please input content": "Proszę wprowadzić treść", + "Please input title": "Proszę wprowadzić tytuł", + "dateCreatedAtFromNow": "Data utworzenia: {date} ({fromNow})", + "RSS Title": "Tytuł RSS", + "Leave blank to use status page title": "Pozostaw puste, aby użyć tytułu strony statusu", + "API Token": "Token API", + "See Jira Cloud Docs": "Zobacz dokumentację Jira Cloud", + "certHostnameMismatch": "Nazwa hosta certyfikatu nie zgadza się z adresem URL monitora.", + "sipsakPingWarning": "Aby korzystać z monitora SIP Options Ping, należy zainstalować Uptime Kuma bez Docker oraz klienta Sipsak na serwerze.", + "frontendVersionIs": "Wersja frontendu: {version}", + "cronScheduleDescription": "Harmonogram: {description}", + "Duration (Minutes)": "Czas trwania (minuty)", + "Clone Maintenance": "Klonuj konserwację", + "ariaPauseMaintenance": "Wstrzymaj ten harmonogram konserwacji", + "ariaResumeMaintenance": "Wznów ten harmonogram konserwacji", + "ariaCloneMaintenance": "Utwórz kopię tego harmonogramu konserwacji", + "ariaEditMaintenance": "Edytuj ten harmonogram konserwacji", + "ariaDeleteMaintenance": "Usuń ten harmonogram konserwacji", + "notificationChatPlatforms": "Platformy czatowe", + "notificationPushServices": "Usługi push", + "notificationSmsServices": "Usługi SMS", + "notificationEmail": "Email", + "notificationIncidentManagement": "Zarządzanie incydentami", + "notificationHomeAutomation": "Automatyka domowa", + "notificationOther": "Inne integracje", + "SMTP Security": "Zabezpieczenia SMTP", + "Ignore STARTTLS": "Ignoruj STARTTLS", + "Use STARTTLS": "Użyj STARTTLS", + "Disable STARTTLS": "Wyłącz STARTTLS", + "disableSTARTTLSDescription": "Włącz tę opcję dla serwerów SMTP, które nie obsługują protokołu STARTTLS. Spowoduje to wysyłanie wiadomości e-mail przez niezaszyfrowane połączenie.", + "deleteMonitorsMsg": "Czy na pewno chcesz usunąć wybrane monitory?", + "noMonitorsPausedMsg": "Żadne monitory nie zostały wstrzymane (żadne nie były aktywne)", + "noMonitorsResumedMsg": "Żadne monitory nie zostały wznowione (żadne nie były nieaktywne)", + "deleteGroupMsg": "Czy na pewno chcesz usunąć tę grupę?", + "pausedMonitorsMsg": "Wstrzymano {n} monitor | Wstrzymano {n} monitory", + "resumedMonitorsMsg": "Wznowiono {n} monitor | Wznowiono {n} monitory", + "bulkDeleteErrorMsg": "Nie udało się usunąć {n} monitor | Nie udało się usunąć {n} monitorów", + "deleteChildrenMonitors": "Usuń również bezpośrednie monitory podrzędne i ich elementy podrzędne, jeśli takie istnieją | Usuń również wszystkie {count} bezpośrednie monitory podrzędne i ich elementy podrzędne, jeśli takie istnieją", + "Sets end time based on start time": "Ustawia czas zakończenia na podstawie czasu rozpoczęcia", + "Please set start time first": "Najpierw ustaw czas rozpoczęcia", + "noMonitorsSelectedWarning": "Tworzysz konserwację bez żadnych monitorów, których dotyczy ta konserwacja. Czy na pewno chcesz kontynuować?", + "noMonitorsOrStatusPagesSelectedError": "Nie można utworzyć konserwacji bez monitorów lub stron statusu, których dotyczy problem", + "Google Apps Script Webhook URL": "Adres URL webhooka Google Apps Script", + "Deploy a Google Apps Script as a web app and paste the URL here": "Wdróż skrypt Google Apps Script jako aplikację internetową i wklej tutaj adres URL", + "Quick Setup Guide": "Szybki przewodnik konfiguracji", + "Open your Google Spreadsheet": "Otwórz arkusz kalkulacyjny Google", + "Google Apps Script Code": "Kod skryptu Google Apps", + "Copy to Clipboard": "Kopiuj do schowka", + "Copied to clipboard!": "Skopiowano do schowka!", + "Failed to copy to clipboard": "Nie udało się skopiować do schowka", + "Copy the web app URL and paste it above": "Skopiuj adres URL aplikacji internetowej i wklej go powyżej", + "Details": "Szczegóły", + "Paste the script code (see below)": "Wklej kod skryptu (patrz poniżej)", + "checkPriceAt": "Sprawdź ceny {service} na stronie {url}", + "OptionalParameters": "Parametry opcjonalne", + "aliyun-template-optional-parameters": "Parametry opcjonalne: {parameters}", + "You can divide numbers with commas or semicolons": "Liczby można dzielić za pomocą {comma} lub {semicolon}", + "aliyun-template-requirements-and-parameters": "Szablon wiadomości SMS aliyun musi zawierać parametry: {parameters}", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Ze względu na ograniczenia operatora, włącz opcjonalne zmienne, ryzykując niedostarczenie przesyłki", + "WeCom Mentioned Mobile List Description": "Wpisz numery telefonów, które chcesz oznaczyć. Wiele numerów oddziel przecinkami. Użyj {'@'}all, aby oznaczyć wszystkich.", + "see Jira Cloud Docs": "zobacz dokumentację Jira Cloud", + "aboutJiraCloudId": "Więcej informacji o Jira Cloud ID: {0}", + "serwersmsRecipientType": "Typ odbiorcy", + "serwersmsRecipientTypePhone": "Numer telefonu", + "serwersmsRecipientTypeGroup": "Grupa", + "serwersmsGroupId": "ID grupy", + "serwersmsGroupIdHelptext": "ID lub identyfikatory grup w Panelu klienta. Identyfikatory te można pobrać za pomocą grup akcji / indeksu lub skopiować z grupy edycji w Panelu klienta.", + "Analytics Type": "Typ analizy", + "ntfyCall": "Rozmowa telefoniczna", + "ntfyCallHelptext": "Wykonaj połączenie telefoniczne po uruchomieniu alertu. Ustaw opcję „tak”, aby użyć pierwszego zweryfikowanego numeru, lub wprowadź konkretny numer telefonu (np. +12223334444). Wymaga aplikacji ntfy Pro i zweryfikowanego numeru telefonu.", + "ntfyUseTemplate": "Dostosuj szablony powiadomień", + "ntfyUseTemplateDescription": "Włącz tę opcję, aby dostosować tytuły powiadomień i wiadomości za pomocą szablonów LiquidJS", + "ntfyCustomMessage": "Szablon wiadomości niestandardowej", + "ntfyCustomTitle": "Szablon tytułu niestandardowego", + "ntfyNotificationTemplateFallback": "Pozostaw puste, aby użyć domyślnego formatu Uptime Kuma", + "twilioApiKeyHelptext": "Klucz API jest opcjonalny, ale zalecany. Możesz podać identyfikator SID konta i token uwierzytelniający z strony TwilioConsole lub identyfikator SID konta oraz parę klucza API i tajnego klucza API", + "twilloMessagingServiceSIDHelptext": "Wpisz tutaj identyfikator SID usługi przesyłania wiadomości, jeśli korzystasz z {twillo_messaging_service_help_link} do zarządzania nadawcami i funkcjami", + "Badge Link Generator Helptext": "Linki do odznak są dostępne dla wszystkich monitorów przypisanych do publicznych stron statusu. Więcej informacji można znaleźć w {documentation}.", + "HTTP Method": "Metoda HTTP", + "webhookPostMethodDesc": "POST jest odpowiedni dla większości nowoczesnych serwerów HTTP.", + "webhookGetMethodDesc": "GET wysyła dane jako parametry zapytania i nie pozwala na konfigurację treści. Przydatne do uruchamiania monitorów Uptime Kuma Push.", + "showOnlyLastHeartbeat": "Pokaż tylko ostatnie uderzenie serca", + "Screenshot Delay": "Opóźnienie zrzutu ekranu (oczekuje {milliseconds})", + "milliseconds": "{n} milisekunda | {n} milisekund", + "screenshotDelayDescription": "Opcjonalnie odczekaj tyle milisekund przed wykonaniem zrzutu ekranu. Maksymalnie: {maxValueMs} ms (0,5 × interwał).", + "screenshotDelayWarning": "Wyższe wartości powodują, że przeglądarka pozostaje otwarta dłużej, co może zwiększyć zużycie pamięci w przypadku wielu monitorów działających jednocześnie.", + "systemService": "Usługa systemowa", + "systemServiceName": "Nazwa usługi", + "systemServiceDescription": "Sprawdza, czy usługa systemowa {service_name} jest aktywna", + "systemServiceDescriptionLinux": "Sprawdza, czy usługa systemd {service_name} systemu Linux jest aktywna", + "systemServiceDescriptionWindows": "Sprawdza, czy usługa Windows Service Manager {service_name} jest uruchomiona", + "systemServiceCommandHint": "Użyte polecenie: {command}", + "systemServiceExpectedOutput": "Oczekiwany wynik: \"{0}\"", + "message": "wiadomość", + "json_value": "Wartość JSON", + "Select All": "Zaznacz wszystko", + "Deselect All": "Odznacz wszystko", + "Enter the list of nodes": "Wprowadź listę węzłów zarządzania RabbitMQ", + "Press Enter to add node": "Naciśnij Enter, aby dodać węzeł", + "resendFromName": "Od Nazwa", + "resendFromEmail": "Z wiadomości e-mail", + "resendLeaveBlankForDefaultName": "pozostawić puste pole dla nazwy domyślnej", + "resendLeaveBlankForDefaultSubject": "Pozostaw puste miejsce dla domyślnego tematu", + "resendToEmail": "Do wiadomości e-mail", + "resendSubject": "Temat", + "resendApiKey": "Ponownie wyślij klucz API", + "resendApiHelp": "Utwórz klucz API tutaj {0}", + "discordSuppressNotificationsHelptext": "Po włączeniu tej opcji wiadomości będą publikowane na kanale, ale nie będą wywoływać powiadomień push ani powiadomień na pulpicie dla odbiorców.", + "Suppress Notifications": "Wyłącz powiadomienia", + "Globalping - Access global monitoring probes": "Globalping – dostęp do globalnych sond monitorujących", + "GlobalpingDescription": "Globalping zapewnia dostęp do tysięcy sond hostowanych przez społeczność, które służą do przeprowadzania testów i pomiarów sieciowych. Dla wszystkich anonimowych użytkowników obowiązuje limit 250 testów na godzinę. Aby podwoić limit do 500 testów na godzinę, należy zapisać token w {accountSettings}.", + "Globalping API Token": "Token API Globalping", + "globalpingApiTokenDescription": "Uzyskaj swój token API Globalping pod adresem {0}.", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" jest zbyt krótkie, aby mogło pełnić funkcję domeny najwyższego poziomu", + "domain_expiry_unsupported_is_icann": "Domena \"{domain}\" nie kwalifikuje się do monitorowania wygaśnięcia domeny, ponieważ jej publiczny sufiks \".{publicSuffix}\" nie jest ICAN", + "username": "Nazwa użytkownika", + "halopsa_setup_step3": "Skopiuj adres URL webhooka i wklej go powyżej pola tekstowego", + "halopsa_setup_step4": "Wybierz opcję „Podstawowe uwierzytelnianie” i utwórz nazwę użytkownika oraz hasło. Następnie wpisz lub wklej nazwę użytkownika i hasło w polach testowych powyżej", + "Clear current filters": "Wyczyść aktualne filtry", + "Sort options": "Opcje sortowania", + "Sort by status": "Sortuj według statusu", + "Sort by name": "Sortuj według nazwy", + "Sort by uptime": "Sortuj według czasu działania", + "versionIs": "Wersja: {version}", + "Google": "Google", + "SSL/TLS": "SSL/TLS", + "Load More": "Załaduj więcej", + "HeadersInvalidFormatBecause": "Nagłówki żądania nie są prawidłowym JSON, ponieważ {error}", + "Examples:": "Przykłady: {0}", + "deletedMonitorsMsg": "Usunięto {n} monitor | Usunięto {n} monitory", + "snmpV3Username": "Nazwa użytkownika SNMPv3", + "settingsDomainExpiry": "Wygaśnięcie domeny", + "Collapse All Groups": "Zwiń wszystkie grupy", + "GlobalpingHostname": "Publicznie dostępny cel pomiaru. Zazwyczaj jest to nazwa hosta lub adres IPv4/IPv6, w zależności od typu pomiaru.", + "Template ID": "ID szablonu", + "halopsa_password_desc": "Hasło do uwierzytelniania za pomocą webhooka Halo PSA", + "monitorTypeGameServer": "Serwer Gier", + "monitorTypeDatabase": "Typ monitora bazy danych", + "monitorTypeSpecial": "Specjalny", + "oracledbConnectionString": "Baza danych Oracle: {connectionString}", + "slug is not found": "Nie znaleziono symbolu", + "maxTemplateFormatDescription": "Messenger MAX wspiera zwykły tekst, Markdown i formatowanie HTML.", + "wayToGetMaxToken": "Możesz otrzymać token MAX i inne szczegóły z {0}.", + "maxUseTemplate": "Użyj customowego szablonu wiadomości", + "maxUseTemplateDescription": "Jeżeli włączone, wiadomość zostanie wysłana z użyciem customowego szablonu.", + "Notification Action": "Akcja powiadomień", + "certificateExpiryNotificationHelp": "Liczba dni wyprzedzenia może zostać skonfigurowana w ustawieniach.", + "notificationUniversal": "Uniwersalny", + "Cloud ID": "ID Chmury", + "matrixUseTemplateDescription": "Jeżeli włączone, wiadomość zostanie wysłana z użyciem customowego szablonu.", + "Certificate Chain:": "Łańcuch certyfikatu:", + "signalUseTemplate": "Użyj customowego szablonu wiadomości", + "teamsEnableTags": "Zawieraj tagi", + "Bearer Token": "Token uwierzytelniający", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Ustaw 'Wykonaj jako: Ja' i 'Kto ma dostęp: Wszyscy'", + "teamsEnableTagsDescription": "Gdy włączone, wiadomość będzie zawierać tagi monitorów.", + "API Version": "Wersja API", + "vkDontParseLinks": "Wyłącz fragmenty linku", + "matrixUseTemplate": "Korzystaj z customowego szablonu wiadomości", + "wayToGetMaxChatID": "Sprecyzuj identyfikator czatu MAX gdzie powinne być dostarczane wiadomości.", + "pingPacketSizeWarning": "Pakiet o rozmiarze mniejszym niż 16 nie produkuje czasu okrążenia w Linuxie", + "Connection Timeout": "Przekroczenie limitu czasu połączenia", + "Peer ID": "Id Peer'a", + "vkApiVersionDescription": "Wersja VK API używana przez żądanie. Zostaw domyślne chyba że potrzebujesz innej wersji VK API przez kompatybilność.", + "vkDontParseLinksDescription": "Jeżeli włączone, VK nie będzie generował podglądu/fragmentów linku." +} diff --git a/src/lang/pt-BR.json b/src/lang/pt-BR.json new file mode 100644 index 0000000..ad9cecd --- /dev/null +++ b/src/lang/pt-BR.json @@ -0,0 +1,1730 @@ +{ + "languageName": "Português (Brasileiro)", + "checkEverySecond": "Verificar a cada {0} segundos", + "retryCheckEverySecond": "Tentar novamente a cada {0} segundos", + "retriesDescription": "Máximo de tentativas antes que o serviço seja marcado como inativo e uma notificação seja enviada", + "ignoreTLSError": "Ignorar erros TLS/SSL para sites HTTPS", + "upsideDownModeDescription": "Inverta o status. Se o serviço estiver acessível, ele está DESLIGADO.", + "maxRedirectDescription": "Número máximo de redirecionamentos a seguir. Defina como 0 para desativar redirecionamentos.", + "acceptedStatusCodesDescription": "Selecione os códigos de status que são considerados uma resposta bem-sucedida.", + "passwordNotMatchMsg": "A senha repetida não corresponde.", + "notificationDescription": "Atribua uma notificação ao (s) monitor (es) para que funcione.", + "keywordDescription": "Pesquise a palavra-chave em html simples ou resposta JSON e diferencia maiúsculas de minúsculas.", + "pauseDashboardHome": "Pausado", + "deleteMonitorMsg": "Tem certeza de que deseja excluir este monitor?", + "deleteNotificationMsg": "Tem certeza de que deseja excluir esta notificação para todos os monitores?", + "resolverserverDescription": "O Cloudflare é o servidor padrão. Você pode especificar uma lista de endereços IP ou nomes de host separados por vírgulas.", + "rrtypeDescription": "Selecione o RR-Type que você deseja monitorar", + "pauseMonitorMsg": "Tem certeza que deseja fazer uma pausa?", + "enableDefaultNotificationDescription": "Para cada novo monitor, esta notificação será habilitada por padrão. Você ainda pode desativar a notificação separadamente para cada monitor.", + "clearEventsMsg": "Tem certeza de que deseja excluir todos os eventos deste monitoramento? Isso removerá permanentemente os dados de mensagens associados e não pode ser desfeito.", + "clearHeartbeatsMsg": "Tem certeza de que deseja excluir todos os heartbeats deste monitor? Isso removerá permanentemente todos os dados de heartbeats e não poderá ser desfeito.", + "confirmClearStatisticsMsg": "Tem certeza que deseja excluir TODAS as estatísticas?", + "importHandleDescription": "Escolha 'Ignorar existente' se quiser ignorar todos os monitores ou notificações com o mesmo nome. 'Substituir' excluirá todos os monitores e notificações existentes.", + "confirmImportMsg": "Tem certeza que deseja importar o backup? Certifique-se de que selecionou a opção de importação correta.", + "twoFAVerifyLabel": "Digite seu token para verificar se 2FA está funcionando:", + "tokenValidSettingsMsg": "O token é válido! Agora você pode salvar as configurações 2FA.", + "confirmEnableTwoFAMsg": "Tem certeza de que deseja habilitar 2FA?", + "confirmDisableTwoFAMsg": "Tem certeza de que deseja desativar 2FA?", + "Settings": "Configurações", + "Dashboard": "Painel", + "New Update": "Nova Atualização", + "Language": "Idioma", + "Appearance": "Aparência", + "Theme": "Tema", + "General": "Geral", + "Version": "Versão", + "Check Update On GitHub": "Verificar atualização no Github", + "List": "Lista", + "Add": "Adicionar", + "Add New Monitor": "Adicionar novo monitor", + "Quick Stats": "Estatísticas rápidas", + "Up": "Ligado", + "Down": "Desligado", + "Pending": "Pendente", + "Unknown": "Desconhecido", + "Pause": "Pausar", + "Name": "Nome", + "Status": "Status", + "DateTime": "Data hora", + "Message": "Mensagem", + "No important events": "Nenhum evento importante", + "Resume": "Retomar", + "Edit": "Editar", + "Delete": "Apagar", + "Current": "Atual", + "Uptime": "Tempo de atividade", + "Cert Exp.": "Expiração Do Certificado.", + "day": "dia | dias", + "-day": "-dia", + "hour": "hora | horas", + "-hour": "-hora", + "Response": "Resposta", + "Ping": "Ping", + "Monitor Type": "Tipo de Monitor", + "Keyword": "Palavra-Chave", + "Friendly Name": "Apelido", + "URL": "URL", + "Hostname": "Hostname", + "Port": "Porta", + "Heartbeat Interval": "Intervalo de Heartbeat", + "Retries": "Novas tentativas", + "Heartbeat Retry Interval": "Intervalo de repetição de Heartbeat", + "Advanced": "Avançado", + "Upside Down Mode": "Modo Invertido", + "Max. Redirects": "Redirecionamentos Máx", + "Accepted Status Codes": "Códigos HTTP Aceitáveis", + "Save": "Salvar", + "Notifications": "Notificações", + "Not available, please setup.": "Não disponível, por favor configure.", + "Setup Notification": "Configurar Notificação", + "Light": "Claro", + "Dark": "Escuro", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Tema - Barra de Heartbeat", + "Normal": "Normal", + "Bottom": "Inferior", + "None": "Nenhum", + "Timezone": "Fuso horário", + "Search Engine Visibility": "Visibilidade do mecanismo de pesquisa", + "Allow indexing": "Permitir Indexação", + "Discourage search engines from indexing site": "Desencoraje os motores de busca de indexar o site", + "Change Password": "Mudar senha", + "Current Password": "Senha atual", + "New Password": "Nova Senha", + "Repeat New Password": "Repetir Nova Senha", + "Update Password": "Atualizar Senha", + "Disable Auth": "Desativar Autenticação", + "Enable Auth": "Ativar Autenticação", + "disableauth.message1": "Você tem certeza que deseja {disableAuth}?", + "disable authentication": "desativar a autenticação", + "disableauth.message2": "Isso é para {intendThirdPartyAuth} na frente do 'UpTime Kuma' como o Cloudflare Access.", + "where you intend to implement third-party authentication": "alguém que tem autenticação de terceiros", + "Please use this option carefully!": "Por favor, utilize isso com cautela!", + "Logout": "Deslogar", + "Leave": "Sair", + "I understand, please disable": "Eu entendo, por favor desative", + "Confirm": "Confirmar", + "Yes": "Sim", + "No": "Não", + "Username": "Usuário", + "Password": "Senha", + "Remember me": "Lembre-me", + "Login": "Autenticar", + "No Monitors, please": "Nenhum monitor, por favor", + "add one": "adicionar um", + "Notification Type": "Tipo de Notificação", + "Email": "Email", + "Test": "Testar", + "Certificate Info": "Info. do Certificado", + "Resolver Server": "Resolver Servidor", + "Resource Record Type": "Tipo de registro de aplicação", + "Last Result": "Último resultado", + "Create your admin account": "Crie sua conta de admin", + "Repeat Password": "Repita a senha", + "Import Backup": "Importar Backup", + "Export Backup": "Exportar Backup", + "Export": "Exportar", + "Import": "Importar", + "respTime": "Tempo de Resp. (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Padrão habilitado", + "Apply on all existing monitors": "Aplicar em todos os monitores existentes", + "Create": "Criar", + "Clear Data": "Limpar Dados", + "Events": "Eventos", + "Heartbeats": "Batimentos Cardíacos", + "Auto Get": "Obter Automático", + "backupDescription": "Você pode fazer backup de todos os monitores e todas as notificações em um arquivo JSON.", + "backupDescription2": "OBS: Os dados do histórico e do evento não estão incluídos.", + "backupDescription3": "Dados confidenciais, como tokens de notificação, estão incluídos no arquivo de exportação, mantenha-o com cuidado.", + "alertNoFile": "Selecione um arquivo para importar.", + "alertWrongFileType": "Selecione um arquivo JSON.", + "Clear all statistics": "Limpar todas as estatísticas", + "Skip existing": "Pular existente", + "Overwrite": "Sobrescrever", + "Options": "Opções", + "Keep both": "Manter os dois", + "Verify Token": "Verificar Token", + "Setup 2FA": "Configurar 2FA", + "Enable 2FA": "Ativar 2FA", + "Disable 2FA": "Desativar 2FA", + "2FA Settings": "Configurações do 2FA", + "Two Factor Authentication": "Autenticação e Dois Fatores", + "Active": "Ativo", + "Inactive": "Inativo", + "Token": "Token", + "Show URI": "Mostrar URI", + "Tags": "Tag", + "Add New below or Select...": "Adicionar Novo abaixo ou Selecionar…", + "Tag with this name already exist.": "Já existe uma etiqueta com este nome.", + "Tag with this value already exist.": "Já existe uma etiqueta com este valor.", + "color": "cor", + "value (optional)": "valor (opcional)", + "Gray": "Cinza", + "Red": "Vermelho", + "Orange": "Laranja", + "Green": "Verde", + "Blue": "Azul", + "Indigo": "Índigo", + "Purple": "Roxo", + "Pink": "Rosa", + "Search...": "Buscar…", + "Avg. Ping": "Ping Médio", + "Avg. Response": "Resposta Média", + "Status Page": "Página de Status", + "Status Pages": "Página de Status", + "Entry Page": "Página de entrada", + "statusPageNothing": "Nada aqui, por favor, adicione um grupo ou monitor.", + "No Services": "Nenhum Serviço", + "All Systems Operational": "Todos os Serviços Operacionais", + "Partially Degraded Service": "Serviço parcialmente degradado", + "Degraded Service": "Serviço Degradado", + "Add Group": "Adicionar Grupo", + "Add a monitor": "Adicionar um monitor", + "Edit Status Page": "Editar Página de Status", + "Go to Dashboard": "Ir para a dashboard", + "apprise": "Apprise (Suporta mais de 50 serviços de notificação)", + "Help": "Ajuda", + "Select status pages...": "Selecionar status pages…", + "Game": "Jogo", + "Passive Monitor Type": "Tipo de monitoramento passivo", + "Specific Monitor Type": "Tipo de monitoramento específico", + "Monitor": "Monitoramento | Monitoramentos", + "needPushEvery": "Você deve chamar esta URL a cada {0} segundos.", + "Push URL": "URL de push", + "Custom": "Personalizado", + "here": "aqui", + "Required": "Requerido", + "webhookJsonDesc": "{0} é bom para qualquer servidor HTTP moderno, como Express.js", + "webhookAdditionalHeadersTitle": "Cabeçalhos Adicionais", + "webhookAdditionalHeadersDesc": "Define cabeçalhos adicionais enviados com o webhook. Cada cabeçalho pode ser definido como um JSON chave/valor.", + "Webhook URL": "URL Do Webhook", + "Priority": "Prioridade", + "Read more": "Ver mais", + "appriseInstalled": "Apprise está instalado.", + "appriseNotInstalled": "Apprise não está instalado. {0}", + "Headers": "Cabeçalhos", + "BodyInvalidFormat": "O corpo da solicitação não é um JSON válido: ", + "Monitor History": "Histórico de monitoramento", + "clearDataOlderThan": "Mantenha os dados do histórico do monitoramento por {0} dias.", + "PasswordsDoNotMatch": "As senhas não coincidem.", + "records": "registros", + "One record": "Um registro", + "Current User": "Usuário atual", + "successMessage": "Mensagem de sucesso", + "Post URL": "Posto URL", + "Application Token": "Token de aplicativo", + "Server URL": "URL do servidor", + "Body": "Corpo", + "PushUrl": "Push URL", + "recent": "Recente", + "Done": "Feito", + "Info": "Informação", + "Security": "Segurança", + "Steam API Key": "API Key da Steam", + "Default": "Padrão", + "HTTP Options": "Opções HTTP", + "Create Incident": "Criar incidente", + "Title": "Título", + "Style": "Estilo", + "info": "informação", + "danger": "perigo", + "Please input title and content": "Por favor, inclua título e o conteúdo", + "Created": "Criado", + "Last Updated": "Última atualização", + "Switch to Light Theme": "Mudar para tema claro", + "Show Tags": "Mostrar tags", + "Hide Tags": "Esconder tags", + "Description": "Descrição", + "No monitors available.": "Nenhum monitoramento disponível.", + "Add one": "Adicionar um", + "No Monitors": "Sem monitoramentos", + "Services": "Serviços", + "Discard": "Descartar", + "Cancel": "Cancelar", + "Customize": "Customizar", + "Custom CSS": "CSS personalizado", + "deleteStatusPageMsg": "Tem certeza que deseja apagar essa status page?", + "Proxies": "Proxies", + "default": "Padrão", + "enabled": "Ativado", + "setAsDefault": "Definir como padrão", + "Primary Base URL": "URL base principal", + "Resend Notification if Down X times consecutively": "Reenviar Notificação se OFFLINE X vezes consecutivamente", + "pushOptionalParams": "Parâmetros opcionais: {0}", + "webhookFormDataDesc": "{multipart} é bom para PHP. O JSON precisará ser analisado com {decodeFunction}", + "HeadersInvalidFormat": "Os cabeçalhos da solicitação não são um JSON válidos: ", + "steamApiKeyDescription": "Para monitorar um Steam Game Server, você precisa de uma chave Steam Web-API. Você pode registrar sua chave de API aqui: ", + "warning": "atenção", + "Switch to Dark Theme": "Mudar para tema escuro", + "Custom Footer": "Rodapé personalizado", + "error": "erro", + "critical": "crítico", + "dark": "escuro", + "statusMaintenance": "Manutenção", + "Maintenance": "Manutenção", + "resendEveryXTimes": "Reenviar a cada {0} vezes", + "resendDisabled": "Reenvio desativado", + "Schedule maintenance": "Manutenção agendada", + "Affected Monitors": "Monitoramentos afetados", + "Start of maintenance": "Iniciar manutenção", + "All Status Pages": "Todas as Status Pages", + "Method": "Método", + "General Monitor Type": "Tipo de monitoramento geral", + "markdownSupported": "A sintaxe Markdown é suportada. Se usar HTML, evite espaços em branco no início para evitar problemas de formatação.", + "emojiCheatSheet": "Dicas de Emojis", + "topic": "Tópico", + "topicExplanation": "Tópico MQTT para monitorar", + "successMessageExplanation": "Mensagem MQTT que será considerada como sucesso", + "Content Type": "Tipo do Conteúdo", + "Shrink Database": "Encolher Banco de Dados", + "Content": "Conteúdo", + "Pick a RR-Type...": "Selecione um RR-Type…", + "Pick Accepted Status Codes...": "Selecione Os Códigos de Status Aceitos…", + "Pick Affected Monitors...": "Selecione os Monitores Afetados…", + "Channel Name": "Nome Do Canal", + "Don't know how to get the token? Please read the guide:": "Não sabe com pegar o token? Por favor, leia o guia:", + "smtpDkimheaderFieldNames": "Chaves Do Cabeçalho para assinar (Opcional)", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "A conexão atual pode ser perdida se você estiver se conectando pelo túnel da Cloudflare. Você tem certeza que deseja pará-lo? Digite a sua senha para confirmar.", + "Powered by": "Fornecido por", + "deleteProxyMsg": "Você tem certeza que deseja deletar este proxy para todos os monitores?", + "proxyDescription": "Os proxies devem ser atribuídos a um monitor para funcionar.", + "Certificate Chain": "Cadeia De Certificados", + "Domain Name Expiry Notification": "Notificação De Expiração Do Nome Do Domínio", + "Proxy": "Proxy", + "wayToGetTelegramChatID": "Você pode pegar o Chat ID enviando uma mensagem marcando o bot no grupo e indo nessa URL para ver o chat_id:", + "wayToGetLineNotifyToken": "Você pode pegar o token de acesso de {0}", + "disableCloudflaredNoAuthMsg": "Você está no modo sem autenticação, a senha não é necessária.", + "Frontend Version do not match backend version!": "Versão do frontend é diferente da versão do backend!", + "strategyManual": "Ativar/Desativar Manualmente", + "weekdayShortThu": "Qui", + "Basic Settings": "Configurações Básicas", + "User ID": "ID Do Usuário", + "Line Developers Console": "Linha Do Terminal De Desenvolvimento", + "lineDevConsoleTo": "Linha Do Terminal De Desenvolvimento- {0}", + "smseagleToken": "Token De Acesso Da API", + "Notification Service": "Serviço De Notificação", + "default: notify all devices": "padrão: notificar todos os dispositivos", + "Trigger type:": "Tipo Do Acionamento:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Em seguida, escolha uma ação, por exemplo, mudar a cena para onde uma luz RGB fica vermelha.", + "Enable": "Habilitado", + "Disable": "Desabilitado", + "IconUrl": "URL Do Ícone", + "Enable DNS Cache": "(Descontinuado) Habilitar Cache de DNS para monitores HTTP(s)", + "Single Maintenance Window": "Janela Única De Manutenção", + "dnsCacheDescription": "Pode não funcionar em alguns ambientes com IPv6, desabita caso encontre qualquer problema.", + "Messaging API": "API Da Mensageira", + "Icon URL": "URL Do Ícone", + "Clone Monitor": "Clonar Monitoramento", + "Clone": "Clonar", + "cloneOf": "Clone do {0}", + "deleteMaintenanceMsg": "Você tem certeza que deseja apagar essa manutenção?", + "sameAsServerTimezone": "O mesmo do servidor de fuso-horário", + "startDateTime": "Início Data/Horário", + "endDateTime": "Fim Data/Horário", + "cronExpression": "Expressão Do Cron", + "cronSchedule": "Agendar: ", + "invalidCronExpression": "Expressão Cron inválida: {0}", + "Display Timezone": "Mostrar Fuso-horário", + "Server Timezone": "Servidor De Fuso-horário", + "statusPageMaintenanceEndDate": "Fim", + "Schedule Maintenance": "Agendar Manutenção", + "Date and Time": "Data E Horário", + "DateTime Range": "Intervalo De Tempo", + "Maintenance Time Window of a Day": "Janela de tempo de manutenção de um dia", + "uninstalling": "Desinstalando", + "confirmUninstallPlugin": "Você tem certeza were quer desinstalar esse plugin?", + "notificationRegional": "Região", + "dnsPortDescription": "Porta do servidor DNS. O padrão é 53. Você pode mudar a porta em qualquer momento.", + "affectedMonitorsDescription": "Selecione os monitores afetados pela manutenção atual", + "Icon Emoji": "Ícone Do Emoji", + "wayToGetKookBotToken": "Criar aplicação e pegar o token do bot em {0}", + "Notification Sound": "Som De Notificação", + "More info on:": "Mais informações em: {0}", + "SMS Type": "Tipo Do SMS", + "Internal Room Id": "ID Interno Da Sala", + "Platform": "Plataforma", + "serwersmsAPIPassword": "Senha Da API", + "serwersmsPhoneNumber": "Número Do Telefone", + "documentation": "documentação", + "smtpDkimDomain": "Nome Do Domínio", + "smtpDkimKeySelector": "Chave Selecionadora", + "smtpDkimPrivateKey": "Chave Privada", + "smtpDkimHashAlgo": "Algoritmo Hash (Opcional)", + "smtpDkimskipFields": "Chaves Do Cabeçalho para não assinar (Opcional)", + "alertaEnvironment": "Ambiente", + "alertaRecoverState": "Estado De Recuperação", + "smseagleEncoding": "Enviar como Unicode (padrão=GSM-7)", + "onebotGroupMessage": "Grupo", + "onebotPrivateMessage": "Privado", + "onebotUserOrGroupId": "ID do Grupo/Usuário", + "No Maintenance": "Sem Manutenção", + "telegramProtectContentDescription": "Se ativado, a mensagens do bot do Telegram serão protegidas contra encaminhamentos e salvamento.", + "telegramProtectContent": "Proteger Contra Encaminhamento/Salvamento", + "affectedStatusPages": "Mostrar essa mensagem de manutenção nas páginas de status selecionadas", + "loadingError": "Não foi possível pegar os dados, por favor tente novamente.", + "Bot Display Name": "Nome Visível Do Bot", + "Access Token": "Token De Acesso", + "Unpin": "Desfixar", + "telegramSendSilently": "Enviar Silenciosamente", + "telegramSendSilentlyDescription": "Enviar a mensagem silenciosamente. Os usuários não receberam uma notificação com som.", + "YOUR BOT TOKEN HERE": "O SEU TOKEN DO BOT VAI AQUI", + "warningTimezone": "Está usando os servidores de fuso-horários", + "dayOfWeek": "Dia Da Semana", + "dayOfMonth": "Dia Do Mês", + "lastDay": "Último Dia", + "lastDay1": "Último Dia Do Mês", + "lastDay2": "Penúltimo Dia Do Mês", + "lastDay3": "Antepenúltimo Dia Do Mês", + "lastDay4": "Quarto Último Dia Do Mês", + "weekdayShortMon": "Seg", + "weekdayShortTue": "Ter", + "weekdayShortWed": "Qua", + "weekdayShortFri": "Sex", + "weekdayShortSat": "Sab", + "weekdayShortSun": "Dom", + "wayToGetTeamsURL": "Você pode aprender a como criar a URL do webhook {0}.", + "Hello @everyone is...": "Olá {'@'}everyone é…", + "Number": "Número", + "install": "Instalar", + "installing": "Instalando", + "uninstall": "Desinstalar", + "Ignore TLS Error": "Ignorar Erro De TLS", + "Discord Webhook URL": "URL Do Webhook Do Discord", + "emailCustomSubject": "Assunto Personalizado", + "Prefix Custom Message": "Prefixo Personalizado Da Mensagem", + "wayToGetZohoCliqURL": "Você pode aprender a como criar uma URL de Webhook {0}.", + "Channel access token": "Canal do token de acesso", + "promosmsPassword": "Senha Da API", + "promosmsLogin": "Nome Do Login Da API", + "atLeastOneMonitor": "Selecione pelo menos um monitoramento afetado", + "apiCredentials": "Credenciais Da API", + "For safety, must use secret key": "Para segurança deve se usar uma chave secreta", + "Device Token": "Token Do Dispositivo", + "Retry": "Tentar Novamente", + "Topic": "Tópico", + "Setup Proxy": "Configuração do Proxy", + "Proxy Protocol": "Protocolo Do Proxy", + "Proxy Server": "Servidor Proxy", + "Proxy server has authentication": "O servidor proxy tem autenticação", + "aboutWebhooks": "Mais informações sobre Webhooks em: {0}", + "Integration Key": "Chave De Integração", + "Integration URL": "URL De Integração", + "do nothing": "fazendo nada", + "onebotSafetyTips": "Por segurança deve adicionar o token de acesso", + "Subject:": "Assunto:", + "Valid To:": "Válido para:", + "For example: nginx, Apache and Traefik.": "Por exemplo: Nginx, Apache e Traefik.", + "Please read": "Por favor, leia", + "RadiusCallingStationIdDescription": "Identificador do dispositivo de chamada", + "certificationExpiryDescription": "O monitoramento por HTTPS envia a notificação quando o certificado TLS expirar em:", + "or": "ou", + "Effective Date Range": "Intervalo Efetivo De Data (Opcional)", + "recurringIntervalMessage": "Rodar diariamente | Rodar a cada {0} dias", + "Status:": "Status: {0}", + "smtpDkimSettings": "Configurações DKIM", + "alertaApiKey": "Chave Da API", + "alertaAlertState": "Estado Do Alerta", + "statusPageRefreshIn": "Atualizando em: {0}", + "Untitled Group": "Grupo Sem Título", + "primary": "primário", + "setAsDefaultProxyDescription": "Este proxy será habilitado por padrão em todos os novos monitores. Você pode desabilitar o proxy individualmente para cada monitor.", + "Valid": "Válido", + "Invalid": "Inválido", + "User": "Usuário", + "Installed": "Instalado", + "Not installed": "Não instalado", + "enableProxyDescription": "Este proxy não afetará as solicitações do monitor até que seja ativado. Você pode controlar temporariamente a desativação do proxy de todos os monitores pelo status de ativação.", + "Not running": "Desabilitado", + "Remove Token": "Remover Token", + "Start": "Iniciar", + "Stop": "Parar", + "Add New Status Page": "Adicionar Nova Página De Status", + "Accept characters:": "Caracteres aceitos:", + "Running": "Habilitado", + "startOrEndWithOnly": "Apenas iniciar ou parar com {0}", + "No consecutive dashes": "Sem traços consecutivos", + "Next": "Próximo", + "No Proxy": "Sem Proxy", + "Authentication": "Autenticação", + "HTTP Basic Auth": "Autenticação Básica No HTTP", + "New Status Page": "Nova Página De Status", + "Page Not Found": "Página Não Encontrada", + "Reverse Proxy": "Proxy Reverso", + "About": "Sobre", + "Message:": "Mensagem:", + "HTTP Headers": "Cabeçalhos HTTP", + "Trust Proxy": "Proxy Confiável", + "Other Software": "Outros Programas", + "Days Remaining:": "Dias Restantes:", + "No status pages": "Sem página de status", + "Date Created": "Data De Criação", + "Backup": "Cópia de Segurança", + "wayToGetCloudflaredURL": "(Baixe o CloudFlareD de {0})", + "cloudflareWebsite": "Site Da CloudaFlare", + "Issuer:": "Emissor:", + "Fingerprint:": "Impressão Digital:", + "Footer Text": "Texto Do Rodapé", + "Domain Names": "Nome Dos Domínios", + "signedInDispDisabled": "Autenticação Desabilitada.", + "RadiusSecretDescription": "Compartilhe o Segredo entre o cliente e o servidor", + "Certificate Expiry Notification": "Notificação De Certificado Expirado", + "The resource is no longer available.": "O recurso não está mais disponível.", + "There might be a typing error in the address.": "Pode ter um erro de digitação no endereço.", + "Retype the address.": "Digitar novamente o endereço.", + "Go back to the previous page.": "Voltar para a página anterior.", + "Query": "Query", + "settingsCertificateExpiry": "O Certificado TLS Expira", + "Connection Type": "Tipo Da Conexão", + "signedInDisp": "Logado como {0}", + "RadiusCallingStationId": "ID Da Estação De Chamada", + "RadiusCalledStationIdDescription": "Identificador do dispositivo de chamada", + "Coming Soon": "Em Breve", + "Connection String": "String De Conexão", + "Docker Daemon": "Daemon Do Docker", + "Show Powered By": "Mostrar Fornecido Por", + "RadiusSecret": "Segredo Radius", + "RadiusCalledStationId": "ID Da Estação Chamada", + "deleteDockerHostMsg": "Você tem certeza que quer deletar esse host do Docker para todos os monitores?", + "tcp": "TCP / HTTP", + "Docker Container": "Container Docker", + "Container Name / ID": "Nome / ID do Container", + "Domain": "Domínio", + "Workstation": "Estação De Trabalho", + "Packet Size": "Tamanho Do Pacote", + "Bot Token": "Token do Bot", + "wayToGetTelegramToken": "Você pode pegar o token de {0}.", + "chatIDNotFound": "Chat ID não encontrado; por favor envia uma mensagem para o bot primeiro", + "Chat ID": "Chat ID", + "Docker Hosts": "Hosts Do Docker", + "Docker Host": "Host Do Docker", + "Examples": "Exemplos", + "maintenanceStatus-under-maintenance": "Em Manutenção", + "Long-Lived Access Token": "Token De Acesso De Longa Duração", + "Home Assistant URL": "URL Do Home Assinant", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "O token de acessos de longa duração pode ser criado clicando no nome do seu perfil, com o botão esquerdo, ir até o final da lista e clicar em Criar Token.", + "Event type:": "Tipo Do Evento:", + "Event data:": "Dados Do Evento:", + "Frontend Version": "Versão Do Frontend", + "backupRecommend": "Por favor faça uma cópia do volume ou da pasta com dados(./data/) diretamente ao invés.", + "Optional": "Opcional", + "recurringInterval": "Intervalo", + "Recurring": "Recorrente", + "pauseMaintenanceMsg": "Você tem certeza que quer pausar?", + "maintenanceStatus-inactive": "Inativo", + "maintenanceStatus-scheduled": "Agendado", + "maintenanceStatus-ended": "Terminando", + "maintenanceStatus-unknown": "Desconhecido", + "enableGRPCTls": "Permita para enviar requisições gRPC com conexões TLS", + "confirmDeleteTagMsg": "Você tem certeza que deseja apagar essa tag? Monitores associados a essa tag não serão apagados.", + "grpcMethodDescription": "O nome do método é convertido para o formato camelCase, exemplos: sayHello, check, etc.", + "infiniteRetention": "Defina como 0 para um tempo infinito de retenção.", + "octopushLegacyHint": "Você usa a versão legada do Octopush (2011-2020) ou a nova versão?", + "Example:": "Exemplo: {0}", + "Read more:": "Leia mais em: {0}", + "promosmsAllowLongSMS": "Permitir SMS grandes", + "Huawei": "Huawei", + "smseagleTo": "Números Dos Telefones", + "smseaglePriority": "Prioridade da mensagem (0-9, prioridade mais alta = 9)", + "dataRetentionTimeError": "O período de retenção tem que ser maior ou igual a 0", + "User Key": "Chave Do Usuário", + "Device": "Dispositivo", + "Message Title": "Título Da Mensagem", + "defaultNotificationName": "Minha {notification} Alerta({number})", + "light": "claro", + "socket": "Soquete", + "Add New Tag": "Adicionar Nova Tag", + "API Username": "Usuário Da API", + "API Key": "Chave Da API", + "Show update if available": "Mostrar atualização se disponível", + "Also check beta release": "Também verificar lançamentos em beta", + "Using a Reverse Proxy?": "Está usando um Proxy Reverso?", + "Check how to config it for WebSocket": "Verifique como configurar para o WebSocket", + "Steam Game Server": "Servidor De Jogo Da Steam", + "Most likely causes:": "Causas mais prováveis:", + "What you can try:": "O que você pode tentar:", + "apiKey-active": "Ativa", + "Expiry": "Expiração", + "endpoint": "endpoint (URL Serviço)", + "pagertreeIntegrationUrl": "URL de Integração", + "pagertreeUrgency": "Urgência", + "telegramMessageThreadID": "(Opcional) ID da thread", + "Edit Tag": "Editar Etiqueta", + "Server Address": "Endereço do Servidor", + "Learn More": "Aprender Mais", + "needSignalAPI": "Você precisa ter um cliente Signal com uma API REST.", + "Generate": "Gerar", + "deleteAPIKeyMsg": "Você tem certeza de que quer apagar essa chave de API?", + "plugin": "Plugin | Plugins", + "Expiry date": "Data de expiração", + "Don't expire": "Não expira", + "Continue": "Continuar", + "Add Another": "Adicionar Outro", + "Key Added": "Chave Adicionada", + "Add API Key": "Adicionar chave de API", + "No API Keys": "Sem chaves de API", + "apiKey-expired": "Expirada", + "apiKey-inactive": "Inativa", + "Expires": "Expira", + "disableAPIKeyMsg": "Você tem certeza de que quer desativar essa chave de API?", + "smtp": "Email (SMTP)", + "secureOptionTLS": "TLS (465)", + "From Email": "Remetente", + "smtpCC": "CC", + "smtpBCC": "BCC", + "To Email": "Destinatário", + "Recipients": "Destinatários", + "Google Analytics ID": "ID Google Analytics", + "Post": "Post", + "Slug": "Apelido", + "The slug is already taken. Please choose another slug.": "Este apelido já está em uso. Por favor escolha outro apelido.", + "Setup Docker Host": "Configurar um host Docker", + "trustProxyDescription": "Confiar nos cabeçalhos 'X-Forwarded-*'. Se você quer obter o endereço IP correto do cliente mas seu Uptime Kuma está atrás de um proxy como Nginx ou Apache, você deve habilitar isso.", + "Automations can optionally be triggered in Home Assistant:": "Automações podem ser acionadas opcionalmente no Home Assistant:", + "secureOptionNone": "Nenhum / STARTTLS (25, 587)", + "apiKeyAddedMsg": "Sua chave de API foi adicionada. Por favor anote essa chave, ela não será mostrada novamente.", + "Show Clickable Link": "Mostrar Link Clicável", + "backupOutdatedWarning": "Obsoleto: Já que muitos recursos foram adicionados e este recurso de backup não foi atualizado, ele não consegue gerar ou restaurar um backup completo.", + "wayToGetDiscordURL": "Voce pode configurar isso indo à Configurações do Servidor -> Integrações -> Ver Webhooks -> Novo Webhook", + "Home": "Início", + "Reconnecting...": "Reconectando...", + "Cannot connect to the socket server": "Não foi possível conectar ao servidor socket", + "Uptime Kuma URL": "URL do Uptime Kuma", + "Saved.": "Salvo.", + "Feishu WebHookUrl": "URL de Webhook do Feishu", + "serwersmsAPIUser": "Nome de usuário da API (incluindo o prefixo webapi_)", + "setupDatabaseEmbeddedMariaDB": "Você não precisa definir nada. Esta imagem Docker incorporou e configurou um MariaDB para você automaticamente. Uptime Kuma se conectará a este banco de dados via soquete Unix.", + "Select": "Selecione", + "supportTelegramChatID": "Suporte para o ID de bate-papo direto / grupo / canal do chat", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Uma lista de Serviços de Notificação pode ser encontrada no Home Assistant em \"Ferramentas de Desenvolvimento > Serviços\". Pesquise por \"notificação\" para encontrar o nome do seu dispositivo/telefone.", + "chromeExecutableAutoDetect": "Auto Detectar", + "chromeExecutableDescription": "Para os usuários do Docker, se o Chromium ainda não estiver instalado, pode demorar alguns minutos para instalar e exibir o resultado do teste. Ele ocupa 1GB de espaço em disco.", + "wayToCheckSignalURL": "Você pode checar esse link para ver como configurar um:", + "wayToGetLineChannelToken": "Primeiro acesse o {0}, crie um provedor e um canal (API de Mensagens), então você pode obter o token de acesso do canal e o ID do usuário nos itens de menu mencionados acima.", + "aboutMattermostChannelName": "Você pode substituir o canal padrão para o qual o Webhook envia postagens, inserindo o nome do canal no campo \"Nome do Canal\". Isso precisa ser habilitado nas configurações do Webhook do Mattermost. Por exemplo: #outro-canal", + "invertKeywordDescription": "Procure pela palavra-chave estar ausente em vez de presente.", + "octopushTypePremium": "Premium (Rápido - recomendado para alertas)", + "octopushTypeLowCost": "Baixo Custo (Lento - às vezes bloqueado pelo operador)", + "octopushSMSSender": "Nome do Remetente de SMS: 3-11 caracteres alfanuméricos e espaço (a-zA-Z0-9)", + "pushoversounds pianobar": "Gráfico em barras de piano", + "SendKey": "\"SendKey\" é uma palavra usada para notificação do ServerChan", + "goAlertInfo": "GoAlert é uma aplicação de código aberto para escalas de plantão, escalonamentos automatizados e notificações (como SMS ou chamadas de voz). Engage automaticamente a pessoa certa, da maneira certa e no momento certo! {0}", + "promosmsTypeFlash": "SMS FLASH - A mensagem será exibida automaticamente no dispositivo do destinatário. Limitado apenas aos destinatários poloneses.", + "promosmsTypeSpeed": "SMS SPEED - Maior prioridade no sistema. Muito rápido e confiável, mas custoso (cerca de duas vezes o preço do SMS FULL).", + "matrixDesc1": "Você pode encontrar o ID da sala interna olhando na seção avançada das configurações da sala em seu cliente Matrix. Deve parecer algo como !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "É altamente recomendado que você crie um novo usuário e não use o token de acesso do seu próprio usuário Matrix, pois isso permitirá acesso total à sua conta e todas as salas às quais você se juntou. Em vez disso, crie um novo usuário e convide-o apenas para a sala na qual você deseja receber a notificação. Você pode obter o token de acesso executando {0}", + "aboutChannelName": "Digite o nome do canal no campo Nome do Canal em {0} se você deseja ignorar o canal do Webhook. Exemplo: #outro-canal", + "wayToGetPagerDutyKey": "Você pode obter isso indo para Serviço -> Diretório de Serviço -> (Selecionar um serviço) -> Integrações -> Adicionar integração. Aqui você pode procurar por \"Events API V2\". Mais informações {0}", + "From Name/Number": "Nome/Número de Origem", + "Server URL should not contain the nfty topic": "A URL do servidor não deve conter o tópico do nfty", + "pushDeerServerDescription": "Deixe em branco para usar o servidor oficial", + "wayToGetPagerTreeIntegrationURL": "Após criar a integração do Uptime Kuma no PagerTree, copie o Endpoint. Veja detalhes completos {0}", + "setupDatabaseChooseDatabase": "Qual banco de dados você deseja usar?", + "setupDatabaseMariaDB": "Conectar a um banco de dados MariaDB externo. Você precisa definir as informações de conexão com o banco de dados.", + "setupDatabaseSQLite": "Um arquivo de banco de dados simples, recomendado para implantações de pequena escala. Antes da versão 2.0.0, o Uptime Kuma usava o SQLite como banco de dados padrão.", + "dbName": "Nome do Banco de Dados", + "styleElapsedTimeShowNoLine": "Mostrar (Sem Linha)", + "styleElapsedTimeShowWithLine": "Mostrar (Com Linha)", + "filterActive": "Ativo", + "filterActivePaused": "Pausado", + "selectedMonitorCount": "Selecione: {0}", + "enableNSCD": "Habilitar o NSCD (Name Service Cache Daemon) para o cache de todas as solicitações DNS", + "chromeExecutable": "Executável do Chrome/Chromium", + "Edit Maintenance": "Editar Manutenção", + "aboutIconURL": "Você pode fornecer um link para uma imagem em \"URL do Ícone\" para substituir a imagem de perfil padrão. Não será usado se o \"Ícone Emoji\" estiver configurado.", + "octopushAPIKey": "\"Chave de API\" das credenciais da API HTTP no painel de controle", + "octopushLogin": "\"Login\" das credenciais da API HTTP no painel de controle", + "pushoversounds pushover": "Pushover (padrão)", + "pushoversounds bike": "Bicicleta", + "pushoversounds bugle": "Corneta", + "pushoversounds cashregister": "Caixa registradora", + "pushoversounds classical": "Clássico", + "pushoversounds cosmic": "Cósmico", + "pushoversounds falling": "Cair", + "pushoversounds gamelan": "Gamelão", + "pushoversounds incoming": "Entrada", + "checkPrice": "Verifique os preços de {0}:", + "Check octopush prices": "Verifique os preços da octopush {0}.", + "octopushPhoneNumber": "Número de telefone (formato internacional, por exemplo: +33612345678)", + "LunaSea Device ID": "ID do Dispositivo LunaSea", + "Apprise URL": "URL do Apprise", + "Strategy": "Estratégia", + "Free Mobile User Identifier": "Identificador de Usuário Free Mobile", + "Free Mobile API Key": "Chave da API Free Mobile", + "Enable TLS": "Habilitar TLS", + "Proto Service Name": "Nome do Serviço Proto", + "Proto Method": "Método Proto", + "Proto Content": "Conteúdo Proto", + "Economy": "Economia", + "Lowcost": "Baixo Custo", + "high": "alto", + "pushoversounds intermission": "Intervalo", + "pushoversounds magic": "Mágico", + "pushoversounds mechanical": "Mecânico", + "pushoversounds siren": "Sirene", + "pushoversounds spacealarm": "Alarme Espacial", + "pushoversounds tugboat": "Rebocador", + "pushoversounds alien": "Alarme Alienígena (longo)", + "pushoversounds climb": "Subir (longo)", + "pushoversounds persistent": "Persistente (longo)", + "pushoversounds echo": "Eco pushover (longo)", + "pushoversounds updown": "Up Down (longo)", + "SMSManager API Docs": "Docs da API SMSManager", + "Gateway Type": "Tipo de gateway", + "You can divide numbers with": "Você pode dividir números com", + "Base URL": "URL base", + "goAlertIntegrationKeyInfo": "Obtenha a chave de integração genérica da API para o serviço neste formato \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\", geralmente o valor do parâmetro token da URL copiada.", + "AccessKeyId": "ID da Chave de Acesso", + "promosmsTypeFull": "SMS FULL - Nível premium de SMS, você pode usar o seu Nome do Remetente (é necessário registrar o nome primeiro). Confiável para alertas.", + "promosmsPhoneNumber": "Número de telefone (para destinatários poloneses, você pode pular os códigos de área)", + "promosmsSMSSender": "Nome do Remetente de SMS: Nome pré-registrado ou um dos padrões: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "matrixHomeserverURL": "URL do Servidor (com http(s):// e opcionalmente a porta)", + "Notify Channel": "Canal de Notificação", + "aboutNotifyChannel": "O canal de notificação acionará uma notificação na área de trabalho ou no dispositivo móvel para todos os membros do canal, independentemente de sua disponibilidade estar definida como ativa ou ausente.", + "signalImportant": "IMPORTANTE: Você não pode misturar grupos e números nos destinatários!", + "aboutKumaURL": "Se você deixar o campo URL do Uptime Kuma em branco, ele será definido como padrão para a página do GitHub do projeto.", + "smtpDkimDesc": "Por favor, consulte o DKIM do Nodemailer em {0} para obter instruções de uso.", + "Auto resolve or acknowledged": "Auto resolva ou reconhecido", + "auto acknowledged": "reconhecimento automático", + "auto resolve": "resolução automática", + "alertaApiEndpoint": "Endpoint da API", + "serwersmsSenderName": "Nome do Remetente de SMS (registrado via portal do cliente)", + "smseagleGroup": "Nome(s) do grupo(s) da agenda telefônica", + "smseagleContact": "Nome(s) do(s) contato(s) da agenda telefônica", + "smseagleRecipientType": "Tipo de destinatário", + "smseagleRecipient": "Destinatário(s) (múltiplos devem ser separados por vírgula)", + "smseagleUrl": "URL do seu dispositivo SMSEagle", + "Recipient Number": "Número do Destinatário", + "Leave blank to use a shared sender number.": "Deixe em branco para usar um número de remetente compartilhado.", + "Octopush API Version": "Versão da API Octopush", + "Legacy Octopush-DM": "Octopush-DM Legado", + "ntfy Topic": "Tópico do ntfy", + "onebotHttpAddress": "Endereço HTTP do OneBot", + "onebotMessageType": "Tipo de Mensagem do OneBot", + "PushDeer Server": "Servidor PushDeer", + "PushDeer Key": "Chave PushDeer", + "wayToGetClickSendSMSToken": "Você pode obter o Nome de Usuário da API e a Chave da API aqui {here}.", + "Custom Monitor Type": "Tipo de Monitor Personalizado", + "Body Encoding": "Codificação do Corpo", + "API Keys": "Chaves da API", + "pagertreeSilent": "Silencioso", + "pagertreeLow": "Baixo", + "pagertreeMedium": "Médio", + "pagertreeHigh": "Alto", + "pagertreeCritical": "Crítico", + "pagertreeResolve": "Resolução Automática", + "pagertreeDoNothing": "Não fazer nada", + "lunaseaTarget": "Alvo", + "lunaseaDeviceID": "ID do Dispositivo", + "lunaseaUserID": "ID do Usuário", + "ntfyAuthenticationMethod": "Método de Autenticação", + "ntfyUsernameAndPassword": "Usuário e Senha", + "twilioAccountSID": "SID da Conta", + "twilioApiKey": "Chave da API (opcional)", + "twilioAuthToken": "Token de Autenticação / Segredo da Chave da API", + "twilioFromNumber": "Número de origem", + "twilioToNumber": "Número de destino", + "Monitor Setting": "Configuração do monitor de {0}", + "Show Clickable Link Description": "Se marcada, todos que têm acesso a esta página de status podem ter acesso ao URL do monitor.", + "Group": "Grupo", + "Monitor Group": "Grupo do Monitor", + "Request Timeout": "Tempo esgotado da requisição", + "timeoutAfter": "Tempo esgotado após {0} segundos", + "webhookBodyPresetOption": "Predefinição - {0}", + "webhookBodyCustomOption": "Corpo Customizado", + "Check/Uncheck": "Marcar/Desmarcar", + "tailscalePingWarning": "Para usar o monitor Tailscale Ping, você precisa instalar o Uptime Kuma sem o Docker e também instalar o cliente Tailscale em seu servidor.", + "telegramMessageThreadIDDescription": "Identificador único opcional para o tópico da mensagem alvo do fórum; apenas para supergrupos de fóruns", + "pushoversounds none": "Nenhum (Silencioso)", + "pushyAPIKey": "Chave Secreta da API", + "pushyToken": "Token do Dispositivo", + "GoogleChat": "Chat Google (Apenas para Google Workspace)", + "wayToGetKookGuildID": "Ative o 'Modo Desenvolvedor' nas configurações do Kook e clique com o botão direito do mouse no servidor para obter seu ID", + "Guild ID": "ID do Servidor (Guild)", + "pushoverDesc1": "A prioridade de emergência (2) possui um intervalo de 30 segundos entre as tentativas padrão e expirará após 1 hora.", + "pushoverDesc2": "Se você deseja enviar notificações para diferentes dispositivos, preencha o campo \"Dispositivo\".", + "pushoverMessageTtl": "Tempo de Vida da Mensagem (Segundos)", + "pushoversounds vibrate": "Somente Vibrar", + "SecretAccessKey": "Segredo da Chave de Acesso", + "PhoneNumbers": "Números de Telefone", + "TemplateCode": "Código de Modelo", + "SignName": "Nome de Assinatura", + "Sms template must contain parameters: ": "O modelo de SMS deve conter parâmetros: ", + "Bark Endpoint": "Endpoint do Bark", + "Bark Group": "Grupo do Bark", + "Bark Sound": "Som do Bark", + "WebHookUrl": "URL de Webhook", + "SecretKey": "Chave Secreta", + "High": "Alto", + "WeCom Bot Key": "Chave do Bot do WeCom", + "promosmsTypeEco": "SMS ECO - barato, mas lento e frequentemente sobrecarregado. Limitado apenas aos destinatários poloneses.", + "styleElapsedTime": "Tempo decorrido na barra de heartbeat", + "Expected Value": "Valor Esperado", + "webhookCustomBodyDesc": "Defina um corpo HTTP personalizado para a solicitação. Variáveis de modelo {msg}, {heartbeat}, {monitor} são aceitas.", + "Invert Keyword": "Palavra-chave de Inversão", + "Json Query": "Consulta JSON", + "toastErrorTimeout": "Tempo limite para Notificações de Erro", + "toastSuccessTimeout": "Tempo limite para Notificações de Sucesso", + "monitorToastMessagesLabel": "Monitorar notificações Toast", + "monitorToastMessagesDescription": "As notificações Toast para monitores desaparecem após um determinado tempo em segundos. Definir como -1 desativa o tempo limite. Definir como 0 desativa as notificações Toast.", + "Open Badge Generator": "Gerador de Distintivo", + "Badge Label Color": "Cor do Nome do Distintivo", + "Badge Color": "Cor do Distintivo", + "Badge Label Prefix": "Prefixo do Nome do Distintivo", + "Badge Preview": "Prévia do Distintivo", + "Badge Label Suffix": "Sufixo do Nome do Distintivo", + "Badge Up Color": "Cor de Cima do Distintivo", + "Badge Down Color": "Cor de Baixo do Distintivo", + "Badge Pending Color": "Cor do Distintivo Pendente", + "Badge Maintenance Color": "Cor do Distintivo Em Manutenção", + "Badge Warn Color": "Cor do Distintivo de Aviso", + "Badge Warn Days": "Dias de Aviso do Distintivo", + "Badge Down Days": "Dias Desligado do Distintivo", + "Badge Style": "Estilo do Distintivo", + "Badge value (For Testing only.)": "Valor do Selo (Apenas para Testes.)", + "Badge URL": "URL do Distintivo", + "Badge Generator": "Gerador de Distintivo de {0}", + "Badge Type": "Tipo de Distintivo", + "Badge Duration (in hours)": "Duração do Distintivo (em horas)", + "Badge Label": "Nome do Distintivo", + "Badge Prefix": "Prefixo do Valor do Distintivo", + "Badge Suffix": "Sufixo do Valor do Distintivo", + "Kafka SASL Options": "Opções Kafka SASL", + "Reset Token": "Redefinir Token", + "Kafka Brokers": "Kafka Brokers", + "Kafka Producer Message": "Mensagem do produtor Kafka", + "pushViewCode": "Como Utilizar o Monitor de Push ( Visualizar Codigo)", + "Enter the list of brokers": "Entre a lista de brokers", + "Press Enter to add broker": "Pressione Enter para adicionar broker", + "Kafka Topic Name": "Nome do tópico Kafka", + "Enable Kafka SSL": "Habilitar Kafka SSL", + "Enable Kafka Producer Auto Topic Creation": "Ativar a criação automática de tópicos do Kafka Producer", + "Mechanism": "Mecanismo", + "Pick a SASL Mechanism...": "Escolha um mecanismo SASL…", + "Bark API Version": "Versão da API Bark", + "pushOthers": "Outros", + "programmingLanguages": "Linguagens de Programação", + "liquidIntroduction": "A possibilidade de criação de modelos é alcançada através da linguagem de modelagem Liquid. Consulte o {0} para obter instruções de uso.", + "Secret AccessKey": "Chave de acesso secreta", + "Authorization Identity": "Identidade de autorização", + "emailCustomisableContent": "Conteúdo personalizável", + "smtpLiquidIntroduction": "Os dois campos a seguir podem ser modelados por meio da linguagem de modelagem Liquid. Consulte {0} para obter instruções de uso. Estas são as variáveis disponíveis:", + "leave blank for default subject": "deixe em branco para o assunto padrão", + "emailCustomBody": "Corpo personalizado", + "leave blank for default body": "deixe em branco para o corpo padrão", + "emailTemplateServiceName": "Nome do Serviço", + "emailTemplateHostnameOrURL": "Nome do host ou URL", + "emailTemplateStatus": "Status", + "emailTemplateMonitorJSON": "objeto que descreve o monitor", + "emailTemplateHeartbeatJSON": "objeto que descreve o batimento cardíaco", + "emailTemplateMsg": "mensagem da notificação", + "emailTemplateLimitedToUpDownNotification": "disponível apenas para pulsações UP/DOWN, caso contrário, nulo", + "wayToGetFlashDutyKey": "Para integrar o Uptime Kuma com o Flashduty: Acesse Canais > Selecionar um canal > Integrações > Adicionar uma nova integração, escolha Uptime Kuma e copie o URL de push.", + "FlashDuty Severity": "Gravidade", + "templateMsg": "mensagem da notificação", + "templateHeartbeatJSON": "objeto que descreve o batimento cardíaco", + "templateMonitorJSON": "objeto que descreve o monitor", + "templateLimitedToUpDownCertNotifications": "disponível apenas para notificações UP/DOWN/expiração de certificado", + "templateLimitedToUpDownNotifications": "disponível apenas para notificações UP/DOWN", + "Session Token": "Token de sessão", + "noGroupMonitorMsg": "Não disponível. Crie primeiro um monitor de grupo.", + "Close": "Fechar", + "Request Body": "Corpo da Solicitação", + "AccessKey Id": "ID da chave de acesso", + "nostrRelays": "Repetidor Nostr", + "nostrRecipientsHelp": "formato npub, um por linha", + "gamedigGuessPortDescription": "A porta usada pelo Valve Server Query Protocol pode ser diferente da porta do cliente. Tente fazer isso se o monitor não conseguir se conectar ao seu servidor.", + "authUserInactiveOrDeleted": "O usuário está inativo ou excluído.", + "authInvalidToken": "Token inválido.", + "authIncorrectCreds": "Usuário ou senha incorretos.", + "2faAlreadyEnabled": "2FA já está ativado.", + "2faEnabled": "2FA habilitado.", + "2faDisabled": "2FA desativado.", + "nostrSender": "Chave privada do remetente (nsec)", + "nostrRecipients": "Chaves Públicas dos Destinatários (npub)", + "GrafanaOncallUrl": "URL do Grafana Oncall", + "noDockerHostMsg": "Não disponível. Configure primeiro um Host Docker.", + "DockerHostRequired": "Defina o Docker Host para este monitor.", + "nostrRelaysHelp": "Um URL de retransmissão por linha", + "showCertificateExpiry": "Mostrar expiração do certificado", + "noOrBadCertificate": "Certificado Ruim/Ausente", + "successAdded": "Adicionado com sucesso.", + "successResumed": "Reiniciado com sucesso.", + "successPaused": "Pausado com sucesso.", + "successDeleted": "Apagado com sucesso.", + "successEdited": "Editado com sucesso.", + "successAuthChangePassword": "A senha foi atualizada com sucesso.", + "successBackupRestored": "Backup restaurado com sucesso.", + "successDisabled": "Desativado com sucesso.", + "successEnabled": "Habilitado com sucesso.", + "tagNotFound": "Etiqueta não encontrada.", + "gamedigGuessPort": "Gamedig: Adivinhar Porta", + "foundChromiumVersion": "Chromium/Chrome encontrado. Versão: {0}", + "successKeywordExplanation": "Palavra-chave MQTT que será considerada bem-sucedida", + "Add a new expiry notification day": "Adicione um novo dia de notificação de expiração", + "Remove the expiry notification": "Remova o dia de notificação de expiração", + "setup a new monitor group": "Configure um novo grupo de monitoramento", + "successKeyword": "Palavra-chave de Sucesso", + "Browser Screenshot": "Captura de tela do navegador", + "Remote Browsers": "Navegadores Remotos", + "Remote Browser": "Navegador Remoto", + "Add a Remote Browser": "Adicione um Navegador Remoto", + "Remote Browser not found!": "Navegador Remoto não encontrado!", + "remoteBrowsersDescription": "Os navegadores remotos são uma alternativa para executar o Chromium localmente. Configure com um serviço como browserless.io ou conecte-se ao seu próprio", + "self-hosted container": "Contêiner auto-hospedado", + "remoteBrowserToggle": "Por padrão, o Chromium é executado dentro do contêiner Uptime Kuma. Você pode usar um navegador remoto diferente, substituindo esse.", + "useRemoteBrowser": "Use um navegador remoto", + "deleteRemoteBrowserMessage": "Tem certeza de que deseja excluir este Navegador Remoto para todos os monitores?", + "Add a domain": "Adicione um domínio", + "Remove domain": "Remover domínio '{0}'", + "openModalTo": "Abrir modal para {0}", + "ntfyPriorityHelptextAllExceptDown": "Todos os eventos são enviados com esta prioridade, exceto {0}-events, que têm prioridade de {1}", + "statusPageSpecialSlugDesc": "Slug especial {0}: esta página será mostrada quando nenhum slug for fornecido", + "settingUpDatabaseMSG": "Configurando o banco de dados. Pode demorar um pouco, por favor, seja paciente.", + "Search monitored sites": "Pesquisar sites monitorados", + "ntfyPriorityHelptextAllEvents": "Todos os eventos são enviados com prioridade máxima", + "What is a Remote Browser?": "O que é um Navegador Remoto ?", + "wayToGetHeiiOnCallDetails": "Como obter o ID do Trigger e as chaves de API é explicado na {documentação}", + "gtxMessagingApiKeyHint": "Você pode encontrar sua chave de API em: Minhas contas de roteamento > Mostrar informações da conta > Credenciais de API > API REST (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Do número de telefone/endereço de origem do caminho de transmissão (TPOA)", + "To Phone Number": "Para número de telefone", + "gtxMessagingToHint": "Formato internacional, com \"+\" inicial ({e164}, {e212} ou {e214})", + "Originator type": "Tipo de originador", + "Alphanumeric (recommended)": "Alfanumérico (recomendado)", + "Telephone number": "Número de telefone", + "cellsyntOriginatortypeAlphanumeric": "String alfanumérica (máximo de 11 caracteres alfanuméricos). Os destinatários não podem responder à mensagem.", + "cellsyntOriginatortypeNumeric": "Valor numérico (máximo de 15 dígitos) com número de telefone em formato internacional sem 00 inicial (por exemplo, o número do Reino Unido 07920 110 000 deve ser definido como 447920110000). Os destinatários podem responder à mensagem.", + "Originator": "Originador", + "Destination": "Destino", + "callMeBotGet": "Aqui você pode gerar um endpoint para {0}, {1} e {2}. Lembre-se de que você pode barrado por uma taxa limitadora. Os limites de taxa parecem ser: {3}", + "cellsyntOriginator": "Visível no celular do destinatário como originador da mensagem. Os valores permitidos e a função dependem do tipo de originador do parâmetro.", + "wayToGetWhapiUrlAndToken": "Você pode obter o URL da API e o token acessando o canal desejado em {0}", + "whapiRecipient": "Número de telefone/ID do contato/ID do grupo", + "API URL": "API URL", + "documentationOf": "{0} Documentação", + "Allow Long SMS": "Permitir SMS longos", + "cellsyntSplitLongMessages": "Divida mensagens longas em até 6 partes. 153 x 6 = 918 caracteres.", + "max 15 digits": "máximo de 15 dígitos", + "max 11 alphanumeric characters": "máximo de 11 caracteres alfanuméricos", + "wayToWriteWhapiRecipient": "O número de telefone com o prefixo internacional, mas sem o sinal de mais no início ({0}), o ID do contato ({1}) ou o ID do grupo ({2}).", + "cellsyntDestination": "Número de telefone do destinatário em formato internacional com 00 inicial seguido do código do país, por ex. 00447920110000 para o número do Reino Unido 07920 110 000 (máximo de 17 dígitos no total). Máximo de 25.000 destinatários separados por vírgula por solicitação HTTP.", + "Your User ID": "Sua ID de usuário", + "gtxMessagingFromHint": "Em telefones celulares, seus destinatários veem o TPOA exibido como remetente da mensagem. São permitidos até 11 caracteres alfanuméricos, um DDD, o DDI local ou números internacionais ({e164}, {e212} ou {e214})", + "Channel access token (Long-lived)": "Token de acesso ao canal (de longa duração)", + "apiKeySevenIO": "SevenIO chave de API", + "wayToGetThreemaGateway": "Você pode se registrar no Threema Gateway {0}.", + "threemaRecipient": "Destinatário", + "threemaRecipientType": "Tipo de destinatário", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypeIdentityFormat": "8 caracteres", + "threemaRecipientTypePhone": "Número de telefone", + "threemaRecipientTypeEmail": "Endereço de email", + "threemaSenderIdentity": "ID do Gateway", + "threemaSenderIdentityFormat": "8 caracteres, geralmente começa com *", + "Command": "Comando", + "mongodbCommandDescription": "Execute um comando MongoDB no banco de dados. Para informações sobre os comandos disponíveis confira a {documentação}", + "smspartnerApiurl": "Você pode encontrar sua chave de API em seu painel em {0}", + "smspartnerPhoneNumber": "Números de telefone)", + "smspartnerPhoneNumberHelptext": "O número deve estar no formato internacional {0}, {1}. Vários números devem ser separados por {2}", + "smspartnerSenderName": "Nome do remetente de SMS", + "smspartnerSenderNameInfo": "Deve ter entre 3..=11 caracteres regulares", + "wayToGetBitrix24Webhook": "Você pode criar um webhook seguindo as etapas em {0}", + "Mentioning": "Mencionando", + "Don't mention people": "Não mencione pessoas", + "Mention group": "Mencionar {grupo}", + "senderSevenIO": "Enviando número ou nome", + "receiverSevenIO": "Número de recebimento", + "wayToGetSevenIOApiKey": "Visite o painel em app.seven.io > desenvolvedor > chave de API > botão verde adicionar", + "Select message type": "Selecione o tipo de mensagem", + "Send to channel": "Enviar para o canal", + "Create new forum post": "Criar nova postagem no fórum", + "threadForumPostID": "ID da postagem do tópico/fórum", + "postToExistingThread": "Postar em tópico/postagem existente", + "forumPostName": "Nome da postagem no fórum", + "Host URL": "Host URL", + "Refresh Interval": "Intervalo de atualização", + "Refresh Interval Description": "A página de status fará uma atualização completa do site a cada {0} segundos", + "locally configured mail transfer agent": "configurado de agente de transferência de e-mail localmente", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Insira o nome do host do servidor ao qual deseja se conectar, ou {localhost} se pretende usar um {local_mta}", + "ignoreTLSErrorGeneral": "Ignorar erro TLS/SSL para conexão", + "threemaRecipientTypePhoneFormat": "E.164, sem o sinal de \"+\" no início", + "threemaApiAuthenticationSecret": "ID secreto do Gateway", + "threemaBasicModeInfo": "Observação: Esta integração usa o Threema Gateway no modo básico (criptografia baseada em servidor). Mais detalhes podem ser encontrados em {0}.", + "bitrix24SupportUserID": "Insira seu ID de usuário no Bitrix24. Você pode encontrar o ID no link ao acessar o perfil do usuário.", + "Bitrix24 Webhook URL": "URL do Webhook do Bitrix24", + "wayToGetDiscordThreadId": "Obter o ID de uma thread/postagem em um fórum é similar a obter o ID de um canal. Saiba mais sobre como obter IDs {0}", + "whatHappensAtForumPost": "Criar um novo tópico no fórum. Isto NÃO publica mensagens em tópicos existentes. Para publicar em um tópico existente, use \"{option}\"", + "e.g. {discordThreadID}": "Por exemplo {discordThreadID}", + "receiverInfoSevenIO": "Se o número de destino não estiver localizado na Alemanha, você deve adicionar o código do país antes do número (por exemplo, para o código de país 1 dos EUA, use 117612121212 em vez de 017612121212)", + "apiKeysDisabledMsg": "As chaves de API estão desativadas porque a autenticação está desativada.", + "now": "agora", + "and": "e", + "jsonQueryDescription": "Analise e extraia dados específicos da resposta JSON do servidor usando uma query JSON ou use \"$\" para a resposta em raw, se não estiver esperando JSON. O resultado é então comparado ao valor esperado, como strings. Veja {0} para documentação e use {1} para experimentar com queries.", + "time ago": "atrás", + "-year": "-ano", + "Json Query Expression": "Expressão Json Query", + "cacheBusterParam": "Adicionar o parâmetro {0}", + "snmpCommunityStringHelptext": "Esta string funciona como uma senha para autenticar e controlar o acesso a dispositivos habilitados para SNMP. Combine-a com a configuração do seu dispositivo SNMP.", + "OID (Object Identifier)": "OID (Identificador do Objeto)", + "Condition": "Condição", + "Recipient Type": "Tipo de destinatário", + "wayToGetOnesenderUrlandToken": "Você pode obter a URL e o Token acessando o site da Onesender. Mais informações {0}", + "cacheBusterParamDescription": "Parâmetro gerado randomicamente para ignorar cache.", + "SNMP Version": "Versão SNMP", + "Please enter a valid OID.": "Por favor, insira um OID válido.", + "Private Number": "Número Privado", + "Group ID": "ID do Grupo", + "Add Remote Browser": "Adicionar Navegador Remoto", + "New Group": "Novo Grupo", + "Group Name": "Nome do Grupo", + "OAuth2: Client Credentials": "OAuth2: Credenciais do Cliente", + "Authentication Method": "Método de Autenticação", + "Authorization Header": "Header de Autorização", + "ignoredTLSError": "Erros TLS/SSL foram ignorados", + "Debug": "Depurar", + "Copy": "Copiar", + "CopyToClipboardError": "Não foi possível copiar para a área de transferência: {error}", + "CopyToClipboardSuccess": "Copiado!", + "firewalls": "firewalls", + "docker networks": "redes docker", + "Message format": "Formato da mensagem", + "snmpOIDHelptext": "Insira o OID do sensor ou do status que você deseja monitorar. Utilize ferramentas de gerenciamento de rede, como navegadores MIB ou softwares SNMP, se não tiver certeza sobre o OID.", + "privateOnesenderDesc": "Certifique-se de que o número de telefone é válido. Para enviar mensagem para o número de telefone privado, ex: 628123456789", + "aboutSlackUsername": "Altera o nome de exibição do remetente da mensagem. Se quiser mencionar alguém, inclua a menção no nome amigável.", + "Send rich messages": "Enviar mensagens ricas", + "Host Onesender": "Servidor Onesender", + "Token Onesender": "Token Onesender", + "shrinkDatabaseDescriptionSqlite": "Acionar banco de dados {vacuum} para SQLite. {auto_vacuum} já está habilitado, mas isso não desfragmenta o banco de dados nem reempacota páginas individuais do banco de dados da maneira que o comando {vacuum} faz.", + "groupOnesenderDesc": "Certifique-se de que o GroupID é válido. Para enviar mensagem para o Grupo, ex: 628123456789-342345", + "Community String": "Cadeia de caracteres da comunidade", + "Form Data Body": "Dados do formulário", + "OAuth Token URL": "URL do token OAuth", + "Client ID": "ID do cliente", + "Client Secret": "Segredo do cliente", + "OAuth Scope": "Escopo OAuth", + "Optional: Space separated list of scopes": "Opcional: Lista de escopos separados por espaços", + "Go back to home page.": "Voltar para a página inicial.", + "No tags found.": "Nenhuma tag encontrada.", + "Lost connection to the socket server.": "Conexão perdida com o servidor de socket.", + "Cannot connect to the socket server.": "Não é possível conectar ao servidor de socket.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "URL do Webhook SIGNL4", + "rabbitmqNodesRequired": "Por favor, defina os nós para este monitor.", + "RabbitMQ Nodes": "Nós de gerenciamento do RabbitMQ", + "rabbitmqNodesDescription": "Insira a URL para os nós de gerenciamento do RabbitMQ, incluindo protocolo e porta. Exemplo: {0}", + "Bubble": "Bolha", + "Clear": "Limpar", + "Custom sound to override default notification sound": "Som personalizado para substituir o som de notificação padrão", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Notificações urgentes serão entregues imediatamente, mesmo se o dispositivo estiver no modo Não perturbe.", + "rabbitmqHelpText": "Para usar o monitor, você precisará habilitar o Management Plugin na sua configuração RabbitMQ. Para mais informações, consulte a {rabitmq_documentation}.", + "Money": "Dinheiro", + "signl4Docs": "Você pode encontrar mais informações sobre como configurar o SIGNL4 e como obter o URL do webhook do SIGNL4 em {0}.", + "Conditions": "Condições", + "conditionAdd": "Adicionar Condição", + "conditionDelete": "Excluir condição", + "conditionAddGroup": "Adicionar grupo", + "conditionDeleteGroup": "Excluir grupo", + "conditionValuePlaceholder": "Valor", + "equals": "igual", + "not equals": "diferente", + "contains": "contém", + "not contains": "não contém", + "starts with": "começa com", + "not starts with": "não começa com", + "ends with": "termina com", + "not ends with": "não termina com", + "less than": "menor que", + "greater than": "maior que", + "less than or equal to": "menor ou igual a", + "greater than or equal to": "maior ou igual a", + "record": "registro", + "Notification Channel": "Canal de Notificação", + "Sound": "Som", + "Alphanumerical string and hyphens only": "Somente sequência alfanumérica e hifens", + "Arcade": "Arcada", + "Correct": "Correto", + "Fail": "Falhou", + "Harp": "Harpa", + "Reveal": "Revelar", + "Doorbell": "Campainha", + "Flute": "Flauta", + "Scifi": "Ficção científica", + "Elevator": "Elevador", + "Guitar": "Guitarra", + "Pop": "Pop", + "Time Sensitive (iOS Only)": "Sensível ao tempo (somente iOS)", + "From": "De", + "Can be found on:": "Pode ser encontrado em: {0}", + "The phone number of the recipient in E.164 format.": "O número de telefone do destinatário no formato E.164.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Um ID de remetente de texto ou um número de telefone no formato E.164, caso você queira receber respostas.", + "rabbitmqNodesInvalid": "Use uma URL totalmente qualificada (começando com 'http') para nós do RabbitMQ.", + "RabbitMQ Username": "Nome de usuário do RabbitMQ", + "RabbitMQ Password": "Senha do RabbitMQ", + "SendGrid API Key": "Chave API do SendGrid", + "Separate multiple email addresses with commas": "Separe vários endereços de e-mail com vírgulas", + "templateServiceName": "nome do serviço", + "telegramUseTemplate": "Use um template personalizado de mensagem", + "telegramTemplateFormatDescription": "O Telegram permite o uso de diferentes linguagens de marcação para mensagens. Veja o Telegram {0} para detalhes específicos.", + "templateHostnameOrURL": "hostname ou URL", + "templateStatus": "status", + "telegramUseTemplateDescription": "Se habilitado, a mensagem será enviada usando um template personalizado.", + "telegramServerUrlDescription": "Para suspender as limitações da API de bots do Telegram ou obter acesso em áreas bloqueadas (China, Irã, etc). Para mais informações, clique em {0}. Padrão: {1}", + "wahaSession": "Sessão", + "wayToGetWahaApiUrl": "URL da sua instância WAHA.", + "wayToGetWahaApiKey": "API Key é o valor da variável de ambiente WHATSAPP_API_KEY que você usou para executar o WAHA.", + "wayToGetWahaSession": "A partir desta sessão, o WAHA envia notificações para o Chat ID. Você pode encontrá-lo no WAHA Dashboard.", + "wayToWriteWahaChatId": "O número de telefone com o prefixo internacional, mas sem o sinal de mais no início ({0}), o Contact ID ({1}) ou o Group ID ({2}). As notificações são enviadas para este Chat ID da sessão WAHA.", + "Plain Text": "Texto Simples", + "wahaChatId": "Chat ID (Número de Telefone / Contact ID / Group ID)", + "YZJ Webhook URL": "URL do Webhook YZJ", + "YZJ Robot Token": "Token do Robô YZJ", + "telegramServerUrl": "(Opcional) URL do Servidor", + "Message Template": "Modelo de Mensagem", + "Template Format": "Formato do Modelo", + "Font Twemoji by Twitter licensed under": "Fonte Twemoji do Twitter licenciada sob", + "the smsplanet documentation": "a documentação do smsplanet", + "Phone numbers": "Números de telefone", + "Sender name": "Nome do remetente", + "smsplanetNeedToApproveName": "Precisa ser aprovado no painel do cliente", + "smsplanetApiToken": "Token para a API SMSPlanet", + "smsplanetApiDocs": "Informações detalhadas sobre a obtenção de tokens de API podem ser encontradas em {the_smsplanet_documentation}.", + "defaultFriendlyName": "Novo Monitor", + "Use HTML for custom E-mail body": "Use HTML para corpo de e-mail personalizado", + "smseagleGroupV2": "ID(s) do grupo da lista telefônica", + "smseagleTtsModel": "ID do modelo de conversão de texto em fala", + "smseagleDocs": "Verifique a documentação ou a disponibilidade da APIv2: {0}", + "pingCountDescription": "Número de pacotes a enviar antes de parar", + "pingGlobalTimeoutLabel": "Tempo limite global", + "pingGlobalTimeoutDescription": "Tempo total em segundos antes que o ping pare, independentemente dos pacotes enviados", + "pingIntervalAdjustedInfo": "Intervalo ajustado com base na contagem de pacotes, tempo limite global e tempo limite por ping", + "smtpHelpText": "'SMTPS' testa se o SMTP/TLS está funcionando; 'Ignorar TLS' conecta por texto simples; 'STARTTLS' conecta, emite um comando STARTTLS e verifica o certificado do servidor. Nenhum desses métodos envia um e-mail.", + "smseagleContactV2": "ID(s) de contato da lista telefônica", + "smseagleMsgType": "Tipo de mensagem", + "smseagleMsgSms": "Mensagem SMS (padrão)", + "smseagleMsgRing": "Chamada de toque", + "smseagleMsgTts": "Chamada de texto para fala", + "smseagleMsgTtsAdvanced": "Chamada avançada de conversão de texto em fala", + "smseagleDuration": "Duração (em segundos)", + "smseagleApiType": "Versão da API", + "smseagleApiv1": "APIv1 (para projetos existentes e compatibilidade com versões anteriores)", + "smseagleApiv2": "APIv2 (recomendado para novas integrações)", + "smseagleComma": "Múltiplos devem ser separados por vírgula", + "SpugPush Template Code": "Código do modelo", + "FlashDuty Push URL": "URL de envio", + "FlashDuty Push URL Placeholder": "Copiar da página de integração de alertas", + "pingCountLabel": "Máximo de Pacotes", + "pingNumericLabel": "Saída Numérica", + "pingNumericDescription": "Se marcado, os endereços IP serão emitidos em vez de nomes de host simbólicos", + "pingPerRequestTimeoutLabel": "Tempo limite por ping", + "pingPerRequestTimeoutDescription": "Este é o tempo máximo de espera (em segundos) antes de considerar um único pacote de ping perdido", + "Custom URL": "URL personalizada", + "customUrlDescription": "Será usado como URL clicável em vez do monitor.", + "OneChatAccessToken": "Token de acesso OneChat", + "OneChatUserIdOrGroupId": "ID de usuário ou ID de grupo do OneChat", + "OneChatBotId": "ID do bot OneChat", + "Disable URL in Notification": "Desativar URL na notificação", + "ntfyPriorityHelptextPriorityHigherThanDown": "A prioridade regular deve ser maior que {0} prioridade. Prioridade {1} é maior que {0} prioridade {2}", + "ntfyPriorityDown": "Prioridade para eventos DOWN", + "tagAlreadyOnMonitor": "Esta tag (nome e valor) já está no monitor ou na adição pendente.", + "tagNameExists": "Uma tag de sistema com este nome já existe. Selecione-o na lista ou use um nome diferente.", + "Add Tags": "Adicionar Tags", + "tagAlreadyStaged": "Esta tag (nome e valor) já está adicionada para este lote.", + "Add Another Tag": "Adicione Outra Tag", + "Staged Tags for Batch Add": "Tags preparadas para adição em lote", + "Clear Form": "Limpar formulário", + "pause": "Pausar", + "Happy Eyeballs algorithm": "Algoritmo Happy Eyeballs", + "Manual": "Manual", + "Ip Family": "Família IP", + "ipFamilyDescriptionAutoSelect": "Usa {happyEyeballs} para determinar a família IP.", + "Optional: The audience to request the JWT for": "Opcional: O público deve solicitar o JWT para", + "OAuth Audience": "Público OAuth", + "mqttWebSocketPath": "Caminho MQTT WebSocket", + "mqttWebsocketPathExplanation": "Caminho do WebSocket para conexões MQTT sobre WebSocket (por exemplo, /mqtt)", + "Path": "Caminho", + "mqttWebsocketPathInvalid": "Use um formato de caminho WebSocket válido", + "mqttHostnameTip": "Por favor, use este formato {hostnameFormat}", + "issueWithGoogleChatOnAndroidHelptext": "Isso também permite contornar bugs no upstream, como {issuetackerURL}", + "Template plain text instead of using cards": "Modelo de texto simples em vez de usar cartões", + "Clear All Events": "Limpar todos os eventos", + "Events cleared successfully": "Eventos limpos com sucesso.", + "No monitors found": "Nenhum monitor encontrado.", + "Could not clear events": "Não foi possível limpar {failed}/{total} eventos", + "clearAllEventsMsg": "Tem certeza de que deseja excluir todos os eventos de todos os monitoramentos? Isso removerá permanentemente os dados de mensagens associados e não pode ser desfeito.", + "wayToWriteEvolutionRecipient": "O número de telefone com o prefixo internacional, mas sem o sinal de mais no início ({0}), o ID de contato ({1}) ou o ID de grupo ({2}).", + "wayToGetEvolutionUrlAndToken": "Você pode obter a URL da API e o token acessando o canal desejado em {0}", + "evolutionRecipient": "Número de Telefone / ID de Contato / ID do Grupo", + "evolutionInstanceName": "Nome da Instância", + "brevoFromName": "De Nome", + "brevoToEmail": "Para Email", + "brevoSeparateMultipleEmails": "Separe vários endereços de email com vírgulas", + "brevoSubject": "Assunto", + "brevoLeaveBlankForDefaultSubject": "deixe em branco para o assunto padrão", + "brevoApiKey": "Chave de API Brevo", + "brevoApiHelp": "Crie uma chave de API aqui: {0}", + "brevoFromEmail": "De Email", + "brevoLeaveBlankForDefaultName": "deixe em branco para o nome padrão", + "brevoCcEmail": "CC Email", + "brevoBccEmail": "CCO Email", + "Nextcloud host": "Host Nextcloud", + "Conversation token": "Token de conversação", + "Bot secret": "Segredo do bot", + "Send UP silently": "Enviar UP silenciosamente", + "Send DOWN silently": "Envie DOWN silenciosamente", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "A instalação de um bot Nextcloud Talk requer acesso administrativo ao servidor.", + "auto-select": "Seleção automática", + "Mention Mobile List": "Mencionar lista de celulares", + "Enter a list of userId": "Insira uma lista de userId", + "supportBaleChatID": "Suporte a ID de Chat Direto / Grupo / Canal", + "wayToGetBaleChatID": "Você pode obter seu ID de bate-papo enviando uma mensagem ao bot e acessando esta URL para visualizar o chat_id:", + "wayToGetBaleToken": "Você pode obter um token de {0}.", + "Mention User List": "Mencionar lista de IDs de usuários", + "Dingtalk Mobile List": "Lista de celulares", + "Dingtalk User List": "Lista de IDs de usuário", + "Enter a list of mobile": "Insira uma lista de dispositivos móveis", + "Invalid mobile": "Celular inválido [{mobile}]", + "Invalid userId": "ID de usuário inválido [{userId}]", + "Number of retry attempts if webhook fails": "Número de tentativas de reenvio (a cada 60-180 segundos) caso o webhook falhe.", + "Maximum Retries": "Máximo de Tentativas", + "HTTP Method": "Método HTTP", + "webhookGetMethodDesc": "GET envia dados como parâmetros de consulta e não permite a configuração de um corpo. Útil para acionar monitores Uptime Kuma Push.", + "descriptionHelpText": "Exibido no painel interno. Markdown é permitido e sanitizado (preserva espaços e recuos) antes da exibição.", + "webhookPostMethodDesc": "O POST é bom para a maioria dos servidores HTTP modernos.", + "Template ID": "ID do modelo", + "wayToGetClickSMSIRTemplateID": "Seu modelo deve conter um campo {uptkumaalert}. Você pode criar um novo modelo {aqui}.", + "Recipient Numbers": "Números de destinatários", + "deleteGroupMsg": "Tem certeza de que deseja excluir este grupo?", + "deleteChildrenMonitors": "Exclua também os monitores filhos diretos e seus filhos, se houver | Exclua também todos os {count} monitores filhos diretos e seus filhos, se houver", + "twilioMessagingServiceSID": "SID do serviço de mensagens (opcional)", + "twilioApiKeyHelptext": "A chave de API é opcional, mas recomendada. Você pode fornecer o SID da conta e o token de autenticação da página do TwilioConsole ou o SID da conta e o par de chave de API e segredo da chave de API", + "twilloMessagingServiceSIDHelptext": "Insira aqui o SID do seu serviço de mensagens se estiver usando {twillo_messaging_service_help_link} para gerenciar remetentes e recursos", + "Clone Maintenance": "Clonar manutenção", + "ariaPauseMaintenance": "Pausar este agendamento de manutenção", + "ariaResumeMaintenance": "Retomar este agendamento de manutenção", + "ariaCloneMaintenance": "Criar uma cópia deste agendamento de manutenção", + "ariaEditMaintenance": "Editar este agendamento de manutenção", + "ariaDeleteMaintenance": "Excluir este agendamento de manutenção", + "showOnlyLastHeartbeat": "Mostrar apenas o último heartbeat", + "Notifications Enabled": "Notificações ativadas", + "Allow Notifications": "Permitir notificações", + "Web Process Control Protocol": "Protocolo de Controle de Processos Web (WPCP)", + "Collection Update": "O subprotocolo Websocket de atualização de coleção", + "Browser not supported": "Navegador não suportado", + "Unable to get permission to notify": "Não foi possível obter permissão para notificar (a solicitação foi negada ou ignorada).", + "ignoreSecWebsocketAcceptHeaderDescription": "Permite que o servidor não responda com o cabeçalho Sec-WebSocket-Accept, caso a atualização do websocket seja bem-sucedida.", + "Ignore Sec-WebSocket-Accept header": "Ignorar cabeçalho {0}", + "wsSubprotocolDescription": "Insira uma lista de subprotocolos separados por vírgulas. Para obter mais informações sobre subprotocolos, consulte a {documentation}", + "WebSocket Application Messaging Protocol": "WAMP (O Protocolo de Mensagens de Aplicação WebSocket)", + "Session Initiation Protocol": "Transporte WebSocket para SIP (Protocolo de Iniciação de Sessão)", + "Network API for Notification Channel": "API de rede RESTful OMA para canal de notificação", + "Advanced Message Queuing Protocol": "Protocolo Avançado de Enfileiramento de Mensagens (AMQP) 1.0+", + "jsflow": "Protocolo jsFlow pubsub/queue", + "Reverse Web Process Control": "Protocolo de Controle de Processo Web Reverso (RWPCP)", + "Extensible Messaging and Presence Protocol": "Transporte WebSocket para o Protocolo Extensível de Mensagens e Presença (XMPP)", + "Smart Home IP": "SHIP - IP para Casa Inteligente", + "Miele Cloud Connect Protocol": "Protocolo Miele Cloud Connect", + "Push Channel Protocol": "Protocolo de canal de push", + "Message Session Relay Protocol": "Transporte WebSocket para MSRP (Message Session Relay Protocol)", + "Binary Floor Control Protocol": "Transporte WebSocket para BFCP (Protocolo de Controle de Piso Binário)", + "Softvelum Low Delay Protocol": "Protocolo de baixa latência Softvelum", + "OPC UA Connection Protocol": "Protocolo de conexão OPC UA", + "OPC UA JSON Encoding": "Codificação JSON OPC UA", + "Swindon Web Server Protocol": "Protocolo do servidor web Swindon (codificação JSON)", + "Broadband Forum User Services Platform": "USP (Plataforma de Serviços ao Usuário do Fórum de Banda Larga)", + "Constrained Application Protocol": "Protocolo de Aplicação Restrita (CoAP)", + "Softvelum WebSocket signaling protocol": "Protocolo de sinalização WebSocket Softvelum", + "Cobra Real Time Messaging Protocol": "Protocolo de mensagens em tempo real Cobra", + "Declarative Resource Protocol": "Protocolo de Recursos Declarativos", + "BACnet Secure Connect Hub Connection": "Conexão segura BACnet ao Hub", + "BACnet Secure Connect Direct Connection": "Conexão direta segura BACnet", + "WebSocket Transport for JMAP": "Transporte WebSocket para JMAP (Protocolo de Aplicação Meta JSON)", + "ITU-T T.140 Real-Time Text": "Texto em tempo real ITU-T T.140", + "Done.best IoT Protocol": "Protocolo IoT Done.best", + "Text IRC Protocol": "Protocolo IRC de texto", + "Binary IRC Protocol": "Protocolo IRC Binário", + "Penguin Statistics Live Protocol v3": "Protocolo de Estatísticas do Pinguim em Tempo Real v3 (codificação Protobuf)", + "certHostnameMismatch": "O nome do host do certificado não corresponde ao URL do monitor.", + "Webpush Helptext": "A notificação push na web funciona apenas com conexões SSL (HTTPS). Em dispositivos iOS, a página da web precisa ser adicionada à tela inicial previamente.", + "minimumIntervalWarning": "Intervalos abaixo de 20 segundos podem resultar em desempenho ruim.", + "lowIntervalWarning": "Tem certeza de que deseja definir o valor do intervalo abaixo de 20 segundos? O desempenho pode ser degradado, especialmente se houver um grande número de monitores.", + "labelDomainExpiry": "Exp. Domínio", + "labelDomainNameExpiryNotification": "Notificação de expiração de nome de domínio", + "settingsDomainExpiry": "Expiração do domínio", + "domainExpiryDescription": "Acionar notificação quando os nomes de domínio expirarem em:", + "Deselect All": "Desmarcar tudo", + "Select All": "Selecionar tudo", + "Subprotocol": "Subprotocolo", + "Duration (Minutes)": "Duração (minutos)", + "SMTP Security": "Segurança SMTP", + "Ignore STARTTLS": "Ignorar STARTTLS", + "Use STARTTLS": "Usar STARTTLS", + "Enter the list of nodes": "Insira a lista de nós de gerenciamento do RabbitMQ", + "Press Enter to add node": "Pressione Enter para adicionar um nó", + "resendApiHelp": "Crie uma chave de API aqui {0}", + "resendFromName": "De Nome", + "resendFromEmail": "De E-mail", + "resendToEmail": "Para e-mail", + "resendSubject": "Assunto", + "resendApiKey": "Chave de API Resend", + "resendLeaveBlankForDefaultName": "Deixe em branco para o nome padrão", + "wsCodeDescription": "Para obter mais informações sobre códigos de status, consulte {rfc6455}", + "Subprotocol(s)": "Subprotocolo(s)", + "imageResetConfirmation": "Imagem redefinida para o padrão", + "systemService": "Serviço de sistema", + "systemServiceName": "Nome do serviço", + "systemServiceDescription": "Verifica se o serviço do sistema {service_name} está ativo", + "systemServiceDescriptionWindows": "Verifica se o Gerenciador de Serviços do Windows {service_name} está em execução", + "systemServiceCommandHint": "Comando utilizado: {comando}", + "systemServiceExpectedOutput": "Saída esperada: \"{0}\"", + "maxPing": "Ping máximo", + "minPing": "Ping mínimo", + "systemServiceDescriptionLinux": "Verifica se o serviço systemd do Linux {service_name} está ativo", + "avgPing": "Ping médio", + "Analytics Type": "Tipo de análise", + "Analytics ID": "ID de análise", + "Analytics Script URL": "URL do script do analytics", + "Google": "Google", + "Plausible": "Plausível", + "Matomo": "Matomo", + "Umami": "Umami", + "invalidHostnameOrIP": "Nome de host ou IP inválido. O nome de host deve ser um FQDN válido. Não é possível usar curinga. Pode conter sublinhado ou terminar com um ponto.", + "hostnameCannotBeIP": "O nome de host DNS não pode ser um endereço IP. Você queria usar o campo \"resolver\"?", + "invalidDNSHostname": "Nome de host inválido. O nome de host deve ser um FQDN válido. Pode ser um caractere curinga, conter um sublinhado ou terminar com um ponto.", + "wildcardOnlyForDNS": "Os nomes de host curinga são suportados apenas para monitores de DNS.", + "invalidURL": "URL inválida", + "sipsakPingWarning": "Para usar o monitor de ping de opções SIP, você precisa instalar o Uptime Kuma sem Docker e também o cliente Sipsak no seu servidor.", + "resendLeaveBlankForDefaultSubject": "Deixe em branco para o assunto padrão", + "mtls-auth-server-cert-label": "Certificado", + "mtls-auth-server-key-label": "Chave", + "mtls-auth-server-key-placeholder": "Corpo chave", + "mtls-auth-server-ca-label": "CA", + "mtls-auth-server-ca-placeholder": "Servidor CA", + "Uptime Kuma": "Uptime Kuma", + "Clear current filters": "Limpar filtros", + "Sort options": "Opções de ordenação", + "Show this Maintenance Message on which Status Pages": "Exibir esta mensagem de manutenção em quais Páginas de Status?", + "Endpoint": "Endpoint", + "Details": "Detalhes", + "year": "ano | anos", + "screenshot of the website": "Captura de tela do site", + "Basic checkbox toggle button group": "Grupo básico de botões de alternância de caixa de seleção", + "Basic radio toggle button group": "Grupo básico de botões de alternância de rádio", + "mtls-auth-server-cert-placeholder": "Corpo cert", + "Sort by status": "Ordenar por status", + "Sort by name": "Ordenar por nome", + "Sort by uptime": "Ordenar por tempo de atividade", + "Sort by certificate expiry": "Ordenar por data de expiração do certificado", + "Splunk Rest URL": "URL REST do Splunk", + "Severity": "Gravidade", + "SMSManager": "SMSManager", + "Message Format": "Formato da mensagem", + "smscTranslit": "smscTranslit", + "promosms": "promosms", + "Region": "Região", + "PushDeer Server URL": "URL do servidor PushDeer", + "To Number": "Para Número", + "GrafanaOncallURL": "URL do Grafana Oncall", + "Never": "Nunca", + "System Service": "Serviço de sistema", + "SSL/TLS": "SSL/TLS", + "playground": "playground", + "Check Type": "Tipo de verificação", + "Service Name": "Nome do serviço", + "GRPC Options": "Opções GRPC", + "Metadata": "Metadados", + "End": "Fim", + "message": "mensagem", + "json_value": "valor JSON", + "serwersmsRecipientType": "Tipo de destinatário", + "serwersmsRecipientTypePhone": "Número de telefone", + "serwersmsRecipientTypeGroup": "Grupo", + "serwersmsGroupId": "ID do grupo", + "Open Badge Link Generator": "Gerador de Links Open Badge", + "Badge Link Generator": "Gerador de links de emblemas de {0}", + "serwersmsGroupIdHelptext": "IDs ou IDs de grupo no Painel do Cliente. Esses identificadores podem ser baixados usando grupos de ações / índice ou copiando-os do grupo de edição no Painel do Cliente.", + "Badge Link Generator Helptext": "Os links para os emblemas estão disponíveis para todos os monitores atribuídos às páginas de status públicas. Para obter mais informações, consulte a {documentação}.", + "RSS Title": "Título do RSS", + "Leave blank to use status page title": "Deixe em branco para usar o título da página de status", + "None (Successful Connection)": "Nenhuma (Conexão bem-sucedida)", + "expectedTlsAlertDescription": "Selecione o alerta TLS que você espera que o servidor retorne. Use {code} para verificar se os endpoints mTLS rejeitam conexões sem certificados de cliente. Consulte {link} para obter detalhes.", + "TLS Alert Spec": "RFC 8446", + "TLS Alerts": "Alertas TLS", + "Expected TLS Alert": "Alerta TLS esperado", + "notificationUniversal": "Universal", + "notificationChatPlatforms": "Plataformas de bate-papo", + "notificationPushServices": "Serviços Push", + "notificationSmsServices": "Serviços de SMS", + "notificationEmail": "Email", + "notificationIncidentManagement": "Gestão de Incidentes", + "notificationHomeAutomation": "Automação residencial", + "notificationOther": "Outras integrações", + "passwordTooWeak": "A senha é muito fraca. Ela deve conter caracteres alfabéticos e numéricos e ter pelo menos 6 caracteres.", + "domain_expiry_unsupported_monitor_type": "O monitoramento de expiração de domínio não é compatível com este tipo de monitor", + "domain_expiry_unsupported_missing_target": "Não há nenhum domínio ou nome de host válido configurado para este monitor", + "domain_expiry_unsupported_invalid_domain": "O valor configurado \"{hostname}\" não é um nome de domínio válido", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" é muito curto para um domínio de nível superior", + "domain_expiry_unsupported_public_suffix": "O domínio \"{domain}\" não possui um sufixo público válido", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" é um endereço IP. O monitoramento de expiração de domínio requer um nome de domínio", + "Resolver Server(s)": "Servidor(es) de resolução", + "steamApiKeyDescriptionAt": "Para monitorar um servidor de jogos Steam, você precisa de uma chave da API Web do Steam. Você pode registrar sua chave de API em {url}", + "You can divide numbers with commas or semicolons": "Você pode dividir números com {comma} ou {semicolon}", + "username": "Nome de usuário", + "password": "Senha", + "Setup Instructions": "Instruções de instalação", + "halopsa_setup_step1": "Crie um Runbook de Integração no HaloPSA (Configuração → Integrações → Runbooks de Integração)", + "HeadersInvalidFormatBecause": "Os cabeçalhos da solicitação não são JSON válidos porque {error}", + "BodyInvalidFormatBecause": "O corpo da solicitação não é um JSON válido porque {error}", + "checkPriceAt": "Confira os preços do serviço {service} em {url}", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "O monitoramento de expiração de domínio não está disponível para \".{publicSuffix}\" porque nenhum serviço RDAP está listado pela IANA", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "URL de Webhook do Halo PSA", + "halopsa_webhook_url_desc": "Insira o URL do webhook no seu Manual de Instruções de Integração do Halo PSA (Configuração > Integrações > Integrações Personalizadas > Manual de Instruções de Integração). Selecione \"Só pode ser iniciado a partir do Halo e de um endpoint público\" ao criar o webhook.", + "halopsa_username_desc": "Nome de usuário para autenticação com o webhook do Halo PSA", + "halopsa_password_desc": "Senha para autenticação com o webhook Halo PSA", + "halopsa_setup_step2": "Configure as ações do runbook para processar alertas (por exemplo, Criar Ticket)", + "halopsa_setup_step3": "Copie o URL do Webhook e cole-o acima do campo de texto", + "halopsa_setup_step4": "Escolha a autenticação básica e crie um nome de usuário e uma senha. Em seguida, digite ou cole esse nome de usuário e senha acima dos campos de teste", + "ntfyCall": "Ligação telefônica", + "ntfyCallHelptext": "Faça uma chamada telefônica quando o alerta for acionado. Selecione \"sim\" para usar seu primeiro número verificado ou insira um número de telefone específico (por exemplo, +12223334444). Requer o NTfy Pro e um número de telefone verificado.", + "noMonitorsSelectedWarning": "Você está criando uma manutenção sem nenhum monitor afetado. Tem certeza de que deseja continuar?", + "noMonitorsOrStatusPagesSelectedError": "Não é possível criar uma manutenção sem monitores ou páginas de status afetados", + "OptionalParameters": "Parâmetros opcionais", + "aliyun-template-requirements-and-parameters": "O modelo de SMS Aliyun deve conter os seguintes parâmetros: {parameters}", + "aliyun-template-optional-parameters": "Parâmetros opcionais: {parameters}", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Devido a restrições da transportadora, habilite as variáveis opcionais sob o risco de não entrega", + "enableSSL": "Ativar SSL/TLS", + "mariadbUseSSLHelptext": "Habilite para usar uma conexão criptografada com seu banco de dados. Requerido para a maioria dos bancos de dados em nuvem.", + "mariadbCaCertificateLabel": "Certificado CA", + "mariadbCaCertificateHelptext": "Cole o certificado da Autoridade Certificadora (CA) no formato PEM para usar com certificados autoassinados. Deixe em branco se o seu banco de dados usar um certificado assinado por uma CA pública.", + "selectedMonitorCountMsg": "selecionado: {n} | selecionado: {n}", + "selectMonitorMsg": "Selecione os monitores para executar ações", + "Actions": "Ações", + "deselectAllMonitorsAria": "Desmarque todos os monitores", + "deleteMonitorsMsg": "Tem certeza de que deseja excluir os monitores selecionados?", + "pausedMonitorsMsg": "Monitor {n} pausado | Monitores {n} pausados", + "noMonitorsResumedMsg": "Nenhum monitor foi retomado (nenhum estava inativo)", + "bulkDeleteErrorMsg": "Falha ao excluir o monitor {n} | Falha ao excluir os monitores {n}", + "deletedMonitorsMsg": "Monitor {n} excluído | Monitores {n} excluídos", + "noMonitorsPausedMsg": "Nenhum monitor foi pausado (nenhum estava ativo)", + "saveResponseForNotifications": "Salvar resposta HTTP bem-sucedida para notificações", + "saveErrorResponseForNotifications": "Salvar resposta de erro HTTP para notificações", + "saveResponseDescription": "Armazena a resposta HTTP e a disponibiliza para os modelos de notificação como {templateVariable}", + "responseMaxLength": "Comprimento máximo da resposta (bytes)", + "responseMaxLengthDescription": "Tamanho máximo dos dados de resposta a serem armazenados. Defina como 0 para ilimitado. Respostas maiores serão truncadas. Padrão: 1024 (1 KB)", + "selectAllMonitorsAria": "Selecionar todos os monitores", + "resumedMonitorsMsg": "Monitor {n} retomado | Monitores {n} retomados", + "Only retry if status code check fails": "Só tente novamente se a verificação do código de status falhar", + "retryOnlyOnStatusCodeFailureDescription": "Se ativada, as novas tentativas ocorrerão somente quando a verificação do código de status HTTP falhar (por exemplo, se o servidor estiver inativo). Se a verificação do código de status for bem-sucedida, mas a consulta JSON falhar, o monitor será marcado como inativo imediatamente, sem novas tentativas.", + "minuteShort": "{n} min | {n} min", + "years": "{n} ano | {n} anos", + "hours": "{n} hora | {n} horas", + "minutes": "{n} minuto | {n} minutos", + "days": "{n} dia | {n} dias", + "Sets end time based on start time": "Define o horário de término com base no horário de início", + "Please set start time first": "Por favor, defina primeiro a hora de início", + "legacyOctopushEndpoint": "Octopush-DM legado (endpoint: {url})", + "unknownDays": "Dias desconhecidos", + "Monitors": "{n} Monitor | {n} Monitores", + "Suppress Notifications": "Suprimir notificações", + "discordSuppressNotificationsHelptext": "Quando ativada, as mensagens serão publicadas no canal, mas não acionarão notificações push ou notificações na área de trabalho para os destinatários.", + "domain_expiry_unsupported_is_icann": "O domínio \"{domain}\" não é candidato ao monitoramento de expiração de domínio, pois seu sufixo público \".{publicSuffix}\" não é gerenciado pela ICANN", + "versionIs": "Versão: {version}", + "logoutCurrentUser": "Sair da sessão {username}", + "lastUpdatedAt": "Última atualização: {date}", + "createdAt": "Criado em: {date}", + "lastUpdatedAtFromNow": "Última atualização: {date} ({fromNow})", + "Certificate Chain:": "Cadeia de Certificados:", + "dateCreatedAtFromNow": "Data de criação: {date} ({fromNow})", + "Examples:": "Exemplos: {0}", + "frontendVersionIs": "Versão do Frontend: {version}", + "cronScheduleDescription": "Cronograma: {description}", + "octopushEndpoint": "octopush (endpoint: {url})", + "snmpV3Username": "Nome de usuário SNMPv3", + "WeCom Mentioned Mobile List": "Lista de dispositivos móveis mencionados pela WeCom", + "WeCom Mentioned Mobile List Description": "Insira os números de telefone que deseja mencionar. Separe vários números com vírgulas. Use {'@'}all para mencionar todos.", + "Collapse All Groups": "Recolher todos os grupos", + "Expand All Groups": "Expandir todos os grupos", + "mariadbSocketPathDetectedHelptext": "Conectando-se ao banco de dados conforme especificado pela variável de ambiente {0}.", + "milliseconds": "{n} milissegundo | {n} milissegundos", + "screenshotDelayDescription": "Opcionalmente, aguarde este número de milissegundos antes de tirar a captura de tela. Máximo: {maxValueMs}ms (0,5 × intervalo).", + "Screenshot Delay": "Atraso na captura de tela (espera {miliseconds})", + "screenshotDelayWarning": "Valores mais altos mantêm o navegador aberto por mais tempo, o que pode aumentar o uso de memória com muitos monitores simultâneos.", + "Disable STARTTLS": "Desativar STARTTLS", + "disableSTARTTLSDescription": "Ative esta opção para servidores SMTP que não suportam STARTTLS. Isso enviará e-mails por meio de uma conexão não criptografada.", + "No incidents recorded": "Nenhum incidente registrado", + "Load More": "Carregar mais", + "Loading...": "Carregando...", + "Pin this incident": "Fixar este incidente", + "Incident description": "Descrição do incidente", + "Incident not found or access denied": "Incidente não encontrado ou acesso negado", + "Past Incidents": "Incidentes passados", + "Incident title": "Título do incidente", + "Pinned incidents are shown prominently on the status page": "Os incidentes fixados são exibidos com destaque na página de status", + "Edit Incident": "Editar incidente", + "Resolve": "Resolver", + "Resolved": "Resolvido", + "deleteIncidentMsg": "Tem certeza de que deseja excluir este incidente?", + "slug is not found": "Slug não encontrado", + "Please input content": "Por favor, insira o conteúdo", + "Please input title": "Por favor, insira o título", + "Google Apps Script Webhook URL": "URL do webhook do Google Apps Script", + "Deploy a Google Apps Script as a web app and paste the URL here": "Implante um script do Google Apps como um aplicativo da Web e cole o URL aqui", + "Quick Setup Guide": "Guia de Configuração Rápida", + "Open your Google Spreadsheet": "Abra sua planilha do Google", + "Paste the script code (see below)": "Cole o código do script (veja abaixo)", + "Click Deploy → New deployment → Web app": "Clique em Implantar → Nova implantação → Aplicativo Web", + "Copy the web app URL and paste it above": "Copie o URL do aplicativo web e cole-o acima", + "Google Apps Script Code": "Código do Google Apps Script", + "Copy to Clipboard": "Copiar para a área de transferência", + "Copied to clipboard!": "Copiado para a área de transferência!", + "Failed to copy to clipboard": "Falha ao copiar para a área de transferência", + "Go to Extensions → Apps Script": "Acesse Extensões → Apps Script", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Defina 'Executar como: Eu' e 'Quem tem acesso: Qualquer pessoa'", + "Globalping - Access global monitoring probes": "Globalping - Acesse sondas de monitoramento global", + "globalpingApiTokenDescription": "Obtenha seu token de API Globalping em {0}.", + "GlobalpingLocationDocs": "Documentação completa de entrada de localização", + "Protocol": "Protocolo", + "Location": "Localização", + "Monitor Subtype": "Subtipo de monitor", + "Check for": "Verifique se", + "GlobalpingDescription": "O Globalping fornece acesso a milhares de sondas hospedadas pela comunidade para executar testes e medições de rede. Há um limite de 250 testes por hora para todos os usuários anônimos. Para dobrar o limite para 500 por hora, salve seu token em {accountSettings}.", + "Globalping API Token": "Token da API Globalping", + "GlobalpingHostname": "Um alvo de medição acessível publicamente. Normalmente, um nome de host ou um endereço IPv4/IPv6, dependendo do tipo de medição.", + "GlobalpingLocation": "O campo de localização aceita continentes, países, regiões, cidades, ASNs, ISPs ou regiões de nuvem. Você pode combinar filtros com {plus} (por exemplo, {amazonPlusGermany} ou {comcastPlusCalifornia}). Se a latência for uma métrica importante, use filtros para restringir a localização a uma região menor para evitar picos. {fullDocs}.", + "GlobalpingIpFamilyInfo": "A versão do IP a ser usada. Permitido somente se o destino for um nome de host.", + "GlobalpingResolverInfo": "Endereço IPv4/IPv6 ou um Nome de Domínio Totalmente Qualificado (FQDN). O padrão é o servidor de resolução de rede local da sonda. Você pode alterar o servidor de resolução a qualquer momento.", + "account settings": "configurações de conta", + "templateAvailableVariables": "Variáveis disponíveis", + "example": "Exemplo", + "Result": "Resultado", + "ntfyUseTemplate": "Personalize os modelos de notificação", + "ntfyUseTemplateDescription": "Ative esta opção para personalizar os títulos e mensagens das notificações usando modelos LiquidJS", + "ntfyCustomTitle": "Modelo de título personalizado", + "ntfyCustomMessage": "Modelo de mensagem personalizado", + "ntfyNotificationTemplateFallback": "Deixe em branco para usar o formato padrão do Uptime Kuma", + "Cloud ID": "Cloud ID", + "API Token": "Token da API", + "See Jira Cloud Docs": "Consulte a documentação do Jira Cloud", + "aboutJiraCloudId": "Mais informações sobre o ID do Jira Cloud: {0}", + "see Jira Cloud Docs": "Consulte a documentação do Jira Cloud", + "Jira Service Management": "Gerenciamento de serviços Jira", + "slackIncludeGroupName": "Incluir o nome do grupo de monitoramento", + "slackIncludeGroupNameDescription": "Se ativada, a opção de incluir o caminho do grupo de monitores nas notificações ajudará a distinguir monitores com o mesmo nome em diferentes grupos.", + "slackUseTemplateDescription": "Se ativada, a mensagem será enviada usando um modelo personalizado. Você pode usar o recurso de modelos Liquid para incluir informações do grupo de monitoramento por meio de `monitorJSON.path` ou `monitorJSON.pathName`.", + "slackUseTemplate": "Use um modelo de mensagem personalizado", + "discordMessageFormatMinimalist": "Minimalista (status curto)", + "discordMessageFormatCustom": "Modelo personalizado", + "discordUseMessageTemplate": "Use um modelo de mensagem personalizado", + "discordMessageTemplate": "Modelo de mensagem", + "discordMessageFormat": "Formato da mensagem", + "discordMessageFormatNormal": "Normal (mensagens formatadas)", + "discordUseMessageTemplateDescription": "Se ativada, a mensagem será enviada usando um modelo personalizado (LiquidJS). Deixe em branco para usar o formato padrão do Uptime Kuma.", + "halopsa_field_title": "Título do alerta (sempre 'Alerta Uptime Kuma')", + "halopsa_field_status": "Status do monitor: ATIVO, INATIVO, NOTIFICAÇÃO ou DESCONHECIDO", + "halopsa_field_monitor": "Nome do monitor", + "halopsa_field_message": "Mensagem de alerta completa com status e detalhes", + "halopsa_field_timestamp": "Registro de data e hora do evento no formato ISO 8601", + "halopsa_field_uptime_kuma_version": "Número da versão do Uptime Kuma", + "halopsa_setup_step5": "Configure o runbook para usar o monitor_id para associar alertas a tickets existentes", + "Webhook Payload Fields": "Campos do payload do webhook", + "halopsa_payload_desc": "Os seguintes campos são enviados para o seu webhook Halo PSA:", + "halopsa_field_monitor_id": "Identificador único do monitor (nulo para notificações de teste) - Use-o para associar alertas a tickets", + "halopsa_id_usage_hint": "💡 Dica: Use o `monitor_id` para associar alertas a tickets de forma confiável e o `heartbeat_id` para acompanhar o histórico de eventos", + "matrixUseTemplateDescription": "Se ativada, a mensagem será enviada usando um modelo personalizado.", + "matrixUseTemplate": "Use um modelo de mensagem personalizado", + "teamsEnableTags": "Incluir etiquetas", + "teamsEnableTagsDescription": "Se ativada, a mensagem incluirá as etiquetas de monitoramento.", + "teltonikaUrl": "URL do seu dispositivo Teltonika", + "teltonikaUrlHelptext": "O URL deve ser especificado como origem completa, por exemplo, {0} ou {1}.", + "teltonikaUnsafeTls": "Ignorar validação de certificado", + "teltonikaUsername": "Nome de usuário da API", + "teltonikaUsernameHelptext": "Recomendação: Crie uma conta separada, restrita apenas ao envio de mensagens SMS, e insira o nome de usuário aqui", + "teltonikaPassword": "Senha da API", + "teltonikaModem": "ID do modem", + "teltonikaPhoneNumber": "Número de telefone", + "teltonikaPhoneNumberHelptext": "O número deve estar no formato internacional {0}, {1}. Apenas um número é permitido.", + "Teltonika SMS Gateway": "Gateway SMS da Teltonika", + "teltonikaVersionWarning": "Este provedor de notificações exige que seu dispositivo Teltonika execute o RMS versão 7.14.0 ou superior.", + "teltonikaUnsafeTlsDescription": "Desativar a validação de certificados TLS expõe seu sistema a ataques de intermediário (man-in-the-middle), podendo levar a vazamentos de dados e à tomada de controle do sistema. Não desative a validação de certificados a menos que você aceite essa possibilidade de ataque. Recomendamos o uso do Let's Encrypt com renovação automática.", + "teltonikaPasswordHelptext": "Você pode definir a senha do usuário da API no seu roteador Teltonika, por exemplo, {0}", + "teltonikaModemHelptext": "O ID do modem SMS deve estar no formato {0}. Consulte https://developers.teltonika-networks.com/reference/ para obter orientações.", + "RecordMatch": "Correspondência de valor de registro", + "RegexMatch": "Insira uma expressão regular para corresponder ao valor do registro", + "GlobalpingMonitorDescription": "O Globalping oferece acesso a milhares de sondas hospedadas pela comunidade para executar testes e medições de rede. Há um limite de 250 testes por hora para todos os usuários anônimos. Para dobrar o limite para 500 por hora, salve seu token em {accountSettings}. Consulte a documentação {docs} para obter mais informações.", + "certificateExpiryNotificationHelp": "O número de dias de antecedência pode ser configurado nas Configurações.", + "domainExpiryNotificationHelp": "O número de dias de antecedência pode ser configurado nas Configurações.", + "signalUseTemplate": "Use um modelo de mensagem personalizado", + "signalUseTemplateDescription": "Se ativada, a mensagem será enviada usando um modelo personalizado. Você pode usar a linguagem de modelos Liquid para personalizar o formato da notificação.", + "monitorTypeGameServer": "Servidor de jogos", + "monitorTypeDatabase": "Tipo de monitor de banco de dados", + "monitorTypeSpecial": "Especial", + "360messengerRecipient": "Número(s) de telefone do destinatário", + "360messengerGroupId": "ID do grupo 360messenger", + "360messengerTemplate": "Modelo de mensagem do 360messenger", + "360messengerGroupList": "grupos do WhatsApp", + "360messengerSelectedGroupID": "ID(s) do(s) grupo(s) selecionado(s)", + "360messengerEnableSendToGroup": "Ativar o envio para grupos do WhatsApp", + "360messengerCustomMessageTemplate": "Modelo de mensagem personalizado", + "360messengerMessageTemplate": "Modelo de mensagem", + "360messengerWayToGetUrlAndToken": "Você pode obter sua chave de API do 360messenger em {0}.", + "360messengerErrorNoApiKey": "Por favor, insira primeiro sua chave de API do 360messenger.", + "360messengerErrorNoGroups": "Não foram encontrados grupos do WhatsApp para esta conta.", + "360messengerErrorGeneric": "Não foi possível carregar a lista de grupos do WhatsApp: {message}", + "360messengerAuthToken": "Chave da API do 360messenger", + "360messengerUseTemplate": "Use um modelo de mensagem personalizado", + "360messengerSelectGroupList": "Selecione um grupo para adicionar", + "360messengerEnableCustomMessage": "Ative um modelo de mensagem personalizado em vez da mensagem padrão.", + "360messengerWayToWriteRecipient": "Insira um ou mais números de telefone no formato internacional, sem o sinal de mais inicial (por exemplo, {0}). Separe vários números com vírgulas.", + "360messengerErrorApi": "Não foi possível carregar a lista de grupos do WhatsApp (Erro {statusCode}: {message}).", + "GlobalpingMultipleLocationsError": "Não é possível realizar várias localizações; utilize uma única localização para cada monitor.", + "GlobalpingLocationDescription": "O campo de localização aceita continentes, países, regiões, cidades, ASNs, ISPs ou regiões de nuvem. Você pode combinar filtros com {plus} (por exemplo, {amazonPlusGermany} ou {comcastPlusCalifornia}). Se a latência for uma métrica importante, use filtros para restringir a localização a uma região pequena para evitar picos e, para maior estabilidade, defina o filtro {datacenter}. {fullDocs}.", + "fluxerMessageFormat": "Formato da mensagem", + "fluxerMessageFormatNormal": "Normal (mensagens formatadas)", + "fluxerMessageFormatMinimalist": "Minimalista (status curto)", + "fluxerUseMessageTemplate": "Use um modelo de mensagem personalizado", + "fluxerMessageTemplate": "Modelo de mensagem", + "fluxerMessageFormatCustom": "Modelo personalizado", + "fluxerUseMessageTemplateDescription": "Se ativada, a mensagem será enviada usando um modelo personalizado (LiquidJS). Deixe em branco para usar o formato padrão do Uptime Kuma.", + "Fluxer Webhook URL": "URL do Webhook do Fluxer", + "wayToGetFluxerURL": "Você pode obter essa informação acessando as configurações do canal de destino > Webhooks > Criar Webhook > Copiar URL do Webhook.", + "Notification Action": "Ação de notificação", + "homeAssistantNotificationActionHelptext": "Uma lista de Ações de Notificação pode ser encontrada no Home Assistant em \"Configurações > Ferramentas do desenvolvedor > Ações\". Busque por \"notify\" para encontrar suas ações. Digite apenas a parte após \"notify.\", por exemplo, para a ação \"notify.mobile_app_xyz\", digite \"mobile_app_xyz\". Para notificações integradas do aplicativo móvel, procure por \"Enviar uma notificação via mobile_app_xyz\" (e não \"Enviar uma notificação\").", + "oracledbConnectionString": "Banco de dados Oracle: {connectionString}", + "wayToGetMaxToken": "Você pode obter um token MAX bot e outros detalhes de {0}.", + "maxMessenger": "mensageiro MAX", + "maxApiUrlDescription": "URL base da API do MAX Messenger. Padrão: https://platform-api.max.ru", + "wayToGetMaxChatID": "Especifique o identificador do chat no MAX para onde as mensagens devem ser entregues.", + "maxUseTemplate": "Use um modelo de mensagem personalizado", + "maxUseTemplateDescription": "Se ativada, a mensagem será enviada usando um modelo personalizado.", + "maxTemplateFormatDescription": "O mensageiro MAX suporta formatação em texto simples, Markdown e HTML.", + "API Version": "Versão da API", + "Peer ID": "ID do par", + "vkApiVersionDescription": "Versão da API do VK usada para as solicitações. Mantenha a versão padrão, a menos que você precise especificamente de outra versão da API do VK para fins de compatibilidade.", + "vkDontParseLinks": "Desativar trechos de links", + "vkDontParseLinksDescription": "Se ativada, a opção VK não gerará pré-visualizações/trechos de links.", + "vkPeerIdDescription": "Insira o peer_id do VK de destino. Esse valor é enviado para a API exatamente como está.", + "telnyxApiKey": "Chave de API (V2)", + "telnyxPhoneNumber": "Número De", + "telnyxPhoneNumberHelptext": "Seu número de telefone Telnyx para envio (deve estar registrado em sua conta Telnyx, por exemplo, +15551234567)", + "telnyxToNumber": "Número Para", + "telnyxMessagingProfileId": "ID do perfil de mensagens (opcional)", + "telnyxMessagingProfileIdHelptext": "Opcionalmente, associe um Perfil de Mensagens Telnyx para controlar as configurações e funcionalidades do remetente", + "telnyxApiKeyHelptext": "Sua chave de API V2 da Telnyx, obtida no Portal da Telnyx em Chaves de API", + "telnyxToNumberHelptext": "O número de telefone de destino para receber notificações por SMS (ex.: +15559876543)", + "evolutionCustomMessageTitle": "Mensagem personalizada (opcional)", + "evolutionCustomMessageDesc": "Deixe em branco para usar a mensagem padrão.", + "WebSocket Options": "Opções do WebSocket", + "pingPacketSizeWarning": "Um tamanho de pacote menor que 16 não produz tempos de ida e volta no Linux", + "VKTeams Chat Id Description": "Para usuários, este é o endereço de e-mail deles. Para grupos e canais, obtenha o ID das configurações e adicione {'@'}chat.agent", + "VKTeams Template Format Description": "Para formatação de mensagens, o VKTeams suporta texto simples, MarkdownV2 e formatação HTML.", + "VKTeams Bot Token Description": "Você pode descobrir como obter um token de bot e outros detalhes em {0}.", + "VKTeams Base Url Description": "O URL base da API do VKTeams pode ser diferente em soluções locais (On-Premise). Padrão: https://myteam.mail.ru", + "VKTeams Use Template": "Use um modelo de mensagem personalizado", + "VKTeams Use Template Description": "Se ativada, a mensagem será enviada usando um modelo personalizado.", + "wayToGetEgoSMSToken": "Você pode obter seu nome de usuário e senha da API do EgoSMS {here}.", + "egosmsSenderDescription": "Identificação do remetente exibida aos destinatários (máximo de 11 caracteres). O padrão é EGOSMS se estiver vazio.", + "egosmsPhoneNumberDescription": "Número do destinatário com código do país, sem + ou 00 (ex.: 2567XXXXXXXX).", + "gamedigToken": "Token", + "gamedigTokenDescription": "Opcional. Alguns servidores de jogos exigem um token de autenticação.", + "Bearer Token": "Bearer Token", + "Connection Timeout": "Tempo limite de conexão", + "smtpAdditionalHeadersTitle": "Cabeçalhos adicionais", + "smtpAdditionalHeadersDesc": "Consulte os cabeçalhos personalizados do Nodemailer {0} para obter informações sobre o uso.", + "Rybbit": "Rybbit", + "Flowtriq Webhook URL": "URL do Webhook", + "Flowtriq API Key": "Chave da API", + "flowtriqApiKeyDescription": "Chave da API opcional para autenticar solicitações de webhook para sua instância do Flowtriq.", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) entrega de forma autônoma a aplicação WxPusher através de 6 plataformas - Android, iOS, HarmonyOS, Windows, macOS e Linux ({download}). Escaneie o código QR abaixo para obter seu SPT e você estará pronto para enviar. Separe multiplos SPTs com virgula (,) - veja a {docs} para mais informações.", + "wxPusherDownloadApp": "Baixe o aplicativo", + "wxPusherDocs": "Guia oficial", + "Access Key": "Chave de Acesso", + "ooredooUsernameDescription": "O endereço de e-mail que você utiliza para se logar no painel Ooredoo bulk SMS.", + "ooredooBearerTokenDescription": "A token exibida após \"Authorization: Bearer\" no exemplo do comando cURL em seu painel Ooredoo SMS.", + "ooredooRecipientDescription": "Entre um ou mais números separados por virgula ou espaço. Os números locais de 7 dígitos serão automaticamente prefixados com 960.", + "Leave blank to use the default Ooredoo endpoint.": "Deixe em branco para usar o endpoint padrão do Ooredoo.", + "plivoAuthID": "ID de Autenticação", + "plivoAuthIDHelptext": "Seu ID de Autenticação Plivio para o Console Plivio", + "plivoAuthToken": "Token de Autenticação", + "plivoAuthTokenHelptext": "Seu Token de Autenticação Plivio para o Console Plivio", + "plivoFromNumber": "Número do Remetente", + "plivoToNumber": "Número do destinatário", + "plivoToNumberHelptext": "O número de telefone de destino que recebe o SMS ou a chamada de voz (por exemplo, +15559876543)", + "pm2Description": "Verifique se o processo PM2 {service_name) está online", + "pm2ExpectedStates": "Estado esperado: online. Estados pausado ou em falha são tratados como CAÍDO.", + "PM2 Process": "Processo PM2", + "Refresh": "Atualizar", + "Select PM2 process": "Selecione o Processo PM2", + "ooredooAccessKeyDescription": "Encontrado em \"Chave de Acesso da API\" em seu painel Ooredoo SMS. Cole isso exatamente como exibido - Será criptografado em base64 automaticamente após submetido.", + "plivoFromNumberHelptext": "O número de telefone Plivo a ser utilizado como origem (por exemplo, +15551234567). O envio de SMS também pode utilizar um ID de remetente alfanumérico aprovado; chamadas de voz exigem um número de telefone Plivo.", + "plivoMessageType": "Tipo de mensagem", + "plivoVoiceCall": "Chamada de voz", + "plivoAnswerUrl": "URL da resposta", + "ntpStratumThreshold": "Limite de Estrato", + "ntpOffsetThreshold": "Limite de Deslocamento de Tempo (ms)", + "ntpOffsetThresholdHelp": "Marcar como atrasado se o desvio de tempo exceder este valor em milissegundos.", + "ntpDispersionThreshold": "Limite de Dispersão da Raiz (ms)", + "plivoAnswerUrlHelptext": "URL que retorna XML do Plivo para a chamada. O texto do alerta é anexado como um parâmetro de consulta `?message=`, permitindo que seu endpoint retorne, por exemplo: `{message}`.", + "ntpThresholdsTitle": "Limiares de NTP", + "ntpStratumThresholdHelp": "Marque como down se o stratum for igual ou superior a este valor. O stratum indica a qualidade da fonte de tempo (1 = GPS/relógio atômico, 16 = não sincronizado).", + "ntpDispersionThresholdHelp": "Marque como down se a dispersão da raiz exceder este valor. A dispersão indica a precisão do relógio do servidor.", + "openwaChatId": "ID do Chat (Número de Telefone / ID de Contato / ID de Grupo)", + "wayToGetOpenwaApiUrl": "A URL da sua instância do OpenWA.", + "wayToGetOpenwaApiKey": "Chave de API usada para sua instância do OpenWA.", + "openwaSession": "Sessão", + "openwaCustomMessageTitle": "Mensagem personalizada (opcional)", + "openwaCustomMessageDesc": "Deixe em branco para usar a mensagem padrão.", + "wayToGetOpenwaSession": "ID de sessão usado pelo OpenWA para enviar notificações.", + "wayToWriteOpenwaChatId": "O número de telefone com o prefixo internacional, mas sem o sinal de mais no início ({0}), o ID do contato ({1}) ou o ID do grupo ({2}). Você pode enviar para vários IDs de chat separando-os por vírgulas.", + "turbosmtpConsumerKey": "Chave do Consumidor", + "turbosmtpConsumerSecret": "Segredo do Consumidor", + "Pinglet Topic URL": "URL do tópico", + "pingletTopicUrlDescription": "A URL de publicação do tópico do Pinglet para o qual os alertas serão enviados (por exemplo, https://app.pinglet.co.uk/your-namespace/alerts). O tópico é criado na primeira publicação, caso ainda não exista.", + "Hash Key": "Chave de hash", + "wayToGetBearSMSToken": "Você pode obter seu nome de usuário e chave hash da API do BearSMS em {0}.", + "bearsmsPhoneNumberDescription": "Número do destinatário com código do país, sem + ou 00 (ex.: 9725XXXXXXXX).", + "bearsmsSenderDescription": "Identificador de remetente opcional exibido aos destinatários (máximo de 11 caracteres). Ele deve ser aprovado em sua conta BearSMS; caso contrário, será utilizado o remetente padrão da conta.", + "ClickUp API Token": "Token da API do ClickUp", + "ClickUp Workspace ID": "ID do Espaço de Trabalho do ClickUp", + "ClickUp Workspace ID Description": "Você pode encontrá-lo na URL do seu espaço de trabalho, logo após app.clickup.com/, por exemplo: app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ID do canal do ClickUp", + "ClickUp API Token Description": "Você pode criar um token de acesso pessoal no ClickUp acessando Configurações -> Integrações e ClickApps -> API do ClickUp -> Token de API", + "ClickUp Channel ID Description": "Você pode encontrá-lo na URL do canal de chat, logo após /chatr/, por exemplo: .../chatr/abc 123-4567.", + "milkyHttpAddress": "Endereço HTTP do Milky (sem o sufixo '/api')", + "milkyMessageType": "Tipo de Mensagem Milky", + "milkyGroupMessage": "Grupo", + "milkyPrivateMessage": "Privado", + "milkyUserOrGroupId": "Grupo/ID do Usuário", + "milkySafetyTips": "Por segurança, é necessário definir o token de acesso", + "Device ID or Group ID": "ID do dispositivo ou ID do grupo", + "notifyAppDeviceIdDescription": "O ID do dispositivo do aplicativo Notify!, ou um ID de grupo para alertar todos os dispositivos de um grupo de uma só vez.", + "notifyAppSetup": "Abra o Notify! e encontre o ID do seu dispositivo e o token em Configurações. Não é necessário criar uma conta. Saiba mais em {0}.", + "smsgatewayUrlHelptext": "A URL base do seu gateway de SMS, sem o caminho da API.", + "smsgatewayTo": "Número(s) de telefone", + "smsgatewayToHelptext": "Lista de números de telefone dos destinatários separados por vírgula, em formato internacional (por exemplo, +15551234567)", + "Client Key": "Client Key", + "Channel": "Canal", + "SFTP Path to Check": "Caminho SFTP para verificar", + "sftpPathHelpText": "Opcional. Se especificado, o monitor verificará se esse caminho existe no servidor SFTP.", + "SSH Private Key": "Chave SSH privada", + "Passphrase": "Senha", + "sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----", + "sshPrivateKeyHelpText": "Cole o conteúdo do seu arquivo de chave privada (por exemplo, id_rsa ou id_ed25519).", + "sshPassphraseHelpText": "Opcional. Necessário apenas se sua chave privada estiver criptografada com uma senha.", + "optional": "opcional", + "Line Number": "Número da linha", + "Amoot SMS recipient numbers help": "Insira um ou mais números de celular separados por vírgulas.", + "Use Pattern": "Usar padrão", + "Pattern Code ID": "ID do código do padrão", + "Amoot SMS pattern help": "Insira o código de padrão (ID) do seu painel de SMS da Amoot. {0}", + "Create an Amoot SMS pattern": "Crie um padrão de SMS Amoot", + "Use Own Line for Pattern": "Use sua própria linha para o padrão", + "Indigo URL": "URL Indigo", + "indigoUrlHelptext": "Seu endereço do Indigo Reflector (https://NAME.indigodomo.net) ou o Indigo Web Server em sua rede (https://IP:8176).", + "indigoIgnoreTlsErrorHelptext": "Necessário para o Servidor Web Indigo em sua rede, que utiliza um certificado autoassinado. Não é necessário para o Reflector.", + "indigoApiKeyHelptext": "Uma chave de API da sua conta Indigo (Autorizações) ou um segredo local para o servidor web na sua rede.", + "Variable ID": "ID da variável", + "indigoVariableIdHelptext": "A mensagem de alerta é gravada nesta variável.", + "Action Group ID": "ID do Grupo de Ação", + "indigoActionGroupIdHelptext": "Executado após a atualização da variável. Defina uma variável, um grupo de ações ou ambos. Copie os IDs do Indigo usando o botão direito do mouse → Copiar ID." +} diff --git a/src/lang/pt-PT.json b/src/lang/pt-PT.json new file mode 100644 index 0000000..745e7cb --- /dev/null +++ b/src/lang/pt-PT.json @@ -0,0 +1,503 @@ +{ + "languageName": "Português (Portugal)", + "checkEverySecond": "Verificar a cada {0} segundos", + "retryCheckEverySecond": "Tentar novamente a cada {0} segundos", + "retriesDescription": "Máximo de tentativas antes que o serviço seja marcado como inativo e uma notificação seja enviada", + "ignoreTLSError": "Ignorar erros TLS/SSL para sites HTTPS", + "upsideDownModeDescription": "Inverte o status de cabeça para baixo. Se o serviço estiver acessível, ele está OFFLINE.", + "maxRedirectDescription": "Número máximo de redirecionamentos a seguir. Define como 0 para desativar redirecionamentos.", + "acceptedStatusCodesDescription": "Seleciona os códigos de status que são considerados uma resposta bem-sucedida.", + "passwordNotMatchMsg": "A senha repetida não corresponde.", + "notificationDescription": "Atribuir uma notificação ao (s) monitor (es) para que funcione.", + "keywordDescription": "Pesquisa a palavra-chave em HTML simples ou resposta JSON e diferencia maiúsculas de minúsculas", + "pauseDashboardHome": "Pausa", + "deleteMonitorMsg": "Tens a certeza de que queres excluir este monitor?", + "deleteNotificationMsg": "Tens a certeza de que queres excluir esta notificação para todos os monitores?", + "resolverserverDescription": "A Cloudflare é o servidor padrão, podes alterar o servidor 'resolvedor' a qualquer momento.", + "rrtypeDescription": "Seleciona o RR-Type que queres monitorizar", + "pauseMonitorMsg": "Tens a certeza que queres fazer uma pausa?", + "enableDefaultNotificationDescription": "Para cada monitor novo esta notificação vai estar activa por padrão. Podes também desativar a notificação separadamente para cada monitor.", + "clearEventsMsg": "Tens a certeza que queres excluir todos os eventos deste monitor?", + "clearHeartbeatsMsg": "Tens a certeza de que queres excluir todos os heartbeats deste monitor?", + "confirmClearStatisticsMsg": "Tens a certeza que queres excluir TODAS as estatísticas?", + "importHandleDescription": "Escolhe 'Ignorar existente' se quiseres ignorar todos os monitores ou notificações com o mesmo nome. 'Substituir' excluirá todos os monitores e notificações existentes.", + "confirmImportMsg": "Tens a certeza que queres importar o backup? Certifica-te que selecionaste a opção de importação correta.", + "twoFAVerifyLabel": "Insire o teu token para verificares se o 2FA está a funcionar", + "tokenValidSettingsMsg": "O token é válido! Agora podes salvar as configurações do 2FA.", + "confirmEnableTwoFAMsg": "Tens a certeza de que queres habilitar 2FA?", + "confirmDisableTwoFAMsg": "Tens a certeza de que queres desativar 2FA?", + "Settings": "Configurações", + "Dashboard": "Dashboard", + "New Update": "Nova Atualização", + "Language": "Idioma", + "Appearance": "Aparência", + "Theme": "Tema", + "General": "Geral", + "Version": "Versão", + "Check Update On GitHub": "Verificar atualização no Github", + "List": "Lista", + "Add": "Adicionar", + "Add New Monitor": "Adicionar novo monitor", + "Quick Stats": "Estatísticas rápidas", + "Up": "On", + "Down": "Off", + "Pending": "Pendente", + "Unknown": "Desconhecido", + "Pause": "Pausa", + "Name": "Nome", + "Status": "Status", + "DateTime": "Data hora", + "Message": "Mensagem", + "No important events": "Nenhum evento importante", + "Resume": "Resumo", + "Edit": "Editar", + "Delete": "Apagar", + "Current": "Atual", + "Uptime": "Tempo de atividade", + "Cert Exp.": "Cert Exp.", + "day": "dia | dias", + "-day": "-dia", + "hour": "hora", + "-hour": "-hora", + "Response": "Resposta", + "Ping": "Ping", + "Monitor Type": "Tipo de Monitor", + "Keyword": "Palavra-Chave", + "Friendly Name": "Nome Amigável", + "URL": "URL", + "Hostname": "Hostname", + "Port": "Porta", + "Heartbeat Interval": "Intervalo de Heartbeats", + "Retries": "Novas tentativas", + "Heartbeat Retry Interval": "Intervalo de repetição de Heartbeats", + "Advanced": "Avançado", + "Upside Down Mode": "Modo de cabeça para baixo", + "Max. Redirects": "Max. Redirecionamentos", + "Accepted Status Codes": "Status Code Aceitáveis", + "Save": "Guardar", + "Notifications": "Notificações", + "Not available, please setup.": "Não disponível, por favor configure.", + "Setup Notification": "Configurar Notificação", + "Light": "Claro", + "Dark": "Escuro", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Tema - Barra de Heartbeat", + "Normal": "Normal", + "Bottom": "Inferior", + "None": "Nenhum", + "Timezone": "Fuso horário", + "Search Engine Visibility": "Visibilidade do mecanismo de pesquisa", + "Allow indexing": "Permitir Indexação", + "Discourage search engines from indexing site": "Desencorajar que motores de busca indexem o site", + "Change Password": "Mudar senha", + "Current Password": "Senha atual", + "New Password": "Nova Senha", + "Repeat New Password": "Repetir Nova Senha", + "Update Password": "Atualizar Senha", + "Disable Auth": "Desativar Autenticação", + "Enable Auth": "Ativar Autenticação", + "disableauth.message1": "Tens a certeza que queres {disableAuth}?", + "disable authentication": "desativar a autenticação", + "disableauth.message2": "Isso é para {intendThirdPartyAuth} em frente ao 'UpTime Kuma' como o Cloudflare Access.", + "where you intend to implement third-party authentication": "alguém que tem autenticação de terceiros", + "Please use this option carefully!": "Por favor, utiliza esta opção com cuidado.", + "Logout": "Terminar sessão", + "Leave": "Sair", + "I understand, please disable": "Eu entendo, por favor desativa.", + "Confirm": "Confirmar", + "Yes": "Sim", + "No": "Não", + "Username": "Utilizador", + "Password": "Senha", + "Remember me": "Lembra-me", + "Login": "Iniciar Sessão", + "No Monitors, please": "Nenhum monitor, por favor", + "add one": "adicionar um", + "Notification Type": "Tipo de Notificação", + "Email": "Email", + "Test": "Testar", + "Certificate Info": "Info. do Certificado", + "Resolver Server": "Resolver Servidor", + "Resource Record Type": "Tipo de registro de aplicação", + "Last Result": "Último resultado", + "Create your admin account": "Cria a tua conta de admin", + "Repeat Password": "Repete a senha", + "Import Backup": "Importar Backup", + "Export Backup": "Exportar Backup", + "Export": "Exportar", + "Import": "Importar", + "respTime": "Tempo de Resp. (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Padrão ativado", + "Apply on all existing monitors": "Aplicar em todos os monitores existentes", + "Create": "Criar", + "Clear Data": "Limpar Dados", + "Events": "Eventos", + "Heartbeats": "Pings", + "Auto Get": "Obter Automático", + "backupDescription": "Podes fazer backup de todos os monitores e todas as notificações num arquivo JSON.", + "backupDescription2": "OBS: Os dados do histórico e do evento não estão incluídos.", + "backupDescription3": "Dados confidenciais, como tokens de notificação, estão incluídos no arquivo de exportação, mantem-no com cuidado.", + "alertNoFile": "Seleciona um arquivo para importar.", + "alertWrongFileType": "Seleciona um arquivo JSON.", + "Clear all statistics": "Limpar todas as estatísticas", + "Skip existing": "Saltar existente", + "Overwrite": "Sobrescrever", + "Options": "Opções", + "Keep both": "Manter os dois", + "Verify Token": "Verificar Token", + "Setup 2FA": "Configurar 2FA", + "Enable 2FA": "Ativar 2FA", + "Disable 2FA": "Desativar 2FA", + "2FA Settings": "Configurações do 2FA", + "Two Factor Authentication": "Autenticação de Dois Fatores", + "Active": "Ativo", + "Inactive": "Inativo", + "Token": "Token", + "Show URI": "Mostrar URI", + "Tags": "Tag", + "Add New below or Select...": "Adicionar Novo abaixo ou Selecionar…", + "Tag with this name already exist.": "Já existe uma etiqueta com este nome.", + "Tag with this value already exist.": "Já existe uma etiqueta com este valor.", + "color": "cor", + "value (optional)": "valor (opcional)", + "Gray": "Cinza", + "Red": "Vermelho", + "Orange": "Laranja", + "Green": "Verde", + "Blue": "Azul", + "Indigo": "Índigo", + "Purple": "Roxo", + "Pink": "Rosa", + "Search...": "Pesquisa…", + "Avg. Ping": "Ping Médio", + "Avg. Response": "Resposta Média", + "Status Page": "Página de Status", + "Status Pages": "Página de Status", + "Entry Page": "Página de entrada", + "statusPageNothing": "Nada aqui, por favor, adiciona um grupo ou monitor.", + "No Services": "Nenhum Serviço", + "All Systems Operational": "Todos os Serviços Operacionais", + "Partially Degraded Service": "Serviço parcialmente degradados", + "Degraded Service": "Serviço Degradado", + "Add Group": "Adicionar Grupo", + "Add a monitor": "Adicionar um monitor", + "Edit Status Page": "Editar Página de Status", + "Go to Dashboard": "Ir para o dashboard", + "backupOutdatedWarning": "Depreciado: Uma vez que muitas funcionalidades foram adicionadas e esta funcionalidade de backup é um pouco desmanchada, não pode gerar ou restaurar um backup completo.", + "Schedule maintenance": "Agendar manutenção", + "Affected Monitors": "Monitores Afetados", + "Pick Affected Monitors...": "Escolher Monitores Afetados…", + "All Status Pages": "Todas as Páginas de Status", + "Select status pages...": "Selecionar Páginas de Status…", + "defaultNotificationName": "Meu alerta de {notification} ({number})", + "here": "aqui", + "Required": "Obrigatório", + "Post URL": "Post URL", + "Content Type": "Tipo de Conteúdo", + "webhookFormDataDesc": "{multipart} é bom para PHP. O JSON precisará ser analisado com {decodeFunction}", + "webhookAdditionalHeadersTitle": "Headers Adicionais", + "Webhook URL": "URL do Webhook", + "Application Token": "Token do Aplicativo", + "Server URL": "URL do Servidor", + "Priority": "Prioridade", + "emojiCheatSheet": "Folha de dicas de emojis: {0}", + "Read more": "Ler Mais", + "Method": "Método", + "Body": "Body", + "Headers": "Headers", + "PushUrl": "Enviar URL", + "HeadersInvalidFormat": "Os headers da solicitação não são JSON válidos: ", + "BodyInvalidFormat": "O body da solicitação não é um JSON válido: ", + "Monitor History": "Histórico do Monitor", + "clearDataOlderThan": "Mantenha os dados do histórico do monitor por {0} dias.", + "PasswordsDoNotMatch": "As passwords não coincidem.", + "records": "registros", + "One record": "Um registro", + "steamApiKeyDescription": "Para monitorar um Steam Game Server, você precisa de uma chave Steam Web-API. Pode registrar a chave da API aqui: ", + "Current User": "Usuário Atual", + "topicExplanation": "Tópico MQTT para monitorar", + "successMessage": "Mensagem de Sucesso", + "recent": "Recente", + "Done": "Feito", + "Info": "Informações", + "Security": "Segurança", + "Steam API Key": "Steam API Key", + "Shrink Database": "Encolher Base de Dados", + "Pick a RR-Type...": "Escolha um tipo RR…", + "Pick Accepted Status Codes...": "Escolha Códigos de Status Aceitos…", + "HTTP Options": "Opções HTTP", + "Create Incident": "Criar Incidente", + "Content": "Conteúdo", + "Style": "Estilo", + "info": "informações", + "warning": "aviso", + "danger": "perigo", + "critical": "crítico", + "primary": "primário", + "light": "luz", + "dark": "escuro", + "Post": "Post", + "Created": "Criado", + "Last Updated": "Ultima Atualização", + "Unpin": "Desmarcar", + "Switch to Light Theme": "Alterar para Tema Claro", + "Switch to Dark Theme": "Alterar para Tema Escuro", + "Show Tags": "Mostrar Tags", + "appriseInstalled": "Apprise está instalado.", + "appriseNotInstalled": "Apprise não está instalado. {0}", + "No monitors available.": "Nenhum monitor disponível.", + "Add one": "Adicione um", + "No Monitors": "Sem Monitores", + "Untitled Group": "Grupo sem Título", + "Services": "Serviços", + "Discard": "Descartar", + "Cancel": "Cancelar", + "Powered by": "Powered by", + "Customize": "Customizar", + "Custom CSS": "CSS Customizado", + "Custom Footer": "Footer Customizado", + "deleteStatusPageMsg": "Tem certeza de que deseja excluir esta página de status?", + "Proxies": "Proxies", + "default": "Padrão", + "enabled": "Ativar", + "setAsDefault": "Definir como Padrão", + "deleteProxyMsg": "Tem a certeza que quer excluir este proxy para todos os monitores?", + "setAsDefaultProxyDescription": "Este proxy será ativado por padrão para novos monitores. Você ainda pode desabilitar o proxy separadamente para cada monitor.", + "Valid": "Válido", + "Invalid": "Inválido", + "Remove Token": "Remover Token", + "Running": "Em Execução", + "Not running": "Não está em execução", + "Start": "Iniciar", + "Stop": "Parar", + "Add New Status Page": "Adicionar Nova Página de Status", + "Next": "Próximo", + "No consecutive dashes": "Sem traços consecutivos", + "Slug": "URL", + "Accept characters:": "Caracteres aceites:", + "startOrEndWithOnly": "Iniciar ou terminar apenas com {0}", + "The slug is already taken. Please choose another slug.": "URL já existe. Por favor escolha outro URL.", + "No Proxy": "Sem Proxy", + "Authentication": "Autenticação", + "HTTP Basic Auth": "Autenticação Básica HTTP", + "New Status Page": "Nova Página de Status", + "Page Not Found": "Página Não Encontrada", + "Reverse Proxy": "Proxy Reverso", + "Backup": "Backup", + "About": "Sobre", + "wayToGetCloudflaredURL": "(Download cloudflared de {0})", + "cloudflareWebsite": "Site da Cloudflare", + "Message:": "Mensagem:", + "HTTP Headers": "Headers HTTP", + "Trust Proxy": "Proxy de Confiança", + "Other Software": "Outro Software", + "For example: nginx, Apache and Traefik.": "Por exemplo: nginx, Apache e Traefik.", + "Please read": "Por favor leia", + "Subject:": "Assunto:", + "Valid To:": "Valido para:", + "Days Remaining:": "Dias Restantes:", + "Issuer:": "Emissor:", + "Fingerprint:": "Impressão Digital:", + "No status pages": "Nenhuma página de status", + "Domain Name Expiry Notification": "Notificação de Expiração de Nome de Domínio", + "Proxy": "Proxy", + "Date Created": "Data da Criação", + "Footer Text": "Texto do Footer", + "Show Powered By": "Mostrar Powered By", + "Domain Names": "Nomes de Domínio", + "signedInDisp": "Conectado como {0}", + "signedInDispDisabled": "Autenticação Desativada.", + "RadiusSecret": "Radius Secret", + "RadiusSecretDescription": "Secret compartilhado entre cliente e servidor", + "RadiusCallingStationIdDescription": "Identificador do dispositivo de chamada", + "Certificate Expiry Notification": "Notificação de Expiração do Certificado", + "API Username": "Nome de utilizador da API", + "API Key": "Chave API", + "Using a Reverse Proxy?": "Utilizando um Proxy Reverso?", + "Check how to config it for WebSocket": "Verifique como configurá-lo para WebSocket", + "Steam Game Server": "Steam Game Server", + "Most likely causes:": "Causas mais prováveis:", + "The resource is no longer available.": "O recurso já não está disponível.", + "There might be a typing error in the address.": "Pode haver um erro de digitação no endereço.", + "What you can try:": "O que pode tentar:", + "Retype the address.": "Volte a escrever o endereço.", + "Go back to the previous page.": "Voltar à página anterior.", + "Coming Soon": "Em Breve", + "Connection String": "Linha de Conexão", + "Query": "Query", + "settingsCertificateExpiry": "Validade do Certificado TLS", + "certificationExpiryDescription": "Os monitores HTTPS ativam a notificação quando o certificado TLS expira:", + "Setup Docker Host": "Configuração do Docker Host", + "Connection Type": "Tipo de conexão", + "Docker Daemon": "Docker Daemon", + "deleteDockerHostMsg": "Tem a certeza de querer apagar este docker host para todos os monitores?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Host": "Docker Host", + "Docker Hosts": "Docker Hosts", + "Domain": "Domínio", + "Workstation": "Estação de trabalho", + "Packet Size": "Tamanho do pacote", + "ZohoCliq": "ZohoCliq", + "Bot Token": "Token do Bot", + "wayToGetTelegramToken": "Pode obter o token a partir de {0}.", + "Chat ID": "ID do Chat", + "wayToGetTelegramChatID": "Pode obter o seu ID de chat enviando uma mensagem para o bot e indo a este URL para ver o chat_id:", + "YOUR BOT TOKEN HERE": "O TOKEN DO BOT AQUI", + "disableCloudflaredNoAuthMsg": "Está no modo Sem Autenticação, não é necessária uma palavra-passe.", + "Examples": "Exemplos", + "Long-Lived Access Token": "Token de Acesso de Longa Duração", + "wayToGetLineNotifyToken": "Pode obter o código de acesso a partir de {0}", + "Notification Service": "Serviço de Notificação", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Uma lista de Serviços de Notificação pode ser encontrada em Home Assistant em \"Developer Tools > Services\" pesquisa por \"notificação\" para encontrar o seu dispositivo/nome do telefone.", + "Home Assistant URL": "URL do Home Assistant", + "Event type:": "Tipo de evento:", + "Event data:": "Dados do evento:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Depois de escolher uma ação, por exemplo mudar a cena para onde uma luz RGB é vermelha.", + "Frontend Version": "Versão Frontend", + "Frontend Version do not match backend version!": "Versão Frontend não corresponde à versão backend!", + "backupRecommend": "Por favor, faça o backup do volume ou da pasta de dados (./data/) diretamente.", + "Optional": "Opcional", + "squadcast": "Squadcast", + "recurringInterval": "Intervalo", + "Recurring": "Recurrente", + "strategyManual": "Ativar/Desativar Manualmente", + "warningTimezone": "Está a utilizar o fuso horário do servidor", + "weekdayShortMon": "Segunda", + "weekdayShortTue": "Terça", + "weekdayShortWed": "Quarta", + "weekdayShortThu": "Quinta", + "weekdayShortFri": "Sexta", + "weekdayShortSat": "Sábado", + "weekdayShortSun": "Domingo", + "dayOfWeek": "Dia da Semana", + "dayOfMonth": "Dia do Mês", + "lastDay": "Último Dia", + "lastDay2": "2º Último Dia do Mês", + "lastDay3": "3º Último Dia do Mês", + "lastDay4": "4º Último Dia do Mês", + "No Maintenance": "Nenhuma Manutenção", + "maintenanceStatus-under-maintenance": "Em Manutenção", + "maintenanceStatus-inactive": "Inativo", + "maintenanceStatus-scheduled": "Agendado", + "maintenanceStatus-ended": "Terminado", + "Display Timezone": "Mostrar Fuso horário", + "Server Timezone": "Fuso horário do Servidor", + "statusPageMaintenanceEndDate": "Acabou", + "Maintenance": "Manutenção", + "Specific Monitor Type": "Tipo de Monitor Específico", + "Resend Notification if Down X times consequently": "Reenviar notificação se Off X vezes consequentemente", + "resendEveryXTimes": "Reenviar a cada {0} vezes", + "resendDisabled": "Reenviar desativado", + "Push URL": "Enviar URL", + "webhook": "Webhook", + "topic": "Tema", + "RadiusCalledStationIdDescription": "Identificador do dispositivo chamado", + "Automations can optionally be triggered in Home Assistant:": "As automatizações podem opcionalmente ser ativadas em Home Assistant:", + "or": "ou", + "markdownSupported": "Sintaxe de redução suportada", + "Start of maintenance": "Início da manutenção", + "webhookJsonDesc": "{0} é bom para qualquer servidor HTTP moderno, como Express.js", + "webhookAdditionalHeadersDesc": "Define headers adicionais enviados com o webhook.", + "successMessageExplanation": "Mensagem MQTT que será considerada como sucesso", + "error": "erro", + "Please input title and content": "Por favor insira o título e o conteúdo", + "Hide Tags": "Ocultar Tags", + "Description": "Descrição", + "proxyDescription": "Os proxies devem ser atribuídos a um monitor para funcionar.", + "enableProxyDescription": "Este proxy não afetará as solicitações do monitor até que seja ativado. Você pode controlar temporariamente a desativação do proxy de todos os monitores pelo status de ativação.", + "Don't know how to get the token? Please read the guide:": "Não sabe como obter o token? Por favor, leia o guia:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "A conexão atual pode ser perdida se estiver conectando via Cloudflare Tunnel. Tem certeza de que deseja pará-lo? Digite sua senha atual para confirmar.", + "Docker Container": "Contentor Docker", + "Container Name / ID": "Nome / ID do Contentor", + "supportTelegramChatID": "Chat de Apoio Direto / Grupo / ID do Chat do Canal", + "chatIDNotFound": "O ID do Chat não é encontrado; por favor envie uma mensagem a este bot primeiro", + "trustProxyDescription": "Confiar nos headers 'X-Forwarded-*'. Se quiser obter o IP correto do cliente e o seu Uptime Kuma está por detrás de um proxy como o Nginx ou Apache, deve activá-lo.", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "O Token de Acesso de Longa Duração pode ser criado clicando no nome do seu perfil (em baixo à esquerda) e descendo para o fundo da pagina e depois clicando em Criar Token. ", + "lastDay1": "Último Dia do Mês", + "pauseMaintenanceMsg": "Quer mesmo colocar em pausa?", + "maintenanceStatus-unknown": "Desconhecido", + "needPushEvery": "Deve chamar este URL a cada {0} segundos.", + "pushOptionalParams": "Parâmetros opcionais: {0}", + "Title": "Título", + "User": "Utilizador", + "Installed": "Instalado", + "Not installed": "Não instalado", + "RadiusCalledStationId": "Id da estação chamada", + "RadiusCallingStationId": "Id da estação de chamada", + "default: notify all devices": "padrão: notificar todos os dispositivos", + "Trigger type:": "Tipo de gatilho:", + "telegram": "Telegram", + "Help": "Ajuda", + "Game": "Jogo", + "Monitor": "Monitor | Monitores", + "Default": "Padrão", + "Certificate Chain": "Certificate Chain", + "Show update if available": "Mostrar atualização se disponível", + "Also check beta release": "Verifique também a versão beta", + "Primary Base URL": "URL Base Principal", + "statusMaintenance": "Manutenção", + "Passive Monitor Type": "Tipo de Monitor Passivo", + "Custom": "Personalizar", + "General Monitor Type": "Tipo de Monitor Geral", + "Invert Keyword": "Palavra-chave invertida", + "setupDatabaseChooseDatabase": "Qual é a base de dados que deseja usar?", + "setupDatabaseEmbeddedMariaDB": "Não é necessário configurar nada. Esta imagem Docker possui o MariaDB incorporado e configurado automaticamente para você. O Uptime Kuma se conectará a esta base de dados através de um socket Unix.", + "setupDatabaseMariaDB": "Conecte-se a uma base de dados MariaDB externa. Você precisa configurar as informações de conexão com a base de dados.", + "setupDatabaseSQLite": "Um arquivo de base de dados simples, recomendado para implementações em pequena escala. Antes da versão 2.0.0, o Uptime Kuma utilizava o SQLite como base de dados padrão.", + "dbName": "Nome da Base de Dados", + "settingUpDatabaseMSG": "Configurando a base de dados. Isso pode levar algum tempo, por favor, seja paciente.", + "pushOthers": "Outros", + "Request Timeout": "Tempo Limite da Requisição", + "timeoutAfter": "Tempo limite após {0} segundos", + "Resend Notification if Down X times consecutively": "Reenviar Notificação se estiver em baixo X vezes consecutivas", + "styleElapsedTime": "Tempo decorrido abaixo da barra de pulsação.", + "Home": "Início", + "Expected Value": "Valor Esperado", + "Json Query": "Consulta JSON", + "Cannot connect to the socket server": "Não é possível conectar ao servidor de sockets", + "Reconnecting...": "Reconectando...", + "liquidIntroduction": "A personalização das templates é conseguida através da linguagem de templates Liquid. Por favor consulte [0] para instruções de utilização. As variáveis disponíveis são :", + "pushViewCode": "Como usar o monitor Push? (Ver Código)", + "Host URL": "URL do Host", + "programmingLanguages": "Linguagens de Programação", + "locally configured mail transfer agent": "Agente de entrega de email local", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Entre o hostname do servidor ao qual se quer ligar ou a {localhost} se pretende usar {local_mta}", + "ignoreTLSErrorGeneral": "Ignorar erros TLS/SSL ao ligar", + "filterActive": "Ativo", + "filterActivePaused": "Em Pausa", + "Add New Tag": "Adicionar Nova Etiqueta", + "Search monitored sites": "Pesquisar sites monitorizados", + "templateMsg": "Mensagem da notificação", + "styleElapsedTimeShowNoLine": "Mostrar (Sem Linha)", + "styleElapsedTimeShowWithLine": "Mostrar (Com Linha)", + "statusPageRefreshIn": "Atualizar em: [0]", + "templateHeartbeatJSON": "objeto que descreve o batimento cardíaco", + "templateMonitorJSON": "objeto que descreve o monitor", + "templateLimitedToUpDownCertNotifications": "apenas disponível para notificações UP/DOWN/certificado expirado", + "templateLimitedToUpDownNotifications": "apenas disponível para notificações UP/DOWN", + "-year": "-ano", + "Json Query Expression": "Expressão Json Query", + "ignoredTLSError": "Erros TLS/SSL foram ignorados", + "Clone Maintenance": "", + "monitorTypeGameServer": "Servidor de Jogos", + "monitorTypeSpecial": "Especial", + "mariadbCaCertificateHelptext": "Cole o Certificado da AC em formato PEM para utilizar com certificados autoassinados. Deixe em branco se a sua base de dados utilizar um certificado assinado por uma AC pública.", + "enableSSL": "Ativar SSL/TLS", + "Load More": "Ver Mais", + "mariadbUseSSLHelptext": "Permite utilizar uma ligação encriptada com a sua base de dados. Obrigatório para a maioria das bases de dados na cloud.", + "Loading...": "A carregar…", + "days": "{n} dia | {n} dias", + "hours": "{n} hora | {n} horas", + "mariadbCaCertificateLabel": "Certificado da AC", + "minutes": "{n} minuto | {n} minutos", + "minuteShort": "{n} min | {n} min", + "years": "{n} ano | {n} anos", + "Only retry if status code check fails": "Repete apenas se o código de estado indicar falha", + "No incidents recorded": "Nenhum incidente registado", + "versionIs": "Versão: {version}", + "Pin this incident": "Fixar este incidente", + "now": "agora", + "time ago": "Há {0}" +} diff --git a/src/lang/pt.json b/src/lang/pt.json new file mode 100644 index 0000000..ccaeeac --- /dev/null +++ b/src/lang/pt.json @@ -0,0 +1,249 @@ +{ + "Settings": "Definições", + "Help": "Ajuda", + "New Update": "Nova atualização", + "Language": "Idioma", + "Appearance": "Aspecto", + "Theme": "Tema", + "General": "Geral", + "Game": "Jogo", + "Version": "Versão", + "List": "Lista", + "Add": "Adicionar", + "Quick Stats": "Estatísticas rápidas", + "Up": "Acima", + "Down": "Abaixo", + "Pending": "Pendente", + "statusMaintenance": "Manutenção", + "Maintenance": "Manutenção", + "Unknown": "Desconhecido", + "Reconnecting...": "Reconectando...", + "pauseDashboardHome": "Pausa", + "Pause": "Pausa", + "Name": "Nome", + "Status": "Estado", + "Message": "Mensagem", + "Resume": "Retomar", + "Edit": "Editar", + "Delete": "Remover", + "Current": "Atual", + "Uptime": "Tempo de atividade", + "day": "dia | dias", + "languageName": "Português", + "Primary Base URL": "URL base primário", + "No important events": "Nenhum evento importante", + "Dashboard": "Dashboard", + "Add New Monitor": "Adicionar Novo Monitor", + "Home": "Home", + "Check Update On GitHub": "Verificar por Atualizações no GitHub", + "setupDatabaseChooseDatabase": "Qual banco de dados você gostaria de usar?", + "setupDatabaseEmbeddedMariaDB": "Não é necessario definir. Esta imagem do docker incorporou e configurou automaticamente o MariaDB para você. O Uptime Kuma se conectará a este banco de dados via soquete unix.", + "setupDatabaseMariaDB": "Conecte-se a um banco de dados MariaDB externo. Você precisa definir as informações de conexão do banco de dados.", + "setupDatabaseSQLite": "Um arquivo de banco de dados simples, recomendado para implantações em pequena escala. Antes da v2.0.0, o Uptime Kuma usava SQLite como banco de dados padrão.", + "dbName": "Nome do banco de dados", + "Monitor": "Monitoramento | Monitoramentos", + "hour": "hora", + "-hour": "-hora", + "Response": "Resposta", + "Ping": "Ping", + "-day": "-dia", + "Port": "Porta", + "Cannot connect to the socket server": "Não é possível conectar-se ao socket server", + "URL": "URL", + "upsideDownModeDescription": "Inverter o status. Se o serviço estiver acessível, ele será considerado INATIVO.", + "settingUpDatabaseMSG": "Configurando o banco de dados. Isso pode levar algum tempo, por favor, seja paciente.", + "Passive Monitor Type": "Tipo de Monitoramento Passivo", + "Specific Monitor Type": "Tipo Específico de Monitoramento", + "markdownSupported": "Sintaxe Markdown suportada. Se estiver usando HTML, evite espaços iniciais para evitar problemas de formatação.", + "DateTime": "Data e Hora", + "Cert Exp.": "Expiração do Certificado.", + "Monitor Type": "Tipo de Monitoramento", + "Keyword": "Palavra-chave", + "Invert Keyword": "Inverter Palavra-chave", + "Expected Value": "Valor Esperado", + "Json Query": "Consulta JSON", + "Friendly Name": "Nome Amigável", + "Hostname": "Hostname", + "Heartbeat Interval": "Intervalo de verificação", + "checkEverySecond": "Verificar a cada {0} segundos", + "Host URL": "URL do Host", + "retryCheckEverySecond": "Tentar novamente a cada {0} segundos", + "Accepted Status Codes": "Códigos de Status Aceitos", + "retriesDescription": "Número máximo de tentativas antes de o serviço ser marcado como inativo e uma notificação ser enviada", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Insira o nome do host do servidor ao qual você deseja se conectar ou {localhost} se você pretende usar um {local_mta}", + "Request Timeout": "Tempo limite de requisição", + "timeoutAfter": "Tempo limite após {0} segundos", + "Retries": "Tentativas", + "Heartbeat Retry Interval": "Intervalo entre novas verificações", + "Resend Notification if Down X times consecutively": "Reenviar notificação se houver X falhas consecutivas", + "Advanced": "Avançado", + "resendEveryXTimes": "Reenviar a cada {0} vezes", + "resendDisabled": "Reenvio desativado", + "ignoreTLSError": "Ignorar erros de TLS/SSL para sites HTTPS", + "maxRedirectDescription": "Número máximo de redirecionamentos a seguir. Defina como 0 para desabilitar redirecionamentos.", + "Upside Down Mode": "Modo Invertido", + "locally configured mail transfer agent": "agente de transferência de correio configurado localmente", + "ignoreTLSErrorGeneral": "Ignorar erro de TLS/SSL para conexão", + "Max. Redirects": "Máx. Redirecionamentos", + "General Monitor Type": "Tipo Geral de Monitoramento", + "needPushEvery": "Você deve chamar essa URL a cada {0} segundos.", + "pushOptionalParams": "Parâmetros opcionais: {0}", + "Notifications": "Notificações", + "Setup Notification": "Configurar notificação", + "Light": "Luz", + "Theme - Heartbeat Bar": "Tema - Heartbeat Bar", + "now": "agora", + "-year": "-ano", + "Push URL": "Enviar URL", + "ignoredTLSError": "Erros TLS/SSL foram ignorados", + "Json Query Expression": "Expressão de consulta Json", + "programmingLanguages": "Linguagens de programação", + "Save": "Salvar", + "Not available, please setup.": "Não disponível, por favor configure.", + "pushViewCode": "Como usar o Push monitor? (Ver código)", + "Create your admin account": "Criar conta de administrador", + "Apply on all existing monitors": "Aplicar em todos os monitores existentes", + "Pick Affected Monitors...": "Escolha os monitores afetados…", + "alertWrongFileType": "Por favor, escolha um arquivo JSON.", + "Two Factor Authentication": "Autenticador de dois fatores", + "Tag with this name already exist.": "Já existe uma tag com esse nome.", + "Entry Page": "Página de entrada", + "All Systems Operational": "Todos os sistemas operacionais", + "Add Group": "Adicionar um grupo", + "Add a monitor": "Adicionar um monitoramento", + "None": "Nenhum", + "Change Password": "Mudar Senha", + "Current Password": "Senha Atual", + "New Password": "Nova Senha", + "Repeat New Password": "Repita a nova senha", + "Update Password": "Atualizar senha", + "Enable Auth": "Ativar Auth", + "Disable Auth": "Desativar Auth", + "disableauth.message1": "Você tem certeza que deseja {disableAuth}?", + "disable authentication": "Desativar Autenticação", + "where you intend to implement third-party authentication": "Onde você pretende implementar autenticador de terceiros", + "Please use this option carefully!": "Use essa opção com cuidado!", + "Leave": "Sair", + "Logout": "Deslogar", + "I understand, please disable": "Eu compreendo, por favor desative", + "Yes": "Sim", + "No": "Não", + "Username": "Nome de usuário", + "Password": "Senha", + "Remember me": "Lembrar-me", + "Login": "Entrar", + "add one": "Adicionar um", + "Notification Type": "Tipo de notificação", + "Email": "Email", + "Test": "Testar", + "Certificate Info": "Informação de certificado", + "Resolver Server": "Servidor de resolução", + "Resource Record Type": "Tipo de registro", + "Last Result": "Último resultado", + "Repeat Password": "Repita a senha", + "Import Backup": "Importar backup", + "Export Backup": "Exportar backup", + "Export": "Exportar", + "Import": "Importar", + "respTime": "Tempo de resposta (ms)", + "notAvailableShort": "Não aplicável", + "Create": "Criar", + "Clear Data": "Limpar dados", + "Schedule maintenance": "Agendar manutenção", + "Affected Monitors": "Monitores afetados", + "Start of maintenance": "Início da manutenção", + "All Status Pages": "Todas as páginas de status", + "Select status pages...": "Selecionar páginas de status…", + "alertNoFile": "Escolha um arquivo para importar.", + "Clear all statistics": "Limpar todas as estatísticas", + "Skip existing": "Pular existentes", + "Overwrite": "Sobrescrever", + "Options": "Opções", + "Keep both": "Manter ambas", + "Verify Token": "Verificar token", + "Setup 2FA": "Configurar 2FA", + "Enable 2FA": "Ativar 2FA", + "Disable 2FA": "Desativar 2FA", + "2FA Settings": "Configurações do autenticador", + "filterActive": "Ativo", + "filterActivePaused": "Pausado", + "Active": "Ativo", + "Inactive": "Desativado", + "Token": "Token", + "Show URI": "Mostrar URI", + "Tags": "Tags", + "Add New Tag": "Adicionar nova tag", + "Tag with this value already exist.": "Já existe uma tag com esse valor.", + "color": "Cor", + "value (optional)": "Valor (opcional)", + "Gray": "Cinza", + "Red": "Vermelho", + "Orange": "Laranja", + "Green": "Verde", + "Blue": "Azul", + "Indigo": "Índigo", + "Purple": "Roxo", + "Pink": "Rosa", + "Custom": "Customizado", + "Search...": "Procurar…", + "Search monitored sites": "Procurar sites monitorados", + "Avg. Ping": "Média de ping", + "Avg. Response": "Média do tempo de resposta", + "statusPageNothing": "Não tem nada aqui, adicione um grupo os monitor.", + "statusPageRefreshIn": "Atualize em: {0}", + "No Services": "Sem serviços", + "Partially Degraded Service": "Algumas funcionalidades estão indisponíveis", + "Degraded Service": "Serviço indisponível", + "Edit Status Page": "Editar página de status", + "Go to Dashboard": "Ir para o painel de controle", + "Status Page": "Status Page", + "Status Pages": "Páginas de status", + "Events": "Eventos", + "Confirm": "Confirmar", + "pushOthers": "Outros", + "time ago": "{0} atrás", + "Dark": "Escuro", + "defaultFriendlyName": "Novo Monitor", + "Normal": "Normal", + "Allow indexing": "Permitir indexação", + "Discourage search engines from indexing site": "Desencorajar os mecanismos de busca de indexar o site", + "One record": "Um registro", + "topic": "Tópico", + "successKeyword": "Palavra-chave de sucesso", + "Timezone": "Fuso horário", + "PasswordsDoNotMatch": "As senhas não coincidem.", + "records": "registros", + "Current User": "Usuário Atual", + "recent": "Recente", + "Add New below or Select...": "Adicionar ou selecione um novo abaixo…", + "Bottom": "inferior", + "Default enabled": "Padrão ativado", + "Path": "Caminho", + "Auto": "Auto", + "styleElapsedTime": "Tempo decorrido na barra de pulso", + "monitorTypeDatabase": "Tipo de Monitor de Base de Dados", + "monitorTypeGameServer": "Servidor de Jogos", + "monitorTypeSpecial": "Especial", + "unknownDays": "Dias desconhecidos", + "No incidents recorded": "Nenhum incidente registrado", + "Loading...": "Carregando...", + "Load More": "Carregar Mais", + "Pin this incident": "Fixar esse incidente", + "hours": "{n} hora | {n} horas", + "Monitors": "{n} Monitor | {n} Monitores", + "years": "{n} ano | {n} anos", + "minutes": "{n} minutos | {n} minutos", + "minuteShort": "{n} min | {n} min", + "days": "{n} dia | {n} dias", + "versionIs": "Versão: {version}", + "oracledbConnectionString": "Banco de dados Oracle: {connectionString}", + "enableSSL": "Ativar SSL/TLS", + "mariadbUseSSLHelptext": "Habilite para utilizar uma conexao criptografada com a sua base de dados. Requerida pela maioria das bases de dados na nuvem.", + "mariadbCaCertificateLabel": "Certificado da CA", + "mariadbCaCertificateHelptext": "Cole o certificado da CA no formato PEM para usar certificados auto assinados. Deixe me branco se a sua base de dados usar um certificado assinado por uma CA publica.", + "Connection Timeout": "Tempo limite de conexao", + "Only retry if status code check fails": "Tentar novamente somente se a checagem de codigo falhar", + "Ignore Sec-WebSocket-Accept header": "Ignorar {0} cabecalho", + "wsCodeDescription": "Para mais informacoes sobre o codigo de status, por favor consulte {rfc6455}", + "Subprotocol(s)": "Subprotocolo(s)" +} diff --git a/src/lang/ro.json b/src/lang/ro.json new file mode 100644 index 0000000..7d1915b --- /dev/null +++ b/src/lang/ro.json @@ -0,0 +1,1092 @@ +{ + "languageName": "Română", + "Dashboard": "Panou de Control", + "Help": "Ajutor", + "Appearance": "Aspect", + "Theme": "Temă", + "General": "General", + "Version": "Versiune", + "Check Update On GitHub": "Verifică actualizarea pe GitHub", + "Quick Stats": "Statistici Rapide", + "Up": "Funcțional", + "Down": "Nefuncțional", + "statusMaintenance": "Mentenanță", + "Maintenance": "Mentenanță", + "General Monitor Type": "Monitor de Tip General", + "Passive Monitor Type": "Monitor de Tip Pasiv", + "markdownSupported": "Se acceptă sintaxa Markdown. Dacă folosești HTML, evită spațiile la început pentru a preveni probleme de formatare.", + "Pause": "Pauză", + "Name": "Nume", + "Status": "Status", + "DateTime": "DatăOră", + "Message": "Mesaj", + "No important events": "Niciun eveniment important", + "Resume": "Reia", + "Delete": "Șterge", + "Uptime": "Timpul de funcționare", + "Cert Exp.": "Exp. Cert.", + "Monitor": "Monitor | Monitoare", + "day": "zi | zile", + "-day": "-zi", + "hour": "oră", + "Edit": "Modifică", + "Ping": "Ping", + "Monitor Type": "Tipul Monitorului", + "Keyword": "Cuvânt Cheie", + "Friendly Name": "Nume ușor de recunoscut", + "URL": "URL", + "Hostname": "Hostname", + "Port": "Port", + "Retries": "Reîncercări", + "Heartbeat Retry Interval": "Intervalul Reîncercării Heartbeat-ului", + "Advanced": "Avansat", + "checkEverySecond": "Verifică la fiecare {0} secunde", + "retryCheckEverySecond": "Reîncearcă la fiecare {0} secunde", + "resendEveryXTimes": "Retrimite de {0} ori", + "resendDisabled": "Retrimiterea dezactivată", + "ignoreTLSError": "Ignoră erorile TLS/SSL pentru site-urile web HTTPS", + "upsideDownModeDescription": "Întoarce statusul cu susul în jos. Dacă serviciul este accesibil, este Nefuncțional.", + "Upside Down Mode": "Modul cu Susul in Jos", + "Max. Redirects": "Nr. Max. de Redirecționări", + "Accepted Status Codes": "Coduri de Status Acceptate", + "Push URL": "URL Push", + "needPushEvery": "Acest URL trebuie să fie contactat la fiecare {0} secunde.", + "pushOptionalParams": "Parametrii opționali: {0}", + "Save": "Salvează", + "Notifications": "Notificări", + "Not available, please setup.": "Indisponibil, trebuie configurat.", + "Setup Notification": "Configurați notificarea", + "Light": "Luminos", + "Dark": "Întunecat", + "Auto": "Automat", + "Normal": "Normal", + "Bottom": "Dedesubt", + "None": "Nimic", + "Timezone": "Fus Orar", + "Search Engine Visibility": "Vizibilitate în Motoarele de Căutare", + "Allow indexing": "Permite Indexarea", + "Change Password": "Schimbaţi parola", + "Current Password": "Parola Curentă", + "New Password": "Parolă Nouă", + "Repeat New Password": "Repetați Parola Nouă", + "Update Password": "Actualizați parola", + "Disable Auth": "Dezactivați Autentificarea", + "Enable Auth": "Activați Autentificarea", + "Please use this option carefully!": "Vă rugăm să utilizați această opțiune cu atenție!", + "Logout": "Delogare", + "Leave": "Părăsiți", + "I understand, please disable": "Am luat la cunoștință, dezactivează", + "Confirm": "Confirmă", + "Yes": "Da", + "No": "Nu", + "Username": "Nume de Utilizator", + "Password": "Parolă", + "Remember me": "Ține-mă Minte", + "No Monitors, please": "Fără monitoare, vă rog", + "add one": "adăugați unul", + "Resource Record Type": "Tipul de Înregistrare a Resurselor", + "Create your admin account": "Creați-vă contul de administrator", + "Repeat Password": "Repetă Parola", + "Import Backup": "Importă Backup", + "Export Backup": "Exportă Backup", + "Export": "Exportă", + "Import": "Importă", + "respTime": "Timp de Răspuns (ms)", + "Apply on all existing monitors": "Aplicați pentru toate monitoarele existente", + "Clear Data": "Șterge Datele", + "Events": "Evenimente", + "Heartbeats": "Heartbeat-uri", + "Auto Get": "Obține Automat", + "Affected Monitors": "Monitoare Afectate", + "Pick Affected Monitors...": "Alege Monitoarele Afectate…", + "Start of maintenance": "Începerea Mentenanței", + "All Status Pages": "Toate Paginile de Status", + "Skip existing": "Omite Similare", + "Overwrite": "Suprascrie", + "Options": "Opțiuni", + "Keep both": "Păstrează Ambele", + "Verify Token": "Verifică Token-ul", + "Enable 2FA": "Activează Autentificarea în Doi Pași", + "Disable 2FA": "Dezactivează Autentificarea în Doi Pași", + "2FA Settings": "Setări Autentificare în Doi Pași", + "Active": "Activ", + "Token": "Token", + "Show URI": "Arată URI", + "Tags": "Etichete", + "Tag with this name already exist.": "Eticheta cu acest nume există deja.", + "Tag with this value already exist.": "Eticheta cu această valoare există deja.", + "color": "Culoare", + "value (optional)": "valoare (opțional)", + "Gray": "Gri", + "Red": "Roșu", + "Orange": "Portocaliu", + "Green": "Verde", + "Blue": "Albastru", + "Indigo": "Indigo", + "Purple": "Violet", + "Custom": "Personalizat", + "Entry Page": "Pagină de intrare", + "No Services": "Niciun Serviciu", + "All Systems Operational": "Toate Sistemele Operaționale", + "Partially Degraded Service": "Servicii Parțial Degradate", + "Degraded Service": "Servicii Degradate", + "Add Group": "Adăugare grup", + "Add a monitor": "Adăugați un monitor", + "Edit Status Page": "Modificați Pagina de Status", + "Status Page": "Pagina de Status", + "Status Pages": "Pagini de Status", + "defaultNotificationName": "Alerta {notification} mea ({number})", + "here": "aici", + "Required": "Necesar", + "webhook": "Webhook", + "Post URL": "Postează URL", + "Content Type": "Tipul Conținutului", + "webhookFormDataDesc": "{multipart} este bun pentru PHP. JSON-ul va fi analizat cu {decodeFunction}", + "webhookAdditionalHeadersTitle": "Antete adiționale", + "Webhook URL": "URL-ul Webhook-ului", + "Application Token": "Token-ul Aplicației", + "Server URL": "URL-ul Server-ului", + "Priority": "Prioritate", + "emojiCheatSheet": "Emoji-uri: {0}", + "Read more": "Citeşte mai mult", + "appriseInstalled": "Apprise este instalat.", + "appriseNotInstalled": "Apprise Neinstalat. {0}", + "Method": "Metodă", + "Body": "Corp", + "Headers": "Antete", + "PushUrl": "URL Push", + "BodyInvalidFormat": "Formatul corpului de request nu este valid: ", + "Monitor History": "Istoricul Monitorului", + "PasswordsDoNotMatch": "Parolele nu se potrivesc.", + "One record": "O înregistrare", + "Current User": "Utilizatorul Curent", + "topic": "Subiect", + "topicExplanation": "Subiectul MQTT către monitor", + "successMessage": "Mesaj de Succes", + "successMessageExplanation": "Mesajul MQTT care va fi considerat un succes", + "Done": "Terminat", + "Info": "Informații", + "Security": "Securitate", + "Shrink Database": "Micșorați baza de date", + "Default": "Implicit", + "HTTP Options": "Opțiuni HTTP", + "Title": "Titlu", + "Content": "Conținut", + "Style": "Stil", + "info": "informații", + "warning": "avertizare", + "danger": "pericol", + "error": "eroare", + "critical": "critic", + "dark": "întunecat", + "Post": "Postează", + "Last Updated": "Ultima actualizare", + "Unpin": "Anulați fixarea", + "Switch to Light Theme": "Schimbați la Tema Luminoasă", + "Show Tags": "Afișați Etichetele", + "Hide Tags": "Ascundeți Etichetele", + "Description": "Descriere", + "No monitors available.": "Nu există monitoare disponibile.", + "Discard": "Eliminați", + "Cancel": "Anulați", + "Powered by": "Cu ajutorul", + "Customize": "Personalizați", + "Custom Footer": "Subsol Personalizat", + "Custom CSS": "CSS Personalizat", + "deleteStatusPageMsg": "Sigur doriți să ștergeți această pagină de status?", + "Proxies": "Proxy-uri", + "default": "Implicit", + "enabled": "Activat", + "setAsDefault": "Setați Ca Implicit", + "deleteProxyMsg": "Sigur doriți să ștergeți acest proxy pentru toate monitoarele?", + "Certificate Chain": "Lanț de certificate", + "Valid": "Valabil", + "Invalid": "Nevalabil", + "User": "Utilizator", + "Installed": "Instalat", + "Running": "Operează", + "Not running": "Nu rulează", + "Remove Token": "Elimină token-ul", + "Start": "Start", + "Stop": "Stop", + "Add New Status Page": "Adăugați o nouă pagină de status", + "Slug": "Slug", + "startOrEndWithOnly": "Începe sau se termină numai cu {0}", + "No consecutive dashes": "Fără cratime consecutive", + "Next": "Următorul", + "No Proxy": "Fără Proxy", + "Authentication": "Autentificare", + "HTTP Basic Auth": "Autentificare de bază HTTP", + "New Status Page": "Pagină de Status Nouă", + "Page Not Found": "Pagina nu a fost gasită", + "Backup": "Backup", + "About": "Despre", + "wayToGetCloudflaredURL": "(Descarcă sub Cloudflare de la {0})", + "cloudflareWebsite": "Website-ul Cloudflare", + "Message:": "Mesaj:", + "HTTP Headers": "Antete HTTP", + "Trust Proxy": "Proxy de încredere", + "Other Software": "Alt Software", + "For example: nginx, Apache and Traefik.": "De exemplu: nginx, Apache și Traefik.", + "Please read": "Te rog citește", + "Subject:": "Subiect:", + "Valid To:": "Valabil Pâna la:", + "Days Remaining:": "Zile rămase:", + "Issuer:": "Emitent:", + "Fingerprint:": "Amprentă:", + "No status pages": "Nicio pagină de status", + "Domain Name Expiry Notification": "Notificare de expirare a numelui de domeniu", + "Proxy": "Proxy", + "Date Created": "Data Creării", + "Footer Text": "Text de subsol", + "Show Powered By": "Arată ”Cu ajutorul”", + "Reverse Proxy": "Proxy invers", + "Domain Names": "Nume de domeniu", + "signedInDisp": "Conectat ca {0}", + "signedInDispDisabled": "Autentificare Dezactivată.", + "RadiusSecret": "Secret Radius", + "RadiusSecretDescription": "Secret împărtășit cu client-ul și server-ul", + "RadiusCalledStationId": "ID-ul Stației Contactate", + "RadiusCalledStationIdDescription": "Identificatorul serviciului apelat", + "RadiusCallingStationId": "ID-ul Stației Contactante", + "RadiusCallingStationIdDescription": "Identificatorul dispozitivului contactant", + "API Username": "Nume de utilizator al API-ului", + "API Key": "Cheie API", + "Also check beta release": "Verifică și actualizările beta", + "Using a Reverse Proxy?": "Utilizați un proxy invers?", + "Check how to config it for WebSocket": "Verificați cum să-l configurați pentru WebSocket", + "Steam Game Server": "Server de Joc Steam", + "Most likely causes:": "Cea mai probabilă cauză:", + "There might be a typing error in the address.": "Ar putea exista o eroare de scriere în adresă.", + "What you can try:": "Ce poți încerca:", + "Retype the address.": "Rescrie adresa.", + "Go back to the previous page.": "Reveniți la pagina anterioară.", + "Coming Soon": "În curând", + "Connection String": "Șirul de conexiune", + "Query": "Interogare", + "settingsCertificateExpiry": "Expirarea certificatului TLS", + "Setup Docker Host": "Configurați Docker Host", + "Connection Type": "Tipul Conexiunii", + "Docker Daemon": "Docker Daemon", + "deleteDockerHostMsg": "Sigur doriți să ștergeți acest docker host pentru toate monitoarele?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Container Docker", + "Container Name / ID": "Numele Container-ului / ID", + "Docker Host": "Gazdă Docker", + "Domain": "Domeniu", + "Workstation": "Stație de lucru", + "Packet Size": "Mărime Pachet", + "telegram": "Telegram", + "ZohoCliq": "ZohoCliq", + "Bot Token": "Token Robot", + "wayToGetTelegramToken": "Puteți obține un token de la {0}.", + "Chat ID": "ID-ul Chat-ului", + "YOUR BOT TOKEN HERE": "TOKEN-UL TĂU DE BOT AICI", + "chatIDNotFound": "ID-ul de chat nu a fost găsit; Vă rugăm să trimiteți mai întâi un mesaj acestui bot", + "disableCloudflaredNoAuthMsg": "Sunteți în modul No Auth, nu este necesară o parolă.", + "wayToGetLineNotifyToken": "Puteți obține un token de acces de la {0}", + "Examples": "Exemple", + "Home Assistant URL": "URL-ul de la Home Assistant", + "Long-Lived Access Token": "Token de acces cu durata de viață mare", + "default: notify all devices": "implicit: notifică toate dispozitivele", + "Automations can optionally be triggered in Home Assistant:": "Automatizările pot fi declanșate opțional în Home Assistant:", + "Trigger type:": "Tipul Declanșatorului:", + "Event type:": "Tipul Evenimentului:", + "Event data:": "Datele Evenimentului:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Apoi alegeți o acțiune, de exemplu comutați scena în care o lumină RGB este roșie.", + "Frontend Version": "Versiune Frontend", + "Frontend Version do not match backend version!": "Versiunea Frontend-ului nu este aceeași cu cea a backend-ului!", + "backupRecommend": "Vă rugăm să faceți backup direct pentru volumul sau folderul de date (./data/).", + "Optional": "Opțional", + "squadcast": "Squadcast", + "or": "sau", + "recurringInterval": "Perioadă", + "Recurring": "Recurentă", + "strategyManual": "Activ/Inactiv Manual", + "warningTimezone": "Folosește fusul orar al server-ului", + "weekdayShortMon": "Luni", + "weekdayShortTue": "Marți", + "weekdayShortWed": "Miercuri", + "weekdayShortThu": "Joi", + "weekdayShortFri": "Vineri", + "weekdayShortSat": "Sâmbătă", + "weekdayShortSun": "Duminică", + "dayOfWeek": "Ziua săptămânii", + "dayOfMonth": "Ziua lunii", + "lastDay": "Ultima zi", + "lastDay1": "Ultima zi a Lunii", + "lastDay2": "Penultima zi a Lunii", + "lastDay3": "Antepenultima zi a Lunii", + "No Maintenance": "Fără mentenanță", + "pauseMaintenanceMsg": "Sunteți sigur că vreți sa puneți pe pauză?", + "maintenanceStatus-under-maintenance": "În mentenanță", + "maintenanceStatus-inactive": "Inactiv", + "maintenanceStatus-scheduled": "Planificat", + "maintenanceStatus-ended": "Terminat", + "maintenanceStatus-unknown": "Necunoscut", + "Server Timezone": "Fusul Orar al Server-ului", + "statusPageMaintenanceEndDate": "Sfârșit", + "IconUrl": "URL-ul pictogramei", + "Enable DNS Cache": "(Depreciat) Activează DNS Cache pentru monitoarele HTTP(s)", + "Enable": "Activează", + "Disable": "Dezactivează", + "Effective Date Range": "Interval în care se aplică (Opțional)", + "Schedule Maintenance": "Planifică Mentenanță", + "Date and Time": "Dată și Oră", + "DateTime Range": "Interval DatăOră", + "loadingError": "Nu se pot prelua datele, vă rugăm să încercați din nou mai târziu.", + "plugin": "Plugin | Plugin-uri", + "install": "Instalează", + "installing": "Instalare", + "uninstall": "Dezinstalează", + "confirmUninstallPlugin": "Sigur doriți să dezinstalați acest plugin?", + "smtp": "Email (SMTP)", + "secureOptionNone": "Niciunul / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (456)", + "From Email": "De la Email", + "emailCustomSubject": "Subiect Personalizat", + "To Email": "La Email", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "URL-ul Webhook-ului Discord", + "Bot Display Name": "Numele Robotului", + "Prefix Custom Message": "Prefix Personalizat", + "Hello @everyone is...": "Bună {'@'}toată lumea este…", + "wayToGetTeamsURL": "Puteți afla cum să creați o adresă URL webhook {0}.", + "wayToGetZohoCliqURL": "Puteți afla cum să creați o adresă URL webhook {0}.", + "needSignalAPI": "Trebuie să aveți un client signal cu API-ul REST.", + "Number": "Număr", + "Recipients": "Destinatari", + "Access Token": "Token de acces", + "Channel access token": "Token de acces al canalului", + "Line Developers Console": "Consola pentru dezvoltatori de linie", + "lineDevConsoleTo": "Consola Dezvoltatorilor de linie - {0}", + "Basic Settings": "Setări de Bază", + "User ID": "ID-ul Utilizatorului", + "Messaging API": "API-ul pentru Mesagerie", + "Icon URL": "URL-ul Pictogramei", + "dataRetentionTimeError": "Perioada de retenție trebuie să fie 0 sau mai mare", + "infiniteRetention": "Setați la „0” pentru retenție pe perioadă nedeterminată.", + "confirmDeleteTagMsg": "Sigur doriți să ștergeți această etichetă? Monitoarele asociate cu această etichetă nu vor fi șterse.", + "enableGRPCTls": "Permiteți trimiterea cererii gRPC cu conexiune TLS", + "grpcMethodDescription": "Numele metodei este convertit în format camelCase, cum ar fi sayHello, check etc.", + "deleteMonitorMsg": "Sigur doriți să ștergeți acest monitor?", + "deleteMaintenanceMsg": "Sigur doriți să ștergeți această mentenanță?", + "dnsPortDescription": "Port-ul server-ului DNS. Este implicit 53. Puteți schimba acest port oricând.", + "resolverserverDescription": "Cloudflare este server-ul implicit. Puteți schimba asta oricând.", + "rrtypeDescription": "Selectați tipul RR pe care doriți să îl monitorizați", + "pauseMonitorMsg": "Sigur vrei să pui pe pauză?", + "clearEventsMsg": "Sigur doriți să ștergeți toate evenimentele pentru acest monitor?", + "clearHeartbeatsMsg": "Sigur doriți să ștergeți toate heartbeat-urile pentru acest monitor?", + "confirmClearStatisticsMsg": "Sigur doriți să ștergeți TOATE statisticile?", + "confirmImportMsg": "Sigur doriți să importați copia de rezervă? Vă rugăm să verificați că ați selectat opțiunea corectă de import.", + "twoFAVerifyLabel": "Va rugăm să introduceți token-ul pentru a verifica Autentificarea în Doi Pași:", + "tokenValidSettingsMsg": "Token-ul este valid! Acum puteți salva setările legate de Autentificarea în Doi Pași.", + "confirmEnableTwoFAMsg": "Sigur doriți să activați Autentificarea în Doi Pași?", + "recurringIntervalMessage": "Rulează o dată pe zi | Rulează o dată la {0} zile", + "affectedMonitorsDescription": "Selectați monitoarele care sunt afectate mentenanța curentă", + "atLeastOneMonitor": "Selectați cel puțin un monitor afectat", + "passwordNotMatchMsg": "Parola repetată nu se potrivește.", + "notificationDescription": "Notificările trebuie alocate unui monitor pentru a funcționa.", + "backupDescription": "Puteți face backup pentru toate monitoarele și notificările într-un fișier JSON.", + "backupDescription2": "Notă: istoricul și datele evenimentelor nu sunt incluse.", + "endpoint": "Punct final", + "octopushAPIKey": "„Cheia API” din credențialele HTTP API în panoul de control", + "octopushLogin": "„Logare” din credențialele HTTP API în panoul de control", + "promosmsLogin": "Nume de conectare API", + "promosmsPassword": "Parola API", + "pushoversounds pushover": "Pushover (implicit)", + "pushoversounds bike": "Bicicletă", + "pushoversounds bugle": "Goarnă", + "pushoversounds cashregister": "Casă de marcat", + "pushoversounds classical": "Clasic", + "pushoversounds cosmic": "Cosmic", + "pushoversounds falling": "Cădere", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Sosire", + "pushoversounds intermission": "Pauză", + "pushoversounds magic": "Magie", + "pushoversounds mechanical": "Mecanic", + "pushoversounds tugboat": "Remorcher", + "pushoversounds alien": "Alarmă Extraterestră (lung)", + "pushoversounds climb": "Urcare (lung)", + "pushoversounds echo": "Ecou Pushover (lung)", + "pushoversounds updown": "Sus Jos (lung)", + "pushoversounds vibrate": "Doar Vibrații", + "pushoversounds none": "Niciunul (silențios)", + "pushyAPIKey": "Cheie API secretă", + "pushyToken": "Token dispozitiv", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "slack": "Slack", + "rocket.chat": "Rocket.Chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "Pushy by Techulus", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Suportă 50+ Servicii de Notificare)", + "GoogleChat": "Google Chat (Doar Google Workspace)", + "pushbullet": "Pushbullet", + "Kook": "Kook", + "Guild ID": "ID-ul Breslei (Guild-ului)", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "Cheie Utilizator", + "Device": "Dispozitiv", + "Message Title": "Titlu Mesaj", + "Notification Sound": "Sunet Notificare", + "More info on:": "Mai multe informații la: {0}", + "pushoverDesc2": "Dacă doriți să trimiteți notificări către diferite dispozitive, completați câmpul Dispozitiv.", + "Settings": "Setări", + "New Update": "Actualizare nouă", + "Language": "Limbă", + "Game": "Joc", + "Primary Base URL": "URL-ul de Bază", + "List": "Listă", + "Add": "Adaugă", + "Add New Monitor": "Adaugă Monitor Nou", + "Pending": "În așteptare", + "Unknown": "Necunoscut", + "Specific Monitor Type": "Monitor de Tip Specific", + "pauseDashboardHome": "Pauză", + "Current": "Curent", + "-hour": "-oră", + "Response": "Răspuns", + "Heartbeat Interval": "Interval Heartbeat", + "Resend Notification if Down X times consequently": "Retrimite Notificarea dacă se Întâmpină Eroarea de X ori consecutiv", + "retriesDescription": "Numărul maxim de reîncercări înainte ca serviciul să fie marcat offline și să se trimită o notificare", + "maxRedirectDescription": "Numărul maxim de redirecționări permise. Setați la 0 pentru a dezactiva redirecționările.", + "Theme - Heartbeat Bar": "Temă - Bara Heartbeat", + "Discourage search engines from indexing site": "Descurajează motoarele de căutare să indexeze acest site", + "disableauth.message1": "Sigur doriți să {disableAuth}?", + "disable authentication": "dezactivați autentificarea", + "disableauth.message2": "Este conceput pentru scenarii {intendThirdPartyAuth} în fața Uptime Kuma, cum ar fi Cloudflare Access, Authelia sau alte mecanisme de autentificare.", + "where you intend to implement third-party authentication": "unde intenționați să implementați autentificarea terță parte", + "Login": "Logare", + "Notification Type": "Tipul Notificării", + "Email": "Email", + "Test": "Test", + "Certificate Info": "Informațiile Certificatului", + "Resolver Server": "Server-ul de Rezolvare", + "Last Result": "Ultimul Rezultat", + "notAvailableShort": "N/A", + "Default enabled": "Implicit activat", + "Create": "Creează", + "Schedule maintenance": "Programează Mentenanță", + "Select status pages...": "Selectați paginile de status…", + "alertNoFile": "Vă rugăm să selectați un fișier de importat.", + "alertWrongFileType": "Vă rugăm să selectați un fișier JSON.", + "Clear all statistics": "Șterge toate Statisticile", + "Setup 2FA": "Configurați Autentificarea în Doi Pași", + "Two Factor Authentication": "Autentificare în Doi Pași", + "Inactive": "Inactiv", + "Add New below or Select...": "Adăugați Nou mai jos sau selectați…", + "Pink": "Roz", + "Search...": "Căutare…", + "Avg. Ping": "Ping Mediu", + "Avg. Response": "Timp de Răspuns Mediu", + "statusPageNothing": "Nimic aici, vă rugăm să adăugați un grup sau un monitor.", + "Go to Dashboard": "Accesați Tabloul de Control", + "webhookJsonDesc": "{0} este bun pentru orice server HTTP modern cum ar fi Express.js", + "webhookAdditionalHeadersDesc": "Setează antete adiționale trimise împreună cu webhook-ul. Fiecare antet ar trebui definit ca și cheie/valoare JSON.", + "HeadersInvalidFormat": "Formatul header-urilor de request nu este valid: ", + "clearDataOlderThan": "Păstrează istoricul monitorului pentru {0} zile.", + "records": "înregistrări", + "steamApiKeyDescription": "Pentru monitorizarea unui server de joc Steam aveți nevoie de o cheie Steam Web-API. Vă puteți înregistra cheia API aici: ", + "recent": "Recent", + "Steam API Key": "Cheia API Steam", + "Pick a RR-Type...": "Alegeți un Tip-RR…", + "Pick Accepted Status Codes...": "Alegeți Codurile de Status Acceptate…", + "Create Incident": "Creează un Incident", + "primary": "principal", + "light": "luminos", + "Please input title and content": "Vă rugăm să introduceți titlul și conținutul", + "Created": "Creat", + "Switch to Dark Theme": "Schimbați la Tema Întunecată", + "Add one": "Adăugați unul", + "No Monitors": "Niciun monitor", + "Untitled Group": "Grup fără nume", + "Services": "Servicii", + "proxyDescription": "Proxy-urile trebuie să fie atribuite unui monitor pentru a funcționa.", + "enableProxyDescription": "Acest proxy nu va avea efect asupra solicitărilor monitoarelor până când nu este activat. Puteți controla dezactivarea temporară a proxy-ului de pe toate monitoarele prin starea de activare.", + "setAsDefaultProxyDescription": "Acest proxy va fi activat în mod implicit pentru monitoare noi. Puteți dezactiva în continuare proxy-ul separat pentru fiecare monitor.", + "Not installed": "Neinstalat", + "Accept characters:": "Acceptă caractere:", + "The slug is already taken. Please choose another slug.": "Slugul este deja luat. Vă rugăm să alegeți un alt slug.", + "Don't know how to get the token? Please read the guide:": "Nu știi cum să obții token-ul? Te rog citește acest ghid:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Conexiunea curentă s-ar putea pierde dacă ești în proces de conectare printr-un tunel Cloudflare. Ești sigur că vrei să îl oprești? Tastează-ți parola curentă pentru a confirma.", + "Certificate Expiry Notification": "Notificare de expirare a certificatului", + "Show update if available": "Arată actualizarea dacă e disponibilă", + "The resource is no longer available.": "Această resursă nu mai este disponibilă.", + "certificationExpiryDescription": "Monitoarele HTTPS declanșează notificarea când certificatul TLS expiră în:", + "Docker Hosts": "Gazde Docker", + "supportTelegramChatID": "Suport Mesaje Directe / Grup / ID-ul Canalului de Text", + "wayToGetTelegramChatID": "Puteți obține ID-ul chat-ului prin trimiterea unui mesaj către robot și accesând acest URL pentru a vedea chat_id:", + "trustProxyDescription": "Aveți incredere in antetele 'X-Forwarded-*'. Dacă doriți să obțineți IP-ul corect al client-ului și Uptime Kuma este în spatele unui proxy, cum ar fi Nginx sau Apache, ar trebui să activați acest lucru.", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Token-ul de acces cu durată de viață mare poate fi creat dând click pe numele profilului dvs. (stânga jos) și derulând în jos, apoi faceți clic pe Creare Token. ", + "Notification Service": "Serviciu de Notificări", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "O listă de servicii de notificare poate fi găsită în Home Assistant, sub „Instrumente pentru dezvoltatori > Servicii”, căutați „notificare” pentru a găsi numele dispozitivului/telefonului dvs.", + "backupOutdatedWarning": "Depășit: Pentru că au fost adăugate multe funcționalități, iar această funcție de backup este neîntreținută, nu poate genera sau restaura un backup complet.", + "lastDay4": "Răsantepenultima zi a Lunii", + "Display Timezone": "Afișează Fusul Orar", + "dnsCacheDescription": "Este posibil să nu funcționeze în unele medii IPv6, dezactivați-l dacă întâmpinați probleme.", + "Single Maintenance Window": "Fereastră unică de timp pentru mentennanță", + "Maintenance Time Window of a Day": "Fereastra de timp alocată pentru mentenanță dintr-o zi", + "uninstalling": "Dezinstalare", + "Ignore TLS Error": "Ignoră erorile TLS", + "wayToGetDiscordURL": "Puteți obține acest lucru mergând la Setări server -> Integrări -> Vizualizați Webhooks -> Webhook nou", + "wayToCheckSignalURL": "Puteți verifica această adresă URL pentru a vedea cum să configurați una:", + "wayToGetLineChannelToken": "Mai întâi accesați {0}, creați un furnizor și un canal (API-ul de mesagerie), apoi puteți obține simbolul de acces la canal și ID-ul utilizatorului din elementele de meniu menționate mai sus.", + "aboutIconURL": "Puteți furniza un link către o imagine în „Adresa URL a pictogramei” pentru a înlocui fotografia de profil implicită. Nu va fi folosit dacă este setat Icon Emoji.", + "Channel Name": "Nume Canal", + "aboutMattermostChannelName": "Puteți înlocui canalul implicit pe care postează Webhook-ul introducând numele canalului în câmpul „Nume canal”. Acest lucru trebuie să fie activat în setările Mattermost Webhook. Ex: #alt-canal", + "acceptedStatusCodesDescription": "Selectați codurile de status care sunt considerate un răspuns de succes.", + "deleteNotificationMsg": "Sigur doriți să ștergeți această notificare pentru toate monitoarele?", + "enableDefaultNotificationDescription": "Această notificare va fi activată în mod implicit pentru monitoare noi. Puteți dezactiva în continuare notificarea separat pentru fiecare monitor.", + "importHandleDescription": "Alegeți „Omite Similare” dacă doriți să omiteți fiecare monitor sau notificare cu același nume. „Suprascrie” va șterge fiecare monitor și notificare existente.", + "confirmDisableTwoFAMsg": "Sigur doriți să dezactivați Autentificarea în Doi Pași?", + "affectedStatusPages": "Afișați acest mesaj de mentenanță pe paginile de status selectate", + "keywordDescription": "Căutați cuvântul cheie în HTML simplu sau răspuns JSON. Căutarea face distincție între majuscule și minuscule.", + "backupDescription3": "Datele importante, cum ar fi tokenele de notificare, sunt incluse în fișierul exportat; vă rugăm să păstrați exportul în siguranță.", + "pushoversounds pianobar": "Bar cu pian", + "pushoversounds siren": "Sirenă", + "pushoversounds spacealarm": "Alarmă Spațială", + "pushoversounds persistent": "Persistent (lung)", + "gotify": "Gotify", + "clicksendsms": "ClickSend SMS", + "wayToGetKookBotToken": "Creați o aplicație și obțineți tokenul dvs. robot la {0}", + "wayToGetKookGuildID": "Activați „Modul dezvoltator” în setarea Kook și faceți clic dreapta pe breaslă pentru a obține ID-ul acesteia", + "pushoverDesc1": "Prioritate de urgență (2) are o pauză implicită de 30 de secunde între reîncercări și expiră după o oră.", + "Setup Proxy": "Configurați proxy", + "Proxy Protocol": "Protocol Proxy", + "For safety, must use secret key": "Pentru siguranță, trebuie să utilizați cheia secretă", + "promosmsTypeFlash": "SMS FLASH - Mesajul va fi afișat automat pe dispozitivul destinatarului. Valabil doar către destinatarii din Polonia.", + "promosmsTypeEco": "SMS ECO - ieftin dar încet și deseori supraîncărcat. Valabil doar către destinatarii din Polonia.", + "SMS Type": "Tip SMS", + "checkPrice": "Verifică {0} prețurile:", + "apiCredentials": "Credențiale API", + "octopushLegacyHint": "Folosiți versiunea veche a Octopush (2011-2020) sau versiunea nouă?", + "Check octopush prices": "Verifică prețurile octopush {0}.", + "octopushPhoneNumber": "Număr de telefon (format internațional, ex : +33612345678)", + "LunaSea Device ID": "ID Dispozitiv LunaSea", + "octopushTypePremium": "Premium (Rapid - recomandat pentru alertare)", + "octopushTypeLowCost": "Cost scăzut (Încet - câteodată este blocat de operator)", + "octopushSMSSender": "Numele expeditorului SMS : 3-11 caractere alfanumerice și spațiu (a-zA-Z0-9)", + "Read more:": "Citește mai mult: {0}", + "promosmsPhoneNumber": "Număr de telefon (pentru destinatarii din Polonia poți sări peste prefixele zonei)", + "Uptime Kuma URL": "URL Uptime Kuma", + "Feishu WebHookUrl": "WebHookURL Feishu", + "matrixHomeserverURL": "URL server personal (cu http(s):// și port opțional)", + "AccessKeyId": "ID AccessKey", + "Platform": "Platformă", + "Device Token": "Token Dispozitiv", + "promosmsAllowLongSMS": "Permite SMS lung", + "Apprise URL": "URL Apprise", + "Example:": "Exemplu: {0}", + "Status:": "Status: {0}", + "Strategy": "Strategie", + "Free Mobile User Identifier": "Identificator utilizator Free Mobile", + "Free Mobile API Key": "Cheie API Free Mobile", + "Proto Service Name": "Nume serviciu Proto", + "Enable TLS": "Activați TLS", + "Economy": "Economie", + "SMSManager API Docs": "Documente API SMSManager", + "Gateway Type": "Tip gateway", + "Base URL": "URL de Bază", + "PhoneNumbers": "NumereTelefon", + "Sms template must contain parameters: ": "Șablonul SMS trebuie sa conțină parametrii: ", + "TemplateCode": "CodȘablon", + "SendKey": "SendKey", + "Proto Method": "Metodă Proto", + "Proto Content": "Conținut Proto", + "Lowcost": "Cost scăzut", + "You can divide numbers with": "Puteți împărți numerele cu", + "Huawei": "Huawei", + "Proxy Server": "Server Proxy", + "Proxy server has authentication": "Serverul Proxy are autentificare", + "WebHookUrl": "WebHookURL", + "aboutWebhooks": "Mai multe detalii despre Webhooks pe: {0}", + "signalImportant": "IMPORTANT: Nu puteți amesteca grupuri și numere în destinatari!", + "Bark Group": "Grup Bark", + "Bark Sound": "Sunet Bark", + "SecretKey": "CheieSecretă", + "Retry": "Reîncercare", + "Topic": "Subiect", + "WeCom Bot Key": "Cheie Bot WeCom", + "successKeywordExplanation": "Cuvântul cheie MQTT care va fi considerat succes", + "successKeyword": "Cuvânt Cheie Succes", + "pushViewCode": "Cum se utilizează monitorul Push? (Vizualizare cod)", + "setupDatabaseChooseDatabase": "Ce bază de date ați dori să utilizați?", + "setupDatabaseMariaDB": "Conectați-vă la o bază de date externă MariaDB. Trebuie să setați informațiile de conectare la baza de date.", + "setupDatabaseSQLite": "Un fișier de bază de date simplu, recomandat pentru implementări la scară mică. Înainte de v2.0.0, Uptime Kuma folosea SQLite ca bază de date implicită.", + "dbName": "Numele bazei de date", + "pagertreeCritical": "Critic", + "setupDatabaseEmbeddedMariaDB": "Nu trebuie să setați nimic. Această imagine Docker a încorporat și configurat automat MariaDB pentru dvs. Uptime Kuma se va conecta la această bază de date prin socket Unix.", + "statusPageSpecialSlugDesc": "Slug special {0}: această pagină va fi afișată atunci când nu este furnizat niciun slug", + "chromeExecutableDescription": "Pentru utilizatorii Docker, dacă Chromium nu este încă instalat, instalarea și afișarea rezultatului testului poate dura câteva minute. Este nevoie de 1 GB de spațiu pe disc.", + "apiKeyAddedMsg": "Cheia dvs. API a fost adăugată. Vă rugăm să rețineți că nu va fi afișată din nou.", + "pagertreeIntegrationUrl": "URL Integrare", + "Reset Token": "Resetare Token", + "telegramSendSilentlyDescription": "Trimite mesajul silențios. Utilizatorii vor primi o notificare fără sunet.", + "telegramProtectContentDescription": "Dacă este activat, mesajele boților din Telegram vor fi protejate de redirecționare și salvare.", + "sameAsServerTimezone": "Identic ca fusul orar al serverului", + "settingUpDatabaseMSG": "Configurarea bazei de date. Poate dura ceva timp, vă rugăm să aveți răbdare.", + "Search monitored sites": "Căutați site-uri monitorizate", + "pushoverMessageTtl": "Mesaj TTL (secunde)", + "matrixDesc1": "Puteți găsi ID-ul intern al camerei uitându-vă în secțiunea avansată a setărilor camerei din clientul dvs. Matrix. Ar trebui să arate ca !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Este foarte recomandat să creați un utilizator nou și să nu utilizați propriul simbol de acces al utilizatorului Matrix, deoarece acesta va permite accesul deplin la contul dvs. și la toate camerele la care v-ați alăturat. În schimb, creați un utilizator nou și invitați-l doar în sala în care doriți să primiți notificarea. Puteți obține tokenul de acces rulând {0}", + "Notify Channel": "Canal de Notificare", + "aboutNotifyChannel": "Canalul de notificare va declanșa o notificare pe desktop sau pe mobil pentru toți membrii canalului, indiferent dacă disponibilitatea acestora este setată activ sau absent.", + "promosmsSMSSender": "Nume Expeditor SMS: nume preînregistrat sau una dintre cele standard: InfoSMS , SMS Info, MaxSMS, INFO, SMS", + "Internal Room Id": "ID cameră internă", + "setup a new monitor group": "configurați un nou grup de monitoare", + "smtpDkimSettings": "Setări DKIM", + "aboutChannelName": "Introduceți numele canalului în câmpul {0} Channel Name (Nume canal) dacă doriți să faceți bypass la canalul Webhook. Ex: #alt-canal", + "aboutKumaURL": "Dacă lăsați necompletat câmpul Uptime Kuma URL, va fi implicit pagina de proiect GitHub.", + "smtpDkimDesc": "Vă rugăm să verificați Nodemailer DKIM {0} pentru utilizare.", + "smtpDkimHashAlgo": "Algoritmul Hash (Opțional)", + "smtpDkimheaderFieldNames": "Chei Header de semnat (Opțional)", + "smtpDkimskipFields": "Chei Header care nu se semnează (Opțional)", + "wayToGetPagerDutyKey": "Puteți obține acest lucru accesând Service -> Service Directory -> (Selectați un serviciu) -> Integrations -> Add integration. Aici puteți căuta \"Events API V2\". Mai multe informații {0}", + "wayToGetClickSendSMSToken": "Puteți obține utilizatorul API și cheia API de la {0} .", + "Custom Monitor Type": "Tip de monitor personalizat", + "Google Analytics ID": "ID Google Analytics", + "leave blank for default subject": "lăsați necompletat pentru subiectul implicit", + "emailCustomBody": "Corp personalizat", + "pushDeerServerDescription": "Lăsați necompletat pentru a utiliza serverul oficial", + "smseagleRecipientType": "Tipul destinatarului", + "ntfy Topic": "Subiect ntfy", + "pushOthers": "Alții", + "programmingLanguages": "Limbaje de programare", + "statusPageRefreshIn": "Reîncărcă în: {0}", + "templateMsg": "mesajul notificării", + "templateLimitedToUpDownCertNotifications": "disponibil numai pentru notificările de expirare UP/DOWN/Certificat", + "templateLimitedToUpDownNotifications": "disponibil numai pentru notificările UP/DOWN", + "templateHeartbeatJSON": "object care descrie heartbeat-ul", + "templateMonitorJSON": "object care descrie monitorul", + "noDockerHostMsg": "Nu este disponibil. Configurați mai întâi un host Docker.", + "DockerHostRequired": "Vă rugăm să setați Host Docker pentru acest monitor.", + "tailscalePingWarning": "Pentru a utiliza monitorul Tailscale Ping, trebuie să instalați Uptime Kuma fără Docker și, de asemenea, să instalați clientul Tailscale pe server.", + "endDateTime": "Dată/Oră de final", + "cronSchedule": "Program: ", + "enableNSCD": "Activare NSCD (Name Service Cache Daemon) pentru memorarea în cache a tuturor solicitărilor DNS", + "chromeExecutableAutoDetect": "Detecție automată", + "Edit Maintenance": "Modifică Mentenanța", + "Clone Monitor": "Clonează Monitorul", + "Clone": "Clonează", + "emailCustomisableContent": "Conținut personalizabil", + "leave blank for default body": "lăsați necompletat pentru corpul implicit", + "emailTemplateServiceName": "Numele Serviciului", + "emailTemplateHostnameOrURL": "Numele Hostului sau URL", + "emailTemplateMonitorJSON": "object care descrie monitorul", + "emailTemplateHeartbeatJSON": "object care descrie heartbeat-ul", + "emailTemplateMsg": "mesajul notificării", + "emailTemplateLimitedToUpDownNotification": "disponibil numai pentru heartbeat-uri UP/DOWN, altfel nul", + "emailTemplateStatus": "Stare", + "invertKeywordDescription": "Căutați după cuvântul cheie să fie absent și nu prezent.", + "goAlertInfo": "GoAlert este o aplicație open source pentru programarea apelurilor, escalări automate și notificări (cum ar fi SMS-uri sau apeluri vocale). Angajați automat persoana potrivită, în modul potrivit și la momentul potrivit! {0}", + "goAlertIntegrationKeyInfo": "Obțineți cheia generică de integrare API pentru serviciu în formatul \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" de obicei valoarea parametrului token al URL-ului copiat.", + "SecretAccessKey": "Secret AccessKey", + "SignName": "NumeSemn", + "Bark API Version": "Versiunea API Bark", + "Bark Endpoint": "Endpoint Bark", + "promosmsTypeFull": "SMS FULL - Abonament premium SMS, poți folosi propriul nume de expeditor (Trebuie să înregistrezi inițial numele). Fiabil pentru alerte.", + "openModalTo": "deschide modal la {0}", + "Add a domain": "Adaugă un domeniu", + "Remove domain": "Elimină domeniul '{0}'", + "Icon Emoji": "Pictogramă Emoji", + "documentation": "documentație", + "smtpDkimDomain": "Numele Domeniului", + "smtpDkimKeySelector": "Selector cheie", + "smtpDkimPrivateKey": "Cheie Privată", + "Integration Key": "Cheie de integrare", + "Integration URL": "URL Integrare", + "Auto resolve or acknowledged": "Rezolvare automată sau confirmată", + "do nothing": "nu face nimic", + "alertaApiEndpoint": "Endpoint API", + "alertaEnvironment": "Mediu", + "alertaApiKey": "Cheie API", + "alertaAlertState": "Statut Alertă", + "promosmsTypeSpeed": "SMS SPEED - Cea mai mare prioritate în sistem. Foarte rapid și fiabil dar costisitor (aproape dublul prețului SMS FULL).", + "serwersmsAPIUser": "Utilizator API (incl. webapi_ prefix)", + "serwersmsAPIPassword": "Parolă API", + "serwersmsPhoneNumber": "Număr de telefon", + "serwersmsSenderName": "Numele expeditorului SMS (înregistrat prin portalul clienților)", + "smseagleTo": "Număr (Numere) de telefon", + "smseagleGroup": "Numele grupului din agenda telefonică", + "smseagleContact": "Nume de contact din agenda telefonică", + "smseagleRecipient": "Destinatar(i) (mai mulți trebuie despărțiți prin virgulă)", + "smseagleToken": "Token Acces API", + "smseagleUrl": "URL-ul dispozitivului tău SMSEagle", + "smseagleEncoding": "Trimiteți ca Unicode", + "smseaglePriority": "Prioritate mesaj (0-9, implicit = 0)", + "Recipient Number": "Număr de telefon Destinatar", + "From Name/Number": "Nume Expeditor/Număr", + "Leave blank to use a shared sender number.": "Lăsați necompletat pentru a utiliza un număr de expeditor partajat.", + "Octopush API Version": "Versiune API Octopush", + "Server URL should not contain the nfty topic": "Adresa URL a serverului nu trebuie să conțină subiectul nfty", + "onebotHttpAddress": "Adresa HTTP OneBot", + "onebotMessageType": "Tipul Mesajului OneBot", + "onebotGroupMessage": "Grup", + "onebotPrivateMessage": "Privat", + "onebotUserOrGroupId": "Grup/ID Utilizator", + "onebotSafetyTips": "Pentru siguranță, trebuie să setați tokenul de acces", + "PushDeer Server": "Serverul PushDeer", + "PushDeer Key": "Cheia PushDeer", + "Edit Tag": "Editați eticheta", + "Server Address": "Adresa Serverului", + "Learn More": "Află mai multe", + "API Keys": "Chei API", + "Expiry": "Expirare", + "Expiry date": "Data de expirare", + "Don't expire": "Nu expiră", + "Continue": "Continuă", + "Add Another": "Adaugă Altul", + "Key Added": "Cheie Adăugată", + "Add API Key": "Adăugați cheia API", + "No API Keys": "Fără Chei APi", + "apiKey-active": "Activ", + "apiKey-expired": "Expirat", + "apiKey-inactive": "Inactiv", + "Expires": "Expiră", + "disableAPIKeyMsg": "Sigur doriți să dezactivați această cheie API ?", + "deleteAPIKeyMsg": "Sigur doriți să ștergeți această cheie API ?", + "Generate": "Generează", + "pagertreeUrgency": "Urgență", + "pagertreeSilent": "Silențios", + "pagertreeLow": "Scăzut", + "pagertreeMedium": "Mediu", + "pagertreeHigh": "Crescut", + "pagertreeResolve": "Rezolvare automată", + "pagertreeDoNothing": "Nu face nimic", + "Add a new expiry notification day": "Adăugați o nouă zi de notificare de expirare", + "Remove the expiry notification": "Eliminați ziua de notificare a expirării", + "startDateTime": "Dată/Oră de început", + "cloneOf": "Clona lui {0}", + "Check/Uncheck": "Bifați/Debifați", + "telegramSendSilently": "Trimite Silențios", + "telegramProtectContent": "Protejați redirecționarea/salvarea", + "Add New Tag": "Adaugă Etichetă Nouă", + "timeoutAfter": "Timeout după {0} secunde", + "styleElapsedTime": "Timp scurs sub bara heartbeat", + "styleElapsedTimeShowNoLine": "Afișați (Fără Linie)", + "styleElapsedTimeShowWithLine": "Afișați (Cu Linie)", + "filterActive": "Activ", + "filterActivePaused": "Întrerupt", + "webhookBodyCustomOption": "Corp Personalizat", + "Select": "Selectați", + "selectedMonitorCount": "Selectat: {0}", + "cronExpression": "Expresie Cron", + "invalidCronExpression": "Expresie Cron invalidă: {0}", + "chromeExecutable": "Executabil Chrome/Chromium", + "High": "Crescut", + "auto acknowledged": "recunoscut automat", + "auto resolve": "rezolvare automată", + "Home": "Acasă", + "Cannot connect to the socket server": "Nu se poate conecta la serverul socket", + "Reconnecting...": "Reconectare...", + "Invert Keyword": "Inversare Cuvânt Cheie", + "Expected Value": "Valoare Așteptată", + "Json Query": "Interogare Json", + "high": "crescut", + "Saved.": "Salvat.", + "ntfyPriorityHelptextAllExceptDown": "Toate evenimentele sunt trimise cu această prioritate, cu excepția {0}-evenimente, care au o prioritate de {1}", + "noOrBadCertificate": "Fără/Certificat greșit", + "monitorToastMessagesLabel": "Notificări Toast Monitoare", + "authInvalidToken": "Token Invalid.", + "Browser Screenshot": "Captură de ecran din browser", + "GrafanaOncallUrl": "URL Grafana Oncall", + "Pick a SASL Mechanism...": "Alegeți un mecanism SASL…", + "What is a Remote Browser?": "Ce este un Browser Remote?", + "Badge Warn Days": "Badge zile de avertizare", + "monitorToastMessagesDescription": "Notificările toast pentru monitoare dispar după un anumit timp în secunde. Setat la -1, dezactivează timpul de expirare. Setat la 0, dezactivează notificările toast.", + "noGroupMonitorMsg": "Nu este disponibil. Creați mai întâi un grup de monitoare.", + "wayToGetFlashDutyKey": "Pentru a integra Uptime Kuma cu Flashduty: accesează Channels > Selectează un canal > Integrations > Add a new integration, alege Uptime Kuma și copiază Push URL.", + "remoteBrowsersDescription": "Browserele Remote sunt o alternativă la rularea locală a Chromium. Configurați cu un serviciu precum browserless.io sau conectați-vă la cel personal", + "Remote Browsers": "Browsere Remote", + "Remote Browser": "Browser Remote", + "Add a Remote Browser": "Adăugați un Browser Remote", + "Remote Browser not found!": "Browserul Remote nu a fost găsit!", + "self-hosted container": "container self-hosted", + "notificationRegional": "Local", + "Monitor Setting": "{0} Setări Monitor", + "Show Clickable Link": "Afișați Linkul Accesibil", + "Show Clickable Link Description": "Dacă este bifată, toți cei care au acces la această pagină de stare pot avea acces la URL-ul monitorului.", + "nostrRecipients": "Chei publice ale destinatarilor (npub)", + "2faAlreadyEnabled": "2FA este deja activat.", + "Badge Prefix": "Prefixul Valorii Badge", + "Badge Suffix": "Sufixul Valorii Badge", + "Badge Label Color": "Culoarea Etichetei Badge", + "Request Body": "Request Body", + "nostrRecipientsHelp": "format npub, unul pe linie", + "showCertificateExpiry": "Afișați expirarea certificatului", + "gamedigGuessPort": "Gamedig: Guess Port", + "gamedigGuessPortDescription": "Portul utilizat de Valve Server Query Protocol poate fi diferit de portul client. Încercați acest lucru dacă monitorul nu se poate conecta la serverul dvs.", + "successAuthChangePassword": "Parola a fost actualizată cu succes.", + "successDisabled": "Dezactivat cu succes.", + "twilioApiKey": "Cheie Api (opțional)", + "Enter the list of brokers": "Introduceți lista de brokeri", + "Close": "Închide", + "Kafka Topic Name": "Numele Subiectului Kafka", + "Kafka Producer Message": "Mesajul Producătorului Kafka", + "Enable Kafka SSL": "Activați SSL Kafka", + "liquidIntroduction": "Șablonarea se realizează prin intermediul limbajului de șabloane Liquid. Consultați {0} pentru instrucțiuni de utilizare.", + "smtpLiquidIntroduction": "Următoarele două câmpuri pot fi șablonate prin Limbajul de șabloane Liquid. Consultați {0} pentru instrucțiuni de utilizare. Acestea sunt variabilele disponibile:", + "Legacy Octopush-DM": "Octopush-DM vechi", + "alertaRecoverState": "Stare Recuperare", + "Body Encoding": "Codificarea Body", + "wayToGetPagerTreeIntegrationURL": "După ce ați creat integrarea Uptime Kuma în PagerTree, copiați Endpoint-ul. Vedeți detaliile complete {0}", + "lunaseaDeviceID": "ID Dispozitiv", + "lunaseaUserID": "ID Utilizator", + "ntfyAuthenticationMethod": "Metodă de autentificare", + "ntfyPriorityHelptextAllEvents": "Toate evenimentele sunt trimise cu prioritate maximă", + "ntfyUsernameAndPassword": "Utilizator și Parolă", + "twilioAccountSID": "SID Cont", + "twilioAuthToken": "Token de autentificare / Secret cheie Api", + "twilioFromNumber": "Număr Expeditor", + "twilioToNumber": "Număr Destinatar", + "Open Badge Generator": "Deschide Generatorul Badge", + "Badge Type": "Tipul Badge", + "Badge Duration (in hours)": "Durată Badge (în ore)", + "Badge Label": "Etichetă Badge", + "Badge Color": "Culoarea Badge", + "Badge Label Prefix": "Prefixul Etichetei Badge", + "Badge Preview": "Previzualizare Badge", + "Badge Label Suffix": "Sufixul Etichetei Badge", + "Badge Generator": "{0} Generator Badge", + "Badge Up Color": "Culoarea Badge Up", + "Badge Down Color": "Culoarea Badge Down", + "Badge Pending Color": "Culoarea în așteptare Badge", + "Badge Maintenance Color": "Culoarea mentenanță Badge", + "Badge Warn Color": "Culoare atenționare Badge", + "Badge Down Days": "Badge Zile Down", + "Badge Style": "Stilul Badge", + "Badge value (For Testing only.)": "Valoare Badge (Numai Pentru Testare.)", + "Badge URL": "URL Badge", + "Group": "Grup", + "Monitor Group": "Grup Monitoare", + "toastErrorTimeout": "Timeout pentru notificările de eroare", + "toastSuccessTimeout": "Timeout pentru notificările de succes", + "Kafka Brokers": "Brokeri Kafka", + "Press Enter to add broker": "Apăsați Enter pentru a adăuga broker", + "Enable Kafka Producer Auto Topic Creation": "Activați crearea automată a subiectelor Kafka Producător", + "Kafka SASL Options": "Opțiuni SASL Kafka", + "Mechanism": "Mecanism", + "Authorization Identity": "Identitate de Autorizare", + "AccessKey Id": "Id AccessKey", + "Secret AccessKey": "AccessKey Secret", + "Session Token": "Token Sesiune", + "FlashDuty Severity": "Severitate", + "nostrRelays": "Relee Nostr", + "nostrRelaysHelp": "Un URL releu pe linie", + "nostrSender": "Cheia privată a expeditorului (nsec)", + "authUserInactiveOrDeleted": "Utilizatorul este inactiv sau șters.", + "authIncorrectCreds": "Numele de utilizator sau parola incorectă.", + "2faEnabled": "2FA Activat.", + "2faDisabled": "2FA Dezactivat.", + "successAdded": "Adăugat cu succes.", + "successResumed": "Reluat cu succes.", + "successPaused": "Întrerupt cu succes.", + "successDeleted": "Șters cu succes.", + "successEdited": "Modificat cu succes.", + "successBackupRestored": "Backup restaurat cu succes.", + "successEnabled": "Activat cu succes.", + "tagNotFound": "Eticheta nu a fost găsită.", + "foundChromiumVersion": "S-a găsit Chromium/Chrome. Versiune: {0}", + "remoteBrowserToggle": "În mod implicit, Chromium rulează în containerul Uptime Kuma. Puteți utiliza un browser remote activând acest comutator.", + "useRemoteBrowser": "Utilizați un Browser Remote", + "deleteRemoteBrowserMessage": "Sigur doriți să ștergeți acest browser remote pentru toate monitoarele?", + "lunaseaTarget": "Țintă", + "telegramMessageThreadID": "(Opțional) ID Thread Mesaje", + "telegramMessageThreadIDDescription": "Opțional Identificator unic pentru threadul de mesaje țintă (subiect) al forumului; numai pentru supergrupuri de forum", + "Request Timeout": "Timeout Solicitare", + "webhookBodyPresetOption": "Presetul - {0}", + "Resend Notification if Down X times consecutively": "Retrimiteți notificarea dacă e Down de X ori consecutiv", + "Channel access token (Long-lived)": "Token de acces la canal (de lungă durată)", + "Your User ID": "ID-ul dvs. de utilizator", + "documentationOf": "{0} Documentaţie", + "wayToGetHeiiOnCallDetails": "Cum să obțineți ID-ul declanșatorului și cheile API este explicat în {documentație}", + "To Phone Number": "La numărul de telefon", + "gtxMessagingToHint": "Format internațional, cu semnul „+” ({e164}, {e212} sau {e214})", + "gtxMessagingApiKeyHint": "Puteți găsi cheia dvs. API la: Conturile mele de rutare > Afișați informații despre cont > Acreditări API > API REST (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "De la numărul de telefon/Adresa de origine a căii de transmisie (TPOA)", + "gtxMessagingFromHint": "Pe telefoanele mobile, destinatarii văd TPOA afișat ca expeditor al mesajului. Sunt permise până la 11 caractere alfanumerice, un cod scurt, un cod lung local sau numere internaționale ({e164}, {e212} sau {e214})", + "Alphanumeric (recommended)": "Alfanumeric (recomandat)", + "Telephone number": "Număr de telefon", + "Originator": "Inițiator", + "cellsyntOriginator": "Vizibil pe telefonul mobil al destinatarului ca emitent al mesajului. Valorile și funcția permise depind de tipul originator al parametrului.", + "Destination": "Destinaţie", + "Allow Long SMS": "Permite SMS-uri lungi", + "cellsyntSplitLongMessages": "Împărțiți mesajele lungi în până la 6 părți. 153 x 6 = 918 caractere.", + "max 15 digits": "max 15 cifre", + "max 11 alphanumeric characters": "max 11 caractere alfanumerice", + "Originator type": "Tip de inițiator", + "cellsyntOriginatortypeAlphanumeric": "Șir alfanumeric (maximum 11 caractere alfanumerice). Destinatarii nu pot răspunde la mesaj.", + "cellsyntOriginatortypeNumeric": "Valoare numerică (maxim 15 cifre) cu număr de telefon în format internațional fără 00 înainte (de exemplu, numărul britanic 07920 110 000 ar trebui setat ca 447920110000). Destinatarii pot răspunde la mesaj.", + "cellsyntDestination": "Numărul de telefon al destinatarului utilizând formatul internațional cu 00 inițial urmat de codul țării, de ex. 00447920110000 pentru numărul britanic 07920 110 000 (maximum 17 cifre în total). Maximum 25000 de destinatari separați prin virgulă pentru fiecare solicitare HTTP.", + "callMeBotGet": "Aici puteți genera un punct final pentru {0}, {1} și {2}. Rețineți că este posibil să obțineți o rată limitată. Limitele ratelor par să fie: {3}", + "wayToGetWhapiUrlAndToken": "Puteți obține adresa URL API și indicativul accesând canalul dorit de la {0}", + "whapiRecipient": "Număr de telefon / ID de contact / ID de grup", + "API URL": "URL API", + "wayToWriteWhapiRecipient": "Numărul de telefon cu prefixul internațional, dar fără semnul plus la început ({0}), ID-ul de contact ({1}) sau ID-ul grupului ({2}).", + "locally configured mail transfer agent": "agent de transfer e-mail configurat local", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Fie introduceți numele de gazdă al serverului la care doriți să vă conectați, fie {localhost} dacă intenționați să utilizați un {local_mta}", + "Host URL": "Adresa URL a gazdei", + "Mentioning": "Menționând", + "Don't mention people": "Nu menționați oameni", + "Mention group": "Menționați {grup}", + "wayToGetSevenIOApiKey": "Accesați tabloul de bord sub app.seven.io > dezvoltator > cheie API > butonul verde de adăugare", + "senderSevenIO": "Numărul sau numele expeditorului", + "receiverSevenIO": "Număr de primire", + "receiverInfoSevenIO": "Dacă numărul de destinație nu se află în Germania, trebuie să adăugați codul de țară în fața numărului (de exemplu, pentru codul de țară 1 din SUA utilizați 117612121212 în loc de 017612121212)", + "apiKeySevenIO": "Cheia API SevenIO", + "Command": "Comanda", + "smspartnerSenderName": "Numele expeditorului SMS", + "smspartnerSenderNameInfo": "Trebuie să aibă între 3..=11 caractere obișnuite", + "mongodbCommandDescription": "Rulați o comandă MongoDB în baza de date. Pentru informații despre comenzile disponibile, consultați {documentația}", + "Bitrix24 Webhook URL": "URL Bitrix24 Webhook", + "bitrix24SupportUserID": "Introduceți ID-ul dvs. de utilizator în Bitrix24. Puteți afla ID-ul din link accesând profilul utilizatorului.", + "smspartnerApiurl": "Puteți găsi cheia dvs. API în tabloul de bord la {0}", + "smspartnerPhoneNumber": "Număr(numere) de telefon", + "smspartnerPhoneNumberHelptext": "Numărul trebuie să fie în format internațional {0}, {1}. Numerele multiple trebuie separate prin {2}", + "wayToGetBitrix24Webhook": "Puteți crea un webhook urmând pașii de la {0}", + "Select message type": "Selectați tipul mesajului", + "Send to channel": "Trimite pe canal", + "Create new forum post": "Creați o nouă postare pe forum", + "postToExistingThread": "Postați în firul de discuție/postul de forum existent", + "forumPostName": "Numele postării pe forum", + "wayToGetDiscordThreadId": "Obținerea unui subiect / ID postare forum este similară cu obținerea unui ID de canal. Citiți mai multe despre cum să obțineți ID-uri {0}", + "threadForumPostID": "Subiect / ID postare forum", + "e.g. {discordThreadID}": "ex. {discordThreadID}", + "whatHappensAtForumPost": "Creați o nouă postare pe forum. Acest lucru NU postează mesaje în postarea existentă. Pentru a posta în postarea existentă, utilizați „{option}”", + "Refresh Interval": "Interval de reîmprospătare", + "Refresh Interval Description": "Pagina de status va efectua o reîmprospătare completă a site-ului la fiecare {0} secunde", + "ignoreTLSErrorGeneral": "Ignorați eroarea TLS/SSL pentru conexiune", + "threemaRecipient": "Destinatar", + "threemaRecipientType": "Tip de destinatar", + "threemaRecipientTypeIdentity": "ID Threema", + "threemaRecipientTypeIdentityFormat": "8 caractere", + "threemaRecipientTypePhone": "Număr de telefon", + "threemaRecipientTypePhoneFormat": "E.164, fără prefixul +", + "threemaRecipientTypeEmail": "Adresa de e-mail", + "threemaSenderIdentity": "ID Gateway", + "threemaApiAuthenticationSecret": "Secret ID Gateway", + "wayToGetThreemaGateway": "Vă puteți înregistra pentru Threema Gateway {0}.", + "threemaSenderIdentityFormat": "8 caractere, de obicei începe cu *", + "threemaBasicModeInfo": "Notă: Această integrare utilizează Threema Gateway în modul de bază (criptare bazată pe server). Mai multe detalii pot fi găsite {0}.", + "apiKeysDisabledMsg": "Cheile API sunt dezactivate deoarece autentificarea este dezactivată.", + "and": "și", + "jsonQueryDescription": "Analizați și extrageți date specifice din răspunsul JSON al serverului folosind interogarea JSON sau folosiți „$” pentru răspunsul brut, dacă nu vă așteptați JSON. Rezultatul este apoi comparat cu valoarea așteptată, sub formă de șiruri. Consultați {0} pentru documentație și utilizați {1} pentru a experimenta interogări.", + "snmpCommunityStringHelptext": "Acest șir funcționează ca o parolă pentru autentificarea și controlul accesului la dispozitivele SNMP activate. Potriviți-l cu configurația dispozitivului dvs. SNMP.", + "Please enter a valid OID.": "Vă rugăm să introduceți un OID valid.", + "now": "acum", + "time ago": "acum {0}", + "-year": "-an", + "Json Query Expression": "Expresie de interogare Json", + "cacheBusterParam": "Adăugați parametrul {0}", + "cacheBusterParamDescription": "Parametru generat aleatoriu pentru a ignora memoria cache.", + "Community String": "Șir de comunitate", + "OID (Object Identifier)": "OID (identificator de obiect)", + "snmpOIDHelptext": "Introduceți OID-ul pentru senzorul sau starea pe care doriți să-l monitorizați. Utilizați instrumente de gestionare a rețelei, cum ar fi browserele MIB sau software-ul SNMP, dacă nu sunteți sigur de OID.", + "Condition": "Stare", + "SNMP Version": "Versiunea SNMP", + "Host Onesender": "Gazdă Onesender", + "privateOnesenderDesc": "Asigurați-vă că numărul de telefon este valid. Pentru a trimite mesaj la numărul de telefon privat, ex: 628123456789", + "wayToGetOnesenderUrlandToken": "Puteți obține adresa URL și Token accesând site-ul web Onesender. Mai multe informații {0}", + "OAuth Token URL": "Adresa URL a tokenului OAuth", + "No tags found.": "Nu au fost găsite etichete.", + "signl4Docs": "Puteți găsi mai multe informații despre cum să configurați SIGNL4 și despre cum să obțineți adresa URL a webhook-ului SIGNL4 în {0}.", + "greater than": "mai mare decât", + "Token Onesender": "Onesender Token", + "Recipient Type": "Tip Destinatar", + "Private Number": "Număr privat", + "groupOnesenderDesc": "Asigurați-vă că GroupID este valid. Pentru a trimite un mesaj în grup, de exemplu: 628123456789-342345", + "Group ID": "ID grup", + "Add Remote Browser": "Adăugați browser la distanță", + "New Group": "Grup nou", + "Group Name": "Numele grupului", + "OAuth2: Client Credentials": "OAuth2: credențialele clientului", + "Authentication Method": "Metoda de autentificare", + "Authorization Header": "Antet de autorizare", + "Form Data Body": "Corpul datelor formularului", + "Client ID": "ID client", + "Client Secret": "Secret client", + "OAuth Scope": "Domeniul OAuth", + "Optional: Space separated list of scopes": "Opțional: listă de domenii separate prin spații", + "Go back to home page.": "Reveniți la pagina de start.", + "Lost connection to the socket server.": "S-a pierdut conexiunea la serverul socket.", + "Cannot connect to the socket server.": "Nu se poate conecta la serverul socket.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "URL SIGNL4 Webhook", + "Conditions": "Condiții", + "conditionAdd": "Adăugați o condiție", + "conditionDelete": "Ștergeți condiția", + "conditionAddGroup": "Adăugați grup", + "conditionDeleteGroup": "Ștergeți grupul", + "conditionValuePlaceholder": "Valoare", + "equals": "egală", + "not equals": "nu este egală", + "contains": "conţine", + "not contains": "nu conține", + "starts with": "începe cu", + "not starts with": "nu începe cu", + "ends with": "se termină cu", + "not ends with": "nu se termină cu", + "less than": "mai puțin decât", + "less than or equal to": "mai mic sau egal cu", + "greater than or equal to": "mai mare sau egal cu", + "record": "înregistrare", + "aboutSlackUsername": "Modifică numele afișat al expeditorului mesajului. Dacă doriți să menționați pe cineva, includeți-l în numele prietenos.", + "Custom sound to override default notification sound": "Sunet personalizat pentru a înlocui sunetul de notificare implicit", + "ignoredTLSError": "Erorile TLS/SSL au fost ignorate", + "Message format": "Formatul mesajului", + "Send rich messages": "Trimiteți mesaje complexe", + "Notification Channel": "Canal de notificare", + "Sound": "Sunet", + "Alphanumerical string and hyphens only": "Doar șir de caractere alfanumerice și liniuțe", + "Arcade": "Galerie", + "RabbitMQ Username": "Utilizator RabbitMQ", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Notificările \"time sensitive\" vor fi livrate imediat, chiar dacă dispozitivul este în modul „nu deranjați”.", + "rabbitmqNodesDescription": "Introduceți adresa URL pentru nodurile de gestionare RabbitMQ, inclusiv protocolul și portul. Exemplu: {0}", + "rabbitmqHelpText": "Pentru a utiliza monitorul, va trebui să activați plugin-ul de gestionare în configurația RabbitMQ. Pentru mai multe informații, vă rugăm să consultați {rabitmq_documentation}.", + "Time Sensitive (iOS Only)": "Time Sensitive (numai iOS)", + "From": "De la", + "Can be found on:": "Poate fi găsit la: {0}", + "The phone number of the recipient in E.164 format.": "Numărul de telefon al destinatarului în format E.164.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Fie un ID expeditor text, fie un număr de telefon în format E.164, dacă doriți să puteți primi răspunsuri.", + "RabbitMQ Nodes": "Noduri de gestionare RabbitMQ", + "Money": "Bani", + "Scifi": "SF", + "Elevator": "Lift", + "Guitar": "Chitară", + "Pop": "Pop", + "Harp": "Harpă", + "Reveal": "Dezvăluire", + "Bubble": "Bule", + "Doorbell": "Sonerie", + "Flute": "Flaut", + "Clear": "Clar", + "rabbitmqNodesRequired": "Vă rugăm să setați nodurile pentru acest monitor.", + "rabbitmqNodesInvalid": "Vă rugăm să utilizați o adresă URL complet calificată (începând cu „http”) pentru nodurile RabbitMQ.", + "RabbitMQ Password": "Parolă RabbitMQ", + "SendGrid API Key": "Cheia API SendGrid", + "Separate multiple email addresses with commas": "Separați adresele de e-mail multiple cu virgule", + "Correct": "Corect", + "Fail": "Eșec", + "shrinkDatabaseDescriptionSqlite": "Declanșează comanda {vacuum} pentru baza de date SQLite. {auto_vacuum} este deja activat, dar acest lucru nu defragmentează baza de date și nici nu reîmpachetează paginile individuale ale bazei de date așa cum o face comanda {vacuum}.", + "monitorTypeGameServer": "Server Jocuri", + "monitorTypeDatabase": "Tip monitorizare bază de date", + "monitorTypeSpecial": "Special", + "Sender name": "Nume expeditor", + "smsplanetNeedToApproveName": "Trebuie să fie aprobat în panoul clientului.", + "mariadbUseSSLHelptext": "Activează utilizarea unei conexiuni criptate către baza ta de date. Este necesar pentru majoritatea bazelor de date din cloud.", + "mariadbCaCertificateHelptext": "Lipește certificatul CA în format PEM pentru a-l utiliza cu certificate self-signed. Lasă câmpul gol dacă baza ta de date folosește un certificat semnat de o autoritate de certificare publică.", + "mariadbCaCertificateLabel": "Certificat CA", + "No incidents recorded": "Nu au fost înregistrate incidente", + "Load More": "Încărcați mai mult", + "versionIs": "Versiune: {version}" +} diff --git a/src/lang/ru-RU.json b/src/lang/ru-RU.json new file mode 100644 index 0000000..e0c9559 --- /dev/null +++ b/src/lang/ru-RU.json @@ -0,0 +1,1730 @@ +{ + "languageName": "Русский", + "checkEverySecond": "Проверка каждые {0} секунд", + "retriesDescription": "Максимальное число попыток перед тем, как сервис будет помечен как неработающий и будет отправлено уведомление", + "ignoreTLSError": "Игнорировать ошибки TLS/SSL для сайтов с HTTPS", + "upsideDownModeDescription": "Инвертировать статус. Если сервис доступен — он считается НЕРАБОТАЮЩИМ.", + "maxRedirectDescription": "Максимальное число перенаправлений. Установите 0, чтобы отключить перенаправления.", + "acceptedStatusCodesDescription": "Выберите коды статусов для определения доступности сервиса.", + "passwordNotMatchMsg": "Введённые пароли не совпадают.", + "notificationDescription": "Необходимо привязать уведомления к монитору чтобы они функционировали.", + "keywordDescription": "Поиск слова в чистом HTML или в JSON-ответе (чувствительно к регистру).", + "pauseDashboardHome": "Пауза", + "deleteMonitorMsg": "Вы действительно хотите удалить данный монитор?", + "deleteNotificationMsg": "Вы действительно хотите удалить это уведомление для всех мониторов?", + "resolverserverDescription": "Cloudflare является сервером по умолчанию. Вы можете указать список IP-адресов или имён хостов, разделённых запятыми.", + "rrtypeDescription": "Выберите тип ресурсной записи, который вы хотите отслеживать", + "pauseMonitorMsg": "Вы действительно хотите приостановить?", + "Settings": "Настройки", + "Dashboard": "Панель управления", + "New Update": "Новое обновление", + "Language": "Язык", + "Appearance": "Внешний вид", + "Theme": "Тема", + "General": "Общее", + "Version": "Версия", + "Check Update On GitHub": "Проверить наличие обновления в GitHub", + "List": "Список", + "Add": "Добавить", + "Add New Monitor": "Новый монитор", + "Quick Stats": "Сводка", + "Up": "Доступен", + "Down": "Недоступен", + "Pending": "В ожидании", + "Unknown": "Неизвестно", + "Pause": "Пауза", + "Name": "Имя", + "Status": "Статус", + "DateTime": "Дата и время", + "Message": "Сообщение", + "No important events": "Нет важных событий", + "Resume": "Возобновить", + "Edit": "Изменить", + "Delete": "Удалить", + "Current": "Текущий", + "Uptime": "Время безотказной работы", + "Cert Exp.": "Окончание сертификата.", + "day": "день | дней", + "-day": "-дней", + "hour": "час | часов", + "-hour": "-часа", + "Response": "Ответ", + "Ping": "Пинг", + "Monitor Type": "Тип монитора", + "Keyword": "Ключевое слово", + "Friendly Name": "Имя", + "URL": "URL-ссылка", + "Hostname": "Имя хоста", + "Port": "Порт", + "Heartbeat Interval": "Интервал опроса", + "Retries": "Попыток", + "Advanced": "Дополнительно", + "Upside Down Mode": "Режим инверсии статуса", + "Max. Redirects": "Макс. количество перенаправлений", + "Accepted Status Codes": "Допустимые коды статуса", + "Save": "Сохранить", + "Notifications": "Уведомления", + "Not available, please setup.": "Недоступно, требуется настройка.", + "Setup Notification": "Настройка уведомления", + "Light": "Светлая", + "Dark": "Тёмная", + "Auto": "Как в системе", + "Theme - Heartbeat Bar": "Полоса частоты опроса", + "Normal": "Обычный", + "Bottom": "Внизу", + "None": "Отсутствует", + "Timezone": "Часовой пояс TZ", + "Search Engine Visibility": "Индексация поисковыми системами", + "Allow indexing": "Разрешить индексирование", + "Discourage search engines from indexing site": "Запретить индексирование", + "Change Password": "Изменить пароль", + "Current Password": "Текущий пароль", + "New Password": "Новый пароль", + "Repeat New Password": "Повтор нового пароля", + "Update Password": "Обновить пароль", + "Disable Auth": "Отключить авторизацию", + "Enable Auth": "Включить авторизацию", + "disableauth.message1": "Вы уверены, что хотите {disableAuth}?", + "disable authentication": "отключить авторизацию", + "disableauth.message2": "Это подходит для сценариев {intendThirdPartyAuth} с аутентифицирующими механизмами перед Uptime Kuma (например Cloudflare Access, Authelia и др.).", + "where you intend to implement third-party authentication": "где вы собираетесь реализовать стороннюю аутентификацию", + "Please use this option carefully!": "Пожалуйста, используйте с осторожностью!", + "Logout": "Выйти", + "Leave": "Оставить", + "I understand, please disable": "Я понимаю, всё равно отключить", + "Confirm": "Подтвердить", + "Yes": "Да", + "No": "Нет", + "Username": "Логин", + "Password": "Пароль", + "Remember me": "Запомнить меня", + "Login": "Вход в систему", + "No Monitors, please": "Без Мониторов, пожалуйста", + "No Monitors": "Мониторы отсутствуют", + "add one": "добавить", + "Notification Type": "Тип уведомления", + "Email": "Электронная почта", + "Test": "Тест", + "Certificate Info": "Информация о сертификате", + "Resolver Server": "DNS сервер", + "Resource Record Type": "Тип ресурсной записи", + "Last Result": "Последний результат", + "Create your admin account": "Создайте аккаунт администратора", + "Repeat Password": "Повторите пароль", + "respTime": "Время ответа (мс)", + "notAvailableShort": "N/A", + "Create": "Создать", + "clearEventsMsg": "Вы уверены, что хотите удалить все события для этого монитора? При этом связанные с сообщением данные удаляются безвозвратно и не могут быть отменены.", + "clearHeartbeatsMsg": "Вы действительно хотите удалить всю статистику опросов данного монитора? При этом связанные с сообщением данные удаляются безвозвратно и не могут быть отменены.", + "confirmClearStatisticsMsg": "Вы действительно хотите удалить ВСЮ статистику?", + "Clear Data": "Стереть данные", + "Events": "События", + "Heartbeats": "Опросы", + "Auto Get": "Авто-получение", + "enableDefaultNotificationDescription": "Это уведомление будет включено по умолчанию для каждого нового монитора. Вы можете отключить уведомления в каждом мониторе отдельно.", + "Default enabled": "По умолчанию включён", + "Also apply to existing monitors": "Применить к существующим мониторам", + "Export": "Экспорт", + "Import": "Импорт", + "backupDescription": "Вы можете сохранить резервную копию всех мониторов и уведомлений в виде JSON-файла.", + "backupDescription2": "Важно: история и события сохранены не будут.", + "backupDescription3": "Важные данные, такие как токены уведомлений, добавляются при экспорте, поэтому храните файлы в безопасном месте.", + "alertNoFile": "Выберите файл для импорта.", + "alertWrongFileType": "Выберите JSON-файл.", + "twoFAVerifyLabel": "Пожалуйста, введите свой токен, чтобы проверить работу 2FA:", + "tokenValidSettingsMsg": "Токен действителен! Теперь вы можете сохранить настройки 2FA.", + "confirmEnableTwoFAMsg": "Вы действительно хотите включить 2FA?", + "confirmDisableTwoFAMsg": "Вы действительно хотите выключить 2FA?", + "Apply on all existing monitors": "Применить ко всем существующим мониторам", + "Verify Token": "Проверить токен", + "Setup 2FA": "Настройка 2FA", + "Enable 2FA": "Включить 2FA", + "Disable 2FA": "Выключить 2FA", + "2FA Settings": "Настройки 2FA", + "Two Factor Authentication": "Двухфакторная аутентификация", + "Active": "Активно", + "Inactive": "Неактивно", + "Token": "Токен", + "Show URI": "Показать URI", + "Clear all statistics": "Очистить статистику", + "retryCheckEverySecond": "Повторять каждые {0} секунд", + "importHandleDescription": "Выберите \"Пропустить существующие\", если вы хотите пропустить каждый монитор или уведомление с таким же именем. \"Перезаписать\" удалит каждый существующий монитор или уведомление и добавит заново. Вариант \"Не проверять\" принудительно восстанавливает все мониторы и уведомления, даже если они уже существуют.", + "confirmImportMsg": "Вы действительно хотите восстановить резервную копию? Убедитесь, что вы выбрали подходящий вариант импорта.", + "Heartbeat Retry Interval": "Интервал повтора запроса", + "Import Backup": "Импорт резервной копии", + "Export Backup": "Скачать резервную копию", + "Skip existing": "Пропустить существующие", + "Overwrite": "Перезаписать", + "Options": "Опции", + "Keep both": "Оставить оба", + "Tags": "Теги", + "Add New below or Select...": "Добавить новый или выбрать…", + "Tag with this name already exist.": "Тег с таким именем уже существует.", + "Tag with this value already exist.": "Тег с таким значением уже существует.", + "color": "Цвет", + "value (optional)": "значение (необязательно)", + "Gray": "Серый", + "Red": "Красный", + "Orange": "Оранжевый", + "Green": "Зелёный", + "Blue": "Синий", + "Indigo": "Индиго", + "Purple": "Пурпурный", + "Pink": "Розовый", + "Search...": "Поиск…", + "Avg. Ping": "Сред. Пинг", + "Avg. Response": "Сред. Отклик", + "Entry Page": "Главная", + "statusPageNothing": "Ничего нет, добавьте группу или монитор.", + "No Services": "Нет Сервисов", + "All Systems Operational": "Все системы работают", + "Partially Degraded Service": "Частичная работа сервисов", + "Degraded Service": "Отказ всех сервисов", + "Add Group": "Добавить Группу", + "Add a monitor": "Добавить монитор", + "Edit Status Page": "Изменить страницу статусов", + "Go to Dashboard": "В Панель Управления", + "Status Page": "Страница статуса", + "Status Pages": "Страницы статуса", + "Discard": "Отмена", + "Create Incident": "Создать инцидент", + "Switch to Dark Theme": "Тёмная тема", + "Switch to Light Theme": "Светлая тема", + "telegram": "Telegram", + "webhook": "Вебхук", + "smtp": "Email (SMTP)", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Поддержка 50+ сервисов уведомлений)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "Primary Base URL": "Основной URL, по которому доступен Uptime Kuma", + "Push URL": "URL-ссылка push уведомлений", + "needPushEvery": "К этому URL необходимо обращаться каждые {0} секунд.", + "pushOptionalParams": "Необязательные параметры: {0}", + "defaultNotificationName": "Уведомления {notification} ({number})", + "here": "здесь", + "Required": "Обязательно", + "Bot Token": "Токен бота", + "wayToGetTelegramToken": "Вы можете получить токен здесь - {0}.", + "Chat ID": "ID чата", + "supportTelegramChatID": "Поддерживаются ID чатов, групп и каналов", + "wayToGetTelegramChatID": "Вы можете получить ID вашего чата, отправив сообщение боту и перейдя по этому URL для просмотра chat_id:", + "YOUR BOT TOKEN HERE": "ВАШ ТОКЕН БОТА ЗДЕСЬ", + "chatIDNotFound": "ID чата не найден; пожалуйста отправьте сначала сообщение боту", + "Post URL": "Post URL", + "Content Type": "Тип контента", + "webhookJsonDesc": "{0} подходит для любых современных HTTP-серверов, например Express.js", + "webhookFormDataDesc": "{multipart} подходит для PHP. JSON-вывод необходимо будет обработать с помощью {decodeFunction}", + "secureOptionNone": "Нет / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Игнорировать ошибки TLS", + "From Email": "От кого", + "emailCustomSubject": "Своя тема", + "To Email": "Кому", + "smtpCC": "Копия", + "smtpBCC": "Скрытая копия", + "Discord Webhook URL": "Discord вебхук URL", + "wayToGetDiscordURL": "Вы можете создать его в настройках канала \"Настройки -> Интеграции -> Создать Вебхук\"", + "Bot Display Name": "Отображаемое имя бота", + "Prefix Custom Message": "Свой префикс сообщения", + "Hello @everyone is...": "Привет {'@'}everyone это…", + "Webhook URL": "URL вебхука", + "wayToGetTeamsURL": "Как создать URL Вебхука вы можете узнать здесь - {0}.", + "Number": "Номер", + "Recipients": "Получатели", + "needSignalAPI": "Вам необходим клиент Signal с поддержкой REST API.", + "wayToCheckSignalURL": "Пройдите по этому URL, чтобы узнать как настроить такой клиент:", + "signalImportant": "ВАЖНО: Нельзя смешивать в Получателях группы и номера!", + "Application Token": "Токен приложения", + "Server URL": "URL сервера", + "Priority": "Приоритет", + "Icon Emoji": "Иконка Emoji", + "Channel Name": "Имя канала", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Больше информации о вебхуках: {0}", + "aboutChannelName": "Введите имя канала в поле {0} Имя канала, если вы хотите обойти канал вебхука. Например: #other-channel", + "aboutKumaURL": "Если поле Uptime Kuma URL в настройках останется пустым, по умолчанию будет использоваться ссылка на проект на GitHub.", + "emojiCheatSheet": "Шпаргалка по Emoji: {0}", + "User Key": "Ключ пользователя", + "Device": "Устройство", + "Message Title": "Заголовок сообщения", + "Notification Sound": "Звук уведомления", + "More info on:": "Больше информации: {0}", + "pushoverDesc1": "Экстренный приоритет (2) имеет таймаут повтора по умолчанию 30 секунд и истекает через 1 час.", + "pushoverDesc2": "Если вы хотите отправлять уведомления различным устройствам, необходимо заполнить поле Устройство.", + "SMS Type": "Тип SMS", + "octopushTypePremium": "Премиум (Быстрый - рекомендуется для алертов)", + "octopushTypeLowCost": "Дешёвый (Медленный - иногда блокируется операторами)", + "checkPrice": "Тарифы {0}:", + "octopushLegacyHint": "Вы используете старую версию Octopush (2011-2020) или новую?", + "Check octopush prices": "Тарифы Octopush {0}.", + "octopushPhoneNumber": "Номер телефона (межд. формат, например: +79831234567)", + "octopushSMSSender": "Имя отправителя SMS: 3-11 символов алфавита, цифр и пробелов (a-zA-Z0-9)", + "LunaSea Device ID": "ID устройства LunaSea", + "Apprise URL": "URL-адрес Apprise", + "Example:": "Пример: {0}", + "Read more:": "Подробнее: {0}", + "Status:": "Статус: {0}", + "Read more": "Подробнее", + "appriseInstalled": "Информирование установлено.", + "appriseNotInstalled": "Информирование не установлено. {0}", + "Access Token": "Токен доступа", + "Channel access token": "Токен доступа канала", + "Line Developers Console": "Консоль разработчиков Line", + "lineDevConsoleTo": "Консоль разработчиков Line - {0}", + "Basic Settings": "Базовые настройки", + "User ID": "ID пользователя", + "Messaging API": "API сообщений", + "wayToGetLineChannelToken": "Сначала зайдите в {0}, создайте провайдера и канал (API сообщений), затем вы сможете получить токен доступа канала и ID пользователя из вышеупомянутых пунктов меню.", + "Icon URL": "URL иконки", + "aboutIconURL": "Вы можете вставить ссылку на иконку в поле \"URL иконки\" чтобы изменить картинку профиля по умолчанию. Не используется, если задана иконка Emoji.", + "aboutMattermostChannelName": "Вы можете переопределить канал по умолчанию, в который вебхук пишет, введя имя канала в поле \"Имя канала\". Это необходимо включить в настройках вебхука Mattermost. Например: #other-channel", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - дёшево и медленно, часто перегружен. Только для получателей из Польши.", + "promosmsTypeFlash": "SMS FLASH - сообщения автоматически появятся на устройстве получателя. Только для получателей из Польши.", + "promosmsTypeFull": "SMS FULL - премиум-уровень SMS, можно использовать своё имя отправителя (предварительно зарегистрировав его). Надёжно для алертов.", + "promosmsTypeSpeed": "SMS SPEED - наивысший приоритет в системе. Очень быстро и надёжно, но очень дорого (в два раза дороже, чем SMS FULL).", + "promosmsPhoneNumber": "Номер телефона (для получателей из Польши можно пропустить код региона)", + "promosmsSMSSender": "Имя отправителя SMS: Зарегистрированное или одно из имён по умолчанию: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "URL-адрес вебхука Feishu", + "matrixHomeserverURL": "URL сервера (вместе с http(s):// и по желанию порт)", + "Internal Room Id": "Внутренний ID комнаты", + "matrixDesc1": "Внутренний ID комнаты можно найти в Подробностях в параметрах канала вашего Matrix клиента. Он должен выглядеть примерно как !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Рекомендуется создать нового пользователя и не использовать токен доступа личного пользователя Matrix, т.к. это влечёт за собой полный доступ к аккаунту и к комнатам, в которых вы состоите. Вместо этого создайте нового пользователя и пригласите его только в ту комнату, в которой вы хотите получать уведомления. Токен доступа можно получить, выполнив команду {0}", + "Method": "Метод", + "Body": "Объект", + "Headers": "Заголовки", + "PushUrl": "URL пуша", + "HeadersInvalidFormat": "Заголовки запроса не являются валидным JSON: ", + "BodyInvalidFormat": "Тело запроса не является валидным JSON: ", + "Monitor History": "История мониторинга", + "clearDataOlderThan": "Сохранять статистику за {0} дней.", + "PasswordsDoNotMatch": "Пароли не совпадают.", + "records": "записей", + "One record": "Одна запись", + "steamApiKeyDescription": "Для мониторинга игрового сервера Steam вам необходим ключ Web-API Steam. Зарегистрировать его можно здесь: ", + "Certificate Chain": "Цепочка сертификатов", + "Valid": "Действительный", + "Hide Tags": "Скрыть тэги", + "Title": "Название", + "Content": "Содержание", + "Post": "Опубликовать", + "Cancel": "Отмена", + "Created": "Создано", + "Unpin": "Открепить", + "Show Tags": "Показать тэги", + "recent": "Последнее", + "3h": "3 часа", + "6h": "6 часов", + "24h": "24 часа", + "1w": "1 неделя", + "No monitors available.": "Нет доступных мониторов.", + "Add one": "Добавить новый", + "Backup": "Резервная копия", + "Security": "Безопасность", + "Shrink Database": "Сжать Базу Данных", + "Current User": "Текущий пользователь", + "About": "О программе", + "Description": "Описание", + "Powered by": "Работает на", + "deleteStatusPageMsg": "Вы действительно хотите удалить эту страницу статуса?", + "Style": "Стиль", + "info": "ИНФО", + "warning": "ВНИМАНИЕ", + "danger": "ОШИБКА", + "primary": "ОСНОВНОЙ", + "light": "СВЕТЛЫЙ", + "dark": "ТЕМНЫЙ", + "New Status Page": "Новая страница статуса", + "Show update if available": "Показывать доступные обновления", + "Also check beta release": "Проверять обновления для бета версий", + "Add New Status Page": "Добавить страницу статуса", + "Next": "Далее", + "Accept characters: a-z 0-9 -": "Разрешены символы: a-z 0-9 -", + "Start or end with a-z 0-9 only": "Начало и окончание имени только на символы: a-z 0-9", + "No consecutive dashes --": "Запрещено использовать тире --", + "HTTP Options": "HTTP Опции", + "Authentication": "Аутентификация", + "HTTP Basic Auth": "HTTP Авторизация", + "PushByTechulus": "Push by Techulus", + "clicksendsms": "ClickSend SMS", + "GoogleChat": "Google Chat (только Google Workspace)", + "apiCredentials": "Учетные данные API", + "Done": "Готово", + "Info": "Инфо", + "Steam API Key": "Ключ API Steam", + "Pick a RR-Type...": "Выберите RR-Тип…", + "Pick Accepted Status Codes...": "Выберите принятые коды статуса…", + "Default": "По умолчанию", + "Please input title and content": "Пожалуйста, введите название и содержание", + "Last Updated": "Последнее обновление", + "Untitled Group": "Группа без названия", + "Services": "Сервисы", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "Пользователь API (включая префикс webapi_)", + "serwersmsAPIPassword": "Пароль API", + "serwersmsPhoneNumber": "Номер телефона", + "serwersmsSenderName": "Имя отправителя СМС (зарегистрированное через клиентский портал)", + "stackfield": "Stackfield", + "smtpDkimSettings": "Настройки DKIM", + "smtpDkimDesc": "Пожалуйста ознакомьтесь с {0} Nodemailer DKIM для использования.", + "documentation": "документацией", + "smtpDkimDomain": "Имя домена", + "smtpDkimKeySelector": "Ключ", + "smtpDkimPrivateKey": "Приватный ключ", + "smtpDkimHashAlgo": "Алгоритм хэша (необязательно)", + "smtpDkimheaderFieldNames": "Заголовок ключей для подписи (необязательно)", + "smtpDkimskipFields": "Заголовок ключей не для подписи (необязательно)", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "Конечная точка API", + "alertaEnvironment": "Среда", + "alertaApiKey": "Ключ API", + "alertaAlertState": "Состояние алерта", + "alertaRecoverState": "Состояние восстановления", + "Proxies": "Прокси", + "Setup Proxy": "Настройка прокси", + "Proxy Protocol": "Протокол Прокси", + "Proxy Server": "Прокси", + "Proxy server has authentication": "Прокси имеет аутентификацию", + "Reverse Proxy": "Обратный прокси", + "No Proxy": "Без прокси", + "default": "По умолчанию", + "enabled": "Включено", + "setAsDefault": "Установить по умолчанию", + "deleteProxyMsg": "Вы действительно хотите удалить этот прокси для всех мониторов?", + "proxyDescription": "Прокси должны быть привязаны к монитору, чтобы работать.", + "enableProxyDescription": "Этот прокси не будет влиять на запросы монитора, пока не будет активирован. Вы можете контролировать временное отключение прокси для всех мониторов через статус активации.", + "setAsDefaultProxyDescription": "Этот прокси будет по умолчанию включен для новых мониторов. Вы всё ещё можете отдельно отключать прокси в каждом мониторе.", + "Invalid": "Недействительный", + "AccessKeyId": "ID ключа доступа", + "SecretAccessKey": "Секретный ключ доступа", + "PhoneNumbers": "Номера телефонов", + "TemplateCode": "Код шаблона", + "SignName": "Подпись", + "Sms template must contain parameters: ": "Шаблон СМС должен содержать параметры: ", + "Bark Endpoint": "Конечная точка Bark", + "Bark Group": "Группа Bark", + "Bark Sound": "Звук уведомления Bark", + "WebHookUrl": "URL-адрес вебхука", + "SecretKey": "Секретный ключ", + "For safety, must use secret key": "В целях безопасности необходимо использовать секретный ключ", + "Device Token": "Токен устройства", + "Platform": "Платформа", + "Huawei": "Huawei(Хуавей)", + "High": "Высокий", + "Retry": "Повторить", + "Topic": "Тема", + "WeCom Bot Key": "Ключ бота WeCom", + "User": "Пользователь", + "Installed": "Установлено", + "Not installed": "Не установлено", + "Running": "Работает", + "Not running": "Не работает", + "Remove Token": "Удалить токен", + "Start": "Запустить", + "Stop": "Остановить", + "Uptime Kuma": "Uptime Kuma", + "Slug": "Slug", + "Accept characters:": "Принимаемые символы:", + "startOrEndWithOnly": "Начинается или заканчивается только на {0}", + "No consecutive dashes": "Без последовательных тире", + "The slug is already taken. Please choose another slug.": "Этот slug уже занят. Пожалуйста, выберите другой.", + "Page Not Found": "Страница не найдена", + "wayToGetCloudflaredURL": "(Скачать cloudflared с {0})", + "cloudflareWebsite": "Веб-сайт Cloudflare", + "Message:": "Сообщение:", + "Don't know how to get the token? Please read the guide:": "Не знаете, как получить токен? Пожалуйста, прочтите руководство:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Текущее соединение может быть потеряно, если вы в данный момент подключаетесь через туннель Cloudflare. Вы уверены, что хотите это остановить? Введите свой текущий пароль, чтобы подтвердить это.", + "HTTP Headers": "Заголовки HTTP", + "Trust Proxy": "Доверенный прокси", + "Other Software": "Другое программное обеспечение", + "For example: nginx, Apache and Traefik.": "К примеру: nginx, Apache и Traefik.", + "Please read": "Пожалуйста, прочитайте", + "Subject:": "Тема:", + "Valid To:": "Действителен до:", + "Days Remaining:": "Дней осталось:", + "Issuer:": "Издатель:", + "Fingerprint:": "Отпечаток:", + "No status pages": "Нет статусных страниц", + "Domain Name Expiry Notification": "Уведомление об истечении срока действия доменного имени", + "Proxy": "Прокси", + "Date Created": "Дата создания", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "HTTP-адрес OneBot", + "onebotMessageType": "Тип сообщения OneBot", + "onebotGroupMessage": "Группа", + "onebotPrivateMessage": "Личное", + "onebotUserOrGroupId": "ID группы/пользователя", + "onebotSafetyTips": "В целях безопасности необходимо установить токен доступа", + "PushDeer Key": "Ключ PushDeer", + "Footer Text": "Текст нижнего колонтитула", + "Show Powered By": "Показывать на чем создано", + "Domain Names": "Доменные имена", + "signedInDisp": "Вы вошли как {0}", + "signedInDispDisabled": "Аутентификация отключена.", + "RadiusSecret": "Секрет Radius", + "RadiusSecretDescription": "Общий секрет между клиентом и сервером", + "RadiusCalledStationId": "Идентификатор вызываемой станции", + "RadiusCalledStationIdDescription": "Идентификатор вызываемого устройства", + "RadiusCallingStationId": "Идентификатор вызывающей станции", + "RadiusCallingStationIdDescription": "Идентификатор вызывающего устройства", + "Certificate Expiry Notification": "Уведомление об истечении срока действия сертификата", + "API Username": "Имя пользователя API", + "API Key": "Ключ API", + "Recipient Number": "Номер получателя", + "From Name/Number": "Имя/номер отправителя", + "Leave blank to use a shared sender number.": "Оставьте пустым, чтобы использовать общий номер отправителя.", + "Octopush API Version": "Версия API Octopush", + "Legacy Octopush-DM": "устаревший Octopush-DM", + "endpoint": "конечная точка", + "octopushAPIKey": "\"API key\" из учетных данных HTTP API в панели управления", + "octopushLogin": "\"Login\" из учетных данных HTTP API в панели управления", + "promosmsLogin": "Логин API", + "promosmsPassword": "Пароль API", + "pushoversounds pushover": "Pushover (по умолчанию)", + "pushoversounds bike": "Велосипед", + "pushoversounds bugle": "Горн", + "pushoversounds cashregister": "Кассовый аппарат", + "pushoversounds classical": "Классический", + "pushoversounds cosmic": "Космический", + "pushoversounds falling": "Падающий", + "pushoversounds gamelan": "Гамелан", + "pushoversounds incoming": "Входящий", + "pushoversounds intermission": "Антракт", + "pushoversounds magic": "Магия", + "pushoversounds mechanical": "Механический", + "pushoversounds pianobar": "Пиано-бар", + "pushoversounds siren": "Сирена", + "pushoversounds spacealarm": "Космическая сигнализация", + "pushoversounds tugboat": "Буксирное судно", + "pushoversounds alien": "Инопланетная тревога (долгое)", + "pushoversounds climb": "Подъем (долгое)", + "pushoversounds persistent": "Стойкий (долгое)", + "pushoversounds echo": "Pushover Эхо (долгое)", + "pushoversounds updown": "Вверх вниз (долгое)", + "pushoversounds vibrate": "Только вибрация", + "pushoversounds none": "Нет (тихо)", + "pushyAPIKey": "Секретный ключ API", + "pushyToken": "Токен устройства", + "Using a Reverse Proxy?": "Используете обратный прокси?", + "Check how to config it for WebSocket": "Проверьте, как настроить его для WebSocket", + "Steam Game Server": "Игровой сервер Steam", + "Most likely causes:": "Наиболее вероятные причины:", + "The resource is no longer available.": "Ресурс больше не доступен.", + "There might be a typing error in the address.": "В адресе может быть опечатка.", + "What you can try:": "Что вы можете попробовать:", + "Retype the address.": "Повторите адрес.", + "Go back to the previous page.": "Вернуться на предыдущую страницу.", + "Coming Soon": "Скоро", + "wayToGetClickSendSMSToken": "Вы можете получить имя пользователя API и ключ API {here}.", + "Connection String": "Строка подключения", + "Query": "Запрос", + "settingsCertificateExpiry": "Окончание TLS сертификата", + "certificationExpiryDescription": "HTTPS Мониторы инициируют уведомление, когда срок действия сертификата TLS истечет:", + "Setup Docker Host": "Настройка хоста Docker", + "Connection Type": "Тип соединения", + "Docker Daemon": "Демон Docker", + "deleteDockerHostMsg": "Вы уверены, что хотите удалить этот узел docker для всех мониторов?", + "socket": "Сокет", + "tcp": "TCP / HTTP", + "Docker Container": "Docker контейнер", + "Container Name / ID": "Название контейнера / ID", + "Docker Host": "Хост Docker", + "Docker Hosts": "Хосты Docker", + "ntfy Topic": "Тема ntfy", + "Domain": "Домен", + "Workstation": "Рабочая станция", + "disableCloudflaredNoAuthMsg": "Вы находитесь в режиме без авторизации, пароль не требуется.", + "trustProxyDescription": "Доверять заголовкам 'X-Forwarded-*'. Если вы хотите получить правильный IP-адрес клиента, а ваш Uptime Kuma находится под Nginx или Apache, вам следует включить этот параметр.", + "wayToGetLineNotifyToken": "Вы можете получить токен доступа в {0}", + "Examples": "Примеры", + "Home Assistant URL": "URL-адрес Home Assistant", + "Long-Lived Access Token": "Токен доступа с длительным сроком службы", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Токен доступа с длительным сроком действия можно создать, нажав на имя вашего профиля (внизу слева) и прокрутив его вниз, затем нажмите Создать токен.", + "Notification Service": "Служба уведомлений", + "default: notify all devices": "по умолчанию: уведомлять все устройства", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Список служб уведомлений можно найти в Home Assistant в разделе \"Инструменты разработчика > Службы\", выполнив поиск по слову \"уведомление\", чтобы найти название вашего устройства/телефона.", + "Automations can optionally be triggered in Home Assistant:": "При желании автоматизацию можно активировать в Home Assistant.:", + "Trigger type:": "Тип триггера:", + "Event type:": "Тип события:", + "Event data:": "Данные события:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Затем выберите действие, например, переключите сцену на красный индикатор RGB..", + "Frontend Version": "Версия интерфейса", + "Frontend Version do not match backend version!": "Версия интерфейса не соответствует версии серверной части!", + "Base URL": "Базовый URL", + "goAlertInfo": "GoAlert — это приложение с открытым исходным кодом для составления расписания вызовов, автоматической эскалации и уведомлений (например, SMS или голосовых звонков). Автоматически привлекайте нужного человека, нужным способом и в нужное время! {0}", + "goAlertIntegrationKeyInfo": "Получите общий ключ интеграции API для сервиса в этом формате \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\", обычно это значение параметра токена скопированного URL.", + "goAlert": "GoAlert", + "backupOutdatedWarning": "Устарело: эта функция резервного копирования более не поддерживается. Поскольку добавлено множество функций, она не может создать или восстановить полную резервную копию.", + "backupRecommend": "Сделайте резервную копию тома или папки с данными (./data/) напрямую.", + "Optional": "Необязательно", + "squadcast": "Squadcast", + "SendKey": "Ключ отправки", + "SMSManager API Docs": "Документация к API SMSManager", + "Gateway Type": "Тип шлюза", + "SMSManager": "SMSManager", + "You can divide numbers with": "Вы можете делить числа с", + "or": "или", + "Maintenance": "Техобслуживание", + "Schedule maintenance": "Запланировать техобслуживание", + "affectedMonitorsDescription": "Выберите мониторы, которые будут затронуты во время техобслуживания", + "affectedStatusPages": "Показывать уведомление о техобслуживании на выбранных страницах статуса", + "atLeastOneMonitor": "Выберите хотя бы один затрагиваемый монитор", + "dnsPortDescription": "По умолчанию порт DNS сервера - 53. Вы можете изменить его в любое время.", + "Monitor": "Монитор | Мониторы", + "webhookAdditionalHeadersTitle": "Дополнительные Заголовки", + "recurringIntervalMessage": "Запускать 1 раз каждый день | Запускать 1 раз каждые {0} дней", + "error": "ошибка", + "statusMaintenance": "Техобслуживание", + "Affected Monitors": "Затронутые мониторы", + "Start of maintenance": "Начало техобслуживания", + "All Status Pages": "Все страницы статусов", + "Select status pages...": "Выберите страницу статуса…", + "resendEveryXTimes": "Повтор каждые {0} раз", + "resendDisabled": "Повторная отправка отключена", + "deleteMaintenanceMsg": "Вы действительно хотите удалить это техобслуживание?", + "critical": "критично", + "Custom Monitor Type": "Собственный тип монитора", + "markdownSupported": "Поддерживается синтаксис Markdown. Если используете HTML, избегайте пробелов в начале строки, чтобы не возникали проблемы с форматированием.", + "Passive Monitor Type": "Пассивный тип монитора", + "Specific Monitor Type": "Специфический тип монитора", + "Help": "Помощь", + "Game": "Игра", + "Resend Notification if Down X times consequently": "Повторно отправить уведомление, если не работает X раз подряд", + "General Monitor Type": "Основной тип монитора", + "weekdayShortWed": "Ср", + "weekdayShortThu": "Чт", + "weekdayShortFri": "Пт", + "weekdayShortSat": "Сб", + "weekdayShortSun": "Вс", + "dayOfMonth": "День месяца", + "Pick Affected Monitors...": "Выберите затронутые мониторы…", + "Custom": "Свой цвет", + "successMessage": "Сообщение об успехе", + "successMessageExplanation": "Сообщение MQTT, которое может рассматриваться как успешное", + "Custom CSS": "Пользовательские CSS", + "weekdayShortTue": "Вт", + "dayOfWeek": "День недели", + "confirmDeleteTagMsg": "Вы уверены, что хотите удалить этот тег? Мониторы, связанные с этим тегом не будут удалены.", + "loadingError": "Невозможно получить данные, пожалуйста попробуйте позже.", + "Packet Size": "Размер пакета", + "warningTimezone": "Используется часовой пояс сервера", + "weekdayShortMon": "Пн", + "ZohoCliq": "ZohoCliq", + "strategyManual": "Активен/Неактивен Вручную", + "lastDay": "Последний день", + "lastDay1": "Последний день месяца", + "lastDay2": "Второй последний день месяца", + "lastDay3": "Третий последний день месяца", + "lastDay4": "Четвертый последний день месяца", + "No Maintenance": "Нет техобслуживаний", + "pauseMaintenanceMsg": "Вы уверены что хотите поставить на паузу?", + "maintenanceStatus-under-maintenance": "На техобслуживании", + "maintenanceStatus-inactive": "Неактивен", + "maintenanceStatus-scheduled": "Запланирован(о)", + "maintenanceStatus-ended": "Закончился(ось)", + "maintenanceStatus-unknown": "Неизвестен", + "Display Timezone": "Показать часовой пояс", + "Server Timezone": "Часовой пояс сервера", + "statusPageMaintenanceEndDate": "Конец", + "IconUrl": "URL иконки", + "Enable DNS Cache": "(Устарело) Включить DNS кэш для мониторов HTTP(S)", + "Enable": "Включить", + "Disable": "Отключить", + "Single Maintenance Window": "Единое окно техобслуживания", + "Schedule Maintenance": "Запланировать техобслуживание", + "Date and Time": "Дата и время", + "DateTime Range": "Промежуток даты и времени", + "uninstalling": "Удаляется", + "dataRetentionTimeError": "Период хранения должен быть равен 0 или больше", + "infiniteRetention": "Установите 0 для бессрочного хранения.", + "enableGRPCTls": "Разрешить отправлять gRPC запрос через TLS соединение", + "Free Mobile API Key": "Ключ API Free Mobile", + "Edit Tag": "Редактировать тэг", + "webhookAdditionalHeadersDesc": "Устанавливает дополнительные заголовки, отправляемые с помощью веб-хука. Каждый заголовок должен быть определён как JSON ключ/значение.", + "topic": "Тема", + "Customize": "Персонализировать", + "Custom Footer": "Пользовательский нижний колонтитул", + "dnsCacheDescription": "Это может не работать на некоторых IPv6 окружениях, отключите это, если у вас возникают проблемы.", + "confirmUninstallPlugin": "Вы уверены, что хотите удалить этот плагин?", + "plugin": "Плагин | Плагины", + "install": "Установить", + "installing": "Устанавливается", + "uninstall": "Удалить", + "Recurring": "Повторяющийся", + "recurringInterval": "Интервал", + "smseagle": "SMSEagle", + "Google Analytics ID": "ID Google Аналитики", + "wayToGetZohoCliqURL": "Вы можете узнать как создать webhook URL тут {0}.", + "Effective Date Range": "Даты действия (Опционально)", + "wayToGetKookGuildID": "Включите \"Режим разработчика\" в настройках Kook, а затем нажмите правой кнопкой по гильдии чтобы скопировать её ID", + "Enable TLS": "Включить TLS", + "Integration Key": "Ключ интеграции", + "Integration URL": "URL интеграции", + "do nothing": "ничего не делать", + "smseagleTo": "Номер(а) телефона", + "smseagleGroup": "Название(я) группы телефонной книги", + "smseagleContact": "Имена контактов телефонной книги", + "smseagleRecipientType": "Тип получателя", + "smseagleRecipient": "Получатель(и) (если множество, должны быть разделены запятой)", + "smseagleToken": "Токен доступа API", + "smseagleUrl": "URL вашего SMSEagle устройства", + "smseagleEncoding": "Отправить в юникоде (по умолчанию=GSM-7)", + "smseaglePriority": "Приоритет сообщения (0-9, высший приоритет = 9)", + "Server Address": "Адрес сервера", + "Learn More": "Узнать больше", + "topicExplanation": "MQTT топик для мониторинга", + "Guild ID": "Идентификатор гильдии", + "Kook": "Kook", + "wayToGetKookBotToken": "Создайте приложение и получите токен бота по адресу {0}", + "Resend Notification if Down X times consecutively": "Повторно отправлять уведомление, если сбой произошёл X раз подряд", + "telegramProtectContent": "Запретить пересылку/сохранение", + "telegramProtectContentDescription": "Если включено, сообщения бота в Telegram будут запрещены для пересылки и сохранения.", + "telegramSendSilently": "Отправить без звука", + "telegramSendSilentlyDescription": "Пользователи получат уведомление без звука.", + "Maintenance Time Window of a Day": "Суточный интервал для техобслуживания", + "Clone Monitor": "Копия", + "Clone": "Клонировать", + "cloneOf": "Копия {0}", + "notificationRegional": "Региональный", + "Add New Tag": "Добавить новый тег", + "Body Encoding": "Тип содержимого запроса.(JSON or XML)", + "Strategy": "Стратегия", + "Free Mobile User Identifier": "Идентификатор пользователя Free Mobile", + "Auto resolve or acknowledged": "Автоматическое разрешение или подтверждение", + "auto acknowledged": "автоматическое подтверждение", + "auto resolve": "автоматическое разрешение", + "API Keys": "Ключи API", + "Expiry": "Срок действия", + "Expiry date": "Дата истечения срока действия", + "Don't expire": "Не истекает", + "Continue": "Продолжить", + "Add Another": "Добавить еще", + "Key Added": "Ключ добавлен", + "Add API Key": "Добавить ключ API", + "No API Keys": "Нет ключей API", + "apiKey-active": "Активный", + "apiKey-expired": "Истёк", + "apiKey-inactive": "Неактивный", + "Expires": "Истекает", + "disableAPIKeyMsg": "Вы уверены, что хотите отключить этот ключ API?", + "Generate": "Сгенерировать", + "pagertreeResolve": "Автоматическое разрешение", + "pagertreeDoNothing": "Ничего не делать", + "lunaseaTarget": "Цель", + "lunaseaDeviceID": "Идентификатор устройства", + "lunaseaUserID": "Идентификатор пользователя", + "Lowcost": "Бюджетный", + "pagertreeIntegrationUrl": "URL-адрес интеграции", + "pagertreeUrgency": "Срочность", + "pagertreeSilent": "Тихий", + "pagertreeLow": "Низкий", + "pagertreeMedium": "Средний", + "pagertreeHigh": "Высокий", + "pagertreeCritical": "Критический", + "high": "высокий", + "promosmsAllowLongSMS": "Разрешить длинные СМС", + "Economy": "Экономия", + "wayToGetPagerDutyKey": "Вы можете получить его, перейдя в Сервис -> Каталог сервисов -> (Выберите сервис) -> Интеграции -> Добавить интеграцию. Здесь вы можете искать \"Events API V2\". Подробнее {0}", + "apiKeyAddedMsg": "Ваш ключ API добавлен. Пожалуйста, обратите внимание на это сообщение, так как оно отображается один раз.", + "deleteAPIKeyMsg": "Вы уверены, что хотите удалить этот ключ API?", + "wayToGetPagerTreeIntegrationURL": "После создания интеграции Uptime Kuma в PagerTree скопируйте файл Endpoint. См. полную информацию {0}", + "telegramMessageThreadIDDescription": "Необязательный уникальный идентификатор для цепочки сообщений (темы) форума; только для форумов-супергрупп", + "grpcMethodDescription": "Имя метода преобразуется в формат camelCase, например, sayHello, check и т. д.", + "Proto Service Name": "Название службы Proto", + "Proto Method": "Метод Proto", + "Proto Content": "Содержание Proto", + "telegramMessageThreadID": "(Необязательно) ID цепочки сообщений", + "statusPageRefreshIn": "Обновление через: {0}", + "twilioAccountSID": "SID учетной записи", + "twilioAuthToken": "Токен авторизации / Секретный ключ API", + "twilioFromNumber": "С номера", + "twilioToNumber": "На номер", + "sameAsServerTimezone": "Аналогично часовому поясу сервера", + "startDateTime": "Начальная дата и время", + "endDateTime": "Конечная дата и время", + "cronExpression": "Выражение для Cron", + "cronSchedule": "Расписание: ", + "invalidCronExpression": "Неверное выражение Cron: {0}", + "ntfyUsernameAndPassword": "Логин и пароль", + "ntfyAuthenticationMethod": "Способ входа", + "Monitor Setting": "Настройка монитора {0}", + "Show Clickable Link": "Показать кликабельную ссылку", + "Badge Generator": "Генератор значков для {0}", + "Badge Type": "Тип значка", + "Badge Duration": "Срок действия значка", + "Badge Label": "Надпись для значка", + "Badge Prefix": "Значение префикса значка", + "Badge Label Color": "Цвет надписи значка", + "Badge Color": "Цвет значка", + "Badge Label Prefix": "Префикс надписи для значка", + "Open Badge Generator": "Открыть генератор значка", + "Badge Up Color": "Цвет значка для статуса \"Доступен\"", + "Badge Pending Color": "Цвет значка для статуса \"Ожидание\"", + "Badge Maintenance Color": "Цвет значка для статуса \"Техобслуживание\"", + "Badge Style": "Стиль значка", + "Badge Suffix": "Значение суффикса значка", + "Badge value (For Testing only.)": "Значение значка (только для тестирования)", + "Badge URL": "URL значка", + "Group": "Группа", + "Monitor Group": "Группа мониторов", + "Show Clickable Link Description": "Если флажок установлен, все, кто имеет доступ к этой странице состояния, могут иметь доступ к URL-адресу монитора.", + "pushoverMessageTtl": "TTL сообщения (в секундах)", + "Badge Down Color": "Цвет значка для статуса \"Недоступен\"", + "Badge Label Suffix": "Суффикс надписи для значка", + "Edit Maintenance": "Редактировать техобслуживание", + "Reconnecting...": "Переподключение...", + "Cannot connect to the socket server": "Не удаётся подключиться к сокет-серверу", + "Badge Warn Color": "Цвет значка для предупреждения", + "Badge Warn Days": "Значок для \"дней предупреждения\"", + "Badge Down Days": "Значок для \"дней недоступности\"", + "Home": "Главная", + "noGroupMonitorMsg": "Не доступно. Создайте сначала группу мониторов.", + "Close": "Закрыть", + "chromeExecutableDescription": "Для пользователей Docker, если Chromium еще не установлен, может потребоваться несколько минут для установки и отображения результата тестирования. Он занимает 1 ГБ дискового пространства.", + "chromeExecutable": "Исполняемый файл Chrome/Chromium", + "chromeExecutableAutoDetect": "Автообнаружение", + "Badge Preview": "Предпросмотр значка", + "Badge Duration (in hours)": "Срок действия значка (в часах)", + "twilioApiKey": "Ключ API (необязательно)", + "Expected Value": "Ожидаемое значение", + "Json Query": "JSON Запрос", + "Kafka Brokers": "Брокеры Kafka", + "Press Enter to add broker": "Нажмите Enter чтобы добавить брокера", + "Kafka Topic Name": "Название темы Kafka", + "Kafka Producer Message": "Сообщение продюсера Kafka", + "Kafka SASL Options": "Параметры SASL в Kafka", + "Mechanism": "Механизм", + "Pick a SASL Mechanism...": "Выберите механизм SASL…", + "AccessKey Id": "Id-значение AccessKey", + "Secret AccessKey": "Секретный ключ доступа", + "Session Token": "Токен сессии", + "Notify Channel": "Канал оповещений", + "aboutNotifyChannel": "Уведомление о канале вызовет настольное или мобильное уведомление для всех участников канала, независимо от того, установлена ли их доступность как активная или отсутствующая.", + "Enter the list of brokers": "Введите список брокеров", + "Enable Kafka SSL": "Включение протокола Kafka SSL", + "Enable Kafka Producer Auto Topic Creation": "Включение автоматического создания тем в Kafka Producer", + "Authorization Identity": "Авторизационная идентичность", + "Request Body": "Тело запроса", + "webhookCustomBodyDesc": "Определите пользовательское HTTP Body для запроса. Принимаются шаблонные переменные {msg}, {heartbeat}, {monitor}.", + "webhookBodyCustomOption": "Пользовательский объект", + "webhookBodyPresetOption": "Пресет - {0}", + "invertKeywordDescription": "Искать, чтобы ключевое слово отсутствовало, а не присутствовало.", + "filterActive": "Активный", + "filterActivePaused": "На паузе", + "Invert Keyword": "Инвертировать ключевое слово", + "tailscalePingWarning": "Для того чтобы использовать монитор Tailscale Ping, необходимо установить Uptime Kuma без Docker, а также установить на сервер клиент Tailscale.", + "PushDeer Server": "Сервер PushDeer", + "pushDeerServerDescription": "Оставьте пустым для использования официального сервера", + "showCertificateExpiry": "Показывать истекающий сертификат", + "Request Timeout": "Тайм-аут запроса", + "timeoutAfter": "Тайм-аут через {0} секунд", + "Select": "Выбрать", + "selectedMonitorCount": "Выбрано: {0}", + "Check/Uncheck": "Отметить/Снять", + "gamedigGuessPort": "Gamedig: Угадай порт", + "styleElapsedTime": "Прошедшее время под полосой частоты опроса", + "noOrBadCertificate": "Отсутствие сертификата", + "gamedigGuessPortDescription": "Порт, используемый протоколом Valve Server Query Protocol, может отличаться от порта клиента. Попробуйте это сделать, если монитор не может подключиться к серверу.", + "nostrSender": "Закрытый ключ отправителя (nsec)", + "wayToGetFlashDutyKey": "Чтобы интегрировать Uptime Kuma с Flashduty: Перейдите в раздел \"Каналы\" > (Выберите канал) > \"Интеграции\" > \"Добавить новую интеграцию\", выберите Uptime Kuma, и скопируйте URL-адрес Push.", + "styleElapsedTimeShowNoLine": "Показать (Без линии)", + "styleElapsedTimeShowWithLine": "Показать (С линией)", + "Server URL should not contain the nfty topic": "URL сервера не должен содержать тему nfty", + "nostrRecipients": "Открытые ключи получателей (npub)", + "nostrRecipientsHelp": "формат npub, по одному в строке", + "FlashDuty Severity": "Серьёзность", + "nostrRelays": "Реле Nostr", + "nostrRelaysHelp": "Один URL-адрес ретрансляции в каждой строке", + "enableNSCD": "Включить NSCD (Name Service Cache Daemon) для кэширования всех DNS-запросов", + "Saved.": "Сохранено.", + "setupDatabaseChooseDatabase": "Какую базу данных вы хотите использовать?", + "setupDatabaseEmbeddedMariaDB": "Ничего настраивать не нужно. Этот образ Docker уже содержит встроенную и настроенную MariaDB. Uptime Kuma будет подключаться к базе данных через Unix-сокет.", + "setupDatabaseSQLite": "Простой файл базы данных, рекомендуется для небольших установок. До версии 2.0.0 Uptime Kuma использовал SQLite в качестве базы данных по умолчанию.", + "setupDatabaseMariaDB": "Подключение к внешней базе данных MariaDB. Необходимо указать информацию для подключения.", + "dbName": "Имя базы данных", + "pushViewCode": "Как настроить Push-монитор? (Показать код)", + "programmingLanguages": "Языки программирования", + "Bark API Version": "Версия API Bark", + "monitorToastMessagesDescription": "Уведомления для мониторов исчезают через заданное время в секундах. Значение -1 отключает тайм-аут. Значение 0 отключает уведомления.", + "monitorToastMessagesLabel": "Уведомления", + "toastErrorTimeout": "Таймаут для уведомлений об ошибках", + "toastSuccessTimeout": "Таймаут для уведомлений об успехе", + "pushOthers": "Другие", + "successPaused": "Успешно приостановлено.", + "authUserInactiveOrDeleted": "Пользователь неактивен или удалён.", + "authInvalidToken": "Неверный токен.", + "authIncorrectCreds": "Неверное имя пользователя или пароль.", + "2faEnabled": "2FA включена.", + "2faAlreadyEnabled": "2FA уже включена.", + "2faDisabled": "2FA отключена.", + "successAdded": "Успешно добавлено.", + "successResumed": "Успешно возобновлено.", + "GrafanaOncallUrl": "URL-адрес Grafana Oncall", + "liquidIntroduction": "Шаблонность достигается с помощью языка шаблонов Liquid. Инструкции по использованию приведены в разделе {0}.", + "templateLimitedToUpDownNotifications": "доступно только для уведомлений UP/DOWN", + "emailTemplateLimitedToUpDownNotification": "доступен только для сигналов UP/DOWN, в противном случае null", + "leave blank for default body": "оставьте пустым для объекта по умолчанию", + "emailTemplateServiceName": "Название сервиса", + "emailCustomisableContent": "Настраиваемый контент", + "smtpLiquidIntroduction": "Следующие два поля являются шаблонизируемыми с помощью языка шаблонов Liquid. Инструкции по их использованию приведены в разделе {0}. Вот доступные переменные:", + "leave blank for default subject": "оставьте пустым для темы по умолчанию", + "emailCustomBody": "Пользовательский объект", + "emailTemplateHostnameOrURL": "Имя хоста или URL", + "emailTemplateStatus": "Статус", + "successAuthChangePassword": "Пароль успешно обновлён.", + "successBackupRestored": "Резервная копия успешно восстановлена.", + "successDisabled": "Успешно отключено.", + "successEnabled": "Успешно включено.", + "tagNotFound": "Тег не найден.", + "foundChromiumVersion": "Обнаружен Chromium/Chrome. Версии: {0}", + "templateMsg": "сообщение уведомления", + "templateHeartbeatJSON": "объект, описывающий сигнал", + "templateMonitorJSON": "объект, описывающий монитор", + "templateLimitedToUpDownCertNotifications": "доступно только для уведомлений UP/DOWN и об окончании срока действия сертификата", + "Reset Token": "Сброс токена", + "emailTemplateMonitorJSON": "объект, описывающий монитор", + "emailTemplateHeartbeatJSON": "объект, описывающий сигнал", + "emailTemplateMsg": "сообщение уведомления", + "successDeleted": "Успешно удалено.", + "successEdited": "Успешно изменено.", + "Browser Screenshot": "Скриншот браузера", + "noDockerHostMsg": "Не доступно. Сначала настройте хост Docker.", + "DockerHostRequired": "Пожалуйста, установите хост Docker для этого монитора.", + "successKeywordExplanation": "Ключевое слово MQTT, которое будет считаться успешным", + "successKeyword": "Ключевое слово успеха", + "statusPageSpecialSlugDesc": "Специальный ярлык {0}: эта страница будет отображаться, если ярлык не указан", + "ntfyPriorityHelptextAllExceptDown": "Все события отправляются с этим приоритетом, кроме {0}-событий, которые имеют приоритет {1}", + "self-hosted container": "контейнер, который хостится самостоятельно", + "remoteBrowserToggle": "По умолчанию Chromium работает внутри контейнера Uptime Kuma. Вы можете использовать удаленный браузер, переключив этот переключатель.", + "Remote Browsers": "Удаленные браузеры", + "Remote Browser": "Удаленный браузер", + "Add a Remote Browser": "Добавить удаленный браузер", + "Remote Browser not found!": "Удаленный браузер не найден!", + "remoteBrowsersDescription": "Удаленные браузеры — альтернатива локальному запуску Chromium. Установите такой сервис, как browserless.io, или подключитесь к своему собственному", + "settingUpDatabaseMSG": "Настройка базы данных. Это может занять некоторое время, пожалуйста, подождите.", + "setup a new monitor group": "настроить новую группу мониторов", + "openModalTo": "открыть модальное окно {0}", + "Add a domain": "Добавить домен", + "Remove domain": "Удалить домен '{0}'", + "Search monitored sites": "Поиск отслеживаемых сайтов", + "ntfyPriorityHelptextAllEvents": "Все события отправляются с максимальным приоритетом", + "useRemoteBrowser": "Использовать удаленный браузер", + "deleteRemoteBrowserMessage": "Вы уверены, что хотите удалить этот удаленный браузер для всех мониторов?", + "Remove the expiry notification": "Удалить дату истечения срока действия уведомления", + "Add a new expiry notification day": "Добавить новый день уведомления об истечении срока действия", + "What is a Remote Browser?": "Что такое удаленный браузер?", + "Mention group": "Упомянуть {group}", + "Your User ID": "Ваш идентификатор пользователя", + "Host URL": "URL-адрес хоста", + "locally configured mail transfer agent": "локальный почтовый агент", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Укажите имя хоста сервера, к которому хотите подключиться, или {localhost}, если планируете использовать {local_mta}", + "wayToGetHeiiOnCallDetails": "Как получить ID триггера и ключи API, описано в {documentation}", + "gtxMessagingApiKeyHint": "Вы можете найти свой ключ API на странице: Мои учетные записи маршрутизации > Показать информацию об учетной записи > Учетные данные API > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Номер телефона / Адрес источника пути передачи (АИПП)", + "Alphanumeric (recommended)": "Буквенно-цифровой (рекомендуется)", + "Originator type": "Тип источника", + "cellsyntOriginatortypeAlphanumeric": "Буквенно-цифровая строка (не более 11 символов). Получатели не могут ответить на это сообщение.", + "cellsyntOriginatortypeNumeric": "Числовое значение (не более 15 цифр) с номером телефона в международном формате, без 00 в начале (например, номер Великобритании 07920 110 000 должен быть задан, как 447920110000). Получатели могут ответить на сообщение.", + "cellsyntDestination": "Номер телефона получателя в международном формате с 00 в начале, за которым следует код страны, например, 00447920110000 для номера Великобритании 07920 110 000 (не более 17 цифр). Не более 25000 получателей, разделенных запятыми, на один HTTP-запрос.", + "callMeBotGet": "Здесь вы можете сгенерировать {endpoint} для {0}, {1} и {2}. Имейте в виду, что вы можете получить ограничение по скорости. Ограничения по скорости выглядят следующим образом: {3}", + "gtxMessagingFromHint": "На мобильных телефонах получатели видят АИПП как отправителя сообщения. Допускается использование до 11 буквенно-цифровых символов, шорткода, местного длинного кода или международных номеров ({e164}, {e212} или {e214})", + "wayToWriteWhapiRecipient": "Номер телефона с международным префиксом, но без знака плюс в начале ({0}), идентификатора контакта ({1}) или идентификатора группы ({2}).", + "cellsyntSplitLongMessages": "Разделять длинные сообщения на 6 частей. 153 x 6 = 918 символов.", + "Mentioning": "Упоминание", + "Don't mention people": "Не упоминайте людей", + "gtxMessagingToHint": "Международный формат, с «+» ({e164}, {e212} или {e214})", + "whapiRecipient": "Номер телефона / ID контакта / ID группы", + "API URL": "URL-адрес API", + "documentationOf": "{0} Документация", + "senderSevenIO": "Номер или имя отправителя", + "receiverSevenIO": "Номер получения", + "wayToGetSevenIOApiKey": "Зайдите на панель управления по адресу app.seven.io > разработчик > api key > зеленая кнопка добавить", + "receiverInfoSevenIO": "Если номер получателя не находится в Германии, то перед номером необходимо добавить код страны (например, для США код страны 1, тогда используйте 117612121212, вместо 017612121212)", + "apiKeySevenIO": "Ключ API SevenIO", + "Telephone number": "Номер телефона", + "Channel access token (Long-lived)": "Токен доступа к каналу (долговечный)", + "wayToGetWhapiUrlAndToken": "Вы можете получить API URL и токен, зайдя в нужный вам канал с {0}", + "To Phone Number": "На номер телефона", + "Originator": "Источник", + "cellsyntOriginator": "Виден на мобильном телефоне получателя как отправитель сообщения. Допустимые значения и функция зависят от параметра {originatortype}.", + "Destination": "Пункт назначения", + "Allow Long SMS": "Разрешить длинные SMS", + "max 15 digits": "максимум 15 цифр", + "max 11 alphanumeric characters": "максимум 11 буквенно-цифровых символов", + "Command": "Команда", + "Create new forum post": "Создать новый пост", + "forumPostName": "Название поста", + "postToExistingThread": "Создать пост в этой ветке", + "Select message type": "Выберите тип сообщения", + "Send to channel": "Отправить в канал", + "Refresh Interval": "Интервал обновления", + "ignoreTLSErrorGeneral": "Игнорировать ошибки TLS/SSL для подключения", + "CurlDebugInfoOAuth2CCUnsupported": "Полная поддержка потока клиентских учетных данных OAuth отсутствует в {curl}.{newline}Пожалуйста, получите токен доступа и передайте его через параметр {oauth2_bearer}.", + "now": "сейчас", + "time ago": "{0} назад", + "Refresh Interval Description": "Страница статуса будет полностью обновлена каждые {0} секунд", + "and": "и", + "e.g. {discordThreadID}": "например {discordThreadID}", + "ignoredTLSError": "TLS/SSL ошибки не учитываются", + "Debug": "Отладка", + "Copy": "Скопировать", + "CopyToClipboardError": "Не удалось скопировать: {error}", + "CopyToClipboardSuccess": "Скопировано!", + "firewalls": "файрволы", + "dns resolvers": "dns резолверы", + "docker networks": "докер-сети", + "CurlDebugInfoProxiesUnsupported": "Поддержка прокси в верхней {curl} команде в настоящее время не реализована.", + "RabbitMQ Nodes": "Узлы управления RabbitMQ", + "RabbitMQ Username": "Имя пользователя RabbitMQ", + "shrinkDatabaseDescriptionSqlite": "Триггерная база данных {vacuum} для SQLite. {auto_vacuum} уже включен, но он не дефрагментирует базу данных и не переупаковывает отдельные страницы базы данных, как это делает команда {vacuum}.", + "threadForumPostID": "ID поста Форума / Ветки", + "whatHappensAtForumPost": "Создать новый пост на форуме. Это НЕ отправит сообщение на текущий пост. Чтобы написать сообщение в текущем посте используйте \"{option}\"", + "wayToGetDiscordThreadId": "Получение идентификатора темы/сообщения на форуме аналогично получению идентификатора канала. Подробнее о том, как получить идентификаторы {0}", + "jsonQueryDescription": "Проанализируйте и извлеките определенные данные из ответа JSON сервера с помощью запроса JSON или используйте «$» для необработанного ответа, если не ожидается JSON. Затем результат сравнивается с ожидаемым значением в виде строк. См. документацию в {0} и используйте {1} для экспериментов с запросами.", + "aboutSlackUsername": "Изменяет отображаемое имя отправителя сообщения. Если вы хотите упомянуть кого-то, вместо этого включите его в понятное имя.", + "smspartnerApiurl": "Вы можете найти свой ключ API в панели управления по адресу {0}", + "smspartnerPhoneNumberHelptext": "Номер должен быть в международном формате {0}, {1}. Несколько чисел должны быть разделены {2}", + "cacheBusterParam": "Добавить параметр {0}", + "cacheBusterParamDescription": "Случайно генерируемый параметр для пропуска кэшей.", + "bitrix24SupportUserID": "Введите свой идентификатор пользователя в Bitrix24. Узнать ID можно по ссылке, зайдя в профиль пользователя.", + "mongodbCommandDescription": "Запустите команду MongoDB для базы данных. Информацию о доступных командах можно найти в {документации}", + "Community String": "Строка сообщества", + "snmpCommunityStringHelptext": "Эта строка действует как пароль для аутентификации и контроля доступа к устройствам с поддержкой SNMP. Сопоставьте его с конфигурацией вашего SNMP-устройства.", + "snmpOIDHelptext": "Введите OID для датчика или состояния, которое вы хотите отслеживать. Используйте инструменты управления сетью, такие как браузеры MIB или программное обеспечение SNMP, если вы не уверены в OID.", + "threemaSenderIdentity": "ID шлюза", + "threemaApiAuthenticationSecret": "Секрет ID-шлюза", + "threemaBasicModeInfo": "Примечание. Эта интеграция использует шлюз Threema в базовом режиме (шифрование на базе сервера). Более подробную информацию можно найти {0}.", + "privateOnesenderDesc": "Убедитесь, что номер телефона действителен. Чтобы отправить сообщение на личный номер телефона, например: 628123456789", + "Group Name": "Имя группы", + "Authorization Header": "Заголовок авторизации", + "Optional: Space separated list of scopes": "Необязательно: список областей действия, разделенный пробелам", + "Lost connection to the socket server.": "Потеряно соединение с сервером сокетов.", + "signl4Docs": "Дополнительную информацию о том, как настроить SIGNL4 и как получить URL-адрес вебхук SIGNL4, можно найти в {0}.", + "greater than": "больше чем", + "Alphanumerical string and hyphens only": "Только буквенно-цифровая строка и дефисы", + "Reveal": "Раскрытие", + "Elevator": "Лифт", + "Custom sound to override default notification sound": "Пользовательский звук для замены звука уведомления по умолчанию", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Уведомления, чувствительные ко времени, будут доставлены немедленно, даже если устройство находится в режиме «Не беспокоить».", + "rabbitmqNodesDescription": "Введите URL-адрес узлов управления RabbitMQ, включая протокол и порт. Пример: {0}", + "rabbitmqHelpText": "Чтобы использовать монитор, вам необходимо включить плагин управления в настройках RabbitMQ. Для получения дополнительной информации обратитесь к {rabitmq_documentation}.", + "Sound": "Звук", + "smspartnerPhoneNumber": "Номер(а) телефона", + "smspartnerSenderName": "Имя отправителя СМС", + "smspartnerSenderNameInfo": "Должно быть от 3 до 11 обычных символов", + "Message format": "Формат сообщения", + "Send rich messages": "Отправить сообщение в формате RCS", + "Bitrix24 Webhook URL": "URL-адрес вебхука Bitrix24", + "wayToGetBitrix24Webhook": "Вы можете создать вебхук, выполнив действия, описанные в {0}", + "OID (Object Identifier)": "OID (идентификатор объекта)", + "Condition": "Условие", + "SNMP Version": "Версия SNMP", + "Please enter a valid OID.": "Пожалуйста, введите действительный OID.", + "wayToGetThreemaGateway": "Вы можете зарегистрироваться на Threema Gateway {0}.", + "threemaRecipient": "Получатель", + "threemaRecipientType": "Тип получателя", + "threemaRecipientTypeIdentity": "Значение Threema-ID", + "threemaRecipientTypeIdentityFormat": "8 знаков", + "threemaRecipientTypePhone": "Номер телефона", + "threemaRecipientTypePhoneFormat": "Е.164, без ведущего +", + "threemaRecipientTypeEmail": "Адрес электронной почты", + "threemaSenderIdentityFormat": "8 символов, обычно начинается с *", + "apiKeysDisabledMsg": "Ключи API отключены, поскольку отключена аутентификация.", + "Host Onesender": "Хост Onesender", + "Token Onesender": "Токен Onesender", + "Recipient Type": "Тип получателя", + "Private Number": "Частный номер", + "groupOnesenderDesc": "Убедитесь, что GroupID действителен. Чтобы отправить сообщение в группу, например: 628123456789-342345", + "Group ID": "ID группы", + "wayToGetOnesenderUrlandToken": "Вы можете получить URL-адрес и токен, перейдя на веб-сайт Onesender. Дополнительная информация {0}", + "Add Remote Browser": "Добавить удаленный браузер", + "New Group": "Новая группа", + "OAuth2: Client Credentials": "OAuth2: учетные данные клиента", + "Authentication Method": "Метод аутентификации", + "Form Data Body": "Тело данных формы", + "OAuth Token URL": "URL-адрес токена OAuth", + "Client ID": "ID клиента", + "Client Secret": "Секрет клиента", + "OAuth Scope": "Область действия OAuth", + "Go back to home page.": "Вернуться на домашнюю страницу.", + "No tags found.": "Теги не найдены.", + "Cannot connect to the socket server.": "Невозможно подключиться к серверу сокетов.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "URL-адрес вебхук SIGNL4", + "Conditions": "Условия", + "conditionAdd": "Добавить условие", + "conditionDelete": "Удалить условие", + "conditionAddGroup": "Добавить группу", + "conditionDeleteGroup": "Удалить группу", + "conditionValuePlaceholder": "Значение", + "equals": "равно", + "not equals": "не равно", + "contains": "содержит", + "not contains": "не содержит", + "starts with": "начинается с", + "not starts with": "не начинается с", + "ends with": "заканчивается с", + "not ends with": "не заканчивается с", + "less than": "меньше чем", + "less than or equal to": "меньше или равно", + "greater than or equal to": "больше или равно", + "record": "запись", + "Notification Channel": "Канал уведомлений", + "Arcade": "Аркада", + "Correct": "Исправить", + "Fail": "Ошибка", + "Harp": "Арфа", + "Bubble": "Пузырь", + "Doorbell": "Дверной звонок", + "Flute": "Флейта", + "Money": "Деньги", + "Scifi": "Сай-фай", + "Clear": "Очистить", + "Guitar": "Гитара", + "Pop": "Поп", + "Time Sensitive (iOS Only)": "Чувствительность ко времени (только iOS)", + "From": "От", + "Can be found on:": "Можно найти: {0}", + "The phone number of the recipient in E.164 format.": "Номер телефона получателя в формате E.164.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Либо идентификатор отправителя текстового сообщения, либо номер телефона в формате E.164, если вы хотите иметь возможность получать ответы.", + "rabbitmqNodesRequired": "Пожалуйста, установите узлы для этого монитора.", + "rabbitmqNodesInvalid": "Пожалуйста, используйте полный URL-адрес (начинающийся с «http») для узлов RabbitMQ.", + "RabbitMQ Password": "Пароль RabbitMQ", + "SendGrid API Key": "Ключ API SendGrid", + "Separate multiple email addresses with commas": "Разделяйте несколько адресов электронной почты запятыми", + "-year": "-год", + "Json Query Expression": "Выражение запроса Json", + "templateServiceName": "имя сервиса", + "templateHostnameOrURL": "hostname или URL", + "templateStatus": "статус", + "telegramServerUrlDescription": "Чтобы обойти ограничения API бота Telegram или получить доступ в заблокированных регионах (например, в Китае или Иране), нажмите {0} для получения подробной информации. Значение по умолчанию: {1}", + "wayToGetWahaApiKey": "Ключ API — это значение переменной окружения WHATSAPP_API_KEY, которое вы использовали для запуска WAHA.", + "wayToGetWahaSession": "Из этой сессии WAHA отправляет уведомления на удостоверение личности чата. Вы можете найти его на приборной панели Waha.", + "wayToWriteWahaChatId": "Номер телефона с международным префиксом, но без знака плюс в начале ({0}), идентификатор контакта ({1}) или идентификатора группы ({2}). Уведомления отправляются на этот идентификатор чата от сеанса Waha.", + "wahaSession": "Сессия", + "wahaChatId": "Идентификатор чата (номер телефона / идентификатор контакта / идентификатор группы)", + "wayToGetWahaApiUrl": "URL-адрес вашего экземпляра WAHA.", + "YZJ Webhook URL": "URL вебхука YZJ", + "YZJ Robot Token": "Токен робота YZJ", + "telegramServerUrl": "(Необязательно) URL Сервера", + "telegramUseTemplate": "Используйте пользовательский шаблон сообщения", + "telegramUseTemplateDescription": "Если включено, сообщение будет отправлено с помощью пользовательского шаблона.", + "telegramTemplateFormatDescription": "Telegram позволяет использовать различные языки разметки в сообщениях. Подробности смотрите в документации Telegram — {0}.", + "Plain Text": "Простой текст", + "Message Template": "Шаблон сообщения", + "Template Format": "Формат шаблона", + "Font Twemoji by Twitter licensed under": "Шрифт Twemoji от Twitter лицензирован на условиях", + "smsplanetApiToken": "Токен для API SMSPlanet", + "smsplanetApiDocs": "Подробную информацию о получении токенов API можно найти в {the_smsplanet_documentation}.", + "the smsplanet documentation": "документация SMSPlanet", + "Phone numbers": "Номера телефонов", + "Sender name": "Имя отправителя", + "smsplanetNeedToApproveName": "Требуется одобрение в панели клиента", + "Add Tags": "Добавить тег", + "tagNameExists": "Тег с таким именем уже существует. Выберите его из списка или используйте другое имя.", + "Use HTML for custom E-mail body": "Используйте HTML для пользовательского текста E-Mail", + "Template plain text instead of using cards": "Шаблон простого текста вместо использования карт", + "issueWithGoogleChatOnAndroidHelptext": "Это также позволяет обойти баги входящего потока, как {issuetakerURL}", + "smseagleGroupV2": "Идентификатор группы Phonebook(s)", + "smseagleApiv2": "APIv2 (рекомендовано для новых интеграций)", + "smseagleApiv1": "APIv1 (для существующих проектов и обратной совместимости)", + "Path": "Путь", + "mqttWebSocketPath": "MQTT WebSocket путь", + "mqttWebsocketPathExplanation": "WebSocket путь для MQTT через WebSocket соединение (например, /mqtt)", + "mqttWebsocketPathInvalid": "Пожалуйста, используйте правильный формат пути WebSocket", + "mqttHostnameTip": "Пожалуйста, используйте этот формат {hostnameFormat}", + "smseagleApiType": "Версия API", + "smseagleComma": "Множественные должны быть разделены запятой", + "smseagleDocs": "Просмотрите документацию или доступность APIv2: {0}", + "smseagleTtsModel": "Модель текст-в-речь ID", + "smseagleDuration": "Длительность (в секундах)", + "smseagleMsgType": "Сообщение типа", + "smseagleMsgSms": "SMS сообщение (по умолчанию)", + "smseagleMsgTts": "Звонок текст-в-речь", + "smseagleMsgTtsAdvanced": "Текст-в-речь расширенный звонок", + "defaultFriendlyName": "Новый монитор", + "tagAlreadyOnMonitor": "Этот тег (имя и значение) уже отображается на мониторе или ожидает добавления.", + "tagAlreadyStaged": "Этот тег (имя и значение) уже подготовлен для этого пакета.", + "clearAllEventsMsg": "Вы уверены, что хотите удалить все события для всех мониторов? При этом связанные с сообщением данные удаляются безвозвратно и не могут быть отменены.", + "No monitors found": "Мониторы не найдены.", + "Could not clear events": "Не удалось удалить {failed}/{total} событий", + "FlashDuty Push URL": "URL-адрес Push", + "FlashDuty Push URL Placeholder": "Скопируйте со страницы интеграции оповещения", + "pingCountLabel": "Максимум пакетов", + "pingNumericLabel": "Числовой вывод", + "pingGlobalTimeoutLabel": "Глобальный тайм-аут", + "pingGlobalTimeoutDescription": "Общее время в секундах до прекращения проверки связи, независимо от отправленных пакетов", + "pingPerRequestTimeoutLabel": "Тайм-аут для каждого пинга", + "pingIntervalAdjustedInfo": "Интервал настраивается в зависимости от количества пакетов, глобального тайм-аута и тайм-аута каждого пинга", + "Custom URL": "Пользовательский URL-адрес", + "customUrlDescription": "Будет использоваться в качестве ссылки вместо URL-адреса монитора.", + "OneChatAccessToken": "Токен доступа OneChat", + "OneChatUserIdOrGroupId": "ID пользователя или группы OneChat", + "OneChatBotId": "ID бота OneChat", + "Add Another Tag": "Добавить еще один тег", + "Staged Tags for Batch Add": "Теги для пакетного добавления", + "Clear Form": "Очистить форму", + "SpugPush Template Code": "Шаблон кода", + "ntfyPriorityDown": "Приоритет для cобытий недоступности", + "Clear All Events": "Очистить все события", + "Events cleared successfully": "События успешно удалены.", + "smseagleContactV2": "Идентификаторы контакта в телефонной книге", + "smseagleMsgRing": "Звонок по телефону", + "pingCountDescription": "Количество пакетов, которые необходимо отправить перед остановкой", + "pingNumericDescription": "Если отмечено, вместо символьных имен хостов будут выводиться IP-адреса", + "pingPerRequestTimeoutDescription": "Это максимальное время ожидания (в секундах), прежде чем будет принято решение о потере одного пинг-пакета", + "smtpHelpText": "'SMTPS' проверяет работоспособность SMTP/TLS; 'Игнорировать TLS' подключается через обычный текст; 'STARTTLS' подключается, выдает команду STARTTLS и проверяет сертификат сервера. Ни одно из этих сообщений не отправляет письмо.", + "pause": "Пауза", + "Disable URL in Notification": "Отключить URL-адрес в уведомлении", + "Happy Eyeballs algorithm": "алгоритм Happy Eyeballs", + "Ip Family": "Cемейство IP-адресов", + "Manual": "Вручную", + "OAuth Audience": "Аудитория OAuth", + "ntfyPriorityHelptextPriorityHigherThanDown": "Обычный приоритет должен быть выше, чем приоритет {0}. Приоритет {1} выше, чем приоритет {0} {2}", + "ipFamilyDescriptionAutoSelect": "Использует {happyEyeballs} для определения семейства IP-адресов.", + "Optional: The audience to request the JWT for": "Опционально: аудитория, для которой запрашивается JWT", + "wayToWriteEvolutionRecipient": "Телефонный номер с международным префиксом, без знака плюс в начале ({0}), ИД Контакта ({1}) или ИД Группы ({2}).", + "wayToGetEvolutionUrlAndToken": "Вы можете получить API URL и токен перейдя в желанный канал через {0}", + "evolutionRecipient": "Телефонный номер / ИД Контакта / ИД Группы", + "evolutionInstanceName": "Имя экземпляра", + "brevoApiKey": "Ключ API Brevo", + "brevoApiHelp": "Создайте ключ API здесь: {0}", + "brevoFromName": "Имя отправителя", + "brevoFromEmail": "Почта отправителя", + "brevoLeaveBlankForDefaultName": "оставьте пустым для имени по умолчанию", + "brevoToEmail": "Почта получателя", + "brevoBccEmail": "Почта для скрытой копии", + "brevoCcEmail": "Почта для копии", + "brevoSeparateMultipleEmails": "Разделяйте несколько адресов электронной почты запятыми", + "brevoSubject": "Тема", + "Bot secret": "Секрет бота", + "Send UP silently": "Отправлять Доступен тихо", + "Send DOWN silently": "Отправлять Недоступен тихо", + "brevoLeaveBlankForDefaultSubject": "оставьте пустым для темы по умолчанию", + "Nextcloud host": "Хост Nextcloud", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Для установки бота Nextcloud Talk требуется административный доступ к серверу.", + "Conversation token": "Токен разговора", + "auto-select": "Автоматический выбор", + "Maximum Retries": "Максимальное количество попыток", + "Number of retry attempts if webhook fails": "Количество повторных попыток (каждые 60-180 секунд) в случае сбоя вебхука.", + "webhookPostMethodDesc": "POST подходит для большинства современных HTTP-серверов.", + "Enter a list of mobile": "Введите список мобильных", + "Dingtalk Mobile List": "Список мобильных", + "Mention Mobile List": "Упомянуть список мобильных", + "Mention User List": "Упомянуть список ID пользователей", + "Invalid userId": "Неверный ID пользователя [{userId}]", + "wayToGetBaleToken": "Вы можете получить токен здесь - {0}.", + "supportBaleChatID": "Поддерживаются ID чатов, групп и каналов", + "wayToGetBaleChatID": "Вы можете получить ID вашего чата, отправив сообщение боту и перейдя по этому URL для просмотра chat_id:", + "descriptionHelpText": "Отображается на внутренней панели. Markdown разрешен и очищается (сохраняет пробелы и отступы) перед отображением.", + "webhookGetMethodDesc": "GET отправляет данные в качестве параметров запроса и не позволяет настраивать тело запроса. Полезно для Push монитора Uptime Kuma.", + "Enter a list of userId": "Введите список ID пользователей", + "Dingtalk User List": "Список ID пользователей", + "HTTP Method": "HTTP-метод", + "Invalid mobile": "Неверный мобильный [{mobile}]", + "Template ID": "ID шаблона", + "wayToGetClickSMSIRTemplateID": "Ваш шаблон должен содержать поле {uptkumaalert}. Вы можете создать новый шаблон {здесь}.", + "deleteGroupMsg": "Вы уверены что хотите удалить эту группу?", + "deleteChildrenMonitors": "Также удалить дочерние мониторы и их дочерние элементы, если они есть | Также удалить все {count} дочерних монитора и их дочерние элементы, если они есть", + "Clone Maintenance": "Копировать техобслуживание", + "ariaPauseMaintenance": "Приостановить расписание этого техобслуживания", + "ariaCloneMaintenance": "Создать копию расписания этого техобслуживания", + "ariaResumeMaintenance": "Возобновить расписание этого техобслуживания", + "ariaEditMaintenance": "Изменить расписание этого техобслуживания", + "ariaDeleteMaintenance": "Удалить расписание этого техобслуживания", + "Recipient Numbers": "Номера получателя", + "twilioMessagingServiceSID": "SID службы обмена сообщениями (необязательно)", + "twilioApiKeyHelptext": "Ключ API является необязательным, но рекомендуется. Вы можете указать либо Account SID и authToken со странице TwilioConsole, либо Account SID и пару из ключа API и секретного ключа API", + "twilloMessagingServiceSIDHelptext": "Введите здесь свой SID службы обмена сообщениями, если вы используете {twillo_messaging_service_help_link} для управления отправителями и функциями", + "showOnlyLastHeartbeat": "Показывать только последнюю проверку", + "Binary Floor Control Protocol": "WebSocket Transport для BFCP (Протокол управления на двоичном уровне)", + "Swindon Web Server Protocol": "Протокол веб-сервера Swindon (кодирование JSON)", + "Softvelum WebSocket signaling protocol": "Протокол сигнализации Softvelum WebSocket", + "BACnet Secure Connect Hub Connection": "Подключение BACnet Secure Connect Hub", + "Done.best IoT Protocol": "Протокол Интернета вещей Done.best", + "Binary IRC Protocol": "Двоичный протокол IRC", + "Browser not supported": "Браузер не поддерживается", + "Unable to get permission to notify": "Не удалось получить разрешение на уведомление (запрос отклонен или проигнорирован).", + "Webpush Helptext": "Функция Web Push работает только при использовании SSL-подключений (HTTPS). Для устройств iOS веб-страницу необходимо предварительно добавить на главный экран.", + "Notifications Enabled": "Уведомления Включены", + "Allow Notifications": "Разрешить Уведомления", + "ignoreSecWebsocketAcceptHeaderDescription": "Позволяет серверу не отвечать заголовком Sec-WebSocket-Accept, если обновление веб-сокета прошло успешно.", + "Ignore Sec-WebSocket-Accept header": "Игнорировать {0} заголовок", + "wsSubprotocolDescription": "Введите список подпротоколов, разделенных запятыми. Более подробную информацию о подпротоколах см. в {documentation}", + "WebSocket Application Messaging Protocol": "WAMP (протокол обмена сообщениями приложений WebSocket)", + "Session Initiation Protocol": "WebSocket Transport для SIP (протокол инициирования сеанса)", + "Network API for Notification Channel": "API OMA RESTful Network для канала уведомлений", + "Web Process Control Protocol": "Протокол управления веб-процессами (WPCP)", + "Advanced Message Queuing Protocol": "Расширенный протокол очереди сообщений (AMQP) 1.0+", + "jsflow": "Протокол pubsub/queue jsFlow", + "Reverse Web Process Control": "Протокол обратного управления веб-процессами (RWPCP)", + "Extensible Messaging and Presence Protocol": "WebSocket Transport для расширяемого протокола обмена сообщениями и присутствия (XMPP)", + "Smart Home IP": "SHIP - IP Умный дом", + "Miele Cloud Connect Protocol": "Протокол Miele Cloud Connect", + "Push Channel Protocol": "Протокол Push-канала", + "Message Session Relay Protocol": "WebSocket Transport для MSRP (протокол ретрансляции сеансов сообщений)", + "Softvelum Low Delay Protocol": "Протокол Softvelum с низкой задержкой", + "OPC UA Connection Protocol": "Протокол соединения OPC UA", + "OPC UA JSON Encoding": "Кодирование OPC UA JSON", + "Broadband Forum User Services Platform": "USP (платформа услуг для пользователей широкополосного форума)", + "Constrained Application Protocol": "Протокол ограниченных приложений (CoAP)", + "Cobra Real Time Messaging Protocol": "Протокол обмена сообщениями в реальном времени Cobra", + "Declarative Resource Protocol": "Протокол декларативных ресурсов", + "BACnet Secure Connect Direct Connection": "Прямое подключение BACnet Secure Connect Hub", + "WebSocket Transport for JMAP": "WebSocket Transport для JMAP (протокол метаприложений JSON)", + "ITU-T T.140 Real-Time Text": "Текст в реальном времени ITU-T T.140", + "Collection Update": "Подпротокол Websocket обновления коллекции", + "Text IRC Protocol": "Текстовый протокол IRC", + "Penguin Statistics Live Protocol v3": "Протокол Penguin Statistics Live v3 (кодирование Protobuf)", + "certHostnameMismatch": "Имя хоста сертификата не соответствует URL-адресу монитора.", + "minimumIntervalWarning": "Интервалы менее 20 секунд могут привести к снижению производительности.", + "lowIntervalWarning": "Вы уверены, что хотите установить значение интервала меньше 20 секунд? Производительность может снизиться, особенно при наличии большого количества мониторов.", + "domainExpiryDescription": "Отправлять уведомление при истечении срока действия доменных имён через:", + "settingsDomainExpiry": "Истечение срока действия домена", + "labelDomainExpiry": "Срок действия домена.", + "labelDomainNameExpiryNotification": "Уведомление об истечении срока действия доменного имени", + "resendApiKey": "Ключ API Resend", + "resendApiHelp": "Создайте ключ API здесь {0}", + "resendFromName": "Имя отправителя", + "resendFromEmail": "Почта отправителя", + "Select All": "Выбрать все", + "Deselect All": "Отменить выбор всех", + "Enter the list of nodes": "Введите список узлов управления RabbitMQ", + "Press Enter to add node": "Нажмите клавишу Enter, чтобы добавить узел", + "wsCodeDescription": "Для получения дополнительной информации о кодах состояния, пожалуйста, обратитесь к {rfc6455}", + "Subprotocol(s)": "Подпротокол(ы)", + "Duration (Minutes)": "Продолжительность (Минуты)", + "SMTP Security": "Безопасность SMTP", + "Ignore STARTTLS": "Игнорировать STARTTLS", + "Use STARTTLS": "Использовать STARTTLS", + "resendToEmail": "Почта получателя", + "resendSubject": "Тема", + "resendLeaveBlankForDefaultName": "оставьте пустым для имени по умолчанию", + "imageResetConfirmation": "Изображение сброшено до значения по умолчанию", + "minPing": "Минимальный пинг", + "avgPing": "Средний пинг", + "maxPing": "Максимальный пинг", + "hostnameCannotBeIP": "DNS-имя хоста не может быть IP-адресом. Возможно, вы хотели указать значение в поле резолвер?", + "invalidHostnameOrIP": "Недопустимое имя хоста или IP-адрес. Имя хоста должно быть допустимым полным доменным именем (FQDN). Использование wildcard недопустимо. Допускается наличие символа подчёркивания или точки в конце.", + "invalidDNSHostname": "Недопустимое имя хоста или IP-адрес. Имя хоста должно быть допустимым полным доменным именем (FQDN). Может быть wildcard, содержать символ подчеркивания или заканчиваться точкой.", + "wildcardOnlyForDNS": "Wildcard имена хостов поддерживаются только для DNS-мониторов.", + "invalidURL": "Недопустимый URL-адрес", + "Google": "Google", + "Plausible": "Plausible", + "Matomo": "Matomo", + "Umami": "Umami", + "RSS Title": "Заголовок RSS", + "Leave blank to use status page title": "Оставьте поле пустым, чтобы использовать заголовок страницы состояния", + "year": "год | годов", + "domain_expiry_unsupported_monitor_type": "Мониторинг срока действия домена не поддерживается для этого типа монитора", + "Analytics Type": "Тип аналитики", + "days": "{n} день | {n} дней", + "hours": "{n} час | {n} часов", + "minutes": "{n} минута | {n} минут", + "minuteShort": "{n} мин | {n} мин", + "years": "{n} год | {n} лет", + "Sets end time based on start time": "Устанавливает время окончания на основе времени начала", + "Please set start time first": "Сначала установите время начала", + "snmpV3Username": "Имя пользователя SNMPv3", + "systemServiceDescriptionLinux": "Проверяет, активна ли служба systemd {service_name} в Linux", + "Resolver Server(s)": "DNS-сервер(а)", + "BodyInvalidFormatBecause": "Тело запроса не является валидным JSON, потому что {error}", + "steamApiKeyDescriptionAt": "Для мониторинга игрового сервера Steam требуется ключ Steam Web API. Вы можете зарегистрировать ключ API по адресу {url}", + "checkPriceAt": "Проверьте тарифы {service} по адресу {url}", + "HeadersInvalidFormatBecause": "Заголовки запроса не являются валидным JSON, потому что {error}", + "You can divide numbers with commas or semicolons": "Вы можете разделять номера с помощью {comma} или {semicolon}", + "Halo PSA": "Halo PSA", + "Quick Setup Guide": "Краткое руководство по настройке", + "Deploy a Google Apps Script as a web app and paste the URL here": "Разверните Google Apps Script как веб-приложение и вставьте URL сюда", + "Open your Google Spreadsheet": "Откройте вашу Google-таблицу", + "Google Apps Script Webhook URL": "URL вебхука Google Apps Script", + "Paste the script code (see below)": "Вставьте код скрипта (см. ниже)", + "Click Deploy → New deployment → Web app": "Нажмите Deploy → New deployment → Web app", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Установите «Execute as: Me» и «Who has access: Anyone»", + "Go to Extensions → Apps Script": "Перейдите в Extensions → Apps Script", + "Google Apps Script Code": "Код Google Apps Script", + "Copy to Clipboard": "Скопировать в буфер обмена", + "Copied to clipboard!": "Скопировано в буфер обмена!", + "Failed to copy to clipboard": "Не удалось скопировать в буфер обмена", + "WeCom Mentioned Mobile List": "Список упоминаемых номеров WeCom", + "WeCom Mentioned Mobile List Description": "Введите номера телефонов для упоминания. Разделяйте несколько номеров запятыми. Используйте {’@’}all, чтобы упомянуть всех.", + "password": "Пароль", + "enableSSL": "Включить SSL/TLS", + "mariadbUseSSLHelptext": "Включите, чтобы использовать зашифрованное соединение с базой данных. Требуется для большинства облачных баз данных.", + "mariadbCaCertificateLabel": "Сертификат CA", + "mariadbCaCertificateHelptext": "Вставьте сертификат CA в формате PEM для использования с самоподписанными сертификатами. Оставьте поле пустым, если база данных использует сертификат, подписанный публичным центром сертификации.", + "None (Successful Connection)": "Нет (успешное соединение)", + "Expand All Groups": "Развернуть все группы", + "mariadbSocketPathDetectedHelptext": "Подключение к базе данных в соответствии с переменной окружения {0}.", + "selectMonitorMsg": "Выберите мониторы для выполнения действий", + "selectAllMonitorsAria": "Выбрать все мониторы", + "Actions": "Действия", + "selectedMonitorCountMsg": "выбрано: {n} | выбрано: {n}", + "deselectAllMonitorsAria": "Снять выбор со всех мониторов", + "message": "сообщение", + "notificationOther": "Другие интеграции", + "deleteMonitorsMsg": "Вы уверены, что хотите удалить выбранные мониторы?", + "resumedMonitorsMsg": "Возобновлён {n} монитор | Возобновлено {n} мониторов", + "deletedMonitorsMsg": "Удалён {n} монитор | Удалено {n} мониторов", + "noMonitorsPausedMsg": "Ни один монитор не был приостановлен (активных не было)", + "noMonitorsResumedMsg": "Ни один монитор не был возобновлён (неактивных не было)", + "bulkDeleteErrorMsg": "Не удалось удалить {n} монитор | Не удалось удалить {n} мониторов", + "Analytics Script URL": "URL скрипта аналитики", + "Analytics ID": "ID аналитики", + "json_value": "Значение JSON", + "domain_expiry_unsupported_missing_target": "Для этого монитора не настроен корректный домен или имя хоста", + "Basic radio toggle button group": "Базовая группа переключателей (радиокнопки)", + "serwersmsRecipientTypePhone": "Номер телефона", + "serwersmsRecipientType": "Тип получателя", + "serwersmsGroupId": "ID группы", + "serwersmsRecipientTypeGroup": "Группа", + "serwersmsGroupIdHelptext": "ID группы или групп в панели клиента. Эти идентификаторы можно скачать с помощью действия groups / index или скопировать при редактировании группы в панели клиента.", + "sipsakPingWarning": "Чтобы использовать монитор SIP Options Ping, необходимо установить Uptime Kuma без Docker, а также установить клиент Sipsak на сервере.", + "mtls-auth-server-key-placeholder": "Тело ключа", + "Globalping - Access global monitoring probes": "Globalping — доступ к глобальным мониторинговым зондaм", + "Globalping API Token": "API-токен Globalping", + "globalpingApiTokenDescription": "Получите API-токен Globalping по адресу {0}.", + "GlobalpingHostname": "Публично доступная цель измерения. Обычно это имя хоста или IPv4/IPv6-адрес, в зависимости от типа измерения.", + "GlobalpingDescription": "Globalping предоставляет доступ к тысячам зондов, размещённых сообществом, для проведения сетевых тестов и измерений. Для всех анонимных пользователей установлен лимит 250 тестов в час. Чтобы увеличить лимит до 500 тестов в час, сохраните свой токен в {accountSettings}.", + "GlobalpingLocationDocs": "Полная документация по вводу местоположения", + "GlobalpingIpFamilyInfo": "Версия IP, которую следует использовать. Допускается только в том случае, если цель — имя хоста.", + "Protocol": "Протокол", + "GlobalpingResolverInfo": "IPv4/IPv6-адрес или полностью квалифицированное доменное имя (FQDN). По умолчанию используется локальный DNS-резолвер зонда. Вы можете изменить DNS-сервер в любое время.", + "account settings": "настройки аккаунта", + "Location": "Местоположение", + "Monitor Subtype": "Подтип монитора", + "Collapse All Groups": "Свернуть все группы", + "Open Badge Link Generator": "Открыть генератор ссылок значков", + "Badge Link Generator": "Генератор ссылок значков {0}", + "Badge Link Generator Helptext": "Ссылки на значки доступны для всех мониторов, привязанных к публичным страницам статуса. Подробнее см. в {documentation}.", + "systemServiceExpectedOutput": "Ожидаемый вывод: «{0}»", + "Sort options": "Параметры сортировки", + "Sort by status": "Сортировать по статусу", + "Sort by certificate expiry": "Сортировать по сроку действия сертификата", + "Sort by name": "Сортировать по имени", + "Sort by uptime": "Сортировать по аптайму", + "Service Name": "Имя службы", + "saveResponseForNotifications": "Сохранять успешный HTTP-ответ для уведомлений", + "saveErrorResponseForNotifications": "Сохранять HTTP-ответ с ошибкой для уведомлений", + "saveResponseDescription": "Сохраняет HTTP-ответ и делает его доступным в шаблонах уведомлений как {templateVariable}", + "responseMaxLength": "Максимальная длина ответа (байты)", + "responseMaxLengthDescription": "Максимальный размер сохраняемых данных ответа. Установите 0 для снятия ограничения. Ответы большего размера будут обрезаны. Значение по умолчанию: 1024 (1 КБ)", + "domain_expiry_unsupported_is_ip": "«{hostname}» является IP-адресом. Для мониторинга срока действия требуется доменное имя", + "Basic checkbox toggle button group": "Базовая группа переключателей (чекбоксы)", + "Severity": "Серьёзность", + "halopsa_setup_step4": "Выберите базовую аутентификацию и создайте имя пользователя и пароль. Затем введите или вставьте их в соответствующие текстовые поля выше", + "GRPC Options": "Параметры gRPC", + "Metadata": "Метаданные", + "noMonitorsSelectedWarning": "Вы создаёте техобслуживание без затронутых мониторов. Вы уверены, что хотите продолжить?", + "noMonitorsOrStatusPagesSelectedError": "Невозможно создать техобслуживание без затронутых мониторов или страниц статуса", + "username": "Имя пользователя", + "unknownDays": "Неизвестные дни", + "Monitors": "{n} Монитор | {n} Мониторов", + "cronScheduleDescription": "Расписание: {description}", + "systemServiceDescription": "Проверяет, активна ли системная служба {service_name}", + "screenshot of the website": "Скриншот сайта", + "halopsa_setup_step1": "Создайте Integration Runbook в HaloPSA (Configuration → Integrations → Integration Runbooks)", + "Disable STARTTLS": "Отключить STARTTLS", + "disableSTARTTLSDescription": "Включите этот параметр для SMTP-серверов, которые не поддерживают STARTTLS. В этом случае письма будут отправляться по незашифрованному соединению.", + "domain_expiry_public_suffix_too_short": "«.{publicSuffix}» слишком короткий для домена верхнего уровня", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Мониторинг срока действия домена недоступен для «.{publicSuffix}», так как для него не указан RDAP-сервис в IANA", + "halopsa_setup_step3": "Скопируйте URL вебхука и вставьте его в текстовое поле выше", + "Setup Instructions": "Инструкция по настройке", + "halopsa_setup_step2": "Настройте действия runbook для обработки оповещений (например, Create Ticket)", + "Splunk Rest URL": "REST URL Splunk", + "Message Format": "Формат сообщения", + "smscTranslit": "Транслитерация SMSC", + "TLS Alerts": "TLS-уведомления", + "TLS Alert Spec": "RFC 8446", + "Region": "Регион", + "PushDeer Server URL": "URL сервера PushDeer", + "resendLeaveBlankForDefaultSubject": "Оставьте пустым, чтобы использовать тему по умолчанию", + "mtls-auth-server-ca-label": "CA", + "Suppress Notifications": "Отключить уведомления", + "discordSuppressNotificationsHelptext": "Если включено, сообщения будут отправляться в канал, но не будут вызывать push- или настольные уведомления у получателей.", + "mtls-auth-server-cert-label": "Сертификат", + "mtls-auth-server-key-label": "Ключ", + "domain_expiry_unsupported_is_icann": "Домен \"{domain}\" не подходит для мониторинга срока действия, так как его публичный суффикс \".{publicSuffix}\" не управляется ICANN", + "mtls-auth-server-cert-placeholder": "Тело сертификата", + "systemService": "Системная служба", + "systemServiceName": "Имя службы", + "aliyun-template-requirements-and-parameters": "Шаблон SMS в Aliyun должен содержать параметры: {parameters}", + "OptionalParameters": "Необязательные параметры", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Из-за ограничений операторов включайте необязательные переменные на свой риск: сообщение может не доставиться", + "aliyun-template-optional-parameters": "Необязательные параметры: {parameters}", + "systemServiceDescriptionWindows": "Проверяет, запущена ли служба {service_name} в диспетчере служб Windows", + "Never": "Никогда", + "GrafanaOncallURL": "URL Grafana Oncall", + "Screenshot Delay": "Задержка скриншота (ожидание {milliseconds})", + "milliseconds": "{n} миллисекунда | {n} миллисекунд", + "screenshotDelayDescription": "При необходимости подождите указанное количество миллисекунд перед созданием скриншота. Максимум: {maxValueMs} мс (0,5 × интервал).", + "screenshotDelayWarning": "Более высокие значения дольше удерживают браузер открытым, что может увеличить потребление памяти при большом количестве одновременных мониторов.", + "To Number": "На номер", + "versionIs": "Версия: {version}", + "logoutCurrentUser": "Выйти из аккаунта {username}", + "Certificate Chain:": "Цепочка сертификатов:", + "lastUpdatedAtFromNow": "Последнее обновление: {date} ({fromNow})", + "createdAt": "Создано: {date}", + "lastUpdatedAt": "Последнее обновление: {date}", + "dateCreatedAtFromNow": "Дата создания: {date} ({fromNow})", + "Examples:": "Примеры: {0}", + "octopushEndpoint": "octopush (конечная точка: {url})", + "legacyOctopushEndpoint": "Устаревший Octopush-DM (endpoint: {url})", + "playground": "песочница", + "SSL/TLS": "SSL/TLS", + "Check Type": "Тип проверки", + "notificationChatPlatforms": "Чат платформы", + "notificationUniversal": "Универсальные", + "notificationPushServices": "Push-сервисы", + "notificationSmsServices": "SMS-сервисы", + "notificationEmail": "Электронная почта", + "passwordTooWeak": "Пароль слишком слабый. Он должен содержать буквенные и цифровые символы и быть длиной не менее 6 символов.", + "ntfyCall": "Телефонный звонок", + "ntfyCallHelptext": "Совершать телефонный звонок при срабатывании оповещения. Установите значение «да», чтобы использовать первый подтверждённый номер, или введите конкретный номер телефона (например, +79123456789). Требуется ntfy Pro и подтверждённый номер телефона.", + "expectedTlsAlertDescription": "Выберите TLS-уведомление, которое, как ожидается, вернёт сервер. Используйте {code}, чтобы проверить, что mTLS-эндпоинты отклоняют соединения без клиентских сертификатов. Подробнее см. в {link}.", + "templateAvailableVariables": "Доступные переменные", + "example": "Пример", + "Result": "Результат", + "frontendVersionIs": "Версия интерфейса: {version}", + "notificationIncidentManagement": "Управление инцидентами", + "notificationHomeAutomation": "Домашняя автоматизация", + "pausedMonitorsMsg": "Приостановлен {n} монитор | Приостановлено {n} мониторов", + "Copy the web app URL and paste it above": "Скопируйте URL веб-приложения и вставьте его выше", + "ntfyUseTemplate": "Настроить шаблоны уведомлений", + "ntfyUseTemplateDescription": "Включите этот параметр, чтобы настраивать заголовки и сообщения уведомлений с использованием шаблонов LiquidJS", + "ntfyCustomTitle": "Пользовательский шаблон заголовка", + "ntfyCustomMessage": "Пользовательский шаблон сообщения", + "ntfyNotificationTemplateFallback": "Оставьте пустым, чтобы использовать формат Uptime Kuma по умолчанию", + "systemServiceCommandHint": "Используемая команда: {command}", + "Check for": "Проверять наличие", + "Only retry if status code check fails": "Повторять попытки только при ошибке кода статуса", + "halopsa_username_desc": "Имя пользователя для аутентификации вебхука Halo PSA", + "mtls-auth-server-ca-placeholder": "CA сервера", + "Clear current filters": "Сбросить текущие фильтры", + "System Service": "Системная служба", + "End": "Конец", + "Show this Maintenance Message on which Status Pages": "Показывать это сообщение о техобслуживании на следующих страницах статуса", + "Endpoint": "Эндпоинт", + "Details": "Детали", + "Expected TLS Alert": "Ожидаемое TLS-уведомление", + "No incidents recorded": "Инциденты отсутствуют", + "Load More": "Загрузить ещё", + "Loading...": "Загрузка…", + "Pin this incident": "Закрепить этот инцидент", + "Incident description": "Описание инцидента", + "Past Incidents": "Прошлые инциденты", + "Incident title": "Заголовок инцидента", + "Edit Incident": "Редактировать инцидент", + "Please input content": "Пожалуйста, введите содержимое", + "Resolve": "Закрыть", + "Resolved": "Закрыт", + "deleteIncidentMsg": "Вы уверены, что хотите удалить этот инцидент?", + "slug is not found": "Slug не найден", + "Please input title": "Пожалуйста, введите название", + "Halo PSA Webhook URL": "URL вебхука Halo PSA", + "halopsa_password_desc": "Пароль для аутентификации вебхука Halo PSA", + "retryOnlyOnStatusCodeFailureDescription": "Если включено, повторные попытки будут выполняться только при ошибке проверки HTTP-кода статуса (например, если сервер недоступен). Если проверка кода статуса проходит успешно, но JSON-запрос завершается ошибкой, монитор будет сразу помечен как недоступный без повторных попыток.", + "Incident not found or access denied": "Инцидент не найден или доступ запрещён", + "Pinned incidents are shown prominently on the status page": "Закреплённые инциденты отображаются в приоритетном месте на странице статуса", + "GlobalpingLocation": "Поле местоположения принимает континенты, страны, регионы, города, ASN, провайдеров или облачные регионы. Вы можете комбинировать фильтры с помощью {plus} (например, {amazonPlusGermany} или {comcastPlusCalifornia}). Если задержка является важным показателем, используйте фильтры, чтобы сузить регион и избежать всплесков. {fullDocs}.", + "halopsa_webhook_url_desc": "Введите URL вебхука из Integration Runbook Halo PSA (Configuration > Integrations > Custom Integrations > Integration Runbooks). При создании вебхука выберите пункт «Can only be started from Halo and from a public endpoint».", + "Cloud ID": "Cloud ID", + "API Token": "API-токен", + "See Jira Cloud Docs": "См. документацию Jira Cloud", + "aboutJiraCloudId": "Подробнее о Jira Cloud ID: {0}", + "see Jira Cloud Docs": "см. документацию Jira Cloud", + "Jira Service Management": "Сервис менеджмент Jira", + "halopsa_field_timestamp": "Временная метка события в формате ISO 8601", + "halopsa_id_usage_hint": "💡 Совет: Используйте monitor_id для надежного сопоставления оповещений с заявками, а heartbeat_id — для отслеживания истории событий", + "halopsa_setup_step5": "Настройте сценарий автоматизации таким образом, чтобы он использовал monitor_id для сопоставления оповещений с существующими заявками", + "matrixUseTemplate": "Использовать собственный шаблон сообщения", + "matrixUseTemplateDescription": "Если эта функция включена, сообщение будет отправлено с использованием пользовательского шаблона.", + "discordMessageFormat": "Формат Сообщения", + "discordMessageFormatNormal": "Обычный (расширенные встраивания)", + "discordMessageFormatMinimalist": "Минималистическое (короткий статус)", + "discordMessageFormatCustom": "Собственный шаблон", + "slackIncludeGroupName": "Включить название группы мониторов", + "slackIncludeGroupNameDescription": "Если эта функция включена, путь к группе мониторов будет добавляться в уведомления, чтобы помочь различать мониторы с одинаковым именем в разных группах.", + "slackUseTemplate": "Используйте собственный шаблон сообщения", + "Webhook Payload Fields": "Поля полезной нагрузки webhook", + "halopsa_payload_desc": "В ваш webhook Halo PSA отправляются следующие поля:", + "halopsa_field_title": "Заголовок оповещения (всегда «Оповещение о времени безотказной работы Kuma»)", + "halopsa_field_status": "Статус мониторинга: ВКЛ., ВЫКЛ., УВЕДОМЛЕНИЕ или НЕИЗВЕСТНО", + "halopsa_field_monitor": "Имя монитора", + "halopsa_field_monitor_id": "Уникальный идентификатор монитора (null для тестовых уведомлений) — используйте его для сопоставления оповещений с заявками", + "halopsa_field_message": "Полное сообщение об оповещении со статусом и подробностями", + "teamsEnableTags": "Добавить теги", + "teamsEnableTagsDescription": "Если эта функция включена, сообщение будет содержать теги монитора.", + "slackUseTemplateDescription": "Если эта функция включена, сообщение будет отправлено с использованием пользовательского шаблона. Вы можете использовать шаблонизацию Liquid для включения информации о группе мониторов через monitorJSON.path или monitorJSON.pathName.", + "discordUseMessageTemplate": "Использовать собственный шаблон сообщения", + "discordUseMessageTemplateDescription": "Если эта функция включена, сообщение будет отправлено с использованием пользовательского шаблона (LiquidJS). Оставьте поле пустым, чтобы использовать формат Uptime Kuma по умолчанию.", + "discordMessageTemplate": "Шаблон Сообщения", + "halopsa_field_uptime_kuma_version": "Номер версии Uptime Kuma", + "domainExpiryNotificationHelp": "Количество дней можно указать в настройках.", + "signalUseTemplateDescription": "Если включено, сообщение будет отправлено с использованием пользовательского шаблона. Вы можете использовать шаблоны Liquid для настройки формата уведомления.", + "RegexMatch": "Введите регулярное выражение, соответствующее значению записи", + "teltonikaUnsafeTlsDescription": "Отключение проверки TLS-сертификата делает вас уязвимым для атак типа \"человек посередине\" (man-in-the-middle), что может привести к утечке данных и компрометации систем. Не отключайте проверку сертификата, если вы не готовы принять этот риск. Рекомендуем использовать LetsEncrypt с функцией автоматического продления.", + "teltonikaUsernameHelptext": "Рекомендация: Создайте отдельную учётную запись, которая ограничена только отправкой SMS-сообщений, и введите здесь имя пользователя", + "teltonikaPasswordHelptext": "Вы можете задать пароль пользователя API в вашем роутере Teltonika, например, {0}", + "monitorTypeGameServer": "Игровой сервер", + "monitorTypeDatabase": "Тип монитора базы данных", + "monitorTypeSpecial": "Особый", + "GlobalpingMonitorDescription": "Globalping даёт возможность использовать тысячи зондов, размещённых сообществом, для тестирования и измерения параметров сети. Для анонимных пользователей действует ограничение — 250 тестов в час. Чтобы удвоить лимит до 500 тестов в час, сохраните свой токен в {accountSettings}. Подробнее смотрите в {docs}.", + "Teltonika SMS Gateway": "SMS-шлюз Teltonika", + "teltonikaVersionWarning": "Этот поставщик уведомлений требует, чтобы на вашем устройстве Teltonika был RMS версии 7.14.0 или выше.", + "teltonikaUrl": "URL-адрес вашего устройства Teltonika", + "signalUseTemplate": "Использовать пользовательский шаблон сообщения", + "RecordMatch": "Совпадение значения записи", + "teltonikaUnsafeTls": "Игнорировать проверку сертификата", + "teltonikaUsername": "Имя пользователя API", + "teltonikaUrlHelptext": "URL-адрес должен быть указан полностью, например {0} или {1}.", + "teltonikaPassword": "Пароль API", + "teltonikaModem": "Id модема", + "teltonikaModemHelptext": "Id SMS-модема должен быть в формате {0}. Инструкции приведены в https://developers.teltonika-networks.com/reference/.", + "teltonikaPhoneNumber": "Номер телефона", + "teltonikaPhoneNumberHelptext": "Номер должен быть в международном формате {0}, {1}. Допускается только один номер.", + "certificateExpiryNotificationHelp": "Количество дней можно указать в настройках.", + "360messengerAuthToken": "Ключ API 360messenger", + "360messengerRecipient": "Номер(а) получателя", + "360messengerGroupId": "ID группы 360messenger", + "360messengerUseTemplate": "Использовать пользовательский шаблон сообщения", + "360messengerTemplate": "Шаблон сообщения 360messenger", + "360messengerGroupList": "Группы WhatsApp", + "360messengerSelectGroupList": "Выберите группу для добавления", + "360messengerSelectedGroupID": "ID выбранных групп", + "360messengerEnableSendToGroup": "Включить отправку в группы WhatsApp", + "360messengerCustomMessageTemplate": "Пользовательский шаблон сообщения", + "360messengerMessageTemplate": "Шаблон сообщения", + "360messengerWayToGetUrlAndToken": "Вы можете получить ключ API 360messenger из {0}.", + "360messengerWayToWriteRecipient": "Введите один или несколько телефонных номеров в международном формате без начального знака плюс (например, {0}). Разделите несколько номеров запятыми.", + "360messengerErrorNoApiKey": "Пожалуйста, сначала введите ключ API 360messenger.", + "360messengerErrorNoGroups": "Для этой учётной записи не было найдено ни одной группы WhatsApp.", + "360messengerErrorApi": "Не удалось загрузить список групп WhatsApp (Ошибка {statusCode}: {message}).", + "360messengerErrorGeneric": "Не удалось загрузить список групп WhatsApp: {message}", + "360messengerEnableCustomMessage": "Включить пользовательский шаблон сообщения вместо сообщения по умолчанию.", + "GlobalpingMultipleLocationsError": "Несколько местоположений не поддерживаются, пожалуйста, используйте одно местоположение для каждого монитора.", + "GlobalpingLocationDescription": "В поле \"Местоположение\" можно указать континенты, страны, регионы, города, ASN, интернет-провайдеров или облачные регионы. Фильтры можно комбинировать с помощью символа {plus} (например, {amazonPlusGermany} или {comcastPlusCalifornia}). Если задержка является важным показателем, используйте фильтры, чтобы сузить местоположение до небольшого региона, чтобы избежать всплесков, а для большей стабильности установите фильтр {datacenter}. {fullDocs}.", + "wayToGetMaxToken": "Токен бота MAX и другие данные вы можете получить по ссылке {0}.", + "maxApiUrlDescription": "Базовый URL API для MAX messenger. По умолчанию: https://platform-api.max.ru", + "wayToGetMaxChatID": "Укажите идентификатор чата MAX, в который должны приходить сообщения.", + "maxUseTemplate": "Использовать собственный шаблон сообщения", + "maxUseTemplateDescription": "Если включено, сообщение будет отправлено по настраиваемому шаблону.", + "maxTemplateFormatDescription": "MAX messenger поддерживает простой текст, Markdown и HTML форматирование.", + "telnyxApiKey": "Ключ API (V2)", + "telnyxApiKeyHelptext": "Ваш ключ API Telnyx V2 из раздела «API Keys» в портале Telnyx", + "telnyxPhoneNumber": "Номер отправителя", + "telnyxPhoneNumberHelptext": "Ваш номер Telnyx, с которого будет выполняться отправка (он должен быть зарегистрирован в вашем аккаунте Telnyx, например: +15551234567)", + "telnyxToNumber": "Номер получателя", + "telnyxToNumberHelptext": "Номер телефона получателя для SMS-уведомлений (например: +15559876543)", + "telnyxMessagingProfileId": "Messaging Profile ID (необязательно)", + "oracledbConnectionString": "База данных Oracle: {connectionString}", + "fluxerUseMessageTemplate": "Использовать пользовательский шаблон сообщения", + "wayToGetFluxerURL": "Вы можете получить его, перейдя в настройки нужного канала > Webhooks > Create Webhook > Copy Webhook URL.", + "fluxerMessageTemplate": "Шаблон сообщения", + "Fluxer Webhook URL": "URL вебхука Fluxer", + "evolutionCustomMessageTitle": "Пользовательское сообщение (необязательно)", + "evolutionCustomMessageDesc": "Оставьте поле пустым, чтобы использовать сообщение по умолчанию.", + "homeAssistantNotificationActionHelptext": "Список действий уведомлений можно найти в Home Assistant в разделе «Настройки > Инструменты разработчика > Действия». Чтобы найти нужные действия, выполните поиск по слову «notify». Введите только часть после «notify.», например для действия «notify.mobile_app_xyz» введите «mobile_app_xyz». Для встроенных мобильных уведомлений ищите «Send a notification via mobile_app_xyz», а не «Send a notification».", + "telnyxMessagingProfileIdHelptext": "При необходимости можно связать профиль сообщений Telnyx для управления настройками и возможностями отправителя", + "maxMessenger": "Мессенджер MAX", + "fluxerMessageFormat": "Формат сообщения", + "fluxerMessageFormatCustom": "Пользовательский шаблон", + "Notification Action": "Действие уведомления", + "API Version": "Версия API", + "Peer ID": "Идентификатор пользователя", + "vkApiVersionDescription": "Версия API ВКонтакте, используемая для запросов. Оставьте значение по умолчанию, если вам не нужна другая версия API ВКонтакте для совместимости.", + "vkDontParseLinks": "Отключить сниппеты ссылок", + "vkDontParseLinksDescription": "Если включено, ВКонтакте не будет создавать предпросмотр ссылок и сниппеты.", + "vkPeerIdDescription": "Введите целевой peer_id. Это значение будет использоваться в API.", + "fluxerMessageFormatNormal": "Обычный (расширенные встраивания)", + "fluxerUseMessageTemplateDescription": "Если включено, сообщение будет отправляться с использованием пользовательского шаблона (LiquidJS). Оставьте поле пустым, чтобы использовать формат Uptime Kuma по умолчанию.", + "fluxerMessageFormatMinimalist": "Минималистичный (краткий статус)", + "WebSocket Options": "Настройки WebSocket", + "openwaSession": "Сессия", + "wayToGetOpenwaApiUrl": "URL-адрес вашего экземпляра OpenWA.", + "wayToGetOpenwaApiKey": "Ключ API, используемый для вашего экземпляра OpenWA.", + "wayToGetOpenwaSession": "Идентификатор сессии, используемый OpenWA для отправки уведомлений.", + "openwaChatId": "Идентификатор чата (номер телефона / идентификатор контакта / идентификатор группы)", + "ntpOffsetThreshold": "Пороговое значение смещения по времени (мс)", + "smtpAdditionalHeadersTitle": "Дополнительные заголовки", + "smtpAdditionalHeadersDesc": "Пожалуйста, обратитесь к пользовательским заголовкам Nodemailer {0} для получения информации об их использовании.", + "VKTeams Bot Token Description": "Вы можете узнать, как получить токен бота и другие подробности на сайте {0}.", + "turbosmtpConsumerKey": "Ключ потребителя", + "turbosmtpConsumerSecret": "Секрет Потребителя", + "openwaCustomMessageTitle": "Пользовательское сообщение (необязательно)", + "openwaCustomMessageDesc": "Оставьте поле пустым, чтобы использовать сообщение по умолчанию.", + "ntpStratumThresholdHelp": "Отметьте как \"ниже\", если значение уровня равно или превышает это значение. Уровень указывает на качество источника времени (1 = GPS/атомные часы, 16 = несинхронизированные).", + "ntpDispersionThreshold": "Порог дисперсии корня (мс)", + "ntpOffsetThresholdHelp": "Отметьте как \"неактивно\", если смещение времени в миллисекундах превышает это значение.", + "egosmsPhoneNumberDescription": "Номер получателя с кодом страны, без знаков + или 00 (например, 2567XXXXXXXX).", + "wayToGetEgoSMSToken": "Вы можете получить имя пользователя и пароль для доступа к API EgoSMS здесь.", + "egosmsSenderDescription": "Идентификатор отправителя, отображаемый получателям (максимум 11 символов). По умолчанию используется EGOSMS, если поле пустое.", + "VKTeams Base Url Description": "URL-адрес базового API VKTeams может отличаться в локальном решении. По умолчанию: https://myteam.mail.ru", + "VKTeams Chat Id Description": "Для пользователей это их адрес электронной почты. Для групп и каналов идентификатор можно получить в их настройках и добавить к нему {'@'}chat.agent", + "Flowtriq Webhook URL": "URL вебхук", + "Flowtriq API Key": "Ключ API", + "flowtriqApiKeyDescription": "Необязательный API-ключ для аутентификации запросов веб-перехватчика к вашему экземпляру Flowtriq.", + "VKTeams Use Template Description": "Если эта функция включена, сообщение будет отправлено с использованием пользовательского шаблона.", + "VKTeams Template Format Description": "Для оформления сообщений VKTeams поддерживает форматирование в виде обычного текста, MarkdownV2 и HTML.", + "wayToGetBearSMSToken": "Вы можете получить имя пользователя и хеш-ключ API BearSMS по адресу {0}.", + "Hash Key": "Хеш-ключ", + "wayToWriteOpenwaChatId": "Номер телефона с международным префиксом, но без знака плюса в начале ({0}), идентификатор контакта ({1}) или идентификатор группы ({2}). Вы можете отправить на несколько идентификаторов чата, разделяя их запятыми.", + "VKTeams Use Template": "Использовать собственный шаблон сообщения", + "wxPusherDownloadApp": "скачать приложение", + "wxPusherDocs": "официальная инструкция", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) доставляет сообщения через автономное приложение WxPusher на 6 платформах — Android, iOS, HarmonyOS, Windows, macOS и Linux ({download}). Отсканируйте QR-код ниже, чтобы получить свой SPT, и вы готовы к отправке. Разделяйте несколько SPT запятой (,) — подробнее см. в {docs}.", + "gamedigToken": "Токен", + "gamedigTokenDescription": "Необязательно. Для некоторых игровых серверов требуется токен аутентификации.", + "Access Key": "Ключ доступа", + "ooredooUsernameDescription": "Адрес электронной почты, который вы используете для входа в панель управления массовой рассылкой SMS-сообщений Ooredoo.", + "ooredooAccessKeyDescription": "Его можно найти в разделе «Ключ доступа к API» на панели управления Ooredoo SMS. Вставьте его точно так, как показано — он автоматически кодируется в Base64 перед отправкой.", + "ooredooRecipientDescription": "Введите одно или несколько чисел, разделенных запятой или пробелом. Местные 7-значные номера автоматически начинаются с префикса 960.", + "ClickUp Workspace ID": "Идентификатор рабочей области ClickUp", + "ClickUp Workspace ID Description": "Вы можете найти его в URL-адресе вашей рабочей области, сразу после app.clickup.com/, например, app.clickup.com/12345678/home.", + "ClickUp Channel ID": "Идентификатор канала ClickUp", + "ClickUp Channel ID Description": "Вы можете найти его в URL-адресе канала чата, сразу после /chatr/, например, .../chatr/abc 123-4567.", + "ooredooBearerTokenDescription": "Токен, отображаемый после \"Authorization: Bearer\" в примере команды cURL на панели управления Ooredoo SMS.", + "ClickUp API Token": "Токен ClickUp API", + "Leave blank to use the default Ooredoo endpoint.": "Оставьте поле пустым, чтобы использовать конечную точку Ooredoo по умолчанию.", + "bearsmsPhoneNumberDescription": "Номер получателя с кодом страны, без знаков + или 00 (например, 9725XXXXXXXX).", + "ClickUp API Token Description": "Создать персональный токен доступа в ClickUp можно, перейдя в Настройки -> Интеграции и ClickApps -> API ClickUp -> Токен API", + "bearsmsSenderDescription": "Идентификатор отправителя (необязательно) отображается получателям (максимум 11 символов). Он должен быть одобрен в вашей учетной записи BearSMS, в противном случае будет использоваться отправитель по умолчанию для этой учетной записи.", + "plivoAuthToken": "Токен авторизации", + "plivoAuthTokenHelptext": "Ваш токен авторизации Plivo из консоли Plivo", + "plivoAuthIDHelptext": "Ваш идентификатор авторизации Plivo из консоли Plivo", + "plivoFromNumber": "С Номера", + "plivoFromNumberHelptext": "Номер телефона Plivo, с которого следует отправлять сообщение (например, +15551234567). Для SMS также можно использовать утвержденный буквенно-цифровой идентификатор отправителя; для голосовых вызовов требуется номер телефона Plivo.", + "plivoToNumber": "На Номер", + "plivoAuthID": "ID Авторизации", + "plivoToNumberHelptext": "Номер телефона получателя, на который будет отправлено SMS-сообщение или совершен голосовой вызов (например, +15559876543)", + "Bearer Token": "Токен предъявителя", + "pm2ExpectedStates": "Ожидаемое состояние: онлайн. Состояния «остановлено» и «ошибка» рассматриваются как «НЕВКЛЮЧЕНО».", + "pm2Description": "Проверяет, находится ли процесс PM2 {service_name} в сети", + "Select PM2 process": "Выбрать PM2 процесс", + "Refresh": "Обновить", + "PM2 Process": "Процесс PM2", + "plivoMessageType": "Тип Сообщения", + "plivoVoiceCall": "Голосовой вызов", + "plivoAnswerUrl": "URL ответа", + "plivoAnswerUrlHelptext": "URL-адрес, возвращающий XML-файл Plivo для вызова. Текст оповещения добавляется в качестве параметра запроса ?message=, поэтому ваша конечная точка может возвращать, например, {message}.", + "ntpThresholdsTitle": "Пороговые значения NTP", + "ntpDispersionThresholdHelp": "Отметьте как \"не работает\", если разброс значений корневых параметров превышает это значение. Разброс значений указывает на точность часов сервера.", + "pingletTopicUrlDescription": "URL-адрес публикации темы Pinglet, на которую будут отправляться оповещения, например, https://app.pinglet.co.uk/your-namespace/alerts. Тема создается при первой публикации, если она еще не существует.", + "Pinglet Topic URL": "URL темы", + "ntpStratumThreshold": "Порог стратификации", + "pingPacketSizeWarning": "Размер пакета меньше 16 не приводит к определению времени кругового пути в Linux", + "Connection Timeout": "Таймаут соединения" +} diff --git a/src/lang/sk.json b/src/lang/sk.json new file mode 100644 index 0000000..b6c4cdd --- /dev/null +++ b/src/lang/sk.json @@ -0,0 +1,1721 @@ +{ + "Settings": "Nastavenia", + "Help": "Pomoc", + "New Update": "Nová aktualizácia", + "Language": "Jazyk", + "Appearance": "Vzhľad", + "Theme": "Téma", + "General": "Základné", + "Primary Base URL": "Základná URL", + "Version": "Verzia", + "List": "Zoznam", + "Add": "Pridať", + "Add New Monitor": "Pridať nové sledovanie", + "Quick Stats": "Rýchly prehľad", + "Pending": "Čaká sa", + "statusMaintenance": "Údržba", + "Maintenance": "Údržba", + "General Monitor Type": "Všeobecný typ sledovania", + "Passive Monitor Type": "Pasívny typ sledovania", + "Specific Monitor Type": "Špecifický typ sledovania", + "pauseDashboardHome": "Pozastavené", + "Pause": "Pozastaviť", + "Status": "Stav", + "Message": "Správa", + "No important events": "Žiadne dôležité udalosti", + "Edit": "Upraviť", + "Delete": "Odstrániť", + "Current": "Aktuálne", + "Cert Exp.": "Platnosť cert.", + "day": "deň | dní", + "hour": "hodina | hodín", + "Response": "Odpoveď", + "Ping": "Ping", + "Keyword": "Kľúčové slovo", + "Friendly Name": "Názov", + "Port": "Port", + "Retries": "Opakovania", + "Resend Notification if Down X times consecutively": "Znovu odoslať oznámenie, ak je X-krát po sebe nedostupné", + "Advanced": "Pokročilé", + "checkEverySecond": "Skontrolovať každých {0} sekúnd", + "retryCheckEverySecond": "opakovať každých {0} sekúnd", + "resendEveryXTimes": "Odoslať znova po {0} pokusoch", + "resendDisabled": "Opakované odoslanie vypnuté", + "ignoreTLSError": "Ignorovať TLS/SSL chyby pre HTTPS stránky", + "upsideDownModeDescription": "Obrátiť stav. Pokiaľ je služba dostupná, zobrazuje sa ako NEDOSTUPNÁ.", + "Upside Down Mode": "Obrátený režim", + "Max. Redirects": "Max. počet presmerovaní", + "Accepted Status Codes": "Akceptované stavové kódy", + "Push URL": "Push URL", + "Save": "Uložiť", + "Notifications": "Oznámenia", + "Not available, please setup.": "Nedostupné, prosím nastavte.", + "Setup Notification": "Nastaviť oznamovanie", + "Dark": "Tmavá", + "Light": "Svetlá", + "Auto": "Automaticky", + "Normal": "Normálna", + "Bottom": "Dole", + "None": "Žiadne", + "Timezone": "Časová zóna", + "languageName": "Slovenčina", + "Dashboard": "Prehľad", + "Check Update On GitHub": "Skontrolovať aktualizáciu na GitHub-e", + "Up": "Dostupné", + "Down": "Nedostupné", + "Unknown": "Neznáme", + "markdownSupported": "Syntax Markdown je podporovaná. Ak používate HTML, vyhnite sa medzerám na začiatku, aby ste predišli problémom s formátovaním.", + "Name": "Názov", + "DateTime": "Dátum a čas", + "Resume": "Pokračovať", + "Uptime": "Doba prevádzky", + "Monitor": "Sledovanie | Sledovania", + "-day": "-dní", + "-hour": "-hodín", + "Monitor Type": "Typ sledovania", + "URL": "URL", + "Hostname": "Adresa servera", + "Heartbeat Interval": "Interval pulzu", + "Heartbeat Retry Interval": "Interval opakovania pulzu", + "retriesDescription": "Maximálny počet opakovaní pred tým, ako je služba označená ako nedostupná a je zaslaná notifikácia", + "maxRedirectDescription": "Maximálny počet presmerovaní, ktoré sa majú sledovať. Nastavte na 0, aby ste presmerovania deaktivovali.", + "needPushEvery": "Tuto adresu by ste mali volať každých {0} sekúnd.", + "pushOptionalParams": "Voliteľné parametre: {0}", + "Theme - Heartbeat Bar": "Téma - lišta pulzu", + "Game": "Hra", + "Search Engine Visibility": "Viditeľnosť vyhľadávačmi", + "Allow indexing": "Povoliť indexovanie", + "Change Password": "Zmeniť heslo", + "Current Password": "Aktuálne heslo", + "New Password": "Nové heslo", + "Repeat New Password": "Zopakovať nové heslo", + "Update Password": "Aktualizovať heslo", + "Disable Auth": "Vypnúť autentifikáciu", + "Enable Auth": "Zapnúť autentifikáciu", + "Please use this option carefully!": "Túto možnosť používajte opatrne!", + "Logout": "Odhlásiť sa", + "Leave": "Odísť", + "I understand, please disable": "Rozumiem, vypnite to", + "Yes": "Áno", + "No": "Nie", + "Username": "Používateľské meno", + "Password": "Heslo", + "Login": "Prihlásiť sa", + "No Monitors, please": "Žiadne sledovanie, prosím", + "add one": "pridať jeden", + "Notification Type": "Typ oznámenia", + "Email": "E-mail", + "Test": "Test", + "Certificate Info": "Informácie o certifikáte", + "Resolver Server": "DNS server", + "Last Result": "Posledný výsledok", + "Repeat Password": "Zopakovať heslo", + "Import Backup": "Importovať zálohu", + "Export Backup": "Exportovať zálohu", + "Export": "Exportovať", + "Import": "Importovať", + "respTime": "Čas odozvy (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Predvolene povolené", + "Create": "Vytvoriť", + "Clear Data": "Vyčistiť údaje", + "Events": "Udalosti", + "Heartbeats": "Pulzy", + "Auto Get": "Získať automaticky", + "Schedule maintenance": "Naplánovať údržbu", + "Affected Monitors": "Príslušné sledovania", + "Pick Affected Monitors...": "Vybrať príslušné sledovania…", + "Start of maintenance": "Začiatok údržby", + "All Status Pages": "Všetky stavové stránky", + "Select status pages...": "Vybrať stránky stavu…", + "alertNoFile": "Vyberte súbor na import.", + "alertWrongFileType": "Vyberte súbor JSON.", + "Clear all statistics": "Vymazať všetky štatistiky", + "Skip existing": "Preskočiť existujúce", + "Overwrite": "Prepísať", + "Options": "Možnosti", + "Keep both": "Ponechať obe", + "Setup 2FA": "Nastavenie 2FA", + "Disable 2FA": "Zakázať 2FA", + "2FA Settings": "Nastavenia 2FA", + "Two Factor Authentication": "Dvojfaktorová autentifikácia", + "Inactive": "Neaktívne", + "Token": "Token", + "Show URI": "Zobraziť URI", + "Tags": "Štítky", + "Add New below or Select...": "Pridať novú nižšie alebo vybrať…", + "Tag with this value already exist.": "Štítok s touto hodnotou už existuje.", + "color": "Farba", + "value (optional)": "hodnota (voliteľné)", + "Gray": "Sivá", + "Red": "Červená", + "Orange": "Oranžová", + "Green": "Zelená", + "Indigo": "Indigo", + "Purple": "Fialová", + "Pink": "Ružová", + "Custom": "Vlastná", + "Avg. Ping": "Priemerný ping", + "Avg. Response": "Priemerný čas odpovede", + "Entry Page": "Vstupná stránka", + "No Services": "Žiadne služby", + "All Systems Operational": "Všetky systémy funkčné", + "Partially Degraded Service": "Čiastočne zhoršená služba", + "Degraded Service": "Degradovaná služba", + "Add Group": "Pridať skupinu", + "Add a monitor": "Pridať sledovanie", + "Edit Status Page": "Upraviť stavovú stránku", + "Go to Dashboard": "Prejsť na prehľad", + "Status Page": "Stavová stránka", + "Status Pages": "Stavové stránky", + "defaultNotificationName": "Moje {notification} upozornenie ({number})", + "here": "tu", + "Required": "Povinné", + "Post URL": "Post URL", + "Content Type": "Druh obsahu", + "webhookJsonDesc": "{0} je vhodný pre všetky moderné servery HTTP, ako napríklad Express.js", + "webhookFormDataDesc": "{multipart} je dobré pre PHP. JSON bude potrebné analyzovať pomocou {decodeFunction}", + "Generate": "Generovať", + "Discourage search engines from indexing site": "Zabrániť vyhľadávačom v indexovaní stránky", + "disableauth.message1": "Ste si istý, že chcete {disableAuth}?", + "disable authentication": "vypnúť autentifikáciu", + "disableauth.message2": "Je navrhnutý pre scenáre, {intendThirdPartyAuth} pred Uptime Kuma, ako je Cloudflare Access, Authelia alebo iné autentifikačné mechanizmy.", + "where you intend to implement third-party authentication": "kde máte v úmysle implementovať autentifikáciu treťou stranou", + "Confirm": "Potvrdiť", + "Remember me": "Zapamätať si ma", + "Resource Record Type": "Typ záznamu", + "Create your admin account": "Vytvorte si účet administrátora", + "Apply on all existing monitors": "Použiť na všetky existujúce sledovania", + "Verify Token": "Overiť token", + "Enable 2FA": "Povoliť 2FA", + "Active": "Aktívne", + "Add New Tag": "Pridať nový štítok", + "Tag with this name already exist.": "Štítok s týmto názvom už existuje.", + "Blue": "Modrá", + "Search...": "Hľadať…", + "statusPageNothing": "Nič tu nie je, pridajte skupinu alebo sledovanie.", + "webhookAdditionalHeadersTitle": "Ďalšie položky", + "webhookAdditionalHeadersDesc": "Nastaví ďalšie hlavičky odoslané pomocou webhooku. Každá hlavička by mala byť definovaná ako JSON kľúč/hodnota.", + "Webhook URL": "URL adresa webhooku", + "Application Token": "Token aplikácie", + "Server URL": "URL adresa servera", + "Priority": "Priorita", + "statusPageRefreshIn": "Obnovenie za: {0}", + "emojiCheatSheet": "Ťahák emotikonov: {0}", + "Read more": "Prečítajte si viac", + "appriseInstalled": "Apprise je nainštalovaný.", + "Reconnecting...": "Prepájanie...", + "Request Timeout": "Časový limit požiadavky", + "styleElapsedTimeShowWithLine": "Zobraziť (s riadkom)", + "webhookCustomBodyDesc": "Zadajte vlastné HTTP Body pre request. Šablónové premenné {msg}, {heartbeat}, {monitor} sú akceptované.", + "timeoutAfter": "časový limit vyprší po {0} sekundách", + "styleElapsedTime": "Uplynutý čas pod lištou pulzu", + "styleElapsedTimeShowNoLine": "Zobraziť (Žiadny riadok)", + "filterActive": "Aktívne", + "filterActivePaused": "Pozastavené", + "Home": "Domov", + "Cannot connect to the socket server": "Nemožno sa pripojiť k socket serveru", + "Invert Keyword": "Prevrátiť kľúčové slovo", + "Expected Value": "Očakávaná hodnota", + "Json Query": "Json dotaz", + "Method": "Metóda", + "successKeywordExplanation": "Kľúčové slovo MQTT, ktoré sa bude považovať za úspešné", + "setupDatabaseChooseDatabase": "Ktorú databázu chcete použiť?", + "pushViewCode": "Ako používať Push sledovanie? (Zobraziť kód)", + "setupDatabaseEmbeddedMariaDB": "Nemusíte nič nastavovať. Tento obraz Docker má automaticky integrovanú a nakonfigurovanú databázu MariaDB. Uptime Kuma sa pripojí k tejto databáze prostredníctvom Unix socketu.", + "setupDatabaseMariaDB": "Pripojenie k externej databáze MariaDB. Musíte nastaviť informácie o pripojení k databáze.", + "setupDatabaseSQLite": "Jednoduchý databázový súbor, ktorý sa odporúča pre malé nasadenia. Pred verziou 2.0.0 používal Uptime Kuma ako predvolenú databázu SQLite.", + "Host URL": "URL adresa servera", + "Search monitored sites": "Vyhľadávanie sledovaných stránok", + "successKeyword": "Kľúčové slovo úspechu", + "startOrEndWithOnly": "Začínať alebo končiť iba s {0}", + "wayToGetCloudflaredURL": "(Stiahnite si cloudflared z {0})", + "Don't know how to get the token? Please read the guide:": "Neviete, ako získať token? Prečítajte si príručku:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Ak sa práve pripájate cez tunel Cloudflare, aktuálne pripojenie sa môže stratiť. Ste si istí, že to chcete zastaviť? Zadajte svoje aktuálne heslo a potvrďte ho.", + "Certificate Chain": "Reťazec certifikátu", + "settingUpDatabaseMSG": "Prebieha nastavovanie databázy. Môže to chvíľu trvať, prosím buďte trpezliví.", + "dbName": "Názov databázy", + "Add one": "Pridať jeden", + "No Monitors": "Žiadne sledovania", + "Untitled Group": "Skupina bez názvu", + "Services": "Služby", + "Invalid": "Neplatné", + "The slug is already taken. Please choose another slug.": "Slug je už obsadený. Vyberte si prosím iný slug.", + "No Proxy": "Žiadny proxy", + "Authentication": "Overenie", + "HTTP Basic Auth": "Základné HTTP overenie", + "New Status Page": "Nová stavová stránka", + "Reverse Proxy": "Reverzný proxy server", + "Backup": "Záloha", + "About": "O programe", + "cloudflareWebsite": "Webová stránka Cloudflare", + "HTTP Headers": "HTTP hlavičky", + "Other Software": "Iný softvér", + "For example: nginx, Apache and Traefik.": "Napríklad: nginx, Apache a Traefik.", + "Please read": "Prečítajte si prosím", + "pushOthers": "Ostatné", + "Created": "Vytvorené", + "ignoreTLSErrorGeneral": "Ignorovať chybu TLS/SSL pre pripojenie", + "programmingLanguages": "Programovacie jazyky", + "No monitors available.": "Žiadne sledovania nie sú k dispozícii.", + "templateLimitedToUpDownCertNotifications": "dostupné len pre oznámenia typu DOSTUPNÉ/NEDOSTUPNÉ/Platnosť certifikátu", + "templateLimitedToUpDownNotifications": "dostupné iba pre oznámenia typu DOSTUPNÉ/NEDOSTUPNÉ", + "BodyInvalidFormat": "Telo požiadavky nie je platný JSON: ", + "liquidIntroduction": "Šablóna sa dosahuje pomocou šablónovacieho jazyka Liquid. Pokyny na použitie nájdete v {0}.", + "templateMsg": "správa oznámenia", + "templateHeartbeatJSON": "objekt popisujúci pulz", + "templateMonitorJSON": "objekt popisujúci sledovanie", + "Monitor History": "História sledovania", + "clearDataOlderThan": "Uchovávať údaje histórie sledovania po dobu {0} dní.", + "PasswordsDoNotMatch": "Heslá sa nezhodujú.", + "records": "záznamy", + "One record": "Jeden záznam", + "steamApiKeyDescription": "Na sledovania herného servera služby Steam potrebujete kľúč Web-API služby Steam. Kľúč API si môžete zaregistrovať tu: ", + "Current User": "Aktuálny používateľ", + "topic": "Téma", + "topicExplanation": "Téma MQTT na sledovanie", + "recent": "Nedávne", + "Last Updated": "Naposledy aktualizované", + "Switch to Light Theme": "Prepnutie na svetelnú tému", + "Show Tags": "Zobraziť štítky", + "Hide Tags": "Skryť štítky", + "Description": "Popis", + "Page Not Found": "Stránka sa nenašla", + "locally configured mail transfer agent": "lokálne nakonfigurovaný agent na prenos pošty", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Zadajte buď názov hostiteľa servera, ku ktorému sa chcete pripojiť, alebo {localhost}, ak chcete používať {local_mta}", + "Reset Token": "Resetovať token", + "Done": "Hotovo", + "Info": "Informácie", + "Security": "Zabezpečenie", + "Steam API Key": "Kľúč API služby Steam", + "Pick a RR-Type...": "Vyberte typ RR…", + "Discard": "Zrušiť zmeny", + "Select": "Vybrať", + "selectedMonitorCount": "Vybrané: {0}", + "Check/Uncheck": "Označiť/Odznačiť", + "Powered by": "Poháňané", + "proxyDescription": "Aby proxy servery fungovali, musia byť priradené k sledovaniu.", + "enableProxyDescription": "Tento proxy server nebude mať vplyv na požiadavky sledovania, kým nebude aktivovaný. Môžete ovládať dočasné vypnutie proxy servera zo všetkých sledovaní podľa stavu aktivácie.", + "Valid": "Platné", + "No consecutive dashes": "Žiadne po sebe idúce pomlčky", + "statusPageSpecialSlugDesc": "Špeciálny slug {0}: táto stránka sa zobrazí, ak nie je uvedený žiadny slug", + "Next": "Ďalej", + "shrinkDatabaseDescription": "Pomocou tejto možnosti vykonáte príkaz VACUUM nad databázou SQLite. Ak bola databáza vytvorená po vydaní verzie 1.10.0, funkcia AUTO_VACUUM je už zapnutá a táto akcia sa nevyžaduje.", + "Customize": "Prispôsobenie", + "Custom Footer": "Vlastná päta", + "Custom CSS": "Vlastné CSS", + "deleteStatusPageMsg": "Ste si istí, že chcete odstrániť túto stavovú stránku?", + "Proxies": "Proxy", + "default": "Predvolené", + "enabled": "Zapnuté", + "setAsDefault": "Nastaviť ako predvolené", + "deleteProxyMsg": "Ste si istí, že chcete odstrániť toto proxy pre všetky sledovania?", + "Body": "Telo", + "Message:": "Správa:", + "webhookBodyPresetOption": "Uložená hodnota - {0}", + "webhookBodyCustomOption": "Vlastné telo", + "Headers": "Hlavičky", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "Hlavičky požiadavky nie sú platný JSON: ", + "Shrink Database": "Zmenšiť databázu", + "Pick Accepted Status Codes...": "Vybrať akceptovateľné stavové kódy…", + "Default": "Predvolené", + "HTTP Options": "Možnosti HTTP", + "Create Incident": "Vytvoriť incident", + "Title": "Názov", + "Content": "Obsah", + "Style": "Štýl", + "info": "info", + "warning": "varovanie", + "danger": "nebezpečenstvo", + "error": "chyba", + "User": "Používateľ", + "Slug": "Slug", + "Accept characters:": "Prípustné znaky:", + "appriseNotInstalled": "Apprise nie je nainštalovaný. {0}", + "critical": "kritické", + "primary": "primárne", + "light": "svetlé", + "dark": "tmavé", + "Please input title and content": "Prosím, zadajte názov a obsah", + "Switch to Dark Theme": "Prepnutie na tmavú tému", + "Cancel": "Zrušiť", + "setAsDefaultProxyDescription": "Tento proxy server bude predvolene zapnutý pre nové sledovania. Proxy server môžete stále vypnúť pre každé sledovanie samostatne.", + "Installed": "Nainštalované", + "Not installed": "Nie je nainštalované", + "Running": "Funguje", + "Remove Token": "Odstrániť token", + "Start": "Spustiť", + "Stop": "Zastaviť", + "Add New Status Page": "Pridať novú stavovú stránku", + "grpcMethodDescription": "Názov metódy sa prevedie do formátu camelCase, napríklad sayHello, kontrola atď.", + "deleteDockerHostMsg": "Ste si istí, že chcete odstrániť tohto docker hostiteľa pre všetky sledovania?", + "Container Name / ID": "Názov kontajnera / ID", + "telegramSendSilentlyDescription": "Odošle správu v tichosti. Používatelia dostanú oznámenie bez zvuku.", + "trustProxyDescription": "Dôverovať hlavičkám 'X-Forwarded-*'. Ak chcete získať správnu IP adresu klienta a vaša služba Uptime Kuma sa nachádza za proxy serverom, napríklad Nginx alebo Apache, mali by ste túto funkciu povoliť.", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Dlhodobý prístupový token môžete vytvoriť kliknutím na názov svojho profilu (vľavo dole) a rolovaním na spodok, potom kliknite na Vytvoriť token.", + "Event data:": "Údaje udalosti:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Potom vyberte akciu, napríklad prepnite scénu na miesto, kde je svetlo RGB červené.", + "warningTimezone": "Používa časové pásmo servera", + "lastDay1": "Posledný deň mesiaca", + "smtpLiquidIntroduction": "Nasledujúce dve polia je možné šablónovať pomocou šablónovacieho jazyka Liquid. Pokyny na použitie nájdete v časti {0}. Toto sú dostupné premenné:", + "wayToGetDiscordURL": "Toto nastavenie nájdete v časti Nastavenia servera -> Integrácie -> Zobraziť webhooky -> Nový webhook", + "wayToGetLineChannelToken": "Najprv pristupte k {0}, vytvorte poskytovateľa a kanál ( Rozhranie API správ), potom môžete získať prístupový token kanála a ID používateľa z vyššie uvedených položiek ponuky.", + "enableDefaultNotificationDescription": "Toto oznámenie bude predvolene povolené pre nové sledovania. Toto oznámenie môžete stále vypnúť pre každé sledovanie samostatne.", + "or": "alebo", + "strategyManual": "Aktívne/neaktívne ručne", + "cloneOf": "Duplikát {0}", + "smtp": "E-mail (SMTP)", + "secureOptionNone": "Žiadne / STARTTLS (25, 587)", + "Trigger type:": "Typ spúšťača:", + "maintenanceStatus-inactive": "Neaktívne", + "confirmUninstallPlugin": "Určite chcete odinštalovať tento modul?", + "dnsPortDescription": "Port servera DNS. Predvolená hodnota je 53. Port môžete kedykoľvek zmeniť.", + "noDockerHostMsg": "Nie je k dispozícii. Najprv nastavte Docker hostiteľa.", + "Subject:": "Predmet:", + "Valid To:": "Platí do:", + "Days Remaining:": "Zostávajúce dni:", + "Issuer:": "Vydavateľ:", + "Fingerprint:": "Odtlačok:", + "No status pages": "Žiadne stavové stránky", + "Domain Name Expiry Notification": "Oznámenie o vypršaní platnosti domény", + "Remove the expiry notification": "Odstrániť deň oznámenia o vypršaní platnosti", + "Proxy": "Proxy", + "Date Created": "Dátum vytvorenia", + "Footer Text": "Text v pätičke", + "RadiusCallingStationIdDescription": "Identifikátor volajúceho zariadenia", + "Certificate Expiry Notification": "Oznámiť o skončení platnosti certifikátu", + "API Username": "Používateľské meno API", + "API Key": "Kľúč API", + "Show update if available": "Zobraziť aktualizáciu, ak je k dispozícii", + "Also check beta release": "Skontrolovať aj beta verziu", + "Using a Reverse Proxy?": "Používanie reverzného proxy servera?", + "Steam Game Server": "Herný server služby Steam", + "Most likely causes:": "Najpravdepodobnejšie príčiny:", + "The resource is no longer available.": "Tento zdroj už nie je k dispozícii.", + "There might be a typing error in the address.": "V adrese môže byť preklep.", + "What you can try:": "Čo môžete vyskúšať:", + "Retype the address.": "Znovu zadajte adresu.", + "Go back to the previous page.": "Návrat na predchádzajúcu stránku.", + "settingsCertificateExpiry": "Vypršanie platnosti certifikátu TLS", + "certificationExpiryDescription": "HTTPS sledovania aktivujú oznámenie, keď vyprší platnosť certifikátu TLS v:", + "Setup Docker Host": "Nastavenie Docker hostiteľa", + "Connection Type": "Typ pripojenia", + "Docker Daemon": "Docker démon", + "DockerHostRequired": "Nastavte Docker hostiteľa pre toto sledovanie.", + "tcp": "TCP / HTTP", + "Docker Container": "Docker kontajner", + "telegramSendSilently": "Poslať potichu", + "telegramProtectContent": "Ochrana preposielania/ukladania", + "telegramProtectContentDescription": "Ak je zapnuté, správy bota v Telegrame budú chránené pred preposielaním a ukladaním.", + "supportTelegramChatID": "Podpora Priameho chatu / Skupiny / ID chatu kanála", + "wayToGetTelegramChatID": "ID chatu môžete získať tak, že botovi pošlete správu a prejdete na túto adresu URL a uvidíte chat_id:", + "YOUR BOT TOKEN HERE": "VÁŠ TOKEN BOTA TU", + "chatIDNotFound": "ID chatu nebolo nájdené; najprv pošlite správu tomuto botovi", + "disableCloudflaredNoAuthMsg": "Ste v režime bez autorizácie, heslo sa nevyžaduje.", + "wayToGetLineNotifyToken": "Prístupový token môžete získať z {0}", + "Examples": "Príklady", + "Home Assistant URL": "URL adresa Home Assistant", + "Long-Lived Access Token": "Dlhodobý prístupový token", + "Automations can optionally be triggered in Home Assistant:": "Automatizácie možno voliteľne spúšťať v aplikácii Home Assistant:", + "Frontend Version": "Verzia frontendu", + "Frontend Version do not match backend version!": "Verzia frontendu sa nezhoduje s verziou backendu!", + "backupRecommend": "Namiesto toho zálohujte zväzok alebo priečinok s údajmi (./data/) priamo.", + "Optional": "Voliteľné", + "sameAsServerTimezone": "Rovnaké ako časové pásmo servera", + "startDateTime": "Dátum/čas začatia", + "endDateTime": "Dátum/čas skončenia", + "cronSchedule": "Harmonogram: ", + "invalidCronExpression": "Neplatný výraz Cron: {0}", + "recurringInterval": "Interval", + "Recurring": "Opakujúce sa", + "weekdayShortTue": "Ut", + "weekdayShortWed": "St", + "weekdayShortThu": "Št", + "weekdayShortFri": "Pia", + "weekdayShortMon": "Pon", + "weekdayShortSat": "So", + "weekdayShortSun": "Ne", + "lastDay": "Posledný deň", + "lastDay2": "2. posledný deň mesiaca", + "lastDay3": "3. posledný deň mesiaca", + "lastDay4": "4. posledný deň mesiaca", + "No Maintenance": "Žiadna údržba", + "maintenanceStatus-unknown": "Neznáme", + "IconUrl": "URL adresa ikony", + "chromeExecutableAutoDetect": "Automatická detekcia", + "chromeExecutableDescription": "Pre používateľov nástroja Docker, ak Chromium ešte nie je nainštalované, môže inštalácia a zobrazenie výsledku testu trvať niekoľko minút. Vyžaduje 1 GB miesta na disku.", + "dnsCacheDescription": "V niektorých prostrediach IPv6 nemusí fungovať, ak narazíte na problémy, vypnite to.", + "Single Maintenance Window": "Jedno okno údržby", + "install": "Nainštalovať", + "installing": "Inštaluje sa", + "uninstall": "Odinštalovať", + "uninstalling": "Odinštalováva sa", + "notificationRegional": "Regionálne", + "Clone Monitor": "Duplikovať sledovanie", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignorovať chybu TLS", + "From Email": "Od e-mailu", + "emailCustomisableContent": "Prispôsobiteľný obsah", + "emailCustomSubject": "Vlastný predmet", + "leave blank for default subject": "nechajte prázdne pre predvolený predmet", + "emailCustomBody": "Vlastné telo", + "leave blank for default body": "nechajte prázdne pre predvolené telo", + "emailTemplateServiceName": "Názov služby", + "emailTemplateHostnameOrURL": "Názov hostiteľa alebo adresa URL", + "emailTemplateStatus": "Stav", + "emailTemplateMonitorJSON": "objekt popisujúci sledovanie", + "emailTemplateHeartbeatJSON": "objekt popisujúci pulz", + "Discord Webhook URL": "URL adresa Discord webhooku", + "Bot Display Name": "Zobrazovacie meno bota", + "Prefix Custom Message": "Predpona vlastnej správy", + "forumPostName": "Názov príspevku vo fóre", + "threadForumPostID": "ID vlákna / príspevku na fóre", + "e.g. {discordThreadID}": "napr. {discordThreadID}", + "whatHappensAtForumPost": "Vytvorte nový príspevok vo fóre. Toto NEPOŠLE správy do existujúceho príspevku. Ak chcete odoslať príspevok v existujúcom príspevku, použite „{option}“", + "wayToGetDiscordThreadId": "Získanie id vlákna / príspevku na fóre je podobné získaniu id kanála. Prečítajte si viac o tom, ako získať id {0}", + "wayToGetTeamsURL": "Môžete sa naučiť, ako vytvoriť URL adresu webhooku {0}.", + "wayToGetZohoCliqURL": "Môžete sa naučiť, ako vytvoriť URL adresu webhooku {0}.", + "needSignalAPI": "Musíte mať klienta signálu s rozhraním REST API.", + "wayToCheckSignalURL": "Na tejto adrese URL si môžete pozrieť, ako ho nastaviť:", + "Recipients": "Príjemcovia", + "Access Token": "Prístupový token", + "Channel access token": "Prístupový token kanála", + "Channel access token (Long-lived)": "Prístupový token kanála (dlhodobý)", + "Basic Settings": "Základné nastavenia", + "User ID": "ID používateľa", + "Your User ID": "Vaše ID používateľa", + "Messaging API": "Rozhranie API pre zasielanie správ", + "aboutIconURL": "Do položky „ URL adresa ikony“ môžete zadať odkaz na obrázok, ktorý nahradí predvolený profilový obrázok. Nepoužije sa, ak je nastavená ikona Emoji.", + "Icon URL": "URL adresa ikony", + "aboutMattermostChannelName": "Predvolený kanál, do ktorého sa webhook odosiela, môžete prepísať zadaním názvu kanála do poľa „Názov kanála“. Toto je potrebné povoliť v nastaveniach aplikácie Mattermost Webhook. Napríklad: #iný-kanál", + "confirmDeleteTagMsg": "Naozaj chcete odstrániť tento štítok? Sledovania spojené s týmto štítkom nebudú odstránené.", + "enableGRPCTls": "Povolenie odosielania požiadaviek gRPC s pripojením TLS", + "acceptedStatusCodesDescription": "Vyberte stavové kódy, ktoré sa považujú za úspešnú odpoveď.", + "deleteMonitorMsg": "Určite chcete odstrániť toto sledovanie?", + "deleteMaintenanceMsg": "Určite chcete odstrániť túto údržbu?", + "deleteNotificationMsg": "Určite chcete odstrániť toto oznámenie pre všetky sledovania?", + "resolverserverDescription": "Cloudflare je predvolený server. Môžete zadať zoznam IP adries alebo názvov hostiteľov oddelených čiarkami.", + "rrtypeDescription": "Vyberte typ RR, ktorý chcete sledovať", + "Select message type": "Vyberte typ správy", + "Send to channel": "Odoslať do kanála", + "Create new forum post": "Vytvorenie nového príspevku vo fóre", + "postToExistingThread": "Príspevok do existujúceho vlákna / príspevku na fóre", + "Number": "Číslo", + "pauseMonitorMsg": "Určite chcete pozastaviť sledovanie?", + "RadiusSecretDescription": "Zdieľaný tajný kľúč medzi klientom a serverom", + "Clone": "Duplikovať", + "Refresh Interval": "Interval obnovovania", + "Refresh Interval Description": "Stavová stránka vykoná úplnú obnovu stránky každých {0} sekúnd", + "Show Powered By": "Zobraziť Poháňané", + "signedInDisp": "Prihlásený ako {0}", + "Domain Names": "Domény", + "RadiusCallingStationId": "Id volajúcej stanice", + "signedInDispDisabled": "Autorizácia zakázaná.", + "RadiusCalledStationIdDescription": "Identifikátor volaného zariadenia", + "Check how to config it for WebSocket": "Pozrite si, ako nakonfigurovať pre WebSocket", + "Notification Service": "Služba oznamovania", + "Event type:": "Typ udalosti:", + "pauseMaintenanceMsg": "Určite chcete pozastaviť údržbu?", + "maintenanceStatus-under-maintenance": "V údržbe", + "maintenanceStatus-scheduled": "Plánované", + "Server Timezone": "Časové pásmo servera", + "statusPageMaintenanceEndDate": "Koniec", + "Disable": "Zakázať", + "Display Timezone": "Časové pásmo pre zobrazenie", + "enableNSCD": "Povoliť NSCD (Name Service Cache Daemon) na ukladanie všetkých požiadaviek DNS do vyrovnávacej pamäte", + "Edit Maintenance": "Upraviť údržbu", + "Date and Time": "Dátum a čas", + "dataRetentionTimeError": "Doba uchovávania musí byť 0 alebo viac", + "infiniteRetention": "Nastavte na 0 pre nekonečné uchovávanie.", + "Query": "Dotaz", + "backupOutdatedWarning": "Vyradené: Keďže bolo pridaných veľa funkcií a táto funkcia zálohovania je trochu neudržiavaná, nemôže generovať alebo obnoviť kompletnú zálohu.", + "maintenanceStatus-ended": "Ukončené", + "Chat ID": "ID chatu", + "telegramMessageThreadID": "(voliteľné) ID vlákna správy", + "telegramMessageThreadIDDescription": "Nepovinný jedinečný identifikátor cieľového vlákna správy (témy) fóra; len pre nadskupiny fóra", + "Docker Host": "Docker hostiteľ", + "Docker Hosts": "Docker hostitelia", + "Domain": "Doména", + "Workstation": "Pracovisko", + "Packet Size": "Veľkosť paketu", + "Bot Token": "Token bota", + "wayToGetTelegramToken": "Token môžete získať z {0}.", + "Effective Date Range": "Rozsah dátumu účinnosti (voliteľné)", + "Schedule Maintenance": "Naplánovať údržbu", + "DateTime Range": "Rozsah dátumu a času", + "loadingError": "Nie je možné načítať údaje, skúste to prosím neskôr.", + "plugin": "Modul | Moduly | Modulov", + "Coming Soon": "Už čoskoro", + "dayOfWeek": "Deň v týždni", + "dayOfMonth": "Deň v mesiaci", + "emailTemplateMsg": "správa oznámenia", + "emailTemplateLimitedToUpDownNotification": "k dispozícii len pre pulzy DOSTUPNÉ/NEDOSTUPNÉ, inak je nulový", + "To Email": "Na e-mail", + "smtpCC": "CC", + "smtpBCC": "BCC", + "default: notify all devices": "predvolené: upozorniť všetky zariadenia", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Zoznam oznamovacích služieb nájdete v aplikácii Home Assistant v časti „Nástroje pre vývojárov > Služby“, kde vyhľadajte položku „oznámenie“ a nájdite názov svojho zariadenia/telefónu.", + "tailscalePingWarning": "Ak chcete používať sledovanie Tailscale Ping, musíte nainštalovať aplikáciu Uptime Kuma bez nástroja Docker a tiež nainštalovať klienta Tailscale na server.", + "Enable": "Povoliť", + "Enable DNS Cache": "(Zastarané) Povoliť vyrovnávaciu pamäť DNS pre HTTP(s) sledovania", + "Post": "Publikovať", + "Not running": "Nefunguje", + "RadiusSecret": "Tajný kľúč Radius", + "cronExpression": "Výraz Cron", + "Maintenance Time Window of a Day": "Časový interval údržby cez deň", + "Hello @everyone is...": "Dobrý deň, {'@'}všetci sú…", + "clearHeartbeatsMsg": "Naozaj chcete odstrániť všetky záznamy pulzy z tohto monitora? Týmto krokom sa natrvalo odstránia všetky údaje o pulzoch a túto akciu nie je možné vrátiť späť.", + "Trust Proxy": "Dôverovať proxy", + "RadiusCalledStationId": "ID volaného zariadenia", + "Connection String": "Reťazec pripojenia", + "socket": "Socket", + "Line Developers Console": "Konzola Line Developers", + "confirmClearStatisticsMsg": "Naozaj chcete odstrániť VŠETKY štatistiky?", + "-year": "-rok", + "and": "a", + "shrinkDatabaseDescriptionSqlite": "Spusti prečistenie databázy {vacuum} pre SQLite. {auto_vacuum} je už povolené, ale to nedefragmentuje databázu ani neprebalí jednotlivé stránky databázy tak, ako to robí príkaz {vacuum}.", + "lineDevConsoleTo": "Konzola Line Developers - {0}", + "clearEventsMsg": "Naozaj chcete odstrániť všetky udalosti pre tento monitor? Týmto sa natrvalo odstránia súvisiace údaje o správach a túto akciu nie je možné vrátiť späť.", + "now": "teraz", + "time ago": "pred {0}", + "Json Query Expression": "Výraz dotazu JSON", + "ignoredTLSError": "Chyby TLS/SSL boli ignorované", + "Add a new expiry notification day": "Pridať nový deň uplynutia platnosti", + "chromeExecutable": "Spustiteľný súbor Chrome/Chromium", + "templateHostnameOrURL": "názov hostiteľa alebo URL", + "templateStatus": "stav", + "templateServiceName": "názov služby", + "telegramTemplateFormatDescription": "Telegram umožňuje používať rôzne značkovacie jazyky pre správy, podrobnosti nájdete v článku Telegram {0}.", + "recurringIntervalMessage": "Spustiť raz denne | Spustiť raz každé {0} dni | Spustiť raz každých {0} dní", + "affectedMonitorsDescription": "Vyberte sledovania, ktoré sú ovplyvnené aktuálnou údržbou", + "keywordDescription": "Vyhľadajte kľúčové slovo v obyčajnej HTML alebo JSON odpovedi. Vyhľadávanie rozlišuje veľké a malé písmená.", + "backupDescription": "Všetky sledovania a oznámenia si môžete zálohovať do súboru JSON.", + "backupDescription2": "Poznámka: história a údaje o udalostiach nie sú zahrnuté.", + "pushoversounds climb": "Stúpanie (dlhé)", + "pushoversounds persistent": "Trvalé (dlhé)", + "pushoversounds echo": "Pushover Echo (dlhé)", + "pushoversounds updown": "Hore dole (dlhé)", + "pushoversounds vibrate": "Iba vibrovanie", + "pushoversounds none": "Žiadne (tiché)", + "pushoversounds alien": "Mimozemský alarm (dlhý)", + "Apprise URL": "URL adresa oznámenia", + "Example:": "Príklad: {0}", + "Read more:": "Čítajte viac: {0}", + "LunaSea Device ID": "LunaSea ID zariadenia", + "Proxy Protocol": "Proxy protokol", + "Setup Proxy": "Nastavenie proxy servera", + "telegramUseTemplate": "Použiť vlastnú šablónu správy", + "telegramUseTemplateDescription": "Ak je povolená, správa sa odošle pomocou vlastnej šablóny.", + "Use HTML for custom E-mail body": "Použitie HTML v tele e-mailu", + "twoFAVerifyLabel": "Zadajte svoj token na overenie 2FA:", + "confirmEnableTwoFAMsg": "Naozaj chcete povoliť 2FA?", + "confirmDisableTwoFAMsg": "Naozaj chcete vypnúť 2FA?", + "affectedStatusPages": "Zobraziť túto správu o údržbe na vybraných stavových stránkach", + "notificationDescription": "Aby oznámenia fungovali, musia byť priradené k sledovaniu.", + "invertKeywordDescription": "Hľadajte, či kľúčové slovo chýba, skôr ako to, že je prítomné.", + "backupDescription3": "Citlivé údaje, ako napríklad tokeny oznámení, sú zahrnuté v exportovanom súbore; export si, prosím, bezpečne uložte.", + "pushyAPIKey": "Tajný kľúč API", + "wayToGetKookBotToken": "Vytvorte si aplikáciu a získajte token bota na adrese {0}", + "wayToGetKookGuildID": "V nastavení Kook zapnite „Režim vývojára“ a kliknite pravým tlačidlom myši na guild, čím získate jeho ID", + "pushoverDesc1": "Priorita núdze (2) má predvolený 30-sekundový časový limit medzi opakovanými pokusmi a jeho platnosť vyprší po 1 hodine.", + "octopushLegacyHint": "Používate staršiu verziu Octopush (2011 – 2020) alebo novú verziu?", + "octopushSMSSender": "Meno odosielateľa SMS: 3 – 11 alfanumerických znakov a medzera (a – z, A – Z, 0 – 9)", + "You can divide numbers with": "Čísla môžete oddeliť pomocou", + "goAlertInfo": "GoAlert je aplikácia s otvoreným zdrojovým kódom na plánovanie pohotovostí, automatizované eskalácie a oznámenia (ako sú SMS alebo hlasové hovory). Automaticky oslovte správnu osobu správnym spôsobom a v správnom čase! {0}", + "For safety, must use secret key": "Z bezpečnostných dôvodov je potrebné použiť tajný kľúč", + "promosmsTypeFlash": "SMS FLASH – Správa sa automaticky zobrazí na zariadení príjemcu. Obmedzené len na poľských príjemcov.", + "promosmsPhoneNumber": "Telefónne číslo (pre poľského príjemcu, môžete vynechať smerové čísla)", + "matrixDesc1": "Interné ID miestnosti nájdete v rozšírenej sekcii nastavení miestnosti vo vašom klientovi Matrix. Malo by vyzerať takto: !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Dôrazne sa odporúča vytvoriť nového používateľa a nepoužívať vlastný prístupový token používateľa Matrix, pretože vám to umožní plný prístup k vášmu účtu a všetkým miestnostiam, ku ktorým ste sa pripojili. Namiesto toho vytvorte nového používateľa a pozvite ho iba do miestnosti, v ktorej chcete dostávať oznámenia. Prístupový token získate spustením príkazu {0}", + "aboutKumaURL": "Ak necháte pole URL adresy Uptime Kuma prázdne, predvolene sa zobrazí stránka projektu GitHub.", + "PhoneNumbers": "Telefónne čísla", + "telegramServerUrl": "(Voliteľné) URL adresa servera", + "telegramServerUrlDescription": "Ak chcete zrušiť obmedzenia rozhrania API botov Telegramu alebo získať prístup v blokovaných oblastiach (Čína, Irán atď.), kliknite na {0}. Predvolené: {1}", + "Device": "Zariadenie", + "aboutSlackUsername": "Zmení zobrazované meno odosielateľa správy. Ak chcete niekoho spomenúť, uveďte ho v poli obecný názov.", + "smtpDkimSettings": "Nastavenia DKIM", + "defaultFriendlyName": "Nové sledovanie", + "promosmsPassword": "Heslo API", + "WebHookUrl": "URL webhooku", + "Add Tags": "Pridať štítky", + "tagAlreadyStaged": "Tento štítok (názov a hodnota) je už pripravený pre túto dávku.", + "tagAlreadyOnMonitor": "Tento štítok (názov a hodnota) je už sledovaný alebo čaká na pridanie.", + "tagNameExists": "Systémový štítok s týmto názvom už existuje. Vyberte ho zo zoznamu alebo použite iný názov.", + "octopushAPIKey": "„Kľúč API“ z prihlasovacích údajov HTTP API v ovládacom paneli", + "octopushLogin": "„Prihlásenie“ z prihlasovacích údajov HTTP API v ovládacom paneli", + "pushoversounds pushover": "Pushover (predvolené)", + "pushoversounds bike": "Bicykel", + "pushoversounds bugle": "Trúbka", + "pushoversounds cashregister": "Pokladňa", + "pushoversounds classical": "Klasická", + "pushoversounds cosmic": "Kozmický", + "pushoversounds falling": "Padajúce", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Prichádzajúce", + "pushoversounds intermission": "Prestávka", + "pushoversounds magic": "Kúzlo", + "pushoversounds mechanical": "Mechanické", + "pushoversounds pianobar": "Klávesa klavíra", + "pushoversounds siren": "Siréna", + "pushoversounds spacealarm": "Vesmírny alarm", + "pushoversounds tugboat": "Remorkér", + "pushyToken": "Token zariadenia", + "apprise": "Apprise (Podpora viac ako 50 oznamovacích služieb)", + "GoogleChat": "Google Chat (iba Google Workspace)", + "Guild ID": "Guild ID", + "User Key": "Používateľský kľúč", + "Message Title": "Názov správy", + "Notification Sound": "Zvuk oznámenia", + "More info on:": "Viac informácií o: {0}", + "pushoverMessageTtl": "Správa TTL (sekundy)", + "pushoverDesc2": "Ak chcete posielať oznámenia na rôzne zariadenia, vyplňte pole Zariadenia.", + "SMS Type": "Typ SMS", + "octopushTypeLowCost": "Nízka cena (Pomalé - niekedy blokované operátorom)", + "octopushTypePremium": "Prémiový (Rýchle – odporúča sa pre upozornenia)", + "checkPrice": "Skontrolujte ceny {0}:", + "apiCredentials": "Prihlasovacie údaje API", + "Check octopush prices": "Skontrolujte ceny Octopush {0}.", + "octopushPhoneNumber": "Telefónne číslo (medzinárodný formát, napr.: +42112345678)", + "Status:": "Stav: {0}", + "Strategy": "Stratégia", + "Free Mobile User Identifier": "Bezplatný identifikátor mobilného používateľa", + "Free Mobile API Key": "Bezplatný kľúč mobilného API", + "Enable TLS": "Povoliť TLS", + "Proto Service Name": "Proto názov služby", + "Proto Method": "Proto metóda", + "Proto Content": "Proto obsah", + "Economy": "Úsporná", + "Lowcost": "Nízkonákladový", + "high": "vysoká", + "SendKey": "Kľúč k odoslaniu", + "SMSManager API Docs": "Dokumentácia k SMSManager API", + "Gateway Type": "Typ brány", + "Base URL": "Základná URL adresa", + "goAlertIntegrationKeyInfo": "Získajte generický integračný kľúč API pre službu v tomto formáte „aaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee“, zvyčajne hodnota parametra tokenu skopírovanej URL adresy.", + "AccessKeyId": "ID prístupového kľúča", + "SecretAccessKey": "Tajný prístupový kľúč", + "TemplateCode": "Kód šablóny", + "SignName": "Názov podpisu", + "Sms template must contain parameters: ": "Šablóna SMS musí obsahovať parametre: ", + "Bark API Version": "Verzia Bark API", + "Bark Endpoint": "Koncový bod Bark", + "Bark Group": "Bark skupina", + "Bark Sound": "Bark zvuk", + "SecretKey": "Tajný Kľúč", + "Mentioning": "Zmienky", + "Don't mention people": "Nespomínajte ľudí", + "Mention group": "Spomeňte {skupinu}", + "Device Token": "Token zariadenia", + "Platform": "Platforma", + "Huawei": "Huawei", + "High": "Vysoký", + "Retry": "Opakovať", + "Topic": "Téma", + "WeCom Bot Key": "Kľúč WeCom Bota", + "Proxy Server": "Proxy server", + "Proxy server has authentication": "Proxy server má overenie", + "promosmsTypeEco": "SMS ECO – lacné, ale pomalé a často preťažené. Obmedzené len na poľských príjemcov.", + "promosmsTypeFull": "SMS PLNÁ – Prémiová úroveň SMS, môžete použiť meno odosielateľa (najprv si musíte meno zaregistrovať). Spoľahlivé pre upozornenia.", + "promosmsTypeSpeed": "RÝCHLA SMS - Najvyššia priorita v systéme. Veľmi rýchle a spoľahlivé, ale drahé (približne dvojnásobok ceny PLNEJ SMS).", + "promosmsSMSSender": "Meno odosielateľa SMS: Predregistrované meno alebo jedno z predvolených: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "promosmsAllowLongSMS": "Povoliť dlhé SMS", + "Feishu WebHookUrl": "URL adresa webhooku Feishu", + "matrixHomeserverURL": "URL adresa domáceho servera (s http(s://) a voliteľným portom)", + "Internal Room Id": "Vnútorné ID miestnosti", + "Channel Name": "Názov kanála", + "Notify Channel": "Upozorniť kanál", + "aboutNotifyChannel": "Oznamovací kanál spustí oznámenie na počítači alebo mobilnom zariadení pre všetkých členov kanála, bez ohľadu na to, či je ich dostupnosť nastavená na aktívnu alebo neprítomnú.", + "Uptime Kuma URL": "URL adresa Uptime Kuma", + "setup a new monitor group": "nastaviť novú skupinu pre sledovanie", + "openModalTo": "otvoriť modálne okno do {0}", + "Add a domain": "Pridať doménu", + "Remove domain": "Odstrániť doménu „{0}“", + "Icon Emoji": "Ikona emotikonu", + "signalImportant": "DÔLEŽITÉ: V zozname príjemcov nemôžete kombinovať skupiny a čísla!", + "aboutWebhooks": "Viac informácií o webhookoch na: {0}", + "aboutChannelName": "Ak chcete obísť kanál Webhook, zadajte názov kanála do poľa Názov kanála {0}. Napr.: #iny-kanal", + "smtpDkimDesc": "Použitie nájdete v dokumentácii Nodemailer DKIM {0}.", + "documentation": "dokumentácia", + "smtpDkimDomain": "Názov domény", + "smtpDkimKeySelector": "Výber kľúča", + "smtpDkimPrivateKey": "Súkromný kľúč", + "smtpDkimHashAlgo": "Hašovací algoritmus (voliteľné)", + "importHandleDescription": "Vyberte možnosť „Preskočiť existujúce“, ak chcete preskočiť všetky sledovania alebo oznámenia s rovnakým názvom. Možnosť „Prepísať“ vymaže všetky existujúce sledovania a oznámenia.", + "confirmImportMsg": "Naozaj chcete importovať zálohu? Skontrolujte, či ste vybrali správnu možnosť importu.", + "atLeastOneMonitor": "Vyberte aspoň jedno príslušné sledovanie", + "passwordNotMatchMsg": "Opakované heslo sa nezhoduje.", + "jsonQueryDescription": "Analyzujte a extrahujte konkrétne údaje z JSON odpovede servera pomocou JSON dotazu alebo použite „$“ pre surovú odpoveď, ak sa neočakáva JSON. Výsledok sa potom porovná s očakávanou hodnotou ako reťazec. Dokumentáciu nájdete v {0} na experimentovanie s dotazmi {1}.", + "promosmsLogin": "Prihlasovacie meno API", + "tokenValidSettingsMsg": "Token je platný! Teraz môžete uložiť nastavenia 2FA.", + "endpoint": "koncový bod (endpoint)", + "Clear All Events": "Vyčistiť všetky udalosti", + "clearAllEventsMsg": "Naozaj chcete odstrániť všetky udalosti zo všetkých monitorov? Týmto krokom sa natrvalo odstránia príslušné údaje o správach a túto akciu nie je možné vrátiť späť.", + "Events cleared successfully": "Udalosti boli úspešne vyčistené.", + "Path": "Cesta", + "mqttWebsocketPathInvalid": "Použite prosím platný formát cesty WebSocket", + "mqttHostnameTip": "Použite prosím tento formát {hostnameFormat}", + "What is a Remote Browser?": "Čo je vzdialený prehliadač?", + "API URL": "API URL adresa", + "Integration Key": "Integračný kľúč", + "Integration URL": "Integračná URL adresa", + "pushDeerServerDescription": "Nechajte prázdne, ak chcete použiť oficiálny server", + "wayToGetClickSendSMSToken": "Používateľské meno API a kľúč API môžete získať z {here}.", + "Google Analytics ID": "ID služby Google Analytics", + "Learn More": "Zistiť viac", + "API Keys": "Kľúče API", + "Don't expire": "Nevyprší platnosť", + "Continue": "Pokračovať", + "Add Another": "Pridať ďalší", + "Add API Key": "Pridať kľúč API", + "No API Keys": "Žiadne API kľúče", + "apiKey-active": "Aktívny", + "Expires": "Vyprší", + "disableAPIKeyMsg": "Naozaj chcete deaktivovať tento kľúč API?", + "pagertreeIntegrationUrl": "Integračná URL adresa", + "pagertreeDoNothing": "Nerobiť nič", + "lunaseaUserID": "ID používateľa", + "lunaseaDeviceID": "ID zariadenia", + "ntfyAuthenticationMethod": "Metóda overovania", + "twilioAccountSID": "SID účtu", + "twilioApiKey": "Kľúč API (voliteľné)", + "authUserInactiveOrDeleted": "Používateľ je neaktívny alebo bol odstránený.", + "authInvalidToken": "Neplatný token.", + "2faEnabled": "2FA povolená.", + "2faDisabled": "2FA je vypnutá.", + "successDeleted": "Úspešne odstránené.", + "successEdited": "Úspešne upravené.", + "successAuthChangePassword": "Heslo bolo úspešne aktualizované.", + "successDisabled": "Úspešne deaktivované.", + "successEnabled": "Úspešne aktivované.", + "tagNotFound": "Štítok nebol nájdený.", + "foundChromiumVersion": "Nájdené Chromium/Chrome. Verzia: {0}", + "Remote Browsers": "Vzdialené prehliadače", + "Remote Browser": "Vzdialený prehliadač", + "Saved.": "Uložené.", + "Edit Tag": "Upraviť štítok", + "Server Address": "Adresa servera", + "Expiry date": "Dátum platnosti", + "apiKeyAddedMsg": "Váš kľúč API bol pridaný. Prosím, zapíšte si ho, pretože už nebude znovu zobrazený.", + "apiKey-expired": "Vypršaná platnosť", + "apiKey-inactive": "Neaktívny", + "deleteAPIKeyMsg": "Naozaj chcete odstrániť tento kľúč API?", + "ntfyPriorityHelptextAllEvents": "Všetky udalosti sú odosielané s maximálnou prioritou", + "ntfyUsernameAndPassword": "Používateľské meno a heslo", + "Authorization Identity": "Autorizačná identita", + "2faAlreadyEnabled": "2FA je už povolená.", + "successBackupRestored": "Záloha bola úspešne obnovená.", + "Remote Browser not found!": "Vzdialený prehliadač nebol nájdený!", + "Expiry": "Platnosť", + "Close": "Zavrieť", + "Add a Remote Browser": "Pridať vzdialený prehliadač", + "Group": "Skupina", + "serwersmsAPIPassword": "Heslo API", + "serwersmsPhoneNumber": "Telefónne číslo", + "smseagleTo": "Telefónne číslo(a)", + "smseagleRecipientType": "Typ príjemcu", + "smspartnerPhoneNumber": "Telefónne číslo(a)", + "onebotUserOrGroupId": "ID skupiny/používateľa", + "Key Added": "Kľúč pridaný", + "pagertreeUrgency": "Naliehavosť", + "pagertreeSilent": "Tiché", + "pagertreeLow": "Nízke", + "pagertreeMedium": "Stredné", + "pagertreeHigh": "Vysoké", + "twilioFromNumber": "Z čísla", + "twilioToNumber": "Na číslo", + "Show Clickable Link": "Zobraziť klikateľný odkaz", + "Mechanism": "Mechanizmus", + "Pick a SASL Mechanism...": "Vyberte mechanizmus SASL…", + "remoteBrowsersDescription": "Vzdialené prehliadače sú alternatívou k lokálnemu spúšťaniu Chromium. Nastavte si službu ako browserless.io alebo sa pripojte k vlastnej", + "Secret AccessKey": "Tajný prístupový kľúč", + "AccessKey Id": "ID prístupového kľúča", + "noGroupMonitorMsg": "Nie je dostupné. Najskôr vytvorte skupinu sledovania.", + "successPaused": "Úspešne pozastavené.", + "whapiRecipient": "Telefónne číslo / ID kontaktu / ID skupiny", + "Destination": "Cieľ", + "Telephone number": "Telefónne číslo", + "Phone numbers": "Telefónne čísla", + "Leave blank to use a shared sender number.": "Nechajte prázdne, ak chcete použiť zdieľané číslo odosielateľa.", + "Octopush API Version": "Verzia Octopush API", + "cellsyntDestination": "Telefónne číslo príjemcu v medzinárodnom formáte s predvoľbou 00, za ktorou nasleduje kód krajiny, napr. 00447920110000 pre číslo vo Veľkej Británii 07920 110 000 (celkovo maximálne 17 číslic). Maximálne 25 000 príjemcov oddelených čiarkami na jednu požiadavku HTTP.", + "pagertreeCritical": "Kritické", + "successResumed": "Pokračovanie úspešne obnovené.", + "threemaRecipientTypeIdentityFormat": "8 znakov", + "threemaRecipientTypePhone": "Telefónne číslo", + "threemaRecipientTypeEmail": "E-mailová adresa", + "No monitors found": "Nenašli sa žiadne sledovania.", + "Recipient Number": "Číslo príjemcu", + "self-hosted container": "samostatne prevádzkovaný kontajner", + "remoteBrowserToggle": "Chromium sa štandardne spúšťa v kontajneri Uptime Kuma. Pomocou tohto prepínača môžete použiť vzdialený prehliadač.", + "useRemoteBrowser": "Použiť vzdialený prehliadač", + "deleteRemoteBrowserMessage": "Naozaj chcete odstrániť tento vzdialený prehliadač zo všetkých sledovaní?", + "Command": "Príkaz", + "mongodbCommandDescription": "Spustiť príkaz MongoDB voči databáze. Informácie o dostupných príkazoch nájdete v {documentation}", + "apiKeySevenIO": "Kľúč API SevenIO", + "Allow Long SMS": "Povoliť dlhé SMS správy", + "cellsyntSplitLongMessages": "Rozdeliť dlhé správy na maximálne 6 častí. 153 x 6 = 918 znakov.", + "max 15 digits": "max. 15 číslic", + "max 11 alphanumeric characters": "max. 11 alfanumerických znakov", + "receiverInfoSevenIO": "Ak sa číslo príjemcu nenachádza v Nemecku, musíte pred číslo pridať kód krajiny (napr. pre kód krajiny 1 z USA použite 117612121212 namiesto 017612121212)", + "Recipient Type": "Typ príjemcu", + "The phone number of the recipient in E.164 format.": "Telefónne číslo príjemcu vo formáte E.164.", + "Please enter a valid OID.": "Zadajte prosím platný identifikátor OID.", + "threemaRecipient": "Príjemca", + "threemaRecipientType": "Typ príjemcu", + "wayToGetSevenIOApiKey": "Navštívte prehľadový panel na app.seven.io > developer > api key > zelené tlačidlo pridať", + "threemaRecipientTypeIdentity": "Threema ID", + "Custom Monitor Type": "Vlastný typ sledovania", + "Monitor Setting": "Nastavenie sledovania {0}", + "Show Clickable Link Description": "Ak je táto možnosť označená, každý, kto má prístup k tejto stránke so stavom, môže mať prístup k sledovacej URL adrese.", + "monitorToastMessagesDescription": "Oznámenia na sledovanie zmiznú po uplynutí nastaveného času v sekundách. Nastavením hodnoty -1 sa časový limit deaktivuje. Nastavením hodnoty 0 sa deaktivujú oznámenia.", + "rabbitmqNodesRequired": "Prosím, nastavte uzly pre toto sledovanie.", + "customUrlDescription": "Bude použitá ako klikateľná URL adresa namiesto adresy sledovania.", + "gamedigGuessPortDescription": "Port, ktorý protokol Valve Server Query Protocol použije, sa môže líšiť od portu klienta. Skúste to, ak sa sledovanie nedokáže pripojiť k vášmu serveru.", + "snmpOIDHelptext": "Zadajte OID pre senzor alebo stav, ktorý chcete sledovať. Ak si nie ste istí OID, použite nástroje na správu siete, ako sú prehliadače MIB alebo softvér SNMP.", + "Monitor Group": "Skupina sledovania", + "monitorToastMessagesLabel": "Oznámenia sledovania", + "rabbitmqHelpText": "Ak chcete používať sledovanie, musíte v nastaveniach RabbitMQ aktivovať doplnok Management Plugin. Ďalšie informácie nájdete v {rabitmq_documentation}.", + "Could not clear events": "Nepodarilo sa vyčistiť {failed}/{total} udalostí", + "do nothing": "nerobiť nič", + "smseagleDuration": "Dĺžka (v sekundách)", + "smseagleApiType": "Verzia API", + "smseagleApiv1": "APIv1 (pre existujúce projekty a spätnú kompatibilitu)", + "smseagleApiv2": "APIv2 (odporúčané pre nové integrácie)", + "smseagleDocs": "Skontrolujte dokumentáciu alebo dostupnosť APIv2: {0}", + "smseagleComma": "Viacnásobné hodnoty musia byť oddelené čiarkou", + "smspartnerApiurl": "Svoj kľúč API nájdete vo svojom prehľade na {0}", + "mqttWebSocketPath": "Cesta MQTT WebSocket", + "mqttWebsocketPathExplanation": "Cesta WebSocket pre MQTT cez WebSocket pripojenia (napr. /mqtt)", + "smspartnerSenderName": "Meno odosielateľa SMS", + "smspartnerPhoneNumberHelptext": "Číslo musí byť v medzinárodnom formáte {0}, {1}. Viac čísel musí byť oddelených znakom {2}", + "smspartnerSenderNameInfo": "Musí mať 3..=11 bežných znakov", + "onebotSafetyTips": "Z bezpečnostných dôvodov je nutné nastaviť prístupový token", + "PushDeer Key": "Kľúč PushDeer", + "SpugPush Template Code": "Kód šablóny", + "From Name/Number": "Od Meno/číslo", + "onebotGroupMessage": "Skupina", + "wayToGetWahaSession": "Z tejto relácie WAHA odosiela oznámenia na Chat ID. Nájdete ho v paneli WAHA.", + "PushDeer Server": "Server PushDeer", + "wahaSession": "Relácia", + "serwersmsAPIUser": "Používateľské meno API (vrátane predpony webapi_)", + "serwersmsSenderName": "Názov odosielateľa SMS (zaregistrovaný prostredníctvom zákazníckeho portálu)", + "smseagleToken": "Prístupový token API", + "smseagleUrl": "Vaša URL adresa zariadenia SMSEagle", + "Body Encoding": "Kódovanie tela stránky", + "lunaseaTarget": "Cieľ", + "twilioAuthToken": "Autentizačný token / Tajný kľúč API", + "showCertificateExpiry": "Zobraziť platnosť certifikátu", + "authIncorrectCreds": "Nesprávne používateľské meno alebo heslo.", + "successAdded": "Úspešne pridané.", + "Group Name": "Názov skupiny", + "Authentication Method": "Metóda overovania", + "Client ID": "ID klienta", + "Client Secret": "Tajný kľúč klienta", + "Group ID": "ID skupiny", + "Go back to home page.": "Návrat na domovskú stránku.", + "No tags found.": "Nenašli sa žiadne štítky.", + "Lost connection to the socket server.": "Stratilo sa pripojenie k serveru socket.", + "Cannot connect to the socket server.": "Nie je možné sa pripojiť k serveru socket.", + "SIGNL4": "SIGNL4", + "OneChatUserIdOrGroupId": "ID používateľa alebo ID skupiny OneChat", + "Plain Text": "Obyčajný text", + "Message Template": "Šablóna správy", + "Add Another Tag": "Pridať ďalší štítok", + "pause": "Pozastaviť", + "New Group": "Nová skupina", + "Add Remote Browser": "Pridať vzdialený prehliadač", + "smseagleRecipient": "Príjemca(-ovia) (viac príjemcov je potrebné oddeliť čiarkou)", + "smseagleEncoding": "Odoslať ako Unicode (predvolené nastavenie = GSM-7)", + "Authorization Header": "Autorizačná hlavička", + "Optional: Space separated list of scopes": "Voliteľné: Zoznam rozsahov oddelených medzerami", + "Template Format": "Formát šablóny", + "Template plain text instead of using cards": "Šablóna obyčajného textu namiesto toho, aby sa použili karty", + "issueWithGoogleChatOnAndroidHelptext": "To tiež umožňuje obísť chyby v upstreamoch, ako napríklad {issuetackerURL}", + "noOrBadCertificate": "Žiadny/neplatný certifikát", + "cacheBusterParam": "Pridať parameter {0}", + "Message format": "Formát správy", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL adresa", + "signl4Docs": "Viac informácií o konfigurácii SIGNL4 a získaní URL adresy webhook SIGNL4 nájdete v {0}.", + "conditionAdd": "Pridať podmienku", + "conditionDelete": "Odstrániť podmienku", + "conditionAddGroup": "Pridať skupinu", + "conditionDeleteGroup": "Odstrániť skupinu", + "Conditions": "Podmienky", + "alertaApiKey": "Kľúč API", + "wayToGetWahaApiUrl": "Vaša URL adresa inštancie WAHA.", + "apiKeysDisabledMsg": "Kľúče API sú deaktivované, pretože je deaktivované overovanie.", + "threemaApiAuthenticationSecret": "Tajný kľúč Gateway-ID", + "SendGrid API Key": "Kľúč API SendGrid", + "smsplanetApiToken": "Token pre SMSPlanet API", + "YZJ Robot Token": "Token YZJ Robot", + "auto acknowledged": "automaticky potvrdené", + "auto resolve": "automaticky vyriešiť", + "alertaApiEndpoint": "Koncový bod API", + "alertaEnvironment": "Prostredie", + "alertaAlertState": "Stav upozornenia", + "smseagleGroupV2": "ID skupiny telefónneho zoznamu", + "smseagleContact": "Meno (mená) kontaktu v telefónnom zozname", + "smseagleGroup": "Názov (názvy) skupiny telefónneho zoznamu", + "smseagleContactV2": "ID kontaktov v telefónnom zozname", + "smseaglePriority": "Priorita správy (0-9, najvyššia priorita = 9)", + "smseagleMsgType": "Typ správy", + "onebotPrivateMessage": "Súkromná", + "ntfyPriorityHelptextAllExceptDown": "Všetky udalosti sú odosielané s touto prioritou, okrem udalostí {0}, ktoré majú prioritu {1}", + "Enable Kafka SSL": "Povoliť Kafka SSL", + "Kafka SASL Options": "Možnosti Kafka SASL", + "Session Token": "Token relácie", + "bitrix24SupportUserID": "Zadajte svoje používateľské ID v Bitrix24. ID nájdete v odkaze v profile používateľa.", + "GrafanaOncallUrl": "URL adresa Grafana Oncall", + "receiverSevenIO": "Prijímajúce číslo", + "wayToGetWhapiUrlAndToken": "URL adresu API a token získate tak, že prejdete do požadovaného kanála z {0}", + "wayToGetHeiiOnCallDetails": "Ako získať ID spúšťača a kľúče API je vysvetlené v {documentation}", + "documentationOf": "{0} Dokumentácia", + "To Phone Number": "Na telefónne číslo", + "gtxMessagingToHint": "Medzinárodný formát s počiatočným znakom „+“ ({e164}, {e212} alebo {e214})", + "Alphanumeric (recommended)": "Alfanumerický (odporúčaný)", + "gtxMessagingApiKeyHint": "Svoj kľúč API nájdete na: Moje smerovacie účty > Zobraziť informácie o účte > Prihlasovacie údaje API > REST API (v2.x)", + "Token Onesender": "Token Onesender", + "threemaSenderIdentityFormat": "8 znakov, zvyčajne začína znakom *", + "wayToGetOnesenderUrlandToken": "URL adresu a token získate na webovej stránke Onesender. Viac informácií {0}", + "OAuth Token URL": "URL adresa tokenu OAuth", + "Private Number": "Súkromné číslo", + "privateOnesenderDesc": "Uistite sa, že telefónne číslo je platné. Ak chcete odoslať správu na súkromné telefónne číslo, napr.: 628123456789", + "conditionValuePlaceholder": "Hodnota", + "equals": "sa rovná", + "not equals": "sa nerovná", + "pingCountDescription": "Počet paketov, ktoré sa majú odoslať pred zastavením", + "wayToGetWahaApiKey": "Kľúč API je hodnota premennej prostredia WHATSAPP_API_KEY, ktorú ste použili na spustenie WAHA.", + "OneChatAccessToken": "Prístupový token OneChat", + "Custom URL": "Vlastná URL adresa", + "OneChatBotId": "ID OneChat Bot", + "wahaChatId": "ID Chat (telefónne číslo / ID kontaktu / ID skupiny)", + "smsplanetApiDocs": "Podrobné informácie o získaní API tokenov nájdete v {the_smsplanet_documentation}.", + "Sender name": "Meno odosielateľa", + "smsplanetNeedToApproveName": "Musí byť schválené v klientskom paneli", + "Clear Form": "Vyčistiť formulár", + "onebotHttpAddress": "HTTP adresa OneBot", + "gtxMessagingFromHint": "Na mobilných telefónoch sa vašim príjemcom zobrazuje TPOA ako odosielateľ správy. Povolených je až 11 alfanumerických znakov, krátky kód, miestny dlhý kód alebo medzinárodné čísla ({e164}, {e212} alebo {e214})", + "Separate multiple email addresses with commas": "Viac e-mailových adries oddeľte čiarkami", + "Disable URL in Notification": "Zakázať URL v oznámení", + "wayToGetPagerTreeIntegrationURL": "Po vytvorení integrácie Uptime Kuma v PagerTree skopírujte koncový bod. Pozrite si všetky podrobnosti {0}", + "ntfyPriorityHelptextPriorityHigherThanDown": "Bežná priorita by mala byť vyššia ako priorita {0}. Priorita {1} je vyššia ako priorita {0} priorita {2}", + "ntfyPriorityDown": "Priorita pre udalosti stavu Nedostupné", + "wayToWriteWhapiRecipient": "Telefónne číslo s medzinárodnou predvoľbou, ale bez znamienka plus na začiatku ({0}), ID kontaktu ({1}) alebo ID skupiny ({2}).", + "callMeBotGet": "Tu môžete vygenerovať koncový bod pre {0}, {1} a {2}. Majte na pamäti, že môže dôjsť k obmedzeniu rýchlosti. Obmedzenia rýchlosti vyzerá byť: {3}", + "Scifi": "Sci-fi", + "Guitar": "Gitara", + "Elevator": "Výťah", + "Clear": "Jasný", + "smseagleMsgSms": "SMS správa (predvolené)", + "pagertreeResolve": "Automaticky vyriešiť", + "FlashDuty Push URL": "Push URL adresa", + "FlashDuty Push URL Placeholder": "Kopírovať zo stránky integrácie upozornení", + "nostrRecipients": "Verejné kľúče príjemcov (npub)", + "nostrRecipientsHelp": "formát npub, jeden na riadok", + "nostrSender": "Súkromný kľúč odosielateľa (nsec)", + "OID (Object Identifier)": "OID (identifikátor objektu)", + "Condition": "Podmienka", + "SNMP Version": "Verzia SNMP", + "wayToGetThreemaGateway": "Môžete sa zaregistrovať na Threema Gateway {0}.", + "greater than or equal to": "väčšia alebo rovná", + "Notification Channel": "Kanál oznámení", + "Sound": "Zvuk", + "wayToGetPagerDutyKey": "Toto môžete získať tak, že prejdete do časti Služby -> Adresár služieb -> (Vyberte službu) -> Integrácie -> Pridať integráciu. Tu môžete vyhľadať „Events API V2“. Viac informácií {0}", + "Auto resolve or acknowledged": "Automaticky vyriešiť alebo potvrdiť", + "onebotMessageType": "Typ správy OneBot", + "threemaRecipientTypePhoneFormat": "E.164, bez predvoľby +", + "threemaSenderIdentity": "Gateway-ID", + "threemaBasicModeInfo": "Poznámka: Táto integrácia používa Threema Gateway v základnom režime (šifrovanie na serveri). Ďalšie podrobnosti nájdete {0}.", + "cellsyntOriginatortypeAlphanumeric": "Alfanumerický reťazec (max. 11 alfanumerických znakov). Príjemcovia nemôžu na správu odpovedať.", + "cellsyntOriginatortypeNumeric": "Číselná hodnota (max. 15 číslic) s telefónnym číslom v medzinárodnom formáte bez predvoľby 00 (napríklad číslo vo Veľkej Británii 07920 110 000 by malo byť nastavené ako 447920110000). Príjemcovia môžu na správu odpovedať.", + "contains": "obsahuje", + "not contains": "neobsahuje", + "less than or equal to": "menšia alebo rovná", + "Alphanumerical string and hyphens only": "Iba alfanumerický reťazec a spojovníky", + "Custom sound to override default notification sound": "Vlastný zvuk, ktorý prepíše predvolený zvuk oznámenia", + "Pop": "Pop", + "Money": "Peniaze", + "Flute": "Flauta", + "Doorbell": "Zvonček", + "Bubble": "Bublina", + "Reveal": "Odhalenie", + "Harp": "Harfa", + "starts with": "začína s", + "not starts with": "nezačína s", + "ends with": "končí s", + "not ends with": "nekončí s", + "less than": "menšia ako", + "greater than": "väčšia ako", + "pingCountLabel": "Maximálny počet paketov", + "pingNumericLabel": "Číselný výstup", + "RabbitMQ Password": "Heslo RabbitMQ", + "RabbitMQ Username": "Používateľské meno RabbitMQ", + "pingPerRequestTimeoutDescription": "Toto je maximálna čakacia doba (v sekundách) predtým, ako sa jedna odozva paketu považuje za stratenú", + "wayToWriteWahaChatId": "Telefónne číslo s medzinárodnou predvoľbou, ale bez znamienka plus na začiatku ({0}), ID kontaktu ({1}) alebo ID skupiny ({2}). Oznámenia sa odosielajú na toto Chat ID z relácie WAHA.", + "rabbitmqNodesInvalid": "Prosím, použite úplnú adresu URL adresu (začínajúcu na „http“) pre uzly RabbitMQ.", + "Fail": "Zlyhanie", + "Correct": "Správne", + "Happy Eyeballs algorithm": "Algoritmus Happy Eyeballs", + "Badge Generator": "Generátor odznakov {0}", + "Badge Type": "Typ odznaku", + "Badge Duration (in hours)": "Trvanie odznaku (v hodinách)", + "Badge Label": "Označenie odznaku", + "Badge Prefix": "Predpona hodnoty odznaku", + "Badge Suffix": "Prípona hodnoty odznaku", + "Open Badge Generator": "Otvoriť generátor odznakov", + "toastErrorTimeout": "Časový limit pre oznámenia o chybách", + "toastSuccessTimeout": "Časový limit pre oznámenia o úspechu", + "Enter the list of brokers": "Zadajte zoznam sprostredkovateľov", + "Kafka Brokers": "Kafka sprostredkovatelia", + "Press Enter to add broker": "Stlačte kláves Enter pre pridanie sprostredkovateľa", + "Kafka Topic Name": "Názov témy Kafka", + "Kafka Producer Message": "Správa producenta Kafka", + "Enable Kafka Producer Auto Topic Creation": "Povoliť automatické vytváranie tém Kafka producenta", + "wayToGetFlashDutyKey": "Ak chcete integrovať Uptime Kuma s Flashduty: Prejdite do sekcie Kanály > Vyberte kanál > Integrácie > Pridať novú integráciu, vyberte Uptime Kuma a skopírujte Push URL adresu.", + "Bitrix24 Webhook URL": "URL adresa Bitrix24 webhook", + "Browser Screenshot": "Snímka obrazovky prehliadača", + "Time Sensitive (iOS Only)": "Časovo citlivé (len iOS)", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Oznámenia, ktoré sú časovo citlivé, budú doručené okamžite, aj keď je zariadenie v režime Nerušiť.", + "From": "Od", + "Can be found on:": "Nájdete na: {0}", + "RabbitMQ Nodes": "Správcovské uzly RabbitMQ", + "the smsplanet documentation": "dokumentácia smsplanet", + "Legacy Octopush-DM": "Staršia verzia Octopush-DM", + "ntfy Topic": "ntfy Téma", + "Server URL should not contain the nfty topic": "URL servera by nemala obsahovať nfty tému", + "snmpCommunityStringHelptext": "Tento reťazec funguje ako heslo na overenie a kontrolu prístupu k zariadeniam s podporou SNMP. Prispôsobte ho konfigurácii vášho zariadenia SNMP.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Ak chcete prijímať odpovede, zadajte buď ID odosielateľa textovej správy alebo telefónne číslo vo formáte E.164.", + "Badge URL": "URL adresa odznaku", + "Badge Label Color": "Farba označenia odznaku", + "Badge Color": "Farba odznaku", + "Badge Label Prefix": "Predpona označenia odznaku", + "Badge Preview": "Náhľad odznaku", + "Badge Label Suffix": "Prípona označenia odznaku", + "Badge Down Color": "Farba odznaku Nedostupné", + "Badge Up Color": "Farba odznaku Dostupné", + "Badge Pending Color": "Farba odznaku Čakajúce", + "Badge Maintenance Color": "Farba odznaku Údržba", + "Badge Style": "Štýl odznaku", + "Badge value (For Testing only.)": "Hodnota odznaku (len na testovanie)", + "Font Twemoji by Twitter licensed under": "Písmo Twemoji od Twitteru na základe licencie", + "YZJ Webhook URL": "YZJ Webhook URL adresa", + "OAuth2: Client Credentials": "OAuth2: Poverenia klienta", + "groupOnesenderDesc": "Uistite sa, že GroupID je platné. Ak chcete odoslať správu do skupiny, napr.: 628123456789-342345", + "pingNumericDescription": "Ak je táto možnosť označená, namiesto symbolických názvov hostiteľov sa budú zobrazovať IP adresy", + "smtpHelpText": "„SMTPS“ testuje, či SMTP/TLS funguje; „“Ignorovať TLS“ sa pripája cez nezabezpečený text; „STARTTLS“ sa prippojí, vydá príkaz STARTTLS a overí certifikát servera. Žiadna z týchto možností neodosiela e-mail.", + "rabbitmqNodesDescription": "Zadajte URL adresu pre uzly na správu RabbitMQ vrátane protokolu a portu. Príklad: {0}", + "ipFamilyDescriptionAutoSelect": "Používa {happyEyeballs} na určenie IP verzie.", + "Ip Family": "Verzia IP", + "Manual": "Manuálne", + "OAuth Audience": "OAuth publikum", + "alertaRecoverState": "Obnoviť stav", + "smseagleMsgRing": "Zavolať (zvoniť)", + "smseagleMsgTts": "Hovor s prevodom textu na reč", + "smseagleMsgTtsAdvanced": "Rozšírený hovor s prevodom textu na reč", + "smseagleTtsModel": "ID modelu pre prevod textu na reč", + "Request Body": "Telo požiadavky", + "Send rich messages": "Odosielať rozšírené správy (rich messages)", + "wayToGetBitrix24Webhook": "Webhook môžete vytvoriť podľa pokynov na {0}", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Z telefónneho čísla / Pôvodná adresa prenosovej cesty (TPOA)", + "Originator type": "Typ odosielateľa", + "Community String": "Komunitný reťazec", + "Host Onesender": "Hostiteľ Onesender", + "Form Data Body": "Telo údajov formulára", + "OAuth Scope": "OAuth rozsah", + "Optional: The audience to request the JWT for": "Voliteľné: Publikum, pre ktoré sa má požadovať JWT", + "pingGlobalTimeoutLabel": "Globálny časový limit", + "pingGlobalTimeoutDescription": "Celkový čas v sekundách, po ktorom ping prestane bez ohľadu na odoslané pakety", + "pingPerRequestTimeoutLabel": "Časový limit pre jednotlivý ping", + "Staged Tags for Batch Add": "Fázované štítky pre hromadné pridávanie", + "senderSevenIO": "Odosielanie čísla alebo mena", + "pingIntervalAdjustedInfo": "Interval upravený na základe počtu paketov, globálneho časového limitu a časového limitu pre jednotlivý ping", + "FlashDuty Severity": "Závažnosť", + "nostrRelays": "Nostr relé", + "nostrRelaysHelp": "Jedna URL adresa relé na riadok", + "cacheBusterParamDescription": "Náhodne vygenerovaný parameter na obídenie cache.", + "gamedigGuessPort": "Gamedig: Odhadnúť port", + "smtpDkimheaderFieldNames": "Hlavičkové kľúče na podpísanie (voliteľné)", + "smtpDkimskipFields": "Hlavičkové kľúče, ktoré sa nemajú podpísať (voliteľné)", + "Badge Warn Color": "Farba odznaku upozornenia", + "Badge Warn Days": "Odznak dní upozornení", + "Badge Down Days": "Odznak dní mimo prevádzky", + "Originator": "Odosielateľ", + "cellsyntOriginator": "Viditeľné na mobilnom telefóne príjemcu ako odosielateľ správy. Povolené hodnoty a funkcia závisia od parametra \"originatortype\".", + "Arcade": "Arkáda", + "record": "záznam", + "wayToWriteEvolutionRecipient": "Telefónne číslo s medzinárodnou predvoľbou, ale bez znamienka plus na začiatku ({0}), ID kontaktu ({1}) alebo ID skupiny ({2}).", + "wayToGetEvolutionUrlAndToken": "API URL adresa a token sa nachádzajú vo vašom požadovanom kanáli z {0}", + "evolutionRecipient": "Telefónne číslo / ID kontaktu / ID skupiny", + "evolutionInstanceName": "Názov inštancie", + "brevoApiHelp": "Tu vytvorte kľúč API: {0}", + "brevoFromEmail": "Od (email)", + "brevoFromName": "Od (meno)", + "brevoToEmail": "Komu (email)", + "brevoCcEmail": "Kópia (email)", + "brevoBccEmail": "Skrytá kópia (email)", + "brevoSeparateMultipleEmails": "Viac e-mailových adries oddeľte čiarkami", + "brevoSubject": "Predmet", + "brevoLeaveBlankForDefaultSubject": "nechajte prázdne pre predvolený predmet", + "Bot secret": "Tajný kľúč bota", + "Nextcloud host": "Nextcloud server", + "Send DOWN silently": "Odoslať NEDOSTUPNÉ potichu", + "brevoApiKey": "Brevo API kľúč", + "brevoLeaveBlankForDefaultName": "nechajte prázdne pre predvolené meno", + "Conversation token": "Token konverzácie", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Inštalácia bota Nextcloud Talk vyžaduje prístup správcu k serveru.", + "Send UP silently": "Odoslať DOSTUPNÉ potichu", + "auto-select": "Automaticky vybrať", + "Enter a list of userId": "Zadajte zoznam používateľských identifikátorov", + "wayToGetBaleChatID": "Svoje chat ID získate tak, že pošlete správu botovi a prejdete na túto URL adresu, kde sa zobrazí chat_id:", + "wayToGetBaleToken": "Token môžete získať z {0}.", + "supportBaleChatID": "Podpora pre Priamy chat / Skupinu / ID chatu kanála", + "Invalid userId": "Neplatné userId [{userId}]", + "Mention User List": "Zoznam používateľských ID Mention", + "Dingtalk Mobile List": "Zoznam mobilov", + "Dingtalk User List": "Zoznam používateľských ID", + "Enter a list of mobile": "Zadajte zoznam mobilov", + "Invalid mobile": "Neplatný mobil [{mobile}]", + "Mention Mobile List": "Zoznam mobilov Mention", + "Maximum Retries": "Maximálny počet opakovaní", + "Number of retry attempts if webhook fails": "Počet pokusov o opakovanie (každých 60–180 sekúnd), ak webhook zlyhá.", + "webhookGetMethodDesc": "GET odosiela údaje ako parametre dopytu a neumožňuje konfiguráciu tela. Je užitočné na spúšťanie Push monitorov Uptime Kuma.", + "descriptionHelpText": "Zobrazené na internom paneli. Markdown je povolený a pred zobrazením je očistený (zachováva medzery a odsadenie).", + "HTTP Method": "Metóda HTTP", + "webhookPostMethodDesc": "POST je vhodný pre väčšinu moderných HTTP serverov.", + "ariaPauseMaintenance": "Pozastaviť tento plán údržby", + "ariaResumeMaintenance": "Pokračovať v tomto pláne údržby", + "ariaCloneMaintenance": "Vytvoriť kópiu tohto plánu údržby", + "ariaEditMaintenance": "Upraviť tento plán údržby", + "ariaDeleteMaintenance": "Odstrániť tento plán údržby", + "Clone Maintenance": "Naklonovať údržbu", + "deleteChildrenMonitors": "Vymazať aj priame podriadené monitory a ich podriadené monitory, ak nejaké existujú | Vymazať aj všetky {count} priame podriadené monitory a ich podriadené monitory, ak nejaké existujú | Vymazať aj všetkých {count} priamych podriadených monitorov a ich podriadených monitorov, ak nejaké existujú", + "deleteGroupMsg": "Určite chcete túto skupinu odstrániť?", + "Template ID": "ID šablóny", + "wayToGetClickSMSIRTemplateID": "Vaša šablóna musí obsahovať pole {uptkumaalert}. Novú šablónu môžete vytvoriť {tu}.", + "Recipient Numbers": "Čísla príjemcu", + "twilioMessagingServiceSID": "SID služby zasielania správ (voliteľné)", + "twilloMessagingServiceSIDHelptext": "Ak používate {twillo_messaging_service_help_link} na spravovanie odosielateľov a funkcií, zadajte sem svoje SID služby zasielania správ", + "twilioApiKeyHelptext": "Kľúč API je voliteľný, ale odporúčaný. Môžete poskytnúť buď SID účtu a AuthToken zo stránky TwilioConsole alebo SID účtu a pár API kľúča a API tajného kľúča", + "showOnlyLastHeartbeat": "Zobraziť len posledný pulz", + "wsSubprotocolDescription": "Zadajte zoznam podprotokolov oddelených čiarkami. Ďalšie informácie o podprotokoloch nájdete v {documentation}", + "WebSocket Application Messaging Protocol": "WAMP (WebSocket Application Messaging Protocol)", + "Extensible Messaging and Presence Protocol": "WebSocket Transport pre protokol XMPP (Extensible Messaging and Presence Protocol)", + "Webpush Helptext": "Webové push správy fungujú iba s pripojeniami SSL (HTTPS). V prípade zariadení iOS je potrebné najskôr pridať webovú stránku na domovskú obrazovku.", + "Notifications Enabled": "Oznámenia povolené", + "Allow Notifications": "Povoliť oznámenia", + "Browser not supported": "Prehliadač nie je podporovaný", + "Unable to get permission to notify": "Nie je možné získať povolenie na zasielanie oznámení (žiadosť bola zamietnutá alebo ignorovaná).", + "ignoreSecWebsocketAcceptHeaderDescription": "Umožňuje serveru neodpovedať hlavičkou Sec-WebSocket-Accept, ak je aktualizácia websocket úspešná.", + "Ignore Sec-WebSocket-Accept header": "Ignorovať hlavičku {0}", + "Session Initiation Protocol": "WebSocket Transport pre SIP (Session Initiation Protocol)", + "Network API for Notification Channel": "OMA RESTful sieťové API pre oznamovací kanál", + "Web Process Control Protocol": "Web Process Control Protocol (WPCP)", + "Advanced Message Queuing Protocol": "Advanced Message Queuing Protocol (AMQP) 1.0+", + "jsflow": "jsFlow pubsub/queue protokol", + "Reverse Web Process Control": "Reverse Web Process Control Protocol (RWPCP)", + "Smart Home IP": "SHIP - Smart Home IP", + "Miele Cloud Connect Protocol": "Miele Cloud Connect Protokol", + "Push Channel Protocol": "Push Channel Protokol", + "Message Session Relay Protocol": "WebSocket Transport pre MSRP (Message Session Relay Protocol)", + "Binary Floor Control Protocol": "WebSocket Transport pre BFCP (Binary Floor Control Protocol)", + "Softvelum Low Delay Protocol": "Softvelum Low Delay Protokol", + "OPC UA Connection Protocol": "OPC UA Connection Protocol", + "OPC UA JSON Encoding": "Kódovanie OPC UA JSON", + "Swindon Web Server Protocol": "Protokol webového servera Swindon (kódovanie JSON)", + "Broadband Forum User Services Platform": "USP (Broadband Forum User Services Platform)", + "Constrained Application Protocol": "Constrained Application Protocol (CoAP)", + "Softvelum WebSocket signaling protocol": "Softvelum WebSocket Signaling Protocol", + "Cobra Real Time Messaging Protocol": "Protokol Cobra Real Time Messaging", + "Declarative Resource Protocol": "Declarative Resource Protocol", + "BACnet Secure Connect Hub Connection": "BACnet Secure Connect Hub Connection", + "BACnet Secure Connect Direct Connection": "BACnet Secure Connect Direct Connection", + "WebSocket Transport for JMAP": "WebSocket Transport pre JMAP (JSON Meta Application Protocol)", + "ITU-T T.140 Real-Time Text": "ITU-T T.140 Real-Time Text", + "Done.best IoT Protocol": "Done.best IoT Protokol", + "Collection Update": "Collection Update Websocket Subprotocol", + "Text IRC Protocol": "Protokol textového IRC", + "Binary IRC Protocol": "Protokol binárneho IRC", + "Penguin Statistics Live Protocol v3": "Protokol Penguin Statistics Live v3 (kódovanie Protobuf)", + "certHostnameMismatch": "Certifikát hostiteľa sa nezhoduje s URL adresou monitora.", + "minimumIntervalWarning": "Intervaly kratšie ako 20 sekúnd môžu mať za následok zníženie výkonu.", + "lowIntervalWarning": "Naozaj chcete nastaviť interval na hodnotu nižšiu ako 20 sekúnd? Výkon sa môže znížiť, najmä ak je veľký počet monitorov.", + "settingsDomainExpiry": "Vypršanie platnosti domény", + "labelDomainExpiry": "Platnosť dom.", + "labelDomainNameExpiryNotification": "Oznámenie o vypršaní platnosti domény", + "domainExpiryDescription": "Spustiť upozornenie, keď platnosť domén vyprší za:", + "Enter the list of nodes": "Zadajte zoznam uzlov spravovania RabbitMQ", + "Subprotocol": "Subprotokol", + "Duration (Minutes)": "Dĺžka (v minútach)", + "SMTP Security": "Zabezpečenie SMTP", + "Ignore STARTTLS": "Ignorovať STARTTLS", + "Use STARTTLS": "Použiť STARTTLS", + "Press Enter to add node": "Stlačte kláves Enter pre pridanie uzla", + "Select All": "Vybrať všetko", + "Deselect All": "Zrušiť výber všetkých", + "resendApiKey": "Znovu odoslať API kľúč", + "resendApiHelp": "API kľúč si vytvorte tu {0}", + "resendFromName": "Meno odosielateľa", + "resendFromEmail": "Email odosielateľa", + "resendLeaveBlankForDefaultName": "nechajte prázdne pre predvolené meno", + "resendToEmail": "Email komu", + "resendSubject": "Predmet", + "wsCodeDescription": "Ďalšie informácie o stavových kódoch nájdete v {rfc6455}", + "Subprotocol(s)": "Podprotokol(y)", + "systemService": "Systémová Služba", + "systemServiceName": "Názov Služby", + "systemServiceDescription": "Kontroluje či je systémová služba {service_name} aktívna", + "systemServiceDescriptionLinux": "Kontroluje či je Linoxová systemd služba {service_name} aktívna", + "systemServiceDescriptionWindows": "Kontroluje či je Windows Service Manager {service_name} spustený", + "systemServiceCommandHint": "Použitý príkaz: {command}", + "systemServiceExpectedOutput": "Očakávaný Výstup: \"{0}\"", + "Analytics Type": "Typ štatistiky", + "Analytics ID": "ID štatistiky", + "Analytics Script URL": "URL skriptu pre štatistiky", + "Plausible": "Plausible", + "Matomo": "Matomo", + "Umami": "Umami", + "Google": "Google", + "imageResetConfirmation": "Obrázok bol obnovený na predvolený", + "maxPing": "Maximálny ping", + "minPing": "Minimálny ping", + "avgPing": "Priemerný ping", + "sipsakPingWarning": "Aby ste mohli používať monitor SIP Options Ping, musíte nainštalovať Uptime Kuma bez Dockeru a tiež nainštalovať klienta Sipsak na svoj server.", + "invalidHostnameOrIP": "Neplatný názov hostiteľa alebo IP adresa. Názov hostiteľa musí byť platné FQDN (presne stanovené meno domény). Nie je možné použiť zástupný znak. Môže obsahovať podčiarkovník alebo končiť bodkou.", + "hostnameCannotBeIP": "DNS názov hostiteľa nemôže byť IP adresa. Chceli ste použiť pole resolver?", + "invalidDNSHostname": "Neplatný názov hostiteľa. Názov hostiteľa musí byť platné FQDN. Môže obsahovať zástupný znak, podčiarkovník alebo končiť bodkou.", + "wildcardOnlyForDNS": "Názvy hostiteľov so zástupnými symbolmi sú podporované iba pre DNS monitory.", + "invalidURL": "Neplatná URL adresa", + "RSS Title": "Názov RSS", + "Leave blank to use status page title": "Nechajte prázdne, ak chcete použiť názov stavovej stránky", + "Basic checkbox toggle button group": "Skupina základných prepínačov s začiarkavacími políčkami", + "message": "správa", + "json_value": "JSON hodnota", + "Badge Link Generator Helptext": "Odkazy na odznaky sú k dispozícii pre všetky monitory priradené k verejným stavovým stránkam. Ďalšie informácie nájdete v {documentation}.", + "Badge Link Generator": "Generátor odkazu na odznak služby {0}", + "Open Badge Link Generator": "Otvoriť generátor odkazov na odznaky", + "SMSManager": "SMSManager", + "smscTranslit": "smscTranslit", + "Message Format": "Formát správy", + "promosms": "promosms", + "Region": "Región", + "PushDeer Server URL": "URL adresa servera PushDeer", + "playground": "ihrisko", + "GRPC Options": "Možnosti GRPC", + "Metadata": "Metadáta", + "Service Name": "Názov služby", + "Show this Maintenance Message on which Status Pages": "Zobraziť túto správu o údržbe na týchto stavových stránkach", + "resendLeaveBlankForDefaultSubject": "Nechajte prázdne pre predvolený predmet", + "mtls-auth-server-cert-label": "Certifikát", + "mtls-auth-server-key-label": "Kľúč", + "mtls-auth-server-key-placeholder": "Telo kľúča", + "mtls-auth-server-cert-placeholder": "Telo certifikátu", + "mtls-auth-server-ca-label": "CA", + "mtls-auth-server-ca-placeholder": "Server CA", + "Uptime Kuma": "Uptime Kuma", + "Sort options": "Možnosti triedenia", + "Clear current filters": "Vyčistiť aktuálne filtre", + "Splunk Rest URL": "URL adresa Splunk Rest", + "Severity": "Závažnosť", + "Sort by status": "Zoradiť podľa stavu", + "Sort by name": "Zoradiť podľa názvu", + "Sort by uptime": "Zoradiť podľa dostupnosti", + "Sort by certificate expiry": "Zoradiť podľa platnosti certifikátu", + "screenshot of the website": "Snímka obrazovky webovej stránky", + "Basic radio toggle button group": "Skupina základných výberových prepínačov", + "Check Type": "Typ kontroly", + "year": "rok | rokov", + "To Number": "Na číslo", + "GrafanaOncallURL": "Grafana Oncall URL adresa", + "Never": "Nikdy", + "System Service": "Systémová služba", + "SSL/TLS": "SSL/TLS", + "End": "Koniec", + "Endpoint": "Koncový bod", + "Details": "Podrobnosti", + "serwersmsRecipientType": "Typ príjemcu", + "serwersmsRecipientTypePhone": "Telefónne číslo", + "serwersmsRecipientTypeGroup": "Skupina", + "serwersmsGroupId": "ID skupiny", + "serwersmsGroupIdHelptext": "ID alebo ID skupiny v paneli zákazníka. Tieto identifikátory je možné stiahnuť pomocou akčných skupín / indexu alebo ich skopírovaním pri úprave skupiny v paneli zákazníka.", + "TLS Alert Spec": "RFC 8446", + "TLS Alerts": "Upozornenia TLS", + "Expected TLS Alert": "Očakávané upozornenie TLS", + "None (Successful Connection)": "Žiadne (úspešné pripojenie)", + "notificationEmail": "Email", + "notificationHomeAutomation": "Domáca automatizácia", + "notificationOther": "Iné integrácie", + "notificationUniversal": "Všeobecné", + "notificationChatPlatforms": "Konverzačné platformy", + "notificationPushServices": "Služby Push oznámení", + "notificationSmsServices": "SMS služby", + "notificationIncidentManagement": "Riadenie incidentov", + "passwordTooWeak": "Heslo je príliš slabé. Malo by obsahovať písmená a číslice. A musí mať minimálne 6 znakov.", + "expectedTlsAlertDescription": "Vyberte upozornenie TLS, ktoré by mal server vrátiť. Pomocou {code} overte, či koncové body mTLS odmietajú pripojenia bez certifikátov klienta. Podrobnosti nájdete na {link}.", + "domain_expiry_unsupported_monitor_type": "Monitorovanie vypršania platnosti domény nie je podporované pre tento typ monitorovania", + "domain_expiry_unsupported_missing_target": "Pre tento monitor nie je nakonfigurovaná žiadna platná doména ani názov hostiteľa", + "domain_expiry_public_suffix_too_short": "„.{publicSuffix}“ je príliš krátke pre doménu najvyššej úrovne", + "domain_expiry_unsupported_public_suffix": "Doména „{domain}“ nemá platnú verejnú príponu", + "domain_expiry_unsupported_is_ip": "„{hostname}“ je IP adresa. Monitorovanie vypršania platnosti domény vyžaduje názov domény", + "domain_expiry_unsupported_invalid_domain": "Nastavená hodnota „{hostname}“ nie je platným názvom domény", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Monitorovanie vypršania platnosti domény nie je k dispozícii pre „.{publicSuffix}“, pretože IANA neeviduje žiadnu službu RDAP", + "ntfyCall": "Telefonický hovor", + "ntfyCallHelptext": "Pri spustení upozornenia vykonať telefonický hovor. Nastavte na „áno“, aby sa použilo vaše prvé overené číslo, alebo zadajte konkrétne telefónne číslo (napr. +12223334444). Vyžaduje ntfy Pro a overené telefónne číslo.", + "Resolver Server(s)": "Resolver Server(y)", + "HeadersInvalidFormatBecause": "Hlavičky požiadavky nie sú platným JSON, pretože {error}", + "BodyInvalidFormatBecause": "Telo požiadavky nie je platný JSON, pretože {error}", + "checkPriceAt": "Skontrolujte ceny služby {service} na {url}", + "You can divide numbers with commas or semicolons": "Čísla môžete oddeliť pomocou {comma} alebo {semicolon}", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "Halo PSA Webhook URL adresa", + "password": "Heslo", + "halopsa_password_desc": "Heslo pre overenie pomocou webhook Halo PSA", + "Setup Instructions": "Pokyny na nastavenie", + "halopsa_setup_step1": "Vytvorte integračný Runbook v HaloPSA (Konfigurácia → Integrácie → Integračné Runbooks)", + "halopsa_setup_step2": "Nakonfigurujte akcie runbook na spracovanie upozornení (napr. Vytvoriť tiket)", + "halopsa_setup_step3": "Skopírujte URL adresu webhook a vložte ju do vyššie uvedeného textového pola", + "steamApiKeyDescriptionAt": "Na monitorovanie herného servera Steam potrebujete kľúč Steam Web-API. Svoj API kľúč si môžete zaregistrovať na adrese {url}", + "username": "Používateľské meno", + "halopsa_username_desc": "Používateľské meno pre overenie pomocou webhook Halo PSA", + "halopsa_setup_step4": "Vyberte základné overenie a vytvorte používateľské meno a heslo. A napíšte alebo vložte toto používateľské meno a heslo do vyššie uvedených polí", + "halopsa_webhook_url_desc": "Zadajte webhook URL adresu z vašej Halo PSA Integration Runbook (Konfigurácia > Integrácie > Vlastné integrácie > Integračné Runbooks). Pri vytváraní webhook vyberte možnosť „Možno spustiť iba z Halo a z verejného koncového bodu“.", + "noMonitorsSelectedWarning": "Vytvárate údržbu bez akýchkoľvek príslušných monitorov. Naozaj chcete pokračovať?", + "noMonitorsOrStatusPagesSelectedError": "Nie je možné vytvoriť údržbu bez relevantných monitorov alebo stavových stránok", + "OptionalParameters": "Voliteľné parametre", + "aliyun-template-requirements-and-parameters": "Šablóna SMS správy Aliyun musí obsahovať parametre: {parameters}", + "aliyun-template-optional-parameters": "Voliteľné parametre: {parameters}", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Vzhľadom na obmedzenia operátorov môžete povoliť voliteľné premenné s rizikom nedoručenia", + "enableSSL": "Povoliť SSL/TLS", + "mariadbUseSSLHelptext": "Povoľte pre používanie šifrovaného pripojenia k vašej databáze. Vyžadované pre väčšinu cloudových databáz.", + "mariadbCaCertificateLabel": "Certifikát CA", + "mariadbCaCertificateHelptext": "Vložte certifikát CA vo formáte PEM, ktorý sa používa s certifikátmi podpísanými vlastným podpisom. Nechajte prázdne, ak vaša databáza používa certifikát podpísaný verejnou certifikačnou autoritou.", + "selectAllMonitorsAria": "Vybrať všetky monitory", + "deselectAllMonitorsAria": "Zrušiť výber všetkých monitorov", + "Actions": "Akcie", + "selectedMonitorCountMsg": "vybraný: {n} | vybrané: {n} | vybrané: {n}", + "selectMonitorMsg": "Vyberte monitory, na ktorých chcete vykonať akcie", + "deleteMonitorsMsg": "Naozaj chcete odstrániť vybrané monitory?", + "pausedMonitorsMsg": "Pozastavený {n} monitor | Pozastavené {n} monitory | Pozastavených {n} monitorov", + "deletedMonitorsMsg": "Odstránený {n} monitor | Odstránené {n} monitory | Odstránených {n} monitorov", + "resumedMonitorsMsg": "Obnovený {n} monitor | Obnovené {n} monitory | Obnovených {n} monitorov", + "noMonitorsPausedMsg": "Žiadne monitory neboli pozastavené (žiadne neboli aktívne)", + "noMonitorsResumedMsg": "Žiadne monitory neboli spustené (žiadne neboli neaktívne)", + "bulkDeleteErrorMsg": "Nepodarilo sa odstrániť {n} monitor | Nepodarilo sa odstrániť {n} monitory | Nepodarilo sa odstrániť {n} monitorov", + "saveResponseForNotifications": "Uložiť úspešnú odpoveď HTTP pre oznámenia", + "saveErrorResponseForNotifications": "Uložiť odpoveď HTTP chyby pre oznámenia", + "saveResponseDescription": "Ukladá odpoveď HTTP a sprístupňuje ju šablónam oznámení ako {templateVariable}", + "responseMaxLength": "Maximálna dĺžka odpovede (v bajtoch)", + "responseMaxLengthDescription": "Maximálna veľkosť údajov odpovede, ktoré sa majú uložiť. Nastavte na 0 pre neobmedzenú veľkosť. Väčšie odpovede budú skrátené. Predvolené nastavenie: 1024 (1 KB)", + "Only retry if status code check fails": "Opätovať len v prípade, ak kontrola stavového kódu zlyhá", + "retryOnlyOnStatusCodeFailureDescription": "Ak je táto funkcia povolená, opakované pokusy sa budú vykonávať len v prípade, že kontrola kódu stavu HTTP zlyhá (napr. server je nefunkčný). Ak kontrola kódu stavu prebehne úspešne, ale dotaz JSON zlyhá, monitor bude okamžite označený ako nefunkčný bez opakovaných pokusov.", + "days": "{n} deň | {n} dni | {n} dní", + "hours": "{n} hodina | {n} hodiny | {n} hodín", + "minutes": "{n} minúta | {n} minúty | {n} minút", + "years": "{n} rok | {n} roky | {n} rokov", + "minuteShort": "{n} min | {n} min | {n} min", + "Sets end time based on start time": "Nastaví čas ukončenia na základe času začatia", + "Please set start time first": "Najskôr nastavte čas začiatku", + "unknownDays": "Neznámych dní", + "Monitors": "{n} monitor | {n} monitory | {n} monitorov", + "versionIs": "Verzia: {version}", + "logoutCurrentUser": "Odhlásiť používateľa {username}", + "createdAt": "Vytvorené: {date}", + "lastUpdatedAt": "Posledná aktualizácia: {date}", + "lastUpdatedAtFromNow": "Posledná aktualizácia: {date} ({fromNow})", + "Certificate Chain:": "Reťaz certifikátov:", + "dateCreatedAtFromNow": "Dátum vytvorenia: {date} ({fromNow})", + "Examples:": "Príklady: {0}", + "frontendVersionIs": "Verzia frontendu: {version}", + "cronScheduleDescription": "Harmonogram: {description}", + "octopushEndpoint": "octopush (koncový bod: {url})", + "legacyOctopushEndpoint": "Staršia verzia Octopush-DM (koncový bod: {url})", + "Suppress Notifications": "Stlmiť oznámenia", + "discordSuppressNotificationsHelptext": "Ak je táto funkcia povolená, správy budú odosielané do kanála, ale nebudú spúšťať push alebo desktopové notifikácie pre príjemcov.", + "domain_expiry_unsupported_is_icann": "Doména „{domain}“ nie je kandidátom na monitorovanie vypršania platnosti domény, pretože jej verejná prípona „.{publicSuffix}“ nie je spravovaná organizáciou ICANN", + "snmpV3Username": "Používateľské meno SNMPv3", + "WeCom Mentioned Mobile List Description": "Zadajte telefónne čísla, ktoré chcete označiť. Viac čísel oddeľte čiarkami. Použite {'@'}all, aby ste označili všetkých.", + "WeCom Mentioned Mobile List": "WeCom zoznam zmienených", + "Collapse All Groups": "Zbaliť všetky skupiny", + "Expand All Groups": "Rozbaliť všetky skupiny", + "mariadbSocketPathDetectedHelptext": "Pripájanie k databáze podľa špecifikácie prostredníctvom premennej prostredia {0}.", + "milliseconds": "{n} milisekunda | {n} milisekundy | {n} milisekúnd", + "screenshotDelayDescription": "Voliteľne počkať toľkoto milisekúnd pred vytvorením snímky obrazovky. Maximum: {maxValueMs}ms (0,5 × interval).", + "Screenshot Delay": "Oneskorenie snímky obrazovky (počká {milliseconds})", + "screenshotDelayWarning": "Vyššie hodnoty udržujú prehliadač otvorený dlhšie, čo môže zvýšiť využitie pamäte pri mnohých súčasných monitoroch.", + "Disable STARTTLS": "Vypnúť STARTTLS", + "disableSTARTTLSDescription": "Túto možnosť aktivujte pre servery SMTP, ktoré nepodporujú STARTTLS. E-maily sa tak budú odosielať cez nezašifrované pripojenie.", + "No incidents recorded": "Neboli zaznamenané žiadne incidenty", + "Load More": "Načítať viac", + "Loading...": "Načítava sa...", + "Pin this incident": "Pripnúť tento incident", + "Incident description": "Popis incidentu", + "Incident not found or access denied": "Incident nebol nájdený alebo prístup bol zamietnutý", + "Past Incidents": "Minulé incidenty", + "Incident title": "Názov incidentu", + "Pinned incidents are shown prominently on the status page": "Pripojené incidenty sú zreteľne zobrazené na stavovej stránke", + "Edit Incident": "Upraviť incident", + "Resolve": "Vyriešiť", + "Resolved": "Vyriešené", + "deleteIncidentMsg": "Naozaj chcete odstrániť tento incident?", + "slug is not found": "Slug nebol nájdený", + "Please input content": "Zadajte prosím obsah", + "Please input title": "Zadajte prosím názov", + "Deploy a Google Apps Script as a web app and paste the URL here": "Implementujte skript Google Apps ako webovú aplikáciu a vložte sem URL adresu", + "Quick Setup Guide": "Rýchly sprievodca nastavením", + "Google Apps Script Webhook URL": "Webhook URL adresa skriptu Google Apps", + "Open your Google Spreadsheet": "Otvorte váš Google Spreadsheet", + "Paste the script code (see below)": "Vložte kód skriptu (pozri nižšie)", + "Click Deploy → New deployment → Web app": "Kliknite na Deploy → New deployment → Web app", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Nastavte „Vykonať ako: Ja“ a „Kto má prístup: Ktokoľvek“", + "Copy the web app URL and paste it above": "Skopírujte URL webovej aplikácie a vložte ju vyššie", + "Google Apps Script Code": "Kód skriptu Google Apps", + "Copy to Clipboard": "Kopírovať do schránky", + "Go to Extensions → Apps Script": "Prejdite do Rozšírenia → Skripty aplikácie", + "Copied to clipboard!": "Skopírované do schránky!", + "Failed to copy to clipboard": "Kopírovanie do schránky sa nepodarilo", + "Globalping - Access global monitoring probes": "Globalping – Prístup k globálnym monitorovacím sondám", + "GlobalpingDescription": "Globalping poskytuje prístup k tisícom komunitných sond na vykonávanie sieťových testov a meraní. Pre všetkých anonymných používateľov je stanovený limit 250 testov za hodinu. Ak chcete tento limit zdvojnásobiť na 500 testov za hodinu, uložte si svoj token v {accountSettings}.", + "Globalping API Token": "Globalping API Token", + "globalpingApiTokenDescription": "Získajte svoj Globalping API token na {0}.", + "GlobalpingHostname": "Verejne dostupný cieľ merania. Zvyčajne ide o názov hostiteľa alebo adresu IPv4/IPv6, v závislosti od typu merania.", + "GlobalpingLocation": "Do poľa polohy môžete zadávať kontinenty, krajiny, regióny, mestá, ASN, ISP alebo cloudové regióny. Filtre môžete kombinovať pomocou znaku {plus} (napr. {amazonPlusGermany} alebo {comcastPlusCalifornia}). Ak je latencia dôležitým ukazovateľom, použite filtre na zúženie polohy na malý región, aby ste sa vyhli výkyvom. {fullDocs}.", + "GlobalpingLocationDocs": "Kompletná dokumentácia o zadávaní polohy", + "GlobalpingIpFamilyInfo": "Verzia IP, ktorá sa má použiť. Povolené len v prípade, ak je cieľom názov hostiteľa.", + "GlobalpingResolverInfo": "Adresa IPv4/IPv6 alebo úplný názov domény (FQDN). Predvolené nastavenie je lokálny sieťový resolver sondy. Server resolveru môžete kedykoľvek zmeniť.", + "Protocol": "Protokol", + "Monitor Subtype": "Podtyp monitora", + "Location": "Poloha", + "account settings": "nastavenia účtu", + "Check for": "Skontrolovať", + "templateAvailableVariables": "Dostupné premenné", + "example": "Príklad", + "Result": "Výsledok", + "ntfyUseTemplate": "Prispôsobiť šablóny oznámení", + "ntfyUseTemplateDescription": "Aktivujte túto funkciu, aby ste mohli prispôsobiť názvy a správy oznámení pomocou šablón LiquidJS", + "ntfyCustomMessage": "Šablóna vlastnej správy", + "ntfyCustomTitle": "Šablóna vlastného názvu", + "ntfyNotificationTemplateFallback": "Nechajte prázdne, ak chcete použiť predvolený formát Uptime Kuma", + "Cloud ID": "Cloud ID", + "API Token": "API Token", + "See Jira Cloud Docs": "Pozrite si dokumentáciu Jira Cloud", + "see Jira Cloud Docs": "pozrite si dokumentáciu Jira Cloud", + "Jira Service Management": "Správa služieb Jira", + "aboutJiraCloudId": "Viac informácií o Jira Cloud ID: {0}", + "slackIncludeGroupName": "Zahrnúť názov skupiny monitorov", + "slackIncludeGroupNameDescription": "Ak je táto funkcia povolená, cesta skupiny monitorov bude zahrnutá do oznámení, aby sa ľahšie rozlišovali monitory s rovnakým názvom v rôznych skupinách.", + "slackUseTemplate": "Použiť vlastnú šablónu správy", + "slackUseTemplateDescription": "Ak je táto funkcia povolená, správa bude odoslaná pomocou vlastnej šablóny. Pomocou šablóny Liquid môžete zahrnúť informácie o skupine monitorov prostredníctvom monitorJSON.path alebo monitorJSON.pathName.", + "discordMessageFormat": "Formát správy", + "discordMessageFormatMinimalist": "Minimalistický (krátky status)", + "discordMessageFormatCustom": "Vlastná šablóna", + "discordUseMessageTemplate": "Použiť vlastnú šablónu správy", + "discordMessageTemplate": "Šablóna správy", + "discordMessageFormatNormal": "Normálny (rozšírené vloženie)", + "discordUseMessageTemplateDescription": "Ak je táto možnosť povolená, správa bude odoslaná pomocou vlastnej šablóny (LiquidJS). Nechajte pole prázdne, ak chcete použiť predvolený formát Uptime Kuma.", + "halopsa_field_title": "Názov upozornenia (vždy „Uptime Kuma Alert“)", + "halopsa_field_monitor": "Názov monitora", + "halopsa_field_monitor_id": "Jedinečný identifikátor monitora (nula pre testovacie oznámenia) – Použite toto na priradenie upozornení k tiketom", + "halopsa_field_message": "Úplná správa s upozornením so stavom a podrobnosťami", + "halopsa_field_uptime_kuma_version": "Číslo verzie Uptime Kuma", + "halopsa_setup_step5": "Nakonfigurujte runbook tak, aby používal monitor_id na spárovanie upozornenia s existujúcimi ticketmi", + "halopsa_payload_desc": "Nasledujúce polia sa odosielajú do vášho Halo PSA webhooku:", + "halopsa_field_status": "Stav monitora: UP, DOWN, NOTIFICATION alebo UNKNOWN", + "halopsa_field_timestamp": "Časová pečiatka udalosti vo formáte ISO 8601", + "halopsa_id_usage_hint": "💡 Tip: Použite monitor_id na spoľahlivé priradenie upozornení k tiketom a heartbeat_id na sledovanie histórie udalostí", + "Webhook Payload Fields": "Polia Webhook Payload", + "matrixUseTemplate": "Použiť šablónu vlastnej správy", + "matrixUseTemplateDescription": "Ak je táto funkcia povolená, správa bude odoslaná pomocou vlastnej šablóny.", + "teamsEnableTagsDescription": "Ak je táto funkcia povolená, správa bude obsahovať štítky monitora.", + "teamsEnableTags": "Pridať štítky", + "Teltonika SMS Gateway": "Teltonika SMS brána", + "teltonikaUrl": "URL adresa vášho zariadenia Teltonika", + "teltonikaUrlHelptext": "URL adresa by mala byť špecifikovaná ako úplný odkaz, napr. {0} alebo {1}.", + "teltonikaUnsafeTls": "Ignorovať overenie certifikátu", + "teltonikaUsername": "API používateľské meno", + "teltonikaPassword": "API heslo", + "teltonikaModem": "ID modemu", + "teltonikaModemHelptext": "ID SMS modemu musí mať formát {0}. Pokyny nájdete na stránke https://developers.teltonika-networks.com/reference/.", + "teltonikaPhoneNumber": "Telefónne číslo", + "teltonikaVersionWarning": "Tento poskytovateľ oznámení vyžaduje, aby vaše zariadenie Teltonika malo nainštalovanú verziu RMS 7.14.0 alebo vyššiu.", + "teltonikaPasswordHelptext": "Heslo používateľa API môžete zadať vo svojom routeri Teltonika, napr. {0}", + "teltonikaUnsafeTlsDescription": "Vypnutie overovania certifikátov TLS vás vystavuje riziku útokov typu „man-in-the-middle“, ktoré môžu viesť k úniku údajov a prevzatiu kontroly nad systémami. Neodporúčame vypínať overovanie certifikátov, pokiaľ nie ste ochotní akceptovať tento vektor útoku. Odporúčame používať LetsEncrypt s automatickým obnovovaním.", + "teltonikaUsernameHelptext": "Odporúčanie: Vytvorte samostatný účet, ktorý je obmedzený iba na odosielanie SMS správ, a zadajte sem jeho používateľské meno", + "teltonikaPhoneNumberHelptext": "Číslo musí byť v medzinárodnom formáte {0}, {1}. Je povolené iba jedno číslo.", + "RegexMatch": "Zadajte regulárny výraz, ktorý zodpovedá hodnote záznamu", + "GlobalpingMonitorDescription": "Globalping poskytuje prístup k tisícom komunitných sond na vykonávanie sieťových testov a meraní. Pre všetkých anonymných používateľov je stanovený limit 250 testov za hodinu. Ak chcete tento limit zdvojnásobiť na 500 testov za hodinu, uložte si svoj token v {accountSettings}. Ďalšie informácie nájdete v {docs}.", + "certificateExpiryNotificationHelp": "Počet dní vopred je možné nastaviť v nastaveniach.", + "signalUseTemplate": "Použite vlastnú šablónu správy", + "signalUseTemplateDescription": "Ak je táto funkcia povolená, správa bude odoslaná pomocou vlastnej šablóny. Na prispôsobenie formátu oznámenia môžete použiť šablóny Liquid.", + "domainExpiryNotificationHelp": "Počet dní vopred je možné nastaviť v nastaveniach.", + "monitorTypeDatabase": "Typ monitoru databázy", + "monitorTypeGameServer": "Herný server", + "monitorTypeSpecial": "Špeciálny", + "360messengerAuthToken": "API kľúč 360messenger", + "360messengerRecipient": "Telefónne číslo/a príjemcu", + "360messengerGroupId": "360messenger ID skupiny", + "360messengerUseTemplate": "Použite vlastnú šablónu správy", + "360messengerTemplate": "Šablóna správy 360messenger", + "360messengerGroupList": "Skupiny WhatsApp", + "360messengerSelectGroupList": "Vyberte skupinu, ktorú chcete pridať", + "360messengerSelectedGroupID": "Vybrané ID skupiny/ín", + "360messengerEnableSendToGroup": "Povoliť odosielanie do skupín WhatsApp", + "360messengerCustomMessageTemplate": "Šablóna vlastnej správy", + "360messengerEnableCustomMessage": "Povoliť vlastnú šablónu správy namiesto predvolenej správy.", + "360messengerMessageTemplate": "Šablóna správy", + "360messengerWayToWriteRecipient": "Zadajte jedno alebo viacero telefónnych čísel v medzinárodnom formáte bez predpony plus (napr. {0}). Viacero čísel oddeľte čiarkami.", + "360messengerErrorNoApiKey": "Najskôr zadajte svoj API kľúč 360messenger.", + "360messengerErrorApi": "Nie je možné načítať zoznam skupín WhatsApp (Chyba {statusCode}: {message}).", + "360messengerErrorGeneric": "Nie je možné načítať zoznam skupín WhatsApp: {message}", + "GlobalpingMultipleLocationsError": "Viacnásobné polohy nie sú podporované, pre každý monitor použite jednu polohu.", + "360messengerWayToGetUrlAndToken": "API kľúč pre 360messenger môžete získať na {0}.", + "360messengerErrorNoGroups": "Pre tento účet neboli nájdené žiadne skupiny WhatsApp.", + "GlobalpingLocationDescription": "Do poľa polohy môžete zadávať kontinenty, krajiny, regióny, mestá, ASN, ISP alebo cloudové regióny. Filtre môžete kombinovať pomocou znaku {plus} (napr. {amazonPlusGermany} alebo {comcastPlusCalifornia}). Ak je dôležitým ukazovateľom latencia, použite filtre na zúženie polohy na malý región, aby ste sa vyhli výkyvom, a pre lepšiu stabilitu nastavte filter {datacenter}. {fullDocs}.", + "fluxerMessageFormat": "Formát správy", + "fluxerMessageFormatMinimalist": "Minimalistický (krátky stav)", + "fluxerMessageFormatCustom": "Vlastná šablóna", + "fluxerUseMessageTemplate": "Použiť vlastnú šablónu správy", + "fluxerMessageTemplate": "Šablóna správy", + "Fluxer Webhook URL": "Fluxer Webhook URL adresa", + "wayToGetFluxerURL": "Získate ju tak, že prejdete do nastavení cieľového kanála > Webhooky > Vytvoriť webhook > Kopírovať URL adresu webhooku.", + "fluxerMessageFormatNormal": "Normálny (rozšírené vloženie)", + "fluxerUseMessageTemplateDescription": "Ak je táto možnosť povolená, správa bude odoslaná pomocou vlastnej šablóny (LiquidJS). Nechajte pole prázdne, ak chcete použiť predvolený formát Uptime Kuma.", + "Notification Action": "Akcia oznámenia", + "homeAssistantNotificationActionHelptext": "Zoznam akcií pre oznámenia nájdete v Home Assistant v časti „Nastavenia > Nástroje pre vývojárov > Akcie“. Akcie nájdete vyhľadávaním výrazu „notify“. Zadajte len časť za „notify.“, napr. pre akciu „notify.mobile_app_xyz“ zadajte „mobile_app_xyz“. Pre vstavané mobilné oznámenia vyhľadajte „Odoslať oznámenie cez mobile_app_xyz“ (nie „Odoslať oznámenie“).", + "oracledbConnectionString": "Databáza Oracle: {connectionString}", + "wayToGetMaxToken": "Token MAX bot a ďalšie informácie získate na {0}.", + "maxMessenger": "MAX messenger", + "maxApiUrlDescription": "Základná adresa URL rozhrania API pre aplikáciu MAX Messenger. Predvolené nastavenie: https://platform-api.max.ru", + "wayToGetMaxChatID": "V poli MAX zadajte identifikátor chatu, do ktorého majú byť správy doručené.", + "maxUseTemplate": "Použiť vlastnú šablónu správy", + "maxUseTemplateDescription": "Ak je táto funkcia zapnutá, správa bude odoslaná pomocou vlastnej šablóny.", + "maxTemplateFormatDescription": "Aplikácia MAX Messenger podporuje formátovanie v podobe obyčajného textu, Markdownu a HTML.", + "API Version": "Verzia API", + "Peer ID": "ID partnera", + "vkApiVersionDescription": "Verzia rozhrania VK API používaná pre požiadavky. Nechajte predvolené nastavenie, pokiaľ z dôvodu kompatibility nevyžadujete inú verziu rozhrania VK API.", + "vkDontParseLinks": "Zakázať ukážky odkazov", + "vkDontParseLinksDescription": "Ak je táto funkcia zapnutá, VK nebude generovať náhľady odkazov ani ukážky.", + "vkPeerIdDescription": "Zadajte cieľové peer_id VK. Táto hodnota sa odošle do API v nezmenenej podobe.", + "telnyxApiKey": "API kľúč (V2)", + "telnyxApiKeyHelptext": "Váš API kľúč Telnyx V2 z portálu Telnyx v sekcii API kľúče", + "telnyxPhoneNumber": "Z čísla", + "telnyxPhoneNumberHelptext": "Vaše telefónne číslo Telnyx, z ktorého chcete odosielať (musí byť zaregistrované vo vašom účte Telnyx, napr. +15551234567)", + "telnyxToNumber": "Na číslo", + "telnyxToNumberHelptext": "Cieľové telefónne číslo, na ktoré majú prichádzať SMS upozornenia (napr. +15559876543)", + "telnyxMessagingProfileId": "ID profilu zasielania správ (voliteľné)", + "telnyxMessagingProfileIdHelptext": "Voliteľne môžete priradiť profil zasielania správ Telnyx, aby ste mohli spravovať nastavenia a funkcie odosielateľa", + "RecordMatch": "Zhoda hodnoty záznamu", + "evolutionCustomMessageTitle": "Vlastná správa (voliteľné)", + "evolutionCustomMessageDesc": "Nechajte prázdne, ak chcete použiť predvolenú správu.", + "WebSocket Options": "Možnosti WebSocket", + "pingPacketSizeWarning": "Pri veľkosti paketu menšej ako 16 sa v systéme Linux nezaznamenávajú časy odozvy", + "VKTeams Chat Id Description": "V prípade používateľov ide o ich e-mailovú adresu. V prípade skupín a kanálov získajte ID z ich nastavení a pripojte k nemu {‚@‘}chat.agent", + "VKTeams Use Template": "Použiť vlastnú šablónu správy", + "VKTeams Use Template Description": "Ak je táto funkcia zapnutá, správa bude odoslaná pomocou vlastnej šablóny.", + "VKTeams Bot Token Description": "Informácie o tom, ako získať bot token, a ďalšie podrobnosti nájdete na {0}.", + "VKTeams Base Url Description": "Základná URL adresa rozhrania API VKTeams; v prípade riešenia On-Premise sa môže líšiť. Predvolené: https://myteam.mail.ru", + "VKTeams Template Format Description": "Pokiaľ ide o formátovanie správ, VKTeams podporuje formátovanie v podobe obyčajného textu, MarkdownV2 a HTML.", + "egosmsPhoneNumberDescription": "Číslo príjemcu s predvoľbou krajiny, bez znaku + alebo 00 (napr. 2567XXXXXXXX).", + "wayToGetEgoSMSToken": "Svoje používateľské meno a heslo pre rozhranie API EgoSMS si môžete vyžiadať {tu}.", + "egosmsSenderDescription": "ID odosielateľa zobrazené príjemcom (max. 11 znakov). Ak je pole prázdne, predvolené nastavenie je EGOSMS.", + "gamedigToken": "Token", + "gamedigTokenDescription": "Voliteľné. Niektoré herné servery vyžadujú overovací token.", + "Bearer Token": "Token na doručiteľa", + "Connection Timeout": "Časový limit pripojenia", + "smtpAdditionalHeadersTitle": "Dodatočné hlavičky", + "smtpAdditionalHeadersDesc": "Informácie o použití nájdete v časti Vlastné hlavičky Nodemailer {0}.", + "Flowtriq Webhook URL": "Webhook URL adresa", + "Flowtriq API Key": "API kľúč", + "flowtriqApiKeyDescription": "Voliteľný kľúč API na overovanie žiadostí webhookov smerujúcich do vašej inštancie Flowtriq.", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) funguje prostredníctvom samostatnej aplikácie WxPusher na 6 platformách – Android, iOS, HarmonyOS, Windows, macOS a Linux ({download}). Naskenujte nižšie uvedený QR kód, aby ste získali svoj SPT, a môžete začať odosielať. Viac SPT oddeľte čiarkou (,) – ďalšie informácie nájdete v {docs}.", + "wxPusherDownloadApp": "stiahnuť aplikáciu", + "wxPusherDocs": "oficiálny návod", + "Access Key": "Prístupový kľúč", + "ooredooUsernameDescription": "E-mailová adresa, ktorú používate na prihlásenie do riadiaceho panela hromadných SMS od Ooredoo.", + "ooredooAccessKeyDescription": "Nájdete ho v časti „API prístupový kľúč“ na vašom SMS paneli Ooredoo. Vložte ho presne tak, ako je uvedené — pred odoslaním sa automaticky zakóduje pomocou kódovania base64.", + "ooredooBearerTokenDescription": "Token zobrazený za textom „Authorization: Bearer“ vo vzorovom príkaze cURL na vašom SMS paneli Ooredoo.", + "ooredooRecipientDescription": "Zadajte jedno alebo viac čísel oddelených čiarkou alebo medzerou. Pred miestne 7-miestne čísla sa automaticky vkladá predpona 960.", + "Leave blank to use the default Ooredoo endpoint.": "Ak toto pole necháte prázdne, použije sa predvolený koncový bod Ooredoo.", + "plivoAuthID": "Auth ID", + "plivoAuthIDHelptext": "Vaše Plivo Auth ID z konzoly Plivo", + "plivoAuthToken": "Autentizačný token", + "plivoAuthTokenHelptext": "Váš autentizačný token Plivo z konzoly Plivo", + "plivoFromNumber": "Z čísla", + "plivoFromNumberHelptext": "Telefónne číslo Plivo, z ktorého sa má odosielať (napr. +15551234567). Pri SMS správach je možné použiť aj schválené alfanumerické ID odosielateľa; pri hlasových hovoroch je potrebné telefónne číslo Plivo.", + "plivoToNumber": "Na číslo", + "plivoToNumberHelptext": "Cieľové telefónne číslo, na ktoré majú prichádzať SMS alebo hlasový hovor (napr. +15559876543)", + "pm2Description": "Skontrolovať, či je PM2 proces {service_name} online", + "pm2ExpectedStates": "Očakávaný stav: online. Stavy „zastavené“ a „chyba“ sa považujú za DOWN.", + "PM2 Process": "PM2 proces", + "Refresh": "Obnoviť", + "Select PM2 process": "Vyberať proces PM2", + "plivoMessageType": "Typ správy", + "plivoVoiceCall": "Hlasový hovor", + "plivoAnswerUrl": "URL adresa odpovede", + "plivoAnswerUrlHelptext": "URL adresa, ktorá vráti XML formát Plivo pre daný hovor. Text upozornenia je pridaný ako ?message= parameter dopytu, takže váš koncový bod môže vrátiť napríklad {message}.", + "ntpThresholdsTitle": "Prahové hodnoty NTP", + "ntpOffsetThreshold": "Prah časového posunu (ms)", + "ntpDispersionThreshold": "Prahová hodnota koreňovej disperzie (ms)", + "ntpStratumThreshold": "Prahová hodnota Stratum", + "ntpDispersionThresholdHelp": "Označte ako nefunkčné, ak koreňovej disperzie presiahne túto hodnotu. Koreňová disperzia udáva presnosť hodín servera.", + "ntpStratumThresholdHelp": "Označiť ako „nefunguje“, ak je hodnota stratum rovná alebo vyššia ako táto hodnota. Stratum udáva kvalitu časového zdroja (1 = GPS/atómové hodiny, 16 = nesynchronizované).", + "ntpOffsetThresholdHelp": "Označiť ako nefunkčné, ak časový posun prekročí túto hodnotu v milisekundách.", + "openwaChatId": "ID konverzácie (telefónne číslo / ID kontaktu / ID skupiny)", + "wayToGetOpenwaApiUrl": "URL adresa vašej inštancie OpenWA.", + "wayToGetOpenwaApiKey": "API kľúč používaný pre vašu inštanciu OpenWA.", + "wayToGetOpenwaSession": "ID relácie, ktoré OpenWA používa na odosielanie oznámení.", + "openwaSession": "Relácia", + "turbosmtpConsumerKey": "Kľúč spotrebiteľa", + "turbosmtpConsumerSecret": "Tajný kľúč spotrebiteľa", + "openwaCustomMessageTitle": "Vlastná správa (voliteľné)", + "openwaCustomMessageDesc": "Nechajte prázdne, ak chcete použiť predvolenú správu.", + "wayToWriteOpenwaChatId": "Telefónne číslo s medzinárodnou predvoľbou, avšak bez znamienka plus na začiatku ({0}), ID kontaktu ({1}) alebo ID skupiny ({2}). Správu môžete odoslať na viacero ID konverzácií, pričom ich oddeľte čiarkami.", + "Pinglet Topic URL": "URL adresa témy", + "pingletTopicUrlDescription": "URL adresa témy Pinglet, na ktorú sa majú odosielať upozornenia, napr. https://app.pinglet.co.uk/your-namespace/alerts. Ak téma ešte neexistuje, vytvorí sa pri prvom zverejnení.", + "Hash Key": "Hash kľúč", + "wayToGetBearSMSToken": "Svoje používateľské meno a hash kľúč pre rozhranie BearSMS API môžete získať z {0}.", + "bearsmsPhoneNumberDescription": "Číslo príjemcu s predvoľbou krajiny, bez znaku + alebo 00 (napr. 9725XXXXXXXX).", + "bearsmsSenderDescription": "Voliteľné ID odosielateľa, ktoré sa zobrazuje príjemcom (max. 11 znakov). Musí byť schválené vo vašom účte BearSMS, inak sa použije predvolený odosielateľ účtu.", + "ClickUp API Token": "ClickUp API token", + "ClickUp API Token Description": "Osobný prístupový token si v ClickUp môžete vytvoriť tak, že prejdete do časti Nastavenia -> Integrácie a ClickApps -> ClickUp API -> API token", + "ClickUp Workspace ID": "ID pracovného priestoru ClickUp", + "ClickUp Workspace ID Description": "Nájdete ho na URL adrese vášho pracovného priestoru, hneď za app.clickup.com/, napr. app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ID kanála ClickUp", + "ClickUp Channel ID Description": "Nájdete ho v adrese kanála konverzácie, hneď za /chat/r/, napr. .../chat/r/abc123-4567.", + "milkyHttpAddress": "Adresa Milky HTTP (bez koncovky „/api“)", + "milkyMessageType": "Typ správy Milky", + "milkyGroupMessage": "Skupina", + "milkyPrivateMessage": "Súkromné", + "milkyUserOrGroupId": "ID skupiny/používateľa", + "milkySafetyTips": "Z bezpečnostných dôvodov je potrebné nastaviť prístupový token", + "smsgatewayUrlHelptext": "Základná URL adresa vašej SMS brány bez cesty k API.", + "smsgatewayTo": "Telefónne číslo (-la)", + "smsgatewayToHelptext": "Zoznam telefónnych čísel príjemcov oddelených čiarkami v medzinárodnom formáte (napr. +15551234567)", + "Device ID or Group ID": "ID zariadenia alebo ID skupiny", + "notifyAppDeviceIdDescription": "ID zariadenia z aplikácie Notify! alebo ID skupiny, aby sa naraz upozornili všetky zariadenia v skupine.", + "notifyAppSetup": "Otvorte Notify! a v časti Nastavenia vyhľadajte ID zariadenia a token. Nie je potrebný žiadny účet. Viac informácií nájdete na {0}.", + "Client Key": "Kľúč klienta", + "Channel": "Kanál", + "SFTP Path to Check": "Cesta SFTP, ktorú treba overiť", + "sftpPathHelpText": "Voliteľné. Ak je táto možnosť určená, monitor overí, či táto cesta na SFTP serveri existuje.", + "SSH Private Key": "Súkromný kľúč SSH", + "Passphrase": "Prístupová fráza", + "sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----", + "sshPrivateKeyHelpText": "Vložte obsah súboru so súkromným kľúčom (napr. id_rsa alebo id_ed25519).", + "sshPassphraseHelpText": "Voliteľné. Vyžaduje sa len v prípade, ak je váš súkromný kľúč zašifrovaný heslom.", + "optional": "voliteľné", + "Amoot SMS recipient numbers help": "Zadajte jedno alebo viacero mobilných čísel oddelených čiarkami.", + "Use Pattern": "Použiť vzor", + "Create an Amoot SMS pattern": "Vytvorte šablónu SMS v aplikácii Amoot", + "Line Number": "Číslo linky", + "Pattern Code ID": "ID kódu šablóny", + "Amoot SMS pattern help": "Zadajte identifikačný kód šablóny z vášho SMS panela Amoot. {0}", + "Use Own Line for Pattern": "Použiť vlastnú linku pre šablónu", + "Indigo URL": "URL adresa Indigo", + "indigoUrlHelptext": "Vaša adresa Indigo Reflector (https://MENO.indigodomo.net) alebo webový server Indigo vo vašej sieti (https://IP:8176).", + "indigoIgnoreTlsErrorHelptext": "Je potrebné pre webový server Indigo vo vašej sieti, ktorý používa certifikát s vlastným podpisom. Pre Reflector to nie je potrebné.", + "indigoApiKeyHelptext": "API kľúč z vášho účtu Indigo (Autorizácie) alebo lokálny tajný kľúč pre webový server vo vašej sieti.", + "Variable ID": "ID premennej", + "indigoVariableIdHelptext": "Do tejto premennej sa zapíše varovná správa.", + "Action Group ID": "ID skupiny akcií", + "indigoActionGroupIdHelptext": "Vykoná sa po aktualizácii premennej. Nastavte premennú, skupinu akcií alebo oboje. ID skopírujte z Indigo kliknutím pravým tlačidlom myši → Kopírovať ID." +} diff --git a/src/lang/sl-SI.json b/src/lang/sl-SI.json new file mode 100644 index 0000000..c3e3da9 --- /dev/null +++ b/src/lang/sl-SI.json @@ -0,0 +1,632 @@ +{ + "languageName": "Slovenščina", + "checkEverySecond": "Preveri na vsakih {0} sekund", + "retryCheckEverySecond": "Ponovno poskusi na vsakih {0} sekund", + "retriesDescription": "Maksimalno število poskusov predenj se storitev označi kot 'ne deluje' in se pošlje obvestilo", + "ignoreTLSError": "Prezrite napake TLS/SSL za spletna mesta HTTPS", + "upsideDownModeDescription": "Negiraj status. Če je storitev deluje bo označena kot 'ne deluje'.", + "maxRedirectDescription": "Maksimalno število sledečih preusmeritev. 0 onemogoči preusmeritve.", + "acceptedStatusCodesDescription": "Izberi kode statusa veljavna kot uspešen odgovor.", + "passwordNotMatchMsg": "Ponovljeno geslo se ne ujema.", + "notificationDescription": "Obvestila morajo biti dodeljena monitorju, da delujejo.", + "keywordDescription": "Iskana ključna beseda v surovem HTML ali JSON odgovoru. Iskanje je občutljivo na začetnico.", + "pauseDashboardHome": "Pavza", + "deleteMonitorMsg": "Ste prepričani, da želite izbrisati ta monitor?", + "deleteNotificationMsg": "Ste prepričani, da želite izbrisati to obvestilo za vse monitorje?", + "resolverserverDescription": "Cloudflare je privzeti strežnik. DNS strežnik lahko spremenite kadarkoli.", + "rrtypeDescription": "Izberite RR tip, ki ga želite spremljati", + "pauseMonitorMsg": "Ste prepričani, da želite pavzirati?", + "enableDefaultNotificationDescription": "To obvestilo bo kot privzeto omogočeno za vse nove monitorje. Še vedno ga lahko izključite posebej za vsak monitor.", + "clearEventsMsg": "Ste prepričani da želite izbrisati vse dogodke tega monitorja?", + "clearHeartbeatsMsg": "Ste prepričani da želite izbrisati vse srčne utripe tega monitorja?", + "confirmClearStatisticsMsg": "Ste prepričani da želite izbrisati VSO statistiko?", + "importHandleDescription": "Izberite 'Preskoči obstoječe', če želite preskočiti vsak monitor ali obvestilo z istim imenom. 'Prepiši' bo prepisal vse obstoječe monitorje in obvestila.", + "confirmImportMsg": "Ste prepričani da želite uvoziti varnostno kopijo? Preverite da ste izbrali pravo opcijo za uvoz.", + "twoFAVerifyLabel": "Prosimo vnesite žeton za potrditev 2FA:", + "tokenValidSettingsMsg": "Žeton je veljaven! Sedaj lahko shranite 2FA nastavitev.", + "confirmEnableTwoFAMsg": "Ste prepričani, da želite omogočiti 2FA?", + "confirmDisableTwoFAMsg": "Ste prepričani, da želite onemogočiti 2FA?", + "Settings": "Nastavitve", + "Dashboard": "Nadzorna plošča", + "New Update": "Nova posodobitev", + "Language": "Jezik", + "Appearance": "Izgled", + "Theme": "Teme", + "General": "Splošno", + "Primary Base URL": "Primaren URL", + "Version": "Različica", + "Check Update On GitHub": "Preveri posodobitev na GitHub-u", + "List": "Seznam", + "Add": "Dodaj", + "Add New Monitor": "Dodaj nov monitor", + "Quick Stats": "Hitro stanje", + "Up": "Deluje", + "Down": "Ne deluje", + "Pending": "Na čakanju", + "Unknown": "Neznano", + "Pause": "Pavza", + "Name": "Ime", + "Status": "Status", + "DateTime": "DatumČas", + "Message": "Sporočilo", + "No important events": "Ni pomembnih dogodkov", + "Resume": "Nadaljuj", + "Edit": "Uredi", + "Delete": "Izbriši", + "Current": "Trenutno", + "Uptime": "Uptime", + "Cert Exp.": "Pot. cer.", + "day": "dan | dni", + "-day": "-dni", + "hour": "ura", + "-hour": "-ur", + "Response": "Odgovor", + "Ping": "Ping", + "Monitor Type": "Tip monitorja", + "Keyword": "Ključna beseda", + "Friendly Name": "Ime za prikaz", + "URL": "URL", + "Hostname": "Hostname", + "Port": "Vrata", + "Heartbeat Interval": "Interval srčnega utripa", + "Retries": "Ponovni poskusi", + "Heartbeat Retry Interval": "Ponovni poskus srčnega utripa", + "Advanced": "Napredno", + "Upside Down Mode": "Negiran način", + "Max. Redirects": "Max. preusmeritev", + "Accepted Status Codes": "Sprejete kode statusa", + "Push URL": "Push URL", + "needPushEvery": "Pokliči ta URL vsakih {0} sekund.", + "pushOptionalParams": "Dodatni parametri: {0}", + "Save": "Shrani", + "Notifications": "Obvestila", + "Not available, please setup.": "Ni na voljo, prosimo nastavite.", + "Setup Notification": "Nastavi obvestila", + "Light": "Svetlo", + "Dark": "Temno", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Tema - vrstica srčnega utripa", + "Normal": "Normalna", + "Bottom": "Spodaj", + "None": "Brez", + "Timezone": "Časovni pas", + "Search Engine Visibility": "Vidljivost v spletnih iskalnikih", + "Allow indexing": "Dovoli indeksiranje", + "Discourage search engines from indexing site": "Odvračaj spletne iskalnike od indeksiranja te strani", + "Change Password": "Zamenjaj geslo", + "Current Password": "Trenutno geslo", + "New Password": "Novo geslo", + "Repeat New Password": "Ponovi novo geslo", + "Update Password": "Posodobi geslo", + "Disable Auth": "Onemogoči auth", + "Enable Auth": "Omogoči auth", + "disableauth.message1": "Ali ste prepričani, da želite onemogočiti {disableAuth}?", + "disable authentication": "avtentikacijo", + "disableauth.message2": "Namenjen je {intendThirdPartyAuth}, na primer Cloudflare Access.", + "where you intend to implement third-party authentication": "nekomu, ki ima pred programom Uptime Kuma vklopljeno zunanje preverjanje pristnosti", + "Please use this option carefully!": "Uporabljajte to opcijo previdno!", + "Logout": "Odjava", + "Leave": "Zapusti", + "I understand, please disable": "Razumem, prosim onemogočite", + "Confirm": "Potrdi", + "Yes": "Da", + "No": "Ne", + "Username": "Uporabniško ime", + "Password": "Geslo", + "Remember me": "Zapomni si me", + "Login": "Vpis", + "No Monitors, please": "Prosim, brez monitorjev", + "add one": "Dodaj enega", + "Notification Type": "Tip obvestila", + "Email": "Email", + "Test": "Test", + "Certificate Info": "Informacije certifikata", + "Resolver Server": "Strežnik za razreševanje", + "Resource Record Type": "Vrsta zapisa o viru", + "Last Result": "Zadnji rezultat", + "Create your admin account": "Ustvari administratorski račun", + "Repeat Password": "Ponovi geslo", + "Import Backup": "Uvozi varnostno kopijo", + "Export Backup": "Izvozi varnostno kopijo", + "Export": "Izvozi", + "Import": "Uvozi", + "respTime": "Odzivni čas (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Privzeto omogočeno", + "Apply on all existing monitors": "Uporabi na vseh obstoječih monitorjih", + "Create": "Ustvari", + "Clear Data": "Izbriši podatke", + "Events": "Dogodki", + "Heartbeats": "Srčni utripi", + "Auto Get": "Auto Get", + "backupDescription": "Izvozite lahko vse monitorje in obvestila v JSON datoteko.", + "backupDescription2": "Pomni: Zgodovina in podatki dogodkov niso vključeni.", + "backupDescription3": "Občutljivi podatki, kot žetoni za obvestila so vlkjučeni v datoteko za izvoz; prosimo hranite na varnem.", + "alertNoFile": "Izberite datoteko za Uvoz.", + "alertWrongFileType": "Prosimo izberite JSON datoteko.", + "Clear all statistics": "Pobrišite vso statistiko", + "Skip existing": "Preskoči obstoječe", + "Overwrite": "Prepiši", + "Options": "Možnosti", + "Keep both": "Ohrani oboje", + "Verify Token": "Potrdi žeton", + "Setup 2FA": "Nastavi 2FA", + "Enable 2FA": "Omogoči 2FA", + "Disable 2FA": "Onemogoči 2FA", + "2FA Settings": "2FA nastavitve", + "Two Factor Authentication": "Preverjanje pristnosti z dvema dejavnikoma", + "Active": "Aktivno", + "Inactive": "Neaktivno", + "Token": "Žeton", + "Show URI": "Prikaži URI", + "Tags": "Značke", + "Add New below or Select...": "Dodaj novo spodaj ali izberi iz seznama…", + "Tag with this name already exist.": "Značka s tem imenom že obstaja.", + "Tag with this value already exist.": "Značka s to vrednostjo že obstaja.", + "color": "barva", + "value (optional)": "vrednost (po želji)", + "Gray": "Siva", + "Red": "Rdeča", + "Orange": "Oranžna", + "Green": "Zelena", + "Blue": "Modra", + "Indigo": "Indigo", + "Purple": "Vijolična", + "Pink": "Roza", + "Search...": "Išči…", + "Avg. Ping": "Povp. Ping", + "Avg. Response": "Avg. odziv", + "Entry Page": "Vstopna stran", + "statusPageNothing": "Nikjer nič... Dodajte skupino ali monitor.", + "No Services": "Ni storitev", + "All Systems Operational": "Vsi sistemi delujejo", + "Partially Degraded Service": "Delno poslabšana storitev", + "Degraded Service": "Poslabšana storitev", + "Add Group": "Dodaj skupino", + "Add a monitor": "Dodaj monitor", + "Edit Status Page": "Uredi statusno stran", + "Go to Dashboard": "Pojdi na nadzorno ploščo", + "Status Page": "Statusna stran", + "Status Pages": "Statusne strani", + "defaultNotificationName": "Moje {notification} Obvestilo ({number})", + "here": "tukaj", + "Required": "Obvezno", + "telegram": "Telegram", + "Bot Token": "Robotkov žetonček", + "wayToGetTelegramToken": "Lahko dobiš žeton od {0}.", + "Chat ID": "ID pogovora", + "supportTelegramChatID": "Direkten pogovor pomoči / Skupina / ID kanala", + "wayToGetTelegramChatID": "Id lahko dobiš, če pošlješ sporočilo robotku in odpreš ta URL, da bi videl chat_id:", + "YOUR BOT TOKEN HERE": "ROBOTKOV ŽETON TUKAJ", + "chatIDNotFound": "Ne najdem Chat Id-ja; prvo pošlji sporočilo robotku", + "webhook": "Webhook", + "Post URL": "Post URL", + "Content Type": "Vrsta vsebine", + "webhookJsonDesc": "{0} je v redu za vsak moderen HTTP strežnik, kot recimo Express.js", + "webhookFormDataDesc": "{multipart} je v redu za PHP. JSON bo moral biti razčlenjen s {decodeFunction}", + "smtp": "Email (SMTP)", + "secureOptionNone": "Brez / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignoriraj TLS napako", + "From Email": "Od Email", + "emailCustomSubject": "Poljubna zadeva", + "To Email": "Za Email", + "smtpCC": "CC", + "smtpBCC": "BCC", + "discord": "Discord", + "Discord Webhook URL": "Discord Webhook URL", + "wayToGetDiscordURL": "To lahko dibiš v Server Settings -> Integrations -> Create Webhook", + "Bot Display Name": "Prikazno ime robotka", + "Prefix Custom Message": "Predpona poljubnega sporočila", + "Hello @everyone is...": "Pozdravljen {'@'}everyone je…", + "teams": "Microsoft Teams", + "Webhook URL": "Webhook URL", + "wayToGetTeamsURL": "Izvedi kako narediš webhook URL {0}.", + "signal": "Signal", + "Number": "Številka", + "Recipients": "Prejemniki", + "needSignalAPI": "Imeti moraš signal klienta z REST API.", + "wayToCheckSignalURL": "Kako se to naredi, lahko preveriš na tem URL-ju:", + "signalImportant": "POMEMBNO: Ne moreš mešati skupin in številk v prejemnikih!", + "gotify": "Gotify", + "Application Token": "Žeton za aplikacijo", + "Server URL": "URL Strežnika", + "Priority": "Prioriteta", + "slack": "Slack", + "Icon Emoji": "Emoji ikona", + "Channel Name": "Ime kanala", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Več o webhook-ih: {0}", + "aboutChannelName": "Vnesi ime kanala na {0} Channel Name polje, če želiš preskočiti webhook kanal. npr.: #drug-kanal", + "aboutKumaURL": "Če pustite polje Uptime Kuma URL prazno, bo nastavljeno privzeto na GitHub stran projekta.", + "emojiCheatSheet": "Emoji plonk listek: {0}", + "rocket.chat": "Rocket.Chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (podpira 50+ storitev za obveščevanje)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "User Key", + "Device": "Naprava", + "Message Title": "Naslov sporočila", + "Notification Sound": "Zvok obvestila", + "More info on:": "Več informacij na: {0}", + "pushoverDesc1": "Prioriteta nujnosti (2) ima privzeto nastavitev 30 sekund časa med ponovni poskusi in poteče po 1 uri.", + "pushoverDesc2": "Če želite pošiljati obvestila na različne naprave izpolnite polje 'Naprava'.", + "SMS Type": "Vrsta SMS-a", + "octopushTypePremium": "Premium (hitro - priporočljivo za opozarjanje)", + "octopushTypeLowCost": "Cenovno ugodno (počasno - včasih jih blokira operater)", + "checkPrice": "preveri {0} cene:", + "apiCredentials": "API poverilnice", + "octopushLegacyHint": "Uporabljate legacy verzijo Octopush-a (2011-2020) ali novo verzijo?", + "Check octopush prices": "Preveri octopush cene {0}.", + "octopushPhoneNumber": "Telefonska številka (npr.: +386031234567) ", + "octopushSMSSender": "Ime SMS pošiljatelja: 3-11 alfanumeričnih znakov in presledki (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Device ID", + "Apprise URL": "Apprise URL", + "Example:": "Primer: {0}", + "Read more:": "Preberi več: {0}", + "Status:": "Status: {0}", + "Read more": "Preberi več", + "appriseInstalled": "Apprise je nameščen.", + "appriseNotInstalled": "Apprise ni nameščen. {0}", + "Access Token": "Žeton za dostop", + "Channel access token": "Žeton za dostop do kanala", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console - {0}", + "Basic Settings": "Osnovne nastavitve", + "User ID": "User ID", + "Messaging API": "Messaging API", + "wayToGetLineChannelToken": "Prvo odpri {0}, ustvarite ponudnika in kanal (Messaging API), potem lahko žeton za dostop do kanala in ID uporabnika dobite iz zgoraj navedenih elementov menija.", + "Icon URL": "URL ikone", + "aboutIconURL": "V razdelku \"URL ikone\" lahko zagotovite povezavo do slike, ki bo nadomestila privzeto sliko profila. Ne bo uporabljena, če je nastavljena ikona Emoji.", + "aboutMattermostChannelName": "V razdelku \"URL ikone\" lahko zagotovite povezavo do slike, ki bo nadomestila privzeto sliko profila. Ne bo uporabljena, če je nastavljena ikona Emoji", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - poceni, vendar počasen in pogosto preobremenjen. Omejeno samo na poljske prejemnike.", + "promosmsTypeFlash": "SMS FLASH - sporočilo se samodejno prikaže v napravi prejemnika. Omejeno samo na poljske prejemnike.", + "promosmsTypeFull": "SMS FULL - Premium raven SMS, Uporabite lahko svoje ime pošiljatelja (najprej morate registrirati ime). Zanesljivo za opozorila.", + "promosmsTypeSpeed": "SMS SPEED - Najvišja prednost v sistemu. Zelo hitro in zanesljivo, vendar drago (približno dvakratnik cene SMS FULL)..", + "promosmsPhoneNumber": "Telefonska številka (za poljskega prejemnika Lahko preskočite področne oznake", + "promosmsSMSSender": "Ime pošiljatelja SMS : vnaprej registrirano ime ali eno od privzetih: SMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu WebHookURL", + "matrixHomeserverURL": "Homeserver URL (z http(s):// in vrata po želji)", + "Internal Room Id": "Interni ID sobe", + "matrixDesc1": "Notranji ID sobe lahko poiščete v naprednem razdelku nastavitev sobe v odjemalcu Matrix. Izgledati mora kot !QMdRCpUIfLwsfjxye6:home.server", + "matrixDesc2": "Zelo priporočljivo je, da ustvarite novega uporabnika in ne uporabljate svojega žetona za dostop uporabnika Matrix, saj bo omogočil popoln dostop do vašega računa in vseh sob, ki ste se jim pridružili. Namesto tega ustvarite novega uporabnika in ga povabite le v sobo, v kateri želite prejemati obvestila. Token dostopa lahko dobite tako, da zaženete {0}", + "Method": "Metoda", + "Body": "Telo", + "Headers": "Glave", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "Glave zahtevka niso veljavni JSON: ", + "BodyInvalidFormat": "Telo zahteve ni veljaven JSON: ", + "Monitor History": "Zgodovina", + "clearDataOlderThan": "Ohrani zgodovino {0} dni.", + "PasswordsDoNotMatch": "Gesli se ne ujemata.", + "records": "vnosi", + "One record": "En vnos", + "steamApiKeyDescription": "Za spremljanje igralnega strežnika Steam potrebujete ključ spletnega vmesnika Steam. Ključ API lahko registrirate tukaj: ", + "Current User": "Trenuten uporabnik", + "recent": "Nedavno", + "Done": "Zaključi", + "Info": "Info", + "Security": "Varnost", + "Steam API Key": "Steam API Ključ", + "Shrink Database": "Stisni bazo", + "Pick a RR-Type...": "Izberi RR tip…", + "Pick Accepted Status Codes...": "Izbiranje sprejetih kod stanja…", + "Default": "Privzeto", + "HTTP Options": "HTTP možnosti", + "Create Incident": "Ustvari incident", + "Title": "Naslov", + "Content": "Vsebina", + "Style": "Stil", + "info": "info", + "warning": "opozorilo", + "danger": "nevarnost", + "primary": "primarno", + "light": "svetlo", + "dark": "temno", + "Post": "Objavi", + "Please input title and content": "Vnesi naslov in vsebino", + "Created": "Ustvarjeno", + "Last Updated": "Nazadnje posodobljeno", + "Unpin": "Odpni", + "Switch to Light Theme": "Preklopi na svetlo temo", + "Switch to Dark Theme": "Preklopi na temno temo", + "Show Tags": "Prikaži značke", + "Hide Tags": "Skrij značke", + "Description": "Opis", + "No monitors available.": "Nobenega monitorja ni na voljo.", + "Add one": "Dodaj enega", + "No Monitors": "Ni monitorjev", + "Untitled Group": "Skupina brez imena", + "Services": "Storitve", + "Discard": "zavrzi", + "Cancel": "Prekliči", + "Powered by": "Poganja ga", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API uporabniško ime (vključno z webapi_ prefix)", + "serwersmsAPIPassword": "API geslo", + "serwersmsPhoneNumber": "Telefonska številka", + "serwersmsSenderName": "Ime SMS pošiljatelja (registrirani prek portala za stranke)", + "Schedule maintenance": "Planiraj vzdrževalna dela", + "Start of maintenance": "Začetek vzdrževalnih del", + "error": "napaka", + "Custom CSS": "Prilagodi CSS", + "Invalid": "Neveljaven", + "Add New Status Page": "Dodaj novo stran s statusom", + "Select status pages...": "Izberi strani s statusom…", + "Custom": "Po meri", + "topic": "Tema", + "successMessage": "Sporočilo o uspešnosti", + "critical": "kritično", + "Customize": "Prilagodi", + "Custom Footer": "Prilagodi nogo", + "deleteStatusPageMsg": "Ali si prepričan, da želiš izbrisati to stran s statusi?", + "Proxies": "Posredniki", + "default": "Privzeto", + "enabled": "Omogočeno", + "setAsDefault": "Nastavi kot Privzeto", + "Valid": "Veljaven", + "User": "Uporabnik", + "Installed": "Nameščeno", + "Not installed": "Ni nameščeno", + "Remove Token": "Odstrani Žeton", + "Start": "Začni", + "Stop": "Ustavi", + "Slug": "Polž", + "resendDisabled": "Ponovno pošiljanje je onemogočeno", + "Game": "Igra", + "Help": "Pomoč", + "statusMaintenance": "Vzdrževanje", + "Maintenance": "Vzdrževanje", + "Proxy Server": "Proxy Strežnik", + "sameAsServerTimezone": "Enako kot časovni pas strežnika", + "warningTimezone": "Uporablja časovni pas strežnika", + "Single Maintenance Window": "Enotno vzdrževalno okno", + "loadingError": "Podatkov ni mogoče pridobiti, poskusite znova pozneje.", + "Topic": "Tema", + "WeCom Bot Key": "WeCom Bot ključ", + "Browser Screenshot": "Zajem zaslona brskalnika", + "setupDatabaseChooseDatabase": "Katero bazo podatkov želite uporabiti?", + "setupDatabaseMariaDB": "Povežite se z zunanjo bazo podatkov MariaDB. Nastaviti morate informacije o povezavi z bazo podatkov.", + "dbName": "Ime baze podatkov", + "Invert Keyword": "Obrni ključno besedo", + "installing": "Nameščam", + "selectedMonitorCount": "Izbrani: {0}", + "startDateTime": "Začetni datum/čas", + "endDateTime": "Končni datum/čas", + "cronExpression": "Cron izraz", + "cronSchedule": "Urnik: ", + "invalidCronExpression": "Neveljaven Cron izraz: {0}", + "recurringInterval": "Interval", + "Recurring": "Ponavljajoče se", + "strategyManual": "Ročno aktivno/neaktivno", + "enableNSCD": "Omogoči NSCD (Name Service Cache Daemon) za predpomnjenje vseh zahtev DNS", + "chromeExecutable": "Chrome/Chromium izvedljiva datoteka", + "chromeExecutableAutoDetect": "Samodejno zaznavanje", + "Maintenance Time Window of a Day": "Časovno okno vzdrževanja enega dneva", + "Effective Date Range": "Datumsko obdobje veljavnosti (neobvezno)", + "Schedule Maintenance": "Načrtuj vzdrževanje", + "Edit Maintenance": "Uredi vzdrževanje", + "Date and Time": "Datum in Čas", + "DateTime Range": "Obseg datuma in časa", + "install": "Namesti", + "uninstall": "Odstrani", + "uninstalling": "Odstranjujem", + "confirmUninstallPlugin": "Ali res želite odstraniti ta vtičnik?", + "notificationRegional": "Regionalno", + "Clone Monitor": "Klon Monitor", + "Clone": "Kloniraj", + "cloneOf": "Klon od {0}", + "Setup Proxy": "Nastavitev proxyja", + "Proxy Protocol": "Proxy protokol", + "plugin": "Vtičnik | Vtičniki", + "Request Timeout": "Časovna omejitev zahteve", + "timeoutAfter": "Časovna omejitev po {0} sekundah", + "resendEveryXTimes": "Ponovno pošlji vsakih {0}-krat", + "weekdayShortMon": "Pon", + "weekdayShortTue": "Tor", + "weekdayShortWed": "Sre", + "weekdayShortThu": "Čet", + "weekdayShortFri": "Pet", + "weekdayShortSat": "Sob", + "weekdayShortSun": "Ned", + "dayOfWeek": "Dan v tednu", + "dayOfMonth": "Dan v mesecu", + "lastDay": "Zadnji dan", + "lastDay1": "Zadnji dan v mesecu", + "lastDay2": "Predzadnji dan v mesecu", + "lastDay3": "Tretji zadnji dan v mesecu", + "lastDay4": "Četrti zadnji dan v mesecu", + "No Maintenance": "Brez vzdrževanja", + "pauseMaintenanceMsg": "Ali res želite začasno ustaviti?", + "maintenanceStatus-under-maintenance": "V vzdrževanju", + "maintenanceStatus-inactive": "Neaktivno", + "maintenanceStatus-ended": "Končano", + "maintenanceStatus-unknown": "Neznano", + "Display Timezone": "Prikaz časovnega pasu", + "Server Timezone": "Strežniški časovni pas", + "statusPageMaintenanceEndDate": "Konec", + "IconUrl": "URL ikone", + "Enable": "Omogoči", + "Disable": "Onemogoči", + "markdownSupported": "Podprta sintaksa Markdown", + "Monitor": "Monitor | Monitorji", + "Expected Value": "Pričakovana vrednost", + "Json Query": "Json poizvedba", + "Home": "Domov", + "Cannot connect to the socket server": "Ni mogoče vzpostaviti povezave s strežnikom", + "Reconnecting...": "Ponovna povezava ...", + "General Monitor Type": "Splošni tip monitorja", + "Passive Monitor Type": "Pasivni monitor tip", + "Specific Monitor Type": "Specifičen monitor tip", + "setup a new monitor group": "nastavite novo skupino monitorjev", + "smtpDkimDesc": "Za uporabo glejte Nodemailer DKIM {0}.", + "smseagleRecipient": "Prejemnik(-i) (več mora biti ločeno z vejico)", + "Server URL should not contain the nfty topic": "URL strežnika ne sme vsebovati teme nfty", + "Add API Key": "Dodaj API ključ", + "deleteAPIKeyMsg": "Ali ste prepričani, da želite izbrisati ta API ključ?", + "ntfyAuthenticationMethod": "Metoda avtentikacije", + "setupDatabaseEmbeddedMariaDB": "Ničesar vam ni treba nastaviti. Ta slika dockerja je za vas samodejno vdelala in konfigurirala MariaDB. Uptime Kuma se bo povezal s to zbirko podatkov prek vtičnice unix.", + "setupDatabaseSQLite": "Preprosta datoteka zbirke podatkov, priporočljiva za uvedbe v majhnem obsegu. Pred v2.0.0 je Uptime Kuma uporabljal SQLite kot privzeto bazo podatkov.", + "Expiry": "Iztek", + "settingUpDatabaseMSG": "Postavitev podatkovne baze. Lahko traja nekaj časa, bodite potrpežljivi.", + "smtpDkimDomain": "Ime domene", + "smtpDkimPrivateKey": "Privatni ključ", + "smtpDkimHashAlgo": "Algoritem zgoščevanja (neobvezno)", + "smseagleGroup": "Ime(na) skupinskega imenika", + "smseagleContact": "Ime kontakta skupinskega imenika(ov)", + "smseagleRecipientType": "Vrsta prejemnika", + "apiKeyAddedMsg": "Vaš ključ API je bil dodan. Zabeležite si ga, saj ne bo več prikazan.", + "Integration Key": "Integracijski ključ", + "Expiry date": "Datum poteka", + "pushOthers": "Ostalo", + "programmingLanguages": "Programski jeziki", + "openModalTo": "odpri modalno za {0}", + "Add a domain": "Dodajte domeno", + "Remove domain": "Odstrani domeno '{0}'", + "smtpDkimSettings": "DKIM Nastavitve", + "documentation": "dokumentacijo", + "Integration URL": "Integracijski URL", + "Auto resolve or acknowledged": "Samodejna razrešitev ali potrditev", + "do nothing": "ne naredi ničesar", + "auto acknowledged": "samodejno potrjeno", + "alertaRecoverState": "Obnovi stanje", + "smseagleTo": "Telefonska številka(ke)", + "smseagleEncoding": "Pošlji kot Unicode", + "smseaglePriority": "Prednost sporočila (0–9, privzeto = 0)", + "Recipient Number": "Številka prejemnika", + "From Name/Number": "Od Ime/Številka", + "Leave blank to use a shared sender number.": "Če želite uporabiti skupno številko pošiljatelja, pustite prazno.", + "onebotGroupMessage": "Skupina", + "onebotPrivateMessage": "Privatno", + "onebotUserOrGroupId": "Skupina/Uporabniški ID", + "onebotSafetyTips": "Zaradi varnosti morate nastaviti žeton za dostop", + "pushDeerServerDescription": "Za uporabo uradnega strežnika pustite prazno", + "Google Analytics ID": "Google Analytics ID", + "Edit Tag": "Uredi oznako", + "Server Address": "Naslov strežnika", + "Learn More": "Več o tem", + "API Keys": "API Ključi", + "Don't expire": "Ne poteče", + "Continue": "Nadaljuj", + "Add Another": "Dodaj še eno", + "Key Added": "Ključ dodan", + "No API Keys": "Ni API ključev", + "apiKey-inactive": "Neaktivno", + "Expires": "Poteče", + "disableAPIKeyMsg": "Ali ste prepričani, da želite onemogočiti ta API ključ?", + "Generate": "Generiraj", + "pagertreeIntegrationUrl": "Integracijski URL", + "pagertreeUrgency": "Nujnost", + "pagertreeSilent": "Tiho", + "pagertreeLow": "Nizko", + "pagertreeMedium": "Srednje", + "pagertreeHigh": "Visoko", + "pagertreeCritical": "Kritično", + "pagertreeResolve": "Samodejno razreši", + "pagertreeDoNothing": "Ne naredi ničesar", + "lunaseaTarget": "Cilj", + "lunaseaDeviceID": "ID Naprave", + "lunaseaUserID": "Uporabniški ID", + "ntfyPriorityHelptextAllEvents": "Vsi dogodki se pošiljajo z najvišjo prioriteto", + "alertaAlertState": "Stanje opozorila", + "auto resolve": "samodejno razreševanje", + "apiKey-active": "Aktivno", + "apiKey-expired": "Poteklo", + "Resend Notification if Down X times consecutively": "Znova pošljite obvestilo, če X-krat zapored ne uspe", + "alertaEnvironment": "Okolje", + "enableProxyDescription": "Ta proxy ne bo vplival na zahteve za spremljanje, dokler ni aktiviran. S stanjem aktivacije lahko nadzirate začasno onemogočanje proxy-ja na vseh monitorjih.", + "setAsDefaultProxyDescription": "Ta proxy bo privzeto omogočen za nove monitorje. Še vedno lahko onemogočite proxy ločeno za vsak monitor.", + "Steam Game Server": "Igralni strežnik Steam", + "There might be a typing error in the address.": "Morda je v naslovu tipkarska napaka.", + "certificationExpiryDescription": "Nadzorniki HTTPS sprožijo obvestilo, ko potrdilo TLS poteče v:", + "pushViewCode": "Kako uporabljati Push monitor? (Ogled kode)", + "No status pages": "Ni Status strani", + "deleteDockerHostMsg": "Ali res želite izbrisati tega docker gostitelja za vse monitorje?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker Container", + "Host URL": "Gostiteljev URL", + "styleElapsedTime": "Pretečeni čas pod vrstico srčnega utripa", + "Affected Monitors": "Prizadeti Monitorji", + "Pick Affected Monitors...": "Izberi prizadete Monitorje…", + "All Status Pages": "Vse Status strani", + "webhookAdditionalHeadersTitle": "Dodatna zaglavja", + "webhookAdditionalHeadersDesc": "Nastavi dodatna zaglavja, poslane z webhookom. Vsaka glava mora biti definirana kot ključ/vrednost JSON.", + "topicExplanation": "MQTT tema za spremljanje", + "deleteProxyMsg": "Ali ste prepričani, da želite izbrisati ta proxy za vse monitorje?", + "Certificate Chain": "Veriga potrdil", + "Running": "Se izvaja", + "Not running": "Se ne izvaja", + "Accept characters:": "Sprejmi znake:", + "For example: nginx, Apache and Traefik.": "Na primer: nginx, Apache in Traefik.", + "Domain Name Expiry Notification": "Obvestilo o poteku imena domene", + "Proxy": "Proxy", + "Date Created": "Datum ustvarjanja", + "Footer Text": "Besedilo v nogi", + "Show Powered By": "Prikaži Powered By", + "Domain Names": "Imena domen", + "signedInDisp": "Prijavljen kot {0}", + "signedInDispDisabled": "Avtentikacija onemogočena.", + "proxyDescription": "Proxy-ji morajo biti dodeljeni monitor funkciji.", + "Certificate Expiry Notification": "Obvestilo o poteku veljavnosti potrdila", + "API Username": "API uporabnik", + "Show update if available": "Prikaži posodobitev, če je na voljo", + "Also check beta release": "Preverite tudi beta izdaje", + "Using a Reverse Proxy?": "Uporabljate povratni proxy?", + "Most likely causes:": "Najverjetnejši vzroki:", + "The resource is no longer available.": "Vir ni več na voljo.", + "What you can try:": "Kaj lahko poskusite:", + "Retype the address.": "Ponovno vnesite naslov.", + "Go back to the previous page.": "Vrni se na prejšnjo stran.", + "Coming Soon": "Prihaja kmalu", + "Query": "Poizvedba", + "settingsCertificateExpiry": "Potek potrdila TLS", + "Setup Docker Host": "Nastavite gostitelja Docker", + "Connection Type": "Vrsta povezave", + "Check how to config it for WebSocket": "Preverite, kako ga konfigurirati za WebSocket", + "Container Name / ID": "Container ime / ID", + "Docker Host": "Docker gostitelj", + "Docker Hosts": "Docker gostitelji", + "Domain": "Domena", + "Workstation": "Delovna postaja", + "Packet Size": "Velikost paketa", + "disableCloudflaredNoAuthMsg": "Ste v No Auth modusu, geslo ni potrebno.", + "wayToGetLineNotifyToken": "Žeton za dostop lahko dobite pri {0}", + "Examples": "Primeri", + "Home Assistant URL": "Home Assistant URL", + "API Key": "API ključ", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Vnesite ime gostitelja strežnika, s katerim se želite povezati, ali {localhost}, če nameravate uporabiti {local_mta}", + "Next": "Naslednji", + "wayToGetCloudflaredURL": "(Prenesite cloudflared iz {0})", + "startOrEndWithOnly": "Začnite ali končajte samo z {0}", + "No consecutive dashes": "Brez zaporednih pomišljajev", + "Authentication": "Avtentikacija", + "HTTP Basic Auth": "HTTP Basic Auth", + "New Status Page": "Nova Status stran", + "Page Not Found": "Stran ne obstaja", + "Reverse Proxy": "Povratni proxy", + "Backup": "Varnostna kopija", + "About": "O nas", + "cloudflareWebsite": "Spletno mesto Cloudflare", + "Message:": "Sporočilo:", + "Don't know how to get the token? Please read the guide:": "Ne veste, kako do žetona? Preberite vodnik:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Trenutna povezava se lahko izgubi, če se trenutno povezujete prek tunela Cloudflare. Ste prepričani, da ga želite ustaviti? Vnesite trenutno geslo, da ga potrdite.", + "HTTP Headers": "HTTP zaglavja", + "Trust Proxy": "Zaupaj proxy-ju", + "Other Software": "Druga programska oprema", + "Please read": "Prosim preberi", + "Subject:": "Zadeva:", + "Valid To:": "Velja do:", + "Days Remaining:": "Preostali dnevi:", + "Issuer:": "Izdajatelj:", + "Fingerprint:": "Prstni odtis:", + "No Proxy": "Ni proxy-ja" +} diff --git a/src/lang/sq.json b/src/lang/sq.json new file mode 100644 index 0000000..d54bc3f --- /dev/null +++ b/src/lang/sq.json @@ -0,0 +1,255 @@ +{ + "Settings": "Rregullime", + "Dashboard": "Pulti", + "Help": "Ndihmë", + "Language": "Gjuha", + "Appearance": "Shfaqja", + "Theme": "Tema", + "General": "Të përgjithshme", + "Game": "Lojë", + "Primary Base URL": "URL e bazës kryesore", + "List": "Listë", + "Add": "Shto", + "Add New Monitor": "Shto Monitor të ri", + "Quick Stats": "Statistika të shpejta", + "Up": "Lartë", + "Down": "Poshtë", + "Pending": "Në pritje", + "statusMaintenance": "Mirëmbatje", + "Maintenance": "Mirëmbajtje", + "Unknown": "Panjohur", + "languageName": "Shqip", + "New Update": "Përditësim i ri", + "Version": "Verzion", + "Check Update On GitHub": "Shiko për përditësim në GitHub", + "setupDatabaseSQLite": "Një skedar i thjeshtë për bazë të të dhënave, rekomandohet për shpërndarje të shkallës të vogël. Deri tek v2.0.0, Uptime Kuma përdorte SQLite si bazë të të dhënave të preferuar.", + "setupDatabaseEmbeddedMariaDB": "Nuk jeni të obliguar të përcaktoni çkado. Ky imazh i docker posedon të ngulitur dhe konfiguruar MariaDB për ju në mënyrë automatike. Uptime Kuma do të lidhet me këtë bazë të dhënash nëpërmjet një prize të unix.", + "setupDatabaseMariaDB": "Lidhu tek ndonjë bazë të dhënash MariaDB të jashtëm. Ju duhet të përcaktoni informacionet e lidhjes për bazën e të dhënave.", + "settingUpDatabaseMSG": "Duke rregulluar bazën e të dhënave. Mund të merr kohë, ju lutemi të jeni të durueshëm.", + "Cert Exp.": "Skadenca e Cert.", + "Invert Keyword": "Përmbys fjalën kyçe", + "needPushEvery": "Duhet të vizitohet URL çdo {0} sekonda.", + "Theme - Heartbeat Bar": "Tema - Bari i Pulsit", + "Search Engine Visibility": "Arritshmëria nga motorët e kërkimit", + "Notification Type": "Lloj Njoftimi", + "Active": "Aktive", + "Tag with this value already exist.": "Tanimë egziston një etiketë me këtë vlerë.", + "statusPageNothing": "Ska asgjë këtu, ju lutem shto një grup ose një monitor.", + "Edit Status Page": "Modifiko faqen e gjendjes", + "defaultNotificationName": "Njoftimi im i ({number}) {notification}", + "webhookJsonDesc": "{0} është e mirë për HTTP server moderne siç është Express.js", + "appriseNotInstalled": "Apprise nuk është instaluar. {0}", + "error": "gabim", + "Reset Token": "Reseto Token", + "Title": "Titulli", + "dbName": "Emri i bazës së të dhënave", + "Edit": "Modifiko", + "Email": "Email", + "Delete": "Fshij", + "Current": "Aktuale", + "Uptime": "Uptime", + "Monitor": "Monitor | Monitorët", + "day": "ditë | ditët", + "-day": "-ditë", + "hour": "orë", + "-hour": "-orë", + "Response": "Përgjegje", + "Ping": "Ping", + "Monitor Type": "Lloj Monitori", + "Keyword": "Fjalë kyçe", + "URL": "URL", + "Hostname": "Emri hostit", + "Port": "Porta", + "Heartbeat Interval": "Shpeshtia e pulsit", + "Friendly Name": "Nofka", + "ignoreTLSError": "Injoro TLS/SSL vërrejtjet për web faqet HTTPS", + "Push URL": "URL Shtysë", + "pushOptionalParams": "Parametrat jo të detyrueshme", + "Save": "Ruaj", + "Notifications": "Njoftimet", + "Setup Notification": "Rregullo njoftimet", + "Not available, please setup.": "Nuk është në dispozicion, ju lutemi rregulloni.", + "Light": "Ndriçuar", + "Dark": "Errët", + "Auto": "Vetvetiu", + "styleElapsedTimeShowWithLine": "Shfaq (Me rreshta)", + "styleElapsedTimeShowNoLine": "Shfaq (Pa rresht)", + "Normal": "Normal", + "Bottom": "Poshtë", + "None": "Asnjë", + "Timezone": "Zona kohore", + "Allow indexing": "Lejo indeksimin", + "Discourage search engines from indexing site": "Dekurajo motorët e kërkimit të indeksojnë këtë faqe", + "Change Password": "Ndrysho Fjalëkalimin", + "Current Password": "Fjalëkalimi Aktual", + "New Password": "Fjalëkalimi i ri", + "Repeat New Password": "Përsërit Fjalëkalimin e ri", + "Update Password": "Përditëso Fjalëkalimin", + "add one": "shto një", + "Current User": "Përdoruesi aktual", + "default": "Paracaktuar", + "pushOthers": "Tjerët", + "programmingLanguages": "Gjuhët e programimit", + "Disable Auth": "Ndalo autentifikimin", + "Enable Auth": "Lësho Autentifikimin", + "disableauth.message1": "A jeni të sigurtë për {disableAuth}?", + "Please use this option carefully!": "Ju lutem përdorni këtë mundësi me kujdes!", + "records": "regjistrat", + "disable authentication": "ndalo autentifikimin", + "Logout": "Ç'kyçu", + "Yes": "Po", + "No": "Jo", + "Username": "Emri përdorues", + "Password": "Fjalëkalimi", + "Remember me": "Më kujto", + "Login": "Kyçu", + "No Monitors, please": "Asnjë Monitor, ju lutem", + "I understand, please disable": "Kuptoj, të lutem ndalo", + "Confirm": "Konfirmo", + "Leave": "Largohu", + "Clear Data": "Pastro të dhënat", + "Start of maintenance": "Fillimi mirëmbajtjes", + "All Status Pages": "Gjithë faqet e gjendjes", + "Select status pages...": "Zgjedh faqe të gjendjes…", + "alertNoFile": "Të lutem zgjedh skedar për importim.", + "alertWrongFileType": "Të lutem zgjedh skedar të tipit JSON.", + "Clear all statistics": "Pastro gjithë statistikat", + "Skip existing": "Tejkalo ato që egzistojnë", + "Options": "Mundësitë", + "Keep both": "Mbaj që të dy", + "Verify Token": "Verifiko Token", + "Setup 2FA": "Rregullo 2FA", + "Enable 2FA": "Aktivizo 2FA", + "Disable 2FA": "Ndalo 2FA", + "2FA Settings": "2FA Rregullimet", + "Two Factor Authentication": "Autentifikimi Dy Faktorësh", + "filterActive": "Aktive", + "filterActivePaused": "Pushuar", + "Inactive": "Jo aktive", + "Token": "Token", + "Show URI": "Shfaq URI", + "Tags": "Etiketat", + "Add New Tag": "Shto Etiketë të re", + "Add New below or Select...": "Shto ose Zgjedh një nga mëposhtë…", + "Tag with this name already exist.": "Tanimë egziston një etiketë me këtë emër.", + "color": "Ngjyra", + "value (optional)": "vlera (jo detyrueshme)", + "Gray": "Hiri", + "Red": "Kuq", + "Orange": "Portokall", + "Green": "Gjelbërt", + "Blue": "Kaltërt", + "Indigo": "Llullaq", + "Purple": "Vjollcë", + "Pink": "Rozë", + "Custom": "Tjetër", + "Search...": "Kërko…", + "Search monitored sites": "Kërko faqet e monitoruara", + "Avg. Ping": "Ping Mesatar", + "Avg. Response": "Përgjegje Mesatare", + "Entry Page": "Faqja hyrjes", + "statusPageRefreshIn": "Rifresko në: {0}", + "No Services": "Ska shërbime", + "All Systems Operational": "Gjithë sistemet në gjendje pune", + "Partially Degraded Service": "Shërbime pjesërisht të degraduara", + "Degraded Service": "Shërbim i degraduar", + "Add Group": "Shto Grup", + "Add a monitor": "Shto një monitor", + "Go to Dashboard": "Shko tek Pulti", + "Status Page": "Faqe e gjendjes", + "Status Pages": "Faqet e gjendjes", + "here": "këtu", + "Required": "E detyrueshme", + "Post URL": "Posto URL", + "Content Type": "Lloji përmbajtjes", + "webhookFormDataDesc": "{multipart} është e mirë për PHP. JSON duhet të jet e kuptueshme me {decodeFunction}", + "Priority": "Prioriteti", + "One record": "Një regjistër", + "Read more": "Lexo më shumë", + "appriseInstalled": "Apprise është instaluar.", + "Method": "Metoda", + "Body": "Trupi", + "Headers": "Kokat", + "PushUrl": "URL Shtysë", + "webhookBodyCustomOption": "Trup tjetër", + "webhookAdditionalHeadersTitle": "Kokat shtesë", + "topic": "Tema", + "recent": "Të fundit", + "Done": "E bërë", + "Info": "Info", + "Security": "Siguri", + "Default": "Paracaktuar", + "HTTP Options": "Mundësitë HTTP", + "Content": "Përmbajtja", + "Style": "Stili", + "Create Incident": "Krijo incident", + "info": "info", + "warning": "vërrejtje", + "danger": "rrezik", + "critical": "kritike", + "primary": "primare", + "light": "ndriçuar", + "dark": "errët", + "Post": "Posto", + "Created": "Krijuar", + "Cancel": "Anulo", + "Discard": "Mos i ruaj", + "Last Updated": "Përditësuar së fundi", + "Show Tags": "Shfaq Etiketat", + "Home": "Shtëpi", + "Request Timeout": "Kërkesa Dështuar", + "timeoutAfter": "Dështo pas {0} sekondash", + "Resend Notification if Down X times consecutively": "Ridërgo Njoftim nëse bien X here njëpasnjë", + "Advanced": "Avancuar", + "checkEverySecond": "Kontrollo çdo {0} sekonda", + "retryCheckEverySecond": "Riprovo çdo {0} sekonda", + "resendEveryXTimes": "Ridërgo çdo {0} herë", + "resendDisabled": "Ridërgimi është ndalur", + "retriesDescription": "Maximumi provave para se shërbimi të shënohet si i rrëzuar dhe njoftimi dërgohet", + "maxRedirectDescription": "Maksimumi numrit të ridrejtimeve për ti ndjekur. Përcakto 0 për të ndaluar ridrejtimet.", + "Max. Redirects": "Maks. Ridrejtimeve", + "Accepted Status Codes": "Kodet e gjendjeve të pranuara", + "Expected Value": "Vlera e supozuar", + "Json Query": "Kërkesë Json", + "Retries": "Provat", + "Heartbeat Retry Interval": "Intervali riprovimit pulsit", + "Upside Down Mode": "Gjendja kokëposhtë", + "upsideDownModeDescription": "Rotullo statusin kokëposhtë. Nëse shërbimi nuk është i arritshëm, është rrëzuar.", + "Test": "Test", + "Certificate Info": "Info e Çertifikatës", + "Resolver Server": "Server Përgjegjës", + "Resource Record Type": "Lloji i regjistrit të resursit", + "Last Result": "Rezultati fundit", + "Create your admin account": "Krijo llogarinë tuaj admin", + "Repeat Password": "Përsërit Fjalëkalimin", + "Import Backup": "Importo Rezervën", + "Export Backup": "Eksporto Rezervë", + "Export": "Eksporto", + "Import": "Importo", + "respTime": "Koha Përgj. (ms)", + "notAvailableShort": "J/A", + "Default enabled": "Normalisht e aktivizuar", + "Apply on all existing monitors": "Apliko në të gjithë monitorët që egzistojnë", + "Create": "Krijo", + "Events": "Ndodhitë", + "Heartbeats": "Pulset", + "Auto Get": "Merr Vetvetiu", + "Schedule maintenance": "Përcakto mirëmbajtje", + "Affected Monitors": "Monitorët e përfshirë", + "Pick Affected Monitors...": "Zgjedh monitorët e përfshirë…", + "Overwrite": "Mbishkruaj", + "Cannot connect to the socket server": "Nuk mund të realizohet lidhja me serverin socket", + "Reconnecting...": "Duke u ri-lidhur...", + "General Monitor Type": "Lloj i përgjithshëm i Monitor", + "Passive Monitor Type": "Lloji pasiv i Monitor", + "Specific Monitor Type": "Lloj i përcaktuar i Monitor", + "markdownSupported": "Sintaksa Markdown përkrahet", + "pauseDashboardHome": "Pusho", + "Pause": "Pusho", + "Name": "Emri", + "Status": "Gjendja", + "DateTime": "DataKoha", + "Message": "Mesazhi", + "No important events": "Nuk ka ndodhi të rëndësishme", + "Resume": "Vazhdo" +} diff --git a/src/lang/sr-latn.json b/src/lang/sr-latn.json new file mode 100644 index 0000000..8c254d5 --- /dev/null +++ b/src/lang/sr-latn.json @@ -0,0 +1,182 @@ +{ + "languageName": "Srpski", + "checkEverySecond": "Proveri svakih {0} sekundi.", + "retriesDescription": "Maksimum pokušaja pre nego što se servis obeleži kao neaktivan i pošalje se obaveštenje.", + "ignoreTLSError": "Ignoriši TLS/SSL greške za HTTPS veb stranice.", + "upsideDownModeDescription": "Obrnite status. Ako je servis dostupan, onda je obeležen kao neaktivan.", + "maxRedirectDescription": "Maksimani broj preusmerenja da se prate. Postavite na 0 da bi se isključila preusmerenja.", + "acceptedStatusCodesDescription": "Odaberite statusne kodove koji se smatraju uspešnim odgovorom.", + "passwordNotMatchMsg": "Ponovljena lozinka se ne poklapa.", + "notificationDescription": "Molim Vas postavite obaveštenje za masmatrače da bise aktivirali.", + "keywordDescription": "Pretraži ključnu reč u čistom html ili JSON odgovoru sa osetljivim velikim i malim slovima", + "pauseDashboardHome": "Pauziraj", + "deleteMonitorMsg": "Da li ste sigurni da želite da obrišete ovog posmatrača?", + "deleteNotificationMsg": "Da li ste sigurni d aželite da uklonite ovo obaveštenje za sve posmatrače?", + "resolverserverDescription": "Cloudflare je podrazumevani server. Možete promeniti server za raszrešavanje u bilo kom trenutku.", + "rrtypeDescription": "Odaberite RR-Type koji želite da posmatrate", + "pauseMonitorMsg": "Da li ste sigurni da želite da pauzirate?", + "Settings": "Podešavanja", + "Dashboard": "Komandna tabla", + "New Update": "Nova verzija", + "Language": "Jezik", + "Appearance": "Izgled", + "Theme": "Tema", + "General": "Opšte", + "Version": "Verzija", + "Check Update On GitHub": "Proverite novu verziju na GitHub-u", + "List": "Lista", + "Add": "Dodaj", + "Add New Monitor": "Dodaj novog posmatrača", + "Quick Stats": "Brze statistike", + "Up": "Aktivno", + "Down": "Neaktivno", + "Pending": "Nerešeno", + "Unknown": "Nepoznato", + "Pause": "Pauziraj", + "Name": "Ime", + "Status": "Status", + "DateTime": "Datum i vreme", + "Message": "Poruka", + "No important events": "Nema bitnih događaja", + "Resume": "Nastavi", + "Edit": "Izmeni", + "Delete": "Ukloni", + "Current": "Trenutno", + "Uptime": "Vreme rada", + "Cert Exp.": "Istek sert.", + "day": "dan | dana", + "-day": "-dana", + "hour": "sat", + "-hour": "-sata", + "Response": "Odgovor", + "Ping": "Ping", + "Monitor Type": "Tip posmatrača", + "Keyword": "Ključna reč", + "Friendly Name": "Prijateljsko ime", + "URL": "URL", + "Hostname": "Hostname", + "Port": "Port", + "Heartbeat Interval": "Interval otkucaja srca", + "Retries": "Pokušaji", + "Advanced": "Napredno", + "Upside Down Mode": "Naopak mod", + "Max. Redirects": "Maks. preusmerenja", + "Accepted Status Codes": "Prihvaćeni statusni kodovi", + "Save": "Sačuvaj", + "Notifications": "Obaveštenja", + "Not available, please setup.": "Nije dostupno, molim Vas podesite.", + "Setup Notification": "Postavi obaveštenje", + "Light": "Svetlo", + "Dark": "Tamno", + "Auto": "Automatsko", + "Theme - Heartbeat Bar": "Tema - Traka otkucaja srca", + "Normal": "Normalno", + "Bottom": "Dole", + "None": "Isključeno", + "Timezone": "Vremenska zona", + "Search Engine Visibility": "Vidljivost pretraživačima", + "Allow indexing": "Dozvoli indeksiranje", + "Discourage search engines from indexing site": "Odvraćajte pretraživače od indeksiranja sajta", + "Change Password": "Promeni lozinku", + "Current Password": "Trenutna lozinka", + "New Password": "Nova lozinka", + "Repeat New Password": "Ponovi novu lozinku", + "Update Password": "Izmeni lozinku", + "Disable Auth": "Isključi autentifikaciju", + "Enable Auth": "Uključi autentifikaciju", + "disableauth.message1": "Da li ste sigurni da želite da {disableAuth}?", + "disable authentication": "isključite autentifikaciju", + "disableauth.message2": "To je za {intendThirdPartyAuth} ispred Uptime Kuma kao na primer Cloudflare Access.", + "where you intend to implement third-party authentication": "one koji imaju dodatu autentifikaciju", + "Please use this option carefully!": "Molim Vas koristite ovo sa pažnjom.", + "Logout": "Odloguj se", + "Leave": "Izađi", + "I understand, please disable": "Razumem, molim te isključi", + "Confirm": "Potvrdi", + "Yes": "Da", + "No": "Ne", + "Username": "Korisničko ime", + "Password": "Lozinka", + "Remember me": "Zapamti me", + "Login": "Uloguj se", + "No Monitors, please": "Bez posmatrača molim", + "add one": "dodaj jednog", + "Notification Type": "Tip obaveštenja", + "Email": "E-pošta", + "Test": "Test", + "Certificate Info": "Informacije sertifikata", + "Resolver Server": "Razrešivački server", + "Resource Record Type": "Tip zapisa resursa", + "Last Result": "Poslednji rezultat", + "Create your admin account": "Naprivi administratorski nalog", + "Repeat Password": "Ponovite lozinku", + "respTime": "Vreme odg. (ms)", + "notAvailableShort": "N/A", + "Green": "Zelena", + "All Systems Operational": "Svi sistemi su operativni", + "Required": "Potrebno", + "Content Type": "Tip Sadržaja", + "oracledbConnectionString": "Oracle baza podataka: {connectionString}", + "Primary Base URL": "Primarni URL baze", + "Home": "Početna", + "Pinned incidents are shown prominently on the status page": "Zakačeni incidenti su istaknuti na statusnoj strani", + "Blue": "Plava", + "Pink": "Ružičasta", + "Purple": "Ljubičasta", + "Custom": "Prilagođeno", + "Search...": "Pretraga…", + "Edit Status Page": "Uredi statusnu stranicu", + "Go to Dashboard": "Idi na kontrolnu tablu", + "Status Page": "Statusna stranica", + "Status Pages": "Statusne stranice", + "here": "ovde", + "Result": "Rezultat", + "templateMsg": "poruka obaveštenja", + "templateServiceName": "ime usluge", + "templateHostnameOrURL": "ime hosta ili URL", + "webhookBodyCustomOption": "Prilagođeno Telo", + "appriseInstalled": "Apprise je instaliran.", + "Webhook URL": "Veb-kuka URL", + "Application Token": "Aplikacioni Token", + "Server URL": "Serverski URL", + "Priority": "Prioritet", + "emojiCheatSheet": "Prečice za Emotikone: {0}", + "Read more": "Pročitaj više", + "appriseNotInstalled": "Apprise nije instaliran. {0}", + "Method": "Metoda", + "Body": "Telo", + "Headers": "Zaglavlje", + "PushUrl": "URL za slanje", + "One record": "jedan izveštaj", + "steamApiKeyDescriptionAt": "Za praćenje Steam servera za igre potreban Vam je Steam Web-API ključ. Moguća je registracija Vašeg API ključa na {url}", + "templateStatus": "status", + "versionIs": "Verzija: {version}", + "setupDatabaseChooseDatabase": "Koju bazu podataka želite da koristite?", + "Help": "Pomoć", + "dbName": "Ime baze podataka", + "enableSSL": "Omogući SSL/TLS", + "mariadbUseSSLHelptext": "Omogući korišćenje šifrovane veze prema bazi podataka. Potrebno za većinu baza podataka u oblaku.", + "mariadbCaCertificateLabel": "CA sertifikat", + "statusMaintenance": "Održavanje", + "Cannot connect to the socket server": "Nije moguće povezivanje na soket servera", + "Search monitored sites": "Pretraži posmatrane lokacije", + "Avg. Ping": "Prosečna Latencija", + "Avg. Response": "Prosečno Vreme Odgovora", + "Add Group": "Dodaj Grupi", + "BodyInvalidFormatBecause": "Zatraženo telo nije validni JSON zbog {error}", + "Monitor History": "Istorija praćenja", + "HeadersInvalidFormatBecause": "Zatražena zaglavlja nisu validni JSON zbog {error}", + "clearDataOlderThan": "Čuvaj istoriju praćenja {0} dana.", + "Indigo": "Indigo", + "statusPageRefreshIn": "Osvežavanje za: {0}", + "No Services": "Nema Usluga", + "Partially Degraded Service": "Parcijalno degradirane usluge", + "Degraded Service": "Degradirane usluge", + "Add a monitor": "Dodaj praćenje", + "Edit Incident": "Uredi incident", + "templateAvailableVariables": "Dostupne promenjive", + "example": "Primer", + "webhookAdditionalHeadersTitle": "Dodatna zaglavlja", + "records": "izveštaji", + "Maintenance": "Održavanje" +} diff --git a/src/lang/sr.json b/src/lang/sr.json new file mode 100644 index 0000000..771e71b --- /dev/null +++ b/src/lang/sr.json @@ -0,0 +1,1145 @@ +{ + "languageName": "Српски", + "checkEverySecond": "Провери сваких {0} секунди", + "retriesDescription": "Максимално покушаја пре него што се сервис обележи као неактиван и пошаље се обавештење", + "ignoreTLSError": "Игнориши TLS/SSL грешке за HTTPS веб странице", + "upsideDownModeDescription": "Обрните статус. Ако је сервис доступан, онда је обележен као неактиван.", + "maxRedirectDescription": "Максималан број преусмерења за праћење. Поставите на 0 да би се искључила преусмерења.", + "acceptedStatusCodesDescription": "Одаберите статусне кодове који се сматрају успешним одговором.", + "passwordNotMatchMsg": "Поновљена лозинка се не поклапа.", + "notificationDescription": "Обавештења морају бити додељена посматрачу да би функционисала.", + "keywordDescription": "Претражи кључну реч у чистом HTML или JSON одговору. Претрага разликује велика и мала слова.", + "pauseDashboardHome": "Паузирано", + "deleteMonitorMsg": "Да ли заиста желите да избришете овог посматрача?", + "deleteNotificationMsg": "Да ли заиста желите да уклоните ово обавештење за све посматраче?", + "resolverserverDescription": "Cloudflare је подразумевани сервер. Можете променити сервер за расзрешавање у било ком тренутку.", + "rrtypeDescription": "Одаберите RR тип који желите да посматрате", + "pauseMonitorMsg": "Да ли заиста желите да паузирате?", + "Settings": "Поставке", + "Dashboard": "Командна табла", + "New Update": "Нова верзија", + "Language": "Језик", + "Appearance": "Изглед", + "Theme": "Тема", + "General": "Опште", + "Version": "Верзија", + "Check Update On GitHub": "Проверите нову верзију на GitHub-у", + "List": "Листа", + "Add": "Додај", + "Add New Monitor": "Додај новог посматрача", + "Quick Stats": "Брзе статистике", + "Up": "Активно", + "Down": "Неактивно", + "Pending": "Нерешено", + "Unknown": "Непознато", + "Pause": "Паузирај", + "Name": "Назив", + "Status": "Статус", + "DateTime": "Датум и време", + "Message": "Порука", + "No important events": "Нема битних догађаја", + "Resume": "Настави", + "Edit": "Измени", + "Delete": "Уклони", + "Current": "Тренутно", + "Uptime": "Време рада", + "Cert Exp.": "Истек сертификата", + "day": "дан | дана", + "-day": "-дана", + "hour": "сат", + "-hour": "-сата", + "Response": "Одговор", + "Ping": "Пинг", + "Monitor Type": "Тип посматрача", + "Keyword": "Кључна реч", + "Friendly Name": "Пријатељско име", + "URL": "Адреса", + "Hostname": "Назив домаћина", + "Port": "Порт", + "Heartbeat Interval": "Интервал откуцаја срца", + "Retries": "Покушаји", + "Advanced": "Напредно", + "Upside Down Mode": "Наопак мод", + "Max. Redirects": "Максимално преусмерења", + "Accepted Status Codes": "Прихваћени статусни кодови", + "Save": "Сачувај", + "Notifications": "Обавештења", + "Not available, please setup.": "Није доступно, молим Вас подесите.", + "Setup Notification": "Постави обавештење", + "Light": "Светло", + "Dark": "Тамно", + "Auto": "Аутоматско", + "Theme - Heartbeat Bar": "Тема - Трака откуцаја срца", + "Normal": "Нормално", + "Bottom": "Доле", + "None": "Искључено", + "Timezone": "Временска зона", + "Search Engine Visibility": "Видљивост претраживачима", + "Allow indexing": "Дозволи индексирање", + "Discourage search engines from indexing site": "Одвраћајте претраживаче од индексирања сајта", + "Change Password": "Промени лозинку", + "Current Password": "Тренутна лозинка", + "New Password": "Нова лозинка", + "Repeat New Password": "Понови нову лозинку", + "Update Password": "Измени лозинку", + "Disable Auth": "Искључи аутентификацију", + "Enable Auth": "Укључи аутентификацију", + "disableauth.message1": "Да ли сте сигурни да желите да {disableAuth}?", + "disable authentication": "искључите аутентификацију", + "disableauth.message2": "То је за {intendThirdPartyAuth} испред Uptime Kuma као на пример Cloudflare Access.", + "where you intend to implement third-party authentication": "оне који имају додату аутентификацију", + "Please use this option carefully!": "Молим вас пажљиво користите ову опцију!", + "Logout": "Одјави се", + "Leave": "Изађи", + "I understand, please disable": "Разумем, молим те искључи", + "Confirm": "Потврди", + "Yes": "Да", + "No": "Не", + "Username": "Корисничко име", + "Password": "Лозинка", + "Remember me": "Запамти ме", + "Login": "Улогуј се", + "No Monitors, please": "Без посматрача молим", + "add one": "додај једног", + "Notification Type": "Тип обавештења", + "Email": "Е-пошта", + "Test": "Тест", + "Certificate Info": "Информације сертификата", + "Resolver Server": "Разрешивачки сервер", + "Resource Record Type": "Тип записа ресурса", + "Last Result": "Последњи резултат", + "Create your admin account": "Наприви администраторски налог", + "Repeat Password": "Поновите лозинку", + "respTime": "Време одг. (мс)", + "tcp": "TCP / HTTP", + "Number": "Број", + "Access Token": "Токен за приступ", + "Heartbeats": "Откуцаји срца", + "Proxy": "Прокси", + "Pin this incident": "Закачи овај инцидент", + "Expected Value": "Очекивана вредност", + "Import": "Увези", + "Create": "Направи", + "Create Incident": "Направи инцидент", + "Title": "Наслов", + "Content": "Садржај", + "danger": "опасност", + "Style": "Стил", + "warning": "упозорење", + "error": "грешка", + "critical": "критично", + "primary": "примарно", + "Loading...": "Учитавање...", + "No incidents recorded": "Нема записаних инцидената", + "minuteShort": "{n} мин | {n} мин", + "years": "{n} године | {n} године", + "Host URL": "Адреса домаћина", + "Path": "Путања", + "programmingLanguages": "Програмски језици", + "Clear Data": "Очисти податке", + "Events": "Догађаји", + "Schedule maintenance": "Закажи одржавање", + "Options": "Опције", + "HTTP Options": "HTTP опције", + "Default": "Подразумевано", + "dark": "тамно", + "Leave blank to use status page title": "Оставите празно да користите наслов странице статуса", + "Refresh Interval": "Интервал освежавања", + "Domain Names": "Називи домена", + "signedInDisp": "Пријављен као {0}", + "Workstation": "Радна станица", + "Packet Size": "Величина пакета", + "telegramSendSilently": "Пошаљи тихо", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Domain": "Домен", + "Optional": "Опционо", + "versionIs": "Верзија: {version}", + "Your User ID": "Ваш ID корисника", + "Read more:": "Прочитај више: {0}", + "Enable TLS": "Омогући TLS", + "Status:": "Статус: {0}", + "Strategy": "Стратегија", + "Example:": "Пример: {0}", + "Economy": "Економично", + "Huawei": "Huawei", + "Help": "Помоћ", + "settingUpDatabaseMSG": "Подешавање базе података. Ово може да потраје, зато будите стрпљиви.", + "setupDatabaseChooseDatabase": "Коју базу података би сте желели да користите?", + "dbName": "Назив базе података", + "enableSSL": "Омогући SSL/TLS", + "mariadbCaCertificateLabel": "CA сертификат", + "statusMaintenance": "Одржавање", + "Maintenance": "Одржавање", + "unknownDays": "Непознато дана", + "Reconnecting...": "Поновно повезивање...", + "now": "сада", + "minutes": "{n} минут | {n} минута", + "time ago": "пре {0}", + "days": "{n} дан | {n} дана", + "hours": "{n} сат | {n} сата", + "retryCheckEverySecond": "Покушавај сваких {0} секунди", + "logoutCurrentUser": "Одјави {username}", + "Incident description": "Опис инцидента", + "Past Incidents": "Прошли инциденти", + "Incident not found or access denied": "Инцидент није пронађен или немате приступ", + "Incident title": "Наслов инцидента", + "Export": "Извези", + "Footer Text": "Текст подножја", + "RSS Title": "RSS наслов", + "User ID": "ID корисника", + "light": "светло", + "secureOptionTLS": "TLS (465)", + "Load More": "Учитај више", + "API Version": "Верзија API-ја", + "Recipients": "Примаоци", + "auto acknowledged": "аутоматски потврђено", + "Schedule Maintenance": "Закажи одржавање", + "No monitors found": "Ниједан посматрач није пронађен.", + "Learn More": "Више информација", + "noGroupMonitorMsg": "Није доступно. Прво направите групу посматрача.", + "Monitor Subtype": "Подтип посматрача", + "Indigo": "Индиго", + "monitorTypeDatabase": "Посматрач базе података", + "monitorTypeGameServer": "Играчки сервер", + "monitorTypeSpecial": "Посебан", + "Apply on all existing monitors": "Примени на све постојеће посматраче", + "Affected Monitors": "Посматрачи на које утиче", + "Gray": "Сива", + "Red": "Црвена", + "Green": "Зелена", + "Blue": "Плава", + "Purple": "Љубичаста", + "statusPageNothing": "Овде нема ничега, молим вас додајте групу или посматрача.", + "No monitors available.": "Нема доступних посматрача.", + "No Monitors": "Нема посматрача", + "slackIncludeGroupName": "Уврсти назив групе посматрача", + "Specific Monitor Type": "Специфичан посматрач", + "General Monitor Type": "Општи посматрач", + "Passive Monitor Type": "Пасивни посматрач", + "defaultFriendlyName": "Нови посматрач", + "Pick Affected Monitors...": "Изаберите посматраче на које утиче…", + "Start of maintenance": "Почетак одржавања", + "All Status Pages": "Све странице статуса", + "Select status pages...": "Изаберите странице статуса…", + "color": "Боја", + "value (optional)": "вредност (опционо)", + "Avg. Ping": "Просечан пинг", + "Avg. Response": "Просечан одговор", + "Pink": "Ружичаста", + "Custom": "Прилагођена", + "Search...": "Претрага…", + "Search monitored sites": "Претражите посматране сајтове", + "Add a monitor": "Додај посматрача", + "Pinned incidents are shown prominently on the status page": "Закачени инциденти су издвојени на страници статуса", + "statusPageRefreshIn": "Освежавање за {0}", + "Edit Status Page": "Измените страницу статуса", + "Status Page": "Страница статуса", + "Status Pages": "Странице статуса", + "templateMonitorJSON": "објекат који описује посматрача", + "Monitor History": "Историја посматрача", + "clearDataOlderThan": "Задржи историјске податке посматрача {0} дана.", + "selectedMonitorCountMsg": "изабрано: {n} | изабрано: {n}", + "selectMonitorMsg": "Изаберите посматраче да би сте извршили радње", + "selectAllMonitorsAria": "Изаберите све посматраче", + "deselectAllMonitorsAria": "Изаберите ниједан посматрач", + "deleteStatusPageMsg": "Да ли заиста желите да избришете ову страницу статуса?", + "Add New Status Page": "Додај нову страницу статуса", + "No status pages": "Нема страница статуса", + "maintenanceStatus-ended": "Завршено", + "maintenanceStatus-unknown": "Непознато", + "Clone Maintenance": "Дуплирај одржавање", + "ariaPauseMaintenance": "Паузирај ово заказано одржавање", + "ariaResumeMaintenance": "Настави ово заказано одржавање", + "ariaCloneMaintenance": "Направи копију овог заказаног државања", + "ariaEditMaintenance": "Измени ово заказано одржавање", + "cloneOf": "Дупликат од", + "Clone": "Дуплирај", + "Clone Monitor": "Дуплирај посматрача", + "affectedMonitorsDescription": "Изаберите посматраче на које утиче тренутно одржавање", + "affectedStatusPages": "Прикажите ову поруку на изабраним страницама статуса", + "domain_expiry_unsupported_monitor_type": "Надгледање истека домена није подржано за овај тип посматрача", + "Show this Maintenance Message on which Status Pages": "Прикажи ову поруку на којим страницама статуса", + "halopsa_field_monitor": "Назив посматрача", + "halopsa_field_status": "Статус посматрача: АКТИВАН, НЕАКТИВАН, ОБАВЕШТЕЊЕ, или НЕПОЗНАТО", + "Refresh Interval Description": "Ова страница статуса ће освежити цео сајт сваких {0} секунди", + "Edit Maintenance": "Измени одржавање", + "statusPageMaintenanceEndDate": "Крај", + "ariaDeleteMaintenance": "Избриши ово заказано одржавање", + "deletedMonitorsMsg": "Избрисан {n} посматрач | Избрисано {n} посматрача", + "Custom Monitor Type": "Прилагођени тип посматрача", + "Single Maintenance Window": "Јединствен прозор одржавања", + "emailTemplateMonitorJSON": "објекат који описује посматрача", + "teamsEnableTagsDescription": "Ако је омогућено, порука ће садржати ознаке посматрача.", + "deleteMonitorsMsg": "Да ли заиста желите да избришете изабране посматраче?", + "deleteMaintenanceMsg": "Да ли заиста желите да избришете ово одржавање?", + "setup a new monitor group": "подесите нову групу посматрача", + "Monitors": "{n} посматрач | {n} посматрача", + "lastUpdatedAtFromNow": "Последње ажурирано: {date} ({fromNow})", + "deleteProxyMsg": "Да ли заиста желите да избришете овај прокси за све посматраче?", + "proxyDescription": "Проксији морају бити додељени посматрачу да би функционисали.", + "New Status Page": "Нова страница статуса", + "dateCreatedAtFromNow": "Датум креирања: {date} ({fromNow})", + "No Maintenance": "Нема одржавања", + "pauseMaintenanceMsg": "Да ли заиста желите да паузирате?", + "maintenanceStatus-under-maintenance": "Одржавање у току", + "maintenanceStatus-inactive": "Неактивно", + "maintenanceStatus-scheduled": "Заказано", + "Clear All Events": "Очисти све догађаје", + "noMonitorsSelectedWarning": "Правите одржавање без посматрача на које утиче. Да ли заста желите да наставите?", + "noMonitorsOrStatusPagesSelectedError": "Не можете направити одржавање без монитора на које утиче или странице статуса", + "Monitor Group": "Група посматрача", + "Badge Maintenance Color": "Боја беџа одржавања", + "rabbitmqNodesRequired": "Молим вас подесите чворове за овај посматрач.", + "Orange": "Наранџаста", + "pausedMonitorsMsg": "Паузиран {n} посматрач | Паузирано {n} посматрача", + "noMonitorsPausedMsg": "Ниједан посматрач паузиран (ниједан није био активан)", + "Show Clickable Link Description": "Ако је потврђено свако ко има приступ овој страници може да приступа адреси посматрача.", + "domain_expiry_unsupported_missing_target": "Није подешен исправан домен или име домаћина за овог посматрача", + "successAuthChangePassword": "Лозинка је успешно измењена.", + "lastUpdatedAt": "Последње ажурирано: {date}", + "Also check beta release": "Такође провери бета верзију", + "Font Twemoji by Twitter licensed under": "Фонт Twemoji од Twitter-а лиценциран са", + "Show update if available": "Прикажи нову верзију ако је доступна", + "Tags": "Ознаке", + "certificateExpiryNotificationHelp": "Број дана унапред може бити подешен у поставкама.", + "settingsCertificateExpiry": "Истек TLS сертификата", + "telegramSendSilentlyDescription": "Шаље поруку тихо. Корисници ће добити обавештење без звука.", + "Display Timezone": "Временска зона за приказ", + "Ignore TLS Error": "Игнориши TLS грешку", + "Authentication Method": "Начин аутентификације", + "telnyxApiKey": "API кључ (V2)", + "Group ID": "ID групе", + "Add API Key": "Додај API кључ", + "Expires": "Истиче", + "maxPing": "Макс пинг", + "minPing": "Мини пинг", + "serwersmsRecipientTypeGroup": "Група", + "New Group": "Нова група", + "Group Name": "Назив групе", + "equals": "једнако", + "conditionDeleteGroup": "Избриши групу", + "conditionValuePlaceholder": "Вредност", + "not ends with": "не завршава се са", + "Notification Channel": "Канал обавештења", + "Sound": "Звук", + "Clear Form": "Очисти формулар", + "Sort by certificate expiry": "Сложи према истеку сертификата", + "timeoutAfter": "Истиче након {0} секунди", + "saveResponseForNotifications": "Сачувајте HTTP одговор успеха за обавештења", + "styleElapsedTime": "Протекло време испод траке откуцаја срца", + "styleElapsedTimeShowNoLine": "Прикажи (без линије)", + "styleElapsedTimeShowWithLine": "Прикажи (са линијом)", + "Auto Get": "Добави аутоматски", + "Add Tags": "Додај ознаке", + "Entry Page": "Почетна страна", + "Add Group": "Додај групу", + "webhookBodyPresetOption": "Преподешено - {0}", + "webhookAdditionalHeadersTitle": "Додатна заглавља", + "Method": "Начин", + "webhookBodyCustomOption": "Прилагођено тело", + "Body": "Тело", + "BodyInvalidFormatBecause": "Тело захтева није исправан JSON зато што {error}", + "Headers": "Заглавља", + "HeadersInvalidFormatBecause": "Заглавље захтева није исправан JSON зато што {error}", + "Security": "Безбедност", + "Steam API Key": "Steam API кључ", + "auto-select": "Изабери аутоматски", + "Untitled Group": "Неименована група", + "Cancel": "Поништи", + "Authentication": "Аутентификација", + "HTTP Basic Auth": "HTTP основна аутентификација", + "Domain Name Expiry Notification": "Обавештење о истеку назива домена", + "RadiusCalledStationIdDescription": "Идентификатор позваног уређаја", + "Docker Container": "Докер контејнер", + "deleteDockerHostMsg": "Да ли заиста желите да избришете докер домаћина за све посматраче?", + "supportTelegramChatID": "Подржава директно ћаскање / групу / ID канала ћаскања", + "supportBaleChatID": "Подржава директно ћаскање / групу / ID канала ћаскања", + "sameAsServerTimezone": "Иста као временска зона сервера", + "cronScheduleDescription": "Заказано: {description}", + "warningTimezone": "Користи временску зону сервера", + "SMTP Security": "SMTP безбедност", + "Ignore STARTTLS": "Игнориши STARTTLS", + "Use STARTTLS": "Користи STARTTLS", + "secureOptionNone": "Ништа / STARTTLS (25, 587)", + "Disable STARTTLS": "Онемогући STARTTLS", + "Use HTML for custom E-mail body": "Користи HTML за прилагођено тело е-поруке", + "emailCustomBody": "Прилагођено тело", + "leave blank for default body": "оставите празно за подразумевано тело", + "smseagleGroup": "Називи групе именика", + "smspartnerApiurl": "Ваш API кључ можете да ронађете на вашој контролној табли код {0}", + "onebotGroupMessage": "Група", + "onebotUserOrGroupId": "Група/ID корисника", + "Expiry": "Истицање", + "Expiry date": "Датум истека", + "API Keys": "API кључеви", + "Don't expire": "Не истиче", + "apiKey-active": "Активан", + "No API Keys": "Нема API кључева", + "ntfyAuthenticationMethod": "Начин аутентификације", + "Request Body": "Тело захтева", + "evolutionRecipient": "Број телефона / ID контакта / ID групе", + "No tags found.": "Ознаке нису пронађене.", + "Go back to home page.": "Врати се на почетну страну.", + "whapiRecipient": "Број телефона / ID контакта / ID групе", + "conditionAdd": "Додај услов", + "conditionDelete": "Избриши услов", + "conditionAddGroup": "Додај групу", + "not equals": "није једнако", + "not starts with": "не почиње са", + "ends with": "завршава се са", + "less than": "мање од", + "greater than": "веће од", + "less than or equal to": "мање или једнако", + "greater than or equal to": "веће или једнако", + "360messengerGroupList": "WhatsApp групе", + "360messengerSelectGroupList": "Изаберите групу да додате", + "settingsDomainExpiry": "Истицање домена", + "avgPing": "Прос пинг", + "labelDomainNameExpiryNotification": "Обавештење о истеку назива домена", + "Collapse All Groups": "Скупи све групе", + "API Key": "API кључ", + "Certificate Expiry Notification": "Обавештење о истеку сертификата", + "RadiusCallingStationIdDescription": "Идентификатор уређаја који позива", + "Setup Docker Host": "Подеси докер домаћина", + "Docker Daemon": "Докер демон", + "noDockerHostMsg": "Није доступно. Прво подесите докер домаћина.", + "DockerHostRequired": "Молим вас подесите докер домаћина за овог посматрача.", + "Docker Host": "Докер домаћин", + "Docker Hosts": "Докер домаћини", + "telegramUseTemplateDescription": "Ако је омогућено, порука ће бити послата коришћењем прилагођеног шаблона.", + "Server Timezone": "Временска зона сервера", + "smtp": "Е-пошта (SMTP)", + "Mention group": "Помени {group}", + "HTTP Method": "HTTP начин", + "domainExpiryNotificationHelp": "Број дана унапред може бити подешен у поставкама.", + "matrixUseTemplateDescription": "Ако је омогућено, порука ће бити послата коришћењем прилагођеног шаблона.", + "maxUseTemplateDescription": "Ако је омогућено, порука ће бити послата коришћењем прилагођеног шаблона.", + "teamsEnableTags": "Уврсти ознаке", + "deleteGroupMsg": "Да ли заиста желите да избришете ову групу?", + "Free Mobile API Key": "Бесплатан API кључ за мобилне", + "signalImportant": "ВАЖНО: Не можете да мешате групе и бројеве у примаоцима!", + "serwersmsGroupId": "ID групе", + "apiKey-expired": "Истекао", + "Primary Base URL": "Примарна основна адреса", + "Resend Notification if Down X times consecutively": "Пошаљи обавештење ако није доступан Х пута узастопно", + "resendDisabled": "Слање онемогућено", + "saveErrorResponseForNotifications": "Сачувајте HTTP одговор грешке за обавештења", + "Two Factor Authentication": "Аутентификација у два корака", + "Show Tags": "Прикажи ознаке", + "Hide Tags": "Сакриј ознаке", + "Description": "Опис", + "Page Not Found": "Страница није пронађена", + "Message:": "Порука:", + "About": "О програму", + "Backup": "Резервна копија", + "Remove the expiry notification": "Уклони дан обавештења о истеку", + "Add a new expiry notification day": "Додај нови дан обавештења о истеку", + "pushyAPIKey": "Тајни API кључ", + "alertaApiKey": "API кључ", + "apiKeyAddedMsg": "Ваш API кључ је додат. Молим вас запишите га јер више никада неће бити приказан.", + "Body Encoding": "Енкодовање тела", + "apiKey-inactive": "Неактиван", + "disableAPIKeyMsg": "Да л заиста желите да онемогућите овај API кључ?", + "deleteAPIKeyMsg": "Да ли заиста желите да избришете овај API кључ?", + "Generate": "Генериши", + "twilioApiKey": "Api кључ (опционо)", + "Group": "Група", + "toastErrorTimeout": "Време чекања на обавештење о грешки", + "toastSuccessTimeout": "Време чекања на обавештење о успеху", + "showCertificateExpiry": "Прикажи истек сертификата", + "Conditions": "Услови", + "contains": "садржи", + "not contains": "не садржи", + "starts with": "почиње са", + "record": "запис", + "message": "порука", + "json_value": "JSON вредност", + "pingNumericLabel": "Нумерички испис", + "pingGlobalTimeoutLabel": "Глобално време чекања", + "Add Another Tag": "Додај додатну ознаку", + "labelDomainExpiry": "Истек домена", + "domain_expiry_unsupported_is_icann": "Домен \"{domain}\" није кандидат за посматрање истека домена, зато што његовим јавним суфиксом \".{publicSuffix}\" не управља ICANN", + "mtls-auth-server-key-placeholder": "Тело кључа", + "Expand All Groups": "Рашири све групе", + "HTTP Headers": "HTTP заглавља", + "Ip Family": "IP породица", + "GlobalpingIpFamilyInfo": "IP верзија за коришћење. Дозвољено само ако је мета назив домаћина.", + "Request Timeout": "Време чекања на захтев", + "mtls-auth-server-cert-placeholder": "Тело сертификата", + "Import Backup": "Извези резервну копију", + "notAvailableShort": "Н/Д", + "Export Backup": "Извези резервну копију", + "Default enabled": "Подразумевано омогућено", + "Inactive": "Неактивно", + "PasswordsDoNotMatch": "Лозинке се не поклапају.", + "records": "записи", + "topic": "Тема", + "Resolve": "Разлучи", + "Resolved": "Разлучено", + "Reset Token": "Поништи токен", + "Done": "Завршено", + "info": "информација", + "Heartbeat Retry Interval": "Интервал покушаја откуцаја", + "ignoreTLSErrorGeneral": "Игнориши TLS/SSL грешке за конекцију", + "pushOptionalParams": "Опциони параметри: {0}", + "alertNoFile": "Молим вас изаберите датотеку за увоз.", + "alertWrongFileType": "Молим вас изаберите JSON датотеку.", + "Clear all statistics": "Очистите сву статистику", + "Skip existing": "Прескочи постојеће", + "Overwrite": "Препиши", + "Keep both": "Задржи оба", + "filterActivePaused": "Паузирано", + "Active": "Активно", + "Token": "Токен", + "Show URI": "Прикажи адресу", + "Add New Tag": "Додај нову ознаку", + "Add New below or Select...": "Додај нову испод или изабери…", + "Tag with this name already exist.": "Ознака са овим називом већ постоји.", + "Tag with this value already exist.": "Ознака са овом вредношћу већ постоји.", + "tagAlreadyOnMonitor": "Ова ознака (назив и вредност) се већ посматра или чека на додавање.", + "tagNameExists": "Системска ознака са овим називом већ постоји. Изаберите га са списка или користите други назив.", + "Edit Incident": "Измени инцидент", + "Go to Dashboard": "Иди на контролну таблу", + "Required": "Неопходно", + "Post URL": "Адреса објаве", + "Content Type": "Тип садржаја", + "templateAvailableVariables": "Доступне променљиве", + "example": "Пример", + "Result": "Резултат", + "templateMsg": "порука обавештења", + "templateHeartbeatJSON": "објекат који описује откуцај", + "templateServiceName": "назив услуге", + "templateHostnameOrURL": "назив домаћина или адреса", + "templateStatus": "статус", + "Priority": "Приоритет", + "Read more": "Више информација", + "One record": "Један запис", + "descriptionHelpText": "Приказано на интерној конторлној табли. Маркдаун је дозвољен и очишћен (задржани су размаци и увлачења) пре приказа.", + "Game": "Игра", + "resendEveryXTimes": "Шаљи свака {0} пута", + "Only retry if status code check fails": "Шаљи само ако не успе провера кода статуса", + "pushOthers": "Остали", + "Resolver Server(s)": "Сервери разлучивања", + "2FA Settings": "Поставке А2К", + "filterActive": "Активно", + "Setup 2FA": "Подеси А2К", + "Enable 2FA": "Омогући А2К", + "Disable 2FA": "Онемогући А2К", + "All Systems Operational": "Сви системи су оперативни", + "No Services": "Нема услуге", + "Partially Degraded Service": "Делимично деградирана услуга", + "Degraded Service": "Деградирана услуга", + "Webhook URL": "Адреса веб закачке", + "Application Token": "Токен апликације", + "Server URL": "Адреса сервера", + "invalidURL": "Неисправна адреса", + "recent": "Недавно", + "Home": "Почетак", + "Verify Token": "Потврди токен", + "here": "овде", + "Info": "Информације", + "Add one": "Додај један", + "default": "Подразумевано", + "Please input title and content": "Молим вас унесите наслов и садржај", + "createdAt": "Направљено: {date}", + "Services": "Услуге", + "Discard": "Одбаци", + "Select": "Изабери", + "Actions": "Радње", + "Customize": "Прилагоди", + "Custom Footer": "Прилагођено подножје", + "Custom CSS": "Прилагођени CSS", + "deleteIncidentMsg": "Да ли заиста желите да избришете овај инцидент?", + "Proxies": "Проксији", + "Certificate Chain:": "Ланац сертификата:", + "Valid": "Исправно", + "Invalid": "Неисправно", + "User": "Корисник", + "Installed": "Инсталирано", + "Not installed": "Није инсталирано", + "Running": "Извршава се", + "Not running": "Не извршава се", + "Remove Token": "Уклони токен", + "Stop": "Заустави", + "Accept characters:": "Прихвати карактере:", + "Next": "Следеће", + "Start": "Почни", + "Post": "Објава", + "Switch to Light Theme": "Пребаци на светлу тему", + "Switch to Dark Theme": "Пребаци на тамну тему", + "enabled": "Омогућено", + "setAsDefault": "Подеси као подразумевано", + "Select message type": "Изаберите тип поруке", + "Query": "Упит", + "default: notify all devices": "подразумевано: обавести све уређаје", + "Enable": "Омогући", + "install": "Инсталирај", + "installing": "Инсталирање", + "uninstall": "Деинсталирај", + "wayToGetZohoCliqURL": "Можете научити како да направите адресу веб закачке {0}.", + "Channel access token": "Токен за приступ каналу", + "matrixUseTemplate": "Користи прилагођени шаблон поруке", + "Issuer:": "Издавач:", + "Fingerprint:": "Отисак прста:", + "Most likely causes:": "Највероватнији узроци:", + "startDateTime": "Датум/време почетка", + "Date and Time": "Датум и време", + "emailTemplateHeartbeatJSON": "објекат који описује откуцај", + "Don't know how to get the token? Please read the guide:": "Не знате како да добавите токен? Прочитајте ово упутство:", + "Valid To:": "Исправно до:", + "Days Remaining:": "Преостало дана:", + "What you can try:": "Шта можете да пробате:", + "Retype the address.": "Поново унесите адресу.", + "Go back to the previous page.": "Вратите се на претходну страну.", + "Container Name / ID": "Назив контејнера / ID", + "Bot Token": "Токен бота", + "wayToGetTelegramToken": "Токен можете добавити од {0}.", + "Chat ID": "ID ћаскања", + "Trigger type:": "Тип окидача:", + "Event type:": "Тип догађаја:", + "and": "и", + "or": "или", + "endDateTime": "Датум/време завршетка", + "cronExpression": "Крон израз", + "recurringInterval": "Интервал", + "Recurring": "Понављајући", + "weekdayShortMon": "Пон", + "weekdayShortTue": "Уто", + "IconUrl": "Адреса иконе", + "Enable DNS Cache": "(Застарело) Омогући DNS кеш за HTTP(s) посматраче", + "Disable": "Онемогући", + "uninstalling": "Деинсталирање", + "notificationIncidentManagement": "Управљање инцидентом", + "notificationOther": "Остале интеграције", + "emailCustomSubject": "Прилагођена тема", + "leave blank for default subject": "оставите празно за подразумевану тему", + "To Email": "За е-пошта", + "Bot Display Name": "Назив за приказ бота", + "wayToGetTeamsURL": "Можете научити како да направите адресу веб закачке {0}.", + "plugin": "Прикључак | Прикључци", + "resumedMonitorsMsg": "Настављен {n} посматрач | Настављено {n} посматрача", + "notificationRegional": "Регионални", + "Other Software": "Други софтвер", + "For example: nginx, Apache and Traefik.": "На пример: nginx, Apache и Traefik.", + "Please input content": "Молим вас унесите садржај", + "API Username": "API корисничко име", + "API Token": "API токен", + "Connection String": "Ниска конекције", + "There might be a typing error in the address.": "Можда постоји грешка куцања адресе.", + "Connection Type": "Тип конекције", + "telegramProtectContent": "Заштитите прослеђивање/чување", + "telegramProtectContentDescription": "Ако је омогућено, поруке бота у Телеграму ће бити заштићене од прослеђивања и чувања.", + "telegramUseTemplate": "Користи прилагођени шаблон поруке", + "weekdayShortWed": "Сре", + "weekdayShortThu": "Чет", + "weekdayShortFri": "Пет", + "Effective Date Range": "Ефективни опсег датума (опционо)", + "Notification Action": "Радња обавештења", + "maxUseTemplate": "Користи прилагођени шаблон поруке", + "Duration (Minutes)": "Трајање (минута)", + "invalidCronExpression": "Неисправан израз крона: {0}", + "chromeExecutableAutoDetect": "Откриј аутоматски", + "confirmUninstallPlugin": "Да ли заиста желите да деинсталирате прикључак?", + "notificationChatPlatforms": "Платформе за ћаскање", + "notificationSmsServices": "SMS сервиси", + "notificationEmail": "Е-пошта", + "From Email": "Од е-пошта", + "emailCustomisableContent": "Прилагодљиви садржај", + "emailTemplateMsg": "порука обавештења", + "Send to channel": "Пошаљи у канал", + "forumPostName": "Назив објаве форума", + "threadForumPostID": "Разговор / ID објаве форума", + "Create new forum post": "Направи нову објаву на форуму", + "postToExistingThread": "Објави у постојећем разговору / објави на форуму", + "e.g. {discordThreadID}": "нпр. {discordThreadID}", + "Event data:": "Подаци догађаја:", + "Basic Settings": "Основне поставке", + "enableDefaultNotificationDescription": "Ово обавештење ће подразумевано бити омогућено за нове посматраче. Ви можете да појединачно онемогућите обавештење за сваки посматрач.", + "notificationUniversal": "Универзални", + "Icon URL": "Адреса иконе", + "dayOfMonth": "Дан у месецу", + "weekdayShortSat": "Суб", + "lastDay": "Последњи дан", + "lastDay1": "Последњи дан у месецу", + "weekdayShortSun": "Нед", + "dayOfWeek": "Дан у седмици", + "No Proxy": "Без проксија", + "Please input title": "Молим вас унесите наслов", + "Please read": "Молим вас прочитајте", + "Subject:": "Тема:", + "The resource is no longer available.": "Ресурс више није доступан.", + "Maintenance Time Window of a Day": "Временски оквир дана за одржавање", + "Prefix Custom Message": "Префикс прилагођене поруке", + "pushoversounds bike": "Бицикла", + "pushyToken": "Токен уређаја", + "pushoversounds intermission": "Интермисија", + "Message Title": "Наслов поруке", + "Could not clear events": "Није се могло очистити {failed}/{total} догађаја", + "recurringIntervalMessage": "Покрени једном сваког дана | Покрени једном свака {0} дана", + "Copy to Clipboard": "Умножи на међуспремник", + "SMS Type": "Тип SMS-а", + "aliyun-template-optional-parameters": "Опциони параметри: {parameters}", + "pushoversounds spacealarm": "Свемирски аларм", + "pushoversounds tugboat": "Реморкер", + "Copied to clipboard!": "Умножено на међуспремник!", + "apiCredentials": "API креденцијали", + "Mention Mobile List": "Помени мобилни списак", + "Platform": "Платформа", + "Proxy Server": "Прокси сервер", + "confirmClearStatisticsMsg": "Да ли заиста желите да избришете СВУ статистику?", + "confirmEnableTwoFAMsg": "Да ли заиста желите да омогућите А2К?", + "twoFAVerifyLabel": "Унесите ваш токен да потврдите А2К:", + "tokenValidSettingsMsg": "Токен је исправан! Сада можете да сачувате А2К поставке.", + "Sets end time based on start time": "Поставља време завршетка према времену почетка", + "promosmsPassword": "API лозинка", + "pushoversounds alien": "Ванземаљски аларм (дугачак)", + "Failed to copy to clipboard": "Неуспело умножавање на међуспремник", + "User Key": "Кључ корисника", + "For safety, must use secret key": "Због безбедности, морате користити тајни кључ", + "Dingtalk Mobile List": "Мобилни списак", + "Dingtalk User List": "Списак ID корисника", + "Topic": "Тема", + "promosmsAllowLongSMS": "Дозволи дугачке SMS-ове", + "Channel Name": "Назив канала", + "Notify Channel": "Обавести канал", + "Add a domain": "Додај домен", + "Remove domain": "Уклони домен '{0}'", + "promosmsLogin": "API име за пријаву", + "pushoversounds updown": "Горе доле (дугачак)", + "pushoversounds vibrate": "Само вибрирање", + "pushoversounds none": "Ништа (тихо)", + "Notification Sound": "Звук обавештења", + "Gateway Type": "Тип мрежног пролаза", + "Base URL": "Коренска адреса", + "Enter a list of mobile": "Унесите мобилни списак", + "Invalid mobile": "Неисправан мобилни [{mobile}]", + "Device Token": "Токен уређаја", + "Events cleared successfully": "Догађаји су успешно очишћени.", + "confirmDisableTwoFAMsg": "Да ли заиста желите да онемогућите А2К?", + "Please set start time first": "Молим вас прво поставите време почетка", + "pushoversounds cashregister": "Каса", + "pushoversounds classical": "Класично", + "pushoversounds cosmic": "Космички", + "pushoversounds incoming": "Долазно", + "pushoversounds magic": "Магија", + "pushoversounds mechanical": "Механичко", + "pushoversounds siren": "Сирена", + "pushoversounds climb": "Пењање (дугачак)", + "pushoversounds persistent": "Истрајан (дугачак)", + "Device": "Уређај", + "Proxy server has authentication": "Прокси сервер има аутентификацију", + "pushoversounds falling": "Пад", + "Mentioning": "Помињање", + "Don't mention people": "Не помињи људе", + "Proxy Protocol": "Прокси протокол", + "Sort by name": "Сложи према називу", + "VKTeams Use Template Description": "Ако је омогућено, порука ће бити послата користећи прилагођени шаблон.", + "mariadbCaCertificateHelptext": "Налепите CA сертификат у PEM формату да би сте користили самопотписане сертификате. Оставите празно ако ваша база користи сертификат потписан од стране јавног CA.", + "smtpDkimDomain": "Назив домена", + "TLS Alert Spec": "RFC 8446", + "telnyxPhoneNumber": "Од број", + "telnyxToNumber": "За број", + "threemaRecipientTypeEmail": "Адреса е-поште", + "Edit Tag": "Измени ознаку", + "pagertreeUrgency": "Ургентност", + "Badge Color": "Боја беџа", + "Badge Label Prefix": "Префикс ознаке беџа", + "Badge Preview": "Преглед беџа", + "Badge Label Suffix": "Суфикс ознаке беџа", + "Kafka Topic Name": "Назив Kafka теме", + "authInvalidToken": "Неисправан токен.", + "successDisabled": "Успешно онемогућено.", + "foundChromiumVersion": "Пронађен је Chromium/Chrome. Верзија: {0}", + "Maximum Retries": "Максимално покушаја", + "Recipient Numbers": "Број примаоца", + "SSL/TLS": "SSL/TLS", + "signalUseTemplate": "Користи прилагођени шаблон поруке", + "Jira Service Management": "Управљање Jira сервисом", + "do nothing": "не чини ништа", + "smseagleMsgType": "Тип поруке", + "smseagleApiType": "API верзија", + "smspartnerPhoneNumber": "Бројеви телефона", + "Authorization Identity": "Идентитет ауторизације", + "authUserInactiveOrDeleted": "Овај корисник није активан или је избрисан.", + "2faEnabled": "А2К је омогућена.", + "2faDisabled": "А2К је онемогућена.", + "successAdded": "Успешно додато.", + "successResumed": "Успешно настављено.", + "2faAlreadyEnabled": "А2К је већ омогућена.", + "successPaused": "Успешно паузирано.", + "successDeleted": "Успешно избрисано.", + "successEdited": "Успешно измењено.", + "successBackupRestored": "Копија успешно повраћена.", + "password": "Лозинка", + "lunaseaTarget": "Мета", + "Pick a SASL Mechanism...": "Одаберите SASL механизам…", + "Condition": "Услов", + "SNMP Version": "SNMP верзија", + "Arcade": "Аркада", + "Flute": "Флаута", + "Money": "Новац", + "Scifi": "Научна фантастика", + "From": "Од", + "halopsa_field_uptime_kuma_version": "Uptime Kuma број верзије", + "360messengerMessageTemplate": "Шаблон поруке", + "Manual": "Ручно", + "Message Format": "Формат поруке", + "To Number": "За број", + "teltonikaUsername": "API корисничко име", + "discordMessageFormatCustom": "Прилагођени шаблон", + "Location": "Локација", + "oracledbConnectionString": "Oracle база података: {connectionString}", + "markdownSupported": "Маркдаун синтакс је подржана. Ако користите HTML, избегавајте размаке на почетку да би сте избегли проблеме са форматирањем.", + "Invert Keyword": "Обрнута кључна реч", + "Json Query Expression": "Израз Json упита", + "locally configured mail transfer agent": "локално подешен агент за слање е-порука", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Или унесите назив домаћина сервера са којим желите да се повежете, или {localhost} ако намеравате да користите {local_mta}", + "retryOnlyOnStatusCodeFailureDescription": "Ако је омогућено, поновни покушаји ће се вршити када не успе провера кода HTTP статуса (нпр. сервер није на мрежи). Ако провера кода статуса прође али JSON упит не успе, посматрач је моментално означен као неактиван без поновних покушаја.", + "Ignore Sec-WebSocket-Accept header": "Игнориши {0} заглавље", + "evolutionCustomMessageTitle": "Прилагођена порука (опционо)", + "Destination": "Одредиште", + "slackUseTemplate": "Користи прилагођени шаблон поруке", + "smseagleContact": "Називи контаката именика", + "onebotPrivateMessage": "Приватно", + "Analytics Type": "Тип аналитике", + "Analytics ID": "ID аналитике", + "Analytics Script URL": "Адреса скрипте аналитике", + "Continue": "Настави", + "Add Another": "Додај други", + "pagertreeSilent": "Тихо", + "lunaseaDeviceID": "ID уређаја", + "lunaseaUserID": "ID корисника", + "twilioToNumber": "За број", + "Badge Suffix": "Суфикс вредности беџа", + "Badge Label Color": "Боја ознаке беџа", + "Badge Pending Color": "Боја беџа на чекању", + "Badge URL": "Адреса беџа", + "Kafka Producer Message": "Порука Kafka продуцента", + "Enable Kafka SSL": "Омогући Kafka SSL", + "Mechanism": "Механизам", + "Close": "Затвори", + "Message format": "Формат поруке", + "max 11 alphanumeric characters": "макс 11 алфанумеричких карактера", + "Select All": "Изабери све", + "Message Template": "Шаблон поруке", + "Template Format": "Формат шаблона", + "discordUseMessageTemplate": "Користи прилагођени шаблон поруке", + "Browser not supported": "Прегледач није подржан", + "Metadata": "Мета подаци", + "teltonikaPassword": "API лозинка", + "gamedigToken": "Токен", + "gamedigTokenDescription": "Опционо. Неки сервери захтевају токен за ауторизацију.", + "systemServiceCommandHint": "Команда коришћена: {command}", + "Command": "Команда", + "receiverSevenIO": "Број примаоца", + "senderSevenIO": "Број или назив пошиљаоца", + "evolutionInstanceName": "Назив инстанце", + "evolutionCustomMessageDesc": "Оставите празно да користите подразумевану поруку.", + "max 15 digits": "макс 15 цифара", + "threemaRecipient": "Прималац", + "threemaRecipientType": "Тип примаоца", + "threemaRecipientTypePhone": "Број телефона", + "Recipient Type": "Тип примаоца", + "Authorization Header": "Заглавље ауторизације", + "Correct": "Исправно", + "Deselect All": "Изабери ништа", + "Elevator": "Лифт", + "Pop": "Поп", + "Custom sound to override default notification sound": "Прилагођени звук да преради подразумевани звук обавештења", + "Can be found on:": "Може бити пронађено на: {0}", + "brevoToEmail": "За е-пошта", + "brevoCcEmail": "CC е-пошта", + "brevoBccEmail": "BCC е-пошта", + "brevoSubject": "Тема", + "brevoLeaveBlankForDefaultSubject": "оставите празно за подразумевану тему", + "resendToEmail": "За е-пошту", + "resendFromName": "Од име", + "resendFromEmail": "Од е-пошта", + "resendLeaveBlankForDefaultName": "оставите празно за подразумевано име", + "discordMessageFormat": "Формат поруке", + "discordMessageTemplate": "Шаблон поруке", + "Nextcloud host": "Nextcloud домаћин", + "Conversation token": "Токен разговора", + "Protocol": "Протокол", + "account settings": "поставке налога", + "Clear current filters": "Очисти тренутне филтере", + "Sort options": "Опције слагања", + "Sort by status": "Сложи према статусу", + "Json Query": "Json упит", + "System Service": "Системски сервис", + "Expected TLS Alert": "Очекивано TLS упозорење", + "fluxerMessageFormat": "Формат поруке", + "fluxerUseMessageTemplate": "Користи прилагођени шаблон поруке", + "fluxerMessageFormatCustom": "Прилагођени шаблон", + "fluxerMessageTemplate": "Шаблон поруке", + "Never": "Никада", + "teltonikaUnsafeTls": "Игнориши исправност сертификата", + "Region": "Регија", + "Key Added": "Кључ је додат", + "systemService": "Системски сервис", + "systemServiceName": "Назив сервиса", + "systemServiceDescription": "Проверава да ли је системски сервис {service_name} активан", + "To Phone Number": "За број телефона", + "Telephone number": "Број телефона", + "domainExpiryDescription": "Окини обавештење када домен истекне у:", + "Google": "Google", + "Integration Key": "Кључ интеграције", + "SpugPush Template Code": "Код шаблона", + "Server Address": "Адреса сервера", + "monitorToastMessagesDescription": "Тост обавештења за посматраче нестају након датог времена у секундама. Поставите на -1 да онемогућите истицање времена. Поставите на 0 да онемогућите тост обавештења.", + "Session Token": "Токен сесије", + "Originator": "Оригинатор", + "snmpV3Username": "SNMPv3 корисничко име", + "Private Number": "Приватни број", + "resendSubject": "Тема", + "pingCountLabel": "Макс пакета", + "Notifications Enabled": "Обавештења су омогућена", + "Allow Notifications": "Дозволи обавештења", + "halopsa_field_message": "Цела порука упозорења са статусом и детаљима", + "VKTeams Use Template": "Користи шаблон прилагођене поруке", + "smtpDkimSettings": "DKIM поставке", + "smtpDkimKeySelector": "Бирач кључа", + "smtpDkimPrivateKey": "Приватни кључ", + "smtpDkimHashAlgo": "Хеш алгоритам (опционо)", + "Integration URL": "Адреса интеграције", + "alertaEnvironment": "Окружење", + "serwersmsRecipientType": "Тип примаоца", + "serwersmsRecipientTypePhone": "Број телефона", + "smseagleRecipientType": "Тип примаоца", + "smseagleApiv2": "APIv2 (препоручено за нове интеграције)", + "Badge Style": "Стил беџа", + "noOrBadCertificate": "Нема/лош сертификат", + "Saved.": "Сачувано.", + "threemaRecipientTypeIdentityFormat": "8 карактера", + "setupDatabaseEmbeddedMariaDB": "Не морате ништа да подешавате. Ова Docker слика има уграђену и аутоматски подешену MariaDB за вас. Uptime Kuma ће се пвезати на ову базу података путем Unix сокета.", + "setupDatabaseMariaDB": "Повежите се на екстерну MariaDB базу података. Морате да унесете информације за повезивање са базом.", + "setupDatabaseSQLite": "Једноставна база података у датотеци, препоручена за мала окружења. Пре v2.0.0, Uptime Kuma је користила SQLite као подразумевану базу података.", + "mariadbUseSSLHelptext": "Омогућите да би сте користили енкриптовану конекцију са вашом базом. Неопходно за већину база података у облаку.", + "Cannot connect to the socket server": "Није могуће повезати се са сокет сервером", + "ignoredTLSError": "TLS/SSL грешке су игнорисане", + "alertaAlertState": "Статус упозорења", + "alertaRecoverState": "Статус опоравка", + "serwersmsAPIPassword": "API лозинка", + "serwersmsPhoneNumber": "Број телефона", + "smseagleDuration": "Трајање (у секундама)", + "smseagleMsgSms": "Sms порука (подразумевано)", + "pagertreeLow": "Ниска", + "pagertreeMedium": "Средња", + "pagertreeHigh": "Висока", + "pagertreeCritical": "Критична", + "twilioFromNumber": "Од број", + "ntfyUseTemplate": "Прилагоди шаблон обавештења", + "Badge Type": "Тип беџа", + "Badge Duration (in hours)": "Трајање беџа (у сатима)", + "monitorToastMessagesLabel": "Тост обавештења посматрача", + "Kafka Brokers": "Kafka брокери", + "Enter the list of brokers": "Унесите списак брокера", + "Press Enter to add broker": "Притисните Enter да додате брокера", + "showOnlyLastHeartbeat": "Прикажи само последњи откуцај", + "systemServiceExpectedOutput": "Очекивани испис: \"{0}\"", + "Fail": "Неуспех", + "Reveal": "Откриће", + "Bubble": "Мехур", + "Harp": "Харфа", + "Guitar": "Гитара", + "360messengerEnableCustomMessage": "Унесите шаблон прилагођене поруке уместо подразумеване поруке.", + "Plain Text": "Обичан текст", + "Phone numbers": "Бројеви телефона", + "Sender name": "Име пошиљаоца", + "username": "Корисничко име", + "Sort by uptime": "Сложи према доступношћу", + "GRPC Options": "GRPC опције", + "Details": "Детаљи", + "teltonikaPhoneNumber": "Број телефона", + "Recipient Number": "Број примаоца", + "pagertreeIntegrationUrl": "Адреса интеграције", + "pagertreeDoNothing": "Не чини ништа", + "Badge Label": "Ознака беџа", + "Badge Prefix": "Префикс вредности беџа", + "authIncorrectCreds": "Нетачно кориснико име или лозинка.", + "tagNotFound": "Ознака није пронађена.", + "successEnabled": "Успешно омогућено.", + "Doorbell": "Улазно звоно", + "Clear": "Очисти", + "brevoSeparateMultipleEmails": "Одвојите вишеструко адреса е-поште са зарезима", + "wahaSession": "Сесија", + "360messengerCustomMessageTemplate": "Прилагођени шаблон поруке", + "Template ID": "ID шаблона", + "Check Type": "Тип провере", + "Service Name": "Назив сервиса", + "TLS Alerts": "TLS упозорења", + "ntfyCall": "Телефонски позив", + "Connection Timeout": "Време истека конекције", + "brevoFromEmail": "Од е-пошта", + "brevoFromName": "Од име", + "brevoLeaveBlankForDefaultName": "оставите празно за подразумевано име", + "smsplanetNeedToApproveName": "Мора бити одобрено у панелу клијента", + "Bot secret": "Тајна бота", + "Custom URL": "Прилагођена адреса", + "mtls-auth-server-cert-label": "Сертификат", + "resendLeaveBlankForDefaultSubject": "Оставите празно за подразумевану тему", + "pause": "Паузирај", + "Examples:": "Примери: {0}", + "smtpAdditionalHeadersTitle": "Додатна заглавља", + "Open your Google Spreadsheet": "Отворите ваше Google Табеле", + "alertaApiEndpoint": "API крајње тачке", + "From Name/Number": "Од име/број", + "FlashDuty Push URL": "Адреса пуша", + "enableGRPCTls": "Омогући слање gRPC захтева путем TLS конекције", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Поставите 'Изврши као: Ја' и 'Ко има приступ: Свако'", + "telnyxToNumberHelptext": "Одредишни број телефона за пријем SMS обавештења (нпр. +38160123456)", + "tailscalePingWarning": "Да би сте користили Tailscale Ping посматрач, морате да инсталирате Uptime Kuma без докера и такође инсталирати Tailscale клијента на вашем серверу.", + "RadiusCallingStationId": "ИД станице која позива", + "ntfyCustomMessage": "Шаблон прилагођене поруке", + "FlashDuty Severity": "Озбиљност", + "defaultNotificationName": "Моје {notification} упозорење ({number})", + "mqttWebSocketPath": "MQTT веб-сокет путања", + "topicExplanation": "MQTT тема за посматрање", + "mqttWebsocketPathInvalid": "Молим вас користите исправан формат веб-сокет путање", + "mqttWebsocketPathExplanation": "Веб-сокет путања за MQTT преко веб-сокет конекција (нпр. /mqtt)", + "wildcardOnlyForDNS": "Називи домаћина са заменским карактером су подржани само за DNS посматраче.", + "invalidDNSHostname": "Неисправан назив домаћина. Назив домаћина мора бити исправно FQDN. Може бити заменски карактер, може садржати доњу црту или се завршити са тачком.", + "telegramServerUrl": "(опционо) Адреса сервера", + "aboutWebhooks": "Више информација о веб-закачкама на: {0}", + "aboutJiraCloudId": "Више информација о Jira Cloud ID: {0}", + "Powered by": "Омогућено са", + "RadiusCalledStationId": "ИД позване станице", + "ignoreSecWebsocketAcceptHeaderDescription": "Омогућава серверу да не одговори са Sec-WebSocket-Accept заглављем, ако успе надоградња веб-сокета.", + "wsSubprotocolDescription": "Унесите списак подпротокола раздвојених зарезом. За више информација о њима, консултујте {documentation}", + "wsCodeDescription": "За више информација и статусним кодовима, консултујте {rfc6455}", + "Subprotocol(s)": "Подпротоколи", + "pushViewCode": "Како користити посматрач слања? (погледај код)", + "tagAlreadyStaged": "Ова ознака (назив и вредност) је већ припремљена за овај скуп.", + "webhookJsonDesc": "{0} је добар за све модерне HTTP сервере попут Express.js-а", + "templateLimitedToUpDownNotifications": "доступно само за обавештења АКТИВНО/НЕАКТИВНО", + "webhookFormDataDesc": "{multipart} је добар за PHP. JSON ће се морати испарсирати са {decodeFunction}", + "liquidIntroduction": "Шаблонство се постиже помоћу Liquid језика за шаблоне. Проверите {0} за упутство за коришћење.", + "templateLimitedToUpDownCertNotifications": "доступно само за обавештења АКТИВНО/НЕАКТИВНО/Истек сертификата", + "webhookAdditionalHeadersDesc": "Поставља додатна заглавља послата са веб закачком. Свако заглавље треба бити дефинисано ка JSON кључ/вредност.", + "appriseInstalled": "Apprise је инсталиран.", + "appriseNotInstalled": "Apprise није инсталиран. {0}", + "hostnameCannotBeIP": "Назив домаћина у DNS-у не може бити IP. Да ли сте хтели да користите поље разрешивача?", + "invalidHostnameOrIP": "Неисправан назив домаћина или IP. Назив домаћина мора бити исправно FQDN. Не може се користити заменски карактер. Може садржати доњу црту или се завршити са тачком.", + "mqttHostnameTip": "Молим вас користите овај формат {hostnameFormat}", + "Shrink Database": "Скупи базу података", + "Check/Uncheck": "Укључи/искључи", + "startOrEndWithOnly": "Почни или заврши само са {0}", + "No consecutive dashes": "Без повезаних црта", + "Trust Proxy": "Прокси поверења", + "signedInDispDisabled": "Ауторизација је онемогућена", + "RadiusSecret": "Radius тајна", + "RadiusSecretDescription": "Подељена тајна између клијента и сервера", + "Check how to config it for WebSocket": "Проверите како да га подесите за веб-прокси", + "Using a Reverse Proxy?": "Користите повратни прокси?", + "Steam Game Server": "Steam Game сервер", + "sipsakPingWarning": "Да би сте користили SIP Options Ping посматрач, морате да инсталирате Uptime Kuma без докера и такође инсталирати Sipsak клијента на вашем серверу.", + "telegramMessageThreadIDDescription": "Опциони јединствени идентификатор за циљни разговор (тему) на форуму; само за супергрупе са форума", + "disableCloudflaredNoAuthMsg": "У режиму сте Без аутентификације, лозинка није неопходна.", + "wayToGetLineNotifyToken": "Можете добити токен за приступ од {0}", + "YOUR BOT TOKEN HERE": "ТОКЕН ВАШЕГ БОТА ИДЕ ОВДЕ", + "chatIDNotFound": "ИД ћаскања није пронађен; прво пошаљите поруку овом боту", + "strategyManual": "Ручно активно/неактивно", + "lastDay4": "4. дан од краја месеца", + "lastDay3": "3. дан од краја месеца", + "chromeExecutable": "Chrome/Chromium извршна датотека", + "loadingError": "Не могу се добавити подаци, покушајте поново касније.", + "smtpLiquidIntroduction": "Следећа два поља се могу шаблонизовати са Liquid језиком за шаблоне. Прочитајте {0} за упутство о коришћењу. Ово су доступне променљиве:", + "wayToGetDiscordURL": "Ово можете добити одласком на Поставке сервера -> Интеграције -> Погледај веб-закачке -> Нова веб-закачка", + "Discord Webhook URL": "Адреса Discord веб-закачке", + "Channel access token (Long-lived)": "Токен за приступ каналу (дуговечни)", + "wayToGetDiscordThreadId": "Добављање ИД-ја разговора / објаве на форуму је слично као и за ИД канала. Прочитајте више о томе како да добавите ИД-јеве {0}", + "backupDescription": "Можете направити резервну копију свих посматрача и обавештења у JSON датотеци.", + "Google Apps Script Code": "Код Google Apps скрипте", + "Click Deploy → New deployment → Web app": "Кликните на Отпреми → Ново отпремање → Веб апликација", + "Copy the web app URL and paste it above": "Умножите адресу веб апликације и налепите је овде", + "smseagleTo": "Бројеви телефона", + "Leave blank to use a shared sender number.": "Оставите празно да користите дељени број пошиљаоца.", + "ntfyNotificationTemplateFallback": "Оставите празно да користите подразумевани формат Uptime Kuma-е", + "ntfyCustomTitle": "Шаблон прилагођеног наслова", + "Kafka SASL Options": "Kafka SASL опције", + "infiniteRetention": "Поставите 0 за бесконачно задржавање.", + "confirmDeleteTagMsg": "Да ли заиста желите да обришете ову ознаку? Посматрачи повезани са њом неће бити обрисани.", + "Uptime Kuma URL": "Адреса Uptime Kuma-е", + "ntfyUsernameAndPassword": "Корисничко име и лозинка", + "socket": "Сокет", + "Bearer Token": "Токен носача", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Тренутна конекција се може прекинути ако сте тренутно повезани са Cloudflare тунелом. Да ли желите га зауставите? Унесите тренутну лозинку да потврдите.", + "Show Powered By": "Прикажи Омогућено са", + "Cloud ID": "ИД облака", + "certificationExpiryDescription": "HTTPS посматрачи окидају обавештење када TLS сертификати истичу за:", + "certHostnameMismatch": "Назив домаћина у сертификату се не подудара са адресом посматрача.", + "telegramTemplateFormatDescription": "Телеграм дозвољава коришћење разних маркап језика за поруке. Погледајте Телеграмов {0} за више детаља.", + "wayToCheckSignalURL": "Можете посетити ову адресу да би сте видели како да подесите:", + "clearHeartbeatsMsg": "Да ли заиста желите да обришете све откуцаје за овог посматрача? Ово трајно уклања све податке откуцаја и не може се поништити.", + "wayToGetKookGuildID": "Укључите 'Режим за програмере' у Kook поставкама, и кликните десним тастером на еснаф да добијете његов ID", + "wayToGetKookBotToken": "Направите апликацију и добавите токен за вашег бота на {0}", + "OptionalParameters": "Опциони параметри", + "Retry": "Покушај поново", + "trustProxyDescription": "Веруј 'X-Forwarded-*' заглављима. Ако желите да добијете исправну IP клијента и ваша Uptime Kuma је иза проксија попут Nginx-а или Apache-а, требало би да укључите ово.", + "wayToGetBaleToken": "Токен можете добити од {0}.", + "Long-Lived Access Token": "Дуговечни токен за приступ", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Дуговечни токен за приступ може бити направљен кликом на име вашег профила (доле лево) и одласком на дно и кликом на Направи токен.", + "frontendVersionIs": "Верзија фронтенда: {version}", + "Frontend Version do not match backend version!": "Верзија фронтенда се не подудара са верзијом бекенда!", + "chromeExecutableDescription": "За докер кориснике, ако Chromium није инсталиран, може потрајати неколико минута да се инсталира и прикажу резултати пробе. Он заузима 1GB простора на диску.", + "dnsCacheDescription": "Можда неће радити у неким IPv6 окружењима, онемогућите ако наиђете на проблеме.", + "notificationPushServices": "Пуш сервиси", + "emailTemplateLimitedToUpDownNotification": "доступно само за АКТИВНО/НЕАКТИВНО откуцаје, за остало је празно", + "whatHappensAtForumPost": "Направите нову објаву на форуму. Ово НЕ објављује поруке у постојећој објави. Да објавите у постојећој објави користите \"{option}\"", + "dataRetentionTimeError": "Период задржавања мора бити 0 или веће", + "clearEventsMsg": "Да ли заиста желите да обришете све догађаје за овог посматрача? Ово трајно уклања повезане податке порука и не може се поништити.", + "importHandleDescription": "Изаберите 'Прескочи постојеће' ако желите да прескочите сваког посматрача или обавештење са истим називом. 'Препиши' ће обрисати сваког постојећег посматрача и обавештење.", + "backupDescription3": "Осетљиви подаци попут токена обавештења су уврштени у датотеци извоза; чувајте извоз на безбедном месту.", + "octopushAPIKey": "\"API кључ\" из HTTP API креденцијала у контролној табли", + "Paste the script code (see below)": "Налепите код скрипте (види испод)", + "Quick Setup Guide": "Водич за брзо подешавање", + "Go to Extensions → Apps Script": "Идите на Додаци → Apps скрипта", + "Guild ID": "ID еснафа", + "Lowcost": "Јефтино", + "Setup Proxy": "Подеси прокси", + "Icon Emoji": "Икона емотикона", + "documentation": "документација", + "ntfyPriorityHelptextAllEvents": "Сви догађаји се шаљу са максималним приоритетом", + "saveResponseDescription": "Складишти HTTP одговор и чини га доступним шаблонима обавештења као {templateVariable}", + "responseMaxLengthDescription": "Максимална величина одговора за складиштење. Поставите на 0 за неограничено. Већи одговори ће бити скраћени. Подразумевано: 1024 (1KB)", + "responseMaxLength": "Максимална дужина одговора (бајтови)", + "successKeyword": "Кључна реч за успех", + "successKeywordExplanation": "MQTT кључна реч која ће се посматрати као успех", + "cloudflareWebsite": "Cloudflare веб-сајт", + "dnsPortDescription": "Порт DNS сервера. Подразумевано 53. Порт можете променити у било ком тренутку.", + "invertKeywordDescription": "Гледа да кључна реч није присутна уместо да јесте.", + "More info on:": "Више информација на: {0}", + "pushoverDesc2": "Ако желите да пошаљете обавештење на друге уређаје, попуните поље Уређај.", + "pushoverDesc1": "Приоритет нужде (2) има подразумевано време чекања од 30 секунди између понављања и истиче након 1 сата.", + "You can divide numbers with commas or semicolons": "Бројеве можете одвојити са {comma} или {semicolon}", + "aboutSlackUsername": "Мења име за приказ пошиљоца. Ако желите да споменете некога, уместо тога га уврстите у пријатељско име.", + "noMonitorsResumedMsg": "Ниједан посматрач наставио (ниједан није био активан)", + "clearAllEventsMsg": "Да ли заиста желите да обришете све догађаје за све посматраче? Ово трајно уклања повезане податке порука и не може се поништити.", + "bulkDeleteErrorMsg": "Није успело брисање {n} посматрача | Неуспело брисање {n} посматрача", + "confirmImportMsg": "Да ли заиста желите да увезете резервну копију? Потврдите да сте изабрали исправну опцију за увоз.", + "backupDescription2": "Напомена: историја и подаци о догађајима нису укључени.", + "jsonQueryDescription": "Парсирај и извуци одређене податке из JSON одговора сервера користећи JSON упит или користите \"$\" за сирови одговор, ако не очекује JSON. Резултат се упоређује са очекиваном вредношћу, као ниска. Погледајте {0} за документацију и користите {1} да експериментишете са упитима.", + "octopushLogin": "\"Пријава\" из HTTP API креденцијала у контролној табли", + "pushoversounds pianobar": "Клавијатура", + "see Jira Cloud Docs": "погледајте Jira Cloud документацију", + "pingPacketSizeWarning": "Пакет величине мањи од 16 не производи повратне токове на Linux-у", + "smspartnerSenderName": "Назив SMS пошиљаоца", + "Push URL": "Адреса за слање", + "needPushEvery": "Требате позивати ову адресу сваких {0} секунди.", + "PushUrl": "Адреса за слање", + "shrinkDatabaseDescriptionSqlite": "Покрени {vacuum} базе података за SQLite. {auto_vacuum} је већ омогућено али ово не дефрагментише базу података нити препакује појединачне стране базе на начин на који то чини {vacuum} команда.", + "Pick Accepted Status Codes...": "Одаберите прихватљиве кодове статуса…", + "enableProxyDescription": "Овај прокси неће утицати на захтеве посматрача док се не активира. Можете контролисати привремено онемогућавање проксија за све посматраче са статусом активирања.", + "setAsDefaultProxyDescription": "Овај прокси ће подразумевано бити омогућен за нове посматраче. Можете га онемогућити одвојено за сваког посматрача.", + "See Jira Cloud Docs": "Погледајте Jira Cloud документацију", + "lastDay2": "Претпоследњи дан у месецу", + "disableSTARTTLSDescription": "Омогућите ову опцију за SMTP сервере који не подржавају STARTTLS. Ово ће слати е-поруке путем неенкриптоване конекције.", + "Hello @everyone is...": "Здраво {'@'}сви су…", + "octopushPhoneNumber": "Број телефона (међународни формат, нпр.: +38160123456)", + "octopushSMSSender": "Назив SMS пошиљаоца : 3-11 алфа нумеричких карактера и размак (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea ID уређаја", + "Show Clickable Link": "Прикажи везе које се могу кликнути", + "Reverse Proxy": "Повратни прокси", + "Coming Soon": "Стиже ускоро" +} diff --git a/src/lang/sv-SE.json b/src/lang/sv-SE.json new file mode 100644 index 0000000..f4ad951 --- /dev/null +++ b/src/lang/sv-SE.json @@ -0,0 +1,1715 @@ +{ + "languageName": "English", + "checkEverySecond": "Uppdatera var {0} sekund", + "retriesDescription": "Max antal försök innan tjänsten markeras som nere och en notis skickas", + "ignoreTLSError": "Ignorera TLS/SSL-fel för webbsidor med HTTPS", + "upsideDownModeDescription": "Vänd upp och ner på statusen. Om tjänsten är nåbar visas den som NERE.", + "maxRedirectDescription": "Max antal omdirigeringar att följa. Välj 0 för att avaktivera omdirigeringar.", + "acceptedStatusCodesDescription": "Välj statuskoder som räknas som lyckade.", + "passwordNotMatchMsg": "Det bekräftade lösenordet stämmer ej överens.", + "notificationDescription": "Vänligen lägg till en notistjänst till dina övervakare.", + "keywordDescription": "Sök efter nyckelord i ren HTML eller JSON-svar. Sökningen är skiftkänslig.", + "pauseDashboardHome": "Pausa", + "deleteMonitorMsg": "Är du säker på att du vill ta bort den här övervakningen?", + "deleteNotificationMsg": "Är du säker på att du vill ta bort den här notisen för alla övervakare?", + "resolverserverDescription": "Cloudflare är den förvalda servern. Du kan byta resolver när som helst.", + "rrtypeDescription": "Välj den RR-typ du vill övervaka", + "pauseMonitorMsg": "Är du säker på att du vill pausa?", + "Settings": "Inställningar", + "Dashboard": "Infopanel", + "New Update": "Ny uppdatering", + "Language": "Språk", + "Appearance": "Utseende", + "Theme": "Tema", + "General": "Allmänt", + "Version": "Version", + "Check Update On GitHub": "Sök efter uppdatering på GitHub", + "List": "Lista", + "Add": "Lägg till", + "Add New Monitor": "Lägg till ny övervakare", + "Quick Stats": "Snabbstatistik", + "Up": "Uppe", + "Down": "Nere", + "Pending": "Pågående", + "Unknown": "Okänt", + "Pause": "Pausa", + "Name": "Namn", + "Status": "Status", + "DateTime": "Datum & tid", + "Message": "Meddelande", + "No important events": "Inga viktiga händelser", + "Resume": "Återuppta", + "Edit": "Redigera", + "Delete": "Ta bort", + "Current": "Nuvarande", + "Uptime": "Drifttid", + "Cert Exp.": "Certifikat utlöper.", + "day": "dag | dagar", + "-day": "-dag", + "hour": "timme", + "-hour": "-timme", + "Response": "Svar", + "Ping": "Ping", + "Monitor Type": "Övervakningstyp", + "Keyword": "Nyckelord", + "Friendly Name": "Namn", + "URL": "URL", + "Hostname": "Värdnamn", + "Port": "Port", + "Heartbeat Interval": "Hjärtslagsintervall", + "Retries": "Försök", + "Advanced": "Avancerat", + "Upside Down Mode": "Upp och ner-läge", + "Max. Redirects": "Max antal omdirigeringar", + "Accepted Status Codes": "Tillåtna statuskoder", + "Save": "Spara", + "Notifications": "Notifieringar", + "Not available, please setup.": "Ej tillgänglig, vänligen konfigurera.", + "Setup Notification": "Konfigurera notifieringstjänst", + "Light": "Ljust", + "Dark": "Mörkt", + "Auto": "Automatiskt", + "Theme - Heartbeat Bar": "Tema - Heartbeat-indikator", + "Normal": "Normal", + "Bottom": "Botten", + "None": "Ingen", + "Timezone": "Tidszon", + "Search Engine Visibility": "Synlighet på sökmotorer", + "Allow indexing": "Tillåt indexering", + "Discourage search engines from indexing site": "Hindra sökmotorer från att indexera sidan", + "Change Password": "Byt lösenord", + "Current Password": "Nuvarande lösenord", + "New Password": "Nytt lösenord", + "Repeat New Password": "Upprepa nytt lösenord", + "Update Password": "Uppdatera lösenord", + "Disable Auth": "Avaktivera autentisering", + "Enable Auth": "Aktivera autentisering", + "Logout": "Logga ut", + "Leave": "Lämna", + "I understand, please disable": "Jag förstår, vänligen avaktivera", + "Confirm": "Bekräfta", + "Yes": "Ja", + "No": "Nej", + "Username": "Användarnamn", + "Password": "Lösenord", + "Remember me": "Kom ihåg mig", + "Login": "Logga in", + "No Monitors, please": "Inga övervakare", + "add one": "lägg till en", + "Notification Type": "Notifieringstyp", + "Email": "Epost", + "Test": "Testa", + "Certificate Info": "Certifikatsinfo", + "Resolver Server": "Resolverserver", + "Resource Record Type": "Resource Record-typ", + "Last Result": "Senaste resultat", + "Create your admin account": "Skapa ditt administratörskonto", + "Repeat Password": "Upprepa lösenord", + "respTime": "Svarstid (ms)", + "Specific Monitor Type": "Applikationsspecifik övervakartyp", + "Push URL": "Push URL", + "Passive Monitor Type": "Passiv övervakartyp", + "markdownSupported": "Markdown-syntax stöds. Om du använder HTML, undvik inledande mellanslag för att förhindra formateringsproblem.", + "Heartbeat Retry Interval": "Omprövningsintervall", + "needPushEvery": "Hämta denna URL var {0} sekund.", + "pushOptionalParams": "Valfria parametrar: {0}", + "disableauth.message1": "Vill du verkligen {disableAuth}?", + "disable authentication": "avaktivera autentisering", + "disableauth.message2": "Det är designat för när en {intendThirdPartyAuth} såsom Cloudflare Access eller Authelia används framför Uptime Kuma.", + "where you intend to implement third-party authentication": "tredjeparts autentiseringstjänst", + "Please use this option carefully!": "Använd denna funktion varsamt!", + "Import Backup": "Importera säkerhetskopia", + "Affected Monitors": "Påverkade övervakare", + "Start of maintenance": "Påbörja underhåll", + "All Status Pages": "Alla statussidor", + "alertNoFile": "Välj en fil att importera.", + "alertWrongFileType": "Välj en JSON-formaterad fil.", + "Help": "Hjälp", + "Export": "Exportera", + "Import": "Importera", + "Game": "Spel", + "resendEveryXTimes": "Omsänd efter {0} gånger", + "Export Backup": "Exportera säkerhetskopia", + "Schedule maintenance": "Schemalägg underhåll", + "Monitor": "Övervakare | Övervakare", + "Resend Notification if Down X times consecutively": "Sänd notis igen om nere X gånger i rad", + "Maintenance": "Underhåll", + "retryCheckEverySecond": "Ompröva var {0} sekund", + "statusMaintenance": "Underhåll", + "resendDisabled": "Omsändning inaktiverat", + "Pick Affected Monitors...": "Välj påverkade övervakare…", + "Select status pages...": "Välj statussidor…", + "General Monitor Type": "Allmän övervakartyp", + "webhookFormDataDesc": "{multipart} är bra för PHP. JSON kommer att bli analyserat med {decodeFunction}", + "appriseInstalled": "Apprise är installerat.", + "clearDataOlderThan": "Behåll övervakarhistoriksdata i {0} dagar.", + "steamApiKeyDescription": "För att övervaka en Steam Game Server behöver man en Steam Web-API nyckel. Du kan registrera din API nyckel här: ", + "No Monitors": "Inga övervakare", + "proxyDescription": "Proxyservrar måste tilldelas en övervakare för att fungera.", + "setAsDefaultProxyDescription": "Denna proxyserver kommer att aktiveras som standard för nya övervakare. Du kan fortfarande inaktivera proxyserven var för sig övervakare.", + "Content Type": "Innehållstyp", + "webhookAdditionalHeadersDesc": "Ställer in ytterligare headers skickat med webbhooken. Varenda header ska definieras som ett JSON nyckel/värde par.", + "RadiusCallingStationIdDescription": "Kallande enhetsidentifierare", + "Slug": "Slugg", + "Invert Keyword": "Invertera nyckelord", + "Degraded Service": "Försämrad tjänst", + "Request Timeout": "Request-timeout", + "timeoutAfter": "Timeout efter {0} sekunder", + "styleElapsedTime": "Förfluten tid under heartbeat-indikatorn", + "styleElapsedTimeShowNoLine": "Visa (utan linje)", + "styleElapsedTimeShowWithLine": "Visa (med linje)", + "Create": "Skapa", + "Clear Data": "Radera data", + "Auto Get": "Hämta automatiskt", + "Overwrite": "Skriv över", + "Options": "Alternativ", + "Verify Token": "Verifiera token", + "Enable 2FA": "Aktivera 2FA", + "Disable 2FA": "Inaktivera 2FA", + "2FA Settings": "2FA inställningar", + "Two Factor Authentication": "Tvåfaktorsautentisering", + "filterActive": "Aktiv", + "filterActivePaused": "Pausad", + "Inactive": "Inaktiv", + "Setup 2FA": "Ställ in 2FA", + "Clear all statistics": "Rensa all statistik", + "Skip existing": "Hoppa över existerande", + "Keep both": "Behåll båda", + "Token": "Token", + "notAvailableShort": "N/A", + "Apply on all existing monitors": "Applicera på alla existerande övervakare", + "Heartbeats": "Hjärtslag", + "Show URI": "Visa URI", + "color": "Färg", + "value (optional)": "Värde (valfritt)", + "Gray": "Grå", + "Tags": "Taggar", + "Tag with this name already exist.": "Tagg med detta namn finns redan.", + "Red": "Röd", + "Orange": "Orange", + "Green": "Grön", + "Blue": "Blå", + "Indigo": "Indigoblå", + "Purple": "Lila", + "Pink": "Rosa", + "Custom": "Anpassad", + "Search...": "Sök…", + "Avg. Ping": "Pingmedelvärde", + "Tag with this value already exist.": "Tagg med detta värde finns redan.", + "Avg. Response": "Responsmedelvärde", + "Entry Page": "Ingångssida", + "statusPageRefreshIn": "Uppdaterar om: {0}", + "No Services": "Inga tjänster", + "All Systems Operational": "Alla system i drift", + "Partially Degraded Service": "Delvis försämrade tjänster", + "Add Group": "Lägg till grupp", + "Add New Tag": "Lägg till ny tagg", + "Add New below or Select...": "Lägg till ny under eller välj…", + "Add a monitor": "Lägg till en övervakare", + "Edit Status Page": "Hantera statussida", + "Status Page": "Statussida", + "Status Pages": "Statussidor", + "Go to Dashboard": "Till infopanel", + "here": "här", + "Required": "Nödvändig", + "webhookJsonDesc": "{0} är bra för moderna HTTP servrar till exempel Express.js", + "webhookCustomBodyDesc": "Definiera en anpassad HTTP Body till den request. Mall variabler {msg}, {heartbeat}, {monitor} accepteras.", + "webhookAdditionalHeadersTitle": "Ytterligare headers", + "webhookBodyPresetOption": "Förinställning - {0}", + "defaultNotificationName": "Min {notification}-notis ({number})", + "webhookBodyCustomOption": "Anpassad body", + "Webhook URL": "Webbhook URL", + "Application Token": "Applikationstoken", + "Server URL": "Server URL", + "Priority": "Prioritet", + "emojiCheatSheet": "Emoji fusklapp: {0}", + "Read more": "Läs mer", + "appriseNotInstalled": "Apprise är inte installerat. {0}", + "Method": "Metod", + "Body": "Innehåll", + "Headers": "Adressinformation", + "HeadersInvalidFormat": "Requestheaders är inte giltig JSON: ", + "BodyInvalidFormat": "Requestens body är inte giltig JSON: ", + "Monitor History": "Övervakarhistorik", + "PasswordsDoNotMatch": "Lösenorden matchar inte.", + "records": "transaktion", + "One record": "En transaktion", + "Current User": "Nuvarande användare", + "topic": "Ämne", + "topicExplanation": "MQTT-ämne att övervaka", + "successMessageExplanation": "MQTT meddelande som ska anses vara framgång", + "successMessage": "Framgång Meddelande", + "recent": "Nyligen", + "Done": "Klar", + "Info": "Info", + "Security": "Säkerhet", + "Steam API Key": "Steam API Nyckel", + "Shrink Database": "Krymp databas", + "Pick a RR-Type...": "Välj en RR-Typ…", + "Pick Accepted Status Codes...": "Välj accepterade statuskoder…", + "Default": "Standard", + "HTTP Options": "HTTP-alternativ", + "Create Incident": "Skapa incident", + "Title": "Titel", + "Content": "Innehåll", + "Style": "Stil", + "info": "info", + "Post URL": "Post URL", + "PushUrl": "Push URL", + "danger": "risk", + "error": "fel", + "critical": "kritisk", + "primary": "primär", + "light": "ljus", + "dark": "mörk", + "Post": "Post", + "Please input title and content": "Vänligen lägg till titel och innehåll", + "Created": "Skapad", + "Last Updated": "Senaste uppdaterad", + "Switch to Light Theme": "Byt till ljust tema", + "Switch to Dark Theme": "Byt till mörkt tema", + "Show Tags": "Visa taggar", + "Hide Tags": "Göm taggar", + "Description": "Beskrivning", + "No monitors available.": "Inga övervakare tillgängliga.", + "Add one": "Lägg till en", + "Untitled Group": "Namnlös grupp", + "Services": "Tjänster", + "Discard": "Förkasta", + "Cancel": "Avbryt", + "Select": "Välj", + "selectedMonitorCount": "Valt: {0}", + "Check/Uncheck": "Markera/Avmarkera", + "Powered by": "Drivs av", + "Customize": "Anpassa", + "Custom Footer": "Anpassad sidfot", + "Custom CSS": "Anpassad CSS", + "deleteStatusPageMsg": "Är du säkert att du vill radera denna statussida?", + "Proxies": "Proxyservrar", + "default": "Standard", + "enabled": "Aktiverad", + "setAsDefault": "Ange som standard", + "deleteProxyMsg": "Är du säkert att du vill radera denna proxyserver för alla övervakare?", + "Certificate Chain": "Certifikatkedja", + "Valid": "Giltig", + "Invalid": "Ogiltig", + "User": "Användare", + "Installed": "Installerat", + "Not installed": "Inte installerat", + "Running": "Körs", + "Not running": "Körs inte", + "Remove Token": "Ta bort token", + "Start": "Starta", + "Stop": "Stoppa", + "Add New Status Page": "Lägg till ny statussida", + "Accept characters:": "Acceptera tecken:", + "Unpin": "Ta bort fastnålat", + "startOrEndWithOnly": "Börja eller sluta med {0} endast", + "No consecutive dashes": "Inga streck i följd", + "Next": "Nästa", + "The slug is already taken. Please choose another slug.": "Sluggen är upptagen. Vänligen välj en annan slugg.", + "No Proxy": "Ingen proxyserver", + "Authentication": "Autentisering", + "HTTP Basic Auth": "HTTP Basic-auth", + "New Status Page": "Ny statussida", + "Page Not Found": "Sidan hittades inte", + "Reverse Proxy": "Omvänd proxyserver", + "Backup": "Säkerhetskopia", + "About": "Om", + "wayToGetCloudflaredURL": "(Ladda ned cloudflared från {0})", + "cloudflareWebsite": "Cloudflare webbsida", + "Message:": "Meddelande:", + "Don't know how to get the token? Please read the guide:": "Vet inte hur man får tag i en token? Vänligen läs guiden:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Den nuvarande anslutningen kan kopplas bort om du ansluter via Cloudflare Tunnel. Är du säkert du vill avsluta den? Skriv ditt nuvarande lösenord för att bekräfta.", + "HTTP Headers": "HTTP-headers", + "Trust Proxy": "Lita på proxyserver", + "Other Software": "Annan programvara", + "For example: nginx, Apache and Traefik.": "Till exempel: nginx, Apache och Traefik.", + "Please read": "Vänligen läs", + "Subject:": "Ämne:", + "Valid To:": "Giltig till:", + "Days Remaining:": "Dagar kvar:", + "Fingerprint:": "Fingeravtryck:", + "No status pages": "Inga statussidor", + "Proxy": "Proxyserver", + "Date Created": "Skapat datum", + "Footer Text": "Sidfotstext", + "Show Powered By": "Visa \"Drivs av\"", + "Domain Names": "Domännamn", + "signedInDisp": "Inloggad som {0}", + "signedInDispDisabled": "Auth inaktiverad.", + "RadiusSecret": "Radius-hemlighet", + "RadiusSecretDescription": "Delad hemlighet mellan klient och server", + "RadiusCalledStationId": "Avbrutet Stationsid", + "RadiusCalledStationIdDescription": "Enhetsidentifierare", + "RadiusCallingStationId": "Uppringande stationsid", + "Certificate Expiry Notification": "Notifiering om certifikatets utgångsdatum", + "Domain Name Expiry Notification": "Notifiering om förfallodatum på domännamn", + "API Key": "API-nyckel", + "API Username": "API-användarnamn", + "Show update if available": "Visa uppdatering om tillgänglig", + "Also check beta release": "Kolla också betaversioner", + "Events": "Händelser", + "Active": "Aktiv", + "statusPageNothing": "Ingenting här, vänligen lägg till en grupp eller en övervakare.", + "warning": "varning", + "Issuer:": "Utfärdare:", + "Expected Value": "Förväntat värde", + "Primary Base URL": "Primär bas-URL", + "Home": "Hem", + "Cannot connect to the socket server": "Kan inte ansluta till socketservern", + "Reconnecting...": "Återansluter...", + "Json Query": "Json-fråga", + "Default enabled": "Aktiverad som standard", + "pushViewCode": "Hur använda Pushövervakare? (Visa kod)", + "Steam Game Server": "Steam Spel Server", + "Docker Container": "Docker-container", + "setupDatabaseChooseDatabase": "Vilken databas vill du använda?", + "dbName": "Databasnamn", + "What you can try:": "Vad du kan försöka:", + "Container Name / ID": "Containernamn / ID", + "Docker Host": "Dockervärd", + "Docker Hosts": "Dockervärdar", + "Domain": "Domän", + "Most likely causes:": "Störst troliga anledningar:", + "Coming Soon": "Kommer snart", + "liquidIntroduction": "Mallar uppnås via mallspråket Liquid. Se {0} för användarinstruktioner.", + "emailTemplateStatus": "Status", + "templateHeartbeatJSON": "objekt beskrivande hjärtslag", + "templateLimitedToUpDownCertNotifications": "Endast tillgänglig för UPP/NER/Certifikat giltigt notifieringar", + "successKeyword": "Nyckelord för lyckat", + "successKeywordExplanation": "MQTT-nyckelord som anses lyckade", + "cronExpression": "Cronuttryck", + "invalidCronExpression": "Ogiltigt cronuttryck: {0}", + "backupRecommend": "Vänligen säkerhetskopiera volymen eller mappen (./data/) direkt istället.", + "disableCloudflaredNoAuthMsg": "Du är i Auth-läge, ett lösenord krävs inte.", + "setupDatabaseEmbeddedMariaDB": "Du behöver inte ställa in någonting. Denna dockeravbild har inbyggd och konfigurerade MariaDB automatiskt. Uptime Kuma kommer att ansluta till databasen via en Unixsocket.", + "setupDatabaseMariaDB": "Anslut till en extern MariaDB databas. Du måste ange anslutningsinformation till databasen.", + "setupDatabaseSQLite": "En enkel databasfil, rekommenderas för småskaliga installationer. Före v2.0.0, använde Uptime Kuma SQLite som standarddatabas.", + "Reset Token": "Återställ token", + "Workstation": "Arbetsstation", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Packet Size": "Paketstorlek", + "Bot Token": "Bott-token", + "telegramSendSilently": "Skicka tyst", + "telegramMessageThreadID": "(Valfritt) Meddelandetrådsid", + "Chat ID": "Chatt-ID", + "deleteDockerHostMsg": "Är du säker att du vill ta bort denna dockervärd för alla övervakare?", + "DockerHostRequired": "Vänligen ange Dockervärd för denna övervakare.", + "wayToGetTelegramToken": "Du kan få en token från {0}.", + "pushOthers": "Andra", + "programmingLanguages": "Programmeringsspråk", + "templateMsg": "meddelande i notifieringen", + "templateMonitorJSON": "objekt beskrivande övervakare", + "templateLimitedToUpDownNotifications": "endast tillgänglig för UPP/NER notifieringar", + "Examples": "Exempel", + "Using a Reverse Proxy?": "Används omvänd proxy?", + "Query": "Fråga", + "Connection Type": "Anslutningstyp", + "YOUR BOT TOKEN HERE": "DIN BOTT-TOKEN HÄR", + "or": "eller", + "Optional": "Valfri", + "Event data:": "Händelsedata:", + "Trigger type:": "Utlösningstyp:", + "Automations can optionally be triggered in Home Assistant:": "Automationer kan valfritt utlösas i Home Assistant:", + "default: notify all devices": "standard: notifiera alla enheter", + "Notification Service": "Notifieringstjänst", + "Home Assistant URL": "Home Assistant-URL", + "wayToGetLineNotifyToken": "Du kan få en åtkomststoken från {0}", + "telegramProtectContent": "Skydda vidarebefordring/sparning", + "weekdayShortWed": "Ons", + "weekdayShortTue": "Tis", + "weekdayShortMon": "Mån", + "weekdayShortSun": "Sön", + "weekdayShortSat": "Lör", + "weekdayShortFri": "Fre", + "weekdayShortThu": "Tors", + "cronSchedule": "Schema: ", + "endDateTime": "Slutdatum/tid", + "startDateTime": "Startdatum/tid", + "sameAsServerTimezone": "Samma som serverns tidszon", + "warningTimezone": "Den använder serverns tidszon", + "strategyManual": "Aktivera/Inaktivera manuellt", + "Recurring": "Återkommande", + "Event type:": "Händelsetyp:", + "recurringInterval": "Intervall", + "emailCustomSubject": "Anpassat ämne", + "emailCustomisableContent": "Anpassningsbart innehåll", + "smtpLiquidIntroduction": "Följande två fält är mallbara via Liquid templating Language. Vänligen referera till {0} för användningsinstruktioner. Detta är dom tillgängliga variablerna:", + "leave blank for default subject": "lämna blankt för standardämne", + "emailCustomBody": "Anpassat meddelande", + "Recipients": "Mottagare", + "twoFAVerifyLabel": "Vänligen ange din token för att verifiera 2FA:", + "pushoversounds tugboat": "Bogserbåt", + "pushoversounds alien": "Utomjordingsalarm (lång)", + "AccessKeyId": "Accessnyckel-ID", + "Uptime Kuma URL": "Uptime Kuma URL", + "Notify Channel": "Notifieringskanal", + "smseagleRecipientType": "Mottagartyp", + "smseagleTo": "Telefonnummer", + "smseagleRecipient": "Mottagare (flera måste separeras med komma)", + "clearEventsMsg": "Är du säker på att du vill ta bort alla händelser för den här övervakaren? Detta tar bort tillhörande meddelandedata permanent och kan inte ångras.", + "successDisabled": "Inaktivering lyckades.", + "confirmImportMsg": "Är du säker på att du vill importera säkerhetskopian? Verifiera att du valt korrekta importeringsval.", + "remoteBrowsersDescription": "Fjärrwebbläsare är ett alternativ istället för att köra Chromium lokalt. Ställ in en tjänst liknande browserless.io eller anslut till din egna", + "pushyToken": "Enhetstoken", + "successDeleted": "Lyckades ta bort.", + "Enable DNS Cache": "(Utfasad) Aktivera DNS-cache för HTTP(s) övervakare", + "enableNSCD": "Aktivera NSCD (Name Service Cache Daemon) för att cacha alla DNS-förfrågningar", + "chromeExecutable": "Chrome/Chromium körbar fil", + "chromeExecutableAutoDetect": "Autodetektera", + "pushoverMessageTtl": "Meddelande TTL (sekunder)", + "Example:": "Exempel: {0}", + "Sms template must contain parameters: ": "SMS-mall måste innehålla dessa parametrar: ", + "WeCom Bot Key": "WeCom-bottsnyckel", + "Add a domain": "Lägg till en domän", + "smtpDkimSettings": "DKIM-inställningar", + "Line Developers Console": "Linjeutvecklarkonsol", + "infiniteRetention": "Ange 0 för oändlig kvarhållningsperiod.", + "enableDefaultNotificationDescription": "Denna notifiering kommer att vara aktiverad som standard för nya övervakere. Du kan inaktivera notifieringen separat för varje övervakare.", + "wayToGetKookBotToken": "Skapa applikation och få din bott-token vid {0}", + "alertaApiEndpoint": "API-slutpunkt", + "Learn More": "Läs mer", + "Add Another": "Lägg till en annan", + "apiKeyAddedMsg": "Din API-nyckel har lagts till. Vänligen notera den för detta visas inte igen.", + "octopushPhoneNumber": "Telefonnummer (int. format, ex: +33612345678)", + "noOrBadCertificate": "Inget/ogiltigt certifikat", + "successBackupRestored": "Lyckades återställa säkerhetskopia.", + "GrafanaOncallUrl": "Grafana OnCall-URL", + "Maintenance Time Window of a Day": "Underhållsfönster-tid på dagen", + "Effective Date Range": "Giltigt datumintervall (valfritt)", + "Enable": "Aktivera", + "Disable": "Inaktivera", + "Schedule Maintenance": "Schemalagt underhåll", + "Integration Key": "Integreringsnyckel", + "Remote Browsers": "Fjärrwebbläsare", + "Remote Browser": "Fjärrwebbläsare", + "Add a Remote Browser": "Lägg till en fjärrwebbläsare", + "Check how to config it for WebSocket": "Kontrollera hur man konfigurerar den för webbsocket", + "The resource is no longer available.": "Resursen är inte längre tillgänglig.", + "Connection String": "Anslutningssträng", + "wayToCheckSignalURL": "Du kan kontrollera denna URL för att visa hur man sätter upp en:", + "pushoverDesc2": "Om du vill skicka notifieringar till andra enheter, fyll i enhetsfältet.", + "Generate": "Generera", + "noDockerHostMsg": "Inte tillgängligt. Ställ in en dockervärd först.", + "tailscalePingWarning": "För att använda Tailscale Ping-övervakare, måste du installera Uptime Kuma utan docker och också installera Tailscale-klienten på din server.", + "trustProxyDescription": "Lita på 'X-Forwarded-*' headers. Om du vill få den korrekta IP-adressen för klienten och Uptime Kuma är bakom en proxy såsom Nginx eller Apache, måste du aktivera detta.", + "statusPageMaintenanceEndDate": "Slut", + "IconUrl": "Ikon-URL", + "Single Maintenance Window": "Ensamt underhållsfönster", + "maintenanceStatus-under-maintenance": "Underhåll pågår", + "maintenanceStatus-inactive": "Inaktiv", + "maintenanceStatus-scheduled": "Schemalagd", + "maintenanceStatus-ended": "Slutade", + "maintenanceStatus-unknown": "Okänd", + "Display Timezone": "Visa tidszon", + "Server Timezone": "Server-tidszon", + "smtpBCC": "Hemlig kopia", + "smtpCC": "Kopia", + "Number": "Nummer", + "To Email": "Till epost", + "emailTemplateMonitorJSON": "objekt som beskriver övervakaren", + "emailTemplateHeartbeatJSON": "objekt som beskriver hjärtslaget", + "emailTemplateMsg": "meddelande i notifieringen", + "emailTemplateLimitedToUpDownNotification": "endast tillgängligt för UPP/NER hjärtslag, annars noll", + "Discord Webhook URL": "Discord webbhook-URL", + "wayToGetDiscordURL": "Du kan hitta detta genom att gå till Serverinställningar -> Integrationer -> Visa webbhocks -> Ny webbhook", + "Bot Display Name": "Bott-visningsnamn", + "dataRetentionTimeError": "Kvarhållningsperiod måste vara större än 0", + "Integration URL": "Integrerings-URL", + "Close": "Stäng", + "Session Token": "Sessionstoken", + "noGroupMonitorMsg": "Inte tillgängligt. Skapa en gruppövervakare först.", + "successEnabled": "Aktivering lyckades.", + "tagNotFound": "Tagg hittades inte.", + "Bark API Version": "Bark API-version", + "Add a new expiry notification day": "Lägg till ett ny dag för notifiering av utgångsdatum", + "Remove the expiry notification": "Ta bort notifieringsdag för utgångsdatum", + "There might be a typing error in the address.": "Det kan vara ett stavfel i adressen.", + "Retype the address.": "Skriv om adressen.", + "Go back to the previous page.": "Gå tillbaka till föregående sida.", + "settingsCertificateExpiry": "TLS-certifikatsutgångsdatum", + "certificationExpiryDescription": "HTTPS-övervakare utlöser en notifiering när TLS-certifikatet löper ut om:", + "Setup Docker Host": "Ställ in dockervärd", + "Docker Daemon": "Docker-daemon", + "telegramSendSilentlyDescription": "Skickar meddelandet tyst. Användare kommer att få en notifiering utan ljud.", + "telegramMessageThreadIDDescription": "Valfri unik identifierare för meddelandets tråd (ämne) på forumet; endast för forum-supergrupper", + "telegramProtectContentDescription": "Om aktiverat kommer bott-meddelanden i Telegram att vara skyddade från vidarebefordring och sparande.", + "supportTelegramChatID": "Stöd direktchatt / grupp / kanalens chatt-id", + "wayToGetTelegramChatID": "Du kan se ditt chatt-id genom att skicka ett meddelande till boten och besöka denna URL för att visa chat_id:", + "chatIDNotFound": "Chatt-id hittade inte; vänligen skicka ett meddelande till den här botten först", + "Long-Lived Access Token": "Långtidsaccess-token", + "Frontend Version": "Frontend-version", + "Frontend Version do not match backend version!": "Frontend-version matchar inte backend-version!", + "backupOutdatedWarning": "Avskrivet: Eftersom en massa funktioner har lagts till och denna säkerhetskopieringsfunktion är lite ounderhållen, kan det inte generera eller återställa en komplett säkerhetskopia.", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "En lista av notifieringstjänster kan hittas i Home Assistant under \"Utvecklarverktyg > Tjänster\", sök efter notifiering för att hitta din enhet/telefon-namn.", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Välj en åtgärd, till exempel byt scen till en där en RGB-lampa är röd.", + "emailTemplateHostnameOrURL": "Värdnamn eller URL", + "From Email": "Från epost", + "dayOfMonth": "Dag i månad", + "dayOfWeek": "Dag i vecka", + "lastDay": "Sista dagen", + "lastDay1": "Sista dagen i månaden", + "lastDay2": "2a sista dagen i månaden", + "lastDay3": "3e sista dagen i månaden", + "lastDay4": "4e sista dagen i månaden", + "No Maintenance": "Inget underhåll", + "pauseMaintenanceMsg": "Är du säker att du vill pausa?", + "chromeExecutableDescription": "Docker-användare, om Chromium inte är installerat kommer det att dröja ett par minuter att installera innan testresultatet visas. Det tar 1GB i diskutrymme.", + "dnsCacheDescription": "Det kan misslyckas i några IPv6-miljöer, inaktivera det om du stöter på några problem.", + "Date and Time": "Datum och tid", + "DateTime Range": "Datum/tid-intervall", + "loadingError": "Kan inte hämta data, vänligen försök senare.", + "install": "Installera", + "installing": "Installerar", + "uninstall": "Avinstallera", + "uninstalling": "Avinstallerar", + "confirmUninstallPlugin": "Är du säker på att du vill avinstallera detta tillägg?", + "plugin": "Tillägg | Tillägg", + "Clone Monitor": "Klona övervakare", + "Clone": "Klona", + "smtp": "Epost (SMTP)", + "secureOptionNone": "Ingen /STARTTLS (25,587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ignorera TLS-fel", + "leave blank for default body": "lämna blankt för standardmeddelande", + "emailTemplateServiceName": "Tjänstenamn", + "Prefix Custom Message": "Anpassat meddelandeprefix", + "Hello @everyone is...": "Hej {'@'}alla är…", + "wayToGetTeamsURL": "Du kan lära dig hur man skapar en webbhook-URL {0}.", + "wayToGetZohoCliqURL": "Du kan lära dig hur man skapar en webbhook-URL {0}.", + "needSignalAPI": "Du måste ha en signal-klient med REST API.", + "Access Token": "Åtkomsttoken", + "Channel access token": "Kanalåtkomststoken", + "User ID": "Användarid", + "Messaging API": "Meddelandeapi", + "Icon URL": "Ikon-URL", + "deleteMaintenanceMsg": "Är du säker att du vill ta bort detta underhåll?", + "clearHeartbeatsMsg": "Är du säker på att du vill radera alla hjärtslag för den här övervakaren? Detta tar bort all hjärtslagsdata permanent och kan inte ångras.", + "confirmClearStatisticsMsg": "Är du säker att du vill ta bort ALL statistik?", + "tokenValidSettingsMsg": "Giltig token! Du kan nu spara 2FA-inställningarna.", + "confirmEnableTwoFAMsg": "Är du säker att du vill aktivera 2FA?", + "confirmDisableTwoFAMsg": "Är du säker att du vill inaktivera 2FA?", + "recurringIntervalMessage": "Kör en gång varje dag | Kör en gång var {0} dagar", + "affectedMonitorsDescription": "Välj övervakare som är påverkade av det nuvarande underhållet", + "affectedStatusPages": "Visa detta underhållsmeddelande på valda statussidor", + "atLeastOneMonitor": "Välj minst en påverkad övervakare", + "Basic Settings": "Grundläggande inställningar", + "dnsPortDescription": "DNS-serverport. Standard är 53. Du kan ändra port när som helst.", + "confirmDeleteTagMsg": "Är du säker att du vill radera denna tagg? Övervakare associerade med denna tagg kommer inte att tas bort.", + "enableGRPCTls": "Tillåt att gRPC-förfrågningar skickas med TLS-anslutning", + "invertKeywordDescription": "Titta om nyckelordet frånvarande istället för närvarande.", + "pushoversounds bike": "Cykel", + "pushoversounds cashregister": "Kassaregister", + "pushoversounds classical": "Klassisk", + "pushoversounds cosmic": "Kosmisk", + "pushoversounds falling": "Fallande", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Inkommande", + "pushoversounds intermission": "Paus", + "pushoversounds magic": "Magisk", + "pushoversounds mechanical": "Mekanisk", + "pushoversounds pianobar": "Pianobar", + "pushoversounds siren": "Siren", + "pushoversounds spacealarm": "Rymdalarm", + "pushoversounds climb": "Klättra (lång)", + "pushoversounds persistent": "Beständig (lång)", + "pushoversounds echo": "Pushovereko (lång)", + "pushoversounds updown": "Upp ner (lång)", + "pushoversounds vibrate": "Vibrera endast", + "pushoversounds none": "Ingen (tyst)", + "pushyAPIKey": "Hemlig API-nyckel", + "User Key": "Användarnyckel", + "Device": "Enhet", + "Message Title": "Meddelandetitel", + "Notification Sound": "Notifieringsljud", + "More info on:": "Mer info på: {0}", + "SMS Type": "SMS-typ", + "promosmsLogin": "API-inloggninsnamn", + "promosmsPassword": "API-lösenord", + "apprise": "Apprise (Stödjer 50+ notifieringstjänster)", + "GoogleChat": "Google Chat (Google Workspace endast)", + "backupDescription": "Du kan säkerhetskopiera alla övervakare och notifieringstjänster till en JSON-fil.", + "endpoint": "slutpunkt", + "Read more:": "Lär mer: {0}", + "Status:": "Status: {0}", + "Strategy": "Strategi", + "high": "hög", + "SecretAccessKey": "Accessnyckelshemlighet", + "PhoneNumbers": "Telefonnummer", + "TemplateCode": "Mallkod", + "SignName": "Signaturnamn", + "Bark Group": "Barkgrupp", + "Bark Endpoint": "Barkslutpunkt", + "Bark Sound": "Barkljud", + "WebHookUrl": "Webbhook-URL", + "SecretKey": "Hemlig nyckel", + "For safety, must use secret key": "För säkerhet måste det användas en hemlig nyckel", + "Device Token": "Enhetstoken", + "Platform": "Plattform", + "Huawei": "Huawei", + "High": "Hög", + "Retry": "Försök igen", + "Topic": "Ämne", + "Setup Proxy": "Ställ in proxy", + "Proxy Protocol": "Proxy-protokoll", + "Proxy server has authentication": "Proxy-server har autentisering", + "Channel Name": "Kanalnamn", + "setup a new monitor group": "ställ in en ny övervakningsgrupp", + "openModalTo": "öppna modal till {0}", + "Remove domain": "Ta bort domän '{0}'", + "Icon Emoji": "Ikonemoji", + "apiCredentials": "API-referenser", + "Apprise URL": "Apprise-URL", + "checkPrice": "Kontrollera {0} pris:", + "matrixHomeserverURL": "Hemserver-URL (med http(s):// och valfri port)", + "Internal Room Id": "Internt rums-ID", + "documentation": "dokumentation", + "smtpDkimDomain": "Domännamn", + "smtpDkimKeySelector": "Nyckelväljare", + "smtpDkimPrivateKey": "Privat nyckel", + "smtpDkimHashAlgo": "Hashningsalgoritm (valfritt)", + "do nothing": "gör ingenting", + "alertaEnvironment": "Miljö", + "alertaApiKey": "API-nyckel", + "alertaAlertState": "Larmläge", + "serwersmsAPIPassword": "API-lösenord", + "serwersmsPhoneNumber": "Telefonnummer", + "API Keys": "API-nycklar", + "Expiry": "Utgången", + "Expiry date": "Utgångsdatum", + "Don't expire": "Inget utgångsdatum", + "Continue": "Fortsätt", + "Key Added": "Nyckel tillagd", + "Add API Key": "Lägg till API-nyckel", + "No API Keys": "Inga API-nycklar", + "apiKey-active": "Aktiv", + "apiKey-expired": "Utgången", + "apiKey-inactive": "Inaktiv", + "Expires": "Utgår", + "disableAPIKeyMsg": "Är du säker att du vill inaktivera denna API-nyckel?", + "deleteAPIKeyMsg": "Är du säker att du vill ta bort denna API-nyckel?", + "pagertreeIntegrationUrl": "Integrations-URL", + "pagertreeUrgency": "Brådskande", + "pagertreeSilent": "Tyst", + "pagertreeLow": "Låg", + "pagertreeMedium": "Mellan", + "pagertreeHigh": "Hög", + "pagertreeCritical": "Kritisk", + "ntfyUsernameAndPassword": "Användarnamn och lösenord", + "foundChromiumVersion": "Hittade Chromium/Chrome. Version: {0}", + "Remote Browser not found!": "Fjärrwebbläsare hittades inte!", + "self-hosted container": "själv-hosted container", + "remoteBrowserToggle": "Som standard körs Chromium inuti Uptime Kuma containern.", + "useRemoteBrowser": "Använd en fjärrwebbläsare", + "Saved.": "Sparad.", + "FlashDuty Severity": "Allvarlighetsgrad", + "2faAlreadyEnabled": "2FA är redan aktiverat.", + "2faEnabled": "2FA aktiverat.", + "2faDisabled": "2FA inaktiverat.", + "successAdded": "Lyckades lägga till.", + "successResumed": "Lyckades återuppta.", + "successPaused": "Lyckades pausa.", + "successEdited": "Lyckades redigera.", + "successAuthChangePassword": "Lyckades uppdatera lösenordet.", + "Monitor Group": "Övervakningsgrupp", + "Group": "Grupp", + "showCertificateExpiry": "Visa certifikatets utgångsdatum", + "authUserInactiveOrDeleted": "Användaren är inaktiverad eller borttagen.", + "authInvalidToken": "Ogiltig token.", + "authIncorrectCreds": "Felaktigt användarnamn eller lösenord.", + "deleteRemoteBrowserMessage": "Är du säker att du vill radera denna fjärrwebbläsare för alla övervakare?", + "Browser Screenshot": "Webbläsarskärmdump", + "enableProxyDescription": "Denna proxy påverkar inte övervakarnas förfrågan förräns den är aktiverad. Du kan kontrollera och tillfälligt inaktivera proxyn från alla övervakare genom aktiveringsstatus.", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Långtidsaccess-token kan skapas genom att klicka på ditt profilnamn (nedre vänstra hörnet) och skrolla till botten och klicka på Skapa token.", + "Edit Maintenance": "Redigera underhåll", + "pushoversounds bugle": "Bugla", + "Enable TLS": "Aktivera TLS", + "Economy": "Ekonomi", + "Lowcost": "Låg kostnad", + "Gateway Type": "Gatewaytyp", + "You can divide numbers with": "Du kan dela nummer med", + "Base URL": "Bas-URL", + "promosmsAllowLongSMS": "Tillåt långa SMS", + "promosmsPhoneNumber": "Telefonnummer (för polska mottagare kan du skippa riktnummer)", + "Recipient Number": "Mottagarens nummer", + "From Name/Number": "Från namn/nummer", + "Leave blank to use a shared sender number.": "Lämna tomt för att använda ett delat avsändarnummer.", + "smseagleEncoding": "Skicka som unicode (standard=GSM-7)", + "smseaglePriority": "Meddelandeprioritet (0-9, högsta prioritet= 9)", + "onebotGroupMessage": "Grupp", + "onebotPrivateMessage": "Privat", + "onebotUserOrGroupId": "Grupp/användar-ID", + "pushDeerServerDescription": "Lämna tomt för att använda den officiella servern", + "Custom Monitor Type": "Anpassad övervakartyp", + "Google Analytics ID": "Google Analytics-ID", + "Edit Tag": "Redigera tagg", + "Server Address": "Serveradress", + "wayToGetClickSendSMSToken": "Du kan hitta API-användarnamn och API-nyckel från {här} .", + "pagertreeDoNothing": "Gör ingenting", + "lunaseaTarget": "Mål", + "lunaseaDeviceID": "Enhetsid", + "lunaseaUserID": "Användarid", + "ntfyAuthenticationMethod": "Autentiseringsmetod", + "twilioApiKey": "API-nyckel (valfritt)", + "twilioFromNumber": "Från nummer", + "twilioToNumber": "Till nummer", + "Show Clickable Link": "Visa klickbar länk", + "notificationRegional": "Regional", + "cloneOf": "Klon av {0}", + "Proxy Server": "Proxy-server", + "lineDevConsoleTo": "Linjeutvecklarkonsol - {0}", + "aboutIconURL": "Du kan tillhandahålla en länk till en bild i \"Ikon-URL\" för att åsidosätta standard profilbilden. Används inte om Ikonemoji är inställt.", + "backupDescription2": "Notera: historik och händelsedata är inte inkluderat.", + "promosmsTypeFlash": "SMS FLASH - Meddelanden visas automatiskt på mottagande enhet. Begränsad till Polska mottagare.", + "promosmsTypeFull": "SMS Full - Premium nivå av SMS, du kan använda ditt avsändarnamn (du måste registrera namnet först). Pålitligt för varningar.", + "serwersmsSenderName": "SMS-avsändarnamn (registrerat via kundportalen)", + "smseagleGroup": "Telefonboksgruppnamn", + "smseagleContact": "Telefonbokskontaktsnamn", + "Body Encoding": "Body-kodning", + "Badge Label Prefix": "Bricketikettsprefix", + "Badge Preview": "Brickförhandsvisning", + "Badge Label Suffix": "Bricketikettssuffix", + "Search monitored sites": "Sök övervakade sajter", + "Badge Warn Days": "Varningsbricksdagar", + "statusPageSpecialSlugDesc": "Speciell slug {0}: denna sida kommer att visas när ingen slug anges", + "wayToGetLineChannelToken": "Först besök {0}, skapa en leverantör och kanal (Meddelande-API), efter det kan du få en accesstoken för kanalen och användarid från ovanstående menyobjekt.", + "aboutMattermostChannelName": "Du kan åsidosätta standardkanalen som webbhook postar till genom att ange kanalnamnet i \"Kanalnamn\" fältet. Detta behöver vara aktiverad i Mattermost Webbhook inställningar. Ex: #annan-kanal", + "promosmsTypeEco": "SMS ECO - billig men långsam och ofta överbelastad. Begränsad till Polska mottagare.", + "ntfyPriorityHelptextAllEvents": "Alla händelser skickas med högsta prioritet", + "aboutNotifyChannel": "Notifieringskanalen kommer att utlösa en skrivbords eller mobilnotifiering för alla medlemmar i kanalen, oavsett om deras tillgänglighet är aktiv eller borta.", + "ntfyPriorityHelptextAllExceptDown": "Alla händelser är skickade med denna prioritet, utom {0}-händelser, som har en prioritet av {1}", + "settingUpDatabaseMSG": "Ställer in databasen. Detta kan ta ett tag, ha tålamod.", + "promosmsTypeSpeed": "SMS SPEED - Högsta prioritet i system. Väldigt snabb och tillförlitlig, men kostsam (nästan dubbelt mot SMS FULL priset).", + "Badge Down Days": "Nerbricksdagar", + "grpcMethodDescription": "Metodnamn är konverterad till kamelNotation format som sayHello, check, etc.", + "importHandleDescription": "Välj 'Hoppa över existerande' om du vill hoppa över varje övervakare eller notifiering med samma namn. 'Skriv över' kommer att ta bort existerande övervakare och notifieringar.", + "pushoversounds pushover": "Pushover (standard)", + "backupDescription3": "Känslig data som ex. notifieringstokens är inkluderade i exportfilen; vänligen lagra exporten säkert.", + "octopushLogin": "\"Inloggning\" från HTTP API autentiseringsuppgifter i kontrollpanelen", + "octopushAPIKey": "\"API-nyckel\" från HTTP API autentiseringsuppgifter i kontrollpanelen", + "wayToGetKookGuildID": "Slå på 'Utvecklarläge' i Kook-inställningarna och högerklicka på guild för att få dess ID", + "Guild ID": "Guild-ID", + "pushoverDesc1": "Nödprioritet (2) har 30 sekunders timeout mellan försök och löper ut efter 1 timme som standard.", + "octopushTypePremium": "Premium (Snabb - rekommenderas för varningar)", + "octopushTypeLowCost": "Låg kostnad (långsam - blockeras ibland av operatören)", + "Check octopush prices": "Kontrollera octopush priser {0}.", + "octopushSMSSender": "SMS avsändarnamn: 3-11 alfanumeriska tecken och mellanslag (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea enhetsid", + "Free Mobile User Identifier": "Fri mobilanvändaridentifierare", + "Feishu WebHookUrl": "Feishu WebbhookURL", + "signalImportant": "VIKTIGT: Du kan inte blanda grupper och nummer i mottagare!", + "octopushLegacyHint": "Använder du äldre versionen av Octopush (2011-2020) eller den nya versionen?", + "goAlertInfo": "GoAlert är en öppen källkodsapplikation för jouruppringning, automatiserade eskaleringar och notifieringar (som SMS eller röstsamtal). Engagera automatiskt rätt person, den rätta vägen och i rätt tid! {0}", + "goAlertIntegrationKeyInfo": "Få generiska API-integrationsnycklar för tjänsten i formatet \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" vanligtvis värdet av token-parametern för den kopierade URLen.", + "matrixDesc1": "Du kan hitta det interna rumsid:t genom att titta i den avancerade sektionen av rumsinställningarna i din Matrix-klient. Det bör vara liknande !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Det är rekommenderat att du skapar en ny användare och inte använder din egna Matrix-användares accesstoken eftersom det ger full access till ditt konto och alla rum du gått med i. Skapa en ny användare och invitera endast till rummen du vill ska få notifieringen. Du kan komma åt accesstoken genom att köra {0}", + "aboutWebhooks": "Mer information om Webbhooks på: {0}", + "aboutKumaURL": "Om du lämnar Uptime Kuma URL-fältet tomt, kommer den som standard till projektsidan på Github.", + "smtpDkimDesc": "Vänligen hänvisa till Nodemailer DKIM {0} för användning.", + "smtpDkimheaderFieldNames": "Headernycklar att signera (Valfritt)", + "smtpDkimskipFields": "Headernycklar att inte signera (Valfritt)", + "Auto resolve or acknowledged": "Automatisk lösning eller bekräftad", + "auto resolve": "automatiskt bekräftad", + "auto acknowledged": "automatiskt löst", + "alertaRecoverState": "Återställningsstatus", + "serwersmsAPIUser": "API-användarnamn (inkl. webapi_prefix)", + "smseagleToken": "API-accesstoken", + "smseagleUrl": "Din SMSEagle enhetsURL", + "pagertreeResolve": "Autolös", + "Badge Up Color": "Uppbricksfärg", + "aboutChannelName": "Ange kanalnamnet på {0} kanalnamnsfältet om du vill gå förbi webbhook-kanalen. Ex: #annan-kanal", + "wayToGetPagerDutyKey": "Du kan komma åt detta genom att gå till Tjänst -> Tjänstekatalog -> (välj tjänst) -> Integrationer -> Lägg till integration. Här kan du söka efter \"Events API V2\". För mer info {0}", + "Badge Pending Color": "Väntanbricksfärg", + "Badge Down Color": "Nerbricksfärg", + "Badge Maintenance Color": "Underhållsbricksfärg", + "Badge Warn Color": "Varningsbricksfärg", + "Badge Style": "Brickstil", + "Badge value (For Testing only.)": "Brickvärde (endast för prov.)", + "Badge URL": "BrickURL", + "nostrRelays": "Nostr-relän", + "Badge Label Color": "Bricketikettsfärg", + "Free Mobile API Key": "Fri mobil-API-nyckel", + "Proto Service Name": "Proto tjänstenamn", + "Proto Method": "Proto-metod", + "Proto Content": "Proto-innehåll", + "SendKey": "Skickanyckel", + "SMSManager API Docs": "SMSManager API-dokumentation", + "promosmsSMSSender": "SMS-avsändarnamn: Förregistrerat namn eller en av standard: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Octopush API Version": "Octopuch API-version", + "Legacy Octopush-DM": "Äldre Octopush-DM", + "ntfy Topic": "ntfy-ämne", + "Server URL should not contain the nfty topic": "Server-URL ska inte innehålla ntfy-ämne", + "onebotHttpAddress": "OneBot HTTP-adress", + "onebotMessageType": "OneBot meddelandetyp", + "onebotSafetyTips": "För säkerhet, måste ange accesstoken", + "PushDeer Server": "PushDeer-server", + "PushDeer Key": "PuchDeer-nyckel", + "wayToGetPagerTreeIntegrationURL": "Efter att du skapat Uptime Kuma integrationen i PagerTree, kopiera endpoint. Se detaljer {0}", + "twilioAccountSID": "Konto-SID", + "Monitor Setting": "{0}s övervakarinställning", + "Show Clickable Link Description": "Om markerad har alla med access till denna statussida även access till övervakarURL.", + "Open Badge Generator": "Öppna brickgenerator", + "Badge Generator": "{0}s brickgenerator", + "Badge Type": "Bricktyp", + "Badge Duration (in hours)": "Brickvaraktighet (i timmar)", + "Badge Label": "Bricketikett", + "Badge Prefix": "Brickvärdeprefix", + "Badge Suffix": "Brickvärdesuffix", + "Badge Color": "Brickfärg", + "monitorToastMessagesLabel": "Övervakar-toastnotifiering", + "wayToGetFlashDutyKey": "För att integrera Uptime Kuma med Flashduty: Gå till kanaler > Välj en kanal > Integrationer > Lägg till ny integration, välj Uptime Kuma och kopiera pushadressen.", + "gamedigGuessPortDescription": "Porten som används av Valve-serverns förfrågningsprotokol kan vara annorlunda från klientporten. Prova detta om övervakaren inte kan kontakta din server.", + "monitorToastMessagesDescription": "Toastnotifieringar för övervakare försvinner efter angiven tid i sekunder. Ange -1 för att inaktivera timeout. Ange 0 för att inkaktivera toastnotifieringar.", + "Mechanism": "Mekanism", + "Pick a SASL Mechanism...": "Välj en SASL-mekanism…", + "Authorization Identity": "Behörighetsidentitet", + "AccessKey Id": "Accessnyckel-ID", + "Secret AccessKey": "Hemlig accessnyckel", + "toastErrorTimeout": "Timeout för felnotifieringar", + "toastSuccessTimeout": "Timeout för lyckadesnotifieringar", + "Enter the list of brokers": "Ange en lista av mäklare", + "Kafka Brokers": "Kafka-mäklare", + "Press Enter to add broker": "Tryck enter för att lägga till mäklare", + "Kafka Topic Name": "Kafka ämnesnamn", + "Kafka Producer Message": "Kafka producentmeddelande", + "Enable Kafka SSL": "Aktivera Kafka SSL", + "Enable Kafka Producer Auto Topic Creation": "Aktivera Kafka producent autoämnesskapande", + "Kafka SASL Options": "Kafka SASL val", + "Request Body": "Förfrågningsbody", + "nostrRelaysHelp": "En relä-URL per rad", + "nostrSender": "Privat avsändarnyckel (nsec)", + "nostrRecipients": "Publik mottagarnyckel (npub)", + "nostrRecipientsHelp": "npub-format, en per rad", + "gamedigGuessPort": "Gamedig: Gissa port", + "twilioAuthToken": "Auth-token / API-nyckelhemlighet", + "What is a Remote Browser?": "Vad är en fjärrläsare?", + "Channel access token (Long-lived)": "Kanalaccess-token (långtids)", + "Your User ID": "Ditt användarID", + "wayToGetHeiiOnCallDetails": "Hur man får Trigger ID och API-nycklar är förklarat i {dokumentationen}", + "documentationOf": "{0} Dokumentation", + "gtxMessagingFromHint": "På mobiltelefoner, dina mottagare ser TPOA visad som avsändare av meddelandet. 11 alfanumeriska tecken är tillåtet, en kortkod, den lokala långkoden eller internationella nummer ({e164}, {e212} or {e214})", + "gtxMessagingApiKeyHint": "Du kan hitta dina API-nycklar i: Mina routningkonton > Visa kontoinformation > API-referenser > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Från telefonnummer / Transmission Path Originating Address (TPOA)", + "To Phone Number": "Till telefonnummer", + "gtxMessagingToHint": "Internationellt format med inledande \"0\" ({e164}, {e212} or {e214})", + "Alphanumeric (recommended)": "Alfanumeriskt (rekommenderat)", + "Originator type": "Avsändartyp", + "cellsyntOriginator": "Synligt på mottagarens mobiltelefon som avsändare av meddelandet. Tillåtna värden och funktioner beror på avsändartyp.", + "cellsyntDestination": "Mottagarens telefonnummer i internationellt format med inledande 00 följt av landskod, ex. 00701234567 för SE nummer 070-123 45 67 (max 17 siffror totalt). Max 25000 kommaseparerade mottagare per HTTP-förfrågan.", + "callMeBotGet": "Här kan du generera en slutpunkt för {0}, {1} och {2}. Var uppmärksam på att du kan få en anslutningsbegränsning. Anslutningsbegränsningarna ser ut som: {3}", + "Telephone number": "Telefonnummer", + "Originator": "Avsändare", + "cellsyntOriginatortypeAlphanumeric": "Alfanumerisk sträng (max 11 alfanumeriska tecken). Mottagare kan inte svara på meddelandet.", + "Destination": "Destination", + "Allow Long SMS": "Tillåt långa SMS", + "cellsyntSplitLongMessages": "Dela långa meddelanden i upp till 6 delar. 153 x 6 = 918 tecken.", + "max 15 digits": "max 15 siffor", + "max 11 alphanumeric characters": "max 11 alfanumeriska tecken", + "cellsyntOriginatortypeNumeric": "Numeriskt värde (max 15 siffror) med telefonnummer i internationellt format utan inledande 00 (exempel SE nummer 070-123 45 67 ska anges som 46701234567). Mottagare kan svara på meddelandet.", + "wayToWriteWhapiRecipient": "Telefonnummer med internationellt prefix, men utan plustecken i början ({0}, KontaktID ({1}) eller GruppID ({2}).", + "wayToGetWhapiUrlAndToken": "Du kan hitta API URL och token genom att gå till din föredragna kanal från {0}", + "whapiRecipient": "Telefonnummer / Kontakt ID / Grupp ID", + "API URL": "API URL", + "Mentioning": "Omnämnande", + "Don't mention people": "Omnämn inte människor", + "Mention group": "Omnämn {group}", + "senderSevenIO": "Skickar nummer eller namn", + "receiverSevenIO": "Mottagande nummer", + "receiverInfoSevenIO": "Om det mottagande numret inte är lokaliserat i Tyskland, måste du lägga till landskoden för numret (ex. för landskod 46 från Sverige, använd 46123123123 istället för 0123123123)", + "apiKeySevenIO": "SevenIO api-nyckel", + "wayToGetSevenIOApiKey": "Besök kontrollpanelen under app.seven.io > utvecklare > api-nyckel > den gröna lägg till knappen", + "locally configured mail transfer agent": "lokalt konfigurerad mejlskickningsagent", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Endera värdnamnet på servern du vill ansluta till eller {localhost} om du vill använda {local_mta}", + "Host URL": "Värd-URL", + "whatHappensAtForumPost": "Skapa ett nytt foruminlägg. Detta postar inte meddelande i befintliga inlägg. För att posta i befintligt inlägg, använd \"{option}\"", + "mongodbCommandDescription": "Kör ett MongoDB-kommando mot databasen. För information om tillgängliga kommandon, läs i {documentation}", + "Command": "Kommando", + "smspartnerApiurl": "Du hittar din API-nyckel i infopanelen på {0}", + "smspartnerPhoneNumber": "Telefonnummer", + "smspartnerSenderName": "SMS-avsändarnamn", + "smspartnerPhoneNumberHelptext": "Numret måste vara i internationellt format {0}, {1}. Flera nummer måste vara separerade med {2}", + "smspartnerSenderNameInfo": "Måste vara mellan 3..=11 vanliga tecken", + "Bitrix24 Webhook URL": "Bitrix24 Webbhook URL", + "wayToGetBitrix24Webhook": "Du kan skapa en webbhook genom att följa stegen på {0}", + "bitrix24SupportUserID": "Ange ditt användarID i Bitrix24. Du kan hitta ditt ID från länken genom att gå till användarprofilen.", + "Send to channel": "Skicka till kanal", + "Create new forum post": "Skapa nytt foruminlägg", + "postToExistingThread": "Posta till befintlig tråd / foruminlägg", + "Select message type": "Välj meddelandetyp", + "forumPostName": "Inläggsnamn i forum", + "threadForumPostID": "Tråd / inläggs-ID", + "e.g. {discordThreadID}": "ex. {discordThreadID}", + "Refresh Interval": "Uppdateringsintervall", + "Refresh Interval Description": "Statussidan uppdateras varje {0} sekunder", + "ignoreTLSErrorGeneral": "Ignorera TLS/SSL fel för anslutningen", + "wayToGetDiscordThreadId": "Hitta ett post- / forum-ID är liknande att hitta ett kanal-ID. Läs mer om hur du hittar IDn {0}", + "threemaRecipient": "Mottagare", + "threemaRecipientType": "Mottagartyp", + "threemaRecipientTypeIdentityFormat": "8 tecken", + "threemaRecipientTypePhone": "Telefonnummer", + "threemaRecipientTypePhoneFormat": "E.164, utan inledande +", + "threemaRecipientTypeEmail": "Epostadress", + "threemaSenderIdentity": "Gateway-ID", + "wayToGetThreemaGateway": "Du kan registrera dig för Threema Gateway {0}.", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaSenderIdentityFormat": "8 tecken, startar oftast med *", + "threemaApiAuthenticationSecret": "Gateway-ID hemlighet", + "threemaBasicModeInfo": "Notera: Denna integration använder Threema Gateway i standardläge (server-baserad kryptering). Mer detaljer kan hittas {0}.", + "apiKeysDisabledMsg": "API-nycklar är inaktiverade för att autentisering är inaktiverad.", + "CopyToClipboardError": "Kunde inte kopiera till urklipp: {fel}", + "Message format": "Meddelandeformat", + "Condition": "Villkor", + "SNMP Version": "SNMP-version", + "No tags found.": "Inga taggar hittades.", + "now": "nu", + "time ago": "{0} sedan", + "-year": "-år", + "Json Query Expression": "Json-frågeutryck", + "and": "och", + "Recipient Type": "Mottagartyp", + "Private Number": "Privat nummer", + "New Group": "Ny grupp", + "Group Name": "Gruppnamn", + "OAuth2: Client Credentials": "OAuth2: Klientuppgifter", + "Authentication Method": "Autentiseringsmetod", + "Authorization Header": "Autentiseringshuvud", + "Client ID": "Klient ID", + "Client Secret": "Klienthemlighet", + "Go back to home page.": "Gå tillbaka till hemsidan.", + "ignoredTLSError": "TLS/SSL-fel har ignorerats", + "Debug": "Felsöka", + "Copy": "Kopiera", + "CopyToClipboardSuccess": "Kopierat!", + "firewalls": "brandväggar", + "dns resolvers": "dns-upplösare", + "docker networks": "docker-nätverk", + "cacheBusterParam": "Lägg till {0} parameter", + "Community String": "Gruppsträng", + "OID (Object Identifier)": "OID (Objektsidentifierare)", + "Please enter a valid OID.": "Ange ett giltigt OID.", + "Group ID": "GruppID", + "Add Remote Browser": "Lägg till fjärrbläddrare", + "Path": "Sökväg", + "mqttHostnameTip": "Vänligen använd detta format {hostnameFormat}", + "templateServiceName": "tjänstnamn", + "templateHostnameOrURL": "värdnamn eller URL", + "templateStatus": "status", + "defaultFriendlyName": "Ny övervakare", + "Add Tags": "Lägg till taggar", + "tagAlreadyOnMonitor": "Denna tagg (namn och värde) finns redan på övervakaren eller är köad för tillägg.", + "conditionAdd": "Lägg till villkor", + "not ends with": "slutar inte med", + "greater than or equal to": "större än eller lika med", + "record": "registrera", + "contains": "Innehåller", + "Separate multiple email addresses with commas": "Separera flera e-postadresser med kommatecken", + "SpugPush Template Code": "Mallkod", + "smseagleGroupV2": "Telefonboksgrupp-ID(n)", + "telegramUseTemplate": "Använd anpassad meddelandemall", + "brevoLeaveBlankForDefaultName": "lämna tomt för standardnamn", + "Session Initiation Protocol": "WebSocket-transport för SIP (Session Initiation Protocol)", + "RabbitMQ Nodes": "RabbitMQ-hanteringsnoder", + "pingPerRequestTimeoutLabel": "Per-Ping timeout", + "Ip Family": "IP-familj", + "Manual": "Manuell", + "Notifications Enabled": "Notifieringar aktiva", + "Allow Notifications": "Tillåt notifieringar", + "Browser not supported": "Webbläsare stöds inte", + "Maximum Retries": "Maximalt antal omförsök", + "auto-select": "Välj automatiskt", + "Correct": "Korrekt", + "Font Twemoji by Twitter licensed under": "Font Twemoji av Twitter licensierad under", + "Phone numbers": "Telefonnummer", + "Sender name": "Avsändarnamn", + "the smsplanet documentation": "smsplanet-dokumentationen", + "smsplanetApiToken": "Token för SMSPlanet API:et", + "Template ID": "Mall-ID", + "Recipient Numbers": "Mottagarantal", + "brevoApiKey": "Brevo API-nyckel", + "YZJ Webhook URL": "YZJ-Webhook-URL", + "YZJ Robot Token": "YZJ Robot-token", + "HTTP Method": "HTTP-metod", + "cacheBusterParamDescription": "Slumpmässigt genererad parameter för att skippa caches.", + "OAuth Scope": "OAuth omfattning", + "OAuth Audience": "OAuth-publik", + "OAuth Token URL": "OAuth-token-URL", + "Fail": "Fel", + "Harp": "Harp", + "Bubble": "Bubbla", + "Doorbell": "Dörrklocka", + "brevoFromEmail": "Från epost", + "rabbitmqNodesRequired": "Vänligen ange noderna för den här övervakaren.", + "Guitar": "Gitarr", + "Use HTML for custom E-mail body": "Använd HTML för anpassad e-posttext", + "RabbitMQ Username": "RabbitMQ Användarnamn", + "brevoToEmail": "Till epost", + "SendGrid API Key": "SendGrid API-nyckel", + "OneChatAccessToken": "OneChat-åtkomsttoken", + "Clear All Events": "Rensa alla händelser", + "OneChatBotId": "OneChat bott-ID", + "Ignore Sec-WebSocket-Accept header": "Ignorera {0} header", + "jsflow": "jsFlow pubsub/queue-protokoll", + "Push Channel Protocol": "Push Channel Protokoll", + "Declarative Resource Protocol": "Deklarativt resursprotokoll", + "Miele Cloud Connect Protocol": "Miele Cloud Connect-protokoll", + "Softvelum Low Delay Protocol": "Softvelum-protokoll med låg fördröjning", + "OPC UA Connection Protocol": "OPC UA-anslutningsprotokoll", + "OPC UA JSON Encoding": "OPC UA JSON-kodning", + "Constrained Application Protocol": "Begränsat applikationsprotokoll (CoAP)", + "Softvelum WebSocket signaling protocol": "Softvelum WebSocket-signalprotokoll", + "Web Process Control Protocol": "Web Process Control Protocol (WPCP)", + "Smart Home IP": "SHIP - Smart Home IP", + "Cobra Real Time Messaging Protocol": "Cobra realtidsmeddelandeprotokoll", + "BACnet Secure Connect Hub Connection": "BACnet Secure Connect Hub-anslutning", + "WebSocket Application Messaging Protocol": "WAMP (The WebSocket Application Messaging Protocol)", + "Advanced Message Queuing Protocol": "Advanced Message Queuing Protocol (AMQP) 1.0+", + "Reverse Web Process Control": "Reverse Web Process Control Protocol (RWPCP)", + "Swindon Web Server Protocol": "Swindon Web Server Protocol (JSON encoding)", + "Broadband Forum User Services Platform": "USP (Broadband Forum User Services Platform)", + "BACnet Secure Connect Direct Connection": "BACnet Secure Connect Direktanslutning", + "ITU-T T.140 Real-Time Text": "ITU-T T.140 realtidstext", + "Done.best IoT Protocol": "Done.best IoT-protokoll", + "Text IRC Protocol": "Text IRC-protokoll", + "Binary IRC Protocol": "Binärt IRC-protokoll", + "Collection Update": "Samlingsuppdateringen av Websocket-underprotokollet", + "smseagleMsgType": "Meddelandetyp", + "smseagleMsgRing": "Ringsamtal", + "smseagleMsgTts": "Text-till-tal samtal", + "smseagleMsgSms": "SMS-meddelande (standard)", + "smseagleMsgTtsAdvanced": "Text-till-tal avancerat samtal", + "smseagleDuration": "Varaktighet (i sekunder)", + "smseagleApiv2": "APIv2 (rekommenderas för nya integrationer)", + "smseagleDocs": "Kontrollera dokumentation eller APIv2-tillgänglighet: {0}", + "smseagleComma": "Flera måste separeras med kommatecken", + "Optional: Space separated list of scopes": "Valfritt: Mellanslagsavgränsad lista över omfång", + "Cannot connect to the socket server.": "Kan inte ansluta till socket-servern.", + "not starts with": "börjar inte med", + "Sound": "Ljud", + "greater than": "större än", + "Alphanumerical string and hyphens only": "Endast alfanumeriska strängar och bindestreck", + "Arcade": "Arkad", + "Flute": "Flöjt", + "Clear": "Rensa", + "Money": "Pengar", + "Scifi": "Scifi", + "pingCountDescription": "Antal paket att skicka innan stopp", + "mqttWebSocketPath": "MQTT WebSocket-sökväg", + "mqttWebsocketPathInvalid": "Använd ett giltigt WebSocket-sökvägsformat", + "wayToGetBaleToken": "Du kan få en token från {0}.", + "smseagleApiType": "API-version", + "RabbitMQ Password": "RabbitMQ Lösenord", + "telegramServerUrl": "(Valfritt) Server-URL", + "twilioMessagingServiceSID": "Meddelandetjänstens SID (valfritt)", + "showOnlyLastHeartbeat": "Visa endast senaste hjärtslag", + "pingNumericLabel": "Numerisk utdata", + "Events cleared successfully": "Händelser har rensats.", + "No monitors found": "Inga övervakare hittades.", + "Could not clear events": "Kunde inte rensa {failed}/{total} händelser", + "jsonQueryDescription": "Analysera och extrahera specifik data från serverns JSON-svar genom en JSON-fråga eller använd \"$\" för det råa svaret om du inte förväntas JSON. Resultatet jämförs sedan med det förväntade värdet som en sträng. Se {0} för dokumentation och använd {1} för att experimentera med frågor.", + "Template plain text instead of using cards": "Mall för vanlig text istället för att använda kort", + "Dingtalk Mobile List": "Mobillista", + "Mention Mobile List": "Nämn mobillista", + "Dingtalk User List": "Användar-ID lista", + "Invalid mobile": "Ogiltig mobil [{mobile}]", + "Invalid userId": "Ogiltigt användar-ID [{userId}]", + "Mention User List": "Nämn användar-ID-lista", + "Enter a list of userId": "Ange en lista med användar-ID:n", + "Enter a list of mobile": "Ange en lista över mobila enheter", + "smseagleContactV2": "Kontakt-ID i telefonboken", + "FlashDuty Push URL": "Push-URL", + "Send rich messages": "Skicka formaterade meddelanden", + "FlashDuty Push URL Placeholder": "Kopiera från sidan för aviseringsintegration", + "Lost connection to the socket server.": "Tappade anslutning till socket-servern.", + "signl4Docs": "Du hittar mer information om hur du konfigurerar SIGNL4 och hur du införskaffar SIGNL4 webhook URL i {0}.", + "Conditions": "Villkor", + "conditionDelete": "Ta bort villkor", + "conditionAddGroup": "Lägg till grupp", + "conditionDeleteGroup": "Ta bort grupp", + "conditionValuePlaceholder": "Värde", + "not contains": "innehåller inte", + "starts with": "börjar med", + "ends with": "slutar med", + "less than": "mindre än", + "From": "Från", + "SIGNL4": "SIGNL4", + "equals": "lika med", + "Elevator": "Hiss", + "not equals": "Inte lika med", + "evolutionInstanceName": "Instansnamn", + "brevoFromName": "Från namn", + "brevoCcEmail": "Kopia epost", + "brevoBccEmail": "Blind kopia epost", + "SIGNL4 Webhook URL": "SIGNL4 Webhook-URL", + "Can be found on:": "Finns på: {0}", + "brevoApiHelp": "Skapa en API-nyckel här: {0}", + "Add Another Tag": "Lägg till ytterligare en tagg", + "Clear Form": "Rensa formulär", + "pause": "Pausa", + "wahaSession": "Session", + "brevoSubject": "Ämne", + "Plain Text": "Ren text", + "Template Format": "Mallformat", + "pingCountLabel": "Max paket", + "pingGlobalTimeoutLabel": "Global timeout", + "Custom URL": "Egen URL", + "Nextcloud host": "Nextcloud-värd", + "Conversation token": "Konversationstoken", + "Bot secret": "Botthemlighet", + "Happy Eyeballs algorithm": "Happy Eyeballs algoritm", + "Send UP silently": "Skicka UPP tyst", + "wayToGetWahaApiUrl": "Din WAHA-instans-URL.", + "Disable URL in Notification": "Inaktivera URL i meddelande", + "Staged Tags for Batch Add": "Etappade taggar för batchtillägg", + "brevoLeaveBlankForDefaultSubject": "lämna tomt för standardämne", + "OneChatUserIdOrGroupId": "OneChat användar-ID eller grupp-ID", + "brevoSeparateMultipleEmails": "Separera flera e-postadresser med kommatecken", + "Send DOWN silently": "Skicka NERE tyst", + "Reveal": "Visa", + "Clone Maintenance": "Klonunderhåll", + "ariaPauseMaintenance": "Pausa detta underhållsschema", + "ariaResumeMaintenance": "Återuppta detta underhållsschema", + "ariaEditMaintenance": "Redigera detta underhållsschema", + "ariaDeleteMaintenance": "Ta bort detta underhållsschema", + "ariaCloneMaintenance": "Skapa en kopia av detta underhållsschema", + "smseagleTtsModel": "Text-till-tal-modell-ID", + "smseagleApiv1": "APIv1 (för befintliga projekt och bakåtkompatibilitet)", + "ntfyPriorityDown": "Prioritet för NERE-händelse", + "less than or equal to": "mindre än eller lika med", + "Notification Channel": "Notifieringskanal", + "Message Template": "Meddelandemall", + "wayToGetOnesenderUrlandToken": "Du får URL och Token genom att gå till Onesenders hemsida. Mer info {0}", + "Host Onesender": "Värd Onesender", + "Token Onesender": "Token Onesender", + "Form Data Body": "Formulärdatakropp", + "Pop": "Pop", + "Time Sensitive (iOS Only)": "Tidskänslig (endast iOS)", + "Custom sound to override default notification sound": "Anpassat ljud för att åsidosätta standardmeddelandeljudet", + "rabbitmqNodesDescription": "Ange URL:en för RabbitMQ-hanteringsnoderna inklusive protokoll och port. Exempel: {0}", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Antingen ett avsändar-ID för SMS eller ett telefonnummer i E.164-format om du vill kunna ta emot svar.", + "pingPerRequestTimeoutDescription": "Detta är den maximala väntetiden (i sekunder) innan ett enskilt pingpaket anses vara förlorat", + "WebSocket Transport for JMAP": "WebSocket-transport för JMAP (JSON Meta Application Protocol)", + "telegramUseTemplateDescription": "Om aktiverat skickas meddelandet med en anpassad mall.", + "pingGlobalTimeoutDescription": "Total tid i sekunder innan pingen slutar, oavsett skickade paket", + "descriptionHelpText": "Visas på den interna infopanelen. Markdown är tillåtet och saneras (bevarar mellanslag och indrag) före visning.", + "aboutSlackUsername": "Ändrar visningsnamnet för meddelandets avsändare. Om du vill nämna någon, inkludera det i det vänliga namnet istället.", + "telegramServerUrlDescription": "För att häva Telegrams bot-API-begränsningar eller få åtkomst i blockerade områden (Kina, Iran, etc.). För mer information klicka på {0}. Standard: {1}", + "Number of retry attempts if webhook fails": "Antal återförsök (var 60–180:e sekund) om webhooken misslyckas.", + "smsplanetApiDocs": "Detaljerad information om hur man hämtar API-tokens finns i {the_smsplanet_documentation}.", + "wayToGetClickSMSIRTemplateID": "Din mall måste innehålla ett {uptkumaalert}-fält. Du kan skapa en ny mall {här}.", + "webhookGetMethodDesc": "GET skickar data som frågeparametrar och tillåter inte konfigurering av en brödtext. Användbart för att utlösa Uptime Kuma Push-övervakare.", + "webhookPostMethodDesc": "POST fungerar bra för de flesta moderna HTTP-servrar.", + "rabbitmqHelpText": "För att använda övervakaren måste du aktivera Management Plugin i din RabbitMQ-installation. För mer information, vänligen se {rabitmq_documentation}.", + "deleteGroupMsg": "Är du säker på att du vill ta bort den här gruppen?", + "Network API for Notification Channel": "OMA RESTful Network API för aviseringskanal", + "Message Session Relay Protocol": "WebSocket-transport för MSRP (Message Session Relay Protocol)", + "Binary Floor Control Protocol": "WebSocket-transport för BFCP (Binary Floor Control Protocol)", + "wsSubprotocolDescription": "För mer information om delprotokoll, vänligen se {dokumentationen}", + "Extensible Messaging and Presence Protocol": "WebSocket-transport för Extensible Messaging and Presence Protocol (XMPP)", + "ignoreSecWebsocketAcceptHeaderDescription": "Tillåter att servern inte svarar med Sec-WebSocket-Accept-headern om websocket-uppgraderingen lyckas.", + "Penguin Statistics Live Protocol v3": "Penguin Statistics Live Protocol v3 (Protobuf-kodning)", + "mqttWebsocketPathExplanation": "WebSocket-sökväg för MQTT över WebSocket-anslutningar (t.ex. /mqtt)", + "shrinkDatabaseDescriptionSqlite": "Aktivera databasen {vacuum} för SQLite. {auto_vacuum} är redan aktiverat men detta defragmenterar inte databasen eller packar om enskilda databassidor på samma sätt som kommandot {vacuum} gör.", + "snmpOIDHelptext": "Ange OID för den sensor eller status du vill övervaka. Använd nätverkshanteringsverktyg som MIB-webbläsare eller SNMP-programvara om du är osäker på OID.", + "Optional: The audience to request the JWT for": "Valfritt: Målgruppen att begära JWT för", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Tidskänsliga aviseringar levereras omedelbart, även om enheten är i stör ej-läge.", + "supportBaleChatID": "Stöd för direktchatt / grupp / kanals chatt-ID", + "wayToGetBaleChatID": "Du kan få ditt chatt-ID genom att skicka ett meddelande till boten och gå till den här URL:en för att se chat_id:n:", + "clearAllEventsMsg": "Är du säker på att du vill ta bort alla händelser för alla övervakare? Detta tar bort tillhörande meddelandedata permanent och kan inte ångras.", + "certHostnameMismatch": "Certifikatets värdnamn matchar inte övervakarens URL.", + "telegramTemplateFormatDescription": "Telegram tillåter användning av olika markupspråk för meddelanden, se Telegram {0} för specifika detaljer.", + "deleteChildrenMonitors": "Ta även bort de direkta underordnade övervakarna och dess underordnade om det finns några | Ta även bort alla {count} direkta underordnade övervakare och deras underordnade om det finns några", + "issueWithGoogleChatOnAndroidHelptext": "Detta gör det också möjligt att kringgå buggar uppströms som {issuetackerURL}", + "twilloMessagingServiceSIDHelptext": "Ange ditt meddelandetjänst-SID här om du använder {twillo_messaging_service_help_link} för att hantera avsändare och funktioner", + "twilioApiKeyHelptext": "API-nyckeln är valfri men rekommenderas. Du kan ange antingen konto-SID och AuthToken från TwilioConsole-sidan eller konto-SID och paret API-nyckel och API-nyckelhemlighet", + "evolutionRecipient": "Telefonnummer / Kontakt-ID / Grupp-ID", + "The phone number of the recipient in E.164 format.": "Mottagarens telefonnummer i E.164-format.", + "rabbitmqNodesInvalid": "Använd en fullständigt kvalificerad (som börjar med 'http') URL för RabbitMQ-noder.", + "wayToGetEvolutionUrlAndToken": "Du kan få API-URL:en och token genom att gå in i önskad kanal från {0}", + "snmpCommunityStringHelptext": "Den här strängen fungerar som ett lösenord för att autentisera och kontrollera åtkomst till SNMP-aktiverade enheter. Matcha den med din SNMP-enhets konfiguration.", + "wayToWriteEvolutionRecipient": "Telefonnumret med det internationella prefixet, men utan plustecknet i början ({0}), kontakt-ID ({1}) eller grupp-ID ({2}).", + "smsplanetNeedToApproveName": "Måste godkännas i klientpanelen", + "ipFamilyDescriptionAutoSelect": "Använder {happyEyeballs} för att bestämma IP-familjen.", + "wahaChatId": "Chatt-ID (Telefonnummer / Kontakt-ID / Grupp-ID)", + "pingNumericDescription": "Om markerad kommer IP-adresser att matas ut istället för symboliska värdnamn", + "pingIntervalAdjustedInfo": "Intervall justeras baserat på paketantal, global timeout och timeout per ping", + "wayToGetWahaApiKey": "API-nyckeln är miljövariabelvärdet WHATSAPP_API_KEY som du använde för att köra WAHA.", + "customUrlDescription": "Kommer att användas som klickbar URL istället för övervakarens.", + "wayToGetWahaSession": "Från den här sessionen skickar WAHA aviseringar till Chat ID. Du hittar det i WAHA Dashboard.", + "smtpHelpText": "'SMTPS' testar att SMTP/TLS fungerar; 'Ignorera TLS' ansluter via klartext; 'STARTTLS' ansluter, utfärdar ett STARTTLS-kommando och verifierar servercertifikatet. Inget av dessa skickar ett e-postmeddelande.", + "wayToWriteWahaChatId": "Telefonnumret med det internationella prefixet, men utan plustecknet i början ({0}), kontakt-ID ({1}) eller grupp-ID ({2}). Meddelanden skickas till detta chatt-ID från WAHA-sessionen.", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Att installera en Nextcloud Talk-bot kräver administrativ åtkomst till servern.", + "Unable to get permission to notify": "Det gick inte att få behörighet att meddela (begäran antingen nekad eller ignorerad).", + "Webpush Helptext": "Webbpush fungerar bara med SSL-anslutningar (HTTPS). För iOS-enheter måste webbsidan läggas till på startskärmen i förväg.", + "ntfyPriorityHelptextPriorityHigherThanDown": "Vanlig prioritet bör vara högre än {0} prioritet. Prioritet {1} är högre än {0} prioritet {2}", + "tagAlreadyStaged": "Den här taggen (namn och värde) är redan satt för den här batchen.", + "tagNameExists": "En systemtagg med det här namnet finns redan. Välj den från listan eller använd ett annat namn.", + "groupOnesenderDesc": "Se till att grupp-ID:t är giltigt. För att skicka meddelande till gruppen, t.ex.: 628123456789-342345", + "privateOnesenderDesc": "Se till att telefonnumret är giltigt. För att skicka meddelande till ett privat telefonnummer, t.ex.: 628123456789", + "versionIs": "Version: {version}", + "enableSSL": "Aktivera SSL/TLS", + "mariadbUseSSLHelptext": "Aktivera för att använda en krypterad anslutning till din databas. Krävs för de flesta molndatabaser.", + "mariadbCaCertificateLabel": "CA Certifikat", + "mariadbCaCertificateHelptext": "Klistra in CA-certifikatet i PEM-format för att använda det med självsignerade certifikat. Lämna tomt om din databas använder ett certifikat som signerats av en offentlig CA.", + "unknownDays": "Okända dagar", + "No incidents recorded": "Inga incidenter registrerade", + "Load More": "Ladda mer", + "Loading...": "Laddar...", + "Monitors": "{n} Övervakare| {n} Övervakare", + "days": "{n} dag| {n} dagar", + "hours": "{n} timme | {n} timmar", + "minutes": "{n} minut | {n} minuter", + "minuteShort": "{n} min | {n} min", + "years": "{n} år | {n} år", + "Pin this incident": "Fäst denna incident", + "Only retry if status code check fails": "Försök igen bara om statuskodkontrollen misslyckas", + "retryOnlyOnStatusCodeFailureDescription": "Om aktiverat, kommer ett nytt försök endast göras när HTTP-statuskodkontrollen misslyckas (t.ex. servern är nere). Om statuskodkontrollen går igenom, men JSON-frågan misslyckas, markeras övervakaren omedelbart som nere utan omförsök.", + "wsCodeDescription": "För mer information om statuskoder, vänligen se {rfc6455}", + "Subprotocol(s)": "Delprotokoll(er)", + "saveResponseForNotifications": "Spara lyckade HTTP-svar för aviseringar", + "saveResponseDescription": "Lagrar HTTP-svaret och gör det tillgängligt för aviseringsmallar som {templateVariable}", + "responseMaxLength": "Svarets maxlängd (byte)", + "responseMaxLengthDescription": "Maximal storlek på svarsdata att lagra. Ställ in på 0 för obegränsad. Större svar kommer att avkortas. Standard: 1024 (1 KB)", + "logoutCurrentUser": "Logga ut {username}", + "Resolver Server(s)": "Resolver-server(ar)", + "Incident description": "Incidentbeskrivning", + "Incident not found or access denied": "Incidenten hittades inte eller åtkomst nekad", + "Past Incidents": "Tidigare incidenter", + "Incident title": "Incidenttitel", + "Pinned incidents are shown prominently on the status page": "Fästa incidenter visas tydligt på statussidan", + "Edit Incident": "Redigera incident", + "templateAvailableVariables": "Tillgängliga variabler", + "example": "Exempel", + "Result": "Resultat", + "HeadersInvalidFormatBecause": "Förfrågningsrubrikerna är inte giltiga JSON-filer på grund av {error}", + "saveErrorResponseForNotifications": "Spara HTTP-felsvar för aviseringar", + "certificateExpiryNotificationHelp": "Antalet dagar i förväg kan konfigureras i Inställningar.", + "API Token": "API-token", + "See Jira Cloud Docs": "Se Jira Cloud-dokumentationen", + "notificationSmsServices": "SMS-tjänster", + "notificationEmail": "E-post", + "notificationHomeAutomation": "Hemautomatisering", + "notificationOther": "Övriga integrationer", + "SMTP Security": "SMTP-säkerhet", + "Ignore STARTTLS": "Ignorera STARTTLS", + "Use STARTTLS": "Använd STARTTLS", + "Disable STARTTLS": "Inaktivera STARTTLS", + "Copy the web app URL and paste it above": "Kopiera webbappens URL och klistra in den ovan", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "På grund av operatörens restriktioner, aktivera valfria variabler med risk för utebliven leverans", + "aliyun-template-requirements-and-parameters": "Aliyun SMS-mallen måste innehålla parametrar: {parameters}", + "aliyun-template-optional-parameters": "Valfria parametrar: {parameters}", + "slackIncludeGroupName": "Inkludera övervakarens gruppnamn", + "signalUseTemplate": "Använd en anpassad meddelandemall", + "signalUseTemplateDescription": "Om aktiverat så skickas meddelandet med en anpassad mall. Du kan använda Liquid-mallar för att anpassa notisformatet.", + "monitorTypeGameServer": "Spelserver", + "monitorTypeDatabase": "Databastyp för övervakare", + "monitorTypeSpecial": "Special", + "noMonitorsPausedMsg": "Inga övervakare pausades (ingen var aktiv)", + "see Jira Cloud Docs": "se Jira Cloud-dokumentationen", + "oracledbConnectionString": "Oracle-databas: {connectionString}", + "selectAllMonitorsAria": "Markera alla övervakare", + "deselectAllMonitorsAria": "Avmarkera alla övervakare", + "matrixUseTemplate": "Använd en anpassad meddelandemall", + "Duration (Minutes)": "Varaktighet (minuter)", + "disableSTARTTLSDescription": "Aktivera det här alternativet för SMTP-servrar som inte stöder STARTTLS. Detta skickar e-postmeddelanden över en okrypterad anslutning.", + "resumedMonitorsMsg": "Återupptog {n} övervakare | Återupptog {n} övervakare", + "deletedMonitorsMsg": "Tog bort {n} övervakare | Tog bort {n} övervakare", + "noMonitorsResumedMsg": "Inga övervakare återupptogs (ingen var inaktiv)", + "bulkDeleteErrorMsg": "Kunde inte ta bort {n} övervakare | Kunde inte ta bort {n} övervakare", + "You can divide numbers with commas or semicolons": "Du kan dividera tal med {komma} eller {semikolon}", + "WeCom Mentioned Mobile List": "WeCom nämnd mobillista", + "invalidHostnameOrIP": "Ogiltigt värdnamn eller IP-adress. Värdnamnet måste vara ett giltigt FQDN. Jokertecken får inte användas. Kan dock innehålla understreck eller avslutas med en punkt.", + "homeAssistantNotificationActionHelptext": "En lista över notisåtgärder finns i Home Assistant under \"Inställningar > Verktyg > Åtgärder\". Sök efter \"notify\" för att hitta dina åtgärder. Ange endast delen efter \"notify\". T.ex. för åtgärden \"notify.mobile_app_xyz\" anger du \"mobile_app_xyz\". För inbyggda mobilaviseringar, leta efter \"Skicka en avisering via mobile_app_xyz\" (inte \"Skicka en avisering\").", + "teamsEnableTags": "Inkludera taggar", + "teamsEnableTagsDescription": "Om aktiverat kommer meddelandet att innehålla övervakartaggarna.", + "Google Apps Script Code": "Google Apps Script-kod", + "Copy to Clipboard": "Kopiera till urklipp", + "Bearer Token": "Bärar-token", + "Failed to copy to clipboard": "Kunde inte kopiera till urklipp", + "Google Apps Script Webhook URL": "Google Apps Script Webhook URL", + "Copied to clipboard!": "Kopierad till urklipp!", + "OptionalParameters": "Valfria parametrar", + "Notification Action": "Notisåtgärd", + "wayToGetMaxToken": "Du kan få en MAX-bot-token och annan information från {0}.", + "maxMessenger": "MAX-budbärare", + "matrixUseTemplateDescription": "Om aktiverat så skickas meddelandet med en anpassad mall.", + "wayToGetMaxChatID": "Ange chatt-ID:t i MAX dit meddelanden ska levereras.", + "maxApiUrlDescription": "Base API URL för MAX Messenger. Standard: https://platform-api.max.ru", + "maxTemplateFormatDescription": "MAX messenger stöder vanlig text, Markdown och HTML-formatering.", + "maxUseTemplateDescription": "Om aktiverat skickas meddelandet med en anpassad mall.", + "notificationPushServices": "Push-tjänster", + "notificationIncidentManagement": "Incidenthantering", + "pausedMonitorsMsg": "Pausade {n} övervakare | Pausade {n} övervakare", + "WeCom Mentioned Mobile List Description": "Ange telefonnummer att nämna. Separera flera nummer med kommatecken. Använd {'@'}all för att nämna alla.", + "BodyInvalidFormatBecause": "Innehållet i begäran är inte giltig JSON på grund av {error}", + "steamApiKeyDescriptionAt": "För att övervaka en Steam-spelserver behöver du en Steam Web-API-nyckel. Du kan registrera din API-nyckel på {url}", + "hostnameCannotBeIP": "DNS-värdnamnet får inte vara en IP-adress. Menade du att använda resolverfältet?", + "Resolve": "Räkna ut", + "Resolved": "Uträknad", + "invalidDNSHostname": "Ogiltigt värdnamn. Värdnamnet måste vara ett giltigt FQDN. Kan vara ett jokertecken, innehålla ett understreck eller sluta med en punkt.", + "wildcardOnlyForDNS": "Värdnamn med jokertecken stöds endast för DNS-övervakare.", + "invalidURL": "Ogiltig URL", + "createdAt": "Skapad: {date}", + "lastUpdatedAt": "Senast uppdaterad: {date}", + "lastUpdatedAtFromNow": "Senast uppdaterad: {date} ({fromNow})", + "Actions": "Åtgärder", + "selectedMonitorCountMsg": "vald: {n} | valda: {n}", + "selectMonitorMsg": "Välj övervakare för att utföra åtgärder", + "deleteIncidentMsg": "Är du säker på att du vill radera denna incident?", + "slug is not found": "Slug hittades ej", + "Certificate Chain:": "Certifikatskedja:", + "Leave blank to use status page title": "Lämna tomt för att använda statussidans titel", + "aboutJiraCloudId": "Mer information om Jira Cloud ID: {0}", + "Please input content": "Vänligen ange innehåll", + "Please input title": "Vänligen ange titel", + "dateCreatedAtFromNow": "Skapad: {date} ({fromNow})", + "RSS Title": "RSS-titel", + "Cloud ID": "Moln-ID", + "sipsakPingWarning": "För att kunna använda SIP Options Ping-övervakaren måste du installera Uptime Kuma utan Docker och även installera Sipsak-klienten på din server.", + "Examples:": "Exempel: {0}", + "frontendVersionIs": "Frontend-version: {version}", + "cronScheduleDescription": "Schema: {description}", + "notificationChatPlatforms": "Chattplattformar", + "deleteMonitorsMsg": "Är du säker på att du vill ta bort de valda övervakarna?", + "Sets end time based on start time": "Ställer in sluttid baserat på starttid", + "checkPriceAt": "Kolla priserna för {service} på {url}", + "notificationUniversal": "Universell", + "Jira Service Management": "Jira-tjänsthantering", + "API Version": "API-version", + "Please set start time first": "Ange starttid först", + "vkPeerIdDescription": "Ange mål för VK peer_id. Detta värde skickas till API:et i befintligt skick.", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Ställ in 'Execute as: Me' och 'Who has access: Anyone'", + "Open your Google Spreadsheet": "Öppna din Google Spreadsheet", + "Go to Extensions → Apps Script": "Gå till Extensions → Apps Script", + "Paste the script code (see below)": "Klistra in skriptkoden (se nedan)", + "Click Deploy → New deployment → Web app": "Klicka på Deploy → New deployment → Web app", + "Deploy a Google Apps Script as a web app and paste the URL here": "Distribuera ett Google Apps Script som en webbapp och klistra in URL:en här", + "Quick Setup Guide": "Snabbinstallationsguide", + "pingPacketSizeWarning": "En packet-storlek som är mindre än 16 genererar inte round-trip-tider på Linux", + "Connection Timeout": "Anslutningstimeout", + "maxUseTemplate": "Använd en anpassad meddelandemall", + "noMonitorsSelectedWarning": "Du skapar ett underhållsprojekt utan några associerade övervakare. Är du säker på att du vill fortsätta?", + "noMonitorsOrStatusPagesSelectedError": "Kan inte skapa underhåll utan associerade övervakare eller statussidor", + "Peer ID": "Peer-ID", + "vkApiVersionDescription": "VK API-version som används för förfrågningar. Lämna standardvärdet om du inte specifikt behöver en annan VK API-version för kompatibilitet.", + "vkDontParseLinks": "Inaktivera länkutdrag", + "vkDontParseLinksDescription": "Om aktiverat genererar VK inte länkförhandsvisningar/utdrag.", + "smtpAdditionalHeadersTitle": "Extra rubriker", + "smtpAdditionalHeadersDesc": "Se Nodemailer Custom Headers {0} för instruktioner.", + "slackUseTemplate": "Använd en anpassad meddelandemall", + "serwersmsGroupIdHelptext": "ID- eller grupp-ID:n i Kundpanelen. Dessa identifierare kan laddas ner med hjälp av åtgärdsgrupper/index eller genom att kopiera dem från redigeringsgruppen i Kundpanelen.", + "Access Key": "Accessnyckel", + "ooredooAccessKeyDescription": "Hittas under \"API Access Key\" på din Ooredoo SMS-kontrollpanel. Klistra in den exakt som den visas — den base64-kodas automatiskt innan den skickas.", + "ooredooUsernameDescription": "E-postadressen du använder för att logga in på Ooredoos kontrollpanel för massutskick av SMS.", + "serwersmsRecipientTypePhone": "Telefonnummer", + "serwersmsRecipientTypeGroup": "Grupp", + "serwersmsGroupId": "Grupp-ID", + "slackIncludeGroupNameDescription": "Om aktiverat kommer sökvägen till övervakargruppen att inkluderas i notiser för att hjälpa till att skilja mellan övervakare med samma namn mellan olika grupper.", + "slackUseTemplateDescription": "Om aktiverat skickas meddelandet med en anpassad mall. Du kan använda Liquid-mallar för att inkludera information om övervakargrupper via monitorJSON.path eller monitorJSON.pathName.", + "serwersmsRecipientType": "Mottagartyp", + "legacyOctopushEndpoint": "Äldre Octopush-DM (endpoint: {url})", + "Analytics Script URL": "Analys-script URL", + "Analytics Type": "Analystyp", + "wayToGetEgoSMSToken": "Du kan få ditt EgoSMS API-användarnamn och lösenord från {here}.", + "egosmsSenderDescription": "Avsändar-ID visas för mottagare (max 11 tecken). Standardvärdet är EGOSMS om det är tomt.", + "egosmsPhoneNumberDescription": "Mottagarnummer med landskod, utan + eller 00 (t.ex. 2567XXXXXXXX).", + "Analytics ID": "Analys-ID", + "ntfyCall": "Telefonsamtal", + "wxPusherDownloadApp": "ladda ner appen", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) levererar via den fristående WxPusher-appen på 6 plattformar — Android, iOS, HarmonyOS, Windows, macOS och Linux ({download}). Skanna QR-koden nedan för att få din SPT så är du redo att skicka. Separera flera SPT:er med ett kommatecken (,) — se {docs} för mer information.", + "wxPusherDocs": "officiell guide", + "ntfyUseTemplateDescription": "Aktivera detta för att anpassa notistitlar och meddelanden med hjälp av LiquidJS-mallar", + "Leave blank to use the default Ooredoo endpoint.": "Lämna tomt om du vill använda standardslutpunkten för Ooredoo.", + "WebSocket Options": "WebSocket-alternativ", + "octopushEndpoint": "octopush (endpoint: {url})", + "ntfyCallHelptext": "Ring ett samtal när larmet utlöses. Sätt på \"ja\" för att använda ditt första verifierade nummer, eller ange ett specifikt telefonnummer (t.ex. +12223334444). Kräver ntfy Pro och ett verifierat telefonnummer.", + "ntfyUseTemplate": "Anpassa notismallar", + "telnyxApiKey": "API-nyckel (V2)", + "telnyxPhoneNumberHelptext": "Ditt Telnyx-telefonnummer att skicka från (måste vara registrerat i ditt Telnyx-konto, t.ex. +15551234567)", + "telnyxApiKeyHelptext": "Din Telnyx V2 API-nyckel från Telnyx-portalen under API-nycklar", + "telnyxToNumber": "Till nummer", + "telnyxPhoneNumber": "Från nummer", + "telnyxMessagingProfileIdHelptext": "Koppla eventuellt en Telnyx Messaging-profil för att ställa in avsändarinställningar och funktioner", + "telnyxMessagingProfileId": "Meddelandeprofil-ID (valfritt)", + "telnyxToNumberHelptext": "Telefonnumret för att ta emot SMS-aviseringar (t.ex. +15559876543)", + "Flowtriq Webhook URL": "Webhook-URL", + "flowtriqApiKeyDescription": "Valfri API-nyckel för autentisering av webhook-förfrågningar till din Flowtriq-instans.", + "Flowtriq API Key": "API-nyckel", + "ntfyCustomMessage": "Anpassad meddelandemall", + "ntfyNotificationTemplateFallback": "Lämna tomt för att använda standardformatet för Uptime Kuma", + "Badge Link Generator": "{0} Badge Link Generator", + "Open Badge Link Generator": "Open Badge Link-generator", + "Badge Link Generator Helptext": "Badge-länkar är tillgängliga för alla övervakare som är tilldelade offentliga statussidor. För mer information, se {documentation}.", + "gamedigTokenDescription": "Valfritt. Vissa spelservrar kräver en autentiserings-token.", + "gamedigToken": "Token", + "plivoAuthID": "Autentiserings-ID", + "plivoAuthIDHelptext": "Ditt Plivo Auth-ID från Plivo-konsolen", + "plivoAuthToken": "Auth-token", + "plivoAuthTokenHelptext": "Din Plivo autentiserings-token från Plivo-konsolen", + "plivoFromNumber": "Från nummer", + "plivoFromNumberHelptext": "Plivo-telefonnumret att skicka från (t.ex. +15551234567). SMS kan också använda ett godkänt alfanumeriskt avsändar-ID; röstsamtal kräver ett Plivo-telefonnummer.", + "plivoToNumber": "Till nummer", + "ntfyCustomTitle": "Anpassad titelmall", + "plivoToNumberHelptext": "Telefonnumret som tar emot SMS:et eller röstsamtalet (t.ex. +15559876543)", + "plivoMessageType": "Meddelandetyp", + "plivoVoiceCall": "Röstsamtal", + "plivoAnswerUrl": "Svars-URL", + "plivoAnswerUrlHelptext": "URL som returnerar Plivo XML för anropet. Aviseringstexten läggs till som en ?message= query-parameter, så din slutpunkt kan returnera t.ex. {message}.", + "systemServiceDescription": "Kontrollerar om systemtjänsten {service_name} är aktiv", + "systemServiceDescriptionLinux": "Kontrollerar om Linux systemd-tjänsten {service_name} är aktiv", + "milliseconds": "{n} millisekund | {n} millisekunder", + "Screenshot Delay": "Skärmdumpsfördröjning (väntar {milliseconds})", + "screenshotDelayDescription": "Vänta så här många millisekunder innan du tar skärmdumpen. Maximalt: {maxValueMs}ms (0,5 × intervall).", + "systemServiceName": "Tjänstnamn", + "screenshotDelayWarning": "Högre värden håller webbläsaren öppen längre, vilket kan öka minnesanvändningen med många samtidiga övervakare.", + "systemService": "Systemtjänst", + "discordMessageFormatMinimalist": "Minimalistisk (kort status)", + "message": "meddelande", + "systemServiceDescriptionWindows": "Kontrollerar om Windows Service Manager {service_name} körs", + "systemServiceCommandHint": "Använt kommando: {command}", + "systemServiceExpectedOutput": "Förväntad utdata: \"{0}\"", + "360messengerUseTemplate": "Använd en anpassad meddelandemall", + "360messengerAuthToken": "360messenger API-nyckel", + "360messengerGroupId": "360messenger grupp-ID", + "360messengerRecipient": "Mottagarens telefonnummer", + "360messengerErrorNoApiKey": "Ange din 360messenger API-nyckel först.", + "discordMessageFormat": "Meddelandeformat", + "discordMessageFormatNormal": "Normal (rika inbäddningar)", + "evolutionCustomMessageDesc": "Lämna tomt för att använda standardmeddelandet.", + "evolutionCustomMessageTitle": "Anpassat meddelande (valfritt)", + "resendSubject": "Ämne", + "360messengerErrorNoGroups": "Inga WhatsApp-grupper hittades för det här kontot.", + "360messengerWayToWriteRecipient": "Ange ett eller flera telefonnummer i internationellt format utan inledande plustecken (t.ex. {0}). Separera flera nummer med kommatecken.", + "360messengerErrorApi": "Det gick inte att ladda WhatsApp-grupplistan (Fel {statusCode}: {message}).", + "discordUseMessageTemplate": "Använd anpassad meddelandemall", + "ooredooBearerTokenDescription": "Token som visas efter \"Auktorisering: Bärare\" i exempelkommandot cURL på din Ooredoo SMS-kontrollpanel.", + "ooredooRecipientDescription": "Ange en eller flera siffror separerade med kommatecken eller mellanslag. Lokala 7-siffriga nummer får automatiskt prefixet 960.", + "json_value": "JSON-värde", + "360messengerTemplate": "360messenger-meddelandemall", + "360messengerCustomMessageTemplate": "Anpassad meddelandemall", + "360messengerErrorGeneric": "Det gick inte att ladda WhatsApp-grupplistan: {message}", + "pm2Description": "Kontrollerar om PM2-processen {service_name} är online", + "pm2ExpectedStates": "Förväntad status: online. Tillstånd som stoppad och felaktig behandlas som NERE.", + "resendApiHelp": "Skapa en API-nyckel här {0}", + "resendFromEmail": "Från e-post", + "Select All": "Markera alla", + "Deselect All": "Avmarkera alla", + "Enter the list of nodes": "Ange listan över RabbitMQ-hanteringsnoder", + "Press Enter to add node": "Tryck Enter för att lägga till nod", + "360messengerGroupList": "WhatsApp-grupper", + "360messengerSelectGroupList": "Välj en grupp att lägga till", + "resendFromName": "Från namn", + "resendApiKey": "Skicka om API-nyckel", + "360messengerSelectedGroupID": "Valda grupp-ID:n", + "360messengerEnableSendToGroup": "Aktivera sändning till WhatsApp-grupp(er)", + "snmpV3Username": "SNMPv3 användarnamn", + "resendLeaveBlankForDefaultSubject": "Lämna tomt för standardämne", + "resendToEmail": "Till e-post", + "360messengerMessageTemplate": "Meddelandemall", + "360messengerEnableCustomMessage": "Aktivera en anpassad meddelandemall istället för standardmeddelandet.", + "360messengerWayToGetUrlAndToken": "Du kan få din 360messenger API-nyckel från {0}.", + "Suppress Notifications": "Tysta notiser", + "discordSuppressNotificationsHelptext": "När det är aktiverat publiceras meddelanden på kanalen men de utlöser inte push- eller skrivbordsaviseringar för mottagarna.", + "discordMessageFormatCustom": "Anpassad mall", + "resendLeaveBlankForDefaultName": "lämna tomt för standardnamn", + "openwaSession": "Session", + "openwaChatId": "Chatt-ID (Telefonnummer / Kontakt-ID / Grupp-ID)", + "turbosmtpConsumerSecret": "Konsumenthemlighet", + "turbosmtpConsumerKey": "Konsumentnyckel", + "Hash Key": "Hash-nyckel", + "wayToGetBearSMSToken": "Du kan få ditt BearSMS API-användarnamn och hash-nyckel från {0}.", + "bearsmsPhoneNumberDescription": "Mottagarnummer med landskod, utan + eller 00 (t.ex. 9725XXXXXXXX).", + "bearsmsSenderDescription": "Valfritt avsändar-ID som visas för mottagare (max 11 tecken). Det måste godkännas i ditt BearSMS-konto, annars används kontots standardavsändare.", + "ClickUp API Token": "ClickUp API-token", + "ClickUp Workspace ID": "ClickUp Workspace-ID", + "ClickUp Workspace ID Description": "Du hittar den i din arbetsytas URL, direkt efter app.clickup.com/, t.ex. app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ClickUp kanal-ID", + "ClickUp Channel ID Description": "Du hittar den i chattkanalens URL, direkt efter /chat/r/, t.ex. .../chat/r/abc123-4567.", + "ClickUp API Token Description": "Du kan skapa en personlig åtkomst-token i ClickUp genom att gå till Inställningar -> Integrationer & ClickApps -> ClickUp API -> API-token", + "Pinglet Topic URL": "Ämnes-URL", + "pingletTopicUrlDescription": "Publicerings-URL:en för Pinglet-ämnet att skicka aviseringar till, t.ex. https://app.pinglet.co.uk/ditt-namespace/alerts. Ämnet skapas vid första publiceringen om det inte redan finns.", + "Show this Maintenance Message on which Status Pages": "Visa detta underhållsmeddelande på vilka statussidor", + "Teltonika SMS Gateway": "Teltonika SMS-nätsluss", + "wayToGetOpenwaApiUrl": "Din OpenWA-instans-URL.", + "wayToGetOpenwaSession": "Sessions-ID som används av OpenWA för att skicka notiser.", + "wayToGetOpenwaApiKey": "API-nyckel som används för din OpenWA-instans.", + "GrafanaOncallURL": "Grafana Oncall-URL", + "mtls-auth-server-cert-label": "Cert", + "teltonikaVersionWarning": "Denna notisleverantör kräver att din Teltonika-enhet kör RMS version 7.14.0 eller senare.", + "teltonikaUnsafeTls": "Ignorera certifikatvalidering", + "wayToWriteOpenwaChatId": "Telefonnumret med det internationella prefixet, men utan plustecknet i början ({0}), kontakt-ID ({1}) eller grupp-ID ({2}). Du kan skicka till flera chatt-ID:n genom att separera dem med kommatecken.", + "openwaCustomMessageTitle": "Anpassat meddelande (valfritt)", + "openwaCustomMessageDesc": "Lämna tomt för att använda standardmeddelandet.", + "teltonikaUrlHelptext": "URL:en ska anges som fullständigt ursprung, t.ex. {0} eller {1}.", + "halopsa_webhook_url_desc": "Ange webhook-URL:en från din Halo PSA Integration Runbook (Konfiguration > Integrationer > Anpassade integrationer > Integration Runbooks). Välj \"Kan endast startas från Halo och från en offentlig slutpunkt\" när du skapar webhooken.", + "Fluxer Webhook URL": "Fluxer webhook-URL", + "Location": "Plats", + "Service Name": "Tjänstens namn", + "Protocol": "Protokoll", + "halopsa_setup_step2": "Konfigurera Runbook-åtgärder för att bearbeta aviseringar (t.ex. Skapa ärende)", + "halopsa_setup_step3": "Kopiera Webhook-URL:en och klistra in den i textfältet ovan", + "Message Format": "Meddelandeformat", + "playground": "lekplats", + "Endpoint": "Ändpunkt", + "mtls-auth-server-cert-placeholder": "Cert-innehåll", + "labelDomainExpiry": "Domänens utg.", + "Globalping - Access global monitoring probes": "Globalping - Få åtkomst till globala övervakarsonder", + "smscTranslit": "smscTranslit", + "domainExpiryNotificationHelp": "Antalet dagar i förväg kan konfigureras i Inställningar.", + "GlobalpingLocationDescription": "Platsfältet accepterar kontinenter, länder, regioner, städer, ASN:er, internetleverantörer eller molnregioner. Du kan kombinera filter med {plus} (t.ex. {amazonPlusGermany} eller {comcastPlusCalifornia}). Om latens är ett viktigt mått, använd filter för att begränsa platsen till en liten region för att undvika toppar och för bättre stabilitet, ställ in filtret {datacenter}. {fullDocs}.", + "domainExpiryDescription": "Trigga notis när domännamn löper ut om:", + "domain_expiry_unsupported_missing_target": "Inget giltigt domän- eller värdnamn är konfigurerat för den här övervakaren", + "mariadbSocketPathDetectedHelptext": "Ansluter till databasen enligt anvisningarna i miljövariabeln {0}.", + "halopsa_setup_step5": "Konfigurera runbooken för att använda monitor_id för att matcha aviseringar till befintliga ärenden", + "Details": "Detaljer", + "minimumIntervalWarning": "Intervaller under 20 sekunder kan resultera i dålig prestanda.", + "Expand All Groups": "Öppna alla grupper", + "halopsa_username_desc": "Användarnamn för autentisering med Halo PSA webhook", + "discordMessageTemplate": "Meddelandemall", + "password": "Lösenord", + "username": "Användarnamn", + "mtls-auth-server-key-label": "Nyckel", + "mtls-auth-server-ca-label": "CA", + "halopsa_setup_step4": "Välj grundläggande autentisering och skapa användarnamn och lösenord. Skriv eller klistra in dessa användarnamn och lösenord i textfälten ovan", + "Severity": "Allvarlighetsgrad", + "Region": "Region", + "Sort by certificate expiry": "Sortera efter certifikatets utgångsdatum", + "Splunk Rest URL": "Splunk Rest-URL", + "halopsa_payload_desc": "Följande fält skickas till din Halo PSA-webhook:", + "Webhook Payload Fields": "Webhook-nyttolastfält", + "SSL/TLS": "SSL/TLS", + "Check Type": "Kontrollera typ", + "GRPC Options": "GRPC-alternativ", + "halopsa_field_message": "Fullständigt varningsmeddelande med status och detaljer", + "halopsa_field_timestamp": "Händelsetidsstämpel i ISO 8601-format", + "Halo PSA": "Halo PSA", + "minPing": "Min ping", + "Setup Instructions": "Konfigurationsanvisningar", + "halopsa_setup_step1": "Skapa en integrations-runbook i HaloPSA (Konfiguration → Integrationer → Integrations-runbooks)", + "Collapse All Groups": "Stäng alla grupper", + "halopsa_field_status": "Övervakarstatus: UPPE, NERE, NOTIS eller OKÄND", + "fluxerMessageFormatNormal": "Normal (rika inbäddningar)", + "fluxerMessageFormat": "Meddelandeformat", + "Globalping API Token": "Globalping API-token", + "wayToGetFluxerURL": "Du kan få detta genom att gå till kanalens inställningar > Webhooks > Skapa Webhook > Kopiera webhook-URL.", + "fluxerMessageFormatMinimalist": "Minimalistisk (kort status)", + "halopsa_id_usage_hint": "💡 Tips: Använd monitor_id för att pålitligt matcha aviseringar med ärenden och heartbeat_id för att spåra händelsehistorik", + "GlobalpingHostname": "Ett publikt nåbart mätmål. Vanligtvis ett värdnamn eller en IPv4/IPv6-adress, beroende på mättyp.", + "globalpingApiTokenDescription": "Skaffa din Globalping API-token på {0}.", + "fluxerMessageFormatCustom": "Anpassad mall", + "Select PM2 process": "Välj PM2-process", + "teltonikaPasswordHelptext": "Du kan definiera API-användarens lösenord i din Teltonika-router, t.ex. {0}", + "account settings": "kontoinställningar", + "Check for": "Kolla efter", + "PM2 Process": "PM2-process", + "Refresh": "Uppdatera", + "teltonikaModemHelptext": "SMS-modemets ID måste vara i formatet {0}. Se https://developers.teltonika-networks.com/reference/ för vägledning.", + "fluxerUseMessageTemplate": "Använd en anpassad meddelandemall", + "Expected TLS Alert": "Förväntad TLS-avisering", + "avgPing": "Genomsnittsping", + "expectedTlsAlertDescription": "Välj den TLS-avisering som du förväntar dig att servern ska returnera. Använd {code} för att verifiera att mTLS-slutpunkter avvisar anslutningar utan klientcertifikat. Se {link} för mer information.", + "maxPing": "Max ping", + "TLS Alert Spec": "RFC 8446", + "halopsa_field_uptime_kuma_version": "Uptime Kuma versionsnummer", + "teltonikaUrl": "Din Teltonika-enhets URL", + "Halo PSA Webhook URL": "Halo PSA webhook-URL", + "screenshot of the website": "Skärmdump av webbplatsen", + "Basic radio toggle button group": "Basgrupp av radioknappar", + "Basic checkbox toggle button group": "Basgrupp av kryssrutor med växlingsknappar", + "Sort options": "Sorteringsalternativ", + "passwordTooWeak": "Lösenordet är för svagt. Det ska innehålla både alfabetiska och numeriska tecken. Det måste vara minst 6 tecken långt.", + "TLS Alerts": "TLS-aviseringar", + "Metadata": "Metadata", + "End": "Slut", + "teltonikaPassword": "API-lösenord", + "teltonikaModem": "Modem-ID", + "teltonikaUsername": "API-användarnamn", + "discordUseMessageTemplateDescription": "Om aktiverat, skickas meddelandet med en anpassad mall (LiquidJS). Lämna tomt om du vill använda standardformatet för Uptime Kuma.", + "fluxerMessageTemplate": "Meddelandemall", + "fluxerUseMessageTemplateDescription": "Om aktiverat, skickas meddelandet med en anpassad mall (LiquidJS). Lämna tomt om du vill använda standardformatet för Uptime Kuma.", + "RecordMatch": "Matchning av uppgift", + "RegexMatch": "Ange ett regex som matchar uppgift", + "GlobalpingResolverInfo": "IPv4/IPv6-adress eller ett fullständigt kvalificerat domännamn (FQDN). Standardinställningen är sondens lokala nätverksresolver. Du kan ändra resolverservern när som helst.", + "labelDomainNameExpiryNotification": "Notis om domännamnets utgång", + "lowIntervalWarning": "Är du säker på att du vill ställa in intervallvärdet under 20 sekunder? Prestandan kan försämras, särskilt om det finns ett stort antal övervakare.", + "halopsa_password_desc": "Lösenord för autentisering med Halo PSA webhook", + "Sort by status": "Sortera efter status", + "Sort by name": "Sortera efter namn", + "Sort by uptime": "Sortera efter drifttid", + "mtls-auth-server-key-placeholder": "Nyckel-innehåll", + "mtls-auth-server-ca-placeholder": "Server-CA", + "PushDeer Server URL": "PushDeer server-URL", + "halopsa_field_monitor": "Namnet på övervakaren", + "halopsa_field_monitor_id": "Unik övervakaridentifierare (null för testmeddelanden) – Använd detta för att matcha aviseringar med ärenden", + "To Number": "Till nummer", + "halopsa_field_title": "Aviseringsrubrik (alltid 'Uptime Kuma-avisering')", + "ntpThresholdsTitle": "NTP-tröskelvärden", + "ntpStratumThreshold": "Stratum-tröskelvärde", + "Monitor Subtype": "Övervakarundertyp", + "teltonikaPhoneNumber": "Telefonnummer", + "GlobalpingMultipleLocationsError": "Flera platser stöds ej, använd en enda plats för varje övervakare.", + "GlobalpingMonitorDescription": "Globalping ger tillgång till tusentals community-hostade sonder för att köra nätverkstester och mätningar. En gräns på 250 tester per timme är satt för alla anonyma användare. För att fördubbla gränsen till 500 per timme, vänligen spara din token i {accountSettings}. Se {docs} för mer information.", + "GlobalpingLocationDocs": "Dokumentation för platsinmatning", + "GlobalpingIpFamilyInfo": "IP-versionen som ska användas. Endast tillåtet om målet är ett värdnamn.", + "Never": "Aldrig", + "imageResetConfirmation": "Bilden återställd till standard", + "Uptime Kuma": "Uptime Kuma", + "Clear current filters": "Nollställ aktuella filter", + "domain_expiry_unsupported_monitor_type": "Övervakning av domänutgång stöds inte för den här övervakartypen", + "teltonikaUsernameHelptext": "Rekommendation: Skapa ett separat konto som endast är begränsat till att skicka SMS-meddelanden och ange dess användarnamn här", + "teltonikaUnsafeTlsDescription": "Om du stänger av TLS-certifikatvalidering riskerar du att utsättas för attacker på vägen (man-in-the-middle), vilket potentiellt kan leda till dataläckor och övertagning av system. Stäng inte av certifikatvalidering om du inte accepterar denna attackvektor. Vi rekommenderar att du använder LetsEncrypt med automatisk förnyelse.", + "teltonikaPhoneNumberHelptext": "Numret måste vara i det internationella formatet {0}, {1}. Endast ett nummer är tillåtet.", + "settingsDomainExpiry": "Domänens utgångsdatum", + "domain_expiry_unsupported_is_icann": "Domänen \"{domain}\" är inte en kandidat för övervakning av domänutgångar, eftersom dess offentliga suffix \".{publicSuffix}\" inte hanteras av ICANN", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Övervakning av domänutgång är inte tillgänglig för \".{publicSuffix}\" eftersom ingen RDAP-tjänst listas av IANA", + "None (Successful Connection)": "Ingen (lyckad anslutning)", + "VKTeams Bot Token Description": "Du kan läsa mer om hur du får en bot-token och annan information på {0}.", + "VKTeams Chat Id Description": "För användare är detta deras e-postadress. För grupper och kanaler, hämta ID:t från deras inställningar och lägg till {'@'}chat.agent", + "VKTeams Use Template": "Använd anpassad meddelandemall", + "ntpDispersionThresholdHelp": "Markera som nere om rotspridningen överstiger detta värde. Spridningen indikerar serverns klocknoggrannhet.", + "VKTeams Base Url Description": "VKTeams bas-API-URL kan skilja sig åt i lokal lösning. Standard: https://myteam.mail.ru", + "VKTeams Use Template Description": "Om aktiverat skickas meddelandet med en anpassad mall.", + "VKTeams Template Format Description": "För meddelandeformatering stöder VKTeams vanlig text, MarkdownV2 och HTML-formatering.", + "ntpStratumThresholdHelp": "Markera som nere om stratum är vid eller över detta värde. Stratum indikerar tidskällans kvalitet (1=GPS/atomur, 16=osynkroniserad).", + "ntpOffsetThresholdHelp": "Markera som nere om tidsförskjutningen överstiger detta värde i millisekunder.", + "ntpDispersionThreshold": "Tröskelvärde för rotspridning (ms)", + "ntpOffsetThreshold": "Tröskelvärde för tidsförskjutning (ms)", + "milkyHttpAddress": "Milky HTTP-adress(utan '/api' suffixet)", + "milkyMessageType": "Milky-meddelandetyp", + "milkyGroupMessage": "Grupp", + "milkyPrivateMessage": "Privat", + "milkyUserOrGroupId": "Grupp/användar-ID", + "milkySafetyTips": "Av säkerhetsskäl måste åtkomst-token anges", + "Device ID or Group ID": "Enhets-ID eller grupp-ID", + "notifyAppDeviceIdDescription": "Enhets-ID:t från Notify!-appen, eller ett grupp-ID för att varna alla enheter i en grupp samtidigt.", + "notifyAppSetup": "Öppna Notify! och hitta ditt enhets-ID och din token under Inställningar. Inget konto krävs. Läs mer på {0}.", + "smsgatewayUrlHelptext": "Bas-URL:en för din SMS Gateway, utan en API-sökväg.", + "smsgatewayTo": "Telefonnummer", + "smsgatewayToHelptext": "Kommaavgränsad lista över mottagartelefonnummer i internationellt format (t.ex. +15551234567)", + "Client Key": "Klientnyckel", + "Channel": "Kanal", + "SFTP Path to Check": "SFTP-sökväg att kontrollera", + "sftpPathHelpText": "Valfritt. Om det anges kommer övervakaren att verifiera att den här sökvägen finns på SFTP-servern.", + "SSH Private Key": "SSH privat nyckel", + "Passphrase": "Lösenfras", + "sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----\n...\n-----END OPENSSH PRIVATE KEY-----", + "sshPrivateKeyHelpText": "Klistra in innehållet från din privata nyckelfil (t.ex. id_rsa eller id_ed25519).", + "sshPassphraseHelpText": "Valfritt. Krävs endast om din privata nyckel är krypterad med en lösenfras.", + "optional": "valfri", + "Line Number": "Radnummer", + "Amoot SMS recipient numbers help": "Ange ett eller flera mobilnummer separerade med kommatecken.", + "Use Pattern": "Använd mönster", + "Pattern Code ID": "Mönsterkod-ID", + "Amoot SMS pattern help": "Ange mönsterkod-ID:t från din Amoot SMS-panel. {0}", + "Create an Amoot SMS pattern": "Skapa ett Amoot SMS-mönster", + "Use Own Line for Pattern": "Använd egen rad för mönster", + "Indigo URL": "Indigo-URL", + "indigoUrlHelptext": "Din Indigo Reflector-adress (https://NAME.indigodomo.net) eller Indigo-webbservern i ditt nätverk (https://IP:8176).", + "indigoIgnoreTlsErrorHelptext": "Krävs för Indigo-webbservern i ditt nätverk, som använder ett självsignerat certifikat. Krävs inte för Reflector.", + "indigoApiKeyHelptext": "En API-nyckel från ditt Indigo-konto (under Authorizations) eller en lokal hemlighet för webbservern i ditt nätverk.", + "Variable ID": "Variabel-ID", + "indigoVariableIdHelptext": "Varningsmeddelandet skrivs till denna variabel.", + "Action Group ID": "Åtgärdsgrupps-ID", + "indigoActionGroupIdHelptext": "Utförs efter att variabeln uppdaterats. Ange en variabel, en åtgärdsgrupp eller båda. Kopiera ID:n från Indigo med högerklick → Copy ID." +} diff --git a/src/lang/te.json b/src/lang/te.json new file mode 100644 index 0000000..af5a551 --- /dev/null +++ b/src/lang/te.json @@ -0,0 +1,314 @@ +{ + "languageName": "తెలుగు", + "Settings": "సెట్టింగ్‌లు", + "Dashboard": "డాష్బోర్డ్", + "Help": "సహాయం", + "Language": "భాష", + "Appearance": "ప్రదర్శన", + "Theme": "నేపథ్యం", + "General": "సాధారణ", + "Game": "ఆట", + "Version": "సంస్కరణ", + "Check Update On GitHub": "GitHubలో నవీకరణను తనిఖీ చేయండి", + "List": "జాబితా", + "Home": "స్వస్థల o", + "Add": "జోడించు", + "Quick Stats": "త్వరిత గణాంకాలు", + "Up": "పనిచేస్తుంది", + "Down": "ఆగిపోయింది", + "Pending": "అనిశ్చిత", + "Maintenance": "నిర్వహణ", + "Unknown": "తెలియని స్థితి", + "Reconnecting...": "మళ్లీ కనెక్ట్ అవుతోంది...", + "General Monitor Type": "సాధారణ మానిటర్ రకం", + "Passive Monitor Type": "నిష్క్రియాత్మక మానిటర్ రకం", + "markdownSupported": "మార్క్‌డౌన్ సింటాక్స్‌కు మద్దతు ఉంది", + "Pause": "విరామం", + "Name": "పేరు", + "Status": "స్థితి", + "DateTime": "తేదీ సమయం", + "Message": "సందేశం", + "No important events": "ముఖ్యమైన సంఘటనలు లేవు", + "Resume": "పునఃప్రారంభం", + "Edit": "సవరించు", + "Current": "ప్రస్తుత", + "Uptime": "సమయ వ్యవధి", + "Monitor": "మానిటర్ | మానిటర్లు", + "day": "రోజు | రోజులు", + "-day": "-రోజు", + "hour": "గంట", + "-hour": "-గంట", + "Response": "ప్రతిస్పందన", + "Ping": "పింగ్", + "Keyword": "కీవర్డ్", + "Invert Keyword": "విలోమ కీవర్డ్", + "Expected Value": "అంచనా విలువ", + "Json Query": "Json ప్రశ్న", + "URL": "URL", + "Hostname": "హోస్ట్ పేరు", + "Port": "పోర్ట్", + "Heartbeat Interval": "హృదయ స్పందన విరామం", + "Request Timeout": "అభ్యర్థన ముగిసె గడువు", + "timeoutAfter": "{0} సెకన్ల తర్వాత గడువు ముగిసింది", + "Heartbeat Retry Interval": "హృదయ స్పందన పునఃప్రయత్న విరామం", + "Advanced": "ఆధునిక", + "checkEverySecond": "ప్రతి {0} సెకన్లకు తనిఖీ చేయండి", + "retryCheckEverySecond": "ప్రతి {0} సెకన్లకు మళ్లీ ప్రయత్నించండి", + "resendDisabled": "మల్లిపంపడము అచేతనము చేయబడ్డది", + "ignoreTLSError": "HTTPS వెబ్‌సైట్‌ల కోసం TLS/SSL లోపాన్ని విస్మరించండి", + "maxRedirectDescription": "అనుసరించాల్సిన దారి మళ్లింపుల గరిష్ట సంఖ్య. దారి మళ్లింపులను నిలిపివేయడానికి 0కి సెట్ చేయండి.", + "Upside Down Mode": "అప్‌సైడ్ డౌన్ మోడ్", + "Max. Redirects": "గరిష్టంగా దారి మళ్లింపులు", + "Push URL": "పుష్ URL", + "needPushEvery": "మీరు ప్రతి {0} సెకన్లకు ఈ URLకి కాల్ చేయాలి.", + "Save": "సేవ్ చేయండి", + "Notifications": "నోటిఫికేషన్లు", + "Setup Notification": "నోటిఫికేషన్ సెటప్ చేయండి", + "Light": "కాంతి", + "Dark": "వెలుతురు లేని", + "Auto": "ఆటో", + "Theme - Heartbeat Bar": "థీమ్ - హార్ట్‌బీట్ బార్", + "styleElapsedTimeShowNoLine": "చూపించు (పంక్తి లేదు)", + "styleElapsedTimeShowWithLine": "చూపించు (పంక్తితో)", + "Normal": "సాధారణ", + "Bottom": "దిగువన", + "None": "ఏదీ లేదు", + "Timezone": "సమయమండలం", + "Allow indexing": "ఇండెక్సింగ్‌ని అనుమతించండి", + "Change Password": "పాస్‌వర్డ్ మార్చండి", + "Current Password": "ప్రస్తుత పాస్వర్డ్", + "New Password": "కొత్త పాస్వర్డ్", + "Repeat New Password": "కొత్త పాస్‌వర్డ్‌ని మళ్లీ నమోదు చేయండి", + "Disable Auth": "ప్రామాణీకరణను నిలిపివేయండి", + "Enable Auth": "ప్రామాణీకరణను ప్రారంభించండి", + "Please use this option carefully!": "దయచేసి ఈ ఎంపికను జాగ్రత్తగా ఉపయోగించండి!", + "Logout": "లాగ్అవుట్", + "Leave": "వదిలేయండి", + "Confirm": "నిర్ధారించండి", + "Yes": "అవును", + "No": "లేదు", + "Username": "వినియోగదారు పేరు", + "Password": "పాస్వర్డ్", + "Remember me": "నన్ను గుర్తు పెట్టుకో", + "Login": "లాగిన్", + "add one": "ఒకటి జోడించండి", + "Notification Type": "నోటిఫికేషన్ రకం", + "Email": "ఇమెయిల్", + "Test": "పరీక్షించండి", + "Resolver Server": "రిసోల్వర్ సర్వర్", + "Resource Record Type": "రిసోర్స్ రికార్డ్ రకం", + "Last Result": "చివరి ఫలితం", + "Repeat Password": "పాస్‌వర్డ్‌ని మళ్లీ నమోదు చేయండి", + "Import Backup": "బ్యాకప్‌ని దిగుమతి చేయండి", + "Export": "ఎగుమతి", + "Import": "దిగుమతి", + "respTime": "ప్రతిస్పందన. సమయం (మిసె)", + "Default enabled": "డిఫాల్ట్ ప్రారంభించబడింది", + "Create": "సృష్టించు", + "Clear Data": "డేటాను క్లియర్ చేయండి", + "Events": "ఈవెంట్స్", + "Heartbeats": "హృదయ స్పందన", + "Auto Get": "స్వయంచాలక పొందండి", + "Affected Monitors": "ప్రభావిత మానిటర్లు", + "Pick Affected Monitors...": "ప్రభావిత మానిటర్‌లను ఎంచుకోండి…", + "All Status Pages": "అన్ని స్థితి పేజీలు", + "Select status pages...": "స్థితి పేజీలను ఎంచుకోండి…", + "alertWrongFileType": "దయచేసి JSON ఫైల్‌ని ఎంచుకోండి.", + "Clear all statistics": "అన్ని గణాంకాలను క్లియర్ చేయండి", + "Skip existing": "ఉనికిని దాటవేయి", + "Options": "ఎంపికలు", + "Keep both": "రెండు ఉంచండి", + "Verify Token": "టోకెన్‌ని ధృవీకరించండి", + "Setup 2FA": "సెటప్ 2FA", + "Disable 2FA": "2FAని నిలిపివేయండి", + "2FA Settings": "2FA సెట్టింగ్‌లు", + "filterActive": "చురుకుగా", + "filterActivePaused": "ఆగిపోయింది", + "Active": "చురుకుగా", + "Inactive": "నిష్క్రియ", + "Token": "టోకెన్", + "Tags": "టాగ్లు", + "Add New Tag": "కొత్త ట్యాగ్‌ని జోడించండి", + "Tag with this name already exist.": "ఈ పేరుతో ట్యాగ్ ఇప్పటికే ఉంది.", + "color": "రంగు", + "value (optional)": "విలువ (ఐచ్ఛికం)", + "Gray": "బూడిద రంగు", + "Red": "ఎరుపు", + "Orange": "నారింజ రంగు", + "Green": "ఆకుపచ్చ", + "Blue": "నీలం", + "Indigo": "నీలిమందు రంగు", + "Purple": "ఊదా రంగు", + "Pink": "పింక్ కలర్", + "Search...": "వెతకండి…", + "Avg. Ping": "సగటు పింగ్", + "Avg. Response": "సగటు ప్రతిస్పందన", + "statusPageRefreshIn": "సెకన్లలో రిఫ్రెష్ చేయండి: {0}", + "New Update": "కొత్త నవీకరణ", + "Primary Base URL": "ప్రాథమిక URL", + "Add New Monitor": "కొత్త మానిటర్‌ని జోడించండి", + "statusMaintenance": "స్థితి నిర్వహణ", + "Cannot connect to the socket server": "సాకెట్ సర్వర్‌కి కనెక్ట్ చేయడం సాధ్యపడదు", + "Specific Monitor Type": "నిర్దిష్ట మానిటర్ రకం", + "pauseDashboardHome": "డాష్‌బోర్డ్ హోమ్‌నకు విరామం", + "Delete": "తొలగించు", + "Cert Exp.": "సర్టిఫికేట్ గడువు.", + "Monitor Type": "మానిటర్ రకం", + "Friendly Name": "స్నేహపూర్వక పేరు", + "Retries": "పునఃప్రయత్నాలు", + "Resend Notification if Down X times consecutively": "వరుసగా X సార్లు డౌన్ అయితే నోటిఫికేషన్‌ని మళ్లీ పంపండి", + "resendEveryXTimes": "ప్రతి {0} సార్లు మళ్లీ పంపండి", + "retriesDescription": "సేవ డౌన్‌గా గుర్తించబడి నోటిఫికేషన్ పంపబడటానికి ముందు గరిష్ట సంఖ్యలో పునఃప్రయత్నాలు", + "upsideDownModeDescription": "స్థితిని తలక్రిందులుగా తిప్పండి. సేవ చేరుకోగలిగితే, అది పని చేయనట్లు పరిగణించబడుతుంది.", + "Accepted Status Codes": "ఆమోదించబడిన HTTP స్థితి కోడ్‌లు", + "pushOptionalParams": "ఐచ్ఛిక పారామితులు: {0}", + "Not available, please setup.": "అందుబాటులో లేదు, దయచేసి సెటప్ చేయండి.", + "styleElapsedTime": "హృదయ స్పందన పట్టీ కింద గడిచిన సమయం", + "Search Engine Visibility": "శోధన ఇంజిన్ దృశ్యమానత", + "Discourage search engines from indexing site": "ఇండెక్సింగ్ సైట్ నుండి శోధన ఇంజిన్‌లను నిరుత్సాహపరచండి", + "Update Password": "పాస్‌వర్డ్‌ని నవీకరించండి", + "disableauth.message1": "మీరు ఖచ్చితంగా {disableAuth}?", + "disable authentication": "ప్రామాణీకరణను నిలిపివేయాలనుకుంటున్నారా", + "disableauth.message2": "ఇది Cloudflare యాక్సెస్, Authelia లేదా ఇతర ప్రమాణీకరణ మెకానిజమ్‌ల వంటి Uptime Kuma ముందు {intendThirdPartyAuth} దృశ్యాల కోసం రూపొందించబడింది.", + "where you intend to implement third-party authentication": "థర్డ్-పార్టీ ప్రామాణీకరణను అమలు చేయాలనుకుంటున్న", + "I understand, please disable": "నాకు అర్థమైంది, దయచేసి నిలిపివేయండి", + "No Monitors, please": "దయచేసి మానిటర్లు వద్దు", + "Certificate Info": "సర్టిఫికేట్ సమాచారం", + "Create your admin account": "మీ నిర్వాహక ఖాతాను సృష్టించండి", + "Export Backup": "బ్యాకప్ ఎగుమతి", + "notAvailableShort": "లేదు/అందుబాటులో లేదు", + "Apply on all existing monitors": "ఇప్పటికే ఉన్న అన్ని మానిటర్‌లపై వర్తించండి", + "Schedule maintenance": "షెడ్యూల్ నిర్వహణ", + "Start of maintenance": "నిర్వహణ ప్రారంభం", + "alertNoFile": "దయచేసి దిగుమతి చేయడానికి ఫైల్‌ను ఎంచుకోండి.", + "Overwrite": "ఓవర్రైట్", + "Enable 2FA": "2FAని ప్రారంభించండి", + "Two Factor Authentication": "రెండు కారకాల ప్రమాణీకరణ", + "Show URI": "URIని చూపు", + "Add New below or Select...": "దిగువన కొత్తది జోడించండి లేదా ఎంచుకోండి…", + "Tag with this value already exist.": "ఈ విలువతో ట్యాగ్ ఇప్పటికే ఉంది.", + "Custom": "కస్టమ్", + "Entry Page": "ఎంట్రీ పేజీ", + "statusPageNothing": "ఇక్కడ ఏమీ లేదు, దయచేసి సమూహాన్ని లేదా మానిటర్‌ని జోడించండి.", + "No Services": "సేవలు లేవు", + "Partially Degraded Service": "పాక్షికంగా క్షీణించిన సేవ", + "Degraded Service": "దిగజారిన సేవ", + "Add Group": "సమూహాన్ని జోడించండి", + "Add a monitor": "మానిటర్‌ను జోడించండి", + "Go to Dashboard": "డాష్బోర్డ్ కు వెళ్ళండి", + "Status Page": "స్థితి పేజీ", + "Status Pages": "స్థితి పేజీలు", + "here": "ఇక్కడ", + "Required": "అవసరం", + "Post URL": "పోస్ట్ URL", + "Content Type": "కంటెంట్ రకం", + "webhookFormDataDesc": "PHPకి {multipart} మంచిది. JSON {decodeFunction}తో అన్వయించబడాలి", + "webhookAdditionalHeadersTitle": "అదనపు శీర్షికలు", + "webhookBodyPresetOption": "ప్రీసెట్ - {0}", + "webhookBodyCustomOption": "కస్టమ్ బాడీ", + "Webhook URL": "వెబ్‌హుక్ URL", + "Application Token": "అప్లికేషన్ టోకెన్", + "Server URL": "సర్వర్ URL", + "Priority": "ప్రాధాన్యత", + "Read more": "ఇంకా చదవండి", + "appriseInstalled": "అప్రైజ్ ఇన్‌స్టాల్ చేయబడింది.", + "Method": "పద్ధతి", + "Body": "శరీరం", + "Headers": "హెడర్సు", + "PushUrl": "పుష్ URL", + "BodyInvalidFormat": "అభ్యర్థన విషయం JSON చెల్లదు: ", + "Monitor History": "మానిటర్ చరిత్ర", + "clearDataOlderThan": "మానిటర్ చరిత్ర డేటాను {0} రోజుల పాటు ఉంచండి.", + "records": "రికార్డులు", + "One record": "ఒక రికార్డు", + "Current User": "ప్రస్తుత వినియోగదారుడు", + "topic": "అంశం", + "topicExplanation": "పర్యవేక్షించడానికి MQTT అంశం", + "successMessage": "విజయ సందేశం", + "successMessageExplanation": "MQTT సందేశం విజయంగా పరిగణించబడుతుంది", + "recent": "ఇటీవలి", + "Done": "పూర్తి", + "Info": "సమాచారం", + "Steam API Key": "స్టీమ్ API కీ", + "Shrink Database": "డేటాబేస్ కుదించు", + "Pick Accepted Status Codes...": "ఆమోదించబడిన స్థితి కోడ్‌లను ఎంచుకోండి…", + "Default": "డిఫాల్ట్", + "HTTP Options": "HTTP ఎంపికలు", + "Title": "శీర్షిక", + "Content": "విషయము", + "Style": "శైలి", + "info": "సమాచారం", + "warning": "హెచ్చరిక", + "danger": "ప్రమాదం", + "error": "లోపం", + "primary": "ప్రాథమిక", + "light": "వెలుతురు", + "dark": "చీకటి", + "Post": "పోస్ట్", + "Created": "సృష్టించబడింది", + "Last Updated": "చివరిగా నవీకరించబడింది", + "Unpin": "అన్‌పిన్", + "Show Tags": "ట్యాగ్‌లను చూపించు", + "Hide Tags": "ట్యాగ్‌లను దాచండి", + "Description": "వివరణ", + "Add one": "ఒకటి జోడించండి", + "No Monitors": "మానిటర్లు లేవు", + "Services": "సేవలు", + "Select": "ఎంచుకోండి", + "selectedMonitorCount": "ఎంచుకున్నది: {0}", + "Powered by": "ద్వారా ఆధారితం", + "Customize": "అనుకూలీకరించండి", + "Custom Footer": "అనుకూల ఫుటర్", + "Custom CSS": "అనుకూల CSS", + "deleteStatusPageMsg": "మీరు ఖచ్చితంగా ఈ స్థితి పేజీని తొలగించాలనుకుంటున్నారా?", + "Proxies": "ప్రాక్సీలు", + "default": "డిఫాల్ట్", + "enabled": "ప్రారంభించబడింది", + "Certificate Chain": "సర్టిఫికేట్ చైన్", + "Valid": "చెల్లుబాటు అవుతుంది", + "Invalid": "చెల్లదు", + "User": "వినియోగదారు", + "Installed": "ఇన్‌స్టాల్ చేయబడింది", + "Not installed": "ఇన్‌స్టాల్ చేయలేదు", + "Running": "నడుస్తోంది", + "Not running": "నడవడం లేదు", + "Remove Token": "టోకెన్‌ని తీసివేయండి", + "Start": "ప్రారంభించండి", + "Stop": "ఆపు", + "Add New Status Page": "కొత్త స్థితి పేజీని జోడించండి", + "Slug": "స్లగ్", + "startOrEndWithOnly": "{0}తో మాత్రమే ప్రారంభించండి లేదా ముగించండి", + "Next": "తరువాత", + "No Proxy": "ప్రాక్సీ లేదు", + "All Systems Operational": "అన్ని సిస్టమ్స్ ఆపరేషనల్", + "Edit Status Page": "స్థితి పేజీని సవరించండి", + "defaultNotificationName": "నా {నోటిఫికేషన్} హెచ్చరిక ({సంఖ్య})", + "webhookJsonDesc": "Express.js వంటి ఏదైనా ఆధునిక HTTP సర్వర్‌లకు {0} మంచిది", + "webhookCustomBodyDesc": "అభ్యర్థన కోసం అనుకూల HTTP బాడీని నిర్వచించండి. టెంప్లేట్ వేరియబుల్స్ {msg}, {heartbeat}, {monitor} ఆమోదయోగ్యమైనవి.", + "webhookAdditionalHeadersDesc": "webhookతో పంపబడిన అదనపు హెడర్లను సెట్ చేస్తుంది. ప్రతి హెడర్ JSON కీ/విలువగా నిర్వచించబడాలి.", + "emojiCheatSheet": "ఎమోజి చీట్ షీట్: {0}", + "appriseNotInstalled": "అప్రైజ్ ఇన్‌స్టాల్ చేయబడలేదు. {0}", + "HeadersInvalidFormat": "అభ్యర్థన హెడర్సు చెల్లుబాటు కావు JSON: ", + "PasswordsDoNotMatch": "గుత్త పదములు సరి పోవట్లేదు.", + "steamApiKeyDescription": "స్టీమ్ గేమ్ సర్వర్‌ని పర్యవేక్షించడానికి మీకు స్టీమ్ వెబ్-API కీ అవసరం. మీరు మీ API కీని ఇక్కడ నమోదు చేసుకోవచ్చు: ", + "Security": "భద్రత", + "Pick a RR-Type...": "RR-రకాన్ని ఎంచుకోండి…", + "Create Incident": "సంఘటనను సృష్టించండి", + "critical": "ప్రమాదకరమైన", + "Please input title and content": "దయచేసి శీర్షిక మరియు కంటెంట్‌ని ఇన్‌పుట్ చేయండి", + "Switch to Light Theme": "లైట్ థీమ్‌కి మారండి", + "Switch to Dark Theme": "డార్క్ థీమ్‌కి మారండి", + "No monitors available.": "మానిటర్లు అందుబాటులో లేవు.", + "Untitled Group": "పేరులేని సమూహం", + "Discard": "విస్మరించండి", + "Cancel": "రద్దు చేయండి", + "Check/Uncheck": "చెక్/చెక్చేయవద్దు", + "setAsDefault": "డిఫాల్ట్ సెట్ చేయబడింది", + "deleteProxyMsg": "మీరు ఖచ్చితంగా అన్ని మానిటర్‌ల కోసం ఈ ప్రాక్సీని తొలగించాలనుకుంటున్నారా?", + "proxyDescription": "పనిచేయడానికి ప్రాక్సీలు తప్పనిసరిగా మానిటర్‌కు కేటాయించబడాలి.", + "enableProxyDescription": "ఈ ప్రాక్సీ సక్రియం చేయబడే వరకు మానిటర్ అభ్యర్థనలపై ప్రభావం చూపదు. మీరు యాక్టివేషన్ స్థితి ద్వారా అన్ని మానిటర్‌ల నుండి ప్రాక్సీని తాత్కాలికంగా నిలిపివేయడాన్ని నియంత్రించవచ్చు.", + "setAsDefaultProxyDescription": "కొత్త మానిటర్‌ల కోసం ఈ ప్రాక్సీ డిఫాల్ట్‌గా ప్రారంభించబడుతుంది. మీరు ఇప్పటికీ ప్రతి మానిటర్‌కు విడిగా ప్రాక్సీని నిలిపివేయవచ్చు.", + "Accept characters:": "అక్షరాలను అంగీకరించండి:", + "No consecutive dashes": "వరుస డాష్‌లను ఉపయోగించవద్దు", + "The slug is already taken. Please choose another slug.": "స్లగ్ ఇప్పటికే తీసుకోబడింది. దయచేసి మరొక స్లగ్‌ని ఎంచుకోండి." +} diff --git a/src/lang/th-TH.json b/src/lang/th-TH.json new file mode 100644 index 0000000..4648bdd --- /dev/null +++ b/src/lang/th-TH.json @@ -0,0 +1,1128 @@ +{ + "languageName": "ไทย", + "checkEverySecond": "ตรวจสอบทุก {0} วินาที", + "retryCheckEverySecond": "ลองใหม่ทุก {0} วินาที", + "retriesDescription": "จำนวนครั้งสูงสุดที่จะลองก่อนบริการถูกระบุว่าไม่สามารถใช้งานได้และส่งการแจ้งเตือน", + "ignoreTLSError": "ไม่สนใจข้อผิดพลาด TLS/SSL สำหรับเว็บไซต์ HTTPS", + "upsideDownModeDescription": "สลับสถานะ เช่น ถ้าบริการสามารถใช้งานได้จะถูกเปลี่ยนเป็นใช้งานไม่ได้", + "maxRedirectDescription": "จำนวนครั้งสูงสุดที่จะเปลี่ยนเส้นทาง, ตั้งเป็น 0 เพื่อปิดการเปลี่ยนเส้นทาง", + "acceptedStatusCodesDescription": "เลือกรหัสสถานะที่ถือว่าการตอบกลับสำเร็จ", + "passwordNotMatchMsg": "รหัสผ่านไม่ตรงกัน", + "notificationDescription": "การแจ้งเตือนต้องกำหนดให้มอนิเตอร์เพื่อให้สามารถใช้งานได้", + "keywordDescription": "ค้นหาคำสำคัญใน HTML หรือ JSON ของการตอบกลับ, คำสำคัญต้องคำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่", + "pauseDashboardHome": "หยุดชั่วคราว", + "deleteMonitorMsg": "คุณแน่ใจหรือไม่ที่จะลบมอนิเตอร์?", + "deleteNotificationMsg": "คุณแน่ใจหรือไม่ที่จะลบการแจ้งเตือนสำหรับมอนิเตอร์ทั้งหมด?", + "resolverserverDescription": "Cloudflare เป็นเซิร์ฟเวอร์ค้นหาเริ่มต้น, คุณสามารถเปลี่ยนเซิร์ฟเวอร์ได้ตลอดเวลา", + "rrtypeDescription": "เลือกประเภท DNS Record ที่คุณต้องการจะมอนิเตอร์", + "pauseMonitorMsg": "คุณแน่ใจหรือไม่ที่จะหยุดมอนิเตอร์ชั่วคราว?", + "enableDefaultNotificationDescription": "การแจ้งเตือนนี้จะถูกเปิดโดยค่าเริ่มต้นสำหรับมอนิเตอร์ใหม่, คุณสามารถปิดการแจ้งเตือนสำหรับแต่ละมอนิเตอร์ได้", + "clearEventsMsg": "คุณแน่ใจหรือไม่ที่จะลบเหตุการณ์ทั้งหมดสำหรับมอนิเตอร์นี้?", + "clearHeartbeatsMsg": "คุณแน่ใจหรือไม่ที่จะลบประวัติการตรวจสอบทั้งหมดสำหรับมอนิเตอร์นี้?", + "confirmClearStatisticsMsg": "คุณแน่ใจหรือไม่ที่จะลบสถิติทั้งหมด?", + "importHandleDescription": "เลือก \"ข้ามรายการที่มีอยู่แล้ว\" ถ้าคุณต้องการข้ามทุกมอนิเตอร์หรือการแจ้งเตือนที่มีชื่อซ้ำกัน, \"เขียนทับ\" จะลบทุกมอนิเตอร์หรือการแจ้งเตือนที่มีชื่อซ้ำกัน", + "confirmImportMsg": "คุณแน่ใจหรือไม่ที่จะนำเข้าข้อมูลสำรอง, กรุณาตรวจสอบว่าคุณเลือกข้อมูลที่ถูกต้อง", + "twoFAVerifyLabel": "โปรดกรอกกุญแจ 2FA ของคุณเพื่อยืนยัน:", + "tokenValidSettingsMsg": "กุญแจถูกต้อง, ตอนนี้คุณสามารถบันทึกการตั้งค่า 2FA ของคุณได้แล้ว", + "confirmEnableTwoFAMsg": "คุณแน่ใจหรือไม่ที่จะเปิดใช้งาน 2FA?", + "confirmDisableTwoFAMsg": "คุณแน่ใจหรือไม่ที่จะปิดใช้งาน 2FA?", + "Settings": "การตั้งค่า", + "Dashboard": "แผงควบคุม", + "New Update": "อัปเดตใหม่", + "Language": "ภาษา", + "Appearance": "ลักษณะการแสดงผล", + "Theme": "ธีม", + "General": "ทั่วไป", + "Primary Base URL": "URL หลัก", + "Version": "เวอร์ชัน", + "Check Update On GitHub": "ตรวจสอบการอัปเดตบน GitHub", + "List": "รายการ", + "Add": "เพิ่ม", + "Add New Monitor": "เพิ่มมอนิเตอร์ใหม่", + "Quick Stats": "สรุปสถานะ", + "Up": "ทำงานปกติ", + "Down": "ทำงานล้มเหลว", + "Pending": "รอดำเนินการ", + "Unknown": "ไม่ทราบ", + "Pause": "หยุดชั่วคราว", + "Name": "ชื่อ", + "Status": "สถานะ", + "DateTime": "วันที่และเวลา", + "Message": "ข้อความ", + "No important events": "ไม่มีเหตการณ์ที่สำคัญ", + "Resume": "ดำเนินการต่อ", + "Edit": "แก้ไข", + "Delete": "ลบ", + "Current": "ปัจจุบัน", + "Uptime": "เวลาที่ใช้งานได้", + "Cert Exp.": "วันหมดอายุใบรับรอง", + "days": "วัน", + "day": "วัน", + "-day": "-วัน", + "hour": "ชั่วโมง", + "-hour": "-ชั่วโมง", + "Response": "การตอบสนอง", + "Ping": "การตอบสนอง", + "Monitor Type": "ประเภทมอนิเตอร์", + "Keyword": "คำสำคัญ", + "Friendly Name": "ชื่อที่เป็นมิตร", + "URL": "URL", + "Hostname": "ชื่อโฮสต์", + "Port": "พอร์ต", + "Heartbeat Interval": "ระยะเวลาระหว่างการทดสอบ", + "Retries": "จำนวนครั้งที่จะลองใหม่", + "Heartbeat Retry Interval": "ระยะห่างระหว่างการทดสอบใหม่หลังจากไม่สำเร็จ", + "Advanced": "ขั้นสูง", + "Upside Down Mode": "โหมดสลับ", + "Max. Redirects": "จำนวนการเปลี่ยนเส้นทางสูงสุด", + "Accepted Status Codes": "รหัสสถานะที่ยอมรับ", + "Push URL": "URL เป้าหมาย", + "needPushEvery": "คุณควรเรียก URL นี้ทุก {0} วินาที", + "pushOptionalParams": "ตัวแปรเสริม: {0}", + "Save": "บันทึก", + "Notifications": "การแจ้งเตือน", + "Not available, please setup.": "ไม่พร้อมใช้งาน, กรุณาตั้งค่า", + "Setup Notification": "ตั้งค่าการแจ้งเตือน", + "Light": "สว่าง", + "Dark": "มืด", + "Auto": "อัตโนมัติ", + "Theme - Heartbeat Bar": "หน้าตา - แถบการตอบสนอง", + "Normal": "ปกติ", + "Bottom": "ด้านล่าง", + "None": "ไม่มี", + "Timezone": "เขตเวลา", + "Search Engine Visibility": "การมองเห็นของเครื่องมือค้นหา", + "Allow indexing": "อนุญาตให้สร้างดัชนี", + "Discourage search engines from indexing site": "ปฏิเสธเครื่องมือค้นหาไม่ให้สร้างดัชนีของเว็บไซต์", + "Change Password": "เปลี่ยนรหัสผ่าน", + "Current Password": "รหัสผ่านปัจจุบัน", + "New Password": "รหัสผ่านใหม่", + "Repeat New Password": "ยืนยันรหัสผ่านใหม่", + "Update Password": "อัปเดตรหัสผ่าน", + "Disable Auth": "ปิดใช้งานการตรวจสอบสิทธิ์", + "Enable Auth": "เปิดใช้งานการตรวจสอบสิทธิ์", + "disableauth.message1": "คุณต้องการที่จะ {disableAuth}?", + "disable authentication": "ปิดใช้งานระบบรับรองความถูกต้องใช่หรือไม่", + "disableauth.message2": "ถูกออกแบบมาสำหรับกรณีที่มี {intendThirdPartyAuth} อยู่หน้าระบบ Uptime Kuma เช่น Cloudflare Access, Authelia หรือกลไกการตรวจสอบสิทธิ์อื่น ๆ", + "Please use this option carefully!": "โปรดใช้ความระมัดระวังในการเลือกใช้งานระบบนี้ !", + "Logout": "ออกจากระบบ", + "Leave": "ออก", + "I understand, please disable": "ฉันเข้าใจแล้ว, กรุณาปิดการใช้งาน", + "Confirm": "ยืนยัน", + "Yes": "ใช่", + "No": "ไม่", + "Username": "ชื่อผู้ใช้", + "Password": "รหัสผ่าน", + "Remember me": "จดจำฉันไว้", + "Login": "เข้าสู่ระบบ", + "No Monitors, please": "ไม่มีมอนิเตอร์, กรุณา", + "add one": "สร้าง", + "Notification Type": "ประเภทการแจ้งเตือน", + "Email": "อีเมล", + "Test": "ทดสอบ", + "Certificate Info": "ข้อมูลใบรับรอง", + "Resolver Server": "เซิร์ฟเวอร์ที่ค้นหา", + "Resource Record Type": "ประเภท DNS Record", + "Last Result": "ผลล่าสุด", + "Create your admin account": "สร้างบัญชีผู้ดูแลระบบ", + "Repeat Password": "ยืนยันรหัสผ่าน", + "Import Backup": "นำเข้าข้อมูลสำรอง", + "Export Backup": "ส่งออกข้อมูลสำรอง", + "Export": "ส่งออก", + "Import": "นำเข้า", + "respTime": "ระยะเวลาการตอบสนอง (ms)", + "notAvailableShort": "ไม่สามารถใช้งานได้", + "Default enabled": "เปิดใช้งานโดยค่าเริ่มต้น", + "Apply on all existing monitors": "ใช้กับมอนิเตอร์ทั้งหมด", + "Create": "สร้าง", + "Clear Data": "ล้างข้อมูล", + "Events": "เหตุการณ์", + "Heartbeats": "ประวัติการตรวจสอบ", + "Auto Get": "ดึงอัตโนมัติ", + "backupDescription": "คุณสามารถสำรองข้อมูลการแจ้งเตือนและมอนิเตอร์ทั้งหมดไว้ได้ในไฟล์ JSON", + "backupDescription2": "หมายเหตุ : ประวัติและข้อมูลเหตการณ์จะไม่ถูกสำรอง", + "backupDescription3": "ข้อมูลที่ละเอียดอ่อนเช่นกุญแจการแจ้งเตือนจะรวมอยู่ในไฟล์ข้อมูลสำรอง, โปรดเก็บข้อมูลสำรองอย่างปลอดภัย", + "alertNoFile": "กรุณาเลือกไฟล์ที่จะใช้งาน", + "alertWrongFileType": "กรุณาเลือกไฟล์ที่เป็น JSON", + "Clear all statistics": "ล้างข้อมูลสถิติทั้งหมด", + "Skip existing": "ข้ามรายการที่มีอยู่แล้ว", + "Overwrite": "เขียนทับ", + "Options": "ตัวเลือก", + "Keep both": "เก็บทั้งสอง", + "Verify Token": "ยืนยันกุญแจ", + "Setup 2FA": "ติดตั้ง 2FA", + "Enable 2FA": "เปิดใช้งาน 2FA", + "Disable 2FA": "ปิดใช้งาน 2FA", + "2FA Settings": "ตั้งค่า 2FA", + "Two Factor Authentication": "การยืนยันตัวตนแบบสองขั้นตอน", + "Active": "ใช้งาน", + "Inactive": "ไม่ใช้งาน", + "Token": "กุญแจ", + "Show URI": "แสดง URI", + "Tags": "แท็ก", + "Add New below or Select...": "เพิ่มใหม่ด้านล่างหรือเลือก…", + "Tag with this name already exist.": "แท็กที่มีชื่อนี้มีอยู่แล้ว", + "Tag with this value already exist.": "แท็กที่มีข้อมูลนี้มีอยู่แล้ว", + "color": "สี", + "value (optional)": "ข้อมูล (ไม่จำเป็น)", + "Gray": "เทา", + "Red": "แดง", + "Orange": "ส้ม", + "Green": "เขียว", + "Blue": "น้ำเงิน", + "Indigo": "ม่วง", + "Purple": "ม่วง", + "Pink": "ชมพู", + "Search...": "ค้นหา…", + "Avg. Ping": "ค่าปิงเฉลี่ย", + "Avg. Response": "ค่า Response เฉลี่ย", + "Entry Page": "หน้าต้อนรับ", + "statusPageNothing": "ไม่มีอะไรตรงนี้ !, กรุณาเพิ่มกลุ่มหรือมอนิเตอร์", + "No Services": "ไม่มีบริการ", + "All Systems Operational": "บริการทั้งหมดทำงานได้ปกติ", + "Partially Degraded Service": "บริการมีปัญหาบางส่วน", + "Degraded Service": "บริการมีปัญหา", + "Add Group": "เพิ่มกลุ่ม", + "Add a monitor": "เพิ่มมอนิเตอร์", + "Edit Status Page": "แก้ไขหน้าสถานะ", + "Go to Dashboard": "ไปที่หน้าควบคุม", + "Status Page": "หน้าสถานะ", + "Status Pages": "หน้าสถานะ", + "defaultNotificationName": "การแจ้งเตือน {notification} ของฉัน ({number})", + "here": "ที่นี่", + "Required": "จำเป็น", + "telegram": "Telegram", + "Bot Token": "กุญแจของบอท", + "wayToGetTelegramToken": "คุณสามารถรับกุญแจได้จาก {0}.", + "Chat ID": "ไอดีแชท", + "supportTelegramChatID": "รองรับ แชทส่วนตัว, แชทกลุ่ม, ไอดีแชท", + "wayToGetTelegramChatID": "คุณสามารถรับ ID แชทของคุณได้โดยส่งข้อความไปยังบอทและไปที่ URL นี้เพื่อดู chat_id :", + "YOUR BOT TOKEN HERE": "กุญแจของบอทของคุณที่นี่", + "chatIDNotFound": "ไม่พบไอดีแชท, กรุณาส่งข้อความไปที่บอท", + "webhook": "Webhook", + "Post URL": "URL โพสต์", + "Content Type": "ประเภทเนื้อหา", + "webhookJsonDesc": "{0} ดีสำหรับเซิร์ฟเวอร์ HTTP สมัยใหม่เช่น Express.js", + "webhookFormDataDesc": "{multipart} ดีสำหรับ PHP, ข้อมูล JSON จะต้องถูกประมวลผลด้วย {decodeFunction}", + "smtp": "Email (SMTP)", + "secureOptionNone": "None / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "เพิกเฉยข้อผิดพลาด TLS", + "From Email": "จากอีเมล", + "emailCustomSubject": "หัวข้อที่กำหนดเอง", + "To Email": "ถึงอีเมล", + "smtpCC": "CC", + "smtpBCC": "BCC", + "discord": "Discord", + "Discord Webhook URL": "Discord Webhook URL", + "wayToGetDiscordURL": "คุณสามารถทำได้โดยการไปที่ Server Settings -> Integrations -> Create Webhook", + "Bot Display Name": "ชื่อบอท", + "Prefix Custom Message": "คำนำหน้าข้อความที่กำหนดเอง", + "Hello @everyone is...": "สวัสดี {'@'}everyone นี่…", + "teams": "Microsoft Teams", + "Webhook URL": "Webhook URL", + "wayToGetTeamsURL": "คุณสามารถเรียนรู้วิธีการสร้าง Webhook URL {0}", + "signal": "Signal", + "Number": "หมายเลข", + "Recipients": "ผู้รับ", + "needSignalAPI": "คุณต้องมี Signal Client ที่มี Rest API", + "wayToCheckSignalURL": "คุณสามารถตรวจสอบ URL นี้เพื่อดูวิธีตั้งค่า :", + "signalImportant": "สำคัญ: คุณไม่สามารถผสมกลุ่มและตัวเลขในผู้รับได้!", + "gotify": "Gotify", + "Application Token": "กุญแจของแอพพลิเคชั่น", + "Server URL": "Server URL", + "Priority": "ลำดับความสำคัญ", + "slack": "Slack", + "Icon Emoji": "Icon Emoji", + "Channel Name": "ชื่อห้อง", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "ข้อมูลเพิ่มเติมสำหรับ Webhooks : {0}", + "aboutChannelName": "ใส่ชื่อห้องใน {0} ในช่องชื่อห้องถ้าต้องการที่จะข้าม Webhook, เช่น: #ช่องอื่นๆ", + "aboutKumaURL": "ถ้าคุณไม่ใส่ข้อมูลในช่อง Uptime Kuma URL ค่าเริ่มต้นจะเป็นจะเป็น Uptime Kuma Github", + "emojiCheatSheet": "ตาราง Emoji : {0}", + "rocket.chat": "Rocket.Chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "Push by Techulus", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (รองรับการแจ้งเตือนมากกว่า 50 บริการ)", + "GoogleChat": "Google Chat (สำหรับ Google Workspace เท่านั้น)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "กุญแจผู้ใช้งาน", + "Device": "อุปกรณ์", + "Message Title": "หัวข้อข้อความ", + "Notification Sound": "เสียงแจ้งเตือน", + "More info on:": "ข้อมูลเพิ่มเติม : {0}", + "pushoverDesc1": "ลำดับความสำคัญฉุกเฉิน (2) มีการหมดเวลาเริ่มต้น 30 วินาทีระหว่างการลองใหม่และจะหมดอายุหลังจาก 1 ชั่วโมง", + "pushoverDesc2": "ถ้าคุณต้องการจะส่งการแจ้งเตือนไปยังอุปกรณ์อื่นๆ สามารถกำหนดได้ที่ช่องอุปกรณ์", + "SMS Type": "ประเภท SMS", + "octopushTypePremium": "พรีเมี่ยม (เร็ว - แนะนำสำหรับการแจ้งเตือน)", + "octopushTypeLowCost": "ต้นทุนต่ำ (ช้า - บางครั้งจะถูกบล็อกโดยผู้ให้บริการ)", + "checkPrice": "ตรวจสอบราคาของ {0} :", + "apiCredentials": "ข้อมูลการตรวจสอบสิทธิ์ API", + "octopushLegacyHint": "คุณใช้เวอร์ชันดั้งเดิมของ Octopush (2011 - 2020) หรือเวอร์ชันใหม่หรือไม่?", + "Check octopush prices": "ตรวจสอบราคาของ Octopush {0}", + "octopushPhoneNumber": "หมายเลขโทรศัพท์ (เช่น +66123456789)", + "octopushSMSSender": "ชื่อผู้ส่ง SMS : ความยาว 3 - 11 ตัวอักษร, ตัวเลข และช่องว่าง (a-zA-Z0-9 )", + "LunaSea Device ID": "ไอดีอุปกรณ์ LunaSea", + "Apprise URL": "Apprise URL", + "Example:": "ตัวอย่าง : {0}", + "Read more:": "อ่านเพิ่มเติม : {0}", + "Status:": "สถานะ : {0}", + "Read more": "อ่านเพิ่มเติม", + "appriseInstalled": "Apprise ถูกติดตั้งแล้ว", + "appriseNotInstalled": "Apprise ยังไม่ถูกติดตั้ง {0}", + "Access Token": "กุญแจการเข้าถึง", + "Channel access token": "กุญแจการเข้าถึงของช่อง", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console - {0}", + "Basic Settings": "การตั้งค่าพื้นฐาน", + "User ID": "ไอดีผู้ใช้", + "Messaging API": "Messaging API", + "wayToGetLineChannelToken": "ขั้นแรกให้เข้า {0} สร้างผู้ให้บริการและช่องทาง (Messaging API) จากนั้นคุณจะได้รับกุญแจการเข้าถึงช่องและไอดีผู้ใช้จากรายการเมนูที่กล่าวถึงข้างต้น", + "Icon URL": "Icon URL", + "aboutIconURL": "คุณสามารถระบุลิงก์รูปภาพใน \"URL ไอคอน\" เพื่อแทนที่รูปภาพโปรไฟล์เริ่มต้น จะไม่ถูกใช้หากมีการตั้งค่า Icon Emoji", + "aboutMattermostChannelName": "คุณลบช่องเริ่มต้นที่ Webhook โพสต์ได้ด้วยการป้อนชื่อช่องลงในช่อง \"ชื่อช่อง\" ต้องเปิดใช้งานในการตั้งค่า Mattermost Webhook เช่น #ช่องอื่นๆ", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - ราคาถูก แต่ช้าและมักจะโอเวอร์โหลด จำกัดเฉพาะผู้รับในโปแลนด์", + "promosmsTypeFlash": "SMS FLASH - ข้อความจะแสดงบนอุปกรณ์ของผู้รับโดยอัตโนมัติ จำกัดเฉพาะผู้รับในโปแลนด์", + "promosmsTypeFull": "SMS FULL - SMS ระดับพรีเมียม คุณสามารถใช้ชื่อผู้ส่งของคุณได้ (คุณต้องลงทะเบียนชื่อก่อน) เชื่อถือได้สำหรับการแจ้งเตือน", + "promosmsTypeSpeed": "SMS SPEED - ลำดับความสำคัญสูงสุดในระบบ รวดเร็วและเชื่อถือได้ แต่มีค่าใช้จ่ายสูง (ประมาณสองเท่าของราคาเต็ม SMS)", + "promosmsPhoneNumber": "หมายเลขโทรศัพท์ (สำหรับผู้รับโปแลนด์ คุณสามารถข้ามรหัสพื้นที่ได้)", + "promosmsSMSSender": "ชื่อผู้ส่ง SMS : ชื่อที่ลงทะเบียนล่วงหน้าหรือหนึ่งในค่าเริ่มต้น: InfoSMS, ข้อมูล SMS, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu WebHookURL", + "matrixHomeserverURL": "URL ของโฮมเซิร์ฟเวอร์ (พร้อม http(s):// และพอร์ตเสริม)", + "Internal Room Id": "รหัสห้องภายใน", + "matrixDesc1": "คุณค้นหารหัสห้องภายในได้โดยดูในส่วนขั้นสูงของการตั้งค่าห้องในไคลเอ็นต์ Matrix มันควรจะมีลักษณะเช่น !PMdRCpsIfLwsfjIye6:kiznick.server.", + "matrixDesc2": "ขอแนะนำเป็นอย่างยิ่งให้คุณสร้างผู้ใช้ใหม่และอย่าใช้โทเค็นการเข้าถึงของผู้ใช้ Matrix ของคุณเอง เนื่องจากจะทำให้สามารถเข้าถึงบัญชีของคุณและห้องทั้งหมดที่คุณเข้าร่วม ให้สร้างผู้ใช้ใหม่และเชิญเฉพาะห้องที่คุณต้องการรับการแจ้งเตือนแทน คุณสามารถรับโทเค็นเพื่อการเข้าถึงได้โดยเรียกใช้ {0}", + "Method": "เมทอด", + "Body": "เนื้อหา", + "Headers": "ส่วนหัว", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "เนื้อหาคำขอส่วนหัวไม่ใช่ JSON ที่ถูกต้อง: ", + "BodyInvalidFormat": "เนื้อหาคำขอไม่ใช่ JSON ที่ถูกต้อง : ", + "Monitor History": "ประวัติมอนิเตอร์", + "clearDataOlderThan": "เก็บข้อมูลมอนิเตอร์ {0} วัน", + "PasswordsDoNotMatch": "รหัสผ่านไม่ตรงกัน", + "records": "บันทึก", + "One record": "หนึ่งบันทึก", + "steamApiKeyDescription": "สำหรับการมอนิเตอร์ Steam Game Server คุณต้องมี Steam Web-API key, คุณสามารถสมัครได้จากที่นี่ : ", + "Current User": "ผู้ใช้ปัจจุบัน", + "topic": "หัวข้อ", + "topicExplanation": "หัวข้อ MQTT ที่จะมอนิเตอร์", + "successMessage": "ข้อความที่จะถือว่าประสบความสำเร็จ", + "successMessageExplanation": "ข้อความ MQTT ที่จะถือว่าประสบความสำเร็จ", + "recent": "ล่าสุด", + "Done": "สำเร็จ", + "Info": "ข้อมูล", + "Security": "ความปลอดภัย", + "Steam API Key": "คีย์ API ของ Steam", + "Shrink Database": "ย่อฐานข้อมูล", + "Pick a RR-Type...": "เลือกชนิด DNS Record…", + "Pick Accepted Status Codes...": "เลือกเลขสถานะที่ยอมรับ…", + "Default": "ค่าเริ่มต้น", + "HTTP Options": "ตัวเลือก HTTP", + "Create Incident": "สร้างเหตุการณ์", + "Title": "หัวข้อ", + "Content": "เนื้อหา", + "Style": "สไตล์", + "info": "ข้อมูล", + "warning": "แจ้งเตือน", + "danger": "อันตราย", + "primary": "หลัก", + "light": "สว่าง", + "dark": "มืด", + "Post": "โพสต์", + "Please input title and content": "กรุณาใส่ชื่อและเนื้อหา", + "Created": "สร้าง", + "Last Updated": "อัปเดตล่าสุด", + "Unpin": "เลิกตรึง", + "Switch to Light Theme": "เปลี่ยนเป็นแบบสว่าง", + "Switch to Dark Theme": "เปลี่ยนเป็นแบบมืด", + "Show Tags": "แสดงแท็ก", + "Hide Tags": "ซ่อนแท็ก", + "Description": "รายละเอียด", + "No monitors available.": "ไม่มีมอนิเตอร์ที่สามารถใช้งานได้", + "Add one": "เพิ่ม", + "No Monitors": "ไม่มีมอนิเตอร์", + "Untitled Group": "กลุ่มที่ไม่มีชื่อ", + "Services": "บริการ", + "Discard": "ทิ้ง", + "Cancel": "ยกเลิก", + "Powered by": "ขับเคลื่อนโดย", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API Username (incl. webapi_ prefix)", + "serwersmsAPIPassword": "API Password", + "serwersmsPhoneNumber": "หมายเลขโทรศัพท์", + "serwersmsSenderName": "ชื่อผู้ส่ง SMS (ลงทะเบียนผ่านหน้าควบคุม)", + "stackfield": "Stackfield", + "Customize": "ปรับแต่ง", + "Custom Footer": "ส่วนท้ายที่กำหนดเอง", + "Custom CSS": "CSS ที่กำหนดเอง", + "smtpDkimSettings": "การตั้งค่า DKIM", + "smtpDkimDesc": "โปรดดู Nodemailer DKIM {0} สำหรับการใช้งาน", + "documentation": "คู่มือการใช้งาน", + "smtpDkimDomain": "ชื่อโดเมน", + "smtpDkimKeySelector": "Key Selector", + "smtpDkimPrivateKey": "Private Key", + "smtpDkimHashAlgo": "อัลกอริทึมแฮช (ไม่บังคับ)", + "smtpDkimheaderFieldNames": "คีย์ส่วนหัวสำหรับลงชื่อ (ไม่บังคับ)", + "smtpDkimskipFields": "Header Keys ไม่ต้องเซ็น (ไม่บังคับ)", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API Endpoint", + "alertaEnvironment": "Environment", + "alertaApiKey": "กุญแจ API", + "alertaAlertState": "แจ้งเตือนสถานะ", + "alertaRecoverState": "กู้คืนสถานะ", + "deleteStatusPageMsg": "คุณแน่ใจหรือไม่ว่าต้องการลบหน้าสถานะนี้?", + "Proxies": "พร็อกซี", + "default": "ค่าเริ่มต้น", + "enabled": "เปิดใช้งานแล้ว", + "setAsDefault": "ตั้งเป็นค่าเริ่มต้น", + "deleteProxyMsg": "คุณแน่ใจหรือไม่ว่าต้องการลบพร็อกซีสำหรับมอนิเตอร์ทั้งหมด?", + "proxyDescription": "ต้องตั้งค่ามอนิเตอร์ให้ใช้พร็อกซีเพื่อให้ใช้งานได้", + "enableProxyDescription": "พร็อกซีนี้จะไม่ส่งผลต่อมอนิเตอร์จนกว่าจะเปิดใช้งาน คุณสามารถควบคุมการปิดใช้งานพร็อกซีชั่วคราวจากมอนิเตอร์ทั้งหมดได้ที่ส่วนสถานะการเปิดใช้งาน", + "setAsDefaultProxyDescription": "พร็อกซีนี้จะถูกเปิดโดนค่าเริ่มต้นสำหรับมอนิเตอร์ใหม่, คุณสามารถปิดการแจ้งเตือนสำหรับแต่ละมอนิเตอร์ได้", + "Certificate Chain": "ห่วงโซ่ใบรับรอง", + "Valid": "ถูกต้อง", + "Invalid": "ไม่ถูกต้อง", + "AccessKeyId": "กุญแจสิทธิ ID", + "SecretAccessKey": "กุญแจสิทธิ Secret", + "PhoneNumbers": "PhoneNumbers", + "TemplateCode": "รหัสเทมเพลต", + "SignName": "ป้ายชื่อ", + "Sms template must contain parameters: ": "เทมเพลต SMS ต้องมีพารามิเตอร์ : ", + "Bark Endpoint": "Bark Endpoint", + "WebHookUrl": "WebHookUrl", + "SecretKey": "SecretKey", + "For safety, must use secret key": "เพื่อความปลอดภัย จำเป็นต้องตั้งค่ากุญแจการเข้าถึง", + "Device Token": "Device Token", + "Platform": "แพลตฟอร์ม", + "Huawei": "Huawei", + "High": "สูง", + "Retry": "ลองใหม่", + "Topic": "หัวข้อ", + "WeCom Bot Key": "WeCom Bot Key", + "Setup Proxy": "ติดตั้งพร็อกซี่", + "Proxy Protocol": "โปรโตคอลพร็อกซี่", + "Proxy Server": "เซิร์ฟเวอร์พร็อกซี", + "Proxy server has authentication": "พร็อกซีเซิร์ฟเวอร์มีการตรวจสอบสิทธิ์", + "User": "ผู้ใช้", + "Installed": "ติดตั้งแล้ว", + "Not installed": "ไม่ได้ติดตั้ง", + "Running": "กำลังทำงาน", + "Not running": "ไม่ได้ทำงาน", + "Remove Token": "ลบกุญแจ", + "Start": "เริ่ม", + "Stop": "หยุด", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "เพิ่มหน้าสถานะใหม่", + "Slug": "ชื่อ", + "Accept characters:": "ตัวอักษรที่ใช้งานได้ :", + "startOrEndWithOnly": "เริ่มหรือจบด้วย {0} เท่านั้น", + "No consecutive dashes": "ไม่มีขีดกลางติดต่อกัน", + "Next": "ต่อไป", + "The slug is already taken. Please choose another slug.": "ชื่อนี้ถูกใช้งานแล้ว กรุณาใช้ชื่ออื่น", + "No Proxy": "ไม่มีพร็อกซี่", + "HTTP Basic Auth": "ตรวจสอบสิทธิ์ HTTP พื้นฐาน", + "New Status Page": "หน้าสถานะใหม่", + "Page Not Found": "ไม่พบหน้านี้", + "Reverse Proxy": "พร็อกซีย้อนกลับ", + "Backup": "สำรองข้อมูล", + "About": "เกี่ยวกับ", + "wayToGetCloudflaredURL": "(ดาวโหลด cloudflared จาก {0})", + "cloudflareWebsite": "เว็บไซต์ Cloudflare", + "Message:": "ข้อความ :", + "Don't know how to get the token? Please read the guide:": "ไม่รู้วิธีการรับกุญแจ? กรุณาอ่านคู่มือ:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "การเชื่อมต่อปัจุบันอาจขาดหายหากคุณกำลังเชื่อมต่อ Cloudflare Tunnel คุณแน่ใจหรือไม่ที่จะหยุด, พิมรหัสผ่านของคุณเพื่อยืนยัน", + "Other Software": "ซอฟต์แวร์อื่นๆ", + "For example: nginx, Apache and Traefik.": "เช่น: nginx, Apache และ Traefik", + "Please read": "กรุณาอ่าน", + "Subject:": "เรื่อง :", + "Valid To:": "ใช้ได้ถึง :", + "Days Remaining:": "จำนวนวันที่เหลือ :", + "Issuer:": "ผู้ออก :", + "Fingerprint:": "ลายนิ้วมือ :", + "No status pages": "ไม่มีหน้าสถานะ", + "Domain Name Expiry Notification": "แจ้งเตือนการหมดอายุของโดเมน", + "Proxy": "Proxy", + "Date Created": "วันที่สร้าง", + "onebotHttpAddress": "ที่อยู่ HTTP OneBot", + "onebotMessageType": "ชนิดข้อความ OneBot", + "onebotGroupMessage": "กลุ่ม", + "onebotPrivateMessage": "ส่วนตัว", + "onebotUserOrGroupId": "กลุ่ม / ไอดีผู้ใช้", + "onebotSafetyTips": "เพื่อความปลอดภัย จำเป็นต้องตั้งค่ากุญแจการเข้าถึง", + "PushDeer Key": "กุญแจ PushDeer", + "Footer Text": "ข้อความส่วนท้าย", + "Show Powered By": "แสดงข้อความ \"ขับเคลื่อนโดย\"", + "Domain Names": "ชื่อโดเมน", + "signedInDisp": "เข้าใช้งานในฐานะ {0}", + "signedInDispDisabled": "ปิดการยืนยันตัวตน", + "Certificate Expiry Notification": "แจ้งเตือนใบรับรองหมดอายุ", + "API Username": "ชื่อผู้ใช้ของ API", + "API Key": "API คีย์", + "Recipient Number": "หมายเลขผู้รับ", + "From Name/Number": "จาก ชื่อ / หมายเลข", + "Leave blank to use a shared sender number.": "ไม่ต้องกรอกเพื่อใช้ชื่อผู้ส่งร่วมกัน", + "Octopush API Version": "เวอร์ชั่น API Octopush", + "Legacy Octopush-DM": "Octopush-DM แบบเก่า", + "endpoint": "endpoint", + "octopushAPIKey": "\"API key\" จากข้อมูลยืนยันตัวตน HTTP API ในแผงควบคุม", + "octopushLogin": "\"Login\" จากข้อมูลยืนยันตัวตน HTTP API ในแผงควบคุม", + "promosmsLogin": "API Login Name", + "promosmsPassword": "รหัสผ่าน API", + "pushoversounds pushover": "Pushover (default)", + "pushoversounds bike": "จักรยาน", + "pushoversounds bugle": "บักเกิล", + "pushoversounds cashregister": "เครื่องคิดเงิน", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "คอสมิก", + "pushoversounds falling": "ตก", + "pushoversounds gamelan": "ระนาด", + "pushoversounds incoming": "กำลังมา", + "pushoversounds intermission": "ช่วงพัก", + "pushoversounds magic": "แมจิก", + "pushoversounds mechanical": "เครื่องกล", + "pushoversounds pianobar": "เปียโนบาร์", + "pushoversounds siren": "ไซเรน", + "pushoversounds spacealarm": "สัญญาณเตือนอวกาศ", + "pushoversounds tugboat": "เรือโยง", + "pushoversounds alien": "แจ้งเตือน เอเลี่ยน (ยาว)", + "pushoversounds climb": "ไต่เขา (ยาว)", + "pushoversounds persistent": "Persistent (long)", + "pushoversounds echo": "Pushover Echo (long)", + "pushoversounds updown": "ขึ้นลง (ยาว)", + "pushoversounds vibrate": "สั่นอย่างเดียว", + "pushoversounds none": "ไม่มี (เงียบ)", + "pushyAPIKey": "Secret API Key", + "pushyToken": "โทเคน ของอุปกรณ์", + "Show update if available": "แสดงการอัปเดตถ้ามี", + "Also check beta release": "ตรวจสอบรุ่นเบต้า", + "Using a Reverse Proxy?": "ใช้ Reverse Proxy อยู่ใช่มั้ย?", + "Check how to config it for WebSocket": "ตรวจสอบวิธีการตั้งค่าสำหรับ WebSocket", + "Steam Game Server": "Steam Game Server", + "Most likely causes:": "สาเหตุที่เป็นไปได้มากที่สุด :", + "The resource is no longer available.": "ทรัพยากรไม่สามารถใช้งานได้อีกต่อไป", + "There might be a typing error in the address.": "อาจมีข้อผิดพลาดในการพิมพ์ที่อยู่", + "What you can try:": "สิ่งที่คุณสามารถลองทำ :", + "Retype the address.": "พิมพ์ที่อยู่อีกครั้ง", + "Go back to the previous page.": "กลับไปหน้าที่แล้ว", + "Coming Soon": "เร็วๆ นี้", + "wayToGetClickSendSMSToken": "คุณสามารถรับ API Username และ API Key ได้จาก {0}", + "wayToGetLineNotifyToken": "คุณสามารถรับ access token ได้จาก {0}", + "resendEveryXTimes": "ส่งซ้ำทุก {0} ครั้ง", + "resendDisabled": "การส่งซ้ำถูกปิดใช้งาน", + "dnsPortDescription": "พอร์ตของเซิร์ฟเวอร์ DNS, ค่าเริ่มต้นคือ 53, คุณสามารถเปลี่ยนพอร์ตตอนไหนก็ได้", + "Resend Notification if Down X times consecutively": "ส่งการแจ้งเตือนซ้ำถ้าออฟไลน์ครบ X ครั้ง", + "error": "เกิดข้อผิดพลาด", + "critical": "วิกฤต", + "wayToGetPagerDutyKey": "คุณสามารถรับคีย์ได้โดยการไปที่ Service -> Service Directory -> (Select a service) -> Integrations -> Add integration, และค้นหา \"Events API V2\", สำหรับข้อมูลเพิ่มเติม {0}", + "Integration Key": "Integration Key", + "Integration URL": "Integration URL", + "Auto resolve or acknowledged": "แก้ไขอัตโนมัติหรือยอมรับ", + "do nothing": "ไม่ทำอะไร", + "auto acknowledged": "ยอมรับอัตโนมัติ", + "auto resolve": "แก้ไขอัตโนมัติ", + "Bark Group": "กลุ่มที่จะประกาศ", + "Bark Sound": "เสียงประกาศ", + "Authentication": "การตรวจสอบสิทธิ์", + "HTTP Headers": "ส่วนหัว HTTP", + "Trust Proxy": "เชื่อถือพร็อกซี", + "HomeAssistant": "Home Assistant", + "RadiusSecret": "Radius Secret", + "RadiusSecretDescription": "แบ่งปันคีย์ลับระหว่างผู้ใช้งานและเซิร์ฟเวอร์", + "RadiusCalledStationId": "Called Station Id", + "RadiusCalledStationIdDescription": "ตัวระบุของอุปกรณ์ที่ถูกเรียก", + "RadiusCallingStationId": "Calling Station Id", + "RadiusCallingStationIdDescription": "Identifier of the calling device", + "Connection String": "Connection String", + "Query": "Query", + "settingsCertificateExpiry": "วันหมดอายุของใบรับรอง TLS", + "certificationExpiryDescription": "การตรวจสอบ HTTPS จะแจ้งเตือนถ้าใบอนุญาติ TLS จะหมดอายุใน:", + "Setup Docker Host": "ติดตั้ง Docker Host", + "Connection Type": "ประเภทการเชื่อมต่อ", + "Docker Daemon": "Docker Daemon", + "deleteDockerHostMsg": "คุณแน่ใจหรือไม่ที่จะลบ Docker host นี้สำหรับการมอนิเตอร์ทั้งหมด?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker Container", + "Container Name / ID": "ชื่อ / ไอดี ของคอนเทนเนอร์", + "Docker Host": "โฮสต์ของ Docker", + "Docker Hosts": "โฮสต์ของ Docker", + "ntfy Topic": "หัวข้อ ntfy", + "Domain": "โดเมน", + "Workstation": "Workstation", + "disableCloudflaredNoAuthMsg": "คุณอยู่ในโหมดไม่มีการตรวจสอบสิทธิ์, ไม่จำเป็นต้องมีรหัสผ่าน", + "trustProxyDescription": "เชื่อ Header 'X-Forwarded-*', คุณควรเปิดใช้งาน ถ้าคุณต้องการ IP ของผู้ใช้ที่ถูกต้องและ Uptime Kuma อยู่ข้างหลัง Nginx หรือ Apache", + "Examples": "ตัวอย่าง", + "Home Assistant URL": "Home Assistant URL", + "Long-Lived Access Token": "Access Token แบบมีอายุนาน", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "คุณสามารถสร้างโทเค็นเข้าถึงแบบใช้งานได้นานโดยคลิกที่ชื่อโปรไฟล์ของคุณ (ด้านล่างซ้าย) แล้วเลื่อนลงไปด้านล่าง จากนั้นคลิก สร้างโทเค็น", + "Notification Service": "บริการแจ้งเตือน", + "default: notify all devices": "ค่าเริ่มต้น: แจ้งเตือนทุกอุปกรณ์", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "รายการแจ้งเตือนสามารถหาได้ใน Home Assistant ในเมนู \"Developer Tools > Services\" แล้วค้นหา \"notification\" เพื่อหาชื่ออุปกรณ์หรือชื่อโทรศัพท์", + "Automations can optionally be triggered in Home Assistant:": "สามารถเลือกสั่งงานระบบอัตโนมัติได้ใน Home Assistant:", + "Trigger type:": "ชนิดสิ่งกระตุ้น:", + "Event type:": "ชนิดเหตการณ์:", + "Event data:": "ข้อมูลกิจกรรม:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "จากนั้นเลือกการกระทำ, ตัวอย่าง เช่น เปลี่ยนเป็นไฟสีแดง", + "Frontend Version": "เวอร์ชั่น Frontend", + "Frontend Version do not match backend version!": "เวอร์ชั่น Frontend ไม่ตรงกับ Backend !", + "webhookAdditionalHeadersTitle": "Header เพิ่มเติม", + "webhookAdditionalHeadersDesc": "กำหนด Header ที่จะส่งไปหร้อมกับ Webhook โดยแต่ละ header ควรระบุในรูปแบบ key/value แบบ JSON", + "Start of maintenance": "เริ่มการซ่อมบำรุง", + "All Status Pages": "หน้าสถานะทั้งหมด", + "Custom": "กำหนดเอง", + "Game": "เกม", + "statusMaintenance": "การซ่อมบำรุง", + "Maintenance": "การซ่อมบำรุง", + "Monitor": "มอนิเตอร์ | มอนิเตอร์", + "Select status pages...": "เลือกหน้าสถานะ…", + "Schedule maintenance": "กำหนดเวลาบำรุงรักษา", + "Affected Monitors": "มอนิเตอร์ที่ได้รับผลกระทบ", + "markdownSupported": "รองรับไวยากรณ์ Markdown หากใช้ HTML โปรดหลีกเลี่ยงการเว้นวรรคหน้าเพื่อป้องกันปัญหาในการจัดรูปแบบ", + "Help": "ช่วยเหลือ", + "Pick Affected Monitors...": "เลือกมอนิเตอร์ที่ได้รับผลกระทบ…", + "Packet Size": "ขนาดของ Packet", + "ZohoCliq": "ZohoCliq", + "backupOutdatedWarning": "ไม่ได้รับการพัฒนาแล้ว : ไม่สามารถสร้างหรือกู้ข้อมูลสำรองได้สมบูรณ์ เนื่องจากมีฟีเจอร์ใหม่เพิ่มขึ้นมากและการแบ็กอัปไม่ได้ถูกพัฒนา", + "backupRecommend": "กรุณาแบ็กอัปข้อมูลทั้งหมดหรือโฟลเดอร์ Data (./data/) โดยตรงแทน", + "Optional": "ไม่จำเป็น", + "squadcast": "Squadcast", + "or": "หรือ", + "recurringInterval": "ช่วงเวลา", + "Recurring": "ทำซ้ำ", + "General Monitor Type": "มอนิเตอร์ชนิดทั่วไป", + "pagertreeCritical": "วิกฤต", + "pagertreeDoNothing": "ไม่ต้องทำอะไร", + "pagertreeResolve": "แก้ไขอัตโนมัติ", + "wayToGetPagerTreeIntegrationURL": "หลังจากสร้างการรวม Uptime Kuma ใน PagerTree แล้ว ให้คัดลอก Endpoint, ดูรายละเอียดทั้งหมด {0}", + "telegramSendSilently": "ส่งอย่างเงียบ ๆ", + "maintenanceStatus-inactive": "ไม่ใช้งาน", + "telegramProtectContent": "ป้องกันการส่งต่อ/บันทึก", + "Add New Tag": "เพิ่มแท็กใหม่", + "strategyManual": "ตั่งให้ใช้งาน/ไม่ใช้งานด้วยตนเอง", + "warningTimezone": "ใช้เขตเวลาของเซิร์ฟเวอร์", + "weekdayShortMon": "จันทร์", + "weekdayShortTue": "วันอังคาร", + "weekdayShortWed": "พุธ", + "weekdayShortThu": "พฤหัสบดี", + "weekdayShortFri": "ศุกร์", + "weekdayShortSat": "เสาร์", + "weekdayShortSun": "อาทิตย์", + "dayOfWeek": "วันในสัปดาห์", + "dayOfMonth": "วันในเดือน", + "maintenanceStatus-under-maintenance": "อยู่ภายใต้การบำรุงรักษา", + "maintenanceStatus-scheduled": "กำหนดการ", + "maintenanceStatus-ended": "สิ้นสุด", + "maintenanceStatus-unknown": "ไม่ทราบ", + "Specific Monitor Type": "ชนิดมอนิเตอร์เฉพาะ", + "telegramMessageThreadID": "(ตัวเลือก) ไอดีเทรดข้อความ", + "telegramMessageThreadIDDescription": "ตัวระบุที่ไม่ซ้ำซึ่งเป็นทางเลือกสำหรับเธรดข้อความเป้าหมาย (หัวข้อ) ของฟอรัม สำหรับฟอรัมซูเปอร์กรุ๊ปเท่านั้น", + "sameAsServerTimezone": "เช่นเดียวกับเขตเวลาของเซิร์ฟเวอร์", + "startDateTime": "วันที่/เวลาเริ่มต้น", + "endDateTime": "วันที่/เวลาสิ้นสุด", + "cronSchedule": "กำหนดการ: ", + "invalidCronExpression": "นิพจน์ Cron ไม่ถูกต้อง: {0}", + "cronExpression": "นิพจน์ Cron", + "lastDay": "วันสุดท้าย", + "lastDay1": "วันสุดท้ายของเดือน", + "lastDay2": "วันที่ 2 สุดท้ายของเดือน", + "lastDay3": "วันที่ 3 สุดท้ายของเดือน", + "lastDay4": "วันที่ 4 สุดท้ายของเดือน", + "No Maintenance": "ไม่มีการบำรุงรักษา", + "pauseMaintenanceMsg": "แน่ใจไหมว่าต้องการหยุดชั่วคราว?", + "Display Timezone": "แสดงเขตเวลา", + "statusPageMaintenanceEndDate": "จบ", + "Server Timezone": "เขตเวลาเซิร์ฟเวอร์", + "statusPageRefreshIn": "รีโหลดใน: {0}", + "telegramSendSilentlyDescription": "ส่งข้อความอย่างเงียบๆ ผู้ใช้จะได้รับการแจ้งเตือนโดยไม่มีเสียง", + "telegramProtectContentDescription": "หากเปิดใช้งาน ข้อความบอทใน Telegram จะได้รับการปกป้องจากการส่งต่อและการบันทึก", + "dnsCacheDescription": "อาจจะทำงานไม่ได้กับ IPv6, ปิดใช้งานถ้าเจอปัญหา", + "IconUrl": "URL ไอคอน", + "Enable DNS Cache": "(เลิกใช้แล้ว) เปิดใช้งานแคช DNS สำหรับตัวตรวจสอบ HTTP(s)", + "Enable": "เปิดใช้งาน", + "Disable": "ปิดใช้งาน", + "Single Maintenance Window": "หน้าการปรับปรุงเดี่ยว", + "Clone Monitor": "มอนิเตอร์", + "Clone": "โคลนมอนิเตอร์", + "cloneOf": "ชื่อเล่นมอนิเตอร์", + "wayToGetZohoCliqURL": "คุณสามารถดูวิธีการสร้าง Webhook URL {0}", + "Cannot connect to the socket server": "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ Socket", + "Reconnecting...": "กำลังเชื่อมต่อใหม่", + "Home": "หน้าหลัก", + "Date and Time": "วันที่และเวลา", + "DateTime Range": "ช่วงวันที่และเวลา", + "loadingError": "ไม่สามารถดึงข้อมูลได้ โปรดลองอีกครั้งในภายหลัง", + "plugin": "ปลั้กอิน | ปลั้กอิน", + "install": "ติดตั้ง", + "installing": "กำลังติดตั้ง", + "uninstall": "ถอนการติดตั้ง", + "uninstalling": "กำลังถอนการติดตั้ง", + "confirmUninstallPlugin": "แน่ใจหรือไม่ว่าต้องการถอนการติดตั้งปลั้กอินนี้?", + "Schedule Maintenance": "กำหนดเวลาซ่อมแซม", + "Edit Maintenance": "แก้ใขการบำรุงรักษา", + "recurringIntervalMessage": "ดำเนินการทุกวัน | ดำเนินการทุก {0} วัน", + "chromeExecutableAutoDetect": "ตรวจจับอัตโนมัติ", + "chromeExecutableDescription": "สำหรับผู้ใช้งาน Docker, ถ้ายังไม่ได้ติดตั่ง Chromium, อาจจะเสียเวลาในการติดตั่งและแสดงผลการทดสอบเพิ่มเติม, ใช้พื้นที่ประมาณ 1GB", + "notificationRegional": "ภูมิภาค", + "timeoutAfter": "หมดเวลาหลังจาก {0} วินาที", + "Select": "เลือก", + "Expected Value": "ค่าที่คาดหวัง", + "setupDatabaseChooseDatabase": "คุณต้องการใช้ฐานข้อมูลใด?", + "setupDatabaseEmbeddedMariaDB": "คุณไม่จำเป็นต้องทำอะไร Docker image จะสร้างและตั้งค่า MariaDB ให้โดยอัตโนมัติ Uptime Kuma จะเชื่อมต่อกับฐานข้อมูลนี้ด้วย unix socket", + "setupDatabaseMariaDB": "เชื่อมต่อไปยัง MariaDB ภายนอก คุณจำเป็นจะต้องตั่งค่าการเชื่อมต่อฐานข้อมูล", + "setupDatabaseSQLite": "ไฟล์ฐานข้อมูลอย่างง่าย แนะนำสำหรับการปรับใช้ขนาดเล็ก ก่อนเวอร์ชัน 2.0.0 Uptime Kuma ใช้ SQLite เป็นฐานข้อมูลเริ่มต้น", + "dbName": "ชื่อฐานข้อมูล", + "Passive Monitor Type": "ประเภทมอนิเตอร์แบบพาสซีฟ", + "documentationOf": "{0} คู่มือ", + "successDeleted": "ลบสำเร็จ.", + "Command": "คำสั่ง", + "authUserInactiveOrDeleted": "ผู้ใช้งานถูกปิดหรือถูกลบแล้ว", + "Saved.": "บันทึกแล้ว", + "authInvalidToken": "โทเค็นไม่ถูกต้อง.", + "successDisabled": "ปิดการใช้งาน สำเร็จ.", + "max 11 alphanumeric characters": "ตัวอักษรสูงสุดไม่เกิน 11 ตัว", + "showCertificateExpiry": "แสดง Certificate ที่หมดอายุ", + "Telephone number": "เบอร์โทรศัพท์", + "successAuthChangePassword": "รหัสผ่านถูกเปลื่ยนเรียบร้อยแล้ว.", + "successEdited": "แก้ไขสำเร็จ", + "styleElapsedTimeShowNoLine": "แสดง (ไม่มีบรรทัด)", + "styleElapsedTimeShowWithLine": "แสดง (พร้อมบรรทัด)", + "where you intend to implement third-party authentication": "ที่คุณตั้งใจจะนำการตรวจสอบสิทธิ์ของบุคคลที่สามไปใช้", + "styleElapsedTime": "เวลาที่ผ่านไปภายใต้แถบวัดอัตราการเต้นของหัวใจ", + "now": "ตอนนี้", + "Host URL": "URL ของโฮสต์", + "ignoreTLSErrorGeneral": "ละเว้นข้อผิดพลาด TLS/SSL สำหรับการเชื่อมต่อ", + "programmingLanguages": "ภาษาโปรแกรมมิ่ง", + "Invert Keyword": "คำสำคัญ", + "settingUpDatabaseMSG": "การตั้งค่าฐานข้อมูล อาจต้องใช้เวลาสักระยะหนึ่ง โปรดอดใจรอ", + "time ago": "{0} ที่ผ่านมา", + "-year": "-ปี", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "ระบุชื่อโฮสต์ของเซิร์ฟเวอร์ที่คุณต้องการเชื่อมต่อ หรือ {localhost} หากคุณต้องการใช้ {local_mta}", + "Request Timeout": "หมดเวลาการเชื่อมต่อ", + "ignoredTLSError": "ข้อผิดพลาด TLS/SSL ถูกละเว้น", + "pushOthers": "อื่น ๆ", + "pushViewCode": "วิธีใช้งาน Push monitor (ดูโค้ด)", + "templateServiceName": "ชื่อบริการ", + "templateHostnameOrURL": "ชื่อโฮสต์หรือ URL", + "templateStatus": "สถานะ", + "webhookBodyCustomOption": "เนื้อหากำหนดเอง", + "Reset Token": "รีเซ็ตโทเคน", + "apiKeyAddedMsg": "คีย์ API ของคุณถูกเพิ่มเรียบร้อยแล้ว โปรดจดบันทึกไว้ เนื่องจากจะไม่แสดงอีกครั้ง", + "wayToGetSevenIOApiKey": "ไปที่แดชบอร์ดที่ app.seven.io > develope > api key > ปุ่มเพิ่มสีเขียว", + "filterActivePaused": "หยุดชั่วคราว", + "Search monitored sites": "ค้นหาเว็บไซต์ที่ตรวจสอบ", + "liquidIntroduction": "การใช้เทมเพลตสามารถทำได้ผ่านภาษาการสร้างเทมเพลต Liquid โปรดดูที่ {0} สำหรับคำแนะนำในการใช้งาน ตัวแปรที่ใช้ได้มีดังนี้:", + "templateLimitedToUpDownCertNotifications": "ใช้ได้เฉพาะสำหรับการแจ้งเตือนสถานะ UP/DOWN/การหมดอายุของใบรับรอง", + "selectedMonitorCount": "ที่เลือกไว้: {0}", + "statusPageSpecialSlugDesc": "Slug พิเศษ {0}: หน้านี้จะแสดงเมื่อไม่มีการระบุ slug", + "Add a new expiry notification day": "เพิ่มวันแจ้งเตือนการหมดอายุใหม่", + "templateMonitorJSON": "ออบเจ็กต์ที่อธิบายเกี่ยวกับตัวตรวจสอบ", + "templateLimitedToUpDownNotifications": "ใช้ได้เฉพาะสำหรับการแจ้งเตือนสถานะ UP/DOWN", + "webhookBodyPresetOption": "ค่าที่ตั้งไว้ล่วงหน้า - {0}", + "Check/Uncheck": "เลือก/ไม่เลือก", + "Learn More": "เรียนรู้เพิ่มเติม", + "Add API Key": "เพิ่มคีย์ API", + "templateMsg": "ข้อความการแจ้งเตือน", + "Json Query Expression": "นิพจน์สำหรับดึงข้อมูล JSON", + "locally configured mail transfer agent": "ตัวส่งอีเมลในเครื่อง", + "filterActive": "กำลังทำงาน", + "successKeyword": "คำสำเร็จ (Success Keyword)", + "smseagleContact": "ชื่อผู้ติดต่อในสมุดโทรศัพท์", + "smspartnerApiurl": "คุณสามารถหาคีย์ API ของคุณได้ในแดชบอร์ดที่ {0}", + "smspartnerPhoneNumber": "หมายเลขโทรศัพท์", + "smspartnerSenderName": "ชื่อผู้ส่ง SMS", + "Remove the expiry notification": "ลบวันแจ้งเตือนการหมดอายุ", + "Refresh Interval": "ช่วงเวลารีเฟรช", + "Refresh Interval Description": "หน้าสถานะจะทำการรีเฟรชเว็บไซต์ทั้งหมดทุก ๆ {0} วินาที", + "noDockerHostMsg": "ไม่พร้อมใช้งาน กรุณาตั้งค่า Docker Host ก่อน", + "tailscalePingWarning": "เพื่อที่จะใช้ตัวตรวจสอบ Tailscale Ping คุณต้องติดตั้ง Uptime Kuma โดยไม่ใช้ Docker และติดตั้ง Tailscale client บนเซิร์ฟเวอร์ของคุณด้วย", + "telegramUseTemplate": "ใช้เทมเพลต ข้อความที่กำหนดเอง", + "telegramUseTemplateDescription": "หากเปิดใช้งาน ข้อความจะถูกส่งโดยใช้เทมเพลตที่กำหนดเอง", + "telegramTemplateFormatDescription": "Telegram อนุญาตให้ใช้ภาษามาร์กอัปต่าง ๆ กับข้อความ ดูรายละเอียดเพิ่มเติมได้ที่ Telegram {0}", + "telegramServerUrl": "(ไม่บังคับ) URL ของเซิร์ฟเวอร์", + "telegramServerUrlDescription": "เพื่อยกระดับข้อจำกัดของ API ของ Telegram หรือให้เข้าถึงพื้นที่ที่ถูกบล็อก (จีน, อิหร่าน, เป็นต้น) สำหรับข้อมูลเพิ่มเติมคลิก {0}. ค่าเริ่มต้น: {1}", + "enableNSCD": "เปิดใช้งาน NSCD (Name Service Cache Daemon) สำหรับการแคชคำขอ DNS ทั้งหมด", + "emailCustomisableContent": "เนื้อหาที่ปรับแต่งได้", + "smtpLiquidIntroduction": "สองฟิลด์ต่อไปนี้สามารถใช้เทมเพลตผ่านภาษาการสร้างเทมเพลต Liquid โปรดดูที่ {0} สำหรับคำแนะนำในการใช้งาน ตัวแปรที่ใช้ได้มีดังนี้:", + "emailTemplateMsg": "ข้อความของการแจ้งเตือน", + "postToExistingThread": "โพสต์ไปยังเธรด / โพสต์ฟอรัมที่มีอยู่แล้ว", + "whatHappensAtForumPost": "สร้างโพสต์ในฟอรัมใหม่ จะไม่โพสต์ข้อความในโพสต์เดิม หากต้องการโพสต์ในโพสต์เดิมให้ใช้ “{option}”", + "wayToGetDiscordThreadId": "การรับค่า ID ของเธรดหรือโพสต์ในฟอรัมจะคล้ายกับการรับ Channel ID อ่านเพิ่มเติมเกี่ยวกับวิธีการรับ ID ได้ที่ {0}", + "infiniteRetention": "ตั้งค่าเป็น 0 เพื่อการเก็บข้อมูลตลอดไป", + "confirmDeleteTagMsg": "คุณแน่ใจหรือว่าต้องการลบแท็กนี้? มอนิเตอร์ที่ใช้กับแท็กนี้จะไม่ได้ถูกลบ", + "affectedMonitorsDescription": "เลือกมอนิเตอร์ที่ได้รับผลกระทบจากการซ่อมบำรุงปัจจุบัน", + "affectedStatusPages": "แสดงข้อความการซ่อมบำรุงนี้บนหน้าสถานะที่เลือก", + "wayToGetKookBotToken": "สร้างแอปพลิเคชันและรับโทเค็นบอตของคุณที่ {0}", + "wayToGetKookGuildID": "เปิดโหมด ‘Developer’ ในการตั้งค่าของ Kook แล้วคลิกขวาที่กิลด์เพื่อรับ ID ของมัน", + "Strategy": "กลยุทธ์", + "Economy": "เศรษฐกิจ", + "You can divide numbers with": "คุณสามารถหารตัวเลขได้ด้วย", + "Notify Channel": "ช่องทางการแจ้งเตือน", + "setup a new monitor group": "ตั้งค่ากลุ่มการมอนิเตอร์หม่", + "smseagleGroup": "ชื่อกลุ่มสมุดโทรศัพท์", + "smseagleEncoding": "ส่งเป็น Unicode", + "smseaglePriority": "ลำดับความสำคัญของข้อความ (0-9, ค่าเริ่มต้น = 0)", + "smspartnerPhoneNumberHelptext": "หมายเลขต้องอยู่ในรูปแบบสากล {0}, {1} และหากมีหลายหมายเลขต้องคั่นด้วย {2}", + "smspartnerSenderNameInfo": "ต้องอยู่ระหว่าง 3 ถึง 11 ตัวอักษรปกติ", + "Custom Monitor Type": "ประเภทการมอนิเตอร์ แบบกำหนดเอง", + "Add Another": "เพิ่มอีกหนึ่ง", + "Expires": "หมดอายุ", + "disableAPIKeyMsg": "คุณแน่ใจหรือไม่ว่าจะปิดการใช้งาน API คีย์นี้?", + "ntfyAuthenticationMethod": "วิธีการยืนยันตัวตน", + "ntfyPriorityHelptextAllEvents": "ทุกกิจกรรมจะถูกส่งด้วยลำดับความสำคัญสูงสุด", + "ntfyPriorityHelptextAllExceptDown": "เหตุการณ์ทั้งหมดจะถูกส่งด้วยลำดับความสำคัญนี้ ยกเว้นเหตุการณ์ {0} ซึ่งมีลำดับความสำคัญ {1}", + "Show Clickable Link Description": "หากทำเครื่องหมายไว้ ทุกคนที่มีสิทธิ์เข้าถึงหน้าสถานะนี้จะสามารถเข้าถึง URL ของมอนิเตอร์ได้", + "monitorToastMessagesDescription": "การแจ้งเตือนแบบ Toast สำหรับการตรวจสอบจะหายไปหลังจากเวลาที่กำหนด (เป็นวินาที) หากตั้งค่าเป็น -1 ระบบจะไม่จำกัดเวลาแสดงผล หากตั้งค่าเป็น 0 จะปิดการแสดงการแจ้งเตือนแบบ Toast ทั้งหมด", + "wayToGetFlashDutyKey": "คุณสามารถไปที่ Channel -> (เลือก Channel) -> Integrations -> Add a new integration' page, add 'Uptime Kuma' to get push address, copy the Integration Key in the address. สำหรับข้อมูลเพิ่มเติม โปรดไปที่", + "cacheBusterParamDescription": "พารามิเตอร์ที่สร้างขึ้นแบบสุ่มเพื่อหลีกเลี่ยงการใช้แคช", + "gamedigGuessPortDescription": "พอร์ตที่ใช้โดย Valve Server Query Protocol อาจแตกต่างจากพอร์ตไคลเอนต์ ลองใช้วิธีนี้หากมอนิเตอร์ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ของคุณได้", + "bitrix24SupportUserID": "กรอกรหัสผู้ใช้ของคุณลงใน Bitrix24 คุณสามารถค้นหารหัสได้จากลิงก์โดยไปที่โปรไฟล์ของผู้ใช้", + "successBackupRestored": "กู้คืนข้อมูลสำรองสำเร็จแล้ว", + "Remote Browser not found!": "ไม่พบ Remote Browse!", + "remoteBrowsersDescription": "Remote Browsers เป็นทางเลือกหนึ่งแทนการเรียกใช้ Chromium บนเครื่องของคุณโดยตรง โดยสามารถตั้งค่าใช้งานร่วมกับบริการ เช่น browserless.io หรือเชื่อมต่อกับ", + "deleteRemoteBrowserMessage": "คุณแน่ใจหรือไม่ว่าต้องการลบ Remote Browser นี้สำหรับมอนิเตอร์ทั้งหมด?", + "mongodbCommandDescription": "รันคำสั่ง MongoDB กับฐานข้อมูล สำหรับข้อมูลเกี่ยวกับคำสั่งที่มีอยู่ โปรดดูที่ {documentation}", + "goAlertInfo": "GoAlert is a An open source application for on-call scheduling, automated escalations and notifications (like SMS or voice calls). Automatically engage the right person, the right way, and at the right time! {0}", + "aboutNotifyChannel": "การแจ้งเตือนไปยังช่องทางจะทำให้เกิดการแจ้งเตือนบนเดสก์ท็อปหรือมือถือของสมาชิกทุกคนในช่องนั้น ไม่ว่าสถานะของพวกเขาจะเป็น “ใช้งาน” หรือ “ไม่อยู่” ก็ตาม", + "DockerHostRequired": "กรุณาตั้งค่า Docker Host สำหรับมอนิเตอร์นี้", + "Select message type": "เลือกประเภทข้อความ", + "dataRetentionTimeError": "ระยะเวลาเก็บข้อมูลต้องเป็น 0 หรือมากกว่า", + "promosmsAllowLongSMS": "อนุญาตให้ส่ง SMS ยาว", + "apiKey-active": "ใช้งานอยู่", + "and": "และ", + "chromeExecutable": "ไฟล์ที่ใช้รัน Chrome/Chromium", + "Maintenance Time Window of a Day": "ช่วงเวลาการซ่อมบำรุงของวัน", + "Effective Date Range": "ช่วงวันที่มีผล (ไม่บังคับ)", + "leave blank for default subject": "เว้นว่างไว้สำหรับหัวข้อเริ่มต้น", + "emailCustomBody": "เนื้อหากำหนดเอง", + "leave blank for default body": "เว้นว่างไว้สำหรับเนื้อหาพื้นฐาน", + "emailTemplateMonitorJSON": "อ็อบเจ็กต์ที่อธิบายเกี่ยวกับมอนิเตอร์", + "Send to channel": "ส่งไปยังช่องทาง", + "Create new forum post": "สร้างโพสต์ฟอรัมใหม่", + "forumPostName": "ชื่อโพสต์ในฟอรัม", + "threadForumPostID": "รหัสเธรด / โพสต์ในฟอรัม", + "e.g. {discordThreadID}": "เช่น {discordThreadID}", + "Channel access token (Long-lived)": "Channel access token (Long-lived)", + "Your User ID": "ไอดีผู้ใช้ของคุณ", + "deleteMaintenanceMsg": "คุณแน่ใจหรือว่าต้องการลบการซ่อมบำรุงนี้?", + "atLeastOneMonitor": "เลือกมอนิเตอร์ที่ได้รับผลกระทบอย่างน้อยหนึ่งมอนิเตอร์", + "invertKeywordDescription": "ค้นหาคำสำคัญที่ไม่มีอยู่ แทนที่จะมีอยู่", + "Guild ID": "กิลด์ ID", + "Proto Service Name": "ชื่อบริการ Proto", + "Proto Method": "Proto เมทอด", + "Proto Content": "เนื้อหา Proto", + "Lowcost": "ต้นทุนต่ำ", + "high": "สูง", + "SMSManager API Docs": "เอกสารประกอบ API ของ SMSManager", + "Gateway Type": "ประเภทเกตเวย์", + "Base URL": "URL หลัก", + "pushoverMessageTtl": "ข้อความ TTL (วินาที)", + "Free Mobile User Identifier": "Free Mobile User Identifier", + "Free Mobile API Key": "Free Mobile API Key", + "SendKey": "SendKey", + "goAlertIntegrationKeyInfo": "Get generic API integration key for the service in this format \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" usually the value of token parameter of copied URL.", + "Mentioning": "การกล่าวถึง", + "Don't mention people": "อย่ากล่าวถึงบุคคล", + "Mention group": "กล่าวถึง {group}", + "Bark API Version": "เวอร์ชัน Bark API", + "openModalTo": "เปิดโมดัลไปยัง {0}", + "Add a domain": "เพิ่มโดเมน", + "Remove domain": "ลบโดเมน ‘{0}’", + "smseagleTo": "หมายเลขโทรศัพท์", + "smseagleRecipientType": "ประเภทผู้รับ", + "smseagleRecipient": "ผู้รับ (หากมีหลายคนให้แยกด้วยเครื่องหมายจุลภาค)", + "smseagleUrl": "URL ของอุปกรณ์ SMSEagle ของคุณ", + "Server URL should not contain the nfty topic": "URL ของเซิร์ฟเวอร์ไม่ควรมีหัวข้อ “nfty”", + "smseagleToken": "API Access token", + "pushDeerServerDescription": "เว้นว่างไว้เพื่อใช้เซิร์ฟเวอร์อย่างเป็นทางการ", + "Edit Tag": "แก้ไขแท็ก", + "Server Address": "ที่อยู่เซิร์ฟเวอร์", + "Expiry": "หมดอายุ", + "Expiry date": "วันที่หมดอายุ", + "Don't expire": "ไม่หมดอายุ", + "Continue": "ดำเนินการต่อ", + "Key Added": "เพิ่มคีย์แล้ว", + "No API Keys": "ไม่มี API คีย์", + "apiKey-expired": "หมดอายุแล้ว", + "apiKey-inactive": "ไม่ได้ใช้งาน", + "deleteAPIKeyMsg": "คุณแน่ใจหรือไม่ว่าต้องการลบ API คีย์นี้?", + "Generate": "สร้าง", + "pagertreeUrgency": "ด่วน", + "pagertreeLow": "ต่ำ", + "pagertreeMedium": "ปานกลาง", + "pagertreeHigh": "สูง", + "lunaseaTarget": "เป้าหมาย", + "lunaseaDeviceID": "ไอดี ของอุปกรณ์", + "lunaseaUserID": "ไอดี ของผู้ใช้", + "ntfyUsernameAndPassword": "ชื่อผู้ใช้ และ รหัสผ่าน", + "twilioAccountSID": "บัญชี SID", + "twilioApiKey": "API คีย์ (ไม่บังคับ)", + "twilioFromNumber": "จากหมายเลข", + "twilioToNumber": "ถึงหมายเลข", + "Monitor Setting": "การตั้งค่ามอนิเตอร์ของ {0}", + "PushDeer Server": "เซิร์ฟเวอร์ PushDeer", + "Google Analytics ID": "ไอดี Google Analytics", + "API Keys": "API คีย์", + "pagertreeIntegrationUrl": "URL สำหรับการเชื่อมต่อระบบ (Integration URL)", + "twilioAuthToken": "Auth Token / Api Key Secret", + "Group": "กลุ่ม", + "Monitor Group": "กลุ่มมอนิเตอร์", + "monitorToastMessagesLabel": "การแจ้งเตือนมอนิเตอร์แบบ Toast", + "Press Enter to add broker": "กด Enter เพื่อเพิ่มโบรกเกอร์", + "Mechanism": "กลไก", + "Pick a SASL Mechanism...": "เลือกกลไก SASL…", + "noGroupMonitorMsg": "ไม่พร้อมใช้งาน โปรดสร้างกลุ่มมอนิเตอร์ก่อน", + "Close": "ปิด", + "FlashDuty Severity": "ความรุนแรง", + "Show Clickable Link": "แสดงลิงก์ที่คลิกได้", + "Open Badge Generator": "เปิดเครื่องมือสร้าง Badge", + "Badge Generator": "เครื่องมือสร้าง Badge ของ {0}", + "Badge Type": "ประเภท Badge", + "Badge Duration (in hours)": "ระยะเวลา Badge (เป็นชั่วโมง)", + "Badge Label": "ป้ายกำกับ Badge", + "nostrRelays": "รีเลย์ Nostr", + "nostrRelaysHelp": "URL รีเลย์หนึ่งรายการต่อบรรทัด", + "cacheBusterParam": "เพิ่มพารามิเตอร์ {0}", + "gamedigGuessPort": "Gamedig: เดาพอร์ต", + "Message format": "รูปแบบข้อความ", + "wayToGetBitrix24Webhook": "คุณสามารถสร้างเว็บฮุกได้โดยทำตามขั้นตอนที่ {0}", + "authIncorrectCreds": "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง", + "2faAlreadyEnabled": "2FA ได้รับการเปิดใช้งานแล้ว", + "2faEnabled": "เปิดใช้งาน 2FA แล้ว", + "2faDisabled": "2FA ถูกปิดการใช้งาน", + "successAdded": "เพิ่มเรียบร้อยแล้ว", + "successResumed": "ดำเนินการต่อสำเร็จแล้ว", + "successPaused": "หยุดชั่วคราวสำเร็จแล้ว", + "successEnabled": "เปิดใช้งานสำเร็จแล้ว", + "tagNotFound": "ไม่พบแท็ก", + "foundChromiumVersion": "พบ Chromium/Chrome เวอร์ชัน: {0}", + "Add a Remote Browser": "เพิ่ม Remote Browser", + "self-hosted container": "คอนเทนเนอร์ที่โฮสต์เอง (self-hosted container)", + "remoteBrowserToggle": "โดยปกติแล้ว Chromium จะทำงานภายในคอนเทนเนอร์ของ Uptime Kuma คุณสามารถใช้ remote browser ได้โดยเปิดสวิตช์นี้", + "useRemoteBrowser": "ใช้ Remote Browser", + "aboutSlackUsername": "เปลี่ยนชื่อที่แสดงของผู้ส่งข้อความ หากคุณต้องการกล่าวถึงใคร ให้รวมไว้ในชื่อที่เป็นมิตรแทน", + "grpcMethodDescription": "ชื่อเมธอดจะถูกแปลงเป็นรูปแบบ camelCase เช่น sayHello, check, เป็นต้น", + "Enable TLS": "เปิดใช้งาน TLS", + "pagertreeSilent": "เงียบ", + "enableGRPCTls": "อนุญาตให้ส่งคำขอแบบ gRPC ด้วยการเชื่อมต่อ TLS", + "Sender name": "ชื่อผู้ส่ง", + "smsplanetNeedToApproveName": "ต้องได้รับการอนุมัติในแผงควบคุมของไคลเอนต์", + "Phone numbers": "หมายเลขโทรศัพท์", + "Badge Prefix": "คำนำหน้าค่าของ Badge", + "Badge Suffix": "คำตามหลังค่าของ Badge", + "Badge Label Color": "สีของป้ายข้อความ (Label) บน Badge", + "Badge Color": "สีของ Badge", + "Badge Preview": "ตัวอย่างการแสดง Badge (Preview)", + "Badge Label Suffix": "คำต่อท้ายข้อความ (Label) บน Badge", + "Badge Label Prefix": "คำนำหน้าข้อความ (Label) บน Badge", + "Badge Up Color": "สีของ Badge เมื่อสถานะเป็นปกติ (Up)", + "Badge Down Color": "สีของ Badge เมื่อสถานะเป็นออฟไลน์ (Down)", + "Badge Pending Color": "สีของ Badge ขณะรอการประมวลผล", + "Badge Maintenance Color": "สีของ Badge ในระหว่างการบำรุงรักษา", + "Badge Warn Color": "สีของ Badge เมื่อสถานะเตือน", + "Badge Warn Days": "จำนวนวันที่ Badge แสดงสถานะเตือน", + "Badge Down Days": "จำนวนวันที่ Badge แสดงสถานะออฟไลน์", + "Badge Style": "สไตล์ของ Badge", + "Badge value (For Testing only.)": "ค่าของ Badge (สำหรับการทดสอบเท่านั้น)", + "Badge URL": "URL ของ Badge", + "rabbitmqNodesRequired": "โปรดตั้งค่าโหนดสำหรับมอนิเตอร์นี้", + "Font Twemoji by Twitter licensed under": "ฟอนต์ Twemoji โดย Twitter ที่มีลิขสิทธิ์ภายใต้", + "the smsplanet documentation": "เอกสารของ smsplanet", + "smsplanetApiToken": "โทเค็นสำหรับ API ของ SMSPlanet", + "smsplanetApiDocs": "ข้อมูลรายละเอียดเกี่ยวกับการขอ API tokens สามารถดูได้ใน {the_smsplanet_documentation}", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "การแจ้งเตือนที่มีความสำคัญตามเวลา จะถูกส่งทันที แม้ว่าจะอยู่ในโหมดไม่รบกวน (Do Not Disturb) ก็ตาม", + "Clear": "กระจ่าง", + "equals": "เท่ากับ", + "Go back to home page.": "กลับไปหน้าหลัก", + "conditionValuePlaceholder": "ค่า", + "not starts with": "ไม่ได้เริ่มต้นด้วย", + "Notification Channel": "ช่องทางการรับแจ้งเตือน", + "Alphanumerical string and hyphens only": "ใช้ได้เฉพาะตัวอักษรและตัวเลข (a-z, A-Z, 0-9) และขีดกลาง (-) เท่านั้น", + "Message Template": "ข้อความของเทมเพลต", + "Plain Text": "ข้อความธรรมดา", + "wayToWriteWahaChatId": "หมายเลขโทรศัพท์ที่มีรหัสประเทศ แต่ไม่มีเครื่องหมายบวกที่เริ่มต้น ({0}), หมายเลขติดต่อ ({1}) หรือ หมายเลขกลุ่ม ({2}) การแจ้งเตือนจะถูกส่งไปยัง Chat ID นี้จาก WAHA Session", + "not equals": "ไม่เท่ากับ", + "No tags found.": "ไม่พบแท็ก", + "Conditions": "เงื่อนไข", + "conditionAdd": "เพิ่มเงื่อนไข", + "conditionDelete": "ลบเงื่อนไข", + "conditionAddGroup": "เพิ่มกลุ่ม", + "conditionDeleteGroup": "ลบกลุ่ม", + "contains": "ประกอบด้วย", + "not contains": "ไม่มี", + "Template Format": "รูปแบบของเทมเพลต", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "ระบุรหัสผู้ส่งข้อความแบบข้อความหรือลำดับหมายเลขโทรศัพท์ในรูปแบบ E.164 หากคุณต้องการรับการตอบกลับ", + "The phone number of the recipient in E.164 format.": "หมายเลขโทรศัพท์ของผู้รับในรูปแบบ E.164", + "Can be found on:": "สามารถดูได้ที่: {0}", + "From": "จาก", + "Time Sensitive (iOS Only)": "การแจ้งเตือนที่มีความสำคัญตามเวลา (เฉพาะ iOS)", + "Custom sound to override default notification sound": "เสียงที่กำหนดเองเพื่อแทนที่เสียงการแจ้งเตือนเริ่มต้น", + "Arcade": "อาร์เคด", + "Correct": "ถูกต้อง", + "Fail": "ล้มเหลว", + "Harp": "พิณ", + "Reveal": "เปิดเผย", + "Bubble": "ฟอง", + "Flute": "ขลุ่ย", + "Scifi": "ไซไฟ", + "Sound": "เสียง", + "starts with": "เริ่มต้นด้วย", + "ends with": "ลงท้ายด้วย", + "not ends with": "ไม่ลงท้ายด้วย", + "greater than or equal to": "มากกว่าหรือเท่ากับ", + "record": "บันทึก", + "less than": "น้อยกว่า", + "greater than": "มากกว่า", + "less than or equal to": "น้อยกว่าหรือเท่ากับ", + "Pop": "พอป", + "Elevator": "ลิฟต์", + "Doorbell": "ออด", + "Money": "เงิน", + "Guitar": "กีตาร์", + "successKeywordExplanation": "คีย์เวิร์ด MQTT ที่จะถูกพิจารณาว่าสำเร็จ", + "defaultFriendlyName": "มอนิเตอร์ใหม่", + "tagAlreadyOnMonitor": "แท็กที่ระบุ (ชื่อและค่า) มีอยู่ในระบบแล้ว หรืออยู่ในระหว่างรอการเพิ่ม", + "Add Tags": "เพิ่มแท็ก", + "Subprotocol(s)": "โปรโตคอลย่อย", + "wsCodeDescription": "สำหรับข้อมูลเพิ่มเติมเกี่ยวกับรหัสสถานะ โปรดดูที่ {rfc6455}", + "minutes": "{n} นาที | {n} นาที", + "minuteShort": "{n} นาที | {n} นาที", + "years": "{n} ปี | {n} ปี", + "hours": "{n} ชั่วโมง | {n} ชั่วโมง", + "mariadbCaCertificateLabel": "ใบรับรอง CA", + "enableSSL": "เปิดใช้งาน SSL/TLS", + "mariadbUseSSLHelptext": "เปิดใช้งานเชื่อมต่อแบบเข้ารหัสกับฐานข้อมูล จำเป็นสำหรับฐานข้อมูลบนคลาวด์ส่วนใหญ่", + "mariadbCaCertificateHelptext": "วางข้อมูลใบรับรอง CA ในรูปแบบ PEM เพื่อใช้กับใบรับรองที่ลงนามด้วยตนเอง เว้นว่างไว้หากฐานข้อมูลของคุณใช้ใบรับรองที่ลงนามโดย CA สาธารณะ", + "saveResponseForNotifications": "บันทึกการตอบสนอง HTTP สำหรับการแจ้งเตือนสำเร็จ", + "saveErrorResponseForNotifications": "บันทึกการตอบสนองข้อผิดพลาด HTTP สำหรับการแจ้งเตือน", + "responseMaxLength": "ความยาวสูงสุดของการตอบกลับ (ไบต์)", + "responseMaxLengthDescription": "ขนาดสูงสุดของข้อมูลตอบกลับที่จะจัดเก็บ ตั้งค่าเป็น 0 สำหรับไม่จำกัด ข้อมูลตอบกลับที่มีขนาดใหญ่กว่านี้จะถูกตัดทอน ค่าเริ่มต้น: 1024 (1กิโลไบต์)", + "saveResponseDescription": "จัดเก็บการตอบสนอง HTTP และพร้อมใช้งานสำหรับเทมเพลตแจ้งเตือนในรูปแบบ {templateVariable}", + "unknownDays": "วันที่ไม่ทราบแน่ชัด", + "Monitors": "จอภาพ {n} | จอภาพ {n}", + "Path": "เส้นทาง", + "ignoreSecWebsocketAcceptHeaderDescription": "อนุญาตให้เซิร์ฟเวอร์ไม่ต้องตอบกลับด้วยส่วนหัว Sec-WebSocket-Accept หากอัปเกรด WebSocket สำเร็จ", + "wsSubprotocolDescription": "ป้อนรายการซับโปรโตคอลโดยคั่นด้วยเครื่องหมายจุลภาค สำหรับข้อมูลเพิ่มเติมเกี่ยวกับซับโปรโตคอล โปรดดูเอกสารประกอบ", + "Ignore Sec-WebSocket-Accept header": "ละเว้นส่วนหัว {0}", + "versionIs": "เวอร์ชัน: {version}", + "logoutCurrentUser": "ออกจากระบบ {username}", + "retryOnlyOnStatusCodeFailureDescription": "หากเปิดใช้งาน การลองใหม่จะเกิดขึ้นเฉพาะเมื่อตรวจสอบรหัสสถานะ HTTP ล้มเหลว (เช่น เซิร์ฟเวอร์ล่ม) หากการตรวจสอบรหัสสถานะผ่าน แต่การสืบค้น JSON ล้มเหลว ตัวตรวจสอบจะถูกทำเครื่องหมายว่าล่มทันทีโดยไม่มีการลองใหม่", + "Only retry if status code check fails": "ลองใหม่เฉพาะในกรณีที่ตรวจสอบรหัสสถานะล้มเหลว", + "No incidents recorded": "ไม่มีเหตุการณ์ใดๆเกิดขึ้น", + "Load More": "โหลดเพิ่มเติม", + "Loading...": "กำลังโหลด...", + "Pin this incident": "ปักหมุดเหตุการณ์นี้", + "Incident description": "คำอธิบายเหตุการณ์", + "Incident not found or access denied": "ไม่พบเหตุการณ์หรือเข้าถึงไม่ได้", + "Incident title": "ชื่อเหตุการณ์", + "tagAlreadyStaged": "แท็กนี้ (ชื่อและค่า) ได้ถูกเตรียมไว้สำหรับชุดนี้แล้ว", + "tagNameExists": "มีแท็กระบบที่มีชื่อนี้อยู่แล้ว เลือกจากรายการหรือใช้ชื่ออื่น", + "Edit Incident": "แก้ไขเหตุการณ์", + "templateAvailableVariables": "ตัวแปรที่ใช้งานได้", + "example": "ตัวอย่าง", + "Result": "ผลลัพธ์", + "HeadersInvalidFormatBecause": "ส่วนหัวของคำขอไม่ใช่ JSON ที่ถูกต้องเนื่องจาก {error}", + "BodyInvalidFormatBecause": "ส่วนตัวของคำขอไม่ใช่ JSON ที่ถูกต้องเนื่องจาก {error}", + "mqttWebSocketPath": "MQTT WebSocket Path", + "mqttWebsocketPathExplanation": "เส้นทาง WebSocket สำหรับ MQTT ผ่านการเชื่อมต่อ WebSocket (เช่น /mqtt)", + "mqttWebsocketPathInvalid": "โปรดใช้รูปแบบเส้นทาง WebSocket ที่ถูกต้อง", + "mqttHostnameTip": "โปรดใช้รูปแบบนี้ {hostnameFormat}", + "hostnameCannotBeIP": "ชื่อโฮสต์ DNS ไม่สามารถเป็นที่อยู่ IP ได้ คุณต้องการใช้ช่องตัวแก้ไขใช่หรือไม่", + "Past Incidents": "เหตุการณ์ที่ผ่านมา", + "Pinned incidents are shown prominently on the status page": "เหตุการณ์ที่ปักหมุดไว้จะแสดงอย่างเด่นชัดบนหน้าสถานะ", + "steamApiKeyDescriptionAt": "ในการตรวจสอบสถานะเซิร์ฟเวอร์เกม Steam คุณต้องมีคีย์ Steam Web-API คุณสามารถลงทะเบียนคีย์ API ของคุณได้ที่ {url}", + "monitorTypeGameServer": "เซิร์ฟเวอร์เกมส์", + "monitorTypeSpecial": "พิเศษ", + "oracledbConnectionString": "ฐานข้อมูล Oracle: {connectionString}", + "monitorTypeDatabase": "ชนิดมอนิเตอร์ดาต้าเบส", + "maxUseTemplate": "ใช้ template ข้อความที่กำหนดเอง", + "maxUseTemplateDescription": "หากเปิดใช้งาน ระบบจะส่งข้อความโดยใช้ template ที่กำหนดเอง", + "matrixUseTemplate": "ใช้ template ข้อความที่กำหนดเอง", + "shrinkDatabaseDescriptionSqlite": "เรียกใช้คำสั่ง {vacuum} สำหรับฐานข้อมูล SQLite คำสั่ง {auto_vacuum} เปิดใช้งานอยู่แล้ว แต่คำสั่งนี้จะไม่ทำการจัดเรียงข้อมูลในฐานข้อมูลใหม่หรือจัดเรียงหน้าฐานข้อมูลแต่ละหน้าใหม่เหมือนกับคำสั่ง {vacuum}", + "sipsakPingWarning": "ในการใช้งานโปรแกรมตรวจสอบ Ping ของ SIP Options คุณต้องติดตั้ง Uptime Kuma โดยไม่ต้องใช้ Docker และติดตั้งไคลเอ็นต์ Sipsak บนเซิร์ฟเวอร์ของคุณด้วย", + "pingPacketSizeWarning": "ขนาดแพ็กเก็ตที่เล็กกว่า 16 จะไม่ทำให้เกิดเวลาไป-กลับบน Linux", + "wayToGetBaleChatID": "คุณสามารถดูรหัสแชทของคุณได้โดยการส่งข้อความไปยังบอท และไปที่ URL นี้เพื่อดูรหัสแชท:", + "certificateExpiryNotificationHelp": "สามารถกำหนดจำนวนวันล่วงหน้าได้ในการตั้งค่า", + "ariaResumeMaintenance": "ดำเนินการตามตารางการบำรุงรักษานี้ต่อ", + "ariaCloneMaintenance": "สร้างสำเนาของตารางการบำรุงรักษานี้", + "ariaEditMaintenance": "แก้ไขตารางการบำรุงรักษานี้", + "ariaDeleteMaintenance": "ลบกำหนดการบำรุงรักษานี้", + "Use HTML for custom E-mail body": "ใช้ HTML สำหรับเนื้อหาอีเมลแบบกำหนดเอง", + "Disable STARTTLS": "ปิดใช้งาน STARTTLS", + "disableSTARTTLSDescription": "เปิดใช้งานตัวเลือกนี้สำหรับเซิร์ฟเวอร์ SMTP ที่ไม่รองรับ STARTTLS การดำเนินการนี้จะส่งอีเมลผ่านการเชื่อมต่อที่ไม่เข้ารหัส", + "deleteMonitorsMsg": "คุณแน่ใจหรือไม่ว่าต้องการลบมอนิเตอร์ที่เลือกไว้?", + "pausedMonitorsMsg": "หยุดการทำงานของมอนิเตอร์ {n} มอนิเตอร์ | หยุดการทำงานของมอนิเตอร์ {n} มอนิเตอร์", + "resumedMonitorsMsg": "กลับมาใช้งานมอนิเตอร์ {n} มอนิเตอร์ | กลับมาใช้งานมอนิเตอร์ {n} มอนิเตอร์", + "deleteGroupMsg": "คุณแน่ใจหรือไม่ว่าต้องการลบกลุ่มนี้?", + "bulkDeleteErrorMsg": "ไม่สามารถลบมอนิเตอร์ {n} ตัว | ไม่สามารถลบมอนิเตอร์ {n} ตัว", + "Resolve": "แก้ไข", + "matrixUseTemplateDescription": "หากเปิดใช้งาน ระบบจะส่งข้อความโดยใช้ template ที่กำหนดเอง", + "noMonitorsResumedMsg": "ไม่มีมอนิเตอร์ใดกลับมาทำงาน (ไม่มีมอนิเตอร์ใดอยู่ในสถานะหยุดทำงาน)", + "teamsEnableTags": "รวมแท็ก", + "teamsEnableTagsDescription": "หากเปิดใช้งาน ข้อความจะรวมแท็กมอนิเตอร์ไว้ด้วย", + "Notification Action": "จัดการการแจ้งเตือน", + "Bearer Token": "โทเค็น Bearer", + "deletedMonitorsMsg": "ลบมอนิเตอร์ {n} ตัว | ลบมอนิเตอร์ {n} ตัว", + "noMonitorsPausedMsg": "ไม่มีมอนิเตอร์ตัวใดหยุดทำงาน (ไม่มีตัวใดทำงานอยู่)", + "wayToGetMaxToken": "คุณสามารถรับโทเค็นบอท MAX และรายละเอียดอื่นๆ ได้จาก {0}", + "Resolver Server(s)": "เซิร์ฟเวอร์ตัวแก้ไข", + "maxTemplateFormatDescription": "MAX Messenger รองรับการจัดรูปแบบข้อความธรรมดา, Markdown และ HTML", + "SMTP Security": "ความปลอดภัยของ SMTP", + "Resolved": "แก้ไขแล้ว", + "createdAt": "สร้างเมื่อ: {date}", + "lastUpdatedAt": "อัปเดตล่าสุด: {date}", + "descriptionHelpText": "แสดงบนแดชบอร์ดภายใน อนุญาตให้ใช้ Markdown และจะมีการตรวจสอบความถูกต้อง (รักษาระยะห่างและการเยื้อง) ก่อนแสดงผล", + "lastUpdatedAtFromNow": "อัปเดตล่าสุด: {date} ({fromNow})", + "auto-select": "เลือกอัตโนมัติ", + "Actions": "จัดการ", + "selectedMonitorCountMsg": "เลือกแล้ว: {n} | เลือกแล้ว: {n}", + "slug is not found": "ไม่พบ Slug", + "Please input content": "โปรดป้อนเนื้อหา", + "Please input title": "กรุณาใส่ชื่อเรื่อง", + "dateCreatedAtFromNow": "วันที่สร้าง: {date} ({fromNow})", + "RSS Title": "ชื่อ RSS", + "Leave blank to use status page title": "เว้นว่างไว้เพื่อใช้เป็นชื่อหน้าสถานะ", + "See Jira Cloud Docs": "ดูเอกสาร Jira Cloud", + "Cloud ID": "คลาวด์ไอดี", + "API Token": "โทเค็น API", + "certHostnameMismatch": "ชื่อโฮสต์ของใบรับรองไม่ตรงกับ URL ของมอนิเตอร์", + "frontendVersionIs": "Frontend เวอร์ชัน: {version}", + "cronScheduleDescription": "กำหนดการ: {description}", + "Duration (Minutes)": "ระยะเวลา (นาที)", + "ariaPauseMaintenance": "หยุดการบำรุงรักษาตามกำหนดการนี้ชั่วคราว", + "notificationChatPlatforms": "แพลตฟอร์มแชท", + "notificationPushServices": "บริการPush", + "notificationSmsServices": "บริการ SMS", + "notificationEmail": "อีเมล", + "notificationIncidentManagement": "การจัดการเหตุการณ์", + "notificationUniversal": "สากล", + "API Version": "เวอร์ชั่น API", + "Connection Timeout": "หมดเวลาการเชื่อมต่อ", + "wildcardOnlyForDNS": "การใช้ชื่อโฮสต์แบบไวด์การ์ดรองรับเฉพาะ DNS เท่านั้น", + "invalidHostnameOrIP": "ชื่อโฮสต์หรือที่อยู่ IP ไม่ถูกต้อง ชื่อโฮสต์ต้องเป็นชื่อโดเมนแบบเต็ม (FQDN) ที่ถูกต้อง ไม่สามารถใช้สัญลักษณ์ตัวแทน (wildcard) ได้ สามารถมีเครื่องหมายขีดล่าง (_) หรือลงท้ายด้วยจุด (.) ได้", + "invalidDNSHostname": "ชื่อโฮสต์ไม่ถูกต้อง ชื่อโฮสต์ต้องเป็นชื่อโดเมนแบบเต็ม (FQDN) ที่ถูกต้อง สามารถใช้สัญลักษณ์ตัวแทน (wildcard) มีเครื่องหมายขีดล่าง (_) หรือลงท้ายด้วยจุดได้", + "selectMonitorMsg": "เลือกมอนิเตอร์เพื่อดำเนินการต่างๆ", + "selectAllMonitorsAria": "เลือกมอนิเตอร์ทั้งหมด", + "deselectAllMonitorsAria": "ยกเลิกการเลือกมอนิเตอร์ทั้งหมด", + "deleteIncidentMsg": "คุณแน่ใจหรือไม่ว่าต้องการลบเหตุการณ์นี้?", + "maxApiUrlDescription": "Base URL API สำหรับแอปพลิเคชันส่งข้อความ MAX. ค่าเริ่มต้น: https://platform-api.max.ru", + "supportBaleChatID": "รองรับการแชทโดยตรง / กลุ่ม / รหัสแชทของช่อง", + "wayToGetBaleToken": "คุณสามารถรับ token ได้จาก {0}", + "notificationHomeAutomation": "ระบบบ้านอัจฉริยะ", + "notificationOther": "การผสานรวมอื่นๆ", + "Ignore STARTTLS": "ละเว้นคำสั่ง STARTTLS", + "Use STARTTLS": "ใช้ STARTTLS", + "invalidURL": "URL ไม่ถูกต้อง", + "Certificate Chain:": "ลำดับชั้นของใบรับรอง:", + "Examples:": "ตัวอย่าง: {0}", + "Clone Maintenance": "บำรุงรักษาClone" +} diff --git a/src/lang/tr-TR.json b/src/lang/tr-TR.json new file mode 100644 index 0000000..c759383 --- /dev/null +++ b/src/lang/tr-TR.json @@ -0,0 +1,1521 @@ +{ + "languageName": "Türkçe", + "checkEverySecond": "{0} saniyede bir kontrol et", + "retryCheckEverySecond": "{0} saniyede bir dene", + "resendEveryXTimes": "Her {0} bir yeniden gönder", + "resendDisabled": "Yeniden gönderme devre dışı", + "retriesDescription": "Servisin kapalı olarak işaretlenmeden ve bir bildirim gönderilmeden önce maksimum yeniden deneme sayısı", + "ignoreTLSError": "HTTPS web siteleri için TLS/SSL hatalarını yok say", + "upsideDownModeDescription": "Servisin durumunu tersine çevirir. Servis çalışıyorsa kapalı olarak işaretler.", + "maxRedirectDescription": "İzlenecek maksimum yönlendirme sayısı. Yönlendirmeleri devre dışı bırakmak için 0'a ayarlayın.", + "acceptedStatusCodesDescription": "Başarılı bir yanıt olarak kabul edilen durum kodlarını seçin.", + "passwordNotMatchMsg": "Şifre eşleşmiyor.", + "notificationDescription": "Servislerin bildirim gönderebilmesi için bir bildirim yöntemi belirleyin.", + "keywordDescription": "Anahtar kelimeyi düz html veya JSON yanıtında arayın ve büyük/küçük harfe duyarlıdır.", + "pauseDashboardHome": "Duraklatılan", + "deleteMonitorMsg": "Servisi silmek istediğinden emin misin?", + "deleteNotificationMsg": "Bu bildirimi tüm servisler için silmek istediğinden emin misin?", + "dnsPortDescription": "DNS sunucusu bağlantı noktası. Varsayılan değer 53'tür. Bağlantı noktasını istediğiniz zaman değiştirebilirsiniz.", + "resolverserverDescription": "Cloudflare varsayılan sunucudur, çözümleyici sunucusunu istediğiniz zaman değiştirebilirsiniz.", + "rrtypeDescription": "İzlemek istediğiniz servisin RR-Tipini seçin", + "pauseMonitorMsg": "Durdurmak istediğinden emin misin?", + "enableDefaultNotificationDescription": "Bu bildirim, yeni eklenen izleyiciler için varsayılan olarak etkin olacaktır. Yine de her izleyici için bildirimi ayrı ayrı devre dışı bırakabilirsiniz.", + "clearEventsMsg": "Bu monitöre ait tüm olayları silmek istediğinizden emin misiniz? Bu işlem, ilgili mesaj verilerini kalıcı olarak siler ve geri alınamaz.", + "clearHeartbeatsMsg": "Bu monitördeki tüm sağlık durumu verilerini silmek istediğinizden emin misiniz? Bu işlem, tüm sağlık durumu verilerini kalıcı olarak siler ve geri alınamaz.", + "confirmClearStatisticsMsg": "Tüm istatistikleri silmek istediğinden emin misin?", + "importHandleDescription": "Aynı isimdeki bütün servisleri ve bildirimleri atlamak için 'Var olanı atla' seçiniz. 'Üzerine yaz' var olan bütün servisleri ve bildirimleri silecektir.", + "confirmImportMsg": "Yedeği içeri aktarmak istediğinize emin misiniz? Lütfen doğru içeri aktarma seçeneğini seçtiğinizden emin olunuz.", + "twoFAVerifyLabel": "2FA doğrulamasını sağlamak için lütfen token bilgisini giriniz:", + "tokenValidSettingsMsg": "Token geçerli! Şimdi 2FA ayarlarını kaydedebilirsiniz.", + "confirmEnableTwoFAMsg": "2FA'ı etkinleştirmek istediğinizden emin misiniz?", + "confirmDisableTwoFAMsg": "2FA'ı devre dışı bırakmak istediğinize emin misiniz?", + "Settings": "Ayarlar", + "Dashboard": "Panel", + "New Update": "Yeni Güncelleme", + "Language": "Dil", + "Appearance": "Görünüm", + "Theme": "Tema", + "General": "Genel", + "Primary Base URL": "Birincil Temel URL", + "Version": "Versiyon", + "Check Update On GitHub": "Güncellemeyi GitHub'da Kontrol Edin", + "List": "Liste", + "Add": "Ekle", + "Add New Monitor": "Yeni Monitör Ekle", + "Quick Stats": "Servis istatistikleri", + "Up": "Aktif", + "Down": "Hizmet Dışı", + "Pending": "Bekliyor", + "Unknown": "Bilinmeyen", + "Pause": "Durdur", + "Name": "Servis ismi", + "Status": "Durum", + "DateTime": "Zaman", + "Message": "Mesaj", + "No important events": "Önemli olay yok", + "Resume": "Devam et", + "Edit": "Düzenle", + "Delete": "Sil", + "Current": "Şu anda", + "Uptime": "Çalışma zamanı", + "Cert Exp.": "Sertifika Geçerlilik Süresi.", + "day": "gün | günler", + "-day": "-gün", + "hour": "saat", + "-hour": "-saat", + "Response": "Cevap Süresi", + "Ping": "Ping", + "Monitor Type": "Servis Tipi", + "Keyword": "Anahtar Kelime", + "Friendly Name": "Panelde görünecek isim", + "URL": "URL", + "Hostname": "Host adı", + "Port": "Port", + "Heartbeat Interval": "Servis Test Aralığı", + "Retries": "Yeniden deneme", + "Heartbeat Retry Interval": "Hata Durumunda Test Aralığı", + "Resend Notification if Down X times consecutively": "Art arda X kez düşerse bildirimi yeniden gönder", + "Advanced": "Gelişmiş", + "Upside Down Mode": "Ters/Düz Modu", + "Max. Redirects": "Maksimum Yönlendirme Sayısı", + "Accepted Status Codes": "Kabul Edilen Durum Kodları", + "Push URL": "Push URL", + "needPushEvery": "Bu URL'yi her {0} saniyede bir aramalısınız.", + "pushOptionalParams": "İsteğe bağlı parametreler: {0}", + "Save": "Kaydet", + "Notifications": "Bildirimler", + "Not available, please setup.": "Mevcut değil, lütfen ayarlardan belirleyin.", + "Setup Notification": "Bildirim yöntemi ayarla", + "Light": "Açık", + "Dark": "Koyu", + "Auto": "Oto", + "Theme - Heartbeat Bar": "Servis Bar Konumu", + "Normal": "Normal", + "Bottom": "Aşağıda", + "None": "Gösterme", + "Timezone": "Zaman Dilimi", + "Search Engine Visibility": "Arama Motoru Görünürlüğü", + "Allow indexing": "İndekslemeye izin ver", + "Discourage search engines from indexing site": "Arama motorlarının siteyi indekslemesini engelleyin", + "Change Password": "Şifre Değiştir", + "Current Password": "Mevcut Şifre", + "New Password": "Yeni Şifre", + "Repeat New Password": "Yeni Şifreyi Tekrar Girin", + "Update Password": "Şifreyi Değiştir", + "Disable Auth": "Şifreli girişi iptal et", + "Enable Auth": "Şifreli girişi aktif et", + "disableauth.message1": "{disableAuth} emin misiniz?", + "disable authentication": "Şifreli girişi devre dışı bırakmak istediğinizden", + "disableauth.message2": "Bu, Uptime Kuma'nın önünde Cloudflare Access gibi {intendThirdPartyAuth} kişiler içindir.", + "where you intend to implement third-party authentication": "üçüncü taraf yetkilendirmesi olan", + "Please use this option carefully!": "Lütfen dikkatli kullanın!", + "Logout": "Çıkış yap", + "Leave": "Ayrıl", + "I understand, please disable": "Evet farkındayım, iptal et", + "Confirm": "Onayla", + "Yes": "Evet", + "No": "Hayır", + "Username": "Kullanıcı Adı", + "Password": "Şifre", + "Remember me": "Beni Hatırla", + "Login": "Giriş yap", + "No Monitors, please": "Servis yok, lütfen", + "add one": "bir servis ekleyin", + "Notification Type": "Bildirim Yöntemi", + "Email": "E-mail", + "Test": "Test", + "Certificate Info": "Sertifika Bilgisi", + "Resolver Server": "Çözümleyici Sunucu", + "Resource Record Type": "Kaynak Kayıt Türü", + "Last Result": "En son sonuçlar", + "Create your admin account": "Yönetici hesabınızı oluşturun", + "Repeat Password": "Şifrenizi tekrar girin", + "Import Backup": "Yedeği içe aktar", + "Export Backup": "Yedeği dışa aktar", + "Export": "Dışa aktar", + "Import": "İçe aktar", + "respTime": "Cevap Süresi (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Varsayılan etkinleştirilmiş", + "Apply on all existing monitors": "Var olan bütün servislere uygula", + "Create": "Oluştur", + "Clear Data": "Verileri Temizle", + "Events": "Olaylar", + "Heartbeats": "Sağlık Durumları", + "Auto Get": "Otomatik Al", + "backupDescription": "Bütün servisleri ve bildirimleri JSON dosyasına yedekleyebilirsiniz.", + "backupDescription2": "Not: Geçmiş ve etkinlik verileri içinde değildir.", + "backupDescription3": "Dışa aktarma dosyasında bildirim tokeni gibi hassas veriler bulunur, dikkatli bir şekilde saklayınız.", + "alertNoFile": "İçeri aktarmak için bir dosya seçiniz.", + "alertWrongFileType": "Lütfen bir JSON dosyası seçiniz.", + "Clear all statistics": "Bütün istatistikleri temizle", + "Skip existing": "Var olanı atla", + "Overwrite": "Üzerine yaz", + "Options": "Seçenekler", + "Keep both": "İkisini sakla", + "Verify Token": "Tokeni doğrula", + "Setup 2FA": "2FA Ayarla", + "Enable 2FA": "2FA Etkinleştir", + "Disable 2FA": "2FA Devre dışı bırak", + "2FA Settings": "2FA Ayarları", + "Two Factor Authentication": "İki Faktörlü Kimlik Doğrulama (2FA)", + "Active": "Aktif", + "Inactive": "İnaktif", + "Token": "Token", + "Show URI": "URI'yi göster", + "Tags": "Etiketler", + "Add New below or Select...": "Aşağıya Yeni Ekle veya Seç…", + "Tag with this name already exist.": "Bu ada sahip etiket zaten var.", + "Tag with this value already exist.": "Bu değere sahip etiket zaten var.", + "color": "renk", + "value (optional)": "değer (isteğe bağlı)", + "Gray": "Gri", + "Red": "Kırmızı", + "Orange": "Turuncu", + "Green": "Yeşil", + "Blue": "Mavi", + "Indigo": "Çivit mavisi", + "Purple": "Mor", + "Pink": "Pembe", + "Search...": "Ara…", + "Avg. Ping": "Ortalama Ping", + "Avg. Response": "Ortalama Cevap Süresi", + "Entry Page": "Giriş Sayfası", + "statusPageNothing": "Burada hiçbir şey yok, lütfen bir grup veya servis ekleyin.", + "No Services": "Hizmet Yok", + "All Systems Operational": "Tüm Sistemler Sorunsuz", + "Partially Degraded Service": "Kısmen Bozulmuş Hizmet", + "Degraded Service": "Bozulmuş Hizmet", + "Add Group": "Grup Ekle", + "Add a monitor": "Servis Ekle", + "Edit Status Page": "Durum Sayfasını Düzenle", + "Go to Dashboard": "Panele Git", + "Status Page": "Durum Sayfası", + "Status Pages": "Durum Sayfaları", + "defaultNotificationName": "Benim {notification} Alarmım ({number})", + "here": "burada", + "Required": "Gerekli", + "telegram": "Telegram", + "Bot Token": "Bot Anahtarı", + "wayToGetTelegramToken": "{0} adresinden bir token alabilirsiniz.", + "Chat ID": "Chat ID", + "supportTelegramChatID": "Doğrudan Sohbet / Grup / Kanalın Sohbet Kimliğini Destekleyin", + "wayToGetTelegramChatID": "Bot'a bir mesaj göndererek ve chat_id'yi görüntülemek için bu URL'ye giderek sohbet kimliğinizi alabilirsiniz:", + "YOUR BOT TOKEN HERE": "BOT TOKENİNİZ BURAYA", + "chatIDNotFound": "Chat ID bulunamadı; lütfen önce bu bota bir mesaj gönderin", + "webhook": "Webhook", + "Post URL": "Post URL", + "Content Type": "Content Type", + "webhookJsonDesc": "{0}, Express.js gibi tüm modern HTTP sunucuları için iyidir", + "webhookFormDataDesc": "{multipart} PHP için iyidir. JSON'un {decodeFunction} ile ayrıştırılması gerekecek", + "smtp": "E-mail (SMTP)", + "secureOptionNone": "Hiçbiri / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "TLS Hatasını Yoksay", + "From Email": "E-postadan", + "emailCustomSubject": "Özel Konu", + "To Email": "E-postaya", + "smtpCC": "CC", + "smtpBCC": "BCC", + "discord": "Discord", + "Discord Webhook URL": "Discord Webhook Bağlantısı", + "wayToGetDiscordURL": "Bunu Sunucu Ayarları -> Entegrasyonlar -> Webhookları Görüntüle -> Yeni Webhook Oluştur adımını izleyerek alabilirsiniz", + "Bot Display Name": "Botun Görünecek Adı", + "Prefix Custom Message": "Özel Önek Mesajı", + "Hello @everyone is...": "Merhaba {'@'}everyone…", + "teams": "Microsoft Teams", + "Webhook URL": "Webhook URL'si", + "wayToGetTeamsURL": "Bir webhook URL'sinin nasıl oluşturulacağını öğrenebilirsiniz {0}.", + "signal": "Sinyal", + "Number": "Numara", + "Recipients": "Alıcılar", + "needSignalAPI": "REST API ile bir signal istemciniz olması gerekiyor.", + "wayToCheckSignalURL": "Nasıl kurulacağını görmek için bu URL'yi kontrol edebilirsiniz:", + "signalImportant": "ÖNEMLİ: Alıcılarda grupları ve sayıları karıştıramazsınız!", + "gotify": "Gotify", + "Application Token": "Uygulama Tokeni", + "Server URL": "Sunucu URL", + "Priority": "Öncelik", + "Icon Emoji": "İkon Emoji", + "Channel Name": "Kanal Adı", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Webhook hakkında daha fazla bilgi: {0}", + "aboutChannelName": "Webhook kanalını atlamak istiyorsanız, {0} Kanal Adı alanına kanal adını girin. Ör: #diğer-kanal", + "aboutKumaURL": "Uptime Kuma URL alanını boş bırakırsanız, varsayılan olarak Project GitHub sayfası olur.", + "emojiCheatSheet": "Emoji referans sayfası: {0}", + "PushByTechulus": "Push by Techulus", + "apprise": "Apprise (50'den fazla Bildirim hizmetini destekler)", + "GoogleChat": "Google Chat (sadece Google Workspace)", + "pushbullet": "Pushbullet", + "User Key": "Kullancı Anahtarı", + "Device": "Cihaz", + "Message Title": "Mesaj Başlığı", + "Notification Sound": "Bilgilendirme sesi", + "More info on:": "Daha fazla bilgi: {0}", + "pushoverDesc1": "Acil durum önceliği (2), yeniden denemeler arasında varsayılan olarak 30 saniyelik bir zaman aşımına sahiptir ve 1 saat sonra sona erecektir.", + "pushoverDesc2": "Farklı cihazlara bildirim göndermek istiyorsanız Cihaz alanını doldurunuz.", + "SMS Type": "SMS Tipi", + "octopushTypePremium": "Premium (Hızlı - uyarı için önerilir)", + "octopushTypeLowCost": "Düşük Maliyet (Yavaş - bazen operatör tarafından engellenir)", + "checkPrice": "{0} fiyatlarını kontrol edin:", + "apiCredentials": "API kimlik bilgileri", + "octopushLegacyHint": "Octopush'un (2011-2020) eski sürümünü mü yoksa yeni sürümünü mü kullanıyorsunuz?", + "Check octopush prices": "Octopush fiyatlarını kontrol edin {0}.", + "octopushPhoneNumber": "Telefon numarası (uluslararası biçim, örneğin: +33612345678)", + "octopushSMSSender": "SMS Gönderici Adı : 3-11 alfanümerik karakter ve boşluk (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Cihaz ID", + "Apprise URL": "Apprise Bağlantısı", + "Example:": "Örnek: {0}", + "Read more:": "Daha fazla oku: {0}", + "Status:": "Durum: {0}", + "Read more": "Daha fazla oku", + "appriseInstalled": "Apprise yüklendi.", + "appriseNotInstalled": "Appris yüklü değil. {0}", + "Access Token": "Erişim Tokeni", + "Channel access token": "Kanal erişim tokeni", + "Line Developers Console": "Line Geliştirici Konsolu", + "lineDevConsoleTo": "Line Geliştirici Konsolu - {0}", + "Basic Settings": "Temel Ayarlar", + "User ID": "Kullanıcı ID", + "Messaging API": "Mesajlaşma API'si", + "wayToGetLineChannelToken": "Önce {0}'e erişin, bir sağlayıcı ve kanal (Messaging API) oluşturun, ardından yukarıda belirtilen menü öğelerinden kanal erişim tokenini ve kullanıcı id alabilirsiniz.", + "Icon URL": "Simge URL", + "aboutIconURL": "Varsayılan profil resmini geçersiz kılmak için \"Simge URL\" bölümünde bir resme bağlantı sağlayabilirsiniz. Simge Emojisi ayarlanmışsa kullanılmayacaktır.", + "aboutMattermostChannelName": "Kanal adını \"Kanal Adı\" alanına girerek Webhook'un gönderi yaptığı varsayılan kanalı geçersiz kılabilirsiniz. Bunun Mattermost Webhook ayarlarında etkinleştirilmesi gerekir. Ör: #diğer-kanal", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - ucuz ama yavaş ve genellikle aşırı yüklü. Yalnızca Polonyalı alıcılarla sınırlıdır.", + "promosmsTypeFlash": "SMS FLASH - Mesaj, alıcı cihazda otomatik olarak gösterilecektir. Yalnızca Polonyalı alıcılarla sınırlıdır.", + "promosmsTypeFull": "SMS FULL - Premium SMS katmanı, Gönderici Adınızı kullanabilirsiniz (Önce adınızı kaydetmeniz gerekir). Uyarılar için güvenilir.", + "promosmsTypeSpeed": "SMS HIZI - Sistemde en yüksek öncelik. Çok hızlı ve güvenilir ancak maliyetli (SMS FULL fiyatının yaklaşık iki katı).", + "promosmsPhoneNumber": "Telefon numarası (Polonyalı alıcı için Alan kodlarını atlayabilirsiniz)", + "promosmsSMSSender": "SMS Gönderici Adı : Ön kayıtlı ad veya varsayılanlardan biri: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu Webhook URL’si", + "matrixHomeserverURL": "Homeserver URL (http(s):// ve isteğe bağlı olarak bağlantı noktası ile)", + "Internal Room Id": "Dahili Oda Kimliği", + "matrixDesc1": "Internal Room ID'sini, Matrix istemcinizdeki oda ayarlarının gelişmiş bölümüne bakarak bulabilirsiniz. !QMdRCpUIfLwsfjxye6:home.server gibi görünmelidir.", + "matrixDesc2": "Hesabınıza ve katıldığınız tüm odalara tam erişime izin vereceğinden, yeni bir kullanıcı oluşturmanız ve kendi Matrix kullanıcınızın erişim belirtecini kullanmamanız şiddetle tavsiye edilir. Bunun yerine, yeni bir kullanıcı oluşturun ve onu yalnızca bildirimi almak istediğiniz odaya davet edin. {0} komutunu çalıştırarak erişim tokenini alabilirsiniz", + "Method": "Yöntem", + "Body": "Gövde", + "Headers": "Başlıklar", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "İstek başlıkları geçerli JSON değil. ", + "BodyInvalidFormat": "İstek gövdesi geçerli JSON değil: ", + "Monitor History": "Servis Geçmişi", + "clearDataOlderThan": "{0} gün boyunca izleme geçmişi verilerini saklayın.", + "PasswordsDoNotMatch": "Parolalar uyuşmuyor.", + "records": "kayıtlar", + "One record": "Bir Kayıt", + "steamApiKeyDescription": "Bir Steam Oyun Sunucusunu izlemek için bir Steam Web-API anahtarına ihtiyacınız vardır. API anahtarınızı buradan kaydedebilirsiniz: ", + "Current User": "Şu anki kullanıcı", + "topic": "Başlık", + "topicExplanation": "İzlenecek MQTT servisi", + "successMessage": "Başarılı Mesaj", + "successMessageExplanation": "Başarılı olarak kabul edilecek MQTT mesajı", + "recent": "Son", + "Done": "Tamamlandı", + "Info": "Bilgi", + "Security": "Güvenlik", + "Steam API Key": "Steam API Anahtarı", + "Shrink Database": "Veritabanını Küçült", + "Pick a RR-Type...": "Bir RR-Tipi seçin…", + "Pick Accepted Status Codes...": "Kabul Edilen Durum Kodlarını Seçin…", + "Default": "Varsayılan", + "HTTP Options": "HTTP Ayarları", + "Create Incident": "Olay Oluştur", + "Title": "Başlık", + "Content": "İçerik", + "Style": "Stil", + "info": "bilgi", + "warning": "uyarı", + "danger": "tehlike", + "error": "hata", + "critical": "kritik", + "primary": "öncelik", + "light": "hafif", + "dark": "koyu", + "Post": "Post", + "Please input title and content": "Lütfen başlık ve içerik girin", + "Created": "Oluşturuldu", + "Last Updated": "Son Güncelleme", + "Unpin": "Sabitlemeyi Kaldır", + "Switch to Light Theme": "Açık Temaya Geç", + "Switch to Dark Theme": "Karanlık Temaya Geç", + "Show Tags": "Etiketleri Göster", + "Hide Tags": "Etiketleri Gizle", + "Description": "Açıklama", + "No monitors available.": "Kullanılabilir servis yok.", + "Add one": "Bir tane ekle", + "No Monitors": "Servis Yok", + "Untitled Group": "Adsız Grup", + "Services": "Hizmetler", + "Discard": "İptal Et", + "Cancel": "İptal Et", + "Powered by": "Tarafından desteklenmektedir", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API Kullanıcı Adı (webapi_ öneki dahil)", + "serwersmsAPIPassword": "API Şifresi", + "serwersmsPhoneNumber": "Telefon numarası", + "serwersmsSenderName": "SMS Gönderici Adı (müşteri portalı üzerinden kayıtlı)", + "stackfield": "Stackfield", + "Customize": "Özelleştirme", + "Custom Footer": "Özel Altbilgi", + "Custom CSS": "Özel CSS", + "smtpDkimSettings": "DKIM Ayarları", + "smtpDkimDesc": "Kullanım için lütfen Nodemailer DKIM'e {0} bakın.", + "documentation": "belgeler", + "smtpDkimDomain": "Alan adı", + "smtpDkimKeySelector": "Anahtar Seçici", + "smtpDkimPrivateKey": "Özel anahtar", + "smtpDkimHashAlgo": "Hash Algoritması (Opsiyonel)", + "smtpDkimheaderFieldNames": "İmzalanacak Başlık Anahtarları (Opsiyonel)", + "smtpDkimskipFields": "İmzalamayacak Başlık Anahtarları (Opsiyonel)", + "wayToGetPagerDutyKey": "Bunu Hizmet -> Hizmet Dizini -> (Bir hizmet seçin) -> Entegrasyonlar -> Entegrasyon ekle'ye giderek alabilirsiniz. Burada \"Events API V2\" için arama yapabilirsiniz. Daha fazla bilgi {0}", + "Integration Key": "Entegrasyon Anahtarı", + "Integration URL": "Entegrasyon URL'si", + "Auto resolve or acknowledged": "Otomatik çözümleme veya onaylandı", + "do nothing": "hiçbir şey yapma", + "auto acknowledged": "otomatik onaylandı", + "auto resolve": "otomatik çözümleme", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API Uç Noktası", + "alertaEnvironment": "Ortam", + "alertaApiKey": "API Anahtarı", + "alertaAlertState": "Uyarı Durumu", + "alertaRecoverState": "Kurtarma Durumu", + "deleteStatusPageMsg": "Bu durum sayfasını silmek istediğinizden emin misiniz?", + "Proxies": "Proxy'ler", + "default": "Varsayılan", + "enabled": "Etkinleştirilmiş", + "setAsDefault": "Varsayılan Olarak Ayarla", + "deleteProxyMsg": "Bu proxy'yi tüm servisler için silmek istediğinizden emin misiniz?", + "proxyDescription": "Proxy'lerin çalışması için bir servise atanması gerekir.", + "enableProxyDescription": "Bu proxy, etkinleştirilene kadar izleme isteklerini etkilemeyecektir. Aktivasyon durumuna göre proxy'yi tüm servislerden geçici olarak devre dışı bırakabilirsiniz.", + "setAsDefaultProxyDescription": "Bu proxy, yeni servisler için varsayılan olarak etkinleştirilecektir. Yine de proxy'yi her bir servis için devre dışı bırakabilirsiniz.", + "Certificate Chain": "Sertifika Zinciri", + "Valid": "Geçerli", + "Invalid": "Geçersiz", + "AccessKeyId": "Erişim Anahtarı Kimliği", + "SecretAccessKey": "AccessKey Gizli Anahtarı", + "PhoneNumbers": "Telefon numaraları", + "TemplateCode": "ŞablonKodu", + "SignName": "İmzaAdı", + "Sms template must contain parameters: ": "Sms şablonu parametreleri içermelidir: ", + "Bark Endpoint": "Bark Uç Noktası", + "Bark Group": "Bark Grubu", + "Bark Sound": "Havlama Sesi", + "WebHookUrl": "Webhook URL’si", + "SecretKey": "GizliAnahtar", + "For safety, must use secret key": "Güvenlik için gizli anahtar kullanılmalıdır", + "Device Token": "Cihaz Tokeni", + "Platform": "Platform", + "Huawei": "Huawei", + "High": "High", + "Retry": "Tekrar", + "Topic": "Başlık", + "WeCom Bot Key": "WeCom Bot Anahtarı", + "Setup Proxy": "Proxy Ayarla", + "Proxy Protocol": "Proxy Protokolü", + "Proxy Server": "Proxy Sunucusu", + "Proxy server has authentication": "Proxy sunucusunun kimlik doğrulaması var", + "User": "Kullanıcı", + "Installed": "Yüklenmiş", + "Not installed": "Yüklü değil", + "Running": "Çalışıyor", + "Not running": "Çalışmıyor", + "Remove Token": "Tokeni Kaldır", + "Start": "Başlat", + "Stop": "Durdur", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Yeni Durum Sayfası Ekle", + "Slug": "Slug", + "Accept characters:": "Kabul edilen karakterler:", + "startOrEndWithOnly": "Yalnızca {0} ile başlayın veya bitirin", + "No consecutive dashes": "Ardışık tire yok", + "Next": "Sonraki", + "The slug is already taken. Please choose another slug.": "Slug zaten alındı. Lütfen başka bir slug seçin.", + "No Proxy": "Proxy Yok", + "Authentication": "Kimlik doğrulama", + "HTTP Basic Auth": "HTTP Temel Yetkilendirme", + "New Status Page": "Yeni Durum Sayfası", + "Page Not Found": "Sayfa bulunamadı", + "Reverse Proxy": "Ters Proxy", + "Backup": "Yedek", + "About": "Hakkında", + "wayToGetCloudflaredURL": "(Cloudflared'i {0} adresinden indirin)", + "cloudflareWebsite": "Cloudflare İnternet Sitesi", + "Message:": "Mesaj:", + "Don't know how to get the token? Please read the guide:": "Tokeni nasıl alacağınızı bilmiyor musunuz? Lütfen kılavuzu okuyun:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Halihazırda Cloudflare Tüneli üzerinden bağlanıyorsanız mevcut bağlantı kesilebilir. Durdurmak istediğinden emin misin? Onaylamak için mevcut şifrenizi yazın.", + "HTTP Headers": "HTTP Başlıkları", + "Trust Proxy": "Vekil Sunucuya Güven", + "Other Software": "Diğer Yazılımlar", + "For example: nginx, Apache and Traefik.": "Örneğin: nginx, Apache ve Traefik.", + "Please read": "Lütfen oku", + "Subject:": "Başlık:", + "Valid To:": "Geçerlilik:", + "Days Remaining:": "Kalan günler:", + "Issuer:": "Veren:", + "Fingerprint:": "Parmak izi:", + "No status pages": "Durum sayfası yok", + "Domain Name Expiry Notification": "Alan Adı Sona Erme Bildirimi", + "Proxy": "Proxy", + "Date Created": "Tarih Oluşturuldu", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "OneBot HTTP Adresi", + "onebotMessageType": "OneBot Mesaj Türü", + "onebotGroupMessage": "Grup", + "onebotPrivateMessage": "Özel", + "onebotUserOrGroupId": "Grup/Kullanıcı Kimliği", + "onebotSafetyTips": "Güvenlik için erişim tokeni ayarlamalısınız", + "PushDeer Key": "PushDeer Anahtarı", + "Footer Text": "Altbilgi metni", + "Show Powered By": "\"Powered by\" kısmını göster", + "Domain Names": "Alan isimleri", + "signedInDisp": "{0} olarak oturum açıldı", + "signedInDispDisabled": "Yetkilendirme Devre Dışı.", + "RadiusSecret": "Radius Gizli Anahtar", + "RadiusSecretDescription": "İstemci ve sunucu arasında paylaşılan gizli anahtar", + "RadiusCalledStationId": "Aranan İstasyon Kimliği", + "RadiusCalledStationIdDescription": "Aranan cihazın tanımlayıcısı", + "RadiusCallingStationId": "Arayan İstasyon Kimliği", + "RadiusCallingStationIdDescription": "Arayan cihazın tanımlayıcısı", + "Certificate Expiry Notification": "Sertifika Sona Erme Bildirimi", + "API Username": "API Kullanıc Adı", + "API Key": "API Anahtarı", + "Recipient Number": "Alıcı Numarası", + "From Name/Number": "İsimden/Numaradan", + "Leave blank to use a shared sender number.": "Paylaşılan bir gönderen numarası kullanmak için boş bırakın.", + "Octopush API Version": "Octopush API Sürümü", + "Legacy Octopush-DM": "Eski Octopush-DM", + "endpoint": "uç nokta", + "octopushAPIKey": "Kontrol panelindeki HTTP API kimlik bilgilerinden \"API Key\"", + "octopushLogin": "Kontrol panelindeki HTTP API kimlik bilgilerinden \"Login\"", + "promosmsLogin": "API Oturum Açma Adı", + "promosmsPassword": "API Şifresi", + "pushoversounds pushover": "Pushover (varsayılan)", + "pushoversounds bike": "Bisiklet", + "pushoversounds bugle": "Boru", + "pushoversounds cashregister": "Yazar kasa", + "pushoversounds classical": "Klasik", + "pushoversounds cosmic": "Kozmik", + "pushoversounds falling": "Düşme", + "pushoversounds gamelan": "Oyun Alanı", + "pushoversounds incoming": "Gelen", + "pushoversounds intermission": "Ara", + "pushoversounds magic": "Büyü", + "pushoversounds mechanical": "Mekanik", + "pushoversounds pianobar": "Piano", + "pushoversounds siren": "Uyarı Sesi", + "pushoversounds spacealarm": "Uzay Alarmı", + "pushoversounds tugboat": "Römorkör", + "pushoversounds alien": "Uzaylı Alarmı (uzun)", + "pushoversounds climb": "Tırmanış (uzun)", + "pushoversounds persistent": "Sürekli (uzun)", + "pushoversounds echo": "Pushover Yankı (uzun)", + "pushoversounds updown": "Yukarı Aşağı (uzun)", + "pushoversounds vibrate": "Sadece titreşim", + "pushoversounds none": "Yok (sessiz)", + "pushyAPIKey": "Gizli API Anahtarı", + "pushyToken": "Cihaz tokeni", + "Show update if available": "Varsa güncellemeyi göster", + "Also check beta release": "Ayrıca beta sürümünü kontrol edin", + "Using a Reverse Proxy?": "Ters Proxy mi Kullanıyorsunuz?", + "Check how to config it for WebSocket": "WebSocket için nasıl yapılandırılacağını kontrol edin", + "Steam Game Server": "Steam Oyun Sunucusu", + "Most likely causes:": "En olası nedenler:", + "The resource is no longer available.": "Kaynak artık mevcut değil.", + "There might be a typing error in the address.": "Adreste bir yazım hatası olabilir.", + "What you can try:": "Deneyebileceğin şeyler:", + "Retype the address.": "Adresi tekrar yazın.", + "Go back to the previous page.": "Bir önceki sayfaya geri git.", + "Coming Soon": "Yakında Gelecek", + "wayToGetClickSendSMSToken": "API Kullanıcı Adı ve API Anahtarını {here} üzerinden alabilirsiniz.", + "Connection String": "Bağlantı Dizisi", + "Query": "Sorgu", + "settingsCertificateExpiry": "TLS Sertifikasının Geçerlilik Süresi", + "certificationExpiryDescription": "HTTPS Monitörleri, TLS sertifikasının süresi dolduğunda bildirimi tetikler:", + "Setup Docker Host": "Docker Ana Bilgisayarını Ayarla", + "Connection Type": "Bağlantı türü", + "Docker Daemon": "Docker Servisi", + "deleteDockerHostMsg": "Bu docker ana bilgisayarını tüm monitörler için silmek istediğinizden emin misiniz?", + "socket": "Soket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker Konteyner", + "Container Name / ID": "Konteyner Adı / Kimliği", + "Docker Host": "Docker Ana Bilgisayarı", + "Docker Hosts": "Docker Ana Bilgisayarları", + "ntfy Topic": "ntfy Konu", + "Domain": "Alan Adı", + "Workstation": "İş İstasyonu", + "disableCloudflaredNoAuthMsg": "Yetki yok modundasınız, şifre gerekli değil.", + "trustProxyDescription": "'X-Forwarded-*' başlıklarına güvenin. Doğru istemci IP'sini almak istiyorsanız ve Uptime Kuma'nız Nginx veya Apache gibi bir proxy'nin arkasındaysa, bunu etkinleştirmelisiniz.", + "wayToGetLineNotifyToken": "{0} adresinden bir erişim jetonu alabilirsiniz", + "Examples": "Örnekler", + "Home Assistant URL": "Home Assistant Bağlantısı", + "Long-Lived Access Token": "Long-Lived Erişim Anahtarı", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Long-Lived Erişim Anahtarı, profil adınıza (sol altta) tıklayarak ve aşağıya kaydırarak ve ardından Anahtar Oluştur'a tıklayarak oluşturulabilir.", + "Notification Service": "Bildirim Hizmeti", + "default: notify all devices": "varsayılan: tüm cihazları bilgilendir", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Cihazınızın/telefonunuzun adını bulmak için Home Assistant'ta \"Geliştirici Araçları > Hizmetler\" \"bildirim\" araması altında bir Bildirim Hizmetleri listesi bulunabilir.", + "Automations can optionally be triggered in Home Assistant:": "Otomasyonlar isteğe bağlı olarak Home Assistant'ta tetiklenebilir:", + "Trigger type:": "Trigger tipi:", + "Event type:": "Etkinlik tipi:", + "Event data:": "Etkinlik verileri:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Ardından bir eylem seçin, örneğin RGB ışığının kırmızı olduğu sahneyi değiştirin.", + "Frontend Version": "Frontend Sürümü", + "Frontend Version do not match backend version!": "Frontend Sürümü, backend sürümüyle eşleşmiyor!", + "Base URL": "Temel URL", + "goAlertInfo": "GoAlert, çağrı üzerine zamanlama, otomatik eskalasyonlar ve bildirimler (SMS veya sesli çağrılar gibi) için açık kaynaklı bir uygulamadır. Doğru kişiyi, doğru şekilde ve doğru zamanda otomatik olarak devreye sokun! {0}", + "goAlertIntegrationKeyInfo": "Servis için genel API entegrasyon anahtarını, genellikle kopyalanan URL'nin belirteç parametresinin değeri olan \"aaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" biçiminde alın.", + "goAlert": "GoAlert", + "backupOutdatedWarning": "Kullanımdan kaldırıldı: Birçok özellik eklendiğinden ve bu yedekleme özelliği biraz bakımsız olduğundan tam bir yedekleme oluşturamaz veya geri yükleyemez.", + "backupRecommend": "Lütfen bunun yerine birimi veya veri klasörünü (./data/) doğrudan yedekleyin.", + "enableGRPCTls": "TLS bağlantısıyla gRPC isteği göndermeye izin ver", + "grpcMethodDescription": "Yöntem adı, sayHello, check, vb. gibi camelCase biçimine dönüştürülür.", + "Maintenance": "Bakım", + "statusMaintenance": "Bakım", + "Schedule maintenance": "Bakım Planla", + "Affected Monitors": "Etkilenen Monitörler", + "Pick Affected Monitors...": "Etkilenen Monitörleri Seçin…", + "Start of maintenance": "Bakım başlangıcı", + "All Status Pages": "Tüm Durum Sayfaları", + "Select status pages...": "Durum sayfalarını seçin…", + "recurringIntervalMessage": "Her gün bir kez çalıştırın | {0} günde bir çalıştırın", + "affectedMonitorsDescription": "Geçerli bakımdan etkilenen monitörleri seçin", + "affectedStatusPages": "Bu bakım mesajını seçili durum sayfalarında göster", + "atLeastOneMonitor": "Etkilenen en az bir monitör seçin", + "deleteMaintenanceMsg": "Bu bakımı silmek istediğinizden emin misiniz?", + "ZohoCliq": "ZohoCliq", + "webhookAdditionalHeadersTitle": "Ek Başlıklar", + "webhookAdditionalHeadersDesc": "Webhook ile gönderilen ek başlıkları ayarlar. Her başlık bir JSON anahtarı/değeri olarak tanımlanmalıdır.", + "wayToGetZohoCliqURL": "Bir webhook URL'sinin nasıl oluşturulacağını öğrenebilirsiniz {0}.", + "Kook": "Kook", + "wayToGetKookBotToken": "Uygulama oluşturun ve {0} adresinde bot tokenı alın", + "wayToGetKookGuildID": "Kook ayarında \"Geliştirici Modu\"nu açın ve kimliğini almak için guild'e sağ tıklayın", + "Guild ID": "Sunucu ID'si", + "smseagle": "SMSEagle", + "smseagleTo": "Telefon numara(ları)", + "smseagleGroup": "Telefon defteri grubu ad(lar)ı", + "smseagleContact": "Telefon rehberi kişi ad(lar)ı", + "smseagleRecipientType": "Alıcı Türü", + "smseagleRecipient": "Alıcı(lar) (birden çok olanlar virgülle ayrılmalıdır)", + "smseagleToken": "API Erişim Tokenı", + "smseagleUrl": "SMSEagle cihaz URL\"niz", + "smseagleEncoding": "Unicode olarak gönder (varsayılan=GSM-7)", + "smseaglePriority": "Mesaj önceliği (0-9, en yüksek öncelik = 9)", + "Optional": "İsteğe bağlı", + "squadcast": "Squadcast", + "SendKey": "Gönderim Anahtarı", + "SMSManager API Docs": "SMSManager API Dökümanları", + "Gateway Type": "Ağ Geçidi Türü", + "SMSManager": "SMSManager", + "You can divide numbers with": "Sayıları aşağıdakilerle bölebilirsiniz", + "or": "veya", + "recurringInterval": "Sıklık", + "Recurring": "Yineleme", + "strategyManual": "Manuel olarak Aktif/Pasif", + "warningTimezone": "Sunucunun kullandığı saat dilimi", + "weekdayShortMon": "Pzt", + "weekdayShortTue": "Sal", + "weekdayShortWed": "Çar", + "weekdayShortThu": "Per", + "weekdayShortFri": "Cum", + "weekdayShortSat": "Cmt", + "weekdayShortSun": "Paz", + "dayOfWeek": "Haftanın Günleri", + "dayOfMonth": "Ayın Günleri", + "lastDay": "Son Gün", + "lastDay1": "Ayın Son Günü", + "lastDay2": "Ayın 2. Son Günü", + "lastDay3": "Ayın 3. Son Günü", + "lastDay4": "Ayın 4. Son Günü", + "No Maintenance": "Bakım Yok", + "pauseMaintenanceMsg": "Duraklatmak istediğinizden emin misiniz?", + "maintenanceStatus-under-maintenance": "Bakımda", + "maintenanceStatus-inactive": "Etkin Değil", + "maintenanceStatus-scheduled": "Planlanmış", + "maintenanceStatus-ended": "Bitti", + "maintenanceStatus-unknown": "Bilinmiyor", + "Display Timezone": "Saat dilimini göster", + "Server Timezone": "Sunucu Saat Dilimi", + "statusPageMaintenanceEndDate": "Bitiş Zamanı", + "IconUrl": "Icon URL", + "Enable DNS Cache": "(Kullanımdan kaldırıldı) HTTP(ler) monitörleri için DNS Önbelleğini etkinleştirin", + "Enable": "Etkin", + "Disable": "Devre Dışı", + "dnsCacheDescription": "Bazı IPv6 ortamlarında çalışmıyor olabilir, herhangi bir sorunla karşılaşırsanız devre dışı bırakın.", + "Single Maintenance Window": "Tek Seferlik Bakım", + "Maintenance Time Window of a Day": "Bür Günlük Bakım", + "Effective Date Range": "Geçerlilik Tarihi Aralığı (Opsiyonel)", + "Schedule Maintenance": "Bakım Planla", + "Date and Time": "Tarih ve Saat", + "DateTime Range": "Tarih ve Saat Aralığı", + "Strategy": "Strateji", + "Free Mobile User Identifier": "Ücretsiz Mobil Kullanıcı ID", + "Free Mobile API Key": "Ücretsiz Mobil API Anahtarı", + "Enable TLS": "TLS'yi Etkinleştir", + "Proto Service Name": "Proto Service İsmi", + "Proto Method": "Proto Yöntemi", + "Proto Content": "Proto İçeriği", + "Economy": "Ekonomik", + "Lowcost": "Düşük maliyetli", + "high": "Yüksek", + "General Monitor Type": "Genel Monitör Tipi", + "Passive Monitor Type": "Pasif Monitör Tipi", + "Specific Monitor Type": "Özel Monitör Tipi", + "Help": "Yardım", + "Monitor": "Monitör | Monitörler", + "Custom": "Özel", + "dataRetentionTimeError": "Saklama süresi 0 veya daha büyük olmalıdır", + "confirmDeleteTagMsg": "Bu etiketi silmek istediğinizden emin misiniz? Bu etiketle ilişkili monitörler silinmez.", + "promosmsAllowLongSMS": "Uzun SMS'e izin ver", + "infiniteRetention": "Sonsuza dek saklamak için 0 giriniz.", + "rocket.chat": "Rocket.Chat", + "slack": "Slack", + "pushover": "Pushover", + "Game": "Oyun", + "Packet Size": "Paket Boyutu", + "Custom Monitor Type": "Özel Monitör Tipi", + "clicksendsms": "ClickSend SMS", + "loadingError": "Veriler getirilemiyor, lütfen daha sonra tekrar deneyin.", + "plugin": "Eklenti | Eklentiler", + "install": "Yükle", + "installing": "Yükleniyor", + "uninstall": "Kaldır", + "uninstalling": "Yükleme kaldırılıyor", + "confirmUninstallPlugin": "Bu eklentiyi kaldırmak istediğinizden emin misiniz?", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "line": "Line Messenger", + "mattermost": "Mattermost", + "markdownSupported": "Markdown sözdizimi desteklenmektedir. HTML kullanıyorsanız, biçimlendirme sorunlarını önlemek için baştaki boşluklardan kaçının.", + "Google Analytics ID": "Google Analytics Kodu", + "Edit Tag": "Etiketi Düzenle", + "Learn More": "Daha fazla bilgi edin", + "Server Address": "Sunucu Adresi", + "telegramMessageThreadIDDescription": "Forumun hedef ileti dizisi (konusu) için isteğe bağlı benzersiz kimlik; yalnızca forum üst grupları için geçerli", + "telegramMessageThreadID": "(İsteğe bağlı) Mesaj Thread ID", + "notificationRegional": "Bölgesel", + "telegramSendSilently": "Sessizce Gönder", + "telegramSendSilentlyDescription": "Mesajı sessizce gönderir. Kullanıcılar sessiz bir bildirim alacaktır.", + "telegramProtectContent": "Yönlendirmeyi/Kaydetmeyi Koru", + "telegramProtectContentDescription": "Etkinleştirilirse, Telegram'daki bot mesajları iletilmeye ve kaydedilmeye karşı korunacaktır.", + "Body Encoding": "JSON veya XML olabilen HTTP İstek Gövdesinin Kodlaması. İstek İçeriği Türü olarak da bilinir: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type", + "Clone Monitor": "Klon Monitör", + "Clone": "Klon", + "cloneOf": "{0} Monitörünün Klonu", + "Expiry": "son kullanma tarihi", + "Expiry date": "Son kullanma tarihi", + "Don't expire": "sona erme", + "Continue": "Devam etmek", + "Key Added": "Anahtar Eklendi", + "Add API Key": "API Anahtarı Ekle", + "No API Keys": "API Anahtarı Yok", + "apiKey-active": "Aktif", + "apiKey-expired": "Günü geçmiş", + "apiKey-inactive": "etkin değil", + "Expires": "süresi doluyor", + "disableAPIKeyMsg": "Bu API anahtarını devre dışı bırakmak istediğinizden emin misiniz?", + "deleteAPIKeyMsg": "Bu API anahtarını silmek istediğinizden emin misiniz?", + "Generate": "oluştur", + "API Keys": "API Anahtarları", + "Add Another": "Başka birtane ekle", + "apiKeyAddedMsg": "API anahtarınız eklendi. Bir daha gösterilmeyeceği için lütfen not edin.", + "pagertreeUrgency": "Önem", + "pagertreeSilent": "Sessiz", + "pagertreeLow": "Düşük", + "pagertreeMedium": "Orta", + "pagertreeHigh": "Yüksek", + "pagertreeCritical": "Kritik", + "pagertreeDoNothing": "Hiçbir şey yapma", + "wayToGetPagerTreeIntegrationURL": "PagerTree'de Uptime Kuma entegrasyonunu oluşturduktan sonra Endpoint'i kopyalayın. Tüm ayrıntıları görün {0}", + "pagertreeIntegrationUrl": "Entegrasyon URL", + "pagertreeResolve": "Otomatik Çöz", + "lunaseaTarget": "Hedef", + "Add New Tag": "Yeni Etiket Ekle", + "lunaseaDeviceID": "Cihaz ID", + "lunaseaUserID": "Kullanıcı ID", + "statusPageRefreshIn": "{0} içinde yenilenecek", + "twilioAuthToken": "Kimlik Doğrulama Jetonu / Gizli Api Anahtarı", + "twilioFromNumber": "Gönderen Numara", + "twilioToNumber": "Alıcı Numara", + "twilioAccountSID": "Hesap ID", + "sameAsServerTimezone": "Sunucu Saat Dilimi ile aynı", + "startDateTime": "Başlangıç Tarihi/Saati", + "endDateTime": "Bitiş Tarihi/Saati", + "cronExpression": "Cron İfadesi", + "cronSchedule": "Zamanlama: ", + "invalidCronExpression": "Geçersiz Cron İfadesi: {0}", + "ntfyAuthenticationMethod": "Kimlik Doğrulama Yöntemi", + "ntfyUsernameAndPassword": "Kullanıcı adı ve şifre", + "pushoverMessageTtl": "Mesajın Yaşama Süresi (Saniye)", + "Show Clickable Link": "Tıklanabilir Bağlantıyı Göster", + "Open Badge Generator": "Rozet Oluşturucuyu Aç", + "Badge Generator": "{0} Rozet Oluşturucu", + "Badge Type": "Rozet Türü", + "Badge Duration": "Rozet Süresi", + "Badge Label": "Rozet Etiketi", + "Badge Prefix": "Rozet Değer Öneki", + "Badge Suffix": "Rozet Değer Soneki", + "Badge Label Color": "Rozet Etiket Rengi", + "Badge Color": "Rozet Rengi", + "Badge Label Prefix": "Rozet Etiket Öneki", + "Badge Label Suffix": "Rozet Etiket Eki", + "Badge Up Color": "Rozet Normal Rengi", + "Badge Down Color": "Rozet Hatalı Rengi", + "Badge Pending Color": "Rozet Bekleyen Rengi", + "Badge Maintenance Color": "Rozet Bakım Rengi", + "Badge Warn Color": "Rozet Uyarı Rengi", + "Badge Warn Days": "Rozet Uyarı Günleri", + "Badge Down Days": "Rozet Hatalı Günleri", + "Badge Style": "Rozet Stili", + "Badge value (For Testing only.)": "Rozet değeri (Yalnızca Test için.)", + "Badge URL": "Rozet URL'i", + "Monitor Setting": "{0}'nin Monitör Ayarı", + "Show Clickable Link Description": "Eğer işaretlenirse, bu durum sayfasına erişimi olan herkes monitor URL'ine erişebilir.", + "Group": "Grup", + "Monitor Group": "Monitor Grubu", + "Cannot connect to the socket server": "Soket sunucusuna bağlanılamıyor", + "Edit Maintenance": "Bakımı Düzenle", + "Reconnecting...": "Yeniden bağlanılıyor...", + "Home": "Anasayfa", + "noGroupMonitorMsg": "Uygun değil. Önce bir Grup Monitörü oluşturun.", + "Close": "Kapalı", + "chromeExecutable": "Çalıştırılabilir Chrome/Chromium", + "chromeExecutableAutoDetect": "Otomatik algılama", + "chromeExecutableDescription": "Docker kullanıcıları için Chromium henüz kurulmamışsa, yüklenmesi ve test sonucunun görüntülenmesi birkaç dakika sürebilir. 1 GB disk alanı gerektirir.", + "Invert Keyword": "Anahtar Kelimeyi Ters Çevir", + "invertKeywordDescription": "Anahtar kelimenin mevcut olmasından ziyade mevcut olmamasına bakın.", + "webhookCustomBodyDesc": "İstek için özel bir HTTP Gövdesi tanımlayın. {msg}, {heartbeat}, {monitor} şablon değişkenleri kabul edilir.", + "webhookBodyPresetOption": "Ön ayar - {0}", + "webhookBodyCustomOption": "Özel Gövde", + "Request Body": "İstek Gövdesi", + "twilioApiKey": "Api Anahtarı (isteğe bağlı)", + "Expected Value": "Beklenen Değer", + "Json Query": "Json Sorgusu", + "Badge Duration (in hours)": "Rozet Süresi (saat cinsinden)", + "Badge Preview": "Rozet Önizlemesi", + "Notify Channel": "Bildirim Kanalı", + "aboutNotifyChannel": "Bildirim kanalı, müsaitlik durumu etkin veya uzakta olarak ayarlanmış olsun, kanalın tüm üyeleri için bir masaüstü veya mobil bildirimi tetikler.", + "filterActive": "Aktif", + "filterActivePaused": "Duraklatıldı", + "Enter the list of brokers": "Aracı listesine girin", + "Kafka Topic Name": "Kafka Başlık Adı", + "Kafka Producer Message": "Kafka Üretici Mesajı", + "Enable Kafka SSL": "Kafka SSL'i etkinleştir", + "Kafka SASL Options": "Kafka SASL Seçenekleri", + "Mechanism": "Mekanizma", + "Pick a SASL Mechanism...": "Bir SASL Mekanizması seçin…", + "Authorization Identity": "Yetki Kimliği", + "Secret AccessKey": "Gizli Erişim Anahtarı", + "Session Token": "Oturum Jetonu", + "Kafka Brokers": "Kafka Aracıları", + "Press Enter to add broker": "Aracı eklemek için Enter'a basın", + "Enable Kafka Producer Auto Topic Creation": "Kafka Üreticisi Otomatik Başlık Oluşturmayı Etkinleştir", + "AccessKey Id": "Erişim Anahtarı Kimliği", + "tailscalePingWarning": "Tailscale Ping monitörünü kullanabilmek için Docker olmadan Uptime Kuma kurmanız ve ayrıca sunucunuza Tailscale client kurmanız gerekmektedir.", + "Server URL should not contain the nfty topic": "Sunucu URL'si nfty konusunu içermemelidir", + "FlashDuty Severity": "Önem derecesi", + "nostrRelays": "Nostr röleleri", + "nostrRelaysHelp": "Satır başına bir geçiş URL'si", + "nostrSender": "Gönderen Özel Anahtarı (nsec)", + "nostrRecipients": "Alıcıların Genel Anahtarları (npub)", + "nostrRecipientsHelp": "npub biçimi, her satıra bir tane", + "showCertificateExpiry": "Sertifika Geçerlilik Süresini Göster", + "noOrBadCertificate": "Sertifika Yok/Geçersiz", + "Select": "Seç", + "PushDeer Server": "PushDeer Sunucusu", + "wayToGetFlashDutyKey": "Uptime Kuma'yı Flashduty ile entegre etmek için: Kanallar > Bir kanal seçin > Entegrasyonlar > Yeni bir entegrasyon ekleyin bölümüne gidin, Uptime Kuma'yı seçin ve Push URL'sini kopyalayın.", + "selectedMonitorCount": "Seçildi: {0}", + "Check/Uncheck": "İşaretle/İşareti Kaldır", + "pushDeerServerDescription": "Resmi sunucuyu kullanmak için boş bırakın", + "Request Timeout": "İsteğin Zaman Aşımına Uğrama Süresi", + "timeoutAfter": "{0} saniye sonra zaman aşımı", + "gamedigGuessPort": "Gamedig: Ziyaretçi Portu", + "gamedigGuessPortDescription": "Valve Server Sorgu Protokolü tarafından kullanılan bağlantı noktası, istemci bağlantı noktasından farklı olabilir. Monitör sunucunuza bağlanamıyorsa bunu deneyin.", + "styleElapsedTimeShowNoLine": "Göster (Satır Yok)", + "styleElapsedTime": "Kalp atışı çubuğunun altında geçen süre", + "styleElapsedTimeShowWithLine": "Göster (Satır ile birlikte)", + "enableNSCD": "Tüm DNS isteklerini önbelleğe almak için NSCD'yi (Ad Hizmeti Önbellek Programı) etkinleştirin", + "setupDatabaseEmbeddedMariaDB": "Hiçbir şey ayarlamanıza gerek yok. Bu Docker görseli, MariaDB’yi otomatik olarak gömülü ve yapılandırılmış şekilde içerir. Uptime Kuma bu veri tabanına Unix soketi aracılığıyla bağlanacaktır.", + "setupDatabaseSQLite": "Küçük ölçekli dağıtımlar için önerilen basit bir veri tabanı dosyası. v2.0.0'dan önce Uptime Kuma, varsayılan veri tabanı olarak SQLite'ı kullanıyordu.", + "setupDatabaseChooseDatabase": "Hangi veritabanını kullanmak istersiniz?", + "setupDatabaseMariaDB": "Harici bir MariaDB veri tabanına bağlanın. Veri tabanı bağlantı bilgilerini ayarlamanız gerekir.", + "dbName": "Veri Tabanı Adı", + "Saved.": "Kaydedildi.", + "toastErrorTimeout": "Hata Bildirimleri için Zaman Aşımı", + "toastSuccessTimeout": "Başarı Bildirimleri için Zaman Aşımı", + "monitorToastMessagesLabel": "Toast bildirimlerini izleyin", + "monitorToastMessagesDescription": "Monitörler için bildirimler, saniye cinsinden belirli bir süre sonunda kaybolur. -1'e ayarlamak zaman aşımını devre dışı bırakır. 0'a ayarlamak, tost bildirimlerini devre dışı bırakır.", + "Bark API Version": "Bark API Sürümü", + "pushViewCode": "Push monitör nasıl kullanılır? (Kodu Görüntüle)", + "programmingLanguages": "Programlama dilleri", + "pushOthers": "Diğerleri", + "authInvalidToken": "Geçersiz Token.", + "authIncorrectCreds": "Kullanıcı adı ya da parola yanlış.", + "2faAlreadyEnabled": "2FA zaten etkin.", + "2faEnabled": "2FA Etkin.", + "2faDisabled": "2FA Devre Dışı.", + "successResumed": "Başarıyla Devam Edildi.", + "successPaused": "Başarıyla Duraklatıldı.", + "successDeleted": "Başarıyla silindi.", + "successEdited": "Başarıyla Düzenlendi.", + "successBackupRestored": "Yedekleme başarıyla geri yüklendi.", + "successDisabled": "Başarıyla Devre Dışı Bırakıldı.", + "successEnabled": "Başarıyla Etkinleştirildi.", + "tagNotFound": "Etiket bulunamadı.", + "authUserInactiveOrDeleted": "Kullanıcı etkin değil veya silinmiş.", + "successAdded": "Başarıyla eklendi.", + "successAuthChangePassword": "Şifre başarıyla güncellendi.", + "foundChromiumVersion": "Chromium/Chrome bulundu. Versiyon: {0}", + "Reset Token": "Jetonu Sıfırla", + "emailCustomisableContent": "Özelleştirilebilir içerik", + "smtpLiquidIntroduction": "Aşağıdaki iki alan Liquid şablonlama Dili aracılığıyla şablonlanabilir. Kullanım talimatları için lütfen {0}'a bakın. Bunlar mevcut değişkenlerdir:", + "leave blank for default subject": "varsayılan konu için boş bırakın", + "emailCustomBody": "Özel Gövde", + "leave blank for default body": "varsayılan gövde için boş bırakın", + "emailTemplateServiceName": "Hizmet Adı", + "emailTemplateHostnameOrURL": "Hostname veya URL", + "emailTemplateStatus": "Durum", + "emailTemplateMonitorJSON": "monitörü tanımlayan nesne", + "emailTemplateHeartbeatJSON": "kalp atışını tanımlayan nesne", + "liquidIntroduction": "Şablonlanabilirlik, Liquid şablonlama dili aracılığıyla sağlanır. Kullanım talimatları için lütfen {0}'a bakın.", + "templateMsg": "bildirim mesajı", + "templateHeartbeatJSON": "kalp atışını tanımlayan nesne", + "templateMonitorJSON": "monitörü tanımlayan nesne", + "templateLimitedToUpDownCertNotifications": "yalnızca Normal/Hatalı/Sertifika sona erme bildirimleri için kullanılabilir", + "templateLimitedToUpDownNotifications": "yalnızca Normal/Hatalı bildirimleri için kullanılabilir", + "emailTemplateMsg": "bildirim mesajı", + "emailTemplateLimitedToUpDownNotification": "yalnızca Normal/Hatalı kalp atışları için kullanılabilir, aksi takdirde boş", + "GrafanaOncallUrl": "Grafana Oncall URL'i", + "noDockerHostMsg": "Uygun değil. Önce Docker Ana Bilgisayarını Ayarlayın.", + "DockerHostRequired": "Lütfen bu monitör için Docker Ana Bilgisayarını ayarlayın.", + "Browser Screenshot": "Tarayıcı Ekran Görüntüsü", + "successKeyword": "Başarı Anahtar Kelimesi", + "successKeywordExplanation": "Başarılı olarak kabul edilecek MQTT anahtar kelimesi", + "Add a new expiry notification day": "Yeni bir sona erme bildirim günü ekle", + "Remove the expiry notification": "Sona erme bildirim gününü kaldır", + "setup a new monitor group": "yeni bir monitör grubu ayarla", + "Remove domain": "'{0}' alan adını kaldır", + "Remote Browsers": "Uzak Tarayıcılar", + "Remote Browser": "Uzak Tarayıcı", + "Add a Remote Browser": "Uzak Tarayıcı Ekle", + "Remote Browser not found!": "Uzak Tarayıcı bulunamadı!", + "remoteBrowsersDescription": "Uzak Tarayıcılar, Chromium'u yerel olarak çalıştırmanın bir alternatifidir. browserless.io gibi bir hizmet ile kurulum yapın veya kendi hizmetinize bağlanın", + "self-hosted container": "kendi kendine barındırılan konteyner", + "remoteBrowserToggle": "Varsayılan olarak Chromium, Uptime Kuma kapsayıcısının içinde çalışır. Bu anahtarı değiştirerek uzak bir tarayıcıyı kullanabilirsiniz.", + "useRemoteBrowser": "Uzak Tarayıcı Kullanın", + "deleteRemoteBrowserMessage": "Bu Uzak Tarayıcıyı tüm monitörler için silmek istediğinizden emin misiniz?", + "openModalTo": "modeli {0}'a aç", + "Add a domain": "Alan adı ekle", + "Search monitored sites": "İzlenen siteleri arayın", + "ntfyPriorityHelptextAllEvents": "Tüm olaylar maksimum öncelik ile gönderilir", + "settingUpDatabaseMSG": "Veri tabanı kuruluyor. Biraz zaman alabilir, lütfen sabırlı olun.", + "statusPageSpecialSlugDesc": "Özel slug {0}: slug belirtilmediğinde bu sayfa gösterilecektir", + "ntfyPriorityHelptextAllExceptDown": "Önceliği {1} olan {0}-olayları hariç tüm olaylar bu öncelik ile gönderilir", + "What is a Remote Browser?": "Uzak Tarayıcı Nedir?", + "Your User ID": "Kullanıcı kimliğiniz", + "Channel access token (Long-lived)": "Kanal erişim tokenı (Uzun ömürlü)", + "wayToGetHeiiOnCallDetails": "Tetikleyici Kimliği ve API Anahtarlarının nasıl alınacağı {dokümantasyon} bölümünde açıklanmıştır", + "documentationOf": "{0} Dokümantasyon", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Telefon Numarasından / İletim Yolu Kaynak Adresinden (TPOA)", + "gtxMessagingFromHint": "Cep telefonlarında, alıcılarınız mesajın göndericisi olarak görüntülenen TPOA'yı görür. En fazla 11 alfanümerik karakter, bir kısa kod, yerel uzun kod veya uluslararası numaralara izin verilir ({e164}, {e212} veya {e214})", + "To Phone Number": "Telefon Numarasına", + "gtxMessagingToHint": "Uluslararası format, başında \"+\" ile ({e164}, {e212} veya {e214})", + "gtxMessagingApiKeyHint": "API anahtarınızı şu adreste bulabilirsiniz: Yönlendirme Hesaplarım > Hesap Bilgilerini Göster > API Kimlik Bilgileri > REST API (v2.x)", + "Destination": "Varış Noktası", + "cellsyntDestination": "Alıcının uluslararası formattaki telefon numarası, baştaki 00 ve ardından ülke kodu, örneğin İngiltere numarası 07920 110 000 için 00447920110000 (toplamda en fazla 17 hane). HTTP isteği başına maksimum 25000 virgülle ayrılmış alıcı.", + "Allow Long SMS": "Uzun SMS'e İzin Ver", + "cellsyntSplitLongMessages": "Uzun mesajları en fazla 6 parçaya bölün. 153 x 6 = 918 karakter.", + "max 15 digits": "maksimum 15 hane", + "Originator type": "Kaynak tipi", + "Alphanumeric (recommended)": "Alfanümerik (önerilir)", + "Telephone number": "Telefon numarası", + "cellsyntOriginatortypeAlphanumeric": "Alfanümerik dize (maksimum 11 alfanümerik karakter). Alıcılar mesajı yanıtlayamaz.", + "cellsyntOriginatortypeNumeric": "Başında 00 olmadan uluslararası formatta telefon numarası içeren sayısal değer (maksimum 15 hane) (örneğin İngiltere numarası 07920 110 000 447920110000 olarak ayarlanmalıdır). Alıcılar mesajı yanıtlayabilir.", + "Originator": "Kaynak", + "cellsyntOriginator": "Alıcının cep telefonunda mesajın kaynağı olarak görünür. İzin verilen değerler ve işlev kaynaktipi parametresine bağlıdır.", + "callMeBotGet": "Burada {0}, {1} ve {2} için bir uç nokta oluşturabilirsiniz. Hız sınırlaması alabileceğinizi unutmayın. Hız limitleri şöyle görünür: {3}", + "wayToWriteWhapiRecipient": "Uluslararası ön eke sahip, ancak başında artı işareti olmayan telefon numarası ({0}), Kişi Kimliği ({1}) veya Grup Kimliği ({2}).", + "wayToGetWhapiUrlAndToken": "API URL'sini ve belirtecini {0} adresinden istediğiniz kanala girerek alabilirsiniz", + "whapiRecipient": "Telefon Numarası / Kişi Kimliği / Grup Kimliği", + "API URL": "API URL", + "max 11 alphanumeric characters": "maksimum 11 alfanümerik karakter", + "Mentioning": "Bahsetme", + "Don't mention people": "İnsanlardan bahsetme", + "Mention group": "{group}'tan bahset", + "wayToGetSevenIOApiKey": "App.seven.io > geliştirici > API anahtarı > yeşil ekle düğmesi altındaki kontrol panelini ziyaret edin", + "senderSevenIO": "Numara veya isim gönderiliyor", + "receiverSevenIO": "Alıcı numarası", + "apiKeySevenIO": "SevenIO API Anahtarı", + "receiverInfoSevenIO": "Alıcı numara Almanya'da değilse, numaranın önüne ülke kodunu eklemeniz gerekir (örneğin, ABD'den ülke kodu 1 için 017612121212 yerine 117612121212 kullanın)", + "locally configured mail transfer agent": "yerel olarak yapılandırılmış posta aktarım aracısı", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Bağlanmak istediğiniz sunucunun ana bilgisayar adını girin veya {local_mta} kullanmayı düşünüyorsanız {localhost} girin", + "Host URL": "Host URL", + "whatHappensAtForumPost": "Yeni bir forum gönderisi oluşturun. Bu, mevcut gönderiye mesaj YAYINLAMAZ. Mevcut gönderide yayınlamak için \"{option}\" seçeneğini kullanın", + "Command": "Komut", + "mongodbCommandDescription": "Veritabanına karşı bir MongoDB komutu çalıştırın. Mevcut komutlar hakkında bilgi için {dokümantasyona} bakın", + "Bitrix24 Webhook URL": "Bitrix24 Webhook URL'si", + "wayToGetBitrix24Webhook": "{0} adresindeki adımları izleyerek bir web kancası oluşturabilirsiniz", + "bitrix24SupportUserID": "Bitrix24'e kullanıcı kimliğinizi girin. Kullanıcının profiline giderek bağlantıdan kimliğini öğrenebilirsiniz.", + "Select message type": "Mesaj türünü seçin", + "Send to channel": "Kanala gönder", + "Create new forum post": "Yeni forum gönderisi oluştur", + "postToExistingThread": "Mevcut konuya / forum gönderisine gönder", + "forumPostName": "Forum gönderi adı", + "Refresh Interval": "Yenileme aralığı", + "Refresh Interval Description": "Durum sayfası her {0} saniyede bir tam site yenilemesi yapacaktır", + "ignoreTLSErrorGeneral": "Bağlantı için TLS/SSL hatasını yoksay", + "threadForumPostID": "Konu / Forum gönderi kimliği", + "e.g. {discordThreadID}": "Örneğin. {discordThreadID}", + "wayToGetDiscordThreadId": "Konu başlığı/forum gönderisi kimliği almak, kanal kimliği almaya benzer. Kimliklerin nasıl alınacağı hakkında daha fazla bilgi edinin {0}", + "smspartnerPhoneNumber": "Telefon numaraları", + "smspartnerSenderName": "SMS Gönderenin Adı", + "smspartnerApiurl": "API anahtarınızı {0} adresindeki kontrol panelinizde bulabilirsiniz", + "smspartnerPhoneNumberHelptext": "Numara uluslararası biçimde {0}, {1} olmalıdır. Birden çok sayı {2} ile ayrılmalıdır", + "smspartnerSenderNameInfo": "3..=11 normal karakter arasında olmalıdır", + "threemaRecipient": "Alıcı", + "threemaRecipientType": "Alıcı Tipi", + "threemaRecipientTypeIdentity": "Threema Kimliği", + "threemaRecipientTypePhone": "Telefon numarası", + "threemaRecipientTypeEmail": "E-posta Adresi", + "threemaSenderIdentity": "Ağ Geçidi Kimliği", + "threemaSenderIdentityFormat": "8 karakter, genellikle * ile başlar", + "threemaApiAuthenticationSecret": "Ağ Geçidi Kimliği Gizli Anahtarı", + "threemaBasicModeInfo": "Not: Bu entegrasyon, temel modda (sunucu tabanlı şifreleme) Threema Gateway'i kullanır. Daha fazla ayrıntıyı {0} adresinde bulabilirsiniz.", + "wayToGetThreemaGateway": "Threema Gateway {0}'e kaydolabilirsiniz.", + "threemaRecipientTypeIdentityFormat": "8 karakter", + "threemaRecipientTypePhoneFormat": "E.164, başında + olmadan", + "apiKeysDisabledMsg": "Kimlik doğrulama devre dışı olduğundan API anahtarları devre dışı bırakıldı.", + "now": "Şimdi", + "time ago": "{0} önce", + "-year": "-yıl", + "Json Query Expression": "JSON Sorgu İfadesi", + "and": "ve", + "cacheBusterParam": "{0} parametresini ekleyin", + "cacheBusterParamDescription": "Önbellekleri atlamak için rastgele oluşturulmuş parametre.", + "Community String": "Topluluk Metni", + "snmpCommunityStringHelptext": "Bu metin, SNMP özellikli cihazlara erişimi doğrulamak ve kontrol etmek için bir şifre işlevi görür. Bunu SNMP cihazınızın yapılandırmasıyla eşleştirin.", + "OID (Object Identifier)": "OID (Nesne Tanımlayıcı)", + "snmpOIDHelptext": "İzlemek istediğiniz sensörün veya durumun OID'sini girin. OID'den emin değilseniz MIB tarayıcıları veya SNMP yazılımı gibi ağ yönetimi araçlarını kullanın.", + "Condition": "Koşul", + "SNMP Version": "SNMP Sürümü", + "Please enter a valid OID.": "Lütfen geçerli bir OID girin.", + "groupOnesenderDesc": "Gruba mesaj göndermek için, GroupID değerinin geçerli olduğundan emin olun. Örneğin: 628123456789-342345", + "Group ID": "Grup Kimliği", + "wayToGetOnesenderUrlandToken": "Onesender web sitesine giderek URL'yi ve Jetonu alabilirsiniz. Daha fazla bilgi için {0}", + "Add Remote Browser": "Uzak Tarayıcı Ekle", + "New Group": "Yeni Grup", + "Host Onesender": "Onesender Host Adresi", + "Token Onesender": "Onesender Jetonu", + "Group Name": "Grup Adı", + "OAuth2: Client Credentials": "OAuth2: İstemci Kimlik Bilgileri", + "Recipient Type": "Alıcı Türü", + "Private Number": "Özel Numara", + "Authentication Method": "Kimlik Doğrulama Yöntemi", + "privateOnesenderDesc": "Özel numaralı telefona mesaj göndermek için, telefon numarasının geçerli olduğundan emin olun. Örneğin: 628123456789", + "Authorization Header": "Yetkilendirme Başlığı", + "Form Data Body": "Form Veri Gövdesi", + "Go back to home page.": "Ana sayfaya geri dönün.", + "No tags found.": "Etiket bulunamadı.", + "Lost connection to the socket server.": "Soket sunucusuyla bağlantı kesildi.", + "Cannot connect to the socket server.": "Soket sunucusuna bağlanılamıyor.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Web Kancası URL'si", + "OAuth Token URL": "OAuth Jetonu URL'si", + "Client ID": "İstemci Kimliği", + "Client Secret": "İstemci Jetonu", + "OAuth Scope": "OAuth Kapsamı", + "Optional: Space separated list of scopes": "İsteğe bağlı: Kapsamların boşlukla ayrılmış listesi", + "signl4Docs": "SIGNL4'ün nasıl yapılandırılacağı ve SIGNL4 web kancası URL'sinin nasıl elde edileceği hakkında daha fazla bilgiyi {0} içinde bulabilirsiniz.", + "not contains": "içermez/içermiyor", + "starts with": "başlar/başlıyor", + "equals": "eşit", + "not starts with": "ile başlamaz", + "not equals": "eşit değil", + "contains": "içerir", + "conditionDeleteGroup": "Grubu Sil", + "conditionValuePlaceholder": "Değer", + "Conditions": "Koşullar", + "conditionAdd": "Koşul Ekle", + "conditionDelete": "Koşul Sil", + "conditionAddGroup": "Grup Ekle", + "ends with": "ile biter", + "not ends with": "ile bitmiyor", + "less than or equal to": "küçük veya eşit", + "greater than": "daha büyük", + "less than": "daha küçük", + "greater than or equal to": "büyük veya eşit", + "record": "kayıt", + "jsonQueryDescription": "JSON sorgusunu kullanarak sunucunun JSON yanıtından belirli verileri ayrıştırın ve çıkarın. JSON beklemiyorsanız ham yanıt için \"$\" sembolünü kullanın. Sonuç daha sonra metin olarak beklenen değerle karşılaştırılır. Belgeler için {0}'a bakın ve sorgularla denemeler yapmak için {1}'i kullanın.", + "shrinkDatabaseDescriptionSqlite": "SQLite için {vacuum} veritabanını tetikle. {auto_vacuum} zaten etkin ancak bu, {vacuum} komutunun yaptığı gibi veritabanını birleştirmez veya tek tek veritabanı sayfalarını yeniden paketlemez.", + "Debug": "Hata ayıklama", + "Copy": "Kopyala", + "CopyToClipboardError": "Panoya kopyalanamadı: {hata}", + "CopyToClipboardSuccess": "Kopyalandı!", + "firewalls": "güvenlik duvarları", + "dns resolvers": "dns çözücüler", + "docker networks": "Docker ağları", + "CurlDebugInfoOAuth2CCUnsupported": "Tam Oauth istemci kimlik bilgisi akışı {curl}'de desteklenmiyor.{newline}Lütfen bir taşıyıcı belirteci alın ve bunu {oauth2_bearer} seçeneği aracılığıyla iletin.", + "CurlDebugInfoProxiesUnsupported": "Yukarıdaki {curl} komutunda proxy desteği şu anda uygulanmamıştır.", + "Message format": "Mesaj biçimi", + "Send rich messages": "Zengin mesajlar gönder", + "Notification Channel": "Bildirim Kanalı", + "Sound": "Ses", + "Alphanumerical string and hyphens only": "Yalnızca alfanümerik dize ve tireler", + "Arcade": "Çarşı", + "Correct": "Doğru", + "Fail": "Hata", + "Harp": "Arp", + "Reveal": "Ortaya çıkarmak", + "Bubble": "Kabarcık", + "Doorbell": "Kapı zili", + "Flute": "Flüt", + "Money": "Para", + "Scifi": "Bilimkurgu", + "Clear": "Temizlemek", + "Elevator": "Asansör", + "Guitar": "Gitar", + "Pop": "Pop", + "Custom sound to override default notification sound": "Varsayılan bildirim sesini geçersiz kılmak için özel ses", + "Time Sensitive (iOS Only)": "Zaman Duyarlı (Yalnızca iOS)", + "From": "Kimden", + "Can be found on:": "Şurada bulunabilir: {0}", + "The phone number of the recipient in E.164 format.": "Alıcının E.164 formatındaki telefon numarası.", + "ignoredTLSError": "TLS/SSL hataları göz ardı edildi", + "CurlDebugInfo": "Monitörü hata ayıklamak için, bunu kendi makinenizin terminaline veya uptime kuma'nın çalıştığı makinenin terminaline yapıştırabilir ve ne istediğinizi görebilirsiniz.{newiline}Lütfen {firewalls}, {dns_resolvers} veya {docker_networks} gibi ağ farklılıklarına dikkat edin.", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Cihaz rahatsız etmeyin modunda olsa bile, zaman açısından hassas bildirimler anında iletilecek.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Cevap alabilmek istiyorsanız E.164 formatında bir kısa mesaj gönderici kimliği veya bir telefon numarası.", + "rabbitmqNodesRequired": "Lütfen bu monitör için sunucuları ayarlayın.", + "rabbitmqNodesInvalid": "Lütfen RabbitMQ düğümleri için tam nitelikli ('http' ile başlayan) bir URL kullanın.", + "RabbitMQ Username": "RabbitMQ Kullanıcı Adı", + "RabbitMQ Password": "RabbitMQ Şifresi", + "SendGrid API Key": "SendGrid API Anahtarı", + "Separate multiple email addresses with commas": "Birden fazla e-posta adresini virgülle ayırın", + "RabbitMQ Nodes": "RabbitMQ Yönetim Sunucuları", + "rabbitmqNodesDescription": "Protokol ve port dahil olmak üzere RabbitMQ yönetim düğümleri için URL'yi girin. Örnek: {0}", + "rabbitmqHelpText": "Monitörü kullanmak için, RabbitMQ kurulumunuzda Yönetim Eklentisini etkinleştirmeniz gerekecektir. Daha fazla bilgi için lütfen {rabitmq_documentation}'a bakın.", + "aboutSlackUsername": "Mesaj göndericinin görünen adını değiştir. Eğer birilerini etiketlemek isterseniz, onu ismini dostça ekleyebilirsiniz.", + "templateHostnameOrURL": "ana bilgisayar adı veya URL", + "templateStatus": "durum", + "telegramUseTemplate": "Özel mesaj şablonu kullan", + "telegramUseTemplateDescription": "Etkinleştirilirse mesaj özel bir şablon kullanılarak gönderilecektir.", + "telegramTemplateFormatDescription": "Telegram, mesajlar için farklı işaretleme dillerinin kullanılmasına izin verir, ayrıntılar için Telegram {0} bölümüne bakın.", + "templateServiceName": "servis adı", + "telegramServerUrlDescription": "Telegram'ın bot API sınırlamalarını kaldırmak veya engellenen alanlarda (Çin, İran vb.) erişim sağlamak için. Daha fazla bilgi için tıklayın {0}. Varsayılan: {1}", + "wahaSession": "Oturum", + "wahaChatId": "Sohbet Kimliği (Telefon Numarası / Kişi Kimliği / Grup Kimliği)", + "wayToGetWahaApiUrl": "WAHA Örnek URL'niz.", + "wayToGetWahaApiKey": "API Anahtarı, WAHA'yı çalıştırmak için kullandığınız WHATSAPP_API_KEY ortam değişkeni değeridir.", + "wayToGetWahaSession": "Bu oturumdan itibaren WAHA, Chat ID'ye bildirimler gönderir. Bunu WAHA Dashboard'da bulabilirsiniz.", + "wayToWriteWahaChatId": "Uluslararası ön eke sahip, ancak başında artı işareti olmayan telefon numarası ({0}), Kişi Kimliği ({1}) veya Grup Kimliği ({2}). Bildirimler WAHA Session'dan bu Sohbet Kimliğine gönderilir.", + "Plain Text": "Düz Metin", + "Message Template": "Mesaj Şablonu", + "Template Format": "Şablon Biçimi", + "YZJ Webhook URL": "YZJ Webhook URL'si", + "YZJ Robot Token": "YZJ Robot tokeni", + "telegramServerUrl": "(İsteğe bağlı) Sunucu URL'si", + "defaultFriendlyName": "Yeni Servis", + "smtpHelpText": "'SMTPS' SMTP/TLS'nin çalıştığını test eder; 'TLS'i reddet' düz metin üzerinden bağlanır; 'STARTTLS' bağlanır, bir STARTTLS komutu verir ve sunucu sertifikasını doğrular. Bunların hiçbiri e-posta göndermez.", + "OneChatAccessToken": "OneChat Erişim Tokeni", + "OneChatUserIdOrGroupId": "OneChat Kullanıcı ID veya Grup ID", + "Font Twemoji by Twitter licensed under": "Twemoji yazı tipi Twitter tarafından lisanslı", + "smsplanetApiToken": "SMSPlanet API için Token", + "smsplanetApiDocs": "API belirteçlerinin elde edilmesiyle ilgili ayrıntılı bilgi {the_smsplanet_documentation} bölümünde bulunabilir.", + "Use HTML for custom E-mail body": "Özel E-posta gövdesi için HTML kullanın", + "smseagleGroupV2": "Telefon defteri grup kimliği(ID)", + "smseagleContactV2": "Telefon defteri kişi kimlikleri(ID)", + "smseagleMsgType": "Mesaj tipi", + "smseagleMsgSms": "Sms mesajı (varsayılan)", + "smseagleMsgRing": "Telefon araması", + "smseagleMsgTts": "Metinden sese çağrı", + "smseagleMsgTtsAdvanced": "Metinden sese Gelişmiş çağrı", + "smseagleDuration": "Süre (saniye cinsinden)", + "smseagleTtsModel": "Metinden sese model kimliği(ID)", + "smseagleApiType": "API versiyonu", + "smseagleApiv1": "APIv1 (mevcut projeler ve geriye dönük uyumluluk için)", + "smseagleApiv2": "APIv2 (yeni entegrasyonlar için önerilir)", + "smseagleDocs": "Belgeleri veya APIv2 kullanılabilirliğini kontrol edin: {0}", + "smseagleComma": "Birden fazla virgül ile ayrılmalıdır", + "SpugPush Template Code": "Şablon Kodu", + "FlashDuty Push URL": "Bildirim URL", + "FlashDuty Push URL Placeholder": "Uyarı entegrasyonu sayfasından kopyalama", + "pingCountLabel": "Maksimum Paketler", + "pingCountDescription": "Durmadan önce gönderilecek paket sayısı", + "pingNumericLabel": "Sayısal Çıktı", + "pingNumericDescription": "İşaretlenirse, sembolik ana bilgisayar adları yerine IP adresleri çıktılanacaktır", + "pingGlobalTimeoutLabel": "Küresel Zaman Aşımı", + "pingGlobalTimeoutDescription": "Gönderilen paketlerden bağımsız olarak ping durmadan önce saniye cinsinden toplam süre", + "pingPerRequestTimeoutLabel": "Ping Başına Zaman Aşımı", + "pingPerRequestTimeoutDescription": "Bu, tek bir ping paketinin kayıp olarak değerlendirilmesinden önceki maksimum bekleme süresidir (saniye cinsinden)", + "pingIntervalAdjustedInfo": "Paket sayısına, genel zaman aşımına ve ping başına zaman aşımına göre ayarlanan aralık", + "Custom URL": "Özel URL", + "customUrlDescription": "Tıklanabilir URL olarak monitörünki yerine kullanılacaktır.", + "OneChatBotId": "OneChat Bot Kimliği", + "the smsplanet documentation": "smsplanet dokümantasyonu", + "Phone numbers": "Telefon numaraları", + "Sender name": "Gönderen adı", + "smsplanetNeedToApproveName": "Müşteri panelinde onaylanması gerekir", + "Disable URL in Notification": "Bildirimdeki URL'yi Devre Dışı Bırak", + "Ip Family": "IP Ailesi", + "ipFamilyDescriptionAutoSelect": "IP ailesini belirlemek için {happyEyeballs} kullanır.", + "Happy Eyeballs algorithm": "Happy Eyeball algoritması", + "Add Another Tag": "Başka Etiket Ekle", + "Staged Tags for Batch Add": "Toplu Ekleme için Aşamalı Etiketler", + "pause": "Durakla", + "Manual": "Manuel", + "ntfyPriorityHelptextPriorityHigherThanDown": "Normal öncelik, {0} önceliğinden yüksek olmalıdır. {1} önceliği, {0} önceliği olan {2} den daha yüksektir", + "ntfyPriorityDown": "DOWN-olayları için öncelik seviyesi", + "Add Tags": "Etiket Ekle", + "tagAlreadyOnMonitor": "Bu etiket (isim ve değer) zaten monitörde veya eklenmeyi bekliyor.", + "tagAlreadyStaged": "Bu etiket (isim ve değer) bu grup için zaten hazırlandı.", + "tagNameExists": "Bu isimde bir sistem etiketi zaten var. Listeden seçin veya farklı bir isim kullanın.", + "Clear Form": "Formu Temizle", + "Optional: The audience to request the JWT for": "İsteğe bağlı: JWT'nin talep edileceği kitle", + "OAuth Audience": "OAuth Kitlesi", + "mqttWebSocketPath": "MQTT WebSocket Yolu", + "Path": "Yol", + "mqttWebsocketPathExplanation": "WebSocket bağlantıları üzerinden MQTT için WebSocket yolu (örn. /mqtt)", + "clearAllEventsMsg": "Tüm monitörler için tüm olayları silmek istediğinizden emin misiniz? Bu işlem, ilgili mesaj verilerini kalıcı olarak siler ve geri alınamaz.", + "Template plain text instead of using cards": "Kartlar yerine düz metin şablonu", + "auto-select": "Otomatik Seç", + "Could not clear events": "{failed}/{total} etkinlik temizlenemedi", + "Clear All Events": "Tüm Etkinlikleri Temizle", + "mqttWebsocketPathInvalid": "Lütfen geçerli bir WebSocket yolu formatı kullanın", + "mqttHostnameTip": "Lütfen bu formatı kullanın {hostnameFormat}", + "wayToGetBaleChatID": "Bot’a bir mesaj göndererek ve şu URL’ye giderek chat_id’nizi görüntüleyebilirsiniz:", + "wayToGetBaleToken": "{0} üzerinden bir token alabilirsiniz.", + "supportBaleChatID": "Destek Direkt Mesaj / Grup / Kanalın Mesaj ID'si", + "Events cleared successfully": "Etkinlikler başarıyla temizlendi.", + "No monitors found": "Monitör bulunamadı.", + "issueWithGoogleChatOnAndroidHelptext": "Bu ayrıca {issuetackerURL} gibi üst akıştaki hataları aşmayı sağlar", + "Number of retry attempts if webhook fails": "Webhook başarısız olursa yeniden deneme sayısı (her 60-180 saniyede bir).", + "Maximum Retries": "Maksimum Yeniden Deneme", + "wayToGetClickSMSIRTemplateID": "Şablonunuz {uptkumaalert} alanını içermelidir. {here} üzerinden yeni bir şablon oluşturabilirsiniz.", + "Template ID": "Şablon Kimliği", + "brevoApiHelp": "Buradan bir API anahtarı oluşturun: {0}", + "brevoApiKey": "Brevo API Anahtarı", + "brevoFromEmail": "Gönderen E-posta", + "brevoBccEmail": "BCC E-posta", + "Conversation token": "Konuşma token'ı", + "Nextcloud host": "Nextcloud sunucusu", + "Bot secret": "Bot gizli anahtarı", + "HTTP Method": "HTTP Metodu", + "brevoSeparateMultipleEmails": "Birden fazla e-posta adresini virgülle ayırın", + "brevoSubject": "Konu", + "descriptionHelpText": "İç pano üzerinde gösterilir. Markdown kullanımı serbesttir ve görüntülenmeden önce boşlukları ve girintileri koruyacak şekilde temizlenir.", + "deleteGroupMsg": "Bu grubu silmek istediğinizden emin misiniz?", + "deleteChildrenMonitors": "Ayrıca, varsa doğrudan alt monitörleri ve bunların alt monitörlerini de silin | Ayrıca, varsa tüm ({count}) doğrudan alt monitörleri ve bunların alt monitörlerini de silin", + "Mention Mobile List": "Mobil listeden bahset", + "Enter a list of userId": "Kullanıcı kimliği listesini girin", + "Enter a list of mobile": "Mobil cihazların bir listesini girin", + "Mention User List": "Kullanıcı kimlik listesinden bahset", + "Dingtalk Mobile List": "Mobil liste", + "Dingtalk User List": "Kullanıcı Kimlik listesi", + "Invalid mobile": "Geçersiz mobil [{mobile}]", + "Invalid userId": "Geçersiz kullanıcı kimliği [{userId}]", + "twilioMessagingServiceSID": "Mesajlaşma Hizmeti SID (isteğe bağlı)", + "twilloMessagingServiceSIDHelptext": "Göndericileri ve özellikleri yönetmek için {twillo_messaging_service_help_link} kullanıyorsanız, Mesajlaşma Hizmeti SID’inizi buraya girin", + "webhookPostMethodDesc": "POST çoğu modern HTTP sunucusu için uygundur.", + "wayToWriteEvolutionRecipient": "Başında artı işareti olmayan uluslararası kodlu telefon numarası ({0}), Kişi Kimliği ({1}) veya Grup Kimliği ({2}).", + "wayToGetEvolutionUrlAndToken": "API URL'sini ve token değerini, {0} üzerinden istediğiniz kanala giderek alabilirsiniz", + "evolutionRecipient": "Telefon Numarası / Kişi Kimliği / Grup Kimliği", + "evolutionInstanceName": "Örnek Adı", + "brevoFromName": "Gönderen İsmi", + "brevoToEmail": "Alıcı E-posta", + "brevoCcEmail": "CC E-posta", + "brevoLeaveBlankForDefaultSubject": "varsayılan konu için boş bırakın", + "Send UP silently": "UP bildirimini sessiz gönder", + "Send DOWN silently": "DOWN bildirimini sessiz gönder", + "twilioApiKeyHelptext": "API anahtarı isteğe bağlıdır ancak önerilir. TwilioConsole sayfasından Hesap SID ve Kimlik Doğrulama Token'ı ikilisini ya da Hesap SID ile API Anahtarı ve API Gizli Anahtarı çiftini sağlayabilirsiniz", + "webhookGetMethodDesc": "GET, verileri sorgu parametreleri olarak gönderir ve gövde (body) yapılandırmasına izin vermez. Uptime Kuma Push monitörlerini tetiklemek için kullanışlıdır.", + "brevoLeaveBlankForDefaultName": "varsayılan ad için boş bırakın", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Nextcloud Talk botu kurmak, sunucuda yönetici erişimi gerektirir.", + "Recipient Numbers": "Alıcı Numaraları", + "Clone Maintenance": "Bakım Kopyası", + "ariaPauseMaintenance": "Bu bakım programını duraklatın", + "ariaResumeMaintenance": "Bu bakım programını devam ettir", + "ariaCloneMaintenance": "Bu bakım programının bir kopyasını oluşturun", + "ariaEditMaintenance": "Bu bakım programını düzenle", + "ariaDeleteMaintenance": "Bu bakım programını sil", + "Browser not supported": "Tarayıcı desteklenmiyor", + "Notifications Enabled": "Bildirimler Etkinleştirildi", + "Allow Notifications": "Bildirimlere İzin Ver", + "Unable to get permission to notify": "Bildirim izni alınamadı (talep reddedildi veya yok sayıldı).", + "Webpush Helptext": "Web push yalnızca SSL (HTTPS) bağlantılarıyla çalışır. iOS cihazlarda, web sayfasının önceden ana ekrana eklenmesi gerekir.", + "showOnlyLastHeartbeat": "Yalnızca Son Ping’i Göster", + "System Service": "Sistem Servisi", + "Message Format": "Mesaj Formatı", + "steamApiKeyDescriptionAt": "Steam Oyun Sunucusunu izlemek için Steam Web-API anahtarı gereklidir. API anahtarı almak için bağlantıyı kullanabilirsiniz: {url}", + "days": "{n} gün | {n} gün", + "invalidURL": "Geçersiz URL", + "settingsDomainExpiry": "Alan Adı Geçerlilik Sonu", + "Metadata": "Metaveri", + "enableSSL": "SSL/TLS'i aktifleştir", + "mariadbUseSSLHelptext": "Veritabanına şifreli bağlantı kullanmak için aktifleştirin. Çoğu bulut veritabanı için gereklidir.", + "mariadbCaCertificateLabel": "CA Sertifikası", + "mariadbCaCertificateHelptext": "CA sertifikasını kendiniziz imzaladığı sertifikayla kullanmak için PEM formatında yapıştırın. Eğer veritabanınız CA tarafından imzalanmış sertifika kullanıyorsa boş bırakın.", + "Service Name": "Servis Adı", + "serwersmsRecipientTypePhone": "Telefon numarası", + "serwersmsRecipientTypeGroup": "Grup", + "serwersmsGroupId": "Grup Kimliği", + "selectAllMonitorsAria": "Bütün monitörleri seç", + "SSL/TLS": "SSL/TLS", + "deselectAllMonitorsAria": "Hiçbir monitörü seçme", + "unknownDays": "Bİlinmeyen gün", + "ignoreSecWebsocketAcceptHeaderDescription": "Websocket yükseltmesi başarılı olursa, sunucunun Sec-WebSocket-Accept başlığıyla yanıt vermemesine izin verir.", + "Ignore Sec-WebSocket-Accept header": "{0} başlığını yoksay", + "versionIs": "Versiyon: {version}", + "Certificate Chain:": "Sertifika Zinciri:", + "Examples:": "Örnekler: {0}", + "wsSubprotocolDescription": "Virgülle ayrılmış bir alt protokol listesi girin. Alt protokoller hakkında daha fazla bilgi için lütfen {documentation} bakın", + "oracledbConnectionString": "Oracle Veritabanı: {connectionString}", + "monitorTypeGameServer": "Oyun Sunucusu", + "monitorTypeDatabase": "Veritabanı Monitör Tipi", + "monitorTypeSpecial": "Özel", + "Load More": "Daha Fazla Yükle", + "Loading...": "Yükleniyor...", + "hours": "Saat", + "No incidents recorded": "Hiçbir kaza kaydolmadı", + "minuteShort": "{n} dk | {n} dk", + "years": "{n} yıl | {n} yıl", + "minutes": "{n} dakika | {n} dakika", + "Pin this incident": "Bu kazayı sabitle", + "Monitors": "{n} Servis | {n} Servis", + "retryOnlyOnStatusCodeFailureDescription": "Eğer etkinleştirilirse, yeniden denemeler sadece HTTP durum kodu denemesi hata verirse (ör., sunucu kapalı) gerçekleştirilir. Eğer durum kodu denemesi hatasız çalışırsa ama JSON isteği hata verirse servis yeniden denemesiz bir şekilde kapalı sayılır.", + "Only retry if status code check fails": "Sadece bir durum kodu hata verirse yeniden dene", + "wsCodeDescription": "Durum kodlarıyla ilgili daha fazla bilgi için bkz. {rfc6455}", + "Subprotocol(s)": "Alt protokol(ler)", + "saveResponseDescription": "HTTP yanıtını saklar ve {templateVariable} olarak bildirim şablonlarına sunar", + "saveResponseForNotifications": "Bildirimler için HTTP Başarılı Yanıtını Kaydet", + "saveErrorResponseForNotifications": "Bildirimler için HTTP Hata Yanıtını Kaydet", + "Connection Timeout": "Bağlantı Zaman Aşımı", + "responseMaxLength": "Maksimum Yanıt Uzunluğu (bayt)", + "responseMaxLengthDescription": "Saklanacak yanıt verilerinin maksimum boyutu. Sınırsız boyut için 0 olarak ayarlayın. Daha büyük yanıtlar kırpılacaktır. Varsayılan: 1024 (1 KB)", + "logoutCurrentUser": "Oturumu kapat {username}", + "Resolver Server(s)": "Çözümleyici Sunucu(lar)", + "Incident description": "Olay açıklaması", + "Incident not found or access denied": "Olay bulunamadı veya erişim engellendi", + "Past Incidents": "Geçmiş Olaylar", + "Incident title": "Olay başlığı", + "Pinned incidents are shown prominently on the status page": "Sabitlenmiş olaylar durum sayfasında belirgin bir şekilde gösterilir", + "Edit Incident": "Olayı Düzenle", + "templateAvailableVariables": "Mevcut değişkenler", + "example": "Örnek", + "Result": "Sonuç", + "HeadersInvalidFormatBecause": "İstek başlıkları geçerli JSON değil çünkü {error}", + "BodyInvalidFormatBecause": "İstek gövdesi geçerli bir JSON değil çünkü {error}", + "hostnameCannotBeIP": "DNS sunucu adı bir IP adresi olamaz. Çözümleyici alanını mı kullanmak istediniz?", + "invalidHostnameOrIP": "Geçersiz ana bilgisayar adı veya IP adresi. Ana bilgisayar adı geçerli bir FQDN olmalıdır. Joker karakter kullanılamaz. Alt çizgi içerebilir veya nokta ile bitebilir.", + "invalidDNSHostname": "Geçersiz ana bilgisayar adı. Ana bilgisayar adı geçerli bir FQDN olmalıdır. Joker karakter içerebilir, alt çizgi içerebilir veya nokta ile bitebilir.", + "wildcardOnlyForDNS": "Joker (*) karakterli ana bilgisayar adları yalnızca DNS izleyicileri için desteklenir.", + "Resolve": "Çöz", + "Resolved": "Çözüldü", + "createdAt": "Oluşturulma tarihi: {tarih}", + "lastUpdatedAt": "Son Güncelleme: {date}", + "lastUpdatedAtFromNow": "Son Güncelleme: {date} ({fromNow})", + "Actions": "Eylemler", + "selectedMonitorCountMsg": "seçildi: {n} | seçildi: {n}", + "selectMonitorMsg": "İşlemlerin gerçekleştirileceği monitörleri seçin", + "deleteIncidentMsg": "Bu olayı silmek istediğinizden emin misiniz?", + "slug is not found": "Slug (kalıcı bağlantı) bulunamadı", + "Bearer Token": "Taşıyıcı Jeton", + "Please input content": "Lütfen içerik girin", + "Please input title": "Lütfen başlığı girin", + "dateCreatedAtFromNow": "Oluşturulma Tarihi: {date} ({fromNow})", + "RSS Title": "RSS Başlığı", + "Leave blank to use status page title": "Durum sayfası başlığını kullanmak için boş bırakın", + "certificateExpiryNotificationHelp": "Önceden kaç gün planlanacağı Ayarlar bölümünden yapılandırılabilir.", + "Cloud ID": "Cloud ID", + "API Token": "API Jetonu", + "See Jira Cloud Docs": "Jira Cloud Belgelerine Bakın", + "certHostnameMismatch": "Sertifika ana bilgisayar adı, izleme URL'siyle eşleşmiyor.", + "sipsakPingWarning": "SIP Options Ping monitörünü kullanabilmek için, Docker olmadan Uptime Kuma'yı ve sunucunuza Sipsak istemcisini kurmanız gerekmektedir.", + "pingPacketSizeWarning": "Linux'ta 16'dan küçük paket boyutları gidiş-dönüş süreleri oluşturmaz", + "wayToGetMaxToken": "MAX bot token'ını ve diğer ayrıntıları {0} adresinden alabilirsiniz.", + "maxMessenger": "MAX kurye", + "matrixUseTemplate": "Özel mesaj şablonu kullanın", + "matrixUseTemplateDescription": "Etkinleştirilirse, mesaj özel bir şablon kullanılarak gönderilecektir.", + "maxApiUrlDescription": "MAX mesajlaşma uygulaması için temel API URL'si. Varsayılan: https://platform-api.max.ru", + "wayToGetMaxChatID": "MAX'te mesajların iletilmesi gereken sohbet tanımlayıcısını belirtin.", + "maxUseTemplate": "Özel mesaj şablonu kullanın", + "maxUseTemplateDescription": "Etkinleştirilirse, mesaj özel bir şablon kullanılarak gönderilecektir.", + "maxTemplateFormatDescription": "MAX mesajlaşma uygulaması düz metin, Markdown ve HTML biçimlendirmesini destekler.", + "Notification Action": "Bildirim Eylemi", + "homeAssistantNotificationActionHelptext": "Bildirim Eylemleri listesini Home Assistant'ta \"Ayarlar > Geliştirici Araçları > Eylemler\" altında bulabilirsiniz. Eylemlerinizi bulmak için \"bildir\" kelimesini arayın. Yalnızca \"bildir.\" kelimesinden sonraki kısmı girin, örneğin \"notify.mobile_app_xyz\" eylemi için \"mobile_app_xyz\" yazın. Yerleşik mobil bildirimler için \"mobile_app_xyz aracılığıyla bildirim gönder\" ifadesini arayın (\"Bildirim gönder\" değil).", + "frontendVersionIs": "Frontend Sürümü: {version}", + "cronScheduleDescription": "Program: {description}", + "Duration (Minutes)": "Süre (Dakika)", + "notificationUniversal": "Evrensel", + "notificationChatPlatforms": "Sohbet Platformları", + "notificationPushServices": "Bildirim Servisleri", + "notificationSmsServices": "SMS Hizmetleri", + "notificationEmail": "Eposta", + "notificationIncidentManagement": "Olay Yönetimi", + "notificationHomeAutomation": "Ev Otomasyonu", + "notificationOther": "Diğer Entegrasyonlar", + "SMTP Security": "SMTP Güvenliği", + "Ignore STARTTLS": "STARTTLS'yi dikkate almayın", + "Use STARTTLS": "STARTTLS Kullan", + "Disable STARTTLS": "STARTTLS'yi devre dışı bırak", + "disableSTARTTLSDescription": "STARTTLS'yi desteklemeyen SMTP sunucuları için bu seçeneği etkinleştirin. Bu, e-postaların şifrelenmemiş bir bağlantı üzerinden gönderilmesine neden olur.", + "teamsEnableTags": "Etiketleri dahil et", + "teamsEnableTagsDescription": "Etkinleştirilirse, mesaj izleme etiketlerini içerecektir.", + "API Version": "API Versiyonu", + "deleteMonitorsMsg": "Seçili monitörleri silmek istediğinizden emin misiniz?", + "pausedMonitorsMsg": "Duraklatılmış {n} monitör | Duraklatılmış {n} monitör", + "resumedMonitorsMsg": "{n} monitör yeniden başlatıldı | {n} monitör yeniden başlatıldı", + "deletedMonitorsMsg": "Silinen {n} monitör | Silinen {n} monitör", + "noMonitorsPausedMsg": "Hiçbir monitör duraklatılmadı (hiçbiri aktif değildi)", + "serwersmsRecipientType": "Alıcı türü", + "serwersmsGroupIdHelptext": "Müşteri Paneli'ndeki kimlikler veya grup kimlikleri. Bu tanımlayıcılar, işlem grupları/dizin kullanılarak indirilebilir veya Müşteri Paneli'ndeki düzenleme grubundan kopyalanabilir.", + "Access Key": "Erişim Anahtarı", + "ooredooUsernameDescription": "Ooredoo toplu SMS kontrol paneline giriş yapmak için kullandığınız e-posta adresi.", + "ooredooAccessKeyDescription": "Ooredoo SMS kontrol panelinizde \"API Erişim Anahtarı\" altında bulunur. Gösterildiği gibi aynen yapıştırın; gönderilmeden önce otomatik olarak base64 kodlaması yapılır.", + "ooredooBearerTokenDescription": "Ooredoo SMS kontrol panelinizdeki örnek cURL komutunda \"Authorization: Bearer\" ifadesinden sonra gösterilen belirteç.", + "ooredooRecipientDescription": "Virgül veya boşlukla ayrılmış bir veya daha fazla sayı girin. Yerel 7 haneli sayılar otomatik olarak 960 önekiyle başlar.", + "Leave blank to use the default Ooredoo endpoint.": "Varsayılan Ooredoo uç noktasını kullanmak için boş bırakın.", + "octopushEndpoint": "octopush (uç nokta (endpoint): {url})", + "legacyOctopushEndpoint": "Eski Octopush-DM (uç nokta: {url})", + "wxPusherSptDescription": "WxPusher Simple Push (SPT), Android, iOS, HarmonyOS, Windows, macOS ve Linux olmak üzere 6 platformda bağımsız WxPusher uygulaması aracılığıyla gönderim yapar ({download}). SPT'nizi almak için aşağıdaki QR kodunu tarayın ve göndermeye hazır olun. Birden fazla SPT'yi virgülle (,) ayırın - daha fazla bilgi için {docs} bakın.", + "wxPusherDownloadApp": "uygulamayı indirin", + "noMonitorsResumedMsg": "Hiçbir monitör yeniden başlatılmadı (hiçbiri devre dışı değildi)", + "bulkDeleteErrorMsg": "{n} monitör silinemedi | {n} monitör silinemedi", + "Sets end time based on start time": "Başlangıç zamanına göre bitiş zamanını belirler", + "Please set start time first": "Lütfen önce başlangıç saatini belirleyin", + "noMonitorsSelectedWarning": "Etkilenen monitörler olmadan bir bakım işlemi oluşturuyorsunuz. Devam etmek istediğinizden emin misiniz?", + "noMonitorsOrStatusPagesSelectedError": "Etkilenen monitörler veya durum sayfaları olmadan bakım oluşturulamaz", + "Peer ID": "Akran (Peer) ID", + "vkApiVersionDescription": "İstekler için kullanılan VK API sürümü. Uyumluluk için özellikle başka bir VK API sürümüne ihtiyacınız yoksa varsayılanı bırakın.", + "vkDontParseLinks": "Bağlantı parçacıklarını devre dışı bırak", + "vkDontParseLinksDescription": "Bu özellik etkinleştirilirse, VK bağlantı önizlemeleri/özetleri oluşturmaz.", + "vkPeerIdDescription": "Hedef VK peer_id'sini girin. Bu değer API'ye olduğu gibi gönderilir.", + "Google Apps Script Webhook URL": "Google Apps Script Webhook URL'si", + "Deploy a Google Apps Script as a web app and paste the URL here": "Google Apps Script'i web uygulaması olarak dağıtın ve URL'yi buraya yapıştırın", + "Quick Setup Guide": "Hızlı Kurulum Kılavuzu", + "Open your Google Spreadsheet": "Google E-Tablonuzu açın", + "You can divide numbers with commas or semicolons": "Sayıları {comma} veya {semicolon} kullanarak bölebilirsiniz", + "Go to Extensions → Apps Script": "Uzantılar → Apps Script'e gidin", + "Paste the script code (see below)": "Komut dosyası kodunu yapıştırın (aşağıya bakınız)", + "Click Deploy → New deployment → Web app": "Dağıt → Yeni dağıtım → Web uygulaması'na tıklayın", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "'Çalıştıracak kişi: Ben' ve 'Erişim yetkisi: Herkes' seçeneklerini ayarlayın", + "Copy the web app URL and paste it above": "Web uygulamasının URL'sini kopyalayıp yukarıya yapıştırın", + "Google Apps Script Code": "Google App Script Code", + "Copy to Clipboard": "Panoya Kopyala", + "Copied to clipboard!": "Panoya kopyalandı!", + "Failed to copy to clipboard": "Panoya kopyalama başarısız oldu", + "checkPriceAt": "{url} adresinden {service} hizmetlerinin fiyatlarını kontrol edin", + "OptionalParameters": "İsteğe bağlı parametreler", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Kargo kısıtlamaları nedeniyle, isteğe bağlı değişkenleri etkinleştirmeniz durumunda teslimatın gerçekleşmeme riski bulunmaktadır", + "aliyun-template-requirements-and-parameters": "Aliyun SMS şablonu şu parametreleri içermelidir: {parameters}", + "aliyun-template-optional-parameters": "İsteğe bağlı parametreler: {parameters}", + "WeCom Mentioned Mobile List": "WeCom'un Bahsedilen Mobil Cihazlar Listesi", + "WeCom Mentioned Mobile List Description": "Bahsetmek istediğiniz telefon numaralarını girin. Birden fazla numarayı virgülle ayırın. Herkesi etiketlemek için {'@'}all ifadesini kullanın.", + "signalUseTemplate": "Özel mesaj şablonu kullanın", + "signalUseTemplateDescription": "Etkinleştirilirse, mesaj özel bir şablon kullanılarak gönderilecektir. Bildirim biçimini özelleştirmek için Liquid şablonlama özelliğini kullanabilirsiniz.", + "aboutJiraCloudId": "Jira Cloud ID hakkında daha fazla bilgi: {0}", + "see Jira Cloud Docs": "Jira Cloud belgelerine bakın", + "Jira Service Management": "Jira Servis Yönetimi", + "slackIncludeGroupName": "İzleme grubu adını ekleyin", + "slackIncludeGroupNameDescription": "Etkinleştirilirse, farklı gruplardaki aynı ada sahip monitörleri ayırt etmeye yardımcı olmak için monitör grubu yolu bildirimlere dahil edilecektir.", + "slackUseTemplate": "Özel mesaj şablonu kullanın", + "slackUseTemplateDescription": "Etkinleştirilirse, mesaj özel bir şablon kullanılarak gönderilecektir. MonitorJSON.path veya monitorJSON.pathName aracılığıyla monitör grubu bilgilerini eklemek için Liquid şablonlama özelliğini kullanabilirsiniz.", + "smtpAdditionalHeadersTitle": "Ek Başlıklar (Header)", + "smtpAdditionalHeadersDesc": "Kullanım için lütfen Nodemailer Özel Başlıkları {0} bölümüne bakın.", + "wxPusherDocs": "resmi rehber", + "wayToGetEgoSMSToken": "EgoSMS API kullanıcı adınızı ve şifrenizi {here} alabilirsiniz.", + "egosmsSenderDescription": "Alıcılara gösterilecek gönderici kimliği (en fazla 11 karakter). Boş bırakılırsa varsayılan olarak EGOSMS kullanılır.", + "egosmsPhoneNumberDescription": "Ülke koduyla birlikte alıcı numarası, + veya 00 olmadan (örneğin 2567XXXXXXXX).", + "Hash Key": "Karma Anahtar", + "wayToGetBearSMSToken": "BearSMS API kullanıcı adınızı ve karma anahtarınızı {0} adresinden alabilirsiniz.", + "bearsmsPhoneNumberDescription": "Ülke koduyla birlikte alıcı numarası, + veya 00 olmadan (örneğin 9725XXXXXXXX).", + "bearsmsSenderDescription": "Alıcılara gösterilecek isteğe bağlı gönderici kimliği (en fazla 11 karakter). Bu kimliğin BearSMS hesabınızda onaylanması gerekir, aksi takdirde hesabın varsayılan gönderici kimliği kullanılır.", + "Analytics Type": "Analytics Türü", + "Analytics ID": "Analytics ID", + "Analytics Script URL": "Analytics Script URL", + "WebSocket Options": "WebSocket Seçenekleri", + "ntfyCall": "Telefon Görüşmesi", + "ntfyCallHelptext": "Uyarı tetiklendiğinde telefon görüşmesi yapın. İlk doğrulanmış numaranızı kullanmak için 'evet' olarak ayarlayın veya belirli bir telefon numarası girin (örneğin, +12223334444). ntfy Pro ve doğrulanmış bir telefon numarası gereklidir.", + "ntfyUseTemplate": "Bildirim şablonlarını özelleştirin", + "ntfyUseTemplateDescription": "LiquidJS şablonlama kullanarak bildirim başlıklarını ve mesajlarını özelleştirmek için bunu etkinleştirin", + "ntfyCustomTitle": "Özel Başlık Şablonu", + "ntfyCustomMessage": "Özel Mesaj Şablonu", + "ntfyNotificationTemplateFallback": "Varsayılan Uptime Kuma formatını kullanmak için boş bırakın", + "Device ID or Group ID": "Cihaz Kimliği veya Grup Kimliği", + "notifyAppDeviceIdDescription": "Notify! uygulamasından alınan Cihaz Kimliği veya bir gruptaki tüm cihazlara aynı anda uyarı göndermek için bir Grup Kimliği kullanılabilir.", + "notifyAppSetup": "Notify! uygulamasını açın ve Ayarlar bölümünden Cihaz Kimliğinizi ve belirtecinizi bulun. Hesap gerekmez. Daha fazla bilgi için {0} adresini ziyaret edin.", + "telnyxApiKey": "API Key (V2)", + "telnyxApiKeyHelptext": "Telnyx Portalındaki API Anahtarları bölümünden Telnyx V2 API anahtarınızı edinin", + "telnyxPhoneNumber": "Numaradan", + "telnyxPhoneNumberHelptext": "Göndermek için kullanacağınız Telnyx telefon numaranız (Telnyx hesabınızda kayıtlı olmalıdır, örneğin +15551234567)", + "telnyxToNumber": "Numaraya", + "telnyxToNumberHelptext": "SMS bildirimlerini almak istediğiniz hedef telefon numarası (örneğin +15559876543)", + "telnyxMessagingProfileId": "Mesajlaşma Profili Kimliği (isteğe bağlı)", + "telnyxMessagingProfileIdHelptext": "İsteğe bağlı olarak, gönderici ayarlarını ve özelliklerini kontrol etmek için bir Telnyx Mesajlaşma Profili ilişkilendirebilirsiniz", + "plivoAuthID": "Kimlik Doğrulama Kimliği", + "plivoAuthIDHelptext": "Plivo Konsolundan aldığınız Plivo Kimlik Doğrulama Kimliğiniz", + "plivoAuthToken": "Kimlik Doğrulama Belirteci", + "plivoAuthTokenHelptext": "Plivo Konsolundan Plivo Kimlik Doğrulama Token'ınız", + "plivoFromNumber": "Numaradan", + "plivoFromNumberHelptext": "Gönderilecek Plivo telefon numarası (örneğin +15551234567). SMS'lerde onaylanmış alfanümerik gönderici kimliği de kullanılabilir; sesli aramalar için Plivo telefon numarası gereklidir.", + "plivoToNumber": "Numaraya", + "plivoToNumberHelptext": "SMS veya sesli aramanın alınacağı hedef telefon numarası (örneğin, +15559876543)", + "plivoMessageType": "Mesaj Türü", + "plivoVoiceCall": "Sesli Arama", + "plivoAnswerUrl": "Yanıt URL'si", + "plivoAnswerUrlHelptext": "Çağrı için Plivo XML'ini döndüren URL. Uyarı metni, ?message= sorgu parametresi olarak eklenir, böylece uç noktanız örneğin {message} döndürebilir.", + "Open Badge Link Generator": "Rozet Bağlantısı Oluşturucuyu Aç", + "Badge Link Generator": "{0}'ın Rozet Bağlantısı Oluşturucusu", + "Badge Link Generator Helptext": "Herkese açık durum sayfalarına atanmış tüm monitörler için rozet bağlantıları mevcuttur. Daha fazla bilgi için lütfen {documentation} bakın.", + "Flowtriq Webhook URL": "Webhook URL", + "Flowtriq API Key": "API Key", + "flowtriqApiKeyDescription": "Flowtriq örneğinize yapılan webhook isteklerinin kimlik doğrulaması için isteğe bağlı API anahtarı.", + "Pinglet Topic URL": "Konu URL'si", + "pingletTopicUrlDescription": "Uyarıların gönderileceği Pinglet konusunun yayın URL'si, örneğin: https://app.pinglet.co.uk/your-namespace/alerts. Konu, henüz mevcut değilse ilk yayın sırasında oluşturulur.", + "gamedigToken": "Token", + "gamedigTokenDescription": "İsteğe bağlı. Bazı oyun sunucuları kimlik doğrulama belirteci gerektirir.", + "ClickUp API Token": "ClickUp API Token", + "ClickUp API Token Description": "ClickUp'ta kişisel erişim belirteci oluşturmak için Settings -> Integrations & ClickApps -> ClickUp API -> API Token yolunu izleyebilirsiniz", + "ClickUp Workspace ID": "ClickUp Çalışma Alanı Kimliği", + "ClickUp Workspace ID Description": "Bunu çalışma alanınızın URL'sinde, app.clickup.com/ adresinden hemen sonra bulabilirsiniz, örneğin app.clickup.com/12345678/home.", + "ClickUp Channel ID": "ClickUp Kanal Kimliği", + "ClickUp Channel ID Description": "Bunu, sohbet kanalının URL'sinde, /chatr/ kısmından hemen sonra bulabilirsiniz, örneğin .../chatr/abc 123-4567.", + "Screenshot Delay": "Ekran Görüntüsü Gecikmesi ({milliseconds} bekler)", + "milliseconds": "{n} milisaniye | {n} milisaniye", + "screenshotDelayDescription": "İsteğe bağlı olarak, ekran görüntüsünü almadan önce bu kadar milisaniye bekleyin. Maksimum: {maxValueMs}ms (0,5 × aralık).", + "screenshotDelayWarning": "Daha yüksek değerler tarayıcının daha uzun süre açık kalmasına neden olur ve bu da aynı anda birçok izleme işlemi yapıldığında bellek kullanımını artırabilir.", + "systemService": "Sistem Servisi", + "systemServiceName": "Hizmet Adı", + "systemServiceDescription": "{service_name} sistem hizmetinin etkin olup olmadığını kontrol eder", + "systemServiceDescriptionLinux": "Linux systemd servisi {service_name}'in etkin olup olmadığını kontrol eder", + "systemServiceDescriptionWindows": "Windows Hizmet Yöneticisi {service_name}'in çalışıp çalışmadığını kontrol eder", + "systemServiceCommandHint": "Kullanılan komut: {command}", + "systemServiceExpectedOutput": "Beklenen Çıktı: \"{0}\"", + "pm2Description": "PM2 işlemi {service_name}'in çevrimiçi olup olmadığını kontrol eder", + "pm2ExpectedStates": "Beklenen durum: çevrimiçi. Durdurulmuş ve hata veren durumlar KAPALI olarak kabul edilir.", + "evolutionCustomMessageTitle": "Özel Mesaj (isteğe bağlı)", + "evolutionCustomMessageDesc": "Varsayılan mesajı kullanmak için boş bırakın.", + "snmpV3Username": "SNMPv3 Kullanıcı Adı", + "message": "mesaj", + "json_value": "JSON değeri", + "Select All": "Tümünü Seç", + "Deselect All": "Tüm Seçimi Kaldır", + "Enter the list of nodes": "RabbitMQ yönetim düğümlerinin listesini girin", + "Press Enter to add node": "Düğüm eklemek için Enter tuşuna basın", + "turbosmtpConsumerKey": "Tüketici Anahtarı", + "turbosmtpConsumerSecret": "Tüketici Gizli Anahtarı", + "resendApiKey": "API Anahtarını Yeniden Gönder", + "resendApiHelp": "Burada bir API anahtarı oluşturun {0}", + "resendFromName": "İsimden", + "resendFromEmail": "E-postadan", + "resendLeaveBlankForDefaultName": "Varsayılan ad için boş bırakın", + "resendLeaveBlankForDefaultSubject": "Varsayılan konu için boş bırakın", + "resendSubject": "Konu", + "openwaSession": "Oturum", + "openwaChatId": "Sohbet Kimliği (Telefon Numarası / İletişim Kimliği / Grup Kimliği)", + "wayToGetOpenwaApiUrl": "OpenWA örneğinizin URL'si.", + "wayToGetOpenwaApiKey": "OpenWA örneğiniz için kullanılan API anahtarı." +} diff --git a/src/lang/ug.json b/src/lang/ug.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/lang/ug.json @@ -0,0 +1 @@ +{} diff --git a/src/lang/uk-UA.json b/src/lang/uk-UA.json new file mode 100644 index 0000000..c1497d3 --- /dev/null +++ b/src/lang/uk-UA.json @@ -0,0 +1,1633 @@ +{ + "languageName": "Українська", + "checkEverySecond": "Перевірка кожні {0} секунд", + "retriesDescription": "Максимальна кількість спроб перед позначенням сервісу як недоступного та надсиланням повідомлення", + "ignoreTLSError": "Ігнорувати помилки TLS/SSL для сайтів HTTPS", + "upsideDownModeDescription": "Реверс статусу сервісу. Якщо сервіс доступний, він позначається як НЕДОСТУПНИЙ.", + "maxRedirectDescription": "Максимальна кількість перенаправлень. Поставте 0, щоб вимкнути перенаправлення.", + "acceptedStatusCodesDescription": "Виберіть коди статусів для визначення доступності сервісу.", + "passwordNotMatchMsg": "Повторення паролю не збігається.", + "notificationDescription": "Прив’яжіть сповіщення до моніторів.", + "keywordDescription": "Пошук слова в чистому HTML або JSON-відповіді. Пошук чутливий до регістру.", + "pauseDashboardHome": "Пауза", + "deleteMonitorMsg": "Ви дійсно хочете видалити цей монітор?", + "deleteNotificationMsg": "Ви дійсно хочете видалити це сповіщення для всіх моніторів?", + "resolverserverDescription": "Cloudflare є сервером за замовчуванням. Ви завжди можете вказати список IP-адре або імен хостів, розділених комами.", + "rrtypeDescription": "Виберіть тип ресурсного запису, який ви хочете відстежувати", + "pauseMonitorMsg": "Ви дійсно хочете поставити на паузу?", + "Settings": "Налаштування", + "Dashboard": "Панель керування", + "New Update": "Оновлення", + "Language": "Мова", + "Appearance": "Зовнішній вигляд", + "Theme": "Тема", + "General": "Загальне", + "Version": "Версія", + "Check Update On GitHub": "Перевірити оновлення на GitHub", + "List": "Список", + "Add": "Додати", + "Add New Monitor": "Новий монітор", + "Quick Stats": "Статистика", + "Up": "Доступний", + "Down": "Недоступний", + "Pending": "Очікування", + "Unknown": "Невідомо", + "Pause": "Пауза", + "Name": "Ім’я", + "Status": "Статус", + "DateTime": "Дата і час", + "Message": "Повідомлення", + "No important events": "Важливих подій немає", + "Resume": "Відновити", + "Edit": "Змінити", + "Delete": "Видалити", + "Current": "Поточний", + "Uptime": "Аптайм", + "Cert Exp.": "Сертифікат сп.", + "day": "день | днів", + "-day": "днів", + "hour": "година | години", + "-hour": "години", + "Response": "Відповідь", + "Ping": "Пінг", + "Monitor Type": "Тип монітора", + "Keyword": "Ключове слово", + "Friendly Name": "Ім’я", + "URL": "URL", + "Hostname": "Адреса хосту", + "Port": "Порт", + "Heartbeat Interval": "Частота опитування", + "Retries": "Спроб", + "Advanced": "Додатково", + "Upside Down Mode": "Реверс статусу", + "Max. Redirects": "Макс. кількість перенаправлень", + "Accepted Status Codes": "Припустимі коди статусу", + "Save": "Зберегти", + "Notifications": "Сповіщення", + "Not available, please setup.": "Недоступно, будь ласка, налаштуйте.", + "Setup Notification": "Створити сповіщення", + "Light": "Світла", + "Dark": "Темна", + "Auto": "Авто", + "Theme - Heartbeat Bar": "Тема — Смуга частоти опитування", + "Normal": "Звичайний", + "Bottom": "Знизу", + "None": "Відсутня", + "Timezone": "Часовий пояс", + "Search Engine Visibility": "Видимість для пошукових систем", + "Allow indexing": "Дозволити індексування", + "Discourage search engines from indexing site": "Заборонити індексування", + "Change Password": "Змінити пароль", + "Current Password": "Поточний пароль", + "New Password": "Новий пароль", + "Repeat New Password": "Повтор нового пароля", + "Update Password": "Оновити пароль", + "Disable Auth": "Вимкнути авторизацію", + "Enable Auth": "Увімкнути авторизацію", + "disableauth.message1": "Ви впевнені, що бажаєте {disableAuth}?", + "disable authentication": "вимкнути авторизацію", + "disableauth.message2": "Це підходить для {intendThirdPartyAuth} пееред відкриттям Uptime Kuma, наприклад Cloudflare Access.", + "where you intend to implement third-party authentication": "тих, у кого встановлена інша авторизація", + "Please use this option carefully!": "Будь ласка, використовуйте з обережністю!", + "Logout": "Вийти", + "Leave": "Відміна", + "I understand, please disable": "Я розумію, все одно відключити", + "Confirm": "Підтвердити", + "Yes": "Так", + "No": "Ні", + "Username": "Логін", + "Password": "Пароль", + "Remember me": "Запам’ятати мене", + "Login": "Увійти", + "No Monitors, please": "Моніторів немає, будь ласка", + "No Monitors": "Монітори відсутні", + "add one": "створіть новий", + "Notification Type": "Тип сповіщення", + "Email": "Пошта", + "Test": "Перевірка", + "Certificate Info": "Інформація про сертифікат", + "Resolver Server": "DNS сервер", + "Resource Record Type": "Тип ресурсного запису", + "Last Result": "Останній результат", + "Create your admin account": "Створіть обліковий запис адміністратора", + "Repeat Password": "Повторіть пароль", + "respTime": "Час відповіді (мс)", + "notAvailableShort": "Н/д", + "Create": "Створити", + "clearEventsMsg": "Ви впевнені, що хочете видалити всі події для цього монітора? Це призведе до остаточного видалення пов’язаних даних повідомлень, і цю дію неможливо скасувати.", + "clearHeartbeatsMsg": "Ви впевнені, що хочете видалити всі дані про серцебиття для цього монітора? Це призведе до остаточного видалення всіх даних про серцебиття, і цю дію неможливо скасувати.", + "confirmClearStatisticsMsg": "Ви дійсно хочете видалити ВСЮ статистику?", + "Clear Data": "Видалити статистику", + "Events": "Події", + "Heartbeats": "Опитування", + "Auto Get": "Авто-отримання", + "enableDefaultNotificationDescription": "Для кожного нового монітора це сповіщення буде включено за замовчуванням. Ви все ще можете відключити сповіщення в кожному моніторі окремо.", + "Default enabled": "Використовувати за замовчуванням", + "Also apply to existing monitors": "Застосувати до існуючих моніторів", + "Export": "Експорт", + "Import": "Імпорт", + "backupDescription": "Ви можете зберегти резервну копію всіх моніторів та повідомлень у вигляді JSON-файлу.", + "backupDescription2": "P.S.: Історія та події збережені не будуть.", + "backupDescription3": "Важливі дані, такі як токени повідомлень, додаються під час експорту, тому зберігайте файли в безпечному місці.", + "alertNoFile": "Виберіть файл для імпорту.", + "alertWrongFileType": "Виберіть JSON-файл.", + "twoFAVerifyLabel": "Будь ласка, введіть свій токен, щоб перевірити роботу 2FA:", + "tokenValidSettingsMsg": "Токен дійсний! Тепер ви можете зберегти налаштування 2FA.", + "confirmEnableTwoFAMsg": "Ви дійсно хочете увімкнути 2FA?", + "confirmDisableTwoFAMsg": "Ви дійсно хочете вимкнути 2FA?", + "Apply on all existing monitors": "Застосувати до всіх існуючих моніторів", + "Verify Token": "Перевірити токен", + "Setup 2FA": "Налаштування 2FA", + "Enable 2FA": "Увімкнути 2FA", + "Disable 2FA": "Вимкнути 2FA", + "2FA Settings": "Налаштування 2FA", + "Two Factor Authentication": "Двофакторна аутентифікація", + "Active": "Активно", + "Inactive": "Неактивно", + "Token": "Токен", + "Show URI": "Показати URI", + "Clear all statistics": "Очистити статистику", + "retryCheckEverySecond": "Повтор кожні {0} секунд", + "importHandleDescription": "Виберіть «Пропустити існуючі», якщо ви хочете пропустити кожен монітор або повідомлення з таким же ім’ям. «Перезаписати» видалить кожен існуючий монітор або повідомлення та додасть заново. Варіант «Не перевіряти» примусово відновлює всі монітори і повідомлення, навіть якщо вони вже існують.", + "confirmImportMsg": "Ви дійсно хочете відновити резервну копію? Переконайтеся, що ви вибрали відповідний варіант імпорту.", + "Heartbeat Retry Interval": "Інтервал повтору опитування", + "Import Backup": "Імпорт", + "Export Backup": "Експорт", + "Skip existing": "Пропустити існуючі", + "Overwrite": "Перезаписати", + "Options": "Опції", + "Keep both": "Не перевіряти", + "Tags": "Теги", + "Add New below or Select...": "Додати новий або вибрати…", + "Tag with this name already exist.": "Такий тег вже існує.", + "Tag with this value already exist.": "Тег із таким значенням вже існує.", + "color": "колір", + "value (optional)": "значення (опціонально)", + "Gray": "Сірий", + "Red": "Червоний", + "Orange": "Помаранчевий", + "Green": "Зелений", + "Blue": "Синій", + "Indigo": "Індиго", + "Purple": "Пурпурний", + "Pink": "Рожевий", + "Search...": "Пошук…", + "Avg. Ping": "Середній пінг", + "Avg. Response": "Середній час відповіді", + "Entry Page": "Головна сторінка", + "statusPageNothing": "Тут порожньо. Додайте групу або монітор.", + "No Services": "Немає сервісів", + "All Systems Operational": "Всі системи працюють у штатному режимі", + "Partially Degraded Service": "Сервіси працюють частково", + "Degraded Service": "Всі сервіси не працюють", + "Add Group": "Додати групу", + "Add a monitor": "Додати монітор", + "Edit Status Page": "Редагувати", + "Go to Dashboard": "Панель керування", + "Status Page": "Сторінка статусу", + "Status Pages": "Сторінки статусу", + "Discard": "Скасування", + "Create Incident": "Створити інцидент", + "Switch to Dark Theme": "Темна тема", + "Switch to Light Theme": "Світла тема", + "telegram": "Telegram", + "webhook": "Вебхук", + "smtp": "Email (SMTP)", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Підтримка 50+ сервісів повідомлень)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "Primary Base URL": "Основна URL", + "Push URL": "URL пуша", + "needPushEvery": "Цю URL необхідно викликати кожні {0} секунд.", + "pushOptionalParams": "Додаткові параметри: {0}", + "defaultNotificationName": "Моє сповіщення {notification} ({number})", + "here": "тут", + "Required": "Потрібно", + "Bot Token": "Токен бота", + "wayToGetTelegramToken": "Ви можете взяти токен тут — {0}.", + "Chat ID": "ID чату", + "supportTelegramChatID": "Підтримуються ID чатів, груп та каналів", + "wayToGetTelegramChatID": "Ви можете взяти ID вашого чату, відправивши повідомлення боту і перейшовши по цьому URL для перегляду chat_id:", + "YOUR BOT TOKEN HERE": "ВАШ ТОКЕН БОТА ТУТ", + "chatIDNotFound": "ID чату не знайдено; будь ласка, відправте спочатку повідомлення боту", + "Post URL": "Post URL", + "Content Type": "Тип контенту", + "webhookJsonDesc": "{0} підходить для будь-яких сучасних HTTP-серверів, наприклад Express.js", + "webhookFormDataDesc": "{multipart} підходить для PHP. JSON-вивід необхідно буде обробити за допомогою {decodeFunction}", + "secureOptionNone": "Ні / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Ігнорувати помилки TLS", + "From Email": "Від кого", + "emailCustomSubject": "Своя тема", + "To Email": "Кому", + "smtpCC": "Копія", + "smtpBCC": "Прихована копія", + "Discord Webhook URL": "Discord Вебхук URL", + "wayToGetDiscordURL": "Ви можете отримати його, перейшовши до Налаштування сервера -> Інтеграції -> Перегляд веб-хуків -> Новий веб-хук", + "Bot Display Name": "Ім’я бота, що відображається", + "Prefix Custom Message": "Свій префікс повідомлення", + "Hello @everyone is...": "Привіт {«@»}everyone це…", + "Webhook URL": "URL вебхука", + "wayToGetTeamsURL": "Як створити URL вебхука ви можете дізнатися тут — {0}.", + "Номер": "Номер", + "Recipients": "Одержувачі", + "needSignalAPI": "Вам необхідний клієнт Signal із підтримкою REST API.", + "wayToCheckSignalURL": "Пройдіть по цьому URL, щоб дізнатися як налаштувати такий клієнт:", + "signalImportant": "ВАЖЛИВО: Не можна змішувати в Одержувачах групи та номери!", + "Application Token": "Токен програми", + "Server URL": "URL сервера", + "Priority": "Пріоритет", + "Icon Emoji": "Іконка Emoji", + "Channel Name": "Ім’я каналу", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Більше інформації про вебхуки: {0}", + "aboutChannelName": "Введіть ім’я каналу в поле {0} Ім’я каналу, якщо ви хочете обійти канал вебхука. Наприклад: #other-channel", + "aboutKumaURL": "Якщо поле Uptime Kuma URL в налаштуваннях залишиться порожнім, за замовчуванням буде використовуватися посилання на проект на GitHub.", + "emojiCheatSheet": "Шпаргалка по Emoji: {0}", + "User Key": "Ключ користувача", + "Device": "Пристрій", + "Message Title": "Заголовок повідомлення", + "Notification Sound": "Звук сповіщення", + "More info on:": "Більше інформації: {0}", + "pushoverDesc1": "Екстренний пріоритет (2) має таймаут повтору за замовчуванням 30 секунд і закінчується через 1 годину.", + "pushoverDesc2": "Якщо ви бажаєте надсилати повідомлення різним пристроям, необхідно заповнити поле Пристрій.", + "SMS Type": "Тип SMS", + "octopushTypePremium": "Преміум (Швидкий - рекомендується для алертів)", + "octopushTypeLowCost": "Дешевий (Повільний - іноді блокується операторами)", + "checkPrice": "Тарифи {0}:", + "octopushLegacyHint": "Ви використовуєте стару версію Octopush (2011-2020) або нову?", + "Check octopush prices": "Тарифи Octopush {0}.", + "octopushPhoneNumber": "Номер телефону (між. формат, наприклад: +380123456789) ", + "octopushSMSSender": "Ім’я відправника SMS: 3-11 символів алвафіту, цифр та пробілів (a-zA-Z0-9)", + "LunaSea Device ID": "ID пристрою LunaSea", + "Apprise URL": "URL-адреса Apprise", + "Example:": "Приклад: {0}", + "Read more:": "Докладніше: {0}", + "Status:": "Статус: {0}", + "Read more": "Докладніше", + "appriseInstalled": "Apprise встановлено.", + "appriseNotInstalled": "Apprise не встановлено. {0}", + "Access Token": "Токен доступу", + "Channel access token": "Токен доступу каналу", + "Line Developers Console": "Консоль розробників Line", + "lineDevConsoleTo": "Консоль розробників Line — {0}", + "Basic Settings": "Базові налаштування", + "User ID": "ID користувача", + "Messaging API": "API повідомлень", + "wayToGetLineChannelToken": "Спочатку зайдіть в {0}, створіть провайдера та канал (API повідомлень), потім ви зможете отримати токен доступу каналу та ID користувача з вищезгаданих пунктів меню.", + "Icon URL": "URL іконки", + "aboutIconURL": "Ви можете надати посилання на іконку в полі «URL іконки», щоб перевизначити картинку профілю за замовчуванням. Не використовується, якщо задана іконка Emoji.", + "aboutMattermostChannelName": "Ви можете перевизначити канал за замовчуванням, в який пише вебхук, ввівши ім’я каналу в полі «Ім’я каналу». Це необхідно включити в налаштуваннях вебхука Mattermost. Наприклад: #other-channel", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO — дешево та повільно, часто перевантажений. Тільки для одержувачів з Польщі.", + "promosmsTypeFlash": "SMS FLASH — повідомлення автоматично з’являться на пристрої одержувача. Тільки для одержувачів з Польщі.", + "promosmsTypeFull": "SMS FULL — преміум-рівень SMS, можна використовувати своє ім’я відправника (попередньо зареєструвавши його). Надійно для алертів.", + "promosmsTypeSpeed": "SMS SPEED — найвищий пріоритет у системі. Дуже швидко і надійно, але дуже дорого (вдвічі дорожче, ніж SMS FULL).", + "promosmsPhoneNumber": "Номер телефону (для одержувачів з Польщі можна пропустити код регіону)", + "promosmsSMSSender": "Ім’я відправника SMS: Зареєстроване або одне з імен за замовчуванням: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookURL": "Feishu WebHookURL", + "matrixHomeserverURL": "URL сервера (разом з http(s):// і опціонально порт)", + "Internal Room Id": "Внутрішній ID кімнати", + "matrixDesc1": "Внутрішній ID кімнати можна знайти в Подробицях у параметрах каналу вашого Matrix клієнта. Він повинен виглядати приблизно як !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Рекомендується створити нового користувача і не використовувати токен доступу особистого користувача Matrix, тому що це спричиняє повний доступ до облікового запису та до кімнат, в яких ви є. Замість цього створіть нового користувача і запросіть його тільки в ту кімнату, в якій ви хочете отримувати повідомлення.Токен доступу можна отримати, виконавши команду {0}", + "Method": "Метод", + "Body": "Тіло", + "Headers": "Заголовки", + "PushUrl": "URL пуша", + "HeadersInvalidFormat": "Заголовки запиту некоректні JSON: ", + "BodyInvalidFormat": "Тіло запиту некоректне JSON: ", + "Monitor History": "Статистика", + "clearDataOlderThan": "Зберігати статистику за {0} днів.", + "PasswordsDoNotMatch": "Паролі не співпадають.", + "records": "записів", + "One record": "Один запис", + "steamApiKeyDescription": "Для моніторингу ігрового сервера Steam вам потрібен Web-API ключ Steam. Зареєструвати його можна тут: ", + "Certificate Chain": "Ланцюжок сертифікатів", + "Valid": "Дійсний", + "Hide Tags": "Приховати теги", + "Title": "Заголовок", + "Content": "Зміст", + "Post": "Опублікувати", + "Cancel": "Скасувати", + "Created": "Створено", + "Unpin": "Відкріпити", + "Show Tags": "Показати теги", + "recent": "Зараз", + "3h": "3 години", + "6h": "6 годин", + "24h": "24 години", + "1w": "1 тиждень", + "No monitors available.": "Немає доступних моніторів.", + "Add one": "Додати новий", + "Backup": "Резервна копія", + "Security": "Безпека", + "Shrink Database": "Стиснути базу даних", + "Current User": "Поточний користувач", + "About": "Про програму", + "Description": "Опис", + "Powered by": "Працює на основі скрипту від", + "Style": "Стиль", + "info": "ІНФО", + "warning": "УВАГА", + "danger": "ПОМИЛКА", + "primary": "ОСНОВНИЙ", + "light": "СВІТЛИЙ", + "dark": "ТЕМНИЙ", + "New Status Page": "Нова сторінка статусу", + "Show update if available": "Показувати доступні оновлення", + "Also check beta release": "Перевіряти оновлення для бета версій", + "Add New Status Page": "Додати сторінку статусу", + "Next": "Далі", + "Acz characters: a-z 0-9 -": "Дозволені символи: a-z 0-9 -", + "Start or end with a-z 0-9 only": "Початок та закінчення імені лише на символи: a-z 0-9", + "No consecutive dashes --": "Заборонено використовувати тире --", + "HTTP Options": "HTTP Опції", + "Authentication": "Аутентифікація", + "HTTP Basic Auth": "Базова HTTP", + "PushByTechulus": "Push by Techulus", + "clicksendsms": "ClickSend SMS", + "GoogleChat": "Google Chat (тільки Google Workspace)", + "apiCredentials": "API реквізити", + "Done": "Готово", + "Info": "Інфо", + "Steam API Key": "API-ключ Steam", + "Pick a RR-Type...": "Виберіть RR-тип…", + "Pick Accepted Status Codes...": "Виберіть прийняті коди стану…", + "Default": "За замовчуванням", + "Please input title and content": "Будь ласка, введіть назву та зміст", + "Last Updated": "Останнє Оновлення", + "Untitled Group": "Група без назви", + "Services": "Сервіси", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API Користувач (включаючи префікс webapi_)", + "serwersmsAPIPassword": "API Пароль", + "serwersmsPhoneNumber": "Номер телефону", + "serwersmsSenderName": "SMS ім’я відправника (реєстрований через портал користувача)", + "stackfield": "Stackfield", + "smtpDkimSettings": "DKIM Налаштування", + "smtpDkimDesc": "Повернутися до Nodemailer DKIM {0} для використання.", + "documentation": "документація", + "smtpDkimDomain": "Ім’я домена", + "smtpDkimKeySelector": "Ключ", + "smtpDkimPrivateKey": "Приватний ключ", + "smtpDkimHashAlgo": "Алгоритм хеша (опціонально)", + "smtpDkimheaderFieldNames": "Заголовок ключів для підпису (опціонально)", + "smtpDkimskipFields": "Заколовок ключів не для підпису (опціонально)", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "Кінцева точка API", + "alertaEnvironment": "Середовище", + "alertaApiKey": "Ключ API", + "alertaAlertState": "Стан алерту", + "alertaRecoverState": "Стан відновлення", + "deleteStatusPageMsg": "Дійсно хочете видалити цю сторінку статусів?", + "Proxies": "Проксі", + "default": "За замовчуванням", + "enabled": "Активно", + "setAsDefault": "Встановити за замовчуванням", + "deleteProxyMsg": "Ви впевнені, що хочете видалити цей проксі для всіх моніторів?", + "proxyDescription": "Щоб функціонувати, монітору потрібно призначити проксі.", + "enableProxyDescription": "Цей проксі не впливатиме на запити моніторингу, доки його не буде активовано. Ви можете контролювати тимчасове відключення проксі з усіх моніторів за статусом активації.", + "setAsDefaultProxyDescription": "Цей проксі буде ввімкнено за умовчанням для нових моніторів. Ви все одно можете вимкнути проксі окремо для кожного монітора.", + "Invalid": "Недійсний", + "AccessKeyId": "ID ключа доступу", + "SecretAccessKey": "Секретний ключ доступу", + "PhoneNumbers": "Номери телефонів", + "TemplateCode": "ШаблонКод", + "SignName": "Підпис", + "Sms template must contain parameters: ": "Шаблон смс повинен містити параметри: ", + "Bark Endpoint": "Кінцева точка Bark", + "WebHookUrl": "URL-адреса вебхука", + "SecretKey": "Секретний ключ", + "For safety, must use secret key": "Для безпеки необхідно використовувати секретний ключ", + "Device Token": "Токен пристрою", + "Platform": "Платформа", + "Huawei": "Huawei", + "High": "Високий", + "Retry": "Повтор", + "Topic": "Тема", + "WeCom Bot Key": "WeCom Bot ключ", + "Setup Proxy": "Налаштувати проксі", + "Proxy Protocol": "Протокол проксі", + "Proxy Server": "Проксі-сервер", + "Proxy server has authentication": "Проксі-сервер має аутентифікацію", + "User": "Користувач", + "Installed": "Встановлено", + "Not installed": "Не встановлено", + "Running": "Запущено", + "Not running": "Не запущено", + "Remove Token": "Видалити токен", + "Start": "Запустити", + "Stop": "Зупинити", + "Uptime Kuma": "Uptime Kuma", + "Slug": "Slug", + "Accept characters:": "Приймаються символи:", + "startOrEndWithOnly": "Починається або закінчується лише {0}", + "No consecutive dashes": "Без послідовних тире", + "The slug is already taken. Please choose another slug.": "Slug вже зайнятий. Будь ласка, виберіть інший slug.", + "No Proxy": "Без проксі", + "Page Not Found": "Сторінку не знайдено", + "Reverse Proxy": "Реверсивний проксі", + "wayToGetCloudflaredURL": "(Завантажити Cloudflare з {0})", + "cloudflareWebsite": "Вебсайт Cloudflare", + "Message:": "Повідомлення:", + "Don't know how to get the token? Please read the guide:": "Не знаєте, як отримати токен? Прочитайте посібник:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Поточне з’єднання може бути втрачено, якщо ви зараз під’єднуєтеся через Cloudflare Tunnel. Ви дійсно хочете зробити це? Для підтвердження введіть поточний пароль.", + "Other Software": "Інше програмне забезпечення", + "For example: nginx, Apache and Traefik.": "Наприклад: nginx, Apache і Traefik.", + "Please read": "Будь ласка, прочитайте", + "Subject:": "Тема:", + "Valid To:": "Дійсний до:", + "Days Remaining:": "Залишилось днів:", + "Issuer:": "Емітент:", + "Fingerprint:": "Відбиток:", + "No status pages": "Немає сторінок статусу", + "Domain Name Expiry Notification": "Сповіщення про закінчення терміну дії доменного імені", + "Proxy": "Проксі", + "Date Created": "Дата створення", + "onebotHttpAddress": "OneBot адреса HTTP", + "onebotMessageType": "OneBot тип повідомлення", + "onebotGroupMessage": "Група", + "onebotPrivateMessage": "Приватне", + "onebotUserOrGroupId": "ID групи/користувача", + "onebotSafetyTips": "Для безпеки необхідно встановити маркер доступу", + "PushDeer Key": "PushDeer ключ", + "Footer Text": "Текст нижнього колонтитула", + "Show Powered By": "Показувати платформу", + "Domain Names": "Доменні імена", + "signedInDisp": "Ви ввійшли як {0}", + "signedInDispDisabled": "Авторизація вимкнена.", + "Certificate Expiry Notification": "Сповіщення про закінчення терміну дії сертифіката", + "API Username": "Користувач API", + "API Key": "Ключ API", + "Recipient Number": "Номер одержувача", + "From Name/Number": "Від Ім’я/Номер", + "Leave blank to use a shared sender number.": "Залиште поле порожнім, щоб використовувати спільний номер відправника.", + "Octopush API Version": "Octopush API версія", + "Legacy Octopush-DM": "Застарілий Octopush-DM", + "endpoint": "кінцева точка", + "octopushAPIKey": "«Ключ API» з облікових даних HTTP API в панелі керування", + "octopushLogin": "«Ім’я користувача» з облікових даних HTTP API на панелі керування", + "promosmsLogin": "API Логін", + "promosmsPassword": "API Пароль", + "pushoversounds pushover": "Pushover (по замовчуванню)", + "pushoversounds bike": "Велосипед", + "pushoversounds bugle": "Горн", + "pushoversounds cashregister": "Касовий апарат", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Космічний", + "pushoversounds falling": "Падіння", + "pushoversounds gamelan": "Гамелан", + "pushoversounds incoming": "Вхідний", + "pushoversounds intermission": "Антракт", + "pushoversounds magic": "Магія", + "pushoversounds mechanical": "Механічний", + "pushoversounds pianobar": "Піано-бар", + "pushoversounds siren": "Сирена", + "pushoversounds spacealarm": "Космічна тривога", + "pushoversounds tugboat": "Буксирний катер", + "pushoversounds alien": "Тривога прибульців (довга)", + "pushoversounds climb": "Підйом (довгий)", + "pushoversounds persistent": "Стійкий (довгий)", + "pushoversounds echo": "Pushover ехо (довгий)", + "pushoversounds updown": "Вгору вниз (довгий)", + "pushoversounds vibrate": "Тільки вібрація", + "pushoversounds none": "Нічого (тиша)", + "pushyAPIKey": "Секретний ключ API", + "pushyToken": "Токен пристрою", + "Using a Reverse Proxy?": "Використовуєте зворотній проксі?", + "Check how to config it for WebSocket": "Перевірте, як налаштувати його для WebSocket", + "Steam Game Server": "Ігровий сервер Steam", + "Most likely causes:": "Найімовірніші причини:", + "The resource is no longer available.": "Ресурс більше не доступний.", + "There might be a typing error in the address.": "Можливо, в адресі є помилка.", + "What you can try:": "Що ви можете спробувати:", + "Retype the address.": "Повторно введіть адресу.", + "Go back to the previous page.": "Повернутися на попередню сторінку.", + "Coming Soon": "Незабаром", + "wayToGetClickSendSMSToken": "Ви можете отримати ім’я користувача API та ключ API з {here} .", + "Connection String": "Рядок підключення", + "Query": "Запит", + "settingsCertificateExpiry": "Закінчення терміну дії TLS сертифіката", + "certificationExpiryDescription": "Монітори HTTPS надсилають сповіщення, коли термін дії TLS сертифіката закінчується через:", + "ntfy Topic": "ntfy Тема", + "Domain": "Домен", + "Workstation": "Робоча станція", + "disableCloudflaredNoAuthMsg": "Ви перебуваєте в режимі без авторизації, пароль не потрібен.", + "Schedule maintenance": "Графік обслуговування", + "Affected Monitors": "Задіяні монітори", + "HomeAssistant": "Home Assistant", + "smseaglePriority": "Пріоритет повідомлення (0-9, найвищий пріоритет = 9)", + "smseagleRecipient": "Отримувач(і) (декілька отримувачів повинні бути відокремлені комами)", + "markdownSupported": "Підтримується синтаксис розмітки", + "Resend Notification if Down X times consequently": "Повторно надсилати сповіщення, якщо падіння відбулося X разів підряд", + "resendEveryXTimes": "Повторно відправляти кожні {0} разів", + "resendDisabled": "Повторне надсилання вимкнено", + "Start of maintenance": "Початок обслуговування", + "Select status pages...": "Вибери сторінку стану…", + "All Status Pages": "Всі сторінки станів", + "Passive Monitor Type": "Пасивний моніторинг", + "Specific Monitor Type": "Специфічний моніторинг", + "Monitor": "Монітор | Монітори", + "smseagle": "SMSEagle", + "smseagleEncoding": "Надсилати в Unicode (за замовчуванням=GSM-7)", + "smseagleUrl": "URL-адреса пристрою SMSEagle", + "smseagleToken": "Токен доступу API", + "smseagleRecipientType": "Тип одержувача", + "smseagleContact": "Телефонний контакт(и)", + "smseagleGroup": "Назва(и) телефонної групи", + "smseagleTo": "Телефонний номер(и)", + "Help": "Допомога", + "Game": "Гра", + "Pick Affected Monitors...": "Виберіть задіяні монітори…", + "statusMaintenance": "Обслуговування", + "Maintenance": "Обслуговування", + "General Monitor Type": "Основний моніторинг", + "error": "Помилка", + "webhookAdditionalHeadersTitle": "Додаткові заголовки", + "webhookAdditionalHeadersDesc": "Задати додаткові заголовки, що за допомогою вебхука. Кожен заголовок має бути заданий у вигляді JSON ключа/значення.", + "critical": "Критичний", + "Custom": "Нестандартний", + "successMessage": "Повідомлення про успіх", + "Customize": "Налаштувати", + "topic": "Тема", + "Body Encoding": "Кодування тіла", + "Event type:": "Тип події:", + "Event data:": "Дані подій:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Потім виберіть дію, наприклад, перемкнути сцену на червоне світло RGB.", + "backupRecommend": "Будь ласка, натомість створіть резервну копію тому або теки даних (./data/) напряму.", + "Optional": "Необов’язково", + "recurringInterval": "Інтервал", + "Recurring": "Повторюваний", + "strategyManual": "Активний/Неактивний вручну", + "telegramSendSilently": "Надіслати беззвучно", + "telegramSendSilentlyDescription": "Надсилає повідомлення беззвучно. Користувачі отримають сповіщення без звуку.", + "Trigger type:": "Тип тригера:", + "dayOfWeek": "День тижня", + "lastDay": "Останній день", + "warningTimezone": "Використовується часовий пояс сервера", + "weekdayShortMon": "Пн", + "weekdayShortTue": "Вт", + "weekdayShortWed": "Ср", + "weekdayShortThu": "Чт", + "weekdayShortFri": "Пт", + "weekdayShortSun": "Нд", + "Single Maintenance Window": "Разове технічне обслуговування", + "Maintenance Time Window of a Day": "Період доби для технічного обслуговування", + "Effective Date Range": "Діапазон дат вступу в силу (необов’язково)", + "Schedule Maintenance": "Розклад обслуговування", + "DateTime Range": "Діапазон дат і часу", + "loadingError": "Не вдалося отримати дані, спробуйте пізніше.", + "install": "Встановити", + "installing": "Встановлення", + "uninstall": "Видалити", + "API Keys": "API-ключі", + "Expiry": "Закінчення терміну дії", + "Expiry date": "Дата закінчення терміну дії", + "Don't expire": "Не прострочувати термін дії", + "Continue": "Продовжити", + "Add Another": "Додати ще", + "Key Added": "Ключ додано", + "No API Keys": "Немає API-ключів", + "apiKey-active": "Активний", + "apiKey-inactive": "Неактивний", + "Expires": "Термін дії закінчується", + "deleteAPIKeyMsg": "Ви впевнені, що хочете видалити цей API-ключ?", + "Generate": "Згенерувати", + "pagertreeIntegrationUrl": "URL-адреса інтеграції", + "pagertreeSilent": "Тихо", + "pagertreeUrgency": "Терміновість", + "pagertreeLow": "Низька", + "pagertreeMedium": "Середня", + "pagertreeHigh": "Висока", + "pagertreeCritical": "Критична", + "pagertreeResolve": "Автоматичне вирішення", + "Edit Tag": "Редагувати тег", + "Server Address": "Адреса сервера", + "Learn More": "Дізнатися більше", + "or": "або", + "uninstalling": "Видалення", + "confirmUninstallPlugin": "Ви дійсно хочете видалити цей плагін?", + "notificationRegional": "Регіональні", + "Clone Monitor": "Копія", + "Clone": "Скопіювати", + "cloneOf": "Копія {0}", + "Custom Footer": "Користувацький Footer", + "Strategy": "Стратегія", + "Free Mobile User Identifier": "ID користувача Free Mobile", + "SendKey": "Ключ відправки", + "Gateway Type": "Тип шлюзу", + "You can divide numbers with": "Числа можна ділити за допомогою", + "Bark Group": "Bark група", + "Bark Sound": "Bark звук", + "Custom CSS": "Користувацький CSS", + "promosmsAllowLongSMS": "Дозволити довгі SMS", + "Feishu WebHookUrl": "URL-адреса вебхуку Feishu", + "auto resolve": "автоматичне вирішення", + "RadiusCallingStationIdDescription": "Ідентифікатор пристрою, який викликає", + "telegramMessageThreadID": "(Необов’язково) ID теми повідомлення", + "telegramMessageThreadIDDescription": "Необов’язковий унікальний ідентифікатор цільової гілки повідомлень (теми) форуму; лише для супергруп форуму", + "backupOutdatedWarning": "Застаріло: Оскільки було додано багато функцій і ця функція резервного копіювання дещо застаріла, вона не може створити або відновити повну резервну копію.", + "dnsCacheDescription": "Це може не працювати в деяких середовищах IPv6, вимкніть це, якщо у вас виникнуть проблеми.", + "deleteMaintenanceMsg": "Ви дійсно хочете видалити це технічне обслуговування?", + "dnsPortDescription": "Порт DNS-сервера. За замовчуванням 53. Ви можете змінити порт у будь-який час.", + "recurringIntervalMessage": "Запускати раз на день | Запускати раз на {0} дні(в)", + "SMSManager API Docs": "SMSManager API документація", + "Base URL": "Базова URL-адреса", + "goAlertInfo": "GoAlert — це програма з відкритим вихідним кодом для планування викликів, автоматичної ескалації та сповіщень (наприклад, SMS або голосових дзвінків). Автоматично залучайте потрібну людину, потрібним чином і в потрібний час! {0}", + "goAlertIntegrationKeyInfo": "Отримайте універсальний ключ інтеграції API для сервісу у форматі «aaaaaaaa-bbbb-bbbb-cccc-dddd-eeeeeeeeeeee», зазвичай це значення параметра токену скопійованої URL-адреси.", + "wayToGetPagerDutyKey": "Ви можете отримати його, перейшовши до Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Тут ви можете шукати \"Events API V2\". Більше інформації {0}", + "Google Analytics ID": "ID Google Аналітики", + "apiKeyAddedMsg": "Ваш API-ключ додано. Будь ласка, запам’ятайте його, оскільки він більше не буде показаний.", + "Add API Key": "Додати API-ключ", + "apiKey-expired": "Прострочений", + "disableAPIKeyMsg": "Ви впевнені, що хочете деактивувати цей API-ключ?", + "pagertreeDoNothing": "Нічого не робити", + "wayToGetPagerTreeIntegrationURL": "Після створення інтеграції Uptime Kuma в PagerTree скопіюйте Endpoint. Дивіться повну інформацію {0}", + "Automations can optionally be triggered in Home Assistant:": "За бажанням можна активувати автоматизацію в Home Assistant:", + "dayOfMonth": "День місяця", + "telegramProtectContent": "Захист від пересилання/збереження", + "telegramProtectContentDescription": "Якщо увімкнено, повідомлення бота в Telegram будуть захищені від пересилання та збереження.", + "Notification Service": "Сервіс сповіщень", + "default: notify all devices": "за замовчуванням: сповіщати всі пристрої", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Список сервісів сповіщень можна знайти в Home Assistant в розділі \"Інструменти для розробників > Служби\", виконавши пошук за словом \"notification\" і знайшовши назву свого пристрою/телефону.", + "weekdayShortSat": "Сб", + "lastDay1": "Останній день місяця", + "lastDay2": "2-й останній день місяця", + "lastDay3": "3-й останній день місяця", + "lastDay4": "4-й останній день місяця", + "No Maintenance": "Немає технічного обслуговування", + "pauseMaintenanceMsg": "Ви впевнені, що хочете поставити на паузу?", + "maintenanceStatus-under-maintenance": "Перебуває на технічному обслуговуванні", + "maintenanceStatus-inactive": "Неактивне", + "maintenanceStatus-scheduled": "Заплановане", + "maintenanceStatus-ended": "Завершене", + "maintenanceStatus-unknown": "Невідоме", + "Display Timezone": "Часовий пояс", + "Server Timezone": "Часовий пояс сервера", + "statusPageMaintenanceEndDate": "Закінчення", + "IconUrl": "URL-адреса іконки", + "Enable DNS Cache": "(Застаріле) Увімкнути DNS-кеш для HTTP(s) моніторів", + "Enable": "Увімкнути", + "confirmDeleteTagMsg": "Ви впевнені, що хочете видалити цей тег? Монітори, пов’язані з цим тегом, не будуть видалені.", + "Guild ID": "ID гільдії", + "Free Mobile API Key": "Free Mobile API ключ", + "Enable TLS": "Увімкнути TLS", + "Proto Service Name": "Назва Proto-сервісу", + "Proto Content": "Вміст Proto", + "Proto Method": "Метод Proto", + "Economy": "Економічний", + "Lowcost": "Дешевий", + "Custom Monitor Type": "Користувацький тип монітора", + "topicExplanation": "MQTT тема для моніторингу", + "successMessageExplanation": "MQTT-повідомлення, яке буде вважатися успішним", + "HTTP Headers": "HTTP заголовки", + "Trust Proxy": "Довірений проксі", + "RadiusSecret": "Секрет Radius", + "RadiusSecretDescription": "Спільний секрет між клієнтом і сервером", + "RadiusCalledStationId": "ID станції, що викликається", + "Frontend Version": "Версія інтерфейсу", + "Frontend Version do not match backend version!": "Версія інтерфейсу не збігається з версією бекенду!", + "Number": "Номер", + "dataRetentionTimeError": "Період зберігання повинен бути 0 або більше", + "infiniteRetention": "Встановіть 0 для нескінченного зберігання.", + "affectedMonitorsDescription": "Виберіть монітори, які зачепить поточне технічне обслуговування", + "affectedStatusPages": "Показувати це повідомлення про технічне обслуговування на вибраних сторінках стану", + "atLeastOneMonitor": "Виберіть принаймні один монітор, який зазнав впливу", + "wayToGetKookBotToken": "Створіть заявку та отримайте токен бота тут {0}", + "wayToGetKookGuildID": "Увімкніть «Режим розробника» в налаштуваннях Kook і клацніть правою кнопкою миші на гільдії, щоб отримати її ID", + "Date and Time": "Дата і час", + "Integration Key": "Ключ інтеграції", + "Integration URL": "URL-адреса інтеграції", + "Auto resolve or acknowledged": "Автоматично вирішено або підтверджено", + "do nothing": "нічого не робити", + "auto acknowledged": "автоматично підтверджено", + "plugin": "Плагін | Плагіни", + "RadiusCalledStationIdDescription": "Ідентифікатор пристрою, що викликається", + "RadiusCallingStationId": "ID станції, яка викликає", + "Setup Docker Host": "Налаштування Docker-хосту", + "Connection Type": "Тип підключення", + "Docker Daemon": "Демон Docker", + "socket": "Сокет", + "tcp": "TCP / HTTP", + "Container Name / ID": "Назва / ID контейнера", + "deleteDockerHostMsg": "Ви дійсно хочете видалити цей Docker-хост для всіх моніторів?", + "Docker Container": "Docker-контейнер", + "Docker Host": "Docker-хост", + "Docker Hosts": "Docker-хости", + "wayToGetZohoCliqURL": "Ви можете дізнатися, як створити URL-адресу вебхука {0}.", + "enableGRPCTls": "Дозволити надсилати gRPC-запити з TLS-з’єднанням", + "grpcMethodDescription": "Ім’я методу перетворюється у формат camelCase, наприклад, sayHello, check тощо.", + "Packet Size": "Розмір пакету", + "trustProxyDescription": "Довіряти заголовкам «X-Forwarded-*». Якщо ви хочете отримати правильний клієнтський IP, а ваш Uptime Kuma знаходиться за проксі-сервером, таким як Nginx або Apache, вам слід увімкнути цю опцію.", + "wayToGetLineNotifyToken": "Ви можете отримати токен доступу з {0}", + "Examples": "Приклади", + "Home Assistant URL": "URL-адреса Home Assistant", + "Long-Lived Access Token": "Довготривалий токен доступу", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Довготривалий токен доступу можна створити, натиснувши на ім’я вашого профілю (внизу ліворуч), прокрутивши його донизу і натиснувши кнопку Створити токен.", + "high": "високий", + "Disable": "Вимкнути", + "Resend Notification if Down X times consecutively": "Повторно надіслати сповіщення, якщо було падіння X разів поспіль", + "lunaseaTarget": "Ціль", + "Add New Tag": "Додати новий тег", + "lunaseaDeviceID": "ID пристрою", + "lunaseaUserID": "ID користувача", + "twilioAccountSID": "SID облікового запису", + "twilioAuthToken": "Токен авторизації / Секретний ключ Api", + "twilioFromNumber": "З номера", + "twilioToNumber": "На номер", + "sameAsServerTimezone": "Такий самий, як часовий пояс сервера", + "startDateTime": "Дата і час початку", + "endDateTime": "Дата і час закінчення", + "cronExpression": "Cron-вираз", + "cronSchedule": "Розклад: ", + "invalidCronExpression": "Неправильний Cron-вираз: {0}", + "statusPageRefreshIn": "Оновлювати кожні: {0}", + "ntfyAuthenticationMethod": "Метод автентифікації", + "ntfyUsernameAndPassword": "Ім’я користувача та пароль", + "pushoverMessageTtl": "TTL повідомлення (секунди)", + "Monitor Setting": "Налаштування монітора {0}", + "Show Clickable Link": "Показувати клікабельне посилання", + "Show Clickable Link Description": "Якщо позначено, кожен, хто має доступ до цієї сторінки статусу, може мати доступ до URL-адреси моніторингу.", + "Open Badge Generator": "Відкрити генератор бейджів", + "Badge Generator": "Генератор бейджів {0}", + "Badge Type": "Тип бейджа", + "Badge Duration": "Тривалість бейджа", + "Badge Label": "Ярлик бейджа", + "Badge Prefix": "Префікс значення бейджа", + "Badge Suffix": "Суфікс значення бейджа", + "Badge Label Color": "Колір ярлика бейджа", + "Badge Color": "Колір бейджа", + "Badge Label Prefix": "Префікс ярлика бейджа", + "Badge Label Suffix": "Суфікс ярлика бейджа", + "Badge Style": "Стиль бейджа", + "Badge value (For Testing only.)": "Значення бейджа (тільки для тестування.)", + "Badge URL": "URL бейджа", + "Badge Up Color": "Колір бейджа «Доступний»", + "Badge Down Color": "Колір бейджа «Недоступний»", + "Badge Pending Color": "Колір бейджа «Очікування»", + "Badge Warn Color": "Колір бейджа «Попередження»", + "Badge Warn Days": "Бейдж «Днів попередження»", + "Badge Maintenance Color": "Колір бейджа «Обслуговування»", + "Badge Down Days": "Бейдж «Днів недоступний»", + "Group": "Група", + "Monitor Group": "Група моніторів", + "Edit Maintenance": "Редагувати обслуговування", + "Cannot connect to the socket server": "Не вдається підключитися до сервера сокетів", + "Reconnecting...": "Повторне підключення...", + "Home": "Головна", + "noGroupMonitorMsg": "Недоступно. Спочатку створіть групу моніторів.", + "Close": "Закрити", + "chromeExecutableDescription": "Для користувачів Docker, якщо Chromium ще не встановлено, встановлення та відображення результатів тесту може зайняти кілька хвилин. Потрібно 1 ГБ дискового простору.", + "chromeExecutableAutoDetect": "Автоматичне визначення", + "chromeExecutable": "Виконуваний файл Chrome/Chromium", + "Invert Keyword": "Інвертоване ключове слово", + "invertKeywordDescription": "Слідкувати за тим, щоб ключове слово було відсутнім, а не присутнім.", + "webhookCustomBodyDesc": "Задати користувацьке HTTP-тіло для запиту. Приймаються шаблонні змінні {msg}, {heartbeat}, {monitor}.", + "webhookBodyPresetOption": "Пресет — {0}", + "webhookBodyCustomOption": "Користувацьке тіло", + "Request Body": "Тіло запиту", + "Badge Preview": "Попередній перегляд бейджа", + "Badge Duration (in hours)": "Тривалість бейджа (у годинах)", + "twilioApiKey": "Api ключ (необов’язково)", + "Expected Value": "Очікуване значення", + "Json Query": "Json-запит", + "Notify Channel": "Сповіщення каналу", + "aboutNotifyChannel": "Сповіщення каналу надішле сповіщення на десктоп або мобільний для всіх учасників каналу, незалежно від того, встановлена їхня доступність як активна чи відсутня.", + "filterActive": "Активні", + "filterActivePaused": "Призупинені", + "Kafka Brokers": "Брокери Kafka", + "Press Enter to add broker": "Натисніть Enter, щоб додати брокер", + "Kafka Topic Name": "Назва теми Kafka", + "Kafka SASL Options": "Параметри Kafka SASL", + "Mechanism": "Механізм", + "Pick a SASL Mechanism...": "Виберіть механізм SASL…", + "Authorization Identity": "Ідентифікатор авторизації", + "AccessKey Id": "AccessKey Id", + "Secret AccessKey": "Secret AccessKey", + "Session Token": "Токен сесії", + "Enable Kafka SSL": "Увімкнути Kafka SSL", + "Enable Kafka Producer Auto Topic Creation": "Увімкнути автоматичне створення тем Kafka Producer", + "Enter the list of brokers": "Введіть список брокерів", + "Kafka Producer Message": "Повідомлення Kafka Producer", + "tailscalePingWarning": "Для того, щоб використовувати монітор Tailscale Ping, вам потрібно встановити Uptime Kuma без Docker, а також встановити клієнт Tailscale на вашому сервері.", + "Server URL should not contain the nfty topic": "URL-адреса сервера не повинна містити тему nfty", + "FlashDuty Severity": "Серйозність", + "nostrRelays": "Реле Nostr", + "nostrRelaysHelp": "Одна URL-адреса реле в рядку", + "nostrSender": "Приватний ключ відправника (nsec)", + "nostrRecipients": "Відкриті ключі одержувачів (npub)", + "showCertificateExpiry": "Показати термін дії сертифікату", + "noOrBadCertificate": "Відсутність/поганий сертифікат", + "Select": "Вибрати", + "selectedMonitorCount": "Вибрано: {0}", + "wayToGetFlashDutyKey": "Щоб інтегрувати Uptime Kuma з Flashduty: Перейдіть до Канали > Виберіть канал > Інтеграції > Додати нову інтеграцію, виберіть Uptime Kuma і скопіюйте URL-адресу пуша.", + "nostrRecipientsHelp": "Формат npub, по одному в рядку", + "Check/Uncheck": "Встановити/зняти галочку", + "PushDeer Server": "Сервер PushDeer", + "pushDeerServerDescription": "Залиште порожнім, щоб використовувати офіційний сервер", + "Request Timeout": "Таймаут запиту", + "timeoutAfter": "Таймаут через {0} секунд", + "styleElapsedTime": "Час, що минув під індикатором опитування", + "gamedigGuessPort": "Gamedig: Вгадати порт", + "gamedigGuessPortDescription": "Порт, що використовується протоколом запитів до сервера Valve, може відрізнятися від порту клієнта. Спробуйте це, якщо монітор не може підключитися до вашого сервера.", + "styleElapsedTimeShowWithLine": "Показати (з лінією)", + "styleElapsedTimeShowNoLine": "Показати (без лінії)", + "enableNSCD": "Увімкнути NSCD (Name Service Cache Daemon) для кешування всіх DNS-запитів", + "setupDatabaseChooseDatabase": "Яку базу даних ви бажаєте використовувати?", + "setupDatabaseEmbeddedMariaDB": "Вам не потрібно нічого налаштовувати. Цей докер-образ містить вбудовану та автоматично налаштовану базу даних MariaDB. Uptime Kuma буде з’єднуватися з базою даних через unix-сокет.", + "setupDatabaseSQLite": "Простий файл бази даних, рекомендований для невеликих проєктів. До версії 2.0.0 Uptime Kuma використовувала SQLite як базу даних за замовчуванням.", + "setupDatabaseMariaDB": "Підключитися до зовнішньої бази даних MariaDB. Вам потрібно задати інформацію для підключення до бази даних.", + "dbName": "Назва бази даних", + "Saved.": "Збережено.", + "monitorToastMessagesLabel": "Тост-сповіщення монітора", + "toastErrorTimeout": "Таймаут для сповіщень про помилки", + "toastSuccessTimeout": "Таймаут для сповіщень про успіх", + "monitorToastMessagesDescription": "Тост-сповіщення для моніторів зникають через заданий час у секундах. Значення -1 вимикає таймаут. Значення 0 вимикає тост-сповіщення.", + "Bark API Version": "Версія Bark API", + "pushViewCode": "Як користуватися Push-монітором? (Переглянути код)", + "pushOthers": "Інші", + "programmingLanguages": "Мови програмування", + "authInvalidToken": "Недійсний токен.", + "2faAlreadyEnabled": "2FA вже увімкнено.", + "2faEnabled": "2FA увімкнено.", + "2faDisabled": "2FA вимкнено.", + "successAdded": "Успішно додано.", + "successResumed": "Успішно відновлено.", + "successPaused": "Успішно зупинено.", + "successDeleted": "Успішно видалено.", + "successAuthChangePassword": "Пароль успішно оновлено.", + "successBackupRestored": "Резервну копію успішно відновлено.", + "successDisabled": "Успішно вимкнено.", + "tagNotFound": "Тег не знайдено.", + "foundChromiumVersion": "Знайдено Chromium/Chrome. Версія: {0}", + "authUserInactiveOrDeleted": "Користувач неактивний або видалений.", + "authIncorrectCreds": "Неправильне ім’я користувача або пароль.", + "successEdited": "Успішно відредаговано.", + "successEnabled": "Успішно увімкнено.", + "Reset Token": "Скинути токен", + "emailCustomisableContent": "Налаштовуваний вміст", + "leave blank for default subject": "залиште порожнім для теми за замовчуванням", + "emailCustomBody": "Користувацьке тіло", + "smtpLiquidIntroduction": "Наступні два поля можна шаблонувати за допомогою мови шаблонів Liquid. Будь ласка, зверніться до {0} для отримання інструкцій з використання. Ось доступні змінні:", + "templateHeartbeatJSON": "об’єкт, що описує опитування", + "liquidIntroduction": "Шаблонність досягається за допомогою мови шаблонів Liquid. Будь ласка, зверніться до {0} для отримання інструкцій з використання. Ось доступні змінні:", + "templateMsg": "повідомлення сповіщення", + "templateMonitorJSON": "об’єкт, що описує монітор", + "templateLimitedToUpDownCertNotifications": "доступно лише для сповіщень Доступний/Недоступний/Закінчення терміну дії сертифіката", + "templateLimitedToUpDownNotifications": "доступно лише для сповіщень Доступний/Недоступний", + "leave blank for default body": "залиште порожнім для тіла за замовчуванням", + "emailTemplateServiceName": "Назва сервісу", + "emailTemplateHostnameOrURL": "Ім'я хоста або URL", + "emailTemplateStatus": "Статус", + "emailTemplateMsg": "повідомлення сповіщення", + "emailTemplateLimitedToUpDownNotification": "доступно лише для опитування Доступний/Недоступний, інакше — нульове значення", + "emailTemplateMonitorJSON": "об’єкт, що описує монітор", + "emailTemplateHeartbeatJSON": "об’єкт, що описує опитування", + "GrafanaOncallUrl": "Grafana Oncall URL", + "noDockerHostMsg": "Недоступно. Спочатку налаштуйте Docker-хост.", + "DockerHostRequired": "Будь ласка, встановіть Docker-хост для цього монітора.", + "Browser Screenshot": "Скріншот браузера", + "Add a new expiry notification day": "Додати новий день сповіщення про закінчення терміну дії", + "setup a new monitor group": "створити нову групу моніторів", + "Add a domain": "Додати домен", + "Remote Browsers": "Віддалені браузери", + "Remote Browser": "Віддалений браузер", + "Add a Remote Browser": "Додати віддалений браузер", + "Remote Browser not found!": "Віддалений браузер не знайдено!", + "remoteBrowsersDescription": "Віддалені браузери — це альтернатива локальному запуску Chromium. Налаштуйте за допомогою сервісу на кшталт browserless.io або підключіть свій власний", + "remoteBrowserToggle": "За замовчуванням Chromium запускається всередині контейнера Uptime Kuma. Ви можете використовувати віддалений браузер, увімкнувши цей перемикач.", + "useRemoteBrowser": "Використовувати віддалений браузер", + "deleteRemoteBrowserMessage": "Ви дійсно хочете видалити цей віддалений браузер для всіх моніторів?", + "self-hosted container": "контейнер, що хоститься самостійно", + "Remove the expiry notification": "Видалити день сповіщення про закінчення терміну дії", + "Remove domain": "Видалити домен «{0}»", + "successKeyword": "Ключове слово успіху", + "successKeywordExplanation": "Ключове слово MQTT, яке вважатиметься успіхом", + "openModalTo": "відкрити модальне вікно для {0}", + "ntfyPriorityHelptextAllExceptDown": "З цим пріоритетом надсилаються всі події, окрім {0}-подій, які мають пріоритет {1}", + "settingUpDatabaseMSG": "Налаштування бази даних. Це може зайняти деякий час, будь ласка, будьте терплячі.", + "statusPageSpecialSlugDesc": "Спеціальний slug {0}: ця сторінка буде показана, якщо slug не надано", + "Search monitored sites": "Пошук сайтів, що відстежуються", + "ntfyPriorityHelptextAllEvents": "Усі події надсилаються з максимальним пріоритетом", + "What is a Remote Browser?": "Що таке віддалений браузер?", + "Your User ID": "Ваш ID користувача", + "Channel access token (Long-lived)": "Токен доступу до каналу (довготривалий)", + "documentationOf": "{0} Документація", + "wayToGetHeiiOnCallDetails": "Як отримати Trigger ID та API-ключі пояснюється в {documentation}", + "To Phone Number": "На номер телефону", + "gtxMessagingToHint": "Міжнародний формат, з «+» на початку ({e164}, {e212} або {e214})", + "gtxMessagingApiKeyHint": "Ви можете знайти свій API-ключ тут: API-ключ: Мої облікові записи маршрутизації > Показати інформацію про обліковий запис > Облікові дані API > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "З номера телефону / вихідної адреси маршруту передачі (TPOA)", + "gtxMessagingFromHint": "На мобільних телефонах ваші одержувачі бачать TPOA як відправника повідомлення. Допускається до 11 буквено-цифрових символів, короткий код, місцевий довгий код або міжнародні номери ({e164}, {e212} або {e214})", + "Originator type": "Тип відправника", + "Alphanumeric (recommended)": "Буквено-цифровий (рекомендовано)", + "Telephone number": "Номер телефону", + "Originator": "Відправник", + "cellsyntOriginator": "Відображається на мобільному телефоні одержувача як відправник повідомлення. Допустимі значення та функції залежать від параметра originatortype.", + "Destination": "Пункт призначення", + "callMeBotGet": "Тут ви можете згенерувати кінцеву точку для {0}, {1} і {2}. Майте на увазі, що ви можете бути обмежені у швидкості. Обмеження швидкості виглядає наступним чином: {3}", + "cellsyntOriginatortypeAlphanumeric": "Рядок буквено-цифровий (максимум 11 буквено-цифрових символів). Отримувачі не можуть відповісти на повідомлення.", + "cellsyntOriginatortypeNumeric": "Числове значення (максимум 15 цифр) з номером телефону в міжнародному форматі без 00 на початку (наприклад, британський номер 07920 110 000 має бути вказаний як 447920110000). Одержувачі можуть відповісти на повідомлення.", + "Allow Long SMS": "Дозволити довгі СМС", + "wayToGetWhapiUrlAndToken": "Можна отримати URL API та токен, зайдіть у бажаний канал з {0}", + "whapiRecipient": "Номер телефону / ID контакту / ID групи", + "API URL": "API URL", + "cellsyntSplitLongMessages": "Розбивати довгі повідомлення на 6 частин. 153 x 6 = 918 символів.", + "max 15 digits": "максимум 15 символів", + "wayToWriteWhapiRecipient": "Телефонний номер з міжнародним префіксом, але без плюсового знаку на початку ({0}), ID контакту ({1}) або ID групи ({2}).", + "cellsyntDestination": "Номер телефону одержувача в міжнародному форматі з 00 на початку, за яким слідує код країни, наприклад, 00447920110000 для британського номера 07920 110 000 (максимум 17 цифр). Максимум 25000 одержувачів, розділених комами, на один HTTP-запит.", + "max 11 alphanumeric characters": "максимум 11 буквено-цифрових символів", + "locally configured mail transfer agent": "локально налаштований агент пересилання пошти", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Або введіть ім’я хосту сервера, до якого ви хочете підключитися, або {localhost}, якщо ви маєте намір використовувати {local_mta}", + "Don't mention people": "Не згадувати людей", + "Mentioning": "Згадування", + "Mention group": "Згадати {group}", + "senderSevenIO": "Номер або ім’я відправника", + "receiverSevenIO": "Номер одержувача", + "apiKeySevenIO": "API-ключ SevenIO", + "wayToGetSevenIOApiKey": "Відвідайте інформаційну панель у розділі app.seven.io > developer > api key > зелена кнопка додавання", + "receiverInfoSevenIO": "Якщо номер одержувача знаходиться не в Німеччині, ви повинні додати код країни перед номером (наприклад, для коду країни 1 з США використовуйте 117612121212 замість 017612121212)", + "Host URL": "URL-адреса хосту", + "threadForumPostID": "ID теми/повідомлення на форумі", + "whatHappensAtForumPost": "Створити нове повідомлення на форумі. Це НЕ призводить до публікації повідомлень в існуючих темах. Щоб написати повідомлення в існуючому дописі, використовуйте «{option}»", + "wayToGetDiscordThreadId": "Отримання id теми/допису на форумі подібне до отримання id каналу. Дізнайтеся більше про те, як отримати ідентифікатори {0}", + "Command": "Команда", + "mongodbCommandDescription": "Виконайте MongoDB-команду до бази даних. Для отримання інформації про доступні команди зверніться до {documentation}", + "Bitrix24 Webhook URL": "URL-адреса вебхука Бітрікс24", + "wayToGetBitrix24Webhook": "Ви можете створити вебхук, виконавши кроки за адресою {0}", + "bitrix24SupportUserID": "Введіть свій ID користувача в Бітрікс24. Дізнатися ID можна за посиланням, перейшовши в профіль користувача.", + "Select message type": "Вибрати тип повідомлення", + "Send to channel": "Надіслати в канал", + "Create new forum post": "Створити нове повідомлення на форумі", + "postToExistingThread": "Написати до існуючої теми/повідомлення на форумі", + "Refresh Interval": "Інтервал оновлення", + "Refresh Interval Description": "Сторінка стану буде робити повне оновлення сайту кожні {0} секунд", + "ignoreTLSErrorGeneral": "Ігнорувати помилку TLS/SSL для з’єднання", + "forumPostName": "Назва повідомлення на форумі", + "e.g. {discordThreadID}": "наприклад, {discordThreadID}", + "smspartnerPhoneNumber": "Телефонний номер(и)", + "smspartnerSenderNameInfo": "Має містити в межах 3..=11 звичайних символів", + "smspartnerSenderName": "Ім’я відправника SMS", + "smspartnerApiurl": "Ви можете знайти свій API-ключ у своїй інформаційній панелі за адресою {0}", + "smspartnerPhoneNumberHelptext": "Номер повинен бути в міжнародному форматі {0}, {1}. Кілька номерів повинні бути розділені символом {2}", + "threemaRecipient": "Одержувач", + "threemaRecipientType": "Тип одержувача", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypePhone": "Номер телефону", + "threemaRecipientTypePhoneFormat": "E.164, без + на початку", + "threemaRecipientTypeEmail": "Електронна адреса", + "threemaSenderIdentity": "ID шлюзу", + "threemaApiAuthenticationSecret": "Секрет ID шлюзу", + "wayToGetThreemaGateway": "Ви можете зареєструватися на Threema Gateway {0}.", + "threemaRecipientTypeIdentityFormat": "8 символів", + "threemaSenderIdentityFormat": "8 символів, зазвичай починається з *", + "threemaBasicModeInfo": "Примітка: Ця інтеграція використовує Threema Gateway у базовому режимі (шифрування на основі сервера). Більш детальну інформацію можна знайти {0}.", + "apiKeysDisabledMsg": "API-ключі вимкнено, оскільки автентифікація вимкнена.", + "jsonQueryDescription": "Розбір і вилучення конкретних даних з JSON-відповіді сервера за допомогою JSON-запиту або використання символу «$» для необробленої відповіді, якщо не очікується JSON. Результат порівнюється з очікуваним значенням у вигляді рядків. Дивіться {0} для отримання документації і використовуйте {1} для експериментів із запитами.", + "snmpCommunityStringHelptext": "Цей рядок слугує паролем для автентифікації та контролю доступу до пристроїв з підтримкою SNMP. Узгодьте його з конфігурацією вашого SNMP-пристрою.", + "snmpOIDHelptext": "Введіть OID для датчика або стану, який ви хочете моніторити. Якщо ви не впевнені щодо OID, скористайтеся інструментами керування мережею, як-от MIB-браузерами або програмним забезпеченням SNMP.", + "wayToGetOnesenderUrlandToken": "Ви можете отримати URL-адресу і токен, перейшовши на сайт Onesender. Більше інформації {0}", + "conditionValuePlaceholder": "Значення", + "not equals": "не дорівнює", + "not ends with": "не закінчується на", + "and": "і", + "Private Number": "Приватний номер", + "signl4Docs": "Ви можете знайти більше інформації про те, як налаштувати SIGNL4 і як отримати URL-адресу вебхука SIGNL4 в розділі {0}.", + "now": "зараз", + "time ago": "{0} тому", + "-year": "-рік", + "Json Query Expression": "Вираз запиту Json", + "cacheBusterParam": "Додати параметр {0}", + "cacheBusterParamDescription": "Випадково згенерований параметр для обходу кешування.", + "OID (Object Identifier)": "OID (ідентифікатор об’єкта)", + "Condition": "Умова", + "SNMP Version": "Версія SNMP", + "Please enter a valid OID.": "Будь ласка, введіть дійсний OID.", + "privateOnesenderDesc": "Переконайтеся, що номер телефону дійсний. Щоб надіслати повідомлення на приватний номер телефону, наприклад: 628123456789", + "groupOnesenderDesc": "Переконайтеся, що GroupID дійсний. Щоб надіслати повідомлення в Групу, наприклад: 628123456789-342345", + "Add Remote Browser": "Додати віддалений браузер", + "New Group": "Нова група", + "Group Name": "Назва групи", + "Group ID": "ID групи", + "OAuth2: Client Credentials": "OAuth2: Облікові дані клієнта", + "Authentication Method": "Метод автентифікації", + "Authorization Header": "Заголовок авторизації", + "OAuth Token URL": "URL-адреса токену OAuth", + "Community String": "Рядок спільноти", + "Host Onesender": "Хост Onesender", + "Token Onesender": "Токен Onesender", + "Recipient Type": "Тип отримувача", + "Form Data Body": "Тіло даних форми", + "Client ID": "ID клієнта", + "Client Secret": "Секрет клієнта", + "Go back to home page.": "Повернутися на головну сторінку.", + "No tags found.": "Тегів не знайдено.", + "Lost connection to the socket server.": "Втрачено з’єднання з сервером сокетів.", + "Cannot connect to the socket server.": "Не вдається з’єднатися з сервером сокетів.", + "Conditions": "Умови", + "conditionAdd": "Додати умову", + "conditionDelete": "Видалити умову", + "conditionAddGroup": "Додати групу", + "conditionDeleteGroup": "Видалити групу", + "OAuth Scope": "OAuth дозволи", + "Optional: Space separated list of scopes": "Необов’язково: Розділений пробілами список дозволів", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "URL-адреса вебхука SIGNL4", + "equals": "дорівнює", + "contains": "містить", + "not contains": "не містить", + "starts with": "починається з", + "not starts with": "не починається з", + "ends with": "закінчується на", + "less than": "менше, ніж", + "greater than": "більше, ніж", + "less than or equal to": "менше або дорівнює", + "greater than or equal to": "більше або дорівнює", + "record": "запис", + "shrinkDatabaseDescriptionSqlite": "Запускає команду {vacuum} для бази даних SQLite. Команда {auto_vacuum} вже увімкнена, але вона не дефрагментує базу даних і не перепаковує окремі сторінки бази даних так, як це робить команда {vacuum}.", + "ignoredTLSError": "Помилки TLS/SSL проігноровано", + "Debug": "Налагодження", + "Copy": "Копіювати", + "CopyToClipboardError": "Не вдалося скопіювати в буфер обміну: {error}", + "CopyToClipboardSuccess": "Скопійовано!", + "firewalls": "фаєрволи", + "dns resolvers": "dns сервери", + "docker networks": "docker-мережі", + "CurlDebugInfoProxiesUnsupported": "Підтримка проксі у наведеній вище команді {curl} наразі не реалізована.", + "Message format": "Формат повідомлення", + "Send rich messages": "Надіслати розгорнуті повідомлення", + "Notification Channel": "Канал сповіщення", + "Sound": "Звук", + "Alphanumerical string and hyphens only": "Тільки алфавітно-цифровий рядок і дефіси", + "Arcade": "Аркада", + "Correct": "Вірно", + "Fail": "Невдача", + "Harp": "Арфа", + "Reveal": "Розкриття", + "Bubble": "Бульбашка", + "Doorbell": "Дзвінок", + "Flute": "Флейта", + "Money": "Гроші", + "Scifi": "Наукова фантастика", + "Clear": "Чисто", + "Elevator": "Ліфт", + "Guitar": "Гітара", + "Pop": "Поп", + "Custom sound to override default notification sound": "Користувацький звук для заміни звуку сповіщень за замовчуванням", + "Time Sensitive (iOS Only)": "Чутливий до часу (лише для iOS)", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Сповіщення, що залежать від часу, будуть доставлені негайно, навіть якщо пристрій перебуває в режимі «Не турбувати».", + "From": "Від", + "The phone number of the recipient in E.164 format.": "Номер телефону одержувача у форматі E.164.", + "CurlDebugInfo": "Для налагодження монітора ви можете вставити цей файл у термінал вашої власної машини або у термінал машини, на якій запущено uptime kuma, і подивитися, що ви запитуєте.{newline}Зверніть увагу на мережеві відмінності, такі як {firewalls}, {dns_resolvers} або {docker_networks}.", + "CurlDebugInfoOAuth2CCUnsupported": "Повний потік облікових даних клієнта oauth не підтримується в {curl}.{newline}Потрібно отримати bearer-токен і передати його за допомогою опції {oauth2_bearer}.", + "Can be found on:": "Можна знайти на: {0}", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Або ідентифікатор відправника тексту, або номер телефону у форматі E.164, якщо ви хочете отримувати відповіді.", + "rabbitmqNodesRequired": "Будь ласка, встановіть вузли для цього монітора.", + "RabbitMQ Username": "Ім’я користувача RabbitMQ", + "RabbitMQ Password": "Пароль RabbitMQ", + "SendGrid API Key": "API-ключ SendGrid", + "Separate multiple email addresses with commas": "Розділяйте кілька адрес комами", + "RabbitMQ Nodes": "Вузли керування RabbitMQ", + "rabbitmqNodesDescription": "Введіть URL-адресу для вузлів керування RabbitMQ, включаючи протокол і порт. Приклад: {0}", + "rabbitmqNodesInvalid": "Будь ласка, використовуйте повну URL-адресу (починаючи з «http») для вузлів RabbitMQ.", + "rabbitmqHelpText": "Щоб використовувати монітор, вам потрібно увімкнути плагін керування у налаштуваннях RabbitMQ. Для отримання додаткової інформації, будь ласка, зверніться до {rabitmq_documentation}.", + "aboutSlackUsername": "Змінює відображуване ім’я відправника повідомлення. Якщо ви хочете згадати когось, додайте його до дружнього імені.", + "templateServiceName": "назва сервісу", + "templateHostnameOrURL": "ім’я хосту або URL", + "telegramUseTemplate": "Використовувати власний шаблон повідомлення", + "telegramUseTemplateDescription": "Якщо увімкнено, повідомлення буде надіслано з використанням спеціального шаблону.", + "telegramTemplateFormatDescription": "Telegram дозволяє використовувати різні мови розмітки для повідомлень, див. Telegram {0} для більш детальної інформації.", + "Plain Text": "Звичайний текст", + "templateStatus": "статус", + "Message Template": "Шаблон повідомлення", + "Template Format": "Формат шаблону", + "YZJ Webhook URL": "URL вебхука YZJ", + "YZJ Robot Token": "Токен YZJ Robot", + "wayToGetWahaApiUrl": "URL вашого екземпляра WAHA.", + "wayToGetWahaApiKey": "Ключ API — це значення змінної оточення WHATSAPP_API_KEY, яку ви використовували для запуску WAHA.", + "wahaSession": "Сесія", + "wahaChatId": "ID чату (номер телефону / ID контакту / ID групи)", + "wayToGetWahaSession": "З цієї сесії WAHA надсилає сповіщення на ID чату. Ви можете знайти його в інформаційній панелі WAHA.", + "wayToWriteWahaChatId": "Номер телефону з міжнародним префіксом, але без знака плюс на початку ({0}), ID контакту ({1}) або ID групи ({2}). На цей ID чату надсилаються сповіщення з сеансу WAHA.", + "telegramServerUrl": "(Необов’язково) URL сервера", + "telegramServerUrlDescription": "Щоб зняти обмеження Telegram Bot API або отримати доступ у регіонах, де сервіс заблоковано (Китай, Іран тощо). Для отримання додаткової інформації натисніть {0}. За замовчуванням: {1}", + "Font Twemoji by Twitter licensed under": "Шрифт Twemoji від Twitter ліцензований під", + "the smsplanet documentation": "документації smsplanet", + "Phone numbers": "Номери телефонів", + "Sender name": "Ім’я відправника", + "smsplanetNeedToApproveName": "Потребує схвалення в клієнтській панелі", + "smsplanetApiToken": "Токен для API SMSPlanet", + "smsplanetApiDocs": "Детальну інформацію про отримання токенів API можна знайти в {the_smsplanet_documentation}.", + "defaultFriendlyName": "Новий монітор", + "Add Tags": "Додати теги", + "tagNameExists": "Системний тег з такою назвою вже існує. Виберіть його зі списку або використовуйте іншу назву.", + "tagAlreadyOnMonitor": "Цей тег (назва та значення) вже є в моніторі або очікує на додавання.", + "smseagleMsgType": "Тип повідомлення", + "smseagleMsgTtsAdvanced": "Розширений текстовий дзвінок", + "smseagleApiv1": "APIv1 (для існуючих проєктів та зворотної сумісності)", + "smseagleComma": "Декілька значень повинні бути відокремлені комою", + "FlashDuty Push URL": "URL пуша", + "pingCountDescription": "Кількість пакетів для відправлення перед зупинкою", + "pingNumericDescription": "Якщо позначено, замість символічних імен хостів будуть виводитися IP-адреси", + "pingPerRequestTimeoutDescription": "Це максимальний час очікування (у секундах) перед тим, як вважати один пінг-пакет втраченим", + "smtpHelpText": "«SMTPS» перевіряє, чи працює SMTP/TLS; «Ігнорувати TLS» з’єднується через відкритий текст; «STARTTLS» з’єднується, видає команду STARTTLS і перевіряє сертифікат сервера. Жоден з них не надсилає електронного листа.", + "Staged Tags for Batch Add": "Покрокові теги для групового додавання", + "ntfyPriorityHelptextPriorityHigherThanDown": "Звичайний пріоритет має бути вищим за пріоритет {0}. Пріоритет {1} вищий за {0} пріоритет {2}", + "ntfyPriorityDown": "Пріоритет для подій НЕДОСТУПНИЙ", + "pingCountLabel": "Максимум пакетів", + "SpugPush Template Code": "Код шаблону", + "tagAlreadyStaged": "Цей тег (назва та значення) вже створено для цієї групи.", + "Use HTML for custom E-mail body": "Використовувати HTML для користувацького тіла електронного листа", + "smseagleGroupV2": "ID телефонної групи", + "smseagleContactV2": "ID телефонного контакту", + "smseagleMsgSms": "Sms повідомлення (за замовчуванням)", + "smseagleMsgRing": "Дзвінок", + "smseagleMsgTts": "Текстовий дзвінок", + "smseagleDuration": "Тривалість (в секундах)", + "smseagleTtsModel": "ID моделі перетворення тексту в мовлення", + "smseagleApiType": "Версія API", + "smseagleApiv2": "APIv2 (рекомендовано для нових інтеграцій)", + "smseagleDocs": "Перевірте наявність документації або APIv2: {0}", + "FlashDuty Push URL Placeholder": "Скопіюйте зі сторінки інтеграції сповіщень", + "pingNumericLabel": "Числовий вивід", + "pingGlobalTimeoutLabel": "Глобальний таймаут", + "pingGlobalTimeoutDescription": "Загальний час у секундах до зупинки пінгу, незалежно від надісланих пакетів", + "pingPerRequestTimeoutLabel": "Таймаут кожного пінгу", + "pingIntervalAdjustedInfo": "Інтервал налаштовується на основі кількості пакетів, глобального таймауту та таймауту кожного пінгу", + "Custom URL": "Власна URL-адреса", + "customUrlDescription": "Буде використано як URL-адресу для кліку замість URL-адреси монітора.", + "OneChatAccessToken": "Токен доступу OneChat", + "OneChatUserIdOrGroupId": "ID користувача або ID групи OneChat", + "OneChatBotId": "ID бота OneChat", + "Disable URL in Notification": "Вимкнути URL-адресу в сповіщенні", + "Add Another Tag": "Додати ще один тег", + "Clear Form": "Очистити форму", + "pause": "Пауза", + "Happy Eyeballs algorithm": "Алгоритм Happy Eyeballs", + "Manual": "Інструкція", + "Ip Family": "Сімейство IP", + "ipFamilyDescriptionAutoSelect": "Використовує {happyEyeballs} для визначення сімейства IP.", + "OAuth Audience": "Аудиторія OAuth", + "Optional: The audience to request the JWT for": "Необов’язково: Аудиторія, для якої необхідно подати запит на JWT", + "mqttWebsocketPathExplanation": "Шлях WebSocket для з’єднань MQTT через WebSocket (наприклад, /mqtt)", + "Path": "Шлях", + "mqttWebSocketPath": "Шлях до MQTT WebSocket", + "mqttWebsocketPathInvalid": "Будь ласка, використовуйте дійсний формат шляху WebSocket", + "mqttHostnameTip": "Будь ласка, використовуйте цей формат {hostnameFormat}", + "Template plain text instead of using cards": "Шаблон простого тексту замість використання карток", + "issueWithGoogleChatOnAndroidHelptext": "Це також дозволяє обійти помилки на кшталт {issuetackerURL}", + "Clear All Events": "Очистити всі події", + "Events cleared successfully": "Події успішно очищено.", + "No monitors found": "Моніторів не знайдено.", + "Could not clear events": "Не вдалося очистити події {failed}/{total}", + "clearAllEventsMsg": "Ви впевнені, що хочете видалити всі події для всіх моніторів? Це призведе до остаточного видалення відповідних даних повідомлень, і цю дію неможливо скасувати.", + "year": "рік | роки", + "HeadersInvalidFormatBecause": "Заголовки запиту не є дійсним JSON, оскільки {error}", + "wsSubprotocolDescription": "Введіть списов підпротоколів, розділенний комами. Дивіться більш детальну інформацію про підпротоколи в {documentation}", + "wsCodeDescription": "Для отримання додаткової інформації про коди стану, будь ласка, зверніться до {rfc6455}", + "Subprotocol(s)": "Підпротокол(и)", + "ignoreSecWebsocketAcceptHeaderDescription": "Позволяє серверу не відповідати заголовком Sec-WebSocket-Accept, якщо оновлення вебсокета пройшло успішно.", + "Ignore Sec-WebSocket-Accept header": "Ігнорувати {0} заголовок", + "oracledbConnectionString": "База даних Oracle: {connectionString}", + "enableSSL": "Увімкнути SSL/TLS", + "matrixUseTemplateDescription": "Якщо ця опція увімкнена, повідомлення буде надіслано за допомогою власного шаблону.", + "Analytics Type": "Тип аналітики", + "Analytics ID": "Ідентифікатор аналітики", + "mariadbCaCertificateHelptext": "Вставте сертифікат ЦС у форматі PEM для використання з самопідписаними сертифікатами. Залиште поле порожнім, якщо ваша база даних використовує сертифікат, підписаний загальнодоступним ЦС.", + "years": "{n} рік | {n} років", + "Pin this incident": "Закріпити цей інцидент", + "responseMaxLength": "Максимальна довжина відповіді (у байтах)", + "responseMaxLengthDescription": "Максимальний розмір даних відповіді, що зберігаються. Встановіть значення 0 для необмеженого розміру. Відповіді, що перевищують цей розмір, будуть обрізані. За замовчуванням: 1024 (1 КБ)", + "Incident title": "Заголовок інциденту", + "invalidHostnameOrIP": "Неправильне ім’я хоста або IP-адреса. Ім’я хоста має бути дійсним повним доменним іменем (FQDN). Не можна використовувати символи-замінники. Допускається використання символу підкреслення або закінчення крапкою.", + "invalidDNSHostname": "Неправильне ім’я хоста. Ім’я хоста має бути дійсним повним доменним іменем (FQDN). Воно може містити символи-замінники, підкреслення або закінчуватися крапкою.", + "wildcardOnlyForDNS": "Імена хостів із символами-замінниками підтримуються лише для моніторів DNS.", + "Resolve": "Вирішити", + "Resolved": "Вирішено", + "deselectAllMonitorsAria": "Скасувати вибір усіх моніторів", + "deleteIncidentMsg": "Ви впевнені, що хочете видалити цей випадок?", + "pingPacketSizeWarning": "У Linux для пакетів розміром менше 16 не обчислюється час проходження в обидва боки", + "maxTemplateFormatDescription": "Месенджер MAX підтримує звичайний текст, форматування Markdown та HTML.", + "ariaCloneMaintenance": "Створити копію цього графіка технічного обслуговування", + "ariaEditMaintenance": "Редагувати цей графік технічного обслуговування", + "ariaDeleteMaintenance": "Видалити цей графік технічного обслуговування", + "notificationChatPlatforms": "Платформи для чату", + "Peer ID": "Ідентифікатор користувача", + "vkApiVersionDescription": "Версія API VK, яка використовується для запитів. Залиште значення за замовчуванням, якщо вам не потрібна інша версія API VK з міркувань сумісності.", + "vkDontParseLinks": "Вимкнути фрагменти посилань", + "vkDontParseLinksDescription": "Якщо ця опція увімкнена, VK не створюватиме попередній перегляд посилань та фрагменти тексту.", + "checkPriceAt": "Перегляньте ціни на «{service}» на сайті {url}", + "Enter a list of userId": "Введіть список ідентифікаторів користувачів", + "Enter a list of mobile": "Введіть список мобільних номерів", + "Invalid mobile": "Неправильний номер мобільного телефону [{mobile}]", + "Invalid userId": "Неправильний ідентифікатор користувача [{userId}]", + "slackUseTemplateDescription": "Якщо ця опція увімкнена, повідомлення буде надіслано з використанням власного шаблону. Ви можете скористатися мовою шаблонів Liquid, щоб включити інформацію про групу моніторів за допомогою monitorJSON.path або monitorJSON.pathName.", + "360messengerSelectedGroupID": "Вибрані ідентифікатори груп", + "discordUseMessageTemplateDescription": "Якщо ця опція увімкнена, повідомлення буде надіслано за допомогою власного шаблону (LiquidJS). Залиште поле порожнім, щоб використовувати стандартний формат Uptime Kuma.", + "fluxerMessageFormatMinimalist": "Мінімалістичний (короткий статус)", + "GlobalpingHostname": "Об’єкт вимірювання, доступний з мережі. Зазвичай це ім’я хоста або адреса IPv4/IPv6, залежно від типу вимірювання.", + "GlobalpingLocationDescription": "У полі «Місцезнаходження» можна вказати континенти, країни, регіони, міста, ASN, інтернет-провайдерів або хмарні регіони. Фільтри можна комбінувати з {plus} (наприклад, {amazonPlusGermany} або {comcastPlusCalifornia}). Якщо затримка є важливим показником, скористайтеся фільтрами, щоб звузити місцезнаходження до невеликого регіону, щоб уникнути стрибків, а для кращої стабільності встановіть фільтр «{datacenter}». {fullDocs}.", + "Webpush Helptext": "Вебпуш-повідомлення працюють лише через SSL-з’єднання (HTTPS). Для пристроїв iOS вебсторінку потрібно заздалегідь додати на головний екран.", + "labelDomainNameExpiryNotification": "Повідомлення про закінчення терміну дії доменного імені", + "Halo PSA Webhook URL": "Привіт, URL-адреса вебхука PSA", + "Show this Maintenance Message on which Status Pages": "На яких сторінках стану відображати це повідомлення про технічне обслуговування", + "Teltonika SMS Gateway": "SMS-шлюз Teltonika", + "teltonikaUrlHelptext": "URL-адресу слід вказувати у повному вигляді, наприклад {0} або {1}.", + "teltonikaUnsafeTls": "Ігнорувати перевірку сертифіката", + "teltonikaUnsafeTlsDescription": "Вимкнення перевірки сертифікатів TLS створює ризик атак типу «людина посередині» (man-in-the-middle), що може призвести до витоку даних та захоплення контролю над системою. Не вимикайте перевірку сертифікатів, якщо ви не готові прийняти цей вектор атаки. Ми рекомендуємо використовувати LetsEncrypt з автоматичним поновленням.", + "VKTeams Bot Token Description": "Дізнатися, як отримати токен бота, та інші подробиці можна на сайті {0}.", + "VKTeams Base Url Description": "Базова URL-адреса API VKTeams; у локальному рішенні вона може відрізнятися. За замовчуванням: https://myteam.mail.ru", + "VKTeams Chat Id Description": "Для користувачів це їхня електронна адреса. Для груп і каналів знайдіть ідентифікатор у налаштуваннях і додайте {«@»} chat.agent", + "VKTeams Use Template": "Використовувати власний шаблон повідомлення", + "VKTeams Use Template Description": "Якщо ця опція увімкнена, повідомлення буде надіслано за допомогою власного шаблону.", + "VKTeams Template Format Description": "Для оформлення повідомлень VKTeams підтримує формат простого тексту, MarkdownV2 та HTML.", + "telnyxApiKey": "Ключ API (V2)", + "telnyxApiKeyHelptext": "Ваш ключ API Telnyx V2 з порталу Telnyx у розділі «Ключі API»", + "telnyxPhoneNumber": "З номера", + "telnyxPhoneNumberHelptext": "Ваш номер телефону Telnyx, з якого буде здійснюватися дзвінок (він має бути зареєстрований у вашому обліковому записі Telnyx, наприклад +15551234567)", + "telnyxToNumber": "До номера", + "telnyxToNumberHelptext": "Номер телефону, на який надсилатимуться SMS-повідомлення (наприклад, +15559876543)", + "telnyxMessagingProfileId": "Ідентифікатор профілю обміну повідомленнями (не обов’язково)", + "telnyxMessagingProfileIdHelptext": "За бажанням можна прив’язати профіль обміну повідомленнями Telnyx для керування налаштуваннями та функціями відправника", + "webhookPostMethodDesc": "Метод POST підходить для більшості сучасних HTTP-серверів.", + "wayToWriteEvolutionRecipient": "Номер телефону з міжнародним префіксом, але без знака «+» на початку ({0}), ідентифікатор контакту ({1}) або ідентифікатор групи ({2}).", + "brevoBccEmail": "Електронний лист із прихованим копіюванням (BCC)", + "brevoSubject": "Тема", + "resendApiHelp": "Створіть ключ API тут: {0}", + "resendFromName": "Від імені", + "Google": "Google", + "fluxerMessageFormatCustom": "Індивідуальний шаблон", + "SSL/TLS": "Протокол безпечних з’єднань (SSL)/Протокол безпеки транспортного рівня (TLS)", + "playground": "дитячий майданчик", + "Check Type": "Тип чека", + "GRPC Options": "Параметри GRPC", + "Metadata": "Метадані", + "Collapse All Groups": "Сховати всі групи", + "halopsa_field_status": "Стан монітора: «У робочому стані», «Не працює», «Повідомлення» або «Невідомо»", + "matrixUseTemplate": "Використовувати власний шаблон повідомлення", + "signalUseTemplate": "Використовувати власний шаблон повідомлення", + "ntfyCallHelptext": "Здійснити дзвінок при надходженні сповіщення. Встановіть значення «так», щоб використовувати ваш перший підтверджений номер, або введіть конкретний номер телефону (наприклад, +12223334444). Для цього потрібні ntfy Pro та підтверджений номер телефону.", + "ntfyUseTemplate": "Налаштування шаблонів сповіщень", + "ntfyUseTemplateDescription": "Увімкніть цю опцію, щоб налаштовувати заголовки та тексти повідомлень за допомогою шаблонів LiquidJS", + "Open Badge Link Generator": "Генератор посилань на Open Badge", + "webhookGetMethodDesc": "GET надсилає дані у вигляді параметрів запиту і не дозволяє налаштовувати тіло запиту. Це зручно для запуску моніторів Uptime Kuma Push.", + "showOnlyLastHeartbeat": "Показати лише останній сигнал", + "screenshotDelayDescription": "За бажанням можна зачекати стільки мілісекунд перед створенням знімка екрана. Максимум: {maxValueMs} мс (0,5 × інтервал).", + "systemServiceCommandHint": "Використана команда: {command}", + "systemServiceExpectedOutput": "Очікуваний результат: «{0}»", + "evolutionInstanceName": "Назва екземпляра", + "wayToGetEvolutionUrlAndToken": "Ви можете отримати URL-адресу API та токен, перейшовши до потрібного каналу за посиланням {0}", + "message": "повідомлення", + "mtls-auth-server-cert-placeholder": "Текст сертифіката", + "mtls-auth-server-key-label": "Ключ", + "mtls-auth-server-key-placeholder": "Основна частина", + "mtls-auth-server-ca-label": "CA", + "Setup Instructions": "Інструкції з налаштування", + "halopsa_setup_step1": "Створити посібник з інтеграції в HaloPSA (Конфігурація → Інтеграції → Посібники з інтеграції)", + "halopsa_setup_step2": "Налаштуйте дії в посібнику для обробки сповіщень (наприклад, «Створити заявку»)", + "Never": "Ніколи", + "Service Name": "Назва послуги", + "teltonikaModemHelptext": "Ідентифікатор SMS-модему повинен мати такий формат: {0}. Докладні вказівки див. за посиланням https://developers.teltonika-networks.com/reference/.", + "teltonikaPhoneNumberHelptext": "Номер повинен бути вказаний у міжнародному форматі: {0}, {1}. Допускається вказати лише один номер.", + "discordMessageFormat": "Формат повідомлення", + "settingsDomainExpiry": "Закінчення терміну дії домену", + "imageResetConfirmation": "Повернути зображення до стандартних налаштувань", + "screenshot of the website": "Знімок екрана вебсайту", + "Basic checkbox toggle button group": "Основна група кнопок для ввімкнення/вимкнення", + "certificateExpiryNotificationHelp": "Кількість днів наперед можна налаштувати в розділі «Налаштування».", + "Cloud ID": "Cloud ID", + "API Token": "Токен API", + "Ignore STARTTLS": "Ігнорувати STARTTLS", + "Sets end time based on start time": "Визначає час закінчення на основі часу початку", + "Google Apps Script Webhook URL": "URL-адреса вебхука Google Apps Script", + "Copy to Clipboard": "Скопіювати в буфер обміну", + "Copied to clipboard!": "Скопійовано в буфер обміну!", + "You can divide numbers with commas or semicolons": "Ви можете ділити числа за допомогою сайтів {comma} або {semicolon}", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Через обмеження оператора зв’язку увімкніть додаткові параметри, однак це може призвести до недоставки повідомлення", + "aliyun-template-optional-parameters": "Додаткові параметри: {parameters}", + "serwersmsGroupId": "Ідентифікатор групи", + "legacyOctopushEndpoint": "Стара версія Octopush-DM (адреса: {url})", + "Analytics Script URL": "URL-адреса скрипта аналітики", + "systemService": "Системне обслуговування", + "Enter the list of nodes": "Увійти до списку вузлів управління RabbitMQ", + "Press Enter to add node": "Натисніть Enter, щоб додати вузол", + "brevoApiKey": "Ключ API Brevo", + "brevoApiHelp": "Створіть ключ API тут: {0}", + "brevoLeaveBlankForDefaultSubject": "залиште поле порожнім, щоб використовувати стандартну тему", + "resendToEmail": "Надіслати електронним листом", + "resendSubject": "Тема", + "discordMessageFormatMinimalist": "Мінімалістичний (короткий статус)", + "discordMessageFormatCustom": "Індивідуальний шаблон", + "discordUseMessageTemplate": "Використовувати власний шаблон повідомлення", + "Conversation token": "Токен розмови", + "Bot secret": "Секретний ключ бота", + "Globalping - Access global monitoring probes": "Globalping — доступ до глобальних моніторингових датчиків", + "Globalping API Token": "Токен API Globalping", + "globalpingApiTokenDescription": "Отримайте свій токен API Globalping на сайті {0}.", + "GlobalpingIpFamilyInfo": "Версія IP-адреси, яку слід використовувати. Допускається лише в тому випадку, якщо ціллю є ім’я хоста.", + "GlobalpingResolverInfo": "Адреса IPv4/IPv6 або повне доменне ім’я (FQDN). За замовчуванням використовується сервер перекладу імен локальної мережі зонда. Ви можете змінити сервер перекладу імен у будь-який час.", + "Location": "Місцезнаходження", + "Monitor Subtype": "Підтип монітора", + "lowIntervalWarning": "Ви впевнені, що хочете встановити значення інтервалу менше 20 секунд? Це може призвести до зниження продуктивності, особливо якщо моніторів багато.", + "minimumIntervalWarning": "Інтервали, менші за 20 секунд, можуть призвести до зниження продуктивності.", + "mtls-auth-server-ca-placeholder": "Сервер CA", + "evolutionCustomMessageTitle": "Індивідуальне повідомлення (за бажанням)", + "evolutionCustomMessageDesc": "Залиште поле порожнім, щоб використати повідомлення за замовчуванням.", + "slackIncludeGroupName": "Вказати назву групи моніторів", + "slackIncludeGroupNameDescription": "Якщо ця опція увімкнена, шлях до групи моніторів буде вказано в повідомленнях, щоб полегшити розрізнення моніторів з однаковою назвою в різних групах.", + "slackUseTemplate": "Використовувати власний шаблон повідомлення", + "resendApiKey": "Повторно надіслати ключ API", + "360messengerAuthToken": "Ключ API 360messenger", + "discordMessageTemplate": "Шаблон повідомлення", + "fluxerMessageFormatNormal": "Звичайний (розширені вбудовані елементи)", + "fluxerUseMessageTemplateDescription": "Якщо ця опція увімкнена, повідомлення буде надіслано за допомогою власного шаблону (LiquidJS). Залиште поле порожнім, щоб використовувати стандартний формат Uptime Kuma.", + "fluxerMessageTemplate": "Шаблон повідомлення", + "wayToGetFluxerURL": "Це можна зробити, перейшовши в налаштування цільового каналу > Веб-хуки > Створити веб-хук > Скопіювати URL-адресу веб-хука.", + "Nextcloud host": "Хостинг Nextcloud", + "Send UP silently": "Надіслати без звуку", + "wayToGetClickSMSIRTemplateID": "Ваш шаблон повинен містити поле «{uptkumaalert}». Створити новий шаблон можна за посиланням {here}.", + "Recipient Numbers": "Номери одержувачів", + "GlobalpingMultipleLocationsError": "Функція використання декількох місць розташування не підтримується; для кожного монітора слід використовувати одне місце розташування.", + "RegexMatch": "Введіть регулярний вираз для пошуку відповідності значенню запису", + "Protocol": "Протокол", + "account settings": "налаштування облікового запису", + "Check for": "Перевірте наявність", + "Number of retry attempts if webhook fails": "Кількість спроб повтору (кожні 60–180 секунд) у разі збою вебхука.", + "Maximum Retries": "Максимальна кількість спроб", + "Template ID": "Ідентифікатор шаблону", + "Halo PSA": "Привіт, PSA", + "halopsa_username_desc": "Ім’я користувача для авторизації через вебхук Halo PSA", + "halopsa_password_desc": "Пароль для авторизації через вебхук Halo PSA", + "Basic radio toggle button group": "Основна група перемикачів", + "avgPing": "Середній час відгуку", + "maxPing": "Максимальний пінг", + "minPing": "Мій Ping", + "Sort by status": "Сортувати за статусом", + "Sort by name": "Сортувати за назвою", + "Sort by uptime": "Сортувати за часом безвідмовної роботи", + "Sort by certificate expiry": "Сортувати за датою закінчення терміну дії сертифіката", + "Severity": "Рівень серйозності", + "Message Format": "Формат повідомлення", + "Region": "Регіон", + "PushDeer Server URL": "URL сервера PushDeer", + "To Number": "До номера", + "GrafanaOncallURL": "URL-адреса Grafana Oncall", + "System Service": "Системне обслуговування", + "End": "Кінець", + "passwordTooWeak": "Пароль занадто слабкий. Він повинен містити літери та цифри. Довжина пароля має становити щонайменше 6 символів.", + "TLS Alerts": "Повідомлення TLS", + "halopsa_field_monitor": "Назва монітора", + "halopsa_field_monitor_id": "Унікальний ідентифікатор монітора (нульове значення для тестових сповіщень) — використовуйте цей параметр для прив’язки сповіщень до квитків", + "halopsa_field_message": "Повне повідомлення про тривогу зі статусом та деталями", + "halopsa_field_timestamp": "Часовий штамп події у форматі ISO 8601", + "GlobalpingLocationDocs": "Повна документація щодо введення координат", + "monitorTypeGameServer": "Ігровий сервер", + "monitorTypeDatabase": "Моніторинг бази даних", + "monitorTypeSpecial": "Спеціальне", + "pausedMonitorsMsg": "Призупинено моніторинг {n} | Призупинено моніторинг {n}", + "Please set start time first": "Будь ласка, спочатку вкажіть час початку", + "signalUseTemplateDescription": "Якщо ця опція увімкнена, повідомлення буде надіслано за допомогою власного шаблону. Ви можете скористатися мовою шаблонів Liquid, щоб налаштувати формат повідомлення.", + "aboutJiraCloudId": "Докладніша інформація про Jira Cloud ID: {0}", + "see Jira Cloud Docs": "Див. документацію Jira Cloud", + "Jira Service Management": "Jira Service Management", + "serwersmsRecipientType": "Тип одержувача", + "octopushEndpoint": "octopush (кінцева точка: {url})", + "ntfyCustomTitle": "Шаблон назви на замовлення", + "evolutionRecipient": "Номер телефону / Ідентифікатор контакту / Ідентифікатор групи", + "Umami": "Умамі", + "RecordMatch": "Збіг за значенням запису", + "halopsa_payload_desc": "На ваш вебхук Halo PSA надсилаються такі поля:", + "Webhook Payload Fields": "Поля даних вебхука", + "halopsa_webhook_url_desc": "Введіть URL-адресу вебхука з вашого посібника з інтеграції Halo PSA (Конфігурація > Інтеграції > Налаштовані інтеграції > Посібники з інтеграції). Під час створення вебхука виберіть опцію «Можна запускати лише з Halo та з публічного кінцевого пункту».", + "halopsa_id_usage_hint": "💡 Порада: використовуйте monitor_id для надійного зіставлення сповіщень із квитками, а також heartbeat_id для відстеження історії подій", + "halopsa_setup_step5": "Налаштуйте посібник з виконання так, щоб він використовував monitor_id для зіставлення сповіщень з існуючими квитками", + "domainExpiryNotificationHelp": "Кількість днів наперед можна налаштувати в розділі «Налаштування».", + "domainExpiryDescription": "Надіслати сповіщення, коли термін дії доменних імен закінчиться через:", + "mtls-auth-server-cert-label": "Сертифікат", + "halopsa_setup_step3": "Скопіюйте URL-адресу вебхука та вставте її у поле для введення тексту вище", + "Clear current filters": "Очистити фільтри поточного перегляду", + "mariadbSocketPathDetectedHelptext": "Підключення до бази даних, як вказано у змінній середовища `{0}`.", + "Expand All Groups": "Розгорнути всі групи", + "See Jira Cloud Docs": "Див. документацію Jira Cloud", + "certHostnameMismatch": "Ім’я хоста сертифіката не збігається з URL-адресою монітора.", + "sipsakPingWarning": "Щоб скористатися монітором Ping для параметрів SIP, вам потрібно встановити Uptime Kuma без Docker, а також встановити клієнт Sipsak на вашому сервері.", + "Examples:": "Приклади: {0}", + "supportBaleChatID": "Підтримка ID чату в прямому чаті / групі / каналі", + "frontendVersionIs": "Версія інтерфейсу: {version}", + "cronScheduleDescription": "Розклад: {description}", + "Clone Maintenance": "Клонувати обслуговування", + "ariaPauseMaintenance": "Призупинити цей графік технічного обслуговування", + "notificationPushServices": "Служби push-повідомлень", + "notificationOther": "Інші інтеграції", + "SMTP Security": "Безпека SMTP", + "Use STARTTLS": "Використовувати STARTTLS", + "disableSTARTTLSDescription": "Увімкніть цю опцію для SMTP-серверів, які не підтримують STARTTLS. У цьому випадку листи надсилатимуться через незашифроване з’єднання.", + "deletedMonitorsMsg": "Видалено монітор {n} | Видалено монітори {n}", + "resumedMonitorsMsg": "Відновлено моніторинг {n} | Відновлено моніторинг {n}", + "deleteChildrenMonitors": "Також видаліть безпосередньо підпорядковані монітори та їхні підпорядковані елементи, якщо такі є | Також видаліть усі безпосередньо підпорядковані монітори {count} та їхні підпорядковані елементи, якщо такі є", + "noMonitorsSelectedWarning": "Ви створюєте завдання на технічне обслуговування без зазначених моніторів. Ви впевнені, що хочете продовжити?", + "Quick Setup Guide": "Короткий посібник з налаштування", + "Deploy a Google Apps Script as a web app and paste the URL here": "Розгорніть скрипт Google Apps Script як вебдодаток і вставте його URL-адресу сюди", + "Go to Extensions → Apps Script": "Перейдіть до розділу «Розширення» → «Apps Script»", + "Paste the script code (see below)": "Вставте код скрипта (див. нижче)", + "Click Deploy → New deployment → Web app": "Натисніть «Розгортання» → «Нове розгортання» → «Вебдодаток»", + "Open your Google Spreadsheet": "Відкрийте свою таблицю Google", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Встановіть параметри «Виконувати як: Я» та «Хто має доступ: Будь-хто»", + "Copy the web app URL and paste it above": "Скопіюйте URL-адресу вебдодатка та вставте її вище", + "Failed to copy to clipboard": "Не вдалося скопіювати в буфер обміну", + "Mention Mobile List": "Перелік мобільних пристроїв", + "Mention User List": "Перелік ідентифікаторів користувачів", + "Dingtalk Mobile List": "Список мобільних пристроїв", + "Dingtalk User List": "Список ідентифікаторів користувачів", + "WeCom Mentioned Mobile List": "Список мобільних пристроїв, згаданих у WeCom", + "WeCom Mentioned Mobile List Description": "Введіть номери телефонів, які потрібно вказати. Кілька номерів розділяйте комами. Щоб вказати всіх, введіть {«@»}.", + "serwersmsRecipientTypeGroup": "Група", + "serwersmsGroupIdHelptext": "Ідентифікатор або ідентифікатори груп у Панелі клієнта. Ці ідентифікатори можна завантажити за допомогою групи дій / індексу або скопіювавши їх із групи редагування в Панелі клієнта.", + "ntfyCustomMessage": "Шаблон індивідуального повідомлення", + "ntfyNotificationTemplateFallback": "Залиште поле порожнім, щоб використовувати стандартний формат Uptime Kuma", + "twilloMessagingServiceSIDHelptext": "Введіть тут ідентифікатор служби обміну повідомленнями (SID), якщо ви використовуєте {twillo_messaging_service_help_link} для управління відправниками та функціями", + "Badge Link Generator": "Генератор посилань на значки від {0}", + "screenshotDelayWarning": "Більші значення призводять до того, що браузер залишається відкритим довше, що може збільшити споживання пам’яті при роботі з великою кількістю моніторів одночасно.", + "json_value": "Значення JSON", + "Deselect All": "Скасувати вибір усіх", + "Select All": "Вибрати все", + "brevoFromEmail": "З електронного листа", + "resendLeaveBlankForDefaultSubject": "Залиште поле порожнім, щоб використовувати стандартну тему", + "resendFromEmail": "З електронного листа", + "resendLeaveBlankForDefaultName": "залиште поле порожнім, щоб використовувати стандартну назву", + "360messengerGroupId": "Ідентифікатор групи 360messenger", + "360messengerUseTemplate": "Використовуйте власний шаблон повідомлення", + "360messengerTemplate": "Шаблон повідомлення 360messenger", + "360messengerGroupList": "Групи WhatsApp", + "360messengerSelectGroupList": "Виберіть групу для додавання", + "360messengerEnableSendToGroup": "Увімкнути надсилання повідомлень у групу(-и) WhatsApp", + "360messengerCustomMessageTemplate": "Шаблон повідомлення, створений користувачем", + "360messengerEnableCustomMessage": "Увімкніть власний шаблон повідомлення замість повідомлення за замовчуванням.", + "360messengerMessageTemplate": "Шаблон повідомлення", + "360messengerWayToGetUrlAndToken": "Ви можете отримати ключ API для 360messenger на сайті {0}.", + "360messengerErrorNoApiKey": "Спочатку введіть свій ключ API 360messenger.", + "Send DOWN silently": "Надіслати DOWN без повідомлення", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Для встановлення бота Nextcloud Talk необхідні права адміністратора на сервері.", + "GlobalpingMonitorDescription": "Globalping надає доступ до тисяч зондів, розміщених у спільноті, для проведення тестів та вимірювань мережі. Для всіх анонімних користувачів встановлено обмеження у 250 тестів на годину. Щоб подвоїти це обмеження до 500 тестів на годину, збережіть свій токен на сторінці {accountSettings}. Докладнішу інформацію можна знайти на сторінці {docs}.", + "domain_expiry_unsupported_monitor_type": "Для цього типу моніторингу не підтримується відстеження терміну дії домену", + "domain_expiry_unsupported_is_icann": "Домен «{domain}» не підлягає моніторингу терміну дії, оскільки його публічний суфікс «.{publicSuffix}» не перебуває під управлінням ICANN", + "halopsa_setup_step4": "Виберіть «Базова автентифікація» та створіть ім’я користувача й пароль. Потім введіть або вставте ці дані у відповідні поля вище", + "Sort options": "Параметри сортування", + "smscTranslit": "SMSC Translit", + "Endpoint": "Кінцева точка", + "Details": "Деталі", + "expectedTlsAlertDescription": "Виберіть тип попередження TLS, яке, на вашу думку, повинен повернути сервер. Скористайтеся командою «{code}», щоб перевірити, чи кінцеві точки mTLS відхиляють з’єднання без сертифікатів клієнта. Детальнішу інформацію див. за посиланням {link}.", + "TLS Alert Spec": "Запит на коментарі № 8446", + "halopsa_field_title": "Заголовок сповіщення (завжди «Сповіщення Uptime Kuma»)", + "teltonikaModem": "Ідентифікатор модему", + "teamsEnableTags": "Додати теги", + "teamsEnableTagsDescription": "Якщо ця опція увімкнена, повідомлення міститиме теги монітора.", + "Allow Notifications": "Дозволити сповіщення", + "Plausible": "Цілком ймовірно", + "Notification Action": "Дія повідомлення", + "homeAssistantNotificationActionHelptext": "Перелік дій сповіщення можна знайти в Home Assistant у розділі «Налаштування > Інструменти розробника > Дії». Знайдіть свої дії, ввівши у пошук «notify». Введіть лише частину після «notify.», наприклад, для дії «notify.mobile_app_xyz» введіть «mobile_app_xyz». Для вбудованих мобільних сповіщень шукайте «Надіслати сповіщення через mobile_app_xyz» (а не «Надіслати сповіщення»).", + "versionIs": "Версія: {version}", + "WebSocket Options": "Параметри WebSocket", + "Badge Link Generator Helptext": "Посилання на значки доступні для всіх моніторів, призначених для публічних сторінок стану. Докладнішу інформацію дивіться у посібнику «{documentation}».", + "systemServiceDescription": "Перевіряє, чи активна системна служба «{service_name}»", + "systemServiceDescriptionLinux": "Перевіряє, чи активна служба systemd в Linux під назвою «{service_name}»", + "systemServiceDescriptionWindows": "Перевіряє, чи працює диспетчер служб Windows {service_name}", + "brevoFromName": "Від імені", + "fluxerUseMessageTemplate": "Використовувати власний шаблон повідомлення", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Моніторинг терміну дії доменів не доступний для домену «.{publicSuffix}», оскільки IANA не вказала жодної служби RDAP", + "Splunk Rest URL": "URL-адреса Rest-інтерфейсу Splunk", + "Expected TLS Alert": "Очікуване попередження TLS", + "None (Successful Connection)": "Немає (з’єднання встановлено)", + "teltonikaVersionWarning": "Для роботи цього постачальника сповіщень на вашому пристрої Teltonika має бути встановлена версія RMS 7.14.0 або новіша.", + "teltonikaUrl": "URL-адреса вашого пристрою Teltonika", + "teltonikaUsernameHelptext": "Рекомендація: створіть окремий обліковий запис, призначений виключно для надсилання SMS-повідомлень, та введіть тут його ім’я користувача", + "teltonikaPassword": "Пароль API", + "teltonikaPasswordHelptext": "Ви можете встановити пароль користувача API у своєму маршрутизаторі Teltonika, наприклад: {0}", + "teltonikaPhoneNumber": "Номер телефону", + "notificationUniversal": "Універсальний", + "mariadbUseSSLHelptext": "Увімкніть використання зашифрованого з’єднання з базою даних. Це необхідно для більшості хмарних баз даних.", + "mariadbCaCertificateLabel": "Сертифікат ЦС", + "unknownDays": "Невідомі дні", + "No incidents recorded": "Інцидентів не зафіксовано", + "Load More": "Завантажити ще", + "Loading...": "Завантаження...", + "Monitors": "{n} Монітор | Монітори{n}", + "days": "{n} день | {n} днів", + "hours": "{n} година | {n} годин", + "minutes": "{n} хвилина | {n} хвилин", + "minuteShort": "{n} хв | {n} хв", + "Only retry if status code check fails": "Повторювати спробу лише у разі невдалої перевірки коду статусу", + "retryOnlyOnStatusCodeFailureDescription": "Якщо ця опція увімкнена, повторні спроби будуть здійснюватися лише у разі невдалої перевірки коду статусу HTTP (наприклад, якщо сервер не працює). Якщо перевірка коду статусу пройде успішно, але запит JSON завершиться невдачею, монітор буде негайно позначений як непрацюючий без повторних спроб.", + "saveResponseForNotifications": "Зберігати успішну HTTP відповідь для сповіщень", + "saveErrorResponseForNotifications": "Зберігати невдалу HTTP відповідь для сповіщень", + "saveResponseDescription": "Зберігає відповідь HTTP і надає її шаблонам сповіщень у вигляді параметра «{templateVariable}»", + "logoutCurrentUser": "Вийти {username}", + "Resolver Server(s)": "Сервери DNS", + "Incident description": "Опис інциденту", + "Incident not found or access denied": "Інцидент не знайдено або доступ заборонено", + "Past Incidents": "Попередні інциденти", + "Pinned incidents are shown prominently on the status page": "Закріплені інциденти відображаються на сторінці стану у видному місці", + "Edit Incident": "Редагувати інцидент", + "templateAvailableVariables": "Доступні змінні", + "example": "Приклад", + "Result": "Результат", + "BodyInvalidFormatBecause": "Тіло запиту не є дійсним JSON, оскільки {error}", + "steamApiKeyDescriptionAt": "Для моніторингу ігрового сервера Steam вам знадобиться ключ Steam Web-API. Ви можете зареєструвати свій ключ API за адресою {url}", + "hostnameCannotBeIP": "Ім’я хоста DNS не може бути IP-адресою. Можливо, ви хотіли скористатися полем «Сервери DNS»?", + "invalidURL": "Неправильна URL-адреса", + "lastUpdatedAt": "Останнє оновлення: {date}", + "lastUpdatedAtFromNow": "Останнє оновлення: {date} ({fromNow})", + "createdAt": "Дата створення: {date}", + "descriptionHelpText": "Відображається на внутрішній інформаційній панелі. Допускається використання Markdown, який перед відображенням проходить очищення (збереження пробілів та відступів).", + "auto-select": "Автоматичний вибір", + "Actions": "Дії", + "selectedMonitorCountMsg": "вибрано: {n} | вибрано: {n}", + "selectMonitorMsg": "Виберіть монітори для виконання дій", + "selectAllMonitorsAria": "Вибрати всі монітори", + "Certificate Chain:": "Ланцюжок сертифікатів:", + "slug is not found": "Не знайдено запису", + "Please input content": "Введіть текст", + "Please input title": "Введіть заголовок", + "Leave blank to use status page title": "Залиште поле порожнім, щоб використати заголовок сторінки стану", + "dateCreatedAtFromNow": "Дата створення: {date} ({fromNow})", + "RSS Title": "Заголовок RSS", + "ariaResumeMaintenance": "Повернутися до цього графіка технічного обслуговування", + "HTTP Method": "Метод HTTP", + "Screenshot Delay": "Затримка знімка екрана (очікування {milliseconds})", + "snmpV3Username": "Ім’я користувача SNMPv3", + "maxMessenger": "Месенджер MAX", + "wayToGetMaxToken": "Отримати токен бота MAX та іншу інформацію можна на сайті {0}.", + "wayToGetMaxChatID": "Вкажіть у MAX ідентифікатор чату, куди слід надсилати повідомлення.", + "maxUseTemplate": "Використовувати власний шаблон повідомлення", + "maxApiUrlDescription": "Базова URL-адреса API для месенджера MAX. За замовчуванням: https://platform-api.max.ru", + "maxUseTemplateDescription": "Якщо ця опція увімкнена, повідомлення буде надіслано за допомогою власного шаблону.", + "wayToGetBaleChatID": "Ви можете дізнатися свій ідентифікатор чату, надіславши повідомлення боту та перейшовши за цим посиланням, щоб переглянути chat_id:", + "wayToGetBaleToken": "Ви можете отримати токен на сайті {0}.", + "Duration (Minutes)": "Тривалість (хвилини)", + "notificationSmsServices": "Послуги SMS", + "notificationIncidentManagement": "Управління інцидентами", + "notificationEmail": "Електронна пошта", + "notificationHomeAutomation": "Домашня автоматизація", + "Disable STARTTLS": "Вимкнути STARTTLS", + "deleteMonitorsMsg": "Ви впевнені, що хочете видалити вибрані монітори?", + "noMonitorsPausedMsg": "Монітори не перебувають у режимі паузи (жодного з них не було увімкнено)", + "noMonitorsResumedMsg": "Монітори не відновили роботу (жодний з них не був неактивним)", + "bulkDeleteErrorMsg": "Не вдалося видалити монітор {n} | Не вдалося видалити монітори {n}", + "deleteGroupMsg": "Ви впевнені, що хочете видалити цю групу?", + "noMonitorsOrStatusPagesSelectedError": "Неможливо створити завдання на технічне обслуговування без вказаних моніторів або сторінок стану", + "OptionalParameters": "Необов’язкові параметри", + "twilioApiKeyHelptext": "API-ключ не є обов’язковим, але його використання рекомендується. Ви можете вказати або SID облікового запису та AuthToken зі сторінки TwilioConsole, або SID облікового запису та пару з API-ключа й секретного ключа API.", + "twilioMessagingServiceSID": "Ідентифікатор служби обміну повідомленнями (SID) (не обов’язково)", + "systemServiceName": "Назва послуги", + "brevoToEmail": "Надіслати електронним листом", + "brevoCcEmail": "Копія електронного листа", + "360messengerRecipient": "Номер(и) телефону одержувача", + "360messengerWayToWriteRecipient": "Введіть один або кілька телефонних номерів у міжнародному форматі без знака «+» на початку (наприклад, {0}). Кілька номерів слід розділяти комами.", + "360messengerErrorNoGroups": "Для цього облікового запису не знайдено жодних груп у WhatsApp.", + "360messengerErrorApi": "Не вдалося завантажити список груп WhatsApp (Помилка {statusCode}: {message}).", + "360messengerErrorGeneric": "Не вдається завантажити список груп WhatsApp: {message}", + "Matomo": "Matomo", + "discordSuppressNotificationsHelptext": "Якщо ця опція увімкнена, повідомлення будуть публікуватися в каналі, але не викликатимуть push-сповіщення чи сповіщення на робочому столі для одержувачів.", + "discordMessageFormatNormal": "Звичайний (розширені вбудовані елементи)", + "fluxerMessageFormat": "Формат повідомлення", + "Notifications Enabled": "Увімкнено сповіщення", + "Browser not supported": "Цей браузер не підтримується", + "Unable to get permission to notify": "Не вдалося отримати дозвіл на надсилання сповіщення (запит було відхилено або проігноровано).", + "labelDomainExpiry": "Термін дії домену", + "API Version": "Версія API", + "vkPeerIdDescription": "Введіть ідентифікатор peer_id цільового профілю у VK. Це значення надсилається до API без змін.", + "serwersmsRecipientTypePhone": "Номер телефону", + "Fluxer Webhook URL": "URL-адреса вебхука Fluxer", + "halopsa_field_uptime_kuma_version": "Номер версії Uptime Kuma", + "teltonikaUsername": "Ім’я користувача API", + "Google Apps Script Code": "Код Google Apps Script", + "aliyun-template-requirements-and-parameters": "Шаблон SMS для Aliyun повинен містити такі параметри: {parameters}", + "ntfyCall": "Телефонний дзвінок", + "milliseconds": "{n} мілісекунда | {n} мілісекунди", + "brevoSeparateMultipleEmails": "Розділіть кілька адрес електронної пошти комами", + "username": "Ім’я користувача", + "password": "Пароль", + "brevoLeaveBlankForDefaultName": "залиште поле порожнім, щоб використовувати ім’я за замовчуванням", + "Suppress Notifications": "Приховати сповіщення", + "domain_expiry_unsupported_missing_target": "Для цього монітора не налаштовано жодного дійсного домену або імені хоста", + "wayToGetEgoSMSToken": "Ви можете отримати ім’я користувача та пароль для API EgoSMS на {here}.", + "egosmsSenderDescription": "Ідентифікатор відправника, що відображається одержувачам (максимум 11 символів). Якщо поле порожнє, за замовчуванням використовується значення «EGOSMS».", + "egosmsPhoneNumberDescription": "Номер абонента з кодом країни, без знаків «+» або «00» (наприклад, 2567XXXXXXXX).", + "gamedigToken": "Токен", + "gamedigTokenDescription": "Необов’язково. Деякі ігрові сервери вимагають токен автентифікації.", + "Bearer Token": "Токен доступу", + "Connection Timeout": "Час очікування з’єднання" +} diff --git a/src/lang/ur.json b/src/lang/ur.json new file mode 100644 index 0000000..2ba588c --- /dev/null +++ b/src/lang/ur.json @@ -0,0 +1,569 @@ +{ + "Dashboard": "ڈیش بورڈ", + "New Update": "نئی تازہ کاری", + "Language": "زبان", + "Appearance": "ظہور", + "Theme": "خیالیہ", + "General": "جنرل", + "Game": "کھیل", + "Version": "ورژن", + "List": "فہرست", + "Add": "شامل کریں۔", + "Add New Monitor": "نیا مانیٹر شامل کریں", + "Quick Stats": "فوری اعدادوشمار", + "Up": "اوپر", + "Down": "نیچے", + "statusMaintenance": "دیکھ بھال", + "Maintenance": "دیکھ بھال", + "Unknown": "نامعلوم", + "General Monitor Type": "جنرل مانیٹر کی قسم", + "Specific Monitor Type": "مانیٹر کی مخصوص قسم", + "markdownSupported": "مارک ڈاون نحو کی حمایت کی گئی", + "pauseDashboardHome": "توقف", + "Pause": "توقف", + "Name": "نام", + "Status": "حالت", + "DateTime": "تاریخ وقت", + "Message": "پیغام", + "Resume": "دوبارہ شروع کریں", + "Edit": "ترمیم", + "Delete": "حذف کریں", + "Current": "کرنٹ", + "Uptime": "اپ ٹائم", + "Cert Exp.": "Cert Exp .", + "Monitor": "مانیٹر | مانیٹر", + "day": "دن | دن", + "-day": "-دن", + "hour": "گھنٹہ", + "Response": "جواب", + "Check Update On GitHub": "GitHub پر اپ ڈیٹ چیک کریں", + "Ping": "پنگ", + "Monitor Type": "مانیٹر کی قسم", + "Friendly Name": "دوستانہ نام", + "URL": "URL", + "Hostname": "میزبان کا نام", + "Port": "بندرگاہ", + "Heartbeat Interval": "دل کی دھڑکن کا وقفہ", + "Heartbeat Retry Interval": "دل کی دھڑکن دوبارہ کوشش کا وقفہ", + "Advanced": "اعلی درجے کی", + "checkEverySecond": "ہر {0} سیکنڈ میں چیک کریں", + "retryCheckEverySecond": "ہر {0} سیکنڈ میں دوبارہ کوشش کریں", + "Help": "مدد", + "ignoreTLSError": "HTTPS ویب سائٹس کے لیے TLS/SSL کی غلطیوں کو نظر انداز کریں", + "upsideDownModeDescription": "اسٹیٹس کو الٹا پلٹائیں۔ اگر سروس قابل رسائی ہے، تو یہ نیچے ہے۔", + "Upside Down Mode": "الٹا ڈاؤن موڈ", + "Max. Redirects": "زیادہ سے زیادہ ری ڈائریکٹ کرتا ہے", + "Accepted Status Codes": "قبول شدہ اسٹیٹس کوڈز", + "Push URL": "یو آر ایل کو پش کریں", + "needPushEvery": "آپ کو اس URL کو ہر {0} سیکنڈ میں کال کرنا چاہیے۔", + "pushOptionalParams": "اختیاری پیرامیٹرز: {0}", + "Save": "محفوظ کریں", + "Notifications": "اطلاعات", + "Setup Notification": "نوٹیفکیشن مرتب کریں", + "Light": "روشنی", + "Dark": "اندھیرا", + "Auto": "آٹو", + "Theme - Heartbeat Bar": "تھیم - دل کی دھڑکن بار", + "Normal": "نارمل", + "Bottom": "نیچے", + "None": "کوئی نہیں۔", + "Search Engine Visibility": "سرچ انجن کی مرئیت", + "Allow indexing": "اشاریہ سازی کی اجازت دیں", + "Change Password": "پاس ورڈ تبدیل کریں", + "Current Password": "موجودہ خفیہ لفظ", + "New Password": "نیا پاس ورڈ", + "Repeat New Password": "نیا پاس ورڈ دہرائیں", + "Update Password": "پاس ورڈ اپ ڈیٹ کریں", + "Disable Auth": "Auth کو غیر فعال کریں", + "Enable Auth": "Auth کو فعال کریں", + "Please use this option carefully!": "براہ کرم اس اختیار کو احتیاط سے استعمال کریں!", + "Logout": "لاگ آوٹ", + "Leave": "چھوڑو", + "I understand, please disable": "میں سمجھتا ہوں، براہ کرم غیر فعال کریں", + "Confirm": "تصدیق کریں", + "Yes": "جی ہاں", + "No": "نہیں", + "Username": "صارف نام", + "Password": "پاس ورڈ", + "Remember me": "مجھے پہچانتے ہو", + "Login": "لاگ ان کریں", + "No Monitors, please": "کوئی مانیٹر نہیں، براہ کرم", + "add one": "ایک شامل کریں", + "Notification Type": "اطلاع کی قسم", + "Email": "ای میل", + "Test": "پرکھ", + "Certificate Info": "سرٹیفکیٹ کی معلومات", + "Resource Record Type": "ریسورس ریکارڈ کی قسم", + "goAlert": "الرٹ جاؤ", + "SecretAccessKey": "کلیدی ID تک رسائی حاصل کریں", + "PhoneNumbers": "فون نمبر", + "TemplateCode": "ٹیمپلیٹ کوڈ", + "SignName": "سائن نام", + "Bark Endpoint": "بارک اینڈ پوائنٹ", + "Bark Group": "بارک گروپ", + "AccessKeyId": "کلیدی ID تک رسائی حاصل کریں", + "languageName": "انگریزی", + "Settings": "ترتیبات", + "Primary Base URL": "بنیادی بنیاد URL", + "Pending": "زیر التواء", + "Passive Monitor Type": "غیر فعال مانیٹر کی قسم", + "No important events": "کوئی اہم واقعات نہیں", + "-hour": "-گھنٹہ", + "goAlertIntegrationKeyInfo": "اس فارمیٹ میں سروس کے لیے عام API انٹیگریشن کلید حاصل کریں \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeee\" عام طور پر کاپی شدہ URL کے ٹوکن پیرامیٹر کی قدر۔", + "Sms template must contain parameters: ": "ایس ایم ایس ٹیمپلیٹ میں پیرامیٹرز ہونا ضروری ہے: ", + "Keyword": "کلیدی لفظ", + "Retries": "دوبارہ کوشش کرتا ہے", + "Resend Notification if Down X times consequently": "نوٹیفکیشن دوبارہ بھیجیں اگر X بار لگاتار نیچے جائیں", + "resendEveryXTimes": "ہر {0} بار دوبارہ بھیجیں", + "resendDisabled": "دوبارہ بھیجنا غیر فعال ہے", + "retriesDescription": "سروس کو ڈاؤن کے بطور نشان زد کرنے اور ایک اطلاع بھیجے جانے سے پہلے زیادہ سے زیادہ کوششیں کریں", + "maxRedirectDescription": "فالو کرنے کے لیے ری ڈائریکٹس کی زیادہ سے زیادہ تعداد۔ ری ڈائریکٹ کو غیر فعال کرنے کے لیے 0 پر سیٹ کریں۔", + "Not available, please setup.": "دستیاب نہیں ہے، براہ کرم ترتیب دیں۔", + "Timezone": "ٹائم زون", + "Discourage search engines from indexing site": "انڈیکسنگ سائٹ سے سرچ انجنوں کی حوصلہ شکنی کریں", + "disableauth.message1": "کیا آپ واقعی {disableAuth} کرنا چاہتے ہیں؟", + "disable authentication": "تصدیق کو غیر فعال", + "disableauth.message2": "یہ ان منظرناموں کے لیے ڈیزائن کیا گیا ہے{intendThirdPartyAuth} اپ ٹائم کوما جیسے کہ Cloudflare Access، Authelia یا دیگر تصدیقی میکانزم کے سامنے۔", + "where you intend to implement third-party authentication": "جہاں آپ کا ارادہ ہے کہ آپ فریق ثالث کی توثیق کو لاگو کریں", + "Resolver Server": "حل کرنے والا سرور", + "Last Result": "آخری نتیجہ", + "Create your admin account": "اپنا ایڈمن اکاؤنٹ بنائیں", + "Repeat Password": "پاس ورڈ دوبارہ لکھیے", + "Import Backup": "بیک اپ درآمد کریں", + "Export Backup": "بیک اپ درآمد کریں", + "Import": "درآمد کریں", + "respTime": "ریسپ وقت (ایم ایس)", + "notAvailableShort": "N / A", + "Default enabled": "ڈیفالٹ فعال ہے", + "Create": "بنانا", + "Clear Data": "واضح اعداد و شمار", + "Events": "تقریبات", + "Heartbeats": "دل کی دھڑکنیں", + "Auto Get": "آٹو حاصل کریں", + "Schedule maintenance": "شیڈول کی بحالی", + "Affected Monitors": "متاثرہ مانیٹر", + "Start of maintenance": "بحالی کا آغاز", + "All Status Pages": "تمام اسٹیٹس پیجز", + "Select status pages...": "اسٹیٹس کے صفحات منتخب کریں…", + "alertWrongFileType": "براہ کرم ایک JSON فائل منتخب کریں۔", + "Clear all statistics": "تمام اعدادوشمار کو صاف کریں", + "Skip existing": "موجودہ کو چھوڑ دیں", + "Overwrite": "اوور رائٹ کریں", + "Options": "اختیارات", + "Verify Token": "ٹوکن کی تصدیق کریں", + "Setup 2FA": "2FA مرتب کریں", + "Enable 2FA": "2FA کو فعال کریں", + "2FA Settings": "2FA کی ترتیبات", + "Two Factor Authentication": "دو عنصر کی تصدیق", + "Active": "فعال", + "Inactive": "غیر فعال", + "Token": "ٹوکن", + "Show URI": "URI دکھائیں", + "Tags": "ٹیگز", + "Tag with this name already exist.": "اس نام کا ٹیگ پہلے سے موجود ہے۔", + "Tag with this value already exist.": "اس قدر کے ساتھ ٹیگ پہلے سے موجود ہے۔", + "color": "رنگ", + "value (optional)": "قدر (اختیاری)", + "Gray": "سرمئی", + "Red": "سرخ", + "Orange": "کینو", + "Blue": "نیلا", + "Indigo": "انڈگو", + "Purple": "جامنی", + "Pink": "گلابی", + "Custom": "اپنی مرضی کے مطابق", + "Avg. Response": "اوسط جواب", + "No Services": "کوئی خدمات نہیں", + "All Systems Operational": "تمام سسٹمز آپریشنل", + "Partially Degraded Service": "جزوی طور پر انحطاط شدہ سروس", + "Degraded Service": "ڈیگریڈڈ سروس", + "Add Group": "گروپ شامل کریں", + "Add a monitor": "مانیٹر شامل کریں", + "Edit Status Page": "اسٹیٹس پیج میں ترمیم کریں", + "Go to Dashboard": "ڈیش بورڈ پر جائیں", + "Export": "برآمد کریں", + "Apply on all existing monitors": "تمام موجودہ مانیٹر پر لاگو کریں", + "Pick Affected Monitors...": "متاثرہ مانیٹر منتخب کریں…", + "alertNoFile": "براہ کرم درآمد کرنے کے لیے ایک فائل منتخب کریں۔", + "Keep both": "دونوں رکھو", + "Disable 2FA": "2FA کو غیر فعال کریں", + "Add New below or Select...": "ذیل میں نیا شامل کریں یا منتخب کریں…", + "Green": "سبز", + "Search...": "تلاش کریں…", + "Avg. Ping": "دسمبر پنگ", + "Entry Page": "داخلہ صفحہ", + "statusPageNothing": "یہاں کچھ نہیں، براہ کرم ایک گروپ یا مانیٹر شامل کریں۔", + "Resend Notification if Down X times consecutively": "نوٹیفکیشن دوبارہ بھیجیں اگر X بار لگاتار نیچے جائیں", + "Status Page": "اسٹیٹس پیج", + "Status Pages": "اسٹیٹس پیجز", + "defaultNotificationName": "میرا {notification} الرٹ ({number})", + "here": "یہاں", + "Required": "درکار ہے", + "webhook": "ویب ہُک", + "Post URL": "یو آر ایل پوسٹ کریں", + "Content Type": "مواد کی قسم", + "webhookJsonDesc": "{0} کسی بھی جدید HTTP سرورز جیسے Express.js کے لیے اچھا ہے", + "webhookFormDataDesc": "{multipart} پی ایچ پی کے لیے اچھا ہے۔ JSON کو {decodeFunction} کے ساتھ پارس کرنے کی ضرورت ہوگی", + "webhookAdditionalHeadersTitle": "اضافی ہیڈرز", + "webhookAdditionalHeadersDesc": "ویب ہک کے ساتھ بھیجے گئے اضافی ہیڈر سیٹ کرتا ہے۔ ہر ہیڈر کو JSON کلید/قدر کے طور پر بیان کیا جانا چاہیے۔", + "Webhook URL": "ابھوک دیہی", + "Application Token": "ایپلیکیشن ٹوکن", + "Server URL": "سرور URL", + "Priority": "ترجیح", + "emojiCheatSheet": "ایموجی چیٹ شیٹ: {0}", + "Read more": "مزید پڑھ", + "appriseInstalled": "اپرائز انسٹال ہے۔", + "appriseNotInstalled": "اپرائز انسٹال نہیں ہے۔ {0}", + "Method": "طریقہ", + "Body": "جسم", + "Headers": "ہیڈرز", + "PushUrl": "یو آر ایل کو پش کریں", + "HeadersInvalidFormat": "درخواست کے ہیڈر درست نہیں ہیں JSON: ", + "BodyInvalidFormat": "درخواست کا باڈی درست نہیں ہے JSON: ", + "Monitor History": "تاریخ کی نگرانی کریں", + "clearDataOlderThan": "مانیٹر کی سرگزشت کا ڈیٹا {0} دنوں تک رکھیں۔", + "PasswordsDoNotMatch": "پاس ورڈ میچ نہیں کرتے.", + "records": "ریکارڈز", + "One record": "ایک ریکارڈ", + "Current User": "موجودہ صارف", + "topic": "موضوع", + "topicExplanation": "نگرانی کے لیے MQTT موضوع", + "successMessage": "کامیابی کا پیغام", + "successMessageExplanation": "MQTT پیغام جسے کامیابی سمجھا جائے گا", + "recent": "حالیہ", + "Done": "ہو گیا", + "Info": "معلومات", + "Security": "سیکورٹی", + "Steam API Key": "بھاپ API کلید", + "Shrink Database": "ڈیٹا بیس کو سکڑیں", + "Pick a RR-Type...": "RR قسم کا انتخاب کریں…", + "Pick Accepted Status Codes...": "قبول شدہ اسٹیٹس کوڈز منتخب کریں…", + "Default": "طے شدہ", + "HTTP Options": "HTTP اختیارات", + "Create Incident": "واقعہ بنائیں", + "Title": "عنوان", + "Content": "مواد", + "Style": "انداز", + "info": "معلومات", + "danger": "خطرہ", + "error": "غلطی", + "critical": "تنقیدی", + "primary": "بنیادی", + "light": "روشنی", + "dark": "اندھیرا", + "Post": "پوسٹ", + "Created": "بنایا", + "Last Updated": "آخری تازہ کاری", + "Unpin": "بادل ساحل", + "Switch to Light Theme": "لائٹ تھیم پر سوئچ کریں", + "Switch to Dark Theme": "ڈارک تھیم پر سوئچ کریں", + "Hide Tags": "ٹیگز چھپائیں", + "Description": "تفصیل", + "No monitors available.": "کوئی مانیٹر دستیاب نہیں۔", + "Add one": "ایک شامل کریں", + "Untitled Group": "بلا عنوان گروپ", + "Services": "خدمات", + "Discard": "رد کر دیں", + "steamApiKeyDescription": "سٹیم گیم سرور کی نگرانی کے لیے آپ کو سٹیم ویب API کلید درکار ہے۔ آپ اپنی API کلید یہاں رجسٹر کر سکتے ہیں: ", + "warning": "انتباہ", + "Please input title and content": "براہ کرم عنوان اور مواد درج کریں", + "Show Tags": "ٹیگز دکھائیں", + "No Monitors": "کوئی مانیٹر نہیں", + "Cancel": "منسوخ کریں", + "Powered by": "کی طرف سے طاقت", + "Custom CSS": "اپنی مرضی کے مطابق سی ایس ایس", + "deleteProxyMsg": "کیا آپ واقعی اس پراکسی کو تمام مانیٹر کے لیے حذف کرنا چاہتے ہیں؟", + "enableProxyDescription": "یہ پراکسی مانیٹر کی درخواستوں پر اس وقت تک اثر نہیں کرے گی جب تک کہ اسے فعال نہ کیا جائے۔ آپ ایکٹیویشن اسٹیٹس کے ذریعے تمام مانیٹرس سے پراکسی کو عارضی طور پر غیر فعال کر سکتے ہیں۔", + "setAsDefaultProxyDescription": "یہ پراکسی نئے مانیٹرز کے لئے ڈیفالٹ طور پر فعال ہوجائے گی۔ آپ اب بھی ہر مانیٹر کے لئے پراکسی کو الگ سے غیر فعال کرسکتے ہیں۔", + "Page Not Found": "صفحہ نہیں ملا", + "wayToGetCloudflaredURL": "({0} سے کلاؤڈ فلارڈ ڈاؤن لوڈ کریں)", + "Don't know how to get the token? Please read the guide:": "ٹوکن حاصل کرنے کا طریقہ نہیں جانتے؟ براہ کرم گائیڈ پڑھیں:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "اگر آپ فی الحال Cloudflare ٹنل کے ذریعے جڑ رہے ہیں تو موجودہ کنکشن ختم ہو سکتا ہے۔ کیا آپ واقعی اسے روکنا چاہتے ہیں؟ اس کی تصدیق کے لیے اپنا موجودہ پاس ورڈ ٹائپ کریں۔", + "RadiusCalledStationId": "اسٹیشن آئی ڈی کو کہتے ہیں", + "Certificate Expiry Notification": "سرٹیفکیٹ ختم ہونے کی اطلاع", + "Check how to config it for WebSocket": "اسے WebSocket کے لیے ترتیب دینے کا طریقہ چیک کریں", + "There might be a typing error in the address.": "ایڈریس میں ٹائپنگ کی غلطی ہو سکتی ہے۔", + "certificationExpiryDescription": "جب TLS سرٹیفکیٹ کی میعاد ختم ہو جاتی ہے تو HTTPS مانیٹر نوٹیفکیشن کو متحرک کرتے ہیں:", + "telegramMessageThreadID": "(اختیاری) میسج تھریڈ آئی ڈی", + "telegramMessageThreadIDDescription": "فورم کے ٹارگٹ میسج تھریڈ (موضوع) کے لیے اختیاری منفرد شناخت کنندہ؛ صرف فورم کے سپر گروپس کے لیے", + "chatIDNotFound": "چیٹ آئی ڈی نہیں ملی۔ براہ کرم پہلے اس بوٹ کو پیغام بھیجیں", + "disableCloudflaredNoAuthMsg": "آپ No Auth موڈ میں ہیں، پاس ورڈ کی ضرورت نہیں ہے۔", + "trustProxyDescription": "'X-Forwarded-*' ہیڈر پر بھروسہ کریں۔ اگر آپ صحیح کلائنٹ آئی پی حاصل کرنا چاہتے ہیں اور آپ کا اپ ٹائم کوما پراکسی جیسے Nginx یا Apache کے پیچھے ہے، تو آپ کو اسے فعال کرنا چاہیے۔", + "supportTelegramChatID": "براہ راست چیٹ / گروپ / چینل کی چیٹ آئی ڈی کو سپورٹ کریں", + "wayToGetTelegramChatID": "آپ بوٹ کو پیغام بھیج کر اور chat_id دیکھنے کے لیے اس URL پر جا کر اپنی چیٹ ID حاصل کر سکتے ہیں:", + "YOUR BOT TOKEN HERE": "یہاں آپ کا بوٹ ٹوکن", + "wayToGetLineNotifyToken": "آپ {0} سے ایک رسائی ٹوکن حاصل کر سکتے ہیں", + "Examples": "مثالیں", + "Running": "چل رہا ہے", + "Not running": "نہیں چل رہا ہے", + "Customize": "حسب ضرورت بنائیں", + "Custom Footer": "حسب ضرورت فوٹر", + "deleteStatusPageMsg": "کیا آپ واقعی اس اسٹیٹس پیج کو حذف کرنا چاہتے ہیں؟", + "Proxies": "پراکسیز", + "default": "طے شدہ", + "enabled": "فعال", + "setAsDefault": "ڈیفالٹ کے طور پر مقرر", + "proxyDescription": "پراکسیز کو کام کرنے کے لیے مانیٹر کو تفویض کیا جانا چاہیے۔", + "Certificate Chain": "سرٹیفکیٹ چین", + "Valid": "درست", + "Invalid": "غلط", + "User": "صارف", + "Installed": "انسٹال", + "Not installed": "انسٹال نہیں ہے", + "Remove Token": "ٹوکن کو ہٹا دیں", + "Start": "شروع کریں", + "Stop": "رک جاؤ", + "Add New Status Page": "نیا اسٹیٹس پیج شامل کریں", + "Slug": "سلگ", + "Accept characters:": "حروف کو قبول کریں:", + "startOrEndWithOnly": "صرف {0} سے شروع یا ختم کریں", + "No consecutive dashes": "کوئی لگاتار ڈیش نہیں ہے", + "Next": "اگلے", + "The slug is already taken. Please choose another slug.": "سلگ پہلے ہی لی گئی ہے۔ براہ کرم کوئی اور سلگ منتخب کریں۔", + "No Proxy": "کوئی پراکسی نہیں", + "Authentication": "تصدیق", + "HTTP Basic Auth": "HTTP بنیادی توثیق", + "New Status Page": "نیا اسٹیٹس پیج", + "Reverse Proxy": "ریورس پراکسی", + "Backup": "بیک اپ", + "About": "کے بارے میں", + "cloudflareWebsite": "Cloudflare ویب سائٹ", + "Message:": "پیغام:", + "HTTP Headers": "HTTP ہیڈر", + "Trust Proxy": "پراکسی پر اعتماد کریں", + "Other Software": "دوسرے سافٹ ویئر", + "For example: nginx, Apache and Traefik.": "مثال کے طور پر: nginx، Apache اور Traefik.", + "Please read": "مہربانی کر کے پڑھیں", + "Subject:": "مضمون:", + "Valid To:": "اس تاریخ تک کارآمد ہ:", + "Days Remaining:": "باقی دنوں:", + "Issuer:": "جاری کنندہ:", + "Fingerprint:": "فنگر پرنٹ:", + "No status pages": "کوئی اسٹیٹس پیجز نہیں", + "Domain Name Expiry Notification": "ڈومین نام کی میعاد ختم ہونے کی اطلاع", + "Proxy": "پراکسی", + "Date Created": "تاریخ تخلیق", + "Footer Text": "فوٹر ٹیکسٹ", + "Show Powered By": "شو کے ذریعہ تقویت یافتہ", + "Domain Names": "ڈومین کے نام", + "signedInDisp": "بطور {0} سائن ان", + "signedInDispDisabled": "توثیق غیر فعال۔", + "RadiusSecret": "رداس راز", + "RadiusSecretDescription": "کلائنٹ اور سرور کے درمیان مشترکہ راز", + "RadiusCalledStationIdDescription": "کہلائے گئے آلے کا شناخت کنندہ", + "RadiusCallingStationId": "کالنگ اسٹیشن آئی ڈی", + "RadiusCallingStationIdDescription": "کالنگ ڈیوائس کا شناخت کنندہ", + "API Username": "API صارف نام", + "API Key": "API کلید", + "Show update if available": "اگر دستیاب ہو تو اپ ڈیٹ دکھائیں", + "Also check beta release": "بیٹا ریلیز بھی چیک کریں", + "Using a Reverse Proxy?": "ایک ریورس پراکسی کا استعمال کرتے ہوئے؟", + "Steam Game Server": "بھاپ گیم سرور", + "Most likely causes:": "زیادہ تر ممکنہ وجوہات:", + "The resource is no longer available.": "وسیلہ اب دستیاب نہیں ہے۔", + "What you can try:": "تم کیا کوشش کر سکتے ہو:", + "Retype the address.": "ایڈریس دوبارہ ٹائپ کریں۔", + "Go back to the previous page.": "پچھلے صفحے پر واپس جائیں۔", + "Coming Soon": "جلد آرہا ہے", + "Connection String": "کنکشن سٹرنگ", + "Query": "استفسار", + "settingsCertificateExpiry": "TLS سرٹیفکیٹ کی میعاد ختم", + "Setup Docker Host": "ڈوکر ہوسٹ مرتب کریں", + "Connection Type": "کنکشن کی قسم", + "Docker Daemon": "ڈوکر ڈیمون", + "deleteDockerHostMsg": "کیا آپ واقعی تمام مانیٹر کے لیے اس ڈاکر ہوسٹ کو حذف کرنا چاہتے ہیں؟", + "socket": "ساکٹ", + "tcp": "TCP / HTTP", + "Docker Container": "ڈوکر کنٹینر", + "Container Name / ID": "کنٹینر کا نام / ID", + "Docker Host": "ڈاکر میزبان", + "Docker Hosts": "ڈاکر میزبان", + "Domain": "ڈومین", + "Workstation": "ورک سٹیشن", + "Packet Size": "پیکٹ کا سائز", + "Bot Token": "بوٹ ٹوکن", + "wayToGetTelegramToken": "آپ {0} سے ٹوکن حاصل کر سکتے ہیں۔", + "Chat ID": "چیٹ آئی ڈی", + "default: notify all devices": "‏ڈیفالٹ: تمام آلات کو مطلع کریں", + "telegramSendSilently": "خاموشی سے بھیجیں", + "telegramSendSilentlyDescription": "خاموشی سے پیغام بھیجتا ہے۔ صارفین کو بغیر آواز کے ایک اطلاع موصول ہوگی۔", + "Long-Lived Access Token": "طویل المدت رسائی ٹوکن", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "آپ کے پروفائل کے نام (نیچے بائیں) پر کلک کرکے اور نیچے تک سکرول کرکے طویل المدت رسائی کا ٹوکن بنایا جاسکتا ہے پھر ٹوکن بنائیں پر کلک کریں۔ ", + "backupOutdatedWarning": "فرسودہ: چونکہ بہت ساری خصوصیات شامل کی گئی ہیں اور یہ بیک اپ خصوصیت تھوڑا سا غیر برقرار ہے، یہ مکمل بیک اپ پیدا یا بحال نہیں کر سکتا۔", + "telegramProtectContent": "فارورڈنگ/محفوظ کرنے کی حفاظت کریں", + "telegramProtectContentDescription": "فعال ہونے پر، ٹیلیگرام میں بوٹ پیغامات کو آگے بھیجنے اور محفوظ کرنے سے محفوظ رکھا جائے گا۔", + "Home Assistant URL": "ہوم اسسٹنٹ یو آر ایل", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "آپ کے آلے/فون کا نام تلاش کرنے کے لیے \"اطلاعات\" تلاش کرنے کے لیے \"ڈیولپر ٹولز > سروسز\" کے تحت ہوم اسسٹنٹ میں اطلاعاتی خدمات کی فہرست مل سکتی ہے۔", + "Automations can optionally be triggered in Home Assistant:": "ہوم اسسٹنٹ میں آٹومیشن کو اختیاری طور پر متحرک کیا جا سکتا ہے:", + "Trigger type:": "محرک کی قسم:", + "Event type:": "تقریب کی قسم:", + "Event data:": "ایونٹ کا ڈیٹا:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "پھر ایک عمل کا انتخاب کریں، مثال کے طور پر منظر کو اس طرف تبدیل کریں جہاں RGB لائٹ سرخ ہو۔", + "Frontend Version": "فرنٹ اینڈ ورژن", + "Frontend Version do not match backend version!": "فرنٹ اینڈ ورژن پسدید ورژن سے مماثل نہیں ہے!", + "backupRecommend": "براہ کرم اس کے بجائے براہ راست والیوم یا ڈیٹا فولڈر (./data/) کا بیک اپ لیں۔", + "Optional": "اختیاری", + "or": "یا", + "Notification Service": "نوٹیفکیشن سروس", + "maintenanceStatus-inactive": "غیر فعال", + "maintenanceStatus-under-maintenance": "دیکھ بھال کے تحت", + "maintenanceStatus-scheduled": "طے شدہ", + "maintenanceStatus-ended": "ختم ہوا", + "recurringInterval": "وقفہ", + "Recurring": "بار چلنے والا", + "strategyManual": "دستی طور پر فعال/غیر فعال", + "warningTimezone": "یہ سرور کا ٹائم زون استعمال کر رہا ہے", + "weekdayShortMon": "پیر", + "weekdayShortWed": "بدھ", + "weekdayShortThu": "جمعرات", + "weekdayShortFri": "جمعہ", + "weekdayShortSat": "سات", + "weekdayShortSun": "سورج", + "dayOfWeek": "ہفتہ کا دن", + "dayOfMonth": "مہینے کا دن", + "lastDay": "آخری دن", + "lastDay1": "مہینے کا آخری دن", + "lastDay2": "مہینے کا دوسرا آخری دن", + "lastDay3": "مہینے کا تیسرا آخری دن", + "lastDay4": "مہینے کا چوتھا آخری دن", + "pauseMaintenanceMsg": "کیا آپ واقعی روکنا چاہتے ہیں؟", + "No Maintenance": "کوئی دیکھ بھال نہیں", + "weekdayShortTue": "منگل", + "Add New Tag": "نیا ٹیگ شامل کریں", + "Enable DNS Cache": "(فرسودہ) HTTP(s) مانیٹرز کے لیے DNS کیش کو فعال کریں", + "Effective Date Range": "مؤثر تاریخ کی حد (اختیاری)", + "Schedule Maintenance": "شیڈول کی بحالی", + "Date and Time": "تاریخ اور وقت", + "DateTime Range": "تاریخ کے وقت کی حد", + "loadingError": "ڈیٹا حاصل نہیں کیا جا سکتا، براہ کرم بعد میں دوبارہ کوشش کریں۔", + "Enable": "فعال", + "Disable": "غیر فعال کریں", + "dnsCacheDescription": "ہو سکتا ہے یہ کچھ IPv6 ماحول میں کام نہ کر رہا ہو، اگر آپ کو کوئی مسئلہ درپیش ہو تو اسے غیر فعال کر دیں۔", + "Single Maintenance Window": "سنگل مینٹیننس ونڈو", + "Maintenance Time Window of a Day": "ایک دن کی مینٹیننس ٹائم ونڈو", + "plugin": "پلگ ان | پلگ انز", + "install": "انسٹال کریں", + "statusPageRefreshIn": "اس میں ریفریش کریں: {0}", + "maintenanceStatus-unknown": "نامعلوم", + "Display Timezone": "ٹائم زون ڈسپلے کریں", + "Server Timezone": "سرور ٹائم زون", + "statusPageMaintenanceEndDate": "ختم", + "IconUrl": "آئیکن یو آر ایل", + "Cannot connect to the socket server": "ساکٹ سرور سے رابطہ قائم نہیں کیا جا سکتا", + "chromeExecutable": "کروم/کرومیم قابل عمل", + "chromeExecutableDescription": "ڈوکر صارفین کے لئے ، اگر کرومیم ابھی تک انسٹال نہیں ہے تو ، ٹیسٹ کے نتائج کو انسٹال کرنے اور ظاہر کرنے میں کچھ منٹ لگ سکتے ہیں۔ یہ ڈسک کی 1 جی بی جگہ لیتا ہے.", + "cronSchedule": "شیڈول: ", + "cronExpression": "کرون ایکسپریشن", + "invalidCronExpression": "غیر قانونی کرون اظہار: {0}", + "Home": "گھر", + "sameAsServerTimezone": "سرور ٹائم زون کی طرح", + "startDateTime": "آغاز کی تاریخ / وقت", + "endDateTime": "اختتام کی تاریخ / وقت", + "installing": "تنصیب", + "chromeExecutableAutoDetect": "آٹو کھوج", + "Edit Maintenance": "دیکھ بھال میں ترمیم کریں", + "Reconnecting...": "دوبارہ رابطہ قائم کرنا...", + "Request Timeout": "ٹائم آؤٹ کی درخواست کریں", + "timeoutAfter": "{0} سیکنڈ کے بعد ٹائم آؤٹ", + "styleElapsedTime": "دل کی دھڑکن بار کے نیچے گزرا ہوا وقت", + "webhookCustomBodyDesc": "درخواست کے لیے حسب ضرورت HTTP باڈی کی وضاحت کریں۔ ٹیمپلیٹ متغیرات {msg}، {heartbeat}، {monitor} قبول کیے گئے ہیں۔", + "filterActive": "فعال", + "Check/Uncheck": "چیک/ان چیک کریں", + "styleElapsedTimeShowNoLine": "دکھائیں (کوئی لائن نہیں)", + "styleElapsedTimeShowWithLine": "دکھائیں (لائن کے ساتھ)", + "filterActivePaused": "روک دیا گیا", + "webhookBodyPresetOption": "پیش سیٹ - {0}", + "webhookBodyCustomOption": "حسب ضرورت باڈی", + "Select": "منتخب کریں", + "selectedMonitorCount": "منتخب کردہ: {0}", + "tailscalePingWarning": "Tailscale Ping مانیٹر استعمال کرنے کے لیے، آپ کو Docker کے بغیر Uptime Kuma انسٹال کرنا ہوگا اور اپنے سرور پر Tailscale کلائنٹ بھی انسٹال کرنا ہوگا۔", + "uninstall": "ان انسٹال کریں", + "Invert Keyword": "مطلوبہ الفاظ کو الٹ دیں", + "Expected Value": "متوقع قدر", + "Json Query": "Json استفسار", + "setupDatabaseMariaDB": "ایک بیرونی ماریا ڈی بی ڈیٹا بیس سے جڑیں۔ آپ کو ڈیٹا بیس کنکشن کی معلومات سیٹ کرنے کی ضرورت ہے۔", + "wayToGetDiscordURL": "آپ اسے سرور کی ترتیبات -> انٹیگریشنز -> ویب ہکس دیکھیں -> نیو ویب ہک پر جاکر حاصل کرسکتے ہیں", + "setupDatabaseChooseDatabase": "آپ کون سا ڈیٹا بیس استعمال کرنا چاہیں گے؟", + "setupDatabaseEmbeddedMariaDB": "آپ کو کچھ بھی سیٹ کرنے کی ضرورت نہیں ہے۔ اس ڈاکر امیج نے آپ کے لیے ماریا ڈی بی کو خود بخود ایمبیڈ اور کنفیگر کر دیا ہے۔ اپ ٹائم کوما یونکس ساکٹ کے ذریعے اس ڈیٹا بیس سے جڑے گا۔", + "setupDatabaseSQLite": "ایک سادہ ڈیٹا بیس فائل، چھوٹے پیمانے پر تعیناتیوں کے لیے تجویز کردہ۔ v2.0.0 سے پہلے، Uptime Kuma SQLite کو بطور ڈیفالٹ ڈیٹا بیس استعمال کرتا تھا۔", + "dbName": "ڈیٹا بیس کا نام", + "enableNSCD": "تمام DNS درخواستوں کو کیش کرنے کے لیے NSCD (Name Service Cache Daemon) کو فعال کریں", + "Reset Token": "ٹوکن ری سیٹ کریں", + "From Email": "ای میل سے", + "Prefix Custom Message": "سابقہ حسب ضرورت پیغام", + "Hello @everyone is...": "ہیلو {'@'}ہر کوئی ہے…", + "wayToGetTeamsURL": "آپ ویب ہک URL {0} بنانے کا طریقہ سیکھ سکتے ہیں۔", + "pushViewCode": "پش مانیٹر کا استعمال کیسے کریں؟ (کوڈ دیکھیں)", + "pushOthers": "دوسرے", + "programmingLanguages": "پروگرامنگ کی زبانیں", + "Clone Monitor": "کلون مانیٹر", + "Clone": "کلون", + "uninstalling": "ان انسٹال کرنا", + "confirmUninstallPlugin": "کیا آپ واقعی اس پلگ ان کو ان انسٹال کرنا چاہتے ہیں؟", + "notificationRegional": "علاقائی", + "cloneOf": "{0} کا کلون", + "smtp": "ای میل (SMTP)", + "secureOptionNone": "کوئی نہیں / STARTTLS (25, 587)", + "Ignore TLS Error": "TLS کی خرابی کو نظر انداز کریں", + "emailCustomSubject": "حسب ضرورت موضوع", + "To Email": "ای میل کرنے کے لئے", + "smtpCC": "سی سی", + "smtpBCC": "بی سی سی", + "Discord Webhook URL": "اختلاف ابھک یو آر ایل", + "Bot Display Name": "بوٹ ڈسپلے کا نام", + "secureOptionTLS": "TLS (465)", + "emailCustomisableContent": "حسب ضرورت مواد", + "smtpLiquidIntroduction": "درج ذیل دو فیلڈز مائع ٹیمپلیٹنگ لینگویج کے ذریعے قابل نمونہ ہیں۔ براہ کرم استعمال کی ہدایات کے لیے {0} سے رجوع کریں۔ یہ دستیاب متغیرات ہیں:", + "leave blank for default subject": "پہلے سے طے شدہ مضمون کے لیے خالی چھوڑ دیں", + "emailCustomBody": "حسب ضرورت باڈی", + "leave blank for default body": "ڈیفالٹ باڈی کے لیے خالی چھوڑ دیں", + "emailTemplateServiceName": "سروس کا نام", + "emailTemplateHostnameOrURL": "میزبان نام یا URL", + "emailTemplateStatus": "حالت", + "noDockerHostMsg": "دستیاب نہیں ہے. پہلے ایک ڈاکر ہوسٹ مرتب کریں۔", + "emailTemplateLimitedToUpDownNotification": "صرف UP/DOWN دل کی دھڑکنوں کے لیے دستیاب ہے، ورنہ کالعدم", + "liquidIntroduction": "Templatability Liquid templating زبان کے ذریعے حاصل کی جاتی ہے۔ براہ کرم استعمال کی ہدایات کے لیے {0} سے رجوع کریں۔ یہ دستیاب متغیرات ہیں:", + "templateMsg": "اطلاع کا پیغام", + "templateHeartbeatJSON": "دل کی دھڑکن کو بیان کرنے والی چیز", + "templateMonitorJSON": "مانیٹر کی وضاحت کرنے والی چیز", + "templateLimitedToUpDownCertNotifications": "صرف UP/DOWN/سرٹیفکیٹ کی میعاد ختم ہونے کی اطلاعات کے لیے دستیاب ہے", + "templateLimitedToUpDownNotifications": "صرف UP/DOWN اطلاعات کے لیے دستیاب ہے", + "emailTemplateHeartbeatJSON": "دل کی دھڑکن کو بیان کرنے والی چیز", + "emailTemplateMsg": "اطلاع کا پیغام", + "DockerHostRequired": "براہ کرم اس مانیٹر کے لیے ڈاکر ہوسٹ سیٹ کریں۔", + "emailTemplateMonitorJSON": "مانیٹر کی وضاحت کرنے والی چیز", + "Recipients": "وصول کنندگان", + "wayToCheckSignalURL": "آپ یہ یو آر ایل چیک کر سکتے ہیں کہ اسے کیسے ترتیب دیا جائے:", + "Basic Settings": "بنیادی ترتیبات", + "Number": "نمبر", + "wayToGetZohoCliqURL": "آپ ویب ہک URL {0} بنانے کا طریقہ سیکھ سکتے ہیں۔", + "needSignalAPI": "آپ کے پاس ریسٹ API کے ساتھ سگنل کلائنٹ ہونا ضروری ہے۔", + "Access Token": "ٹوکن تک رسائی حاصل کریں", + "Channel access token": "چینل تک رسائی کا ٹوکن", + "Line Developers Console": "لائن ڈویلپرز کنسول", + "lineDevConsoleTo": "لائن ڈیولپرز کنسول - {0}", + "successKeywordExplanation": "MQTT کلیدی لفظ جسے کامیابی سمجھا جائے گا", + "User ID": "صارف کی شناخت", + "Search monitored sites": "نگرانی شدہ سائٹس تلاش کریں", + "successKeyword": "کامیابی کا کلیدی لفظ", + "statusPageSpecialSlugDesc": "خصوصی سلگ {0}: یہ صفحہ اس وقت دکھایا جائے گا جب کوئی سلگ فراہم نہ کیا جائے", + "settingUpDatabaseMSG": "ڈیٹا بیس کی ترتیب۔ اس میں کچھ وقت لگ سکتا ہے، براہ کرم صبر کریں۔", + "Add a new expiry notification day": "میعاد ختم ہونے کی اطلاع کا نیا دن شامل کریں", + "Remove the expiry notification": "میعاد ختم ہونے کی اطلاع کے دن کو ہٹا دیں", + "postToExistingThread": "موجودہ تھریڈ/فورم پوسٹ پر پوسٹ کریں", + "forumPostName": "فورم پوسٹ کا نام", + "threadForumPostID": "تھریڈ / فورم پوسٹ آئی ڈی", + "e.g. {discordThreadID}": "جیسے {discordThreadID}", + "Select message type": "پیغام کی قسم منتخب کریں", + "Send to channel": "چینل پر بھیجیں", + "Create new forum post": "نئی فورم پوسٹ بنائیں", + "whatHappensAtForumPost": "ایک نئی فورم پوسٹ بنائیں۔ یہ موجودہ پوسٹ میں پیغامات پوسٹ نہیں کرتا ہے۔ موجودہ پوسٹ میں پوسٹ کرنے کے لیے \"{option}\" کا استعمال کریں", + "wayToGetDiscordThreadId": "تھریڈ/فورم پوسٹ آئی ڈی حاصل کرنا چینل آئی ڈی حاصل کرنے کے مترادف ہے۔ آئی ڈی حاصل کرنے کے طریقے کے بارے میں مزید پڑھیں {0}", + "Channel access token (Long-lived)": "چینل تک رسائی کا ٹوکن (طویل المدت)", + "Host URL": "میزبان یو آر ایل", + "Refresh Interval": "تروتازہ کیا وقفہ", + "Refresh Interval Description": "اسٹیٹس کا صفحہ ہر {0} سیکنڈ میں مکمل سائٹ ریفریش کرے گا", + "ignoreTLSErrorGeneral": "کنکشن کے لیے TLS/SSL کی خرابی کو نظر انداز کریں", + "locally configured mail transfer agent": "مقامی طور پر ترتیب شدہ میل ٹرانسفر ایجنٹ", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "یا تو اس سرور کا میزبان نام درج کریں جس سے آپ جڑنا چاہتے ہیں یا اگر آپ {local_mta} استعمال کرنا چاہتے ہیں تو {localhost}" +} diff --git a/src/lang/uz.json b/src/lang/uz.json new file mode 100644 index 0000000..3e82daa --- /dev/null +++ b/src/lang/uz.json @@ -0,0 +1,97 @@ +{ + "languageName": "O'zbek tili", + "setupDatabaseChooseDatabase": "Qaysi ma'lumotlar ba'zasidan foydalanmoqchisiz?", + "setupDatabaseEmbeddedMariaDB": "Siz hech narsani sozlashga hojat yo'q. Bu docker obrazida MariaDB avtomatik ravishda o'rnatilgan va sozlangan. Uptime Kuma bu ma'lumotlar bazasiga unix socket orqali ulanadi.", + "setupDatabaseMariaDB": "Tashqi MariaDB ma'lumotlar bazasiga ulaning. Siz ma'lumotlar bazasi bilan bog'lanish ma'lumotlarini sozlashingiz kerak.", + "setupDatabaseSQLite": "Kichik miqyosli o'rnatishlar uchun tavsiya etiladigan sodda ma'lumotlar bazasi fayli. Uptime Kuma v2.0.0 dan oldin ma'lumotlar bazasi sifatida SQLite dan foydalangan.", + "dbName": "Ma'lumotlar ba'zasi nomi", + "Settings": "Sozlamalar", + "Dashboard": "Boshqaruv paneli", + "Help": "Yordam", + "New Update": "Yangi yangilanish", + "Language": "Til", + "Appearance": "Tashqi ko'rinish", + "Theme": "Mavzu", + "General": "Umumiy", + "Game": "O'yin", + "Primary Base URL": "Asosiy URL manzil", + "Version": "Versiya", + "List": "Ro'yxat", + "Home": "Uy", + "Add": "Qo'shish", + "Add New Monitor": "Yangi monitor qo'shish", + "Quick Stats": "Tezkor Statistika", + "Down": "Ishdan chiqqan", + "Up": "Ishga tushgan", + "Pending": "Kutilmoqda", + "statusMaintenance": "Xizmat ko'rsatish", + "Maintenance": "Xizmat ko'rsatish", + "Unknown": "Noma'lum", + "Reconnecting...": "Qayta ulanmoqda...", + "Passive Monitor Type": "Passiv Monitor turi", + "General Monitor Type": "Umumiy Monitor Turi", + "Specific Monitor Type": "Maxsus Monitor turi", + "markdownSupported": "Belgilash tili sintaksisi qo'llab-quvvatlanadi", + "pauseDashboardHome": "Tanaffus", + "Pause": "Tanaffus", + "Name": "Nom", + "Status": "Holat", + "DateTime": "Sana va vaqt", + "Message": "Xabar", + "Resume": "Davom ettirish", + "Edit": "Tahrirlash", + "Delete": "O'chirish", + "Current": "Hozirgi", + "Uptime": "Ish vaqti", + "Cert Exp.": "Sertifikat AQM.", + "Monitor": "Monitor | Monitorlar", + "day": "kun | kunlar", + "-day": "-kun", + "hour": "soat", + "-hour": "-soat", + "Response": "Javob", + "Ping": "Aloqani tekshirish", + "Invert Keyword": "Teskari Kalit so'z", + "Json Query": "Json So'rovi", + "Friendly Name": "Do'stona Nom", + "URL": "URL", + "Hostname": "Xosting nomi", + "Port": "Port", + "Heartbeat Interval": "Signal almashinuvi davomiyligi", + "Request Timeout": "So'rov muddati tugashi", + "Retries": "Qayta urinishlar", + "Heartbeat Retry Interval": "Signal almashinuvining takrorlanishlar davomiyligi", + "Advanced": "Murakkab", + "checkEverySecond": "Har {0} soniyada tekshiring", + "retryCheckEverySecond": "Har {0} soniyada qayta urining", + "resendEveryXTimes": "Har {0} martada qayta yuborish", + "resendDisabled": "Qayta yuborish o'chirilgan", + "Check Update On GitHub": "Github-da yangilanishni tekshiring", + "Cannot connect to the socket server": "Socket serverga ulanib bo'lmadi", + "No important events": "Muhim voqealar yo'q", + "Monitor Type": "Monitor Turi", + "Keyword": "Kalit so'z", + "Expected Value": "Kutilayotgan qiymat", + "timeoutAfter": "{0} soniyadan keyin kutish vaqti tugaydi", + "Resend Notification if Down X times consecutively": "Agar X marta ishdan chiqsa, xabarnomani qayta yuboring", + "Setup Notification": "Bildirishnomani sozlang", + "ignoreTLSError": "HTTPS veb-saytlari uchun TLS/SSL xatolariga e'tibor bermang", + "retriesDescription": "Xizmat o'chirilgan deb belgilanishi va bildirishnoma yuborilishidan oldin maksimal qayta urinishlar", + "upsideDownModeDescription": "Agar server ishlash holatida bo'lsa, uni O'CHIRILGAN deb hisoblang", + "maxRedirectDescription": "Yo'naltirishlar sonining maksimal miqdori. Yo'naltirishlarni o'chirish uchun 0 ga sozlang.", + "Upside Down Mode": "Teskari rejim", + "Accepted Status Codes": "Qabul qilingan holat kodlari", + "Push URL": "URL ni yuborish", + "Max. Redirects": "Qayta yo'naltirishlar chegarasi", + "needPushEvery": "Bu URL manziliga har {0} soniyada murojaat qilishingiz kerak.", + "pushOptionalParams": "Ixtiyoriy parametrlar: {0}", + "pushViewCode": "Push monitoridan qanday foydalanish kerak? (Kodni ko'rish)", + "pushOthers": "Boshqalar", + "programmingLanguages": "Dasturlash tillari", + "Save": "Saqlash", + "Notifications": "Bildirishnomalar", + "Not available, please setup.": "Mavjud emas, sozlang.", + "Light": "Yorug'", + "Dark": "Qorung'u", + "Auto": "Avto" +} diff --git a/src/lang/vi-VN.json b/src/lang/vi-VN.json new file mode 100644 index 0000000..7853b31 --- /dev/null +++ b/src/lang/vi-VN.json @@ -0,0 +1,649 @@ +{ + "languageName": "Tiếng Việt", + "checkEverySecond": "Kiểm tra mỗi {0} giây", + "retryCheckEverySecond": "Thử lại mỗi {0} giây", + "retriesDescription": "Số lần thử lại tối đa trước khi dịch vụ được đánh dấu là down và gửi thông báo", + "ignoreTLSError": "Bỏ qua các lỗi TLS/SSL với các web HTTPS", + "upsideDownModeDescription": "Chế độ đảo ngược. nếu dịch vụ có thể truy cập được nghĩa là DOWN.", + "maxRedirectDescription": "Số lần chuyển hướng (redirect) tối đa. Đặt thành 0 để tắt chuyển hướng.", + "acceptedStatusCodesDescription": "Chọn mã trạng thái được coi là phản hồi thành công.", + "passwordNotMatchMsg": "Mật khẩu nhập lại không khớp.", + "notificationDescription": "Vui lòng chỉ định một kênh thông báo.", + "keywordDescription": "Từ khoá tìm kiếm phản hồi ở dạng html hoặc JSON, có phân biệt chữ HOA - thường", + "pauseDashboardHome": "Tạm dừng", + "deleteMonitorMsg": "Bạn chắc chắn muốn xóa kênh theo dõi này chứ?", + "deleteNotificationMsg": "Bạn có chắc chắn muốn xóa kênh thông báo này cho tất cả kênh theo dõi?", + "resolverserverDescription": "Cloudflare là máy chủ mặc định, bạn có thể thay đổi bất cứ lúc nào.", + "rrtypeDescription": "Hãy chọn RR-Type mà bạn muốn giám sát", + "pauseMonitorMsg": "Bạn chắc chắn muốn tạm dừng chứ?", + "enableDefaultNotificationDescription": "Bật làm mặc định cho mọi kênh theo dõi mới về sau. Bạn vẫn có thể tắt thông báo riêng cho từng kênh theo dõi.", + "clearEventsMsg": "Bạn chắc chắn muốn xoá TẤT CẢ sự kiện cho kênh theo dõi này chứ?", + "clearHeartbeatsMsg": "Bạn chắc chắn muốn xoá TẤT CẢ heartbeats cho kênh theo dõi này chứ?", + "confirmClearStatisticsMsg": "Bạn chắc chắn muốn xoá TẤT CẢ số liệu thống kê?", + "importHandleDescription": "Chọn 'Giữ lại' nếu bạn muốn bỏ qua mọi kênh theo dõi và kênh thông báo trùng tên. 'Ghi đè' sẽ ghi đè lên tất cả các kênh theo dõi và kênh thông báo.", + "confirmImportMsg": "Bạn có chắc chắn muốn khôi phục bản bản sao lưu này không?.", + "twoFAVerifyLabel": "Vui lòng nhập mã token của bạn để xác minh rằng xác thực 2 lớp (2FA) đang hoạt động", + "tokenValidSettingsMsg": "Mã token hợp lệ! Bạn có thể lưu cài đặt xác thực 2 lớp (2FA) bây giờ.", + "confirmEnableTwoFAMsg": "Bạn chắc chắn muốn bật xác thực 2 lớp (2FA) chứ?", + "confirmDisableTwoFAMsg": "Bạn chắc chắn muốn tắt xác thực 2 lớp (2FA) chứ?", + "Settings": "Cài đặt", + "Dashboard": "Trang tổng quan", + "New Update": "Bản cập nhật mới", + "Language": "Ngôn ngữ", + "Appearance": "Giao diện", + "Theme": "Theme", + "General": "Chung", + "Primary Base URL": "URL chính", + "Version": "Phiên bản", + "Check Update On GitHub": "Kiểm tra bản cập nhật mới trên GitHub", + "List": "List", + "Add": "Thêm", + "Add New Monitor": "Thêm mới kênh theo dõi", + "Quick Stats": "Thống kê nhanh", + "Up": "Up", + "Down": "Down", + "Pending": "Chờ xử lý", + "Unknown": "Không xác định", + "Pause": "Tạm dừng", + "Name": "Tên", + "Status": "Trạng thái", + "DateTime": "Ngày tháng", + "Message": "Trạng thái request", + "No important events": "Không có sự kiện quan trọng nào", + "Resume": "Khôi phục", + "Edit": "Sửa", + "Delete": "Xoá", + "Current": "Hiện tại", + "Uptime": "Uptime", + "Cert Exp.": "Hạn chứng chỉ.", + "day": "ngày", + "-day": "-ngày", + "hour": "giờ", + "-hour": "-giờ", + "Response": "Phản hồi", + "Ping": "Ping", + "Monitor Type": "Kiểu kênh theo dõi", + "Keyword": "Từ khoá", + "Friendly Name": "Tên rút gọn", + "URL": "URL", + "Hostname": "Hostname", + "Port": "Port", + "Heartbeat Interval": "Tần suất kiểm tra", + "Retries": "Thử lại", + "Heartbeat Retry Interval": "Tần suất kiểm tra lại", + "Advanced": "Nâng cao", + "Upside Down Mode": "Chế độ đảo ngược", + "Max. Redirects": "Số chuyển hướng tối đa", + "Accepted Status Codes": "Status Code Được Chấp Nhận", + "Push URL": "Push URL", + "needPushEvery": "Bạn nên gọi URL mỗi {0} giây.", + "pushOptionalParams": "Tham số tuỳ chọn: {0}", + "Save": "Lưu", + "Notifications": "Thông báo", + "Not available, please setup.": "Không tồn tại, hãy cài đặt.", + "Setup Notification": "Thiết lập thông báo", + "Light": "Sáng", + "Dark": "Tối", + "Auto": "Tự động", + "Theme - Heartbeat Bar": "Giao Diện - Thanh Trạng Thái", + "Normal": "Bình thường", + "Bottom": "Phía dưới", + "None": "Không", + "Timezone": "Múi giờ", + "Search Engine Visibility": "Khả năng hiển thị với các công cụ tìm kiếm", + "Allow indexing": "Cho phép index", + "Discourage search engines from indexing site": "Ngăn chặn các công cụ tìm kiếm index trang", + "Change Password": "Thay đổi mật khẩu", + "Current Password": "Mật khẩu hiện tại", + "New Password": "Mật khẩu mới", + "Repeat New Password": "Lặp lại mật khẩu mới", + "Update Password": "Cập nhật mật khẩu", + "Disable Auth": "Tắt xác minh đăng nhập", + "Enable Auth": "Bật xác minh đăng nhập", + "disableauth.message1": "Bạn có muốn {disableAuth} không?", + "disable authentication": "tắt xác thực đăng nhập", + "disableauth.message2": "Điều này rất nguy hiểm {intendThirdPartyAuth} cũng có thể truy cập và cướp quyền điều khiển.", + "where you intend to implement third-party authentication": "BẤT KỲ AI", + "Please use this option carefully!": "Vui lòng cẩn thận!", + "Logout": "Đăng xuất", + "Leave": "Rời", + "I understand, please disable": "Tôi hiểu, làm ơn hãy tắt", + "Confirm": "Xác nhận", + "Yes": "Có", + "No": "Không", + "Username": "Tài khoản", + "Password": "Mật khẩu", + "Remember me": "Lưu phiên đăng nhập", + "Login": "Đăng nhập", + "No Monitors, please": "Không có kênh theo dõi nào", + "add one": "Thêm mới", + "Notification Type": "Kiểu thông báo", + "Email": "Email", + "Test": "Thử", + "Certificate Info": "Thông tin Certificate", + "Resolver Server": "Máy chủ Resolver", + "Resource Record Type": "Loại bản ghi", + "Last Result": "Kết quả cuối cùng", + "Create your admin account": "Tạo tài khoản quản trị", + "Repeat Password": "Lặp lại mật khẩu", + "Import Backup": "Khôi phục bản sao lưu", + "Export Backup": "Xuất bản sao lưu", + "Export": "Xuất", + "Import": "Nhập", + "respTime": "Thời gian phản hồi (ms)", + "notAvailableShort": "N/A", + "Default enabled": "Mặc định bật", + "Apply on all existing monitors": "Áp dụng cho tất cả kênh theo dõi đang có", + "Create": "Tạo", + "Clear Data": "Xoá dữ liệu", + "Events": "Sự kiện", + "Heartbeats": "Heartbeats", + "Auto Get": "Tự động lấy", + "backupDescription": "Sao lưu tất cả các kênh theo dõi và tất cả các thông báo vào một file định dạng JSON.", + "backupDescription2": "Lưu ý: Không bao gồm dữ liệu lịch sử các sự kiện.", + "backupDescription3": "Hãy lưu giữ file này cẩn thận, trong file đó chứa cả các token thông báo.", + "alertNoFile": "Hãy chọn file để khôi phục.", + "alertWrongFileType": "Hãy chọn file định dạng JSON.", + "Clear all statistics": "Xoá tất cả thống kê", + "Skip existing": "Giữ lại", + "Overwrite": "Ghi đè", + "Options": "Tuỳ chọn", + "Keep both": "Giữ lại cả hai", + "Verify Token": "Xác minh Token", + "Setup 2FA": "Thiết lập xác thực 2 lớp (2FA)", + "Enable 2FA": "Bật xác thực 2 lớp (2FA)", + "Disable 2FA": "Tắt xác thực 2 lớp (2FA)", + "2FA Settings": "Cài đặt xác thực 2 lớp (2FA)", + "Two Factor Authentication": "Xác thực hai yếu tố", + "Active": "Hoạt động", + "Inactive": "Ngừng hoạt động", + "Token": "Token", + "Show URI": "Hiển thị URI", + "Tags": "Tags", + "Add New below or Select...": "Thêm mới ở dưới hoặc Chọn…", + "Tag with this name already exist.": "Tag với tên này đã tồn tại.", + "Tag with this value already exist.": "Tag với giá trị này đã tồn tại.", + "color": "Màu sắc", + "value (optional)": "Giá trị (tuỳ chọn)", + "Gray": "Xám", + "Red": "Đỏ", + "Orange": "Cam", + "Green": "Xanh lá", + "Blue": "Xanh da trời", + "Indigo": "Chàm", + "Purple": "Tím", + "Pink": "Hồng", + "Search...": "Tìm kiếm…", + "Avg. Ping": "Ping trung bình", + "Avg. Response": "Phản hồi trung bình", + "Entry Page": "Entry Page", + "statusPageNothing": "Chưa có thông tin gì, hãy thêm nhóm kênh theo dõi hoặc kênh theo dõi.", + "No Services": "Không có dịch vụ", + "All Systems Operational": "Tất cả các hệ thống hoạt động bình thường", + "Partially Degraded Service": "Có hệ thống bị ngưng", + "Degraded Service": "Toàn bộ hệ thống bị ngưng", + "Add Group": "Thêm nhóm", + "Add a monitor": "Thêm kênh theo dõi", + "Edit Status Page": "Sửa trang trạng thái", + "Go to Dashboard": "Đi tới Dashboard", + "Status Page": "Trang trạng thái", + "Status Pages": "Trang trạng thái", + "defaultNotificationName": "My {notification} Alerts ({number})", + "here": "tại đây", + "Required": "Bắt buộc", + "telegram": "Telegram", + "Bot Token": "Bot Token", + "wayToGetTelegramToken": "Bạn có thể lấy mã token từ", + "Chat ID": "Chat ID", + "supportTelegramChatID": "Hỗ trợ chat trực tiếp / Nhóm / Kênh Chat ID", + "wayToGetTelegramChatID": "Bạn có thể lấy chat id của mình bằng cách gửi tin nhắn tới bot và truy cập url này để xem chat_id:", + "YOUR BOT TOKEN HERE": "MÃ BOT TOKEN CỦA BẠN", + "chatIDNotFound": "Không tìm thấy Chat ID, vui lòng gửi tin nhắn cho bot này trước", + "webhook": "Webhook", + "Post URL": "URL webhook", + "Content Type": "Loại nội dung", + "webhookJsonDesc": "{0} tương thích với máy chủ HTTP ví dụ như Express.js", + "webhookFormDataDesc": "{multipart} tương thích với máy chủ PHP, bạn chỉ cần phân tích cú pháp json bằng {decodeFunction}", + "smtp": "Email (SMTP)", + "secureOptionNone": "None/STARTTLS(25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Bỏ qua lỗi TLS", + "From Email": "Email gửi", + "emailCustomSubject": "Tuỳ chỉnh tiêu đề", + "To Email": "Email nhận", + "smtpCC": "CC", + "smtpBCC": "BCC", + "discord": "Discord", + "Discord Webhook URL": "Discord Webhook URL", + "wayToGetDiscordURL": "Để lấy Discord, hãy vào: Server Settings -> Integrations -> Create Webhook", + "Bot Display Name": "Tên hiển thị của BOT", + "Prefix Custom Message": "Tiền tố tin nhắn tuỳ chọn", + "Hello @everyone is...": "Xin chào {'@'} mọi người đang…", + "teams": "Microsoft Teams", + "Webhook URL": "Webhook URL", + "wayToGetTeamsURL": "Bạn có thể học cách tạo webhook url {0}.", + "signal": "Tín hiệu", + "Number": "Số", + "Recipients": "Người nhận", + "needSignalAPI": "Bạn cần một tín hiệu kết nối với REST API.", + "wayToCheckSignalURL": "Bạn có thể kiểm tra URL này để xem cách thiết lập:", + "signalImportant": "QUAN TRỌNG: Bạn không thể kết hợp các nhóm và số trong người nhận!", + "gotify": "Gotify", + "Application Token": "Mã Token ứng dụng", + "Server URL": "URL máy chủ", + "Priority": "Mức ưu tiên", + "slack": "Slack", + "Icon Emoji": "Icon Emoji", + "Channel Name": "Tên Channel", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "Thông tin thêm về webhook trên: {0}", + "aboutChannelName": "Nhập tên kênh trên {0} trường Channel Name nếu bạn muốn bỏ qua kênh webhook. vd: #other-channel", + "aboutKumaURL": "Nếu bạn để trống trường Uptime Kuma URL, mặc định sẽ là trang Project Github.", + "emojiCheatSheet": "Bảng tra cứu Emoji: {0}", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "Push by Techulus", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (Hỗ trợ trên 50 dịch vụ thông báo)", + "GoogleChat": "Google Chat (Google Workspace only)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "User Key", + "Device": "Thiết bị", + "Message Title": "Tiêu đề tin nhắn", + "Notification Sound": "Âm thanh thông báo", + "More info on:": "Thông tin chi tiết tại: {0}", + "pushoverDesc1": "Mức ưu tiên khẩn cấp (2) có thời gian chờ mặc định là 30 giây giữa các lần thử lại và sẽ hết hạn sau 1 giờ.", + "pushoverDesc2": "Nếu bạn muốn gửi thông báo đến các thiết bị khác nhau, hãy điền vào trường Thiết bị.", + "SMS Type": "SMS Type", + "octopushTypePremium": "Premium (Nhanh - Khuyến nghị nên dùng cho cảnh báo)", + "octopushTypeLowCost": "Giá rẻ (Chậm, thỉnh thoảng bị chặn)", + "checkPrice": "Kiểm tra giá {0}:", + "apiCredentials": "API credentials", + "octopushLegacyHint": "Bạn muốn sử dụng phiên bản cũ của Octopush (2011-2020) hay phiên bản mới?", + "Check octopush prices": "Kiểm tra giá octopush {0}.", + "octopushPhoneNumber": "Số điện thoại (Định dạng intl, vd : +84692341165) ", + "octopushSMSSender": "SMS người gửi : 3-11 ký tự chữ, số và dấu cách (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea ID thiết bị", + "Apprise URL": "Apprise URL", + "Example:": "Ví dụ: {0}", + "Read more:": "Đọc thêm: {0}", + "Status:": "Trạng thái: {0}", + "Read more": "Đọc thêm", + "appriseInstalled": "Đã cài đặt Apprise.", + "appriseNotInstalled": "Chưa cài đặt Apprise. {0}", + "Access Token": "Token truy cập", + "Channel access token": "Token kênh truy cập", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console - {0}", + "Basic Settings": "Cài đặt cơ bản", + "User ID": "User ID", + "Messaging API": "Messaging API", + "wayToGetLineChannelToken": "Trước tiên, hãy truy cập {0},tạo nhà cung cấp và kênh (Messaging API), sau đó bạn có thể nhận mã token truy cập kênh và id người dùng từ các mục menu được đề cập ở trên.", + "Icon URL": "Icon URL", + "aboutIconURL": "Bạn có thể cung cấp liên kết đến ảnh trong \"Icon URL\" để ghi đè ảnh hồ sơ mặc định. Sẽ không được sử dụng nếu Biểu tượng cảm xúc được thiết lập.", + "aboutMattermostChannelName": "Bạn có thể ghi đè kênh mặc định mà webhook đăng lên bằng cách nhập tên kênh vào trường \"Channel Name\". Điều này cần được bật trong cài đặt Mattermost webhook. Ví dụ: #other-channel", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - rẻ nhưng chậm và thường xuyên quá tải. Chỉ dành cho người Ba Lan.", + "promosmsTypeFlash": "SMS FLASH - Tin nhắn sẽ tự động hiển thị trên thiết bị của người nhận. Chỉ dành cho người Ba Lan.", + "promosmsTypeFull": "SMS FULL - SMS cao cấp, Bạn có thể sử dụng Tên Người gửi (Bạn cần đăng ký tên trước). Đáng tin cậy cho các cảnh báo.", + "promosmsTypeSpeed": "SMS SPEED - Ưu tiên cao nhất trong hệ thống. Rất nhanh chóng và đáng tin cậy nhưng tốn kém, (giá gấp đôi SMS FULL).", + "promosmsPhoneNumber": "Số điện thoại (Bỏ qua mã vùng với người Ba Lan)", + "promosmsSMSSender": "SMS Tên người gửi: Tên đã đăng ký trước hoặc tên mặc định: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu WebHookUrl", + "matrixHomeserverURL": "Homeserver URL (với http(s):// và port tuỳ chỉnh)", + "Internal Room Id": "Room ID Nội bộ", + "matrixDesc1": "Bạn có thể tìm thấy room ID nội bộ bằng cách tìm trong mục advanced của phần room settings trong Matrix client của bạn. Nó có dạng giống như !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Bạn nên tạo người dùng mới và đừng sử dụng mã token truy cập của Matrix user vì nó sẽ cho phép truy cập toàn quyền vào tài khoản của bạn và tất cả các phòng bạn đã tham gia. Thay vào đó, hãy tạo một người dùng mới và chỉ mời người đó vào phòng mà bạn muốn nhận thông báo. Bạn có thể lấy được mã token truy cập bằng cách chạy {0}", + "Method": "Method", + "Body": "Body", + "Headers": "Headers", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "Header request không hợp lệ JSON: ", + "BodyInvalidFormat": "Tequest body không hợp lệ JSON: ", + "Monitor History": "Lịch sử kênh theo dõi", + "clearDataOlderThan": "Giữ dữ liệu lịch sử kênh theo dõi {0} ngày.", + "PasswordsDoNotMatch": "Passwords không khớp.", + "records": "records", + "One record": "One record", + "steamApiKeyDescription": "Để theo dõi các Steam Game Server bạn cần một Steam Web-API key. Bạn có thể đăng ký API key tại đây: ", + "Current User": "User hiện tại", + "topic": "Topic", + "topicExplanation": "MQTT topic to monitor", + "successMessage": "Success Message", + "successMessageExplanation": "MQTT message that will be considered as success", + "recent": "Gần đây", + "Done": "Hoàn thành", + "Info": "Thông tin", + "Security": "Bảo mật", + "Steam API Key": "Steam API Key", + "Shrink Database": "Shrink Database", + "Pick a RR-Type...": "Pick a RR-Type…", + "Pick Accepted Status Codes...": "Chọn các Codes trạng thái chấp nhận được…", + "Default": "Mặc định", + "HTTP Options": "Tuỳ chọn HTTP", + "Create Incident": "Tạo Incident", + "Title": "Tiêu đề", + "Content": "Nội dung", + "Style": "Style", + "info": "thông tin", + "warning": "cảnh báo", + "danger": "nguy hiểm", + "primary": "cơ sở", + "light": "sáng", + "dark": "tối", + "Post": "Post", + "Please input title and content": "Hãy nhập tiêu đề và nội dung", + "Created": "Đã tạo", + "Last Updated": "Cập nhật mới nhất", + "Unpin": "Bỏ ghim", + "Switch to Light Theme": "Chuyển sang giao diện Sáng", + "Switch to Dark Theme": "Chuyển sang giao diện Tối", + "Show Tags": "Hiện Tags", + "Hide Tags": "Ẩn Tags", + "Description": "Mô tả", + "No monitors available.": "Không có kênh theo dõi nào.", + "Add one": "Thêm mới", + "No Monitors": "Không có kênh theo dõi", + "Untitled Group": "Nhóm không có tiêu đề", + "Services": "Dịch vụ", + "Discard": "Bỏ", + "Cancel": "Hủy", + "Powered by": "Được cung cấp bởi", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API Username (incl. webapi_ prefix)", + "serwersmsAPIPassword": "API Password", + "serwersmsPhoneNumber": "Số điện thoại", + "serwersmsSenderName": "Tên người gửi SMS (Đã đăng ký qua portal)", + "stackfield": "Stackfield", + "Customize": "Customize", + "Custom Footer": "Custom Footer", + "Custom CSS": "Custom CSS", + "smtpDkimSettings": "Cài đặt xác thực Email(DKIM)", + "smtpDkimDesc": "Xem hướng dẫn tại {0}.", + "documentation": "Nodemailer DKIM", + "smtpDkimDomain": "Mail domain", + "smtpDkimKeySelector": "DKIM Key Selector", + "smtpDkimPrivateKey": "Private Key", + "smtpDkimHashAlgo": "Hash Algorithm (Tuỳ chọn)", + "smtpDkimheaderFieldNames": "Header Keys to sign (Tuỳ chọn)", + "smtpDkimskipFields": "Header Keys not to sign (Tuỳ chọn)", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API Endpoint", + "alertaEnvironment": "Environment", + "alertaApiKey": "API Key", + "alertaAlertState": "Alert State", + "alertaRecoverState": "Recover State", + "deleteStatusPageMsg": "Bạn có chắc chắn muốn xoá trang status này?", + "Proxies": "Proxies", + "default": "Mặc định", + "enabled": "Enabled", + "setAsDefault": "Set As Default", + "deleteProxyMsg": "Bạn muốn xoá proxy này cho tất cả monitors?", + "proxyDescription": "Proxies must be assigned to a monitor to function.", + "enableProxyDescription": "Proxy này chưa ảnh hưởng tới monitor requests cho tới khi được activated. Bạn có thể tạm thời tắt proxy cho tất cả monitors bằng trạng thái activation.", + "setAsDefaultProxyDescription": "Proxy này sẽ bật mặc định cho tất cả monitors mới. Bạn có thể tắt riêng lẻ proxy trên mỗi monitor.", + "Certificate Chain": "Certificate Chain", + "Valid": "Hợp lệ", + "Invalid": "Không hợp lệ", + "AccessKeyId": "AccessKey ID", + "SecretAccessKey": "AccessKey Secret", + "PhoneNumbers": "PhoneNumbers", + "TemplateCode": "TemplateCode", + "SignName": "SignName", + "Sms template must contain parameters: ": "Sms template must contain parameters: ", + "Bark Endpoint": "Bark Endpoint", + "WebHookUrl": "WebHookUrl", + "SecretKey": "SecretKey", + "For safety, must use secret key": "Để an toàn, hãy dùng secret key", + "Device Token": "Device Token", + "Platform": "Platform", + "Huawei": "Huawei", + "High": "High", + "Retry": "Thử lại", + "Topic": "Topic", + "WeCom Bot Key": "WeCom Bot Key", + "Setup Proxy": "Setup Proxy", + "Proxy Protocol": "Proxy Protocol", + "Proxy Server": "Proxy Server", + "Proxy server has authentication": "Proxy server has authentication", + "User": "User", + "Installed": "Installed", + "Not installed": "Not installed", + "Running": "Running", + "Not running": "Not running", + "Remove Token": "Remove Token", + "Start": "Start", + "Stop": "Stop", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Thêm mới Status Page", + "Slug": "Slug", + "Accept characters:": "Accept characters:", + "startOrEndWithOnly": "Start or end with {0} only", + "No consecutive dashes": "No consecutive dashes", + "Next": "Next", + "The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.", + "No Proxy": "No Proxy", + "HTTP Basic Auth": "HTTP Basic Auth", + "New Status Page": "New Status Page", + "Page Not Found": "Page Not Found", + "Reverse Proxy": "Reverse Proxy", + "Backup": "Backup", + "About": "Về", + "wayToGetCloudflaredURL": "(Download cloudflared from {0})", + "cloudflareWebsite": "Cloudflare Website", + "Message:": "Message:", + "Don't know how to get the token? Please read the guide:": "Chưa biết cách lấy token? Xem hướng dẫn tại:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Nếu bạn đang dùng Cloudflare Tunnel, kết nối hiện tại có thể đang bị mất. Bạn có muốn dừng lại? Nhập lại password để xác nhận.", + "Other Software": "Phần mềm khác", + "For example: nginx, Apache and Traefik.": "Ví dụ: Nginx, Apache hay Traefik.", + "Please read": "Hãy xem qua", + "Subject:": "Subject:", + "Valid To:": "Valid To:", + "Days Remaining:": "Số ngày còn lại:", + "Issuer:": "Issuer:", + "Fingerprint:": "Fingerprint:", + "No status pages": "No status pages", + "Domain Name Expiry Notification": "Cảnh báo hết hạn tên miền", + "Proxy": "Proxy", + "Date Created": "Ngày khởi tạo", + "onebotHttpAddress": "OneBot HTTP Address", + "onebotMessageType": "OneBot Message Type", + "onebotGroupMessage": "Group", + "onebotPrivateMessage": "Private", + "onebotUserOrGroupId": "Group/User ID", + "onebotSafetyTips": "Để đảm bảo an toàn, hãy thiết lập access token", + "Custom": "Tùy chỉnh", + "Add New Tag": "Thêm thẻ mới", + "webhookAdditionalHeadersDesc": "Đặt header bổ sung được gửi cùng với webhook. Mỗi header nên được xác định bằng một JSON key/value.", + "error": "lỗi", + "HTTP Headers": "HTTP Headers", + "recurringIntervalMessage": "Chạy một lần mỗi ngày | Chạy một lần mỗi {0} ngày", + "Retype the address.": "Nhập lại địa chỉ.", + "enableGRPCTls": "Cho phép gửi yêu cầu gRPC với kết nối TLS", + "affectedMonitorsDescription": "Chọn kênh theo dõi bị ảnh hưởng bởi lịch bảo trì này", + "statusMaintenance": "Bảo trì", + "Maintenance": "Bảo trì", + "Affected Monitors": "Kênh theo dõi bị ảnh hưởng", + "Schedule maintenance": "Thêm lịch bảo trì", + "markdownSupported": "Hỗ trợ cú pháp Markdown. Nếu sử dụng HTML, hãy tránh khoảng trắng đầu dòng để tránh các vấn đề về định dạng.", + "Start of maintenance": "Bắt đầu bảo trì", + "All Status Pages": "Tất cả các trang trạng thái", + "Select status pages...": "Chọn trang trạng thái…", + "Certificate Expiry Notification": "Thông báo hết hạn chứng chỉ", + "Show update if available": "Hiển thị cập nhật (nếu có)", + "What you can try:": "Bạn có thể thử:", + "trustProxyDescription": "Tin tưởng các header 'X-Forwarded-*'. Nếu bạn muốn lấy đúng IP máy khách và Uptime Kuma của bạn đứng sau một proxy như Nginx hoặc Apache, bạn nên kích hoạt tính năng này.", + "webhookAdditionalHeadersTitle": "Header bổ sung", + "Help": "Trợ giúp", + "Game": "Trò chơi", + "Pick Affected Monitors...": "Chọn kênh theo dõi…", + "statusPageRefreshIn": "Làm mới trong: {0}", + "Authentication": "Xác thực", + "Using a Reverse Proxy?": "Bạn đang sử dụng Reverse Proxy?", + "Check how to config it for WebSocket": "Kiểm tra cách cấu hình nó cho WebSocket", + "Go back to the previous page.": "Quay trở lại trang trước.", + "wayToGetLineNotifyToken": "Bạn có thể lấy access token từ {0}", + "Resend Notification if Down X times consecutively": "Gửi lại thông báo nếu Down X lần liên tiếp", + "Cannot connect to the socket server": "Không thể kết nối đến máy chủ socket", + "Home": "Trang chủ", + "Reconnecting...": "Đang kết nối lại...", + "Trust Proxy": "Proxy tin cậy", + "signedInDispDisabled": "Xác thực bị vô hiệu hóa.", + "RadiusSecretDescription": "Bí mật được chia sẻ giữa máy khách và máy chủ", + "Show Powered By": "Hiển thị được cung cấp bởi", + "Domain Names": "Tên tên miền", + "signedInDisp": "Đăng ký với tư cách là {0}", + "resendDisabled": "Vô hiệu hoá gửi phản hồi liên tục", + "resendEveryXTimes": "Gửi phản hồi mỗi {0} lần", + "setupDatabaseMariaDB": "Kết nối tới một cơ sở dữ liệu MariaDB bên ngoài. Bạn cần cấu hình thông tin kết nối cơ sở dữ liệu.", + "setupDatabaseChooseDatabase": "Bạn muốn dùng loại cơ sở dữ liệu nào?", + "setupDatabaseEmbeddedMariaDB": "Bạn không cần phải cài đặt bất cứ thứ gì. Docker image đã nhúng và cấu hình tự động cơ sở dữ liệu MariaDB cho bạn. Uptime Kuma sẽ kết nối tới cơ sở dữ liệu thông qua unix socket.", + "setupDatabaseSQLite": "Một tệp cơ sở dữ liệu đơn giản, được khuyên dùng cho quy mô triển khai nhỏ. Trước v2.0.0, Uptime Kuma sử dụng SQLite như là một cơ sở dữ liệu mặc định.", + "dbName": "Tên cơ sở dữ liệu", + "General Monitor Type": "Loại giám sát chung", + "Passive Monitor Type": "Loại giám sát bị động", + "Specific Monitor Type": "Loại giám sát cụ thể", + "Monitor": "Giám sát", + "Invert Keyword": "Đảo ngược từ khóa", + "Expected Value": "Giá trị mong muốn", + "Json Query": "Truy vấn Json", + "settingUpDatabaseMSG": "Đang khởi tạo cơ sở dữ liệu. Có thể mất một lúc, vui lòng chờ.", + "pushOthers": "Khác", + "programmingLanguages": "Ngôn Ngữ Lập Trình", + "timeoutAfter": "Hết thời gian chờ sau {0} giây", + "styleElapsedTime": "Thời gian đã qua dưới thanh trạng thái", + "styleElapsedTimeShowNoLine": "Hiển Thị (Không Dòng Kẻ)", + "styleElapsedTimeShowWithLine": "Hiển Thị (Có Dòng Kẻ)", + "Request Timeout": "Yêu cầu hết hạn", + "now": "hiện tại", + "Add a new expiry notification day": "Thêm ngày hết hạn", + "time ago": "đã qua {0} giây", + "There might be a typing error in the address.": "Có lỗi trong quá trình nhập địa chỉ", + "DockerHostRequired": "Vui lòng đặt Docker Host cho trang giám sát này", + "templateMsg": "Tin nhắn", + "-year": "-năm", + "filterActive": "Hoạt động", + "filterActivePaused": "Tạm dừng", + "Search monitored sites": "Tìm kiếm trang", + "Reset Token": "Thiết lập lại Token", + "Remove the expiry notification": "Xóa ngày hết hạn", + "API Username": "Tên người dùng API", + "Also check beta release": "Hãy kiểm tra bản beta", + "Check/Uncheck": "Chọn/Bỏ chọn", + "API Key": "mã API", + "Coming Soon": "Sắp tới", + "Query": "Tìm kiếm", + "Setup Docker Host": "Thiết lập máy chủ Docker", + "Connection Type": "Loại kết nối", + "Docker Daemon": "Trình nền docker", + "noDockerHostMsg": "Không tồn tại. Vui lòng thiết lập máy chủ Docker trước", + "Host URL": "URL Máy chủ", + "Footer Text": "Chân trang", + "Refresh Interval": "Tốc độ làm mới", + "Refresh Interval Description": "Trang status sẽ tự làm mới mỗi {0} giây", + "Connection String": "Chuỗi kết nối", + "settingsCertificateExpiry": "Ngày hết hạn chứng chỉ TLS", + "ignoreTLSErrorGeneral": "Bỏ qua lỗi TLS/SSL cho kết nối", + "Select": "Chọn", + "selectedMonitorCount": "Đã chọn: {0}", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "nhập hostname của server bạn muốn kết nối hoặc {localhost} nếu bạn muốn dùng {local_mta}", + "webhookBodyPresetOption": "Cài đặt sẵn", + "critical": "nguy kịch", + "Google": "Google", + "Template Format": "Định dạng mẫu", + "YZJ Robot Token": "Mã thông báo Robot YZJ", + "YZJ Webhook URL": "URL Webhook YZJ", + "discordMessageFormatCustom": "Mẫu tùy chỉnh", + "discordMessageFormatMinimalist": "Nhỏ nhất (trạng thái gọn)", + "discordMessageFormatNormal": "Thông thường (nhúng đa dạng)", + "Suppress Notifications": "Chặn thông báo", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Việc cài đặt bot Nextcloud Talk yêu cầu quyền quản trị máy chủ.", + "Send DOWN silently": "Gửi thông báo DOWN một cách im lặng", + "Send UP silently": "Gửi trạng thái UP một cách yên lặng", + "Bot secret": "Mã bí mật của bot", + "Conversation token": "Token trao đổi", + "Nextcloud host": "Máy chủ Nextcloud", + "Manual": "Thủ công", + "Staged Tags for Batch Add": "Thẻ được thiết lập cho việc thêm hàng loạt", + "Add Another Tag": "Thêm thẻ mới", + "discordMessageTemplate": "Mẫu tin nhắn", + "Globalping API Token": "Mã thông báo API Globalping", + "domain_expiry_unsupported_is_icann": "Tên miền \"{domain}\" không phải là ứng cử viên cho việc giám sát hết hạn tên miền, vì hậu tố công khai \".{publicSuffix}\" của nó không phải là ICAN", + "Never": "Không bao giờ", + "discordMessageFormat": "Định dạng tin nhắn", + "Ip Family": "IP gia đình", + "Expected TLS Alert": "Cảnh báo TLS dự kiến", + "Basic radio toggle button group": "Nhóm nút chuyển đổi radio cơ bản", + "GlobalpingMonitorDescription": "Globalping cung cấp quyền truy cập vào hàng ngàn công cụ thăm dò do cộng đồng lưu trữ để chạy các bài kiểm tra và đo lường mạng. Giới hạn 250 bài kiểm tra mỗi giờ được đặt cho tất cả người dùng ẩn danh. Để tăng gấp đôi giới hạn lên 500 bài kiểm tra mỗi giờ, vui lòng lưu mã thông báo của bạn trong {accountSettings}. Kiểm tra {docs} để biết thêm thông tin.", + "Happy Eyeballs algorithm": "Thuật toán Happy Eyeballs", + "discordUseMessageTemplateDescription": "Nếu được bật, tin nhắn sẽ được gửi dùng 1 mẫu tùy chỉnh (LiquidJS), để trống để dùng cấu hình Uptime Kuma mặc định", + "discordUseMessageTemplate": "Dùng mẫu tin nhắn tùy chỉnh", + "discordSuppressNotificationsHelptext": "Khi được bật, tin nhắn sẽ được đăng lên kênh nhưng sẽ không kích hoạt thông báo đẩy hoặc thông báo trên màn hình cho người nhận.", + "smsplanetApiToken": "Mã thông báo cho API SMSPlanet", + "Font Twemoji by Twitter licensed under": "Phông chữ Twemoji của Twitter được cấp phép theo", + "wayToWriteWahaChatId": "Số điện thoại có mã vùng quốc tế nhưng không có dấu cộng ở đầu ({0}), ID Liên hệ ({1}) hoặc ID Nhóm ({2}). Thông báo được gửi đến ID Trò chuyện này từ Phiên WAHA.", + "Clear Form": "Xóa biểu mẫu", + "Disable URL in Notification": "Tắt URL trong Thông báo", + "Umami": "Umami", + "Matomo": "Matomo", + "Plausible": "Plausible", + "smsplanetNeedToApproveName": "Cần được chấp thuận từ bảng điều khiển", + "Sender name": "Tên người gửi", + "Phone numbers": "Số điện thoại", + "the smsplanet documentation": "tài liệu smsplanet", + "globalpingApiTokenDescription": "Nhận mã thông báo API Globalping của bạn tại {0}.", + "GlobalpingHostname": "Một mục tiêu đo lường có thể truy cập công khai. Thông thường là tên máy chủ hoặc địa chỉ IPv4/IPv6, tùy thuộc vào loại phép đo.", + "GlobalpingLocation": "Trường vị trí chấp nhận châu lục, quốc gia, khu vực, thành phố, ASN, ISP hoặc khu vực đám mây. Bạn có thể kết hợp các bộ lọc với {plus} (ví dụ: {amazonPlusGermany} hoặc {comcastPlusCalifornia}). Nếu độ trễ là một chỉ số quan trọng, hãy sử dụng bộ lọc để thu hẹp vị trí xuống một khu vực nhỏ hơn nhằm tránh các đỉnh điểm. {fullDocs}.", + "wayToGetClickSMSIRTemplateID": "Mẫu của bạn phải chứa trường {uptkumaalert}. Bạn có thể tạo mẫu mới {tại đây}.", + "halopsa_field_uptime_kuma_version": "Số phiên bản Uptime Kuma", + "teltonikaPhoneNumberHelptext": "Số phải ở định dạng quốc tế {0}, {1}. Chỉ được phép sử dụng một số.", + "Message Template": "Mẫu tin nhắn", + "Plain Text": "Văn bản thô", + "versionIs": "Phiên bản: {version}", + "enableSSL": "Bật SSL/TLS", + "mariadbUseSSLHelptext": "Cho phép sử dụng kết nối mã hóa với cơ sở dữ liệu của bạn. Cần thiết đối với hầu hết các cơ sở dữ liệu đám mây.", + "mariadbCaCertificateLabel": "Chứng chỉ CA", + "mariadbCaCertificateHelptext": "Dán chứng chỉ CA ở định dạng PEM để sử dụng với chứng chỉ tự ký. Để trống nếu cơ sở dữ liệu của bạn sử dụng chứng chỉ được ký bởi một CA công cộng.", + "unknownDays": "Ngày không rõ", + "No incidents recorded": "Không có sự cố nào được ghi nhận", + "Load More": "Tải Thêm", + "Loading...": "Đang tải...", + "Monitors": "{n} Trình theo dõi | {n} Trình theo dõi", + "days": "{n} ngày | {n} ngày", + "hours": "{n} giờ | {n} giờ", + "minutes": "{n} phút | {n} phút", + "minuteShort": "{n} phút | {n} phút", + "years": "{n} năm | {n} năm", + "Pin this incident": "Ghim sự cố này", + "Json Query Expression": "Biểu thức truy vấn JSON", + "defaultFriendlyName": "Trình theo dõi mới", + "locally configured mail transfer agent": "Tác nhân chuyển thư được cấu hình cục bộ", + "Path": "Đường dẫn", + "ipFamilyDescriptionAutoSelect": "Sử dụng {happyEyeballs} để xác định IP gia đình.", + "smsplanetApiDocs": "Thông tin chi tiết về cách lấy mã thông báo API có thể được tìm thấy trong {the_smsplanet_documentation}.", + "Globalping - Access global monitoring probes": "Globalping - Truy cập các thiết bị thăm dò giám sát toàn cầu", + "halopsa_setup_step4": "Chọn xác thực cơ bản và tạo tên người dùng và mật khẩu. Sau đó nhập hoặc dán tên người dùng và mật khẩu đó vào các trường kiểm tra phía trên", + "pause": "Tạm dừng", + "monitorTypeSpecial": "Đặc biệt", + "ignoreSecWebsocketAcceptHeaderDescription": "Cho phép máy chủ không phản hồi với header Sec-WebSocket-Accept, nếu websocket nâng cấp thành công.", + "wsCodeDescription": "Để biết thêm thông tin về mã trạng thái, vui lòng tham khảo {rfc6455}", + "wsSubprotocolDescription": "Nhập danh sách các giao thức phụ được phân tách bằng dấu phẩy. Để biết thêm thông tin về các giao thức phụ, vui lòng tham khảo {documentation}", + "saveResponseDescription": "Lưu phản hồi HTTP và cung cấp nó cho các mẫu thông báo dưới dạng {templateVariable}", + "monitorTypeGameServer": "Máy chủ Game", + "Subprotocol(s)": "Giao thức phụ", + "oracledbConnectionString": "Cơ sở dữ liệu Oracle: {connectionString}", + "monitorTypeDatabase": "Kiểu cơ sở dữ liệu kênh theo dõi", + "Only retry if status code check fails": "Chỉ thử lại khi kiểm tra mã trạng thái thất bại", + "retryOnlyOnStatusCodeFailureDescription": "Nếu bật, các lần thử lại sẽ được thực hiện khi kiểm tra mã trạng thái HTTP thất bại (Ví dụ máy chủ đã tắt). Nếu kiểm tra mã trạng thái thành công nhưng truy vấn JSON thất bại, kênh theo dõi sẽ được đánh dấu là down ngay lập tức mà không thử lại.", + "ignoredTLSError": "Lỗi TLS/SSL đã được bỏ qua", + "Ignore Sec-WebSocket-Accept header": "Đã bỏ qua {0} header", + "saveResponseForNotifications": "Lưu phản hồi thành công của HTTP để Thông báo", + "saveErrorResponseForNotifications": "Lưu phản hồi thất bại của HTTP để Thông báo" +} diff --git a/src/lang/vls.json b/src/lang/vls.json new file mode 100644 index 0000000..c8c1497 --- /dev/null +++ b/src/lang/vls.json @@ -0,0 +1,1457 @@ +{ + "languageName": "West-Vloams", + "setupDatabaseChooseDatabase": "Welke database wil ge gebruukn?", + "setupDatabaseMariaDB": "Verbind met een externe MariaDB database. Ge moet de database-connectie informatie instellen.", + "settingUpDatabaseMSG": "Database wordt ingesteld. Dit kan even duren, dus wees geduldig.", + "dbName": "Database naam", + "enableSSL": "Schakel SSL/TLS in", + "mariadbUseSSLHelptext": "Schakel in om een versleutelde verbinding met ge database te gebruiken. Vereist voor de meeste clouddatabases.", + "mariadbCaCertificateLabel": "CA-certificaat", + "mariadbCaCertificateHelptext": "Plak het CA-certificaat in PEM-formaat om deze te gebruiken met zelfondertekende certificaten. Laat dit veld leeg als ge database een certificaat gebruikt dat is ondertekend door een openbare CA.", + "setupDatabaseEmbeddedMariaDB": "Ge hoeft niks in te stellen. Deze Docker image heeft een ingebouwde en geconfigureerde MariaDB instantie. Uptime Kuma verbindt met deze database via een Unix socket.", + "setupDatabaseSQLite": "Een simpel database bestand, aanbevolen voor kleinschalige implementaties. Voor v2.0.0, maakte Uptime Kuma standaard gebruik van een SQLite database.", + "Settings": "Instellingen", + "Dashboard": "Dashboard", + "Help": "Hulp", + "New Update": "Nieuwe update", + "Language": "Taal", + "Appearance": "Weergave", + "Theme": "Thema", + "General": "Algemeen", + "Game": "Spel", + "List": "Lijst", + "Unknown": "Onbekend", + "unknownDays": "Onbekende dagen", + "Cannot connect to the socket server": "Kan gie verbinding maken met de socket-server", + "Reconnecting...": "Opnieuw verbinden...", + "General Monitor Type": "Algemeen Monitor Type", + "Passive Monitor Type": "Passief Monitor Type", + "Specific Monitor Type": "Specifiek Monitor Type", + "markdownSupported": "Markdown syntax ondersteund. Gebruik ge HTML, vermijd dan spaties aan het begin om opmaakproblemen te voorkomen.", + "pauseDashboardHome": "Gepauzeerd", + "Pause": "Pauze", + "Name": "Naam", + "Status": "Status", + "DateTime": "Datum Tijd", + "Message": "Bericht", + "No incidents recorded": "Gie incidenten geregistreerd", + "Load More": "Meer laden", + "Loading...": "Laden...", + "No important events": "Gie belangrijke gebeurtenissen", + "Resume": "Hervatten", + "defaultFriendlyName": "Nieuwe monitor", + "URL": "URL", + "Hostname": "Hostnaam", + "Host URL": "Host URL", + "locally configured mail transfer agent": "lokaal geconfigureerde mail agent", + "Port": "Poort", + "Path": "Pad", + "ignoreTLSError": "Negeer TLS/SSL-fout voor HTTPS-websites", + "Light": "Licht", + "Dark": "Donker", + "Normal": "Normaal", + "Auto Get": "Auto Get", + "Verify Token": "Controleer token", + "templateStatus": "status", + "Valid": "Geldig", + "User ID": "Gebruiker ID", + "noMonitorsPausedMsg": "Gie monitor(s) gepauzeerd (er waren er gie actief)", + "deleteGroupMsg": "Zijt ge zeker dat ge deze groep wil wegdoen?", + "Sets end time based on start time": "Stel eindtijd in op basis van starttijd", + "Please set start time first": "Stel starttijd eerst in", + "pushoversounds intermission": "Pauze", + "pushoversounds magic": "Magie", + "pushoversounds mechanical": "Mechanisch", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Sirene", + "pushoversounds spacealarm": "Ruimte Alarm", + "pushoversounds tugboat": "Tug Boat", + "pushoversounds alien": "Alien Alarm (long)", + "pushoversounds climb": "Climb (long)", + "pushoversounds persistent": "Aanhoudend (lang)", + "pushoversounds echo": "Pushover Echo (long)", + "SMS Type": "SMS Type", + "AccessKeyId": "AccessKey ID", + "Bark Group": "Bark Groep", + "Retry": "Opnieuw", + "Channel Name": "Kanaal Naam", + "smtpDkimDesc": "Refereer alsjeblieft naar Nodemailer DKIM {0} voor gebruik.", + "Analytics ID": "Analytics ID", + "Edit Tag": "Tag bewerken", + "systemServiceCommandHint": "Gebruikte commando: {command}", + "Alphanumeric (recommended)": "Alfanumeriek (aanbevolen)", + "threemaRecipient": "Ontvanger", + "discordMessageFormat": "Berichtformaat", + "discordMessageFormatNormal": "Normaal (rijke insluitingen)", + "discordMessageFormatMinimalist": "Minimalistisch (korte status)", + "discordMessageFormatCustom": "Aangepaste sjabloon", + "discordUseMessageTemplate": "Gebruik een aangepast berichtsjabloon", + "discordMessageTemplate": "Berichtsjabloon", + "Send UP silently": "Stuur UP stil", + "Send DOWN silently": "Stuur DOWN stil", + "Allow Notifications": "Notificaties toestaan", + "Browser not supported": "Browser nie ondersteund", + "Unable to get permission to notify": "Nie mogelijk om toestemming tot notificaties te verkrijgen (verzoek geweigerd of genegeerd).", + "Severity": "Ernst", + "halopsa_setup_step5": "Configureer runbook om monitor_id te gebruiken voor het matchen van waarschuwingen met bestaande tickets", + "Pending": "In afwachting", + "Primary Base URL": "Hoofd Basis URL", + "versionIs": "Versie: {version}", + "Check Update On GitHub": "Zoek naar updates op GitHub", + "Home": "Home", + "Add": "Toevoegen", + "statusMaintenance": "Onderhoud", + "Maintenance": "Onderhoud", + "Edit": "Wijzigen", + "Delete": "Verwijderen", + "Current": "Huidig", + "Add New Monitor": "Nieuwe monitor toevoegen", + "Quick Stats": "Snelle statistieken", + "Up": "Online", + "Down": "Offline", + "Uptime": "Uptime", + "Cert Exp.": "Cert. verl.", + "Monitors": "{n} Monitor | {n} Monitoren", + "now": "nu", + "time ago": "{0} geleden", + "days": "{n} dag| {n} dagen", + "hours": "{n} uur| {n} uren", + "resendDisabled": "Opnieuw versturen uitgeschakeld", + "minutes": "{n} minuut| {n} minuten", + "minuteShort": "{n} min | {n} min", + "years": "{n} jaar| {n} jaren", + "Response": "Antwoord", + "Pin this incident": "Incident vastzetten", + "Ping": "Ping", + "Monitor Type": "Monitortype", + "Keyword": "Trefwoord", + "Invert Keyword": "Trefwoord omkeren", + "Expected Value": "Verwachte waarde", + "Json Query Expression": "Json-queryexpressie", + "Friendly Name": "Vriendelijke naam", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Geef de hostnaam van de server waar ge naar wil verbinden op of {localhost} als ge een {local-mta} wil gebruiken", + "Heartbeat Interval": "Hartslaginterval", + "Request Timeout": "Verzoek Timeout", + "timeoutAfter": "Timeout na {0} seconden", + "Retries": "Pogingen", + "Heartbeat Retry Interval": "Heartbeat opnieuw proberen interval", + "Resend Notification if Down X times consecutively": "Melding x keer opnieuw sturen als monitor offline is", + "Advanced": "Geavanceerd", + "checkEverySecond": "Controleer elke {0} seconden", + "retryCheckEverySecond": "Probeer elke {0} seconden", + "resendEveryXTimes": "Verstuur elke {0} keer opnieuw", + "retriesDescription": "Maximum aantal nieuwe pogingen voordat de service wordt gemarkeerd als nie beschikbaar en er een melding wordt verzonden", + "Only retry if status code check fails": "Alleen opnieuw proberen als de statuscode-controle mislukt", + "retryOnlyOnStatusCodeFailureDescription": "Indien ingeschakeld zal een nieuwe poging alleen plaatsvinden als de HTTP statuscode faalt. Als de statuscode geldig is, maar de JSON query faalt, zal de monitor als offline gemarkeerd wordt zonder nieuwe pogingen.", + "ignoredTLSError": "TLS/SSL fouten zijn genegeerd", + "ignoreTLSErrorGeneral": "Negeer TLS/SSL-fout voor verbinding", + "upsideDownModeDescription": "Draai de status om. Als de service bereikbaar is, zal OFFLINE getoond wordt.", + "ignoreSecWebsocketAcceptHeaderDescription": "Staat toe dat de server gie Sec-WebSocket-Accept-header terugstuurt als de WebSocket-upgrade succesvol is.", + "Ignore Sec-WebSocket-Accept header": "Negeer {0} header", + "wsSubprotocolDescription": "Voor een kommagescheiden lijst in van subprotocollen. Voor meer informatie over subprotocollen raadpleeg ge de {documentation}", + "wsCodeDescription": "Voor meer informatie over statuscodes, raadpleeg {rfc6455}", + "Subprotocol(s)": "Subprotocol(len)", + "maxRedirectDescription": "Maximaal aantal te volgen omleidingen. Stel in op 0 om omleidingen uit te zetten.", + "Upside Down Mode": "Ondersteboven modus", + "Max. Redirects": "Max. Omleidingen", + "saveResponseForNotifications": "HTTP-succesrespons voor meldingen opslaan", + "saveErrorResponseForNotifications": "HTTP-foutmelding voor meldingen opslaan", + "saveResponseDescription": "Slaat het HTTP-antwoord op en maakt het beschikbaar voor meldingssjablonen als {templateVariable}", + "responseMaxLength": "Antwoord maximale lengte (bytes)", + "responseMaxLengthDescription": "Maximale grootte van responsdata om op te slaan. Stel in op 0 om gie limiet te zetten. Grotere responses wordt afgekapt. Standaard: 1024 (1KB)", + "Accepted Status Codes": "Geaccepteerde statuscodes", + "Push URL": "Push URL", + "needPushEvery": "Ge moet deze URL elke {0} seconden aanroepen.", + "pushOptionalParams": "Optionele parameters: {0}", + "pushViewCode": "Hoe gebruik ge Push monitor?(View Code)", + "pushOthers": "Anderen", + "programmingLanguages": "Programmeertalen", + "Save": "Opslaan", + "Notifications": "Meldingen", + "Not available, please setup.": "Nie beschikbaar, stel a.u.b. in.", + "Setup Notification": "Melding instellen", + "Auto": "Auto", + "Theme - Heartbeat Bar": "Thema - Hartslagbalk", + "styleElapsedTime": "Verstreken tijd onder de hartslagbalk", + "styleElapsedTimeShowNoLine": "Laat zien (Gie lijn)", + "styleElapsedTimeShowWithLine": "Laat zien (Met lijn)", + "Bottom": "Onderkant", + "None": "Gie", + "Timezone": "Tijdzone", + "Search Engine Visibility": "Zichtbaarheid voor zoekmachines", + "Allow indexing": "Indexering toestaan", + "Discourage search engines from indexing site": "Ontmoedig zoekmachines om de site te indexeren", + "Change Password": "Verander wachtwoord", + "Current Password": "Huidig wachtwoord", + "New Password": "Nieuw wachtwoord", + "Repeat New Password": "Herhaal nieuw wachtwoord", + "Repeat Password": "Herhaal wachtwoord", + "Incident description": "Omschrijving incident", + "Update Password": "Vernieuw wachtwoord", + "Disable Auth": "Authenticatie uitzetten", + "Enable Auth": "Authenticatie inzetten", + "disableauth.message1": "Zijt ge zeker dat ge {disableAuth}?", + "disable authentication": "authenticatie wil uitzetten", + "disableauth.message2": "Er zijn omstandigheden waarbij ge {intendThirdPartyAuth} voor Uptime Kuma, zoals Cloudflare Access, Authelia of andere authenticatiemechanismen.", + "where you intend to implement third-party authentication": "authenticatie door derden wil implementeren", + "Please use this option carefully!": "Gebruik deze optie zorgvuldig!", + "Logout": "Uitloggen", + "logoutCurrentUser": "Log uit {username}", + "Leave": "Vertrekken", + "I understand, please disable": "Ik begrijp het, schakel a.u.b. uit", + "Confirm": "Bevestigen", + "Yes": "Ja", + "No": "Nee", + "Username": "Gebruikersnaam", + "Password": "Wachtwoord", + "Remember me": "Wachtwoord onthouden", + "Login": "Inloggen", + "No Monitors, please": "Gie monitoren, astublieft", + "add one": "voeg een toe", + "Notification Type": "Melding type", + "Email": "E-mail", + "Test": "Testen", + "Certificate Info": "Certificaat informatie", + "Resolver Server(s)": "Resolver server(s)", + "Resource Record Type": "Type bronrecord", + "Last Result": "Laatste resultaat", + "Create your admin account": "Maak uw beheerdersaccount aan", + "Incident not found or access denied": "Incident nie gevonden of toegang geweigerd", + "Past Incidents": "Afgelopen incidenten", + "Incident title": "Incident titel", + "Disable 2FA": "Schakel 2FA uit", + "Import Backup": "Importeer Backup", + "Export Backup": "Exporteer Backup", + "Export": "Exporteren", + "Import": "Importeren", + "respTime": "reactietijd (ms)", + "notAvailableShort": "N.v.t.", + "Default enabled": "Standaard ingeschakeld", + "Apply on all existing monitors": "Pas toe op alle bestaande monitors", + "Create": "Aanmaken", + "Clear Data": "Data wissen", + "Events": "Gebeurtenissen", + "Heartbeats": "Hartslagen", + "Schedule maintenance": "Onderhoud inplannen", + "Affected Monitors": "Aangetaste monitors", + "Pick Affected Monitors...": "Kies aangetaste monitors…", + "Start of maintenance": "Start van onderhoud", + "All Status Pages": "Alle status pagina's", + "Select status pages...": "Selecteer status pagina's…", + "alertNoFile": "Selecteer een bestand om te importeren.", + "alertWrongFileType": "Selecteer een JSON-bestand.", + "Clear all statistics": "Wis alle statistieken", + "Skip existing": "Sla bestaande over", + "Overwrite": "Overschrijf", + "Options": "Opties", + "Keep both": "Bewaar beide", + "Setup 2FA": "Stel 2FA in", + "Enable 2FA": "Schakel 2FA in", + "2FA Settings": "2FA-instellingen", + "Two Factor Authentication": "Twee Factor Authenticatie", + "filterActive": "Actief", + "filterActivePaused": "Gepauzeerd", + "Active": "Actief", + "Inactive": "Inactief", + "Token": "Token", + "Show URI": "Toon URI", + "Tags": "Labels", + "Add New Tag": "Voeg nieuw label toe", + "Add Tags": "Labels toevoegen", + "Add New below or Select...": "Voeg nieuwe toe of selecteer…", + "Tag with this name already exist.": "Label met deze naam bestaat al.", + "Tag with this value already exist.": "Label met deze waarde bestaat al.", + "tagAlreadyOnMonitor": "Dit label (naam en waarde) is al op de monitor gekoppeld of in afwachting van koppelen.", + "tagAlreadyStaged": "Deze tag (naam en waarde) is al gekozen voor deze batch.", + "tagNameExists": "Een systeemtag met de deze naam bestaat al. Selecteer er een van de lijst of gebruik een andere naam.", + "color": "Kleur", + "value (optional)": "waarde (optioneel)", + "Gray": "Grijs", + "Red": "Rood", + "Orange": "Oranje", + "Green": "Groen", + "Blue": "Blauw", + "Indigo": "Indigo", + "Purple": "Paars", + "Pinned incidents are shown prominently on the status page": "Gepinde incidenten wordt prominent getoond op de statuspagina", + "Pink": "Roze", + "Custom": "Aangepast", + "Search...": "Zoeken…", + "Search monitored sites": "Zoek naar gemonitorde sites", + "Avg. Ping": "Gemiddelde ping", + "Avg. Response": "Gemiddelde response", + "Entry Page": "Opstartpagina", + "statusPageNothing": "Nies hier, voeg een groep of monitor toe.", + "statusPageRefreshIn": "Ververs in: {0}", + "No Services": "Gie diensten", + "All Systems Operational": "Alle systemen operationeel", + "Partially Degraded Service": "Gedeeltelijk verminderde prestaties", + "Degraded Service": "Verminderde prestaties", + "Add Group": "Voeg groep toe", + "Add a monitor": "Voeg monitor toe", + "Application Token": "Applicatie Token", + "Edit Incident": "Incident bewerken", + "Edit Status Page": "Wijzig status pagina", + "Go to Dashboard": "Ga naar Dashboard", + "Status Page": "Status Pagina", + "Status Pages": "Status Pagina", + "defaultNotificationName": "Mijn {notification} Alert ({number})", + "here": "hier", + "Required": "Verplicht", + "Post URL": "Post URL", + "Content Type": "Content Type", + "webhookJsonDesc": "{0} is goed voor een moderne HTTP server zoals Express.js", + "webhookFormDataDesc": "{multipart} is goed voor PHP. De JSON moet wordt ontleed met {decodeFunction}", + "liquidIntroduction": "Templatabiliteit wordt bereikt via de Liquid-templatingtaal. Raadpleeg de {0} voor gebruiksinstructies.", + "templateAvailableVariables": "Beschikbare variabelen", + "example": "Voorbeeld", + "Result": "Resultaat", + "templateMsg": "bericht van de melding", + "templateHeartbeatJSON": "object dat de heartbeat beschrijft", + "templateMonitorJSON": "object dat de monitor beschrijft", + "templateLimitedToUpDownCertNotifications": "alleen beschikbaar voor UP/DOWN/Certificaatverloop notificaties", + "templateLimitedToUpDownNotifications": "alleen beschikbaar voor UP/DOWN notificaties", + "templateServiceName": "service naam", + "templateHostnameOrURL": "hostnaam of url", + "webhookAdditionalHeadersTitle": "Extra Headers", + "webhookAdditionalHeadersDesc": "Voegt extra headers toe die meegestuurd wordt met de webhook. Iedere header moet een JSON key/value zijn.", + "webhookBodyPresetOption": "Voorinstelling - {0}", + "webhookBodyCustomOption": "Aangepaste Body", + "Webhook URL": "URL webhook", + "Server URL": "Server URL", + "Priority": "Prioriteit", + "emojiCheatSheet": "Emoji spiekbrief: {0}", + "Read more": "Lees meer", + "appriseInstalled": "Apprise is geïnstalleerd.", + "appriseNotInstalled": "Apprise is nie geïnstalleerd. {0}", + "Method": "Methode", + "Body": "Inhoud", + "Headers": "Headers", + "PushUrl": "Push URL", + "HeadersInvalidFormatBecause": "De verzoekheaders zijn gie geldige JSON omdat {error}", + "BodyInvalidFormatBecause": "Het verzoek body zijn gie geldige JSON omdat {error}", + "Monitor History": "Monitor Geschiedenis", + "clearDataOlderThan": "Bewaar monitor geschiedenis voor {0} dagen.", + "PasswordsDoNotMatch": "Wachtwoorden komen nie overeen.", + "records": "gegevens", + "One record": "Een record", + "steamApiKeyDescriptionAt": "Voor het monitoren van een Steam gameserver heb ge een Steam Web-API sleutel nodig. Ge kan een API sleutel registreren op {url}", + "topic": "Onderwerp", + "topicExplanation": "MQTT onderwerp om te monitoren", + "mqttWebSocketPath": "Pad voor MQTT WebSocket", + "mqttWebsocketPathExplanation": "WebSocket pad voor MQTT via WebSocket verbindingen (bijv., /mqtt)", + "mqttWebsocketPathInvalid": "Gebruik een geldig WebSocket pad formaat alsjeblieft", + "mqttHostnameTip": "Gebruik dit formaat alsjeblieft {hostnaamFormat}", + "hostnameCannotBeIP": "De DNS-hostnaam kan gie IP-adres zijn. Wilt ge het veld ‘resolver’ gebruiken?", + "invalidHostnameOrIP": "Ongeldige hostnaam of IP. Hostnaam moet een geldige FQDN zijn. Wildcards nie toegestaan. Mag underscores bevatten of eindigen met een punt.", + "invalidDNSHostname": "Ongeldige hostnaam. Hostnaam moet een geldige FQDN zijn. Wildcards nie toegestaan. Mag underscores bevatten of eindigen met een punt.", + "wildcardOnlyForDNS": "Wildcard hostnamen zijn alleen ondersteund voor DNS monitors.", + "invalidURL": "Ongeldige URL", + "successKeyword": "Succes trefwoord", + "successKeywordExplanation": "MQTT Keyword dat als succes wordt gezien", + "recent": "Recent", + "Resolve": "Oplossen", + "Resolved": "Opgelost", + "Reset Token": "Hersteltoken", + "Done": "Klaar", + "Info": "Info", + "Security": "Beveiliging", + "Steam API Key": "Steam API Sleutel", + "Shrink Database": "Verklein Database", + "shrinkDatabaseDescriptionSqlite": "Trigger database {vacuum} voor SQLite. {auto_vacuum} is al aangezet maar dit defragmenteert de database nie en zal individuele database pagina's nie opnieuw organiseren op de manier dat het {vacuum} commando dat doet.", + "Pick a RR-Type...": "Kies een RR-Type…", + "Pick Accepted Status Codes...": "Kies geaccepteerde Status Codes…", + "Default": "Standaard", + "HTTP Options": "HTTP Opties", + "Create Incident": "Creëer Incident", + "Title": "Titel", + "Content": "Content", + "Style": "Stijl", + "info": "info", + "warning": "waarschuwing", + "danger": "gevaar", + "error": "fout", + "critical": "kritisch", + "primary": "primair", + "light": "licht", + "dark": "donker", + "Post": "Post", + "Please input title and content": "Voer astublieft titel en content in", + "createdAt": "Gecreëerd: {date}", + "lastUpdatedAt": "Laatst bijgewerkt: {date}", + "lastUpdatedAtFromNow": "Laatst aangepast: {date} ({fromNow})", + "Switch to Light Theme": "Wissel naar Licht Thema", + "Switch to Dark Theme": "Wissel naar Donker Thema", + "Show Tags": "Toon Labels", + "Hide Tags": "Verberg Labels", + "Description": "Beschrijving", + "descriptionHelpText": "Getoond op het interne dashboard. Markdown is toegestaan en wordt gezuiverd voor weergave.", + "No monitors available.": "Gie monitors beschikbaar.", + "Add one": "Voeg een toe", + "No Monitors": "Gie Monitors", + "Untitled Group": "Naamloze Groep", + "Services": "Diensten", + "Discard": "Weggooien", + "Cancel": "Annuleren", + "auto-select": "Automatisch selecteren", + "Select": "Selecteer", + "Actions": "Acties", + "selectedMonitorCountMsg": "geselecteerd: {n} | geselecteerd: {n}", + "selectMonitorMsg": "Selecteer monitors om acties uit te voeren", + "selectAllMonitorsAria": "Selecteer alle monitors", + "deselectAllMonitorsAria": "Deselecteer alle monitors", + "Check/Uncheck": "Vink/Ontvink", + "Powered by": "Mogelijk gemaakt door", + "Customize": "Aanpassen", + "Custom Footer": "Aangepaste Footer", + "Custom CSS": "Aangepaste CSS", + "deleteIncidentMsg": "Zijt ge zeker dat ge dit incident wil wegdoen?", + "deleteStatusPageMsg": "Zijt ge zeker ge deze status pagina wil wegdoen?", + "Proxies": "Proxies", + "default": "Standaard", + "enabled": "Ingeschakeld", + "setAsDefault": "Stel in als standaard", + "deleteProxyMsg": "Zijt ge zeker dat ge deze proxy wil wegdoen voor alle monitors?", + "proxyDescription": "Proxies moeten wordt toegewezen aan een monitor om te functioneren.", + "enableProxyDescription": "Deze proxy heeft gie effect op monitor verzoeken totdat het is geactiveerd. Ge kunt tijdelijk de proxy uitzetten voor alle monitors voor activatie status.", + "setAsDefaultProxyDescription": "Deze proxy wordt standaard aangezet voor alle nieuwe monitors. Ge kunt nog steeds de proxy apart uitzetten voor elke monitor.", + "Don't know how to get the token? Please read the guide:": "Lees de uitleg als ge nie weet hoe ge een token krijgt:", + "Certificate Chain:": "Certificaatketen:", + "Invalid": "Ongeldig", + "User": "Gebruiker", + "Installed": "Geïnstalleerd", + "Not installed": "Nie geïnstalleerd", + "Running": "Actief", + "Not running": "Nie actief", + "Remove Token": "Verwijder Token", + "Start": "Start", + "Stop": "Stop", + "Add New Status Page": "Voeg nieuwe status pagina toe", + "Slug": "Slug", + "slug is not found": "Afkorting is nie gevonden", + "Accept characters:": "Geaccepteerde tekens:", + "startOrEndWithOnly": "Start of eindig alleen met {0}", + "No consecutive dashes": "Gie opeenvolgende streepjes", + "statusPageSpecialSlugDesc": "Speciale padnaam {0}: deze pagina wordt getoond als er gie padnaam wordt opgegeven", + "Next": "Volgende", + "The slug is already taken. Please choose another slug.": "De slug is al in gebruik. Kies een andere slug.", + "No Proxy": "Gie Proxy", + "Authentication": "Authenticatie", + "HTTP Basic Auth": "HTTP Basis Authenticatie", + "New Status Page": "Nieuwe Status Pagina", + "Page Not Found": "Pagina Nie gevonden", + "Reverse Proxy": "Reverse Proxy", + "Backup": "Backup", + "About": "Over", + "wayToGetCloudflaredURL": "(Download cloudflared van {0})", + "cloudflareWebsite": "Cloudflare website", + "Message:": "Bericht:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "De huidge verbinding kan wordt verbroken als ge momenteel bent verbonden met Cloudflare Tunnel. Zijt ge zeker dat ge het wil stoppen? Typ ge huidige wachtwoord om het te bevestigen.", + "signedInDisp": "Aangemeld als {0}", + "HTTP Headers": "HTTP Headers", + "Trust Proxy": "Trust Proxy", + "Other Software": "Andere Software", + "For example: nginx, Apache and Traefik.": "Bijvoorbeeld: nginx, Apache and Traefik.", + "Please input content": "Voer content in", + "Please input title": "Voer een titel in", + "Please read": "Lees astublieft", + "Subject:": "Onderwerp:", + "Valid To:": "Geldig Tot:", + "Days Remaining:": "Dagen Resterend:", + "Issuer:": "Uitgever:", + "Fingerprint:": "Vingerafruk:", + "No status pages": "Gie status pagina's", + "Domain Name Expiry Notification": "Domeinnaam verlopen melding", + "Add a new expiry notification day": "Voeg een nieuwe dag voor een vervaldatummelding toe", + "Remove the expiry notification": "Verwijder de melding voor de vervaldatum", + "Proxy": "Proxy", + "dateCreatedAtFromNow": "Datum aangemaakt: {date} ({fromNow})", + "Footer Text": "Footer Tekst", + "RSS Title": "RSS titel", + "Leave blank to use status page title": "Laat leeg om titel van de statuspagina te gebruiken", + "Refresh Interval": "Ververs interval", + "Refresh Interval Description": "De status pagina zal elke {0} seconden een volledige website verversing doen", + "Show Powered By": "Laat \"Mogeljik gemaakt door\" zien", + "Domain Names": "Domein Namen", + "signedInDispDisabled": "Authenticatie uitgeschakeld.", + "RadiusSecret": "Radius Geheim", + "RadiusSecretDescription": "Shared Secret tussen client en server", + "RadiusCalledStationId": "Genoemde stations ID", + "Check how to config it for WebSocket": "Controleer hoe ge het configureert voor een WebSocket", + "RadiusCalledStationIdDescription": "Identificatie van het genoemde apparaat", + "RadiusCallingStationId": "Calling stations-ID", + "RadiusCallingStationIdDescription": "Identificatie van het bellende device", + "Certificate Expiry Notification": "Melding over verlopen certificaat", + "Cloud ID": "Cloud ID", + "API Username": "API Gebruikersnaam", + "API Key": "API Sleutel", + "API Token": "API sleutel", + "See Jira Cloud Docs": "Zie Jira cloud docs", + "Show update if available": "Update weergeven indien beschikbaar", + "Also check beta release": "Controleer ook de bètaversies", + "Using a Reverse Proxy?": "Een reverse proxy gebruiken?", + "Steam Game Server": "Steam gameserver", + "Most likely causes:": "Meest waarschijnlijke oorzaken:", + "The resource is no longer available.": "De paginabron is nie langer beschikbaar.", + "There might be a typing error in the address.": "Er zit een typefout in het de URL.", + "What you can try:": "Wat ge kan proberen:", + "Retype the address.": "De URL controleren en/of opnnieuw typen.", + "Go back to the previous page.": "Terug naar de vorige pagina.", + "Coming Soon": "Binnenkort beschikbaar", + "Connection String": "Connectie-string", + "Query": "Query", + "settingsCertificateExpiry": "TLS Certificaat verloopdatum", + "certificationExpiryDescription": "Stuur een melding bij het verlopen van het HTTPS TLS certificaat in:", + "certHostnameMismatch": "Hostnaam op certificaat komt nie overeen met de monitor-URL.", + "Setup Docker Host": "Stel Docker Host in", + "Connection Type": "Verbindingstype", + "Docker Daemon": "Docker daemon", + "noDockerHostMsg": "Nie beschikbaar. Stel eerst een Docker host in.", + "DockerHostRequired": "Stel de Docker host voor deze monitor in.", + "deleteDockerHostMsg": "Zijt ge zeker dat ge deze Docker host wil wegdoen voor alle monitors?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "tailscalePingWarning": "Om de Tailscale Ping-monitor te kunnen gebruiken, moet ge Uptime Kuma zonder Docker installeren en ook de Tailscale-client op uw server installeren.", + "sipsakPingWarning": "Om de SIP Options Ping monitor te gebruiken, moet ge Uptime Kuma zonder docker installeren en ook een Sipsak client op ge server installeren.", + "Docker Container": "Docker container", + "Container Name / ID": "Container Naam / ID", + "Docker Host": "Docker host", + "Docker Hosts": "Docker hosts", + "Domain": "Domein", + "Workstation": "Werkstation", + "Packet Size": "Packet Grootte", + "Bot Token": "Bot token", + "wayToGetTelegramToken": "Ge kunt een token krijgen van {0}.", + "Chat ID": "Chat ID", + "telegramMessageThreadID": "(Optioneel) Berichtthread-ID", + "telegramMessageThreadIDDescription": "Optioneel Unieke identifier voor de doelthread (onderwerp) van het forum; alleen voor forumsupergroepen", + "telegramSendSilently": "Stil sturen", + "telegramSendSilentlyDescription": "Stille verzending van het bericht. Gebruikers ontvangen een melding zonder geluid.", + "telegramProtectContent": "Bescherm doorsturen/opslaan", + "telegramProtectContentDescription": "Indien ingeschakeld, wordt de botberichten in Telegram beschermd tegen doorsturen en opslaan.", + "telegramUseTemplate": "Gebruik aangepaste bericht sjabloon", + "telegramUseTemplateDescription": "Indien ingeschakeld, wordt het bericht verzonden met een aangepaste sjabloon.", + "telegramTemplateFormatDescription": "Telegram staat het gebruik van verschillende opmaaktalen voor berichten toe, zie Telegram {0} voor specifieke details.", + "supportTelegramChatID": "Ondersteuning Directe Chat / Groep / Kanaal Chat ID", + "wayToGetTelegramChatID": "Ge kunt ge CHAT ID krijgen door een bericht te sturen naar de bot en naar deze URL te gaan om het chat_id te bekijken:", + "telegramServerUrl": "(Optioneel) Server Url", + "telegramServerUrlDescription": "Om de beperkingen van Telegram's bot api op te heffen of toegang te krijgen in geblokkeerde gebieden (China, Iran, enz.). Klik voor meer informatie op {0}. Standaard: {1}", + "YOUR BOT TOKEN HERE": "DE BOT TOKEN HIER", + "chatIDNotFound": "Chat ID is nie gevonden; stuur eerst een bericht naar de bot", + "disableCloudflaredNoAuthMsg": "De \"Gie authenticatie\" modus staat aan, wachtwoord is nie vereist.", + "trustProxyDescription": "'X-Forwarded-*' headers vertrouwen. Als ge de correcte client IP wil krijgen en de Uptime Kuma installatie is achter een proxy zoals Nginx of Apache, schakel dan dit in.", + "wayToGetLineNotifyToken": "Ge kan een Access Token van {0} krijgen", + "Examples:": "Voorbeelden: {0}", + "supportBaleChatID": "Support directe chat / groep / kanaal chat ID", + "wayToGetBaleChatID": "Ge kan een chat ID verkrijgen door een bericht te sturen naar de bot en naar deze URL te gaan:", + "wayToGetBaleToken": "Ge kan een token opvragen via {0}.", + "Home Assistant URL": "Home Assistant URL", + "Long-Lived Access Token": "Long-Lived Access Token", + "cronExpression": "Cron-expressie", + "cronScheduleDescription": "Planning: {description}", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "Long-Lived Access Token kan aangemaakt wordt via ge profiel naam (linksonder) en door naar beneden te scrollen en te klikken op Token Aanmaken.", + "Notification Service": "Melding diensten", + "default: notify all devices": "Standaard: stuur melding naar alle apparaten", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "Een lijst van melding diensten kan wordt gevonden in Home Assistant onder \"Developer Tools > Services\" en zoek voor \"notification\" om ge apparaat/telefoon naam te vinden.", + "Automations can optionally be triggered in Home Assistant:": "Automations kunnen optioneel wordt getriggerd in Home Assistant:", + "Trigger type:": "Trigger type:", + "Event type:": "Event type:", + "Event data:": "Event data:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "Kies een actie, bijvoorbeeld het activeren van een scene.", + "frontendVersionIs": "Frontend versie: {version}", + "Frontend Version do not match backend version!": "Frontend versie komt nie overeen nie met de backend versie!", + "backupOutdatedWarning": "Deprecated: Er zijn een hoop nieuwe functies toegevoegd en daarom is de backup functie nie onderhouden, het is op dit moment nie mogelijk om een volledige backup te maken en te herstellen.", + "backupRecommend": "In plaats daarvan, maak een backup van ge Docker volume of de data map (./data/).", + "Optional": "Optioneel", + "and": "en", + "or": "of", + "sameAsServerTimezone": "Hetzelfde als Server Tijdzone", + "startDateTime": "Begindatum/-tijd", + "endDateTime": "Einddatum/-tijd", + "Duration (Minutes)": "Duur (minuten)", + "invalidCronExpression": "Ongeldige Cron-expressie: {0}", + "recurringInterval": "Interval", + "Recurring": "Terugkerend", + "strategyManual": "Actief/Inactief handmatig", + "warningTimezone": "De tijdzone van de server wordt gebruikt", + "weekdayShortMon": "ma", + "weekdayShortTue": "di", + "weekdayShortWed": "wo", + "weekdayShortThu": "do", + "weekdayShortFri": "vr", + "weekdayShortSat": "za", + "weekdayShortSun": "zo", + "dayOfWeek": "Dag van de week", + "dayOfMonth": "Dag van de maand", + "lastDay": "Laatste dag", + "lastDay1": "Laatste dag van de maand", + "lastDay2": "1 na laatste dag van de maand", + "lastDay3": "2 na laatste dag van de maand", + "lastDay4": "3 na laatste dag van de maand", + "No Maintenance": "Gie onderhoud", + "pauseMaintenanceMsg": "Zijt ge zeker dat ge wil pauzeren?", + "maintenanceStatus-under-maintenance": "In onderhoud", + "maintenanceStatus-inactive": "Inactief", + "maintenanceStatus-scheduled": "Ingepland", + "maintenanceStatus-ended": "Beëindigd", + "maintenanceStatus-unknown": "Onbekend", + "Display Timezone": "Toon tijdzone", + "Server Timezone": "Server tijdzone", + "statusPageMaintenanceEndDate": "Einde", + "IconUrl": "Icoon URL", + "Enable DNS Cache": "(Verouderd) Schakel DNS-cache in voor HTTP(s)-monitors", + "Enable": "Inzetten", + "Disable": "Uitzetten", + "enableNSCD": "Schakel NSCD (Name Service Cache Daemon) in voor het cachen van alle DNS-verzoeken", + "chromeExecutable": "Chrome/Chromium Uitvoerbaar bestand", + "chromeExecutableAutoDetect": "Automatisch detecteren", + "notificationIncidentManagement": "Incident management", + "notificationHomeAutomation": "Home automation", + "chromeExecutableDescription": "Voor Docker-gebruikers, als Chromium nog nie is geïnstalleerd, kan het een paar minuten duren om te installeren en het testresultaat weer te geven. Het neemt 1GB schijfruimte in beslag.", + "dnsCacheDescription": "Het werkt nie in sommige IPv6 omgevingen, schakel het uit als ge problemen ervaart.", + "Single Maintenance Window": "Enkel onderhoudsperiode", + "Maintenance Time Window of a Day": "Onderhoud tijdsvak van een dag", + "Effective Date Range": "Effectieve periode (Optioneel)", + "Schedule Maintenance": "Onderhoud inplannen", + "Edit Maintenance": "Onderhoud bewerken", + "Clone Maintenance": "Kloon onderhoud", + "ariaPauseMaintenance": "Pauzeer dit onderhoud", + "ariaResumeMaintenance": "Hervat dit onderhoud", + "ariaCloneMaintenance": "Maak een kopie van dit onderhoudsvenster", + "ariaEditMaintenance": "Bewerk dit onderhoudsvenster", + "ariaDeleteMaintenance": "Verwijder dit onderhoudsvenster", + "Date and Time": "Datum en tijd", + "DateTime Range": "Datum en tijd periode", + "loadingError": "Kan de data nie ophalen, probeer het later opnieuw.", + "plugin": "Plugin | Plugins", + "install": "Installeer", + "installing": "Installeren", + "uninstall": "Verwijderen", + "uninstalling": "Aan het wegdoen", + "confirmUninstallPlugin": "Zijt ge zeker dat ge deze plugin wil wegdoen?", + "notificationRegional": "Regionaal", + "notificationUniversal": "Universeel", + "notificationChatPlatforms": "Chat platforms", + "notificationPushServices": "Push diensten", + "notificationSmsServices": "SMS diensten", + "notificationEmail": "E-mail", + "notificationOther": "Andere integraties", + "Clone Monitor": "Kloon Monitor", + "Clone": "Dupliceer", + "cloneOf": "Duplicaat van {0}", + "smtp": "Email (SMTP)", + "SMTP Security": "SMTP beveiliging", + "Ignore STARTTLS": "Negeer STARTTLS", + "Use STARTTLS": "Gebruik STARTTLS", + "Use HTML for custom E-mail body": "Gebruik HTML voor een aangepaste E-mailtekst", + "secureOptionNone": "Gie / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "Negeer TLS Error", + "Disable STARTTLS": "STARTTLS uitzetten", + "disableSTARTTLSDescription": "Schakel deze optie in voor SMTP servers die gie STARTTLS ondersteunen. Dit zal emails onversleuteld versturen.", + "From Email": "Van Email", + "emailCustomisableContent": "Aanpasbare inhoud", + "smtpLiquidIntroduction": "De volgende twee velden zijn in te stellen via de Liquid-templatingtaal. Raadpleeg de {0} voor gebruiksinstructies. Dit zijn de beschikbare variabelen:", + "emailCustomSubject": "Aangepast Onderwerp", + "leave blank for default subject": "laat leeg voor standaard onderwerp", + "emailCustomBody": "Aangepaste inhoud", + "leave blank for default body": "laat leeg voor standaard inhoud", + "emailTemplateMonitorJSON": "object dat de monitor beschrijft", + "emailTemplateHeartbeatJSON": "object dat de heartbeat beschrijft", + "emailTemplateMsg": "meldingsbericht", + "emailTemplateLimitedToUpDownNotification": "alleen beschikbaar voor UP/DOWN heartbeats, anders null", + "To Email": "Naar Email", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord Webhook URL", + "wayToGetDiscordURL": "Ge kunt dit krijgen door te gaan naar Server Instellingen -> Integraties -> Bekijk webhooks -> Nieuwe webhook", + "Bot Display Name": "Bot Weergave Naam", + "Prefix Custom Message": "Prefix Aangepast Bericht", + "Hello @everyone is...": "Hallo {'@'}iedereen is…", + "Select message type": "Selecteer bericht type", + "Send to channel": "Stuur naar kanaal", + "Create new forum post": "Maak nieuw forum bericht", + "postToExistingThread": "Plaats op bestaande thread / forum bericht", + "forumPostName": "Forum bericht naam", + "threadForumPostID": "Thread / Forum bericht ID", + "e.g. {discordThreadID}": "bijv. {discordThreadID}", + "whatHappensAtForumPost": "Maak een nieuw forum bericht. Dit stuurt GEEN berichten in bestaande forum berichten. Om naar een bestaand forum een nieuw bericht te sturen, gebruik \"{option}\"", + "wayToGetDiscordThreadId": "Verkrijgen van een thread / forum bericht id is vergelijkbaar met het verkrijgen van een kanaal id. Lees meer over het verkrijgen van ids {0}", + "wayToGetTeamsURL": "Ge kunt hier leren hoe ge een webhook URL kunt maken {0}.", + "wayToGetZohoCliqURL": "Via deze link kun ge uitvinden hoe ge een webhook URL maakt {0}.", + "needSignalAPI": "Ge moet een signal client met REST API hebben.", + "wayToCheckSignalURL": "Ge kunt op deze URL zien hoe ge een kunt instellen:", + "Number": "Nummer", + "Recipients": "Ontvangers", + "Access Token": "Access Token", + "Channel access token": "Kanaal access token", + "Channel access token (Long-lived)": "Kanaal access token (blijvend)", + "Line Developers Console": "Line Developers Console", + "lineDevConsoleTo": "Line Developers Console - {0}", + "Basic Settings": "Basis Instellingen", + "Your User ID": "Jouw gebruikers ID", + "Messaging API": "Berichten API", + "wayToGetLineChannelToken": "Begin met {0} te openen, creëer een provider en kanaal (Messaging API), dan kun ge de kanaal access token en gebruikers ID van de hierboven genoemde menge items krijgen.", + "Icon URL": "Icoon URL", + "aboutIconURL": "Ge kunt een link om de standaard profiel afbeelding te overschrijving in \"Icoon URL\" meegeven. Dit wordt nie gebruikt als Icon Emoji is ingesteld.", + "pauseMonitorMsg": "Zijt ge zeker dat ge wil pauzeren?", + "aboutMattermostChannelName": "Ge kunt het standaard kanaal dat de Webhook plaatst overschijven door de kanaal naam in te vullen in het \"Channel Name\" veld. Dit moet wordt ingeschakeld in de Mattermost Webhook instellingen. Bv. #ander-kanaal", + "dataRetentionTimeError": "Bewaarperiode moet 0 of groter zijn", + "infiniteRetention": "Stel in op 0 voor oneindige bewaarperiode.", + "confirmDeleteTagMsg": "Zijt ge zeker dat ge dit label wil wegdoen? Monitors die gekoppeld zijn aan dit label wordt nie verwijderd.", + "enableGRPCTls": "Toestaan om gRPC aanvragen te sturen over TLS verbinding", + "grpcMethodDescription": "Methodenaam moet in camelCase formaat zijn zoals zegHallo, check, etc.", + "acceptedStatusCodesDescription": "Selecteer statuscodes die als een succesvol antwoord wordt beschouwd.", + "deleteMonitorMsg": "Zijt ge zeker dat ge deze monitor wil wegdoen?", + "deleteMonitorsMsg": "Zijt ge zeker dat ge de geselecteerde monitors wil wegdoen?", + "pausedMonitorsMsg": "Gepauzeerd {n} monitor | Gepauzeerd {n} monitors", + "resumedMonitorsMsg": "Hervat {n} monitor | Hervat {n} monitors", + "deletedMonitorsMsg": "Verwijderd {n} monitor | Verwijderd {n} monitors", + "noMonitorsResumedMsg": "Gie monitors hervat (er waren er gie gepauzeerd)", + "bulkDeleteErrorMsg": "Verwijderen van {n} monitor mislukt | Verwijderen van {n} monitors mislukt", + "deleteChildrenMonitors": "Verwijder de onderliggende monitoren en kinderen indien aanwezig | Verwijder ook alle {count} directe kinderen en hun kinderen als er enige zijn", + "deleteMaintenanceMsg": "Zijt ge zeker dat ge dit onderhoud wil wegdoen?", + "deleteNotificationMsg": "Zijt ge zeker dat ge deze melding voor alle monitoren wil wegdoen?", + "dnsPortDescription": "DNS-serverpoort. Standaard ingesteld op 53. Ge kunt de poort op elk moment wijzigen.", + "resolverserverDescription": "Cloudflare is de standaardserver, ge kunt de resolver server op elk moment wijzigen.", + "rrtypeDescription": "Selecteer het RR-type dat ge wil monitoren", + "enableDefaultNotificationDescription": "Voor elke nieuwe monitor wordt deze melding standaard ingeschakeld. Ge kunt de melding nog steeds afzonderlijk uitzetten voor elke monitor.", + "Clear All Events": "Wis alle evenementen", + "clearAllEventsMsg": "Zijt ge zeker dat ge alle evenementen wil wegdoen?", + "Events cleared successfully": "Evenementen succesvol verwijderd.", + "No monitors found": "Gie monitors gevonden.", + "Could not clear events": "Kon {failed}/{total} evenementen nie wegdoen", + "clearEventsMsg": "Zijt ge zeker dat ge alle evenementen voor deze monitor wil wegdoen?", + "clearHeartbeatsMsg": "Zijt ge zeker dat ge alle heartbeats voor deze monitor wil wegdoen?", + "confirmClearStatisticsMsg": "Zijt ge zeker dat ge alle statistieken wil wegdoen?", + "importHandleDescription": "Kies 'Sla bestaande over' als ge elke monitor of melding met dezelfde naam wil overslaan. Kies 'Overschrijf' als ge elke monitor of notificatie wil wegdoen.", + "confirmImportMsg": "Zijt ge zeker dat ge dit bestand wil importeren? Controleer of ge de correcte importeer optie hebt geselecteerd.", + "twoFAVerifyLabel": "Voer uw 2FA controle token in voor verificatie:", + "tokenValidSettingsMsg": "Token is geldig! Ge kunt nge de 2FA-instellingen opslaan.", + "confirmEnableTwoFAMsg": "Zijt ge zeker dat ge 2FA wil inzetten?", + "confirmDisableTwoFAMsg": "Zijt ge zeker dat ge 2FA wil uitzetten?", + "recurringIntervalMessage": "1 keer per dag uitvoeren | 1 keer per elke {0} dagen uitvoeren", + "affectedMonitorsDescription": "Selecteer de monitors die zullen wordt aangetast door het huidige onderhoud", + "affectedStatusPages": "Toon het onderhoudsbericht op de geselecteerde status pagina's", + "noMonitorsSelectedWarning": "Ge maakt een onderhoudsvenster aan zonder getroffen monitors. Zijt ge zeker dat ge wil doorgaan?", + "noMonitorsOrStatusPagesSelectedError": "Kan gie onderhoud aanmaken zonder geraakte monitors of statuspagina's", + "passwordNotMatchMsg": "Het herhaalwachtwoord komt nie overeen.", + "notificationDescription": "Wijs a.u.b. een melding toe aan de monitor(s) om het te laten werken.", + "keywordDescription": "Zoek trefwoord in gewone html of JSON-response en het is hoofdlettergevoelig.", + "invertKeywordDescription": "Kijk of het trefwoord afwezig is in plaats van aanwezig.", + "jsonQueryDescription": "Parseer en haal specifieke gegevens uit de JSON-respons van de server met behulp van JSON-query of gebruik \"$\" voor de onbewerkte respons, als ge gie JSON verwacht. Het resultaat wordt vervolgens vergeleken met de verwachte waarde, als strings. Zie {0} voor documentatie en gebruik {1} om te experimenteren met query's.", + "backupDescription": "Ge kunt een back-up maken van alle monitoren en alle meldingen in een JSON-bestand.", + "backupDescription2": "PS: Geschiedenis- en gebeurtenisgegevens zijn nie inbegrepen.", + "backupDescription3": "Gevoelige gegevens zoals melding tokens zijn opgenomen in het exportbestand, houd het veilig opgeslagen.", + "octopushAPIKey": "\"API key\" van HTTP API inloggegevens van het controle paneel", + "pushyToken": "Apparaat token", + "octopushLogin": "\"Login\" van HTTP API inloggegevens controle paneel", + "promosmsLogin": "API Login naam", + "promosmsPassword": "API Wachtwoord", + "pushoversounds pushover": "Pushover (standaard)", + "pushoversounds bike": "Fiets", + "pushoversounds bugle": "Trompet", + "pushoversounds cashregister": "Kassa", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Buitenaards", + "pushoversounds falling": "Vallend", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Inkomend", + "pushoversounds updown": "Up Down (long)", + "pushoversounds vibrate": "Alleen trillen", + "pushoversounds none": "Gie (stil)", + "pushyAPIKey": "Secret API Key", + "apprise": "Apprise (Ondersteunt 50+ notificatie-diensten)", + "GoogleChat": "Google Chat (Google Workspace alleen)", + "Google Apps Script Webhook URL": "Google Apps script webhook URL", + "Deploy a Google Apps Script as a web app and paste the URL here": "Publiceer een Google Apps script als web app en plak de URL hier", + "Quick Setup Guide": "Snelstartgids", + "Open your Google Spreadsheet": "Open ge Google Spreadsheet", + "Go to Extensions → Apps Script": "Ga naar extensies → Apps Script", + "Paste the script code (see below)": "Plak de script code (zie onderstaand)", + "Click Deploy → New deployment → Web app": "Klik op Deploy → New deployment → Web app", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "Stel 'Uitvoeren als: mij' en 'Wie heeft toegang: Iedereen' in", + "Copy the web app URL and paste it above": "Kopieer de web app URL en plak deze hierboven", + "Google Apps Script Code": "Google apps script code", + "Copy to Clipboard": "Kopieer naar klembord", + "Copied to clipboard!": "Gekopieerd naar klembord!", + "Failed to copy to clipboard": "Kopieren naar klembord mislukt", + "Template plain text instead of using cards": "Sjabloon platte tekst van gebruikte kaarten", + "issueWithGoogleChatOnAndroidHelptext": "Dit geeft ook de mogelijkheid om upstream bugs als {issuetrackerURL} te passeren", + "wayToGetKookBotToken": "Maak een applicatie en haal ge bot token op bij {0}", + "wayToGetKookGuildID": "Switch naar 'Developer Mode' in de Kook instellingen, en klik met de rechter muisknop op de guild om de ID op te halen", + "Guild ID": "Guild ID", + "User Key": "Gebruikers sleutel", + "Device": "Apparaat", + "Message Title": "Bericht Titel", + "Notification Sound": "Notificatie Geluid", + "More info on:": "Meer info op: {0}", + "pushoverDesc1": "Nood prioriteit (2) heeft standaard een 30 seconden timeout tussen pogingen en verloopt na 1 uur.", + "pushoverDesc2": "Vul het appraat veld in als ge notificaties naar andere apparaten wil versturen.", + "pushoverMessageTtl": "Bericht TTL (seconden)", + "octopushTypePremium": "Premium (Snel - aangeraden voor te alarmeren)", + "octopushTypeLowCost": "Low Cost (Langzaam - wordt soms geblokkeerd door operator)", + "checkPriceAt": "Bekijk {service} prijzen op {url}", + "apiCredentials": "API referenties", + "octopushLegacyHint": "Wil ge de legacy versie van Octopush (2011-2020) gebruiken of de nieuwe versie?", + "Check octopush prices": "Controleer Octopush prijzen {0}.", + "octopushPhoneNumber": "Telefoonnummer (Int. formaat, bijv: +33612345678)", + "octopushSMSSender": "SMS zender naam : 3-11 alfanumerieke karakters en spatie (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Apparaat ID", + "Apprise URL": "Apprise URL", + "Example:": "Voorbeeld: {0}", + "Read more:": "Lees meer: {0}", + "Status:": "Status: {0}", + "Strategy": "Strategie", + "Free Mobile User Identifier": "Free Mobile gebruikers ID", + "Free Mobile API Key": "Free Mobile API Sleutel", + "Enable TLS": "TLS inzetten", + "Proto Service Name": "Proto service naam", + "Proto Method": "Proto methode", + "Proto Content": "Proto inhoud", + "Economy": "Economy", + "Lowcost": "Lowcost", + "high": "hoog", + "SendKey": "SendKey", + "SMSManager API Docs": "SMSManager API documentatie", + "Gateway Type": "Gateway-type", + "You can divide numbers with commas or semicolons": "Ge kan getallen scheiden met {comma} of {semicolon}", + "Base URL": "Base URL", + "goAlertInfo": "GoAlert is een open source applicatie voor het plannen van aanwezigheidsdiensten, geautomatiseerde escalaties en meldingen (zoals SMS of spraakoproepen). Schakel automatisch de juiste persoon in, op de juiste manier en op het juiste moment! {0}", + "goAlertIntegrationKeyInfo": "Verkrijg generieke API-integratiesleutel voor de service in dit formaat \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\" meestal de waarde van tokenparameter van gekopieerde URL.", + "SecretAccessKey": "AccessKey Secret", + "PhoneNumbers": "TelefoonNummers", + "TemplateCode": "TemplateCode", + "Proxy Protocol": "Proxy Protocol", + "SignName": "SignName", + "OptionalParameters": "Optionele parameters", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "Door beperkingen van de provider, schakel optionele variabelen in met het risico nie af te leveren", + "aliyun-template-requirements-and-parameters": "De aliyun SMS template moet parameters bevatten: {parameters}", + "aliyun-template-optional-parameters": "Optionele parameters: {parameters}", + "Bark API Version": "Bark API-versie", + "Bark Endpoint": "Bark Endpoint", + "Bark Sound": "Bark Geluid", + "WebHookUrl": "Webhook URL", + "SecretKey": "SecretKey", + "For safety, must use secret key": "Voor de veiligheid moet ge de secret key gebruiken", + "Mentioning": "Vermelden", + "Don't mention people": "Vermeld gie mensen", + "Mention group": "Vermeld {group}", + "Mention Mobile List": "Mobiele lijst noemen", + "Mention User List": "Gebruikers-id lijst", + "Dingtalk Mobile List": "Mobiel lijst", + "Dingtalk User List": "Gebruikers-id lijst", + "Enter a list of userId": "Voer een lijst van userId in", + "Enter a list of mobile": "Voer een lijst van mobiel in", + "Invalid mobile": "Ongeldige mobiel [{mobile}]", + "Invalid userId": "Ongeldig userId [{userId}]", + "Device Token": "Apparaat Token", + "Platform": "Platform", + "Huawei": "Huawei", + "High": "Hoog", + "Topic": "Onderwerp", + "WeCom Bot Key": "WeCom Bot Sleutel", + "WeCom Mentioned Mobile List": "WeCom mobiel lijst", + "WeCom Mentioned Mobile List Description": "Voor het telefoonnummer in om te noemen. Scheid meerdere nummers met een komma. Gebruik {'@'}all om iedereen te noemen.", + "Setup Proxy": "Stel Proxy in", + "Proxy Server": "Proxy Server", + "Proxy server has authentication": "Proxy server heeft authenticatie", + "promosmsTypeEco": "SMS ECO - Goedkoop maar langzaam en vaak overbelast. Gelimiteerd tot Poolse ontvangers.", + "promosmsTypeFlash": "SMS FLASH - Berichten wordt automatisch weergegeven op het apparaat van de ontvanger. Gelimiteerd tot Poolse ontvangers.", + "Notify Channel": "Notify Channel", + "aboutNotifyChannel": "Notify channel activeert een melding op bureaublad of mobiel voor alle leden van de channel, ongeacht of hun beschikbaarheid is ingesteld op actief of afwezig.", + "promosmsTypeFull": "SMS FULL - Premium tier van SMS, ge kunt de ontvanger naam gebruiken (Ge moet eerst de naam registreren). Betrouwbaar voor alarmeringen.", + "promosmsTypeSpeed": "SMS SPEED - Hoogste prioriteit in systeem. Is veel sneller en betrouwbaarder maar kost meer (ongeveer twee keer zoveel als volle SMS prijs).", + "promosmsPhoneNumber": "Telefoon nummer (voor Poolse ontvangers. Ge kunt gebieds codes overslaan)", + "promosmsSMSSender": "SMS Ontvanger naam : Voor geregistreerde naam of een van de standaarden: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "promosmsAllowLongSMS": "Sta lange SMS toe", + "Feishu WebHookUrl": "Feishge Webhook URL", + "matrixHomeserverURL": "Homeserver URL (met http(s):// en optioneel poort)", + "Internal Room Id": "Interne Room ID", + "matrixDesc1": "Ge kunt de interne room ID vinden door in de geavanceerde sectie van de room instellingen in ge Matrix client te kijken. Het zoge moeten uitzien als !QMdRCpUIfLwsfjxye6:home.server.", + "matrixDesc2": "Het wordt ten zeerste aanbevolen om een nieuwe gebruiker aan te maken en nie de access token van ge account te gebruiken, aangezien dit volledige toegang geeft tot ge account en alle kamers waar ge lid van bent. Maak in plaats daarvan een nieuwe gebruiker aan en nodig deze alleen uit voor de ruimte waarin ge de melding wil ontvangen. Ge kunt de access token krijgen door het volgende uit te voeren {0}", + "Uptime Kuma URL": "Uptime Kuma URL", + "setup a new monitor group": "Stel een nieuwe monitorgroep in", + "smtpDkimskipFields": "Header sleutels nie om te ondertekenen (Optioneel)", + "openModalTo": "Modal openen naar {0}", + "Add a domain": "Voeg een domein toe", + "Remove domain": "Verwijder domein '{0}'", + "Icon Emoji": "Icoon Emoji", + "signalImportant": "BELANGRIJK: Ge kunt groepen en nummers nie mengen in ontvangers!", + "aboutWebhooks": "Meer info over Webhooks op: {0}", + "aboutJiraCloudId": "Meer informatie over Jira Cloud ID: {0}", + "see Jira Cloud Docs": "zie Jira Cloud documentatie", + "Jira Service Management": "Jira Service management", + "aboutSlackUsername": "Verandert de weergavenaam van de afzender. Als ge iemand wil vermelden, voeg dit dan aan de vriendelijke naam toe.", + "slackIncludeGroupName": "Voeg monitor groepsnaam in", + "slackIncludeGroupNameDescription": "Indien ingeschakeld, het pad van de monitor groep zal toegevoegd wordt in notificaties om meer onderscheid te bieden in monitoren met dezelfde naam in verschillende groepen.", + "slackUseTemplate": "Gebruik eigen berichttemplate", + "slackUseTemplateDescription": "Indien ingeschakeld zal het bericht met een eigen template verstuurd wordt. Ge kan liquid templating gebruiken om monitor groepsinformatie in te voegen via monitorJSON.path of monitorJSON.pathName.", + "aboutChannelName": "Voer de kanaal naam in op {0} Kanaal naam veld als ge het Webhook kanaal wil omzeilen. Bijv: #other-channel", + "aboutKumaURL": "Als ge de Uptime Kuma URL veld leeg laat, wordt standaard het GitHub project pagina weergegeven.", + "smtpDkimSettings": "DKIM Instellingen", + "documentation": "documentatie", + "smtpDkimDomain": "Domein Naam", + "smtpDkimKeySelector": "Sleutel Kiezer", + "smtpDkimPrivateKey": "Prive Sleutel", + "smtpDkimHashAlgo": "Hash Algoritme (Optioneel)", + "smtpDkimheaderFieldNames": "Header sleutels om te ondertekenen (Optioneel)", + "wayToGetPagerDutyKey": "Ge kunt dit krijgen door naar Service -> Service Directory -> (Selecteer een service) -> Integraties -> Integratie toevoegen te gaan. Hier kunt ge zoeken naar \"Events API V2\". Meer informatie {0}", + "Integration Key": "Integratie-key", + "Integration URL": "Integratie-URL", + "Auto resolve or acknowledged": "Automatisch oplossen of bevestigen", + "do nothing": "nies doen", + "auto acknowledged": "automatisch bevestigen", + "auto resolve": "automatisch oplossen", + "alertaApiEndpoint": "API Eindpunt", + "alertaEnvironment": "Omgeving", + "alertaApiKey": "API Sleutel", + "alertaAlertState": "Alert Staat", + "alertaRecoverState": "Herstel Staat", + "serwersmsAPIUser": "API Gebruikersnaam (incl. webapi_ prefix)", + "serwersmsAPIPassword": "API Wachtwoord", + "serwersmsPhoneNumber": "Telefoon nummer", + "serwersmsRecipientType": "Ontvanger type", + "serwersmsRecipientTypePhone": "Telefonnnummer", + "serwersmsRecipientTypeGroup": "Groep", + "serwersmsGroupId": "Groep ID", + "serwersmsGroupIdHelptext": "ID of groep ID in het klantenpanel. Deze ID nummers kunnen gedownload wordt met actie groepen / index of door ze te kopieren van het bewerk groep deel in het klantenpanel.", + "serwersmsSenderName": "SMS Zender Naam (geregistreerd via klant portaal)", + "smseagleTo": "Telefoonnummer(s)", + "smseagleGroup": "Telefoonboek groep namen", + "smseagleContact": "Telefoonboek contact namen", + "smseagleGroupV2": "Telefoonboek groep ID(s)", + "smseagleContactV2": "Telefoonboek contact ID(s)", + "smseagleRecipientType": "Ontvanger type", + "smseagleRecipient": "Ontvanger(s) (gescheiden met comma)", + "smseagleToken": "API access token", + "smseagleUrl": "SMSEagle apparaat URL", + "smseagleEncoding": "Stuur als Unicode", + "smseaglePriority": "Bericht prioriteit (0-9, standaard = 0)", + "smseagleMsgType": "Berichttype", + "onebotSafetyTips": "Voor de veiligheid moet een toegangssleutel wordt ingesteld", + "smseagleMsgSms": "Sms bericht (standaard)", + "smseagleMsgRing": "Oproep", + "smseagleMsgTts": "Tekst-naar-spraak gesprek", + "smseagleMsgTtsAdvanced": "Tekst-naar-spraak geavanceerd gesprek", + "smseagleDuration": "Duur (in seconden)", + "smseagleTtsModel": "Tekst-naar-spraak model ID", + "smseagleApiType": "API versie", + "smseagleApiv1": "APIv1 (voor bestaande projecten)", + "smseagleApiv2": "APIv2 (aanbevolen voor nieuwe integraties)", + "smseagleDocs": "Controleer documentatie of APIv2 beschikbaarheid: {0}", + "smseagleComma": "Meerdere waarden moeten gescheiden wordt met een komma", + "smspartnerApiurl": "Ge kunt ge API-sleutel vinden in ge dashboard bij {0}", + "smspartnerPhoneNumber": "Telefoonnummer(s)", + "smspartnerPhoneNumberHelptext": "Het nummer moet in het internationale format {0}, {1} zijn. Meerdere nummers moeten gescheiden zijn door {2}", + "smspartnerSenderName": "SMS afzender naam", + "smspartnerSenderNameInfo": "Moet tussen 3..=11 reguliere karakters zijn", + "Recipient Number": "Nummer ontvanger", + "From Name/Number": "Van naam/nummer", + "Leave blank to use a shared sender number.": "Laat leeg om een gedeeld afzendernummer te gebruiken.", + "Octopush API Version": "Octopush API versie", + "octopushEndpoint": "octopush (endpoint: {url})", + "legacyOctopushEndpoint": "Legacy Octopush-DM (endpoint: {url})", + "ntfy Topic": "ntfy Topic", + "Server URL should not contain the nfty topic": "De server-URL mag het nfty-onderwerp nie bevatten", + "onebotHttpAddress": "OneBot HTTP Adres", + "onebotMessageType": "OneBot Bericht Type", + "onebotGroupMessage": "Groep", + "onebotPrivateMessage": "Privé", + "onebotUserOrGroupId": "Groep/Gebruiker ID", + "PushDeer Server": "PushDeer-server", + "pushDeerServerDescription": "Laat dit veld leeg om de officiële server te gebruiken", + "PushDeer Key": "PushDeer Sleutel", + "SpugPush Template Code": "Template code", + "wayToGetClickSendSMSToken": "Ge kan een API Username en API Key krijgen vanuit {0} .", + "Custom Monitor Type": "Eigen Monitor Type", + "Google Analytics ID": "Google Analytics ID", + "Analytics Type": "Analytics type", + "Analytics Script URL": "Analytics-script-URL", + "Server Address": "Server Adres", + "Learn More": "Meer leren", + "Body Encoding": "Body Encoding", + "API Keys": "API Sleutels", + "Expiry": "Verval", + "Expiry date": "Vervaldatum", + "Don't expire": "Verval nooit", + "Continue": "Ga verder", + "Add Another": "Nog een toevoegen", + "Key Added": "Sleutel toegevoegd", + "apiKeyAddedMsg": "Ge API-sleutel is toegevoegd. Noteer deze, want hij wordt nie meer weergegeven.", + "Add API Key": "Voeg API Sleutel toe", + "No API Keys": "Gie API Sleutels", + "apiKey-active": "Actief", + "apiKey-expired": "Verlopen", + "apiKey-inactive": "Inactief", + "Expires": "Vervalt", + "disableAPIKeyMsg": "Zijt ge zeker dat ge deze API-sleutel wil uitzetten?", + "deleteAPIKeyMsg": "Zijt ge zeker dat ge deze API-sleutel wil wegdoen?", + "Generate": "Genereer", + "pagertreeIntegrationUrl": "Integratie URL", + "pagertreeUrgency": "Urgentie", + "pagertreeSilent": "Stil", + "pagertreeLow": "Laag", + "pagertreeMedium": "Medium", + "pagertreeHigh": "Hoog", + "pagertreeCritical": "Kritisch", + "pagertreeResolve": "Automatisch oplossen", + "pagertreeDoNothing": "Doe niks", + "wayToGetPagerTreeIntegrationURL": "Nadat ge de Uptime Kuma-integratie in PagerTree hebt gemaakt, kopieert ge het eindpunt. Bekijk alle details {0}", + "lunaseaTarget": "Doel", + "lunaseaDeviceID": "Apparaat ID", + "lunaseaUserID": "Gebruiker ID", + "twilioAuthToken": "Auth Token / Api Sleutel Secret", + "twilioFromNumber": "Van Nummer", + "ntfyAuthenticationMethod": "Authenticatiemethode", + "ntfyPriorityHelptextAllEvents": "Alle meldingen wordt verzonden met de hoogste prioriteit", + "ntfyPriorityHelptextAllExceptDown": "Alle meldingen wordt verzonden met deze prioriteit, behalve {0}-meldingen, die hebben een prioriteit van {1}", + "ntfyPriorityHelptextPriorityHigherThanDown": "Reguliere prioriteit moet hoger zijn dan {0}. Prioriteit {1} is hoger dan {2}", + "ntfyPriorityDown": "Prioriteit voor DOWN-events", + "ntfyUsernameAndPassword": "Gebruikersnaam en Wachtwoord", + "ntfyCall": "Telefoongesprek", + "ntfyCallHelptext": "Maak een telefoongesprek wanneer een alert triggert. Stel in op 'Ja' om ge eerst geverifieerde nummer te gebruiken, of voer een specifiek telefoonnummer in. Vereist een geverifieerd telefoonnummer.", + "ntfyUseTemplate": "Notificatie-templates aanpassen", + "ntfyUseTemplateDescription": "Schakel in om notificatie-titels en berichten aan te passen met LiquidJS templating", + "ntfyCustomTitle": "Eigen titel template", + "ntfyCustomMessage": "Eigen berichttemplate", + "ntfyNotificationTemplateFallback": "Laat leeg om het standaard Uptime Kuma format te gebruiken", + "twilioAccountSID": "Account SID", + "twilioApiKey": "Api Sleutel (optioneel)", + "twilioApiKeyHelptext": "De API sleutel is optioneel maar aanbevolen. Ge kan een account SID en AuthToken van de TwilioConsole pagina geven of een Account SID en paar van Api Key en Api Secret", + "twilioMessagingServiceSID": "Berichtenservice SID (optioneel)", + "twilloMessagingServiceSIDHelptext": "Voer hier ge Messaging Service SID in als ge {twillo_messaging_service_help_link} gebruikt om afzenders en functies te beheren", + "twilioToNumber": "Naar Nummer", + "Monitor Setting": "{0}'s Monitor Instelling", + "Show Clickable Link": "Laat klikbare link zien", + "Show Clickable Link Description": "Als deze optie is aangevinkt, heeft iedereen die toegang heeft tot deze statuspagina toegang tot de monitor URL.", + "Open Badge Link Generator": "Open badge link generator", + "Badge Link Generator": "{0}'s badge link generator", + "Badge Link Generator Helptext": "Badge links zijn beschikbaar voor alle monitors toegewezen aan openbare statuspagina's. Voor meer informatie, zie de {documentation}.", + "Badge Type": "Badge type", + "Badge Duration (in hours)": "Duur badge (in uren)", + "Badge Label": "Badge Label", + "Badge Prefix": "Badge Waarde Voorvoegsel", + "Badge Suffix": "Badge waarde achtervoegsel", + "Badge Label Color": "Badge label kleur", + "Badge Color": "Badge kleur", + "Badge Label Prefix": "Badge Label Voorvoegsel", + "Badge Preview": "Badge voorbeeld", + "Badge Label Suffix": "Badge label achtervoegsel", + "Badge Up Color": "Badge Online Kleur", + "Badge Down Color": "Badge Offline Kleur", + "Badge Pending Color": "Badge In Afwachting Kleur", + "Badge Maintenance Color": "Badge Onderhoud Kleur", + "Badge Warn Color": "Badge Waarschuwing Kleur", + "Badge Warn Days": "Badge Waarschuwing dagen", + "Badge Down Days": "Badge Offline dagen", + "Badge Style": "Badge stijl", + "Badge value (For Testing only.)": "Badgewaarde (Alleen voor testen)", + "Badge URL": "Badge URL", + "Group": "Groep", + "Monitor Group": "Monitor Groep", + "monitorToastMessagesLabel": "Monitor toast-meldingen", + "monitorToastMessagesDescription": "Toast-meldingen voor monitors verdwijnen na het opgegeven aantal meldingen. Zet dit op -1 om het verdwijnen uit te zetten. Zet op 0 om meldingen helemaal uit te zetten.", + "toastErrorTimeout": "Timeout voor foutmeldingen", + "toastSuccessTimeout": "Timeout voor succesmeldingen", + "Kafka Brokers": "Kafka brokers", + "Enter the list of brokers": "Voer de lijst met brokers in", + "Press Enter to add broker": "Druk op enter om een broker toe te voegen", + "Kafka Topic Name": "Kafka Topicnaam", + "Kafka Producer Message": "Kafka Producer bericht", + "Enable Kafka SSL": "Kafka SSL inzetten", + "Enable Kafka Producer Auto Topic Creation": "Kafka Producer automatische topic aanmaak inzetten", + "Kafka SASL Options": "Kafka SASL opties", + "Mechanism": "Mechanisme", + "Pick a SASL Mechanism...": "Kies een SASL mechanisme…", + "Authorization Identity": "Autorisatie-identiteit", + "AccessKey Id": "AccessKey Id", + "Secret AccessKey": "Geheim AccessKey", + "Session Token": "Sessietoken", + "noGroupMonitorMsg": "Nie beschikbaar. Creëer eerst een Groep Monitor.", + "Close": "Sluit", + "Request Body": "Request Body", + "HTTP Method": "HTTP methode", + "webhookPostMethodDesc": "POST is goed voor de meeste moderne HTTP servers.", + "webhookGetMethodDesc": "GET verstuurt gegevens als queryparameters en staat nie toe om een body te configureren. Handig voor het activeren van Uptime Kuma Push-monitors.", + "wayToGetFlashDutyKey": "Ga naar Kanaal -> (Kies een kanaal) -> Integraties -> Voeg een nieuwe integratie toe, voeg een nieuwe 'Uptime Kuma' toe om een push-adres te verkrijgen, kopieer de integratiesleutel in het adres-veld.", + "FlashDuty Severity": "Hevigheid", + "FlashDuty Push URL": "Push URL", + "FlashDuty Push URL Placeholder": "Kopieer van de alert-integratiepagina", + "nostrRelays": "Nostr relays", + "nostrRelaysHelp": "Een relay-URL per regel", + "nostrSender": "Private key van de afzender (nsec)", + "nostrRecipients": "Publieke sleutels van de ontvangers (npub)", + "nostrRecipientsHelp": "npub formaat, een per regel", + "showCertificateExpiry": "Toon certificaat verloopdatum", + "showOnlyLastHeartbeat": "Toon alleen laatste heartbeat", + "noOrBadCertificate": "Gie/Fout certificaat", + "cacheBusterParam": "Voeg de {0} parameter", + "cacheBusterParamDescription": "Willekeurig gegenereerde parameter om caches over te slaan.", + "gamedigGuessPort": "Gamedig: Guess poort", + "gamedigGuessPortDescription": "De poort die wordt gebruikt door het Valve Server Query Protocol kan verschillen van de clientpoort. Probeer dit als de monitor gie verbinding kan maken met ge server.", + "Message format": "Bericht opmaak", + "Send rich messages": "Verstuur berichten met opmaak", + "Bitrix24 Webhook URL": "Bitrix24 webhook URL", + "wayToGetBitrix24Webhook": "Ge kunt een webhook maken door de stappen bij {0} te volgen", + "bitrix24SupportUserID": "Voer ge gebruikers ID van Bitrix25 in. Ge kunt dit achterhalen uit de link naar ge gebruikersprofiel.", + "Saved.": "Opgeslagen.", + "authUserInactiveOrDeleted": "De gebruiker is inactief of verwijderd.", + "authInvalidToken": "Ongeldig token.", + "authIncorrectCreds": "Ongeldige gebruikersnaam of wachtwoord.", + "2faAlreadyEnabled": "2FA is al ingeschakeld.", + "2faEnabled": "2FA Ingeschakeld.", + "2faDisabled": "2FA Uitgeschakeld.", + "successAdded": "Succesvol toegevoegd.", + "successResumed": "Succesvol doorgegaan.", + "successPaused": "Succesvol gepauzeerd.", + "successDeleted": "Succesvol verwijderd.", + "successEdited": "Succesvol bewerkt.", + "successAuthChangePassword": "Wachtwoord is succesvol gewijzigd.", + "successBackupRestored": "Backup succesvol teruggezet.", + "successDisabled": "Succesvol uitgeschakeld.", + "successEnabled": "Succesvol ingeschakeld.", + "tagNotFound": "Tag nie gevonden.", + "foundChromiumVersion": "Chromium/Chrome gevonden. Versie {0}", + "Remote Browsers": "Remote Browsers", + "Remote Browser": "Remote Browser", + "Add a Remote Browser": "Voeg een Remote Browser toe", + "Remote Browser not found!": "Remote Browser nie gevonden!", + "remoteBrowsersDescription": "Remote Browsers zijn een alternatief voor het lokaal draaien van Chromium. Stel in via een service zoals browserless.io of verbind ge eigen", + "self-hosted container": "Self-hosted container", + "remoteBrowserToggle": "Chromium draait standaard in de Uptime Kuma container. Door dit aan te zetten kun ge een remote browser gebruiken.", + "useRemoteBrowser": "Gebruik een Remote Browser", + "deleteRemoteBrowserMessage": "Zijt ge zeker dat ge deze Remote Browser voor alle monitors wil wegdoen?", + "Screenshot Delay": "Screenshot vertraging (wacht {milliseconds})", + "milliseconds": "{n} millisecond | {n} milliseconden", + "screenshotDelayDescription": "Wacht eventueel zoveel milliseconden voordat ge de schermafbeelding maakt. Maximaal: {maxValueMs} ms (0,5 × interval).", + "screenshotDelayWarning": "Hogere waarden zorgen ervoor dat de browser langer open blijft, waardoor het geheugengebruik bij veel gelijktijdige monitoren kan toenemen.", + "GrafanaOncallUrl": "Grafana Oncall URL", + "systemService": "Systeemservice", + "systemServiceName": "Service naam", + "systemServiceDescription": "Controleert of systeem service {service_name} actief is", + "systemServiceDescriptionLinux": "Controleert of Linux systemd service {service_name} actief is", + "systemServiceDescriptionWindows": "Controleert of Windows Service Manager {service_name} actief is", + "systemServiceExpectedOutput": "Verwachte output: \"{0}\"", + "Browser Screenshot": "Browser Screenshot", + "Command": "Commando", + "mongodbCommandDescription": "Draai een MongoDB commando tegen de database. Voor meer informatie over beschikbare commando's, raadpleeg de {documentation}", + "wayToGetSevenIOApiKey": "Bezoek het dashboard onder app.seven.io > developer > api key > de groene knop", + "senderSevenIO": "Verzenden van nummer of naam", + "receiverSevenIO": "Nummer ontvangen", + "receiverInfoSevenIO": "Als het ontvangende nummer zich nie in Duitsland bevindt, moet ge de landcode vóór het nummer zetten (bijvoorbeeld voor de landcode 1 uit de VS gebruikt ge 117612121212 in plaats van 017612121212)", + "gtxMessagingFromHint": "Op mobiele telefoons, de ontvangers zien het TPOA als de afzender van het bericht. Toegestaan zijn maximaal 11 alfanumerieke karakters, een shortcode, het lokale longcode of internationale nummers ({e164}, {e212} of {e214})", + "apiKeySevenIO": "SevenIO API Sleutel", + "wayToWriteWhapiRecipient": "Het telefoonnummer met landcode, maar zonder + aan het begin ({0}), het contact ID ({1}) of groep ID ({2}).", + "wayToGetWhapiUrlAndToken": "Ge kunt de API-URL en de token verkrijgen door naar het gewenste kanaal te gaan vanaf {0}", + "whapiRecipient": "Telefoonnummer / Contact-ID / Groeps-ID", + "API URL": "API URL", + "wayToWriteEvolutionRecipient": "Het telefoonnummer met het internationale voorvoegsel, maar zonder de plus aan het begin ({0}), het Contact ID ({1}) of het groeps-ID ({2}).", + "wayToGetEvolutionUrlAndToken": "Ge kan een API URL en token verkrijgen door naar het gewenste kanaal te gaan van {0}", + "evolutionRecipient": "Telefoonnummer / Contact ID / Group ID", + "evolutionInstanceName": "Instantienaam", + "What is a Remote Browser?": "Wat is een remote webbrowser?", + "wayToGetHeiiOnCallDetails": "Hoe de Trigger ID en de API Keys verkregen kunnen wordt is terug te lezen in de {documentation}", + "documentationOf": "{0} Documentatie", + "callMeBotGet": "Hier kan ge een eindpunt genereren voor {0}, {1} en {2}. Houd er wel rekening mee dat er een maximaal aantal aanvragen zijn. Het maximum lijkt te zijn: {3}", + "gtxMessagingApiKeyHint": "Ge kan ge API key vinden bij: My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "Van telefoonnummer / Transmission Path Originating Address (TPOA)", + "To Phone Number": "Naar telefoonnummer", + "gtxMessagingToHint": "Internationaal formaat, met \"+\" aan het begin ({e164}, {e212} of {e214})", + "Originator type": "Afzender type", + "Telephone number": "Telefoonnummer", + "cellsyntOriginatortypeAlphanumeric": "Alfanumerieke reeks (max. 11 alfanumerieke karakters). Ontvangers kunnen nie op het bericht reageren.", + "cellsyntOriginatortypeNumeric": "Numerieke waarde (max. 15 cijfers) met telefoonnummer met landcode zonder + aan het begin (een Nederlands nummer 06 12 34 56 78 moet bijvoorbeeld wordt ingesteld als 31612345678). Ontvangers kunnen op het bericht reageren.", + "Originator": "Afzender", + "cellsyntOriginator": "Zichtbaar op de mobiele telefoon van de ontvanger als afzender van het bericht. Toegestane waarden en functie zijn afhankelijk van het parameter-originatortype.", + "Destination": "Bestemming", + "cellsyntDestination": "Ontvanger's telefoonnummer met landcode zonder + aan het begin (een Nederlands nummer 06 12 34 56 78 moet bijvoorbeeld wordt ingesteld als 31612345678). Maximaal 25.000 door komma's gescheiden ontvangers per HTTP-verzoek.", + "Allow Long SMS": "Lange SMS toestaan", + "cellsyntSplitLongMessages": "Scheid lange berichten op in 6 delen. 153 x 6 = 918 karakters.", + "max 15 digits": "max 15 cijfers", + "max 11 alphanumeric characters": "max 11 alfanumerieke tekens", + "Community String": "Gemeenschapsreeks", + "snmpCommunityStringHelptext": "Deze string fungeert als een wachtwoord om toegang tot SNMP-apparaten te verifiëren en te beheren. Match het met de configuratie van uw SNMP-apparaat.", + "OID (Object Identifier)": "OID (Object indentificatie)", + "snmpOIDHelptext": "Voer de OID in voor de sensor of status die ge wil monitoren. Gebruik netwerkbeheertools zoals MIB-browsers of SNMP-software als ge nie zeker bent over de OID.", + "snmpV3Username": "SNMPv3 gebruikersnaam", + "Condition": "Conditie", + "SNMP Version": "SNMP Versie", + "Please enter a valid OID.": "Voer a.u.b. een geldige OID in.", + "wayToGetThreemaGateway": "Ge kunt ge registreren voor Threema Gateway {0}.", + "threemaRecipientType": "Type ontvanger", + "threemaRecipientTypeIdentity": "Threema-ID", + "threemaRecipientTypeIdentityFormat": "8 Karakters", + "threemaRecipientTypePhone": "Telefoonnummer", + "threemaRecipientTypePhoneFormat": "E.164, zonder voorgaande +", + "threemaRecipientTypeEmail": "E-mailadres", + "threemaSenderIdentity": "Gateway ID", + "threemaSenderIdentityFormat": "8 karakters, begint normaal met een *", + "threemaApiAuthenticationSecret": "Gateway ID Geheim", + "threemaBasicModeInfo": "Notitie: Deze integratie gebruikt Threema Gateway in de basis modus (server gebaseerde versleuteling). Meer details vind ge hier {0}.", + "apiKeysDisabledMsg": "API-Sleutels zijn uitgeschakeld omdat authenticatie nie is ingeschakeld.", + "Host Onesender": "Host Onesender", + "Token Onesender": "Token Onesender", + "Recipient Type": "Ontvanger Type", + "Private Number": "Privenummer", + "privateOnesenderDesc": "Zorg ervoor dat het telefoonnummer juist is. Om een bericht te sturen naar een privenummer, bijvoorbeeld: 628123456789", + "groupOnesenderDesc": "Zorg ervoor dat de GroupID juist is. Om een bericht naar een groep te sturen, bijvoorbeeld: 628123456789-342345", + "Group ID": "Groep ID", + "wayToGetOnesenderUrlandToken": "Ge kunt de URL en Token krijgen door naar de Onesender website te gaan. Meer informatie {0}", + "Add Remote Browser": "Externe browser toevoegen", + "New Group": "Nieuwe Groep", + "Group Name": "Groeps naam", + "OAuth2: Client Credentials": "OAuth2: Client referenties", + "Authentication Method": "Authenticatie methode", + "Authorization Header": "Autorisatie Header", + "Form Data Body": "Formulier Gegevens Body", + "OAuth Token URL": "OAuth Token URL", + "Client ID": "Client ID", + "Client Secret": "Client geheim", + "OAuth Scope": "OAuth bereik", + "OAuth Audience": "OAuth publiek", + "Optional: The audience to request the JWT for": "Optioneel: het publiek om de JWT voor aan te vragen", + "Optional: Space separated list of scopes": "Optioneel: door spaties gescheiden lijst met scopes", + "Go back to home page.": "Ga terug naar de home pagina.", + "No tags found.": "Gie tags gevonden.", + "Lost connection to the socket server.": "Verbinding met de socketserver verloren.", + "Cannot connect to the socket server.": "Kan gie verbinding maken met de socketserver.", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL", + "signl4Docs": "Meer informatie over het configureren van SIGNL4 en het verkrijgen van de SIGNL4-webhook-URL kunt ge hier vinden {0}.", + "Conditions": "Voorwaarden", + "conditionAdd": "Voorwaarde toevoegen", + "conditionDelete": "Voorwaarde verweideren", + "conditionAddGroup": "Groep toevoegen", + "conditionDeleteGroup": "Groep verweideren", + "conditionValuePlaceholder": "Waarde", + "equals": "hetzelfde als", + "not equals": "nie gelijk aan", + "contains": "bevat", + "not contains": "bevat nie", + "starts with": "start met", + "not starts with": "start nie met", + "ends with": "eindigt met", + "not ends with": "eindigt nie met", + "less than": "minder dan", + "greater than": "groter dan", + "less than or equal to": "minder dan of gelijk aan", + "greater than or equal to": "meer dan of gelijk aan", + "record": "dossier", + "message": "Bericht", + "json_value": "JSON waarde", + "Notification Channel": "Notificatie kanaal", + "Sound": "Geluid", + "Alphanumerical string and hyphens only": "Alleen alfanumerieke tekens en streepjes", + "Arcade": "Spelletjes", + "Correct": "Goed", + "Fail": "Mislukt", + "rabbitmqNodesDescription": "Voer het URL voor de RabbitMQ beheer nodes inclusief protocol en poort in. Bijvoorbeeld: {0}", + "rabbitmqNodesRequired": "Aub stel de knooppunten voor deze monitor in.", + "rabbitmqNodesInvalid": "Gebruik een volledig gekwalificeerde (beginnend met 'http') URL voor de RabbitMQ nodes.", + "RabbitMQ Username": "RabbitMQ gebruikersnaam", + "Harp": "Harp", + "Reveal": "Laat zien", + "Bubble": "Bubbel", + "Doorbell": "Deurbel", + "Flute": "Fluit", + "Money": "Geld", + "Scifi": "Science fiction", + "Clear": "Helder", + "Select All": "Selecteer alle", + "Deselect All": "Deselecteer alle", + "Elevator": "Lift", + "Guitar": "Gitaar", + "Pop": "Pop", + "Custom sound to override default notification sound": "Aangepast geluid om het standaard geluid te vervangen", + "Time Sensitive (iOS Only)": "Tijdsgevoelig (alleen voor iOs)", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "Tijdsgevoelige meldingen wordt meteen afgeleverd, zelfs als het apparaat in nie storen modus staat.", + "From": "Van", + "Can be found on:": "Kan gevonden wordt op: {0}", + "The phone number of the recipient in E.164 format.": "Het telefoonnummer van de ontvanger in E.164 formaat.", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "Ofwel een sms zender ID of een telefoonnummer in E.164 formaat als ge reacties wil ontvangen.", + "RabbitMQ Nodes": "RabbitMQ beheer Nodes", + "Enter the list of nodes": "Voer de lijst met RabbitMQ-beheer­nodes in", + "Press Enter to add node": "Druk op Enter om node toe te voegen", + "RabbitMQ Password": "RabbitMQ wachtwoord", + "rabbitmqHelpText": "Om gebruik te maken van de monitor moet ge de Management Plugin in de RabbitMQ setup aanzetten. Voor meer informatie zie de {rabitmq_documentatie}.", + "SendGrid API Key": "SendGrid API sleutel", + "Separate multiple email addresses with commas": "Splits meerdere emailadressen met kommas", + "brevoApiKey": "Brevo API Sleutel", + "brevoApiHelp": "Maak hier een API sleutel: {0}", + "brevoFromEmail": "Van Email", + "brevoFromName": "Van Naam", + "brevoLeaveBlankForDefaultName": "laat leeg voor de standaard naam", + "brevoToEmail": "Naar Email", + "brevoCcEmail": "CC email", + "brevoBccEmail": "BCC email", + "brevoSeparateMultipleEmails": "Scheid meerdere email-adressen met komma", + "brevoSubject": "Onderwerp", + "brevoLeaveBlankForDefaultSubject": "laat leeg voor standaard onderwerp", + "resendApiKey": "Verstuur API sleutel opnieuw", + "resendApiHelp": "Maak een api sleutel hier {0}", + "resendFromName": "Van naam", + "resendFromEmail": "Van email", + "resendLeaveBlankForDefaultName": "laat leeg voor de standaard naam", + "resendLeaveBlankForDefaultSubject": "Laat dit leeg voor het standaardonderwerp", + "resendToEmail": "Naar email", + "resendSubject": "Onderwerp", + "pingCountLabel": "Maximaal pakketten", + "pingCountDescription": "Aantal pakketten om te sturen voordat gestopt wordt", + "pingNumericLabel": "Numerieke output", + "pingNumericDescription": "Indien aangevinkt, wordt IP-adressen weergegeven in plaats van symbolische hostnamen", + "pingGlobalTimeoutLabel": "Globale timeout", + "pingGlobalTimeoutDescription": "Totale tijd in seconden voordat pingen stopt, ongeacht aantal verstuurde pakketten", + "pingPerRequestTimeoutLabel": "Per-ping timeout", + "pingPerRequestTimeoutDescription": "Dit is de maximale wachttijd (in seconden) voordat een afzonderlijk pingpakket als verloren wordt beschouwd", + "pingIntervalAdjustedInfo": "Het interval wordt aangepast op basis van het aantal pakketten, de globale time-out en de time-out per ping", + "smtpHelpText": "‘SMTPS’ test of SMTP/TLS werkt; ‘Ignore TLS’ maakt verbinding via platte tekst; ‘STARTTLS’ maakt verbinding, geeft een STARTTLS-commando en verifieert het servercertificaat. Gie van deze opties verstuurt een e-mail.", + "Custom URL": "Eigen URL", + "customUrlDescription": "Wordt gebruikt als klikbare URL in plaats van de monitor URL.", + "OneChatAccessToken": "OneChat toegangstoken", + "OneChatUserIdOrGroupId": "OneChat User ID of Group ID", + "OneChatBotId": "OneChat Bot id", + "wahaSession": "Sessie", + "wahaChatId": "Chat-ID (telefoonnummer / contact-ID / groeps-ID)", + "wayToGetWahaApiUrl": "Ge WAHA Instance URL.", + "wayToGetWahaApiKey": "API Key is de WHATSAPP_API_KEY omgevingsvariabele die ge hebt gebruikt om WAHA uit te voeren.", + "wayToGetWahaSession": "Vanaf deze sessie stuurt WAHA meldingen naar Chat ID. Ge kunt deze vinden in WAHA Dashboard.", + "wayToWriteWahaChatId": "Het telefoonnummer met het internationale voorvoegsel, maar zonder het plusteken aan het begin ({0}), de contact-ID ({1}) of de groeps-ID ({2}). Vanuit WAHA Sessie wordt meldingen naar deze Chat-ID verzonden.", + "YZJ Webhook URL": "YZJ Webhook URL", + "YZJ Robot Token": "YZJ Robot token", + "Plain Text": "Platte tekst", + "Message Template": "Bericht Sjabloon", + "Template Format": "Sjabloonformaat", + "Font Twemoji by Twitter licensed under": "Lettertype Twemoji van Twitter gelicentieerd onder", + "smsplanetApiToken": "Token voor de SMSPlanet API", + "smsplanetApiDocs": "Gedetailleerde informatie over het verkrijgen van API-tokens vindt ge op {the_smsplanet_documentation}.", + "the smsplanet documentation": "de smsplanet documentatie", + "Phone numbers": "Telefoonnummers", + "Sender name": "Naam afzender", + "smsplanetNeedToApproveName": "Moet wordt goedgekeurd in het clientpaneel", + "Google": "Google", + "Plausible": "Plausibel", + "Matomo": "Matomo", + "Umami": "Umami", + "Disable URL in Notification": "URL in notificatie uitzetten", + "Suppress Notifications": "Meldingen onderdrukken", + "discordSuppressNotificationsHelptext": "Indien ingeschakeld, wordt berichten op het kanaal geplaatst, maar wordt er gie push- of bureaubladmeldingen voor ontvangers geactiveerd.", + "discordUseMessageTemplateDescription": "Indien ingeschakeld, wordt het bericht verzonden met behulp van een aangepaste sjabloon (LiquidJS). Laat dit veld leeg om het standaard Uptime Kuma-formaat te gebruiken.", + "Ip Family": "IP familie", + "ipFamilyDescriptionAutoSelect": "Gebruikt {happyEyeballs} voor het vaststellen van de IP familie.", + "Happy Eyeballs algorithm": "Happy Eyeballs algoritme", + "Add Another Tag": "Nog een tag toevoegen", + "Staged Tags for Batch Add": "Gefaseerde tags voor batchtoevoeging", + "Clear Form": "Formulier wissen", + "pause": "Pauzeer", + "Manual": "Handmatig", + "Nextcloud host": "Nextcloud host", + "Conversation token": "Conversatie token", + "Bot secret": "Bot geheim", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "Installeren van een Nextcloud Talk bot vereist administrator-toegang op de server.", + "Globalping - Access global monitoring probes": "Globalping - Toegang tot wereldwijde monitoringsondes", + "GlobalpingDescription": "Globalping biedt toegang tot duizenden door de gemeenschap gehoste tests om netwerktests en metingen uit te voeren. Voor alle anonieme gebruikers is een limiet van 250 tests per uur ingesteld. Als ge de limiet wil verdubbelen naar 500 per uur, slaat ge uw token op in {accountSettings}.", + "Globalping API Token": "Globalping API token", + "globalpingApiTokenDescription": "Haal uw Globalping API-token op bij {0}.", + "GlobalpingHostname": "Een publiek bereikbaar meetdoel. Meestal een hostnaam of IPv4/IPv6-adres, afhankelijk van het meettype.", + "GlobalpingLocation": "Het locatieveld accepteert continenten, landen, regio's, steden, ASN's, ISP's of cloudregio's. Ge kunt filters combineren met {plus} (bijvoorbeeld {amazonPlusGermany} of {comcastPlusCalifornia}). Als latentie een belangrijke statistiek is, gebruikt ge filters om de locatie te beperken tot een kleine regio om pieken te voorkomen. {fullDocs}.", + "GlobalpingLocationDocs": "Volledige documentatie voor locatie-invoer", + "GlobalpingIpFamilyInfo": "De IP-versie die moet wordt gebruikt. Alleen toegestaan als het doel een hostnaam is.", + "GlobalpingResolverInfo": "IPv4/IPv6-adres of een volledig gekwalificeerde domeinnaam (FQDN). Standaard ingesteld op de lokale netwerkresolver van de probe. Ge kunt de resolutieserver op elk gewenst moment wijzigen.", + "Resolver Server": "Resolver Server", + "Protocol": "Protocol", + "account settings": "account instellingen", + "Location": "Locatie", + "Monitor Subtype": "Monitor subtype", + "Check for": "Controleer op", + "Number of retry attempts if webhook fails": "Aantal pogingen (elke 60-180 seconden) wanneer de webhook mislukt.", + "domain_expiry_unsupported_is_ip": "\"{hostname}\" is een IP-adres. Voor het monitoren van het verlopen van domeinen is een domeinnaam vereist", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "Controle van domeinverval is nie beschikbaar voor \".{publicSuffix}\" omdat er gie RDAP-service wordt vermeld door IANA", + "Maximum Retries": "Maximaal aantal pogingen", + "Template ID": "Sjabloon ID", + "wayToGetClickSMSIRTemplateID": "Ge sjabloon moet een {uptkumaalert} veld bevatten. Ge kan een nieuwe sjabloon {here} aanmaken.", + "Recipient Numbers": "Nummers ontvangers", + "Notifications Enabled": "Notificaties ingeschakeld", + "Webpush Helptext": "Web push werkt alleen met SSL- (HTTPS-)verbindingen. Voor iOS-apparaten moet de webpagina vooraf aan het beginscherm zijn toegevoegd.", + "settingsDomainExpiry": "Domein vervaldatum", + "labelDomainExpiry": "Domein verval.", + "labelDomainNameExpiryNotification": "Domeinnaam verval notificatie", + "domainExpiryDescription": "Trigger notificatie wanneer de domeinnaam vervalt binnen:", + "domain_expiry_unsupported_monitor_type": "Domein verloop monitoring is nie ondersteund voor dit monitor-type", + "domain_expiry_unsupported_missing_target": "Gie geldige domeinnaam of hostnaam is ingesteld voor deze monitor", + "domain_expiry_public_suffix_too_short": "\".{publicSuffix}\" is te kort voor een topleveldomein", + "domain_expiry_unsupported_is_icann": "Het domein \"{domain}\" komt nie in aanmerking voor monitoring van domeinverval, omdat het openbare achtervoegsel \".{publicSuffix}\" gie ICANN is", + "minimumIntervalWarning": "Intervallen onder de 20 seconden kunnen resulteren in slechte performance.", + "lowIntervalWarning": "Zijt ge zeker dat ge de intervalwaarde onder de 20 seconden wil instellen? De prestaties kunnen hierdoor verslechteren, vooral als er een groot aantal monitors is.", + "Halo PSA": "Halo PSA", + "Halo PSA Webhook URL": "Halo PSA webhook URL", + "halopsa_webhook_url_desc": "Voer de webhook-URL uit uw Halo PSA Integration Runbook in (Configuratie > Integraties > Aangepaste integraties > Integratie Runbooks). Selecteer 'Kan alleen wordt gestart vanuit Halo en vanaf een openbaar eindpunt' bij het maken van de webhook.", + "username": "Gebruikersnaam", + "password": "Wachtwoord", + "halopsa_username_desc": "Gebruikersnaam voor authenticatie bij Halo PSA webhook", + "halopsa_password_desc": "Wachtwoord voor authenticatie bij Halo PSA webhook", + "imageResetConfirmation": "Afbeelding terugzetten naar standaard", + "screenshot of the website": "Screenshot van de website", + "Basic checkbox toggle button group": "Basis selectievakge zetten knopgroep", + "Basic radio toggle button group": "Basis radioknoppen groep", + "mtls-auth-server-cert-label": "Certificaat", + "mtls-auth-server-cert-placeholder": "Certificaat body", + "mtls-auth-server-key-label": "Sleutel", + "mtls-auth-server-key-placeholder": "Sleutel inhoud", + "mtls-auth-server-ca-label": "CA", + "mtls-auth-server-ca-placeholder": "Server CA", + "avgPing": "Gemiddelde ping", + "Uptime Kuma": "Uptime Kuma", + "maxPing": "Max ping", + "minPing": "Min ping", + "Setup Instructions": "Instel-instructies", + "halopsa_setup_step1": "Maak een integratie runbook in HaloPSA (Configuration → Integrations → Integration Runbooks)", + "halopsa_setup_step2": "Configureer runbook acties om meldingen te verwerken (bijv. Maak ticket)", + "halopsa_setup_step3": "Kopieer de webhook URL en plak deze in het bovenstaande tekstveld", + "halopsa_setup_step4": "Kies basis authenticatie en maak een gebruikersnaam en wachtwoord. En typ of plak de gebruikersnaam en wachtwoord in bovenstaande velden", + "Clear current filters": "Wis huidige filters", + "Sort options": "Sorteer opties", + "Sort by status": "Sorteer op status", + "Sort by name": "Sorteer op naam", + "Sort by uptime": "Sorteer op uptime", + "Sort by certificate expiry": "Sorteer op certificaat verlooptijd", + "Splunk Rest URL": "Splunk rest URL", + "Message Format": "Berichtformaat", + "smscTranslit": "smscTranslit", + "Region": "Regio", + "PushDeer Server URL": "PushDeer server URL", + "To Number": "Naar nummer", + "GrafanaOncallURL": "Grafana Oncall URL", + "Never": "Nooit", + "Json Query": "Json zoekopdracht", + "System Service": "Systeemservice", + "SSL/TLS": "SSL/TLS", + "playground": "playground", + "Check Type": "Controleer type", + "Service Name": "Service naam", + "GRPC Options": "GRPC opties", + "Metadata": "Metagegevens", + "End": "Eind", + "Show this Maintenance Message on which Status Pages": "Toon dit onderhoudsbericht op deze statuspagina's", + "Endpoint": "Endpoint", + "Details": "Details", + "passwordTooWeak": "Wachtwoord is te zwak. Het moet minimaal alfanumerieke karakters bevatten en minstens 6 karakters lang zijn.", + "TLS Alerts": "TLS waarschuwingen", + "Expected TLS Alert": "Verwachtte TLS melding", + "None (Successful Connection)": "Gie (succesvolle verbinding)", + "expectedTlsAlertDescription": "Selecteer de TLS-waarschuwing waarvan ge verwacht dat de server deze retourneert. Gebruik {code} om te verifiëren dat mTLS-eindpunten verbindingen weigeren zonder clientcertificaten. Zie {link} voor details.", + "TLS Alert Spec": "RFC 8446", + "mariadbSocketPathDetectedHelptext": "Verbinding maken met de database zoals opgegeven via de omgevingsvariabele {0}.", + "Expand All Groups": "Alle groepen uitklappen", + "Collapse All Groups": "Alle groepen inklappen", + "Webhook Payload Fields": "Webhook payload velden", + "halopsa_payload_desc": "De volgende velden zijn verstuurd naar ge Halo PSA webhook:", + "halopsa_field_title": "Melding titel (altijd 'Uptime Kuma Alert')", + "halopsa_field_status": "Monitor status: UP, DOWN, NOTIFICATION, of UNKNOWN", + "halopsa_field_monitor": "Naam van de monitor", + "halopsa_field_monitor_id": "Uniek monitor ID (null voor test meldingen) - gebruik dit om te matchen met tickets", + "halopsa_field_message": "Volledige meldtekst met status en details", + "halopsa_field_timestamp": "Event tijdstempel in ISO 8601 formaat", + "halopsa_field_uptime_kuma_version": "Uptime Kuma versienummer", + "halopsa_id_usage_hint": "💡 Tip: Gebruik monitor_id om waarschuwingen betrouwbaar te koppelen aan tickets, en heartbeat_id om de gebeurtenisgeschiedenis bij te houden" +} diff --git a/src/lang/xh.json b/src/lang/xh.json new file mode 100644 index 0000000..29d4c0e --- /dev/null +++ b/src/lang/xh.json @@ -0,0 +1,7 @@ +{ + "setupDatabaseEmbeddedMariaDB": "Akukho mfuneko yokuba usethe nantoni na. Lo mfanekiso weDocker sele uqulathe kwaye ulungiselele iMariaDB ngokuzenzekelayo. I-Uptime Kuma iya kuqhagamshela kule database nge-unix socket.", + "setupDatabaseChooseDatabase": "Ngowuphi umthombo wedatha ofuna ukuwusebenzisa?", + "setupDatabaseMariaDB": "Qhagamshela kwi-database yeMariaDB yangaphandle. Kuya kufuneka usethe ulwazi loqhagamshelo lwedathabase.", + "setupDatabaseSQLite": "I database elula, ecetyiswayo kusetyenziso oluncinci. Ngaphambi kwe v2.0.0, i-Uptime Kuma yayisebenzisa i-SQLite njenge database yomdibaniso.", + "settingUpDatabaseMSG": "Ukumisela i-database. Kungathatha ixesha, nceda ube nomonde." +} diff --git a/src/lang/yue.json b/src/lang/yue.json new file mode 100644 index 0000000..65eb635 --- /dev/null +++ b/src/lang/yue.json @@ -0,0 +1,110 @@ +{ + "languageName": "繁體中文 (廣東話 / 粵語)", + "Settings": "設定", + "General": "一般", + "Dashboard": "表板", + "Help": "幫助", + "New Update": "有新版本", + "Language": "語言", + "Appearance": "外觀", + "Theme": "主題", + "Game": "遊戲", + "Version": "版本", + "Check Update On GitHub": "去 GitHub 睇下有冇更新", + "List": "列表", + "Add": "新增", + "Primary Base URL": "主要 Base URL", + "Heartbeat Retry Interval": "確定為離線的重試間隔", + "retryCheckEverySecond": "每 {0} 秒重試一次", + "add one": "加一個", + "upsideDownModeDescription": "反轉狀態,如果網址係可以正常瀏覽,會被判定為 '離線/DOWN'", + "Not available, please setup.": "未可以用,需要設定。", + "Discourage search engines from indexing site": "唔建議搜尋器索引", + "Remember me": "記住我", + "Test": "測試", + "DateTime": "日期時間", + "Resume": "恢復", + "statusMaintenance": "維護緊", + "Maintenance": "維護", + "Unknown": "唔知", + "pauseDashboardHome": "暫停", + "Pause": "暫停", + "Status": "狀態", + "Message": "內容", + "No important events": "冇重要事件", + "Edit": "編輯", + "Delete": "刪除", + "Current": "目前", + "Uptime": "上線率", + "day": "日 | 日", + "-day": "日", + "hour": "個鐘", + "-hour": "個鐘", + "Response": "反應時間", + "Ping": "反應時間", + "URL": "網址", + "Retries": "重試數次確定為離線", + "Advanced": "進階", + "ignoreTLSError": "唔理 TLS/SSL 錯誤", + "Upside Down Mode": "反轉模式", + "Accepted Status Codes": "接受為上線嘅 HTTP 狀態碼", + "Save": "儲存", + "Notifications": "通知", + "Setup Notification": "設定通知", + "Light": "明亮", + "Dark": "暗黑", + "Auto": "自動", + "Normal": "一般", + "Bottom": "下方", + "None": "冇", + "Timezone": "時區", + "Search Engine Visibility": "係咪允許搜尋器索引", + "Allow indexing": "允許索引", + "Change Password": "改密碼", + "Current Password": "而家嘅密碼", + "New Password": "新密碼", + "Repeat New Password": "確認新密碼", + "Update Password": "更新密碼", + "Disable Auth": "取消登入認証", + "Enable Auth": "開啟登入認証", + "disableauth.message1": "你係咪確認想{disableAuth}?", + "disable authentication": "取消登入認証", + "Please use this option carefully!": "請小心使用。", + "Logout": "登出", + "Leave": "離開", + "I understand, please disable": "我知,唔該取消登入認証", + "Confirm": "確認", + "Yes": "係", + "No": "唔係", + "Username": "帳號", + "Password": "密碼", + "Login": "登入", + "Notification Type": "通知類型", + "Email": "電郵", + "Repeat Password": "重複密碼", + "Up": "上線", + "Down": "離線", + "Pending": "待定", + "Name": "名稱", + "General Monitor Type": "一般監測器類型", + "Passive Monitor Type": "被動監測器類型", + "Specific Monitor Type": "特定監測器類型", + "Monitor": "監測器 | 監測器", + "Keyword": "關鍵字", + "Friendly Name": "名稱", + "Hostname": "Hostname", + "Port": "Port", + "No Monitors, please": "冇監測器,請", + "Monitor Type": "監測器類型", + "Heartbeat Interval": "檢查間距", + "Add New Monitor": "新增監測器", + "Quick Stats": "綜合數據", + "markdownSupported": "可以用 Markdown", + "wayToGetFlashDutyKey": "您可以进入 协作空间 -> (选择一个 协作空间) -> 集成数据 -> 新增一个集成 页面,添加“自定义事件”获得一个推送地址,复制地址中的 Integration Key,更多信息前往{0}", + "FlashDuty Severity": "严重程度", + "setupDatabaseChooseDatabase": "你想用咩資料庫?", + "dbName": "資料庫名稱", + "Home": "首頁", + "Cannot connect to the socket server": "連唔到 Socket 伺服器", + "Reconnecting...": "重新連線中..." +} diff --git a/src/lang/zh-CN.json b/src/lang/zh-CN.json new file mode 100644 index 0000000..b987aa7 --- /dev/null +++ b/src/lang/zh-CN.json @@ -0,0 +1,1765 @@ +{ + "languageName": "简体中文", + "checkEverySecond": "检测频率 {0} 秒", + "retryCheckEverySecond": "重试间隔 {0} 秒", + "resendEveryXTimes": "每 {0} 次失败则重复发送一次", + "resendDisabled": "禁用重复发送", + "retriesDescription": "服务被标记为故障并发送通知之前的最大重试次数", + "ignoreTLSError": "忽略 HTTPS 站点的 TLS/SSL 错误", + "upsideDownModeDescription": "反转监控状态。如果服务可访问,则认为是故障。", + "maxRedirectDescription": "允许的最大重定向次数。设置为 0 以禁用重定向。", + "enableGRPCTls": "允许通过 TLS 连接发送 gRPC 请求", + "grpcMethodDescription": "方法名会转换为小驼峰格式,例如 sayHello、check 等等。", + "acceptedStatusCodesDescription": "选择被视为成功响应的状态码。", + "Maintenance": "维护", + "statusMaintenance": "维护", + "Schedule maintenance": "计划维护", + "Affected Monitors": "受影响的监控项", + "Pick Affected Monitors...": "选择受影响的监控项…", + "Start of maintenance": "维护开始", + "All Status Pages": "所有状态页面", + "Select status pages...": "选择状态页面…", + "recurringIntervalMessage": "每天一次 | 每 {0} 天一次", + "affectedMonitorsDescription": "选择受当前维护影响的监控项", + "affectedStatusPages": "在所选状态页面上显示此维护消息", + "atLeastOneMonitor": "至少选择一个受影响的监控项", + "passwordNotMatchMsg": "两次输入的密码不一致。", + "notificationDescription": "通知必须被分配给监控项才能正常工作。", + "keywordDescription": "在纯 HTML 或 JSON 响应中搜索关键字,区分大小写。", + "pauseDashboardHome": "暂停", + "deleteMonitorMsg": "确定要删除此监控项吗?", + "deleteMaintenanceMsg": "确定要删除此维护事件吗?", + "deleteNotificationMsg": "确定要为所有监控项删除此通知吗?", + "dnsPortDescription": "DNS 服务器端口,默认为 53,您可以在任何时候更改此端口。", + "resolverserverDescription": "默认服务器是 Cloudflare。您可以指定以逗号分隔的IP地址或主机名列表。", + "rrtypeDescription": "选择要监控的资源记录类型", + "pauseMonitorMsg": "确定要暂停吗?", + "enableDefaultNotificationDescription": "新的监控项将默认启用此通知,您仍然为每个监控项单独禁用。", + "clearEventsMsg": "确定要删除此监控项的所有事件吗?这将永久删除关联的消息数据,且无法撤销。", + "clearHeartbeatsMsg": "确定要删除此监控项的所有心跳状态吗?这将永久删除关联的心跳数据,且无法撤销。", + "confirmClearStatisticsMsg": "确定要删除所有统计信息吗?", + "importHandleDescription": "如果想跳过同名的监控项或消息通知,请选择“跳过已存在”。“覆盖”将删除所有现有的监控项和通知。", + "confirmImportMsg": "确定要导入备份吗?请确保已经选择了正确的导入选项。", + "twoFAVerifyLabel": "请输入令牌码以确认二次验证:", + "tokenValidSettingsMsg": "令牌码有效!您现在可以保存二次验证设置了。", + "confirmEnableTwoFAMsg": "确定要启用二次验证吗?", + "confirmDisableTwoFAMsg": "确定要禁用二次验证吗?", + "Settings": "设置", + "Dashboard": "仪表盘", + "New Update": "有新版本", + "Language": "语言", + "Appearance": "外观", + "Theme": "主题", + "General": "常规", + "Primary Base URL": "站点主 URL", + "Version": "版本", + "Check Update On GitHub": "检查 GitHub 上的更新", + "List": "列表", + "Add": "添加", + "Add New Monitor": "添加监控项", + "Quick Stats": "状态速览", + "Up": "正常", + "Down": "故障", + "Pending": "重试中", + "Unknown": "未知", + "Pause": "暂停", + "Name": "名称", + "Status": "状态", + "DateTime": "日期时间", + "Message": "消息", + "No important events": "暂无重要事件", + "Resume": "恢复", + "Edit": "编辑", + "Delete": "删除", + "Current": "当前", + "Uptime": "在线时间", + "Cert Exp.": "证书有效期", + "day": "天", + "-day": "天", + "hour": "小时", + "-hour": "小时", + "Response": "响应", + "Ping": "Ping", + "Monitor Type": "监控类型", + "Keyword": "关键字", + "Friendly Name": "显示名称", + "URL": "URL", + "Hostname": "主机名", + "Port": "端口", + "Heartbeat Interval": "心跳间隔", + "Retries": "重试次数", + "Heartbeat Retry Interval": "心跳重试间隔", + "Resend Notification if Down X times consecutively": "连续失败时重复发送通知的间隔次数", + "Advanced": "高级", + "Upside Down Mode": "反转模式", + "Max. Redirects": "最大重定向次数", + "Accepted Status Codes": "有效状态码", + "Push URL": "推送 URL", + "needPushEvery": "您需要每 {0} 秒调用一次该 URL。", + "pushOptionalParams": "可选参数:{0}", + "Save": "保存", + "Notifications": "通知", + "Not available, please setup.": "暂不可用,请先设置。", + "Setup Notification": "设置通知", + "Light": "明亮", + "Dark": "黑暗", + "Auto": "自动", + "Theme - Heartbeat Bar": "主题 - 心跳栏", + "Normal": "正常", + "Bottom": "靠下", + "None": "不显示", + "Timezone": "时区", + "Search Engine Visibility": "搜索引擎可见性", + "Allow indexing": "允许索引", + "Discourage search engines from indexing site": "阻止搜索引擎索引网站", + "Change Password": "修改密码", + "Current Password": "当前密码", + "New Password": "新密码", + "Repeat New Password": "重复新密码", + "Update Password": "更新密码", + "Disable Auth": "禁用登录验证", + "Enable Auth": "启用登录验证", + "disableauth.message1": "是否确定 {disableAuth}?", + "disable authentication": "取消登录验证", + "disableauth.message2": "这是为 {intendThirdPartyAuth} (如 Cloudflare Access、Authelia 或其他认证方式)的用户提供的功能。", + "where you intend to implement third-party authentication": "您打算在哪里实施第三方身份验证", + "Please use this option carefully!": "请谨慎使用!", + "Logout": "退出", + "Leave": "离开", + "I understand, please disable": "我已了解,继续禁用", + "Confirm": "确认", + "Yes": "是", + "No": "否", + "Username": "用户名", + "Password": "密码", + "Remember me": "记住我", + "Login": "登录", + "No Monitors, please": "还没有监控项,", + "add one": "点击添加", + "Notification Type": "通知类型", + "Email": "邮件", + "Test": "测试", + "Certificate Info": "证书信息", + "Resolver Server": "解析服务器", + "Resource Record Type": "资源记录类型", + "Last Result": "上次结果", + "Create your admin account": "创建管理员账户", + "Repeat Password": "重复密码", + "Import Backup": "导入备份", + "Export Backup": "导出备份", + "Export": "导出", + "Import": "导入", + "respTime": "响应时间(毫秒)", + "notAvailableShort": "N/A", + "Default enabled": "默认开启", + "Apply on all existing monitors": "应用到所有现有监控项", + "Create": "创建", + "Clear Data": "清除数据", + "Events": "事件", + "Heartbeats": "心跳", + "Auto Get": "自动获取", + "backupDescription": "您可以将所有监控项和通知备份到 JSON 文件。", + "backupDescription2": "注意: 不包括历史状态和事件数据。", + "backupDescription3": "导出的文件可能包含敏感信息,例如通知的令牌,请小心存放。", + "alertNoFile": "请选择要导入的文件。", + "alertWrongFileType": "请选择一个 JSON 文件。", + "Clear all statistics": "清除所有统计数据", + "Skip existing": "跳过已存在", + "Overwrite": "覆盖", + "Options": "选项", + "Keep both": "全部保留", + "Verify Token": "验证令牌", + "Setup 2FA": "设置二次验证", + "Enable 2FA": "启用二次验证", + "Disable 2FA": "禁用二次验证", + "2FA Settings": "二次验证设置", + "Two Factor Authentication": "二次验证", + "Active": "激活", + "Inactive": "停用", + "Token": "令牌", + "Show URI": "显示 URI", + "Tags": "标签", + "Add New below or Select...": "在下面添加或选择…", + "Tag with this name already exist.": "相同名称的标签已存在。", + "Tag with this value already exist.": "相同内容的标签已存在。", + "color": "颜色", + "value (optional)": "值(可选)", + "Gray": "灰色", + "Red": "红色", + "Orange": "橙色", + "Green": "绿色", + "Blue": "蓝色", + "Indigo": "靛蓝", + "Purple": "紫色", + "Pink": "粉色", + "Search...": "搜索…", + "Avg. Ping": "平均 Ping", + "Avg. Response": "平均响应", + "Entry Page": "入口页面", + "statusPageNothing": "这里什么也没有,请添加一个分组或一个监控项。", + "No Services": "无服务", + "All Systems Operational": "所有服务运行正常", + "Partially Degraded Service": "部分服务出现故障", + "Degraded Service": "全部服务出现故障", + "Add Group": "添加分组", + "Add a monitor": "添加监控项", + "Edit Status Page": "编辑状态页面", + "Go to Dashboard": "前往仪表盘", + "Status Page": "状态页面", + "Status Pages": "状态页面", + "defaultNotificationName": "{notification} 通知({number})", + "here": "这里", + "Required": "必填", + "telegram": "Telegram", + "ZohoCliq": "ZohoCliq", + "Bot Token": "机器人令牌", + "wayToGetTelegramToken": "您可以从 {0} 获取 Token。", + "Chat ID": "Chat ID", + "supportTelegramChatID": "支持对话/群组/频道的 Chat ID", + "wayToGetTelegramChatID": "您可以发送一条消息给您的机器人,然后访问此链接来查看 chat_id:", + "YOUR BOT TOKEN HERE": "这里替换成您的 BOT TOKEN", + "chatIDNotFound": "未找到 Chat ID,请先给您的机器人发送一条消息", + "webhook": "Webhook", + "Post URL": "Post URL", + "Content Type": "Content Type", + "webhookJsonDesc": "{0} 适合现代的 HTTP 服务器,例如 Express.js", + "webhookFormDataDesc": "{multipart} 适合 PHP,其中 JSON 需要使用 {decodeFunction} 解码", + "webhookAdditionalHeadersTitle": "额外 Header", + "webhookAdditionalHeadersDesc": "设置 webhook 请求的额外 Header。每一个 Header 应被定义为一对 JSON 键值对。", + "smtp": "电子邮件(SMTP)", + "secureOptionNone": "无 / STARTTLS(常用端口 25、587)", + "secureOptionTLS": "TLS(常用端口 465)", + "Ignore TLS Error": "忽略 TLS 错误", + "From Email": "发信人", + "emailCustomSubject": "邮件主题", + "To Email": "收信人", + "smtpCC": "抄送", + "smtpBCC": "密送", + "discord": "Discord", + "Discord Webhook URL": "Discord Webhook 网址", + "wayToGetDiscordURL": "可在服务器设置 -> 整合 -> Webhook -> 创建 Webhook 中获取", + "Bot Display Name": "机器人显示名称", + "Prefix Custom Message": "自定义消息前缀", + "Hello @everyone is...": "{'@'}everyone,……", + "teams": "Microsoft Teams", + "Webhook URL": "Webhook 网址", + "wayToGetTeamsURL": "您可以在{0}了解如何获取 Webhook URL。", + "wayToGetZohoCliqURL": "您可以在{0}了解如何创建 Webhook URL。", + "signal": "Signal", + "Number": "号码", + "Recipients": "收件人", + "needSignalAPI": "您需要有一个支持 REST API 的 Signal 客户端。", + "wayToCheckSignalURL": "您可以通过下面的 URL 了解如何设置:", + "signalImportant": "重要:您不能混合设定收件人的分组和号码!", + "gotify": "Gotify", + "Application Token": "应用程序令牌", + "Server URL": "服务器 URL", + "Priority": "优先级", + "slack": "Slack", + "Icon Emoji": "Emoji 图标", + "Channel Name": "频道名称", + "Uptime Kuma URL": "Uptime Kuma URL", + "aboutWebhooks": "关于 Webhook 的更多信息:{0}", + "aboutChannelName": "如果您想绕过 Webhook 频道,请在 {0} 字段输入所需的频道名称。例如:#other-channel", + "aboutKumaURL": "如果保留 Uptime Kuma URL 为空,将会默认指向项目的 GitHub 页面。", + "emojiCheatSheet": "Emoji 速查:{0}", + "rocket.chat": "Rocket.Chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "使用 Techulus 推送", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (支持 50+ 种通知服务)", + "GoogleChat": "Google Chat(仅 Google Workspace)", + "pushbullet": "Pushbullet", + "Kook": "Kook", + "wayToGetKookBotToken": "在 {0} 创建应用并获取机器人 Token", + "wayToGetKookGuildID": "在 Kook 设置中打开“开发者模式”,然后右键点击频道可获取其 ID", + "Guild ID": "频道 ID", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "用户密钥", + "Device": "设备", + "Message Title": "消息标题", + "Notification Sound": "通知铃声", + "More info on:": "更多信息:{0}", + "pushoverDesc1": "紧急优先级(2)会在一小时内每隔 30 秒重试一次。", + "pushoverDesc2": "如果您想发送通知给不同的设备,请填写“设备”字段。", + "SMS Type": "短信类型", + "octopushTypePremium": "Premium(快 - 推荐用于警报)", + "octopushTypeLowCost": "Low Cost(慢 - 有时会被运营商屏蔽)", + "checkPrice": "查看 {0} 的价格:", + "apiCredentials": "API Credentials", + "octopushLegacyHint": "您是否在使用旧版本的 Octopush(2011-2020)?", + "Check octopush prices": "查看 Octopush 的价格 {0}。", + "octopushPhoneNumber": "手机号码(国际通用格式,例如:+33612345678)", + "octopushSMSSender": "短信发送名称:3-11 位大小写字母、数字和空格(a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea 设备 ID", + "Apprise URL": "Apprise 网址", + "Example:": "例如:{0}", + "Read more:": "了解更多:{0}", + "Status:": "状态:{0}", + "Read more": "了解更多", + "appriseInstalled": "Apprise 已安装。", + "appriseNotInstalled": "Apprise 未安装。{0}", + "Access Token": "访问令牌", + "Channel access token": "频道 Access Token", + "Line Developers Console": "Line 开发者控制台", + "lineDevConsoleTo": "Line 开发者控制台 - {0}", + "Basic Settings": "基本设置", + "User ID": "用户 ID", + "Messaging API": "消息 API", + "wayToGetLineChannelToken": "首先访问 {0},创建一个提供者和频道(Messaging API),然后您就可以从上面提到的菜单获取频道的 Access Token 和用户 ID。", + "Icon URL": "图标 URL", + "aboutIconURL": "您可以在“图标 URL”中提供一个图片链接来覆盖默认的资料图片。如果设置了 Emoji 图标则此字段会被忽略。", + "aboutMattermostChannelName": "您可以覆盖 Webhook 发送消息的默认频道,只需在“频道名称”字段中输入您想要的频道名。这需要在 Mattermost 的 Webhook 设置中启用。例如:#other-channel", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - 便宜但是慢,并且容易超负荷。仅限波兰地区的收信人。", + "promosmsTypeFlash": "SMS FLASH - 消息会自动显示在收信人设备上。仅限波兰地区的收信人。", + "promosmsTypeFull": "SMS FULL - 高级短信,您可以使用您自己的发信人名称(需要先注册)。对于警报来说更可靠。", + "promosmsTypeSpeed": "SMS SPEED - 最高优先级。非常快速可靠,但更贵(大约两倍 SMS FULL 的价格)。", + "promosmsPhoneNumber": "手机号码(波兰地区收信人可以不填区号)", + "promosmsSMSSender": "短信发信人名称:已注册的名称或以下默认值之一:InfoSMS、SMS Info、MaxSMS、INFO、SMS", + "Feishu": "飞书", + "Feishu WebHookUrl": "飞书 WebHook URL", + "matrixHomeserverURL": "服务器 URL(包含 http(s):// 和可选的端口号)", + "Internal Room Id": "内部房间 ID", + "matrixDesc1": "您可以在 Matrix 客户端房间设置的高级选项内找到内部房间 ID。格式类似于 !QMdRCpUIfLwsfjxye6:home.server。", + "matrixDesc2": "请不要使用您自己的 Access Token,这将开放您所有的账户权限和您已加入房间的权限。我们强烈建议您创建一个新用户并邀请它至您接收通知的房间中。您可以运行以下命令来获取 Access Token:{0}", + "Method": "方法", + "Body": "请求体", + "Headers": "请求头", + "PushUrl": "推送 URL", + "HeadersInvalidFormat": "请求头不是有效的 JSON: ", + "BodyInvalidFormat": "请求体不是有效的 JSON: ", + "Monitor History": "监控历史", + "clearDataOlderThan": "保留监控历史数据 {0} 天。", + "PasswordsDoNotMatch": "密码不匹配。", + "records": "记录", + "One record": "一条记录", + "steamApiKeyDescription": "要监控 Steam 游戏服务器,您需要 Steam Web-API 密钥。您可以在这里注册您的 API 密钥: ", + "Current User": "当前用户", + "topic": "主题", + "topicExplanation": "要监控的 MQTT Topic", + "successMessage": "成功消息", + "successMessageExplanation": "视为成功的 MQTT 消息", + "recent": "最近", + "Done": "完成", + "Info": "信息", + "Security": "安全性", + "Steam API Key": "Steam API 密钥", + "Shrink Database": "压缩数据库", + "Pick a RR-Type...": "选择资源记录类型…", + "Pick Accepted Status Codes...": "选择有效的状态码…", + "Default": "默认", + "HTTP Options": "HTTP 选项", + "Create Incident": "创建事件", + "Title": "标题", + "Content": "内容", + "Style": "类型", + "info": "信息", + "warning": "警告", + "danger": "危险", + "error": "错误", + "critical": "关键", + "primary": "主要", + "light": "明亮", + "dark": "黑暗", + "Post": "发布", + "Please input title and content": "请输入标题和内容", + "Created": "创建时间", + "Last Updated": "更新时间", + "Unpin": "取消钉选", + "Switch to Light Theme": "切换到浅色主题", + "Switch to Dark Theme": "切换到深色主题", + "Show Tags": "显示标签", + "Hide Tags": "隐藏标签", + "Description": "描述", + "No monitors available.": "没有可用的监控项。", + "Add one": "添加一个", + "No Monitors": "没有监控项", + "Untitled Group": "无标题分组", + "Services": "服务", + "Discard": "放弃", + "Cancel": "取消", + "Powered by": "本站使用", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API 用户名(包括 webapi_ 前缀)", + "serwersmsAPIPassword": "API 密码", + "serwersmsPhoneNumber": "手机号码", + "serwersmsSenderName": "SMS 发信人名称(需要在客户中心注册)", + "smseagle": "SMSEagle", + "smseagleTo": "手机号码", + "smseagleGroup": "通讯录群组名", + "smseagleContact": "通讯录联系人", + "smseagleRecipientType": "收信人类型", + "smseagleRecipient": "收信人(多个需用半角逗号分隔)", + "smseagleToken": "API 访问令牌", + "smseagleUrl": "您的 SMSEagle 设备 URL", + "smseagleEncoding": "以 Unicode 发送 (默认为GSM-7)", + "smseaglePriority": "消息优先级(0-9,9 为最高优先级)", + "stackfield": "Stackfield", + "Customize": "自定义", + "Custom Footer": "自定义底部", + "Custom CSS": "自定义 CSS", + "smtpDkimSettings": "DKIM 设置", + "smtpDkimDesc": "请访问 Nodemailer DKIM {0} 了解配置方法。", + "documentation": "文档", + "smtpDkimDomain": "域名", + "smtpDkimKeySelector": "前缀选择器", + "smtpDkimPrivateKey": "密钥", + "smtpDkimHashAlgo": "哈希算法(可选)", + "smtpDkimheaderFieldNames": "包含在哈希计算对象内的 Header 列表(可选)", + "smtpDkimskipFields": "不包含在哈希计算对象内的 Header 列表(可选)", + "wayToGetPagerDutyKey": "您可以在 Service -> Service Directory -> (选择一个 Service) -> Integrations -> Add integration 页面中搜索“Events API V2”以获取此 Integration Key,更多信息请看{0}", + "wayToGetFlashDutyKey": "将Uptime Kuma与Flashduty集成:进入 协作空间 -> (选择一个 协作空间) -> 集成数据 -> 新增一个集成 页面,选择“Uptime Kuma”,复制推送URL。", + "Integration Key": "集成密钥", + "Integration URL": "集成网址", + "Auto resolve or acknowledged": "自动标记为已解决或已读", + "do nothing": "不做任何操作", + "auto acknowledged": "自动标记为已读", + "auto resolve": "自动标记为已解决", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API 接入点", + "alertaEnvironment": "环境参数", + "alertaApiKey": "API 密钥", + "alertaAlertState": "报警时的严重性", + "alertaRecoverState": "恢复后的严重性", + "deleteStatusPageMsg": "您确认要删除此状态页吗?", + "Proxies": "代理", + "default": "默认", + "enabled": "启用", + "setAsDefault": "设为默认", + "deleteProxyMsg": "您确认要在所有监控项中删除此代理吗?", + "proxyDescription": "代理必须配置到至少一个监控项后才会工作。", + "enableProxyDescription": "此代理必须启用才能对监控项的网络请求起作用。您可以通过修改激活状态,临时在所有监控项中禁用此代理。", + "setAsDefaultProxyDescription": "此代理会对新创建的监控项默认激活,您仍可以在监控项配置中单独禁用此代理。", + "Certificate Chain": "证书链", + "Valid": "有效", + "Invalid": "无效", + "AccessKeyId": "访问密钥 ID", + "SecretAccessKey": "AccessKey 密码", + "PhoneNumbers": "电话号码", + "TemplateCode": "模板代码", + "SignName": "签名", + "Sms template must contain parameters: ": "短信模板必须包含以下变量: ", + "Bark Endpoint": "Bark 接入点", + "Bark Group": "Bark 群组", + "Bark Sound": "Bark 铃声", + "WebHookUrl": "钉钉自定义机器人 Webhook 地址", + "SecretKey": "钉钉自定义机器人加签密钥", + "For safety, must use secret key": "出于安全考虑,必须使用加签密钥", + "Device Token": "Apple Device Token", + "Platform": "平台", + "Huawei": "华为", + "High": "高", + "Retry": "重试次数", + "Topic": "Gorush Topic", + "WeCom": "企业微信群机器人", + "WeCom Bot Key": "企业微信群机器人 Key", + "Setup Proxy": "设置代理", + "Proxy Protocol": "代理协议", + "Proxy Server": "代理服务器", + "Server Address": "服务器地址", + "Proxy server has authentication": "代理服务器启用了身份验证功能", + "User": "用户名", + "Installed": "已安装", + "Not installed": "未安装", + "Running": "运行中", + "Not running": "未运行", + "Remove Token": "移除 Token", + "Start": "启动", + "Stop": "停止", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "添加新的状态页", + "Slug": "路径", + "Accept characters:": "可接受的字符:", + "startOrEndWithOnly": "开头和结尾必须为 {0}", + "No consecutive dashes": "不能有连续的破折号", + "Next": "下一步", + "The slug is already taken. Please choose another slug.": "该路径已被使用。请选择其他路径。", + "No Proxy": "无代理", + "Authentication": "验证", + "HTTP Basic Auth": "HTTP 基础身份验证", + "New Status Page": "新的状态页", + "Page Not Found": "未找到该页面", + "Reverse Proxy": "反向代理", + "Backup": "备份", + "About": "关于", + "wayToGetCloudflaredURL": "(可从 {0} 下载 cloudflared)", + "cloudflareWebsite": "Cloudflare 网站", + "Message:": "信息:", + "Don't know how to get the token? Please read the guide:": "不知道如何获取 Token?请阅读指南:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "如果您正在通过 Cloudflare Tunnel 访问网站,则停止可能会导致当前连接断开。您确定要停止吗?请输入密码以确认。", + "HTTP Headers": "HTTP 头", + "Trust Proxy": "可信的代理类字段", + "Other Software": "其他软件", + "For example: nginx, Apache and Traefik.": "例如:nginx、Apache 和 Traefik。", + "Please read": "请阅读", + "Subject:": "颁发给:", + "Valid To:": "有效期至:", + "Days Remaining:": "剩余有效天数:", + "Issuer:": "颁发者:", + "Fingerprint:": "指纹:", + "No status pages": "无状态页", + "Domain Name Expiry Notification": "域名到期时通知", + "Proxy": "代理", + "Date Created": "创建于", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "OneBot HTTP 地址", + "onebotMessageType": "OneBot 消息类型", + "onebotGroupMessage": "群聊", + "onebotPrivateMessage": "私聊", + "onebotUserOrGroupId": "群组/用户 ID", + "onebotSafetyTips": "出于安全原因,请务必设置 AccessToken", + "PushDeer Key": "PushDeer 密钥", + "Footer Text": "底部自定义文本", + "Show Powered By": "显示 Powered By", + "Domain Names": "域名", + "signedInDisp": "当前用户: {0}", + "signedInDispDisabled": "已禁用身份验证。", + "RadiusSecret": "Radius 共享机密", + "RadiusSecretDescription": "客户端和服务器之间共享的密钥", + "RadiusCalledStationId": "被叫站 Id", + "RadiusCalledStationIdDescription": "所访问的服务器的标识", + "RadiusCallingStationId": "呼叫方号码(Calling Station Id)", + "RadiusCallingStationIdDescription": "发出请求的设备的标识", + "Certificate Expiry Notification": "证书到期时通知", + "API Username": "API 用户名", + "API Key": "API 密钥", + "Recipient Number": "收件人手机号码", + "From Name/Number": "发件人名称/手机号码", + "Leave blank to use a shared sender number.": "留空以使用平台共享的发件人手机号码。", + "Octopush API Version": "Octopush API 版本", + "Legacy Octopush-DM": "旧版本 Octopush-DM", + "endpoint": "接入点", + "octopushAPIKey": "控制台 HTTP API credentials 里的 \"API key\"", + "octopushLogin": "控制台 HTTP API credentials 里的 \"Login\"", + "promosmsLogin": "API 登录名", + "promosmsPassword": "API 密码", + "pushoversounds pushover": "Pushover(默认)", + "pushoversounds bike": "自行车", + "pushoversounds bugle": "军号", + "pushoversounds cashregister": "收银机", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "宇宙", + "pushoversounds falling": "坠落", + "pushoversounds gamelan": "GameLAN", + "pushoversounds incoming": "即将到来", + "pushoversounds intermission": "中场休息", + "pushoversounds magic": "魔法", + "pushoversounds mechanical": "机械的", + "pushoversounds pianobar": "钢琴酒吧", + "pushoversounds siren": "警笛", + "pushoversounds spacealarm": "太空警报", + "pushoversounds tugboat": "汽笛", + "pushoversounds alien": "Alien Alarm(长铃声)", + "pushoversounds climb": "Climb(长铃声)", + "pushoversounds persistent": "Persistent(长铃声)", + "pushoversounds echo": "Pushover Echo(长铃声)", + "pushoversounds updown": "Up Down(长铃声)", + "pushoversounds vibrate": "仅震动", + "pushoversounds none": "无(静音)", + "pushyAPIKey": "API 密钥", + "pushyToken": "设备 Token", + "Show update if available": "有更新时通知", + "Also check beta release": "一并检查 Beta 版更新", + "Using a Reverse Proxy?": "正在使用反向代理?", + "Check how to config it for WebSocket": "查看如何将反向代理与 WebSocket 一起使用", + "Steam Game Server": "Steam 游戏服务器", + "Most likely causes:": "最可能的原因:", + "The resource is no longer available.": "您所请求的资源已不再可用。", + "There might be a typing error in the address.": "您输入的地址可能有误。", + "What you can try:": "您可以尝试以下操作:", + "Retype the address.": "重新输入地址。", + "Go back to the previous page.": "返回到上一页面。", + "Coming Soon": "即将推出", + "wayToGetClickSendSMSToken": "你可以从 {here} 取得 API 用户名和 API Key。", + "Connection String": "连接字符串", + "Query": "查询语句", + "settingsCertificateExpiry": "TLS 证书过期通知", + "certificationExpiryDescription": "HTTPS 监控项发现被监控目标的 TLS 证书剩余有效期少于以下天数时将发出通知:", + "Setup Docker Host": "配置 Docker 宿主信息", + "Connection Type": "连接方式", + "Docker Daemon": "Docker 守护进程", + "deleteDockerHostMsg": "您确定要为所有监控项删除此 Docker 宿主设置吗?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker 容器", + "Container Name / ID": "容器名称 / ID", + "Docker Host": "Docker 宿主", + "Docker Hosts": "Docker 宿主", + "ntfy Topic": "ntfy 主题", + "Domain": "域名", + "Workstation": "工作站", + "disableCloudflaredNoAuthMsg": "您现在正处于 No Auth 模式,无需输入密码。", + "trustProxyDescription": "信任 'X-Forwarded-*' 头。如果您的 Uptime Kuma 是通过 Nginx 或 Apache 等反代服务对外提供访问的话,则您应当启用本功能以获取正确的客户端 IP。", + "wayToGetLineNotifyToken": "您可以在 {0} 获取 Access token", + "Examples": "例如", + "Home Assistant URL": "Home Assistant 地址", + "Long-Lived Access Token": "长期访问令牌", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "长期访问令牌可通过点击左下角您的用户名,滚动到页面底部并点击 Create Token 按钮获取。", + "Notification Service": "Notification Service", + "default: notify all devices": "默认:通知所有设备", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "通知服务的列表可在 Home Assistant 中的 Developer Tools > Services 通过搜索您的设备或手机的名称来获得。", + "Automations can optionally be triggered in Home Assistant:": "可以在 Home Assistant 使用下列模板设置自动化操作的触发条件:", + "Trigger type:": "触发类型:", + "Event type:": "事件类型:", + "Event data:": "事件数据:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "然后您可以选择关联操作,例如切换到 RGB 灯发出红光的场景。", + "Frontend Version": "前端版本", + "Frontend Version do not match backend version!": "前端版本与后端版本不匹配!", + "Base URL": "API 基础地址", + "goAlertInfo": "GoAlert 是一个用于呼叫调度、自动汇报和通知(如 SMS 或语音呼叫)的开源应用程序。在正确的时间以正确的方式自动让正确的人参与!{0}", + "goAlertIntegrationKeyInfo": "使用形如 aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee 的通用 API 集成密钥,通常是复制来的链接中的 token 参数值。", + "goAlert": "GoAlert", + "backupOutdatedWarning": "已弃用:由于大量新功能的加入,以及备份功能没有时时维护,现在备份功能已经无法生成完整的备份和恢复完整的设置。", + "backupRecommend": "请改为直接备份 docker 卷或者数据文件夹(./data/)。", + "Optional": "可选的", + "squadcast": "Squadcast", + "SendKey": "发送密钥", + "SMSManager API Docs": "SMSManager API 文档", + "Gateway Type": "网关类型", + "SMSManager": "SMSManager", + "You can divide numbers with": "可用的数字分隔符包括", + "or": "或", + "recurringInterval": "时间间隔", + "Recurring": "重复", + "strategyManual": "手动启用/禁用", + "warningTimezone": "使用服务器时区", + "weekdayShortMon": "周一", + "weekdayShortTue": "周二", + "weekdayShortWed": "周三", + "weekdayShortThu": "周四", + "weekdayShortFri": "周五", + "weekdayShortSat": "周六", + "weekdayShortSun": "周日", + "dayOfWeek": "每周计划", + "dayOfMonth": "每月计划", + "lastDay": "结束日", + "lastDay1": "每月最后一天", + "lastDay2": "每月倒数第二天", + "lastDay3": "每月倒数第三天", + "lastDay4": "每月倒数第四天", + "No Maintenance": "无维护事件", + "pauseMaintenanceMsg": "确定要暂停吗?", + "maintenanceStatus-under-maintenance": "正在维护", + "maintenanceStatus-inactive": "未启用", + "maintenanceStatus-scheduled": "已计划", + "maintenanceStatus-ended": "已结束", + "maintenanceStatus-unknown": "未知", + "Display Timezone": "显示时区", + "Server Timezone": "服务器时区", + "statusPageMaintenanceEndDate": "结束时间", + "IconUrl": "图标 URL", + "Enable DNS Cache": "(已弃用)为 HTTP(s) 监控项启用 DNS 缓存", + "Enable": "启用", + "Disable": "禁用", + "dnsCacheDescription": "可能无法在某些 IPv6 环境工作,如果遇到问题请禁用。", + "Single Maintenance Window": "单一维护时间窗口", + "Maintenance Time Window of a Day": "每日维护时间窗口", + "Effective Date Range": "生效日期范围(可选)", + "Schedule Maintenance": "计划维护", + "Date and Time": "日期时间", + "DateTime Range": "日期时间范围", + "Strategy": "策略", + "Free Mobile User Identifier": "Free Mobile 用户 ID", + "Free Mobile API Key": "Free Mobile API 密钥", + "Enable TLS": "启用 TLS", + "Proto Service Name": "Proto 服务名称", + "Proto Method": "Proto 方法", + "Proto Content": "Proto 内容", + "Economy": "经济", + "Lowcost": "低价", + "high": "高价", + "General Monitor Type": "常规监控类型", + "Passive Monitor Type": "被动监控类型", + "Specific Monitor Type": "特殊监控类型", + "dataRetentionTimeError": "保留期必须为0或更大", + "Monitor": "监控项", + "Custom": "自定义", + "promosmsAllowLongSMS": "允许长的短信", + "confirmDeleteTagMsg": "您确定要删除这个标签?与此标签关联的监控项不会被删除。", + "infiniteRetention": "设为0表示无限保留期。", + "Help": "帮助", + "Game": "游戏", + "Packet Size": "数据包大小", + "loadingError": "无法获取数据,请稍后重试。", + "plugin": "插件", + "install": "安装", + "installing": "正在安装", + "uninstall": "卸载", + "uninstalling": "正在卸载", + "confirmUninstallPlugin": "您确定要卸载此插件吗?", + "Custom Monitor Type": "自定义监控类型", + "markdownSupported": "支持 Markdown 语法。如果使用 HTML,请避免在代码开头添加空格,以免出现格式问题。", + "Google Analytics ID": "Google Analytics(分析)ID", + "Learn More": "了解更多", + "Edit Tag": "编辑标签", + "telegramMessageThreadID": "(可选)话题 ID", + "telegramMessageThreadIDDescription": "可选的唯一标识符,用以向该标识符对应的话题发送消息,仅限启用了话题功能的超级群组可用", + "notificationRegional": "地区性通知平台", + "telegramSendSilently": "静默发送", + "Body Encoding": "请求体编码", + "telegramSendSilentlyDescription": "静默地发送消息。消息发布后用户会收到无声通知。", + "telegramProtectContent": "阻止转发/保存", + "telegramProtectContentDescription": "启用后Telegram 中的机器人消息将受到保护,无法被转发和保存。", + "Clone Monitor": "克隆监控项", + "Clone": "克隆", + "cloneOf": "{0} 的克隆", + "Expiry": "过期", + "Expiry date": "过期时间", + "Continue": "继续", + "Add Another": "添加另一个", + "Add API Key": "添加 API 密钥", + "No API Keys": "没有 API 密钥", + "apiKey-active": "有效", + "apiKey-expired": "已过期", + "Expires": "过期时间", + "apiKey-inactive": "已禁用", + "disableAPIKeyMsg": "您确定要禁用这个 API 密钥?", + "deleteAPIKeyMsg": "您确定要删除这个 API 密钥?", + "Generate": "生成", + "API Keys": "API 密钥", + "Don't expire": "从不过期", + "Key Added": "API 密钥已生成", + "apiKeyAddedMsg": "您的 API 密钥已生成。此页只会显示一次,请妥当保存。", + "pagertreeUrgency": "紧急程度", + "pagertreeLow": "低", + "pagertreeCritical": "严重", + "pagertreeIntegrationUrl": "集成 URL 地址", + "pagertreeSilent": "静默", + "pagertreeMedium": "中", + "pagertreeHigh": "高", + "pagertreeResolve": "自动解除", + "pagertreeDoNothing": "什么都不做", + "wayToGetPagerTreeIntegrationURL": "在 PagerTree 中创建 Uptime Kuma 集成后,复制端点 URL 到此处。在 {0} 查看详情", + "Add New Tag": "添加新标签", + "lunaseaDeviceID": "设备 ID", + "lunaseaTarget": "目标", + "lunaseaUserID": "用户 ID", + "statusPageRefreshIn": "将于 {0} 后刷新", + "twilioAccountSID": "账户 SID", + "twilioAuthToken": "鉴权 Token / API Key Secret", + "twilioFromNumber": "发件人号码", + "twilioToNumber": "收件人号码", + "sameAsServerTimezone": "使用服务器时区", + "startDateTime": "开始日期/时间", + "invalidCronExpression": "无效的 Cron 表达式:{0}", + "endDateTime": "结束日期/时间", + "cronExpression": "Cron 表达式", + "cronSchedule": "计划: ", + "ntfyAuthenticationMethod": "鉴权方式", + "ntfyUsernameAndPassword": "用户名和密码", + "pushoverMessageTtl": "消息存活时间(秒)", + "Monitor Setting": "{0} 监控项设置", + "Badge Color": "徽章内容颜色", + "Badge Suffix": "徽章内容后缀", + "Badge Prefix": "徽章内容前缀", + "Badge Label": "徽章标签", + "Badge Duration": "徽章显示时段(最后 x 小时)", + "Badge Type": "徽章类型", + "Badge Generator": "{0} 徽章生成器", + "Open Badge Generator": "打开徽章生成器", + "Badge Style": "徽章样式", + "Badge Down Days": "故障状态所需剩余天数", + "Badge Warn Days": "徽章预警天数", + "Badge Warn Color": "警告状态下徽章颜色", + "Badge Maintenance Color": "维护状态下徽章颜色", + "Badge Down Color": "故障状态下徽章颜色", + "Badge Up Color": "正常状态下徽章颜色", + "Badge Label Suffix": "徽章标签后缀", + "Badge URL": "徽章网址", + "Badge value (For Testing only.)": "徽章内容(仅供测试)", + "Badge Pending Color": "重试中状态下徽章颜色", + "Badge Label Prefix": "徽章标签前缀", + "Badge Label Color": "徽章标签颜色", + "Show Clickable Link Description": "勾选后所有能访问本状态页的访客均可查看该监控项网址。", + "Show Clickable Link": "显示可点击的监控项链接", + "Group": "分组", + "Monitor Group": "监控项组", + "Cannot connect to the socket server": "无法连接到后端服务器", + "Reconnecting...": "重连中……", + "Edit Maintenance": "编辑维护事件", + "Home": "首页", + "noGroupMonitorMsg": "暂无可用,请先创建一个监控项组。", + "Close": "关闭", + "nostrRecipients": "接收者公钥(npub 格式)", + "nostrSender": "发送者私钥(nsec 格式)", + "nostrRecipientsHelp": "npub 格式,每行一个", + "nostrRelaysHelp": "Relay 服务地址,每行一个", + "nostrRelays": "Nostr relay 服务", + "chromeExecutableDescription": "致 Docker 用户:如果 Chromium 尚未安装,则需要几分钟时间来安装并显示测试结果。这需要 1 GB 硬盘空间。", + "chromeExecutableAutoDetect": "自动检测", + "chromeExecutable": "Chrome/Chromium 可执行文件", + "Invert Keyword": "反转模式", + "invertKeywordDescription": "出现关键词将令检测结果为失败,而非成功。", + "webhookBodyCustomOption": "自定义内容", + "webhookCustomBodyDesc": "为 webhook 设定一个自定义 HTTP 请求体。可在模板内使用 {msg}、{heartbeat}和{monitor} 变量。", + "webhookBodyPresetOption": "预设 - {0}", + "Request Body": "请求体", + "Json Query": "JSON 查询", + "twilioApiKey": "API Key(可选)", + "Expected Value": "预期值", + "Badge Duration (in hours)": "徽章时间范围(以小时为单位)", + "Badge Preview": "徽章预览", + "Notify Channel": "通知该频道", + "aboutNotifyChannel": "勾选“通知该频道”,会令该频道内所有成员都收到一条桌面端或移动端通知,无论其状态是在线或离开。", + "filterActive": "启用", + "filterActivePaused": "暂停", + "Kafka Topic Name": "Kafka 主题名称", + "Press Enter to add broker": "按回车键添加缓存代理(broker)", + "Enter the list of brokers": "输入缓存代理(broker)列表", + "Kafka Brokers": "Kafka 缓存代理(Broker)", + "Pick a SASL Mechanism...": "选择一种 SASL 鉴权方式……", + "Mechanism": "鉴权方式", + "Kafka SASL Options": "Kafka SASL 选项", + "AccessKey Id": "密钥 ID(AccessKey Id)", + "Authorization Identity": "授权实体(Authorization Identity)", + "Session Token": "会话令牌(Session Token)", + "Secret AccessKey": "访问密钥(Secret AccessKey)", + "Enable Kafka Producer Auto Topic Creation": "启用 Kafka 生成者(Producer)自动创建主题(Topic)功能", + "Enable Kafka SSL": "启用 Kafka SSL 功能", + "Kafka Producer Message": "Kafka 生成者(Producer)消息", + "tailscalePingWarning": "如需使用 Tailscale Ping 客户端,您需要以非 docker 方式安装 Uptime Kuma,并同时安装 Tailscale 客户端。", + "Server URL should not contain the nfty topic": "服务器地址不应包含 ntfy主题", + "Select": "选择", + "selectedMonitorCount": "已选:{0}", + "noOrBadCertificate": "无证书或证书错误", + "showCertificateExpiry": "显示证书有效期", + "FlashDuty Severity": "严重程度", + "Check/Uncheck": "选中/取消选中", + "pushDeerServerDescription": "留空则使用官方服务器", + "PushDeer Server": "PushDeer 服务器", + "timeoutAfter": "{0} 秒后超时", + "Request Timeout": "请求超时", + "gamedigGuessPortDescription": "Valve 服务器查询协议使用的端口可能与客户端端口不同。如果监控器无法连接到服务器,请尝试此方法。", + "gamedigGuessPort": "Gamedig: 自动检测端口号", + "styleElapsedTimeShowWithLine": "显示(带连接线)", + "styleElapsedTimeShowNoLine": "显示(不带连接线)", + "styleElapsedTime": "在监控项详情的心跳栏下显示起止时间", + "enableNSCD": "启用 NSCD(名称服务缓存)以缓存所有 DNS 请求", + "setupDatabaseChooseDatabase": "您想用哪种数据库?", + "setupDatabaseEmbeddedMariaDB": "您无需设置此项。通过 Docker 方式安装时已自动配置了一个 MariaDB 数据库。Uptime Kuma 会通过 Unix 套接字方式连接该数据库。", + "setupDatabaseMariaDB": "连接到外部 MariaDB 数据库。您需要设置该数据库的连接方式。", + "setupDatabaseSQLite": "一个简单的数据库文件,推荐小规模部署使用。在 v2.0.0 版本之前,Uptime Kuma 使用 SQLite 作为默认数据库。", + "dbName": "数据库名称", + "Saved.": "已保存。", + "monitorToastMessagesLabel": "监控项的弹窗通知", + "toastSuccessTimeout": "成功类弹窗通知的自动关闭用时", + "toastErrorTimeout": "失败类弹窗通知的自动关闭用时", + "monitorToastMessagesDescription": "监控项的弹窗通知的自动关闭用时,以秒为单位。设置为 -1 将禁用弹窗通知的自动关闭功能,设置为 0 将完全禁用弹窗通知功能。", + "Bark API Version": "Bark API 版本", + "programmingLanguages": "编程语言", + "pushOthers": "其他", + "pushViewCode": "如何使用 Push 监控项?(查看示例代码)", + "authInvalidToken": "无效的令牌。", + "authUserInactiveOrDeleted": "该用户被禁用或删除。", + "tagNotFound": "标签未找到。", + "successEnabled": "已成功启用。", + "successDisabled": "已成功禁用。", + "successBackupRestored": "已成功恢复备份。", + "successEdited": "已成功编辑。", + "successDeleted": "已成功删除。", + "successPaused": "已成功暂停。", + "successResumed": "已成功恢复。", + "successAdded": "已成功添加。", + "2faDisabled": "已成功禁用 2FA。", + "2faEnabled": "已成功启用 2FA。", + "2faAlreadyEnabled": "2FA 已经启用。", + "foundChromiumVersion": "已找到 Chromium/Chrome。版本:{0}", + "successAuthChangePassword": "已成功更新密码。", + "authIncorrectCreds": "错误的用户名或密码。", + "Reset Token": "重置令牌", + "templateMsg": "通知的消息", + "templateHeartbeatJSON": "描述心跳信息的对象", + "templateLimitedToUpDownCertNotifications": "仅适用于“正常”、“故障”、“证书到期”类通知", + "templateMonitorJSON": "描述监控项信息的对象", + "emailCustomisableContent": "可自定义的内容", + "smtpLiquidIntroduction": "以下两个字段可通过 Liquid 模板语言进行模板化。使用说明请参阅 {0}。这些是可用变量:", + "leave blank for default subject": "留空以使用默认主题", + "emailCustomBody": "自定义正文", + "leave blank for default body": "留空以使用默认正文", + "emailTemplateMsg": "通知的消息", + "emailTemplateMonitorJSON": "描述监控项信息的对象", + "emailTemplateHeartbeatJSON": "描述心跳信息的对象", + "emailTemplateStatus": "状态", + "emailTemplateHostnameOrURL": "主机名或 URL", + "emailTemplateServiceName": "服务名称", + "liquidIntroduction": "可通过 Liquid 模板语言实现模板化。使用说明请参阅 {0}。", + "templateLimitedToUpDownNotifications": "仅适用于“正常”、“故障”类通知", + "emailTemplateLimitedToUpDownNotification": "仅适用于“正常”、“故障”类心跳,否则为空", + "GrafanaOncallUrl": "Grafana Oncall 服务 URL", + "noDockerHostMsg": "不可用。请先设置一个 Docker 宿主。", + "DockerHostRequired": "请为此监控项设置一个 Docker 宿主。", + "Browser Screenshot": "浏览器截图", + "successKeyword": "“成功”关键词", + "successKeywordExplanation": "视为成功的MQTT关键词", + "Add a new expiry notification day": "添加一个新的天数条件", + "setup a new monitor group": "创建一个新的监控项组", + "Remote Browsers": "远程浏览器", + "Remote Browser": "远程浏览器", + "Add a Remote Browser": "添加一个远程浏览器", + "Remote Browser not found!": "未找到远程浏览器!", + "remoteBrowsersDescription": "远程浏览器可用以代替本地 Chromium 浏览器。您可使用类似于 browserless.io 的服务,或者自行运行一个类似服务", + "self-hosted container": "自托管容器", + "remoteBrowserToggle": "默认情况下 Chromium 运行于 Uptime Kuma 所在容器内。您可以通过切换此开关来使用远程浏览器。", + "useRemoteBrowser": "使用远程浏览器", + "deleteRemoteBrowserMessage": "您确定要删除此远程浏览器吗,这会影响所有监控项?", + "Remove the expiry notification": "移除该天数条件", + "openModalTo": "打开一个新窗口以{0}", + "Add a domain": "添加一个域名", + "Remove domain": "移除域名 {0}", + "ntfyPriorityHelptextAllExceptDown": "除了 {0} 类事件使用 {1} 优先级外,其他所有事件均使用该优先级", + "statusPageSpecialSlugDesc": "特殊路径 {0}:当访客未指定路径时将显示此路径对应的页面", + "Search monitored sites": "搜索监控中站点", + "settingUpDatabaseMSG": "正在配置数据库中。这需要一定的时间,请耐心等候。", + "ntfyPriorityHelptextAllEvents": "所有事件将使用最高优先级", + "What is a Remote Browser?": "什么是远程浏览器?", + "Your User ID": "你的用户 ID", + "Channel access token (Long-lived)": "频道访问令牌(长期版)", + "wayToGetHeiiOnCallDetails": "如需了解如何获取 Trigger ID 和 API 密钥,请访问 {documentation}", + "documentationOf": "{0} 文档", + "gtxMessagingToHint": "国际通用格式,需要前导 \"+\" ({e164}、{e212} 或 {e214} 格式)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "发件人手机号码 / 传输路径起始地址(TPOA)", + "gtxMessagingApiKeyHint": "你可以在此找到你的 API 密钥:My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "To Phone Number": "收件人手机号码", + "gtxMessagingFromHint": "在手机上,收件人会看到 TPOA 地址作为消息的发送者。TPOA 允许的格式包括:至多11个字母或数字、短代码、当地长代码或国际号码({e164}、{e212} 或 {e214} 格式)", + "Allow Long SMS": "允许长消息", + "Destination": "收件人", + "cellsyntOriginator": "在收件人处作为消息发送者显示。允许的内容取决于发件人类型。", + "Originator": "发件人", + "max 15 digits": "最多 15 位数字", + "cellsyntOriginatortypeAlphanumeric": "字符或数字类型(最多 11 个字母或数字)。收件人无法向此号码回复消息。", + "Telephone number": "手机号码", + "Alphanumeric (recommended)": "字符或数字类型(推荐)", + "Originator type": "发件人类型", + "max 11 alphanumeric characters": "最多 11 个字母或数字", + "cellsyntSplitLongMessages": "长消息会被切分为至多 6 段,每段至多 153 个字符,总共至多 918 个字符。", + "cellsyntDestination": "收件人的手机号码需要使用以 00+国家代码开头的国际通用格式,例如若要发给英国的号码 07920 110 000 需使用 00447920110000 作为收件人手机号码(至多17位数)。需发送给多个收件人手机号码时可使用英文逗号分隔,每次请求最 多250 00个收件人手机号码。", + "cellsyntOriginatortypeNumeric": "数字类型(最多 15 位数)需使用国际通用格式,不以 00+国家代码开头,例如若要使用英国的号码 07920 110 000 需填写 447920110000。收件人可向此号码回复消息。", + "callMeBotGet": "您可以在此处填写您生成的用于 {0}、{1} 或 {2} 的端点。 请注意您可能会受到速率限制。 速率限制被推测为:{3}(仅供参考)", + "whapiRecipient": "手机号码 / 联系人 ID / 组 ID", + "API URL": "API 地址", + "wayToGetWhapiUrlAndToken": "您可以通过进入您想要的频道来获取 API URL 和令牌:{0}", + "wayToWriteWhapiRecipient": "可用格式为不含 + 号的国际通用格式手机号码({0})、联系人 ID({1})或组 ID({2})。", + "Mentioning": "是否提及成员", + "Don't mention people": "不提及任何人", + "Mention group": "提及 {group}", + "senderSevenIO": "发信人号码或名称", + "wayToGetSevenIOApiKey": "访问 app.seven.io > 开发人员 > api 密钥 > 绿色添加按钮下的仪表板", + "receiverInfoSevenIO": "如果接收号码不在德国,您必须在号码前面添加国家代码(例如,对于来自美国的国家代码 1,请使用 117612121212 而不是 017612121212)", + "apiKeySevenIO": "SevenIO API 密钥", + "locally configured mail transfer agent": "本地配置的邮件传输代理", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "输入您要连接的服务器的主机名,或者输入 {localhost}(如果您打算使用 {local_mta})", + "receiverSevenIO": "收信人号码", + "Host URL": "服务器地址", + "ignoreTLSErrorGeneral": "连接时忽视 TLS/SSL 错误", + "wayToGetDiscordThreadId": "获取一个子区(thread)或论坛帖子的消息 ID 的方法与获取一个频道的 ID 的相似。关于如何获取 ID 请参阅 {0}", + "whatHappensAtForumPost": "创建一个新论坛帖子。本功能不会向已存在的论坛帖子发送消息。如果需要请使用 \"{option}\"", + "mongodbCommandDescription": "对数据库运行 MongoDB 命令。有关可用命令的信息,请查阅 {documentation}", + "Command": "命令", + "bitrix24SupportUserID": "输入你在 Bitrix24 的用户 ID。你可以在你的用户个人资料页找到你的用户 ID。", + "wayToGetBitrix24Webhook": "你可以按以下步骤创建一个 webhook:{0}", + "Bitrix24 Webhook URL": "Bitrix24 Webhook 地址", + "postToExistingThread": "发送到已存在的子区/论坛帖子", + "Create new forum post": "创建新的论坛帖子", + "Send to channel": "发送到频道", + "Select message type": "选择消息类型", + "Refresh Interval Description": "状态页面会每 {0} 秒进行一次完全刷新", + "Refresh Interval": "刷新间隔", + "forumPostName": "论坛帖子名称", + "e.g. {discordThreadID}": "例如 {discordThreadID}", + "threadForumPostID": "子区/论坛帖子 ID", + "smspartnerSenderName": "SMS 发件人名称", + "smspartnerPhoneNumber": "手机号码", + "smspartnerApiurl": "你可以在此处找到你的 API 密钥:{0}", + "smspartnerSenderNameInfo": "不能使用特殊字符,字符数在 3 到 11 个之间", + "smspartnerPhoneNumberHelptext": "号码必须使用国际通用格式,例如 {0}、{1}。多个号码必须使用 {2} 分隔", + "threemaRecipientTypePhone": "手机号码", + "threemaRecipientTypeIdentityFormat": "8 位字符", + "threemaRecipientTypeIdentity": "Threema ID", + "threemaRecipientType": "收信人类型", + "threemaRecipient": "收件人", + "threemaApiAuthenticationSecret": "网关密钥", + "threemaSenderIdentityFormat": "8 位字符,通常以 * 开头", + "threemaSenderIdentity": "网关 ID", + "threemaRecipientTypeEmail": "邮件地址", + "threemaBasicModeInfo": "注:此通知类型所使用的 Threema 网关为基础模式(服务器端加密)。更多细节参见 {0}。", + "threemaRecipientTypePhoneFormat": "E.164 标准,不含前导 + 号", + "wayToGetThreemaGateway": "你可以在 {0} 注册 Threema 网关。", + "apiKeysDisabledMsg": "由于登录验证被禁用,API 密钥也被禁用。", + "now": "现在", + "Json Query Expression": "JSON 查询表达式", + "and": "与", + "cacheBusterParam": "添加参数 {0}", + "cacheBusterParamDescription": "随机生成一个参数以绕过缓存。", + "Community String": "SNMP 通讯字符串", + "snmpCommunityStringHelptext": "此字符串用作密码,以验证和控制对SNMP启用设备的访问。请将其与您的SNMP设备配置匹配。", + "Please enter a valid OID.": "请输入一个合法的 OID。", + "privateOnesenderDesc": "请确保电话号码有效。要向私人电话号码发送消息,格式形如:628123456789", + "wayToGetOnesenderUrlandToken": "你可以在 Onesender 网站获取地址和令牌。更多信息参见 {0}", + "signl4Docs": "你可以在此找到更多关于如何配置 SIGNL4 以及如何获取 SIGNL4 Webhook 地址的信息:{0}。", + "groupOnesenderDesc": "请确保分组 ID 有效。要向分组发送消息,格式形如:628123456789-342345", + "time ago": "{0} 之前", + "-year": "年", + "OID (Object Identifier)": "OID(对象标识符)", + "snmpOIDHelptext": "输入您想监控的传感器或状态的 OID。如果您不确定 OID 是什么,可以使用 MIB 浏览器或 SNMP 软件等网络管理工具进行查找。", + "Condition": "条件", + "SNMP Version": "SNMP 版本", + "Host Onesender": "Onesender 服务器", + "Token Onesender": "Onesender 令牌", + "Recipient Type": "收件人类型", + "Private Number": "私密号码", + "Add Remote Browser": "添加远程浏览器", + "OAuth2: Client Credentials": "OAuth2:客户端凭据", + "Authentication Method": "鉴权方式", + "Authorization Header": "鉴权请求头", + "Form Data Body": "表单数据请求体", + "OAuth Token URL": "OAuth 令牌地址", + "Client ID": "客户端 ID", + "Client Secret": "客户端秘钥", + "OAuth Scope": "OAuth 范围", + "New Group": "新分组", + "Group ID": "分组 ID", + "Group Name": "分组名称", + "Optional: Space separated list of scopes": "可选项:用空格分隔的范围列表", + "Go back to home page.": "返回到首页。", + "No tags found.": "未找到标签。", + "Lost connection to the socket server.": "与 socket 服务器的连接丢失。", + "Cannot connect to the socket server.": "无法连接到 socket 服务器。", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhook 地址", + "Conditions": "条件", + "conditionAdd": "添加条件", + "conditionDelete": "删除条件", + "conditionAddGroup": "添加分组", + "conditionDeleteGroup": "删除分组", + "conditionValuePlaceholder": "值", + "equals": "相等", + "not equals": "不相等", + "contains": "包含", + "not contains": "不包含", + "starts with": "以此开头", + "not starts with": "不以此开头", + "ends with": "以此结尾", + "not ends with": "不以此结尾", + "less than": "少于", + "greater than": "多于", + "less than or equal to": "不多于", + "greater than or equal to": "不少于", + "record": "记录", + "jsonQueryDescription": "使用 JSON 查询解析并提取服务器 JSON 响应中的特定数据,或者,如果不期望得到 JSON 响应,则可使用 \"$\" 获取原始响应。然后将结果转为字符串并与期望值进行字符串比较。有关更多文档,请参阅 {0},亦可使用 {1} 来尝试查询。", + "shrinkDatabaseDescriptionSqlite": "触发 SQLite 数据库的 {vacuum} 命令。{auto_vacuum} 已经启用,但它不会像 {vacuum} 命令那样对数据库进行碎片整理,也不会重新打包各个数据库页面。", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "如需可被回复,请输入发送者 ID 或 E.164 格式的手机号码。", + "Copy": "复制", + "Debug": "Debug", + "CopyToClipboardSuccess": "已复制!", + "CopyToClipboardError": "无法复制到剪贴板:{error}", + "CurlDebugInfoProxiesUnsupported": "上述 {curl} 命令中的代理支持目前尚未实现。", + "docker networks": "Docker 网络", + "dns resolvers": "DNS 解析器", + "firewalls": "防火墙", + "Message format": "消息格式", + "Send rich messages": "发送富文本消息", + "Sound": "声音", + "Notification Channel": "通知频道", + "Bubble": "Bubble(气泡)", + "Reveal": "Reveal(揭示)", + "Harp": "Harp(竖琴)", + "Correct": "Correct(成功音)", + "Fail": "Fail(失败音)", + "Arcade": "Arcade(拱廊)", + "Alphanumerical string and hyphens only": "仅限字母、数字和连字符(-)", + "Custom sound to override default notification sound": "自定义声音,用以覆盖默认通知声音", + "Pop": "Pop(流行音乐)", + "Guitar": "Guitar(吉他)", + "Elevator": "Elevator(电梯)", + "Clear": "Clear(清除声)", + "Scifi": "Scifi(科幻)", + "Flute": "Flute(长笛)", + "Doorbell": "Doorbell(门铃)", + "The phone number of the recipient in E.164 format.": "收件人的 E.164 格式手机号码。", + "Can be found on:": "可在此找到:{0}", + "From": "发件人", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "即使设备处于专注模式,即时通知也会立即发送。", + "Time Sensitive (iOS Only)": "即时通知(仅 iOS 可用)", + "Money": "Money(钱)", + "CurlDebugInfoOAuth2CCUnsupported": "{curl} 不支持完整的 oauth 客户端凭证流。{newline}请获取承载令牌(bearer token)并通过 {oauth2_bearer} 选项传递。", + "CurlDebugInfo": "要调试监控项,您可以将该命令粘贴到您自己的或 uptime kuma 正在运行的机器的命令行终端中,并查看结果。{newiline}请注意网络差异,例如 {firewalls}、{dns_resolvers} 或 {docker_networks}。", + "ignoredTLSError": "TLS/SSL 错误已被忽略", + "SendGrid API Key": "SendGrid API 密钥", + "RabbitMQ Password": "RabbitMQ 密码", + "RabbitMQ Username": "RabbitMQ 用户名", + "rabbitmqNodesInvalid": "请使用 RabbitMQ 节点的完整 URL(即完全限定 URL,以 http 开头)。", + "rabbitmqNodesRequired": "请设置此监视项的节点。", + "rabbitmqNodesDescription": "输入 RabbitMQ 管理节点的 URL,包括协议和端口。例如:{0}", + "RabbitMQ Nodes": "RabbitMQ 管理节点", + "Separate multiple email addresses with commas": "用逗号分隔多个电子邮件地址", + "rabbitmqHelpText": "要使用此监控项,您需要在 RabbitMQ 设置中启用管理插件。有关更多信息,请参阅 {rabitmq_documentation}。", + "aboutSlackUsername": "更改消息发件人的显示名称。如果您想提及某人,请另行将其包含在友好名称中。", + "templateStatus": "状态", + "templateHostnameOrURL": "主机名或 URL", + "templateServiceName": "服务名", + "telegramUseTemplateDescription": "启用后该消息将使用自定义模板发送。", + "telegramUseTemplate": "使用自定义消息模板", + "wayToGetWahaSession": "在此会话中,WAHA 会向聊天 ID 发送通知。您可以在 WAHA 仪表板中找到它。", + "wayToGetWahaApiUrl": "你的 WAHA 实例 URL。", + "wahaChatId": "聊天 ID(手机号码 / 联系人 ID / 群组 ID)", + "wahaSession": "会话", + "Template Format": "模板格式", + "Message Template": "消息模板", + "Plain Text": "纯文本", + "wayToWriteWahaChatId": "包含国际区号但不含开头加号({0})的手机号码、联系人 ID({1})、组 ID({2})。通知将从 WAHA 会话发送到此聊天 ID。", + "wayToGetWahaApiKey": "API 密钥是你用于运行 WAHA 的 WHATSAPP_API_KEY 环境变量值。", + "telegramTemplateFormatDescription": "Telegram 允许在消息中使用不同的标记语言,具体细节请参见 Telegram {0}。", + "YZJ Webhook URL": "YZJ Webhook 地址", + "YZJ Robot Token": "YZJ 机器人令牌", + "telegramServerUrl": "(可选)服务器 Url", + "telegramServerUrlDescription": "用以解除 Telegram 的机器人 API 限制或在封锁区域(中国、伊朗等)获得访问权限。获取更多信息,请点击 {0}。默认值:{1}", + "Font Twemoji by Twitter licensed under": "由 Twitter 制作的 Twemoji 字体根据此许可证授权", + "smsplanetApiDocs": "有关获取 API token 的详细信息,请参阅 {the_smsplanet_documentation}。", + "smsplanetNeedToApproveName": "需要在客户端面板进行确认", + "Sender name": "发件人名称", + "Phone numbers": "手机号码", + "the smsplanet documentation": "smsplanet 文档", + "smsplanetApiToken": "SMSPlanet API 的 Token", + "Use HTML for custom E-mail body": "在自定义电子邮件内容中使用 HTML", + "smseagleDuration": "时长 (以秒为单位)", + "smseagleApiv1": "APIv1 (用于现有项目和向后兼容性)", + "ntfyPriorityHelptextPriorityHigherThanDown": "常规优先级应高于优先级{0}。优先级{1}高于优先级{0}和优先级{2}", + "FlashDuty Push URL Placeholder": "从警告集成页面复制", + "pingNumericDescription": "如果勾选,将输出IP地址而不是符号主机名", + "pingPerRequestTimeoutDescription": "在视作单次ping数据包丢失前的最大等待时间 (以秒为单位)", + "ntfyPriorityDown": "故障事件的优先级", + "FlashDuty Push URL": "推送URL", + "pingCountLabel": "数据包最大数量", + "pingCountDescription": "停止前发送的数据包数量", + "pingNumericLabel": "数字输出", + "pingGlobalTimeoutLabel": "全局超时", + "pingGlobalTimeoutDescription": "无论多少发送了多少数据包,总时间到达此值后停止ping (以秒为单位)", + "pingPerRequestTimeoutLabel": "单次ping超时时间", + "pingIntervalAdjustedInfo": "间隔时间将根据数据包数量、全局超时时间和单次ping超时时间调整", + "Custom URL": "自定义链接", + "defaultFriendlyName": "新监控项", + "smseagleMsgType": "消息类型", + "smseagleTtsModel": "文本转语音模型ID", + "smseagleApiType": "API 版本", + "smseagleApiv2": "APIv2 (推荐用于新的集成)", + "smseagleDocs": "阅读文档以检查APIv2的可用性: {0}", + "smseagleComma": "使用逗号分隔多项", + "Clear Form": "清除表单", + "pause": "暂停", + "Ip Family": "IP 协议", + "Add Another Tag": "新增标签", + "Staged Tags for Batch Add": "暂存标签以便后续批量添加", + "Happy Eyeballs algorithm": "Happy Eyeballs 算法", + "ipFamilyDescriptionAutoSelect": "使用 {happyEyeballs} 判断 IP 协议。", + "Manual": "手动", + "OAuth Audience": "OAuth 接收方", + "OneChatAccessToken": "OneChat Access Token(密钥)", + "Optional: The audience to request the JWT for": "可选项:JWT 请求的接收方(aud)", + "tagNameExists": "该标签名已与系统标签重复。请直接从列表选择系统标签,或使用另一个标签名。", + "tagAlreadyOnMonitor": "该标签(键值对)已被用在监控项,或是等待添加。", + "OneChatUserIdOrGroupId": "OneChat 用户 ID 或群组 ID", + "customUrlDescription": "将替换监控项监控的链接,用于状态页面处,显示为可点击的链接。", + "smtpHelpText": "SMTPS:测试 SMTP/TLS 是否正常工作;Ignore TLS:通过明文连接;STARTTLS:通过明文连接,然后发出 STARTTLS 命令并验证服务器证书。这些方式都不会导致实际发送电子邮件。", + "SpugPush Template Code": "模板代码", + "Disable URL in Notification": "在通知中禁止解析链接", + "OneChatBotId": "OneChat 机器人 ID", + "smseagleMsgTtsAdvanced": "文本转语音高级呼叫", + "smseagleMsgTts": "文本转语音呼叫", + "Add Tags": "添加标签", + "tagAlreadyStaged": "该标签(键值对)已经被暂存。", + "smseagleMsgRing": "响铃呼叫", + "smseagleMsgSms": "SMS 短信(默认)", + "smseagleContactV2": "联系人 ID", + "smseagleGroupV2": "群组 ID", + "mqttWebsocketPathExplanation": "MQTT 通过 WebSocket 连接时所使用的 WebSocket 路径(例如 /mqtt)", + "Path": "路径", + "mqttHostnameTip": "请使用此格式:{hostnameFormat}", + "mqttWebsocketPathInvalid": "请使用合法的 WebSocket 路径格式", + "mqttWebSocketPath": "MQTT WebSocket 路径", + "Could not clear events": "在 {total} 个事件里有 {failed} 个未能清除", + "No monitors found": "未找到监控项。", + "clearAllEventsMsg": "确定要删除所有事件吗?这将永久删除关联的消息数据,且无法撤销。", + "Clear All Events": "清除所有事件", + "Events cleared successfully": "事件已成功清除。", + "issueWithGoogleChatOnAndroidHelptext": "这也能规避上游的类似 {issuetackerURL} 的 bug", + "Template plain text instead of using cards": "使用纯文本模板而非卡片", + "evolutionInstanceName": "实例名称", + "evolutionRecipient": "手机号码 / 联系人 ID / 组 ID", + "wayToGetEvolutionUrlAndToken": "您可以通过 {0} 进入所需频道获取 API URL 和令牌", + "wayToWriteEvolutionRecipient": "可用格式为不含 + 号的国际通用格式手机号码({0})、联系人 ID({1})或组 ID({2})。", + "brevoFromEmail": "发件邮箱", + "brevoApiKey": "Brevo API Key(密钥)", + "brevoToEmail": "收件邮箱", + "brevoSeparateMultipleEmails": "用逗号分隔多个电子邮件地址", + "brevoBccEmail": "密送邮箱", + "brevoLeaveBlankForDefaultSubject": "留空以使用默认主题", + "brevoSubject": "主题", + "brevoCcEmail": "抄送邮箱", + "brevoLeaveBlankForDefaultName": "留空以使用默认名称", + "brevoFromName": "发件人名称", + "brevoApiHelp": "在此创建一个 API Key:{0}", + "Installing a Nextcloud Talk bot requires administrative access to the server.": "需要服务器的管理权限以安装 Nextcloud 聊天机器人。", + "Send DOWN silently": "静默发送故障事件", + "Send UP silently": "静默发送恢复正常事件", + "Bot secret": "机器人密钥", + "Conversation token": "会话令牌", + "Nextcloud host": "Nextcloud 主机", + "auto-select": "自动选择", + "Maximum Retries": "最大重试次数", + "Number of retry attempts if webhook fails": "webhook 失败后的重试次数(60-180秒为一周期)。", + "webhookPostMethodDesc": "POST 适用于大多数现代 HTTP 服务器。", + "HTTP Method": "HTTP 方法", + "webhookGetMethodDesc": "GET 将数据作为查询参数发送,不允许配置请求体。对于触发 Uptime Kuma Push 监控项非常有用。", + "Invalid userId": "不合法的 userId [{userId}]", + "Invalid mobile": "不合法的电话号码 [{mobile}]", + "descriptionHelpText": "在内部仪表板上显示。允许使用 Markdown,显示前会进行净化处理(保留空格和缩进)。", + "Enter a list of mobile": "输入手机列表", + "Enter a list of userId": "输入用户 ID 列表", + "Dingtalk User List": "用户ID列表", + "Dingtalk Mobile List": "移动列表", + "Mention User List": "需要提及的用户的 ID 列表", + "Mention Mobile List": "提及移动列表", + "wayToGetBaleToken": "你可以从 {0} 获取一个令牌。", + "wayToGetBaleChatID": "你可以通过向机器人发送消息并访问此 URL 来查看 chat_id:", + "supportBaleChatID": "支持私聊/群聊/频道的聊天ID", + "deleteChildrenMonitors": "也删除所有子监视器 | 也删除所有 {count} 个子监视器", + "deleteGroupMsg": "确定要删除此分组吗?", + "wayToGetClickSMSIRTemplateID": "你的模板必须包含一个{uptkumaalert}字段。你可以在{here}创建一个新模板。", + "Template ID": "模板 ID", + "Recipient Numbers": "收件人手机号码", + "twilioMessagingServiceSID": "消息服务 SID(可选)", + "twilloMessagingServiceSIDHelptext": "如果使用 {twillo_messaging_service_help_link} 来管理发件人和功能,请在此处输入消息服务 SID", + "twilioApiKeyHelptext": "API 密钥是可选的,但也是推荐项。你可以提供 Twilio 控制台页面中的 Account SID 和 AuthToken,或提供 Account SID、API Key 和 API Key Secret", + "ariaDeleteMaintenance": "删除此维护计划", + "ariaEditMaintenance": "编辑此维护计划", + "ariaCloneMaintenance": "克隆此维护计划", + "ariaResumeMaintenance": "重启此维护计划", + "ariaPauseMaintenance": "暂停此维护计划", + "Clone Maintenance": "克隆维护事件", + "Swindon Web Server Protocol": "Swindon 网络服务器协议(JSON 编码)", + "WebSocket Transport for JMAP": "通过 WebSocket 传输的 JSON 元应用协议(JMAP)", + "Miele Cloud Connect Protocol": "Miele 云连接协议", + "Extensible Messaging and Presence Protocol": "通过 WebSocket 传输的可扩展消息和表示协议(XMPP)", + "Network API for Notification Channel": "用于通知渠道的 OMA RESTful API", + "ignoreSecWebsocketAcceptHeaderDescription": "如果 WebSocket 升级成功,则允许服务器不回复 Sec-WebSocket-Accept 标头。", + "Unable to get permission to notify": "无法获得通知权限(请求被拒绝或忽略)。", + "Browser not supported": "浏览器不支持", + "Allow Notifications": "允许通知", + "Notifications Enabled": "通知已启用", + "showOnlyLastHeartbeat": "仅显示最后一次心跳信息", + "Reverse Web Process Control": "反向网络进程控制协议(RWPCP)", + "jsflow": "jsFlow 发布、订阅、队列协议", + "Advanced Message Queuing Protocol": "高级消息队列协议(AMQP)1.0+", + "Web Process Control Protocol": "网络进程控制协议(WPCP)", + "Session Initiation Protocol": "通过 WebSocket 传输的会话发起协议(SIP)", + "WebSocket Application Messaging Protocol": "WebSocket 应用消息协议(WAMP)", + "wsSubprotocolDescription": "请输入以逗号分隔的子协议列表。有关子协议的更多信息,请参阅 {documentation}", + "Ignore Sec-WebSocket-Accept header": "忽视 {0} 标头", + "Smart Home IP": "智能家居互联网协议(SHIP)", + "OPC UA JSON Encoding": "OPC UA JSON 编码", + "OPC UA Connection Protocol": "OPC UA 连接协议", + "Softvelum Low Delay Protocol": "Softvelum 低延迟协议 (SLDP)", + "Binary Floor Control Protocol": "通过 WebSocket 传输的二进制发言权控制协议(BFCP)", + "Message Session Relay Protocol": "通过 WebSocket 传输的消息会话中继协议(MSRP)", + "Push Channel Protocol": "推送通道协议(PCP)", + "Penguin Statistics Live Protocol v3": "Penguin Statistics Live Protocol v3 (Protobuf 编码)", + "Binary IRC Protocol": "二进制 IRC 协议", + "Text IRC Protocol": "纯文本 IRC 协议", + "Collection Update": "Collection Update Websocket Subprotocol", + "BACnet Secure Connect Direct Connection": "BACnet 安全连接直连方式", + "BACnet Secure Connect Hub Connection": "BACnet 安全连接 Hub 转发方式", + "Cobra Real Time Messaging Protocol": "Cobra 实时消息协议", + "Softvelum WebSocket signaling protocol": "Softvelum WebSocket 信令协议", + "Constrained Application Protocol": "受限应用协议(CoAP)", + "Broadband Forum User Services Platform": "USP(宽带论坛用户服务平台)", + "Done.best IoT Protocol": "Done.best 物联网(IoT)协议", + "ITU-T T.140 Real-Time Text": "ITU-T T.140 实时传输文本(RTT)协议", + "Declarative Resource Protocol": "声明式资源协议(Declarative Resource Protocol)", + "Webpush Helptext": "Web push 仅支持 SSL (HTTPS) 连接。对于 iOS 设备,必须事先将其网页添加到主屏幕。", + "certHostnameMismatch": "证书的域名与监控项的地址不符。", + "Subprotocol": "子协议", + "Duration (Minutes)": "持续时间(以分钟计)", + "Ignore STARTTLS": "忽略 STARTTLS", + "Use STARTTLS": "使用 STARTTLS", + "SMTP Security": "SMTP 安全", + "Enter the list of nodes": "输入 RabbitMQ 管理节点列表", + "Press Enter to add node": "按 Enter 键以添加节点", + "lowIntervalWarning": "确定要将间隔时间设置成不足 20 秒吗?性能可能会下降,尤其是在大量监控项的情况下。", + "minimumIntervalWarning": "间隔时间低于 20 秒可能会导致性能不佳。", + "settingsDomainExpiry": "域名到期通知", + "labelDomainExpiry": "域名有效期剩余天数", + "labelDomainNameExpiryNotification": "域名到期通知", + "domainExpiryDescription": "域名有效期小于该天数时触发通知:", + "Deselect All": "取消全选", + "Select All": "全选", + "Analytics Script URL": "自定义分析脚本 URL", + "Analytics ID": "分析 ID", + "Analytics Type": "分析类型", + "resendFromEmail": "发件邮箱地址", + "resendFromName": "发件人名称", + "resendApiHelp": "在此处创建一个 API 密钥:{0}", + "resendApiKey": "Resend API 密钥", + "Subprotocol(s)": "子协议", + "wsCodeDescription": "有关状态代码的更多信息,请参阅 {rfc6455}", + "imageResetConfirmation": "重置为默认图片", + "sipsakPingWarning": "如需使用 SIP Options Ping 监控项,请不要使用 Docker 版 Uptime Kuma,另外还需要在服务器上安装 Sipsak 客户端。", + "resendSubject": "标题", + "resendToEmail": "收件邮箱地址", + "resendLeaveBlankForDefaultName": "留空以使用默认名称", + "Umami": "Umami", + "Matomo": "Matomo", + "Plausible": "Plausible", + "Google": "Google", + "systemServiceName": "服务名", + "systemService": "系统服务", + "systemServiceCommandHint": "所用命令:{command}", + "systemServiceDescriptionWindows": "检查 Windows 服务 {service_name} 是否正在运行", + "systemServiceDescriptionLinux": "检查 Linux systemd 服务 {service_name} 是否正在运行", + "systemServiceDescription": "检查系统服务 {service_name} 是否正在运行", + "systemServiceExpectedOutput": "预期输出:\"{0}\"", + "avgPing": "平均 Ping 延迟", + "maxPing": "最大 Ping 延迟", + "minPing": "最小 Ping 延迟", + "invalidURL": "非法的 URL", + "wildcardOnlyForDNS": "仅 DNS 监控项支持在主机名中使用通配符。", + "invalidDNSHostname": "非法的主机名。主机名必须是有效的完全限定域名 (FQDN)。可以使用通配符,可以包含下划线,可以以点号结尾。", + "invalidHostnameOrIP": "非法的主机名或 IP。主机名必须是有效的完全限定域名 (FQDN)。主机名不可使用通配符,但可以包含下划线、以点号结尾。", + "hostnameCannotBeIP": "DNS 主机名不能是 IP 地址。您或许想指定该 IP 为解析服务器?", + "year": "年", + "message": "消息", + "json_value": "JSON 值", + "screenshot of the website": "网站截图", + "Basic checkbox toggle button group": "基本复选框切换按钮组", + "Basic radio toggle button group": "基本单选框切换按钮组", + "resendLeaveBlankForDefaultSubject": "留空以使用默认标题", + "mtls-auth-server-cert-label": "证书", + "mtls-auth-server-cert-placeholder": "证书体", + "mtls-auth-server-key-label": "密钥", + "mtls-auth-server-key-placeholder": "密钥体", + "Sort by certificate expiry": "按证书有效期排序", + "Clear current filters": "清空当前筛选项", + "Sort options": "排序选项", + "Sort by status": "按状态排序", + "Sort by name": "按名称排序", + "Sort by uptime": "按在线时间排序", + "Splunk Rest URL": "Splunk Rest API 地址", + "Severity": "严重程度", + "Message Format": "消息格式", + "smscTranslit": "字符转写模式", + "Region": "地区", + "PushDeer Server URL": "PushDeer 服务器地址", + "To Number": "收件人号码", + "GrafanaOncallURL": "Grafana Oncall 地址", + "Never": "从不", + "System Service": "系统服务", + "SSL/TLS": "SSL/TLS", + "Service Name": "服务名", + "GRPC Options": "GRPC 选项", + "Metadata": "元数据", + "End": "结尾", + "playground": "在线测试", + "Check Type": "检测方式", + "Show this Maintenance Message on which Status Pages": "在哪些状态页面上显示此维护消息", + "Endpoint": "端点", + "Details": "细节", + "RSS Title": "RSS 标题", + "Leave blank to use status page title": "留空以使用状态页标题", + "Open Badge Link Generator": "打开徽章链接生成器", + "Badge Link Generator": "徽章链接生成器 - {0}", + "Badge Link Generator Helptext": "徽章链接适用于已被添加到公开状态页的监控项。更多信息请见 {documentation}。", + "mtls-auth-server-ca-label": "根证书", + "mtls-auth-server-ca-placeholder": "根证书体", + "noMonitorsSelectedWarning": "您正在创建一个未关联任何监控项的维护计划。确定要继续吗?", + "noMonitorsOrStatusPagesSelectedError": "无法创建无任何关联的监控项或状态页的维护计划", + "Resolver Server(s)": "域名解析服务器", + "HeadersInvalidFormatBecause": "请求头不是有效的 JSON 格式,原因:{error}", + "BodyInvalidFormatBecause": "请求体不是有效的 JSON 格式,原因:{error}", + "aliyun_enable_optional_variables_at_the_risk_of_non_delivery": "由于运营商限制,启用可选变量会导致消息无法发送", + "aliyun-template-requirements-and-parameters": "阿里云短信模板必须包含以下变量:{parameters}", + "aliyun-template-optional-parameters": "可选变量:{parameters}", + "notificationSmsServices": "短信服务", + "ntfyCallHelptext": "报警触发时拨打电话。将手机号码设置为 yes 以使用你的首个已验证号码,也可设置为特定手机号码(如 +12223334444)。需要 ntfy Pro 且手机号码已验证。", + "domain_expiry_public_suffix_too_short": ".{publicSuffix} 过短而无法用作顶级域名", + "domain_expiry_unsupported_invalid_domain": "已设置的值 {hostname} 不是有效的域名", + "domain_expiry_unsupported_monitor_type": "该监控项类型不支持域名过期监控", + "domain_expiry_unsupported_missing_target": "该监控项未设置有效的域名或主机名", + "domain_expiry_unsupported_is_ip": "{hostname} 是一个 IP 地址。域名过期监控项只能监控域名", + "domain_expiry_unsupported_public_suffix": "域名 {domain} 没有有效的公共后缀", + "checkPriceAt": "在 {url} 查看 {service} 的价格", + "steamApiKeyDescriptionAt": "要监控 Steam 游戏服务器,您需要一个 Steam Web API 密钥。您可以在 {url} 注册您的 API 密钥", + "You can divide numbers with commas or semicolons": "你可以用{comma}或{semicolon}来分隔数字", + "Halo PSA": "HaloPSA", + "serwersmsRecipientTypePhone": "手机号码", + "serwersmsRecipientType": "收件人类型", + "serwersmsGroupIdHelptext": "客户面板中的 ID 或组 ID。这些 ID 可通过操作组、索引下载,也可以从客户面板的编辑组中复制。", + "TLS Alerts": "TLS 错误", + "None (Successful Connection)": "无(表示成功连接)", + "Expected TLS Alert": "预期的 TLS 错误", + "TLS Alert Spec": "RFC 8446 标准", + "expectedTlsAlertDescription": "选择你希望服务器返回的 TLS 错误。使用 {code} 以在无需客户端证书时验证 mTLS 端点。详情请参见 {link} 。", + "enableSSL": "启用 SSL/TLS", + "mariadbCaCertificateLabel": "根证书", + "mariadbUseSSLHelptext": "启用以使用加密链接。大多数云端数据库要求此项启用。", + "mariadbCaCertificateHelptext": "将自签名证书的根证书以 PEM 格式粘贴到此处以使用自签名证书。留空以使用由公共 CA 签发的证书。", + "serwersmsGroupId": "组 ID", + "serwersmsRecipientTypeGroup": "组", + "halopsa_username_desc": "用于 HaloPSA webhook 身份验证的用户名", + "password": "密码", + "username": "用户名", + "halopsa_webhook_url_desc": "输入从你在 HaloPSA 的集成 Runbook 获得的 webhook 地址(Configuration > Integrations > Custom Integrations > Integration Runbooks)。在创建 webhook 时请勾选 Can only be started from Halo and from a public endpoint(只能从 Halo 和公共端点启动)。", + "halopsa_setup_step3": "复制 Webhook 地址并粘贴到上方输入框中", + "halopsa_setup_step1": "在 HaloPSA 创建一个集成 Runbook(Configuration → Integrations → Integration Runbooks)", + "Setup Instructions": "设置说明", + "halopsa_setup_step2": "设置 runbook 使其能处理警告(如创建工单等)", + "OptionalParameters": "可选变量", + "domain_expiry_unsupported_unsupported_tld_no_rdap_endpoint": "由于 IANA 没有列出相关的 RDAP 服务,域名过期监控项无法监控 .{publicSuffix} 域名", + "Halo PSA Webhook URL": "HaloPSA Webhook 地址", + "halopsa_password_desc": "用于 HaloPSA webhook 身份验证的密码", + "halopsa_setup_step4": "选择基本身份验证并创建用户名和密码。然后输入或粘贴到上方输入框中", + "notificationUniversal": "通用服务", + "notificationPushServices": "推送服务", + "notificationChatPlatforms": "聊天平台", + "notificationOther": "其他集成服务", + "notificationHomeAutomation": "智能家居系统", + "notificationIncidentManagement": "事件管理系统", + "notificationEmail": "邮件", + "passwordTooWeak": "密码强度不足。密码应包含字母和数字,且长度至少为 6 个字符。", + "ntfyCall": "拨打电话", + "versionIs": "版本:{version}", + "days": "{n} 天", + "hours": "{n} 小时", + "years": "{n} 年", + "minutes": "{n} 分钟", + "minuteShort": "{n} 分", + "Sets end time based on start time": "根据起始时间设置结束时间", + "selectedMonitorCountMsg": "已选 {n} 个", + "Actions": "操作", + "deselectAllMonitorsAria": "取消全选", + "selectAllMonitorsAria": "全选", + "selectMonitorMsg": "选择要操作的监控项", + "deleteMonitorsMsg": "确定要删除所选监控项吗?", + "resumedMonitorsMsg": "恢复 {n} 个监控项", + "noMonitorsResumedMsg": "没有监控项被恢复(因为没有监控项被禁用)", + "deletedMonitorsMsg": "删除 {n} 个监控项", + "noMonitorsPausedMsg": "没有监控项被暂停(因为没有监控项被启用)", + "saveResponseDescription": "存储 HTTP 响应并将其作为 {templateVariable} 提供给通知模板", + "responseMaxLength": "响应数据可保存大小的最大值(以字节计)", + "saveErrorResponseForNotifications": "保存 HTTP 错误响应以用于通知", + "saveResponseForNotifications": "保存 HTTP 成功响应以用于通知", + "pausedMonitorsMsg": "暂停 {n} 个监控项", + "responseMaxLengthDescription": "响应数据可保存大小的最大值。设置为 0 表示不限制。超出该值的部分会被截断。默认值:1024(即 1 KiB)", + "Monitors": "{n} 个监控项", + "unknownDays": "未知天数", + "bulkDeleteErrorMsg": "删除 {n} 个监控项失败", + "Please set start time first": "请先设置起始时间", + "discordSuppressNotificationsHelptext": "启用后,消息将发布到频道,但不会触发接收者的推送或桌面通知。", + "Suppress Notifications": "避免通知", + "domain_expiry_unsupported_is_icann": "域名 {domain} 无法作为域名过期监控的目标,其公共后缀 .{publicSuffix} 不受 ICANN 管理", + "lastUpdatedAt": "最后更新于 {date}", + "createdAt": "创建于 {date}", + "logoutCurrentUser": "登出 {username}", + "Certificate Chain:": "证书链:", + "dateCreatedAtFromNow": "创建于 {date}({fromNow})", + "lastUpdatedAtFromNow": "最后更新于 {date}({fromNow})", + "Examples:": "例如:{0}", + "cronScheduleDescription": "Cron 计划:{description}", + "frontendVersionIs": "前端版本:{version}", + "legacyOctopushEndpoint": "旧版 Octopush-DM(地址:{url})", + "octopushEndpoint": "octopush(地址:{url})", + "retryOnlyOnStatusCodeFailureDescription": "启用后仅当 HTTP 状态码检查失败时(例如服务器宕机)才会进行重试。如果状态码检查通过但 JSON 查询失败,监控项将立即被标记为故障状态,不会进行重试。", + "Only retry if status code check fails": "仅当状态码检查失败时重试", + "snmpV3Username": "SNMPv3 用户名", + "WeCom Mentioned Mobile List": "手机号列表", + "screenshotDelayWarning": "该值越大,浏览器运行时间越长,如果有大量同时运行的监控项,可能会导致内存占用升高。", + "Collapse All Groups": "折叠所有分组", + "Expand All Groups": "展开所有分组", + "WeCom Mentioned Mobile List Description": "输入需要提醒的群成员的手机号。用逗号分隔多个号码。用 @all 来提醒所有人。", + "milliseconds": "{n} 毫秒", + "Screenshot Delay": "截图延时(等待 {milliseconds})", + "screenshotDelayDescription": "可选的,在截图前等待的时长。最大值为 {maxValueMs}ms(间隔的 0.5 倍)。", + "mariadbSocketPathDetectedHelptext": "正在连接到环境变量 {0} 对应的数据库。", + "Open your Google Spreadsheet": "打开你的谷歌表格", + "Quick Setup Guide": "快速设置指南", + "Deploy a Google Apps Script as a web app and paste the URL here": "将 Google Apps Script 部署为 Web 应用,并将 URL 粘贴到此处", + "Google Apps Script Webhook URL": "Google Apps Script Webhook 网址", + "Click Deploy → New deployment → Web app": "点击【部署】→【新建部署】,选择类型为【web 应用】", + "Copied to clipboard!": "复制到剪贴板成功!", + "Copy to Clipboard": "复制到剪贴板", + "Copy the web app URL and paste it above": "复制网页 app 地址并粘贴到上方", + "Failed to copy to clipboard": "复制到剪贴板失败", + "Paste the script code (see below)": "粘贴下方代码", + "Google Apps Script Code": "Google Apps Script 代码", + "Go to Extensions → Apps Script": "点击【扩展程序】→【Apps 脚本】", + "Set 'Execute as: Me' and 'Who has access: Anyone'": "设置【执行身份】为【我】、【有访问权限的人员】为【任何人】", + "Please input title": "请输入标题", + "Please input content": "请输入内容", + "slug is not found": "路径未找到", + "deleteIncidentMsg": "确认要删除该事件吗?", + "Resolved": "已解决", + "Resolve": "解决", + "Edit Incident": "编辑事件", + "Pinned incidents are shown prominently on the status page": "置顶事件会在状态页面上显著显示", + "Incident title": "事件标题", + "Past Incidents": "历史事件", + "Incident not found or access denied": "事件未找到或无权访问", + "Incident description": "事件描述", + "Pin this incident": "置顶该事件", + "Loading...": "加载中……", + "Load More": "加载更多", + "No incidents recorded": "未记录到任何事件", + "Disable STARTTLS": "禁用 STARTTLS", + "disableSTARTTLSDescription": "对于不支持 STARTTLS 的 SMTP 服务器,可启用此选项以通过未加密的连接发送电子邮件。", + "Globalping - Access global monitoring probes": "Globalping - 全球监控探测器服务", + "GlobalpingDescription": "Globalping 提供网络测试和 ping 测量服务,通过数千个来自社区的探测器。匿名用户每小时最多只能进行 250 次测试。要将限制提高到每小时 500 次,请将 token 保存在 {accountSettings} 中。", + "globalpingApiTokenDescription": "在 {0} 获取你的 Globalping API Token。", + "Globalping API Token": "Globalping 的 API Token", + "GlobalpingLocationDocs": "查看完整文档", + "GlobalpingLocation": "位置字段可接受大洲、国家/地区、区域、城市、ASN、ISP 或云区域。可通过 {plus} 将多个筛选条件结合起来使用(例如 {amazonPlusGermany} 或 {comcastPlusCalifornia})。如果延迟是一项重要指标,请使用筛选条件将位置范围缩小到更小的区域,以避免出现过高延迟。{fullDocs}。", + "GlobalpingResolverInfo": "IPv4/IPv6 地址或完全限定域名 (FQDN)。默认使用探测器的本地网络的 DNS 服务器。您可以随时更改 DNS 服务器。", + "Protocol": "协议", + "account settings": "账户设置", + "Check for": "检查", + "Monitor Subtype": "监控项子类型", + "Result": "结果", + "example": "例子", + "templateAvailableVariables": "可用变量", + "ntfyNotificationTemplateFallback": "留空以使用 Uptime Kuma 的默认格式", + "ntfyCustomMessage": "自定义消息模板", + "ntfyCustomTitle": "自定义标题模板", + "ntfyUseTemplateDescription": "启用以通过 LiquidJS 模板语法自定义通知标题和消息", + "ntfyUseTemplate": "自定义通知模板", + "Location": "地点", + "GlobalpingIpFamilyInfo": "要使用的 IP 版本。仅在目标为域名时可用。", + "GlobalpingHostname": "可公开访问的目标。通常是域名或 IPv4/IPv6 地址,取决于检测类型。", + "discordMessageTemplate": "消息模板", + "discordUseMessageTemplate": "使用自定义消息模板", + "discordMessageFormatCustom": "自定义模板", + "discordMessageFormatMinimalist": "极简(短状态)", + "discordMessageFormatNormal": "正常(富文本嵌入)", + "discordMessageFormat": "消息格式", + "discordUseMessageTemplateDescription": "启用后消息将会使用基于 LiquidJS 的自定义模板。留空以使用默认格式。", + "slackUseTemplateDescription": "启用后消息将会使用自定义模板发送。你可以使用 Liquid 模板语法,通过 monitorJSON.path 或 monitorJSON.pathName 嵌入监控组信息。", + "slackUseTemplate": "使用自定义消息模板", + "slackIncludeGroupNameDescription": "启用后监控项的分组路径将包含在通知中,以帮助区分不同分组中名称相同的监控项。", + "slackIncludeGroupName": "包括监控项的分组名称", + "API Token": "API 令牌", + "Cloud ID": "Cloud ID", + "See Jira Cloud Docs": "参阅 Jira Cloud 文档", + "aboutJiraCloudId": "关于 Jira Cloud ID 的更多信息:{0}", + "see Jira Cloud Docs": "参阅 Jira Cloud 文档", + "Jira Service Management": "Jira 服务管理", + "halopsa_payload_desc": "下列字段将会发送至 Halo PSA webhook:", + "Webhook Payload Fields": "Webhook 内容字段", + "halopsa_field_uptime_kuma_version": "Uptime Kuma 版本号", + "halopsa_field_timestamp": "ISO 8601 格式的事件时间戳", + "halopsa_field_message": "含状态和详情的完整警报消息", + "halopsa_field_monitor_id": "监控项的唯一识别符(测试时为 null),可用于将警报与工单匹配", + "halopsa_field_monitor": "监控项名称", + "halopsa_field_status": "监控项状态:正常、故障、通知或未知", + "halopsa_setup_step5": "配置 runbook 以通过 monitor_id 将警报与现有工单进行匹配", + "halopsa_id_usage_hint": "💡提示:使用 monitor_id 可以可靠地将警报与工单匹配,如需跟踪事件历史记录则可用 heartbeat_id", + "halopsa_field_title": "警告标题(总是 \"Uptime Kuma Alert\")", + "matrixUseTemplateDescription": "启用后该消息将使用自定义模板发送。", + "matrixUseTemplate": "使用自定义消息模板", + "Teltonika SMS Gateway": "Teltonika SMS 网关", + "RegexMatch": "输入正则表达式以筛选记录值", + "RecordMatch": "记录值筛选", + "teltonikaPassword": "API 密码", + "signalUseTemplate": "使用自定义消息模板", + "teltonikaPhoneNumber": "手机号码", + "teltonikaModemHelptext": "使用 {0} 格式的短信调制解调器 ID。参见 https://developers.teltonika-networks.com/reference/ 上的指引。", + "teltonikaModem": "调制解调器 ID", + "teltonikaPasswordHelptext": "可以在 Teltonika 路由里指定 API 用户密码,如 {0}", + "teltonikaUsername": "API 用户名", + "teltonikaUnsafeTls": "关闭证书验证", + "teltonikaUrlHelptext": "URL 应指定为完整来源,例如 {0} 或 {1}。", + "teltonikaUrl": "Teltonika 设备 URL", + "teltonikaVersionWarning": "此通知类型要求您的 Teltonika 设备运行的 RMS 为 7.14.0 或更高版本。", + "signalUseTemplateDescription": "启用后该消息将使用自定义模板发送。可以使用 Liquid 模板语法以自定义消息格式。", + "domainExpiryNotificationHelp": "提前天数可在设置中配置。", + "GlobalpingMonitorDescription": "Globalping 提供网络测试和 ping 测量服务,通过数千个来自社区的探测器。匿名用户每小时最多只能进行 250 次测试。要将限制提高到每小时 500 次,请将 token 保存在 {accountSettings} 中。更多信息请参阅 {docs}。", + "teltonikaPhoneNumberHelptext": "号码必须使用国际通用格式(如 {0}、{1})。仅限使用一个号码。", + "teltonikaUsernameHelptext": "建议:创建单独的仅用于发送短信的用户并在此填入其用户名", + "teltonikaUnsafeTlsDescription": "关闭 TLS 证书验证后容易受到中间人攻击,可能导致数据泄露和系统被入侵。除非能容忍这种攻击介质,否则请勿关闭证书验证。我们建议使用 Let's Encrypt 并启用自动续期功能。", + "certificateExpiryNotificationHelp": "提前天数可在设置中配置。", + "teamsEnableTagsDescription": "启用后消息将会包含监控项标签。", + "teamsEnableTags": "包含标签", + "360messengerErrorGeneric": "无法加载 WhatsApp 分组列表:{message}", + "360messengerErrorNoGroups": "该账号下未发现 WhatsApp 分组。", + "360messengerErrorNoApiKey": "请先输入你的 360messenger API 密钥。", + "360messengerWayToGetUrlAndToken": "可以从 {0} 获取 360messenger API 密钥。", + "360messengerMessageTemplate": "消息模板", + "360messengerCustomMessageTemplate": "自定义消息模板", + "360messengerEnableCustomMessage": "启用后将使用自定义消息模板以代替默认消息。", + "360messengerEnableSendToGroup": "启用后将发送至 WhatsApp 分组", + "360messengerSelectGroupList": "选择以添加一个分组", + "360messengerSelectedGroupID": "选择分组 ID", + "360messengerGroupList": "WhatsApp 分组", + "360messengerTemplate": "360messenger 消息模板", + "360messengerUseTemplate": "使用自定义消息模板", + "360messengerGroupId": "360messenger 分组 ID", + "360messengerRecipient": "收件人电话号码", + "360messengerAuthToken": "360messenger API 密钥", + "360messengerErrorApi": "无法加载 WhatsApp 分组列表(错误 {statusCode}:{message})。", + "360messengerWayToWriteRecipient": "输入使用国际通用格式且不含前导 + 号的手机号(如:{0})。用逗号分隔多个号码。", + "monitorTypeGameServer": "游戏服务器", + "monitorTypeSpecial": "特殊", + "monitorTypeDatabase": "数据库监控项类型", + "GlobalpingMultipleLocationsError": "尚未支持多选区域,单个监控项请仅使用单个区域。", + "GlobalpingLocationDescription": "位置字段可接受大洲、国家/地区、区域、城市、ASN、ISP 或云区域。可通过 {plus} 将多个筛选条件结合起来使用(例如 {amazonPlusGermany} 或 {comcastPlusCalifornia})。如果延迟是重要指标,请使用筛选条件将位置范围缩小到较小的区域,以避免出现过高延迟。为了获得更好的稳定性,请设置 {datacenter} 筛选条件。{fullDocs}。", + "fluxerMessageFormatMinimalist": "极简(短状态)", + "fluxerMessageFormat": "消息格式", + "fluxerMessageTemplate": "消息模板", + "fluxerUseMessageTemplate": "使用自定义消息模板", + "wayToGetFluxerURL": "可通过进入目标频道的设置 > Webhooks > 创建 Webhook > 复制 Webhook URL 来获取。", + "Notification Action": "通知操作", + "homeAssistantNotificationActionHelptext": "可在 Home Assistant 的“设置 > 开发者工具 > 操作”中找到通知操作列表。搜索“notify”即可找到所需操作。只需输入“notify.”后面的部分,例如,要使用操作“notify.mobile_app_xyz”,请只输入“mobile_app_xyz”。对于内置移动通知,请使用“通过 mobile_app_xyz 发送通知”(而不是“发送通知”)。", + "Fluxer Webhook URL": "Fluxer Webhook 地址", + "fluxerUseMessageTemplateDescription": "启用后消息将会使用基于 LiquidJS 的自定义模板。留空以使用默认格式。", + "fluxerMessageFormatCustom": "自定义模板", + "fluxerMessageFormatNormal": "正常(富文本嵌入)", + "oracledbConnectionString": "Oracle 数据库:{connectionString}", + "maxMessenger": "MAX messenger", + "wayToGetMaxToken": "可在 {0} 获取 MAX 机器人令牌和其他详细信息。", + "maxTemplateFormatDescription": "MAX Messenger 支持纯文本、Markdown 和 HTML 格式。", + "maxUseTemplateDescription": "启用后该消息将使用自定义模板发送。", + "maxUseTemplate": "使用自定义消息模板", + "wayToGetMaxChatID": "指定消息目的地的 chat id。", + "maxApiUrlDescription": "MAX messenger 的 API 基础地址。默认:https://platform-api.max.ru", + "vkPeerIdDescription": "请输入目标 VK 对等节点 ID。此值将原样发送至 API。", + "vkApiVersionDescription": "用于请求的 VK API 版本。仅在需要特定的 VK API 版本以保持兼容性时使用。", + "API Version": "API 版本", + "vkDontParseLinksDescription": "启用后 VK 将不会生成链接预览、摘要。", + "vkDontParseLinks": "禁用链接摘要", + "Peer ID": "对等节点 ID", + "telnyxPhoneNumber": "发件人号码", + "telnyxApiKeyHelptext": "Telnyx V2 API 密钥,可从 Telnyx 门户网站的 API 密钥下找到", + "telnyxApiKey": "API Key(V2)", + "telnyxMessagingProfileIdHelptext": "可选,关联到 Telnyx 消息配置文件以控制发件人设置和功能", + "telnyxMessagingProfileId": "消息配置文件 ID(可选)", + "telnyxToNumberHelptext": "接收短信通知的目标手机号码(例如:+15559876543)", + "telnyxToNumber": "收件人号码", + "evolutionCustomMessageDesc": "留空以使用默认消息。", + "evolutionCustomMessageTitle": "自定义消息(可选)", + "telnyxPhoneNumberHelptext": "发件人 Telnyx 手机号码(必须注册在 Telnyx 账户上,例如:+15551234567)", + "pingPacketSizeWarning": "在 Linux 系统上,小于 16 字节的数据包大小无法计算往返时间(rtt)", + "WebSocket Options": "WebSocket 选项", + "VKTeams Use Template": "使用自定义消息模板", + "VKTeams Chat Id Description": "对于用户,这是他们的邮箱地址。对于群组和频道,从其设置里复制 ID 并在其后添加 @chat.agent", + "VKTeams Base Url Description": "VKTeams 基础 API 地址,在自建方案下需要修改。默认:https://myteam.mail.ru", + "VKTeams Bot Token Description": "可访问 {0} 了解如何获取机器人令牌和其他详细信息。", + "VKTeams Use Template Description": "启用后该消息将使用自定义模板发送。", + "VKTeams Template Format Description": "VKTeams 支持纯文本、MarkdownV2 和 HTML 格式的消息样式。", + "egosmsPhoneNumberDescription": "收件人号码(含国家代码),不带 + 或 00(例如 2567XXXXXXXX)。", + "egosmsSenderDescription": "向收件人显示的发送方 ID(最多 11 个字符)。如果为空,则默认为 EGOSMS。", + "wayToGetEgoSMSToken": "可在 {here} 获得 EgoSMS 的 API 用户名和密码。", + "gamedigToken": "令牌", + "gamedigTokenDescription": "可选项。部分游戏服务器需要身份验证令牌。", + "Bearer Token": "Bearer Token", + "smtpAdditionalHeadersTitle": "额外 Header", + "smtpAdditionalHeadersDesc": "请访问 Nodemailer 自定义 Header {0} 了解配置方法。", + "Connection Timeout": "连接超时", + "Rybbit": "Rybbit", + "flowtriqApiKeyDescription": "可选的 API 密钥以验证发送至 Flowtriq 实例的 Webhook 请求。", + "Flowtriq API Key": "API 密钥", + "Flowtriq Webhook URL": "Webhook 网址", + "wxPusherDocs": "官方指南", + "wxPusherDownloadApp": "下载 app", + "wxPusherSptDescription": "WxPusher Simple Push (SPT) 支持通过独立的 WxPusher 应用程序在 Android、iOS、HarmonyOS、Windows、macOS 和 Linux 等 6 大平台进行推送({download})。扫描下方二维码获取 SPT 即可立即开始发送。若需使用多个 SPT,请用逗号分隔。详情请参阅 {docs}。", + "ooredooAccessKeyDescription": "可在 Ooredoo 短信控制面板上的 API Access Key 处找到。请按原样粘贴,系统会在发送前自动对其进行 Base64 编码。", + "ooredooUsernameDescription": "用于登录 Ooredoo 群发短信控制面板的电子邮件地址。", + "Leave blank to use the default Ooredoo endpoint.": "留空以使用默认 Ooredoo 端点。", + "ooredooRecipientDescription": "请输入用逗号或空格分隔的号码列表。本地 7 位格式号码会自动加上 960 前缀。", + "ooredooBearerTokenDescription": "令牌可在 Ooredoo SMS 仪表板上的示例 cURL 命令中接在 \"Authorization: Bearer\" 后的字符串中获得。", + "Access Key": "访问密钥", + "plivoFromNumber": "发件人号码", + "plivoAuthTokenHelptext": "从 Plivo 控制台获取的 Plivo Auth Token", + "plivoAuthToken": "Auth Token", + "plivoAuthIDHelptext": "从 Plivo 控制台获取的 Plivo Auth ID", + "plivoAuthID": "Auth ID", + "plivoToNumberHelptext": "接收短信或电话通知的目标手机号码(例如:+15559876543)", + "plivoToNumber": "收件人号码", + "plivoFromNumberHelptext": "Plivo 发件人号码(如:+15551234567)。短信可使用经批准的含字母、数字的发件人 ID,而语音通话要求使用 Plivo 提供的手机号码。", + "pm2Description": "检查 PM2 进程 {service_name} 是否在线", + "pm2ExpectedStates": "期望状态:在线。停止或错误都会被视为故障。", + "plivoAnswerUrl": "响应网址", + "plivoVoiceCall": "语音通话", + "plivoMessageType": "消息类型", + "Refresh": "刷新", + "PM2 Process": "PM2 进程", + "Select PM2 process": "选择 PM2 进程", + "plivoAnswerUrlHelptext": "根据调用数据返回 Plivo XML 的网址。警告信息会通过 ?message= 查询参数传递,所以你的端点网址可以将警告信息嵌入进去,例如 {message}。", + "ntpOffsetThreshold": "时间偏移量阈值 (ms)", + "ntpStratumThreshold": "层级阈值", + "ntpThresholdsTitle": "NTP 阈值", + "ntpDispersionThreshold": "根时钟偏差阈值 (ms)", + "ntpDispersionThresholdHelp": "根时钟偏差超过此值时,标记为故障。偏差值表示服务器时钟的精度。", + "ntpStratumThresholdHelp": "层级等于或高于此值时,标记为故障。层级表示时间源质量(1 代表来自GPS/原子钟,16 代表无法同步)。", + "ntpOffsetThresholdHelp": "时间偏移量超过此毫秒值时,标记为故障。", + "Pinglet Topic URL": "主题URL", + "pingletTopicUrlDescription": "用于发送告警的 Pinglet 主题发布 URL,例如 https://app.pinglet.co.uk/your-namespace/alerts。如果主题尚不存在,将在首次发布时自动创建。", + "wayToGetOpenwaApiUrl": "您的 OpenWA 实例 URL。", + "wayToGetOpenwaApiKey": "用于您的 OpenWA 实例的 API 密钥。", + "openwaSession": "会话", + "openwaChatId": "聊天 ID(电话号码 / 联系人 ID / 群组 ID)", + "openwaCustomMessageTitle": "自定义消息(可选)", + "wayToGetOpenwaSession": "OpenWA 用于发送通知的会话 ID。", + "turbosmtpConsumerKey": "消费者密钥", + "turbosmtpConsumerSecret": "消费者密钥(Secret)", + "wayToWriteOpenwaChatId": "带国际区号的电话号码(开头不带加号,{0})、联系人 ID({1})或群组 ID({2})。多个聊天 ID 之间用逗号分隔。", + "openwaCustomMessageDesc": "留空则使用默认消息。", + "Hash Key": "哈希密钥", + "wayToGetBearSMSToken": "您可以从 {0} 获取您的 BearSMS API 用户名和哈希密钥。", + "bearsmsPhoneNumberDescription": "包含国家/地区代码的收信人号码,请勿输入 + 或 00(例如:9725XXXXXXXX)。", + "bearsmsSenderDescription": "向收信人显示的可选发信人 ID(最多 11 个字符)。该 ID 必须在您的 BearSMS 帐户中获得批准,否则将使用该帐户的默认发信人。", + "ClickUp API Token": "ClickUp API 令牌", + "ClickUp Workspace ID": "ClickUp 工作区 ID", + "ClickUp Workspace ID Description": "您可以在工作区的 URL 中找到它,紧跟在 app.clickup.com/ 之后,例如 app.clickup.com/12345678/home。", + "ClickUp Channel ID": "ClickUp 频道 ID", + "ClickUp Channel ID Description": "您可以在聊天频道的 URL 中找到它,紧跟在 /chat/r/ 之后,例如 .../chat/r/abc123-4567。", + "ClickUp API Token Description": "您可以在 ClickUp 中创建个人访问令牌,方法是转到“设置”(Settings) -> “集成与 ClickApps”(Integrations & ClickApps) -> “ClickUp API” -> “API 令牌”(API Token)", + "smsgatewayTo": "(多个)手机号码", + "smsgatewayToHelptext": "用逗号分割的国际号码格式收件人手机号列表(例: +15551234567)", + "smsgatewayUrlHelptext": "您的SMS网关的根地址,不包含API路径。", + "milkyHttpAddress": "Milky HTTP地址(无需“/api”后缀)", + "milkyMessageType": "Milky消息类型", + "milkyGroupMessage": "群聊", + "milkyPrivateMessage": "私信", + "milkyUserOrGroupId": "群聊/用户ID", + "milkySafetyTips": "为了安全起见,必须设置访问令牌", + "notifyAppSetup": "打开 Notify! 并在设置中找到设备 ID(Device ID)和令牌(token)。无需账户。了解更多信息,请访问 {0}。", + "notifyAppDeviceIdDescription": "Notify! App 的设备 ID(Device ID),或分组 ID(用于同时向分组内所有设备发送警报)。", + "Device ID or Group ID": "设备 ID(Device ID)或分组 ID", + "indigoActionGroupIdHelptext": "变量更新后执行该操作。可设置变量、动作组,或同时设置这两个。在 Indigo 中,通过“右键点击 > 复制 ID”来复制 ID。", + "Action Group ID": "操作分组 ID", + "indigoVariableIdHelptext": "警报信息将会写入该变量。", + "Variable ID": "变量 ID", + "indigoApiKeyHelptext": "来自 Indigo 账户的“授权”设置里的 API 密钥,或者是本地网络中 Web 服务器的本地密钥。", + "indigoIgnoreTlsErrorHelptext": "使用本地网络的 Indigo Web 服务器时必选,以忽略自签名证书的信任链错误。如使用 Reflector 则不需要勾选。", + "indigoUrlHelptext": "你的 Indigo Reflector 地址(形如 https://NAME.indigodomo.net)或本地网络上的 Indigo Web 服务器(形如 https://IP:8176)。", + "Indigo URL": "Indigo 地址", + "optional": "可选的", + "sshPassphraseHelpText": "可选的。仅当你的私钥使用密码加密时必填。", + "sshPrivateKeyHelpText": "粘贴密钥文件(如 id_rsa 或 id_ed25519)的文本内容。", + "sshPrivateKeyPlaceholder": "-----BEGIN OPENSSH PRIVATE KEY-----\n……\n-----END OPENSSH PRIVATE KEY-----", + "Passphrase": "密码", + "SSH Private Key": "SSH 密钥", + "sftpPathHelpText": "可选的。勾选后,监控项将会验证该文件路径是否在 SFTP 服务器上存在。", + "SFTP Path to Check": "SFTP 检测文件路径", + "Use Own Line for Pattern": "通过专线发送短信模板", + "Create an Amoot SMS pattern": "创建一个 Amoot 短信模板", + "Amoot SMS pattern help": "输入 Amoot 短信控制面板上的短信模板 ID。{0}", + "Pattern Code ID": "短信模板 ID", + "Use Pattern": "使用短信模板", + "Amoot SMS recipient numbers help": "输入用逗号分隔的一个或多个手机号码。", + "Line Number": "专线编号", + "Channel": "频道", + "Client Key": "客户密钥" +} diff --git a/src/lang/zh-HK.json b/src/lang/zh-HK.json new file mode 100644 index 0000000..9ecd5dd --- /dev/null +++ b/src/lang/zh-HK.json @@ -0,0 +1,1089 @@ +{ + "languageName": "繁體中文 (香港)", + "Settings": "設定", + "Dashboard": "主控台", + "New Update": "有更新", + "Language": "語言", + "Appearance": "外觀", + "Theme": "主題", + "General": "一般", + "Version": "版本", + "Check Update On GitHub": "到 Github 查看更新", + "List": "列表", + "Add": "新增", + "Add New Monitor": "新增監測器", + "Quick Stats": "綜合數據", + "Up": "上線", + "Down": "離線", + "Pending": "待定", + "Unknown": "不明", + "Pause": "暫停", + "pauseDashboardHome": "暫停", + "Name": "名稱", + "Status": "狀態", + "DateTime": "日期時間", + "Message": "內容", + "No important events": "沒有重要事件", + "Resume": "恢復", + "Edit": "編輯", + "Delete": "刪除", + "Current": "目前", + "Uptime": "上線率", + "Cert Exp.": "証書期限", + "day": "日", + "-day": "日", + "hour": "小時", + "-hour": "小時", + "checkEverySecond": "每 {0} 秒檢查一次", + "Response": "反應時間", + "Ping": "反應時間", + "Monitor Type": "監測器類型", + "Keyword": "關鍵字", + "Friendly Name": "名稱", + "URL": "網址 URL", + "Hostname": "Hostname", + "Port": "Port", + "Heartbeat Interval": "檢查間距", + "Retries": "重試數次確定為離線", + "retriesDescription": "重試多少次後才判定為離線及傳送通知。如數值為 0 會即判定為離線及傳送通知。", + "Advanced": "進階", + "ignoreTLSError": "忽略 HTTPS 網站的 TLS/SSL 錯誤", + "Upside Down Mode": "反轉模式", + "upsideDownModeDescription": "反轉狀態,如網址是可正常瀏覽,會被判定為 '離線/DOWN'", + "Max. Redirects": "跟隨重新導向 (Redirect) 的次數", + "maxRedirectDescription": "設為 0 即不跟蹤", + "Accepted Status Codes": "接受為上線的 HTTP 狀態碼", + "acceptedStatusCodesDescription": "可多選", + "Save": "儲存", + "Notifications": "通知", + "Not available, please setup.": "無法使用,需要設定。", + "Setup Notification": "設置通知", + "Light": "明亮", + "Dark": "暗黑", + "Auto": "自動", + "Theme - Heartbeat Bar": "監測器列表 狀態條外觀", + "Normal": "一般", + "Bottom": "下方", + "None": "沒有", + "Timezone": "時區", + "Search Engine Visibility": "是否允許搜尋器索引", + "Allow indexing": "允許索引", + "Discourage search engines from indexing site": "不建議搜尋器索引", + "Change Password": "變更密碼", + "Current Password": "目前密碼", + "New Password": "新密碼", + "Repeat New Password": "確認新密碼", + "passwordNotMatchMsg": "密碼不一致", + "Update Password": "更新密碼", + "Disable Auth": "取消登入認証", + "Enable Auth": "開啟登入認証", + "disableauth.message1": "你是否確認{disableAuth}?", + "disable authentication": "取消登入認証", + "disableauth.message2": "這個功能是設計給已有{intendThirdPartyAuth}的用家,例如 Cloudflare Access。", + "where you intend to implement third-party authentication": "第三方認証", + "Please use this option carefully!": "請小心使用。", + "Logout": "退出", + "notificationDescription": "新增後,你需要在監測器裡啟用。", + "Leave": "離開", + "I understand, please disable": "我明白,請取消登入認証", + "Confirm": "確認", + "Yes": "是", + "No": "否", + "Username": "帳號", + "Password": "密碼", + "Remember me": "記住我", + "Login": "登錄", + "No Monitors, please": "沒有監測器,請", + "add one": "新增", + "Notification Type": "通知類型", + "Email": "電郵", + "Test": "測試", + "keywordDescription": "搜索 HTML 或 JSON 裡是否有出現關鍵字(注意英文大細階)", + "Certificate Info": "憑證詳細資料", + "deleteMonitorMsg": "是否確定刪除這個監測器?", + "deleteNotificationMsg": "是否確定刪除這個通知設定?如監測器啟用了這個通知,將會收不到通知。", + "Resolver Server": "DNS 伺服器", + "Resource Record Type": "DNS 記錄類型", + "resolverserverDescription": "預設為 Cloudflare DNS 伺服器,你可以轉用其他 DNS 伺服器。", + "rrtypeDescription": "請選擇 DNS 記錄類型", + "pauseMonitorMsg": "是否確定要暫停?", + "Last Result": "最後結果", + "Create your admin account": "建立管理員帳號", + "Repeat Password": "重複密碼", + "respTime": "反應時間 (ms)", + "notAvailableShort": "N/A", + "Create": "建立", + "clearEventsMsg": "是否確定刪除這個監測器的所有事件?", + "clearHeartbeatsMsg": "是否確定刪除這個監測器的所有脈搏資料?", + "confirmClearStatisticsMsg": "是否確定刪除所有監測器的脈搏資料?(您的監測器會繼續正常運作)", + "Clear Data": "清除資料", + "Events": "事件", + "Heartbeats": "脈搏", + "Auto Get": "自動獲取", + "enableDefaultNotificationDescription": "新增監測器時這個通知會預設啟用,當然每個監測器亦可分別控制開關。", + "Default enabled": "預設通知", + "Also apply to existing monitors": "同時取用至目前所有監測器", + "Export": "匯出", + "Import": "匯入", + "backupDescription": "您可以備份所有監測器及所有通知。", + "backupDescription2": "註:此備份不包括歷史記錄。", + "backupDescription3": "此備份可能包含了一些敏感資料如通知裡的 Token,請小心保存備份。", + "alertNoFile": "請選擇一個檔案", + "alertWrongFileType": "請選擇 JSON 檔案", + "twoFAVerifyLabel": "請輸入 Token 以確認 2FA:", + "tokenValidSettingsMsg": "Token 有效!您現在可以儲存 2FA 設定。", + "confirmEnableTwoFAMsg": "您確定要啟用 2FA 嗎?", + "confirmDisableTwoFAMsg": "您確定要停用 2FA 嗎?", + "Apply on all existing monitors": "套用至目前所有監測器", + "Verify Token": "驗証 Token", + "Setup 2FA": "設定 2FA", + "Enable 2FA": "開啟 2FA", + "Disable 2FA": "關閉 2FA", + "2FA Settings": "2FA 設定", + "Two Factor Authentication": "雙重認證", + "filterActive": "執行狀態", + "filterActivePaused": "已暫停", + "Active": "生效", + "Inactive": "未生效", + "Token": "Token", + "Show URI": "顯示 URI", + "Clear all statistics": "清除所有歷史記錄", + "retryCheckEverySecond": "Retry every {0} seconds.", + "importHandleDescription": "\"略過已存在的\" 會跳過所有相同名稱的監測器或通知。 '覆蓋' 將刪除所有現有的監測器及通知。", + "confirmImportMsg": "您確定要匯入備份嗎?請確認你已選擇正確的匯入設定。", + "Heartbeat Retry Interval": "Heartbeat Retry Interval", + "Import Backup": "匯入備份", + "Export Backup": "匯出備份", + "Skip existing": "略過已存在的", + "Overwrite": "覆蓋", + "Options": "選項", + "Keep both": "兩者並存", + "Tags": "標籤", + "Add New below or Select...": "在下方新增或選取…", + "Tag with this name already exist.": "Tag with this name already exist.", + "Tag with this value already exist.": "Tag with this value already exist.", + "color": "顏色", + "value (optional)": "值 (非必需)", + "Gray": "灰", + "Red": "紅", + "Orange": "橙", + "Green": "綠", + "Blue": "藍", + "Indigo": "靛", + "Purple": "紫", + "Pink": "粉紅", + "Search...": "搜尋…", + "Avg. Ping": "平均反應時間", + "Avg. Response": "平均反應時間", + "Entry Page": "Entry Page", + "statusPageNothing": "Nothing here, please add a group or a monitor.", + "No Services": "沒有服務", + "All Systems Operational": "一切正常", + "Partially Degraded Service": "部份服務受阻", + "Degraded Service": "服務受阻", + "Add Group": "新增群組", + "Add a monitor": "新增監測器", + "Edit Status Page": "編輯 Status Page", + "Go to Dashboard": "前往主控台", + "Status Page": "Status Page", + "Status Pages": "Status Pages", + "telegram": "Telegram", + "webhook": "Webhook", + "smtp": "電郵 (SMTP)", + "discord": "Discord", + "teams": "Microsoft Teams", + "signal": "Signal", + "gotify": "Gotify", + "slack": "Slack", + "rocket.chat": "Rocket.chat", + "pushover": "Pushover", + "pushy": "Pushy", + "octopush": "Octopush", + "promosms": "PromoSMS", + "lunasea": "LunaSea", + "apprise": "Apprise (支援 50 多種通知)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "deleteStatusPageMsg": "是否確定刪除這個 Status Page?", + "Push URL": "推送網址", + "needPushEvery": "您應每 {0} 秒呼叫此網址。", + "pushOptionalParams": "選填參數:{0}", + "defaultNotificationName": "我的 {notification} 通知 ({number})", + "here": "此處", + "Required": "必填", + "Bot Token": "機器人權杖", + "wayToGetTelegramToken": "您可以從 {0} 取得 Token。", + "Chat ID": "聊天 ID", + "supportTelegramChatID": "支援 對話/群組/頻道的聊天 ID", + "wayToGetTelegramChatID": "傳送訊息給機器人,並前往以下網址以取得您的 chat ID:", + "YOUR BOT TOKEN HERE": "在此填入您的機器人權杖", + "chatIDNotFound": "找不到 Chat ID;請先傳送訊息給機器人", + "Post URL": "Post 網址", + "Content Type": "Content Type", + "webhookJsonDesc": "{0} 適合任何現代的 HTTP 伺服器,如 Express.js", + "webhookFormDataDesc": "{multipart} 適合 PHP。 JSON 必須先經由 {decodeFunction} 剖析。", + "secureOptionNone": "無 / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "忽略 TLS 錯誤", + "From Email": "寄件人", + "emailCustomSubject": "自訂主旨", + "To Email": "收件人", + "smtpCC": "CC", + "smtpBCC": "BCC", + "Discord Webhook URL": "Discord Webhook 網址", + "wayToGetDiscordURL": "您可以前往 伺服器設定 -> 整合 -> Webhook -> 新 Webhook 以取得", + "Bot Display Name": "機器人顯示名稱", + "Prefix Custom Message": "前綴自訂訊息", + "Webhook URL": "Webhook 網址", + "wayToGetTeamsURL": "您可以前往此頁面以了解如何建立 Webhook 網址 {0}。", + "Number": "號碼", + "Recipients": "收件人", + "needSignalAPI": "您需要有 REST API 的 Signal 客戶端。", + "wayToCheckSignalURL": "您可以前往下列網址以了解如何設定:", + "signalImportant": "注意: 不得混合收件人的群組和號碼!", + "Application Token": "應用程式權杖", + "Server URL": "伺服器網址", + "Priority": "優先度", + "Icon Emoji": "Emoji 圖示", + "Channel Name": "頻道名稱", + "Uptime Kuma URL": "Uptime Kuma 網址", + "aboutWebhooks": "更多關於 Webhook 的資訊: {0}", + "aboutChannelName": "如果您不想使用 Webhook 頻道,請在 {0} 頻道名稱欄位填入您想使用的頻道。例如: #其他頻道", + "aboutKumaURL": "如果您未填入 Uptime Kuma 網址。將預設使用專案 Github 頁面。", + "emojiCheatSheet": "Emoji 一覽表: {0}", + "PushByTechulus": "Push by Techulus", + "clicksendsms": "ClickSend SMS", + "GoogleChat": "Google Chat (僅限 Google Workspace)", + "User Key": "使用者金鑰", + "Device": "裝置", + "Message Title": "訊息標題", + "Notification Sound": "通知音效", + "More info on:": "更多資訊: {0}", + "pushoverDesc1": "緊急優先度 (2) 的重試間隔為 30 秒並且會在 1 小時後過期。", + "pushoverDesc2": "如果您想要傳送通知到不同裝置,請填寫裝置欄位。", + "SMS Type": "簡訊類型", + "octopushTypePremium": "Premium (快速 - 建議用於警報)", + "octopushTypeLowCost": "Low Cost (緩慢 - 有時會被營運商阻擋)", + "checkPrice": "查看 {0} 價格:", + "apiCredentials": "API 認證", + "octopushLegacyHint": "您使用的是舊版的 Octopush (2011-2020) 還是新版?", + "Check octopush prices": "查看 octopush 價格 {0}。", + "octopushPhoneNumber": "電話號碼 (intl 格式,例如:+33612345678) ", + "octopushSMSSender": "簡訊寄件人名稱:3-11位英數字元及空白 (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea 裝置 ID", + "Apprise URL": "Apprise 網址", + "Example:": "範例:{0}", + "Read more:": "深入瞭解:{0}", + "Status:": "狀態:{0}", + "Read more": "深入瞭解", + "appriseInstalled": "已安裝 Apprise。", + "appriseNotInstalled": "尚未安裝 Apprise。{0}", + "Access Token": "存取權杖", + "Channel access token": "頻道存取權杖", + "Line Developers Console": "Line 開發者控制台", + "lineDevConsoleTo": "Line 開發者控制台 - {0}", + "Basic Settings": "基本設定", + "User ID": "使用者 ID", + "Messaging API": "Messaging API", + "wayToGetLineChannelToken": "首先,前往 {0},建立 provider 和 channel (Messaging API)。接著您就可以從上面提到的選單項目中取得頻道存取權杖及使用者 ID。", + "Icon URL": "圖示網址", + "aboutIconURL": "您可以在 \"圖示網址\" 中提供圖片網址以覆蓋預設個人檔案圖片。若已設定 Emoji 圖示,將忽略此設定。", + "aboutMattermostChannelName": "您可以在 \"頻道名稱\" 欄位中填寫頻道名稱以覆蓋 Webhook 的預設頻道。必須在 Mattermost 的 Webhook 設定中啟用。例如:#其他頻道", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - 便宜,但是很慢且經常過載。僅限位於波蘭的收件人。", + "promosmsTypeFlash": "SMS FLASH - 訊息會自動在收件人的裝置上顯示。僅限位於波蘭的收件人。", + "promosmsTypeFull": "SMS FULL - 高級版,您可以使用您的寄件人名稱 (必須先註冊名稱。對於警報來說十分可靠。", + "promosmsTypeSpeed": "SMS SPEED - 系統中的最高優先度。快速、可靠,但昂貴 (約 SMS FULL 的兩倍價格)。", + "promosmsPhoneNumber": "電話號碼 (若收件人位於波蘭則無需輸入區域代碼)", + "promosmsSMSSender": "簡訊寄件人名稱:預先註冊的名稱或以下的預設名稱:InfoSMS、SMS Info、MaxSMS、INFO、SMS", + "Feishu WebHookUrl": "飛書 WebHook 網址", + "matrixHomeserverURL": "Homeserver 網址 (開頭為 http(s)://,結尾可能帶連接埠)", + "Internal Room Id": "Internal Room ID", + "matrixDesc1": "您可以在 Matrix 客戶端的房間設定中的進階選項找到 internal room ID。應該看起來像 !QMdRCpUIfLwsfjxye6:home.server。", + "matrixDesc2": "使用您自己的 Matrix 使用者存取權杖將賦予存取您的帳號和您加入的房間的完整權限。建議建立新使用者,並邀請至您想要接收通知的房間中。您可以執行 {0} 以取得存取權杖", + "Method": "方法", + "Body": "主體", + "Headers": "標頭", + "PushUrl": "Push URL", + "HeadersInvalidFormat": "要求標頭不是有效的 JSON:", + "BodyInvalidFormat": "請求主體不是有效的 JSON:", + "Monitor History": "監測器歷史紀錄", + "clearDataOlderThan": "保留 {0} 天內的監測器歷史紀錄。", + "PasswordsDoNotMatch": "密碼不相符。", + "records": "記錄", + "One record": "一項記錄", + "Showing {from} to {to} of {count} records": "正在顯示 {count} 項記錄中的 {from} 至 {to} 項", + "steamApiKeyDescription": "若要監測 Steam 遊戲伺服器,您將需要 Steam Web-API 金鑰。您可以在此註冊您的 API 金鑰:", + "Current User": "目前使用者", + "recent": "最近", + "Done": "完成", + "Info": "資訊", + "Security": "安全性", + "Steam API Key": "Steam API Key", + "Shrink Database": "壓縮資料庫", + "Pick a RR-Type...": "選擇資源記錄類型…", + "Pick Accepted Status Codes...": "選擇可接受的狀態碼…", + "Default": "預設", + "HTTP Options": "HTTP 選項", + "Create Incident": "建立事件", + "Title": "標題", + "Content": "內容", + "Style": "樣式", + "info": "資訊", + "warning": "警告", + "danger": "危險", + "primary": "主要", + "light": "淺色", + "dark": "暗色", + "Post": "發佈", + "Please input title and content": "請輸入標題及內容", + "Created": "建立", + "Last Updated": "最後更新", + "Unpin": "取消釘選", + "Switch to Light Theme": "切換至淺色佈景主題", + "Switch to Dark Theme": "切換至深色佈景主題", + "Show Tags": "顯示標籤", + "Hide Tags": "隱藏標籤", + "Description": "描述", + "No monitors available.": "沒有可用的監測器。", + "Add one": "新增一個", + "No Monitors": "無監測器", + "Untitled Group": "未命名群組", + "Services": "服務", + "Discard": "捨棄", + "Cancel": "取消", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API 使用者名稱 (包括 webapi_ 前綴)", + "serwersmsAPIPassword": "API 密碼", + "serwersmsPhoneNumber": "電話號碼", + "serwersmsSenderName": "SMS 寄件人名稱 (由客戶入口網站註冊)", + "stackfield": "Stackfield", + "smtpDkimSettings": "DKIM 設定", + "smtpDkimDesc": "請參考 Nodemailer DKIM {0} 使用方式。", + "documentation": "文件", + "smtpDkimDomain": "網域名稱", + "smtpDkimKeySelector": "Key Selector", + "smtpDkimPrivateKey": "私密金鑰", + "smtpDkimHashAlgo": "雜湊演算法 (選填)", + "smtpDkimheaderFieldNames": "要簽署的郵件標頭 (選填)", + "smtpDkimskipFields": "不簽署的郵件標頭 (選填)", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API Endpoint", + "alertaEnvironment": "環境", + "alertaApiKey": "API 金鑰", + "alertaAlertState": "警示狀態", + "alertaRecoverState": "恢復狀態", + "Proxies": "代理伺服器", + "default": "預設", + "enabled": "啟用", + "setAsDefault": "設為預設", + "deleteProxyMsg": "您確定要為所有監測器刪除此 Proxy 嗎?", + "proxyDescription": "必須將 Proxy 指派給監測器才能運作。", + "enableProxyDescription": "此 Proxy 在啟用前不會在監測器上生效,您可以藉由控制啟用狀態來暫時對所有的監測器停用 Proxy。", + "setAsDefaultProxyDescription": "預設情況下,新監測器將啟用此 Proxy。您仍可分別停用各監測器的 Proxy。", + "Maintenance": "維護", + "statusMaintenance": "維護中", + "Enable DNS Cache": "(已棄用)啟用 DNS 快取", + "Enable": "啟用", + "Disable": "停用", + "Schedule maintenance": "計劃維護", + "Help": "幫助", + "Valid To:": "有效期至:", + "Date Created": "新增日期", + "resendEveryXTimes": "每 {0} 次便重新傳送", + "resendDisabled": "重新傳送已停用", + "enableGRPCTls": "允許以 TLS 連線傳送 gRPC 要求", + "recurringIntervalMessage": "每天一次 | 每 {0} 天一次", + "affectedMonitorsDescription": "選擇受目前維護影響的監測器", + "affectedStatusPages": "在已選取的狀態頁中顯示此維護訊息", + "Primary Base URL": "主要 Base URL", + "Passive Monitor Type": "被動監測器類型", + "Resend Notification if Down X times consecutively": "每 X 次心跳皆離線,重新傳送通知", + "Game": "遊戲", + "Specific Monitor Type": "特定監測器類型", + "Monitor": "監測器 | 監測器", + "General Monitor Type": "一般監測器類型", + "Affected Monitors": "受影響的監測器", + "Powered by": "技術支持:", + "Add New Status Page": "新增 Status Page", + "Page Not Found": "找不到頁面", + "Start of maintenance": "維護開始時間", + "All Status Pages": "所有 Status Page", + "webhookAdditionalHeadersTitle": "額外 Header", + "successMessage": "成功訊息", + "error": "錯誤", + "critical": "嚴重", + "Customize": "自定義", + "Custom Footer": "自訂 Footer", + "Custom CSS": "自訂 CSS", + "Valid": "有效", + "Invalid": "無效", + "Installed": "已安裝", + "Not installed": "未安裝", + "Running": "執行中", + "Stop": "停止", + "Next": "下一步", + "No Proxy": "無 Proxy", + "Backup": "備份", + "Pick Affected Monitors...": "挑選受影響的監測器…", + "Custom": "自訂", + "Not running": "未執行", + "Remove Token": "移除 Token", + "Start": "開始", + "User": "使用者", + "trustProxyDescription": "信任 'X-Forwarded-*' 的 Header。如果您想取得正確的 Client IP,且您的 Uptime Kuma 架設於 Nginx 或 Apache Proxy 之後,您應啟用此選項。", + "Reverse Proxy": "反向 Proxy", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "若要取得長期有效 Access Token,請按您的個人檔案名稱 (左下角),捲動至最下方,然後按建立 Token。 ", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "您可以在 Home Assistant 中查看通知服務的列表,在\"開發者工具 > 服務\"下搜尋\"通知\"來找到您的裝置/手機的名稱。", + "loadingError": "未能取得數據,請重新再試。", + "uninstall": "解除安裝", + "wayToGetZohoCliqURL": "您可以前往此頁面以了解如何建立 Webhook 網址 {0}。", + "Select status pages...": "選擇 Status Page…", + "webhookAdditionalHeadersDesc": "設定傳送 Webhook 時使用的額外 Header。請使用 JSON key/value 格式。", + "topic": "Topic", + "topicExplanation": "監測 MQTT 中的一個 Topic", + "successMessageExplanation": "MQTT 中收到視為成功的訊息", + "Certificate Chain": "証書信任鏈", + "Slug": "Slug", + "Accept characters:": "可用字元:", + "startOrEndWithOnly": "只能使用 {0} 開頭或結尾", + "No consecutive dashes": "不得連續使用破折號", + "The slug is already taken. Please choose another slug.": "此 slug 已被使用。請選擇其他 slug。", + "Authentication": "驗證", + "HTTP Basic Auth": "HTTP Basic Auth", + "New Status Page": "新 Status Page", + "Docker Daemon": "Docker Daemon", + "About": "關於", + "wayToGetCloudflaredURL": "(到 {0} 下載 cloudflared)", + "cloudflareWebsite": "Cloudflare 網頁", + "Message:": "訊息:", + "Don't know how to get the token? Please read the guide:": "不知道如何取得權杖?請閱讀指南:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "如果您正透過 Cloudflare Tunnel 連線,可能會導致連線中斷。您確定要停止嗎?請輸入密碼以確認。", + "HTTP Headers": "HTTP Headers", + "Trust Proxy": "信任 Proxy", + "Other Software": "其他軟件", + "For example: nginx, Apache and Traefik.": "例如 nginx、Apache 和 Traefik。", + "Please read": "請閱覽", + "Subject:": "標題:", + "Days Remaining:": "餘下日數:", + "Issuer:": "簽發者:", + "Fingerprint:": "指紋:", + "No status pages": "無 Status Page", + "Domain Name Expiry Notification": "Domain 到期通知", + "Footer Text": "Footer 文字", + "Show Powered By": "顯示 \"Powered By\"", + "Domain Names": "Domain", + "signedInDisp": "登入為 {0}", + "signedInDispDisabled": "驗證已停用。", + "RadiusSecret": "Radius Secret", + "RadiusSecretDescription": "Client 與 Server 之間的共享 Secret", + "RadiusCalledStationId": "Called Station Id", + "RadiusCallingStationId": "Calling Station Id", + "Certificate Expiry Notification": "証書過期通知", + "API Username": "API 使用者名稱", + "API Key": "API Key", + "Show update if available": "有更新時顯示", + "Also check beta release": "檢查 Beta 版本", + "Using a Reverse Proxy?": "正在使用反向代理 (Reverse Proxy)?", + "Check how to config it for WebSocket": "查看如何加入 WebSocket 設定", + "Steam Game Server": "Steam 遊戲 Server", + "Most likely causes:": "最可能原因:", + "The resource is no longer available.": "資源已不能存取。", + "There might be a typing error in the address.": "網址可能輸入錯誤。", + "What you can try:": "您可以嘗試:", + "Retype the address.": "重新輸入網址。", + "Go back to the previous page.": "返回上一頁。", + "Coming Soon": "即將推出", + "Connection String": "Connection String", + "Query": "Query", + "settingsCertificateExpiry": "TLS 証書到期", + "certificationExpiryDescription": "証書將於 X 天後到期時觸發 HTTPS 監測器通知:", + "Setup Docker Host": "配置 Docker 宿主資訊", + "Connection Type": "連線方式", + "deleteDockerHostMsg": "您確定要為所有監測器刪除此 Docker 主機嗎?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker Container", + "Container Name / ID": "Container 名稱 / ID", + "Docker Host": "Docker 主機", + "Docker Hosts": "Docker 主機", + "Domain": "Domain", + "Workstation": "Workstation", + "ZohoCliq": "ZohoCliq", + "disableCloudflaredNoAuthMsg": "您處於無驗證模式。無須輸入密碼。", + "wayToGetLineNotifyToken": "您可以從 {0} 取得 Access Token。", + "Examples": "例子", + "Home Assistant URL": "Home Assistant 網址", + "Long-Lived Access Token": "長期有效 Access Token", + "Notification Service": "通知服務", + "default: notify all devices": "預設:通知所有服務", + "Automations can optionally be triggered in Home Assistant:": "可以選擇在 Home Assistant 中觸發自動程序:", + "Trigger type:": "觸發類型:", + "backupRecommend": "請直接備份 Docker Volume 或 ./data/ 資料夾。", + "squadcast": "Squadcast", + "or": "或", + "recurringInterval": "間隔", + "Recurring": "重複性", + "strategyManual": "手動啟用/停用", + "warningTimezone": "正在使用 Server 的時區", + "weekdayShortMon": "一", + "weekdayShortTue": "二", + "weekdayShortWed": "三", + "weekdayShortThu": "四", + "weekdayShortFri": "五", + "weekdayShortSat": "六", + "weekdayShortSun": "日", + "dayOfWeek": "每周特定一天", + "dayOfMonth": "每月特定一天", + "lastDay": "最後一天", + "lastDay1": "每月最後一天", + "maintenanceStatus-ended": "已結束", + "maintenanceStatus-unknown": "未知", + "Display Timezone": "顯示時區", + "Schedule Maintenance": "排程維護", + "Date and Time": "日期與時間", + "DateTime Range": "日期與時間範圍", + "plugin": "插件 | 插件", + "install": "安裝", + "installing": "正在安装", + "uninstalling": "正在解除安裝", + "confirmUninstallPlugin": "你確定要解除安裝?", + "dataRetentionTimeError": "保留限期必需為 0 或正數", + "infiniteRetention": "設定為 0 以作無限期保留。", + "Effective Date Range": "有效日期範圍 (可選)", + "Hello @everyone is...": "Hello {'@'}everyone is…", + "Packet Size": "Packet 大小", + "Event type:": "事件類型:", + "Event data:": "事件資料:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "然後選擇操作,例如切換至 RGB 燈為紅色的場景。", + "Frontend Version": "前端版本", + "Frontend Version do not match backend version!": "前端版本與後端版本不符!", + "lastDay2": "每月倒數第二天", + "lastDay3": "每月倒數第三天", + "lastDay4": "每月倒數第四天", + "No Maintenance": "無維護", + "pauseMaintenanceMsg": "您確定要暫停嗎?", + "maintenanceStatus-under-maintenance": "維護中", + "maintenanceStatus-inactive": "已停用", + "maintenanceStatus-scheduled": "已排程", + "Server Timezone": "伺服器時區", + "statusPageMaintenanceEndDate": "結束", + "IconUrl": "Icon 網址", + "dnsCacheDescription": "在某些情況 IPv6 可能會出現異常,如果您遇到任何問題,請停用。", + "Single Maintenance Window": "單一維護時段", + "Maintenance Time Window of a Day": "每日維護時段", + "Proxy": "Proxy", + "backupOutdatedWarning": "過時:由於備份功能未顧及新功能的增加,因此備份功能無法產生或復原完整的備份。", + "Optional": "可選填", + "markdownSupported": "支援 Markdown", + "Custom Monitor Type": "自訂監測器", + "Google Analytics ID": "Google Analytics ID", + "Learn More": "了解更多", + "Server Address": "Server 地址", + "Edit Tag": "編輯標籤", + "confirmDeleteTagMsg": "你確定你要刪除此標籤?相關的監測器不會被刪除。", + "pushoversounds pushover": "Pushover (預設)", + "pushoversounds tugboat": "Tug Boat", + "pushyToken": "裝置 Token", + "Proto Content": "Proto 內容", + "onebotHttpAddress": "OneBot HTTP 地址", + "HomeAssistant": "Home Assistant", + "Leave blank to use a shared sender number.": "留空以使用平台共享的發送人號碼。", + "auto acknowledged": "自動標記已讀", + "wayToGetPagerDutyKey": "您可以前往 Service -> Service Directory -> (Select a service) -> Integrations -> Add integration 以取得。您可以搜尋 \"Events API V2\"。詳細資訊 {0}", + "Kook": "Kook", + "wayToGetKookBotToken": "到 {0} 創建應用並取得 Bot Token", + "grpcMethodDescription": "方法名會轉換為小駝峰格式,例如 sayHello、check 等等。", + "deleteMaintenanceMsg": "您確定要刪除此維護嗎?", + "dnsPortDescription": "DNS 伺服器 port。預設為 53。您可以隨時變更 port。", + "atLeastOneMonitor": "選擇至少一個受影響的監測器", + "endpoint": "endpoint", + "octopushAPIKey": "在控制台的 HTTP API 憑證取得的 \"API Key\"", + "octopushLogin": "在控制台的 HTTP API 憑證取得的 \"Login\"", + "promosmsLogin": "API 登入名稱", + "promosmsPassword": "API 密碼", + "pushoversounds bike": "Bike", + "pushoversounds bugle": "Bugle", + "pushoversounds cashregister": "Cash Register", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Cosmic", + "pushoversounds falling": "Falling", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Incoming", + "pushoversounds intermission": "Intermission", + "pushoversounds magic": "Magic", + "pushoversounds mechanical": "Mechanical", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Siren", + "pushoversounds spacealarm": "Space Alarm", + "pushoversounds alien": "Alien Alarm (long)", + "pushoversounds climb": "Climb (long)", + "pushoversounds persistent": "Persistent (long)", + "pushoversounds echo": "Pushover Echo (long)", + "pushoversounds updown": "Up Down (long)", + "pushoversounds vibrate": "Vibrate Only", + "pushoversounds none": "None (silent)", + "pushyAPIKey": "Secret API Key", + "Guild ID": "Guild ID", + "Strategy": "策略", + "Free Mobile User Identifier": "Free Mobile User Identifier", + "Free Mobile API Key": "Free Mobile API Key", + "Enable TLS": "使用 TLS", + "Proto Service Name": "Proto 服務名稱", + "Proto Method": "Proto 方式", + "onebotGroupMessage": "群組", + "onebotMessageType": "OneBot 訊息類型", + "ntfy Topic": "ntfy Topic", + "Legacy Octopush-DM": "舊版 Octopush-DM", + "Octopush API Version": "Octopush API 版本", + "From Name/Number": "發送人名稱/號碼", + "Recipient Number": "收件人號碼", + "smseaglePriority": "訊息優先度 (0-9,預設 = 0)", + "smseagleEncoding": "以 Unicode 傳送", + "smseagleUrl": "您的 SMSEagle 裝置 URL", + "smseagleToken": "API 存取 Token", + "smseagleRecipient": "收件者 (以逗號分隔)", + "smseagleRecipientType": "收件者類型", + "smseagleContact": "聯絡人名稱", + "smseagleGroup": "群組名稱", + "smseagleTo": "電話號碼", + "smseagle": "SMSEagle", + "auto resolve": "自動解決", + "do nothing": "不進行任何操作", + "Auto resolve or acknowledged": "自動解決或標記已讀", + "Integration URL": "Integration URL", + "Integration Key": "Integration Key", + "wayToGetClickSendSMSToken": "您可以到 {0} 取得 API 使用者名稱和 API Key。", + "PushDeer Key": "PushDeer Key", + "onebotSafetyTips": "為了安全起見,必須設置存取 Token", + "onebotUserOrGroupId": "群組/使用者 ID", + "onebotPrivateMessage": "私人", + "notificationRegional": "地區性", + "RadiusCalledStationIdDescription": "Called Device 識別碼", + "telegramSendSilently": "靜音發送", + "telegramMessageThreadID": "(選填) Message Thread ID", + "RadiusCallingStationIdDescription": "Calling Device 識別碼", + "Body Encoding": "Body Encoding", + "API Keys": "API Keys", + "deleteAPIKeyMsg": "你確定要刪除此 API Key?", + "disableAPIKeyMsg": "你確定要停用此 API Key?", + "apiKey-inactive": "已停用", + "apiKey-active": "有效", + "No API Keys": "沒有 API Keys", + "Add API Key": "新增 API Key", + "Expiry date": "失效時間", + "Don't expire": "不會失效", + "apiKey-expired": "已失效", + "Expires": "失效時間", + "Key Added": "API Key 已產生", + "Add Another": "加另一個", + "Continue": "繼續", + "Generate": "產生", + "apiKeyAddedMsg": "你的 API Key 已被產生。此頁只會顯示一次,請適當保存。", + "Expiry": "過期", + "telegramSendSilentlyDescription": "選擇以靜音發送。用戶會收到無聲通知。", + "Clone Monitor": "複製監察器", + "Clone": "複製", + "cloneOf": "複製的 {0}", + "Proxy server has authentication": "Proxy 伺服器啟用了驗證功能", + "Proxy Server": "Proxy 伺服器", + "Proxy Protocol": "Proxy 通訊協定", + "Setup Proxy": "設置代理", + "Topic": "Topic", + "Retry": "重試", + "High": "高", + "Huawei": "華為", + "For safety, must use secret key": "為安全起見,必須使用 Secret Key", + "SecretKey": "SecretKey", + "WebHookUrl": "WebHookUrl", + "Bark Sound": "Bark 鈴聲", + "Bark Group": "Bark 群組", + "Bark Endpoint": "Bark Endpoint", + "Platform": "平台", + "Device Token": "裝置 Token", + "telegramProtectContent": "禁止轉發/儲存", + "telegramProtectContentDescription": "如果選擇,用戶將不能轉發/儲存收到的信息。", + "Add New Tag": "加新標籤", + "Economy": "經濟", + "Lowcost": "平價", + "high": "高價", + "statusPageRefreshIn": "將於 {0} 後重新整理", + "SendKey": "SendKey", + "SMSManager API Docs": "SMSManager API 文件 ", + "startDateTime": "開始時間", + "pagertreeLow": "低", + "endDateTime": "結束時間", + "cronExpression": "Cron 表達式", + "cronSchedule": "排程: ", + "invalidCronExpression": "無效 Cron 表達式:{0}", + "sameAsServerTimezone": "使用伺服器時區", + "WeCom Bot Key": "WeCom 機器人 Key", + "pagertreeMedium": "中", + "pagertreeHigh": "高", + "Cannot connect to the socket server": "無法連線到 Socket 伺服器", + "Reconnecting...": "重新連線...", + "chromeExecutable": "Chrome/Chromium 執行檔", + "chromeExecutableAutoDetect": "自動偵測", + "chromeExecutableDescription": "如果您使用 Docker 且未安裝 Chromium,可能要花數分鐘安裝後才能顯示測試結果。安裝會使用 1GB 的硬碟空間。", + "Edit Maintenance": "編輯維護", + "Invert Keyword": "以上關鍵字不能出現", + "Home": "首頁", + "Expected Value": "預期值", + "Json Query": "JSON 查询", + "Saved.": "已儲存。", + "Select": "選擇", + "selectedMonitorCount": "已選:{0}", + "Check/Uncheck": "選取中/取消選取", + "telegramMessageThreadIDDescription": "(可選) Telegram 在超級群組使用的話題標識字串", + "pushViewCode": "如何使用 Push 監控?(檢視程式碼)", + "pushOthers": "其他", + "webhookBodyCustomOption": "自定義內容", + "tailscalePingWarning": "如要使用 Tailscale Ping ,您需要以非 docker 方式安裝 Uptime Kuma,並在系統安裝 Tailscale 客戶端。", + "invertKeywordDescription": "出現關鍵詞將令檢測結果設為失敗,而非成功。", + "enableNSCD": "啟用 NSCD(名稱服務緩存)以緩存所有 DNS 請求", + "setupDatabaseChooseDatabase": "你想使用以下哪種資料庫?", + "setupDatabaseEmbeddedMariaDB": "你無需作任何設定。Docker image 中已包含設定好的 MariaDB。Uptime Kuma 會自動通過 Unix socket 連接到此資料庫。", + "setupDatabaseMariaDB": "連接到額外 MariaDB 資料庫。 你需要設置資料庫連接資訊。", + "setupDatabaseSQLite": "以一個檔案作為資料庫,建議用於小型的部署。在 v2.0.0 版本之前,Uptime Kuma 使用 SQLite 作為預設資料庫。", + "dbName": "資料庫名稱", + "webhookBodyPresetOption": "預設 - {0}", + "programmingLanguages": "編程語言", + "styleElapsedTime": "狀態條下顯示已過的時間", + "styleElapsedTimeShowNoLine": "顯示(不帶連接線)", + "styleElapsedTimeShowWithLine": "顯示(帶連接線)", + "Request Timeout": "請求超時", + "timeoutAfter": "{0} 秒後為超時", + "settingUpDatabaseMSG": "正在設定資料庫。可能需要一段時間,請耐心等待。", + "Host URL": "主機網址", + "locally configured mail transfer agent": "本機設定的郵件傳輸代理", + "now": "現在", + "time ago": "{0} 之前", + "ignoreTLSErrorGeneral": "忽略連接中的TLS/SSL錯誤", + "liquidIntroduction": "可透過 Liquid 模板語言實現模板化。請參考 {0} 的使用說明。這些是可用的變數:", + "Reset Token": "重設代幣", + "shrinkDatabaseDescriptionSqlite": "觸發 SQLite 資料庫 {vacuum}。{auto_vacuum} 已經啟用,但這不會像 {vacuum} 指令那樣整理資料庫或重新包裝個別資料庫頁面。", + "statusPageSpecialSlugDesc": "特殊標題 {0}:當未提供標題時,將顯示此頁面", + "Add a new expiry notification day": "新增到期通知日", + "DockerHostRequired": "請設定此監視器的 Docker 主機。", + "and": "與", + "smtpLiquidIntroduction": "以下兩個欄位可透過 Liquid 模板語言進行模板化。請參考 {0} 的使用說明。這些是可用的變數:", + "Select message type": "選擇訊息類型", + "Create new forum post": "建立新的討論區文章", + "whatHappensAtForumPost": "建立新的論壇文章。這不會在現有的文章中發佈訊息。要在現有文章中發佈訊息,請使用 \"{option}\" 。", + "Search monitored sites": "搜尋受監控的網站", + "templateMsg": "通知訊息", + "templateMonitorJSON": "描述監視器的物件", + "templateLimitedToUpDownCertNotifications": "僅適用於上線/下線/證書到期通知", + "templateLimitedToUpDownNotifications": "僅適用於 上/下線 通知", + "Remove the expiry notification": "移除到期通知日", + "Refresh Interval": "刷新時間間隔", + "Refresh Interval Description": "狀態頁面會每隔 {0} 秒刷新一次全站", + "emailCustomisableContent": "可客製化內容", + "leave blank for default subject": "留空以使用默認主題", + "emailTemplateServiceName": "服務名", + "emailTemplateHostnameOrURL": "主機名稱或 URL", + "emailTemplateStatus": "狀態", + "emailTemplateMonitorJSON": "監視器物件的描述", + "emailTemplateMsg": "通知訊息內容", + "Send to channel": "傳送至頻道", + "postToExistingThread": "張貼到現有的主題/論壇文章", + "forumPostName": "論壇文章名稱", + "threadForumPostID": "主題 / 論壇文章 ID", + "wayToGetDiscordThreadId": "取得主題 / 論壇文章 id 與取得頻道 id 相似。閱讀更多如何取得id {0}", + "Channel access token (Long-lived)": "通道存取標記 (長效)", + "successKeywordExplanation": "將被視為成功的 MQTT 關鍵字", + "noDockerHostMsg": "無法使用。 請先設定 Docker 主機 。", + "ignoredTLSError": "已忽略 TLS/SSL 錯誤", + "-year": "-年", + "Json Query Expression": "Json 查詢表達式", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "請輸入您要連線的伺服器主機名稱 若要使用 {local_mta} 則輸入 {localhost}", + "RabbitMQ Nodes": "RabbitMQ 管理節點", + "jsonQueryDescription": "使用 JSON 查詢解析並提取伺服器 JSON 響應中的特定數據,或者,如果不期望得到 JSON 響應,則可使用 \"$\" 獲取原始響應。然後將結果轉為字符串並與期望值進行字符串比較。有關更多文檔,請參閱 {0},亦可使用 {1} 來嘗試查詢。", + "wayToGetKookGuildID": "在 Kook 設置中打開“開發者模式”,然後右鍵點擊頻道可獲取其 ID", + "Gateway Type": "網關類型", + "You can divide numbers with": "可用的數字分隔符包括", + "Base URL": "API 基礎地址", + "goAlertInfo": "GoAlert 是一個用於呼叫調度、自動匯報和通知(如 SMS 或語音呼叫)的開源應用程式。在正確的時間以正確的方式自動讓正確的人參與!{0}", + "goAlertIntegrationKeyInfo": "使用形如 aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee 的通用 API 集成密鑰,通常是複製來的鏈接中的 token 參數值。", + "AccessKeyId": "AccessKey ID", + "SecretAccessKey": "AccessKey 密碼", + "PhoneNumbers": "電話號碼", + "TemplateCode": "TemplateCode", + "SignName": "SignName", + "Sms template must contain parameters: ": "短訊模板必須包含以下變量: ", + "Bark API Version": "Bark API 版本", + "Mentioning": "是否提及成員", + "Mention group": "提及 {group}", + "aboutSlackUsername": "更改消息發件人的顯示名稱。如果您想提及某人,請另行將其包含在友好名稱中。", + "smspartnerApiurl": "你可以在此處找到你的 API 密鑰:{0}", + "smspartnerPhoneNumber": "手機號碼", + "smspartnerPhoneNumberHelptext": "號碼必須使用國際通用格式,例如 {0}、{1}。多個號碼必須使用 {2} 分隔", + "smspartnerSenderName": "SMS 發件人名稱", + "smspartnerSenderNameInfo": "不能使用特殊字符,字符數在 3 到 11 個之間", + "Server URL should not contain the nfty topic": "伺服器地址不應包含 ntfy主題", + "PushDeer Server": "PushDeer 伺服器", + "pushDeerServerDescription": "留空則使用官方伺服器", + "pagertreeIntegrationUrl": "集成 URL 地址", + "pagertreeUrgency": "緊急程度", + "pagertreeSilent": "靜默", + "pagertreeCritical": "嚴重", + "pagertreeResolve": "自動解除", + "pagertreeDoNothing": "甚麼都不做", + "wayToGetPagerTreeIntegrationURL": "在 PagerTree 中創建 Uptime Kuma 集成後,複製端點 URL 到此處。在 {0} 查看詳情", + "lunaseaTarget": "目標", + "lunaseaDeviceID": "設備 ID", + "lunaseaUserID": "用戶 ID", + "ntfyAuthenticationMethod": "鑒權方式", + "ntfyPriorityHelptextAllEvents": "所有事件將使用最高優先級", + "ntfyPriorityHelptextAllExceptDown": "除了 {0} 類事件使用 {1} 優先級外,其他所有事件均使用該優先級", + "ntfyUsernameAndPassword": "用戶名和密碼", + "twilioAccountSID": "賬戶 SID", + "twilioApiKey": "API Key(可選)", + "twilioAuthToken": "鑒權 Token / API Key Secret", + "twilioFromNumber": "發信號碼", + "twilioToNumber": "收信號碼", + "Monitor Setting": "{0} 監控項設置", + "Show Clickable Link": "顯示可點擊的監控項鍊接", + "Show Clickable Link Description": "勾選後所有能訪問本狀態頁的訪客均可查看該監控項網址。", + "Open Badge Generator": "打開徽章生成器", + "Badge Generator": "{0} 徽章生成器", + "Badge Type": "徽章類型", + "Badge Duration (in hours)": "徽章時間範圍(以小時為單位)", + "Badge Label": "徽章標籤", + "Badge Prefix": "徽章內容前綴", + "Badge Suffix": "徽章內容後綴", + "Badge Label Color": "徽章標籤顏色", + "Badge Color": "徽章內容顏色", + "Badge Label Prefix": "徽章標籤前綴", + "Badge Preview": "徽章預覽", + "Badge Label Suffix": "徽章標籤後綴", + "Badge Up Color": "正常狀態下徽章顏色", + "Badge Down Color": "故障狀態下徽章顏色", + "Badge Pending Color": "重試中狀態下徽章顏色", + "Badge Maintenance Color": "維護狀態下徽章顏色", + "Badge Warn Color": "警告狀態下徽章顏色", + "Badge Warn Days": "徽章預警天數", + "Badge Down Days": "故障狀態所需剩餘天數", + "Badge Style": "徽章樣式", + "Badge value (For Testing only.)": "徽章內容(僅供測試)", + "Badge URL": "徽章網址", + "Group": "分組", + "Monitor Group": "監控項組", + "monitorToastMessagesLabel": "監控項的彈窗通知", + "monitorToastMessagesDescription": "監控項的彈窗通知的自動關閉用時,以秒為單位。設置為 -1 將禁用彈窗通知的自動關閉功能,設置為 0 將完全禁用彈窗通知功能。", + "toastErrorTimeout": "失敗類彈窗通知的自動關閉用時", + "Enter the list of brokers": "輸入緩存代理(broker)列表", + "Press Enter to add broker": "按回車鍵添加緩存代理(broker)", + "authInvalidToken": "無效的令牌。", + "Enable Kafka SSL": "啟用 Kafka SSL 功能", + "Enable Kafka Producer Auto Topic Creation": "啟用 Kafka 生成者(Producer)自動創建主題(Topic)功能", + "Kafka SASL Options": "Kafka SASL 選項", + "Pick a SASL Mechanism...": "選擇一種 SASL 鑒權方式……", + "Authorization Identity": "授權實體(Authorization Identity)", + "Secret AccessKey": "訪問密鑰(Secret AccessKey)", + "Session Token": "會話令牌(Session Token)", + "noGroupMonitorMsg": "暫無可用,請先創建一個監控項組。", + "Close": "關閉", + "Request Body": "請求體", + "wayToGetFlashDutyKey": "您可以進入 協作空間 -> (選擇一個 協作空間) -> 集成數據 -> 新增一個集成 頁面,添加“Uptime Kuma”集成獲得一個推送地址,複製地址中的 Integration Key,更多資訊前往{0}", + "FlashDuty Severity": "嚴重程度", + "nostrRelays": "Nostr relay 服務", + "nostrRelaysHelp": "Relay 服務地址,每行一個", + "nostrSender": "發送者私鑰(nsec 格式)", + "nostrRecipients": "接收者公鑰(npub 格式)", + "nostrRecipientsHelp": "npub 格式,每行一個", + "showCertificateExpiry": "顯示證書有效期", + "noOrBadCertificate": "無證書或證書錯誤", + "cacheBusterParam": "添加參數 {0}", + "cacheBusterParamDescription": "隨機生成一個參數以繞過緩存。", + "gamedigGuessPort": "Gamedig: 自動檢測端口號", + "gamedigGuessPortDescription": "Valve 伺服器查詢協議使用的端口可能與客戶端端口不同。如果監控器無法連接到伺服器,請嘗試此方法。", + "Message format": "消息格式", + "Send rich messages": "發送富文本消息", + "Bitrix24 Webhook URL": "Bitrix24 Webhook 地址", + "wayToGetBitrix24Webhook": "你可以按以下步驟創建一個 webhook:{0}", + "bitrix24SupportUserID": "輸入你在 Bitrix24 的用戶 ID。你可以在你的用戶個人資料頁找到你的用戶 ID。", + "authUserInactiveOrDeleted": "該用戶被禁用或刪除。", + "authIncorrectCreds": "錯誤的用戶名或密碼。", + "2faAlreadyEnabled": "2FA 已經啟用。", + "2faEnabled": "已成功啟用 2FA。", + "2faDisabled": "已成功禁用 2FA。", + "successAdded": "已成功添加。", + "successResumed": "已成功恢復。", + "successPaused": "已成功暫停。", + "successDeleted": "已成功刪除。", + "successEdited": "已成功編輯。", + "successAuthChangePassword": "已成功更新密碼。", + "successBackupRestored": "已成功恢復備份。", + "successDisabled": "已成功禁用。", + "tagNotFound": "標籤未找到。", + "foundChromiumVersion": "已找到 Chromium/Chrome。版本:{0}", + "Remote Browsers": "遠程瀏覽器", + "Remote Browser": "遠程瀏覽器", + "Add a Remote Browser": "添加一個遠程瀏覽器", + "Remote Browser not found!": "未找到遠程瀏覽器!", + "remoteBrowsersDescription": "遠程瀏覽器可用以代替本地 Chromium 瀏覽器。您可使用類似於 browserless.io 的服務,或者自行運行一個類似服務", + "self-hosted container": "自託管容器", + "remoteBrowserToggle": "默認情況下 Chromium 運行於 Uptime Kuma 所在容器內。您可以通過切換此開關來使用遠程瀏覽器。", + "useRemoteBrowser": "使用遠程瀏覽器", + "deleteRemoteBrowserMessage": "您確定要刪除此遠程瀏覽器嗎,這會影響所有監控項?", + "GrafanaOncallUrl": "Grafana Oncall 服務 URL", + "Browser Screenshot": "瀏覽器截圖", + "Command": "命令", + "mongodbCommandDescription": "對資料庫運行 MongoDB 命令。有關可用命令的資訊,請查閱 {documentation}", + "wayToGetSevenIOApiKey": "訪問 app.seven.io > 開發人員 > api 密鑰 > 綠色添加按鈕下的儀錶板", + "senderSevenIO": "發信人號碼或名稱", + "receiverSevenIO": "收信人號碼", + "receiverInfoSevenIO": "如果接收號碼不在德國,您必須在號碼前面添加國家代碼(例如,對於來自美國的國家代碼 1,請使用 117612121212 而不是 017612121212)", + "apiKeySevenIO": "SevenIO API 密鑰", + "wayToWriteWhapiRecipient": "可用格式為不含 + 號的國際通用格式手機號碼({0})、聯繫人 ID({1})或組 ID({2})。", + "wayToGetWhapiUrlAndToken": "您可以通過進入您想要的頻道來獲取 API URL 和令牌:{0}", + "whapiRecipient": "手機號碼 / 聯繫人 ID / 組 ID", + "API URL": "API 地址", + "What is a Remote Browser?": "甚麼是遠程瀏覽器?", + "wayToGetHeiiOnCallDetails": "如需了解如何獲取 Trigger ID 和 API 密鑰,請訪問 {documentation}", + "documentationOf": "{0} 文檔", + "callMeBotGet": "您可以在此處填寫您生成的用於 {0}、{1} 或 {2} 的端點。 請注意您可能會受到速率限制。 速率限制被推測為:{3}(僅供參考)", + "gtxMessagingApiKeyHint": "你可以在此找到你的 API 密鑰:My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "發件人電話號碼 / 傳輸路徑起始地址(TPOA)", + "gtxMessagingFromHint": "在手機上,收件人會看到 TPOA 地址作為消息的發送者。TPOA 允許的格式包括:至多11個字母或數字、短代碼、當地長代碼或國際號碼({e164}、{e212} 或 {e214} 格式)", + "To Phone Number": "收件人電話號碼", + "gtxMessagingToHint": "國際通用格式,需要前導 \"+\" ({e164}、{e212} 或 {e214} 格式)", + "Originator type": "發件人類型", + "Alphanumeric (recommended)": "字符或數字類型(推薦)", + "Telephone number": "手機號碼", + "cellsyntOriginatortypeAlphanumeric": "字符或數字類型(最多 11 個字母或數字)。收件人無法向此號碼回覆消息。", + "cellsyntOriginatortypeNumeric": "數字類型(最多 15 位數)需使用國際通用格式,不以 00+國家代碼開頭,例如若要使用英國的號碼 07920 110 000 需填寫 447920110000。收件人可向此號碼回覆消息。", + "Originator": "發件人", + "Destination": "收件人", + "Allow Long SMS": "允許長消息", + "cellsyntSplitLongMessages": "長消息會被切分為至多 6 段,每段至多 153 個字符,總共至多 918 個字符。", + "max 15 digits": "最多 15 位數字", + "max 11 alphanumeric characters": "最多 11 個字母或數字", + "Community String": "SNMP 通訊字符串", + "snmpCommunityStringHelptext": "此字符串用作密碼,以驗證和控制對SNMP啟用設備的訪問。請將其與您的SNMP設備配置匹配。", + "OID (Object Identifier)": "OID(對象標識符)", + "snmpOIDHelptext": "輸入您想監控的傳感器或狀態的 OID。如果您不確定 OID 是甚麼,可以使用 MIB 瀏覽器或 SNMP 軟件等網絡管理工具進行查找。", + "Condition": "條件", + "SNMP Version": "SNMP 版本", + "Please enter a valid OID.": "請輸入一個合法的 OID。", + "wayToGetThreemaGateway": "你可以在 {0} 註冊 Threema 網關。", + "threemaRecipientType": "收信人類型", + "threemaRecipientTypeIdentity": "Threema ID", + "threemaRecipientTypePhone": "電話號碼", + "threemaRecipientTypePhoneFormat": "E.164 標準,不含前導 + 號", + "threemaRecipientTypeEmail": "郵件地址", + "threemaSenderIdentity": "網關 ID", + "threemaSenderIdentityFormat": "8 位字符,通常以 * 開頭", + "threemaApiAuthenticationSecret": "網關密鑰", + "threemaBasicModeInfo": "註:此通知類型所使用的 Threema 網關為基礎模式(伺服器端加密)。更多細節參見 {0}。", + "apiKeysDisabledMsg": "由於登錄驗證被禁用,API 密鑰也被禁用。", + "Host Onesender": "Onesender 伺服器", + "Token Onesender": "Onesender 令牌", + "Recipient Type": "收件人類型", + "Private Number": "私密號碼", + "privateOnesenderDesc": "請確保電話號碼有效。要向私人電話號碼發送消息,格式形如:628123456789", + "groupOnesenderDesc": "請確保分組 ID 有效。要向分組發送消息,格式形如:628123456789-342345", + "Group ID": "分組 ID", + "wayToGetOnesenderUrlandToken": "你可以在 Onesender 網站獲取地址和令牌。更多資訊參見 {0}", + "Add Remote Browser": "添加遠程瀏覽器", + "New Group": "新分組", + "Group Name": "分組名稱", + "OAuth2: Client Credentials": "OAuth2:客戶端憑據", + "Authentication Method": "鑒權方式", + "Authorization Header": "鑒權請求頭", + "Form Data Body": "表單數據請求體", + "OAuth Token URL": "OAuth 令牌地址", + "Client ID": "客戶端 ID", + "Client Secret": "客戶端祕鑰", + "OAuth Scope": "OAuth 範圍", + "Optional: Space separated list of scopes": "可選項:用空格分隔的範圍列表", + "Go back to home page.": "返回到首頁。", + "No tags found.": "未找到標籤。", + "Lost connection to the socket server.": "與 socket 伺服器的連接丟失。", + "Cannot connect to the socket server.": "無法連接到 socket 伺服器。", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhook 地址", + "signl4Docs": "你可以在此找到更多關於如何配置 SIGNL4 以及如何獲取 SIGNL4 Webhook 地址的資訊:{0}。", + "conditionAdd": "添加條件", + "conditionDelete": "刪除條件", + "conditionAddGroup": "添加分組", + "conditionDeleteGroup": "刪除分組", + "conditionValuePlaceholder": "值", + "equals": "相等", + "not equals": "不相等", + "contains": "包含", + "not contains": "不包含", + "starts with": "以此開頭", + "not starts with": "不以此開頭", + "ends with": "以此結尾", + "not ends with": "不以此結尾", + "less than": "少於", + "greater than": "多於", + "less than or equal to": "不多於", + "greater than or equal to": "不少於", + "record": "記錄", + "Notification Channel": "通知頻道", + "Sound": "聲音", + "Alphanumerical string and hyphens only": "僅限字母、數字和連字符(-)", + "Arcade": "Arcade(拱廊)", + "Correct": "Correct(成功音)", + "Fail": "Fail(失敗音)", + "Harp": "Harp(豎琴)", + "Reveal": "Reveal(揭示)", + "Bubble": "Bubble(氣泡)", + "Doorbell": "Doorbell(門鈴)", + "Flute": "Flute(長笛)", + "Money": "Money(錢)", + "Scifi": "Scifi(科幻)", + "Clear": "Clear(清除聲)", + "Elevator": "Elevator(電梯)", + "Guitar": "Guitar(結他)", + "Pop": "Pop(流行音樂)", + "Custom sound to override default notification sound": "自定義聲音,用以覆蓋默認通知聲音", + "Time Sensitive (iOS Only)": "即時通知(僅 iOS 可用)", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "即使設備處於專注模式,即時通知也會立即發送。", + "From": "發件人", + "Can be found on:": "可在此找到:{0}", + "The phone number of the recipient in E.164 format.": "收件人的 E.164 格式電話號碼。", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "如需可被回復,請輸入發送者 ID 或 E.164 格式的手機號碼。", + "rabbitmqNodesDescription": "輸入 RabbitMQ 管理節點的 URL,包括協議和端口。例如:{0}", + "rabbitmqNodesRequired": "請設置此監視項的節點。", + "rabbitmqNodesInvalid": "請使用 RabbitMQ 節點的完整 URL(即完全限定 URL,以 http 開頭)。", + "RabbitMQ Username": "RabbitMQ 用戶名", + "RabbitMQ Password": "RabbitMQ 密碼", + "rabbitmqHelpText": "要使用此監控項,您需要在 RabbitMQ 設置中啟用管理插件。有關更多資訊,請參閱 {rabitmq_documentation}。", + "SendGrid API Key": "SendGrid API 密鑰", + "Separate multiple email addresses with commas": "用逗號分隔多個電子郵件地址", + "templateHeartbeatJSON": "描述心跳資訊的對象", + "successKeyword": "“成功”關鍵詞", + "emailCustomBody": "自定義正文", + "leave blank for default body": "留空以使用默認正文", + "emailTemplateHeartbeatJSON": "描述心跳資訊的對象", + "emailTemplateLimitedToUpDownNotification": "僅適用於“正常”、“故障”類心跳,否則為空", + "e.g. {discordThreadID}": "例如 {discordThreadID}", + "Your User ID": "你的用戶 ID", + "promosmsAllowLongSMS": "允許長的短訊", + "Notify Channel": "通知該頻道", + "aboutNotifyChannel": "勾選“通知該頻道”,會令該頻道內所有成員都收到一條桌面端或移動端通知,無論其狀態是在線或離開。", + "setup a new monitor group": "創建一個新的監控項組", + "openModalTo": "打開一個新窗口以{0}", + "Add a domain": "添加一個域名", + "Remove domain": "移除域名 {0}", + "successEnabled": "已成功啟用。", + "Conditions": "條件", + "Kafka Topic Name": "Kafka 主題名稱", + "Kafka Producer Message": "Kafka 生成者(Producer)消息", + "Mechanism": "鑒權方式", + "toastSuccessTimeout": "成功類彈窗通知的自動關閉用時", + "pushoverMessageTtl": "消息存活時間(秒)", + "Don't mention people": "不提及任何人", + "Kafka Brokers": "Kafka 緩存代理(Broker)", + "AccessKey Id": "密鑰 ID(AccessKey Id)", + "cellsyntOriginator": "在收件人處作為消息發送者顯示。允許的內容取決於發件人類型。", + "cellsyntDestination": "收件人的手機號碼需要使用以 00+國家代碼開頭的國際通用格式,例如若要發給英國的號碼 07920 110 000 需使用 00447920110000 作為收件人手機號碼(至多17位數)。需發送給多個收件人手機號碼時可使用英文逗號分隔,每次請求最 多250 00個收件人手機號碼。", + "threemaRecipient": "收件人", + "threemaRecipientTypeIdentityFormat": "8 位字符" +} diff --git a/src/lang/zh-TW.json b/src/lang/zh-TW.json new file mode 100644 index 0000000..9a54283 --- /dev/null +++ b/src/lang/zh-TW.json @@ -0,0 +1,1266 @@ +{ + "languageName": "繁體中文 (臺灣)", + "checkEverySecond": "每 {0} 秒檢查一次", + "retryCheckEverySecond": "每 {0} 秒重試一次", + "resendEveryXTimes": "每 {0} 次便重新傳送", + "resendDisabled": "重新傳送已停用", + "retriesDescription": "在服務被標記為離線並傳送通知前的最大重試次數", + "ignoreTLSError": "忽略 HTTPS 網站的 TLS/SSL 錯誤", + "upsideDownModeDescription": "反轉顯示狀態。若服務可以連線,將顯示離線。", + "maxRedirectDescription": "最大重新導向跟隨次數。設為 0 將停用重新導向。", + "enableGRPCTls": "允許以 TLS 連線傳送 gRPC 請求", + "grpcMethodDescription": "方法名稱將轉換至駝峰式命名,如 sayHello、check 等。", + "acceptedStatusCodesDescription": "選擇視為成功回應的狀態碼。", + "Maintenance": "維護", + "statusMaintenance": "維護", + "Schedule maintenance": "排程維護", + "Affected Monitors": "受影響的監測器", + "Pick Affected Monitors...": "挑選受影響的監測器…", + "Start of maintenance": "維護起始", + "All Status Pages": "所有狀態頁", + "Select status pages...": "選擇狀態頁…", + "recurringIntervalMessage": "每日執行 | 每 {0} 天執行", + "affectedMonitorsDescription": "選擇受目前維護影響的監測器", + "affectedStatusPages": "在已選取的狀態頁中顯示此維護訊息", + "atLeastOneMonitor": "至少選擇一個受影響的監測器", + "passwordNotMatchMsg": "密碼不相符。", + "notificationDescription": "必須將通知指派給監測器才能運作。", + "keywordDescription": "HTML 或 JSON 回應的搜尋關鍵字。區分大小寫。", + "pauseDashboardHome": "暫停", + "deleteMonitorMsg": "您確定要刪除此監測器嗎?", + "deleteMaintenanceMsg": "您確定要刪除此維護嗎?", + "deleteNotificationMsg": "您確定要為所有監測器刪除此通知嗎?", + "dnsPortDescription": "DNS 伺服器連接埠。預設為 53。您可以隨時變更連接埠。", + "resolverserverDescription": "Cloudflare 為預設伺服器。您可以隨時更換解析伺服器。", + "rrtypeDescription": "選擇您想要監測的資源記錄類型", + "pauseMonitorMsg": "您確定要暫停嗎?", + "enableDefaultNotificationDescription": "預設情況下,新監測器將啟用此通知。您仍可分別停用各監測器的通知。", + "clearEventsMsg": "您確定要刪除此監測器的所有事件嗎?", + "clearHeartbeatsMsg": "您確定要刪除此監測器的所有心跳嗎?", + "confirmClearStatisticsMsg": "您確定要刪除所有統計資料嗎?", + "importHandleDescription": "若您想跳過所有相同名稱的監測器或通知,請選擇「略過現有」。選擇「覆寫」將刪除所有現有的監測器及通知。", + "confirmImportMsg": "您確定要匯入備份嗎?請確認是否選擇正確的匯入設定。", + "twoFAVerifyLabel": "請輸入權杖以驗證雙步驟驗證:", + "tokenValidSettingsMsg": "權杖有效!您可以儲存雙步驟驗證設定了。", + "confirmEnableTwoFAMsg": "您確定要啟用雙步驟驗證嗎?", + "confirmDisableTwoFAMsg": "您確定要停用雙步驟驗證嗎?", + "Settings": "設定", + "Dashboard": "儀錶板", + "New Update": "新版本", + "Language": "語言", + "Appearance": "外觀", + "Theme": "主題", + "General": "一般", + "Primary Base URL": "主要基底網址", + "Version": "版本", + "Check Update On GitHub": "在 GitHub 檢查更新", + "List": "清單", + "Add": "新增", + "Add New Monitor": "新增監測器", + "Quick Stats": "狀態概覽", + "Up": "正常", + "Down": "離線", + "Pending": "等待中", + "Unknown": "未知", + "Pause": "暫停", + "Name": "名稱", + "Status": "狀態", + "DateTime": "日期時間", + "Message": "訊息", + "No important events": "無重要事件", + "Resume": "繼續", + "Edit": "編輯", + "Delete": "刪除", + "Current": "目前", + "Uptime": "運作率", + "Cert Exp.": "憑證期限", + "day": "天", + "-day": "天", + "hour": "小時", + "-hour": "小時", + "Response": "回應", + "Ping": "Ping", + "Monitor Type": "監測器類型", + "Keyword": "關鍵字", + "Friendly Name": "易記名稱", + "URL": "網址", + "Hostname": "主機名稱", + "Port": "連接埠", + "Heartbeat Interval": "心跳間隔", + "Retries": "重試次數", + "Heartbeat Retry Interval": "心跳重試間隔", + "Resend Notification if Down X times consecutively": "若 X 次心跳皆離線,重新傳送通知", + "Advanced": "進階", + "Upside Down Mode": "顛倒模式", + "Max. Redirects": "最大重新導向次數", + "Accepted Status Codes": "可接受的狀態碼", + "Push URL": "推送網址", + "needPushEvery": "您應每 {0} 秒呼叫此網址。", + "pushOptionalParams": "選填參數:{0}", + "Save": "儲存", + "Notifications": "通知", + "Not available, please setup.": "無法使用,請先設定。", + "Setup Notification": "設定通知", + "Light": "亮色", + "Dark": "深色", + "Auto": "自動", + "Theme - Heartbeat Bar": "主題 - 心跳條", + "Normal": "正常", + "Bottom": "下方", + "None": "無", + "Timezone": "時區", + "Search Engine Visibility": "搜尋引擎可見度", + "Allow indexing": "允許索引", + "Discourage search engines from indexing site": "不建議搜尋引擎索引網頁", + "Change Password": "修改密碼", + "Current Password": "目前密碼", + "New Password": "新密碼", + "Repeat New Password": "確認新密碼", + "Update Password": "更新密碼", + "Disable Auth": "停用驗證", + "Enable Auth": "啟用驗證", + "disableauth.message1": "你是否要{disableAuth}?", + "disable authentication": "取消登入驗證", + "disableauth.message2": "此功能是設計給已有{intendThirdPartyAuth}的使用者,例如 Cloudflare Access。", + "where you intend to implement third-party authentication": "第三方認證", + "Please use this option carefully!": "請謹慎使用!", + "Logout": "登出", + "Leave": "離開", + "I understand, please disable": "我瞭解了,請停用", + "Confirm": "確認", + "Yes": "是", + "No": "否", + "Username": "使用者名稱", + "Password": "密碼", + "Remember me": "記住我", + "Login": "登入", + "No Monitors, please": "沒有監測器,請", + "add one": "新增", + "Notification Type": "通知類型", + "Email": "電子郵件", + "Test": "測試", + "Certificate Info": "憑證資訊", + "Resolver Server": "解析伺服器", + "Resource Record Type": "資源記錄類型", + "Last Result": "最後結果", + "Create your admin account": "建立您的管理員帳號", + "Repeat Password": "確認密碼", + "Import Backup": "匯入備份", + "Export Backup": "匯出備份", + "Export": "匯出", + "Import": "匯入", + "respTime": "回應時間 (毫秒)", + "notAvailableShort": "N/A", + "Default enabled": "啟用預設", + "Apply on all existing monitors": "套用到目前所有的監測器", + "Create": "建立", + "Clear Data": "清除資料", + "Events": "活動", + "Heartbeats": "心跳", + "Auto Get": "自動取得", + "backupDescription": "您可以將所有監測器及通知備份成一個 JSON 檔案。", + "backupDescription2": "提醒:不包含歷史紀錄及活動紀錄。", + "backupDescription3": "如通知權杖等機密資料也會一同匯出。請妥善儲存。", + "alertNoFile": "請選擇要匯入的檔案。", + "alertWrongFileType": "請選擇 JSON 檔案。", + "Clear all statistics": "清除所有統計資料", + "Skip existing": "略過現有", + "Overwrite": "覆寫", + "Options": "選項", + "Keep both": "保留兩者", + "Verify Token": "認證權杖", + "Setup 2FA": "設定雙步驟驗證", + "Enable 2FA": "啟用雙步驟驗證", + "Disable 2FA": "停用雙步驟驗證", + "2FA Settings": "雙步驟驗證設定", + "Two Factor Authentication": "雙步驟驗證", + "Active": "啟用", + "Inactive": "停用", + "Token": "權杖", + "Show URI": "顯示 URI", + "Tags": "標籤", + "Add New below or Select...": "在下方新增或選取…", + "Tag with this name already exist.": "已存在相同名稱的標籤。", + "Tag with this value already exist.": "已存在相同數值的標籤。", + "color": "顏色", + "value (optional)": "數值 (選填)", + "Gray": "灰色", + "Red": "紅色", + "Orange": "橘色", + "Green": "綠色", + "Blue": "藍色", + "Indigo": "靛色", + "Purple": "紫色", + "Pink": "粉色", + "Search...": "搜尋…", + "Avg. Ping": "平均 Ping", + "Avg. Response": "平均回應", + "Entry Page": "入口頁面", + "statusPageNothing": "空空如也,請新增群組或監測器。", + "No Services": "無服務", + "All Systems Operational": "所有系統正常運作", + "Partially Degraded Service": "部分服務效能降低", + "Degraded Service": "服務效能降低", + "Add Group": "新增群組", + "Add a monitor": "加入監測器", + "Edit Status Page": "編輯狀態頁", + "Go to Dashboard": "前往儀錶板", + "Status Page": "狀態頁", + "Status Pages": "狀態頁", + "defaultNotificationName": "我的 {notification} 通知 ({number})", + "here": "此處", + "Required": "必填", + "telegram": "Telegram", + "Bot Token": "機器人權杖", + "wayToGetTelegramToken": "您可以從 {0} 取得權杖。", + "Chat ID": "聊天 ID", + "supportTelegramChatID": "支援 對話/群組/頻道的聊天 ID", + "wayToGetTelegramChatID": "傳送訊息給機器人,並前往以下網址以取得您的 chat ID:", + "YOUR BOT TOKEN HERE": "在此填入您的機器人權杖", + "chatIDNotFound": "找不到 Chat ID;請先傳送訊息給機器人", + "webhook": "Webhook", + "Post URL": "Post 網址", + "Content Type": "內容類型", + "webhookJsonDesc": "{0} 適合任何現代的 HTTP 伺服器,如 Express.js", + "webhookFormDataDesc": "{multipart} 適合 PHP。 JSON 必須先經由 {decodeFunction} 剖析", + "webhookAdditionalHeadersTitle": "額外標頭", + "webhookAdditionalHeadersDesc": "設定 webhook 請求的額外標頭。每個標頭應由一對 JSON 鍵值對定義。", + "smtp": "Email (SMTP)", + "secureOptionNone": "無 / STARTTLS (25, 587)", + "secureOptionTLS": "TLS (465)", + "Ignore TLS Error": "忽略 TLS 錯誤", + "From Email": "寄件人", + "emailCustomSubject": "自訂主旨", + "To Email": "收件者", + "smtpCC": "副本", + "smtpBCC": "密件副本", + "discord": "Discord", + "Discord Webhook URL": "Discord Webhook 網址", + "wayToGetDiscordURL": "您可以前往伺服器設定 (Server Settings) -> 整合 (Integrations) -> 檢視 Webhooks (View Webhooks) -> 新 Webhook (New Webhook) 以取得新的 Webhook", + "Bot Display Name": "機器人顯示名稱", + "Prefix Custom Message": "前綴自訂訊息", + "Hello @everyone is...": "哈囉 {'@'} 每個人都是…", + "teams": "Microsoft Teams", + "Webhook URL": "Webhook 網址", + "wayToGetTeamsURL": "您可以前往此頁面以瞭解如何建立 Webhook 網址 {0}。", + "signal": "Signal", + "Number": "號碼", + "Recipients": "收件者", + "needSignalAPI": "您需要有 REST API 的 Signal 客戶端。", + "wayToCheckSignalURL": "您可以前往下列網址以瞭解如何設定:", + "signalImportant": "注意: 不得混合收件者的群組和號碼!", + "gotify": "Gotify", + "Application Token": "應用程式權杖", + "Server URL": "伺服器網址", + "Priority": "優先度", + "slack": "Slack", + "Icon Emoji": "Emoji 圖示", + "Channel Name": "頻道名稱", + "Uptime Kuma URL": "Uptime Kuma 網址", + "aboutWebhooks": "更多關於 Webhook 的資訊: {0}", + "aboutChannelName": "如果您不想使用 Webhook 頻道,請在 {0} 頻道名稱欄位填入您想使用的頻道。例如: #其他頻道", + "aboutKumaURL": "如果您未填入 Uptime Kuma 網址。將預設使用專案 Github 頁面。", + "emojiCheatSheet": "Emoji 一覽表: {0}", + "rocket.chat": "Rocket.Chat", + "pushover": "Pushover", + "pushy": "Pushy", + "PushByTechulus": "Push by Techulus", + "octopush": "Octopush", + "promosms": "PromoSMS", + "clicksendsms": "ClickSend SMS", + "lunasea": "LunaSea", + "apprise": "Apprise (支援 50 種以上的通知服務)", + "GoogleChat": "Google Chat (僅限 Google Workspace)", + "pushbullet": "Pushbullet", + "line": "Line Messenger", + "mattermost": "Mattermost", + "User Key": "使用者金鑰", + "Device": "裝置", + "Message Title": "訊息標題", + "Notification Sound": "通知音效", + "More info on:": "更多資訊: {0}", + "pushoverDesc1": "緊急優先度 (2) 的重試間隔為 30 秒並且會在 1 小時後過期。", + "pushoverDesc2": "如果您想要傳送通知到不同裝置,請填寫裝置欄位。", + "SMS Type": "簡訊類型", + "octopushTypePremium": "Premium (快速 - 建議用於警報)", + "octopushTypeLowCost": "Low Cost (緩慢 - 有時會被營運商阻擋)", + "checkPrice": "查看 {0} 價格:", + "apiCredentials": "API 認證", + "octopushLegacyHint": "您使用的是舊版的 Octopush (2011-2020) 還是新版?", + "Check octopush prices": "查看 octopush 價格 {0}。", + "octopushPhoneNumber": "電話號碼 (intl 格式,例如:+33612345678) ", + "octopushSMSSender": "簡訊寄件人名稱:3-11位英數字元及空白 (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea 裝置 ID", + "Apprise URL": "Apprise 網址", + "Example:": "範例:{0}", + "Read more:": "深入瞭解:{0}", + "Status:": "狀態:{0}", + "Read more": "深入瞭解", + "appriseInstalled": "已安裝 Apprise。", + "appriseNotInstalled": "尚未安裝 Apprise。{0}", + "Access Token": "存取權杖", + "Channel access token": "頻道存取權杖", + "Line Developers Console": "Line 開發者控制台", + "lineDevConsoleTo": "Line 開發者控制台 - {0}", + "Basic Settings": "基本設定", + "User ID": "使用者 ID", + "Messaging API": "即時通訊 API", + "wayToGetLineChannelToken": "首先,前往 {0},建立 provider 和 channel (Messaging API)。接著您就可以從上面提到的選單項目中取得頻道存取權杖及使用者 ID。", + "Icon URL": "圖示網址", + "aboutIconURL": "您可以在「圖示網址」中提供圖片網址以覆蓋預設個人檔案圖片。若已設定 Emoji 圖示,將忽略此設定。", + "aboutMattermostChannelName": "您可以在「頻道名稱」欄位中填寫頻道名稱以覆蓋 Webhook 的預設頻道。必須在 Mattermost 的 Webhook 設定中啟用。例如:#其他頻道", + "matrix": "Matrix", + "promosmsTypeEco": "SMS ECO - 便宜,但是很慢且經常過載。僅限位於波蘭的收件者。", + "promosmsTypeFlash": "SMS FLASH - 訊息會自動在收件者的裝置上顯示。僅限位於波蘭的收件者。", + "promosmsTypeFull": "SMS FULL - 高級版,您可以使用您的寄件人名稱 (必須先註冊名稱。對於警報來說十分可靠。", + "promosmsTypeSpeed": "SMS SPEED - 系統中的最高優先度。快速、可靠,但昂貴 (約 SMS FULL 的兩倍價格)。", + "promosmsPhoneNumber": "電話號碼 (若收件者位於波蘭則無需輸入區域代碼)", + "promosmsSMSSender": "簡訊寄件人名稱:預先註冊的名稱或以下的預設名稱:InfoSMS、SMS Info、MaxSMS、INFO、SMS", + "Feishu WebHookUrl": "飛書 WebHook 網址", + "matrixHomeserverURL": "Homeserver 網址 (開頭為 http(s)://,結尾可能帶連接埠)", + "Internal Room Id": "內部識別碼", + "matrixDesc1": "您可以在 Matrix 客戶端的房間設定中的進階選項找到 internal room ID。應該看起來像 !QMdRCpUIfLwsfjxye6:home.server。", + "matrixDesc2": "使用您自己的 Matrix 使用者存取權杖將賦予存取您的帳號和您加入的房間的完整權限。建議建立新使用者,並邀請至您想要接收通知的房間中。您可以執行 {0} 以取得存取權杖", + "Method": "方法", + "Body": "主體", + "Headers": "標頭", + "PushUrl": "Push 網址", + "HeadersInvalidFormat": "請求標頭不是有效的 JSON: ", + "BodyInvalidFormat": "請求主體不是有效的 JSON: ", + "Monitor History": "監測器歷史紀錄", + "clearDataOlderThan": "保留 {0} 天內的監測器歷史紀錄。", + "PasswordsDoNotMatch": "密碼不相符。", + "records": "記錄", + "One record": "一項記錄", + "steamApiKeyDescription": "若要監測 Steam 遊戲伺服器,您將需要 Steam Web-API 金鑰。您可以在此註冊您的 API 金鑰: ", + "Current User": "目前使用者", + "topic": "問題", + "topicExplanation": "要監測的 MQTT Topic", + "successMessage": "成功訊息", + "successMessageExplanation": "視為成功的 MQTT 訊息", + "recent": "最近", + "Done": "完成", + "Info": "資訊", + "Security": "安全性", + "Steam API Key": "Steam API 金鑰", + "Shrink Database": "壓縮資料庫", + "Pick a RR-Type...": "選擇資源記錄類型…", + "Pick Accepted Status Codes...": "選擇可接受的狀態碼…", + "Default": "預設", + "HTTP Options": "HTTP 選項", + "Create Incident": "建立事件", + "Title": "標題", + "Content": "內容", + "Style": "樣式", + "info": "資訊", + "warning": "警告", + "danger": "危險", + "error": "錯誤", + "critical": "嚴重", + "primary": "主要", + "light": "淺色", + "dark": "暗色", + "Post": "發佈", + "Please input title and content": "請輸入標題及內容", + "Created": "建立", + "Last Updated": "最後更新", + "Unpin": "取消釘選", + "Switch to Light Theme": "切換至淺色佈景主題", + "Switch to Dark Theme": "切換至深色佈景主題", + "Show Tags": "顯示標籤", + "Hide Tags": "隱藏標籤", + "Description": "說明", + "No monitors available.": "沒有可用的監測器。", + "Add one": "新增一個", + "No Monitors": "無監測器", + "Untitled Group": "未命名群組", + "Services": "服務", + "Discard": "捨棄", + "Cancel": "取消", + "Powered by": "技術支援", + "serwersms": "SerwerSMS.pl", + "serwersmsAPIUser": "API 使用者名稱 (包含 webapi_ 前置碼)", + "serwersmsAPIPassword": "API 密碼", + "serwersmsPhoneNumber": "電話號碼", + "serwersmsSenderName": "SMS 寄件人名稱 (由客戶入口網站註冊)", + "smseagle": "SMSEagle", + "smseagleTo": "電話號碼", + "smseagleGroup": "電話簿群組名稱", + "smseagleContact": "電話簿聯絡人名稱", + "smseagleRecipientType": "收件者類型", + "smseagleRecipient": "收件者 (用逗號分隔)", + "smseagleToken": "API 存取權杖", + "smseagleUrl": "您的 SMSEagle 裝置網址", + "smseagleEncoding": "以 Unicode 傳送 (預設:GSM-7)", + "smseaglePriority": "訊息優先度 (0-9,最高為9)", + "stackfield": "Stackfield", + "Customize": "自訂", + "Custom Footer": "自訂頁尾", + "Custom CSS": "自訂 CSS", + "smtpDkimSettings": "DKIM 設定", + "smtpDkimDesc": "請參考 Nodemailer DKIM {0} 使用方式。", + "documentation": "文件", + "smtpDkimDomain": "網域名稱", + "smtpDkimKeySelector": "DKIM 選擇字", + "smtpDkimPrivateKey": "私密金鑰", + "smtpDkimHashAlgo": "雜湊演算法 (選填)", + "smtpDkimheaderFieldNames": "要簽署的郵件標頭 (選填)", + "smtpDkimskipFields": "不簽署的郵件標頭 (選填)", + "wayToGetPagerDutyKey": "您可以前往服務 -> 服務目錄 -> (選取服務) -> 整合 -> 新增整合以取得。您可以搜尋 \"Events API V2\"。詳細資訊 {0}", + "Integration Key": "整合金鑰", + "Integration URL": "整合網址", + "Auto resolve or acknowledged": "自動解決或認可", + "do nothing": "不進行任何操作", + "auto acknowledged": "自動認可", + "auto resolve": "自動解決", + "gorush": "Gorush", + "alerta": "Alerta", + "alertaApiEndpoint": "API 端點", + "alertaEnvironment": "環境", + "alertaApiKey": "API 金鑰", + "alertaAlertState": "警示狀態", + "alertaRecoverState": "恢復狀態", + "deleteStatusPageMsg": "您確定要刪除此狀態頁嗎?", + "Proxies": "代理伺服器", + "default": "預設", + "enabled": "啟用", + "setAsDefault": "設為預設", + "deleteProxyMsg": "您確定要為所有監測器刪除此代理伺服器嗎?", + "proxyDescription": "必須將代理伺服器指派給監測器才能運作。", + "enableProxyDescription": "此代理伺服器在啟用前不會在監測器上生效,您可以藉由控制啟用狀態來暫時對所有的監測器停用代理伺服器。", + "setAsDefaultProxyDescription": "預設情況下,新監測器將啟用此代理伺服器。您仍可分別停用各監測器的代理伺服器。", + "Certificate Chain": "憑證鏈結", + "Valid": "有效", + "Invalid": "無效", + "AccessKeyId": "AccessKey ID", + "SecretAccessKey": "AccessKey 密碼", + "PhoneNumbers": "電話號碼", + "TemplateCode": "範例程式碼", + "SignName": "簽名", + "Sms template must contain parameters: ": "SMS 範本必須包含參數: ", + "Bark Endpoint": "Bark 端點", + "Bark Group": "Bark 群組", + "Bark Sound": "Bark 鈴聲", + "WebHookUrl": "WebHookURL", + "SecretKey": "祕密金鑰", + "For safety, must use secret key": "為了安全起見,必須使用秘密金鑰", + "Device Token": "裝置權杖", + "Platform": "平臺", + "Huawei": "華為", + "High": "高", + "Retry": "重試", + "Topic": "問題", + "WeCom Bot Key": "WeCom 機器人金鑰", + "Setup Proxy": "設定 Proxy", + "Proxy Protocol": "Proxy 通訊協定", + "Proxy Server": "Proxy 伺服器", + "Proxy server has authentication": "Proxy 伺服器啟用了驗證功能", + "User": "使用者", + "Installed": "已安裝", + "Not installed": "未安裝", + "Running": "執行中", + "Not running": "未執行", + "Remove Token": "移除權杖", + "Start": "開始", + "Stop": "停止", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "新增狀態頁", + "Slug": "Slug", + "Accept characters:": "可用字元:", + "startOrEndWithOnly": "僅能使用 {0} 開頭或結尾", + "No consecutive dashes": "不得連續使用破折號", + "Next": "下一步", + "The slug is already taken. Please choose another slug.": "此 slug 已被使用。請選擇其他 slug。", + "No Proxy": "無 Proxy", + "Authentication": "驗證", + "HTTP Basic Auth": "HTTP 基本驗證", + "New Status Page": "新狀態頁", + "Page Not Found": "找不到頁面", + "Reverse Proxy": "反向代理", + "Backup": "備份", + "About": "關於", + "wayToGetCloudflaredURL": "(從 {0} 下載 cloudflared)", + "cloudflareWebsite": "Cloudflare 網站", + "Message:": "訊息:", + "Don't know how to get the token? Please read the guide:": "不知道如何取得權杖嗎?請閱讀指南:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "如果您目前正透過 Cloudflare Tunnel 連線,可能會導致連線中斷。您確定要停止嗎?請輸入密碼以確認。", + "HTTP Headers": "HTTP 標頭", + "Trust Proxy": "信任的 Proxy", + "Other Software": "其他軟體", + "For example: nginx, Apache and Traefik.": "例如 nginx、Apache 和 Traefik。", + "Please read": "請閱覽", + "Subject:": "簽發給:", + "Valid To:": "有效期限:", + "Days Remaining:": "剩餘天數:", + "Issuer:": "簽發者:", + "Fingerprint:": "指紋:", + "No status pages": "無狀態頁", + "Domain Name Expiry Notification": "網域名稱到期通知", + "Proxy": "Proxy", + "Date Created": "建立日期", + "HomeAssistant": "Home Assistant", + "onebotHttpAddress": "OneBot HTTP 位址", + "onebotMessageType": "OneBot 訊息類型", + "onebotGroupMessage": "群組", + "onebotPrivateMessage": "私人", + "onebotUserOrGroupId": "群組/使用者 ID", + "onebotSafetyTips": "為了安全起見,必須設定存取權杖", + "PushDeer Key": "PushDeer 金鑰", + "Footer Text": "頁尾文字", + "Show Powered By": "顯示技術支援文字", + "Domain Names": "網域名稱", + "signedInDisp": "以 {0} 身分登入", + "signedInDispDisabled": "驗證已停用。", + "RadiusSecret": "Radius 加密", + "RadiusSecretDescription": "客戶端與伺服器端的共享機密", + "RadiusCalledStationId": "被叫站 Id", + "RadiusCalledStationIdDescription": "被呼叫裝置的識別碼", + "RadiusCallingStationId": "呼叫站 Id", + "RadiusCallingStationIdDescription": "呼叫裝置的識別碼", + "Certificate Expiry Notification": "憑證到期通知", + "API Username": "API 使用者名稱", + "API Key": "API 金鑰", + "Recipient Number": "收件者號碼", + "From Name/Number": "來自名字/號碼", + "Leave blank to use a shared sender number.": "留空以使用共享寄件人號碼。", + "Octopush API Version": "Octopush API 版本", + "Legacy Octopush-DM": "舊版 Octopush-DM", + "endpoint": "端點", + "octopushAPIKey": "在控制台的 HTTP API 憑證取得的 \"API 金鑰\"", + "octopushLogin": "在控制台的 HTTP API 憑證取得的 \"Login\"", + "promosmsLogin": "API 登入名稱", + "promosmsPassword": "API 密碼", + "pushoversounds pushover": "Pushover (預設)", + "pushoversounds bike": "車鈴", + "pushoversounds bugle": "號角", + "pushoversounds cashregister": "收銀機", + "pushoversounds classical": "古典", + "pushoversounds cosmic": "宇宙", + "pushoversounds falling": "下落", + "pushoversounds gamelan": "甘美朗", + "pushoversounds incoming": "來電", + "pushoversounds intermission": "中場休息", + "pushoversounds magic": "魔法", + "pushoversounds mechanical": "機械", + "pushoversounds pianobar": "鋼琴酒吧", + "pushoversounds siren": "警鈴", + "pushoversounds spacealarm": "太空鬧鐘", + "pushoversounds tugboat": "汽笛", + "pushoversounds alien": "外星鬧鐘 (長)", + "pushoversounds climb": "爬升 (長)", + "pushoversounds persistent": "持續 (長)", + "pushoversounds echo": "Pushover 迴音 (長)", + "pushoversounds updown": "上下 (長)", + "pushoversounds vibrate": "僅震動", + "pushoversounds none": "無 (靜音)", + "pushyAPIKey": "API 密鑰", + "pushyToken": "裝置權杖", + "Show update if available": "顯示可用更新", + "Also check beta release": "檢查 Beta 版", + "Using a Reverse Proxy?": "正在使用反向代理?", + "Check how to config it for WebSocket": "查看如何為 WebSocket 設定", + "Steam Game Server": "Steam 遊戲伺服器", + "Most likely causes:": "可能原因:", + "The resource is no longer available.": "資源已不可用。", + "There might be a typing error in the address.": "網址可能有誤。", + "What you can try:": "您可以嘗試:", + "Retype the address.": "重新輸入網址。", + "Go back to the previous page.": "返回上一頁。", + "Coming Soon": "即將推出", + "wayToGetClickSendSMSToken": "您可以從 {here} 取得 API 使用者名稱和金鑰。", + "Connection String": "連線字串", + "Query": "查詢", + "settingsCertificateExpiry": "TLS 憑證到期", + "certificationExpiryDescription": "TLS 將於 X 天後到期時觸發 HTTPS 監測器通知:", + "Setup Docker Host": "設定 Docker 主機", + "Connection Type": "連線類型", + "Docker Daemon": "Docker Daemon", + "deleteDockerHostMsg": "您確定要為所有監測器刪除此 Docker 主機嗎?", + "socket": "Socket", + "tcp": "TCP / HTTP", + "Docker Container": "Docker 容器", + "Container Name / ID": "容器名稱 / ID", + "Docker Host": "Docker 主機", + "Docker Hosts": "Docker 主機", + "ntfy Topic": "ntfy 主題", + "Domain": "網域", + "Workstation": "工作站", + "disableCloudflaredNoAuthMsg": "您處於無驗證模式。無須輸入密碼。", + "trustProxyDescription": "信任“X-Forwarded-*”標頭。如果您想要取得正確的用戶端 IP,而您的 Uptime Kuma 位於 Nginx 或 Apache 等代理程式後面,則應該啟用此功能。", + "wayToGetLineNotifyToken": "您可以從 {0} 取得存取權杖", + "Examples": "範例", + "Home Assistant URL": "Home Assistant 網址", + "Long-Lived Access Token": "長期有效存取權杖", + "Long-Lived Access Token can be created by clicking on your profile name (bottom left) and scrolling to the bottom then click Create Token. ": "若要建立長期有效存取權杖,請點選您的個人檔案名稱 (左下角),捲動至最下方,然後點選建立權杖。 ", + "Notification Service": "通知服務", + "default: notify all devices": "預設:通知所有服務", + "A list of Notification Services can be found in Home Assistant under \"Developer Tools > Services\" search for \"notification\" to find your device/phone name.": "您可以在 Home Assistant 中查看通知服務的列表,在\"開發者工具 > 服務\"下搜尋\"通知\"來找到您的裝置/手機的名稱。", + "Automations can optionally be triggered in Home Assistant:": "可以選擇在 Home Assistant 中觸發自動化程序:", + "Trigger type:": "觸發器類型:", + "Event type:": "事件類型:", + "Event data:": "事件資料:", + "Then choose an action, for example switch the scene to where an RGB light is red.": "然後選擇動作,例如切換至 RGB 燈為紅色的場景。", + "Frontend Version": "前端版本", + "Frontend Version do not match backend version!": "前端版本與後端版本不符!", + "Base URL": "基底網址", + "goAlertInfo": "GoAlert 是用於待命排程、升級自動化,以及通知 (如簡訊或語音通話) 的開放原始碼應用程式。自動在正確的時間、用恰當的方法、聯絡合適的人! {0}", + "goAlertIntegrationKeyInfo": "取得服務的通用 API 整合金鑰,格式為 \"aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee\"。通常是已複製的網址的權杖參數值。", + "goAlert": "GoAlert", + "backupOutdatedWarning": "已棄用:由於添加了許多功能且此備份功能有點無人維護,因此它無法產生或還原完整的備份。", + "backupRecommend": "請直接備份磁碟區或 ./data/ 資料夾。", + "Optional": "選填", + "squadcast": "Squadcast", + "SendKey": "傳送金鑰", + "SMSManager API Docs": "SMSManager API 文件 ", + "Gateway Type": "閘道類型", + "SMSManager": "SMSManager", + "You can divide numbers with": "若要除數,您可以使用", + "or": "或是", + "recurringInterval": "間隔", + "Recurring": "週期性", + "strategyManual": "手動切換使用中/非使用中", + "warningTimezone": "正在使用伺服器的時區", + "weekdayShortMon": "一", + "weekdayShortTue": "二", + "weekdayShortWed": "三", + "weekdayShortThu": "四", + "weekdayShortFri": "五", + "weekdayShortSat": "六", + "weekdayShortSun": "日", + "dayOfWeek": "每週特定一天", + "dayOfMonth": "每月特定一天", + "lastDay": "最後一天", + "lastDay1": "每月的最後一天", + "lastDay2": "每月的倒數第二天", + "lastDay3": "每月的倒數第三天", + "lastDay4": "每月的倒數第四天", + "No Maintenance": "無維護", + "pauseMaintenanceMsg": "您確定要暫停嗎?", + "maintenanceStatus-under-maintenance": "維護中", + "maintenanceStatus-inactive": "非使用中", + "maintenanceStatus-scheduled": "已排程", + "maintenanceStatus-ended": "已結束", + "maintenanceStatus-unknown": "未知", + "Display Timezone": "顯示時區", + "Server Timezone": "伺服器時區", + "statusPageMaintenanceEndDate": "結束", + "IconUrl": "圖示網址", + "Enable DNS Cache": "(已棄用)為 HTTP(s) 監控項啟用 DNS 快取", + "Enable": "啟用", + "Disable": "停用", + "dnsCacheDescription": "在某些 IPv6 環境可能會無法運作,如果您遇到任何問題,請停用。", + "Single Maintenance Window": "單一維護時段", + "Maintenance Time Window of a Day": "每日的維護時段", + "Effective Date Range": "有效的日期範圍(可選)", + "Schedule Maintenance": "排程維護", + "Date and Time": "時間和日期", + "DateTime Range": "DateTime 範圍", + "Strategy": "策略", + "Free Mobile User Identifier": "免費的行動用戶識別碼", + "Free Mobile API Key": "免費行動裝置 API 金鑰", + "Enable TLS": "啟用 TLS", + "Proto Service Name": "Proto 服務名稱", + "Proto Method": "Proto 方式", + "Proto Content": "Proto 內容", + "Economy": "節約", + "Lowcost": "低費率", + "high": "高", + "General Monitor Type": "一般監測器類型", + "Passive Monitor Type": "被動監測器類型", + "Specific Monitor Type": "指定監測器類型", + "plugin": "外掛程式 | 外掛程式", + "install": "安裝", + "Game": "遊戲", + "Help": "幫助", + "Monitor": "監測器 | 監測器", + "Custom": "自訂", + "sameAsServerTimezone": "使用伺服器時區", + "cronExpression": "Cron 表達式", + "telegramSendSilently": "悄悄的發送", + "telegramSendSilentlyDescription": "悄悄的發送訊息。使用者會收到通知,但是是無聲的。", + "pagertreeDoNothing": "什麼都不做", + "Add New Tag": "新增標籤", + "telegramMessageThreadIDDescription": "(可選) Telegram 話題描述", + "telegramMessageThreadID": "(選填) Telegram 話題 ID", + "startDateTime": "開始日期/時間", + "endDateTime": "結束日期/時間", + "cronSchedule": "排程: ", + "invalidCronExpression": "無效的 Cron 表達式:{0}", + "telegramProtectContent": "阻止轉發/儲存", + "telegramProtectContentDescription": "如果啟用,Telegram 中的機器人訊息將受到保護,不會被轉發和儲存。", + "installing": "安裝中", + "uninstall": "移除", + "loadingError": "無法取得數據, 請重試。", + "markdownSupported": "支援 Markdown 語法", + "Packet Size": "封包大小", + "statusPageRefreshIn": "將於 {0} 後重新整理", + "confirmUninstallPlugin": "是否要移除這個外掛程式?", + "Key Added": "已建立金鑰", + "Clone Monitor": "複製監控項目", + "Clone": "複製", + "cloneOf": "從 {0} 複製", + "uninstalling": "正在移除", + "notificationRegional": "地區性的", + "wayToGetZohoCliqURL": "您可以前往此頁面以瞭解如何建立 webhook 網址 {0}。", + "wayToGetKookBotToken": "到 {0} 建立應用程式並取得 bot token", + "dataRetentionTimeError": "保留期限必須為 0 或正數", + "infiniteRetention": "設定為 0 以作無限期保留。", + "confirmDeleteTagMsg": "你確定你要刪除此標籤?相關的監測器不會被刪除。", + "twilioAuthToken": "認證 Token / API 金鑰", + "twilioAccountSID": "帳號 SID", + "ntfyUsernameAndPassword": "使用者名稱和密碼", + "ntfyAuthenticationMethod": "認證類型", + "API Keys": "API 金鑰", + "Expiry": "過期", + "apiKey-inactive": "無效", + "apiKey-expired": "已過期", + "Reconnecting...": "重新連線中...", + "Expiry date": "過期時間", + "Don't expire": "永不過期", + "Continue": "繼續", + "Add Another": "新增作者", + "Add API Key": "新增 API 金鑰", + "Generate": "產生", + "lunaseaTarget": "目標", + "lunaseaDeviceID": "裝置 ID", + "lunaseaUserID": "使用者 ID", + "Cannot connect to the socket server": "無法連線到 Socket 伺服器", + "Edit Maintenance": "編輯維護", + "deleteAPIKeyMsg": "您確定要刪除這個 API 金鑰?", + "Custom Monitor Type": "自訂監視器類型", + "Google Analytics ID": "Google Analytics ID", + "Server Address": "伺服器位置", + "Edit Tag": "編輯標籤", + "pagertreeMedium": "中", + "pagertreeHigh": "高", + "pagertreeResolve": "自動解決", + "pagertreeLow": "低", + "Learn More": "閱讀更多", + "pushoverMessageTtl": "Message TTL (秒)", + "apiKeyAddedMsg": "您的 API 金鑰已建立。金鑰不會再次顯示,請將它放在安全的地方。", + "No API Keys": "沒有 API Keys", + "apiKey-active": "啟用", + "Expires": "過期", + "disableAPIKeyMsg": "確定要停用這個 API 金鑰?", + "Monitor Setting": "{0} 的監視器設定", + "Guild ID": "公會 ID", + "chromeExecutableDescription": "如果您使用 Docker 且未安裝 Chromium,可能要花數分鐘安裝後才能顯示測試結果。安裝會使用 1GB 的硬碟空間。", + "promosmsAllowLongSMS": "允許長 SMS 訊息", + "Home": "首頁", + "chromeExecutable": "Chrome/Chromium 執行檔", + "chromeExecutableAutoDetect": "自動偵測", + "pagertreeCritical": "緊急", + "PushDeer Server": "PushDeer 伺服器", + "pushDeerServerDescription": "留空則使用官方伺服器", + "Body Encoding": "請求體編碼", + "filterActive": "啟用", + "filterActivePaused": "暫停", + "Select": "選擇", + "enableNSCD": "啟用 NSCD(名稱服務快取)以快取所有 DNS 請求", + "Server URL should not contain the nfty topic": "伺服器地址不應包含 ntfy 主題", + "Invert Keyword": "反轉模式", + "Request Timeout": "請求逾時", + "timeoutAfter": "在 {0} 秒後超時", + "styleElapsedTime": "在監控項詳情的心跳欄下顯示起止時間", + "styleElapsedTimeShowNoLine": "顯示(不帶連接線)", + "styleElapsedTimeShowWithLine": "顯示(帶連接線)", + "webhookCustomBodyDesc": "為 webhook 設定一個自定義 HTTP 請求體。可在模板內使用{msg},、{heartbeat}和{monitor} 變量。", + "webhookBodyPresetOption": "預設 - {0}", + "webhookBodyCustomOption": "自訂主體內容", + "selectedMonitorCount": "已選:{0}", + "Check/Uncheck": "選取/取消選取", + "tailscalePingWarning": "如需使用 Tailscale Ping 監測器,您必須在伺服器上以非 Docker 方式安裝 Uptime Kuma,且同時安裝 Tailscale 用戶端。", + "invertKeywordDescription": "出現關鍵詞將令檢測結果設為失敗,而非成功。", + "wayToGetKookGuildID": "在 Kook 設定中打開「開發者模式」,然後右鍵點選頻道可取得其 ID", + "Notify Channel": "通知該頻道", + "aboutNotifyChannel": "勾選“通知該頻道”,會令該頻道內所有成員都收到一條桌面端或移動端通知,無論其狀態是在線或離開。", + "pagertreeIntegrationUrl": "整合 URL 地址", + "pagertreeUrgency": "緊急程度", + "Expected Value": "預期值", + "Json Query": "JSON 查詢", + "setupDatabaseChooseDatabase": "您想使用哪個資料庫?", + "setupDatabaseEmbeddedMariaDB": "您不需要做任何設定。此 Docker 映像檔已內建並設定了 MariaDB。Uptime Kuma 將透過 Unix socket 連線到該資料庫。", + "setupDatabaseMariaDB": "連線到外部 MariaDB 資料庫。 需要設定資料庫連線資訊。", + "dbName": "資料庫名稱", + "Show Clickable Link": "顯示可點選連結", + "pagertreeSilent": "靜音", + "twilioApiKey": "API 金鑰 (選用)", + "Badge Preview": "徽章預覽", + "pushViewCode": "如何使用 Push 監控?(檢視程式碼)", + "pushOthers": "其它", + "programmingLanguages": "程式語言", + "twilioToNumber": "目標號碼", + "Badge Duration (in hours)": "徽章持續時間(小時)", + "Badge Prefix": "徽章值字首", + "Badge Suffix": "徽章值字尾", + "Badge Label Color": "徽章標籤顏色", + "Badge Color": "徽章顏色", + "Badge Label Prefix": "徽章標籤字首", + "Badge Label Suffix": "徽章標籤字尾", + "Badge Up Color": "徽章上線顏色", + "Badge Down Color": "徽章離線顏色", + "Badge Pending Color": "徽章待定顏色", + "Badge Maintenance Color": "徽章維護顏色", + "Badge Warn Color": "徽章警告顏色", + "Badge Warn Days": "徽章警告天數", + "Badge Down Days": "徽章離線天數", + "Badge Style": "徽章風格", + "Badge value (For Testing only.)": "徽章值(僅供測試。)", + "Badge URL": "徽章網址", + "Group": "群組", + "Monitor Group": "監控群組", + "monitorToastMessagesLabel": "監控 Toast 通知", + "monitorToastMessagesDescription": "監控的 Toast 通知會在指定的秒數後消失。設定為 -1 以停用逾時。設定為 0 以停用 Toast 通知。", + "toastErrorTimeout": "錯誤通知的逾時", + "toastSuccessTimeout": "成功通知的逾時", + "Kafka Brokers": "Kafka Brokers", + "Enter the list of brokers": "輸入 Broker 清單", + "Press Enter to add broker": "按 Enter 以新增 Broker", + "Kafka Topic Name": "Kafka 主題名稱", + "Kafka Producer Message": "Kafka Producer 訊息", + "Enable Kafka SSL": "啟用 Kafka SSL", + "Enable Kafka Producer Auto Topic Creation": "啟用 Kafka Producer 自動主題建立", + "Kafka SASL Options": "Kafka SASL 選項", + "Mechanism": "機制", + "gamedigGuessPortDescription": "Valve 伺服器查詢協定使用的連接埠可能與客戶端連接埠不同。如果監控無法連線到您的伺服器,請嘗試此選項。", + "Saved.": "已儲存。", + "authInvalidToken": "無效權杖。", + "authIncorrectCreds": "使用者名稱或密碼錯誤。", + "2faAlreadyEnabled": "已啟用 2FA。", + "2faDisabled": "已停用 2FA。", + "successAdded": "新增成功。", + "successResumed": "已成功恢復。", + "successPaused": "已成功暫停。", + "successDeleted": "刪除成功。", + "successEdited": "編輯成功。", + "successAuthChangePassword": "密碼已成功更新。", + "authUserInactiveOrDeleted": "使用者已停用或已刪除。", + "2faEnabled": "已啟用 2FA。", + "liquidIntroduction": "模板功能是透過 Liquid 模板語言實現的。請參考 {0} 以取得使用說明。以下是可用的變數:", + "templateMsg": "通知訊息", + "templateHeartbeatJSON": "描述心跳的物件", + "templateMonitorJSON": "描述監控的物件", + "templateLimitedToUpDownCertNotifications": "僅適用於 UP/DOWN/憑證到期通知", + "templateLimitedToUpDownNotifications": "僅適用於 UP/DOWN 通知", + "Open Badge Generator": "開啟徽章產生器", + "Badge Generator": "{0} 的徽章產生器", + "Badge Type": "徽章類型", + "GrafanaOncallUrl": "Grafana Oncall 網址", + "emailCustomisableContent": "可自訂內容", + "smtpLiquidIntroduction": "以下兩個欄位可以透過 Liquid 模板語言進行模板化。請參考 {0} 以取得使用說明。以下是可用的變數:", + "leave blank for default subject": "留空以使用預設主旨", + "emailCustomBody": "自訂內文", + "leave blank for default body": "留空以使用預設內文", + "emailTemplateServiceName": "服務名稱", + "emailTemplateHostnameOrURL": "主機名稱或網址", + "emailTemplateStatus": "狀態", + "emailTemplateMonitorJSON": "描述監控的物件", + "emailTemplateHeartbeatJSON": "描述心跳的物件", + "emailTemplateMsg": "通知訊息", + "emailTemplateLimitedToUpDownNotification": "僅適用於 UP/DOWN 心跳,否則為空", + "successBackupRestored": "備份已成功還原。", + "successDisabled": "已成功停用。", + "successEnabled": "已成功啟用。", + "tagNotFound": "找不到標籤。", + "foundChromiumVersion": "發現 Chromium/Chrome。版本:{0}", + "setupDatabaseSQLite": "一個簡單的資料庫檔案,適用於小規模部署。在 v2.0.0 之前,Uptime Kuma 預設使用 SQLite 作為資料庫。", + "Pick a SASL Mechanism...": "選擇一個 SASL 機制…", + "Authorization Identity": "授權身份", + "AccessKey Id": "存取金鑰 ID", + "Secret AccessKey": "秘密存取金鑰", + "Session Token": "工作階段權杖", + "noGroupMonitorMsg": "無法使用。請先建立群組監控。", + "Close": "關閉", + "Request Body": "請求主體", + "noOrBadCertificate": "無/錯誤憑證", + "Reset Token": "重設權杖", + "Bark API Version": "Bark API 版本", + "noDockerHostMsg": "無法使用。請先設定 Docker 主機。", + "DockerHostRequired": "請為此監控設定 Docker 主機。", + "Show Clickable Link Description": "如果勾選,所有能存取此狀態頁面的人都能存取監控網址。", + "Badge Label": "徽章標籤", + "wayToGetFlashDutyKey": "如果要將 Uptime Kuma 和 Flashduty 整合: 前往 頻道>選擇頻道>整合>新增整合,選擇 Uptime Kuma,然後複製推送網址。", + "FlashDuty Severity": "嚴重性", + "nostrRelays": "Nostr 中繼", + "nostrRelaysHelp": "每行一個中繼網址", + "nostrSender": "傳送者私鑰 (nsec)", + "nostrRecipients": "接收者公鑰 (npub)", + "nostrRecipientsHelp": "npub 格式,每行一個", + "showCertificateExpiry": "顯示憑證到期", + "wayToGetPagerTreeIntegrationURL": "在 PagerTree 中建立 Uptime Kuma 整合後,複製端點。檢視完整資訊 {0}", + "twilioFromNumber": "來源號碼", + "Browser Screenshot": "瀏覽器畫面截圖", + "Remote Browsers": "遠端瀏覽器", + "Remote Browser": "遠端瀏覽器", + "Add a Remote Browser": "新增遠端瀏覽器", + "Remote Browser not found!": "找不到遠端瀏覽器!", + "self-hosted container": "自架容器", + "useRemoteBrowser": "使用遠端瀏覽器", + "Add a domain": "新增網域", + "Remove domain": "移除網域「{0}」", + "settingUpDatabaseMSG": "設定資料庫中,可能需要一段時間,請耐心等待。", + "smspartnerApiurl": "您可以在儀表板上的 {0} 找到您的 API 金鑰", + "smspartnerPhoneNumberHelptext": "號碼必須是國際格式 {0}, {1}。多個號碼必須用 {2} 分隔", + "statusPageSpecialSlugDesc": "特殊 slug {0}: 當未提供 slug 時,將顯示此頁面", + "Add a new expiry notification day": "新增到期通知日", + "Remove the expiry notification": "移除到期通知日", + "Mentioning": "提及", + "Select message type": "選擇訊息類型", + "Send to channel": "發送至頻道", + "Create new forum post": "新增論壇貼文", + "postToExistingThread": "發佈到現有的討論串/論壇貼文", + "forumPostName": "論壇貼文名稱", + "threadForumPostID": "討論串 / 論壇貼文 ID", + "e.g. {discordThreadID}": "例如: {discordThreadID}", + "Channel access token (Long-lived)": "頻道存取權杖(長期有效)", + "Your User ID": "你的 user ID", + "Host URL": "主機 URL", + "Refresh Interval": "更新間隔", + "ignoreTLSErrorGeneral": "忽略連接中的TLS/SSL錯誤", + "Search monitored sites": "搜索監控中的站點", + "openModalTo": "以 {0} 打開一個新的對話框", + "locally configured mail transfer agent": "本機端的郵件傳輸代理", + "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "請輸入您要連接的伺服器主機名稱,或者輸入 {localhost} (如果您打算使用 {local_mta})", + "documentationOf": "{0} 文件", + "threemaRecipientType": "收件者類型", + "threemaRecipientTypeIdentityFormat": "8 個字元", + "threemaRecipientTypePhone": "電話號碼", + "threemaSenderIdentityFormat": "8 個字元,通常以 * 開頭", + "threemaRecipient": "收件者", + "mongodbCommandDescription": "對資料庫執行 MongoDB 命令。有關可用命令的資訊,請參閱 {documentation}", + "threemaRecipientTypeEmail": "電子郵件地址", + "Originator type": "發送者類型", + "smspartnerPhoneNumber": "電話號碼", + "Allow Long SMS": "允許長 SMS", + "cellsyntSplitLongMessages": "長訊息最多會被分成 6 段,每段最多 153 個字元,總共最多 918 字元。", + "max 15 digits": "最多 15 位數字", + "What is a Remote Browser?": "什麼是遠端瀏覽器?", + "Bitrix24 Webhook URL": "Bitrix24 WebHook URL", + "wayToGetBitrix24Webhook": "您可以按照 {0} 的步驟建立 Webhook", + "apiKeySevenIO": "SevenIO API 金鑰", + "ntfyPriorityHelptextAllEvents": "所有事件都以最高優先級發送", + "Telephone number": "手機號碼", + "Destination": "收件人", + "smspartnerSenderName": "SMS 寄件人名稱", + "setup a new monitor group": "設定新的監控群組", + "jsonQueryDescription": "使用 JSON 查詢從伺服器的 JSON 回應中解析並擷取特定資料,或者如果不需要 JSON,則使用“$”作為預設回應。然後將結果與字串形式的預期值進行比較。請參閱 {0} 以了解文件並使用 {1} 來測試查詢。", + "shrinkDatabaseDescriptionSqlite": "SQLite 的觸發器資料庫 {vacuum}。 {auto_vacuum} 已啟用,但這不會對資料庫進行片段整理,也不會像 {vacuum} 指令那樣重新打包各個資料庫頁面。", + "and": "和", + "whatHappensAtForumPost": "建立一個新的論壇文章。這不會在現有文章中發布。要在現有文章中發文,請使用“{option}”", + "aboutSlackUsername": "變更訊息寄件者的顯示名稱。如果您想提及他人,請將其包含在好友的名稱中。", + "remoteBrowsersDescription": "遠端瀏覽器可作為在本機執行 Chromium 的替代方案。使用 browserless.io 等服務進行設定或連接到您自己的服務", + "Money": "錢", + "successKeyword": "成功關鍵字", + "successKeywordExplanation": "作為成功判斷依據的 MQTT 關鍵字", + "Refresh Interval Description": "狀態頁面將每 {0} 秒完全重新整理一次網站", + "wayToGetDiscordThreadId": "取得主題 / 論壇文章 ID 與取得頻道 ID 類似。詳細了解如何取得 ID {0}", + "Don't mention people": "不要提及他人", + "Mention group": "提及 {group}", + "smspartnerSenderNameInfo": "必須介於 3..=11 個字元之間", + "cacheBusterParam": "新增 {0} 參數", + "cacheBusterParamDescription": "隨機生成參數以跳過快取。", + "gamedigGuessPort": "GameDig:隨機埠", + "Message format": "訊息格式", + "Send rich messages": "發送豐富格式文字訊息", + "bitrix24SupportUserID": "輸入您在 Bitrix24 中的使用者 ID。您可以透過使用者的個人資料連結找到 ID。", + "remoteBrowserToggle": "預設情況下,Chromium 在 Uptime Kuma 容器內運作。您可以透過切換此開關來使用遠端瀏覽器。", + "Elevator": "電梯", + "Clear": "清除", + "Scifi": "幻想", + "Doorbell": "電鈴", + "Bubble": "氣泡", + "Reveal": "暴露", + "Fail": "失敗", + "Correct": "正確的", + "time ago": "{0} 以前", + "ignoredTLSError": "已忽略 TLS/SSL 錯誤", + "now": "現在", + "-year": "-年", + "Json Query Expression": "JSON查詢表達式", + "ntfyPriorityHelptextAllExceptDown": "所有事件均以此優先權發送,但 {0} 事件除外,其優先權為 {1}", + "receiverInfoSevenIO": "如果接收號碼不在德國,您必須在號碼前面添加國家代碼(例如,對於來自美國的國家代碼 1,請使用 117612121212 而不是 017612121212)", + "callMeBotGet": "您可以在此處填寫您生成的用於 {0}、{1} 或 {2} 的端點。 請注意您可能會受到速率限制。 速率限制被推測為:{3}(僅供參考)", + "gtxMessagingFromHint": "在手機上,收件人會看到 TPOA 地址作為消息的發送者。TPOA 允許的格式包括:至多11個字母或數字、短代碼、當地長代碼或國際號碼({e164}、{e212} 或 {e214} 格式)", + "deleteRemoteBrowserMessage": "您確定要為所有監控器刪除此遠端瀏覽器嗎?", + "Command": "命令", + "wayToGetSevenIOApiKey": "造訪儀表板,位於 app.seven.io > 開發人員 > API 金鑰 > 綠色新增按鈕下方", + "senderSevenIO": "發信人號碼或名稱", + "receiverSevenIO": "收信人號碼", + "wayToWriteWhapiRecipient": "可用格式為不含 + 號的國際通用格式手機號碼({0})、聯絡人 ID({1})或群組 ID({2})。", + "wayToGetWhapiUrlAndToken": "您可以透過從 {0} 進入您想要的頻道來取得 API URL 及權杖", + "whapiRecipient": "手機號碼 / 聯絡人 ID / 群組 ID", + "API URL": "API URL", + "wayToGetHeiiOnCallDetails": "如需了解如何獲取 Trigger ID 和 API 密鑰,請訪問 {documentation}", + "gtxMessagingApiKeyHint": "你可以在此找到你的 API 密鑰:My Routing Accounts > Show Account Information > API Credentials > REST API (v2.x)", + "From Phone Number / Transmission Path Originating Address (TPOA)": "發件人電話號碼 / 傳輸路徑起始地址(TPOA)", + "To Phone Number": "收件人電話號碼", + "gtxMessagingToHint": "國際通用格式,需要前導 \"+\" ({e164}、{e212} 或 {e214} 格式)", + "Alphanumeric (recommended)": "英數字(推薦)", + "cellsyntOriginatortypeAlphanumeric": "英數字字串(最多 11 個字母或數字)。收件人無法向此號碼回覆訊息。", + "cellsyntOriginatortypeNumeric": "數字類型(最多 15 位數)需使用國際通用格式,不以 00+國家代碼開頭,例如若要使用英國的號碼 07920 110 000 需填寫 447920110000。收件人可向此號碼回覆消息。", + "max 11 alphanumeric characters": "最多 11 個字母或數字", + "Community String": "SNMP 通訊字符串", + "snmpCommunityStringHelptext": "此字符串用作密碼,以驗證和控制對SNMP啟用設備的訪問。請將其與您的SNMP設備配置匹配。", + "OID (Object Identifier)": "OID(物件識別碼)", + "snmpOIDHelptext": "輸入您想監控的感測器或狀態的 OID。如果您不確定 OID 是什麼,可以使用 MIB 瀏覽器或 SNMP 軟體等網路管理工具進行查詢。", + "Condition": "條件", + "SNMP Version": "SNMP 版本", + "Please enter a valid OID.": "請輸入一個合法的 OID。", + "wayToGetThreemaGateway": "你可以在 {0} 註冊 Threema 閘道。", + "threemaRecipientTypeIdentity": "Threema ID", + "threemaRecipientTypePhoneFormat": "E.164 標準,不含前導 + 號", + "threemaSenderIdentity": "閘道 ID", + "threemaApiAuthenticationSecret": "閘道金鑰", + "threemaBasicModeInfo": "註:此通知類型所使用的 Threema 閘道為基礎模式(伺服器端加密)。更多細節參見 {0}。", + "apiKeysDisabledMsg": "由於停用登入驗證,API 金鑰也被停用。", + "Host Onesender": "", + "Token Onesender": "Onesender 權杖", + "Recipient Type": "收件人類型", + "Private Number": "私密號碼", + "privateOnesenderDesc": "請確保電話號碼有效。要對私人電話號碼傳送訊息,格式形如:628123456789", + "groupOnesenderDesc": "請確保群組 ID 有效。要對群組傳送訊息,格式形如:628123456789-342345", + "Group ID": "群組 ID", + "wayToGetOnesenderUrlandToken": "你可以在 Onesender 網站取得 URL 及權杖。詳細訊息請見 {0}", + "Add Remote Browser": "新增遠端瀏覽器", + "Group Name": "群組名稱", + "OAuth2: Client Credentials": "OAuth2:用戶端認證", + "Authentication Method": "認證方式", + "Authorization Header": "驗證標頭", + "Form Data Body": "表單資料主體", + "OAuth Token URL": "OAuth 權杖 URL", + "Client ID": "用戶端 ID", + "Client Secret": "用戶端密碼", + "OAuth Scope": "OAuth 範圍", + "Optional: Space separated list of scopes": "可選項:用空格分隔的範圍列表", + "Go back to home page.": "返回到首頁。", + "No tags found.": "未找到標籤。", + "Lost connection to the socket server.": "與 Socket 伺服器的連線中斷。", + "Cannot connect to the socket server.": "無法連接 Socket 伺服器。", + "conditionDelete": "刪除條件", + "conditionAddGroup": "新增群組", + "conditionDeleteGroup": "刪除群組", + "equals": "相等", + "not equals": "不相等", + "contains": "包含", + "not contains": "不包含", + "starts with": "以此開頭", + "not starts with": "不以此開頭", + "ends with": "以此結尾", + "greater than": "多於", + "less than or equal to": "不多於", + "greater than or equal to": "不少於", + "Notification Channel": "通知頻道", + "Sound": "聲音", + "Alphanumerical string and hyphens only": "僅限字母、數字和連字號(-)", + "Arcade": "Arcade(拱廊)", + "Harp": "Harp(豎琴)", + "Flute": "Flute(長笛)", + "Guitar": "Guitar(吉他)", + "Pop": "Pop(流行音樂)", + "Custom sound to override default notification sound": "自訂鈴聲以覆蓋預設通知鈴聲", + "Time Sensitive (iOS Only)": "具時效性訊息(僅限 iOS )", + "Time sensitive notifications will be delivered immediately, even if the device is in do not disturb mode.": "即使裝置處於專注模式,具時效性通知也會立即送達。", + "From": "寄件人", + "Can be found on:": "可在此找到:{0}", + "The phone number of the recipient in E.164 format.": "收件人的 E.164 格式電話號碼。", + "Either a text sender ID or a phone number in E.164 format if you want to be able to receive replies.": "如需可被回復,請輸入發送者 ID 或 E.164 格式的手機號碼。", + "RabbitMQ Nodes": "RabbitMQ 管理節點", + "rabbitmqNodesDescription": "輸入 RabbitMQ 管理節點的 URL,包括協定及連接埠。例如:{0}", + "rabbitmqNodesRequired": "請設定此監視器的節點。", + "rabbitmqNodesInvalid": "請使用 RabbitMQ 節點的完整 URL(即完全限定 URL,以 http 開頭)。", + "RabbitMQ Username": "RabbitMQ 帳號", + "RabbitMQ Password": "RabbitMQ 密碼", + "rabbitmqHelpText": "要使用此監控項,您需要在 RabbitMQ 設置中啟用管理插件。有關更多信息,請參閱 {rabitmq_documentation}。", + "SendGrid API Key": "SendGrid API 金鑰", + "not ends with": "不以此結尾", + "less than": "少於", + "Originator": "發件人", + "cellsyntOriginator": "在收件人處作為消息發送者顯示。允許的內容取決於發件人類型。", + "cellsyntDestination": "收件人的手機號碼需要使用以 00+國家代碼開頭的國際通用格式,例如若要發給英國的號碼 07920 110 000 需使用 00447920110000 作為收件人手機號碼(至多17位數)。需發送給多個收件人手機號碼時可使用英文逗號分隔,每次請求最 多250 00個收件人手機號碼。", + "SIGNL4": "SIGNL4", + "SIGNL4 Webhook URL": "SIGNL4 Webhook URL", + "signl4Docs": "您可以在此了解更多關於如何設定 SIGNL4 以及如何取得 SIGNL4 Webhook URL的資訊:{0}。", + "Conditions": "條件", + "conditionAdd": "新增條件", + "conditionValuePlaceholder": "值", + "Separate multiple email addresses with commas": "用逗號分隔多個電子郵件地址", + "record": "記錄", + "New Group": "新群組", + "Font Twemoji by Twitter licensed under": "已經由 Twitter 的 Twemoji 授權", + "Phone numbers": "手機號碼", + "smsplanetApiToken": "SMSPlanet 的 Token", + "smsplanetApiDocs": "有關取得 API token的詳細信息,請參閱 {the_smsplanet_documentation}。", + "the smsplanet documentation": "smsplanet 說明文件", + "Sender name": "發送者名稱", + "smsplanetNeedToApproveName": "需在用戶介面獲得允許", + "templateServiceName": "伺服器名稱", + "templateHostnameOrURL": "主機名稱或 URL", + "templateStatus": "狀態", + "telegramUseTemplate": "使用自訂訊息模板", + "telegramTemplateFormatDescription": "Telegram 允許使用不同的標記語言來傳送訊息,有關具體詳情請參閱 Telegram {0}。", + "telegramUseTemplateDescription": "如果啟用則使用自訂模板發送訊息。", + "Plain Text": "純文字", + "Message Template": "訊息模板", + "wayToGetWahaApiUrl": "你的 WAHA 主機 URL。", + "wayToGetWahaApiKey": "API Key 是你在 WAHA 使用的環境變數 WHATSAPP_API_KEY 的值。", + "wayToGetWahaSession": "從這個 WAHA 會話發送通知到聊天ID。你可以從 WAHA 儀錶板找到它。", + "Template Format": "模板格式", + "wahaSession": "WAHA 會話", + "wahaChatId": "聊天室ID (手機號碼/聯絡人 ID/群組 ID)", + "wayToWriteWahaChatId": "帶有國際前綴但不含開頭加號 ({0})、聯絡人 ID ({1}) 或群組 ID ({2}) 的電話號碼。通知從 WAHA 會話傳送到此聊天 ID。", + "YZJ Webhook URL": "YZJ 的 Webhook URL", + "YZJ Robot Token": "YZJ 的機器人 token", + "telegramServerUrl": "(可選) 伺服器 URL", + "telegramServerUrlDescription": "解除 Telegram 的機器人 API 限製或在被封鎖區域(中國、伊朗等)取得存取權限。欲了解更多信息,請點擊{0}。預設值:{1}", + "ipFamilyDescriptionAutoSelect": "透過 {happyEyeballs} 判斷 IP 家族。", + "Happy Eyeballs algorithm": "Happy Eyeballs 演算法", + "Use HTML for custom E-mail body": "在自訂 Email 內文中使用 HTML", + "Disable URL in Notification": "在通知中停用 URL", + "smseagleMsgType": "訊息類型", + "smseagleApiv1": "APIv1(用於現有專案及維持向後相容)", + "Ip Family": "IP 家族", + "ntfyPriorityDown": "下線事件優先級", + "smseagleGroupV2": "電話簿群組 ID", + "smseagleContactV2": "電話簿聯絡人 ID", + "smseagleMsgSms": "SMS 訊息(預設)", + "smseagleDuration": "長度(秒)", + "smseagleTtsModel": "文字轉語音模型 ID", + "smseagleApiType": "API 版本", + "smseagleApiv2": "APIv2(推薦用於新的整合)", + "smseagleComma": "多個項目間須以逗號分隔", + "SpugPush Template Code": "範本代碼", + "Clear Form": "清除表單", + "pause": "暫停", + "Staged Tags for Batch Add": "暫存標籤以批次新增", + "Add Another Tag": "新增標籤", + "Path": "路徑", + "defaultFriendlyName": "新監測器", + "Add Tags": "加入標籤", + "tagAlreadyOnMonitor": "此標籤(名稱與值)已經存在監視器或等待加入中。", + "Events cleared successfully": "事件清除成功。", + "No monitors found": "未找到監控。", + "Could not clear events": "無法清除事件 {failed}/{total}", + "brevoBccEmail": "BCC郵件", + "auto-select": "自動選擇", + "deleteGroupMsg": "你確定要刪除此群組嗎?", + "clearAllEventsMsg": "你確定要刪除所有事件嗎?", + "brevoCcEmail": "CC郵件", + "brevoFromEmail": "來自郵件", + "brevoFromName": "來自名稱", + "brevoApiHelp": "在此創建API:{0}", + "brevoSubject": "標題", + "descriptionHelpText": "將顯示在內部控制面板,可以使用Markdown格式,內容會在顯示前處理(保留空格和縮排)。", + "Clear All Events": "清除所有事件", + "FlashDuty Push URL": "推送網址", + "supportBaleChatID": "支援 私訊/群組/頻道 ID", + "wayToGetBaleChatID": "你可以傳送訊息給這個機器人然後用此連結查看你的頻道ID:", + "wayToGetBaleToken": "你可以從{0}取得Token。", + "brevoToEmail": "給郵件", + "mqttWebSocketPath": "MQTT WebSocket路徑", + "mqttWebsocketPathExplanation": "用於經由WebSocket連線的MQTT的WebSocket路徑(例如:/mqtt)", + "mqttWebsocketPathInvalid": "請使用有效的WebSocket格式", + "mqttHostnameTip": "請使用以下格式:{hostnameFormat}", + "ariaPauseMaintenance": "暫停此維護排程", + "ariaResumeMaintenance": "重啟這個維護排程", + "ariaCloneMaintenance": "複製此維護排程", + "ariaEditMaintenance": "編輯此維護排程", + "ariaDeleteMaintenance": "刪除此維護排程", + "tagAlreadyStaged": "這個標籤(名稱和數值)已經準備就緒。", + "tagNameExists": "已經有與此表情同名的系統標籤,請從清單中選擇或使用其他名稱。", + "issueWithGoogleChatOnAndroidHelptext": "這也能規避掉像是 {issustackerURL} 的問題", + "Dingtalk User List": "使用者ID列表", + "Enter a list of userId": "輸入使用者ID列表", + "Dingtalk Mobile List": "行動裝置列表", + "Enter a list of mobile": "輸入行動裝置列表", + "Invalid mobile": "無效的行動裝置(電話) [{mobile}]", + "Invalid userId": "無效的使用者ID [{userId}]", + "webhookPostMethodDesc": "POST 適用於大多的現代化HTTP伺服器。", + "webhookGetMethodDesc": "GET 可以將數據作為查詢參數發送,不允許配置請求體。對於觸發 Uptime Kuma Push 的 監控來說非常有用。", + "HTTP Method": "HTTP方法", + "wayToGetEvolutionUrlAndToken": "您可以從 {0} 獲取所需頻道的 API網址 和 Token(密鑰)", + "Mention Mobile List": "提及行動裝置列表", + "Mention User List": "提及使用者ID列表", + "wayToWriteEvolutionRecipient": "帶有國際前綴但開頭沒有加號的電話號碼 ({0})、聯絡人 ID ({1}) 或群組 ID ({2})。", + "evolutionRecipient": "電話號碼 / 聯絡ID / 群組ID", + "FlashDuty Push URL Placeholder": "從警報整合頁面複製", + "Clone Maintenance": "複製維護", + "smseagleMsgRing": "鈴聲呼叫 (電話呼叫)", + "smseagleMsgTts": "文字轉語音通話", + "smseagleMsgTtsAdvanced": "文字轉語音進階通話", + "smseagleDocs": "檢查文件或 APIv2 的可用性: {0}", + "Ignore Sec-WebSocket-Accept header": "忽視 {0} 標頭", + "wsSubprotocolDescription": "更多關於子協議的資訊詳見 {documentation}", + "logoutCurrentUser": "登出 {username}", + "Resolver Server(s)": "解析器伺服器", + "Incident description": "事件描述", + "Incident not found or access denied": "未發現事件或訪問被拒絕", + "Past Incidents": "過往事件", + "Incident title": "事件標題", + "Edit Incident": "編輯事件", + "templateAvailableVariables": "可用變數", + "HeadersInvalidFormatBecause": "請求標頭不是有效的 JSON,因為 {error}", + "BodyInvalidFormatBecause": "請求體不是有效的 JSON,因為 {error}", + "steamApiKeyDescriptionAt": "要監控 Steam 遊戲伺服器,您需要一個 Steam Web API 金鑰。您可以在 {url} 註冊您的 API 金鑰", + "hostnameCannotBeIP": "DNS主機名稱不能是IP位址。您是否想使用解析器欄位?", + "invalidHostnameOrIP": "主機名稱或 IP 位址無效。主機名稱必須是有效的完全限定網域名稱 (FQDN)。不能使用萬用字元。主機名稱可以包含底線或以句點結尾。", + "invalidDNSHostname": "主機名稱無效。主機名稱必須是有效的完全限定網域名稱 (FQDN)。可以使用萬用字元,主機名稱可以包含底線或以點號結尾。", + "wildcardOnlyForDNS": "萬用字元主機名稱僅支援 DNS 監控。", + "invalidURL": "無效的URL", + "lastUpdatedAt": "最後更新時間:{date}", + "createdAt": "建立日期:{date}", + "lastUpdatedAtFromNow": "最後更新時間:{date} ({fromNow})", + "selectAllMonitorsAria": "選取所有監示器", + "monitorTypeGameServer": "遊戲伺服器", + "monitorTypeDatabase": "資料庫監視器類型", + "selectMonitorMsg": "選擇要執行操作的監視器", + "deleteIncidentMsg": "您確定要刪除此事件嗎?", + "Pinned incidents are shown prominently on the status page": "置頂事件會在狀態頁面上明顯顯示", + "deselectAllMonitorsAria": "取消選取所有監示器", + "versionIs": "版本:{version}", + "enableSSL": "啟用 SSL/TLS", + "mariadbUseSSLHelptext": "啟用此功能後,您將可以使用加密連線存取資料庫。大多數雲端資料庫都需要啟動此功能。", + "mariadbCaCertificateLabel": "CA憑證", + "mariadbCaCertificateHelptext": "請以 PEM 格式貼上 CA 證書,以便與自簽名證書一起使用。如果您的資料庫是使用由公共 CA 簽署的證書,請留空白。", + "unknownDays": "未知天數", + "No incidents recorded": "未記錄到任何事件", + "Load More": "載入更多", + "Loading...": "載入中...", + "Monitors": "{n} 顯示器 | {n} 顯示器", + "days": "{n} 天 | {n} 天", + "hours": "{n} 小時 | {n} 小時", + "minutes": "{n} 分鐘 | {n} 分鐘", + "Pin this incident": "釘選此事件", + "minuteShort": "{n} 分鐘 | {n} 分鐘", + "years": "{n} 年 | {n} 年", + "Only retry if status code check fails": "僅當狀態碼檢查失敗時才會重試", + "ignoreSecWebsocketAcceptHeaderDescription": "如果 WebSocket 升級成功,則允許伺服器不回覆 Sec-WebSocket-Accept 標頭。", + "wsCodeDescription": "有關狀態代碼的更多信息,請參閱 {rfc6455}", + "retryOnlyOnStatusCodeFailureDescription": "如果啟用此功能,重試僅在 HTTP 狀態碼檢查失敗時(例如,伺服器當機)才會發生。如果狀態碼檢查通過但 JSON 查詢失敗,則監視器將立即標記為下線,而不會進行重試。", + "Subprotocol(s)": "子協定", + "saveResponseForNotifications": "保存 HTTP 成功回應以用於通知", + "saveErrorResponseForNotifications": "保存 HTTP 錯誤回應以用於通知", + "saveResponseDescription": "儲存 HTTP 回應,並將其作為 {templateVariable} 提供給通知範本。", + "responseMaxLength": "響應最大長度(bytes)", + "responseMaxLengthDescription": "要儲存的回應資料的最大大小。設定為 0 表示無限制。較大的回應將被截斷。預設值:1024 (1KB)", + "example": "範例", + "Result": "結果", + "Resolve": "解決", + "Resolved": "已解決", + "Actions": "操作", + "monitorTypeSpecial": "特殊的", + "oracledbConnectionString": "Oracle 資料庫:{connectionString}", + "selectedMonitorCountMsg": "已選:{n} | 已選取:{n}", + "matrixUseTemplateDescription": "如果啟用,訊息將使用自訂範本發送。", + "wayToGetMaxToken": "您可以從 {0} 獲取 MAX 機器人令牌和其他詳細資訊。", + "matrixUseTemplate": "使用自訂訊息範本", + "Please input content": "請輸入內容", + "Please input title": "請輸入標題", + "dateCreatedAtFromNow": "建立日期:{date}({fromNow})", + "RSS Title": "RSS 標題", + "Leave blank to use status page title": "留空則使用狀態頁面標題", + "Cloud ID": "Cloud ID", + "API Token": "API Token", + "See Jira Cloud Docs": "請參閱 Jira Cloud 文檔", + "certHostnameMismatch": "憑證主機名稱與監控 URL 不符。", + "sipsakPingWarning": "要使用 SIP Options Ping 監視器,您需要在不安裝 Docker 的情況下安裝 Uptime Kuma,並在伺服器上安裝 Sipsak 用戶端。" +} diff --git a/src/lang/zu.json b/src/lang/zu.json new file mode 100644 index 0000000..a5fc451 --- /dev/null +++ b/src/lang/zu.json @@ -0,0 +1,13 @@ +{ + "languageName": "IsiNgisi", + "setupDatabaseChooseDatabase": "Isiphi isizinda sedatha ongathanda ukusisebenzisa?", + "setupDatabaseEmbeddedMariaDB": "Awudingi ukusetha noma yini. Lesi sithombe se-Docker sishumekiwe futhi sakulungisela i-MariaDB ngokuzenzakalelayo. I-Uptime Kuma izoxhuma kule database ngesokhethi le-Unix.", + "setupDatabaseMariaDB": "Xhuma kusizindalwazi sangaphandle se-MariaDB. Udinga ukusetha imininingwane yokuxhumana yesizindalwazi.", + "setupDatabaseSQLite": "Ifayela lesizindalwazi elilula, elinconyelwe ukuthunyelwa kwezinga elincane. Ngaphambi kwe-v2.0.0, i-Uptime Kuma yasebenzisa i-SQLite njengesizindalwazi esizenzakalelayo.", + "settingUpDatabaseMSG": "Ukusetha isizindalwazi. Kungase kuthathe isikhashana, sicela ubekezele.", + "dbName": "Igama lesizindalwazi", + "enableSSL": "Nika amandla i-SSL/TLS", + "mariadbUseSSLHelptext": "Nika amandla ukusebenzisa uxhumano olubethelwe kusizindalwazi sakho. Iyadingeka kuzigcinilwazi eziningi zamafu.", + "mariadbCaCertificateLabel": "Isitifiketi se-CA", + "mariadbCaCertificateHelptext": "Namathisela I-CA Cert ngefomethi ye-PEM ukuze uyisebenzise nezitifiketi ezizisayinele. Shiya kungenalutho uma isizindalwazi sakho sisebenzisa isitifiketi esisayinwe yi-CA yomphakathi." +} diff --git a/src/layouts/EmptyLayout.vue b/src/layouts/EmptyLayout.vue new file mode 100644 index 0000000..601eac1 --- /dev/null +++ b/src/layouts/EmptyLayout.vue @@ -0,0 +1,7 @@ + + + diff --git a/src/layouts/Layout.vue b/src/layouts/Layout.vue new file mode 100644 index 0000000..63fdb1b --- /dev/null +++ b/src/layouts/Layout.vue @@ -0,0 +1,432 @@ + + + + + diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..4c88583 --- /dev/null +++ b/src/main.js @@ -0,0 +1,59 @@ +import "bootstrap"; +import { createApp, h } from "vue"; +import contenteditable from "vue-contenteditable"; +import Toast from "vue-toastification"; +import "vue-toastification/dist/index.css"; +import App from "./App.vue"; +import "./assets/app.scss"; +import "./assets/vue-datepicker.scss"; +import { i18n } from "./i18n"; +import { FontAwesomeIcon } from "./icon.js"; +import datetime from "./mixins/datetime"; +import mobile from "./mixins/mobile"; +import publicMixin from "./mixins/public"; +import socket from "./mixins/socket"; +import theme from "./mixins/theme"; +import lang from "./mixins/lang"; +import { router } from "./router"; +import { appName } from "./util.ts"; +import dayjs from "dayjs"; +import timezone from "./modules/dayjs/plugin/timezone"; +import utc from "dayjs/plugin/utc"; +import relativeTime from "dayjs/plugin/relativeTime"; +import { loadToastSettings } from "./util-frontend"; +dayjs.extend(utc); +dayjs.extend(timezone); +dayjs.extend(relativeTime); + +const app = createApp({ + mixins: [socket, theme, mobile, datetime, publicMixin, lang], + data() { + return { + appName: appName, + }; + }, + render: () => h(App), +}); + +app.use(router); +app.use(i18n); + +app.use(Toast, loadToastSettings()); +app.component("Editable", contenteditable); +app.component("FontAwesomeIcon", FontAwesomeIcon); + +app.mount("#app"); + +// Service Worker +// Mainly for Webpush notification +if ("serviceWorker" in navigator) { + navigator.serviceWorker.register("/serviceWorker.js", { scope: "/" }).catch((error) => { + console.error("Service worker registration failed:", error); + }); +} + +// Expose the vue instance for development +if (process.env.NODE_ENV === "development") { + console.log("Dev Only: window.app is the vue instance"); + window.app = app._instance; +} diff --git a/src/mixins/datetime.js b/src/mixins/datetime.js new file mode 100644 index 0000000..eca5dfe --- /dev/null +++ b/src/mixins/datetime.js @@ -0,0 +1,141 @@ +import dayjs from "dayjs"; + +/** + * DateTime Mixin + * Handled timezone and format + */ +export default { + data() { + return { + userTimezone: localStorage.timezone || "auto", + }; + }, + + methods: { + /** + * Convert value to UTC + * @param {string | number | Date | dayjs.Dayjs} value Time + * value to convert + * @returns {dayjs.Dayjs} Converted time + */ + toUTC(value) { + return dayjs.tz(value, this.timezone).utc().format(); + }, + + /** + * Used for + * @param {string | number | Date | dayjs.Dayjs} value Value to + * convert + * @returns {string} Datetime string + */ + toDateTimeInputFormat(value) { + return this.datetimeFormat(value, "YYYY-MM-DDTHH:mm"); + }, + + /** + * Return a given value in the format YYYY-MM-DD HH:mm:ss + * @param {any} value Value to format as date time + * @returns {string} Formatted string + */ + datetime(value) { + return this.datetimeFormat(value, "YYYY-MM-DD HH:mm:ss"); + }, + + /** + * Converts a Unix timestamp to a formatted date and time string. + * @param {number} value - The Unix timestamp to convert. + * @returns {string} The formatted date and time string. + */ + unixToDateTime(value) { + return dayjs.unix(value).tz(this.timezone).format("YYYY-MM-DD HH:mm:ss"); + }, + + /** + * Converts a Unix timestamp to a dayjs object. + * @param {number} value - The Unix timestamp to convert. + * @returns {dayjs.Dayjs} The dayjs object representing the given timestamp. + */ + unixToDayjs(value) { + return dayjs.unix(value).tz(this.timezone); + }, + + /** + * Converts the given value to a dayjs object. + * @param {string} value - the value to be converted + * @returns {dayjs.Dayjs} a dayjs object in the timezone of this instance + */ + toDayjs(value) { + return dayjs.utc(value).tz(this.timezone); + }, + + /** + * Get time for maintenance + * @param {string | number | Date | dayjs.Dayjs} value Time to + * format + * @returns {string} Formatted string + */ + datetimeMaintenance(value) { + const inputDate = new Date(value); + const now = new Date(Date.now()); + + if ( + inputDate.getFullYear() === now.getUTCFullYear() && + inputDate.getMonth() === now.getUTCMonth() && + inputDate.getDay() === now.getUTCDay() + ) { + return this.datetimeFormat(value, "HH:mm"); + } else { + return this.datetimeFormat(value, "YYYY-MM-DD HH:mm"); + } + }, + + /** + * Return a given value in the format YYYY-MM-DD + * @param {any} value Value to format as date + * @returns {string} Formatted string + */ + date(value) { + return this.datetimeFormat(value, "YYYY-MM-DD"); + }, + + /** + * Return a given value in the format HH:mm or if second is set + * to true, HH:mm:ss + * @param {any} value Value to format + * @param {boolean} second Should seconds be included? + * @returns {string} Formatted string + */ + time(value, second = true) { + let secondString; + if (second) { + secondString = ":ss"; + } else { + secondString = ""; + } + return this.datetimeFormat(value, "HH:mm" + secondString); + }, + + /** + * Return a value in a custom format + * @param {any} value Value to format + * @param {any} format Format to return value in + * @returns {string} Formatted string + */ + datetimeFormat(value, format) { + if (value !== undefined && value !== "") { + return dayjs.utc(value).tz(this.timezone).format(format); + } + return ""; + }, + }, + + computed: { + timezone() { + if (this.userTimezone === "auto") { + return dayjs.tz.guess(); + } + + return this.userTimezone; + }, + }, +}; diff --git a/src/mixins/lang.js b/src/mixins/lang.js new file mode 100644 index 0000000..8ab8bd1 --- /dev/null +++ b/src/mixins/lang.js @@ -0,0 +1,39 @@ +import { currentLocale } from "../i18n"; +import { setPageLocale, timeDurationFormatter } from "../util-frontend"; +const langModules = import.meta.glob("../lang/*.json"); + +export default { + data() { + return { + language: currentLocale(), + }; + }, + + async created() { + if (this.language !== "en") { + await this.changeLang(this.language); + } + }, + + watch: { + async language(lang) { + await this.changeLang(lang); + }, + }, + + methods: { + /** + * Change the application language + * @param {string} lang Language code to switch to + * @returns {Promise} + */ + async changeLang(lang) { + let message = (await langModules["../lang/" + lang + ".json"]()).default; + this.$i18n.setLocaleMessage(lang, message); + this.$i18n.locale = lang; + localStorage.locale = lang; + setPageLocale(); + timeDurationFormatter.updateLocale(lang); + }, + }, +}; diff --git a/src/mixins/mobile.js b/src/mixins/mobile.js new file mode 100644 index 0000000..4734a00 --- /dev/null +++ b/src/mixins/mobile.js @@ -0,0 +1,41 @@ +export default { + data() { + return { + windowWidth: window.innerWidth, + }; + }, + + created() { + window.addEventListener("resize", this.onResize); + this.updateBody(); + }, + + methods: { + /** + * Handle screen resize + * @returns {void} + */ + onResize() { + this.windowWidth = window.innerWidth; + this.updateBody(); + }, + + /** + * Add css-class "mobile" to body if needed + * @returns {void} + */ + updateBody() { + if (this.isMobile) { + document.body.classList.add("mobile"); + } else { + document.body.classList.remove("mobile"); + } + }, + }, + + computed: { + isMobile() { + return this.windowWidth <= 767.98; + }, + }, +}; diff --git a/src/mixins/public.js b/src/mixins/public.js new file mode 100644 index 0000000..2010756 --- /dev/null +++ b/src/mixins/public.js @@ -0,0 +1,51 @@ +import axios from "axios"; +import { baseURL } from "../auth-client"; + +const env = process.env.NODE_ENV || "production"; + +// change the axios base url for development +axios.defaults.baseURL = baseURL; + +export default { + data() { + return { + publicGroupList: [], + }; + }, + computed: { + publicMonitorList() { + let result = {}; + + for (let group of this.publicGroupList) { + for (let monitor of group.monitorList) { + result[monitor.id] = monitor; + } + } + return result; + }, + + publicLastHeartbeatList() { + let result = {}; + + for (let monitorID in this.publicMonitorList) { + if (this.lastHeartbeatList[monitorID]) { + result[monitorID] = this.lastHeartbeatList[monitorID]; + } + } + + return result; + }, + + baseURL() { + if (this.$root.info.primaryBaseURL) { + return this.$root.info.primaryBaseURL; + } + + if (env === "development" || localStorage.dev === "dev") { + return axios.defaults.baseURL; + } else { + return location.protocol + "//" + location.host; + } + }, + }, +}; diff --git a/src/mixins/socket.js b/src/mixins/socket.js new file mode 100644 index 0000000..fb9bcba --- /dev/null +++ b/src/mixins/socket.js @@ -0,0 +1,782 @@ +import { io } from "socket.io-client"; +import { useToast } from "vue-toastification"; +import Favico from "favico.js"; +import dayjs from "dayjs"; +import mitt from "mitt"; + +import { DOWN, MAINTENANCE, PENDING, UP } from "../util.ts"; +import { getToastSuccessTimeout, getToastErrorTimeout } from "../util-frontend.js"; +import { logout as betterAuthLogout } from "../auth-client"; +const toast = useToast(); + +let socket; +let disconnectTimeout; + +const noSocketIOPages = [ + /^\/status-page$/, // /status-page + /^\/status/, // /status** + /^\/$/, // / +]; + +const favicon = new Favico({ + animation: "none", +}); + +export default { + data() { + return { + info: {}, + socket: { + firstConnect: true, + connected: false, + connectCount: 0, + initedSocketIO: false, + }, + username: null, + remember: localStorage.remember !== "0", + allowLoginDialog: false, // Allowed to show login dialog, but "loggedIn" have to be true too. This exists because prevent the login dialog show 0.1s in first before the socket server auth-ed. + loggedIn: false, + monitorList: {}, + monitorTypeList: {}, + maintenanceList: {}, + apiKeyList: {}, + heartbeatList: {}, + avgPingList: {}, + uptimeList: {}, + tlsInfoList: {}, + domainInfoList: {}, + notificationList: [], + dockerHostList: [], + remoteBrowserList: [], + statusPageListLoaded: false, + statusPageList: [], + proxyList: [], + connectionErrorMsg: `${this.$t("Cannot connect to the socket server.")} ${this.$t("Reconnecting...")}`, + showReverseProxyGuide: true, + cloudflared: { + cloudflareTunnelToken: "", + installed: null, + running: false, + message: "", + errorMessage: "", + currentPassword: "", + }, + faviconUpdateDebounce: null, + emitter: mitt(), + }; + }, + + created() { + this.initSocketIO(); + }, + + methods: { + /** + * Initialize connection to socket server + * @param {boolean} bypass Should the check for if we + * are on a status page be bypassed? + * @returns {void} + */ + initSocketIO(bypass = false) { + // No need to re-init + if (this.socket.initedSocketIO) { + return; + } + + // No need to connect to the socket.io for status page + if (!bypass && location.pathname) { + for (let page of noSocketIOPages) { + if (location.pathname.match(page)) { + return; + } + } + } + + // Also don't need to connect to the socket.io for setup database page + if (location.pathname === "/setup-database") { + return; + } + + this.socket.initedSocketIO = true; + + let protocol = location.protocol + "//"; + + let url; + const env = process.env.NODE_ENV || "production"; + if (env === "development" || localStorage.dev === "dev") { + url = protocol + location.hostname + ":3001"; + } else { + // Connect to the current url + url = undefined; + } + + socket = io(url, { + withCredentials: true, + }); + + socket.on("info", (info) => { + this.info = info; + }); + + socket.on("setup", () => { + this.$router.push("/setup"); + }); + + socket.on("session", (username) => { + this.loggedIn = true; + this.username = username; + }); + + socket.on("loginRequired", () => { + this.allowLoginDialog = true; + }); + + socket.on("monitorList", (data) => { + this.assignMonitorUrlParser(data); + this.monitorList = data; + }); + + socket.on("updateMonitorIntoList", (data) => { + this.assignMonitorUrlParser(data); + Object.entries(data).forEach(([monitorID, updatedMonitor]) => { + this.monitorList[monitorID] = updatedMonitor; + }); + }); + + socket.on("deleteMonitorFromList", (monitorID) => { + if (this.monitorList[monitorID]) { + delete this.monitorList[monitorID]; + } + }); + + socket.on("monitorTypeList", (data) => { + this.monitorTypeList = data; + }); + + socket.on("maintenanceList", (data) => { + this.maintenanceList = data; + }); + + socket.on("apiKeyList", (data) => { + this.apiKeyList = data; + }); + + socket.on("notificationList", (data) => { + this.notificationList = data; + }); + + socket.on("statusPageList", (data) => { + this.statusPageListLoaded = true; + this.statusPageList = data; + }); + + socket.on("proxyList", (data) => { + this.proxyList = data.map((item) => { + item.auth = !!item.auth; + item.active = !!item.active; + item.default = !!item.default; + + return item; + }); + }); + + socket.on("dockerHostList", (data) => { + this.dockerHostList = data; + }); + + socket.on("remoteBrowserList", (data) => { + this.remoteBrowserList = data; + }); + + socket.on("heartbeat", (data) => { + if (!(data.monitorID in this.heartbeatList)) { + this.heartbeatList[data.monitorID] = []; + } + + this.heartbeatList[data.monitorID].push(data); + + if (this.heartbeatList[data.monitorID].length >= 150) { + this.heartbeatList[data.monitorID].shift(); + } + + // Add to important list if it is important + // Also toast + if (data.important) { + if (this.monitorList[data.monitorID] !== undefined) { + if (data.status === 0) { + toast.error(`[${this.monitorList[data.monitorID].name}] [DOWN] ${data.msg}`, { + timeout: getToastErrorTimeout(), + }); + } else if (data.status === 1) { + toast.success(`[${this.monitorList[data.monitorID].name}] [Up] ${data.msg}`, { + timeout: getToastSuccessTimeout(), + }); + } else { + toast(`[${this.monitorList[data.monitorID].name}] ${data.msg}`); + } + } + + this.emitter.emit("newImportantHeartbeat", data); + } + }); + + socket.on("heartbeatList", (monitorID, data, overwrite = false) => { + if (!(monitorID in this.heartbeatList) || overwrite) { + this.heartbeatList[monitorID] = data; + } else { + this.heartbeatList[monitorID] = data.concat(this.heartbeatList[monitorID]); + } + }); + + socket.on("avgPing", (monitorID, data) => { + this.avgPingList[monitorID] = data; + }); + + socket.on("uptime", (monitorID, type, data) => { + this.uptimeList[`${monitorID}_${type}`] = data; + }); + + socket.on("certInfo", (monitorID, data) => { + this.tlsInfoList[monitorID] = JSON.parse(data); + }); + + socket.on("domainInfo", (monitorID, daysRemaining, expiresOn) => { + this.domainInfoList[monitorID] = { daysRemaining: daysRemaining, expiresOn: expiresOn }; + }); + + socket.on("connect_error", (err) => { + console.error(`Failed to connect to the backend. Socket.io connect_error: ${err.message}`); + this.connectionErrorMsg = `${this.$t("Cannot connect to the socket server.")} [${err}] ${this.$t("Reconnecting...")}`; + this.showReverseProxyGuide = true; + this.socket.connected = false; + this.socket.firstConnect = false; + }); + + socket.on("disconnect", () => { + console.log("disconnect"); + // As we are using cookie based auth, we have to reconnect the socket + // avoid noise by add a delay + disconnectTimeout = setTimeout(() => { + this.connectionErrorMsg = `${this.$t("Lost connection to the socket server.")} ${this.$t("Reconnecting...")}`; + this.socket.connected = false; + }, 2000); + }); + + socket.on("connect", () => { + console.log("Connected to the socket server"); + + clearTimeout(disconnectTimeout); + + this.socket.connectCount++; + this.socket.connected = true; + this.showReverseProxyGuide = false; + + // Reset Heartbeat list if it is re-connect + if (this.socket.connectCount >= 2) { + this.clearData(); + } + + this.socket.firstConnect = false; + }); + + // cloudflared + socket.on("cloudflared_installed", (res) => (this.cloudflared.installed = res)); + socket.on("cloudflared_running", (res) => (this.cloudflared.running = res)); + socket.on("cloudflared_message", (res) => (this.cloudflared.message = res)); + socket.on("cloudflared_errorMessage", (res) => (this.cloudflared.errorMessage = res)); + socket.on("cloudflared_token", (res) => (this.cloudflared.cloudflareTunnelToken = res)); + + socket.on("initServerTimezone", () => { + socket.emit("initServerTimezone", dayjs.tz.guess()); + }); + + socket.on("refresh", () => { + location.reload(); + }); + }, + /** + * parse all urls from list. + * @param {object} data Monitor data to modify + * @returns {object} list + */ + assignMonitorUrlParser(data) { + Object.entries(data).forEach(([monitorID, monitor]) => { + monitor.getUrl = () => { + try { + return new URL(monitor.url); + } catch (_) { + return null; + } + }; + }); + return data; + }, + + /** + * The storage currently in use + * @returns {Storage} Current storage + */ + storage() { + return this.remember ? localStorage : sessionStorage; + }, + + /** + * Get current socket + * @returns {Socket} Current socket + */ + getSocket() { + return socket; + }, + + /** + * Apply translation to a message if possible + * @param {string | {key: string, values: object}} msg Message to translate + * @returns {string} Translated message + */ + applyTranslation(msg) { + if (msg != null && typeof msg === "object") { + return this.$t(msg.key, msg.values); + } else { + return this.$t(msg); + } + }, + + /** + * Show success or error toast dependent on response status code + * @param {{ok:boolean, msg: string, msgi18n: false} | {ok:boolean, msg: string|{key: string, values: object}, msgi18n: true}} res Response object + * @returns {void} + */ + toastRes(res) { + if (res.msgi18n) { + res.msg = this.applyTranslation(res.msg); + } + + if (res.ok) { + toast.success(res.msg); + } else { + toast.error(res.msg); + } + }, + + /** + * Show a success toast + * @param {string} msg Message to show + * @returns {void} + */ + toastSuccess(msg) { + toast.success(this.$t(msg)); + }, + + /** + * Show an error toast + * @param {string} msg Message to show + * @returns {void} + */ + toastError(msg) { + toast.error(this.$t(msg)); + }, + + /** + * Log out of the web application + * @returns {void} + */ + async logout() { + await betterAuthLogout(() => { + console.log("Logged out"); + this.loggedIn = false; + this.username = null; + this.allowLoginDialog = false; + this.clearData(); + reconnectSocket(); + }); + }, + + /** + * Get list of monitors + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + getMonitorList(callback) { + if (!callback) { + callback = () => {}; + } + socket.emit("getMonitorList", callback); + }, + + /** + * Get list of maintenances + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + getMaintenanceList(callback) { + if (!callback) { + callback = () => {}; + } + socket.emit("getMaintenanceList", callback); + }, + + /** + * Send list of API keys + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + getAPIKeyList(callback) { + if (!callback) { + callback = () => {}; + } + socket.emit("getAPIKeyList", callback); + }, + + /** + * Add a monitor + * @param {object} monitor Object representing monitor to add + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + add(monitor, callback) { + socket.emit("add", monitor, callback); + }, + + /** + * Adds a maintenance + * @param {object} maintenance Maintenance to add + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + addMaintenance(maintenance, callback) { + socket.emit("addMaintenance", maintenance, callback); + }, + + /** + * Add monitors to maintenance + * @param {number} maintenanceID Maintenance to modify + * @param {number[]} monitors IDs of monitors to add + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + addMonitorMaintenance(maintenanceID, monitors, callback) { + socket.emit("addMonitorMaintenance", maintenanceID, monitors, callback); + }, + + /** + * Add status page to maintenance + * @param {number} maintenanceID Maintenance to modify + * @param {number} statusPages ID of status page to add + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + addMaintenanceStatusPage(maintenanceID, statusPages, callback) { + socket.emit("addMaintenanceStatusPage", maintenanceID, statusPages, callback); + }, + + /** + * Get monitors affected by maintenance + * @param {number} maintenanceID Maintenance to read + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + getMonitorMaintenance(maintenanceID, callback) { + socket.emit("getMonitorMaintenance", maintenanceID, callback); + }, + + /** + * Get status pages where maintenance is shown + * @param {number} maintenanceID Maintenance to read + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + getMaintenanceStatusPage(maintenanceID, callback) { + socket.emit("getMaintenanceStatusPage", maintenanceID, callback); + }, + + /** + * Delete monitor by ID + * @param {number} monitorID ID of monitor to delete + * @param {boolean} deleteChildren Whether to delete child monitors (for groups) + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + deleteMonitor(monitorID, deleteChildren, callback) { + socket.emit("deleteMonitor", monitorID, deleteChildren, callback); + }, + + /** + * Delete specified maintenance + * @param {number} maintenanceID Maintenance to delete + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + deleteMaintenance(maintenanceID, callback) { + socket.emit("deleteMaintenance", maintenanceID, callback); + }, + + /** + * Add an API key + * @param {object} key API key to add + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + addAPIKey(key, callback) { + socket.emit("addAPIKey", key, callback); + }, + + /** + * Delete specified API key + * @param {int} keyID ID of key to delete + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + deleteAPIKey(keyID, callback) { + socket.emit("deleteAPIKey", keyID, callback); + }, + + /** + * Clear the heartbeat list + * @returns {void} + */ + clearData() { + console.log("reset heartbeat list"); + this.heartbeatList = {}; + }, + + /** + * Upload the provided backup + * @param {string} uploadedJSON JSON to upload + * @param {string} importHandle Type of import. If set to + * most data in database will be replaced + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + uploadBackup(uploadedJSON, importHandle, callback) { + socket.emit("uploadBackup", uploadedJSON, importHandle, callback); + }, + + /** + * Clear events for a specified monitor + * @param {number} monitorID ID of monitor to clear + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + clearEvents(monitorID, callback) { + socket.emit("clearEvents", monitorID, callback); + }, + + /** + * Clear the heartbeats of a specified monitor + * @param {number} monitorID Id of monitor to clear + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + clearHeartbeats(monitorID, callback) { + socket.emit("clearHeartbeats", monitorID, callback); + }, + + /** + * Clear all statistics + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + clearStatistics(callback) { + socket.emit("clearStatistics", callback); + }, + + /** + * Get monitor beats for a specific monitor in a time range + * @param {number} monitorID ID of monitor to fetch + * @param {number} period Time in hours from now + * @param {socketCB} callback Callback for socket response + * @returns {void} + */ + getMonitorBeats(monitorID, period, callback) { + socket.emit("getMonitorBeats", monitorID, period, callback); + }, + + /** + * Retrieves monitor chart data. + * @param {string} monitorID - The ID of the monitor. + * @param {number} period - The time period for the chart data, in hours. + * @param {socketCB} callback - The callback function to handle the chart data. + * @returns {void} + */ + getMonitorChartData(monitorID, period, callback) { + socket.emit("getMonitorChartData", monitorID, period, callback); + }, + }, + + computed: { + usernameFirstChar() { + if (typeof this.username == "string" && this.username.length >= 1) { + return this.username.charAt(0).toUpperCase(); + } else { + return "🐻"; + } + }, + + lastHeartbeatList() { + let result = {}; + + for (let monitorID in this.heartbeatList) { + let index = this.heartbeatList[monitorID].length - 1; + result[monitorID] = this.heartbeatList[monitorID][index]; + } + + return result; + }, + + statusList() { + let result = {}; + + let unknown = { + text: this.$t("Unknown"), + color: "secondary", + }; + + for (let monitorID in this.lastHeartbeatList) { + let lastHeartBeat = this.lastHeartbeatList[monitorID]; + + if (!lastHeartBeat) { + result[monitorID] = unknown; + } else if (lastHeartBeat.status === UP) { + result[monitorID] = { + text: this.$t("Up"), + color: "primary", + }; + } else if (lastHeartBeat.status === DOWN) { + result[monitorID] = { + text: this.$t("Down"), + color: "danger", + }; + } else if (lastHeartBeat.status === PENDING) { + result[monitorID] = { + text: this.$t("Pending"), + color: "warning", + }; + } else if (lastHeartBeat.status === MAINTENANCE) { + result[monitorID] = { + text: this.$t("statusMaintenance"), + color: "maintenance", + }; + } else { + result[monitorID] = unknown; + } + } + + return result; + }, + + stats() { + let result = { + active: 0, + up: 0, + down: 0, + maintenance: 0, + pending: 0, + unknown: 0, + pause: 0, + }; + + for (let monitorID in this.$root.monitorList) { + let beat = this.$root.lastHeartbeatList[monitorID]; + let monitor = this.$root.monitorList[monitorID]; + + if (monitor && !monitor.active) { + result.pause++; + } else if (beat) { + result.active++; + if (beat.status === UP) { + result.up++; + } else if (beat.status === DOWN) { + result.down++; + } else if (beat.status === PENDING) { + result.pending++; + } else if (beat.status === MAINTENANCE) { + result.maintenance++; + } else { + result.unknown++; + } + } else { + result.unknown++; + } + } + + return result; + }, + + /** + * Frontend Version + * It should be compiled to a static value while building the frontend. + * Please see ./config/vite.config.js, it is defined via vite.js + * @returns {string} Current version + */ + frontendVersion() { + // eslint-disable-next-line no-undef + return FRONTEND_VERSION; + }, + + /** + * Are both frontend and backend in the same version? + * @returns {boolean} The frontend and backend match? + */ + isFrontendBackendVersionMatched() { + if (!this.info.version) { + return true; + } + return this.info.version === this.frontendVersion; + }, + }, + + watch: { + // Update Badge + "stats.down"(to, from) { + if (to !== from) { + if (this.faviconUpdateDebounce != null) { + clearTimeout(this.faviconUpdateDebounce); + } + this.faviconUpdateDebounce = setTimeout(() => { + favicon.badge(to); + }, 1000); + } + }, + + // Reload the SPA if the server version is changed. + "info.version"(to, from) { + // No need to refresh, when the version is not obtained, which means it is not logged in. + if (!to) { + return; + } + + if (from && from !== to) { + console.log(`Server version changed from ${from} to ${to}, reloading the page`); + window.location.reload(); + } + }, + + remember() { + localStorage.remember = this.remember ? "1" : "0"; + }, + + // Reconnect the socket io, if status-page to dashboard + "$route.fullPath"(newValue, oldValue) { + if (newValue) { + for (let page of noSocketIOPages) { + if (newValue.match(page)) { + return; + } + } + } + + this.initSocketIO(); + }, + }, +}; + +/** + * Reconnect the socket.io connection. + * Usage: Reconnect socket.io with auth-ed cookie, or drop the auth-ed session. + */ +export function reconnectSocket() { + if (socket) { + socket.disconnect(); + socket.connect(); + } +} diff --git a/src/mixins/theme.js b/src/mixins/theme.js new file mode 100644 index 0000000..dac11ab --- /dev/null +++ b/src/mixins/theme.js @@ -0,0 +1,109 @@ +export default { + data() { + return { + system: window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light", + userTheme: localStorage.theme, + userHeartbeatBar: localStorage.heartbeatBarTheme, + styleElapsedTime: localStorage.styleElapsedTime, + statusPageTheme: "light", + forceStatusPageTheme: false, + path: "", + }; + }, + + mounted() { + // Default Light + if (!this.userTheme) { + this.userTheme = "auto"; + } + + // Default Heartbeat Bar + if (!this.userHeartbeatBar) { + this.userHeartbeatBar = "normal"; + } + + // Default Elapsed Time Style + if (!this.styleElapsedTime) { + this.styleElapsedTime = "no-line"; + } + + document.body.classList.add(this.theme); + this.updateThemeColorMeta(); + }, + + computed: { + theme() { + // As entry can be status page now, set forceStatusPageTheme to true to use status page theme + if (this.forceStatusPageTheme) { + if (this.statusPageTheme === "auto") { + return this.system; + } + return this.statusPageTheme; + } + + // Entry no need dark + if (this.path === "") { + return "light"; + } + + if (this.path.startsWith("/status-page") || this.path.startsWith("/status")) { + if (this.statusPageTheme === "auto") { + return this.system; + } + return this.statusPageTheme; + } else { + if (this.userTheme === "auto") { + return this.system; + } + return this.userTheme; + } + }, + + isDark() { + return this.theme === "dark"; + }, + }, + + watch: { + "$route.fullPath"(path) { + this.path = path; + }, + + userTheme(to, from) { + localStorage.theme = to; + }, + + styleElapsedTime(to, from) { + localStorage.styleElapsedTime = to; + }, + + theme(to, from) { + document.body.classList.remove(from); + document.body.classList.add(this.theme); + this.updateThemeColorMeta(); + }, + + userHeartbeatBar(to, from) { + localStorage.heartbeatBarTheme = to; + }, + + heartbeatBarTheme(to, from) { + document.body.classList.remove(from); + document.body.classList.add(this.heartbeatBarTheme); + }, + }, + + methods: { + /** + * Update the theme color meta tag + * @returns {void} + */ + updateThemeColorMeta() { + if (this.theme === "dark") { + document.querySelector("#theme-color").setAttribute("content", "#161B22"); + } else { + document.querySelector("#theme-color").setAttribute("content", "#5cdd8b"); + } + }, + }, +}; diff --git a/src/modules/dayjs/constant.js b/src/modules/dayjs/constant.js new file mode 100644 index 0000000..741a623 --- /dev/null +++ b/src/modules/dayjs/constant.js @@ -0,0 +1,25 @@ +export let SECONDS_A_MINUTE = 60; +export let SECONDS_A_HOUR = SECONDS_A_MINUTE * 60; +export let SECONDS_A_DAY = SECONDS_A_HOUR * 24; +export let SECONDS_A_WEEK = SECONDS_A_DAY * 7; +export let MILLISECONDS_A_SECOND = 1e3; +export let MILLISECONDS_A_MINUTE = SECONDS_A_MINUTE * MILLISECONDS_A_SECOND; +export let MILLISECONDS_A_HOUR = SECONDS_A_HOUR * MILLISECONDS_A_SECOND; +export let MILLISECONDS_A_DAY = SECONDS_A_DAY * MILLISECONDS_A_SECOND; +export let MILLISECONDS_A_WEEK = SECONDS_A_WEEK * MILLISECONDS_A_SECOND; // English locales + +export let MS = "millisecond"; +export let S = "second"; +export let MIN = "minute"; +export let H = "hour"; +export let D = "day"; +export let W = "week"; +export let M = "month"; +export let Q = "quarter"; +export let Y = "year"; +export let DATE = "date"; +export let FORMAT_DEFAULT = "YYYY-MM-DDTHH:mm:ssZ"; +export let INVALID_DATE_STRING = "Invalid Date"; // regex + +export let REGEX_PARSE = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/; +export let REGEX_FORMAT = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g; diff --git a/src/modules/dayjs/plugin/timezone/index.d.ts b/src/modules/dayjs/plugin/timezone/index.d.ts new file mode 100644 index 0000000..ce8e98e --- /dev/null +++ b/src/modules/dayjs/plugin/timezone/index.d.ts @@ -0,0 +1,20 @@ +import { PluginFunc, ConfigType } from "dayjs/esm"; + +declare const plugin: PluginFunc; +export = plugin; + +declare module "dayjs/esm" { + 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; + } + + const tz: DayjsTimezone; +} diff --git a/src/modules/dayjs/plugin/timezone/index.js b/src/modules/dayjs/plugin/timezone/index.js new file mode 100644 index 0000000..c377c00 --- /dev/null +++ b/src/modules/dayjs/plugin/timezone/index.js @@ -0,0 +1,204 @@ +/** + * Copy from node_modules/dayjs/plugin/timezone.js + * Try to fix https://github.com/louislam/uptime-kuma/issues/2318 + * Source: https://github.com/iamkun/dayjs/tree/dev/src/plugin/utc + * License: MIT + */ +import { MIN, MS } from "../../constant"; +let typeToPos = { + year: 0, + month: 1, + day: 2, + hour: 3, + minute: 4, + second: 5, +}; // Cache time-zone lookups from Intl.DateTimeFormat, +// as it is a *very* slow method. + +let dtfCache = {}; + +let getDateTimeFormat = function getDateTimeFormat(timezone, options) { + if (options === void 0) { + options = {}; + } + + let timeZoneName = options.timeZoneName || "short"; + let key = timezone + "|" + timeZoneName; + let dtf = dtfCache[key]; + + if (!dtf) { + dtf = new Intl.DateTimeFormat("en-US", { + hour12: false, + timeZone: timezone, + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + timeZoneName: timeZoneName, + }); + dtfCache[key] = dtf; + } + + return dtf; +}; + +export default (function (o, c, d) { + let defaultTimezone; + + let makeFormatParts = function makeFormatParts(timestamp, timezone, options) { + if (options === void 0) { + options = {}; + } + + let date = new Date(timestamp); + let dtf = getDateTimeFormat(timezone, options); + return dtf.formatToParts(date); + }; + + let tzOffset = function tzOffset(timestamp, timezone) { + let formatResult = makeFormatParts(timestamp, timezone); + let filled = []; + + for (let i = 0; i < formatResult.length; i += 1) { + let _formatResult$i = formatResult[i]; + let type = _formatResult$i.type; + let value = _formatResult$i.value; + let pos = typeToPos[type]; + + if (pos >= 0) { + filled[pos] = parseInt(value, 10); + } + } + + let hour = filled[3]; // Workaround for the same behavior in different node version + // https://github.com/nodejs/node/issues/33027 + + /* istanbul ignore next */ + + let fixedHour = hour === 24 ? 0 : hour; + let utcString = + filled[0] + + "-" + + filled[1] + + "-" + + filled[2] + + " " + + fixedHour + + ":" + + filled[4] + + ":" + + filled[5] + + ":000"; + let utcTs = d.utc(utcString).valueOf(); + let asTS = +timestamp; + let over = asTS % 1000; + asTS -= over; + return (utcTs - asTS) / (60 * 1000); + }; // find the right offset a given local time. The o input is our guess, which determines which + // offset we'll pick in ambiguous cases (e.g. there are two 3 AMs b/c Fallback DST) + // https://github.com/moment/luxon/blob/master/src/datetime.js#L76 + + let fixOffset = function fixOffset(localTS, o0, tz) { + // Our UTC time is just a guess because our offset is just a guess + let utcGuess = localTS - o0 * 60 * 1000; // Test whether the zone matches the offset for this ts + + let o2 = tzOffset(utcGuess, tz); // If so, offset didn't change and we're done + + if (o0 === o2) { + return [utcGuess, o0]; + } // If not, change the ts by the difference in the offset + + utcGuess -= (o2 - o0) * 60 * 1000; // If that gives us the local time we want, we're done + + let o3 = tzOffset(utcGuess, tz); + + if (o2 === o3) { + return [utcGuess, o2]; + } // If it's different, we're in a hole time. + // The offset has changed, but the we don't adjust the time + + return [localTS - Math.min(o2, o3) * 60 * 1000, Math.max(o2, o3)]; + }; + + let proto = c.prototype; + + proto.tz = function (timezone, keepLocalTime) { + if (timezone === void 0) { + timezone = defaultTimezone; + } + + let oldOffset = this.utcOffset(); + let date = this.toDate(); + let target = date + .toLocaleString("en-US", { + timeZone: timezone, + }) + .replace("\u202f", " "); + let diff = Math.round((date - new Date(target)) / 1000 / 60); + let ins = d(target) + .$set(MS, this.$ms) + .utcOffset(-Math.round(date.getTimezoneOffset() / 15) * 15 - diff, true); + + if (keepLocalTime) { + let newOffset = ins.utcOffset(); + ins = ins.add(oldOffset - newOffset, MIN); + } + + ins.$x.$timezone = timezone; + return ins; + }; + + proto.offsetName = function (type) { + // type: short(default) / long + let zone = this.$x.$timezone || d.tz.guess(); + let result = makeFormatParts(this.valueOf(), zone, { + timeZoneName: type, + }).find(function (m) { + return m.type.toLowerCase() === "timezonename"; + }); + return result && result.value; + }; + + let oldStartOf = proto.startOf; + + proto.startOf = function (units, startOf) { + if (!this.$x || !this.$x.$timezone) { + return oldStartOf.call(this, units, startOf); + } + + let withoutTz = d(this.format("YYYY-MM-DD HH:mm:ss:SSS")); + let startOfWithoutTz = oldStartOf.call(withoutTz, units, startOf); + return startOfWithoutTz.tz(this.$x.$timezone, true); + }; + + d.tz = function (input, arg1, arg2) { + let parseFormat = arg2 && arg1; + let timezone = arg2 || arg1 || defaultTimezone; + let previousOffset = tzOffset(+d(), timezone); + + if (typeof input !== "string") { + // timestamp number || js Date || Day.js + return d(input).tz(timezone); + } + + let localTs = d.utc(input, parseFormat).valueOf(); + + let _fixOffset = fixOffset(localTs, previousOffset, timezone); + let targetTs = _fixOffset[0]; + let targetOffset = _fixOffset[1]; + + let ins = d(targetTs).utcOffset(targetOffset); + ins.$x.$timezone = timezone; + return ins; + }; + + d.tz.guess = function () { + return Intl.DateTimeFormat().resolvedOptions().timeZone; + }; + + d.tz.setDefault = function (timezone) { + defaultTimezone = timezone; + }; +}); diff --git a/src/pages/AddStatusPage.vue b/src/pages/AddStatusPage.vue new file mode 100644 index 0000000..83d67fe --- /dev/null +++ b/src/pages/AddStatusPage.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/src/pages/Dashboard.vue b/src/pages/Dashboard.vue new file mode 100644 index 0000000..5f019da --- /dev/null +++ b/src/pages/Dashboard.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/pages/DashboardHome.vue b/src/pages/DashboardHome.vue new file mode 100644 index 0000000..344d4c2 --- /dev/null +++ b/src/pages/DashboardHome.vue @@ -0,0 +1,350 @@ + + + + + diff --git a/src/pages/Details.vue b/src/pages/Details.vue new file mode 100644 index 0000000..3cdc594 --- /dev/null +++ b/src/pages/Details.vue @@ -0,0 +1,1052 @@ + + + + + diff --git a/src/pages/EditMaintenance.vue b/src/pages/EditMaintenance.vue new file mode 100644 index 0000000..4376d6c --- /dev/null +++ b/src/pages/EditMaintenance.vue @@ -0,0 +1,949 @@ + + + + + diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue new file mode 100644 index 0000000..1ac73ce --- /dev/null +++ b/src/pages/EditMonitor.vue @@ -0,0 +1,4644 @@ + + + + + diff --git a/src/pages/Entry.vue b/src/pages/Entry.vue new file mode 100644 index 0000000..1733dc6 --- /dev/null +++ b/src/pages/Entry.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/pages/List.vue b/src/pages/List.vue new file mode 100644 index 0000000..2d8db2d --- /dev/null +++ b/src/pages/List.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/src/pages/ManageMaintenance.vue b/src/pages/ManageMaintenance.vue new file mode 100644 index 0000000..acfcf1a --- /dev/null +++ b/src/pages/ManageMaintenance.vue @@ -0,0 +1,338 @@ + + + + + diff --git a/src/pages/ManageStatusPage.vue b/src/pages/ManageStatusPage.vue new file mode 100644 index 0000000..bf1b537 --- /dev/null +++ b/src/pages/ManageStatusPage.vue @@ -0,0 +1,204 @@ + + + + + diff --git a/src/pages/NotFound.vue b/src/pages/NotFound.vue new file mode 100644 index 0000000..1fbd006 --- /dev/null +++ b/src/pages/NotFound.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue new file mode 100644 index 0000000..c4210f9 --- /dev/null +++ b/src/pages/Settings.vue @@ -0,0 +1,318 @@ + + + + + diff --git a/src/pages/Setup.vue b/src/pages/Setup.vue new file mode 100644 index 0000000..ccf077d --- /dev/null +++ b/src/pages/Setup.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/src/pages/SetupDatabase.vue b/src/pages/SetupDatabase.vue new file mode 100644 index 0000000..264bf82 --- /dev/null +++ b/src/pages/SetupDatabase.vue @@ -0,0 +1,325 @@ + + + + + diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue new file mode 100644 index 0000000..8627f28 --- /dev/null +++ b/src/pages/StatusPage.vue @@ -0,0 +1,1798 @@ + + + + + diff --git a/src/router.js b/src/router.js new file mode 100644 index 0000000..36d2587 --- /dev/null +++ b/src/router.js @@ -0,0 +1,198 @@ +import { createRouter, createWebHistory } from "vue-router"; + +import EmptyLayout from "./layouts/EmptyLayout.vue"; +import Layout from "./layouts/Layout.vue"; +import Dashboard from "./pages/Dashboard.vue"; +import DashboardHome from "./pages/DashboardHome.vue"; +import Details from "./pages/Details.vue"; +import EditMonitor from "./pages/EditMonitor.vue"; +import EditMaintenance from "./pages/EditMaintenance.vue"; +import List from "./pages/List.vue"; +const Settings = () => import("./pages/Settings.vue"); +import Setup from "./pages/Setup.vue"; +import StatusPage from "./pages/StatusPage.vue"; +import Entry from "./pages/Entry.vue"; +import ManageStatusPage from "./pages/ManageStatusPage.vue"; +import AddStatusPage from "./pages/AddStatusPage.vue"; +import NotFound from "./pages/NotFound.vue"; +import DockerHosts from "./components/settings/Docker.vue"; +import ManageMaintenance from "./pages/ManageMaintenance.vue"; +import APIKeys from "./components/settings/APIKeys.vue"; +import SetupDatabase from "./pages/SetupDatabase.vue"; + +// Settings - Sub Pages +import Appearance from "./components/settings/Appearance.vue"; +import General from "./components/settings/General.vue"; +const Notifications = () => import("./components/settings/Notifications.vue"); +import ReverseProxy from "./components/settings/ReverseProxy.vue"; +import Tags from "./components/settings/Tags.vue"; +import MonitorHistory from "./components/settings/MonitorHistory.vue"; +const Security = () => import("./components/settings/Security.vue"); +import Proxies from "./components/settings/Proxies.vue"; +import About from "./components/settings/About.vue"; +import RemoteBrowsers from "./components/settings/RemoteBrowsers.vue"; + +const routes = [ + { + path: "/", + component: Entry, + }, + { + // If it is "/dashboard", the active link is not working + // If it is "", it overrides the "/" unexpectedly + // Give a random name to solve the problem. + path: "/empty", + component: Layout, + children: [ + { + path: "", + component: Dashboard, + children: [ + { + name: "DashboardHome", + path: "/dashboard", + component: DashboardHome, + children: [ + { + path: "/dashboard/:id", + component: EmptyLayout, + children: [ + { + path: "", + component: Details, + }, + { + path: "/edit/:id", + component: EditMonitor, + }, + ], + }, + ], + }, + { + path: "/add", + component: EditMonitor, + children: [ + { + path: "/clone/:id", + component: EditMonitor, + }, + ], + }, + { + path: "/list", + component: List, + }, + { + path: "/settings", + component: Settings, + children: [ + { + path: "general", + component: General, + }, + { + path: "appearance", + component: Appearance, + }, + { + path: "notifications", + component: Notifications, + }, + { + path: "reverse-proxy", + component: ReverseProxy, + }, + { + path: "tags", + component: Tags, + }, + { + path: "monitor-history", + component: MonitorHistory, + }, + { + path: "docker-hosts", + component: DockerHosts, + }, + { + path: "remote-browsers", + component: RemoteBrowsers, + }, + { + path: "security", + component: Security, + }, + { + path: "api-keys", + component: APIKeys, + }, + { + path: "proxies", + component: Proxies, + }, + { + path: "about", + component: About, + }, + ], + }, + { + path: "/manage-status-page", + component: ManageStatusPage, + }, + { + path: "/add-status-page", + component: AddStatusPage, + }, + { + path: "/maintenance", + component: ManageMaintenance, + }, + { + path: "/add-maintenance", + component: EditMaintenance, + }, + { + path: "/maintenance/edit/:id", + component: EditMaintenance, + }, + { + path: "/maintenance/clone/:id", + component: EditMaintenance, + }, + ], + }, + ], + }, + { + path: "/setup", + component: Setup, + }, + { + path: "/setup-database", + component: SetupDatabase, + }, + { + path: "/status-page", + component: StatusPage, + }, + { + path: "/status", + component: StatusPage, + }, + { + path: "/status/:slug", + component: StatusPage, + }, + { + path: "/:pathMatch(.*)*", + component: NotFound, + }, +]; + +export const router = createRouter({ + linkActiveClass: "active", + history: createWebHistory(), + routes, +}); diff --git a/src/util-frontend.js b/src/util-frontend.js new file mode 100644 index 0000000..2ea45af --- /dev/null +++ b/src/util-frontend.js @@ -0,0 +1,255 @@ +import dayjs from "dayjs"; +import { getTimeZones } from "@vvo/tzdb"; +import { localeDirection, currentLocale } from "./i18n"; +import { POSITION } from "vue-toastification"; +import { baseURL } from "./auth-client"; + +/** + * Returns the offset from UTC in hours for the current locale. + * @param {string} timeZone Timezone to get offset for + * @returns {number} The offset from UTC in hours. + * + * Generated by Trelent + */ +function getTimezoneOffset(timeZone) { + const now = new Date(); + const tzString = now.toLocaleString("en-US", { + timeZone, + }); + const localString = now.toLocaleString("en-US"); + const diff = (Date.parse(localString) - Date.parse(tzString)) / 3600000; + const offset = diff + now.getTimezoneOffset() / 60; + return -offset; +} + +/** + * Returns a list of timezones sorted by their offset from UTC. + * @returns {object[]} A list of the given timezones sorted by their offset from UTC. + * + * Generated by Trelent + */ +export function timezoneList() { + let result = []; + const timeZones = getTimeZones(); + + for (let timezone of timeZones) { + try { + let display = dayjs().tz(timezone.name).format("Z"); + + result.push({ + name: `(UTC${display}) ${timezone.name}`, + value: timezone.name, + time: getTimezoneOffset(timezone.name), + }); + } catch (e) { + // Skipping not supported timezone.name by dayjs + } + } + + result.sort((a, b) => { + if (a.time > b.time) { + return 1; + } + + if (b.time > a.time) { + return -1; + } + + return 0; + }); + + return result; +} + +/** + * Set the locale of the HTML page + * @returns {void} + */ +export function setPageLocale() { + const html = document.documentElement; + html.setAttribute("lang", currentLocale()); + html.setAttribute("dir", localeDirection()); +} + +/** + * Get the base URL + * Mainly used for dev, because the backend and the frontend are in different ports. + * @returns {string} Base URL + */ +export function getResBaseURL() { + return baseURL; +} + +/** + * Get the tag color options + * Shared between components + * @param {any} self Component + * @returns {object[]} Colour options + */ +export function colorOptions(self) { + return [ + { name: self.$t("Gray"), color: "#4B5563" }, + { name: self.$t("Red"), color: "#DC2626" }, + { name: self.$t("Orange"), color: "#D97706" }, + { name: self.$t("Green"), color: "#059669" }, + { name: self.$t("Blue"), color: "#2563EB" }, + { name: self.$t("Indigo"), color: "#4F46E5" }, + { name: self.$t("Purple"), color: "#7C3AED" }, + { name: self.$t("Pink"), color: "#DB2777" }, + ]; +} + +/** + * Loads the toast timeout settings from storage. + * @returns {object} The toast plugin options object. + */ +export function loadToastSettings() { + return { + position: POSITION.BOTTOM_RIGHT, + containerClassName: "toast-container mb-5", + showCloseButtonOnHover: true, + + filterBeforeCreate: (toast, toasts) => { + if (toast.timeout === 0) { + return false; + } else { + return toast; + } + }, + }; +} + +/** + * Get timeout for success toasts + * @returns {(number|boolean)} Timeout in ms. If false timeout disabled. + */ +export function getToastSuccessTimeout() { + let successTimeout = 20000; + + if (localStorage.toastSuccessTimeout !== undefined) { + const parsedTimeout = parseInt(localStorage.toastSuccessTimeout); + if (parsedTimeout != null && !Number.isNaN(parsedTimeout)) { + successTimeout = parsedTimeout; + } + } + + if (successTimeout === -1) { + successTimeout = false; + } + + return successTimeout; +} + +/** + * Get timeout for error toasts + * @returns {(number|boolean)} Timeout in ms. If false timeout disabled. + */ +export function getToastErrorTimeout() { + let errorTimeout = -1; + + if (localStorage.toastErrorTimeout !== undefined) { + const parsedTimeout = parseInt(localStorage.toastErrorTimeout); + if (parsedTimeout != null && !Number.isNaN(parsedTimeout)) { + errorTimeout = parsedTimeout; + } + } + + if (errorTimeout === -1) { + errorTimeout = false; + } + + return errorTimeout; +} + +class TimeDurationFormatter { + /** + * Default locale and options for Time Duration Formatter (supports both DurationFormat and RelativeTimeFormat) + */ + constructor() { + this.durationFormatOptions = { style: "long" }; + this.relativeTimeFormatOptions = { numeric: "always" }; + if (Intl.DurationFormat !== undefined) { + this.durationFormatInstance = new Intl.DurationFormat(currentLocale(), this.durationFormatOptions); + } else { + this.relativeTimeFormatInstance = new Intl.RelativeTimeFormat( + currentLocale(), + this.relativeTimeFormatOptions + ); + } + } + + /** + * Method to update the instance locale and options + * @param {string} locale Localization identifier (e.g., "en", "ar-sy") to update the instance with. + * @returns {void} No return value. + */ + updateLocale(locale) { + if (Intl.DurationFormat !== undefined) { + this.durationFormatInstance = new Intl.DurationFormat(locale, this.durationFormatOptions); + } else { + this.relativeTimeFormatInstance = new Intl.RelativeTimeFormat(locale, this.relativeTimeFormatOptions); + } + } + + /** + * Method to convert seconds into Human readable format + * @param {number} seconds Receive value in seconds. + * @returns {string} String converted to Days Mins Seconds Format + */ + secondsToHumanReadableFormat(seconds) { + const days = Math.floor(seconds / 86400); + const hours = Math.floor((seconds % 86400) / 3600); + const minutes = Math.floor(((seconds % 86400) % 3600) / 60); + const secs = ((seconds % 86400) % 3600) % 60; + + if (this.durationFormatInstance !== undefined) { + // use Intl.DurationFormat if available + return this.durationFormatInstance.format({ + days, + hours, + minutes, + seconds: secs, + }); + } + + const parts = []; + /** + * Build the formatted string from parts + * 1. Get the relative time formatted parts from the instance. + * 2. Filter out the relevant parts literal (unit of time) or integer (value). + * 3. Map out the required values. + * @param {number} value Receives value in seconds. + * @param {string} unitOfTime Expected unit of time after conversion. + * @returns {void} + */ + const toFormattedPart = (value, unitOfTime) => { + const partsArray = this.relativeTimeFormatInstance.formatToParts(value, unitOfTime); + const filteredParts = partsArray + .filter((part, index) => part.type === "integer" || (part.type === "literal" && index > 0)) + .map((part) => part.value); + + const formattedString = filteredParts.join("").trim(); + parts.push(formattedString); + }; + + if (days > 0) { + toFormattedPart(days, "day"); + } + if (hours > 0) { + toFormattedPart(hours, "hour"); + } + if (minutes > 0) { + toFormattedPart(minutes, "minute"); + } + if (secs > 0) { + toFormattedPart(secs, "second"); + } + + if (parts.length > 0) { + return `${parts.join(" ")}`; + } + return this.relativeTimeFormatInstance.format(0, "second"); // Handle case for 0 seconds + } +} + +export const timeDurationFormatter = new TimeDurationFormatter(); diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000..a1f6688 --- /dev/null +++ b/src/util.ts @@ -0,0 +1,805 @@ +/* eslint-disable camelcase */ +/*! +// Common Util for frontend and backend +*/ + +import dayjsFrontend from "dayjs"; + +// For dayjs plugins' type checking, don't remove event though it is not used in this file +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import * as timezone from "dayjs/plugin/timezone"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import * as utc from "dayjs/plugin/utc"; + +import jsonata from "jsonata"; + +/** + * Check if NODE_ENV is development + * @returns yes or no + */ +export function isDevEnv() { + return process.env.NODE_ENV === "development"; +} + +export const isNode = typeof process !== "undefined" && process?.versions?.node; + +/** + * Smarter dayjs import that supports both frontend and backend + * @returns {dayjs.Dayjs} dayjs instance + */ +const dayjs = isNode ? require("dayjs") : dayjsFrontend; + +export const devOriginList = [ + "http://127.0.0.1:3000", + "http://127.0.0.1:3001", + "http://localhost:3000", + "http://localhost:3001", +]; + +export const appName = "Uptime Kuma"; +export const DOWN = 0; +export const UP = 1; +export const PENDING = 2; +export const MAINTENANCE = 3; + +export const STATUS_PAGE_ALL_DOWN = 0; +export const STATUS_PAGE_ALL_UP = 1; +export const STATUS_PAGE_PARTIAL_DOWN = 2; +export const STATUS_PAGE_MAINTENANCE = 3; + +export const SQL_DATE_FORMAT = "YYYY-MM-DD"; +export const SQL_DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss"; +export const SQL_DATETIME_FORMAT_WITHOUT_SECOND = "YYYY-MM-DD HH:mm"; + +export const MIN_INTERVAL_SECOND = 1; // 1 second + +export const INCIDENT_PAGE_SIZE = 10; + +// Packet Size limits +export const PING_PACKET_SIZE_MIN = 1; +export const PING_PACKET_SIZE_MAX = 65500; +export const PING_PACKET_SIZE_DEFAULT = 56; + +// Global timeout (aka deadline) limits in seconds +export const PING_GLOBAL_TIMEOUT_MIN = 1; +export const PING_GLOBAL_TIMEOUT_MAX = 300; +export const PING_GLOBAL_TIMEOUT_DEFAULT = 10; + +// Ping count limits +export const PING_COUNT_MIN = 1; +export const PING_COUNT_MAX = 100; +export const PING_COUNT_DEFAULT = 1; + +// per-request timeout (aka timeout) limits in seconds +export const PING_PER_REQUEST_TIMEOUT_MIN = 1; +export const PING_PER_REQUEST_TIMEOUT_MAX = 60; +export const PING_PER_REQUEST_TIMEOUT_DEFAULT = 2; + +/** + * Response body length cutoff used by default (10kb) + * (measured in bytes) + */ +export const RESPONSE_BODY_LENGTH_DEFAULT = 1024; +/** + * Maximum allowed response body length to store (1mb) + * (measured in bytes) + */ +export const RESPONSE_BODY_LENGTH_MAX = 1024 * 1024; + +// Console colors +// https://stackoverflow.com/questions/9781218/how-to-change-node-jss-console-font-color +export const CONSOLE_STYLE_Reset = "\x1b[0m"; +export const CONSOLE_STYLE_Bright = "\x1b[1m"; +export const CONSOLE_STYLE_Dim = "\x1b[2m"; +export const CONSOLE_STYLE_Underscore = "\x1b[4m"; +export const CONSOLE_STYLE_Blink = "\x1b[5m"; +export const CONSOLE_STYLE_Reverse = "\x1b[7m"; +export const CONSOLE_STYLE_Hidden = "\x1b[8m"; + +export const CONSOLE_STYLE_FgBlack = "\x1b[30m"; +export const CONSOLE_STYLE_FgRed = "\x1b[31m"; +export const CONSOLE_STYLE_FgGreen = "\x1b[32m"; +export const CONSOLE_STYLE_FgYellow = "\x1b[33m"; +export const CONSOLE_STYLE_FgBlue = "\x1b[34m"; +export const CONSOLE_STYLE_FgMagenta = "\x1b[35m"; +export const CONSOLE_STYLE_FgCyan = "\x1b[36m"; +export const CONSOLE_STYLE_FgWhite = "\x1b[37m"; +export const CONSOLE_STYLE_FgGray = "\x1b[90m"; +export const CONSOLE_STYLE_FgOrange = "\x1b[38;5;208m"; +export const CONSOLE_STYLE_FgLightGreen = "\x1b[38;5;119m"; +export const CONSOLE_STYLE_FgLightBlue = "\x1b[38;5;117m"; +export const CONSOLE_STYLE_FgViolet = "\x1b[38;5;141m"; +export const CONSOLE_STYLE_FgBrown = "\x1b[38;5;130m"; +export const CONSOLE_STYLE_FgPink = "\x1b[38;5;219m"; + +export const CONSOLE_STYLE_BgBlack = "\x1b[40m"; +export const CONSOLE_STYLE_BgRed = "\x1b[41m"; +export const CONSOLE_STYLE_BgGreen = "\x1b[42m"; +export const CONSOLE_STYLE_BgYellow = "\x1b[43m"; +export const CONSOLE_STYLE_BgBlue = "\x1b[44m"; +export const CONSOLE_STYLE_BgMagenta = "\x1b[45m"; +export const CONSOLE_STYLE_BgCyan = "\x1b[46m"; +export const CONSOLE_STYLE_BgWhite = "\x1b[47m"; +export const CONSOLE_STYLE_BgGray = "\x1b[100m"; + +const consoleModuleColors = [ + CONSOLE_STYLE_FgCyan, + CONSOLE_STYLE_FgGreen, + CONSOLE_STYLE_FgLightGreen, + CONSOLE_STYLE_FgBlue, + CONSOLE_STYLE_FgLightBlue, + CONSOLE_STYLE_FgMagenta, + CONSOLE_STYLE_FgOrange, + CONSOLE_STYLE_FgViolet, + CONSOLE_STYLE_FgBrown, + CONSOLE_STYLE_FgPink, +]; + +type LogLevel = "info" | "warn" | "error" | "debug"; + +const consoleLevelColors = { + info: CONSOLE_STYLE_FgCyan, + warn: CONSOLE_STYLE_FgYellow, + error: CONSOLE_STYLE_FgRed, + debug: CONSOLE_STYLE_FgGray, +} as const; + +export const badgeConstants = { + naColor: "#999", + defaultUpColor: "#66c20a", + defaultWarnColor: "#eed202", + defaultDownColor: "#c2290a", + defaultPendingColor: "#f8a306", + defaultMaintenanceColor: "#1747f5", + defaultPingColor: "blue", // as defined by badge-maker / shields.io + defaultStyle: "flat", + defaultPingValueSuffix: "ms", + defaultPingLabelSuffix: "h", + defaultUptimeValueSuffix: "%", + defaultUptimeLabelSuffix: "h", + defaultCertExpValueSuffix: " days", + defaultCertExpLabelSuffix: "h", + // Values Come From Default Notification Times + defaultCertExpireWarnDays: "14", + defaultCertExpireDownDays: "7", +}; + +/** + * Flip the status of s between UP and DOWN if this is possible + * @param s status + * @returns flipped status + */ +export function flipStatus(s: number) { + if (s === UP) { + return DOWN; + } + + if (s === DOWN) { + return UP; + } + + return s; +} + +/** + * Delays for specified number of seconds + * @param ms Number of milliseconds to sleep for + */ +export function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +/** + * PHP's ucfirst + * @param str string input + * @returns string with first letter capitalized + */ +export function ucfirst(str: string) { + if (!str) { + return str; + } + + const firstLetter = str.substr(0, 1); + return firstLetter.toUpperCase() + str.substr(1); +} + +/** + * @deprecated Use log.debug (https://github.com/louislam/uptime-kuma/pull/910) + * @param msg Message to write + */ +export function debug(msg: unknown) { + log.log("", "debug", msg); +} + +class Logger { + /** + * UPTIME_KUMA_HIDE_LOG=debug_monitor,info_monitor + * + * Example: + * [ + * "debug_monitor", // Hide all logs that level is debug and the module is monitor + * "info_monitor", + * ] + */ + hideLog: Record = { + info: [], + warn: [], + error: [], + debug: [], + }; + + /** + * + */ + constructor() { + if (typeof process !== "undefined" && process.env.UPTIME_KUMA_HIDE_LOG) { + const list = process.env.UPTIME_KUMA_HIDE_LOG.split(",").map((v) => v.toLowerCase()); + + for (const pair of list) { + // split first "_" only + const values = pair.split(/_(.*)/s); + + if (values.length >= 2) { + this.hideLog[values[0]].push(values[1]); + } + } + + this.debug("server", "UPTIME_KUMA_HIDE_LOG is set"); + this.debug("server", this.hideLog); + } + } + + /** + * Write a message to the log + * @param module The module the log comes from + * @param level Log level. One of info, warn, error, debug. + * @param msg Message to write + */ + log(module: string, level: LogLevel, ...msg: unknown[]) { + if (level === "debug" && !isDevEnv()) { + return; + } + + if (this.hideLog[level] && this.hideLog[level].includes(module.toLowerCase())) { + return; + } + + module = module.toUpperCase(); + const levelLabel = level.toUpperCase(); + + let now; + if (dayjs.tz) { + now = dayjs.tz(new Date()).format(); + } else { + now = dayjs().format(); + } + + if (process.env.UPTIME_KUMA_LOG_FORMAT === "json") { + const msgString = msg + .map((m) => { + if (typeof m === "string") { + return m; + } else { + try { + return JSON.stringify(m); + } catch { + return String(m); + } + } + }) + .join(" "); + + console.log( + JSON.stringify({ + time: now, + module: module, + level: level, + msg: msgString, + }) + ); + return; + } + + const levelColor = consoleLevelColors[level]; + const moduleColor = consoleModuleColors[intHash(module, consoleModuleColors.length)]; + + let timePart: string; + let modulePart: string; + let levelPart: string; + + if (isNode) { + // Add console colors + switch (level) { + case "debug": + timePart = CONSOLE_STYLE_FgGray + now + CONSOLE_STYLE_Reset; + break; + default: + timePart = CONSOLE_STYLE_FgCyan + now + CONSOLE_STYLE_Reset; + break; + } + + modulePart = "[" + moduleColor + module + CONSOLE_STYLE_Reset + "]"; + levelPart = levelColor + `${levelLabel}:` + CONSOLE_STYLE_Reset; + } else { + // No console colors + timePart = now; + modulePart = `[${module}]`; + levelPart = `${levelLabel}:`; + } + + // Write to console + switch (level) { + case "error": + console.error(timePart, modulePart, levelPart, ...msg); + break; + case "warn": + console.warn(timePart, modulePart, levelPart, ...msg); + break; + case "info": + console.info(timePart, modulePart, levelPart, ...msg); + break; + case "debug": + if (isDevEnv()) { + console.debug(timePart, modulePart, levelPart, ...msg); + } + break; + default: + console.log(timePart, modulePart, levelPart, ...msg); + break; + } + } + + /** + * Log an INFO message + * @param module Module log comes from + * @param msg Message to write + * @returns {void} + */ + info(module: string, ...msg: unknown[]) { + this.log(module, "info", ...msg); + } + + /** + * Log a WARN message + * @param module Module log comes from + * @param msg Message to write + * @returns {void} + */ + warn(module: string, ...msg: unknown[]) { + this.log(module, "warn", ...msg); + } + + /** + * Log an ERROR message + * @param module Module log comes from + * @param msg Message to write + * @returns {void} + */ + error(module: string, ...msg: unknown[]) { + this.log(module, "error", ...msg); + } + + /** + * Log a DEBUG message + * @param module Module log comes from + * @param msg Message to write + * @returns {void} + */ + debug(module: string, ...msg: unknown[]) { + this.log(module, "debug", ...msg); + } + + /** + * Log an exception as an ERROR + * @param module Module log comes from + * @param exception The exception to include + * @param msg The message to write + * @returns {void} + */ + exception(module: string, exception: unknown, ...msg: unknown[]) { + this.log(module, "error", ...msg, exception); + } +} + +export const log = new Logger(); + +declare global { + interface String { + replaceAll(str: string, newStr: string): string; + } +} + +export class TimeLogger { + startTime: number; + + /** + * + */ + constructor() { + this.startTime = dayjs().valueOf(); + } + + /** + * Output time since start of monitor + * @param name Name of monitor + * @returns {void} + */ + print(name: string): void { + if (isDevEnv() && process.env.TIMELOGGER === "1") { + console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms"); + } + } +} + +/** + * Returns a random number between min (inclusive) and max (exclusive) + * @param min minumim value, inclusive + * @param max maximum value, exclusive + * @returns Random number + */ +export function getRandomArbitrary(min: number, max: number) { + return Math.random() * (max - min) + min; +} + +/** + * From: https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range + * + * Returns a random integer between min (inclusive) and max (inclusive). + * The value is no lower than min (or the next integer greater than min + * if min isn't an integer) and no greater than max (or the next integer + * lower than max if max isn't an integer). + * Using Math.round() will give you a non-uniform distribution! + * @param min minumim value, inclusive + * @param max maximum value, exclusive + * @returns {number} Random number + */ +export function getRandomInt(min: number, max: number) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; +} + +/** + * Returns either the NodeJS crypto.randomBytes() function or its + * browser equivalent implemented via window.crypto.getRandomValues() + * @returns {Uint8Array} Random bytes + */ +const getRandomBytes = ( + typeof window !== "undefined" && window.crypto + ? // Browsers + function () { + return (numBytes: number) => { + const randomBytes = new Uint8Array(numBytes); + for (let i = 0; i < numBytes; i += 65536) { + window.crypto.getRandomValues(randomBytes.subarray(i, i + Math.min(numBytes - i, 65536))); + } + return randomBytes; + }; + } + : // Node + function () { + // eslint-disable-next-line @typescript-eslint/no-var-requires + return require("crypto").randomBytes; + } +)(); + +/** + * Get a random integer suitable for use in cryptography between upper + * and lower bounds. + * @param min Minimum value of integer + * @param max Maximum value of integer + * @returns Cryptographically suitable random integer + */ +export function getCryptoRandomInt(min: number, max: number): number { + // synchronous version of: https://github.com/joepie91/node-random-number-csprng + + const range = max - min; + if (range >= Math.pow(2, 32)) { + console.log("Warning! Range is too large."); + } + + let tmpRange = range; + let bitsNeeded = 0; + let bytesNeeded = 0; + let mask = 1; + + while (tmpRange > 0) { + if (bitsNeeded % 8 === 0) { + bytesNeeded += 1; + } + bitsNeeded += 1; + mask = (mask << 1) | 1; + tmpRange = tmpRange >>> 1; + } + + const randomBytes = getRandomBytes(bytesNeeded); + let randomValue = 0; + + for (let i = 0; i < bytesNeeded; i++) { + randomValue |= randomBytes[i] << (8 * i); + } + + randomValue = randomValue & mask; + + if (randomValue <= range) { + return min + randomValue; + } else { + return getCryptoRandomInt(min, max); + } +} + +/** + * Generate a random alphanumeric string of fixed length + * @param length Length of string to generate + * @returns string + */ +export function genSecret(length = 64) { + let secret = ""; + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + const charsLength = chars.length; + for (let i = 0; i < length; i++) { + secret += chars.charAt(getCryptoRandomInt(0, charsLength - 1)); + } + return secret; +} + +/** + * Get the path of a monitor + * @param id ID of monitor + * @returns Formatted relative path + */ +export function getMonitorRelativeURL(id: string) { + return "/dashboard/" + id; +} + +/** + * Parse to Time Object that used in VueDatePicker + * @param {string} time E.g. 12:00 + * @returns object + * @throws {Error} if time string is invalid + */ +export function parseTimeObject(time: string) { + if (!time) { + return { + hours: 0, + minutes: 0, + }; + } + + const array = time.split(":"); + + if (array.length < 2) { + throw new Error("parseVueDatePickerTimeFormat: Invalid Time"); + } + + const obj = { + hours: parseInt(array[0]), + minutes: parseInt(array[1]), + seconds: 0, + }; + if (array.length >= 3) { + obj.seconds = parseInt(array[2]); + } + return obj; +} + +/** + * Parse time to string from object {hours: number, minutes: number, seconds?: number} + * @param obj object to parse + * @returns {string} e.g. 12:00 + */ +export function parseTimeFromTimeObject(obj: any) { + if (!obj) { + return obj; + } + + let result = ""; + + result += obj.hours.toString().padStart(2, "0") + ":" + obj.minutes.toString().padStart(2, "0"); + + if (obj.seconds) { + result += ":" + obj.seconds.toString().padStart(2, "0"); + } + + return result; +} + +/** + * Convert ISO date to UTC + * @param input Date + * @returns ISO Date time + */ +export function isoToUTCDateTime(input: string) { + return dayjs(input).utc().format(SQL_DATETIME_FORMAT); +} + +/** + * @param input valid datetime string + * @returns ISO DateTime string + */ +export function utcToISODateTime(input: string) { + return dayjs.utc(input).toISOString(); +} + +/** + * For SQL_DATETIME_FORMAT + * @param input valid datetime string + * @param format Format to return + * @returns A string date of SQL_DATETIME_FORMAT + */ +export function utcToLocal(input: string, format = SQL_DATETIME_FORMAT): string { + return dayjs.utc(input).local().format(format); +} + +/** + * Convert local datetime to UTC + * @param input Local date + * @param format Format to return + * @returns Date in requested format + */ +export function localToUTC(input: string, format = SQL_DATETIME_FORMAT) { + return dayjs(input).utc().format(format); +} + +/** + * Generate a decimal integer number from a string + * @param str Input + * @param length Default is 10 which means 0 - 9 + * @returns {number} output number + */ +export function intHash(str: string, length = 10): number { + // A simple hashing function (you can use more complex hash functions if needed) + let hash = 0; + for (let i = 0; i < str.length; i++) { + hash += str.charCodeAt(i); + } + // Normalize the hash to the range [0, 10] + return ((hash % length) + length) % length; // Ensure the result is non-negative +} + +/** + * Evaluate a JSON query expression against the provided data. + * @param data The data to evaluate the JSON query against. + * @param jsonPath The JSON path or custom JSON query expression. + * @param jsonPathOperator The operator to use for comparison. + * @param expectedValue The expected value to compare against. + * @returns An object containing the status and the evaluation result. + * @throws Error if the evaluation returns undefined. + */ +export async function evaluateJsonQuery( + data: any, + jsonPath: string, + jsonPathOperator: string, + expectedValue: any +): Promise<{ status: boolean; response: any }> { + // Attempt to parse data as JSON; if unsuccessful, handle based on data type. + let response: any; + try { + response = JSON.parse(data); + } catch { + response = + (typeof data === "object" || typeof data === "number") && !Buffer.isBuffer(data) ? data : data.toString(); + } + + try { + // If a JSON path is provided, pre-evaluate the data using it. + response = jsonPath ? await jsonata(jsonPath).evaluate(response) : response; + + if (response === null || response === undefined) { + throw new Error("Empty or undefined response. Check query syntax and response structure"); + } + + // Check for arrays: JSONata filter expressions like .[predicate] always return arrays + if (Array.isArray(response)) { + const responseStr = JSON.stringify(response); + const truncatedResponse = responseStr.length > 25 ? responseStr.substring(0, 25) + "...]" : responseStr; + throw new Error( + "JSON query returned the array " + + truncatedResponse + + ", but a primitive value is required. " + + "Modify your query to return a single value via [0] to get the first element or use an aggregation like $count(), $sum() or $boolean()." + ); + } + + if (typeof response === "object" || response instanceof Date || typeof response === "function") { + throw new Error( + `The post-JSON query evaluated response from the server is of type ${typeof response}, which cannot be directly compared to the expected value` + ); + } + + // Perform the comparison logic using the chosen operator + let jsonQueryExpression; + switch (jsonPathOperator) { + case ">": + case ">=": + case "<": + case "<=": + jsonQueryExpression = `$number($.value) ${jsonPathOperator} $number($.expected)`; + break; + case "!=": + jsonQueryExpression = "$.value != $.expected"; + break; + case "==": + jsonQueryExpression = "$.value = $.expected"; + break; + case "contains": + jsonQueryExpression = "$contains($.value, $.expected)"; + break; + default: + throw new Error(`Invalid condition ${jsonPathOperator}`); + } + + // Evaluate the JSON Query Expression + const expression = jsonata(jsonQueryExpression); + const status = await expression.evaluate({ + value: response.toString(), + expected: expectedValue.toString(), + }); + + if (status === undefined) { + throw new Error( + "Query evaluation returned undefined. Check query syntax and the structure of the response data" + ); + } + + return { + status, // The evaluation of the json query + response, // The response from the server or result from initial json-query evaluation + }; + } catch (err: any) { + response = JSON.stringify(response); // Ensure the response is treated as a string for the console + response = response && response.length > 50 ? `${response.substring(0, 100)}… (truncated)` : response; // Truncate long responses to the console + throw new Error(`Error evaluating JSON query: ${err.message}. Response from server was: ${response}`); + } +} + +// these types will have domain expiry support via the specified field +export const TYPES_WITH_DOMAIN_EXPIRY_SUPPORT_VIA_FIELD = { + http: "url", + keyword: "url", + "json-query": "url", + "real-browser": "url", + "websocket-upgrade": "url", + port: "hostname", + ping: "hostname", + "grpc-keyword": "grpcUrl", + dns: "hostname", + smtp: "hostname", + snmp: "hostname", + gamedig: "hostname", + steam: "hostname", + mqtt: "hostname", + radius: "hostname", + "tailscale-ping": "hostname", + "sip-options": "hostname", +} as const; + +/** + * @param res Response object from fetch + */ +export async function checkFetch(res: Response): Promise { + let data; + + try { + if (!res.ok) { + data = await res.json(); + } + } catch (e) { + throw new Error("Failed to fetch without message: " + res.status); + } + + if (data) { + if (data.msg) { + throw new Error(data.msg); + } else { + throw new Error(JSON.stringify(data)); + } + } + + const contentType = res.headers.get("content-type"); + + // if response is not in json type + if (!contentType || !contentType.startsWith("application/json")) { + throw new Error("Response is not in JSON format"); + } +} diff --git a/test/backend-test/README.md b/test/backend-test/README.md new file mode 100644 index 0000000..09a13f3 --- /dev/null +++ b/test/backend-test/README.md @@ -0,0 +1,35 @@ +# Node.js Test Runner + +Documentation: https://nodejs.org/api/test.html + +Create a test file in this directory with the name `*.js`. + +> [!TIP] +> Writing great tests is hard. +> +> You can make our live much simpler by following this guidance: +> +> - Use `describe()` to group related tests +> - Use `test()` for individual test cases +> - One test per scenario +> - Use descriptive test names: `function() [behavior] [condition]` +> - Don't prefix with "Test" or "Should" + +## Template + +```js +const { describe, test } = require("node:test"); +const assert = require("node:assert"); + +describe("Feature Name", () => { + test("function() returns expected value when condition is met", () => { + assert.strictEqual(1, 1); + }); +}); +``` + +## Run + +```bash +npm run test-backend +``` diff --git a/test/backend-test/check-translations.test.js b/test/backend-test/check-translations.test.js new file mode 100644 index 0000000..e761754 --- /dev/null +++ b/test/backend-test/check-translations.test.js @@ -0,0 +1,175 @@ +const { describe, it } = require("node:test"); +const assert = require("node:assert"); +const fs = require("fs/promises"); +const path = require("path"); +const { retryExternalService } = require("./test-util"); + +/** + * Recursively walks a directory and yields file paths. + * @param {string} dir The directory to walk. + * @yields {string} The path to a file. + * @returns {AsyncGenerator} A generator that yields file paths. + */ +async function* walk(dir) { + const files = await fs.readdir(dir, { withFileTypes: true }); + for (const file of files) { + if (file.isDirectory()) { + yield* walk(path.join(dir, file.name)); + } else { + yield path.join(dir, file.name); + } + } +} + +const UPSTREAM_EN_JSON = "https://raw.githubusercontent.com/louislam/uptime-kuma/refs/heads/master/src/lang/en.json"; + +/** + * Extract `{placeholders}` from a translation string. + * @param {string} value The translation string to extract placeholders from. + * @returns {Set} A set of placeholder names. + */ +function extractParams(value) { + if (typeof value !== "string") { + return new Set(); + } + + const regex = /\{([^}]+)\}/g; + const params = new Set(); + + let match; + while ((match = regex.exec(value)) !== null) { + params.add(match[1]); + } + + return params; +} + +/** + * Fallback to get start/end indices of a key within a line. + * @param {string} line - Line of text to search in. + * @param {string} key - Key to find. + * @returns {[number, number]} Array [start, end] representing the indices of the key in the line. + */ +function getStartEnd(line, key) { + let start = line.indexOf(key); + if (start === -1) { + start = 0; + } + return [start, start + key.length]; +} + +describe("Check Translations", () => { + it("should not have missing translation keys", async () => { + const enTranslations = JSON.parse(await fs.readFile("src/lang/en.json", "utf-8")); + + // this is a reasonably crude check, you can get around this trivially + /// this check is just to save on maintainer energy to explain this on every review ^^ + const translationRegex = /\$t\(['"](?.*?)['"]\s*[,)]|i18n-t[^>]*\s+keypath="(?[^"]+)"/dg; + + // detect server-side TranslatableError usage: new TranslatableError("key") + const translatableErrorRegex = /new\s+TranslatableError\(\s*['"](?[^'"]+)['"]\s*\)/g; + + const missingKeys = []; + + const roots = ["src", "server"]; + + for (const root of roots) { + for await (const filePath of walk(root)) { + if (filePath.endsWith(".vue") || filePath.endsWith(".js")) { + const lines = (await fs.readFile(filePath, "utf-8")).split("\n"); + lines.forEach((line, lineNum) => { + let match; + // front-end style keys ($t / i18n-t) + while ((match = translationRegex.exec(line)) !== null) { + const key = match.groups.key1 || match.groups.key2; + if (key && !enTranslations[key]) { + const [start, end] = getStartEnd(line, key); + missingKeys.push({ + filePath, + lineNum: lineNum + 1, + key, + line: line, + start, + end, + }); + } + } + + // server-side TranslatableError usage + let m; + while ((m = translatableErrorRegex.exec(line)) !== null) { + const key3 = m.groups.key3; + if (key3 && !enTranslations[key3]) { + const [start, end] = getStartEnd(line, key3); + missingKeys.push({ + filePath, + lineNum: lineNum + 1, + key: key3, + line: line, + start, + end, + }); + } + } + }); + } + } + } + + if (missingKeys.length > 0) { + let report = "Missing translation keys found:\n"; + missingKeys.forEach(({ filePath, lineNum, key, line, start, end }) => { + report += `\nerror: Missing translation key: '${key}'`; + report += `\n --> ${filePath}:${lineNum}:${start}`; + report += "\n |"; + report += `\n${String(lineNum).padEnd(5)}| ${line}`; + const arrow = " ".repeat(start) + "^".repeat(end - start); + report += `\n | ${arrow} unrecognized translation key`; + report += "\n |"; + report += `\n = note: please register the translation key '${key}' in en.json so that our awesome team of translators can translate them`; + report += + "\n = tip: if you want to contribute translations, please visit https://weblate.kuma.pet\n"; + }); + report += "\n==============================="; + const fileCount = new Set(missingKeys.map((item) => item.filePath)).size; + report += `\nFound a total of ${missingKeys.length} missing keys in ${fileCount} files.`; + assert.fail(report); + } + }); + + it("en.json translations must not change placeholder parameters", async () => { + await retryExternalService(async () => { + // Load local reference (the one translators are synced against) + const enTranslations = JSON.parse(await fs.readFile("src/lang/en.json", "utf-8")); + + // Fetch upstream version + const res = await fetch(UPSTREAM_EN_JSON); + assert.equal(res.ok, true, "Failed to fetch upstream en.json"); + + const upstreamEn = await res.json(); + + for (const [key, upstreamValue] of Object.entries(upstreamEn)) { + if (!(key in enTranslations)) { + // deleted keys are fine + continue; + } + + const localParams = extractParams(enTranslations[key]); + const upstreamParams = extractParams(upstreamValue); + + assert.deepEqual( + localParams, + upstreamParams, + [ + `Translation key "${key}" changed placeholder parameters.`, + `This is a breaking change for existing translations.`, + `Please rename the translation key instead of changing placeholders.`, + ``, + `your version: ${[...localParams].join(", ")}`, + `on master: ${[...upstreamParams].join(", ")}`, + ].join("\n") + ); + } + }); + }); +}); diff --git a/test/backend-test/monitor-conditions/test-evaluator.js b/test/backend-test/monitor-conditions/test-evaluator.js new file mode 100644 index 0000000..9dc20cd --- /dev/null +++ b/test/backend-test/monitor-conditions/test-evaluator.js @@ -0,0 +1,52 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { + ConditionExpressionGroup, + ConditionExpression, + LOGICAL, +} = require("../../../server/monitor-conditions/expression.js"); +const { evaluateExpressionGroup, evaluateExpression } = require("../../../server/monitor-conditions/evaluator.js"); + +describe("Expression Evaluator", () => { + test("evaluateExpression() returns true when condition matches and false otherwise", () => { + const expr = new ConditionExpression("record", "contains", "mx1.example.com"); + assert.strictEqual(true, evaluateExpression(expr, { record: "mx1.example.com" })); + assert.strictEqual(false, evaluateExpression(expr, { record: "mx2.example.com" })); + }); + + test("evaluateExpressionGroup() with AND logic requires all conditions to be true", () => { + const group = new ConditionExpressionGroup([ + new ConditionExpression("record", "contains", "mx1."), + new ConditionExpression("record", "contains", "example.com", LOGICAL.AND), + ]); + assert.strictEqual(true, evaluateExpressionGroup(group, { record: "mx1.example.com" })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "mx1." })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.com" })); + }); + + test("evaluateExpressionGroup() with OR logic requires at least one condition to be true", () => { + const group = new ConditionExpressionGroup([ + new ConditionExpression("record", "contains", "example.com"), + new ConditionExpression("record", "contains", "example.org", LOGICAL.OR), + ]); + assert.strictEqual(true, evaluateExpressionGroup(group, { record: "example.com" })); + assert.strictEqual(true, evaluateExpressionGroup(group, { record: "example.org" })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.net" })); + }); + + test("evaluateExpressionGroup() evaluates nested groups correctly", () => { + const group = new ConditionExpressionGroup([ + new ConditionExpression("record", "contains", "mx1."), + new ConditionExpressionGroup([ + new ConditionExpression("record", "contains", "example.com"), + new ConditionExpression("record", "contains", "example.org", LOGICAL.OR), + ]), + ]); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "mx1." })); + assert.strictEqual(true, evaluateExpressionGroup(group, { record: "mx1.example.com" })); + assert.strictEqual(true, evaluateExpressionGroup(group, { record: "mx1.example.org" })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.com" })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "example.org" })); + assert.strictEqual(false, evaluateExpressionGroup(group, { record: "mx1.example.net" })); + }); +}); diff --git a/test/backend-test/monitor-conditions/test-expressions.js b/test/backend-test/monitor-conditions/test-expressions.js new file mode 100644 index 0000000..e3d8403 --- /dev/null +++ b/test/backend-test/monitor-conditions/test-expressions.js @@ -0,0 +1,55 @@ +const test = require("node:test"); +const assert = require("node:assert"); +const { ConditionExpressionGroup, ConditionExpression } = require("../../../server/monitor-conditions/expression.js"); + +test("Test ConditionExpressionGroup.fromMonitor", async (t) => { + const monitor = { + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + operator: "contains", + value: "foo", + variable: "record", + }, + { + type: "group", + andOr: "and", + children: [ + { + type: "expression", + andOr: "and", + operator: "contains", + value: "bar", + variable: "record", + }, + { + type: "group", + andOr: "and", + children: [ + { + type: "expression", + andOr: "and", + operator: "contains", + value: "car", + variable: "record", + }, + ], + }, + ], + }, + ]), + }; + const root = ConditionExpressionGroup.fromMonitor(monitor); + assert.strictEqual(true, root.children.length === 2); + assert.strictEqual(true, root.children[0] instanceof ConditionExpression); + assert.strictEqual(true, root.children[0].value === "foo"); + assert.strictEqual(true, root.children[1] instanceof ConditionExpressionGroup); + assert.strictEqual(true, root.children[1].children.length === 2); + assert.strictEqual(true, root.children[1].children[0] instanceof ConditionExpression); + assert.strictEqual(true, root.children[1].children[0].value === "bar"); + assert.strictEqual(true, root.children[1].children[1] instanceof ConditionExpressionGroup); + assert.strictEqual(true, root.children[1].children[1].children.length === 1); + assert.strictEqual(true, root.children[1].children[1].children[0] instanceof ConditionExpression); + assert.strictEqual(true, root.children[1].children[1].children[0].value === "car"); +}); diff --git a/test/backend-test/monitor-conditions/test-operators.js b/test/backend-test/monitor-conditions/test-operators.js new file mode 100644 index 0000000..d474d89 --- /dev/null +++ b/test/backend-test/monitor-conditions/test-operators.js @@ -0,0 +1,126 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { + operatorMap, + OP_CONTAINS, + OP_NOT_CONTAINS, + OP_LT, + OP_GT, + OP_LTE, + OP_GTE, + OP_STR_EQUALS, + OP_STR_NOT_EQUALS, + OP_NUM_EQUALS, + OP_NUM_NOT_EQUALS, + OP_STARTS_WITH, + OP_ENDS_WITH, + OP_NOT_STARTS_WITH, + OP_NOT_ENDS_WITH, +} = require("../../../server/monitor-conditions/operators.js"); + +describe("Expression Operators", () => { + test("StringEqualsOperator returns true for identical strings and false otherwise", () => { + const op = operatorMap.get(OP_STR_EQUALS); + assert.strictEqual(true, op.test("mx1.example.com", "mx1.example.com")); + assert.strictEqual(false, op.test("mx1.example.com", "mx1.example.org")); + assert.strictEqual(false, op.test("1", 1)); // strict equality + }); + + test("StringNotEqualsOperator returns true for different strings and false for identical strings", () => { + const op = operatorMap.get(OP_STR_NOT_EQUALS); + assert.strictEqual(true, op.test("mx1.example.com", "mx1.example.org")); + assert.strictEqual(false, op.test("mx1.example.com", "mx1.example.com")); + assert.strictEqual(true, op.test(1, "1")); // variable is not typecasted (strict equality) + }); + + test("ContainsOperator returns true when scalar contains substring", () => { + const op = operatorMap.get(OP_CONTAINS); + assert.strictEqual(true, op.test("mx1.example.org", "example.org")); + assert.strictEqual(false, op.test("mx1.example.org", "example.com")); + }); + + test("ContainsOperator returns true when array contains element", () => { + const op = operatorMap.get(OP_CONTAINS); + assert.strictEqual(true, op.test(["example.org"], "example.org")); + assert.strictEqual(false, op.test(["example.org"], "example.com")); + }); + + test("NotContainsOperator returns true when scalar does not contain substring", () => { + const op = operatorMap.get(OP_NOT_CONTAINS); + assert.strictEqual(true, op.test("example.org", ".com")); + assert.strictEqual(false, op.test("example.org", ".org")); + }); + + test("NotContainsOperator returns true when array does not contain element", () => { + const op = operatorMap.get(OP_NOT_CONTAINS); + assert.strictEqual(true, op.test(["example.org"], "example.com")); + assert.strictEqual(false, op.test(["example.org"], "example.org")); + }); + + test("StartsWithOperator returns true when string starts with prefix", () => { + const op = operatorMap.get(OP_STARTS_WITH); + assert.strictEqual(true, op.test("mx1.example.com", "mx1")); + assert.strictEqual(false, op.test("mx1.example.com", "mx2")); + }); + + test("NotStartsWithOperator returns true when string does not start with prefix", () => { + const op = operatorMap.get(OP_NOT_STARTS_WITH); + assert.strictEqual(true, op.test("mx1.example.com", "mx2")); + assert.strictEqual(false, op.test("mx1.example.com", "mx1")); + }); + + test("EndsWithOperator returns true when string ends with suffix", () => { + const op = operatorMap.get(OP_ENDS_WITH); + assert.strictEqual(true, op.test("mx1.example.com", "example.com")); + assert.strictEqual(false, op.test("mx1.example.com", "example.net")); + }); + + test("NotEndsWithOperator returns true when string does not end with suffix", () => { + const op = operatorMap.get(OP_NOT_ENDS_WITH); + assert.strictEqual(true, op.test("mx1.example.com", "example.net")); + assert.strictEqual(false, op.test("mx1.example.com", "example.com")); + }); + + test("NumberEqualsOperator returns true for equal numbers with type coercion", () => { + const op = operatorMap.get(OP_NUM_EQUALS); + assert.strictEqual(true, op.test(1, 1)); + assert.strictEqual(true, op.test(1, "1")); + assert.strictEqual(false, op.test(1, "2")); + }); + + test("NumberNotEqualsOperator returns true for different numbers", () => { + const op = operatorMap.get(OP_NUM_NOT_EQUALS); + assert.strictEqual(true, op.test(1, "2")); + assert.strictEqual(false, op.test(1, "1")); + }); + + test("LessThanOperator returns true when first number is less than second", () => { + const op = operatorMap.get(OP_LT); + assert.strictEqual(true, op.test(1, 2)); + assert.strictEqual(true, op.test(1, "2")); + assert.strictEqual(false, op.test(1, 1)); + }); + + test("GreaterThanOperator returns true when first number is greater than second", () => { + const op = operatorMap.get(OP_GT); + assert.strictEqual(true, op.test(2, 1)); + assert.strictEqual(true, op.test(2, "1")); + assert.strictEqual(false, op.test(1, 1)); + }); + + test("LessThanOrEqualToOperator returns true when first number is less than or equal to second", () => { + const op = operatorMap.get(OP_LTE); + assert.strictEqual(true, op.test(1, 1)); + assert.strictEqual(true, op.test(1, 2)); + assert.strictEqual(true, op.test(1, "2")); + assert.strictEqual(false, op.test(1, 0)); + }); + + test("GreaterThanOrEqualToOperator returns true when first number is greater than or equal to second", () => { + const op = operatorMap.get(OP_GTE); + assert.strictEqual(true, op.test(1, 1)); + assert.strictEqual(true, op.test(2, 1)); + assert.strictEqual(true, op.test(2, "2")); + assert.strictEqual(false, op.test(2, 3)); + }); +}); diff --git a/test/backend-test/monitors/test-gamedig.js b/test/backend-test/monitors/test-gamedig.js new file mode 100644 index 0000000..79a2288 --- /dev/null +++ b/test/backend-test/monitors/test-gamedig.js @@ -0,0 +1,238 @@ +const { describe, test, mock } = require("node:test"); +const assert = require("node:assert"); +const { GameDigMonitorType } = require("../../../server/monitor-types/gamedig"); +const { UP, PENDING } = require("../../../src/util"); +const { GameDig } = require("gamedig"); + +describe("GameDig Monitor", () => { + test("check() sets status to UP when Gamedig.query returns valid server response", async () => { + const gamedigMonitor = new GameDigMonitorType(); + + mock.method(GameDig, "query", async () => { + return { + name: "Test Minecraft Server", + ping: 42, + players: [], + }; + }); + + const monitor = { + hostname: "127.0.0.1", + port: 25565, + game: "minecraft", + gamedigGivenPortOnly: true, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await gamedigMonitor.check(monitor, heartbeat, {}); + + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Test Minecraft Server"); + assert.strictEqual(heartbeat.ping, 42); + } finally { + mock.restoreAll(); + } + }); + + test("check() passes hostname directly to GameDig when hostname is not an IP", async () => { + const gamedigMonitor = new GameDigMonitorType(); + + let capturedOptions = null; + + mock.method(GameDig, "query", async (options) => { + capturedOptions = options; + return { + name: "Test Server", + ping: 50, + }; + }); + + const monitor = { + hostname: "localhost", + port: 25565, + game: "minecraft", + gamedigGivenPortOnly: false, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await gamedigMonitor.check(monitor, heartbeat, {}); + + assert.strictEqual(capturedOptions.host, "localhost"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Test Server"); + assert.strictEqual(heartbeat.ping, 50); + } finally { + mock.restoreAll(); + } + }); + + test("check() passes IPv4 address directly to GameDig", async () => { + const gamedigMonitor = new GameDigMonitorType(); + + let capturedOptions = null; + + mock.method(GameDig, "query", async (options) => { + capturedOptions = options; + return { + name: "Test Server", + ping: 30, + }; + }); + + const monitor = { + hostname: "192.168.1.100", + port: 27015, + game: "valve", + gamedigGivenPortOnly: true, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await gamedigMonitor.check(monitor, heartbeat, {}); + + assert.strictEqual(capturedOptions.host, "192.168.1.100"); + assert.strictEqual(heartbeat.status, UP); + } finally { + mock.restoreAll(); + } + }); + + test("check() passes IPv6 address directly to GameDig", async () => { + const gamedigMonitor = new GameDigMonitorType(); + + let capturedOptions = null; + + mock.method(GameDig, "query", async (options) => { + capturedOptions = options; + return { + name: "Test Server", + ping: 30, + }; + }); + + const monitor = { + hostname: "::1", + port: 27015, + game: "valve", + gamedigGivenPortOnly: true, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await gamedigMonitor.check(monitor, heartbeat, {}); + + assert.strictEqual(capturedOptions.host, "::1"); + assert.strictEqual(heartbeat.status, UP); + } finally { + mock.restoreAll(); + } + }); + + test("check() passes correct parameters to Gamedig.query", async () => { + const gamedigMonitor = new GameDigMonitorType(); + + let capturedOptions = null; + + mock.method(GameDig, "query", async (options) => { + capturedOptions = options; + return { + name: "Test Server", + ping: 25, + }; + }); + + const monitor = { + hostname: "192.168.1.100", + port: 27015, + game: "valve", + gamedigGivenPortOnly: true, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await gamedigMonitor.check(monitor, heartbeat, {}); + + assert.strictEqual(capturedOptions.type, "valve"); + assert.strictEqual(capturedOptions.host, "192.168.1.100"); + assert.strictEqual(capturedOptions.port, 27015); + assert.strictEqual(capturedOptions.givenPortOnly, true); + } finally { + mock.restoreAll(); + } + }); + + test("check() converts gamedigGivenPortOnly to boolean when value is truthy non-boolean", async () => { + const gamedigMonitor = new GameDigMonitorType(); + + let capturedOptions = null; + + mock.method(GameDig, "query", async (options) => { + capturedOptions = options; + return { + name: "Test Server", + ping: 30, + }; + }); + + const monitor = { + hostname: "127.0.0.1", + port: 25565, + game: "minecraft", + gamedigGivenPortOnly: 1, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await gamedigMonitor.check(monitor, heartbeat, {}); + + assert.strictEqual(capturedOptions.givenPortOnly, true); + assert.strictEqual(typeof capturedOptions.givenPortOnly, "boolean"); + } finally { + mock.restoreAll(); + } + }); + + test("check() rejects when game server is unreachable", async () => { + const gamedigMonitor = new GameDigMonitorType(); + + const monitor = { + hostname: "127.0.0.1", + port: 54321, + game: "minecraft", + gamedigGivenPortOnly: true, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects(gamedigMonitor.check(monitor, heartbeat, {}), /Error/); + }); +}); diff --git a/test/backend-test/monitors/test-grpc.js b/test/backend-test/monitors/test-grpc.js new file mode 100644 index 0000000..fc669e9 --- /dev/null +++ b/test/backend-test/monitors/test-grpc.js @@ -0,0 +1,294 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const grpc = require("@grpc/grpc-js"); +const protoLoader = require("@grpc/proto-loader"); +const { GrpcKeywordMonitorType } = require("../../../server/monitor-types/grpc"); +const { UP, PENDING } = require("../../../src/util"); +const fs = require("fs"); +const path = require("path"); +const os = require("os"); + +const testProto = ` +syntax = "proto3"; +package test; + +service TestService { + rpc Echo (EchoRequest) returns (EchoResponse); +} + +message EchoRequest { + string message = 1; +} + +message EchoResponse { + string message = 1; +} +`; + +/** + * Create a gRPC server for testing + * @param {number} port Port to listen on + * @param {object} methodHandlers Object with method handlers + * @returns {Promise} gRPC server instance + */ +async function createTestGrpcServer(port, methodHandlers) { + // Write proto to temp file + const tmpDir = os.tmpdir(); + const protoPath = path.join(tmpDir, `test-${port}.proto`); + fs.writeFileSync(protoPath, testProto); + + // Load proto file + const packageDefinition = protoLoader.loadSync(protoPath, { + keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true, + }); + const protoDescriptor = grpc.loadPackageDefinition(packageDefinition); + const testPackage = protoDescriptor.test; + + const server = new grpc.Server(); + + // Add service implementation + server.addService(testPackage.TestService.service, { + Echo: (call, callback) => { + if (methodHandlers.Echo) { + methodHandlers.Echo(call, callback); + } else { + callback(null, { message: call.request.message }); + } + }, + }); + + return new Promise((resolve, reject) => { + server.bindAsync(`0.0.0.0:${port}`, grpc.ServerCredentials.createInsecure(), (err) => { + if (err) { + reject(err); + } else { + server.start(); + // Clean up temp file + fs.unlinkSync(protoPath); + resolve(server); + } + }); + }); +} + +describe( + "GrpcKeywordMonitorType", + { + skip: !!process.env.CI && (process.platform !== "linux" || process.arch !== "x64"), + }, + () => { + test("check() sets status to UP when keyword is found in response", async () => { + const port = 50051; + const server = await createTestGrpcServer(port, { + Echo: (call, callback) => { + callback(null, { message: "Hello World with SUCCESS keyword" }); + }, + }); + + const grpcMonitor = new GrpcKeywordMonitorType(); + const monitor = { + grpcUrl: `localhost:${port}`, + grpcProtobuf: testProto, + grpcServiceName: "test.TestService", + grpcMethod: "echo", + grpcBody: JSON.stringify({ message: "test" }), + keyword: "SUCCESS", + invertKeyword: false, + grpcEnableTls: false, + isInvertKeyword: () => false, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await grpcMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP); + assert.ok(heartbeat.msg.includes("SUCCESS")); + assert.ok(heartbeat.msg.includes("is")); + } finally { + server.forceShutdown(); + } + }); + + test("check() rejects when keyword is not found in response", async () => { + const port = 50052; + const server = await createTestGrpcServer(port, { + Echo: (call, callback) => { + callback(null, { message: "Hello World without the expected keyword" }); + }, + }); + + const grpcMonitor = new GrpcKeywordMonitorType(); + const monitor = { + grpcUrl: `localhost:${port}`, + grpcProtobuf: testProto, + grpcServiceName: "test.TestService", + grpcMethod: "echo", + grpcBody: JSON.stringify({ message: "test" }), + keyword: "MISSING", + invertKeyword: false, + grpcEnableTls: false, + isInvertKeyword: () => false, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await assert.rejects(grpcMonitor.check(monitor, heartbeat, {}), (err) => { + assert.ok(err.message.includes("MISSING")); + assert.ok(err.message.includes("not")); + return true; + }); + } finally { + server.forceShutdown(); + } + }); + + test("check() rejects when inverted keyword is present in response", async () => { + const port = 50053; + const server = await createTestGrpcServer(port, { + Echo: (call, callback) => { + callback(null, { message: "Response with ERROR keyword" }); + }, + }); + + const grpcMonitor = new GrpcKeywordMonitorType(); + const monitor = { + grpcUrl: `localhost:${port}`, + grpcProtobuf: testProto, + grpcServiceName: "test.TestService", + grpcMethod: "echo", + grpcBody: JSON.stringify({ message: "test" }), + keyword: "ERROR", + invertKeyword: true, + grpcEnableTls: false, + isInvertKeyword: () => true, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await assert.rejects(grpcMonitor.check(monitor, heartbeat, {}), (err) => { + assert.ok(err.message.includes("ERROR")); + assert.ok(err.message.includes("present")); + return true; + }); + } finally { + server.forceShutdown(); + } + }); + + test("check() sets status to UP when inverted keyword is not present in response", async () => { + const port = 50054; + const server = await createTestGrpcServer(port, { + Echo: (call, callback) => { + callback(null, { message: "Response without error keyword" }); + }, + }); + + const grpcMonitor = new GrpcKeywordMonitorType(); + const monitor = { + grpcUrl: `localhost:${port}`, + grpcProtobuf: testProto, + grpcServiceName: "test.TestService", + grpcMethod: "echo", + grpcBody: JSON.stringify({ message: "test" }), + keyword: "ERROR", + invertKeyword: true, + grpcEnableTls: false, + isInvertKeyword: () => true, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await grpcMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP); + assert.ok(heartbeat.msg.includes("ERROR")); + assert.ok(heartbeat.msg.includes("not")); + } finally { + server.forceShutdown(); + } + }); + + test("check() rejects when gRPC server is unreachable", async () => { + const grpcMonitor = new GrpcKeywordMonitorType(); + const monitor = { + grpcUrl: "localhost:50099", + grpcProtobuf: testProto, + grpcServiceName: "test.TestService", + grpcMethod: "echo", + grpcBody: JSON.stringify({ message: "test" }), + keyword: "SUCCESS", + invertKeyword: false, + grpcEnableTls: false, + isInvertKeyword: () => false, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects(grpcMonitor.check(monitor, heartbeat, {}), (err) => { + // Should fail with connection error + return true; + }); + }); + + test("check() truncates long response messages in error output", async () => { + const port = 50055; + const longMessage = "A".repeat(100) + " with SUCCESS keyword"; + + const server = await createTestGrpcServer(port, { + Echo: (call, callback) => { + callback(null, { message: longMessage }); + }, + }); + + const grpcMonitor = new GrpcKeywordMonitorType(); + const monitor = { + grpcUrl: `localhost:${port}`, + grpcProtobuf: testProto, + grpcServiceName: "test.TestService", + grpcMethod: "echo", + grpcBody: JSON.stringify({ message: "test" }), + keyword: "MISSING", + invertKeyword: false, + grpcEnableTls: false, + isInvertKeyword: () => false, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await assert.rejects(grpcMonitor.check(monitor, heartbeat, {}), (err) => { + // Should truncate message to 50 characters with "..." + assert.ok(err.message.includes("...")); + return true; + }); + } finally { + server.forceShutdown(); + } + }); + } +); diff --git a/test/backend-test/monitors/test-kafka-producer.js b/test/backend-test/monitors/test-kafka-producer.js new file mode 100644 index 0000000..ec8dd50 --- /dev/null +++ b/test/backend-test/monitors/test-kafka-producer.js @@ -0,0 +1,15 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { kafkaProducerAsync } = require("../../../server/util-server"); + +describe("Kafka Producer", () => { + test("rejects when broker is not reachable", async () => { + await assert.rejects( + kafkaProducerAsync(["localhost:19092"], "test-topic", "test-message", { + interval: 5, + connectionTimeout: 1, + }), + /.*/ // any error + ); + }); +}); diff --git a/test/backend-test/monitors/test-mqtt.js b/test/backend-test/monitors/test-mqtt.js new file mode 100644 index 0000000..96fa8a7 --- /dev/null +++ b/test/backend-test/monitors/test-mqtt.js @@ -0,0 +1,238 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { HiveMQContainer } = require("@testcontainers/hivemq"); +const mqtt = require("mqtt"); +const { MqttMonitorType } = require("../../../server/monitor-types/mqtt"); +const { UP, PENDING } = require("../../../src/util"); + +/** + * Runs an MQTT test with the + * @param {string} mqttSuccessMessage the message that the monitor expects + * @param {null|"keyword"|"json-query"} mqttCheckType the type of check we perform + * @param {string} receivedMessage what message is received from the mqtt channel + * @param {string} monitorTopic which MQTT topic is monitored (wildcards are allowed) + * @param {string} publishTopic to which MQTT topic the message is sent + * @param {string|null} conditions JSON string of conditions or null + * @returns {Promise} the heartbeat produced by the check + */ +async function testMqtt( + mqttSuccessMessage, + mqttCheckType, + receivedMessage, + monitorTopic = "test", + publishTopic = "test", + conditions = null +) { + const hiveMQContainer = await new HiveMQContainer().start(); + const connectionString = hiveMQContainer.getConnectionString(); + const mqttMonitorType = new MqttMonitorType(); + const monitor = { + jsonPath: "firstProp", // always return firstProp for the json-query monitor + hostname: connectionString.split(":", 2).join(":"), + mqttTopic: monitorTopic, + port: connectionString.split(":")[2], + mqttUsername: null, + mqttPassword: null, + mqttWebsocketPath: null, // for WebSocket connections + interval: 20, // controls the timeout + mqttSuccessMessage: mqttSuccessMessage, // for keywords + expectedValue: mqttSuccessMessage, // for json-query + mqttCheckType: mqttCheckType, + conditions: conditions, // for conditions system + }; + const heartbeat = { + msg: "", + status: PENDING, + }; + + const testMqttClient = mqtt.connect(hiveMQContainer.getConnectionString()); + testMqttClient.on("connect", () => { + testMqttClient.subscribe(monitorTopic, (error) => { + if (!error) { + testMqttClient.publish(publishTopic, receivedMessage); + } + }); + }); + + try { + await mqttMonitorType.check(monitor, heartbeat, {}); + } finally { + testMqttClient.end(); + hiveMQContainer.stop(); + } + return heartbeat; +} + +describe( + "MqttMonitorType", + { + concurrency: 1, + skip: + (!!process.env.CI && (process.platform !== "linux" || process.arch !== "x64")) || + process.env.SKIP_TESTCONTAINER, + }, + () => { + test("check() sets status to UP when keyword is found in message (type=default)", async () => { + const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Topic: test; Message: -> KEYWORD <-"); + }); + + test("check() sets status to UP when keyword is found in nested topic", async () => { + const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-", "a/b/c", "a/b/c"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Topic: a/b/c; Message: -> KEYWORD <-"); + }); + + test("check() sets status to UP when keyword is found in nested topic with special characters", async () => { + const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-", "a/'/$/./*/%", "a/'/$/./*/%"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Topic: a/'/$/./*/%; Message: -> KEYWORD <-"); + }); + + test("check() sets status to UP when keyword is found using # wildcard", async () => { + const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-", "a/#", "a/b/c"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Topic: a/b/c; Message: -> KEYWORD <-"); + }); + + test("check() sets status to UP when keyword is found using + wildcard", async () => { + const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-", "a/+/c", "a/b/c"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Topic: a/b/c; Message: -> KEYWORD <-"); + }); + + test("check() sets status to UP when keyword is found using + and # wildcards", async () => { + const heartbeat = await testMqtt("KEYWORD", null, "-> KEYWORD <-", "a/+/c/#", "a/b/c/d/e"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Topic: a/b/c/d/e; Message: -> KEYWORD <-"); + }); + + test("check() rejects with timeout when topic does not match", async () => { + await assert.rejects( + testMqtt("keyword will not be checked anyway", null, "message", "x/y/z", "a/b/c"), + new Error("Timeout, Message not received") + ); + }); + + test("check() rejects with timeout when # wildcard is not last character", async () => { + await assert.rejects( + testMqtt("", null, "# should be last character", "#/c", "a/b/c"), + new Error("Timeout, Message not received") + ); + }); + + test("check() rejects with timeout when + wildcard topic does not match", async () => { + await assert.rejects( + testMqtt("", null, "message", "x/+/z", "a/b/c"), + new Error("Timeout, Message not received") + ); + }); + + test("check() sets status to UP when keyword is found in message (type=keyword)", async () => { + const heartbeat = await testMqtt("KEYWORD", "keyword", "-> KEYWORD <-"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Topic: test; Message: -> KEYWORD <-"); + }); + + test("check() rejects when keyword is not found in message (type=default)", async () => { + await assert.rejects( + testMqtt("NOT_PRESENT", null, "-> KEYWORD <-"), + new Error("Message Mismatch - Topic: test; Message: -> KEYWORD <-") + ); + }); + + test("check() rejects when keyword is not found in message (type=keyword)", async () => { + await assert.rejects( + testMqtt("NOT_PRESENT", "keyword", "-> KEYWORD <-"), + new Error("Message Mismatch - Topic: test; Message: -> KEYWORD <-") + ); + }); + + test("check() sets status to UP when json-query finds expected value", async () => { + // works because the monitors' jsonPath is hard-coded to "firstProp" + const heartbeat = await testMqtt("present", "json-query", '{"firstProp":"present"}'); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Message received, expected value is found"); + }); + + test("check() rejects when json-query path returns undefined", async () => { + // works because the monitors' jsonPath is hard-coded to "firstProp" + await assert.rejects( + testMqtt("[not_relevant]", "json-query", "{}"), + new Error("Message received but value is not equal to expected value, value was: [undefined]") + ); + }); + + test("check() rejects when json-query value does not match expected value", async () => { + // works because the monitors' jsonPath is hard-coded to "firstProp" + await assert.rejects( + testMqtt("[wrong_success_messsage]", "json-query", '{"firstProp":"present"}'), + new Error("Message received but value is not equal to expected value, value was: [present]") + ); + }); + + // Conditions system tests + test("check() sets status to UP when message condition matches (contains)", async () => { + const conditions = JSON.stringify([ + { + type: "expression", + variable: "message", + operator: "contains", + value: "KEYWORD", + }, + ]); + const heartbeat = await testMqtt("", null, "-> KEYWORD <-", "test", "test", conditions); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Topic: test; Message: -> KEYWORD <-"); + }); + + test("check() sets status to UP when topic condition matches (equals)", async () => { + const conditions = JSON.stringify([ + { + type: "expression", + variable: "topic", + operator: "equals", + value: "sensors/temp", + }, + ]); + const heartbeat = await testMqtt("", null, "any message", "sensors/temp", "sensors/temp", conditions); + assert.strictEqual(heartbeat.status, UP); + }); + + test("check() rejects when message condition does not match", async () => { + const conditions = JSON.stringify([ + { + type: "expression", + variable: "message", + operator: "contains", + value: "EXPECTED", + }, + ]); + await assert.rejects( + testMqtt("", null, "actual message without keyword", "test", "test", conditions), + new Error("Conditions not met - Topic: test; Message: actual message without keyword") + ); + }); + + test("check() sets status to UP with multiple conditions (AND)", async () => { + const conditions = JSON.stringify([ + { + type: "expression", + variable: "topic", + operator: "equals", + value: "test", + }, + { + type: "expression", + variable: "message", + operator: "contains", + value: "success", + andOr: "and", + }, + ]); + const heartbeat = await testMqtt("", null, "operation success", "test", "test", conditions); + assert.strictEqual(heartbeat.status, UP); + }); + } +); diff --git a/test/backend-test/monitors/test-mssql.js b/test/backend-test/monitors/test-mssql.js new file mode 100644 index 0000000..cacbbf8 --- /dev/null +++ b/test/backend-test/monitors/test-mssql.js @@ -0,0 +1,266 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { MSSQLServerContainer } = require("@testcontainers/mssqlserver"); +const { MssqlMonitorType } = require("../../../server/monitor-types/mssql"); +const { UP, PENDING } = require("../../../src/util"); + +/** + * Helper function to create and start a MSSQL container + * @returns {Promise<{container: MSSQLServerContainer, connectionString: string}>} The started container and connection string + */ +async function createAndStartMSSQLContainer() { + const container = await new MSSQLServerContainer("mcr.microsoft.com/mssql/server:2022-latest") + .acceptLicense() + // The default timeout of 30 seconds might not be enough for the container to start + .withStartupTimeout(60000) + .start(); + + return { + container, + connectionString: container.getConnectionUri(false), + }; +} + +describe( + "MSSQL Monitor", + { + skip: + (!!process.env.CI && (process.platform !== "linux" || process.arch !== "x64")) || + process.env.SKIP_TESTCONTAINER, + }, + () => { + test("check() sets status to UP when MSSQL server is reachable", async () => { + const { container, connectionString } = await createAndStartMSSQLContainer(); + + const mssqlMonitor = new MssqlMonitorType(); + const monitor = { + databaseConnectionString: connectionString, + conditions: "[]", + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await mssqlMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP, `Expected status ${UP} but got ${heartbeat.status}`); + } finally { + await container.stop(); + } + }); + + test("check() rejects when MSSQL server is not reachable", async () => { + const mssqlMonitor = new MssqlMonitorType(); + const monitor = { + databaseConnectionString: + "Server=localhost,15433;Database=master;User Id=Fail;Password=Fail;Encrypt=false", + conditions: "[]", + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects( + mssqlMonitor.check(monitor, heartbeat, {}), + new Error( + "Database connection/query failed: Failed to connect to localhost:15433 - Could not connect (sequence)" + ) + ); + assert.notStrictEqual(heartbeat.status, UP, `Expected status should not be ${heartbeat.status}`); + }); + + test("check() sets status to UP when custom query returns single value", async () => { + const { container, connectionString } = await createAndStartMSSQLContainer(); + + const mssqlMonitor = new MssqlMonitorType(); + const monitor = { + databaseConnectionString: connectionString, + databaseQuery: "SELECT 42", + conditions: "[]", + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await mssqlMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP, `Expected status ${UP} but got ${heartbeat.status}`); + } finally { + await container.stop(); + } + }); + + test("check() sets status to UP when custom query result meets condition", async () => { + const { container, connectionString } = await createAndStartMSSQLContainer(); + + const mssqlMonitor = new MssqlMonitorType(); + const monitor = { + databaseConnectionString: connectionString, + databaseQuery: "SELECT 42 AS value", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "42", + }, + ]), + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await mssqlMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP, `Expected status ${UP} but got ${heartbeat.status}`); + } finally { + await container.stop(); + } + }); + + test("check() rejects when custom query result does not meet condition", async () => { + const { container, connectionString } = await createAndStartMSSQLContainer(); + + const mssqlMonitor = new MssqlMonitorType(); + const monitor = { + databaseConnectionString: connectionString, + databaseQuery: "SELECT 99 AS value", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "42", + }, + ]), + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await assert.rejects( + mssqlMonitor.check(monitor, heartbeat, {}), + new Error("Query result did not meet the specified conditions (99)") + ); + assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`); + } finally { + await container.stop(); + } + }); + + test("check() rejects when query returns no results with conditions", async () => { + const { container, connectionString } = await createAndStartMSSQLContainer(); + + const mssqlMonitor = new MssqlMonitorType(); + const monitor = { + databaseConnectionString: connectionString, + databaseQuery: "SELECT 1 WHERE 1 = 0", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "1", + }, + ]), + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await assert.rejects( + mssqlMonitor.check(monitor, heartbeat, {}), + new Error("Database connection/query failed: Query returned no results") + ); + assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`); + } finally { + await container.stop(); + } + }); + + test("check() rejects when query returns multiple rows with conditions", async () => { + const { container, connectionString } = await createAndStartMSSQLContainer(); + + const mssqlMonitor = new MssqlMonitorType(); + const monitor = { + databaseConnectionString: connectionString, + databaseQuery: "SELECT 1 UNION ALL SELECT 2", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "1", + }, + ]), + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await assert.rejects( + mssqlMonitor.check(monitor, heartbeat, {}), + new Error("Database connection/query failed: Multiple values were found, expected only one value") + ); + assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`); + } finally { + await container.stop(); + } + }); + + test("check() rejects when query returns multiple columns with conditions", async () => { + const { container, connectionString } = await createAndStartMSSQLContainer(); + + const mssqlMonitor = new MssqlMonitorType(); + const monitor = { + databaseConnectionString: connectionString, + databaseQuery: "SELECT 1 AS col1, 2 AS col2", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "1", + }, + ]), + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await assert.rejects( + mssqlMonitor.check(monitor, heartbeat, {}), + new Error("Database connection/query failed: Multiple columns were found, expected only one value") + ); + assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`); + } finally { + await container.stop(); + } + }); + } +); diff --git a/test/backend-test/monitors/test-mysql.js b/test/backend-test/monitors/test-mysql.js new file mode 100644 index 0000000..f8f9f08 --- /dev/null +++ b/test/backend-test/monitors/test-mysql.js @@ -0,0 +1,139 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { MariaDbContainer } = require("@testcontainers/mariadb"); +const { MysqlMonitorType } = require("../../../server/monitor-types/mysql"); +const { UP, PENDING } = require("../../../src/util"); + +/** + * Helper function to create and start a MariaDB container + * @returns {Promise<{container: MariaDbContainer, connectionString: string}>} The started container and connection string + */ +async function createAndStartMariaDBContainer() { + const container = await new MariaDbContainer("mariadb:10.11").withStartupTimeout(90000).start(); + + const connectionString = `mysql://${container.getUsername()}:${container.getUserPassword()}@${container.getHost()}:${container.getPort()}/${container.getDatabase()}`; + + return { + container, + connectionString, + }; +} + +describe( + "MySQL/MariaDB Monitor", + { + skip: + (!!process.env.CI && (process.platform !== "linux" || process.arch !== "x64")) || + process.env.SKIP_TESTCONTAINER, + }, + () => { + test("check() sets status to UP when MariaDB server is reachable", async () => { + const { container, connectionString } = await createAndStartMariaDBContainer(); + + const mysqlMonitor = new MysqlMonitorType(); + const monitor = { + databaseConnectionString: connectionString, + conditions: "[]", + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await mysqlMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP, `Expected status ${UP} but got ${heartbeat.status}`); + } finally { + await container.stop(); + } + }); + + test("check() rejects when MariaDB server is not reachable", async () => { + const mysqlMonitor = new MysqlMonitorType(); + const monitor = { + databaseConnectionString: "mysql://invalid:invalid@localhost:13306/test", + conditions: "[]", + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects(mysqlMonitor.check(monitor, heartbeat, {}), (err) => { + assert.ok( + err.message.includes("Database connection/query failed"), + `Expected error message to include "Database connection/query failed" but got: ${err.message}` + ); + return true; + }); + assert.notStrictEqual(heartbeat.status, UP, `Expected status should not be ${UP}`); + }); + + test("check() sets status to UP when custom query result meets condition", async () => { + const { container, connectionString } = await createAndStartMariaDBContainer(); + + const mysqlMonitor = new MysqlMonitorType(); + const monitor = { + databaseConnectionString: connectionString, + databaseQuery: "SELECT 42 AS value", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "42", + }, + ]), + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await mysqlMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP, `Expected status ${UP} but got ${heartbeat.status}`); + } finally { + await container.stop(); + } + }); + + test("check() rejects when custom query result does not meet condition", async () => { + const { container, connectionString } = await createAndStartMariaDBContainer(); + + const mysqlMonitor = new MysqlMonitorType(); + const monitor = { + databaseConnectionString: connectionString, + databaseQuery: "SELECT 99 AS value", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "42", + }, + ]), + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await assert.rejects( + mysqlMonitor.check(monitor, heartbeat, {}), + new Error("Query result did not meet the specified conditions (99)") + ); + assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`); + } finally { + await container.stop(); + } + }); + } +); diff --git a/test/backend-test/monitors/test-oracledb.js b/test/backend-test/monitors/test-oracledb.js new file mode 100644 index 0000000..3dac3d6 --- /dev/null +++ b/test/backend-test/monitors/test-oracledb.js @@ -0,0 +1,234 @@ +const { after, before, describe, test } = require("node:test"); +const assert = require("node:assert"); +const { OracleDbContainer } = require("@testcontainers/oraclefree"); +const { OracleDbMonitorType } = require("../../../server/monitor-types/oracledb"); +const { UP, PENDING } = require("../../../src/util"); + +const ORACLE_IMAGE = "gvenzl/oracle-free:23-slim-faststart"; +const APP_USER = "uptimekuma"; +const APP_USER_PASSWORD = "Oracle123"; + +/** + * Create a monitor payload for Oracle monitor tests. + * @param {object} overrides Partial monitor overrides + * @returns {object} Monitor payload + */ +function createMonitor(overrides = {}) { + return { + basic_auth_user: APP_USER, + basic_auth_pass: APP_USER_PASSWORD, + conditions: "[]", + ...overrides, + }; +} + +/** + * Create a baseline heartbeat object for Oracle monitor tests. + * @returns {{msg: string, status: string}} Heartbeat payload + */ +function createHeartbeat() { + return { + msg: "", + status: PENDING, + }; +} + +/** + * Helper function to create and start an Oracle container. + * @returns {Promise<{container: import("@testcontainers/oraclefree").StartedOracleDbContainer, connectString: string}>} + */ +async function createAndStartOracleContainer() { + const container = await new OracleDbContainer(ORACLE_IMAGE) + .withUsername(APP_USER) + .withPassword(APP_USER_PASSWORD) + .start(); + + return { + container, + connectString: container.getUrl(), + }; +} + +describe( + "Oracle Database Monitor", + { + skip: + (!!process.env.CI && (process.platform !== "linux" || process.arch !== "x64")) || + process.env.SKIP_TESTCONTAINER, + }, + () => { + /** @type {import("@testcontainers/oraclefree").StartedOracleDbContainer | undefined} */ + let container; + /** @type {string | undefined} */ + let connectString; + + before(async () => { + const oracle = await createAndStartOracleContainer(); + container = oracle.container; + connectString = oracle.connectString; + }); + + after(async () => { + if (container) { + await container.stop(); + } + }); + + test("check() sets status to UP when Oracle server is reachable", async () => { + const oracleMonitor = new OracleDbMonitorType(); + const monitor = createMonitor({ + databaseConnectionString: connectString, + }); + const heartbeat = createHeartbeat(); + + await oracleMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP, `Expected status ${UP} but got ${heartbeat.status}`); + }); + + test("check() rejects when Oracle server is not reachable", async () => { + const oracleMonitor = new OracleDbMonitorType(); + const monitor = createMonitor({ + databaseConnectionString: "localhost:1/FREEPDB1", + }); + const heartbeat = createHeartbeat(); + + await assert.rejects(oracleMonitor.check(monitor, heartbeat, {}), (err) => { + assert.ok( + err.message.includes("Database connection/query failed"), + `Expected error message to include "Database connection/query failed" but got: ${err.message}` + ); + return true; + }); + assert.notStrictEqual(heartbeat.status, UP, `Expected status should not be ${UP}`); + }); + + test("check() sets status to UP when custom query returns single value", async () => { + const oracleMonitor = new OracleDbMonitorType(); + const monitor = createMonitor({ + databaseConnectionString: connectString, + databaseQuery: "SELECT 42 FROM DUAL", + }); + const heartbeat = createHeartbeat(); + + await oracleMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP, `Expected status ${UP} but got ${heartbeat.status}`); + }); + + test("check() sets status to UP when custom query result meets condition", async () => { + const oracleMonitor = new OracleDbMonitorType(); + const monitor = createMonitor({ + databaseConnectionString: connectString, + databaseQuery: "SELECT 42 AS value FROM DUAL", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "42", + }, + ]), + }); + const heartbeat = createHeartbeat(); + + await oracleMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP, `Expected status ${UP} but got ${heartbeat.status}`); + }); + + test("check() rejects when custom query result does not meet condition", async () => { + const oracleMonitor = new OracleDbMonitorType(); + const monitor = createMonitor({ + databaseConnectionString: connectString, + databaseQuery: "SELECT 99 AS value FROM DUAL", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "42", + }, + ]), + }); + const heartbeat = createHeartbeat(); + + await assert.rejects( + oracleMonitor.check(monitor, heartbeat, {}), + new Error("Query result did not meet the specified conditions (99)") + ); + assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`); + }); + + test("check() rejects when query returns no results with conditions", async () => { + const oracleMonitor = new OracleDbMonitorType(); + const monitor = createMonitor({ + databaseConnectionString: connectString, + databaseQuery: "SELECT 1 AS value FROM DUAL WHERE 1 = 0", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "1", + }, + ]), + }); + const heartbeat = createHeartbeat(); + + await assert.rejects( + oracleMonitor.check(monitor, heartbeat, {}), + new Error("Database connection/query failed: Query returned no results") + ); + assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`); + }); + + test("check() rejects when query returns multiple rows with conditions", async () => { + const oracleMonitor = new OracleDbMonitorType(); + const monitor = createMonitor({ + databaseConnectionString: connectString, + databaseQuery: "SELECT 1 AS value FROM DUAL UNION ALL SELECT 2 AS value FROM DUAL", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "1", + }, + ]), + }); + const heartbeat = createHeartbeat(); + + await assert.rejects( + oracleMonitor.check(monitor, heartbeat, {}), + new Error("Database connection/query failed: Multiple values were found, expected only one value") + ); + assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`); + }); + + test("check() rejects when query returns multiple columns with conditions", async () => { + const oracleMonitor = new OracleDbMonitorType(); + const monitor = createMonitor({ + databaseConnectionString: connectString, + databaseQuery: "SELECT 1 AS col1, 2 AS col2 FROM DUAL", + conditions: JSON.stringify([ + { + type: "expression", + andOr: "and", + variable: "result", + operator: "equals", + value: "1", + }, + ]), + }); + const heartbeat = createHeartbeat(); + + await assert.rejects( + oracleMonitor.check(monitor, heartbeat, {}), + new Error("Database connection/query failed: Multiple columns were found, expected only one value") + ); + assert.strictEqual(heartbeat.status, PENDING, `Expected status should not be ${heartbeat.status}`); + }); + } +); diff --git a/test/backend-test/monitors/test-postgres.js b/test/backend-test/monitors/test-postgres.js new file mode 100644 index 0000000..e50d5a9 --- /dev/null +++ b/test/backend-test/monitors/test-postgres.js @@ -0,0 +1,55 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { PostgreSqlContainer } = require("@testcontainers/postgresql"); +const { PostgresMonitorType } = require("../../../server/monitor-types/postgres"); +const { UP, PENDING } = require("../../../src/util"); + +describe( + "Postgres Single Node", + { + skip: + (!!process.env.CI && (process.platform !== "linux" || process.arch !== "x64")) || + process.env.SKIP_TESTCONTAINER, + }, + () => { + test("check() sets status to UP when Postgres server is reachable", async () => { + // The default timeout of 30 seconds might not be enough for the container to start + const postgresContainer = await new PostgreSqlContainer("postgres:latest") + .withStartupTimeout(60000) + .start(); + const postgresMonitor = new PostgresMonitorType(); + const monitor = { + databaseConnectionString: postgresContainer.getConnectionUri(), + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await postgresMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP); + } finally { + postgresContainer.stop(); + } + }); + + test("check() rejects when Postgres server is not reachable", async () => { + const postgresMonitor = new PostgresMonitorType(); + const monitor = { + databaseConnectionString: "http://localhost:15432", + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + // regex match any string + const regex = /.+/; + + await assert.rejects(postgresMonitor.check(monitor, heartbeat, {}), regex); + }); + } +); diff --git a/test/backend-test/monitors/test-rabbitmq.js b/test/backend-test/monitors/test-rabbitmq.js new file mode 100644 index 0000000..668a828 --- /dev/null +++ b/test/backend-test/monitors/test-rabbitmq.js @@ -0,0 +1,236 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { RabbitMQContainer } = require("@testcontainers/rabbitmq"); +const { RabbitMqMonitorType } = require("../../../server/monitor-types/rabbitmq"); +const { UP, PENDING } = require("../../../src/util"); + +describe( + "RabbitMQ Single Node", + { + skip: + (!!process.env.CI && (process.platform !== "linux" || process.arch !== "x64")) || + process.env.SKIP_TESTCONTAINER, + }, + () => { + test("check() sets status to UP when RabbitMQ server is reachable", async () => { + // The default timeout of 30 seconds might not be enough for the container to start + const rabbitMQContainer = await new RabbitMQContainer().withStartupTimeout(60000).start(); + const rabbitMQMonitor = new RabbitMqMonitorType(); + const connectionString = `http://${rabbitMQContainer.getHost()}:${rabbitMQContainer.getMappedPort(15672)}`; + + const monitor = { + rabbitmqNodes: JSON.stringify([connectionString]), + rabbitmqUsername: "guest", + rabbitmqPassword: "guest", + timeout: 10, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + try { + await rabbitMQMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Node is reachable and there are no alerts in the cluster"); + } finally { + rabbitMQContainer.stop(); + } + }); + + test("check() rejects when RabbitMQ server is not reachable", async () => { + const rabbitMQMonitor = new RabbitMqMonitorType(); + const monitor = { + rabbitmqNodes: JSON.stringify(["http://localhost:15672"]), + rabbitmqUsername: "rabbitmqUser", + rabbitmqPassword: "rabbitmqPass", + timeout: 10, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + // regex match any string + const regex = /.+/; + + await assert.rejects(rabbitMQMonitor.check(monitor, heartbeat, {}), regex); + }); + + test("checkSingleNode() succeeds when node is healthy", async () => { + const rabbitMQContainer = await new RabbitMQContainer().withStartupTimeout(60000).start(); + const rabbitMQMonitor = new RabbitMqMonitorType(); + const connectionString = `http://${rabbitMQContainer.getHost()}:${rabbitMQContainer.getMappedPort(15672)}`; + + const monitor = { + name: "Test Monitor", + rabbitmqUsername: "guest", + rabbitmqPassword: "guest", + timeout: 10, + }; + + try { + // Should not throw - just validates the node is healthy + await rabbitMQMonitor.checkSingleNode(monitor, connectionString, "1/1"); + } finally { + rabbitMQContainer.stop(); + } + }); + + test("checkSingleNode() throws error when node is unreachable", async () => { + const rabbitMQMonitor = new RabbitMqMonitorType(); + const monitor = { + name: "Test Monitor", + rabbitmqUsername: "guest", + rabbitmqPassword: "guest", + timeout: 10, + }; + + // Should reject with any error (connection refused, timeout, etc.) + await assert.rejects(rabbitMQMonitor.checkSingleNode(monitor, "http://localhost:15672", "1/1"), Error); + }); + } +); + +describe("RabbitMQ Multi-Node (Mocked)", () => { + test("check() succeeds when first node is healthy", async () => { + const rabbitMQMonitor = new RabbitMqMonitorType(); + const monitor = { + rabbitmqNodes: JSON.stringify(["http://node1:15672", "http://node2:15672"]), + rabbitmqUsername: "guest", + rabbitmqPassword: "guest", + timeout: 10, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + // Mock checkSingleNode to succeed on first call (just don't throw) + let callCount = 0; + rabbitMQMonitor.checkSingleNode = async (mon, url, nodeInfo) => { + callCount++; + // Success - don't throw + }; + + await rabbitMQMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "One of the 2 nodes is reachable and there are no alerts in the cluster"); + assert.strictEqual(callCount, 1, "Should only check first node"); + }); + + test("check() succeeds when second node is healthy after first fails", async () => { + const rabbitMQMonitor = new RabbitMqMonitorType(); + const monitor = { + rabbitmqNodes: JSON.stringify(["http://node1:15672", "http://node2:15672"]), + rabbitmqUsername: "guest", + rabbitmqPassword: "guest", + timeout: 10, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + // Mock checkSingleNode to fail first, succeed second + let callCount = 0; + rabbitMQMonitor.checkSingleNode = async (mon, url, nodeInfo) => { + callCount++; + if (callCount === 1) { + throw new Error("Node 1 connection failed"); + } + // Second call succeeds - don't throw + }; + + await rabbitMQMonitor.check(monitor, heartbeat, {}); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "One of the 2 nodes is reachable and there are no alerts in the cluster"); + assert.strictEqual(callCount, 2, "Should check both nodes"); + }); + + test("check() fails with consolidated error when all nodes are down", async () => { + const rabbitMQMonitor = new RabbitMqMonitorType(); + const monitor = { + rabbitmqNodes: JSON.stringify(["http://node1:15672", "http://node2:15672", "http://node3:15672"]), + rabbitmqUsername: "guest", + rabbitmqPassword: "guest", + timeout: 10, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + // Mock checkSingleNode to always fail + let callCount = 0; + rabbitMQMonitor.checkSingleNode = async (mon, url, nodeInfo) => { + callCount++; + throw new Error(`Connection failed to node ${callCount}`); + }; + + await assert.rejects(rabbitMQMonitor.check(monitor, heartbeat, {}), (error) => { + assert.match(error.message, /All 3 nodes failed/); + assert.match(error.message, /Node 1:/); + assert.match(error.message, /Node 2:/); + assert.match(error.message, /Node 3:/); + return true; + }); + assert.strictEqual(callCount, 3, "Should check all three nodes"); + }); + + test("check() fails when no nodes are configured", async () => { + const rabbitMQMonitor = new RabbitMqMonitorType(); + const monitor = { + rabbitmqNodes: JSON.stringify([]), + rabbitmqUsername: "guest", + rabbitmqPassword: "guest", + timeout: 10, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects(rabbitMQMonitor.check(monitor, heartbeat, {}), /No RabbitMQ nodes configured/); + }); + + test("check() tries all nodes before failing", async () => { + const rabbitMQMonitor = new RabbitMqMonitorType(); + const monitor = { + rabbitmqNodes: JSON.stringify([ + "http://node1:15672", + "http://node2:15672", + "http://node3:15672", + "http://node4:15672", + ]), + rabbitmqUsername: "guest", + rabbitmqPassword: "guest", + timeout: 10, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + const checkedNodes = []; + rabbitMQMonitor.checkSingleNode = async (mon, url, nodeInfo) => { + checkedNodes.push(url); + throw new Error(`Failed: ${url}`); + }; + + await assert.rejects(rabbitMQMonitor.check(monitor, heartbeat, {}), /All 4 nodes failed/); + + assert.strictEqual(checkedNodes.length, 4, "Should check all 4 nodes"); + assert.strictEqual(checkedNodes[0], "http://node1:15672"); + assert.strictEqual(checkedNodes[1], "http://node2:15672"); + assert.strictEqual(checkedNodes[2], "http://node3:15672"); + assert.strictEqual(checkedNodes[3], "http://node4:15672"); + }); +}); diff --git a/test/backend-test/monitors/test-sftp.js b/test/backend-test/monitors/test-sftp.js new file mode 100644 index 0000000..d5afba9 --- /dev/null +++ b/test/backend-test/monitors/test-sftp.js @@ -0,0 +1,482 @@ +"use strict"; + +const { describe, test, before, after } = require("node:test"); +const assert = require("node:assert"); +const { timingSafeEqual } = require("crypto"); +const { constants } = require("fs"); +const { Server, utils: sshUtils } = require("ssh2"); +const { GenericContainer, Wait } = require("testcontainers"); +const { SFTPMonitorType } = require("../../../server/monitor-types/sftp"); +const { UP, PENDING } = require("../../../src/util"); + +// --------------------------------------------------------------------------- +// STATUS_CODE: ssh2 exposes this under utils.sftp (NOT as a top-level export) +// --------------------------------------------------------------------------- +const { STATUS_CODE } = sshUtils.sftp; + +// --------------------------------------------------------------------------- +// Host / client key material — generated once for the entire test suite +// --------------------------------------------------------------------------- + +/** + * ssh2's ed25519 keygen strips leading zero bytes from the public key, corrupting + * about 1 in 256 keys (mscdex/ssh2#1513). Regenerate until the key parses. + * @returns {object} A parsable ed25519 key pair + */ +function generateEd25519KeyPair() { + let keyPair; + do { + keyPair = sshUtils.generateKeyPairSync("ed25519"); + } while (sshUtils.parseKey(keyPair.private) instanceof Error); + return keyPair; +} + +const HOST_KEY = generateEd25519KeyPair().private; +const clientKeyPair = generateEd25519KeyPair(); +/** PEM-encoded private key string to pass as monitor.sshPrivateKey */ +const CLIENT_PRIVATE_KEY = clientKeyPair.private.toString("utf8"); +const parsedClientPubKey = sshUtils.parseKey(clientKeyPair.public); + +const TEST_USER = "testuser"; +const TEST_PASSWORD = "testpassword"; +const EXISTING_PATH = "/existing/path"; + +// --------------------------------------------------------------------------- +// Real SSH/SFTP server container (see the second describe block below) +// --------------------------------------------------------------------------- + +/** Pinned so the suite cannot break when a new image is published upstream. */ +const SSH_IMAGE = "lscr.io/linuxserver/openssh-server:10.3_p1-r0-ls233"; +/** Port sshd listens on inside the linuxserver image (not 22). */ +const SSH_CONTAINER_PORT = 2222; +/** Home directory of the ssh user in the image; used for the path-exists check. */ +const CONTAINER_HOME = "/config"; +/** Passphrase protecting the encrypted key used by the passphrase tests. */ +const KEY_PASSPHRASE = "secretpassphrase"; + +// --------------------------------------------------------------------------- +// Minimal in-process SFTP server +// --------------------------------------------------------------------------- + +/** + * @typedef {object} SftpServerOptions + * @property {boolean} [acceptPassword=false] Accept password auth for TEST_USER / TEST_PASSWORD + * @property {boolean} [acceptPublicKey=false] Accept publickey auth using CLIENT_PRIVATE_KEY + * @property {string[]} [existingPaths=[]] Paths reported as existing by STAT / LSTAT + */ + +/** + * Spins up a minimal in-process SFTP server and resolves once listening. + * The server uses a dynamically assigned port (port 0) so tests never conflict. + * @param {SftpServerOptions} opts Server behaviour options + * @returns {Promise<{port: number, close: Function}>} Bound port and async close helper + */ +function createSftpServer(opts = {}) { + const { acceptPassword = false, acceptPublicKey = false, existingPaths = [] } = opts; + + return new Promise((resolve, reject) => { + const server = new Server({ hostKeys: [HOST_KEY] }, (client) => { + client.on("authentication", (ctx) => { + if (ctx.username !== TEST_USER) { + return ctx.reject(); + } + + if (ctx.method === "password" && acceptPassword) { + return ctx.password === TEST_PASSWORD ? ctx.accept() : ctx.reject(); + } + + if (ctx.method === "publickey" && acceptPublicKey) { + const keyMatch = + ctx.key.algo === parsedClientPubKey.type && + timingSafeEqual(ctx.key.data, parsedClientPubKey.getPublicSSH()); + // Accept the auth if the key matches (signature verified by SSH protocol itself) + return keyMatch ? ctx.accept() : ctx.reject(); + } + + return ctx.reject(); + }); + + client.on("ready", () => { + client.on("session", (acceptSession) => { + const session = acceptSession(); + + session.on("sftp", (acceptSftp) => { + const sftp = acceptSftp(); + + // REALPATH is required by ssh2-sftp-client on connect to resolve "." + sftp.on("REALPATH", (reqid, reqPath) => { + sftp.name(reqid, [{ filename: reqPath, longname: reqPath, attrs: {} }]); + }); + + /** + * Handle both STAT and LSTAT: return directory attrs for known paths, + * NO_SUCH_FILE for everything else. + * @param {number} reqid SFTP request identifier + * @param {string} reqPath Path the client is querying + * @returns {void} + */ + function handleStat(reqid, reqPath) { + if (existingPaths.includes(reqPath)) { + const mode = constants.S_IFDIR | 0o755; + sftp.attrs(reqid, { mode, uid: 0, gid: 0, size: 0, atime: 0, mtime: 0 }); + } else { + sftp.status(reqid, STATUS_CODE.NO_SUCH_FILE); + } + } + + sftp.on("STAT", handleStat); + sftp.on("LSTAT", handleStat); + }); + }); + }); + + // Suppress per-client errors so the test runner doesn't see them as uncaught + client.on("error", () => {}); + }); + + server.on("error", reject); + + // Port 0 → OS picks a free port + server.listen(0, "127.0.0.1", () => { + const { port } = server.address(); + resolve({ + port, + close: () => new Promise((res) => server.close(res)), + }); + }); + }); +} + +// --------------------------------------------------------------------------- +// Fixtures +// --------------------------------------------------------------------------- + +/** + * Builds a minimal monitor object; callers override only the fields they need. + * @param {object} overrides Fields to merge on top of the defaults + * @returns {object} Monitor configuration + */ +function makeMonitor(overrides) { + return { + hostname: "127.0.0.1", + port: null, + sshUsername: TEST_USER, + sshAuthMethod: "password", + sshPassword: TEST_PASSWORD, + sshPrivateKey: null, + sshPassphrase: null, + sftpPath: null, + ...overrides, + }; +} + +/** + * Returns a fresh heartbeat object in PENDING state. + * @returns {{ msg: string, status: number }} Heartbeat object + */ +function makeHeartbeat() { + return { msg: "", status: PENDING }; +} + +// --------------------------------------------------------------------------- +// Test suite +// --------------------------------------------------------------------------- + +describe("SFTP Monitor", () => { + let monitor; + + before(() => { + monitor = new SFTPMonitorType(); + }); + + // ── Happy paths ───────────────────────────────────────────────────────── + + test("check() sets status to UP with correct password", async (t) => { + const { port, close } = await createSftpServer({ acceptPassword: true }); + t.after(close); + + const heartbeat = makeHeartbeat(); + await monitor.check(makeMonitor({ port }), heartbeat, {}); + + assert.strictEqual(heartbeat.status, UP); + assert.ok(heartbeat.msg.toLowerCase().includes("successful"), `unexpected msg: "${heartbeat.msg}"`); + }); + + test("check() sets status to UP with correct SSH private key", async (t) => { + const { port, close } = await createSftpServer({ acceptPublicKey: true }); + t.after(close); + + const heartbeat = makeHeartbeat(); + await monitor.check( + makeMonitor({ + port, + sshAuthMethod: "privateKey", + sshPrivateKey: CLIENT_PRIVATE_KEY, + }), + heartbeat, + {} + ); + + assert.strictEqual(heartbeat.status, UP); + assert.ok(heartbeat.msg.toLowerCase().includes("successful"), `unexpected msg: "${heartbeat.msg}"`); + }); + + test("check() sets status to UP when sftpPath exists on server", async (t) => { + const { port, close } = await createSftpServer({ + acceptPassword: true, + existingPaths: [EXISTING_PATH], + }); + t.after(close); + + const heartbeat = makeHeartbeat(); + await monitor.check(makeMonitor({ port, sftpPath: EXISTING_PATH }), heartbeat, {}); + + assert.strictEqual(heartbeat.status, UP); + }); + + // ── Error paths ───────────────────────────────────────────────────────── + + test("check() throws when connection is refused (server offline)", async () => { + const heartbeat = makeHeartbeat(); + // Port 19922 has nothing listening — OS immediately refuses the connection + await assert.rejects(monitor.check(makeMonitor({ port: 19922 }), heartbeat, {})); + }); + + test("check() throws when password is wrong", async (t) => { + const { port, close } = await createSftpServer({ acceptPassword: true }); + t.after(close); + + const heartbeat = makeHeartbeat(); + await assert.rejects(monitor.check(makeMonitor({ port, sshPassword: "wrongpassword" }), heartbeat, {})); + }); + + test("check() throws when private key is missing for privateKey auth", async (t) => { + const { port, close } = await createSftpServer({ acceptPublicKey: true }); + t.after(close); + + const heartbeat = makeHeartbeat(); + await assert.rejects( + monitor.check( + makeMonitor({ + port, + sshAuthMethod: "privateKey", + sshPrivateKey: null, + }), + heartbeat, + {} + ), + /private key is required/i + ); + }); + + test("check() throws when sftpPath does not exist on server", async (t) => { + const { port, close } = await createSftpServer({ acceptPassword: true }); + t.after(close); + + const heartbeat = makeHeartbeat(); + await assert.rejects( + monitor.check(makeMonitor({ port, sftpPath: "/does/not/exist" }), heartbeat, {}), + /does not exist/i + ); + }); + + test("check() throws when private key content is invalid", async (t) => { + const { port, close } = await createSftpServer({ acceptPublicKey: true }); + t.after(close); + + const heartbeat = makeHeartbeat(); + await assert.rejects( + monitor.check( + makeMonitor({ + port, + sshAuthMethod: "privateKey", + sshPrivateKey: + "-----BEGIN OPENSSH PRIVATE KEY-----\nNOTAREALKEY\n-----END OPENSSH PRIVATE KEY-----", + }), + heartbeat, + {} + ) + ); + }); +}); + +// --------------------------------------------------------------------------- +// Integration suite against a real OpenSSH server. +// +// The suite above uses a hand-rolled in-process SFTP server: it is fast and runs +// on every CI lane, but it only implements REALPATH/STAT/LSTAT and never performs +// a real key exchange. These tests cover what that one structurally cannot — +// a genuine handshake, algorithm negotiation and real `exists()` semantics. +// +// They also use an RSA key, whereas the in-process suite uses ed25519. +// +// Skipped on CI outside linux/x64 (the standard guard used by every other +// container-backed test in this repo) because Docker is unavailable there. +// --------------------------------------------------------------------------- + +describe( + "SFTP Monitor (real OpenSSH server)", + { + skip: !!process.env.CI && (process.platform !== "linux" || process.arch !== "x64"), + }, + () => { + let monitor; + let container; + let host; + let port; + let rsaPrivateKey; + let encryptedPrivateKey; + + before(async () => { + monitor = new SFTPMonitorType(); + + const rsaKeyPair = sshUtils.generateKeyPairSync("rsa", { bits: 2048 }); + rsaPrivateKey = rsaKeyPair.private.toString("utf8"); + + // A second key, encrypted at rest, to exercise the passphrase branch. + const encryptedKeyPair = sshUtils.generateKeyPairSync("rsa", { + bits: 2048, + passphrase: KEY_PASSPHRASE, + cipher: "aes256-cbc", + }); + encryptedPrivateKey = encryptedKeyPair.private.toString("utf8"); + + container = await new GenericContainer(SSH_IMAGE) + .withEnvironment({ + PUID: "1000", + PGID: "1000", + TZ: "Etc/UTC", + USER_NAME: TEST_USER, + USER_PASSWORD: TEST_PASSWORD, + PASSWORD_ACCESS: "true", + // Public keys are emitted by ssh2 in OpenSSH "ssh-rsa AAAA..." form, + // which is exactly what this image appends to authorized_keys. + // PUBLIC_KEY takes a single key, so both are passed newline-separated. + PUBLIC_KEY: `${rsaKeyPair.public}\n${encryptedKeyPair.public}`, + SUDO_ACCESS: "false", + LOG_STDOUT: "true", + }) + .withExposedPorts(SSH_CONTAINER_PORT) + // sshd is started by the image's init system, so waiting on the port + // alone would race; wait for init to report completion instead. + .withWaitStrategy(Wait.forLogMessage(/\[ls\.io-init\] done\./)) + .withStartupTimeout(120000) + .start(); + + host = container.getHost(); + port = container.getMappedPort(SSH_CONTAINER_PORT); + }); + + after(async () => { + await container?.stop(); + }); + + test("check() sets status to UP with correct password", async () => { + const heartbeat = makeHeartbeat(); + await monitor.check(makeMonitor({ hostname: host, port }), heartbeat, {}); + + assert.strictEqual(heartbeat.status, UP); + assert.ok(heartbeat.msg.toLowerCase().includes("successful"), `unexpected msg: "${heartbeat.msg}"`); + }); + + test("check() sets status to UP with correct RSA private key", async () => { + const heartbeat = makeHeartbeat(); + await monitor.check( + makeMonitor({ + hostname: host, + port, + sshAuthMethod: "privateKey", + sshPrivateKey: rsaPrivateKey, + }), + heartbeat, + {} + ); + + assert.strictEqual(heartbeat.status, UP); + assert.ok(heartbeat.msg.toLowerCase().includes("successful"), `unexpected msg: "${heartbeat.msg}"`); + }); + + test("check() sets status to UP with passphrase-protected private key", async () => { + const heartbeat = makeHeartbeat(); + await monitor.check( + makeMonitor({ + hostname: host, + port, + sshAuthMethod: "privateKey", + sshPrivateKey: encryptedPrivateKey, + sshPassphrase: KEY_PASSPHRASE, + }), + heartbeat, + {} + ); + + assert.strictEqual(heartbeat.status, UP); + assert.ok(heartbeat.msg.toLowerCase().includes("successful"), `unexpected msg: "${heartbeat.msg}"`); + }); + + test("check() throws when passphrase is missing for an encrypted private key", async () => { + const heartbeat = makeHeartbeat(); + await assert.rejects( + monitor.check( + makeMonitor({ + hostname: host, + port, + sshAuthMethod: "privateKey", + sshPrivateKey: encryptedPrivateKey, + sshPassphrase: null, + }), + heartbeat, + {} + ), + /passphrase/i + ); + }); + + test("check() throws when passphrase is wrong", async () => { + const heartbeat = makeHeartbeat(); + await assert.rejects( + monitor.check( + makeMonitor({ + hostname: host, + port, + sshAuthMethod: "privateKey", + sshPrivateKey: encryptedPrivateKey, + sshPassphrase: "wrongpassphrase", + }), + heartbeat, + {} + ) + ); + }); + + test("check() records heartbeat.ping as the connection latency", async () => { + const heartbeat = makeHeartbeat(); + await monitor.check(makeMonitor({ hostname: host, port }), heartbeat, {}); + + assert.strictEqual(typeof heartbeat.ping, "number"); + assert.ok(heartbeat.ping >= 0, `ping should be non-negative, got ${heartbeat.ping}`); + }); + + test("check() sets status to UP when sftpPath exists on server", async () => { + const heartbeat = makeHeartbeat(); + await monitor.check(makeMonitor({ hostname: host, port, sftpPath: CONTAINER_HOME }), heartbeat, {}); + + assert.strictEqual(heartbeat.status, UP); + }); + + test("check() throws when sftpPath does not exist on server", async () => { + const heartbeat = makeHeartbeat(); + await assert.rejects( + monitor.check(makeMonitor({ hostname: host, port, sftpPath: "/does/not/exist" }), heartbeat, {}), + /does not exist/i + ); + }); + + test("check() throws when password is wrong", async () => { + const heartbeat = makeHeartbeat(); + await assert.rejects( + monitor.check(makeMonitor({ hostname: host, port, sshPassword: "wrongpassword" }), heartbeat, {}) + ); + }); + } +); diff --git a/test/backend-test/monitors/test-steam.js b/test/backend-test/monitors/test-steam.js new file mode 100644 index 0000000..2540055 --- /dev/null +++ b/test/backend-test/monitors/test-steam.js @@ -0,0 +1,140 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { SteamMonitorType } = require("../../../server/monitor-types/steam"); +const { UP, PENDING } = require("../../../src/util"); + +describe("Steam Monitor", () => { + test("resolveSteamHostname() returns IP addresses without DNS lookup", async () => { + let lookupCalled = false; + const steamMonitor = new SteamMonitorType({ + lookup: async () => { + lookupCalled = true; + }, + }); + + assert.strictEqual(await steamMonitor.resolveSteamHostname("192.0.2.10"), "192.0.2.10"); + assert.strictEqual(await steamMonitor.resolveSteamHostname("2001:db8::10"), "2001:db8::10"); + assert.strictEqual(lookupCalled, false); + }); + + test("buildServerFilter() resolves hostnames before building the Steam API addr filter", async () => { + let capturedHostname = null; + let capturedOptions = null; + const steamMonitor = new SteamMonitorType({ + lookup: async (hostname, options) => { + capturedHostname = hostname; + capturedOptions = options; + return [ + { + address: "203.0.113.10", + family: 4, + }, + ]; + }, + }); + + const filter = await steamMonitor.buildServerFilter("server.example.com", 27015); + + assert.strictEqual(filter, "addr\\203.0.113.10:27015"); + assert.strictEqual(capturedHostname, "server.example.com"); + assert.deepStrictEqual(capturedOptions, { all: true }); + }); + + test("resolveSteamHostname() prefers IPv4 addresses returned by DNS lookup", async () => { + const steamMonitor = new SteamMonitorType({ + lookup: async () => { + return [ + { + address: "2001:db8::20", + family: 6, + }, + { + address: "203.0.113.20", + family: 4, + }, + ]; + }, + }); + + assert.strictEqual(await steamMonitor.resolveSteamHostname("server.example.com"), "203.0.113.20"); + }); + + test("check() uses the resolved IP address in the Steam API filter", async () => { + let capturedUrl = null; + let capturedOptions = null; + const steamMonitor = new SteamMonitorType({ + lookup: async () => { + return [ + { + address: "203.0.113.30", + family: 4, + }, + ]; + }, + getSteamAPIKey: async () => "test-steam-api-key", + steamApiClient: { + get: async (url, options) => { + capturedUrl = url; + capturedOptions = options; + return { + data: { + response: { + servers: [ + { + name: "Test Steam Server", + }, + ], + }, + }, + }; + }, + }, + ping: async () => 42, + }); + + const monitor = { + hostname: "server.example.com", + port: 27015, + timeout: 30, + maxredirects: 10, + packetSize: 56, + getIgnoreTls: () => false, + getAcceptedStatuscodes: () => ["200"], + }; + const heartbeat = { + msg: "", + status: PENDING, + }; + + await steamMonitor.check(monitor, heartbeat); + + assert.strictEqual(capturedUrl, "https://api.steampowered.com/IGameServersService/GetServerList/v1/"); + assert.strictEqual(capturedOptions.params.filter, "addr\\203.0.113.30:27015"); + assert.strictEqual(capturedOptions.params.key, "test-steam-api-key"); + assert.strictEqual(heartbeat.status, UP); + assert.strictEqual(heartbeat.msg, "Test Steam Server"); + assert.strictEqual(heartbeat.ping, 42); + }); + + test("check() does not resolve hostnames when the Steam API key is missing", async () => { + let lookupCalled = false; + const steamMonitor = new SteamMonitorType({ + lookup: async () => { + lookupCalled = true; + }, + getSteamAPIKey: async () => "", + }); + + const monitor = { + hostname: "server.example.com", + port: 27015, + }; + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects(steamMonitor.check(monitor, heartbeat), /Steam API Key not found/); + assert.strictEqual(lookupCalled, false); + }); +}); diff --git a/test/backend-test/monitors/test-tcp.js b/test/backend-test/monitors/test-tcp.js new file mode 100644 index 0000000..6466ad4 --- /dev/null +++ b/test/backend-test/monitors/test-tcp.js @@ -0,0 +1,307 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { TCPMonitorType } = require("../../../server/monitor-types/tcp"); +const { UP, PENDING } = require("../../../src/util"); +const net = require("net"); +const tls = require("node:tls"); +const { retryExternalService } = require("../test-util"); + +// Long-lived self-signed certificate for "localhost", only used by the local test server below. +const TEST_TLS_CERT = ` +-----BEGIN CERTIFICATE----- +MIIBmzCCAUGgAwIBAgIULOZ/IPXjnktyuutdVTqyHOYVJAswCgYIKoZIzj0EAwIw +FDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTI2MDkxNjEwMjQyM1oYDzIxMjYwODIz +MTAyNDIzWjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjO +PQMBBwNCAASqopjGRvobCZ0LTWtHKPL/hUkNIBC9sO/zmHS+KbzZnbv6rYWzrEq4 +kmykegJ1XlpjrhAvnaKrUC+EhLPKVIKho28wbTAdBgNVHQ4EFgQU5PjoP18qZSQb +M9I2fOFi/IelGz0wHwYDVR0jBBgwFoAU5PjoP18qZSQbM9I2fOFi/IelGz0wDwYD +VR0TAQH/BAUwAwEB/zAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwCgYIKoZI +zj0EAwIDSAAwRQIhAIIhPs4ZhDTiBAUdXYhZI2/dzIffT4vfcewYM0Aa8DmDAiAU +gF9s+ViNQIIXMYU1Su2ulLAVBkwXwBNbsznHhA4fCg== +-----END CERTIFICATE----- +`; + +const TEST_TLS_KEY = ` +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg1r5pxb+fJfjjPm4l +tpS2kbUpNgRYIL2YX1/Vwi1ZjV6hRANCAASqopjGRvobCZ0LTWtHKPL/hUkNIBC9 +sO/zmHS+KbzZnbv6rYWzrEq4kmykegJ1XlpjrhAvnaKrUC+EhLPKVIKh +-----END PRIVATE KEY----- +`; + +describe("TCP Monitor", () => { + /** + * Creates a TCP server on a specified port + * @param {number} port - The port number to listen on + * @returns {Promise} A promise that resolves with the created server + */ + async function createTCPServer(port) { + return new Promise((resolve, reject) => { + const server = net.createServer(); + + server.listen(port, () => { + resolve(server); + }); + + server.on("error", (err) => { + reject(err); + }); + }); + } + + test("check() sets status to UP when TCP server is reachable", async () => { + const port = 12345; + const server = await createTCPServer(port); + + try { + const tcpMonitor = new TCPMonitorType(); + + const monitor = { + hostname: "localhost", + port: port, + isEnabledExpiryNotification: () => false, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await tcpMonitor.check(monitor, heartbeat, {}); + + assert.strictEqual(heartbeat.status, UP); + } finally { + server.close(); + } + }); + + test("check() rejects with connection failed when TCP server is not running", async () => { + const tcpMonitor = new TCPMonitorType(); + + const monitor = { + hostname: "localhost", + port: 54321, + isEnabledExpiryNotification: () => false, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects(tcpMonitor.check(monitor, heartbeat, {}), new Error("Connection failed")); + }); + + test("check() rejects when TLS certificate is expired or invalid", async () => { + const tcpMonitor = new TCPMonitorType(); + + const monitor = { + hostname: "expired.badssl.com", + port: 443, + smtpSecurity: "secure", + isEnabledExpiryNotification: () => true, + handleTlsInfo: async (tlsInfo) => { + return tlsInfo; + }, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + // Regex: contains with "TLS Connection failed:" or "Certificate is invalid" + const regex = /TLS Connection failed:|Certificate is invalid/; + + await retryExternalService(async () => { + await assert.rejects(tcpMonitor.check(monitor, heartbeat, {}), regex); + }); + }); + + test("check() sets status to UP when TLS certificate is valid (SSL)", async () => { + const tcpMonitor = new TCPMonitorType(); + + const monitor = { + hostname: "smtp.gmail.com", + port: 465, + smtpSecurity: "secure", + isEnabledExpiryNotification: () => true, + handleTlsInfo: async (tlsInfo) => { + return tlsInfo; + }, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await retryExternalService(async () => { + await tcpMonitor.check(monitor, heartbeat, {}); + }); + assert.strictEqual(heartbeat.status, UP); + }); + + test("check() sets status to UP when TLS certificate is valid (STARTTLS)", async () => { + const tcpMonitor = new TCPMonitorType(); + + const monitor = { + hostname: "smtp.gmail.com", + port: 587, + smtpSecurity: "starttls", + isEnabledExpiryNotification: () => true, + handleTlsInfo: async (tlsInfo) => { + return tlsInfo; + }, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await retryExternalService(async () => { + await tcpMonitor.check(monitor, heartbeat, {}); + }); + assert.strictEqual(heartbeat.status, UP); + }); + + test("check() rejects when TLS certificate hostname does not match (STARTTLS)", async () => { + const tcpMonitor = new TCPMonitorType(); + + const monitor = { + hostname: "wr-in-f108.1e100.net", + port: 587, + smtpSecurity: "starttls", + isEnabledExpiryNotification: () => true, + handleTlsInfo: async (tlsInfo) => { + return tlsInfo; + }, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + const regex = /does not match certificate/; + + await retryExternalService(async () => { + await assert.rejects(tcpMonitor.check(monitor, heartbeat, {}), regex); + }); + }); + test("check() sets status to UP for XMPP server with valid certificate (STARTTLS)", async () => { + const tcpMonitor = new TCPMonitorType(); + + const monitor = { + hostname: "xmpp.earth", + port: 5222, + smtpSecurity: "starttls", + isEnabledExpiryNotification: () => true, + handleTlsInfo: async (tlsInfo) => { + return tlsInfo; + }, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await retryExternalService(async () => { + await tcpMonitor.check(monitor, heartbeat, {}); + }); + assert.strictEqual(heartbeat.status, UP); + }); + + test("checkTlsCertificate() releases the TLS socket instead of leaving it half-open", async () => { + const tcpMonitor = new TCPMonitorType(); + + const openSockets = new Set(); + + // The server deliberately keeps its side of the connection open after receiving FIN + // (allowHalfOpen + no explicit close), so cleanup must not depend on the peer closing. + const server = tls.createServer({ key: TEST_TLS_KEY, cert: TEST_TLS_CERT, allowHalfOpen: true }, (socket) => { + openSockets.add(socket); + socket.on("close", () => openSockets.delete(socket)); + socket.on("error", () => {}); + socket.on("data", () => {}); + }); + + await new Promise((resolve) => server.listen(0, resolve)); + const port = server.address().port; + + try { + const monitor = { + hostname: "localhost", + port: port, + smtpSecurity: "secure", + isEnabledExpiryNotification: () => true, + handleTlsInfo: async (tlsInfo) => tlsInfo, + }; + + await tcpMonitor.checkTlsCertificate(monitor, { ca: TEST_TLS_CERT }); + + await new Promise((resolve) => setTimeout(resolve, 200)); + + assert.strictEqual( + [...openSockets].filter((socket) => !socket.destroyed).length, + 0, + "checkTlsCertificate() left the TLS socket open after the check" + ); + } finally { + for (const socket of openSockets) { + socket.destroy(); + } + server.close(); + } + }); + + // TLS Alert checking tests + test("check() rejects when expecting TLS alert but connection succeeds", async () => { + const tcpMonitor = new TCPMonitorType(); + + const monitor = { + hostname: "google.com", + port: 443, + expected_tls_alert: "certificate_required", + timeout: 10, + isEnabledExpiryNotification: () => false, + getIgnoreTls: () => false, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + // Retry with backoff for external service reliability, expecting rejection + await retryExternalService(async () => { + await assert.rejects( + tcpMonitor.check(monitor, heartbeat, {}), + /Expected TLS alert 'certificate_required' but connection succeeded/ + ); + }); + }); + + test("parseTlsAlertNumber() extracts alert number from error message", async () => { + const { parseTlsAlertNumber } = require("../../../server/monitor-types/tcp"); + + // Test various error message formats + assert.strictEqual(parseTlsAlertNumber("alert number 116"), 116); + assert.strictEqual(parseTlsAlertNumber("SSL alert number 42"), 42); + assert.strictEqual(parseTlsAlertNumber("TLS alert number 48"), 48); + assert.strictEqual(parseTlsAlertNumber("no alert here"), null); + assert.strictEqual(parseTlsAlertNumber(""), null); + }); + + test("getTlsAlertName() returns correct alert name for known codes", async () => { + const { getTlsAlertName } = require("../../../server/monitor-types/tcp"); + + assert.strictEqual(getTlsAlertName(116), "certificate_required"); + assert.strictEqual(getTlsAlertName(42), "bad_certificate"); + assert.strictEqual(getTlsAlertName(48), "unknown_ca"); + assert.strictEqual(getTlsAlertName(40), "handshake_failure"); + assert.strictEqual(getTlsAlertName(999), "unknown_alert_999"); + }); +}); diff --git a/test/backend-test/monitors/test-websocket.js b/test/backend-test/monitors/test-websocket.js new file mode 100644 index 0000000..759d07f --- /dev/null +++ b/test/backend-test/monitors/test-websocket.js @@ -0,0 +1,426 @@ +const { WebSocketServer } = require("ws"); +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { WebSocketMonitorType } = require("../../../server/monitor-types/websocket-upgrade"); +const { UP, PENDING } = require("../../../src/util"); +const net = require("node:net"); +const http = require("node:http"); + +/** + * Simulates non compliant WS Server, doesn't send Sec-WebSocket-Accept header + * @returns {Promise<{server: net.Server, port: number}>} Promise that resolves to the created server and its port + */ +function nonCompliantWS() { + const srv = net.createServer((socket) => { + socket.once("data", (buf) => { + socket.write( + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n\r\n" + ); + socket.destroy(); + }); + }); + return new Promise((resolve) => { + srv.listen(0, () => { + resolve({ server: srv, port: srv.address().port }); + }); + }); +} + +/** + * Creates a regular HTTP server (non-WebSocket) for testing WebSocket rejection + * @returns {Promise<{server: http.Server, port: number}>} Promise that resolves to the created server and its port + */ +function httpServer() { + const srv = http.createServer((req, res) => { + res.writeHead(200, { "Content-Type": "text/plain" }); + res.end("This is not a WebSocket server"); + }); + return new Promise((resolve) => { + srv.listen(0, () => { + resolve({ server: srv, port: srv.address().port }); + }); + }); +} + +/** + * Creates a WebSocket server for testing + * @param {object} options Options to pass to WebSocketServer + * @returns {Promise<{server: WebSocketServer, port: number}>} Promise that resolves to the created server and its port + */ +function createWebSocketServer(options = {}) { + return new Promise((resolve) => { + const wss = new WebSocketServer({ port: 0, ...options }); + wss.on("listening", () => { + resolve({ server: wss, port: wss.address().port }); + }); + }); +} + +describe("WebSocket Monitor", {}, () => { + test("check() rejects with unexpected server response when connecting to non-WebSocket server", {}, async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: srv, port } = await httpServer(); + t.after(() => srv.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: false, + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects( + websocketMonitor.check(monitor, heartbeat, {}), + new Error("Unexpected server response: 200") + ); + }); + + test("check() sets status to UP when connecting to WebSocket server", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: wss, port } = await createWebSocketServer(); + t.after(() => wss.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: false, + accepted_statuscodes_json: JSON.stringify(["1000"]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + const expected = { + msg: "1000 - OK", + status: UP, + }; + + await websocketMonitor.check(monitor, heartbeat, {}); + assert.deepStrictEqual(heartbeat, expected); + }); + + test("check() sets status to UP when connecting to insecure WebSocket server", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: wss, port } = await createWebSocketServer(); + t.after(() => wss.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: false, + accepted_statuscodes_json: JSON.stringify(["1000"]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + const expected = { + msg: "1000 - OK", + status: UP, + }; + + await websocketMonitor.check(monitor, heartbeat, {}); + assert.deepStrictEqual(heartbeat, expected); + }); + + test("check() rejects when status code does not match expected value", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: wss, port } = await createWebSocketServer(); + t.after(() => wss.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: false, + accepted_statuscodes_json: JSON.stringify(["1001"]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects(websocketMonitor.check(monitor, heartbeat, {}), new Error("Unexpected status code: 1000")); + }); + + test("check() rejects when expected status code is empty", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: wss, port } = await createWebSocketServer(); + t.after(() => wss.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: false, + accepted_statuscodes_json: JSON.stringify([""]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects(websocketMonitor.check(monitor, heartbeat, {}), new Error("Unexpected status code: 1000")); + }); + + test("check() rejects when Sec-WebSocket-Accept header is invalid", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: wss, port } = await nonCompliantWS(); + t.after(() => wss.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: false, + accepted_statuscodes_json: JSON.stringify(["1000"]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects( + websocketMonitor.check(monitor, heartbeat, {}), + new Error("Invalid Sec-WebSocket-Accept header") + ); + }); + + test("check() sets status to UP when ignoring invalid Sec-WebSocket-Accept header", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: wss, port } = await nonCompliantWS(); + t.after(() => wss.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: true, + accepted_statuscodes_json: JSON.stringify(["1000"]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + const expected = { + msg: "1000 - OK", + status: UP, + }; + + await websocketMonitor.check(monitor, heartbeat, {}); + assert.deepStrictEqual(heartbeat, expected); + }); + + test("check() sets status to UP for compliant WebSocket server when ignoring Sec-WebSocket-Accept", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: wss, port } = await createWebSocketServer(); + t.after(() => wss.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: true, + accepted_statuscodes_json: JSON.stringify(["1000"]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + const expected = { + msg: "1000 - OK", + status: UP, + }; + + await websocketMonitor.check(monitor, heartbeat, {}); + assert.deepStrictEqual(heartbeat, expected); + }); + + test("check() rejects non-WebSocket server even when ignoring Sec-WebSocket-Accept", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: srv, port } = await httpServer(); + t.after(() => srv.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: true, + accepted_statuscodes_json: JSON.stringify(["1000"]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects( + websocketMonitor.check(monitor, heartbeat, {}), + new Error("Unexpected server response: 200") + ); + }); + + test("check() rejects when server does not support requested subprotocol", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: wss, port } = await createWebSocketServer({ + handleProtocols: (protocols) => { + // Explicitly reject all subprotocols + return null; + }, + }); + t.after(() => wss.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: false, + wsSubprotocol: "ocpp1.6", + accepted_statuscodes_json: JSON.stringify(["1000"]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects(websocketMonitor.check(monitor, heartbeat, {}), new Error("Server sent no subprotocol")); + }); + + test("check() rejects when multiple subprotocols contain invalid characters", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: wss, port } = await createWebSocketServer(); + t.after(() => wss.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: false, + wsSubprotocol: " # & ,ocpp2.0 [] , ocpp1.6 , ,, ; ", + accepted_statuscodes_json: JSON.stringify(["1000"]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + await assert.rejects( + websocketMonitor.check(monitor, heartbeat, {}), + new SyntaxError("An invalid or duplicated subprotocol was specified") + ); + }); + + test("check() sets status to UP when subprotocol with multiple spaces is accepted", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: wss, port } = await createWebSocketServer({ + handleProtocols: (protocols) => { + return Array.from(protocols).includes("test") ? "test" : null; + }, + }); + t.after(() => wss.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: false, + wsSubprotocol: "invalid , test ", + accepted_statuscodes_json: JSON.stringify(["1000"]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + const expected = { + msg: "1000 - OK", + status: UP, + }; + + await websocketMonitor.check(monitor, heartbeat, {}); + assert.deepStrictEqual(heartbeat, expected); + }); + + test("check() sets status to UP when server supports requested subprotocol", async (t) => { + const websocketMonitor = new WebSocketMonitorType(); + const { server: wss, port } = await createWebSocketServer({ + handleProtocols: (protocols) => { + return Array.from(protocols).includes("test") ? "test" : null; + }, + }); + t.after(() => wss.close()); + + const monitor = { + url: `ws://localhost:${port}`, + wsIgnoreSecWebsocketAcceptHeader: false, + wsSubprotocol: "invalid,test", + accepted_statuscodes_json: JSON.stringify(["1000"]), + timeout: 30, + }; + + const heartbeat = { + msg: "", + status: PENDING, + }; + + const expected = { + msg: "1000 - OK", + status: UP, + }; + + await websocketMonitor.check(monitor, heartbeat, {}); + assert.deepStrictEqual(heartbeat, expected); + }); + + test("buildWsOptions() includes custom headers", async () => { + const websocketMonitor = new WebSocketMonitorType(); + + const options = await websocketMonitor.buildWsOptions({ + headers: JSON.stringify({ + "X-Test": "test-value", + }), + }); + + assert.deepStrictEqual(options.headers, { + "X-Test": "test-value", + }); + }); + + test("buildWsOptions() ignores invalid custom headers JSON", async () => { + const websocketMonitor = new WebSocketMonitorType(); + + const options = await websocketMonitor.buildWsOptions({ + headers: "{ invalid-json", + }); + + assert.deepStrictEqual(options.headers, {}); + }); + + test("buildWsOptions() authentication header overrides custom Authorization header", async () => { + const websocketMonitor = new WebSocketMonitorType(); + + const options = await websocketMonitor.buildWsOptions({ + headers: JSON.stringify({ + Authorization: "Bearer custom-token", + "X-Test": "test-value", + }), + authMethod: "basic", + basic_auth_user: "user", + basic_auth_pass: "pass", + }); + + assert.deepStrictEqual(options.headers, { + Authorization: "Basic dXNlcjpwYXNz", + "X-Test": "test-value", + }); + }); +}); diff --git a/test/backend-test/notification-providers/mock-webhook.js b/test/backend-test/notification-providers/mock-webhook.js new file mode 100644 index 0000000..70cc5fd --- /dev/null +++ b/test/backend-test/notification-providers/mock-webhook.js @@ -0,0 +1,28 @@ +const express = require("express"); +const bodyParser = require("body-parser"); + +/** + * @param {number} port Port number + * @param {string} url Webhook URL + * @param {number} timeout Timeout + * @returns {Promise} Webhook data + */ +async function mockWebhook(port, url, timeout = 2500) { + return new Promise((resolve, reject) => { + const app = express(); + const tmo = setTimeout(() => { + server.close(); + reject({ reason: "Timeout" }); + }, timeout); + app.use(bodyParser.json()); // Middleware to parse JSON bodies + app.post(`/${url}`, (req, res) => { + res.status(200).send("OK"); + server.close(); + tmo && clearTimeout(tmo); + resolve(req.body); + }); + const server = app.listen(port); + }); +} + +module.exports = mockWebhook; diff --git a/test/backend-test/notification-providers/test-apprise-output.js b/test/backend-test/notification-providers/test-apprise-output.js new file mode 100644 index 0000000..253b6ae --- /dev/null +++ b/test/backend-test/notification-providers/test-apprise-output.js @@ -0,0 +1,23 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); + +// Stub promisify-child-process before the provider is loaded so we can simulate +// a successful apprise run that prints nothing to stdout (see #5547). +const childProcessPath = require.resolve("promisify-child-process"); +require.cache[childProcessPath] = { + id: childProcessPath, + filename: childProcessPath, + loaded: true, + exports: { + spawn: () => Promise.resolve({ code: 0, signal: null, stdout: "", stderr: "" }), + }, +}; + +const Apprise = require("../../../server/notification-providers/apprise"); + +describe("Apprise notification provider output handling", () => { + test("send() succeeds when apprise exits 0 without any stdout", async () => { + const result = await new Apprise().send({ appriseURL: "json://example.com" }, "msg"); + assert.strictEqual(result, "Sent Successfully."); + }); +}); diff --git a/test/backend-test/notification-providers/test-apprise.js b/test/backend-test/notification-providers/test-apprise.js new file mode 100644 index 0000000..8e14de6 --- /dev/null +++ b/test/backend-test/notification-providers/test-apprise.js @@ -0,0 +1,72 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { execFile } = require("node:child_process"); +const { promisify } = require("node:util"); +const express = require("express"); +const commandExists = require("command-exists"); + +const Apprise = require("../../../server/notification-providers/apprise"); + +const execFileAsync = promisify(execFile); + +// Apprise is an external CLI. It is bundled in the Docker image, but not +// installed on every dev machine / CI runner, so skip when it is unavailable. +// The `json://` (Custom JSON) service is used as a throwaway local endpoint. +const appriseAvailable = commandExists.sync("apprise"); +const skip = appriseAvailable ? false : "apprise CLI is not installed"; + +describe("Apprise notification provider", { skip }, () => { + test("send() delivers the title and message to a Custom JSON endpoint", async () => { + const app = express(); + app.use(express.json()); + + let received = null; + app.post("/notify", (req, res) => { + received = req.body; + res.status(200).json({ ok: true }); + }); + + const server = await new Promise((resolve) => { + const s = app.listen(0, () => resolve(s)); + }); + + try { + const notification = { + appriseURL: `json://127.0.0.1:${server.address().port}/notify`, + title: "The Title", + }; + + const result = await new Apprise().send(notification, "the body text"); + + assert.strictEqual(result, "Sent Successfully."); + assert.deepStrictEqual(received, { + version: "1.0", + title: "The Title", + message: "the body text", + attachments: [], + type: "info", + }); + } finally { + await new Promise((resolve) => server.close(resolve)); + } + }); + + test("send() rejects when the endpoint cannot be reached", async () => { + // Nothing is listening on port 1, so apprise exits non-zero. + await assert.rejects( + new Apprise().send({ appriseURL: "json://127.0.0.1:1/notify" }, "msg"), + /exited with code/ + ); + }); + + test("xmpp:// service is usable (slixmpp is bundled)", async () => { + // --dry-run only loads/validates the plugin, so no XMPP server is needed. + const { stdout } = await execFileAsync("apprise", [ + "--dry-run", + "-b", + "test", + "xmpp://user:pass@example.com/recipient@example.com", + ]); + assert.match(stdout, /xmpp:\/\//); + }); +}); diff --git a/test/backend-test/notification-providers/test-indigo.js b/test/backend-test/notification-providers/test-indigo.js new file mode 100644 index 0000000..10f7043 --- /dev/null +++ b/test/backend-test/notification-providers/test-indigo.js @@ -0,0 +1,80 @@ +const { describe, test, before, after } = require("node:test"); +const assert = require("node:assert"); +const express = require("express"); + +const Indigo = require("../../../server/notification-providers/indigo"); + +describe("Indigo notification provider", () => { + let server; + let baseUrl; + let requests = []; + let reply = {}; + let status = 200; + + before(async () => { + const app = express(); + app.use(express.json()); + app.post("/v2/api/command", (req, res) => { + requests.push({ auth: req.headers.authorization, body: req.body }); + res.status(status).json(reply); + }); + await new Promise((resolve) => { + server = app.listen(0, resolve); + }); + baseUrl = `http://127.0.0.1:${server.address().port}`; + }); + + after(() => server.close()); + + const notification = (extra) => ({ indigoUrl: `${baseUrl}/`, indigoApiKey: "key", ...extra }); + + test("writes the variable, then executes the action group", async () => { + requests = []; + reply = { success: true }; + const result = await new Indigo().send( + notification({ indigoVariableId: "111", indigoActionGroupId: "222" }), + "[MQTT] [🔴 Down] timeout" + ); + + assert.strictEqual(result, "Sent Successfully."); + assert.deepStrictEqual( + requests.map((r) => r.body), + [ + { + id: "uptime-kuma", + message: "indigo.variable.updateValue", + objectId: 111, + parameters: { value: "[MQTT] [🔴 Down] timeout" }, + }, + { id: "uptime-kuma", message: "indigo.actionGroup.execute", objectId: 222 }, + ] + ); + assert.strictEqual(requests[0].auth, "Bearer key"); + }); + + test("requires a variable or an action group", async () => { + await assert.rejects(new Indigo().send(notification({}), "msg"), /variable ID, an action group ID/); + }); + + test("reports Indigo's HTTP 400 validation errors", async () => { + status = 400; + reply = { + id: "uptime-kuma", + validationErrors: { objectId: "id is not a valid Indigo Action Group" }, + error: "invalid command payload received, id: 'uptime-kuma'", + }; + await assert.rejects( + new Indigo().send(notification({ indigoActionGroupId: "1" }), "msg"), + /not a valid Indigo Action Group/ + ); + status = 200; + }); + + test("reports errors Indigo returns in the response body", async () => { + reply = { error: "object not found", id: "uptime-kuma" }; + await assert.rejects( + new Indigo().send(notification({ indigoActionGroupId: "999" }), "msg"), + /object not found/ + ); + }); +}); diff --git a/test/backend-test/notification-providers/test-notification-provider.js b/test/backend-test/notification-providers/test-notification-provider.js new file mode 100644 index 0000000..06ebab8 --- /dev/null +++ b/test/backend-test/notification-providers/test-notification-provider.js @@ -0,0 +1,34 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); + +const NotificationProvider = require("../../../server/notification-providers/notification-provider"); + +describe("NotificationProvider.throwGeneralAxiosError()", () => { + const provider = new NotificationProvider(); + + test("expands AggregateError causes", () => { + let err1 = new Error("connect ECONNREFUSED 127.0.0.1:443"); + err1.code = "ECONNREFUSED"; + let err2 = new Error("connect ECONNREFUSED ::1:443"); + err2.code = "ECONNREFUSED"; + + let aggErr = new AggregateError([err1, err2], "AggregateError"); + + assert.throws(() => provider.throwGeneralAxiosError(aggErr), { + message: /^AggregateError - caused by: .+/, + }); + }); + + test("expands AggregateError wrapped in error.cause", () => { + let innerErr = new Error("connect ETIMEDOUT 10.0.0.1:443"); + innerErr.code = "ETIMEDOUT"; + + let aggErr = new AggregateError([innerErr], "AggregateError"); + let outerErr = new Error("Request failed"); + outerErr.cause = aggErr; + + assert.throws(() => provider.throwGeneralAxiosError(outerErr), { + message: /^Request failed - caused by: .+/, + }); + }); +}); diff --git a/test/backend-test/notification-providers/test-ntlm.js b/test/backend-test/notification-providers/test-ntlm.js new file mode 100644 index 0000000..5a82620 --- /dev/null +++ b/test/backend-test/notification-providers/test-ntlm.js @@ -0,0 +1,27 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); + +const hash = require("../../../server/modules/axios-ntlm/lib/hash"); + +describe("createPseudoRandomValue()", () => { + test("returns a hexadecimal string with the requested length", () => { + for (const length of [0, 8, 16, 32, 64]) { + const result = hash.createPseudoRandomValue(length); + assert.strictEqual(typeof result, "string"); + assert.strictEqual(result.length, length); + assert.ok(/^[0-9a-f]*$/.test(result)); + } + }); + + test("returns unique values across multiple calls with the same length", () => { + const length = 16; + const iterations = 10; + const results = new Set(); + + for (let i = 0; i < iterations; i++) { + results.add(hash.createPseudoRandomValue(length)); + } + + assert.strictEqual(results.size, iterations); + }); +}); diff --git a/test/backend-test/test-apicache.js b/test/backend-test/test-apicache.js new file mode 100644 index 0000000..e9eb372 --- /dev/null +++ b/test/backend-test/test-apicache.js @@ -0,0 +1,57 @@ +const { after, before, describe, test } = require("node:test"); +const assert = require("node:assert"); +const http = require("node:http"); +const apicache = require("../../server/modules/apicache/apicache").newInstance(); + +/** + * Request the test server. + * @param {number} port Server port + * @param {object} headers Request headers + * @returns {Promise} Server response + */ +function request(port, headers = {}) { + return new Promise((resolve, reject) => { + const req = http.request({ port, path: "/cached", headers }, resolve); + req.on("error", reject); + req.end(); + }); +} + +describe("API cache", () => { + let server; + let port; + + before(async () => { + const cache = apicache.middleware("1 minute"); + server = http.createServer((req, res) => { + cache(req, res, () => { + const body = "cached body"; + res.setHeader("Content-Type", "text/plain"); + res.setHeader("Content-Length", Buffer.byteLength(body)); + res.setHeader("ETag", '"cached"'); + res.end(body); + }); + }); + await new Promise((resolve) => server.listen(0, resolve)); + port = server.address().port; + }); + + after(async () => { + apicache.clear("/cached"); + await new Promise((resolve) => server.close(resolve)); + }); + + test("cached response omits representation headers for a matching ETag", async () => { + const warmResponse = await request(port); + warmResponse.resume(); + await new Promise((resolve) => warmResponse.on("end", resolve)); + + const response = await request(port, { "If-None-Match": '"cached"' }); + response.resume(); + + assert.strictEqual(response.statusCode, 304); + assert.strictEqual(response.headers.etag, '"cached"'); + assert.strictEqual(response.headers["content-length"], undefined); + assert.strictEqual(response.headers["content-type"], undefined); + }); +}); diff --git a/test/backend-test/test-better-auth-disabled-paths.ts b/test/backend-test/test-better-auth-disabled-paths.ts new file mode 100644 index 0000000..a2f0209 --- /dev/null +++ b/test/backend-test/test-better-auth-disabled-paths.ts @@ -0,0 +1,62 @@ +import { test } from "node:test"; +import assert from "node:assert"; +import { auth } from "../../server/better-auth"; + +// @ts-ignore +import TestDB from "../mock-testdb"; + +const testDb = new TestDB("./data/test-better-auth-disabled-paths"); + +// Admin paths disabled in server/better-auth.ts, mapped to their real HTTP method. +const disabledPaths = { + "/admin/create-user": "POST", + "/admin/get-user": "GET", + "/admin/list-users": "GET", + "/admin/update-user": "POST", + "/admin/set-user-password": "POST", + "/admin/set-role": "POST", + "/admin/ban-user": "POST", + "/admin/unban-user": "POST", + "/admin/impersonate-user": "POST", + "/admin/stop-impersonating": "POST", + "/admin/remove-user": "POST", + "/admin/list-user-sessions": "POST", + "/admin/revoke-user-session": "POST", + "/admin/revoke-user-sessions": "POST", + "/admin/has-permission": "POST", +}; + +test("Better Auth disabled admin paths are not accessible", async (t) => { + t.before(async () => { + await testDb.create(); + }); + + t.after(async () => { + console.log("Cleaning up test database..."); + await testDb.destroy(); + }); + + for (const [path, method] of Object.entries(disabledPaths)) { + await t.test(`${method} ${path} returns 404`, async () => { + const options: RequestInit = { + method, + headers: { + "content-type": "application/json", + }, + }; + + if (method === "POST") { + options.body = JSON.stringify({}); + } + + const res = await auth().handler(new Request(`http://localhost:3000/api/auth${path}`, options)); + assert.strictEqual(res.status, 404); + }); + } + + await t.test("GET /ok still works (control)", async () => { + const res = await auth().handler(new Request("http://localhost:3000/api/auth/ok")); + assert.strictEqual(res.status, 200); + assert.deepStrictEqual(await res.json(), { ok: true }); + }); +}); diff --git a/test/backend-test/test-better-auth.ts b/test/backend-test/test-better-auth.ts new file mode 100644 index 0000000..524f99e --- /dev/null +++ b/test/backend-test/test-better-auth.ts @@ -0,0 +1,45 @@ +import { test } from "node:test"; +import { auth, checkPassword } from "../../server/better-auth"; +import assert from "node:assert"; + +// @ts-ignore +import TestDB from "../mock-testdb"; + +const testDb = new TestDB(); + +test("Basic Auth", async (t) => { + t.before(async () => { + await testDb.create(); + await auth().api.createUser({ + body: { + name: "admin", + email: "admin@noreply.uptime-kuma.internal", + password: "Kuma-Test-8f4Q2xR9p", + role: "admin", + data: { + username: "admin", + }, + }, + }); + }); + + t.after(async () => { + console.log("Cleaning up test database..."); + await testDb.destroy(); + }); + + await t.test("[Basic Auth] returns true for valid credentials", async () => { + const result = await checkPassword("admin", "Kuma-Test-8f4Q2xR9p"); + assert.strictEqual(result, true); + }); + + await t.test("returns false for wrong password", async () => { + const result = await checkPassword("admin", "wrongpassword"); + assert.strictEqual(result, false); + }); + + await t.test("returns false for non-existent user", async () => { + const result = await checkPassword("nonexistent", "Kuma-Test-8f4Q2xR9p"); + assert.strictEqual(result, false); + }); +}); diff --git a/test/backend-test/test-cert-hostname-match.js b/test/backend-test/test-cert-hostname-match.js new file mode 100644 index 0000000..f35b7aa --- /dev/null +++ b/test/backend-test/test-cert-hostname-match.js @@ -0,0 +1,49 @@ +const { describe, test } = require("node:test"); + +const assert = require("node:assert"); + +const { checkCertificateHostname } = require("../../server/util-server"); + +const testCert = ` +-----BEGIN CERTIFICATE----- +MIIFCTCCA/GgAwIBAgISBEROD0/r+BjpW4TvWCcZYxjpMA0GCSqGSIb3DQEBCwUA +MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD +EwJSMzAeFw0yMzA5MDQxMjExMThaFw0yMzEyMDMxMjExMTdaMBQxEjAQBgNVBAMM +CSouZWZmLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALywpmHr +GOFlhw9CcW11fVloL6dceeUexbIwVd/gOt0/rIlgBViOGCh1pFYA/Essty4vXBzx +cp6W4WurmwU6ZOJA0/T6rxnmsjxSdrHVGBGgW18HJ9IWqBl9MigjpRo9h4SlAPJq +cAsiBfPhQ0oSe/8IqwgKA4HTvlcTf5/HKnbe0MyQt7WNILWHm+zpfLE0AmLVXxqA +MNc/ynQDLTsWDZnqqri4MKOW1yOAMbUoAWSsNaagoGnZU4bg8uhu/2JTi/vdjl0g +fTDOjsELc70cWekZ9Mv4ND4w3SEthotbMCCtZE5bUqcGzSm4pQEJ37kQ7xjJ0onT +RRcuZI6/jDWzwZ0CAwEAAaOCAjUwggIxMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUE +FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU +hTqVTd8TZ2pknzGJtKw2JaIrPJAwHwYDVR0jBBgwFoAUFC6zF7dYVsuuUAlA5h+v +nYsUwsYwVQYIKwYBBQUHAQEESTBHMCEGCCsGAQUFBzABhhVodHRwOi8vcjMuby5s +ZW5jci5vcmcwIgYIKwYBBQUHMAKGFmh0dHA6Ly9yMy5pLmxlbmNyLm9yZy8wPwYD +VR0RBDgwNoIJKi5lZmYub3JnghEqLnN0YWdpbmcuZWZmLm9yZ4IWd3d3Lmh0dHBz +LXJ1bGVzZXRzLm9yZzATBgNVHSAEDDAKMAgGBmeBDAECATCCAQMGCisGAQQB1nkC +BAIEgfQEgfEA7wB2ALc++yTfnE26dfI5xbpY9Gxd/ELPep81xJ4dCYEl7bSZAAAB +imBRp0EAAAQDAEcwRQIhAMW3HZwWZWXPWfahH2pr/lxCcoSluHv2huAW6rlzU3zn +AiAOzD/p8F3gT1bzDgdSW+X5WDBeU+EutRbHMSV+Cx0mZwB1AHoyjFTYty22IOo4 +4FIe6YQWcDIThU070ivBOlejUutSAAABimBRqRQAAAQDAEYwRAIgFXvRRZS3xx83 +XdTsnto5SxSnGi1+YfzYobMdV1yqHGACIDurLvkt58TwifUbyXflGZJmOMhcC2G1 +KUd29yCUjIahMA0GCSqGSIb3DQEBCwUAA4IBAQA6t2F3PKMLlb2A/JsQhPFUJLS3 +6cx+97dzROQLBdnUQIMxPkJBN/lltNdsVxJa4A3DMbrJOayefX2l8UIvFiEFVseF +WrxbmXDF68fwhBKBgeqZ25/S8jEdP5PWYWXHgXvx0zRdhfe9vuba5WeFyz79cR7K +t3bSyv6GMJ2z3qBkVFGHSeYakcxPWes3CNmGxInwZNBXA2oc7xuncFrjno/USzUI +nEefDfF3H3jC+0iP3IpsK8orwgWz4lOkcMYdan733lSZuVJ6pm7C9phTV04NGF3H +iPenGDCg1awOyRnvxNq1MtMDkR9AHwksukzwiYNexYjyvE2t0UzXhFXwazQ3 +-----END CERTIFICATE----- +`; + +describe("Certificate Hostname Validation", () => { + test("checkCertificateHostname() returns true when certificate matches hostname", () => { + const result = checkCertificateHostname(testCert, "www.eff.org"); + assert.strictEqual(result, true); + }); + + test("checkCertificateHostname() returns false when certificate does not match hostname", () => { + const result = checkCertificateHostname(testCert, "example.com"); + assert.strictEqual(result, false); + }); +}); diff --git a/test/backend-test/test-domain.js b/test/backend-test/test-domain.js new file mode 100644 index 0000000..d48f5ee --- /dev/null +++ b/test/backend-test/test-domain.js @@ -0,0 +1,319 @@ +process.env.UPTIME_KUMA_HIDE_LOG = ["info_db", "info_server"].join(","); + +const { describe, test, mock, before, after } = require("node:test"); +const assert = require("node:assert"); +const DomainExpiry = require("../../server/model/domain_expiry"); +const mockWebhook = require("./notification-providers/mock-webhook"); +const TestDB = require("../mock-testdb"); +const { R } = require("redbean-node"); +const { Notification } = require("../../server/notification"); +const { Settings } = require("../../server/settings"); +const { setSetting } = require("../../server/util-server"); +const dayjs = require("dayjs"); +const { retryExternalService } = require("./test-util"); +dayjs.extend(require("dayjs/plugin/utc")); + +const testDb = new TestDB("./data/test-domain"); + +describe("Domain Expiry", () => { + const monHttpCom = { + type: "http", + url: "https://www.google.com", + domainExpiryNotification: true, + }; + + before(async () => { + await testDb.create(); + Notification.init(); + }); + + after(async () => { + await testDb.destroy(); + }); + + test("getExpiryDate() returns correct expiry date for .wiki domain with no A record", async () => { + await retryExternalService(async () => { + const d = DomainExpiry.createByName("google.wiki"); + assert.deepEqual(await d.getExpiryDate(), new Date("2026-11-26T23:59:59.000Z")); + }); + }); + + describe("checkSupport()", () => { + test("allows and correctly parses http monitor with valid domain", async () => { + const supportInfo = await DomainExpiry.checkSupport(monHttpCom); + let expected = { + domain: "google.com", + tld: "com", + }; + assert.deepStrictEqual(supportInfo, expected); + }); + + describe("Target Validation", () => { + test("throws error for empty string target", async () => { + const monitor = { + type: "http", + url: "", + domainExpiryNotification: true, + }; + await assert.rejects( + async () => await DomainExpiry.checkSupport(monitor), + (error) => { + assert.strictEqual(error.constructor.name, "TranslatableError"); + assert.strictEqual(error.message, "domain_expiry_unsupported_missing_target"); + return true; + } + ); + }); + + test("throws error for undefined target", async () => { + const monitor = { + type: "http", + domainExpiryNotification: true, + }; + await assert.rejects( + async () => await DomainExpiry.checkSupport(monitor), + (error) => { + assert.strictEqual(error.constructor.name, "TranslatableError"); + assert.strictEqual(error.message, "domain_expiry_unsupported_missing_target"); + return true; + } + ); + }); + + test("throws error for null target", async () => { + const monitor = { + type: "http", + url: null, + domainExpiryNotification: true, + }; + await assert.rejects( + async () => await DomainExpiry.checkSupport(monitor), + (error) => { + assert.strictEqual(error.constructor.name, "TranslatableError"); + assert.strictEqual(error.message, "domain_expiry_unsupported_missing_target"); + return true; + } + ); + }); + }); + + describe("Domain Parsing", () => { + test("throws error for non-ICANN TLD (e.g. .local)", async () => { + const monitor = { + type: "http", + url: "https://example.local", + domainExpiryNotification: true, + }; + await assert.rejects( + async () => await DomainExpiry.checkSupport(monitor), + (error) => { + assert.strictEqual(error.constructor.name, "TranslatableError"); + assert.strictEqual(error.message, "domain_expiry_unsupported_is_icann"); + return true; + } + ); + }); + }); + + describe("Edge Cases & RDAP Support", () => { + test("handles subdomain correctly", async () => { + const monitor = { + type: "http", + url: "https://api.staging.example.com/v1/users", + domainExpiryNotification: true, + }; + const supportInfo = await DomainExpiry.checkSupport(monitor); + assert.strictEqual(supportInfo.domain, "example.com"); + assert.strictEqual(supportInfo.tld, "com"); + }); + + test("supports multi-level public suffix via RDAP fallback (e.g. com.br)", async () => { + await retryExternalService(async () => { + const monitor = { + type: "http", + url: "https://record.com.br", + domainExpiryNotification: true, + }; + const supportInfo = await DomainExpiry.checkSupport(monitor); + assert.strictEqual(supportInfo.domain, "record.com.br"); + assert.strictEqual(supportInfo.tld, "br"); + }); + }); + + test("handles complex subdomain correctly", async () => { + const monitor = { + type: "http", + url: "https://mail.subdomain.example.org", + domainExpiryNotification: true, + }; + const supportInfo = await DomainExpiry.checkSupport(monitor); + assert.strictEqual(supportInfo.domain, "example.org"); + assert.strictEqual(supportInfo.tld, "org"); + }); + + test("handles URL with port correctly", async () => { + const monitor = { + type: "http", + url: "https://example.com:8080/api", + domainExpiryNotification: true, + }; + const supportInfo = await DomainExpiry.checkSupport(monitor); + assert.strictEqual(supportInfo.domain, "example.com"); + assert.strictEqual(supportInfo.tld, "com"); + }); + + test("handles URL with query parameters correctly", async () => { + const monitor = { + type: "http", + url: "https://example.com/search?q=test&page=1", + domainExpiryNotification: true, + }; + const supportInfo = await DomainExpiry.checkSupport(monitor); + assert.strictEqual(supportInfo.domain, "example.com"); + assert.strictEqual(supportInfo.tld, "com"); + }); + }); + }); + + test("findByDomainNameOrCreate() retrieves expiration date for .com domain from RDAP", async () => { + await retryExternalService(async () => { + const domain = await DomainExpiry.findByDomainNameOrCreate("google.com"); + const expiryFromRdap = await domain.getExpiryDate(); // from RDAP + assert.deepEqual(expiryFromRdap, new Date("2028-09-14T04:00:00.000Z")); + }); + }); + + test("checkExpiry() caches expiration date in database", async () => { + await retryExternalService(async () => { + await DomainExpiry.checkExpiry("google.com"); // RDAP -> Cache + const domain = await DomainExpiry.findByName("google.com"); + assert(dayjs.utc().diff(dayjs.utc(domain.lastCheck), "second") < 5); + }); + }); + + test("sendNotifications() triggers notification for expiring domain", async () => { + await DomainExpiry.findByName("google.com"); + const hook = { + port: 3010, + url: "capture", + }; + const manyDays = 3650; + await setSetting("domainExpiryNotifyDays", [manyDays], "general"); + const notif = R.convertToBean("notification", { + config: JSON.stringify({ + type: "webhook", + httpMethod: "post", + webhookContentType: "json", + webhookURL: `http://127.0.0.1:${hook.port}/${hook.url}`, + }), + active: 1, + user_id: 1, + name: "Testhook", + }); + const [, data] = await Promise.all([ + DomainExpiry.sendNotifications("google.com", [notif]), + mockWebhook(hook.port, hook.url), + ]); + assert.match(data.msg, /will expire in/); + }); + + test("sendNotifications() handles domain with null expiry without sending NaN", async () => { + // Regression test for bug: "Domain name will expire in NaN days" + // Mock findByDomainNameOrCreate to return a bean with null expiry + const mockDomain = { + domain: "test-null.com", + expiry: null, + lastExpiryNotificationSent: null, + }; + + mock.method(DomainExpiry, "findByDomainNameOrCreate", async () => mockDomain); + + try { + const hook = { + port: 3012, + url: "should-not-be-called-null", + }; + + const notif = { + name: "TestNullExpiry", + config: JSON.stringify({ + type: "webhook", + httpMethod: "post", + webhookContentType: "json", + webhookURL: `http://127.0.0.1:${hook.port}/${hook.url}`, + }), + }; + + // Race between sendNotifications and mockWebhook timeout + // If webhook is called, we fail. If it times out, we pass. + const result = await Promise.race([ + DomainExpiry.sendNotifications("test-null.com", [notif]), + mockWebhook(hook.port, hook.url, 500) + .then(() => { + throw new Error("Webhook was called but should not have been for null expiry"); + }) + .catch((e) => { + if (e.reason === "Timeout") { + return "timeout"; // Expected - webhook was not called + } + throw e; + }), + ]); + + assert.ok(result === undefined || result === "timeout", "Should not send notification for null expiry"); + } finally { + mock.restoreAll(); + } + }); + + test("sendNotifications() handles domain with undefined expiry without sending NaN", async () => { + try { + // Mock findByDomainNameOrCreate to return a bean with undefined expiry (newly created bean scenario) + const mockDomain = { + domain: "test-undefined.com", + expiry: undefined, + lastExpiryNotificationSent: null, + }; + + mock.method(DomainExpiry, "findByDomainNameOrCreate", async () => mockDomain); + + const hook = { + port: 3013, + url: "should-not-be-called-undefined", + }; + + const notif = { + name: "TestUndefinedExpiry", + config: JSON.stringify({ + type: "webhook", + httpMethod: "post", + webhookContentType: "json", + webhookURL: `http://127.0.0.1:${hook.port}/${hook.url}`, + }), + }; + + // Race between sendNotifications and mockWebhook timeout + // If webhook is called, we fail. If it times out, we pass. + const result = await Promise.race([ + DomainExpiry.sendNotifications("test-undefined.com", [notif]), + mockWebhook(hook.port, hook.url, 500) + .then(() => { + throw new Error("Webhook was called but should not have been for undefined expiry"); + }) + .catch((e) => { + if (e.reason === "Timeout") { + return "timeout"; // Expected - webhook was not called + } + throw e; + }), + ]); + + assert.ok( + result === undefined || result === "timeout", + "Should not send notification for undefined expiry" + ); + } finally { + mock.restoreAll(); + } + }); +}); diff --git a/test/backend-test/test-globalping.js b/test/backend-test/test-globalping.js new file mode 100644 index 0000000..a02cbbc --- /dev/null +++ b/test/backend-test/test-globalping.js @@ -0,0 +1,1398 @@ +const { describe, test, mock } = require("node:test"); +const assert = require("node:assert"); +const { encodeBase64 } = require("../../server/util-server"); +const { UP, PENDING } = require("../../src/util"); + +describe("GlobalpingMonitorType", () => { + const { GlobalpingMonitorType } = require("../../server/monitor-types/globalping"); + + describe("ping", () => { + test("should handle successful ping", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createPingMeasurement(); + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + hostname: "example.com", + location: "North America", + ping_count: 3, + protocol: "ICMP", + ipFamily: "ipv4", + }; + + const heartbeat = { + status: PENDING, + msg: "", + ping: 0, + }; + + await monitorType.ping(mockClient, monitor, heartbeat, true); + + assert.strictEqual(mockClient.createMeasurement.mock.calls.length, 1); + assert.deepStrictEqual(mockClient.createMeasurement.mock.calls[0].arguments[0], { + type: "ping", + target: "example.com", + inProgressUpdates: false, + limit: 1, + locations: [{ magic: "North America" }], + measurementOptions: { + packets: 3, + protocol: "ICMP", + ipVersion: 4, + }, + }); + + assert.deepStrictEqual(heartbeat, { + status: UP, + msg: "Ashburn (VA), US, NA, Amazon.com (AS14618), (aws-us-east-1) : OK", + ping: 2.169, + }); + }); + + test("should handle failed ping with status failed", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createPingMeasurement(); + measurement.results[0].result.status = "failed"; + measurement.results[0].result.rawOutput = "Host unreachable"; + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + hostname: "unreachable.example.com", + location: "Europe", + ping_count: 3, + protocol: "ICMP", + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects(monitorType.ping(mockClient, monitor, heartbeat, true), (error) => { + assert.deepStrictEqual( + error, + new Error("Ashburn (VA), US, NA, Amazon.com (AS14618), (aws-us-east-1) : Failed: Host unreachable") + ); + return true; + }); + }); + + test("should handle API error on create measurement", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + error: { + type: "validation_error", + message: "Invalid target", + params: { target: "example.com" }, + }, + }); + createResponse.ok = false; + createResponse.response.status = 400; + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + + const monitor = { + hostname: "example.com", + location: "North America", + ping_count: 3, + protocol: "ICMP", + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects(monitorType.ping(mockClient, monitor, heartbeat, true), (error) => { + assert.deepStrictEqual( + error, + new Error("Failed to create measurement: validation_error Invalid target.\ntarget: example.com") + ); + return true; + }); + }); + + test("should handle API error on await measurement", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const awaitResponse = createMockResponse({ + error: { + type: "internal_error", + message: "Server error", + }, + }); + awaitResponse.ok = false; + awaitResponse.response.status = 400; + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + hostname: "example.com", + location: "North America", + ping_count: 3, + protocol: "ICMP", + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects(monitorType.ping(mockClient, monitor, heartbeat, true), (error) => { + assert.deepStrictEqual( + error, + new Error("Failed to fetch measurement (2g8T7V3OwXG3JV6Y10011zF2v): internal_error Server error.") + ); + return true; + }); + }); + + test("should retry create measurement on status 500", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createPingMeasurement(); + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementationOnce(() => ({ + ok: false, + response: { + status: 500, + }, + })); + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + hostname: "example.com", + location: "North America", + ping_count: 3, + protocol: "ICMP", + ipFamily: "ipv4", + }; + + const heartbeat = { + status: PENDING, + msg: "", + ping: 0, + }; + + await monitorType.ping(mockClient, monitor, heartbeat, true); + + const expectedCreateMeasurement = { + type: "ping", + target: "example.com", + inProgressUpdates: false, + limit: 1, + locations: [{ magic: "North America" }], + measurementOptions: { + packets: 3, + protocol: "ICMP", + ipVersion: 4, + }, + }; + assert.strictEqual(mockClient.createMeasurement.mock.calls.length, 2); + assert.deepStrictEqual(mockClient.createMeasurement.mock.calls[0].arguments[0], expectedCreateMeasurement); + assert.deepStrictEqual(mockClient.createMeasurement.mock.calls[1].arguments[0], expectedCreateMeasurement); + + assert.deepStrictEqual(heartbeat, { + status: UP, + msg: "Ashburn (VA), US, NA, Amazon.com (AS14618), (aws-us-east-1) : OK", + ping: 2.169, + }); + }); + }); + + describe("http", () => { + test("should handle successful HTTP request", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createHttpMeasurement(); + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + url: "https://example.com:444/api/test?test=1", + location: "North America", + method: "GET", + accepted_statuscodes_json: JSON.stringify(["200-299", "300-399"]), + headers: '{"Test-Header": "Test-Value"}', + ipFamily: "ipv4", + dns_resolve_server: "8.8.8.8", + auth_method: "basic", + basic_auth_user: "username", + basic_auth_pass: "password", + }; + + const heartbeat = { + status: PENDING, + msg: "", + ping: 0, + }; + + await monitorType.http(mockClient, monitor, heartbeat, true); + + assert.strictEqual(mockClient.createMeasurement.mock.calls.length, 1); + const expectedToken = encodeBase64(monitor.basic_auth_user, monitor.basic_auth_pass); + assert.deepStrictEqual(mockClient.createMeasurement.mock.calls[0].arguments[0], { + type: "http", + target: "example.com", + inProgressUpdates: false, + limit: 1, + locations: [{ magic: "North America" }], + measurementOptions: { + request: { + host: "example.com", + path: "/api/test", + query: "test=1", + method: "GET", + headers: { + "Test-Header": "Test-Value", + Authorization: `Basic ${expectedToken}`, + }, + }, + port: 444, + protocol: "HTTPS", + ipVersion: 4, + resolver: "8.8.8.8", + }, + }); + + assert.deepStrictEqual(heartbeat, { + status: UP, + msg: "New York (NY), US, NA, MASSIVEGRID (AS49683) : OK", + ping: 1440, + }); + }); + + test("should handle failed HTTP request", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createHttpMeasurement(); + measurement.results[0].result.status = "failed"; + measurement.results[0].result.rawOutput = "Host unreachable"; + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + url: "https://example.com", + location: "North America", + method: "GET", + accepted_statuscodes_json: JSON.stringify(["200-299", "300-399"]), + headers: null, + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects(monitorType.http(mockClient, monitor, heartbeat, true), (error) => { + assert.deepStrictEqual( + error, + new Error("New York (NY), US, NA, MASSIVEGRID (AS49683) : Failed: Host unreachable") + ); + return true; + }); + }); + + test("should handle API error on create measurement", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + error: { + type: "validation_error", + message: "Invalid target", + params: { target: "example.com" }, + }, + }); + createResponse.ok = false; + createResponse.response.status = 400; + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + + const monitor = { + url: "https://example.com", + location: "North America", + method: "GET", + accepted_statuscodes_json: JSON.stringify(["200-299", "300-399"]), + headers: null, + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects(monitorType.http(mockClient, monitor, heartbeat, true), (error) => { + assert.deepStrictEqual( + error, + new Error("Failed to create measurement: validation_error Invalid target.\ntarget: example.com") + ); + return true; + }); + }); + + test("should handle API error on await measurement", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const awaitResponse = createMockResponse({ + error: { + type: "internal_error", + message: "Server error", + }, + }); + awaitResponse.ok = false; + awaitResponse.response.status = 400; + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + url: "https://example.com", + location: "North America", + method: "GET", + accepted_statuscodes_json: JSON.stringify(["200-299", "300-399"]), + headers: null, + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects(monitorType.http(mockClient, monitor, heartbeat, true), (error) => { + assert.deepStrictEqual( + error, + new Error("Failed to fetch measurement (2g8T7V3OwXG3JV6Y10011zF2v): internal_error Server error.") + ); + return true; + }); + }); + + test("should handle invalid status code", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createHttpMeasurement(); + measurement.results[0].result.rawOutput = "RAW OUTPUT"; + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + url: "https://example.com/api/test", + location: "North America", + method: "GET", + accepted_statuscodes_json: JSON.stringify(["200-299"]), + headers: null, + }; + + const heartbeat = { + status: PENDING, + msg: "", + ping: 0, + }; + + await assert.rejects(monitorType.http(mockClient, monitor, heartbeat, true), (error) => { + assert.deepStrictEqual( + error, + new Error( + "New York (NY), US, NA, MASSIVEGRID (AS49683) : Status code 301 not accepted. Output: RAW OUTPUT" + ) + ); + return true; + }); + + // heartbeat.ping should still be set before the error is thrown + assert.strictEqual(heartbeat.ping, 1440); + }); + + test("should handle keyword check (keyword present)", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createHttpMeasurement(); + measurement.results[0].result.rawOutput = "Response body with KEYWORD word"; + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + url: "https://example.com", + location: "North America", + protocol: "HTTPS", + accepted_statuscodes_json: JSON.stringify(["300-399"]), + keyword: "KEYWORD", + invertKeyword: false, + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await monitorType.http(mockClient, monitor, heartbeat, true); + + assert.deepStrictEqual(heartbeat, { + status: UP, + msg: "New York (NY), US, NA, MASSIVEGRID (AS49683) : 301 - Moved Permanently, keyword is found", + ping: 1440, + }); + }); + + test("should handle keyword check (keyword not present)", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createHttpMeasurement(); + measurement.results[0].result.rawOutput = "Response body with KEYWORD word"; + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + url: "https://example.com", + location: "North America", + protocol: "HTTPS", + accepted_statuscodes_json: JSON.stringify(["300-399"]), + keyword: "MISSING_KEYWORD", + invertKeyword: false, + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects(monitorType.http(mockClient, monitor, heartbeat, true), (error) => { + assert.deepStrictEqual( + error, + new Error( + "New York (NY), US, NA, MASSIVEGRID (AS49683) : 301 - Moved Permanently, but keyword is not in [Response body with KEYWORD word]" + ) + ); + return true; + }); + }); + + test("should handle inverted keyword check", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createHttpMeasurement(); + measurement.results[0].result.rawOutput = "Response body with KEYWORD word"; + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + url: "https://example.com", + location: "North America", + protocol: "HTTPS", + accepted_statuscodes_json: JSON.stringify(["300-399"]), + keyword: "ERROR", + invertKeyword: true, + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await monitorType.http(mockClient, monitor, heartbeat, true); + + assert.deepStrictEqual(heartbeat, { + status: UP, + msg: "New York (NY), US, NA, MASSIVEGRID (AS49683) : 301 - Moved Permanently, keyword not found", + ping: 1440, + }); + }); + + test("should handle JSON query check (valid)", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createHttpMeasurement(); + measurement.results[0].result.rawOutput = JSON.stringify({ + status: "success", + value: 42, + }); + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + url: "https://api.example.com/status", + location: "North America", + protocol: "HTTPS", + accepted_statuscodes_json: JSON.stringify(["300-399"]), + jsonPath: "$.status", + jsonPathOperator: "==", + expectedValue: "success", + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await monitorType.http(mockClient, monitor, heartbeat, true); + + assert.deepStrictEqual(heartbeat, { + status: UP, + msg: "New York (NY), US, NA, MASSIVEGRID (AS49683) : JSON query passes (comparing success == success)", + ping: 1440, + }); + }); + + test("should handle JSON query check (invalid)", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createHttpMeasurement(); + measurement.results[0].result.rawOutput = JSON.stringify({ + status: "failed", + value: 42, + }); + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + url: "https://api.example.com/status", + location: "North America", + protocol: "HTTPS", + accepted_statuscodes_json: JSON.stringify(["300-399"]), + jsonPath: "$.status", + jsonPathOperator: "==", + expectedValue: "success", + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects(monitorType.http(mockClient, monitor, heartbeat, true), (error) => { + assert.deepStrictEqual( + error, + new Error( + "New York (NY), US, NA, MASSIVEGRID (AS49683) : JSON query does not pass (comparing failed == success)" + ) + ); + return true; + }); + }); + + test("should retry create measurement on status 500", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createHttpMeasurement(); + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementationOnce(() => ({ + ok: false, + response: { + status: 500, + }, + })); + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + url: "https://example.com:444/api/test?test=1", + location: "North America", + method: "GET", + accepted_statuscodes_json: JSON.stringify(["200-299", "300-399"]), + headers: '{"Test-Header": "Test-Value"}', + ipFamily: "ipv4", + dns_resolve_server: "8.8.8.8", + auth_method: "basic", + basic_auth_user: "username", + basic_auth_pass: "password", + }; + + const heartbeat = { + status: PENDING, + msg: "", + ping: 0, + }; + + await monitorType.http(mockClient, monitor, heartbeat, true); + + const expectedToken = encodeBase64(monitor.basic_auth_user, monitor.basic_auth_pass); + const expectedCreateMeasurement = { + type: "http", + target: "example.com", + inProgressUpdates: false, + limit: 1, + locations: [{ magic: "North America" }], + measurementOptions: { + request: { + host: "example.com", + path: "/api/test", + query: "test=1", + method: "GET", + headers: { + "Test-Header": "Test-Value", + Authorization: `Basic ${expectedToken}`, + }, + }, + port: 444, + protocol: "HTTPS", + ipVersion: 4, + resolver: "8.8.8.8", + }, + }; + assert.strictEqual(mockClient.createMeasurement.mock.calls.length, 2); + assert.deepStrictEqual(mockClient.createMeasurement.mock.calls[0].arguments[0], expectedCreateMeasurement); + assert.deepStrictEqual(mockClient.createMeasurement.mock.calls[1].arguments[0], expectedCreateMeasurement); + + assert.deepStrictEqual(heartbeat, { + status: UP, + msg: "New York (NY), US, NA, MASSIVEGRID (AS49683) : OK", + ping: 1440, + }); + }); + }); + + describe("dns", () => { + test("should handle successful dns", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createDnsMeasurement(); + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createMockResponse(createResponse)); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const redbeanMock = createRedbeanMock(); + redbeanMock.exec.mock.mockImplementation(() => Promise.resolve()); + + const monitor = { + id: "1", + hostname: "example.com", + location: "us-east-1", + dns_resolve_type: "A", + port: 53, + protocol: "udp", + }; + + const heartbeat = { + status: PENDING, + msg: "", + ping: null, + }; + + await monitorType.dns(mockClient, monitor, heartbeat, false, redbeanMock); + + assert.strictEqual(mockClient.createMeasurement.mock.calls.length, 1); + assert.deepStrictEqual(mockClient.createMeasurement.mock.calls[0].arguments[0], { + type: "dns", + target: "example.com", + inProgressUpdates: false, + limit: 1, + locations: [{ magic: "us-east-1" }], + measurementOptions: { + query: { type: "A" }, + port: 53, + protocol: "udp", + }, + }); + + assert.deepStrictEqual(heartbeat, { + status: UP, + msg: "New York (NY), US, NA, MASSIVEGRID (AS49683) : 93.184.216.34", + ping: 25, + }); + + assert.strictEqual(redbeanMock.exec.mock.calls.length, 1); + assert.deepStrictEqual(redbeanMock.exec.mock.calls[0].arguments, [ + "UPDATE `monitor` SET dns_last_result = ? WHERE id = ? ", + ["93.184.216.34", "1"], + ]); + }); + + test("should handle failed dns with status failed", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createDnsMeasurement(); + measurement.results[0].result.status = "failed"; + measurement.results[0].result.rawOutput = "NXDOMAIN"; + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createMockResponse(createResponse)); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + hostname: "nonexistent.example.com", + location: "us-east-1", + dns_resolve_type: "A", + port: 53, + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects( + async () => { + await monitorType.dns(mockClient, monitor, heartbeat, false); + }, + (error) => { + assert.strictEqual( + error.message, + "New York (NY), US, NA, MASSIVEGRID (AS49683) : Failed: NXDOMAIN" + ); + return true; + } + ); + }); + + test("should handle API error on create measurement", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + error: { + type: "validation_error", + message: "Invalid target", + params: { target: "example.com" }, + }, + }); + createResponse.ok = false; + createResponse.response.status = 400; + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + + const monitor = { + hostname: "example.com", + location: "us-east-1", + dns_resolve_type: "A", + port: 53, + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects(monitorType.dns(mockClient, monitor, heartbeat, false), (error) => { + assert.deepStrictEqual( + error, + new Error("Failed to create measurement: validation_error Invalid target.\ntarget: example.com") + ); + return true; + }); + }); + + test("should handle API error on await measurement", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const awaitResponse = createMockResponse({ + error: { + type: "internal_error", + message: "Server error", + }, + }); + awaitResponse.ok = false; + awaitResponse.response.status = 400; + + mockClient.createMeasurement.mock.mockImplementation(() => createResponse); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const monitor = { + hostname: "example.com", + location: "us-east-1", + dns_resolve_type: "A", + port: 53, + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects(monitorType.dns(mockClient, monitor, heartbeat, false), (error) => { + assert.deepStrictEqual( + error, + new Error("Failed to fetch measurement (2g8T7V3OwXG3JV6Y10011zF2v): internal_error Server error.") + ); + return true; + }); + }); + + test("should handle regex matched", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = { + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }; + const measurement = createDnsMeasurement(); + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createMockResponse(createResponse)); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const redbeanMock = createRedbeanMock(); + redbeanMock.exec.mock.mockImplementation(() => Promise.resolve()); + + const monitor = { + id: "1", + hostname: "example.com", + location: "us-east-1", + dns_resolve_type: "A", + port: 53, + protocol: "udp", + keyword: "93\\.184\\.216\\.34", + }; + + const heartbeat = { + status: PENDING, + msg: "", + ping: null, + }; + + await monitorType.dns(mockClient, monitor, heartbeat, false, redbeanMock); + + assert.deepStrictEqual(heartbeat, { + status: UP, + msg: "New York (NY), US, NA, MASSIVEGRID (AS49683) : 93.184.216.34", + ping: 25, + }); + + assert.deepStrictEqual(redbeanMock.exec.mock.calls[0].arguments, [ + "UPDATE `monitor` SET dns_last_result = ? WHERE id = ? ", + ["93.184.216.34", "1"], + ]); + }); + + test("should handle regex not matched", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = { + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }; + const measurement = createDnsMeasurement(); + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementation(() => createMockResponse(createResponse)); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const redbeanMock = createRedbeanMock(); + redbeanMock.exec.mock.mockImplementation(() => Promise.resolve()); + + const monitor = { + id: "1", + hostname: "example.com", + location: "us-east-1", + dns_resolve_type: "A", + port: 53, + protocol: "udp", + keyword: "192\\.168\\.1\\.1", + }; + + const heartbeat = { + status: PENDING, + msg: "", + }; + + await assert.rejects(monitorType.dns(mockClient, monitor, heartbeat, false, redbeanMock), (error) => { + assert.deepStrictEqual( + error, + new Error("New York (NY), US, NA, MASSIVEGRID (AS49683) : No record matched. 93.184.216.34") + ); + return true; + }); + + assert.deepStrictEqual(redbeanMock.exec.mock.calls[0].arguments, [ + "UPDATE `monitor` SET dns_last_result = ? WHERE id = ? ", + ["93.184.216.34", "1"], + ]); + }); + + test("should retry create measurement on status 500", async () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + const mockClient = createGlobalpingClientMock(); + const createResponse = createMockResponse({ + id: "2g8T7V3OwXG3JV6Y10011zF2v", + }); + const measurement = createDnsMeasurement(); + const awaitResponse = createMockResponse(measurement); + + mockClient.createMeasurement.mock.mockImplementationOnce(() => ({ + ok: false, + response: { + status: 500, + }, + })); + mockClient.createMeasurement.mock.mockImplementation(() => createMockResponse(createResponse)); + mockClient.awaitMeasurement.mock.mockImplementation(() => awaitResponse); + + const redbeanMock = createRedbeanMock(); + redbeanMock.exec.mock.mockImplementation(() => Promise.resolve()); + + const monitor = { + id: "1", + hostname: "example.com", + location: "us-east-1", + dns_resolve_type: "A", + port: 53, + protocol: "udp", + }; + + const heartbeat = { + status: PENDING, + msg: "", + ping: null, + }; + + await monitorType.dns(mockClient, monitor, heartbeat, false, redbeanMock); + + const expectedCreateMeasurement = { + type: "dns", + target: "example.com", + inProgressUpdates: false, + limit: 1, + locations: [{ magic: "us-east-1" }], + measurementOptions: { + query: { type: "A" }, + port: 53, + protocol: "udp", + }, + }; + assert.strictEqual(mockClient.createMeasurement.mock.calls.length, 2); + assert.deepStrictEqual(mockClient.createMeasurement.mock.calls[0].arguments[0], expectedCreateMeasurement); + assert.deepStrictEqual(mockClient.createMeasurement.mock.calls[1].arguments[0], expectedCreateMeasurement); + + assert.deepStrictEqual(heartbeat, { + status: UP, + msg: "New York (NY), US, NA, MASSIVEGRID (AS49683) : 93.184.216.34", + ping: 25, + }); + + assert.strictEqual(redbeanMock.exec.mock.calls.length, 1); + assert.deepStrictEqual(redbeanMock.exec.mock.calls[0].arguments, [ + "UPDATE `monitor` SET dns_last_result = ? WHERE id = ? ", + ["93.184.216.34", "1"], + ]); + }); + }); + + describe("helper methods", () => { + test("formatProbeLocation should format location correctly", () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + + const probe = { + city: "New York", + state: "NY", + country: "US", + continent: "NA", + network: "Amazon.com", + asn: 14618, + tags: ["aws-us-east-1", "datacenter"], + }; + + const result = monitorType.formatProbeLocation(probe); + + assert.strictEqual(result, "New York (NY), US, NA, Amazon.com (AS14618), (aws-us-east-1)"); + }); + + test("formatProbeLocation should handle missing state", () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + + const probe = { + city: "London", + state: null, + country: "GB", + continent: "EU", + network: "Example Network", + asn: 12345, + tags: [], + }; + + const result = monitorType.formatProbeLocation(probe); + + assert.strictEqual(result, "London, GB, EU, Example Network (AS12345)"); + }); + + test("formatResponse should combine location and text", () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + + const probe = { + city: "Tokyo", + state: null, + country: "JP", + continent: "AS", + network: "Example ISP", + asn: 54321, + tags: [], + }; + + const result = monitorType.formatResponse(probe, "Test message"); + + assert.strictEqual(result, "Tokyo, JP, AS, Example ISP (AS54321) : Test message"); + }); + + test("formatApiError should format error with params", () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + + const error = { + type: "validation_error", + message: "Invalid request", + params: { + field: "target", + value: "invalid", + }, + }; + + const result = monitorType.formatApiError(error); + + assert.strictEqual(result, "validation_error Invalid request.\nfield: target\nvalue: invalid"); + }); + + test("formatApiError should format error without params", () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + + const error = { + type: "internal_error", + message: "Server error", + }; + + const result = monitorType.formatApiError(error); + + assert.strictEqual(result, "internal_error Server error."); + }); + + test("formatTooManyRequestsError with API token", () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + + const result = monitorType.formatTooManyRequestsError(true); + + assert.strictEqual( + result, + "You have run out of credits. Get higher limits by sponsoring us or hosting probes. Learn more at https://dash.globalping.io?view=add-credits." + ); + }); + + test("formatTooManyRequestsError without API token", () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + + const result = monitorType.formatTooManyRequestsError(false); + + assert.strictEqual( + result, + "You have run out of credits. Get higher limits by creating an account. Sign up at https://dash.globalping.io?view=add-credits." + ); + }); + + test("getBasicAuthHeader should return empty for non-basic auth", () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + + const monitor = { + auth_method: "none", + }; + + const result = monitorType.getBasicAuthHeader(monitor); + + assert.deepStrictEqual(result, {}); + }); + + test("getBasicAuthHeader should return Authorization header", () => { + const monitorType = new GlobalpingMonitorType("test-agent/1.0"); + + const monitor = { + auth_method: "basic", + basic_auth_user: "testuser", + basic_auth_pass: "testpass", + }; + + const result = monitorType.getBasicAuthHeader(monitor); + + const expectedToken = encodeBase64(monitor.basic_auth_user, monitor.basic_auth_pass); + + assert.strictEqual(result.Authorization, `Basic ${expectedToken}`); + }); + }); +}); + +/** + * Reusable mock factory for Globalping client + * @returns {object} Mocked Globalping client + */ +function createGlobalpingClientMock() { + return { + createMeasurement: mock.fn(), + awaitMeasurement: mock.fn(), + }; +} + +/** + * Reusable mock factory for RedBean + * @returns {object} Mocked RedBean + */ +function createRedbeanMock() { + return { + exec: mock.fn(), + }; +} + +/** + * Reusable mock factory for Globalping response + * @param {object} data Response data + * @returns {object} Mocked Globalping response + */ +function createMockResponse(data) { + return { + ok: true, + response: { + status: 200, + }, + data, + }; +} + +/** + * Creates a successful ping measurement response + * @returns {object} Mock measurement response + */ +function createPingMeasurement() { + return { + id: "2g8T7V3OwXG3JV6Y10011zF2v", + type: "ping", + status: "finished", + createdAt: "2025-11-05T08:25:33.173Z", + updatedAt: "2025-11-05T08:25:34.750Z", + target: "google.com", + probesCount: 1, + locations: [{ magic: "us-east-1" }], + results: [ + { + probe: { + continent: "NA", + region: "Northern America", + country: "US", + state: "VA", + city: "Ashburn", + asn: 14618, + longitude: -77.49, + latitude: 39.04, + network: "Amazon.com", + tags: [ + "aws-us-east-1", + "aws", + "datacenter-network", + "u-cloudlookingglass:aws-us-east-1-use1-az6", + "u-cloudlookingglass:aws-us-east-1-use1-az6-net", + ], + resolvers: ["private"], + }, + result: { + status: "finished", + rawOutput: + "PING (142.251.16.100) 56(84) bytes of data.\n64 bytes from bl-in-f100.1e100.net (142.251.16.100): icmp_seq=1 ttl=106 time=2.07 ms\n64 bytes from bl-in-f100.1e100.net (142.251.16.100): icmp_seq=2 ttl=106 time=2.08 ms\n64 bytes from bl-in-f100.1e100.net (142.251.16.100): icmp_seq=3 ttl=106 time=2.35 ms\n\n--- ping statistics ---\n3 packets transmitted, 3 received, 0% packet loss, time 1002ms\nrtt min/avg/max/mdev = 2.073/2.169/2.351/0.128 ms", + resolvedAddress: "142.251.16.100", + resolvedHostname: "bl-in-f100.1e100.net", + timings: [ + { ttl: 106, rtt: 2.07 }, + { ttl: 106, rtt: 2.08 }, + { ttl: 106, rtt: 2.35 }, + ], + stats: { + min: 2.073, + max: 2.351, + avg: 2.169, + total: 3, + loss: 0, + rcv: 3, + drop: 0, + }, + }, + }, + ], + }; +} + +/** + * Creates a successful HTTP measurement response + * @returns {object} Mock measurement response + */ +function createHttpMeasurement() { + return { + id: "2m6DeD067jeT6licX0011zF2x", + type: "http", + status: "finished", + createdAt: "2025-11-05T08:27:29.034Z", + updatedAt: "2025-11-05T08:27:30.718Z", + target: "google.com", + probesCount: 1, + locations: [{ magic: "New York" }], + results: [ + { + probe: { + continent: "NA", + region: "Northern America", + country: "US", + state: "NY", + city: "New York", + asn: 49683, + longitude: -74.01, + latitude: 40.71, + network: "MASSIVEGRID", + tags: ["datacenter-network", "u-gbzret4d"], + resolvers: ["private"], + }, + result: { + status: "finished", + resolvedAddress: "209.85.201.101", + headers: { + location: "https://www.google.com/", + "content-type": "text/html; charset=UTF-8", + "content-security-policy-report-only": + "object-src 'none';base-uri 'self';script-src 'nonce-Eft2LKpM01f69RvQoV6QJA' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp", + date: "Wed, 05 Nov 2025 08:27:30 GMT", + expires: "Fri, 05 Dec 2025 08:27:30 GMT", + "cache-control": "public, max-age=2592000", + server: "gws", + "content-length": "220", + "x-xss-protection": "0", + "x-frame-options": "SAMEORIGIN", + "alt-svc": 'h3=":443"; ma=2592000,h3-29=":443"; ma=2592000', + connection: "close", + }, + rawHeaders: + "Location: https://www.google.com/\nContent-Type: text/html; charset=UTF-8\nContent-Security-Policy-Report-Only: object-src 'none';base-uri 'self';script-src 'nonce-Eft2LKpM01f69RvQoV6QJA' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp\nDate: Wed, 05 Nov 2025 08:27:30 GMT\nExpires: Fri, 05 Dec 2025 08:27:30 GMT\nCache-Control: public, max-age=2592000\nServer: gws\nContent-Length: 220\nX-XSS-Protection: 0\nX-Frame-Options: SAMEORIGIN\nAlt-Svc: h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000\nConnection: close", + rawBody: null, + rawOutput: + "HTTP/1.1 301\nLocation: https://www.google.com/\nContent-Type: text/html; charset=UTF-8\nContent-Security-Policy-Report-Only: object-src 'none';base-uri 'self';script-src 'nonce-Eft2LKpM01f69RvQoV6QJA' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp\nDate: Wed, 05 Nov 2025 08:27:30 GMT\nExpires: Fri, 05 Dec 2025 08:27:30 GMT\nCache-Control: public, max-age=2592000\nServer: gws\nContent-Length: 220\nX-XSS-Protection: 0\nX-Frame-Options: SAMEORIGIN\nAlt-Svc: h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000\nConnection: close", + truncated: false, + statusCode: 301, + statusCodeName: "Moved Permanently", + timings: { + total: 1440, + download: 1, + firstByte: 1391, + dns: 9, + tls: 22, + tcp: 16, + }, + }, + }, + ], + }; +} + +/** + * Creates a successful DNS measurement response + * @returns {object} Mock measurement response + */ +function createDnsMeasurement() { + return { + id: "2g8T7V3OwXG3JV6Y10011zF2v", + type: "dns", + status: "finished", + createdAt: "2025-11-05T08:30:00.000Z", + updatedAt: "2025-11-05T08:30:01.000Z", + target: "example.com", + probesCount: 1, + locations: [{ magic: "us-east-1" }], + results: [ + { + probe: { + continent: "NA", + region: "Northern America", + country: "US", + state: "NY", + city: "New York", + asn: 49683, + longitude: -74.01, + latitude: 40.71, + network: "MASSIVEGRID", + tags: ["datacenter-network", "u-gbzret4d"], + resolvers: ["private"], + }, + result: { + status: "finished", + rawOutput: ";; ANSWER SECTION:\nexample.com.\t\t86400\tIN\tA\t93.184.216.34", + answers: [ + { + name: "example.com.", + type: "A", + ttl: 86400, + class: "IN", + value: "93.184.216.34", + }, + ], + timings: { + total: 25, + }, + }, + }, + ], + }; +} diff --git a/test/backend-test/test-migration-v1-to-v3.ts b/test/backend-test/test-migration-v1-to-v3.ts new file mode 100644 index 0000000..7f3b5dd --- /dev/null +++ b/test/backend-test/test-migration-v1-to-v3.ts @@ -0,0 +1,182 @@ +import { test } from "node:test"; +import assert from "node:assert"; +import fs from "node:fs"; +import net from "node:net"; +import path from "node:path"; +import zlib from "node:zlib"; +import { spawn } from "node:child_process"; +import { fileURLToPath } from "node:url"; +import { io } from "socket.io-client"; +import BetterSqlite3 from "better-sqlite3"; +import bcrypt from "bcryptjs"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const fixturePath = path.join(__dirname, "../fixtures/v1-kuma.db.gz"); +const dataDir = path.join(__dirname, "../../data/test-migration-v1-upgrade"); + +const username = "admin"; + +// A password that Have I Been Pwned reports as breached, to cover the migration path from #7946. +const password = "Admin123!x"; + +/** + * Get a free TCP port from the OS. + * @returns {Promise} A free port + */ +function getFreePort() { + return new Promise((resolve, reject) => { + const server = net.createServer(); + server.on("error", reject); + server.listen(0, "127.0.0.1", () => { + const { port } = server.address(); + server.close(() => resolve(port)); + }); + }); +} + +/** + * Wait until the server answers HTTP requests. + * @param {string} url URL to poll + * @param {number} timeoutMs Maximum time to wait + * @returns {Promise} + */ +async function waitForServer(url, timeoutMs = 60000) { + const start = Date.now(); + while (Date.now() - start < timeoutMs) { + try { + if ((await fetch(url)).ok) { + return; + } + } catch (e) { + // Not up yet + } + await new Promise((resolve) => setTimeout(resolve, 500)); + } + throw new Error(`Server did not become ready within ${timeoutMs}ms`); +} + +/** + * Emit a socket.io event and await its acknowledgement. + * @param {import("socket.io-client").Socket} socket Connected socket + * @param {string} event Event name + * @param {any[]} args Event arguments + * @returns {Promise} Acknowledgement payload + */ +function emitAsync(socket, event, ...args) { + return new Promise((resolve) => socket.emit(event, ...args, resolve)); +} + +/** + * Assert a socket call succeeded. + * @param {import("socket.io-client").Socket} socket Connected socket + * @param {string} event Event name + * @param {any[]} args Event arguments + * @returns {Promise} The acknowledgement payload + */ +async function callOk(socket, event, ...args) { + const result = await emitAsync(socket, event, ...args); + assert.strictEqual(result.ok, true, `${event} failed: ${result.msg}`); + return result; +} + +test("A v1.23.17 database still works after upgrading to v3 (#7944, #7946)", { timeout: 180000 }, async () => { + assert.ok(fs.existsSync(fixturePath), `v1 fixture not found: ${fixturePath}`); + + // Start from a real 1.23.17 database (see extra/prepare-v1-test-db.mjs) and let the server + // migrate it, exactly like a user upgrading the image with their data dir. + fs.rmSync(dataDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 200 }); + fs.mkdirSync(dataDir, { recursive: true }); + fs.writeFileSync(path.join(dataDir, "kuma.db"), zlib.gunzipSync(fs.readFileSync(fixturePath))); + + // Seed the admin with a breached password, so migrating it must skip the leak check (#7946). + const seedDb = new BetterSqlite3(path.join(dataDir, "kuma.db")); + seedDb.prepare("UPDATE user SET password = ? WHERE username = ?").run(await bcrypt.hash(password, 10), username); + seedDb.close(); + + let child; + let socket; + + try { + const port = await getFreePort(); + child = spawn(process.execPath, ["--import=tsx", "server/server.js"], { + cwd: path.join(__dirname, "../.."), + env: { ...process.env, DATA_DIR: dataDir, UPTIME_KUMA_PORT: String(port), UPTIME_KUMA_DB_TYPE: "sqlite" }, + stdio: ["ignore", "pipe", "pipe"], + }); + let serverOutput = ""; + child.stdout.on("data", (data) => (serverOutput += data.toString())); + child.stderr.on("data", (data) => (serverOutput += data.toString())); + + const baseUrl = `http://127.0.0.1:${port}`; + await waitForServer(`${baseUrl}/api/entry-page`); + + // Log in. The legacy v1 user is migrated to better-auth on the first sign-in. + const login = await fetch(`${baseUrl}/api/auth/sign-in/username`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ username, password }), + }); + assert.strictEqual(login.status, 200, `login failed: ${login.status}\n${serverOutput}`); + const cookie = login.headers + .getSetCookie() + .map((value) => value.split(";")[0]) + .join("; "); + + socket = io(baseUrl, { extraHeaders: { cookie }, reconnection: false }); + await new Promise((resolve, reject) => { + socket.on("connect", resolve); + socket.on("connect_error", reject); + }); + + // Monitors: the operations that were broken in #7944 + const monitor = (await callOk(socket, "getMonitor", 1)).monitor; + monitor.name = "Renamed Monitor"; + await callOk(socket, "editMonitor", monitor); + await callOk(socket, "pauseMonitor", 1); + await callOk(socket, "resumeMonitor", 1); + + // Maintenance + const maintenance = (await callOk(socket, "getMaintenance", 1)).maintenance; + maintenance.title = "Renamed Maintenance"; + await callOk(socket, "editMaintenance", maintenance); + + // The other resources can be updated and deleted by the logged-in user. + await callOk( + socket, + "addProxy", + { protocol: "http", host: "127.0.0.1", port: 8081, auth: false, active: true, default: false }, + 1 + ); + await callOk( + socket, + "addDockerHost", + { dockerDaemon: "tcp://127.0.0.1:2375", dockerType: "socket", name: "Updated Docker" }, + 1 + ); + // v1 has no remote_browser table, so the v3 migration creates it and this adds the first row + await callOk(socket, "addRemoteBrowser", { name: "Updated Browser", url: "http://127.0.0.1:9222" }, null); + await callOk(socket, "addNotification", { name: "Updated Notif", isDefault: false, active: true }, 1); + + await callOk(socket, "deleteProxy", 1); + await callOk(socket, "deleteDockerHost", 1); + await callOk(socket, "deleteRemoteBrowser", 1); + await callOk(socket, "deleteNotification", 1); + await callOk(socket, "deleteAPIKey", 1); + await callOk(socket, "deleteMaintenance", 1); + await callOk(socket, "deleteMonitor", 1); + } finally { + if (socket) { + socket.close(); + } + if (child) { + child.kill(); + } + await new Promise((resolve) => setTimeout(resolve, 500)); + try { + fs.rmSync(dataDir, { recursive: true, force: true, maxRetries: 20, retryDelay: 250 }); + } catch (e) { + // Windows/Dropbox can keep a handle on the directory for a moment; the next run cleans it up + } + } +}); diff --git a/test/backend-test/test-migration-v2-to-v3.ts b/test/backend-test/test-migration-v2-to-v3.ts new file mode 100644 index 0000000..8a5306e --- /dev/null +++ b/test/backend-test/test-migration-v2-to-v3.ts @@ -0,0 +1,181 @@ +import { test } from "node:test"; +import assert from "node:assert"; +import fs from "node:fs"; +import net from "node:net"; +import path from "node:path"; +import zlib from "node:zlib"; +import { spawn } from "node:child_process"; +import { fileURLToPath } from "node:url"; +import { io } from "socket.io-client"; +import BetterSqlite3 from "better-sqlite3"; +import bcrypt from "bcryptjs"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const fixturePath = path.join(__dirname, "../fixtures/v2-kuma.db.gz"); +const dataDir = path.join(__dirname, "../../data/test-migration-v2-upgrade"); + +const username = "admin"; + +// A password that Have I Been Pwned reports as breached, to cover the migration path from #7946. +const password = "Admin123!x"; + +/** + * Get a free TCP port from the OS. + * @returns {Promise} A free port + */ +function getFreePort() { + return new Promise((resolve, reject) => { + const server = net.createServer(); + server.on("error", reject); + server.listen(0, "127.0.0.1", () => { + const { port } = server.address(); + server.close(() => resolve(port)); + }); + }); +} + +/** + * Wait until the server answers HTTP requests. + * @param {string} url URL to poll + * @param {number} timeoutMs Maximum time to wait + * @returns {Promise} + */ +async function waitForServer(url, timeoutMs = 60000) { + const start = Date.now(); + while (Date.now() - start < timeoutMs) { + try { + if ((await fetch(url)).ok) { + return; + } + } catch (e) { + // Not up yet + } + await new Promise((resolve) => setTimeout(resolve, 500)); + } + throw new Error(`Server did not become ready within ${timeoutMs}ms`); +} + +/** + * Emit a socket.io event and await its acknowledgement. + * @param {import("socket.io-client").Socket} socket Connected socket + * @param {string} event Event name + * @param {any[]} args Event arguments + * @returns {Promise} Acknowledgement payload + */ +function emitAsync(socket, event, ...args) { + return new Promise((resolve) => socket.emit(event, ...args, resolve)); +} + +/** + * Assert a socket call succeeded. + * @param {import("socket.io-client").Socket} socket Connected socket + * @param {string} event Event name + * @param {any[]} args Event arguments + * @returns {Promise} The acknowledgement payload + */ +async function callOk(socket, event, ...args) { + const result = await emitAsync(socket, event, ...args); + assert.strictEqual(result.ok, true, `${event} failed: ${result.msg}`); + return result; +} + +test("A v2.5.3 database still works after upgrading to v3 (#7944, #7946)", { timeout: 180000 }, async () => { + assert.ok(fs.existsSync(fixturePath), `v2 fixture not found: ${fixturePath}`); + + // Start from a real 2.5.3 database (see extra/prepare-v2-test-db.mjs) and let the server + // migrate it, exactly like a user upgrading the image with their data dir. + fs.rmSync(dataDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 200 }); + fs.mkdirSync(dataDir, { recursive: true }); + fs.writeFileSync(path.join(dataDir, "kuma.db"), zlib.gunzipSync(fs.readFileSync(fixturePath))); + + // Seed the admin with a breached password, so migrating it must skip the leak check (#7946). + const seedDb = new BetterSqlite3(path.join(dataDir, "kuma.db")); + seedDb.prepare("UPDATE user SET password = ? WHERE username = ?").run(await bcrypt.hash(password, 10), username); + seedDb.close(); + + let child; + let socket; + + try { + const port = await getFreePort(); + child = spawn(process.execPath, ["--import=tsx", "server/server.js"], { + cwd: path.join(__dirname, "../.."), + env: { ...process.env, DATA_DIR: dataDir, UPTIME_KUMA_PORT: String(port), UPTIME_KUMA_DB_TYPE: "sqlite" }, + stdio: ["ignore", "pipe", "pipe"], + }); + let serverOutput = ""; + child.stdout.on("data", (data) => (serverOutput += data.toString())); + child.stderr.on("data", (data) => (serverOutput += data.toString())); + + const baseUrl = `http://127.0.0.1:${port}`; + await waitForServer(`${baseUrl}/api/entry-page`); + + // Log in. The legacy v2 user is migrated to better-auth on the first sign-in. + const login = await fetch(`${baseUrl}/api/auth/sign-in/username`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ username, password }), + }); + assert.strictEqual(login.status, 200, `login failed: ${login.status}\n${serverOutput}`); + const cookie = login.headers + .getSetCookie() + .map((value) => value.split(";")[0]) + .join("; "); + + socket = io(baseUrl, { extraHeaders: { cookie }, reconnection: false }); + await new Promise((resolve, reject) => { + socket.on("connect", resolve); + socket.on("connect_error", reject); + }); + + // Monitors: the operations that were broken in #7944 + const monitor = (await callOk(socket, "getMonitor", 1)).monitor; + monitor.name = "Renamed Monitor"; + await callOk(socket, "editMonitor", monitor); + await callOk(socket, "pauseMonitor", 1); + await callOk(socket, "resumeMonitor", 1); + + // Maintenance + const maintenance = (await callOk(socket, "getMaintenance", 1)).maintenance; + maintenance.title = "Renamed Maintenance"; + await callOk(socket, "editMaintenance", maintenance); + + // The other resources can be updated and deleted by the logged-in user + await callOk( + socket, + "addProxy", + { protocol: "http", host: "127.0.0.1", port: 8081, auth: false, active: true, default: false }, + 1 + ); + await callOk( + socket, + "addDockerHost", + { dockerDaemon: "tcp://127.0.0.1:2375", dockerType: "socket", name: "Updated Docker" }, + 1 + ); + await callOk(socket, "addRemoteBrowser", { name: "Updated Browser", url: "http://127.0.0.1:9222" }, 1); + await callOk(socket, "addNotification", { name: "Updated Notif", isDefault: false, active: true }, 1); + + await callOk(socket, "deleteProxy", 1); + await callOk(socket, "deleteDockerHost", 1); + await callOk(socket, "deleteRemoteBrowser", 1); + await callOk(socket, "deleteNotification", 1); + await callOk(socket, "deleteAPIKey", 1); + await callOk(socket, "deleteMaintenance", 1); + await callOk(socket, "deleteMonitor", 1); + } finally { + if (socket) { + socket.close(); + } + if (child) { + child.kill(); + } + await new Promise((resolve) => setTimeout(resolve, 500)); + try { + fs.rmSync(dataDir, { recursive: true, force: true, maxRetries: 20, retryDelay: 250 }); + } catch (e) { + // Windows/Dropbox can keep a handle on the directory for a moment; the next run cleans it up + } + } +}); diff --git a/test/backend-test/test-migration.js b/test/backend-test/test-migration.js new file mode 100644 index 0000000..507c515 --- /dev/null +++ b/test/backend-test/test-migration.js @@ -0,0 +1,190 @@ +const { describe, test } = require("node:test"); +const fs = require("fs"); +const path = require("path"); +const { GenericContainer, Wait } = require("testcontainers"); +const { MySqlContainer } = require("@testcontainers/mysql"); + +describe("Database Migration", () => { + test("SQLite migrations run successfully from fresh database", async () => { + const testDbPath = path.join(__dirname, "../../data/test-migration.db"); + const testDbDir = path.dirname(testDbPath); + + // Ensure data directory exists + if (!fs.existsSync(testDbDir)) { + fs.mkdirSync(testDbDir, { recursive: true }); + } + + // Clean up any existing test database + if (fs.existsSync(testDbPath)) { + fs.unlinkSync(testDbPath); + } + + // Use the same SQLite driver as the project + const knex = require("knex"); + const db = knex({ + client: "better-sqlite3", + connection: { + filename: testDbPath, + }, + useNullAsDefault: true, + }); + + // Setup R (redbean) with knex instance like production code does + const { R } = require("redbean-node"); + R.setup(db); + + try { + // Use production code to initialize SQLite tables (like first run) + const { createTables } = require("../../db/knex_init_db.js"); + await createTables(); + + // Run all migrations like production code does + await R.knex.migrate.latest({ + directory: path.join(__dirname, "../../db/knex_migrations"), + }); + + // Test passes if migrations complete successfully without errors + } finally { + // Clean up + await R.knex.destroy(); + if (fs.existsSync(testDbPath)) { + fs.unlinkSync(testDbPath); + } + } + }); + + test( + "MariaDB migrations run successfully from fresh database", + { + skip: + (!!process.env.CI && (process.platform !== "linux" || process.arch !== "x64")) || + process.env.SKIP_TESTCONTAINER, + }, + async () => { + // Start MariaDB container (using MariaDB 12 to match current production) + const mariadbContainer = await new GenericContainer("mariadb:12") + .withEnvironment({ + MYSQL_ROOT_PASSWORD: "root", + MYSQL_DATABASE: "kuma_test", + MYSQL_USER: "kuma", + MYSQL_PASSWORD: "kuma", + }) + .withExposedPorts(3306) + .withWaitStrategy(Wait.forLogMessage("ready for connections", 2)) + .withStartupTimeout(120000) + .start(); + + // Wait a bit more to ensure MariaDB is fully ready + await new Promise((resolve) => setTimeout(resolve, 2000)); + + const knex = require("knex"); + const knexInstance = knex({ + client: "mysql2", + connection: { + host: mariadbContainer.getHost(), + port: mariadbContainer.getMappedPort(3306), + user: "kuma", + password: "kuma", + database: "kuma_test", + connectTimeout: 60000, + }, + pool: { + min: 0, + max: 10, + acquireTimeoutMillis: 60000, + idleTimeoutMillis: 60000, + }, + }); + + // Setup R (redbean) with knex instance like production code does + const { R } = require("redbean-node"); + R.setup(knexInstance); + + try { + // Use production code to initialize MariaDB tables + const { createTables } = require("../../db/knex_init_db.js"); + await createTables(); + + // Run all migrations like production code does + await R.knex.migrate.latest({ + directory: path.join(__dirname, "../../db/knex_migrations"), + }); + + // Test passes if migrations complete successfully without errors + } finally { + // Clean up + try { + await R.knex.destroy(); + } catch (e) { + // Ignore cleanup errors + } + try { + await mariadbContainer.stop(); + } catch (e) { + // Ignore cleanup errors + } + } + } + ); + + test( + "MySQL migrations run successfully from fresh database", + { + skip: + (!!process.env.CI && (process.platform !== "linux" || process.arch !== "x64")) || + process.env.SKIP_TESTCONTAINER, + }, + async () => { + // Start MySQL 8.0 container (the version mentioned in the issue) + const mysqlContainer = await new MySqlContainer("mysql:8.0").withStartupTimeout(120000).start(); + + const knex = require("knex"); + const knexInstance = knex({ + client: "mysql2", + connection: { + host: mysqlContainer.getHost(), + port: mysqlContainer.getPort(), + user: mysqlContainer.getUsername(), + password: mysqlContainer.getUserPassword(), + database: mysqlContainer.getDatabase(), + connectTimeout: 60000, + }, + pool: { + min: 0, + max: 10, + acquireTimeoutMillis: 60000, + idleTimeoutMillis: 60000, + }, + }); + + // Setup R (redbean) with knex instance like production code does + const { R } = require("redbean-node"); + R.setup(knexInstance); + + try { + // Use production code to initialize MySQL tables + const { createTables } = require("../../db/knex_init_db.js"); + await createTables(); + + // Run all migrations like production code does + await R.knex.migrate.latest({ + directory: path.join(__dirname, "../../db/knex_migrations"), + }); + + // Test passes if migrations complete successfully without errors + } finally { + // Clean up + try { + await R.knex.destroy(); + } catch (e) { + // Ignore cleanup errors + } + try { + await mysqlContainer.stop(); + } catch (e) { + // Ignore cleanup errors + } + } + } + ); +}); diff --git a/test/backend-test/test-monitor-response.js b/test/backend-test/test-monitor-response.js new file mode 100644 index 0000000..ff3d63d --- /dev/null +++ b/test/backend-test/test-monitor-response.js @@ -0,0 +1,36 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const Monitor = require("../../server/model/monitor"); +const Heartbeat = require("../../server/model/heartbeat"); +const { RESPONSE_BODY_LENGTH_DEFAULT } = require("../../src/util"); + +describe("Monitor response saving", () => { + test("getSaveResponse and getSaveErrorResponse parse booleans", () => { + const monitor = Object.create(Monitor.prototype); + monitor.save_response = 1; + monitor.save_error_response = 0; + + assert.strictEqual(monitor.getSaveResponse(), true); + assert.strictEqual(monitor.getSaveErrorResponse(), false); + }); + + test("saveResponseData stores and truncates response", async () => { + const monitor = Object.create(Monitor.prototype); + monitor.response_max_length = 5; + + const bean = {}; + await monitor.saveResponseData(bean, "abcdef"); + + assert.strictEqual(await Heartbeat.decodeResponseValue(bean.response), "abcde... (truncated)"); + }); + + test("saveResponseData stringifies objects", async () => { + const monitor = Object.create(Monitor.prototype); + monitor.response_max_length = RESPONSE_BODY_LENGTH_DEFAULT; + + const bean = {}; + await monitor.saveResponseData(bean, { ok: true }); + + assert.strictEqual(await Heartbeat.decodeResponseValue(bean.response), JSON.stringify({ ok: true })); + }); +}); diff --git a/test/backend-test/test-ntp.js b/test/backend-test/test-ntp.js new file mode 100644 index 0000000..0b02746 --- /dev/null +++ b/test/backend-test/test-ntp.js @@ -0,0 +1,263 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert/strict"); +const { NTPMonitorType } = require("../../server/monitor-types/ntp"); +const { UP } = require("../../src/util"); + +describe("NTPMonitorType", () => { + const ntp = new NTPMonitorType(); + + test("createNTPPacket() returns a 48-byte buffer with correct header", () => { + const packet = ntp.createNTPPacket(); + assert.strictEqual(packet.length, 48); + // LI=0, VN=3, Mode=3 => 0x1B + assert.strictEqual(packet[0], 0x1b); + // Rest should be zeros + for (let i = 1; i < 48; i++) { + assert.strictEqual(packet[i], 0, `byte ${i} should be zero`); + } + }); + + test("readNTPTimestamp() correctly converts NTP timestamp to milliseconds", () => { + const buf = Buffer.alloc(8); + // 1 second since NTP epoch + buf.writeUInt32BE(1, 0); + buf.writeUInt32BE(0, 4); + assert.strictEqual(ntp.readNTPTimestamp(buf, 0), 1000); + + // 0.5 seconds fractional + buf.writeUInt32BE(0, 0); + buf.writeUInt32BE(0x80000000, 4); + assert.strictEqual(ntp.readNTPTimestamp(buf, 0), 500); + }); + + test("parseNTPResponse() extracts correct fields from a valid packet", () => { + // Construct a minimal valid NTP response packet + const msg = Buffer.alloc(48); + + // Byte 0: LI=0, VN=3, Mode=4 (server) => 00 011 100 = 0x1C + msg[0] = 0x1c; + // Stratum 2 + msg[1] = 2; + + // Root dispersion at offset 8: 0.5 seconds = 0x00008000 + msg.writeUInt32BE(0x00008000, 8); + + // Reference ID at offset 12: "GPS\0" for stratum 1 test not applicable here + // For stratum 2, it's an IPv4 address + msg[12] = 192; + msg[13] = 168; + msg[14] = 1; + msg[15] = 1; + + // Server receive timestamp (T2) at offset 32: 3912710400 seconds (approx 2024-01-01) + msg.writeUInt32BE(3912710400, 32); + msg.writeUInt32BE(0, 36); + + // Server transmit timestamp (T3) at offset 40: same + 1ms + msg.writeUInt32BE(3912710400, 40); + msg.writeUInt32BE(4294967, 44); // ~1ms in fractional seconds + + const t1 = 3912710400 * 1000; // Client originate in ms since NTP epoch + const t4 = 3912710400 * 1000 + 50; // Client receive 50ms later + + const result = ntp.parseNTPResponse(msg, t1, t4); + + assert.strictEqual(result.stratum, 2); + assert.strictEqual(result.leapIndicator, 0); + assert.strictEqual(result.refid, "192.168.1.1"); + // Root dispersion: 0x8000 / 65536 * 1000 = 500ms + assert.ok( + Math.abs(result.rootDispersion - 500) < 0.1, + `rootDispersion should be ~500ms, got ${result.rootDispersion}` + ); + assert.strictEqual(typeof result.offset, "number"); + assert.strictEqual(typeof result.roundTripDelay, "number"); + }); + + test("parseNTPResponse() parses ASCII refid for stratum 1", () => { + const msg = Buffer.alloc(48); + msg[0] = 0x1c; + msg[1] = 1; // Stratum 1 + msg.write("GPS\0", 12, "ascii"); + // Set timestamps to avoid NaN + msg.writeUInt32BE(3912710400, 32); + msg.writeUInt32BE(0, 36); + msg.writeUInt32BE(3912710400, 40); + msg.writeUInt32BE(0, 44); + + const t1 = 3912710400 * 1000; + const t4 = t1 + 10; + + const result = ntp.parseNTPResponse(msg, t1, t4); + assert.strictEqual(result.stratum, 1); + assert.strictEqual(result.refid, "GPS"); + }); + + test("parseNTPResponse() rejects packets shorter than 48 bytes", () => { + const short = Buffer.alloc(20); + assert.throws(() => ntp.parseNTPResponse(short, 0, 0), /expected 48\+ bytes/); + }); + + test("check() throws for stratum 16 (unsynchronized)", async () => { + const monitor = { + hostname: "localhost", + port: 123, + timeout: 5, + ntp_stratum_threshold: 5, + ntp_time_offset_threshold: 1000, + ntp_root_dispersion_threshold: 500, + }; + const heartbeat = {}; + + // Stub queryNTP to return stratum 16 + const originalQuery = ntp.queryNTP; + ntp.queryNTP = async () => ({ + stratum: 16, + offset: 0, + rootDispersion: 10, + refid: "INIT", + roundTripDelay: 5, + leapIndicator: 3, + }); + + try { + await assert.rejects(() => ntp.check(monitor, heartbeat, null), /unsynchronized.*stratum 16/); + } finally { + ntp.queryNTP = originalQuery; + } + }); + + test("check() throws when stratum exceeds threshold", async () => { + const monitor = { + hostname: "localhost", + port: 123, + timeout: 5, + ntp_stratum_threshold: 2, + ntp_time_offset_threshold: 1000, + ntp_root_dispersion_threshold: 500, + }; + const heartbeat = {}; + + const originalQuery = ntp.queryNTP; + ntp.queryNTP = async () => ({ + stratum: 3, + offset: 0.5, + rootDispersion: 10, + refid: "GPS", + roundTripDelay: 5, + leapIndicator: 0, + }); + + try { + await assert.rejects(() => ntp.check(monitor, heartbeat, null), /Stratum 3 meets or exceeds threshold 2/); + } finally { + ntp.queryNTP = originalQuery; + } + }); + + test("check() throws when offset exceeds threshold", async () => { + const monitor = { + hostname: "localhost", + port: 123, + timeout: 5, + ntp_stratum_threshold: 5, + ntp_time_offset_threshold: 100, + ntp_root_dispersion_threshold: 500, + }; + const heartbeat = {}; + + const originalQuery = ntp.queryNTP; + ntp.queryNTP = async () => ({ + stratum: 2, + offset: -150.5, + rootDispersion: 10, + refid: "GPS", + roundTripDelay: 5, + leapIndicator: 0, + }); + + try { + await assert.rejects(() => ntp.check(monitor, heartbeat, null), /Time offset.*exceeds threshold 100ms/); + } finally { + ntp.queryNTP = originalQuery; + } + }); + + test("check() throws when dispersion exceeds threshold", async () => { + const monitor = { + hostname: "localhost", + port: 123, + timeout: 5, + ntp_stratum_threshold: 5, + ntp_time_offset_threshold: 1000, + ntp_root_dispersion_threshold: 50, + }; + const heartbeat = {}; + + const originalQuery = ntp.queryNTP; + ntp.queryNTP = async () => ({ + stratum: 2, + offset: 1.5, + rootDispersion: 100, + refid: "GPS", + roundTripDelay: 5, + leapIndicator: 0, + }); + + try { + await assert.rejects(() => ntp.check(monitor, heartbeat, null), /Root dispersion.*exceeds threshold 50ms/); + } finally { + ntp.queryNTP = originalQuery; + } + }); + + test("check() sets heartbeat UP when all thresholds pass", async () => { + const monitor = { + hostname: "localhost", + port: 123, + timeout: 5, + ntp_stratum_threshold: 5, + ntp_time_offset_threshold: 1000, + ntp_root_dispersion_threshold: 500, + }; + const heartbeat = {}; + + const originalQuery = ntp.queryNTP; + ntp.queryNTP = async () => ({ + stratum: 2, + offset: 1.5, + rootDispersion: 10.2, + refid: "GPS", + roundTripDelay: 5.3, + leapIndicator: 0, + }); + + try { + await ntp.check(monitor, heartbeat, null); + assert.strictEqual(heartbeat.status, UP); + assert.match(heartbeat.msg, /Stratum: 2/); + assert.match(heartbeat.msg, /RefID: GPS/); + assert.match(heartbeat.msg, /Offset: 1\.500ms/); + assert.match(heartbeat.msg, /Dispersion: 10\.200ms/); + assert.strictEqual(typeof heartbeat.ping, "number"); + } finally { + ntp.queryNTP = originalQuery; + } + }); + + test( + "queryNTP() can reach a public NTP server", + { + skip: !!process.env.CI, + }, + async () => { + const result = await ntp.queryNTP("time.google.com", 123, 10000); + assert.strictEqual(typeof result.stratum, "number"); + assert.ok(result.stratum >= 1 && result.stratum <= 15, `stratum should be 1-15, got ${result.stratum}`); + assert.strictEqual(typeof result.offset, "number"); + assert.strictEqual(typeof result.roundTripDelay, "number"); + assert.strictEqual(typeof result.rootDispersion, "number"); + assert.strictEqual(typeof result.refid, "string"); + } + ); +}); diff --git a/test/backend-test/test-ping-chart.js b/test/backend-test/test-ping-chart.js new file mode 100644 index 0000000..55c781f --- /dev/null +++ b/test/backend-test/test-ping-chart.js @@ -0,0 +1,50 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); + +/** + * Extracts the ping value filtering logic from PingChart.vue pushDatapoint(). + * This mirrors the condition: datapoint.up > 0 && datapoint.avgPing != null + * @param {object} datapoint Datapoint with up, avgPing, minPing, maxPing + * @returns {number|null} The avgPing value or null if filtered out + */ +function filterPingValue(datapoint) { + return datapoint.up > 0 && datapoint.avgPing != null ? datapoint.avgPing : null; +} + +describe("PingChart pushDatapoint filtering", () => { + test("avgPing of 0 should be rendered, not filtered out (#7143)", () => { + const datapoint = { up: 1, down: 0, avgPing: 0, minPing: 0, maxPing: 0 }; + const result = filterPingValue(datapoint); + assert.strictEqual(result, 0, "avgPing of 0 must not be converted to null"); + }); + + test("avgPing of 1 should be rendered", () => { + const datapoint = { up: 1, down: 0, avgPing: 1, minPing: 1, maxPing: 1 }; + const result = filterPingValue(datapoint); + assert.strictEqual(result, 1); + }); + + test("avgPing of null should be filtered out", () => { + const datapoint = { up: 1, down: 0, avgPing: null, minPing: null, maxPing: null }; + const result = filterPingValue(datapoint); + assert.strictEqual(result, null); + }); + + test("avgPing of undefined should be filtered out", () => { + const datapoint = { up: 1, down: 0, avgPing: undefined, minPing: undefined, maxPing: undefined }; + const result = filterPingValue(datapoint); + assert.strictEqual(result, null); + }); + + test("datapoint with no up counts should be filtered out", () => { + const datapoint = { up: 0, down: 1, avgPing: 5, minPing: 5, maxPing: 5 }; + const result = filterPingValue(datapoint); + assert.strictEqual(result, null); + }); + + test("normal ping value with up count should be rendered", () => { + const datapoint = { up: 3, down: 0, avgPing: 42, minPing: 30, maxPing: 55 }; + const result = filterPingValue(datapoint); + assert.strictEqual(result, 42); + }); +}); diff --git a/test/backend-test/test-pm2.js b/test/backend-test/test-pm2.js new file mode 100644 index 0000000..727841e --- /dev/null +++ b/test/backend-test/test-pm2.js @@ -0,0 +1,123 @@ +const { describe, test, beforeEach, afterEach } = require("node:test"); +const assert = require("node:assert"); +const childProcess = require("child_process"); +const { DOWN, UP } = require("../../src/util"); + +/** + * Load the PM2 monitor after swapping child_process.execFile. + * @param {typeof childProcess.execFile} execFileStub Stub implementation. + * @returns {import("../../server/monitor-types/pm2").PM2MonitorType} A PM2 monitor instance wired to the stub. + */ +function createMonitorType(execFileStub) { + childProcess.execFile = execFileStub; + delete require.cache[require.resolve("../../server/util/pm2")]; + delete require.cache[require.resolve("../../server/monitor-types/pm2")]; + const { PM2MonitorType } = require("../../server/monitor-types/pm2"); + return new PM2MonitorType(); +} + +describe("PM2MonitorType", () => { + let monitorType; + let heartbeat; + let originalExecFile; + + beforeEach(() => { + monitorType = null; + heartbeat = { + status: DOWN, + msg: "", + }; + originalExecFile = childProcess.execFile; + }); + + afterEach(() => { + childProcess.execFile = originalExecFile; + }); + + test("check() returns UP for an online PM2 process", async () => { + monitorType = createMonitorType((command, args, options, callback) => { + callback( + null, + JSON.stringify([ + { + pm_id: 0, + name: "api", + pm2_env: { + status: "online", + }, + }, + ]), + "" + ); + }); + + await monitorType.check( + { + system_service_name: "api", + }, + heartbeat + ); + + assert.strictEqual(heartbeat.status, UP); + assert.ok(heartbeat.msg.includes("online")); + }); + + test("check() matches PM2 process by name when pm_id changes", async () => { + monitorType = createMonitorType((command, args, options, callback) => { + callback( + null, + JSON.stringify([ + { + // PM2 may assign a different id when a process is deleted and recreated. + pm_id: 7, + name: "api", + pm2_env: { + status: "online", + }, + }, + ]), + "" + ); + }); + + await monitorType.check( + { + system_service_name: "api", + }, + heartbeat + ); + + assert.strictEqual(heartbeat.status, UP); + assert.ok(heartbeat.msg.includes("online")); + }); + + test("check() returns DOWN for a stopped PM2 process", async () => { + monitorType = createMonitorType((command, args, options, callback) => { + callback( + null, + JSON.stringify([ + { + pm_id: 0, + name: "api", + pm2_env: { + status: "stopped", + }, + }, + ]), + "" + ); + }); + + await assert.rejects( + monitorType.check( + { + system_service_name: "api", + }, + heartbeat + ), + /stopped/ + ); + + assert.strictEqual(heartbeat.status, DOWN); + }); +}); diff --git a/test/backend-test/test-radius.js b/test/backend-test/test-radius.js new file mode 100644 index 0000000..9d8eb85 --- /dev/null +++ b/test/backend-test/test-radius.js @@ -0,0 +1,76 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert/strict"); +const dgram = require("dgram"); +const radius = require("radius"); +const { radius: radiusCheck } = require("../../server/util-server"); + +/** + * Start a UDP RADIUS server that replies to every Access-Request with the given response code. + * @param {string} secret RADIUS shared secret + * @param {string} responseCode RADIUS response code to send back + * @returns {Promise<{socket: import("dgram").Socket, port: number, address: string}>} The bound UDP socket and its address + */ +function startRadiusServer(secret, responseCode) { + const socket = dgram.createSocket("udp4"); + + socket.on("message", (msg, rinfo) => { + try { + const request = radius.decode({ packet: msg, secret }); + const response = radius.encode_response({ + packet: request, + code: responseCode, + secret, + }); + socket.send(response, 0, response.length, rinfo.port, rinfo.address, () => {}); + } catch (error) { + // Ignore decode errors, the client will time out + } + }); + + return new Promise((resolve) => { + socket.on("listening", () => { + const { address, port } = socket.address(); + resolve({ socket, port, address }); + }); + socket.bind(0, "127.0.0.1"); + }); +} + +describe("RadiusMonitorType", () => { + const secret = "testing123"; + + test("radius() resolves with code Access-Challenge when server responds with Access-Challenge", async () => { + const { socket, port } = await startRadiusServer(secret, "Access-Challenge"); + + try { + const resp = await radiusCheck("127.0.0.1", "bob", "testpw", "called", "calling", secret, port, 2000); + assert.strictEqual(resp.code, "Access-Challenge"); + } finally { + socket.close(); + } + }); + + test("radius() rejects when server responds with Access-Reject", async () => { + const { socket, port } = await startRadiusServer(secret, "Access-Reject"); + + try { + await assert.rejects( + radiusCheck("127.0.0.1", "bob", "testpw", "called", "calling", secret, port, 2000), + /RADIUS Access-Reject from 127\.0\.0\.1:/ + ); + } finally { + socket.close(); + } + }); + + test("radius() resolves with code Access-Accept when server responds with Access-Accept", async () => { + const { socket, port } = await startRadiusServer(secret, "Access-Accept"); + + try { + const resp = await radiusCheck("127.0.0.1", "bob", "testpw", "called", "calling", secret, port, 2000); + assert.strictEqual(resp.code, "Access-Accept"); + } finally { + socket.close(); + } + }); +}); diff --git a/test/backend-test/test-snmp.js b/test/backend-test/test-snmp.js new file mode 100644 index 0000000..e0f2a2a --- /dev/null +++ b/test/backend-test/test-snmp.js @@ -0,0 +1,132 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert/strict"); +const { GenericContainer } = require("testcontainers"); +const { SNMPMonitorType } = require("../../server/monitor-types/snmp"); +const { UP } = require("../../src/util"); +const snmp = require("net-snmp"); + +describe("SNMPMonitorType", () => { + test( + "check() sets heartbeat to UP when SNMP agent responds", + { + skip: + (!!process.env.CI && (process.platform !== "linux" || process.arch !== "x64")) || + process.env.SKIP_TESTCONTAINER, + }, + async () => { + const container = await new GenericContainer("polinux/snmpd").withExposedPorts("161/udp").start(); + + try { + // Get the mapped UDP port + const hostPort = container.getMappedPort("161/udp"); + const hostIp = container.getHost(); + + // UDP service small wait to ensure snmpd is ready inside container + await new Promise((r) => setTimeout(r, 2000)); + + const monitor = { + type: "snmp", + hostname: hostIp, + port: hostPort, + snmpVersion: "2c", + radiusPassword: "public", + snmpOid: "1.3.6.1.2.1.1.1.0", + timeout: 5, + maxretries: 1, + jsonPath: "$", + jsonPathOperator: "!=", + expectedValue: "", + }; + + const snmpMonitor = new SNMPMonitorType(); + const heartbeat = {}; + + await snmpMonitor.check(monitor, heartbeat); + + assert.strictEqual(heartbeat.status, UP); + assert.match(heartbeat.msg, /JSON query passes/); + } finally { + await container.stop(); + } + } + ); + + test( + "check() throws when SNMP agent does not respond", + { + skip: + (!!process.env.CI && (process.platform !== "linux" || process.arch !== "x64")) || + process.env.SKIP_TESTCONTAINER, + }, + async () => { + const monitor = { + type: "snmp", + hostname: "127.0.0.1", + port: 65530, // Assuming no SNMP agent is running here + snmpVersion: "2c", + radiusPassword: "public", + snmpOid: "1.3.6.1.2.1.1.1.0", + timeout: 1, + maxretries: 1, + }; + + const snmpMonitor = new SNMPMonitorType(); + const heartbeat = {}; + + await assert.rejects(() => snmpMonitor.check(monitor, heartbeat), /timeout|RequestTimedOutError/i); + } + ); + + test("check() uses SNMPv3 noAuthNoPriv session when version is 3", async () => { + const originalCreateV3Session = snmp.createV3Session; + const originalCreateSession = snmp.createSession; + + let createV3Called = false; + let createSessionCalled = false; + let receivedOptions = null; + + // Stub createV3Session + snmp.createV3Session = function (_host, _username, options) { + createV3Called = true; + receivedOptions = options; + + return { + on: () => {}, + close: () => {}, + // Stop execution after session creation to avoid real network I/O. + get: (_oids, cb) => cb(new Error("stop test here")), + }; + }; + + // Stub createSession + snmp.createSession = function () { + createSessionCalled = true; + return {}; + }; + + const monitor = { + type: "snmp", + hostname: "127.0.0.1", + port: 161, + timeout: 5, + maxretries: 1, + snmpVersion: "3", + snmp_v3_username: "testuser", + snmpOid: "1.3.6.1.2.1.1.1.0", + }; + + const snmpMonitor = new SNMPMonitorType(); + const heartbeat = {}; + + await assert.rejects(() => snmpMonitor.check(monitor, heartbeat), /stop test here/); + + // Assertions + assert.strictEqual(createV3Called, true); + assert.strictEqual(createSessionCalled, false); + assert.strictEqual(receivedOptions.securityLevel, snmp.SecurityLevel.noAuthNoPriv); + + // Restore originals + snmp.createV3Session = originalCreateV3Session; + snmp.createSession = originalCreateSession; + }); +}); diff --git a/test/backend-test/test-status-page.js b/test/backend-test/test-status-page.js new file mode 100644 index 0000000..3a30605 --- /dev/null +++ b/test/backend-test/test-status-page.js @@ -0,0 +1,88 @@ +const { describe, test, mock } = require("node:test"); +const assert = require("node:assert"); +const StatusPage = require("../../server/model/status_page"); +const dayjs = require("dayjs"); +const utc = require("dayjs/plugin/utc"); +const { + STATUS_PAGE_ALL_UP, + STATUS_PAGE_ALL_DOWN, + STATUS_PAGE_PARTIAL_DOWN, + STATUS_PAGE_MAINTENANCE, +} = require("../../src/util"); + +dayjs.extend(utc); + +describe("StatusPage", () => { + describe("getStatusDescription()", () => { + test("returns 'No Services' when status is -1", () => { + const description = StatusPage.getStatusDescription(-1); + assert.strictEqual(description, "No Services"); + }); + + test("returns 'All Systems Operational' when all services are up", () => { + const description = StatusPage.getStatusDescription(STATUS_PAGE_ALL_UP); + assert.strictEqual(description, "All Systems Operational"); + }); + + test("returns 'Partially Degraded Service' when some services are down", () => { + const description = StatusPage.getStatusDescription(STATUS_PAGE_PARTIAL_DOWN); + assert.strictEqual(description, "Partially Degraded Service"); + }); + + test("returns 'Degraded Service' when all services are down", () => { + const description = StatusPage.getStatusDescription(STATUS_PAGE_ALL_DOWN); + assert.strictEqual(description, "Degraded Service"); + }); + + test("returns 'Under maintenance' when status page is in maintenance", () => { + const description = StatusPage.getStatusDescription(STATUS_PAGE_MAINTENANCE); + assert.strictEqual(description, "Under maintenance"); + }); + + test("returns '?' for unknown status values", () => { + const description = StatusPage.getStatusDescription(999); + assert.strictEqual(description, "?"); + }); + }); + + describe("renderRSS()", () => { + const MOCK_FEED_URL = "http://localhost:3001/status/test"; + + test("pubDate uses UTC timezone for heartbeat.time without timezone info", async () => { + const mockStatusPage = { + title: "Test Status Page", + }; + + const mockIncidents = [ + { + title: "Test Monitor", + content: "Test content", + id: 1, + createdDate: "2026-05-21 15:07:35.600", + }, + ]; + + const mockHeartbeats = [ + { + name: "Test Monitor", + monitorID: 1, + time: "2026-01-24 13:16:25.400", + }, + ]; + + mock.method(StatusPage, "getRSSPageData", async () => ({ + incidents: mockIncidents, + heartbeats: mockHeartbeats, + statusDescription: "All Systems Operational", + })); + + try { + const rss = await StatusPage.renderRSS(mockStatusPage, MOCK_FEED_URL); + + assert.ok(rss.includes("Sat, 24 Jan 2026 13:16:25 GMT")); + } finally { + mock.restoreAll(); + } + }); + }); +}); diff --git a/test/backend-test/test-system-service.js b/test/backend-test/test-system-service.js new file mode 100644 index 0000000..f6e059a --- /dev/null +++ b/test/backend-test/test-system-service.js @@ -0,0 +1,115 @@ +const { describe, test, beforeEach, afterEach } = require("node:test"); +const assert = require("node:assert"); +const { SystemServiceMonitorType } = require("../../server/monitor-types/system-service"); +const { DOWN, UP } = require("../../src/util"); +const process = require("process"); +const { execSync } = require("node:child_process"); + +/** + * Check if the test should be skipped. + * @returns {boolean} True if the test should be skipped + */ +function shouldSkip() { + if (process.platform === "win32") { + return false; + } + if (process.platform !== "linux") { + return true; + } + + // We currently only support systemd as an init system on linux + // -> Check if PID 1 is systemd (or init which maps to systemd) + try { + const pid1Comm = execSync("ps -p 1 -o comm=", { encoding: "utf-8" }).trim(); + return !["systemd", "init"].includes(pid1Comm); + } catch (e) { + return true; + } +} + +describe("SystemServiceMonitorType", { skip: shouldSkip() }, () => { + let monitorType; + let heartbeat; + let originalPlatform; + + beforeEach(() => { + monitorType = new SystemServiceMonitorType(); + heartbeat = { + status: DOWN, + msg: "", + }; + originalPlatform = Object.getOwnPropertyDescriptor(process, "platform"); + }); + + afterEach(() => { + if (originalPlatform) { + Object.defineProperty(process, "platform", originalPlatform); + } + }); + + test( + "check() returns UP for a running service", + { + // Disabled on Windows, because it looks like (not sure) Powershell takes too long to cold start on GitHub CI, for unknown reason + // TODO: Feel free to investigate if you want to + skip: process.platform === "win32", + }, + async () => { + // Windows: 'EventLog' is always running. + // Linux: 'dbus' or 'cron' are standard services. + const serviceName = process.platform === "win32" ? "EventLog" : "dbus"; + + const monitor = { + system_service_name: serviceName, + }; + + await monitorType.check(monitor, heartbeat); + + assert.strictEqual(heartbeat.status, UP); + assert.ok(heartbeat.msg.includes("is running")); + } + ); + + test("check() returns DOWN for a stopped service", async () => { + const monitor = { + system_service_name: "non-existent-service-12345", + }; + + // Query a non-existent service to force an error/down state. + // We pass the promise directly to assert.rejects, avoiding unnecessary async wrappers. + await assert.rejects(monitorType.check(monitor, heartbeat)); + + assert.strictEqual(heartbeat.status, DOWN); + }); + + test("check() fails gracefully with invalid characters", async () => { + // Mock platform for validation logic test + Object.defineProperty(process, "platform", { + value: "linux", + configurable: true, + }); + + const monitor = { + system_service_name: "invalid&service;name", + }; + + // Expected validation error + await assert.rejects(monitorType.check(monitor, heartbeat)); + + assert.strictEqual(heartbeat.status, DOWN); + }); + + test("check() throws on unsupported platforms", async () => { + // This test mocks the platform, so it can run anywhere. + Object.defineProperty(process, "platform", { + value: "darwin", + configurable: true, + }); + + const monitor = { + system_service_name: "test-service", + }; + + await assert.rejects(monitorType.check(monitor, heartbeat), /not supported/); + }); +}); diff --git a/test/backend-test/test-uptime-calculator.js b/test/backend-test/test-uptime-calculator.js new file mode 100644 index 0000000..0826fba --- /dev/null +++ b/test/backend-test/test-uptime-calculator.js @@ -0,0 +1,500 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { UptimeCalculator } = require("../../server/uptime-calculator"); +const dayjs = require("dayjs"); +const { UP, DOWN, PENDING, MAINTENANCE } = require("../../src/util"); +dayjs.extend(require("dayjs/plugin/utc")); +dayjs.extend(require("../../server/modules/dayjs/plugin/timezone")); +dayjs.extend(require("dayjs/plugin/customParseFormat")); + +describe("Uptime Calculator", () => { + test("getCurrentDate() returns custom date when set", () => { + let c1 = new UptimeCalculator(); + + // Test custom date + UptimeCalculator.currentDate = dayjs.utc("2021-01-01T00:00:00.000Z"); + assert.strictEqual(c1.getCurrentDate().unix(), dayjs.utc("2021-01-01T00:00:00.000Z").unix()); + }); + + test("update() with UP status returns correct timestamp", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + let c2 = new UptimeCalculator(); + let date = await c2.update(UP); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:46:59").unix()); + }); + + test("update() with MAINTENANCE status returns correct timestamp", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); + let c2 = new UptimeCalculator(); + let date = await c2.update(MAINTENANCE); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); + }); + + test("update() with DOWN status returns correct timestamp", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); + let c2 = new UptimeCalculator(); + let date = await c2.update(DOWN); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); + }); + + test("update() with PENDING status returns correct timestamp", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:47:20"); + let c2 = new UptimeCalculator(); + let date = await c2.update(PENDING); + assert.strictEqual(date.unix(), dayjs.utc("2023-08-12 20:47:20").unix()); + }); + + test("flatStatus() converts statuses correctly", () => { + let c2 = new UptimeCalculator(); + assert.strictEqual(c2.flatStatus(UP), UP); + //assert.strictEqual(c2.flatStatus(MAINTENANCE), UP); + assert.strictEqual(c2.flatStatus(DOWN), DOWN); + assert.strictEqual(c2.flatStatus(PENDING), DOWN); + }); + + test("getMinutelyKey() returns correct timestamp for start of minute", () => { + let c2 = new UptimeCalculator(); + let divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:00")); + assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); + + // Edge case 1 + c2 = new UptimeCalculator(); + divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:01")); + assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); + + // Edge case 2 + c2 = new UptimeCalculator(); + divisionKey = c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:59")); + assert.strictEqual(divisionKey, dayjs.utc("2023-08-12 20:46:00").unix()); + }); + + test("missing cleanup buckets are not created when createIfMissing is false", () => { + let c2 = new UptimeCalculator(); + + c2.getMinutelyKey(dayjs.utc("2023-08-12 20:46:59")); + c2.getHourlyKey(dayjs.utc("2023-08-12 20:46:59")); + c2.getDailyKey(dayjs.utc("2023-08-12 20:46:59")); + + let minutelyCleanupKey = c2.getMinutelyKey(dayjs.utc("2023-08-11 20:46:59"), false); + let hourlyCleanupKey = c2.getHourlyKey(dayjs.utc("2023-07-13 20:46:59"), false); + let dailyCleanupKey = c2.getDailyKey(dayjs.utc("2022-08-12 20:46:59"), false); + + assert.strictEqual(c2.minutelyUptimeDataList.length(), 1); + assert.strictEqual(c2.hourlyUptimeDataList.length(), 1); + assert.strictEqual(c2.dailyUptimeDataList.length(), 1); + assert.strictEqual(c2.minutelyUptimeDataList[minutelyCleanupKey], undefined); + assert.strictEqual(c2.hourlyUptimeDataList[hourlyCleanupKey], undefined); + assert.strictEqual(c2.dailyUptimeDataList[dailyCleanupKey], undefined); + }); + + test("cleanup lookup should not create missing minutely/hourly buckets", () => { + let startDate = dayjs.utc("2023-08-12 00:00:00"); + + // First test the broken version that creates missing buckets during cleanup lookup. + let broken = new UptimeCalculator(); + let minutelyQueueLimit = broken.minutelyUptimeDataList.__limit; + let hourlyQueueLimit = broken.hourlyUptimeDataList.__limit; + let totalTicks = Math.max(minutelyQueueLimit, hourlyQueueLimit); + + let minutelyEndDate = startDate; + let hourlyEndDate = startDate; + for (let tick = 0; tick < totalTicks; tick++) { + minutelyEndDate = startDate.add(tick, "minute"); + hourlyEndDate = startDate.add(tick, "hour"); + + // Simulate normal key lookup that creates buckets. + broken.getMinutelyKey(minutelyEndDate); + broken.getHourlyKey(hourlyEndDate); + + // Simulate pre-fix cleanup key lookup that accidentally creates missing buckets. + broken.getMinutelyKey(minutelyEndDate.subtract(broken.statMinutelyKeepHour, "hour")); + broken.getHourlyKey(hourlyEndDate.subtract(broken.statHourlyKeepDay, "day")); + } + + UptimeCalculator.currentDate = minutelyEndDate; + assert.strictEqual(broken.getDataArray(minutelyQueueLimit, "minute").length, minutelyQueueLimit / 2); + + UptimeCalculator.currentDate = hourlyEndDate; + assert.strictEqual(broken.getDataArray(hourlyQueueLimit, "hour").length, hourlyQueueLimit / 2); + + // Now test the fixed version that should not create missing buckets. + let fixed = new UptimeCalculator(); + let fixedMinutelyTickDate = startDate; + let fixedHourlyTickDate = startDate; + for (let tick = 0; tick < totalTicks; tick++) { + fixedMinutelyTickDate = startDate.add(tick, "minute"); + fixedHourlyTickDate = startDate.add(tick, "hour"); + + // Simulate normal key lookup that creates buckets. + fixed.getMinutelyKey(fixedMinutelyTickDate); + fixed.getHourlyKey(fixedHourlyTickDate); + + // Simulate pre-fix cleanup key lookup that should not create missing buckets. + fixed.getMinutelyKey(fixedMinutelyTickDate.subtract(fixed.statMinutelyKeepHour, "hour"), false); + fixed.getHourlyKey(fixedHourlyTickDate.subtract(fixed.statHourlyKeepDay, "day"), false); + } + + UptimeCalculator.currentDate = minutelyEndDate; + assert.strictEqual(fixed.getDataArray(minutelyQueueLimit, "minute").length, minutelyQueueLimit); + + UptimeCalculator.currentDate = hourlyEndDate; + assert.strictEqual(fixed.getDataArray(hourlyQueueLimit, "hour").length, hourlyQueueLimit); + }); + + test("getDailyKey() returns correct timestamp for start of day", () => { + let c2 = new UptimeCalculator(); + let dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 20:46:00")); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); + + c2 = new UptimeCalculator(); + dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 23:45:30")); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); + + // Edge case 1 + c2 = new UptimeCalculator(); + dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 23:59:59")); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); + + // Edge case 2 + c2 = new UptimeCalculator(); + dailyKey = c2.getDailyKey(dayjs.utc("2023-08-12 00:00:00")); + assert.strictEqual(dailyKey, dayjs.utc("2023-08-12").unix()); + + // Test timezone + c2 = new UptimeCalculator(); + dailyKey = c2.getDailyKey(dayjs("Sat Dec 23 2023 05:38:39 GMT+0800 (Hong Kong Standard Time)")); + assert.strictEqual(dailyKey, dayjs.utc("2023-12-22").unix()); + }); + + test("lastDailyUptimeData tracks UP status correctly", async () => { + let c2 = new UptimeCalculator(); + await c2.update(UP); + assert.strictEqual(c2.lastDailyUptimeData.up, 1); + }); + + test("get24Hour() calculates uptime and average ping correctly", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + + // No data + let c2 = new UptimeCalculator(); + let data = c2.get24Hour(); + assert.strictEqual(data.uptime, 0); + assert.strictEqual(data.avgPing, null); + + // 1 Up + c2 = new UptimeCalculator(); + await c2.update(UP, 100); + let uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 1); + assert.strictEqual(c2.get24Hour().avgPing, 100); + + // 2 Up + c2 = new UptimeCalculator(); + await c2.update(UP, 100); + await c2.update(UP, 200); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 1); + assert.strictEqual(c2.get24Hour().avgPing, 150); + + // 3 Up + c2 = new UptimeCalculator(); + await c2.update(UP, 0); + await c2.update(UP, 100); + await c2.update(UP, 400); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 1); + assert.strictEqual(c2.get24Hour().avgPing, 166.66666666666666); + + // 1 MAINTENANCE + c2 = new UptimeCalculator(); + await c2.update(MAINTENANCE); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); + + // 1 PENDING + c2 = new UptimeCalculator(); + await c2.update(PENDING); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); + + // 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); + + // 2 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0); + assert.strictEqual(c2.get24Hour().avgPing, null); + + // 1 DOWN, 1 UP + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(UP, 0.5); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.5); + assert.strictEqual(c2.get24Hour().avgPing, 0.5); + + // 1 UP, 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(UP, 123); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.5); + assert.strictEqual(c2.get24Hour().avgPing, 123); + + // Add 24 hours + c2 = new UptimeCalculator(); + await c2.update(UP, 0); + await c2.update(UP, 0); + await c2.update(UP, 0); + await c2.update(UP, 1); + await c2.update(DOWN); + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.8); + assert.strictEqual(c2.get24Hour().avgPing, 0.25); + + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(24, "hour"); + + // After 24 hours, even if there is no data, the uptime should be still 80% + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.8); + assert.strictEqual(c2.get24Hour().avgPing, 0.25); + + // Add more 24 hours (48 hours) + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(24, "hour"); + + // After 48 hours, even if there is no data, the uptime should be still 80% + uptime = c2.get24Hour().uptime; + assert.strictEqual(uptime, 0.8); + assert.strictEqual(c2.get24Hour().avgPing, 0.25); + }); + + test("get7Day() calculates 7-day uptime correctly", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + + // No data + let c2 = new UptimeCalculator(); + let uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); + + // 1 Up + c2 = new UptimeCalculator(); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 1); + + // 2 Up + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 1); + + // 3 Up + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(UP); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 1); + + // 1 MAINTENANCE + c2 = new UptimeCalculator(); + await c2.update(MAINTENANCE); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); + + // 1 PENDING + c2 = new UptimeCalculator(); + await c2.update(PENDING); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); + + // 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); + + // 2 DOWN + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0); + + // 1 DOWN, 1 UP + c2 = new UptimeCalculator(); + await c2.update(DOWN); + await c2.update(UP); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.5); + + // 1 UP, 1 DOWN + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.5); + + // Add 7 days + c2 = new UptimeCalculator(); + await c2.update(UP); + await c2.update(UP); + await c2.update(UP); + await c2.update(UP); + await c2.update(DOWN); + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.8); + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(7, "day"); + + // After 7 days, even if there is no data, the uptime should be still 80% + uptime = c2.get7Day().uptime; + assert.strictEqual(uptime, 0.8); + }); + + test("get30Day() calculates 30-day uptime correctly with 1 check per day", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + + let c2 = new UptimeCalculator(); + let uptime = c2.get30Day().uptime; + assert.strictEqual(uptime, 0); + + let up = 0; + let down = 0; + let flip = true; + for (let i = 0; i < 30; i++) { + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(1, "day"); + + if (flip) { + await c2.update(UP); + up++; + } else { + await c2.update(DOWN); + down++; + } + + uptime = c2.get30Day().uptime; + assert.strictEqual(uptime, up / (up + down)); + + flip = !flip; + } + + // Last 7 days + // Down, Up, Down, Up, Down, Up, Down + // So 3 UP + assert.strictEqual(c2.get7Day().uptime, 3 / 7); + }); + + test("get1Year() calculates 1-year uptime correctly with 1 check per day", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + + let c2 = new UptimeCalculator(); + let uptime = c2.get1Year().uptime; + assert.strictEqual(uptime, 0); + + let flip = true; + for (let i = 0; i < 365; i++) { + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(1, "day"); + + if (flip) { + await c2.update(UP); + } else { + await c2.update(DOWN); + } + + uptime = c2.get30Day().time; + flip = !flip; + } + + assert.strictEqual(c2.get1Year().uptime, 183 / 365); + assert.strictEqual(c2.get30Day().uptime, 15 / 30); + assert.strictEqual(c2.get7Day().uptime, 4 / 7); + }); + + describe("Worst case scenario", () => { + test( + "handles year-long simulation with various statuses", + { + skip: process.env.GITHUB_ACTIONS, // Not stable on GitHub Actions" + }, + async (t) => { + console.log("Memory usage before preparation", memoryUsage()); + + let c = new UptimeCalculator(); + let up = 0; + let down = 0; + let interval = 20; + + await t.test("Prepare data", async () => { + UptimeCalculator.currentDate = dayjs.utc("2023-08-12 20:46:59"); + + // Since 2023-08-12 will be out of 365 range, it starts from 2023-08-13 actually + let actualStartDate = dayjs.utc("2023-08-13 00:00:00").unix(); + + // Simulate 1s interval for a year + for (let i = 0; i < 365 * 24 * 60 * 60; i += interval) { + UptimeCalculator.currentDate = UptimeCalculator.currentDate.add(interval, "second"); + + //Randomly UP, DOWN, MAINTENANCE, PENDING + let rand = Math.random(); + if (rand < 0.25) { + c.update(UP); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + up++; + } + } else if (rand < 0.5) { + c.update(DOWN); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + down++; + } + } else if (rand < 0.75) { + c.update(MAINTENANCE); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + //up++; + } + } else { + c.update(PENDING); + if (UptimeCalculator.currentDate.unix() > actualStartDate) { + down++; + } + } + } + console.log("Final Date: ", UptimeCalculator.currentDate.format("YYYY-MM-DD HH:mm:ss")); + console.log("Memory usage before preparation", memoryUsage()); + + assert.strictEqual(c.minutelyUptimeDataList.length(), 1440); + assert.strictEqual(c.dailyUptimeDataList.length(), 365); + }); + + await t.test("get1YearUptime()", async () => { + assert.strictEqual(c.get1Year().uptime, up / (up + down)); + }); + } + ); + }); +}); + +/** + * Code from here: https://stackoverflow.com/a/64550489/1097815 + * @returns {{rss: string, heapTotal: string, heapUsed: string, external: string}} Current memory usage + */ +function memoryUsage() { + const formatMemoryUsage = (data) => `${Math.round((data / 1024 / 1024) * 100) / 100} MB`; + const memoryData = process.memoryUsage(); + + return { + rss: `${formatMemoryUsage(memoryData.rss)} -> Resident Set Size - total memory allocated for the process execution`, + heapTotal: `${formatMemoryUsage(memoryData.heapTotal)} -> total size of the allocated heap`, + heapUsed: `${formatMemoryUsage(memoryData.heapUsed)} -> actual memory used during the execution`, + external: `${formatMemoryUsage(memoryData.external)} -> V8 external memory`, + }; +} diff --git a/test/backend-test/test-util-server.js b/test/backend-test/test-util-server.js new file mode 100644 index 0000000..29628d8 --- /dev/null +++ b/test/backend-test/test-util-server.js @@ -0,0 +1,57 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const { pingAsync } = require("../../server/util-server"); + +describe("Server Utilities: pingAsync", () => { + test("should convert IDN domains to Punycode before pinging", async () => { + const idnDomain = "münchen.de"; + const punycodeDomain = "xn--mnchen-3ya.de"; + + await assert.rejects(pingAsync(idnDomain, false, 1, "", true, 56, 1, 1), (err) => { + if (err.message.includes("Parameter string not correctly encoded")) { + assert.fail("Ping failed with encoding error: IDN was not converted"); + } + assert.ok( + err.message.includes(punycodeDomain), + `Error message should contain the Punycode domain "${punycodeDomain}". Got: ${err.message}` + ); + return true; + }); + }); + + test("should strip brackets from IPv6 addresses before pinging", async () => { + const ipv6WithBrackets = "[2606:4700:4700::1111]"; + const ipv6Raw = "2606:4700:4700::1111"; + + await assert.rejects(pingAsync(ipv6WithBrackets, true, 1, "", true, 56, 1, 1), (err) => { + assert.strictEqual( + err.message.includes(ipv6WithBrackets), + false, + "Error message should not contain brackets" + ); + // Allow either the IP in the message (local) OR "Network is unreachable" + const containsIP = err.message.includes(ipv6Raw); + const isUnreachable = + err.message.includes("Network is unreachable") || err.message.includes("Network unreachable"); + // macOS error when IPv6 stack is missing + const isMacOSError = err.message.includes("nodename nor servname provided"); + assert.ok( + containsIP || isUnreachable || isMacOSError, + `Ping failed correctly, but error message format was unexpected.\nGot: "${err.message}"\nExpected to contain IP "${ipv6Raw}" OR be a standard network error.` + ); + return true; + }); + }); + + test("should handle standard ASCII domains correctly", async () => { + const domain = "invalid-domain.test"; + await assert.rejects(pingAsync(domain, false, 1, "", true, 56, 1, 1), (err) => { + assert.strictEqual(err.message.includes("Parameter string not correctly encoded"), false); + assert.ok( + err.message.includes(domain), + `Error message should contain the domain "${domain}". Got: ${err.message}` + ); + return true; + }); + }); +}); diff --git a/test/backend-test/test-util.js b/test/backend-test/test-util.js new file mode 100644 index 0000000..7e0b17c --- /dev/null +++ b/test/backend-test/test-util.js @@ -0,0 +1,54 @@ +const { describe, test } = require("node:test"); +const assert = require("node:assert"); +const dayjs = require("dayjs"); + +const { SQL_DATETIME_FORMAT } = require("../../src/util"); + +/** + * Retries a test function with exponential backoff for external service reliability. + * Logs a warning instead of failing the test if all retries are exhausted. + * @param {Function} testFn - Async function to retry + * @param {number} maxAttempts - Maximum number of retry attempts (default: 5) + * @returns {Promise} + */ +async function retryExternalService(testFn, maxAttempts = 5) { + console.warn(`[WARN] It is better to reimplement the test to avoid relying on external services.`); + + let lastError; + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + try { + await testFn(); + return; // Success, exit retry loop + } catch (error) { + lastError = error; + // Wait a bit before retrying with exponential backoff + if (attempt < maxAttempts) { + await new Promise((resolve) => setTimeout(resolve, 500 * 2 ** (attempt - 1))); + } + } + } + // If all retries failed, log warning instead of failing the test + console.warn(`[WARN] External service test failed after ${maxAttempts} attempts: ${lastError.message}`); +} + +module.exports = { retryExternalService }; + +dayjs.extend(require("dayjs/plugin/utc")); +dayjs.extend(require("dayjs/plugin/customParseFormat")); + +describe("Server Utilities", () => { + test("SQL_DATETIME_FORMAT constant matches MariaDB/MySQL format", () => { + assert.strictEqual(SQL_DATETIME_FORMAT, "YYYY-MM-DD HH:mm:ss"); + }); + + test("SQL_DATETIME_FORMAT produces valid SQL datetime string", () => { + const current = dayjs.utc("2025-12-19T01:04:02.129Z"); + const sqlFormat = current.utc().format(SQL_DATETIME_FORMAT); + + assert.strictEqual(sqlFormat, "2025-12-19 01:04:02"); + + // Verify it can be parsed back + const parsedDate = dayjs.utc(sqlFormat, SQL_DATETIME_FORMAT); + assert.strictEqual(parsedDate.unix(), current.unix()); + }); +}); diff --git a/test/e2e/specs/basic-auth.spec.js b/test/e2e/specs/basic-auth.spec.js new file mode 100644 index 0000000..111c74d --- /dev/null +++ b/test/e2e/specs/basic-auth.spec.js @@ -0,0 +1,45 @@ +import { test, expect } from "@playwright/test"; +import { restoreSqliteSnapshot } from "../util-test"; + +const serverUrl = require("../../../playwright.config.js").url; + +test.describe("/metrics basic auth", () => { + test.beforeEach(async () => { + await restoreSqliteSnapshot(); + }); + + test("returns 401 without credentials", async ({ request }) => { + const response = await request.get(`${serverUrl}/metrics`); + expect(response.status()).toBe(401); + }); + + test("returns 401 with wrong password", async ({ request }) => { + const response = await request.get(`${serverUrl}/metrics`, { + headers: { + Authorization: "Basic " + btoa("admin:wrongpassword"), + }, + }); + expect(response.status()).toBe(401); + }); + + test("returns 200 with correct credentials", async ({ request }) => { + const response = await request.get(`${serverUrl}/metrics`, { + headers: { + Authorization: "Basic " + btoa("admin:Kuma-E2E-8f4Q2xR9p"), + }, + }); + expect(response.status()).toBe(200); + }); + + test("response contains prometheus metrics", async ({ request }) => { + const response = await request.get(`${serverUrl}/metrics`, { + headers: { + Authorization: "Basic " + btoa("admin:Kuma-E2E-8f4Q2xR9p"), + }, + }); + expect(response.status()).toBe(200); + const body = await response.text(); + expect(body).toContain("# HELP"); + expect(body).toContain("# TYPE"); + }); +}); diff --git a/test/e2e/specs/domain-expiry-notification.spec.js b/test/e2e/specs/domain-expiry-notification.spec.js new file mode 100644 index 0000000..94c4083 --- /dev/null +++ b/test/e2e/specs/domain-expiry-notification.spec.js @@ -0,0 +1,23 @@ +import { expect, test } from "@playwright/test"; +import { login, restoreSqliteSnapshot, screenshot } from "../util-test"; + +test.describe("Domain Expiry Notification", () => { + test.beforeEach(async ({ page }) => { + await restoreSqliteSnapshot(page); + }); + + test("TLD enabled for new monitor", async ({ page }, testInfo) => { + await page.goto("./add"); + await login(page); + + const monitorTypeSelect = page.getByTestId("monitor-type-select"); + await monitorTypeSelect.selectOption("http"); + + await page.getByTestId("url-input").fill("https://example.com"); + + const checkbox = page.getByLabel("Domain Name Expiry Notification"); + await expect(checkbox).toBeChecked(); + + await screenshot(testInfo, page); + }); +}); diff --git a/test/e2e/specs/example.spec.js b/test/e2e/specs/example.spec.js new file mode 100644 index 0000000..93d335b --- /dev/null +++ b/test/e2e/specs/example.spec.js @@ -0,0 +1,37 @@ +import { expect, test } from "@playwright/test"; +import { login, restoreSqliteSnapshot, screenshot } from "../util-test"; + +test.describe("Example Spec", () => { + test.beforeEach(async ({ page }) => { + await restoreSqliteSnapshot(page); + }); + + test("dashboard", async ({ page }, testInfo) => { + await page.goto("./dashboard"); + await login(page); + await screenshot(testInfo, page); + }); + + test("set up monitor", async ({ page }, testInfo) => { + await page.goto("./add"); + await login(page); + + await expect(page.getByTestId("monitor-type-select")).toBeVisible(); + await page.getByTestId("monitor-type-select").selectOption("http"); + await page.getByTestId("friendly-name-input").fill("example.com"); + await page.getByTestId("url-input").fill("https://www.example.com/"); + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); // wait for the monitor to be created + + await expect(page.getByTestId("monitor-list")).toContainText("example.com"); + await screenshot(testInfo, page); + }); + + test("database is reset after previous test", async ({ page }, testInfo) => { + await page.goto("./dashboard"); + await login(page); + + await expect(page.getByTestId("monitor-list")).not.toContainText("example.com"); + await screenshot(testInfo, page); + }); +}); diff --git a/test/e2e/specs/fridendly-name.spec.js b/test/e2e/specs/fridendly-name.spec.js new file mode 100644 index 0000000..7dbe9dd --- /dev/null +++ b/test/e2e/specs/fridendly-name.spec.js @@ -0,0 +1,83 @@ +import { expect, test } from "@playwright/test"; +import { login, restoreSqliteSnapshot, screenshot } from "../util-test"; + +test.describe("Friendly Name Tests", () => { + test.beforeEach(async ({ page }) => { + await restoreSqliteSnapshot(page); + }); + + test("hostname", async ({ page }, testInfo) => { + // Test DNS monitor with hostname + await page.goto("./add"); + await login(page); + await screenshot(testInfo, page); + + await page.getByTestId("monitor-type-select").selectOption("dns"); + await page.getByTestId("hostname-input").fill("example.com"); + await screenshot(testInfo, page); + + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); + + expect(page.getByTestId("monitor-list")).toContainText("example.com"); + await screenshot(testInfo, page); + }); + + test("URL hostname", async ({ page }, testInfo) => { + // Test HTTP monitor with URL + await page.goto("./add"); + await login(page); + await screenshot(testInfo, page); + + await page.getByTestId("monitor-type-select").selectOption("http"); + await page.getByTestId("url-input").fill("https://www.example.com/"); + await screenshot(testInfo, page); + + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); + + expect(page.getByTestId("monitor-list")).toContainText("www.example.com"); + await screenshot(testInfo, page); + }); + + test("custom friendly name", async ({ page }, testInfo) => { + // Test custom friendly name for HTTP monitor + await page.goto("./add"); + await login(page); + await screenshot(testInfo, page); + + await page.getByTestId("monitor-type-select").selectOption("http"); + await page.getByTestId("url-input").fill("https://www.example.com/"); + + // Check if the friendly name placeholder is set to the hostname + const friendlyNameInput = page.getByTestId("friendly-name-input"); + expect(friendlyNameInput).toHaveAttribute("placeholder", "www.example.com"); + await screenshot(testInfo, page); + + const customName = "Example Monitor"; + await friendlyNameInput.fill(customName); + await screenshot(testInfo, page); + + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); + + expect(page.getByTestId("monitor-list")).toContainText(customName); + await screenshot(testInfo, page); + }); + + test("default friendly name", async ({ page }, testInfo) => { + // Test default friendly name when no custom name is provided + await page.goto("./add"); + await login(page); + await screenshot(testInfo, page); + + await page.getByTestId("monitor-type-select").selectOption("group"); + await screenshot(testInfo, page); + + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); + + expect(page.getByTestId("monitor-list")).toContainText("New Monitor"); + await screenshot(testInfo, page); + }); +}); diff --git a/test/e2e/specs/incident-history.spec.js b/test/e2e/specs/incident-history.spec.js new file mode 100644 index 0000000..2e2d9e9 --- /dev/null +++ b/test/e2e/specs/incident-history.spec.js @@ -0,0 +1,150 @@ +import { expect, test } from "@playwright/test"; +import { login, restoreSqliteSnapshot, screenshot } from "../util-test"; + +test.describe("Incident History", () => { + test.beforeEach(async ({ page }) => { + await restoreSqliteSnapshot(page); + }); + + test("past incidents section is hidden when no incidents exist", async ({ page }, testInfo) => { + test.setTimeout(60000); + + await page.goto("./add"); + await login(page); + await expect(page.getByTestId("monitor-type-select")).toBeVisible(); + + await page.goto("./add-status-page"); + await page.getByTestId("name-input").fill("Empty Test"); + await page.getByTestId("slug-input").fill("empty-test"); + await page.getByTestId("submit-button").click(); + await page.waitForURL("/status/empty-test?edit"); + + await page.getByTestId("save-button").click(); + await expect(page.getByTestId("edit-sidebar")).toHaveCount(0); + + const pastIncidentsSection = page.locator(".past-incidents-section"); + await expect(pastIncidentsSection).toHaveCount(0); + + await screenshot(testInfo, page); + }); + + test("active pinned incidents are shown at top and not in past incidents", async ({ page }, testInfo) => { + test.setTimeout(60000); + + await page.goto("./add"); + await login(page); + await expect(page.getByTestId("monitor-type-select")).toBeVisible(); + + await page.goto("./add-status-page"); + await page.getByTestId("name-input").fill("Dedup Test"); + await page.getByTestId("slug-input").fill("dedup-test"); + await page.getByTestId("submit-button").click(); + await page.waitForURL("/status/dedup-test?edit"); + + await page.getByTestId("create-incident-button").click(); + await page.getByTestId("incident-title").fill("Active Incident"); + await page.getByTestId("incident-content-editable").fill("This is an active incident"); + await page.getByTestId("post-incident-button").click(); + + await page.waitForTimeout(500); + + await page.getByTestId("save-button").click(); + await expect(page.getByTestId("edit-sidebar")).toHaveCount(0); + + const activeIncident = page.getByTestId("incident").filter({ hasText: "Active Incident" }); + await expect(activeIncident).toBeVisible(); + + const pastIncidentsSection = page.locator(".past-incidents-section"); + await expect(pastIncidentsSection).toHaveCount(0); + + await screenshot(testInfo, page); + }); + + test("resolved incidents appear in past incidents section", async ({ page }, testInfo) => { + test.setTimeout(120000); + + await page.goto("./add"); + await login(page); + await expect(page.getByTestId("monitor-type-select")).toBeVisible(); + + await page.goto("./add-status-page"); + await page.getByTestId("name-input").fill("Resolve Test"); + await page.getByTestId("slug-input").fill("resolve-test"); + await page.getByTestId("submit-button").click(); + await page.waitForURL("/status/resolve-test?edit"); + + await page.getByTestId("create-incident-button").click(); + await page.getByTestId("incident-title").fill("Resolved Incident"); + await page.getByTestId("incident-content-editable").fill("This incident will be resolved"); + await page.getByTestId("post-incident-button").click(); + + await page.waitForTimeout(500); + + const activeIncidentBanner = page.getByTestId("incident").filter({ hasText: "Resolved Incident" }); + await expect(activeIncidentBanner).toBeVisible({ timeout: 10000 }); + + const resolveButton = activeIncidentBanner.locator("button", { hasText: "Resolve" }); + await expect(resolveButton).toBeVisible(); + await resolveButton.click(); + + await expect(activeIncidentBanner).toHaveCount(0, { timeout: 10000 }); + + await page.getByTestId("save-button").click(); + await expect(page.getByTestId("edit-sidebar")).toHaveCount(0); + + await page.goto("./status/resolve-test"); + await page.waitForLoadState("networkidle"); + + const pastIncidentsSection = page.locator(".past-incidents-section"); + await expect(pastIncidentsSection).toBeVisible({ timeout: 15000 }); + + const resolvedIncidentTitle = pastIncidentsSection.locator(".incident-title"); + await expect(resolvedIncidentTitle).toContainText("Resolved Incident", { timeout: 15000 }); + + await screenshot(testInfo, page); + }); + + test("incident history pagination loads more incidents", async ({ page }, testInfo) => { + test.setTimeout(180000); + + await page.goto("./add"); + await login(page); + await expect(page.getByTestId("monitor-type-select")).toBeVisible(); + + await page.goto("./add-status-page"); + await page.getByTestId("name-input").fill("Pagination Test"); + await page.getByTestId("slug-input").fill("pagination-test"); + await page.getByTestId("submit-button").click(); + await page.waitForURL("/status/pagination-test?edit"); + + for (let i = 1; i <= 12; i++) { + await page.getByTestId("create-incident-button").click(); + await page.getByTestId("incident-title").fill("Incident " + i); + await page.getByTestId("incident-content-editable").fill("Content for incident " + i); + await page.getByTestId("post-incident-button").click(); + await page.waitForTimeout(300); + + const resolveButton = page.locator("button", { hasText: "Resolve" }).first(); + if (await resolveButton.isVisible()) { + await resolveButton.click(); + await page.waitForTimeout(300); + } + } + + await page.getByTestId("save-button").click(); + await expect(page.getByTestId("edit-sidebar")).toHaveCount(0); + + await page.waitForTimeout(1000); + + const pastIncidentsSection = page.locator(".past-incidents-section"); + await expect(pastIncidentsSection).toBeVisible(); + + const loadMoreButton = page.locator("button", { hasText: "Load More" }); + + if (await loadMoreButton.isVisible()) { + await loadMoreButton.click(); + await page.waitForTimeout(1000); + await screenshot(testInfo, page); + } + }); +}); diff --git a/test/e2e/specs/monitor-form.spec.js b/test/e2e/specs/monitor-form.spec.js new file mode 100644 index 0000000..9f1a216 --- /dev/null +++ b/test/e2e/specs/monitor-form.spec.js @@ -0,0 +1,138 @@ +import { expect, test } from "@playwright/test"; +import { login, restoreSqliteSnapshot, screenshot } from "../util-test"; + +/** + * Selects the monitor type from the dropdown. + * @param {import('@playwright/test').Page} page - The Playwright page instance. + * @param {string} monitorType - The monitor type to select (default is "dns"). + * @returns {Promise} - A promise that resolves when the monitor type is selected. + */ +async function selectMonitorType(page, monitorType = "dns") { + const monitorTypeSelect = page.getByTestId("monitor-type-select"); + await expect(monitorTypeSelect).toBeVisible(); + await monitorTypeSelect.selectOption(monitorType); + + const selectedValue = await monitorTypeSelect.evaluate((select) => select.value); + expect(selectedValue).toBe(monitorType); +} + +test.describe("Monitor Form", () => { + test.beforeEach(async ({ page }) => { + await restoreSqliteSnapshot(page); + }); + + test("condition ui", async ({ page }, testInfo) => { + await page.goto("./add"); + await login(page); + await screenshot(testInfo, page); + await selectMonitorType(page); + + await page.getByTestId("add-condition-button").click(); + expect(await page.getByTestId("condition").count()).toEqual(1); // 1 explicitly added + + await page.getByTestId("add-group-button").click(); + expect(await page.getByTestId("condition-group").count()).toEqual(1); + expect(await page.getByTestId("condition").count()).toEqual(2); // 1 solo conditions + 1 condition in group + + await screenshot(testInfo, page); + + await page.getByTestId("remove-condition").first().click(); + expect(await page.getByTestId("condition").count()).toEqual(1); // 0 solo condition + 1 condition in group + + await page.getByTestId("remove-condition-group").first().click(); + expect(await page.getByTestId("condition-group").count()).toEqual(0); + + await screenshot(testInfo, page); + }); + + test("successful condition", async ({ page }, testInfo) => { + await page.goto("./add"); + await login(page); + await screenshot(testInfo, page); + await selectMonitorType(page); + + const friendlyName = "Example DNS NS"; + await page.getByTestId("friendly-name-input").fill(friendlyName); + await page.getByTestId("hostname-input").fill("kuma.pet"); + + const resolveTypeSelect = page.getByTestId("resolve-type-select"); + await resolveTypeSelect.click(); + await resolveTypeSelect.getByRole("option", { name: "NS" }).click(); + + await page.getByTestId("add-condition-button").click(); + expect(await page.getByTestId("condition").count()).toEqual(1); // 1 explicitly added + + await page.getByTestId("add-condition-button").click(); + expect(await page.getByTestId("condition").count()).toEqual(2); // 2 explicitly added + + await page.getByTestId("condition-value").nth(0).fill("carl.ns.cloudflare.com"); + await page.getByTestId("condition-and-or").nth(0).selectOption("or"); + await page.getByTestId("condition-value").nth(1).fill("jean.ns.cloudflare.com"); + + await screenshot(testInfo, page); + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); + + await expect(page.getByTestId("monitor-status")).toHaveText("up", { ignoreCase: true }); + + await screenshot(testInfo, page); + }); + + test("failing condition", async ({ page }, testInfo) => { + await page.goto("./add"); + await login(page); + await screenshot(testInfo, page); + await selectMonitorType(page); + + const friendlyName = "Example DNS NS"; + await page.getByTestId("friendly-name-input").fill(friendlyName); + await page.getByTestId("hostname-input").fill("kuma.pet"); + + const resolveTypeSelect = page.getByTestId("resolve-type-select"); + await resolveTypeSelect.click(); + await resolveTypeSelect.getByRole("option", { name: "NS" }).click(); + + await page.getByTestId("add-condition-button").click(); + expect(await page.getByTestId("condition").count()).toEqual(1); // 1 explicitly added + + await page.getByTestId("condition-value").nth(0).fill("definitely-not.net"); + + await screenshot(testInfo, page); + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); + + await expect(page.getByTestId("monitor-status")).toHaveText("down", { ignoreCase: true }); + + await screenshot(testInfo, page); + }); + + test("save response settings persist", async ({ page }, testInfo) => { + await page.goto("./add"); + await login(page); + await selectMonitorType(page, "http"); + + const friendlyName = "Example HTTP Save Response"; + await page.getByTestId("friendly-name-input").fill(friendlyName); + await page.getByTestId("url-input").fill("https://www.example.com/"); + + // Expect error response save enabled by default + await expect(page.getByLabel("Save HTTP Error Response for Notifications")).toBeChecked(); + + await page.getByLabel("Save HTTP Success Response for Notifications").check(); + await page.getByLabel("Save HTTP Error Response for Notifications").uncheck(); + await page.getByLabel("Response Max Length (bytes)").fill("2048"); + + await screenshot(testInfo, page); + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); + + await page.getByRole("link", { name: "Edit" }).click(); + await page.waitForURL("/edit/*"); + + await expect(page.getByLabel("Save HTTP Success Response for Notifications")).toBeHidden(); + await expect(page.getByLabel("Save HTTP Error Response for Notifications")).not.toBeChecked(); + await expect(page.getByLabel("Response Max Length (bytes)")).toHaveValue("2048"); + + await screenshot(testInfo, page); + }); +}); diff --git a/test/e2e/specs/setup-process.once.js b/test/e2e/specs/setup-process.once.js new file mode 100644 index 0000000..8783b91 --- /dev/null +++ b/test/e2e/specs/setup-process.once.js @@ -0,0 +1,62 @@ +import { test } from "@playwright/test"; +import { getSqliteDatabaseExists, login, screenshot, takeSqliteSnapshot } from "../util-test"; + +test.describe("Uptime Kuma Setup", () => { + test.skip(() => getSqliteDatabaseExists(), "Must only run once per session"); + + test.afterEach(async ({ page }, testInfo) => { + await screenshot(testInfo, page); + }); + + /* + * Setup + */ + + test("setup sqlite", async ({ page }, testInfo) => { + await page.goto("./setup-database"); + await page.getByText("SQLite").click(); + await page.getByRole("button", { name: "Next" }).click(); + await screenshot(testInfo, page); + await page.waitForURL("/setup"); // ensures the server is ready to continue to the next test + }); + + test("setup admin", async ({ page }) => { + await page.goto("./setup"); + await page.getByRole("textbox", { name: "Username" }).click(); + await page.getByRole("textbox", { name: "Username" }).fill("admin"); + await page.getByRole("textbox", { name: "Password", exact: true }).fill("Kuma-E2E-8f4Q2xR9p"); + await page.getByRole("textbox", { name: "Repeat Password" }).fill("Kuma-E2E-8f4Q2xR9p"); + await page.getByRole("button", { name: "Create" }).click(); + // User is auto-logged in and redirected to dashboard + await page.waitForURL("/dashboard"); + }); + + /* + * All other tests should be run after setup + */ + + test("login", async ({ page }) => { + await page.goto("./dashboard"); + await login(page); + }); + + test("failed login shows error alert", async ({ page }, testInfo) => { + await page.goto("./dashboard"); + await page.getByPlaceholder("Username").fill("admin"); + await page.getByPlaceholder("Password").fill("wrongpassword"); + await page.getByRole("button", { name: "Log in" }).click(); + await page.waitForSelector(".alert.alert-danger", { state: "visible" }); + await screenshot(testInfo, page); + }); + + test("logout", async ({ page }) => { + await page.goto("./dashboard"); + await login(page); + await page.getByText("A", { exact: true }).click(); + await page.getByRole("button", { name: "Log out" }).click(); + }); + + test("take sqlite snapshot", async ({ page }) => { + await takeSqliteSnapshot(page); + }); +}); diff --git a/test/e2e/specs/status-page.spec.js b/test/e2e/specs/status-page.spec.js new file mode 100644 index 0000000..19f3414 --- /dev/null +++ b/test/e2e/specs/status-page.spec.js @@ -0,0 +1,345 @@ +import { expect, test } from "@playwright/test"; +import { login, restoreSqliteSnapshot, screenshot } from "../util-test"; + +/** + * Wait until the status page has finished loading its config after the socket logs in. + * Clicking "Edit" connects the socket; the page then asynchronously fetches the private + * config, which can overwrite edits made too early. The private config is the only one + * that carries the status page `id`, so wait until that attribute is populated. + * @param {import("@playwright/test").Page} page Page + * @returns {Promise} + */ +async function waitForConfigLoaded(page) { + await expect(page.getByTestId("edit-sidebar")).toHaveAttribute("data-status-page-id", /^\d+$/); +} + +/** + * Open the status page edit sidebar and wait until its config has finished loading. + * @param {import("@playwright/test").Page} page Page + * @returns {Promise} + */ +async function openEditSidebar(page) { + await page.getByTestId("edit-button").click(); + await waitForConfigLoaded(page); +} + +test.describe("Status Page", () => { + test.beforeEach(async ({ page }) => { + await restoreSqliteSnapshot(page); + }); + + test("create and edit", async ({ page }, testInfo) => { + test.setTimeout(60000); // Keep the timeout increase for stability + + // Monitor + const monitorName = "Monitor for Status Page"; + const tagName = "Client"; + const tagValue = "Acme Inc"; + const tagName2 = "Project"; // Add second tag name + const tagValue2 = "Phoenix"; // Add second tag value + const monitorUrl = "https://www.example.com/status"; + const monitorCustomUrl = "https://www.example.com"; + + // Status Page + const footerText = "This is footer text."; + const refreshInterval = 30; + const theme = "dark"; + const googleAnalyticsId = "G-123"; + const umamiAnalyticsScriptUrl = "https://umami.example.com/script.js"; + const umamiAnalyticsWebsiteId = "606487e2-bc25-45f9-9132-fa8b065aad46"; + const plausibleAnalyticsScriptUrl = "https://plausible.example.com/js/script.js"; + const plausibleAnalyticsDomainsUrls = "one.com,two.com"; + const matomoUrl = "https://matomoto.example.com"; + const matomoSiteId = "123456789"; + const customCss = "body { background: rgb(0, 128, 128) !important; }"; + const descriptionText = "This is an example status page."; + const incidentTitle = "Example Outage Incident"; + const incidentContent = "Sample incident message."; + const groupName = "Example Group 1"; + + // Set up a monitor that can be added to the Status Page + await page.goto("./add"); + await login(page); + await expect(page.getByTestId("monitor-type-select")).toBeVisible(); + await page.getByTestId("monitor-type-select").selectOption("http"); + await page.getByTestId("friendly-name-input").fill(monitorName); + await page.getByTestId("url-input").fill(monitorUrl); + + // Modified tag section to add multiple tags + await page.getByTestId("add-tag-button").click(); + await page.getByTestId("tag-name-input").fill(tagName); + await page.getByTestId("tag-value-input").fill(tagValue); + await page.getByTestId("tag-color-select").click(); // Vue-Multiselect component + await page.getByTestId("tag-color-select").getByRole("option", { name: "Orange" }).click(); + + // Add another tag instead of submitting directly + await page.getByRole("button", { name: "Add Another Tag" }).click(); + + // Add second tag + await page.getByTestId("tag-name-input").fill(tagName2); + await page.getByTestId("tag-value-input").fill(tagValue2); + await page.getByTestId("tag-color-select").click(); + await page.getByTestId("tag-color-select").getByRole("option", { name: "Blue" }).click(); + + // Submit both tags + await page.getByTestId("add-tags-final-button").click(); + + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); // wait for the monitor to be created + + // Create a new status page + await page.goto("./add-status-page"); + await screenshot(testInfo, page); + + await page.getByTestId("name-input").fill("Example"); + await page.getByTestId("slug-input").fill("example"); + await page.getByTestId("submit-button").click(); + await page.waitForURL("/status/example?edit"); // wait for the page to be created + await waitForConfigLoaded(page); // editing connects the socket, which reloads the config + + // Fill in some details + await page.getByTestId("description-input").fill(descriptionText); + await page.getByTestId("footer-text-input").fill(footerText); + await page.getByTestId("refresh-interval-input").fill(String(refreshInterval)); + await page.getByTestId("theme-select").selectOption(theme); + await page.getByTestId("show-tags-checkbox").uncheck(); + await page.getByTestId("show-powered-by-checkbox").uncheck(); + await page.getByTestId("show-certificate-expiry-checkbox").uncheck(); + await page.getByTestId("analytics-type-select").selectOption("google"); + await page.getByTestId("analytics-id-input").fill(googleAnalyticsId); + await page.getByTestId("custom-css-input").getByTestId("textarea").fill(customCss); // Prism + + // Add an incident + await page.getByTestId("create-incident-button").click(); + await page.getByTestId("incident-title").isEditable(); + await page.getByTestId("incident-title").fill(incidentTitle); + await page.getByTestId("incident-content-editable").fill(incidentContent); + await page.getByTestId("post-incident-button").click(); + + // Add a group + await page.getByTestId("add-group-button").click(); + await page.getByTestId("group-name").isEditable(); + await page.getByTestId("group-name").fill(groupName); + + // Add the monitor + await page.getByTestId("monitor-select").click(); // Vue-Multiselect component + await page.getByTestId("monitor-select").getByRole("option", { name: monitorName }).click(); + await expect(page.getByTestId("monitor")).toHaveCount(1); + await expect(page.getByTestId("monitor-name")).toContainText(monitorName); + await expect(page.getByTestId("monitor-name")).not.toHaveAttribute("href"); + + // Set public url on + await page.getByTestId("monitor-settings").click(); + await page.getByTestId("show-clickable-link").check(); + await page.getByTestId("custom-url-input").fill(monitorCustomUrl); + await page.getByTestId("monitor-settings-close").click(); + + // Save the changes + await screenshot(testInfo, page); + // Saving reloads the whole page (location.href), so wait for that navigation + // before asserting on the SSR'd . + await Promise.all([page.waitForEvent("load"), page.getByTestId("save-button").click()]); + await expect(page.getByTestId("edit-sidebar")).toHaveCount(0); + + // Ensure changes are visible + await expect(page.getByTestId("incident")).toHaveCount(1); + await expect(page.getByTestId("incident-title")).toContainText(incidentTitle); + await expect(page.getByTestId("incident-content")).toContainText(incidentContent); + await expect(page.getByTestId("group-name")).toContainText(groupName); + await expect(page.getByTestId("powered-by")).toHaveCount(0); + + await expect(page.getByTestId("monitor-name")).toHaveAttribute("href", monitorCustomUrl); + + await expect(page.getByTestId("update-countdown-text")).toContainText("00:"); + const updateCountdown = Number( + (await page.getByTestId("update-countdown-text").textContent()).match(/(\d+):(\d+)/)[2] + ); + expect(updateCountdown).toBeGreaterThanOrEqual(refreshInterval - 10); // can't be certain when the timer will start, so ensure it's within expected range + expect(updateCountdown).toBeLessThanOrEqual(refreshInterval); + + await expect(page.locator("body")).toHaveClass(theme); + + // Add Google Analytics ID to head and verify + await expect.poll(() => page.locator("head").innerHTML()).toContain(googleAnalyticsId); + + const backgroundColor = await page.evaluate(() => window.getComputedStyle(document.body).backgroundColor); + expect(backgroundColor).toEqual("rgb(0, 128, 128)"); + + await screenshot(testInfo, page); + expect(await page.locator("head").innerHTML()).toContain(googleAnalyticsId); + + // Flip the "Show Tags" and "Show Powered By" switches: + await openEditSidebar(page); + await expect(page.getByTestId("edit-sidebar")).toHaveCount(1); + await page.getByTestId("show-tags-checkbox").setChecked(true); + await page.getByTestId("show-powered-by-checkbox").setChecked(true); + + await screenshot(testInfo, page); + + // Fill in umami analytics after editing + await page.getByTestId("analytics-type-select").selectOption("umami"); + await page.getByTestId("analytics-script-url-input").fill(umamiAnalyticsScriptUrl); + await page.getByTestId("analytics-id-input").fill(umamiAnalyticsWebsiteId); + // Saving reloads the whole page (location.href), so wait for that navigation + // before asserting on the SSR'd . + await Promise.all([page.waitForEvent("load"), page.getByTestId("save-button").click()]); + + await expect(page.getByTestId("edit-sidebar")).toHaveCount(0); + await expect(page.getByTestId("powered-by")).toContainText("Powered by"); + + // Modified tag verification to check both tags + await expect(page.getByTestId("monitor-tag").filter({ hasText: tagValue })).toBeVisible(); + await expect(page.getByTestId("monitor-tag").filter({ hasText: tagValue2 })).toBeVisible(); + + await screenshot(testInfo, page); + + await expect.poll(() => page.locator("head").innerHTML()).toContain(umamiAnalyticsScriptUrl); + expect(await page.locator("head").innerHTML()).toContain(umamiAnalyticsWebsiteId); + + await openEditSidebar(page); + // Fill in plausible analytics after editing + await page.getByTestId("analytics-type-select").selectOption("plausible"); + await page.getByTestId("analytics-script-url-input").fill(plausibleAnalyticsScriptUrl); + await page.getByTestId("analytics-id-input").fill(plausibleAnalyticsDomainsUrls); + // Saving reloads the whole page (location.href), so wait for that navigation + // before asserting on the SSR'd . + await Promise.all([page.waitForEvent("load"), page.getByTestId("save-button").click()]); + await screenshot(testInfo, page); + await expect.poll(() => page.locator("head").innerHTML()).toContain(plausibleAnalyticsScriptUrl); + expect(await page.locator("head").innerHTML()).toContain(plausibleAnalyticsDomainsUrls); + + await openEditSidebar(page); + // Fill in matomo analytics after editing + await page.getByTestId("analytics-type-select").selectOption("matomo"); + await page.getByTestId("analytics-script-url-input").fill(matomoUrl); + await page.getByTestId("analytics-id-input").fill(matomoSiteId); + // Saving reloads the whole page (location.href), so wait for that navigation + // before asserting on the SSR'd . + await Promise.all([page.waitForEvent("load"), page.getByTestId("save-button").click()]); + await screenshot(testInfo, page); + await expect.poll(() => page.locator("head").innerHTML()).toContain(matomoUrl); + expect(await page.locator("head").innerHTML()).toContain(matomoSiteId); + }); + + // @todo Test certificate expiry + // @todo Test domain names + + test("RSS feed escapes malicious monitor names", async ({ page }, testInfo) => { + test.setTimeout(60000); + + // Test various XSS payloads in monitor names + const maliciousMonitorName1 = ""; + const maliciousMonitorName2 = "x"; + const normalMonitorName = "Production API Server"; + + await page.goto("./add"); + await login(page); + + // Create first monitor with script tag payload + await expect(page.getByTestId("monitor-type-select")).toBeVisible(); + await page.getByTestId("monitor-type-select").selectOption("http"); + await page.getByTestId("friendly-name-input").fill(maliciousMonitorName1); + await page.getByTestId("url-input").fill("https://malicious1.example.com"); + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); + + // Create second monitor with title breakout payload + await page.goto("./add"); + await page.getByTestId("monitor-type-select").selectOption("http"); + await page.getByTestId("friendly-name-input").fill(maliciousMonitorName2); + await page.getByTestId("url-input").fill("https://malicious2.example.com"); + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); + + // Create third monitor with normal name + await page.goto("./add"); + await page.getByTestId("monitor-type-select").selectOption("http"); + await page.getByTestId("friendly-name-input").fill(normalMonitorName); + await page.getByTestId("url-input").fill("https://normal.example.com"); + await page.getByTestId("save-button").click(); + await page.waitForURL("/dashboard/*"); + + // Create a status page + await page.goto("./add-status-page"); + await page.getByTestId("name-input").fill("Security Test"); + await page.getByTestId("slug-input").fill("security-test"); + await page.getByTestId("submit-button").click(); + await page.waitForURL("/status/security-test?edit"); + await waitForConfigLoaded(page); // editing connects the socket, which reloads the config + + // Add a group and all monitors + await page.getByTestId("add-group-button").click(); + await page.getByTestId("group-name").fill("Test Group"); + + // Add all three monitors + await page.getByTestId("monitor-select").click(); + await page.getByTestId("monitor-select").getByRole("option", { name: maliciousMonitorName1 }).click(); + await page.getByTestId("monitor-select").click(); + await page.getByTestId("monitor-select").getByRole("option", { name: maliciousMonitorName2 }).click(); + await page.getByTestId("monitor-select").click(); + await page.getByTestId("monitor-select").getByRole("option", { name: normalMonitorName }).click(); + + await page.getByTestId("save-button").click(); + await expect(page.getByTestId("edit-sidebar")).toHaveCount(0); + + // Fetch the RSS feed + const rssResponse = await page.request.get("/status/security-test/rss"); + expect(rssResponse.status()).toBe(200); + expect(rssResponse.headers()["content-type"]).toBe("application/rss+xml; charset=utf-8"); + expect(rssResponse.ok()).toBeTruthy(); + + const rssContent = await rssResponse.text(); + + // Attach RSS content for inspection + await testInfo.attach("rss-feed.xml", { + body: rssContent, + contentType: "application/xml", + }); + + // Verify all payloads are escaped using CDATA + expect(rssContent).toContain(`<title><![CDATA[${maliciousMonitorName1} is down]]>`); + expect(rssContent).toContain(`<![CDATA[${maliciousMonitorName2} is down]]>`); + expect(rssContent).toContain(`<![CDATA[${normalMonitorName} is down]]>`); + + // Verify RSS feed structure is valid + expect(rssContent).toContain('"); + + // Verify RSS feed uses status page title as fallback (from issue #6217) + expect(rssContent).toContain("Security Test RSS Feed"); + + // Verify RSS link uses the correct domain (not localhost hardcoded) + expect(rssContent).toMatch(/https?:\/\/[^<]+\/status\/security-test<\/link>/); + + // Test custom RSS title functionality + const customRssTitle = "Custom RSS Feed Title"; + await openEditSidebar(page); + await expect(page.getByTestId("edit-sidebar")).toHaveCount(1); + await page.getByTestId("rss-title-input").fill(customRssTitle); + await page.getByTestId("save-button").click(); + await expect(page.getByTestId("edit-sidebar")).toHaveCount(0); + + // Fetch RSS feed again - retry until custom title appears (DB write may not be committed yet) + let rssContentCustom; + for (let i = 0; i < 10; i++) { + const rssResponseCustom = await page.request.get("/status/security-test/rss"); + expect(rssResponseCustom.status()).toBe(200); + rssContentCustom = await rssResponseCustom.text(); + if (rssContentCustom.includes(`${customRssTitle}`)) { + break; + } + await page.waitForTimeout(500); + } + + // Verify RSS feed uses custom title + expect(rssContentCustom).toContain(`${customRssTitle}`); + + await testInfo.attach("rss-feed-custom-title.xml", { + body: rssContentCustom, + contentType: "application/xml", + }); + + await screenshot(testInfo, page); + }); +}); diff --git a/test/e2e/util-test.js b/test/e2e/util-test.js new file mode 100644 index 0000000..68149c6 --- /dev/null +++ b/test/e2e/util-test.js @@ -0,0 +1,62 @@ +const fs = require("fs"); +const path = require("path"); +const serverUrl = require("../../playwright.config.js").url; + +const dbPath = "./../../data/playwright-test/kuma.db"; + +/** + * @param {TestInfo} testInfo Test info + * @param {Page} page Page + * @returns {Promise} + */ +export async function screenshot(testInfo, page) { + const screenshot = await page.screenshot(); + await testInfo.attach("screenshot", { + body: screenshot, + contentType: "image/png", + }); +} + +/** + * @param {Page} page Page + * @returns {Promise} + */ +export async function login(page) { + // Login + await page.getByPlaceholder("Username").click(); + await page.getByPlaceholder("Username").fill("admin"); + await page.getByPlaceholder("Username").press("Tab"); + await page.getByPlaceholder("Password").fill("Kuma-E2E-8f4Q2xR9p"); + await page.getByLabel("Remember me").check(); + await page.getByRole("button", { name: "Log in" }).click(); + await page.isVisible("text=Add New Monitor"); +} + +/** + * Determines if the SQLite database has been created. This indicates setup has completed. + * @returns {boolean} True if exists + */ +export function getSqliteDatabaseExists() { + return fs.existsSync(path.resolve(__dirname, dbPath)); +} + +/** + * Makes a request to the server to take a snapshot of the SQLite database. + * @param {Page|null} page Page + * @returns {Promise} Promise of response from snapshot request. + */ +export async function takeSqliteSnapshot(page = null) { + if (page) { + return page.goto("./_e2e/take-sqlite-snapshot"); + } else { + return fetch(`${serverUrl}/_e2e/take-sqlite-snapshot`); + } +} + +/** + * Makes a request to the server to restore the snapshot of the SQLite database. + * @returns {Promise} Promise of response from restoration request. + */ +export async function restoreSqliteSnapshot() { + return fetch(`${serverUrl}/_e2e/restore-sqlite-snapshot`); +} diff --git a/test/fixtures/v1-kuma.db.gz b/test/fixtures/v1-kuma.db.gz new file mode 100644 index 0000000..e7b5abd Binary files /dev/null and b/test/fixtures/v1-kuma.db.gz differ diff --git a/test/fixtures/v2-kuma.db.gz b/test/fixtures/v2-kuma.db.gz new file mode 100644 index 0000000..ca2905d Binary files /dev/null and b/test/fixtures/v2-kuma.db.gz differ diff --git a/test/manual-test-grpc/echo.proto b/test/manual-test-grpc/echo.proto new file mode 100644 index 0000000..39ae6a6 --- /dev/null +++ b/test/manual-test-grpc/echo.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; +package echo; +service EchoService { + rpc Echo (EchoRequest) returns (EchoResponse); +} +message EchoRequest { string message = 1; } +message EchoResponse { string message = 1; } diff --git a/test/manual-test-grpc/simple-grpc-server.js b/test/manual-test-grpc/simple-grpc-server.js new file mode 100644 index 0000000..91a401e --- /dev/null +++ b/test/manual-test-grpc/simple-grpc-server.js @@ -0,0 +1,22 @@ +const grpc = require("@grpc/grpc-js"); +const protoLoader = require("@grpc/proto-loader"); +const packageDef = protoLoader.loadSync("echo.proto", {}); +const grpcObject = grpc.loadPackageDefinition(packageDef); +const { echo } = grpcObject; + +/** + * Echo service implementation + * @param {object} call Call object + * @param {Function} callback Callback function + * @returns {void} + */ +function Echo(call, callback) { + callback(null, { message: call.request.message }); +} + +const server = new grpc.Server(); +server.addService(echo.EchoService.service, { Echo }); +server.bindAsync("0.0.0.0:50051", grpc.ServerCredentials.createInsecure(), () => { + console.log("gRPC server running on :50051"); + server.start(); +}); diff --git a/test/manual-test-radius-tls/certs/redis.crt b/test/manual-test-radius-tls/certs/redis.crt new file mode 100644 index 0000000..29e3719 --- /dev/null +++ b/test/manual-test-radius-tls/certs/redis.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUF6AMqH3K5nlbnWuHAMu12MUjqCUwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MTExNTIwMTk0OFoXDTI2MTEx +NTIwMTk0OFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAtRVPm2BOFyaRoFVP3g+OO8YYvUUzVnMlp8T+FkQUWQ3g +Xzk3k059ca67uPhfujRQ0S+e4FTPyHPrbCh73aTtFfpg1BTlEcuOAXUpxrtSpXbY +D4XvO5OZK3QmFIkUOfOMoJoi0Iv75CgVlcGnQOxPY5A12+uyzMNGPFWBX42qgeW9 +huijfqE3jwN2gM05ryA6EBk1TIewUTMycTFI3t3YLJyZdNMG4kmphIJ/Ie6Na4u3 +YaKXp9o+VCN8t5bd/IYhFW8PznlXbv2NU83ARjmfk7Vc0OrvTUsr9+NTPOdFFoCp +sAbxogO/hZpv3UtX9lzk22MpzcBbpzPodXAikNZeswIDAQABo1MwUTAdBgNVHQ4E +FgQU9PhfZPxI1PB50DdRrI82U1cRPvkwHwYDVR0jBBgwFoAU9PhfZPxI1PB50DdR +rI82U1cRPvkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAclr4 +M5PjA1nqTZfbdaerA5uvyPnIdjc6Ms6ZvP75h6kHxvUJj9kcoSbg/2nqyCY6UJod +oOPTJZppB+xfHD6ahPLO30IPw1HPr+OfTaiwBoZMnjGn08p5dAjyG0hAyHciDClY +UAxQld+5NNI82QEx5BJ3mcpO5Mi36SW7Kck2TnCza6JxjOmtFi4BLMFsnRD9Vepu +vIvu3DrCxnpYdCV6zUT55414NKCPsut7YHqkDc4gOUHcQ0QOQSRwhaGpLnkfwihe +DkHgJvcBlBOMcr2UCQvXPwPPP1dJ4Y9OQrjGxCnSeDy89cu5/6z/ZBy2ygGHehGZ +zx5PrQLiqgdChdwb6w== +-----END CERTIFICATE----- diff --git a/test/manual-test-radius-tls/certs/redis.key b/test/manual-test-radius-tls/certs/redis.key new file mode 100644 index 0000000..a3e02b2 --- /dev/null +++ b/test/manual-test-radius-tls/certs/redis.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC1FU+bYE4XJpGg +VU/eD447xhi9RTNWcyWnxP4WRBRZDeBfOTeTTn1xrru4+F+6NFDRL57gVM/Ic+ts +KHvdpO0V+mDUFOURy44BdSnGu1KldtgPhe87k5krdCYUiRQ584ygmiLQi/vkKBWV +wadA7E9jkDXb67LMw0Y8VYFfjaqB5b2G6KN+oTePA3aAzTmvIDoQGTVMh7BRMzJx +MUje3dgsnJl00wbiSamEgn8h7o1ri7dhopen2j5UI3y3lt38hiEVbw/OeVdu/Y1T +zcBGOZ+TtVzQ6u9NSyv341M850UWgKmwBvGiA7+Fmm/dS1f2XOTbYynNwFunM+h1 +cCKQ1l6zAgMBAAECggEAcfZ4hEOmwcEfMzWdrxZBIHM6jJtphMWw9BXxBthPqgAm +ricXR9jE7+/U6sM9k7VrC0uaBHq7zd7OUeJkmgg6wXMyzyZbR8jAlUBES3MGv2W8 +8oALIIZyhjtx38ipdxNE9KossOz4WQ21D+uxNBXEnNdcXgzHGUDoCf6SDi3dGiQI +kloSkoyuHv3HiJdmUCII0EWVoDW0YnhKs4xpYwt0xlQ3IFA1FeqhYN3GwDb7hnp4 +HmefiydHW+rWPgXPzI3tIo6LgN+nMBPAAa95u9XfswRmBcZIhTxzlg7HaJJ1yP2B +HmyJBG20HX1RY0IHAgAQy2jA2HPIRdpQNU2FJGi34QKBgQDvMDRqr6qQjLSq8dkG +H7olc10gZHHTyY25aQG+m54SXMEdmU5zhbMoShnfQ/HVtWCAd3b5kwUBSjXacQiL +kMBV6S5+FQ+27nfgx8szqUJJi09iU43xcBDYSyJCohkM/HrHSElN3c1Y9//SneqJ +Zjeg+80+p7LvsgMM4MOKad0DowKBgQDBz5o9m9FZ+nHC1eXwUYVYNXA9wter9Erd +WosPWKnhGTJ2mNebn0aSVlagiZtyMUMps4IyWEvBHMIt0wJz2T5v+SDx9VN0si7q +Jy1i1uDO56tSHDZaN+QT/WDeaHTpzl3I28K7lqM6PDx1KQ/kqlTnWHD3wnXQ2Hfh +xXYgdeNpsQKBgQDaT+w3yBhtERBByrZkIYc8cXx5eVRvktJ5fX9rIwx3BBP7WRdC +17B7QI82ugQ8I+1ttBxylR8HW12mAG9lO3xhrZCS4dRTCnt+Pb2ZbI6lI7MUMuEm +kju9v24I1Xz53mSOCctmd+DaqJjl+t68BAEYPVvLKTNoFdk04t13M0LX8wKBgQCU +7+/M5oA0UES6AFxKmKsLRU2y+Jd6miop/tmcentZ814XS49tFTJLZLP/fKALWhYN +5tfnzniwt2P1iRF6a3kS3XVW2zs+E2wAHwk+ynhKKDg8yldub0MDpZSaddVak274 +2wDD3ZgkOYQQWPTQuaCzhACUNUkKrD88Ld2ARnfzkQKBgGTEOwN+aaOn/bU9Dm+y +Xg1tezMmxFcnewZVak+91xU6K+PS9B3GbszAs5MMYXo3UQXCUlXUI71XI7WG50LJ +NmwClF05ZcH/qYpp1mK/w6i+7VnqN0RAkD0hA3AyivmF5VCI/hzZanxLnPDvMbxS +wpjLPuBRTukoNvxJGT16z8L+ +-----END PRIVATE KEY----- diff --git a/test/mock-testdb.js b/test/mock-testdb.js new file mode 100644 index 0000000..0251fbf --- /dev/null +++ b/test/mock-testdb.js @@ -0,0 +1,35 @@ +const { sync: rimrafSync } = require("rimraf"); +const Database = require("../server/database"); +const { Settings } = require("../server/settings"); +class TestDB { + dataDir; + + constructor(dir = "./data/test") { + this.dataDir = dir; + } + + async create() { + Database.initDataDir({ "data-dir": this.dataDir }); + Database.dbConfig = { + type: "sqlite", + }; + Database.writeDBConfig(Database.dbConfig); + await Database.connect(true); + await Database.patch(); + } + + async destroy() { + await Database.close(); + Settings.stopCacheCleaner(); + if (this.dataDir) { + try { + rimrafSync(this.dataDir); + } catch (e) { + console.error("Windows may hold file lock?"); + console.error(e); + } + } + } +} + +module.exports = TestDB; diff --git a/test/prepare-test-server.js b/test/prepare-test-server.js new file mode 100644 index 0000000..1e0ed5c --- /dev/null +++ b/test/prepare-test-server.js @@ -0,0 +1,10 @@ +const fs = require("fs"); + +const path = "./data/test"; + +if (fs.existsSync(path)) { + fs.rmSync(path, { + recursive: true, + force: true, + }); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..85b23cb --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compileOnSave": true, + "compilerOptions": { + "newLine": "LF", + "target": "esnext", + "module": "esnext", + "lib": ["es2020", "DOM"], + "declaration": false, + "removeComments": true, + "preserveConstEnums": true, + "sourceMap": false, + "strict": true, + "esModuleInterop": true + }, + "files": [] +} diff --git a/vite.config.mjs b/vite.config.mjs new file mode 100644 index 0000000..5905d93 --- /dev/null +++ b/vite.config.mjs @@ -0,0 +1,61 @@ +import vue from "@vitejs/plugin-vue"; +import { defineConfig } from "vite"; +import visualizer from "rollup-plugin-visualizer"; +import viteCompression from "vite-plugin-compression"; +import postCssScss from "postcss-scss"; +import postcssRTLCSS from "postcss-rtlcss"; + +const viteCompressionFilter = /\.(js|mjs|json|css|html|svg)$/i; + +// https://vitejs.dev/config/ +export default defineConfig({ + server: { + port: 3000, + }, + define: { + FRONTEND_VERSION: JSON.stringify(process.env.npm_package_version), + "process.env": {}, + }, + plugins: [ + vue(), + visualizer({ + filename: "tmp/dist-stats.html", + }), + viteCompression({ + algorithm: "gzip", + filter: viteCompressionFilter, + }), + viteCompression({ + algorithm: "brotliCompress", + filter: viteCompressionFilter, + }), + ], + css: { + postcss: { + parser: postCssScss, + map: false, + plugins: [postcssRTLCSS], + }, + preprocessorOptions: { + scss: { + loadPaths: [process.cwd()], + quietDeps: true, + + // Most of them are coming from boostrap unfortunately + silenceDeprecations: [ + "import", + "global-builtin", + "if-function", + "color-functions", + "abs-percent", + "function-units", + ], + }, + }, + }, + build: { + commonjsOptions: { + include: [/.js$/], + }, + }, +});