Files
sass-mohfarawati/lib/media.ts
T
MOH 0f9f9e5f79
CI / quality (push) Waiting to run
Add production-ready media library
2026-03-07 16:20:11 +01:00

204 lines
4.5 KiB
TypeScript

import type {
MediaAsset,
MediaKind,
MediaSource,
MediaUsage,
MediaUsageType,
} from "@prisma/client";
import { prisma } from "@/lib/prisma";
export type MediaAssetView = Pick<
MediaAsset,
"id" | "source" | "kind" | "url" | "fileName" | "label" | "altText" | "mimeType" | "size" | "createdAt"
> & {
usages: Array<
Pick<MediaUsage, "id" | "usageType" | "entityType" | "entityId" | "fieldKey">
>;
};
export type MediaOption = Pick<MediaAssetView, "id" | "kind" | "url" | "label" | "source">;
export type PortfolioMediaBindings = {
coverAssetId: string | null;
sectionAssetIds: Record<string, string>;
assetIds: Record<string, string>;
};
function mapMediaAsset(
asset: MediaAsset & {
usages: MediaUsage[];
},
): MediaAssetView {
return {
id: asset.id,
source: asset.source,
kind: asset.kind,
url: asset.url,
fileName: asset.fileName,
label: asset.label,
altText: asset.altText,
mimeType: asset.mimeType,
size: asset.size,
createdAt: asset.createdAt,
usages: asset.usages.map((usage) => ({
id: usage.id,
usageType: usage.usageType,
entityType: usage.entityType,
entityId: usage.entityId,
fieldKey: usage.fieldKey,
})),
};
}
export async function getAdminMediaAssets() {
const assets = await prisma.mediaAsset.findMany({
include: {
usages: {
orderBy: [{ createdAt: "desc" }],
},
},
orderBy: [{ createdAt: "desc" }],
});
return assets.map(mapMediaAsset);
}
export async function getMediaOptions(filters?: { kind?: MediaKind }) {
const assets = await prisma.mediaAsset.findMany({
where: filters?.kind ? { kind: filters.kind } : undefined,
orderBy: [{ createdAt: "desc" }],
select: {
id: true,
kind: true,
url: true,
label: true,
source: true,
},
});
return assets;
}
export async function getMediaAssetById(id: string) {
const asset = await prisma.mediaAsset.findUnique({
where: { id },
include: {
usages: true,
},
});
return asset ? mapMediaAsset(asset) : null;
}
export async function createMediaAsset(input: {
source: MediaSource;
kind: MediaKind;
url: string;
fileName: string;
label: string;
altText?: string | null;
mimeType?: string | null;
size?: number | null;
}) {
return prisma.mediaAsset.create({
data: {
source: input.source,
kind: input.kind,
url: input.url,
fileName: input.fileName,
label: input.label,
altText: input.altText ?? null,
mimeType: input.mimeType ?? null,
size: input.size ?? null,
},
});
}
export async function replaceEntityMediaUsages(input: {
entityType: string;
entityId: string;
usages: Array<{
assetId: string;
usageType: MediaUsageType;
fieldKey: string;
}>;
}) {
await prisma.$transaction(async (tx) => {
await tx.mediaUsage.deleteMany({
where: {
entityType: input.entityType,
entityId: input.entityId,
},
});
if (input.usages.length === 0) {
return;
}
await tx.mediaUsage.createMany({
data: input.usages.map((usage) => ({
assetId: usage.assetId,
usageType: usage.usageType,
entityType: input.entityType,
entityId: input.entityId,
fieldKey: usage.fieldKey,
})),
});
});
}
export async function deleteEntityMediaUsages(entityType: string, entityId: string) {
await prisma.mediaUsage.deleteMany({
where: {
entityType,
entityId,
},
});
}
export async function getPortfolioMediaBindings(projectId: string): Promise<PortfolioMediaBindings> {
const usages = await prisma.mediaUsage.findMany({
where: {
entityType: "portfolio-project",
entityId: projectId,
},
select: {
assetId: true,
usageType: true,
fieldKey: true,
},
});
return usages.reduce<PortfolioMediaBindings>(
(result, usage) => {
if (usage.usageType === "PORTFOLIO_COVER") {
result.coverAssetId = usage.assetId;
}
if (usage.usageType === "PORTFOLIO_SECTION") {
result.sectionAssetIds[usage.fieldKey] = usage.assetId;
}
if (usage.usageType === "PORTFOLIO_ASSET") {
result.assetIds[usage.fieldKey] = usage.assetId;
}
return result;
},
{
coverAssetId: null,
sectionAssetIds: {},
assetIds: {},
},
);
}
export async function countMediaUsageReferences(assetId: string) {
return prisma.mediaUsage.count({
where: {
assetId,
},
});
}