From 9479fd7d0961d2b17daaeac107fd5d767ef5044a Mon Sep 17 00:00:00 2001 From: Eli Bosley Date: Thu, 18 May 2023 11:34:41 -0400 Subject: [PATCH] feat: notification support (#640) --- .github/workflows/main.yml | 28 +- .github/workflows/pull-request.yml | 26 +- api/codegen.yml | 31 +- api/dev/Unraid.net/myservers.cfg | 2 +- api/dev/data/permissions.json | 191 +++ api/dev/dynamix/dynamix.cfg | 35 + .../archive/Appdata_Backup_1683699325.notify | 6 + .../Community_Applications_1683223240.notify | 5 + .../Community_Applications_1683568807.notify | 5 + .../Community_Applications_1683655211.notify | 5 + .../Community_Applications_1683741604.notify | 5 + ...cker___AirConnect_3d53..ad24_ticket.notify | 5 + ...cker___AirConnect_91bd..76e7_ticket.notify | 5 + ...cker___AirConnect_ca7f..6b17_ticket.notify | 5 + ...cker___PhotoPrism_295d..2ed1_ticket.notify | 5 + ...cker___PhotoPrism_906c..d43c_ticket.notify | 5 + ...__binhex_prowlarr_1126..5559_ticket.notify | 5 + ...er___caddy_custom_024e..04d1_ticket.notify | 5 + ...er___caddy_custom_18da..c9bd_ticket.notify | 5 + ...er___caddy_custom_3005..022f_ticket.notify | 5 + ...er___caddy_custom_7c36..343c_ticket.notify | 5 + ...er___caddy_custom_7f52..acca_ticket.notify | 5 + ...er___caddy_custom_9f05..b531_ticket.notify | 5 + ...er___caddy_custom_cf91..b716_ticket.notify | 5 + ...er___caddy_custom_d851..8877_ticket.notify | 5 + ...er___caddy_custom_dfd0..0e5d_ticket.notify | 5 + ...er___caddy_custom_e6c6..fbb4_ticket.notify | 5 + ...er___caddy_custom_fcc0..f692_ticket.notify | 5 + ...ocker___filestash_6b94..7dd4_ticket.notify | 5 + ...Docker___jellyfin_2902..af01_ticket.notify | 5 + .../Docker___mariadb_45af..47d4_ticket.notify | 5 + .../Docker___mariadb_e036..3e8e_ticket.notify | 5 + .../Docker___plex_7bfa..19fb_ticket.notify | 5 + ...ker___qbittorrent_43b9..9adc_ticket.notify | 5 + ...ocker___radarr_4k_62ca..1a81_ticket.notify | 5 + .../Docker___radarr_62ca..1a81_ticket.notify | 5 + ...torrent_rutorrent_a16c..af8b_ticket.notify | 5 + ...torrent_rutorrent_b722..38c5_ticket.notify | 5 + .../Docker___sonarr_12c7..6b53_ticket.notify | 5 + .../Docker___sonarr_12c9..01df_ticket.notify | 5 + .../Docker___sonarr_27f9..c81b_ticket.notify | 5 + .../Docker___sonarr_354f..3ed7_ticket.notify | 5 + .../Docker___sonarr_6145..3ba7_ticket.notify | 5 + .../Docker___sonarr_b2f0..9f04_ticket.notify | 5 + .../Docker___sonarr_bcfd..b7bd_ticket.notify | 5 + .../Docker___sonarr_c04c..6b30_ticket.notify | 5 + ...cker___synclounge_38f9..8503_ticket.notify | 5 + .../Parity_Check_Tuning_1683212331.notify | 5 + .../Parity_Check_Tuning_1683212357.notify | 5 + .../Parity_Check_Tuning_1683212414.notify | 5 + .../Parity_Check_Tuning_1683212430.notify | 5 + .../Parity_Check_Tuning_1683696603.notify | 5 + .../Parity_Check_Tuning_1683699512.notify | 5 + .../Parity_Check_Tuning_1683699523.notify | 5 + .../Parity_Check_Tuning_1683700590.notify | 5 + .../Parity_Check_Tuning_1683700596.notify | 5 + .../Parity_Check_Tuning_1683730812.notify | 5 + .../Parity_Check_Tuning_1683783001.notify | 5 + .../Parity_Check_Tuning_1683817213.notify | 5 + .../Parity_Check_Tuning_1683869402.notify | 5 + .../Parity_Check_Tuning_1683903615.notify | 5 + .../Parity_Check_Tuning_1683955802.notify | 5 + ...___appdata.backup_2023.05.09_ticket.notify | 5 + ...ity.applications_2023.05.07a_ticket.notify | 5 + ...rity.check.tuning_2023.05.07_ticket.notify | 5 + .../Remote_Access_Started_1683325569.notify | 5 + .../Remote_Access_Started_1683325597.notify | 5 + .../Remote_Access_Started_1683383543.notify | 5 + .../Remote_Access_Stopped_1683397158.notify | 5 + .../Remote_Access_Stopped_1683422667.notify | 5 + .../Remote_Access_Stopped_1683820748.notify | 5 + .../Remote_Access_Stopped_1683986670.notify | 5 + .../Remote_Access_Stopped_1684161220.notify | 5 + .../Unraid_Parity_check_1683971161.notify | 5 + .../Unraid_Server_Alert_1683570896.notify | 5 + .../Unraid_Server_Alert_1683570945.notify | 5 + .../Unraid_Server_Alert_1683570980.notify | 5 + .../Unraid_Server_Alert_1683571299.notify | 5 + .../Unraid_Server_Alert_1683571468.notify | 5 + .../Unraid_Server_Alert_1683571535.notify | 5 + .../Unraid_Server_Alert_1683571565.notify | 5 + .../archive/Unraid_Status_1683260401.notify | 6 + .../archive/Unraid_Status_1683346801.notify | 6 + .../archive/Unraid_Status_1683433201.notify | 6 + .../archive/Unraid_Status_1683519601.notify | 6 + .../archive/Unraid_Status_1683606001.notify | 6 + .../archive/Unraid_Status_1683692401.notify | 6 + .../archive/Unraid_Status_1683778802.notify | 6 + .../archive/Unraid_Status_1683865201.notify | 6 + .../archive/Unraid_Status_1683951601.notify | 6 + .../archive/Unraid_Status_1684038001.notify | 6 + .../archive/Unraid_Status_1684124401.notify | 6 + .../Unraid_Parity_check_1683971161.notify | 5 + .../Unraid_Server_Alert_1683570896.notify | 5 + .../Unraid_Server_Alert_1683570945.notify | 5 + .../Unraid_Server_Alert_1683570980.notify | 5 + .../Unraid_Server_Alert_1683571299.notify | 5 + .../Unraid_Server_Alert_1683571468.notify | 5 + .../Unraid_Server_Alert_1683571535.notify | 5 + .../Unraid_Server_Alert_1683571565.notify | 5 + .../unread/Unraid_Status_1683260401.notify | 6 + .../unread/Unraid_Status_1683346801.notify | 6 + .../unread/Unraid_Status_1683433201.notify | 6 + .../unread/Unraid_Status_1683519601.notify | 6 + .../unread/Unraid_Status_1683606001.notify | 6 + .../unread/Unraid_Status_1683692401.notify | 6 + .../unread/Unraid_Status_1683778802.notify | 6 + .../unread/Unraid_Status_1683865201.notify | 6 + .../unread/Unraid_Status_1683951601.notify | 6 + .../unread/Unraid_Status_1684038001.notify | 6 + .../unread/Unraid_Status_1684124401.notify | 6 + api/dev/states/myservers.cfg | 6 +- api/docker-compose.yml | 68 + .../default-permissions.test.ts.snap | 350 +++++ .../utils/images/image-file-helpers.test.ts | 2 +- .../__snapshots__/notifications.test.ts.snap | 196 +++ .../store/modules/notifications.test.ts | 14 + .../__test__/store/state-parsers/var.test.ts | 172 +++ api/src/common/dashboard/generate-data.ts | 2 +- api/src/core/default-permissions.ts | 120 ++ api/src/core/log.ts | 143 ++ .../notifications/setup-notification-watch.ts | 34 + api/src/core/pubsub.ts | 22 + api/src/core/types/ini.ts | 85 ++ api/src/graphql/generated/api/operations.ts | 1171 +++++++++++++++++ api/src/graphql/generated/api/types.ts | 41 +- .../client}/validators.ts | 0 api/src/graphql/resolvers/query/index.ts | 2 + .../graphql/resolvers/query/notifications.ts | 39 + .../graphql/resolvers/subscription/index.ts | 74 ++ .../graphql/resolvers/subscription/network.ts | 2 +- .../types/notifications/notifications.graphql | 51 + api/src/index.ts | 2 +- .../store/actions/load-dynamix-config-file.ts | 33 + api/src/store/index.ts | 64 +- .../store/listeners/listener-middleware.ts | 54 + .../listeners/notification-path-listener.ts | 20 + api/src/store/modules/dynamix.ts | 54 +- api/src/store/modules/notifications.ts | 113 ++ api/src/store/store-sync.ts | 57 + api/src/store/watch/dynamix-config-watch.ts | 2 +- 141 files changed, 3710 insertions(+), 144 deletions(-) create mode 100644 api/dev/data/permissions.json create mode 100644 api/dev/dynamix/dynamix.cfg create mode 100644 api/dev/notifications/archive/Appdata_Backup_1683699325.notify create mode 100644 api/dev/notifications/archive/Community_Applications_1683223240.notify create mode 100644 api/dev/notifications/archive/Community_Applications_1683568807.notify create mode 100644 api/dev/notifications/archive/Community_Applications_1683655211.notify create mode 100644 api/dev/notifications/archive/Community_Applications_1683741604.notify create mode 100644 api/dev/notifications/archive/Docker___AirConnect_3d53..ad24_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___AirConnect_91bd..76e7_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___AirConnect_ca7f..6b17_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___PhotoPrism_295d..2ed1_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___PhotoPrism_906c..d43c_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___binhex_prowlarr_1126..5559_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___caddy_custom_024e..04d1_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___caddy_custom_18da..c9bd_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___caddy_custom_3005..022f_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___caddy_custom_7c36..343c_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___caddy_custom_7f52..acca_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___caddy_custom_9f05..b531_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___caddy_custom_cf91..b716_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___caddy_custom_d851..8877_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___caddy_custom_dfd0..0e5d_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___caddy_custom_e6c6..fbb4_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___caddy_custom_fcc0..f692_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___filestash_6b94..7dd4_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___jellyfin_2902..af01_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___mariadb_45af..47d4_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___mariadb_e036..3e8e_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___plex_7bfa..19fb_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___qbittorrent_43b9..9adc_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___radarr_4k_62ca..1a81_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___radarr_62ca..1a81_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___rtorrent_rutorrent_a16c..af8b_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___rtorrent_rutorrent_b722..38c5_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___sonarr_12c7..6b53_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___sonarr_12c9..01df_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___sonarr_27f9..c81b_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___sonarr_354f..3ed7_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___sonarr_6145..3ba7_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___sonarr_b2f0..9f04_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___sonarr_bcfd..b7bd_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___sonarr_c04c..6b30_ticket.notify create mode 100644 api/dev/notifications/archive/Docker___synclounge_38f9..8503_ticket.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683212331.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683212357.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683212414.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683212430.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683696603.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683699512.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683699523.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683700590.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683700596.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683730812.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683783001.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683817213.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683869402.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683903615.notify create mode 100644 api/dev/notifications/archive/Parity_Check_Tuning_1683955802.notify create mode 100644 api/dev/notifications/archive/Plugin___appdata.backup_2023.05.09_ticket.notify create mode 100644 api/dev/notifications/archive/Plugin___community.applications_2023.05.07a_ticket.notify create mode 100644 api/dev/notifications/archive/Plugin___parity.check.tuning_2023.05.07_ticket.notify create mode 100644 api/dev/notifications/archive/Remote_Access_Started_1683325569.notify create mode 100644 api/dev/notifications/archive/Remote_Access_Started_1683325597.notify create mode 100644 api/dev/notifications/archive/Remote_Access_Started_1683383543.notify create mode 100644 api/dev/notifications/archive/Remote_Access_Stopped_1683397158.notify create mode 100644 api/dev/notifications/archive/Remote_Access_Stopped_1683422667.notify create mode 100644 api/dev/notifications/archive/Remote_Access_Stopped_1683820748.notify create mode 100644 api/dev/notifications/archive/Remote_Access_Stopped_1683986670.notify create mode 100644 api/dev/notifications/archive/Remote_Access_Stopped_1684161220.notify create mode 100644 api/dev/notifications/archive/Unraid_Parity_check_1683971161.notify create mode 100644 api/dev/notifications/archive/Unraid_Server_Alert_1683570896.notify create mode 100644 api/dev/notifications/archive/Unraid_Server_Alert_1683570945.notify create mode 100644 api/dev/notifications/archive/Unraid_Server_Alert_1683570980.notify create mode 100644 api/dev/notifications/archive/Unraid_Server_Alert_1683571299.notify create mode 100644 api/dev/notifications/archive/Unraid_Server_Alert_1683571468.notify create mode 100644 api/dev/notifications/archive/Unraid_Server_Alert_1683571535.notify create mode 100644 api/dev/notifications/archive/Unraid_Server_Alert_1683571565.notify create mode 100644 api/dev/notifications/archive/Unraid_Status_1683260401.notify create mode 100644 api/dev/notifications/archive/Unraid_Status_1683346801.notify create mode 100644 api/dev/notifications/archive/Unraid_Status_1683433201.notify create mode 100644 api/dev/notifications/archive/Unraid_Status_1683519601.notify create mode 100644 api/dev/notifications/archive/Unraid_Status_1683606001.notify create mode 100644 api/dev/notifications/archive/Unraid_Status_1683692401.notify create mode 100644 api/dev/notifications/archive/Unraid_Status_1683778802.notify create mode 100644 api/dev/notifications/archive/Unraid_Status_1683865201.notify create mode 100644 api/dev/notifications/archive/Unraid_Status_1683951601.notify create mode 100644 api/dev/notifications/archive/Unraid_Status_1684038001.notify create mode 100644 api/dev/notifications/archive/Unraid_Status_1684124401.notify create mode 100644 api/dev/notifications/unread/Unraid_Parity_check_1683971161.notify create mode 100644 api/dev/notifications/unread/Unraid_Server_Alert_1683570896.notify create mode 100644 api/dev/notifications/unread/Unraid_Server_Alert_1683570945.notify create mode 100644 api/dev/notifications/unread/Unraid_Server_Alert_1683570980.notify create mode 100644 api/dev/notifications/unread/Unraid_Server_Alert_1683571299.notify create mode 100644 api/dev/notifications/unread/Unraid_Server_Alert_1683571468.notify create mode 100644 api/dev/notifications/unread/Unraid_Server_Alert_1683571535.notify create mode 100644 api/dev/notifications/unread/Unraid_Server_Alert_1683571565.notify create mode 100644 api/dev/notifications/unread/Unraid_Status_1683260401.notify create mode 100644 api/dev/notifications/unread/Unraid_Status_1683346801.notify create mode 100644 api/dev/notifications/unread/Unraid_Status_1683433201.notify create mode 100644 api/dev/notifications/unread/Unraid_Status_1683519601.notify create mode 100644 api/dev/notifications/unread/Unraid_Status_1683606001.notify create mode 100644 api/dev/notifications/unread/Unraid_Status_1683692401.notify create mode 100644 api/dev/notifications/unread/Unraid_Status_1683778802.notify create mode 100644 api/dev/notifications/unread/Unraid_Status_1683865201.notify create mode 100644 api/dev/notifications/unread/Unraid_Status_1683951601.notify create mode 100644 api/dev/notifications/unread/Unraid_Status_1684038001.notify create mode 100644 api/dev/notifications/unread/Unraid_Status_1684124401.notify create mode 100644 api/docker-compose.yml create mode 100644 api/src/__test__/core/__snapshots__/default-permissions.test.ts.snap create mode 100644 api/src/__test__/store/modules/__snapshots__/notifications.test.ts.snap create mode 100644 api/src/__test__/store/modules/notifications.test.ts create mode 100644 api/src/__test__/store/state-parsers/var.test.ts create mode 100644 api/src/core/default-permissions.ts create mode 100644 api/src/core/log.ts create mode 100644 api/src/core/modules/notifications/setup-notification-watch.ts create mode 100644 api/src/core/pubsub.ts create mode 100644 api/src/core/types/ini.ts create mode 100755 api/src/graphql/generated/api/operations.ts rename api/src/graphql/{generate => generated/client}/validators.ts (100%) create mode 100644 api/src/graphql/resolvers/query/notifications.ts create mode 100644 api/src/graphql/resolvers/subscription/index.ts create mode 100644 api/src/graphql/schema/types/notifications/notifications.graphql create mode 100644 api/src/store/actions/load-dynamix-config-file.ts create mode 100644 api/src/store/listeners/listener-middleware.ts create mode 100644 api/src/store/listeners/notification-path-listener.ts create mode 100644 api/src/store/modules/notifications.ts create mode 100644 api/src/store/store-sync.ts diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 493fe3538..c903af6a4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -78,29 +78,13 @@ jobs: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - - name: Install node - uses: actions/setup-node@v3 - with: - node-version-file: "api/.nvmrc" + - name: Build Docker Compose + run: | + docker network create mothership_default + docker-compose build builder - # - name: Get npm cache directory - # id: npm-cache - # run: echo "::set-output name=dir::$(npm config get cache)" - - # - name: Load npm cache - # uses: actions/cache@v3 - # with: - # path: ${{ steps.npm-cache.outputs.dir }} - # key: ${{ runner.os }}-npm-cache-${{ hashFiles('**/package-lock.json') }} - - - name: Install libvirt-dev - run: sudo apt-get update && sudo apt-get install libvirt-dev - - - name: Installing node deps - run: npm install - - - name: Run unit tests with code coverage - run: npm run coverage + - name: Run Docker Compose + run: docker-compose run builder npm run coverage build-api: defaults: diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 08d604744..c6bf4ae25 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -66,26 +66,12 @@ jobs: git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - - name: Install node - uses: actions/setup-node@v3 - with: - node-version-file: "api/.nvmrc" - - # - name: Cache dependencies - # uses: actions/cache@v2 - # with: - # path: ~/.npm - # key: npm-${{ hashFiles('package-lock.json') }} - # restore-keys: npm- - - - name: Install libvirt-dev - run: sudo apt-get update && sudo apt-get install libvirt-dev - - - name: Installing node deps - run: npm ci - - - name: Run unit tests with code coverage - run: npm run coverage + - name: Build Docker Compose + run: | + docker network create mothership_default + docker-compose build builder + - name: Run Docker Compose + run: docker-compose run builder npm run coverage build-api: defaults: diff --git a/api/codegen.yml b/api/codegen.yml index 539b8cb1d..bc83e71f2 100644 --- a/api/codegen.yml +++ b/api/codegen.yml @@ -31,18 +31,7 @@ generates: withObjectType: true plugins: - add: { content: '/* eslint-disable */' } - src/graphql/generate/validators.ts: - schema: - '${MOTHERSHIP_GRAPHQL_LINK}': - headers: - origin: 'https://forums.unraid.net' - plugins: - - typescript-validation-schema - - add: { content: '/* eslint-disable */'} - config: - importFrom: '@app/graphql/generated/client/graphql' - strictScalars: false - schema: 'zod' + # Generate Types for the API Server src/graphql/generated/api/types.ts: schema: @@ -65,6 +54,24 @@ generates: presetConfig: typesPath: '@app/graphql/generated/api/types' plugins: + - typescript-validation-schema - typescript-operations - typed-document-node - add: { content: '/* eslint-disable */' } + config: + importFrom: '@app/graphql/generated/api/types' + strictScalars: false + schema: 'zod' + withObjectType: true + src/graphql/generated/client/validators.ts: + schema: + '${MOTHERSHIP_GRAPHQL_LINK}': + headers: + origin: 'https://forums.unraid.net' + plugins: + - typescript-validation-schema + - add: { content: '/* eslint-disable */'} + config: + importFrom: '@app/graphql/generated/client/graphql' + strictScalars: false + schema: 'zod' diff --git a/api/dev/Unraid.net/myservers.cfg b/api/dev/Unraid.net/myservers.cfg index 6abaa4070..769b89710 100644 --- a/api/dev/Unraid.net/myservers.cfg +++ b/api/dev/Unraid.net/myservers.cfg @@ -1,5 +1,5 @@ [api] -version="3.1.0+948d5ecf" +version="3.1.1+bf51f1f0" [local] [notifier] apikey="unnotify_30994bfaccf839c65bae75f7fa12dd5ee16e69389f754c3b98ed7d5" diff --git a/api/dev/data/permissions.json b/api/dev/data/permissions.json new file mode 100644 index 000000000..b35ffb587 --- /dev/null +++ b/api/dev/data/permissions.json @@ -0,0 +1,191 @@ +{ + "admin": { + "extends": "user", + "permissions": [ + { + "resource": "apikey", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "array", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "cpu", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "device", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "device/unassigned", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "disk", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "disk/settings", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "display", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "docker/container", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "docker/network", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "info", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "license-key", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "machine-id", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "memory", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "notifications", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "online", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "os", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "parity-history", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "permission", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "servers", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "service", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "service/emhttpd", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "service/unraid-api", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "services", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "share", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "software-versions", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "unraid-version", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "user", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "var", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "vars", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "vm/domain", + "action": "read:any", + "attributes": "*" + }, + { + "resource": "vm/network", + "action": "read:any", + "attributes": "*" + } + ] + }, + "user": { + "extends": "guest", + "permissions": [ + { + "resource": "apikey", + "action": "read:own", + "attributes": "*" + }, + { + "resource": "permission", + "action": "read:any", + "attributes": "*" + } + ] + }, + "guest": { + "permissions": [ + { + "resource": "welcome", + "action": "read:any", + "attributes": "*" + } + ] + } +} \ No newline at end of file diff --git a/api/dev/dynamix/dynamix.cfg b/api/dev/dynamix/dynamix.cfg new file mode 100644 index 000000000..86fc75cfb --- /dev/null +++ b/api/dev/dynamix/dynamix.cfg @@ -0,0 +1,35 @@ +[display] +date="%c" +number=".," +scale="-1" +tabs="1" +users="Tasks:3" +resize="0" +wwn="0" +total="1" +usage="0" +banner="image" +dashapps="icons" +theme="white" +text="1" +unit="C" +warning="70" +critical="90" +hot="45" +max="55" +sysinfo="/Tools/SystemProfiler" +[notify] +entity="1" +normal="1" +warning="1" +alert="1" +unraid="1" +plugin="1" +docker_notify="1" +report="1" +display="0" +date="d-m-Y" +time="H:i" +position="top-right" +path="/app/dev/notifications" +system="*/1 * * * *" \ No newline at end of file diff --git a/api/dev/notifications/archive/Appdata_Backup_1683699325.notify b/api/dev/notifications/archive/Appdata_Backup_1683699325.notify new file mode 100644 index 000000000..259de6a79 --- /dev/null +++ b/api/dev/notifications/archive/Appdata_Backup_1683699325.notify @@ -0,0 +1,6 @@ +timestamp=1683699325 +event=Appdata Backup +subject=Warning +description=Please check the backup log tab! +importance=warning +message=Container did not started! - Code: Server error diff --git a/api/dev/notifications/archive/Community_Applications_1683223240.notify b/api/dev/notifications/archive/Community_Applications_1683223240.notify new file mode 100644 index 000000000..3f41bab5f --- /dev/null +++ b/api/dev/notifications/archive/Community_Applications_1683223240.notify @@ -0,0 +1,5 @@ +timestamp=1683223240 +event=Community Applications +subject=Application Auto Update +description=dynamix.unraid.net.staging.plg Automatically Updated +importance=normal diff --git a/api/dev/notifications/archive/Community_Applications_1683568807.notify b/api/dev/notifications/archive/Community_Applications_1683568807.notify new file mode 100644 index 000000000..2edeeb39b --- /dev/null +++ b/api/dev/notifications/archive/Community_Applications_1683568807.notify @@ -0,0 +1,5 @@ +timestamp=1683568807 +event=Community Applications +subject=Application Auto Update +description=community.applications.plg Automatically Updated +importance=normal diff --git a/api/dev/notifications/archive/Community_Applications_1683655211.notify b/api/dev/notifications/archive/Community_Applications_1683655211.notify new file mode 100644 index 000000000..0a21df280 --- /dev/null +++ b/api/dev/notifications/archive/Community_Applications_1683655211.notify @@ -0,0 +1,5 @@ +timestamp=1683655211 +event=Community Applications +subject=Application Auto Update +description=parity.check.tuning.plg Automatically Updated +importance=normal diff --git a/api/dev/notifications/archive/Community_Applications_1683741604.notify b/api/dev/notifications/archive/Community_Applications_1683741604.notify new file mode 100644 index 000000000..98095d354 --- /dev/null +++ b/api/dev/notifications/archive/Community_Applications_1683741604.notify @@ -0,0 +1,5 @@ +timestamp=1683741604 +event=Community Applications +subject=Application Auto Update +description=appdata.backup.plg Automatically Updated +importance=normal diff --git a/api/dev/notifications/archive/Docker___AirConnect_3d53..ad24_ticket.notify b/api/dev/notifications/archive/Docker___AirConnect_3d53..ad24_ticket.notify new file mode 100644 index 000000000..56da0c7c7 --- /dev/null +++ b/api/dev/notifications/archive/Docker___AirConnect_3d53..ad24_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683951054 +event=Docker - AirConnect [3d53..ad24] +subject=Notice [UNRAID] - Version update 3d53..ad24 +description=A new version of AirConnect is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___AirConnect_91bd..76e7_ticket.notify b/api/dev/notifications/archive/Docker___AirConnect_91bd..76e7_ticket.notify new file mode 100644 index 000000000..7d13fc149 --- /dev/null +++ b/api/dev/notifications/archive/Docker___AirConnect_91bd..76e7_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683605449 +event=Docker - AirConnect [91bd..76e7] +subject=Notice [UNRAID] - Version update 91bd..76e7 +description=A new version of AirConnect is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___AirConnect_ca7f..6b17_ticket.notify b/api/dev/notifications/archive/Docker___AirConnect_ca7f..6b17_ticket.notify new file mode 100644 index 000000000..a1175c7c9 --- /dev/null +++ b/api/dev/notifications/archive/Docker___AirConnect_ca7f..6b17_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683346254 +event=Docker - AirConnect [ca7f..6b17] +subject=Notice [UNRAID] - Version update ca7f..6b17 +description=A new version of AirConnect is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___PhotoPrism_295d..2ed1_ticket.notify b/api/dev/notifications/archive/Docker___PhotoPrism_295d..2ed1_ticket.notify new file mode 100644 index 000000000..45d8d3b3d --- /dev/null +++ b/api/dev/notifications/archive/Docker___PhotoPrism_295d..2ed1_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683432654 +event=Docker - PhotoPrism [295d..2ed1] +subject=Notice [UNRAID] - Version update 295d..2ed1 +description=A new version of PhotoPrism is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___PhotoPrism_906c..d43c_ticket.notify b/api/dev/notifications/archive/Docker___PhotoPrism_906c..d43c_ticket.notify new file mode 100644 index 000000000..73cb97284 --- /dev/null +++ b/api/dev/notifications/archive/Docker___PhotoPrism_906c..d43c_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1684037454 +event=Docker - PhotoPrism [906c..d43c] +subject=Notice [UNRAID] - Version update 906c..d43c +description=A new version of PhotoPrism is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___binhex_prowlarr_1126..5559_ticket.notify b/api/dev/notifications/archive/Docker___binhex_prowlarr_1126..5559_ticket.notify new file mode 100644 index 000000000..a43f777a7 --- /dev/null +++ b/api/dev/notifications/archive/Docker___binhex_prowlarr_1126..5559_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683346254 +event=Docker - binhex-prowlarr [1126..5559] +subject=Notice [UNRAID] - Version update 1126..5559 +description=A new version of binhex-prowlarr is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___caddy_custom_024e..04d1_ticket.notify b/api/dev/notifications/archive/Docker___caddy_custom_024e..04d1_ticket.notify new file mode 100644 index 000000000..44e77b826 --- /dev/null +++ b/api/dev/notifications/archive/Docker___caddy_custom_024e..04d1_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683778254 +event=Docker - caddy-custom [024e..04d1] +subject=Notice [UNRAID] - Version update 024e..04d1 +description=A new version of caddy-custom is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___caddy_custom_18da..c9bd_ticket.notify b/api/dev/notifications/archive/Docker___caddy_custom_18da..c9bd_ticket.notify new file mode 100644 index 000000000..1f35ce9d1 --- /dev/null +++ b/api/dev/notifications/archive/Docker___caddy_custom_18da..c9bd_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1684037454 +event=Docker - caddy-custom [18da..c9bd] +subject=Notice [UNRAID] - Version update 18da..c9bd +description=A new version of caddy-custom is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___caddy_custom_3005..022f_ticket.notify b/api/dev/notifications/archive/Docker___caddy_custom_3005..022f_ticket.notify new file mode 100644 index 000000000..2eae81809 --- /dev/null +++ b/api/dev/notifications/archive/Docker___caddy_custom_3005..022f_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683864655 +event=Docker - caddy-custom [3005..022f] +subject=Notice [UNRAID] - Version update 3005..022f +description=A new version of caddy-custom is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___caddy_custom_7c36..343c_ticket.notify b/api/dev/notifications/archive/Docker___caddy_custom_7c36..343c_ticket.notify new file mode 100644 index 000000000..a86a5fe91 --- /dev/null +++ b/api/dev/notifications/archive/Docker___caddy_custom_7c36..343c_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683259850 +event=Docker - caddy-custom [7c36..343c] +subject=Notice [UNRAID] - Version update 7c36..343c +description=A new version of caddy-custom is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___caddy_custom_7f52..acca_ticket.notify b/api/dev/notifications/archive/Docker___caddy_custom_7f52..acca_ticket.notify new file mode 100644 index 000000000..7437727b9 --- /dev/null +++ b/api/dev/notifications/archive/Docker___caddy_custom_7f52..acca_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683605449 +event=Docker - caddy-custom [7f52..acca] +subject=Notice [UNRAID] - Version update 7f52..acca +description=A new version of caddy-custom is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___caddy_custom_9f05..b531_ticket.notify b/api/dev/notifications/archive/Docker___caddy_custom_9f05..b531_ticket.notify new file mode 100644 index 000000000..3030c8a31 --- /dev/null +++ b/api/dev/notifications/archive/Docker___caddy_custom_9f05..b531_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1684123859 +event=Docker - caddy-custom [9f05..b531] +subject=Notice [UNRAID] - Version update 9f05..b531 +description=A new version of caddy-custom is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___caddy_custom_cf91..b716_ticket.notify b/api/dev/notifications/archive/Docker___caddy_custom_cf91..b716_ticket.notify new file mode 100644 index 000000000..269f4ee06 --- /dev/null +++ b/api/dev/notifications/archive/Docker___caddy_custom_cf91..b716_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683519032 +event=Docker - caddy-custom [cf91..b716] +subject=Notice [UNRAID] - Version update cf91..b716 +description=A new version of caddy-custom is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___caddy_custom_d851..8877_ticket.notify b/api/dev/notifications/archive/Docker___caddy_custom_d851..8877_ticket.notify new file mode 100644 index 000000000..e2c07b8b3 --- /dev/null +++ b/api/dev/notifications/archive/Docker___caddy_custom_d851..8877_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683691842 +event=Docker - caddy-custom [d851..8877] +subject=Notice [UNRAID] - Version update d851..8877 +description=A new version of caddy-custom is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___caddy_custom_dfd0..0e5d_ticket.notify b/api/dev/notifications/archive/Docker___caddy_custom_dfd0..0e5d_ticket.notify new file mode 100644 index 000000000..5a6af3367 --- /dev/null +++ b/api/dev/notifications/archive/Docker___caddy_custom_dfd0..0e5d_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683432654 +event=Docker - caddy-custom [dfd0..0e5d] +subject=Notice [UNRAID] - Version update dfd0..0e5d +description=A new version of caddy-custom is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___caddy_custom_e6c6..fbb4_ticket.notify b/api/dev/notifications/archive/Docker___caddy_custom_e6c6..fbb4_ticket.notify new file mode 100644 index 000000000..315e8090e --- /dev/null +++ b/api/dev/notifications/archive/Docker___caddy_custom_e6c6..fbb4_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683346254 +event=Docker - caddy-custom [e6c6..fbb4] +subject=Notice [UNRAID] - Version update e6c6..fbb4 +description=A new version of caddy-custom is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___caddy_custom_fcc0..f692_ticket.notify b/api/dev/notifications/archive/Docker___caddy_custom_fcc0..f692_ticket.notify new file mode 100644 index 000000000..e65629ee0 --- /dev/null +++ b/api/dev/notifications/archive/Docker___caddy_custom_fcc0..f692_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683951054 +event=Docker - caddy-custom [fcc0..f692] +subject=Notice [UNRAID] - Version update fcc0..f692 +description=A new version of caddy-custom is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___filestash_6b94..7dd4_ticket.notify b/api/dev/notifications/archive/Docker___filestash_6b94..7dd4_ticket.notify new file mode 100644 index 000000000..aa6cb0fa7 --- /dev/null +++ b/api/dev/notifications/archive/Docker___filestash_6b94..7dd4_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683864655 +event=Docker - filestash [6b94..7dd4] +subject=Notice [UNRAID] - Version update 6b94..7dd4 +description=A new version of filestash is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___jellyfin_2902..af01_ticket.notify b/api/dev/notifications/archive/Docker___jellyfin_2902..af01_ticket.notify new file mode 100644 index 000000000..d43579f31 --- /dev/null +++ b/api/dev/notifications/archive/Docker___jellyfin_2902..af01_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683691842 +event=Docker - jellyfin [2902..af01] +subject=Notice [UNRAID] - Version update 2902..af01 +description=A new version of jellyfin is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___mariadb_45af..47d4_ticket.notify b/api/dev/notifications/archive/Docker___mariadb_45af..47d4_ticket.notify new file mode 100644 index 000000000..7e1dbd615 --- /dev/null +++ b/api/dev/notifications/archive/Docker___mariadb_45af..47d4_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683951054 +event=Docker - mariadb [45af..47d4] +subject=Notice [UNRAID] - Version update 45af..47d4 +description=A new version of mariadb is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___mariadb_e036..3e8e_ticket.notify b/api/dev/notifications/archive/Docker___mariadb_e036..3e8e_ticket.notify new file mode 100644 index 000000000..b3f39799a --- /dev/null +++ b/api/dev/notifications/archive/Docker___mariadb_e036..3e8e_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683691842 +event=Docker - mariadb [e036..3e8e] +subject=Notice [UNRAID] - Version update e036..3e8e +description=A new version of mariadb is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___plex_7bfa..19fb_ticket.notify b/api/dev/notifications/archive/Docker___plex_7bfa..19fb_ticket.notify new file mode 100644 index 000000000..dd4e417c9 --- /dev/null +++ b/api/dev/notifications/archive/Docker___plex_7bfa..19fb_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683605449 +event=Docker - plex [7bfa..19fb] +subject=Notice [UNRAID] - Version update 7bfa..19fb +description=A new version of plex is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___qbittorrent_43b9..9adc_ticket.notify b/api/dev/notifications/archive/Docker___qbittorrent_43b9..9adc_ticket.notify new file mode 100644 index 000000000..38e702c5f --- /dev/null +++ b/api/dev/notifications/archive/Docker___qbittorrent_43b9..9adc_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683605449 +event=Docker - qbittorrent [43b9..9adc] +subject=Notice [UNRAID] - Version update 43b9..9adc +description=A new version of qbittorrent is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___radarr_4k_62ca..1a81_ticket.notify b/api/dev/notifications/archive/Docker___radarr_4k_62ca..1a81_ticket.notify new file mode 100644 index 000000000..9f1cdbeb4 --- /dev/null +++ b/api/dev/notifications/archive/Docker___radarr_4k_62ca..1a81_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683519032 +event=Docker - radarr-4k [62ca..1a81] +subject=Notice [UNRAID] - Version update 62ca..1a81 +description=A new version of radarr-4k is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___radarr_62ca..1a81_ticket.notify b/api/dev/notifications/archive/Docker___radarr_62ca..1a81_ticket.notify new file mode 100644 index 000000000..e586e2ec3 --- /dev/null +++ b/api/dev/notifications/archive/Docker___radarr_62ca..1a81_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683519032 +event=Docker - radarr [62ca..1a81] +subject=Notice [UNRAID] - Version update 62ca..1a81 +description=A new version of radarr is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___rtorrent_rutorrent_a16c..af8b_ticket.notify b/api/dev/notifications/archive/Docker___rtorrent_rutorrent_a16c..af8b_ticket.notify new file mode 100644 index 000000000..83126edf8 --- /dev/null +++ b/api/dev/notifications/archive/Docker___rtorrent_rutorrent_a16c..af8b_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1684037454 +event=Docker - rtorrent-rutorrent [a16c..af8b] +subject=Notice [UNRAID] - Version update a16c..af8b +description=A new version of rtorrent-rutorrent is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___rtorrent_rutorrent_b722..38c5_ticket.notify b/api/dev/notifications/archive/Docker___rtorrent_rutorrent_b722..38c5_ticket.notify new file mode 100644 index 000000000..9edcc86df --- /dev/null +++ b/api/dev/notifications/archive/Docker___rtorrent_rutorrent_b722..38c5_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683519032 +event=Docker - rtorrent-rutorrent [b722..38c5] +subject=Notice [UNRAID] - Version update b722..38c5 +description=A new version of rtorrent-rutorrent is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___sonarr_12c7..6b53_ticket.notify b/api/dev/notifications/archive/Docker___sonarr_12c7..6b53_ticket.notify new file mode 100644 index 000000000..b15520aa7 --- /dev/null +++ b/api/dev/notifications/archive/Docker___sonarr_12c7..6b53_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683432654 +event=Docker - sonarr [12c7..6b53] +subject=Notice [UNRAID] - Version update 12c7..6b53 +description=A new version of sonarr is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___sonarr_12c9..01df_ticket.notify b/api/dev/notifications/archive/Docker___sonarr_12c9..01df_ticket.notify new file mode 100644 index 000000000..29cf9a3b4 --- /dev/null +++ b/api/dev/notifications/archive/Docker___sonarr_12c9..01df_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1684123859 +event=Docker - sonarr [12c9..01df] +subject=Notice [UNRAID] - Version update 12c9..01df +description=A new version of sonarr is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___sonarr_27f9..c81b_ticket.notify b/api/dev/notifications/archive/Docker___sonarr_27f9..c81b_ticket.notify new file mode 100644 index 000000000..5867bf010 --- /dev/null +++ b/api/dev/notifications/archive/Docker___sonarr_27f9..c81b_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683605449 +event=Docker - sonarr [27f9..c81b] +subject=Notice [UNRAID] - Version update 27f9..c81b +description=A new version of sonarr is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___sonarr_354f..3ed7_ticket.notify b/api/dev/notifications/archive/Docker___sonarr_354f..3ed7_ticket.notify new file mode 100644 index 000000000..3b6fd79c8 --- /dev/null +++ b/api/dev/notifications/archive/Docker___sonarr_354f..3ed7_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1684037454 +event=Docker - sonarr [354f..3ed7] +subject=Notice [UNRAID] - Version update 354f..3ed7 +description=A new version of sonarr is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___sonarr_6145..3ba7_ticket.notify b/api/dev/notifications/archive/Docker___sonarr_6145..3ba7_ticket.notify new file mode 100644 index 000000000..b1ebf3294 --- /dev/null +++ b/api/dev/notifications/archive/Docker___sonarr_6145..3ba7_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683864655 +event=Docker - sonarr [6145..3ba7] +subject=Notice [UNRAID] - Version update 6145..3ba7 +description=A new version of sonarr is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___sonarr_b2f0..9f04_ticket.notify b/api/dev/notifications/archive/Docker___sonarr_b2f0..9f04_ticket.notify new file mode 100644 index 000000000..fdcfbf689 --- /dev/null +++ b/api/dev/notifications/archive/Docker___sonarr_b2f0..9f04_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683691842 +event=Docker - sonarr [b2f0..9f04] +subject=Notice [UNRAID] - Version update b2f0..9f04 +description=A new version of sonarr is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___sonarr_bcfd..b7bd_ticket.notify b/api/dev/notifications/archive/Docker___sonarr_bcfd..b7bd_ticket.notify new file mode 100644 index 000000000..acc698331 --- /dev/null +++ b/api/dev/notifications/archive/Docker___sonarr_bcfd..b7bd_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683951054 +event=Docker - sonarr [bcfd..b7bd] +subject=Notice [UNRAID] - Version update bcfd..b7bd +description=A new version of sonarr is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___sonarr_c04c..6b30_ticket.notify b/api/dev/notifications/archive/Docker___sonarr_c04c..6b30_ticket.notify new file mode 100644 index 000000000..0c9241355 --- /dev/null +++ b/api/dev/notifications/archive/Docker___sonarr_c04c..6b30_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683519032 +event=Docker - sonarr [c04c..6b30] +subject=Notice [UNRAID] - Version update c04c..6b30 +description=A new version of sonarr is available +importance=normal diff --git a/api/dev/notifications/archive/Docker___synclounge_38f9..8503_ticket.notify b/api/dev/notifications/archive/Docker___synclounge_38f9..8503_ticket.notify new file mode 100644 index 000000000..a69fb66bf --- /dev/null +++ b/api/dev/notifications/archive/Docker___synclounge_38f9..8503_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683691842 +event=Docker - synclounge [38f9..8503] +subject=Notice [UNRAID] - Version update 38f9..8503 +description=A new version of synclounge is available +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683212331.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683212331.notify new file mode 100644 index 000000000..397fe03f2 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683212331.notify @@ -0,0 +1,5 @@ +timestamp=1683212331 +event=Parity Check Tuning +subject=[UNRAID] Array operation will not be restarted +description=Unclean shutdown detected +importance=alert diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683212357.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683212357.notify new file mode 100644 index 000000000..8699ece07 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683212357.notify @@ -0,0 +1,5 @@ +timestamp=1683212357 +event=Parity Check Tuning +subject=[UNRAID] Automatic unRaid Parity-Check will be started +description=Unclean shutdown detected +importance=warning diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683212414.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683212414.notify new file mode 100644 index 000000000..c6c2d87d6 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683212414.notify @@ -0,0 +1,5 @@ +timestamp=1683212414 +event=Parity Check Tuning +subject=[UNRAID] Paused +description=Automatic Correcting Parity-Check (0.0% completed) +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683212430.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683212430.notify new file mode 100644 index 000000000..a8a147c11 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683212430.notify @@ -0,0 +1,5 @@ +timestamp=1683212430 +event=Parity Check Tuning +subject=[UNRAID] Paused +description=No array operation in progress (0.0% completed) +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683696603.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683696603.notify new file mode 100644 index 000000000..412a488f3 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683696603.notify @@ -0,0 +1,5 @@ +timestamp=1683696603 +event=Parity Check Tuning +subject=[UNRAID] Resumed +description=Automatic Correcting Parity-Check (0.0% completed) +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683699512.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683699512.notify new file mode 100644 index 000000000..d6692e640 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683699512.notify @@ -0,0 +1,5 @@ +timestamp=1683699512 +event=Parity Check Tuning +subject=[UNRAID] Backup running +description=No description +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683699523.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683699523.notify new file mode 100644 index 000000000..a66c5c123 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683699523.notify @@ -0,0 +1,5 @@ +timestamp=1683699523 +event=Parity Check Tuning +subject=[UNRAID] Paused +description=Automatic Correcting Parity-Check (2.7% completed) +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683700590.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683700590.notify new file mode 100644 index 000000000..384cbe6f4 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683700590.notify @@ -0,0 +1,5 @@ +timestamp=1683700590 +event=Parity Check Tuning +subject=[UNRAID] Backup no longer running +description=No description +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683700596.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683700596.notify new file mode 100644 index 000000000..6550f11d1 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683700596.notify @@ -0,0 +1,5 @@ +timestamp=1683700596 +event=Parity Check Tuning +subject=[UNRAID] Resumed +description=Automatic Correcting Parity-Check (2.7% completed) +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683730812.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683730812.notify new file mode 100644 index 000000000..b276b9666 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683730812.notify @@ -0,0 +1,5 @@ +timestamp=1683730812 +event=Parity Check Tuning +subject=[UNRAID] Paused +description=Automatic Correcting Parity-Check (32.6% completed) +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683783001.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683783001.notify new file mode 100644 index 000000000..7f17f297b --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683783001.notify @@ -0,0 +1,5 @@ +timestamp=1683783001 +event=Parity Check Tuning +subject=[UNRAID] Resumed +description=Automatic Correcting Parity-Check (32.6% completed) +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683817213.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683817213.notify new file mode 100644 index 000000000..221596b96 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683817213.notify @@ -0,0 +1,5 @@ +timestamp=1683817213 +event=Parity Check Tuning +subject=[UNRAID] Paused +description=Automatic Correcting Parity-Check (59.2% completed) +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683869402.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683869402.notify new file mode 100644 index 000000000..a50b612e0 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683869402.notify @@ -0,0 +1,5 @@ +timestamp=1683869402 +event=Parity Check Tuning +subject=[UNRAID] Resumed +description=Automatic Correcting Parity-Check (59.2% completed) +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683903615.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683903615.notify new file mode 100644 index 000000000..9ca26416a --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683903615.notify @@ -0,0 +1,5 @@ +timestamp=1683903615 +event=Parity Check Tuning +subject=[UNRAID] Paused +description=Automatic Correcting Parity-Check (88.1% completed) +importance=normal diff --git a/api/dev/notifications/archive/Parity_Check_Tuning_1683955802.notify b/api/dev/notifications/archive/Parity_Check_Tuning_1683955802.notify new file mode 100644 index 000000000..0a18df148 --- /dev/null +++ b/api/dev/notifications/archive/Parity_Check_Tuning_1683955802.notify @@ -0,0 +1,5 @@ +timestamp=1683955802 +event=Parity Check Tuning +subject=[UNRAID] Resumed +description=Automatic Correcting Parity-Check (88.1% completed) +importance=normal diff --git a/api/dev/notifications/archive/Plugin___appdata.backup_2023.05.09_ticket.notify b/api/dev/notifications/archive/Plugin___appdata.backup_2023.05.09_ticket.notify new file mode 100644 index 000000000..b6e9e75ac --- /dev/null +++ b/api/dev/notifications/archive/Plugin___appdata.backup_2023.05.09_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683691803 +event=Plugin - appdata.backup [2023.05.09] +subject=Notice [UNRAID] - Version update 2023.05.09 +description=A new version of appdata.backup is available +importance=normal diff --git a/api/dev/notifications/archive/Plugin___community.applications_2023.05.07a_ticket.notify b/api/dev/notifications/archive/Plugin___community.applications_2023.05.07a_ticket.notify new file mode 100644 index 000000000..63debb0a4 --- /dev/null +++ b/api/dev/notifications/archive/Plugin___community.applications_2023.05.07a_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683519002 +event=Plugin - community.applications [2023.05.07a] +subject=Notice [UNRAID] - Version update 2023.05.07a +description=A new version of community.applications is available +importance=normal diff --git a/api/dev/notifications/archive/Plugin___parity.check.tuning_2023.05.07_ticket.notify b/api/dev/notifications/archive/Plugin___parity.check.tuning_2023.05.07_ticket.notify new file mode 100644 index 000000000..17ebc047e --- /dev/null +++ b/api/dev/notifications/archive/Plugin___parity.check.tuning_2023.05.07_ticket.notify @@ -0,0 +1,5 @@ +timestamp=1683519003 +event=Plugin - parity.check.tuning [2023.05.07] +subject=Notice [UNRAID] - Version update 2023.05.07 +description=A new version of parity.check.tuning is available +importance=normal diff --git a/api/dev/notifications/archive/Remote_Access_Started_1683325569.notify b/api/dev/notifications/archive/Remote_Access_Started_1683325569.notify new file mode 100644 index 000000000..400ccc120 --- /dev/null +++ b/api/dev/notifications/archive/Remote_Access_Started_1683325569.notify @@ -0,0 +1,5 @@ +timestamp=1683325569 +event=Remote Access Started +subject=Unraid API +description=Remote access has been started +importance=normal diff --git a/api/dev/notifications/archive/Remote_Access_Started_1683325597.notify b/api/dev/notifications/archive/Remote_Access_Started_1683325597.notify new file mode 100644 index 000000000..5d3529ca7 --- /dev/null +++ b/api/dev/notifications/archive/Remote_Access_Started_1683325597.notify @@ -0,0 +1,5 @@ +timestamp=1683325597 +event=Remote Access Started +subject=Unraid API +description=Remote access has been started +importance=normal diff --git a/api/dev/notifications/archive/Remote_Access_Started_1683383543.notify b/api/dev/notifications/archive/Remote_Access_Started_1683383543.notify new file mode 100644 index 000000000..6db6f16c6 --- /dev/null +++ b/api/dev/notifications/archive/Remote_Access_Started_1683383543.notify @@ -0,0 +1,5 @@ +timestamp=1683383543 +event=Remote Access Started +subject=Unraid API +description=Remote access has been started +importance=normal diff --git a/api/dev/notifications/archive/Remote_Access_Stopped_1683397158.notify b/api/dev/notifications/archive/Remote_Access_Stopped_1683397158.notify new file mode 100644 index 000000000..4b93c68a3 --- /dev/null +++ b/api/dev/notifications/archive/Remote_Access_Stopped_1683397158.notify @@ -0,0 +1,5 @@ +timestamp=1683397158 +event=Remote Access Stopped +subject=Unraid API +description=Remote access has been stopped +importance=normal diff --git a/api/dev/notifications/archive/Remote_Access_Stopped_1683422667.notify b/api/dev/notifications/archive/Remote_Access_Stopped_1683422667.notify new file mode 100644 index 000000000..133fde8a1 --- /dev/null +++ b/api/dev/notifications/archive/Remote_Access_Stopped_1683422667.notify @@ -0,0 +1,5 @@ +timestamp=1683422667 +event=Remote Access Stopped +subject=Unraid API +description=Remote access has been stopped +importance=normal diff --git a/api/dev/notifications/archive/Remote_Access_Stopped_1683820748.notify b/api/dev/notifications/archive/Remote_Access_Stopped_1683820748.notify new file mode 100644 index 000000000..c527e325a --- /dev/null +++ b/api/dev/notifications/archive/Remote_Access_Stopped_1683820748.notify @@ -0,0 +1,5 @@ +timestamp=1683820748 +event=Remote Access Stopped +subject=Unraid API +description=Remote access has been stopped +importance=normal diff --git a/api/dev/notifications/archive/Remote_Access_Stopped_1683986670.notify b/api/dev/notifications/archive/Remote_Access_Stopped_1683986670.notify new file mode 100644 index 000000000..4cbe7aec0 --- /dev/null +++ b/api/dev/notifications/archive/Remote_Access_Stopped_1683986670.notify @@ -0,0 +1,5 @@ +timestamp=1683986670 +event=Remote Access Stopped +subject=Unraid API +description=Remote access has been stopped +importance=normal diff --git a/api/dev/notifications/archive/Remote_Access_Stopped_1684161220.notify b/api/dev/notifications/archive/Remote_Access_Stopped_1684161220.notify new file mode 100644 index 000000000..dc0cfbc7a --- /dev/null +++ b/api/dev/notifications/archive/Remote_Access_Stopped_1684161220.notify @@ -0,0 +1,5 @@ +timestamp=1684161220 +event=Remote Access Stopped +subject=Unraid API +description=Remote access has been stopped +importance=normal diff --git a/api/dev/notifications/archive/Unraid_Parity_check_1683971161.notify b/api/dev/notifications/archive/Unraid_Parity_check_1683971161.notify new file mode 100644 index 000000000..01a2d79ff --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Parity_check_1683971161.notify @@ -0,0 +1,5 @@ +timestamp=1683971161 +event=Unraid Parity check +subject=Notice [UNRAID] - Parity check finished (0 errors) +description=Canceled +importance=warning diff --git a/api/dev/notifications/archive/Unraid_Server_Alert_1683570896.notify b/api/dev/notifications/archive/Unraid_Server_Alert_1683570896.notify new file mode 100644 index 000000000..fe8d6cc9d --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Server_Alert_1683570896.notify @@ -0,0 +1,5 @@ +timestamp=1683570896 +event=Unraid Server Alert +subject=UPS Alert +description=Power failure on UPS Unraid. Running on batteries. +importance=alert diff --git a/api/dev/notifications/archive/Unraid_Server_Alert_1683570945.notify b/api/dev/notifications/archive/Unraid_Server_Alert_1683570945.notify new file mode 100644 index 000000000..9b3eba7c5 --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Server_Alert_1683570945.notify @@ -0,0 +1,5 @@ +timestamp=1683570945 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/archive/Unraid_Server_Alert_1683570980.notify b/api/dev/notifications/archive/Unraid_Server_Alert_1683570980.notify new file mode 100644 index 000000000..8ca7deb93 --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Server_Alert_1683570980.notify @@ -0,0 +1,5 @@ +timestamp=1683570980 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/archive/Unraid_Server_Alert_1683571299.notify b/api/dev/notifications/archive/Unraid_Server_Alert_1683571299.notify new file mode 100644 index 000000000..33116ab1d --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Server_Alert_1683571299.notify @@ -0,0 +1,5 @@ +timestamp=1683571299 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/archive/Unraid_Server_Alert_1683571468.notify b/api/dev/notifications/archive/Unraid_Server_Alert_1683571468.notify new file mode 100644 index 000000000..c5211ec1c --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Server_Alert_1683571468.notify @@ -0,0 +1,5 @@ +timestamp=1683571468 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/archive/Unraid_Server_Alert_1683571535.notify b/api/dev/notifications/archive/Unraid_Server_Alert_1683571535.notify new file mode 100644 index 000000000..e722286fc --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Server_Alert_1683571535.notify @@ -0,0 +1,5 @@ +timestamp=1683571535 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/archive/Unraid_Server_Alert_1683571565.notify b/api/dev/notifications/archive/Unraid_Server_Alert_1683571565.notify new file mode 100644 index 000000000..b975b56c2 --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Server_Alert_1683571565.notify @@ -0,0 +1,5 @@ +timestamp=1683571565 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/archive/Unraid_Status_1683260401.notify b/api/dev/notifications/archive/Unraid_Status_1683260401.notify new file mode 100644 index 000000000..c9ef8b79a --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Status_1683260401.notify @@ -0,0 +1,6 @@ +timestamp=1683260401 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 32 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 31 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 31 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 22 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 31 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 34 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 41 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 44 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (yesterday), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/archive/Unraid_Status_1683346801.notify b/api/dev/notifications/archive/Unraid_Status_1683346801.notify new file mode 100644 index 000000000..48e252299 --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Status_1683346801.notify @@ -0,0 +1,6 @@ +timestamp=1683346801 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 26 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 29 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 31 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 31 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 22 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 31 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 33 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 39 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 43 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (2 days ago), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/archive/Unraid_Status_1683433201.notify b/api/dev/notifications/archive/Unraid_Status_1683433201.notify new file mode 100644 index 000000000..44473d75f --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Status_1683433201.notify @@ -0,0 +1,6 @@ +timestamp=1683433201 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 26 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 29 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 32 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 32 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 23 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 31 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 32 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 39 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 43 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (3 days ago), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/archive/Unraid_Status_1683519601.notify b/api/dev/notifications/archive/Unraid_Status_1683519601.notify new file mode 100644 index 000000000..b546df63c --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Status_1683519601.notify @@ -0,0 +1,6 @@ +timestamp=1683519601 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 27 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 30 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 32 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 24 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 32 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 33 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 39 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 43 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (4 days ago), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/archive/Unraid_Status_1683606001.notify b/api/dev/notifications/archive/Unraid_Status_1683606001.notify new file mode 100644 index 000000000..59e64b900 --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Status_1683606001.notify @@ -0,0 +1,6 @@ +timestamp=1683606001 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 27 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 30 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 34 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 24 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 32 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 34 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 41 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 44 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (5 days ago), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/archive/Unraid_Status_1683692401.notify b/api/dev/notifications/archive/Unraid_Status_1683692401.notify new file mode 100644 index 000000000..661bcaadd --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Status_1683692401.notify @@ -0,0 +1,6 @@ +timestamp=1683692401 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 31 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 35 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 25 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 33 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 34 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 40 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 44 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (6 days ago), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/archive/Unraid_Status_1683778802.notify b/api/dev/notifications/archive/Unraid_Status_1683778802.notify new file mode 100644 index 000000000..1e292c9c4 --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Status_1683778802.notify @@ -0,0 +1,6 @@ +timestamp=1683778802 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 31 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 33 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 24 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 33 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 34 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 40 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 44 C [OK]

Last check incomplete on Wed 10 May 2023 11:00:07 AM EDT (yesterday), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/archive/Unraid_Status_1683865201.notify b/api/dev/notifications/archive/Unraid_Status_1683865201.notify new file mode 100644 index 000000000..c7bc5f901 --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Status_1683865201.notify @@ -0,0 +1,6 @@ +timestamp=1683865201 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 27 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 30 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 33 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 32 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 24 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 32 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 33 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 40 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 43 C [OK]

Last check incomplete on Thu 11 May 2023 11:00:07 AM EDT (yesterday), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/archive/Unraid_Status_1683951601.notify b/api/dev/notifications/archive/Unraid_Status_1683951601.notify new file mode 100644 index 000000000..a2620d565 --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Status_1683951601.notify @@ -0,0 +1,6 @@ +timestamp=1683951601 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 31 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 33 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 25 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 33 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 34 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 40 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 44 C [OK]

Last check incomplete on Fri 12 May 2023 11:00:08 AM EDT (yesterday), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/archive/Unraid_Status_1684038001.notify b/api/dev/notifications/archive/Unraid_Status_1684038001.notify new file mode 100644 index 000000000..962848b5d --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Status_1684038001.notify @@ -0,0 +1,6 @@ +timestamp=1684038001 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 31 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 33 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 25 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 33 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 35 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 41 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 45 C [OK]

Parity is valid
Last checked on Sat 13 May 2023 05:45:11 AM EDT (yesterday), finding 0 errors.
Duration: 4 hours, 15 minutes, 4 seconds. Average speed: 1.2 GB/s diff --git a/api/dev/notifications/archive/Unraid_Status_1684124401.notify b/api/dev/notifications/archive/Unraid_Status_1684124401.notify new file mode 100644 index 000000000..6e5d0a2ae --- /dev/null +++ b/api/dev/notifications/archive/Unraid_Status_1684124401.notify @@ -0,0 +1,6 @@ +timestamp=1684124401 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 31 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 33 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 34 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 25 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 33 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 35 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 41 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 45 C [OK]

Parity is valid
Last checked on Sat 13 May 2023 05:45:11 AM EDT (2 days ago), finding 0 errors.
Duration: 4 hours, 15 minutes, 4 seconds. Average speed: 1.2 GB/s diff --git a/api/dev/notifications/unread/Unraid_Parity_check_1683971161.notify b/api/dev/notifications/unread/Unraid_Parity_check_1683971161.notify new file mode 100644 index 000000000..01a2d79ff --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Parity_check_1683971161.notify @@ -0,0 +1,5 @@ +timestamp=1683971161 +event=Unraid Parity check +subject=Notice [UNRAID] - Parity check finished (0 errors) +description=Canceled +importance=warning diff --git a/api/dev/notifications/unread/Unraid_Server_Alert_1683570896.notify b/api/dev/notifications/unread/Unraid_Server_Alert_1683570896.notify new file mode 100644 index 000000000..fe8d6cc9d --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Server_Alert_1683570896.notify @@ -0,0 +1,5 @@ +timestamp=1683570896 +event=Unraid Server Alert +subject=UPS Alert +description=Power failure on UPS Unraid. Running on batteries. +importance=alert diff --git a/api/dev/notifications/unread/Unraid_Server_Alert_1683570945.notify b/api/dev/notifications/unread/Unraid_Server_Alert_1683570945.notify new file mode 100644 index 000000000..9b3eba7c5 --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Server_Alert_1683570945.notify @@ -0,0 +1,5 @@ +timestamp=1683570945 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/unread/Unraid_Server_Alert_1683570980.notify b/api/dev/notifications/unread/Unraid_Server_Alert_1683570980.notify new file mode 100644 index 000000000..8ca7deb93 --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Server_Alert_1683570980.notify @@ -0,0 +1,5 @@ +timestamp=1683570980 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/unread/Unraid_Server_Alert_1683571299.notify b/api/dev/notifications/unread/Unraid_Server_Alert_1683571299.notify new file mode 100644 index 000000000..33116ab1d --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Server_Alert_1683571299.notify @@ -0,0 +1,5 @@ +timestamp=1683571299 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/unread/Unraid_Server_Alert_1683571468.notify b/api/dev/notifications/unread/Unraid_Server_Alert_1683571468.notify new file mode 100644 index 000000000..c5211ec1c --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Server_Alert_1683571468.notify @@ -0,0 +1,5 @@ +timestamp=1683571468 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/unread/Unraid_Server_Alert_1683571535.notify b/api/dev/notifications/unread/Unraid_Server_Alert_1683571535.notify new file mode 100644 index 000000000..e722286fc --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Server_Alert_1683571535.notify @@ -0,0 +1,5 @@ +timestamp=1683571535 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/unread/Unraid_Server_Alert_1683571565.notify b/api/dev/notifications/unread/Unraid_Server_Alert_1683571565.notify new file mode 100644 index 000000000..b975b56c2 --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Server_Alert_1683571565.notify @@ -0,0 +1,5 @@ +timestamp=1683571565 +event=Unraid Server Alert +subject=UPS Alert +description=Communications restored with UPS Unraid +importance=normal diff --git a/api/dev/notifications/unread/Unraid_Status_1683260401.notify b/api/dev/notifications/unread/Unraid_Status_1683260401.notify new file mode 100644 index 000000000..c9ef8b79a --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Status_1683260401.notify @@ -0,0 +1,6 @@ +timestamp=1683260401 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 32 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 31 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 31 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 22 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 31 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 34 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 41 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 44 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (yesterday), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/unread/Unraid_Status_1683346801.notify b/api/dev/notifications/unread/Unraid_Status_1683346801.notify new file mode 100644 index 000000000..48e252299 --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Status_1683346801.notify @@ -0,0 +1,6 @@ +timestamp=1683346801 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 26 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 29 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 31 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 31 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 22 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 31 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 33 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 39 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 43 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (2 days ago), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/unread/Unraid_Status_1683433201.notify b/api/dev/notifications/unread/Unraid_Status_1683433201.notify new file mode 100644 index 000000000..44473d75f --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Status_1683433201.notify @@ -0,0 +1,6 @@ +timestamp=1683433201 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 26 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 29 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 32 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 32 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 23 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 31 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 32 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 39 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 43 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (3 days ago), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/unread/Unraid_Status_1683519601.notify b/api/dev/notifications/unread/Unraid_Status_1683519601.notify new file mode 100644 index 000000000..b546df63c --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Status_1683519601.notify @@ -0,0 +1,6 @@ +timestamp=1683519601 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 27 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 30 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 32 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 24 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 32 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 33 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 39 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 43 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (4 days ago), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/unread/Unraid_Status_1683606001.notify b/api/dev/notifications/unread/Unraid_Status_1683606001.notify new file mode 100644 index 000000000..59e64b900 --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Status_1683606001.notify @@ -0,0 +1,6 @@ +timestamp=1683606001 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 27 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 30 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 34 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 24 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 32 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 34 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 41 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 44 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (5 days ago), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/unread/Unraid_Status_1683692401.notify b/api/dev/notifications/unread/Unraid_Status_1683692401.notify new file mode 100644 index 000000000..661bcaadd --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Status_1683692401.notify @@ -0,0 +1,6 @@ +timestamp=1683692401 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 31 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 35 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 25 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 33 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 34 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 40 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 44 C [OK]

Last check incomplete on Thu 4 May 2023 11:00:08 AM EDT (6 days ago), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/unread/Unraid_Status_1683778802.notify b/api/dev/notifications/unread/Unraid_Status_1683778802.notify new file mode 100644 index 000000000..1e292c9c4 --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Status_1683778802.notify @@ -0,0 +1,6 @@ +timestamp=1683778802 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 31 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 33 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 24 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 33 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 34 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 40 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 44 C [OK]

Last check incomplete on Wed 10 May 2023 11:00:07 AM EDT (yesterday), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/unread/Unraid_Status_1683865201.notify b/api/dev/notifications/unread/Unraid_Status_1683865201.notify new file mode 100644 index 000000000..c7bc5f901 --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Status_1683865201.notify @@ -0,0 +1,6 @@ +timestamp=1683865201 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 27 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 30 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 33 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 32 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 24 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 32 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 33 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 40 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 43 C [OK]

Last check incomplete on Thu 11 May 2023 11:00:07 AM EDT (yesterday), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/unread/Unraid_Status_1683951601.notify b/api/dev/notifications/unread/Unraid_Status_1683951601.notify new file mode 100644 index 000000000..a2620d565 --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Status_1683951601.notify @@ -0,0 +1,6 @@ +timestamp=1683951601 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 31 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 33 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 25 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 33 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 34 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 40 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 44 C [OK]

Last check incomplete on Fri 12 May 2023 11:00:08 AM EDT (yesterday), finding 0 errors.
Error code: -4 diff --git a/api/dev/notifications/unread/Unraid_Status_1684038001.notify b/api/dev/notifications/unread/Unraid_Status_1684038001.notify new file mode 100644 index 000000000..962848b5d --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Status_1684038001.notify @@ -0,0 +1,6 @@ +timestamp=1684038001 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 31 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 33 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 33 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 25 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 33 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 35 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 41 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 45 C [OK]

Parity is valid
Last checked on Sat 13 May 2023 05:45:11 AM EDT (yesterday), finding 0 errors.
Duration: 4 hours, 15 minutes, 4 seconds. Average speed: 1.2 GB/s diff --git a/api/dev/notifications/unread/Unraid_Status_1684124401.notify b/api/dev/notifications/unread/Unraid_Status_1684124401.notify new file mode 100644 index 000000000..6e5d0a2ae --- /dev/null +++ b/api/dev/notifications/unread/Unraid_Status_1684124401.notify @@ -0,0 +1,6 @@ +timestamp=1684124401 +event=Unraid Status +subject=Notice [UNRAID] - array health report [PASS] +description=Array has 9 disks (including parity & pools) +importance=normal +message=Parity - ST18000NM000J-2TV103_ZR585CPY (sdg) - active 28 C [OK]
Disk 1 - ST18000NM000J-2TV103_ZR5B1W9X (sdf) - active 31 C [OK]
Disk 2 - WDC_WD120EDAZ-11F3RA0_5PJRD45C (sdi) - active 33 C [OK]
Disk 3 - WDC_WD120EMAZ-11BLFA0_5PH8BTYD (sde) - active 34 C [OK]
Cache - Samsung_SSD_850_EVO_250GB_S2R5NX0H643734Z (sdh) - active 25 C [OK]
Cache 2 - KINGSTON_SA2000M8250G_50026B7282669D9E (nvme3n1) - active 33 C [OK]
Zpool - CT2000P5PSSD8_22513DA16E6C (nvme0n1) - active 35 C [OK]
Zpool 2 - CT2000P5PSSD8_22513DA1657C (nvme1n1) - active 41 C [OK]
Zpool 3 - CT2000P5PSSD8_22513DA13F82 (nvme2n1) - active 45 C [OK]

Parity is valid
Last checked on Sat 13 May 2023 05:45:11 AM EDT (2 days ago), finding 0 errors.
Duration: 4 hours, 15 minutes, 4 seconds. Average speed: 1.2 GB/s diff --git a/api/dev/states/myservers.cfg b/api/dev/states/myservers.cfg index dabdfaf77..77cd0d248 100644 --- a/api/dev/states/myservers.cfg +++ b/api/dev/states/myservers.cfg @@ -1,5 +1,5 @@ [api] -version="3.1.0+0baf1385" +version="3.1.1+bf51f1f0" [local] [notifier] apikey="unnotify_30994bfaccf839c65bae75f7fa12dd5ee16e69389f754c3b98ed7d5" @@ -15,8 +15,8 @@ regWizTime="1611175408732_0951-1653-3509-FBA155FA23C0" idtoken="" accesstoken="" refreshtoken="" -allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://connect.myunraid.net, https://staging.connect.myunraid.net, https://dev-my.myunraid.net:4000" +allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://connect.myunraid.net, https://staging.connect.myunraid.net, https://dev-my.myunraid.net:4000, https://studio.apollographql.com" [upc] apikey="unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810" [connectionStatus] -minigraph="CONNECTED" +minigraph="PRE_INIT" diff --git a/api/docker-compose.yml b/api/docker-compose.yml new file mode 100644 index 000000000..f32676823 --- /dev/null +++ b/api/docker-compose.yml @@ -0,0 +1,68 @@ +version: '3.8' + +x-volumes: &volumes + volumes: + - ./dev:/app/dev + - ./src:/app/src + - ./patches:/app/patches + - ./package.json:/app/package.json + - ./package-lock.json:/app/package-lock.json + - ./tsconfig.json:/app/tsconfig.json + - ./tsup.config.ts:/app/tsup.config.ts + - ./vite.config.ts:/app/vite.config.ts + - ./dist/:/app/dist/ + - ./deploy/:/app/deploy/ + - ./README.md:/app/README.md + - ./scripts/:/app/scripts/ + - ../.git/:/app/.git/ + - ./.env.production:/app/.env.production + - ./.env.staging:/app/.env.staging + - ./.env.test:/app/.env.test + - ./.env.development:/app/.env.development + - ./.pkg-cache:/app/.pkg-cache + - ./codegen.yml:/app/codegen.yml + - ./fix-array-type.cjs:/app/fix-array-type.cjs + - /var/run/docker.sock:/var/run/docker.sock + + +networks: + mothership_default: + external: true +services: + + dev: + networks: + - mothership_default + image: unraid-api-node-18 + ports: + - "3001:3001" + build: + context: . + dockerfile: Dockerfile + args: + NODE_IMAGE: node:18.5 + NODE_ENV: development + NPM_I_CMD: npm i + <<: *volumes + stdin_open: true + tty: true + entrypoint: /bin/bash + environment: + - IS_DOCKER=true + profiles: + - builder + + + builder: + image: unraid-api-node-18 + build: + context: . + dockerfile: Dockerfile + args: + NODE_IMAGE: node:18.5 + NODE_ENV: development + NPM_I_CMD: npm i + <<: *volumes + platform: linux/amd64 + profiles: + - builder \ No newline at end of file diff --git a/api/src/__test__/core/__snapshots__/default-permissions.test.ts.snap b/api/src/__test__/core/__snapshots__/default-permissions.test.ts.snap new file mode 100644 index 000000000..f4a77e6b1 --- /dev/null +++ b/api/src/__test__/core/__snapshots__/default-permissions.test.ts.snap @@ -0,0 +1,350 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Returns default permissions 1`] = ` +{ + "admin": { + "extends": "user", + "permissions": [ + { + "action": "read:any", + "attributes": "*", + "resource": "apikey", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "array", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "cpu", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "crash-reporting-enabled", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "device", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "device/unassigned", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "disk", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "disk/settings", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "display", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "docker/container", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "docker/network", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "flash", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "info", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "license-key", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "machine-id", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "memory", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "notifications", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "online", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "os", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "owner", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "parity-history", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "permission", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "registration", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "servers", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "service", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "service/emhttpd", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "service/unraid-api", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "services", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "share", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "software-versions", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "unraid-version", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "uptime", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "user", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "vars", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "vms", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "vms/domain", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "vms/network", + }, + ], + }, + "guest": { + "permissions": [ + { + "action": "read:any", + "attributes": "*", + "resource": "me", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "welcome", + }, + ], + }, + "my_servers": { + "extends": "guest", + "permissions": [ + { + "action": "read:any", + "attributes": "*", + "resource": "dashboard", + }, + { + "action": "read:own", + "attributes": "*", + "resource": "two-factor", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "array", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "docker/container", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "docker/network", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "notifications", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "vms/domain", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "unraid-version", + }, + ], + }, + "notifier": { + "extends": "guest", + "permissions": [ + { + "action": "create:own", + "attributes": "*", + "resource": "notifications", + }, + ], + }, + "upc": { + "extends": "guest", + "permissions": [ + { + "action": "read:own", + "attributes": "*", + "resource": "apikey", + }, + { + "action": "read:own", + "attributes": "*", + "resource": "cloud", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "config", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "crash-reporting-enabled", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "disk", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "display", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "flash", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "os", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "owner", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "permission", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "registration", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "servers", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "vars", + }, + ], + }, + "user": { + "extends": "guest", + "permissions": [ + { + "action": "read:own", + "attributes": "*", + "resource": "apikey", + }, + { + "action": "read:any", + "attributes": "*", + "resource": "permission", + }, + ], + }, +} +`; diff --git a/api/src/__test__/core/utils/images/image-file-helpers.test.ts b/api/src/__test__/core/utils/images/image-file-helpers.test.ts index 37994111d..2cd25f9c3 100644 --- a/api/src/__test__/core/utils/images/image-file-helpers.test.ts +++ b/api/src/__test__/core/utils/images/image-file-helpers.test.ts @@ -1,6 +1,6 @@ import { getBannerPathIfPresent, getCasePathIfPresent } from "@app/core/utils/images/image-file-helpers"; +import { loadDynamixConfigFile } from "@app/store/actions/load-dynamix-config-file"; import { store } from "@app/store/index"; -import { loadDynamixConfigFile } from "@app/store/modules/dynamix"; import { expect, test } from "vitest"; diff --git a/api/src/__test__/store/modules/__snapshots__/notifications.test.ts.snap b/api/src/__test__/store/modules/__snapshots__/notifications.test.ts.snap new file mode 100644 index 000000000..aac8ef17d --- /dev/null +++ b/api/src/__test__/store/modules/__snapshots__/notifications.test.ts.snap @@ -0,0 +1,196 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`loads notifications properly 1`] = ` +{ + "/app/dev/notifications/unread/Unraid_Parity_check_1683971161.notify": { + "description": "Canceled", + "id": "/app/dev/notifications/unread/Unraid_Parity_check_1683971161.notify", + "importance": "WARNING", + "link": undefined, + "subject": "Notice [UNRAID] - Parity check finished (0 errors)", + "timestamp": "2023-05-13T09:46:01.000Z", + "title": "Unraid Parity check", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Server_Alert_1683570896.notify": { + "description": "Power failure on UPS Unraid. Running on batteries.", + "id": "/app/dev/notifications/unread/Unraid_Server_Alert_1683570896.notify", + "importance": "ALERT", + "link": undefined, + "subject": "UPS Alert", + "timestamp": "2023-05-08T18:34:56.000Z", + "title": "Unraid Server Alert", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Server_Alert_1683570945.notify": { + "description": "Communications restored with UPS Unraid", + "id": "/app/dev/notifications/unread/Unraid_Server_Alert_1683570945.notify", + "importance": "INFO", + "link": undefined, + "subject": "UPS Alert", + "timestamp": "2023-05-08T18:35:45.000Z", + "title": "Unraid Server Alert", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Server_Alert_1683570980.notify": { + "description": "Communications restored with UPS Unraid", + "id": "/app/dev/notifications/unread/Unraid_Server_Alert_1683570980.notify", + "importance": "INFO", + "link": undefined, + "subject": "UPS Alert", + "timestamp": "2023-05-08T18:36:20.000Z", + "title": "Unraid Server Alert", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Server_Alert_1683571299.notify": { + "description": "Communications restored with UPS Unraid", + "id": "/app/dev/notifications/unread/Unraid_Server_Alert_1683571299.notify", + "importance": "INFO", + "link": undefined, + "subject": "UPS Alert", + "timestamp": "2023-05-08T18:41:39.000Z", + "title": "Unraid Server Alert", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Server_Alert_1683571468.notify": { + "description": "Communications restored with UPS Unraid", + "id": "/app/dev/notifications/unread/Unraid_Server_Alert_1683571468.notify", + "importance": "INFO", + "link": undefined, + "subject": "UPS Alert", + "timestamp": "2023-05-08T18:44:28.000Z", + "title": "Unraid Server Alert", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Server_Alert_1683571535.notify": { + "description": "Communications restored with UPS Unraid", + "id": "/app/dev/notifications/unread/Unraid_Server_Alert_1683571535.notify", + "importance": "INFO", + "link": undefined, + "subject": "UPS Alert", + "timestamp": "2023-05-08T18:45:35.000Z", + "title": "Unraid Server Alert", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Server_Alert_1683571565.notify": { + "description": "Communications restored with UPS Unraid", + "id": "/app/dev/notifications/unread/Unraid_Server_Alert_1683571565.notify", + "importance": "INFO", + "link": undefined, + "subject": "UPS Alert", + "timestamp": "2023-05-08T18:46:05.000Z", + "title": "Unraid Server Alert", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Status_1683260401.notify": { + "description": "Array has 9 disks (including parity & pools)", + "id": "/app/dev/notifications/unread/Unraid_Status_1683260401.notify", + "importance": "INFO", + "link": undefined, + "subject": "Notice [UNRAID] - array health report [PASS]", + "timestamp": "2023-05-05T04:20:01.000Z", + "title": "Unraid Status", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Status_1683346801.notify": { + "description": "Array has 9 disks (including parity & pools)", + "id": "/app/dev/notifications/unread/Unraid_Status_1683346801.notify", + "importance": "INFO", + "link": undefined, + "subject": "Notice [UNRAID] - array health report [PASS]", + "timestamp": "2023-05-06T04:20:01.000Z", + "title": "Unraid Status", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Status_1683433201.notify": { + "description": "Array has 9 disks (including parity & pools)", + "id": "/app/dev/notifications/unread/Unraid_Status_1683433201.notify", + "importance": "INFO", + "link": undefined, + "subject": "Notice [UNRAID] - array health report [PASS]", + "timestamp": "2023-05-07T04:20:01.000Z", + "title": "Unraid Status", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Status_1683519601.notify": { + "description": "Array has 9 disks (including parity & pools)", + "id": "/app/dev/notifications/unread/Unraid_Status_1683519601.notify", + "importance": "INFO", + "link": undefined, + "subject": "Notice [UNRAID] - array health report [PASS]", + "timestamp": "2023-05-08T04:20:01.000Z", + "title": "Unraid Status", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Status_1683606001.notify": { + "description": "Array has 9 disks (including parity & pools)", + "id": "/app/dev/notifications/unread/Unraid_Status_1683606001.notify", + "importance": "INFO", + "link": undefined, + "subject": "Notice [UNRAID] - array health report [PASS]", + "timestamp": "2023-05-09T04:20:01.000Z", + "title": "Unraid Status", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Status_1683692401.notify": { + "description": "Array has 9 disks (including parity & pools)", + "id": "/app/dev/notifications/unread/Unraid_Status_1683692401.notify", + "importance": "INFO", + "link": undefined, + "subject": "Notice [UNRAID] - array health report [PASS]", + "timestamp": "2023-05-10T04:20:01.000Z", + "title": "Unraid Status", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Status_1683778802.notify": { + "description": "Array has 9 disks (including parity & pools)", + "id": "/app/dev/notifications/unread/Unraid_Status_1683778802.notify", + "importance": "INFO", + "link": undefined, + "subject": "Notice [UNRAID] - array health report [PASS]", + "timestamp": "2023-05-11T04:20:02.000Z", + "title": "Unraid Status", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Status_1683865201.notify": { + "description": "Array has 9 disks (including parity & pools)", + "id": "/app/dev/notifications/unread/Unraid_Status_1683865201.notify", + "importance": "INFO", + "link": undefined, + "subject": "Notice [UNRAID] - array health report [PASS]", + "timestamp": "2023-05-12T04:20:01.000Z", + "title": "Unraid Status", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Status_1683951601.notify": { + "description": "Array has 9 disks (including parity & pools)", + "id": "/app/dev/notifications/unread/Unraid_Status_1683951601.notify", + "importance": "INFO", + "link": undefined, + "subject": "Notice [UNRAID] - array health report [PASS]", + "timestamp": "2023-05-13T04:20:01.000Z", + "title": "Unraid Status", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Status_1684038001.notify": { + "description": "Array has 9 disks (including parity & pools)", + "id": "/app/dev/notifications/unread/Unraid_Status_1684038001.notify", + "importance": "INFO", + "link": undefined, + "subject": "Notice [UNRAID] - array health report [PASS]", + "timestamp": "2023-05-14T04:20:01.000Z", + "title": "Unraid Status", + "type": "UNREAD", + }, + "/app/dev/notifications/unread/Unraid_Status_1684124401.notify": { + "description": "Array has 9 disks (including parity & pools)", + "id": "/app/dev/notifications/unread/Unraid_Status_1684124401.notify", + "importance": "INFO", + "link": undefined, + "subject": "Notice [UNRAID] - array health report [PASS]", + "timestamp": "2023-05-15T04:20:01.000Z", + "title": "Unraid Status", + "type": "UNREAD", + }, +} +`; diff --git a/api/src/__test__/store/modules/notifications.test.ts b/api/src/__test__/store/modules/notifications.test.ts new file mode 100644 index 000000000..748b69b11 --- /dev/null +++ b/api/src/__test__/store/modules/notifications.test.ts @@ -0,0 +1,14 @@ +import { setupNotificationWatch } from '@app/core/modules/notifications/setup-notification-watch'; +import { sleep } from '@app/core/utils/misc/sleep'; +import { loadDynamixConfigFile } from '@app/store/actions/load-dynamix-config-file'; +import { store } from '@app/store/index'; +import { expect, test } from 'vitest'; + +test('loads notifications properly', async () => { + await store.dispatch(loadDynamixConfigFile()).unwrap(); + const watch = await setupNotificationWatch(); + expect(watch).not.toBeNull(); + await sleep(400); + expect(store.getState().notifications.notifications).toMatchSnapshot(); + await watch?.close(); +}); diff --git a/api/src/__test__/store/state-parsers/var.test.ts b/api/src/__test__/store/state-parsers/var.test.ts new file mode 100644 index 000000000..479164911 --- /dev/null +++ b/api/src/__test__/store/state-parsers/var.test.ts @@ -0,0 +1,172 @@ +import { join } from 'path'; +import { expect, test } from 'vitest'; +import { store } from '@app/store'; +import type { VarIni } from '@app/store/state-parsers/var'; + +test('Returns parsed state file', async () => { + const { parse } = await import('@app/store/state-parsers/var'); + const { parseConfig } = await import('@app/core/utils/misc/parse-config'); + const { paths } = store.getState(); + const filePath = join(paths.states, 'var.ini'); + const stateFile = parseConfig({ + filePath, + type: 'ini', + }); + + expect(parse(stateFile)).toMatchInlineSnapshot(` + { + "bindMgt": false, + "cacheNumDevices": NaN, + "cacheSbNumDisks": NaN, + "comment": "Dev Server", + "configState": "yes", + "configValid": true, + "csrfToken": "0000000000000000", + "defaultFsType": "xfs", + "deviceCount": 4, + "domain": "", + "domainLogin": "Administrator", + "domainShort": "", + "enableFruit": "no", + "flashGuid": "0000-0000-0000-000000000000", + "flashProduct": "DataTraveler_3.0", + "flashVendor": "KINGSTON", + "fsCopyPrcnt": 0, + "fsNumMounted": 0, + "fsNumUnmountable": 0, + "fsProgress": "Autostart disabled", + "fsState": "Stopped", + "fsUnmountableMask": "", + "fuseDirectio": "auto", + "fuseDirectioDefault": "auto", + "fuseDirectioStatus": "default", + "fuseRemember": "330", + "fuseRememberDefault": "330", + "fuseRememberStatus": "default", + "fuseUseino": "yes", + "hideDotFiles": false, + "joinStatus": "Not joined", + "localMaster": true, + "localTld": "local", + "luksKeyfile": "/tmp/unraid/keyfile", + "maxArraysz": 30, + "maxCachesz": 30, + "mdColor": "green-blink", + "mdNumDisabled": 1, + "mdNumDisks": 4, + "mdNumErased": 0, + "mdNumInvalid": 1, + "mdNumMissing": 0, + "mdNumNew": 0, + "mdNumStripes": 1280, + "mdNumStripesDefault": 1280, + "mdNumStripesStatus": "default", + "mdQueueLimit": "80", + "mdQueueLimitDefault": "80", + "mdQueueLimitStatus": "default", + "mdResync": 0, + "mdResyncAction": "check P", + "mdResyncCorr": "0", + "mdResyncDb": "0", + "mdResyncDt": "0", + "mdResyncPos": 0, + "mdResyncSize": 438960096, + "mdScheduler": "auto", + "mdSchedulerDefault": "auto", + "mdSchedulerStatus": "default", + "mdState": "STOPPED", + "mdSyncLimit": "5", + "mdSyncLimitDefault": "5", + "mdSyncLimitStatus": "default", + "mdSyncThresh": NaN, + "mdSyncThreshDefault": NaN, + "mdSyncWindow": NaN, + "mdSyncWindowDefault": NaN, + "mdVersion": "2.9.14", + "mdWriteMethod": NaN, + "mdWriteMethodDefault": "auto", + "mdWriteMethodStatus": "default", + "name": "Tower", + "nrRequests": NaN, + "nrRequestsDefault": NaN, + "nrRequestsStatus": "default", + "ntpServer1": "time1.google.com", + "ntpServer2": "time2.google.com", + "ntpServer3": "time3.google.com", + "ntpServer4": "time4.google.com", + "pollAttributes": "1800", + "pollAttributesDefault": "1800", + "pollAttributesStatus": "default", + "port": 80, + "portssh": 22, + "portssl": 443, + "porttelnet": 23, + "queueDepth": "auto", + "regCheck": "Valid", + "regFile": "/app/dev/Unraid.net/Pro.key", + "regGen": "0", + "regGuid": "13FE-4200-C300-58C372A52B19", + "regState": "PRO", + "regTm": "1833409182", + "regTm2": "0", + "regTo": "Eli Bosley", + "regTy": "PRO", + "reservedNames": "parity,parity2,parity3,diskP,diskQ,diskR,disk,disks,flash,boot,user,user0,disk0,disk1,disk2,disk3,disk4,disk5,disk6,disk7,disk8,disk9,disk10,disk11,disk12,disk13,disk14,disk15,disk16,disk17,disk18,disk19,disk20,disk21,disk22,disk23,disk24,disk25,disk26,disk27,disk28,disk29,disk30,disk31", + "safeMode": false, + "sbClean": true, + "sbEvents": 173, + "sbName": "/boot/config/super.dat", + "sbNumDisks": 5, + "sbState": "1", + "sbSyncErrs": 0, + "sbSyncExit": "0", + "sbSynced": 1586819259, + "sbSynced2": 1586822456, + "sbUpdated": "1596079143", + "sbVersion": "2.9.13", + "security": "user", + "shareAvahiEnabled": true, + "shareAvahiSmbModel": "Xserve", + "shareAvahiSmbName": "%h", + "shareCacheEnabled": true, + "shareCacheFloor": "2000000", + "shareCount": 0, + "shareDisk": "yes", + "shareInitialGroup": "Domain Users", + "shareInitialOwner": "Administrator", + "shareMoverActive": false, + "shareMoverLogging": false, + "shareMoverSchedule": "40 3 * * *", + "shareNfsCount": 0, + "shareNfsEnabled": false, + "shareSmbCount": 1, + "shareSmbEnabled": true, + "shareSmbMode": "workgroup", + "shareUser": "e", + "shareUserExclude": "", + "shareUserInclude": "", + "shfsLogging": "1", + "shutdownTimeout": 90, + "spindownDelay": 0, + "spinupGroups": false, + "startArray": false, + "startMode": "Normal", + "startPage": "Main", + "sysArraySlots": 24, + "sysCacheSlots": NaN, + "sysFlashSlots": 1, + "sysModel": "Dell R710", + "timeZone": "Australia/Adelaide", + "useNetbios": "yes", + "useNtp": true, + "useSsh": true, + "useSsl": null, + "useTelnet": true, + "useUpnp": true, + "useWsd": "no", + "version": "6.11.2", + "workgroup": "WORKGROUP", + "wsdOpt": "", + } + `); +}); diff --git a/api/src/common/dashboard/generate-data.ts b/api/src/common/dashboard/generate-data.ts index 92c58e213..87a75d838 100644 --- a/api/src/common/dashboard/generate-data.ts +++ b/api/src/common/dashboard/generate-data.ts @@ -9,7 +9,7 @@ import { getters, store } from '@app/store'; import { type DashboardServiceInput, type DashboardInput } from '@app/graphql/generated/client/graphql'; import { API_VERSION } from '@app/environment'; import { DynamicRemoteAccessType } from '@app/remoteAccess/types'; -import { DashboardInputSchema } from '@app/graphql/generate/validators'; +import { DashboardInputSchema } from '@app/graphql/generated/client/validators'; import { ZodError } from 'zod'; const getVmSummary = async (): Promise => { diff --git a/api/src/core/default-permissions.ts b/api/src/core/default-permissions.ts new file mode 100644 index 000000000..c260d1802 --- /dev/null +++ b/api/src/core/default-permissions.ts @@ -0,0 +1,120 @@ +export interface Permission { resource: string, action: string, attributes: string } +export interface Role { + permissions: Array + extends?: string; +} + +export const admin: Role = { + extends: 'user', + permissions: [ + // @NOTE: Uncomment the first line to enable creation of api keys. + // See the README.md for more information. + // @WARNING: This is currently unsupported, please be careful. + // { resource: 'apikey', action: 'create:any', attributes: '*' }, + { resource: 'apikey', action: 'read:any', attributes: '*' }, + { resource: 'array', action: 'read:any', attributes: '*' }, + { resource: 'cpu', action: 'read:any', attributes: '*' }, + { + resource: 'crash-reporting-enabled', + action: 'read:any', + attributes: '*', + }, + { resource: 'device', action: 'read:any', attributes: '*' }, + { resource: 'device/unassigned', action: 'read:any', attributes: '*' }, + { resource: 'disk', action: 'read:any', attributes: '*' }, + { resource: 'disk/settings', action: 'read:any', attributes: '*' }, + { resource: 'display', action: 'read:any', attributes: '*' }, + { resource: 'docker/container', action: 'read:any', attributes: '*' }, + { resource: 'docker/network', action: 'read:any', attributes: '*' }, + { resource: 'flash', action: 'read:any', attributes: '*' }, + { resource: 'info', action: 'read:any', attributes: '*' }, + { resource: 'license-key', action: 'read:any', attributes: '*' }, + { resource: 'machine-id', action: 'read:any', attributes: '*' }, + { resource: 'memory', action: 'read:any', attributes: '*' }, + { resource: 'notifications', action: 'read:any', attributes: '*' }, + { resource: 'online', action: 'read:any', attributes: '*' }, + { resource: 'os', action: 'read:any', attributes: '*' }, + { resource: 'owner', action: 'read:any', attributes: '*' }, + { resource: 'parity-history', action: 'read:any', attributes: '*' }, + { resource: 'permission', action: 'read:any', attributes: '*' }, + { resource: 'registration', action: 'read:any', attributes: '*' }, + { resource: 'servers', action: 'read:any', attributes: '*' }, + { resource: 'service', action: 'read:any', attributes: '*' }, + { resource: 'service/emhttpd', action: 'read:any', attributes: '*' }, + { resource: 'service/unraid-api', action: 'read:any', attributes: '*' }, + { resource: 'services', action: 'read:any', attributes: '*' }, + { resource: 'share', action: 'read:any', attributes: '*' }, + { resource: 'software-versions', action: 'read:any', attributes: '*' }, + { resource: 'unraid-version', action: 'read:any', attributes: '*' }, + { resource: 'uptime', action: 'read:any', attributes: '*' }, + { resource: 'user', action: 'read:any', attributes: '*' }, + { resource: 'vars', action: 'read:any', attributes: '*' }, + { resource: 'vms', action: 'read:any', attributes: '*' }, + { resource: 'vms/domain', action: 'read:any', attributes: '*' }, + { resource: 'vms/network', action: 'read:any', attributes: '*' }, + ], +}; + +export const user: Role = { + extends: 'guest', + permissions: [ + { resource: 'apikey', action: 'read:own', attributes: '*' }, + { resource: 'permission', action: 'read:any', attributes: '*' }, + ], +}; + +export const upc: Role = { + extends: 'guest', + permissions: [ + { resource: 'apikey', action: 'read:own', attributes: '*' }, + { resource: 'cloud', action: 'read:own', attributes: '*' }, + { resource: 'config', action: 'read:any', attributes: '*' }, + { resource: 'crash-reporting-enabled', action: 'read:any', attributes: '*' }, + { resource: 'disk', action: 'read:any', attributes: '*' }, + { resource: 'display', action: 'read:any', attributes: '*' }, + { resource: 'flash', action: 'read:any', attributes: '*' }, + { resource: 'os', action: 'read:any', attributes: '*' }, + { resource: 'owner', action: 'read:any', attributes: '*' }, + { resource: 'permission', action: 'read:any', attributes: '*' }, + { resource: 'registration', action: 'read:any', attributes: '*' }, + { resource: 'servers', action: 'read:any', attributes: '*' }, + { resource: 'vars', action: 'read:any', attributes: '*' }, + ], +}; + +export const my_servers: Role = { + extends: 'guest', + permissions: [ + { resource: 'dashboard', action: 'read:any', attributes: '*' }, + { resource: 'two-factor', action: 'read:own', attributes: '*' }, + { resource: 'array', action: 'read:any', attributes: '*' }, + { resource: 'docker/container', action: 'read:any', attributes: '*' }, + { resource: 'docker/network', action: 'read:any', attributes: '*' }, + { resource: 'notifications', action: 'read:any', attributes: '*' }, + { resource: 'vms/domain', action: 'read:any', attributes: '*' }, + { resource: 'unraid-version', action: 'read:any', attributes: '*' }, + ], +}; + +export const notifier: Role = { + extends: 'guest', + permissions: [ + { resource: 'notifications', action: 'create:own', attributes: '*' }, + ], +}; + +export const guest: Role = { + permissions: [ + { resource: 'me', action: 'read:any', attributes: '*' }, + { resource: 'welcome', action: 'read:any', attributes: '*' }, + ], +}; + +export const permissions: Record = { + guest, + user, + admin, + upc, + my_servers, + notifier, +}; diff --git a/api/src/core/log.ts b/api/src/core/log.ts new file mode 100644 index 000000000..62087e20e --- /dev/null +++ b/api/src/core/log.ts @@ -0,0 +1,143 @@ +/*! + * Copyright 2019-2022 Lime Technology Inc. All rights reserved. + * Written by: Alexis Tyler + */ + +import chalk from 'chalk'; +import { configure, getLogger } from 'log4js'; +import { serializeError } from 'serialize-error'; + +export const levels = ['ALL', 'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', 'MARK', 'OFF'] as const; + +const contextEnabled = Boolean(process.env.LOG_CONTEXT); +const stackEnabled = Boolean(process.env.LOG_STACKTRACE); +const tracingEnabled = Boolean(process.env.LOG_TRACING); +const fullLoggingPattern = chalk`{gray [%d]} %x\{id\} %[[%p]%] %[[%c]%] %m{gray %x\{context\}}${tracingEnabled ? ' %[%f:%l%]' : ''}`; +const minimumLoggingPattern = '%m'; +const appenders = process.env.LOG_TRANSPORT?.split(',').map(transport => transport.trim()) ?? ['out']; +const level = levels[levels.indexOf(process.env.LOG_LEVEL?.toUpperCase() as typeof levels[number])] ?? 'INFO'; +const logLayout = { + type: 'pattern', + // Depending on what this env is set to we'll either get raw or pretty logs + // The reason we do this is to allow the app to change this value + // This way pretty logs can be turned off programmatically + pattern: process.env.LOG_TYPE === 'pretty' ? fullLoggingPattern : minimumLoggingPattern, + tokens: { + id() { + return chalk`{gray [${process.pid}]}`; + }, + context({ context }: { context?: any }) { + if (!contextEnabled || !context) { + return ''; + } + + try { + const contextEntries = Object.entries(context) + .map(([key, value]) => [key, value instanceof Error ? (stackEnabled ? serializeError(value) : value) : value]) + .filter(([key]) => key !== 'pid'); + const cleanContext = Object.fromEntries(contextEntries); + return `\n${Object.entries(cleanContext).map(([key, value]) => `${key}=${JSON.stringify(value, null, 2)}`).join(' ')}`; + } catch (error: unknown) { + const errorInfo = error instanceof Error ? `${error.message}: ${error.stack ?? 'no stack'}` : 'Error not instance of error'; + return `Error generating context: ${errorInfo}`; + } + }, + }, +}; + +if (process.env.NODE_ENV !== 'test') { + // We log to both the stdout and log file + // The log file should be changed to errors only unless in debug mode + configure({ + appenders: { + file: { + type: 'file', + filename: '/var/log/unraid-api/stdout.log', + maxLogSize: 10_000_000, + backups: 0, + layout: { + ...logLayout, + // File logs should always be pretty + pattern: fullLoggingPattern, + }, + }, + errorFile: { + type: 'file', + filename: '/var/log/unraid-api/stderr.log', + maxLogSize: 2_500_000, + backups: 0, + layout: { + ...logLayout, + // File logs should always be pretty + pattern: fullLoggingPattern, + }, + }, + out: { + type: 'stdout', + layout: logLayout, + }, + errors: { type: 'logLevelFilter', appender: 'errorFile', level: 'error' }, + }, + categories: { + default: { + appenders, + level, + enableCallStack: tracingEnabled, + }, + }, + }); +} + + +export const internalLogger = getLogger('internal'); +export const logger = getLogger('app'); +export const mothershipLogger = getLogger('mothership'); +export const dashboardLogger = getLogger('dashboard'); +export const emhttpLogger = getLogger('emhttp'); +export const libvirtLogger = getLogger('libvirt'); +export const graphqlLogger = getLogger('graphql'); +export const dockerLogger = getLogger('docker'); +export const cliLogger = getLogger('cli'); +export const minigraphLogger = getLogger('minigraph'); +export const cloudConnectorLogger = getLogger('cloud-connector'); +export const upnpLogger = getLogger('upnp'); +export const keyServerLogger = getLogger('key-server'); +export const remoteAccessLogger = getLogger('remote-access'); +export const remoteQueryLogger = getLogger('remote-query'); + +export const loggers = [ + logger, + mothershipLogger, + dashboardLogger, + emhttpLogger, + libvirtLogger, + graphqlLogger, + dockerLogger, + cliLogger, + minigraphLogger, + cloudConnectorLogger, + upnpLogger, + keyServerLogger, + remoteAccessLogger, + remoteQueryLogger, +]; + +// Send SIGUSR1 to increase log level +process.on('SIGUSR1', () => { + const level = typeof logger.level === 'string' ? logger.level : logger.level.levelStr; + const nextLevel = levels[levels.findIndex(_level => _level === level) + 1] ?? levels[0]; + loggers.forEach(logger => { + logger.level = nextLevel; + }); + internalLogger.mark('Log level changed from %s to %s', level, nextLevel); +}); + +// Send SIGUSR1 to decrease log level +process.on('SIGUSR2', () => { + const level = typeof logger.level === 'string' ? logger.level : logger.level.levelStr; + const nextLevel = levels[levels.findIndex(_level => _level === level) - 1] ?? levels[levels.length - 1]; + loggers.forEach(logger => { + logger.level = nextLevel; + }); + internalLogger.mark('Log level changed from %s to %s', level, nextLevel); +}); diff --git a/api/src/core/modules/notifications/setup-notification-watch.ts b/api/src/core/modules/notifications/setup-notification-watch.ts new file mode 100644 index 000000000..39b7bfc1d --- /dev/null +++ b/api/src/core/modules/notifications/setup-notification-watch.ts @@ -0,0 +1,34 @@ +import { getters, store } from '@app/store/index'; +import { clearNotification, loadNotification } from '@app/store/modules/notifications'; +import { FileLoadStatus } from '@app/store/types'; +import { type FSWatcher, watch } from 'chokidar'; +import { join } from 'node:path'; + +const handleNotificationAdd = (path: string) => { + store.dispatch(loadNotification({ path })); +}; + +const handleNotificationRemove = (path: string) => { + store.dispatch(clearNotification({ path })); +}; + +let watcher: FSWatcher | null = null; + +export const setupNotificationWatch = async (): Promise => { + const { notify, status } = getters.dynamix(); + if (status === FileLoadStatus.LOADED && notify?.path) { + if (watcher) { + await watcher.close() + } + watcher = watch(join(notify.path, 'unread'), {}) + .on('add', (path) => { + handleNotificationAdd(path); + }) + .on('unlink', (path) => { + handleNotificationRemove(path); + }); + + return watcher; + } + return null; +}; diff --git a/api/src/core/pubsub.ts b/api/src/core/pubsub.ts new file mode 100644 index 000000000..69653dff2 --- /dev/null +++ b/api/src/core/pubsub.ts @@ -0,0 +1,22 @@ +/*! + * Copyright 2019-2022 Lime Technology Inc. All rights reserved. + * Written by: Alexis Tyler + */ + +import { PubSub } from 'graphql-subscriptions'; +import EventEmitter from 'events'; + +// Allow subscriptions to have 30 connections +const eventEmitter = new EventEmitter(); +eventEmitter.setMaxListeners(30); + +export enum PUBSUB_CHANNEL { + DISPLAY = 'DISPLAY', + INFO = 'INFO', + NOTIFICATION = 'NOTIFICATION', + OWNER = 'OWNER', + SERVERS = 'SERVERS', + +} + +export const pubsub = new PubSub({ eventEmitter }); diff --git a/api/src/core/types/ini.ts b/api/src/core/types/ini.ts new file mode 100644 index 000000000..fa772627a --- /dev/null +++ b/api/src/core/types/ini.ts @@ -0,0 +1,85 @@ +export type IniEnabled = 'e' | ''; +export type IniNumberBoolean = '0' | '1'; +export type IniStringBoolean = 'no' | 'false' | 'yes' | 'true'; +export type IniStringBooleanOrAuto = 'auto' | 'no' | 'yes'; + +type Unit = 'C' | 'F'; + +interface Display { + align: string; + banner: string; + critical: string; + custom: string; + dashapps: string; + date: string; + hot: string; + max: string; + number: string; + refresh: string; + resize: string; + scale: string; + tabs: string; + text: string; + theme: string; + total: string; + unit: Unit; + usage: string; + warning: string; + wwn: string; + locale: string; +} + +interface Notify { + entity: string; + normal: string; + warning: string; + alert: string; + plugin: string; + docker_notify: string; + report: string; + date: string; + time: string; + position: string; + /** path for notifications (defaults to '/tmp/notifications') */ + path: string; + display: string; + system: string; + version: string; + docker_update: string; +} + +interface Ssmtp { + service: string; + root: string; + rcptTo: string; + setEmailPriority: string; + subject: string; + server: string; + port: string; + useTls: string; + useStarttls: string; + useTlsCert: string; + authMethod: string; + authUser: string; + authPass: string; +} + +interface Parity { + mode: string; + dotm: string; + hour: string; +} + +interface Remote { + wanaccess: string; + wanport: string; + apikey: string; +} + +export interface DynamixConfig extends Record { + display: Display; + notify: Notify; + ssmtp: Ssmtp; + parity: Parity; + remote: Remote; +} diff --git a/api/src/graphql/generated/api/operations.ts b/api/src/graphql/generated/api/operations.ts new file mode 100755 index 000000000..945ad6acb --- /dev/null +++ b/api/src/graphql/generated/api/operations.ts @@ -0,0 +1,1171 @@ +/* eslint-disable */ +import * as Types from '@app/graphql/generated/api/types'; + +import { z } from 'zod' +import { ApiKey, ApiKeyResponse, ArrayType, ArrayCapacity, ArrayDisk, ArrayDiskFsColor, ArrayDiskStatus, ArrayDiskType, ArrayPendingState, ArrayState, Baseboard, Capacity, Case, Cloud, CloudResponse, Config, ConfigErrorState, ContainerHostConfig, ContainerMount, ContainerPort, ContainerPortType, ContainerState, Device, Devices, Disk, DiskFsType, DiskInterfaceType, DiskPartition, DiskSmartStatus, Display, DockerContainer, DockerNetwork, Flash, Gpu, Importance, Info, InfoApps, InfoCpu, InfoMemory, KeyFile, Me, MemoryFormFactor, MemoryLayout, MemoryType, MinigraphStatus, MinigraphqlResponse, Mount, Network, Notification, NotificationFilter, NotificationInput, NotificationType, Os, Owner, ParityCheck, Partition, Pci, Permissions, ProfileModel, Registration, RegistrationState, RelayResponse, Scope, Server, ServerStatus, Service, Share, System, Temperature, Theme, TwoFactorLocal, TwoFactorRemote, TwoFactorWithToken, TwoFactorWithoutToken, UnassignedDevice, Uptime, Usb, User, Vars, Versions, VmDomain, VmNetwork, VmState, Vms, Welcome, addApiKeyInput, addScopeInput, addScopeToApiKeyInput, addUserInput, arrayDiskInput, authenticateInput, deleteUserInput, mdState, registrationType, testMutationInput, testQueryInput, updateApikeyInput, usersInput } from '@app/graphql/generated/api/types' +import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; + +type Properties = Required<{ + [K in keyof T]: z.ZodType; +}>; + +type definedNonNullAny = {}; + +export const isDefinedNonNullAny = (v: any): v is definedNonNullAny => v !== undefined && v !== null; + +export const definedNonNullAnySchema = z.any().refine((v) => isDefinedNonNullAny(v)); + +export function ApiKeySchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('ApiKey').optional(), + description: z.string().nullish(), + expiresAt: z.number(), + key: z.string(), + name: z.string(), + scopes: definedNonNullAnySchema + }) +} + +export function ApiKeyResponseSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('ApiKeyResponse').optional(), + error: z.string().nullish(), + valid: z.boolean() + }) +} + +export function ArrayTypeSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Array').optional(), + boot: ArrayDiskSchema().nullish(), + caches: z.array(ArrayDiskSchema()), + capacity: ArrayCapacitySchema(), + disks: z.array(ArrayDiskSchema()), + parities: z.array(ArrayDiskSchema()), + pendingState: ArrayPendingStateSchema.nullish(), + previousState: ArrayStateSchema.nullish(), + state: ArrayStateSchema + }) +} + +export function ArrayCapacitySchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('ArrayCapacity').optional(), + disks: CapacitySchema(), + kilobytes: CapacitySchema() + }) +} + +export function ArrayDiskSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('ArrayDisk').optional(), + comment: z.string().nullish(), + critical: z.number().nullish(), + device: z.string().nullish(), + exportable: z.boolean().nullish(), + format: z.string().nullish(), + fsFree: z.number().nullish(), + fsSize: z.number().nullish(), + fsType: z.string().nullish(), + fsUsed: z.number().nullish(), + id: z.string(), + idx: z.number(), + name: z.string().nullish(), + numErrors: z.number(), + numReads: z.number(), + numWrites: z.number(), + rotational: z.boolean().nullish(), + size: z.number(), + status: ArrayDiskStatusSchema.nullish(), + temp: z.number().nullish(), + transport: z.string().nullish(), + type: ArrayDiskTypeSchema, + warning: z.number().nullish() + }) +} + +export const ArrayDiskFsColorSchema = z.nativeEnum(ArrayDiskFsColor); + +export const ArrayDiskStatusSchema = z.nativeEnum(ArrayDiskStatus); + +export const ArrayDiskTypeSchema = z.nativeEnum(ArrayDiskType); + +export const ArrayPendingStateSchema = z.nativeEnum(ArrayPendingState); + +export const ArrayStateSchema = z.nativeEnum(ArrayState); + +export function BaseboardSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Baseboard').optional(), + assetTag: z.string().nullish(), + manufacturer: z.string(), + model: z.string().nullish(), + serial: z.string().nullish(), + version: z.string().nullish() + }) +} + +export function CapacitySchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Capacity').optional(), + free: z.string(), + total: z.string(), + used: z.string() + }) +} + +export function CaseSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Case').optional(), + base64: z.string().nullish(), + error: z.string().nullish(), + icon: z.string().nullish(), + url: z.string().nullish() + }) +} + +export function CloudSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Cloud').optional(), + allowedOrigins: z.array(z.string()), + apiKey: ApiKeyResponseSchema(), + cloud: CloudResponseSchema(), + error: z.string().nullish(), + minigraphql: MinigraphqlResponseSchema(), + relay: RelayResponseSchema().nullish() + }) +} + +export function CloudResponseSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('CloudResponse').optional(), + error: z.string().nullish(), + ip: z.string().nullish(), + status: z.string() + }) +} + +export function ConfigSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Config').optional(), + error: ConfigErrorStateSchema.nullish(), + valid: z.boolean().nullish() + }) +} + +export const ConfigErrorStateSchema = z.nativeEnum(ConfigErrorState); + +export function ContainerHostConfigSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('ContainerHostConfig').optional(), + networkMode: z.string() + }) +} + +export function ContainerMountSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('ContainerMount').optional(), + destination: z.string(), + driver: z.string(), + mode: z.string(), + name: z.string(), + propagation: z.string(), + rw: z.boolean(), + source: z.string(), + type: z.string() + }) +} + +export function ContainerPortSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('ContainerPort').optional(), + ip: z.string().nullish(), + privatePort: z.number().nullish(), + publicPort: z.number().nullish(), + type: ContainerPortTypeSchema.nullish() + }) +} + +export const ContainerPortTypeSchema = z.nativeEnum(ContainerPortType); + +export const ContainerStateSchema = z.nativeEnum(ContainerState); + +export function DeviceSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Device').optional(), + device: z.string().nullish(), + id: z.string(), + sectorSize: z.string().nullish(), + sectors: z.string().nullish(), + tag: z.string().nullish() + }) +} + +export function DevicesSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Devices').optional(), + gpu: z.array(GpuSchema().nullable()).nullish(), + network: z.array(NetworkSchema().nullable()).nullish(), + pci: z.array(PciSchema().nullable()).nullish(), + usb: z.array(UsbSchema().nullable()).nullish() + }) +} + +export function DiskSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Disk').optional(), + bytesPerSector: z.number(), + device: z.string(), + firmwareRevision: z.string(), + interfaceType: DiskInterfaceTypeSchema, + name: z.string(), + partitions: z.array(DiskPartitionSchema()).nullish(), + sectorsPerTrack: z.number(), + serialNum: z.string(), + size: z.number(), + smartStatus: DiskSmartStatusSchema, + temperature: z.number(), + totalCylinders: z.number(), + totalHeads: z.number(), + totalSectors: z.number(), + totalTracks: z.number(), + tracksPerCylinder: z.number(), + type: z.string(), + vendor: z.string() + }) +} + +export const DiskFsTypeSchema = z.nativeEnum(DiskFsType); + +export const DiskInterfaceTypeSchema = z.nativeEnum(DiskInterfaceType); + +export function DiskPartitionSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('DiskPartition').optional(), + fsType: DiskFsTypeSchema, + name: z.string(), + size: z.number() + }) +} + +export const DiskSmartStatusSchema = z.nativeEnum(DiskSmartStatus); + +export function DisplaySchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Display').optional(), + banner: z.string().nullish(), + case: CaseSchema().nullish(), + critical: z.number().nullish(), + dashapps: z.string().nullish(), + date: z.string().nullish(), + hot: z.number().nullish(), + locale: z.string().nullish(), + max: z.number().nullish(), + number: z.string().nullish(), + resize: z.boolean().nullish(), + scale: z.boolean().nullish(), + tabs: z.boolean().nullish(), + text: z.boolean().nullish(), + theme: ThemeSchema.nullish(), + total: z.boolean().nullish(), + unit: TemperatureSchema.nullish(), + usage: z.boolean().nullish(), + users: z.string().nullish(), + warning: z.number().nullish(), + wwn: z.boolean().nullish() + }) +} + +export function DockerContainerSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('DockerContainer').optional(), + autoStart: z.boolean(), + command: z.string(), + created: z.number(), + hostConfig: ContainerHostConfigSchema().nullish(), + id: z.string(), + image: z.string(), + imageId: z.string(), + labels: definedNonNullAnySchema.nullish(), + mounts: z.array(definedNonNullAnySchema.nullable()).nullish(), + names: z.array(z.string()).nullish(), + networkSettings: definedNonNullAnySchema.nullish(), + ports: z.array(ContainerPortSchema()), + sizeRootFs: z.number().nullish(), + state: ContainerStateSchema, + status: z.string() + }) +} + +export function DockerNetworkSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('DockerNetwork').optional(), + attachable: z.boolean(), + configFrom: definedNonNullAnySchema.nullish(), + configOnly: z.boolean(), + containers: definedNonNullAnySchema.nullish(), + created: z.string().nullish(), + driver: z.string().nullish(), + enableIPv6: z.boolean(), + id: z.string().nullish(), + ingress: z.boolean(), + internal: z.boolean(), + ipam: definedNonNullAnySchema.nullish(), + labels: definedNonNullAnySchema.nullish(), + name: z.string().nullish(), + options: definedNonNullAnySchema.nullish(), + scope: z.string().nullish() + }) +} + +export function FlashSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Flash').optional(), + guid: z.string().nullish(), + product: z.string().nullish(), + vendor: z.string().nullish() + }) +} + +export function GpuSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Gpu').optional(), + blacklisted: z.boolean(), + class: z.string(), + id: z.string(), + productid: z.string(), + type: z.string(), + typeid: z.string(), + vendorname: z.string() + }) +} + +export const ImportanceSchema = z.nativeEnum(Importance); + +export function InfoSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Info').optional(), + apps: InfoAppsSchema().nullish(), + baseboard: BaseboardSchema().nullish(), + cpu: InfoCpuSchema().nullish(), + devices: DevicesSchema().nullish(), + display: DisplaySchema().nullish(), + machineId: z.string().nullish(), + memory: InfoMemorySchema().nullish(), + os: OsSchema().nullish(), + system: SystemSchema().nullish(), + versions: VersionsSchema().nullish() + }) +} + +export function InfoAppsSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('InfoApps').optional(), + installed: z.number().nullish(), + started: z.number().nullish() + }) +} + +export function InfoCpuSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('InfoCpu').optional(), + brand: z.string(), + cache: definedNonNullAnySchema, + cores: z.number(), + family: z.string(), + flags: z.array(z.string()).nullish(), + manufacturer: z.string(), + model: z.string(), + processors: z.number(), + revision: z.string(), + socket: z.string(), + speed: z.number(), + speedmax: z.number(), + speedmin: z.number(), + stepping: z.number(), + threads: z.number(), + vendor: z.string(), + voltage: z.string().nullish() + }) +} + +export function InfoMemorySchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('InfoMemory').optional(), + active: z.number(), + available: z.number(), + buffcache: z.number(), + free: z.number(), + layout: z.array(MemoryLayoutSchema()).nullish(), + max: z.number(), + swapfree: z.number(), + swaptotal: z.number(), + swapused: z.number(), + total: z.number(), + used: z.number() + }) +} + +export function KeyFileSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('KeyFile').optional(), + contents: z.string().nullish(), + location: z.string().nullish() + }) +} + +export function MeSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Me').optional(), + description: z.string(), + id: z.string(), + name: z.string(), + permissions: definedNonNullAnySchema.nullish(), + role: z.string() + }) +} + +export const MemoryFormFactorSchema = z.nativeEnum(MemoryFormFactor); + +export function MemoryLayoutSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('MemoryLayout').optional(), + bank: z.string().nullish(), + clockSpeed: z.number().nullish(), + formFactor: MemoryFormFactorSchema.nullish(), + manufacturer: z.string().nullish(), + partNum: z.string().nullish(), + serialNum: z.string().nullish(), + size: z.number(), + type: MemoryTypeSchema.nullish(), + voltageConfigured: z.number().nullish(), + voltageMax: z.number().nullish(), + voltageMin: z.number().nullish() + }) +} + +export const MemoryTypeSchema = z.nativeEnum(MemoryType); + +export const MinigraphStatusSchema = z.nativeEnum(MinigraphStatus); + +export function MinigraphqlResponseSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('MinigraphqlResponse').optional(), + error: z.string().nullish(), + status: MinigraphStatusSchema, + timeout: z.number().nullish() + }) +} + +export function MountSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Mount').optional(), + directory: z.string().nullish(), + name: z.string().nullish(), + permissions: z.string().nullish(), + type: z.string().nullish() + }) +} + +export function NetworkSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Network').optional(), + carrierChanges: z.string().nullish(), + duplex: z.string().nullish(), + iface: z.string().nullish(), + ifaceName: z.string().nullish(), + internal: z.string().nullish(), + ipv4: z.string().nullish(), + ipv6: z.string().nullish(), + mac: z.string().nullish(), + mtu: z.string().nullish(), + operstate: z.string().nullish(), + speed: z.string().nullish(), + type: z.string().nullish() + }) +} + +export function NotificationSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Notification').optional(), + description: z.string(), + id: z.string(), + importance: ImportanceSchema, + link: z.string().nullish(), + subject: z.string(), + timestamp: z.string().nullish(), + title: z.string(), + type: NotificationTypeSchema + }) +} + +export function NotificationFilterSchema(): z.ZodObject> { + return z.object>({ + importance: ImportanceSchema.nullish(), + type: NotificationTypeSchema.nullish() + }) +} + +export function NotificationInputSchema(): z.ZodObject> { + return z.object>({ + description: z.string().nullish(), + id: z.string(), + importance: ImportanceSchema, + link: z.string().nullish(), + subject: z.string(), + timestamp: z.string().nullish(), + title: z.string(), + type: NotificationTypeSchema + }) +} + +export const NotificationTypeSchema = z.nativeEnum(NotificationType); + +export function OsSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Os').optional(), + arch: z.string().nullish(), + build: z.string().nullish(), + codename: z.string().nullish(), + codepage: z.string().nullish(), + distro: z.string().nullish(), + hostname: z.string().nullish(), + kernel: z.string().nullish(), + logofile: z.string().nullish(), + platform: z.string().nullish(), + release: z.string().nullish(), + serial: z.string().nullish(), + uptime: z.string().nullish() + }) +} + +export function OwnerSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Owner').optional(), + avatar: z.string().nullish(), + url: z.string().nullish(), + username: z.string().nullish() + }) +} + +export function ParityCheckSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('ParityCheck').optional(), + date: z.string(), + duration: z.number(), + errors: z.string(), + speed: z.string(), + status: z.string() + }) +} + +export function PartitionSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Partition').optional(), + devlinks: z.string().nullish(), + devname: z.string().nullish(), + devpath: z.string().nullish(), + devtype: z.string().nullish(), + idAta: z.string().nullish(), + idAtaDownloadMicrocode: z.string().nullish(), + idAtaFeatureSetAam: z.string().nullish(), + idAtaFeatureSetAamCurrentValue: z.string().nullish(), + idAtaFeatureSetAamEnabled: z.string().nullish(), + idAtaFeatureSetAamVendorRecommendedValue: z.string().nullish(), + idAtaFeatureSetApm: z.string().nullish(), + idAtaFeatureSetApmCurrentValue: z.string().nullish(), + idAtaFeatureSetApmEnabled: z.string().nullish(), + idAtaFeatureSetHpa: z.string().nullish(), + idAtaFeatureSetHpaEnabled: z.string().nullish(), + idAtaFeatureSetPm: z.string().nullish(), + idAtaFeatureSetPmEnabled: z.string().nullish(), + idAtaFeatureSetPuis: z.string().nullish(), + idAtaFeatureSetPuisEnabled: z.string().nullish(), + idAtaFeatureSetSecurity: z.string().nullish(), + idAtaFeatureSetSecurityEnabled: z.string().nullish(), + idAtaFeatureSetSecurityEnhancedEraseUnitMin: z.string().nullish(), + idAtaFeatureSetSecurityEraseUnitMin: z.string().nullish(), + idAtaFeatureSetSmart: z.string().nullish(), + idAtaFeatureSetSmartEnabled: z.string().nullish(), + idAtaRotationRateRpm: z.string().nullish(), + idAtaSata: z.string().nullish(), + idAtaSataSignalRateGen1: z.string().nullish(), + idAtaSataSignalRateGen2: z.string().nullish(), + idAtaWriteCache: z.string().nullish(), + idAtaWriteCacheEnabled: z.string().nullish(), + idBus: z.string().nullish(), + idFsType: z.string().nullish(), + idFsUsage: z.string().nullish(), + idFsUuid: z.string().nullish(), + idFsUuidEnc: z.string().nullish(), + idModel: z.string().nullish(), + idModelEnc: z.string().nullish(), + idPartEntryDisk: z.string().nullish(), + idPartEntryNumber: z.string().nullish(), + idPartEntryOffset: z.string().nullish(), + idPartEntryScheme: z.string().nullish(), + idPartEntrySize: z.string().nullish(), + idPartEntryType: z.string().nullish(), + idPartTableType: z.string().nullish(), + idPath: z.string().nullish(), + idPathTag: z.string().nullish(), + idRevision: z.string().nullish(), + idSerial: z.string().nullish(), + idSerialShort: z.string().nullish(), + idType: z.string().nullish(), + idWwn: z.string().nullish(), + idWwnWithExtension: z.string().nullish(), + major: z.string().nullish(), + minor: z.string().nullish(), + partn: z.string().nullish(), + subsystem: z.string().nullish(), + usecInitialized: z.string().nullish() + }) +} + +export function PciSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Pci').optional(), + blacklisted: z.string().nullish(), + class: z.string().nullish(), + id: z.string(), + productid: z.string().nullish(), + productname: z.string().nullish(), + type: z.string().nullish(), + typeid: z.string().nullish(), + vendorid: z.string().nullish(), + vendorname: z.string().nullish() + }) +} + +export function PermissionsSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Permissions').optional(), + grants: definedNonNullAnySchema.nullish(), + scopes: definedNonNullAnySchema.nullish() + }) +} + +export function ProfileModelSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('ProfileModel').optional(), + avatar: z.string().nullish(), + url: z.string().nullish(), + userId: z.string().nullish(), + username: z.string().nullish() + }) +} + +export function RegistrationSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Registration').optional(), + expiration: z.string().nullish(), + guid: z.string().nullish(), + keyFile: KeyFileSchema().nullish(), + state: RegistrationStateSchema.nullish(), + type: registrationTypeSchema.nullish() + }) +} + +export const RegistrationStateSchema = z.nativeEnum(RegistrationState); + +export function RelayResponseSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('RelayResponse').optional(), + error: z.string().nullish(), + status: z.string(), + timeout: z.string().nullish() + }) +} + +export function ScopeSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Scope').optional(), + description: z.string().nullish(), + name: z.string().nullish() + }) +} + +export function ServerSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Server').optional(), + apikey: z.string(), + guid: z.string(), + lanip: z.string(), + localurl: z.string(), + name: z.string(), + owner: ProfileModelSchema(), + remoteurl: z.string(), + status: ServerStatusSchema, + wanip: z.string() + }) +} + +export const ServerStatusSchema = z.nativeEnum(ServerStatus); + +export function ServiceSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Service').optional(), + name: z.string().nullish(), + online: z.boolean().nullish(), + uptime: UptimeSchema().nullish(), + version: z.string().nullish() + }) +} + +export function ShareSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Share').optional(), + allocator: z.string().nullish(), + cache: z.boolean().nullish(), + color: z.string().nullish(), + comment: z.string().nullish(), + cow: z.string().nullish(), + exclude: z.array(z.string().nullable()).nullish(), + floor: z.string().nullish(), + free: z.number().nullish(), + include: z.array(z.string().nullable()).nullish(), + luksStatus: z.string().nullish(), + name: z.string().nullish(), + nameOrig: z.string().nullish(), + size: z.number().nullish(), + splitLevel: z.string().nullish(), + used: z.number().nullish() + }) +} + +export function SystemSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('System').optional(), + manufacturer: z.string().nullish(), + model: z.string().nullish(), + serial: z.string().nullish(), + sku: z.string().nullish(), + uuid: z.string().nullish(), + version: z.string().nullish() + }) +} + +export const TemperatureSchema = z.nativeEnum(Temperature); + +export const ThemeSchema = z.nativeEnum(Theme); + +export function TwoFactorLocalSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('TwoFactorLocal').optional(), + enabled: z.boolean().nullish() + }) +} + +export function TwoFactorRemoteSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('TwoFactorRemote').optional(), + enabled: z.boolean().nullish() + }) +} + +export function TwoFactorWithTokenSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('TwoFactorWithToken').optional(), + local: TwoFactorLocalSchema().nullish(), + remote: TwoFactorRemoteSchema().nullish(), + token: z.string().nullish() + }) +} + +export function TwoFactorWithoutTokenSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('TwoFactorWithoutToken').optional(), + local: TwoFactorLocalSchema().nullish(), + remote: TwoFactorRemoteSchema().nullish() + }) +} + +export function UnassignedDeviceSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('UnassignedDevice').optional(), + devlinks: z.string().nullish(), + devname: z.string().nullish(), + devpath: z.string().nullish(), + devtype: z.string().nullish(), + idAta: z.string().nullish(), + idAtaDownloadMicrocode: z.string().nullish(), + idAtaFeatureSetAam: z.string().nullish(), + idAtaFeatureSetAamCurrentValue: z.string().nullish(), + idAtaFeatureSetAamEnabled: z.string().nullish(), + idAtaFeatureSetAamVendorRecommendedValue: z.string().nullish(), + idAtaFeatureSetApm: z.string().nullish(), + idAtaFeatureSetApmCurrentValue: z.string().nullish(), + idAtaFeatureSetApmEnabled: z.string().nullish(), + idAtaFeatureSetHpa: z.string().nullish(), + idAtaFeatureSetHpaEnabled: z.string().nullish(), + idAtaFeatureSetPm: z.string().nullish(), + idAtaFeatureSetPmEnabled: z.string().nullish(), + idAtaFeatureSetPuis: z.string().nullish(), + idAtaFeatureSetPuisEnabled: z.string().nullish(), + idAtaFeatureSetSecurity: z.string().nullish(), + idAtaFeatureSetSecurityEnabled: z.string().nullish(), + idAtaFeatureSetSecurityEnhancedEraseUnitMin: z.string().nullish(), + idAtaFeatureSetSecurityEraseUnitMin: z.string().nullish(), + idAtaFeatureSetSmart: z.string().nullish(), + idAtaFeatureSetSmartEnabled: z.string().nullish(), + idAtaRotationRateRpm: z.string().nullish(), + idAtaSata: z.string().nullish(), + idAtaSataSignalRateGen1: z.string().nullish(), + idAtaSataSignalRateGen2: z.string().nullish(), + idAtaWriteCache: z.string().nullish(), + idAtaWriteCacheEnabled: z.string().nullish(), + idBus: z.string().nullish(), + idModel: z.string().nullish(), + idModelEnc: z.string().nullish(), + idPartTableType: z.string().nullish(), + idPath: z.string().nullish(), + idPathTag: z.string().nullish(), + idRevision: z.string().nullish(), + idSerial: z.string().nullish(), + idSerialShort: z.string().nullish(), + idType: z.string().nullish(), + idWwn: z.string().nullish(), + idWwnWithExtension: z.string().nullish(), + major: z.string().nullish(), + minor: z.string().nullish(), + mount: MountSchema().nullish(), + mounted: z.boolean().nullish(), + name: z.string().nullish(), + partitions: z.array(PartitionSchema().nullable()).nullish(), + subsystem: z.string().nullish(), + temp: z.number().nullish(), + usecInitialized: z.string().nullish() + }) +} + +export function UptimeSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Uptime').optional(), + timestamp: z.string().nullish() + }) +} + +export function UsbSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Usb').optional(), + id: z.string(), + name: z.string().nullish() + }) +} + +export function UserSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('User').optional(), + description: z.string(), + id: z.string(), + name: z.string(), + password: z.boolean().nullish(), + role: z.string() + }) +} + +export function VarsSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Vars').optional(), + bindMgt: z.boolean().nullish(), + cacheNumDevices: z.number().nullish(), + cacheSbNumDisks: z.number().nullish(), + comment: z.string().nullish(), + configError: ConfigErrorStateSchema.nullish(), + configValid: z.boolean().nullish(), + csrfToken: z.string().nullish(), + defaultFormat: z.string().nullish(), + defaultFsType: z.string().nullish(), + deviceCount: z.number().nullish(), + domain: z.string().nullish(), + domainLogin: z.string().nullish(), + domainShort: z.string().nullish(), + enableFruit: z.string().nullish(), + flashGuid: z.string().nullish(), + flashProduct: z.string().nullish(), + flashVendor: z.string().nullish(), + fsCopyPrcnt: z.number().nullish(), + fsNumMounted: z.number().nullish(), + fsNumUnmountable: z.number().nullish(), + fsProgress: z.string().nullish(), + fsState: z.string().nullish(), + fsUnmountableMask: z.string().nullish(), + fuseDirectio: z.string().nullish(), + fuseDirectioDefault: z.string().nullish(), + fuseDirectioStatus: z.string().nullish(), + fuseRemember: z.string().nullish(), + fuseRememberDefault: z.string().nullish(), + fuseRememberStatus: z.string().nullish(), + hideDotFiles: z.boolean().nullish(), + joinStatus: z.string().nullish(), + localMaster: z.boolean().nullish(), + localTld: z.string().nullish(), + luksKeyfile: z.string().nullish(), + maxArraysz: z.number().nullish(), + maxCachesz: z.number().nullish(), + mdColor: z.string().nullish(), + mdNumDisabled: z.number().nullish(), + mdNumDisks: z.number().nullish(), + mdNumErased: z.number().nullish(), + mdNumInvalid: z.number().nullish(), + mdNumMissing: z.number().nullish(), + mdNumNew: z.number().nullish(), + mdNumStripes: z.number().nullish(), + mdNumStripesDefault: z.number().nullish(), + mdNumStripesStatus: z.string().nullish(), + mdResync: z.number().nullish(), + mdResyncAction: z.string().nullish(), + mdResyncCorr: z.string().nullish(), + mdResyncDb: z.string().nullish(), + mdResyncDt: z.string().nullish(), + mdResyncPos: z.string().nullish(), + mdResyncSize: z.number().nullish(), + mdState: z.string().nullish(), + mdSyncThresh: z.number().nullish(), + mdSyncThreshDefault: z.number().nullish(), + mdSyncThreshStatus: z.string().nullish(), + mdSyncWindow: z.number().nullish(), + mdSyncWindowDefault: z.number().nullish(), + mdSyncWindowStatus: z.string().nullish(), + mdVersion: z.string().nullish(), + mdWriteMethod: z.number().nullish(), + mdWriteMethodDefault: z.string().nullish(), + mdWriteMethodStatus: z.string().nullish(), + name: z.string().nullish(), + nrRequests: z.number().nullish(), + nrRequestsDefault: z.number().nullish(), + nrRequestsStatus: z.string().nullish(), + ntpServer1: z.string().nullish(), + ntpServer2: z.string().nullish(), + ntpServer3: z.string().nullish(), + ntpServer4: z.string().nullish(), + pollAttributes: z.string().nullish(), + pollAttributesDefault: z.string().nullish(), + pollAttributesStatus: z.string().nullish(), + port: z.number().nullish(), + portssh: z.number().nullish(), + portssl: z.number().nullish(), + porttelnet: z.number().nullish(), + queueDepth: z.string().nullish(), + regCheck: z.string().nullish(), + regFile: z.string().nullish(), + regGen: z.string().nullish(), + regGuid: z.string().nullish(), + regState: RegistrationStateSchema.nullish(), + regTm: z.string().nullish(), + regTm2: z.string().nullish(), + regTo: z.string().nullish(), + regTy: z.string().nullish(), + safeMode: z.boolean().nullish(), + sbClean: z.boolean().nullish(), + sbEvents: z.number().nullish(), + sbName: z.string().nullish(), + sbNumDisks: z.number().nullish(), + sbState: z.string().nullish(), + sbSyncErrs: z.number().nullish(), + sbSyncExit: z.string().nullish(), + sbSynced: z.number().nullish(), + sbSynced2: z.number().nullish(), + sbUpdated: z.string().nullish(), + sbVersion: z.string().nullish(), + security: z.string().nullish(), + shareAfpCount: z.number().nullish(), + shareAfpEnabled: z.boolean().nullish(), + shareAvahiAfpModel: z.string().nullish(), + shareAvahiAfpName: z.string().nullish(), + shareAvahiEnabled: z.boolean().nullish(), + shareAvahiSmbModel: z.string().nullish(), + shareAvahiSmbName: z.string().nullish(), + shareCacheEnabled: z.boolean().nullish(), + shareCacheFloor: z.string().nullish(), + shareCount: z.number().nullish(), + shareDisk: z.string().nullish(), + shareInitialGroup: z.string().nullish(), + shareInitialOwner: z.string().nullish(), + shareMoverActive: z.boolean().nullish(), + shareMoverLogging: z.boolean().nullish(), + shareMoverSchedule: z.string().nullish(), + shareNfsCount: z.number().nullish(), + shareNfsEnabled: z.boolean().nullish(), + shareSmbCount: z.number().nullish(), + shareSmbEnabled: z.boolean().nullish(), + shareUser: z.string().nullish(), + shareUserExclude: z.string().nullish(), + shareUserInclude: z.string().nullish(), + shutdownTimeout: z.number().nullish(), + spindownDelay: z.string().nullish(), + spinupGroups: z.boolean().nullish(), + startArray: z.boolean().nullish(), + startMode: z.string().nullish(), + startPage: z.string().nullish(), + sysArraySlots: z.number().nullish(), + sysCacheSlots: z.number().nullish(), + sysFlashSlots: z.number().nullish(), + sysModel: z.string().nullish(), + timeZone: z.string().nullish(), + useNtp: z.boolean().nullish(), + useSsh: z.boolean().nullish(), + useSsl: z.boolean().nullish(), + useTelnet: z.boolean().nullish(), + version: z.string().nullish(), + workgroup: z.string().nullish() + }) +} + +export function VersionsSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Versions').optional(), + apache: z.string().nullish(), + docker: z.string().nullish(), + gcc: z.string().nullish(), + git: z.string().nullish(), + grunt: z.string().nullish(), + gulp: z.string().nullish(), + kernel: z.string().nullish(), + mongodb: z.string().nullish(), + mysql: z.string().nullish(), + nginx: z.string().nullish(), + node: z.string().nullish(), + npm: z.string().nullish(), + openssl: z.string().nullish(), + perl: z.string().nullish(), + php: z.string().nullish(), + pm2: z.string().nullish(), + postfix: z.string().nullish(), + postgresql: z.string().nullish(), + python: z.string().nullish(), + redis: z.string().nullish(), + systemOpenssl: z.string().nullish(), + systemOpensslLib: z.string().nullish(), + tsc: z.string().nullish(), + unraid: z.string().nullish(), + v8: z.string().nullish(), + yarn: z.string().nullish() + }) +} + +export function VmDomainSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('VmDomain').optional(), + name: z.string().nullish(), + state: VmStateSchema, + uuid: z.string() + }) +} + +export function VmNetworkSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('VmNetwork').optional(), + _placeholderType: z.string().nullish() + }) +} + +export const VmStateSchema = z.nativeEnum(VmState); + +export function VmsSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Vms').optional(), + domain: z.array(VmDomainSchema()).nullish() + }) +} + +export function WelcomeSchema(): z.ZodObject> { + return z.object>({ + __typename: z.literal('Welcome').optional(), + message: z.string() + }) +} + +export function addApiKeyInputSchema(): z.ZodObject> { + return z.object>({ + key: z.string().nullish(), + name: z.string().nullish(), + userId: z.string().nullish() + }) +} + +export function addScopeInputSchema(): z.ZodObject> { + return z.object>({ + description: z.string().nullish(), + name: z.string() + }) +} + +export function addScopeToApiKeyInputSchema(): z.ZodObject> { + return z.object>({ + apiKey: z.string(), + name: z.string() + }) +} + +export function addUserInputSchema(): z.ZodObject> { + return z.object>({ + description: z.string().nullish(), + name: z.string(), + password: z.string() + }) +} + +export function arrayDiskInputSchema(): z.ZodObject> { + return z.object>({ + id: z.string(), + slot: z.number().nullish() + }) +} + +export function authenticateInputSchema(): z.ZodObject> { + return z.object>({ + password: z.string() + }) +} + +export function deleteUserInputSchema(): z.ZodObject> { + return z.object>({ + name: z.string() + }) +} + +export const mdStateSchema = z.nativeEnum(mdState); + +export const registrationTypeSchema = z.nativeEnum(registrationType); + +export function testMutationInputSchema(): z.ZodObject> { + return z.object>({ + state: z.string() + }) +} + +export function testQueryInputSchema(): z.ZodObject> { + return z.object>({ + optional: z.boolean().nullish(), + state: z.string() + }) +} + +export function updateApikeyInputSchema(): z.ZodObject> { + return z.object>({ + description: z.string().nullish(), + expiresAt: z.number() + }) +} + +export function usersInputSchema(): z.ZodObject> { + return z.object>({ + slim: z.boolean().nullish() + }) +} + +export type getCloudQueryVariables = Types.Exact<{ [key: string]: never; }>; + + +export type getCloudQuery = { __typename?: 'Query', cloud?: { __typename?: 'Cloud', error?: string | null, allowedOrigins: Array, apiKey: { __typename?: 'ApiKeyResponse', valid: boolean, error?: string | null }, minigraphql: { __typename?: 'MinigraphqlResponse', status: Types.MinigraphStatus, timeout?: number | null, error?: string | null }, cloud: { __typename?: 'CloudResponse', status: string, error?: string | null, ip?: string | null } } | null }; + +export type getServersQueryVariables = Types.Exact<{ [key: string]: never; }>; + + +export type getServersQuery = { __typename?: 'Query', servers: Array<{ __typename?: 'Server', name: string, guid: string, status: Types.ServerStatus, owner: { __typename?: 'ProfileModel', username?: string | null } }> }; + + +export const getCloudDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getCloud"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cloud"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"apiKey"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"valid"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"minigraphql"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"timeout"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cloud"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}},{"kind":"Field","name":{"kind":"Name","value":"ip"}}]}},{"kind":"Field","name":{"kind":"Name","value":"allowedOrigins"}}]}}]}}]} as unknown as DocumentNode; +export const getServersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getServers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"servers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"guid"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"owner"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"username"}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/api/src/graphql/generated/api/types.ts b/api/src/graphql/generated/api/types.ts index 00ab587a6..dc18847ce 100644 --- a/api/src/graphql/generated/api/types.ts +++ b/api/src/graphql/generated/api/types.ts @@ -695,20 +695,38 @@ export type Network = { export type Notification = { __typename?: 'Notification'; description: Scalars['String']; + id: Scalars['ID']; importance: Importance; - link: Scalars['String']; + link?: Maybe; subject: Scalars['String']; + /** ISO Timestamp for when the notification occurred */ + timestamp?: Maybe; title: Scalars['String']; + type: NotificationType; +}; + +export type NotificationFilter = { + importance?: InputMaybe; + type?: InputMaybe; }; export type NotificationInput = { description?: InputMaybe; + id: Scalars['ID']; importance: Importance; link?: InputMaybe; - subject?: InputMaybe; - title?: InputMaybe; + subject: Scalars['String']; + timestamp?: InputMaybe; + title: Scalars['String']; + type: NotificationType; }; +export enum NotificationType { + ARCHIVED = 'ARCHIVED', + RESTORED = 'RESTORED', + UNREAD = 'UNREAD' +} + export type Os = { __typename?: 'Os'; arch?: Maybe; @@ -856,6 +874,7 @@ export type Query = { info?: Maybe; /** Current user account */ me?: Maybe; + notifications: Array; online?: Maybe; owner?: Maybe; parityHistory?: Maybe>>; @@ -906,6 +925,11 @@ export type QuerydockerNetworksArgs = { }; +export type QuerynotificationsArgs = { + filter?: InputMaybe; +}; + + export type QueryserverArgs = { name: Scalars['String']; }; @@ -1068,6 +1092,7 @@ export type Subscription = { flash: Flash; info: Info; me?: Maybe; + notificationAdded: Notification; online: Scalars['Boolean']; owner: Owner; parityHistory: ParityCheck; @@ -1686,7 +1711,9 @@ export type ResolversTypes = ResolversObject<{ Mutation: ResolverTypeWrapper<{}>; Network: ResolverTypeWrapper; Notification: ResolverTypeWrapper; + NotificationFilter: NotificationFilter; NotificationInput: NotificationInput; + NotificationType: NotificationType; Os: ResolverTypeWrapper; Owner: ResolverTypeWrapper; ParityCheck: ResolverTypeWrapper; @@ -1784,6 +1811,7 @@ export type ResolversParentTypes = ResolversObject<{ Mutation: {}; Network: Network; Notification: Notification; + NotificationFilter: NotificationFilter; NotificationInput: NotificationInput; Os: Os; Owner: Owner; @@ -2251,10 +2279,13 @@ export type NetworkResolvers = ResolversObject<{ description?: Resolver; + id?: Resolver; importance?: Resolver; - link?: Resolver; + link?: Resolver, ParentType, ContextType>; subject?: Resolver; + timestamp?: Resolver, ParentType, ContextType>; title?: Resolver; + type?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }>; @@ -2396,6 +2427,7 @@ export type QueryResolvers, ParentType, ContextType>; info?: Resolver, ParentType, ContextType>; me?: Resolver, ParentType, ContextType>; + notifications?: Resolver, ParentType, ContextType, Partial>; online?: Resolver, ParentType, ContextType>; owner?: Resolver, ParentType, ContextType>; parityHistory?: Resolver>>, ParentType, ContextType>; @@ -2492,6 +2524,7 @@ export type SubscriptionResolvers; info?: SubscriptionResolver; me?: SubscriptionResolver, "me", ParentType, ContextType>; + notificationAdded?: SubscriptionResolver; online?: SubscriptionResolver; owner?: SubscriptionResolver; parityHistory?: SubscriptionResolver; diff --git a/api/src/graphql/generate/validators.ts b/api/src/graphql/generated/client/validators.ts similarity index 100% rename from api/src/graphql/generate/validators.ts rename to api/src/graphql/generated/client/validators.ts diff --git a/api/src/graphql/resolvers/query/index.ts b/api/src/graphql/resolvers/query/index.ts index 9c2ce99f4..d9e33366e 100644 --- a/api/src/graphql/resolvers/query/index.ts +++ b/api/src/graphql/resolvers/query/index.ts @@ -7,6 +7,7 @@ import { disksResolver } from '@app/graphql/resolvers/query/disks'; import display from '@app/graphql/resolvers/query/display'; import { dockerContainersResolver } from '@app/graphql/resolvers/query/docker'; import flash from '@app/graphql/resolvers/query/flash'; +import { notificationsResolver } from '@app/graphql/resolvers/query/notifications'; import online from '@app/graphql/resolvers/query/online'; import owner from '@app/graphql/resolvers/query/owner'; import { registration } from '@app/graphql/resolvers/query/registration'; @@ -24,6 +25,7 @@ export const Query: QueryResolvers = { dockerContainers: dockerContainersResolver, display, flash, + notifications: notificationsResolver, online, owner, registration, diff --git a/api/src/graphql/resolvers/query/notifications.ts b/api/src/graphql/resolvers/query/notifications.ts new file mode 100644 index 000000000..d693809c3 --- /dev/null +++ b/api/src/graphql/resolvers/query/notifications.ts @@ -0,0 +1,39 @@ +import { ensurePermission } from '@app/core/utils/permissions/ensure-permission'; +import { type QueryResolvers } from '@app/graphql/generated/api/types'; +import { getters } from '@app/store/index'; + +export const notificationsResolver: QueryResolvers['notifications'] = async ( + _, + args, + context +) => { + ensurePermission(context.user, { + possession: 'any', + resource: 'notifications', + action: 'read', + }); + + return Object.values(getters.notifications().notifications) + .filter((notification) => { + if (args.filter) { + if ( + args.filter.importance && + args.filter.importance !== notification.importance + ) { + return false; + } + if ( + args.filter.type && + args.filter.type !== notification.type + ) { + return false; + } + } + return true; + }) + .sort( + (a, b) => + new Date(b.timestamp ?? 0).getTime() - + new Date(a.timestamp ?? 0).getTime() + ); +}; diff --git a/api/src/graphql/resolvers/subscription/index.ts b/api/src/graphql/resolvers/subscription/index.ts new file mode 100644 index 000000000..b27cf5bcb --- /dev/null +++ b/api/src/graphql/resolvers/subscription/index.ts @@ -0,0 +1,74 @@ +/*! + * Copyright 2019-2022 Lime Technology Inc. All rights reserved. + * Written by: Alexis Tyler + */ + +import { PUBSUB_CHANNEL, pubsub } from '@app/core/pubsub'; +import { ensurePermission } from '@app/core/utils/permissions/ensure-permission'; +import { type Resolvers } from '@app/graphql/generated/api/types'; +import { createSubscription } from '@app/graphql/schema/utils'; + +export const Subscription: Resolvers['Subscription'] = { + display: { + ...createSubscription('display'), + }, + apikeys: { + // Not sure how we're going to secure this + // ...createSubscription('apikeys') + }, + config: { + ...createSubscription('config'), + }, + array: { + ...createSubscription('array'), + }, + dockerContainers: { + ...createSubscription('docker/container'), + }, + dockerNetworks: { + ...createSubscription('docker/network'), + }, + notificationAdded: { + subscribe: (_parent, _args, context) => { + ensurePermission(context.user, { + possession: 'any', + resource: 'notifications', + action: 'read', + }); + return { + [Symbol.asyncIterator]: () => + pubsub.asyncIterator(PUBSUB_CHANNEL.NOTIFICATION), + }; + }, + }, + info: { + ...createSubscription('info'), + }, + servers: { + ...createSubscription('servers'), + }, + shares: { + ...createSubscription('shares'), + }, + unassignedDevices: { + ...createSubscription('devices/unassigned'), + }, + users: { + ...createSubscription('users'), + }, + vars: { + ...createSubscription('vars'), + }, + vms: { + ...createSubscription('vms'), + }, + registration: { + ...createSubscription('registration'), + }, + online: { + ...createSubscription('online'), + }, + owner: { + ...createSubscription('owner'), + }, +}; diff --git a/api/src/graphql/resolvers/subscription/network.ts b/api/src/graphql/resolvers/subscription/network.ts index 268e75181..8084cfe73 100644 --- a/api/src/graphql/resolvers/subscription/network.ts +++ b/api/src/graphql/resolvers/subscription/network.ts @@ -7,7 +7,7 @@ import { isEqual } from 'lodash'; import { SEND_NETWORK_MUTATION } from '@app/graphql/mothership/mutations'; import { saveNetworkPacket } from '@app/store/modules/dashboard'; import { ApolloError } from '@apollo/client/core/core.cjs'; -import { AccessUrlInputSchema, NetworkInputSchema } from '@app/graphql/generate/validators'; +import { AccessUrlInputSchema, NetworkInputSchema } from '@app/graphql/generated/client/validators'; import { ZodError } from 'zod'; interface UrlForFieldInput { diff --git a/api/src/graphql/schema/types/notifications/notifications.graphql b/api/src/graphql/schema/types/notifications/notifications.graphql new file mode 100644 index 000000000..1b32efd84 --- /dev/null +++ b/api/src/graphql/schema/types/notifications/notifications.graphql @@ -0,0 +1,51 @@ +enum NotificationType { + UNREAD + ARCHIVED + RESTORED +} + +input NotificationInput { + id: ID! + title: String! + subject: String! + description: String + importance: Importance! + link: String + type: NotificationType! + timestamp: String +} + +input NotificationFilter { + importance: Importance + type: NotificationType +} + +type Mutation { + sendNotification(notification: NotificationInput!): Notification +} + +type Query { + notifications(filter: NotificationFilter): [Notification!]! +} + +type Subscription { + notificationAdded: Notification! +} + +enum Importance { + ALERT + INFO + WARNING +} + +type Notification { + id: ID! + title: String! + subject: String! + description: String! + importance: Importance! + link: String + type: NotificationType! + """ ISO Timestamp for when the notification occurred """ + timestamp: String +} diff --git a/api/src/index.ts b/api/src/index.ts index 1c25aaadf..d3c818178 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -23,9 +23,9 @@ import { PORT, environment } from '@app/environment'; import { shutdownApiEvent } from '@app/store/actions/shutdown-api-event'; import { PingTimeoutJobs } from '@app/mothership/jobs/ping-timeout-jobs'; import { type BaseContext, type ApolloServer } from '@apollo/server'; -import { loadDynamixConfigFile } from '@app/store/modules/dynamix'; import { setupDynamixConfigWatch } from '@app/store/watch/dynamix-config-watch'; import { setupVarRunWatch } from '@app/store/watch/var-run-watch'; +import { loadDynamixConfigFile } from '@app/store/actions/load-dynamix-config-file'; let server: ApolloServer; diff --git a/api/src/store/actions/load-dynamix-config-file.ts b/api/src/store/actions/load-dynamix-config-file.ts new file mode 100644 index 000000000..fd37b48df --- /dev/null +++ b/api/src/store/actions/load-dynamix-config-file.ts @@ -0,0 +1,33 @@ +import { parseConfig } from '@app/core/utils/misc/parse-config'; +import { + createAsyncThunk, +} from '@reduxjs/toolkit'; +import { access } from 'fs/promises'; +import { F_OK } from 'constants'; +import { type RecursivePartial, type RecursiveNullable } from '@app/types'; +import { type DynamixConfig } from '@app/core/types/ini'; + +/** + * Load the dynamix.cfg into the store. + * + * Note: If the file doesn't exist this will fallback to default values. + */ +export const loadDynamixConfigFile = createAsyncThunk< + RecursiveNullable>, + string | undefined +>('config/load-dynamix-config-file', async (filePath) => { + const store = await import('@app/store'); + const paths = store.getters.paths(); + const path = filePath ?? paths['dynamix-config']; + const fileExists = await access(path, F_OK) + .then(() => true) + .catch(() => false); + const file: RecursivePartial = fileExists + ? parseConfig>({ + filePath: path, + type: 'ini', + }) + : {}; + + return file; +}); diff --git a/api/src/store/index.ts b/api/src/store/index.ts index c23166ac0..1ac013890 100644 --- a/api/src/store/index.ts +++ b/api/src/store/index.ts @@ -13,42 +13,46 @@ import { apiKeyReducer } from '@app/store/modules/apikey'; import { dynamicRemoteAccessReducer } from '@app/store/modules/dynamic-remote-access'; import { remoteGraphQLReducer } from '@app/store/modules/remote-graphql'; import { dynamix } from '@app/store/modules/dynamix'; +import { notificationReducer } from '@app/store/modules/notifications'; export const store = configureStore({ - reducer: { - apiKey: apiKeyReducer, - config: configReducer, - dynamicRemoteAccess: dynamicRemoteAccessReducer, - minigraph: mothership.reducer, - paths: paths.reducer, - emhttp: emhttp.reducer, - registration: registration.reducer, - remoteGraphQL: remoteGraphQLReducer, - cache: cache.reducer, - dashboard: dashboard.reducer, - docker: docker.reducer, - upnp: upnp.reducer, - dynamix: dynamix.reducer, - }, - middleware: getDefaultMiddleware => getDefaultMiddleware({ - serializableCheck: false, - }).prepend(listenerMiddleware.middleware), + reducer: { + apiKey: apiKeyReducer, + config: configReducer, + dynamicRemoteAccess: dynamicRemoteAccessReducer, + minigraph: mothership.reducer, + paths: paths.reducer, + emhttp: emhttp.reducer, + registration: registration.reducer, + remoteGraphQL: remoteGraphQLReducer, + notifications: notificationReducer, + cache: cache.reducer, + dashboard: dashboard.reducer, + docker: docker.reducer, + upnp: upnp.reducer, + dynamix: dynamix.reducer, + }, + middleware: (getDefaultMiddleware) => + getDefaultMiddleware({ + serializableCheck: false, + }).prepend(listenerMiddleware.middleware), }); export type RootState = ReturnType; export type AppDispatch = typeof store.dispatch; export const getters = { - apiKey: () => store.getState().apiKey, - config: () => store.getState().config, - minigraph: () => store.getState().minigraph, - paths: () => store.getState().paths, - emhttp: () => store.getState().emhttp, - registration: () => store.getState().registration, - remoteGraphQL: () => store.getState().remoteGraphQL, - cache: () => store.getState().cache, - dashboard: () => store.getState().dashboard, - docker: () => store.getState().docker, - upnp: () => store.getState().upnp, - dynamix: () => store.getState().dynamix, + apiKey: () => store.getState().apiKey, + cache: () => store.getState().cache, + config: () => store.getState().config, + dashboard: () => store.getState().dashboard, + docker: () => store.getState().docker, + dynamix: () => store.getState().dynamix, + emhttp: () => store.getState().emhttp, + minigraph: () => store.getState().minigraph, + notifications: () => store.getState().notifications, + paths: () => store.getState().paths, + registration: () => store.getState().registration, + remoteGraphQL: () => store.getState().remoteGraphQL, + upnp: () => store.getState().upnp, }; diff --git a/api/src/store/listeners/listener-middleware.ts b/api/src/store/listeners/listener-middleware.ts new file mode 100644 index 000000000..b6aaed68d --- /dev/null +++ b/api/src/store/listeners/listener-middleware.ts @@ -0,0 +1,54 @@ +import { + addListener, + createListenerMiddleware, + type TypedAddListener, + type TypedStartListening, +} from '@reduxjs/toolkit'; +import { type AppDispatch, type RootState } from '@app/store'; +import { enableUpnpListener } from '@app/store/listeners/upnp-listener'; +import { enableAllowedOriginListener } from '@app/store/listeners/allowed-origin-listener'; +import { enableConfigFileListener } from '@app/store/listeners/config-listener'; +import { enableVersionListener } from '@app/store/listeners/version-listener'; +import { enableApiKeyListener } from '@app/store/listeners/api-key-listener'; +import { + enableLoginListener, + enableLogoutListener, +} from '@app/store/listeners/login-logout-listener'; +import { enableMothershipJobsListener } from '@app/store/listeners/mothership-subscription-listener'; +import { enableDynamicRemoteAccessListener } from '@app/store/listeners/dynamic-remote-access-listener'; +import { enableArrayEventListener } from '@app/store/listeners/array-event-listener'; +import { enableServerStateListener } from '@app/store/listeners/server-state-listener'; +import { enableWanAccessChangeListener } from '@app/store/listeners/wan-access-change-listener'; + +import 'reflect-metadata'; +import { enableNotificationPathListener } from '@app/store/listeners/notification-path-listener'; + +export const listenerMiddleware = createListenerMiddleware(); + +export type AppStartListening = TypedStartListening; + +export const startAppListening = + listenerMiddleware.startListening as AppStartListening; + +export type AppStartListeningParams = Parameters[0]; + +export const addAppListener = addListener as TypedAddListener< + RootState, + AppDispatch +>; + +// Begin listening for events +enableConfigFileListener('flash')(); +enableConfigFileListener('memory')(); +enableLoginListener(); +enableLogoutListener(); +enableApiKeyListener(); +enableUpnpListener(); +enableAllowedOriginListener(); +enableVersionListener(); +enableMothershipJobsListener(); +enableDynamicRemoteAccessListener(); +enableArrayEventListener(); +enableWanAccessChangeListener(); +enableServerStateListener(); +enableNotificationPathListener(); diff --git a/api/src/store/listeners/notification-path-listener.ts b/api/src/store/listeners/notification-path-listener.ts new file mode 100644 index 000000000..229553e81 --- /dev/null +++ b/api/src/store/listeners/notification-path-listener.ts @@ -0,0 +1,20 @@ +import { startAppListening } from '@app/store/listeners/listener-middleware'; +import { logger } from '@app/core/log'; +import { setupNotificationWatch } from '@app/core/modules/notifications/setup-notification-watch'; +import { clearAllNotifications } from '@app/store/modules/notifications'; + +export const enableNotificationPathListener = () => + startAppListening({ + predicate(_, currentState, previousState) { + if (currentState.dynamix.notify?.path !== '' && previousState.dynamix.notify?.path !== currentState.dynamix.notify?.path) { + return true; + } + + return false; + }, + async effect(_, { dispatch }) { + logger.debug('Notification Path Changed or Loaded, Recreating Watcher') + dispatch(clearAllNotifications()) + await setupNotificationWatch() + }, + }); diff --git a/api/src/store/modules/dynamix.ts b/api/src/store/modules/dynamix.ts index 646ef0702..4e1ca4c06 100644 --- a/api/src/store/modules/dynamix.ts +++ b/api/src/store/modules/dynamix.ts @@ -1,12 +1,12 @@ -import { parseConfig } from '@app/core/utils/misc/parse-config'; -import { createAsyncThunk, createSlice, type PayloadAction } from '@reduxjs/toolkit'; -import { access } from 'fs/promises'; +import { + createSlice, + type PayloadAction, +} from '@reduxjs/toolkit'; import merge from 'lodash/merge'; import { FileLoadStatus } from '@app/store/types'; -import { F_OK } from 'constants'; -import { type RecursivePartial, type RecursiveNullable } from '@app/types'; -import { toBoolean } from '@app/core/utils/casting'; +import { type RecursivePartial } from '@app/types'; import { type DynamixConfig } from '@app/core/types/ini'; +import { loadDynamixConfigFile } from '@app/store/actions/load-dynamix-config-file'; export type SliceState = { status: FileLoadStatus; @@ -16,47 +16,7 @@ export const initialState: Partial = { status: FileLoadStatus.UNLOADED, }; -/** - * Load the dynamix.cfg into the store. - * - * Note: If the file doesn't exist this will fallback to default values. - */ -export const loadDynamixConfigFile = createAsyncThunk< - RecursiveNullable>, - string | undefined ->('config/load-dynamix-config-file', async (filePath) => { - const store = await import('@app/store'); - const paths = store.getters.paths(); - const path = filePath ?? paths['dynamix-config']; - const fileExists = await access(path, F_OK) - .then(() => true) - .catch(() => false); - const file = fileExists - ? parseConfig>({ - filePath: path, - type: 'ini', - }) - : {}; - const { display } = file; - return merge(file, { - ...(display?.scale ? { scale: toBoolean(display?.scale) } : {}), - ...(display?.tabs ? { tabs: toBoolean(display?.tabs) } : {}), - ...(display?.resize ? { resize: toBoolean(display?.resize) } : {}), - ...(display?.wwn ? { wwn: toBoolean(display?.wwn) } : {}), - ...(display?.total ? { total: toBoolean(display?.total) } : {}), - ...(display?.usage ? { usage: toBoolean(display?.usage) } : {}), - ...(display?.text ? { text: toBoolean(display?.text) } : {}), - ...(display?.warning - ? { warning: Number.parseInt(display?.warning, 10) } - : {}), - ...(display?.critical - ? { critical: Number.parseInt(display?.critical, 10) } - : {}), - ...(display?.hot ? { hot: Number.parseInt(display?.hot, 10) } : {}), - ...(display?.max ? { max: Number.parseInt(display?.max, 10) } : {}), - locale: display?.locale ?? 'en_US', - }) as RecursivePartial; -}); + export const dynamix = createSlice({ name: 'dynamix', diff --git a/api/src/store/modules/notifications.ts b/api/src/store/modules/notifications.ts new file mode 100644 index 000000000..2c43961d8 --- /dev/null +++ b/api/src/store/modules/notifications.ts @@ -0,0 +1,113 @@ +import { logger } from '@app/core/log'; +import { parseConfig } from '@app/core/utils/misc/parse-config'; +import { + Importance, + NotificationType, + type Notification, + type NotificationInput, +} from '@app/graphql/generated/api/types'; +import { NotificationSchema } from '@app/graphql/generated/api/operations'; +import { type RootState, type AppDispatch } from '@app/store/index'; +import { + type PayloadAction, + createAsyncThunk, + createSlice, +} from '@reduxjs/toolkit'; +import { PUBSUB_CHANNEL, pubsub } from '@app/core/pubsub'; + +interface NotificationState { + notifications: Record; +} + +const notificationInitialState: NotificationState = { + notifications: {}, +}; + +interface NotificationIni { + timestamp: string; + event: string; + subject: string; + description: string; + importance: 'normal' | 'alert' | 'warning'; + link?: string; +} + +const fileImportanceToGqlImportance = ( + importance: NotificationIni['importance'] +): Importance => { + switch (importance) { + case 'alert': + return Importance.ALERT; + case 'warning': + return Importance.WARNING; + default: + return Importance.INFO; + } +}; + +const parseNotificationDateToIsoDate = ( + unixStringSeconds: string | undefined +): string | null => { + if (unixStringSeconds && !isNaN(Number(unixStringSeconds))) { + return new Date(Number(unixStringSeconds) * 1_000).toISOString(); + } + return null; +}; + +export const loadNotification = createAsyncThunk< + { id: string; notification: Notification }, + { path: string }, + { state: RootState; dispatch: AppDispatch } +>('notifications/loadNotification', ({ path }) => { + const notificationFile = parseConfig({ + filePath: path, + type: 'ini', + }); + + const notification: NotificationInput = { + id: path, + title: notificationFile.event, + subject: notificationFile.subject, + description: notificationFile.description ?? '', + importance: fileImportanceToGqlImportance(notificationFile.importance), + link: notificationFile.link, + timestamp: parseNotificationDateToIsoDate(notificationFile.timestamp), + type: NotificationType.UNREAD, + }; + const convertedNotification = NotificationSchema().parse(notification); + + if (convertedNotification) { + pubsub.publish(PUBSUB_CHANNEL.NOTIFICATION, { notificationAdded: convertedNotification }) + return { id: path, notification: convertedNotification }; + } + throw new Error('Failed to parse notification'); +}); + +export const notificationsStore = createSlice({ + name: 'notifications', + initialState: notificationInitialState, + reducers: { + clearNotification: (state, action: PayloadAction<{ path: string }>) => { + if (state.notifications[action.payload.path]) { + delete state.notifications[action.payload.path]; + } + }, + clearAllNotifications: (state) => { + state.notifications = {}; + } + }, + extraReducers: (builder) => { + builder.addCase(loadNotification.fulfilled, (state, { payload }) => { + state.notifications[payload.id] = payload.notification; + }); + builder.addCase(loadNotification.rejected, (_, action) => { + logger.debug( + 'Failed to load notification with error %o', + action.error + ); + }); + }, +}); + +export const notificationReducer = notificationsStore.reducer; +export const { clearNotification, clearAllNotifications } = notificationsStore.actions; diff --git a/api/src/store/store-sync.ts b/api/src/store/store-sync.ts new file mode 100644 index 000000000..f3b28cef6 --- /dev/null +++ b/api/src/store/store-sync.ts @@ -0,0 +1,57 @@ +import type { RootState } from '@app/store'; +import { store } from '@app/store'; +import { FileLoadStatus } from './types'; +import { syncRegistration } from '@app/store/sync/registration-sync'; +import { syncInfoApps } from '@app/store/sync/info-apps-sync'; +import { setupConfigPathWatch } from '@app/store/watch/config-watch'; +import { NODE_ENV } from '@app/environment'; +import { writeFileSync } from 'fs'; +import { isEqual } from 'lodash'; +import { join } from 'path'; + +export const startStoreSync = async () => { + // The last state is stored so we don't end up in a loop of writing -> reading -> writing + let lastState: RootState | null = null; + + // Update cfg when store changes + store.subscribe(async () => { + const state = store.getState(); + // Config dependent options, wait until config loads to execute + if (state.config.status === FileLoadStatus.LOADED) { + // Update 2FA + // await sync2FA(); + + // Update registration + await syncRegistration(lastState); + + // Update docker app counts + await syncInfoApps(lastState); + } + + if ( + NODE_ENV === 'development' && + !isEqual(state, lastState) && + state.paths['myservers-config-states'] + ) { + writeFileSync( + join(state.paths.states, 'config.log'), + JSON.stringify(state.config, null, 2) + ); + writeFileSync( + join(state.paths.states, 'dynamicRemoteAccess.log'), + JSON.stringify(state.dynamicRemoteAccess, null, 2) + ); + writeFileSync( + join(state.paths.states, 'graphql.log'), + JSON.stringify(state.minigraph, null, 2) + ); + writeFileSync( + join(state.paths.states, 'notifications.log'), JSON.stringify(state.notifications, null, 2) + ) + } + + lastState = state; + }); + + setupConfigPathWatch(); +}; diff --git a/api/src/store/watch/dynamix-config-watch.ts b/api/src/store/watch/dynamix-config-watch.ts index b385abc2e..97f2f4d18 100644 --- a/api/src/store/watch/dynamix-config-watch.ts +++ b/api/src/store/watch/dynamix-config-watch.ts @@ -1,6 +1,6 @@ import { getters, store } from '@app/store'; +import { loadDynamixConfigFile } from '@app/store/actions/load-dynamix-config-file'; import { watch } from 'chokidar'; -import { loadDynamixConfigFile } from '@app/store/modules/dynamix'; export const setupDynamixConfigWatch = () => { const configPath = getters.paths()?.['dynamix-config'];