refactor(icons): move walkxcode/dashboard-icons to homarr-labs/dashboard-icons (#1883)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
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;
|
||||
|
||||
@@ -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<string, integrationDefinition>;
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user