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
// This val provides an HTML interface for uploading a CSV file of URLs, validates them,
// and returns a downloadable CSV file with the results.
// It uses the Fetch API to check each URL's status and generates a CSV report.
// We'll use the built-in URL constructor for basic URL validation and the native fetch for HTTP requests.
// The UI is now styled using Tailwind CSS and shadcn UI components for a more polished look.
// Added functionality to show the selected filename in the UI.
import { parse } from "https://deno.land/std@0.182.0/encoding/csv.ts";
import { stringify } from "https://deno.land/std@0.182.0/encoding/csv.ts";
export default async function main(req: Request): Promise<Response> {
if (req.method === "GET") {
// Return HTML form for file upload with shadcn UI styling
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>URL Validator</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/@shadcn/ui@0.0.1/dist/index.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100 flex items-center justify-center min-h-screen">
<div class="bg-white p-8 rounded-lg shadow-md w-96">
<h1 class="text-2xl font-bold mb-6 text-center">CSV URL Validator</h1>
<form action="/" method="post" enctype="multipart/form-data" class="space-y-4">
<div class="flex items-center justify-center w-full">
<label for="file-upload" class="flex flex-col items-center justify-center w-full h-32 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 hover:bg-gray-100">
<div class="flex flex-col items-center justify-center pt-5 pb-6">
<svg class="w-8 h-8 mb-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 16">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2
</svg>
<p class="mb-2 text-sm text-gray-500"><span class="font-semibold">Click to upload</span> or drag and drop</p>
<p class="text-xs text-gray-500">CSV file only</p>
</div>
<input id="file-upload" name="file" type="file" accept=".csv" class="hidden" required />
</label>
</div>
<p id="file-name" class="text-sm text-gray-500 text-center"></p>
<button type="submit" class="w-full px-4 py-2 text-white bg-blue-500 rounded hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">
Validate URLs
</button>
</form>
</div>
<script>
document.getElementById('file-upload').addEventListener('change', function(e) {
var fileName = e.target.files[0].name;
document.getElementById('file-name').textContent = 'Selected file: ' + fileName;
});
</script>
</body>
</html>
`;
return new Response(html, {
headers: { "Content-Type": "text/html" },
});
} else if (req.method === "POST") {
const formData = await req.formData();
const file = formData.get("file") as File | null;
if (!file) {
return new Response("No file uploaded", { status: 400 });
}
const csvContent = await file.text();
const urls = parse(csvContent, { skipFirstRow: false }).flat();
const results = await Promise.all(urls.map(async (url) => {
try {
new URL(url); // Basic URL validation
const response = await fetch(url, { method: "HEAD" });
return { url, valid: response.ok, status: response.status };
} catch {
return { url, valid: false, status: "Invalid URL" };
}
}));
// Prepare CSV data
const csvData = [
["URL", "Valid", "Status"],
...results.map(r => [r.url, r.valid.toString(), r.status.toString()]),
];
const csvOutput = stringify(csvData);
// Return CSV file for download
return new Response(csvOutput, {
headers: {
"Content-Type": "text/csv",
"Content-Disposition": "attachment; filename=url_validation_results.csv",
},
});
} else {
return new Response("Method not allowed", { status: 405 });
}
}