Merge branch 'dev'

This commit is contained in:
shamoon 2025-03-27 13:21:45 -07:00
commit 6bae078a8d
No known key found for this signature in database
75 changed files with 1106 additions and 312 deletions

View File

@ -15,7 +15,7 @@ export HOMEPAGE_BUILDTIME=$(date +%s)
# Set privileges for /app but only if pid 1 user is root and we are dropping privileges. # Set privileges for /app but only if pid 1 user is root and we are dropping privileges.
# If container is run as an unprivileged user, it means owner already handled ownership setup on their own. # If container is run as an unprivileged user, it means owner already handled ownership setup on their own.
# Running chown in that case (as non-root) will cause error # Running chown in that case (as non-root) will cause error
[ "$(id -u)" == "0" ] && [ "${PUID}" != "0" ] && chown -R ${PUID}:${PGID} /app [ "$(id -u)" == "0" ] && [ "${PUID}" != "0" ] && chown -R ${PUID}:${PGID} /app/config /app/public
# Drop privileges (when asked to) if root, otherwise run as current user # Drop privileges (when asked to) if root, otherwise run as current user
if [ "$(id -u)" == "0" ] && [ "${PUID}" != "0" ]; then if [ "$(id -u)" == "0" ] && [ "${PUID}" != "0" ]; then

View File

@ -78,7 +78,7 @@ background:
You can apply a blur filter to the service & bookmark cards. Note this option is incompatible with the background blur, saturate and brightness filters. You can apply a blur filter to the service & bookmark cards. Note this option is incompatible with the background blur, saturate and brightness filters.
```yaml ```yaml
cardBlur: sm # sm, "", md, etc... see https://tailwindcss.com/docs/backdrop-blur cardBlur: xs # xs, md, etc... see https://tailwindcss.com/docs/backdrop-blur
``` ```
## Favicon ## Favicon
@ -254,15 +254,29 @@ layout:
columns: 4 columns: 4
``` ```
### Five Columns ### Full Width
You can add a fifth column to services (when `style: columns` which is default) by adding: You can make homepage take up the entire window width by adding:
```yaml ```yaml
fiveColumns: true fullWidth: true
``` ```
By default homepage will max out at 4 columns for services with `columns` style ### Maximum Group Columns
You can set the maximum number of columns of groups on larger screen sizes (note this is only for groups with the default `style: columns`, not groups with `stle: row`) by adding:
```yaml
maxGroupColumns: 8 # default is 4 for services, 6 for bookmarks, max 8
```
By default homepage will max out at 4 columns for services and 6 for bookmarks, thus the minimum for this setting is _5_. Of course, if you're setting this to higher numbers, you may want to consider enabling the [fullWidth](#full-width) option as well.
If you want to set the maximum columns for bookmark groups separately, you can do so by adding:
```yaml
maxBookmarkGroupColumns: 6 # default is 6, max 8
```
### Collapsible sections ### Collapsible sections

View File

