@@ -0,0 +1,200 @@
|
||||
import path from "path";
|
||||
|
||||
import { MediaKind, MediaSource } from "@prisma/client";
|
||||
|
||||
import { createMediaAsset, getMediaAssetById } from "@/lib/media";
|
||||
import { getExtensionForMimeType, removeManagedMediaFile, saveMediaUpload } from "@/lib/media-storage";
|
||||
import type { MediaFieldInput } from "@/lib/media-validation";
|
||||
|
||||
function getFileNameFromUrl(url: string) {
|
||||
const pathname = new URL(url, "https://placeholder.local").pathname;
|
||||
const fileName = path.basename(pathname);
|
||||
|
||||
return fileName && fileName !== "/" ? fileName : "external-file";
|
||||
}
|
||||
|
||||
export async function resolveMediaSelection(input: {
|
||||
media: MediaFieldInput | undefined;
|
||||
uploadFile: FormDataEntryValue | null;
|
||||
folder: string;
|
||||
fallbackLabel: string;
|
||||
required: boolean;
|
||||
}) {
|
||||
const media = input.media;
|
||||
|
||||
if (!media) {
|
||||
if (input.required) {
|
||||
throw new Error("Media configuration is missing.");
|
||||
}
|
||||
|
||||
return {
|
||||
assetId: null,
|
||||
url: "",
|
||||
uploadedUrl: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (media.mode === "library") {
|
||||
const asset = media.assetId ? await getMediaAssetById(media.assetId) : null;
|
||||
|
||||
if (!asset) {
|
||||
throw new Error("Selected media asset was not found.");
|
||||
}
|
||||
|
||||
return {
|
||||
assetId: asset.id,
|
||||
url: asset.url,
|
||||
createdAssetId: null,
|
||||
uploadedUrl: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (media.mode === "external") {
|
||||
if (!media.url) {
|
||||
if (input.required) {
|
||||
throw new Error("Media URL is required.");
|
||||
}
|
||||
|
||||
return {
|
||||
assetId: null,
|
||||
url: "",
|
||||
uploadedUrl: null,
|
||||
};
|
||||
}
|
||||
|
||||
const asset = await createMediaAsset({
|
||||
source: MediaSource.EXTERNAL,
|
||||
kind: media.kind,
|
||||
url: media.url,
|
||||
fileName: getFileNameFromUrl(media.url),
|
||||
label: media.label || input.fallbackLabel,
|
||||
altText: media.label || input.fallbackLabel,
|
||||
});
|
||||
|
||||
return {
|
||||
assetId: asset.id,
|
||||
url: asset.url,
|
||||
createdAssetId: asset.id,
|
||||
uploadedUrl: null,
|
||||
};
|
||||
}
|
||||
|
||||
const uploadFile = input.uploadFile;
|
||||
|
||||
if (!(uploadFile instanceof File) || uploadFile.size === 0) {
|
||||
if (input.required) {
|
||||
throw new Error("Upload file is required.");
|
||||
}
|
||||
|
||||
return {
|
||||
assetId: null,
|
||||
url: "",
|
||||
createdAssetId: null,
|
||||
uploadedUrl: null,
|
||||
};
|
||||
}
|
||||
|
||||
const savedFile = await saveMediaUpload(uploadFile, input.folder);
|
||||
|
||||
if (!savedFile) {
|
||||
throw new Error("Unable to save upload.");
|
||||
}
|
||||
|
||||
const asset = await createMediaAsset({
|
||||
source: MediaSource.UPLOAD,
|
||||
kind: media.kind,
|
||||
url: savedFile.url,
|
||||
fileName: savedFile.fileName,
|
||||
label: media.label || input.fallbackLabel,
|
||||
altText: media.label || input.fallbackLabel,
|
||||
mimeType: savedFile.mimeType,
|
||||
size: savedFile.size,
|
||||
});
|
||||
|
||||
return {
|
||||
assetId: asset.id,
|
||||
url: asset.url,
|
||||
createdAssetId: asset.id,
|
||||
uploadedUrl: asset.url,
|
||||
};
|
||||
}
|
||||
|
||||
export async function createStandaloneMediaAsset(input: {
|
||||
kind: MediaKind;
|
||||
label: string;
|
||||
uploadFile: FormDataEntryValue | null;
|
||||
externalUrl: string;
|
||||
}) {
|
||||
const trimmedLabel = input.label.trim();
|
||||
|
||||
if (!trimmedLabel) {
|
||||
throw new Error("Media label is required.");
|
||||
}
|
||||
|
||||
if (input.uploadFile instanceof File && input.uploadFile.size > 0) {
|
||||
const savedFile = await saveMediaUpload(input.uploadFile, input.kind.toLowerCase());
|
||||
|
||||
if (!savedFile) {
|
||||
throw new Error("Unable to save upload.");
|
||||
}
|
||||
|
||||
return createMediaAsset({
|
||||
source: MediaSource.UPLOAD,
|
||||
kind: input.kind,
|
||||
url: savedFile.url,
|
||||
fileName: savedFile.fileName,
|
||||
label: trimmedLabel,
|
||||
altText: trimmedLabel,
|
||||
mimeType: savedFile.mimeType,
|
||||
size: savedFile.size,
|
||||
});
|
||||
}
|
||||
|
||||
const url = input.externalUrl.trim();
|
||||
|
||||
if (!url) {
|
||||
throw new Error("Either an upload file or an external URL is required.");
|
||||
}
|
||||
|
||||
return createMediaAsset({
|
||||
source: MediaSource.EXTERNAL,
|
||||
kind: input.kind,
|
||||
url,
|
||||
fileName: getFileNameFromUrl(url),
|
||||
label: trimmedLabel,
|
||||
altText: trimmedLabel,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteMediaAssetAndFile(input: {
|
||||
assetId: string;
|
||||
assetUrl: string;
|
||||
}) {
|
||||
await removeManagedMediaFile(input.assetUrl);
|
||||
}
|
||||
|
||||
export function inferMediaKindFromMimeType(mimeType: string | null | undefined): MediaKind {
|
||||
return mimeType?.startsWith("image/") || mimeType === "image/svg+xml"
|
||||
? MediaKind.IMAGE
|
||||
: MediaKind.DOCUMENT;
|
||||
}
|
||||
|
||||
export function inferMediaKindFromFileName(fileName: string): MediaKind {
|
||||
const extension = path.extname(fileName).toLowerCase();
|
||||
|
||||
if ([".jpg", ".jpeg", ".png", ".webp", ".svg"].includes(extension)) {
|
||||
return MediaKind.IMAGE;
|
||||
}
|
||||
|
||||
return MediaKind.DOCUMENT;
|
||||
}
|
||||
|
||||
export function getKindFromUploadFile(file: File) {
|
||||
const extension = getExtensionForMimeType(file.type);
|
||||
|
||||
if (extension && [".jpg", ".jpeg", ".png", ".webp", ".svg"].includes(extension)) {
|
||||
return MediaKind.IMAGE;
|
||||
}
|
||||
|
||||
return MediaKind.DOCUMENT;
|
||||
}
|
||||
Reference in New Issue
Block a user