chore(release): automatic release v2.1.0
This commit is contained in:
68 files changed
+7280
-575
No files matched your search
@@ -37,4 +37,4 @@ Orient from the current checkout before editing. Treat package manifests, export
|
||||
- Cron jobs: `packages/cron-jobs/src/jobs/`
|
||||
- App routes: `apps/nextjs/src/app/[locale]/`
|
||||
|
||||
Invoke `documentation-sync` when hidden capabilities or information not evident to an advanced user change, or existing guidance becomes inaccurate; follow the Documentation Sync criteria in `AGENTS.md`. When exposing tRPC through MCP, invoke `mcp-integration`.
|
||||
Invoke `documentation-sync` only when users need new or corrected information to set up, use, or troubleshoot Homarr; follow the Documentation Sync criteria in `AGENTS.md`. When exposing tRPC through MCP, invoke `mcp-integration`.
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
name: documentation-sync
|
||||
description: Write concise Homarr docs only when changed behavior is not clear to a smart, advanced user from the UI or generated schema. Skip routine, obvious, or self-explanatory changes.
|
||||
description: Write Homarr docs only when users need information to set up, use, or troubleshoot the app. Skip routine, obvious, or self-explanatory changes.
|
||||
---
|
||||
|
||||
# Documentation Sync
|
||||
|
||||
Apply the audience and scope rules in `AGENTS.md` first. A code, UI, or API change alone does not warrant documentation. Add a passage only when a smart, advanced user cannot infer the behavior from the interface or generated schema.
|
||||
Apply the audience and scope rules in `AGENTS.md` first. Documentation exists only for information users need to set up, use, or troubleshoot Homarr. A code, UI, or API change alone does not warrant documentation; if a user does not need to know it, do not add it.
|
||||
|
||||
For hidden capabilities such as the advanced widget feature, explain discovery, activation, and non-obvious behavior. For visible standard controls such as Delete, omit narration of what the label already conveys. Use the shortest explanation that preserves necessary technical detail; remove redundant prose in the passage being edited.
|
||||
|
||||
@@ -50,7 +50,7 @@ For a widget:
|
||||
|
||||
## Completion criteria
|
||||
|
||||
1. Every passage adds information an advanced user cannot reasonably infer from the interface. Remove obvious, repetitive, or unnecessary prose.
|
||||
1. Every passage adds information a user needs and cannot reasonably infer from the interface. Remove obvious, repetitive, or unnecessary prose.
|
||||
2. Keep names, defaults, paths, screenshots, links, and prerequisites consistent with code.
|
||||
3. Update every affected page and remove superseded guidance.
|
||||
4. Run the narrowest useful docs validation. Use `pnpm turbo build --filter=@homarr/docs` when links, MDX, generated definitions, or navigation can fail; otherwise run the docs package formatter on touched files.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
interface:
|
||||
display_name: "Homarr Documentation Sync"
|
||||
short_description: "Keep user-facing docs aligned with code"
|
||||
default_prompt: "Use $documentation-sync to map this Homarr code change to the required documentation updates."
|
||||
short_description: "Document only what users need to know"
|
||||
default_prompt: "Use $documentation-sync only when users need new or corrected information to set up, use, or troubleshoot Homarr."
|
||||
@@ -45,6 +45,7 @@ body:
|
||||
options:
|
||||
# The below comment is used to insert a new version with on-release.yml
|
||||
#NEXT_VERSION#
|
||||
- 2.0.0
|
||||
- 1.77.0
|
||||
- 1.76.2
|
||||
- 1.76.1
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
- [ ] Pull request targets the intended base branch (`dev` by default; `release/v2` for v2 release work)
|
||||
- [ ] Commits follow the [conventional commits guideline](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||
- [ ] No shorthand variable names are used (for example, `x`, `y`, `i`, or abbreviations)
|
||||
- [ ] User-facing changes include matching documentation updates under `apps/docs`
|
||||
- [ ] Documentation under `apps/docs` is updated only when users need new or corrected information to use the app
|
||||
- [ ] When using AI, no temporary files are checked in and the code style follows the rest of the project
|
||||
|
||||
**When contributing a new integration or widget:**
|
||||
|
||||
@@ -57,11 +57,11 @@ homarr/
|
||||
|
||||
## Documentation
|
||||
|
||||
Add or update docs only when a smart, advanced user cannot infer the changed behavior from the UI or generated API schema. Document hidden capabilities, surprising behavior, non-obvious prerequisites or constraints, configuration contracts, and migrations. A code or API change alone is not a reason to add docs.
|
||||
Add documentation only when users need the information to set up, use, or troubleshoot Homarr and cannot reasonably discover it from the UI or generated API schema. If a user does not need to know it, do not document it. A code, UI, or API change alone is never a reason to add docs.
|
||||
|
||||
Keep warranted documentation concise. Omit UI walkthroughs, visible control descriptions, and details already clear from the interface or schema.
|
||||
Keep warranted documentation concise. Omit UI walkthroughs, visible control descriptions, internal implementation details, and anything already clear from the interface or schema.
|
||||
|
||||
Only after this reader-value test passes, use these locations:
|
||||
Only after this user-need test passes, use these locations:
|
||||
|
||||
- New integration → `apps/docs/docs/integrations/<slug>/index.mdx` + `index.ts`
|
||||
- New widget → `apps/docs/docs/widgets/<slug>/index.mdx` + `index.ts`
|
||||
@@ -97,7 +97,7 @@ Only after this reader-value test passes, use these locations:
|
||||
|
||||
## Testing
|
||||
|
||||
Add tests only when requested. Favor assertions that would fail for a plausible regression in user-visible behavior or a security boundary; avoid checks that repeat implementation details or duplicate stronger coverage. For focused changes, run only the relevant existing or newly requested checks when validation is needed. Do not run broad test, Docker, or E2E suites by default.
|
||||
Add tests only when requested, and only when the test is extremely relevant and will plausibly prevent a future regression in user-visible behavior or a security boundary. Do not add tests that restate implementation details or duplicate stronger coverage. For focused changes, run only the relevant existing or newly requested checks when validation is needed. Do not run broad test, Docker, or E2E suites by default.
|
||||
|
||||
## MCP servers
|
||||
|
||||
@@ -108,6 +108,6 @@ Add tests only when requested. Favor assertions that would fail for a plausible
|
||||
Portable skills live in `.agents/skills/`. Read the relevant `SKILL.md` before working in that domain; detailed references are loaded only when needed. Claude-compatible discovery is provided through `.claude/skills`.
|
||||
|
||||
- `codebase-context` — architecture, package boundaries, and shared utilities
|
||||
- `documentation-sync` — documentation for changes users need explained
|
||||
- `documentation-sync` — user-critical documentation only
|
||||
- `mcp-integration` — safe tRPC-to-MCP exposure
|
||||
- `homarr-custom-widget` — safe Custom JSX v2 authoring
|
||||
@@ -43,7 +43,7 @@ Homarr 2.0 builds on the dashboard you already use. Here is what changed.
|
||||
|
||||
{/* Historical excerpt boundary. */}
|
||||
|
||||
SQLite and PostgreSQL users can update their Homarr image as usual; database migrations run automatically at startup. **MySQL users must convert before upgrading**, as explained below. Workshop uses the production service by default, with no preview URL configuration required.
|
||||
**Back up before upgrading:** database migrations run automatically at startup and change the existing database in place. Follow the [upgrade and rollback guidance](/docs/getting-started/installation/docker#update), including the note on board sizing. **MySQL users must convert before upgrading**, as explained below. Workshop uses the production service by default, with no preview URL configuration required.
|
||||
|
||||
## Database support in v2
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ Using the `PUID` and `PGID` will require you to set the correct permissions on t
|
||||
| `LOG_LEVEL` | Log level to use | `debug` / `info` / `warn` / `error` | `info` |
|
||||
| `BASE_URL` | Public Homarr origin used by MCP OAuth metadata and redirects. Set this when a reverse proxy does not preserve the public host and protocol. | HTTP(S) origin without a path | - |
|
||||
| `NO_EXTERNAL_CONNECTION` | Disables Homarr's internet-backed requests, including update checks, weather, location search, and RSS feeds | `true` or `false` | `false` |
|
||||
| `ENABLE_DNS_CACHING` | Enables dns caching. Enabled by default, but if you experience issues with IPv6 or static IPs, see [#4006](https://github.com/homarr-labs/homarr/issues/4006) or disable this flag | `true` or `false` | `true` |
|
||||
| `ENABLE_DNS_CACHING` | Opt-in DNS cache. Keep disabled for Docker service names and `extra_hosts`; cached addresses can survive container recreation and DNS-first lookup can override hosts-file entries. | `true` or `false` | `false` |
|
||||
| `HOMARR_DISABLE_IPV6` | Disables the IPv6 listener of the internal nginx proxy, even when the host supports IPv6. IPv6 is normally disabled automatically when the host has no IPv6 configured. See [#4596](https://github.com/homarr-labs/homarr/issues/4596) | `true` or `false` | `false` |
|
||||
| `HOMARR_WEBSITE_URL` | Main Homarr documentation/site base used by runtime links. Must be an absolute HTTP(S) URL. | HTTP(S) URL | `https://homarr.dev` |
|
||||
| `WORKSHOP_API_URL` | PocketBase API used by Homarr and standalone documentation. | HTTP(S) URL | `HOMARR_WEBSITE_URL` |
|
||||
|
||||
@@ -16,7 +16,9 @@ import {
|
||||
} from "@tabler/icons-react";
|
||||
|
||||
After Homarr starts, open its URL. An unfinished installation redirects to the onboarding screen at `/init`. Choose
|
||||
**Get started** for a new installation or **Restore backup** to import a compatible SQLite backup.
|
||||
**Get started** for a new installation or **Restore backup** to import a compatible SQLite backup. Before creating the
|
||||
administrator, you can return to Welcome with **Back**, including after refreshing `/init`. Creating the administrator
|
||||
commits this setup stage; later steps do not rewind it.
|
||||
|
||||
<Callout type="info" title="Persist /appdata before you continue">
|
||||
Onboarding writes the administrator and board configuration to the database. If onboarding returns after a restart,
|
||||
|
||||
@@ -24,15 +24,44 @@ see [Managing integrations](/docs/management/integrations#configuration).
|
||||
|
||||
### Update
|
||||
|
||||
<Alert type="warning">
|
||||
**Back up before upgrading from v1 to v2.** v2 migrates the existing database in place. For SQLite, this is
|
||||
`/appdata/db/db.sqlite` by default; v1 cannot use the upgraded database. Switching back to the v1 image alone is not a
|
||||
rollback. Image-rollback tools that revert only the container image do not restore the database; you still need
|
||||
the pre-upgrade backup.
|
||||
|
||||
Stop Homarr before copying the complete `/appdata` directory or volume. Keep the pre-upgrade copy, your container
|
||||
configuration, and `SECRET_ENCRYPTION_KEY` securely. If you use an external PostgreSQL database, back it up with its
|
||||
native tools as well. See [Backup and restore](/docs/management/backup).
|
||||
|
||||
</Alert>
|
||||
|
||||
Update the image and recreate the container with the same configuration. Homarr runs database migrations automatically
|
||||
at startup, including when moving from v1 to v2; no separate upgrade procedure is needed. Keep the `/appdata` mount and
|
||||
`SECRET_ENCRYPTION_KEY` unchanged. Workshop connects to the production service by default.
|
||||
|
||||
Existing MySQL installations must [convert to SQLite before upgrading to v2](/docs/advanced/mysql-to-sqlite).
|
||||
|
||||
```sh
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
To roll back, stop v2, restore the **pre-upgrade** database and `/appdata` backup with the matching configuration and
|
||||
encryption key, then start the previously used v1 image. A backup taken after v2 has migrated the database cannot
|
||||
restore v1 compatibility.
|
||||
|
||||
#### Board sizing after upgrading to v2
|
||||
|
||||
v2 uses fixed `200 × 200` logical-pixel cells and scales the whole board to fit the available width. A migrated
|
||||
12-column layout can therefore show smaller widgets and text than in v1. Reducing the column count in
|
||||
[board layout settings](/docs/management/boards#responsive-layouts) makes cells appear larger at the same viewport
|
||||
width; choose a count that fits your content.
|
||||
|
||||
Back up before changing columns: reducing the count can resize Containers and displace widgets. Check the layout in
|
||||
edit mode before saving. In v2.0.0, increasing the count again may not restore the previous arrangement after widgets
|
||||
or Containers have been displaced or resized; keep a backup rather than relying on reversing the column change.
|
||||
|
||||
### Uninstall
|
||||
|
||||
`docker compose down` removes the containers and Compose network. Delete the configured `/appdata` host directory or
|
||||
@@ -53,5 +82,6 @@ docker run \
|
||||
-d ghcr.io/homarr-labs/homarr:latest
|
||||
```
|
||||
|
||||
Remove the Docker socket mount when it is not required. To update, pull the current image, recreate the container with
|
||||
the same arguments, and keep the `/appdata` mount and `SECRET_ENCRYPTION_KEY` unchanged.
|
||||
Remove the Docker socket mount when it is not required. To update, follow the [backup guidance above](#update), pull
|
||||
the current image, recreate the container with the same arguments, and keep the `/appdata` mount and
|
||||
`SECRET_ENCRYPTION_KEY` unchanged.
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
<img src="https://raw.githubusercontent.com/homarr-labs/charts/refs/heads/main/charts/homarr/icon.svg" align="right" width="92" alt="homarr logo">
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
A Helm chart to deploy homarr for Kubernetes
|
||||
|
||||
@@ -12,7 +12,7 @@ A Helm chart to deploy homarr for Kubernetes
|
||||
|
||||
## Source Code
|
||||
|
||||
- <https://github.com/homarr-labs/homarr>
|
||||
* <https://github.com/homarr-labs/homarr>
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -76,39 +76,38 @@ To avoid including sensitive information in plain text within your version contr
|
||||
|
||||
Below is an exhaustive list of all secrets:
|
||||
|
||||
| FEATURE | SECRET NAME | SECRET KEYS | Required |
|
||||
| -------- | ---------------- | ------------------------------------ | -------- |
|
||||
| OIDC | auth-oidc-secret | oidc-client-id<br>oidc-client-secret | No |
|
||||
| LDAP | auth-ldap-secret | bind-password | No |
|
||||
| DATABASE | db-secret | db-url | No |
|
||||
| DATABASE | db-encryption | db-encryption-key | yes |
|
||||
| FEATURE | SECRET NAME | SECRET KEYS | Required |
|
||||
|-----------|-------------------------|-----------------------------------------|----------|
|
||||
| OIDC | auth-oidc-secret | oidc-client-id<br>oidc-client-secret | No |
|
||||
| LDAP | auth-ldap-secret | bind-password | No |
|
||||
| DATABASE | db-secret | db-url | No |
|
||||
| DATABASE | db-encryption | db-encryption-key | yes |
|
||||
|
||||
### Database
|
||||
|
||||
SQLite is the default; PostgreSQL is also supported. Existing MySQL installations must [convert to SQLite before upgrading to v2](/docs/advanced/mysql-to-sqlite).
|
||||
|
||||
You have multiple options for configuring the database:
|
||||
|
||||
| DRIVER TYPE | Persistence mode |
|
||||
| -------------- | ---------------------------- |
|
||||
| better-sqlite3 | Pod disk |
|
||||
| better-sqlite3 | homarr-database PVC |
|
||||
| node-postgres | External Postgresql database |
|
||||
| DRIVER TYPE | Persistence mode |
|
||||
|----------------|---------------------------------|
|
||||
| better-sqlite3 | Pod disk |
|
||||
| better-sqlite3 | homarr-database PVC |
|
||||
| mysql2 | External MySql database |
|
||||
| node-postgres | External Postgresql database |
|
||||
|
||||
#### Pod disk
|
||||
|
||||
No additional configuration is required. However, keep in mind that if the pod restarts, all data will be lost. This setup is not _recommended_ for production use.
|
||||
No additional configuration is required. However, keep in mind that if the pod restarts, all data will be lost. This setup is not *recommended* for production use.
|
||||
|
||||
To create the necessary database secret, execute the following command:
|
||||
|
||||
<details>
|
||||
<summary>Required Secrets</summary>
|
||||
|
||||
```yaml
|
||||
````yaml
|
||||
kubectl create secret generic db-encryption \
|
||||
--from-literal=db-encryption-key='<SECRET_ENCRYPTION_KEY_SECRET_TO_CHANGE>' \
|
||||
--namespace homarr
|
||||
```
|
||||
````
|
||||
|
||||
</details>
|
||||
|
||||
@@ -121,11 +120,11 @@ Associated secret to create :
|
||||
<details>
|
||||
<summary>DB Required Secrets</summary>
|
||||
|
||||
```yaml
|
||||
````yaml
|
||||
kubectl create secret generic db-encryption \
|
||||
--from-literal=db-encryption-key='<SECRET_ENCRYPTION_KEY_SECRET_TO_CHANGE>' \
|
||||
--namespace homarr
|
||||
```
|
||||
````
|
||||
|
||||
</details>
|
||||
|
||||
@@ -134,16 +133,47 @@ Bellow an example of the override value file :
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
```yaml
|
||||
````yaml
|
||||
persistence:
|
||||
homarrDatabase:
|
||||
enabled: true
|
||||
storageClassName: "default"
|
||||
size: "1Gi"
|
||||
```
|
||||
````
|
||||
|
||||
</details>
|
||||
|
||||
#### External MySql database
|
||||
|
||||
To create the necessary database secrets, execute the following command:
|
||||
|
||||
<details>
|
||||
<summary>Required Secrets</summary>
|
||||
|
||||
````yaml
|
||||
kubectl create secret generic db-encryption \
|
||||
--from-literal=db-encryption-key='<SECRET_ENCRYPTION_KEY_SECRET_TO_CHANGE>' \
|
||||
--namespace homarr
|
||||
````
|
||||
|
||||
````yaml
|
||||
kubectl create secret generic db-secret \
|
||||
--from-literal=db-url='mysql://user:password@host:port/homarrdb' \
|
||||
--namespace homarr
|
||||
````
|
||||
</details>
|
||||
|
||||
Below is an example of the override values file:
|
||||
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
````yaml
|
||||
database:
|
||||
type: mysql
|
||||
````
|
||||
</details>
|
||||
|
||||
#### External Postgresql database
|
||||
|
||||
To create the necessary database secrets, execute the following command:
|
||||
@@ -151,17 +181,17 @@ To create the necessary database secrets, execute the following command:
|
||||
<details>
|
||||
<summary>Required Secrets</summary>
|
||||
|
||||
```yaml
|
||||
````yaml
|
||||
kubectl create secret generic db-encryption \
|
||||
--from-literal=db-encryption-key='<SECRET_ENCRYPTION_KEY_SECRET_TO_CHANGE>' \
|
||||
--namespace homarr
|
||||
```
|
||||
````
|
||||
|
||||
```yaml
|
||||
````yaml
|
||||
kubectl create secret generic db-secret \
|
||||
--from-literal=db-url='postgresql://user:password@host:port/homarrdb' \
|
||||
--namespace homarr
|
||||
```
|
||||
````
|
||||
|
||||
</details>
|
||||
|
||||
@@ -170,11 +200,10 @@ Below is an example of the override values file:
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
```yaml
|
||||
````yaml
|
||||
database:
|
||||
type: postgresql
|
||||
```
|
||||
|
||||
````
|
||||
</details>
|
||||
|
||||
### Ingress
|
||||
@@ -184,7 +213,7 @@ The ingress section in the values.yaml file allows you to configure how external
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
```yaml
|
||||
````yaml
|
||||
service:
|
||||
enabled: true # Ensure the service is enabled for Ingress to route traffic
|
||||
ingress:
|
||||
@@ -202,8 +231,7 @@ ingress:
|
||||
- "homarr.homelab.dev"
|
||||
- "www.homarr.homelab.dev"
|
||||
secretName: homelab-tls
|
||||
```
|
||||
|
||||
````
|
||||
</details>
|
||||
|
||||
### HTTPRoute (Gateway API)
|
||||
@@ -214,9 +242,9 @@ This provides a more expressive and future-proof alternative to Ingress, with su
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
```yaml
|
||||
````yaml
|
||||
service:
|
||||
enabled: true # must be enabled for HTTPRoute to forward traffic
|
||||
enabled: true # must be enabled for HTTPRoute to forward traffic
|
||||
httproute:
|
||||
enabled: true
|
||||
parentRefs:
|
||||
@@ -232,15 +260,14 @@ httproute:
|
||||
backendRefs:
|
||||
- name: homarr
|
||||
port: 7575
|
||||
```
|
||||
|
||||
````
|
||||
</details>
|
||||
|
||||
#### Notes:
|
||||
|
||||
- parentRefs: the Gateway(s) this route attaches to. Gateways must already exist.
|
||||
- hostnames: domain names this route applies to.
|
||||
- rules: each rule can define matches (paths, headers, queries), optional filters, and backendRefs to services
|
||||
- rules: each rule can define matches (paths, headers, queries), optional filters, and backendRefs to services
|
||||
- TLS must be configured on the Gateway (not on HTTPRoute). For example, the Gateway can have a TLS listener for homarr.homelab.dev, and this route will automatically apply once matched.
|
||||
|
||||
### Certificates
|
||||
@@ -257,7 +284,7 @@ Use inline certificates to generate a ConfigMap, mounted as individual files.
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
```yaml
|
||||
````yaml
|
||||
persistence:
|
||||
homarrTrustedCerts:
|
||||
enabled: true
|
||||
@@ -271,8 +298,7 @@ persistence:
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID...EFGH==
|
||||
-----END CERTIFICATE-----
|
||||
```
|
||||
|
||||
````
|
||||
</details>
|
||||
|
||||
Behavior:
|
||||
@@ -287,7 +313,7 @@ Use inline certificates to generate a Kubernetes Secret, mounted as files.
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
```yaml
|
||||
````yaml
|
||||
persistence:
|
||||
homarrTrustedCerts:
|
||||
enabled: true
|
||||
@@ -301,8 +327,7 @@ persistence:
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC...XYZ==
|
||||
-----END CERTIFICATE-----
|
||||
```
|
||||
|
||||
````
|
||||
</details>
|
||||
|
||||
Behavior:
|
||||
@@ -317,17 +342,16 @@ Use an existing Kubernetes Secret, assuming its keys are filenames.
|
||||
<details>
|
||||
<summary>values.yaml</summary>
|
||||
|
||||
```yaml
|
||||
````yaml
|
||||
persistence:
|
||||
homarrTrustedCerts:
|
||||
enabled: true
|
||||
type: existingSecret
|
||||
existingSecretName: "existingSecretName"
|
||||
existingSecretKeys:
|
||||
- cert3.crt
|
||||
- cert4.crt
|
||||
```
|
||||
|
||||
- cert3.crt
|
||||
- cert4.crt
|
||||
````
|
||||
</details>
|
||||
|
||||
Behavior:
|
||||
@@ -348,115 +372,114 @@ All available values are listed on the [artifacthub](https://artifacthub.io/pack
|
||||
|
||||
## Values
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| -------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| additionalObjects | list | `[]` | Additional resources to deploy. These objects are templated. |
|
||||
| affinity | object | `{}` | Node affinity for pod scheduling |
|
||||
| autoscaling.enabled | bool | `false` | Enable autoscaling |
|
||||
| autoscaling.maxReplicas | int | `100` | Maximum replicas |
|
||||
| autoscaling.minReplicas | int | `1` | Minimum replicas |
|
||||
| autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization for autoscaling |
|
||||
| containerPorts | object | `{"http":{"port":7575,"protocol":"TCP"}}` | containerPorts defines the ports to open on the container. It is a map where each entry specifies: - `port` (int) (required): The port number to expose inside the container. - `protocol` (string) (required): The network protocol (TCP or UDP) used for the port. - `disabled` (bool) : Optional flag to disable this port (defaults to false). Can be overridden via Helm values. By default, this configuration exposes TCP port 7575 with the name `http`. |
|
||||
| database.migrationEnabled | bool | `true` | Database migration configuration. DB_MIGRATIONS_DISABLED Set to `true` to disable database migrations. Migrations are enabled by default (`false`). |
|
||||
| database.type | string | `"sqlite"` | Database type: sqlite or postgresql |
|
||||
| env.AUTH_COOKIE_PREFIX | string | `"homarr"` | Prefix used for all authentication cookies. Change if you run another Auth.js/NextAuth app on the same hostname to avoid cookie name collisions. Only letters, numbers, hyphens and underscores. |
|
||||
| env.AUTH_LDAP_BASE | string | `nil` | Base dn of your LDAP server |
|
||||
| env.AUTH_LDAP_BIND_DN | string | `nil` | User used for finding users and groups |
|
||||
| env.AUTH_LDAP_GROUP_CLASS | string | `"groupOfUniqueNames"` | Class used for querying groups |
|
||||
| env.AUTH_LDAP_GROUP_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user's groups search filter (& based) |
|
||||
| env.AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE | string | `"member"` | Attribute used for querying group member |
|
||||
| env.AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE | string | `"dn"` | User attribute used for comparing with group member |
|
||||
| env.AUTH_LDAP_SEARCH_SCOPE | string | `"base"` | LDAP search scope between base, one or sub |
|
||||
| env.AUTH_LDAP_URI | string | `nil` | URI of your LDAP server |
|
||||
| env.AUTH_LDAP_USERNAME_ATTRIBUTE | string | `"uid"` | Attribute used for username |
|
||||
| env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user search filter (& based) |
|
||||
| env.AUTH_LDAP_USER_MAIL_ATTRIBUTE | string | `"mail"` | Attribute used for mail field |
|
||||
| env.AUTH_LOGOUT_REDIRECT_URL | string | `nil` | URL to redirect to after clicking logging out. |
|
||||
| env.AUTH_OIDC_AUTO_LOGIN | string | `"false"` | Automatically redirect to OIDC login |
|
||||
| env.AUTH_OIDC_CLIENT_NAME | string | `"OIDC"` | Display name of provider (in login screen) |
|
||||
| env.AUTH_OIDC_GROUPS_ATTRIBUTE | string | `"groups"` | Attribute used for groups (roles) claim |
|
||||
| env.AUTH_OIDC_ISSUER | string | `nil` | Issuer URI of OIDC provider without trailing slash (/) |
|
||||
| env.AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE | string | `nil` | Overwrite name attribute. By default, it will use preferred_username if it does not contain a @ and otherwise name. |
|
||||
| env.AUTH_OIDC_SCOPE_OVERWRITE | string | `"openid email profile groups"` | Override the OIDC scopes |
|
||||
| env.AUTH_PROVIDERS | string | `"credentials"` | Enabled authentication methods. Multiple providers can be enabled with by separating them with , (ex. AUTH_PROVIDERS=credentials,oidc, it is highly recommended to just enable one provider). |
|
||||
| env.AUTH_SESSION_EXPIRY_TIME | string | `"30d"` | Time for the session to time out. Can be set as pure number, which will automatically be used in seconds, or followed by s, m, h or d for seconds, minutes, hours or days. (ex: "30m") |
|
||||
| env.ENABLE_DNS_CACHING | string | `"false"` | Enables dns caching. This is not yet working for all users. See #4006 |
|
||||
| env.LOG_LEVEL | string | `"info"` | Log level to use. Possible values: debug/info/warn/error |
|
||||
| env.NO_EXTERNAL_CONNECTION | string | `"false"` | Disables some requests that need internet connection |
|
||||
| env.TZ | string | `"Europe/Paris"` | Your local time zone |
|
||||
| envSecrets.authLdapCredentials.existingSecret | string | `"auth-ldap-secret"` | Name of existing secret containing LDAP credentials |
|
||||
| envSecrets.authLdapCredentials.ldapBindingPassword | string | `"bind-password"` | Password for bind user secret key |
|
||||
| envSecrets.authOidcCredentials.existingSecret | string | `"auth-oidc-secret"` | Name of existing secret containing OIDC credentials |
|
||||
| envSecrets.authOidcCredentials.oidcClientId | string | `"oidc-client-id"` | ID of OIDC client (application) secret key |
|
||||
| envSecrets.authOidcCredentials.oidcClientSecret | string | `"oidc-client-secret"` | Secret of OIDC client (application) secret key |
|
||||
| envSecrets.dbCredentials.dbUrlKey | string | `"db-url"` | Secret key for DB_URL Example for external database: `postgresql://username:password@host:port/homarrdb` |
|
||||
| envSecrets.dbCredentials.existingSecret | string | `"db-secret"` | Name of existing secret containing DB credentials |
|
||||
| envSecrets.dbEncryption.existingSecret | string | `"db-encryption"` | Name of existing secret containing DB encryption |
|
||||
| envSecrets.dbEncryption.key | string | `"db-encryption-key"` | Secret key for SECRET_ENCRYPTION_KEY can be generated with `openssl rand -hex 32` |
|
||||
| fullnameOverride | string | `""` | Overrides chart's fullname |
|
||||
| hostAliases | list | `[]` | Add static entries to /etc/hosts in the Pod. This is useful in the following cases: - You are running in a dual-stack cluster (IPv4 + IPv6) and want to force usage of IPv4 for specific hostnames - Your application is having DNS resolution issues or IPv6 preference issues - You need to override or simulate DNS entries without changing global DNS - You are running in an air-gapped or isolated environment without external DNS Example: hostAliases: - ip: "192.168.1.10" hostnames: - "example.com" - "example.internal" |
|
||||
| httproute | object | `{"enabled":false,"hostnames":["chart-example.local"],"parentRefs":[{"name":"my-gateway","namespace":"default"}],"rules":[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]}` | Gateway API HTTPRoute configuration |
|
||||
| httproute.enabled | bool | `false` | Enable HTTPRoute |
|
||||
| httproute.hostnames | list | `["chart-example.local"]` | Hostnames this route matches (similar to ingress.hosts.host) |
|
||||
| httproute.parentRefs | list | `[{"name":"my-gateway","namespace":"default"}]` | References to the parent Gateway(s) this route attaches to. Each item must include at least a `name`, and optionally a `namespace`. |
|
||||
| httproute.rules | list | `[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]` | List of routing rules. Each rule can include: - matches: path/header/query matching - filters: optional transformations (redirects, header modifications, etc.) - backendRefs: one or more Kubernetes Services to forward traffic to |
|
||||
| httproute.rules[0].filters | list | `[]` | Optional filters for this rule (default: empty) |
|
||||
| httproute.rules[0].matches[0].path.type | string | `"PathPrefix"` | Path match type. One of: Exact, PathPrefix, RegularExpression |
|
||||
| httproute.rules[0].matches[0].path.value | string | `"/"` | Path value to match |
|
||||
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
|
||||
| image.repository | string | `"ghcr.io/homarr-labs/homarr"` | Image repository |
|
||||
| image.tag | string | `"v1.76.2"` | Overrides the image tag whose default is the chart appVersion |
|
||||
| imagePullSecrets | list | `[]` | Secrets for Docker registry |
|
||||
| ingress.annotations | object | `{}` | Ingress annotations |
|
||||
| ingress.enabled | bool | `false` | Enable ingress |
|
||||
| ingress.hosts | list | `[{"host":"chart-example.local","paths":[{"path":"/"}]}]` | Ingress hosts configuration |
|
||||
| ingress.ingressClassName | string | `""` | Ingress class name |
|
||||
| ingress.tls | list | `[]` | Ingress TLS configuration |
|
||||
| livenessProbe.failureThreshold | int | `3` | Failure threshold for liveness probe - number of consecutive failures before pod is restarted |
|
||||
| livenessProbe.httpGet.path | string | `"/api/health/live"` | This is the liveness check endpoint used by Kubernetes to determine if the application is still running. |
|
||||
| livenessProbe.httpGet.port | int | `7575` | The port on which the liveness check will be performed. This must be the same as the container port exposed by the application. |
|
||||
| livenessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the liveness probe starts |
|
||||
| livenessProbe.periodSeconds | int | `10` | Period in seconds between liveness probe checks |
|
||||
| livenessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each liveness probe check |
|
||||
| nameOverride | string | `""` | Overrides chart's name |
|
||||
| nodeSelector | object | `{}` | Node selectors for pod scheduling |
|
||||
| persistence.homarrDatabase.accessMode | string | `"ReadWriteOnce"` | homarr-database access mode |
|
||||
| persistence.homarrDatabase.enabled | bool | `false` | Enable homarr-database persistent storage |
|
||||
| persistence.homarrDatabase.mountPath | string | `"/appdata"` | homarr-database mount path inside the pod |
|
||||
| persistence.homarrDatabase.name | string | `"homarr-database"` | homarr-database persistent storage name |
|
||||
| persistence.homarrDatabase.size | string | `"50Mi"` | homarr-database storage size |
|
||||
| persistence.homarrDatabase.storageClassName | string | `"local-path"` | homarr-database storage class name |
|
||||
| persistence.homarrDatabase.volumeClaimName | string | `""` | homarr-database optional volumeClaimName to target specific PV |
|
||||
| persistence.homarrTrustedCerts.certificates | string | `nil` | homarr-trusted-certificates certificates, each entry will become a new trusted certificate as a dedicated file (works only for "configmap" and "secret" mode) |
|
||||
| persistence.homarrTrustedCerts.enabled | bool | `false` | Enable trusted certificates persistence |
|
||||
| persistence.homarrTrustedCerts.existingSecretKeys | string | `nil` | List of keys (filenames) to mount from the existing secret (used only when type is "existingSecret") |
|
||||
| persistence.homarrTrustedCerts.existingSecretName | string | `""` | Name of the existing Kubernetes Secret to mount (required if type is "existingSecret") |
|
||||
| persistence.homarrTrustedCerts.mountPath | string | `"/appdata/trusted-certificates"` | homarr-trusted-certificates mount path inside the pod |
|
||||
| persistence.homarrTrustedCerts.type | string | `"configmap"` | Persistence mode can be : configmap (declarative), secret (declarative) or existingSecret (mount an existing Kubernetes Secret by name and specify which keys to mount as files) |
|
||||
| podAnnotations | object | `{}` | Pod annotations |
|
||||
| podLabels | object | `{}` | Pod labels |
|
||||
| podSecurityContext | object | `{}` | Pod security context |
|
||||
| rbac | object | `{"enabled":false}` | Enable RBAC resources for Kubernetes integration Creates Role, ClusterRole, and associated bindings for Homarr's Kubernetes features |
|
||||
| rbac.enabled | bool | `false` | Enable to create RBAC resources and activate Kubernetes integration |
|
||||
| readinessProbe.failureThreshold | int | `3` | Failure threshold for readiness probe - number of consecutive failures before pod is considered unready |
|
||||
| readinessProbe.httpGet.path | string | `"/api/health/ready"` | This is the readiness check endpoint used by Kubernetes to determine if the application is ready to handle traffic. |
|
||||
| readinessProbe.httpGet.port | int | `7575` | The port on which the readiness check will be performed. This must match the container's exposed port. |
|
||||
| readinessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the readiness probe starts. increase this value if the pod is slow to fully start. |
|
||||
| readinessProbe.periodSeconds | int | `10` | Period in seconds between readiness probe checks |
|
||||
| readinessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each readiness probe check |
|
||||
| replicaCount | int | `1` | Number of replicas |
|
||||
| resources | object | `{}` | Resource configuration |
|
||||
| securityContext | object | `{}` | Security context |
|
||||
| service.enabled | bool | `true` | Enable service |
|
||||
| service.ipFamilies | list | `[]` | List of IP families to use for the service. Examples: - ["IPv4"] - ["IPv6"] - ["IPv4", "IPv6"] for dual-stack Leave empty to use cluster default behavior |
|
||||
| service.ipFamilyPolicy | string | `"SingleStack"` | Defines how the service assigns IP families (IPv4/IPv6) Possible values: - SingleStack (default): Only one IP family, usually IPv4 - PreferDualStack: Use dual-stack if the cluster supports it, fallback to single - RequireDualStack: Fail if dual-stack cannot be assigned |
|
||||
| service.ports.app.port | int | `7575` | Service port |
|
||||
| service.ports.app.protocol | string | `"TCP"` | Service protocol |
|
||||
| service.ports.app.targetPort | string | `"http"` | Service target port |
|
||||
| service.type | string | `"ClusterIP"` | Service type |
|
||||
| strategyType | string | `"RollingUpdate"` | `strategyType` specifies the strategy used to replace old Pods by new ones. `strategyType` can be `"Recreate"` or `"RollingUpdate"`. `"RollingUpdate"` is the default value and updates Pods in a rolling update fashion. `"Recreate"` will kill all existing Pods before new ones are created. The `"Recreate"` strategy is necessary when persistent volume's `accessMode` is set to `"ReadWriteOnce"` when using `helm upgrade`, as pod volume attachments to an existing PersistentVolumeClaim need to be cleared before a new pod can attach to it. |
|
||||
| tolerations | list | `[]` | Node tolerations for pod scheduling |
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| additionalObjects | list | `[]` | Additional resources to deploy. These objects are templated. |
|
||||
| affinity | object | `{}` | Node affinity for pod scheduling |
|
||||
| autoscaling.enabled | bool | `false` | Enable autoscaling |
|
||||
| autoscaling.maxReplicas | int | `100` | Maximum replicas |
|
||||
| autoscaling.minReplicas | int | `1` | Minimum replicas |
|
||||
| autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization for autoscaling |
|
||||
| containerPorts | object | `{"http":{"port":7575,"protocol":"TCP"}}` | containerPorts defines the ports to open on the container. It is a map where each entry specifies: - `port` (int) (required): The port number to expose inside the container. - `protocol` (string) (required): The network protocol (TCP or UDP) used for the port. - `disabled` (bool) : Optional flag to disable this port (defaults to false). Can be overridden via Helm values. By default, this configuration exposes TCP port 7575 with the name `http`. |
|
||||
| database.migrationEnabled | bool | `true` | Database migration configuration. DB_MIGRATIONS_DISABLED Set to `true` to disable database migrations. Migrations are enabled by default (`false`). |
|
||||
| database.type | string | `"sqlite"` | Database type: sqlite, mysql or postgresql |
|
||||
| env.AUTH_COOKIE_PREFIX | string | `"homarr"` | Prefix used for all authentication cookies. Change if you run another Auth.js/NextAuth app on the same hostname to avoid cookie name collisions. Only letters, numbers, hyphens and underscores. |
|
||||
| env.AUTH_LDAP_BASE | string | `nil` | Base dn of your LDAP server |
|
||||
| env.AUTH_LDAP_BIND_DN | string | `nil` | User used for finding users and groups |
|
||||
| env.AUTH_LDAP_GROUP_CLASS | string | `"groupOfUniqueNames"` | Class used for querying groups |
|
||||
| env.AUTH_LDAP_GROUP_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user's groups search filter (& based) |
|
||||
| env.AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE | string | `"member"` | Attribute used for querying group member |
|
||||
| env.AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE | string | `"dn"` | User attribute used for comparing with group member |
|
||||
| env.AUTH_LDAP_SEARCH_SCOPE | string | `"base"` | LDAP search scope between base, one or sub |
|
||||
| env.AUTH_LDAP_URI | string | `nil` | URI of your LDAP server |
|
||||
| env.AUTH_LDAP_USERNAME_ATTRIBUTE | string | `"uid"` | Attribute used for username |
|
||||
| env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user search filter (& based) |
|
||||
| env.AUTH_LDAP_USER_MAIL_ATTRIBUTE | string | `"mail"` | Attribute used for mail field |
|
||||
| env.AUTH_LOGOUT_REDIRECT_URL | string | `nil` | URL to redirect to after clicking logging out. |
|
||||
| env.AUTH_OIDC_AUTO_LOGIN | string | `"false"` | Automatically redirect to OIDC login |
|
||||
| env.AUTH_OIDC_CLIENT_NAME | string | `"OIDC"` | Display name of provider (in login screen) |
|
||||
| env.AUTH_OIDC_GROUPS_ATTRIBUTE | string | `"groups"` | Attribute used for groups (roles) claim |
|
||||
| env.AUTH_OIDC_ISSUER | string | `nil` | Issuer URI of OIDC provider without trailing slash (/) |
|
||||
| env.AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE | string | `nil` | Overwrite name attribute. By default, it will use preferred_username if it does not contain a @ and otherwise name. |
|
||||
| env.AUTH_OIDC_SCOPE_OVERWRITE | string | `"openid email profile groups"` | Override the OIDC scopes |
|
||||
| env.AUTH_PROVIDERS | string | `"credentials"` | Enabled authentication methods. Multiple providers can be enabled with by separating them with , (ex. AUTH_PROVIDERS=credentials,oidc, it is highly recommended to just enable one provider). |
|
||||
| env.AUTH_SESSION_EXPIRY_TIME | string | `"30d"` | Time for the session to time out. Can be set as pure number, which will automatically be used in seconds, or followed by s, m, h or d for seconds, minutes, hours or days. (ex: "30m") |
|
||||
| env.ENABLE_DNS_CACHING | string | `"false"` | Enables dns caching. This is not yet working for all users. See #4006 |
|
||||
| env.LOG_LEVEL | string | `"info"` | Log level to use. Possible values: debug/info/warn/error |
|
||||
| env.NO_EXTERNAL_CONNECTION | string | `"false"` | Disables some requests that need internet connection |
|
||||
| env.TZ | string | `"Europe/Paris"` | Your local time zone |
|
||||
| envSecrets.authLdapCredentials.existingSecret | string | `"auth-ldap-secret"` | Name of existing secret containing LDAP credentials |
|
||||
| envSecrets.authLdapCredentials.ldapBindingPassword | string | `"bind-password"` | Password for bind user secret key |
|
||||
| envSecrets.authOidcCredentials.existingSecret | string | `"auth-oidc-secret"` | Name of existing secret containing OIDC credentials |
|
||||
| envSecrets.authOidcCredentials.oidcClientId | string | `"oidc-client-id"` | ID of OIDC client (application) secret key |
|
||||
| envSecrets.authOidcCredentials.oidcClientSecret | string | `"oidc-client-secret"` | Secret of OIDC client (application) secret key |
|
||||
| envSecrets.dbCredentials.dbUrlKey | string | `"db-url"` | Secret key for DB_URL Example for external database: `mysql://username:password@host:port/homarrdb` or `postgresql://username:password@host:port/homarrdb` |
|
||||
| envSecrets.dbCredentials.existingSecret | string | `"db-secret"` | Name of existing secret containing DB credentials |
|
||||
| envSecrets.dbEncryption.existingSecret | string | `"db-encryption"` | Name of existing secret containing DB encryption |
|
||||
| envSecrets.dbEncryption.key | string | `"db-encryption-key"` | Secret key for SECRET_ENCRYPTION_KEY can be generated with `openssl rand -hex 32` |
|
||||
| fullnameOverride | string | `""` | Overrides chart's fullname |
|
||||
| hostAliases | list | `[]` | Add static entries to /etc/hosts in the Pod. This is useful in the following cases: - You are running in a dual-stack cluster (IPv4 + IPv6) and want to force usage of IPv4 for specific hostnames - Your application is having DNS resolution issues or IPv6 preference issues - You need to override or simulate DNS entries without changing global DNS - You are running in an air-gapped or isolated environment without external DNS Example: hostAliases: - ip: "192.168.1.10" hostnames: - "example.com" - "example.internal" |
|
||||
| httproute | object | `{"enabled":false,"hostnames":["chart-example.local"],"parentRefs":[{"name":"my-gateway","namespace":"default"}],"rules":[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]}` | Gateway API HTTPRoute configuration |
|
||||
| httproute.enabled | bool | `false` | Enable HTTPRoute |
|
||||
| httproute.hostnames | list | `["chart-example.local"]` | Hostnames this route matches (similar to ingress.hosts.host) |
|
||||
| httproute.parentRefs | list | `[{"name":"my-gateway","namespace":"default"}]` | References to the parent Gateway(s) this route attaches to. Each item must include at least a `name`, and optionally a `namespace`. |
|
||||
| httproute.rules | list | `[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]` | List of routing rules. Each rule can include: - matches: path/header/query matching - filters: optional transformations (redirects, header modifications, etc.) - backendRefs: one or more Kubernetes Services to forward traffic to |
|
||||
| httproute.rules[0].filters | list | `[]` | Optional filters for this rule (default: empty) |
|
||||
| httproute.rules[0].matches[0].path.type | string | `"PathPrefix"` | Path match type. One of: Exact, PathPrefix, RegularExpression |
|
||||
| httproute.rules[0].matches[0].path.value | string | `"/"` | Path value to match |
|
||||
| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
|
||||
| image.repository | string | `"ghcr.io/homarr-labs/homarr"` | Image repository |
|
||||
| image.tag | string | `"v2.0.0"` | Overrides the image tag whose default is the chart appVersion |
|
||||
| imagePullSecrets | list | `[]` | Secrets for Docker registry |
|
||||
| ingress.annotations | object | `{}` | Ingress annotations |
|
||||
| ingress.enabled | bool | `false` | Enable ingress |
|
||||
| ingress.hosts | list | `[{"host":"chart-example.local","paths":[{"path":"/"}]}]` | Ingress hosts configuration |
|
||||
| ingress.ingressClassName | string | `""` | Ingress class name |
|
||||
| ingress.tls | list | `[]` | Ingress TLS configuration |
|
||||
| livenessProbe.failureThreshold | int | `3` | Failure threshold for liveness probe - number of consecutive failures before pod is restarted |
|
||||
| livenessProbe.httpGet.path | string | `"/api/health/live"` | This is the liveness check endpoint used by Kubernetes to determine if the application is still running. |
|
||||
| livenessProbe.httpGet.port | int | `7575` | The port on which the liveness check will be performed. This must be the same as the container port exposed by the application. |
|
||||
| livenessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the liveness probe starts |
|
||||
| livenessProbe.periodSeconds | int | `10` | Period in seconds between liveness probe checks |
|
||||
| livenessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each liveness probe check |
|
||||
| nameOverride | string | `""` | Overrides chart's name |
|
||||
| nodeSelector | object | `{}` | Node selectors for pod scheduling |
|
||||
| persistence.homarrDatabase.accessMode | string | `"ReadWriteOnce"` | homarr-database access mode |
|
||||
| persistence.homarrDatabase.enabled | bool | `false` | Enable homarr-database persistent storage |
|
||||
| persistence.homarrDatabase.mountPath | string | `"/appdata"` | homarr-database mount path inside the pod |
|
||||
| persistence.homarrDatabase.name | string | `"homarr-database"` | homarr-database persistent storage name |
|
||||
| persistence.homarrDatabase.size | string | `"50Mi"` | homarr-database storage size |
|
||||
| persistence.homarrDatabase.storageClassName | string | `"local-path"` | homarr-database storage class name |
|
||||
| persistence.homarrDatabase.volumeClaimName | string | `""` | homarr-database optional volumeClaimName to target specific PV |
|
||||
| persistence.homarrTrustedCerts.certificates | string | `nil` | homarr-trusted-certificates certificates, each entry will become a new trusted certificate as a dedicated file (works only for "configmap" and "secret" mode) |
|
||||
| persistence.homarrTrustedCerts.enabled | bool | `false` | Enable trusted certificates persistence |
|
||||
| persistence.homarrTrustedCerts.existingSecretKeys | string | `nil` | List of keys (filenames) to mount from the existing secret (used only when type is "existingSecret") |
|
||||
| persistence.homarrTrustedCerts.existingSecretName | string | `""` | Name of the existing Kubernetes Secret to mount (required if type is "existingSecret") |
|
||||
| persistence.homarrTrustedCerts.mountPath | string | `"/appdata/trusted-certificates"` | homarr-trusted-certificates mount path inside the pod |
|
||||
| persistence.homarrTrustedCerts.type | string | `"configmap"` | Persistence mode can be : configmap (declarative), secret (declarative) or existingSecret (mount an existing Kubernetes Secret by name and specify which keys to mount as files) |
|
||||
| podAnnotations | object | `{}` | Pod annotations |
|
||||
| podLabels | object | `{}` | Pod labels |
|
||||
| podSecurityContext | object | `{}` | Pod security context |
|
||||
| rbac | object | `{"enabled":false}` | Enable RBAC resources for Kubernetes integration Creates Role, ClusterRole, and associated bindings for Homarr's Kubernetes features |
|
||||
| rbac.enabled | bool | `false` | Enable to create RBAC resources and activate Kubernetes integration |
|
||||
| readinessProbe.failureThreshold | int | `3` | Failure threshold for readiness probe - number of consecutive failures before pod is considered unready |
|
||||
| readinessProbe.httpGet.path | string | `"/api/health/ready"` | This is the readiness check endpoint used by Kubernetes to determine if the application is ready to handle traffic. |
|
||||
| readinessProbe.httpGet.port | int | `7575` | The port on which the readiness check will be performed. This must match the container's exposed port. |
|
||||
| readinessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the readiness probe starts. increase this value if the pod is slow to fully start. |
|
||||
| readinessProbe.periodSeconds | int | `10` | Period in seconds between readiness probe checks |
|
||||
| readinessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each readiness probe check |
|
||||
| replicaCount | int | `1` | Number of replicas |
|
||||
| resources | object | `{}` | Resource configuration |
|
||||
| securityContext | object | `{}` | Security context |
|
||||
| service.enabled | bool | `true` | Enable service |
|
||||
| service.ipFamilies | list | `[]` | List of IP families to use for the service. Examples: - ["IPv4"] - ["IPv6"] - ["IPv4", "IPv6"] for dual-stack Leave empty to use cluster default behavior |
|
||||
| service.ipFamilyPolicy | string | `"SingleStack"` | Defines how the service assigns IP families (IPv4/IPv6) Possible values: - SingleStack (default): Only one IP family, usually IPv4 - PreferDualStack: Use dual-stack if the cluster supports it, fallback to single - RequireDualStack: Fail if dual-stack cannot be assigned |
|
||||
| service.ports.app.port | int | `7575` | Service port |
|
||||
| service.ports.app.protocol | string | `"TCP"` | Service protocol |
|
||||
| service.ports.app.targetPort | string | `"http"` | Service target port |
|
||||
| service.type | string | `"ClusterIP"` | Service type |
|
||||
| strategyType | string | `"RollingUpdate"` | `strategyType` specifies the strategy used to replace old Pods by new ones. `strategyType` can be `"Recreate"` or `"RollingUpdate"`. `"RollingUpdate"` is the default value and updates Pods in a rolling update fashion. `"Recreate"` will kill all existing Pods before new ones are created. The `"Recreate"` strategy is necessary when persistent volume's `accessMode` is set to `"ReadWriteOnce"` when using `helm upgrade`, as pod volume attachments to an existing PersistentVolumeClaim need to be cleared before a new pod can attach to it. |
|
||||
| tolerations | list | `[]` | Node tolerations for pod scheduling |
|
||||
|
||||
---
|
||||
|
||||
Autogenerated from chart metadata using [helm-docs](https://github.com/norwoodj/helm-docs)
|
||||
@@ -67,6 +67,15 @@ When `homarr.name` is absent, onboarding also understands these Homepage labels:
|
||||
`homepage.name` and `homepage.href` are required for the fallback. Homarr does not combine partial Homarr metadata with
|
||||
Homepage metadata: once `homarr.name` is present, use `homarr.*` labels for the remaining fields too.
|
||||
|
||||
Homepage Dashboard Icons keys such as `sonarr` or `jellyfin.svg` are converted to full icon URLs. Keys are lowercased
|
||||
to match published filenames. Bare keys use PNG;
|
||||
`.png`, `.svg`, and `.webp` select the corresponding format. Explicit URLs and local paths are preserved. Homepage's
|
||||
`mdi-`, `si-`, and `sh-` icon prefixes are not converted; use a full icon URL for those sets. Local paths must also be
|
||||
available on the Homarr server. `homarr.icon` continues to accept an icon URL.
|
||||
|
||||
This applies to new onboarding imports. For apps already imported with broken icons, change their icons in app
|
||||
management.
|
||||
|
||||
## What onboarding creates
|
||||
|
||||
In **Connect**, label-discovered apps and integrations appear before image-based suggestions. You can import multiple
|
||||
|
||||
@@ -22,4 +22,6 @@ The Frigate statistics use `/api/stats` and report camera count, service uptime,
|
||||
|
||||
<AddingIntegration />
|
||||
|
||||
Use Frigate's internal HTTP API (port **5000**), reachable from Homarr. This endpoint does not require credentials; the authenticated web interface on port 8971 is not supported by this integration. Keep the internal API on a trusted network.
|
||||
Use Frigate's authenticated interface (port **8971**) with an admin account's username and password. Homarr exchanges these for a session token and verifies the account before requesting statistics. Frigate's default `frigate_token` cookie name is required. The integration URL must use `https://`; Homarr rejects `http://` URLs so credentials are never sent unencrypted, and certificates must be trusted by Homarr.
|
||||
|
||||
Existing integrations using the unauthenticated port **5000** must update their URL and add credentials. Homarr does not fall back to the unauthenticated API; keep that listener private.
|
||||
@@ -19,6 +19,8 @@ import { mediaReleasesWidget } from "@site/docs/widgets/media-releases";
|
||||
|
||||
<IntegrationCapabilites items={[{ widget: mediaServerWidget }, { widget: mediaReleasesWidget }]} />
|
||||
|
||||
The Media Releases widget shows up to 100 recently added individual items across the libraries accessible to the Jellyfin user used by the integration. Episodes and music tracks appear individually instead of being grouped into seasons or albums. Jellyfin's library access and latest-media preferences still apply.
|
||||
|
||||
### Adding the integration
|
||||
|
||||
<AddingIntegration />
|
||||
|
||||
@@ -19,6 +19,8 @@ import { mediaReleasesWidget } from "@site/docs/widgets/media-releases";
|
||||
|
||||
<IntegrationCapabilites items={[{ widget: mediaServerWidget }, { widget: mediaReleasesWidget }]} />
|
||||
|
||||
The Media releases widget classifies Plex albums, artists, and tracks as Music.
|
||||
|
||||
### Adding the integration
|
||||
|
||||
<AddingIntegration />
|
||||
|
||||
@@ -24,6 +24,12 @@ Therefore it should support CloudKey Gen1, CloudKey Gen2, UnifiOS-based UDM-Pro
|
||||
items={[{ widget: networkControllerSummaryWidget }, { widget: networkControllerStatusWidget }]}
|
||||
/>
|
||||
|
||||
VPN users are counted from active remote VPN sessions across all sites. Controllers that return HTTP 404 for
|
||||
`stat/remoteuservpn` use the legacy health counter instead; a successful empty session list counts as zero.
|
||||
This avoids the stale L2TP health counter on UniFi Network 10.6.106. VPN types absent from that endpoint cannot be
|
||||
counted. If a site's session request fails or returns an invalid response, the VPN user total is shown as unknown
|
||||
while the other network metrics remain available. VPN status still uses the controller's health subsystem.
|
||||
|
||||
### Adding the integration
|
||||
|
||||
<AddingIntegration />
|
||||
|
||||
@@ -38,14 +38,20 @@ Mobile and Base are protected layouts. New boards use three Mobile columns; Base
|
||||
creation. Additional layouts use unique breakpoints. Homarr selects the layout with the highest breakpoint that fits
|
||||
the current viewport.
|
||||
|
||||
Tiles use fixed `200 × 200` logical-pixel cells with a `24px` gap. Homarr scales the complete layout to the available
|
||||
width instead of changing each widget's proportions. **Reset from Base** regenerates a Mobile or custom layout from the
|
||||
The logical grid uses fixed `200 × 200` cells with a `12px` gap. Visible tile spacing also depends on card insets and
|
||||
canvas scaling. Homarr scales the complete layout to the available width instead of changing each widget's proportions. **Reset from Base** regenerates a Mobile or custom layout from the
|
||||
current Base layout.
|
||||
|
||||
Changing column or sidebar counts keeps a saved source arrangement while its generated positions remain unedited.
|
||||
Widening back restores that arrangement, including Container sizes. Moving, resizing, adding, or removing content
|
||||
starts a new source arrangement on the next layout resize. This cannot restore positions lost before this behavior
|
||||
was introduced; widen the affected Container before moving widgets into its right-hand columns.
|
||||
|
||||
### Containers and rails
|
||||
|
||||
A **Container** groups apps, widgets, and nested Containers. It can have a label, collapse control, app-opening action,
|
||||
border color, and CSS classes.
|
||||
border color, and CSS classes. Click anywhere on a collapsible Container’s header to collapse or expand it.
|
||||
The header also supports Enter and Space; its app-opening action and edit controls work independently.
|
||||
|
||||
Items can occupy the outermost rows and columns of a Container. The drop preview shows the position where the item
|
||||
will remain when released.
|
||||
@@ -61,6 +67,9 @@ with enabled sidebars shown beside the main canvas.
|
||||
Enter edit mode from the board header to add, move, resize, configure, or remove content. Save the board to persist the
|
||||
layout.
|
||||
|
||||
Dismissing the setup checklist is remembered per board in the current browser, independently of board contents.
|
||||
Clearing browser site data restores it; dismissal does not sync across browsers.
|
||||
|
||||
- Drag from an item's inactive content area; Containers use their grip.
|
||||
- Hold `Ctrl` on Windows/Linux or `Cmd` on macOS to select several items.
|
||||
- Use **Move / resize item** for another canvas, rail, Container, or exact coordinates.
|
||||
|
||||
@@ -52,6 +52,9 @@ used by widgets that open the service.
|
||||
Integrations can also be created while adding content to a board. Editing a connection from a widget updates that
|
||||
integration everywhere it is used.
|
||||
|
||||
Existing integration IDs are preserved when editing, including IDs containing hyphens from externally seeded data.
|
||||
Changing settings requires full access to that integration; IDs do not grant access.
|
||||
|
||||
## Permissions
|
||||
|
||||
| Level | Allows |
|
||||
|
||||
@@ -5923,6 +5923,134 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/integrations/{id}/test-connection": {
|
||||
"get": {
|
||||
"operationId": "integration-testConnection",
|
||||
"summary": "Test a saved integration using stored credentials",
|
||||
"description": "Requires full integration access. Runs the native connection check without changing saved configuration. Upstream authentication may establish a session. Success is a connection check, not widget or service acceptance. Failures expose only a safe category, not upstream errors or credentials.",
|
||||
"tags": [
|
||||
"integrations"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"apikey": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "id",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean",
|
||||
"const": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"success"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean",
|
||||
"const": false
|
||||
},
|
||||
"error": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"missingSecret",
|
||||
"authorization",
|
||||
"certificate",
|
||||
"request",
|
||||
"statusCode",
|
||||
"parse",
|
||||
"unknown"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"success",
|
||||
"error"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
],
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid input data",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/error.BAD_REQUEST"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Authorization not provided",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/error.UNAUTHORIZED"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Insufficient access",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/error.FORBIDDEN"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/error.NOT_FOUND"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/error.INTERNAL_SERVER_ERROR"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { ActionIcon, Affix, Button, Group, Paper, Progress, Stack, Text, ThemeIcon, Tooltip } from "@mantine/core";
|
||||
import { useLocalStorage } from "@mantine/hooks";
|
||||
import { IconCheck, IconChevronDown, IconChecklist } from "@tabler/icons-react";
|
||||
import { IconCheck, IconChevronDown, IconChecklist, IconX } from "@tabler/icons-react";
|
||||
|
||||
import { useIntegrationsWithUseAccess } from "@homarr/auth/client";
|
||||
import { useRequiredBoard } from "@homarr/boards/context";
|
||||
@@ -22,6 +22,10 @@ export const BoardSetupChecklist = () => {
|
||||
key: `homarr-board-setup-checklist-${board.id}`,
|
||||
defaultValue: false,
|
||||
});
|
||||
const [dismissed, setDismissed] = useLocalStorage({
|
||||
key: `homarr-board-setup-checklist-dismissed-${board.id}`,
|
||||
defaultValue: false,
|
||||
});
|
||||
const t = useI18n("board.setupChecklist");
|
||||
const tCommon = useI18n("common.action");
|
||||
const progress = getBoardSetupProgress({
|
||||
@@ -29,7 +33,7 @@ export const BoardSetupChecklist = () => {
|
||||
usableIntegrationCount: integrations.length,
|
||||
});
|
||||
|
||||
if (!isEditMode || progress.isComplete) return null;
|
||||
if (!isEditMode || progress.isComplete || dismissed) return null;
|
||||
|
||||
if (collapsed) {
|
||||
return (
|
||||
@@ -58,11 +62,18 @@ export const BoardSetupChecklist = () => {
|
||||
{t("description")}
|
||||
</Text>
|
||||
</div>
|
||||
<Tooltip label={t("collapse")}>
|
||||
<ActionIcon variant="subtle" aria-label={t("collapse")} onClick={() => setCollapsed(true)}>
|
||||
<IconChevronDown size={18} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Group gap={4} wrap="nowrap">
|
||||
<Tooltip label={t("collapse")}>
|
||||
<ActionIcon variant="subtle" aria-label={t("collapse")} onClick={() => setCollapsed(true)}>
|
||||
<IconChevronDown size={18} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t("dismiss")}>
|
||||
<ActionIcon variant="subtle" aria-label={t("dismiss")} onClick={() => setDismissed(true)}>
|
||||
<IconX size={18} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
</Group>
|
||||
<Progress
|
||||
value={progress.totalCount === 0 ? 0 : (progress.completedCount / progress.totalCount) * 100}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createContext, useContext } from "react";
|
||||
import { createContext, useContext, useRef } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import { ActionIcon, Badge, Box, Button, Card } from "@mantine/core";
|
||||
import { IconChevronDown, IconChevronUp, IconExternalLink } from "@tabler/icons-react";
|
||||
@@ -33,6 +33,7 @@ interface Props {
|
||||
|
||||
export const BoardContainerSection = ({ section }: Props) => {
|
||||
const board = useRequiredBoard();
|
||||
const togglePointer = useRef<{ x: number; y: number; moved: boolean } | null>(null);
|
||||
const containerDepth = useContext(ContainerDepthContext);
|
||||
// Stagger nested controls inward and downward without wrapping back over ancestors.
|
||||
const menuRightOffset = 4 + containerDepth * 32;
|
||||
@@ -73,15 +74,15 @@ export const BoardContainerSection = ({ section }: Props) => {
|
||||
labelMaxWidth = `calc(100% - ${labelLeft}px - ${reservedMenuWidth})`;
|
||||
collapsedLabelPaddingRight = reservedMenuWidth;
|
||||
}
|
||||
// Expanded controls sit on the border without reserving a header row.
|
||||
// The native toggle spans the header; sibling actions stay above it.
|
||||
const toggleLayout = isVisuallyCollapsed
|
||||
? { top: 0, left: 0, w: "100%", h: "100%", maw: "100%" }
|
||||
: {
|
||||
top: labelTop,
|
||||
left: labelLeft,
|
||||
w: "auto",
|
||||
left: 0,
|
||||
w: "100%",
|
||||
h: 20,
|
||||
maw: labelMaxWidth,
|
||||
maw: "100%",
|
||||
};
|
||||
const toggleIcon = isVisuallyCollapsed ? (
|
||||
<IconChevronDown size="var(--mantine-font-size-md)" />
|
||||
@@ -125,13 +126,29 @@ export const BoardContainerSection = ({ section }: Props) => {
|
||||
pos="absolute"
|
||||
{...toggleLayout}
|
||||
px={6}
|
||||
ps={isVisuallyCollapsed ? labelLeft : 6}
|
||||
pe={isVisuallyCollapsed ? collapsedLabelPaddingRight : 6}
|
||||
ps={labelLeft}
|
||||
pe={collapsedLabelPaddingRight}
|
||||
radius="sm"
|
||||
variant="default"
|
||||
justify={options.showLabel ? "flex-start" : "center"}
|
||||
leftSection={options.showLabel && toggleIcon}
|
||||
onClick={toggle}
|
||||
onPointerDown={(event) => {
|
||||
togglePointer.current = { x: event.clientX, y: event.clientY, moved: false };
|
||||
}}
|
||||
onPointerMove={(event) => {
|
||||
const pointer = togglePointer.current;
|
||||
if (!pointer || event.buttons === 0) return;
|
||||
if (Math.hypot(event.clientX - pointer.x, event.clientY - pointer.y) > 5) {
|
||||
pointer.moved = true;
|
||||
}
|
||||
}}
|
||||
onPointerCancel={() => {
|
||||
togglePointer.current = null;
|
||||
}}
|
||||
onClick={(event) => {
|
||||
if (event.detail > 0 && togglePointer.current?.moved) return;
|
||||
toggle();
|
||||
}}
|
||||
aria-expanded={!isVisuallyCollapsed}
|
||||
aria-controls={contentId}
|
||||
aria-label={`${t(isVisuallyCollapsed ? "action.expand" : "action.collapse")}: ${label}`}
|
||||
|
||||
+253
-22
@@ -22,7 +22,7 @@
|
||||
<a href="https://demo.homarr.dev">
|
||||
<strong>Live Demo 🎮</strong>
|
||||
</a> •
|
||||
<a href="https://homarr.dev/docs/category/installation-1/">
|
||||
<a href="https://homarr.dev/docs/getting-started/">
|
||||
<strong>Install 💻</strong>
|
||||
</a> •
|
||||
<a href="https://crowdin.com/project/homarr_labs">
|
||||
@@ -95,20 +95,27 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/autobrr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/autobrr.svg" alt="Autobrr" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Autobrr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/bazarr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/bazarr.svg" alt="Bazarr" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Bazarr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/beszel" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/beszel.svg" alt="Beszel" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Beszel</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/bindery" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/bindery.svg" alt="Bindery" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -116,6 +123,20 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/caddy" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/caddy.svg" alt="Caddy" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Caddy</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/changedetection" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/changedetection.svg" alt="Changedetection.io" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Changedetection.io</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/coolify" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/coolify.svg" alt="Coolify" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -135,8 +156,8 @@
|
||||
<br/>
|
||||
<p align="center">Deluge</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/emby" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/emby.svg" alt="Emby" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -144,6 +165,27 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/fileflows" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/fileflows.svg" alt="FileFlows" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">FileFlows</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/frigate" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/frigate.svg" alt="Frigate" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Frigate</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/gatus" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/gatus.svg" alt="Gatus" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Gatus</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/glances" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/glances.svg" alt="Glances" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -156,13 +198,20 @@
|
||||
<br/>
|
||||
<p align="center">Gluetun</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/gotify" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/gotify.svg" alt="Gotify" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Gotify</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/healthchecks" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/healthchecks.svg" alt="Healthchecks" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Healthchecks</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/home-assistant" target="_blank" rel="noreferrer noopener">
|
||||
@@ -172,6 +221,13 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/homebox" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/homebox.svg" alt="Homebox" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Homebox</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/ical" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/ical.svg" alt="iCal" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -186,13 +242,20 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/jackett" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jackett.svg" alt="Jackett" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Jackett</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/jellyfin" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jellyfin.svg" alt="Jellyfin" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Jellyfin</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/jellyseerr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jellyseerr.svg" alt="Jellyseerr" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -200,13 +263,69 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/jellystat" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/jellystat.svg" alt="Jellystat" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Jellystat</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/karakeep" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/karakeep.svg" alt="Karakeep" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Karakeep</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/komga" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/komga.svg" alt="Komga" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Komga</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/lidarr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/lidarr.svg" alt="Lidarr" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Lidarr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/linkwarden" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/linkwarden.png" alt="Linkwarden" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Linkwarden</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/llama-cpp" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/selfhst/icons/svg/llama-cpp.svg" alt="llama.cpp" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">llama.cpp</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/maintainerr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/maintainerr.svg" alt="Maintainerr" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Maintainerr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/mealie" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/mealie.svg" alt="Mealie" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Mealie</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/miniflux" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/miniflux.svg" alt="Miniflux" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Miniflux</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/navidrome" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/navidrome.svg" alt="Navidrome" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -214,13 +333,27 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/netalertx" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/netalertx.svg" alt="NetAlertX" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">NetAlertX</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/netdata" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/netdata.svg" alt="Netdata" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Netdata</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/nextcloud" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/nextcloud.svg" alt="Nextcloud" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Nextcloud</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/ntfy" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/ntfy.svg" alt="ntfy" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -254,8 +387,8 @@
|
||||
<br/>
|
||||
<p align="center">Overseerr</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/paperless-ngx" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/paperless-ngx.svg" alt="Paperless-ngx" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -268,8 +401,8 @@
|
||||
<br/>
|
||||
<p align="center">PatchMon</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/peanut" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/peanut.svg" alt="PeaNUT" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -284,6 +417,13 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/plantit" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/plant-it.png" alt="Plant-it" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Plant-it</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/plex" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/plex.svg" alt="Plex" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -291,6 +431,13 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/prometheus" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/prometheus.svg" alt="Prometheus" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Prometheus</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/prowlarr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/prowlarr.svg" alt="Prowlarr" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -326,6 +473,13 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/romm" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/romm.svg" alt="RomM" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">RomM</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/sabnzbd" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/sabnzbd.svg" alt="SABnzbd" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -333,13 +487,20 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/scrutiny" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/scrutiny.svg" alt="Scrutiny" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Scrutiny</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/seerr" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/seerr.svg" alt="Seerr" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Seerr</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/slskd" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/slskd.svg" alt="Slskd" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -352,8 +513,8 @@
|
||||
<br/>
|
||||
<p align="center">Sonarr</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/speedtest-tracker" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/speedtest-tracker.png" alt="Speedtest Tracker" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -361,11 +522,39 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/spoolman" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/spoolman.svg" alt="Spoolman" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Spoolman</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/stash" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/stash.svg" alt="Stash" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Stash</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/syncthing-relay" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/syncthing.svg" alt="Syncthing Relay" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Syncthing<br/>Relay</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/synology" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/synology.svg" alt="Synology DiskStation" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Synology<br/>DiskStation</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/tandoor" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/tandoor-recipes.svg" alt="Tandoor" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Tandoor</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/tdarr" target="_blank" rel="noreferrer noopener">
|
||||
@@ -401,6 +590,13 @@
|
||||
<br/>
|
||||
<p align="center">Transmission</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/trilium" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/trilium.svg" alt="Trilium" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Trilium</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/truenas" target="_blank" rel="noreferrer noopener">
|
||||
@@ -410,6 +606,13 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/tubearchivist" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/tube-archivist.png" alt="Tube Archivist" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Tube<br/>Archivist</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/umami" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/umami.svg" alt="Umami" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -424,6 +627,13 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/unmanic" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/unmanic.png" alt="Unmanic" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Unmanic</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/unraid" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/unraid.svg" alt="Unraid" width="90" height="90" />
|
||||
<br/>
|
||||
@@ -436,6 +646,13 @@
|
||||
<br/>
|
||||
<p align="center">Uptime<br/>Kuma</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/wazuh" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/wazuh.svg" alt="Wazuh" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Wazuh</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/whats-up-docker" target="_blank" rel="noreferrer noopener">
|
||||
@@ -443,6 +660,20 @@
|
||||
<br/>
|
||||
<p align="center">What's<br/>Up<br/>Docker</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/xteve" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/xteve.png" alt="xTeVe" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">xTeVe</p>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://homarr.dev/docs/integrations/your-spotify" target="_blank" rel="noreferrer noopener">
|
||||
<img src="https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/your-spotify.svg" alt="Your Spotify" width="90" height="90" />
|
||||
<br/>
|
||||
<p align="center">Your<br/>Spotify</p>
|
||||
</a>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -456,7 +687,7 @@
|
||||

|
||||
|
||||
<h2>
|
||||
<a href="https://homarr.dev/docs/category/installation-1/">
|
||||
<a href="https://homarr.dev/docs/getting-started/">
|
||||
Please click here for official installation instructions
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { API_KEY_HEADER_NAME } from "@homarr/auth/api-key";
|
||||
import { appRouter } from "./router/app";
|
||||
import { boardRouter } from "./router/board";
|
||||
import { integrationRequestProcedure } from "./router/integration/integration-request";
|
||||
import { integrationTestStoredConnectionProcedure } from "./router/integration/integration-test-stored-connection";
|
||||
import { infoRouter } from "./router/info";
|
||||
import { inviteRouter } from "./router/invite";
|
||||
import { serverSettingsRouter } from "./router/serverSettings";
|
||||
@@ -20,7 +21,10 @@ export const openApiRouter = createTRPCRouter({
|
||||
inviteRouter,
|
||||
serverSettingsRouter,
|
||||
userRouter,
|
||||
integration: createTRPCRouter({ request: integrationRequestProcedure }),
|
||||
integration: createTRPCRouter({
|
||||
request: integrationRequestProcedure,
|
||||
testConnection: integrationTestStoredConnectionProcedure,
|
||||
}),
|
||||
});
|
||||
|
||||
export const openApiDocument = (base: string) => {
|
||||
|
||||
@@ -4,7 +4,12 @@ import { z } from "zod/v4";
|
||||
|
||||
import type { Session } from "@homarr/auth";
|
||||
import { constructBoardPermissions } from "@homarr/auth/shared";
|
||||
import { createId, generateResponsiveGridFor } from "@homarr/common";
|
||||
import {
|
||||
createId,
|
||||
generateResponsiveGridFor,
|
||||
getLayoutResizeSource,
|
||||
serializeLayoutResizeSource,
|
||||
} from "@homarr/common";
|
||||
import type { GridAlgorithmItem } from "@homarr/common";
|
||||
import type { DeviceType } from "@homarr/common/server";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
@@ -864,6 +869,7 @@ export const boardRouter = createTRPCRouter({
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
id: layoutsMap.get(layout.id)!,
|
||||
boardId: newBoardId,
|
||||
resizeSource: null,
|
||||
}));
|
||||
|
||||
const sectionMap = new Map<string, string>(boardSections.map((section) => [section.id, createId()]));
|
||||
@@ -1358,6 +1364,7 @@ export const boardRouter = createTRPCRouter({
|
||||
const itemSectionLayoutsToUpdate: InferInsertModel<typeof itemLayouts>[] = [];
|
||||
const sectionLayoutsToUpdate: InferInsertModel<typeof sectionLayouts>[] = [];
|
||||
const layoutsToUpdate = filterUpdatedItems(input.layouts, board.layouts);
|
||||
const resizeSourcesToUpdate = new Map<string, string | null>();
|
||||
|
||||
for (const updatedLayout of layoutsToUpdate) {
|
||||
const dbLayout = existingLayoutsById.get(updatedLayout.id);
|
||||
@@ -1386,6 +1393,7 @@ export const boardRouter = createTRPCRouter({
|
||||
rightGutterColumnCount: updatedLayout.rightGutterColumnCount,
|
||||
},
|
||||
});
|
||||
resizeSourcesToUpdate.set(updatedLayout.id, projectedLayout.resizeSource);
|
||||
itemSectionLayoutsToUpdate.push(...projectedLayout.itemSectionLayouts);
|
||||
sectionLayoutsToUpdate.push(...projectedLayout.sectionLayouts);
|
||||
}
|
||||
@@ -1441,6 +1449,7 @@ export const boardRouter = createTRPCRouter({
|
||||
await transaction
|
||||
.update(schema.layouts)
|
||||
.set({
|
||||
...getResizeSourceUpdate(resizeSourcesToUpdate, layout.id),
|
||||
name: layout.name,
|
||||
columnCount: layout.columnCount,
|
||||
leftGutterColumnCount: layout.leftGutterColumnCount,
|
||||
@@ -1499,6 +1508,7 @@ export const boardRouter = createTRPCRouter({
|
||||
transaction
|
||||
.update(layouts)
|
||||
.set({
|
||||
...getResizeSourceUpdate(resizeSourcesToUpdate, layout.id),
|
||||
name: layout.name,
|
||||
columnCount: layout.columnCount,
|
||||
leftGutterColumnCount: layout.leftGutterColumnCount,
|
||||
@@ -1564,6 +1574,10 @@ export const boardRouter = createTRPCRouter({
|
||||
await handleTransactionsAsync(ctx.db, {
|
||||
async handleAsync(db, schema) {
|
||||
await db.transaction(async (transaction) => {
|
||||
await transaction
|
||||
.update(schema.layouts)
|
||||
.set({ resizeSource: null })
|
||||
.where(eq(schema.layouts.id, targetLayout.id));
|
||||
await transaction.delete(schema.itemLayouts).where(eq(schema.itemLayouts.layoutId, targetLayout.id));
|
||||
await transaction.delete(schema.sectionLayouts).where(eq(schema.sectionLayouts.layoutId, targetLayout.id));
|
||||
if (projectedLayout.itemSectionLayouts.length > 0) {
|
||||
@@ -1576,6 +1590,7 @@ export const boardRouter = createTRPCRouter({
|
||||
},
|
||||
handleSync(db) {
|
||||
db.transaction((transaction) => {
|
||||
transaction.update(layouts).set({ resizeSource: null }).where(eq(layouts.id, targetLayout.id)).run();
|
||||
transaction.delete(itemLayouts).where(eq(itemLayouts.layoutId, targetLayout.id)).run();
|
||||
transaction.delete(sectionLayouts).where(eq(sectionLayouts.layoutId, targetLayout.id)).run();
|
||||
if (projectedLayout.itemSectionLayouts.length > 0) {
|
||||
@@ -2511,8 +2526,14 @@ const noBoardWithSimilarNameAsync = async (db: Database, name: string, ignoredId
|
||||
}
|
||||
};
|
||||
|
||||
const getResizeSourceUpdate = (sources: ReadonlyMap<string, string | null>, layoutId: string) => {
|
||||
if (!sources.has(layoutId)) return {};
|
||||
return { resizeSource: sources.get(layoutId) ?? null };
|
||||
};
|
||||
|
||||
interface BoardForLayoutProjection {
|
||||
id: string;
|
||||
layouts: Array<{ id: string; resizeSource?: string | null }>;
|
||||
items: Array<{
|
||||
id: string;
|
||||
layouts: Array<{
|
||||
@@ -2547,7 +2568,13 @@ const getUpdatedBoardLayout = (
|
||||
current: BoardLayoutGeometry;
|
||||
},
|
||||
) => {
|
||||
const elements = options.previous.elements ?? getElementsForLayout(board, options.previous.layoutId);
|
||||
const currentElements = options.previous.elements ?? getElementsForLayout(board, options.previous.layoutId);
|
||||
const savedLayout = board.layouts.find((layout) => layout.id === options.previous.layoutId);
|
||||
let serializedSource: string | null | undefined;
|
||||
if (options.previous.layoutId === options.current.layoutId) serializedSource = savedLayout?.resizeSource;
|
||||
const source = getLayoutResizeSource(options.previous, currentElements, serializedSource);
|
||||
const elements = source.elements;
|
||||
const previousGeometry = { ...source.geometry, layoutId: options.previous.layoutId };
|
||||
const emptyRoots = board.sections
|
||||
.filter((section) => section.kind === "empty")
|
||||
.toSorted((first, second) => (first.yOffset ?? 0) - (second.yOffset ?? 0) || first.id.localeCompare(second.id));
|
||||
@@ -2586,12 +2613,12 @@ const getUpdatedBoardLayout = (
|
||||
const sourceLanes = boardLanes.filter(
|
||||
(sourceLane) =>
|
||||
rootByLane.has(sourceLane) &&
|
||||
getBoardLaneColumnCount(options.previous, sourceLane) > 0 &&
|
||||
getBoardLaneColumnCount(previousGeometry, sourceLane) > 0 &&
|
||||
targetLaneBySourceLane.get(sourceLane) === lane,
|
||||
);
|
||||
const previousWidth =
|
||||
sourceLanes.length === 1
|
||||
? getBoardLaneColumnCount(options.previous, sourceLanes[0] ?? "main")
|
||||
? getBoardLaneColumnCount(previousGeometry, sourceLanes[0] ?? "main")
|
||||
: Number.MAX_SAFE_INTEGER;
|
||||
|
||||
return [
|
||||
@@ -2644,6 +2671,7 @@ const getUpdatedBoardLayout = (
|
||||
return {
|
||||
itemSectionLayouts: itemSectionLayoutsCollection,
|
||||
sectionLayouts: sectionLayoutsCollection,
|
||||
resizeSource: serializeLayoutResizeSource(source, options.current, updatedElements),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2948,7 +2976,11 @@ const getFullBoardWithWhereAsync = async (
|
||||
return {
|
||||
...otherBoardProperties,
|
||||
layouts: layouts
|
||||
.map(({ boardId: _, ...layout }) => layout)
|
||||
.map(({ boardId: _, resizeSource, ...layout }) => {
|
||||
const result: typeof layout & { resizeSource?: string | null } = layout;
|
||||
if (resizeSource) result.resizeSource = resizeSource;
|
||||
return result;
|
||||
})
|
||||
.toSorted((layoutA, layoutB) => layoutA.breakpoint - layoutB.breakpoint),
|
||||
sections: sections.map(({ collapseStates, ...section }) =>
|
||||
parseSection({
|
||||
|
||||
@@ -44,6 +44,7 @@ import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure, publ
|
||||
import { throwIfActionForbiddenAsync } from "./integration-access";
|
||||
import { integrationRequestProcedure } from "./integration-request";
|
||||
import { MissingSecretError, testConnectionAsync } from "./integration-test-connection";
|
||||
import { integrationTestStoredConnectionProcedure } from "./integration-test-stored-connection";
|
||||
import { mapTestConnectionError } from "./map-test-connection-error";
|
||||
|
||||
const logger = createLogger({ module: "integrationRouter" });
|
||||
@@ -51,6 +52,7 @@ const mediaRequestSearchKinds = getIntegrationKindsByCategory("mediaSearch");
|
||||
|
||||
export const integrationRouter = createTRPCRouter({
|
||||
request: integrationRequestProcedure,
|
||||
testConnection: integrationTestStoredConnectionProcedure,
|
||||
getKinds: publicProcedure
|
||||
.meta({
|
||||
mcp: {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { eq } from "@homarr/db";
|
||||
import { integrations } from "@homarr/db/schema";
|
||||
import { byIdSchema } from "@homarr/validation/common";
|
||||
|
||||
import { protectedProcedure } from "../../trpc";
|
||||
import { throwIfActionForbiddenAsync } from "./integration-access";
|
||||
import { MissingSecretError, testConnectionAsync } from "./integration-test-connection";
|
||||
|
||||
export const integrationTestStoredConnectionProcedure = protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: "GET",
|
||||
path: "/api/integrations/{id}/test-connection",
|
||||
tags: ["integrations"],
|
||||
protect: true,
|
||||
summary: "Test a saved integration using stored credentials",
|
||||
description:
|
||||
"Requires full integration access. Runs the native connection check without changing saved configuration. Upstream authentication may establish a session. Success is a connection check, not widget or service acceptance. Failures expose only a safe category, not upstream errors or credentials.",
|
||||
},
|
||||
})
|
||||
.input(byIdSchema)
|
||||
.output(
|
||||
z.discriminatedUnion("success", [
|
||||
z.object({ success: z.literal(true) }),
|
||||
z.object({
|
||||
success: z.literal(false),
|
||||
error: z.enum(["missingSecret", "authorization", "certificate", "request", "statusCode", "parse", "unknown"]),
|
||||
}),
|
||||
]),
|
||||
)
|
||||
.query(async ({ ctx, input }) => {
|
||||
const where = eq(integrations.id, input.id);
|
||||
await throwIfActionForbiddenAsync(ctx, where, "full");
|
||||
|
||||
const integration = await ctx.db.query.integrations.findFirst({ where, with: { secrets: true } });
|
||||
if (!integration) {
|
||||
throw new TRPCError({ code: "NOT_FOUND", message: "Integration not found" });
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await testConnectionAsync(
|
||||
{
|
||||
...integration,
|
||||
secrets: integration.secrets.map(({ kind }) => ({ kind, value: null })),
|
||||
},
|
||||
integration.secrets,
|
||||
);
|
||||
if (result.success) return { success: true as const };
|
||||
return { success: false as const, error: result.error.type };
|
||||
} catch (error) {
|
||||
if (error instanceof MissingSecretError) return { success: false as const, error: "missingSecret" as const };
|
||||
return { success: false as const, error: "unknown" as const };
|
||||
}
|
||||
});
|
||||
@@ -8,7 +8,7 @@ import { createId } from "@homarr/common";
|
||||
import { encryptSecret } from "@homarr/common/server";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
|
||||
import { handleTransactionsAsync, eq, inArray, like, or } from "@homarr/db";
|
||||
import { handleTransactionsAsync, and, eq, inArray, like, notExists, or } from "@homarr/db";
|
||||
import { getServerSettingByKeyAsync } from "@homarr/db/queries";
|
||||
import {
|
||||
apps,
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
sectionLayouts,
|
||||
sections,
|
||||
serverSettings,
|
||||
users,
|
||||
} from "@homarr/db/schema";
|
||||
import {
|
||||
emptySuperJSON,
|
||||
@@ -147,6 +148,23 @@ export const onboardRouter = createTRPCRouter({
|
||||
await nextOnboardingStepAsync(ctx.db);
|
||||
}),
|
||||
|
||||
returnToWelcome: onboardingProcedure.requiresStep("user").mutation(async ({ ctx }) => {
|
||||
const returnedRows = await ctx.db
|
||||
.update(onboarding)
|
||||
.set({ step: "start", previousStep: null })
|
||||
.where(
|
||||
and(
|
||||
eq(onboarding.step, "user"),
|
||||
eq(onboarding.previousStep, "start"),
|
||||
notExists(ctx.db.select({ id: users.id }).from(users)),
|
||||
),
|
||||
)
|
||||
.returning({ id: onboarding.id });
|
||||
if (returnedRows.length !== 1) {
|
||||
throw new TRPCError({ code: "PRECONDITION_FAILED", message: "Administrator setup can no longer be restarted." });
|
||||
}
|
||||
}),
|
||||
|
||||
testIntegration: onboardingProcedure
|
||||
.requiresStep("setup")
|
||||
.input(onboardingIntegrationDraftSchema)
|
||||
@@ -352,6 +370,7 @@ export const onboardRouter = createTRPCRouter({
|
||||
leftGutterColumnCount: 0,
|
||||
rightGutterColumnCount: 0,
|
||||
breakpoint: 0,
|
||||
resizeSource: null,
|
||||
role: "mobile" as const,
|
||||
},
|
||||
{
|
||||
@@ -362,6 +381,7 @@ export const onboardRouter = createTRPCRouter({
|
||||
leftGutterColumnCount: 0,
|
||||
rightGutterColumnCount: 0,
|
||||
breakpoint: 768,
|
||||
resizeSource: null,
|
||||
role: "base" as const,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { beforeEach, expect, test, vi } from "vitest";
|
||||
|
||||
import type { Session } from "@homarr/auth";
|
||||
import { encryptSecret } from "@homarr/common/server";
|
||||
import { integrations, integrationSecrets } from "@homarr/db/schema";
|
||||
import { createDb } from "@homarr/db/test";
|
||||
|
||||
import { integrationTestStoredConnectionProcedure } from "../../integration/integration-test-stored-connection";
|
||||
import { createTRPCRouter } from "../../../trpc";
|
||||
|
||||
const { factory, upstreamTest } = vi.hoisted(() => ({ factory: vi.fn(), upstreamTest: vi.fn() }));
|
||||
vi.mock("@homarr/integrations/factory", () => ({ createIntegrationAsync: factory }));
|
||||
|
||||
beforeEach(() => {
|
||||
factory.mockReset().mockResolvedValue({ testConnectionAsync: upstreamTest });
|
||||
upstreamTest.mockReset().mockResolvedValue({ success: true });
|
||||
});
|
||||
|
||||
const setup = async (permissions: Session["user"]["permissions"] = ["integration-full-all"]) => {
|
||||
const db = createDb();
|
||||
await db.insert(integrations).values({ id: "saved", name: "Sonarr", kind: "sonarr", url: "http://sonarr:8989" });
|
||||
await db
|
||||
.insert(integrationSecrets)
|
||||
.values({ integrationId: "saved", kind: "apiKey", value: encryptSecret("private-key") });
|
||||
const session: Session = {
|
||||
user: { id: "owner", permissions, colorScheme: "light" },
|
||||
expires: new Date().toISOString(),
|
||||
};
|
||||
const router = createTRPCRouter({ testConnection: integrationTestStoredConnectionProcedure });
|
||||
return { db, router, caller: router.createCaller({ db, session, deviceType: undefined }) };
|
||||
};
|
||||
|
||||
test("tests stored credentials without modifying saved configuration", async () => {
|
||||
const { db, caller } = await setup();
|
||||
const before = await db.query.integrations.findFirst({ with: { secrets: true } });
|
||||
expect(await caller.testConnection({ id: "saved" })).toEqual({ success: true });
|
||||
expect(factory).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: "saved",
|
||||
decryptedSecrets: [expect.objectContaining({ kind: "apiKey", value: "private-key" })],
|
||||
}),
|
||||
);
|
||||
expect(await db.query.integrations.findFirst({ with: { secrets: true } })).toEqual(before);
|
||||
});
|
||||
|
||||
test("denies use-only access and hides missing integrations before loading secrets", async () => {
|
||||
const { caller } = await setup(["integration-use-all"]);
|
||||
await expect(caller.testConnection({ id: "saved" })).rejects.toMatchObject({ code: "NOT_FOUND" });
|
||||
await expect(caller.testConnection({ id: "missing" })).rejects.toMatchObject({ code: "NOT_FOUND" });
|
||||
expect(factory).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("requires authentication", async () => {
|
||||
const { db, router } = await setup();
|
||||
const caller = router.createCaller({ db, session: null, deviceType: undefined });
|
||||
await expect(caller.testConnection({ id: "saved" })).rejects.toMatchObject({ code: "UNAUTHORIZED" });
|
||||
expect(factory).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("returns only safe categories for upstream failures and thrown errors", async () => {
|
||||
const { caller } = await setup();
|
||||
upstreamTest.mockResolvedValueOnce({
|
||||
success: false,
|
||||
error: {
|
||||
type: "authorization",
|
||||
message: "private-key",
|
||||
data: { token: "private-key" },
|
||||
cause: new Error("private-key"),
|
||||
},
|
||||
});
|
||||
expect(await caller.testConnection({ id: "saved" })).toEqual({ success: false, error: "authorization" });
|
||||
factory.mockRejectedValueOnce(new Error("private-key"));
|
||||
expect(await caller.testConnection({ id: "saved" })).toEqual({ success: false, error: "unknown" });
|
||||
const { db } = await setup();
|
||||
await db.delete(integrationSecrets);
|
||||
const router = createTRPCRouter({ testConnection: integrationTestStoredConnectionProcedure });
|
||||
const noSecretsCaller = router.createCaller({
|
||||
db,
|
||||
session: {
|
||||
user: { id: "owner", permissions: ["integration-full-all"], colorScheme: "light" },
|
||||
expires: new Date().toISOString(),
|
||||
},
|
||||
deviceType: undefined,
|
||||
});
|
||||
expect(await noSecretsCaller.testConnection({ id: "saved" })).toEqual({ success: false, error: "missingSecret" });
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { generateResponsiveGridFor } from "@homarr/common";
|
||||
import { generateResponsiveGridFor, getLayoutResizeSource } from "@homarr/common";
|
||||
import type { GridAlgorithmItem } from "@homarr/common";
|
||||
import type { BoardLane, LayoutRole, WidgetKind } from "@homarr/definitions";
|
||||
import { boardLanes, getBoardLaneColumnCount, getRootSectionLane } from "@homarr/definitions";
|
||||
@@ -10,6 +10,7 @@ export interface BoardPreviewLayout {
|
||||
rightGutterColumnCount: number;
|
||||
breakpoint: number;
|
||||
role: LayoutRole;
|
||||
resizeSource?: string | null;
|
||||
}
|
||||
|
||||
interface BoardPreviewItemLayout {
|
||||
@@ -77,7 +78,13 @@ export const projectBoardLayout = (
|
||||
sourceLayout: LayoutGeometry,
|
||||
targetLayout: LayoutGeometry,
|
||||
) => {
|
||||
const elements = getElementsForLayout(board, sourceLayout.id);
|
||||
const currentElements = getElementsForLayout(board, sourceLayout.id);
|
||||
let serializedSource: string | null | undefined;
|
||||
if (sourceLayout.id === targetLayout.id) {
|
||||
serializedSource = board.layouts.find((layout) => layout.id === sourceLayout.id)?.resizeSource;
|
||||
}
|
||||
const source = getLayoutResizeSource(sourceLayout, currentElements, serializedSource);
|
||||
const elements = source.elements;
|
||||
|
||||
if (
|
||||
sourceLayout.id === targetLayout.id &&
|
||||
@@ -85,7 +92,7 @@ export const projectBoardLayout = (
|
||||
sourceLayout.leftGutterColumnCount === targetLayout.leftGutterColumnCount &&
|
||||
sourceLayout.rightGutterColumnCount === targetLayout.rightGutterColumnCount
|
||||
) {
|
||||
return elements;
|
||||
return currentElements;
|
||||
}
|
||||
|
||||
const emptyRoots = board.sections
|
||||
@@ -124,12 +131,12 @@ export const projectBoardLayout = (
|
||||
const sourceLanes = boardLanes.filter(
|
||||
(sourceLane) =>
|
||||
rootByLane.has(sourceLane) &&
|
||||
getBoardLaneColumnCount(sourceLayout, sourceLane) > 0 &&
|
||||
getBoardLaneColumnCount(source.geometry, sourceLane) > 0 &&
|
||||
targetLaneBySourceLane.get(sourceLane) === lane,
|
||||
);
|
||||
const previousWidth =
|
||||
sourceLanes.length === 1
|
||||
? getBoardLaneColumnCount(sourceLayout, sourceLanes[0] ?? "main")
|
||||
? getBoardLaneColumnCount(source.geometry, sourceLanes[0] ?? "main")
|
||||
: Number.MAX_SAFE_INTEGER;
|
||||
|
||||
return generateResponsiveGridFor({
|
||||
|
||||
@@ -12,3 +12,5 @@ export * from "./error";
|
||||
export * from "./theme";
|
||||
export * from "./function";
|
||||
export * from "./grid-algorithm";
|
||||
|
||||
export * from "./layout-resize-source";
|
||||
@@ -0,0 +1,79 @@
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import type { GridAlgorithmItem } from "./grid-algorithm";
|
||||
|
||||
const geometrySchema = z.object({
|
||||
columnCount: z.number().int().positive(),
|
||||
leftGutterColumnCount: z.number().int().nonnegative(),
|
||||
rightGutterColumnCount: z.number().int().nonnegative(),
|
||||
});
|
||||
const elementSchema = z.object({
|
||||
id: z.string(),
|
||||
type: z.enum(["item", "section"]),
|
||||
sectionId: z.string(),
|
||||
xOffset: z.number().int().nonnegative(),
|
||||
yOffset: z.number().int().nonnegative(),
|
||||
width: z.number().int().positive(),
|
||||
height: z.number().int().positive(),
|
||||
});
|
||||
const resizeSourceSchema = z.object({
|
||||
geometry: geometrySchema,
|
||||
elements: z.array(elementSchema),
|
||||
projectedGeometry: geometrySchema,
|
||||
projectedElements: z.array(elementSchema),
|
||||
});
|
||||
|
||||
type LayoutGeometry = z.infer<typeof geometrySchema>;
|
||||
export interface LayoutResizeSource {
|
||||
geometry: LayoutGeometry;
|
||||
elements: GridAlgorithmItem[];
|
||||
}
|
||||
|
||||
const geometryKey = (geometry: LayoutGeometry) =>
|
||||
JSON.stringify([geometry.columnCount, geometry.leftGutterColumnCount, geometry.rightGutterColumnCount]);
|
||||
const elementsKey = (elements: readonly GridAlgorithmItem[]) =>
|
||||
JSON.stringify(
|
||||
elements
|
||||
.map((element) => [
|
||||
element.id,
|
||||
element.type,
|
||||
element.sectionId,
|
||||
element.xOffset,
|
||||
element.yOffset,
|
||||
element.width,
|
||||
element.height,
|
||||
])
|
||||
.toSorted((first, second) => String(first[0]).localeCompare(String(second[0]))),
|
||||
);
|
||||
|
||||
/** Keep the pre-resize arrangement only while the projected placements remain untouched. */
|
||||
export const getLayoutResizeSource = (
|
||||
geometry: LayoutGeometry,
|
||||
elements: GridAlgorithmItem[],
|
||||
serializedSource?: string | null,
|
||||
): LayoutResizeSource => {
|
||||
if (serializedSource) {
|
||||
try {
|
||||
const parsed = resizeSourceSchema.safeParse(JSON.parse(serializedSource));
|
||||
if (
|
||||
parsed.success &&
|
||||
geometryKey(parsed.data.projectedGeometry) === geometryKey(geometry) &&
|
||||
elementsKey(parsed.data.projectedElements) === elementsKey(elements)
|
||||
) {
|
||||
return { geometry: parsed.data.geometry, elements: parsed.data.elements };
|
||||
}
|
||||
} catch {
|
||||
// Malformed or obsolete history must never prevent resizing the current layout.
|
||||
}
|
||||
}
|
||||
return { geometry, elements };
|
||||
};
|
||||
|
||||
export const serializeLayoutResizeSource = (
|
||||
source: LayoutResizeSource,
|
||||
projectedGeometry: LayoutGeometry,
|
||||
projectedElements: GridAlgorithmItem[],
|
||||
): string | null => {
|
||||
if (geometryKey(source.geometry) === geometryKey(projectedGeometry)) return null;
|
||||
return JSON.stringify({ ...source, projectedGeometry, projectedElements });
|
||||
};
|
||||
@@ -2,7 +2,7 @@ import { createBooleanSchema, createEnv } from "../env";
|
||||
|
||||
export const dnsEnv = createEnv({
|
||||
server: {
|
||||
ENABLE_DNS_CACHING: createBooleanSchema(true),
|
||||
ENABLE_DNS_CACHING: createBooleanSchema(false),
|
||||
},
|
||||
experimental__runtimeEnv: process.env,
|
||||
});
|
||||
@@ -14,15 +14,15 @@ declare global {
|
||||
|
||||
const logger = createLogger({ module: "dns" });
|
||||
|
||||
// Initialize global.homarr if not present
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
global.homarr ??= {};
|
||||
global.homarr.dnsCacheManager ??= new DnsCacheManager({
|
||||
cacheMaxEntries: 1000,
|
||||
forceMinTtl: 5 * 60 * 1000, // 5 minutes
|
||||
logger,
|
||||
});
|
||||
|
||||
if (dnsEnv.ENABLE_DNS_CACHING) {
|
||||
// Opt-in only: cached addresses can outlive Docker service endpoints, and
|
||||
// DNS-first resolution does not preserve /etc/hosts precedence.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
global.homarr ??= {};
|
||||
global.homarr.dnsCacheManager ??= new DnsCacheManager({
|
||||
cacheMaxEntries: 1000,
|
||||
forceMinTtl: 5 * 60 * 1000, // 5 minutes
|
||||
logger,
|
||||
});
|
||||
global.homarr.dnsCacheManager.initialize();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// @vitest-environment node
|
||||
|
||||
import dns from "node:dns";
|
||||
import { afterEach, beforeEach, expect, test, vi } from "vitest";
|
||||
|
||||
vi.mock("@homarr/core/infrastructure/logs", () => ({
|
||||
createLogger: () => ({ debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }),
|
||||
}));
|
||||
|
||||
const nativeLookup = dns.lookup;
|
||||
const nativePromiseLookup = dns.promises.lookup;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
// Exercise runtime validation, including the default, rather than CI's raw env values.
|
||||
vi.stubEnv("CI", "");
|
||||
vi.stubEnv("SKIP_ENV_VALIDATION", "");
|
||||
vi.stubEnv("npm_lifecycle_event", "test");
|
||||
vi.stubEnv("ENABLE_DNS_CACHING", "");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dns.lookup = nativeLookup;
|
||||
dns.promises.lookup = nativePromiseLookup;
|
||||
delete global.homarr?.dnsCacheManager;
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
test.each([undefined, "false"])(
|
||||
"preserves native address-family filtering with DNS caching set to '%s'",
|
||||
async (value) => {
|
||||
vi.stubEnv("ENABLE_DNS_CACHING", value);
|
||||
await import("@homarr/core/infrastructure/dns/init");
|
||||
const options = { family: 4, all: true } as const;
|
||||
const expected = await nativePromiseLookup("localhost", options);
|
||||
const addresses = await dns.promises.lookup("localhost", options);
|
||||
expect(addresses).toEqual(expected);
|
||||
expect(addresses.every(({ family }) => family === 4)).toBe(true);
|
||||
|
||||
const callbackAddresses = await new Promise<dns.LookupAddress[]>((resolve, reject) => {
|
||||
dns.lookup("localhost", options, (error, result) => {
|
||||
if (error) reject(error);
|
||||
else resolve(result);
|
||||
});
|
||||
});
|
||||
expect(callbackAddresses).toEqual(expected);
|
||||
},
|
||||
);
|
||||
|
||||
test("still enables DNS caching when explicitly requested", async () => {
|
||||
vi.stubEnv("ENABLE_DNS_CACHING", "true");
|
||||
await import("@homarr/core/infrastructure/dns/init");
|
||||
expect(dns.lookup).not.toBe(nativeLookup);
|
||||
expect(dns.promises.lookup).not.toBe(nativePromiseLookup);
|
||||
expect(await dns.promises.lookup("localhost")).toEqual({ address: "127.0.0.1", family: 4 });
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE "layout" ADD COLUMN "resize_source" text;
|
||||
File diff suppressed because it is too large.
Load diff
@@ -113,6 +113,13 @@
|
||||
"when": 1788763848932,
|
||||
"tag": "0015_add_byte_unit_system",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 16,
|
||||
"version": "7",
|
||||
"when": 1790988597957,
|
||||
"tag": "0016_add_layout_resize_source",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE `layout` ADD `resize_source` text;
|
||||
File diff suppressed because it is too large.
Load diff
@@ -337,6 +337,13 @@
|
||||
"when": 1788763848930,
|
||||
"tag": "0047_add_byte_unit_system",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 48,
|
||||
"version": "6",
|
||||
"when": 1790988595595,
|
||||
"tag": "0048_add_layout_resize_source",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -349,6 +349,7 @@ export const layouts = pgTable("layout", {
|
||||
rightGutterColumnCount: smallint().notNull().default(0),
|
||||
breakpoint: smallint().notNull().default(0),
|
||||
role: varchar({ length: 16 }).$type<LayoutRole>().notNull().default("custom"),
|
||||
resizeSource: text(),
|
||||
});
|
||||
|
||||
export const itemLayouts = pgTable(
|
||||
|
||||
@@ -334,6 +334,7 @@ export const layouts = sqliteTable("layout", {
|
||||
rightGutterColumnCount: int().notNull().default(0),
|
||||
breakpoint: int().notNull().default(0),
|
||||
role: text().$type<LayoutRole>().notNull().default("custom"),
|
||||
resizeSource: text(),
|
||||
});
|
||||
|
||||
export const itemLayouts = sqliteTable(
|
||||
|
||||
@@ -1,150 +1,46 @@
|
||||
// This file is auto-generated by the codegen script
|
||||
// it uses the sitemap.xml to generate the HomarrDocumentationPath type
|
||||
export type HomarrDocumentationPath =
|
||||
| "/about-us"
|
||||
| "/api-reference"
|
||||
| "/blog"
|
||||
| "/blog/2023/01/11/version0.11"
|
||||
| "/blog/2023/04/16/version0.12-more-widgets"
|
||||
| "/blog/2023/11/10/authentication"
|
||||
| "/blog/2023/12/22/updated-documentation"
|
||||
| "/blog/2024/09/23/version-1.0"
|
||||
| "/blog/2024/12/17/open-beta-1.0"
|
||||
| "/blog/2025/01/19/migration-guide-1.0"
|
||||
| "/blog/2025/08/02/using-argus"
|
||||
| "/blog/archive"
|
||||
| "/blog/authors"
|
||||
| "/blog/authors/ajnart"
|
||||
| "/blog/authors/manuel-rw"
|
||||
| "/blog/authors/meierschlumpf"
|
||||
| "/blog/authors/tagashi"
|
||||
| "/blog/authors/walkx"
|
||||
| "/blog/documentation-migration"
|
||||
| "/blog/tags"
|
||||
| "/blog/tags/authentication"
|
||||
| "/blog/tags/breaking-changes"
|
||||
| "/blog/tags/contributions"
|
||||
| "/blog/tags/design"
|
||||
| "/blog/tags/dnd"
|
||||
| "/blog/tags/docs"
|
||||
| "/blog/tags/documentation"
|
||||
| "/blog/tags/gridstack"
|
||||
| "/blog/tags/homarr"
|
||||
| "/blog/tags/migration"
|
||||
| "/blog/tags/notepad"
|
||||
| "/blog/tags/security"
|
||||
| "/blog/tags/translations"
|
||||
| "/blog/tags/update"
|
||||
| "/blog/tags/version"
|
||||
| "/blog/translations"
|
||||
| "/carbon-test"
|
||||
| "/search"
|
||||
| "/docs/tags"
|
||||
| "/docs/tags/active-directory"
|
||||
| "/docs/tags/administration"
|
||||
| "/docs/tags/advanced"
|
||||
| "/docs/tags/ai"
|
||||
| "/docs/tags/analytics"
|
||||
| "/docs/tags/api"
|
||||
| "/docs/tags/apps"
|
||||
| "/docs/tags/automation"
|
||||
| "/docs/tags/background"
|
||||
| "/docs/tags/backup"
|
||||
| "/docs/tags/boards"
|
||||
| "/docs/tags/bookmarks"
|
||||
| "/docs/tags/caddy"
|
||||
| "/docs/tags/certificates"
|
||||
| "/docs/tags/code"
|
||||
| "/docs/tags/community"
|
||||
| "/docs/tags/configuration"
|
||||
| "/docs/tags/connections"
|
||||
| "/docs/tags/custom-widgets"
|
||||
| "/docs/tags/customization"
|
||||
| "/docs/tags/data"
|
||||
| "/docs/tags/data-sources"
|
||||
| "/docs/tags/database"
|
||||
| "/docs/tags/developer"
|
||||
| "/docs/tags/development"
|
||||
| "/docs/tags/docker"
|
||||
| "/docs/tags/donation"
|
||||
| "/docs/tags/edit-mode"
|
||||
| "/docs/tags/env"
|
||||
| "/docs/tags/environment-variables"
|
||||
| "/docs/tags/getting-started"
|
||||
| "/docs/tags/google"
|
||||
| "/docs/tags/groups"
|
||||
| "/docs/tags/help"
|
||||
| "/docs/tags/icon-picker"
|
||||
| "/docs/tags/icon-repositories"
|
||||
| "/docs/tags/icons"
|
||||
| "/docs/tags/installation"
|
||||
| "/docs/tags/integration"
|
||||
| "/docs/tags/integrations"
|
||||
| "/docs/tags/interface"
|
||||
| "/docs/tags/jobs"
|
||||
| "/docs/tags/jsx"
|
||||
| "/docs/tags/layout"
|
||||
| "/docs/tags/ldap"
|
||||
| "/docs/tags/management"
|
||||
| "/docs/tags/mcp"
|
||||
| "/docs/tags/media"
|
||||
| "/docs/tags/oidc"
|
||||
| "/docs/tags/open-collective"
|
||||
| "/docs/tags/permissions"
|
||||
| "/docs/tags/pgid"
|
||||
| "/docs/tags/ping"
|
||||
| "/docs/tags/programming"
|
||||
| "/docs/tags/proxy"
|
||||
| "/docs/tags/puid"
|
||||
| "/docs/tags/redis"
|
||||
| "/docs/tags/responsive"
|
||||
| "/docs/tags/roles"
|
||||
| "/docs/tags/search"
|
||||
| "/docs/tags/search-engines"
|
||||
| "/docs/tags/secrets"
|
||||
| "/docs/tags/security"
|
||||
| "/docs/tags/self-signed"
|
||||
| "/docs/tags/seo"
|
||||
| "/docs/tags/server"
|
||||
| "/docs/tags/settings"
|
||||
| "/docs/tags/sq-lite"
|
||||
| "/docs/tags/sso"
|
||||
| "/docs/tags/tasks"
|
||||
| "/docs/tags/technical-documentation"
|
||||
| "/docs/tags/traefik"
|
||||
| "/docs/tags/translations"
|
||||
| "/docs/tags/unraid"
|
||||
| "/docs/tags/uploads"
|
||||
| "/docs/tags/users"
|
||||
| "/docs/tags/variables"
|
||||
| "/docs/tags/widgets"
|
||||
| "/docs/advanced/command-line"
|
||||
| "/docs/advanced/command-line/development"
|
||||
| "/docs/advanced/command-line/fix-usernames"
|
||||
| "/docs/advanced/command-line/integrations"
|
||||
| "/docs/advanced/command-line/password-recovery"
|
||||
| "/docs/advanced/command-line/users"
|
||||
| "/docs/advanced/development/getting-started"
|
||||
| "/docs/advanced/development/kubernetes"
|
||||
| "/docs/advanced/environment-variables"
|
||||
| "/docs/advanced/icons"
|
||||
| "/docs/advanced/keyboard-shortcuts"
|
||||
| "/docs/advanced/proxy"
|
||||
| "/docs/advanced/running-as-different-user"
|
||||
| "/docs/advanced/single-sign-on"
|
||||
| "/docs/advanced/styling"
|
||||
| ""
|
||||
| "/docs"
|
||||
| "/docs/advanced"
|
||||
| "/docs/advanced/mysql-to-sqlite"
|
||||
| "/docs/getting-started/after-the-installation"
|
||||
| "/docs/getting-started/glossary"
|
||||
| "/docs/getting-started"
|
||||
| "/docs/community/donate"
|
||||
| "/docs/community/faq"
|
||||
| "/docs/community/get-in-touch"
|
||||
| "/docs/community"
|
||||
| "/docs/community/license"
|
||||
| "/docs/community/translations"
|
||||
| "/docs/getting-started"
|
||||
| "/docs/getting-started/after-the-installation"
|
||||
| "/docs/getting-started/glossary"
|
||||
| "/docs/management/assistant"
|
||||
| "/docs/management/backup"
|
||||
| "/docs/management"
|
||||
| "/docs/management/mcp"
|
||||
| "/docs/integrations"
|
||||
| "/docs/workshop/homarr-provider"
|
||||
| "/docs/workshop"
|
||||
| "/docs/widgets"
|
||||
| "/docs/advanced/command-line"
|
||||
| "/docs/advanced/environment-variables"
|
||||
| "/docs/advanced/development/getting-started"
|
||||
| "/docs/advanced/development/kubernetes"
|
||||
| "/docs/advanced/development/widgets-and-integrations"
|
||||
| "/docs/advanced/development/workshop-operator"
|
||||
| "/docs/advanced/development/workshop"
|
||||
| "/docs/advanced/icons"
|
||||
| "/docs/advanced/keyboard-shortcuts"
|
||||
| "/docs/advanced/running-as-different-user"
|
||||
| "/docs/advanced/proxy"
|
||||
| "/docs/advanced/single-sign-on"
|
||||
| "/docs/advanced/styling"
|
||||
| "/docs/getting-started/installation/docker"
|
||||
| "/docs/getting-started/installation/easy-panel"
|
||||
| "/docs/getting-started/installation/helm"
|
||||
| "/docs/getting-started/installation/home-assistant"
|
||||
| "/docs/getting-started/installation/hostinger"
|
||||
| "/docs/getting-started/installation"
|
||||
| "/docs/getting-started/installation/pika-pods"
|
||||
| "/docs/getting-started/installation/portainer"
|
||||
| "/docs/getting-started/installation/proxmox"
|
||||
@@ -155,29 +51,68 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/getting-started/installation/source"
|
||||
| "/docs/getting-started/installation/synology"
|
||||
| "/docs/getting-started/installation/unraid"
|
||||
| "/docs/management/api"
|
||||
| "/docs/management/apps"
|
||||
| "/docs/management/boards"
|
||||
| "/docs/management/certificates"
|
||||
| "/docs/management/custom-widgets/agent-authoring"
|
||||
| "/docs/management/custom-widgets/authoring"
|
||||
| "/docs/management/custom-widgets/component-reference"
|
||||
| "/docs/management/custom-widgets/custom-jsx"
|
||||
| "/docs/management/custom-widgets"
|
||||
| "/docs/management/custom-widgets/requests-and-security"
|
||||
| "/docs/management/custom-widgets/troubleshooting"
|
||||
| "/docs/management/logs"
|
||||
| "/docs/management/media"
|
||||
| "/docs/management/integrations"
|
||||
| "/docs/management/search-engines"
|
||||
| "/docs/management/settings"
|
||||
| "/docs/management/tasks"
|
||||
| "/docs/management/users"
|
||||
| "/docs/integrations/adguard-home"
|
||||
| "/docs/integrations/anchor"
|
||||
| "/docs/integrations/archiveteam-warrior"
|
||||
| "/docs/integrations/aria2"
|
||||
| "/docs/integrations/audiobookshelf"
|
||||
| "/docs/integrations/autobrr"
|
||||
| "/docs/integrations/bazarr"
|
||||
| "/docs/integrations/beszel"
|
||||
| "/docs/integrations/bindery"
|
||||
| "/docs/integrations/caddy"
|
||||
| "/docs/integrations/changedetection"
|
||||
| "/docs/integrations/coolify"
|
||||
| "/docs/integrations/dash-dot"
|
||||
| "/docs/integrations/deluge"
|
||||
| "/docs/integrations/docker"
|
||||
| "/docs/integrations/docker-labels"
|
||||
| "/docs/integrations/emby"
|
||||
| "/docs/integrations/fileflows"
|
||||
| "/docs/integrations/frigate"
|
||||
| "/docs/integrations/gatus"
|
||||
| "/docs/integrations/glances"
|
||||
| "/docs/integrations/gluetun"
|
||||
| "/docs/integrations/gotify"
|
||||
| "/docs/integrations/healthchecks"
|
||||
| "/docs/integrations/home-assistant"
|
||||
| "/docs/integrations/homebox"
|
||||
| "/docs/integrations/ical"
|
||||
| "/docs/integrations/immich"
|
||||
| "/docs/integrations/jackett"
|
||||
| "/docs/integrations/jellyfin"
|
||||
| "/docs/integrations/jellyseerr"
|
||||
| "/docs/integrations/jellystat"
|
||||
| "/docs/integrations/karakeep"
|
||||
| "/docs/integrations/komga"
|
||||
| "/docs/integrations/kubernetes"
|
||||
| "/docs/integrations/lidarr"
|
||||
| "/docs/integrations/linkwarden"
|
||||
| "/docs/integrations/llama-cpp"
|
||||
| "/docs/integrations/maintainerr"
|
||||
| "/docs/integrations/mealie"
|
||||
| "/docs/integrations/miniflux"
|
||||
| "/docs/integrations/navidrome"
|
||||
| "/docs/integrations/netalertx"
|
||||
| "/docs/integrations/netdata"
|
||||
| "/docs/integrations/nextcloud"
|
||||
| "/docs/integrations/ntfy"
|
||||
| "/docs/integrations/nzbget"
|
||||
@@ -188,44 +123,48 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/integrations/patchmon"
|
||||
| "/docs/integrations/peanut"
|
||||
| "/docs/integrations/pi-hole"
|
||||
| "/docs/integrations/plantit"
|
||||
| "/docs/integrations/plex"
|
||||
| "/docs/integrations/prometheus"
|
||||
| "/docs/integrations/prowlarr"
|
||||
| "/docs/integrations/proxmox"
|
||||
| "/docs/integrations/q-bittorent"
|
||||
| "/docs/integrations/radarr"
|
||||
| "/docs/integrations/readarr"
|
||||
| "/docs/integrations/romm"
|
||||
| "/docs/integrations/sabnzbd"
|
||||
| "/docs/integrations/scrutiny"
|
||||
| "/docs/integrations/seerr"
|
||||
| "/docs/integrations/slskd"
|
||||
| "/docs/integrations/sonarr"
|
||||
| "/docs/integrations/speedtest-tracker"
|
||||
| "/docs/integrations/spoolman"
|
||||
| "/docs/integrations/stash"
|
||||
| "/docs/integrations/syncthing-relay"
|
||||
| "/docs/integrations/synology"
|
||||
| "/docs/integrations/tandoor"
|
||||
| "/docs/integrations/tdarr"
|
||||
| "/docs/integrations/technitium-dns"
|
||||
| "/docs/integrations/tracearr"
|
||||
| "/docs/integrations/traefik"
|
||||
| "/docs/integrations/transmission"
|
||||
| "/docs/integrations/trilium"
|
||||
| "/docs/integrations/truenas"
|
||||
| "/docs/integrations/tubearchivist"
|
||||
| "/docs/integrations/umami"
|
||||
| "/docs/integrations/unifi-controller"
|
||||
| "/docs/integrations/unmanic"
|
||||
| "/docs/integrations/unraid"
|
||||
| "/docs/integrations/uptime-kuma"
|
||||
| "/docs/integrations/wazuh"
|
||||
| "/docs/integrations/whats-up-docker"
|
||||
| "/docs/management/api"
|
||||
| "/docs/management/apps"
|
||||
| "/docs/management/backup"
|
||||
| "/docs/management/boards"
|
||||
| "/docs/management/certificates"
|
||||
| "/docs/management/custom-widgets"
|
||||
| "/docs/management/integrations"
|
||||
| "/docs/management/mcp"
|
||||
| "/docs/management/media"
|
||||
| "/docs/management/search-engines"
|
||||
| "/docs/management/settings"
|
||||
| "/docs/management/tasks"
|
||||
| "/docs/management/users"
|
||||
| "/docs/integrations/xteve"
|
||||
| "/docs/integrations/your-spotify"
|
||||
| "/docs/widgets/air-quality"
|
||||
| "/docs/widgets/anchor-note"
|
||||
| "/docs/widgets/app"
|
||||
| "/docs/widgets/archive-team-warrior"
|
||||
| "/docs/widgets/assistant"
|
||||
| "/docs/widgets/audio-stats"
|
||||
| "/docs/widgets/bazarr"
|
||||
| "/docs/widgets/beszel-alerts"
|
||||
@@ -236,6 +175,7 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/widgets/calendar"
|
||||
| "/docs/widgets/clock"
|
||||
| "/docs/widgets/coolify"
|
||||
| "/docs/widgets/countdown"
|
||||
| "/docs/widgets/custom-api"
|
||||
| "/docs/widgets/dns-hole-controls"
|
||||
| "/docs/widgets/dns-hole-summary"
|
||||
@@ -244,9 +184,7 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/widgets/firewall"
|
||||
| "/docs/widgets/health-monitoring"
|
||||
| "/docs/widgets/iframe"
|
||||
| "/docs/widgets/immich-album-carousel"
|
||||
| "/docs/widgets/immich-server-stats"
|
||||
| "/docs/widgets/indexer-manager"
|
||||
| "/docs/widgets/llama-cpp"
|
||||
| "/docs/widgets/media-missing"
|
||||
| "/docs/widgets/media-releases"
|
||||
| "/docs/widgets/media-request-list"
|
||||
@@ -257,6 +195,9 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/widgets/network-controller-status"
|
||||
| "/docs/widgets/network-controller-summary"
|
||||
| "/docs/widgets/notebook"
|
||||
| "/docs/widgets/immich-server-stats"
|
||||
| "/docs/widgets/immich-album-carousel"
|
||||
| "/docs/widgets/indexer-manager"
|
||||
| "/docs/widgets/notifications"
|
||||
| "/docs/widgets/paperless-ngx"
|
||||
| "/docs/widgets/patchmon"
|
||||
@@ -265,9 +206,10 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/widgets/smart-home-entity-state"
|
||||
| "/docs/widgets/smart-home-execute-automation"
|
||||
| "/docs/widgets/speedtest-tracker"
|
||||
| "/docs/widgets/stats"
|
||||
| "/docs/widgets/stock-price"
|
||||
| "/docs/widgets/system-disks"
|
||||
| "/docs/widgets/system-resources"
|
||||
| "/docs/widgets/timer"
|
||||
| "/docs/widgets/timetable"
|
||||
| "/docs/widgets/tracearr"
|
||||
| "/docs/widgets/traefik"
|
||||
@@ -278,7 +220,69 @@ export type HomarrDocumentationPath =
|
||||
| "/docs/widgets/vpn"
|
||||
| "/docs/widgets/weather"
|
||||
| "/docs/widgets/whats-up-docker"
|
||||
| ""
|
||||
| "/docs/advanced/command-line/development"
|
||||
| "/docs/advanced/command-line/fix-usernames"
|
||||
| "/docs/advanced/command-line/integrations"
|
||||
| "/docs/advanced/command-line/password-recovery"
|
||||
| "/docs/advanced/command-line/users"
|
||||
| "/docs/widgets/system-resources"
|
||||
| "/api-reference/apps/appRouter-getPaginated"
|
||||
| "/api-reference/apps/appRouter-all"
|
||||
| "/api-reference/apps/appRouter-create"
|
||||
| "/api-reference/apps/appRouter-search"
|
||||
| "/api-reference/apps/appRouter-selectable"
|
||||
| "/api-reference/apps/appRouter-byId"
|
||||
| "/api-reference/apps/appRouter-update"
|
||||
| "/api-reference/apps/appRouter-delete"
|
||||
| "/api-reference/boards/boardRouter-getAllBoards"
|
||||
| "/api-reference/boards/boardRouter-createBoard"
|
||||
| "/api-reference/boards/boardRouter-duplicateBoard"
|
||||
| "/api-reference/boards/boardRouter-renameBoard"
|
||||
| "/api-reference/boards/boardRouter-changeBoardVisibility"
|
||||
| "/api-reference/boards/boardRouter-deleteBoard"
|
||||
| "/api-reference/boards/boardRouter-setHomeBoard"
|
||||
| "/api-reference/boards/boardRouter-setMobileHomeBoard"
|
||||
| "/api-reference/boards/boardRouter-savePartialBoardSettings"
|
||||
| "/api-reference/boards/boardRouter-addItem"
|
||||
| "/api-reference/boards/boardRouter-updateItemLayout"
|
||||
| "/api-reference/info/infoRouter-getInfo"
|
||||
| "/api-reference/invites/inviteRouter-getAll"
|
||||
| "/api-reference/invites/inviteRouter-createInvite"
|
||||
| "/api-reference/invites/inviteRouter-deleteInvite"
|
||||
| "/api-reference/settings/serverSettingsRouter-getBoardSettings"
|
||||
| "/api-reference/settings/serverSettingsRouter-updateBoardSettings"
|
||||
| "/api-reference/users/userRouter-getAll"
|
||||
| "/api-reference/users/userRouter-create"
|
||||
| "/api-reference/users/userRouter-setProfileImage"
|
||||
| "/api-reference/users/userRouter-selectable"
|
||||
| "/api-reference/users/userRouter-search"
|
||||
| "/api-reference/users/userRouter-getById"
|
||||
| "/api-reference/users/userRouter-delete"
|
||||
| "/api-reference/users/userRouter-editProfile"
|
||||
| "/api-reference/users/userRouter-changePassword"
|
||||
| "/api-reference/users/userRouter-changeHomeBoards"
|
||||
| "/api-reference/users/userRouter-changeDefaultSearchEngine"
|
||||
| "/api-reference/users/userRouter-changeSearchPreferences"
|
||||
| "/api-reference/users/userRouter-changeColorScheme"
|
||||
| "/api-reference/users/userRouter-changeEnableRightClickOnWidgets"
|
||||
| "/api-reference/users/userRouter-changeHeaderPreferences"
|
||||
| "/api-reference/users/userRouter-changeDdgBangs"
|
||||
| "/api-reference/users/userRouter-changeFirstDayOfWeek"
|
||||
| "/api-reference/integrations/integration-request"
|
||||
| "/blog/translations"
|
||||
| "/blog/2024/09/23/version-1.0"
|
||||
| "/blog/2024/12/17/open-beta-1.0"
|
||||
| "/blog/documentation-migration"
|
||||
| "/blog/2023/01/11/version0.11"
|
||||
| "/blog/2023/04/16/version0.12-more-widgets"
|
||||
| "/blog/2023/12/22/updated-documentation"
|
||||
| "/blog/2023/11/10/authentication"
|
||||
| "/blog/2025/01/19/migration-guide-1.0"
|
||||
| "/blog/2026/09/03/homarr-2.0"
|
||||
| "/blog/2025/08/02/using-argus"
|
||||
| "/workshop"
|
||||
| "/api-reference"
|
||||
| "/about-us"
|
||||
| "/sitemap.xml"
|
||||
| "/docs/integrations/autobrr"
|
||||
| "/docs/integrations/jellystat"
|
||||
|
||||
@@ -119,11 +119,11 @@ export const integrationDefs = {
|
||||
frigate: {
|
||||
httpAuth: { type: "adapter" },
|
||||
name: "Frigate",
|
||||
secretKinds: [[]],
|
||||
secretKinds: [["username", "password"]],
|
||||
iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/frigate.svg",
|
||||
category: ["miscellaneous"],
|
||||
documentationSlug: "frigate",
|
||||
defaultPort: 5000,
|
||||
defaultPort: 8971,
|
||||
},
|
||||
komga: {
|
||||
httpAuth: { type: "apiKeyHeader", name: "X-API-Key" },
|
||||
|
||||
@@ -55,7 +55,7 @@ describe("parseContainerLabels", () => {
|
||||
name: "Jellyfin",
|
||||
group: "Media",
|
||||
href: "http://jellyfin:8096",
|
||||
icon: "jellyfin.svg",
|
||||
icon: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/jellyfin.svg",
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -25,6 +25,21 @@ const resolveLabelValue = (
|
||||
useHomepageFallback: boolean,
|
||||
) => readLabel(labels, homarrKey) ?? (useHomepageFallback ? readLabel(labels, homepageKey) : undefined);
|
||||
|
||||
const resolveIcon = (labels: Record<string, string>, useHomepageFallback: boolean) => {
|
||||
const homarrIcon = readLabel(labels, dockerLabels.icon);
|
||||
if (homarrIcon || !useHomepageFallback) return homarrIcon;
|
||||
|
||||
const homepageIcon = readLabel(labels, homepageLabels.icon);
|
||||
if (!homepageIcon || /^(mdi|si|sh)-/.test(homepageIcon)) return homepageIcon;
|
||||
|
||||
// Homepage's Dashboard Icons keys default to PNG; URLs and local paths pass through.
|
||||
const dashboardIcon = /^([a-zA-Z0-9][a-zA-Z0-9_-]*)(?:\.(png|svg|webp))?$/.exec(homepageIcon);
|
||||
if (!dashboardIcon?.[1]) return homepageIcon;
|
||||
|
||||
const extension = dashboardIcon[2] ?? "png";
|
||||
return `https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/${extension}/${dashboardIcon[1].toLowerCase()}.${extension}`;
|
||||
};
|
||||
|
||||
const parseIntegrationKind = (value: string | undefined): IntegrationKind | undefined =>
|
||||
value && integrationKindSet.has(value) ? (value as IntegrationKind) : undefined;
|
||||
|
||||
@@ -57,7 +72,7 @@ export const parseContainerLabels = (
|
||||
group,
|
||||
name,
|
||||
href,
|
||||
icon: resolveLabelValue(labels, dockerLabels.icon, homepageLabels.icon, useHomepageFallback),
|
||||
icon: resolveIcon(labels, useHomepageFallback),
|
||||
description: resolveLabelValue(labels, dockerLabels.description, homepageLabels.description, useHomepageFallback),
|
||||
pingUrl: readLabel(labels, dockerLabels.ping),
|
||||
externalId,
|
||||
|
||||
+1
-1
@@ -22,6 +22,6 @@ export interface NetworkControllerSummary {
|
||||
|
||||
vpn: {
|
||||
status: "enabled" | "disabled";
|
||||
users: number;
|
||||
users: number | null;
|
||||
};
|
||||
}
|
||||
@@ -236,6 +236,8 @@ export class JellyfinIntegration extends Integration implements IMediaServerInte
|
||||
fields: ["CustomRating", "Studios", "Genres", "ChildCount", "DateCreated", "Overview", "Taglines"],
|
||||
userId,
|
||||
limit: 100,
|
||||
// Root-level grouping uses the first library's type and can omit items from other libraries.
|
||||
groupItems: false,
|
||||
});
|
||||
return result.data.map((item) => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
|
||||
@@ -606,7 +606,7 @@ const identitySchema = z.object({
|
||||
}),
|
||||
});
|
||||
|
||||
const mapType = (type: string): "movie" | "tv" | "unknown" => {
|
||||
const mapType = (type: string): "movie" | "tv" | "music" | "unknown" => {
|
||||
switch (type) {
|
||||
case "movie":
|
||||
return "movie";
|
||||
@@ -614,6 +614,10 @@ const mapType = (type: string): "movie" | "tv" | "unknown" => {
|
||||
case "season":
|
||||
case "episode":
|
||||
return "tv";
|
||||
case "album":
|
||||
case "artist":
|
||||
case "track":
|
||||
return "music";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
@@ -71,6 +71,34 @@ beforeEach(() => {
|
||||
});
|
||||
|
||||
describe("PlexIntegration.getMediaReleasesAsync", () => {
|
||||
test.each([
|
||||
["album", "music"],
|
||||
["artist", "music"],
|
||||
["track", "music"],
|
||||
["movie", "movie"],
|
||||
["show", "tv"],
|
||||
["unsupported", "unknown"],
|
||||
])("classifies %s releases as %s", async (type, expectedType) => {
|
||||
mockFetch.mockImplementation((url) => {
|
||||
const urlString = String(url);
|
||||
if (urlString.endsWith("/identity")) {
|
||||
return respond({ MediaContainer: { machineIdentifier: "server-id" } });
|
||||
}
|
||||
if (urlString.endsWith("/library/recentlyAdded")) {
|
||||
return respond({ MediaContainer: { Metadata: [createMetadataItem(type, { type })] } });
|
||||
}
|
||||
if (urlString.endsWith("/library/sections")) {
|
||||
return respond({ MediaContainer: { Directory: [] } });
|
||||
}
|
||||
throw new Error(`Unexpected Plex request: ${urlString}`);
|
||||
});
|
||||
|
||||
const releases = await createIntegration().getMediaReleasesAsync();
|
||||
|
||||
expect(releases).toHaveLength(1);
|
||||
expect(releases[0]).toMatchObject({ title: "Movie", type: expectedType });
|
||||
});
|
||||
|
||||
test("uses the series title for TV seasons and episodes", async () => {
|
||||
mockFetch.mockImplementation((url) => {
|
||||
const urlString = String(url);
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
// @vitest-environment node
|
||||
|
||||
import { inspect } from "node:util";
|
||||
|
||||
import { Headers, Response } from "undici";
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
||||
import { integrationDefs } from "@homarr/definitions";
|
||||
|
||||
import type { IntegrationInput, IntegrationTestingInput } from "../base/integration";
|
||||
import { frigateStatsProvider } from "./providers/frigate";
|
||||
import { StatsIntegration } from "./stats-integration";
|
||||
|
||||
const logger = vi.hoisted(() => ({ debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() }));
|
||||
|
||||
vi.mock("@homarr/core/infrastructure/http", () => ({
|
||||
fetchWithTrustedCertificatesAsync: vi.fn(),
|
||||
createAxiosCertificateInstanceAsync: vi.fn(),
|
||||
createCertificateAgentAsync: vi.fn(),
|
||||
}));
|
||||
vi.mock("@homarr/core/infrastructure/certificates", () => ({
|
||||
getTrustedCertificateHostnamesAsync: vi.fn().mockResolvedValue([]),
|
||||
}));
|
||||
vi.mock("@homarr/core/infrastructure/logs", () => ({
|
||||
createLogger: () => logger,
|
||||
}));
|
||||
|
||||
const token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9.test_signature";
|
||||
const credentials = [
|
||||
{ kind: "username" as const, value: "operator" },
|
||||
{ kind: "password" as const, value: "private-test-password" },
|
||||
];
|
||||
const fetchAsync = vi.mocked(fetchWithTrustedCertificatesAsync);
|
||||
const createIntegration = (
|
||||
decryptedSecrets: IntegrationInput["decryptedSecrets"] = credentials,
|
||||
url = "https://frigate.test:8971",
|
||||
) =>
|
||||
new StatsIntegration(
|
||||
{
|
||||
id: "frigate-test",
|
||||
kind: "frigate",
|
||||
name: "Frigate",
|
||||
url,
|
||||
externalUrl: null,
|
||||
decryptedSecrets,
|
||||
},
|
||||
frigateStatsProvider,
|
||||
);
|
||||
|
||||
const loginResponse = (cookie = `frigate_token=${token}; HttpOnly; Path=/; SameSite=Lax`) =>
|
||||
new Response(null, { status: 200, headers: { "set-cookie": cookie } });
|
||||
const profileResponse = (username = "operator", role = "admin") =>
|
||||
Response.json({ username, role, allowed_cameras: [] });
|
||||
const statsResponse = () => Response.json({ cameras: { front: {} }, service: { uptime: 42, version: "0.18.0" } });
|
||||
|
||||
beforeEach(() => fetchAsync.mockReset());
|
||||
|
||||
describe("authenticated Frigate", () => {
|
||||
test("uses a bodyless cookie login and the same verified session for native stats", async () => {
|
||||
fetchAsync
|
||||
.mockResolvedValueOnce(loginResponse())
|
||||
.mockResolvedValueOnce(profileResponse())
|
||||
.mockResolvedValueOnce(statsResponse());
|
||||
expect(await createIntegration().getStatsAsync()).toEqual({ cameras: 1, uptime: 42, version: "0.18.0" });
|
||||
expect(fetchAsync.mock.calls.map(([url]) => String(url))).toEqual([
|
||||
"https://frigate.test:8971/api/login",
|
||||
"https://frigate.test:8971/api/profile",
|
||||
"https://frigate.test:8971/api/stats",
|
||||
]);
|
||||
const login = fetchAsync.mock.calls[0]?.[1];
|
||||
expect(login).toMatchObject({
|
||||
method: "POST",
|
||||
body: JSON.stringify({ user: "operator", password: credentials[1]?.value }),
|
||||
redirect: "error",
|
||||
});
|
||||
expect(new Headers(login?.headers).get("origin")).toBeNull();
|
||||
expect(login?.signal).toBeInstanceOf(AbortSignal);
|
||||
for (const [, init] of fetchAsync.mock.calls.slice(1)) {
|
||||
expect(new Headers(init?.headers).get("authorization")).toBe(`Bearer ${token}`);
|
||||
expect(init?.redirect).toBe("error");
|
||||
}
|
||||
});
|
||||
|
||||
test("supplies verified bearer credentials and explicit token redaction for generic requests", async () => {
|
||||
fetchAsync.mockResolvedValueOnce(loginResponse()).mockResolvedValueOnce(profileResponse());
|
||||
expect(await createIntegration().getHttpAuthenticationAsync()).toEqual({
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
redactValues: [token],
|
||||
});
|
||||
expect(fetchAsync).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test("uses the injected trusted transport for native connectivity testing", async () => {
|
||||
const injectedFetch = vi
|
||||
.fn<typeof fetchWithTrustedCertificatesAsync>()
|
||||
.mockResolvedValueOnce(loginResponse())
|
||||
.mockResolvedValueOnce(profileResponse())
|
||||
.mockResolvedValueOnce(statsResponse());
|
||||
// Only fetch is used by this provider; the other transports must never be invoked.
|
||||
const testing = { fetchAsync: injectedFetch } as unknown as IntegrationTestingInput;
|
||||
expect(await createIntegration().getStatsAsync(AbortSignal.timeout(1000), testing)).toMatchObject({ uptime: 42 });
|
||||
expect(injectedFetch).toHaveBeenCalledTimes(3);
|
||||
expect(fetchAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test.each([
|
||||
{ secrets: [] },
|
||||
{ secrets: [{ kind: "username" as const, value: "operator" }] },
|
||||
{
|
||||
secrets: [
|
||||
{ kind: "username" as const, value: "" },
|
||||
{ kind: "password" as const, value: "private-test-password" },
|
||||
],
|
||||
},
|
||||
])("rejects missing credentials without attempting an unauthenticated request (%j)", async ({ secrets }) => {
|
||||
await expect(createIntegration(secrets).getStatsAsync()).rejects.toThrow();
|
||||
expect(fetchAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test.each([401, 403, 404, 302])("rejects login status %i before profile or stats", async (status) => {
|
||||
fetchAsync.mockResolvedValueOnce(new Response("private login error", { status }));
|
||||
await expect(createIntegration().getStatsAsync()).rejects.toThrow();
|
||||
expect(fetchAsync).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test.each(["other_cookie=value", "frigate_token=not-a-jwt", `frigate_token=${"x".repeat(4097)}.b.c`])(
|
||||
"rejects missing or invalid cookies",
|
||||
async (cookie) => {
|
||||
fetchAsync.mockResolvedValueOnce(loginResponse(cookie));
|
||||
await expect(createIntegration().getHttpAuthenticationAsync()).rejects.toThrow();
|
||||
expect(fetchAsync).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
);
|
||||
|
||||
test("rejects duplicate session cookies", async () => {
|
||||
const headers = new Headers();
|
||||
headers.append("set-cookie", `frigate_token=${token}; Path=/`);
|
||||
headers.append("set-cookie", `frigate_token=${token}; Path=/api`);
|
||||
fetchAsync.mockResolvedValueOnce(new Response(null, { headers }));
|
||||
await expect(createIntegration().getStatsAsync()).rejects.toThrow();
|
||||
expect(fetchAsync).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test.each([
|
||||
["operator", "viewer"],
|
||||
["another-admin", "admin"],
|
||||
])("rejects a profile for %s with role %s before stats", async (username, role) => {
|
||||
fetchAsync.mockResolvedValueOnce(loginResponse()).mockResolvedValueOnce(profileResponse(username, role));
|
||||
await expect(createIntegration().getStatsAsync()).rejects.toThrow();
|
||||
expect(fetchAsync).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test("bounds private response headers and discards the login body without parsing it", async () => {
|
||||
const response = new Response("not JSON", { headers: { "set-cookie": `other=${"x".repeat(16_384)}` } });
|
||||
fetchAsync.mockResolvedValueOnce(response);
|
||||
await expect(createIntegration().getHttpAuthenticationAsync()).rejects.toThrow();
|
||||
expect(response.body?.locked).toBe(false);
|
||||
expect(fetchAsync).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("discards a successful login body without parsing or exposing its contents", async () => {
|
||||
const response = new Response(`private body ${"x".repeat(65_536)}`, {
|
||||
headers: { "set-cookie": `frigate_token=${token}; Path=/` },
|
||||
});
|
||||
const parse = vi.spyOn(response, "json");
|
||||
if (!response.body) throw new Error("Fixture must have a body");
|
||||
const cancel = vi.spyOn(response.body, "cancel");
|
||||
fetchAsync.mockResolvedValueOnce(response).mockResolvedValueOnce(profileResponse());
|
||||
expect(await createIntegration().getHttpAuthenticationAsync()).toMatchObject({ redactValues: [token] });
|
||||
expect(cancel).toHaveBeenCalledOnce();
|
||||
expect(parse).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test.each(["login", "profile", "stats"])("does not expose transport secrets from %s", async (stage) => {
|
||||
if (stage !== "login") fetchAsync.mockResolvedValueOnce(loginResponse());
|
||||
if (stage === "stats") fetchAsync.mockResolvedValueOnce(profileResponse());
|
||||
fetchAsync.mockRejectedValueOnce(
|
||||
new Error(`secret ${credentials[1]?.value} ${token}`, { cause: new Error(token) }),
|
||||
);
|
||||
const failure: unknown = await createIntegration()
|
||||
.getStatsAsync()
|
||||
.catch((error: unknown) => error);
|
||||
expect(String(failure)).not.toContain(token);
|
||||
expect(String(failure)).not.toContain(credentials[1]?.value);
|
||||
expect(JSON.stringify(failure)).not.toContain(token);
|
||||
expect(JSON.stringify(failure)).not.toContain(credentials[1]?.value);
|
||||
expect(inspect(failure, { depth: 10 })).not.toContain(token);
|
||||
expect(inspect(failure, { depth: 10 })).not.toContain(credentials[1]?.value);
|
||||
expect(inspect(logger.warn.mock.calls, { depth: 10 })).not.toContain(token);
|
||||
expect(inspect(logger.warn.mock.calls, { depth: 10 })).not.toContain(credentials[1]?.value);
|
||||
});
|
||||
|
||||
test("rejects HTTP integration URLs without transmitting credentials", async () => {
|
||||
const integration = createIntegration(credentials, "http://frigate.test:8971");
|
||||
await expect(integration.getHttpAuthenticationAsync()).rejects.toThrow();
|
||||
await expect(integration.getStatsAsync()).rejects.toThrow();
|
||||
expect(fetchAsync).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("requires saved credentials and defaults to the authenticated port", () => {
|
||||
expect(integrationDefs.frigate.secretKinds).toEqual([["username", "password"]]);
|
||||
expect(integrationDefs.frigate.defaultPort).toBe(8971);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import type { StatsProvider } from "../types";
|
||||
import type { StatsFetchContext, StatsProvider } from "../types";
|
||||
|
||||
const statsSchema = z
|
||||
.object({
|
||||
@@ -10,14 +10,56 @@ const statsSchema = z
|
||||
.passthrough();
|
||||
|
||||
export const frigateStatsProvider = {
|
||||
getHttpAuthentication: () => ({ headers: {} }),
|
||||
getHttpAuthenticationAsync,
|
||||
metrics: [
|
||||
{ key: "cameras", label: "Cameras", unit: "count" },
|
||||
{ key: "uptime", label: "Uptime", unit: "seconds" },
|
||||
{ key: "version", label: "Version", unit: "text" },
|
||||
],
|
||||
async fetchAsync(context) {
|
||||
const stats = statsSchema.parse(await context.requestAsync("/api/stats", { signal: context.signal }));
|
||||
return { cameras: Object.keys(stats.cameras).length, uptime: stats.service.uptime, version: stats.service.version };
|
||||
const authentication = await getHttpAuthenticationAsync(context);
|
||||
try {
|
||||
const stats = statsSchema.parse(
|
||||
await context.requestAsync("/api/stats", { headers: authentication.headers, signal: context.signal }),
|
||||
);
|
||||
return {
|
||||
cameras: Object.keys(stats.cameras).length,
|
||||
uptime: stats.service.uptime,
|
||||
version: stats.service.version,
|
||||
};
|
||||
} catch {
|
||||
throw new Error("Frigate stats request failed");
|
||||
}
|
||||
},
|
||||
} satisfies StatsProvider;
|
||||
|
||||
async function getHttpAuthenticationAsync(context: StatsFetchContext) {
|
||||
if (context.url.protocol !== "https:") {
|
||||
throw new Error("Frigate authentication requires an HTTPS integration URL to protect credentials");
|
||||
}
|
||||
try {
|
||||
const username = context.secret("username");
|
||||
const password = context.secret("password");
|
||||
if (!username || !password) throw new Error("Missing credentials");
|
||||
const response = await context.requestResponseAsync("/api/login", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ user: username, password }),
|
||||
signal: context.signal,
|
||||
});
|
||||
if (response.status !== 200) throw new Error("Login rejected");
|
||||
const cookies = response.headers.getSetCookie().filter((cookie) => cookie.startsWith("frigate_token="));
|
||||
if (cookies.length !== 1) throw new Error("Missing or ambiguous session cookie");
|
||||
const token = cookies[0]?.split(";", 1)[0]?.slice("frigate_token=".length);
|
||||
if (!token || token.length > 4096 || !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(token)) {
|
||||
throw new Error("Invalid session cookie");
|
||||
}
|
||||
const headers = { Authorization: `Bearer ${token}` };
|
||||
z.object({ username: z.literal(username), role: z.literal("admin"), allowed_cameras: z.array(z.string()) }).parse(
|
||||
await context.requestAsync("/api/profile", { headers, signal: context.signal }),
|
||||
);
|
||||
return { headers, redactValues: [token] };
|
||||
} catch {
|
||||
throw new Error("Frigate authentication failed");
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,11 @@ export class StatsIntegration extends Integration {
|
||||
const signal = AbortSignal.timeout(15_000);
|
||||
return await this.provider.getHttpAuthenticationAsync({
|
||||
signal,
|
||||
url: new URL(this.integration.url),
|
||||
secret: (kind) => this.getSecretValue(kind),
|
||||
hasSecret: (kind) => this.hasSecretValue(kind),
|
||||
requestAsync: this.createRequestAsync(signal, undefined, undefined, true),
|
||||
requestResponseAsync: this.createResponseRequestAsync(signal),
|
||||
});
|
||||
}
|
||||
if (!this.provider.getHttpAuthentication) return await super.getHttpAuthenticationAsync();
|
||||
@@ -49,12 +51,37 @@ export class StatsIntegration extends Integration {
|
||||
}
|
||||
return await this.provider.fetchAsync({
|
||||
signal,
|
||||
url: new URL(this.integration.url),
|
||||
secret: (kind) => this.getSecretValue(kind),
|
||||
hasSecret: (kind) => this.hasSecretValue(kind),
|
||||
requestAsync: this.createRequestAsync(signal, testing, authentication),
|
||||
requestResponseAsync: this.createResponseRequestAsync(signal, testing),
|
||||
});
|
||||
}
|
||||
|
||||
private createResponseRequestAsync(
|
||||
signal: AbortSignal,
|
||||
testing?: IntegrationTestingInput,
|
||||
): StatsFetchContext["requestResponseAsync"] {
|
||||
return async (path, init) => {
|
||||
try {
|
||||
const fetchAsync = testing?.fetchAsync ?? fetchWithTrustedCertificatesAsync;
|
||||
const response = await fetchAsync(this.url(path), { ...init, signal, redirect: "error" });
|
||||
// Login bodies can be empty or contain sensitive error details. Do not read them.
|
||||
await response.body?.cancel();
|
||||
let headerBytes = 0;
|
||||
for (const [name, value] of response.headers) {
|
||||
headerBytes += Buffer.byteLength(name) + Buffer.byteLength(value);
|
||||
if (headerBytes > 16_384) throw new Error("Response headers are too large");
|
||||
}
|
||||
return { status: response.status, headers: response.headers };
|
||||
} catch {
|
||||
// Fetch errors may retain the password body or session cookies in their cause.
|
||||
throw new Error("Stats authentication request failed");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private createRequestAsync(
|
||||
signal: AbortSignal,
|
||||
testing?: IntegrationTestingInput,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { RequestInit } from "undici";
|
||||
import type { Headers, RequestInit } from "undici";
|
||||
import type { IntegrationSecretKind } from "@homarr/definitions";
|
||||
|
||||
import type { IntegrationHttpAuthentication } from "../http-auth";
|
||||
@@ -26,8 +26,12 @@ export interface StatsAuthenticationContext {
|
||||
}
|
||||
|
||||
export interface StatsFetchContext extends StatsAuthenticationContext {
|
||||
/** Base integration URL. Credential exchanges must verify its protocol before reading or sending secrets. */
|
||||
url: URL;
|
||||
/** Relative service path. The shared client applies trusted certificates and a deadline. */
|
||||
requestAsync: (path: `/${string}`, init?: RequestInit) => Promise<unknown>;
|
||||
/** Private response metadata for cookie-based exchanges; the body is discarded, never parsed or exposed. */
|
||||
requestResponseAsync: (path: `/${string}`, init?: RequestInit) => Promise<{ status: number; headers: Headers }>;
|
||||
signal: AbortSignal;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type tls from "node:tls";
|
||||
import axios from "axios";
|
||||
import axios, { isAxiosError } from "axios";
|
||||
import { HttpCookieAgent, HttpsCookieAgent } from "http-cookie-agent/http";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import {
|
||||
getAllTrustedCertificatesAsync,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
fetchWithTrustedCertificatesAsync,
|
||||
getHttpRequestSignal,
|
||||
} from "@homarr/core/infrastructure/http";
|
||||
import { createLogger } from "@homarr/core/infrastructure/logs";
|
||||
import type { SiteStats } from "@homarr/node-unifi";
|
||||
import Unifi from "@homarr/node-unifi";
|
||||
|
||||
@@ -25,6 +27,10 @@ import type { NetworkControllerSummaryIntegration } from "../interfaces/network-
|
||||
import type { NetworkControllerSummary } from "../interfaces/network-controller-summary/network-controller-summary-types";
|
||||
import type { HealthSubsystem } from "./unifi-controller-types";
|
||||
|
||||
const logger = createLogger({ module: "unifi-controller-integration" });
|
||||
|
||||
const remoteVpnSessionsSchema = z.array(z.object({ up: z.boolean() }));
|
||||
|
||||
@HandleIntegrationErrors([integrationAxiosHttpErrorHandler])
|
||||
export class UnifiControllerIntegration extends Integration implements NetworkControllerSummaryIntegration {
|
||||
private readonly httpSessionStore = createSessionStore<{ cookie: string; csrfToken?: string }>(this.integration);
|
||||
@@ -45,6 +51,43 @@ export class UnifiControllerIntegration extends Integration implements NetworkCo
|
||||
public async getNetworkSummaryAsync(): Promise<NetworkControllerSummary> {
|
||||
const client = await this.createControllerClientAsync();
|
||||
const stats = await client.getSitesStats();
|
||||
const vpnUserCounts = await Promise.all(
|
||||
stats.map(async (site) => {
|
||||
try {
|
||||
const sessions: unknown = await client.customApiRequest(
|
||||
`/api/s/${encodeURIComponent(site.name)}/stat/remoteuservpn`,
|
||||
);
|
||||
return remoteVpnSessionsSchema.parse(sessions).filter((session) => session.up).length;
|
||||
} catch (error) {
|
||||
// Older controllers may not expose remote sessions. An empty successful
|
||||
// response is authoritative; only an unsupported endpoint uses health.
|
||||
if (isAxiosError(error) && error.response?.status === 404) {
|
||||
return this.getNumericValueOverAllSites([site], "vpn", (vpn) => vpn.remote_user_num_active, "sum");
|
||||
}
|
||||
let status: number | undefined;
|
||||
if (isAxiosError(error)) status = error.response?.status;
|
||||
let reason = "request-failed";
|
||||
if (error instanceof z.ZodError) reason = "invalid-response";
|
||||
logger.warn("UniFi remote VPN session count unavailable", {
|
||||
integrationId: this.integration.id,
|
||||
site: site.name,
|
||||
status,
|
||||
reason,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
// A partial site total cannot describe the controller's connected users.
|
||||
let vpnUsers: number | null = 0;
|
||||
for (const count of vpnUserCounts) {
|
||||
if (count === null) {
|
||||
vpnUsers = null;
|
||||
break;
|
||||
}
|
||||
vpnUsers += count;
|
||||
}
|
||||
|
||||
return {
|
||||
wanStatus: this.getStatusValueOverAllSites(stats, "wan", (site) => site.status === "ok"),
|
||||
@@ -66,7 +109,7 @@ export class UnifiControllerIntegration extends Integration implements NetworkCo
|
||||
},
|
||||
vpn: {
|
||||
status: this.getStatusValueOverAllSites(stats, "vpn", (site) => site.status === "ok"),
|
||||
users: this.getNumericValueOverAllSites(stats, "vpn", (site) => site.remote_user_num_active, "sum"),
|
||||
users: vpnUsers,
|
||||
},
|
||||
} satisfies NetworkControllerSummary;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Alert, Button, Stack, Text, TextInput } from "@mantine/core";
|
||||
import { IconArrowRight, IconShieldCheck, IconUserPlus } from "@tabler/icons-react";
|
||||
import { IconArrowLeft, IconArrowRight, IconShieldCheck, IconUserPlus } from "@tabler/icons-react";
|
||||
import type { z } from "zod/v4";
|
||||
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
@@ -36,11 +36,12 @@ const AccountShell = ({
|
||||
);
|
||||
|
||||
export const AccountSetup = ({ environment }: OnboardingStudioProps) => {
|
||||
if (environment.currentStep === "user") return <CredentialsSetup />;
|
||||
if (environment.currentStep === "user") return <CredentialsSetup canReturnToWelcome={!environment.hasUsers} />;
|
||||
return <ExternalGroupSetup />;
|
||||
};
|
||||
|
||||
const CredentialsSetup = () => {
|
||||
const CredentialsSetup = ({ canReturnToWelcome }: { canReturnToWelcome: boolean }) => {
|
||||
const navigationT = useI18n("init.studio");
|
||||
const t = useI18n("init.studio.account");
|
||||
const [requiresSignIn, setRequiresSignIn] = useState(false);
|
||||
const [submitError, setSubmitError] = useState<string | null>(null);
|
||||
@@ -54,6 +55,16 @@ const CredentialsSetup = () => {
|
||||
showErrorNotification({ title: t("errorTitle"), message });
|
||||
},
|
||||
});
|
||||
const returnToWelcome = clientApi.onboard.returnToWelcome.useMutation({
|
||||
async onSuccess() {
|
||||
sounds.swoosh();
|
||||
await revalidatePathActionAsync("/init");
|
||||
},
|
||||
onError() {
|
||||
sounds.error();
|
||||
showErrorNotification({ title: t("errorTitle"), message: t("unknownError") });
|
||||
},
|
||||
});
|
||||
const form = useZodForm(userInitSchema, {
|
||||
initialValues: { username: "", password: "", confirmPassword: "" },
|
||||
});
|
||||
@@ -119,9 +130,27 @@ const CredentialsSetup = () => {
|
||||
{submitError}
|
||||
</Alert>
|
||||
) : null}
|
||||
<Button type="submit" size="md" loading={form.submitting} rightSection={<IconArrowRight size={18} />}>
|
||||
<Button
|
||||
type="submit"
|
||||
size="md"
|
||||
loading={form.submitting}
|
||||
disabled={returnToWelcome.isPending}
|
||||
rightSection={<IconArrowRight size={18} />}
|
||||
>
|
||||
{t("create")}
|
||||
</Button>
|
||||
{canReturnToWelcome && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="default"
|
||||
leftSection={<IconArrowLeft size={18} />}
|
||||
loading={returnToWelcome.isPending}
|
||||
disabled={form.submitting}
|
||||
onClick={() => returnToWelcome.mutate()}
|
||||
>
|
||||
{navigationT("back")}
|
||||
</Button>
|
||||
)}
|
||||
</Stack>
|
||||
</form>
|
||||
</AccountShell>
|
||||
|
||||
@@ -5738,6 +5738,7 @@
|
||||
"title": "Make this board useful",
|
||||
"description": "Pick up where you left off. Progress follows the content already on this board.",
|
||||
"collapse": "Collapse setup checklist",
|
||||
"dismiss": "Dismiss setup checklist",
|
||||
"resume": "Continue setup ({completed}/{total})",
|
||||
"progress": "Board setup progress",
|
||||
"step": {
|
||||
|
||||
@@ -29,7 +29,8 @@ export const integrationCreateSchema = z.object({
|
||||
});
|
||||
|
||||
export const integrationUpdateSchema = z.object({
|
||||
id: z.string().cuid2(),
|
||||
// Updates address an existing row, including externally seeded IDs.
|
||||
id: z.string().nonempty(),
|
||||
name: z.string().nonempty().max(127),
|
||||
url: z.string().url(),
|
||||
secrets: z.array(
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
.appText {
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.appText > * {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.appIcon {
|
||||
/* Keep labels and descriptions from consuming all of a short tile. */
|
||||
min-height: 40%;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
|
||||
@@ -21,7 +21,13 @@ import classes from "./app.module.css";
|
||||
import { PingDot } from "./ping/ping-dot";
|
||||
import { PingIndicator } from "./ping/ping-indicator";
|
||||
|
||||
export default function AppWidget({ options, isEditMode, height, width }: WidgetComponentProps<"app">) {
|
||||
export default function AppWidget({
|
||||
options,
|
||||
isEditMode,
|
||||
height,
|
||||
width,
|
||||
displayScale = 1,
|
||||
}: WidgetComponentProps<"app">) {
|
||||
const tCommon = useI18n("common");
|
||||
const settings = useSettings();
|
||||
const board = useRequiredBoard();
|
||||
@@ -33,33 +39,50 @@ export default function AppWidget({ options, isEditMode, height, width }: Widget
|
||||
if (isInitialWidgetQueryPending(appQuery)) return <WidgetQueryLoadingState />;
|
||||
if (!app) return <WidgetEmptyState />;
|
||||
|
||||
const isTiny = height < 100 || width < 100;
|
||||
let layoutScale = displayScale;
|
||||
if (!Number.isFinite(layoutScale) || layoutScale <= 0) layoutScale = 1;
|
||||
|
||||
// Widget dimensions are logical pixels; compact sizing follows the displayed tile.
|
||||
const isTiny = height * layoutScale < 100 || width * layoutScale < 100;
|
||||
const isColumnLayout = options.layout.startsWith("column");
|
||||
let padding = 12;
|
||||
let fontSize = rem(14);
|
||||
let gap = 12;
|
||||
let titleLineClamp = 2;
|
||||
if (isTiny) {
|
||||
padding = 4;
|
||||
fontSize = rem(8);
|
||||
titleLineClamp = 1;
|
||||
}
|
||||
if (isColumnLayout) gap = 0;
|
||||
|
||||
return (
|
||||
<Box h="100%" w="100%" pos="relative">
|
||||
<Box h="100%" w="100%" pos="relative" style={{ "--mantine-scale": 1 / layoutScale }}>
|
||||
<AppLink href={href} openInNewTab={options.openInNewTab} enabled={Boolean(href) && !isEditMode}>
|
||||
<AppDescriptionTooltip
|
||||
description={app.description}
|
||||
enabled={options.descriptionDisplayMode === "tooltip" && Boolean(app.description) && !isEditMode}
|
||||
>
|
||||
<Flex
|
||||
p={isTiny ? 4 : "sm"}
|
||||
p={padding}
|
||||
className={combineClasses("app-flex-wrapper", app.name, app.id, href && classes.appWithUrl)}
|
||||
h="100%"
|
||||
w="100%"
|
||||
direction={options.layout}
|
||||
justify="center"
|
||||
align="center"
|
||||
gap={isColumnLayout ? 0 : "sm"}
|
||||
gap={gap}
|
||||
onContextMenu={isEditMode ? (e) => e.preventDefault() : undefined}
|
||||
>
|
||||
<Stack gap={0}>
|
||||
<Stack gap={0} className={classes.appText}>
|
||||
{options.showTitle && (
|
||||
<Text
|
||||
className="app-title"
|
||||
title={app.name}
|
||||
fw={700}
|
||||
size={isTiny ? rem(8) : "sm"}
|
||||
size={fontSize}
|
||||
lh="sm"
|
||||
lineClamp={titleLineClamp}
|
||||
ta={isColumnLayout ? "center" : undefined}
|
||||
>
|
||||
{app.name}
|
||||
@@ -68,7 +91,8 @@ export default function AppWidget({ options, isEditMode, height, width }: Widget
|
||||
{options.descriptionDisplayMode === "normal" && (
|
||||
<Text
|
||||
className="app-description"
|
||||
size={isTiny ? rem(8) : "sm"}
|
||||
size={fontSize}
|
||||
lh="sm"
|
||||
ta={isColumnLayout ? "center" : undefined}
|
||||
c="dimmed"
|
||||
lineClamp={4}
|
||||
|
||||
@@ -66,6 +66,10 @@ export default function NetworkControllerSummaryWidget({
|
||||
</Group>
|
||||
);
|
||||
const firstSummary = summaries[0];
|
||||
const formatVpnCount = (count: number | null) => {
|
||||
if (count === null) return statusLabels.unknown;
|
||||
return t("card.vpn.countConnected", { count });
|
||||
};
|
||||
|
||||
let responsiveWidth = width;
|
||||
let responsiveHeight = height;
|
||||
@@ -154,15 +158,11 @@ export default function NetworkControllerSummaryWidget({
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Text
|
||||
title={t("card.vpn.countConnected", { count: summary.vpn.users })}
|
||||
truncate="end"
|
||||
style={{ minWidth: 0 }}
|
||||
>
|
||||
<Text title={formatVpnCount(summary.vpn.users)} truncate="end" style={{ minWidth: 0 }}>
|
||||
{t("card.vpn.label")}
|
||||
{showSecondary && (
|
||||
<Text c="dimmed" size={secondaryTextSize} ms="xs" span>
|
||||
{t("card.vpn.countConnected", { count: summary.vpn.users })}
|
||||
{formatVpnCount(summary.vpn.users)}
|
||||
</Text>
|
||||
)}
|
||||
</Text>
|
||||
@@ -234,7 +234,7 @@ export default function NetworkControllerSummaryWidget({
|
||||
{getMatrixMetricLabel(metric.key, t)}
|
||||
</Text>
|
||||
<Text size="xs" fw={500}>
|
||||
{formatMatrixMetric(metric.key, metric.value)}
|
||||
{formatMatrixMetric(metric.key, metric.value, statusLabels.unknown)}
|
||||
</Text>
|
||||
</Stack>
|
||||
))}
|
||||
@@ -257,7 +257,8 @@ const getMatrixMetricLabel = (
|
||||
t: ScopedTranslationFunction<"widget.networkControllerSummary">,
|
||||
) => t(`advanced.metric.${key}` as never);
|
||||
|
||||
const formatMatrixMetric = (key: NetworkControllerMatrixMetricKey, value: number) => {
|
||||
const formatMatrixMetric = (key: NetworkControllerMatrixMetricKey, value: number | null, unknown: string) => {
|
||||
if (value === null) return unknown;
|
||||
if (key === "latency" || key === "ping") return `${value}ms`;
|
||||
if (key === "uptime") return dayjs.duration(value, "seconds").humanize();
|
||||
return String(value);
|
||||
|
||||
@@ -6,7 +6,7 @@ export type NetworkControllerMatrixMetricKey = "latency" | "ping" | "uptime" | "
|
||||
export interface NetworkControllerMatrixSection {
|
||||
key: NetworkControllerMatrixSectionKey;
|
||||
status: "enabled" | "disabled";
|
||||
metrics: { key: NetworkControllerMatrixMetricKey; value: number }[];
|
||||
metrics: { key: NetworkControllerMatrixMetricKey; value: number | null }[];
|
||||
}
|
||||
|
||||
export const getNetworkControllerMatrix = (summary: NetworkControllerSummary): NetworkControllerMatrixSection[] => [
|
||||
|
||||
Generated
+145
-133
@@ -529,8 +529,8 @@ catalogs:
|
||||
specifier: ^11.16.1
|
||||
version: 11.17.2
|
||||
next:
|
||||
specifier: 16.3.3
|
||||
version: 16.3.3
|
||||
specifier: 16.3.6
|
||||
version: 16.3.6
|
||||
next-auth:
|
||||
specifier: 5.0.0-beta.32
|
||||
version: 5.0.0-beta.32
|
||||
@@ -705,25 +705,25 @@ overrides:
|
||||
'@assistant-ui/core': 0.3.7
|
||||
'@assistant-ui/store': 0.3.5
|
||||
'@assistant-ui/tap': 0.9.10
|
||||
'@grpc/grpc-js@<1.12.7': 1.12.7
|
||||
'@grpc/grpc-js@<1.12.7': 1.13.6
|
||||
'@rvf/set-get@<7.0.2': 7.0.2
|
||||
'@xmldom/xmldom@<0.8.15': 0.8.15
|
||||
assistant-cloud: 0.1.38
|
||||
'@babel/helpers@<7.26.10': '>=7.29.7'
|
||||
'@babel/runtime@<7.26.10': '>=7.29.7'
|
||||
axios@>=1.0.0 <1.18.1: 1.18.1
|
||||
brace-expansion@>=1.0.0 <1.1.18: 1.1.18
|
||||
brace-expansion@>=2.0.0 <2.1.4: 2.1.4
|
||||
brace-expansion@>=4.0.0 <5.0.9: 5.0.9
|
||||
axios@>=1.0.0 <1.18.1: 1.20.0
|
||||
brace-expansion@>=1.0.0 <1.1.18: 1.1.21
|
||||
brace-expansion@>=2.0.0 <2.1.4: 2.1.7
|
||||
brace-expansion@>=4.0.0 <5.0.9: 5.0.12
|
||||
browserslist@<4.28.7: 4.28.7
|
||||
esbuild@<=0.24.2: '>=0.28.1'
|
||||
fast-uri@>=3.0.0 <3.1.6: 3.1.6
|
||||
fast-uri@>=3.0.0 <3.1.6: 3.1.8
|
||||
'@ai-sdk/provider-utils>undici': 8.10.2
|
||||
find-my-way@<9.6.1: 9.9.0
|
||||
form-data@>=4.0.0 <4.0.4: '>=4.0.6'
|
||||
hono@<4.6.5: '>=4.13.7'
|
||||
immutable@>=5.0.0-beta.1 <5.1.8: 5.1.8
|
||||
ip-address@<10.3.1: 10.3.1
|
||||
ip-address@<10.3.1: 10.7.1
|
||||
image-size@<=2.0.2: 2.0.3
|
||||
js-yaml@>=3.0.0 <3.15.2: 3.15.2
|
||||
js-yaml@>=4.0.0 <4.3.2: 4.3.2
|
||||
@@ -873,7 +873,7 @@ importers:
|
||||
version: 0.99.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
'@scalar/api-client-react':
|
||||
specifier: 'catalog:'
|
||||
version: 2.0.51(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(react@19.2.7)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
version: 2.0.51(axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(react@19.2.7)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@tabler/icons-react':
|
||||
specifier: 'catalog:'
|
||||
version: 3.44.0(react@19.2.7)
|
||||
@@ -897,16 +897,16 @@ importers:
|
||||
version: 6.0.0
|
||||
fumadocs-core:
|
||||
specifier: 'catalog:'
|
||||
version: 16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3)
|
||||
version: 16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3)
|
||||
fumadocs-mdx:
|
||||
specifier: 'catalog:'
|
||||
version: 15.4.3(ca407bc45757862c0553f46d1969d5d6)
|
||||
version: 15.4.3(0bc99cc3ab6490dd33b76f4900ace488)
|
||||
fumadocs-openapi:
|
||||
specifier: 'catalog:'
|
||||
version: 12.0.1(2b987dc9753a22b34010c9167028a29c)
|
||||
version: 12.0.1(226091f6acf12b47d5f750357219f3e3)
|
||||
fumadocs-ui:
|
||||
specifier: 'catalog:'
|
||||
version: 16.15.12(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.2)
|
||||
version: 16.15.12(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.2)
|
||||
json-schema-typed:
|
||||
specifier: 'catalog:'
|
||||
version: 8.0.2
|
||||
@@ -915,7 +915,7 @@ importers:
|
||||
version: 11.17.2
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
version: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
next-themes:
|
||||
specifier: 'catalog:'
|
||||
version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
@@ -1186,7 +1186,7 @@ importers:
|
||||
version: 5.101.2(@tanstack/react-query@5.101.2(react@19.2.7))(react@19.2.7)
|
||||
'@tanstack/react-query-next-experimental':
|
||||
specifier: 'catalog:'
|
||||
version: 5.101.2(@tanstack/react-query@5.101.2(react@19.2.7))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7)
|
||||
version: 5.101.2(@tanstack/react-query@5.101.2(react@19.2.7))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7)
|
||||
'@tanstack/react-query-persist-client':
|
||||
specifier: 'catalog:'
|
||||
version: 5.101.2(@tanstack/react-query@5.101.2(react@19.2.7))(react@19.2.7)
|
||||
@@ -1195,7 +1195,7 @@ importers:
|
||||
version: 11.18.0(@trpc/server@11.18.0(typescript@7.0.2))(typescript@7.0.2)
|
||||
'@trpc/next':
|
||||
specifier: 'catalog:'
|
||||
version: 11.18.0(@tanstack/react-query@5.101.2(react@19.2.7))(@trpc/client@11.18.0(@trpc/server@11.18.0(typescript@7.0.2))(typescript@7.0.2))(@trpc/react-query@11.18.0(@tanstack/react-query@5.101.2(react@19.2.7))(@trpc/client@11.18.0(@trpc/server@11.18.0(typescript@7.0.2))(typescript@7.0.2))(@trpc/server@11.18.0(typescript@7.0.2))(react@19.2.7)(typescript@7.0.2))(@trpc/server@11.18.0(typescript@7.0.2))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@7.0.2)
|
||||
version: 11.18.0(@tanstack/react-query@5.101.2(react@19.2.7))(@trpc/client@11.18.0(@trpc/server@11.18.0(typescript@7.0.2))(typescript@7.0.2))(@trpc/react-query@11.18.0(@tanstack/react-query@5.101.2(react@19.2.7))(@trpc/client@11.18.0(@trpc/server@11.18.0(typescript@7.0.2))(typescript@7.0.2))(@trpc/server@11.18.0(typescript@7.0.2))(react@19.2.7)(typescript@7.0.2))(@trpc/server@11.18.0(typescript@7.0.2))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@7.0.2)
|
||||
'@trpc/react-query':
|
||||
specifier: 'catalog:'
|
||||
version: 11.18.0(@tanstack/react-query@5.101.2(react@19.2.7))(@trpc/client@11.18.0(@trpc/server@11.18.0(typescript@7.0.2))(typescript@7.0.2))(@trpc/server@11.18.0(typescript@7.0.2))(react@19.2.7)(typescript@7.0.2)
|
||||
@@ -1267,7 +1267,7 @@ importers:
|
||||
version: 11.17.2
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
version: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
postcss-preset-mantine:
|
||||
specifier: 'catalog:'
|
||||
version: 1.18.0(postcss@8.5.23)
|
||||
@@ -1595,7 +1595,7 @@ importers:
|
||||
version: 4.5.0
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
version: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 19.2.7
|
||||
@@ -1656,10 +1656,10 @@ importers:
|
||||
version: 8.1.8
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
version: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
next-auth:
|
||||
specifier: 'catalog:'
|
||||
version: 5.0.0-beta.32(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7)
|
||||
version: 5.0.0-beta.32(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7)
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 19.2.7
|
||||
@@ -1755,7 +1755,7 @@ importers:
|
||||
version: 1.11.21
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
version: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
octokit:
|
||||
specifier: 'catalog:'
|
||||
version: 5.0.5
|
||||
@@ -2308,7 +2308,7 @@ importers:
|
||||
version: 3.0.2
|
||||
'@jellyfin/sdk':
|
||||
specifier: 'catalog:'
|
||||
version: 0.13.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))
|
||||
version: 0.13.0(axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))
|
||||
ical.js:
|
||||
specifier: 'catalog:'
|
||||
version: 2.2.1
|
||||
@@ -2424,7 +2424,7 @@ importers:
|
||||
version: 1.11.21
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
version: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 19.2.7
|
||||
@@ -2662,7 +2662,7 @@ importers:
|
||||
version: 9.6.0(@mantine/core@9.6.0(@mantine/hooks@9.6.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.6.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
version: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 19.2.7
|
||||
@@ -2729,7 +2729,7 @@ importers:
|
||||
version: 2.20.1(@babel/core@7.29.0(supports-color@10.2.2))(@babel/template@7.29.7)(@types/react@19.2.17)(react@19.2.7)
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
version: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 19.2.7
|
||||
@@ -2766,10 +2766,10 @@ importers:
|
||||
version: mantine-react-table-open@9.0.3(@mantine/core@9.6.0(@mantine/hooks@9.6.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.6.0(@mantine/core@9.6.0(@mantine/hooks@9.6.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.6.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.6.0(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
version: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
next-intl:
|
||||
specifier: 'catalog:'
|
||||
version: 4.13.1(@swc/helpers@0.5.23)(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7)(typescript@7.0.2)
|
||||
version: 4.13.1(@swc/helpers@0.5.23)(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7)(typescript@7.0.2)
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 19.2.7
|
||||
@@ -2821,7 +2821,7 @@ importers:
|
||||
version: mantine-react-table-open@9.0.3(@mantine/core@9.6.0(@mantine/hooks@9.6.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.6.0(@mantine/core@9.6.0(@mantine/hooks@9.6.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.6.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.6.0(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
version: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 19.2.7
|
||||
@@ -3045,7 +3045,7 @@ importers:
|
||||
version: mantine-react-table-open@9.0.3(@mantine/core@9.6.0(@mantine/hooks@9.6.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/dates@9.6.0(@mantine/core@9.6.0(@mantine/hooks@9.6.0(react@19.2.7))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.6.0(react@19.2.7))(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(@mantine/hooks@9.6.0(react@19.2.7))(@tabler/icons-react@3.44.0(react@19.2.7))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
next:
|
||||
specifier: 'catalog:'
|
||||
version: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
version: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
react:
|
||||
specifier: 'catalog:'
|
||||
version: 19.2.7
|
||||
@@ -4516,8 +4516,8 @@ packages:
|
||||
react: ^18.x || ^19.x
|
||||
react-dom: ^18.x || ^19.x
|
||||
|
||||
'@grpc/grpc-js@1.12.7':
|
||||
resolution: {integrity: sha512-Tugep4V4GCsN9dSAVkXoBWqJXDoH+NSXd50kFGsm7ZG1tBXFEAnLoxzkGUIksTrDjiaMwlpsbC24oWbESGHpIQ==}
|
||||
'@grpc/grpc-js@1.13.6':
|
||||
resolution: {integrity: sha512-S9U8ioEds9fo8zCt4K8CyWHLJPg63FBgZo/hYUPVTiKppwsx/ykTnFg4g4/70beoa/DYao9uROQRcoahVH8PVQ==}
|
||||
engines: {node: '>=12.10.0'}
|
||||
|
||||
'@grpc/proto-loader@0.7.13':
|
||||
@@ -4903,7 +4903,7 @@ packages:
|
||||
'@jellyfin/sdk@0.13.0':
|
||||
resolution: {integrity: sha512-oiBAOXH6s+dKdReSsYgNktBDzbxtg4JVWhEzIxZSxKcWMdSKmBtK41MhXRO7IWAC40DguKUm3nU/Z493qPAlWA==}
|
||||
peerDependencies:
|
||||
axios: 1.18.1
|
||||
axios: 1.20.0
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
|
||||
@@ -5306,57 +5306,57 @@ packages:
|
||||
'@emnapi/core': ^1.7.1
|
||||
'@emnapi/runtime': ^1.7.1
|
||||
|
||||
'@next/env@16.3.3':
|
||||
resolution: {integrity: sha512-U2eYQRwXj+dsqxV79zFqExDdatnNY/ZWc2nsJU1p/OgT7fd3dXwlF6OjYaFQCfMoeTA19PWq+wVmYgimVA+V+g==}
|
||||
'@next/env@16.3.6':
|
||||
resolution: {integrity: sha512-x9Vblze1EbtltQYnNH38xCPWU3TVfBd1eXqA3+w9+BTpedkkdNpAaltXlGQ/nsc1+E0mVTNrtcbX3GoO09zeLQ==}
|
||||
|
||||
'@next/swc-darwin-arm64@16.3.3':
|
||||
resolution: {integrity: sha512-8Hiv32QJPwdV6KYJ8meR9SBA061tQqnIKTJDocvOXlEQqib0xMFpzArosuffFUUc0sslbh7QQ8a3Yey1QV8EIw==}
|
||||
'@next/swc-darwin-arm64@16.3.6':
|
||||
resolution: {integrity: sha512-E/7GEqaUkt8mk/T8v9lAnrhzR06kdq1ZBkC12F8tAMkdIadwNp3H1KqHynDHrpcTlGCUdq/qu6vUL2aYVyYBdw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@next/swc-darwin-x64@16.3.3':
|
||||
resolution: {integrity: sha512-A1lgKgwVchRYmSe467zdwhxT9040dd8lH+o65sL5Jet8fjB4kegw/rDyPIpYVRb6jAqwXFOJpjIXJLxQKLiE3A==}
|
||||
'@next/swc-darwin-x64@16.3.6':
|
||||
resolution: {integrity: sha512-yBE893/nDWTlaiBD1p+qgt7NUen4U5R6FXyH0s67Npq1S3E0cVSef1WIXC2xBRgQvwAvJq6DnS6Y6PrY0cy4Ew==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@next/swc-linux-arm64-gnu@16.3.3':
|
||||
resolution: {integrity: sha512-bf0FIssMFueU2dm7vQEWWxk0c8UjKTdW0yzuh0sQsD8pf1+KCLDdaqhYZNMYGmXwEOiHAUzgBKudovIlcvvBjg==}
|
||||
'@next/swc-linux-arm64-gnu@16.3.6':
|
||||
resolution: {integrity: sha512-KJDpjBqBPYlvkivmyrp+Qys6k/7ksbqGQvRVc6ZEGfR+cjQxx+nUkJaWmNZJsmoOrqYNbaXByF8wa0lBwDhB3Q==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-arm64-musl@16.3.3':
|
||||
resolution: {integrity: sha512-W7viwCk9JY/cAkdz/A273rd5bb3RgT/IHwR7Upv90tunjBWNtAAhGhoecHh+teRNRSinuAFmE+l7fwZ4YKkrXg==}
|
||||
'@next/swc-linux-arm64-musl@16.3.6':
|
||||
resolution: {integrity: sha512-mqNg2K+hvWskSRb/QM+Ix412DvBsuSF0XV+frTSw5vmoucNnIlynFwKYew8D01bfATErMOM7Bujrf0BA5DRKFA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-linux-x64-gnu@16.3.3':
|
||||
resolution: {integrity: sha512-0W46zw1N3ODpI6n0GeivHvvob1pooozgZVqy65k0mh4/7vr+FbY9+WpHzNVXjHipJf/A3FDheBG19H1s5A25rA==}
|
||||
'@next/swc-linux-x64-gnu@16.3.6':
|
||||
resolution: {integrity: sha512-nFncBNGAYouRHjRVaITs9beZRfhX4ssVwpnvPIAbkZVH6LtGoAVlH4bJ8Cnf9SOo9bsXgPFer/GdHtEE3JNOkw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@next/swc-linux-x64-musl@16.3.3':
|
||||
resolution: {integrity: sha512-H4mBso8ZTMBPtdT0PN0pBx2ayTvQuTuvS6qT13d77yVFJXAPCxkyIhLTmdMaGTJs0krQYI/qpzdHijCeihXhbg==}
|
||||
'@next/swc-linux-x64-musl@16.3.6':
|
||||
resolution: {integrity: sha512-5Mf3cHDGR/Iz0ng2Bj3zUR3p5QS9YK3Hn2QiAfavFmyF48zwThAjpFoiTKNIcOHLYS4zEk+gzyJ/9deQ2ZB8yQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@next/swc-win32-arm64-msvc@16.3.3':
|
||||
resolution: {integrity: sha512-cTMUJpcEGmeywofCUfhR+rSsoE33+rVPnPEYNTNdLNlsOeEg/vktOsKUSTb28vUGqD2jkm4Zaskcwn7OCI6FQg==}
|
||||
'@next/swc-win32-arm64-msvc@16.3.6':
|
||||
resolution: {integrity: sha512-0jkJy0C2kbrJWTk4YLa3xk80pVBpx8FCHJym7CnUfDAXe/FWv5qT7SQJbR0KuemyxaEDlEx5WT4VQJoTW+/9Qw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@next/swc-win32-x64-msvc@16.3.3':
|
||||
resolution: {integrity: sha512-2VR4cTBzHXaBjnGsuH6GyJjENzQOmHeAh11uY1iUhjm3j5dEUrVJuUj+VL78jaGi/Dik8xS76zEj18BsFhlVZQ==}
|
||||
'@next/swc-win32-x64-msvc@16.3.6':
|
||||
resolution: {integrity: sha512-/YXjI1e5OXcZ7YpxRwgP/1jAV/SBKTzeVKqN2mk7mLpcICsyn3Gl5+dIfDTJp70M0ccMhyMMRso4v6mPDCGepg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@@ -8368,7 +8368,7 @@ packages:
|
||||
resolution: {integrity: sha512-SDobKBbPIOe0cVL7QxMzGkuUGHvWTdihi9zOrrWaWUgFKe15cwEcwfWmgrcNzjT6kHnNmWuTajPHoIzUjYNYYQ==}
|
||||
peerDependencies:
|
||||
async-validator: ^4
|
||||
axios: 1.18.1
|
||||
axios: 1.20.0
|
||||
change-case: ^5
|
||||
drauu: ^0.4
|
||||
focus-trap: ^7
|
||||
@@ -8496,67 +8496,79 @@ packages:
|
||||
resolution: {integrity: sha512-xt4Vy3RTBw1NfszBlf4A0Rr6ZjA/th2Pj9XlWFLQHW9A1qOCtxNCoIWSzn/INtnZLMB4jO2gWdEtSaHUtK73wA==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-analyzer/binding-darwin-arm64@0.10.2':
|
||||
resolution: {integrity: sha512-ftokSqvYXZxfmA/bfIbp8c1BJ8QxNax371kUOQNFyvR5hmtKqQyTqSKtMk+WaixHM3w4tKj/JdyY+ud1bGamXA==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-analyzer/binding-darwin-x64@0.10.2':
|
||||
resolution: {integrity: sha512-yUrmmhmF5R4Xmbfo8gUxJKr3YbvbLcE2JvLhDLzFJkMN29/fGm6K3dZX6YpNecfgBDnQ+VywGp8K9xjRaBFZ3Q==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-analyzer/binding-freebsd-x64@0.10.2':
|
||||
resolution: {integrity: sha512-cy9+KvVoK5Xad3Vs5pLwAeZf3JC/1y0XJElgv1q6g7W7g/n52FRjedpYmQiJeMZdnhJiAshq7Vz/Xj3jC5Ns2Q==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-analyzer/binding-linux-arm-gnu@0.10.2':
|
||||
resolution: {integrity: sha512-AgVZ6VCV2XP4yHCwyv78KLV76gcu2neiEF2+RBTPQN7sqYY+bLvMxphGPmfG+9qk5Uf1PJAhZYIUnMGVbbRCZw==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-analyzer/binding-linux-arm-musl@0.10.2':
|
||||
resolution: {integrity: sha512-5qAj2GjoSN6+HuH75+g5r40CFhWmMvH0K8VKWmPfQyOgA/LonoDMr5H5O5feJwzdnfdCytPDAh1RAf7/SSg7dg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-analyzer/binding-linux-arm64-gnu@0.10.2':
|
||||
resolution: {integrity: sha512-eJvQ4Ja5oPf/HOjmBkCLYyhOHUkL7U+iMAN8sX44x8exBBpPRyqnhEgTsuU73HzxPLrGPdRhybFnAE63Omib/Q==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-analyzer/binding-linux-arm64-musl@0.10.2':
|
||||
resolution: {integrity: sha512-TppF2j194mOQ/kBXvNr9u5TYO+eR9r8ieg4lTIEyJIVYHAR9GWeB0oFHLcJiLXVa3qUJGzE4bmzSntCW5ylA7A==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-analyzer/binding-linux-x64-gnu@0.10.2':
|
||||
resolution: {integrity: sha512-03xu6ocJuwhm1a/JKmrHiaJUO1Oj86rABsm4FAl8AvfewfW/9idIUmPiFxZfb6RfiIONegebsTGJhFx0NbBNpA==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-analyzer/binding-linux-x64-musl@0.10.2':
|
||||
resolution: {integrity: sha512-f7W2sAWWQN8x8YB0RRxIche7mLImH7lDgzUy9+B69cM2I6VFJ4a5+0pgAuMLROPOqimaxFPdXqLdDE/vpgGltg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-analyzer/binding-win32-arm64@0.10.2':
|
||||
resolution: {integrity: sha512-f534qNc5YaJI29e1oryxEnxPkp7WCYJ1j7ChSec8pQNlYqTm8Tao7hij7NxRF+9BuxzeZCZ+WsbaAfLlklR0Eg==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-analyzer/binding-win32-x64@0.10.2':
|
||||
resolution: {integrity: sha512-sKHkmLK8BsindMfyIThoN0bWC5uqSJEVNaLRD/ifA99mtb0UDin1ZCg4WYSVQAmM4k2iOTl4UZx/p3zcBiBgYw==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
deprecated: yuku-analyzer runs on yuku-core since 0.14
|
||||
|
||||
'@yuku-toolchain/types@0.10.2':
|
||||
resolution: {integrity: sha512-sSeo4SSSToiS+sSD+bwn/s94EEcaLJ7tG5LCp8gFYC1G5VzxzX7fqB6m9RU1yMD8KTpu6zdU/I1NlQf8hVBJ9Q==}
|
||||
@@ -8755,8 +8767,8 @@ packages:
|
||||
avvio@9.2.0:
|
||||
resolution: {integrity: sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==}
|
||||
|
||||
axios@1.18.1:
|
||||
resolution: {integrity: sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==}
|
||||
axios@1.20.0:
|
||||
resolution: {integrity: sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==}
|
||||
|
||||
b4a@1.6.6:
|
||||
resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==}
|
||||
@@ -8851,14 +8863,14 @@ packages:
|
||||
resolution: {integrity: sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==}
|
||||
engines: {node: '>=14.16'}
|
||||
|
||||
brace-expansion@1.1.18:
|
||||
resolution: {integrity: sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==}
|
||||
brace-expansion@1.1.21:
|
||||
resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==}
|
||||
|
||||
brace-expansion@2.1.4:
|
||||
resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==}
|
||||
brace-expansion@2.1.7:
|
||||
resolution: {integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==}
|
||||
|
||||
brace-expansion@5.0.9:
|
||||
resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==}
|
||||
brace-expansion@5.0.12:
|
||||
resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==}
|
||||
engines: {node: 20 || >=22}
|
||||
|
||||
braces@3.0.3:
|
||||
@@ -10034,8 +10046,8 @@ packages:
|
||||
fast-string-width@3.0.2:
|
||||
resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==}
|
||||
|
||||
fast-uri@3.1.6:
|
||||
resolution: {integrity: sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==}
|
||||
fast-uri@3.1.8:
|
||||
resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==}
|
||||
|
||||
fast-uri@4.2.1:
|
||||
resolution: {integrity: sha512-TmHQgewjHtMq1E5QKA0tOE0yeYGQs25KZC/ziJpubRtWI15W92e6vPFydWOeZBVROSHBYw/QhD9d5OefdD6LDg==}
|
||||
@@ -10685,8 +10697,8 @@ packages:
|
||||
resolution: {integrity: sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==}
|
||||
engines: {node: '>=12.22.0'}
|
||||
|
||||
ip-address@10.3.1:
|
||||
resolution: {integrity: sha512-1e9d3kb97NHJTIJDZW9rKqW2h6+dFa50Dy0fpPSMQp2ADje5gvKsXmdiK6dwY5t76TaTt5+P5N1Y/LoToIxP6g==}
|
||||
ip-address@10.7.1:
|
||||
resolution: {integrity: sha512-4OUAqU9Z1i3vCnS05hzGiFnEMDpQ+62pAD/MVQOp83fYyNC8GleCqaS0QikQBmcWCrKFiUs/B8ztRRiYOAXuCA==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
ipaddr.js@2.3.0:
|
||||
@@ -11641,8 +11653,8 @@ packages:
|
||||
'@types/unist':
|
||||
optional: true
|
||||
|
||||
next@16.3.3:
|
||||
resolution: {integrity: sha512-tuRTx1nQ/yVw83cwJBo9F+njGUgMn3UHQycreWHB8XsStvvAh1AthbI8/4IpKnFaF58F+iSiHejYOlMQ/eq83g==}
|
||||
next@16.3.6:
|
||||
resolution: {integrity: sha512-L+otWM/aQbYTx98aZhgEoMb4bZAXx1YVW4UMA/vuCyCoWG5HJyZUili8QAkqzrcC+5///tsz3s0M+SlyB5bLMw==}
|
||||
engines: {node: '>=20.9.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -15397,7 +15409,7 @@ snapshots:
|
||||
dependencies:
|
||||
ajv: 8.18.0
|
||||
ajv-formats: 3.0.1(ajv@8.18.0)
|
||||
fast-uri: 3.1.6
|
||||
fast-uri: 3.1.8
|
||||
|
||||
'@fastify/error@4.2.0': {}
|
||||
|
||||
@@ -15568,7 +15580,7 @@ snapshots:
|
||||
react: 19.2.7
|
||||
react-dom: 19.2.7(react@19.2.7)
|
||||
|
||||
'@grpc/grpc-js@1.12.7':
|
||||
'@grpc/grpc-js@1.13.6':
|
||||
dependencies:
|
||||
'@grpc/proto-loader': 0.7.13
|
||||
'@js-sdsl/ordered-map': 4.4.2
|
||||
@@ -15593,7 +15605,7 @@ snapshots:
|
||||
|
||||
'@homarr/node-unifi@2.6.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)(undici@7.29.1)':
|
||||
dependencies:
|
||||
axios: 1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)
|
||||
axios: 1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)
|
||||
eventemitter2: 6.4.9
|
||||
http-cookie-agent: 6.0.8(tough-cookie@5.1.2)(undici@7.29.1)
|
||||
tough-cookie: 5.1.2
|
||||
@@ -15902,9 +15914,9 @@ snapshots:
|
||||
|
||||
'@isaacs/cliui@9.0.0': {}
|
||||
|
||||
'@jellyfin/sdk@0.13.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))':
|
||||
'@jellyfin/sdk@0.13.0(axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))':
|
||||
dependencies:
|
||||
axios: 1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)
|
||||
axios: 1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
dependencies:
|
||||
@@ -16430,30 +16442,30 @@ snapshots:
|
||||
'@tybys/wasm-util': 0.10.3
|
||||
optional: true
|
||||
|
||||
'@next/env@16.3.3': {}
|
||||
'@next/env@16.3.6': {}
|
||||
|
||||
'@next/swc-darwin-arm64@16.3.3':
|
||||
'@next/swc-darwin-arm64@16.3.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-darwin-x64@16.3.3':
|
||||
'@next/swc-darwin-x64@16.3.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-arm64-gnu@16.3.3':
|
||||
'@next/swc-linux-arm64-gnu@16.3.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-arm64-musl@16.3.3':
|
||||
'@next/swc-linux-arm64-musl@16.3.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-x64-gnu@16.3.3':
|
||||
'@next/swc-linux-x64-gnu@16.3.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-x64-musl@16.3.3':
|
||||
'@next/swc-linux-x64-musl@16.3.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-win32-arm64-msvc@16.3.3':
|
||||
'@next/swc-win32-arm64-msvc@16.3.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-win32-x64-msvc@16.3.3':
|
||||
'@next/swc-win32-x64-msvc@16.3.6':
|
||||
optional: true
|
||||
|
||||
'@nivo/annotations@0.99.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)':
|
||||
@@ -17865,7 +17877,7 @@ snapshots:
|
||||
'@scalar/agent-chat@0.12.37(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)(zod@4.4.3)':
|
||||
dependencies:
|
||||
'@ai-sdk/vue': 3.0.33(vue@3.5.42(typescript@7.0.2))(zod@4.4.3)
|
||||
'@scalar/api-client': 3.21.1(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@scalar/api-client': 3.21.1(axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@scalar/components': 0.30.3(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@scalar/helpers': 0.15.0
|
||||
'@scalar/icons': 0.7.6(typescript@7.0.2)
|
||||
@@ -17900,9 +17912,9 @@ snapshots:
|
||||
- universal-cookie
|
||||
- zod
|
||||
|
||||
'@scalar/api-client-react@2.0.51(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(react@19.2.7)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)':
|
||||
'@scalar/api-client-react@2.0.51(axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(react@19.2.7)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)':
|
||||
dependencies:
|
||||
'@scalar/api-client': 3.21.1(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@scalar/api-client': 3.21.1(axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@scalar/blocks': 0.4.0(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@scalar/helpers': 0.15.0
|
||||
'@scalar/workspace-store': 0.67.0(typescript@7.0.2)
|
||||
@@ -17923,7 +17935,7 @@ snapshots:
|
||||
- typescript
|
||||
- universal-cookie
|
||||
|
||||
'@scalar/api-client@3.21.1(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)':
|
||||
'@scalar/api-client@3.21.1(axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)':
|
||||
dependencies:
|
||||
'@headlessui/tailwindcss': 0.2.2(tailwindcss@4.3.2)
|
||||
'@headlessui/vue': 1.7.23(vue@3.5.42(typescript@7.0.2))
|
||||
@@ -17944,7 +17956,7 @@ snapshots:
|
||||
'@scalar/use-toasts': 0.10.5(typescript@7.0.2)
|
||||
'@scalar/workspace-store': 0.67.0(typescript@7.0.2)
|
||||
'@vueuse/core': 13.9.0(vue@3.5.42(typescript@7.0.2))
|
||||
'@vueuse/integrations': 13.9.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(focus-trap@7.8.0)(fuse.js@7.5.0)(nprogress@0.2.0)(vue@3.5.42(typescript@7.0.2))
|
||||
'@vueuse/integrations': 13.9.0(axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(focus-trap@7.8.0)(fuse.js@7.5.0)(nprogress@0.2.0)(vue@3.5.42(typescript@7.0.2))
|
||||
focus-trap: 7.8.0
|
||||
fuse.js: 7.5.0
|
||||
js-base64: 3.9.3
|
||||
@@ -17998,7 +18010,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@headlessui/vue': 1.7.23(vue@3.5.42(typescript@7.0.2))
|
||||
'@scalar/agent-chat': 0.12.37(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)(zod@4.4.3)
|
||||
'@scalar/api-client': 3.21.1(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@scalar/api-client': 3.21.1(axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@scalar/blocks': 0.4.0(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@scalar/code-highlight': 0.4.7(supports-color@10.2.2)
|
||||
'@scalar/components': 0.30.3(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
@@ -18778,10 +18790,10 @@ snapshots:
|
||||
'@tanstack/react-query': 5.101.2(react@19.2.7)
|
||||
react: 19.2.7
|
||||
|
||||
'@tanstack/react-query-next-experimental@5.101.2(@tanstack/react-query@5.101.2(react@19.2.7))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7)':
|
||||
'@tanstack/react-query-next-experimental@5.101.2(@tanstack/react-query@5.101.2(react@19.2.7))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7)':
|
||||
dependencies:
|
||||
'@tanstack/react-query': 5.101.2(react@19.2.7)
|
||||
next: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
next: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
react: 19.2.7
|
||||
|
||||
'@tanstack/react-query-persist-client@5.101.2(@tanstack/react-query@5.101.2(react@19.2.7))(react@19.2.7)':
|
||||
@@ -19074,11 +19086,11 @@ snapshots:
|
||||
'@trpc/server': 11.18.0(typescript@7.0.2)
|
||||
typescript: 7.0.2
|
||||
|
||||
'@trpc/next@11.18.0(@tanstack/react-query@5.101.2(react@19.2.7))(@trpc/client@11.18.0(@trpc/server@11.18.0(typescript@7.0.2))(typescript@7.0.2))(@trpc/react-query@11.18.0(@tanstack/react-query@5.101.2(react@19.2.7))(@trpc/client@11.18.0(@trpc/server@11.18.0(typescript@7.0.2))(typescript@7.0.2))(@trpc/server@11.18.0(typescript@7.0.2))(react@19.2.7)(typescript@7.0.2))(@trpc/server@11.18.0(typescript@7.0.2))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@7.0.2)':
|
||||
'@trpc/next@11.18.0(@tanstack/react-query@5.101.2(react@19.2.7))(@trpc/client@11.18.0(@trpc/server@11.18.0(typescript@7.0.2))(typescript@7.0.2))(@trpc/react-query@11.18.0(@tanstack/react-query@5.101.2(react@19.2.7))(@trpc/client@11.18.0(@trpc/server@11.18.0(typescript@7.0.2))(typescript@7.0.2))(@trpc/server@11.18.0(typescript@7.0.2))(react@19.2.7)(typescript@7.0.2))(@trpc/server@11.18.0(typescript@7.0.2))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@7.0.2)':
|
||||
dependencies:
|
||||
'@trpc/client': 11.18.0(@trpc/server@11.18.0(typescript@7.0.2))(typescript@7.0.2)
|
||||
'@trpc/server': 11.18.0(typescript@7.0.2)
|
||||
next: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
next: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
react: 19.2.7
|
||||
react-dom: 19.2.7(react@19.2.7)
|
||||
typescript: 7.0.2
|
||||
@@ -19747,13 +19759,13 @@ snapshots:
|
||||
'@vueuse/shared': 13.9.0(vue@3.5.42(typescript@7.0.2))
|
||||
vue: 3.5.42(typescript@7.0.2)
|
||||
|
||||
'@vueuse/integrations@13.9.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(focus-trap@7.8.0)(fuse.js@7.5.0)(nprogress@0.2.0)(vue@3.5.42(typescript@7.0.2))':
|
||||
'@vueuse/integrations@13.9.0(axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(focus-trap@7.8.0)(fuse.js@7.5.0)(nprogress@0.2.0)(vue@3.5.42(typescript@7.0.2))':
|
||||
dependencies:
|
||||
'@vueuse/core': 13.9.0(vue@3.5.42(typescript@7.0.2))
|
||||
'@vueuse/shared': 13.9.0(vue@3.5.42(typescript@7.0.2))
|
||||
vue: 3.5.42(typescript@7.0.2)
|
||||
optionalDependencies:
|
||||
axios: 1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)
|
||||
axios: 1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2)
|
||||
focus-trap: 7.8.0
|
||||
fuse.js: 7.5.0
|
||||
nprogress: 0.2.0
|
||||
@@ -19978,7 +19990,7 @@ snapshots:
|
||||
ajv@8.18.0:
|
||||
dependencies:
|
||||
fast-deep-equal: 3.1.3
|
||||
fast-uri: 3.1.6
|
||||
fast-uri: 3.1.8
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
@@ -20108,7 +20120,7 @@ snapshots:
|
||||
'@fastify/error': 4.2.0
|
||||
fastq: 1.20.1
|
||||
|
||||
axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2):
|
||||
axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2):
|
||||
dependencies:
|
||||
follow-redirects: 1.16.0(debug@4.4.3(supports-color@10.2.2))
|
||||
form-data: 4.0.6
|
||||
@@ -20212,16 +20224,16 @@ snapshots:
|
||||
widest-line: 4.0.1
|
||||
wrap-ansi: 8.1.0
|
||||
|
||||
brace-expansion@1.1.18:
|
||||
brace-expansion@1.1.21:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
concat-map: 0.0.1
|
||||
|
||||
brace-expansion@2.1.4:
|
||||
brace-expansion@2.1.7:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
|
||||
brace-expansion@5.0.9:
|
||||
brace-expansion@5.0.12:
|
||||
dependencies:
|
||||
balanced-match: 4.0.2
|
||||
|
||||
@@ -21024,7 +21036,7 @@ snapshots:
|
||||
dockerode@5.0.0(supports-color@10.2.2):
|
||||
dependencies:
|
||||
'@balena/dockerignore': 1.0.2
|
||||
'@grpc/grpc-js': 1.12.7
|
||||
'@grpc/grpc-js': 1.13.6
|
||||
'@grpc/proto-loader': 0.7.13
|
||||
docker-modem: 5.0.7(supports-color@10.2.2)
|
||||
protobufjs: 7.6.6
|
||||
@@ -21035,7 +21047,7 @@ snapshots:
|
||||
dockerode@5.0.1(supports-color@10.2.2):
|
||||
dependencies:
|
||||
'@balena/dockerignore': 1.0.2
|
||||
'@grpc/grpc-js': 1.12.7
|
||||
'@grpc/grpc-js': 1.13.6
|
||||
'@grpc/proto-loader': 0.7.13
|
||||
docker-modem: 5.0.7(supports-color@10.2.2)
|
||||
protobufjs: 7.6.6
|
||||
@@ -21502,7 +21514,7 @@ snapshots:
|
||||
'@fastify/merge-json-schemas': 0.2.1
|
||||
ajv: 8.18.0
|
||||
ajv-formats: 3.0.1(ajv@8.18.0)
|
||||
fast-uri: 3.1.6
|
||||
fast-uri: 3.1.8
|
||||
json-schema-ref-resolver: 3.0.0
|
||||
rfdc: 1.4.1
|
||||
|
||||
@@ -21527,7 +21539,7 @@ snapshots:
|
||||
fast-string-truncated-width: 3.0.3
|
||||
optional: true
|
||||
|
||||
fast-uri@3.1.6: {}
|
||||
fast-uri@3.1.8: {}
|
||||
|
||||
fast-uri@4.2.1: {}
|
||||
|
||||
@@ -21673,7 +21685,7 @@ snapshots:
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
fumadocs-core@16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3):
|
||||
fumadocs-core@16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3):
|
||||
dependencies:
|
||||
'@fumari/image-size': 0.1.1
|
||||
estree-util-value-to-estree: 3.5.0
|
||||
@@ -21703,21 +21715,21 @@ snapshots:
|
||||
'@types/react': 19.2.17
|
||||
algoliasearch: 5.53.0
|
||||
lucide-react: 1.47.0(react@19.2.7)
|
||||
next: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
next: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
react: 19.2.7
|
||||
react-dom: 19.2.7(react@19.2.7)
|
||||
zod: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
fumadocs-mdx@15.4.3(ca407bc45757862c0553f46d1969d5d6):
|
||||
fumadocs-mdx@15.4.3(0bc99cc3ab6490dd33b76f4900ace488):
|
||||
dependencies:
|
||||
'@mdx-js/mdx': 3.1.1(supports-color@10.2.2)
|
||||
'@standard-schema/spec': 1.1.0
|
||||
chokidar: 5.0.0
|
||||
esbuild: 0.28.2
|
||||
estree-util-value-to-estree: 3.5.0
|
||||
fumadocs-core: 16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3)
|
||||
fumadocs-core: 16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3)
|
||||
github-slugger: 2.0.0
|
||||
magic-string: 1.4.1
|
||||
mdast-util-mdx: 3.0.0(supports-color@10.2.2)
|
||||
@@ -21737,14 +21749,14 @@ snapshots:
|
||||
'@types/mdx': 2.0.14
|
||||
'@types/react': 19.2.17
|
||||
mdast-util-directive: 3.1.0(supports-color@10.2.2)
|
||||
next: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
next: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
react: 19.2.7
|
||||
rolldown: 1.1.4
|
||||
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.101.0)(sugarss@5.0.0(postcss@8.5.23))(terser@5.46.1)(tsx@4.20.4)(yaml@2.9.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
fumadocs-openapi@12.0.1(2b987dc9753a22b34010c9167028a29c):
|
||||
fumadocs-openapi@12.0.1(226091f6acf12b47d5f750357219f3e3):
|
||||
dependencies:
|
||||
'@base-ui/react': 1.8.0(@date-fns/tz@1.5.0)(@types/react@19.2.17)(date-fns@4.4.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
'@fuma-translate/react': 1.0.2(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
@@ -21756,8 +21768,8 @@ snapshots:
|
||||
chokidar: 5.0.0
|
||||
class-variance-authority: 0.7.1
|
||||
cn: 0.3.2
|
||||
fumadocs-core: 16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3)
|
||||
fumadocs-ui: 16.15.12(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.2)
|
||||
fumadocs-core: 16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3)
|
||||
fumadocs-ui: 16.15.12(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.2)
|
||||
github-slugger: 2.0.0
|
||||
hast-util-to-jsx-runtime: 2.3.6(supports-color@10.2.2)
|
||||
lucide-react: 1.47.0(react@19.2.7)
|
||||
@@ -21768,7 +21780,7 @@ snapshots:
|
||||
shiki: 4.4.3
|
||||
yaml: 2.9.1
|
||||
optionalDependencies:
|
||||
'@scalar/api-client-react': 2.0.51(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(react@19.2.7)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@scalar/api-client-react': 2.0.51(axios@1.20.0(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(nprogress@0.2.0)(react@19.2.7)(supports-color@10.2.2)(tailwindcss@4.3.2)(typescript@7.0.2)
|
||||
'@types/react': 19.2.17
|
||||
json-schema-typed: 8.0.2
|
||||
transitivePeerDependencies:
|
||||
@@ -21776,7 +21788,7 @@ snapshots:
|
||||
- date-fns
|
||||
- supports-color
|
||||
|
||||
fumadocs-ui@16.15.12(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.2):
|
||||
fumadocs-ui@16.15.12(@types/mdx@2.0.14)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(fumadocs-core@16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(tailwindcss@4.3.2):
|
||||
dependencies:
|
||||
'@fuma-translate/react': 1.0.2(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
'@fumadocs/tailwind': 0.1.2(tailwindcss@4.3.2)
|
||||
@@ -21792,7 +21804,7 @@ snapshots:
|
||||
'@radix-ui/react-tabs': 1.1.21(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
class-variance-authority: 0.7.1
|
||||
cn: 0.3.2
|
||||
fumadocs-core: 16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3)
|
||||
fumadocs-core: 16.15.12(@mdx-js/mdx@3.1.1(supports-color@10.2.2))(@modelcontextprotocol/server@2.0.0)(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.17)(algoliasearch@5.53.0)(lucide-react@1.47.0(react@19.2.7))(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(supports-color@10.2.2)(zod@4.4.3)
|
||||
lucide-react: 1.47.0(react@19.2.7)
|
||||
motion: 13.4.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
next-themes: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
|
||||
@@ -21806,7 +21818,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/mdx': 2.0.14
|
||||
'@types/react': 19.2.17
|
||||
next: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
next: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react-dom'
|
||||
- tailwindcss
|
||||
@@ -22329,7 +22341,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ip-address@10.3.1: {}
|
||||
ip-address@10.7.1: {}
|
||||
|
||||
ipaddr.js@2.3.0: {}
|
||||
|
||||
@@ -23349,19 +23361,19 @@ snapshots:
|
||||
|
||||
minimatch@10.2.5:
|
||||
dependencies:
|
||||
brace-expansion: 5.0.9
|
||||
brace-expansion: 5.0.12
|
||||
|
||||
minimatch@3.1.5:
|
||||
dependencies:
|
||||
brace-expansion: 1.1.18
|
||||
brace-expansion: 1.1.21
|
||||
|
||||
minimatch@5.1.9:
|
||||
dependencies:
|
||||
brace-expansion: 2.1.4
|
||||
brace-expansion: 2.1.7
|
||||
|
||||
minimatch@9.0.9:
|
||||
dependencies:
|
||||
brace-expansion: 2.1.4
|
||||
brace-expansion: 2.1.7
|
||||
|
||||
minimist@1.2.8: {}
|
||||
|
||||
@@ -23461,22 +23473,22 @@ snapshots:
|
||||
|
||||
neverpanic@0.0.8: {}
|
||||
|
||||
next-auth@5.0.0-beta.32(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7):
|
||||
next-auth@5.0.0-beta.32(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7):
|
||||
dependencies:
|
||||
'@auth/core': 0.41.3
|
||||
next: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
next: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
react: 19.2.7
|
||||
|
||||
next-intl-swc-plugin-extractor@4.13.1: {}
|
||||
|
||||
next-intl@4.13.1(@swc/helpers@0.5.23)(next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7)(typescript@7.0.2):
|
||||
next-intl@4.13.1(@swc/helpers@0.5.23)(next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0))(react@19.2.7)(typescript@7.0.2):
|
||||
dependencies:
|
||||
'@formatjs/intl-localematcher': 0.8.1
|
||||
'@parcel/watcher': 2.4.1
|
||||
'@swc/core': 1.15.3(@swc/helpers@0.5.23)
|
||||
icu-minify: 4.13.1
|
||||
negotiator: 1.0.0
|
||||
next: 16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
next: 16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0)
|
||||
next-intl-swc-plugin-extractor: 4.13.1
|
||||
po-parser: 2.1.1
|
||||
react: 19.2.7
|
||||
@@ -23507,9 +23519,9 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
next@16.3.3(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0):
|
||||
next@16.3.6(@babel/core@7.29.0(supports-color@10.2.2))(@opentelemetry/api@1.9.0)(@playwright/test@1.61.1)(@types/node@24.13.3)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.101.0):
|
||||
dependencies:
|
||||
'@next/env': 16.3.3
|
||||
'@next/env': 16.3.6
|
||||
'@swc/helpers': 0.5.23
|
||||
baseline-browser-mapping: 2.11.25
|
||||
caniuse-lite: 1.0.30001810
|
||||
@@ -23518,14 +23530,14 @@ snapshots:
|
||||
react-dom: 19.2.7(react@19.2.7)
|
||||
styled-jsx: 5.1.6(@babel/core@7.29.0(supports-color@10.2.2))(react@19.2.7)
|
||||
optionalDependencies:
|
||||
'@next/swc-darwin-arm64': 16.3.3
|
||||
'@next/swc-darwin-x64': 16.3.3
|
||||
'@next/swc-linux-arm64-gnu': 16.3.3
|
||||
'@next/swc-linux-arm64-musl': 16.3.3
|
||||
'@next/swc-linux-x64-gnu': 16.3.3
|
||||
'@next/swc-linux-x64-musl': 16.3.3
|
||||
'@next/swc-win32-arm64-msvc': 16.3.3
|
||||
'@next/swc-win32-x64-msvc': 16.3.3
|
||||
'@next/swc-darwin-arm64': 16.3.6
|
||||
'@next/swc-darwin-x64': 16.3.6
|
||||
'@next/swc-linux-arm64-gnu': 16.3.6
|
||||
'@next/swc-linux-arm64-musl': 16.3.6
|
||||
'@next/swc-linux-x64-gnu': 16.3.6
|
||||
'@next/swc-linux-x64-musl': 16.3.6
|
||||
'@next/swc-win32-arm64-msvc': 16.3.6
|
||||
'@next/swc-win32-x64-msvc': 16.3.6
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@playwright/test': 1.61.1
|
||||
babel-plugin-react-compiler: 1.0.0
|
||||
@@ -25223,7 +25235,7 @@ snapshots:
|
||||
|
||||
socks@2.8.3:
|
||||
dependencies:
|
||||
ip-address: 10.3.1
|
||||
ip-address: 10.7.1
|
||||
smart-buffer: 4.2.0
|
||||
|
||||
solid-js@1.9.14:
|
||||
|
||||
+8
-8
@@ -177,7 +177,7 @@ catalog:
|
||||
mermaid: ^11.16.1
|
||||
motion: ^13.0.0
|
||||
maria2: ^0.4.1
|
||||
next: 16.3.3
|
||||
next: 16.3.6
|
||||
next-auth: 5.0.0-beta.32
|
||||
next-intl: 4.13.1
|
||||
next-themes: ^0.4.6
|
||||
@@ -261,25 +261,25 @@ overrides:
|
||||
"@assistant-ui/core": 0.3.7
|
||||
"@assistant-ui/store": 0.3.5
|
||||
"@assistant-ui/tap": 0.9.10
|
||||
"@grpc/grpc-js@<1.12.7": "1.12.7"
|
||||
"@grpc/grpc-js@<1.12.7": "1.13.6"
|
||||
"@rvf/set-get@<7.0.2": "7.0.2"
|
||||
"@xmldom/xmldom@<0.8.15": "0.8.15"
|
||||
"assistant-cloud": 0.1.38
|
||||
"@babel/helpers@<7.26.10": ">=7.29.7"
|
||||
"@babel/runtime@<7.26.10": ">=7.29.7"
|
||||
"axios@>=1.0.0 <1.18.1": "1.18.1"
|
||||
"brace-expansion@>=1.0.0 <1.1.18": "1.1.18"
|
||||
"brace-expansion@>=2.0.0 <2.1.4": "2.1.4"
|
||||
"brace-expansion@>=4.0.0 <5.0.9": "5.0.9"
|
||||
"axios@>=1.0.0 <1.18.1": "1.20.0"
|
||||
"brace-expansion@>=1.0.0 <1.1.18": "1.1.21"
|
||||
"brace-expansion@>=2.0.0 <2.1.4": "2.1.7"
|
||||
"brace-expansion@>=4.0.0 <5.0.9": "5.0.12"
|
||||
"browserslist@<4.28.7": "4.28.7"
|
||||
"esbuild@<=0.24.2": ">=0.28.1"
|
||||
"fast-uri@>=3.0.0 <3.1.6": "3.1.6"
|
||||
"fast-uri@>=3.0.0 <3.1.6": "3.1.8"
|
||||
"@ai-sdk/provider-utils>undici": "8.10.2"
|
||||
"find-my-way@<9.6.1": "9.9.0"
|
||||
"form-data@>=4.0.0 <4.0.4": ">=4.0.6"
|
||||
"hono@<4.6.5": ">=4.13.7"
|
||||
"immutable@>=5.0.0-beta.1 <5.1.8": "5.1.8"
|
||||
"ip-address@<10.3.1": "10.3.1"
|
||||
"ip-address@<10.3.1": "10.7.1"
|
||||
"image-size@<=2.0.2": "2.0.3"
|
||||
"js-yaml@>=3.0.0 <3.15.2": "3.15.2"
|
||||
"js-yaml@>=4.0.0 <4.3.2": "4.3.2"
|
||||
|
||||
@@ -1 +1 @@
|
||||
[{"id":"zaxon793-jy8gpl0v-d89pbrkd-emwl5v04","name":"Mike","slug":"guest-5da646e8","imageUrl":"https://images.opencollective.com/guest-5da646e8/avatar.png"},{"id":"ywz9j4av-god8pgwg-db36mr35-nxklb0e7","name":"Guest","slug":"guest-554ec68f","imageUrl":"https://images.opencollective.com/guest-554ec68f/avatar.png"},{"id":"mlo94zn7-x08dpo09-xlxpewga-3vjbrky5","name":"Robert Hermann","slug":"robert-hermann","imageUrl":"https://images.opencollective.com/robert-hermann/avatar.png"},{"id":"zaxon793-jy8gpl0n-8r3pbrkd-emwl5v04","name":"Sebastian Raubuch","slug":"sebastian-raubuch","imageUrl":"https://images.opencollective.com/sebastian-raubuch/avatar.png"},{"id":"nmlo94zn-7x08dpod-ykm6ewga-3vjbrky5","name":"Didier LUTHI","slug":"didier-luthi","imageUrl":"https://images.opencollective.com/didier-luthi/avatar.png"},{"id":"vedj9wro-z3a56dgk-z3z67blg-8x4m0ykn","name":"Guest","slug":"guest-b1459a68","imageUrl":"https://images.opencollective.com/guest-b1459a68/avatar.png"},{"id":"gm9bnk80-437xqry4-8vepvzeo-ljdayw5r","name":"Mohister","slug":"mohister","imageUrl":"https://images.opencollective.com/mohister/1d8a592/avatar.png"},{"id":"vrgbk357-l4x96e70-m0apomew-a0jdyzn8","name":"sidens","slug":"sidens","imageUrl":"https://images.opencollective.com/sidens/avatar.png"},{"id":"mlo94zn7-x08dpo0d-l7epewga-3vjbrky5","name":"Chris","slug":"guest-937065bb","imageUrl":"https://images.opencollective.com/guest-937065bb/avatar.png"},{"id":"k9mbw7y4-8r3zq3ol-7zlq0ej5-lavnodgx","name":"Nick W","slug":"guest-7501840a","imageUrl":"https://images.opencollective.com/guest-7501840a/avatar.png"},{"id":"e0adkjrv-8xwm69o8-5al67z5n-4l93bgoy","name":"Jeppedy","slug":"guest-ab78e46a","imageUrl":"https://images.opencollective.com/guest-ab78e46a/avatar.png"},{"id":"a47byg9n-xozdp8xo-jgm6mjlv-03rek5w8","name":"naru","slug":"guest-02fd264a","imageUrl":"https://images.opencollective.com/guest-02fd264a/avatar.png"},{"id":"3k0exgzn-m8yj64g3-ll865wao-9r7b4dlv","name":"Jared Hocutt","slug":"jared-hocutt","imageUrl":"https://images.opencollective.com/jared-hocutt/avatar.png"},{"id":"eng0kzdy-vor4pz8l-exopbma8-37xlw95j","name":"Incognito","slug":"user-e63f4a5b","imageUrl":"https://images.opencollective.com/user-e63f4a5b/avatar.png"},{"id":"8k03reyd-5agmq50v-8vyplbwo-z7j4nxv9","name":"dazi","slug":"guest-11663a46","imageUrl":"https://images.opencollective.com/guest-11663a46/avatar.png"},{"id":"vedj9wro-z3a56dgd-zwl67blg-8x4m0ykn","name":"ZoopaMario","slug":"zoopamario","imageUrl":"https://images.opencollective.com/zoopamario/bea900d/avatar.png"},{"id":"a47byg9n-xozdp8xe-xmb6mjlv-03rek5w8","name":"FlamebergUA","slug":"guest-a59fd500","imageUrl":"https://images.opencollective.com/guest-a59fd500/avatar.png"},{"id":"8k03reyd-5agmq50y-kzoplbwo-z7j4nxv9","name":"StLCards","slug":"guest-e1de33dc","imageUrl":"https://images.opencollective.com/guest-e1de33dc/avatar.png"},{"id":"ejoxl3az-45w9pa54-dk46y870-mgkbrvdn","name":"Varadha","slug":"guest-6f68eed9","imageUrl":"https://images.opencollective.com/guest-6f68eed9/avatar.png"},{"id":"jrkx5lmn-v904qjl0-3oz68bwa-7zdygoe3","name":"Guest","slug":"guest-6da419b5","imageUrl":"https://images.opencollective.com/guest-6da419b5/avatar.png"},{"id":"mywxoz34-09rl6k3z-lmlqvenb-dj7gk85a","name":"Guest","slug":"guest-122d7e0a","imageUrl":"https://images.opencollective.com/guest-122d7e0a/avatar.png"},{"id":"3z8arxve-ymko60vm-3eaqgl5n-bj9w704d","name":"Ricardo","slug":"ricardo34","imageUrl":"https://images.opencollective.com/ricardo34/avatar.png"},{"id":"e0adkjrv-8xwm69or-reo67z5n-4l93bgoy","name":"GoM","slug":"guest-3944feab","imageUrl":"https://images.opencollective.com/guest-3944feab/avatar.png"},{"id":"gm9bnk80-437xqrnr-r5o6vzeo-ljdayw5r","name":"Brent Maxwell","slug":"brentmaxwell","imageUrl":"https://images.opencollective.com/brentmaxwell/avatar.png"},{"id":"gnxdwzj3-le5mpwzd-ll0py8rv-bko04a97","name":"Eric G","slug":"guest-478270f1","imageUrl":"https://images.opencollective.com/guest-478270f1/avatar.png"},{"id":"v349mrwg-z75lpyjo-ez8pa08d-jeybknox","name":"ninja","slug":"guest-0bb6ba36","imageUrl":"https://images.opencollective.com/guest-0bb6ba36/avatar.png"},{"id":"ywz9j4av-god8pgw0-53y6mr35-nxklb0e7","name":"s11","slug":"guest-6e9a2588","imageUrl":"https://images.opencollective.com/guest-6e9a2588/avatar.png"},{"id":"mlo94zn7-x08dpo30-v73pewga-3vjbrky5","name":"Incognito","slug":"user-cbad11af","imageUrl":"https://images.opencollective.com/user-cbad11af/avatar.png"},{"id":"v349mrwg-z75lpy7l-yzgpa08d-jeybknox","name":"Ronald Simmons","slug":"ronald-simmons","imageUrl":"https://images.opencollective.com/ronald-simmons/avatar.png"},{"id":"7ywz9j4a-vgod8pgl-me9qmr35-nxklb0e7","name":"Joel","slug":"guest-3376ab02","imageUrl":"https://images.opencollective.com/guest-3376ab02/avatar.png"},{"id":"v349mrwg-z75lpyjw-kxzpa08d-jeybknox","name":"Gokay Yamankurt","slug":"gokay-yamankurt","imageUrl":"https://images.opencollective.com/gokay-yamankurt/avatar.png"},{"id":"v349mrwg-z75lpyjv-59rpa08d-jeybknox","name":"Rhiz3K","slug":"rhiz3k","imagLine truncated
|
||||
[{"id":"zaxon793-jy8gpl0v-d89pbrkd-emwl5v04","name":"Mike","slug":"guest-5da646e8","imageUrl":"https://images.opencollective.com/guest-5da646e8/avatar.png"},{"id":"ywz9j4av-god8pgwg-db36mr35-nxklb0e7","name":"Guest","slug":"guest-554ec68f","imageUrl":"https://images.opencollective.com/guest-554ec68f/avatar.png"},{"id":"mlo94zn7-x08dpo09-xlxpewga-3vjbrky5","name":"Robert Hermann","slug":"robert-hermann","imageUrl":"https://images.opencollective.com/robert-hermann/avatar.png"},{"id":"zaxon793-jy8gpl0n-8r3pbrkd-emwl5v04","name":"Sebastian Raubuch","slug":"sebastian-raubuch","imageUrl":"https://images.opencollective.com/sebastian-raubuch/avatar.png"},{"id":"nmlo94zn-7x08dpod-ykm6ewga-3vjbrky5","name":"Didier LUTHI","slug":"didier-luthi","imageUrl":"https://images.opencollective.com/didier-luthi/avatar.png"},{"id":"vedj9wro-z3a56dgk-z3z67blg-8x4m0ykn","name":"Guest","slug":"guest-b1459a68","imageUrl":"https://images.opencollective.com/guest-b1459a68/avatar.png"},{"id":"gm9bnk80-437xqry4-8vepvzeo-ljdayw5r","name":"Mohister","slug":"mohister","imageUrl":"https://images.opencollective.com/mohister/1d8a592/avatar.png"},{"id":"vrgbk357-l4x96e70-m0apomew-a0jdyzn8","name":"sidens","slug":"sidens","imageUrl":"https://images.opencollective.com/sidens/avatar.png"},{"id":"mlo94zn7-x08dpo0d-l7epewga-3vjbrky5","name":"Chris","slug":"guest-937065bb","imageUrl":"https://images.opencollective.com/guest-937065bb/avatar.png"},{"id":"k9mbw7y4-8r3zq3ol-7zlq0ej5-lavnodgx","name":"Nick W","slug":"guest-7501840a","imageUrl":"https://images.opencollective.com/guest-7501840a/avatar.png"},{"id":"e0adkjrv-8xwm69o8-5al67z5n-4l93bgoy","name":"Jeppedy","slug":"guest-ab78e46a","imageUrl":"https://images.opencollective.com/guest-ab78e46a/avatar.png"},{"id":"a47byg9n-xozdp8xo-jgm6mjlv-03rek5w8","name":"naru","slug":"guest-02fd264a","imageUrl":"https://images.opencollective.com/guest-02fd264a/avatar.png"},{"id":"3k0exgzn-m8yj64g3-ll865wao-9r7b4dlv","name":"Jared Hocutt","slug":"jared-hocutt","imageUrl":"https://images.opencollective.com/jared-hocutt/avatar.png"},{"id":"eng0kzdy-vor4pz8l-exopbma8-37xlw95j","name":"Incognito","slug":"user-e63f4a5b","imageUrl":"https://images.opencollective.com/user-e63f4a5b/avatar.png"},{"id":"8k03reyd-5agmq50v-8vyplbwo-z7j4nxv9","name":"dazi","slug":"guest-11663a46","imageUrl":"https://images.opencollective.com/guest-11663a46/avatar.png"},{"id":"vedj9wro-z3a56dgd-zwl67blg-8x4m0ykn","name":"ZoopaMario","slug":"zoopamario","imageUrl":"https://images.opencollective.com/zoopamario/bea900d/avatar.png"},{"id":"a47byg9n-xozdp8xe-xmb6mjlv-03rek5w8","name":"FlamebergUA","slug":"guest-a59fd500","imageUrl":"https://images.opencollective.com/guest-a59fd500/avatar.png"},{"id":"8k03reyd-5agmq50y-kzoplbwo-z7j4nxv9","name":"StLCards","slug":"guest-e1de33dc","imageUrl":"https://images.opencollective.com/guest-e1de33dc/avatar.png"},{"id":"ejoxl3az-45w9pa54-dk46y870-mgkbrvdn","name":"Varadha","slug":"guest-6f68eed9","imageUrl":"https://images.opencollective.com/guest-6f68eed9/avatar.png"},{"id":"jrkx5lmn-v904qjl0-3oz68bwa-7zdygoe3","name":"Guest","slug":"guest-6da419b5","imageUrl":"https://images.opencollective.com/guest-6da419b5/avatar.png"},{"id":"mywxoz34-09rl6k3z-lmlqvenb-dj7gk85a","name":"Guest","slug":"guest-122d7e0a","imageUrl":"https://images.opencollective.com/guest-122d7e0a/avatar.png"},{"id":"3z8arxve-ymko60vm-3eaqgl5n-bj9w704d","name":"Ricardo","slug":"ricardo34","imageUrl":"https://images.opencollective.com/ricardo34/avatar.png"},{"id":"e0adkjrv-8xwm69or-reo67z5n-4l93bgoy","name":"GoM","slug":"guest-3944feab","imageUrl":"https://images.opencollective.com/guest-3944feab/avatar.png"},{"id":"gm9bnk80-437xqrnr-r5o6vzeo-ljdayw5r","name":"Brent Maxwell","slug":"brentmaxwell","imageUrl":"https://images.opencollective.com/brentmaxwell/avatar.png"},{"id":"gnxdwzj3-le5mpwzd-ll0py8rv-bko04a97","name":"Eric G","slug":"guest-478270f1","imageUrl":"https://images.opencollective.com/guest-478270f1/avatar.png"},{"id":"v349mrwg-z75lpyjo-ez8pa08d-jeybknox","name":"ninja","slug":"guest-0bb6ba36","imageUrl":"https://images.opencollective.com/guest-0bb6ba36/avatar.png"},{"id":"ywz9j4av-god8pgw0-53y6mr35-nxklb0e7","name":"s11","slug":"guest-6e9a2588","imageUrl":"https://images.opencollective.com/guest-6e9a2588/avatar.png"},{"id":"mlo94zn7-x08dpo30-v73pewga-3vjbrky5","name":"Incognito","slug":"user-cbad11af","imageUrl":"https://images.opencollective.com/user-cbad11af/avatar.png"},{"id":"v349mrwg-z75lpy7l-yzgpa08d-jeybknox","name":"Ronald Simmons","slug":"ronald-simmons","imageUrl":"https://images.opencollective.com/ronald-simmons/avatar.png"},{"id":"7ywz9j4a-vgod8pgl-me9qmr35-nxklb0e7","name":"Joel","slug":"guest-3376ab02","imageUrl":"https://images.opencollective.com/guest-3376ab02/avatar.png"},{"id":"v349mrwg-z75lpyjw-kxzpa08d-jeybknox","name":"Gokay Yamankurt","slug":"gokay-yamankurt","imageUrl":"https://images.opencollective.com/gokay-yamankurt/avatar.png"},{"id":"v349mrwg-z75lpyjv-59rpa08d-jeybknox","name":"Rhiz3K","slug":"rhiz3k","imagLine truncated
|
||||
@@ -1 +1 @@
|
||||
[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/aaa1f083e04c572f953fde4563171e4a.jpeg"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14861042/medium/2d5c4e62613f03082f3e645fa92efd59.jpeg"},{"username":"djismgaming","avatarUrl":"https://crowLine truncated
|
||||
[{"username":"carlchina","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/6697/medium/d22bbe7797bbeb30dbdc73a5648d329a_default.png"},{"username":"zielmann","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/7795/medium/ad22b8b8d5eb33e4154d53a454c862fd_default.png"},{"username":"Thalyn","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12494028/medium/5faf5acc945a874ed116c4567edaa09f_default.png"},{"username":"magnushj","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12503488/medium/68f32e33ea7b891960055c9f831f7344_default.png"},{"username":"almontegil","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12580457/medium/f4136cacbdfdb4c28ae7f85dc5f840db_default.png"},{"username":"andibing","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12664938/medium/b8be63e4dcb2e791ced1ffc9e3a049a5.jpg"},{"username":"gronare","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12680911/medium/c6eb14d1504156f34a158e507f73aa3d_default.png"},{"username":"SmartPhoneLover","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12701640/medium/ec95486662ec875cda080e778c3ff702.jpg"},{"username":"marcelotk_15","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/12773227/medium/ecf6bea16ab0c873c131df2154ee9718_default.png"},{"username":"S3OD177","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13051544/medium/11dd990837f61f7e8f7d36b041a7fddb_default.png"},{"username":"marcg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13153301/medium/ebcf60fbf27deb148ec40a659566c6ff_default.png"},{"username":"BeersTeddy","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13185230/medium/2f1f4e1effe74a23422b195cbefb2a95_default.png"},{"username":"jamesmcmahon0","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13245578/medium/586aa873b4abddbd9abc6f3de99ab70e.jpeg"},{"username":"b1nar10","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13286752/medium/aaa1f083e04c572f953fde4563171e4a.jpeg"},{"username":"vannCN","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13330448/medium/9b8a9ee3611e51e951e22d5fd4eb7d8d.jpg"},{"username":"binswm","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13343482/medium/45dde0e6097b9b72705d2eba9dbbc276_default.png"},{"username":"SkewRam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13496556/medium/188f5c2deb7938eda51eb786cc4539ca.jpeg"},{"username":"raphcatarino","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13547726/medium/b003511e67df13a4b4b5689488fa8099.jpg"},{"username":"shillos5","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13600557/medium/b17650b2b84f829d54fa548c44c1ab27.jpeg"},{"username":"wolong98","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/13641407/medium/f4634edc58c7857a357e5293543c15cf.jpg"},{"username":"spair0039","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14012333/medium/72430e96027c09c19141cac38eae4617.png"},{"username":"MajorMarcin","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14072183/medium/0a197e64bb92b6ea94cecbb607512c45.jpg"},{"username":"KosmoMoustache","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14103535/medium/51c438cdd9f86e8811c75f4869de479c.png"},{"username":"Soochaehwa","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14118689/medium/496a1ce63111547bf455a1e0a7ac75f1_default.png"},{"username":"garryfield","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14187035/medium/520c964706676045f6eeec1b08edc880.jpg"},{"username":"droidenko","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14444264/medium/4aa3a8e824f72bc9e5ec0d8de307095e.jpeg"},{"username":"maathis","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14489950/medium/6eeddf13a757ee35f78f1763f94d95e1.jpeg"},{"username":"bukvam","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14546502/medium/a819dc40a13c57395a17dc46e847d3ce.jpg"},{"username":"gm.cinalli","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14670666/medium/2d466a0fdbda40764526be86c97c0ab4.jpeg"},{"username":"antoine2tt","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14722148/medium/f88d926900862dd59007ea4b3419cb9d.png"},{"username":"cretzen","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14799754/medium/3ba5c1805ec20c000d5810c07985afe9.jpg"},{"username":"ktKongTong","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14817246/medium/97cfc3c028dbdaf85ebd1102da71e58c.jpeg"},{"username":"marinkaberg","avatarUrl":"https://crowdin-static.cf-downloads.crowdin.com/avatar/14861042/medium/2d5c4e62613f03082f3e645fa92efd59.jpeg"},{"username":"djismgaming","avatarUrl":"https://crowLine truncated
|
||||
Generated
+5
-5
@@ -8,7 +8,7 @@
|
||||
"name": "homarr-mysql-to-sqlite",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"mysql2": "3.22.6"
|
||||
"mysql2": "3.23.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24.18.0 <25"
|
||||
@@ -95,9 +95,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/mysql2": {
|
||||
"version": "3.22.6",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.22.6.tgz",
|
||||
"integrity": "sha512-fPKmeDGUzvFP7bMD5SASlJ5zIgvCC4hbanTmhbUlEmhyrY1hR4Hi3xLOWgTd3luYjLVifx6uGvMNJ2m/LlqEpg==",
|
||||
"version": "3.23.1",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.23.1.tgz",
|
||||
"integrity": "sha512-tTuRnC7qCet2IOfSNMYZ5SwXuBnfvBPAcIA28P0gtruXyZlU1LMxA6uha32kYypoFgyYklMqhLWwt4laYwXR/Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"aws-ssl-profiles": "^1.1.2",
|
||||
@@ -107,7 +107,7 @@
|
||||
"long": "^5.3.2",
|
||||
"lru.min": "^1.1.4",
|
||||
"named-placeholders": "^1.1.6",
|
||||
"sql-escaper": "^1.4.0"
|
||||
"sql-escaper": "^1.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"test": "node --test test/*.test.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"mysql2": "3.22.6"
|
||||
"mysql2": "3.23.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24.18.0 <25"
|
||||
|
||||
Reference in new issue
Block a user