chat-ui / src /lib /utils /loadAttachmentsFromUrls.ts
coyotte508
A new start
fc69895
import { base } from "$app/paths";
export interface AttachmentLoadResult {
files: File[];
errors: string[];
}
/**
* Parse attachment URLs from query parameters
* Supports both comma-separated (?attachments=url1,url2) and multiple params (?attachments=url1&attachments=url2)
*/
function parseAttachmentUrls(searchParams: URLSearchParams): string[] {
const urls: string[] = [];
// Get all 'attachments' parameters
const attachmentParams = searchParams.getAll("attachments");
for (const param of attachmentParams) {
// Split by comma in case multiple URLs are in one param
const splitUrls = param.split(",").map((url) => url.trim());
urls.push(...splitUrls);
}
// Filter out empty strings
return urls.filter((url) => url.length > 0);
}
/**
* Extract filename from URL or Content-Disposition header
*/
function extractFilename(url: string, contentDisposition?: string | null): string {
// Try to get filename from Content-Disposition header
if (contentDisposition) {
const match = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
if (match && match[1]) {
return match[1].replace(/['"]/g, "");
}
}
// Fallback: extract from URL
try {
const urlObj = new URL(url);
const pathname = urlObj.pathname;
const segments = pathname.split("/");
const lastSegment = segments[segments.length - 1];
if (lastSegment && lastSegment.length > 0) {
return decodeURIComponent(lastSegment);
}
} catch {
// Invalid URL, fall through to default
}
return "attachment";
}
/**
* Load files from remote URLs via server-side proxy
*/
export async function loadAttachmentsFromUrls(
searchParams: URLSearchParams
): Promise<AttachmentLoadResult> {
const urls = parseAttachmentUrls(searchParams);
if (urls.length === 0) {
return { files: [], errors: [] };
}
const files: File[] = [];
const errors: string[] = [];
await Promise.all(
urls.map(async (url) => {
try {
// Fetch via our proxy endpoint to bypass CORS
const proxyUrl = `${base}/api/fetch-url?${new URLSearchParams({ url })}`;
const response = await fetch(proxyUrl);
if (!response.ok) {
const errorText = await response.text();
errors.push(`Failed to fetch ${url}: ${errorText}`);
return;
}
const blob = await response.blob();
const contentDisposition = response.headers.get("content-disposition");
const filename = extractFilename(url, contentDisposition);
// Create File object
const file = new File([blob], filename, {
type: blob.type || "application/octet-stream",
});
files.push(file);
} catch (err) {
const message = err instanceof Error ? err.message : "Unknown error";
errors.push(`Failed to load ${url}: ${message}`);
console.error(`Error loading attachment from ${url}:`, err);
}
})
);
return { files, errors };
}