chore: fix type errors and update bun sql syntax
This commit is contained in:
parent
b9fe32053c
commit
ae2455e73e
9 changed files with 325 additions and 398 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -46,4 +46,5 @@ package-lock.json
|
||||||
/output
|
/output
|
||||||
/db
|
/db
|
||||||
/data
|
/data
|
||||||
/Bruno
|
/Bruno
|
||||||
|
/tsconfig.tsbuildinfo
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
export const BaseHtml = ({ children, title = "ConvertX" }) => (
|
export const BaseHtml = ({
|
||||||
|
children,
|
||||||
|
title = "ConvertX",
|
||||||
|
}: { children: JSX.Element; title?: string }) => (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export const Header = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="container">
|
<header class="container">
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
|
|
||||||
|
|
@ -260,6 +260,7 @@ export const properties = {
|
||||||
"mpegts",
|
"mpegts",
|
||||||
"mpegtsraw",
|
"mpegtsraw",
|
||||||
"mpegvideo",
|
"mpegvideo",
|
||||||
|
"mpg",
|
||||||
"mpjpeg",
|
"mpjpeg",
|
||||||
"mpl2",
|
"mpl2",
|
||||||
"mpo",
|
"mpo",
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,7 @@ for (const converterName in properties) {
|
||||||
}
|
}
|
||||||
possibleInputs.sort();
|
possibleInputs.sort();
|
||||||
|
|
||||||
export const getPossibleInputs = () => {
|
const getPossibleInputs = () => {
|
||||||
return possibleInputs;
|
return possibleInputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,119 +0,0 @@
|
||||||
import sharp from "sharp";
|
|
||||||
import type { FormatEnum } from "sharp";
|
|
||||||
|
|
||||||
// declare possible conversions
|
|
||||||
export const properties = {
|
|
||||||
from: {
|
|
||||||
images: [
|
|
||||||
"avif",
|
|
||||||
"bif",
|
|
||||||
"csv",
|
|
||||||
"exr",
|
|
||||||
"fits",
|
|
||||||
"gif",
|
|
||||||
"hdr.gz",
|
|
||||||
"hdr",
|
|
||||||
"heic",
|
|
||||||
"heif",
|
|
||||||
"img.gz",
|
|
||||||
"img",
|
|
||||||
"j2c",
|
|
||||||
"j2k",
|
|
||||||
"jp2",
|
|
||||||
"jpeg",
|
|
||||||
"jpx",
|
|
||||||
"jxl",
|
|
||||||
"mat",
|
|
||||||
"mrxs",
|
|
||||||
"ndpi",
|
|
||||||
"nia.gz",
|
|
||||||
"nia",
|
|
||||||
"nii.gz",
|
|
||||||
"nii",
|
|
||||||
"pdf",
|
|
||||||
"pfm",
|
|
||||||
"pgm",
|
|
||||||
"pic",
|
|
||||||
"png",
|
|
||||||
"ppm",
|
|
||||||
"raw",
|
|
||||||
"scn",
|
|
||||||
"svg",
|
|
||||||
"svs",
|
|
||||||
"svslide",
|
|
||||||
"szi",
|
|
||||||
"tif",
|
|
||||||
"tiff",
|
|
||||||
"v",
|
|
||||||
"vips",
|
|
||||||
"vms",
|
|
||||||
"vmu",
|
|
||||||
"webp",
|
|
||||||
"zip",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
images: [
|
|
||||||
"avif",
|
|
||||||
"dzi",
|
|
||||||
"fits",
|
|
||||||
"gif",
|
|
||||||
"hdr.gz",
|
|
||||||
"heic",
|
|
||||||
"heif",
|
|
||||||
"img.gz",
|
|
||||||
"j2c",
|
|
||||||
"j2k",
|
|
||||||
"jp2",
|
|
||||||
"jpeg",
|
|
||||||
"jpx",
|
|
||||||
"jxl",
|
|
||||||
"mat",
|
|
||||||
"nia.gz",
|
|
||||||
"nia",
|
|
||||||
"nii.gz",
|
|
||||||
"nii",
|
|
||||||
"png",
|
|
||||||
"tiff",
|
|
||||||
"vips",
|
|
||||||
"webp",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
svg: {
|
|
||||||
scale: {
|
|
||||||
description: "Scale the image up or down",
|
|
||||||
type: "number",
|
|
||||||
default: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function convert(
|
|
||||||
filePath: string,
|
|
||||||
fileType: string,
|
|
||||||
convertTo: keyof FormatEnum,
|
|
||||||
targetPath: string,
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
||||||
options?: any,
|
|
||||||
) {
|
|
||||||
if (fileType === "svg") {
|
|
||||||
const scale = options.scale || 1;
|
|
||||||
const metadata = await sharp(filePath).metadata();
|
|
||||||
|
|
||||||
if (!metadata || !metadata.width || !metadata.height) {
|
|
||||||
throw new Error("Could not get metadata from image");
|
|
||||||
}
|
|
||||||
|
|
||||||
const newWidth = Math.round(metadata.width * scale);
|
|
||||||
const newHeight = Math.round(metadata.height * scale);
|
|
||||||
|
|
||||||
return await sharp(filePath)
|
|
||||||
.resize(newWidth, newHeight)
|
|
||||||
.toFormat(convertTo)
|
|
||||||
.toFile(targetPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return await sharp(filePath).toFormat(convertTo).toFile(targetPath);
|
|
||||||
}
|
|
||||||
|
|
@ -72,4 +72,14 @@ if (process.env.NODE_ENV === "production") {
|
||||||
console.log(stdout.split("\n")[0]);
|
console.log(stdout.split("\n")[0]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
exec("bun -v", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Bun is not installed. wait what");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`Bun v${stdout.split("\n")[0]}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
578
src/index.tsx
578
src/index.tsx
|
|
@ -75,27 +75,27 @@ if (dbVersion === 0) {
|
||||||
|
|
||||||
let FIRST_RUN = db.query("SELECT * FROM users").get() === null || false;
|
let FIRST_RUN = db.query("SELECT * FROM users").get() === null || false;
|
||||||
|
|
||||||
interface IUser {
|
class User {
|
||||||
id: number;
|
id!: number;
|
||||||
email: string;
|
email!: string;
|
||||||
password: string;
|
password!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFileNames {
|
class Filename {
|
||||||
id: number;
|
id!: number;
|
||||||
job_id: number;
|
job_id!: number;
|
||||||
file_name: string;
|
file_name!: string;
|
||||||
output_file_name: string;
|
output_file_name!: string;
|
||||||
status: string;
|
status!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IJobs {
|
class Jobs {
|
||||||
finished_files: number;
|
finished_files!: number;
|
||||||
id: number;
|
id!: number;
|
||||||
user_id: number;
|
user_id!: number;
|
||||||
date_created: string;
|
date_created!: string;
|
||||||
status: string;
|
status!: string;
|
||||||
num_files: number;
|
num_files!: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable WAL mode
|
// enable WAL mode
|
||||||
|
|
@ -174,36 +174,38 @@ const app = new Elysia({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseHtml title="ConvertX | Register">
|
<BaseHtml title="ConvertX | Register">
|
||||||
<Header accountRegistration={ACCOUNT_REGISTRATION} />
|
<>
|
||||||
<main class="container">
|
<Header accountRegistration={ACCOUNT_REGISTRATION} />
|
||||||
<article>
|
<main class="container">
|
||||||
<form method="post">
|
<article>
|
||||||
<fieldset>
|
<form method="post">
|
||||||
<label>
|
<fieldset>
|
||||||
Email
|
<label>
|
||||||
<input
|
Email
|
||||||
type="email"
|
<input
|
||||||
name="email"
|
type="email"
|
||||||
placeholder="Email"
|
name="email"
|
||||||
autocomplete="email"
|
placeholder="Email"
|
||||||
required
|
autocomplete="email"
|
||||||
/>
|
required
|
||||||
</label>
|
/>
|
||||||
<label>
|
</label>
|
||||||
Password
|
<label>
|
||||||
<input
|
Password
|
||||||
type="password"
|
<input
|
||||||
name="password"
|
type="password"
|
||||||
placeholder="Password"
|
name="password"
|
||||||
autocomplete="new-password"
|
placeholder="Password"
|
||||||
required
|
autocomplete="new-password"
|
||||||
/>
|
required
|
||||||
</label>
|
/>
|
||||||
</fieldset>
|
</label>
|
||||||
<input type="submit" value="Register" />
|
</fieldset>
|
||||||
</form>
|
<input type="submit" value="Register" />
|
||||||
</article>
|
</form>
|
||||||
</main>
|
</article>
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -234,9 +236,17 @@ const app = new Elysia({
|
||||||
savedPassword,
|
savedPassword,
|
||||||
);
|
);
|
||||||
|
|
||||||
const user = (await db
|
const user = db
|
||||||
.query("SELECT * FROM users WHERE email = ?")
|
.query("SELECT * FROM users WHERE email = ?")
|
||||||
.get(body.email)) as IUser;
|
.as(User)
|
||||||
|
.get(body.email);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
set.status = 500;
|
||||||
|
return {
|
||||||
|
message: "Failed to create user.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const accessToken = await jwt.sign({
|
const accessToken = await jwt.sign({
|
||||||
id: String(user.id),
|
id: String(user.id),
|
||||||
|
|
@ -280,52 +290,55 @@ const app = new Elysia({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseHtml title="ConvertX | Login">
|
<BaseHtml title="ConvertX | Login">
|
||||||
<Header accountRegistration={ACCOUNT_REGISTRATION} />
|
<>
|
||||||
<main class="container">
|
<Header accountRegistration={ACCOUNT_REGISTRATION} />
|
||||||
<article>
|
<main class="container">
|
||||||
<form method="post">
|
<article>
|
||||||
<fieldset>
|
<form method="post">
|
||||||
<label>
|
<fieldset>
|
||||||
Email
|
<label>
|
||||||
<input
|
Email
|
||||||
type="email"
|
<input
|
||||||
name="email"
|
type="email"
|
||||||
placeholder="Email"
|
name="email"
|
||||||
autocomplete="email"
|
placeholder="Email"
|
||||||
required
|
autocomplete="email"
|
||||||
/>
|
required
|
||||||
</label>
|
/>
|
||||||
<label>
|
</label>
|
||||||
Password
|
<label>
|
||||||
<input
|
Password
|
||||||
type="password"
|
<input
|
||||||
name="password"
|
type="password"
|
||||||
placeholder="Password"
|
name="password"
|
||||||
autocomplete="current-password"
|
placeholder="Password"
|
||||||
required
|
autocomplete="current-password"
|
||||||
/>
|
required
|
||||||
</label>
|
/>
|
||||||
</fieldset>
|
</label>
|
||||||
<div role="group">
|
</fieldset>
|
||||||
{ACCOUNT_REGISTRATION && (
|
<div role="group">
|
||||||
<a href="/register" role="button" class="secondary">
|
{ACCOUNT_REGISTRATION && (
|
||||||
Register an account
|
<a href="/register" role="button" class="secondary">
|
||||||
</a>
|
Register an account
|
||||||
)}
|
</a>
|
||||||
<input type="submit" value="Login" />
|
)}
|
||||||
</div>
|
<input type="submit" value="Login" />
|
||||||
</form>
|
</div>
|
||||||
</article>
|
</form>
|
||||||
</main>
|
</article>
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.post(
|
.post(
|
||||||
"/login",
|
"/login",
|
||||||
async function handler({ body, set, redirect, jwt, cookie: { auth } }) {
|
async function handler({ body, set, redirect, jwt, cookie: { auth } }) {
|
||||||
const existingUser = (await db
|
const existingUser = await db
|
||||||
.query("SELECT * FROM users WHERE email = ?")
|
.query("SELECT * FROM users WHERE email = ?")
|
||||||
.get(body.email)) as IUser;
|
.as(User)
|
||||||
|
.get(body.email);
|
||||||
|
|
||||||
if (!existingUser) {
|
if (!existingUser) {
|
||||||
set.status = 403;
|
set.status = 403;
|
||||||
|
|
@ -399,9 +412,10 @@ const app = new Elysia({
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure user exists in db
|
// make sure user exists in db
|
||||||
const existingUser = (await db
|
const existingUser = await db
|
||||||
.query("SELECT * FROM users WHERE id = ?")
|
.query("SELECT * FROM users WHERE id = ?")
|
||||||
.get(user.id)) as IUser;
|
.as(User)
|
||||||
|
.get(user.id);
|
||||||
|
|
||||||
if (!existingUser) {
|
if (!existingUser) {
|
||||||
if (auth?.value) {
|
if (auth?.value) {
|
||||||
|
|
@ -438,16 +452,17 @@ const app = new Elysia({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseHtml>
|
<BaseHtml>
|
||||||
<Header loggedIn />
|
<>
|
||||||
<main class="container">
|
<Header loggedIn />
|
||||||
<article>
|
<main class="container">
|
||||||
<h1>Convert</h1>
|
<article>
|
||||||
<div style={{ maxHeight: "50vh", overflowY: "auto" }}>
|
<h1>Convert</h1>
|
||||||
<table id="file-list" class="striped" />
|
<div style={{ maxHeight: "50vh", overflowY: "auto" }}>
|
||||||
</div>
|
<table id="file-list" class="striped" />
|
||||||
<input type="file" name="file" multiple />
|
</div>
|
||||||
{/* <label for="convert_from">Convert from</label> */}
|
<input type="file" name="file" multiple />
|
||||||
{/* <select name="convert_from" aria-label="Convert from" required>
|
{/* <label for="convert_from">Convert from</label> */}
|
||||||
|
{/* <select name="convert_from" aria-label="Convert from" required>
|
||||||
<option selected disabled value="">
|
<option selected disabled value="">
|
||||||
Convert from
|
Convert from
|
||||||
</option>
|
</option>
|
||||||
|
|
@ -456,31 +471,34 @@ const app = new Elysia({
|
||||||
<option>{input}</option>
|
<option>{input}</option>
|
||||||
))}
|
))}
|
||||||
</select> */}
|
</select> */}
|
||||||
</article>
|
|
||||||
<form method="post" action="/convert">
|
|
||||||
<input type="hidden" name="file_names" id="file_names" />
|
|
||||||
<article>
|
|
||||||
<select name="convert_to" aria-label="Convert to" required>
|
|
||||||
<option selected disabled value="">
|
|
||||||
Convert to
|
|
||||||
</option>
|
|
||||||
{Object.entries(getAllTargets()).map(([converter, targets]) => (
|
|
||||||
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
|
||||||
<optgroup label={converter}>
|
|
||||||
{targets.map((target) => (
|
|
||||||
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
|
||||||
<option value={`${target},${converter}`} safe>
|
|
||||||
{target}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</optgroup>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</article>
|
</article>
|
||||||
<input type="submit" value="Convert" />
|
<form method="post" action="/convert">
|
||||||
</form>
|
<input type="hidden" name="file_names" id="file_names" />
|
||||||
</main>
|
<article>
|
||||||
<script src="script.js" defer />
|
<select name="convert_to" aria-label="Convert to" required>
|
||||||
|
<option selected disabled value="">
|
||||||
|
Convert to
|
||||||
|
</option>
|
||||||
|
{Object.entries(getAllTargets()).map(
|
||||||
|
([converter, targets]) => (
|
||||||
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
|
<optgroup label={converter}>
|
||||||
|
{targets.map((target) => (
|
||||||
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
|
<option value={`${target},${converter}`} safe>
|
||||||
|
{target}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</optgroup>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
</article>
|
||||||
|
<input type="submit" value="Convert" />
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
<script src="script.js" defer />
|
||||||
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -604,9 +622,10 @@ const app = new Elysia({
|
||||||
return redirect("/", 302);
|
return redirect("/", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingJob = (await db
|
const existingJob = await db
|
||||||
.query("SELECT * FROM jobs WHERE id = ? AND user_id = ?")
|
.query("SELECT * FROM jobs WHERE id = ? AND user_id = ?")
|
||||||
.get(jobId.value, user.id)) as IJobs;
|
.as(Jobs)
|
||||||
|
.get(jobId.value, user.id);
|
||||||
|
|
||||||
if (!existingJob) {
|
if (!existingJob) {
|
||||||
return redirect("/", 302);
|
return redirect("/", 302);
|
||||||
|
|
@ -635,14 +654,12 @@ const app = new Elysia({
|
||||||
return redirect("/", 302);
|
return redirect("/", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
db.run(
|
db.query(
|
||||||
"UPDATE jobs SET num_files = ?, status = 'pending' WHERE id = ?",
|
"UPDATE jobs SET num_files = ?1, status = 'pending' WHERE id = ?2",
|
||||||
fileNames.length,
|
).run(fileNames.length, jobId.value);
|
||||||
jobId.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
const query = db.query(
|
const query = db.query(
|
||||||
"INSERT INTO file_names (job_id, file_name, output_file_name, status) VALUES (?, ?, ?, ?)",
|
"INSERT INTO file_names (job_id, file_name, output_file_name, status) VALUES (?1, ?2, ?3, ?4)",
|
||||||
);
|
);
|
||||||
|
|
||||||
// Start the conversion process in the background
|
// Start the conversion process in the background
|
||||||
|
|
@ -663,16 +680,18 @@ const app = new Elysia({
|
||||||
{},
|
{},
|
||||||
converterName,
|
converterName,
|
||||||
);
|
);
|
||||||
|
if (jobId.value) {
|
||||||
query.run(jobId.value, fileName, newFileName, result);
|
query.run(jobId.value, fileName, newFileName, result);
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// All conversions are done, update the job status to 'completed'
|
// All conversions are done, update the job status to 'completed'
|
||||||
db.run(
|
if (jobId.value) {
|
||||||
"UPDATE jobs SET status = 'completed' WHERE id = ?",
|
db.query("UPDATE jobs SET status = 'completed' WHERE id = ?1").run(
|
||||||
jobId.value,
|
jobId.value,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// delete all uploaded files in userUploadsDir
|
// delete all uploaded files in userUploadsDir
|
||||||
// rmSync(userUploadsDir, { recursive: true, force: true });
|
// rmSync(userUploadsDir, { recursive: true, force: true });
|
||||||
|
|
@ -703,12 +722,14 @@ const app = new Elysia({
|
||||||
|
|
||||||
let userJobs = db
|
let userJobs = db
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ?")
|
.query("SELECT * FROM jobs WHERE user_id = ?")
|
||||||
.all(user.id) as IJobs[];
|
.as(Jobs)
|
||||||
|
.all(user.id);
|
||||||
|
|
||||||
for (const job of userJobs) {
|
for (const job of userJobs) {
|
||||||
const files = db
|
const files = db
|
||||||
.query("SELECT * FROM file_names WHERE job_id = ?")
|
.query("SELECT * FROM file_names WHERE job_id = ?")
|
||||||
.all(job.id) as IFileNames[];
|
.as(Filename)
|
||||||
|
.all(job.id);
|
||||||
|
|
||||||
job.finished_files = files.length;
|
job.finished_files = files.length;
|
||||||
}
|
}
|
||||||
|
|
@ -718,37 +739,39 @@ const app = new Elysia({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseHtml title="ConvertX | Results">
|
<BaseHtml title="ConvertX | Results">
|
||||||
<Header loggedIn />
|
<>
|
||||||
<main class="container">
|
<Header loggedIn />
|
||||||
<article>
|
<main class="container">
|
||||||
<h1>Results</h1>
|
<article>
|
||||||
<table>
|
<h1>Results</h1>
|
||||||
<thead>
|
<table>
|
||||||
<tr>
|
<thead>
|
||||||
<th>Time</th>
|
|
||||||
<th>Files</th>
|
|
||||||
<th>Files Done</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>View</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{userJobs.map((job) => (
|
|
||||||
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td safe>{job.date_created}</td>
|
<th>Time</th>
|
||||||
<td>{job.num_files}</td>
|
<th>Files</th>
|
||||||
<td>{job.finished_files}</td>
|
<th>Files Done</th>
|
||||||
<td safe>{job.status}</td>
|
<th>Status</th>
|
||||||
<td>
|
<th>View</th>
|
||||||
<a href={`/results/${job.id}`}>View</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{userJobs.map((job) => (
|
||||||
</article>
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
</main>
|
<tr>
|
||||||
|
<td safe>{job.date_created}</td>
|
||||||
|
<td>{job.num_files}</td>
|
||||||
|
<td>{job.finished_files}</td>
|
||||||
|
<td safe>{job.status}</td>
|
||||||
|
<td>
|
||||||
|
<a href={`/results/${job.id}`}>View</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -769,9 +792,10 @@ const app = new Elysia({
|
||||||
return redirect("/login", 302);
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = (await db
|
const job = await db
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
||||||
.get(user.id, params.jobId)) as IJobs;
|
.as(Jobs)
|
||||||
|
.get(user.id, params.jobId);
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
set.status = 404;
|
set.status = 404;
|
||||||
|
|
@ -784,65 +808,68 @@ const app = new Elysia({
|
||||||
|
|
||||||
const files = db
|
const files = db
|
||||||
.query("SELECT * FROM file_names WHERE job_id = ?")
|
.query("SELECT * FROM file_names WHERE job_id = ?")
|
||||||
.all(params.jobId) as IFileNames[];
|
.as(Filename)
|
||||||
|
.all(params.jobId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseHtml title="ConvertX | Result">
|
<BaseHtml title="ConvertX | Result">
|
||||||
<Header loggedIn />
|
<>
|
||||||
<main class="container">
|
<Header loggedIn />
|
||||||
<article>
|
<main class="container">
|
||||||
<div class="grid">
|
<article>
|
||||||
<h1>Results</h1>
|
<div class="grid">
|
||||||
<div>
|
<h1>Results</h1>
|
||||||
<button
|
<div>
|
||||||
type="button"
|
<button
|
||||||
style={{ width: "10rem", float: "right" }}
|
type="button"
|
||||||
onclick="downloadAll()"
|
style={{ width: "10rem", float: "right" }}
|
||||||
{...(files.length !== job.num_files
|
onclick="downloadAll()"
|
||||||
? { disabled: true, "aria-busy": "true" }
|
{...(files.length !== job.num_files
|
||||||
: "")}>
|
? { disabled: true, "aria-busy": "true" }
|
||||||
{files.length === job.num_files
|
: "")}>
|
||||||
? "Download All"
|
{files.length === job.num_files
|
||||||
: "Converting..."}
|
? "Download All"
|
||||||
</button>
|
: "Converting..."}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<progress max={job.num_files} value={files.length} />
|
||||||
<progress max={job.num_files} value={files.length} />
|
<table>
|
||||||
<table>
|
<thead>
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Converted File Name</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>View</th>
|
|
||||||
<th>Download</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{files.map((file) => (
|
|
||||||
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td safe>{file.output_file_name}</td>
|
<th>Converted File Name</th>
|
||||||
<td safe>{file.status}</td>
|
<th>Status</th>
|
||||||
<td>
|
<th>View</th>
|
||||||
<a
|
<th>Download</th>
|
||||||
href={`/download/${outputPath}${file.output_file_name}`}>
|
|
||||||
View
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<a
|
|
||||||
href={`/download/${outputPath}${file.output_file_name}`}
|
|
||||||
download={file.output_file_name}>
|
|
||||||
Download
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{files.map((file) => (
|
||||||
</article>
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
</main>
|
<tr>
|
||||||
<script src="/results.js" defer />
|
<td safe>{file.output_file_name}</td>
|
||||||
|
<td safe>{file.status}</td>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
href={`/download/${outputPath}${file.output_file_name}`}>
|
||||||
|
View
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
href={`/download/${outputPath}${file.output_file_name}`}
|
||||||
|
download={file.output_file_name}>
|
||||||
|
Download
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
<script src="/results.js" defer />
|
||||||
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -864,9 +891,10 @@ const app = new Elysia({
|
||||||
return redirect("/login", 302);
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = (await db
|
const job = await db
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
||||||
.get(user.id, params.jobId)) as IJobs;
|
.as(Jobs)
|
||||||
|
.get(user.id, params.jobId);
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
set.status = 404;
|
set.status = 404;
|
||||||
|
|
@ -879,7 +907,8 @@ const app = new Elysia({
|
||||||
|
|
||||||
const files = db
|
const files = db
|
||||||
.query("SELECT * FROM file_names WHERE job_id = ?")
|
.query("SELECT * FROM file_names WHERE job_id = ?")
|
||||||
.all(params.jobId) as IFileNames[];
|
.as(Filename)
|
||||||
|
.all(params.jobId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article>
|
<article>
|
||||||
|
|
@ -975,50 +1004,54 @@ const app = new Elysia({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseHtml title="ConvertX | Converters">
|
<BaseHtml title="ConvertX | Converters">
|
||||||
<Header loggedIn />
|
<>
|
||||||
<main class="container">
|
<Header loggedIn />
|
||||||
<article>
|
<main class="container">
|
||||||
<h1>Converters</h1>
|
<article>
|
||||||
<table>
|
<h1>Converters</h1>
|
||||||
<thead>
|
<table>
|
||||||
<tr>
|
<thead>
|
||||||
<th>Converter</th>
|
<tr>
|
||||||
<th>From (Count)</th>
|
<th>Converter</th>
|
||||||
<th>To (Count)</th>
|
<th>From (Count)</th>
|
||||||
</tr>
|
<th>To (Count)</th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody>
|
</thead>
|
||||||
{Object.entries(getAllTargets()).map(([converter, targets]) => {
|
<tbody>
|
||||||
const inputs = getAllInputs(converter);
|
{Object.entries(getAllTargets()).map(
|
||||||
return (
|
([converter, targets]) => {
|
||||||
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
const inputs = getAllInputs(converter);
|
||||||
<tr>
|
return (
|
||||||
<td safe>{converter}</td>
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
<td>
|
<tr>
|
||||||
Count: {inputs.length}
|
<td safe>{converter}</td>
|
||||||
<ul>
|
<td>
|
||||||
{inputs.map((input) => (
|
Count: {inputs.length}
|
||||||
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
<ul>
|
||||||
<li safe>{input}</li>
|
{inputs.map((input) => (
|
||||||
))}
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
</ul>
|
<li safe>{input}</li>
|
||||||
</td>
|
))}
|
||||||
<td>
|
</ul>
|
||||||
Count: {targets.length}
|
</td>
|
||||||
<ul>
|
<td>
|
||||||
{targets.map((target) => (
|
Count: {targets.length}
|
||||||
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
<ul>
|
||||||
<li safe>{target}</li>
|
{targets.map((target) => (
|
||||||
))}
|
// biome-ignore lint/correctness/useJsxKeyInIterable: <explanation>
|
||||||
</ul>
|
<li safe>{target}</li>
|
||||||
</td>
|
))}
|
||||||
</tr>
|
</ul>
|
||||||
);
|
</td>
|
||||||
})}
|
</tr>
|
||||||
</tbody>
|
);
|
||||||
</table>
|
},
|
||||||
</article>
|
)}
|
||||||
</main>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
|
@ -1065,7 +1098,8 @@ const clearJobs = () => {
|
||||||
// get all files older than 24 hours
|
// get all files older than 24 hours
|
||||||
const jobs = db
|
const jobs = db
|
||||||
.query("SELECT * FROM jobs WHERE date_created < ?")
|
.query("SELECT * FROM jobs WHERE date_created < ?")
|
||||||
.all(new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()) as IJobs[];
|
.as(Jobs)
|
||||||
|
.all(new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString());
|
||||||
|
|
||||||
for (const job of jobs) {
|
for (const job of jobs) {
|
||||||
// delete the directories
|
// delete the directories
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,6 @@
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"types": [
|
|
||||||
"bun-types" // add Bun global
|
|
||||||
],
|
|
||||||
// non bun init
|
// non bun init
|
||||||
"plugins": [{ "name": "@kitajs/ts-html-plugin" }],
|
"plugins": [{ "name": "@kitajs/ts-html-plugin" }],
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue