1
0
Fork 0
mirror of https://github.com/bastienwirtz/homer.git synced 2026-01-23 10:25:19 +00:00

Compare commits

..

135 commits

Author SHA1 Message Date
Bastien Wirtz
6367012675 dependency updates 2026-01-17 15:17:11 +01:00
Bastien Wirtz
d7bee37405 chore: dependency updates 2025-12-14 16:01:41 +01:00
Iustin G
4cf69b3a25
Fix grammar in connectivity checks documentation (#1003)
Corrected grammatical errors in the connectivity checks section.
2025-11-23 06:36:57 -08:00
Zach Damit
2e1c7b3d27
fix: ensure smart cards re-mount correctly between pages (stable keys in ServiceGroup) (#1005)
* fix: prevent smart card reuse between pages by adding stable keys in ServiceGroup

Previously ServiceGroup keyed components only by index, causing Vue to reuse the wrong smart-card instance when navigating between pages. Keys now include groupIndex and item identity to ensure components re-mount correctly.

* Fix duplicate Service cards by ensuring unique Vue keys

The search feature was causing some Service cards to duplicate when multiple items in a group shared the same `name` value. Vue reused DOM nodes because the generated `key` for <Service> was not always unique:

This caused instability during filtering and resulted in one of the cards being duplicated repeatedly until a full page reload.
2025-11-23 06:34:31 -08:00
Bastien Wirtz
151c136923 Release version bump 2025-11-16 13:59:13 +01:00
Bastien Wirtz
2a27bee30e
Added Miniflux custom service
Co-authored-by: Moritz Kreutzer
Co-authored-by: Reiko Kaps 
Co-authored-by: igorkulman
2025-11-16 02:49:01 -08:00
Bastien Wirtz
d1356c3e6a chore: lint apply 2025-11-15 15:47:36 +01:00
Bastien Wirtz
8d82c77630 chore: dependency updates 2025-11-15 15:46:28 +01:00
dependabot[bot]
f11c14e764 chore(deps-dev): bump vite from 7.1.6 to 7.1.11
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.1.6 to 7.1.11.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.11/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.11
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-26 10:40:52 +01:00
M
62606e0caf Gatus seems to have changed the duration format from us to ns 2025-10-08 09:31:32 +02:00
Bastien Wirtz
184c16d46c chore: release version bump 2025-10-06 16:22:17 +02:00
M
ee57fa05fb Add documentation about hiding times 2025-10-06 16:20:40 +02:00
M
8249aa8ae4 Add a flag to hide the averages. They are not calculated in this case 2025-10-06 16:20:40 +02:00
M
a4ec46ee35 Remove console logging 2025-10-06 16:20:40 +02:00
M
bac62457f1 Add doc about update Interval 2025-10-06 16:20:40 +02:00
M
3913c30a56 Refactor: Allow reload. Reformat with template by joriswvanrijn 2025-10-06 16:20:40 +02:00
M
3f49479556 Simplify durations 2025-10-06 16:20:40 +02:00
M
6aa29935f6 Add missing link in docs 2025-10-06 16:20:40 +02:00
M
19c5f174e8 Add documentation on Groups 2025-10-06 16:20:40 +02:00
M
89a264563a Update dummy data for groups example 2025-10-06 16:20:40 +02:00
M
d19724b896 Add optional filtering on groups 2025-10-06 16:20:40 +02:00
M
8a598dbdc0 Add documentation 2025-10-06 16:20:40 +02:00
M
2f4bbee491 Add dummy data 2025-10-06 16:20:40 +02:00
M
7bd56d941a Add script 2025-10-06 16:20:40 +02:00
Robert Dixon
5a816709e5 Update walkxcode.scss
Addresses issue #769 where background image url is referencing <ip_address>/resources/assets/themes/walkxcode (404) rather than <ip_address>/assets/themes/walkxcode where the images are stored
2025-10-05 15:00:16 +02:00
Bastien Wirtz
81c7496264 feat: Inject package.json version into the app 2025-09-29 22:00:20 +02:00
Bastien Wirtz
4904717db0 fix(smartcard): fix transmission service rework 2025-09-28 22:06:53 +02:00
Bastien Wirtz
92a79ffdfb chore(transmission): cleanup component code 2025-09-27 15:20:57 +02:00
Igor Kulman
35e49e3d91 Update Transmission documentation to reflect code changes 2025-09-27 14:54:35 +02:00
Igor Kulman
68fb183c20 Use service mixin fetch method with custom session handling for Transmission RPC 2025-09-27 14:54:35 +02:00
Igor Kulman
9054bd8941 Use consistent auth format with auth field instead of username/password 2025-09-27 14:54:35 +02:00
Igor Kulman
b821651017 Remove endpoint validation as service mixin handles this 2025-09-27 14:54:35 +02:00
Igor Kulman
5b29bc411c Fix variable naming and bounds check in displayRate function 2025-09-27 14:54:35 +02:00
Igor Kulman
06b677ab76 Add subtitle support - display subtitle if provided, otherwise show data 2025-09-27 14:54:35 +02:00
Igor Kulman
843a814ac5 Remove showWhenEmpty option and always show data for consistency 2025-09-27 14:54:35 +02:00
Igor Kulman
1b6c3e6213 Use single interval config instead of separate rate and torrent intervals 2025-09-27 14:54:35 +02:00
Igor Kulman
90ba82de8f Add Transmission service 2025-09-27 14:54:35 +02:00
Bastien Wirtz
6f902b78c0 doc: add agent instructions file. 2025-09-27 14:42:31 +02:00
Bastien Wirtz
9aaef4eaef chore(release): version bump 2025-09-26 21:21:27 +02:00
Rodrigo Maia
e4588bc634 fix(neon-theme): add missing highlight-variant-inverted colors for better contrast
Adds --highlight-variant-inverted color definitions to both light and dark variants of the neon theme to improve text contrast against the bright neon green background.
2025-09-26 20:50:31 +02:00
Bastien Wirtz
61d5d0b722 fix: ensure independent service state between page 2025-09-21 15:45:16 +02:00
Igor Kulman
63647e837a Do not append subtitle to document title when empty 2025-09-21 15:03:02 +02:00
Bastien Wirtz
2df7d5947b chore: add ai generated data for missing mocks 2025-09-21 14:51:04 +02:00
Bastien Wirtz
8ce2daff4d chore: dependencies updates 2025-09-21 14:48:49 +02:00
Bastien Wirtz
1f2c2058f6 doc: cosmetic update on Traefic documentation 2025-09-21 14:44:44 +02:00
3thibaut1304
a36634c237 update customeservices docs 2025-09-21 14:42:20 +02:00
3thibaut1304
ee152fd202 fix service traefik with basic auth 2025-09-21 14:42:20 +02:00
Jeremy Meyers
5eaa479b3c
Update customservices.md (#887)
updated paperless title to reflect active version of this project and added note about custom service info not showing up with subtitle enabled

Co-authored-by: Bastien Wirtz <bastien.wirtz@gmail.com>
2025-09-21 05:36:04 -07:00
dependabot[bot]
11bd5fd9d5 chore(deps-dev): bump vite from 7.0.6 to 7.0.7
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 7.0.6 to 7.0.7.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v7.0.7/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.0.7/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.0.7
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-21 09:21:06 +02:00
Bastien Wirtz
da32035841 doc: improve smart card documentation. 2025-09-19 21:45:12 +02:00
Bastien Wirtz
2046d8d30c
Update FUNDING.yml 2025-09-15 14:34:14 +02:00
Bastien Wirtz
308deb95e0 fix(dev-server): fix unparsable dummy-data fson file 2025-08-17 17:26:37 +02:00
Bastien Wirtz
a941e94a3b chore: Update dependencies 2025-08-06 23:02:37 +02:00
Bastien Wirtz
83152453c5 fix(connectivity-checker): fix network offline issue with auth proxies #961 2025-08-06 23:02:02 +02:00
Bastien Wirtz
b4e20fe8af refactor(layouts): remove duplicated code. 2025-08-06 16:13:26 +02:00
Cs137
a38514739d Sort community themes alphabetically 2025-07-22 12:43:44 -07:00
Cs137
7ea9b2e882 Add community theme: DietPi 2025-07-22 12:43:44 -07:00
Cs137
60cc984f12 Update docs: Fix typo mentioning user 2025-07-22 12:43:44 -07:00
Cs137
fcf730f610 Update docs: Fix typo 2025-07-22 12:43:44 -07:00
tanasegabriel
c230392da8 Use promises for HTTP calls 2025-05-20 20:49:21 +02:00
tanasegabriel
adf671772b Add orange indicator for disabled blocking 2025-05-20 20:49:21 +02:00
tanasegabriel
51b4649628 Improve PiHole status 2025-05-20 20:49:21 +02:00
Bastien Wirtz
6344970eb6 chore(release): version bump 2025-05-17 14:52:27 +02:00
Bastien Wirtz
3f1d8e01ad fix(fresh-rss): Fix response decoding #902 2025-05-17 14:50:09 +02:00
Bastien Wirtz
58a1a0764d update dependencies 2025-05-17 14:49:36 +02:00
Juan Jose Pablos
105937d438 Do not force lang
hardcoding lang attribute on the html tag force to define the english language. 
If your user is a non english language the browser try to suggest to translate. But maybe the dashboard is already translated.
2025-05-17 14:38:04 +02:00
tanasegabriel
abb0cf84bb Do not mandate the usage of an API key for Pi-hole v6 2025-05-17 14:26:52 +02:00
蔡鳳駿
517de68e74 docs: configure Pi-Hole v6 API with URL that ends with admin 2025-05-05 02:51:25 -07:00
Bastien Wirtz
1afa0afd00 chore(lint): Apply lint 2025-05-04 15:15:12 +02:00
Bastien Wirtz
a5eeb1e44e feat(smart-cards): handle dynamic loading error 2025-05-04 06:14:06 -07:00
Bastien Wirtz
acb304adec deps update 2025-05-04 14:15:57 +02:00
Molham
347a3d062b remove deleteing the session over the API call when leaving the page 2025-05-04 04:11:59 -07:00
Molham
15f59b9e36 delete unused object 2025-05-04 04:11:59 -07:00
Molham
42f3a3ee71 reset the mixin service.js and make use of the actual error message from the current service.js 2025-05-04 04:11:59 -07:00
Molham
ad76093a38 modify service.js mixin to accept one more parameter and return full response and use this.fetch in PiHole.vue 2025-05-04 04:11:59 -07:00
Molham
9e314c960b fix integration test 2025-05-04 04:11:59 -07:00
Molham
07207dca55 reset the api_v5 function to fetch the remote api with this.fetch 2025-05-04 04:11:59 -07:00
Molham
59b0ed7688 set this.status after checking the response is OK 2025-05-04 04:11:59 -07:00
Molham
4684b23a8c better handle errors and set the subtitle as a message holder for errors 2025-05-04 04:11:59 -07:00
Molham
28ad80369f add support to dynamic interval time for polling the status 2025-05-04 04:11:59 -07:00
Molham
9307f5a926 feat(pihole): Support Pi-hole v6 API with session management (#875)
Add support for Pi-hole v6 API while maintaining v5 compatibility. The component now
handles both API versions with proper session-based authentication for v6 and legacy
authentication for v5. Includes automated retries, session caching, and proper cleanup.

- Add new apiVersion config option (default: 5, accepts: 5,6)
- Implement session-based auth with caching for v6 API
- Add auto-retry mechanism with configurable attempts
- Add periodic status polling for v6 API
- Separate v5/v6 API handling logic for better maintenance
- Improve error handling and status display
- Update documentation with v6 API support details

Breaking changes: None. V5 remains default for backward compatibility.
V6 mode must be explicitly enabled by setting apiVersion: 6 in config.
2025-05-04 04:11:59 -07:00
dependabot[bot]
1f6e6e7cce chore(deps-dev): bump vite from 6.1.5 to 6.1.6
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.1.5 to 6.1.6.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.1.6/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.1.6/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.1.6
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-04 04:10:01 -07:00
Gabriel Tanase
cde338a48d Fix typo in customservices docs
I was checking the newly added services and noticed that upon clicking the hyperlink it doesn't automatically scroll down to the corresponding section. Seems to be caused by a small typo.

Looking forward to the new release, these additions are great!
2025-04-26 01:54:21 -07:00
Rishi1208
66e7989e97 Update CONTRIBUTING.md 2025-04-26 01:53:22 -07:00
Rishi1208
fef1e8dcbe Update CONTRIBUTING.md 2025-04-26 01:53:22 -07:00
Marco Kreeft
b40d008400 Removed upload 2025-04-14 00:06:32 -07:00
Marco Kreeft
f4c026fe2e Handled subtitle if present 2025-04-14 00:06:32 -07:00
Marco Kreeft
1de57d9423 Comments Bastien 2025-04-14 00:06:32 -07:00
Marco Kreeft
042e4a0529 Update SABnzbd.vue 2025-04-14 00:06:32 -07:00
Marco Kreeft
ef95630225 Added download and upload speed to Sabnzbd 2025-04-14 00:06:32 -07:00
Marco Kreeft
68441f2b81 Update dockerhub.yml 2025-04-14 00:06:32 -07:00
Marco Kreeft
5de2344dc2 Update dockerhub.yml 2025-04-14 00:06:32 -07:00
Marco Kreeft
5976f8f561 Edited the anchors 2025-04-14 00:06:32 -07:00
Marco Kreeft
5c4b5e805e Added documentation for the services 2025-04-14 00:06:32 -07:00
Marco Kreeft
1ecbef0aca Added Docker Socket Proxy service 2025-04-14 00:06:32 -07:00
Marco Kreeft
4b63b7784f Added Plex Service 2025-04-14 00:06:32 -07:00
dependabot[bot]
deec0aaa68 chore(deps-dev): bump vite from 6.1.4 to 6.1.5
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.1.4 to 6.1.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.1.5/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.1.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.1.5
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-14 00:05:59 -07:00
Molham Al Nasr
bf531404f5 fix typo 2025-04-13 05:47:02 -07:00
Molham Al Nasr
fc10b5c512 Markdown-linter consider <string> as HTML and it shows the wrong text in a preview mode 2025-04-13 05:47:02 -07:00
Molham Al Nasr
04164acff3 Markdown-linter hates the trailing-punctuation. avoid warnings in editors 2025-04-13 05:47:02 -07:00
Molham Al Nasr
7cc7effd2e add blanks around headings. avoid warnings in editors 2025-04-13 05:47:02 -07:00
Molham Al Nasr
4a05f0d113 Markdown-linter hates the Bare URLs. avoid warnings in editors 2025-04-13 05:47:02 -07:00
Molham Al Nasr
d15ebb9d09 Markdown-linter hates the trailing-spaces. avoid warnings in editors 2025-04-13 05:47:02 -07:00
Bastien Wirtz
933e7a0991 Release version bump 2025-04-05 14:38:48 +02:00
dependabot[bot]
f0cd8ce91f chore(deps-dev): bump vite from 6.1.3 to 6.1.4
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.1.3 to 6.1.4.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.1.4/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.1.4/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.1.4
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-05 05:20:11 -07:00
dependabot[bot]
fcf332a31c chore(deps-dev): bump vite from 6.1.2 to 6.1.3
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.1.2 to 6.1.3.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.1.3/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.1.3/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-04 00:27:13 -07:00
Bastien Wirtz
7cb7293abf
Merge pull request #895 from cbos/linkding_support
Add Linkding support
2025-04-04 00:26:53 -07:00
Cees Bos
2024297f61 Merge branch 'main' into linkding_support
# Conflicts:
#	docs/customservices.md
2025-04-01 20:28:18 +02:00
Cees Bos
a63f9e2c7c Rework Linkding integration based on feedback on the PR 2025-04-01 20:24:54 +02:00
Bastien Wirtz
3f154b07a7 chore(lint): apply lint 2025-03-30 16:02:39 +02:00
thibaut1304
6916c2fed3 add service TruenasScale, view status and version 2025-03-30 07:00:41 -07:00
thibaut1304
e5bd328d21 add service vaultwarden, view status and version 2025-03-30 06:58:51 -07:00
thibaut1304
2a290004b7 add service matrix, view status and version 2025-03-30 06:53:44 -07:00
3thibaut1304
b11bee7d64 add status online/offline and version of the card 2025-03-30 06:51:09 -07:00
Bastien Wirtz
234e063d2e feat(components): Register Generic component globally 2025-03-30 15:23:22 +02:00
dependabot[bot]
02ff6a2039 chore(deps-dev): bump vite from 6.1.1 to 6.1.2
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.1.1 to 6.1.2.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.1.2/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.1.2/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-30 05:21:59 -07:00
Cees Bos
e1fdb0069b Fix issues in Linkding integration revealed by code check in the pipeline 2025-03-20 18:27:59 +01:00
Mathieu Bélanger
37716c8d42 Fix typos in config-schema.json 2025-03-20 01:14:51 -07:00
Cees Bos
50acb9957e Add Linkding support 2025-03-19 22:25:34 +01:00
Bastien Wirtz
76e6c70696 release 25.03.3 2025-03-17 21:54:56 +01:00
Bastien Wirtz
c812bda08f fix(docker): disable log for healthcheck requests 2025-03-17 13:50:35 -07:00
Cees Bos
1b607b6357 Update schema based on review 2025-03-08 05:37:36 -08:00
Cees Bos
c546fc1605 Add YAML config schema for auto complete support 2025-03-08 05:37:36 -08:00
Bastien Wirtz
7dfb3b8faf release 25.03.2 2025-03-04 21:27:53 +01:00
Bastien Wirtz
11934c3995 fix: hide overflow to avoid scroll bars in cards #885 2025-03-04 21:24:20 +01:00
Bastien Wirtz
60a45191b9 Release 25.03.1 2025-03-01 09:56:26 +01:00
Bastien Wirtz
4bd449e036 fix(navbar): Adjust icon spacing #857 2025-03-01 09:54:26 +01:00
Molham Al Nasr
a8f7d09bd4 fix(Dockerfile): add default value for ARG VERSION_TAG to prevent build errors
Added a default value `latest` to the ARG VERSION_TAG in the Dockerfile.
This ensures that the build does not fail when the VERSION_TAG argument
is not explicitly provided, making manual builds more reliable.

Fixes #881
2025-03-01 00:27:32 -08:00
Molham Al Nasr
2a0387f90e fix(Dockerfile): ensure VERSION_TAG is correctly passed as a build argument
The VERSION_TAG label was not correctly set in the built image due to
a missing ARG definition in the Dockerfile. This commit adds ARG VERSION_TAG
so that the build argument can be properly passed and used in image labels.

Fixes #881
2025-03-01 00:27:32 -08:00
Bastien Wirtz
12a004a9e1 chore: Set official config 2025-03-01 09:25:49 +01:00
Bastien Wirtz
20fb0c2254 fix(cards): Avoid lowercase letters cut off #794 2025-03-01 09:24:44 +01:00
Bastien Wirtz
13fb05696a feat(logs): get lighthttpd accesslog in docker logs 2025-02-28 19:21:32 +01:00
Bastien Wirtz
31f7cfa09f fix(search): Allow usage of the hotkey in the text 2025-02-27 04:55:30 -08:00
tanasegabriel
36d753df58 Fix quicklinks conditional icon rendering 2025-02-27 04:31:25 -08:00
Bastien Wirtz
1c09714c43 fix: make sure healthcheck dont use a proxy #394 2025-02-25 21:55:09 +01:00
137 changed files with 7873 additions and 2014 deletions

5
.github/FUNDING.yml vendored
View file

@ -1,3 +1,6 @@
# These are supported funding model platforms
custom: ['https://www.buymeacoffee.com/bastien']
# These are supported funding model platforms
github: [bastienwirtz]
buy_me_a_coffee: bastien

View file

@ -9,10 +9,11 @@ Fixes # (issue)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Refactoring
## Checklist:
- [ ] I've read & comply with the [contributing guidelines](https://github.com/bastienwirtz/homer/blob/main/CONTRIBUTING.md)
- [ ] I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers.
- [ ] I have made corresponding changes to the documentation (README.md).
- [ ] I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers.
- [ ] I have made corresponding changes to the documentation (`README.md`).
- [ ] I've checked my modifications for any breaking changes, especially in the `config.yml` file

6
.gitignore vendored
View file

@ -23,4 +23,8 @@ yarn-error.log*
# App configuration
config.yml
.drone.yml
.drone.yml
# Specific Agent file
CLAUDE.md
GEMINI.md

8
.jsconfig.json Normal file
View file

@ -0,0 +1,8 @@
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "dist"]
}

359
.schema/config-schema.json Normal file
View file

@ -0,0 +1,359 @@
{
"$id": "https://raw.githubusercontent.com/bastienwirtz/homer/main/.schema/config-schema.json",
"$schema": "http://json-schema.org/draft-07/schema",
"description": "https://github.com/bastienwirtz/homer/blob/main/docs/configuration.md",
"examples": [],
"title": "Homer Dashboard configuration",
"type": "object",
"definitions": {
"Colors": {
"type": "object",
"additionalProperties": false,
"properties": {
"light": {
"$ref": "#/definitions/ColorSet"
},
"dark": {
"$ref": "#/definitions/ColorSet"
}
},
"title": "Colors"
},
"ColorSet": {
"type": "object",
"additionalProperties": false,
"properties": {
"highlight-primary": {
"type": "string"
},
"highlight-secondary": {
"type": "string"
},
"highlight-hover": {
"type": "string"
},
"background": {
"type": "string"
},
"card-background": {
"type": "string"
},
"text": {
"type": "string"
},
"text-header": {
"type": "string"
},
"text-title": {
"type": "string"
},
"text-subtitle": {
"type": "string"
},
"card-shadow": {
"type": "string"
},
"link": {
"type": "string"
},
"link-hover": {
"type": "string"
},
"background-image": {
"type": "string"
}
}
},
"Defaults": {
"type": "object",
"additionalProperties": false,
"properties": {
"layout": {
"enum": [
"columns",
"list"
],
"description": "Layout of the dashboard, either 'columns' or 'list'"
},
"colorTheme": {
"enum": [
"auto",
"light",
"dark"
],
"description": "One of 'auto', 'light', or 'dark'"
}
},
"title": "Defaults"
},
"Hotkey": {
"type": "object",
"additionalProperties": false,
"properties": {
"search": {
"type": "string",
"description": "hotkey for search, e.g. Shift"
}
},
"required": [
"search"
]
},
"Link": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Name as seen in the navbar"
},
"icon": {
"type": "string",
"description": "Fontawesome icon"
},
"url": {
"type": "string",
"description": "Url of the link. When #filename is used, it is a link to another homer page, while 'filename' is the name of the config file"
},
"target": {
"type": "string",
"description": "html tag target attribute like _blank for a new page"
}
},
"required": [
"url"
],
"title": "Link"
},
"Message": {
"type": "object",
"additionalProperties": false,
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"mapping": {
"$ref": "#/definitions/Mapping",
"description": "Mapping for the content loaded from the URL"
},
"refreshInterval": {
"type": "integer",
"description": "The refresh interval in milliseconds for reloading the message url"
},
"style": {
"type": "string",
"description": "See https://bulma.io/documentation/components/message/#colors for styling options"
},
"title": {
"type": "string",
"description": "Title of the message box"
},
"icon": {
"type": "string",
"description": "Fontawesome icon for the message box"
},
"content": {
"type": "string",
"description": "HTML content for the message box"
}
},
"title": "Messagebox"
},
"Mapping": {
"type": "object",
"additionalProperties": true,
"title": "Mapping"
},
"Proxy": {
"type": "object",
"additionalProperties": false,
"properties": {
"useCredentials": {
"type": "boolean",
"description": "# send cookies & authorization headers when fetching service specific data. Set to `true` if you use an authentication proxy. Can be overrided on service level. "
},
"headers": {
"$ref": "#/definitions/Headers",
"description": "send custom headers when fetching service specific data. Can also be set on a service level."
}
},
"title": "Proxy"
},
"Headers": {
"type": "object",
"additionalProperties": true,
"title": "Headers"
},
"Service": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Service name"
},
"icon": {
"type": "string",
"description": "Fontawesome icon for the service"
},
"logo": {
"type": "string",
"description": "A path to an image can also be provided. Note that icon take precedence if both icon and logo are set."
},
"class": {
"type": "string",
"description": "Optional css class to add on the service group. Example 'highlight-purple'"
},
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/Item"
}
}
},
"required": [
"items"
],
"title": "Service"
},
"Item": {
"type": "object",
"additionalProperties": true,
"properties": {
"name": {
"type": "string"
},
"logo": {
"type": "string",
"description": "Path to a logo. Alternatively a fa icon can be provided"
},
"icon": {
"type": "string",
"description": "Fontawesome icon for the item, alternative for logo"
},
"subtitle": {
"type": "string"
},
"tag": {
"type": "string",
"description": "Show tag"
},
"keywords": {
"type": "string",
"description": "Optional keyword used for searching purpose"
},
"url": {
"type": "string",
"description": "Url of this item"
},
"target": {
"type": "string",
"description": "html tag target attribute like _blank for a new page"
},
"tagstyle": {
"type": "string",
"description": "Styleclass for the tag"
},
"type": {
"type": "string",
"description": "Optional, loads a specific component that provides extra features. MUST MATCH a file name (without file extension) available in `src/components/services`"
}
},
"title": "Item"
}
},
"properties": {
"externalConfig": {
"type": "string",
"description": "Use external configuration file. Using this will ignore remaining config in this file externalConfig: https://example.com/server-luci/config.yaml"
},
"title": {
"type": "string",
"description": "Title of the dashboard"
},
"subtitle": {
"type": "string",
"description": "Subtitle of the dashboard"
},
"documentTitle": {
"type": "string",
"description": "Title of the document. When not filled, title (and subtitle will be used)"
},
"logo": {
"type": "string",
"description": "Path to logo image"
},
"icon": {
"type": "string",
"description": "Dashboard icon"
},
"header": {
"type": "boolean",
"description": "Show header, default is true"
},
"hotkey": {
"$ref": "#/definitions/Hotkey",
"description": "Define hotkeys, for example for search"
},
"footer": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "string"
}
],
"description": "footer Line content. HTML is supported. Set false if you want to hide it."
},
"columns": {
"type": "string",
"description": "'auto' or number (must be a factor of 12: 1, 2, 3, 4, 6, 12)",
"format": "integer"
},
"connectivityCheck": {
"type": "boolean",
"description": "# whether you want to display a message when the apps are not accessible anymore (VPN disconnected for example). You should set it to true when using an authentication proxy, it also reloads the page when a redirection is detected when checking connectivity."
},
"proxy": {
"$ref": "#/definitions/Proxy",
"description": "Optional: Proxy / hosting option"
},
"defaults": {
"$ref": "#/definitions/Defaults"
},
"theme": {
"type": "string",
"description": "'default' or one of the themes available in 'src/assets/themes'"
},
"stylesheet": {
"type": "array",
"items": {
"type": "string"
},
"description": "Will load custom CSS files. Especially useful for custom icon sets. Entries are paths to the stylesheets"
},
"colors": {
"$ref": "#/definitions/Colors"
},
"message": {
"$ref": "#/definitions/Message",
"description": "Messagebox"
},
"links": {
"description": "Links in the navigation bar",
"type": "array",
"items": {
"$ref": "#/definitions/Link"
}
},
"services": {
"description": "Services",
"type": "array",
"items": {
"$ref": "#/definitions/Service"
}
}
}
}

