Compare commits
52
Commits
No files matched your search
@@ -33,6 +33,7 @@ body:
|
||||
options:
|
||||
# The below comment is used to insert a new version with on-release.yml
|
||||
#NEXT_VERSION#
|
||||
- 1.43.1
|
||||
- 1.43.0
|
||||
- 1.42.1
|
||||
- 1.42.0
|
||||
|
||||
@@ -61,10 +61,10 @@ jobs:
|
||||
persist-credentials: false
|
||||
- uses: pnpm/action-setup@v4
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
- uses: actions/setup-node@v5
|
||||
- uses: actions/setup-node@v6
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
with:
|
||||
node-version: 24.10.0
|
||||
node-version: 24.11.0
|
||||
cache: "pnpm"
|
||||
- run: npm i -g pnpm
|
||||
if: env.SKIP_RELEASE == 'false'
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v5
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: "pnpm"
|
||||
|
||||
@@ -1,3 +1,51 @@
|
||||
## [1.43.2](https://github.com/homarr-labs/homarr/compare/v1.43.1...v1.43.2) (2025-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @ctrl/deluge to ^7.5.0 ([#4407](https://github.com/homarr-labs/homarr/issues/4407)) ([57d1a91](https://github.com/homarr-labs/homarr/commit/57d1a9186653dd4c327b360a3bdaa78fd8e1187b))
|
||||
* **deps:** update dependency @gitbeaker/rest to ^43.7.0 ([#4386](https://github.com/homarr-labs/homarr/issues/4386)) ([dcf960f](https://github.com/homarr-labs/homarr/commit/dcf960fa97011c24475f303d587152b42eabec45))
|
||||
* **deps:** update dependency @gitbeaker/rest to ^43.8.0 ([#4425](https://github.com/homarr-labs/homarr/issues/4425)) ([4516f27](https://github.com/homarr-labs/homarr/commit/4516f27fa69445e5aec16760e1f6ed8080bc8fff))
|
||||
* **deps:** update dependency @jellyfin/sdk to ^0.13.0 ([#4399](https://github.com/homarr-labs/homarr/issues/4399)) ([4c4e901](https://github.com/homarr-labs/homarr/commit/4c4e901e7d38faf83a4725b7cee5526b0b223dc3))
|
||||
* **deps:** update dependency @octokit/auth-app to ^8.1.2 ([#4412](https://github.com/homarr-labs/homarr/issues/4412)) ([018a33b](https://github.com/homarr-labs/homarr/commit/018a33b07812fa59f1469ce8b4baa7b1d55c6103))
|
||||
* **deps:** update dependency @tanstack/react-query to ^5.90.6 ([#4427](https://github.com/homarr-labs/homarr/issues/4427)) ([5f9054b](https://github.com/homarr-labs/homarr/commit/5f9054b162daf974baafdbee9f844b3a860cbdd0))
|
||||
* **deps:** update dependency dayjs to ^1.11.19 ([#4414](https://github.com/homarr-labs/homarr/issues/4414)) ([97cbade](https://github.com/homarr-labs/homarr/commit/97cbade7135cb06e0780dd1b448c78c8802794ea))
|
||||
* **deps:** update dependency drizzle-kit to ^0.31.6 ([#4397](https://github.com/homarr-labs/homarr/issues/4397)) ([59434ee](https://github.com/homarr-labs/homarr/commit/59434ee33ae16e06f96e2f07cf85337c313c73f6))
|
||||
* **deps:** update dependency jotai to ^2.15.1 ([#4426](https://github.com/homarr-labs/homarr/issues/4426)) ([07af9d8](https://github.com/homarr-labs/homarr/commit/07af9d8673ea9573bc436626cd84d11980d023bc))
|
||||
* **deps:** update dependency octokit to ^5.0.5 ([#4413](https://github.com/homarr-labs/homarr/issues/4413)) ([186e3e4](https://github.com/homarr-labs/homarr/commit/186e3e445dfe6b4fb1aeda3830ae1f260b605882))
|
||||
* **deps:** update dependency sass to ^1.93.3 ([#4421](https://github.com/homarr-labs/homarr/issues/4421)) ([f216427](https://github.com/homarr-labs/homarr/commit/f2164272a5ca9b51bdf9110664ed5a40b87b60f6))
|
||||
* **deps:** update dependency superjson to v2.2.5 ([#4398](https://github.com/homarr-labs/homarr/issues/4398)) ([19e25a0](https://github.com/homarr-labs/homarr/commit/19e25a095fb0252e8f2a459e0a97d44a37d2188a))
|
||||
* **deps:** update dependency swagger-ui-react to ^5.30.0 ([#4391](https://github.com/homarr-labs/homarr/issues/4391)) ([b51e285](https://github.com/homarr-labs/homarr/commit/b51e285c92ef707076f321f0ce1a15d5cbd5de7d))
|
||||
* **deps:** update dependency swagger-ui-react to ^5.30.1 ([#4418](https://github.com/homarr-labs/homarr/issues/4418)) ([d9b14db](https://github.com/homarr-labs/homarr/commit/d9b14db6a1af515796951a1db670431d10f81eaf))
|
||||
* **deps:** update dependency tsdav to ^2.1.6 ([#4417](https://github.com/homarr-labs/homarr/issues/4417)) ([160c20a](https://github.com/homarr-labs/homarr/commit/160c20a9a6f850f75537e246f42e93f9a2ed79a0))
|
||||
* **deps:** update mantine monorepo to ^8.3.6 ([#4403](https://github.com/homarr-labs/homarr/issues/4403)) ([9e2bbb8](https://github.com/homarr-labs/homarr/commit/9e2bbb8a3f2db95c5598e5dca993bf6cd4ebc127))
|
||||
* **deps:** update nextjs monorepo to v16 (major) ([#4363](https://github.com/homarr-labs/homarr/issues/4363)) ([6ce23a6](https://github.com/homarr-labs/homarr/commit/6ce23a6e971b064292970fc4df90cc69cdb59a95))
|
||||
* **deps:** update tiptap monorepo to v2.27.0 ([#4404](https://github.com/homarr-labs/homarr/issues/4404)) ([f4caddf](https://github.com/homarr-labs/homarr/commit/f4caddfd564553014854347eb6dd14cc800eb027))
|
||||
* **deps:** update tiptap monorepo to v2.27.1 ([#4405](https://github.com/homarr-labs/homarr/issues/4405)) ([d34f9e5](https://github.com/homarr-labs/homarr/commit/d34f9e5b3ef999559a4c5f1165488fe4f8828a83))
|
||||
* **deps:** update trpc monorepo to ^11.7.1 ([#4396](https://github.com/homarr-labs/homarr/issues/4396)) ([5aca5d1](https://github.com/homarr-labs/homarr/commit/5aca5d1b0a76ab4cf0ed27dac62183c766ad398a))
|
||||
* **deps:** update turbo monorepo to ^2.6.0 ([#4422](https://github.com/homarr-labs/homarr/issues/4422)) ([742ef66](https://github.com/homarr-labs/homarr/commit/742ef66e3cf56fd351c8dee2d3dd05cd639e51fa))
|
||||
|
||||
## [1.43.1](https://github.com/homarr-labs/homarr/compare/v1.43.0...v1.43.1) (2025-10-31)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency @auth/core to ^0.41.1 ([#4387](https://github.com/homarr-labs/homarr/issues/4387)) ([747bb92](https://github.com/homarr-labs/homarr/commit/747bb922ccf0508bd00c37faeab29e28876577b1))
|
||||
* **deps:** update dependency @auth/drizzle-adapter to ^1.11.1 ([#4388](https://github.com/homarr-labs/homarr/issues/4388)) ([2fe3656](https://github.com/homarr-labs/homarr/commit/2fe36564c26c33ae2625eb22ef4503c6dd8ebcf9))
|
||||
* **deps:** update dependency @jellyfin/sdk to ^0.12.0 ([#4359](https://github.com/homarr-labs/homarr/issues/4359)) ([554d19a](https://github.com/homarr-labs/homarr/commit/554d19a458e856141f90e795ecebabf205e945e9))
|
||||
* **deps:** update dependency @paralleldrive/cuid2 to v3 ([#4343](https://github.com/homarr-labs/homarr/issues/4343)) ([074d27e](https://github.com/homarr-labs/homarr/commit/074d27e37e3430e7e98cf28e2876afe009811027))
|
||||
* **deps:** update dependency drizzle-orm to ^0.44.7 ([#4372](https://github.com/homarr-labs/homarr/issues/4372)) ([8242bfc](https://github.com/homarr-labs/homarr/commit/8242bfc95cd0e67397b1bfc46d65aec3f568c304))
|
||||
* **deps:** update dependency ioredis to v5.8.2 ([#4357](https://github.com/homarr-labs/homarr/issues/4357)) ([3c4c26e](https://github.com/homarr-labs/homarr/commit/3c4c26ee214a37193aea31b69ce75322862fc534))
|
||||
* **deps:** update dependency mysql2 to v3.15.3 ([#4360](https://github.com/homarr-labs/homarr/issues/4360)) ([6e6c912](https://github.com/homarr-labs/homarr/commit/6e6c9122257934402848d6fdc51e6ccdfc2dcca0))
|
||||
* **deps:** update dependency next-auth to v5.0.0-beta.30 [security] ([#4379](https://github.com/homarr-labs/homarr/issues/4379)) ([c544d6e](https://github.com/homarr-labs/homarr/commit/c544d6ec2e426bc4c82db547d7c4a5dc4d6e2a27))
|
||||
* **deps:** update dependency next-intl to v4.4.0 ([#4365](https://github.com/homarr-labs/homarr/issues/4365)) ([ce63388](https://github.com/homarr-labs/homarr/commit/ce6338840c20fc46048687ed5566ff12fa4b2502))
|
||||
* **deps:** update dependency node-ical to ^0.22.1 ([#4346](https://github.com/homarr-labs/homarr/issues/4346)) ([ef18c58](https://github.com/homarr-labs/homarr/commit/ef18c58f5b09bafd697365a0182ab7bc8719db2b))
|
||||
* **deps:** update dependency superjson to v2.2.3 ([#4364](https://github.com/homarr-labs/homarr/issues/4364)) ([18cc04e](https://github.com/homarr-labs/homarr/commit/18cc04e7f2740226f7bc7c75c0b0cdaec7cda6f8))
|
||||
* **deps:** update dependency typescript-eslint to ^8.46.2 ([#4352](https://github.com/homarr-labs/homarr/issues/4352)) ([f96cbe0](https://github.com/homarr-labs/homarr/commit/f96cbe06525515112fad466cb8a5f33d74517aba))
|
||||
* **deps:** update testcontainers-node monorepo to ^11.7.2 ([#4366](https://github.com/homarr-labs/homarr/issues/4366)) ([7329135](https://github.com/homarr-labs/homarr/commit/7329135fd85906602a16a9bdc16a1ef1cc4607c5))
|
||||
* **deps:** update tiptap monorepo to v2.26.4 ([#4371](https://github.com/homarr-labs/homarr/issues/4371)) ([136ed8e](https://github.com/homarr-labs/homarr/commit/136ed8e8b401b801ba16f2079bdfb5aeebe9f57f))
|
||||
* **deps:** update trpc monorepo to ^11.7.0 ([#4385](https://github.com/homarr-labs/homarr/issues/4385)) ([24def99](https://github.com/homarr-labs/homarr/commit/24def9982709cd6de1765a7235169fe1ceb3cbea))
|
||||
* **media-releases:** support more media type mappings for jellyfin and emby ([#4382](https://github.com/homarr-labs/homarr/issues/4382)) ([6f3b7ec](https://github.com/homarr-labs/homarr/commit/6f3b7ec4915754e183300a915baebc11f264df62))
|
||||
* **releases-widget:** Search multiple docker hub pages for releases ([#4158](https://github.com/homarr-labs/homarr/issues/4158)) ([e0526c5](https://github.com/homarr-labs/homarr/commit/e0526c5ea7ee83adcd22d78fbf63334cb59bcf79))
|
||||
|
||||
## [1.43.0](https://github.com/homarr-labs/homarr/compare/v1.42.1...v1.43.0) (2025-10-24)
|
||||
|
||||
### Features
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
FROM node:24.10.0-alpine AS base
|
||||
FROM node:24.11.0-alpine AS base
|
||||
|
||||
FROM base AS builder
|
||||
RUN apk add --no-cache libc6-compat
|
||||
|
||||
@@ -20,8 +20,8 @@ const withNextIntl = createNextIntlPlugin({
|
||||
const nextConfig: NextConfig = {
|
||||
output: "standalone",
|
||||
reactStrictMode: true,
|
||||
/** We already do linting and typechecking as separate tasks in CI */
|
||||
eslint: { ignoreDuringBuilds: true },
|
||||
reactCompiler: true,
|
||||
/** We already do typechecking as separate tasks in CI */
|
||||
typescript: { ignoreBuildErrors: true },
|
||||
/**
|
||||
* dockerode is required in the external server packages because of https://github.com/homarr-labs/homarr/issues/612
|
||||
@@ -29,10 +29,16 @@ const nextConfig: NextConfig = {
|
||||
serverExternalPackages: ["dockerode"],
|
||||
experimental: {
|
||||
optimizePackageImports: ["@mantine/core", "@mantine/hooks", "@tabler/icons-react"],
|
||||
turbopackFileSystemCacheForDev: true,
|
||||
},
|
||||
transpilePackages: ["@homarr/ui", "@homarr/notifications", "@homarr/modals", "@homarr/spotlight", "@homarr/widgets"],
|
||||
images: {
|
||||
domains: ["cdn.jsdelivr.net"],
|
||||
localPatterns: [
|
||||
{
|
||||
pathname: "/**",
|
||||
search: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
// eslint-disable-next-line @typescript-eslint/require-await,no-restricted-syntax
|
||||
async headers() {
|
||||
|
||||
+21
-20
@@ -50,42 +50,43 @@
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@homarr/widgets": "workspace:^0.1.0",
|
||||
"@mantine/colors-generator": "^8.3.5",
|
||||
"@mantine/core": "^8.3.5",
|
||||
"@mantine/dropzone": "^8.3.5",
|
||||
"@mantine/hooks": "^8.3.5",
|
||||
"@mantine/modals": "^8.3.5",
|
||||
"@mantine/tiptap": "^8.3.5",
|
||||
"@mantine/colors-generator": "^8.3.6",
|
||||
"@mantine/core": "^8.3.6",
|
||||
"@mantine/dropzone": "^8.3.6",
|
||||
"@mantine/hooks": "^8.3.6",
|
||||
"@mantine/modals": "^8.3.6",
|
||||
"@mantine/tiptap": "^8.3.6",
|
||||
"@million/lint": "1.0.14",
|
||||
"@tabler/icons-react": "^3.35.0",
|
||||
"@tanstack/react-query": "^5.90.5",
|
||||
"@tanstack/react-query": "^5.90.6",
|
||||
"@tanstack/react-query-devtools": "^5.90.2",
|
||||
"@tanstack/react-query-next-experimental": "^5.90.2",
|
||||
"@trpc/client": "^11.6.0",
|
||||
"@trpc/next": "^11.6.0",
|
||||
"@trpc/react-query": "^11.6.0",
|
||||
"@trpc/server": "^11.6.0",
|
||||
"@trpc/client": "^11.7.1",
|
||||
"@trpc/next": "^11.7.1",
|
||||
"@trpc/react-query": "^11.7.1",
|
||||
"@trpc/server": "^11.7.1",
|
||||
"@xterm/addon-canvas": "^0.7.0",
|
||||
"@xterm/addon-fit": "0.10.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"babel-plugin-react-compiler": "^1.0.0",
|
||||
"chroma-js": "^3.1.2",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.18",
|
||||
"dayjs": "^1.11.19",
|
||||
"dotenv": "^17.2.3",
|
||||
"flag-icons": "^7.5.0",
|
||||
"glob": "^11.0.3",
|
||||
"jotai": "^2.15.0",
|
||||
"jotai": "^2.15.1",
|
||||
"mantine-react-table": "2.0.0-beta.9",
|
||||
"next": "15.5.6",
|
||||
"next": "16.0.1",
|
||||
"postcss-preset-mantine": "^1.18.0",
|
||||
"prismjs": "^1.30.0",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"react-error-boundary": "^6.0.0",
|
||||
"react-simple-code-editor": "^0.14.1",
|
||||
"sass": "^1.93.2",
|
||||
"superjson": "2.2.3",
|
||||
"swagger-ui-react": "^5.29.5",
|
||||
"sass": "^1.93.3",
|
||||
"superjson": "2.2.5",
|
||||
"swagger-ui-react": "^5.30.1",
|
||||
"use-deep-compare-effect": "^1.8.1",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
@@ -93,14 +94,14 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/chroma-js": "3.1.1",
|
||||
"@types/node": "^24.9.1",
|
||||
"@types/chroma-js": "3.1.2",
|
||||
"@types/node": "^24.9.2",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/react": "19.2.2",
|
||||
"@types/react-dom": "19.2.2",
|
||||
"@types/swagger-ui-react": "^5.18.0",
|
||||
"concurrently": "^9.2.1",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"node-loader": "^2.1.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.9.3"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Group, Menu, ScrollArea } from "@mantine/core";
|
||||
import { useHotkeys } from "@mantine/hooks";
|
||||
@@ -29,6 +28,7 @@ import { useConfirmModal, useModalAction } from "@homarr/modals";
|
||||
import { AppSelectModal } from "@homarr/modals-collection";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import { useItemActions } from "~/components/board/items/item-actions";
|
||||
import { ItemSelectModal } from "~/components/board/items/item-select-modal";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Link from "next/link";
|
||||
import type { MantineColor } from "@mantine/core";
|
||||
import { Button, Card, Stack, Text } from "@mantine/core";
|
||||
import { IconBook2, IconCategoryPlus, IconLayoutDashboard, IconMailForward } from "@tabler/icons-react";
|
||||
@@ -8,6 +7,7 @@ import { getMantineColor } from "@homarr/common";
|
||||
import { db } from "@homarr/db";
|
||||
import { createDocumentationLink } from "@homarr/definitions";
|
||||
import { getScopedI18n } from "@homarr/translation/server";
|
||||
import { Link } from "@homarr/ui";
|
||||
import type { TablerIcon } from "@homarr/ui";
|
||||
|
||||
export const InitFinish = async () => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Fragment } from "react";
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { ActionIcon, ActionIconGroup, Anchor, Avatar, Card, Group, Stack, Text, Title } from "@mantine/core";
|
||||
import { IconBox, IconPencil } from "@tabler/icons-react";
|
||||
@@ -10,7 +9,7 @@ import { api } from "@homarr/api/server";
|
||||
import { auth } from "@homarr/auth/next";
|
||||
import type { inferSearchParamsFromSchema } from "@homarr/common/types";
|
||||
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
||||
import { SearchInput, TablePagination } from "@homarr/ui";
|
||||
import { Link, SearchInput, TablePagination } from "@homarr/ui";
|
||||
|
||||
import { ManageContainer } from "~/components/manage/manage-container";
|
||||
import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback } from "react";
|
||||
import Link from "next/link";
|
||||
import { Menu } from "@mantine/core";
|
||||
import { IconCopy, IconDeviceMobile, IconHome, IconSettings, IconTrash } from "@tabler/icons-react";
|
||||
|
||||
@@ -12,6 +11,7 @@ import { revalidatePathActionAsync } from "@homarr/common/client";
|
||||
import { useConfirmModal, useModalAction } from "@homarr/modals";
|
||||
import { DuplicateBoardModal } from "@homarr/modals-collection";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import { useBoardPermissions } from "~/components/board/permissions/client";
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Link from "next/link";
|
||||
import {
|
||||
ActionIcon,
|
||||
Badge,
|
||||
@@ -21,7 +20,7 @@ import type { RouterOutputs } from "@homarr/api";
|
||||
import { api } from "@homarr/api/server";
|
||||
import { auth } from "@homarr/auth/next";
|
||||
import { getScopedI18n } from "@homarr/translation/server";
|
||||
import { UserAvatar } from "@homarr/ui";
|
||||
import { Link, UserAvatar } from "@homarr/ui";
|
||||
|
||||
import { getBoardPermissionsAsync } from "~/components/board/permissions/server";
|
||||
import { ManageContainer } from "~/components/manage/manage-container";
|
||||
|
||||
+1
-1
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Alert, Anchor, Button, ButtonGroup, Fieldset, Group, Stack, Text, TextInput } from "@mantine/core";
|
||||
import { IconInfoCircle, IconPencil, IconPlus, IconUnlink } from "@tabler/icons-react";
|
||||
@@ -17,6 +16,7 @@ import { useConfirmModal, useModalAction } from "@homarr/modals";
|
||||
import { AppSelectModal } from "@homarr/modals-collection";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
import { integrationUpdateSchema } from "@homarr/validation/integration";
|
||||
|
||||
import { SecretCard } from "../../_components/secrets/integration-secret-card";
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
|
||||
import type { ChangeEvent } from "react";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { Flex, Group, Menu, ScrollArea, Text, TextInput } from "@mantine/core";
|
||||
import { IconSearch } from "@tabler/icons-react";
|
||||
|
||||
import { getIntegrationName, integrationKinds } from "@homarr/definitions";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { IntegrationAvatar } from "@homarr/ui";
|
||||
import { IntegrationAvatar, Link } from "@homarr/ui";
|
||||
|
||||
interface IntegrationCreateDropdownContentProps {
|
||||
enableMockIntegration: boolean;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { startTransition, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import {
|
||||
Alert,
|
||||
@@ -36,6 +35,7 @@ import type { GetInputPropsReturnType, UseFormReturnType } from "@homarr/form";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
import { appHrefSchema } from "@homarr/validation/app";
|
||||
import { integrationCreateSchema } from "@homarr/validation/integration";
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Fragment } from "react";
|
||||
import type { PropsWithChildren } from "react";
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import {
|
||||
AccordionControl,
|
||||
@@ -36,7 +35,7 @@ import { objectEntries } from "@homarr/common";
|
||||
import type { IntegrationKind } from "@homarr/definitions";
|
||||
import { getIntegrationName } from "@homarr/definitions";
|
||||
import { getScopedI18n } from "@homarr/translation/server";
|
||||
import { CountBadge, IntegrationAvatar } from "@homarr/ui";
|
||||
import { CountBadge, IntegrationAvatar, Link } from "@homarr/ui";
|
||||
|
||||
import { ManageContainer } from "~/components/manage/manage-container";
|
||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import {
|
||||
ActionIcon,
|
||||
@@ -25,7 +24,7 @@ import { humanFileSize } from "@homarr/common";
|
||||
import type { inferSearchParamsFromSchema } from "@homarr/common/types";
|
||||
import { createLocalImageUrl } from "@homarr/icons/local";
|
||||
import { getI18n } from "@homarr/translation/server";
|
||||
import { SearchInput, TablePagination, UserAvatar } from "@homarr/ui";
|
||||
import { Link, SearchInput, TablePagination, UserAvatar } from "@homarr/ui";
|
||||
|
||||
import { ManageContainer } from "~/components/manage/manage-container";
|
||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Metadata } from "next";
|
||||
import Link from "next/link";
|
||||
import { Card, Group, SimpleGrid, Space, Stack, Text } from "@mantine/core";
|
||||
import { IconArrowRight } from "@tabler/icons-react";
|
||||
|
||||
import { api } from "@homarr/api/server";
|
||||
import { getScopedI18n } from "@homarr/translation/server";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||
import { createMetaTitle } from "~/metadata";
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import type { SegmentedControlItem } from "@mantine/core";
|
||||
import { Button, Fieldset, Grid, Group, SegmentedControl, Stack, Textarea, TextInput } from "@mantine/core";
|
||||
import { WidgetIntegrationSelect } from "node_modules/@homarr/widgets/src/widget-integration-select";
|
||||
@@ -12,6 +11,7 @@ import { useZodForm } from "@homarr/form";
|
||||
import { IconPicker } from "@homarr/forms-collection";
|
||||
import type { TranslationFunction } from "@homarr/translation";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
import { searchEngineManageSchema } from "@homarr/validation/search-engine";
|
||||
|
||||
type FormType = z.infer<typeof searchEngineManageSchema>;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { ActionIcon, ActionIconGroup, Anchor, Avatar, Card, Group, Stack, Text, Title } from "@mantine/core";
|
||||
import { IconPencil, IconSearch } from "@tabler/icons-react";
|
||||
@@ -9,7 +8,7 @@ import { api } from "@homarr/api/server";
|
||||
import { auth } from "@homarr/auth/next";
|
||||
import type { inferSearchParamsFromSchema } from "@homarr/common/types";
|
||||
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
||||
import { SearchInput, TablePagination } from "@homarr/ui";
|
||||
import { Link, SearchInput, TablePagination } from "@homarr/ui";
|
||||
|
||||
import { ManageContainer } from "~/components/manage/manage-container";
|
||||
import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { X509Certificate } from "node:crypto";
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import {
|
||||
Button,
|
||||
@@ -19,6 +18,7 @@ import { IconCertificateOff } from "@tabler/icons-react";
|
||||
import { auth } from "@homarr/auth/next";
|
||||
import { getTrustedCertificateHostnamesAsync } from "@homarr/certificates/server";
|
||||
import { getI18n } from "@homarr/translation/server";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||
import { NoResults } from "~/components/no-results";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { X509Certificate } from "node:crypto";
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { Button, Card, Group, SimpleGrid, Stack, Text, Title } from "@mantine/core";
|
||||
import { IconAlertTriangle, IconCertificate, IconCertificateOff } from "@tabler/icons-react";
|
||||
@@ -10,6 +9,7 @@ import { loadCustomRootCertificatesAsync } from "@homarr/certificates/server";
|
||||
import { getMantineColor } from "@homarr/common";
|
||||
import type { SupportedLanguage } from "@homarr/translation";
|
||||
import { getI18n } from "@homarr/translation/server";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||
import { NoResults } from "~/components/no-results";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { Anchor, Center, Stack, Text } from "@mantine/core";
|
||||
import { IconShipOff } from "@tabler/icons-react";
|
||||
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
export default function DockerErrorPage() {
|
||||
const t = useI18n();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { Anchor, Center, Stack, Text } from "@mantine/core";
|
||||
import { IconCubeOff } from "@tabler/icons-react";
|
||||
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
export default function KubernetesErrorPage() {
|
||||
const t = useI18n();
|
||||
|
||||
+1
-1
@@ -1,11 +1,11 @@
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Card, Group, Text } from "@mantine/core";
|
||||
import { IconArrowRight } from "@tabler/icons-react";
|
||||
|
||||
import type { KubernetesLabelResourceType } from "@homarr/definitions";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import classes from "./resource-tile.module.css";
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import Link from "next/link";
|
||||
import { Anchor, Button, Group, Text, Title, Tooltip } from "@mantine/core";
|
||||
import { IconCheck } from "@tabler/icons-react";
|
||||
import type { MRT_ColumnDef } from "mantine-react-table";
|
||||
@@ -10,7 +9,7 @@ import { MantineReactTable } from "mantine-react-table";
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
import { UserAvatar } from "@homarr/ui";
|
||||
import { Link, UserAvatar } from "@homarr/ui";
|
||||
import { useTranslatedMantineReactTable } from "@homarr/ui/hooks";
|
||||
|
||||
interface UserListComponentProps {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import Link from "next/link";
|
||||
import { Button, Card, Group } from "@mantine/core";
|
||||
import { IconArrowBackUp, IconArrowLeft, IconArrowRight, IconRotate } from "@tabler/icons-react";
|
||||
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
interface StepperNavigationComponentProps {
|
||||
hasPrevious: boolean;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { NavLink } from "@mantine/core";
|
||||
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
interface NavigationLinkProps {
|
||||
href: string;
|
||||
label: string;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { Alert, Anchor } from "@mantine/core";
|
||||
import { IconExclamationCircle } from "@tabler/icons-react";
|
||||
|
||||
import { createDocumentationLink } from "@homarr/definitions";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
export const ReservedGroupAlert = () => {
|
||||
const t = useI18n();
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { PropsWithChildren } from "react";
|
||||
import Link from "next/link";
|
||||
import { Button, Grid, GridCol, Group, Stack, Text, Title } from "@mantine/core";
|
||||
import { IconId, IconLock, IconSettings, IconUsersGroup } from "@tabler/icons-react";
|
||||
|
||||
import { api } from "@homarr/api/server";
|
||||
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import { ManageContainer } from "~/components/manage/manage-container";
|
||||
import { NavigationLink } from "./_navigation";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { Alert, Anchor, Center, Group, Stack, Table, TableTbody, TableTd, TableTr, Text, Title } from "@mantine/core";
|
||||
import { IconExclamationCircle } from "@tabler/icons-react";
|
||||
@@ -10,7 +9,7 @@ import { auth } from "@homarr/auth/next";
|
||||
import { isProviderEnabled } from "@homarr/auth/server";
|
||||
import { everyoneGroup } from "@homarr/definitions";
|
||||
import { getI18n, getScopedI18n } from "@homarr/translation/server";
|
||||
import { SearchInput, UserAvatar } from "@homarr/ui";
|
||||
import { Link, SearchInput, UserAvatar } from "@homarr/ui";
|
||||
|
||||
import { ReservedGroupAlert } from "../_reserved-group-alert";
|
||||
import { AddGroupMember } from "./_add-group-member";
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import type { DragEndEvent, DraggableAttributes, DragStartEvent } from "@dnd-kit/core";
|
||||
import {
|
||||
closestCenter,
|
||||
@@ -41,7 +40,7 @@ import { clientApi } from "@homarr/api/client";
|
||||
import { revalidatePathActionAsync } from "@homarr/common/client";
|
||||
import { showSuccessNotification } from "@homarr/notifications";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { UserAvatarGroup } from "@homarr/ui";
|
||||
import { Link, UserAvatarGroup } from "@homarr/ui";
|
||||
|
||||
interface GroupsTableProps {
|
||||
initialGroupIds: string[];
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { Card, Group, Stack, Text, ThemeIcon, Title, UnstyledButton } from "@mantine/core";
|
||||
import { IconChevronRight, IconUsersGroup } from "@tabler/icons-react";
|
||||
@@ -7,6 +6,7 @@ import { api } from "@homarr/api/server";
|
||||
import { auth } from "@homarr/auth/next";
|
||||
import { everyoneGroup } from "@homarr/definitions";
|
||||
import { getI18n } from "@homarr/translation/server";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import { ManageContainer } from "~/components/manage/manage-container";
|
||||
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { Anchor, Button, Group, Stack, Table, TableTbody, TableTh, TableThead, TableTr } from "@mantine/core";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
|
||||
import { useModalAction } from "@homarr/modals";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import type { AccessQueryData } from "./access-settings";
|
||||
import { AccessSelectRow } from "./access-table-rows";
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { Anchor, Box, Button, Group, Stack, Table, TableTbody, TableTh, TableThead, TableTr } from "@mantine/core";
|
||||
import { IconPlus } from "@tabler/icons-react";
|
||||
|
||||
import { useModalAction } from "@homarr/modals";
|
||||
import { useI18n, useScopedI18n } from "@homarr/translation/client";
|
||||
import { UserAvatar } from "@homarr/ui";
|
||||
import { Link, UserAvatar } from "@homarr/ui";
|
||||
|
||||
import type { AccessQueryData } from "./access-settings";
|
||||
import { AccessDisplayRow, AccessSelectRow } from "./access-table-rows";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { ReactNode } from "react";
|
||||
import Link from "next/link";
|
||||
import { AppShellHeader, Group, UnstyledButton } from "@mantine/core";
|
||||
|
||||
import { Spotlight } from "@homarr/spotlight";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import { ClientBurger } from "./header/burger";
|
||||
import { DesktopSearchInput, MobileSearchButton } from "./header/search";
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { ForwardedRef, ReactNode } from "react";
|
||||
import { forwardRef } from "react";
|
||||
import Link from "next/link";
|
||||
import type { ActionIconProps } from "@mantine/core";
|
||||
import { ActionIcon } from "@mantine/core";
|
||||
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
type HeaderButtonProps = (
|
||||
| {
|
||||
onClick?: () => void;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { NavLink } from "@mantine/core";
|
||||
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
export const CommonNavLink = (props: ClientNavigationLink) =>
|
||||
"href" in props ? <NavLinkHref {...props} /> : <NavLinkWithItems {...props} />;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Center, Menu, Stack, Text, useMantineColorScheme } from "@mantine/core";
|
||||
import { useHotkeys, useTimeout } from "@mantine/hooks";
|
||||
@@ -22,6 +21,7 @@ import { signOut, useSession } from "@homarr/auth/client";
|
||||
import { hotkeys } from "@homarr/definitions";
|
||||
import { createModal, useModalAction } from "@homarr/modals";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import { useAuthContext } from "~/app/[locale]/_client-providers/session";
|
||||
import { CurrentLanguageCombobox } from "./language/current-language-combobox";
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import type { NextRequest } from "next/server";
|
||||
import { createTRPCClient, httpLink } from "@trpc/client";
|
||||
import SuperJSON from "superjson";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import type { AppRouter } from "@homarr/api";
|
||||
import { createHeadersCallbackForSource, getTrpcUrl } from "@homarr/api/shared";
|
||||
import { api } from "@homarr/api/server";
|
||||
import { localeCookieKey } from "@homarr/definitions";
|
||||
import type { SupportedLanguage } from "@homarr/translation";
|
||||
import { supportedLanguages } from "@homarr/translation";
|
||||
@@ -12,12 +9,12 @@ import { createI18nMiddleware } from "@homarr/translation/middleware";
|
||||
|
||||
let isOnboardingFinished = false;
|
||||
|
||||
export async function middleware(request: NextRequest) {
|
||||
export async function proxy(request: NextRequest) {
|
||||
// Redirect to onboarding if it's not finished yet
|
||||
const pathname = request.nextUrl.pathname;
|
||||
|
||||
if (!isOnboardingFinished && !pathname.endsWith("/init")) {
|
||||
const currentOnboardingStep = await serverFetchApi.onboard.currentStep.query();
|
||||
const currentOnboardingStep = await api.onboard.currentStep();
|
||||
if (currentOnboardingStep.current !== "finish") {
|
||||
return NextResponse.redirect(new URL("/init", request.url));
|
||||
}
|
||||
@@ -29,11 +26,12 @@ export async function middleware(request: NextRequest) {
|
||||
const currentLocale = request.cookies.get(localeCookieKey)?.value;
|
||||
let defaultLocale: SupportedLanguage = "en";
|
||||
if (!currentLocale || !supportedLanguages.includes(currentLocale as SupportedLanguage)) {
|
||||
defaultLocale = await serverFetchApi.serverSettings.getCulture.query().then((culture) => culture.defaultLocale);
|
||||
defaultLocale = await api.serverSettings.getCulture().then((culture) => culture.defaultLocale);
|
||||
}
|
||||
|
||||
// We don't want to fallback to accept-language header so we clear it
|
||||
request.headers.set("accept-language", "");
|
||||
|
||||
const next = createI18nMiddleware(defaultLocale);
|
||||
return next(request);
|
||||
}
|
||||
@@ -41,13 +39,3 @@ export async function middleware(request: NextRequest) {
|
||||
export const config = {
|
||||
matcher: ["/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)"],
|
||||
};
|
||||
|
||||
export const serverFetchApi = createTRPCClient<AppRouter>({
|
||||
links: [
|
||||
httpLink({
|
||||
url: getTrpcUrl(),
|
||||
transformer: SuperJSON,
|
||||
headers: createHeadersCallbackForSource("server-fetch"),
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -37,20 +37,20 @@
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@homarr/widgets": "workspace:^0.1.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"dayjs": "^1.11.19",
|
||||
"dotenv": "^17.2.3",
|
||||
"fastify": "^5.6.1",
|
||||
"superjson": "2.2.3",
|
||||
"superjson": "2.2.5",
|
||||
"undici": "7.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/node": "^24.9.1",
|
||||
"dotenv-cli": "^10.0.0",
|
||||
"esbuild": "^0.25.11",
|
||||
"eslint": "^9.38.0",
|
||||
"@types/node": "^24.9.2",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"esbuild": "^0.25.12",
|
||||
"eslint": "^9.39.0",
|
||||
"prettier": "^3.6.2",
|
||||
"tsx": "4.20.4",
|
||||
"typescript": "^5.9.3"
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/ws": "^8.18.1",
|
||||
"esbuild": "^0.25.11",
|
||||
"eslint": "^9.38.0",
|
||||
"esbuild": "^0.25.12",
|
||||
"eslint": "^9.39.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
+13
-13
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "homarr",
|
||||
"version": "1.43.0",
|
||||
"version": "1.43.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "cross-env CI=true turbo build",
|
||||
@@ -39,28 +39,28 @@
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/commit-analyzer": "^13.0.1",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"@semantic-release/github": "^12.0.0",
|
||||
"@semantic-release/github": "^12.0.1",
|
||||
"@semantic-release/npm": "^13.1.1",
|
||||
"@semantic-release/release-notes-generator": "^14.1.0",
|
||||
"@testcontainers/redis": "^11.7.2",
|
||||
"@turbo/gen": "^2.5.8",
|
||||
"@turbo/gen": "^2.6.0",
|
||||
"@vitejs/plugin-react": "^5.1.0",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"@vitest/coverage-v8": "^4.0.6",
|
||||
"@vitest/ui": "^4.0.6",
|
||||
"conventional-changelog-conventionalcommits": "^9.1.0",
|
||||
"cross-env": "^10.1.0",
|
||||
"jsdom": "^27.0.1",
|
||||
"jsdom": "^27.1.0",
|
||||
"prettier": "^3.6.2",
|
||||
"semantic-release": "^25.0.1",
|
||||
"testcontainers": "^11.7.2",
|
||||
"turbo": "^2.5.8",
|
||||
"turbo": "^2.6.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite-tsconfig-paths": "^5.1.4",
|
||||
"vitest": "^3.2.4"
|
||||
"vitest": "^4.0.6"
|
||||
},
|
||||
"packageManager": "pnpm@10.19.0",
|
||||
"packageManager": "pnpm@10.20.0",
|
||||
"engines": {
|
||||
"node": ">=22.21.0"
|
||||
"node": ">=24.11.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
@@ -77,12 +77,12 @@
|
||||
"overrides": {
|
||||
"@babel/helpers@<7.26.10": ">=7.28.4",
|
||||
"@babel/runtime@<7.26.10": ">=7.28.4",
|
||||
"axios@>=1.0.0 <1.8.2": ">=1.12.2",
|
||||
"axios@>=1.0.0 <1.8.2": ">=1.13.1",
|
||||
"brace-expansion@>=2.0.0 <=2.0.1": ">=4.0.1",
|
||||
"brace-expansion@>=1.0.0 <=1.1.11": ">=4.0.1",
|
||||
"esbuild@<=0.24.2": ">=0.25.11",
|
||||
"esbuild@<=0.24.2": ">=0.25.12",
|
||||
"form-data@>=4.0.0 <4.0.4": ">=4.0.4",
|
||||
"hono@<4.6.5": ">=4.10.3",
|
||||
"hono@<4.6.5": ">=4.10.4",
|
||||
"linkifyjs@<4.3.2": ">=4.3.2",
|
||||
"nanoid@>=4.0.0 <5.0.9": ">=5.1.6",
|
||||
"prismjs@<1.30.0": ">=1.30.0",
|
||||
|
||||
@@ -26,13 +26,13 @@
|
||||
"@homarr/log": "workspace:^0.1.0",
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@umami/node": "^0.4.0",
|
||||
"superjson": "2.2.3"
|
||||
"superjson": "2.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -43,16 +43,16 @@
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@kubernetes/client-node": "^1.4.0",
|
||||
"@tanstack/react-query": "^5.90.5",
|
||||
"@trpc/client": "^11.6.0",
|
||||
"@trpc/react-query": "^11.6.0",
|
||||
"@trpc/server": "^11.6.0",
|
||||
"@trpc/tanstack-react-query": "^11.6.0",
|
||||
"@tanstack/react-query": "^5.90.6",
|
||||
"@trpc/client": "^11.7.1",
|
||||
"@trpc/react-query": "^11.7.1",
|
||||
"@trpc/server": "^11.7.1",
|
||||
"@trpc/tanstack-react-query": "^11.7.1",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"next": "15.5.6",
|
||||
"next": "16.0.1",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"superjson": "2.2.3",
|
||||
"superjson": "2.2.5",
|
||||
"trpc-to-openapi": "^3.1.0",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
@@ -60,7 +60,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
@@ -40,15 +40,22 @@ export const releasesRouter = createTRPCRouter({
|
||||
.query(async ({ ctx, input }) => {
|
||||
return await Promise.all(
|
||||
input.repositories.map(async (repository) => {
|
||||
const innerHandler = releasesRequestHandler.handler(ctx.integration, {
|
||||
id: repository.id,
|
||||
identifier: repository.identifier,
|
||||
versionRegex: formatVersionFilterRegex(repository.versionFilter),
|
||||
});
|
||||
const response = await releasesRequestHandler
|
||||
.handler(ctx.integration, {
|
||||
id: repository.id,
|
||||
identifier: repository.identifier,
|
||||
versionRegex: formatVersionFilterRegex(repository.versionFilter),
|
||||
})
|
||||
.getCachedOrUpdatedDataAsync({
|
||||
forceUpdate: false,
|
||||
});
|
||||
|
||||
return await innerHandler.getCachedOrUpdatedDataAsync({
|
||||
forceUpdate: false,
|
||||
});
|
||||
return {
|
||||
id: repository.id,
|
||||
integration: { name: ctx.integration.name, kind: ctx.integration.kind },
|
||||
timestamp: response.timestamp,
|
||||
...response.data,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}),
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@auth/core": "^0.41.0",
|
||||
"@auth/drizzle-adapter": "^1.11.0",
|
||||
"@auth/core": "^0.41.1",
|
||||
"@auth/drizzle-adapter": "^1.11.1",
|
||||
"@homarr/certificates": "workspace:^0.1.0",
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
@@ -35,8 +35,8 @@
|
||||
"bcrypt": "^6.0.0",
|
||||
"cookies": "^0.9.1",
|
||||
"ldapts": "8.0.9",
|
||||
"next": "15.5.6",
|
||||
"next-auth": "5.0.0-beta.29",
|
||||
"next": "16.0.1",
|
||||
"next-auth": "5.0.0-beta.30",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"zod": "^4.1.12"
|
||||
@@ -47,7 +47,7 @@
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/bcrypt": "6.0.0",
|
||||
"@types/cookies": "0.9.2",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
@@ -23,12 +23,11 @@ describe("authorizeWithLdapCredentials", () => {
|
||||
test("should fail when wrong ldap base credentials", async () => {
|
||||
// Arrange
|
||||
const spy = vi.spyOn(ldapClient, "LdapClient");
|
||||
spy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
bindAsync: vi.fn(() => Promise.reject(new Error("bindAsync"))),
|
||||
}) as unknown as ldapClient.LdapClient,
|
||||
);
|
||||
spy.mockImplementation(function () {
|
||||
return {
|
||||
bindAsync: vi.fn(() => Promise.reject(new Error("bindAsync"))),
|
||||
} as unknown as ldapClient.LdapClient;
|
||||
});
|
||||
|
||||
// Act
|
||||
const act = () =>
|
||||
@@ -44,13 +43,12 @@ describe("authorizeWithLdapCredentials", () => {
|
||||
test("should fail when user not found", async () => {
|
||||
// Arrange
|
||||
const spy = vi.spyOn(ldapClient, "LdapClient");
|
||||
spy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn(() => Promise.resolve([])),
|
||||
}) as unknown as ldapClient.LdapClient,
|
||||
);
|
||||
spy.mockImplementation(function () {
|
||||
return {
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn(() => Promise.resolve([])),
|
||||
} as unknown as ldapClient.LdapClient;
|
||||
});
|
||||
|
||||
// Act
|
||||
const act = () =>
|
||||
@@ -66,20 +64,19 @@ describe("authorizeWithLdapCredentials", () => {
|
||||
test("should fail when user has invalid email", async () => {
|
||||
// Arrange
|
||||
const spy = vi.spyOn(ldapClient, "LdapClient");
|
||||
spy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
dn: "test",
|
||||
mail: "test",
|
||||
},
|
||||
]),
|
||||
),
|
||||
}) as unknown as ldapClient.LdapClient,
|
||||
);
|
||||
spy.mockImplementation(function () {
|
||||
return {
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
dn: "test",
|
||||
mail: "test",
|
||||
},
|
||||
]),
|
||||
),
|
||||
} as unknown as ldapClient.LdapClient;
|
||||
});
|
||||
|
||||
// Act
|
||||
const act = () =>
|
||||
@@ -103,15 +100,14 @@ describe("authorizeWithLdapCredentials", () => {
|
||||
]),
|
||||
);
|
||||
const spy = vi.spyOn(ldapClient, "LdapClient");
|
||||
spy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
bindAsync: vi.fn((props: ldapClient.BindOptions) =>
|
||||
props.distinguishedName === "test" ? Promise.reject(new Error("bindAsync")) : Promise.resolve(),
|
||||
),
|
||||
searchAsync: searchSpy,
|
||||
}) as unknown as ldapClient.LdapClient,
|
||||
);
|
||||
spy.mockImplementation(function () {
|
||||
return {
|
||||
bindAsync: vi.fn((props: ldapClient.BindOptions) =>
|
||||
props.distinguishedName === "test" ? Promise.reject(new Error("bindAsync")) : Promise.resolve(),
|
||||
),
|
||||
searchAsync: searchSpy,
|
||||
} as unknown as ldapClient.LdapClient;
|
||||
});
|
||||
|
||||
// Act
|
||||
const act = () =>
|
||||
@@ -129,21 +125,20 @@ describe("authorizeWithLdapCredentials", () => {
|
||||
// Arrange
|
||||
const db = createDb();
|
||||
const spy = vi.spyOn(ldapClient, "LdapClient");
|
||||
spy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
dn: "test",
|
||||
mail: "test@gmail.com",
|
||||
},
|
||||
]),
|
||||
),
|
||||
disconnectAsync: vi.fn(),
|
||||
}) as unknown as ldapClient.LdapClient,
|
||||
);
|
||||
spy.mockImplementation(function () {
|
||||
return {
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
dn: "test",
|
||||
mail: "test@gmail.com",
|
||||
},
|
||||
]),
|
||||
),
|
||||
disconnectAsync: vi.fn(),
|
||||
} as unknown as ldapClient.LdapClient;
|
||||
});
|
||||
|
||||
// Act
|
||||
const result = await authorizeWithLdapCredentialsAsync(db, {
|
||||
@@ -168,21 +163,20 @@ describe("authorizeWithLdapCredentials", () => {
|
||||
// Arrange
|
||||
const db = createDb();
|
||||
const spy = vi.spyOn(ldapClient, "LdapClient");
|
||||
spy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
dn: "test",
|
||||
mail: "test@gmail.com",
|
||||
},
|
||||
]),
|
||||
),
|
||||
disconnectAsync: vi.fn(),
|
||||
}) as unknown as ldapClient.LdapClient,
|
||||
);
|
||||
spy.mockImplementation(function () {
|
||||
return {
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
dn: "test",
|
||||
mail: "test@gmail.com",
|
||||
},
|
||||
]),
|
||||
),
|
||||
disconnectAsync: vi.fn(),
|
||||
} as unknown as ldapClient.LdapClient;
|
||||
});
|
||||
await db.insert(users).values({
|
||||
id: createId(),
|
||||
name: "test",
|
||||
@@ -220,21 +214,20 @@ describe("authorizeWithLdapCredentials", () => {
|
||||
test("should authorize user with correct credentials and return updated name", async () => {
|
||||
// Arrange
|
||||
const spy = vi.spyOn(ldapClient, "LdapClient");
|
||||
spy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
dn: "test55",
|
||||
mail: "test@gmail.com",
|
||||
},
|
||||
]),
|
||||
),
|
||||
disconnectAsync: vi.fn(),
|
||||
}) as unknown as ldapClient.LdapClient,
|
||||
);
|
||||
spy.mockImplementation(function () {
|
||||
return {
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn(() =>
|
||||
Promise.resolve([
|
||||
{
|
||||
dn: "test55",
|
||||
mail: "test@gmail.com",
|
||||
},
|
||||
]),
|
||||
),
|
||||
disconnectAsync: vi.fn(),
|
||||
} as unknown as ldapClient.LdapClient;
|
||||
});
|
||||
|
||||
const userId = createId();
|
||||
const db = createDb();
|
||||
@@ -268,27 +261,26 @@ describe("authorizeWithLdapCredentials", () => {
|
||||
test("should authorize user with correct credentials and return his groups", async () => {
|
||||
// Arrange
|
||||
const spy = vi.spyOn(ldapClient, "LdapClient");
|
||||
spy.mockImplementation(
|
||||
() =>
|
||||
({
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn((argument: { options: { filter: string } }) =>
|
||||
argument.options.filter.includes("group")
|
||||
? Promise.resolve([
|
||||
{
|
||||
cn: "homarr_example",
|
||||
},
|
||||
])
|
||||
: Promise.resolve([
|
||||
{
|
||||
dn: "test55",
|
||||
mail: "test@gmail.com",
|
||||
},
|
||||
]),
|
||||
),
|
||||
disconnectAsync: vi.fn(),
|
||||
}) as unknown as ldapClient.LdapClient,
|
||||
);
|
||||
spy.mockImplementation(function () {
|
||||
return {
|
||||
bindAsync: vi.fn(() => Promise.resolve()),
|
||||
searchAsync: vi.fn((argument: { options: { filter: string } }) =>
|
||||
argument.options.filter.includes("group")
|
||||
? Promise.resolve([
|
||||
{
|
||||
cn: "homarr_example",
|
||||
},
|
||||
])
|
||||
: Promise.resolve([
|
||||
{
|
||||
dn: "test55",
|
||||
mail: "test@gmail.com",
|
||||
},
|
||||
]),
|
||||
),
|
||||
disconnectAsync: vi.fn(),
|
||||
} as unknown as ldapClient.LdapClient;
|
||||
});
|
||||
const db = createDb();
|
||||
const userId = createId();
|
||||
await db.insert(users).values({
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -34,8 +34,8 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"esbuild": "^0.25.11",
|
||||
"eslint": "^9.38.0",
|
||||
"esbuild": "^0.25.12",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,10 @@
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/log": "workspace:^0.1.0",
|
||||
"@paralleldrive/cuid2": "^3.1.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"dayjs": "^1.11.19",
|
||||
"dns-caching": "^0.2.7",
|
||||
"next": "15.5.6",
|
||||
"octokit": "^5.0.4",
|
||||
"next": "16.0.1",
|
||||
"octokit": "^5.0.5",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"undici": "7.16.0",
|
||||
@@ -44,7 +44,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -29,10 +29,10 @@
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/cron-jobs": "workspace:^0.1.0",
|
||||
"@homarr/log": "workspace:^0.1.0",
|
||||
"@tanstack/react-query": "^5.90.5",
|
||||
"@trpc/client": "^11.6.0",
|
||||
"@trpc/server": "^11.6.0",
|
||||
"@trpc/tanstack-react-query": "^11.6.0",
|
||||
"@tanstack/react-query": "^5.90.6",
|
||||
"@trpc/client": "^11.7.1",
|
||||
"@trpc/server": "^11.7.1",
|
||||
"@trpc/tanstack-react-query": "^11.7.1",
|
||||
"node-cron": "^4.2.1",
|
||||
"react": "19.2.0",
|
||||
"zod": "^4.1.12"
|
||||
@@ -43,7 +43,7 @@
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@types/react": "19.2.2",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -43,34 +43,34 @@
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@auth/core": "^0.41.0",
|
||||
"@auth/core": "^0.41.1",
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"@homarr/definitions": "workspace:^0.1.0",
|
||||
"@homarr/log": "workspace:^0.1.0",
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.5",
|
||||
"@mantine/core": "^8.3.6",
|
||||
"@paralleldrive/cuid2": "^3.1.0",
|
||||
"@testcontainers/mysql": "^11.7.2",
|
||||
"@testcontainers/postgresql": "^11.7.2",
|
||||
"better-sqlite3": "^12.4.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"drizzle-kit": "^0.31.5",
|
||||
"drizzle-kit": "^0.31.6",
|
||||
"drizzle-orm": "^0.44.7",
|
||||
"drizzle-zod": "^0.8.3",
|
||||
"mysql2": "3.15.3",
|
||||
"pg": "^8.16.3",
|
||||
"superjson": "2.2.3"
|
||||
"superjson": "2.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"@types/pg": "^8.15.5",
|
||||
"dotenv-cli": "^10.0.0",
|
||||
"esbuild": "^0.25.11",
|
||||
"eslint": "^9.38.0",
|
||||
"@types/pg": "^8.15.6",
|
||||
"dotenv-cli": "^11.0.0",
|
||||
"esbuild": "^0.25.12",
|
||||
"eslint": "^9.39.0",
|
||||
"prettier": "^3.6.2",
|
||||
"tsx": "4.20.4",
|
||||
"typescript": "^5.9.3"
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"tsx": "4.20.4",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/dockerode": "^3.3.45",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/form": "^8.3.5",
|
||||
"@mantine/form": "^8.3.6",
|
||||
"mantine-form-zod-resolver": "^1.3.0",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
@@ -34,7 +34,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,9 @@
|
||||
"@homarr/form": "workspace:^0.1.0",
|
||||
"@homarr/notifications": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.5",
|
||||
"@mantine/core": "^8.3.6",
|
||||
"react": "19.2.0",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
@@ -37,7 +38,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import type { ChangeEventHandler } from "react";
|
||||
import { useEffect, useRef } from "react";
|
||||
import Link from "next/link";
|
||||
import { Button, Checkbox, Collapse, Group, Stack, Textarea, TextInput } from "@mantine/core";
|
||||
import { useDebouncedValue, useDisclosure } from "@mantine/hooks";
|
||||
import type { z } from "zod/v4";
|
||||
@@ -10,6 +9,7 @@ import type { z } from "zod/v4";
|
||||
import { clientApi } from "@homarr/api/client";
|
||||
import { useZodForm } from "@homarr/form";
|
||||
import { useI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
import { appManageSchema } from "@homarr/validation/app";
|
||||
|
||||
import { IconPicker } from "../icon-picker/icon-picker";
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/bcrypt": "6.0.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,10 @@
|
||||
},
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@ctrl/deluge": "^7.4.0",
|
||||
"@ctrl/deluge": "^7.5.0",
|
||||
"@ctrl/qbittorrent": "^9.10.0",
|
||||
"@ctrl/transmission": "^7.4.0",
|
||||
"@gitbeaker/rest": "^43.5.0",
|
||||
"@gitbeaker/rest": "^43.8.0",
|
||||
"@homarr/certificates": "workspace:^0.1.0",
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
@@ -39,14 +39,14 @@
|
||||
"@homarr/redis": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@jellyfin/sdk": "^0.12.0",
|
||||
"@octokit/auth-app": "^8.1.1",
|
||||
"@jellyfin/sdk": "^0.13.0",
|
||||
"@octokit/auth-app": "^8.1.2",
|
||||
"ical.js": "^2.2.1",
|
||||
"maria2": "^0.4.1",
|
||||
"node-ical": "^0.22.1",
|
||||
"octokit": "^5.0.4",
|
||||
"octokit": "^5.0.5",
|
||||
"proxmox-api": "1.1.1",
|
||||
"tsdav": "^2.1.5",
|
||||
"tsdav": "^2.1.6",
|
||||
"undici": "7.16.0",
|
||||
"xml2js": "^0.6.2",
|
||||
"zod": "^4.1.12"
|
||||
@@ -57,7 +57,7 @@
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/node-unifi": "^2.5.1",
|
||||
"@types/xml2js": "^0.4.14",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,7 @@ import type { ReleasesProviderIntegration } from "../interfaces/releases-provide
|
||||
import { getLatestRelease } from "../interfaces/releases-providers/releases-providers-integration";
|
||||
import type {
|
||||
DetailsProviderResponse,
|
||||
ReleasesRepository,
|
||||
ReleasesResponse,
|
||||
ReleaseResponse,
|
||||
} from "../interfaces/releases-providers/releases-providers-types";
|
||||
import { detailsResponseSchema, releasesResponseSchema } from "./codeberg-schemas";
|
||||
|
||||
@@ -43,22 +42,23 @@ export class CodebergIntegration extends Integration implements ReleasesProvider
|
||||
};
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(repository: ReleasesRepository): Promise<ReleasesResponse> {
|
||||
const [owner, name] = repository.identifier.split("/");
|
||||
private parseIdentifier(identifier: string) {
|
||||
const [owner, name] = identifier.split("/");
|
||||
if (!owner || !name) {
|
||||
localLogger.warn(
|
||||
`Invalid identifier format. Expected 'owner/name', for ${repository.identifier} with Codeberg integration`,
|
||||
{
|
||||
identifier: repository.identifier,
|
||||
},
|
||||
`Invalid identifier format. Expected 'owner/name', for ${identifier} with Codeberg integration`,
|
||||
{ identifier },
|
||||
);
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { code: "invalidIdentifier" },
|
||||
};
|
||||
return null;
|
||||
}
|
||||
return { owner, name };
|
||||
}
|
||||
|
||||
const details = await this.getDetailsAsync(owner, name);
|
||||
public async getLatestMatchingReleaseAsync(identifier: string, versionRegex?: string): Promise<ReleaseResponse> {
|
||||
const parsedIdentifier = this.parseIdentifier(identifier);
|
||||
if (!parsedIdentifier) return { success: false, error: { code: "invalidIdentifier" } };
|
||||
|
||||
const { owner, name } = parsedIdentifier;
|
||||
|
||||
const releasesResponse = await this.withHeadersAsync(async (headers) => {
|
||||
return await fetchWithTrustedCertificatesAsync(
|
||||
@@ -66,34 +66,36 @@ export class CodebergIntegration extends Integration implements ReleasesProvider
|
||||
{ headers },
|
||||
);
|
||||
});
|
||||
|
||||
if (!releasesResponse.ok) {
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { message: releasesResponse.statusText },
|
||||
};
|
||||
return { success: false, error: { code: "unexpected", message: releasesResponse.statusText } };
|
||||
}
|
||||
|
||||
const releasesResponseJson: unknown = await releasesResponse.json();
|
||||
const { data, success, error } = releasesResponseSchema.safeParse(releasesResponseJson);
|
||||
|
||||
if (!success) {
|
||||
return {
|
||||
id: repository.id,
|
||||
success: false,
|
||||
error: {
|
||||
code: "unexpected",
|
||||
message: releasesResponseJson ? JSON.stringify(releasesResponseJson, null, 2) : error.message,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
const formattedReleases = data.map((tag) => ({
|
||||
latestRelease: tag.tag_name,
|
||||
latestReleaseAt: tag.published_at,
|
||||
releaseUrl: tag.url,
|
||||
releaseDescription: tag.body,
|
||||
isPreRelease: tag.prerelease,
|
||||
}));
|
||||
return getLatestRelease(formattedReleases, repository, details);
|
||||
}
|
||||
|
||||
const formattedReleases = data.map((tag) => ({
|
||||
latestRelease: tag.tag_name,
|
||||
latestReleaseAt: tag.published_at,
|
||||
releaseUrl: tag.url,
|
||||
releaseDescription: tag.body,
|
||||
isPreRelease: tag.prerelease,
|
||||
}));
|
||||
|
||||
const latestRelease = getLatestRelease(formattedReleases, versionRegex);
|
||||
if (!latestRelease) return { success: false, error: { code: "noMatchingVersion" } };
|
||||
|
||||
const details = await this.getDetailsAsync(owner, name);
|
||||
|
||||
return { success: true, data: { ...details, ...latestRelease } };
|
||||
}
|
||||
|
||||
protected async getDetailsAsync(owner: string, name: string): Promise<DetailsProviderResponse | undefined> {
|
||||
|
||||
@@ -14,8 +14,7 @@ import type { ReleasesProviderIntegration } from "../interfaces/releases-provide
|
||||
import { getLatestRelease } from "../interfaces/releases-providers/releases-providers-integration";
|
||||
import type {
|
||||
DetailsProviderResponse,
|
||||
ReleasesRepository,
|
||||
ReleasesResponse,
|
||||
ReleaseResponse,
|
||||
} from "../interfaces/releases-providers/releases-providers-types";
|
||||
import { accessTokenResponseSchema, detailsResponseSchema, releasesResponseSchema } from "./docker-hub-schemas";
|
||||
|
||||
@@ -73,49 +72,61 @@ export class DockerHubIntegration extends Integration implements ReleasesProvide
|
||||
};
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(repository: ReleasesRepository): Promise<ReleasesResponse> {
|
||||
const relativeUrl = this.getRelativeUrl(repository.identifier);
|
||||
if (relativeUrl === "/") {
|
||||
localLogger.warn(
|
||||
`Invalid identifier format. Expected 'owner/name' or 'name', for ${repository.identifier} on DockerHub`,
|
||||
{
|
||||
identifier: repository.identifier,
|
||||
},
|
||||
);
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { code: "invalidIdentifier" },
|
||||
};
|
||||
}
|
||||
|
||||
const details = await this.getDetailsAsync(relativeUrl);
|
||||
|
||||
const releasesResponse = await this.withHeadersAsync(async (headers) => {
|
||||
return await fetchWithTrustedCertificatesAsync(this.url(`${relativeUrl}/tags?page_size=100`), {
|
||||
headers,
|
||||
private parseIdentifier(identifier: string) {
|
||||
if (!identifier.includes("/")) return { owner: "", name: identifier };
|
||||
const [owner, name] = identifier.split("/");
|
||||
if (!owner || !name) {
|
||||
localLogger.warn(`Invalid identifier format. Expected 'owner/name' or 'name', for ${identifier} on DockerHub`, {
|
||||
identifier,
|
||||
});
|
||||
});
|
||||
return null;
|
||||
}
|
||||
return { owner, name };
|
||||
}
|
||||
|
||||
if (!releasesResponse.ok) {
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { message: releasesResponse.statusText },
|
||||
};
|
||||
public async getLatestMatchingReleaseAsync(identifier: string, versionRegex?: string): Promise<ReleaseResponse> {
|
||||
const parsedIdentifier = this.parseIdentifier(identifier);
|
||||
if (!parsedIdentifier) return { success: false, error: { code: "invalidIdentifier" } };
|
||||
|
||||
const { owner, name } = parsedIdentifier;
|
||||
|
||||
const relativeUrl: `/${string}` = owner
|
||||
? `/v2/namespaces/${encodeURIComponent(owner)}/repositories/${encodeURIComponent(name)}`
|
||||
: `/v2/repositories/library/${encodeURIComponent(name)}`;
|
||||
|
||||
for (let page = 0; page <= 5; page++) {
|
||||
const releasesResponse = await this.withHeadersAsync(async (headers) => {
|
||||
return await fetchWithTrustedCertificatesAsync(this.url(`${relativeUrl}/tags?page_size=100&page=${page}`), {
|
||||
headers,
|
||||
});
|
||||
});
|
||||
if (!releasesResponse.ok) {
|
||||
return { success: false, error: { code: "unexpected", message: releasesResponse.statusText } };
|
||||
}
|
||||
|
||||
const releasesResponseJson: unknown = await releasesResponse.json();
|
||||
const releasesResult = releasesResponseSchema.safeParse(releasesResponseJson);
|
||||
if (!releasesResult.success) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: "unexpected",
|
||||
message: releasesResponseJson
|
||||
? JSON.stringify(releasesResponseJson, null, 2)
|
||||
: releasesResult.error.message,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const latestRelease = getLatestRelease(releasesResult.data.results, versionRegex);
|
||||
if (!latestRelease) continue;
|
||||
|
||||
const details = await this.getDetailsAsync(relativeUrl);
|
||||
|
||||
return { success: true, data: { ...details, ...latestRelease } };
|
||||
}
|
||||
|
||||
const releasesResponseJson: unknown = await releasesResponse.json();
|
||||
const releasesResult = releasesResponseSchema.safeParse(releasesResponseJson);
|
||||
|
||||
if (!releasesResult.success) {
|
||||
return {
|
||||
id: repository.id,
|
||||
error: {
|
||||
message: releasesResponseJson ? JSON.stringify(releasesResponseJson, null, 2) : releasesResult.error.message,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return getLatestRelease(releasesResult.data.results, repository, details);
|
||||
}
|
||||
return { success: false, error: { code: "noMatchingVersion" } };
|
||||
}
|
||||
|
||||
private async getDetailsAsync(relativeUrl: `/${string}`): Promise<DetailsProviderResponse | undefined> {
|
||||
@@ -154,18 +165,6 @@ export class DockerHubIntegration extends Integration implements ReleasesProvide
|
||||
};
|
||||
}
|
||||
|
||||
private getRelativeUrl(identifier: string): `/${string}` {
|
||||
if (identifier.indexOf("/") > 0) {
|
||||
const [owner, name] = identifier.split("/");
|
||||
if (!owner || !name) {
|
||||
return "/";
|
||||
}
|
||||
return `/v2/namespaces/${encodeURIComponent(owner)}/repositories/${encodeURIComponent(name)}`;
|
||||
} else {
|
||||
return `/v2/repositories/library/${encodeURIComponent(identifier)}`;
|
||||
}
|
||||
}
|
||||
|
||||
private async getSessionAsync(fetchAsync: typeof fetch = fetchWithTrustedCertificatesAsync): Promise<string> {
|
||||
const response = await fetchAsync(this.url("/v2/auth/token"), {
|
||||
method: "POST",
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { TestingResult } from "../base/test-connection/test-connection-serv
|
||||
import type { IMediaServerIntegration } from "../interfaces/media-server/media-server-integration";
|
||||
import type { CurrentSessionsInput, StreamSession } from "../interfaces/media-server/media-server-types";
|
||||
import { convertJellyfinType } from "../jellyfin/jellyfin-integration";
|
||||
import type { IMediaReleasesIntegration, MediaRelease } from "../types";
|
||||
import type { IMediaReleasesIntegration, MediaRelease, MediaType } from "../types";
|
||||
|
||||
const sessionSchema = z.object({
|
||||
NowPlayingItem: z
|
||||
@@ -163,7 +163,7 @@ export class EmbyIntegration extends Integration implements IMediaServerIntegrat
|
||||
|
||||
return items.map((item) => ({
|
||||
id: item.Id,
|
||||
type: item.Type === "Movie" ? "movie" : item.Type === "Series" ? "tv" : "unknown",
|
||||
type: this.mapMediaReleaseType(item.Type),
|
||||
title: item.Name,
|
||||
subtitle: item.Taglines.at(0),
|
||||
description: item.Overview,
|
||||
@@ -179,6 +179,27 @@ export class EmbyIntegration extends Integration implements IMediaServerIntegrat
|
||||
}));
|
||||
}
|
||||
|
||||
private mapMediaReleaseType(type: string | undefined): MediaType {
|
||||
switch (type) {
|
||||
case "Audio":
|
||||
case "AudioBook":
|
||||
case "MusicAlbum":
|
||||
return "music";
|
||||
case "Book":
|
||||
return "book";
|
||||
case "Episode":
|
||||
case "Series":
|
||||
case "Season":
|
||||
return "tv";
|
||||
case "Movie":
|
||||
return "movie";
|
||||
case "Video":
|
||||
return "video";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// https://dev.emby.media/reference/RestAPI/UserService/getUsersPublic.html
|
||||
private async fetchUsersPublicAsync(): Promise<{ id: string; name: string }[]> {
|
||||
const apiKey = super.getSecretValue("apiKey");
|
||||
|
||||
+20
-20
@@ -15,8 +15,7 @@ import { getLatestRelease } from "../interfaces/releases-providers/releases-prov
|
||||
import type {
|
||||
DetailsProviderResponse,
|
||||
ReleaseProviderResponse,
|
||||
ReleasesRepository,
|
||||
ReleasesResponse,
|
||||
ReleaseResponse,
|
||||
} from "../interfaces/releases-providers/releases-providers-types";
|
||||
|
||||
const localLogger = logger.child({ module: "GitHubContainerRegistryIntegration" });
|
||||
@@ -43,23 +42,24 @@ export class GitHubContainerRegistryIntegration extends Integration implements R
|
||||
};
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(repository: ReleasesRepository): Promise<ReleasesResponse> {
|
||||
const [owner, name] = repository.identifier.split("/");
|
||||
private parseIdentifier(identifier: string) {
|
||||
const [owner, name] = identifier.split("/");
|
||||
if (!owner || !name) {
|
||||
localLogger.warn(
|
||||
`Invalid identifier format. Expected 'owner/name', for ${repository.identifier} with GitHub Container Registry integration`,
|
||||
{
|
||||
identifier: repository.identifier,
|
||||
},
|
||||
`Invalid identifier format. Expected 'owner/name', for ${identifier} with GitHub Container Registry integration`,
|
||||
{ identifier },
|
||||
);
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { code: "invalidIdentifier" },
|
||||
};
|
||||
return null;
|
||||
}
|
||||
return { owner, name };
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(identifier: string, versionRegex?: string): Promise<ReleaseResponse> {
|
||||
const parsedIdentifier = this.parseIdentifier(identifier);
|
||||
if (!parsedIdentifier) return { success: false, error: { code: "invalidIdentifier" } };
|
||||
|
||||
const { owner, name } = parsedIdentifier;
|
||||
const api = this.getApi();
|
||||
const details = await this.getDetailsAsync(api, owner, name);
|
||||
|
||||
try {
|
||||
const releasesResponse = await api.rest.packages.getAllPackageVersionsForPackageOwnedByUser({
|
||||
@@ -83,20 +83,20 @@ export class GitHubContainerRegistryIntegration extends Integration implements R
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return getLatestRelease(releasesProviderResponse, repository, details);
|
||||
const latestRelease = getLatestRelease(releasesProviderResponse, versionRegex);
|
||||
if (!latestRelease) return { success: false, error: { code: "noMatchingVersion" } };
|
||||
|
||||
const details = await this.getDetailsAsync(api, owner, name);
|
||||
|
||||
return { success: true, data: { ...details, ...latestRelease } };
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof RequestError ? error.message : String(error);
|
||||
|
||||
localLogger.warn(`Failed to get releases for ${owner}\\${name} with GitHub Container Registry integration`, {
|
||||
owner,
|
||||
name,
|
||||
error: errorMessage,
|
||||
});
|
||||
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { message: errorMessage },
|
||||
};
|
||||
return { success: false, error: { code: "unexpected", message: errorMessage } };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,7 @@ import { getLatestRelease } from "../interfaces/releases-providers/releases-prov
|
||||
import type {
|
||||
DetailsProviderResponse,
|
||||
ReleaseProviderResponse,
|
||||
ReleasesRepository,
|
||||
ReleasesResponse,
|
||||
ReleaseResponse,
|
||||
} from "../interfaces/releases-providers/releases-providers-types";
|
||||
|
||||
const localLogger = logger.child({ module: "GithubIntegration" });
|
||||
@@ -43,38 +42,32 @@ export class GithubIntegration extends Integration implements ReleasesProviderIn
|
||||
};
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(repository: ReleasesRepository): Promise<ReleasesResponse> {
|
||||
const [owner, name] = repository.identifier.split("/");
|
||||
private parseIdentifier(identifier: string) {
|
||||
const [owner, name] = identifier.split("/");
|
||||
if (!owner || !name) {
|
||||
localLogger.warn(
|
||||
`Invalid identifier format. Expected 'owner/name', for ${repository.identifier} with Github integration`,
|
||||
{
|
||||
identifier: repository.identifier,
|
||||
},
|
||||
);
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { code: "invalidIdentifier" },
|
||||
};
|
||||
localLogger.warn(`Invalid identifier format. Expected 'owner/name', for ${identifier} with Github integration`, {
|
||||
identifier,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
return { owner, name };
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(identifier: string, versionRegex?: string): Promise<ReleaseResponse> {
|
||||
const parsedIdentifier = this.parseIdentifier(identifier);
|
||||
if (!parsedIdentifier) return { success: false, error: { code: "invalidIdentifier" } };
|
||||
|
||||
const { owner, name } = parsedIdentifier;
|
||||
const api = this.getApi();
|
||||
const details = await this.getDetailsAsync(api, owner, name);
|
||||
|
||||
try {
|
||||
const releasesResponse = await api.rest.repos.listReleases({
|
||||
owner,
|
||||
repo: name,
|
||||
});
|
||||
const releasesResponse = await api.rest.repos.listReleases({ owner, repo: name });
|
||||
|
||||
if (releasesResponse.data.length === 0) {
|
||||
localLogger.warn(`No releases found, for ${repository.identifier} with Github integration`, {
|
||||
identifier: repository.identifier,
|
||||
localLogger.warn(`No releases found, for ${owner}/${name} with Github integration`, {
|
||||
identifier: `${owner}/${name}`,
|
||||
});
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { code: "noReleasesFound" },
|
||||
};
|
||||
return { success: false, error: { code: "noMatchingVersion" } };
|
||||
}
|
||||
|
||||
const releasesProviderResponse = releasesResponse.data.reduce<ReleaseProviderResponse[]>((acc, release) => {
|
||||
@@ -90,20 +83,20 @@ export class GithubIntegration extends Integration implements ReleasesProviderIn
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return getLatestRelease(releasesProviderResponse, repository, details);
|
||||
const latestRelease = getLatestRelease(releasesProviderResponse, versionRegex);
|
||||
if (!latestRelease) return { success: false, error: { code: "noMatchingVersion" } };
|
||||
|
||||
const details = await this.getDetailsAsync(api, owner, name);
|
||||
|
||||
return { success: true, data: { ...details, ...latestRelease } };
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof OctokitRequestError ? error.message : String(error);
|
||||
|
||||
localLogger.warn(`Failed to get releases for ${owner}\\${name} with Github integration`, {
|
||||
owner,
|
||||
name,
|
||||
error: errorMessage,
|
||||
});
|
||||
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { message: errorMessage },
|
||||
};
|
||||
return { success: false, error: { code: "unexpected", message: errorMessage } };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,7 @@ import { getLatestRelease } from "../interfaces/releases-providers/releases-prov
|
||||
import type {
|
||||
DetailsProviderResponse,
|
||||
ReleaseProviderResponse,
|
||||
ReleasesRepository,
|
||||
ReleasesResponse,
|
||||
ReleaseResponse,
|
||||
} from "../interfaces/releases-providers/releases-providers-types";
|
||||
|
||||
const localLogger = logger.child({ module: "GitlabIntegration" });
|
||||
@@ -40,25 +39,20 @@ export class GitlabIntegration extends Integration implements ReleasesProviderIn
|
||||
};
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(repository: ReleasesRepository): Promise<ReleasesResponse> {
|
||||
public async getLatestMatchingReleaseAsync(identifier: string, versionRegex?: string): Promise<ReleaseResponse> {
|
||||
const api = this.getApi();
|
||||
|
||||
const details = await this.getDetailsAsync(api, repository.identifier);
|
||||
|
||||
try {
|
||||
const releasesResponse = await api.ProjectReleases.all(repository.identifier, {
|
||||
const releasesResponse = await api.ProjectReleases.all(identifier, {
|
||||
perPage: 100,
|
||||
});
|
||||
|
||||
if (releasesResponse instanceof Error) {
|
||||
localLogger.warn(`Failed to get releases for ${repository.identifier} with Gitlab integration`, {
|
||||
identifier: repository.identifier,
|
||||
localLogger.warn(`Failed to get releases for ${identifier} with Gitlab integration`, {
|
||||
identifier,
|
||||
error: releasesResponse.message,
|
||||
});
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { code: "noReleasesFound" },
|
||||
};
|
||||
return { success: false, error: { code: "noReleasesFound" } };
|
||||
}
|
||||
|
||||
const releasesProviderResponse = releasesResponse.reduce<ReleaseProviderResponse[]>((acc, release) => {
|
||||
@@ -76,17 +70,19 @@ export class GitlabIntegration extends Integration implements ReleasesProviderIn
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return getLatestRelease(releasesProviderResponse, repository, details);
|
||||
} catch (error) {
|
||||
localLogger.warn(`Failed to get releases for ${repository.identifier} with Gitlab integration`, {
|
||||
identifier: repository.identifier,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
const latestRelease = getLatestRelease(releasesProviderResponse, versionRegex);
|
||||
if (!latestRelease) return { success: false, error: { code: "noMatchingVersion" } };
|
||||
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { code: "noReleasesFound" },
|
||||
};
|
||||
const details = await this.getDetailsAsync(api, identifier);
|
||||
|
||||
return { success: true, data: { ...details, ...latestRelease } };
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
localLogger.warn(`Failed to get releases for ${identifier} with Gitlab integration`, {
|
||||
identifier,
|
||||
error: errorMessage,
|
||||
});
|
||||
return { success: false, error: { code: "unexpected", message: errorMessage } };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +108,7 @@ export class GitlabIntegration extends Integration implements ReleasesProviderIn
|
||||
|
||||
return {
|
||||
projectUrl: response.web_url,
|
||||
projectDescription: response.description,
|
||||
projectDescription: response.description ?? undefined,
|
||||
isFork: response.forked_from_project !== null,
|
||||
isArchived: response.archived,
|
||||
createdAt: new Date(response.created_at),
|
||||
|
||||
@@ -47,7 +47,7 @@ export type {
|
||||
TdarrStatistics,
|
||||
TdarrWorker,
|
||||
} from "./interfaces/media-transcoding/media-transcoding-types";
|
||||
export type { ReleasesResponse } from "./interfaces/releases-providers/releases-providers-types";
|
||||
export type { ReleasesRepository, ReleaseResponse } from "./interfaces/releases-providers/releases-providers-types";
|
||||
export type { Notification } from "./interfaces/notifications/notification-types";
|
||||
|
||||
// Schemas
|
||||
|
||||
+8
-34
@@ -1,47 +1,21 @@
|
||||
import type {
|
||||
DetailsProviderResponse,
|
||||
ReleaseProviderResponse,
|
||||
ReleasesRepository,
|
||||
ReleasesResponse,
|
||||
} from "./releases-providers-types";
|
||||
import type { ReleaseProviderResponse, ReleaseResponse } from "./releases-providers-types";
|
||||
|
||||
export const getLatestRelease = (
|
||||
releases: ReleaseProviderResponse[],
|
||||
repository: ReleasesRepository,
|
||||
details?: DetailsProviderResponse,
|
||||
): ReleasesResponse => {
|
||||
versionRegex?: string,
|
||||
): ReleaseProviderResponse | null => {
|
||||
const validReleases = releases.filter((result) => {
|
||||
if (result.latestRelease) {
|
||||
return repository.versionRegex ? new RegExp(repository.versionRegex).test(result.latestRelease) : true;
|
||||
return versionRegex ? new RegExp(versionRegex).test(result.latestRelease) : true;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
const latest =
|
||||
validReleases.length === 0
|
||||
? ({
|
||||
id: repository.id,
|
||||
error: { code: "noMatchingVersion" },
|
||||
} as ReleasesResponse)
|
||||
: validReleases.reduce(
|
||||
(latest, result) => {
|
||||
return {
|
||||
...details,
|
||||
...(result.latestReleaseAt > latest.latestReleaseAt ? result : latest),
|
||||
id: repository.id,
|
||||
};
|
||||
},
|
||||
{
|
||||
id: "",
|
||||
latestRelease: "",
|
||||
latestReleaseAt: new Date(0),
|
||||
},
|
||||
);
|
||||
|
||||
return latest;
|
||||
return validReleases.length === 0
|
||||
? null
|
||||
: validReleases.reduce((latest, current) => (current.latestReleaseAt > latest.latestReleaseAt ? current : latest));
|
||||
};
|
||||
|
||||
export interface ReleasesProviderIntegration {
|
||||
getLatestMatchingReleaseAsync(repository: ReleasesRepository): Promise<ReleasesResponse>;
|
||||
getLatestMatchingReleaseAsync(identifier: string, versionRegex?: string): Promise<ReleaseResponse>;
|
||||
}
|
||||
+10
-29
@@ -1,5 +1,11 @@
|
||||
import type { TranslationObject } from "@homarr/translation";
|
||||
|
||||
export interface ReleasesRepository extends Record<string, unknown> {
|
||||
id: string;
|
||||
identifier: string;
|
||||
versionRegex?: string;
|
||||
}
|
||||
|
||||
export interface DetailsProviderResponse {
|
||||
projectUrl?: string;
|
||||
projectDescription?: string;
|
||||
@@ -19,35 +25,10 @@ export interface ReleaseProviderResponse {
|
||||
isPreRelease?: boolean;
|
||||
}
|
||||
|
||||
export interface ReleasesRepository {
|
||||
id: string;
|
||||
identifier: string;
|
||||
versionRegex?: string;
|
||||
}
|
||||
|
||||
type ReleasesErrorKeys = keyof TranslationObject["widget"]["releases"]["error"]["messages"];
|
||||
|
||||
export interface ReleasesResponse {
|
||||
id: string;
|
||||
latestRelease?: string;
|
||||
latestReleaseAt?: Date;
|
||||
releaseUrl?: string;
|
||||
releaseDescription?: string;
|
||||
isPreRelease?: boolean;
|
||||
projectUrl?: string;
|
||||
projectDescription?: string;
|
||||
isFork?: boolean;
|
||||
isArchived?: boolean;
|
||||
createdAt?: Date;
|
||||
starsCount?: number;
|
||||
openIssues?: number;
|
||||
forksCount?: number;
|
||||
export type ReleaseData = DetailsProviderResponse & ReleaseProviderResponse;
|
||||
|
||||
error?:
|
||||
| {
|
||||
code: ReleasesErrorKeys;
|
||||
}
|
||||
| {
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
export type ReleaseError = { code: ReleasesErrorKeys } | { code: "unexpected"; message: string };
|
||||
|
||||
export type ReleaseResponse = { success: true; data: ReleaseData } | { success: false; error: ReleaseError };
|
||||
@@ -15,7 +15,7 @@ import { Integration } from "../base/integration";
|
||||
import type { TestingResult } from "../base/test-connection/test-connection-service";
|
||||
import type { IMediaServerIntegration } from "../interfaces/media-server/media-server-integration";
|
||||
import type { CurrentSessionsInput, StreamSession } from "../interfaces/media-server/media-server-types";
|
||||
import type { IMediaReleasesIntegration, MediaRelease } from "../types";
|
||||
import type { IMediaReleasesIntegration, MediaRelease, MediaType } from "../types";
|
||||
|
||||
@HandleIntegrationErrors([integrationAxiosHttpErrorHandler])
|
||||
export class JellyfinIntegration extends Integration implements IMediaServerIntegration, IMediaReleasesIntegration {
|
||||
@@ -122,7 +122,7 @@ export class JellyfinIntegration extends Integration implements IMediaServerInte
|
||||
return result.data.map((item) => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
id: item.Id!,
|
||||
type: item.Type === "Movie" ? "movie" : item.Type === "Series" ? "tv" : "unknown",
|
||||
type: this.mapMediaReleaseType(item.Type),
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
title: item.Name!,
|
||||
subtitle: item.Taglines?.at(0),
|
||||
@@ -140,6 +140,27 @@ export class JellyfinIntegration extends Integration implements IMediaServerInte
|
||||
}));
|
||||
}
|
||||
|
||||
private mapMediaReleaseType(type: BaseItemKind | undefined): MediaType {
|
||||
switch (type) {
|
||||
case "Audio":
|
||||
case "AudioBook":
|
||||
case "MusicAlbum":
|
||||
return "music";
|
||||
case "Book":
|
||||
return "book";
|
||||
case "Episode":
|
||||
case "Series":
|
||||
case "Season":
|
||||
return "tv";
|
||||
case "Movie":
|
||||
return "movie";
|
||||
case "Video":
|
||||
return "video";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an ApiClient synchronously with an ApiKey or asynchronously
|
||||
* with a username and password.
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Integration } from "../base/integration";
|
||||
import { TestConnectionError } from "../base/test-connection/test-connection-error";
|
||||
import type { TestingResult } from "../base/test-connection/test-connection-service";
|
||||
import type { ReleasesProviderIntegration } from "../interfaces/releases-providers/releases-providers-integration";
|
||||
import type { ReleasesRepository, ReleasesResponse } from "../interfaces/releases-providers/releases-providers-types";
|
||||
import type { ReleaseResponse } from "../interfaces/releases-providers/releases-providers-types";
|
||||
import { releasesResponseSchema } from "./linuxserverio-schemas";
|
||||
|
||||
const localLogger = logger.child({ module: "LinuxServerIOsIntegration" });
|
||||
@@ -24,56 +24,44 @@ export class LinuxServerIOIntegration extends Integration implements ReleasesPro
|
||||
};
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(repository: ReleasesRepository): Promise<ReleasesResponse> {
|
||||
const [owner, name] = repository.identifier.split("/");
|
||||
private parseIdentifier(identifier: string) {
|
||||
const [owner, name] = identifier.split("/");
|
||||
if (!owner || !name) {
|
||||
localLogger.warn(
|
||||
`Invalid identifier format. Expected 'owner/name', for ${repository.identifier} with LinuxServerIO integration`,
|
||||
{
|
||||
identifier: repository.identifier,
|
||||
},
|
||||
`Invalid identifier format. Expected 'owner/name', for ${identifier} with LinuxServerIO integration`,
|
||||
{ identifier },
|
||||
);
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { code: "invalidIdentifier" },
|
||||
};
|
||||
return null;
|
||||
}
|
||||
return { owner, name };
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(identifier: string): Promise<ReleaseResponse> {
|
||||
const { name } = this.parseIdentifier(identifier) ?? {};
|
||||
if (!name) return { success: false, error: { code: "invalidIdentifier" } };
|
||||
|
||||
const releasesResponse = await fetchWithTrustedCertificatesAsync(this.url("/api/v1/images"));
|
||||
|
||||
if (!releasesResponse.ok) {
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { message: releasesResponse.statusText },
|
||||
};
|
||||
return { success: false, error: { code: "unexpected", message: releasesResponse.statusText } };
|
||||
}
|
||||
|
||||
const releasesResponseJson: unknown = await releasesResponse.json();
|
||||
const { data, success, error } = releasesResponseSchema.safeParse(releasesResponseJson);
|
||||
|
||||
if (!success) {
|
||||
return {
|
||||
id: repository.id,
|
||||
error: {
|
||||
message: error.message,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
const release = data.data.repositories.linuxserver.find((repo) => repo.name === name);
|
||||
if (!release) {
|
||||
localLogger.warn(`Repository ${name} not found on provider, with LinuxServerIO integration`, {
|
||||
owner,
|
||||
name,
|
||||
});
|
||||
return { success: false, error: { code: "unexpected", message: error.message } };
|
||||
}
|
||||
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { code: "noReleasesFound" },
|
||||
};
|
||||
}
|
||||
const release = data.data.repositories.linuxserver.find((repo) => repo.name === name);
|
||||
if (!release) {
|
||||
localLogger.warn(`Repository ${name} not found on provider, with LinuxServerIO integration`, {
|
||||
name,
|
||||
});
|
||||
return { success: false, error: { code: "noMatchingVersion" } };
|
||||
}
|
||||
|
||||
return {
|
||||
id: repository.id,
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
latestRelease: release.version,
|
||||
latestReleaseAt: release.version_timestamp,
|
||||
releaseDescription: release.changelog?.shift()?.desc,
|
||||
@@ -82,7 +70,7 @@ export class LinuxServerIOIntegration extends Integration implements ReleasesPro
|
||||
isArchived: release.deprecated,
|
||||
createdAt: release.initial_date ? new Date(release.initial_date) : undefined,
|
||||
starsCount: release.stars,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import { TestConnectionError } from "../base/test-connection/test-connection-err
|
||||
import type { TestingResult } from "../base/test-connection/test-connection-service";
|
||||
import type { ReleasesProviderIntegration } from "../interfaces/releases-providers/releases-providers-integration";
|
||||
import { getLatestRelease } from "../interfaces/releases-providers/releases-providers-integration";
|
||||
import type { ReleasesRepository, ReleasesResponse } from "../interfaces/releases-providers/releases-providers-types";
|
||||
import type { ReleaseResponse } from "../interfaces/releases-providers/releases-providers-types";
|
||||
import { releasesResponseSchema } from "./npm-schemas";
|
||||
|
||||
export class NPMIntegration extends Integration implements ReleasesProviderIntegration {
|
||||
@@ -22,35 +22,35 @@ export class NPMIntegration extends Integration implements ReleasesProviderInteg
|
||||
};
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(repository: ReleasesRepository): Promise<ReleasesResponse> {
|
||||
const releasesResponse = await fetchWithTrustedCertificatesAsync(
|
||||
this.url(`/${encodeURIComponent(repository.identifier)}`),
|
||||
);
|
||||
public async getLatestMatchingReleaseAsync(identifier: string, versionRegex?: string): Promise<ReleaseResponse> {
|
||||
if (!identifier) return { success: false, error: { code: "invalidIdentifier" } };
|
||||
|
||||
const releasesResponse = await fetchWithTrustedCertificatesAsync(this.url(`/${encodeURIComponent(identifier)}`));
|
||||
if (!releasesResponse.ok) {
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { message: releasesResponse.statusText },
|
||||
};
|
||||
return { success: false, error: { code: "unexpected", message: releasesResponse.statusText } };
|
||||
}
|
||||
|
||||
const releasesResponseJson: unknown = await releasesResponse.json();
|
||||
const { data, success, error } = releasesResponseSchema.safeParse(releasesResponseJson);
|
||||
|
||||
if (!success) {
|
||||
return {
|
||||
id: repository.id,
|
||||
success: false,
|
||||
error: {
|
||||
code: "unexpected",
|
||||
message: releasesResponseJson ? JSON.stringify(releasesResponseJson, null, 2) : error.message,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
const formattedReleases = data.time.map((tag) => ({
|
||||
...tag,
|
||||
releaseUrl: `https://www.npmjs.com/package/${encodeURIComponent(data.name)}/v/${encodeURIComponent(tag.latestRelease)}`,
|
||||
releaseDescription: data.versions[tag.latestRelease]?.description ?? "",
|
||||
}));
|
||||
return getLatestRelease(formattedReleases, repository);
|
||||
}
|
||||
|
||||
const formattedReleases = data.time.map((tag) => ({
|
||||
...tag,
|
||||
releaseUrl: `https://www.npmjs.com/package/${encodeURIComponent(data.name)}/v/${encodeURIComponent(tag.latestRelease)}`,
|
||||
releaseDescription: data.versions[tag.latestRelease]?.description ?? "",
|
||||
}));
|
||||
|
||||
const latestRelease = getLatestRelease(formattedReleases, versionRegex);
|
||||
if (!latestRelease) return { success: false, error: { code: "noMatchingVersion" } };
|
||||
|
||||
return { success: true, data: latestRelease };
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,7 @@ import type { ReleasesProviderIntegration } from "../interfaces/releases-provide
|
||||
import { getLatestRelease } from "../interfaces/releases-providers/releases-providers-integration";
|
||||
import type {
|
||||
ReleaseProviderResponse,
|
||||
ReleasesRepository,
|
||||
ReleasesResponse,
|
||||
ReleaseResponse,
|
||||
} from "../interfaces/releases-providers/releases-providers-types";
|
||||
import { releasesResponseSchema } from "./quay-schemas";
|
||||
|
||||
@@ -43,20 +42,22 @@ export class QuayIntegration extends Integration implements ReleasesProviderInte
|
||||
};
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(repository: ReleasesRepository): Promise<ReleasesResponse> {
|
||||
const [owner, name] = repository.identifier.split("/");
|
||||
private parseIdentifier(identifier: string) {
|
||||
const [owner, name] = identifier.split("/");
|
||||
if (!owner || !name) {
|
||||
localLogger.warn(
|
||||
`Invalid identifier format. Expected 'owner/name', for ${repository.identifier} with LinuxServerIO integration`,
|
||||
{
|
||||
identifier: repository.identifier,
|
||||
},
|
||||
);
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { code: "invalidIdentifier" },
|
||||
};
|
||||
localLogger.warn(`Invalid identifier format. Expected 'owner/name', for ${identifier} with Quay integration`, {
|
||||
identifier,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
return { owner, name };
|
||||
}
|
||||
|
||||
public async getLatestMatchingReleaseAsync(identifier: string, versionRegex?: string): Promise<ReleaseResponse> {
|
||||
const parsedIdentifier = this.parseIdentifier(identifier);
|
||||
if (!parsedIdentifier) return { success: false, error: { code: "invalidIdentifier" } };
|
||||
|
||||
const { owner, name } = parsedIdentifier;
|
||||
|
||||
const releasesResponse = await this.withHeadersAsync(async (headers) => {
|
||||
return await fetchWithTrustedCertificatesAsync(
|
||||
@@ -68,42 +69,29 @@ export class QuayIntegration extends Integration implements ReleasesProviderInte
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
if (!releasesResponse.ok) {
|
||||
return {
|
||||
id: repository.id,
|
||||
error: { message: releasesResponse.statusText },
|
||||
};
|
||||
return { success: false, error: { code: "unexpected", message: releasesResponse.statusText } };
|
||||
}
|
||||
|
||||
const releasesResponseJson: unknown = await releasesResponse.json();
|
||||
const { data, success, error } = releasesResponseSchema.safeParse(releasesResponseJson);
|
||||
|
||||
if (!success) {
|
||||
return {
|
||||
id: repository.id,
|
||||
error: {
|
||||
message: error.message,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
const details = {
|
||||
projectDescription: data.description,
|
||||
};
|
||||
|
||||
const releasesProviderResponse = Object.entries(data.tags).reduce<ReleaseProviderResponse[]>((acc, [_, tag]) => {
|
||||
if (!tag.name || !tag.last_modified) return acc;
|
||||
|
||||
acc.push({
|
||||
latestRelease: tag.name,
|
||||
latestReleaseAt: new Date(tag.last_modified),
|
||||
releaseUrl: `https://quay.io/repository/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/tag/${encodeURIComponent(tag.name)}`,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
return getLatestRelease(releasesProviderResponse, repository, details);
|
||||
return { success: false, error: { code: "unexpected", message: error.message } };
|
||||
}
|
||||
|
||||
const releasesProviderResponse = Object.entries(data.tags).reduce<ReleaseProviderResponse[]>((acc, [_, tag]) => {
|
||||
if (!tag.name || !tag.last_modified) return acc;
|
||||
acc.push({
|
||||
latestRelease: tag.name,
|
||||
latestReleaseAt: new Date(tag.last_modified),
|
||||
releaseUrl: `https://quay.io/repository/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/tag/${encodeURIComponent(tag.name)}`,
|
||||
});
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const latestRelease = getLatestRelease(releasesProviderResponse, versionRegex);
|
||||
if (!latestRelease) return { success: false, error: { code: "noMatchingVersion" } };
|
||||
|
||||
return { success: true, data: { projectDescription: data.description, ...latestRelease } };
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/core": "workspace:^0.1.0",
|
||||
"superjson": "2.2.3",
|
||||
"superjson": "2.2.5",
|
||||
"winston": "3.18.3",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
@@ -33,7 +33,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -33,10 +33,10 @@
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.5",
|
||||
"@mantine/core": "^8.3.6",
|
||||
"@tabler/icons-react": "^3.35.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"next": "15.5.6",
|
||||
"dayjs": "^1.11.19",
|
||||
"next": "16.0.1",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"zod": "^4.1.12"
|
||||
@@ -45,7 +45,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { Button, CopyButton, Mark, Stack, Text } from "@mantine/core";
|
||||
|
||||
import type { RouterOutputs } from "@homarr/api";
|
||||
import { createModal } from "@homarr/modals";
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
export const InviteCopyModal = createModal<RouterOutputs["invite"]["createInvite"]>(({ actions, innerProps }) => {
|
||||
const t = useScopedI18n("management.page.user.invite");
|
||||
|
||||
@@ -24,15 +24,15 @@
|
||||
"dependencies": {
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.5",
|
||||
"@mantine/hooks": "^8.3.5",
|
||||
"@mantine/core": "^8.3.6",
|
||||
"@mantine/hooks": "^8.3.6",
|
||||
"react": "19.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -24,14 +24,14 @@
|
||||
"prettier": "@homarr/prettier-config",
|
||||
"dependencies": {
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@mantine/notifications": "^8.3.5",
|
||||
"@mantine/notifications": "^8.3.6",
|
||||
"@tabler/icons-react": "^3.35.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -37,13 +37,13 @@
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.5",
|
||||
"@mantine/hooks": "^8.3.5",
|
||||
"@mantine/core": "^8.3.6",
|
||||
"@mantine/hooks": "^8.3.6",
|
||||
"adm-zip": "0.5.16",
|
||||
"next": "15.5.6",
|
||||
"next": "16.0.1",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"superjson": "2.2.3",
|
||||
"superjson": "2.2.5",
|
||||
"zod": "^4.1.12",
|
||||
"zod-form-data": "^3.0.1"
|
||||
},
|
||||
@@ -52,7 +52,7 @@
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/adm-zip": "0.5.7",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -28,13 +28,13 @@
|
||||
"@homarr/definitions": "workspace:^",
|
||||
"@homarr/log": "workspace:^",
|
||||
"ioredis": "5.8.2",
|
||||
"superjson": "2.2.3"
|
||||
"superjson": "2.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,9 @@
|
||||
"@homarr/integrations": "workspace:^0.1.0",
|
||||
"@homarr/log": "workspace:^0.1.0",
|
||||
"@homarr/redis": "workspace:^0.1.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"octokit": "^5.0.4",
|
||||
"superjson": "2.2.3",
|
||||
"dayjs": "^1.11.19",
|
||||
"octokit": "^5.0.5",
|
||||
"superjson": "2.2.5",
|
||||
"undici": "7.16.0",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
@@ -39,7 +39,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,19 @@
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import type { IntegrationKindByCategory } from "@homarr/definitions";
|
||||
import { getIconUrl } from "@homarr/definitions";
|
||||
import type { ReleasesResponse } from "@homarr/integrations";
|
||||
import type { ReleaseResponse, ReleasesRepository } from "@homarr/integrations";
|
||||
import { createIntegrationAsync } from "@homarr/integrations";
|
||||
|
||||
import { createCachedIntegrationRequestHandler } from "./lib/cached-integration-request-handler";
|
||||
|
||||
export const releasesRequestHandler = createCachedIntegrationRequestHandler<
|
||||
ReleasesResponse,
|
||||
ReleaseResponse,
|
||||
IntegrationKindByCategory<"releasesProvider">,
|
||||
{
|
||||
id: string;
|
||||
identifier: string;
|
||||
versionRegex?: string;
|
||||
}
|
||||
ReleasesRepository
|
||||
>({
|
||||
async requestAsync(integration, input) {
|
||||
const integrationInstance = await createIntegrationAsync(integration);
|
||||
const response = await integrationInstance.getLatestMatchingReleaseAsync({
|
||||
id: input.id,
|
||||
identifier: input.identifier,
|
||||
versionRegex: input.versionRegex,
|
||||
});
|
||||
|
||||
return {
|
||||
...response,
|
||||
integration: {
|
||||
name: integration.name,
|
||||
iconUrl: getIconUrl(integration.kind),
|
||||
},
|
||||
};
|
||||
requestAsync: async (integration, input) => {
|
||||
const instance = await createIntegrationAsync(integration);
|
||||
return instance.getLatestMatchingReleaseAsync(input.identifier, input.versionRegex);
|
||||
},
|
||||
cacheDuration: dayjs.duration(5, "minutes"),
|
||||
queryKey: "repositoriesReleases",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,8 @@
|
||||
"@homarr/api": "workspace:^0.1.0",
|
||||
"@homarr/db": "workspace:^0.1.0",
|
||||
"@homarr/server-settings": "workspace:^0.1.0",
|
||||
"@mantine/dates": "^8.3.5",
|
||||
"next": "15.5.6",
|
||||
"@mantine/dates": "^8.3.6",
|
||||
"next": "16.0.1",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0"
|
||||
},
|
||||
@@ -35,7 +35,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -33,12 +33,12 @@
|
||||
"@homarr/settings": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/ui": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.5",
|
||||
"@mantine/hooks": "^8.3.5",
|
||||
"@mantine/spotlight": "^8.3.5",
|
||||
"@mantine/core": "^8.3.6",
|
||||
"@mantine/hooks": "^8.3.6",
|
||||
"@mantine/spotlight": "^8.3.6",
|
||||
"@tabler/icons-react": "^3.35.0",
|
||||
"jotai": "^2.15.0",
|
||||
"next": "15.5.6",
|
||||
"jotai": "^2.15.1",
|
||||
"next": "16.0.1",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"use-deep-compare-effect": "^1.8.1"
|
||||
@@ -47,7 +47,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import Link from "next/link";
|
||||
import { Spotlight } from "@mantine/spotlight";
|
||||
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import type { inferSearchInteractionOptions } from "../../../lib/interaction";
|
||||
import classes from "./action-item.module.css";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Link from "next/link";
|
||||
import { Spotlight } from "@mantine/spotlight";
|
||||
|
||||
import type { TranslationObject } from "@homarr/translation";
|
||||
import { Link } from "@homarr/ui";
|
||||
|
||||
import type { SearchGroup } from "../../../lib/group";
|
||||
import type { inferSearchInteractionOptions } from "../../../lib/interaction";
|
||||
|
||||
@@ -29,10 +29,10 @@
|
||||
"dependencies": {
|
||||
"@homarr/common": "workspace:^0.1.0",
|
||||
"@homarr/definitions": "workspace:^0.1.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"dayjs": "^1.11.19",
|
||||
"deepmerge": "4.3.1",
|
||||
"mantine-react-table": "2.0.0-beta.9",
|
||||
"next": "15.5.6",
|
||||
"next": "16.0.1",
|
||||
"next-intl": "4.4.0",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0"
|
||||
@@ -41,7 +41,7 @@
|
||||
"@homarr/eslint-config": "workspace:^0.2.0",
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -30,12 +30,12 @@
|
||||
"@homarr/log": "workspace:^0.1.0",
|
||||
"@homarr/translation": "workspace:^0.1.0",
|
||||
"@homarr/validation": "workspace:^0.1.0",
|
||||
"@mantine/core": "^8.3.5",
|
||||
"@mantine/dates": "^8.3.5",
|
||||
"@mantine/hooks": "^8.3.5",
|
||||
"@mantine/core": "^8.3.6",
|
||||
"@mantine/dates": "^8.3.6",
|
||||
"@mantine/hooks": "^8.3.6",
|
||||
"@tabler/icons-react": "^3.35.0",
|
||||
"mantine-react-table": "2.0.0-beta.9",
|
||||
"next": "15.5.6",
|
||||
"next": "16.0.1",
|
||||
"react": "19.2.0",
|
||||
"react-dom": "19.2.0",
|
||||
"svgson": "^5.3.1"
|
||||
@@ -45,7 +45,7 @@
|
||||
"@homarr/prettier-config": "workspace:^0.1.0",
|
||||
"@homarr/tsconfig": "workspace:^0.1.0",
|
||||
"@types/css-modules": "^1.0.5",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint": "^9.39.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -15,3 +15,4 @@ export { BetaBadge } from "./beta-badge";
|
||||
export { MaskedImage } from "./masked-image";
|
||||
export { MaskedOrNormalImage } from "./masked-or-normal-image";
|
||||
export { LanguageIcon } from "./language-icon";
|
||||
export { Link } from "./link";
|
||||
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user