72 const menuRef = useRef(null);
73 const isPublic = blob.key.startsWith("__public/");
74 const publicUrl = isPublic ? `${window.location.origin}/api/public/${encodeURIComponent(blob.key.slice(9))}` : null;
75
76 useEffect(() => {
236 setLoading(true);
237 try {
238 const response = await fetch(`/api/blobs?prefix=${encodeKey(searchPrefix)}&limit=${limit}`);
239 const data = await response.json();
240 setBlobs(data);
263 setBlobContentLoading(true);
264 try {
265 const response = await fetch(`/api/blob?key=${encodeKey(clickedBlob.key)}`);
266 const content = await response.text();
267 setSelectedBlob({ ...clickedBlob, key: decodeKey(clickedBlob.key) });
277 const handleSave = async () => {
278 try {
279 await fetch(`/api/blob?key=${encodeKey(selectedBlob.key)}`, {
280 method: "PUT",
281 body: editContent,
289 const handleDelete = async (key) => {
290 try {
291 await fetch(`/api/blob?key=${encodeKey(key)}`, { method: "DELETE" });
292 setBlobs(blobs.filter(b => b.key !== key));
293 if (selectedBlob && selectedBlob.key === key) {
306 const key = `${searchPrefix}${file.name}`;
307 formData.append("key", encodeKey(key));
308 await fetch("/api/blob", { method: "POST", body: formData });
309 const newBlob = { key, size: file.size, lastModified: new Date().toISOString() };
310 setBlobs([newBlob, ...blobs]);
328 try {
329 const fullKey = `${searchPrefix}${key}`;
330 await fetch(`/api/blob?key=${encodeKey(fullKey)}`, {
331 method: "PUT",
332 body: "",
343 const handleDownload = async (key) => {
344 try {
345 const response = await fetch(`/api/blob?key=${encodeKey(key)}`);
346 const blob = await response.blob();
347 const url = window.URL.createObjectURL(blob);
362 if (newKey && newKey !== oldKey) {
363 try {
364 const response = await fetch(`/api/blob?key=${encodeKey(oldKey)}`);
365 const content = await response.blob();
366 await fetch(`/api/blob?key=${encodeKey(newKey)}`, {
367 method: "PUT",
368 body: content,
369 });
370 await fetch(`/api/blob?key=${encodeKey(oldKey)}`, { method: "DELETE" });
371 setBlobs(blobs.map(b => b.key === oldKey ? { ...b, key: newKey } : b));
372 if (selectedBlob && selectedBlob.key === oldKey) {
382 const newKey = `__public/${key}`;
383 try {
384 const response = await fetch(`/api/blob?key=${encodeKey(key)}`);
385 const content = await response.blob();
386 await fetch(`/api/blob?key=${encodeKey(newKey)}`, {
387 method: "PUT",
388 body: content,
389 });
390 await fetch(`/api/blob?key=${encodeKey(key)}`, { method: "DELETE" });
391 setBlobs(blobs.map(b => b.key === key ? { ...b, key: newKey } : b));
392 if (selectedBlob && selectedBlob.key === key) {
401 const newKey = key.slice(9); // Remove "__public/" prefix
402 try {
403 const response = await fetch(`/api/blob?key=${encodeKey(key)}`);
404 const content = await response.blob();
405 await fetch(`/api/blob?key=${encodeKey(newKey)}`, {
406 method: "PUT",
407 body: content,
408 });
409 await fetch(`/api/blob?key=${encodeKey(key)}`, { method: "DELETE" });
410 setBlobs(blobs.map(b => b.key === key ? { ...b, key: newKey } : b));
411 if (selectedBlob && selectedBlob.key === key) {
556 onClick={() =>
557 copyToClipboard(
558 `${window.location.origin}/api/public/${encodeURIComponent(selectedBlob.key.slice(9))}`,
559 )}
560 className="text-blue-400 hover:text-blue-300 text-sm"
579 >
580 <img
581 src={`/api/blob?key=${encodeKey(selectedBlob.key)}`}
582 alt="Blob content"
583 className="max-w-full h-auto"
655
656export default lastlogin(async function handler(request: Request): Promise<Response> {
657 if (request.method === "GET" && request.url.includes("/api/public/")) {
658 const url = new URL(request.url);
659 const key = `__public/${decodeURIComponent(url.pathname.split("/api/public/")[1])}`;
660 const { blob } = await import("https://esm.town/v/std/blob");
661 const content = await blob.get(key);
762 }
763
764 if (request.method === "GET" && request.url.includes("/api/blobs")) {
765 const url = new URL(request.url);
766 const prefix = decodeURIComponent(url.searchParams.get("prefix") || "");
774 }
775
776 if (request.method === "GET" && request.url.includes("/api/blob")) {
777 const url = new URL(request.url);
778 const key = decodeURIComponent(url.searchParams.get("key"));
783 }
784
785 if (request.method === "PUT" && request.url.includes("/api/blob")) {
786 const url = new URL(request.url);
787 const key = decodeURIComponent(url.searchParams.get("key"));
793 }
794
795 if (request.method === "DELETE" && request.url.includes("/api/blob")) {
796 const url = new URL(request.url);
797 const key = decodeURIComponent(url.searchParams.get("key"));
802 }
803
804 if (request.method === "POST" && request.url.includes("/api/blob")) {
805 const formData = await request.formData();
806 const file = formData.get("file") as File;