84
AGENTS.md Normal file
View file

@ -0,0 +1,84 @@
# AGENTS Instructions
This file provides guidance to AI Agents when working with code in this repository.
## Development Commands
```bash
pnpm install # Install dependencies (PNPM enforced via packageManager)
pnpm dev # Start development server on http://localhost:3000
pnpm mock # Start mock API server for testing service integrations
pnpm build # Build for production
pnpm preview # Preview production build
pnpm lint # Run ESLint with auto-fix
```
## Architecture Overview
Homer is a static Vue.js 3 PWA dashboard that loads configuration from YAML files. The architecture is service-oriented with dynamic component loading.
### Core Application Structure
- **Entry Point**: `src/main.js` mounts the Vue app
- **Root Component**: `src/App.vue` handles layout, configuration loading, and routing
- **Configuration System**: YAML-based with runtime merging of defaults (`src/assets/defaults.yml`) and user config (`/assets/config.yml`)
- **Service Components**: 53 specialized integrations in `src/components/services/` that extend a Generic component pattern
### Service Integration Pattern
All service components follow this architecture:
- Extend `Generic.vue` using Vue slots (`<template #indicator>`, `<template #content>`, `<template #icon>`)
- Use the `service.js` mixin (`src/mixins/service.js`) for common API functionality
- Use a custom `fetch` method provided by the service mixin to seamlessly support proxy configuration, custom headers, and credentials.
### Configuration & Routing
- **Multi-page Support**: Hash-based routing without Vue Router
- **Dynamic Config Loading**: External URLs supported via `config.remote_config`
- **Theme System**: CSS layers architecture with three built-in themes in `src/assets/themes/`
- **Asset Management**: Static files served from `/assets/` with runtime configuration merging
### Build System Details
- **Vite 7**: Modern build tool with Vue plugin
- **PWA**: Auto-updating service worker via `vite-plugin-pwa`
- **SCSS**: Bulma framework with modular component styling
- **Docker**: Multi-stage build (Node.js → Alpine + Lighttpd)
### Mock Data Creation Pattern
When creating mock data for service components testing:
**Structure**: `dummy-data/[component-name]/[api-path]/[endpoint]`
**Steps**:
1. **Analyze component**: Read the Vue component file to identify API calls (look for `this.fetch()` calls)
2. **Check existing mock**: If mock directory exists, read existing files to check for missing fields
3. **Create/update structure**: `mkdir -p dummy-data/[lowercase-component-name]/` and mirror API endpoint paths
4. **Create/update JSON files**: Write realistic mock responses matching the expected data structure
5. **Verify fields**: Ensure all fields used in the component's computed properties and templates are included
6. **Update existing mocks**: If mock files exist but are missing fields, add the missing fields without removing existing data
**Key Points**:
- Component directory name should be lowercase version of component name (e.g., `AdGuardHome.vue``adguardhome/`)
- Directory structure mirrors API endpoints exactly
- Files contain JSON responses (no file extension needed)
- Mock server serves from `dummy-data/` via `pnpm mock` command
- Each component gets isolated directory to prevent API path conflicts
- When updating existing mocks, preserve existing data and only add missing fields required by the component
- Always read existing mock files first to understand current structure before making changes
**Example**: For `AdGuardHome.vue`:
- API calls: `/control/status`, `/control/stats`
- Mock files: `dummy-data/adguardhome/control/status`, `dummy-data/adguardhome/control/stats`
### Development Notes
- Use `pnpm mock` to test service integrations with dummy data
- Configuration changes require restart in development mode
- New service components should follow the Generic component slot pattern
- Themes use CSS custom properties for dynamic color switching
- The app has no backend dependencies and generates static files only

View file

@ -6,10 +6,10 @@ First off, thank you for considering contributing to Homer!
### Project philosophy
Homer is meant to be a light and very simple dashboard that keeps all your useful utilities at hands. The few features implemented in Homer focus on
UX and usability. If you are looking for a full featured dashboard, there is tons of great stuff out there like https://heimdall.site/, https://github.com/rmountjoy92/DashMachine or https://organizr.app/.
Homer is meant to be a light and very simple dashboard that keeps all your useful utilities at hand. The few features implemented in Homer focus on
UX and usability. If you are looking for a full featured dashboard, there are tons of great stuff out there like https://gethomepage.dev/, https://heimdall.site/, https://github.com/rmountjoy92/DashMachine or https://organizr.app/.
- Configuration is stored in a simple config file, avoiding the need for a backend/database while making possible to use versioning or [config template](https://docs.ansible.com/ansible/latest/user_guide/playbooks_templating.html).
- Configuration is stored in a simple config file, avoiding the need for a backend/database while making it possible to use versioning or [config template](https://docs.ansible.com/ansible/latest/user_guide/playbooks_templating.html).
- Only modern browsers are supported, feel free to use any JS features without any polyfill as soon as the latest version of the major browsers supports them.
@ -33,10 +33,19 @@ For all contributions, please respect the following guidelines:
If you want to add a feature, it's often best to talk about it before starting to work on it and submitting a pull request. It's not mandatory at all, but
feel free to open an issue to present your idea.
### Working with AI Agents
This repository include an [`AGENTS.md`](https://github.com/bastienwirtz/homer/blob/main/AGENTS.md) instruction file for agents. It use an [open format](https://agents.md/), which most agent should natively use for context. However, for specific agent like Claude Code or Gemini, you will have to specifically ask it to read the file or create symlink:
```sh
ln -s AGENTS.md CLAUDE.md
ln -s AGENTS.md GEMINI.md
```
### How to submit a contribution
The general process to submit a contribution is as follow:
1. Take a look to the [development guideline](https://github.com/bastienwirtz/homer/blob/main/docs/development.md).
1. Take a look at the [development guideline](https://github.com/bastienwirtz/homer/blob/main/docs/development.md).
2. Create your own fork of the code
3. Do the changes in your fork
4. Make sure to fill the [pull request description](https://github.com/bastienwirtz/homer/blob/main/.github/PULL_REQUEST_TEMPLATE.md) properly.

View file

@ -17,6 +17,8 @@ RUN pnpm build
# production stage
FROM alpine:3.21
ARG VERSION_TAG=latest
LABEL \
org.label-schema.schema-version="1.0" \
org.label-schema.version="$VERSION_TAG" \
@ -49,7 +51,7 @@ COPY --from=build-stage --chown=${UID}:${GID} /app/dist/assets /www/default-asse
USER ${UID}:${GID}
HEALTHCHECK --start-period=10s --start-interval=1s --interval=30s --timeout=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:${PORT}/ || exit 1
CMD wget --no-verbose -Y off --tries=1 --spider http://127.0.0.1:${PORT}/ || exit 1
EXPOSE ${PORT}

View file

@ -1,6 +1,6 @@
# Configuration
Homer rely on a single [yaml](http://yaml.org/) configuration file, located in the `/assets` directory.
Homer relies on a single [yaml](http://yaml.org/) configuration file, located in the `/assets` directory.
`.dist` sample configuration files are available to help you get started. Alternatively, the example below can be
copied into the config file.
@ -183,9 +183,9 @@ Empty values (either in `config.yml` or the endpoint data) will hide the element
## Connectivity checks
As a webapp (PWA) the dashboard can still be displayed when your homer server is offline.
The connectivity checker periodically send a HEAD request bypassing the PWA cache to the dashbord page to make sure it's still reachable.
The connectivity checker periodically sends a HEAD request bypassing the PWA cache to the dashbord page to make sure it's still reachable.
It can be useful when you access your dashboard through a VPN or ssh tunnel for example, to know is your conection is up. It also helps when using an authentication proxy, it will reloads the page if the authentication expires (when a redirection is send in response to the HEAD request).
It can be useful when you access your dashboard through a VPN or ssh tunnel for example, to know if your conection is up. It also helps when using an authentication proxy, it will reload the page if the authentication expires (when a redirect is send in response to the HEAD request).
## Style Options
@ -206,9 +206,10 @@ The `/assets/manifest.json` can also be edited to change the app (pwa) name, des
### Community theme
- [Dracula theme](https://draculatheme.com/homer) by [@Tuetenk0pp](https://github.com/Tuetenk0pp)
- [Homer Theme v2](https://github.com/walkxcode/homer-theme) by [walkxcode](https://github.com/walkxcode)
- [Catppuccin theme](https://github.com/mrpbennett/catppucin-homer) by [@mrpbenett](https://github.com/mrpbennett)
- [DietPi theme](https://codeberg.org/Cs137/homer-theme-dietpi) by [@Cs137](https://codeberg.org/Cs137)
- [Dracula theme](https://draculatheme.com/homer) by [@Tuetenk0pp](https://github.com/Tuetenk0pp)
- [Homer Theme v2](https://github.com/walkxcode/homer-theme) by [@walkxcode](https://github.com/walkxcode)
## PWA Icons

File diff suppressed because it is too large Load diff

View file

@ -78,6 +78,21 @@ Then when Homer reads your config, it will substitute your anchors automatically
The end result is that if you want to update the name or style of any particular tag, just update it once, in the tags section!
Great if you have a lot of services or a lot of tags!
## YAML auto complete with a YAML schema
A lot of editor support auto completion, see <https://www.schemastore.org/json/>
The homer schema is available here: <https://raw.githubusercontent.com/bastienwirtz/homer/main/.schema/config-schema.json>
For example with IntelliJ you can define:
```yaml
# $schema: https://raw.githubusercontent.com/bastienwirtz/homer/main/.schema/config-schema.json
```
With VSCode you can define it like this:
```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/bastienwirtz/homer/main/.schema/config-schema.json
```
## Remotely edit your config with Code Server
#### `by @JamiePhonic`

View file

@ -41,7 +41,7 @@ To resolve this, you can either:
- Host all your target service under the same domain & port.
- Modify the target server configuration so that the response of the server included following header- `Access-Control-Allow-Origin: *` (<https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests>). It might be an option in the targeted service, otherwise depending on how the service is hosted, the proxy or web server can seamlessly add it.
- Use a cors proxy server like [`cors-container`](https://github.com/imjacobclark/cors-container), [`cors-anywhere`](https://github.com/Rob--W/cors-anywhere) or many others.
- **Use a proxy** to add the necessary CORS headers (lot of options, some of them described [here](https://enable-cors.org/server.html). Also check [`CORSair`](https://github.com/bastienwirtz/corsair), a light and simple solution)
## I am using an authentication proxy and homer says I am offline

View file

@ -0,0 +1,51 @@
{
"time_units": "hours",
"num_dns_queries": 28947,
"num_blocked_filtering": 12489,
"num_replaced_safebrowsing": 0,
"num_replaced_safesearch": 0,
"num_replaced_parental": 0,
"avg_processing_time": 0.34,
"top_queried_domains": [
{
"name": "example.com",
"count": 1289
},
{
"name": "api.github.com",
"count": 892
}
],
"top_clients": [
{
"name": "192.168.1.100",
"count": 8945
},
{
"name": "192.168.1.101",
"count": 6234
}
],
"top_blocked_domains": [
{
"name": "ads.google.com",
"count": 1245
},
{
"name": "tracker.example.com",
"count": 987
}
],
"dns_queries": [
12450, 13200, 14100, 13800, 12900, 11200, 10800, 9600, 8200, 7800,
9200, 10500, 12100, 13600, 14800, 15200, 14900, 13700, 12800, 11900,
11200, 10800, 10200, 9800
],
"blocked_filtering": [
5200, 5800, 6100, 5900, 5400, 4800, 4600, 4200, 3600, 3400,
4000, 4500, 5200, 5800, 6300, 6500, 6300, 5800, 5400, 5100,
4800, 4600, 4300, 4200
],
"replaced_safebrowsing": [],
"replaced_parental": []
}

View file

@ -0,0 +1,16 @@
{
"protection_enabled": true,
"version": "v0.107.48",
"language": "en",
"dns_address": "127.0.0.1:53",
"dns_port": 53,
"protection_disabled_duration": null,
"http_port": 80,
"https_port": 443,
"querylog_enabled": true,
"querylog_size": 5000,
"querylog_size_memory": 1000,
"querylog_interval": 2160,
"dhcp_available": true,
"running": true
}

View file

@ -0,0 +1,215 @@
[
{
"Id": "8dfafdbc3a40",
"Names": ["/boring_feynman"],
"Image": "nginx:latest",
"ImageID": "sha256:f6d0b4767a6c",
"Command": "/docker-entrypoint.sh nginx -g 'daemon off;'",
"Created": 1640995200,
"Ports": [
{
"IP": "0.0.0.0",
"PrivatePort": 80,
"PublicPort": 8080,
"Type": "tcp"
}
],
"Labels": {
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
},
"State": "running",
"Status": "Up 2 hours",
"HostConfig": {
"NetworkMode": "default"
},
"NetworkSettings": {
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "f2de39df4171",
"EndpointID": "2cdc4edb1ded",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02"
}
}
},
"Mounts": []
},
{
"Id": "9e87a2b84b8e",
"Names": ["/web-app"],
"Image": "node:16-alpine",
"ImageID": "sha256:c85b8f829d1f",
"Command": "npm start",
"Created": 1640991600,
"Ports": [
{
"IP": "0.0.0.0",
"PrivatePort": 3000,
"PublicPort": 3000,
"Type": "tcp"
}
],
"Labels": {},
"State": "running",
"Status": "Up 3 hours",
"HostConfig": {
"NetworkMode": "default"
},
"NetworkSettings": {
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "f2de39df4171",
"EndpointID": "3edc5fdb2efe",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:03"
}
}
},
"Mounts": [
{
"Type": "bind",
"Source": "/home/user/app",
"Destination": "/app",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
]
},
{
"Id": "7b9a3c6d2e1f",
"Names": ["/database"],
"Image": "postgres:13",
"ImageID": "sha256:b4ed8d5b4f3a",
"Command": "docker-entrypoint.sh postgres",
"Created": 1640988000,
"Ports": [
{
"IP": "127.0.0.1",
"PrivatePort": 5432,
"PublicPort": 5432,
"Type": "tcp"
}
],
"Labels": {},
"State": "dead",
"Status": "Up 4 hours",
"HostConfig": {
"NetworkMode": "default"
},
"NetworkSettings": {
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "f2de39df4171",
"EndpointID": "4fdc6gdb3gfg",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.4",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:04"
}
}
},
"Mounts": [
{
"Type": "volume",
"Name": "postgres_data",
"Source": "/var/lib/docker/volumes/postgres_data/_data",
"Destination": "/var/lib/postgresql/data",
"Driver": "local",
"Mode": "rw",
"RW": true,
"Propagation": ""
}
]
},
{
"Id": "5c8d1f4e9a2b",
"Names": ["/old-service"],
"Image": "ubuntu:20.04",
"ImageID": "sha256:f643c72bc252",
"Command": "/bin/bash",
"Created": 1640984400,
"Ports": [],
"Labels": {},
"State": "exited",
"Status": "Exited (0) 2 hours ago",
"HostConfig": {
"NetworkMode": "default"
},
"NetworkSettings": {
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "",
"EndpointID": "",
"Gateway": "",
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": ""
}
}
},
"Mounts": []
},
{
"Id": "1a2b3c4d5e6f",
"Names": ["/backup-job"],
"Image": "alpine:latest",
"ImageID": "sha256:c059bfaa849c",
"Command": "sh -c 'sleep 3600'",
"Created": 1640980800,
"Ports": [],
"Labels": {},
"State": "exited",
"Status": "Exited (0) 30 minutes ago",
"HostConfig": {
"NetworkMode": "default"
},
"NetworkSettings": {
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "",
"EndpointID": "",
"Gateway": "",
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": ""
}
}
},
"Mounts": []
}
]

View file

@ -0,0 +1,24 @@
{
"LocalAddress": "192.168.1.100:8096",
"ServerName": "Homer-Emby-Server",
"Version": "4.8.8.0",
"ProductName": "Emby Server",
"OperatingSystem": "Linux",
"Id": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"StartupWizardCompleted": true,
"SupportsLibraryMonitor": true,
"WebSocketPortNumber": 8096,
"CompletedInstallations": [],
"CanSelfRestart": true,
"CanSelfUpdate": true,
"CanLaunchWebBrowser": false,
"WanAddress": "192.168.1.100:8096",
"HasUpdateAvailable": false,
"SupportsAutoRunAtStartup": false,
"TranscodingTempPath": "/var/lib/emby/transcoding-temp",
"CachePath": "/var/lib/emby/cache",
"LogPath": "/var/log/emby",
"InternalMetadataPath": "/var/lib/emby/metadata",
"ItemsByNamePath": "/var/lib/emby/metadata/People",
"ProgramDataPath": "/var/lib/emby"
}

View file

@ -0,0 +1,12 @@
{
"MovieCount": 1247,
"SeriesCount": 89,
"EpisodeCount": 2156,
"ArtistCount": 234,
"AlbumCount": 567,
"SongCount": 8923,
"MusicVideoCount": 42,
"BoxSetCount": 23,
"BookCount": 156,
"ItemCount": 13437
}

View file

@ -0,0 +1,211 @@
[
{
"name": "Gateway",
"group": "Services",
"key": "services_gateway",
"results": [
{
"status": 200,
"hostname": "gateway.example.com",
"duration": 10000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": true
},
{
"condition": "[RESPONSE_TIME] < 500",
"success": true
}
],
"success": true,
"timestamp": "2025-05-26T07:35:41.784208588Z"
},
{
"status": 200,
"hostname": "gateway.example.com",
"duration": 10000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": true
},
{
"condition": "[RESPONSE_TIME] < 500",
"success": true
}
],
"success": true,
"timestamp": "2025-05-26T07:40:41.804489793Z"
},
{
"status": 200,
"hostname": "gateway.example.com",
"duration": 10000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": true
},
{
"condition": "[RESPONSE_TIME] < 500",
"success": true
}
],
"success": true,
"timestamp": "2025-05-26T07:45:41.837925713Z"
},
{
"status": 200,
"hostname": "gateway.example.com",
"duration": 10000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": true
},
{
"condition": "[RESPONSE_TIME] < 500",
"success": true
}
],
"success": true,
"timestamp": "2025-05-26T07:50:41.848391366Z"
}
]
},
{
"name": "Website",
"group": "External",
"key": "external_website",
"results": [
{
"status": 200,
"hostname": "www.example.com",
"duration": 10000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": true
},
{
"condition": "[RESPONSE_TIME] < 500",
"success": false
}
],
"success": false,
"timestamp": "2025-05-26T07:35:41.784208588Z"
},
{
"status": 200,
"hostname": "gateway.example.com",
"duration": 10000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": false
},
{
"condition": "[RESPONSE_TIME] < 500",
"success": true
}
],
"success": false,
"timestamp": "2025-05-26T07:40:41.804489793Z"
},
{
"status": 200,
"hostname": "gateway.example.com",
"duration": 10000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": true
},
{
"condition": "[RESPONSE_TIME] < 500",
"success": true
}
],
"success": true,
"timestamp": "2025-05-26T07:45:41.837925713Z"
},
{
"status": 200,
"hostname": "gateway.example.com",
"duration": 10000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": true
},
{
"condition": "[RESPONSE_TIME] < 500",
"success": true
}
],
"success": true,
"timestamp": "2025-05-26T07:50:41.848391366Z"
}
]
},
{
"name": "DNS",
"group": "Services",
"key": "services_dns",
"results": [
{
"status": 200,
"hostname": "ns1.example",
"duration": 20000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": false
}
],
"success": false,
"timestamp": "2025-05-26T07:35:41.784208588Z"
},
{
"status": 200,
"hostname": "ns1.example.com",
"duration": 20000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": false
}
],
"success": false,
"timestamp": "2025-05-26T07:40:41.804489793Z"
},
{
"status": 200,
"hostname": "ns1.example.com",
"duration": 20000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": false
}
],
"success": false,
"timestamp": "2025-05-26T07:45:41.837925713Z"
},
{
"status": 200,
"hostname": "ns1.example.com",
"duration": 20000000,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": false
}
],
"success": false,
"timestamp": "2025-05-26T07:50:41.848391366Z"
}
]
}
]