@ -70,7 +70,9 @@ If, after correctly adding and mapping your custom icons via the [Icons](../conf
## Disabling IPv6 ## Disabling IPv6
If you are having issues with certain widgets that are unable to reach public APIs (e.g. weather), you may need to disable IPv6 on your host machine. This can be done by adding the following to your `docker-compose.yml` file (or for docker run, the equivalent flag): If you are having issues with certain widgets that are unable to reach public APIs (e.g. weather), in certain setups you may need to disable IPv6. You can set the environment variable `HOMEPAGE_PROXY_DISABLE_IPV6` to `true` to disable IPv6 for the homepage proxy.
Alternatively, you can use the `sysctls` option in your docker-compose file to disable IPv6 for the homepage container completely:
```yaml ```yaml
services: services:
@ -79,12 +81,3 @@ services:
sysctls: sysctls:
- net.ipv6.conf.all.disable_ipv6=1 - net.ipv6.conf.all.disable_ipv6=1
``` ```
or disable IPv6 for the docker network:
```yaml
networks:
some_network:
driver: bridge
enable_ipv6: false
```

View File

@ -19,27 +19,22 @@ widget:
requestBody: # optional, can be string or object, see below requestBody: # optional, can be string or object, see below
display: # optional, default to block, see below display: # optional, default to block, see below
mappings: mappings:
- field: key # needs to be YAML string or object - field: key
label: Field 1 label: Field 1
format: text # optional - defaults to text format: text # optional - defaults to text
- field: # needs to be YAML string or object - field: path.to.key2
path:
to: key2
format: number # optional - defaults to text format: number # optional - defaults to text
label: Field 2 label: Field 2
- field: # needs to be YAML string or object - field: path.to.another.key3
path:
to:
another: key3
label: Field 3 label: Field 3
format: percent # optional - defaults to text format: percent # optional - defaults to text
- field: key # needs to be YAML string or object - field: key
label: Field 4 label: Field 4
format: date # optional - defaults to text format: date # optional - defaults to text
locale: nl # optional locale: nl # optional
dateStyle: long # optional - defaults to "long". Allowed values: `["full", "long", "medium", "short"]`. dateStyle: long # optional - defaults to "long". Allowed values: `["full", "long", "medium", "short"]`.
timeStyle: medium # optional - Allowed values: `["full", "long", "medium", "short"]`. timeStyle: medium # optional - Allowed values: `["full", "long", "medium", "short"]`.
- field: key # needs to be YAML string or object - field: key
label: Field 5 label: Field 5
format: relativeDate # optional - defaults to text format: relativeDate # optional - defaults to text
locale: nl # optional locale: nl # optional
@ -49,9 +44,7 @@ widget:
label: Field 6 label: Field 6
format: text format: text
additionalField: # optional additionalField: # optional
field: field: hourly.time.key
hourly:
time: other key
color: theme # optional - defaults to "". Allowed values: `["theme", "adaptive", "black", "white"]`. color: theme # optional - defaults to "". Allowed values: `["theme", "adaptive", "black", "white"]`.
format: date # optional format: date # optional
- field: key - field: key
@ -103,9 +96,16 @@ mappings:
label: Name label: Name
- field: status # Alive - field: status # Alive
label: Status label: Status
- field: - field: origin.name # Earth (C-137)
origin: name # Earth (C-137)
label: Origin label: Origin
- field: locations.1.name # Citadel of Ricks
label: Location
```
Note that older versions of the widget accepted fields as a yaml object, which is still supported. E.g.:
```yaml
mappings:
- field: - field:
locations: locations:
1: name # Citadel of Ricks 1: name # Citadel of Ricks
@ -138,7 +138,15 @@ You can manipulate data with the following tools `remap`, `scale`, `prefix` and
prefix: "$" prefix: "$"
``` ```
## List View ## Display Options
The widget supports different display modes that can be set using the `display` property.
### Block View (Default)
The default display mode is `block`, which shows fields in a block format.
### List View
You can change the default block view to a list view by setting the `display` option to `list`. You can change the default block view to a list view by setting the `display` option to `list`.
@ -162,13 +170,53 @@ The list view can optionally display an additional field next to the primary fie
- any: true # will map all other values - any: true # will map all other values
to: Unknown to: Unknown
additionalField: additionalField:
field: field: hourly.time.key
hourly:
time: key
color: theme color: theme
format: date format: date
``` ```
### Dynamic List View
To display a list of items from an array in the API response, set the `display` property to `dynamic-list` and configure the `mappings` object with the following properties:
```yaml
widget:
type: customapi
url: https://example.com/api/servers
display: dynamic-list
mappings:
items: data # optional, the path to the array in the API response. Omit this option if the array is at the root level
name: id # required, field in each item to use as the item name (left side)
label: ip_address # required, field in each item to use as the item label (right side)
limit: 5 # optional, limit the number of items to display
target: https://example.com/server/{id} # optional, makes items clickable with template support
```
This configuration would work with an API that returns a response like:
```json
{
"data": [
{ "id": "server1", "name": "Server 1", "ip_address": "192.168.0.1" },
{ "id": "server2", "name": "Server 2", "ip_address": "192.168.0.2" }
]
}
```
The widget would display a list with two items:
- "Server 1" on the left and "192.168.0.1" on the right, clickable to "https://example.com/server/server1"
- "Server 2" on the left and "192.168.0.2" on the right, clickable to "https://example.com/server/server2"
For nested fields in the items, you can use dot notation:
```yaml
mappings:
items: data.results.servers
name: details.id
label: details.name
```
## Custom Headers ## Custom Headers
Pass custom headers using the `headers` option, for example: Pass custom headers using the `headers` option, for example:

View File

@ -7,7 +7,7 @@ Learn more about [Gitea](https://gitea.com).
API token requires `notifications`, `repository` and `issue` permissions. See the [gitea documentation](https://docs.gitea.com/development/api-usage#generating-and-listing-api-tokens) for details on generating tokens. API token requires `notifications`, `repository` and `issue` permissions. See the [gitea documentation](https://docs.gitea.com/development/api-usage#generating-and-listing-api-tokens) for details on generating tokens.
Allowed fields: `["notifications", "issues", "pulls"]`. Allowed fields: `["repositories", "notifications", "issues", "pulls"]`.
```yaml ```yaml
widget: widget:

View File

@ -117,6 +117,7 @@ You can also find a list of all available service widgets in the sidebar navigat
- [ruTorrent](rutorrent.md) - [ruTorrent](rutorrent.md)
- [SABnzbd](sabnzbd.md) - [SABnzbd](sabnzbd.md)
- [Scrutiny](scrutiny.md) - [Scrutiny](scrutiny.md)
- [Slskd](slskd.md)
- [Sonarr](sonarr.md) - [Sonarr](sonarr.md)
- [Speedtest Tracker](speedtest-tracker.md) - [Speedtest Tracker](speedtest-tracker.md)
- [Stash](stash.md) - [Stash](stash.md)

View File

@ -0,0 +1,25 @@
---
title: Slskd
description: Slskd Widget Configuration
---
Learn more about [Slskd](https://github.com/slskd/slskd).
Generate an API key for slskd with `openssl rand -base64 48`.
Add it to your `path/to/config/slskd.yml` in `web > authentication > api_keys`:
```yaml
homepage_widget:
key: <generated key>
role: readonly
cidr: <homepage subnet>
```
Allowed fields: `["slskStatus", "updateStatus", "downloads", "uploads", "sharedFiles"]` (maximum of 4).
```yaml
widget:
type: slskd
url: http[s]://slskd.host.or.ip[:5030]
key: generatedapikey
```

View File

@ -25,7 +25,7 @@
"luxon": "^3.5.0", "luxon": "^3.5.0",
"memory-cache": "^0.2.0", "memory-cache": "^0.2.0",
"minecraftstatuspinger": "^1.2.2", "minecraftstatuspinger": "^1.2.2",
"next": "^15.1.7", "next": "^15.2.3",
"next-i18next": "^12.1.0", "next-i18next": "^12.1.0",
"ping": "^0.4.4", "ping": "^0.4.4",
"pretty-bytes": "^6.1.1", "pretty-bytes": "^6.1.1",

113
pnpm-lock.yaml generated
View File

@ -51,11 +51,11 @@ importers:
specifier: ^1.2.2 specifier: ^1.2.2
version: 1.2.2 version: 1.2.2
next: next:
specifier: ^15.1.7 specifier: ^15.2.3
version: 15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 15.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-i18next: next-i18next:
specifier: ^12.1.0 specifier: ^12.1.0
version: 12.1.0(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 12.1.0(next@15.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
ping: ping:
specifier: ^0.4.4 specifier: ^0.4.4
version: 0.4.4 version: 0.4.4
@ -101,6 +101,10 @@ importers:
xml-js: xml-js:
specifier: ^1.6.11 specifier: ^1.6.11
version: 1.6.11 version: 1.6.11
optionalDependencies:
osx-temperature-sensor:
specifier: ^1.0.8
version: 1.0.8
devDependencies: devDependencies:
'@tailwindcss/forms': '@tailwindcss/forms':
specifier: ^0.5.10 specifier: ^0.5.10
@ -147,10 +151,6 @@ importers:
typescript: typescript:
specifier: ^5.7.3 specifier: ^5.7.3
version: 5.7.3 version: 5.7.3
optionalDependencies:
osx-temperature-sensor:
specifier: ^1.0.8
version: 1.0.8
packages: packages:
@ -376,56 +376,56 @@ packages:
'@kubernetes/client-node@1.0.0': '@kubernetes/client-node@1.0.0':
resolution: {integrity: sha512-a8NSvFDSHKFZ0sR1hbPSf8IDFNJwctEU5RodSCNiq/moRXWmrdmqhb1RRQzF+l+TSBaDgHw3YsYNxxE92STBzw==} resolution: {integrity: sha512-a8NSvFDSHKFZ0sR1hbPSf8IDFNJwctEU5RodSCNiq/moRXWmrdmqhb1RRQzF+l+TSBaDgHw3YsYNxxE92STBzw==}
'@next/env@15.1.7': '@next/env@15.2.3':
resolution: {integrity: sha512-d9jnRrkuOH7Mhi+LHav2XW91HOgTAWHxjMPkXMGBc9B2b7614P7kjt8tAplRvJpbSt4nbO1lugcT/kAaWzjlLQ==} resolution: {integrity: sha512-a26KnbW9DFEUsSxAxKBORR/uD9THoYoKbkpFywMN/AFvboTt94b8+g/07T8J6ACsdLag8/PDU60ov4rPxRAixw==}
'@next/eslint-plugin-next@15.1.7': '@next/eslint-plugin-next@15.1.7':
resolution: {integrity: sha512-kRP7RjSxfTO13NE317ek3mSGzoZlI33nc/i5hs1KaWpK+egs85xg0DJ4p32QEiHnR0mVjuUfhRIun7awqfL7pQ==} resolution: {integrity: sha512-kRP7RjSxfTO13NE317ek3mSGzoZlI33nc/i5hs1KaWpK+egs85xg0DJ4p32QEiHnR0mVjuUfhRIun7awqfL7pQ==}
'@next/swc-darwin-arm64@15.1.7': '@next/swc-darwin-arm64@15.2.3':
resolution: {integrity: sha512-hPFwzPJDpA8FGj7IKV3Yf1web3oz2YsR8du4amKw8d+jAOHfYHYFpMkoF6vgSY4W6vB29RtZEklK9ayinGiCmQ==} resolution: {integrity: sha512-uaBhA8aLbXLqwjnsHSkxs353WrRgQgiFjduDpc7YXEU0B54IKx3vU+cxQlYwPCyC8uYEEX7THhtQQsfHnvv8dw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@next/swc-darwin-x64@15.1.7': '@next/swc-darwin-x64@15.2.3':
resolution: {integrity: sha512-2qoas+fO3OQKkU0PBUfwTiw/EYpN+kdAx62cePRyY1LqKtP09Vp5UcUntfZYajop5fDFTjSxCHfZVRxzi+9FYQ==} resolution: {integrity: sha512-pVwKvJ4Zk7h+4hwhqOUuMx7Ib02u3gDX3HXPKIShBi9JlYllI0nU6TWLbPT94dt7FSi6mSBhfc2JrHViwqbOdw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@next/swc-linux-arm64-gnu@15.1.7': '@next/swc-linux-arm64-gnu@15.2.3':
resolution: {integrity: sha512-sKLLwDX709mPdzxMnRIXLIT9zaX2w0GUlkLYQnKGoXeWUhcvpCrK+yevcwCJPdTdxZEUA0mOXGLdPsGkudGdnA==} resolution: {integrity: sha512-50ibWdn2RuFFkOEUmo9NCcQbbV9ViQOrUfG48zHBCONciHjaUKtHcYFiCwBVuzD08fzvzkWuuZkd4AqbvKO7UQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-arm64-musl@15.1.7': '@next/swc-linux-arm64-musl@15.2.3':
resolution: {integrity: sha512-zblK1OQbQWdC8fxdX4fpsHDw+VSpBPGEUX4PhSE9hkaWPrWoeIJn+baX53vbsbDRaDKd7bBNcXRovY1hEhFd7w==} resolution: {integrity: sha512-2gAPA7P652D3HzR4cLyAuVYwYqjG0mt/3pHSWTCyKZq/N/dJcUAEoNQMyUmwTZWCJRKofB+JPuDVP2aD8w2J6Q==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-x64-gnu@15.1.7': '@next/swc-linux-x64-gnu@15.2.3':
resolution: {integrity: sha512-GOzXutxuLvLHFDAPsMP2zDBMl1vfUHHpdNpFGhxu90jEzH6nNIgmtw/s1MDwpTOiM+MT5V8+I1hmVFeAUhkbgQ==} resolution: {integrity: sha512-ODSKvrdMgAJOVU4qElflYy1KSZRM3M45JVbeZu42TINCMG3anp7YCBn80RkISV6bhzKwcUqLBAmOiWkaGtBA9w==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-linux-x64-musl@15.1.7': '@next/swc-linux-x64-musl@15.2.3':
resolution: {integrity: sha512-WrZ7jBhR7ATW1z5iEQ0ZJfE2twCNSXbpCSaAunF3BKcVeHFADSI/AW1y5Xt3DzTqPF1FzQlwQTewqetAABhZRQ==} resolution: {integrity: sha512-ZR9kLwCWrlYxwEoytqPi1jhPd1TlsSJWAc+H/CJHmHkf2nD92MQpSRIURR1iNgA/kuFSdxB8xIPt4p/T78kwsg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-win32-arm64-msvc@15.1.7': '@next/swc-win32-arm64-msvc@15.2.3':
resolution: {integrity: sha512-LDnj1f3OVbou1BqvvXVqouJZKcwq++mV2F+oFHptToZtScIEnhNRJAhJzqAtTE2dB31qDYL45xJwrc+bLeKM2Q==} resolution: {integrity: sha512-+G2FrDcfm2YDbhDiObDU/qPriWeiz/9cRR0yMWJeTLGGX6/x8oryO3tt7HhodA1vZ8r2ddJPCjtLcpaVl7TE2Q==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@next/swc-win32-x64-msvc@15.1.7': '@next/swc-win32-x64-msvc@15.2.3':
resolution: {integrity: sha512-dC01f1quuf97viOfW05/K8XYv2iuBgAxJZl7mbCKEjMgdQl5JjAKJ0D2qMKZCgPWDeFbFT0Q0nYWwytEW0DWTQ==} resolution: {integrity: sha512-gHYS9tc+G2W0ZC8rBL+H6RdtXIyk40uLiaos0yj5US85FNhbFEndMA2nW3z47nzOWiSvXTZ5kBClc3rD0zJg0w==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@ -882,8 +882,8 @@ packages:
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
caniuse-lite@1.0.30001700: caniuse-lite@1.0.30001706:
resolution: {integrity: sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==} resolution: {integrity: sha512-3ZczoTApMAZwPKYWmwVbQMFpXBDds3/0VciVoUwPUbldlYyVLmRVuRs/PcUZtHpbLRpzzDvrvnFuREsGt6lUug==}
chalk@4.1.2: chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
@ -1955,6 +1955,11 @@ packages:
nan@2.22.0: nan@2.22.0:
resolution: {integrity: sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==} resolution: {integrity: sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==}
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
nanoid@3.3.8: nanoid@3.3.8:
resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@ -1970,8 +1975,8 @@ packages:
next: '>= 10.0.0' next: '>= 10.0.0'
react: '>= 16.8.0' react: '>= 16.8.0'
next@15.1.7: next@15.2.3:
resolution: {integrity: sha512-GNeINPGS9c6OZKCvKypbL8GTsT5GhWPp4DM0fzkXJuXMilOO2EeFxuAY6JZbtk6XIl6Ws10ag3xRINDjSO5+wg==} resolution: {integrity: sha512-x6eDkZxk2rPpu46E1ZVUWIBhYCLszmUY6fvHBFcbzJ9dD+qRX6vcHusaqqDlnY+VngKzKbAiG2iRCkPbmi8f7w==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -2977,34 +2982,34 @@ snapshots:
- encoding - encoding
- utf-8-validate - utf-8-validate
'@next/env@15.1.7': {} '@next/env@15.2.3': {}
'@next/eslint-plugin-next@15.1.7': '@next/eslint-plugin-next@15.1.7':
dependencies: dependencies:
fast-glob: 3.3.1 fast-glob: 3.3.1
'@next/swc-darwin-arm64@15.1.7': '@next/swc-darwin-arm64@15.2.3':
optional: true optional: true
'@next/swc-darwin-x64@15.1.7': '@next/swc-darwin-x64@15.2.3':
optional: true optional: true
'@next/swc-linux-arm64-gnu@15.1.7': '@next/swc-linux-arm64-gnu@15.2.3':
optional: true optional: true
'@next/swc-linux-arm64-musl@15.1.7': '@next/swc-linux-arm64-musl@15.2.3':
optional: true optional: true
'@next/swc-linux-x64-gnu@15.1.7': '@next/swc-linux-x64-gnu@15.2.3':
optional: true optional: true
'@next/swc-linux-x64-musl@15.1.7': '@next/swc-linux-x64-musl@15.2.3':
optional: true optional: true
'@next/swc-win32-arm64-msvc@15.1.7': '@next/swc-win32-arm64-msvc@15.2.3':
optional: true optional: true
'@next/swc-win32-x64-msvc@15.1.7': '@next/swc-win32-x64-msvc@15.2.3':
optional: true optional: true
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
@ -3486,7 +3491,7 @@ snapshots:
callsites@3.1.0: {} callsites@3.1.0: {}
caniuse-lite@1.0.30001700: {} caniuse-lite@1.0.30001706: {}
chalk@4.1.2: chalk@4.1.2:
dependencies: dependencies:
@ -4684,11 +4689,13 @@ snapshots:
nan@2.22.0: nan@2.22.0:
optional: true optional: true
nanoid@3.3.11: {}
nanoid@3.3.8: {} nanoid@3.3.8: {}
natural-compare@1.4.0: {} natural-compare@1.4.0: {}
next-i18next@12.1.0(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): next-i18next@12.1.0(next@15.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
'@babel/runtime': 7.26.9 '@babel/runtime': 7.26.9
'@types/hoist-non-react-statics': 3.3.6 '@types/hoist-non-react-statics': 3.3.6
@ -4696,33 +4703,33 @@ snapshots:
hoist-non-react-statics: 3.3.2 hoist-non-react-statics: 3.3.2
i18next: 21.10.0 i18next: 21.10.0
i18next-fs-backend: 1.2.0 i18next-fs-backend: 1.2.0
next: 15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next: 15.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1 react: 18.3.1
react-i18next: 11.18.6(i18next@21.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-i18next: 11.18.6(i18next@21.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
transitivePeerDependencies: transitivePeerDependencies:
- react-dom - react-dom
- react-native - react-native
next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): next@15.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
'@next/env': 15.1.7 '@next/env': 15.2.3
'@swc/counter': 0.1.3 '@swc/counter': 0.1.3
'@swc/helpers': 0.5.15 '@swc/helpers': 0.5.15
busboy: 1.6.0 busboy: 1.6.0
caniuse-lite: 1.0.30001700 caniuse-lite: 1.0.30001706
postcss: 8.4.31 postcss: 8.4.31
react: 18.3.1 react: 18.3.1
react-dom: 18.3.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1)
styled-jsx: 5.1.6(react@18.3.1) styled-jsx: 5.1.6(react@18.3.1)
optionalDependencies: optionalDependencies:
'@next/swc-darwin-arm64': 15.1.7 '@next/swc-darwin-arm64': 15.2.3
'@next/swc-darwin-x64': 15.1.7 '@next/swc-darwin-x64': 15.2.3
'@next/swc-linux-arm64-gnu': 15.1.7 '@next/swc-linux-arm64-gnu': 15.2.3
'@next/swc-linux-arm64-musl': 15.1.7 '@next/swc-linux-arm64-musl': 15.2.3
'@next/swc-linux-x64-gnu': 15.1.7 '@next/swc-linux-x64-gnu': 15.2.3
'@next/swc-linux-x64-musl': 15.1.7 '@next/swc-linux-x64-musl': 15.2.3
'@next/swc-win32-arm64-msvc': 15.1.7 '@next/swc-win32-arm64-msvc': 15.2.3
'@next/swc-win32-x64-msvc': 15.1.7 '@next/swc-win32-x64-msvc': 15.2.3
sharp: 0.33.5 sharp: 0.33.5
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
@ -4860,7 +4867,7 @@ snapshots:
postcss@8.4.31: postcss@8.4.31:
dependencies: dependencies:
nanoid: 3.3.8 nanoid: 3.3.11
picocolors: 1.1.1 picocolors: 1.1.1
source-map-js: 1.2.1 source-map-js: 1.2.1

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Kennisgewings", "notifications": "Kennisgewings",
"issues": "Kwessies", "issues": "Kwessies",
"pulls": "Trek Versoeke" "pulls": "Trek Versoeke",
"repositories": "Bewaarplekke"
}, },
"stash": { "stash": {
"scenes": "Tonele", "scenes": "Tonele",
@ -1024,11 +1025,22 @@
"timeleft": "Oorblywende Tyd" "timeleft": "Oorblywende Tyd"
}, },
"hoarder": { "hoarder": {
"bookmarks": "Bookmarks", "bookmarks": "Boekmerke",
"favorites": "Favorites", "favorites": "Gunstelinge",
"archived": "Archived", "archived": "Geargiveer",
"highlights": "Highlights", "highlights": "Hoogtepunte",
"lists": "Lists", "lists": "Lyste",
"tags": "Merkers" "tags": "Merkers"
},
"slskd": {
"slskStatus": "Netwerk",
"connected": "Gekoppel",
"disconnected": "Ontkoppel",
"updateStatus": "Opdateer",
"update_yes": "Beskikbaar",
"update_no": "Op Datum",
"downloads": "Aflaaie",
"uploads": "Oplaaie",
"sharedFiles": "Lêers"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "الإشعارات", "notifications": "الإشعارات",
"issues": "المُشكِلات", "issues": "المُشكِلات",
"pulls": "طلبات السحب" "pulls": "طلبات السحب",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "المشاهد", "scenes": "المشاهد",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "التصنيفات" "tags": "التصنيفات"
},
"slskd": {
"slskStatus": "الشبكة",
"connected": "متصل",
"disconnected": "غير متصل",
"updateStatus": "Update",
"update_yes": "متاح",
"update_no": "حتى الآن",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "ملفات"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Известия", "notifications": "Известия",
"issues": "Издания", "issues": "Издания",
"pulls": "Заявки за сливане" "pulls": "Заявки за сливане",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Сцени", "scenes": "Сцени",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Тагове" "tags": "Тагове"
},
"slskd": {
"slskStatus": "Мрежа",
"connected": "Свързан",
"disconnected": "Не е свързан",
"updateStatus": "Update",
"update_yes": "Наличен",
"update_no": "Актуално",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Файлове"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notificacions", "notifications": "Notificacions",
"issues": "Problemes", "issues": "Problemes",
"pulls": "Sol·licitud de Canvis" "pulls": "Sol·licitud de Canvis",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Escenes", "scenes": "Escenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Etiquetes" "tags": "Etiquetes"
},
"slskd": {
"slskStatus": "Xarxa",
"connected": "Connectat",
"disconnected": "Desconnectat",
"updateStatus": "Update",
"update_yes": "Disponible",
"update_no": "Actualitzat",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Fitxers"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Problémy", "issues": "Problémy",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "",
"disconnected": "Odpojeno",
"updateStatus": "Update",
"update_yes": "Dostupné",
"update_no": "Žádné",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Soubory"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Problemer", "issues": "Problemer",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "Tilgængelig",
"update_no": "Opdateret",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Filer"
} }
} }

View File

@ -447,7 +447,7 @@
"write": "Schreiben", "write": "Schreiben",
"gpu": "GPU", "gpu": "GPU",
"mem": "RAM", "mem": "RAM",
"swap": "Swap" "swap": "Auslagerung"
}, },
"quicklaunch": { "quicklaunch": {
"bookmark": "Lesezeichen", "bookmark": "Lesezeichen",
@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Benachrichtigungen", "notifications": "Benachrichtigungen",
"issues": "Probleme", "issues": "Probleme",
"pulls": "Pull-Requests" "pulls": "Pull-Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Szenen", "scenes": "Szenen",
@ -1024,11 +1025,22 @@
"timeleft": "Verbleibende Zeit" "timeleft": "Verbleibende Zeit"
}, },
"hoarder": { "hoarder": {
"bookmarks": "Bookmarks", "bookmarks": "Lesezeichen",
"favorites": "Favorites", "favorites": "Favoriten",
"archived": "Archived", "archived": "Archiviert",
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Listen",
"tags": "Schlagwörter" "tags": "Schlagwörter"
},
"slskd": {
"slskStatus": "Netzwerk",
"connected": "Verbunden",
"disconnected": "Getrennt",
"updateStatus": "Update",
"update_yes": "Verfügbar",
"update_no": "Aktuell",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Dateien"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Ετικέτες" "tags": "Ετικέτες"
},
"slskd": {
"slskStatus": "Δίκτυο",
"connected": "Συνδέθηκε",
"disconnected": "Αποσυνδέθηκε",
"updateStatus": "Update",
"update_yes": "Διαθέσιμο",
"update_no": "Ενημερωμένο",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Αρχεία"
} }
} }

View File

@ -883,9 +883,10 @@
"species": "Species" "species": "Species"
}, },
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "Available",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "Havebla",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notificaciones", "notifications": "Notificaciones",
"issues": "Números", "issues": "Números",
"pulls": "Solicitudes de cambios" "pulls": "Solicitudes de cambios",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Escenas", "scenes": "Escenas",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Etiquetas" "tags": "Etiquetas"
},
"slskd": {
"slskStatus": "Red",
"connected": "Conectado",
"disconnected": "Desconectado",
"updateStatus": "Update",
"update_yes": "Disponible",
"update_no": "Actualizado",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Archivos"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Jakinarazpenak", "notifications": "Jakinarazpenak",
"issues": "Arazoak", "issues": "Arazoak",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Zerrendak", "lists": "Zerrendak",
"tags": "Etiketak" "tags": "Etiketak"
},
"slskd": {
"slskStatus": "Network",
"connected": "Konektatuta",
"disconnected": "Deskonektatuta",
"updateStatus": "Update",
"update_yes": "Available",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "Saatavilla",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -149,8 +149,8 @@
"received": "Reçu", "received": "Reçu",
"sent": "Envoyé", "sent": "Envoyé",
"externalIPAddress": "IP externe", "externalIPAddress": "IP externe",
"externalIPv6Address": "Ext. IPv6", "externalIPv6Address": "IPv6 externe",
"externalIPv6Prefix": "Ext. IPv6-Prefix" "externalIPv6Prefix": "Préfixe IPv6 externe"
}, },
"caddy": { "caddy": {
"upstreams": "En amont", "upstreams": "En amont",
@ -178,7 +178,7 @@
"connectedAp": "AP connectés", "connectedAp": "AP connectés",
"activeUser": "Périphériques actifs", "activeUser": "Périphériques actifs",
"alerts": "Alertes", "alerts": "Alertes",
"connectedGateways": "Connected gateways", "connectedGateways": "Passerelles connectées",
"connectedSwitches": "Switchs connectés" "connectedSwitches": "Switchs connectés"
}, },
"nzbget": { "nzbget": {
@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Anomalies", "issues": "Anomalies",
"pulls": "Demandes de tirage" "pulls": "Demandes de tirage",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scènes", "scenes": "Scènes",
@ -1024,11 +1025,22 @@
"timeleft": "Temps restant" "timeleft": "Temps restant"
}, },
"hoarder": { "hoarder": {
"bookmarks": "Bookmarks", "bookmarks": "Marque-pages",
"favorites": "Favorites", "favorites": "Favoris",
"archived": "Archived", "archived": "Archivé",
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Listes",
"tags": "Étiquettes" "tags": "Étiquettes"
},
"slskd": {
"slskStatus": "Réseau",
"connected": "Connecté",
"disconnected": "Déconnecté",
"updateStatus": "Mise à jour",
"update_yes": "Disponible",
"update_no": "À jour",
"downloads": "Téléchargements",
"uploads": "Téléversements",
"sharedFiles": "Fichiers"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "זמין",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "Available",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Obavijesti", "notifications": "Obavijesti",
"issues": "Problemi", "issues": "Problemi",
"pulls": "Zahtjevi za povlačenje" "pulls": "Zahtjevi za povlačenje",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scene", "scenes": "Scene",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Oznake" "tags": "Oznake"
},
"slskd": {
"slskStatus": "Mreža",
"connected": "Povezano",
"disconnected": "Odspojeno",
"updateStatus": "Update",
"update_yes": "Dostupno",
"update_no": "Aktualno",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Datoteke"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Üzenetek", "notifications": "Üzenetek",
"issues": "Problémák", "issues": "Problémák",
"pulls": "Pull request-ek" "pulls": "Pull request-ek",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Jelenetek", "scenes": "Jelenetek",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Címkék" "tags": "Címkék"
},
"slskd": {
"slskStatus": "Hálózat",
"connected": "Csatlakozva",
"disconnected": "Kapcsolat bontva",
"updateStatus": "Update",
"update_yes": "Elérhető",
"update_no": "Naprakész",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Fájlok"
} }
} }

View File

@ -342,7 +342,7 @@
"totalNxDomain": "Domain NX", "totalNxDomain": "Domain NX",
"totalRefused": "Ditolak", "totalRefused": "Ditolak",
"totalAuthoritative": "Authoritative", "totalAuthoritative": "Authoritative",
"totalRecursive": "Recursive", "totalRecursive": "Rekursif",
"totalCached": "Cached", "totalCached": "Cached",
"totalBlocked": "Terblokir", "totalBlocked": "Terblokir",
"totalDropped": "Dropped", "totalDropped": "Dropped",
@ -705,8 +705,8 @@
"time": "Waktu" "time": "Waktu"
}, },
"firefly": { "firefly": {
"networth": "Net Worth", "networth": "Kekayaan Bersih",
"budget": "Budget" "budget": "Anggaran"
}, },
"grafana": { "grafana": {
"dashboards": "Dasbor", "dashboards": "Dasbor",
@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifikasi", "notifications": "Notifikasi",
"issues": "Isu", "issues": "Isu",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Adegan", "scenes": "Adegan",
@ -965,7 +966,7 @@
"lubelogger": { "lubelogger": {
"vehicle": "Kendaraan", "vehicle": "Kendaraan",
"vehicles": "Kendaraan", "vehicles": "Kendaraan",
"serviceRecords": "Service Records", "serviceRecords": "Catatan Servis",
"reminders": "Pengingat", "reminders": "Pengingat",
"nextReminder": "Pengingat Berikutnya", "nextReminder": "Pengingat Berikutnya",
"none": "Tidak ada" "none": "Tidak ada"
@ -1024,11 +1025,22 @@
"timeleft": "Sisa Waktu" "timeleft": "Sisa Waktu"
}, },
"hoarder": { "hoarder": {
"bookmarks": "Bookmarks", "bookmarks": "Markah",
"favorites": "Favorites", "favorites": "Favorit",
"archived": "Archived", "archived": "Diarsipkan",
"highlights": "Highlights", "highlights": "Sorotan",
"lists": "Lists", "lists": "Daftar",
"tags": "Tag" "tags": "Tag"
},
"slskd": {
"slskStatus": "Jaringan",
"connected": "Tersambung",
"disconnected": "Terputus",
"updateStatus": "Update",
"update_yes": "Tersedia",
"update_no": "Terbaru",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "File"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifiche", "notifications": "Notifiche",
"issues": "Problemi", "issues": "Problemi",
"pulls": "Richieste di Pull" "pulls": "Richieste di Pull",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scene", "scenes": "Scene",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tag" "tags": "Tag"
},
"slskd": {
"slskStatus": "Rete",
"connected": "Connesso",
"disconnected": "Disconnesso",
"updateStatus": "Update",
"update_yes": "Disponibili",
"update_no": "Aggiornato",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "File"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "通知", "notifications": "通知",
"issues": "課題", "issues": "課題",
"pulls": "プルリクエスト" "pulls": "プルリクエスト",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "シーン", "scenes": "シーン",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "タグ" "tags": "タグ"
},
"slskd": {
"slskStatus": "ネットワーク",
"connected": "接続済",
"disconnected": "切断されました",
"updateStatus": "Update",
"update_yes": "利用可",
"update_no": "最新",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "ファイル"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "알림", "notifications": "알림",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "장면", "scenes": "장면",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "태그" "tags": "태그"
},
"slskd": {
"slskStatus": "네트워크",
"connected": "연결됨",
"disconnected": "연결 끊김",
"updateStatus": "Update",
"update_yes": "이용 가능",
"update_no": "최신 상태",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "파일"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "Available",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Adegan", "scenes": "Adegan",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tanda nama" "tags": "Tanda nama"
},
"slskd": {
"slskStatus": "Rangkaian",
"connected": "Connected",
"disconnected": "Sambungan Terputus",
"updateStatus": "Update",
"update_yes": "Sudah Ada",
"update_no": "Terkemaskini",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notificaties", "notifications": "Notificaties",
"issues": "Problemen", "issues": "Problemen",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scènes", "scenes": "Scènes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Label" "tags": "Label"
},
"slskd": {
"slskStatus": "Netwerk",
"connected": "Verbonden",
"disconnected": "Verbinding verbroken",
"updateStatus": "Update",
"update_yes": "Beschikbaar",
"update_no": "Bijgewerkt",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Bestanden"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Varslinger", "notifications": "Varslinger",
"issues": "Issues", "issues": "Issues",
"pulls": "Forespørsel" "pulls": "Forespørsel",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scener", "scenes": "Scener",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Stikkord" "tags": "Stikkord"
},
"slskd": {
"slskStatus": "Nettverk",
"connected": "Tilkoblet",
"disconnected": "Frakoblet",
"updateStatus": "Update",
"update_yes": "Tilgjengelig",
"update_no": "Oppdatert",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Powiadomienia", "notifications": "Powiadomienia",
"issues": "Zgłoszenia", "issues": "Zgłoszenia",
"pulls": "Żądania Pull" "pulls": "Żądania Pull",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Sceny", "scenes": "Sceny",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tagi" "tags": "Tagi"
},
"slskd": {
"slskStatus": "Sieć",
"connected": "Połączono",
"disconnected": "Rozłączono",
"updateStatus": "Update",
"update_yes": "Dostępne",
"update_no": "Aktualny",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Pliki"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notificações", "notifications": "Notificações",
"issues": "Problemas", "issues": "Problemas",
"pulls": "Solicitar pull" "pulls": "Solicitar pull",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Cenas", "scenes": "Cenas",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Etiquetas" "tags": "Etiquetas"
},
"slskd": {
"slskStatus": "Rede",
"connected": "Conectado",
"disconnected": "Desconectado",
"updateStatus": "Update",
"update_yes": "Disponível",
"update_no": "Atualizado",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Ficheiros"
} }
} }

