File size: 2,844 Bytes
fc69895
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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 };
}