diff --git a/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx b/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx index 575c5afe2..b9aec4e3a 100644 --- a/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx +++ b/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx @@ -5,29 +5,29 @@ import { splitToNChunks } from "@homarr/common"; import classes from "./hero-banner.module.css"; const icons = [ - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/homarr.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/sabnzbd.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/deluge.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/radarr.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/sonarr.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/lidarr.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homarr.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/sabnzbd.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/deluge.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/radarr.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/sonarr.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/lidarr.svg", "https://cdn.jsdelivr.net/gh/loganmarchione/homelab-svg-assets/assets/pihole.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/dashdot.png", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/overseerr.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/plex.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/jellyfin.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/dashdot.png", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/overseerr.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/plex.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/jellyfin.svg", "https://cdn.jsdelivr.net/gh/loganmarchione/homelab-svg-assets/assets/homeassistant.svg", "https://cdn.jsdelivr.net/gh/loganmarchione/homelab-svg-assets/assets/freshrss.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/readarr.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/transmission.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/qbittorrent.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/nzbget.png", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/openmediavault.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/readarr.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/transmission.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/qbittorrent.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/nzbget.png", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/openmediavault.svg", "https://cdn.jsdelivr.net/gh/loganmarchione/homelab-svg-assets/assets/docker.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/jellyseerr.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/jellyseerr.svg", "https://cdn.jsdelivr.net/gh/loganmarchione/homelab-svg-assets/assets/adguardhome.svg", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/tdarr.png", - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/prowlarr.svg", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/tdarr.png", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/prowlarr.svg", ]; const countIconGroups = 3; diff --git a/packages/api/src/router/app.ts b/packages/api/src/router/app.ts index 2d1a70ed1..43cc24031 100644 --- a/packages/api/src/router/app.ts +++ b/packages/api/src/router/app.ts @@ -10,7 +10,7 @@ import { convertIntersectionToZodObject } from "../schema-merger"; import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure, publicProcedure } from "../trpc"; import { canUserSeeAppAsync } from "./app/app-access-control"; -const defaultIcon = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/svg/homarr.svg"; +const defaultIcon = "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/svg/homarr.svg"; export const appRouter = createTRPCRouter({ getPaginated: protectedProcedure diff --git a/packages/api/src/router/test/integration/integration-router.spec.ts b/packages/api/src/router/test/integration/integration-router.spec.ts index 3a3efb1ed..6b8b48cc8 100644 --- a/packages/api/src/router/test/integration/integration-router.spec.ts +++ b/packages/api/src/router/test/integration/integration-router.spec.ts @@ -240,7 +240,7 @@ describe("create should create a new integration", () => { expect(dbSearchEngine!.short).toBe("j"); expect(dbSearchEngine!.name).toBe(input.name); expect(dbSearchEngine!.iconUrl).toBe( - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyseerr.png", + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/jellyseerr.png", ); }); diff --git a/packages/cron-jobs/src/jobs/icons-updater.ts b/packages/cron-jobs/src/jobs/icons-updater.ts index 48fa0c59c..53b948d69 100644 --- a/packages/cron-jobs/src/jobs/icons-updater.ts +++ b/packages/cron-jobs/src/jobs/icons-updater.ts @@ -1,7 +1,7 @@ import { splitToNChunks, Stopwatch } from "@homarr/common"; import { EVERY_WEEK } from "@homarr/cron-jobs-core/expressions"; import type { InferInsertModel } from "@homarr/db"; -import { db, inArray } from "@homarr/db"; +import { db, inArray, sql } from "@homarr/db"; import { createId } from "@homarr/db/client"; import { iconRepositories, icons } from "@homarr/db/schema"; import { fetchIconsAsync } from "@homarr/icons"; @@ -22,13 +22,13 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, { `Successfully fetched ${countIcons} icons from ${repositoryIconGroups.length} repositories within ${stopWatch.getElapsedInHumanWords()}`, ); - const databaseIconGroups = await db.query.iconRepositories.findMany({ + const databaseIconRepositories = await db.query.iconRepositories.findMany({ with: { icons: true, }, }); - const skippedChecksums: string[] = []; + const skippedChecksums: `${string}.${string}`[] = []; let countDeleted = 0; let countInserted = 0; @@ -43,18 +43,24 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, { continue; } - const repositoryInDb = databaseIconGroups.find((dbIconGroup) => dbIconGroup.slug === repositoryIconGroup.slug); - const repositoryIconGroupId: string = repositoryInDb?.id ?? createId(); + const repositoryInDb = databaseIconRepositories.find( + (dbIconGroup) => dbIconGroup.slug === repositoryIconGroup.slug, + ); + const iconRepositoryId: string = repositoryInDb?.id ?? createId(); if (!repositoryInDb?.id) { newIconRepositories.push({ - id: repositoryIconGroupId, + id: iconRepositoryId, slug: repositoryIconGroup.slug, }); } for (const icon of repositoryIconGroup.icons) { - if (databaseIconGroups.flatMap((group) => group.icons).some((dbIcon) => dbIcon.checksum === icon.checksum)) { - skippedChecksums.push(icon.checksum); + if ( + databaseIconRepositories + .flatMap((repository) => repository.icons) + .some((dbIcon) => dbIcon.checksum === icon.checksum && dbIcon.iconRepositoryId === iconRepositoryId) + ) { + skippedChecksums.push(`${iconRepositoryId}.${icon.checksum}`); continue; } @@ -63,34 +69,54 @@ export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, { checksum: icon.checksum, name: icon.fileNameWithExtension, url: icon.imageUrl, - iconRepositoryId: repositoryIconGroupId, + iconRepositoryId: iconRepositoryId, }); countInserted++; } } - const deadIcons = databaseIconGroups - .flatMap((group) => group.icons) - .filter((icon) => !skippedChecksums.includes(icon.checksum)); + const deadIcons = databaseIconRepositories + .flatMap((repository) => repository.icons) + .filter((icon) => !skippedChecksums.includes(`${icon.iconRepositoryId}.${icon.checksum}`)); - await db.transaction(async (transaction) => { + const deadIconRepositories = databaseIconRepositories.filter( + (iconRepository) => !repositoryIconGroups.some((group) => group.slug === iconRepository.slug), + ); + + db.transaction((transaction) => { if (newIconRepositories.length >= 1) { - await transaction.insert(iconRepositories).values(newIconRepositories); + transaction.insert(iconRepositories).values(newIconRepositories).run(); } if (newIcons.length >= 1) { // We only insert 5000 icons at a time to avoid SQLite limitations for (const chunck of splitToNChunks(newIcons, Math.ceil(newIcons.length / 5000))) { - await transaction.insert(icons).values(chunck); + transaction.insert(icons).values(chunck).run(); } } if (deadIcons.length >= 1) { - await transaction.delete(icons).where( - inArray( - icons.checksum, - deadIcons.map((icon) => icon.checksum), - ), - ); + transaction + .delete(icons) + .where( + inArray( + // Combine iconRepositoryId and checksum to allow same icons on different repositories + sql`concat(${icons.iconRepositoryId}, '.', ${icons.checksum})`, + deadIcons.map((icon) => `${icon.iconRepositoryId}.${icon.checksum}`), + ), + ) + .run(); + } + + if (deadIconRepositories.length >= 1) { + transaction + .delete(iconRepositories) + .where( + inArray( + iconRepositories.id, + deadIconRepositories.map((iconRepository) => iconRepository.id), + ), + ) + .run(); } countDeleted += deadIcons.length; diff --git a/packages/definitions/src/integration.ts b/packages/definitions/src/integration.ts index 1ace6b955..82f6e2080 100644 --- a/packages/definitions/src/integration.ts +++ b/packages/definitions/src/integration.ts @@ -20,122 +20,122 @@ export const integrationDefs = { sabNzbd: { name: "SABnzbd", secretKinds: [["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sabnzbd.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/sabnzbd.png", category: ["downloadClient", "usenet"], }, nzbGet: { name: "NZBGet", secretKinds: [["username", "password"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/nzbget.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/nzbget.png", category: ["downloadClient", "usenet"], }, deluge: { name: "Deluge", secretKinds: [["password"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/deluge.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/deluge.png", category: ["downloadClient", "torrent"], }, transmission: { name: "Transmission", secretKinds: [["username", "password"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/transmission.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/transmission.png", category: ["downloadClient", "torrent"], }, qBittorrent: { name: "qBittorrent", secretKinds: [["username", "password"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/qbittorrent.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/qbittorrent.png", category: ["downloadClient", "torrent"], }, sonarr: { name: "Sonarr", secretKinds: [["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/sonarr.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/sonarr.png", category: ["calendar"], }, radarr: { name: "Radarr", secretKinds: [["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/radarr.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/radarr.png", category: ["calendar"], }, lidarr: { name: "Lidarr", secretKinds: [["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/lidarr.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/lidarr.png", category: ["calendar"], }, readarr: { name: "Readarr", secretKinds: [["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/readarr.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/readarr.png", category: ["calendar"], }, prowlarr: { name: "Prowlarr", secretKinds: [["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/prowlarr.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/prowlarr.png", category: ["indexerManager"], }, jellyfin: { name: "Jellyfin", secretKinds: [["username", "password"], ["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyfin.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/jellyfin.png", category: ["mediaService"], }, plex: { name: "Plex", secretKinds: [["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/plex.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/plex.png", category: ["mediaService"], }, jellyseerr: { name: "Jellyseerr", secretKinds: [["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/jellyseerr.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/jellyseerr.png", category: ["mediaSearch", "mediaRequest", "search"], }, overseerr: { name: "Overseerr", secretKinds: [["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/overseerr.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/overseerr.png", category: ["mediaSearch", "mediaRequest", "search"], }, piHole: { name: "Pi-hole", secretKinds: [["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/pi-hole.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/pi-hole.png", category: ["dnsHole"], }, adGuardHome: { name: "AdGuard Home", secretKinds: [["username", "password"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/adguard-home.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/adguard-home.png", category: ["dnsHole"], }, homeAssistant: { name: "Home Assistant", secretKinds: [["apiKey"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/home-assistant.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/home-assistant.png", category: ["smartHomeServer"], }, openmediavault: { name: "OpenMediaVault", secretKinds: [["username", "password"]], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/openmediavault.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/openmediavault.png", category: ["healthMonitoring"], }, dashDot: { name: "Dash.", secretKinds: [[]], category: ["healthMonitoring"], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/dashdot.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/dashdot.png", }, tdarr: { name: "Tdarr", secretKinds: [[]], category: ["mediaTranscoding"], - iconUrl: "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/tdarr.png", + iconUrl: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons@master/png/tdarr.png", }, } as const satisfies Record; diff --git a/packages/icons/src/icons-fetcher.ts b/packages/icons/src/icons-fetcher.ts index d1be6a75a..b9ab3c450 100644 --- a/packages/icons/src/icons-fetcher.ts +++ b/packages/icons/src/icons-fetcher.ts @@ -5,12 +5,12 @@ import type { RepositoryIconGroup } from "./types"; const repositories = [ new GitHubIconRepository( - "Walkxcode", - "walkxcode/dashboard-icons", + "Dashboard Icons", + "homarr-labs/dashboard-icons", undefined, - new URL("https://github.com/walkxcode/dashboard-icons"), - new URL("https://api.github.com/repos/walkxcode/dashboard-icons/git/trees/main?recursive=true"), - "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/{0}", + new URL("https://github.com/homarr-labs/dashboard-icons"), + new URL("https://api.github.com/repos/homarr-labs/dashboard-icons/git/trees/main?recursive=true"), + "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/{0}", ), new GitHubIconRepository( "selfh.st",