View File

@ -47,7 +47,7 @@
"load": "Carga", "load": "Carga",
"temp": "TEMP", "temp": "TEMP",
"max": "Máximo", "max": "Máximo",
"uptime": "CIMA" "uptime": "ATIVO"
}, },
"unifi": { "unifi": {
"users": "Usuários", "users": "Usuários",
@ -61,7 +61,7 @@
"wlan_devices": "Dispositivos WLAN", "wlan_devices": "Dispositivos WLAN",
"lan_users": "Usuários de LAN", "lan_users": "Usuários de LAN",
"wlan_users": "Usuários de WLAN", "wlan_users": "Usuários de WLAN",
"up": "CIMA", "up": "ATIVO",
"down": "Desligado", "down": "Desligado",
"wait": "Por favor, aguarde", "wait": "Por favor, aguarde",
"empty_data": "Status do Subsistema desconhecido" "empty_data": "Status do Subsistema desconhecido"
@ -149,8 +149,8 @@
"received": "Recebido", "received": "Recebido",
"sent": "Enviado", "sent": "Enviado",
"externalIPAddress": "IP Externo", "externalIPAddress": "IP Externo",
"externalIPv6Address": "Ext. IPv6", "externalIPv6Address": "IPv6 Externo",
"externalIPv6Prefix": "Ext. IPv6-Prefix" "externalIPv6Prefix": "Prefixo IPv6 Externo"
}, },
"caddy": { "caddy": {
"upstreams": "Streams de Envio", "upstreams": "Streams de Envio",
@ -178,7 +178,7 @@
"connectedAp": "APs Ligados", "connectedAp": "APs Ligados",
"activeUser": "Dispositivos ativos", "activeUser": "Dispositivos ativos",
"alerts": "Alertas", "alerts": "Alertas",
"connectedGateways": "Connected gateways", "connectedGateways": "Gateways conectados",
"connectedSwitches": "Switches conectados" "connectedSwitches": "Switches conectados"
}, },
"nzbget": { "nzbget": {
@ -229,8 +229,8 @@
"seed": "Semente" "seed": "Semente"
}, },
"develancacheui": { "develancacheui": {
"cachehitbytes": "Cache Hit Bytes", "cachehitbytes": "Bytes de Acerto de Cache",
"cachemissbytes": "Cache Miss Bytes" "cachemissbytes": "Bytes de Falha de Cache"
}, },
"downloadstation": { "downloadstation": {
"download": "Descarregar", "download": "Descarregar",
@ -313,13 +313,13 @@
}, },
"suwayomi": { "suwayomi": {
"download": "Baixado", "download": "Baixado",
"nondownload": "Non-Downloaded", "nondownload": "Não Baixado",
"read": "Lido", "read": "Lido",
"unread": "Não lida", "unread": "Não lida",
"downloadedread": "Downloaded & Read", "downloadedread": "Baixado e Lido",
"downloadedunread": "Downloaded & Unread", "downloadedunread": "Baixado e Não Lido",
"nondownloadedread": "Non-Downloaded & Read", "nondownloadedread": "Não Baixado e Lido",
"nondownloadedunread": "Non-Downloaded & Unread" "nondownloadedunread": "Não Baixado e Não Lido"
}, },
"tailscale": { "tailscale": {
"address": "Endereço", "address": "Endereço",
@ -337,15 +337,15 @@
}, },
"technitium": { "technitium": {
"totalQueries": "Consultas", "totalQueries": "Consultas",
"totalNoError": "Success", "totalNoError": "Sucesso",
"totalServerFailure": "Failures", "totalServerFailure": "Falhas",
"totalNxDomain": "NX Domains", "totalNxDomain": "Domínios NX",
"totalRefused": "Refused", "totalRefused": "Recusado",
"totalAuthoritative": "Authoritative", "totalAuthoritative": "Autoritativo",
"totalRecursive": "Recursive", "totalRecursive": "Recursivo",
"totalCached": "Cached", "totalCached": "Em cache",
"totalBlocked": "Bloqueado", "totalBlocked": "Bloqueado",
"totalDropped": "Dropped", "totalDropped": "Perdidos",
"totalClients": "Clientes" "totalClients": "Clientes"
}, },
"tdarr": { "tdarr": {
@ -436,7 +436,7 @@
"temp": "TEMP", "temp": "TEMP",
"_temp": "Temperatura", "_temp": "Temperatura",
"warn": "Aviso", "warn": "Aviso",
"uptime": "CIMA", "uptime": "ATIVO",
"total": "Total", "total": "Total",
"free": "Livre", "free": "Livre",
"used": "Utilizado", "used": "Utilizado",
@ -705,8 +705,8 @@
"time": "Hora" "time": "Hora"
}, },
"firefly": { "firefly": {
"networth": "Net Worth", "networth": "Valor Líquido",
"budget": "Budget" "budget": "Orçamento"
}, },
"grafana": { "grafana": {
"dashboards": "Painéis", "dashboards": "Painéis",
@ -860,16 +860,16 @@
}, },
"romm": { "romm": {
"platforms": "Plataformas", "platforms": "Plataformas",
"totalRoms": "Games", "totalRoms": "Jogos",
"saves": "Saves", "saves": "Saves",
"states": "States", "states": "Estados",
"screenshots": "Screenshots", "screenshots": "Capturas de Tela",
"totalfilesize": "Total Size" "totalfilesize": "Tamanho total"
}, },
"mailcow": { "mailcow": {
"domains": "Domínios", "domains": "Domínios",
"mailboxes": "Mailboxes", "mailboxes": "Caixas de e-mail",
"mails": "Mails", "mails": "Mensagens",
"storage": "Armazenamento" "storage": "Armazenamento"
}, },
"netdata": { "netdata": {
@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notificações", "notifications": "Notificações",
"issues": "Problemas", "issues": "Problemas",
"pulls": "Solicitações de Envio" "pulls": "Solicitações de Envio",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Cenas", "scenes": "Cenas",
@ -951,30 +952,30 @@
}, },
"linkwarden": { "linkwarden": {
"links": "Links", "links": "Links",
"collections": "Collections", "collections": "Coleções",
"tags": "Marcadores" "tags": "Marcadores"
}, },
"zabbix": { "zabbix": {
"unclassified": "Not classified", "unclassified": "Não classificado",
"information": "Informação", "information": "Informação",
"warning": "Warning", "warning": "Aviso",
"average": "Average", "average": "Médio",
"high": "High", "high": "Alto",
"disaster": "Disaster" "disaster": "Desastre"
}, },
"lubelogger": { "lubelogger": {
"vehicle": "Vehicle", "vehicle": "Veículo",
"vehicles": "Vehicles", "vehicles": "Veículos",
"serviceRecords": "Service Records", "serviceRecords": "Registros de Serviço",
"reminders": "Reminders", "reminders": "Lembretes",
"nextReminder": "Next Reminder", "nextReminder": "Próximo Lembrete",
"none": "None" "none": "Nenhum"
}, },
"vikunja": { "vikunja": {
"projects": "Active Projects", "projects": "Projetos Ativos",
"tasks7d": "Tasks Due This Week", "tasks7d": "Tarefas que vencem nesta semana",
"tasksOverdue": "Overdue Tasks", "tasksOverdue": "Tarefas Atrasadas",
"tasksInProgress": "Tasks In Progress" "tasksInProgress": "Tarefas em Andamento"
}, },
"headscale": { "headscale": {
"name": "Nome", "name": "Nome",
@ -986,7 +987,7 @@
}, },
"beszel": { "beszel": {
"name": "Nome", "name": "Nome",
"systems": "Systems", "systems": "Sistemas",
"up": "Ativo", "up": "Ativo",
"down": "Inativo", "down": "Inativo",
"paused": "Pausado", "paused": "Pausado",
@ -995,27 +996,27 @@
"updated": "Atualizado", "updated": "Atualizado",
"cpu": "CPU", "cpu": "CPU",
"memory": "MEM", "memory": "MEM",
"disk": "Disk", "disk": "Disco",
"network": "NET" "network": "Rede"
}, },
"argocd": { "argocd": {
"apps": "Apps", "apps": "Aplicativos",
"synced": "Synced", "synced": "Sincronizado",
"outOfSync": "Out Of Sync", "outOfSync": "Fora de sincronia",
"healthy": "Saudável", "healthy": "Saudável",
"degraded": "Degraded", "degraded": "Degradado",
"progressing": "Progressing", "progressing": "Progredindo",
"missing": "Faltando", "missing": "Faltando",
"suspended": "Suspended" "suspended": "Suspenso"
}, },
"spoolman": { "spoolman": {
"loading": "Carregando" "loading": "Carregando"
}, },
"gitlab": { "gitlab": {
"groups": "Groups", "groups": "Grupos",
"issues": "Problemas", "issues": "Problemas",
"merges": "Merge Requests", "merges": "Solicitações de mesclagem",
"projects": "Projects" "projects": "Projetos"
}, },
"apcups": { "apcups": {
"status": "Status", "status": "Status",
@ -1024,11 +1025,22 @@
"timeleft": "Tempo restante" "timeleft": "Tempo restante"
}, },
"hoarder": { "hoarder": {
"bookmarks": "Bookmarks", "bookmarks": "Favoritos",
"favorites": "Favorites", "favorites": "Favoritos",
"archived": "Archived", "archived": "Arquivados",
"highlights": "Highlights", "highlights": "Destaques",
"lists": "Lists", "lists": "Listas",
"tags": "Marcadores" "tags": "Marcadores"
},
"slskd": {
"slskStatus": "Rede",
"connected": "Conectado",
"disconnected": "Desconectado",
"updateStatus": "Update",
"update_yes": "Disponível",
"update_no": "Atualizado",
"downloads": "Transferências",
"uploads": "Envios",
"sharedFiles": "Arquivos"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "Disponibile",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -149,8 +149,8 @@
"received": "Получено", "received": "Получено",
"sent": "Отправлено", "sent": "Отправлено",
"externalIPAddress": "Внеш. IP", "externalIPAddress": "Внеш. IP",
"externalIPv6Address": "Ext. IPv6", "externalIPv6Address": "Внешний IPv6",
"externalIPv6Prefix": "Ext. IPv6-Prefix" "externalIPv6Prefix": "Внешний IPv6 префикс"
}, },
"caddy": { "caddy": {
"upstreams": "Входящие каналы", "upstreams": "Входящие каналы",
@ -178,7 +178,7 @@
"connectedAp": "Подключенные точки доступа", "connectedAp": "Подключенные точки доступа",
"activeUser": "Активные устройства", "activeUser": "Активные устройства",
"alerts": "Предупреждения", "alerts": "Предупреждения",
"connectedGateways": "Connected gateways", "connectedGateways": "Подключенные шлюзы",
"connectedSwitches": "Подключенные коммутаторы" "connectedSwitches": "Подключенные коммутаторы"
}, },
"nzbget": { "nzbget": {
@ -705,8 +705,8 @@
"time": "Время" "time": "Время"
}, },
"firefly": { "firefly": {
"networth": "Net Worth", "networth": "Общая средства",
"budget": "Budget" "budget": "Бюджет"
}, },
"grafana": { "grafana": {
"dashboards": "Панели", "dashboards": "Панели",
@ -861,7 +861,7 @@
"romm": { "romm": {
"platforms": "Платформы", "platforms": "Платформы",
"totalRoms": "Игры", "totalRoms": "Игры",
"saves": "Сейвы", "saves": "Сохранения",
"states": "Состояния", "states": "Состояния",
"screenshots": "Скриншоты", "screenshots": "Скриншоты",
"totalfilesize": "Общий объем" "totalfilesize": "Общий объем"
@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Уведомления", "notifications": "Уведомления",
"issues": "Вопросы", "issues": "Вопросы",
"pulls": "Запросы на слияние (Pull Request)" "pulls": "Запросы на слияние (Pull Request)",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Сцены", "scenes": "Сцены",
@ -927,7 +928,7 @@
"total": "Всего" "total": "Всего"
}, },
"swagdashboard": { "swagdashboard": {
"proxied": "Прокси", "proxied": "Проксировано",
"auth": "С Авторизацией", "auth": "С Авторизацией",
"outdated": "Устаревшие", "outdated": "Устаревшие",
"banned": "Заблокированные" "banned": "Заблокированные"
@ -958,17 +959,17 @@
"unclassified": "Не классифицировано", "unclassified": "Не классифицировано",
"information": "Информация", "information": "Информация",
"warning": "Предупреждение", "warning": "Предупреждение",
"average": "Средняя", "average": "Среднее",
"high": "Высокая", "high": "Высокая",
"disaster": "Чрезвычайная" "disaster": "Чрезвычайное"
}, },
"lubelogger": { "lubelogger": {
"vehicle": "Автомобиль", "vehicle": "Транспорт",
"vehicles": "Автомобили", "vehicles": "Транспорты",
"serviceRecords": "Сервисные работы", "serviceRecords": "Сервисные записи",
"reminders": "Напоминания", "reminders": "Напоминания",
"nextReminder": "Следующее напоминание", "nextReminder": "Следующее напоминание",
"none": "Нет" "none": "Отсутствует"
}, },
"vikunja": { "vikunja": {
"projects": "Активные Проекты", "projects": "Активные Проекты",
@ -1024,11 +1025,22 @@
"timeleft": "Осталось" "timeleft": "Осталось"
}, },
"hoarder": { "hoarder": {
"bookmarks": "Bookmarks", "bookmarks": "Закладки",
"favorites": "Favorites", "favorites": "Избранное",
"archived": "Archived", "archived": "Архивированное",
"highlights": "Highlights", "highlights": "События",
"lists": "Lists", "lists": "Список",
"tags": "Теги" "tags": "Теги"
},
"slskd": {
"slskStatus": "Сеть",
"connected": "Подключено",
"disconnected": "Отключено",
"updateStatus": "Обновление",
"update_yes": "Доступно",
"update_no": "Последняя версия",
"downloads": "Скачивания",
"uploads": "Загрузки",
"sharedFiles": "Файлов"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Oznámenia", "notifications": "Oznámenia",
"issues": "Problémy", "issues": "Problémy",
"pulls": "Pull requesty" "pulls": "Pull requesty",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scény", "scenes": "Scény",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Štítky" "tags": "Štítky"
},
"slskd": {
"slskStatus": "Sieť",
"connected": "Pripojené",
"disconnected": "Odpojené",
"updateStatus": "Update",
"update_yes": "Dostupné",
"update_no": "Aktuálny",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Súborov"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Obvestila", "notifications": "Obvestila",
"issues": "Težave", "issues": "Težave",
"pulls": "Zahteve za prenos" "pulls": "Zahteve za prenos",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scene", "scenes": "Scene",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Značke" "tags": "Značke"
},
"slskd": {
"slskStatus": "Omrežje",
"connected": "Povezan",
"disconnected": "Prekinjeno",
"updateStatus": "Update",
"update_yes": "Na voljo",
"update_no": "Posodobljeno",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Datotek"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "Available",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "Tillgänglig",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "అందుబాటులో వున్నవి",
"update_no": "తాజాగా",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "Available",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Bildirimler", "notifications": "Bildirimler",
"issues": "Sorunlar", "issues": "Sorunlar",
"pulls": "Değişiklik İstekleri" "pulls": "Değişiklik İstekleri",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Sahneler", "scenes": "Sahneler",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Etiketler" "tags": "Etiketler"
},
"slskd": {
"slskStatus": "Ağ",
"connected": "Bağlandı",
"disconnected": "Bağlantı kesildi",
"updateStatus": "Update",
"update_yes": "Kullanılabilir",
"update_no": "Güncel",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Dosyalar"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Сповіщення", "notifications": "Сповіщення",
"issues": "Питання", "issues": "Питання",
"pulls": "Pull-запити" "pulls": "Pull-запити",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Сцени", "scenes": "Сцени",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Теги" "tags": "Теги"
},
"slskd": {
"slskStatus": "Мережа",
"connected": "З'єднано",
"disconnected": "Відключено",
"updateStatus": "Update",
"update_yes": "Доступно",
"update_no": "Актуально",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Файли"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "Notifications", "notifications": "Notifications",
"issues": "Issues", "issues": "Issues",
"pulls": "Pull Requests" "pulls": "Pull Requests",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "Scenes", "scenes": "Scenes",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "Network",
"connected": "Connected",
"disconnected": "Disconnected",
"updateStatus": "Update",
"update_yes": "Available",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "信息", "notifications": "信息",
"issues": "出版", "issues": "出版",
"pulls": "提取請求" "pulls": "提取請求",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "場景", "scenes": "場景",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "標籤" "tags": "標籤"
},
"slskd": {
"slskStatus": "網絡",
"connected": "Connected",
"disconnected": "連接已中斷",
"updateStatus": "Update",
"update_yes": "可用",
"update_no": "已更新至最新",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "檔案"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "通知", "notifications": "通知",
"issues": "问题", "issues": "问题",
"pulls": "PR" "pulls": "PR",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "场景", "scenes": "场景",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "Tags" "tags": "Tags"
},
"slskd": {
"slskStatus": "网络",
"connected": "已连接",
"disconnected": "未连接",
"updateStatus": "Update",
"update_yes": "可用",
"update_no": "Up to Date",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "Files"
} }
} }