4
dummy-data/gotify/health Normal file
View file

@ -0,0 +1,4 @@
{
"health": "green",
"database": "green"
}

65
dummy-data/gotify/message Normal file
View file

@ -0,0 +1,65 @@
{
"messages": [
{
"id": 1,
"appid": 1,
"message": "System backup completed successfully",
"title": "Backup Service",
"priority": 2,
"date": "2024-01-15T10:30:00Z"
},
{
"id": 2,
"appid": 2,
"message": "Database optimization finished",
"title": "Database Manager",
"priority": 1,
"date": "2024-01-15T09:15:00Z"
},
{
"id": 3,
"appid": 1,
"message": "Server restart scheduled for maintenance",
"title": "System Admin",
"priority": 5,
"date": "2024-01-15T08:45:00Z"
},
{
"id": 4,
"appid": 3,
"message": "New user registration: john.doe@example.com",
"title": "User Management",
"priority": 1,
"date": "2024-01-15T07:20:00Z"
},
{
"id": 5,
"appid": 2,
"message": "Weekly report generated and sent",
"title": "Report Generator",
"priority": 2,
"date": "2024-01-14T18:00:00Z"
},
{
"id": 6,
"appid": 4,
"message": "Security scan completed - no threats detected",
"title": "Security Monitor",
"priority": 2,
"date": "2024-01-14T16:30:00Z"
},
{
"id": 7,
"appid": 1,
"message": "Disk usage is at 85% on /var partition",
"title": "System Monitor",
"priority": 4,
"date": "2024-01-14T14:15:00Z"
}
],
"paging": {
"size": 7,
"since": 0,
"limit": 100
}
}

View file

@ -0,0 +1,95 @@
{
"checks": [
{
"name": "Database Backup",
"tags": "backup database",
"desc": "Daily database backup job",
"grace": 3600,
"n_pings": 127,
"status": "up",
"last_ping": "2024-01-15T10:30:00+00:00",
"next_ping": "2024-01-16T10:30:00+00:00",
"manual_resume": false,
"methods": "",
"unique_key": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
},
{
"name": "Web Server Monitoring",
"tags": "web server nginx",
"desc": "Monitor web server health",
"grace": 300,
"n_pings": 2847,
"status": "up",
"last_ping": "2024-01-15T10:25:00+00:00",
"next_ping": "2024-01-15T10:30:00+00:00",
"manual_resume": false,
"methods": "",
"unique_key": "b2c3d4e5-f6g7-8901-bcde-f23456789012"
},
{
"name": "SSL Certificate Check",
"tags": "ssl certificate",
"desc": "Weekly SSL certificate expiry check",
"grace": 86400,
"n_pings": 52,
"status": "up",
"last_ping": "2024-01-14T12:00:00+00:00",
"next_ping": "2024-01-21T12:00:00+00:00",
"manual_resume": false,
"methods": "",
"unique_key": "c3d4e5f6-g7h8-9012-cdef-345678901234"
},
{
"name": "Log Cleanup Service",
"tags": "cleanup logs maintenance",
"desc": "Weekly log file cleanup",
"grace": 7200,
"n_pings": 15,
"status": "grace",
"last_ping": "2024-01-13T02:00:00+00:00",
"next_ping": "2024-01-20T02:00:00+00:00",
"manual_resume": false,
"methods": "",
"unique_key": "d4e5f6g7-h8i9-0123-defa-456789012345"
},
{
"name": "Email Service",
"tags": "email smtp",
"desc": "Email service availability check",
"grace": 600,
"n_pings": 0,
"status": "down",
"last_ping": "2024-01-12T08:15:00+00:00",
"next_ping": "2024-01-15T08:15:00+00:00",
"manual_resume": false,
"methods": "",
"unique_key": "e5f6g7h8-i9j0-1234-efab-567890123456"
},
{
"name": "API Health Check",
"tags": "api health",
"desc": "External API endpoint health monitoring",
"grace": 180,
"n_pings": 1440,
"status": "up",
"last_ping": "2024-01-15T10:28:00+00:00",
"next_ping": "2024-01-15T10:30:00+00:00",
"manual_resume": false,
"methods": "",
"unique_key": "f6g7h8i9-j0k1-2345-fabc-678901234567"
},
{
"name": "Backup Verification",
"tags": "backup verify",
"desc": "Verify backup integrity",
"grace": 1800,
"n_pings": 45,
"status": "grace",
"last_ping": "2024-01-14T22:30:00+00:00",
"next_ping": "2024-01-15T22:30:00+00:00",
"manual_resume": false,
"methods": "",
"unique_key": "g7h8i9j0-k1l2-3456-gbcd-789012345678"
}
]
}

View file

@ -0,0 +1,45 @@
{
"location_name": "Home",
"latitude": 40.7128,
"longitude": -74.0060,
"elevation": 10,
"unit_system": {
"length": "km",
"mass": "kg",
"pressure": "Pa",
"temperature": "°C",
"volume": "L"
},
"time_zone": "America/New_York",
"components": [
"automation",
"climate",
"device_tracker",
"frontend",
"history",
"light",
"logger",
"media_player",
"recorder",
"script",
"sensor",
"switch",
"system_health",
"weather"
],
"config_dir": "/config",
"allowlist_external_dirs": [
"/config",
"/share"
],
"allowlist_external_urls": [],
"version": "2024.1.5",
"config_source": "storage",
"recovery_mode": false,
"state": "RUNNING",
"external_url": null,
"internal_url": null,
"currency": "USD",
"country": "US",
"language": "en"
}

View file

@ -0,0 +1,141 @@
[
{
"entity_id": "sensor.living_room_temperature",
"state": "22.5",
"attributes": {
"unit_of_measurement": "°C",
"device_class": "temperature",
"friendly_name": "Living Room Temperature"
},
"last_changed": "2024-01-15T10:30:00+00:00",
"last_updated": "2024-01-15T10:30:00+00:00",
"context": {
"id": "01HMV123456789",
"parent_id": null,
"user_id": null
}
},
{
"entity_id": "light.bedroom_ceiling",
"state": "on",
"attributes": {
"brightness": 180,
"color_mode": "brightness",
"supported_color_modes": ["brightness"],
"friendly_name": "Bedroom Ceiling Light"
},
"last_changed": "2024-01-15T09:15:00+00:00",
"last_updated": "2024-01-15T09:15:00+00:00",
"context": {
"id": "01HMV234567890",
"parent_id": null,
"user_id": "user123"
}
},
{
"entity_id": "switch.coffee_maker",
"state": "off",
"attributes": {
"friendly_name": "Coffee Maker"
},
"last_changed": "2024-01-14T22:00:00+00:00",
"last_updated": "2024-01-14T22:00:00+00:00",
"context": {
"id": "01HMV345678901",
"parent_id": null,
"user_id": null
}
},
{
"entity_id": "climate.living_room",
"state": "heat",
"attributes": {
"temperature": 21.0,
"current_temperature": 20.5,
"hvac_modes": ["off", "heat", "cool", "auto"],
"min_temp": 7.0,
"max_temp": 35.0,
"target_temp_step": 0.5,
"friendly_name": "Living Room Thermostat"
},
"last_changed": "2024-01-15T08:00:00+00:00",
"last_updated": "2024-01-15T10:25:00+00:00",
"context": {
"id": "01HMV456789012",
"parent_id": null,
"user_id": "user123"
}
},
{
"entity_id": "sensor.front_door",
"state": "closed",
"attributes": {
"device_class": "door",
"friendly_name": "Front Door"
},
"last_changed": "2024-01-15T07:30:00+00:00",
"last_updated": "2024-01-15T07:30:00+00:00",
"context": {
"id": "01HMV567890123",
"parent_id": null,
"user_id": null
}
},
{
"entity_id": "media_player.living_room_tv",
"state": "playing",
"attributes": {
"volume_level": 0.4,
"is_volume_muted": false,
"media_content_type": "tvshow",
"media_title": "The Office",
"app_name": "Netflix",
"friendly_name": "Living Room TV"
},
"last_changed": "2024-01-15T10:00:00+00:00",
"last_updated": "2024-01-15T10:20:00+00:00",
"context": {
"id": "01HMV678901234",
"parent_id": null,
"user_id": "user123"
}
},
{
"entity_id": "automation.morning_routine",
"state": "on",
"attributes": {
"last_triggered": "2024-01-15T07:00:00+00:00",
"mode": "single",
"current": 0,
"friendly_name": "Morning Routine"
},
"last_changed": "2024-01-14T07:00:00+00:00",
"last_updated": "2024-01-15T07:00:00+00:00",
"context": {
"id": "01HMV789012345",
"parent_id": null,
"user_id": null
}
},
{
"entity_id": "weather.home",
"state": "partly-cloudy",
"attributes": {
"temperature": 18.0,
"humidity": 65,
"pressure": 1013.2,
"wind_speed": 12.5,
"wind_bearing": 225,
"visibility": 16.0,
"forecast": [],
"friendly_name": "Home Weather"
},
"last_changed": "2024-01-15T10:00:00+00:00",
"last_updated": "2024-01-15T10:30:00+00:00",
"context": {
"id": "01HMV890123456",
"parent_id": null,
"user_id": null
}
}
]

View file

@ -0,0 +1,3 @@
{
"message": "API running."
}

View file

@ -0,0 +1,41 @@
{
"photos": 12847,
"videos": 1523,
"usage": 248576851456,
"usageByUser": [
{
"userId": "user-1234-5678-9abc-def0",
"userName": "john.doe",
"photos": 8945,
"videos": 892,
"usage": 156789012345
},
{
"userId": "user-2345-6789-abcd-ef01",
"userName": "jane.smith",
"photos": 2134,
"videos": 423,
"usage": 67891234567
},
{
"userId": "user-3456-789a-bcde-f012",
"userName": "family.shared",
"photos": 1768,
"videos": 208,
"usage": 23896604544
}
],
"usageRaw": 248576851456,
"photosGrowth": {
"date": "2024-01-15",
"value": 42
},
"videosGrowth": {
"date": "2024-01-15",
"value": 7
},
"usageGrowth": {
"date": "2024-01-15",
"value": 2147483648
}
}

View file

@ -0,0 +1,342 @@
[
{
"PlayState": {
"CanSeek": true,
"IsPaused": false,
"IsMuted": false,
"RepeatMode": "RepeatNone",
"ShuffleMode": "Sorted",
"VolumeLevel": 85,
"AudioStreamIndex": 1,
"SubtitleStreamIndex": -1,
"MediaSourceId": "12345abcdef",
"PlayMethod": "DirectPlay",
"PlaySessionId": "session-1-abc123",
"PlaylistItemId": "playlist-item-1",
"PositionTicks": 18000000000
},
"AdditionalUsers": [],
"Capabilities": {
"PlayableMediaTypes": ["Audio", "Video"],
"SupportedCommands": ["Play", "Pause", "Stop", "Seek"]
},
"RemoteEndPoint": "192.168.1.100",
"PlayableMediaTypes": ["Audio", "Video"],
"Id": "session-1-abc123",
"UserId": "user123abc",
"UserName": "john_doe",
"Client": "Jellyfin Web",
"LastActivityDate": "2024-01-15T10:30:00.0000000Z",
"LastPlaybackCheckIn": "2024-01-15T10:30:00.0000000Z",
"DeviceName": "Chrome on Desktop",
"DeviceId": "device-desktop-chrome",
"ApplicationVersion": "10.8.13",
"IsActive": true,
"SupportsMediaControl": true,
"SupportsRemoteControl": true,
"NowPlayingItem": {
"Name": "The Office - S03E01 - Gay Witch Hunt",
"OriginalTitle": "Gay Witch Hunt",
"Id": "episode123abc",
"Etag": "etag123",
"SourceType": "Library",
"PlaylistItemId": "playlist-item-1",
"DateCreated": "2024-01-10T00:00:00.0000000Z",
"DateLastMediaAdded": "2024-01-10T00:00:00.0000000Z",
"ExtraType": null,
"AirsBeforeSeasonNumber": null,
"AirsAfterSeasonNumber": null,
"AirsBeforeEpisodeNumber": null,
"CanDelete": false,
"CanDownload": false,
"HasSubtitles": true,
"Container": "mkv",
"SortName": "office s03e01 gay witch hunt",
"ForcedSortName": null,
"Video3DFormat": null,
"PremiereDate": "2006-09-21T00:00:00.0000000Z",
"ExternalUrls": [],
"MediaSources": [],
"CriticRating": null,
"ProductionLocations": [],
"Path": "/media/tv/The Office/Season 03/S03E01.mkv",
"EnableMediaSourceDisplay": true,
"OfficialRating": "TV-14",
"CustomRating": null,
"ChannelId": null,
"ChannelName": null,
"Overview": "Michael's kiss with Oscar at the Dundies leads to sensitivity training for the office.",
"Taglines": [],
"Genres": ["Comedy"],
"CommunityRating": 8.1,
"CumulativeRunTimeTicks": 13050000000,
"RunTimeTicks": 13050000000,
"PlayAccess": "Full",
"AspectRatio": "16:9",
"ProductionYear": 2006,
"IsPlaceHolder": false,
"Number": null,
"ChannelNumber": null,
"IndexNumber": 1,
"IndexNumberEnd": null,
"ParentIndexNumber": 3,
"RemoteTrailers": [],
"ProviderIds": {},
"IsHD": true,
"IsFolder": false,
"ParentId": "season3abc",
"Type": "Episode",
"People": [],
"Studios": [],
"GenreItems": [],
"ParentLogoItemId": null,
"ParentBackdropItemId": "series123",
"ParentBackdropImageTags": ["backdrop1"],
"LocalTrailerCount": 0,
"UserData": {
"Rating": null,
"PlayedPercentage": 75.5,
"UnplayedItemCount": null,
"PlaybackPositionTicks": 18000000000,
"PlayCount": 1,
"IsFavorite": false,
"Likes": null,
"LastPlayedDate": "2024-01-15T10:30:00.0000000Z",
"Played": false,
"Key": "episode123abc"
},
"RecursiveItemCount": 0,
"ChildCount": 0,
"SeriesName": "The Office",
"SeriesId": "series123",
"SeasonId": "season3abc",
"SpecialFeatureCount": 0,
"DisplayPreferencesId": "episode123abc",
"Status": null,
"AirTime": null,
"AirDays": [],
"Tags": [],
"PrimaryImageAspectRatio": 1.777777777777778,
"Artists": [],
"ArtistItems": [],
"Album": null,
"CollectionType": null,
"DisplayOrder": null,
"AlbumId": null,
"AlbumPrimaryImageTag": null,
"SeriesPrimaryImageTag": "series-primary",
"AlbumArtist": null,
"AlbumArtists": [],
"SeasonName": "Season 3",
"MediaStreams": [],
"VideoType": "VideoFile",
"PartCount": 1,
"MediaSourceCount": 1,
"ImageTags": {
"Primary": "episode-primary"
},
"BackdropImageTags": [],
"ScreenshotImageTags": [],
"ParentLogoImageTag": null,
"ParentArtItemId": null,
"ParentArtImageTag": null,
"SeriesThumbImageTag": null,
"ImageBlurHashes": {},
"SeriesStudio": "NBC",
"ParentThumbItemId": null,
"ParentThumbImageTag": null,
"ParentPrimaryImageItemId": "series123",
"ParentPrimaryImageTag": "series-primary",
"Chapters": [],
"LocationType": "FileSystem",
"IsoType": null,
"MediaType": "Video",
"EndDate": null,
"LockedFields": [],
"TrailerCount": 0,
"MovieCount": 0,
"SeriesCount": 0,
"ProgramCount": 0,
"EpisodeCount": 0,
"SongCount": 0,
"AlbumCount": 0,
"ArtistCount": 0,
"MusicVideoCount": 0,
"LockData": false,
"Width": 1920,
"Height": 1080,
"CameraMake": null,
"CameraModel": null,
"Software": null,
"ExposureTime": null,
"FocalLength": null,
"ImageOrientation": null,
"Aperture": null,
"ShutterSpeed": null,
"Latitude": null,
"Longitude": null,
"Altitude": null,
"IsoSpeedRating": null,
"SeriesTimerId": null,
"ProgramId": null,
"ChannelPrimaryImageTag": null,
"StartDate": null,
"CompletionPercentage": null,
"IsRepeat": null,
"EpisodeTitle": "Gay Witch Hunt",
"ChannelType": null,
"Audio": null,
"IsMovie": false,
"IsSports": false,
"IsNews": false,
"IsKids": false,
"IsPremiere": false,
"TimerId": null,
"NormalizationGain": null,
"CurrentProgram": null
},
"FullNowPlayingItem": {},
"NowViewingItem": null,
"DeviceType": "Desktop",
"NowPlayingQueue": [],
"NowPlayingQueueFullItems": [],
"HasCustomDeviceName": false,
"PlaylistItemId": "playlist-item-1",
"ServerId": "jellyfin-server-123",
"UserPrimaryImageTag": null,
"SupportedCommands": []
},
{
"PlayState": {
"CanSeek": true,
"IsPaused": true,
"IsMuted": false,
"RepeatMode": "RepeatNone",
"ShuffleMode": "Sorted",
"VolumeLevel": 65,
"AudioStreamIndex": 1,
"SubtitleStreamIndex": 2,
"MediaSourceId": "67890defghi",
"PlayMethod": "DirectPlay",
"PlaySessionId": "session-2-def456",
"PlaylistItemId": "playlist-item-2",
"PositionTicks": 45000000000
},
"AdditionalUsers": [],
"Capabilities": {
"PlayableMediaTypes": ["Audio", "Video"],
"SupportedCommands": ["Play", "Pause", "Stop", "Seek"]
},
"RemoteEndPoint": "192.168.1.101",
"PlayableMediaTypes": ["Audio", "Video"],
"Id": "session-2-def456",
"UserId": "user456def",
"UserName": "jane_smith",
"Client": "Jellyfin Android",
"LastActivityDate": "2024-01-15T10:25:00.0000000Z",
"LastPlaybackCheckIn": "2024-01-15T10:25:00.0000000Z",
"DeviceName": "Samsung Galaxy S21",
"DeviceId": "device-android-samsung",
"ApplicationVersion": "2.6.2",
"IsActive": true,
"SupportsMediaControl": true,
"SupportsRemoteControl": true,
"NowPlayingItem": {
"Name": "Inception",
"OriginalTitle": "Inception",
"Id": "movie456def",
"Etag": "etag456",
"SourceType": "Library",
"PlaylistItemId": "playlist-item-2",
"DateCreated": "2024-01-05T00:00:00.0000000Z",
"DateLastMediaAdded": "2024-01-05T00:00:00.0000000Z",
"Container": "mkv",
"SortName": "inception",
"PremiereDate": "2010-07-16T00:00:00.0000000Z",
"Path": "/media/movies/Inception (2010)/Inception.mkv",
"EnableMediaSourceDisplay": true,
"OfficialRating": "PG-13",
"Overview": "A thief who steals corporate secrets through the use of dream-sharing technology is given the inverse task of planting an idea into the mind of a C.E.O.",
"Taglines": ["Your mind is the scene of the crime"],
"Genres": ["Action", "Sci-Fi", "Thriller"],
"CommunityRating": 8.8,
"CumulativeRunTimeTicks": 88800000000,
"RunTimeTicks": 88800000000,
"PlayAccess": "Full",
"AspectRatio": "2.40:1",
"ProductionYear": 2010,
"IsPlaceHolder": false,
"IsHD": true,
"IsFolder": false,
"Type": "Movie",
"LocalTrailerCount": 0,
"UserData": {
"PlayedPercentage": 50.6,
"PlaybackPositionTicks": 45000000000,
"PlayCount": 0,
"IsFavorite": true,
"LastPlayedDate": "2024-01-15T10:25:00.0000000Z",
"Played": false,
"Key": "movie456def"
},
"PrimaryImageAspectRatio": 0.6666666666666666,
"VideoType": "VideoFile",
"PartCount": 1,
"MediaSourceCount": 1,
"ImageTags": {
"Primary": "movie-primary"
},
"BackdropImageTags": ["backdrop1", "backdrop2"],
"LocationType": "FileSystem",
"MediaType": "Video",
"Width": 1920,
"Height": 800,
"IsMovie": true,
"IsSports": false,
"IsNews": false,
"IsKids": false,
"IsPremiere": false
},
"FullNowPlayingItem": {},
"NowViewingItem": null,
"DeviceType": "Phone",
"NowPlayingQueue": [],
"NowPlayingQueueFullItems": [],
"HasCustomDeviceName": false,
"PlaylistItemId": "playlist-item-2",
"ServerId": "jellyfin-server-123",
"UserPrimaryImageTag": null,
"SupportedCommands": []
},
{
"PlayState": null,
"AdditionalUsers": [],
"Capabilities": {
"PlayableMediaTypes": ["Audio", "Video"],
"SupportedCommands": ["Play", "Pause", "Stop"]
},
"RemoteEndPoint": "192.168.1.102",
"PlayableMediaTypes": ["Audio", "Video"],
"Id": "session-3-ghi789",
"UserId": "user789ghi",
"UserName": "family_user",
"Client": "Jellyfin for Roku",
"LastActivityDate": "2024-01-15T10:20:00.0000000Z",
"LastPlaybackCheckIn": "2024-01-15T10:20:00.0000000Z",
"DeviceName": "Roku Ultra",
"DeviceId": "device-roku-ultra",
"ApplicationVersion": "1.6.8",
"IsActive": true,
"SupportsMediaControl": true,
"SupportsRemoteControl": true,
"FullNowPlayingItem": {},
"NowViewingItem": null,
"DeviceType": "Tv",
"NowPlayingQueue": [],
"NowPlayingQueueFullItems": [],
"HasCustomDeviceName": false,
"ServerId": "jellyfin-server-123",
"UserPrimaryImageTag": null,
"SupportedCommands": []
}
]

