fix(icons): local icon repository does not work with medias (#1765)

This commit is contained in:
Meier Lukas
2024-12-24 14:15:18 +01:00
committed by GitHub
parent f127c9325c
commit e220087e96
6 changed files with 49 additions and 13 deletions

View File

@@ -27,6 +27,7 @@
"@homarr/cron-jobs": "workspace:^0.1.0", "@homarr/cron-jobs": "workspace:^0.1.0",
"@homarr/db": "workspace:^0.1.0", "@homarr/db": "workspace:^0.1.0",
"@homarr/definitions": "workspace:^0.1.0", "@homarr/definitions": "workspace:^0.1.0",
"@homarr/icons": "workspace:^0.1.0",
"@homarr/integrations": "workspace:^0.1.0", "@homarr/integrations": "workspace:^0.1.0",
"@homarr/log": "workspace:^", "@homarr/log": "workspace:^",
"@homarr/old-import": "workspace:^0.1.0", "@homarr/old-import": "workspace:^0.1.0",

View File

@@ -1,7 +1,9 @@
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import type { InferInsertModel } from "@homarr/db";
import { and, createId, desc, eq, like } from "@homarr/db"; import { and, createId, desc, eq, like } from "@homarr/db";
import { medias } from "@homarr/db/schema"; import { iconRepositories, icons, medias } from "@homarr/db/schema";
import { createLocalImageUrl, LOCAL_ICON_REPOSITORY_SLUG, mapMediaToIcon } from "@homarr/icons/local";
import { validation, z } from "@homarr/validation"; import { validation, z } from "@homarr/validation";
import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure } from "../../trpc"; import { createTRPCRouter, permissionRequiredProcedure, protectedProcedure } from "../../trpc";
@@ -52,13 +54,29 @@ export const mediaRouter = createTRPCRouter({
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const content = Buffer.from(await input.file.arrayBuffer()); const content = Buffer.from(await input.file.arrayBuffer());
const id = createId(); const id = createId();
await ctx.db.insert(medias).values({ const media = {
id, id,
creatorId: ctx.session.user.id, creatorId: ctx.session.user.id,
content, content,
size: input.file.size, size: input.file.size,
contentType: input.file.type, contentType: input.file.type,
name: input.file.name, name: input.file.name,
} satisfies InferInsertModel<typeof medias>;
await ctx.db.insert(medias).values(media);
const localIconRepository = await ctx.db.query.iconRepositories.findFirst({
where: eq(iconRepositories.slug, LOCAL_ICON_REPOSITORY_SLUG),
});
if (!localIconRepository) return id;
const icon = mapMediaToIcon(media);
await ctx.db.insert(icons).values({
id: createId(),
checksum: icon.checksum,
name: icon.fileNameWithExtension,
url: icon.imageUrl,
iconRepositoryId: localIconRepository.id,
}); });
return id; return id;
@@ -67,6 +85,7 @@ export const mediaRouter = createTRPCRouter({
const dbMedia = await ctx.db.query.medias.findFirst({ const dbMedia = await ctx.db.query.medias.findFirst({
where: eq(medias.id, input.id), where: eq(medias.id, input.id),
columns: { columns: {
id: true,
creatorId: true, creatorId: true,
}, },
}); });
@@ -87,5 +106,6 @@ export const mediaRouter = createTRPCRouter({
} }
await ctx.db.delete(medias).where(eq(medias.id, input.id)); await ctx.db.delete(medias).where(eq(medias.id, input.id));
await ctx.db.delete(icons).where(eq(icons.url, createLocalImageUrl(input.id)));
}), }),
}); });

View File

@@ -5,7 +5,8 @@
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
"exports": { "exports": {
".": "./index.ts" ".": "./index.ts",
"./local": "./src/local.ts"
}, },
"typesVersions": { "typesVersions": {
"*": { "*": {

View File

@@ -0,0 +1 @@
export { createLocalImageUrl, mapMediaToIcon, LOCAL_ICON_REPOSITORY_SLUG } from "./repositories/local.icon-repository";

View File

@@ -1,26 +1,36 @@
import { createHash } from "crypto"; import { createHash } from "crypto";
import type { InferSelectModel } from "@homarr/db";
import { db } from "@homarr/db"; import { db } from "@homarr/db";
import type { medias } from "@homarr/db/schema";
import type { RepositoryIconGroup } from "../types"; import type { RepositoryIcon, RepositoryIconGroup } from "../types";
import { IconRepository } from "./icon-repository"; import { IconRepository } from "./icon-repository";
export const LOCAL_ICON_REPOSITORY_SLUG = "local";
export class LocalIconRepository extends IconRepository { export class LocalIconRepository extends IconRepository {
constructor() { constructor() {
super("Local", "local", undefined, undefined, undefined, undefined); super("Local", LOCAL_ICON_REPOSITORY_SLUG, undefined, undefined, undefined, undefined);
} }
protected async getAllIconsInternalAsync(): Promise<RepositoryIconGroup> { protected async getAllIconsInternalAsync(): Promise<RepositoryIconGroup> {
const medias = await db.query.medias.findMany(); const medias = await db.query.medias.findMany();
return { return {
success: true, success: true,
icons: medias.map((media) => ({ icons: medias.map(mapMediaToIcon),
local: true, slug: LOCAL_ICON_REPOSITORY_SLUG,
fileNameWithExtension: media.name,
imageUrl: `/api/user-medias/${media.id}`,
checksum: createHash("md5").update(media.content).digest("hex"),
sizeInBytes: media.size,
})),
slug: "local",
}; };
} }
} }
export const createLocalImageUrl = (id: string) => `/api/user-medias/${id}`;
export const mapMediaToIcon = (
media: Pick<InferSelectModel<typeof medias>, "name" | "id" | "content" | "size">,
): RepositoryIcon => ({
local: true,
fileNameWithExtension: media.name,
imageUrl: createLocalImageUrl(media.id),
checksum: createHash("md5").update(media.content).digest("hex"),
sizeInBytes: media.size,
});

3
pnpm-lock.yaml generated
View File

@@ -509,6 +509,9 @@ importers:
'@homarr/definitions': '@homarr/definitions':
specifier: workspace:^0.1.0 specifier: workspace:^0.1.0
version: link:../definitions version: link:../definitions
'@homarr/icons':
specifier: workspace:^0.1.0
version: link:../icons
'@homarr/integrations': '@homarr/integrations':
specifier: workspace:^0.1.0 specifier: workspace:^0.1.0
version: link:../integrations version: link:../integrations