瀏覽代碼

Merge pull request #182 from C4illin/feature/#180/add-webroot-env-variable

Emrik Östling 8 月之前
父節點
當前提交
1589f8d24e
共有 7 個文件被更改,包括 114 次插入86 次删除
  1. 15 6
      README.md
  2. 1 0
      compose.yaml
  3. 3 1
      public/results.js
  4. 6 4
      public/script.js
  5. 8 5
      src/components/base.tsx
  6. 7 5
      src/components/header.tsx
  7. 74 65
      src/index.tsx

+ 15 - 6
README.md

@@ -47,12 +47,8 @@ services:
     restart: unless-stopped
     ports:
       - "3000:3000"
-    environment: # Defaults are listed below. All are optional.
-      - 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() 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
+    environment:
+      - JWT_SECRET=aLongAndSecretStringUsedToSignTheJSONWebToken1234 # will use randomUUID() if unset
     volumes:
       - convertx:/app/data
 ```
@@ -67,6 +63,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.
 
+### 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 in french: <https://belginux.com/installer-convertx-avec-docker/>

+ 1 - 0
compose.yaml

@@ -11,5 +11,6 @@ services:
       - 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
       - 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:
       - 3000:3000

+ 3 - 1
public/results.js

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

+ 6 - 4
public/script.js

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

+ 8 - 5
src/components/base.tsx

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

+ 7 - 5
src/components/header.tsx

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

+ 74 - 65
src/index.tsx

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