View file

@ -0,0 +1,35 @@
[
{
"source": "IndexerStatusCheck",
"type": "warning",
"message": "Indexer MusicBrainzDB is unavailable due to recent indexer errors: Service temporarily unavailable",
"wikiUrl": "https://wiki.servarr.com/lidarr/health#indexers-are-unavailable-due-to-recent-failures"
},
{
"source": "ImportMechanismCheck",
"type": "ok",
"message": "No issues with import mechanism checks"
},
{
"source": "DownloadClientStatusCheck",
"type": "ok",
"message": "All download clients are available"
},
{
"source": "RootFolderCheck",
"type": "error",
"message": "Missing root folder: /music",
"wikiUrl": "https://wiki.servarr.com/lidarr/health#missing-root-folder"
},
{
"source": "UpdateCheck",
"type": "ok",
"message": "Update available: 1.3.6.3557 -> 2.0.7.3849"
},
{
"source": "MetadataProviderCheck",
"type": "warning",
"message": "Metadata provider Last.fm API key is invalid or expired",
"wikiUrl": "https://wiki.servarr.com/lidarr/health#metadata-provider-issues"
}
]

View file

@ -0,0 +1,7 @@
{
"totalCount": 4,
"count": 4,
"unknownCount": 0,
"errors": false,
"warnings": false
}

View file

@ -0,0 +1,179 @@
{
"page": 1,
"pageSize": 20,
"sortKey": "releaseDate",
"sortDirection": "descending",
"totalRecords": 7,
"records": [
{
"artistId": 1,
"albumId": 12345,
"foreignAlbumId": "mbid-123-456-789",
"title": "Dark Side of the Moon",
"disambiguation": "",
"overview": "The eighth studio album by Pink Floyd, released in 1973.",
"artistName": "Pink Floyd",
"foreignArtistId": "mbid-artist-123",
"monitored": true,
"anyReleaseOk": true,
"profileId": 1,
"duration": 2580000,
"albumType": "Album",
"secondaryTypes": [],
"mediumCount": 1,
"releaseDate": "1973-03-01T00:00:00Z",
"releases": [
{
"id": 67890,
"albumId": 12345,
"foreignReleaseId": "mbid-release-123",
"title": "Dark Side of the Moon",
"status": "Official",
"duration": 2580000,
"trackCount": 10,
"mediumCount": 1,
"disambiguation": "",
"country": ["US"],
"label": ["Harvest", "Capitol"],
"monitored": true
}
],
"genres": ["Progressive Rock", "Psychedelic Rock"],
"media": [
{
"mediumNumber": 1,
"mediumName": "",
"mediumFormat": "CD"
}
],
"artist": {
"artistName": "Pink Floyd",
"foreignArtistId": "mbid-artist-123",
"nameSlug": "pink-floyd",
"overview": "English rock band formed in London in 1965.",
"disambiguation": "",
"links": [],
"images": [],
"path": "/music/Pink Floyd",
"qualityProfileId": 1,
"metadataProfileId": 1,
"monitored": true,
"monitorNewItems": "all",
"genres": ["Progressive Rock", "Psychedelic Rock", "Art Rock"],
"cleanName": "pinkfloyd",
"sortName": "Pink Floyd",
"tags": [],
"added": "2024-01-01T00:00:00Z",
"ratings": {
"votes": 54321,
"value": 9.2
},
"statistics": {
"albumCount": 15,
"trackFileCount": 142,
"trackCount": 149,
"totalTrackCount": 149,
"sizeOnDisk": 7516192768,
"percentOfTracks": 95.3
},
"id": 1
},
"images": [],
"links": [],
"statistics": {
"trackFileCount": 0,
"trackCount": 10,
"totalTrackCount": 10,
"sizeOnDisk": 0,
"percentOfTracks": 0.0
},
"grabbed": false,
"id": 12345
},
{
"artistId": 2,
"albumId": 23456,
"foreignAlbumId": "mbid-234-567-890",
"title": "OK Computer",
"disambiguation": "",
"overview": "The third studio album by Radiohead, released in 1997.",
"artistName": "Radiohead",
"foreignArtistId": "mbid-artist-234",
"monitored": true,
"anyReleaseOk": true,
"profileId": 1,
"duration": 3230000,
"albumType": "Album",
"secondaryTypes": [],
"mediumCount": 1,
"releaseDate": "1997-06-16T00:00:00Z",
"releases": [
{
"id": 78901,
"albumId": 23456,
"foreignReleaseId": "mbid-release-234",
"title": "OK Computer",
"status": "Official",
"duration": 3230000,
"trackCount": 12,
"mediumCount": 1,
"disambiguation": "",
"country": ["GB"],
"label": ["Parlophone", "Capitol"],
"monitored": true
}
],
"genres": ["Alternative Rock", "Art Rock"],
"media": [
{
"mediumNumber": 1,
"mediumName": "",
"mediumFormat": "CD"
}
],
"artist": {
"artistName": "Radiohead",
"foreignArtistId": "mbid-artist-234",
"nameSlug": "radiohead",
"overview": "English rock band formed in Abingdon, Oxfordshire, in 1985.",
"disambiguation": "",
"links": [],
"images": [],
"path": "/music/Radiohead",
"qualityProfileId": 1,
"metadataProfileId": 1,
"monitored": true,
"monitorNewItems": "all",
"genres": ["Alternative Rock", "Art Rock", "Electronic"],
"cleanName": "radiohead",
"sortName": "Radiohead",
"tags": [],
"added": "2024-01-01T00:00:00Z",
"ratings": {
"votes": 45678,
"value": 8.9
},
"statistics": {
"albumCount": 9,
"trackFileCount": 89,
"trackCount": 95,
"totalTrackCount": 95,
"sizeOnDisk": 4831838208,
"percentOfTracks": 93.7
},
"id": 2
},
"images": [],
"links": [],
"statistics": {
"trackFileCount": 0,
"trackCount": 12,
"totalTrackCount": 12,
"sizeOnDisk": 0,
"percentOfTracks": 0.0
},
"grabbed": false,
"id": 23456
}
]
}

View file

@ -0,0 +1,61 @@
{
"count": 12,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"url": "https://github.com/bastienwirtz/homer",
"title": "Homer - A very simple static homepage for your server",
"description": "A dead simple static HOMepage for your servER to keep your services on hand, from a simple yaml configuration file.",
"notes": "",
"website_title": "GitHub",
"website_description": "GitHub is where over 100 million developers shape the future of software, together.",
"web_archive_snapshot_url": "",
"favicon_url": "https://github.githubassets.com/favicons/favicon.svg",
"preview_image_url": "",
"is_archived": false,
"unread": false,
"shared": false,
"tag_names": ["selfhosted", "dashboard", "yaml"],
"date_added": "2024-01-15T10:30:00.123456Z",
"date_modified": "2024-01-15T10:30:00.123456Z"
},
{
"id": 2,
"url": "https://docs.docker.com/",
"title": "Docker Documentation",
"description": "Official Docker documentation with guides, references, and tutorials for containerization.",
"notes": "Essential for container management",
"website_title": "Docker Docs",
"website_description": "Docker helps developers build, share, run, and verify applications anywhere.",
"web_archive_snapshot_url": "",
"favicon_url": "https://docs.docker.com/favicons/docs@2x.ico",
"preview_image_url": "",
"is_archived": false,
"unread": false,
"shared": false,
"tag_names": ["docker", "containers", "documentation"],
"date_added": "2024-01-14T15:20:00.123456Z",
"date_modified": "2024-01-14T15:20:00.123456Z"
},
{
"id": 3,
"url": "https://nginx.org/en/docs/",
"title": "nginx documentation",
"description": "Official nginx web server documentation covering installation, configuration, and modules.",
"notes": "",
"website_title": "nginx",
"website_description": "nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server.",
"web_archive_snapshot_url": "",
"favicon_url": "https://nginx.org/favicon.ico",
"preview_image_url": "",
"is_archived": false,
"unread": false,
"shared": false,
"tag_names": ["nginx", "webserver", "proxy"],
"date_added": "2024-01-13T09:45:00.123456Z",
"date_modified": "2024-01-13T09:45:00.123456Z"
}
]
}

View file

@ -0,0 +1,6 @@
{
"server": {
"name": "Synapse",
"version": "1.99.0"
}
}

View file

@ -0,0 +1,18 @@
{
"totalRecipes": 247,
"totalUsers": 5,
"totalGroups": 2,
"totalCategories": 18,
"totalTags": 42,
"totalTools": 15,
"totalMealPlans": 156,
"totalShoppingLists": 28,
"totalComments": 89,
"lastUpdated": "2024-01-15T10:30:00Z",
"version": "1.0.0",
"demoStatus": false,
"allowSignup": true,
"defaultGroup": "Home",
"buildVersion": "v1.0.0-1234567",
"apiVersion": "v1"
}

View file

@ -0,0 +1,64 @@
[
{
"id": "meal-123-abc",
"date": "2024-01-15",
"entryType": "dinner",
"title": "Dinner",
"text": "",
"recipe": {
"id": "recipe-456-def",
"name": "Chicken Tikka Masala",
"slug": "chicken-tikka-masala",
"image": "recipe-456-def.webp",
"description": "Creamy tomato-based curry with tender chicken pieces and aromatic spices",
"recipeCategory": [
{
"id": "cat-1",
"name": "Indian",
"slug": "indian"
},
{
"id": "cat-2",
"name": "Main Course",
"slug": "main-course"
}
],
"tags": [
{
"id": "tag-1",
"name": "Curry",
"slug": "curry"
},
{
"id": "tag-2",
"name": "Chicken",
"slug": "chicken"
},
{
"id": "tag-3",
"name": "Spicy",
"slug": "spicy"
}
],
"rating": 4.5,
"totalTime": "45 minutes",
"prepTime": "15 minutes",
"cookTime": "30 minutes",
"performTime": null,
"servings": 4,
"dateAdded": "2024-01-10T00:00:00Z",
"dateUpdated": "2024-01-14T12:30:00Z",
"createdBy": {
"id": "user-789",
"username": "chef_sarah",
"fullName": "Sarah Mitchell"
},
"updateBy": {
"id": "user-789",
"username": "chef_sarah",
"fullName": "Sarah Mitchell"
}
},
"groupId": "group-default-123"
}
]

View file

@ -0,0 +1,108 @@
{
"system": {
"news": {
"unread": 3,
"latest": [
{
"title": "Medusa v1.0.19 Released",
"date": "2024-01-14",
"content": "Bug fixes and performance improvements",
"read": false
},
{
"title": "New indexer support added",
"date": "2024-01-12",
"content": "Support for additional torrent indexers",
"read": false
},
{
"title": "Database maintenance completed",
"date": "2024-01-10",
"content": "Weekly database optimization finished",
"read": false
}
]
},
"version": {
"version": "1.0.19",
"branch": "master",
"commit": "abc123def456",
"dbVersion": 44,
"pythonVersion": "3.11.7"
},
"os": {
"platform": "Linux",
"release": "6.5.0-15-generic",
"version": "#15~22.04.1-Ubuntu"
},
"memory": {
"used": 512.5,
"total": 8192.0,
"percent": 6.3
}
},
"main": {
"logs": {
"numWarnings": 2,
"numErrors": 1,
"loggingLevels": [
"DEBUG",
"INFO",
"WARNING",
"ERROR"
],
"currentLevel": "INFO"
},
"general": {
"webHost": "0.0.0.0",
"webPort": 8081,
"webRoot": "",
"launchBrowser": false,
"versionNotify": true,
"autoUpdate": false,
"logDir": "/app/Logs",
"dataDir": "/app/Data",
"configVersion": 12
},
"showDefaults": {
"status": "Skipped/Wanted/Snatched/Downloaded",
"statusAfter": "Downloaded",
"season_folders": true,
"anime": false,
"scene": false,
"archive_firstmatch": false,
"quality_default": "Standard Definition",
"subtitles": false,
"flatten_folders": false,
"indexer_default": "tvdb",
"indexer_timeout": 20,
"skip_removed_files": false
}
},
"search": {
"general": {
"randomize_providers": false,
"download_propers": true,
"check_propers_interval": "daily",
"propers_search_days": 2,
"backlog_days": 7,
"cache_trimming": false,
"max_cache_age": 30
},
"nzb": {
"nzbs": false,
"nzbs_uid": "",
"nzbs_hash": ""
},
"torrent": {
"torrent_method": "blackhole",
"torrent_path": "",
"torrent_seed_time": 0,
"torrent_paused": false,
"torrent_high_bandwidth": false,
"torrent_label": "",
"torrent_label_anime": "",
"torrent_verify_cert": false
}
}
}

View file

@ -0,0 +1,36 @@
{
"total": 42,
"entries": [
{
"id": 888,
"user_id": 1,
"feed_id": 42,
"title": "Example Unread Entry",
"url": "http://example.org/article.html",
"comments_url": "",
"author": "John Doe",
"content": "<p>This is an unread RSS entry</p>",
"hash": "29f99e4074cdacca1766f47697d03c66070ef6a14770a1fd5a867483c207a1bb",
"published_at": "2025-11-11T16:15:19Z",
"created_at": "2025-11-11T16:15:19Z",
"status": "unread",
"share_code": "",
"starred": false,
"reading_time": 5,
"enclosures": null,
"feed": {
"id": 42,
"user_id": 1,
"title": "Tech Blog",
"site_url": "http://example.org",
"feed_url": "http://example.org/feed.atom",
"checked_at": "2025-11-11T21:06:03.133839Z",
"category": {
"id": 22,
"user_id": 1,
"title": "Technology"
}
}
}
]
}

View file

@ -0,0 +1,10 @@
{
"installed": true,
"maintenance": false,
"needsDbUpgrade": false,
"version": "28.0.2.1",
"versionstring": "28.0.2",
"edition": "",
"productname": "Nextcloud",
"extendedSupport": false
}

View file

@ -0,0 +1,88 @@
{
"count": 1847,
"next": "http://paperless.local/api/documents/?page=2",
"previous": null,
"all": [1, 2, 3, 4, 5],
"results": [
{
"id": 1847,
"correspondent": 15,
"document_type": 12,
"storage_path": null,
"title": "Bank Statement - January 2024",
"content": "Monthly bank statement with account summary and transaction details",
"tags": [8, 15, 23],
"created": "2024-01-15T10:30:00Z",
"created_date": "2024-01-15",
"modified": "2024-01-15T10:30:00Z",
"added": "2024-01-15T10:30:00Z",
"archive_serial_number": "ASN2024001847",
"original_file_name": "bank_statement_202401.pdf",
"archived_file_name": "0001847.pdf"
},
{
"id": 1846,
"correspondent": 23,
"document_type": 5,
"storage_path": null,
"title": "Utility Bill - Electric Company",
"content": "Monthly electricity bill for December 2023",
"tags": [12, 18],
"created": "2024-01-14T16:45:00Z",
"created_date": "2024-01-14",
"modified": "2024-01-14T16:45:00Z",
"added": "2024-01-14T16:45:00Z",
"archive_serial_number": "ASN2024001846",
"original_file_name": "electric_bill_202312.pdf",
"archived_file_name": "0001846.pdf"
},
{
"id": 1845,
"correspondent": 7,
"document_type": 18,
"storage_path": null,
"title": "Insurance Policy Renewal Notice",
"content": "Annual home insurance policy renewal documentation",
"tags": [5, 11, 19],
"created": "2024-01-13T14:20:00Z",
"created_date": "2024-01-13",
"modified": "2024-01-13T14:20:00Z",
"added": "2024-01-13T14:20:00Z",
"archive_serial_number": "ASN2024001845",
"original_file_name": "insurance_renewal_2024.pdf",
"archived_file_name": "0001845.pdf"
},
{
"id": 1844,
"correspondent": 31,
"document_type": 9,
"storage_path": null,
"title": "Tax Document - W2 Form 2023",
"content": "Annual W2 tax form from employer",
"tags": [2, 14, 25],
"created": "2024-01-12T09:15:00Z",
"created_date": "2024-01-12",
"modified": "2024-01-12T09:15:00Z",
"added": "2024-01-12T09:15:00Z",
"archive_serial_number": "ASN2024001844",
"original_file_name": "w2_form_2023.pdf",
"archived_file_name": "0001844.pdf"
},
{
"id": 1843,
"correspondent": 42,
"document_type": 21,
"storage_path": null,
"title": "Medical Records - Annual Checkup",
"content": "Annual physical examination results and health summary",
"tags": [6, 17, 28],
"created": "2024-01-11T11:30:00Z",
"created_date": "2024-01-11",
"modified": "2024-01-11T11:30:00Z",
"added": "2024-01-11T11:30:00Z",
"archive_serial_number": "ASN2024001843",
"original_file_name": "medical_checkup_2024.pdf",
"archived_file_name": "0001843.pdf"
}
]
}