View File

@ -885,7 +885,8 @@
"gitea": { "gitea": {
"notifications": "信息", "notifications": "信息",
"issues": "出版", "issues": "出版",
"pulls": "提取請求" "pulls": "提取請求",
"repositories": "Repositories"
}, },
"stash": { "stash": {
"scenes": "場景", "scenes": "場景",
@ -1030,5 +1031,16 @@
"highlights": "Highlights", "highlights": "Highlights",
"lists": "Lists", "lists": "Lists",
"tags": "標籤" "tags": "標籤"
},
"slskd": {
"slskStatus": "網絡",
"connected": "Connected",
"disconnected": "連接已中斷",
"updateStatus": "Update",
"update_yes": "可觀看",
"update_no": "已更新至最新",
"downloads": "Downloads",
"uploads": "Uploads",
"sharedFiles": "檔案"
} }
} }

View File

@ -12,6 +12,7 @@ export default function BookmarksGroup({
disableCollapse, disableCollapse,
groupsInitiallyCollapsed, groupsInitiallyCollapsed,
bookmarksStyle, bookmarksStyle,
maxGroupColumns,
}) { }) {
const panel = useRef(); const panel = useRef();
@ -25,6 +26,9 @@ export default function BookmarksGroup({
className={classNames( className={classNames(
"bookmark-group flex-1 overflow-hidden", "bookmark-group flex-1 overflow-hidden",
layout?.style === "row" ? "basis-full" : "basis-full md:basis-1/4 lg:basis-1/5 xl:basis-1/6", layout?.style === "row" ? "basis-full" : "basis-full md:basis-1/4 lg:basis-1/5 xl:basis-1/6",
layout?.style !== "row" && maxGroupColumns && parseInt(maxGroupColumns, 10) > 6
? `3xl:basis-1/${maxGroupColumns}`
: "",
layout?.header === false ? "px-1" : "p-1 pb-0", layout?.header === false ? "px-1" : "p-1 pb-0",
)} )}
> >

View File

@ -10,7 +10,7 @@ import { columnMap } from "../../utils/layout/columns";
export default function ServicesGroup({ export default function ServicesGroup({
group, group,
layout, layout,
fiveColumns, maxGroupColumns,
disableCollapse, disableCollapse,
useEqualHeights, useEqualHeights,
groupsInitiallyCollapsed, groupsInitiallyCollapsed,
@ -31,7 +31,7 @@ export default function ServicesGroup({
className={classNames( className={classNames(
"services-group flex-1", "services-group flex-1",
layout?.style === "row" ? "basis-full" : "basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4", layout?.style === "row" ? "basis-full" : "basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4",
layout?.style !== "row" && fiveColumns ? "3xl:basis-1/5" : "", layout?.style !== "row" && maxGroupColumns ? `3xl:basis-1/${maxGroupColumns}` : "",
groupPadding, groupPadding,
isSubgroup ? "subgroup" : "", isSubgroup ? "subgroup" : "",
)} )}
@ -97,7 +97,7 @@ export default function ServicesGroup({
key={subgroup.name} key={subgroup.name}
group={subgroup} group={subgroup}
layout={layout?.[subgroup.name]} layout={layout?.[subgroup.name]}
fiveColumns={fiveColumns} maxGroupColumns={maxGroupColumns}
disableCollapse={disableCollapse} disableCollapse={disableCollapse}
useEqualHeights={useEqualHeights} useEqualHeights={useEqualHeights}
groupsInitiallyCollapsed={groupsInitiallyCollapsed} groupsInitiallyCollapsed={groupsInitiallyCollapsed}

View File

@ -16,6 +16,7 @@ import nextI18nextConfig from "../../next-i18next.config";
const tailwindSafelist = [ const tailwindSafelist = [
// TODO: remove pending https://github.com/tailwindlabs/tailwindcss/pull/17147 // TODO: remove pending https://github.com/tailwindlabs/tailwindcss/pull/17147
"backdrop-blur", "backdrop-blur",
"backdrop-blur-xs",
"backdrop-blur-sm", "backdrop-blur-sm",
"backdrop-blur-md", "backdrop-blur-md",
"backdrop-blur-xl", "backdrop-blur-xl",
@ -52,6 +53,11 @@ const tailwindSafelist = [
"dark:bg-white", "dark:bg-white",
"bg-orange-400", "bg-orange-400",
"dark:bg-orange-400", "dark:bg-orange-400",
// maxGroupColumns
"3xl:basis-1/5",
"3xl:basis-1/6",
"3xl:basis-1/7",
"3xl:basis-1/8",
// yep // yep
"h-0 h-1 h-2 h-3 h-4 h-5 h-6 h-7 h-8 h-9 h-10 h-11 h-12 h-13 h-14 h-15 h-16 h-17 h-18 h-19 h-20 h-21 h-22 h-23 h-24 h-25 h-26 h-27 h-28 h-29 h-30 h-31 h-32 h-33 h-34 h-35 h-36 h-37 h-38 h-39 h-40 h-41 h-42 h-43 h-44 h-45 h-46 h-47 h-48 h-49 h-50 h-51 h-52 h-53 h-54 h-55 h-56 h-57 h-58 h-59 h-60 h-61 h-62 h-63 h-64 h-65 h-66 h-67 h-68 h-69 h-70 h-71 h-72 h-73 h-74 h-75 h-76 h-77 h-78 h-79 h-80 h-81 h-82 h-83 h-84 h-85 h-86 h-87 h-88 h-89 h-90 h-91 h-92 h-93 h-94 h-95 h-96", "h-0 h-1 h-2 h-3 h-4 h-5 h-6 h-7 h-8 h-9 h-10 h-11 h-12 h-13 h-14 h-15 h-16 h-17 h-18 h-19 h-20 h-21 h-22 h-23 h-24 h-25 h-26 h-27 h-28 h-29 h-30 h-31 h-32 h-33 h-34 h-35 h-36 h-37 h-38 h-39 h-40 h-41 h-42 h-43 h-44 h-45 h-46 h-47 h-48 h-49 h-50 h-51 h-52 h-53 h-54 h-55 h-56 h-57 h-58 h-59 h-60 h-61 h-62 h-63 h-64 h-65 h-66 h-67 h-68 h-69 h-70 h-71 h-72 h-73 h-74 h-75 h-76 h-77 h-78 h-79 h-80 h-81 h-82 h-83 h-84 h-85 h-86 h-87 h-88 h-89 h-90 h-91 h-92 h-93 h-94 h-95 h-96",
"sm:h-0 sm:h-1 sm:h-2 sm:h-3 sm:h-4 sm:h-5 sm:h-6 sm:h-7 sm:h-8 sm:h-9 sm:h-10 sm:h-11 sm:h-12 sm:h-13 sm:h-14 sm:h-15 sm:h-16 sm:h-17 sm:h-18 sm:h-19 sm:h-20 sm:h-21 sm:h-22 sm:h-23 sm:h-24 sm:h-25 sm:h-26 sm:h-27 sm:h-28 sm:h-29 sm:h-30 sm:h-31 sm:h-32 sm:h-33 sm:h-34 sm:h-35 sm:h-36 sm:h-37 sm:h-38 sm:h-39 sm:h-40 sm:h-41 sm:h-42 sm:h-43 sm:h-44 sm:h-45 sm:h-46 sm:h-47 sm:h-48 sm:h-49 sm:h-50 sm:h-51 sm:h-52 sm:h-53 sm:h-54 sm:h-55 sm:h-56 sm:h-57 sm:h-58 sm:h-59 sm:h-60 sm:h-61 sm:h-62 sm:h-63 sm:h-64 sm:h-65 sm:h-66 sm:h-67 sm:h-68 sm:h-69 sm:h-70 sm:h-71 sm:h-72 sm:h-73 sm:h-74 sm:h-75 sm:h-76 sm:h-77 sm:h-78 sm:h-79 sm:h-80 sm:h-81 sm:h-82 sm:h-83 sm:h-84 sm:h-85 sm:h-86 sm:h-87 sm:h-88 sm:h-89 sm:h-90 sm:h-91 sm:h-92 sm:h-93 sm:h-94 sm:h-95 sm:h-96", "sm:h-0 sm:h-1 sm:h-2 sm:h-3 sm:h-4 sm:h-5 sm:h-6 sm:h-7 sm:h-8 sm:h-9 sm:h-10 sm:h-11 sm:h-12 sm:h-13 sm:h-14 sm:h-15 sm:h-16 sm:h-17 sm:h-18 sm:h-19 sm:h-20 sm:h-21 sm:h-22 sm:h-23 sm:h-24 sm:h-25 sm:h-26 sm:h-27 sm:h-28 sm:h-29 sm:h-30 sm:h-31 sm:h-32 sm:h-33 sm:h-34 sm:h-35 sm:h-36 sm:h-37 sm:h-38 sm:h-39 sm:h-40 sm:h-41 sm:h-42 sm:h-43 sm:h-44 sm:h-45 sm:h-46 sm:h-47 sm:h-48 sm:h-49 sm:h-50 sm:h-51 sm:h-52 sm:h-53 sm:h-54 sm:h-55 sm:h-56 sm:h-57 sm:h-58 sm:h-59 sm:h-60 sm:h-61 sm:h-62 sm:h-63 sm:h-64 sm:h-65 sm:h-66 sm:h-67 sm:h-68 sm:h-69 sm:h-70 sm:h-71 sm:h-72 sm:h-73 sm:h-74 sm:h-75 sm:h-76 sm:h-77 sm:h-78 sm:h-79 sm:h-80 sm:h-81 sm:h-82 sm:h-83 sm:h-84 sm:h-85 sm:h-86 sm:h-87 sm:h-88 sm:h-89 sm:h-90 sm:h-91 sm:h-92 sm:h-93 sm:h-94 sm:h-95 sm:h-96",

View File

@ -1,4 +1,4 @@
import cachedFetch from "utils/proxy/cached-fetch"; import { cachedRequest } from "utils/proxy/http";
import createLogger from "utils/logger"; import createLogger from "utils/logger";
const logger = createLogger("releases"); const logger = createLogger("releases");
@ -6,7 +6,7 @@ const logger = createLogger("releases");
export default async function handler(req, res) { export default async function handler(req, res) {
const releasesURL = "https://api.github.com/repos/gethomepage/homepage/releases"; const releasesURL = "https://api.github.com/repos/gethomepage/homepage/releases";
try { try {
return res.send(await cachedFetch(releasesURL, 5)); return res.send(await cachedRequest(releasesURL, 5));
} catch (e) { } catch (e) {
logger.error(`Error checking GitHub releases: ${e}`); logger.error(`Error checking GitHub releases: ${e}`);
return res.send([]); return res.send([]);

View File

@ -1,7 +1,7 @@
import { searchProviders } from "components/widgets/search/search"; import { searchProviders } from "components/widgets/search/search";
import { getSettings } from "utils/config/config"; import { getSettings } from "utils/config/config";
import cachedFetch from "utils/proxy/cached-fetch"; import { cachedRequest } from "utils/proxy/http";
import { widgetsFromConfig } from "utils/config/widget-helpers"; import { widgetsFromConfig } from "utils/config/widget-helpers";
export default async function handler(req, res) { export default async function handler(req, res) {
@ -29,5 +29,5 @@ export default async function handler(req, res) {
return res.json([query, []]); // Responde with the same array format but with no suggestions. return res.json([query, []]); // Responde with the same array format but with no suggestions.
} }
return res.send(await cachedFetch(`${provider.suggestionUrl}${encodeURIComponent(query)}`, 5, "Mozilla/5.0")); return res.send(await cachedRequest(`${provider.suggestionUrl}${encodeURIComponent(query)}`, 5, "Mozilla/5.0"));
} }

View File

@ -1,9 +1,9 @@
import cachedFetch from "utils/proxy/cached-fetch"; import { cachedRequest } from "utils/proxy/http";
export default async function handler(req, res) { export default async function handler(req, res) {
const { latitude, longitude, units, cache, timezone } = req.query; const { latitude, longitude, units, cache, timezone } = req.query;
const degrees = units === "metric" ? "celsius" : "fahrenheit"; const degrees = units === "metric" ? "celsius" : "fahrenheit";
const timezeone = timezone ?? "auto"; const timezeone = timezone ?? "auto";
const apiUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=sunrise,sunset&current_weather=true&temperature_unit=${degrees}&timezone=${timezeone}`; const apiUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&daily=sunrise,sunset&current_weather=true&temperature_unit=${degrees}&timezone=${timezeone}`;
return res.send(await cachedFetch(apiUrl, cache)); return res.send(await cachedRequest(apiUrl, cache));
} }

View File

@ -1,4 +1,4 @@
import cachedFetch from "utils/proxy/cached-fetch"; import { cachedRequest } from "utils/proxy/http";
import { getSettings } from "utils/config/config"; import { getSettings } from "utils/config/config";
import { getPrivateWidgetOptions } from "utils/config/widget-helpers"; import { getPrivateWidgetOptions } from "utils/config/widget-helpers";
@ -26,5 +26,5 @@ export default async function handler(req, res) {
const apiUrl = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${apiKey}&units=${units}&lang=${lang}`; const apiUrl = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${apiKey}&units=${units}&lang=${lang}`;
return res.send(await cachedFetch(apiUrl, cache)); return res.send(await cachedRequest(apiUrl, cache));
} }

View File

@ -1,4 +1,4 @@
import cachedFetch from "utils/proxy/cached-fetch"; import { cachedRequest } from "utils/proxy/http";
import { getSettings } from "utils/config/config"; import { getSettings } from "utils/config/config";
import createLogger from "utils/logger"; import createLogger from "utils/logger";
@ -60,7 +60,7 @@ export default async function handler(req, res) {
const apiUrl = `https://finnhub.io/api/v1/quote?symbol=${ticker}&token=${apiKey}`; const apiUrl = `https://finnhub.io/api/v1/quote?symbol=${ticker}&token=${apiKey}`;
// Finnhub free accounts allow up to 60 calls/minute // Finnhub free accounts allow up to 60 calls/minute
// https://finnhub.io/pricing // https://finnhub.io/pricing
const { c, dp } = await cachedFetch(apiUrl, cache || 1); const { c, dp } = await cachedRequest(apiUrl, cache || 1);
logger.debug("Finnhub API response for %s: %o", ticker, { c, dp }); logger.debug("Finnhub API response for %s: %o", ticker, { c, dp });
// API sometimes returns 200, but values returned are `null` // API sometimes returns 200, but values returned are `null`

View File

@ -1,4 +1,4 @@
import cachedFetch from "utils/proxy/cached-fetch"; import { cachedRequest } from "utils/proxy/http";
import { getSettings } from "utils/config/config"; import { getSettings } from "utils/config/config";
import { getPrivateWidgetOptions } from "utils/config/widget-helpers"; import { getPrivateWidgetOptions } from "utils/config/widget-helpers";
@ -26,5 +26,5 @@ export default async function handler(req, res) {
const apiUrl = `http://api.weatherapi.com/v1/current.json?q=${latitude},${longitude}&key=${apiKey}&lang=${lang}`; const apiUrl = `http://api.weatherapi.com/v1/current.json?q=${latitude},${longitude}&key=${apiKey}&lang=${lang}`;
return res.send(await cachedFetch(apiUrl, cache)); return res.send(await cachedRequest(apiUrl, cache));
} }

View File

@ -323,7 +323,7 @@ function Home({ initialSettings }) {
key={group.name} key={group.name}
group={group} group={group}
layout={settings.layout?.[group.name]} layout={settings.layout?.[group.name]}
fiveColumns={settings.fiveColumns} maxGroupColumns={settings.fiveColumns ? 5 : settings.maxGroupColumns}
disableCollapse={settings.disableCollapse} disableCollapse={settings.disableCollapse}
useEqualHeights={settings.useEqualHeights} useEqualHeights={settings.useEqualHeights}
groupsInitiallyCollapsed={settings.groupsInitiallyCollapsed} groupsInitiallyCollapsed={settings.groupsInitiallyCollapsed}
@ -334,6 +334,7 @@ function Home({ initialSettings }) {
bookmarks={group} bookmarks={group}
layout={settings.layout?.[group.name]} layout={settings.layout?.[group.name]}
disableCollapse={settings.disableCollapse} disableCollapse={settings.disableCollapse}
maxGroupColumns={settings.maxBookmarkGroupColumns ?? settings.maxGroupColumns}
groupsInitiallyCollapsed={settings.groupsInitiallyCollapsed} groupsInitiallyCollapsed={settings.groupsInitiallyCollapsed}
/> />
), ),
@ -347,7 +348,7 @@ function Home({ initialSettings }) {
key={group.name} key={group.name}
group={group} group={group}
layout={settings.layout?.[group.name]} layout={settings.layout?.[group.name]}
fiveColumns={settings.fiveColumns} maxGroupColumns={settings.fiveColumns ? 5 : settings.maxGroupColumns}
disableCollapse={settings.disableCollapse} disableCollapse={settings.disableCollapse}
groupsInitiallyCollapsed={settings.groupsInitiallyCollapsed} groupsInitiallyCollapsed={settings.groupsInitiallyCollapsed}
/> />
@ -362,6 +363,7 @@ function Home({ initialSettings }) {
bookmarks={group} bookmarks={group}
layout={settings.layout?.[group.name]} layout={settings.layout?.[group.name]}
disableCollapse={settings.disableCollapse} disableCollapse={settings.disableCollapse}
maxGroupColumns={settings.maxBookmarkGroupColumns ?? settings.maxGroupColumns}
groupsInitiallyCollapsed={settings.groupsInitiallyCollapsed} groupsInitiallyCollapsed={settings.groupsInitiallyCollapsed}
bookmarksStyle={settings.bookmarksStyle} bookmarksStyle={settings.bookmarksStyle}
/> />
@ -377,6 +379,8 @@ function Home({ initialSettings }) {
bookmarks, bookmarks,
settings.layout, settings.layout,
settings.fiveColumns, settings.fiveColumns,
settings.maxGroupColumns,
settings.maxBookmarkGroupColumns,
settings.disableCollapse, settings.disableCollapse,
settings.useEqualHeights, settings.useEqualHeights,
settings.cardBlur, settings.cardBlur,
@ -417,7 +421,12 @@ function Home({ initialSettings }) {
<Script src="/api/config/custom.js" /> <Script src="/api/config/custom.js" />
<div className="relative container m-auto flex flex-col justify-start z-10 h-full"> <div
className={classNames(
settings.fullWidth ? "" : "container",
"relative m-auto flex flex-col justify-start z-10 h-full",
)}
>
<QuickLaunch <QuickLaunch
servicesAndBookmarks={servicesAndBookmarks} servicesAndBookmarks={servicesAndBookmarks}
searchString={searchString} searchString={searchString}

View File

@ -56,21 +56,22 @@ export async function cleanWidgetGroups(widgets) {
export async function getPrivateWidgetOptions(type, widgetIndex) { export async function getPrivateWidgetOptions(type, widgetIndex) {
const widgets = await widgetsFromConfig(); const widgets = await widgetsFromConfig();
const privateOptions = widgets.map((widget) => { const privateOptions =
const { index, url, username, password, key, apiKey } = widget.options; widgets.map((widget) => {
const { index, url, username, password, key, apiKey } = widget.options;
return { return {
type: widget.type, type: widget.type,
options: { options: {
index, index,
url, url,
username, username,
password, password,
key, key,
apiKey, apiKey,
}, },
}; };
}); }) || {};
return type !== undefined && widgetIndex !== undefined return type !== undefined && widgetIndex !== undefined
? privateOptions.find((o) => o.type === type && o.options.index === parseInt(widgetIndex, 10))?.options ? privateOptions.find((o) => o.type === type && o.options.index === parseInt(widgetIndex, 10))?.options

View File

@ -1,25 +0,0 @@
import cache from "memory-cache";
const defaultDuration = 5;
export default async function cachedFetch(url, duration, ua) {
const cached = cache.get(url);
// eslint-disable-next-line no-param-reassign
duration = duration || defaultDuration;
if (cached) {
return cached;
}
// wrapping text in JSON.parse to handle utf-8 issues
const options = {};
if (ua) {
options.headers = {
"User-Agent": ua,
};
}
const data = await fetch(url, options).then((res) => res.json());
cache.put(url, data, duration * 1000 * 60);
return data;
}

View File

@ -3,6 +3,7 @@
import { createUnzip, constants as zlibConstants } from "node:zlib"; import { createUnzip, constants as zlibConstants } from "node:zlib";
import { http, https } from "follow-redirects"; import { http, https } from "follow-redirects";
import cache from "memory-cache";
import { addCookieToJar, setCookieHeader } from "./cookie-jar"; import { addCookieToJar, setCookieHeader } from "./cookie-jar";
import { sanitizeErrorURL } from "./api-helpers"; import { sanitizeErrorURL } from "./api-helpers";
@ -81,20 +82,46 @@ export function httpRequest(url, params) {
return handleRequest(http, url, params); return handleRequest(http, url, params);
} }
export async function cachedRequest(url, duration = 5, ua = "homepage") {
const cached = cache.get(url);
if (cached) {
return cached;
}
const options = {
headers: {
"User-Agent": ua,
Accept: "application/json",
},
};
let [, , data] = await httpProxy(url, options);
if (Buffer.isBuffer(data)) {
try {
data = JSON.parse(Buffer.from(data).toString());
} catch (e) {
logger.debug("Error parsing cachedRequest data for %s: %s %s", url, Buffer.from(data).toString(), e);
data = Buffer.from(data).toString();
}
}
cache.put(url, data, duration * 1000 * 60);
return data;
}
export async function httpProxy(url, params = {}) { export async function httpProxy(url, params = {}) {
const constructedUrl = new URL(url); const constructedUrl = new URL(url);
const disableIpv6 = process.env.HOMEPAGE_PROXY_DISABLE_IPV6 === "true";
const agentOptions = disableIpv6 ? { family: 4, autoSelectFamily: false } : {};
let request = null; let request = null;
if (constructedUrl.protocol === "https:") { if (constructedUrl.protocol === "https:") {
request = httpsRequest(constructedUrl, { request = httpsRequest(constructedUrl, {
agent: new https.Agent({ agent: new https.Agent({ ...agentOptions, rejectUnauthorized: false }),
rejectUnauthorized: false,
}),
...params, ...params,
}); });
} else { } else {
request = httpRequest(constructedUrl, { request = httpRequest(constructedUrl, {
agent: new http.Agent(), agent: new http.Agent(agentOptions),
...params, ...params,
}); });
} }

View File

@ -113,6 +113,7 @@ const components = {
rutorrent: dynamic(() => import("./rutorrent/component")), rutorrent: dynamic(() => import("./rutorrent/component")),
sabnzbd: dynamic(() => import("./sabnzbd/component")), sabnzbd: dynamic(() => import("./sabnzbd/component")),
scrutiny: dynamic(() => import("./scrutiny/component")), scrutiny: dynamic(() => import("./scrutiny/component")),
slskd: dynamic(() => import("./slskd/component")),
sonarr: dynamic(() => import("./sonarr/component")), sonarr: dynamic(() => import("./sonarr/component")),
speedtest: dynamic(() => import("./speedtest/component")), speedtest: dynamic(() => import("./speedtest/component")),
spoolman: dynamic(() => import("./spoolman/component")), spoolman: dynamic(() => import("./spoolman/component")),

View File

@ -1,8 +1,10 @@
import { useTranslation } from "next-i18next"; import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container"; import Container from "components/services/widget/container";
import Block from "components/services/widget/block"; import Block from "components/services/widget/block";
import classNames from "classnames";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
import * as shvl from "utils/config/shvl";
function getValue(field, data) { function getValue(field, data) {
let value = data; let value = data;
@ -14,6 +16,11 @@ function getValue(field, data) {
return value; return value;
} }
// shvl is easier, everything else is kept for backwards compatibility.
if (typeof field === "string") {
return shvl.get(data, field, null);
}
while (typeof lastField === "object") { while (typeof lastField === "object") {
key = Object.keys(lastField)[0] ?? null; key = Object.keys(lastField)[0] ?? null;
@ -165,6 +172,16 @@ export default function Component({ service }) {
if (!customData) { if (!customData) {
switch (display) { switch (display) {
case "dynamic-list":
return (
<Container service={service}>
<div className="flex flex-col w-full">
<div className="bg-theme-200/50 dark:bg-theme-900/20 rounded-sm m-1 flex-1 flex flex-row items-center justify-between p-1 text-xs animate-pulse">
<div className="font-thin pl-2">Loading...</div>
</div>
</div>
</Container>
);
case "list": case "list":
return ( return (
<Container service={service}> <Container service={service}>
@ -196,6 +213,68 @@ export default function Component({ service }) {
} }
switch (display) { switch (display) {
case "dynamic-list":
let listItems = customData;
if (mappings.items) listItems = shvl.get(customData, mappings.items, null);
let error;
if (!listItems || !Array.isArray(listItems)) {
error = { message: "Unable to find items" };
}
const name = mappings.name;
const label = mappings.label;
if (!name || !label) {
error = { message: "Name and label properties are required" };
}
if (error) {
return <Container service={service} error={error}></Container>;
}
const target = mappings.target;
if (mappings.limit && parseInt(mappings.limit, 10) > 0) {
listItems.splice(mappings.limit);
}
return (
<Container service={service}>
<div className="flex flex-col w-full">
{listItems.length === 0 ? (
<div className="bg-theme-200/50 dark:bg-theme-900/20 rounded-sm m-1 flex-1 flex flex-row items-center justify-between p-1 text-xs">
<div className="font-thin pl-2">No items found</div>
</div>
) : (
listItems.map((item, index) => {
const itemName = shvl.get(item, name, "");
const itemLabel = shvl.get(item, label, "");
const itemUrl = target ? target.replace(/\{([^}]+)\}/g, (_, key) => item[key] || "") : null;
const className =
"bg-theme-200/50 dark:bg-theme-900/20 rounded-sm m-1 flex-1 flex flex-row items-center justify-between p-1 text-xs";
return itemUrl ? (
<a
key={`${itemName}-${index}`}
className={classNames(className, "hover:bg-theme-300/50 dark:hover:bg-theme-800/20")}
href={itemUrl}
target="_blank"
rel="noopener noreferrer"
>
<div className="font-thin pl-2">{itemName}</div>
<div className="flex flex-row text-right">
<div className="font-bold mr-2">{itemLabel}</div>
</div>
</a>
) : (
<div key={`${itemName}-${index}`} className={className}>
<div className="font-thin pl-2">{itemName}</div>
<div className="flex flex-row text-right">
<div className="font-bold mr-2">{itemLabel}</div>
</div>
</div>
);
})
)}
</div>
</Container>
);
case "list": case "list":
return ( return (
<Container service={service}> <Container service={service}>

View File

@ -8,17 +8,21 @@ export default function Component({ service }) {
const { data: giteaNotifications, error: giteaNotificationsError } = useWidgetAPI(widget, "notifications"); const { data: giteaNotifications, error: giteaNotificationsError } = useWidgetAPI(widget, "notifications");
const { data: giteaIssues, error: giteaIssuesError } = useWidgetAPI(widget, "issues"); const { data: giteaIssues, error: giteaIssuesError } = useWidgetAPI(widget, "issues");
const { data: giteaRepositories, error: giteaRepositoriesError } = useWidgetAPI(widget, "repositories");
if (giteaNotificationsError || giteaIssuesError) { if (giteaNotificationsError || giteaIssuesError || giteaRepositoriesError) {
return <Container service={service} error={giteaNotificationsError ?? giteaIssuesError} />; return (
<Container service={service} error={giteaNotificationsError ?? giteaIssuesError ?? giteaRepositoriesError} />
);
} }
if (!giteaNotifications || !giteaIssues) { if (!giteaNotifications || !giteaIssues || !giteaRepositories) {
return ( return (
<Container service={service}> <Container service={service}>
<Block label="gitea.notifications" /> <Block label="gitea.notifications" />
<Block label="gitea.issues" /> <Block label="gitea.issues" />
<Block label="gitea.pulls" /> <Block label="gitea.pulls" />
<Block label="gitea.repositories" />
</Container> </Container>
); );
} }
@ -28,6 +32,7 @@ export default function Component({ service }) {
<Block label="gitea.notifications" value={giteaNotifications.length} /> <Block label="gitea.notifications" value={giteaNotifications.length} />
<Block label="gitea.issues" value={giteaIssues.issues.length} /> <Block label="gitea.issues" value={giteaIssues.issues.length} />
<Block label="gitea.pulls" value={giteaIssues.pulls.length} /> <Block label="gitea.pulls" value={giteaIssues.pulls.length} />
<Block label="gitea.repositories" value={giteaRepositories.data.length} />
</Container> </Container>
); );
} }

View File

@ -16,6 +16,9 @@ const widget = {
issues: asJson(data).filter((issue) => !issue.pull_request), issues: asJson(data).filter((issue) => !issue.pull_request),
}), }),
}, },
repositories: {
endpoint: "repos/search",
},
}, },
}; };

View File

@ -1,7 +1,7 @@
import genericProxyHandler from "utils/proxy/handlers/generic"; import genericProxyHandler from "utils/proxy/handlers/generic";
const widget = { const widget = {
api: "{url}/api/v1/{key}/{endpoint}/", api: "{url}/api/v1/{key}/{endpoint}",
proxyHandler: genericProxyHandler, proxyHandler: genericProxyHandler,
mappings: { mappings: {

View File

@ -18,7 +18,7 @@ export default async function minecraftProxyHandler(req, res) {
res.status(200).send({ res.status(200).send({
version: pingResponse.status.version.name, version: pingResponse.status.version.name,
online: true, online: true,
players: pingResponse.status.players.online, players: pingResponse.status.players,
}); });
} catch (e) { } catch (e) {
if (e) logger.error(e); if (e) logger.error(e);

View File

@ -36,14 +36,16 @@ export default function Component({ service }) {
const printStatsInfo = printStats.result.status.print_stats.info ?? {}; const printStatsInfo = printStats.result.status.print_stats.info ?? {};
const { current_layer: currentLayer = "-", total_layer: totalLayer = "-" } = printStatsInfo; const { current_layer: currentLayer = "-", total_layer: totalLayer = "-" } = printStatsInfo;
const layers = printStats.result.status.print_stats.state === "standby" ? "- / -" : `${currentLayer} / ${totalLayer}`;
const progress =
printStats.result.status.print_stats.state === "standby"
? "-"
: t("common.percent", { value: displayStatus.result.status.display_status.progress * 100 });
return ( return (
<Container service={service}> <Container service={service}>
<Block label="moonraker.layers" value={`${currentLayer} / ${totalLayer}`} /> <Block label="moonraker.layers" value={layers} />
<Block <Block label="moonraker.print_progress" value={progress} />
label="moonraker.print_progress"
value={t("common.percent", { value: displayStatus.result.status.display_status.progress * 100 })}
/>
<Block label="moonraker.print_status" value={printStats.result.status.print_stats.state} /> <Block label="moonraker.print_status" value={printStats.result.status.print_stats.state} />
</Container> </Container>
); );

View File

@ -0,0 +1,55 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
const slskdDefaultFields = ["slskStatus", "downloads", "uploads", "sharedFiles"];
const MAX_ALLOWED_FIELDS = 4;
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
const { data: appData, error: appError } = useWidgetAPI(widget, "application");
const { data: downData, error: downError } = useWidgetAPI(widget, "downloads");
const { data: upData, error: upError } = useWidgetAPI(widget, "uploads");
if (appError || downError || upError) {
return <Container service={service} error={appError ?? downError ?? upError} />;
}
if (!widget.fields || widget.fields.length === 0) {
widget.fields = slskdDefaultFields;
} else if (widget.fields?.length > MAX_ALLOWED_FIELDS) {
widget.fields = widget.fields.slice(0, MAX_ALLOWED_FIELDS);
}
if (!appData || !downData || !upData) {
return (
<Container service={service}>
<Block label="slskd.slskStatus" />
<Block label="slskd.updateStatus" />
<Block label="slskd.downloads" />
<Block label="slskd.uploads" />
<Block label="slskd.sharedFiles" />
</Container>
);
}
return (
<Container service={service}>
<Block
label="slskd.slskStatus"
value={appData.server?.isConnected ? t("slskd.connected") : t("slskd.disconnected")}
/>
<Block
label="slskd.updateStatus"
value={appData.version?.isUpdateAvailable ? t("slskd.update_yes") : t("slskd.update_no")}
/>
<Block label="slskd.downloads" value={t("common.number", { value: downData.length ?? 0 })} />
<Block label="slskd.uploads" value={t("common.number", { value: upData.length ?? 0 })} />
<Block label="slskd.sharedFiles" value={t("common.number", { value: appData.shares?.files ?? 0 })} />
</Container>
);
}

View File

@ -0,0 +1,21 @@
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
import { asJson } from "utils/proxy/api-helpers";
const widget = {
api: `{url}/api/v0/{endpoint}`,
proxyHandler: credentialedProxyHandler,
mappings: {
application: {
endpoint: "application",
},
downloads: {
endpoint: "transfers/downloads",
},
uploads: {
endpoint: "transfers/uploads",
},
},
};
export default widget;

View File

@ -104,6 +104,7 @@ import readarr from "./readarr/widget";
import rutorrent from "./rutorrent/widget"; import rutorrent from "./rutorrent/widget";
import sabnzbd from "./sabnzbd/widget"; import sabnzbd from "./sabnzbd/widget";
import scrutiny from "./scrutiny/widget"; import scrutiny from "./scrutiny/widget";
import slskd from "./slskd/widget";
import sonarr from "./sonarr/widget"; import sonarr from "./sonarr/widget";
import speedtest from "./speedtest/widget"; import speedtest from "./speedtest/widget";
import spoolman from "./spoolman/widget"; import spoolman from "./spoolman/widget";
@ -244,6 +245,7 @@ const widgets = {
rutorrent, rutorrent,
sabnzbd, sabnzbd,
scrutiny, scrutiny,
slskd,
sonarr, sonarr,
speedtest, speedtest,
spoolman, spoolman,