feat: Allow to chose webroot

issue #180
This commit is contained in:
C4illin 2024-11-06 08:49:53 +01:00
parent f3a4aece46
commit 36cb6cc589
7 changed files with 114 additions and 86 deletions

View file

@ -45,12 +45,8 @@ services:
restart: unless-stopped restart: unless-stopped
ports: ports:
- "3000:3000" - "3000:3000"
environment: # Defaults are listed below. All are optional. environment:
- ACCOUNT_REGISTRATION=false # true or false, doesn't matter for the first account (e.g. keep this to false if you only want one account) - JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234 # will use randomUUID() if unset
- JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234 # will use randomUUID() by default
- HTTP_ALLOWED=false # setting this to true is unsafe, only set this to true locally
- ALLOW_UNAUTHENTICATED=false # allows anyone to use the service without logging in, only set this to true locally
- AUTO_DELETE_EVERY_N_HOURS=24 # checks every n hours for files older then n hours and deletes them, set to 0 to disable
volumes: volumes:
- convertx:/app/data - convertx:/app/data
``` ```
@ -65,6 +61,19 @@ Then visit `http://localhost:3000` in your browser and create your account. Don'
If you get unable to open database file run `chown -R $USER:$USER path` on the path you choose. If you get unable to open database file run `chown -R $USER:$USER path` on the path you choose.
### Environment variables
All are optional, JWT_SECRET is recommended to be set.
| Name | Default | Description |
|---------------------------|---------|-------------|
| JWT_SECRET | when unset it will use the value from randomUUID() | A long and secret string used to sign the JSON Web Token |
| ACCOUNT_REGISTRATION | false | Allow users to register accounts |
| HTTP_ALLOWED | false | Allow HTTP connections, only set this to true locally |
| ALLOW_UNAUTHENTICATED | false | Allow unauthenticated users to use the service, only set this to true locally |
| AUTO_DELETE_EVERY_N_HOURS | 24 | Checks every n hours for files older then n hours and deletes them, set to 0 to disable |
| WEBROOT | "" | The address to the root path setting this to "/convert" will serve the website on "example.com/convert/" |
### Tutorial ### Tutorial
Tutorial in french: https://belginux.com/installer-convertx-avec-docker/ Tutorial in french: https://belginux.com/installer-convertx-avec-docker/

View file

@ -11,5 +11,6 @@ services:
- HTTP_ALLOWED=true # setting this to true is unsafe, only set this to true locally - HTTP_ALLOWED=true # setting this to true is unsafe, only set this to true locally
- ALLOW_UNAUTHENTICATED=true # allows anyone to use the service without logging in, only set this to true locally - ALLOW_UNAUTHENTICATED=true # allows anyone to use the service without logging in, only set this to true locally
- AUTO_DELETE_EVERY_N_HOURS=1 # checks every n hours for files older then n hours and deletes them, set to 0 to disable - AUTO_DELETE_EVERY_N_HOURS=1 # checks every n hours for files older then n hours and deletes them, set to 0 to disable
- WEBROOT=/convertx # the root path of the web interface, leave empty to disable
ports: ports:
- 3000:3000 - 3000:3000

View file

@ -1,3 +1,5 @@
const webroot = document.querySelector("meta[name='webroot']").content;
window.downloadAll = function () { window.downloadAll = function () {
// Get all download links // Get all download links
const downloadLinks = document.querySelectorAll("a[download]"); const downloadLinks = document.querySelectorAll("a[download]");
@ -18,7 +20,7 @@ let progressElem = document.querySelector("progress");
const refreshData = () => { const refreshData = () => {
// console.log("Refreshing data...", progressElem.value, progressElem.max); // console.log("Refreshing data...", progressElem.value, progressElem.max);
if (progressElem.value !== progressElem.max) { if (progressElem.value !== progressElem.max) {
fetch(`/progress/${jobId}`, { fetch(`${webroot}/progress/${jobId}`, {
method: "POST", method: "POST",
}) })
.then((res) => res.text()) .then((res) => res.text())

View file

@ -1,3 +1,5 @@
const webroot = document.querySelector("meta[name='webroot']").content;
// Select the file input element // Select the file input element
const fileInput = document.querySelector('input[type="file"]'); const fileInput = document.querySelector('input[type="file"]');
const dropZone = document.getElementById("dropzone"); const dropZone = document.getElementById("dropzone");
@ -132,7 +134,7 @@ fileInput.addEventListener("change", (e) => {
// } // }
// } // }
fetch("/conversions", { fetch(`${webroot}/conversions`, {
method: "POST", method: "POST",
body: JSON.stringify({ fileType: fileType }), body: JSON.stringify({ fileType: fileType }),
headers: { headers: {
@ -180,7 +182,7 @@ const deleteRow = (target) => {
setTitle(); setTitle();
} }
fetch("/delete", { fetch(`${webroot}/delete`, {
method: "POST", method: "POST",
body: JSON.stringify({ filename: filename }), body: JSON.stringify({ filename: filename }),
headers: { headers: {
@ -201,7 +203,7 @@ const uploadFiles = (files) => {
formData.append("file", file, file.name); formData.append("file", file, file.name);
} }
fetch("/upload", { fetch(`${webroot}/upload`, {
method: "POST", method: "POST",
body: formData, body: formData,
}) })
@ -212,7 +214,7 @@ const uploadFiles = (files) => {
.catch((err) => console.log(err)); .catch((err) => console.log(err));
}; };
const formConvert = document.querySelector("form[action='/convert']"); const formConvert = document.querySelector(`form[action='${webroot}/convert']`);
formConvert.addEventListener("submit", () => { formConvert.addEventListener("submit", () => {
const hiddenInput = document.querySelector("input[name='file_names']"); const hiddenInput = document.querySelector("input[name='file_names']");

View file

@ -3,34 +3,37 @@ import { Html } from "@elysiajs/html";
export const BaseHtml = ({ export const BaseHtml = ({
children, children,
title = "ConvertX", title = "ConvertX",
webroot = "",
}: { }: {
children: JSX.Element; children: JSX.Element;
title?: string; title?: string;
webroot?: string;
}) => ( }) => (
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="webroot" content={webroot} />
<title safe>{title}</title> <title safe>{title}</title>
<link rel="stylesheet" href="/generated.css" /> <link rel="stylesheet" href={`${webroot}/generated.css`} />
<link <link
rel="apple-touch-icon" rel="apple-touch-icon"
sizes="180x180" sizes="180x180"
href="/apple-touch-icon.png" href={`${webroot}/apple-touch-icon.png`}
/> />
<link <link
rel="icon" rel="icon"
type="image/png" type="image/png"
sizes="32x32" sizes="32x32"
href="/favicon-32x32.png" href={`${webroot}/favicon-32x32.png`}
/> />
<link <link
rel="icon" rel="icon"
type="image/png" type="image/png"
sizes="16x16" sizes="16x16"
href="/favicon-16x16.png" href={`${webroot}/favicon-16x16.png`}
/> />
<link rel="manifest" href="/site.webmanifest" /> <link rel="manifest" href={`${webroot}/site.webmanifest`} />
</head> </head>
<body class="w-full bg-neutral-900 text-neutral-200">{children}</body> <body class="w-full bg-neutral-900 text-neutral-200">{children}</body>
</html> </html>

View file

@ -3,9 +3,11 @@ import { Html } from "@kitajs/html";
export const Header = ({ export const Header = ({
loggedIn, loggedIn,
accountRegistration, accountRegistration,
webroot = "",
}: { }: {
loggedIn?: boolean; loggedIn?: boolean;
accountRegistration?: boolean; accountRegistration?: boolean;
webroot?: string;
}) => { }) => {
let rightNav: JSX.Element; let rightNav: JSX.Element;
if (loggedIn) { if (loggedIn) {
@ -17,7 +19,7 @@ export const Header = ({
text-accent-600 transition-all text-accent-600 transition-all
hover:text-accent-500 hover:underline hover:text-accent-500 hover:underline
`} `}
href="/history" href={`${webroot}/history`}
> >
History History
</a> </a>
@ -28,7 +30,7 @@ export const Header = ({
text-accent-600 transition-all text-accent-600 transition-all
hover:text-accent-500 hover:underline hover:text-accent-500 hover:underline
`} `}
href="/logoff" href={`${webroot}/logoff`}
> >
Logout Logout
</a> </a>
@ -44,7 +46,7 @@ export const Header = ({
text-accent-600 transition-all text-accent-600 transition-all
hover:text-accent-500 hover:underline hover:text-accent-500 hover:underline
`} `}
href="/login" href={`${webroot}/login`}
> >
Login Login
</a> </a>
@ -56,7 +58,7 @@ export const Header = ({
text-accent-600 transition-all text-accent-600 transition-all
hover:text-accent-500 hover:underline hover:text-accent-500 hover:underline
`} `}
href="/register" href={`${webroot}/register`}
> >
Register Register
</a> </a>
@ -72,7 +74,7 @@ export const Header = ({
<ul> <ul>
<li> <li>
<strong> <strong>
<a href="/">ConvertX</a> <a href={`${webroot}/`}>ConvertX</a>
</strong> </strong>
</li> </li>
</ul> </ul>

View file

@ -37,6 +37,8 @@ const AUTO_DELETE_EVERY_N_HOURS = process.env.AUTO_DELETE_EVERY_N_HOURS
? Number(process.env.AUTO_DELETE_EVERY_N_HOURS) ? Number(process.env.AUTO_DELETE_EVERY_N_HOURS)
: 24; : 24;
const WEBROOT = process.env.WEBROOT ?? "/convertx";
// fileNames: fileNames, // fileNames: fileNames,
// filesToConvert: fileNames.length, // filesToConvert: fileNames.length,
// convertedFiles : 0, // convertedFiles : 0,
@ -112,6 +114,7 @@ const app = new Elysia({
serve: { serve: {
maxRequestBodySize: Number.MAX_SAFE_INTEGER, maxRequestBodySize: Number.MAX_SAFE_INTEGER,
}, },
prefix: WEBROOT,
}) })
.use(cookie()) .use(cookie())
.use(html()) .use(html())
@ -145,18 +148,18 @@ const app = new Elysia({
}) })
.get("/setup", ({ redirect }) => { .get("/setup", ({ redirect }) => {
if (!FIRST_RUN) { if (!FIRST_RUN) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
return ( return (
<BaseHtml title="ConvertX | Setup"> <BaseHtml title="ConvertX | Setup" webroot={WEBROOT}>
<main class="mx-auto w-full max-w-4xl px-4"> <main class="mx-auto w-full max-w-4xl px-4">
<h1 class="my-8 text-3xl">Welcome to ConvertX!</h1> <h1 class="my-8 text-3xl">Welcome to ConvertX!</h1>
<article class="article p-0"> <article class="article p-0">
<header class="w-full bg-neutral-800 p-4"> <header class="w-full bg-neutral-800 p-4">
Create your account Create your account
</header> </header>
<form method="post" action="/register" class="p-4"> <form method="post" action={`${WEBROOT}/register`} class="p-4">
<fieldset class="mb-4 flex flex-col gap-4"> <fieldset class="mb-4 flex flex-col gap-4">
<label class="flex flex-col gap-1"> <label class="flex flex-col gap-1">
Email Email
@ -203,13 +206,16 @@ const app = new Elysia({
}) })
.get("/register", ({ redirect }) => { .get("/register", ({ redirect }) => {
if (!ACCOUNT_REGISTRATION) { if (!ACCOUNT_REGISTRATION) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
return ( return (
<BaseHtml title="ConvertX | Register"> <BaseHtml webroot={WEBROOT} title="ConvertX | Register">
<> <>
<Header accountRegistration={ACCOUNT_REGISTRATION} /> <Header
webroot={WEBROOT}
accountRegistration={ACCOUNT_REGISTRATION}
/>
<main class="w-full px-4"> <main class="w-full px-4">
<article class="article"> <article class="article">
<form method="post" class="flex flex-col gap-4"> <form method="post" class="flex flex-col gap-4">
@ -253,7 +259,7 @@ const app = new Elysia({
"/register", "/register",
async ({ body, set, redirect, jwt, cookie: { auth } }) => { async ({ body, set, redirect, jwt, cookie: { auth } }) => {
if (!ACCOUNT_REGISTRATION && !FIRST_RUN) { if (!ACCOUNT_REGISTRATION && !FIRST_RUN) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
if (FIRST_RUN) { if (FIRST_RUN) {
@ -308,13 +314,13 @@ const app = new Elysia({
sameSite: "strict", sameSite: "strict",
}); });
return redirect("/", 302); return redirect(`${WEBROOT}/`, 302);
}, },
{ body: t.Object({ email: t.String(), password: t.String() }) }, { body: t.Object({ email: t.String(), password: t.String() }) },
) )
.get("/login", async ({ jwt, redirect, cookie: { auth } }) => { .get("/login", async ({ jwt, redirect, cookie: { auth } }) => {
if (FIRST_RUN) { if (FIRST_RUN) {
return redirect("/setup", 302); return redirect(`${WEBROOT}/setup`, 302);
} }
// if already logged in, redirect to home // if already logged in, redirect to home
@ -322,16 +328,19 @@ const app = new Elysia({
const user = await jwt.verify(auth.value); const user = await jwt.verify(auth.value);
if (user) { if (user) {
return redirect("/", 302); return redirect(`${WEBROOT}/`, 302);
} }
auth.remove(); auth.remove();
} }
return ( return (
<BaseHtml title="ConvertX | Login"> <BaseHtml webroot={WEBROOT} title="ConvertX | Login">
<> <>
<Header accountRegistration={ACCOUNT_REGISTRATION} /> <Header
webroot={WEBROOT}
accountRegistration={ACCOUNT_REGISTRATION}
/>
<main class="w-full px-4"> <main class="w-full px-4">
<article class="article"> <article class="article">
<form method="post" class="flex flex-col gap-4"> <form method="post" class="flex flex-col gap-4">
@ -362,7 +371,7 @@ const app = new Elysia({
<div role="group"> <div role="group">
{ACCOUNT_REGISTRATION ? ( {ACCOUNT_REGISTRATION ? (
<a <a
href="/register" href={`${WEBROOT}/register`}
role="button" role="button"
class="btn-primary w-full" class="btn-primary w-full"
> >
@ -429,7 +438,7 @@ const app = new Elysia({
sameSite: "strict", sameSite: "strict",
}); });
return redirect("/", 302); return redirect(`${WEBROOT}/`, 302);
}, },
{ body: t.Object({ email: t.String(), password: t.String() }) }, { body: t.Object({ email: t.String(), password: t.String() }) },
) )
@ -438,22 +447,22 @@ const app = new Elysia({
auth.remove(); auth.remove();
} }
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
}) })
.post("/logoff", ({ redirect, cookie: { auth } }) => { .post("/logoff", ({ redirect, cookie: { auth } }) => {
if (auth?.value) { if (auth?.value) {
auth.remove(); auth.remove();
} }
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
}) })
.get("/", async ({ jwt, redirect, cookie: { auth, jobId } }) => { .get("/", async ({ jwt, redirect, cookie: { auth, jobId } }) => {
if (FIRST_RUN) { if (FIRST_RUN) {
return redirect("/setup", 302); return redirect(`${WEBROOT}/setup`, 302);
} }
if (!auth?.value && !ALLOW_UNAUTHENTICATED) { if (!auth?.value && !ALLOW_UNAUTHENTICATED) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
// validate jwt // validate jwt
@ -473,7 +482,7 @@ const app = new Elysia({
if (auth?.value) { if (auth?.value) {
auth.remove(); auth.remove();
} }
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
} }
} }
@ -506,7 +515,7 @@ const app = new Elysia({
} }
if (!user) { if (!user) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
// create a new job // create a new job
@ -536,9 +545,9 @@ const app = new Elysia({
console.log("jobId set to:", id); console.log("jobId set to:", id);
return ( return (
<BaseHtml> <BaseHtml webroot={WEBROOT}>
<> <>
<Header loggedIn /> <Header webroot={WEBROOT} loggedIn />
<main class="w-full px-4"> <main class="w-full px-4">
<article class="article"> <article class="article">
<h1 class="mb-4 text-xl">Convert</h1> <h1 class="mb-4 text-xl">Convert</h1>
@ -574,7 +583,7 @@ const app = new Elysia({
</article> </article>
<form <form
method="post" method="post"
action="/convert" action={`${WEBROOT}/convert`}
class="relative mx-auto mb-[35vh] w-full max-w-4xl" class="relative mx-auto mb-[35vh] w-full max-w-4xl"
> >
<input type="hidden" name="file_names" id="file_names" /> <input type="hidden" name="file_names" id="file_names" />
@ -739,16 +748,16 @@ const app = new Elysia({
"/upload", "/upload",
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => { async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
if (!auth?.value) { if (!auth?.value) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
const user = await jwt.verify(auth.value); const user = await jwt.verify(auth.value);
if (!user) { if (!user) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
if (!jobId?.value) { if (!jobId?.value) {
return redirect("/", 302); return redirect(`${WEBROOT}/`, 302);
} }
const existingJob = await db const existingJob = await db
@ -756,7 +765,7 @@ const app = new Elysia({
.get(jobId.value, user.id); .get(jobId.value, user.id);
if (!existingJob) { if (!existingJob) {
return redirect("/", 302); return redirect(`${WEBROOT}/`, 302);
} }
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`; const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
@ -781,16 +790,16 @@ const app = new Elysia({
"/delete", "/delete",
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => { async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
if (!auth?.value) { if (!auth?.value) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
const user = await jwt.verify(auth.value); const user = await jwt.verify(auth.value);
if (!user) { if (!user) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
if (!jobId?.value) { if (!jobId?.value) {
return redirect("/", 302); return redirect(`${WEBROOT}/`, 302);
} }
const existingJob = await db const existingJob = await db
@ -798,7 +807,7 @@ const app = new Elysia({
.get(jobId.value, user.id); .get(jobId.value, user.id);
if (!existingJob) { if (!existingJob) {
return redirect("/", 302); return redirect(`${WEBROOT}/`, 302);
} }
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`; const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
@ -811,16 +820,16 @@ const app = new Elysia({
"/convert", "/convert",
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => { async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
if (!auth?.value) { if (!auth?.value) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
const user = await jwt.verify(auth.value); const user = await jwt.verify(auth.value);
if (!user) { if (!user) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
if (!jobId?.value) { if (!jobId?.value) {
return redirect("/", 302); return redirect(`${WEBROOT}/`, 302);
} }
const existingJob = db const existingJob = db
@ -829,7 +838,7 @@ const app = new Elysia({
.get(jobId.value, user.id); .get(jobId.value, user.id);
if (!existingJob) { if (!existingJob) {
return redirect("/", 302); return redirect(`${WEBROOT}/`, 302);
} }
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`; const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
@ -850,7 +859,7 @@ const app = new Elysia({
const fileNames = JSON.parse(body.file_names) as string[]; const fileNames = JSON.parse(body.file_names) as string[];
if (!Array.isArray(fileNames) || fileNames.length === 0) { if (!Array.isArray(fileNames) || fileNames.length === 0) {
return redirect("/", 302); return redirect(`${WEBROOT}/`, 302);
} }
db.query( db.query(
@ -903,7 +912,7 @@ const app = new Elysia({
}); });
// Redirect the client immediately // Redirect the client immediately
return redirect(`/results/${jobId.value}`, 302); return redirect(`${WEBROOT}/results/${jobId.value}`, 302);
}, },
{ {
body: t.Object({ body: t.Object({
@ -914,12 +923,12 @@ const app = new Elysia({
) )
.get("/history", async ({ jwt, redirect, cookie: { auth } }) => { .get("/history", async ({ jwt, redirect, cookie: { auth } }) => {
if (!auth?.value) { if (!auth?.value) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
const user = await jwt.verify(auth.value); const user = await jwt.verify(auth.value);
if (!user) { if (!user) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
let userJobs = db let userJobs = db
@ -940,9 +949,9 @@ const app = new Elysia({
userJobs = userJobs.filter((job) => job.num_files > 0); userJobs = userJobs.filter((job) => job.num_files > 0);
return ( return (
<BaseHtml title="ConvertX | Results"> <BaseHtml webroot={WEBROOT} title="ConvertX | Results">
<> <>
<Header loggedIn /> <Header webroot={WEBROOT} loggedIn />
<main class="w-full px-4"> <main class="w-full px-4">
<article class="article"> <article class="article">
<h1 class="mb-4 text-xl">Results</h1> <h1 class="mb-4 text-xl">Results</h1>
@ -975,7 +984,7 @@ const app = new Elysia({
text-accent-500 underline text-accent-500 underline
hover:text-accent-400 hover:text-accent-400
`} `}
href={`/results/${job.id}`} href={`${WEBROOT}/results/${job.id}`}
> >
View View
</a> </a>
@ -994,7 +1003,7 @@ const app = new Elysia({
"/results/:jobId", "/results/:jobId",
async ({ params, jwt, set, redirect, cookie: { auth, job_id } }) => { async ({ params, jwt, set, redirect, cookie: { auth, job_id } }) => {
if (!auth?.value) { if (!auth?.value) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
if (job_id?.value) { if (job_id?.value) {
@ -1004,7 +1013,7 @@ const app = new Elysia({
const user = await jwt.verify(auth.value); const user = await jwt.verify(auth.value);
if (!user) { if (!user) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
const job = db const job = db
@ -1027,9 +1036,9 @@ const app = new Elysia({
.all(params.jobId); .all(params.jobId);
return ( return (
<BaseHtml title="ConvertX | Result"> <BaseHtml webroot={WEBROOT} title="ConvertX | Result">
<> <>
<Header loggedIn /> <Header webroot={WEBROOT} loggedIn />
<main class="w-full px-4"> <main class="w-full px-4">
<article class="article"> <article class="article">
<div class="mb-4 flex items-center justify-between"> <div class="mb-4 flex items-center justify-between">
@ -1087,7 +1096,7 @@ const app = new Elysia({
text-accent-500 underline text-accent-500 underline
hover:text-accent-400 hover:text-accent-400
`} `}
href={`/download/${outputPath}${file.output_file_name}`} href={`${WEBROOT}/download/${outputPath}${file.output_file_name}`}
> >
View View
</a> </a>
@ -1098,7 +1107,7 @@ const app = new Elysia({
text-accent-500 underline text-accent-500 underline
hover:text-accent-400 hover:text-accent-400
`} `}
href={`/download/${outputPath}${file.output_file_name}`} href={`${WEBROOT}/download/${outputPath}${file.output_file_name}`}
download={file.output_file_name} download={file.output_file_name}
> >
Download Download
@ -1110,7 +1119,7 @@ const app = new Elysia({
</table> </table>
</article> </article>
</main> </main>
<script src="/results.js" defer /> <script src={`${WEBROOT}/results.js`} defer />
</> </>
</BaseHtml> </BaseHtml>
); );
@ -1120,7 +1129,7 @@ const app = new Elysia({
"/progress/:jobId", "/progress/:jobId",
async ({ jwt, set, params, redirect, cookie: { auth, job_id } }) => { async ({ jwt, set, params, redirect, cookie: { auth, job_id } }) => {
if (!auth?.value) { if (!auth?.value) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
if (job_id?.value) { if (job_id?.value) {
@ -1130,7 +1139,7 @@ const app = new Elysia({
const user = await jwt.verify(auth.value); const user = await jwt.verify(auth.value);
if (!user) { if (!user) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
const job = db const job = db
@ -1209,7 +1218,7 @@ const app = new Elysia({
text-accent-500 underline text-accent-500 underline
hover:text-accent-400 hover:text-accent-400
`} `}
href={`/download/${outputPath}${file.output_file_name}`} href={`${WEBROOT}/download/${outputPath}${file.output_file_name}`}
> >
View View
</a> </a>
@ -1220,7 +1229,7 @@ const app = new Elysia({
text-accent-500 underline text-accent-500 underline
hover:text-accent-400 hover:text-accent-400
`} `}
href={`/download/${outputPath}${file.output_file_name}`} href={`${WEBROOT}/download/${outputPath}${file.output_file_name}`}
download={file.output_file_name} download={file.output_file_name}
> >
Download Download
@ -1238,12 +1247,12 @@ const app = new Elysia({
"/download/:userId/:jobId/:fileName", "/download/:userId/:jobId/:fileName",
async ({ params, jwt, redirect, cookie: { auth } }) => { async ({ params, jwt, redirect, cookie: { auth } }) => {
if (!auth?.value) { if (!auth?.value) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
const user = await jwt.verify(auth.value); const user = await jwt.verify(auth.value);
if (!user) { if (!user) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
const job = await db const job = await db
@ -1251,7 +1260,7 @@ const app = new Elysia({
.get(user.id, params.jobId); .get(user.id, params.jobId);
if (!job) { if (!job) {
return redirect("/results", 302); return redirect(`${WEBROOT}/results`, 302);
} }
// parse from url encoded string // parse from url encoded string
const userId = decodeURIComponent(params.userId); const userId = decodeURIComponent(params.userId);
@ -1264,18 +1273,18 @@ const app = new Elysia({
) )
.get("/converters", async ({ jwt, redirect, cookie: { auth } }) => { .get("/converters", async ({ jwt, redirect, cookie: { auth } }) => {
if (!auth?.value) { if (!auth?.value) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
const user = await jwt.verify(auth.value); const user = await jwt.verify(auth.value);
if (!user) { if (!user) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
return ( return (
<BaseHtml title="ConvertX | Converters"> <BaseHtml webroot={WEBROOT} title="ConvertX | Converters">
<> <>
<Header loggedIn /> <Header webroot={WEBROOT} loggedIn />
<main class="w-full px-4"> <main class="w-full px-4">
<article class="article"> <article class="article">
<h1 class="mb-4 text-xl">Converters</h1> <h1 class="mb-4 text-xl">Converters</h1>
@ -1334,12 +1343,12 @@ const app = new Elysia({
async ({ params, jwt, redirect, cookie: { auth } }) => { async ({ params, jwt, redirect, cookie: { auth } }) => {
// TODO: Implement zip download // TODO: Implement zip download
if (!auth?.value) { if (!auth?.value) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
const user = await jwt.verify(auth.value); const user = await jwt.verify(auth.value);
if (!user) { if (!user) {
return redirect("/login", 302); return redirect(`${WEBROOT}/login`, 302);
} }
const job = await db const job = await db
@ -1347,12 +1356,12 @@ const app = new Elysia({
.get(user.id, params.jobId); .get(user.id, params.jobId);
if (!job) { if (!job) {
return redirect("/results", 302); return redirect(`${WEBROOT}/results`, 302);
} }
// const userId = decodeURIComponent(params.userId); // const userId = decodeURIComponent(params.userId);
// const jobId = decodeURIComponent(params.jobId); // const jobId = decodeURIComponent(params.jobId);
// const outputPath = `${outputDir}${userId}/${jobId}/`; // const outputPath = `${outputDir}${userId}/`{jobId}/);
// return Bun.zip(outputPath); // return Bun.zip(outputPath);
}, },
@ -1376,7 +1385,7 @@ if (process.env.NODE_ENV !== "production") {
app.listen(3000); app.listen(3000);
console.log( console.log(
`🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}`, `🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}${WEBROOT}`,
); );
const clearJobs = () => { const clearJobs = () => {