mirror of
https://github.com/gethomepage/homepage.git
synced 2026-01-04 14:32:15 +08:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16ddb2461b | ||
|
|
f75827c4c6 | ||
|
|
cf03e60186 | ||
|
|
8ee071769a | ||
|
|
b312183a7b | ||
|
|
5baaf5faec | ||
|
|
d685bfd11d | ||
|
|
cf4b230b7a | ||
|
|
d46f5f4613 | ||
|
|
945ed854a4 | ||
|
|
25f0672c18 | ||
|
|
6f6c8b2ae0 | ||
|
|
7e62410f98 | ||
|
|
f49e8486c7 | ||
|
|
844bc23f8c | ||
|
|
850226b260 | ||
|
|
c5100567d6 | ||
|
|
5344854199 | ||
|
|
d790b17507 | ||
|
|
667d3851ce | ||
|
|
ba4d345f4f | ||
|
|
52816426fc | ||
|
|
38a423cf2a | ||
|
|
75ad7eb7e4 | ||
|
|
533c3b7b1b | ||
|
|
1c98999994 | ||
|
|
b19b4f047e | ||
|
|
95b6ea0e23 | ||
|
|
b3db549a65 | ||
|
|
cd768000e9 | ||
|
|
da6099c29d | ||
|
|
d36e569ede | ||
|
|
eca3757af5 | ||
|
|
7d8634ce5e | ||
|
|
d4f6785946 | ||
|
|
b468045039 | ||
|
|
f3b4f21c2e | ||
|
|
a50ae64397 | ||
|
|
2a5c58e138 | ||
|
|
d21945a6e6 | ||
|
|
0c572cb029 | ||
|
|
9d30b952ee | ||
|
|
e32876d08d | ||
|
|
340b138962 | ||
|
|
7ae0ba31cb | ||
|
|
f566671975 | ||
|
|
b7ff123e44 | ||
|
|
e1c34bc489 | ||
|
|
dedd341e02 | ||
|
|
dc8fc04b57 | ||
|
|
6a85859a35 | ||
|
|
ff1e8d9e8c | ||
|
|
16da998452 | ||
|
|
2fc7c6ab99 | ||
|
|
834f33e5a5 | ||
|
|
90a13a4e83 | ||
|
|
e4343a4f2f | ||
|
|
7852797bab | ||
|
|
4a93d2ba1e | ||
|
|
9287d711dc | ||
|
|
b5538655e0 |
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
30
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: "[Bug] "
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Configuration**
|
||||||
|
If applicable,
|
||||||
|
```yaml
|
||||||
|
# Please provide your service, widget or otherwise related configuration here
|
||||||
|
```
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here. This includes things like:
|
||||||
|
- Service version or API version
|
||||||
|
- Docker version
|
||||||
|
- Deployment method
|
||||||
|
- Sample YAML configurations
|
||||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: "[Feature Request] "
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a service? Please describe.**
|
||||||
|
A clear and concise description of what you would like to see from this service.
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I would like it if [...]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
ben@phelps.io.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
||||||
41
CONTRIBUTING.md
Normal file
41
CONTRIBUTING.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Contributing to Homepage
|
||||||
|
We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's:
|
||||||
|
|
||||||
|
- Reporting a bug
|
||||||
|
- Discussing the current state of the project
|
||||||
|
- Submitting a fix
|
||||||
|
- Proposing new features
|
||||||
|
- Becoming a maintainer
|
||||||
|
|
||||||
|
## We Develop with Github
|
||||||
|
We use github to host code, to track issues and feature requests, as well as accept pull requests.
|
||||||
|
|
||||||
|
## Any contributions you make will be under the GNU General Public License v3.0
|
||||||
|
In short, when you submit code changes, your submissions are understood to be under the same [GNU General Public License v3.0](https://choosealicense.com/licenses/gpl-3.0/) that covers the project. Feel free to contact the maintainers if that's a concern.
|
||||||
|
|
||||||
|
## Report bugs using Github's [issues](https://github.com/benphelps/homepage/issues)
|
||||||
|
We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/benphelps/homepage/issues/new); it's that easy!
|
||||||
|
|
||||||
|
## Write bug reports with detail, background, and sample configurations
|
||||||
|
Homepage includes a lot of configuration options and is often deploying in larger systems. Please include as much information (configurations, deployment method, Docker & API versions, etc) as you can when reporting an issue.
|
||||||
|
|
||||||
|
**Great Bug Reports** tend to have:
|
||||||
|
|
||||||
|
- A quick summary and/or background
|
||||||
|
- Steps to reproduce
|
||||||
|
- Be specific!
|
||||||
|
- Give example configurations if you can.
|
||||||
|
- What you expected would happen
|
||||||
|
- What actually happens
|
||||||
|
- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
|
||||||
|
|
||||||
|
People *love* thorough bug reports. I'm not even kidding.
|
||||||
|
|
||||||
|
## Use a Consistent Coding Style
|
||||||
|
This project follows the [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript), please follow it when submitting pull requests.
|
||||||
|
|
||||||
|
## License
|
||||||
|
By contributing, you agree that your contributions will be licensed under its GNU General Public License.
|
||||||
|
|
||||||
|
## References
|
||||||
|
This document was adapted from the open-source contribution guidelines for [Facebook's Draft](https://github.com/facebook/draft-js/blob/main/CONTRIBUTING.md)
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
- Container status (Running / Stopped) & statistics (CPU, Memory, Network)
|
- Container status (Running / Stopped) & statistics (CPU, Memory, Network)
|
||||||
- Automatic service discovery (via labels)
|
- Automatic service discovery (via labels)
|
||||||
* Service Integration
|
* Service Integration
|
||||||
- Sonarr, Radarr, Readarr, Emby, Jellyfin, Tautulli (Plex)
|
- Sonarr, Radarr, Readarr, Prowlarr, Emby, Jellyfin, Tautulli (Plex)
|
||||||
- Ombi, Overseerr, Jellyseerr, NZBGet, SABnzbd, ruTorrent
|
- Ombi, Overseerr, Jellyseerr, NZBGet, SABnzbd, ruTorrent
|
||||||
- Portainer, Traefik, Speedtest Tracker, PiHole, Nginx Proxy Manager, Gotify
|
- Portainer, Traefik, Speedtest Tracker, PiHole, Nginx Proxy Manager, Gotify
|
||||||
* Information Providers
|
* Information Providers
|
||||||
@@ -78,6 +78,8 @@ pnpm install
|
|||||||
pnpm build
|
pnpm build
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If this is your first time starting, copy the `src/skeleton` directory to `config/` to populate initial example config files.
|
||||||
|
|
||||||
Finally, run the server:
|
Finally, run the server:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -131,4 +133,4 @@ Huge thanks to the all the contributors who have helped make this project what i
|
|||||||
* [Nonoss117](https://github.com/benphelps/homepage/commits?author=Nonoss117) - French Translation
|
* [Nonoss117](https://github.com/benphelps/homepage/commits?author=Nonoss117) - French Translation
|
||||||
* [quod](https://github.com/benphelps/homepage/commits?author=quod) - Fixed Typos
|
* [quod](https://github.com/benphelps/homepage/commits?author=quod) - Fixed Typos
|
||||||
* [schklom](https://github.com/benphelps/homepage/commits?author=schklom) - ARM64, ARMv7 and ARMv6
|
* [schklom](https://github.com/benphelps/homepage/commits?author=schklom) - ARM64, ARMv7 and ARMv6
|
||||||
* [xicopitz](https://github.com/benphelps/homepage/commits?author=xicopitz) - Gotify Integration
|
* [xicopitz](https://github.com/benphelps/homepage/commits?author=xicopitz) - Gotify & Prowlarr Integration
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"total": "Gesamt",
|
"total": "Gesamt",
|
||||||
"free": "Frei",
|
"free": "Frei",
|
||||||
"used": "Gebraucht"
|
"used": "Gebraucht",
|
||||||
|
"load": "Load"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
"rx": "Rx",
|
"rx": "Rx",
|
||||||
@@ -114,5 +115,18 @@
|
|||||||
"apps": "Applications",
|
"apps": "Applications",
|
||||||
"clients": "Clients",
|
"clients": "Clients",
|
||||||
"messages": "Messages"
|
"messages": "Messages"
|
||||||
|
},
|
||||||
|
"prowlarr": {
|
||||||
|
"enableIndexers": "Indexers",
|
||||||
|
"numberOfGrabs": "Grabs",
|
||||||
|
"numberOfQueries": "Queries",
|
||||||
|
"numberOfFailGrabs": "Fail Grabs",
|
||||||
|
"numberOfFailQueries": "Fail Queries"
|
||||||
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,8 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"free": "Free",
|
"free": "Free",
|
||||||
"used": "Used"
|
"used": "Used",
|
||||||
|
"load": "Load"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
"rx": "RX",
|
"rx": "RX",
|
||||||
@@ -63,6 +64,12 @@
|
|||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"download": "Download"
|
"download": "Download"
|
||||||
},
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
|
},
|
||||||
"sonarr": {
|
"sonarr": {
|
||||||
"wanted": "Wanted",
|
"wanted": "Wanted",
|
||||||
"queued": "Queued",
|
"queued": "Queued",
|
||||||
@@ -125,5 +132,12 @@
|
|||||||
"apps": "Applications",
|
"apps": "Applications",
|
||||||
"clients": "Clients",
|
"clients": "Clients",
|
||||||
"messages": "Messages"
|
"messages": "Messages"
|
||||||
|
},
|
||||||
|
"prowlarr":{
|
||||||
|
"enableIndexers": "Indexers",
|
||||||
|
"numberOfGrabs": "Grabs",
|
||||||
|
"numberOfQueries": "Queries",
|
||||||
|
"numberOfFailGrabs": "Fail Grabs",
|
||||||
|
"numberOfFailQueries": "Fail Queries"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"free": "Libre",
|
"free": "Libre",
|
||||||
"used": "Usado"
|
"used": "Usado",
|
||||||
|
"load": "Load"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
"rx": "Recibido",
|
"rx": "Recibido",
|
||||||
@@ -47,9 +48,9 @@
|
|||||||
"movies": "Películas"
|
"movies": "Películas"
|
||||||
},
|
},
|
||||||
"readarr": {
|
"readarr": {
|
||||||
"wanted": "Wanted",
|
"wanted": "Más deseado",
|
||||||
"queued": "Queued",
|
"queued": "Puesto en cola",
|
||||||
"books": "Books"
|
"books": "Libros"
|
||||||
},
|
},
|
||||||
"ombi": {
|
"ombi": {
|
||||||
"pending": "Pendiente",
|
"pending": "Pendiente",
|
||||||
@@ -98,21 +99,34 @@
|
|||||||
"available": "Disponible"
|
"available": "Disponible"
|
||||||
},
|
},
|
||||||
"sabnzbd": {
|
"sabnzbd": {
|
||||||
"rate": "Rate",
|
"rate": "Tasa de descarga",
|
||||||
"queue": "Queue",
|
"queue": "Puesto en cola",
|
||||||
"timeleft": "Time Left"
|
"timeleft": "Tiempo Restante"
|
||||||
},
|
},
|
||||||
"nzbget": {
|
"nzbget": {
|
||||||
"rate": "Rate",
|
"rate": "Tasa de descarga",
|
||||||
"remaining": "Remaining",
|
"remaining": "Restante",
|
||||||
"downloaded": "Downloaded"
|
"downloaded": "Descargado"
|
||||||
},
|
},
|
||||||
"coinmarketcap": {
|
"coinmarketcap": {
|
||||||
"configure": "Configure one or more crypto currencies to track"
|
"configure": "Configurar una o varias criptomonedas para su seguimiento"
|
||||||
},
|
},
|
||||||
"gotify": {
|
"gotify": {
|
||||||
"apps": "Applications",
|
"apps": "Aplicaciones",
|
||||||
"clients": "Clients",
|
"clients": "Clientes",
|
||||||
"messages": "Messages"
|
"messages": "Mensajes"
|
||||||
|
},
|
||||||
|
"prowlarr": {
|
||||||
|
"enableIndexers": "Indexers",
|
||||||
|
"numberOfGrabs": "Grabs",
|
||||||
|
"numberOfQueries": "Queries",
|
||||||
|
"numberOfFailGrabs": "Fail Grabs",
|
||||||
|
"numberOfFailQueries": "Fail Queries"
|
||||||
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"free": "Libre",
|
"free": "Libre",
|
||||||
"used": "Utilisée"
|
"used": "Utilisé",
|
||||||
|
"load": "Charge"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
"rx": "Rx",
|
"rx": "Rx",
|
||||||
@@ -32,53 +33,53 @@
|
|||||||
"no_active": "Aucun flux actif"
|
"no_active": "Aucun flux actif"
|
||||||
},
|
},
|
||||||
"rutorrent": {
|
"rutorrent": {
|
||||||
"active": "Active",
|
"active": "Actif",
|
||||||
"upload": "Téléverser",
|
"upload": "Téléverser",
|
||||||
"download": "Télécharger"
|
"download": "Télécharger"
|
||||||
},
|
},
|
||||||
"sonarr": {
|
"sonarr": {
|
||||||
"wanted": "Recherchée",
|
"wanted": "Demandé",
|
||||||
"queued": "En queue",
|
"queued": "En queue",
|
||||||
"series": "Séries"
|
"series": "Séries"
|
||||||
},
|
},
|
||||||
"radarr": {
|
"radarr": {
|
||||||
"wanted": "Recherchée",
|
"wanted": "Demandé",
|
||||||
"queued": "En queue",
|
"queued": "En queue",
|
||||||
"movies": "Films"
|
"movies": "Films"
|
||||||
},
|
},
|
||||||
"readarr": {
|
"readarr": {
|
||||||
"wanted": "Wanted",
|
"wanted": "Demandé",
|
||||||
"queued": "Queued",
|
"queued": "En Queue",
|
||||||
"books": "Books"
|
"books": "Livres"
|
||||||
},
|
},
|
||||||
"ombi": {
|
"ombi": {
|
||||||
"pending": "En attente",
|
"pending": "En attente",
|
||||||
"approved": "Approuvée",
|
"approved": "Validé",
|
||||||
"available": "Disponible"
|
"available": "Disponible"
|
||||||
},
|
},
|
||||||
"jellyseerr": {
|
"jellyseerr": {
|
||||||
"pending": "En attente",
|
"pending": "En attente",
|
||||||
"approved": "Approuvée",
|
"approved": "Validé",
|
||||||
"available": "Disponible"
|
"available": "Disponible"
|
||||||
},
|
},
|
||||||
"pihole": {
|
"pihole": {
|
||||||
"queries": "Requêtes",
|
"queries": "Requêtes",
|
||||||
"blocked": "Bloquée",
|
"blocked": "Bloqué",
|
||||||
"gravity": "La gravité"
|
"gravity": "Listes dom. bloqués"
|
||||||
},
|
},
|
||||||
"speedtest": {
|
"speedtest": {
|
||||||
"upload": "Téléversement",
|
"upload": "Téléversement",
|
||||||
"download": "Téléchargement",
|
"download": "Téléchargement",
|
||||||
"ping": "Ping-ping"
|
"ping": "Ping"
|
||||||
},
|
},
|
||||||
"portainer": {
|
"portainer": {
|
||||||
"running": "Fonctionnement",
|
"running": "Démarré",
|
||||||
"stopped": "Arrêté",
|
"stopped": "Arrêté",
|
||||||
"total": "Total"
|
"total": "Total"
|
||||||
},
|
},
|
||||||
"traefik": {
|
"traefik": {
|
||||||
"routers": "Routeurs",
|
"routers": "Routeurs",
|
||||||
"services": "Prestations de service",
|
"services": "Services",
|
||||||
"middleware": "Middleware"
|
"middleware": "Middleware"
|
||||||
},
|
},
|
||||||
"npm": {
|
"npm": {
|
||||||
@@ -105,25 +106,38 @@
|
|||||||
},
|
},
|
||||||
"overseerr": {
|
"overseerr": {
|
||||||
"pending": "En attente",
|
"pending": "En attente",
|
||||||
"approved": "Approuvée",
|
"approved": "Validé",
|
||||||
"available": "Disponible"
|
"available": "Disponible"
|
||||||
},
|
},
|
||||||
"sabnzbd": {
|
"sabnzbd": {
|
||||||
"rate": "Rate",
|
"rate": "Taux",
|
||||||
"queue": "Queue",
|
"queue": "Queue",
|
||||||
"timeleft": "Time Left"
|
"timeleft": "Temps restant"
|
||||||
},
|
},
|
||||||
"nzbget": {
|
"nzbget": {
|
||||||
"remaining": "Remaining",
|
"remaining": "Restant",
|
||||||
"downloaded": "Downloaded",
|
"downloaded": "Téléchargé",
|
||||||
"rate": "Rate"
|
"rate": "Évaluer"
|
||||||
},
|
},
|
||||||
"coinmarketcap": {
|
"coinmarketcap": {
|
||||||
"configure": "Configure one or more crypto currencies to track"
|
"configure": "Configurer une ou plusieurs crypto-monnaies à suivre"
|
||||||
},
|
},
|
||||||
"gotify": {
|
"gotify": {
|
||||||
"apps": "Applications",
|
"apps": "Applications",
|
||||||
"clients": "Clients",
|
"clients": "Clients",
|
||||||
"messages": "Messages"
|
"messages": "Messages"
|
||||||
|
},
|
||||||
|
"prowlarr": {
|
||||||
|
"enableIndexers": "Indexeurs",
|
||||||
|
"numberOfGrabs": "Capture",
|
||||||
|
"numberOfQueries": "Demandes",
|
||||||
|
"numberOfFailGrabs": "Capture échouée",
|
||||||
|
"numberOfFailQueries": "Demande échouée"
|
||||||
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,8 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"total": "Totale",
|
"total": "Totale",
|
||||||
"free": "Libero",
|
"free": "Libero",
|
||||||
"used": "In utilizzo"
|
"used": "In utilizzo",
|
||||||
|
"load": "Load"
|
||||||
},
|
},
|
||||||
"rutorrent": {
|
"rutorrent": {
|
||||||
"active": "Attivo",
|
"active": "Attivo",
|
||||||
@@ -114,5 +115,18 @@
|
|||||||
"apps": "Applications",
|
"apps": "Applications",
|
||||||
"clients": "Clients",
|
"clients": "Clients",
|
||||||
"messages": "Messages"
|
"messages": "Messages"
|
||||||
|
},
|
||||||
|
"prowlarr": {
|
||||||
|
"enableIndexers": "Indexers",
|
||||||
|
"numberOfGrabs": "Grabs",
|
||||||
|
"numberOfQueries": "Queries",
|
||||||
|
"numberOfFailGrabs": "Fail Grabs",
|
||||||
|
"numberOfFailQueries": "Fail Queries"
|
||||||
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"total": "Totalt",
|
"total": "Totalt",
|
||||||
"free": "Ledig",
|
"free": "Ledig",
|
||||||
"used": "Brukt"
|
"used": "Brukt",
|
||||||
|
"load": "Last inn"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
"rx": "Mottatt",
|
"rx": "Mottatt",
|
||||||
@@ -23,13 +24,13 @@
|
|||||||
"playing": "Spiller",
|
"playing": "Spiller",
|
||||||
"transcoding": "Transkoding",
|
"transcoding": "Transkoding",
|
||||||
"bitrate": "Bitrate",
|
"bitrate": "Bitrate",
|
||||||
"no_active": "No Active Streams"
|
"no_active": "Ingen aktive strømmer"
|
||||||
},
|
},
|
||||||
"tautulli": {
|
"tautulli": {
|
||||||
"playing": "Spiller",
|
"playing": "Spiller",
|
||||||
"transcoding": "Transkoding",
|
"transcoding": "Transkoding",
|
||||||
"bitrate": "Bitrate",
|
"bitrate": "Bitrate",
|
||||||
"no_active": "No Active Streams"
|
"no_active": "Ingen aktive strømmer"
|
||||||
},
|
},
|
||||||
"rutorrent": {
|
"rutorrent": {
|
||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
@@ -93,26 +94,39 @@
|
|||||||
"current": "Nåværende posisjon"
|
"current": "Nåværende posisjon"
|
||||||
},
|
},
|
||||||
"overseerr": {
|
"overseerr": {
|
||||||
"pending": "Pending",
|
"pending": "Venter",
|
||||||
"approved": "Approved",
|
"approved": "Godkjent",
|
||||||
"available": "Available"
|
"available": "Tilgjengelig"
|
||||||
},
|
},
|
||||||
"sabnzbd": {
|
"sabnzbd": {
|
||||||
"rate": "Rate",
|
"rate": "Takt",
|
||||||
"queue": "Queue",
|
"queue": "Kø",
|
||||||
"timeleft": "Time Left"
|
"timeleft": "Gjenstående tid"
|
||||||
},
|
},
|
||||||
"nzbget": {
|
"nzbget": {
|
||||||
"rate": "Rate",
|
"rate": "Takt",
|
||||||
"downloaded": "Downloaded",
|
"downloaded": "Nedlastet",
|
||||||
"remaining": "Remaining"
|
"remaining": "Gjenstående"
|
||||||
},
|
},
|
||||||
"coinmarketcap": {
|
"coinmarketcap": {
|
||||||
"configure": "Configure one or more crypto currencies to track"
|
"configure": "Sett opp én eller flere kryptovalutaer å holde øye med"
|
||||||
},
|
},
|
||||||
"gotify": {
|
"gotify": {
|
||||||
"apps": "Applications",
|
"apps": "Programmer",
|
||||||
"clients": "Clients",
|
"clients": "Klienter",
|
||||||
"messages": "Messages"
|
"messages": "Meldinger"
|
||||||
|
},
|
||||||
|
"prowlarr": {
|
||||||
|
"enableIndexers": "Indekserere",
|
||||||
|
"numberOfGrabs": "Hentninger",
|
||||||
|
"numberOfQueries": "Spørringer",
|
||||||
|
"numberOfFailGrabs": "Mislykkede hentinger",
|
||||||
|
"numberOfFailQueries": "Mislykkede spørringer"
|
||||||
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"total": "Totaal",
|
"total": "Totaal",
|
||||||
"free": "Vrij",
|
"free": "Vrij",
|
||||||
"used": "Gebruikt"
|
"used": "Gebruikt",
|
||||||
|
"load": "Load"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
"rx": "RX",
|
"rx": "RX",
|
||||||
@@ -114,5 +115,18 @@
|
|||||||
"apps": "Applications",
|
"apps": "Applications",
|
||||||
"clients": "Clients",
|
"clients": "Clients",
|
||||||
"messages": "Messages"
|
"messages": "Messages"
|
||||||
|
},
|
||||||
|
"prowlarr": {
|
||||||
|
"enableIndexers": "Indexers",
|
||||||
|
"numberOfGrabs": "Grabs",
|
||||||
|
"numberOfQueries": "Queries",
|
||||||
|
"numberOfFailGrabs": "Fail Grabs",
|
||||||
|
"numberOfFailQueries": "Fail Queries"
|
||||||
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"free": "Livre",
|
"free": "Livre",
|
||||||
"used": "Usada"
|
"used": "Usada",
|
||||||
|
"load": "Load"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
"rx": "Rx",
|
"rx": "Rx",
|
||||||
@@ -125,5 +126,18 @@
|
|||||||
"apps": "Aplicações",
|
"apps": "Aplicações",
|
||||||
"clients": "Clientes",
|
"clients": "Clientes",
|
||||||
"messages": "Mensagens"
|
"messages": "Mensagens"
|
||||||
|
},
|
||||||
|
"prowlarr": {
|
||||||
|
"enableIndexers": "Indexers",
|
||||||
|
"numberOfGrabs": "Grabs",
|
||||||
|
"numberOfQueries": "Queries",
|
||||||
|
"numberOfFailGrabs": "Fail Grabs",
|
||||||
|
"numberOfFailQueries": "Fail Queries"
|
||||||
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"total": "Общий",
|
"total": "Общий",
|
||||||
"free": "Свободно",
|
"free": "Свободно",
|
||||||
"used": "Использовано"
|
"used": "Использовано",
|
||||||
|
"load": "Load"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
"rx": "Rx",
|
"rx": "Rx",
|
||||||
@@ -114,5 +115,18 @@
|
|||||||
"apps": "Applications",
|
"apps": "Applications",
|
||||||
"clients": "Clients",
|
"clients": "Clients",
|
||||||
"messages": "Messages"
|
"messages": "Messages"
|
||||||
|
},
|
||||||
|
"prowlarr": {
|
||||||
|
"enableIndexers": "Indexers",
|
||||||
|
"numberOfGrabs": "Grabs",
|
||||||
|
"numberOfQueries": "Queries",
|
||||||
|
"numberOfFailGrabs": "Fail Grabs",
|
||||||
|
"numberOfFailQueries": "Fail Queries"
|
||||||
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"total": "Tổng",
|
"total": "Tổng",
|
||||||
"free": "Dư",
|
"free": "Dư",
|
||||||
"used": "Đã dùng"
|
"used": "Đã dùng",
|
||||||
|
"load": "Load"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
"rx": "RX",
|
"rx": "RX",
|
||||||
@@ -114,5 +115,18 @@
|
|||||||
"apps": "Applications",
|
"apps": "Applications",
|
||||||
"clients": "Clients",
|
"clients": "Clients",
|
||||||
"messages": "Messages"
|
"messages": "Messages"
|
||||||
|
},
|
||||||
|
"prowlarr": {
|
||||||
|
"numberOfFailGrabs": "Fail Grabs",
|
||||||
|
"enableIndexers": "Indexers",
|
||||||
|
"numberOfGrabs": "Grabs",
|
||||||
|
"numberOfQueries": "Queries",
|
||||||
|
"numberOfFailQueries": "Fail Queries"
|
||||||
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,31 +8,32 @@
|
|||||||
"placeholder": "搜索…"
|
"placeholder": "搜索…"
|
||||||
},
|
},
|
||||||
"resources": {
|
"resources": {
|
||||||
"total": "全部的",
|
"total": "共",
|
||||||
"free": "自由的",
|
"free": "空闲",
|
||||||
"used": "用过的"
|
"used": "已用",
|
||||||
|
"load": "负载"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
"rx": "rx",
|
"rx": "接收",
|
||||||
"tx": "TX",
|
"tx": "发送",
|
||||||
"mem": "mem",
|
"mem": "内存",
|
||||||
"cpu": "中央处理器",
|
"cpu": "处理器",
|
||||||
"offline": "离线"
|
"offline": "离线"
|
||||||
},
|
},
|
||||||
"emby": {
|
"emby": {
|
||||||
"playing": "玩",
|
"playing": "正在播放",
|
||||||
"transcoding": "转码",
|
"transcoding": "转码",
|
||||||
"bitrate": "比特率",
|
"bitrate": "比特率",
|
||||||
"no_active": "No Active Streams"
|
"no_active": "暂无播放"
|
||||||
},
|
},
|
||||||
"tautulli": {
|
"tautulli": {
|
||||||
"playing": "玩",
|
"playing": "正在播放",
|
||||||
"transcoding": "转码",
|
"transcoding": "转码",
|
||||||
"bitrate": "比特率",
|
"bitrate": "比特率",
|
||||||
"no_active": "No Active Streams"
|
"no_active": "暂无播放"
|
||||||
},
|
},
|
||||||
"rutorrent": {
|
"rutorrent": {
|
||||||
"active": "积极的",
|
"active": "活动中",
|
||||||
"upload": "上传",
|
"upload": "上传",
|
||||||
"download": "下载"
|
"download": "下载"
|
||||||
},
|
},
|
||||||
@@ -42,18 +43,18 @@
|
|||||||
"series": "系列"
|
"series": "系列"
|
||||||
},
|
},
|
||||||
"radarr": {
|
"radarr": {
|
||||||
"wanted": "通缉",
|
"wanted": "订阅",
|
||||||
"queued": "排队",
|
"queued": "队列",
|
||||||
"movies": "电影"
|
"movies": "电影"
|
||||||
},
|
},
|
||||||
"readarr": {
|
"readarr": {
|
||||||
"wanted": "Wanted",
|
"wanted": "订阅",
|
||||||
"queued": "Queued",
|
"queued": "队列",
|
||||||
"books": "Books"
|
"books": "书籍"
|
||||||
},
|
},
|
||||||
"ombi": {
|
"ombi": {
|
||||||
"pending": "待办的",
|
"pending": "待办的",
|
||||||
"approved": "得到正式认可的",
|
"approved": "已批准",
|
||||||
"available": "可用的"
|
"available": "可用的"
|
||||||
},
|
},
|
||||||
"jellyseerr": {
|
"jellyseerr": {
|
||||||
@@ -72,9 +73,9 @@
|
|||||||
"ping": "ping"
|
"ping": "ping"
|
||||||
},
|
},
|
||||||
"portainer": {
|
"portainer": {
|
||||||
"running": "跑步",
|
"running": "运行中",
|
||||||
"stopped": "停了下来",
|
"stopped": "已停止",
|
||||||
"total": "全部的"
|
"total": "总计"
|
||||||
},
|
},
|
||||||
"traefik": {
|
"traefik": {
|
||||||
"routers": "路由器",
|
"routers": "路由器",
|
||||||
@@ -87,32 +88,45 @@
|
|||||||
"total": "全部的"
|
"total": "全部的"
|
||||||
},
|
},
|
||||||
"weather": {
|
"weather": {
|
||||||
"current": "Current Location",
|
"current": "当前位置",
|
||||||
"allow": "Click to allow",
|
"allow": "点击并允许",
|
||||||
"updating": "Updating",
|
"updating": "更新中",
|
||||||
"wait": "Please wait"
|
"wait": "请等待"
|
||||||
},
|
},
|
||||||
"overseerr": {
|
"overseerr": {
|
||||||
"pending": "Pending",
|
"pending": "待办",
|
||||||
"approved": "Approved",
|
"approved": "已批准",
|
||||||
"available": "Available"
|
"available": "可用"
|
||||||
},
|
},
|
||||||
"sabnzbd": {
|
"sabnzbd": {
|
||||||
"rate": "Rate",
|
"rate": "速率",
|
||||||
"queue": "Queue",
|
"queue": "队列",
|
||||||
"timeleft": "Time Left"
|
"timeleft": "剩余时间"
|
||||||
},
|
},
|
||||||
"nzbget": {
|
"nzbget": {
|
||||||
"rate": "Rate",
|
"rate": "速率",
|
||||||
"remaining": "Remaining",
|
"remaining": "剩余",
|
||||||
"downloaded": "Downloaded"
|
"downloaded": "下载"
|
||||||
},
|
},
|
||||||
"coinmarketcap": {
|
"coinmarketcap": {
|
||||||
"configure": "Configure one or more crypto currencies to track"
|
"configure": "配置一个或多个需要追踪的加密"
|
||||||
},
|
},
|
||||||
"gotify": {
|
"gotify": {
|
||||||
"apps": "Applications",
|
"apps": "应用",
|
||||||
"clients": "Clients",
|
"clients": "客户端",
|
||||||
"messages": "Messages"
|
"messages": "信息"
|
||||||
|
},
|
||||||
|
"prowlarr": {
|
||||||
|
"enableIndexers": "Indexers",
|
||||||
|
"numberOfGrabs": "Grabs",
|
||||||
|
"numberOfQueries": "Queries",
|
||||||
|
"numberOfFailGrabs": "Fail Grabs",
|
||||||
|
"numberOfFailQueries": "Fail Queries"
|
||||||
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
132
public/locales/zh-Hant/common.json
Normal file
132
public/locales/zh-Hant/common.json
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"widget": {
|
||||||
|
"missing_type": "Missing Widget Type: {{type}}",
|
||||||
|
"api_error": "API Error",
|
||||||
|
"status": "Status"
|
||||||
|
},
|
||||||
|
"weather": {
|
||||||
|
"current": "Current Location",
|
||||||
|
"allow": "Click to allow",
|
||||||
|
"updating": "Updating",
|
||||||
|
"wait": "Please wait"
|
||||||
|
},
|
||||||
|
"docker": {
|
||||||
|
"rx": "RX",
|
||||||
|
"offline": "Offline",
|
||||||
|
"tx": "TX",
|
||||||
|
"mem": "MEM",
|
||||||
|
"cpu": "CPU"
|
||||||
|
},
|
||||||
|
"emby": {
|
||||||
|
"playing": "Playing",
|
||||||
|
"transcoding": "Transcoding",
|
||||||
|
"bitrate": "Bitrate",
|
||||||
|
"no_active": "No Active Streams"
|
||||||
|
},
|
||||||
|
"tautulli": {
|
||||||
|
"playing": "Playing",
|
||||||
|
"transcoding": "Transcoding",
|
||||||
|
"bitrate": "Bitrate",
|
||||||
|
"no_active": "No Active Streams"
|
||||||
|
},
|
||||||
|
"jellyseerr": {
|
||||||
|
"pending": "Pending",
|
||||||
|
"approved": "Approved",
|
||||||
|
"available": "Available"
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"placeholder": "Search…"
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"total": "Total",
|
||||||
|
"free": "Free",
|
||||||
|
"used": "Used",
|
||||||
|
"load": "Load"
|
||||||
|
},
|
||||||
|
"nzbget": {
|
||||||
|
"rate": "Rate",
|
||||||
|
"remaining": "Remaining",
|
||||||
|
"downloaded": "Downloaded"
|
||||||
|
},
|
||||||
|
"sabnzbd": {
|
||||||
|
"rate": "Rate",
|
||||||
|
"queue": "Queue",
|
||||||
|
"timeleft": "Time Left"
|
||||||
|
},
|
||||||
|
"rutorrent": {
|
||||||
|
"active": "Active",
|
||||||
|
"upload": "Upload",
|
||||||
|
"download": "Download"
|
||||||
|
},
|
||||||
|
"radarr": {
|
||||||
|
"movies": "Movies",
|
||||||
|
"wanted": "Wanted",
|
||||||
|
"queued": "Queued"
|
||||||
|
},
|
||||||
|
"sonarr": {
|
||||||
|
"wanted": "Wanted",
|
||||||
|
"queued": "Queued",
|
||||||
|
"series": "Series"
|
||||||
|
},
|
||||||
|
"readarr": {
|
||||||
|
"wanted": "Wanted",
|
||||||
|
"queued": "Queued",
|
||||||
|
"books": "Books"
|
||||||
|
},
|
||||||
|
"ombi": {
|
||||||
|
"pending": "Pending",
|
||||||
|
"approved": "Approved",
|
||||||
|
"available": "Available"
|
||||||
|
},
|
||||||
|
"overseerr": {
|
||||||
|
"pending": "Pending",
|
||||||
|
"approved": "Approved",
|
||||||
|
"available": "Available"
|
||||||
|
},
|
||||||
|
"pihole": {
|
||||||
|
"queries": "Queries",
|
||||||
|
"blocked": "Blocked",
|
||||||
|
"gravity": "Gravity"
|
||||||
|
},
|
||||||
|
"speedtest": {
|
||||||
|
"upload": "Upload",
|
||||||
|
"download": "Download",
|
||||||
|
"ping": "Ping"
|
||||||
|
},
|
||||||
|
"portainer": {
|
||||||
|
"running": "Running",
|
||||||
|
"stopped": "Stopped",
|
||||||
|
"total": "Total"
|
||||||
|
},
|
||||||
|
"traefik": {
|
||||||
|
"routers": "Routers",
|
||||||
|
"services": "Services",
|
||||||
|
"middleware": "Middleware"
|
||||||
|
},
|
||||||
|
"gotify": {
|
||||||
|
"clients": "Clients",
|
||||||
|
"apps": "Applications",
|
||||||
|
"messages": "Messages"
|
||||||
|
},
|
||||||
|
"npm": {
|
||||||
|
"enabled": "Enabled",
|
||||||
|
"disabled": "Disabled",
|
||||||
|
"total": "Total"
|
||||||
|
},
|
||||||
|
"coinmarketcap": {
|
||||||
|
"configure": "Configure one or more crypto currencies to track"
|
||||||
|
},
|
||||||
|
"prowlarr": {
|
||||||
|
"enableIndexers": "Indexers",
|
||||||
|
"numberOfGrabs": "Grabs",
|
||||||
|
"numberOfQueries": "Queries",
|
||||||
|
"numberOfFailGrabs": "Fail Grabs",
|
||||||
|
"numberOfFailQueries": "Fail Queries"
|
||||||
|
},
|
||||||
|
"transmission": {
|
||||||
|
"download": "Download",
|
||||||
|
"upload": "Upload",
|
||||||
|
"leech": "Leech",
|
||||||
|
"seed": "Seed"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ export default function Item({ bookmark }) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => window.open(bookmark.href, "_blank").focus()}
|
onClick={() => window.open(bookmark.href, "_blank").focus()}
|
||||||
className="w-full text-left mb-3 cursor-pointer rounded-md font-medium text-theme-700 hover:text-theme-700 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/10 dark:bg-white/10 dark:hover:bg-white/20 backdrop-blur-md"
|
className="w-full text-left mb-3 cursor-pointer rounded-md font-medium text-theme-700 hover:text-theme-700 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/10 dark:bg-white/10 dark:hover:bg-white/20"
|
||||||
>
|
>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="flex-shrink-0 flex items-center justify-center w-11 bg-theme-500/10 dark:bg-theme-900/50 text-theme-700 hover:text-theme-700 dark:text-theme-200 text-sm font-medium rounded-l-md">
|
<div className="flex-shrink-0 flex items-center justify-center w-11 bg-theme-500/10 dark:bg-theme-900/50 text-theme-700 hover:text-theme-700 dark:text-theme-200 text-sm font-medium rounded-l-md">
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default function Item({ service }) {
|
|||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
hasLink ? "cursor-pointer " : " "
|
hasLink ? "cursor-pointer " : " "
|
||||||
}transition-all h-15 mb-3 p-1 rounded-md font-medium text-theme-700 hover:text-theme-700/70 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/20 dark:bg-white/10 dark:hover:bg-white/20 backdrop-blur-md`}
|
}transition-all h-15 mb-3 p-1 rounded-md font-medium text-theme-700 hover:text-theme-700/70 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/20 dark:bg-white/10 dark:hover:bg-white/20`}
|
||||||
>
|
>
|
||||||
<div className="flex select-none">
|
<div className="flex select-none">
|
||||||
{service.icon &&
|
{service.icon &&
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import Portainer from "./widgets/service/portainer";
|
|||||||
import Emby from "./widgets/service/emby";
|
import Emby from "./widgets/service/emby";
|
||||||
import Nzbget from "./widgets/service/nzbget";
|
import Nzbget from "./widgets/service/nzbget";
|
||||||
import SABnzbd from "./widgets/service/sabnzbd";
|
import SABnzbd from "./widgets/service/sabnzbd";
|
||||||
|
import Transmission from "./widgets/service/transmission";
|
||||||
import Docker from "./widgets/service/docker";
|
import Docker from "./widgets/service/docker";
|
||||||
import Pihole from "./widgets/service/pihole";
|
import Pihole from "./widgets/service/pihole";
|
||||||
import Rutorrent from "./widgets/service/rutorrent";
|
import Rutorrent from "./widgets/service/rutorrent";
|
||||||
@@ -20,6 +21,7 @@ import Npm from "./widgets/service/npm";
|
|||||||
import Tautulli from "./widgets/service/tautulli";
|
import Tautulli from "./widgets/service/tautulli";
|
||||||
import CoinMarketCap from "./widgets/service/coinmarketcap";
|
import CoinMarketCap from "./widgets/service/coinmarketcap";
|
||||||
import Gotify from "./widgets/service/gotify";
|
import Gotify from "./widgets/service/gotify";
|
||||||
|
import Prowlarr from "./widgets/service/prowlarr";
|
||||||
|
|
||||||
const widgetMappings = {
|
const widgetMappings = {
|
||||||
docker: Docker,
|
docker: Docker,
|
||||||
@@ -31,6 +33,8 @@ const widgetMappings = {
|
|||||||
emby: Emby,
|
emby: Emby,
|
||||||
jellyfin: Jellyfin,
|
jellyfin: Jellyfin,
|
||||||
nzbget: Nzbget,
|
nzbget: Nzbget,
|
||||||
|
sabnzbd: SABnzbd,
|
||||||
|
transmission: Transmission,
|
||||||
pihole: Pihole,
|
pihole: Pihole,
|
||||||
rutorrent: Rutorrent,
|
rutorrent: Rutorrent,
|
||||||
speedtest: Speedtest,
|
speedtest: Speedtest,
|
||||||
@@ -41,7 +45,7 @@ const widgetMappings = {
|
|||||||
npm: Npm,
|
npm: Npm,
|
||||||
tautulli: Tautulli,
|
tautulli: Tautulli,
|
||||||
gotify: Gotify,
|
gotify: Gotify,
|
||||||
sabnzbd: SABnzbd
|
prowlarr: Prowlarr
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Widget({ service }) {
|
export default function Widget({ service }) {
|
||||||
|
|||||||
55
src/components/services/widgets/service/prowlarr.jsx
Normal file
55
src/components/services/widgets/service/prowlarr.jsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import useSWR from "swr";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import Widget from "../widget";
|
||||||
|
import Block from "../block";
|
||||||
|
|
||||||
|
import { formatApiUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
|
export default function Prowlarr({ service }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const config = service.widget;
|
||||||
|
|
||||||
|
const { data: indexersData, error: indexersError } = useSWR(formatApiUrl(config, "indexer"));
|
||||||
|
const { data: grabsData, error: grabsError } = useSWR(formatApiUrl(config, "indexerstats"));
|
||||||
|
|
||||||
|
if (indexersError || grabsError) {
|
||||||
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!indexersData || !grabsData) {
|
||||||
|
return (
|
||||||
|
<Widget>
|
||||||
|
<Block label={t("prowlarr.enableIndexers")} />
|
||||||
|
<Block label={t("prowlarr.numberOfGrabs")} />
|
||||||
|
<Block label={t("prowlarr.numberOfQueries")} />
|
||||||
|
<Block label={t("prowlarr.numberOfFailGrabs")} />
|
||||||
|
<Block label={t("prowlarr.numberOfFailQueries")} />
|
||||||
|
</Widget>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexers = indexersData?.filter((indexer) => indexer.enable === true);
|
||||||
|
|
||||||
|
let numberOfGrabs = 0
|
||||||
|
let numberOfQueries = 0
|
||||||
|
let numberOfFailedGrabs = 0
|
||||||
|
let numberOfFailedQueries = 0
|
||||||
|
grabsData?.indexers?.forEach(element => {
|
||||||
|
numberOfGrabs += element.numberOfGrabs;
|
||||||
|
numberOfQueries += element.numberOfQueries;
|
||||||
|
numberOfFailedGrabs += numberOfFailedGrabs + element.numberOfFailedGrabs;
|
||||||
|
numberOfFailedQueries += numberOfFailedQueries + element.numberOfFailedQueries;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Widget>
|
||||||
|
<Block label={t("prowlarr.enableIndexers")} value={indexers.length} />
|
||||||
|
<Block label={t("prowlarr.numberOfGrabs")} value={numberOfGrabs} />
|
||||||
|
<Block label={t("prowlarr.numberOfQueries")} value={numberOfQueries} />
|
||||||
|
<Block label={t("prowlarr.numberOfFailGrabs")} value={numberOfFailedGrabs} />
|
||||||
|
<Block label={t("prowlarr.numberOfFailQueries")} value={numberOfFailedQueries} />
|
||||||
|
</Widget>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ export default function SABnzbd({ service }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Widget>
|
<Widget>
|
||||||
<Block label={t("sabnzbd.rate")} value={`${queueData.queue.speed}bps`} />
|
<Block label={t("sabnzbd.rate")} value={`${queueData.queue.speed}B/s`} />
|
||||||
<Block label={t("sabnzbd.queue")} value={queueData.queue.noofslots} />
|
<Block label={t("sabnzbd.queue")} value={queueData.queue.noofslots} />
|
||||||
<Block label={t("sabnzbd.timeleft")} value={queueData.queue.timeleft} />
|
<Block label={t("sabnzbd.timeleft")} value={queueData.queue.timeleft} />
|
||||||
</Widget>
|
</Widget>
|
||||||
|
|||||||
@@ -48,10 +48,10 @@ function SingleSessionEntry({ session }) {
|
|||||||
/>
|
/>
|
||||||
<div className="text-xs z-10 self-center ml-1">
|
<div className="text-xs z-10 self-center ml-1">
|
||||||
{state === "paused" && (
|
{state === "paused" && (
|
||||||
<BsFillPlayFill className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" />
|
<BsPauseFill className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" />
|
||||||
)}
|
)}
|
||||||
{state !== "paused" && (
|
{state !== "paused" && (
|
||||||
<BsPauseFill className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" />
|
<BsFillPlayFill className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="grow " />
|
<div className="grow " />
|
||||||
@@ -76,10 +76,10 @@ function SessionEntry({ session }) {
|
|||||||
/>
|
/>
|
||||||
<div className="text-xs z-10 self-center ml-1">
|
<div className="text-xs z-10 self-center ml-1">
|
||||||
{state === "paused" && (
|
{state === "paused" && (
|
||||||
<BsFillPlayFill className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" />
|
<BsPauseFill className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" />
|
||||||
)}
|
)}
|
||||||
{state !== "paused" && (
|
{state !== "paused" && (
|
||||||
<BsPauseFill className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" />
|
<BsFillPlayFill className="inline-block w-4 h-4 cursor-pointer -mt-[1px] mr-1 opacity-80" />
|
||||||
)}
|
)}
|
||||||
<span>{full_title}</span>
|
<span>{full_title}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
70
src/components/services/widgets/service/transmission.jsx
Normal file
70
src/components/services/widgets/service/transmission.jsx
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import useSWR from "swr";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import Widget from "../widget";
|
||||||
|
import Block from "../block";
|
||||||
|
|
||||||
|
import { formatApiUrl } from "utils/api-helpers";
|
||||||
|
|
||||||
|
export default function Transmission({ service }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const config = service.widget;
|
||||||
|
|
||||||
|
const { data: torrentData, error: torrentError } = useSWR(formatApiUrl(config));
|
||||||
|
|
||||||
|
if (torrentError) {
|
||||||
|
return <Widget error={t("widget.api_error")} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!torrentData) {
|
||||||
|
return (
|
||||||
|
<Widget>
|
||||||
|
<Block label={t("transmission.leech")} />
|
||||||
|
<Block label={t("transmission.download")} />
|
||||||
|
<Block label={t("transmission.seed")} />
|
||||||
|
<Block label={t("transmission.upload")} />
|
||||||
|
</Widget>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { torrents } = torrentData.arguments;
|
||||||
|
let rateDl = 0;
|
||||||
|
let rateUl = 0;
|
||||||
|
let completed = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < torrents.length; i += 1) {
|
||||||
|
const torrent = torrents[i];
|
||||||
|
rateDl += torrent.rateDownload;
|
||||||
|
rateUl += torrent.rateUpload;
|
||||||
|
if (torrent.percentDone === 1) {
|
||||||
|
completed += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const leech = torrents.length - completed;
|
||||||
|
|
||||||
|
let unitsDl = "KB/s";
|
||||||
|
let unitsUl = "KB/s";
|
||||||
|
rateDl /= 1024;
|
||||||
|
rateUl /= 1024;
|
||||||
|
|
||||||
|
if (rateDl > 1024) {
|
||||||
|
rateDl /= 1024;
|
||||||
|
unitsDl = "MB/s";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rateUl > 1024) {
|
||||||
|
rateUl /= 1024;
|
||||||
|
unitsUl = "MB/s";
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Widget>
|
||||||
|
<Block label={t("transmission.leech")} value={leech} />
|
||||||
|
<Block label={t("transmission.download")} value={`${rateDl.toFixed(2)} ${unitsDl}`} />
|
||||||
|
<Block label={t("transmission.seed")} value={completed} />
|
||||||
|
<Block label={t("transmission.upload")} value={`${rateUl.toFixed(2)} ${unitsUl}`} />
|
||||||
|
</Widget>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
|
|
||||||
import UsageBar from "./usage-bar";
|
import UsageBar from "./usage-bar";
|
||||||
|
|
||||||
export default function Cpu() {
|
export default function Cpu({ expanded }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { data, error } = useSWR(`/api/widgets/resources?type=cpu`, {
|
const { data, error } = useSWR(`/api/widgets/resources?type=cpu`, {
|
||||||
@@ -39,11 +39,29 @@ export default function Cpu() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
|
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
|
||||||
<FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" />
|
<FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" />
|
||||||
<div className="flex flex-col ml-3 text-left font-mono min-w-[80px]">
|
<div className="flex flex-col ml-3 text-left min-w-[85px]">
|
||||||
<div className="text-theme-800 dark:text-theme-200 text-xs">
|
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||||
{t("common.number", { value: data.cpu.usage, style: "unit", unit: "percent", maximumFractionDigits: 0 })}{" "}
|
<div className="pl-0.5">
|
||||||
{t("docker.cpu")}
|
{t("common.number", {
|
||||||
|
value: data.cpu.usage,
|
||||||
|
style: "unit",
|
||||||
|
unit: "percent",
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="pr-1">{t("docker.cpu")}</div>
|
||||||
</div>
|
</div>
|
||||||
|
{expanded && (
|
||||||
|
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||||
|
<div className="pl-0.5">
|
||||||
|
{t("common.number", {
|
||||||
|
value: data.cpu.load,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="pr-1">{t("resources.load")}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<UsageBar percent={percent} />
|
<UsageBar percent={percent} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
|
|
||||||
import UsageBar from "./usage-bar";
|
import UsageBar from "./usage-bar";
|
||||||
|
|
||||||
export default function Disk({ options }) {
|
export default function Disk({ options, expanded }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { data, error } = useSWR(`/api/widgets/resources?type=disk&target=${options.disk}`, {
|
const { data, error } = useSWR(`/api/widgets/resources?type=disk&target=${options.disk}`, {
|
||||||
@@ -37,15 +37,19 @@ export default function Disk({ options }) {
|
|||||||
const percent = Math.round((data.drive.usedGb / data.drive.totalGb) * 100);
|
const percent = Math.round((data.drive.usedGb / data.drive.totalGb) * 100);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-none flex flex-row items-center mr-3 py-1.5 group">
|
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
|
||||||
<FiHardDrive className="text-theme-800 dark:text-theme-200 w-5 h-5" />
|
<FiHardDrive className="text-theme-800 dark:text-theme-200 w-5 h-5" />
|
||||||
<div className="flex flex-col ml-3 text-left min-w-[80px]">
|
<div className="flex flex-col ml-3 text-left min-w-[85px]">
|
||||||
<span className="text-theme-800 dark:text-theme-200 text-xs group-hover:hidden">
|
<span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||||
{t("common.bytes", { value: data.drive.freeGb * 1024 * 1024 * 1024 })} {t("resources.free")}
|
<div className="pl-0.5">{t("common.bytes", { value: data.drive.freeGb * 1024 * 1024 * 1024 })}</div>
|
||||||
</span>
|
<div className="pr-1">{t("resources.free")}</div>
|
||||||
<span className="text-theme-800 dark:text-theme-200 text-xs hidden group-hover:block">
|
|
||||||
{t("common.bytes", { value: data.drive.totalGb * 1024 * 1024 * 1024 })} {t("resources.total")}
|
|
||||||
</span>
|
</span>
|
||||||
|
{expanded && (
|
||||||
|
<span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||||
|
<div className="pl-0.5">{t("common.bytes", { value: data.drive.totalGb * 1024 * 1024 * 1024 })}</div>
|
||||||
|
<div className="pr-1">{t("resources.total")}</div>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<UsageBar percent={percent} />
|
<UsageBar percent={percent} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
|
|||||||
|
|
||||||
import UsageBar from "./usage-bar";
|
import UsageBar from "./usage-bar";
|
||||||
|
|
||||||
export default function Memory() {
|
export default function Memory({ expanded }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { data, error } = useSWR(`/api/widgets/resources?type=memory`, {
|
const { data, error } = useSWR(`/api/widgets/resources?type=memory`, {
|
||||||
@@ -37,15 +37,27 @@ export default function Memory() {
|
|||||||
const percent = Math.round((data.memory.usedMemMb / data.memory.totalMemMb) * 100);
|
const percent = Math.round((data.memory.usedMemMb / data.memory.totalMemMb) * 100);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-none flex flex-row items-center mr-3 py-1.5 group">
|
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
|
||||||
<FaMemory className="text-theme-800 dark:text-theme-200 w-5 h-5" />
|
<FaMemory className="text-theme-800 dark:text-theme-200 w-5 h-5" />
|
||||||
<div className="flex flex-col ml-3 text-left min-w-[80px]">
|
<div className="flex flex-col ml-3 text-left min-w-[85px]">
|
||||||
<span className="text-theme-800 dark:text-theme-200 text-xs group-hover:hidden">
|
<span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||||
{t("common.bytes", { value: data.memory.freeMemMb * 1024 * 1024 })} {t("resources.free")}
|
<div className="pl-0.5">
|
||||||
</span>
|
{t("common.bytes", { value: data.memory.freeMemMb * 1024 * 1024, maximumFractionDigits: 0, binary: true })}
|
||||||
<span className="text-theme-800 dark:text-theme-200 text-xs hidden group-hover:block">
|
</div>
|
||||||
{t("common.bytes", { value: data.memory.usedMemMb * 1024 * 1024 })} {t("resources.used")}
|
<div className="pr-1">{t("resources.free")}</div>
|
||||||
</span>
|
</span>
|
||||||
|
{expanded && (
|
||||||
|
<span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
|
||||||
|
<div className="pl-0.5">
|
||||||
|
{t("common.bytes", {
|
||||||
|
value: data.memory.totalMemMb * 1024 * 1024,
|
||||||
|
maximumFractionDigits: 0,
|
||||||
|
binary: true,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div className="pr-1">{t("resources.total")}</div>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
<UsageBar percent={percent} />
|
<UsageBar percent={percent} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,14 +3,16 @@ import Cpu from "./cpu";
|
|||||||
import Memory from "./memory";
|
import Memory from "./memory";
|
||||||
|
|
||||||
export default function Resources({ options }) {
|
export default function Resources({ options }) {
|
||||||
|
console.log(options);
|
||||||
|
const { expanded } = options;
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col max-w:full sm:basis-auto self-center m-auto flex-wrap">
|
<div className="flex flex-col max-w:full sm:basis-auto self-center m-auto flex-wrap">
|
||||||
<div className="flex flex-row self-center flex-wrap justify-between">
|
<div className="flex flex-row self-center flex-wrap justify-between">
|
||||||
{options.cpu && <Cpu />}
|
{options.cpu && <Cpu expanded={expanded} />}
|
||||||
{options.memory && <Memory />}
|
{options.memory && <Memory expanded={expanded} />}
|
||||||
{Array.isArray(options.disk)
|
{Array.isArray(options.disk)
|
||||||
? options.disk.map((disk) => <Disk key={disk} options={{ disk }} />)
|
? options.disk.map((disk) => <Disk key={disk} options={{ disk }} expanded={expanded} />)
|
||||||
: options.disk && <Disk options={options} />}
|
: options.disk && <Disk options={options} expanded={expanded} />}
|
||||||
</div>
|
</div>
|
||||||
{options.label && (
|
{options.label && (
|
||||||
<div className="ml-6 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div>
|
<div className="ml-6 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export default function UsageBar({ percent }) {
|
export default function UsageBar({ percent }) {
|
||||||
return (
|
return (
|
||||||
<div className="mt-0.5 w-full bg-theme-800/30 rounded-full h-1 dark:bg-white/20 backdrop-blur-md">
|
<div className="mt-0.5 w-full bg-theme-800/30 rounded-full h-1 dark:bg-white/20">
|
||||||
<div
|
<div
|
||||||
className="bg-theme-800/70 h-1 rounded-full dark:bg-white/50"
|
className="bg-theme-800/70 h-1 rounded-full dark:bg-white/50"
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -62,8 +62,7 @@ export default function Search({ options }) {
|
|||||||
bg-white/50 dark:bg-white/10
|
bg-white/50 dark:bg-white/10
|
||||||
focus:ring-theme-500 dark:focus:ring-white/50
|
focus:ring-theme-500 dark:focus:ring-white/50
|
||||||
focus:border-theme-500 dark:focus:border-white/50
|
focus:border-theme-500 dark:focus:border-white/50
|
||||||
border border-theme-300 dark:border-theme-200/50
|
border border-theme-300 dark:border-theme-200/50"
|
||||||
backdrop-blur-md"
|
|
||||||
placeholder={t("search.placeholder")}
|
placeholder={t("search.placeholder")}
|
||||||
onChange={(s) => setQuery(s.currentTarget.value)}
|
onChange={(s) => setQuery(s.currentTarget.value)}
|
||||||
required
|
required
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import credentialedProxyHandler from "utils/proxies/credentialed";
|
|||||||
import rutorrentProxyHandler from "utils/proxies/rutorrent";
|
import rutorrentProxyHandler from "utils/proxies/rutorrent";
|
||||||
import nzbgetProxyHandler from "utils/proxies/nzbget";
|
import nzbgetProxyHandler from "utils/proxies/nzbget";
|
||||||
import npmProxyHandler from "utils/proxies/npm";
|
import npmProxyHandler from "utils/proxies/npm";
|
||||||
|
import transmissionProxyHandler from "utils/proxies/transmission";
|
||||||
|
|
||||||
const serviceProxyHandlers = {
|
const serviceProxyHandlers = {
|
||||||
// uses query param auth
|
// uses query param auth
|
||||||
@@ -23,10 +24,12 @@ const serviceProxyHandlers = {
|
|||||||
overseerr: credentialedProxyHandler,
|
overseerr: credentialedProxyHandler,
|
||||||
ombi: credentialedProxyHandler,
|
ombi: credentialedProxyHandler,
|
||||||
coinmarketcap: credentialedProxyHandler,
|
coinmarketcap: credentialedProxyHandler,
|
||||||
|
prowlarr: credentialedProxyHandler,
|
||||||
// super specific handlers
|
// super specific handlers
|
||||||
rutorrent: rutorrentProxyHandler,
|
rutorrent: rutorrentProxyHandler,
|
||||||
nzbget: nzbgetProxyHandler,
|
nzbget: nzbgetProxyHandler,
|
||||||
npm: npmProxyHandler,
|
npm: npmProxyHandler,
|
||||||
|
transmission: transmissionProxyHandler,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const formats = {
|
|||||||
traefik: `{url}/api/{endpoint}`,
|
traefik: `{url}/api/{endpoint}`,
|
||||||
portainer: `{url}/api/endpoints/{env}/{endpoint}`,
|
portainer: `{url}/api/endpoints/{env}/{endpoint}`,
|
||||||
rutorrent: `{url}/plugins/httprpc/action.php`,
|
rutorrent: `{url}/plugins/httprpc/action.php`,
|
||||||
|
transmission: `{url}/transmission/rpc`,
|
||||||
jellyseerr: `{url}/api/v1/{endpoint}`,
|
jellyseerr: `{url}/api/v1/{endpoint}`,
|
||||||
overseerr: `{url}/api/v1/{endpoint}`,
|
overseerr: `{url}/api/v1/{endpoint}`,
|
||||||
ombi: `{url}/api/v1/{endpoint}`,
|
ombi: `{url}/api/v1/{endpoint}`,
|
||||||
@@ -17,6 +18,7 @@ const formats = {
|
|||||||
sabnzbd: `{url}/api/?apikey={key}&output=json&mode={endpoint}`,
|
sabnzbd: `{url}/api/?apikey={key}&output=json&mode={endpoint}`,
|
||||||
coinmarketcap: `https://pro-api.coinmarketcap.com/{endpoint}`,
|
coinmarketcap: `https://pro-api.coinmarketcap.com/{endpoint}`,
|
||||||
gotify: `{url}/{endpoint}`,
|
gotify: `{url}/{endpoint}`,
|
||||||
|
prowlarr: `{url}/api/v1/{endpoint}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function formatApiCall(api, args) {
|
export function formatApiCall(api, args) {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export function httpsRequest(url, params) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
response.on("end", () => {
|
response.on("end", () => {
|
||||||
resolve([response.statusCode, response.headers["content-type"], Buffer.concat(data)]);
|
resolve([response.statusCode, response.headers["content-type"], Buffer.concat(data), response.headers]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -20,6 +20,10 @@ export function httpsRequest(url, params) {
|
|||||||
reject([500, error]);
|
reject([500, error]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (params.body) {
|
||||||
|
request.write(params.body);
|
||||||
|
}
|
||||||
|
|
||||||
request.end();
|
request.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -34,7 +38,7 @@ export function httpRequest(url, params) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
response.on("end", () => {
|
response.on("end", () => {
|
||||||
resolve([response.statusCode, response.headers["content-type"], Buffer.concat(data)]);
|
resolve([response.statusCode, response.headers["content-type"], Buffer.concat(data), response.headers]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -42,6 +46,10 @@ export function httpRequest(url, params) {
|
|||||||
reject([500, error]);
|
reject([500, error]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (params.body) {
|
||||||
|
request.write(params.body);
|
||||||
|
}
|
||||||
|
|
||||||
request.end();
|
request.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
56
src/utils/proxies/transmission.js
Normal file
56
src/utils/proxies/transmission.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { httpProxy } from "utils/http";
|
||||||
|
import { formatApiCall } from "utils/api-helpers";
|
||||||
|
import getServiceWidget from "utils/service-helpers";
|
||||||
|
|
||||||
|
export default async function transmissionProxyHandler(req, res) {
|
||||||
|
const { group, service, endpoint } = req.query;
|
||||||
|
|
||||||
|
if (!group || !service) {
|
||||||
|
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const widget = await getServiceWidget(group, service);
|
||||||
|
|
||||||
|
if (!widget) {
|
||||||
|
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(formatApiCall(widget.type, { endpoint, ...widget }));
|
||||||
|
const csrfHeaderName = "x-transmission-session-id";
|
||||||
|
|
||||||
|
const method = "POST";
|
||||||
|
const auth = `${widget.username}:${widget.password}`;
|
||||||
|
const body = JSON.stringify({
|
||||||
|
method: "torrent-get",
|
||||||
|
arguments: {
|
||||||
|
fields: ["percentDone", "status", "rateDownload", "rateUpload"]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
"content-type": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
let [status, contentType, data, responseHeaders] = await httpProxy(url, {
|
||||||
|
method,
|
||||||
|
auth,
|
||||||
|
body,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (status === 409) {
|
||||||
|
// Transmission is rejecting the request, but returning a CSRF token
|
||||||
|
headers[csrfHeaderName] = responseHeaders[csrfHeaderName];
|
||||||
|
|
||||||
|
// retry the request, now with the CSRF token
|
||||||
|
[status, contentType, data, responseHeaders] = await httpProxy(url, {
|
||||||
|
method,
|
||||||
|
auth,
|
||||||
|
body,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentType) res.setHeader("Content-Type", contentType);
|
||||||
|
return res.status(status).send(data);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user