View file

@ -2,7 +2,7 @@
"domains_being_blocked": 152588,
"dns_queries_today": 0,
"ads_blocked_today": 0,
"ads_percentage_today": 42,
"percent_blocked": 42,
"unique_domains": 0,
"queries_forwarded": 0,
"queries_cached": 0,

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<MediaContainer size="3" allowSync="1" art="/:/resources/show-fanart.jpg" identifier="com.plexapp.plugins.library" librarySectionID="0" mediaTagPrefix="/system/bundle/media/flags/" mediaTagVersion="1705317600" thumb="/:/resources/show.png" title1="Plex Library" viewGroup="secondary" viewMode="458752">
<Directory allowSync="1" art="/:/resources/movie-fanart.jpg" composite="/library/sections/1/composite/1705317600" filters="1" refreshing="0" thumb="/:/resources/movie.png" key="1" type="movie" title="Movies" agent="com.plexapp.agents.themoviedb" scanner="Plex Movie" language="en-US" uuid="abcd1234-5678-90ab-cdef-123456789012" updatedAt="1705317600" createdAt="1704067200" scannedAt="1705317000" content="1" directory="1" contentChangedAt="1705316400" hidden="0">
<Location id="1" path="/media/movies" />
</Directory>
<Directory allowSync="1" art="/:/resources/show-fanart.jpg" composite="/library/sections/2/composite/1705317600" filters="1" refreshing="0" thumb="/:/resources/show.png" key="2" type="show" title="TV Shows" agent="com.plexapp.agents.thetvdb" scanner="Plex TV Series" language="en-US" uuid="efgh5678-90ab-cdef-1234-567890abcdef" updatedAt="1705317600" createdAt="1704067200" scannedAt="1705317000" content="1" directory="1" contentChangedAt="1705316400" hidden="0">
<Location id="2" path="/media/tv" />
</Directory>
<Directory allowSync="1" art="/:/resources/artist-fanart.jpg" composite="/library/sections/3/composite/1705317600" filters="1" refreshing="0" thumb="/:/resources/artist.png" key="3" type="artist" title="Music" agent="com.plexapp.agents.lastfm" scanner="Plex Music" language="en-US" uuid="ijkl9012-3456-789a-bcde-f0123456789a" updatedAt="1705317600" createdAt="1704067200" scannedAt="1705317000" content="1" directory="1" contentChangedAt="1705316400" hidden="0">
<Location id="3" path="/media/music" />
</Directory>
</MediaContainer>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<MediaContainer size="247" allowSync="1" art="/:/resources/movie-fanart.jpg" identifier="com.plexapp.plugins.library" librarySectionID="1" librarySectionTitle="Movies" librarySectionUUID="abcd1234-5678-90ab-cdef-123456789012" mediaTagPrefix="/system/bundle/media/flags/" mediaTagVersion="1705317600" mixedParents="0" nocache="1" offset="0" thumb="/:/resources/movie.png" title1="Movies" title2="All Movies" totalSize="247" viewGroup="movie" viewMode="458752">
<Video ratingKey="1001" key="/library/metadata/1001" guid="plex://movie/5d9c086fe98e47001e0d5001" type="movie" title="Inception" titleSort="Inception" contentRating="PG-13" summary="A thief who steals corporate secrets through dream-sharing technology." rating="8.8" audienceRating="9.1" year="2010" tagline="Your mind is the scene of the crime" thumb="/library/metadata/1001/thumb/1705317600" art="/library/metadata/1001/art/1705317600" duration="8880000" originallyAvailableAt="2010-07-16" addedAt="1705144800" updatedAt="1705317600">
<Media id="2001" duration="8880000" bitrate="12000" width="1920" height="1080" aspectRatio="1.78" audioChannels="6" audioCodec="dts" videoCodec="h264" videoResolution="1080" container="mkv" videoFrameRate="24p" audioProfile="dts" videoProfile="high" />
</Video>
<!-- 246 more movies would be here, showing just one for brevity -->
</MediaContainer>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<MediaContainer size="89" allowSync="1" art="/:/resources/show-fanart.jpg" identifier="com.plexapp.plugins.library" librarySectionID="2" librarySectionTitle="TV Shows" librarySectionUUID="efgh5678-90ab-cdef-1234-567890abcdef" mediaTagPrefix="/system/bundle/media/flags/" mediaTagVersion="1705317600" mixedParents="0" nocache="1" offset="0" thumb="/:/resources/show.png" title1="TV Shows" title2="All Shows" totalSize="89" viewGroup="show" viewMode="458752">
<Directory ratingKey="2001" key="/library/metadata/2001" guid="plex://show/5d9c081fe98e47001e0c2001" type="show" title="Breaking Bad" titleSort="Breaking Bad" contentRating="TV-MA" summary="A high school chemistry teacher diagnosed with inoperable lung cancer turns to manufacturing and selling methamphetamine in order to secure his family's future." rating="9.5" year="2008" thumb="/library/metadata/2001/thumb/1705317600" art="/library/metadata/2001/art/1705317600" banner="/library/metadata/2001/banner/1705317600" duration="2820000" originallyAvailableAt="2008-01-20" leafCount="62" viewedLeafCount="62" childCount="5" addedAt="1705144800" updatedAt="1705317600">
<Genre tag="Crime" />
<Genre tag="Drama" />
<Genre tag="Thriller" />
</Directory>
<Directory ratingKey="2002" key="/library/metadata/2002" guid="plex://show/5d9c081fe98e47001e0c2002" type="show" title="Friends" titleSort="Friends" contentRating="TV-14" summary="Follows the personal and professional lives of six twenty to thirty-something-year-old friends living in Manhattan." rating="8.9" year="1994" thumb="/library/metadata/2002/thumb/1705317600" art="/library/metadata/2002/art/1705317600" banner="/library/metadata/2002/banner/1705317600" duration="1320000" originallyAvailableAt="1994-09-22" leafCount="236" viewedLeafCount="236" childCount="10" addedAt="1705144800" updatedAt="1705317600">
<Genre tag="Comedy" />
<Genre tag="Romance" />
</Directory>
<!-- 87 more shows would be here, showing just two for brevity -->
</MediaContainer>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<MediaContainer size="2" allowCameraUpload="1" allowChannelAccess="1" allowMediaDeletion="1" allowSharing="1" allowSync="1" backgroundProcessing="1" certificateVersion="2" companionProxy="1" countryCode="US" diagnostics="logs,databases,streaminglogs" eventStream="1" friendlyName="Homer-Plex-Server" hubSearch="1" itemClusters="1" livenessTimeout="0" machineIdentifier="abc123def456ghi789jkl012" mediaProviders="1" multiuser="1" myPlex="1" myPlexMappingState="mapped" myPlexSigninState="ok" myPlexSubscription="1" myPlexUsername="homer@example.com" offlineTranscode="1" ownerFeatures="adaptive_bitrate,collections,content_filter,dvr,hardware_transcoding,home,loudness_analysis,music_videos,pass,photo_autotags,premium_music_metadata,session_bandwidth_restrictions,sync,trailers,webhooks" photoAutoTag="1" platform="Linux" platformVersion="6.5.0-15-generic" pluginHost="1" pushNotifications="1" readOnlyLibraries="1" requestParametersInCookie="1" streamingBrainABRVersion="3" streamingBrainVersion="2" sync="1" transcoderActiveVideoSessions="0" transcoderAudio="1" transcoderLyrics="1" transcoderPhoto="1" transcoderSubtitles="1" transcoderVideo="1" transcoderVideoBitrates="64,96,208,320,720,1500,2000,3000,4000,8000,10000,12000,20000" transcoderVideoQualities="0,1,2,3,4,5,6,7,8,9,10,11,12" transcoderVideoResolutions="128,128,160,240,320,480,768,720,720,1080,1080,1080,1080" updatedAt="1705317600" updater="1" version="1.40.1.8227-c0dd5a73e" voiceSearch="1">
<Video sessionKey="1" key="/library/metadata/12345" parentKey="/library/metadata/12300" grandparentKey="/library/metadata/12000" guid="plex://episode/5d9c086fe98e47001e0d5c3f" parentGuid="plex://season/602e67d31d3358002d2fb2bd" grandparentGuid="plex://show/5d9c081fe98e47001e0c8382" type="episode" title="The One Where Monica Gets a Roommate" titleSort="One Where Monica Gets a Roommate, The" grandparentTitle="Friends" parentTitle="Season 1" contentRating="TV-14" summary="Monica and the gang introduce Rachel to the real world after she leaves her fiancé at the altar." index="1" parentIndex="1" lastViewedAt="1705315800" year="1994" thumb="/library/metadata/12345/thumb/1705317600" art="/library/metadata/12000/art/1705317600" parentThumb="/library/metadata/12300/thumb/1705317600" grandparentThumb="/library/metadata/12000/thumb/1705317600" grandparentArt="/library/metadata/12000/art/1705317600" duration="1380000" originallyAvailableAt="1994-09-22" addedAt="1705230400" updatedAt="1705317600" chapterSource="media" primaryExtraKey="/library/metadata/12346" ratingKey="12345" viewOffset="420000" skipCount="1">
<Media id="67890" duration="1380000" bitrate="8137" width="1920" height="1080" aspectRatio="1.78" audioChannels="2" audioCodec="aac" videoCodec="h264" videoResolution="1080" container="mkv" videoFrameRate="24p" audioProfile="lc" videoProfile="high">
<Part accessible="1" exists="1" id="98765" key="/library/parts/98765/1705317600/file.mkv" duration="1380000" file="/media/tv/Friends/Season 01/Friends - S01E01 - The One Where Monica Gets a Roommate.mkv" size="1398101419" audioProfile="lc" container="mkv" indexes="sd" videoProfile="high" />
</Media>
<User id="1" thumb="https://plex.tv/users/abc123def456/avatar?c=1705317600" title="John Doe" />
<Player address="192.168.1.100" device="Chrome" machineIdentifier="browser-chrome-192-168-1-100" model="hosted" platform="Chrome" platformVersion="120" product="Plex Web" profile="Web" remotePublicAddress="203.0.113.1" state="playing" title="Chrome (John's Desktop)" userID="1" vendor="Google" version="4.126.1" />
<Session id="session123abc" bandwidth="8000" location="lan" />
</Video>
<Video sessionKey="2" key="/library/metadata/54321" guid="plex://movie/5d9c086fe98e47001e0d5c4a" type="movie" title="The Matrix" titleSort="Matrix, The" contentRating="R" summary="A computer hacker learns from mysterious rebels about the true nature of his reality and his role in the war against its controllers." rating="8.7" year="1999" tagline="The fight for the future begins" thumb="/library/metadata/54321/thumb/1705317600" art="/library/metadata/54321/art/1705317600" duration="8160000" originallyAvailableAt="1999-03-31" addedAt="1705144800" updatedAt="1705317600" chapterSource="media" ratingKey="54321" viewOffset="2100000">
<Media id="11223" duration="8160000" bitrate="15463" width="1920" height="816" aspectRatio="2.35" audioChannels="6" audioCodec="dts" videoCodec="h264" videoResolution="1080" container="mkv" videoFrameRate="24p" audioProfile="dts" videoProfile="high">
<Part accessible="1" exists="1" id="33445" key="/library/parts/33445/1705317600/file.mkv" duration="8160000" file="/media/movies/The Matrix (1999)/The Matrix (1999).mkv" size="15758698701" audioProfile="dts" container="mkv" indexes="sd" videoProfile="high" />
</Media>
<User id="2" thumb="https://plex.tv/users/def456ghi789/avatar?c=1705317600" title="Jane Smith" />
<Player address="192.168.1.101" device="Android" machineIdentifier="android-phone-samsung" model="SM-G991B" platform="Android" platformVersion="14" product="Plex for Android" profile="Mobile" remotePublicAddress="203.0.113.2" state="paused" title="Samsung Galaxy S21" userID="2" vendor="Samsung" version="9.8.1" />
<Session id="session456def" bandwidth="4000" location="lan" />
</Video>
</MediaContainer>

View file

@ -0,0 +1,94 @@
[
{
"Id": 1,
"Name": "local",
"Type": 1,
"URL": "unix:///var/run/docker.sock",
"GroupId": 1,
"PublicURL": "",
"Status": 1,
"UserAccessPolicies": {},
"TeamAccessPolicies": {},
"Extensions": [],
"TagIds": [],
"AssociatedEndpoints": [],
"Snapshots": [
{
"Time": 1705317600,
"DockerVersion": "24.0.7",
"Swarm": false,
"TotalCPU": 8,
"TotalMemory": 16777216000,
"RunningContainerCount": 12,
"StoppedContainerCount": 5,
"HealthyContainerCount": 10,
"UnhealthyContainerCount": 0,
"VolumeCount": 25,
"ImageCount": 47,
"ServiceCount": 0,
"StackCount": 0,
"DockerRootDir": "/var/lib/docker"
}
],
"Kubernetes": {
"Snapshots": []
},
"Agent": {
"NodeName": "",
"ChecklnInterval": 5,
"Version": ""
},
"Edge": {
"AsyncMode": false,
"PingInterval": 60,
"CommandInterval": 5,
"SnapshotInterval": 5
}
},
{
"Id": 2,
"Name": "production",
"Type": 2,
"URL": "tcp://prod-docker:2376",
"GroupId": 1,
"PublicURL": "https://prod-docker.example.com",
"Status": 1,
"UserAccessPolicies": {},
"TeamAccessPolicies": {},
"Extensions": [],
"TagIds": [],
"AssociatedEndpoints": [],
"Snapshots": [
{
"Time": 1705317600,
"DockerVersion": "24.0.7",
"Swarm": false,
"TotalCPU": 16,
"TotalMemory": 33554432000,
"RunningContainerCount": 25,
"StoppedContainerCount": 3,
"HealthyContainerCount": 23,
"UnhealthyContainerCount": 2,
"VolumeCount": 40,
"ImageCount": 75,
"ServiceCount": 0,
"StackCount": 0,
"DockerRootDir": "/var/lib/docker"
}
],
"Kubernetes": {
"Snapshots": []
},
"Agent": {
"NodeName": "",
"ChecklnInterval": 5,
"Version": ""
},
"Edge": {
"AsyncMode": false,
"PingInterval": 60,
"CommandInterval": 5,
"SnapshotInterval": 5
}
}
]

View file

@ -0,0 +1,94 @@
[
{
"Id": 1,
"Name": "local",
"Type": 1,
"URL": "unix:///var/run/docker.sock",
"GroupId": 1,
"PublicURL": "",
"Status": 1,
"UserAccessPolicies": {},
"TeamAccessPolicies": {},
"Extensions": [],
"TagIds": [],
"AssociatedEndpoints": [],
"Snapshots": [
{
"Time": 1705317600,
"DockerVersion": "24.0.7",
"Swarm": false,
"TotalCPU": 8,
"TotalMemory": 16777216000,
"RunningContainerCount": 12,
"StoppedContainerCount": 5,
"HealthyContainerCount": 10,
"UnhealthyContainerCount": 0,
"VolumeCount": 25,
"ImageCount": 47,
"ServiceCount": 0,
"StackCount": 0,
"DockerRootDir": "/var/lib/docker"
}
],
"Kubernetes": {
"Snapshots": []
},
"Agent": {
"NodeName": "",
"ChecklnInterval": 5,
"Version": ""
},
"Edge": {
"AsyncMode": false,
"PingInterval": 60,
"CommandInterval": 5,
"SnapshotInterval": 5
}
},
{
"Id": 2,
"Name": "production",
"Type": 2,
"URL": "tcp://prod-docker:2376",
"GroupId": 1,
"PublicURL": "https://prod-docker.example.com",
"Status": 1,
"UserAccessPolicies": {},
"TeamAccessPolicies": {},
"Extensions": [],
"TagIds": [],
"AssociatedEndpoints": [],
"Snapshots": [
{
"Time": 1705317600,
"DockerVersion": "24.0.7",
"Swarm": false,
"TotalCPU": 16,
"TotalMemory": 33554432000,
"RunningContainerCount": 25,
"StoppedContainerCount": 3,
"HealthyContainerCount": 23,
"UnhealthyContainerCount": 2,
"VolumeCount": 40,
"ImageCount": 75,
"ServiceCount": 0,
"StackCount": 0,
"DockerRootDir": "/var/lib/docker"
}
],
"Kubernetes": {
"Snapshots": []
},
"Agent": {
"NodeName": "",
"ChecklnInterval": 5,
"Version": ""
},
"Edge": {
"AsyncMode": false,
"PingInterval": 60,
"CommandInterval": 5,
"SnapshotInterval": 5
}
}
]

View file

@ -0,0 +1,38 @@
{
"Version": "2.19.4",
"APIVersion": "2.19.4",
"DatabaseVersion": "34",
"Build": {
"BuildNumber": "1234567890",
"ImageTag": "2.19.4-alpine",
"NodejsVersion": "v18.17.1",
"YarnVersion": "1.22.19",
"WebpackVersion": "5.88.2",
"GoVersion": "go1.21.5"
},
"InstanceID": "portainer-instance-abc123def456",
"Edition": "CE",
"DemoEnvironment": {
"Enabled": false,
"URL": ""
},
"AnalyticsEnabled": false,
"AuthenticationMethod": 1,
"Users": 3,
"ValidLicense": false,
"LicenseInfo": {
"Company": "",
"CreatedAt": 0,
"ExpiresAt": 0,
"LicenseKey": "",
"ProductEdition": "",
"Seats": 0,
"Valid": false
},
"RequiredPasswordLength": 12,
"UserSessionTimeout": "8h",
"Features": {
"EdgeDeviceUntrustedMode": false
},
"EdgeAgentCheckinIntervalSeconds": 5
}

View file

@ -0,0 +1,99 @@
{
"status": "success",
"data": {
"alerts": [
{
"labels": {
"alertname": "HighCPUUsage",
"instance": "localhost:9090",
"job": "prometheus",
"severity": "warning"
},
"annotations": {
"description": "CPU usage is above 80% for more than 5 minutes",
"summary": "High CPU usage detected"
},
"state": "firing",
"activeAt": "2024-01-15T10:30:00.000Z",
"value": "85.2"
},
{
"labels": {
"alertname": "HighMemoryUsage",
"instance": "web-server-01:9090",
"job": "node-exporter",
"severity": "critical"
},
"annotations": {
"description": "Memory usage is above 90% for more than 10 minutes",
"summary": "Critical memory usage detected"
},
"state": "firing",
"activeAt": "2024-01-15T10:25:00.000Z",
"value": "92.8"
},
{
"labels": {
"alertname": "DiskSpaceLow",
"instance": "db-server-01:9090",
"job": "node-exporter",
"severity": "warning",
"device": "/dev/sda1"
},
"annotations": {
"description": "Disk space is below 20% on {{ $labels.device }}",
"summary": "Low disk space warning"
},
"state": "pending",
"activeAt": "2024-01-15T11:00:00.000Z",
"value": "18.5"
},
{
"labels": {
"alertname": "ServiceDown",
"instance": "api-server-02:8080",
"job": "api-health-check",
"severity": "critical",
"service": "user-api"
},
"annotations": {
"description": "Service {{ $labels.service }} is not responding",
"summary": "Service is down"
},
"state": "pending",
"activeAt": "2024-01-15T11:10:00.000Z",
"value": "0"
},
{
"labels": {
"alertname": "DatabaseConnections",
"instance": "db-server-01:5432",
"job": "postgres-exporter",
"severity": "info"
},
"annotations": {
"description": "Database connection count is normal",
"summary": "Database connections stable"
},
"state": "inactive",
"activeAt": "2024-01-15T09:00:00.000Z",
"value": "45"
},
{
"labels": {
"alertname": "HTTPResponseTime",
"instance": "web-server-02:80",
"job": "blackbox-exporter",
"severity": "info"
},
"annotations": {
"description": "HTTP response time is within acceptable limits",
"summary": "Response time normal"
},
"state": "inactive",
"activeAt": "2024-01-15T08:30:00.000Z",
"value": "150"
}
]
}
}

View file

@ -0,0 +1,41 @@
[
{
"source": "IndexerStatusCheck",
"type": "warning",
"message": "Indexer 1337x has been disabled due to recent failures: Connection timeout after 30 seconds",
"wikiUrl": "https://wiki.servarr.com/prowlarr/health#indexers-are-unavailable-due-to-recent-failures"
},
{
"source": "IndexerRSSCheck",
"type": "ok",
"message": "All indexer RSS feeds are functioning normally"
},
{
"source": "ApplicationStatusCheck",
"type": "warning",
"message": "Application Sonarr sync failed: Unable to connect to Sonarr at http://sonarr:8989",
"wikiUrl": "https://wiki.servarr.com/prowlarr/health#applications-are-unavailable-due-to-recent-failures"
},
{
"source": "UpdateCheck",
"type": "ok",
"message": "Update available: 1.11.4.4173 -> 1.12.2.4211"
},
{
"source": "IndexerSearchCheck",
"type": "error",
"message": "Indexer TorrentLeech returned invalid search results: Malformed JSON response",
"wikiUrl": "https://wiki.servarr.com/prowlarr/health#indexer-search-failures"
},
{
"source": "ProxyCheck",
"type": "ok",
"message": "No proxy configuration issues detected"
},
{
"source": "IndexerLongTermStatusCheck",
"type": "warning",
"message": "Indexer RARBG has been failing for more than 6 hours: HTTP 403 Forbidden",
"wikiUrl": "https://wiki.servarr.com/prowlarr/health#indexers-are-unavailable-due-to-recent-failures"
}
]

View file

@ -0,0 +1,35 @@
[
{
"source": "IndexerStatusCheck",
"type": "warning",
"message": "Indexer TorrentDay is unavailable due to recent indexer errors: HTTP 503 Service Unavailable",
"wikiUrl": "https://wiki.servarr.com/radarr/health#indexers-are-unavailable-due-to-recent-failures"
},
{
"source": "ImportMechanismCheck",
"type": "ok",
"message": "No issues with import mechanism checks"
},
{
"source": "DownloadClientStatusCheck",
"type": "ok",
"message": "All download clients are available"
},
{
"source": "RootFolderCheck",
"type": "warning",
"message": "Missing root folder: /movies",
"wikiUrl": "https://wiki.servarr.com/radarr/health#missing-root-folder"
},
{
"source": "UpdateCheck",
"type": "ok",
"message": "Update available: 4.7.5.7809 -> 5.0.3.8127"
},
{
"source": "DiskSpaceCheck",
"type": "error",
"message": "Disk space is critically low on /movies: 2.1 GB remaining",
"wikiUrl": "https://wiki.servarr.com/radarr/health#disk-space"
}
]

View file

@ -0,0 +1,8 @@
{
"page": 1,
"pageSize": 20,
"sortKey": "progress",
"sortDirection": "descending",
"totalRecords": 2,
"records": []
}

View file

@ -0,0 +1,16 @@
[
{
"movieId": 1,
"title": "Inception (2010)",
"trackedDownloadStatus": "ok",
"trackedDownloadState": "importPending",
"id": 1
},
{
"movieId": 2,
"title": "The Matrix (1999)",
"trackedDownloadStatus": "warning",
"trackedDownloadState": "downloading",
"id": 2
}
]

View file

@ -0,0 +1,161 @@
{
"page": 1,
"pageSize": 20,
"sortKey": "digitalRelease",
"sortDirection": "descending",
"totalRecords": 5,
"records": [
{
"title": "Dune: Part Two",
"originalTitle": "Dune: Part Two",
"originalLanguage": {
"id": 1,
"name": "English"
},
"alternateTitles": [],
"secondaryYear": null,
"secondaryYearSourceId": 0,
"sortTitle": "dune part two",
"sizeOnDisk": 0,
"status": "released",
"overview": "Follow the mythic journey of Paul Atreides as he unites with Chani and the Fremen while on a path of revenge against the conspirators who destroyed his family.",
"inCinemas": "2024-02-29T00:00:00Z",
"physicalRelease": "2024-05-14T00:00:00Z",
"digitalRelease": "2024-04-16T00:00:00Z",
"images": [],
"website": "",
"remotePoster": "",
"year": 2024,
"hasFile": false,
"youTubeTrailerId": "",
"studio": "Warner Bros. Pictures",
"path": "/movies/Dune Part Two (2024)",
"qualityProfileId": 1,
"monitored": true,
"minimumAvailability": "announced",
"isAvailable": true,
"folderName": "Dune Part Two (2024)",
"runtime": 166,
"cleanTitle": "duneparttwo",
"imdbId": "tt15239678",
"tmdbId": 693134,
"titleSlug": "dune-part-two-2024",
"certification": "PG-13",
"genres": ["Adventure", "Drama", "Science Fiction"],
"tags": [],
"added": "2024-01-10T00:00:00Z",
"ratings": {
"votes": 234567,
"value": 8.9
},
"movieFile": null,
"collection": {
"name": "Dune Collection",
"tmdbId": 726871,
"images": []
},
"popularity": 89.245,
"id": 3
},
{
"title": "Oppenheimer",
"originalTitle": "Oppenheimer",
"originalLanguage": {
"id": 1,
"name": "English"
},
"alternateTitles": [],
"secondaryYear": null,
"secondaryYearSourceId": 0,
"sortTitle": "oppenheimer",
"sizeOnDisk": 0,
"status": "released",
"overview": "The story of J. Robert Oppenheimer's role in the development of the atomic bomb during World War II.",
"inCinemas": "2023-07-21T00:00:00Z",
"physicalRelease": "2023-11-21T00:00:00Z",
"digitalRelease": "2023-10-31T00:00:00Z",
"images": [],
"website": "",
"remotePoster": "",
"year": 2023,
"hasFile": false,
"youTubeTrailerId": "",
"studio": "Universal Pictures",
"path": "/movies/Oppenheimer (2023)",
"qualityProfileId": 1,
"monitored": true,
"minimumAvailability": "announced",
"isAvailable": true,
"folderName": "Oppenheimer (2023)",
"runtime": 180,
"cleanTitle": "oppenheimer",
"imdbId": "tt15398776",
"tmdbId": 872585,
"titleSlug": "oppenheimer-2023",
"certification": "R",
"genres": ["Drama", "History"],
"tags": [],
"added": "2024-01-05T00:00:00Z",
"ratings": {
"votes": 456789,
"value": 8.4
},
"movieFile": null,
"collection": null,
"popularity": 92.567,
"id": 4
},
{
"title": "Spider-Man: Across the Spider-Verse",
"originalTitle": "Spider-Man: Across the Spider-Verse",
"originalLanguage": {
"id": 1,
"name": "English"
},
"alternateTitles": [],
"secondaryYear": null,
"secondaryYearSourceId": 0,
"sortTitle": "spider man across spider verse",
"sizeOnDisk": 0,
"status": "released",
"overview": "After reuniting with Gwen Stacy, Brooklyn's full-time, friendly neighborhood Spider-Man is catapulted across the Multiverse, where he encounters the Spider-Society.",
"inCinemas": "2023-06-02T00:00:00Z",
"physicalRelease": "2023-09-05T00:00:00Z",
"digitalRelease": "2023-08-08T00:00:00Z",
"images": [],
"website": "",
"remotePoster": "",
"year": 2023,
"hasFile": false,
"youTubeTrailerId": "",
"studio": "Sony Pictures Animation",
"path": "/movies/Spider-Man Across the Spider-Verse (2023)",
"qualityProfileId": 2,
"monitored": true,
"minimumAvailability": "announced",
"isAvailable": true,
"folderName": "Spider-Man Across the Spider-Verse (2023)",
"runtime": 140,
"cleanTitle": "spidermanacrossthespiderverse",
"imdbId": "tt9362722",
"tmdbId": 569094,
"titleSlug": "spider-man-across-the-spider-verse-2023",
"certification": "PG",
"genres": ["Animation", "Action", "Adventure"],
"tags": [],
"added": "2024-01-03T00:00:00Z",
"ratings": {
"votes": 345678,
"value": 8.7
},
"movieFile": null,
"collection": {
"name": "Spider-Verse Collection",
"tmdbId": 573436,
"images": []
},
"popularity": 85.432,
"id": 5
}
]
}

97
dummy-data/sabnzbd/api Normal file
View file

@ -0,0 +1,97 @@
{
"queue": {
"version": "4.1.0",
"paused": false,
"pause_int": "0",
"paused_all": false,
"diskspace1": "465.47",
"diskspace2": "465.47",
"diskspacetotal1": "931.51",
"diskspacetotal2": "931.51",
"speedlimit": "",
"speedlimit_abs": "",
"have_warnings": "0",
"finishaction": null,
"quota": "",
"left_quota": "0 ",
"cache_art": "0",
"cache_size": "0 B",
"kbpersec": "8547.82",
"speed": "8.35",
"mbleft": "2847.93",
"mb": "2847.93",
"noofslots": 3,
"noofslots_total": 8,
"status": "Downloading",
"timeleft": "0:05:41",
"eta": "22:15:41",
"slots": [
{
"index": 0,
"nzo_id": "SABnzbd_nzo_abc123",
"unpackopts": "3",
"priority": "Normal",
"script": "None",
"filename": "Ubuntu.22.04.3.Desktop.amd64.iso",
"labels": [],
"password": "",
"cat": "software",
"mbleft": "1847.52",
"mb": "1847.52",
"size": "1.8 GB",
"sizeleft": "1.8 GB",
"percentage": "0",
"mbmissing": "0.00",
"direct_unpack": "0",
"status": "Downloading",
"timeleft": "0:03:41",
"eta": "unknown",
"avg_age": "4d"
},
{
"index": 1,
"nzo_id": "SABnzbd_nzo_def456",
"unpackopts": "3",
"priority": "High",
"script": "None",
"filename": "Movie.Collection.2023.1080p.BluRay.x264",
"labels": ["movie"],
"password": "",
"cat": "movies",
"mbleft": "756.41",
"mb": "756.41",
"size": "756.4 MB",
"sizeleft": "756.4 MB",
"percentage": "12",
"mbmissing": "0.00",
"direct_unpack": "0",
"status": "Downloading",
"timeleft": "0:01:35",
"eta": "unknown",
"avg_age": "1d"
},
{
"index": 2,
"nzo_id": "SABnzbd_nzo_ghi789",
"unpackopts": "3",
"priority": "Normal",
"script": "cleanup.py",
"filename": "TV.Show.S05E08.1080p.WEB.H264-GROUP",
"labels": ["tv"],
"password": "",
"cat": "tv",
"mbleft": "244.00",
"mb": "244.00",
"size": "244.0 MB",
"sizeleft": "244.0 MB",
"percentage": "65",
"mbmissing": "0.00",
"direct_unpack": "1",
"status": "Downloading",
"timeleft": "0:00:25",
"eta": "unknown",
"avg_age": "12h"
}
]
}
}

View file

@ -0,0 +1,162 @@
{
"success": true,
"data": {
"summary": {
"0x5000cca264eb01d7": {
"device": {
"wwn": "0x5000cca264eb01d7",
"device_name": "Samsung SSD 980 1TB",
"device_uuid": "WWN-0x5000cca264eb01d7",
"device_serial_id": "S64HNE0T123456A",
"device_label": "",
"manufacture": "Samsung",
"model_name": "Samsung SSD 980 1TB",
"interface_type": "nvme",
"interface_speed": "",
"serial_number": "S64HNE0T123456A",
"firmware": "2B4QGXA7",
"rotational_speed": 0,
"capacity": 1000204886016,
"form_factor": "",
"smart_support": true,
"device_protocol": "NVMe",
"device_type": "",
"device_status": 0,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T11:45:00Z",
"deleted_at": null
},
"smart": {
"collector_date": "2024-01-15T11:45:00Z",
"temp": 35,
"power_on_hours": 2847,
"power_cycle_count": 1247
}
},
"0x5000cca264eb01d8": {
"device": {
"wwn": "0x5000cca264eb01d8",
"device_name": "Western Digital WD Blue 2TB",
"device_uuid": "WWN-0x5000cca264eb01d8",
"device_serial_id": "WD-WCC4N7DS2468",
"device_label": "",
"manufacture": "Western Digital",
"model_name": "WDC WD20EZAZ-00GXCB0",
"interface_type": "ata",
"interface_speed": "",
"serial_number": "WD-WCC4N7DS2468",
"firmware": "80.00A80",
"rotational_speed": 5400,
"capacity": 2000398934016,
"form_factor": "",
"smart_support": true,
"device_protocol": "ATA",
"device_type": "",
"device_status": 0,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T11:45:00Z",
"deleted_at": null
},
"smart": {
"collector_date": "2024-01-15T11:45:00Z",
"temp": 41,
"power_on_hours": 8942,
"power_cycle_count": 892
}
},
"0x500a0751e6b8a7c3": {
"device": {
"wwn": "0x500a0751e6b8a7c3",
"device_name": "Seagate Barracuda 4TB",
"device_uuid": "WWN-0x500a0751e6b8a7c3",
"device_serial_id": "ST4000DM004-2CV104",
"device_label": "",
"manufacture": "Seagate",
"model_name": "ST4000DM004-2CV104",
"interface_type": "ata",
"interface_speed": "",
"serial_number": "ZFN123AB",
"firmware": "0001",
"rotational_speed": 5400,
"capacity": 4000787030016,
"form_factor": "",
"smart_support": true,
"device_protocol": "ATA",
"device_type": "",
"device_status": 2,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T11:45:00Z",
"deleted_at": null
},
"smart": {
"collector_date": "2024-01-15T11:45:00Z",
"temp": 47,
"power_on_hours": 12456,
"power_cycle_count": 456
}
},
"0x5000cca264eb01d9": {
"device": {
"wwn": "0x5000cca264eb01d9",
"device_name": "Kingston NV2 500GB",
"device_uuid": "WWN-0x5000cca264eb01d9",
"device_serial_id": "50026B7784123456",
"device_label": "",
"manufacture": "Kingston",
"model_name": "KINGSTON SNV2S500G",
"interface_type": "nvme",
"interface_speed": "",
"serial_number": "50026B7784123456",
"firmware": "SNV2S2.1.0",
"rotational_speed": 0,
"capacity": 500107862016,
"form_factor": "",
"smart_support": true,
"device_protocol": "NVMe",
"device_type": "",
"device_status": 4,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T11:45:00Z",
"deleted_at": null
},
"smart": {
"collector_date": "2024-01-15T11:45:00Z",
"temp": 52,
"power_on_hours": 1247,
"power_cycle_count": 89
}
},
"0x5000cca264eb01da": {
"device": {
"wwn": "0x5000cca264eb01da",
"device_name": "Crucial MX500 1TB",
"device_uuid": "WWN-0x5000cca264eb01da",
"device_serial_id": "194251A12345",
"device_label": "",
"manufacture": "Crucial",
"model_name": "CT1000MX500SSD1",
"interface_type": "ata",
"interface_speed": "",
"serial_number": "194251A12345",
"firmware": "M3CR033",
"rotational_speed": 0,
"capacity": 1000204886016,
"form_factor": "",
"smart_support": true,
"device_protocol": "ATA",
"device_type": "",
"device_status": 0,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T11:45:00Z",
"deleted_at": null
},
"smart": {
"collector_date": "2024-01-15T11:45:00Z",
"temp": 39,
"power_on_hours": 5642,
"power_cycle_count": 1089
}
}
}
}
}

View file

@ -0,0 +1,29 @@
[
{
"source": "IndexerStatusCheck",
"type": "warning",
"message": "Indexer MyIndexer is unavailable due to recent indexer errors: Request timeout",
"wikiUrl": "https://wiki.servarr.com/sonarr/health#indexers-are-unavailable-due-to-recent-failures"
},
{
"source": "ImportMechanismCheck",
"type": "ok",
"message": "No issues with import mechanism checks"
},
{
"source": "DownloadClientStatusCheck",
"type": "ok",
"message": "All download clients are available"
},
{
"source": "RootFolderCheck",
"type": "warning",
"message": "Missing root folder: /media/tv",
"wikiUrl": "https://wiki.servarr.com/sonarr/health#missing-root-folder"
},
{
"source": "UpdateCheck",
"type": "ok",
"message": "Update available: 3.0.10.1567 -> 4.0.1.929"
}
]

View file

@ -0,0 +1,77 @@
{
"page": 1,
"pageSize": 20,
"sortKey": "progress",
"sortDirection": "descending",
"totalRecords": 3,
"records": [
{
"seriesId": 1,
"episodeId": 12345,
"seasonNumber": 5,
"episodeNumber": 8,
"title": "The Office S05E08 - Business Trip",
"size": 1073741824,
"sizeleft": 0,
"timeleft": "00:00:00",
"estimatedCompletionTime": "2024-01-15T10:30:00Z",
"status": "completed",
"trackedDownloadStatus": "ok",
"trackedDownloadState": "importPending",
"statusMessages": [],
"downloadId": "download123abc",
"protocol": "torrent",
"downloadClient": "qBittorrent",
"indexer": "Prowlarr",
"outputPath": "/downloads/complete/The.Office.US.S05E08.720p.WEB.x264-GROUP",
"id": 1
},
{
"seriesId": 2,
"episodeId": 67890,
"seasonNumber": 3,
"episodeNumber": 15,
"title": "Breaking Bad S03E15 - Half Measures",
"size": 2147483648,
"sizeleft": 536870912,
"timeleft": "00:15:23",
"estimatedCompletionTime": "2024-01-15T10:45:23Z",
"status": "downloading",
"trackedDownloadStatus": "ok",
"trackedDownloadState": "downloading",
"statusMessages": [],
"downloadId": "download456def",
"protocol": "torrent",
"downloadClient": "qBittorrent",
"indexer": "Prowlarr",
"outputPath": "/downloads/incomplete/Breaking.Bad.S03E15.1080p.BluRay.x264-GROUP",
"id": 2
},
{
"seriesId": 3,
"episodeId": 11121,
"seasonNumber": 1,
"episodeNumber": 3,
"title": "Better Call Saul S01E03 - Nacho",
"size": 1610612736,
"sizeleft": 1073741824,
"timeleft": "01:24:15",
"estimatedCompletionTime": "2024-01-15T12:09:15Z",
"status": "downloading",
"trackedDownloadStatus": "warning",
"trackedDownloadState": "downloading",
"statusMessages": [
{
"title": "Slow Download Speed",
"messages": ["Download speed is below expected threshold"]
}
],
"downloadId": "download789ghi",
"protocol": "usenet",
"downloadClient": "SABnzbd",
"indexer": "NZBgeek",
"outputPath": "/downloads/incomplete/Better.Call.Saul.S01E03.720p.WEB.x264-GROUP",
"id": 3
}
]
}

View file

@ -0,0 +1,129 @@
{
"page": 1,
"pageSize": 20,
"sortKey": "airDateUtc",
"sortDirection": "descending",
"totalRecords": 15,
"records": [
{
"seriesId": 1,
"tvdbId": 73244,
"seasonNumber": 9,
"episodeNumber": 23,
"title": "Finale",
"airDate": "2013-05-16",
"airDateUtc": "2013-05-17T01:00:00Z",
"overview": "One year later, Dunder Mifflin Scranton has thrived, but Jim and Pam are looking to a move to Philadelphia.",
"episodeFile": null,
"hasFile": false,
"monitored": true,
"absoluteEpisodeNumber": 201,
"series": {
"title": "The Office (US)",
"sortTitle": "office us",
"seasonCount": 9,
"status": "ended",
"overview": "A mockumentary about a group of typical office workers.",
"network": "NBC",
"airTime": "21:00",
"images": [],
"seasons": [],
"year": 2005,
"path": "/media/tv/The Office (US)",
"qualityProfileId": 1,
"languageProfileId": 1,
"seasonFolder": true,
"monitored": true,
"useSceneNumbering": false,
"runtime": 22,
"tvdbId": 73244,
"tvRageId": 6061,
"tvMazeId": 526,
"firstAired": "2005-03-24T00:00:00Z",
"lastInfoSync": "2024-01-15T09:00:00Z",
"seriesType": "standard",
"cleanTitle": "theoffice",
"imdbId": "tt0386676",
"titleSlug": "the-office-us",
"certification": "TV-14",
"genres": ["Comedy"],
"tags": [],
"added": "2024-01-01T00:00:00Z",
"ratings": {
"votes": 654321,
"value": 8.9
},
"statistics": {
"seasonCount": 9,
"episodeFileCount": 200,
"episodeCount": 201,
"totalEpisodeCount": 201,
"sizeOnDisk": 107374182400,
"percentOfEpisodes": 99.5
},
"id": 1
},
"id": 4567
},
{
"seriesId": 2,
"tvdbId": 81189,
"seasonNumber": 5,
"episodeNumber": 16,
"title": "Felina",
"airDate": "2013-09-29",
"airDateUtc": "2013-09-30T01:00:00Z",
"overview": "Walt's final confrontation with his past mistakes leads to a climactic conclusion.",
"episodeFile": null,
"hasFile": false,
"monitored": true,
"absoluteEpisodeNumber": 62,
"series": {
"title": "Breaking Bad",
"sortTitle": "breaking bad",
"seasonCount": 5,
"status": "ended",
"overview": "A high school chemistry teacher turned methamphetamine manufacturer.",
"network": "AMC",
"airTime": "21:00",
"images": [],
"seasons": [],
"year": 2008,
"path": "/media/tv/Breaking Bad",
"qualityProfileId": 1,
"languageProfileId": 1,
"seasonFolder": true,
"monitored": true,
"useSceneNumbering": false,
"runtime": 47,
"tvdbId": 81189,
"tvRageId": 18164,
"tvMazeId": 169,
"firstAired": "2008-01-20T00:00:00Z",
"lastInfoSync": "2024-01-15T09:00:00Z",
"seriesType": "standard",
"cleanTitle": "breakingbad",
"imdbId": "tt0903747",
"titleSlug": "breaking-bad",
"certification": "TV-MA",
"genres": ["Crime", "Drama", "Thriller"],
"tags": [],
"added": "2024-01-01T00:00:00Z",
"ratings": {
"votes": 1234567,
"value": 9.5
},
"statistics": {
"seasonCount": 5,
"episodeFileCount": 61,
"episodeCount": 62,
"totalEpisodeCount": 62,
"sizeOnDisk": 161061273600,
"percentOfEpisodes": 98.4
},
"id": 2
},
"id": 8901
}
]
}

185
dummy-data/tautulli/api/v2 Normal file
View file

@ -0,0 +1,185 @@
{
"response": {
"result": "success",
"message": null,
"data": {
"stream_count": 3,
"stream_count_direct_play": 1,
"stream_count_direct_stream": 1,
"stream_count_transcode": 1,
"total_bandwidth": 15420,
"wan_bandwidth": 8240,
"lan_bandwidth": 7180,
"sessions": [
{
"session_key": "425",
"session_id": "tautulli_session_425",
"media_index": "1",
"parent_media_index": "1",
"art": "/library/metadata/98765/art/1705317600",
"thumb": "/library/metadata/98765/thumb/1705317600",
"grandparent_thumb": "/library/metadata/98765/thumb/1705317600",
"title": "The One Where Monica Gets a Roommate",
"parent_title": "Season 1",
"grandparent_title": "Friends",
"original_title": "",
"year": 1994,
"media_type": "episode",
"rating_key": "98765",
"parent_rating_key": "98764",
"grandparent_rating_key": "98763",
"state": "playing",
"session_progress": 42,
"view_offset": 630000,
"duration": 1500000,
"remaining_time": 870000,
"progress_percent": 42,
"username": "john_doe",
"friendly_name": "John's iPhone",
"user_id": 1,
"user": "john_doe",
"ip_address": "192.168.1.105",
"ip_address_public": "203.0.113.45",
"location": "lan",
"secure": 1,
"relayed": 0,
"platform": "iOS",
"platform_name": "iPhone",
"platform_version": "17.2",
"product": "Plex for iOS",
"product_version": "8.25.1",
"profile": "Mobile",
"player": "PlexMobile",
"machine_id": "abc123def456",
"bandwidth": 4820,
"quality_profile": "4 Mbps 720p",
"video_resolution": "720p",
"video_framerate": "24p",
"video_codec": "h264",
"video_bitrate": 4200,
"video_width": 1280,
"video_height": 720,
"audio_codec": "aac",
"audio_bitrate": 128,
"audio_channels": 2,
"transcode_decision": "transcode",
"stream_container": "mkv",
"stream_video_codec": "h264",
"stream_audio_codec": "aac"
},
{
"session_key": "426",
"session_id": "tautulli_session_426",
"media_index": "3",
"parent_media_index": "2",
"art": "/library/metadata/45678/art/1705317600",
"thumb": "/library/metadata/45678/thumb/1705317600",
"grandparent_thumb": "/library/metadata/45678/thumb/1705317600",
"title": "The One with the Sonogram at the End",
"parent_title": "Season 1",
"grandparent_title": "Friends",
"original_title": "",
"year": 1994,
"media_type": "episode",
"rating_key": "45678",
"parent_rating_key": "45677",
"grandparent_rating_key": "45676",
"state": "playing",
"session_progress": 18,
"view_offset": 270000,
"duration": 1500000,
"remaining_time": 1230000,
"progress_percent": 18,
"username": "sarah_smith",
"friendly_name": "Sarah's TV",
"user_id": 2,
"user": "sarah_smith",
"ip_address": "192.168.1.110",
"ip_address_public": "203.0.113.45",
"location": "lan",
"secure": 1,
"relayed": 0,
"platform": "Roku",
"platform_name": "Roku Ultra",
"platform_version": "12.5.0",
"product": "Plex for Roku",
"product_version": "6.8.0",
"profile": "Roku",
"player": "Roku",
"machine_id": "def456ghi789",
"bandwidth": 2360,
"quality_profile": "2 Mbps 480p",
"video_resolution": "480p",
"video_framerate": "24p",
"video_codec": "h264",
"video_bitrate": 2000,
"video_width": 720,
"video_height": 480,
"audio_codec": "aac",
"audio_bitrate": 128,
"audio_channels": 2,
"transcode_decision": "direct_stream",
"stream_container": "mkv",
"stream_video_codec": "h264",
"stream_audio_codec": "aac"
},
{
"session_key": "427",
"session_id": "tautulli_session_427",
"media_index": null,
"parent_media_index": null,
"art": "/library/metadata/12345/art/1705317600",
"thumb": "/library/metadata/12345/thumb/1705317600",
"grandparent_thumb": "",
"title": "Inception",
"parent_title": "",
"grandparent_title": "",
"original_title": "Inception",
"year": 2010,
"media_type": "movie",
"rating_key": "12345",
"parent_rating_key": "",
"grandparent_rating_key": "",
"state": "playing",
"session_progress": 67,
"view_offset": 5952000,
"duration": 8880000,
"remaining_time": 2928000,
"progress_percent": 67,
"username": "movie_buff",
"friendly_name": "Living Room TV",
"user_id": 3,
"user": "movie_buff",
"ip_address": "10.0.0.15",
"ip_address_public": "198.51.100.25",
"location": "wan",
"secure": 1,
"relayed": 1,
"platform": "Android TV",
"platform_name": "NVIDIA Shield TV",
"platform_version": "11",
"product": "Plex for Android TV",
"product_version": "9.12.0",
"profile": "Android TV",
"player": "AndroidTV",
"machine_id": "ghi789jkl012",
"bandwidth": 8240,
"quality_profile": "8 Mbps 1080p",
"video_resolution": "1080p",
"video_framerate": "24p",
"video_codec": "h264",
"video_bitrate": 8000,
"video_width": 1920,
"video_height": 1080,
"audio_codec": "ac3",
"audio_bitrate": 640,
"audio_channels": 6,
"transcode_decision": "direct_play",
"stream_container": "mkv",
"stream_video_codec": "h264",
"stream_audio_codec": "ac3"
}
]
}
}
}

View file

@ -0,0 +1 @@
"TrueNAS-SCALE-22.12.4.2"

View file

@ -0,0 +1,64 @@
{
"config": {
"title": "Homer Dashboard Status",
"description": "Status page for all monitored services",
"icon": "/icon.svg",
"theme": "light",
"published": true,
"showTags": true,
"domainNames": [
"status.homer.local"
],
"customCSS": "",
"footerText": null,
"showPoweredBy": true,
"googleAnalyticsId": null,
"showCertificateExpiry": false,
"certExpiryDays": 14
},
"incident": null,
"publicGroupList": [
{
"id": 1,
"name": "Web Services",
"weight": 1,
"monitorList": [
{
"id": 1,
"name": "Main Website",
"url": "https://example.com",
"type": "http",
"interval": 60
},
{
"id": 2,
"name": "API Server",
"url": "https://api.example.com",
"type": "http",
"interval": 60
}
]
},
{
"id": 2,
"name": "Infrastructure",
"weight": 2,
"monitorList": [
{
"id": 3,
"name": "Database Server",
"url": "postgresql://db.example.com:5432",
"type": "postgres",
"interval": 120
},
{
"id": 4,
"name": "Redis Cache",
"url": "redis://cache.example.com:6379",
"type": "redis",
"interval": 60
}
]
}
]
}

View file

@ -0,0 +1,114 @@
{
"heartbeatList": {
"1": [
{
"status": 1,
"time": "2024-01-15 10:00:00",
"msg": "200 - OK",
"ping": 45,
"important": false,
"duration": 0
},
{
"status": 1,
"time": "2024-01-15 10:01:00",
"msg": "200 - OK",
"ping": 52,
"important": false,
"duration": 0
},
{
"status": 1,
"time": "2024-01-15 10:02:00",
"msg": "200 - OK",
"ping": 38,
"important": false,
"duration": 0
}
],
"2": [
{
"status": 1,
"time": "2024-01-15 10:00:00",
"msg": "200 - OK",
"ping": 67,
"important": false,
"duration": 0
},
{
"status": 1,
"time": "2024-01-15 10:01:00",
"msg": "200 - OK",
"ping": 71,
"important": false,
"duration": 0
},
{
"status": 1,
"time": "2024-01-15 10:02:00",
"msg": "200 - OK",
"ping": 63,
"important": false,
"duration": 0
}
],
"3": [
{
"status": 1,
"time": "2024-01-15 10:00:00",
"msg": "Connected successfully",
"ping": 12,
"important": false,
"duration": 0
},
{
"status": 0,
"time": "2024-01-15 10:02:00",
"msg": "Connection timeout",
"ping": null,
"important": true,
"duration": 5000
},
{
"status": 1,
"time": "2024-01-15 10:04:00",
"msg": "Connected successfully",
"ping": 15,
"important": false,
"duration": 0
}
],
"4": [
{
"status": 1,
"time": "2024-01-15 10:00:00",
"msg": "PONG received",
"ping": 3,
"important": false,
"duration": 0
},
{
"status": 1,
"time": "2024-01-15 10:01:00",
"msg": "PONG received",
"ping": 2,
"important": false,
"duration": 0
},
{
"status": 1,
"time": "2024-01-15 10:02:00",
"msg": "PONG received",
"ping": 4,
"important": false,
"duration": 0
}
]
},
"uptimeList": {
"1": 1.0,
"2": 1.0,
"3": 0.95,
"4": 1.0
}
}

View file

@ -0,0 +1 @@
"1.30.3"

View file

@ -0,0 +1,182 @@
[
{
"id": "nginx-proxy",
"name": "nginx-proxy",
"watcher": "docker",
"image": {
"registry": {
"name": "hub.docker.com",
"url": "https://registry-1.docker.io/v2"
},
"name": "nginx",
"tag": {
"value": "1.25.3",
"semver": true
},
"digest": {
"watch": false,
"repo": "sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
},
"architecture": "amd64",
"os": "linux",
"created": "2023-12-15T10:30:00Z"
},
"result": {
"tag": "1.25.4"
},
"updateAvailable": true,
"status": "running",
"created": "2024-01-10T08:15:00Z",
"updated": "2024-01-15T11:30:00Z"
},
{
"id": "postgres-db",
"name": "postgres-db",
"watcher": "docker",
"image": {
"registry": {
"name": "hub.docker.com",
"url": "https://registry-1.docker.io/v2"
},
"name": "postgres",
"tag": {
"value": "15.5-alpine",
"semver": true
},
"digest": {
"watch": false,
"repo": "sha256:2345678901abcdef2345678901abcdef2345678901abcdef2345678901abcdef"
},
"architecture": "amd64",
"os": "linux",
"created": "2023-11-28T14:22:00Z"
},
"result": {
"tag": "16.1-alpine"
},
"updateAvailable": true,
"status": "running",
"created": "2024-01-08T12:45:00Z",
"updated": "2024-01-15T11:30:00Z"
},
{
"id": "redis-cache",
"name": "redis-cache",
"watcher": "docker",
"image": {
"registry": {
"name": "hub.docker.com",
"url": "https://registry-1.docker.io/v2"
},
"name": "redis",
"tag": {
"value": "7.2.4-alpine",
"semver": true
},
"digest": {
"watch": false,
"repo": "sha256:3456789012abcdef3456789012abcdef3456789012abcdef3456789012abcdef"
},
"architecture": "amd64",
"os": "linux",
"created": "2024-01-12T09:18:00Z"
},
"result": {
"tag": "7.2.4-alpine"
},
"updateAvailable": false,
"status": "running",
"created": "2024-01-12T16:20:00Z",
"updated": "2024-01-15T11:30:00Z"
},
{
"id": "portainer-agent",
"name": "portainer-agent",
"watcher": "docker",
"image": {
"registry": {
"name": "hub.docker.com",
"url": "https://registry-1.docker.io/v2"
},
"name": "portainer/agent",
"tag": {
"value": "2.19.4",
"semver": true
},
"digest": {
"watch": false,
"repo": "sha256:4567890123abcdef4567890123abcdef4567890123abcdef4567890123abcdef"
},
"architecture": "amd64",
"os": "linux",
"created": "2024-01-05T07:42:00Z"
},
"result": {
"tag": "2.19.4"
},
"updateAvailable": false,
"status": "running",
"created": "2024-01-05T14:30:00Z",
"updated": "2024-01-15T11:30:00Z"
},
{
"id": "app-backend",
"name": "app-backend",
"watcher": "docker",
"image": {
"registry": {
"name": "hub.docker.com",
"url": "https://registry-1.docker.io/v2"
},
"name": "node",
"tag": {
"value": "18.19.0-alpine",
"semver": true
},
"digest": {
"watch": false,
"repo": "sha256:5678901234abcdef5678901234abcdef5678901234abcdef5678901234abcdef"
},
"architecture": "amd64",
"os": "linux",
"created": "2023-12-28T11:15:00Z"
},
"result": {
"tag": "20.11.0-alpine"
},
"updateAvailable": true,
"status": "running",
"created": "2024-01-07T09:12:00Z",
"updated": "2024-01-15T11:30:00Z"
},
{
"id": "prometheus",
"name": "prometheus",
"watcher": "docker",
"image": {
"registry": {
"name": "quay.io",
"url": "https://quay.io/v2"
},
"name": "prometheus/prometheus",
"tag": {
"value": "v2.48.1",
"semver": true
},
"digest": {
"watch": false,
"repo": "sha256:6789012345abcdef6789012345abcdef6789012345abcdef6789012345abcdef"
},
"architecture": "amd64",
"os": "linux",
"created": "2023-12-20T16:33:00Z"
},
"result": {
"tag": "v2.49.1"
},
"updateAvailable": true,
"status": "running",
"created": "2024-01-03T13:55:00Z",
"updated": "2024-01-15T11:30:00Z"
}
]

View file

@ -14,4 +14,5 @@ if [[ "${INIT_ASSETS}" == "1" ]] && [[ ! -f "/www/assets/config.yml" ]]; then
fi
echo "Starting webserver"
exec 3>&1
exec lighttpd -D -f /lighttpd.conf

View file

@ -6,7 +6,14 @@ import eslintConfigPrettier from "@vue/eslint-config-prettier";
/** @type {import('eslint').Linter.Config[]} */
export default [
{ files: ["**/*.{js,mjs,cjs,vue}"] },
{ languageOptions: { globals: globals.browser } },
{
languageOptions: {
globals: {
...globals.browser,
__APP_VERSION__: "readable",
},
},
},
pluginJs.configs.recommended,
...pluginVue.configs["flat/recommended"],
eslintConfigPrettier,
@ -18,6 +25,6 @@ export default [
},
},
{
ignores: ["*.d.ts", "**/coverage", "**/dist"],
ignores: ["**/dist/**", "**/dist-ssr/**", "**/coverage/**"],
},
];

View file

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<meta charset="UTF-8" />
<link rel="icon" href="assets/icons/favicon.ico" />

View file

@ -2,7 +2,7 @@ include "/etc/lighttpd/mime-types.conf"
include_shell "/etc/lighttpd/ipv6.sh"
server.port = env.PORT
server.modules = ( "mod_alias" )
server.modules = ( "mod_alias", "mod_accesslog" )
server.username = "lighttpd"
server.groupname = "lighttpd"
server.document-root = "/www"
@ -10,3 +10,9 @@ alias.url = ( env.SUBFOLDER => "/www" )
server.indexfiles = ("index.html")
server.follow-symlink = "enable"
server.feature-flags += ( "server.clock-jump-restart" => 0 )
server.max-request-field-size = 65535
accesslog.filename = "/dev/fd/3"
# Avoid logging docker healthcheck request
$HTTP["remote-ip"] == "127.0.0.1" { accesslog.filename = "" }
$HTTP["remote-ip"] == "[::1]" { accesslog.filename = "" }

View file

