parent
f3a4aece46
commit
36cb6cc589
7 changed files with 114 additions and 86 deletions
21
README.md
21
README.md
|
|
@ -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/
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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']");
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
139
src/index.tsx
139
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)
|
? 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 = () => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue