Compare commits

...
Author SHA1 Message Date
Alexander Holliday 73b74ffd80 Merge pull request #3705 from bluewave-labs/fix/migrations
bump version
2026-06-05 12:02:09 -07:00
Alex Holliday 2b393f9ebe bump version 2026-06-05 12:01:35 -07:00
Alexander Holliday 562a17707d Merge pull request #3704 from bluewave-labs/fix/migrations
fix/migrations
2026-06-05 12:00:51 -07:00
Alex Holliday 7d800b0f38 Merge branch 'develop' into fix/migrations 2026-06-05 11:58:11 -07:00
Alex Holliday a19479b578 fix migration race condition 2026-06-05 11:58:00 -07:00
Alexander Holliday 9b4a810e21 Merge pull request #3700 from bluewave-labs/chore/version-bump
bump version
2026-06-05 10:38:05 -07:00
Alex Holliday 1388d61b07 bump version 2026-06-05 10:37:43 -07:00
Alexander Holliday 8dbe42288b Merge pull request #3684 from jhcpeixoto/feat/ntfy-notifications
feat: add ntfy notification support
2026-06-05 09:35:04 -07:00
Alexander Holliday 45e31e7212 Merge pull request #3694 from bluewave-labs/feat/fe/progress-indicator-infra
Feat: Progress indicator infra & pagespeed
2026-06-04 00:25:19 -07:00
Br0wnHammer 7872b18108 refactor(client): inline isEditMode instead of shadowing it with isWizard 2026-06-04 11:09:09 +05:30
Alexander Holliday 15ac5fa84d Merge pull request #3697 from bluewave-labs/fix/jobs-pagination
fix: jobs pagination
2026-06-03 13:32:47 -07:00
Alex Holliday 3c85b36b1b add pagnation validation to queue query 2026-06-03 13:30:03 -07:00
Alex Holliday ee0bc5e500 add pagination components 2026-06-03 13:29:17 -07:00
Alex Holliday 4729863826 add tests 2026-06-03 13:29:03 -07:00
Alex Holliday 174fb048f7 implement pagination, update metrics to use scheduler's aggregation 2026-06-03 13:28:53 -07:00
Alex Holliday 63fd3f6afd bump scheduler 2026-06-03 13:28:25 -07:00
Alex Holliday c3944f2fe7 Add pagination to controller 2026-06-03 13:28:15 -07:00
Alexander Holliday ddd937d42f Merge pull request #3696 from bluewave-labs/fix/connection-reuse
fix: connection reuse
2026-06-03 11:54:33 -07:00
Alex Holliday 5e225f9173 update tests 2026-06-03 11:51:42 -07:00
Alex Holliday d2d110fcd5 reuse http agent, use firstByte instead of total 2026-06-03 11:48:59 -07:00
Alexander Holliday 724f30070d Merge pull request #3695 from ajhollid/fix/less-simple-queue-tweaks
fix: less simple queue tweaks
2026-06-03 11:07:24 -07:00
Alex Holliday b8a44f09fc update tests 2026-06-03 11:04:13 -07:00
Alex Holliday f85f1aa1d1 add processEVery 2026-06-03 11:02:56 -07:00
Alex Holliday 707b8f903b add bulk ops for inserting monitors, set primary concurrency to 0, handles scheduling only 2026-06-03 11:00:40 -07:00
Alex Holliday 9e4fd6d708 bump scheduler 2026-06-03 10:59:28 -07:00
Br0wnHammer 8572065ef7 fix(client): validate hardware alert thresholds on the wizard's second step 2026-06-03 20:16:10 +05:30
Br0wnHammer 01c3870b22 feat(client): enable step wizard on infrastructure and pagespeed create pages 2026-06-03 20:15:01 +05:30
Alexander Holliday 420c103f41 Merge pull request #3693 from bluewave-labs/feat/worker-stats
feat: worker stats
2026-06-02 11:33:44 -07:00
Alex Holliday b4970d321d update tests 2026-06-02 11:31:22 -07:00
Alex Holliday f5a4a03898 reorder stop 2026-06-02 11:31:11 -07:00
Alex Holliday dfae6c8abd type metrics for super simple queue 2026-06-02 11:26:10 -07:00
Alex Holliday 2b6734bb78 update tests 2026-06-02 11:25:21 -07:00
Alex Holliday b9fd29b906 add compliant worker ID to super simple queue 2026-06-02 11:11:24 -07:00
Alex Holliday caf8f22fec add worker table 2026-06-02 10:59:14 -07:00
Alex Holliday 599c599588 add tests 2026-06-02 09:47:13 -07:00
Alex Holliday cfed235d1f add worker stats 2026-06-02 09:47:00 -07:00
Alex Holliday c9eacff71f add queue worker type 2026-06-02 09:46:23 -07:00
Alex Holliday b2526a5be9 init queue worker repo 2026-06-02 09:45:56 -07:00
Alex Holliday cffe3f57fa add models 2026-06-02 09:45:37 -07:00
Alex Holliday 8b2506cc3d add queue worker repository 2026-06-02 09:44:49 -07:00
Alex Holliday 60356a95fe bump less-simple-scheduler 2026-06-02 09:44:23 -07:00
Alexander Holliday 1195567da8 Merge pull request #3692 from bluewave-labs/feat/fe/progress-indicator-status-page
Feat: Progress indicator status page
2026-06-02 09:19:32 -07:00
Br0wnHammer 7617929700 feat(status-page): add step-wizard progress indicator to create flow 2026-06-02 20:31:01 +05:30
Br0wnHammer 7df3ee6ecd feat(status-page): add wizard step metadata and helpers to validation schema 2026-06-02 20:27:12 +05:30
Alexander Holliday dc788dbf85 Merge pull request #3691 from bluewave-labs/feat/workers
feat: workers
2026-06-01 11:24:11 -07:00
Alex Holliday 89c4f4005e add lessSimpleQueue tests 2026-06-01 11:21:49 -07:00
Alex Holliday 53ed3b3465 remove unused imports 2026-06-01 11:17:50 -07:00
Alex Holliday e37fbf2a5c replace empty monitor check 2026-06-01 11:11:36 -07:00
Alex Holliday 4df08bfa79 add worker scripts 2026-06-01 10:45:20 -07:00
Alex Holliday f74231cf33 early return if worker mode 2026-06-01 10:45:09 -07:00
Alex Holliday e1558d35b0 use queue type on config 2026-06-01 10:44:53 -07:00
Alex Holliday 51672890df add queue type to less simple queue 2026-06-01 10:44:39 -07:00
Alex Holliday fc357fe3ae add queue mode to env validation 2026-06-01 10:44:18 -07:00
Alex Holliday 1a0e6d9404 add queue mode type 2026-06-01 10:44:05 -07:00
Alexander Holliday b9e8a6dbc8 Merge pull request #3689 from bluewave-labs/fix/listener-leak
fix: move listener registration to constructor
2026-05-29 11:51:27 -07:00
Alex Holliday c704f67e57 move listener registration to constructor 2026-05-29 11:45:17 -07:00
Alexander Holliday 4629a947a4 Merge pull request #3688 from bluewave-labs/feat/less-simple-scheduler
feat: Configurable Schedulers
2026-05-29 11:41:32 -07:00
Alex Holliday da92b9d067 update tests 2026-05-29 11:33:31 -07:00
Alex Holliday 458442f364 fix env sample, fix log queue, use dbType for enum 2026-05-29 11:31:11 -07:00
Alex Holliday 400b295dae Merge branch 'develop' into feat/less-simple-scheduler 2026-05-29 11:24:51 -07:00
Alex Holliday 6ddee6e85b fix imports 2026-05-29 11:23:31 -07:00
Alex Holliday 2db88ac192 add lastRunAt back 2026-05-29 11:20:19 -07:00
Alex Holliday 0342870bbd lastRunAt -> lastFinishedAT 2026-05-29 11:08:32 -07:00
Alex Holliday 4280a6c2c3 update env vars 2026-05-29 10:34:42 -07:00
Alex Holliday 0a3232419a update prettierrc 2026-05-29 10:33:15 -07:00
Alex Holliday 4dc3a756a8 add less-simple-scheduler, bump dependencies 2026-05-29 10:32:58 -07:00
Alex Holliday cd91e83817 Remove old queues 2026-05-29 10:28:59 -07:00
Alex Holliday f476710c29 add queue type to settings service 2026-05-29 10:28:49 -07:00
Alex Holliday 0d255c8962 update imports 2026-05-29 10:28:18 -07:00
Alex Holliday bf25a65d8e add configurable queue selection to service config, default to in memory 2026-05-29 10:28:01 -07:00
Alex Holliday f1e16b65ee Extract job queue interfaces, add less simple queue 2026-05-29 10:27:38 -07:00
Alex Holliday 201ad987ce Add validation for Queue type 2026-05-29 10:27:03 -07:00
Alex Holliday c12ce2fbcf add queue types 2026-05-29 10:26:46 -07:00
Alexander Holliday 6e77738ecf Merge pull request #3687 from bluewave-labs/fix/readme-translations
docs: add README translations for all supported locales
2026-05-29 09:17:42 -07:00
gorkem-bwl cbd1153189 docs(readme): polish German translation — informal 'du', tighter phrasing, fewer AI tells 2026-05-29 12:11:56 -04:00
gorkem-bwl 88042e7c50 docs(readme): add machine-translation disclaimer to 7 less-confident translations
For ar, ca, cs, fi, th, uk, vi the AI-generated translation quality
is hardest for me to vouch for. Added a clear "machine-translated
draft — native-speaker PRs welcome" notice at the top of each so
the community can fix them. The note appears below the language
selector for visibility.
2026-05-29 12:09:40 -04:00
gorkem-bwl ba44985763 docs(readme): fix TOC anchors and refresh Spanish translation
## Anchor fix
GitHub auto-generates anchor IDs from the rendered heading text, so
the English TOC links (#users-guide, #installation, etc.) in the
translated READMEs were broken — the matching anchor IDs only existed
in the English README. Added hidden <a id="..."></a> markers before
each section heading in all 17 READMEs (root + 16 translations) so
the TOC works in every language.

## Spanish refresh
The existing Spanish README was a human translation of an older
revision of the project README — it carried its own Traducciones and
Patrocinadores sections, an outdated demo URL, and a different
feature list. Rewrote it from the current English README structure
while keeping a fluent Spanish tone.
2026-05-29 11:49:24 -04:00
gorkem-bwl b164fa63bf docs(readme): add README translations for all supported locales
Mirrors the Graphify pattern: a centered flag + native-language link
bar at the top of every README points to the rest. All translated
files live in docs/translations/ so the repo root stays clean.

## Coverage
- Added 15 new translations: ar, ca, cs, de, fi, fr, ja, pt-BR, ru,
  th, tr, uk, vi, zh-CN, zh-TW
- Moved README.es.md from the repo root to docs/translations/
- Updated the root README to include the language selector

The 17 languages here match the locales the Checkmate UI already
ships translations for, so README parity is consistent with what a
user sees inside the app.

## Quality note
Translations were drafted as a starting point and may need
native-speaker review for tone and idiom — especially Arabic, Thai,
the two Chinese variants, Japanese, and Vietnamese. Technical terms
(Docker, MongoDB, Redis, Webhook, etc.), URLs, contributor names,
and badge URLs are intentionally left as-is.
2026-05-29 11:09:05 -04:00
Alexander Holliday 7630491ad4 Merge pull request #3673 from bluewave-labs/feat/fe/uptime-progress-indicator
Feat: Uptime Progress Indicator
2026-05-28 09:13:40 -07:00
Alex Holliday 09be4cdf9a fix import, move cast 2026-05-28 09:10:11 -07:00
Br0wnHammer 8360c05448 refactor(client): derive wizard step count from Zod field metadata 2026-05-28 14:20:02 +05:30
Br0wnHammer 3cce1aa842 feat(client): split uptime monitor creation into a 3-step wizard 2026-05-28 00:21:02 +05:30
joaquimpeixoto 8a83bc99e9 feat: add ntfy notification support 2026-05-27 16:57:11 +01:00
Br0wnHammer 37dfc67e33 feat(client): split uptime monitor creation into a 3-step wizard 2026-05-27 12:07:07 +05:30
Alexander Holliday 4a939ad2c4 Merge pull request #3677 from bluewave-labs/feat/bump-concurrency
feat: bump concurrency
2026-05-26 15:48:43 -07:00
Alex Holliday 56ee8ef5ea bump concurrency to 50 2026-05-26 15:45:49 -07:00
Alexander Holliday 8dd13c4b75 Merge pull request #3676 from bluewave-labs/feat/mongo-diagnostics
reset page index
2026-05-26 15:14:17 -07:00
Alex Holliday 6b5c3bba47 reset page index 2026-05-26 15:13:36 -07:00
Alexander Holliday 02998ed06e Merge pull request #3675 from bluewave-labs/feat/mongo-diagnostics
Feat/mongo diagnostics
2026-05-26 15:13:10 -07:00
Alex Holliday 397541a789 extract strings 2026-05-26 15:09:33 -07:00
Alex Holliday 949beff4f9 add mongo collection stats component, reorder layout 2026-05-26 14:55:22 -07:00
Alex Holliday 806c7764b1 add slotprops prop to tooltip 2026-05-26 14:54:54 -07:00
Alex Holliday d18a53eec2 add forward ref to gauage for tooltip 2026-05-26 14:54:30 -07:00
Alex Holliday f4b14e7df9 update clinet side diagnostics type 2026-05-26 14:53:56 -07:00
Alex Holliday e834402672 tighten server types 2026-05-26 14:53:33 -07:00
Alex Holliday 290adf024c add mongo stats 2026-05-26 13:32:48 -07:00
Alex Holliday 2c89086b68 add mongo stats component 2026-05-26 13:32:30 -07:00
Alex Holliday 20167d537b update diagnostics type 2026-05-26 13:32:05 -07:00
Alex Holliday bf43632482 add mongo types 2026-05-26 13:31:44 -07:00
Alex Holliday 2d432e0a3d add mongo stats to diagnostic service 2026-05-26 13:31:13 -07:00
Br0wnHammer 967e415b0a feat(client): split uptime monitor creation into a 3-step wizard 2026-05-27 01:07:48 +05:30
Alexander Holliday 2764468e90 Merge pull request #3674 from bluewave-labs/chore/dependency-bump
chore: bump dependencies
2026-05-26 09:58:22 -07:00
Alex Holliday 10d0835eb3 bump dependencies 2026-05-26 09:56:35 -07:00
Br0wnHammer 1702fd1c5a fix(ui): react button dom 2026-05-25 23:41:55 +05:30
Br0wnHammer c98ec7d047 feat(client): split uptime monitor creation into a 3-step wizard 2026-05-25 23:35:51 +05:30
Br0wnHammer 9ffe0caed9 feat(client): add StepProgress segmented progress bar component 2026-05-25 23:31:02 +05:30
Alexander Holliday ef8a8016df Merge pull request #3672 from bluewave-labs/fix/tag-apperance-column
fix: add centering box for Tag labels
2026-05-25 10:42:43 -07:00
Alex Holliday 3a1a9bde4d add centering box 2026-05-25 10:40:22 -07:00
Alexander Holliday 0e24bb7c94 Merge pull request #3671 from bluewave-labs/fix/tests
fix: add tests for mw and monitor services
2026-05-25 09:59:24 -07:00
Alex Holliday a415c75eb4 add tests for mw and monitor services 2026-05-25 09:57:34 -07:00
Alexander Holliday d1f46aba9e Merge pull request #3669 from harsh-aghara/fix/checkbox-indeterminate-size
Add indeterminate icon to Checkbox component for consistency
2026-05-25 09:12:39 -07:00
Alexander Holliday bb60d22dea Merge pull request #3670 from harsh-aghara/fix/bulk-pause-toast-partial-failure
Expose failedCount and surface partial failure warning in UI
2026-05-25 09:11:10 -07:00
harsh-aghara 0ff642cb24 fix(ui): surface partial failure warning toast for bulk actions 2026-05-23 18:47:55 +05:30
harsh-aghara a044d7b2c7 fix(monitors): expose failedCount in bulk pause API response 2026-05-23 17:53:49 +05:30
harsh-aghara 9cee2eebd0 fix(ui): add missing indeterminate icon to Checkbox component
The custom Checkbox used Lucide Square/SquareCheck for unchecked/checked
states but had no indeterminateIcon, so MUI fell back to its default 24px
Material icon. This caused a visible size mismatch and row height jitter
in the bulk-select header of monitor tables.

Add SquareMinus from lucide-react at the same size (16px) and strokeWidth
(1.5) as the existing icons, and extend the checked color rule to also
cover the indeterminate state.
2026-05-22 23:28:42 +05:30
Alexander Holliday bec5ef09cb Merge pull request #3668 from bluewave-labs/fix/update-monitors-on-maintenance-window-change
fix: update monitors on maintenance window change
2026-05-21 11:18:59 -07:00
Alex Holliday 3abacb046b handle paused status 2026-05-21 11:02:59 -07:00
Alex Holliday fbfb861447 flip status on create 2026-05-21 10:50:53 -07:00
Alex Holliday 0470eab782 extract flipping monitor status, flip status on delete 2026-05-21 10:27:46 -07:00
Alex Holliday abf66eed95 add logic for entering/leaving maintenance windows, add tests 2026-05-21 10:23:27 -07:00
Alex Holliday 4f571a258c update scheduler to use extacted method 2026-05-21 10:02:56 -07:00
Alex Holliday 85196852f4 extract active maintenance window check 2026-05-21 10:02:39 -07:00
Alex Holliday 46c80985ec add findByIds to MW repository 2026-05-21 09:59:42 -07:00
Alex Holliday 8c7b691fda add updatebyids to monitor repository 2026-05-21 09:59:23 -07:00
Alexander Holliday d5dac6b557 Merge pull request #3663 from bluewave-labs/feat/fe-tags
Feat/fe tags
2026-05-20 12:29:02 -07:00
Alexander Holliday f0129f257a Merge pull request #3664 from bluewave-labs/fix/monitor-summary-with-tags
fix: monitor summary with tags
2026-05-20 12:28:46 -07:00
Alex Holliday 4b9f1ba43b add tags to summary query 2026-05-20 12:25:36 -07:00
Alex Holliday 1322033d41 add tags to infra monitors 2026-05-20 12:18:17 -07:00
Alex Holliday 7c201d087c add tag filtering to pagespeed 2026-05-20 12:14:22 -07:00
Alex Holliday 27e34f3b95 make filters optional 2026-05-20 12:14:09 -07:00
Alex Holliday 130d825d66 add active filters list 2026-05-20 12:02:45 -07:00
Alex Holliday c6d82223f9 add tags to filters 2026-05-20 12:02:32 -07:00
Alex Holliday 3bcdd3c445 remove chip referecnes, update strings 2026-05-20 11:39:57 -07:00
Alex Holliday 3f290bc963 add tags to Uptime montitor table 2026-05-20 11:37:03 -07:00
Alex Holliday 7d7926fab9 add tags to validation 2026-05-20 11:36:46 -07:00
Alex Holliday a09a885786 add tags to monitor type 2026-05-20 11:36:40 -07:00
Alex Holliday 76953f9a5c add tags to monitor forms 2026-05-20 11:36:23 -07:00
Alex Holliday 67bba1479c remove chip, add coloredlabel, add render options to autocomplete 2026-05-20 11:36:03 -07:00
Alex Holliday b981427d8d mutate SWR on success 2026-05-20 10:49:16 -07:00
Alex Holliday f37ae8fddb add menu string 2026-05-20 10:45:55 -07:00
Alex Holliday dc80b3cafc add tag to table 2026-05-20 10:45:45 -07:00
Alex Holliday 896d1b784b add a colored label 2026-05-20 10:45:36 -07:00
Alex Holliday 02640c3155 add routes 2026-05-20 10:29:25 -07:00
Alex Holliday 5a187cd1c9 add new chip component 2026-05-20 10:29:13 -07:00
Alex Holliday e6a0f89c68 add strings 2026-05-20 10:29:06 -07:00
Alex Holliday e96de364c0 add form hook for tags 2026-05-20 10:28:55 -07:00
Alex Holliday 8aa55885e5 add tags validaiton 2026-05-20 10:28:39 -07:00
Alex Holliday 1f7344f2e6 add Tags pages 2026-05-20 10:28:26 -07:00
Alex Holliday fe74d4b465 add tag type 2026-05-20 10:28:07 -07:00
Alexander Holliday 9c62dc9c52 Merge pull request #3661 from bluewave-labs/feat/monitor-tags
feat: monitor tags
2026-05-20 09:20:18 -07:00
Alex Holliday 2a2f101d4b add tests and validation 2026-05-19 14:45:42 -07:00
Alex Holliday 76ce362eea add tags to monitor controller 2026-05-19 14:45:29 -07:00
Alex Holliday 029f732a86 add tags to monitor service 2026-05-19 14:44:56 -07:00
Alex Holliday a7abbd60bd add tags to monitors repo 2026-05-19 14:44:18 -07:00
Alex Holliday 9b84b98391 add tests 2026-05-19 11:16:25 -07:00
Alex Holliday 6c2e3b4fe1 init tags 2026-05-19 11:16:16 -07:00
Alex Holliday 57acbd3497 add validations 2026-05-19 11:16:03 -07:00
Alex Holliday 48735c7dc8 add tag service 2026-05-19 11:15:54 -07:00
Alex Holliday 451c752170 add tags controller and routes 2026-05-19 11:15:42 -07:00
Alex Holliday b1871b31a5 create tags repository, update related repositories 2026-05-19 11:15:16 -07:00
Alex Holliday a740a9e005 update DB models, create tag db model 2026-05-19 11:14:55 -07:00
Alex Holliday 43ff7ca4bd create new tag type, update related types 2026-05-19 11:14:31 -07:00
Alexander Holliday 01a53298c2 Merge pull request #3655 from bluewave-labs/fix/chart-size
fix: width for stats size, noWrap for stats
2026-05-15 11:30:08 -07:00
Alex Holliday 3de392c3c9 fix width for stats size, noWrap for stats 2026-05-15 11:27:48 -07:00
Alexander Holliday d1b5830c3d Merge pull request #3654 from bluewave-labs/fix/set-password
add password and newPassword to editUserValidation
2026-05-15 11:16:34 -07:00
Alex Holliday 704f25b987 extract password validation, add regression test 2026-05-15 11:14:39 -07:00
Alex Holliday f46c2edd1b add password and newPassword to editUserValidation 2026-05-15 10:58:38 -07:00
Alexander Holliday 9d7523c406 Merge pull request #3653 from bluewave-labs/fix/locale-gaps
fix(i18n): close translation gaps across 16 locales
2026-05-15 10:46:57 -07:00
gorkem-bwl bc7b3a5ffb fix(i18n): close translation gaps across 16 locales
## Changes
- Add 59 missing keys to every non-en locale (DNS monitor strings,
  status-page themes, monitor status labels, network error messages,
  email sender display name)
- Catalan: add 12 bulk-monitor selection keys that were introduced after
  ca.json was first added
- Fill 188 empty string values across ar, cs, fi, fr, ja, pt-BR, tr,
  uk and vi
- Remove 10 orphan keys from es.json that were not referenced anywhere
  in the client code

## Benefits
- All 16 locales now report 100% structural coverage against en.json
- Status page theme picker, DNS monitor type, and bulk monitor actions
  are fully translated
- No more empty strings rendered as blank labels in production UI
- es.json no longer carries dead keys diverging from the reference

## Notes
- Placeholders ({{count}}, {{value}}, {{name}}) verified intact in
  every locale
- Brand terms (DNS, A, AAAA, CNAME, MX, NS, TXT, 8.8.8.8) intentionally
  kept untranslated
- Build, prettier format-check and JSON validity all pass
2026-05-14 07:36:37 -04:00
162 changed files with 11690 additions and 2007 deletions

No files matched your search

+1
View File
@@ -151,6 +151,7 @@ LOG_LEVEL="debug"
- `ORIGIN`: Origin for CORS purposes
- `TOKEN_TTL`: Token time to live (in vercel/ms format)
- `LOG_LEVEL`: Debug level (debug, info, warn, error)
- `QUEUE_TYPE`: Job queue implementation (`superSimpleQueue` or `lessSimpleQueue`, optional, default: `superSimpleQueue`)
Start the backend server:
-172
View File
@@ -1,172 +0,0 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![OpenSSF Mejores Prácticas](https://www.bestpractices.dev/projects/9901/badge)](https://www.bestpractices.dev/projects/9901)
[![Pregunta en DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Una aplicación de código abierto para monitoreo de infraestructura y tiempo de actividad</strong></p>
<img width="1660" alt="image" src="https://github.com/user-attachments/assets/b748f36d-a271-4965-ad0a-18bf153bbee7" />
Este repositorio contiene tanto el frontend como el backend de Checkmate, una herramienta de monitoreo de código abierto y autoalojada para rastrear hardware de servidores, tiempo de actividad, tiempos de respuesta e incidentes en tiempo real con visualizaciones atractivas. Checkmate revisa regularmente si un servidor o sitio web es accesible y funciona de manera óptima, proporcionando alertas e informes en tiempo real sobre la disponibilidad, el tiempo de inactividad y el tiempo de respuesta de los servicios monitoreados.
Checkmate también tiene un agente llamado [Capture](https://github.com/bluewave-labs/capture), para recuperar datos de servidores remotos. Aunque Capture no es obligatorio para ejecutar Checkmate, proporciona información adicional sobre el estado de la CPU, RAM, disco y temperatura de tus servidores.
Checkmate ha sido probado con más de 1000 monitores activos sin problemas ni cuellos de botella de rendimiento.
**Si deseas patrocinar una función, [visita este enlace](https://checkmate.so/sponsored-features).**
## 📚 Tabla de contenidos
- [📦 Demo](#demo)
- [🔗 Guía del usuario](#guía-del-usuario)
- [🛠️ Instalación](#instalación)
- [🏁 Traducciones](#traducciones)
- [🚀 Rendimiento](#rendimiento)
- [💚 Preguntas e ideas](#preguntas-e-ideas)
- [🧩 Características](#características)
- [🏗️ Capturas de pantalla](#capturas-de-pantalla)
- [🏗️ Tecnologías](#tecnologías)
- [🔗 Enlaces útiles](#enlaces-útiles)
- [🤝 Contribuciones](#contribuciones)
- [💰 Patrocinadores](#patrocinadores)
...
**[Texto truncado para mantener la longitud del mensaje manejable]**
## Demo
Puedes ver la última versión de [Checkmate](https://checkmate-demo.bluewavelabs.ca/) en acción. El usuario es uptimedemo@demo.com y la contraseña es Demouser1! (ten en cuenta que actualizamos el servidor de demostración de vez en cuando, así que si no funciona para ti, por favor contáctanos en el canal de Discusiones).
## Guía del usuario
Las instrucciones de uso se pueden encontrar [aquí](https://checkmate.so/docs). Todavía está en desarrollo y parte de la información puede estar desactualizada ya que continuamente añadimos funciones cada semana. ¡Ten por seguro que estamos haciendo lo mejor posible! :)
## Instalación
Consulta las instrucciones de instalación en el [portal de documentación de Checkmate](https://checkmate.so/docs/getting-started/installation).
Alternativamente, también puedes usar [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md) o [Pikapods](https://www.pikapods.com/) para desplegar rápidamente una instancia de Checkmate. Si deseas monitorear tu infraestructura de servidores, necesitarás el agente [Capture](https://github.com/bluewave-labs/capture). El repositorio de Capture también contiene las instrucciones de instalación.
## Traducciones
Si deseas usar Checkmate en tu idioma, por favor [ve a esta página](https://poeditor.com/join/project/lRUoGZFCsJ) y regístrate para el idioma al que te gustaría traducir Checkmate.
## Rendimiento
Gracias a extensas optimizaciones, Checkmate opera con un uso de memoria excepcionalmente bajo, requiriendo recursos mínimos de memoria y CPU. Aquí está el uso de memoria de una instancia de Node.js ejecutándose en un servidor que monitorea 323 servidores cada minuto:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
También puedes ver el consumo de memoria de MongoDB y Redis en el mismo servidor (398Mb y 15Mb) para la misma cantidad de servidores:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
## Preguntas e Ideas
Si tienes alguna pregunta, sugerencia o comentario, tienes varias opciones:
- [Canal de Discord](https://discord.gg/NAb6H3UTjK)
- [Discusiones en GitHub](https://github.com/bluewave-labs/checkmate/discussions)
- [Grupo en Reddit](https://www.reddit.com/r/CheckmateMonitoring/)
¡No dudes en hacer preguntas o compartir tus ideas, nos encantaría saber de ti!
## Características
- Completamente de código abierto, desplegable en tus propios servidores o dispositivos personales (por ejemplo, Raspberry Pi 4 o 5)
- Monitoreo de sitios web
- Monitoreo de velocidad de carga
- Monitoreo de infraestructura (memoria, uso de disco, rendimiento de CPU, etc) - requiere el agente [Capture](https://github.com/bluewave-labs/capture)
- Monitoreo de Docker
- Monitoreo con ping
- Monitoreo de certificados SSL
- Monitoreo de puertos
- Incidentes en una sola vista
- Páginas de estado
- Notificaciones por correo, Webhooks, Discord, Telegram, Slack
- Mantenimiento programado
- Monitoreo de consultas JSON
- Soporte para múltiples idiomas
**Hoja de ruta a corto plazo:** ([Milestone 2.2](https://github.com/bluewave-labs/Checkmate/milestone/8))
- Mejores notificaciones
- Monitoreo de red
- ...y algunas funciones más
## Capturas de pantalla
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
## Tecnologías
- [ReactJs](https://react.dev/)
- [MUI (framework de React)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- ¡Y muchos otros componentes de código abierto!
## Enlaces útiles
- Si deseas apoyarnos, por favor considera darle una ⭐ y haz clic en "watch".
- ¿Tienes una pregunta o sugerencia para la hoja de ruta? Revisa nuestro [canal de Discord](https://discord.gg/NAb6H3UTjK) o el foro de [Discusiones](https://github.com/bluewave-labs/checkmate/discussions).
- ¿Quieres saber cuándo hay una nueva versión? Usa [Newreleases](https://newreleases.io/), un servicio gratuito para seguir lanzamientos.
- Mira un video de instalación y uso de Checkmate [aquí](https://www.youtube.com/watch?v=GfFOc0xHIwY)
## Contribuciones
Somos [Alex](http://github.com/ajhollid) (líder de equipo), [Vishnu](http://github.com/vishnusn77), [Mohadeseh](http://github.com/mohicody), [Gorkem](http://github.com/gorkem-bwl/), [Owaise](http://github.com/Owaiseimdad), [Aryaman](https://github.com/Br0wnHammer) y [Mert](https://github.com/mertssmnoglu), ayudando a personas y empresas a monitorear su infraestructura y servidores.
Nos enorgullecemos de construir conexiones fuertes con contribuyentes de todos los niveles. A pesar de ser un proyecto joven, Checkmate ya ha ganado más de 7000 estrellas y ha atraído a más de 90 contribuyentes de todo el mundo.
Nuestro repositorio ha sido marcado con estrella por empleados de **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes y NEC**, ¡así que no te detengas — participa, contribuye y aprende con nosotros!
Cómo contribuir:
0. Dale una estrella al repositorio :)
1. Revisa la [guía para contribuidores](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). Se anima a los nuevos a revisar las etiquetas `good-first-issue`.
2. Consulta la [estructura del proyecto](https://deepwiki.com/bluewave-labs/Checkmate#system-architecture) y la [visión general](https://deepwiki.com/bluewave-labs/Checkmate#overview).
3. Lee una estructura detallada de [Checkmate](https://deepwiki.com/bluewave-labs/Checkmate) si deseas profundizar en la arquitectura.
4. Abre un issue si crees que has encontrado un error.
5. Revisa los issues con la etiqueta `good-first-issue` si eres nuevo.
6. Haz un pull request para añadir nuevas funciones, mejoras o correcciones.
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Historial de estrellas](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/bluewave-uptime&Date)
## Patrocinadores
Gracias a [Gitbook](https://gitbook.io/) por darnos una cuenta gratuita para su plataforma de documentación, y a [Poeditor](https://poeditor.com/) por proporcionarnos una cuenta gratuita para servicios de traducción. Si deseas patrocinar Checkmate, por favor envía un correo a hello@bluewavelabs.ca
Si deseas patrocinar una función, [visita esta página](https://checkmate.so/sponsored-features).
También puedes revisar otros proyectos de BlueWave orientados a desarrolladores y contribuidores:
- [VerifyWise](https://github.com/bluewave-labs/verifywise), la primera plataforma de gobernanza de IA de código abierto.
- [DataRoom](https://github.com/bluewave-labs/bluewave-dataroom), una aplicación de intercambio de archivos seguro, también conocida como dataroom.
- [Guidefox](https://github.com/bluewave-labs/guidefox), una app que ayuda a nuevos usuarios a aprender a usar tu producto mediante pistas, tours, popups y banners.
+31 -1
View File
@@ -1,5 +1,25 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="README.md">English</a> |
🇸🇦 <a href="docs/translations/README.ar.md">العربية</a> |
🇪🇸 <a href="docs/translations/README.ca.md">Català</a> |
🇨🇿 <a href="docs/translations/README.cs.md">Čeština</a> |
🇩🇪 <a href="docs/translations/README.de.md">Deutsch</a> |
🇪🇸 <a href="docs/translations/README.es.md">Español</a> |
🇫🇮 <a href="docs/translations/README.fi.md">Suomi</a> |
🇫🇷 <a href="docs/translations/README.fr.md">Français</a> |
🇯🇵 <a href="docs/translations/README.ja.md">日本語</a> |
🇧🇷 <a href="docs/translations/README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="docs/translations/README.ru.md">Русский</a> |
🇹🇭 <a href="docs/translations/README.th.md">ไทย</a> |
🇹🇷 <a href="docs/translations/README.tr.md">Türkçe</a> |
🇺🇦 <a href="docs/translations/README.uk.md">Українська</a> |
🇻🇳 <a href="docs/translations/README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="docs/translations/README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="docs/translations/README.zh-TW.md">繁體中文</a>
</p>
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
@@ -40,12 +60,14 @@ Checkmate has been stress-tested with 1000+ active monitors without any particul
- [🤝 Contributing](#contributing)
<a id="demo"></a>
## Demo
You can see the latest build of [Checkmate](https://demo.checkmate.so/) in action.
The username is demouser@demo.com and the password is Demouser1! (just a note that we update the demo server from time to time, so if it doesn't work for you, please ping us on the Discussions channel).
<a id="users-guide"></a>
## User's guide
Usage instructions can be found [here](https://checkmate.so/docs).
@@ -54,6 +76,7 @@ Usage instructions can be found [here](https://checkmate.so/docs).
- [Docker](https://www.docker.com/) installed
- [Git](https://git-scm.com/) installed
<a id="installation"></a>
## Installation
See installation instructions in [Checkmate documentation portal](https://checkmate.so/docs).
@@ -66,6 +89,7 @@ If you need to monitor internal HTTPS endpoints with certificates from private C
For more documentation, see the [docs directory](./docs/).
<a id="performance"></a>
## Performance
Thanks to extensive optimizations, Checkmate operates with an exceptionally small memory footprint, requiring minimal memory and CPU resources. Here’s the memory usage of a Node.js instance running on a server that monitors 323 servers every minute:
@@ -76,6 +100,7 @@ You can see the memory footprint of MongoDB and Redis on the same server (398Mb
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Questions & Ideas
If you have any questions, suggestions or comments, you have several options:
@@ -85,6 +110,7 @@ If you have any questions, suggestions or comments, you have several options:
Feel free to ask questions or share your ideas - we'd love to hear from you!
<a id="features"></a>
## Features
- Completely open source, deployable on your servers or home devices (e.g Raspberry Pi 4 or 5)
@@ -109,6 +135,7 @@ Feel free to ask questions or share your ideas - we'd love to hear from you!
5. Upon a state change: an incident is either created or resolved, depending on the monitor's current status
6. Notifications are triggered based on configuration
<a id="screenshots"></a>
## Screenshots
<p>
@@ -126,6 +153,7 @@ Feel free to ask questions or share your ideas - we'd love to hear from you!
<a id="tech-stack"></a>
## Tech stack
- [ReactJs](https://react.dev/)
@@ -135,6 +163,7 @@ Feel free to ask questions or share your ideas - we'd love to hear from you!
- [Recharts](https://recharts.org)
- Lots of other open source components!
<a id="a-few-links"></a>
## A few links
- If you would like to support us, please consider giving it a ⭐ and click on "watch".
@@ -142,6 +171,7 @@ Feel free to ask questions or share your ideas - we'd love to hear from you!
- Need a ping when there's a new release? Use [Newreleases](https://newreleases.io/), a free service to track releases.
- Watch a Checkmate [installation and usage video](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## Contributing
We are [Alex](http://github.com/ajhollid) (team lead), [Gorkem](http://github.com/gorkem-bwl/), [Aryaman](https://github.com/Br0wnHammer), [Mert](https://github.com/mertssmnoglu) and [Karen](https://github.com/karenvicent) helping individuals and businesses monitor their infra and servers.
+236 -4
View File
@@ -1,12 +1,12 @@
{
"name": "client",
"version": "3.7.1",
"version": "3.8.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "client",
"version": "3.7.1",
"version": "3.8.1",
"dependencies": {
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
@@ -57,6 +57,7 @@
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.6",
"lint-staged": "^16.2.7",
"mongoose": "9.6.2",
"prettier": "^3.3.3",
"typescript": "5.9.3",
"vite": "^6.4.2"
@@ -84,6 +85,7 @@
"node_modules/@babel/core": {
"version": "7.29.0",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -354,6 +356,7 @@
"node_modules/@emotion/react": {
"version": "11.14.0",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.13.5",
@@ -391,6 +394,7 @@
"node_modules/@emotion/styled": {
"version": "11.14.1",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.18.3",
"@emotion/babel-plugin": "^11.13.5",
@@ -1107,6 +1111,16 @@
"supercluster": "^8.0.1"
}
},
"node_modules/@mongodb-js/saslprep": {
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.4.11.tgz",
"integrity": "sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA==",
"dev": true,
"license": "MIT",
"dependencies": {
"sparse-bitfield": "^3.0.3"
}
},
"node_modules/@mui/core-downloads-tracker": {
"version": "7.3.7",
"license": "MIT",
@@ -1160,6 +1174,7 @@
"node_modules/@mui/material": {
"version": "7.3.7",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.28.4",
"@mui/core-downloads-tracker": "^7.3.7",
@@ -1264,6 +1279,7 @@
"node_modules/@mui/system": {
"version": "7.3.7",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.28.4",
"@mui/private-theming": "^7.3.7",
@@ -2304,6 +2320,7 @@
"version": "24.5.2",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.12.0"
}
@@ -2319,6 +2336,7 @@
"node_modules/@types/react": {
"version": "18.3.27",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.2.2"
@@ -2352,6 +2370,23 @@
"version": "0.0.6",
"license": "MIT"
},
"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==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/whatwg-url": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz",
"integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/webidl-conversions": "*"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "7.18.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz",
@@ -2392,6 +2427,7 @@
"integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==",
"dev": true,
"license": "BSD-2-Clause",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "7.18.0",
"@typescript-eslint/types": "7.18.0",
@@ -2672,6 +2708,7 @@
"version": "8.15.0",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -3006,6 +3043,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -3020,6 +3058,16 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/bson": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz",
"integrity": "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/bytewise": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz",
@@ -3437,7 +3485,8 @@
},
"node_modules/dayjs": {
"version": "1.11.13",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/debug": {
"version": "4.4.3",
@@ -3832,6 +3881,7 @@
"version": "8.57.1",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
@@ -4627,6 +4677,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/runtime": "^7.27.6"
},
@@ -5222,6 +5273,16 @@
"node": ">=4.0"
}
},
"node_modules/kareem": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-3.3.0.tgz",
"integrity": "sha512-kpSuLD3/7RenBnjnJdOHXCKC8dTd1JzeOiJhN0necWWci6cC+qX+VuwPnMVgb+a4+KNJSfgqahpnfWaeDXCimw==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/kdbush": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz",
@@ -5412,6 +5473,7 @@
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.19.0.tgz",
"integrity": "sha512-REhYUN8gNP3HlcIZS6QU2uy8iovl31cXsrNDkCcqWSQbCkcpdYLczqDz5PVIwNH42UQNyvukjes/RoHPDrOUmQ==",
"license": "BSD-3-Clause",
"peer": true,
"dependencies": {
"@mapbox/geojson-rewind": "^0.5.2",
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
@@ -5451,6 +5513,13 @@
"node": ">= 0.4"
}
},
"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==",
"dev": true,
"license": "MIT"
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -5540,6 +5609,109 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/mongodb": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.2.0.tgz",
"integrity": "sha512-F/2+BMZtLVhY30ioZp0dAmZ+IRZMBqI+nrv6t5+9/1AIwCa8sMRC3jBf81lpxMhnZgqq8CoUD503Z1oZWq1/sw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@mongodb-js/saslprep": "^1.3.0",
"bson": "^7.2.0",
"mongodb-connection-string-url": "^7.0.0"
},
"engines": {
"node": ">=20.19.0"
},
"peerDependencies": {
"@aws-sdk/credential-providers": "^3.806.0",
"@mongodb-js/zstd": "^7.0.0",
"gcp-metadata": "^7.0.1",
"kerberos": "^7.0.0",
"mongodb-client-encryption": ">=7.0.0 <7.1.0",
"snappy": "^7.3.2",
"socks": "^2.8.6"
},
"peerDependenciesMeta": {
"@aws-sdk/credential-providers": {
"optional": true
},
"@mongodb-js/zstd": {
"optional": true
},
"gcp-metadata": {
"optional": true
},
"kerberos": {
"optional": true
},
"mongodb-client-encryption": {
"optional": true
},
"snappy": {
"optional": true
},
"socks": {
"optional": true
}
}
},
"node_modules/mongodb-connection-string-url": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz",
"integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@types/whatwg-url": "^13.0.0",
"whatwg-url": "^14.1.0"
},
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/mongoose": {
"version": "9.6.2",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.6.2.tgz",
"integrity": "sha512-7m8HntjkoRnwEmuPC0kdlwcZXJOQf4twumFj+PNzg/anqqZE2Er7hQslqyzy07mP3JcFjoTSgH5765PyqOXsxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"kareem": "3.3.0",
"mongodb": "~7.2",
"mpath": "0.9.0",
"mquery": "6.0.0",
"ms": "2.1.3",
"sift": "17.1.3"
},
"engines": {
"node": ">=20.19.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mongoose"
}
},
"node_modules/mpath": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
"integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/mquery": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz",
"integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/ms": {
"version": "2.1.3",
"license": "MIT"
@@ -6078,6 +6250,7 @@
"node_modules/react-dom": {
"version": "18.3.1",
"license": "MIT",
"peer": true,
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
@@ -6089,6 +6262,7 @@
"node_modules/react-hook-form": {
"version": "7.71.1",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18.0.0"
},
@@ -6288,7 +6462,8 @@
},
"node_modules/redux": {
"version": "5.0.1",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/redux-persist": {
"version": "6.0.0",
@@ -6438,6 +6613,7 @@
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
"integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -6713,6 +6889,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sift": {
"version": "17.1.3",
"resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz",
"integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==",
"dev": true,
"license": "MIT"
},
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
@@ -6823,6 +7006,16 @@
"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==",
"dev": true,
"license": "MIT",
"dependencies": {
"memory-pager": "^1.0.2"
}
},
"node_modules/split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -7136,6 +7329,19 @@
"node": ">=8.0"
}
},
"node_modules/tr46": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
"integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
"dev": true,
"license": "MIT",
"dependencies": {
"punycode": "^2.3.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/ts-api-utils": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz",
@@ -7249,6 +7455,7 @@
"version": "5.9.3",
"devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -7384,6 +7591,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz",
"integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
@@ -7472,6 +7680,30 @@
"node": ">=0.10.0"
}
},
"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==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
}
},
"node_modules/whatwg-url": {
"version": "14.2.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
"integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"tr46": "^5.1.0",
"webidl-conversions": "^7.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/which": {
"version": "2.0.2",
"dev": true,
+2 -1
View File
@@ -1,7 +1,7 @@
{
"name": "client",
"private": true,
"version": "3.7.1",
"version": "3.8.1",
"type": "module",
"scripts": {
"dev": "vite",
@@ -66,6 +66,7 @@
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.6",
"lint-staged": "^16.2.7",
"mongoose": "9.6.2",
"prettier": "^3.3.3",
"typescript": "5.9.3",
"vite": "^6.4.2"
@@ -35,7 +35,7 @@ const calculateResponseTimeStats = (checks: CheckSnapshot[]): ResponseTimeStats
}
const responseTimes = validChecks.map((check) => check.originalResponseTime);
const max = Math.max(...responseTimes);
const max = Math.round(Math.max(...responseTimes));
const avg = Math.round(
responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length
);
@@ -73,14 +73,20 @@ export const HistogramResponseTime = ({
<Stack
justifyContent="center"
alignItems={statsPosition === "left" ? "flex-end" : "flex-start"}
minWidth={70}
width={110}
pr={statsPosition === "left" ? theme.spacing(8) : 0}
pl={statsPosition === "right" ? theme.spacing(8) : 0}
>
<Typography variant="body2">
<Typography
variant="body2"
noWrap
>
{t("common.charts.histogram.avg", { value: stats.avg })}
</Typography>
<Typography variant="body2">
<Typography
variant="body2"
noWrap
>
{t("common.charts.histogram.max", { value: stats.max })}
</Typography>
</Stack>
+21 -13
View File
@@ -1,30 +1,36 @@
import { BaseChart } from "@/Components/design-elements";
import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
import Box, { type BoxProps } from "@mui/material/Box";
import Typography from "@mui/material/Typography";
import { useTheme } from "@mui/material/styles";
import { useMemo, useState, useEffect } from "react";
import { forwardRef, useMemo, useState, useEffect } from "react";
import { getInfraGaugeColor } from "@/Utils/MonitorUtils";
const MINIMUM_VALUE = 0;
const MAXIMUM_VALUE = 100;
export const Gauge = ({
isLoading = false,
progress = 0,
radius = 70,
strokeWidth = 15,
precision = 1,
unit = "%",
}: {
interface GaugeProps extends Omit<BoxProps, "color"> {
isLoading?: boolean;
progress?: number;
radius?: number;
strokeWidth?: number;
precision?: number;
unit?: string;
}) => {
}
export const Gauge = forwardRef<HTMLDivElement, GaugeProps>(function Gauge(
{
isLoading = false,
progress = 0,
radius = 70,
strokeWidth = 15,
precision = 1,
unit = "%",
...rest
},
ref
) {
const theme = useTheme();
const progressWithinRange = Math.max(MINIMUM_VALUE, Math.min(progress, MAXIMUM_VALUE));
@@ -51,17 +57,19 @@ export const Gauge = ({
const fillColor = getInfraGaugeColor(progressWithinRange, theme);
if (isLoading) {
return;
return null;
}
return (
<Box
ref={ref}
display={"inline-block"}
position={"relative"}
width={radius}
height={radius}
bgcolor={theme.palette.background.paper}
borderRadius={"50%"}
{...rest}
>
<svg
viewBox={`0 0 ${totalSize} ${totalSize}`}
@@ -105,7 +113,7 @@ export const Gauge = ({
</Typography>
</Box>
);
};
});
export const DetailGauge = ({
title,
@@ -93,3 +93,20 @@ export const ValueLabel = ({ value, text }: { value: ValueType; text: string })
</BaseBox>
);
};
export const ColoredLabel = ({ text, color }: { text: string; color: string }) => {
const theme = useTheme();
return (
<BaseBox
sx={{
width: "fit-content",
padding: theme.spacing(1, 3),
color: color,
borderColor: color,
}}
>
<Typography>{text}</Typography>
</BaseBox>
);
};
@@ -0,0 +1,42 @@
import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
import { useTheme, alpha } from "@mui/material/styles";
import { LAYOUT } from "@/Utils/Theme/constants";
interface StepProgressProps {
steps: number;
current: number;
}
export const StepProgress = ({ steps, current }: StepProgressProps) => {
const theme = useTheme();
const activeBg = `linear-gradient(135deg, ${theme.palette.primary.light} 0%, ${theme.palette.primary.main} 100%)`;
const completedBg = `linear-gradient(135deg, ${alpha(theme.palette.primary.light, 0.5)} 0%, ${alpha(theme.palette.primary.main, 0.5)} 100%)`;
const getSegmentBackground = (index: number) => {
if (index < current) return completedBg;
if (index === current) return activeBg;
return theme.palette.divider;
};
return (
<Stack
direction="row"
gap={theme.spacing(LAYOUT.XS)}
width="100%"
>
{Array.from({ length: steps }).map((_, index) => (
<Box
key={index}
flex={1}
height={theme.spacing(LAYOUT.XXS)}
borderRadius={theme.shape.borderRadius}
sx={{
background: getSegmentBackground(index),
transition: "background 0.2s ease",
}}
/>
))}
</Stack>
);
};
@@ -6,7 +6,11 @@ import type { TooltipProps } from "@mui/material/Tooltip";
type StyledTooltipProps = TooltipProps;
export const Tooltip = ({ placement = "top", ...props }: StyledTooltipProps) => {
export const Tooltip = ({
placement = "top",
slotProps,
...props
}: StyledTooltipProps) => {
const theme = useTheme();
return (
@@ -27,6 +31,7 @@ export const Tooltip = ({ placement = "top", ...props }: StyledTooltipProps) =>
boxShadow: theme.shadows[6],
},
},
...slotProps,
}}
>
{props.children}
@@ -27,3 +27,4 @@ export * from "./OfflineBanner";
export * from "./Avatar";
export * from "./StatusCodeLabel";
export * from "./StrategyBadge";
export * from "./StepProgress";
+25 -19
View File
@@ -14,17 +14,19 @@ type AutoCompleteInputProps = Omit<
renderInput?: AutocompleteProps<any, boolean, boolean, boolean>["renderInput"];
fieldLabel?: string;
required?: boolean;
renderOptionContent?: (option: any) => React.ReactNode;
};
export const AutoCompleteInput = ({
fieldLabel,
required,
renderInput,
renderOptionContent,
...props
}: AutoCompleteInputProps) => {
const theme = useTheme();
const multiple = props.multiple;
const { renderOption: customRenderOption, ...autocompleteProps } = props;
const defaultRenderInput = (params: any) => (
<TextField
{...params}
@@ -34,7 +36,7 @@ export const AutoCompleteInput = ({
const autocomplete = (
<Autocomplete
{...props}
{...autocompleteProps}
disableCloseOnSelect={!!multiple}
popupIcon={
<ChevronDown
@@ -46,24 +48,28 @@ export const AutoCompleteInput = ({
renderInput={renderInput || defaultRenderInput}
getOptionKey={(option) => option.id}
renderTags={() => null}
renderOption={(props, option, { selected }) => {
const { key, ...optionProps } = props;
return (
<ListItem
key={key}
{...optionProps}
>
<Stack
direction={"row"}
alignItems={"center"}
gap={theme.spacing(2)}
renderOption={
customRenderOption ||
((props, option, { selected }) => {
const { key, ...optionProps } = props;
return (
<ListItem
key={key}
{...optionProps}
>
{multiple && <Checkbox checked={selected} />}
{option.name}
</Stack>
</ListItem>
);
}}
<Stack
direction={"row"}
alignItems={"center"}
gap={theme.spacing(2)}
width="100%"
>
{multiple && <Checkbox checked={selected} />}
{renderOptionContent ? renderOptionContent(option) : option.name}
</Stack>
</ListItem>
);
})
}
sx={{
"&.MuiAutocomplete-root .MuiAutocomplete-input": {
padding: `0 ${theme.spacing(5)}`,
+8 -2
View File
@@ -1,7 +1,7 @@
import { forwardRef } from "react";
import Checkbox from "@mui/material/Checkbox";
import type { CheckboxProps } from "@mui/material/Checkbox";
import { Square, SquareCheck } from "lucide-react";
import { Square, SquareCheck, SquareMinus } from "lucide-react";
import { useTheme } from "@mui/material/styles";
type CheckboxInputProps = CheckboxProps & {
@@ -34,9 +34,15 @@ export const CheckboxInput = forwardRef<HTMLInputElement, CheckboxInputProps>(
strokeWidth={1.5}
/>
}
indeterminateIcon={
<SquareMinus
size={16}
strokeWidth={1.5}
/>
}
sx={{
color: theme.palette.text.secondary,
"&.Mui-checked": {
"&.Mui-checked, &.MuiCheckbox-indeterminate": {
color: theme.palette.primary.main,
},
"&:hover": { backgroundColor: "transparent" },
@@ -1,9 +1,11 @@
import Stack from "@mui/material/Stack";
import { Select, Button } from "@/Components/inputs";
import { Select, Autocomplete, Button, TextField } from "@/Components/inputs";
import { ColoredLabel } from "@/Components/design-elements";
import MenuItem from "@mui/material/MenuItem";
import useMediaQuery from "@mui/material/useMediaQuery";
import type { MonitorType } from "@/Types/Monitor";
import type { Tag } from "@/Types/Tag";
import { Typography, useTheme } from "@mui/material";
import { useTranslation } from "react-i18next";
@@ -28,22 +30,34 @@ export const ControlsFilter = ({
setSelectedStatus,
selectedState,
setSelectedState,
tagOptions,
selectedTags,
setSelectedTags,
onClearFilters,
}: {
showTypes?: boolean;
selectedTypes?: MonitorType[];
setSelectedTypes?: React.Dispatch<React.SetStateAction<MonitorType[]>>;
selectedStatus: string;
setSelectedStatus: React.Dispatch<React.SetStateAction<string>>;
selectedState: string;
setSelectedState: React.Dispatch<React.SetStateAction<string>>;
selectedStatus?: string;
setSelectedStatus?: React.Dispatch<React.SetStateAction<string>>;
selectedState?: string;
setSelectedState?: React.Dispatch<React.SetStateAction<string>>;
tagOptions?: Tag[];
selectedTags?: string[];
setSelectedTags?: React.Dispatch<React.SetStateAction<string[]>>;
onClearFilters: () => void;
}) => {
const theme = useTheme();
const { t } = useTranslation();
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
const isFilterActive =
(selectedTypes?.length ?? 0) > 0 || selectedStatus !== "" || selectedState !== "";
(selectedTypes?.length ?? 0) > 0 ||
Boolean(selectedStatus) ||
Boolean(selectedState) ||
(selectedTags?.length ?? 0) > 0;
const selectedTagObjects = (tagOptions ?? []).filter((tag) =>
(selectedTags ?? []).includes(tag.id)
);
return (
<Stack
direction={isSmall ? "column" : "row"}
@@ -66,34 +80,68 @@ export const ControlsFilter = ({
))}
</Select>
)}
<Select
placeholder="Status"
value={selectedStatus}
onChange={(e) => setSelectedStatus(e.target.value)}
>
{statuses.map((status) => (
<MenuItem
key={status}
value={status}
>
<Typography textTransform={"capitalize"}>{status}</Typography>
</MenuItem>
))}
</Select>
<Select
placeholder="State"
value={selectedState}
onChange={(e) => setSelectedState(e.target.value)}
>
{states.map((state) => (
<MenuItem
key={state}
value={state}
>
<Typography textTransform={"capitalize"}>{state}</Typography>
</MenuItem>
))}
</Select>
{setSelectedStatus && (
<Select
placeholder="Status"
value={selectedStatus ?? ""}
onChange={(e) => setSelectedStatus(e.target.value)}
>
{statuses.map((status) => (
<MenuItem
key={status}
value={status}
>
<Typography textTransform={"capitalize"}>{status}</Typography>
</MenuItem>
))}
</Select>
)}
{setSelectedState && (
<Select
placeholder="State"
value={selectedState ?? ""}
onChange={(e) => setSelectedState(e.target.value)}
>
{states.map((state) => (
<MenuItem
key={state}
value={state}
>
<Typography textTransform={"capitalize"}>{state}</Typography>
</MenuItem>
))}
</Select>
)}
{tagOptions && setSelectedTags && (
<Autocomplete
multiple
options={tagOptions}
value={selectedTagObjects}
getOptionLabel={(option) => option.name}
onChange={(_: unknown, newValue: Tag[]) => {
setSelectedTags(newValue.map((tag) => tag.id));
}}
isOptionEqualToValue={(option, value) => option.id === value.id}
renderOptionContent={(option) => (
<ColoredLabel
text={option.name}
color={option.color}
/>
)}
renderInput={(params) => (
<TextField
{...params}
placeholder={t("common.table.headers.tags")}
/>
)}
slotProps={{
popper: {
sx: { width: "fit-content !important", minWidth: 280 },
},
}}
sx={{ minWidth: 180 }}
/>
)}
{isFilterActive && (
<Button
variant="contained"
+6
View File
@@ -17,6 +17,7 @@ import {
User,
Lock,
Users,
Tag,
} from "lucide-react";
export const getMenu = (t: Function) => {
@@ -41,6 +42,11 @@ export const getMenu = (t: Function) => {
path: "notifications",
icon: <Icon icon={Bell} />,
},
{
name: t("components.sidebar.menu.tags"),
path: "tags",
icon: <Icon icon={Tag} />,
},
{
name: t("components.sidebar.menu.checks"),
path: "checks",
+3 -1
View File
@@ -20,6 +20,8 @@ export const useToast = () => {
showToast(msg, { ...opts, type: "error" });
const toastInfo = (msg: string, opts?: ToastOptions) =>
showToast(msg, { ...opts, type: "info" });
const toastWarning = (msg: string, opts?: ToastOptions) =>
showToast(msg, { ...opts, type: "warning" });
return { showToast, toastSuccess, toastError, toastInfo };
return { showToast, toastSuccess, toastError, toastInfo, toastWarning };
};
+14 -3
View File
@@ -9,7 +9,10 @@ import type { Monitor } from "@/Types/Monitor";
interface ApiResponse {
success: boolean;
msg: string;
data: Monitor[];
data: {
monitors: Monitor[];
failedCount: number;
};
}
interface UseBulkMonitorActionsReturn {
@@ -25,7 +28,7 @@ export const useBulkMonitorActions = (
page?: number
): UseBulkMonitorActionsReturn => {
const [selectedRows, setSelectedRows] = useState<string[]>([]);
const { toastSuccess, toastError, toastInfo } = useToast();
const { toastSuccess, toastError, toastInfo, toastWarning } = useToast();
const { t } = useTranslation();
// Clear selection when page changes
@@ -40,13 +43,21 @@ export const useBulkMonitorActions = (
pause,
});
const affectedCount = res.data?.data?.length ?? 0;
const affectedCount = res.data?.data?.monitors?.length ?? 0;
const failedCount = res.data?.data?.failedCount ?? 0;
if (affectedCount === 0) {
const key = pause
? "pages.common.monitors.bulkPause.alreadyPaused"
: "pages.common.monitors.bulkPause.alreadyRunning";
toastInfo(t(key, { count: selectedRows.length }));
} else if (failedCount > 0) {
toastWarning(
t("pages.common.monitors.bulkPause.partialFailure", {
successCount: affectedCount,
failedCount,
})
);
} else {
const key = pause
? "pages.common.monitors.bulkPause.paused"
+125 -110
View File
@@ -13,128 +13,143 @@ const getBaseDefaults = (data?: Monitor | null) => ({
description: data?.description || "",
interval: data?.interval || 60000,
notifications: data?.notifications || [],
tags: data?.tags || [],
statusWindowSize: data?.statusWindowSize || 5,
statusWindowThreshold: data?.statusWindowThreshold || 60,
});
// Geo-check defaults — only http and ping support geo checks.
const getGeoCheckDefaults = (data?: Monitor | null) => ({
geoCheckEnabled: data?.geoCheckEnabled ?? false,
geoCheckLocations: data?.geoCheckLocations || [],
geoCheckInterval: data?.geoCheckInterval || 300000,
});
export const getMonitorDefaults = (
type: MonitorType,
data: Monitor | null = null
): MonitorFormData => {
const base = getBaseDefaults(data);
let defaults: MonitorFormData;
switch (type) {
case "http":
defaults = {
...base,
...getGeoCheckDefaults(data),
type: "http",
url: data?.url || "",
ignoreTlsErrors: data?.ignoreTlsErrors || false,
useAdvancedMatching: data?.useAdvancedMatching || false,
matchMethod: data?.matchMethod || "",
expectedValue: data?.expectedValue || "",
jsonPath: data?.jsonPath || "",
};
break;
case "ping":
defaults = {
...base,
...getGeoCheckDefaults(data),
type: "ping",
url: data?.url || "",
};
break;
case "port":
defaults = {
...base,
type: "port",
url: data?.url || "",
port: data?.port || 80,
};
break;
case "docker":
defaults = {
...base,
type: "docker",
url: data?.url || "",
};
break;
case "game":
defaults = {
...base,
type: "game",
url: data?.url || "",
port: data?.port || 27015,
gameId: data?.gameId || "",
};
break;
case "grpc":
defaults = {
...base,
type: "grpc",
url: data?.url || "",
port: data?.port || 50051,
grpcServiceName: data?.grpcServiceName || "",
ignoreTlsErrors: data?.ignoreTlsErrors || false,
};
break;
case "pagespeed":
defaults = {
...base,
type: "pagespeed",
url: data?.url || "",
strategy: data?.strategy ?? DefaultPageSpeedStrategy,
};
break;
case "hardware":
defaults = {
...base,
type: "hardware",
url: data?.url || "",
secret: data?.secret || "",
cpuAlertThreshold: data?.cpuAlertThreshold ?? 100,
memoryAlertThreshold: data?.memoryAlertThreshold ?? 100,
diskAlertThreshold: data?.diskAlertThreshold ?? 100,
tempAlertThreshold: data?.tempAlertThreshold ?? 100,
selectedDisks: data?.selectedDisks || [],
};
break;
case "websocket":
defaults = {
...base,
type: "websocket",
url: data?.url || "",
ignoreTlsErrors: data?.ignoreTlsErrors || false,
};
break;
case "dns":
defaults = {
...base,
type: "dns",
url: data?.url || "",
dnsServer: data?.dnsServer || "",
dnsRecordType: data?.dnsRecordType || "A",
};
break;
default:
defaults = {
...base,
...getGeoCheckDefaults(data),
type: "http",
url: "",
ignoreTlsErrors: false,
useAdvancedMatching: false,
matchMethod: "",
expectedValue: "",
jsonPath: "",
};
}
return defaults;
};
export const useMonitorForm = ({
data = null,
defaultType = "http",
}: UseMonitorFormOptions = {}) => {
return useMemo(() => {
const type = data?.type || defaultType;
const base = getBaseDefaults(data);
let defaults: MonitorFormData;
switch (type) {
case "http":
defaults = {
...base,
type: "http",
url: data?.url || "",
ignoreTlsErrors: data?.ignoreTlsErrors || false,
useAdvancedMatching: data?.useAdvancedMatching || false,
matchMethod: data?.matchMethod || "",
expectedValue: data?.expectedValue || "",
jsonPath: data?.jsonPath || "",
};
break;
case "ping":
defaults = {
...base,
type: "ping",
url: data?.url || "",
};
break;
case "port":
defaults = {
...base,
type: "port",
url: data?.url || "",
port: data?.port || 80,
};
break;
case "docker":
defaults = {
...base,
type: "docker",
url: data?.url || "",
};
break;
case "game":
defaults = {
...base,
type: "game",
url: data?.url || "",
port: data?.port || 27015,
gameId: data?.gameId || "",
};
break;
case "grpc":
defaults = {
...base,
type: "grpc",
url: data?.url || "",
port: data?.port || 50051,
grpcServiceName: data?.grpcServiceName || "",
ignoreTlsErrors: data?.ignoreTlsErrors || false,
};
break;
case "pagespeed":
defaults = {
...base,
type: "pagespeed",
url: data?.url || "",
strategy: data?.strategy ?? DefaultPageSpeedStrategy,
};
break;
case "hardware":
defaults = {
...base,
type: "hardware",
url: data?.url || "",
secret: data?.secret || "",
cpuAlertThreshold: data?.cpuAlertThreshold ?? 100,
memoryAlertThreshold: data?.memoryAlertThreshold ?? 100,
diskAlertThreshold: data?.diskAlertThreshold ?? 100,
tempAlertThreshold: data?.tempAlertThreshold ?? 100,
selectedDisks: data?.selectedDisks || [],
};
break;
case "websocket":
defaults = {
...base,
type: "websocket",
url: data?.url || "",
ignoreTlsErrors: data?.ignoreTlsErrors || false,
};
break;
case "dns":
defaults = {
...base,
type: "dns",
url: data?.url || "",
dnsServer: data?.dnsServer || "",
dnsRecordType: data?.dnsRecordType || "A",
};
break;
default:
defaults = {
...base,
type: "http",
url: "",
ignoreTlsErrors: false,
useAdvancedMatching: false,
matchMethod: "",
expectedValue: "",
jsonPath: "",
};
}
return { schema: monitorSchema, defaults };
return { schema: monitorSchema, defaults: getMonitorDefaults(type, data) };
}, [data, defaultType]);
};
+8
View File
@@ -78,6 +78,14 @@ function buildDefaults(data: Notification | null): NotificationFormData {
accessToken: data.accessToken || "",
};
}
if (data?.type === "ntfy") {
return {
type: "ntfy",
notificationName: data.notificationName || "",
address: data.address || "",
topic: data.topic || "",
};
}
// Default: email (covers both data === null and data.type === "email")
return {
type: "email",
+18
View File
@@ -0,0 +1,18 @@
import { useMemo } from "react";
import { tagSchema, type TagFormData } from "@/Validation/tag";
import type { Tag } from "@/Types/Tag";
interface UseTagsFormOptions {
data?: Tag | null;
}
export const useTagsForm = ({ data }: UseTagsFormOptions) => {
return useMemo(() => {
const defaults: TagFormData = {
name: data?.name ?? "",
color: data?.color ?? "#13715B",
};
return { schema: tagSchema, defaults };
}, [data]);
};
File diff suppressed because it is too large. Load diff
@@ -1,7 +1,14 @@
import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import { Table, Pagination, StatusLabel, Gauge } from "@/Components/design-elements";
import {
Table,
Pagination,
StatusLabel,
Gauge,
ColoredLabel,
} from "@/Components/design-elements";
import { SPACING } from "@/Utils/Theme/constants";
import type { Header } from "@/Components/design-elements/Table";
import { ActionsMenu, type ActionMenuItem } from "@/Components/actions-menu";
import { ArrowUp, ArrowDown } from "lucide-react";
@@ -13,10 +20,12 @@ import { useNavigate } from "react-router-dom";
import { usePost } from "@/Hooks/UseApi";
import type { Monitor } from "@/Types/Monitor";
import type { Tag } from "@/Types/Tag";
import { Checkbox } from "@/Components/inputs";
interface InfraMonitorsTableProps {
monitors: Monitor[];
tags: Tag[];
refetch: () => void;
setSelectedMonitor: (monitor: Monitor | null) => void;
sortField: string;
@@ -34,6 +43,7 @@ interface InfraMonitorsTableProps {
export const InfraMonitorsTable = ({
monitors,
tags,
refetch,
setSelectedMonitor,
sortField,
@@ -267,6 +277,32 @@ export const InfraMonitorsTable = ({
return <Gauge progress={diskUsage} />;
},
},
{
id: "tags",
content: t("common.table.headers.tags"),
render: (row) => {
if (row.tags.length === 0) return "-";
return (
<Stack
justifyContent={"center"}
direction={isSmall ? "column" : "row"}
gap={theme.spacing(SPACING.LG)}
>
{row.tags.map((tagId) => {
const fullTag = tags.find((t) => t.id === tagId);
if (!fullTag) return null;
return (
<ColoredLabel
key={fullTag.id}
text={fullTag.name}
color={fullTag.color}
/>
);
})}
</Stack>
);
},
},
{
id: "actions",
@@ -1,6 +1,7 @@
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import useMediaQuery from "@mui/material/useMediaQuery";
import { MonitorBasePageWithStates } from "@/Components/design-elements";
import { MonitorBasePageWithStates, ColoredLabel } from "@/Components/design-elements";
import { HeaderCreate } from "@/Components/common";
import {
ControlsFilter,
@@ -13,7 +14,9 @@ import { Play, Pause } from "lucide-react";
import { useGet, useDelete } from "@/Hooks/UseApi";
import { useIsAdmin } from "@/Hooks/useIsAdmin";
import type { Monitor, MonitorsWithChecksResponse } from "@/Types/Monitor";
import type { Tag } from "@/Types/Tag";
import { useTheme } from "@mui/material";
import { SPACING } from "@/Utils/Theme/constants";
import { useState, useCallback, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { useSelector, useDispatch } from "react-redux";
@@ -38,12 +41,15 @@ const InfrastructureMonitors = () => {
// Local state
const [selectedStatus, setSelectedStatus] = useState<string>("");
const [selectedState, setSelectedState] = useState<string>("");
const [selectedTags, setSelectedTags] = useState<string[]>([]);
const [search, setSearch] = useState<string>("");
const [page, setPage] = useState<number>(0);
const [sortField, setSortField] = useState<string>("");
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc");
const [selectedMonitor, setSelectedMonitor] = useState<Monitor | null>(null);
const { data: tags } = useGet<Tag[]>("/tags/team");
const isDialogOpen = Boolean(selectedMonitor);
const debouncedSearch = useDebounce<string>(search, 300);
@@ -51,6 +57,7 @@ const InfrastructureMonitors = () => {
const handleClearFilters = useCallback(() => {
setSelectedStatus("");
setSelectedState("");
setSelectedTags([]);
setSearch("");
}, []);
@@ -81,6 +88,7 @@ const InfrastructureMonitors = () => {
const monitorsWithChecksUrl = useMemo(() => {
const params = new URLSearchParams();
params.append("type", "hardware");
selectedTags.forEach((tagId) => params.append("tags", tagId));
params.append("limit", "1");
if (page !== undefined) params.append("page", String(page));
if (rowsPerPage) params.append("rowsPerPage", String(rowsPerPage));
@@ -88,7 +96,7 @@ const InfrastructureMonitors = () => {
if (field) params.append("field", field);
if (sortOrder) params.append("order", sortOrder);
return `/monitors/team/with-checks?${params.toString()}`;
}, [page, rowsPerPage, filter, field, sortOrder]);
}, [page, rowsPerPage, filter, field, sortOrder, selectedTags]);
const {
data: monitorsWithChecksData,
@@ -114,7 +122,9 @@ const InfrastructureMonitors = () => {
} = useBulkMonitorActions(refetch, page);
// Check if any filters are active
const hasActiveFilters = Boolean(selectedStatus || selectedState || search);
const hasActiveFilters = Boolean(
selectedStatus || selectedState || selectedTags.length > 0 || search
);
// Show empty state only when there are truly no monitors (not just filtered out)
const effectiveTotalCount =
@@ -165,6 +175,9 @@ const InfrastructureMonitors = () => {
setSelectedStatus={setSelectedStatus}
selectedState={selectedState}
setSelectedState={setSelectedState}
tagOptions={tags ?? []}
selectedTags={selectedTags}
setSelectedTags={setSelectedTags}
onClearFilters={handleClearFilters}
/>
<TextField
@@ -176,6 +189,30 @@ const InfrastructureMonitors = () => {
/>
</Stack>
{selectedTags.length > 0 && (
<Stack
direction={isSmall ? "column" : "row"}
alignItems={isSmall ? "flex-start" : "center"}
flexWrap="wrap"
gap={theme.spacing(SPACING.XL)}
>
<Typography color={theme.palette.text.secondary}>
{t("pages.uptime.filters.activeTags")}
</Typography>
{selectedTags.map((tagId) => {
const tag = tags?.find((t) => t.id === tagId);
if (!tag) return null;
return (
<ColoredLabel
key={tag.id}
text={tag.name}
color={tag.color}
/>
);
})}
</Stack>
)}
{!isLoading && (
<BulkActionsBar
selectedCount={selectedRows.length}
@@ -200,6 +237,7 @@ const InfrastructureMonitors = () => {
<InfraMonitorsTable
monitors={monitorsWithChecksData?.monitors || []}
tags={tags ?? []}
refetch={refetch}
setSelectedMonitor={setSelectedMonitor}
sortField={sortField}
+34 -7
View File
@@ -1,12 +1,15 @@
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import { Stats } from "@/Pages/Logs/components/Stats";
import { MongoStats } from "@/Pages/Logs/components/MongoStats";
import { MongoCollectionStats } from "./components/MongoCollectionStats";
import { StatGauges } from "@/Pages/Logs/components/StatGauges";
import { useTheme } from "@mui/material";
import { useTranslation } from "react-i18next";
import { useGet } from "@/Hooks/UseApi";
import type { Diagnostics } from "@/Types/Diagnostics";
import { LAYOUT, SPACING } from "@/Utils/Theme/constants";
export const TabDiagnostics = () => {
const theme = useTheme();
@@ -19,12 +22,12 @@ export const TabDiagnostics = () => {
} = useGet<Diagnostics>("/diagnostic/system", {}, { refreshInterval: 5000 });
return (
<Stack gap={theme.spacing(12)}>
<Stack gap={theme.spacing(4)}>
<Stack gap={theme.spacing(1)}>
<Stack gap={theme.spacing(LAYOUT.XL)}>
<Stack gap={theme.spacing(LAYOUT.XS)}>
<Stack gap={theme.spacing(SPACING.SM)}>
<Typography
variant="eyebrow"
color="text.secondary"
color={theme.palette.text.secondary}
>
{t("pages.logs.diagnostics.sections.runtimeStats.title")}
</Typography>
@@ -34,11 +37,11 @@ export const TabDiagnostics = () => {
</Stack>
<Stats diagnostics={diagnostics} />
</Stack>
<Stack gap={theme.spacing(4)}>
<Stack gap={theme.spacing(1)}>
<Stack gap={theme.spacing(LAYOUT.XS)}>
<Stack gap={theme.spacing(SPACING.SM)}>
<Typography
variant="eyebrow"
color="text.secondary"
color={theme.palette.text.secondary}
>
{t("pages.logs.diagnostics.sections.memoryCpu.title")}
</Typography>
@@ -48,6 +51,30 @@ export const TabDiagnostics = () => {
</Stack>
<StatGauges diagnostics={diagnostics} />
</Stack>
<Stack gap={theme.spacing(LAYOUT.XS)}>
<Typography
variant="eyebrow"
color={theme.palette.text.secondary}
>
{t("pages.logs.diagnostics.sections.mongoDBStats.title")}
</Typography>
<Typography color={theme.palette.text.secondary}>
{t("pages.logs.diagnostics.sections.mongoDBStats.description")}
</Typography>
<MongoStats diagnostics={diagnostics} />
</Stack>
<Stack gap={theme.spacing(LAYOUT.XS)}>
<Typography
variant="eyebrow"
color={theme.palette.text.secondary}
>
{t("pages.logs.diagnostics.sections.mongoDBCollectionStats.title")}
</Typography>
<Typography color={theme.palette.text.secondary}>
{t("pages.logs.diagnostics.sections.mongoDBCollectionStats.description")}
</Typography>
<MongoCollectionStats diagnostics={diagnostics} />
</Stack>
</Stack>
);
};
+53 -3
View File
@@ -2,8 +2,9 @@ import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import { TableJobs, TableFailedJobs } from "./components/TableJobs";
import { TableWorkers } from "./components/TableWorkers";
import { useTheme } from "@mui/material";
import { useState } from "react";
import { useTranslation, Trans } from "react-i18next";
import { useGet, usePost } from "@/Hooks/UseApi";
import type { QueueData } from "@/Types/Queue";
@@ -15,16 +16,27 @@ import { SPACING } from "@/Utils/Theme/constants";
export const TabQueue = () => {
const theme = useTheme();
const { t } = useTranslation();
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const {
data: queueData,
isLoading,
// error,
refetch,
} = useGet<QueueData>("/queue/all-metrics", {}, { refreshInterval: 1000 });
} = useGet<QueueData>(
`/queue/all-metrics?page=${page}&rowsPerPage=${rowsPerPage}`,
{},
{ refreshInterval: 1000, keepPreviousData: true }
);
const { post, loading: isFlushing } = usePost();
const jobs = queueData?.jobs ?? [];
const jobsCount = queueData?.count ?? 0;
const workers = (queueData?.metrics?.workers ?? []).map((worker, idx) => ({
...worker,
id: idx,
}));
const metrics = queueData?.metrics ?? null;
const hasNeverRun = !isLoading && metrics !== null && (metrics.totalRuns ?? 0) === 0;
const noQueueData = !isLoading && metrics === null && jobs.length === 0;
@@ -46,6 +58,34 @@ export const TabQueue = () => {
return (
<Stack gap={theme.spacing(16)}>
<Metrics metrics={metrics} />
<Stack gap={theme.spacing(3)}>
<Stack gap={theme.spacing(1)}>
<Typography
variant="eyebrow"
color={theme.palette.text.secondary}
>
{t("pages.logs.workers")}
</Typography>
<Typography color={theme.palette.text.secondary}>
<Trans
i18nKey="pages.logs.workersExplainers"
components={{
highlight: (
<Box
component="span"
bgcolor={theme.palette.rowStatus.running}
color={theme.palette.text.primary}
px={SPACING.SM}
py={SPACING.XXS}
borderRadius={theme.shape.borderRadius}
/>
),
}}
/>
</Typography>
</Stack>
<TableWorkers queueWorkers={workers} />
</Stack>
<Stack gap={theme.spacing(3)}>
<Stack gap={theme.spacing(1)}>
<Typography
@@ -72,7 +112,17 @@ export const TabQueue = () => {
/>
</Typography>
</Stack>
<TableJobs jobs={jobs} />
<TableJobs
jobs={jobs}
count={jobsCount}
page={page}
rowsPerPage={rowsPerPage}
onPageChange={(_, newPage) => setPage(newPage)}
onRowsPerPageChange={(event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
}}
/>
</Stack>
<Stack gap={theme.spacing(3)}>
<Stack gap={theme.spacing(1)}>
@@ -0,0 +1,104 @@
import Box from "@mui/material/Box";
import { StatBox, Gauge, Tooltip } from "@/Components/design-elements";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import { useTranslation } from "react-i18next";
import prettyBytes from "pretty-bytes";
import { useTheme } from "@mui/material";
import type { Diagnostics } from "@/Types/Diagnostics";
import { LAYOUT } from "@/Utils/Theme/constants";
interface MongoStatsProps {
diagnostics: Diagnostics | null;
}
export const MongoCollectionStats = ({ diagnostics }: MongoStatsProps) => {
const theme = useTheme();
const { t } = useTranslation();
const mongoStats = diagnostics?.mongoStats;
if (!mongoStats) {
return null;
}
return (
<Box
display={"grid"}
gap={theme.spacing(LAYOUT.MD)}
sx={{
gridTemplateColumns: { xs: "1fr", md: "repeat(5, 1fr)" },
"& > *": { width: "100% !important" },
}}
>
{mongoStats.collections.map((colStat) => (
<StatBox
key={colStat.name}
title={colStat.name}
subtitle={""}
>
<Stack
direction={"row"}
alignItems={"center"}
justifyContent={"space-between"}
>
<Stack>
{/* <Typography variant="body2">{`Documents: ${colStat.documentCount}`}</Typography> */}
<Typography variant="body2">
{t("pages.logs.diagnostics.mongoDBCollectionStats.documents", {
count: colStat.documentCount,
})}
</Typography>
{colStat.bucketCount !== undefined && (
<Typography variant="body2">
{t("pages.logs.diagnostics.mongoDBCollectionStats.bucketCount", {
count: colStat.bucketCount,
})}
</Typography>
)}
<Typography variant="body2">
{t("pages.logs.diagnostics.mongoDBCollectionStats.storageSize", {
size: prettyBytes(colStat.storageSize),
})}
</Typography>
<Typography variant="body2">
{t("pages.logs.diagnostics.mongoDBCollectionStats.indexSize", {
size: prettyBytes(colStat.totalIndexSize),
})}
</Typography>
<Typography variant="body2">
{t("pages.logs.diagnostics.mongoDBCollectionStats.totalSize", {
size: prettyBytes(colStat.totalSize),
})}
</Typography>
</Stack>
<Stack alignItems={"center"}>
<Tooltip
title={t("pages.logs.diagnostics.mongoDBCollectionStats.dbUsageTooltip", {
percentage: ((colStat.totalSize / mongoStats.totalSize) * 100).toFixed(
2
),
})}
placement="top"
slotProps={{
popper: {
modifiers: [
{
name: "offset",
options: {
offset: [0, -10],
},
},
],
},
}}
>
<Gauge progress={(colStat.totalSize / mongoStats.totalSize) * 100} />
</Tooltip>
</Stack>
</Stack>
</StatBox>
))}
</Box>
);
};
@@ -0,0 +1,54 @@
import Box from "@mui/material/Box";
import { StatBox } from "@/Components/design-elements";
import { useTranslation } from "react-i18next";
import prettyBytes from "pretty-bytes";
import { useTheme } from "@mui/material";
import type { Diagnostics } from "@/Types/Diagnostics";
import { LAYOUT } from "@/Utils/Theme/constants";
interface MongoStatsProps {
diagnostics: Diagnostics | null;
}
export const MongoStats = ({ diagnostics }: MongoStatsProps) => {
const theme = useTheme();
const { t } = useTranslation();
const mongoStats = diagnostics?.mongoStats;
if (!mongoStats) {
return null;
}
return (
<Box
display={"grid"}
gap={theme.spacing(LAYOUT.MD)}
sx={{
gridTemplateColumns: { xs: "1fr", md: "repeat(5, 1fr)" },
"& > *": { width: "100% !important" },
}}
>
<StatBox
title={t("pages.logs.diagnostics.mongoDBStats.readyState")}
subtitle={mongoStats.readyState.toString()}
/>
<StatBox
title={t("pages.logs.diagnostics.mongoDBStats.host")}
subtitle={mongoStats.host}
/>
<StatBox
title={t("pages.logs.diagnostics.mongoDBStats.port")}
subtitle={mongoStats.port.toString()}
/>
<StatBox
title={t("pages.logs.diagnostics.mongoDBStats.dbName")}
subtitle={mongoStats.dbName}
/>
<StatBox
title={t("pages.logs.diagnostics.mongoDBStats.storageSize")}
subtitle={prettyBytes(mongoStats.totalSize)}
/>
</Box>
);
};
+59 -14
View File
@@ -1,4 +1,4 @@
import { Table } from "@/Components/design-elements";
import { Table, Pagination } from "@/Components/design-elements";
import { Typography, useTheme } from "@mui/material";
import prettyMilliseconds from "pretty-ms";
import { formatTimestamp } from "@/Utils/TimeUtils";
@@ -6,14 +6,27 @@ import { formatTimestamp } from "@/Utils/TimeUtils";
import { useTranslation } from "react-i18next";
import type { QueueJobFailure, QueueJobSummary, QueueMetrics } from "@/Types/Queue";
import type { Header } from "@/Components/design-elements";
import type { TablePaginationProps } from "@mui/material/TablePagination";
type QueueJobWithId = QueueJobSummary & { id: string | number };
interface TableJobsProps {
jobs: QueueJobSummary[];
count: number;
page: number;
rowsPerPage: number;
onPageChange: TablePaginationProps["onPageChange"];
onRowsPerPageChange: TablePaginationProps["onRowsPerPageChange"];
}
export const TableJobs = ({ jobs }: TableJobsProps) => {
export const TableJobs = ({
jobs,
count,
page,
rowsPerPage,
onPageChange,
onRowsPerPageChange,
}: TableJobsProps) => {
const { t } = useTranslation();
const theme = useTheme();
@@ -127,21 +140,53 @@ export const TableJobs = ({ jobs }: TableJobsProps) => {
);
},
},
{
id: "lockedBy",
content: t("pages.logs.table.headers.lockedBy"),
render: (row) => {
const lockedBy = row.lockedBy;
if (!lockedBy) {
return <Typography sx={cellSx}>-</Typography>;
}
const workerId = lockedBy.split(":")[2];
const shortWorkerId = workerId.slice(workerId.length - 8);
return (
<Typography
title={workerId}
sx={cellSx}
>
{`...${shortWorkerId}`}
</Typography>
);
},
},
];
return (
<Table
headers={headers}
data={jobsWithId}
getRowSx={(row) => ({
...(row.lockedAt && {
"& td": { backgroundColor: theme.palette.rowStatus.running },
}),
...(row.monitorActive === false && {
"& td": { backgroundColor: theme.palette.rowStatus.paused },
}),
})}
/>
<>
<Table
headers={headers}
data={jobsWithId}
getRowSx={(row) => ({
...(row.lockedAt && {
"& td": { backgroundColor: theme.palette.rowStatus.running },
}),
...(row.monitorActive === false && {
"& td": { backgroundColor: theme.palette.rowStatus.paused },
}),
})}
/>
<Pagination
component="div"
count={count}
page={page}
rowsPerPage={rowsPerPage}
onPageChange={onPageChange}
onRowsPerPageChange={onRowsPerPageChange}
itemsOnPage={jobs.length}
/>
</>
);
};
@@ -0,0 +1,94 @@
import { Table } from "@/Components/design-elements";
import { Typography, useTheme } from "@mui/material";
import { formatTimestamp } from "@/Utils/TimeUtils";
import { useTranslation } from "react-i18next";
import type { QueueWorker } from "@/Types/Queue";
import type { Header } from "@/Components/design-elements";
type QueueWorkerWithId = QueueWorker & { id: number };
interface TableWorkersProps {
queueWorkers: QueueWorkerWithId[];
}
export const TableWorkers = ({ queueWorkers }: TableWorkersProps) => {
const { t } = useTranslation();
const theme = useTheme();
const cellSx = {
whiteSpace: "nowrap" as const,
overflow: "hidden",
textOverflow: "ellipsis",
maxWidth: 220,
};
const headers: Header<QueueWorkerWithId>[] = [
{
id: "host",
content: t("pages.logs.table.headers.host"),
render: (row) => {
const host = row.workerId.split(":")[0];
return (
<Typography
fontFamily={theme.typography.fontFamilyMonospace}
sx={cellSx}
>
{host}
</Typography>
);
},
},
{
id: "processId",
content: t("pages.logs.table.headers.processId"),
render: (row) => {
const processId = row.workerId.split(":")[1];
return (
<Typography
fontFamily={theme.typography.fontFamilyMonospace}
sx={cellSx}
>
{processId}
</Typography>
);
},
},
{
id: "id",
content: t("pages.logs.table.headers.workerId"),
render: (row) => {
const workerId = row.workerId.split(":")[2];
const shortWorkerId = workerId.slice(workerId.length - 8);
return (
<Typography
fontFamily={theme.typography.fontFamilyMonospace}
title={workerId}
sx={cellSx}
>
{`...${shortWorkerId}`}
</Typography>
);
},
},
{
id: "mode",
content: t("pages.logs.table.headers.workerMode"),
render: (row) => <Typography sx={cellSx}>{row.mode}</Typography>,
},
{
id: "lastSeen",
content: t("pages.logs.table.headers.lastSeenAt"),
render: (row) => (
<Typography sx={cellSx}>{formatTimestamp(row.lastSeenAt)}</Typography>
),
},
];
return (
<Table
headers={headers}
data={queueWorkers}
/>
);
};
@@ -150,7 +150,8 @@ const NotificationsCreatePage = () => {
{watchedType !== "matrix" &&
watchedType !== "telegram" &&
watchedType !== "pushover" &&
watchedType !== "twilio" && (
watchedType !== "twilio" &&
watchedType !== "ntfy" && (
<ConfigBox
title={addressConfig.title}
subtitle={addressConfig.description}
@@ -174,6 +175,48 @@ const NotificationsCreatePage = () => {
}
/>
)}
{watchedType === "ntfy" && (
<ConfigBox
title={t("pages.notifications.form.ntfy.title")}
subtitle={t("pages.notifications.form.ntfy.description")}
rightContent={
<Stack spacing={theme.spacing(8)}>
<Controller
name="address"
control={control}
defaultValue={"address" in defaults ? defaults.address : ""}
render={({ field, fieldState }) => (
<TextField
{...field}
type="text"
fieldLabel={t("pages.notifications.form.ntfy.optionServerUrl")}
placeholder={t("pages.notifications.form.ntfy.placeholderServerUrl")}
fullWidth
error={!!fieldState.error}
helperText={fieldState.error?.message ?? ""}
/>
)}
/>
<Controller
name="topic"
control={control}
defaultValue={"topic" in defaults ? defaults.topic : ""}
render={({ field, fieldState }) => (
<TextField
{...field}
type="text"
fieldLabel={t("pages.notifications.form.ntfy.optionTopic")}
placeholder={t("pages.notifications.form.ntfy.placeholderTopic")}
fullWidth
error={!!fieldState.error}
helperText={fieldState.error?.message ?? ""}
/>
)}
/>
</Stack>
}
/>
)}
{watchedType === "telegram" && (
<ConfigBox
title={t("pages.notifications.form.telegram.title")}
@@ -6,8 +6,9 @@ import {
Pagination,
StatusLabel,
StrategyBadge,
ColoredLabel,
} from "@/Components/design-elements";
import { LAYOUT } from "@/Utils/Theme/constants";
import { LAYOUT, SPACING } from "@/Utils/Theme/constants";
import { HistogramPageSpeed } from "@/Components/monitors";
import { ArrowDown, ArrowUp } from "lucide-react";
import type { Header } from "@/Components/design-elements/Table";
@@ -20,10 +21,12 @@ import { useNavigate } from "react-router-dom";
import { usePatch } from "@/Hooks/UseApi";
import type { Monitor, MonitorWithChecks } from "@/Types/Monitor";
import type { Tag } from "@/Types/Tag";
import type { ActionMenuItem } from "@/Components/actions-menu";
export const PageSpeedMonitorsTable = ({
monitors,
tags,
refetch,
setSelectedMonitor,
sortField,
@@ -37,6 +40,7 @@ export const PageSpeedMonitorsTable = ({
setRowsPerPage,
}: {
monitors: MonitorWithChecks[];
tags: Tag[];
refetch: Function;
setSelectedMonitor: Function;
sortField: string;
@@ -233,6 +237,32 @@ export const PageSpeedMonitorsTable = ({
);
},
},
{
id: "tags",
content: t("common.table.headers.tags"),
render: (row) => {
if (row.tags.length === 0) return "-";
return (
<Stack
justifyContent={"center"}
direction={isSmall ? "column" : "row"}
gap={theme.spacing(SPACING.LG)}
>
{row.tags.map((tagId) => {
const fullTag = tags.find((tag) => tag.id === tagId);
if (!fullTag) return null;
return (
<ColoredLabel
key={fullTag.id}
text={fullTag.name}
color={fullTag.color}
/>
);
})}
</Stack>
);
},
},
{
id: "actions",
+116 -6
View File
@@ -1,22 +1,32 @@
import {
MonitorBasePageWithStates,
PageSpeedKeyPriorityFallback,
ColoredLabel,
} from "@/Components/design-elements";
import { Dialog } from "@/Components/inputs";
import { Dialog, TextField } from "@/Components/inputs";
import { HeaderCreate } from "@/Components/common";
import { PageSpeedMonitorsTable } from "@/Pages/PageSpeed/Monitors/Components/PageSpeedMonitorsTable";
import type { Monitor } from "@/Types/Monitor";
import type { Tag } from "@/Types/Tag";
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import useMediaQuery from "@mui/material/useMediaQuery";
import { useTheme } from "@mui/material";
import { SPACING } from "@/Utils/Theme/constants";
import { useTranslation } from "react-i18next";
import { useMemo, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import { useIsAdmin } from "@/Hooks/useIsAdmin";
import { useGet, useDelete } from "@/Hooks/UseApi";
import useDebounce from "@/Hooks/useDebounce";
import type { MonitorsWithChecksResponse } from "@/Types/Monitor";
import type { AppSettingsResponse } from "@/Types/Settings";
import { HeaderMonitorsSummary } from "@/Components/monitors";
import { ControlsFilter, HeaderMonitorsSummary } from "@/Components/monitors";
const PageSpeedMonitorsPage = () => {
const { t } = useTranslation();
const theme = useTheme();
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
const isAdmin = useIsAdmin();
const { deleteFn, loading: isDeleting } = useDelete();
@@ -33,17 +43,50 @@ const PageSpeedMonitorsPage = () => {
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc");
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [selectedTags, setSelectedTags] = useState<string[]>([]);
const [selectedStatus, setSelectedStatus] = useState<string>("");
const [selectedState, setSelectedState] = useState<string>("");
const [search, setSearch] = useState<string>("");
const debouncedSearch = useDebounce<string>(search, 300);
// Status: pass "up"/"down" directly to the API
// State: "active" -> true, "paused" -> false
const toFilterStatus = useMemo(() => {
if (selectedStatus === "up") return "up";
if (selectedStatus === "down") return "down";
return undefined;
}, [selectedStatus]);
const toFilterActive = useMemo(() => {
if (selectedState === "active") return "true";
if (selectedState === "paused") return "false";
return undefined;
}, [selectedState]);
// Priority: status > isActive > search > sort
const filterLookup = new Map<string | undefined, string>([
[toFilterStatus, "status"],
[toFilterActive, "isActive"],
]);
const activeFilter = [...filterLookup].find(([key]) => key !== undefined);
const field = activeFilter?.[1] || (debouncedSearch ? "name" : sortField || undefined);
const filter = activeFilter?.[0] || debouncedSearch;
const { data: tags } = useGet<Tag[]>("/tags/team");
const monitorsWithChecksUrl = useMemo(() => {
const params = new URLSearchParams();
params.append("type", "pagespeed");
selectedTags.forEach((tagId) => params.append("tags", tagId));
params.append("limit", "25");
if (page !== undefined) params.append("page", String(page));
if (rowsPerPage) params.append("rowsPerPage", String(rowsPerPage));
if (sortField) params.append("field", sortField);
if (filter) params.append("filter", filter);
if (field) params.append("field", field);
if (sortOrder) params.append("order", sortOrder);
return `/monitors/team/with-checks?${params.toString()}`;
}, [page, rowsPerPage, sortField, sortOrder]);
}, [page, rowsPerPage, sortOrder, selectedTags, filter, field]);
const {
data: monitorsData,
@@ -75,12 +118,28 @@ const PageSpeedMonitorsPage = () => {
setSelectedMonitor(null);
};
const handleClearFilters = useCallback(() => {
setSelectedStatus("");
setSelectedState("");
setSelectedTags([]);
setSearch("");
}, []);
const hasActiveFilters = Boolean(
selectedStatus || selectedState || selectedTags.length > 0 || search
);
const effectiveTotalCount =
hasActiveFilters && (summary?.totalMonitors ?? 0) === 0
? 1
: (summary?.totalMonitors ?? 0);
return (
<MonitorBasePageWithStates
headerKey="pageSpeed"
loading={isLoading}
error={monitorsError || settingsError}
totalCount={summary?.totalMonitors ?? 0}
totalCount={effectiveTotalCount}
page="pageSpeed"
actionLink="/pagespeed/create"
priorityFallback={showApiKeyWarning ? <PageSpeedKeyPriorityFallback /> : undefined}
@@ -91,8 +150,59 @@ const PageSpeedMonitorsPage = () => {
isAdmin={isAdmin}
/>
<HeaderMonitorsSummary summary={summary} />
<Stack
direction={isSmall ? "column" : "row"}
justifyContent={isSmall ? "flex-start" : "space-between"}
gap={theme.spacing(4)}
>
<ControlsFilter
showTypes={false}
selectedStatus={selectedStatus}
setSelectedStatus={setSelectedStatus}
selectedState={selectedState}
setSelectedState={setSelectedState}
tagOptions={tags ?? []}
selectedTags={selectedTags}
setSelectedTags={setSelectedTags}
onClearFilters={handleClearFilters}
/>
<TextField
placeholder={t("pages.uptime.filters.search.placeholder")}
value={search}
onChange={(event) => {
setSearch(event.target.value);
}}
/>
</Stack>
{selectedTags.length > 0 && (
<Stack
direction={isSmall ? "column" : "row"}
alignItems={isSmall ? "flex-start" : "center"}
flexWrap="wrap"
gap={theme.spacing(SPACING.XL)}
>
<Typography color={theme.palette.text.secondary}>
{t("pages.uptime.filters.activeTags")}
</Typography>
{selectedTags.map((tagId) => {
const tag = tags?.find((t) => t.id === tagId);
if (!tag) return null;
return (
<ColoredLabel
key={tag.id}
text={tag.name}
color={tag.color}
/>
);
})}
</Stack>
)}
<PageSpeedMonitorsTable
monitors={monitors || []}
tags={tags ?? []}
refetch={refetch}
setSelectedMonitor={setSelectedMonitor}
sortField={sortField}
+361 -302
View File
@@ -1,4 +1,4 @@
import { BasePage, ConfigBox } from "@/Components/design-elements";
import { BasePage, ConfigBox, StepProgress } from "@/Components/design-elements";
import Stack from "@mui/material/Stack";
import { logger } from "@/Utils/logger";
import { SPACING, LAYOUT } from "@/Utils/Theme/constants";
@@ -26,6 +26,7 @@ import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useStatusPageForm } from "@/Hooks/useStatusPageForm";
import type { StatusPageFormData } from "@/Validation/statusPage";
import { statusPageStepCount, statusPageStepFieldsFor } from "@/Validation/statusPage";
import { useGet, usePost, usePut, useDelete } from "@/Hooks/UseApi";
import type { Monitor } from "@/Types/Monitor";
import type { MonitorDisplayType, StatusPageResponse } from "@/Types/StatusPage";
@@ -86,13 +87,27 @@ const CreateStatusPage = () => {
defaultValues: defaults,
});
const { control, reset, handleSubmit } = form;
const { control, reset, handleSubmit, trigger } = form;
// Reset form when defaults change (from fetched data)
useEffect(() => {
reset(defaults);
}, [defaults, reset]);
// Multi-step wizard, create only. The configure (edit) flow keeps the flat
// form, where showStep() is always true.
const [currentStep, setCurrentStep] = useState(0);
const showStep = (step: number) => !isCreate || currentStep === step;
const totalSteps = statusPageStepCount();
const handleNext = async () => {
const isValid = await trigger(statusPageStepFieldsFor(currentStep));
if (isValid) {
setCurrentStep((step) => step + 1);
}
};
const handleBack = () => setCurrentStep((step) => step - 1);
const watchedMonitorIds: string[] = form.watch("monitors") ?? [];
const computedTypes: MonitorDisplayType[] = useMemo(() => {
const selectedMonitors = (watchedMonitorIds ?? [])
@@ -200,315 +215,334 @@ const CreateStatusPage = () => {
onSubmit={handleSubmit(onSubmit, onError)}
>
{!isCreate && <HeaderConfigStatusControls onDelete={handleDeleteClick} />}
<ConfigBox
title={t("pages.statusPages.form.access.title")}
subtitle={t("pages.statusPages.form.access.description")}
rightContent={
<Stack
direction="row"
alignItems="center"
spacing={theme.spacing(SPACING.MD)}
>
<Controller
name="isPublished"
control={control}
render={({ field }) => (
<SwitchComponent
checked={field.value ?? false}
onChange={(e) => field.onChange(e.target.checked)}
/>
)}
/>
<Typography>
{t("pages.statusPages.form.access.option.published.name")}
</Typography>
</Stack>
}
/>
<ConfigBox
title={t("pages.statusPages.form.basicInfo.title")}
subtitle={t("pages.statusPages.form.basicInfo.description")}
rightContent={
<Stack spacing={theme.spacing(LAYOUT.MD)}>
<Controller
name="companyName"
control={control}
render={({ field, fieldState }) => (
<TextField
{...field}
fieldLabel={t("pages.statusPages.form.basicInfo.option.name.label")}
placeholder={t(
"pages.statusPages.form.basicInfo.option.name.placeholder"
)}
error={!!fieldState.error}
helperText={fieldState.error?.message}
/>
)}
/>
<Controller
name="url"
control={control}
render={({ field, fieldState }) => (
<TextField
{...field}
fieldLabel={t("pages.statusPages.form.basicInfo.option.url.label")}
placeholder={t(
"pages.statusPages.form.basicInfo.option.url.placeholder"
)}
error={!!fieldState.error}
helperText={fieldState.error?.message}
/>
)}
/>
</Stack>
}
/>
<ConfigBox
title={t("pages.statusPages.form.monitors.title")}
subtitle={t("pages.statusPages.form.monitors.description")}
rightContent={
<Controller
name="monitors"
control={control}
render={({ field, fieldState }) => {
const selectedMonitors = field.value
.map((id: string) => monitors.find((m) => m.id === id))
.filter((m): m is Monitor => m !== undefined);
const handleDragEnd = (result: DropResult) => {
if (!result.destination) return;
const reordered = Array.from(field.value);
const [removed] = reordered.splice(result.source.index, 1);
reordered.splice(result.destination.index, 0, removed);
field.onChange(reordered);
};
return (
<Stack spacing={theme.spacing(LAYOUT.MD)}>
<Autocomplete
multiple
options={monitors}
getOptionLabel={(option: Monitor) => option.name}
value={selectedMonitors}
onChange={(_, newValue) => {
field.onChange(newValue.map((m: Monitor) => m.id));
}}
fieldLabel={t(
"pages.statusPages.form.monitors.option.monitors.label"
)}
renderInput={(params) => (
<TextField
{...params}
placeholder={
selectedMonitors.length === 0
? t(
"pages.statusPages.form.monitors.option.monitors.placeholder"
)
: ""
}
error={!!fieldState.error}
helperText={fieldState.error?.message}
/>
)}
/>
{selectedMonitors.length > 0 && (
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId="monitors-list">
{(provided) => (
<Stack
{...provided.droppableProps}
ref={provided.innerRef}
>
{selectedMonitors.map((monitor, index) => (
<Draggable
key={monitor.id}
draggableId={monitor.id}
index={index}
>
{(provided) => (
<Stack
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
direction="row"
alignItems="center"
spacing={theme.spacing(LAYOUT.XS)}
padding={theme.spacing(LAYOUT.XS)}
marginTop={theme.spacing(SPACING.LG)}
borderRadius={1}
sx={{
border: `1px solid ${theme.palette.divider}`,
cursor: "grab",
"&:active": { cursor: "grabbing" },
}}
>
<GripVertical size={20} />
<Typography
flexGrow={1}
>{`${monitor.name} (${getMonitorTypeLabel(monitor.type, t)})`}</Typography>
<IconButton
size="small"
onClick={() => {
field.onChange(
field.value.filter(
(id: string) => id !== monitor.id
)
);
}}
aria-label="Remove monitor"
>
<Trash2 size={16} />
</IconButton>
</Stack>
)}
</Draggable>
))}
{provided.placeholder}
</Stack>
)}
</Droppable>
</DragDropContext>
)}
</Stack>
);
}}
/>
}
/>
<ConfigBox
title={t("pages.statusPages.form.timezone.title")}
subtitle={t("pages.statusPages.form.timezone.description")}
rightContent={
<Controller
name="timezone"
control={control}
render={({ field }) => (
<Autocomplete
options={timezones}
getOptionLabel={(option: TimezoneOption) => option.name}
value={
timezones.find((tz: TimezoneOption) => tz._id === field.value) ?? null
}
onChange={(_, newValue: TimezoneOption | null) => {
field.onChange(newValue?._id ?? "");
}}
fieldLabel={t("pages.statusPages.form.timezone.option.timezone.label")}
renderInput={(params) => (
<TextField
{...params}
placeholder={t(
"pages.statusPages.form.timezone.option.timezone.placeholder"
)}
{isCreate && (
<StepProgress
steps={totalSteps}
current={currentStep}
/>
)}
{showStep(0) && (
<ConfigBox
title={t("pages.statusPages.form.access.title")}
subtitle={t("pages.statusPages.form.access.description")}
rightContent={
<Stack
direction="row"
alignItems="center"
spacing={theme.spacing(SPACING.MD)}
>
<Controller
name="isPublished"
control={control}
render={({ field }) => (
<SwitchComponent
checked={field.value ?? false}
onChange={(e) => field.onChange(e.target.checked)}
/>
)}
/>
)}
/>
}
/>
<ConfigBox
title={t("pages.statusPages.form.appearance.title")}
subtitle={t("pages.statusPages.form.appearance.description")}
rightContent={
<Stack spacing={theme.spacing(LAYOUT.MD)}>
<Stack alignItems={"center"}>
<Typography>
{t("pages.statusPages.form.access.option.published.name")}
</Typography>
</Stack>
}
/>
)}
{showStep(0) && (
<ConfigBox
title={t("pages.statusPages.form.basicInfo.title")}
subtitle={t("pages.statusPages.form.basicInfo.description")}
rightContent={
<Stack spacing={theme.spacing(LAYOUT.MD)}>
<Controller
name="logo"
name="companyName"
control={control}
render={({ field }) => (
<ImageUpload
src={field.value?.data}
onChange={(file) => {
if (file) {
field.onChange({
data: file.src,
contentType: file.file.type,
});
} else {
field.onChange(null);
}
}}
render={({ field, fieldState }) => (
<TextField
{...field}
fieldLabel={t("pages.statusPages.form.basicInfo.option.name.label")}
placeholder={t(
"pages.statusPages.form.basicInfo.option.name.placeholder"
)}
error={!!fieldState.error}
helperText={fieldState.error?.message}
/>
)}
/>
<Controller
name="url"
control={control}
render={({ field, fieldState }) => (
<TextField
{...field}
fieldLabel={t("pages.statusPages.form.basicInfo.option.url.label")}
placeholder={t(
"pages.statusPages.form.basicInfo.option.url.placeholder"
)}
error={!!fieldState.error}
helperText={fieldState.error?.message}
/>
)}
/>
</Stack>
}
/>
)}
{showStep(0) && (
<ConfigBox
title={t("pages.statusPages.form.monitors.title")}
subtitle={t("pages.statusPages.form.monitors.description")}
rightContent={
<Controller
name="color"
name="monitors"
control={control}
render={({ field }) => (
<ColorInput
format="hex"
value={field.value}
onChange={field.onChange}
fieldLabel={t("pages.statusPages.form.appearance.option.color.label")}
/>
)}
/>
</Stack>
}
/>
<ConfigBox
title={t("pages.statusPages.form.theme.title")}
subtitle={t("pages.statusPages.form.theme.description")}
rightContent={
<Controller
name="theme"
control={control}
render={({ field: themeField }) => (
<Controller
name="themeMode"
control={control}
render={({ field: modeField }) => (
<ThemePicker
theme={themeField.value ?? "refined"}
themeMode={modeField.value ?? "auto"}
onThemeChange={themeField.onChange}
onThemeModeChange={modeField.onChange}
/>
)}
/>
)}
/>
}
/>
<ConfigBox
title={t("pages.statusPages.form.features.title")}
subtitle={t("pages.statusPages.form.features.description")}
rightContent={
<Stack spacing={theme.spacing(LAYOUT.MD)}>
<Controller
name="showCharts"
control={control}
render={({ field }) => (
<FormControlLabel
control={
<Checkbox
checked={field.value}
onChange={field.onChange}
render={({ field, fieldState }) => {
const selectedMonitors = field.value
.map((id: string) => monitors.find((m) => m.id === id))
.filter((m): m is Monitor => m !== undefined);
const handleDragEnd = (result: DropResult) => {
if (!result.destination) return;
const reordered = Array.from(field.value);
const [removed] = reordered.splice(result.source.index, 1);
reordered.splice(result.destination.index, 0, removed);
field.onChange(reordered);
};
return (
<Stack spacing={theme.spacing(LAYOUT.MD)}>
<Autocomplete
multiple
options={monitors}
getOptionLabel={(option: Monitor) => option.name}
value={selectedMonitors}
onChange={(_, newValue) => {
field.onChange(newValue.map((m: Monitor) => m.id));
}}
fieldLabel={t(
"pages.statusPages.form.monitors.option.monitors.label"
)}
renderInput={(params) => (
<TextField
{...params}
placeholder={
selectedMonitors.length === 0
? t(
"pages.statusPages.form.monitors.option.monitors.placeholder"
)
: ""
}
error={!!fieldState.error}
helperText={fieldState.error?.message}
/>
)}
/>
}
label={t("pages.statusPages.form.features.option.showCharts.label")}
/>
)}
{selectedMonitors.length > 0 && (
<DragDropContext onDragEnd={handleDragEnd}>
<Droppable droppableId="monitors-list">
{(provided) => (
<Stack
{...provided.droppableProps}
ref={provided.innerRef}
>
{selectedMonitors.map((monitor, index) => (
<Draggable
key={monitor.id}
draggableId={monitor.id}
index={index}
>
{(provided) => (
<Stack
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
direction="row"
alignItems="center"
spacing={theme.spacing(LAYOUT.XS)}
padding={theme.spacing(LAYOUT.XS)}
marginTop={theme.spacing(SPACING.LG)}
borderRadius={1}
sx={{
border: `1px solid ${theme.palette.divider}`,
cursor: "grab",
"&:active": { cursor: "grabbing" },
}}
>
<GripVertical size={20} />
<Typography
flexGrow={1}
>{`${monitor.name} (${getMonitorTypeLabel(monitor.type, t)})`}</Typography>
<IconButton
size="small"
onClick={() => {
field.onChange(
field.value.filter(
(id: string) => id !== monitor.id
)
);
}}
aria-label="Remove monitor"
>
<Trash2 size={16} />
</IconButton>
</Stack>
)}
</Draggable>
))}
{provided.placeholder}
</Stack>
)}
</Droppable>
</DragDropContext>
)}
</Stack>
);
}}
/>
}
/>
)}
{showStep(0) && (
<ConfigBox
title={t("pages.statusPages.form.timezone.title")}
subtitle={t("pages.statusPages.form.timezone.description")}
rightContent={
<Controller
name="showInfrastructure"
name="timezone"
control={control}
render={({ field }) => (
<FormControlLabel
control={
<Checkbox
checked={field.value}
onChange={field.onChange}
/>
<Autocomplete
options={timezones}
getOptionLabel={(option: TimezoneOption) => option.name}
value={
timezones.find((tz: TimezoneOption) => tz._id === field.value) ?? null
}
label={t(
"pages.statusPages.form.features.option.showInfrastructure.label"
onChange={(_, newValue: TimezoneOption | null) => {
field.onChange(newValue?._id ?? "");
}}
fieldLabel={t("pages.statusPages.form.timezone.option.timezone.label")}
renderInput={(params) => (
<TextField
{...params}
placeholder={t(
"pages.statusPages.form.timezone.option.timezone.placeholder"
)}
/>
)}
/>
)}
/>
{/* <Controller
}
/>
)}
{showStep(1) && (
<ConfigBox
title={t("pages.statusPages.form.appearance.title")}
subtitle={t("pages.statusPages.form.appearance.description")}
rightContent={
<Stack spacing={theme.spacing(LAYOUT.MD)}>
<Stack alignItems={"center"}>
<Controller
name="logo"
control={control}
render={({ field }) => (
<ImageUpload
src={field.value?.data}
onChange={(file) => {
if (file) {
field.onChange({
data: file.src,
contentType: file.file.type,
});
} else {
field.onChange(null);
}
}}
/>
)}
/>
</Stack>
<Controller
name="color"
control={control}
render={({ field }) => (
<ColorInput
format="hex"
value={field.value}
onChange={field.onChange}
fieldLabel={t("pages.statusPages.form.appearance.option.color.label")}
/>
)}
/>
</Stack>
}
/>
)}
{showStep(1) && (
<ConfigBox
title={t("pages.statusPages.form.theme.title")}
subtitle={t("pages.statusPages.form.theme.description")}
rightContent={
<Controller
name="theme"
control={control}
render={({ field: themeField }) => (
<Controller
name="themeMode"
control={control}
render={({ field: modeField }) => (
<ThemePicker
theme={themeField.value ?? "refined"}
themeMode={modeField.value ?? "auto"}
onThemeChange={themeField.onChange}
onThemeModeChange={modeField.onChange}
/>
)}
/>
)}
/>
}
/>
)}
{showStep(1) && (
<ConfigBox
title={t("pages.statusPages.form.features.title")}
subtitle={t("pages.statusPages.form.features.description")}
rightContent={
<Stack spacing={theme.spacing(LAYOUT.MD)}>
<Controller
name="showCharts"
control={control}
render={({ field }) => (
<FormControlLabel
control={
<Checkbox
checked={field.value}
onChange={field.onChange}
/>
}
label={t("pages.statusPages.form.features.option.showCharts.label")}
/>
)}
/>
<Controller
name="showInfrastructure"
control={control}
render={({ field }) => (
<FormControlLabel
control={
<Checkbox
checked={field.value}
onChange={field.onChange}
/>
}
label={t(
"pages.statusPages.form.features.option.showInfrastructure.label"
)}
/>
)}
/>
{/* <Controller
name="showUptimePercentage"
control={control}
render={({ field }) => (
@@ -542,21 +576,46 @@ const CreateStatusPage = () => {
/>
)}
/> */}
</Stack>
}
/>
</Stack>
}
/>
)}
<Stack
direction="row"
justifyContent="flex-end"
justifyContent={isCreate ? "space-between" : "flex-end"}
>
<Button
loading={isSubmitting}
type="submit"
variant="contained"
color="primary"
>
{t("common.buttons.save")}
</Button>
{isCreate && (
<Button
type="button"
variant="outlined"
color="secondary"
disabled={currentStep === 0}
onClick={handleBack}
>
{t("common.buttons.back")}
</Button>
)}
{isCreate && currentStep < totalSteps - 1 ? (
<Button
key="wizard-next"
type="button"
variant="contained"
color="primary"
onClick={handleNext}
>
{t("common.buttons.next")}
</Button>
) : (
<Button
key="wizard-save"
loading={isSubmitting}
type="submit"
variant="contained"
color="primary"
>
{t("common.buttons.save")}
</Button>
)}
</Stack>
<Dialog
open={isDeleteDialogOpen}
@@ -0,0 +1,109 @@
import { ActionsMenu, type ActionMenuItem } from "@/Components/actions-menu";
import Typography from "@mui/material/Typography";
import Box from "@mui/material/Box";
import type { Header } from "@/Components/design-elements/Table";
import { Table, ColoredLabel } from "@/Components/design-elements";
import { Pagination } from "@/Components/design-elements/Table";
import { useClientPagination } from "@/Hooks/useClientPagination";
import type { Tag } from "@/Types/Tag";
import { useNavigate } from "react-router";
import { useTranslation } from "react-i18next";
import { useTheme } from "@mui/material";
interface TagsTableProps {
tags: Tag[];
setSelectedTag: Function;
}
export const TagsTable = ({ tags, setSelectedTag }: TagsTableProps) => {
const navigate = useNavigate();
const { t } = useTranslation();
const theme = useTheme();
const { pagedRows, paginationProps } = useClientPagination(tags);
const getActions = (tag: Tag): ActionMenuItem[] => {
return [
{
id: 1,
label: t("pages.common.monitors.actions.configure"),
action: () => {
navigate(`/tags/configure/${tag.id}`);
},
closeMenu: true,
},
{
id: 2,
label: (
<Typography color={theme.palette.error.main}>
{t("pages.common.monitors.actions.delete")}
</Typography>
),
action: async () => {
setSelectedTag(tag);
},
closeMenu: true,
},
];
};
const getHeaders = () => {
const headers: Header<Tag>[] = [
{
id: "name",
content: t("common.table.headers.name"),
render: (row) => {
return <Typography>{row?.name}</Typography>;
},
},
{
id: "color",
content: t("pages.tags.table.headers.color"),
render: (row) => {
return <Typography textTransform={"capitalize"}>{row?.color}</Typography>;
},
},
{
id: "chip",
content: t("pages.tags.table.headers.appearance"),
render: (row) => {
return (
<Box
display="flex"
justifyContent="center"
>
<ColoredLabel
text={row.name}
color={row.color}
/>
</Box>
);
},
},
{
id: "actions",
content: t("common.table.headers.actions"),
render: (row) => {
return <ActionsMenu items={getActions(row)} />;
},
},
];
return headers;
};
return (
<>
<Table
headers={getHeaders()}
data={pagedRows}
onRowClick={(row) => {
navigate(`/tags/configure/${row.id}`);
}}
/>
{tags.length > 0 && <Pagination {...paginationProps} />}
</>
);
};
+118
View File
@@ -0,0 +1,118 @@
import { BasePage, ConfigBox } from "@/Components/design-elements";
import { TextField, Button, ColorInput } from "@/Components/inputs";
import Stack from "@mui/material/Stack";
import { useTheme } from "@mui/material/styles";
import { useEffect } from "react";
import { useParams } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { Controller, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { mutate } from "swr";
import { useGet, usePost, usePatch } from "@/Hooks/UseApi";
import type { TagFormData } from "@/Validation/tag";
import type { Tag } from "@/Types/Tag";
import { useTranslation } from "react-i18next";
import { useTagsForm } from "@/Hooks/useTagsForm";
const TagsCreatePage = () => {
const { t } = useTranslation();
const theme = useTheme();
const navigate = useNavigate();
const { tagId } = useParams<{ tagId: string }>();
const isEditMode = Boolean(tagId);
const { data: existingTag } = useGet<Tag>(isEditMode ? `/tags/${tagId}` : null);
const { post, loading: isSubmitting } = usePost<TagFormData, Tag>();
const { patch, loading: isPatching } = usePatch<TagFormData, Tag>();
const { schema, defaults } = useTagsForm({ data: existingTag });
const form = useForm<TagFormData>({
resolver: zodResolver(schema),
defaultValues: defaults,
});
const { control, reset, handleSubmit } = form;
useEffect(() => {
reset(defaults);
}, [defaults, reset]);
const onSubmit = async (data: TagFormData) => {
const result = isEditMode
? await patch(`/tags/${tagId}`, data)
: await post("/tags", data);
if (result?.success) {
await mutate((key) => typeof key === "string" && key.startsWith("/tags"));
navigate("/tags");
}
};
return (
<BasePage
component="form"
onSubmit={handleSubmit(onSubmit)}
>
<ConfigBox
title={t("pages.tags.form.name.title")}
subtitle={t("pages.tags.form.name.description")}
rightContent={
<Controller
name="name"
control={control}
defaultValue={defaults.name}
render={({ field, fieldState }) => (
<TextField
{...field}
type="text"
fieldLabel={t("pages.tags.form.name.optionName")}
placeholder={t("pages.tags.form.name.placeholder")}
fullWidth
error={!!fieldState.error}
helperText={fieldState.error?.message ?? ""}
/>
)}
/>
}
/>
<ConfigBox
title={t("pages.tags.form.color.title")}
subtitle={t("pages.tags.form.color.description")}
rightContent={
<Controller
name="color"
control={control}
defaultValue={defaults.color}
render={({ field }) => (
<ColorInput
format="hex"
value={field.value}
onChange={field.onChange}
fieldLabel={t("pages.tags.form.color.optionName")}
/>
)}
/>
}
/>
<Stack
direction="row"
justifyContent="flex-end"
spacing={theme.spacing(2)}
>
<Button
loading={isSubmitting || isPatching}
type="submit"
variant="contained"
color="primary"
>
{t("common.buttons.save")}
</Button>
</Stack>
</BasePage>
);
};
export default TagsCreatePage;
+72
View File
@@ -0,0 +1,72 @@
import { BasePageWithStates } from "@/Components/design-elements";
import { TagsTable } from "@/Pages/Tags/components/TagsTable";
import { Dialog } from "@/Components/inputs";
import { HeaderCreate } from "@/Components/common";
import { useState } from "react";
import { useGet, useDelete } from "@/Hooks/UseApi";
import { useTranslation } from "react-i18next";
import type { Tag } from "@/Types/Tag";
import { useIsAdmin } from "@/Hooks/useIsAdmin";
const TagsPage = () => {
const { t } = useTranslation();
const isAdmin = useIsAdmin();
const [selectedTag, setSelectedTag] = useState<Tag | null>(null);
const isDialogOpen = Boolean(selectedTag);
const {
data: tags,
isLoading,
isValidating,
error,
refetch,
} = useGet<Tag[]>("/tags/team", {}, { keepPreviousData: true });
const { deleteFn, loading: isDeleting } = useDelete();
const handleConfirm = async () => {
if (!selectedTag) return;
await deleteFn(`/tags/${selectedTag.id}`);
setSelectedTag(null);
refetch();
};
const handleCancel = () => {
setSelectedTag(null);
};
return (
<BasePageWithStates
headerKey="tags"
page={t("pages.tags.fallback.title")}
description={t("pages.tags.fallback.description")}
loading={isLoading || isValidating}
error={!!error}
totalCount={tags?.length ?? 0}
actionButtonText={t("pages.tags.fallback.actionButton")}
actionLink="/tags/create"
>
<HeaderCreate
path="/tags/create"
isLoading={isLoading || isValidating}
isAdmin={isAdmin}
/>
<TagsTable
tags={tags ?? []}
setSelectedTag={setSelectedTag}
/>
<Dialog
open={isDialogOpen}
title={t("common.dialogs.delete.title")}
content={t("common.dialogs.delete.description")}
onConfirm={handleConfirm}
onCancel={handleCancel}
loading={isDeleting}
/>
</BasePageWithStates>
);
};
export default TagsPage;
@@ -6,6 +6,7 @@ import {
type Header,
Pagination,
StatusLabel,
ColoredLabel,
} from "@/Components/design-elements";
import { HeatmapResponseTime, HistogramResponseTime } from "@/Components/common";
import { ActionsMenu } from "@/Components/actions-menu";
@@ -16,14 +17,18 @@ import { useTheme } from "@mui/material/styles";
import { useNavigate } from "react-router-dom";
import { usePost } from "@/Hooks/UseApi";
import { useSelector } from "react-redux";
import useMediaQuery from "@mui/material/useMediaQuery";
import type { Monitor } from "@/Types/Monitor";
import type { ActionMenuItem } from "@/Components/actions-menu";
import type { RootState } from "@/Types/state";
import { Checkbox } from "@/Components/inputs";
import type { Tag } from "@/Types/Tag";
import { SPACING } from "@/Utils/Theme/constants";
interface MonitorTableProps {
monitors: Monitor[];
tags: Tag[];
refetch: () => void;
setSelectedMonitor: (monitor: Monitor | null) => void;
sortField: string;
@@ -41,6 +46,7 @@ interface MonitorTableProps {
export const MonitorTable = ({
monitors,
tags,
refetch,
setSelectedMonitor,
sortField,
@@ -60,6 +66,7 @@ export const MonitorTable = ({
const navigate = useNavigate();
const chartType = useSelector((state: RootState) => state.ui?.chartType ?? "histogram");
const { post } = usePost<Record<string, never>, Monitor>();
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
const selectedSet = new Set(selectedRows);
const isAllSelected =
@@ -252,6 +259,33 @@ export const MonitorTable = ({
}
},
},
{
id: "tags",
content: t("common.table.headers.tags"),
render: (row) => {
if (row.tags.length === 0) return "-";
return (
<Stack
justifyContent={"center"}
direction={isSmall ? "column" : "row"}
gap={theme.spacing(SPACING.LG)}
>
{row.tags.map((tag) => {
const fullTag = tags.find((t) => t.id === tag);
if (!fullTag) return null;
return (
<ColoredLabel
key={fullTag.id}
text={fullTag.name}
color={fullTag.color}
/>
);
})}
</Stack>
);
},
},
{
id: "type",
mobileLabel: t("common.table.headers.type"),
+43 -3
View File
@@ -3,17 +3,19 @@ import {
HeaderMonitorsSummary,
BulkActionsBar,
} from "@/Components/monitors";
import { MonitorBasePageWithStates } from "@/Components/design-elements";
import { MonitorBasePageWithStates, ColoredLabel } from "@/Components/design-elements";
import { TextField, Dialog, Button } from "@/Components/inputs";
import Stack from "@mui/material/Stack";
import { MonitorTable } from "@/Pages/Uptime/Monitors/Components/UptimeMonitorsTable";
import { HeaderCreate } from "@/Components/common";
import { Play, Pause } from "lucide-react";
import Typography from "@mui/material/Typography";
import { useTranslation } from "react-i18next";
import useMediaQuery from "@mui/material/useMediaQuery";
import { useGet, useDelete } from "@/Hooks/UseApi";
import type { Monitor, MonitorType, MonitorsWithChecksResponse } from "@/Types/Monitor";
import type { Tag } from "@/Types/Tag";
import { useState, useMemo, useCallback } from "react";
import { useSelector, useDispatch } from "react-redux";
import { setRowsPerPage } from "@/Features/UI/uiSlice.js";
@@ -22,6 +24,7 @@ import type { RootState } from "@/Types/state";
import { useTheme } from "@mui/material";
import useDebounce from "@/Hooks/useDebounce";
import { useBulkMonitorActions } from "@/Hooks/useBulkMonitorActions";
import { SPACING } from "@/Utils/Theme/constants";
const UptimeMonitorsPage = () => {
const { t } = useTranslation();
@@ -38,6 +41,7 @@ const UptimeMonitorsPage = () => {
const [selectedTypes, setSelectedTypes] = useState<MonitorType[]>([]);
const [selectedStatus, setSelectedStatus] = useState<string>("");
const [selectedState, setSelectedState] = useState<string>("");
const [selectedTags, setSelectedTags] = useState<string[]>([]);
const [page, setPage] = useState<number>(0);
const [search, setSearch] = useState<string>("");
const [sortField, setSortField] = useState<string>("");
@@ -83,6 +87,7 @@ const UptimeMonitorsPage = () => {
const monitorsWithChecksUrl = useMemo(() => {
const params = new URLSearchParams();
effectiveTypes.forEach((type) => params.append("type", type));
selectedTags.forEach((tagId) => params.append("tags", tagId));
params.append("limit", "25");
if (page !== undefined) params.append("page", String(page));
if (rowsPerPage) params.append("rowsPerPage", String(rowsPerPage));
@@ -90,7 +95,7 @@ const UptimeMonitorsPage = () => {
if (field) params.append("field", field);
if (sortOrder) params.append("order", sortOrder);
return `/monitors/team/with-checks?${params.toString()}`;
}, [effectiveTypes, page, rowsPerPage, filter, field, sortOrder]);
}, [effectiveTypes, selectedTags, page, rowsPerPage, filter, field, sortOrder]);
const {
data: monitorsWithChecksData,
@@ -103,6 +108,8 @@ const UptimeMonitorsPage = () => {
{ refreshInterval: 5000, keepPreviousData: true }
);
const { data: tags } = useGet<Tag[]>("/tags/team");
const {
monitors: monitorsWithChecks,
summary,
@@ -126,6 +133,7 @@ const UptimeMonitorsPage = () => {
setSelectedTypes([]);
setSelectedStatus("");
setSelectedState("");
setSelectedTags([]);
setSearch("");
}, []);
@@ -144,7 +152,11 @@ const UptimeMonitorsPage = () => {
// Check if any filters are active
const hasActiveFilters = Boolean(
selectedTypes.length > 0 || selectedStatus || selectedState || search
selectedTypes.length > 0 ||
selectedStatus ||
selectedState ||
selectedTags.length > 0 ||
search
);
// Show empty state only when there are truly no monitors (not just filtered out)
@@ -183,6 +195,9 @@ const UptimeMonitorsPage = () => {
setSelectedStatus={setSelectedStatus}
selectedState={selectedState}
setSelectedState={setSelectedState}
tagOptions={tags ?? []}
selectedTags={selectedTags}
setSelectedTags={setSelectedTags}
onClearFilters={handleClearFilters}
/>
<TextField
@@ -194,6 +209,30 @@ const UptimeMonitorsPage = () => {
/>
</Stack>
{selectedTags.length > 0 && (
<Stack
direction={isSmall ? "column" : "row"}
alignItems={isSmall ? "flex-start" : "center"}
flexWrap="wrap"
gap={theme.spacing(SPACING.XL)}
>
<Typography color={theme.palette.text.secondary}>
{t("pages.uptime.filters.activeTags")}
</Typography>
{selectedTags.map((tagId) => {
const tag = tags?.find((t) => t.id === tagId);
if (!tag) return null;
return (
<ColoredLabel
key={tag.id}
text={tag.name}
color={tag.color}
/>
);
})}
</Stack>
)}
{!isLoading && (
<BulkActionsBar
selectedCount={selectedRows.length}
@@ -218,6 +257,7 @@ const UptimeMonitorsPage = () => {
<MonitorTable
monitors={monitorsWithChecks || []}
tags={tags || []}
refetch={refetch}
setSelectedMonitor={setSelectedMonitor}
count={count || 0}
+18
View File
@@ -31,9 +31,14 @@ import CreateStatus from "@/Pages/StatusPage/Create/";
import StatusPages from "@/Pages/StatusPage/StatusPages";
import Status from "@/Pages/StatusPage/Status";
// Notifications
import Notifications from "@/Pages/Notifications";
import CreateNotifications from "@/Pages/Notifications/create";
// Tags
import Tags from "@/Pages/Tags";
import CreateTags from "@/Pages/Tags/create";
// Settings
import Account from "@/Pages/Account";
import EditUser from "@/Pages/Account/EditUser";
@@ -159,6 +164,19 @@ const Routes = () => {
path="notifications/configure/:notificationId"
element={<CreateNotifications />}
/>
<Route
path="tags"
element={<Tags />}
/>
<Route
path="tags/create"
element={<CreateTags />}
/>
<Route
path="tags/configure/:tagId"
element={<CreateTags />}
/>
<Route
path="maintenance"
+19 -12
View File
@@ -1,28 +1,17 @@
export interface Diagnostics {
osStats: OsStats;
memoryUsage: MemoryUsage;
cpuUsage: CpuUsage;
v8HeapStats: V8HeapStats;
eventLoopDelayMs: number;
uptimeMs: number;
mongoStats: MongoStats;
}
export interface OsStats {
freeMemoryBytes: number;
totalMemoryBytes: number;
}
export interface MemoryUsage {
rss: number;
heapTotal: number;
heapUsed: number;
external: number;
arrayBuffers: number;
}
export interface CpuUsage {
userUsageMs: number;
systemUsageMs: number;
usagePercentage: number;
}
@@ -31,3 +20,21 @@ export interface V8HeapStats {
usedHeapSizeBytes: number;
heapSizeLimitBytes: number;
}
export interface MongoStats {
readyState: number;
host: string;
port: number;
dbName: string;
totalSize: number;
collections: CollectionStats[];
}
export interface CollectionStats {
name: string;
documentCount: number;
storageSize: number;
totalIndexSize: number;
totalSize: number;
bucketCount?: number;
}
+1
View File
@@ -70,6 +70,7 @@ export interface Monitor {
interval: number;
uptimePercentage?: number;
notifications: string[];
tags: string[];
secret?: string;
cpuAlertThreshold: number;
cpuAlertCounter: number;
+2
View File
@@ -9,6 +9,7 @@ export const NotificationChannels = [
"telegram",
"pushover",
"twilio",
"ntfy",
] as const;
export type NotificationChannel = (typeof NotificationChannels)[number];
@@ -25,6 +26,7 @@ export interface Notification {
accessToken?: string;
accountSid?: string;
twilioPhoneNumber?: string;
topic?: string;
createdAt: string;
updatedAt: string;
}
+18 -5
View File
@@ -1,5 +1,14 @@
import type { MonitorType } from "./Monitor";
export const QueueModes = ["primary", "worker"] as const;
export type QueueMode = (typeof QueueModes)[number];
export type QueueWorker = {
workerId: string; // hostname:pid:uuid
mode: QueueMode;
lastSeenAt: number; // epoch ms of the last heartbeat
};
export interface QueueJobFailure {
monitorId: string | number;
monitorUrl: string | null;
@@ -9,23 +18,26 @@ export interface QueueJobFailure {
failReason: string | null;
}
export interface QueueMetrics {
export type QueueMetrics = {
jobs: number;
activeJobs: number;
failingJobs: number;
jobsWithFailures: QueueJobFailure[];
totalRuns: number;
totalFailures: number;
}
workers: QueueWorker[];
};
export interface QueueJobSummary {
export type QueueJobSummary = {
monitorId: string | number;
monitorUrl: string | null;
monitorType: MonitorType | null;
monitorType: string | null;
monitorInterval: number | null;
monitorGeoInterval: number | null;
monitorActive: boolean | null;
active: boolean;
lockedBy: string | null;
lockedUntil: number | null;
lockedAt: number | null;
runCount: number;
failCount: number;
@@ -35,9 +47,10 @@ export interface QueueJobSummary {
lastRunTook: number | null;
lastFailedAt: number | null;
repeat: number | null;
}
};
export interface QueueData {
jobs: QueueJobSummary[];
count: number;
metrics: QueueMetrics;
}
+8
View File
@@ -0,0 +1,8 @@
export interface Tag {
id: string;
teamId: string;
name: string;
color: string;
createdAt: string;
updatedAt: string;
}
+79 -20
View File
@@ -1,6 +1,12 @@
import { z } from "zod";
import type { FieldPath } from "react-hook-form";
import { GeoContinents } from "@/Types/GeoCheck";
import { DnsRecordTypes, PageSpeedStrategies } from "@/Types/Monitor";
import { DnsRecordTypes, PageSpeedStrategies, type MonitorType } from "@/Types/Monitor";
// Wizard step a field is validated on. Attached inline to each field below so
// the grouping lives next to the field definition; unannotated fields default
// to step 0. Read via `stepFieldsFor`.
export const monitorStepRegistry = z.registry<{ step: number }>();
// URL schema with custom error message
const urlSchema = z.url({ message: "Please enter a valid URL" });
@@ -12,39 +18,60 @@ const baseSchema = z.object({
.min(1, "Monitor name is required")
.max(50, "Monitor name must be at most 50 characters"),
description: z.string().optional(),
interval: z.number().min(15000, "Interval must be at least 15 seconds"),
notifications: z.array(z.string()),
interval: z
.number()
.min(15000, "Interval must be at least 15 seconds")
.register(monitorStepRegistry, { step: 1 }),
notifications: z.array(z.string()).register(monitorStepRegistry, { step: 1 }),
tags: z.array(z.string()).register(monitorStepRegistry, { step: 1 }),
statusWindowSize: z
.number({ message: "Status window size is required" })
.min(1, "Status window size must be at least 1")
.max(25, "Status window size must be at most 25"),
.max(25, "Status window size must be at most 25")
.register(monitorStepRegistry, { step: 1 }),
statusWindowThreshold: z
.number({ message: "Threshold percentage is required" })
.min(1, "Incident percentage must be at least 1")
.max(100, "Incident percentage must be at most 100"),
geoCheckEnabled: z.boolean().optional(),
geoCheckLocations: z.array(z.enum(GeoContinents)).optional(),
.max(100, "Incident percentage must be at most 100")
.register(monitorStepRegistry, { step: 1 }),
});
// Geo-distributed check fields. Only http and ping support geo checks
// (GeoCheckSupportedTypes), so these live on those schemas rather than the base
// — that way each type's step count is exactly what its own fields declare.
const geoCheckFields = {
geoCheckEnabled: z.boolean().optional().register(monitorStepRegistry, { step: 2 }),
geoCheckLocations: z
.array(z.enum(GeoContinents))
.optional()
.register(monitorStepRegistry, { step: 2 }),
geoCheckInterval: z
.number()
.min(300000, "Interval must be at least 5 minutes")
.optional(),
});
.optional()
.register(monitorStepRegistry, { step: 2 }),
};
// HTTP monitor schema
const httpSchema = baseSchema.extend({
type: z.literal("http"),
url: urlSchema,
ignoreTlsErrors: z.boolean(),
useAdvancedMatching: z.boolean(),
matchMethod: z.enum(["equal", "include", "regex", ""]).optional(),
expectedValue: z.string().optional(),
jsonPath: z.string().optional(),
ignoreTlsErrors: z.boolean().register(monitorStepRegistry, { step: 2 }),
useAdvancedMatching: z.boolean().register(monitorStepRegistry, { step: 2 }),
matchMethod: z
.enum(["equal", "include", "regex", ""])
.optional()
.register(monitorStepRegistry, { step: 2 }),
expectedValue: z.string().optional().register(monitorStepRegistry, { step: 2 }),
jsonPath: z.string().optional().register(monitorStepRegistry, { step: 2 }),
...geoCheckFields,
});
// Ping monitor schema
const pingSchema = baseSchema.extend({
type: z.literal("ping"),
url: z.string().min(1, "Host is required"),
...geoCheckFields,
});
// Port monitor schema
@@ -83,7 +110,7 @@ const grpcSchema = baseSchema.extend({
.min(1, "Port must be at least 1")
.max(65535, "Port must be at most 65535"),
grpcServiceName: z.string().optional(),
ignoreTlsErrors: z.boolean(),
ignoreTlsErrors: z.boolean().register(monitorStepRegistry, { step: 2 }),
});
// PageSpeed monitor schema
@@ -101,19 +128,23 @@ const hardwareSchema = baseSchema.extend({
cpuAlertThreshold: z
.number()
.min(0, "CPU threshold must be at least 0")
.max(100, "CPU threshold must be at most 100"),
.max(100, "CPU threshold must be at most 100")
.register(monitorStepRegistry, { step: 1 }),
memoryAlertThreshold: z
.number()
.min(0, "Memory threshold must be at least 0")
.max(100, "Memory threshold must be at most 100"),
.max(100, "Memory threshold must be at most 100")
.register(monitorStepRegistry, { step: 1 }),
diskAlertThreshold: z
.number()
.min(0, "Disk threshold must be at least 0")
.max(100, "Disk threshold must be at most 100"),
.max(100, "Disk threshold must be at most 100")
.register(monitorStepRegistry, { step: 1 }),
tempAlertThreshold: z
.number()
.min(0, "Temperature threshold must be at least 0")
.max(150, "Temperature threshold must be at most 150"),
.max(150, "Temperature threshold must be at most 150")
.register(monitorStepRegistry, { step: 1 }),
selectedDisks: z.array(z.string()),
});
@@ -121,7 +152,7 @@ const hardwareSchema = baseSchema.extend({
const websocketSchema = baseSchema.extend({
type: z.literal("websocket"),
url: z.string().min(1, "WebSocket URL is required"),
ignoreTlsErrors: z.boolean(),
ignoreTlsErrors: z.boolean().register(monitorStepRegistry, { step: 2 }),
});
// Hostname (FQDN) — labels of 1-63 alphanumerics/hyphens separated by dots,
@@ -163,6 +194,34 @@ export const monitorSchema = z.discriminatedUnion("type", [
export type MonitorFormData = z.infer<typeof monitorSchema>;
const optionForType = (type: MonitorType) =>
monitorSchema.options.find((o) => o.shape.type.value === type);
// The step each field of a type is validated on, from the per-field
// `monitorStepRegistry` metadata (unannotated fields default to step 0).
const fieldStepsFor = (
type: MonitorType
): [name: FieldPath<MonitorFormData>, step: number][] =>
Object.entries(optionForType(type)?.shape ?? {}).map(([name, field]) => [
name as FieldPath<MonitorFormData>,
monitorStepRegistry.get(field)?.step ?? 0,
]);
// The selected type's fields that belong to a given wizard step. Derived from
// the schema so it can't drift from the field definitions.
export const stepFieldsFor = (
type: MonitorType,
step: number
): FieldPath<MonitorFormData>[] =>
fieldStepsFor(type)
.filter(([, fieldStep]) => fieldStep === step)
.map(([name]) => name);
// Number of wizard steps a type has, derived from the highest step its fields
// declare. A type with no step-2 fields is a 2-step form, and so on.
export const monitorStepCount = (type: MonitorType): number =>
Math.max(0, ...fieldStepsFor(type).map(([, step]) => step)) + 1;
// Type-specific schemas exported for individual use
export {
httpSchema,
+7
View File
@@ -70,6 +70,12 @@ const twilioSchema = baseSchema.extend({
twilioPhoneNumber: z.string().min(1, "Twilio phone number is required"),
});
const ntfySchema = baseSchema.extend({
type: z.literal("ntfy"),
address: z.string().min(1, "Server URL is required").url("Please enter a valid URL"),
topic: z.string().min(1, "Topic is required"),
});
export const notificationSchema = z.discriminatedUnion("type", [
emailSchema,
slackSchema,
@@ -81,6 +87,7 @@ export const notificationSchema = z.discriminatedUnion("type", [
telegramSchema,
pushoverSchema,
twilioSchema,
ntfySchema,
]);
export type NotificationFormData = z.infer<typeof notificationSchema>;
+45 -9
View File
@@ -1,6 +1,14 @@
import { z } from "zod";
import type { FieldPath } from "react-hook-form";
import { STATUS_PAGE_THEMES, STATUS_PAGE_THEME_MODES } from "@/Types/StatusPage";
// Wizard step a field is validated on. Attached inline to each field below so
// the grouping lives next to the field definition; unannotated fields default
// to step 0. Read via `statusPageStepFieldsFor`. Mirrors the monitor schema's
// `monitorStepRegistry` so the create-status-page wizard can't drift from the
// field definitions.
export const statusPageStepRegistry = z.registry<{ step: number }>();
export const statusPageSchema = z.object({
companyName: z
.string()
@@ -18,23 +26,51 @@ export const statusPageSchema = z.object({
type: z
.array(z.enum(["uptime", "infrastructure"]))
.min(1, "At least one type is required"),
color: z.string().min(1, "Color is required"),
color: z.string().min(1, "Color is required").register(statusPageStepRegistry, {
step: 1,
}),
monitors: z.array(z.string()).min(1, "At least one monitor is required"),
isPublished: z.boolean(),
showCharts: z.boolean(),
showUptimePercentage: z.boolean(),
showAdminLoginLink: z.boolean(),
showInfrastructure: z.boolean(),
customCSS: z.string().optional(),
theme: z.enum(STATUS_PAGE_THEMES).optional(),
themeMode: z.enum(STATUS_PAGE_THEME_MODES).optional(),
showCharts: z.boolean().register(statusPageStepRegistry, { step: 1 }),
showUptimePercentage: z.boolean().register(statusPageStepRegistry, { step: 1 }),
showAdminLoginLink: z.boolean().register(statusPageStepRegistry, { step: 1 }),
showInfrastructure: z.boolean().register(statusPageStepRegistry, { step: 1 }),
customCSS: z.string().optional().register(statusPageStepRegistry, { step: 1 }),
theme: z
.enum(STATUS_PAGE_THEMES)
.optional()
.register(statusPageStepRegistry, { step: 1 }),
themeMode: z
.enum(STATUS_PAGE_THEME_MODES)
.optional()
.register(statusPageStepRegistry, { step: 1 }),
logo: z
.object({
data: z.string(),
contentType: z.string(),
})
.nullable()
.optional(),
.optional()
.register(statusPageStepRegistry, { step: 1 }),
});
export type StatusPageFormData = z.infer<typeof statusPageSchema>;
// The step each field is validated on, from the per-field
// `statusPageStepRegistry` metadata (unannotated fields default to step 0).
const fieldSteps = (): [name: FieldPath<StatusPageFormData>, step: number][] =>
Object.entries(statusPageSchema.shape).map(([name, field]) => [
name as FieldPath<StatusPageFormData>,
statusPageStepRegistry.get(field)?.step ?? 0,
]);
// The fields that belong to a given wizard step. Derived from the schema so it
// can't drift from the field definitions.
export const statusPageStepFieldsFor = (step: number): FieldPath<StatusPageFormData>[] =>
fieldSteps()
.filter(([, fieldStep]) => fieldStep === step)
.map(([name]) => name);
// Number of wizard steps, derived from the highest step its fields declare.
export const statusPageStepCount = (): number =>
Math.max(0, ...fieldSteps().map(([, step]) => step)) + 1;
+10
View File
@@ -0,0 +1,10 @@
import { z } from "zod";
export const tagSchema = z.object({
name: z.string().min(1, "Please enter a tag name"),
color: z
.string()
.regex(/^#([0-9a-fA-F]{6})$/, "Color must be a 6-digit hex value (e.g. #13715B)"),
});
export type TagFormData = z.infer<typeof tagSchema>;
+142 -44
View File
@@ -73,7 +73,9 @@
"paused": "متوقف مؤقتاً",
"na": "غير متوفر",
"resolved": "تم الحل",
"responseTime": "وقت الاستجابة"
"responseTime": "وقت الاستجابة",
"idle": "خامل",
"running": "قيد التشغيل"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "نشط",
"responseTime": "وقت الاستجابة"
}
},
"statusCode": {
"down": "متوقف",
"networkError": {
"dns": "تعذر تحليل اسم المضيف (فشل بحث DNS).",
"refused": "رفض الخادم الاتصال (المنفذ مغلق أو الخدمة لا تستمع).",
"reset": "تمت إعادة تعيين الاتصال قبل تلقي استجابة.",
"timeout": "لم يستجب الخادم في الوقت المحدد (انتهت مهلة الطلب).",
"tls": "فشل تأكيد اتصال TLS أو SSL (مشكلة في الشهادة أو التشفير).",
"unknown": "لم يتم تلقي أي استجابة HTTP من الهدف."
}
}
},
"components": {
@@ -306,28 +319,28 @@
"common": {
"passwordRules": {
"length": {
"beginning": "",
"highlighted": ""
"beginning": "يجب أن يكون على الأقل",
"highlighted": "8 أحرف"
},
"lowercase": {
"beginning": "",
"highlighted": ""
"beginning": "يجب أن يحتوي على الأقل على",
"highlighted": "حرف صغير واحد"
},
"match": {
"beginning": "",
"highlighted": ""
"beginning": "كلمات المرور",
"highlighted": "يجب أن تتطابق"
},
"number": {
"beginning": "",
"highlighted": ""
"beginning": "يجب أن يحتوي على الأقل على",
"highlighted": "رقم واحد"
},
"special": {
"beginning": "",
"highlighted": ""
"beginning": "يجب أن يحتوي على الأقل على",
"highlighted": "حرف خاص واحد (!?@#$%^&*()-_=+[]{}|;:'\",./\\~`)"
},
"uppercase": {
"beginning": "",
"highlighted": ""
"beginning": "يجب أن يحتوي على الأقل على",
"highlighted": "حرف كبير واحد"
}
},
"form": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "البنية التحتية",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "تم إيقاف {{count}} شاشة مؤقتاً",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "الشاشة المحددة متوقفة مؤقتاً بالفعل",
"alreadyPaused_other": "جميع الشاشات المحددة متوقفة مؤقتاً بالفعل",
"alreadyRunning_one": "الشاشة المحددة قيد التشغيل بالفعل",
"alreadyRunning_other": "جميع الشاشات المحددة قيد التشغيل بالفعل"
"alreadyRunning_other": "جميع الشاشات المحددة قيد التشغيل بالفعل",
"partialFailure": "تم تحديث {{successCount}} من أجهزة المراقبة، ولكن فشلت مزامنة {{failedCount}} مع قائمة انتظار الوظائف"
}
}
},
@@ -483,9 +498,9 @@
},
"matchMethod": {
"label": "طريقة المطابقة",
"equal": "",
"include": "",
"regex": ""
"equal": "يساوي",
"include": "يحتوي",
"regex": "تعبير عادي"
}
},
"title": "إعدادات متقدمة"
@@ -497,15 +512,15 @@
"label": "تكرار الفحص",
"value": {
"fifteenMinutes": "15 دقيقة",
"fifteenSeconds": "",
"fifteenSeconds": "15 ثانية",
"fiveMinutes": "5 دقائق",
"fourMinutes": "",
"oneMinute": "",
"fourMinutes": "4 دقائق",
"oneMinute": "دقيقة واحدة",
"tenMinutes": "10 دقائق",
"thirtyMinutes": "30 دقيقة",
"thirtySeconds": "",
"threeMinutes": "",
"twoMinutes": ""
"thirtySeconds": "30 ثانية",
"threeMinutes": "3 دقائق",
"twoMinutes": "دقيقتان"
}
}
},
@@ -534,8 +549,8 @@
"placeholder": "https://www.google.com"
},
"game": {
"label": "",
"placeholder": ""
"label": "اختر اللعبة",
"placeholder": "حدد لعبة"
},
"port": {
"label": "المنفذ المراد مراقبته",
@@ -552,19 +567,40 @@
"captureUrl": {
"label": "نقطة نهاية Capture",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "نوع سجل DNS",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "خادم DNS",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "سطح المكتب",
"label": "الاستراتيجية",
"mobile": "الجوال"
}
},
"title": "الإعدادات العامة",
"description": {
"http": "أدخل الرابط أو عنوان IP للمراقبة (مثال: https://example.com/ أو 192.168.1.100) وأضف اسم عرض واضح يظهر في لوحة التحكم.",
"ping": "",
"port": "",
"docker": "",
"ping": "أدخل عنوان IP أو اسم المضيف للاتصال (على سبيل المثال، 192.168.1.100 أو example.com) وأضف اسماً واضحاً يظهر في لوحة المعلومات.",
"port": "أدخل عنوان URL أو IP للخادم ورقم المنفذ واسماً واضحاً يظهر في لوحة المعلومات.",
"docker": "أدخل اسم حاوية Docker أو المعرف. يمكنك استخدام اسم الحاوية (مثل my-app) أو معرف الحاوية (معرف كامل 64 حرف أو معرف قصير).",
"game": "أدخل عنوان IP أو اسم المضيف ورقم المنفذ للاختبار (مثل 192.168.1.100 أو example.com) واختر نوع اللعبة. تتطلب بعض الألعاب تفعيل استعلامات الخادم في ملف الإعدادات الخاص بها — يُرجى مراجعة وثائق خادم اللعبة. اطّلع على <gamedigLink>قائمة الألعاب المدعومة</gamedigLink> لمعرفة تفاصيل البروتوكول.",
"pagespeed": "تتبع أداء تحميل الصفحة ومؤشرات Core Web Vitals ودرجات التحسين لموقعك.",
"grpc": "أدخل اسم المضيف ومنفذ خادم gRPC، وحدد اختيارياً اسم خدمة لفحص الصحة، وأضف اسم عرض.",
"websocket": "أدخل رابط WebSocket للمراقبة (مثال: wss://example.com/socket) وأضف اسم عرض.",
"hardware": "راقب استخدام وحدة المعالجة المركزية والذاكرة والقرص ودرجة الحرارة لبنيتك التحتية. يجب أن يتضمن عنوان URL لنقطة نهاية Capture المسار الكامل ‎/api/v1/metrics‎ (مثال: ‎http://192.168.1.10:5001/api/v1/metrics‎). بالنسبة لمفتاح التفويض، الصق سلسلة الرمز فقط — لا تُضمّن كلمة 'Bearer'."
"hardware": "راقب استخدام وحدة المعالجة المركزية والذاكرة والقرص ودرجة الحرارة لبنيتك التحتية. يجب أن يتضمن عنوان URL لنقطة نهاية Capture المسار الكامل ‎/api/v1/metrics‎ (مثال: ‎http://192.168.1.10:5001/api/v1/metrics‎). بالنسبة لمفتاح التفويض، الصق سلسلة الرمز فقط — لا تُضمّن كلمة 'Bearer'.",
"dns": "راقب ما إذا كان خادم DNS محدد يستجيب."
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "استخدم HTTP(S) لمراقبة موقعك الإلكتروني أو نقطة نهاية API.",
"optionPingDescription": "استخدم ICMP Ping لمراقبة ما إذا كان الخادم متصلاً.",
"optionPortDescription": "مراقبة ما إذا كان منفذ معين على الخادم مفتوحاً.",
"title": "النوع"
"title": "النوع",
"optionDnsDescription": "راقب ما إذا كان خادم DNS محدد يستجيب."
},
"thresholds": {
"title": "حدود التنبيه",
@@ -765,7 +802,7 @@
"headers": {
"endTime": "وقت الانتهاء",
"resolutionType": "نوع الحل",
"startTime": "",
"startTime": "وقت البدء",
"statusCode": "رمز الحالة"
},
"resolvedIncidents": "الحوادث المحلولة",
@@ -809,13 +846,13 @@
}
},
"statBoxes": {
"avgCpuTemperature": "",
"cpuFrequency": "",
"cpuLogical": "",
"cpuPhysical": "",
"avgCpuTemperature": "متوسط درجة حرارة المعالج",
"cpuFrequency": "تردد المعالج",
"cpuLogical": "المعالج (المنطقي)",
"cpuPhysical": "المعالج (الفعلي)",
"disk": "القرص",
"memory": "الذاكرة",
"os": ""
"os": "نظام التشغيل"
},
"table": {
"headers": {
@@ -826,7 +863,7 @@
},
"tabs": {
"labels": {
"network": "",
"network": "الشبكة",
"overview": "نظرة عامة"
}
},
@@ -909,7 +946,8 @@
"lastFinishedAt": "آخر انتهاء في",
"lastRunTook": "مدة آخر تشغيل",
"lastFailedAt": "آخر فشل في",
"failReason": "سبب الفشل"
"failReason": "سبب الفشل",
"state": "الحالة"
}
},
"queueFallback": {
@@ -929,8 +967,8 @@
},
"table": {
"headers": {
"nextWindow": "",
"repeat": ""
"nextWindow": "النافذة التالية",
"repeat": "تكرار"
}
},
"form": {
@@ -1032,7 +1070,7 @@
},
"telegram": {
"title": "تهيئة Telegram",
"description": "",
"description": "قم بتكوين بوت Telegram الخاص بك لإرسال الإشعارات.",
"optionBotToken": "رمز البوت",
"placeholderBotToken": "123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ",
"optionChatId": "معرّف المحادثة",
@@ -1099,6 +1137,10 @@
"header": {
"title": "مراقبات PageSpeed",
"description": "تابع Core Web Vitals ودرجات Lighthouse عبر الزمن لاكتشاف التراجع قبل أن يلاحظه المستخدمون."
},
"strategy": {
"desktop": "سطح المكتب",
"mobile": "الجوال"
}
},
"settings": {
@@ -1201,7 +1243,7 @@
"email": {
"title": "إعدادات البريد الإلكتروني",
"description": "تهيئة إعدادات البريد الإلكتروني لنظامك. يُستخدم لإرسال الإشعارات والتنبيهات.",
"descriptionTransport": "",
"descriptionTransport": "هذا يبني نقل SMTP لـ NodeMailer",
"descriptionTransportLink": "اطلع على المواصفات هنا",
"option": {
"host": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "رفض الشهادات غير الصالحة: رفض الاتصالات ذات الشهادات الموقعة ذاتياً أو غير الموثوقة"
},
"displayName": {
"label": "اسم المرسل (اختياري) - الاسم الودي المعروض في حقل المرسل",
"placeholder": "إشعارات Checkmate"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "وقت التشغيل",
"responseTime": "وقت الاستجابة"
},
"chart": {
"avg": "متوسط {{value}} مللي ثانية",
"downTooltip": "متوقف",
"heatmapAria": "خريطة حرارية لزمن الاستجابة",
"max": "أقصى {{value}} مللي ثانية"
},
"status": {
"breached": "تم تجاوز الحد",
"down": "متوقف",
"initializing": "قيد التهيئة",
"maintenance": "صيانة",
"paused": "متوقف مؤقتاً",
"up": "قيد التشغيل"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "بعض الأنظمة قيد الصيانة",
"partiallyPaused": "بعض الأنظمة متوقفة مؤقتًا",
"initializing": "بعض الأنظمة قيد التهيئة",
"noMonitors": "لم يتم تكوين أي شاشة"
"noMonitors": "لم يتم تكوين أي شاشة",
"monitoringSummary": "مراقبة {{count}} خدمة · يتم التحديث كل 10 ثوانٍ",
"monitoringSummary_other": "مراقبة {{count}} خدمات · يتم التحديث كل 10 ثوانٍ"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "إظهار مقاييس البنية التحتية"
}
}
},
"theme": {
"description": "اختر النمط المرئي لصفحة الحالة العامة الخاصة بك.",
"label": "السمة",
"options": {
"bold": {
"description": "خط كبير ومسافات واثقة. رائعة للصفحات التي تركز على العلامة التجارية.",
"name": "غامق"
},
"editorial": {
"description": "عناوين بخط مسرف وفواصل دقيقة. مميز وهادئ.",
"name": "تحريري"
},
"modern": {
"description": "بجودة SaaS مع نقطة حالة نابضة وبطاقات تتلاشى بنعومة.",
"name": "حديث"
},
"refined": {
"description": "نظيف واحترافي وقريب من مظهر Checkmate الكلاسيكي.",
"name": "مكرر"
}
},
"title": "سمة الصفحة"
},
"themeMode": {
"auto": "تلقائي (اتبع جهاز الزائر)",
"dark": "داكن فقط",
"label": "وضع السمة",
"light": "فاتح فقط"
}
},
"header": {
"title": "صفحات الحالة",
"description": "انشر صفحات عامة تُظهر للعملاء وأصحاب المصلحة وقت التشغيل الفعلي وتاريخ الحوادث."
},
"editorial": {
"allUp": "جميع الأنظمة تعمل بشكل طبيعي.",
"reportTitle": "تقرير الخدمة"
},
"footer": {
"poweredBy": "مشغل بواسطة"
}
},
"uptime": {
+62 -6
View File
@@ -73,7 +73,9 @@
"paused": "pausat",
"na": "N/D",
"resolved": "Resolt",
"responseTime": "Temps de resposta"
"responseTime": "Temps de resposta",
"idle": "Inactiu",
"running": "En execució"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "Actiu",
"responseTime": "Temps de resposta"
}
},
"statusCode": {
"down": "Inactiu",
"networkError": {
"dns": "No s'ha pogut resoldre el nom d'amfitrió (la cerca DNS ha fallat).",
"refused": "El servidor ha rebutjat la connexió (port tancat o servei no actiu).",
"reset": "La connexió s'ha reiniciat abans de rebre una resposta.",
"timeout": "El servidor no ha respost a temps (s'ha esgotat el temps d'espera).",
"tls": "Ha fallat la negociació TLS o SSL (problema de certificat o xifratge).",
"unknown": "No s'ha rebut cap resposta HTTP de l'objectiu."
}
}
},
"components": {
@@ -421,7 +434,11 @@
"inviteMember": "Convidar membre",
"openSite": "Obrir lloc",
"pause": "Pausar",
"resume": "Reprendre"
"resume": "Reprendre",
"bulkSelected_one": "{{count}} monitor seleccionat",
"bulkSelected_other": "{{count}} monitors seleccionats",
"selectAll": "Selecciona tots els monitors",
"selectMonitor": "Selecciona {{name}}"
},
"statBoxes": {
"activeFor": "Actiu des de",
@@ -448,7 +465,19 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "Infraestructura",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"alreadyPaused_one": "El monitor seleccionat ja està pausat",
"alreadyPaused_other": "Tots els monitors seleccionats ja estan pausats",
"alreadyRunning_one": "El monitor seleccionat ja s'està executant",
"alreadyRunning_other": "Tots els monitors seleccionats ja s'estan executant",
"paused_one": "{{count}} monitor pausat",
"paused_other": "{{count}} monitors pausats",
"resumed_one": "{{count}} monitor reprès",
"resumed_other": "{{count}} monitors represos",
"partialFailure": "S'han actualitzat {{successCount}} monitors, però {{failedCount}} han fallat en sincronitzar-se amb la cua de tasques"
}
}
},
@@ -538,6 +567,26 @@
"captureUrl": {
"label": "Endpoint de captura",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "Tipus de registre DNS",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "Servidor DNS",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "Escriptori",
"label": "Estratègia",
"mobile": "Mòbil"
}
},
"title": "Configuració general",
@@ -550,7 +599,8 @@
"pagespeed": "Fes un seguiment del rendiment de càrrega de pàgina, Core Web Vitals i puntuacions d'optimització del teu lloc web.",
"grpc": "Introdueix el nom d'host i el port del servidor gRPC, opcionalment especifica un nom de servei per a la comprovació de salut, i afegeix un nom de visualització.",
"websocket": "Introdueix la URL de WebSocket a monitorar (p. ex., wss://example.com/socket) i afegeix un nom de visualització.",
"hardware": "Monitoritza l'ús de CPU, memòria, disc i temperatura per a la teva infraestructura. La URL de l'endpoint de captura ha d'incloure la ruta completa /api/v1/metrics (p. ex., http://192.168.1.10:5001/api/v1/metrics). Per al secret d'autorització, enganxa només la cadena del token — no incloguis 'Bearer'."
"hardware": "Monitoritza l'ús de CPU, memòria, disc i temperatura per a la teva infraestructura. La URL de l'endpoint de captura ha d'incloure la ruta completa /api/v1/metrics (p. ex., http://192.168.1.10:5001/api/v1/metrics). Per al secret d'autorització, enganxa només la cadena del token — no incloguis 'Bearer'.",
"dns": "Monitoritza si un servidor DNS específic respon."
}
},
"ignoreTls": {
@@ -587,7 +637,8 @@
"optionHttpDescription": "Usa HTTP(S) per monitorar el teu lloc web o endpoint d'API.",
"optionPingDescription": "Usa ICMP Ping per monitorar si un servidor és en línia.",
"optionPortDescription": "Monitoritza si un port específic d'un servidor és obert.",
"title": "Tipus"
"title": "Tipus",
"optionDnsDescription": "Monitoritza si un servidor DNS específic respon."
},
"thresholds": {
"title": "Llindars d'alerta",
@@ -899,7 +950,8 @@
"lastFinishedAt": "Darrera finalització",
"lastRunTook": "Durada de la darrera execució",
"lastFailedAt": "Darrera fallada",
"failReason": "Motiu de la fallada"
"failReason": "Motiu de la fallada",
"state": "Estat"
}
},
"header": {
@@ -1085,6 +1137,10 @@
"header": {
"title": "Monitors de PageSpeed",
"description": "Fes un seguiment de les Core Web Vitals i les puntuacions de Lighthouse al llarg del temps per detectar regressions abans que els usuaris ho facin."
},
"strategy": {
"desktop": "Escriptori",
"mobile": "Mòbil"
}
},
"settings": {
+130 -32
View File
@@ -73,7 +73,9 @@
"paused": "pozastaveno",
"na": "N/A",
"resolved": "Vyřešeno",
"responseTime": "Doba odezvy"
"responseTime": "Doba odezvy",
"idle": "Nečinný",
"running": "Spuštěno"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "Aktivní",
"responseTime": "Doba odezvy"
}
},
"statusCode": {
"down": "Mimo provoz",
"networkError": {
"dns": "Nepodařilo se přeložit název hostitele (vyhledávání DNS selhalo).",
"refused": "Server odmítl spojení (port je uzavřený nebo služba neposlouchá).",
"reset": "Spojení bylo resetováno před přijetím odpovědi.",
"timeout": "Server neodpověděl včas (vypršel časový limit požadavku).",
"tls": "Selhal handshake TLS nebo SSL (problém s certifikátem nebo šifrováním).",
"unknown": "Z cíle nebyla obdržena žádná odpověď HTTP."
}
}
},
"components": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "Infrastruktura",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "{{count}} monitor pozastaven",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "Vybraný monitor je již pozastaven",
"alreadyPaused_other": "Všechny vybrané monitory jsou již pozastaveny",
"alreadyRunning_one": "Vybraný monitor již běží",
"alreadyRunning_other": "Všechny vybrané monitory již běží"
"alreadyRunning_other": "Všechny vybrané monitory již běží",
"partialFailure": "Bylo aktualizováno {{successCount}} monitorů, ale u {{failedCount}} se nezdařila synchronizace s frontou úloh"
}
}
},
@@ -483,9 +498,9 @@
},
"matchMethod": {
"label": "Metoda porovnání",
"equal": "",
"include": "",
"regex": ""
"equal": "Rovná se",
"include": "Obsahuje",
"regex": "Regulární výraz"
}
},
"title": "Pokročilá nastavení"
@@ -497,15 +512,15 @@
"label": "Frekvence kontroly",
"value": {
"fifteenMinutes": "15 minut",
"fifteenSeconds": "",
"fifteenSeconds": "15 sekund",
"fiveMinutes": "5 minut",
"fourMinutes": "",
"oneMinute": "",
"fourMinutes": "4 minuty",
"oneMinute": "1 minuta",
"tenMinutes": "10 minut",
"thirtyMinutes": "30 minut",
"thirtySeconds": "",
"threeMinutes": "",
"twoMinutes": ""
"thirtySeconds": "30 sekund",
"threeMinutes": "3 minuty",
"twoMinutes": "2 minuty"
}
}
},
@@ -534,8 +549,8 @@
"placeholder": "https://www.google.com"
},
"game": {
"label": "",
"placeholder": ""
"label": "Vybrat hru",
"placeholder": "Vyberte hru"
},
"port": {
"label": "Port ke sledování",
@@ -552,19 +567,40 @@
"captureUrl": {
"label": "Endpoint Capture",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "Typ záznamu DNS",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "DNS server",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "Stolní počítač",
"label": "Strategie",
"mobile": "Mobil"
}
},
"title": "Obecná nastavení",
"description": {
"http": "Zadejte URL nebo IP adresu ke sledování (např. https://example.com/ nebo 192.168.1.100) a přidejte jasný zobrazovaný název, který se zobrazí na přehledu.",
"ping": "",
"port": "",
"docker": "",
"ping": "Zadejte IP adresu nebo název hostitele pro ping (např. 192.168.1.100 nebo example.com) a přidejte přehledný název, který se zobrazí na dashboardu.",
"port": "Zadejte URL nebo IP serveru, číslo portu a přehledný název, který se zobrazí na dashboardu.",
"docker": "Zadejte název nebo ID Docker kontejneru. Můžete použít buď název kontejneru (např. my-app) nebo ID kontejneru (úplné 64znakové ID nebo krátké ID).",
"game": "Zadejte IP adresu nebo název hostitele a číslo portu pro ping (např. 192.168.1.100 nebo example.com) a vyberte typ hry. Některé hry vyžadují povolení dotazů na server v jejich konfiguračním souboru — viz dokumentace vašeho herního serveru. Podrobnosti o protokolu najdete v <gamedigLink>seznamu podporovaných her</gamedigLink>.",
"pagespeed": "Sledujte výkon načítání stránky, metriky Core Web Vitals a hodnocení optimalizace vašeho webu.",
"grpc": "Zadejte název hostitele a port gRPC serveru, volitelně zadejte název služby pro kontrolu stavu a přidejte zobrazovaný název.",
"websocket": "Zadejte WebSocket URL ke sledování (např. wss://example.com/socket) a přidejte zobrazovaný název.",
"hardware": "Sledujte využití CPU, paměti, disku a teplotu vaší infrastruktury. URL endpointu Capture musí obsahovat celou cestu /api/v1/metrics (např. http://192.168.1.10:5001/api/v1/metrics). Do autorizačního klíče vložte pouze řetězec tokenu — nezahrnujte 'Bearer'."
"hardware": "Sledujte využití CPU, paměti, disku a teplotu vaší infrastruktury. URL endpointu Capture musí obsahovat celou cestu /api/v1/metrics (např. http://192.168.1.10:5001/api/v1/metrics). Do autorizačního klíče vložte pouze řetězec tokenu — nezahrnujte 'Bearer'.",
"dns": "Sledujte, zda konkrétní DNS server odpovídá."
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "Použijte HTTP(S) ke sledování vašeho webu nebo API koncového bodu.",
"optionPingDescription": "Použijte ICMP Ping ke sledování, zda je server online.",
"optionPortDescription": "Sledujte, zda je konkrétní port na serveru otevřen.",
"title": "Typ"
"title": "Typ",
"optionDnsDescription": "Sledujte, zda konkrétní DNS server odpovídá."
},
"thresholds": {
"title": "Prahové hodnoty upozornění",
@@ -765,7 +802,7 @@
"headers": {
"endTime": "Čas ukončení",
"resolutionType": "Typ řešení",
"startTime": "",
"startTime": "Čas zahájení",
"statusCode": "Stavový kód"
},
"resolvedIncidents": "Vyřešené incidenty",
@@ -809,13 +846,13 @@
}
},
"statBoxes": {
"avgCpuTemperature": "",
"cpuFrequency": "",
"cpuLogical": "",
"cpuPhysical": "",
"avgCpuTemperature": "Průměrná teplota CPU",
"cpuFrequency": "Frekvence CPU",
"cpuLogical": "CPU (logický)",
"cpuPhysical": "CPU (fyzický)",
"disk": "Disk",
"memory": "Paměť",
"os": ""
"os": "OS"
},
"table": {
"headers": {
@@ -826,7 +863,7 @@
},
"tabs": {
"labels": {
"network": "",
"network": "Síť",
"overview": "Přehled"
}
},
@@ -909,7 +946,8 @@
"lastFinishedAt": "Naposledy dokončeno",
"lastRunTook": "Poslední běh trval",
"lastFailedAt": "Poslední selhání",
"failReason": "Důvod selhání"
"failReason": "Důvod selhání",
"state": "Stav"
}
},
"queueFallback": {
@@ -929,8 +967,8 @@
},
"table": {
"headers": {
"nextWindow": "",
"repeat": ""
"nextWindow": "Další okno",
"repeat": "Opakovat"
}
},
"form": {
@@ -1032,7 +1070,7 @@
},
"telegram": {
"title": "Konfigurace Telegramu",
"description": "",
"description": "Nakonfigurujte svého Telegram bota pro odesílání oznámení.",
"optionBotToken": "Token bota",
"placeholderBotToken": "123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ",
"optionChatId": "ID chatu",
@@ -1099,6 +1137,10 @@
"header": {
"title": "Monitory PageSpeed",
"description": "Sledujte Core Web Vitals a skóre Lighthouse v čase a zachyťte regrese dříve než vaši uživatelé."
},
"strategy": {
"desktop": "Stolní počítač",
"mobile": "Mobil"
}
},
"settings": {
@@ -1201,7 +1243,7 @@
"email": {
"title": "Nastavení emailu",
"description": "Nastavte emailovou konfiguraci pro váš systém. Používá se k odesílání oznámení a upozornění.",
"descriptionTransport": "",
"descriptionTransport": "Toto vytvoří SMTP transport pro NodeMailer",
"descriptionTransportLink": "Zobrazit specifikace zde",
"option": {
"host": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "Odmítnout neplatné certifikáty: Odmítnout připojení se sebou podepsanými nebo nedůvěryhodnými certifikáty"
},
"displayName": {
"label": "Jméno odesílatele (nepovinné) - Přátelské jméno zobrazené v poli Od",
"placeholder": "Oznámení Checkmate"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "Dostupnost",
"responseTime": "Doba odezvy"
},
"chart": {
"avg": "průměr {{value}}ms",
"downTooltip": "Mimo provoz",
"heatmapAria": "Tepelná mapa doby odezvy",
"max": "max {{value}}ms"
},
"status": {
"breached": "Překročen limit",
"down": "Mimo provoz",
"initializing": "Inicializace",
"maintenance": "Údržba",
"paused": "Pozastaveno",
"up": "V provozu"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "Některé systémy jsou v údržbě",
"partiallyPaused": "Některé systémy jsou pozastavené",
"initializing": "Některé systémy se inicializují",
"noMonitors": "Nebyly nakonfigurovány žádné monitory"
"noMonitors": "Nebyly nakonfigurovány žádné monitory",
"monitoringSummary": "Sledování {{count}} služby · Aktualizace každých 10 sekund",
"monitoringSummary_other": "Sledování {{count}} služeb · Aktualizace každých 10 sekund"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "Zobrazit metriky infrastruktury"
}
}
},
"theme": {
"description": "Vyberte vizuální styl pro veřejnou stavovou stránku.",
"label": "Motiv",
"options": {
"bold": {
"description": "Velké písmo a sebevědomé rozložení. Vhodné pro stránky zaměřené na značku.",
"name": "Tučný"
},
"editorial": {
"description": "Patkové nadpisy a tenké oddělovače. Odlišný a klidný.",
"name": "Redakční"
},
"modern": {
"description": "Úroveň SaaS s pulzující stavovou tečkou a jemně mizejícími kartami.",
"name": "Moderní"
},
"refined": {
"description": "Čistý, profesionální, blízký klasickému vzhledu Checkmate.",
"name": "Vytříbený"
}
},
"title": "Motiv stránky"
},
"themeMode": {
"auto": "Automaticky (podle zařízení návštěvníka)",
"dark": "Pouze tmavý",
"label": "Režim motivu",
"light": "Pouze světlý"
}
},
"header": {
"title": "Stavové stránky",
"description": "Publikujte veřejné stránky, které zákazníkům a zúčastněným stranám zobrazují dostupnost v reálném čase a historii incidentů."
},
"editorial": {
"allUp": "Všechny systémy fungují normálně.",
"reportTitle": "Zpráva o službě"
},
"footer": {
"poweredBy": "Provozováno službou"
}
},
"uptime": {
+105 -7
View File
@@ -73,7 +73,9 @@
"paused": "pausiert",
"na": "N/A",
"resolved": "Gelöst",
"responseTime": "Antwortzeit"
"responseTime": "Antwortzeit",
"idle": "Untätig",
"running": "Läuft"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "Aktiv",
"responseTime": "Antwortzeit"
}
},
"statusCode": {
"down": "Ausgefallen",
"networkError": {
"dns": "Hostname konnte nicht aufgelöst werden (DNS-Auflösung fehlgeschlagen).",
"refused": "Der Server hat die Verbindung abgelehnt (Port geschlossen oder Dienst lauscht nicht).",
"reset": "Die Verbindung wurde zurückgesetzt, bevor eine Antwort empfangen wurde.",
"timeout": "Der Server hat nicht rechtzeitig geantwortet (Zeitüberschreitung der Anfrage).",
"tls": "TLS- oder SSL-Handshake fehlgeschlagen (Zertifikats- oder Verschlüsselungsproblem).",
"unknown": "Es wurde keine HTTP-Antwort vom Ziel empfangen."
}
}
},
"components": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "Infrastruktur",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "{{count}} Monitor pausiert",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "Der ausgewählte Monitor ist bereits pausiert",
"alreadyPaused_other": "Alle ausgewählten Monitore sind bereits pausiert",
"alreadyRunning_one": "Der ausgewählte Monitor wird bereits ausgeführt",
"alreadyRunning_other": "Alle ausgewählten Monitore werden bereits ausgeführt"
"alreadyRunning_other": "Alle ausgewählten Monitore werden bereits ausgeführt",
"partialFailure": "{{successCount}} Monitore aktualisiert, aber {{failedCount}} konnten nicht mit der Job-Warteschlange synchronisiert werden"
}
}
},
@@ -552,6 +567,26 @@
"captureUrl": {
"label": "Capture-Endpunkt",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "DNS-Eintragstyp",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "DNS-Server",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "Desktop",
"label": "Strategie",
"mobile": "Mobil"
}
},
"title": "Allgemeine Einstellungen",
@@ -564,7 +599,8 @@
"pagespeed": "Verfolgen Sie die Ladezeit, Core Web Vitals und Optimierungswerte Ihrer Website.",
"grpc": "Geben Sie den Hostnamen und Port des gRPC-Servers ein, optional einen Dienstnamen für den Health-Check, und fügen Sie einen Anzeigenamen hinzu.",
"websocket": "Geben Sie die zu überwachende WebSocket-URL ein (z. B. wss://example.com/socket) und fügen Sie einen Anzeigenamen hinzu.",
"hardware": "Überwachen Sie CPU-, Speicher-, Festplattenauslastung und Temperatur Ihrer Infrastruktur. Die URL des Capture-Endpunkts muss den vollständigen Pfad /api/v1/metrics enthalten (z. B. http://192.168.1.10:5001/api/v1/metrics). Fügen Sie für das Autorisierungsgeheimnis nur den Token-String ein — geben Sie 'Bearer' nicht mit an."
"hardware": "Überwachen Sie CPU-, Speicher-, Festplattenauslastung und Temperatur Ihrer Infrastruktur. Die URL des Capture-Endpunkts muss den vollständigen Pfad /api/v1/metrics enthalten (z. B. http://192.168.1.10:5001/api/v1/metrics). Fügen Sie für das Autorisierungsgeheimnis nur den Token-String ein — geben Sie 'Bearer' nicht mit an.",
"dns": "Überwachen Sie, ob ein bestimmter DNS-Server antwortet."
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "HTTP(S) verwenden, um Ihre Website oder Ihren API-Endpunkt zu überwachen.",
"optionPingDescription": "ICMP-Ping verwenden, um zu überwachen, ob ein Server online ist.",
"optionPortDescription": "Überwachen, ob ein bestimmter Port auf einem Server geöffnet ist.",
"title": "Typ"
"title": "Typ",
"optionDnsDescription": "Überwachen Sie, ob ein bestimmter DNS-Server antwortet."
},
"thresholds": {
"title": "Alarmschwellenwerte",
@@ -909,7 +946,8 @@
"lastFinishedAt": "Zuletzt abgeschlossen am",
"lastRunTook": "Letzter Lauf dauerte",
"lastFailedAt": "Letzter Fehler am",
"failReason": "Fehlergrund"
"failReason": "Fehlergrund",
"state": "Status"
}
},
"queueFallback": {
@@ -1099,6 +1137,10 @@
"header": {
"title": "PageSpeed-Monitore",
"description": "Verfolgen Sie Core Web Vitals und Lighthouse-Werte im Zeitverlauf, um Regressionen zu erkennen, bevor Nutzer es tun."
},
"strategy": {
"desktop": "Desktop",
"mobile": "Mobil"
}
},
"settings": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "Ungültige Zertifikate ablehnen: Verbindungen mit selbstsignierten oder nicht vertrauenswürdigen Zertifikaten ablehnen"
},
"displayName": {
"label": "Absendername (optional) - Anzeigename im Absenderfeld",
"placeholder": "Checkmate-Benachrichtigungen"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "Verfügbarkeit",
"responseTime": "Antwortzeit"
},
"chart": {
"avg": "Ø {{value}}ms",
"downTooltip": "Ausgefallen",
"heatmapAria": "Heatmap der Antwortzeit",
"max": "max. {{value}}ms"
},
"status": {
"breached": "Schwellenwert überschritten",
"down": "Ausgefallen",
"initializing": "Initialisierung",
"maintenance": "Wartung",
"paused": "Pausiert",
"up": "Betriebsbereit"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "Einige Systeme befinden sich in Wartung",
"partiallyPaused": "Einige Systeme sind pausiert",
"initializing": "Einige Systeme werden initialisiert",
"noMonitors": "Es wurden keine Monitore konfiguriert"
"noMonitors": "Es wurden keine Monitore konfiguriert",
"monitoringSummary": "Überwachung von {{count}} Dienst · Aktualisierung alle 10 Sekunden",
"monitoringSummary_other": "Überwachung von {{count}} Diensten · Aktualisierung alle 10 Sekunden"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "Infrastruktur-Metriken anzeigen"
}
}
},
"theme": {
"description": "Wählen Sie den visuellen Stil für Ihre öffentliche Statusseite.",
"label": "Theme",
"options": {
"bold": {
"description": "Große Schrift und großzügige Abstände. Ideal für markenorientierte Seiten.",
"name": "Fett"
},
"editorial": {
"description": "Serifenüberschriften und feine Trennlinien. Markant und ruhig.",
"name": "Redaktionell"
},
"modern": {
"description": "SaaS-Niveau mit pulsierendem Statuspunkt und sanft verblassenden Karten.",
"name": "Modern"
},
"refined": {
"description": "Sauber, professionell, nah am klassischen Checkmate-Look.",
"name": "Verfeinert"
}
},
"title": "Seiten-Theme"
},
"themeMode": {
"auto": "Automatisch (Gerät des Besuchers folgen)",
"dark": "Nur dunkel",
"label": "Theme-Modus",
"light": "Nur hell"
}
},
"header": {
"title": "Statusseiten",
"description": "Veröffentlichen Sie öffentliche Seiten, die Kunden und Stakeholdern Echtzeit-Verfügbarkeit und Vorfallsverlauf zeigen."
},
"editorial": {
"allUp": "Alle Systeme funktionieren normal.",
"reportTitle": "Servicebericht"
},
"footer": {
"poweredBy": "Bereitgestellt von"
}
},
"uptime": {
+83 -3
View File
@@ -43,6 +43,8 @@
"pause": "Pause",
"resume": "Resume",
"save": "Save",
"next": "Next",
"back": "Back",
"sendInvite": "Send invite",
"test": "Test",
"testNotifications": "Test notifications",
@@ -131,7 +133,8 @@
"url": "Url",
"interval": "Interval",
"active": "Active",
"responseTime": "Response time"
"responseTime": "Response time",
"tags": "Tags"
}
}
},
@@ -184,6 +187,7 @@
"pagespeed": "Pagespeed",
"infrastructure": "Infrastructure",
"notifications": "Notifications",
"tags": "Tags",
"checks": "Checks",
"incidents": "Incidents",
"statusPages": "Status pages",
@@ -448,7 +452,8 @@
"paused_one": "{{count}} monitor paused",
"paused_other": "{{count}} monitors paused",
"resumed_one": "{{count}} monitor resumed",
"resumed_other": "{{count}} monitors resumed"
"resumed_other": "{{count}} monitors resumed",
"partialFailure": "{{successCount}} monitors updated, but {{failedCount}} failed to sync with the job queue"
},
"statBoxes": {
"activeFor": "Active for",
@@ -627,6 +632,10 @@
"description": "Select the notification channels you want to use",
"title": "Notifications"
},
"tags": {
"description": "Select the tags you want to apply to this monitor",
"title": "Tags"
},
"type": {
"description": "Select the type of check to perform",
"optionDockerDescription": "Use Docker to monitor if a container is running.",
@@ -876,6 +885,37 @@
"description": "Track server performance, identify bottlenecks, and ensure reliability with real-time infrastructure monitoring."
}
},
"tags": {
"header": {
"title": "Tags",
"description": "Organize your monitors with tags"
},
"fallback": {
"title": "No tags yet",
"description": "Organize your monitors with tags. Once you create a tag and assign it to a monitor, you'll be able to filter by that tag across the app and see tag details here.",
"actionButton": "Create a tag"
},
"table": {
"headers": {
"color": "Color",
"appearance": "Appearance"
}
},
"form": {
"name": {
"title": "Tag name",
"description": "Choose a name for your tag",
"optionName": "Tag name",
"placeholder": "e.g. Group 1"
},
"color": {
"title": "Tag color",
"description": "Choose a color for your tag",
"optionName": "Tag color",
"placeholder": "e.g. #13715B"
}
}
},
"logs": {
"fallback": {
"title": "No logs yet",
@@ -893,6 +933,8 @@
"logLevelSelect": {
"label": "Log level"
},
"workers": "Workers",
"workersExplainers": "Shows worker processes. Worker IDs are truncated, hover for full ID",
"jobQueue": "Job queue",
"jobQueueExplainer": "<highlight>Highlighted rows</highlight> are jobs currently being processed by a worker.",
"failedJobs": "Failed jobs",
@@ -914,6 +956,14 @@
"memoryCpu": {
"title": "Memory & CPU",
"description": "How the process is using its allocated memory and host CPU."
},
"mongoDBStats": {
"title": "MongoDB stats",
"description": "Snapshot of MongoDB metrics"
},
"mongoDBCollectionStats": {
"title": "MongoDB collection stats",
"description": "Stats for MongoDB collections"
}
},
"stats": {
@@ -923,6 +973,21 @@
"totalHeapSize": "Total heap size",
"osMemoryLimit": "OS memory limit"
},
"mongoDBStats": {
"readyState": "Ready state",
"host": "Host",
"port": "Port",
"dbName": "Database name",
"storageSize": "Storage size"
},
"mongoDBCollectionStats": {
"documents": "Documents: {{count}}",
"bucketCount": "Bucket count: {{count}}",
"storageSize": "Storage size: {{size}}",
"indexSize": "Index size: {{size}}",
"totalSize": "Total size: {{size}}",
"dbUsageTooltip": "{{percentage}}% of DB storage"
},
"gauges": {
"heapAllocation": "Heap allocation",
"heapUsage": "Heap usage",
@@ -937,6 +1002,11 @@
},
"table": {
"headers": {
"host": "Host",
"processId": "Process ID",
"workerId": "Worker ID",
"workerMode": "Worker mode",
"lastSeenAt": "Last seen at",
"state": "State",
"timestamp": "Timestamp",
"level": "Level",
@@ -947,6 +1017,7 @@
"failCount": "Count",
"lastRunAt": "Last run at",
"lockedAt": "Locked at",
"lockedBy": "Locked by",
"lastFinishedAt": "Last finished at",
"lastRunTook": "Last run took",
"lastFailedAt": "Last failed",
@@ -1094,6 +1165,14 @@
"placeholderFromNumber": "+15551234567",
"optionToNumber": "To number (recipient)",
"placeholderToNumber": "+15559876543"
},
"ntfy": {
"title": "ntfy configuration",
"description": "Configure an ntfy server and topic for push notifications.",
"optionServerUrl": "Server URL",
"placeholderServerUrl": "https://ntfy.sh",
"optionTopic": "Topic",
"placeholderTopic": "checkmate-alerts"
}
},
"table": {
@@ -1523,7 +1602,8 @@
"filters": {
"search": {
"placeholder": "Search monitors..."
}
},
"activeTags": "Active tags"
},
"table": {
"headers": {
+105 -21
View File
@@ -47,14 +47,7 @@
"exportToJSON": "Exportar a JSON",
"importFromJSON": "Importar desde JSON",
"clearFilters": "Limpiar filtros",
"removeUser": "Eliminar usuario",
"testNotification": "Probar notificación",
"invite": "Invitar",
"copyLink": "Copiar enlace",
"regenerateToken": "Regenerar token",
"copyToken": "Copiar token",
"exportLogs": "Exportar registros",
"refresh": "Actualizar"
"removeUser": "Eliminar usuario"
},
"charts": {
"labels": {
@@ -80,7 +73,9 @@
"paused": "pausado",
"na": "N/D",
"resolved": "Resuelto",
"responseTime": "Tiempo de respuesta"
"responseTime": "Tiempo de respuesta",
"idle": "Inactivo",
"running": "En ejecución"
},
"form": {
"role": {
@@ -127,6 +122,17 @@
"active": "Activo",
"responseTime": "Tiempo de respuesta"
}
},
"statusCode": {
"down": "Caído",
"networkError": {
"dns": "No se pudo resolver el nombre de host (la búsqueda DNS falló).",
"refused": "El servidor rechazó la conexión (puerto cerrado o servicio inactivo).",
"reset": "La conexión se restableció antes de recibir una respuesta.",
"timeout": "El servidor no respondió a tiempo (tiempo de espera agotado).",
"tls": "Falló el protocolo TLS o SSL (problema de certificado o cifrado).",
"unknown": "No se recibió ninguna respuesta HTTP del destino."
}
}
},
"components": {
@@ -459,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "Infraestructura",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "{{count}} monitor pausado",
@@ -469,7 +476,8 @@
"alreadyPaused_one": "El monitor seleccionado ya está pausado",
"alreadyPaused_other": "Todos los monitores seleccionados ya están pausados",
"alreadyRunning_one": "El monitor seleccionado ya se está ejecutando",
"alreadyRunning_other": "Todos los monitores seleccionados ya se están ejecutando"
"alreadyRunning_other": "Todos los monitores seleccionados ya se están ejecutando",
"partialFailure": "Se actualizaron {{successCount}} monitores, pero {{failedCount}} fallaron al sincronizarse con la cola de trabajos"
}
}
},
@@ -559,6 +567,26 @@
"captureUrl": {
"label": "Endpoint de Capture",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "Tipo de registro DNS",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "Servidor DNS",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "Escritorio",
"label": "Estrategia",
"mobile": "Móvil"
}
},
"title": "Configuración general",
@@ -571,7 +599,8 @@
"pagespeed": "Rastree el rendimiento de carga de página, Core Web Vitals y puntuaciones de optimización de su sitio web.",
"grpc": "Ingrese el nombre de host y puerto del servidor gRPC, opcionalmente especifique un nombre de servicio para la verificación de salud, y agregue un nombre para mostrar.",
"websocket": "Ingrese la URL de WebSocket a monitorear (p. ej., wss://example.com/socket) y agregue un nombre para mostrar.",
"hardware": "Supervisa el uso de CPU, memoria, disco y temperatura de tu infraestructura. La URL del endpoint de Capture debe incluir la ruta completa /api/v1/metrics (p. ej., http://192.168.1.10:5001/api/v1/metrics). Para el secreto de autorización, pega solo la cadena del token — no incluyas 'Bearer'."
"hardware": "Supervisa el uso de CPU, memoria, disco y temperatura de tu infraestructura. La URL del endpoint de Capture debe incluir la ruta completa /api/v1/metrics (p. ej., http://192.168.1.10:5001/api/v1/metrics). Para el secreto de autorización, pega solo la cadena del token — no incluyas 'Bearer'.",
"dns": "Monitorea si un servidor DNS específico responde."
}
},
"ignoreTls": {
@@ -608,7 +637,8 @@
"optionHttpDescription": "Use HTTP(S) para monitorear su sitio web o endpoint de API.",
"optionPingDescription": "Use ICMP Ping para monitorear si un servidor está en línea.",
"optionPortDescription": "Monitoree si un puerto específico de un servidor está abierto.",
"title": "Tipo"
"title": "Tipo",
"optionDnsDescription": "Monitorea si un servidor DNS específico responde."
},
"thresholds": {
"title": "Umbrales de alerta",
@@ -916,7 +946,8 @@
"lastFinishedAt": "Última finalización",
"lastRunTook": "Última ejecución tomó",
"lastFailedAt": "Última falla",
"failReason": "Razón de falla"
"failReason": "Razón de falla",
"state": "Estado"
}
},
"queueFallback": {
@@ -1106,6 +1137,10 @@
"header": {
"title": "Monitores de PageSpeed",
"description": "Sigue las Core Web Vitals y las puntuaciones de Lighthouse a lo largo del tiempo para detectar regresiones antes que tus usuarios."
},
"strategy": {
"desktop": "Escritorio",
"mobile": "Móvil"
}
},
"settings": {
@@ -1254,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "Rechazar certificados inválidos: Rechazar conexiones con certificados autofirmados o no confiables"
},
"displayName": {
"label": "Nombre del remitente (opcional) - Nombre amigable mostrado en el campo De",
"placeholder": "Notificaciones de Checkmate"
}
}
},
@@ -1314,6 +1353,20 @@
"uptime": {
"title": "Tiempo de actividad",
"responseTime": "Tiempo de respuesta"
},
"chart": {
"avg": "prom {{value}}ms",
"downTooltip": "Caído",
"heatmapAria": "Mapa de calor del tiempo de respuesta",
"max": "máx {{value}}ms"
},
"status": {
"breached": "Umbral superado",
"down": "Caído",
"initializing": "Inicializando",
"maintenance": "Mantenimiento",
"paused": "Pausado",
"up": "Operativo"
}
},
"statusBar": {
@@ -1331,7 +1384,9 @@
"maintenance": "Algunos sistemas están en mantenimiento",
"partiallyPaused": "Algunos sistemas están en pausa",
"initializing": "Algunos sistemas se están inicializando",
"noMonitors": "No se ha configurado ningún monitor"
"noMonitors": "No se ha configurado ningún monitor",
"monitoringSummary": "Monitoreando {{count}} servicio · Actualizaciones cada 10 segundos",
"monitoringSummary_other": "Monitoreando {{count}} servicios · Actualizaciones cada 10 segundos"
},
"table": {
"headers": {
@@ -1422,18 +1477,47 @@
"label": "Mostrar métricas de infraestructura"
}
}
},
"theme": {
"description": "Elige el estilo visual para tu página de estado pública.",
"label": "Tema",
"options": {
"bold": {
"description": "Tipografía grande y espaciado confiado. Ideal para páginas centradas en la marca.",
"name": "Audaz"
},
"editorial": {
"description": "Encabezados con serifa y divisores finos. Distintivo y tranquilo.",
"name": "Editorial"
},
"modern": {
"description": "Calidad SaaS con un punto de estado pulsante y tarjetas que se desvanecen suavemente.",
"name": "Moderno"
},
"refined": {
"description": "Limpio, profesional, cercano al aspecto clásico de Checkmate.",
"name": "Refinado"
}
},
"title": "Tema de la página"
},
"themeMode": {
"auto": "Automático (seguir dispositivo del visitante)",
"dark": "Solo oscuro",
"label": "Modo de tema",
"light": "Solo claro"
}
},
"header": {
"title": "Páginas de estado",
"description": "Publica páginas públicas que muestren a tus clientes y partes interesadas la disponibilidad en tiempo real y el historial de incidentes."
},
"publicPage": {
"maintenance": {
"upcoming": "Mantenimiento programado",
"inProgress": "Mantenimiento en curso",
"completed": "Mantenimiento completado"
}
"editorial": {
"allUp": "Todos los sistemas funcionan con normalidad.",
"reportTitle": "Informe de servicio"
},
"footer": {
"poweredBy": "Desarrollado por"
}
},
"uptime": {
+142 -44
View File
@@ -73,7 +73,9 @@
"paused": "keskeytetty",
"na": "Ei saatavilla",
"resolved": "Ratkaistu",
"responseTime": "Vasteaika"
"responseTime": "Vasteaika",
"idle": "Toimeton",
"running": "Käynnissä"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "Aktiivinen",
"responseTime": "Vasteaika"
}
},
"statusCode": {
"down": "Alhaalla",
"networkError": {
"dns": "Isäntänimeä ei voitu selvittää (DNS-haku epäonnistui).",
"refused": "Palvelin hylkäsi yhteyden (portti suljettu tai palvelu ei kuuntele).",
"reset": "Yhteys nollattiin ennen vastauksen vastaanottamista.",
"timeout": "Palvelin ei vastannut ajoissa (pyynnön aikakatkaisu).",
"tls": "TLS- tai SSL-kättely epäonnistui (sertifikaatti- tai salausongelma).",
"unknown": "Kohteesta ei vastaanotettu HTTP-vastausta."
}
}
},
"components": {
@@ -306,28 +319,28 @@
"common": {
"passwordRules": {
"length": {
"beginning": "",
"highlighted": ""
"beginning": "Vähintään",
"highlighted": "8 merkkiä pitkä"
},
"lowercase": {
"beginning": "",
"highlighted": ""
"beginning": "Sisällettävä vähintään",
"highlighted": "yksi pieni kirjain"
},
"match": {
"beginning": "",
"highlighted": ""
"beginning": "Salasanat",
"highlighted": "täytyy täsmätä"
},
"number": {
"beginning": "",
"highlighted": ""
"beginning": "Sisällettävä vähintään",
"highlighted": "yksi numero"
},
"special": {
"beginning": "",
"highlighted": ""
"beginning": "Sisällettävä vähintään",
"highlighted": "yksi erikoismerkki (!?@#$%^&*()-_=+[]{}|;:'\",./\\~`)"
},
"uppercase": {
"beginning": "",
"highlighted": ""
"beginning": "Sisällettävä vähintään",
"highlighted": "yksi iso kirjain"
}
},
"form": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "Infrastruktuuri",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "{{count}} näyttö keskeytetty",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "Valittu näyttö on jo keskeytetty",
"alreadyPaused_other": "Kaikki valitut näytöt on jo keskeytetty",
"alreadyRunning_one": "Valittu näyttö on jo käynnissä",
"alreadyRunning_other": "Kaikki valitut näytöt ovat jo käynnissä"
"alreadyRunning_other": "Kaikki valitut näytöt ovat jo käynnissä",
"partialFailure": "{{successCount}} monitoria päivitettiin, mutta {{failedCount}} epäonnistui synkronoinnissa työjonon kanssa"
}
}
},
@@ -483,9 +498,9 @@
},
"matchMethod": {
"label": "Vertailutapa",
"equal": "",
"include": "",
"regex": ""
"equal": "Yhtä suuri",
"include": "Sisältää",
"regex": "Säännöllinen lauseke"
}
},
"title": "Lisäasetukset"
@@ -497,15 +512,15 @@
"label": "Tarkistusväli",
"value": {
"fifteenMinutes": "15 minuuttia",
"fifteenSeconds": "",
"fifteenSeconds": "15 sekuntia",
"fiveMinutes": "5 minuuttia",
"fourMinutes": "",
"oneMinute": "",
"fourMinutes": "4 minuuttia",
"oneMinute": "1 minuutti",
"tenMinutes": "10 minuuttia",
"thirtyMinutes": "30 minuuttia",
"thirtySeconds": "",
"threeMinutes": "",
"twoMinutes": ""
"thirtySeconds": "30 sekuntia",
"threeMinutes": "3 minuuttia",
"twoMinutes": "2 minuuttia"
}
}
},
@@ -534,8 +549,8 @@
"placeholder": "https://www.google.com"
},
"game": {
"label": "",
"placeholder": ""
"label": "Valitse peli",
"placeholder": "Valitse peli"
},
"port": {
"label": "Monitoroitava portti",
@@ -552,19 +567,40 @@
"captureUrl": {
"label": "Capture-päätepiste",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "DNS-tietuetyyppi",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "DNS-palvelin",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "Työpöytä",
"label": "Strategia",
"mobile": "Mobiili"
}
},
"title": "Yleiset asetukset",
"description": {
"http": "Syötä monitoroitava URL tai IP (esim. https://example.com/ tai 192.168.1.100) ja lisää selkeä näyttönimi, joka näkyy hallintapaneelissa.",
"ping": "",
"port": "",
"docker": "",
"ping": "Anna pingattava IP-osoite tai isäntänimi (esim. 192.168.1.100 tai example.com) ja lisää selkeä näyttönimi, joka näkyy hallintapaneelissa.",
"port": "Anna palvelimen URL tai IP, portin numero ja selkeä näyttönimi, joka näkyy hallintapaneelissa.",
"docker": "Anna Docker-kontin nimi tai tunnus. Voit käyttää joko kontin nimeä (esim. my-app) tai kontin tunnusta (täysi 64-merkkinen tunnus tai lyhyt tunnus).",
"game": "Anna IP-osoite tai isäntänimi ja portin numero pingausta varten (esim. 192.168.1.100 tai example.com) ja valitse pelityyppi. Jotkin pelit vaativat palvelinkyselyiden käyttöönoton asetustiedostossaan — tarkista pelipalvelimesi dokumentaatio. Katso protokollatiedot <gamedigLink>tuettujen pelien luettelosta</gamedigLink>.",
"pagespeed": "Seuraa sivun latausnopeutta, Core Web Vitals -arvoja ja optimointipisteitä verkkosivustollasi.",
"grpc": "Syötä gRPC-palvelimen isäntänimi ja portti, määritä halutessasi palvelun nimi terveystarkistusta varten ja lisää näyttönimi.",
"websocket": "Syötä monitoroitava WebSocket URL (esim. wss://example.com/socket) ja lisää näyttönimi.",
"hardware": "Seuraa infrastruktuurisi suorittimen, muistin, levyn käyttöä ja lämpötilaa. Capture-päätepisteen URL-osoitteen on sisällettävä koko polku /api/v1/metrics (esim. http://192.168.1.10:5001/api/v1/metrics). Liitä valtuutusavaimeen vain tokenin merkkijono — älä sisällytä sanaa 'Bearer'."
"hardware": "Seuraa infrastruktuurisi suorittimen, muistin, levyn käyttöä ja lämpötilaa. Capture-päätepisteen URL-osoitteen on sisällettävä koko polku /api/v1/metrics (esim. http://192.168.1.10:5001/api/v1/metrics). Liitä valtuutusavaimeen vain tokenin merkkijono — älä sisällytä sanaa 'Bearer'.",
"dns": "Tarkkaile, vastaako tietty DNS-palvelin."
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "Käytä HTTP(S)-protokollaa verkkosivustosi tai API-päätepisteen monitorointiin.",
"optionPingDescription": "Käytä ICMP Ping -komentoa monitoroimaan, onko palvelin verkossa.",
"optionPortDescription": "Monitoroi, onko tietty palvelimen portti auki.",
"title": "Tyyppi"
"title": "Tyyppi",
"optionDnsDescription": "Tarkkaile, vastaako tietty DNS-palvelin."
},
"thresholds": {
"title": "Hälytysrajat",
@@ -765,7 +802,7 @@
"headers": {
"endTime": "Päättymisaika",
"resolutionType": "Ratkaisun tyyppi",
"startTime": "",
"startTime": "Aloitusaika",
"statusCode": "Tilakoodi"
},
"resolvedIncidents": "Ratkaistut häiriöt",
@@ -809,13 +846,13 @@
}
},
"statBoxes": {
"avgCpuTemperature": "",
"cpuFrequency": "",
"cpuLogical": "",
"cpuPhysical": "",
"avgCpuTemperature": "Keskimääräinen CPU-lämpötila",
"cpuFrequency": "CPU-taajuus",
"cpuLogical": "CPU (looginen)",
"cpuPhysical": "CPU (fyysinen)",
"disk": "Levy",
"memory": "Muisti",
"os": ""
"os": "Käyttöjärjestelmä"
},
"table": {
"headers": {
@@ -826,7 +863,7 @@
},
"tabs": {
"labels": {
"network": "",
"network": "Verkko",
"overview": "Yleiskatsaus"
}
},
@@ -909,7 +946,8 @@
"lastFinishedAt": "Viimeisin valmistuminen",
"lastRunTook": "Viimeisin suoritus kesti",
"lastFailedAt": "Viimeisin epäonnistuminen",
"failReason": "Epäonnistumisen syy"
"failReason": "Epäonnistumisen syy",
"state": "Tila"
}
},
"queueFallback": {
@@ -929,8 +967,8 @@
},
"table": {
"headers": {
"nextWindow": "",
"repeat": ""
"nextWindow": "Seuraava ikkuna",
"repeat": "Toista"
}
},
"form": {
@@ -1032,7 +1070,7 @@
},
"telegram": {
"title": "Telegram-määritykset",
"description": "",
"description": "Määritä Telegram-bottisi lähettämään ilmoituksia.",
"optionBotToken": "Bottitunniste",
"placeholderBotToken": "123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ",
"optionChatId": "Chat-tunnus",
@@ -1099,6 +1137,10 @@
"header": {
"title": "PageSpeed-monitorit",
"description": "Seuraa Core Web Vitals -mittareita ja Lighthouse-pisteitä ajan mittaan ja huomaa heikennykset ennen käyttäjiä."
},
"strategy": {
"desktop": "Työpöytä",
"mobile": "Mobiili"
}
},
"settings": {
@@ -1201,7 +1243,7 @@
"email": {
"title": "Sähköpostiasetukset",
"description": "Määritä järjestelmäsi sähköpostiasetukset. Niitä käytetään ilmoitusten ja hälytysten lähettämiseen.",
"descriptionTransport": "",
"descriptionTransport": "Tämä rakentaa SMTP-kuljetuksen NodeMailerille",
"descriptionTransportLink": "Katso tekniset tiedot täältä",
"option": {
"host": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "Hylkää virheelliset varmenteet: Hylkää yhteydet itseallekirjoitetuilla tai ei-luotetuilla varmenteilla"
},
"displayName": {
"label": "Lähettäjän nimi (valinnainen) - Lähettäjäkentässä näkyvä ystävällinen nimi",
"placeholder": "Checkmate-ilmoitukset"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "Käytettävyys",
"responseTime": "Vasteaika"
},
"chart": {
"avg": "ka {{value}}ms",
"downTooltip": "Alhaalla",
"heatmapAria": "Vasteajan lämpökartta",
"max": "max {{value}}ms"
},
"status": {
"breached": "Kynnys ylittyi",
"down": "Alhaalla",
"initializing": "Alustetaan",
"maintenance": "Huolto",
"paused": "Keskeytetty",
"up": "Toiminnassa"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "Jotkin järjestelmät ovat huollossa",
"partiallyPaused": "Jotkin järjestelmät on keskeytetty",
"initializing": "Jotkin järjestelmät alustuvat",
"noMonitors": "Yhtään monitoria ei ole määritetty"
"noMonitors": "Yhtään monitoria ei ole määritetty",
"monitoringSummary": "Tarkkaillaan {{count}} palvelua · Päivitykset 10 sekunnin välein",
"monitoringSummary_other": "Tarkkaillaan {{count}} palvelua · Päivitykset 10 sekunnin välein"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "Näytä infrastruktuurin mittarit"
}
}
},
"theme": {
"description": "Valitse julkisen tilasivusi visuaalinen tyyli.",
"label": "Teema",
"options": {
"bold": {
"description": "Suuri typografia ja itsevarma välistys. Sopii brändilähtöisille sivuille.",
"name": "Rohkea"
},
"editorial": {
"description": "Antiikvaotsikot ja ohuet erottimet. Erottuva ja rauhallinen.",
"name": "Toimituksellinen"
},
"modern": {
"description": "SaaS-tasoa sykkivällä tilapisteellä ja pehmeästi haalistuvilla korteilla.",
"name": "Moderni"
},
"refined": {
"description": "Siisti, ammattimainen, lähellä klassista Checkmate-ilmettä.",
"name": "Hienostunut"
}
},
"title": "Sivun teema"
},
"themeMode": {
"auto": "Automaattinen (seuraa vierailijan laitetta)",
"dark": "Vain tumma",
"label": "Teematila",
"light": "Vain vaalea"
}
},
"header": {
"title": "Tilasivut",
"description": "Julkaise julkisia sivuja, jotka näyttävät asiakkaille ja sidosryhmille reaaliaikaisen käytettävyyden ja tapaushistorian."
},
"editorial": {
"allUp": "Kaikki järjestelmät toimivat normaalisti.",
"reportTitle": "Palveluraportti"
},
"footer": {
"poweredBy": "Tarjoaa"
}
},
"uptime": {
+108 -10
View File
@@ -73,7 +73,9 @@
"paused": "en pause",
"na": "N/A",
"resolved": "Résolu",
"responseTime": "Temps de réponse"
"responseTime": "Temps de réponse",
"idle": "Inactif",
"running": "En cours d'exécution"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "Actif",
"responseTime": "Temps de réponse"
}
},
"statusCode": {
"down": "Hors service",
"networkError": {
"dns": "Impossible de résoudre le nom d'hôte (échec de la recherche DNS).",
"refused": "Le serveur a refusé la connexion (port fermé ou service non à l'écoute).",
"reset": "La connexion a été réinitialisée avant la réception d'une réponse.",
"timeout": "Le serveur n'a pas répondu à temps (délai d'attente dépassé).",
"tls": "Échec de la négociation TLS ou SSL (problème de certificat ou de chiffrement).",
"unknown": "Aucune réponse HTTP reçue de la cible."
}
}
},
"components": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "Infrastructure",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "{{count}} moniteur en pause",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "Le moniteur sélectionné est déjà en pause",
"alreadyPaused_other": "Tous les moniteurs sélectionnés sont déjà en pause",
"alreadyRunning_one": "Le moniteur sélectionné est déjà en cours d'exécution",
"alreadyRunning_other": "Tous les moniteurs sélectionnés sont déjà en cours d'exécution"
"alreadyRunning_other": "Tous les moniteurs sélectionnés sont déjà en cours d'exécution",
"partialFailure": "{{successCount}} moniteurs mis à jour, mais {{failedCount}} n'ont pas pu se synchroniser avec la file d'attente des tâches"
}
}
},
@@ -534,8 +549,8 @@
"placeholder": "https://www.google.com"
},
"game": {
"label": "",
"placeholder": ""
"label": "Choisir un jeu",
"placeholder": "Sélectionner un jeu"
},
"port": {
"label": "Port à surveiller",
@@ -552,6 +567,26 @@
"captureUrl": {
"label": "Point de terminaison Capture",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "Type d'enregistrement DNS",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "Serveur DNS",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "Bureau",
"label": "Stratégie",
"mobile": "Mobile"
}
},
"title": "Paramètres généraux",
@@ -564,7 +599,8 @@
"pagespeed": "Suivez les performances de chargement, les Core Web Vitals et les scores d'optimisation de votre site web.",
"grpc": "Saisissez le nom d'hôte et le port du serveur gRPC, spécifiez éventuellement un nom de service pour le bilan de santé, et ajoutez un nom d'affichage.",
"websocket": "Saisissez l'URL WebSocket à surveiller (ex. wss://example.com/socket) et ajoutez un nom d'affichage.",
"hardware": "Surveillez l'utilisation du processeur, de la mémoire, du disque et la température de votre infrastructure. L'URL du point de terminaison Capture doit inclure le chemin complet /api/v1/metrics (par ex. http://192.168.1.10:5001/api/v1/metrics). Pour le secret d'autorisation, collez uniquement la chaîne du jeton — n'incluez pas 'Bearer'."
"hardware": "Surveillez l'utilisation du processeur, de la mémoire, du disque et la température de votre infrastructure. L'URL du point de terminaison Capture doit inclure le chemin complet /api/v1/metrics (par ex. http://192.168.1.10:5001/api/v1/metrics). Pour le secret d'autorisation, collez uniquement la chaîne du jeton — n'incluez pas 'Bearer'.",
"dns": "Surveillez si un serveur DNS spécifique répond."
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "Utilisez HTTP(S) pour surveiller votre site web ou point de terminaison API.",
"optionPingDescription": "Utilisez le Ping ICMP pour surveiller si un serveur est en ligne.",
"optionPortDescription": "Surveillez si un port spécifique sur un serveur est ouvert.",
"title": "Type"
"title": "Type",
"optionDnsDescription": "Surveillez si un serveur DNS spécifique répond."
},
"thresholds": {
"title": "Seuils d'alerte",
@@ -826,7 +863,7 @@
},
"tabs": {
"labels": {
"network": "",
"network": "Réseau",
"overview": "Aperçu"
}
},
@@ -909,7 +946,8 @@
"lastFinishedAt": "Dernière fin",
"lastRunTook": "Durée dernière exécution",
"lastFailedAt": "Dernier échec",
"failReason": "Raison de l'échec"
"failReason": "Raison de l'échec",
"state": "État"
}
},
"queueFallback": {
@@ -1099,6 +1137,10 @@
"header": {
"title": "Moniteurs PageSpeed",
"description": "Suivez les Core Web Vitals et les scores Lighthouse au fil du temps pour détecter les régressions avant vos utilisateurs."
},
"strategy": {
"desktop": "Bureau",
"mobile": "Mobile"
}
},
"settings": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "Rejeter les certificats invalides : refuser les connexions avec des certificats auto-signés ou non fiables"
},
"displayName": {
"label": "Nom de l'expéditeur (facultatif) - Nom convivial affiché dans le champ De",
"placeholder": "Notifications Checkmate"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "Disponibilité",
"responseTime": "Temps de réponse"
},
"chart": {
"avg": "moy {{value}}ms",
"downTooltip": "Hors service",
"heatmapAria": "Carte thermique des temps de réponse",
"max": "max {{value}}ms"
},
"status": {
"breached": "Seuil dépassé",
"down": "Hors service",
"initializing": "Initialisation",
"maintenance": "Maintenance",
"paused": "En pause",
"up": "Opérationnel"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "Certains systèmes sont en maintenance",
"partiallyPaused": "Certains systèmes sont en pause",
"initializing": "Certains systèmes sont en cours d'initialisation",
"noMonitors": "Aucun moniteur n'a été configuré"
"noMonitors": "Aucun moniteur n'a été configuré",
"monitoringSummary": "Surveillance de {{count}} service · Mises à jour toutes les 10 secondes",
"monitoringSummary_other": "Surveillance de {{count}} services · Mises à jour toutes les 10 secondes"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "Afficher les métriques d'infrastructure"
}
}
},
"theme": {
"description": "Choisissez le style visuel de votre page de statut publique.",
"label": "Thème",
"options": {
"bold": {
"description": "Grande typographie et espacement assuré. Idéal pour les pages axées sur la marque.",
"name": "Audacieux"
},
"editorial": {
"description": "Titres avec serif et séparateurs fins. Distinctif et calme.",
"name": "Éditorial"
},
"modern": {
"description": "Qualité SaaS avec un point de statut pulsant et des cartes qui s'estompent doucement.",
"name": "Moderne"
},
"refined": {
"description": "Propre, professionnel, proche du look classique de Checkmate.",
"name": "Raffiné"
}
},
"title": "Thème de la page"
},
"themeMode": {
"auto": "Automatique (selon l'appareil du visiteur)",
"dark": "Sombre uniquement",
"label": "Mode de thème",
"light": "Clair uniquement"
}
},
"header": {
"title": "Pages de statut",
"description": "Publiez des pages publiques montrant à vos clients et parties prenantes la disponibilité en temps réel et l'historique des incidents."
},
"editorial": {
"allUp": "Tous les systèmes fonctionnent normalement.",
"reportTitle": "Rapport de service"
},
"footer": {
"poweredBy": "Propulsé par"
}
},
"uptime": {
+116 -18
View File
@@ -73,7 +73,9 @@
"paused": "一時停止中",
"na": "N/A",
"resolved": "解決済み",
"responseTime": "応答時間"
"responseTime": "応答時間",
"idle": "アイドル",
"running": "実行中"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "アクティブ",
"responseTime": "応答時間"
}
},
"statusCode": {
"down": "ダウン",
"networkError": {
"dns": "ホスト名を解決できませんでした(DNSルックアップに失敗)。",
"refused": "サーバーが接続を拒否しました(ポートが閉じているか、サービスがリッスンしていません)。",
"reset": "応答を受信する前に接続がリセットされました。",
"timeout": "サーバーがタイムアウト内に応答しませんでした(リクエストタイムアウト)。",
"tls": "TLSまたはSSLハンドシェイクに失敗しました(証明書または暗号化の問題)。",
"unknown": "ターゲットからのHTTP応答がありませんでした。"
}
}
},
"components": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "インフラストラクチャ",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "{{count}} 個のモニターを一時停止しました",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "選択したモニターはすでに一時停止されています",
"alreadyPaused_other": "選択したすべてのモニターはすでに一時停止されています",
"alreadyRunning_one": "選択したモニターはすでに実行中です",
"alreadyRunning_other": "選択したすべてのモニターはすでに実行中です"
"alreadyRunning_other": "選択したすべてのモニターはすでに実行中です",
"partialFailure": "{{successCount}} 個のモニターが更新されましたが、{{failedCount}} 個はジョブキューとの同期に失敗しました"
}
}
},
@@ -483,9 +498,9 @@
},
"matchMethod": {
"label": "マッチ方法",
"equal": "",
"include": "",
"regex": ""
"equal": "等しい",
"include": "含む",
"regex": "正規表現"
}
},
"title": "詳細設定"
@@ -497,15 +512,15 @@
"label": "チェック頻度",
"value": {
"fifteenMinutes": "15分",
"fifteenSeconds": "",
"fifteenSeconds": "15秒",
"fiveMinutes": "5分",
"fourMinutes": "",
"oneMinute": "",
"fourMinutes": "4分",
"oneMinute": "1分",
"tenMinutes": "10分",
"thirtyMinutes": "30分",
"thirtySeconds": "",
"thirtySeconds": "30秒",
"threeMinutes": "3分",
"twoMinutes": ""
"twoMinutes": "2分"
}
}
},
@@ -534,8 +549,8 @@
"placeholder": "https://www.google.com"
},
"game": {
"label": "",
"placeholder": ""
"label": "ゲームを選択",
"placeholder": "ゲームを選択"
},
"port": {
"label": "監視するポート",
@@ -552,6 +567,26 @@
"captureUrl": {
"label": "Capture エンドポイント",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "DNSレコードタイプ",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "DNSサーバー",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "デスクトップ",
"label": "ストラテジー",
"mobile": "モバイル"
}
},
"title": "一般設定",
@@ -564,7 +599,8 @@
"pagespeed": "Webサイトのページ読み込みパフォーマンス、Core Web Vitals、最適化スコアを追跡します。",
"grpc": "gRPCサーバーのホスト名とポートを入力し、必要に応じてヘルスチェック用のサービス名を指定し、表示名を追加してください。",
"websocket": "監視するWebSocket URLを入力し(例: wss://example.com/socket)、表示名を追加してください。",
"hardware": "インフラの CPU、メモリ、ディスク使用率、温度を監視します。Capture エンドポイント URL には完全なパス /api/v1/metrics を含める必要があります (例: http://192.168.1.10:5001/api/v1/metrics)。認証シークレットにはトークン文字列のみを貼り付けてください — 'Bearer' は含めないでください。"
"hardware": "インフラの CPU、メモリ、ディスク使用率、温度を監視します。Capture エンドポイント URL には完全なパス /api/v1/metrics を含める必要があります (例: http://192.168.1.10:5001/api/v1/metrics)。認証シークレットにはトークン文字列のみを貼り付けてください — 'Bearer' は含めないでください。",
"dns": "特定のDNSサーバーが応答しているかを監視します。"
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "HTTP(S)を使用してWebサイトまたはAPIエンドポイントを監視します。",
"optionPingDescription": "ICMP Pingを使用してサーバーがオンラインかどうかを監視します。",
"optionPortDescription": "サーバーの特定のポートが開いているかどうかを監視します。",
"title": "タイプ"
"title": "タイプ",
"optionDnsDescription": "特定のDNSサーバーが応答しているかを監視します。"
},
"thresholds": {
"title": "アラートしきい値",
@@ -826,7 +863,7 @@
},
"tabs": {
"labels": {
"network": "",
"network": "ネットワーク",
"overview": "概要"
}
},
@@ -909,7 +946,8 @@
"lastFinishedAt": "最終完了日時",
"lastRunTook": "最終実行時間",
"lastFailedAt": "最終失敗日時",
"failReason": "失敗理由"
"failReason": "失敗理由",
"state": "状態"
}
},
"queueFallback": {
@@ -1099,6 +1137,10 @@
"header": {
"title": "PageSpeed監視",
"description": "Core Web VitalsとLighthouseスコアを継続的に追跡し、ユーザーが気付く前に劣化を発見します。"
},
"strategy": {
"desktop": "デスクトップ",
"mobile": "モバイル"
}
},
"settings": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "無効な証明書を拒否: 自己署名または信頼されていない証明書の接続を拒否"
},
"displayName": {
"label": "送信者名(任意)- 差出人フィールドに表示されるわかりやすい名前",
"placeholder": "Checkmate通知"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "稼働時間",
"responseTime": "応答時間"
},
"chart": {
"avg": "平均 {{value}}ms",
"downTooltip": "ダウン",
"heatmapAria": "応答時間のヒートマップ",
"max": "最大 {{value}}ms"
},
"status": {
"breached": "しきい値超過",
"down": "ダウン",
"initializing": "初期化中",
"maintenance": "メンテナンス",
"paused": "一時停止中",
"up": "稼働中"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "一部のシステムがメンテナンス中です",
"partiallyPaused": "一部のシステムが一時停止中です",
"initializing": "一部のシステムが初期化中です",
"noMonitors": "モニターが設定されていません"
"noMonitors": "モニターが設定されていません",
"monitoringSummary": "{{count}}サービスを監視中 · 10秒ごとに更新",
"monitoringSummary_other": "{{count}}サービスを監視中 · 10秒ごとに更新"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "インフラストラクチャメトリクスを表示"
}
}
},
"theme": {
"description": "公開ステータスページの視覚スタイルを選択します。",
"label": "テーマ",
"options": {
"bold": {
"description": "大きな書体と自信のある間隔。ブランド中心のページに最適です。",
"name": "ボールド"
},
"editorial": {
"description": "セリフ見出しと細い区切り線。独特で落ち着いています。",
"name": "エディトリアル"
},
"modern": {
"description": "脈動するステータスドットと柔らかくフェードするカードを備えたSaaS品質。",
"name": "モダン"
},
"refined": {
"description": "クリーンでプロフェッショナル、Checkmateのクラシックな外観に近い。",
"name": "リファインド"
}
},
"title": "ページテーマ"
},
"themeMode": {
"auto": "自動(訪問者のデバイスに従う)",
"dark": "ダークのみ",
"label": "テーマモード",
"light": "ライトのみ"
}
},
"header": {
"title": "ステータスページ",
"description": "顧客と関係者にリアルタイムの稼働状況とインシデント履歴を表示する公開ページを公開します。"
},
"editorial": {
"allUp": "すべてのシステムは正常に動作しています。",
"reportTitle": "サービスレポート"
},
"footer": {
"poweredBy": "提供元"
}
},
"uptime": {
+112 -14
View File
@@ -73,7 +73,9 @@
"paused": "pausado",
"na": "N/D",
"resolved": "Resolvido",
"responseTime": "Tempo de resposta"
"responseTime": "Tempo de resposta",
"idle": "Inativo",
"running": "Em execução"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "Ativo",
"responseTime": "Tempo de resposta"
}
},
"statusCode": {
"down": "Fora do ar",
"networkError": {
"dns": "Não foi possível resolver o nome do host (a busca DNS falhou).",
"refused": "O servidor recusou a conexão (porta fechada ou serviço não escutando).",
"reset": "A conexão foi redefinida antes que uma resposta fosse recebida.",
"timeout": "O servidor não respondeu a tempo (tempo limite da solicitação esgotado).",
"tls": "Falha no handshake TLS ou SSL (problema de certificado ou criptografia).",
"unknown": "Nenhuma resposta HTTP recebida do destino."
}
}
},
"components": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "Infraestrutura",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "{{count}} monitor pausado",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "O monitor selecionado já está pausado",
"alreadyPaused_other": "Todos os monitores selecionados já estão pausados",
"alreadyRunning_one": "O monitor selecionado já está em execução",
"alreadyRunning_other": "Todos os monitores selecionados já estão em execução"
"alreadyRunning_other": "Todos os monitores selecionados já estão em execução",
"partialFailure": "{{successCount}} monitores atualizados, mas {{failedCount}} falharam ao sincronizar com a fila de trabalhos"
}
}
},
@@ -483,8 +498,8 @@
},
"matchMethod": {
"label": "Método de correspondência",
"equal": "",
"include": "",
"equal": "Igual",
"include": "Inclui",
"regex": "Regex"
}
},
@@ -497,13 +512,13 @@
"label": "Frequência de verificação",
"value": {
"fifteenMinutes": "15 minutos",
"fifteenSeconds": "",
"fifteenSeconds": "15 segundos",
"fiveMinutes": "5 minutos",
"fourMinutes": "4 minutos",
"oneMinute": "1 minuto",
"tenMinutes": "10 minutos",
"thirtyMinutes": "30 minutos",
"thirtySeconds": "",
"thirtySeconds": "30 segundos",
"threeMinutes": "3 minutos",
"twoMinutes": "2 minutos"
}
@@ -534,8 +549,8 @@
"placeholder": "https://www.google.com"
},
"game": {
"label": "",
"placeholder": ""
"label": "Escolher jogo",
"placeholder": "Selecione um jogo"
},
"port": {
"label": "Porta para monitorar",
@@ -552,6 +567,26 @@
"captureUrl": {
"label": "Endpoint do Capture",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "Tipo de registro DNS",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "Servidor DNS",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "Desktop",
"label": "Estratégia",
"mobile": "Mobile"
}
},
"title": "Configurações gerais",
@@ -564,7 +599,8 @@
"pagespeed": "Acompanhe o desempenho de carregamento da página, Core Web Vitals e pontuações de otimização do seu site.",
"grpc": "Digite o hostname e a porta do servidor gRPC, opcionalmente especifique um nome de serviço para a verificação de saúde e adicione um nome de exibição.",
"websocket": "Digite a URL do WebSocket para monitorar (ex.: wss://exemplo.com/socket) e adicione um nome de exibição.",
"hardware": "Monitore o uso de CPU, memória, disco e temperatura da sua infraestrutura. A URL do endpoint do Capture deve incluir o caminho completo /api/v1/metrics (por exemplo, http://192.168.1.10:5001/api/v1/metrics). Para a chave de autorização, cole apenas a string do token — não inclua 'Bearer'."
"hardware": "Monitore o uso de CPU, memória, disco e temperatura da sua infraestrutura. A URL do endpoint do Capture deve incluir o caminho completo /api/v1/metrics (por exemplo, http://192.168.1.10:5001/api/v1/metrics). Para a chave de autorização, cole apenas a string do token — não inclua 'Bearer'.",
"dns": "Monitore se um servidor DNS específico está respondendo."
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "Use HTTP(S) para monitorar seu site ou endpoint de API.",
"optionPingDescription": "Use ICMP Ping para monitorar se um servidor está online.",
"optionPortDescription": "Monitore se uma porta específica em um servidor está aberta.",
"title": "Tipo"
"title": "Tipo",
"optionDnsDescription": "Monitore se um servidor DNS específico está respondendo."
},
"thresholds": {
"title": "Limites de alerta",
@@ -826,7 +863,7 @@
},
"tabs": {
"labels": {
"network": "",
"network": "Rede",
"overview": "Visão geral"
}
},
@@ -909,7 +946,8 @@
"lastFinishedAt": "Última conclusão em",
"lastRunTook": "Última execução durou",
"lastFailedAt": "Última falha em",
"failReason": "Motivo da falha"
"failReason": "Motivo da falha",
"state": "Estado"
}
},
"queueFallback": {
@@ -1099,6 +1137,10 @@
"header": {
"title": "Monitores PageSpeed",
"description": "Acompanhe Core Web Vitals e pontuações do Lighthouse ao longo do tempo para detectar regressões antes dos seus usuários."
},
"strategy": {
"desktop": "Desktop",
"mobile": "Mobile"
}
},
"settings": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "Rejeitar certificados inválidos: Rejeitar conexões com certificados autoassinados ou não confiáveis"
},
"displayName": {
"label": "Nome do remetente (opcional) - Nome amigável exibido no campo De",
"placeholder": "Notificações do Checkmate"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "Uptime",
"responseTime": "Tempo de resposta"
},
"chart": {
"avg": "méd {{value}}ms",
"downTooltip": "Fora do ar",
"heatmapAria": "Mapa de calor do tempo de resposta",
"max": "máx {{value}}ms"
},
"status": {
"breached": "Limite excedido",
"down": "Fora do ar",
"initializing": "Inicializando",
"maintenance": "Manutenção",
"paused": "Pausado",
"up": "Operacional"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "Alguns sistemas estão em manutenção",
"partiallyPaused": "Alguns sistemas estão pausados",
"initializing": "Alguns sistemas estão inicializando",
"noMonitors": "Nenhum monitor foi configurado"
"noMonitors": "Nenhum monitor foi configurado",
"monitoringSummary": "Monitorando {{count}} serviço · Atualizações a cada 10 segundos",
"monitoringSummary_other": "Monitorando {{count}} serviços · Atualizações a cada 10 segundos"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "Mostrar métricas de infraestrutura"
}
}
},
"theme": {
"description": "Escolha o estilo visual da sua página de status pública.",
"label": "Tema",
"options": {
"bold": {
"description": "Tipografia grande e espaçamento confiante. Ótimo para páginas voltadas à marca.",
"name": "Negrito"
},
"editorial": {
"description": "Títulos com serifa e divisores finos. Distinto e calmo.",
"name": "Editorial"
},
"modern": {
"description": "Qualidade SaaS com um ponto de status pulsante e cartões que desaparecem suavemente.",
"name": "Moderno"
},
"refined": {
"description": "Limpo, profissional, próximo ao visual clássico do Checkmate.",
"name": "Refinado"
}
},
"title": "Tema da página"
},
"themeMode": {
"auto": "Automático (seguir dispositivo do visitante)",
"dark": "Apenas escuro",
"label": "Modo de tema",
"light": "Apenas claro"
}
},
"header": {
"title": "Páginas de status",
"description": "Publique páginas públicas que mostram aos seus clientes e partes interessadas o tempo de atividade em tempo real e o histórico de incidentes."
},
"editorial": {
"allUp": "Todos os sistemas estão operando normalmente.",
"reportTitle": "Relatório de serviço"
},
"footer": {
"poweredBy": "Desenvolvido por"
}
},
"uptime": {
+105 -7
View File
@@ -73,7 +73,9 @@
"paused": "приостановлен",
"na": "Н/Д",
"resolved": "Решён",
"responseTime": "Время ответа"
"responseTime": "Время ответа",
"idle": "Бездействие",
"running": "Выполняется"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "Активный",
"responseTime": "Время ответа"
}
},
"statusCode": {
"down": "Не работает",
"networkError": {
"dns": "Не удалось разрешить имя хоста (поиск DNS не удался).",
"refused": "Сервер отказал в соединении (порт закрыт или служба не прослушивает).",
"reset": "Соединение было сброшено до получения ответа.",
"timeout": "Сервер не ответил вовремя (тайм-аут запроса).",
"tls": "Сбой рукопожатия TLS или SSL (проблема с сертификатом или шифрованием).",
"unknown": "HTTP-ответ от цели не получен."
}
}
},
"components": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "Инфраструктура",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "{{count}} монитор приостановлен",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "Выбранный монитор уже приостановлен",
"alreadyPaused_other": "Все выбранные мониторы уже приостановлены",
"alreadyRunning_one": "Выбранный монитор уже запущен",
"alreadyRunning_other": "Все выбранные мониторы уже запущены"
"alreadyRunning_other": "Все выбранные мониторы уже запущены",
"partialFailure": "Обновлено {{successCount}} мониторов, но {{failedCount}} не удалось синхронизировать с очередью задач"
}
}
},
@@ -552,6 +567,26 @@
"captureUrl": {
"label": "Эндпоинт Capture",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "Тип записи DNS",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "DNS-сервер",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "Десктоп",
"label": "Стратегия",
"mobile": "Мобильный"
}
},
"title": "Основные настройки",
@@ -564,7 +599,8 @@
"pagespeed": "Отслеживайте производительность загрузки, Core Web Vitals и показатели оптимизации для Вашего сайта.",
"grpc": "Введите имя хоста и порт gRPC-сервера, при необходимости укажите имя сервиса для проверки состояния и добавьте отображаемое имя.",
"websocket": "Введите WebSocket URL для мониторинга (напр., wss://example.com/socket) и добавьте отображаемое имя.",
"hardware": "Отслеживайте использование ЦП, памяти, диска и температуру вашей инфраструктуры. URL эндпоинта Capture должен включать полный путь /api/v1/metrics (например, http://192.168.1.10:5001/api/v1/metrics). В секрете авторизации укажите только строку токена — не включайте 'Bearer'."
"hardware": "Отслеживайте использование ЦП, памяти, диска и температуру вашей инфраструктуры. URL эндпоинта Capture должен включать полный путь /api/v1/metrics (например, http://192.168.1.10:5001/api/v1/metrics). В секрете авторизации укажите только строку токена — не включайте 'Bearer'.",
"dns": "Отслеживайте, отвечает ли конкретный DNS-сервер."
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "Используйте HTTP(S) для мониторинга Вашего сайта или API-эндпоинта.",
"optionPingDescription": "Используйте ICMP Ping для проверки доступности сервера.",
"optionPortDescription": "Мониторинг доступности определённого порта на сервере.",
"title": "Тип"
"title": "Тип",
"optionDnsDescription": "Отслеживайте, отвечает ли конкретный DNS-сервер."
},
"thresholds": {
"title": "Пороги оповещений",
@@ -909,7 +946,8 @@
"lastFinishedAt": "Последнее завершение",
"lastRunTook": "Последний запуск занял",
"lastFailedAt": "Последняя ошибка",
"failReason": "Причина ошибки"
"failReason": "Причина ошибки",
"state": "Состояние"
}
},
"queueFallback": {
@@ -1099,6 +1137,10 @@
"header": {
"title": "Мониторы PageSpeed",
"description": "Отслеживайте Core Web Vitals и оценки Lighthouse во времени, чтобы заметить регрессии раньше пользователей."
},
"strategy": {
"desktop": "Десктоп",
"mobile": "Мобильный"
}
},
"settings": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "Отклонять недействительные сертификаты: отклонять соединения с самоподписанными или ненадёжными сертификатами"
},
"displayName": {
"label": "Имя отправителя (необязательно) - Понятное имя в поле От",
"placeholder": "Уведомления Checkmate"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "Аптайм",
"responseTime": "Время ответа"
},
"chart": {
"avg": "ср {{value}}мс",
"downTooltip": "Не работает",
"heatmapAria": "Тепловая карта времени отклика",
"max": "макс {{value}}мс"
},
"status": {
"breached": "Превышен порог",
"down": "Не работает",
"initializing": "Инициализация",
"maintenance": "Обслуживание",
"paused": "Приостановлено",
"up": "Работает"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "Некоторые системы на техобслуживании",
"partiallyPaused": "Некоторые системы приостановлены",
"initializing": "Некоторые системы инициализируются",
"noMonitors": "Мониторы не настроены"
"noMonitors": "Мониторы не настроены",
"monitoringSummary": "Отслеживается {{count}} сервис · Обновление каждые 10 секунд",
"monitoringSummary_other": "Отслеживается {{count}} сервисов · Обновление каждые 10 секунд"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "Показывать метрики инфраструктуры"
}
}
},
"theme": {
"description": "Выберите визуальный стиль для вашей публичной страницы состояния.",
"label": "Тема",
"options": {
"bold": {
"description": "Крупная типографика и уверенные отступы. Отлично подходит для брендовых страниц.",
"name": "Жирный"
},
"editorial": {
"description": "Заголовки с засечками и тонкие разделители. Отличительный и спокойный.",
"name": "Редакционный"
},
"modern": {
"description": "Уровень SaaS с пульсирующей точкой состояния и плавно исчезающими карточками.",
"name": "Современный"
},
"refined": {
"description": "Чистый, профессиональный, близкий к классическому виду Checkmate.",
"name": "Утонченный"
}
},
"title": "Тема страницы"
},
"themeMode": {
"auto": "Авто (по устройству посетителя)",
"dark": "Только темная",
"label": "Режим темы",
"light": "Только светлая"
}
},
"header": {
"title": "Страницы состояния",
"description": "Публикуйте публичные страницы со временем работы и историей инцидентов для клиентов и заинтересованных сторон."
},
"editorial": {
"allUp": "Все системы работают в нормальном режиме.",
"reportTitle": "Отчет о сервисе"
},
"footer": {
"poweredBy": "Работает на"
}
},
"uptime": {
+105 -7
View File
@@ -73,7 +73,9 @@
"paused": "หยุดชั่วคราว",
"na": "ไม่มีข้อมูล",
"resolved": "แก้ไขแล้ว",
"responseTime": "เวลาตอบสนอง"
"responseTime": "เวลาตอบสนอง",
"idle": "ไม่ทำงาน",
"running": "กำลังทำงาน"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "ใช้งานอยู่",
"responseTime": "เวลาตอบสนอง"
}
},
"statusCode": {
"down": "ไม่พร้อมใช้งาน",
"networkError": {
"dns": "ไม่สามารถแก้ไขชื่อโฮสต์ได้ (การค้นหา DNS ล้มเหลว)",
"refused": "เซิร์ฟเวอร์ปฏิเสธการเชื่อมต่อ (พอร์ตปิดอยู่หรือบริการไม่ได้รับฟัง)",
"reset": "การเชื่อมต่อถูกรีเซ็ตก่อนที่จะได้รับการตอบกลับ",
"timeout": "เซิร์ฟเวอร์ไม่ตอบกลับทันเวลา (คำขอหมดเวลา)",
"tls": "การจับมือ TLS หรือ SSL ล้มเหลว (ปัญหาใบรับรองหรือการเข้ารหัส)",
"unknown": "ไม่ได้รับการตอบกลับ HTTP จากเป้าหมาย"
}
}
},
"components": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "โครงสร้างพื้นฐาน",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "หยุดจอภาพชั่วคราว {{count}} จอ",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "จอภาพที่เลือกถูกหยุดชั่วคราวแล้ว",
"alreadyPaused_other": "จอภาพที่เลือกทั้งหมดถูกหยุดชั่วคราวแล้ว",
"alreadyRunning_one": "จอภาพที่เลือกกำลังทำงานอยู่แล้ว",
"alreadyRunning_other": "จอภาพที่เลือกทั้งหมดกำลังทำงานอยู่แล้ว"
"alreadyRunning_other": "จอภาพที่เลือกทั้งหมดกำลังทำงานอยู่แล้ว",
"partialFailure": "อัปเดตมอนิเตอร์ {{successCount}} รายการแล้ว แต่ {{failedCount}} รายการไม่สามารถซิงค์กับคิวงานได้"
}
}
},
@@ -552,6 +567,26 @@
"captureUrl": {
"label": "ปลายทาง Capture",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "ประเภทเรกคอร์ด DNS",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "เซิร์ฟเวอร์ DNS",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "เดสก์ท็อป",
"label": "กลยุทธ์",
"mobile": "มือถือ"
}
},
"title": "ตั้งค่าทั่วไป",
@@ -564,7 +599,8 @@
"pagespeed": "ติดตามประสิทธิภาพการโหลดหน้า Core Web Vitals และคะแนนการเพิ่มประสิทธิภาพของเว็บไซต์",
"grpc": "กรอกชื่อโฮสต์และพอร์ตของเซิร์ฟเวอร์ gRPC ระบุชื่อบริการสำหรับ health check (ไม่บังคับ) และตั้งชื่อที่แสดง",
"websocket": "กรอก URL ของ WebSocket ที่ต้องการตรวจสอบ (เช่น wss://example.com/socket) และตั้งชื่อที่แสดง",
"hardware": "ตรวจสอบการใช้ CPU หน่วยความจำ ดิสก์ และอุณหภูมิของโครงสร้างพื้นฐานของคุณ URL ของปลายทาง Capture ต้องรวมเส้นทางแบบเต็ม /api/v1/metrics (ตัวอย่าง: http://192.168.1.10:5001/api/v1/metrics) สำหรับรหัสลับสำหรับยืนยันตัวตน ให้วางเฉพาะสตริงโทเค็น — อย่ารวมคำว่า 'Bearer'"
"hardware": "ตรวจสอบการใช้ CPU หน่วยความจำ ดิสก์ และอุณหภูมิของโครงสร้างพื้นฐานของคุณ URL ของปลายทาง Capture ต้องรวมเส้นทางแบบเต็ม /api/v1/metrics (ตัวอย่าง: http://192.168.1.10:5001/api/v1/metrics) สำหรับรหัสลับสำหรับยืนยันตัวตน ให้วางเฉพาะสตริงโทเค็น — อย่ารวมคำว่า 'Bearer'",
"dns": "ติดตามว่าเซิร์ฟเวอร์ DNS เฉพาะตอบกลับหรือไม่"
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "ใช้ HTTP(S) เพื่อตรวจสอบเว็บไซต์หรือ API endpoint",
"optionPingDescription": "ใช้ ICMP Ping เพื่อตรวจสอบว่าเซิร์ฟเวอร์ออนไลน์อยู่หรือไม่",
"optionPortDescription": "ตรวจสอบว่าพอร์ตเฉพาะบนเซิร์ฟเวอร์เปิดอยู่หรือไม่",
"title": "ประเภท"
"title": "ประเภท",
"optionDnsDescription": "ติดตามว่าเซิร์ฟเวอร์ DNS เฉพาะตอบกลับหรือไม่"
},
"thresholds": {
"title": "เกณฑ์การแจ้งเตือน",
@@ -909,7 +946,8 @@
"lastFinishedAt": "เสร็จสิ้นล่าสุดเมื่อ",
"lastRunTook": "รันล่าสุดใช้เวลา",
"lastFailedAt": "ล้มเหลวล่าสุดเมื่อ",
"failReason": "สาเหตุที่ล้มเหลว"
"failReason": "สาเหตุที่ล้มเหลว",
"state": "สถานะ"
}
},
"queueFallback": {
@@ -1099,6 +1137,10 @@
"header": {
"title": "ตัวตรวจสอบ PageSpeed",
"description": "ติดตาม Core Web Vitals และคะแนน Lighthouse เพื่อจับความถดถอยก่อนที่ผู้ใช้จะสังเกตเห็น"
},
"strategy": {
"desktop": "เดสก์ท็อป",
"mobile": "มือถือ"
}
},
"settings": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "ปฏิเสธใบรับรองที่ไม่ถูกต้อง: ปฏิเสธการเชื่อมต่อด้วยใบรับรอง self-signed หรือไม่น่าเชื่อถือ"
},
"displayName": {
"label": "ชื่อผู้ส่ง (ไม่บังคับ) - ชื่อที่เป็นมิตรซึ่งแสดงในช่องจาก",
"placeholder": "การแจ้งเตือน Checkmate"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "อัปไทม์",
"responseTime": "เวลาตอบสนอง"
},
"chart": {
"avg": "เฉลี่ย {{value}}ms",
"downTooltip": "ไม่พร้อมใช้งาน",
"heatmapAria": "แผนที่ความร้อนของเวลาตอบสนอง",
"max": "สูงสุด {{value}}ms"
},
"status": {
"breached": "เกินขีดจำกัด",
"down": "ไม่พร้อมใช้งาน",
"initializing": "กำลังเริ่มต้น",
"maintenance": "การบำรุงรักษา",
"paused": "หยุดชั่วคราว",
"up": "ใช้งานได้"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "บางระบบอยู่ระหว่างการบำรุงรักษา",
"partiallyPaused": "บางระบบถูกหยุดชั่วคราว",
"initializing": "บางระบบกำลังเริ่มต้น",
"noMonitors": "ยังไม่มีการกำหนดค่ามอนิเตอร์"
"noMonitors": "ยังไม่มีการกำหนดค่ามอนิเตอร์",
"monitoringSummary": "กำลังตรวจสอบ {{count}} บริการ · อัปเดตทุก 10 วินาที",
"monitoringSummary_other": "กำลังตรวจสอบ {{count}} บริการ · อัปเดตทุก 10 วินาที"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "แสดงเมตริกโครงสร้างพื้นฐาน"
}
}
},
"theme": {
"description": "เลือกรูปแบบภาพสำหรับหน้าสถานะสาธารณะของคุณ",
"label": "ธีม",
"options": {
"bold": {
"description": "ตัวอักษรขนาดใหญ่และการเว้นระยะที่มั่นใจ เหมาะสำหรับหน้าที่เน้นแบรนด์",
"name": "ตัวหนา"
},
"editorial": {
"description": "หัวเรื่องแบบเซริฟและตัวคั่นบาง โดดเด่นและสงบ",
"name": "บรรณาธิการ"
},
"modern": {
"description": "ระดับ SaaS พร้อมจุดสถานะที่เต้นและการ์ดที่จางหายอย่างนุ่มนวล",
"name": "ทันสมัย"
},
"refined": {
"description": "สะอาด เป็นมืออาชีพ ใกล้เคียงกับรูปลักษณ์คลาสสิกของ Checkmate",
"name": "ละเอียดอ่อน"
}
},
"title": "ธีมของหน้า"
},
"themeMode": {
"auto": "อัตโนมัติ (ตามอุปกรณ์ของผู้เข้าชม)",
"dark": "มืดเท่านั้น",
"label": "โหมดธีม",
"light": "สว่างเท่านั้น"
}
},
"header": {
"title": "หน้าสถานะ",
"description": "เผยแพร่หน้าสาธารณะที่แสดง uptime แบบเรียลไทม์และประวัติเหตุการณ์ให้กับลูกค้าและผู้มีส่วนได้ส่วนเสีย"
},
"editorial": {
"allUp": "ทุกระบบทำงานเป็นปกติ",
"reportTitle": "รายงานบริการ"
},
"footer": {
"poweredBy": "ขับเคลื่อนโดย"
}
},
"uptime": {
+123 -25
View File
@@ -73,7 +73,9 @@
"paused": "duraklatıldı",
"na": "N/A",
"resolved": "Çözüldü",
"responseTime": "Yanıt süresi"
"responseTime": "Yanıt süresi",
"idle": "Boşta",
"running": "Çalışıyor"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "Aktif",
"responseTime": "Yanıt süresi"
}
},
"statusCode": {
"down": "Çalışmıyor",
"networkError": {
"dns": "Ana bilgisayar adı çözümlenemedi (DNS araması başarısız oldu).",
"refused": "Sunucu bağlantıyı reddetti (bağlantı noktası kapalı veya hizmet dinlemiyor).",
"reset": "Yanıt alınmadan bağlantı sıfırlandı.",
"timeout": "Sunucu zamanında yanıt vermedi (istek zaman aşımına uğradı).",
"tls": "TLS veya SSL el sıkışması başarısız (sertifika veya şifreleme sorunu).",
"unknown": "Hedeften HTTP yanıtı alınamadı."
}
}
},
"components": {
@@ -306,7 +319,7 @@
"common": {
"passwordRules": {
"length": {
"beginning": "",
"beginning": "En az",
"highlighted": "8 karakter uzunluğunda"
},
"lowercase": {
@@ -314,7 +327,7 @@
"highlighted": "Bir küçük harf"
},
"match": {
"beginning": "",
"beginning": "Parolalar",
"highlighted": "eşleşmelidir"
},
"number": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "Altyapı",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "{{count}} monitör duraklatıldı",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "Seçili monitör zaten duraklatıldı",
"alreadyPaused_other": "Seçili tüm monitörler zaten duraklatıldı",
"alreadyRunning_one": "Seçili monitör zaten çalışıyor",
"alreadyRunning_other": "Seçili tüm monitörler zaten çalışıyor"
"alreadyRunning_other": "Seçili tüm monitörler zaten çalışıyor",
"partialFailure": "{{successCount}} monitör güncellendi, ancak {{failedCount}} tanesi iş kuyruğuyla senkronize edilemedi"
}
}
},
@@ -483,9 +498,9 @@
},
"matchMethod": {
"label": "Eşleştirme yöntemi",
"equal": "",
"include": "",
"regex": ""
"equal": "Eşit",
"include": "İçerir",
"regex": "Regex"
}
},
"title": "Gelişmiş ayarlar"
@@ -497,15 +512,15 @@
"label": "Kontrol sıklığı",
"value": {
"fifteenMinutes": "15 dakika",
"fifteenSeconds": "",
"fifteenSeconds": "15 saniye",
"fiveMinutes": "5 dakika",
"fourMinutes": "",
"oneMinute": "",
"fourMinutes": "4 dakika",
"oneMinute": "1 dakika",
"tenMinutes": "10 dakika",
"thirtyMinutes": "30 dakika",
"thirtySeconds": "",
"threeMinutes": "",
"twoMinutes": ""
"thirtySeconds": "30 saniye",
"threeMinutes": "3 dakika",
"twoMinutes": "2 dakika"
}
}
},
@@ -534,8 +549,8 @@
"placeholder": "https://www.google.com"
},
"game": {
"label": "",
"placeholder": ""
"label": "Oyun seç",
"placeholder": "Bir oyun seçin"
},
"port": {
"label": "İzlenecek port",
@@ -552,19 +567,40 @@
"captureUrl": {
"label": "Capture uç noktası",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "DNS kayıt türü",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "DNS sunucusu",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "Masaüstü",
"label": "Strateji",
"mobile": "Mobil"
}
},
"title": "Genel ayarlar",
"description": {
"http": "İzlenecek URL veya IP adresini girin (örn. https://example.com/ veya 192.168.1.100) ve panoda görünecek açık bir ad ekleyin.",
"ping": "",
"port": "",
"docker": "",
"ping": "Ping atılacak IP adresini veya ana bilgisayar adını girin (ör. 192.168.1.100 veya example.com) ve panoda görünen net bir görünen ad ekleyin.",
"port": "Sunucunun URL veya IP adresini, port numarasını ve panoda görünen net bir görünen ad girin.",
"docker": "Docker kapsayıcı adını veya kimliğini girin. Kapsayıcı adını (ör. my-app) veya kapsayıcı kimliğini (tam 64 karakterli kimlik veya kısa kimlik) kullanabilirsiniz.",
"game": "Ping atılacak IP adresini veya ana bilgisayar adını ve port numarasını girin (ör. 192.168.1.100 veya example.com) ve oyun türünü seçin. Bazı oyunlar yapılandırma dosyasında sunucu sorgularının etkinleştirilmesini gerektirir — oyun sunucunuzun belgelerine bakın. Protokol ayrıntıları için <gamedigLink>desteklenen oyunlar listesine</gamedigLink> bakın.",
"pagespeed": "Web sitenizin sayfa yükleme performansını, Core Web Vitals metriklerini ve optimizasyon puanlarını takip edin.",
"grpc": "gRPC sunucusunun ana bilgisayar adını ve portunu girin, isteğe bağlı olarak sağlık kontrolü için bir servis adı belirtin ve bir görünen ad ekleyin.",
"websocket": "İzlenecek WebSocket URL'sini girin (örn. wss://example.com/socket) ve bir görünen ad ekleyin.",
"hardware": "Altyapınızın CPU, bellek, disk kullanımını ve sıcaklığını izleyin. Capture uç nokta URL'si tam yolu /api/v1/metrics içermelidir (ör. http://192.168.1.10:5001/api/v1/metrics). Yetkilendirme anahtarına yalnızca token dizesini yapıştırın — 'Bearer' eklemeyin."
"hardware": "Altyapınızın CPU, bellek, disk kullanımını ve sıcaklığını izleyin. Capture uç nokta URL'si tam yolu /api/v1/metrics içermelidir (ör. http://192.168.1.10:5001/api/v1/metrics). Yetkilendirme anahtarına yalnızca token dizesini yapıştırın — 'Bearer' eklemeyin.",
"dns": "Belirli bir DNS sunucusunun yanıt verip vermediğini izleyin."
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "Web sitenizi veya API uç noktanızı izlemek için HTTP(S) kullanın.",
"optionPingDescription": "Bir sunucunun çevrimiçi olup olmadığını izlemek için ICMP Ping kullanın.",
"optionPortDescription": "Bir sunucuda belirli bir portun açık olup olmadığını izleyin.",
"title": "Tür"
"title": "Tür",
"optionDnsDescription": "Belirli bir DNS sunucusunun yanıt verip vermediğini izleyin."
},
"thresholds": {
"title": "Uyarı eşikleri",
@@ -826,7 +863,7 @@
},
"tabs": {
"labels": {
"network": "",
"network": "Ağ",
"overview": "Genel bakış"
}
},
@@ -909,7 +946,8 @@
"lastFinishedAt": "Son tamamlanma zamanı",
"lastRunTook": "Son çalışma süresi",
"lastFailedAt": "Son başarısızlık zamanı",
"failReason": "Başarısızlık nedeni"
"failReason": "Başarısızlık nedeni",
"state": "Durum"
}
},
"queueFallback": {
@@ -1099,6 +1137,10 @@
"header": {
"title": "PageSpeed izleyicileri",
"description": "Core Web Vitals ve Lighthouse puanlarını zaman içinde izleyerek kullanıcılarınız fark etmeden gerilemeleri yakalayın."
},
"strategy": {
"desktop": "Masaüstü",
"mobile": "Mobil"
}
},
"settings": {
@@ -1201,7 +1243,7 @@
"email": {
"title": "E-posta ayarları",
"description": "Sisteminiz için e-posta ayarlarını yapılandırın. Bu, bildirim ve uyarı göndermek için kullanılır.",
"descriptionTransport": "",
"descriptionTransport": "Bu, NodeMailer için SMTP aktarımı oluşturur",
"descriptionTransportLink": "Özellikleri burada görün",
"option": {
"host": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "Geçersiz sertifikaları reddet: Kendinden imzalı veya güvenilmeyen sertifikalı bağlantıları reddet"
},
"displayName": {
"label": "Gönderen adı (isteğe bağlı) - Gönderen alanında gösterilen kullanıcı dostu ad",
"placeholder": "Checkmate bildirimleri"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "Çalışma süresi",
"responseTime": "Yanıt süresi"
},
"chart": {
"avg": "ort {{value}}ms",
"downTooltip": "Çalışmıyor",
"heatmapAria": "Yanıt süresi ısı haritası",
"max": "maks {{value}}ms"
},
"status": {
"breached": "Eşik aşıldı",
"down": "Çalışmıyor",
"initializing": "Başlatılıyor",
"maintenance": "Bakım",
"paused": "Duraklatıldı",
"up": "Çalışıyor"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "Bazı sistemler bakımda",
"partiallyPaused": "Bazı sistemler duraklatıldı",
"initializing": "Bazı sistemler başlatılıyor",
"noMonitors": "Hiç monitör yapılandırılmadı"
"noMonitors": "Hiç monitör yapılandırılmadı",
"monitoringSummary": "{{count}} hizmet izleniyor · 10 saniyede bir güncellenir",
"monitoringSummary_other": "{{count}} hizmet izleniyor · 10 saniyede bir güncellenir"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "Altyapı metriklerini göster"
}
}
},
"theme": {
"description": "Genel durum sayfanız için görsel stili seçin.",
"label": "Tema",
"options": {
"bold": {
"description": "Büyük tipografi ve kendinden emin aralıklar. Marka odaklı sayfalar için harika.",
"name": "Kalın"
},
"editorial": {
"description": "Serifli başlıklar ve ince ayraçlar. Belirgin ve sakin.",
"name": "Editöryal"
},
"modern": {
"description": "Atan bir durum noktası ve yumuşakça solan kartlarla SaaS kalitesinde.",
"name": "Modern"
},
"refined": {
"description": "Temiz, profesyonel, klasik Checkmate görünümüne yakın.",
"name": "Zarif"
}
},
"title": "Sayfa teması"
},
"themeMode": {
"auto": "Otomatik (ziyaretçinin cihazına göre)",
"dark": "Yalnızca koyu",
"label": "Tema modu",
"light": "Yalnızca açık"
}
},
"header": {
"title": "Durum sayfaları",
"description": "Müşterilerinize ve paydaşlarınıza gerçek zamanlı çalışma süresi ve olay geçmişini gösteren herkese açık sayfalar yayınlayın."
},
"editorial": {
"allUp": "Tüm sistemler normal şekilde çalışıyor.",
"reportTitle": "Hizmet raporu"
},
"footer": {
"poweredBy": "Sağlayan"
}
},
"uptime": {
+130 -32
View File
@@ -73,7 +73,9 @@
"paused": "призупинено",
"na": "Н/Д",
"resolved": "Вирішено",
"responseTime": "Час відповіді"
"responseTime": "Час відповіді",
"idle": "Очікування",
"running": "Виконується"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "Активний",
"responseTime": "Час відповіді"
}
},
"statusCode": {
"down": "Не працює",
"networkError": {
"dns": "Не вдалося розв'язати ім'я хоста (пошук DNS не вдався).",
"refused": "Сервер відхилив з'єднання (порт закрито або служба не слухає).",
"reset": "З'єднання було скинуто до отримання відповіді.",
"timeout": "Сервер не відповів вчасно (час очікування запиту вичерпано).",
"tls": "Збій рукостискання TLS або SSL (проблема з сертифікатом або шифруванням).",
"unknown": "HTTP-відповідь від цілі не отримана."
}
}
},
"components": {
@@ -306,28 +319,28 @@
"common": {
"passwordRules": {
"length": {
"beginning": "",
"highlighted": ""
"beginning": "Має бути щонайменше",
"highlighted": "8 символів"
},
"lowercase": {
"beginning": "",
"highlighted": ""
"beginning": "Має містити щонайменше",
"highlighted": "одну малу літеру"
},
"match": {
"beginning": "",
"highlighted": ""
"beginning": "Паролі",
"highlighted": "мають збігатися"
},
"number": {
"beginning": "",
"highlighted": ""
"beginning": "Має містити щонайменше",
"highlighted": "одну цифру"
},
"special": {
"beginning": "",
"highlighted": ""
"beginning": "Має містити щонайменше",
"highlighted": "один спеціальний символ (!?@#$%^&*()-_=+[]{}|;:'\",./\\~`)"
},
"uppercase": {
"beginning": "",
"highlighted": ""
"beginning": "Має містити щонайменше",
"highlighted": "одну велику літеру"
}
},
"form": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "Інфраструктура",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "{{count}} монітор призупинено",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "Вибраний монітор уже призупинено",
"alreadyPaused_other": "Всі вибрані монітори уже призупинено",
"alreadyRunning_one": "Вибраний монітор уже запущено",
"alreadyRunning_other": "Всі вибрані монітори уже запущено"
"alreadyRunning_other": "Всі вибрані монітори уже запущено",
"partialFailure": "Оновлено {{successCount}} моніторів, але {{failedCount}} не вдалося синхронізувати з чергою завдань"
}
}
},
@@ -552,19 +567,40 @@
"captureUrl": {
"label": "Точка доступу Capture",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "Тип запису DNS",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "DNS-сервер",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "Десктоп",
"label": "Стратегія",
"mobile": "Мобільний"
}
},
"title": "Загальні налаштування",
"description": {
"http": "Введіть URL або IP для моніторингу (напр., https://example.com/ або 192.168.1.100) та додайте зрозуміле ім'я для відображення на панелі.",
"ping": "",
"port": "",
"docker": "",
"ping": "Введіть IP-адресу або ім'я хоста для пінгу (наприклад, 192.168.1.100 або example.com) і додайте чітку відображувану назву, що з'явиться на дашборді.",
"port": "Введіть URL або IP сервера, номер порту та чітку відображувану назву, що з'явиться на дашборді.",
"docker": "Введіть назву або ідентифікатор Docker-контейнера. Можна використовувати назву контейнера (наприклад, my-app) або ідентифікатор (повний 64-символьний або скорочений).",
"game": "Введіть IP-адресу або ім'я хоста та номер порту для пінгу (наприклад, 192.168.1.100 або example.com) і виберіть тип гри. Деяким іграм потрібно ввімкнути серверні запити у файлі конфігурації — перевірте документацію вашого ігрового сервера. Деталі протоколу див. у <gamedigLink>списку підтримуваних ігор</gamedigLink>.",
"pagespeed": "Відстежуйте швидкість завантаження сторінки, показники Core Web Vitals та оцінки оптимізації Вашого сайту.",
"grpc": "Введіть ім'я хоста та порт gRPC-сервера, за бажанням вкажіть назву сервісу для перевірки стану та додайте відображуване ім'я.",
"websocket": "Введіть WebSocket URL для моніторингу (напр., wss://example.com/socket) та додайте відображуване ім'я.",
"hardware": "Відстежуйте використання ЦП, пам'яті, диска та температуру вашої інфраструктури. URL точки доступу Capture має містити повний шлях /api/v1/metrics (наприклад, http://192.168.1.10:5001/api/v1/metrics). У секрет авторизації вставте лише рядок токена — не додавайте 'Bearer'."
"hardware": "Відстежуйте використання ЦП, пам'яті, диска та температуру вашої інфраструктури. URL точки доступу Capture має містити повний шлях /api/v1/metrics (наприклад, http://192.168.1.10:5001/api/v1/metrics). У секрет авторизації вставте лише рядок токена — не додавайте 'Bearer'.",
"dns": "Стежте, чи відповідає певний DNS-сервер."
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "Використовуйте HTTP(S) для моніторингу Вашого сайту або API-ендпоінту.",
"optionPingDescription": "Використовуйте ICMP Ping для моніторингу доступності сервера.",
"optionPortDescription": "Моніторинг відкритості конкретного порту на сервері.",
"title": "Тип"
"title": "Тип",
"optionDnsDescription": "Стежте, чи відповідає певний DNS-сервер."
},
"thresholds": {
"title": "Порогові значення сповіщень",
@@ -765,7 +802,7 @@
"headers": {
"endTime": "Час завершення",
"resolutionType": "Тип вирішення",
"startTime": "",
"startTime": "Час початку",
"statusCode": "Код статусу"
},
"resolvedIncidents": "Вирішені інциденти",
@@ -809,13 +846,13 @@
}
},
"statBoxes": {
"avgCpuTemperature": "",
"cpuFrequency": "",
"cpuLogical": "",
"cpuPhysical": "",
"avgCpuTemperature": "Середня температура CPU",
"cpuFrequency": "Частота CPU",
"cpuLogical": "CPU (логічний)",
"cpuPhysical": "CPU (фізичний)",
"disk": "Диск",
"memory": "Пам'ять",
"os": ""
"os": "ОС"
},
"table": {
"headers": {
@@ -909,7 +946,8 @@
"lastFinishedAt": "Останнє завершення",
"lastRunTook": "Останній запуск тривав",
"lastFailedAt": "Остання помилка",
"failReason": "Причина помилки"
"failReason": "Причина помилки",
"state": "Стан"
}
},
"queueFallback": {
@@ -929,8 +967,8 @@
},
"table": {
"headers": {
"nextWindow": "",
"repeat": ""
"nextWindow": "Наступне вікно",
"repeat": "Повторити"
}
},
"form": {
@@ -1032,7 +1070,7 @@
},
"telegram": {
"title": "Налаштування Telegram",
"description": "",
"description": "Налаштуйте свого Telegram-бота для надсилання сповіщень.",
"optionBotToken": "Токен бота",
"placeholderBotToken": "123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ",
"optionChatId": "ID чату",
@@ -1099,6 +1137,10 @@
"header": {
"title": "Монітори PageSpeed",
"description": "Відстежуйте Core Web Vitals і оцінки Lighthouse у часі, щоб помітити регресії раніше за користувачів."
},
"strategy": {
"desktop": "Десктоп",
"mobile": "Мобільний"
}
},
"settings": {
@@ -1201,7 +1243,7 @@
"email": {
"title": "Налаштування пошти",
"description": "Налаштуйте параметри електронної пошти для Вашої системи. Використовується для надсилання сповіщень та попереджень.",
"descriptionTransport": "",
"descriptionTransport": "Це створює SMTP-транспорт для NodeMailer",
"descriptionTransportLink": "Дивіться специфікації тут",
"option": {
"host": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "Відхиляти недійсні сертифікати: відхиляти з'єднання із самопідписаними або ненадійними сертифікатами"
},
"displayName": {
"label": "Ім'я відправника (необов'язково) - Дружнє ім'я, що відображається в полі Від",
"placeholder": "Сповіщення Checkmate"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "Час роботи",
"responseTime": "Час відповіді"
},
"chart": {
"avg": "сер {{value}}мс",
"downTooltip": "Не працює",
"heatmapAria": "Теплова карта часу відгуку",
"max": "макс {{value}}мс"
},
"status": {
"breached": "Перевищено поріг",
"down": "Не працює",
"initializing": "Ініціалізація",
"maintenance": "Обслуговування",
"paused": "Призупинено",
"up": "Працює"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "Деякі системи на техобслуговуванні",
"partiallyPaused": "Деякі системи призупинено",
"initializing": "Деякі системи ініціалізуються",
"noMonitors": "Не налаштовано жодного монітора"
"noMonitors": "Не налаштовано жодного монітора",
"monitoringSummary": "Відстежується {{count}} сервіс · Оновлення кожні 10 секунд",
"monitoringSummary_other": "Відстежується {{count}} сервісів · Оновлення кожні 10 секунд"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "Показувати метрики інфраструктури"
}
}
},
"theme": {
"description": "Виберіть візуальний стиль для вашої публічної сторінки стану.",
"label": "Тема",
"options": {
"bold": {
"description": "Велика типографіка та впевнені відступи. Чудово для брендових сторінок.",
"name": "Жирний"
},
"editorial": {
"description": "Заголовки з засічками та тонкі роздільники. Відмінний і спокійний.",
"name": "Редакційний"
},
"modern": {
"description": "Рівень SaaS з пульсуючою крапкою стану та м'яко зникаючими картками.",
"name": "Сучасний"
},
"refined": {
"description": "Чистий, професійний, близький до класичного вигляду Checkmate.",
"name": "Витончений"
}
},
"title": "Тема сторінки"
},
"themeMode": {
"auto": "Авто (за пристроєм відвідувача)",
"dark": "Тільки темна",
"label": "Режим теми",
"light": "Тільки світла"
}
},
"header": {
"title": "Сторінки статусу",
"description": "Публікуйте публічні сторінки з часом роботи в реальному часі та історією інцидентів для клієнтів і зацікавлених сторін."
},
"editorial": {
"allUp": "Усі системи працюють у нормальному режимі.",
"reportTitle": "Звіт про обслуговування"
},
"footer": {
"poweredBy": "Працює на"
}
},
"uptime": {
+130 -32
View File
@@ -73,7 +73,9 @@
"paused": "tạm dừng",
"na": "N/A",
"resolved": "Đã giải quyết",
"responseTime": "Thời gian phản hồi"
"responseTime": "Thời gian phản hồi",
"idle": "Không hoạt động",
"running": "Đang chạy"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "Đang hoạt động",
"responseTime": "Thời gian phản hồi"
}
},
"statusCode": {
"down": "Ngừng hoạt động",
"networkError": {
"dns": "Không thể phân giải tên máy chủ (tra cứu DNS thất bại).",
"refused": "Máy chủ từ chối kết nối (cổng đã đóng hoặc dịch vụ không lắng nghe).",
"reset": "Kết nối đã được đặt lại trước khi nhận được phản hồi.",
"timeout": "Máy chủ không phản hồi kịp thời (yêu cầu hết thời gian chờ).",
"tls": "Bắt tay TLS hoặc SSL thất bại (vấn đề chứng chỉ hoặc mã hóa).",
"unknown": "Không nhận được phản hồi HTTP từ mục tiêu."
}
}
},
"components": {
@@ -306,28 +319,28 @@
"common": {
"passwordRules": {
"length": {
"beginning": "",
"highlighted": ""
"beginning": "Phải có ít nhất",
"highlighted": "8 ký tự"
},
"lowercase": {
"beginning": "",
"highlighted": ""
"beginning": "Phải chứa ít nhất",
"highlighted": "một chữ thường"
},
"match": {
"beginning": "",
"highlighted": ""
"beginning": "Mật khẩu",
"highlighted": "phải khớp"
},
"number": {
"beginning": "",
"highlighted": ""
"beginning": "Phải chứa ít nhất",
"highlighted": "một chữ số"
},
"special": {
"beginning": "",
"highlighted": ""
"beginning": "Phải chứa ít nhất",
"highlighted": "một ký tự đặc biệt (!?@#$%^&*()-_=+[]{}|;:'\",./\\~`)"
},
"uppercase": {
"beginning": "",
"highlighted": ""
"beginning": "Phải chứa ít nhất",
"highlighted": "một chữ hoa"
}
},
"form": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "Hạ tầng",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "Đã tạm dừng {{count}} màn hình",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "Màn hình đã chọn đã được tạm dừng",
"alreadyPaused_other": "Tất cả các màn hình đã chọn đã được tạm dừng",
"alreadyRunning_one": "Màn hình đã chọn đã đang chạy",
"alreadyRunning_other": "Tất cả các màn hình đã chọn đã đang chạy"
"alreadyRunning_other": "Tất cả các màn hình đã chọn đã đang chạy",
"partialFailure": "Đã cập nhật {{successCount}} trình giám sát, nhưng {{failedCount}} trình giám sát không thể đồng bộ hóa với hàng đợi công việc"
}
}
},
@@ -552,19 +567,40 @@
"captureUrl": {
"label": "Điểm cuối Capture",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "Loại bản ghi DNS",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "Máy chủ DNS",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "Máy tính để bàn",
"label": "Chiến lược",
"mobile": "Di động"
}
},
"title": "Cài đặt chung",
"description": {
"http": "Nhập URL hoặc IP cần giám sát (vd: https://example.com/ hoặc 192.168.1.100) và thêm tên hiển thị rõ ràng trên bảng điều khiển.",
"ping": "",
"port": "",
"docker": "",
"ping": "Nhập địa chỉ IP hoặc tên máy chủ để ping (ví dụ: 192.168.1.100 hoặc example.com) và thêm tên hiển thị rõ ràng xuất hiện trên bảng điều khiển.",
"port": "Nhập URL hoặc IP của máy chủ, số cổng và tên hiển thị rõ ràng xuất hiện trên bảng điều khiển.",
"docker": "Nhập tên hoặc ID của container Docker. Bạn có thể sử dụng tên container (ví dụ: my-app) hoặc ID container (ID đầy đủ 64 ký tự hoặc ID ngắn).",
"game": "Nhập địa chỉ IP hoặc tên máy chủ và số cổng để ping (ví dụ: 192.168.1.100 hoặc example.com) và chọn loại trò chơi. Một số trò chơi yêu cầu bật truy vấn máy chủ trong tệp cấu hình của chúng — hãy xem tài liệu của máy chủ trò chơi. Xem <gamedigLink>danh sách các trò chơi được hỗ trợ</gamedigLink> để biết chi tiết giao thức.",
"pagespeed": "Theo dõi hiệu suất tải trang, Core Web Vitals và điểm tối ưu hóa cho trang web của bạn.",
"grpc": "Nhập tên máy chủ và cổng của máy chủ gRPC, tùy chọn chỉ định tên dịch vụ cho kiểm tra sức khỏe và thêm tên hiển thị.",
"websocket": "Nhập URL WebSocket cần giám sát (vd: wss://example.com/socket) và thêm tên hiển thị.",
"hardware": "Giám sát mức sử dụng CPU, bộ nhớ, ổ đĩa và nhiệt độ của hạ tầng. URL điểm cuối Capture phải bao gồm đường dẫn đầy đủ /api/v1/metrics (ví dụ: http://192.168.1.10:5001/api/v1/metrics). Đối với khóa xác thực, chỉ dán chuỗi token — không bao gồm 'Bearer'."
"hardware": "Giám sát mức sử dụng CPU, bộ nhớ, ổ đĩa và nhiệt độ của hạ tầng. URL điểm cuối Capture phải bao gồm đường dẫn đầy đủ /api/v1/metrics (ví dụ: http://192.168.1.10:5001/api/v1/metrics). Đối với khóa xác thực, chỉ dán chuỗi token — không bao gồm 'Bearer'.",
"dns": "Giám sát xem một máy chủ DNS cụ thể có phản hồi hay không."
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "Sử dụng HTTP(S) để giám sát trang web hoặc điểm cuối API của bạn.",
"optionPingDescription": "Sử dụng ICMP Ping để giám sát xem máy chủ có trực tuyến không.",
"optionPortDescription": "Giám sát xem một cổng cụ thể trên máy chủ có mở không.",
"title": "Loại"
"title": "Loại",
"optionDnsDescription": "Giám sát xem một máy chủ DNS cụ thể có phản hồi hay không."
},
"thresholds": {
"title": "Ngưỡng cảnh báo",
@@ -765,7 +802,7 @@
"headers": {
"endTime": "Thời gian kết thúc",
"resolutionType": "Loại giải quyết",
"startTime": "",
"startTime": "Thời gian bắt đầu",
"statusCode": "Mã trạng thái"
},
"resolvedIncidents": "Sự cố đã giải quyết",
@@ -809,13 +846,13 @@
}
},
"statBoxes": {
"avgCpuTemperature": "",
"cpuFrequency": "",
"cpuLogical": "",
"cpuPhysical": "",
"avgCpuTemperature": "Nhiệt độ CPU trung bình",
"cpuFrequency": "Tần số CPU",
"cpuLogical": "CPU (logic)",
"cpuPhysical": "CPU (vật lý)",
"disk": "Ổ đĩa",
"memory": "Bộ nhớ",
"os": ""
"os": "Hệ điều hành"
},
"table": {
"headers": {
@@ -909,7 +946,8 @@
"lastFinishedAt": "Hoàn thành lần cuối lúc",
"lastRunTook": "Lần chạy cuối mất",
"lastFailedAt": "Lỗi lần cuối lúc",
"failReason": "Lý do lỗi"
"failReason": "Lý do lỗi",
"state": "Trạng thái"
}
},
"queueFallback": {
@@ -929,8 +967,8 @@
},
"table": {
"headers": {
"nextWindow": "",
"repeat": ""
"nextWindow": "Cửa sổ tiếp theo",
"repeat": "Lặp lại"
}
},
"form": {
@@ -1032,7 +1070,7 @@
},
"telegram": {
"title": "Cấu hình Telegram",
"description": "",
"description": "Cấu hình bot Telegram của bạn để gửi thông báo.",
"optionBotToken": "Token bot",
"placeholderBotToken": "123456789:ABCdefGhIJKlmNoPQRsTUVwxyZ",
"optionChatId": "ID cuộc trò chuyện",
@@ -1099,6 +1137,10 @@
"header": {
"title": "Trình giám sát PageSpeed",
"description": "Theo dõi Core Web Vitals và điểm Lighthouse theo thời gian để phát hiện sự suy giảm trước khi người dùng nhận ra."
},
"strategy": {
"desktop": "Máy tính để bàn",
"mobile": "Di động"
}
},
"settings": {
@@ -1201,7 +1243,7 @@
"email": {
"title": "Cài đặt email",
"description": "Cấu hình cài đặt email cho hệ thống. Được sử dụng để gửi thông báo và cảnh báo.",
"descriptionTransport": "",
"descriptionTransport": "Điều này xây dựng một SMTP transport cho NodeMailer",
"descriptionTransportLink": "Xem thông số kỹ thuật tại đây",
"option": {
"host": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "Từ chối chứng chỉ không hợp lệ: Từ chối kết nối với chứng chỉ tự ký hoặc không đáng tin cậy"
},
"displayName": {
"label": "Tên người gửi (tùy chọn) - Tên thân thiện hiển thị trong trường Từ",
"placeholder": "Thông báo Checkmate"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "Thời gian hoạt động",
"responseTime": "Thời gian phản hồi"
},
"chart": {
"avg": "TB {{value}}ms",
"downTooltip": "Ngừng hoạt động",
"heatmapAria": "Bản đồ nhiệt thời gian phản hồi",
"max": "tối đa {{value}}ms"
},
"status": {
"breached": "Vượt ngưỡng",
"down": "Ngừng hoạt động",
"initializing": "Đang khởi tạo",
"maintenance": "Bảo trì",
"paused": "Tạm dừng",
"up": "Hoạt động"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "Một số hệ thống đang bảo trì",
"partiallyPaused": "Một số hệ thống đã tạm dừng",
"initializing": "Một số hệ thống đang khởi tạo",
"noMonitors": "Chưa cấu hình màn hình giám sát nào"
"noMonitors": "Chưa cấu hình màn hình giám sát nào",
"monitoringSummary": "Đang giám sát {{count}} dịch vụ · Cập nhật mỗi 10 giây",
"monitoringSummary_other": "Đang giám sát {{count}} dịch vụ · Cập nhật mỗi 10 giây"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "Hiển thị số liệu hạ tầng"
}
}
},
"theme": {
"description": "Chọn kiểu hiển thị cho trang trạng thái công khai của bạn.",
"label": "Chủ đề",
"options": {
"bold": {
"description": "Kiểu chữ lớn và khoảng cách tự tin. Tuyệt vời cho các trang tập trung vào thương hiệu.",
"name": "Đậm"
},
"editorial": {
"description": "Tiêu đề serif và dải phân cách mỏng. Đặc biệt và tĩnh lặng.",
"name": "Biên tập"
},
"modern": {
"description": "Chất lượng SaaS với chấm trạng thái nhấp nháy và thẻ mờ dần nhẹ nhàng.",
"name": "Hiện đại"
},
"refined": {
"description": "Sạch sẽ, chuyên nghiệp, gần với giao diện Checkmate cổ điển.",
"name": "Tinh tế"
}
},
"title": "Chủ đề trang"
},
"themeMode": {
"auto": "Tự động (theo thiết bị của khách)",
"dark": "Chỉ tối",
"label": "Chế độ chủ đề",
"light": "Chỉ sáng"
}
},
"header": {
"title": "Trang trạng thái",
"description": "Công bố các trang công khai cho khách hàng và các bên liên quan thấy thời gian hoạt động theo thời gian thực và lịch sử sự cố."
},
"editorial": {
"allUp": "Tất cả các hệ thống đang hoạt động bình thường.",
"reportTitle": "Báo cáo dịch vụ"
},
"footer": {
"poweredBy": "Được cung cấp bởi"
}
},
"uptime": {
+105 -7
View File
@@ -73,7 +73,9 @@
"paused": "已暂停",
"na": "不适用",
"resolved": "已解决",
"responseTime": "响应时间"
"responseTime": "响应时间",
"idle": "空闲",
"running": "运行中"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "活跃",
"responseTime": "响应时间"
}
},
"statusCode": {
"down": "宕机",
"networkError": {
"dns": "无法解析主机名(DNS查找失败)。",
"refused": "服务器拒绝连接(端口关闭或服务未监听)。",
"reset": "在收到响应之前连接被重置。",
"timeout": "服务器未及时响应(请求超时)。",
"tls": "TLS或SSL握手失败(证书或加密问题)。",
"unknown": "未从目标接收HTTP响应。"
}
}
},
"components": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "基础设施",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "已暂停 {{count}} 个监视器",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "所选监视器已暂停",
"alreadyPaused_other": "所有选定的监视器均已暂停",
"alreadyRunning_one": "所选监视器已经在运行",
"alreadyRunning_other": "所有选定的监视器均已在运行"
"alreadyRunning_other": "所有选定的监视器均已在运行",
"partialFailure": "已更新 {{successCount}} 个监控,但有 {{failedCount}} 个无法与任务队列同步"
}
}
},
@@ -552,6 +567,26 @@
"captureUrl": {
"label": "Capture 端点",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "DNS记录类型",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "DNS服务器",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "桌面",
"label": "策略",
"mobile": "移动端"
}
},
"title": "常规设置",
@@ -564,7 +599,8 @@
"pagespeed": "跟踪网站的页面加载性能、Core Web Vitals 和优化评分。",
"grpc": "输入 gRPC 服务器的主机名和端口,可选指定健康检查的服务名称,并添加显示名称。",
"websocket": "输入要监控的 WebSocket URL(例如 wss://example.com/socket),并添加显示名称。",
"hardware": "监控基础设施的 CPU、内存、磁盘使用情况和温度。Capture 端点 URL 必须包含完整路径 /api/v1/metrics(例如 http://192.168.1.10:5001/api/v1/metrics)。授权密钥只需粘贴令牌字符串——请勿包含 'Bearer'。"
"hardware": "监控基础设施的 CPU、内存、磁盘使用情况和温度。Capture 端点 URL 必须包含完整路径 /api/v1/metrics(例如 http://192.168.1.10:5001/api/v1/metrics)。授权密钥只需粘贴令牌字符串——请勿包含 'Bearer'。",
"dns": "监控特定DNS服务器是否响应。"
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "使用 HTTP(S) 监控您的网站或 API 端点。",
"optionPingDescription": "使用 ICMP Ping 监控服务器是否在线。",
"optionPortDescription": "监控服务器上的特定端口是否开放。",
"title": "类型"
"title": "类型",
"optionDnsDescription": "监控特定DNS服务器是否响应。"
},
"thresholds": {
"title": "告警阈值",
@@ -909,7 +946,8 @@
"lastFinishedAt": "上次完成于",
"lastRunTook": "上次运行耗时",
"lastFailedAt": "上次失败于",
"failReason": "失败原因"
"failReason": "失败原因",
"state": "状态"
}
},
"queueFallback": {
@@ -1099,6 +1137,10 @@
"header": {
"title": "PageSpeed 监控器",
"description": "持续追踪 Core Web Vitals 和 Lighthouse 评分,在用户察觉之前发现性能回退。"
},
"strategy": {
"desktop": "桌面",
"mobile": "移动端"
}
},
"settings": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "拒绝无效证书:拒绝自签名或不受信任证书的连接"
},
"displayName": {
"label": "发件人姓名(可选)- 显示在发件人字段中的友好名称",
"placeholder": "Checkmate通知"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "正常运行时间",
"responseTime": "响应时间"
},
"chart": {
"avg": "平均 {{value}}ms",
"downTooltip": "宕机",
"heatmapAria": "响应时间热图",
"max": "最大 {{value}}ms"
},
"status": {
"breached": "超出阈值",
"down": "宕机",
"initializing": "初始化中",
"maintenance": "维护中",
"paused": "已暂停",
"up": "运行中"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "部分系统正在维护",
"partiallyPaused": "部分系统已暂停",
"initializing": "部分系统正在初始化",
"noMonitors": "尚未配置任何监控器"
"noMonitors": "尚未配置任何监控器",
"monitoringSummary": "正在监控{{count}}个服务 · 每10秒更新一次",
"monitoringSummary_other": "正在监控{{count}}个服务 · 每10秒更新一次"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "显示基础设施指标"
}
}
},
"theme": {
"description": "为您的公共状态页面选择视觉样式。",
"label": "主题",
"options": {
"bold": {
"description": "大字体和自信的间距。非常适合品牌导向的页面。",
"name": "粗体"
},
"editorial": {
"description": "衬线标题和细分隔线。独特而平静。",
"name": "编辑风格"
},
"modern": {
"description": "SaaS级品质,带有脉动状态点和柔和褪色卡片。",
"name": "现代"
},
"refined": {
"description": "干净、专业,接近经典Checkmate外观。",
"name": "精致"
}
},
"title": "页面主题"
},
"themeMode": {
"auto": "自动(跟随访客设备)",
"dark": "仅深色",
"label": "主题模式",
"light": "仅浅色"
}
},
"header": {
"title": "状态页面",
"description": "发布公开页面,向客户和利益相关者展示实时运行情况和事件历史。"
},
"editorial": {
"allUp": "所有系统运行正常。",
"reportTitle": "服务报告"
},
"footer": {
"poweredBy": "技术支持"
}
},
"uptime": {
+105 -7
View File
@@ -73,7 +73,9 @@
"paused": "已暫停",
"na": "N/A",
"resolved": "已解決",
"responseTime": "回應時間"
"responseTime": "回應時間",
"idle": "閒置",
"running": "執行中"
},
"form": {
"role": {
@@ -120,6 +122,17 @@
"active": "啟用中",
"responseTime": "回應時間"
}
},
"statusCode": {
"down": "停機",
"networkError": {
"dns": "無法解析主機名稱(DNS查詢失敗)。",
"refused": "伺服器拒絕連線(連接埠關閉或服務未監聽)。",
"reset": "在收到回應之前連線被重設。",
"timeout": "伺服器未及時回應(請求逾時)。",
"tls": "TLS或SSL交握失敗(憑證或加密問題)。",
"unknown": "未從目標接收HTTP回應。"
}
}
},
"components": {
@@ -452,7 +465,8 @@
"optionPagespeed": "PageSpeed",
"optionHardware": "基礎架構",
"optionGrpc": "gRPC",
"optionWebSocket": "WebSocket"
"optionWebSocket": "WebSocket",
"optionDns": "DNS"
},
"bulkPause": {
"paused_one": "已暫停 {{count}} 個監視器",
@@ -462,7 +476,8 @@
"alreadyPaused_one": "所選監視器已暫停",
"alreadyPaused_other": "所有選定的監視器均已暫停",
"alreadyRunning_one": "所選監視器已經在運行",
"alreadyRunning_other": "所有選定的監視器均已在運行"
"alreadyRunning_other": "所有選定的監視器均已在運行",
"partialFailure": "已更新 {{successCount}} 個監控,但有 {{failedCount}} 個無法與工作佇列同步"
}
}
},
@@ -552,6 +567,26 @@
"captureUrl": {
"label": "Capture 端點",
"placeholder": "http://192.168.1.10:5001/api/v1/metrics"
},
"dnsRecordType": {
"label": "DNS記錄類型",
"value": {
"A": "A",
"AAAA": "AAAA",
"CNAME": "CNAME",
"MX": "MX",
"NS": "NS",
"TXT": "TXT"
}
},
"dnsServer": {
"label": "DNS伺服器",
"placeholder": "8.8.8.8"
},
"strategy": {
"desktop": "桌面",
"label": "策略",
"mobile": "行動裝置"
}
},
"title": "一般設定",
@@ -564,7 +599,8 @@
"pagespeed": "追蹤您網站的頁面載入效能、Core Web Vitals 和最佳化分數。",
"grpc": "輸入 gRPC 伺服器的主機名稱和連接埠,可選擇指定健康檢查的服務名稱,並新增一個顯示名稱。",
"websocket": "輸入要監控的 WebSocket URL(例如 wss://example.com/socket),並新增一個顯示名稱。",
"hardware": "監控基礎架構的 CPU、記憶體、磁碟使用情況和溫度。Capture 端點 URL 必須包含完整路徑 /api/v1/metrics(例如 http://192.168.1.10:5001/api/v1/metrics)。授權密鑰僅貼上權杖字串——請勿包含 'Bearer'。"
"hardware": "監控基礎架構的 CPU、記憶體、磁碟使用情況和溫度。Capture 端點 URL 必須包含完整路徑 /api/v1/metrics(例如 http://192.168.1.10:5001/api/v1/metrics)。授權密鑰僅貼上權杖字串——請勿包含 'Bearer'。",
"dns": "監控特定DNS伺服器是否回應。"
}
},
"ignoreTls": {
@@ -601,7 +637,8 @@
"optionHttpDescription": "使用 HTTP(S) 監控您的網站或 API 端點。",
"optionPingDescription": "使用 ICMP Ping 監控伺服器是否上線。",
"optionPortDescription": "監控伺服器上的特定連接埠是否開啟。",
"title": "類型"
"title": "類型",
"optionDnsDescription": "監控特定DNS伺服器是否回應。"
},
"thresholds": {
"title": "警報閾值",
@@ -909,7 +946,8 @@
"lastFinishedAt": "最後完成於",
"lastRunTook": "最後執行耗時",
"lastFailedAt": "最後失敗於",
"failReason": "失敗原因"
"failReason": "失敗原因",
"state": "狀態"
}
},
"queueFallback": {
@@ -1099,6 +1137,10 @@
"header": {
"title": "PageSpeed 監視器",
"description": "持續追蹤 Core Web Vitals 與 Lighthouse 評分,在使用者察覺前發現效能退化。"
},
"strategy": {
"desktop": "桌面",
"mobile": "行動裝置"
}
},
"settings": {
@@ -1247,6 +1289,10 @@
},
"rejectUnauthorized": {
"label": "拒絕無效憑證:拒絕使用自簽或不受信任憑證的連線"
},
"displayName": {
"label": "寄件者名稱(選填)- 顯示在寄件者欄位的友善名稱",
"placeholder": "Checkmate通知"
}
}
},
@@ -1307,6 +1353,20 @@
"uptime": {
"title": "正常運行時間",
"responseTime": "回應時間"
},
"chart": {
"avg": "平均 {{value}}ms",
"downTooltip": "停機",
"heatmapAria": "回應時間熱圖",
"max": "最大 {{value}}ms"
},
"status": {
"breached": "超過閾值",
"down": "停機",
"initializing": "初始化中",
"maintenance": "維護中",
"paused": "已暫停",
"up": "運行中"
}
},
"statusBar": {
@@ -1324,7 +1384,9 @@
"maintenance": "部分系統正在維護",
"partiallyPaused": "部分系統已暫停",
"initializing": "部分系統正在初始化",
"noMonitors": "尚未設定任何監視器"
"noMonitors": "尚未設定任何監視器",
"monitoringSummary": "正在監控{{count}}個服務 · 每10秒更新一次",
"monitoringSummary_other": "正在監控{{count}}個服務 · 每10秒更新一次"
},
"table": {
"headers": {
@@ -1415,11 +1477,47 @@
"label": "顯示基礎架構指標"
}
}
},
"theme": {
"description": "為您的公開狀態頁面選擇視覺樣式。",
"label": "主題",
"options": {
"bold": {
"description": "大字體和自信的間距。非常適合品牌導向的頁面。",
"name": "粗體"
},
"editorial": {
"description": "襯線標題和細分隔線。獨特而平靜。",
"name": "編輯風格"
},
"modern": {
"description": "SaaS級品質,帶有脈動狀態點和柔和淡出的卡片。",
"name": "現代"
},
"refined": {
"description": "乾淨、專業,接近經典Checkmate外觀。",
"name": "精緻"
}
},
"title": "頁面主題"
},
"themeMode": {
"auto": "自動(依照訪客裝置)",
"dark": "僅深色",
"label": "主題模式",
"light": "僅淺色"
}
},
"header": {
"title": "狀態頁面",
"description": "發佈公開頁面,向客戶和利害關係人顯示即時的運行狀態與事件歷史。"
},
"editorial": {
"allUp": "所有系統運作正常。",
"reportTitle": "服務報告"
},
"footer": {
"poweredBy": "技術支援"
}
},
"uptime": {
+204
View File
@@ -0,0 +1,204 @@
<div dir="rtl">
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
> ⚠️ هذه الترجمة مسودة آلية. مرحبًا بمراجعات الناطقين الأصليين عبر pull request.
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>تطبيق مفتوح المصدر لمراقبة الجاهزية والبنية التحتية</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
يحتوي هذا المستودع على واجهة المستخدم والخادم الخلفي معًا لـ Checkmate، وهي أداة مراقبة مفتوحة المصدر وقابلة للاستضافة الذاتية، تتيح تتبع عتاد الخوادم، وحالة التشغيل، وأزمنة الاستجابة، والحوادث في الوقت الحقيقي بتصورات أنيقة. يقوم Checkmate بفحص الخادم/الموقع بشكل دوري للتأكد من إمكانية الوصول إليه وعمله على النحو الأمثل، ويوفر تنبيهات وتقارير فورية حول توافر الخدمات المراقبة، وفترات التوقف، وأزمنة الاستجابة.
يمتلك Checkmate أيضًا وكيلًا يُسمى [Capture](https://github.com/bluewave-labs/capture) لجلب البيانات من الخوادم البعيدة. ليس Capture مطلوبًا لتشغيل Checkmate، لكنه يوفر معلومات إضافية حول حالة المعالج، والذاكرة، والقرص، ودرجة الحرارة لخوادمك. يعمل Capture على Linux وWindows وMac وRaspberry Pi، أو أي جهاز قادر على تشغيل Go.
تم اختبار Checkmate تحت الضغط مع أكثر من 1000 مراقبة نشطة دون أي مشاكل أو اختناقات أداء ملحوظة.
## 📚 المحتويات
- [📦 العرض التجريبي](#demo)
- [🔗 دليل المستخدم](#users-guide)
- [🛠️ التثبيت](#installation)
- [🚀 الأداء](#performance)
- [💚 الأسئلة والأفكار](#questions--ideas)
- [🧩 المزايا](#features)
- [🏗️ لقطات الشاشة](#screenshots)
- [🏗️ التقنيات المستخدمة](#tech-stack)
- [🔗 بعض الروابط](#a-few-links)
- [🤝 المساهمة](#contributing)
<a id="demo"></a>
## العرض التجريبي
يمكنك مشاهدة آخر إصدار من [Checkmate](https://demo.checkmate.so/) أثناء التشغيل.
اسم المستخدم هو demouser@demo.com وكلمة المرور Demouser1! (ملاحظة: نقوم بتحديث خادم العرض التجريبي من حين لآخر، فإذا لم يعمل، يُرجى إخبارنا في قناة Discussions).
<a id="users-guide"></a>
## دليل المستخدم
يمكنك العثور على تعليمات الاستخدام [هنا](https://checkmate.so/docs).
## المتطلبات المسبقة
- تثبيت [Docker](https://www.docker.com/)
- تثبيت [Git](https://git-scm.com/)
<a id="installation"></a>
## التثبيت
اطّلع على تعليمات التثبيت في [بوابة وثائق Checkmate](https://checkmate.so/docs).
بدلاً من ذلك، يمكنك استخدام [Coolify](https://coolify.io/)، أو [Elestio](https://elest.io/open-source/checkmate)، أو [K8s](./charts/helm/checkmate/INSTALLATION.md)، أو [Sive Host](https://sive.host) (جنوب أفريقيا)، أو [Cloudzy](https://cloudzy.com/marketplace/checkmate)، أو [Pikapods](https://www.pikapods.com/) لتشغيل نسخة من Checkmate بسرعة. إن أردت مراقبة البنية التحتية لخوادمك، ستحتاج إلى [وكيل Capture](https://github.com/bluewave-labs/capture). يحتوي مستودع Capture أيضًا على تعليمات التثبيت.
### استخدام شهادة CA مخصصة
إذا كنت بحاجة لمراقبة نقاط نهاية HTTPS داخلية بشهادات صادرة عن سلطات شهادات خاصة (مثل Smallstep)، راجع [دليل الثقة بشهادات CA المخصصة](../custom-ca-trust.md) لخيارات إعداد Docker.
للمزيد من الوثائق، راجع [مجلد docs](../).
<a id="performance"></a>
## الأداء
بفضل التحسينات الواسعة، يعمل Checkmate ببصمة ذاكرة صغيرة استثنائيًا، ولا يتطلب سوى الحد الأدنى من الذاكرة وموارد المعالج. فيما يلي استخدام الذاكرة لنسخة Node.js على خادم يراقب 323 خادمًا كل دقيقة:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
ويمكنك أيضًا رؤية بصمة الذاكرة لـ MongoDB وRedis على الخادم نفسه (398 ميجابايت و15 ميجابايت) لنفس عدد الخوادم:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## الأسئلة والأفكار
إذا كانت لديك أسئلة أو اقتراحات أو تعليقات، فلديك عدة خيارات:
- [قناة Discord](https://discord.gg/NAb6H3UTjK) (مفضّلة)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (نمر عليها من حين لآخر)
لا تتردد في طرح الأسئلة أو مشاركة أفكارك — يسعدنا التواصل معك!
<a id="features"></a>
## المزايا
- مفتوح المصدر بالكامل، يمكن نشره على خوادمك أو على أجهزتك المنزلية (مثل Raspberry Pi 4 أو 5)
- خيارات مراقبة متعددة: الجاهزية، Docker، Ping، SSL، المنفذ، خادم الألعاب
- مراقبة سرعة الصفحة
- مراقبة البنية التحتية (الذاكرة، استخدام القرص، أداء المعالج، الشبكة، إلخ) — يتطلب وكيل [Capture](https://github.com/bluewave-labs/capture)
- مراقبة انتقائية للأقراص مع اختيار نقاط التحميل
- متابعة الحوادث بنظرة واحدة
- صفحات حالة بأربعة قوالب أنيقة
- إشعارات عبر البريد الإلكتروني، Webhooks، Discord، Slack، PagerDuty، Matrix، Microsoft Teams، Telegram، Pushover، Twilio (SMS)
- صيانة مجدولة
- مراقبة باستخدام استعلام JSON
- دعم متعدد اللغات: العربية، الصينية (المبسطة)، الصينية (التقليدية، تايوان)، التشيكية، الإنجليزية، الفنلندية، الفرنسية، الألمانية، اليابانية، البرتغالية (البرازيلية)، الروسية، الإسبانية، التايلاندية، التركية، الأوكرانية، والفيتنامية
## دورة حياة المراقبة
1. تقوم المراقبة بإجراء فحص (HTTP / Ping / منفذ / فحص عتاد عبر وكيل Capture)
2. يتم تخزين النتيجة (نجاح/فشل + زمن الاستجابة)
3. تُقيَّم النتائج الأخيرة وفقًا لعتبة تغيّر الحالة المضبوطة للمراقبة
4. إذا تحققت العتبة وكانت الحالة الحالية تختلف عن السابقة، تتغير حالة المراقبة (مثل `initializing`, `up`, `down`, `breached`)
5. عند تغير الحالة، يتم إنشاء حادث أو حله بحسب الحالة الحالية للمراقبة
6. تُطلَق الإشعارات استنادًا إلى الإعدادات
<a id="screenshots"></a>
## لقطات الشاشة
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## التقنيات المستخدمة
- [ReactJs](https://react.dev/)
- [MUI (إطار عمل React)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- والكثير من المكونات مفتوحة المصدر الأخرى!
<a id="a-few-links"></a>
## بعض الروابط
- إذا أردت دعمنا، فضع ⭐ على المستودع وانقر على "watch".
- لديك سؤال أو اقتراح يخص خارطة الطريق/المزايا؟ تفضل بزيارة [قناة Discord](https://discord.gg/NAb6H3UTjK) أو منتدى [Discussions](https://github.com/bluewave-labs/checkmate/discussions).
- هل تحتاج إشعارًا عند صدور إصدار جديد؟ استخدم [Newreleases](https://newreleases.io/) — وهي خدمة مجانية لتتبع الإصدارات.
- شاهد [فيديو تثبيت Checkmate واستخدامه](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## المساهمة
نحن [Alex](http://github.com/ajhollid) (قائد الفريق)، و[Gorkem](http://github.com/gorkem-bwl/)، و[Aryaman](https://github.com/Br0wnHammer)، و[Mert](https://github.com/mertssmnoglu)، و[Karen](https://github.com/karenvicent) نساعد الأفراد والشركات على مراقبة بنيتهم التحتية وخوادمهم.
نفخر ببناء علاقات قوية مع المساهمين على جميع المستويات. وعلى الرغم من حداثة المشروع، فقد حصل Checkmate بالفعل على أكثر من 7000 نجمة، وجذب أكثر من 90 مساهمًا من جميع أنحاء العالم.
تم تنجيم مستودعنا من قِبل موظفين في **Google، Microsoft، Intel، Cisco، Tencent، Electronic Arts، ByteDance، JP Morgan Chase، Deloitte، Accenture، Foxconn، Broadcom، China Telecom، Barclays، Capgemini، Wipro، Cloudflare، Dassault Systèmes، وNEC** — فلا تتردد، انضم إلينا، وساهم، وتعلّم معنا!
كيف يمكنك المساهمة:
0. ضع نجمة على هذا المستودع :)
1. راجع [دليل المساهمين](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). يُشجَّع المبتدئون على تفقد وسم `good-first-issue`.
2. اقرأ بنية [Checkmate](https://deepwiki.com/bluewave-labs/Checkmate) المفصّلة إذا أردت التعمق في المعمارية.
3. افتح issue إذا كنت تعتقد أنك واجهت مشكلة.
4. إذا كنت قادمًا جديدًا، فابحث عن وسوم `good-first-issue`.
5. أنشئ pull request لإضافة ميزات جديدة، أو تحسينات في تجربة المستخدم، أو إصلاح الأخطاء.
6. تصفّح الجولة التفاعلية في قاعدة كود `Checkmate` على CodeCanvas [من هنا](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true). لتحسين محاكاة تدفق البيانات الحالية أو إنشاء جديدة، اتبع البرنامج التعليمي السريع [هنا](https://docs.code-canvas.com/updating-diagram).
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
</div>
+200
View File
@@ -0,0 +1,200 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
> ⚠️ Aquesta traducció és un esborrany generat de manera assistida. Es valoren les revisions de parlants natius a través d'un pull request.
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Una aplicació de codi obert per a la monitorització de disponibilitat i infraestructura</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
Aquest repositori conté tant el frontend com el backend de Checkmate, una eina de monitorització de codi obert i auto-allotjable per fer el seguiment del maquinari del servidor, la disponibilitat, els temps de resposta i els incidents en temps real amb visualitzacions atractives. Checkmate comprova regularment si un servidor o lloc web és accessible i funciona correctament, i ofereix alertes i informes en temps real sobre la disponibilitat, les caigudes i els temps de resposta dels serveis monitoritzats.
Checkmate també disposa d'un agent anomenat [Capture](https://github.com/bluewave-labs/capture) per recuperar dades de servidors remots. Tot i que Capture no és necessari per executar Checkmate, proporciona informació addicional sobre l'estat de la CPU, la RAM, el disc i la temperatura dels servidors. Capture pot executar-se a Linux, Windows, Mac, Raspberry Pi o qualsevol dispositiu capaç d'executar Go.
Checkmate s'ha provat sota càrrega amb més de 1.000 monitors actius sense cap problema o coll d'ampolla notable.
## 📚 Taula de continguts
- [📦 Demo](#demo)
- [🔗 Guia d'usuari](#users-guide)
- [🛠️ Instal·lació](#installation)
- [🚀 Rendiment](#performance)
- [💚 Preguntes i idees](#questions--ideas)
- [🧩 Funcionalitats](#features)
- [🏗️ Captures de pantalla](#screenshots)
- [🏗️ Stack tecnològic](#tech-stack)
- [🔗 Alguns enllaços](#a-few-links)
- [🤝 Contribuir](#contributing)
<a id="demo"></a>
## Demo
Pots veure la darrera versió de [Checkmate](https://demo.checkmate.so/) en acció.
L'usuari és demouser@demo.com i la contrasenya és Demouser1! (Nota: actualitzem el servidor de demostració de tant en tant; si no funciona, escriu-nos al canal Discussions).
<a id="users-guide"></a>
## Guia d'usuari
Pots trobar les instruccions d'ús [aquí](https://checkmate.so/docs).
## Requisits previs
- [Docker](https://www.docker.com/) instal·lat
- [Git](https://git-scm.com/) instal·lat
<a id="installation"></a>
## Instal·lació
Consulta les instruccions d'instal·lació al [portal de documentació de Checkmate](https://checkmate.so/docs).
Alternativament, també pots utilitzar [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (Sud-àfrica), [Cloudzy](https://cloudzy.com/marketplace/checkmate) o [Pikapods](https://www.pikapods.com/) per posar en marxa ràpidament una instància de Checkmate. Si vols monitoritzar la infraestructura de servidors, necessitaràs l'[agent Capture](https://github.com/bluewave-labs/capture). El repositori de Capture també conté les instruccions d'instal·lació.
### Utilitzar una CA personalitzada
Si necessites monitoritzar punts HTTPS interns amb certificats d'autoritats certificadores privades (com Smallstep), consulta la nostra [Guia de confiança per a CA personalitzades](../custom-ca-trust.md) per a les opcions de configuració de Docker.
Per a més documentació, consulta el [directori docs](../).
<a id="performance"></a>
## Rendiment
Gràcies a una optimització exhaustiva, Checkmate funciona amb un consum de memòria excepcionalment baix, requerint un mínim de RAM i CPU. Aquí tens l'ús de memòria d'una instància de Node.js en un servidor que monitoritza 323 servidors cada minut:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
Aquí pots veure la petjada de memòria de MongoDB i Redis al mateix servidor (398 MB i 15 MB) per al mateix nombre de servidors:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Preguntes i idees
Si tens preguntes, suggeriments o comentaris, tens diverses opcions:
- [Canal de Discord](https://discord.gg/NAb6H3UTjK) (preferit)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (hi entrem de tant en tant)
No dubtis a preguntar o compartir les teves idees — ens encantarà tenir notícies teves!
<a id="features"></a>
## Funcionalitats
- Totalment de codi obert, desplegable als teus servidors o dispositius domèstics (per ex. Raspberry Pi 4 o 5)
- Diverses opcions de monitorització: disponibilitat, Docker, ping, SSL, port, servidor de jocs
- Monitorització de velocitat de pàgina
- Monitorització d'infraestructura (memòria, ús de disc, rendiment de CPU, xarxa, etc.) — requereix l'agent [Capture](https://github.com/bluewave-labs/capture)
- Monitorització selectiva de disc amb selecció de punts de muntatge
- Incidents amb un cop d'ull
- Pàgines d'estat amb 4 temes elegants
- Notificacions per correu electrònic, webhooks, Discord, Slack, PagerDuty, Matrix, Microsoft Teams, Telegram, Pushover, Twilio (SMS)
- Manteniment programat
- Monitorització mitjançant consultes JSON
- Suport multilingüe per a àrab, xinès (simplificat), xinès (tradicional, Taiwan), txec, anglès, finès, francès, alemany, japonès, portuguès (Brasil), rus, espanyol, tailandès, turc, ucraïnès i vietnamita
## Cicle de vida d'un monitor
1. Un monitor executa una comprovació (HTTP / ping / port / maquinari mitjançant l'agent Capture)
2. El resultat es desa (èxit/fracàs + temps de resposta)
3. Els resultats recents s'avaluen contra el llindar de canvi d'estat configurat al monitor
4. Si s'assoleix el llindar i l'estat actual difereix de l'anterior, l'estat del monitor canvia (per ex. `initializing`, `up`, `down`, `breached`)
5. En un canvi d'estat, es crea o resol un incident, segons l'estat actual del monitor
6. Es disparen les notificacions segons la configuració
<a id="screenshots"></a>
## Captures de pantalla
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Stack tecnològic
- [ReactJs](https://react.dev/)
- [MUI (framework de React)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- I molts altres components de codi obert!
<a id="a-few-links"></a>
## Alguns enllaços
- Si vols donar-nos suport, considera donar-nos una ⭐ i prémer "watch".
- Tens alguna pregunta o suggeriment per al roadmap o les funcionalitats? Visita el nostre [canal de Discord](https://discord.gg/NAb6H3UTjK) o el fòrum de [Discussions](https://github.com/bluewave-labs/checkmate/discussions).
- Vols rebre una notificació quan hi hagi una nova versió? Utilitza [Newreleases](https://newreleases.io/), un servei gratuït per seguir versions.
- Mira un [vídeo d'instal·lació i ús de Checkmate](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## Contribuir
Som l'[Alex](http://github.com/ajhollid) (líder d'equip), en [Gorkem](http://github.com/gorkem-bwl/), l'[Aryaman](https://github.com/Br0wnHammer), en [Mert](https://github.com/mertssmnoglu) i la [Karen](https://github.com/karenvicent), i ajudem persones i empreses a monitoritzar la seva infraestructura i els seus servidors.
Ens enorgullim de construir vincles forts amb els col·laboradors a tots els nivells. Tot i ser un projecte jove, Checkmate ja ha aconseguit més de 7.000 estrelles i ha atret més de 90 col·laboradors d'arreu del món.
El nostre repositori té estrelles d'empleats de **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes i NEC** — no t'estiguis de res: uneix-te, contribueix i aprèn amb nosaltres!
Així pots contribuir:
0. Posa una estrella a aquest repositori :)
1. Consulta la [guia per a col·laboradors](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). Recomanem als principiants revisar l'etiqueta `good-first-issue`.
2. Llegeix una estructura detallada de [Checkmate](https://deepwiki.com/bluewave-labs/Checkmate) si vols aprofundir en l'arquitectura.
3. Obre una issue si creus que has trobat un error.
4. Mira els `good-first-issue` si ets nou.
5. Fes una pull request per afegir noves funcionalitats, millores de qualitat o solucions a errors.
6. Fes una ullada al recorregut interactiu del codi de `Checkmate` a CodeCanvas [aquí](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true). Per refinar simulacions de flux de dades existents o crear-ne de noves, segueix el tutorial ràpid [aquí](https://docs.code-canvas.com/updating-diagram).
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+200
View File
@@ -0,0 +1,200 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
> ⚠️ Tento překlad je strojový koncept. Revize od rodilých mluvčích jsou vítány formou pull requestu.
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Open source aplikace pro monitorování dostupnosti a infrastruktury</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
Tento repozitář obsahuje jak frontend, tak backend Checkmate, open source, samohostovatelného monitorovacího nástroje pro sledování hardwaru serveru, dostupnosti, doby odezvy a incidentů v reálném čase s krásnými vizualizacemi. Checkmate pravidelně kontroluje, zda je server/web dostupný a zda funguje optimálně, a poskytuje upozornění a zprávy o dostupnosti, výpadcích a době odezvy monitorovaných služeb v reálném čase.
Checkmate má také agenta zvaného [Capture](https://github.com/bluewave-labs/capture), který získává data ze vzdálených serverů. Capture není pro běh Checkmate povinný, ale poskytuje další informace o stavu CPU, RAM, disku a teploty serverů. Capture lze provozovat na Linuxu, Windows, Macu, Raspberry Pi nebo jakémkoli zařízení, které dokáže spouštět Go.
Checkmate byl zátěžově otestován s více než 1000 aktivními monitory bez jakýchkoli problémů nebo výkonnostních úzkých míst.
## 📚 Obsah
- [📦 Demo](#demo)
- [🔗 Uživatelská příručka](#users-guide)
- [🛠️ Instalace](#installation)
- [🚀 Výkon](#performance)
- [💚 Dotazy & nápady](#questions--ideas)
- [🧩 Funkce](#features)
- [🏗️ Snímky obrazovky](#screenshots)
- [🏗️ Technologie](#tech-stack)
- [🔗 Několik odkazů](#a-few-links)
- [🤝 Přispívání](#contributing)
<a id="demo"></a>
## Demo
Nejnovější sestavení [Checkmate](https://demo.checkmate.so/) si můžete vyzkoušet v akci.
Uživatelské jméno je demouser@demo.com a heslo Demouser1! (Poznámka: demo server čas od času aktualizujeme; pokud něco nefunguje, dejte nám prosím vědět na kanále Discussions).
<a id="users-guide"></a>
## Uživatelská příručka
Pokyny k použití najdete [zde](https://checkmate.so/docs).
## Předpoklady
- Nainstalovaný [Docker](https://www.docker.com/)
- Nainstalovaný [Git](https://git-scm.com/)
<a id="installation"></a>
## Instalace
Pokyny k instalaci najdete v [dokumentačním portálu Checkmate](https://checkmate.so/docs).
Případně můžete použít [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (Jižní Afrika), [Cloudzy](https://cloudzy.com/marketplace/checkmate) nebo [Pikapods](https://www.pikapods.com/) pro rychlé spuštění instance Checkmate. Pokud chcete monitorovat infrastrukturu serverů, budete potřebovat [agenta Capture](https://github.com/bluewave-labs/capture). Repozitář Capture obsahuje také pokyny k instalaci.
### Použití vlastní CA
Pokud potřebujete monitorovat interní HTTPS endpointy s certifikáty od soukromých certifikačních autorit (jako je Smallstep), přečtěte si naši [příručku pro důvěřování vlastním CA](../custom-ca-trust.md) pro možnosti konfigurace Dockeru.
Další dokumentaci najdete v [adresáři docs](../).
<a id="performance"></a>
## Výkon
Díky rozsáhlým optimalizacím funguje Checkmate s mimořádně malým paměťovým otiskem a vyžaduje jen minimum paměti a CPU. Zde je využití paměti instance Node.js na serveru, který každou minutu monitoruje 323 serverů:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
A zde paměťový otisk MongoDB a Redis na stejném serveru (398 MB, resp. 15 MB) pro stejný počet serverů:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Dotazy & nápady
Pokud máte dotazy, návrhy nebo komentáře, máte několik možností:
- [Discord kanál](https://discord.gg/NAb6H3UTjK) (preferováno)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (kontrolujeme čas od času)
Neváhejte se ptát nebo sdílet své nápady — moc rádi vás slyšíme!
<a id="features"></a>
## Funkce
- Plně open source, nasaditelné na vlastních serverech nebo domácích zařízeních (např. Raspberry Pi 4 nebo 5)
- Několik způsobů monitorování: dostupnost, Docker, ping, SSL, port, herní server
- Monitorování rychlosti stránek
- Monitorování infrastruktury (paměť, využití disku, výkon CPU, síť atd.) — vyžaduje agenta [Capture](https://github.com/bluewave-labs/capture)
- Selektivní monitorování disků s výběrem přípojných bodů
- Incidenty na první pohled
- Stavové stránky se 4 elegantními tématy
- Notifikace přes e-mail, webhooky, Discord, Slack, PagerDuty, Matrix, Microsoft Teams, Telegram, Pushover, Twilio (SMS)
- Plánovaná údržba
- Monitorování pomocí JSON dotazů
- Vícejazyčná podpora pro arabštinu, čínštinu (zjednodušenou), čínštinu (tradiční, Tchaj-wan), češtinu, angličtinu, finštinu, francouzštinu, němčinu, japonštinu, portugalštinu (Brazílie), ruštinu, španělštinu, thajštinu, turečtinu, ukrajinštinu a vietnamštinu
## Životní cyklus monitoru
1. Monitor provede kontrolu (HTTP / ping / port / hardware přes agenta Capture)
2. Výsledek se uloží (úspěch/neúspěch + doba odezvy)
3. Nedávné výsledky se vyhodnotí proti nastavenému prahu změny stavu monitoru
4. Pokud je prah dosažen a aktuální stav se liší od předchozího, změní se stav monitoru (např. `initializing`, `up`, `down`, `breached`)
5. Při změně stavu se podle aktuálního stavu vytvoří nebo vyřeší incident
6. Notifikace se spouštějí podle konfigurace
<a id="screenshots"></a>
## Snímky obrazovky
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Technologie
- [ReactJs](https://react.dev/)
- [MUI (React framework)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- A mnoho dalších open source komponent!
<a id="a-few-links"></a>
## Několik odkazů
- Pokud nás chcete podpořit, zvažte přidělení ⭐ a kliknutí na „watch“.
- Máte dotaz nebo návrh na roadmapu/funkce? Podívejte se na náš [Discord kanál](https://discord.gg/NAb6H3UTjK) nebo fórum [Discussions](https://github.com/bluewave-labs/checkmate/discussions).
- Chcete být upozorněni na nové vydání? Použijte [Newreleases](https://newreleases.io/), bezplatnou službu pro sledování vydání.
- Podívejte se na [instalační a uživatelské video Checkmate](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## Přispívání
Jsme [Alex](http://github.com/ajhollid) (vedoucí týmu), [Gorkem](http://github.com/gorkem-bwl/), [Aryaman](https://github.com/Br0wnHammer), [Mert](https://github.com/mertssmnoglu) a [Karen](https://github.com/karenvicent) a pomáháme jednotlivcům a firmám monitorovat jejich infrastrukturu a servery.
Jsme hrdí na to, že budujeme silné vztahy s přispěvateli na všech úrovních. Přestože jsme mladý projekt, Checkmate už získal přes 7000 hvězdiček a přilákal více než 90 přispěvatelů z celého světa.
Náš repozitář mají označený zaměstnanci společností **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes a NEC** — takže neváhejte, zapojte se, přispějte a učte se s námi!
Jak přispět:
0. Dejte tomuto repozitáři hvězdičku :)
1. Přečtěte si [pokyny pro přispěvatele](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). Začátečníkům doporučujeme prohlédnout si štítek `good-first-issue`.
2. Pokud chcete proniknout do architektury, přečtěte si podrobnou strukturu [Checkmate](https://deepwiki.com/bluewave-labs/Checkmate).
3. Otevřete issue, pokud se domníváte, že jste narazili na chybu.
4. Pokud jste nováček, podívejte se po `good-first-issue`.
5. Vytvořte pull request pro přidání funkcí, drobná vylepšení nebo opravy chyb.
6. Vyzkoušejte interaktivní průchod kódovou základnou `Checkmate` na CodeCanvas [zde](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true). Pro vylepšení existujících simulací datových toků nebo vytvoření nových postupujte podle krátkého tutoriálu [zde](https://docs.code-canvas.com/updating-diagram).
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+197
View File
@@ -0,0 +1,197 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Eine Open-Source-Anwendung zur Überwachung von Verfügbarkeit und Infrastruktur</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
Dieses Repository enthält Frontend und Backend von Checkmate — einem Open-Source-, selbst gehosteten Monitoring-Tool, das Server-Hardware, Verfügbarkeit, Antwortzeiten und Vorfälle in Echtzeit mit klaren Visualisierungen verfolgt. Checkmate prüft regelmäßig, ob ein Server oder eine Website erreichbar ist und sauber läuft, und liefert Echtzeit-Alerts und Reports zu Verfügbarkeit, Ausfallzeiten und Antwortzeiten der überwachten Dienste.
Checkmate hat außerdem einen Agenten namens [Capture](https://github.com/bluewave-labs/capture), der Daten von entfernten Servern abholt. Capture ist nicht zwingend erforderlich, liefert aber zusätzliche Informationen zu CPU, RAM, Festplatte und Temperatur deiner Server. Capture läuft auf Linux, Windows, macOS, Raspberry Pi oder jedem anderen Gerät, das Go ausführen kann.
Checkmate wurde mit über 1.000 aktiven Monitoren stresstestet — ohne erkennbare Probleme oder Performance-Engpässe.
## 📚 Inhaltsverzeichnis
- [📦 Demo](#demo)
- [🔗 Benutzerhandbuch](#users-guide)
- [🛠️ Installation](#installation)
- [🚀 Leistung](#performance)
- [💚 Fragen & Ideen](#questions--ideas)
- [🧩 Funktionen](#features)
- [🏗️ Screenshots](#screenshots)
- [🏗️ Tech-Stack](#tech-stack)
- [🔗 Einige Links](#a-few-links)
- [🤝 Mitwirken](#contributing)
<a id="demo"></a>
## Demo
Den aktuellen Build von [Checkmate](https://demo.checkmate.so/) kannst du live ausprobieren.
Der Benutzername ist demouser@demo.com, das Passwort Demouser1!. (Wir aktualisieren den Demo-Server ab und zu — sollte er gerade nicht erreichbar sein, sag uns Bescheid im Discussions-Kanal.)
<a id="users-guide"></a>
## Benutzerhandbuch
Die Nutzungsanleitung findest du [hier](https://checkmate.so/docs).
## Voraussetzungen
- [Docker](https://www.docker.com/) installiert
- [Git](https://git-scm.com/) installiert
<a id="installation"></a>
## Installation
Die Installationsanleitung findest du im [Dokumentationsportal von Checkmate](https://checkmate.so/docs).
Alternativ kannst du [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (Südafrika), [Cloudzy](https://cloudzy.com/marketplace/checkmate) oder [Pikapods](https://www.pikapods.com/) nutzen, um schnell eine Checkmate-Instanz hochzuziehen. Für das Monitoring deiner Server-Infrastruktur brauchst du den [Capture-Agenten](https://github.com/bluewave-labs/capture); dessen Repository enthält ebenfalls eine Installationsanleitung.
### Eigene CA verwenden
Wenn du interne HTTPS-Endpunkte mit Zertifikaten privater Zertifizierungsstellen (z. B. Smallstep) überwachen möchtest, schau in unseren [Leitfaden für eigene CAs](../custom-ca-trust.md) — dort stehen die passenden Docker-Konfigurationen.
Weitere Dokumentation liegt im [docs-Verzeichnis](../).
<a id="performance"></a>
## Leistung
Dank umfangreicher Optimierungen läuft Checkmate mit einem außergewöhnlich geringen Speicherverbrauch und braucht nur sehr wenig RAM und CPU. Hier der Speicherverbrauch einer Node.js-Instanz auf einem Server, der jede Minute 323 Server prüft:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
Hier der Speicherverbrauch von MongoDB und Redis auf demselben Server (398 MB bzw. 15 MB) für dieselbe Anzahl von Servern:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Fragen & Ideen
Falls du Fragen, Vorschläge oder Anmerkungen hast, gibt es mehrere Anlaufstellen:
- [Discord](https://discord.gg/NAb6H3UTjK) (bevorzugt)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (wir schauen regelmäßig vorbei)
Frag einfach drauflos oder teile deine Ideen — wir freuen uns auf dein Feedback!
<a id="features"></a>
## Funktionen
- Vollständig Open Source, auf eigenen Servern oder Heimgeräten (z. B. Raspberry Pi 4 oder 5) bereitstellbar
- Mehrere Monitoring-Optionen: Uptime, Docker, Ping, SSL, Port, Game-Server
- Page-Speed-Monitoring
- Infrastruktur-Monitoring (RAM, Festplattennutzung, CPU-Leistung, Netzwerk etc.) — benötigt den [Capture](https://github.com/bluewave-labs/capture)-Agenten
- Selektives Festplatten-Monitoring mit Mountpoint-Auswahl
- Vorfälle auf einen Blick
- Statusseiten mit 4 schönen Themes
- Benachrichtigungen per E-Mail, Webhooks, Discord, Slack, PagerDuty, Matrix, Microsoft Teams, Telegram, Pushover, Twilio (SMS)
- Geplante Wartungen
- JSON-Abfrage-Monitoring
- Mehrsprachige Unterstützung für Arabisch, Chinesisch (Vereinfacht), Chinesisch (Traditionell, Taiwan), Tschechisch, Englisch, Finnisch, Französisch, Deutsch, Japanisch, Portugiesisch (Brasilien), Russisch, Spanisch, Thailändisch, Türkisch, Ukrainisch und Vietnamesisch
## Monitor-Lebenszyklus
1. Ein Monitor führt eine Prüfung durch (HTTP / Ping / Port / Hardware über den Capture-Agenten)
2. Das Ergebnis wird gespeichert (Erfolg/Fehler + Antwortzeit)
3. Die jüngsten Prüfergebnisse werden gegen den konfigurierten Schwellenwert für Statusänderungen des Monitors ausgewertet
4. Wenn der Schwellenwert erreicht ist und der aktuelle Status vom vorherigen abweicht, ändert sich der Zustand des Monitors (z. B. `initializing`, `up`, `down`, `breached`)
5. Bei einem Zustandswechsel wird je nach aktuellem Status entweder ein Vorfall erstellt oder geschlossen
6. Benachrichtigungen werden je nach Konfiguration ausgelöst
<a id="screenshots"></a>
## Screenshots
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Tech-Stack
- [ReactJs](https://react.dev/)
- [MUI (React-Framework)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- Viele weitere Open-Source-Komponenten!
<a id="a-few-links"></a>
## Einige Links
- Wenn dir das Projekt gefällt, gib ihm gerne einen ⭐ und klick auf „watch“.
- Frage oder Vorschlag zur Roadmap oder zum Funktionsumfang? Komm in unseren [Discord-Kanal](https://discord.gg/NAb6H3UTjK) oder ins [Discussions](https://github.com/bluewave-labs/checkmate/discussions)-Forum.
- Du willst benachrichtigt werden, wenn ein neues Release rauskommt? [Newreleases](https://newreleases.io/) ist ein kostenloser Release-Tracker.
- Schau dir gerne ein [Installations- und Anwendungs-Video](https://www.youtube.com/watch?v=GfFOc0xHIwY) zu Checkmate an.
<a id="contributing"></a>
## Mitwirken
Wir sind [Alex](http://github.com/ajhollid) (Team-Lead), [Gorkem](http://github.com/gorkem-bwl/), [Aryaman](https://github.com/Br0wnHammer), [Mert](https://github.com/mertssmnoglu) und [Karen](https://github.com/karenvicent) und helfen Einzelpersonen und Unternehmen dabei, ihre Infrastruktur und Server zu überwachen.
Wir sind stolz darauf, auf jeder Ebene starke Beziehungen zu unseren Beitragenden zu pflegen. Obwohl Checkmate ein junges Projekt ist, hat es bereits über 7.000 Sterne und mehr als 90 Beitragende aus aller Welt angezogen.
Mitarbeiterinnen und Mitarbeiter von **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes und NEC** haben unser Repository bereits gesternt — also keine falsche Zurückhaltung: mitmachen, beitragen und mit uns lernen!
So kannst du beitragen:
0. Gib dem Repo einen Stern :)
1. Lies den [Beitragsleitfaden](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). Neulinge schauen am besten zuerst nach dem Tag `good-first-issue`.
2. Wenn du tiefer in die Architektur einsteigen willst, lies die ausführliche Struktur von [Checkmate](https://deepwiki.com/bluewave-labs/Checkmate).
3. Öffne ein Issue, wenn du einen Bug findest.
4. Wenn du neu hier bist, schau dir die `good-first-issue`-Tasks an.
5. Mach einen Pull Request, um neue Funktionen einzubringen, die Nutzung zu verbessern oder Bugs zu fixen.
6. Schau dir den interaktiven Walkthrough durch die `Checkmate`-Codebasis auf CodeCanvas an: [hier](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true). Um bestehende Datenfluss-Simulationen zu verfeinern oder neue anzulegen, folge dem kurzen Tutorial [hier](https://docs.code-canvas.com/updating-diagram).
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+197
View File
@@ -0,0 +1,197 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Una aplicación de código abierto para monitorización de disponibilidad e infraestructura</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
Este repositorio contiene tanto el frontend como el backend de Checkmate, una herramienta de monitorización de código abierto y auto-alojable que rastrea hardware de servidores, disponibilidad, tiempos de respuesta e incidentes en tiempo real con bonitas visualizaciones. Checkmate comprueba periódicamente si un servidor o sitio web está accesible y funciona de forma óptima, y proporciona alertas e informes en tiempo real sobre la disponibilidad, el tiempo de inactividad y los tiempos de respuesta de los servicios monitorizados.
Checkmate también dispone de un agente llamado [Capture](https://github.com/bluewave-labs/capture) para obtener datos de servidores remotos. Capture no es necesario para ejecutar Checkmate, pero aporta información adicional sobre el estado de CPU, RAM, disco y temperatura de los servidores. Capture puede ejecutarse en Linux, Windows, Mac, Raspberry Pi o cualquier dispositivo que pueda ejecutar Go.
Checkmate ha sido sometido a pruebas de carga con más de 1000 monitores activos sin presentar problemas ni cuellos de botella de rendimiento.
## 📚 Tabla de contenidos
- [📦 Demo](#demo)
- [🔗 Guía de usuario](#users-guide)
- [🛠️ Instalación](#installation)
- [🚀 Rendimiento](#performance)
- [💚 Preguntas e ideas](#questions--ideas)
- [🧩 Características](#features)
- [🏗️ Capturas de pantalla](#screenshots)
- [🏗️ Stack tecnológico](#tech-stack)
- [🔗 Algunos enlaces](#a-few-links)
- [🤝 Contribuir](#contributing)
<a id="demo"></a>
## Demo
Puedes ver la última versión de [Checkmate](https://demo.checkmate.so/) en acción.
El usuario es demouser@demo.com y la contraseña es Demouser1! (ten en cuenta que actualizamos el servidor de demostración de vez en cuando; si no funciona, escríbenos en el canal Discussions).
<a id="users-guide"></a>
## Guía de usuario
Las instrucciones de uso están disponibles [aquí](https://checkmate.so/docs).
## Prerrequisitos
- [Docker](https://www.docker.com/) instalado
- [Git](https://git-scm.com/) instalado
<a id="installation"></a>
## Instalación
Consulta las instrucciones de instalación en el [portal de documentación de Checkmate](https://checkmate.so/docs).
También puedes utilizar [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (Sudáfrica), [Cloudzy](https://cloudzy.com/marketplace/checkmate) o [Pikapods](https://www.pikapods.com/) para levantar rápidamente una instancia de Checkmate. Si quieres monitorizar la infraestructura de tus servidores, necesitarás el [agente Capture](https://github.com/bluewave-labs/capture). El repositorio de Capture también incluye las instrucciones de instalación.
### Uso de una CA personalizada
Si necesitas monitorizar endpoints HTTPS internos con certificados emitidos por autoridades certificadoras privadas (como Smallstep), consulta nuestra [guía de confianza para CAs personalizadas](../custom-ca-trust.md) con las opciones de configuración para Docker.
Para más documentación, consulta el [directorio docs](../).
<a id="performance"></a>
## Rendimiento
Gracias a optimizaciones extensas, Checkmate funciona con una huella de memoria excepcionalmente baja y requiere un mínimo de memoria y CPU. A continuación se muestra el uso de memoria de una instancia de Node.js en un servidor que monitoriza 323 servidores cada minuto:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
Y aquí la huella de memoria de MongoDB y Redis en el mismo servidor (398 MB y 15 MB) para la misma cantidad de servidores:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Preguntas e ideas
Si tienes preguntas, sugerencias o comentarios, dispones de varias opciones:
- [Canal de Discord](https://discord.gg/NAb6H3UTjK) (preferido)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (entramos a verlo de vez en cuando)
No dudes en preguntar o compartir tus ideas — ¡nos encantará saber de ti!
<a id="features"></a>
## Características
- Totalmente de código abierto, desplegable en tus propios servidores o dispositivos domésticos (por ejemplo, Raspberry Pi 4 o 5)
- Varias opciones de monitorización: disponibilidad, Docker, Ping, SSL, puerto, servidor de juegos
- Monitorización de velocidad de página
- Monitorización de infraestructura (memoria, uso de disco, rendimiento de CPU, red, etc.) — requiere el agente [Capture](https://github.com/bluewave-labs/capture)
- Monitorización selectiva de discos con selección de puntos de montaje
- Incidentes de un vistazo
- Páginas de estado con 4 temas elegantes
- Notificaciones por correo electrónico, webhooks, Discord, Slack, PagerDuty, Matrix, Microsoft Teams, Telegram, Pushover, Twilio (SMS)
- Mantenimiento programado
- Monitorización mediante consultas JSON
- Soporte multiidioma para árabe, chino (simplificado), chino (tradicional, Taiwán), checo, inglés, finés, francés, alemán, japonés, portugués (Brasil), ruso, español, tailandés, turco, ucraniano y vietnamita
## Ciclo de vida de un monitor
1. Un monitor ejecuta una comprobación (HTTP / Ping / puerto / hardware vía el agente Capture)
2. El resultado se almacena (éxito/fallo + tiempo de respuesta)
3. Los resultados recientes se evalúan frente al umbral de cambio de estado configurado para el monitor
4. Si se alcanza el umbral y el estado actual difiere del anterior, el estado del monitor cambia (por ejemplo, `initializing`, `up`, `down`, `breached`)
5. Al cambiar de estado, se crea o resuelve un incidente según el estado actual del monitor
6. Las notificaciones se disparan según la configuración
<a id="screenshots"></a>
## Capturas de pantalla
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Stack tecnológico
- [ReactJs](https://react.dev/)
- [MUI (framework de React)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- ¡Y muchos otros componentes de código abierto!
<a id="a-few-links"></a>
## Algunos enlaces
- Si quieres apoyarnos, dale a ⭐ y pulsa "watch".
- ¿Tienes una pregunta o sugerencia sobre la hoja de ruta o las funcionalidades? Pásate por nuestro [canal de Discord](https://discord.gg/NAb6H3UTjK) o el foro de [Discussions](https://github.com/bluewave-labs/checkmate/discussions).
- ¿Quieres una notificación cuando salga una nueva versión? Usa [Newreleases](https://newreleases.io/), un servicio gratuito para seguir releases.
- Mira un [vídeo de instalación y uso de Checkmate](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## Contribuir
Somos [Alex](http://github.com/ajhollid) (team lead), [Gorkem](http://github.com/gorkem-bwl/), [Aryaman](https://github.com/Br0wnHammer), [Mert](https://github.com/mertssmnoglu) y [Karen](https://github.com/karenvicent), y ayudamos a personas y empresas a monitorizar su infraestructura y sus servidores.
Nos enorgullecemos de construir relaciones sólidas con la comunidad de contribuidores de cualquier nivel. A pesar de ser un proyecto joven, Checkmate ya ha conseguido más de 7000 estrellas y reúne a 90+ contribuidores de todo el mundo.
Nuestro repositorio cuenta con estrellas de empleados de **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes y NEC** — así que no te cortes: únete, contribuye y aprende con nosotros.
Cómo puedes contribuir:
0. Pon una estrella a este repo :)
1. Revisa la [guía de contribución](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). Si es tu primera vez, te recomendamos mirar la etiqueta `good-first-issue`.
2. Lee la estructura detallada de [Checkmate](https://deepwiki.com/bluewave-labs/Checkmate) si quieres profundizar en la arquitectura.
3. Abre un issue si crees que has encontrado un bug.
4. Busca `good-first-issue` si eres nuevo en el proyecto.
5. Abre un pull request para añadir nuevas funciones, mejoras o correcciones de bugs.
6. Echa un vistazo a este recorrido interactivo del código de `Checkmate` en CodeCanvas [aquí](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true). Para mejorar las simulaciones de flujo de datos existentes o crear nuevas, sigue el tutorial rápido [aquí](https://docs.code-canvas.com/updating-diagram).
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+200
View File
@@ -0,0 +1,200 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
> ⚠️ Tämä käännös on konekäännöksen luonnos. Äidinkielisten puhujien tarkistuksia otetaan mielellään vastaan pull requestilla.
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Avoimen lähdekoodin sovellus saatavuuden ja infrastruktuurin valvontaan</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
Tämä repositorio sisältää Checkmaten frontendin ja backendin — avoimen lähdekoodin, itse isännöitävän valvontatyökalun, jolla seurataan reaaliaikaisesti palvelinten laitteistoa, saatavuutta, vasteaikoja ja häiriöitä näyttävien visualisointien avulla. Checkmate tarkistaa säännöllisesti, ovatko palvelin tai sivusto tavoitettavissa ja toimivatko ne odotetusti, sekä antaa reaaliaikaisia hälytyksiä ja raportteja valvottujen palvelujen saatavuudesta, käyttökatkoista ja vasteajoista.
Checkmatella on myös agentti nimeltä [Capture](https://github.com/bluewave-labs/capture), joka kerää tietoja etäpalvelimilta. Capture ei ole välttämätön Checkmaten ajamiseen, mutta se tarjoaa lisätietoja palvelimiesi CPU:n, RAM-muistin, levyn ja lämpötilan tilasta. Capture toimii Linuxissa, Windowsissa, Macissa, Raspberry Pissä ja kaikissa Go-yhteensopivissa laitteissa.
Checkmate on rasitustestattu yli 1000 aktiivisella valvomolla ilman merkittäviä ongelmia tai suorituskykypullonkauloja.
## 📚 Sisällysluettelo
- [📦 Demo](#demo)
- [🔗 Käyttöopas](#users-guide)
- [🛠️ Asennus](#installation)
- [🚀 Suorituskyky](#performance)
- [💚 Kysymykset & ideat](#questions--ideas)
- [🧩 Ominaisuudet](#features)
- [🏗️ Kuvakaappaukset](#screenshots)
- [🏗️ Teknologiat](#tech-stack)
- [🔗 Muutamia linkkejä](#a-few-links)
- [🤝 Osallistuminen](#contributing)
<a id="demo"></a>
## Demo
Voit kokeilla uusinta [Checkmate-versiota](https://demo.checkmate.so/) käytännössä.
Käyttäjätunnus on demouser@demo.com ja salasana Demouser1! (Huom: päivitämme demopalvelinta ajoittain; jos se ei toimi, kerro meille Discussions-kanavalla).
<a id="users-guide"></a>
## Käyttöopas
Käyttöohjeet löytyvät [täältä](https://checkmate.so/docs).
## Esivaatimukset
- [Docker](https://www.docker.com/) asennettuna
- [Git](https://git-scm.com/) asennettuna
<a id="installation"></a>
## Asennus
Asennusohjeet löytyvät [Checkmaten dokumentaatioportaalista](https://checkmate.so/docs).
Vaihtoehtoisesti voit käyttää [Coolifya](https://coolify.io/), [Elestiota](https://elest.io/open-source/checkmate), [K8s:ää](./charts/helm/checkmate/INSTALLATION.md), [Sive Hostia](https://sive.host) (Etelä-Afrikka), [Cloudzya](https://cloudzy.com/marketplace/checkmate) tai [Pikapodsia](https://www.pikapods.com/) Checkmate-instanssin nopeaan käynnistämiseen. Jos haluat valvoa palvelininfrastruktuuriasi, tarvitset [Capture-agentin](https://github.com/bluewave-labs/capture). Capturen repositorio sisältää myös sen asennusohjeet.
### Mukautetun CA:n käyttö
Jos haluat valvoa sisäisiä HTTPS-päätepisteitä yksityisten varmenneauktoriteettien (kuten Smallstepin) varmenteilla, lue [mukautetun CA:n luottamusopas](../custom-ca-trust.md) Dockerin konfigurointivaihtoehdoista.
Lisää dokumentaatiota löydät [docs-hakemistosta](../).
<a id="performance"></a>
## Suorituskyky
Laajojen optimointien ansiosta Checkmate toimii poikkeuksellisen pienellä muistinkulutuksella ja vaatii minimaalisesti muistia ja CPU:ta. Tässä Node.js-instanssin muistinkulutus palvelimella, joka valvoo 323 palvelinta minuutin välein:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
Ja tässä MongoDB:n ja Redisin muistijälki samalla palvelimella (398 Mt ja 15 Mt) samalle palvelinmäärälle:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Kysymykset & ideat
Jos sinulla on kysymyksiä, ehdotuksia tai kommentteja, vaihtoehtoja on useita:
- [Discord-kanava](https://discord.gg/NAb6H3UTjK) (suositeltu)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (katsomme aika ajoin)
Kysy rohkeasti tai jaa ideoitasi — kuulemme sinusta mielellämme!
<a id="features"></a>
## Ominaisuudet
- Täysin avoimen lähdekoodin, asennettavissa omille palvelimille tai kotilaitteille (esim. Raspberry Pi 4 tai 5)
- Useita valvontavaihtoehtoja: saatavuus, Docker, ping, SSL, portti, pelipalvelin
- Sivunopeuden valvonta
- Infrastruktuurin valvonta (muisti, levyn käyttö, CPU:n suorituskyky, verkko jne.) — vaatii [Capture](https://github.com/bluewave-labs/capture)-agentin
- Valikoitu levyjen valvonta liitospisteen valinnalla
- Häiriöt yhdellä silmäyksellä
- Tilasivut neljällä tyylikkäällä teemalla
- Ilmoitukset sähköpostitse, webhookeilla, Discordilla, Slackilla, PagerDutylla, Matrixilla, Microsoft Teamsilla, Telegramilla, Pushoverilla ja Twiliolla (SMS)
- Ajastettu huolto
- JSON-kyselyihin perustuva valvonta
- Monikielinen tuki: arabia, kiina (yksinkertaistettu), kiina (perinteinen, Taiwan), tšekki, englanti, suomi, ranska, saksa, japani, portugali (Brasilia), venäjä, espanja, thai, turkki, ukraina ja vietnam
## Monitorin elinkaari
1. Monitori suorittaa tarkistuksen (HTTP / ping / portti / laitteisto Capture-agentin kautta)
2. Tulos tallennetaan (onnistui/epäonnistui + vasteaika)
3. Viimeaikaisia tuloksia arvioidaan monitorin asetettua tilamuutoskynnystä vasten
4. Jos kynnys täyttyy ja nykyinen tila eroaa edellisestä, monitorin tila muuttuu (esim. `initializing`, `up`, `down`, `breached`)
5. Tilamuutoksen yhteydessä häiriö joko luodaan tai ratkaistaan monitorin nykyisen tilan mukaan
6. Ilmoitukset laukaistaan asetusten mukaisesti
<a id="screenshots"></a>
## Kuvakaappaukset
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Teknologiat
- [ReactJs](https://react.dev/)
- [MUI (React-kehys)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- Ja monia muita avoimen lähdekoodin komponentteja!
<a id="a-few-links"></a>
## Muutamia linkkejä
- Jos haluat tukea meitä, harkitse ⭐ antamista ja "watch"-napin painamista.
- Onko kysymyksiä tai ehdotuksia tiekartasta/ominaisuuksista? Tule [Discord-kanavalle](https://discord.gg/NAb6H3UTjK) tai [Discussions-foorumille](https://github.com/bluewave-labs/checkmate/discussions).
- Haluatko ilmoituksen uusista julkaisuista? Käytä [Newreleases](https://newreleases.io/) -palvelua, joka on ilmainen julkaisujen seuraamiseen.
- Katso Checkmaten [asennus- ja käyttövideo](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## Osallistuminen
Olemme [Alex](http://github.com/ajhollid) (tiiminvetäjä), [Gorkem](http://github.com/gorkem-bwl/), [Aryaman](https://github.com/Br0wnHammer), [Mert](https://github.com/mertssmnoglu) ja [Karen](https://github.com/karenvicent) — autamme yksityishenkilöitä ja yrityksiä valvomaan infrastruktuuriaan ja palvelimiaan.
Olemme ylpeitä siitä, että rakennamme vahvoja suhteita osallistujiin kaikilla tasoilla. Vaikka projekti on nuori, Checkmate on jo saanut yli 7000 tähteä ja yli 90 osallistujaa eri puolilta maailmaa.
Repoamme tähdittävät työntekijät yhtiöistä **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes ja NEC** — älä siis epäröi, hyppää mukaan, osallistu ja opi kanssamme!
Näin voit osallistua:
0. Tähditä tämä repo :)
1. Tutustu [osallistujan oppaaseen](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). Aloittelijoille suositellaan `good-first-issue`-tagia.
2. Lue tarkka [Checkmate-arkkitehtuurikuvaus](https://deepwiki.com/bluewave-labs/Checkmate), jos haluat syventyä rakenteeseen.
3. Avaa issue, jos uskot löytäneesi virheen.
4. Etsi `good-first-issue`-merkintöjä, jos olet uusi.
5. Tee pull request lisätäksesi ominaisuuksia, parannuksia tai virheenkorjauksia.
6. Tutustu `Checkmate`-koodikannan interaktiiviseen läpikäyntiin CodeCanvasissa [täällä](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true). Olemassa olevia datavirtasimulaatioita voi viimeistellä tai uusia luoda seuraamalla pikatutorialia [täällä](https://docs.code-canvas.com/updating-diagram).
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+197
View File
@@ -0,0 +1,197 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Une application open source de surveillance de la disponibilité et de l'infrastructure</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
Ce dépôt contient à la fois le frontend et le backend de Checkmate, un outil de surveillance open source et auto-hébergeable permettant de suivre le matériel des serveurs, la disponibilité, les temps de réponse et les incidents en temps réel grâce à de belles visualisations. Checkmate vérifie régulièrement la disponibilité et le bon fonctionnement de vos serveurs ou sites web, et fournit des alertes et rapports en temps réel sur la disponibilité, les pannes et les temps de réponse des services surveillés.
Checkmate dispose aussi d'un agent, appelé [Capture](https://github.com/bluewave-labs/capture), pour récupérer des données depuis des serveurs distants. Capture n'est pas requis pour faire tourner Checkmate, mais il apporte des informations supplémentaires sur l'état du CPU, de la RAM, du disque et de la température de vos serveurs. Capture fonctionne sur Linux, Windows, Mac, Raspberry Pi ou tout appareil capable d'exécuter Go.
Checkmate a été testé en charge avec plus de 1 000 moniteurs actifs sans aucun problème ni goulet d'étranglement notable.
## 📚 Table des matières
- [📦 Démo](#demo)
- [🔗 Guide utilisateur](#users-guide)
- [🛠️ Installation](#installation)
- [🚀 Performance](#performance)
- [💚 Questions & idées](#questions--ideas)
- [🧩 Fonctionnalités](#features)
- [🏗️ Captures d'écran](#screenshots)
- [🏗️ Pile technique](#tech-stack)
- [🔗 Quelques liens](#a-few-links)
- [🤝 Contribuer](#contributing)
<a id="demo"></a>
## Démo
Vous pouvez voir la dernière version de [Checkmate](https://demo.checkmate.so/) en action.
L'identifiant est demouser@demo.com et le mot de passe Demouser1! (Note : nous mettons à jour le serveur de démo de temps en temps ; si cela ne fonctionne pas, faites-nous signe sur le canal Discussions).
<a id="users-guide"></a>
## Guide utilisateur
Les instructions d'utilisation sont disponibles [ici](https://checkmate.so/docs).
## Prérequis
- [Docker](https://www.docker.com/) installé
- [Git](https://git-scm.com/) installé
<a id="installation"></a>
## Installation
Consultez les instructions d'installation dans le [portail de documentation Checkmate](https://checkmate.so/docs).
Vous pouvez également utiliser [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (Afrique du Sud), [Cloudzy](https://cloudzy.com/marketplace/checkmate) ou [Pikapods](https://www.pikapods.com/) pour démarrer rapidement une instance Checkmate. Pour surveiller votre infrastructure serveur, vous aurez besoin de l'[agent Capture](https://github.com/bluewave-labs/capture). Le dépôt Capture contient également les instructions d'installation.
### Utiliser une autorité de certification personnalisée
Si vous devez surveiller des points d'accès HTTPS internes avec des certificats issus d'autorités de certification privées (comme Smallstep), consultez notre [guide de confiance d'autorité de certification personnalisée](../custom-ca-trust.md) pour les options de configuration Docker.
Pour plus de documentation, consultez le [répertoire docs](../).
<a id="performance"></a>
## Performance
Grâce à de nombreuses optimisations, Checkmate fonctionne avec une empreinte mémoire exceptionnellement faible, nécessitant un minimum de RAM et de CPU. Voici l'utilisation mémoire d'une instance Node.js sur un serveur qui surveille 323 serveurs chaque minute :
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
Voici l'empreinte mémoire de MongoDB et Redis sur le même serveur (398 Mo et 15 Mo) pour le même nombre de serveurs :
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Questions & idées
Si vous avez des questions, suggestions ou commentaires, plusieurs options s'offrent à vous :
- [Canal Discord](https://discord.gg/NAb6H3UTjK) (préféré)
- [Discussions GitHub](https://github.com/bluewave-labs/Checkmate/discussions) (nous y passons de temps en temps)
N'hésitez pas à poser vos questions ou partager vos idées — nous adorerions vous entendre !
<a id="features"></a>
## Fonctionnalités
- Entièrement open source, déployable sur vos serveurs ou appareils personnels (par ex. Raspberry Pi 4 ou 5)
- Plusieurs options de surveillance : disponibilité, Docker, ping, SSL, port, serveur de jeu
- Surveillance de la vitesse des pages
- Surveillance de l'infrastructure (mémoire, utilisation du disque, performance CPU, réseau, etc.) — nécessite l'agent [Capture](https://github.com/bluewave-labs/capture)
- Surveillance sélective du disque avec choix de points de montage
- Incidents en un coup d'œil
- Pages de statut avec 4 thèmes élégants
- Notifications par e-mail, webhooks, Discord, Slack, PagerDuty, Matrix, Microsoft Teams, Telegram, Pushover, Twilio (SMS)
- Maintenance planifiée
- Surveillance par requête JSON
- Support multilingue : arabe, chinois (simplifié), chinois (traditionnel, Taïwan), tchèque, anglais, finnois, français, allemand, japonais, portugais (Brésil), russe, espagnol, thaï, turc, ukrainien et vietnamien
## Cycle de vie d'un moniteur
1. Un moniteur exécute une vérification (HTTP / ping / port / matériel via l'agent Capture)
2. Le résultat est stocké (succès/échec + temps de réponse)
3. Les résultats récents sont évalués par rapport au seuil de changement de statut configuré pour le moniteur
4. Si le seuil est atteint et que le statut actuel diffère du précédent, l'état du moniteur change (ex. `initializing`, `up`, `down`, `breached`)
5. Lors d'un changement d'état, un incident est créé ou résolu selon le statut actuel
6. Des notifications sont déclenchées selon la configuration
<a id="screenshots"></a>
## Captures d'écran
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Pile technique
- [ReactJs](https://react.dev/)
- [MUI (framework React)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- Beaucoup d'autres composants open source !
<a id="a-few-links"></a>
## Quelques liens
- Si vous souhaitez nous soutenir, merci de mettre une ⭐ et de cliquer sur « watch ».
- Une question ou une suggestion pour la roadmap ou les fonctionnalités ? Rejoignez notre [canal Discord](https://discord.gg/NAb6H3UTjK) ou les [Discussions](https://github.com/bluewave-labs/checkmate/discussions).
- Besoin d'être notifié à chaque nouvelle version ? Utilisez [Newreleases](https://newreleases.io/), un service gratuit de suivi des sorties.
- Regardez une [vidéo d'installation et d'utilisation de Checkmate](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## Contribuer
Nous sommes [Alex](http://github.com/ajhollid) (chef d'équipe), [Gorkem](http://github.com/gorkem-bwl/), [Aryaman](https://github.com/Br0wnHammer), [Mert](https://github.com/mertssmnoglu) et [Karen](https://github.com/karenvicent), et nous aidons les particuliers et les entreprises à surveiller leur infrastructure et leurs serveurs.
Nous sommes fiers de construire des liens solides avec les contributeurs à tous les niveaux. Bien que jeune, Checkmate a déjà obtenu plus de 7 000 étoiles et attiré plus de 90 contributeurs du monde entier.
Notre dépôt a été suivi par des employés de **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes et NEC** — alors n'hésitez pas, rejoignez-nous, contribuez et apprenez avec nous !
Voici comment contribuer :
0. Mettez une étoile à ce dépôt :)
1. Consultez le [guide du contributeur](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). Les nouveaux contributeurs sont encouragés à consulter le tag `good-first-issue`.
2. Lisez une structure détaillée de [Checkmate](https://deepwiki.com/bluewave-labs/Checkmate) si vous souhaitez plonger dans l'architecture.
3. Ouvrez une issue si vous pensez avoir rencontré un bug.
4. Cherchez les `good-first-issue` si vous débutez.
5. Faites une pull request pour ajouter de nouvelles fonctionnalités, des améliorations ou corriger des bugs.
6. Découvrez ce parcours interactif de la base de code `Checkmate` sur CodeCanvas [ici](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true). Pour affiner les simulations de flux de données existantes ou en créer de nouvelles, suivez le tutoriel rapide [ici](https://docs.code-canvas.com/updating-diagram).
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+197
View File
@@ -0,0 +1,197 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>オープンソースの稼働状況とインフラ監視アプリケーション</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
このリポジトリには、Checkmate のフロントエンドとバックエンドの両方が含まれています。Checkmate は、サーバーのハードウェア、稼働状況、応答時間、インシデントを美しい可視化でリアルタイムに追跡する、オープンソースかつセルフホスト型の監視ツールです。Checkmate はサーバーや Web サイトにアクセスできるか、最適に動作しているかを定期的にチェックし、監視対象サービスの可用性・ダウンタイム・応答時間に関するリアルタイムのアラートとレポートを提供します。
Checkmate には、リモートサーバーからデータを取得するためのエージェントとして [Capture](https://github.com/bluewave-labs/capture) もあります。Capture は Checkmate の動作に必須ではありませんが、サーバーの CPU、RAM、ディスク、温度の状態について追加の情報を提供します。Capture は Linux、Windows、Mac、Raspberry Pi、その他 Go を実行できるあらゆるデバイスで動作します。
Checkmate は 1000 以上のアクティブなモニターでストレステストされており、目立った問題や性能のボトルネックは発生していません。
## 📚 目次
- [📦 デモ](#demo)
- [🔗 ユーザーガイド](#users-guide)
- [🛠️ インストール](#installation)
- [🚀 パフォーマンス](#performance)
- [💚 質問とアイデア](#questions--ideas)
- [🧩 機能](#features)
- [🏗️ スクリーンショット](#screenshots)
- [🏗️ 技術スタック](#tech-stack)
- [🔗 関連リンク](#a-few-links)
- [🤝 コントリビューション](#contributing)
<a id="demo"></a>
## デモ
[Checkmate](https://demo.checkmate.so/) の最新ビルドを実際に確認できます。
ユーザー名は demouser@demo.com、パスワードは Demouser1! です(注: デモサーバーは時々更新されています。動作しない場合は Discussions チャンネルでお知らせください)。
<a id="users-guide"></a>
## ユーザーガイド
使用方法は [こちら](https://checkmate.so/docs) を参照してください。
## 前提条件
- [Docker](https://www.docker.com/) がインストールされていること
- [Git](https://git-scm.com/) がインストールされていること
<a id="installation"></a>
## インストール
インストール手順は [Checkmate ドキュメントポータル](https://checkmate.so/docs) を参照してください。
代わりに、[Coolify](https://coolify.io/)、[Elestio](https://elest.io/open-source/checkmate)、[K8s](./charts/helm/checkmate/INSTALLATION.md)、[Sive Host](https://sive.host)(南アフリカ)、[Cloudzy](https://cloudzy.com/marketplace/checkmate)、[Pikapods](https://www.pikapods.com/) を使って Checkmate インスタンスをすばやく立ち上げることもできます。サーバーインフラを監視したい場合は、[Capture エージェント](https://github.com/bluewave-labs/capture) が必要です。Capture リポジトリにもインストール手順が記載されています。
### カスタム CA の利用
プライベート認証局(Smallstep など)が発行した証明書で内部 HTTPS エンドポイントを監視する必要がある場合は、Docker 設定のオプションを記載した [カスタム CA 信頼ガイド](../custom-ca-trust.md) をご覧ください。
その他のドキュメントは [docs ディレクトリ](../) を参照してください。
<a id="performance"></a>
## パフォーマンス
広範な最適化により、Checkmate は非常に小さなメモリフットプリントで動作し、必要なメモリと CPU リソースは最小限です。以下は、毎分 323 台のサーバーを監視するサーバー上の Node.js インスタンスのメモリ使用量です:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
同じサーバー上の MongoDB と Redis のメモリフットプリント(それぞれ 398MB と 15MB、同じサーバー数を監視)も以下の通りです:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## 質問とアイデア
質問・提案・コメントがあれば、いくつかの選択肢があります:
- [Discord チャンネル](https://discord.gg/NAb6H3UTjK)(推奨)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions)(時々確認しています)
質問やアイデアの共有はお気軽にどうぞ — お声をお聞かせいただけると嬉しいです!
<a id="features"></a>
## 機能
- 完全にオープンソース、自身のサーバーや家庭用デバイス(例: Raspberry Pi 4 / 5)にデプロイ可能
- 複数の監視オプション: 稼働状況、Docker、Ping、SSL、ポート、ゲームサーバー
- ページスピード監視
- インフラ監視(メモリ、ディスク使用量、CPU 性能、ネットワークなど) — [Capture](https://github.com/bluewave-labs/capture) エージェントが必要
- マウントポイントを選択した選択的なディスク監視
- インシデントを一目で確認
- 4 種類の美しいテーマを備えたステータスページ
- メール、Webhook、Discord、Slack、PagerDuty、Matrix、Microsoft Teams、Telegram、Pushover、Twilio (SMS) による通知
- メンテナンスのスケジューリング
- JSON クエリ監視
- 多言語対応: アラビア語、中国語(簡体字)、中国語(繁体字・台湾)、チェコ語、英語、フィンランド語、フランス語、ドイツ語、日本語、ポルトガル語(ブラジル)、ロシア語、スペイン語、タイ語、トルコ語、ウクライナ語、ベトナム語
## モニターのライフサイクル
1. モニターがチェックを実行する(HTTP / Ping / ポート / Capture エージェント経由のハードウェア)
2. 結果を保存する(成功・失敗 + 応答時間)
3. 直近の結果を、モニターに設定されたステータス変更のしきい値で評価する
4. しきい値を満たし、現在のステータスが直前と異なる場合、モニターの状態が変わる(例: `initializing`、`up`、`down`、`breached`)
5. 状態変化時に、現在のステータスに応じてインシデントを作成または解決する
6. 設定に基づいて通知をトリガーする
<a id="screenshots"></a>
## スクリーンショット
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## 技術スタック
- [ReactJs](https://react.dev/)
- [MUI (React フレームワーク)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- その他多数のオープンソースコンポーネント!
<a id="a-few-links"></a>
## 関連リンク
- 応援いただける場合は、ぜひ ⭐ を付け、「watch」もクリックしてください。
- ロードマップや機能セットへの質問・提案がありますか? [Discord チャンネル](https://discord.gg/NAb6H3UTjK) または [Discussions](https://github.com/bluewave-labs/checkmate/discussions) フォーラムをご覧ください。
- 新しいリリースの通知が欲しいですか? リリース追跡用の無料サービス [Newreleases](https://newreleases.io/) をご利用ください。
- Checkmate の [インストールおよび利用方法の動画](https://www.youtube.com/watch?v=GfFOc0xHIwY) もご覧ください。
<a id="contributing"></a>
## コントリビューション
私たちは [Alex](http://github.com/ajhollid)(チームリード)、[Gorkem](http://github.com/gorkem-bwl/)、[Aryaman](https://github.com/Br0wnHammer)、[Mert](https://github.com/mertssmnoglu)、[Karen](https://github.com/karenvicent) で、個人や企業がインフラとサーバーを監視するお手伝いをしています。
あらゆるレベルのコントリビューターとの強いつながりを築くことを誇りにしています。若いプロジェクトでありながら、Checkmate はすでに 7000 を超えるスターと、世界中から 90 名以上のコントリビューターを集めています。
このリポジトリには **Google、Microsoft、Intel、Cisco、Tencent、Electronic Arts、ByteDance、JP Morgan Chase、Deloitte、Accenture、Foxconn、Broadcom、China Telecom、Barclays、Capgemini、Wipro、Cloudflare、Dassault Systèmes、NEC** の社員からスターが付いています — ぜひ気軽に参加し、貢献し、私たちと一緒に学びましょう!
コントリビュートの方法:
0. このリポジトリにスターを付ける :)
1. [コントリビューションガイド](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md) を読む。初参加の方には `good-first-issue` タグの確認をおすすめします。
2. アーキテクチャを深く知りたい場合は、[Checkmate の詳細構成](https://deepwiki.com/bluewave-labs/Checkmate) を読む。
3. バグを発見したと思ったら issue を開く。
4. はじめての方は `good-first-issue` を探す。
5. 新機能、品質改善、バグ修正のために pull request を作成する。
6. CodeCanvas 上の `Checkmate` コードベースのインタラクティブなウォークスルー([こちら](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true))を確認する。既存のデータフロー・シミュレーションを改善したり、新しいものを作成したりするには、[こちら](https://docs.code-canvas.com/updating-diagram) のクイックチュートリアルに従ってください。
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+197
View File
@@ -0,0 +1,197 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Uma aplicação de código aberto para monitoramento de disponibilidade e infraestrutura</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
Este repositório contém o frontend e o backend do Checkmate, uma ferramenta de monitoramento de código aberto e auto-hospedável para acompanhar hardware de servidores, disponibilidade, tempos de resposta e incidentes em tempo real com visualizações bonitas. O Checkmate verifica regularmente se um servidor ou site está acessível e funcionando de forma ótima, oferecendo alertas e relatórios em tempo real sobre disponibilidade, indisponibilidade e tempos de resposta dos serviços monitorados.
O Checkmate também possui um agente chamado [Capture](https://github.com/bluewave-labs/capture) para coletar dados de servidores remotos. O Capture não é necessário para rodar o Checkmate, mas fornece informações adicionais sobre o status de CPU, RAM, disco e temperatura dos servidores. O Capture pode rodar em Linux, Windows, Mac, Raspberry Pi ou qualquer dispositivo capaz de executar Go.
O Checkmate passou por testes de estresse com mais de 1000 monitores ativos sem nenhum problema ou gargalo de desempenho notável.
## 📚 Sumário
- [📦 Demo](#demo)
- [🔗 Guia do usuário](#users-guide)
- [🛠️ Instalação](#installation)
- [🚀 Desempenho](#performance)
- [💚 Perguntas & ideias](#questions--ideas)
- [🧩 Funcionalidades](#features)
- [🏗️ Capturas de tela](#screenshots)
- [🏗️ Stack tecnológica](#tech-stack)
- [🔗 Alguns links](#a-few-links)
- [🤝 Contribuindo](#contributing)
<a id="demo"></a>
## Demo
Você pode ver a versão mais recente do [Checkmate](https://demo.checkmate.so/) em ação.
O usuário é demouser@demo.com e a senha é Demouser1! (Observação: atualizamos o servidor de demonstração de tempos em tempos; se não funcionar, fale conosco no canal Discussions).
<a id="users-guide"></a>
## Guia do usuário
As instruções de uso podem ser encontradas [aqui](https://checkmate.so/docs).
## Pré-requisitos
- [Docker](https://www.docker.com/) instalado
- [Git](https://git-scm.com/) instalado
<a id="installation"></a>
## Instalação
Veja as instruções de instalação no [portal de documentação do Checkmate](https://checkmate.so/docs).
Alternativamente, você pode usar [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (África do Sul), [Cloudzy](https://cloudzy.com/marketplace/checkmate) ou [Pikapods](https://www.pikapods.com/) para subir rapidamente uma instância do Checkmate. Se quiser monitorar sua infraestrutura de servidores, você precisará do [agente Capture](https://github.com/bluewave-labs/capture). O repositório do Capture também contém as instruções de instalação.
### Usando uma CA personalizada
Se você precisa monitorar endpoints HTTPS internos com certificados emitidos por autoridades certificadoras privadas (como Smallstep), veja nosso [Guia de confiança em CA personalizada](../custom-ca-trust.md) para opções de configuração do Docker.
Para mais documentação, veja o [diretório docs](../).
<a id="performance"></a>
## Desempenho
Graças a otimizações extensas, o Checkmate opera com uma pegada de memória excepcionalmente pequena, exigindo memória e CPU mínimos. Veja o uso de memória de uma instância Node.js em um servidor monitorando 323 servidores a cada minuto:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
Você pode ver a pegada de memória do MongoDB e Redis no mesmo servidor (398 MB e 15 MB) para a mesma quantidade de servidores:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Perguntas & ideias
Se você tem perguntas, sugestões ou comentários, há várias opções:
- [Canal do Discord](https://discord.gg/NAb6H3UTjK) (preferido)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (passamos por aqui de vez em quando)
Sinta-se à vontade para perguntar ou compartilhar suas ideias — adoramos ouvir você!
<a id="features"></a>
## Funcionalidades
- Totalmente código aberto, implantável em seus servidores ou dispositivos domésticos (ex.: Raspberry Pi 4 ou 5)
- Várias opções de monitoramento: disponibilidade, Docker, ping, SSL, porta, servidor de jogos
- Monitoramento de velocidade de página
- Monitoramento de infraestrutura (memória, uso de disco, desempenho de CPU, rede, etc.) — requer o agente [Capture](https://github.com/bluewave-labs/capture)
- Monitoramento seletivo de disco com escolha de pontos de montagem
- Incidentes em um relance
- Páginas de status com 4 temas elegantes
- Notificações por e-mail, webhooks, Discord, Slack, PagerDuty, Matrix, Microsoft Teams, Telegram, Pushover, Twilio (SMS)
- Manutenção agendada
- Monitoramento por consulta JSON
- Suporte a múltiplos idiomas: árabe, chinês (simplificado), chinês (tradicional, Taiwan), tcheco, inglês, finlandês, francês, alemão, japonês, português (Brasil), russo, espanhol, tailandês, turco, ucraniano e vietnamita
## Ciclo de vida do monitor
1. Um monitor executa uma verificação (HTTP / ping / porta / hardware via agente Capture)
2. O resultado é armazenado (sucesso/falha + tempo de resposta)
3. Os resultados recentes são avaliados em relação ao limite de mudança de status configurado
4. Se o limite for atingido e o status atual diferir do anterior, o estado do monitor muda (ex.: `initializing`, `up`, `down`, `breached`)
5. Em uma mudança de estado, um incidente é criado ou resolvido conforme o status atual do monitor
6. As notificações são disparadas com base na configuração
<a id="screenshots"></a>
## Capturas de tela
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Stack tecnológica
- [ReactJs](https://react.dev/)
- [MUI (framework React)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- E muitos outros componentes de código aberto!
<a id="a-few-links"></a>
## Alguns links
- Se quiser nos apoiar, considere dar uma ⭐ e clicar em "watch".
- Tem alguma pergunta ou sugestão sobre o roadmap/funcionalidades? Confira nosso [canal do Discord](https://discord.gg/NAb6H3UTjK) ou o fórum [Discussions](https://github.com/bluewave-labs/checkmate/discussions).
- Quer receber um aviso a cada novo lançamento? Use o [Newreleases](https://newreleases.io/), um serviço gratuito de acompanhamento de releases.
- Assista a um [vídeo de instalação e uso do Checkmate](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## Contribuindo
Somos o [Alex](http://github.com/ajhollid) (líder de equipe), o [Gorkem](http://github.com/gorkem-bwl/), o [Aryaman](https://github.com/Br0wnHammer), o [Mert](https://github.com/mertssmnoglu) e a [Karen](https://github.com/karenvicent) — ajudamos indivíduos e empresas a monitorar suas infraestruturas e servidores.
Nos orgulhamos de construir conexões fortes com contribuidores de todos os níveis. Apesar de ser um projeto jovem, o Checkmate já conquistou mais de 7000 estrelas e atraiu mais de 90 contribuidores no mundo todo.
Nosso repositório recebeu estrelas de funcionários do **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes e NEC** — então não se segure: entre, contribua e aprenda com a gente!
Como contribuir:
0. Dê uma estrela neste repositório :)
1. Confira o [guia do contribuidor](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). Iniciantes são incentivados a verificar a tag `good-first-issue`.
2. Leia uma estrutura detalhada do [Checkmate](https://deepwiki.com/bluewave-labs/Checkmate) se quiser mergulhar na arquitetura.
3. Abra uma issue se acreditar ter encontrado um bug.
4. Procure por `good-first-issue`s se você é novo.
5. Faça um pull request para adicionar novas funcionalidades, melhorias ou correções de bugs.
6. Confira este passo a passo interativo da base de código do `Checkmate` no CodeCanvas [aqui](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true). Para refinar simulações existentes de fluxo de dados ou criar novas, siga o tutorial rápido [aqui](https://docs.code-canvas.com/updating-diagram).
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+197
View File
@@ -0,0 +1,197 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Open source приложение для мониторинга доступности и инфраструктуры</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
В этом репозитории находятся как frontend, так и backend Checkmate — open-source-инструмента самостоятельного хостинга для отслеживания оборудования серверов, времени работы, времени отклика и инцидентов в реальном времени с наглядными визуализациями. Checkmate регулярно проверяет, доступен ли сервер/сайт и работает ли он оптимально, предоставляя оповещения и отчёты о доступности, простое и времени отклика отслеживаемых сервисов в реальном времени.
У Checkmate также есть агент под названием [Capture](https://github.com/bluewave-labs/capture) для получения данных с удалённых серверов. Capture не является обязательным для работы Checkmate, но даёт дополнительную информацию о состоянии CPU, RAM, диска и температуры ваших серверов. Capture работает на Linux, Windows, Mac, Raspberry Pi и любом устройстве, способном запускать Go.
Checkmate прошёл нагрузочное тестирование с 1000+ активных мониторов без каких-либо заметных проблем или просадок производительности.
## 📚 Содержание
- [📦 Демо](#demo)
- [🔗 Руководство пользователя](#users-guide)
- [🛠️ Установка](#installation)
- [🚀 Производительность](#performance)
- [💚 Вопросы и идеи](#questions--ideas)
- [🧩 Возможности](#features)
- [🏗️ Скриншоты](#screenshots)
- [🏗️ Технологии](#tech-stack)
- [🔗 Несколько ссылок](#a-few-links)
- [🤝 Участие](#contributing)
<a id="demo"></a>
## Демо
Последнюю сборку [Checkmate](https://demo.checkmate.so/) можно увидеть в действии.
Имя пользователя — demouser@demo.com, пароль — Demouser1! (Обратите внимание: мы периодически обновляем демо-сервер; если что-то не работает, напишите нам на канале Discussions).
<a id="users-guide"></a>
## Руководство пользователя
Инструкции по использованию доступны [здесь](https://checkmate.so/docs).
## Требования
- Установленный [Docker](https://www.docker.com/)
- Установленный [Git](https://git-scm.com/)
<a id="installation"></a>
## Установка
Инструкции по установке смотрите на [портале документации Checkmate](https://checkmate.so/docs).
В качестве альтернативы вы можете использовать [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (ЮАР), [Cloudzy](https://cloudzy.com/marketplace/checkmate) или [Pikapods](https://www.pikapods.com/), чтобы быстро поднять инстанс Checkmate. Если вы хотите мониторить серверную инфраструктуру, потребуется агент [Capture](https://github.com/bluewave-labs/capture). В репозитории Capture также есть инструкции по установке.
### Использование собственного УЦ
Если нужно мониторить внутренние HTTPS-эндпоинты с сертификатами от приватных УЦ (например, Smallstep), смотрите наше [руководство по доверию собственным УЦ](../custom-ca-trust.md) с параметрами конфигурации Docker.
Дополнительная документация — в [каталоге docs](../).
<a id="performance"></a>
## Производительность
Благодаря многочисленным оптимизациям Checkmate работает с исключительно низким потреблением памяти и требует минимума RAM и CPU. Ниже — потребление памяти инстансом Node.js на сервере, который мониторит 323 сервера каждую минуту:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
А вот объём памяти MongoDB и Redis на том же сервере (398 МБ и 15 МБ) для того же количества серверов:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Вопросы и идеи
Если у вас есть вопросы, предложения или комментарии — есть несколько вариантов:
- [Канал в Discord](https://discord.gg/NAb6H3UTjK) (предпочтительно)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (заглядываем туда время от времени)
Смело задавайте вопросы и делитесь идеями — мы будем рады услышать вас!
<a id="features"></a>
## Возможности
- Полностью open source, можно развернуть на собственных серверах или домашних устройствах (например, Raspberry Pi 4 или 5)
- Несколько вариантов мониторинга: доступность, Docker, ping, SSL, порт, игровой сервер
- Мониторинг скорости загрузки страниц
- Мониторинг инфраструктуры (память, использование диска, производительность CPU, сеть и т. д.) — требуется агент [Capture](https://github.com/bluewave-labs/capture)
- Выборочный мониторинг дисков с выбором точек монтирования
- Инциденты в одном месте
- Страницы статуса с 4 красивыми темами
- Уведомления по e-mail, через вебхуки, Discord, Slack, PagerDuty, Matrix, Microsoft Teams, Telegram, Pushover, Twilio (SMS)
- Запланированное обслуживание
- Мониторинг JSON-запросов
- Многоязычная поддержка: арабский, китайский (упрощённый), китайский (традиционный, Тайвань), чешский, английский, финский, французский, немецкий, японский, португальский (Бразилия), русский, испанский, тайский, турецкий, украинский и вьетнамский
## Жизненный цикл монитора
1. Монитор выполняет проверку (HTTP / ping / порт / оборудование через агента Capture)
2. Результат сохраняется (успех/неудача + время отклика)
3. Недавние результаты проверяются по заданному порогу смены статуса
4. Если порог достигнут, а текущий статус отличается от предыдущего — состояние монитора изменяется (например, `initializing`, `up`, `down`, `breached`)
5. При смене состояния инцидент создаётся или закрывается в зависимости от текущего статуса
6. Уведомления отправляются в соответствии с настройками
<a id="screenshots"></a>
## Скриншоты
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Технологии
- [ReactJs](https://react.dev/)
- [MUI (React-фреймворк)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- И множество других open-source компонентов!
<a id="a-few-links"></a>
## Несколько ссылок
- Если хотите поддержать нас — поставьте ⭐ и нажмите «watch».
- Есть вопрос или предложение по дорожной карте/функциям? Загляните в наш [канал Discord](https://discord.gg/NAb6H3UTjK) или форум [Discussions](https://github.com/bluewave-labs/checkmate/discussions).
- Нужно уведомление о новых релизах? Используйте [Newreleases](https://newreleases.io/) — бесплатный сервис отслеживания релизов.
- Посмотрите [видео по установке и использованию Checkmate](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## Участие
Мы — [Alex](http://github.com/ajhollid) (тимлид), [Gorkem](http://github.com/gorkem-bwl/), [Aryaman](https://github.com/Br0wnHammer), [Mert](https://github.com/mertssmnoglu) и [Karen](https://github.com/karenvicent) — помогаем частным лицам и компаниям мониторить их инфраструктуру и серверы.
Мы гордимся тем, что выстраиваем крепкие связи с контрибьюторами на всех уровнях. Несмотря на то что проект молодой, Checkmate уже получил более 7000 звёзд и более 90 контрибьюторов со всего мира.
Наш репозиторий помечен сотрудниками **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes и NEC** — так что не стесняйтесь, присоединяйтесь, вносите вклад и учитесь вместе с нами!
Как внести вклад:
0. Поставьте звезду этому репозиторию :)
1. Прочтите [руководство контрибьютора](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). Новичкам советуем посмотреть метку `good-first-issue`.
2. Если хотите глубже погрузиться в архитектуру — прочтите подробное описание [Checkmate](https://deepwiki.com/bluewave-labs/Checkmate).
3. Откройте issue, если столкнулись с багом.
4. Если вы новичок — ищите `good-first-issue`.
5. Откройте pull request для добавления функций, улучшений или исправлений.
6. Посмотрите интерактивный обзор кодовой базы `Checkmate` в CodeCanvas [здесь](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true). Чтобы доработать имеющиеся симуляции потоков данных или создать новые, следуйте краткому туториалу [здесь](https://docs.code-canvas.com/updating-diagram).
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+200
View File
@@ -0,0 +1,200 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
> ⚠️ คำแปลนี้เป็นฉบับร่างที่สร้างโดยเครื่อง ยินดีรับการตรวจทานจากเจ้าของภาษาผ่าน pull request
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>แอปพลิเคชันโอเพนซอร์สสำหรับตรวจสอบสถานะการทำงานและโครงสร้างพื้นฐาน</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
รีโพนี้ประกอบด้วยทั้งฟรอนต์เอนด์และแบ็กเอนด์ของ Checkmate ซึ่งเป็นเครื่องมือมอนิเตอร์โอเพนซอร์สและสามารถโฮสต์เองได้ ใช้ติดตามฮาร์ดแวร์ของเซิร์ฟเวอร์ เวลาการทำงาน เวลาในการตอบสนอง และเหตุการณ์ต่าง ๆ แบบเรียลไทม์พร้อมการแสดงผลที่สวยงาม Checkmate จะตรวจสอบเป็นระยะ ๆ ว่าเซิร์ฟเวอร์/เว็บไซต์ใช้งานได้และทำงานได้อย่างเหมาะสมหรือไม่ พร้อมส่งการแจ้งเตือนและรายงานแบบเรียลไทม์เกี่ยวกับความพร้อมใช้งาน ช่วงเวลาที่ระบบไม่พร้อมใช้งาน และเวลาในการตอบสนองของบริการที่ตรวจสอบ
Checkmate ยังมีเอเจนต์ชื่อ [Capture](https://github.com/bluewave-labs/capture) สำหรับดึงข้อมูลจากเซิร์ฟเวอร์ระยะไกล Capture ไม่จำเป็นต้องใช้เพื่อรัน Checkmate แต่จะให้ข้อมูลเพิ่มเติมเกี่ยวกับ CPU, RAM, ดิสก์ และอุณหภูมิของเซิร์ฟเวอร์ Capture ทำงานได้บน Linux, Windows, Mac, Raspberry Pi หรือทุกอุปกรณ์ที่รัน Go ได้
Checkmate ผ่านการทดสอบโหลดสำหรับมอนิเตอร์ที่ทำงานอยู่กว่า 1000 รายการโดยไม่พบปัญหาหรือคอขวดด้านประสิทธิภาพที่สำคัญ
## 📚 สารบัญ
- [📦 เดโม](#demo)
- [🔗 คู่มือผู้ใช้](#users-guide)
- [🛠️ การติดตั้ง](#installation)
- [🚀 ประสิทธิภาพ](#performance)
- [💚 คำถามและไอเดีย](#questions--ideas)
- [🧩 ฟีเจอร์](#features)
- [🏗️ ภาพหน้าจอ](#screenshots)
- [🏗️ Tech stack](#tech-stack)
- [🔗 ลิงก์เพิ่มเติม](#a-few-links)
- [🤝 การมีส่วนร่วม](#contributing)
<a id="demo"></a>
## เดโม
คุณสามารถดูบิลด์ล่าสุดของ [Checkmate](https://demo.checkmate.so/) ที่ใช้งานจริง
ชื่อผู้ใช้คือ demouser@demo.com และรหัสผ่านคือ Demouser1! (โปรดทราบว่าเราอัปเดตเซิร์ฟเวอร์เดโมเป็นระยะ ๆ หากไม่ทำงาน โปรดแจ้งเราที่ช่อง Discussions)
<a id="users-guide"></a>
## คู่มือผู้ใช้
วิธีใช้งานสามารถดูได้ [ที่นี่](https://checkmate.so/docs)
## ข้อกำหนดเบื้องต้น
- ติดตั้ง [Docker](https://www.docker.com/)
- ติดตั้ง [Git](https://git-scm.com/)
<a id="installation"></a>
## การติดตั้ง
ดูวิธีการติดตั้งใน [พอร์ทัลเอกสารของ Checkmate](https://checkmate.so/docs)
อีกทางเลือกหนึ่ง คุณสามารถใช้ [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (แอฟริกาใต้), [Cloudzy](https://cloudzy.com/marketplace/checkmate) หรือ [Pikapods](https://www.pikapods.com/) เพื่อเปิดอินสแตนซ์ Checkmate ได้อย่างรวดเร็ว หากต้องการมอนิเตอร์โครงสร้างพื้นฐานเซิร์ฟเวอร์ คุณจะต้องใช้ [เอเจนต์ Capture](https://github.com/bluewave-labs/capture) รีโพ Capture ยังมีวิธีการติดตั้งด้วย
### การใช้ Custom CA
หากคุณต้องการมอนิเตอร์ HTTPS เอนด์พอยต์ภายในที่ใช้ใบรับรองจาก CA ส่วนตัว (เช่น Smallstep) โปรดดู [คู่มือการเชื่อถือ CA แบบกำหนดเอง](../custom-ca-trust.md) สำหรับตัวเลือกการตั้งค่า Docker
สำหรับเอกสารเพิ่มเติม ดูที่ [โฟลเดอร์ docs](../)
<a id="performance"></a>
## ประสิทธิภาพ
ด้วยการปรับแต่งอย่างละเอียด Checkmate ใช้หน่วยความจำน้อยอย่างมาก และต้องการ RAM กับ CPU น้อยที่สุด ด้านล่างเป็นการใช้หน่วยความจำของอินสแตนซ์ Node.js บนเซิร์ฟเวอร์ที่มอนิเตอร์เซิร์ฟเวอร์ 323 เครื่องทุกนาที:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
คุณยังสามารถดูการใช้หน่วยความจำของ MongoDB และ Redis บนเซิร์ฟเวอร์เดียวกัน (398MB และ 15MB) สำหรับจำนวนเซิร์ฟเวอร์เท่าเดิม:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## คำถามและไอเดีย
หากมีคำถาม ข้อเสนอแนะ หรือความคิดเห็น คุณมีหลายทางเลือก:
- [ช่อง Discord](https://discord.gg/NAb6H3UTjK) (แนะนำ)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (เราเข้ามาดูเป็นระยะ)
อย่าลังเลที่จะถามหรือแชร์ไอเดีย — เรายินดีรับฟังเสมอ!
<a id="features"></a>
## ฟีเจอร์
- โอเพนซอร์สอย่างสมบูรณ์ สามารถดีพลอยบนเซิร์ฟเวอร์หรืออุปกรณ์ภายในบ้าน (เช่น Raspberry Pi 4 หรือ 5)
- ตัวเลือกการมอนิเตอร์หลายแบบ: สถานะการทำงาน, Docker, Ping, SSL, พอร์ต, เซิร์ฟเวอร์เกม
- มอนิเตอร์ความเร็วของหน้าเว็บ
- มอนิเตอร์โครงสร้างพื้นฐาน (หน่วยความจำ การใช้ดิสก์ ประสิทธิภาพ CPU เครือข่าย ฯลฯ) — ต้องใช้เอเจนต์ [Capture](https://github.com/bluewave-labs/capture)
- มอนิเตอร์ดิสก์แบบเลือกได้ด้วยการเลือก mount point
- ดูเหตุการณ์ได้ในที่เดียว
- หน้าสถานะที่มี 4 ธีมสวยงาม
- การแจ้งเตือนผ่านอีเมล Webhook, Discord, Slack, PagerDuty, Matrix, Microsoft Teams, Telegram, Pushover, Twilio (SMS)
- การบำรุงรักษาตามกำหนด
- มอนิเตอร์ด้วย JSON query
- รองรับหลายภาษา: อาหรับ, จีน (ตัวย่อ), จีน (ตัวเต็ม, ไต้หวัน), เช็ก, อังกฤษ, ฟินแลนด์, ฝรั่งเศส, เยอรมัน, ญี่ปุ่น, โปรตุเกส (บราซิล), รัสเซีย, สเปน, ไทย, ตุรกี, ยูเครน และเวียดนาม
## วงจรชีวิตของมอนิเตอร์
1. มอนิเตอร์ทำการตรวจสอบ (HTTP / Ping / พอร์ต / ฮาร์ดแวร์ผ่านเอเจนต์ Capture)
2. ผลลัพธ์ถูกบันทึก (สำเร็จ/ล้มเหลว + เวลาในการตอบสนอง)
3. ผลลัพธ์ล่าสุดจะถูกประเมินเทียบกับเกณฑ์การเปลี่ยนสถานะที่ตั้งค่าไว้
4. ถ้าเกณฑ์ถูกตอบสนอง และสถานะปัจจุบันไม่เท่ากับสถานะก่อนหน้า สถานะของมอนิเตอร์จะเปลี่ยน (เช่น `initializing`, `up`, `down`, `breached`)
5. เมื่อสถานะเปลี่ยน เหตุการณ์จะถูกสร้างหรือถูกแก้ไขตามสถานะปัจจุบัน
6. การแจ้งเตือนจะถูกทริกเกอร์ตามการตั้งค่า
<a id="screenshots"></a>
## ภาพหน้าจอ
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Tech stack
- [ReactJs](https://react.dev/)
- [MUI (React framework)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- และส่วนประกอบโอเพนซอร์สอื่นอีกมาก!
<a id="a-few-links"></a>
## ลิงก์เพิ่มเติม
- หากต้องการสนับสนุนเรา โปรดให้ ⭐ และคลิก "watch"
- มีคำถามหรือข้อเสนอแนะเกี่ยวกับโรดแมป/ฟีเจอร์? เยี่ยมชม [ช่อง Discord](https://discord.gg/NAb6H3UTjK) หรือฟอรัม [Discussions](https://github.com/bluewave-labs/checkmate/discussions) ของเรา
- ต้องการการแจ้งเตือนเมื่อมีรีลีสใหม่? ใช้ [Newreleases](https://newreleases.io/) บริการฟรีสำหรับติดตามการรีลีส
- ดู [วิดีโอการติดตั้งและใช้งาน Checkmate](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## การมีส่วนร่วม
พวกเราคือ [Alex](http://github.com/ajhollid) (หัวหน้าทีม), [Gorkem](http://github.com/gorkem-bwl/), [Aryaman](https://github.com/Br0wnHammer), [Mert](https://github.com/mertssmnoglu) และ [Karen](https://github.com/karenvicent) ที่ช่วยให้บุคคลและธุรกิจสามารถมอนิเตอร์โครงสร้างพื้นฐานและเซิร์ฟเวอร์ได้
เราภูมิใจกับการสร้างความสัมพันธ์ที่แข็งแกร่งกับผู้ร่วมพัฒนาในทุกระดับ แม้จะเป็นโครงการที่ยังใหม่ แต่ Checkmate ได้รับมากกว่า 7000 ดาว และมีผู้ร่วมพัฒนามากกว่า 90 คนจากทั่วโลก
รีโพของเราได้รับดาวจากพนักงานของ **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes และ NEC** — เพราะฉะนั้นอย่าลังเล ร่วมพัฒนา เรียนรู้ และเติบโตไปกับเรา!
วิธีที่คุณสามารถร่วมพัฒนา:
0. ให้ดาวกับรีโพนี้ :)
1. อ่าน [คู่มือผู้ร่วมพัฒนา](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md) ผู้เริ่มต้นแนะนำให้ดูแท็ก `good-first-issue`
2. อ่านโครงสร้างเชิงลึกของ [Checkmate](https://deepwiki.com/bluewave-labs/Checkmate) หากต้องการเจาะลึกสถาปัตยกรรม
3. เปิด issue หากคุณคิดว่าพบบั๊ก
4. มองหาประเด็นที่ติดแท็ก `good-first-issue` หากเป็นมือใหม่
5. ส่ง pull request เพื่อเพิ่มฟีเจอร์ ปรับปรุงการใช้งาน หรือแก้บั๊ก
6. ดู interactive walkthrough ของโค้ดเบส `Checkmate` บน CodeCanvas [ที่นี่](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true) เพื่อปรับปรุงการจำลอง dataflow ที่มีอยู่หรือสร้างใหม่ ทำตามบทเรียนสั้น [ที่นี่](https://docs.code-canvas.com/updating-diagram)
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+197
View File
@@ -0,0 +1,197 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Açık kaynaklı çalışma süresi ve altyapı izleme uygulaması</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
Bu depo, sunucu donanımını, çalışma süresini, yanıt sürelerini ve olayları zarif görselleştirmelerle gerçek zamanlı takip eden, açık kaynaklı ve kendi sunucunuzda barındırılan bir izleme aracı olan Checkmate'in hem frontend hem de backend tarafını içerir. Checkmate, bir sunucunun ya da web sitesinin erişilebilir ve düzgün çalışıyor olup olmadığını düzenli olarak kontrol eder; izlenen servislerin kullanılabilirliği, kesintileri ve yanıt süreleri hakkında gerçek zamanlı uyarılar ve raporlar sağlar.
Checkmate'in ayrıca uzak sunuculardan veri çekmek için [Capture](https://github.com/bluewave-labs/capture) adında bir ajanı vardır. Capture, Checkmate'i çalıştırmak için zorunlu değildir; ancak sunucularınızın CPU, RAM, disk ve sıcaklık durumu hakkında ek bilgi sağlar. Capture; Linux, Windows, Mac, Raspberry Pi ve Go çalıştırabilen herhangi bir cihazda kullanılabilir.
Checkmate, 1000'den fazla aktif monitör ile herhangi bir sorun ya da performans darboğazı yaşamadan stres testinden geçirilmiştir.
## 📚 İçindekiler
- [📦 Demo](#demo)
- [🔗 Kullanıcı kılavuzu](#users-guide)
- [🛠️ Kurulum](#installation)
- [🚀 Performans](#performance)
- [💚 Sorular & fikirler](#questions--ideas)
- [🧩 Özellikler](#features)
- [🏗️ Ekran görüntüleri](#screenshots)
- [🏗️ Teknoloji yığını](#tech-stack)
- [🔗 Birkaç bağlantı](#a-few-links)
- [🤝 Katkıda bulunma](#contributing)
<a id="demo"></a>
## Demo
[Checkmate](https://demo.checkmate.so/)'in en son sürümünü iş başında görebilirsiniz.
Kullanıcı adı demouser@demo.com, şifre Demouser1!'dir (Not: Demo sunucusunu zaman zaman güncelliyoruz; çalışmazsa Discussions kanalından bize haber verin).
<a id="users-guide"></a>
## Kullanıcı kılavuzu
Kullanım talimatlarını [burada](https://checkmate.so/docs) bulabilirsiniz.
## Ön koşullar
- [Docker](https://www.docker.com/) kurulu
- [Git](https://git-scm.com/) kurulu
<a id="installation"></a>
## Kurulum
Kurulum talimatları için [Checkmate dokümantasyon portalına](https://checkmate.so/docs) bakın.
Alternatif olarak hızlıca bir Checkmate örneği çalıştırmak için [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (Güney Afrika), [Cloudzy](https://cloudzy.com/marketplace/checkmate) veya [Pikapods](https://www.pikapods.com/)'u kullanabilirsiniz. Sunucu altyapınızı izlemek isterseniz [Capture ajanına](https://github.com/bluewave-labs/capture) ihtiyacınız olacak. Capture deposu da kurulum talimatlarını içerir.
### Özel CA Kullanımı
Özel sertifika otoritelerinden (örneğin Smallstep) gelen sertifikalarla iç HTTPS uç noktalarını izlemeniz gerekiyorsa, Docker yapılandırma seçenekleri için [Özel CA Güven Kılavuzumuza](../custom-ca-trust.md) bakın.
Daha fazla dokümantasyon için [docs dizinine](../) göz atın.
<a id="performance"></a>
## Performans
Geniş kapsamlı optimizasyonlar sayesinde Checkmate son derece düşük bellek ayak iziyle çalışır ve minimum bellek ile CPU kaynağı gerektirir. Aşağıda her dakika 323 sunucuyu izleyen bir sunucudaki Node.js örneğinin bellek kullanımı yer almaktadır:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
Aynı sayıda sunucu için aynı sunucudaki MongoDB ve Redis bellek ayak izini de görebilirsiniz (398 MB ve 15 MB):
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Sorular & fikirler
Sorularınız, önerileriniz ya da yorumlarınız varsa birkaç seçeneğiniz var:
- [Discord kanalı](https://discord.gg/NAb6H3UTjK) (tercih edilen)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (zaman zaman buraya bakıyoruz)
Soru sormaktan ya da fikirlerinizi paylaşmaktan çekinmeyin — sizi dinlemekten mutluluk duyarız!
<a id="features"></a>
## Özellikler
- Tamamen açık kaynak; kendi sunucularınızda ya da ev cihazlarınızda (örn. Raspberry Pi 4 veya 5) çalıştırılabilir
- Birden fazla izleme seçeneği: çalışma süresi, Docker, ping, SSL, port, oyun sunucusu
- Sayfa hızı izleme
- Altyapı izleme (bellek, disk kullanımı, CPU performansı, ağ vb.) — [Capture](https://github.com/bluewave-labs/capture) ajanı gerekir
- Bağlama noktası seçimiyle seçici disk izleme
- Olayları tek bakışta görme
- 4 farklı şık temaya sahip durum sayfaları
- E-posta, webhook, Discord, Slack, PagerDuty, Matrix, Microsoft Teams, Telegram, Pushover, Twilio (SMS) bildirimleri
- Planlı bakım
- JSON sorgu izleme
- Çoklu dil desteği: Arapça, Çince (Basitleştirilmiş), Çince (Geleneksel, Tayvan), Çekçe, İngilizce, Fince, Fransızca, Almanca, Japonca, Portekizce (Brezilya), Rusça, İspanyolca, Tayca, Türkçe, Ukraynaca ve Vietnamca
## Monitör yaşam döngüsü
1. Bir monitör kontrol gerçekleştirir (HTTP / ping / port / Capture ajanı üzerinden donanım)
2. Sonuç kaydedilir (başarılı/başarısız + yanıt süresi)
3. Son sonuçlar, monitörün durum değişikliği eşiğine göre değerlendirilir
4. Eşik aşıldığında ve mevcut durum öncekinden farklıysa, monitörün durumu değişir (örn. `initializing`, `up`, `down`, `breached`)
5. Durum değişikliğinde, monitörün geçerli durumuna bağlı olarak bir olay oluşturulur ya da çözülür
6. Yapılandırmaya göre bildirimler tetiklenir
<a id="screenshots"></a>
## Ekran görüntüleri
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Teknoloji yığını
- [ReactJs](https://react.dev/)
- [MUI (React framework)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- Ve birçok başka açık kaynak bileşen!
<a id="a-few-links"></a>
## Birkaç bağlantı
- Bizi desteklemek isterseniz, lütfen ⭐ verip "watch" düğmesine basmayı düşünün.
- Yol haritası veya özellik seti için bir sorunuz ya da öneriniz var mı? [Discord kanalımıza](https://discord.gg/NAb6H3UTjK) veya [Discussions](https://github.com/bluewave-labs/checkmate/discussions) forumumuza bakın.
- Yeni bir sürüm çıktığında haber almak ister misiniz? Ücretsiz bir sürüm takip servisi olan [Newreleases](https://newreleases.io/)'ü kullanın.
- Bir Checkmate [kurulum ve kullanım videosu](https://www.youtube.com/watch?v=GfFOc0xHIwY) izleyin.
<a id="contributing"></a>
## Katkıda bulunma
Biz; [Alex](http://github.com/ajhollid) (takım lideri), [Gorkem](http://github.com/gorkem-bwl/), [Aryaman](https://github.com/Br0wnHammer), [Mert](https://github.com/mertssmnoglu) ve [Karen](https://github.com/karenvicent) olarak bireylerin ve işletmelerin altyapı ile sunucularını izlemelerine yardımcı oluyoruz.
Her seviyedeki katkıda bulunanlarla güçlü bağlar kurmaktan gurur duyuyoruz. Genç bir proje olmasına rağmen Checkmate şimdiden 7000'den fazla yıldız ve dünya genelinde 90'dan fazla katkıda bulunan kazanmıştır.
Reponuz **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes ve NEC** çalışanları tarafından yıldızlanmıştır — siz de çekinmeyin, katılın, katkıda bulunun ve bizimle birlikte öğrenin!
Nasıl katkıda bulunabilirsiniz:
0. Bu repoya yıldız verin :)
1. [Katkıda bulunma kılavuzunu](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md) inceleyin. Yeni başlayanların `good-first-issue` etiketine bakmasını öneririz.
2. Mimari hakkında derinlemesine bilgi almak isterseniz, ayrıntılı [Checkmate yapısını](https://deepwiki.com/bluewave-labs/Checkmate) okuyun.
3. Bir hata yakaladığınızı düşünüyorsanız bir issue açın.
4. Yeni başlıyorsanız `good-first-issue`'lara bakın.
5. Yeni özellikler eklemek, yaşam kalitesi iyileştirmeleri yapmak veya hata düzeltmek için pull request açın.
6. CodeCanvas'taki `Checkmate` kod tabanının interaktif gezintisine [buradan](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true) göz atın. Mevcut veri akışı simülasyonlarını iyileştirmek veya yenilerini oluşturmak için [buradaki](https://docs.code-canvas.com/updating-diagram) hızlı eğitimi takip edin.
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+200
View File
@@ -0,0 +1,200 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
> ⚠️ Цей переклад є машинним чернетковим варіантом. Будемо вдячні за виправлення від носіїв мови через pull request.
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Open source застосунок для моніторингу доступності та інфраструктури</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
Цей репозиторій містить як фронтенд, так і бекенд Checkmate — інструменту моніторингу з відкритим кодом і самостійним розгортанням для відстеження апаратного забезпечення серверів, часу безперебійної роботи, часу відгуку та інцидентів у реальному часі з красивими візуалізаціями. Checkmate регулярно перевіряє, чи доступний сервер або сайт і чи працює він оптимально, надаючи сповіщення та звіти у реальному часі про доступність, простої та час відгуку моніторованих сервісів.
У Checkmate також є агент під назвою [Capture](https://github.com/bluewave-labs/capture) для отримання даних з віддалених серверів. Capture не є обов'язковим для роботи Checkmate, але дає додаткову інформацію про стан CPU, RAM, диска та температури ваших серверів. Capture можна запускати на Linux, Windows, Mac, Raspberry Pi або будь-якому пристрої, здатному виконувати Go.
Checkmate пройшов навантажувальне тестування з понад 1000 активних моніторів без жодних помітних проблем чи вузьких місць.
## 📚 Зміст
- [📦 Демо](#demo)
- [🔗 Посібник користувача](#users-guide)
- [🛠️ Встановлення](#installation)
- [🚀 Продуктивність](#performance)
- [💚 Запитання та ідеї](#questions--ideas)
- [🧩 Можливості](#features)
- [🏗️ Знімки екрана](#screenshots)
- [🏗️ Технології](#tech-stack)
- [🔗 Кілька посилань](#a-few-links)
- [🤝 Внески](#contributing)
<a id="demo"></a>
## Демо
Найновішу збірку [Checkmate](https://demo.checkmate.so/) можна побачити в дії.
Логін — demouser@demo.com, пароль — Demouser1! (Зверніть увагу: ми час від часу оновлюємо демо-сервер; якщо щось не працює — напишіть нам у Discussions).
<a id="users-guide"></a>
## Посібник користувача
Інструкції з використання доступні [тут](https://checkmate.so/docs).
## Передумови
- Встановлений [Docker](https://www.docker.com/)
- Встановлений [Git](https://git-scm.com/)
<a id="installation"></a>
## Встановлення
Інструкції зі встановлення дивіться у [порталі документації Checkmate](https://checkmate.so/docs).
Альтернативно, для швидкого розгортання Checkmate можна скористатися [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (Південна Африка), [Cloudzy](https://cloudzy.com/marketplace/checkmate) або [Pikapods](https://www.pikapods.com/). Якщо хочете моніторити серверну інфраструктуру, знадобиться агент [Capture](https://github.com/bluewave-labs/capture). У репозиторії Capture також є інструкції зі встановлення.
### Використання власного УЦ
Якщо потрібно моніторити внутрішні HTTPS-ендпоїнти з сертифікатами від приватних центрів сертифікації (як-от Smallstep), див. наш [посібник з довіри власному УЦ](../custom-ca-trust.md) для опцій конфігурації Docker.
Додаткову документацію шукайте у [каталозі docs](../).
<a id="performance"></a>
## Продуктивність
Завдяки численним оптимізаціям Checkmate працює з винятково низьким споживанням пам'яті, потребуючи мінімум RAM і CPU. Ось використання пам'яті Node.js-інстансу на сервері, який моніторить 323 сервери щохвилини:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
А ось обсяг пам'яті MongoDB і Redis на тому ж сервері (398 МБ і 15 МБ) для тієї ж кількості серверів:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Запитання та ідеї
Якщо у вас є запитання, пропозиції чи коментарі, є кілька варіантів:
- [Канал у Discord](https://discord.gg/NAb6H3UTjK) (бажано)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (заходимо туди час від часу)
Не соромтеся ставити запитання чи ділитися ідеями — ми будемо раді почути вас!
<a id="features"></a>
## Можливості
- Повністю відкритий код, розгортається на ваших серверах чи домашніх пристроях (наприклад, Raspberry Pi 4 або 5)
- Кілька варіантів моніторингу: доступність, Docker, ping, SSL, порт, ігровий сервер
- Моніторинг швидкості сторінок
- Моніторинг інфраструктури (пам'ять, використання диска, продуктивність CPU, мережа тощо) — потрібен агент [Capture](https://github.com/bluewave-labs/capture)
- Вибірковий моніторинг дисків із вибором точок монтування
- Інциденти з одного погляду
- Сторінки статусу з 4 красивими темами
- Сповіщення електронною поштою, через вебхуки, Discord, Slack, PagerDuty, Matrix, Microsoft Teams, Telegram, Pushover, Twilio (SMS)
- Заплановане обслуговування
- Моніторинг за допомогою JSON-запитів
- Багатомовна підтримка: арабська, китайська (спрощена), китайська (традиційна, Тайвань), чеська, англійська, фінська, французька, німецька, японська, португальська (Бразилія), російська, іспанська, тайська, турецька, українська та в'єтнамська
## Життєвий цикл монітора
1. Монітор виконує перевірку (HTTP / ping / порт / апаратне забезпечення через агента Capture)
2. Результат зберігається (успіх/невдача + час відгуку)
3. Останні результати оцінюються відносно налаштованого порога зміни статусу
4. Якщо порогу досягнуто, а поточний статус відрізняється від попереднього — стан монітора змінюється (наприклад, `initializing`, `up`, `down`, `breached`)
5. При зміні стану інцидент або створюється, або закривається, залежно від поточного статусу
6. Сповіщення спрацьовують згідно з налаштуваннями
<a id="screenshots"></a>
## Знімки екрана
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Технології
- [ReactJs](https://react.dev/)
- [MUI (React-фреймворк)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- І безліч інших компонентів з відкритим кодом!
<a id="a-few-links"></a>
## Кілька посилань
- Якщо хочете підтримати нас, поставте ⭐ і натисніть «watch».
- Маєте запитання чи пропозицію щодо дорожньої карти/функцій? Загляньте в наш [Discord-канал](https://discord.gg/NAb6H3UTjK) або форум [Discussions](https://github.com/bluewave-labs/checkmate/discussions).
- Потрібне сповіщення про нові релізи? Скористайтеся [Newreleases](https://newreleases.io/) — безкоштовним сервісом відстеження релізів.
- Подивіться [відео зі встановлення та використання Checkmate](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## Внески
Ми — [Alex](http://github.com/ajhollid) (тімлід), [Gorkem](http://github.com/gorkem-bwl/), [Aryaman](https://github.com/Br0wnHammer), [Mert](https://github.com/mertssmnoglu) і [Karen](https://github.com/karenvicent) — допомагаємо приватним особам і компаніям моніторити їх інфраструктуру та сервери.
Ми пишаємося тим, що будуємо міцні зв'язки з контриб'юторами на всіх рівнях. Попри молодий вік, Checkmate уже зібрав понад 7000 зірок і понад 90 контриб'юторів з усього світу.
Наш репозиторій позначений співробітниками **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes і NEC** — тож не соромтеся, приєднуйтесь, робіть внесок і вчіться разом з нами!
Як долучитися:
0. Поставте зірку цьому репозиторію :)
1. Прочитайте [посібник контриб'ютора](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). Початківцям радимо подивитися мітку `good-first-issue`.
2. Прочитайте детальний опис [Checkmate](https://deepwiki.com/bluewave-labs/Checkmate), якщо хочете глибше зануритись у архітектуру.
3. Відкрийте issue, якщо вважаєте, що знайшли баг.
4. Якщо ви новачок, шукайте `good-first-issue`.
5. Робіть pull request, щоб додати функції, покращити зручність використання або виправити баги.
6. Перегляньте інтерактивний огляд кодової бази `Checkmate` у CodeCanvas [тут](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true). Щоб уточнити наявні симуляції потоків даних або створити нові, перейдіть до короткого туторіалу [тут](https://docs.code-canvas.com/updating-diagram).
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+200
View File
@@ -0,0 +1,200 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
> ⚠️ Bản dịch này là bản nháp được dịch bằng máy. Rất hoan nghênh sự đóng góp chỉnh sửa từ người bản ngữ qua pull request.
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>Ứng dụng mã nguồn mở giám sát tình trạng hoạt động và hạ tầng</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
Kho mã nguồn này chứa cả frontend và backend của Checkmate — một công cụ giám sát mã nguồn mở, tự lưu trữ, theo dõi phần cứng máy chủ, thời gian hoạt động, thời gian phản hồi và sự cố theo thời gian thực với các biểu đồ trực quan đẹp mắt. Checkmate định kỳ kiểm tra xem một máy chủ/trang web có khả truy cập và hoạt động tối ưu hay không, đồng thời cung cấp cảnh báo và báo cáo theo thời gian thực về độ khả dụng, thời gian gián đoạn và thời gian phản hồi của các dịch vụ được giám sát.
Checkmate cũng có một agent tên là [Capture](https://github.com/bluewave-labs/capture) dùng để lấy dữ liệu từ các máy chủ từ xa. Capture không bắt buộc để chạy Checkmate, nhưng cung cấp thêm thông tin về CPU, RAM, ổ đĩa và nhiệt độ của máy chủ. Capture có thể chạy trên Linux, Windows, Mac, Raspberry Pi hoặc bất kỳ thiết bị nào có thể chạy Go.
Checkmate đã được kiểm thử với hơn 1000 monitor đang hoạt động mà không gặp bất kỳ vấn đề nào hay điểm nghẽn hiệu năng đáng kể.
## 📚 Mục lục
- [📦 Demo](#demo)
- [🔗 Hướng dẫn sử dụng](#users-guide)
- [🛠️ Cài đặt](#installation)
- [🚀 Hiệu năng](#performance)
- [💚 Câu hỏi & ý tưởng](#questions--ideas)
- [🧩 Tính năng](#features)
- [🏗️ Ảnh chụp màn hình](#screenshots)
- [🏗️ Tech stack](#tech-stack)
- [🔗 Một vài liên kết](#a-few-links)
- [🤝 Đóng góp](#contributing)
<a id="demo"></a>
## Demo
Bạn có thể xem bản dựng mới nhất của [Checkmate](https://demo.checkmate.so/) đang chạy.
Tên đăng nhập là demouser@demo.com và mật khẩu là Demouser1! (lưu ý: chúng tôi cập nhật máy chủ demo theo thời gian; nếu không hoạt động, hãy báo cho chúng tôi qua kênh Discussions).
<a id="users-guide"></a>
## Hướng dẫn sử dụng
Có thể tìm thấy hướng dẫn sử dụng [tại đây](https://checkmate.so/docs).
## Yêu cầu trước
- Đã cài [Docker](https://www.docker.com/)
- Đã cài [Git](https://git-scm.com/)
<a id="installation"></a>
## Cài đặt
Xem hướng dẫn cài đặt trong [cổng tài liệu của Checkmate](https://checkmate.so/docs).
Hoặc bạn có thể dùng [Coolify](https://coolify.io/), [Elestio](https://elest.io/open-source/checkmate), [K8s](./charts/helm/checkmate/INSTALLATION.md), [Sive Host](https://sive.host) (Nam Phi), [Cloudzy](https://cloudzy.com/marketplace/checkmate) hoặc [Pikapods](https://www.pikapods.com/) để dựng nhanh một instance Checkmate. Nếu muốn giám sát hạ tầng máy chủ, bạn sẽ cần [agent Capture](https://github.com/bluewave-labs/capture). Kho Capture cũng chứa hướng dẫn cài đặt.
### Sử dụng CA tuỳ chỉnh
Nếu bạn cần giám sát các endpoint HTTPS nội bộ với chứng chỉ từ các tổ chức phát hành riêng (ví dụ Smallstep), hãy xem [Hướng dẫn tin cậy CA tuỳ chỉnh](../custom-ca-trust.md) với các tuỳ chọn cấu hình Docker.
Để biết thêm tài liệu, xem [thư mục docs](../).
<a id="performance"></a>
## Hiệu năng
Nhờ tối ưu rộng rãi, Checkmate hoạt động với mức sử dụng bộ nhớ rất nhỏ, chỉ cần lượng RAM và CPU tối thiểu. Dưới đây là mức sử dụng bộ nhớ của một instance Node.js trên máy chủ giám sát 323 máy chủ mỗi phút:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
Bạn cũng có thể thấy dung lượng bộ nhớ của MongoDB và Redis trên cùng máy chủ (398MB và 15MB) cho cùng số lượng máy chủ:
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## Câu hỏi & ý tưởng
Nếu bạn có câu hỏi, góp ý hoặc bình luận, có một vài tuỳ chọn:
- [Kênh Discord](https://discord.gg/NAb6H3UTjK) (khuyến nghị)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions) (chúng tôi sẽ ghé qua đều đặn)
Đừng ngần ngại hỏi hoặc chia sẻ ý tưởng — chúng tôi rất mong nghe ý kiến của bạn!
<a id="features"></a>
## Tính năng
- Hoàn toàn mã nguồn mở, có thể triển khai trên máy chủ riêng hoặc thiết bị tại nhà (ví dụ Raspberry Pi 4 hoặc 5)
- Nhiều tuỳ chọn giám sát: thời gian hoạt động, Docker, Ping, SSL, cổng, máy chủ game
- Giám sát tốc độ tải trang
- Giám sát hạ tầng (bộ nhớ, ổ đĩa, hiệu năng CPU, mạng, v.v.) — yêu cầu agent [Capture](https://github.com/bluewave-labs/capture)
- Giám sát ổ đĩa có chọn lọc với lựa chọn điểm gắn kết
- Quản lý sự cố một cái nhìn
- Trang trạng thái với 4 theme đẹp mắt
- Thông báo qua e-mail, webhook, Discord, Slack, PagerDuty, Matrix, Microsoft Teams, Telegram, Pushover, Twilio (SMS)
- Bảo trì theo lịch
- Giám sát qua truy vấn JSON
- Hỗ trợ đa ngôn ngữ: Ả Rập, Trung Quốc (giản thể), Trung Quốc (phồn thể, Đài Loan), Séc, Anh, Phần Lan, Pháp, Đức, Nhật, Bồ Đào Nha (Brazil), Nga, Tây Ban Nha, Thái, Thổ Nhĩ Kỳ, Ukraine và Việt Nam
## Vòng đời của một monitor
1. Một monitor thực hiện kiểm tra (HTTP / Ping / cổng / phần cứng qua agent Capture)
2. Kết quả được lưu lại (thành công/thất bại + thời gian phản hồi)
3. Các kết quả gần đây được đánh giá dựa trên ngưỡng thay đổi trạng thái đã cấu hình
4. Nếu đạt ngưỡng và trạng thái hiện tại khác trạng thái trước đó, trạng thái monitor sẽ thay đổi (ví dụ `initializing`, `up`, `down`, `breached`)
5. Khi trạng thái thay đổi, một sự cố sẽ được tạo hoặc giải quyết tuỳ vào trạng thái hiện tại
6. Thông báo được kích hoạt theo cấu hình
<a id="screenshots"></a>
## Ảnh chụp màn hình
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## Tech stack
- [ReactJs](https://react.dev/)
- [MUI (framework React)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- Và rất nhiều thành phần mã nguồn mở khác!
<a id="a-few-links"></a>
## Một vài liên kết
- Nếu bạn muốn ủng hộ chúng tôi, hãy cân nhắc cho dự án một ⭐ và nhấn "watch".
- Có câu hỏi hoặc gợi ý cho roadmap/tính năng? Hãy ghé [kênh Discord](https://discord.gg/NAb6H3UTjK) hoặc diễn đàn [Discussions](https://github.com/bluewave-labs/checkmate/discussions).
- Muốn nhận thông báo khi có bản phát hành mới? Hãy dùng [Newreleases](https://newreleases.io/) — dịch vụ miễn phí theo dõi phiên bản phát hành.
- Xem video [cài đặt và sử dụng Checkmate](https://www.youtube.com/watch?v=GfFOc0xHIwY)
<a id="contributing"></a>
## Đóng góp
Chúng tôi là [Alex](http://github.com/ajhollid) (trưởng nhóm), [Gorkem](http://github.com/gorkem-bwl/), [Aryaman](https://github.com/Br0wnHammer), [Mert](https://github.com/mertssmnoglu) và [Karen](https://github.com/karenvicent), hỗ trợ cá nhân và doanh nghiệp giám sát hạ tầng và máy chủ.
Chúng tôi tự hào xây dựng những mối liên kết bền chặt với người đóng góp ở mọi cấp độ. Dù còn trẻ, Checkmate đã có hơn 7000 sao và thu hút hơn 90 người đóng góp trên khắp thế giới.
Kho của chúng tôi đã được nhân viên của **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes và NEC** đánh sao — đừng ngần ngại, hãy tham gia, đóng góp và học hỏi cùng chúng tôi!
Cách bạn có thể đóng góp:
0. Đánh sao cho repo này :)
1. Đọc [hướng dẫn dành cho người đóng góp](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md). Người mới được khuyến khích kiểm tra tag `good-first-issue`.
2. Nếu muốn đi sâu vào kiến trúc, đọc [cấu trúc chi tiết của Checkmate](https://deepwiki.com/bluewave-labs/Checkmate).
3. Mở issue nếu bạn cho rằng đã gặp lỗi.
4. Nếu mới tham gia, hãy tìm các issue gắn nhãn `good-first-issue`.
5. Tạo pull request để thêm tính năng mới, cải thiện trải nghiệm hoặc sửa lỗi.
6. Xem qua bản hướng dẫn tương tác về codebase `Checkmate` trên CodeCanvas [tại đây](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true). Để hoàn thiện mô phỏng luồng dữ liệu hiện có hoặc tạo mới, xem hướng dẫn nhanh [tại đây](https://docs.code-canvas.com/updating-diagram).
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+197
View File
@@ -0,0 +1,197 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>开源的可用性与基础设施监控应用</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
本仓库同时包含 Checkmate 的前端和后端代码。Checkmate 是一款开源、可自托管的监控工具,通过精美的可视化效果实时追踪服务器硬件、可用性、响应时间以及事件。Checkmate 会定期检查服务器或网站是否可访问、运行是否正常,并针对被监控服务的可用性、停机时间和响应时间提供实时告警与报告。
Checkmate 还提供了一个名为 [Capture](https://github.com/bluewave-labs/capture) 的代理,用于从远程服务器拉取数据。Capture 并不是运行 Checkmate 所必需的,但它能提供服务器 CPU、内存、磁盘和温度的额外信息。Capture 可在 Linux、Windows、Mac、Raspberry Pi 或任何能运行 Go 的设备上运行。
Checkmate 已经在 1000+ 个活跃监控项的环境下完成压力测试,未出现任何明显问题或性能瓶颈。
## 📚 目录
- [📦 演示](#demo)
- [🔗 用户指南](#users-guide)
- [🛠️ 安装](#installation)
- [🚀 性能](#performance)
- [💚 问题与想法](#questions--ideas)
- [🧩 功能](#features)
- [🏗️ 截图](#screenshots)
- [🏗️ 技术栈](#tech-stack)
- [🔗 一些链接](#a-few-links)
- [🤝 参与贡献](#contributing)
<a id="demo"></a>
## 演示
你可以体验最新版本的 [Checkmate](https://demo.checkmate.so/)。
用户名是 demouser@demo.com,密码是 Demouser1!(请注意,我们会不定期更新演示服务器,如果遇到问题,请在 Discussions 频道告诉我们)。
<a id="users-guide"></a>
## 用户指南
使用说明请参见[这里](https://checkmate.so/docs)。
## 前置要求
- 已安装 [Docker](https://www.docker.com/)
- 已安装 [Git](https://git-scm.com/)
<a id="installation"></a>
## 安装
请参阅 [Checkmate 文档门户](https://checkmate.so/docs)中的安装说明。
你也可以使用 [Coolify](https://coolify.io/)、[Elestio](https://elest.io/open-source/checkmate)、[K8s](./charts/helm/checkmate/INSTALLATION.md)、[Sive Host](https://sive.host)(南非)、[Cloudzy](https://cloudzy.com/marketplace/checkmate) 或 [Pikapods](https://www.pikapods.com/) 快速启动一个 Checkmate 实例。如果想监控服务器基础设施,你需要 [Capture 代理](https://github.com/bluewave-labs/capture),Capture 仓库中也提供了安装说明。
### 使用自定义 CA
如果你需要使用私有 CA(例如 Smallstep)签发的证书来监控内部 HTTPS 端点,请查看我们的 [自定义 CA 信任指南](../custom-ca-trust.md),其中介绍了 Docker 的配置选项。
更多文档请参见 [docs 目录](../)。
<a id="performance"></a>
## 性能
得益于大量优化,Checkmate 在运行时占用的内存非常少,对内存和 CPU 资源的需求极低。下方是一个每分钟监控 323 台服务器的 Node.js 实例的内存使用情况:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
同一台服务器、相同监控数量下的 MongoDB 与 Redis 的内存占用(分别为 398MB 与 15MB):
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## 问题与想法
如果你有任何问题、建议或意见,欢迎通过以下方式联系我们:
- [Discord 频道](https://discord.gg/NAb6H3UTjK)(推荐)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions)(我们会不定期查看)
欢迎提问或分享你的想法 — 期待听到你的声音!
<a id="features"></a>
## 功能
- 完全开源,可部署到自己的服务器或家用设备(如 Raspberry Pi 4/5)
- 多种监控选项: 可用性、Docker、Ping、SSL、端口、游戏服务器
- 页面速度监控
- 基础设施监控(内存、磁盘使用情况、CPU 性能、网络等) — 需要 [Capture](https://github.com/bluewave-labs/capture) 代理
- 通过挂载点选择实现的选择性磁盘监控
- 一目了然的事件视图
- 提供 4 套精美主题的状态页
- 通过邮件、Webhook、Discord、Slack、PagerDuty、Matrix、Microsoft Teams、Telegram、Pushover、Twilio (SMS) 发送通知
- 计划维护
- JSON 查询监控
- 多语言支持: 阿拉伯语、简体中文、繁体中文(台湾)、捷克语、英语、芬兰语、法语、德语、日语、葡萄牙语(巴西)、俄语、西班牙语、泰语、土耳其语、乌克兰语和越南语
## 监控生命周期
1. 监控项执行一次检查(HTTP / Ping / 端口 / 通过 Capture 代理的硬件检查)
2. 结果被保存(成功/失败 + 响应时间)
3. 近期的检查结果会根据该监控项配置的状态变更阈值进行评估
4. 当达到状态变更阈值且当前状态与上一状态不同时,监控项状态发生变化(例如 `initializing`、`up`、`down`、`breached`)
5. 状态发生变化时,会根据监控项当前状态创建或解决一个事件
6. 根据配置触发通知
<a id="screenshots"></a>
## 截图
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## 技术栈
- [ReactJs](https://react.dev/)
- [MUI (React 框架)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- 以及大量其他开源组件!
<a id="a-few-links"></a>
## 一些链接
- 如果你想支持我们,请考虑给项目点 ⭐ 并点击 “watch”。
- 对路线图/功能集有问题或建议?欢迎加入我们的 [Discord 频道](https://discord.gg/NAb6H3UTjK) 或 [Discussions](https://github.com/bluewave-labs/checkmate/discussions) 论坛。
- 想在新版本发布时收到通知?可以使用免费的版本追踪服务 [Newreleases](https://newreleases.io/)。
- 观看 Checkmate 的 [安装与使用视频](https://www.youtube.com/watch?v=GfFOc0xHIwY)。
<a id="contributing"></a>
## 参与贡献
我们是 [Alex](http://github.com/ajhollid)(团队负责人)、[Gorkem](http://github.com/gorkem-bwl/)、[Aryaman](https://github.com/Br0wnHammer)、[Mert](https://github.com/mertssmnoglu) 和 [Karen](https://github.com/karenvicent),致力于帮助个人和企业监控他们的基础设施与服务器。
我们以与各个层次的贡献者建立稳固关系为荣。尽管 Checkmate 还是一个年轻的项目,但它已经获得超过 7000 颗星,并吸引了来自全球的 90+ 位贡献者。
我们的仓库已经被来自 **Google、Microsoft、Intel、Cisco、Tencent、Electronic Arts、ByteDance、JP Morgan Chase、Deloitte、Accenture、Foxconn、Broadcom、China Telecom、Barclays、Capgemini、Wipro、Cloudflare、Dassault Systèmes 和 NEC** 的员工点亮 — 所以不要犹豫,欢迎加入我们,一起贡献和成长!
参与贡献的方式:
0. 给本仓库点亮 ⭐ :)
1. 阅读 [贡献者指南](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md)。新手建议先查看 `good-first-issue` 标签。
2. 如果想深入了解架构,请阅读 [Checkmate 的详细结构介绍](https://deepwiki.com/bluewave-labs/Checkmate)。
3. 如果你认为遇到了 bug,请提交一个 issue。
4. 如果你是新手,建议从 `good-first-issue` 入手。
5. 提交 pull request 来添加新功能、改进体验或修复 bug。
6. 通过 CodeCanvas 上的 `Checkmate` 代码交互式演示了解项目([点这里](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true))。如要完善已有数据流仿真或创建新的仿真,请参考 [这里](https://docs.code-canvas.com/updating-diagram) 的快速教程。
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+197
View File
@@ -0,0 +1,197 @@
<p align=center> <a href="https://trendshift.io/repositories/12443" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12443" alt="bluewave-labs%2Fcheckmate | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
<p align="center">
🇺🇸 <a href="../../README.md">English</a> |
🇸🇦 <a href="README.ar.md">العربية</a> |
🇪🇸 <a href="README.ca.md">Català</a> |
🇨🇿 <a href="README.cs.md">Čeština</a> |
🇩🇪 <a href="README.de.md">Deutsch</a> |
🇪🇸 <a href="README.es.md">Español</a> |
🇫🇮 <a href="README.fi.md">Suomi</a> |
🇫🇷 <a href="README.fr.md">Français</a> |
🇯🇵 <a href="README.ja.md">日本語</a> |
🇧🇷 <a href="README.pt-BR.md">Português (Brasil)</a> |
🇷🇺 <a href="README.ru.md">Русский</a> |
🇹🇭 <a href="README.th.md">ไทย</a> |
🇹🇷 <a href="README.tr.md">Türkçe</a> |
🇺🇦 <a href="README.uk.md">Українська</a> |
🇻🇳 <a href="README.vi.md">Tiếng Việt</a> |
🇨🇳 <a href="README.zh-CN.md">简体中文</a> |
🇹🇼 <a href="README.zh-TW.md">繁體中文</a>
</p>
![](https://img.shields.io/github/license/bluewave-labs/checkmate)
![](https://img.shields.io/github/repo-size/bluewave-labs/checkmate)
![](https://img.shields.io/github/commit-activity/m/bluewave-labs/checkmate)
![](https://img.shields.io/github/last-commit/bluewave-labs/checkmate)
![](https://img.shields.io/github/languages/top/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues/bluewave-labs/checkmate)
![](https://img.shields.io/github/issues-pr/bluewave-labs/checkmate)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/bluewave-labs/checkmate)
<h1 align="center"><a href="https://bluewavelabs.ca" target="_blank">Checkmate</a></h1>
<p align="center"><strong>開源的可用性與基礎架構監控應用程式</strong></p>
[![Run on PikaPods](https://www.pikapods.com/static/run-button.svg)](https://www.pikapods.com/pods?run=checkmate)
<img width="1703" height="1041" alt="image" src="https://github.com/user-attachments/assets/0f4dcf38-9b42-4b84-8633-ff34778df1a8" />
<br>
本儲存庫同時包含 Checkmate 的前端與後端程式碼。Checkmate 是一款開源、可自我託管的監控工具,透過美觀的視覺化即時追蹤伺服器硬體、運作時間、回應時間以及事件。Checkmate 會定期檢查伺服器或網站是否可存取且運作正常,並針對被監控服務的可用性、停機時間和回應時間提供即時警報與報告。
Checkmate 還提供一個名為 [Capture](https://github.com/bluewave-labs/capture) 的代理程式,用以從遠端伺服器擷取資料。Capture 並非執行 Checkmate 的必要元件,但能提供伺服器 CPU、記憶體、磁碟與溫度的額外資訊。Capture 可在 Linux、Windows、Mac、Raspberry Pi 或任何能執行 Go 的裝置上運行。
Checkmate 已在 1000+ 個活躍監控項目下進行過壓力測試,未發生任何明顯問題或效能瓶頸。
## 📚 目錄
- [📦 展示](#demo)
- [🔗 使用者指南](#users-guide)
- [🛠️ 安裝](#installation)
- [🚀 效能](#performance)
- [💚 問題與想法](#questions--ideas)
- [🧩 功能](#features)
- [🏗️ 螢幕截圖](#screenshots)
- [🏗️ 技術堆疊](#tech-stack)
- [🔗 一些連結](#a-few-links)
- [🤝 參與貢獻](#contributing)
<a id="demo"></a>
## 展示
你可以體驗最新版本的 [Checkmate](https://demo.checkmate.so/)。
使用者名稱為 demouser@demo.com,密碼為 Demouser1!(我們會不定期更新示範伺服器,若無法使用,請於 Discussions 頻道告知我們)。
<a id="users-guide"></a>
## 使用者指南
使用說明請見[這裡](https://checkmate.so/docs)。
## 先決條件
- 已安裝 [Docker](https://www.docker.com/)
- 已安裝 [Git](https://git-scm.com/)
<a id="installation"></a>
## 安裝
安裝說明請參閱 [Checkmate 文件入口](https://checkmate.so/docs)。
你也可以使用 [Coolify](https://coolify.io/)、[Elestio](https://elest.io/open-source/checkmate)、[K8s](./charts/helm/checkmate/INSTALLATION.md)、[Sive Host](https://sive.host)(南非)、[Cloudzy](https://cloudzy.com/marketplace/checkmate) 或 [Pikapods](https://www.pikapods.com/) 來快速啟動一個 Checkmate 實例。若要監控伺服器基礎架構,需要 [Capture 代理程式](https://github.com/bluewave-labs/capture),Capture 儲存庫中也提供了安裝說明。
### 使用自訂 CA
如果你需要使用私有 CA(例如 Smallstep)發行的憑證來監控內部 HTTPS 端點,請參考我們的 [自訂 CA 信任指南](../custom-ca-trust.md),其中介紹了 Docker 的設定選項。
更多文件請參閱 [docs 目錄](../)。
<a id="performance"></a>
## 效能
由於進行了大量最佳化,Checkmate 的記憶體佔用極小,所需記憶體與 CPU 資源也非常少。以下是每分鐘監控 323 台伺服器的 Node.js 實例的記憶體使用情況:
![image](https://github.com/user-attachments/assets/37e04a75-d83a-488f-b25c-025511b492c9)
同一台伺服器、相同監控數量下,MongoDB 與 Redis 的記憶體佔用(分別為 398MB 與 15MB):
![image](https://github.com/user-attachments/assets/3b469e85-e675-4040-a162-3f24c1afc751)
<a id="questions--ideas"></a>
## 問題與想法
若有任何問題、建議或意見,歡迎透過以下方式聯繫我們:
- [Discord 頻道](https://discord.gg/NAb6H3UTjK)(建議使用)
- [GitHub Discussions](https://github.com/bluewave-labs/Checkmate/discussions)(我們會不定期查看)
歡迎隨時提問或分享你的想法 — 我們很期待聽到你的聲音!
<a id="features"></a>
## 功能
- 完全開源,可部署到自己的伺服器或家用裝置(例如 Raspberry Pi 4 / 5)
- 多種監控選項:可用性、Docker、Ping、SSL、連接埠、遊戲伺服器
- 頁面速度監控
- 基礎架構監控(記憶體、磁碟使用量、CPU 效能、網路等) — 需要 [Capture](https://github.com/bluewave-labs/capture) 代理程式
- 透過掛載點選擇進行的選擇性磁碟監控
- 一目了然的事件檢視
- 提供 4 套精美主題的狀態頁
- 透過電子郵件、Webhook、Discord、Slack、PagerDuty、Matrix、Microsoft Teams、Telegram、Pushover、Twilio (SMS) 傳送通知
- 排程維護
- JSON 查詢監控
- 多語言支援:阿拉伯文、簡體中文、繁體中文(台灣)、捷克文、英文、芬蘭文、法文、德文、日文、葡萄牙文(巴西)、俄文、西班牙文、泰文、土耳其文、烏克蘭文與越南文
## 監控項目生命週期
1. 監控項目執行一次檢查(HTTP / Ping / 連接埠 / 透過 Capture 代理的硬體檢查)
2. 結果會被儲存(成功/失敗 + 回應時間)
3. 近期的檢查結果會依該監控項目所設定的狀態變更門檻進行評估
4. 當達到狀態變更門檻且目前狀態與上一次狀態不同時,監控項目的狀態會改變(例如 `initializing`、`up`、`down`、`breached`)
5. 狀態變更時,會根據監控項目目前的狀態建立或解決一個事件
6. 通知會依照設定觸發
<a id="screenshots"></a>
## 螢幕截圖
<p>
<img width="1628" alt="image" src="https://github.com/user-attachments/assets/2eff6464-0738-4a32-9312-26e1e8e86275" />
</p>
<p>
<img width="1656" alt="image" src="https://github.com/user-attachments/assets/616c3563-c2a7-4ee4-af6c-7e6068955d1a" />
</p>
<p>
</p><img width="1652" alt="image" src="https://github.com/user-attachments/assets/7912d7cf-0d0e-4f26-aa5c-2ad7170b5c99" />
</p>
<p>
<img width="1652" alt="image" src="https://github.com/user-attachments/assets/08c2c6ac-3a2f-44d1-a229-d1746a3f9d16" />
</p>
<a id="tech-stack"></a>
## 技術堆疊
- [ReactJs](https://react.dev/)
- [MUI (React 框架)](https://mui.com/)
- [Node.js](https://nodejs.org/en)
- [MongoDB](https://mongodb.com)
- [Recharts](https://recharts.org)
- 以及許多其他開源元件!
<a id="a-few-links"></a>
## 一些連結
- 如果你想支持我們,請考慮給專案點 ⭐ 並點選 "watch"。
- 對藍圖或功能集有問題或建議嗎?歡迎加入我們的 [Discord 頻道](https://discord.gg/NAb6H3UTjK) 或 [Discussions](https://github.com/bluewave-labs/checkmate/discussions) 討論區。
- 想在新版本釋出時收到通知?可以使用免費的版本追蹤服務 [Newreleases](https://newreleases.io/)。
- 觀看 Checkmate 的 [安裝與使用影片](https://www.youtube.com/watch?v=GfFOc0xHIwY)。
<a id="contributing"></a>
## 參與貢獻
我們是 [Alex](http://github.com/ajhollid)(團隊負責人)、[Gorkem](http://github.com/gorkem-bwl/)、[Aryaman](https://github.com/Br0wnHammer)、[Mert](https://github.com/mertssmnoglu) 和 [Karen](https://github.com/karenvicent),致力於協助個人與企業監控其基礎架構與伺服器。
我們以與各層級貢獻者建立穩固關係為榮。儘管 Checkmate 還是一個年輕的專案,但已獲得超過 7000 顆星,並吸引了來自全球的 90+ 位貢獻者。
我們的儲存庫已被來自 **Google、Microsoft、Intel、Cisco、Tencent、Electronic Arts、ByteDance、JP Morgan Chase、Deloitte、Accenture、Foxconn、Broadcom、China Telecom、Barclays、Capgemini、Wipro、Cloudflare、Dassault Systèmes 與 NEC** 的員工點亮 — 所以請不要猶豫,加入我們,一起貢獻、一起學習吧!
如何貢獻:
0. 給本儲存庫點亮 ⭐ :)
1. 閱讀 [貢獻者指南](https://github.com/bluewave-labs/Checkmate/blob/develop/CONTRIBUTING.md)。新手建議先查看 `good-first-issue` 標籤。
2. 如果想深入了解架構,請閱讀 [Checkmate 的詳細結構介紹](https://deepwiki.com/bluewave-labs/Checkmate)。
3. 若你認為遇到了 bug,請開啟一個 issue。
4. 如果你是新手,建議從 `good-first-issue` 開始。
5. 提交 pull request 來新增功能、改善體驗或修正 bug。
6. 透過 CodeCanvas 上的 `Checkmate` 程式碼互動導覽(請點 [這裡](https://www.code-canvas.com/?session=unauthenticatedGithub&repo=Checkmate&owner=bluewave-labs&branch=develop&OnboardingTutorial=true))。若要精修現有的資料流模擬或建立新的模擬,請依 [此處](https://docs.code-canvas.com/updating-diagram) 的快速教學操作。
<a href="https://github.com/bluewave-labs/checkmate/graphs/contributors">
<img src="https://contrib.rocks/image?repo=bluewave-labs/checkmate" />
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=bluewave-labs/checkmate&type=Date)](https://star-history.com/#bluewave-labs/Checkmate&Date)
+1
View File
@@ -11,6 +11,7 @@ LOG_LEVEL=debug
# Database
# --------
DB_CONNECTION_STRING=mongodb://localhost:27017/uptime_db
QUEUE_TYPE=superSimpleQueue
# JWT Authentication
# ------------------
+10 -1
View File
@@ -12,5 +12,14 @@
"quoteProps": "as-needed",
"arrowParens": "always",
"trailingComma": "es5",
"htmlWhitespaceSensitivity": "css"
"htmlWhitespaceSensitivity": "css",
"plugins": ["prettier-plugin-ini"],
"overrides": [
{
"files": [".env", ".env.*"],
"options": {
"parser": "ini"
}
}
]
}
+107 -2
View File
@@ -392,6 +392,12 @@
"type": "string"
}
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"secret": {
"type": "string"
},
@@ -469,6 +475,7 @@
"ignoreTlsErrors",
"useAdvancedMatching",
"notifications",
"tags",
"cpuAlertThreshold",
"memoryAlertThreshold",
"diskAlertThreshold",
@@ -545,6 +552,9 @@
},
{
"$ref": "#/components/schemas/TwilioNotification"
},
{
"$ref": "#/components/schemas/NtfyNotification"
}
],
"discriminator": {
@@ -559,7 +569,8 @@
"teams": "#/components/schemas/TeamsNotification",
"telegram": "#/components/schemas/TelegramNotification",
"pushover": "#/components/schemas/PushoverNotification",
"twilio": "#/components/schemas/TwilioNotification"
"twilio": "#/components/schemas/TwilioNotification",
"ntfy": "#/components/schemas/NtfyNotification"
}
}
},
@@ -1003,6 +1014,34 @@
"phone": "+15551234567",
"twilioPhoneNumber": "+15557654321"
}
},
"NtfyNotification": {
"type": "object",
"properties": {
"notificationName": {
"type": "string",
"minLength": 1
},
"type": {
"type": "string",
"enum": ["ntfy"]
},
"address": {
"type": "string",
"format": "uri"
},
"topic": {
"type": "string",
"minLength": 1
}
},
"required": ["notificationName", "type", "address", "topic"],
"example": {
"notificationName": "ntfy topic",
"type": "ntfy",
"address": "https://ntfy.sh",
"topic": "checkmate-alerts"
}
}
},
"parameters": {}
@@ -2362,6 +2401,16 @@
"profilePicture": {
"type": "string"
},
"password": {
"type": "string",
"minLength": 8,
"pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$"
},
"newPassword": {
"type": "string",
"minLength": 8,
"pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/])[A-Za-z0-9!?@#$%^&*()\\-_=+[\\]{};:'\",.~`|\\\\/]+$"
},
"profileImage": {
"type": "string",
"format": "binary"
@@ -5207,7 +5256,8 @@
"type": "array",
"items": {
"type": "string"
}
},
"minItems": 1
},
"duration": {
"type": "number"
@@ -5478,6 +5528,24 @@
"required": false,
"name": "filter",
"in": "query"
},
{
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"required": false,
"name": "tags",
"in": "query"
}
],
"responses": {
@@ -5672,6 +5740,24 @@
"name": "type",
"in": "query"
},
{
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"required": false,
"name": "tags",
"in": "query"
},
{
"schema": {
"type": "boolean"
@@ -6924,6 +7010,12 @@
"type": "string"
}
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"secret": {
"type": "string"
},
@@ -7586,6 +7678,13 @@
},
"default": []
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"secret": {
"type": "string"
},
@@ -8104,6 +8203,12 @@
"type": "string"
}
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"secret": {
"type": "string"
},
+4
View File
@@ -68,6 +68,10 @@ const notificationVariantMeta: Record<string, { component: string; example: Reco
twilioPhoneNumber: "+15557654321",
},
},
ntfy: {
component: "NtfyNotification",
example: { notificationName: "ntfy topic", type: "ntfy", address: "https://ntfy.sh", topic: "checkmate-alerts" },
},
};
const decoratedVariants = createNotificationBodyValidation.options.map((variant) => {
+215 -86
View File
@@ -18,7 +18,7 @@
"compression": "1.8.1",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dockerode": "4.0.6",
"dockerode": "5.0.0",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-rate-limit": "^8.5.1",
@@ -31,6 +31,7 @@
"jmespath": "^0.16.0",
"jsdom": "^26.1.0",
"jsonwebtoken": "9.0.2",
"less-simple-scheduler": "2.6.0",
"mailersend": "^2.2.0",
"mjml": "^5.0.0-alpha.4",
"mongoose": "^8.3.3",
@@ -39,7 +40,7 @@
"ping": "0.4.4",
"sharp": "0.33.5",
"ssl-checker": "2.0.10",
"super-simple-scheduler": "1.9.1",
"super-simple-scheduler": "1.9.2",
"swagger-ui-express": "5.0.1",
"winston": "^3.13.0",
"ws": "^8.19.0",
@@ -75,6 +76,7 @@
"lint-staged": "^16.2.7",
"nodemon": "^3.1.11",
"prettier": "^3.3.3",
"prettier-plugin-ini": "1.3.0",
"ts-jest": "^29.4.6",
"ts-node": "^10.9.2",
"tsc-alias": "1.8.16",
@@ -866,6 +868,7 @@
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6",
@@ -1485,6 +1488,7 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
},
@@ -1529,6 +1533,7 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=18"
}
@@ -3278,25 +3283,24 @@
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/codegen": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
"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.0",
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
"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.0",
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
"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",
"@protobufjs/inquire": "^1.1.0"
"@protobufjs/aspromise": "^1.1.1"
}
},
"node_modules/@protobufjs/float": {
@@ -3306,9 +3310,9 @@
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/inquire": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz",
"integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==",
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/path": {
@@ -3324,9 +3328,9 @@
"license": "BSD-3-Clause"
},
"node_modules/@protobufjs/utf8": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz",
"integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==",
"license": "BSD-3-Clause"
},
"node_modules/@pulsecron/pulse": {
@@ -4255,6 +4259,7 @@
"integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0",
@@ -4415,6 +4420,7 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -4652,6 +4658,7 @@
"integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.56.1",
"@typescript-eslint/types": "8.56.1",
@@ -4860,9 +4867,9 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5265,6 +5272,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -5462,16 +5470,42 @@
"license": "MIT"
},
"node_modules/axios": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz",
"integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==",
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz",
"integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.11",
"follow-redirects": "^1.16.0",
"form-data": "^4.0.5",
"https-proxy-agent": "^5.0.1",
"proxy-from-env": "^2.1.0"
}
},
"node_modules/axios/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==",
"license": "MIT",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/axios/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==",
"license": "MIT",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/babel-jest": {
"version": "30.2.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz",
@@ -5721,9 +5755,9 @@
}
},
"node_modules/body-parser": {
"version": "1.20.4",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
"integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
"version": "1.20.5",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
"integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
"license": "MIT",
"dependencies": {
"bytes": "~3.1.2",
@@ -5734,7 +5768,7 @@
"http-errors": "~2.0.1",
"iconv-lite": "~0.4.24",
"on-finished": "~2.4.1",
"qs": "~6.14.0",
"qs": "~6.15.1",
"raw-body": "~2.5.3",
"type-is": "~1.6.18",
"unpipe": "~1.0.0"
@@ -5843,6 +5877,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.8.19",
"caniuse-lite": "^1.0.30001751",
@@ -5926,9 +5961,9 @@
"license": "MIT"
},
"node_modules/buildcheck": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz",
"integrity": "sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==",
"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"
@@ -6736,6 +6771,7 @@
"resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.1.tgz",
"integrity": "sha512-fm4D8ti0dQmFPeF8DXSAA//btEmqCOgAc/9Oa3C1LW94h5usNrJEfrON7b4FkPZgnDEn6OUs5NdxiJZmAtGOpQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"cssnano-preset-default": "^7.0.9",
"lilconfig": "^3.1.3"
@@ -7076,9 +7112,9 @@
}
},
"node_modules/docker-modem": {
"version": "5.0.6",
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.6.tgz",
"integrity": "sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==",
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.7.tgz",
"integrity": "sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==",
"license": "Apache-2.0",
"dependencies": {
"debug": "^4.1.1",
@@ -7091,21 +7127,20 @@
}
},
"node_modules/dockerode": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.6.tgz",
"integrity": "sha512-FbVf3Z8fY/kALB9s+P9epCpWhfi/r0N2DgYYcYpsAUlaTxPjdsitsFobnltb+lyCgAIvf9C+4PSWlTnHlJMf1w==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-5.0.0.tgz",
"integrity": "sha512-C52mvJ+7lcyhWNfrzVfFsbTrBfy/ezE9FGEYLpu17FUeBcCkxERk9nN7uDl/478ynDiQ4U+5DbQC2vENHkVEtQ==",
"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.6",
"docker-modem": "^5.0.7",
"protobufjs": "^7.3.2",
"tar-fs": "~2.1.2",
"uuid": "^10.0.0"
"tar-fs": "^2.1.4"
},
"engines": {
"node": ">= 8.0"
"node": ">= 14.17"
}
},
"node_modules/dockerode/node_modules/@grpc/proto-loader": {
@@ -7126,19 +7161,6 @@
"node": ">=6"
}
},
"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==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
@@ -7500,6 +7522,7 @@
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -7857,14 +7880,15 @@
}
},
"node_modules/express": {
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
"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",
"peer": true,
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "~1.20.3",
"body-parser": "~1.20.5",
"content-disposition": "~0.5.4",
"content-type": "~1.0.4",
"cookie": "~0.7.1",
@@ -7883,7 +7907,7 @@
"parseurl": "~1.3.3",
"path-to-regexp": "~0.1.12",
"proxy-addr": "~2.0.7",
"qs": "~6.14.0",
"qs": "~6.15.1",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "~0.19.0",
@@ -9531,6 +9555,7 @@
"integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@jest/core": "30.2.0",
"@jest/types": "30.2.0",
@@ -10391,6 +10416,95 @@
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
"license": "MIT"
},
"node_modules/less-simple-scheduler": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/less-simple-scheduler/-/less-simple-scheduler-2.6.0.tgz",
"integrity": "sha512-Vi8ouiodLqe2fyh+DqCmxE5cCJZDI3xOyUAH4jcj9sf7GunLUzH71PBcYJy2P8lxTK3LxEkHtr1reymiogs6JQ==",
"license": "ISC",
"dependencies": {
"mongodb": "7.2.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/less-simple-scheduler/node_modules/@types/whatwg-url": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz",
"integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==",
"license": "MIT",
"dependencies": {
"@types/webidl-conversions": "*"
}
},
"node_modules/less-simple-scheduler/node_modules/bson": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/bson/-/bson-7.2.0.tgz",
"integrity": "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/less-simple-scheduler/node_modules/mongodb": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.2.0.tgz",
"integrity": "sha512-F/2+BMZtLVhY30ioZp0dAmZ+IRZMBqI+nrv6t5+9/1AIwCa8sMRC3jBf81lpxMhnZgqq8CoUD503Z1oZWq1/sw==",
"license": "Apache-2.0",
"dependencies": {
"@mongodb-js/saslprep": "^1.3.0",
"bson": "^7.2.0",
"mongodb-connection-string-url": "^7.0.0"
},
"engines": {
"node": ">=20.19.0"
},
"peerDependencies": {
"@aws-sdk/credential-providers": "^3.806.0",
"@mongodb-js/zstd": "^7.0.0",
"gcp-metadata": "^7.0.1",
"kerberos": "^7.0.0",
"mongodb-client-encryption": ">=7.0.0 <7.1.0",
"snappy": "^7.3.2",
"socks": "^2.8.6"
},
"peerDependenciesMeta": {
"@aws-sdk/credential-providers": {
"optional": true
},
"@mongodb-js/zstd": {
"optional": true
},
"gcp-metadata": {
"optional": true
},
"kerberos": {
"optional": true
},
"mongodb-client-encryption": {
"optional": true
},
"snappy": {
"optional": true
},
"socks": {
"optional": true
}
}
},
"node_modules/less-simple-scheduler/node_modules/mongodb-connection-string-url": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.1.tgz",
"integrity": "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==",
"license": "Apache-2.0",
"dependencies": {
"@types/whatwg-url": "^13.0.0",
"whatwg-url": "^14.1.0"
},
"engines": {
"node": ">=20.19.0"
}
},
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -11629,9 +11743,9 @@
}
},
"node_modules/nan": {
"version": "2.23.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.23.0.tgz",
"integrity": "sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==",
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz",
"integrity": "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==",
"license": "MIT",
"optional": true
},
@@ -12375,6 +12489,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -12993,6 +13108,16 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-plugin-ini": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/prettier-plugin-ini/-/prettier-plugin-ini-1.3.0.tgz",
"integrity": "sha512-kX1KnPwITCTtYV0MTBZQKuFLxcNcbHOS9r7Ta1jHLLvRvZRTzHHqX0MEZMrCkedjLuxpQSgzr4aC5IRd+R3FPw==",
"dev": true,
"license": "MIT",
"dependencies": {
"prettier": ">=3.0.0-alpha.3"
}
},
"node_modules/pretty-format": {
"version": "30.2.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz",
@@ -13028,24 +13153,24 @@
"license": "MIT"
},
"node_modules/protobufjs": {
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.5.tgz",
"integrity": "sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==",
"version": "7.6.1",
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.1.tgz",
"integrity": "sha512-4K0myLaWL5EteuSAro91EGFgcfVgxb64Jx+7oDAY6GOkXD4M69yuSEljNcInGVCA5sOPxmZ/EqDLj2x0Q0+Ygg==",
"hasInstallScript": true,
"license": "BSD-3-Clause",
"dependencies": {
"@protobufjs/aspromise": "^1.1.2",
"@protobufjs/base64": "^1.1.2",
"@protobufjs/codegen": "^2.0.4",
"@protobufjs/eventemitter": "^1.1.0",
"@protobufjs/fetch": "^1.1.0",
"@protobufjs/codegen": "^2.0.5",
"@protobufjs/eventemitter": "^1.1.1",
"@protobufjs/fetch": "^1.1.1",
"@protobufjs/float": "^1.0.2",
"@protobufjs/inquire": "^1.1.0",
"@protobufjs/inquire": "^1.1.2",
"@protobufjs/path": "^1.1.2",
"@protobufjs/pool": "^1.1.0",
"@protobufjs/utf8": "^1.1.0",
"@protobufjs/utf8": "^1.1.1",
"@types/node": ">=13.7.0",
"long": "^5.0.0"
"long": "^5.3.2"
},
"engines": {
"node": ">=12.0.0"
@@ -13117,9 +13242,9 @@
"license": "MIT"
},
"node_modules/qs": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"version": "6.15.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
"integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
@@ -14242,18 +14367,18 @@
}
},
"node_modules/super-simple-scheduler": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/super-simple-scheduler/-/super-simple-scheduler-1.9.1.tgz",
"integrity": "sha512-JSiEQLk7MsnlSkJ87uoyFKIseG2N27ikB0FSxlh25nnQHWC7R5Lrd0VcoLpwSBanyLzlAslhK4W2rQ0mCU8jCQ==",
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/super-simple-scheduler/-/super-simple-scheduler-1.9.2.tgz",
"integrity": "sha512-dfeI1R8jFLeHXIrNsDRxqvdJN2r4BjwKrvv/pPThu0X4RQa008/ubkFno96esHad04J6KyWYTK+P/qgOcoaReg==",
"license": "MIT",
"dependencies": {
"uuid": "11.1.0"
"uuid": "11.1.1"
}
},
"node_modules/super-simple-scheduler/node_modules/uuid": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
"version": "11.1.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz",
"integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
@@ -14397,6 +14522,7 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -14565,6 +14691,7 @@
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
@@ -14728,6 +14855,7 @@
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
"devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -15361,9 +15489,9 @@
}
},
"node_modules/ws": {
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
"version": "8.21.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -15578,6 +15706,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
"integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
+7 -3
View File
@@ -9,8 +9,10 @@
"test:unit": "NODE_OPTIONS=--experimental-vm-modules jest --coverage --collectCoverageFrom='src/**/*.ts' test/unit",
"test:unit:services": "NODE_OPTIONS=--experimental-vm-modules jest --coverage --collectCoverageFrom='src/service/**/*.ts' test/unit/services test/unit/providers/network test/unit/providers/notifications",
"test:integration": "NODE_OPTIONS=--experimental-vm-modules jest --no-coverage test/integration",
"dev": "nodemon --exec tsx src/index.js",
"start": "node --watch ./dist/index.js",
"dev": "nodemon --exec tsx src/index.js",
"worker": "QUEUE_MODE=worker node ./dist/index.js",
"worker:dev": "QUEUE_MODE=worker nodemon --exec tsx src/index.js",
"clean": "rm -rf dist",
"build": "npm run clean && npm run build:openapi && tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && cp -r src/templates dist/templates",
"build:openapi": "tsc --noEmit && tsx scripts/buildOpenApi.ts",
@@ -38,7 +40,7 @@
"compression": "1.8.1",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dockerode": "4.0.6",
"dockerode": "5.0.0",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-rate-limit": "^8.5.1",
@@ -51,6 +53,7 @@
"jmespath": "^0.16.0",
"jsdom": "^26.1.0",
"jsonwebtoken": "9.0.2",
"less-simple-scheduler": "2.6.0",
"mailersend": "^2.2.0",
"mjml": "^5.0.0-alpha.4",
"mongoose": "^8.3.3",
@@ -59,7 +62,7 @@
"ping": "0.4.4",
"sharp": "0.33.5",
"ssl-checker": "2.0.10",
"super-simple-scheduler": "1.9.1",
"super-simple-scheduler": "1.9.2",
"swagger-ui-express": "5.0.1",
"winston": "^3.13.0",
"ws": "^8.19.0",
@@ -95,6 +98,7 @@
"lint-staged": "^16.2.7",
"nodemon": "^3.1.11",
"prettier": "^3.3.3",
"prettier-plugin-ini": "1.3.0",
"ts-jest": "^29.4.6",
"ts-node": "^10.9.2",
"tsc-alias": "1.8.16",
+3
View File
@@ -9,6 +9,7 @@ import QueueController from "../controllers/queueController.js";
import LogController from "../controllers/logController.js";
import StatusPageController from "../controllers/statusPageController.js";
import NotificationController from "../controllers/notificationController.js";
import TagsController from "../controllers/tagController.js";
import DiagnosticController from "../controllers/diagnosticController.js";
import IncidentController from "../controllers/incidentController.js";
import type { InitializedServices } from "@/config/services.js";
@@ -25,6 +26,7 @@ export interface InitializedControllers {
logController: LogController;
statusPageController: StatusPageController;
notificationController: NotificationController;
tagController: TagsController;
diagnosticController: DiagnosticController;
incidentController: IncidentController;
}
@@ -41,6 +43,7 @@ export const initializeControllers = (services: InitializedServices): Initialize
logController: new LogController(services.logger),
statusPageController: new StatusPageController(services.statusPageService, services.monitorsRepository, services.settingsService),
notificationController: new NotificationController(services.notificationsService, services.monitorsRepository),
tagController: new TagsController(services.tagsService),
diagnosticController: new DiagnosticController(services.diagnosticService),
incidentController: new IncidentController(services.incidentService),
};
+3
View File
@@ -17,6 +17,7 @@ import QueueRoutes from "../routes/queueRoute.js";
import LogRoutes from "../routes/logRoutes.js";
import DiagnosticRoutes from "../routes/diagnosticRoute.js";
import NotificationRoutes from "../routes/notificationRoute.js";
import TagRoutes from "../routes/tagRoutes.js";
import IncidentRoutes from "../routes/incidentRoute.js";
@@ -34,6 +35,7 @@ export const setupRoutes = (app: Application, controllers: InitializedController
const verifyStatusPageAccess = createVerifyStatusPageAccess(services.statusPagesRepository, verifyJWT);
const statusPageRoutes = new StatusPageRoutes(controllers.statusPageController, verifyJWT, verifyStatusPageAccess);
const notificationRoutes = new NotificationRoutes(controllers.notificationController);
const tagRoutes = new TagRoutes(controllers.tagController);
const diagnosticRoutes = new DiagnosticRoutes(controllers.diagnosticController, verifyJWT);
const incidentRoutes = new IncidentRoutes(controllers.incidentController);
@@ -48,6 +50,7 @@ export const setupRoutes = (app: Application, controllers: InitializedController
app.use("/api/v1/logs", verifyJWT, logRoutes.getRouter());
app.use("/api/v1/status-page", statusPageRoutes.getRouter());
app.use("/api/v1/notifications", verifyJWT, notificationRoutes.getRouter());
app.use("/api/v1/tags", verifyJWT, tagRoutes.getRouter());
app.use("/api/v1/diagnostic", verifyJWT, diagnosticRoutes.getRouter());
app.use("/api/v1/incidents", verifyJWT, incidentRoutes.getRouter());
};
+111 -52
View File
@@ -1,3 +1,4 @@
import { Mongoose } from "mongoose";
import MongoDB from "../db/MongoDB.js";
import { IDb } from "@/db/IDb.js";
import {
@@ -6,9 +7,11 @@ import {
EmailService,
BufferService,
GlobalPingService,
LessSimpleQueue,
SuperSimpleQueue,
SuperSimpleQueueHelper,
QueueHelper,
NotificationsService,
TagsService,
StatusService,
NotificationMessageBuilder,
MonitorService,
@@ -31,12 +34,14 @@ import {
TelegramProvider,
PushoverProvider,
TwilioProvider,
NtfyProvider,
// Interfaces
INetworkService,
IEmailService,
IBufferService,
ISuperSimpleQueue,
IJobQueue,
INotificationsService,
ITagsService,
IStatusService,
IMonitorService,
IUserService,
@@ -98,10 +103,12 @@ import {
MongoInvitesRepository,
MongoRecoveryTokensRepository,
MongoNotificationsRepository,
MongoTagsRepository,
MongoIncidentsRepository,
MongoTeamsRepository,
MongoMaintenanceWindowsRepository,
MongoSettingsRepository,
MongoQueueWorkersRepository,
IMonitorsRepository,
IChecksRepository,
IGeoChecksRepository,
@@ -112,21 +119,23 @@ import {
IRecoveryTokensRepository,
ISettingsRepository,
INotificationsRepository,
ITagsRepository,
IIncidentsRepository,
ITeamsRepository,
IMaintenanceWindowsRepository,
} from "@/repositories/index.js";
import { ILogger } from "@/utils/logger.js";
import { AppError } from "@/utils/AppError.js";
import { type QueueMode } from "@/types/settings.js";
export type InitializedServices = {
settingsService: ISettingsService;
db: IDb;
db: IDb<Mongoose>;
networkService: INetworkService;
emailService: IEmailService;
bufferService: IBufferService;
statusService: IStatusService;
jobQueue: ISuperSimpleQueue;
jobQueue: IJobQueue;
userService: IUserService;
checkService: ICheckService;
geoChecksService: IGeoChecksService;
@@ -137,6 +146,7 @@ export type InitializedServices = {
incidentService: IIncidentService;
logger: ILogger;
notificationsService: INotificationsService;
tagsService: ITagsService;
statusPageService: IStatusPageService;
notificationMessageBuilder: INotificationMessageBuilder;
@@ -151,6 +161,7 @@ export type InitializedServices = {
recoveryTokensRepository: IRecoveryTokensRepository;
settingsRepository: ISettingsRepository;
notificationsRepository: INotificationsRepository;
tagsRepository: ITagsRepository;
incidentsRepository: IIncidentsRepository;
teamsRepository: ITeamsRepository;
maintenanceWindowsRepository: IMaintenanceWindowsRepository;
@@ -160,60 +171,86 @@ export const initializeServices = async ({
logger,
envSettings,
settingsService,
queueMode = "primary",
}: {
logger: ILogger;
envSettings: EnvConfig;
settingsService: ISettingsService;
queueMode?: QueueMode;
}): Promise<InitializedServices> => {
// Create DB
const dbType = envSettings.dbType;
let db: IDb | null = null;
if (dbType === "mongodb") {
db = new MongoDB(logger, envSettings);
}
if (!db) {
throw new AppError({ message: "Unsupported database type", status: 500 });
}
let db: IDb<Mongoose> | null = null;
db = new MongoDB(logger, envSettings);
await db.connect();
let monitorsRepository: IMonitorsRepository;
let checksRepository: IChecksRepository;
let geoChecksRepository: IGeoChecksRepository;
let monitorStatsRepository: IMonitorStatsRepository;
let statusPagesRepository: IStatusPagesRepository;
let usersRepository: IUsersRepository;
let invitesRepository: IInvitesRepository;
let recoveryTokensRepository: IRecoveryTokensRepository;
let settingsRepository: ISettingsRepository;
let notificationsRepository: INotificationsRepository;
let incidentsRepository: IIncidentsRepository;
let teamsRepository: ITeamsRepository;
let maintenanceWindowsRepository: IMaintenanceWindowsRepository;
// ** NOTE **
// const dbType = envSettings.dbType;
// DB type has been fixed to MongoDB for now
// if (dbType === "mongodb") {
// db = new MongoDB(logger, envSettings);
// }
// if (!db) {
// throw new AppError({ message: "Unsupported database type", status: 500 });
// }
// await db.connect();
// Repositories
if (dbType === "mongodb") {
monitorsRepository = new MongoMonitorsRepository();
checksRepository = new MongoChecksRepository(logger);
geoChecksRepository = new MongoGeoChecksRepository(logger);
monitorStatsRepository = new MongoMonitorStatsRepository();
statusPagesRepository = new MongoStatusPagesRepository();
usersRepository = new MongoUsersRepository();
invitesRepository = new MongoInvitesRepository();
recoveryTokensRepository = new MongoRecoveryTokensRepository();
settingsRepository = new MongoSettingsRepository();
notificationsRepository = new MongoNotificationsRepository();
incidentsRepository = new MongoIncidentsRepository();
teamsRepository = new MongoTeamsRepository();
maintenanceWindowsRepository = new MongoMaintenanceWindowsRepository();
} else {
throw new AppError({ message: "Unsupported database type", status: 500 });
}
// ** NOTE **
// DB Type fixed to MongoDB for now
// let monitorsRepository: IMonitorsRepository;
// let checksRepository: IChecksRepository;
// let geoChecksRepository: IGeoChecksRepository;
// let monitorStatsRepository: IMonitorStatsRepository;
// let statusPagesRepository: IStatusPagesRepository;
// let usersRepository: IUsersRepository;
// let invitesRepository: IInvitesRepository;
// let recoveryTokensRepository: IRecoveryTokensRepository;
// let settingsRepository: ISettingsRepository;
// let notificationsRepository: INotificationsRepository;
// let tagsRepository: ITagsRepository;
// let incidentsRepository: IIncidentsRepository;
// let teamsRepository: ITeamsRepository;
// let maintenanceWindowsRepository: IMaintenanceWindowsRepository;
// if (dbType === "mongodb") {
// monitorsRepository = new MongoMonitorsRepository();
// checksRepository = new MongoChecksRepository(logger);
// geoChecksRepository = new MongoGeoChecksRepository(logger);
// monitorStatsRepository = new MongoMonitorStatsRepository();
// statusPagesRepository = new MongoStatusPagesRepository();
// usersRepository = new MongoUsersRepository();
// invitesRepository = new MongoInvitesRepository();
// recoveryTokensRepository = new MongoRecoveryTokensRepository();
// settingsRepository = new MongoSettingsRepository();
// notificationsRepository = new MongoNotificationsRepository();
// tagsRepository = new MongoTagsRepository();
// incidentsRepository = new MongoIncidentsRepository();
// teamsRepository = new MongoTeamsRepository();
// maintenanceWindowsRepository = new MongoMaintenanceWindowsRepository();
// } else {
// throw new AppError({ message: "Unsupported database type", status: 500 });
// }
const monitorsRepository = new MongoMonitorsRepository();
const checksRepository = new MongoChecksRepository(logger);
const geoChecksRepository = new MongoGeoChecksRepository(logger);
const monitorStatsRepository = new MongoMonitorStatsRepository();
const statusPagesRepository = new MongoStatusPagesRepository();
const usersRepository = new MongoUsersRepository();
const invitesRepository = new MongoInvitesRepository();
const recoveryTokensRepository = new MongoRecoveryTokensRepository();
const settingsRepository = new MongoSettingsRepository();
const notificationsRepository = new MongoNotificationsRepository();
const tagsRepository = new MongoTagsRepository();
const incidentsRepository = new MongoIncidentsRepository();
const teamsRepository = new MongoTeamsRepository();
const maintenanceWindowsRepository = new MongoMaintenanceWindowsRepository();
const queueWorkersRepository = new MongoQueueWorkersRepository();
// Inject settings repository into settings service (now that DB is connected)
(settingsService as SettingsService).setRepository(settingsRepository);
@@ -274,6 +311,7 @@ export const initializeServices = async ({
const telegramProvider = new TelegramProvider(logger);
const pushoverProvider = new PushoverProvider(logger);
const twilioProvider = new TwilioProvider(logger);
const ntfyProvider = new NtfyProvider(logger);
const notificationsService = new NotificationsService(
notificationsRepository,
@@ -288,12 +326,15 @@ export const initializeServices = async ({
telegramProvider,
pushoverProvider,
twilioProvider,
ntfyProvider,
settingsService,
logger,
notificationMessageBuilder
);
const superSimpleQueueHelper = new SuperSimpleQueueHelper(
const tagsService = new TagsService(tagsRepository, monitorsRepository);
const queueHelper = new QueueHelper(
logger,
networkService,
statusService,
@@ -312,7 +353,23 @@ export const initializeServices = async ({
geoChecksRepository
);
const superSimpleQueue = await SuperSimpleQueue.create(logger, superSimpleQueueHelper, monitorsRepository);
if (queueMode === "worker" && envSettings.queueType !== "lessSimpleQueue") {
throw new AppError({
message: `Worker mode requires QUEUE_TYPE="lessSimpleQueue" got ${envSettings.queueType}`,
status: 500,
service: "config services",
method: "initializeServices",
});
}
let jobQueue: IJobQueue;
switch (envSettings.queueType) {
case "lessSimpleQueue":
jobQueue = await LessSimpleQueue.create(logger, queueHelper, monitorsRepository, queueWorkersRepository, envSettings, queueMode);
break;
default:
jobQueue = await SuperSimpleQueue.create(logger, queueHelper, monitorsRepository);
}
// Business services
const userService = new UserService({
@@ -321,7 +378,7 @@ export const initializeServices = async ({
settingsService,
logger,
jwt,
jobQueue: superSimpleQueue,
jobQueue,
monitorsRepository,
usersRepository,
invitesRepository,
@@ -330,7 +387,7 @@ export const initializeServices = async ({
teamsRepository,
});
const diagnosticService = new DiagnosticService();
const diagnosticService = new DiagnosticService(db);
const inviteService = new InviteService({
invitesRepository,
settingsService,
@@ -341,7 +398,7 @@ export const initializeServices = async ({
maintenanceWindowsRepository,
});
const monitorService = new MonitorService({
jobQueue: superSimpleQueue,
jobQueue,
logger,
games,
monitorsRepository,
@@ -361,7 +418,7 @@ export const initializeServices = async ({
emailService,
bufferService,
statusService,
jobQueue: superSimpleQueue,
jobQueue,
userService,
checkService,
geoChecksService,
@@ -372,6 +429,7 @@ export const initializeServices = async ({
incidentService,
logger,
notificationsService,
tagsService,
statusPageService,
notificationMessageBuilder,
@@ -386,6 +444,7 @@ export const initializeServices = async ({
recoveryTokensRepository,
settingsRepository,
notificationsRepository,
tagsRepository,
incidentsRepository,
teamsRepository,
maintenanceWindowsRepository,
+5 -2
View File
@@ -328,7 +328,7 @@ class MonitorController implements IMonitorController {
return res.status(200).json({
success: true,
msg,
data: monitors,
data: { monitors, failedCount },
});
} catch (error) {
next(error);
@@ -357,9 +357,10 @@ class MonitorController implements IMonitorController {
const teamId = requireTeamId(req.user?.teamId);
const type = validatedQuery.type;
const tags = validatedQuery.tags;
const filter = validatedQuery.filter;
const monitors = await this.monitorService.getMonitorsByTeamId({ teamId, type, filter });
const monitors = await this.monitorService.getMonitorsByTeamId({ teamId, type, tags, filter });
return res.status(200).json({
success: true,
@@ -382,12 +383,14 @@ class MonitorController implements IMonitorController {
const field = validatedQuery.field;
const order = validatedQuery.order;
const type = validatedQuery.type;
const tags = validatedQuery.tags;
const teamId = requireTeamId(req.user?.teamId);
const monitors = await this.monitorService.getMonitorsWithChecksByTeamId({
teamId,
limit,
type,
tags,
page,
rowsPerPage,
filter,
+10 -7
View File
@@ -1,4 +1,5 @@
import { ISuperSimpleQueue } from "@/service/index.js";
import { IJobQueue } from "@/service/index.js";
import { getQueueJobsQueryValidation } from "@/validation/queueValidation.js";
import { Request, Response, NextFunction } from "express";
const SERVICE_NAME = "JobQueueController";
@@ -12,8 +13,8 @@ export interface IJobQueueController {
class JobQueueController implements IJobQueueController {
static SERVICE_NAME = SERVICE_NAME;
private jobQueue: ISuperSimpleQueue;
constructor(jobQueue: ISuperSimpleQueue) {
private jobQueue: IJobQueue;
constructor(jobQueue: IJobQueue) {
this.jobQueue = jobQueue;
}
@@ -36,11 +37,12 @@ class JobQueueController implements IJobQueueController {
getJobs = async (req: Request, res: Response, next: NextFunction) => {
try {
const jobs = await this.jobQueue.getJobs();
const pagination = getQueueJobsQueryValidation.parse(req.query);
const { jobs, count } = await this.jobQueue.getJobs(pagination);
return res.status(200).json({
success: true,
msg: "Queue jobs fetched successfully",
data: jobs,
data: { jobs, count },
});
} catch (error) {
next(error);
@@ -49,12 +51,13 @@ class JobQueueController implements IJobQueueController {
getAllMetrics = async (req: Request, res: Response, next: NextFunction) => {
try {
const jobs = await this.jobQueue.getJobs();
const pagination = getQueueJobsQueryValidation.parse(req.query);
const { jobs, count } = await this.jobQueue.getJobs(pagination);
const metrics = await this.jobQueue.getMetrics();
return res.status(200).json({
success: true,
msg: "Queue metrics fetched successfully",
data: { jobs, metrics },
data: { jobs, count, metrics },
});
} catch (error) {
next(error);
+98
View File
@@ -0,0 +1,98 @@
import { Request, Response, NextFunction } from "express";
import { ITagsService } from "@/service/index.js";
import { requireTeamId } from "./controllerUtils.js";
import {
createTagBodyValidation,
editTagBodyValidation,
getTagByIdParamValidation,
editTagParamValidation,
deleteTagParamValidation,
} from "@/validation/index.js";
export interface ITagsController {
createTag: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
getTagById: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
getTagsByTeamId: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
editTag: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
deleteTag: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
}
class TagsController implements ITagsController {
constructor(private tagsService: ITagsService) {}
createTag = async (req: Request, res: Response, next: NextFunction) => {
try {
const validatedBody = createTagBodyValidation.parse(req.body);
const teamId = requireTeamId(req.user?.teamId);
const tag = await this.tagsService.createTag(validatedBody, teamId);
return res.status(200).json({
success: true,
msg: "Tag created successfully",
data: tag,
});
} catch (error) {
next(error);
}
};
getTagById = async (req: Request, res: Response, next: NextFunction) => {
try {
const teamId = requireTeamId(req.user?.teamId);
const { id: tagId } = getTagByIdParamValidation.parse(req.params);
const tag = await this.tagsService.getTag(tagId, teamId);
return res.status(200).json({
success: true,
msg: "Tag retrieved successfully",
data: tag,
});
} catch (error) {
next(error);
}
};
getTagsByTeamId = async (req: Request, res: Response, next: NextFunction) => {
try {
const teamId = requireTeamId(req.user?.teamId);
const tags = await this.tagsService.getTagsByTeamId(teamId);
return res.status(200).json({
success: true,
msg: "Tags retrieved successfully",
data: tags,
});
} catch (error) {
next(error);
}
};
editTag = async (req: Request, res: Response, next: NextFunction) => {
try {
const teamId = requireTeamId(req.user?.teamId);
const { id: tagId } = editTagParamValidation.parse(req.params);
const validatedBody = editTagBodyValidation.parse(req.body);
const updatedTag = await this.tagsService.updateTag(tagId, teamId, validatedBody);
return res.status(200).json({
success: true,
msg: "Tag updated successfully",
data: updatedTag,
});
} catch (error) {
next(error);
}
};
deleteTag = async (req: Request, res: Response, next: NextFunction) => {
try {
const teamId = requireTeamId(req.user?.teamId);
const { id: tagId } = deleteTagParamValidation.parse(req.params);
await this.tagsService.deleteTag(tagId, teamId);
return res.status(200).json({
success: true,
msg: "Tag deleted successfully",
});
} catch (error) {
next(error);
}
};
}
export default TagsController;
+2 -1
View File
@@ -1,4 +1,5 @@
export interface IDb {
export interface IDb<TConnection = unknown> {
connect(): Promise<void>;
disconnect(): Promise<void>;
getConnection(): Promise<TConnection>;
}
+13 -2
View File
@@ -1,3 +1,4 @@
import { Mongoose } from "mongoose";
import mongoose from "mongoose";
import AppSettings from "@/db/models/AppSettings.js";
import { runMigrations } from "@/db/migration/index.js";
@@ -5,16 +6,19 @@ import { ILogger } from "@/utils/logger.js";
import { EnvConfig } from "@/service/system/settingsService.js";
const SERVICE_NAME = "MongoDB";
import { IDb } from "@/db/IDb.js";
import { AppError } from "@/utils/AppError.js";
class MongoDB implements IDb {
class MongoDB implements IDb<Mongoose> {
static SERVICE_NAME = SERVICE_NAME;
private logger: ILogger;
private envSettings: EnvConfig;
private db: Mongoose | null;
constructor(logger: ILogger, envSettings: EnvConfig) {
this.logger = logger;
this.envSettings = envSettings;
this.db = null;
}
get serviceName() {
@@ -24,7 +28,7 @@ class MongoDB implements IDb {
connect = async () => {
try {
const connectionString = this.envSettings.dbConnectionString || "mongodb://localhost:27017/uptime_db";
await mongoose.connect(connectionString);
this.db = await mongoose.connect(connectionString);
// If there are no AppSettings, create one // TODO why is this here?
await AppSettings.findOneAndUpdate(
{}, // empty filter to match any document
@@ -63,6 +67,13 @@ class MongoDB implements IDb {
}
};
getConnection = async (): Promise<Mongoose> => {
if (this.db === null) {
throw new AppError({ message: "Database not connected", service: SERVICE_NAME, method: "getConnection" });
}
return this.db;
};
disconnect = async () => {
try {
this.logger.info({ message: "Disconnecting from MongoDB", service: SERVICE_NAME, method: "disconnect" });
@@ -5,6 +5,7 @@ const CHECKS_COLLECTION = "checks";
const BACKUP_COLLECTION = "checks_backup";
const FAILED_DOCS_COLLECTION = "checks_migration_failed";
const BATCH_SIZE = 1000;
const NAMESPACE_EXISTS_CODE = 48;
interface MigrationStats {
totalSource: number;
@@ -41,6 +42,13 @@ const backupAndDropExistingCollection = async () => {
if (collections.length === 0) {
return false;
}
// The Check model declares `timeseries`, so on a fresh database Mongoose's autoCreate
// already creates `checks` as a time-series collection. Skip and return false
if (collections[0]?.type === "timeseries") {
return false;
}
const backupExists = await db.listCollections({ name: BACKUP_COLLECTION }).toArray();
if (backupExists.length > 0) {
throw new Error(`Backup collection "${BACKUP_COLLECTION}" already exists. ` + `Please remove it manually before running migration.`);
@@ -59,13 +67,21 @@ const createTimeSeriesCollection = async (backedUp: boolean) => {
try {
const existing = await db.listCollections({ name: CHECKS_COLLECTION }).toArray();
if (existing.length === 0) {
await db.createCollection(CHECKS_COLLECTION, {
timeseries: {
timeField: "createdAt",
metaField: "metadata",
granularity: "seconds",
},
});
try {
await db.createCollection(CHECKS_COLLECTION, {
timeseries: {
timeField: "createdAt",
metaField: "metadata",
granularity: "seconds",
},
});
} catch (error) {
// Mongoose's autoCreate can create `checks` concurrently with this migration,
// already exists as a time-series collection, this is success, otherwise throw
if ((error as { code?: number })?.code !== NAMESPACE_EXISTS_CODE) {
throw error;
}
}
}
await db
@@ -80,7 +96,11 @@ const createTimeSeriesCollection = async (backedUp: boolean) => {
]);
} catch (error) {
if (backedUp) {
await restoreFromBackup();
try {
await restoreFromBackup();
} catch (restoreError) {
console.error("rollback failed:", restoreError);
}
}
throw error;
}
@@ -123,8 +143,7 @@ const migrateBackupData = async (backedUp: boolean): Promise<MigrationStats> =>
const target = db.collection(CHECKS_COLLECTION);
stats.totalSource = await source.countDocuments();
const cursor = source.find().addCursorFlag("noCursorTimeout", true);
const cursor = source.find();
const operations: AnyBulkWriteOperation<Document>[] = [];
const invalidDocs: Document[] = [];
@@ -166,7 +185,11 @@ const migrateBackupData = async (backedUp: boolean): Promise<MigrationStats> =>
return stats;
} catch (error) {
if (backedUp) {
await restoreFromBackup();
try {
await restoreFromBackup();
} catch (restoreError) {
console.error("rollback failed:", restoreError);
}
}
throw error;
}
+8 -1
View File
@@ -18,11 +18,12 @@ type CheckSnapshotDocument = Omit<CheckSnapshot, "createdAt"> & { createdAt: Dat
type MonitorDocumentBase = Omit<
Monitor,
"id" | "userId" | "teamId" | "notifications" | "selectedDisks" | "statusWindow" | "recentChecks" | "createdAt" | "updatedAt"
"id" | "userId" | "teamId" | "notifications" | "tags" | "selectedDisks" | "statusWindow" | "recentChecks" | "createdAt" | "updatedAt"
> & {
statusWindow: boolean[];
recentChecks: CheckSnapshotDocument[];
notifications: Types.ObjectId[];
tags: Types.ObjectId[];
selectedDisks: string[];
matchMethod?: MonitorMatchMethod;
};
@@ -284,6 +285,12 @@ const MonitorSchema = new Schema<MonitorDocument>(
ref: "Notification",
},
],
tags: [
{
type: Schema.Types.ObjectId,
ref: "Tag",
},
],
secret: {
type: String,
},
+14 -1
View File
@@ -25,7 +25,19 @@ const NotificationSchema = new Schema<NotificationDocument>(
},
type: {
type: String,
enum: ["email", "slack", "discord", "webhook", "pager_duty", "matrix", "teams", "telegram", "pushover", "twilio"] as NotificationChannel[],
enum: [
"email",
"slack",
"discord",
"webhook",
"pager_duty",
"matrix",
"teams",
"telegram",
"pushover",
"twilio",
"ntfy",
] as NotificationChannel[],
required: true,
},
notificationName: {
@@ -39,6 +51,7 @@ const NotificationSchema = new Schema<NotificationDocument>(
accessToken: { type: String },
accountSid: { type: String },
twilioPhoneNumber: { type: String },
topic: { type: String },
},
{
timestamps: true,
+28
View File
@@ -0,0 +1,28 @@
import { Schema, model } from "mongoose";
import { QueueModes, type QueueMode } from "@/types/settings.js";
// Heartbeat presence record for a scheduler instance. _id is the scheduler's
// workerId (hostname:pid:uuid). The TTL index on lastSeenAt garbage-collects
// records for instances that have stopped heartbeating.
interface QueueWorkerDocument {
_id: string;
mode: QueueMode;
lastSeenAt: Date;
createdAt: Date;
updatedAt: Date;
}
const QueueWorkerSchema = new Schema<QueueWorkerDocument>(
{
_id: { type: String, required: true },
mode: { type: String, enum: QueueModes, required: true },
lastSeenAt: { type: Date, default: Date.now, expires: 30 },
},
{ timestamps: true }
);
const QueueWorkerModel = model<QueueWorkerDocument>("QueueWorker", QueueWorkerSchema);
export type { QueueWorkerDocument };
export { QueueWorkerModel };
export default QueueWorkerModel;
+33
View File
@@ -0,0 +1,33 @@
import { Schema, model, type Types } from "mongoose";
import type { Tag as TagEntity } from "@/types/index.js";
type TagDocumentBase = Omit<TagEntity, "id" | "teamId" | "createdAt" | "updatedAt">;
interface TagDocument extends TagDocumentBase {
_id: Types.ObjectId;
teamId: Types.ObjectId;
createdAt: Date;
updatedAt: Date;
}
const TagSchema = new Schema<TagDocument>(
{
teamId: {
type: Schema.Types.ObjectId,
ref: "Team",
immutable: true,
required: true,
},
name: { type: String, required: true },
color: { type: String, required: true, default: "#13715B" },
},
{ timestamps: true }
);
TagSchema.index({ teamId: 1, name: 1 }, { unique: true });
const TagModel = model<TagDocument>("Tag", TagSchema);
export type { TagDocument };
export { TagModel };
export default TagModel;
Loaded 100 of 162 files, more files were not shown because too many files have changed in this diff. Show more