Add converting from URL (#11)

This commit is contained in:
strawberry-raccoon
2026-03-24 20:53:40 +00:00
committed by GitHub
parent 32d1ce6c28
commit 6a7ff645fe
4 changed files with 112 additions and 0 deletions
+31
View File
@@ -1,4 +1,6 @@
const webroot = document.querySelector("meta[name='webroot']").content;
const urlInput = document.querySelector("#url-input");
const urlSubmit = document.querySelector("#url-submit");
const fileInput = document.querySelector('input[type="file"]');
const dropZone = document.getElementById("dropzone");
const convertButton = document.querySelector("input[type='submit']");
@@ -33,6 +35,35 @@ dropZone.addEventListener("drop", (e) => {
}
});
urlSubmit.addEventListener("click", (e) => {
e.preventDefault();
handleUrl(urlInput.value);
});
function handleUrl(url) {
fetch(`${webroot}/url`, {
method: "POST",
body: JSON.stringify({ url }),
headers: { "Content-Type": "application/json" },
})
.then((res) => res.json())
.then((res) => {
const fileList = document.querySelector("#file-list");
const row = document.createElement("tr");
row.innerHTML = `
<td>${res.filename}</td>
<td></td>
<td>${(res.fileSizeBytes / 1024).toFixed(2)} kB</td>
<td><a onclick="deleteRow(this)">Remove</a></td>
`;
fileList.appendChild(row);
fileNames.push(res.filename);
})
.catch(console.error);
}
// Extracted handleFile function for reusability in drag-and-drop and file input
function handleFile(file) {
const fileList = document.querySelector("#file-list");
+2
View File
@@ -16,6 +16,7 @@ import { listConverters } from "./pages/listConverters";
import { results } from "./pages/results";
import { root } from "./pages/root";
import { upload } from "./pages/upload";
import { url } from "./pages/url";
import { user } from "./pages/user";
import { healthcheck } from "./pages/healthcheck";
@@ -41,6 +42,7 @@ const app = new Elysia({
.use(user)
.use(root)
.use(upload)
.use(url)
.use(history)
.use(convert)
.use(download)
+14
View File
@@ -152,6 +152,20 @@ export const root = new Elysia().use(userService).get(
class="absolute inset-0 size-full cursor-pointer opacity-0"
/>
</div>
<label class="mt-4 flex flex-col gap-1 text-neutral-400">
Or enter a URL
<div class="flex flex-row">
<input
name="url"
id="url-input"
class="mr-2 flex-auto rounded-sm bg-neutral-800 p-3"
placeholder="https://example.com/cat.jpg"
/>
<button id="url-submit" type="button" class="flex-initial btn-secondary">
Add URL
</button>
</div>
</label>
</article>
<form
method="post"
+65
View File
@@ -0,0 +1,65 @@
import { Elysia, t } from "elysia";
import { uploadsDir } from "..";
import db from "../db/db";
import { WEBROOT } from "../helpers/env";
import { userService } from "./user";
import sanitize from "sanitize-filename";
import { randomUUID } from "node:crypto";
import mime from "mime";
const getFilename = (url: string, headers: Headers) => {
const contentDisposition = headers.get("Content-Disposition");
if (contentDisposition) {
const match = /filename="([^"]+)"/.exec(contentDisposition);
if (match && match[1]) {
return sanitize(match[1]);
}
}
const path = new URL(url).pathname;
const lastPart = path.split("/").at(-1);
const contentType = headers.get("content-type");
const extension = contentType ? mime.getExtension(contentType) : null;
if (!lastPart) {
if (extension) {
return `${randomUUID()}.${extension}`;
}
return randomUUID();
}
if (!lastPart.includes(".") && extension) {
return `${sanitize(lastPart)}.${extension}`;
}
return sanitize(lastPart);
};
export const url = new Elysia().use(userService).post(
"/url",
async ({ body, redirect, user, cookie: { jobId } }) => {
if (!jobId?.value) {
return redirect(`${WEBROOT}/`, 302);
}
const existingJob = await db
.query("SELECT * FROM jobs WHERE id = ? AND user_id = ?")
.get(jobId.value, user.id);
if (!existingJob) {
return redirect(`${WEBROOT}/`, 302);
}
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
const res = await fetch(body.url);
if (!res.ok) {
throw new Error(`Failed to download URL, received ${res.status}`);
}
const filename = getFilename(body.url, res.headers);
const fileSizeBytes = await Bun.write(`${userUploadsDir}${filename}`, await res.blob());
return {
message: "Files downloaded successfully.",
filename,
fileSizeBytes,
};
},
{ body: t.Object({ url: t.String() }), auth: true },
);