@ -1,6 +1,6 @@
{
"name": "homer",
"version": "25.02.2",
"version": "25.11.1",
"type": "module",
"scripts": {
"dev": "vite",
@ -11,26 +11,26 @@
},
"dependencies": {
"@fortawesome/fontawesome-free": "^6.7.2",
"bulma": "^1.0.3",
"bulma": "^1.0.4",
"lodash.merge": "^4.6.2",
"vue": "^3.5.13",
"yaml": "^2.7.0"
"vue": "^3.5.26",
"yaml": "^2.8.2"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
"@vitejs/plugin-vue": "^5.2.1",
"@eslint/js": "^9.39.2",
"@vitejs/plugin-vue": "^6.0.3",
"@vue/eslint-config-prettier": "^10.2.0",
"eslint": "^9.21.0",
"eslint-plugin-vue": "^9.32.0",
"globals": "^16.0.0",
"eslint": "^9.39.2",
"eslint-plugin-vue": "^9.33.0",
"globals": "^17.0.0",
"http-server": "^14.1.1",
"prettier": "^3.5.2",
"sass-embedded": "^1.85.0",
"vite": "^6.1.1",
"vite-plugin-pwa": "^0.21.1"
"prettier": "^3.8.0",
"sass-embedded": "^1.97.2",
"vite": "^7.3.1",
"vite-plugin-pwa": "^1.2.0"
},
"license": "Apache-2.0",
"packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af",
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48",
"pnpm": {
"neverBuiltDependencies": []
}

3182
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -54,7 +54,6 @@
/>
</Navbar>
</div>
<section id="main-section" class="section">
<div v-cloak class="container">
<ConnectivityChecker
@ -68,64 +67,23 @@
<!-- Optional messages -->
<Message :item="config.message" />
<!-- Horizontal layout -->
<div v-if="!vlayout || filter" class="columns is-multiline">
<template v-for="(group, groupIndex) in services">
<h2
v-if="group.name"
:key="`header-${groupIndex}`"
class="column is-full group-title"
:class="group.class"
>
<i v-if="group.icon" :class="['fa-fw', group.icon]"></i>
<div v-else-if="group.logo" class="group-logo media-left">
<figure class="image is-48x48">
<img :src="group.logo" :alt="`${group.name} logo`" />
</figure>
</div>
{{ group.name }}
</h2>
<Service
v-for="(item, index) in group.items"
:key="`service-${groupIndex}-${index}`"
:item="item"
:proxy="config.proxy"
:class="[
'column',
`is-${12 / config.columns}`,
`${item.class || group.class || ''}`,
]"
/>
</template>
</div>
<!-- Vertical layout -->
<!-- Unified layout -->
<div
v-if="!filter && vlayout"
class="columns is-multiline layout-vertical"
:class="[
'columns',
'is-multiline',
{ 'layout-vertical': vlayout && !filter },
]"
>
<div
<ServiceGroup
v-for="(group, groupIndex) in services"
:key="groupIndex"
:class="['column', `is-${12 / config.columns}`]"
>
<h2 v-if="group.name" class="group-title" :class="group.class">
<i v-if="group.icon" :class="['fa-fw', group.icon]"></i>
<div v-else-if="group.logo" class="group-logo media-left">
<figure class="image is-48x48">
<img :src="group.logo" :alt="`${group.name} logo`" />
</figure>
</div>
{{ group.name }}
</h2>
<Service
v-for="(item, index) in group.items"
:key="index"
:item="item"
:proxy="config.proxy"
:class="item.class || group.class"
/>
</div>
:key="`${currentPage}-${groupIndex}`"
:group="group"
:is-vertical="vlayout && !filter"
:proxy="config.proxy"
:columns="config.columns"
:group-index="groupIndex"
/>
</div>
</div>
</div>
@ -150,7 +108,7 @@ import merge from "lodash.merge";
import Navbar from "./components/Navbar.vue";
import GetStarted from "./components/GetStarted.vue";
import ConnectivityChecker from "./components/ConnectivityChecker.vue";
import Service from "./components/Service.vue";
import ServiceGroup from "./components/ServiceGroup.vue";
import Message from "./components/Message.vue";
import SearchInput from "./components/SearchInput.vue";
import SettingToggle from "./components/SettingToggle.vue";
@ -165,7 +123,7 @@ export default {
Navbar,
GetStarted,
ConnectivityChecker,
Service,
ServiceGroup,
Message,
SearchInput,
SettingToggle,
@ -195,6 +153,10 @@ export default {
this.buildDashboard();
window.onhashchange = this.buildDashboard;
this.loaded = true;
console.info(`Homer v${__APP_VERSION__}`);
},
beforeUnmount() {
window.onhashchange = null;
},
methods: {
searchHotkey() {
@ -224,7 +186,8 @@ export default {
document.title =
this.config.documentTitle ||
`${this.config.title} | ${this.config.subtitle}`;
[this.config.title, this.config.subtitle].filter(Boolean).join(" | ");
if (this.config.stylesheet) {
let stylesheet = "";
let addtionnal_styles = this.config.stylesheet;

View file

@ -55,6 +55,10 @@
background-color: var(--card-background);
}
}
.component-error .card {
border: 1px solid rgba(255, 33, 33, 0.664);
background-color: rgba(255, 58, 58, 0.24);
}
.message {
.message-body {
@ -171,9 +175,9 @@
.title {
font-size: 1.1em;
line-height: 1.2em;
line-height: 1.3em;
font-weight: 500;
margin-bottom: 4px;
margin-bottom: 3px;
@include ellipsis();
}

View file

@ -13,6 +13,7 @@
--link: #b5ff57;
--link-hover: #8cce36;
--background-image: none;
--highlight-variant-inverted: #2d2d2d;
}
.theme-neon.dark {
@ -29,6 +30,7 @@
--link: #b5ff57;
--link-hover: #aeff45;
--background-image: none;
--highlight-variant-inverted: #696969;
}
// theme

View file

@ -12,7 +12,7 @@
--card-shadow: rgba(0, 0, 0, 0.5);
--link: #3273dc;
--link-hover: #2e4053;
--background-image: url("assets/themes/walkxcode/wallpaper-light.webp");
--background-image: url("/assets/themes/walkxcode/wallpaper-light.webp");
}
.theme-walkxcode.dark {
@ -28,7 +28,7 @@
--card-shadow: rgba(0, 0, 0, 0.5);
--link: #ffffff;
--link-hover: #fafafa;
--background-image: url("assets/themes/walkxcode/wallpaper.webp");
--background-image: url("/assets/themes/walkxcode/wallpaper.webp");
}
// theme

View file

@ -1,9 +1,20 @@
<template>
<div v-if="offline" class="offline-message mb-4">
<div
v-if="offline"
class="offline-message mb-4"
role="alert"
aria-live="polite"
>
<i class="fa-solid fa-triangle-exclamation"></i>
<h1>
Network unreachable
<span @click="checkOffline"> <i class="fas fa-redo-alt"></i></span>
<button
aria-label="Retry connection check"
class="retry-button"
@click="checkOffline"
>
<i class="fas fa-redo-alt"></i>
</button>
</h1>
<p>
<a
@ -26,46 +37,49 @@ export default {
if (/t=\d+/.test(window.location.href)) {
window.history.replaceState({}, document.title, window.location.pathname);
}
let that = this;
this.checkOffline();
document.addEventListener(
"visibilitychange",
function () {
if (document.visibilityState == "visible") {
that.checkOffline();
}
},
this.handleVisibilityChange,
false,
);
window.addEventListener(
"online",
function () {
that.checkOffline();
},
false,
);
window.addEventListener(
"offline",
function () {
this.offline = true;
},
false,
window.addEventListener("online", this.handleOnline, false);
window.addEventListener("offline", this.handleOffline, false);
},
beforeUnmount: function () {
document.removeEventListener(
"visibilitychange",
this.handleVisibilityChange,
);
window.removeEventListener("online", this.handleOnline);
window.removeEventListener("offline", this.handleOffline);
},
methods: {
handleVisibilityChange: function () {
if (document.visibilityState === "visible") {
this.checkOffline();
}
},
handleOnline: function () {
this.checkOffline();
},
handleOffline: function () {
this.offline = true;
},
checkOffline: function () {
// Global online check
if (!navigator.onLine) {
this.offline = true;
return;
}
// extra check to make sure we're not offline
// Check if the current URL is reachable
let that = this;
const urlPath = window.location.pathname.replace(/\/+$/, "");
const aliveCheckUrl = `${
window.location.origin
}${urlPath}/index.html?t=${new Date().valueOf()}`;
const aliveCheckUrl = new URL(window.location);
aliveCheckUrl.searchParams.set("t", new Date().valueOf());
return fetch(aliveCheckUrl, {
method: "HEAD",
cache: "no-store",

View file

@ -0,0 +1,23 @@
<template>
<h2 :class="group.class">
<i v-if="group.icon" :class="['fa-fw', group.icon]"></i>
<div v-else-if="group.logo" class="group-logo media-left">
<figure class="image is-48x48">
<img :src="group.logo" :alt="`${group.name} logo`" />
</figure>
</div>
{{ group.name }}
</h2>
</template>
<script>
export default {
name: "GroupHeader",
props: {
group: {
type: Object,
required: true,
},
},
};
</script>

View file

@ -26,10 +26,7 @@
:href="link.url"
:target="link.target"
>
<i
v-if="link.icon"
:class="['fa-fw', link.icon, { 'mr-2': link.name }]"
></i>
<i v-if="link.icon" :class="['fa-fw', link.icon]"></i>
{{ link.name }}
</a>
</div>
@ -65,3 +62,11 @@ export default {
},
};
</script>
<style lang="scss" scoped>
@media (min-width: 1023px) {
i.fa-fw {
width: 0.8em;
}
}
</style>

View file

@ -29,7 +29,7 @@ export default {
emits: ["search-open", "search-focus", "search-cancel", "input"],
mounted() {
this._keyListener = function (event) {
if (event.key === this.hotkey) {
if (!this.hasFocus() && event.key === this.hotkey) {
event.preventDefault();
this.focus();
}
@ -63,6 +63,9 @@ export default {
this.$refs.search.focus();
});
},
hasFocus: function () {
return document.activeElement == this.$refs.search;
},
setSearchURL: function (value) {
const url = new URL(window.location);
if (value === "") {

View file

@ -1,10 +1,12 @@
<template>
<component :is="component" :item="item" :proxy="proxy"></component>
<Generic v-if="isGeneric" :item="item"></Generic>
<component :is="component" v-else :item="item" :proxy="proxy"></component>
</template>
<script>
import { defineAsyncComponent } from "vue";
import Generic from "./services/Generic.vue";
import errorComponent from "./services/_error.vue";
const defaultService = "Generic";
export default {
name: "Service",
@ -13,12 +15,15 @@ export default {
proxy: Object,
},
computed: {
isGeneric() {
return defaultService === (this.item.type || defaultService);
},
component() {
const type = this.item.type || "Generic";
if (type === "Generic") {
return Generic;
}
return defineAsyncComponent(() => import(`./services/${type}.vue`));
return defineAsyncComponent({
loader: () => import(`./services/${this.item.type}.vue`),
errorComponent: errorComponent,
timeout: 3000,
});
},
},
};

View file

@ -0,0 +1,69 @@
<template>
<!-- Vertical layout: Group container wrapper -->
<div v-if="isVertical" :class="['column', `is-${12 / columns}`]">
<GroupHeader v-if="group.name" :group="group" class="group-title" />
<Service
v-for="(item, index) in group.items"
:key="`srv-${groupIndex}-${index}-${item.name || item.type}`"
:item="item"
:proxy="proxy"
:class="item.class || group.class"
/>
</div>
<!-- Horizontal layout: Direct rendering -->
<template v-else>
<GroupHeader
v-if="group.name"
:key="`header-${groupIndex}`"
:group="group"
class="column is-full group-title"
/>
<Service
v-for="(item, index) in group.items"
:key="`srv-${groupIndex}-${index}-${item.name || item.type}`"
:item="item"
:proxy="proxy"
:class="[
'column',
`is-${12 / columns}`,
`${item.class || group.class || ''}`,
]"
/>
</template>
</template>
<script>
import Service from "./Service.vue";
import GroupHeader from "./GroupHeader.vue";
export default {
name: "ServiceGroup",
components: {
Service,
GroupHeader,
},
props: {
group: {
type: Object,
required: true,
},
isVertical: {
type: Boolean,
default: false,
},
proxy: {
type: String,
default: null,
},
columns: {
type: String,
required: true,
},
groupIndex: {
type: Number,
required: true,
},
},
};
</script>

View file

@ -21,13 +21,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "AdGuardHome",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -15,13 +15,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "CopyToClipboard",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -0,0 +1,109 @@
<template>
<Generic :item="item">
<template #indicator>
<div class="notifs">
<strong
v-if="running > 0"
class="notif running"
title="Running Containers"
>
{{ running }}
</strong>
<strong
v-if="stopped > 0"
class="notif stopped"
title="Stopped Containers"
>
{{ stopped }}
</strong>
<strong v-if="errors > 0" class="notif errors" title="Error">
{{ errors }}
</strong>
<strong
v-if="serverError"
class="notif errors"
title="Connection error to Docker Socket Proxy API"
>
Unavailable
</strong>
</div>
</template>
</Generic>
</template>
<script>
import service from "@/mixins/service.js";
export default {
name: "DockerSocketProxy",
mixins: [service],
props: {
item: Object,
},
data: () => {
return {
running: null,
stopped: null,
errors: null,
serverError: false,
};
},
created: function () {
const checkInterval = parseInt(this.item.checkInterval, 10) || 0;
if (checkInterval > 0) {
setInterval(() => this.fetchData(), checkInterval);
}
this.fetchData();
},
methods: {
fetchData: function () {
const handleError = (e) => {
console.error(e);
this.serverError = true;
};
// Fetch all containers (including stopped) from Docker Socket Proxy
this.fetch("/containers/json?all=true") // Docker endpoint for container statuses
.then((containers) => {
this.running = containers.filter(
(container) => container.State === "running",
).length;
this.stopped = containers.filter(
(container) => container.State === "exited",
).length;
})
.catch(handleError);
},
},
};
</script>
<style scoped lang="scss">
.notifs {
position: absolute;
color: white;
font-family: sans-serif;
top: 0.3em;
right: 0.5em;
.notif {
display: inline-block;
padding: 0.2em 0.35em;
border-radius: 0.25em;
position: relative;
margin-left: 0.3em;
font-size: 0.8em;
&.running {
background-color: #4fb5d6;
}
&.stopped {
background-color: #d08d2e;
}
&.errors {
background-color: #e51111;
}
}
}
</style>

View file

@ -21,13 +21,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Docuseal",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -21,13 +21,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Emby",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -25,13 +25,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "FreshRSS",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,
@ -57,13 +53,9 @@ export default {
`/api/greader.php/accounts/ClientLogin?Email=${this.item.username}&Passwd=${this.item.password}`,
{ method: "GET", cache: "no-cache" },
false,
)
.then((response) => {
return response.text();
})
.then((body) => {
return body.match(/Auth=(([([a-z0-9]+)\/([([a-z0-9]+))/i);
});
).then((body) => {
return body.match(/Auth=(([([a-z0-9]+)\/([([a-z0-9]+))/i);
});
if (match !== null) this.auth = match[1];
}

View file

@ -0,0 +1,153 @@
<template>
<Generic :item="item">
<template #content>
<p class="title is-4">{{ item.name }}</p>
<p class="subtitle is-6">
<template v-if="item.subtitle">
{{ item.subtitle }}
</template>
<i class="fa-solid fa-signal"></i> {{ up }}/{{ total }}
<template v-if="avgRespTime > 0">
<span class="separator"> | </span>
<i class="fa-solid fa-stopwatch"></i> {{ avgRespTime }} ms avg.
</template>
</p>
</template>
<template #indicator>
<div v-if="status !== false" class="status" :class="status">
{{ percentageGood }}&percnt;
</div>
</template>
</Generic>
</template>
<script>
import service from "@/mixins/service.js";
export default {
name: "Gatus",
mixins: [service],
props: {
item: Object,
},
data: () => ({
up: 0,
down: 0,
total: 0,
avgRespTime: NaN,
percentageGood: NaN,
status: false,
statusMessage: false,
}),
created() {
const updateInterval = parseInt(this.item.updateInterval, 10) || 0;
if (updateInterval > 0) {
setInterval(() => this.fetchStatus(), updateInterval);
}
this.fetchStatus();
},
methods: {
fetchStatus: async function () {
this.fetch("/api/v1/endpoints/statuses", {
method: "GET",
cache: "no-cache",
})
.then((response) => {
// Apply filtering by groups, if defined
if (this.item.groups) {
response = response?.filter((job) => {
return this.item.groups.includes(job.group) === true;
});
}
// Initialise counts, avg times
this.total = response.length;
this.up = 0;
let totalrestime = 0;
let totalresults = 0;
response.forEach((job) => {
if (job.results[job.results.length - 1].success === true) {
this.up++;
}
if (!this.item.hideaverages) {
// Update array of average times
let totalduration = 0;
let rescounter = 0;
job.results.forEach((res) => {
totalduration += parseInt(res.duration, 10) / 1000000;
rescounter++;
});
totalrestime += totalduration;
totalresults += rescounter;
} else {
totalrestime = 0;
totalresults = 1;
}
});
// Rest are down
this.down = this.total - this.up;
// Calculate overall average response time
this.avgRespTime = (totalrestime / totalresults).toFixed(2);
// Update representations
if (this.up == 0 || this.total == 0) {
this.percentageGood = 0;
} else {
this.percentageGood = Math.round((this.up / this.total) * 100);
}
// Status flag
if (this.up == 0 && this.down == 0) {
this.status = false;
} else if (this.down == this.total) {
this.status = "bad";
} else if (this.up == this.total) {
this.status = "good";
} else {
this.status = "warn";
}
})
.catch((e) => {
console.error(e);
});
},
},
};
</script>
<style scoped lang="scss">
.status {
font-size: 0.8rem;
color: var(--text-title);
&.good:before {
background-color: #94e185;
border-color: #78d965;
box-shadow: 0 0 5px 1px #94e185;
}
&.warn:before {
background-color: #f8a306;
border-color: #e1b35e;
box-shadow: 0 0 5px 1px #f8a306;
}
&.bad:before {
background-color: #c9404d;
border-color: #c42c3b;
box-shadow: 0 0 5px 1px #c9404d;
}
&:before {
content: " ";
display: inline-block;
width: 7px;
height: 7px;
margin-right: 10px;
border: 1px solid #000;
border-radius: 7px;
}
}
</style>

View file

@ -28,7 +28,7 @@
:target="link.target"
rel="noreferrer"
>
<span v-if="item.icon"
<span v-if="link.icon"
><i
style="font-size: 12px"
:class="['fa-fw', link.icon]"

View file

@ -21,13 +21,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Gitea",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -17,13 +17,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Glances",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -19,13 +19,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Gotify",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -18,13 +18,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Healthchecks",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -21,13 +21,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "HomeAssistant",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -27,13 +27,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Immich",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -20,13 +20,9 @@
</template>
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Jellyfin",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -27,13 +27,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Lidarr",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -0,0 +1,63 @@
<template>
<Generic v-for="bookmark in bookmarks" :key="bookmark.name" :item="bookmark">
</Generic>
</template>
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Linkding",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,
},
data: () => ({
bookmarks: [],
}),
computed: {
calculatedLimit: function () {
const limit = parseInt(this.item.limit) || 5;
return Math.min(Math.max(limit, 1), 15);
},
},
created() {
this.fetchBookmarks();
},
methods: {
fetchBookmarks: async function () {
const headers = {
Authorization: `Token ${this.item.token}`,
Accept: "application/json",
};
let query = "";
if (this.item.query) {
query = `&q=${encodeURIComponent(this.item.query)}`;
}
let url = `/api/bookmarks/?limit=${this.calculatedLimit}${query}`;
this.fetch(url, {
headers,
})
.then((ld_response) => {
this.bookmarks = ld_response.results.map((bookmark) => ({
name: `${bookmark.title}`,
subtitle: `${bookmark.description}`,
url: bookmark.url,
logo: `${bookmark.favicon_url}`,
tag: `${bookmark.tag_names.join(" #")}`,
}));
})
.catch((e) => {
console.log(e);
});
},
},
};
</script>

View file

@ -0,0 +1,88 @@
<template>
<Generic :item="item">
<template #content>
<p class="title is-4">{{ item.name }}</p>
<p class="subtitle is-6">
<template v-if="item.subtitle">
{{ item.subtitle }}
</template>
<template v-else-if="versionstring">
Version {{ versionstring }}
</template>
</p>
</template>
<template #indicator>
<div v-if="status" class="status" :class="status">
{{ status }}
</div>
</template>
</Generic>
</template>
<script>
import service from "@/mixins/service.js";
export default {
name: "Matrix",
mixins: [service],
props: {
item: Object,
},
data: () => ({
fetchOk: null,
versionstring: null,
}),
computed: {
status: function () {
return this.fetchOk ? "online" : "offline";
},
},
created() {
this.fetchStatus();
},
methods: {
fetchStatus: async function () {
this.fetch("_matrix/federation/v1/version")
.then((response) => {
this.fetchOk = true;
this.versionstring = response.server.version;
})
.catch((e) => {
this.fetchOk = false;
console.log(e);
});
},
},
};
</script>
<style scoped lang="scss">
.status {
font-size: 0.8rem;
color: var(--text-title);
white-space: nowrap;
margin-left: 0.25rem;
&.online:before {
background-color: #94e185;
border-color: #78d965;
box-shadow: 0 0 5px 1px #94e185;
}
&.offline:before {
background-color: #c9404d;
border-color: #c42c3b;
box-shadow: 0 0 5px 1px #c9404d;
}
&:before {
content: " ";
display: inline-block;
width: 7px;
height: 7px;
margin-right: 10px;
border: 1px solid #000;
border-radius: 7px;
}
}
</style>

View file

@ -19,13 +19,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Mealie",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -33,13 +33,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Medusa",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

View file

@ -0,0 +1,160 @@
<template>
<Generic :item="item">
<template #content>
<p class="title is-4">{{ item.name }}</p>
<p class="subtitle is-6">
<template v-if="item.subtitle"> {{ item.subtitle }} </template>
<template v-else-if="unreadEntries">
<template v-if="unreadFeeds < 2">
{{ unreadEntries }} unread
</template>
<template v-else>
{{ unreadEntries }} unread in {{ unreadFeeds }} feeds
</template>
</template>
</p>
</template>
<template #indicator>
<i v-if="loading" class="fa fa-circle-notch fa-spin"></i>
<div v-else-if="style == 'status'" class="status" :class="statusClass">
{{ status }}
</div>
<div v-else class="notifs">
<strong v-if="unreadEntries > 0" class="notif unread" title="Unread">
{{ unreadEntries }}
</strong>
<strong
v-if="!isHealthy"
class="notif errors"
title="Connection error to Miniflux API, check url and apikey in config.yml"
>
?
</strong>
</div>
</template>
</Generic>
</template>
<script>
import service from "@/mixins/service.js";
export default {
name: "Miniflux",
mixins: [service],
props: {
item: Object,
},
data: () => ({
unreadEntries: 0,
unreadFeeds: 0,
isHealthy: false,
loading: true,
style: "status",
}),
computed: {
status: function () {
if (!this.isHealthy) {
return "Error";
}
return this.unreadEntries > 0 ? "Unread" : "Online";
},
statusClass: function () {
return this.status.toLowerCase();
},
},
created() {
const checkInterval = parseInt(this.item.checkInterval, 10) || 0;
if (checkInterval > 0) {
setInterval(() => this.fetchConfig(), checkInterval);
}
this.fetchStatus();
},
methods: {
fetchStatus: async function () {
const headers = {
"X-Auth-Token": this.item.apikey,
};
let counters;
try {
counters = await this.fetch("/v1/feeds/counters", { headers });
this.isHealthy = true;
} catch (e) {
console.log(e);
} finally {
this.loading = false;
}
if (!this.isHealthy) {
return;
}
const unreads = Object.values(counters.unreads || {});
this.unreadFeeds = unreads.length;
this.unreadEntries = unreads.reduce((accumulator, value) => {
return accumulator + value;
}, 0);
},
},
};
</script>
<style scoped lang="scss">
.status {
font-size: 0.8rem;
color: var(--text-title);
&.online:before {
background-color: #94e185;
border-color: #78d965;
box-shadow: 0 0 5px 1px #94e185;
}
&.unread:before {
background-color: #1774ff;
border-color: #1774ff;
box-shadow: 0 0 5px 1px #1774ff;
}
&.error:before {
background-color: #c9404d;
border-color: #c42c3b;
box-shadow: 0 0 5px 1px #c9404d;
}
&:before {
content: " ";
display: inline-block;
width: 7px;
height: 7px;
margin-right: 10px;
border: 1px solid #000;
border-radius: 7px;
}
}
.notifs {
position: absolute;
color: white;
font-family: sans-serif;
top: 0.3em;
right: 0.5em;
.notif {
display: inline-block;
padding: 0.2em 0.35em;
border-radius: 0.25em;
position: relative;
margin-left: 0.3em;
font-size: 0.8em;
&.unread {
background-color: #4fb5d6;
}
&.errors {
background-color: #e51111;
}
}
}
</style>

View file

@ -22,13 +22,9 @@
<script>
import service from "@/mixins/service.js";
import Generic from "./Generic.vue";
export default {
name: "Mylar",
components: {
Generic,
},
mixins: [service],
props: {
item: Object,

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