fix download
This commit is contained in:
parent
13cc37d5a2
commit
61a75c6d36
13 changed files with 198 additions and 27 deletions
|
|
@ -6,6 +6,24 @@ export const BaseHtml = ({ children, title = "ConvertX" }) => (
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<link rel="stylesheet" href="/pico.lime.min.css" />
|
<link rel="stylesheet" href="/pico.lime.min.css" />
|
||||||
<link rel="stylesheet" href="/style.css" />
|
<link rel="stylesheet" href="/style.css" />
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
<script src="https://unpkg.com/htmx.org@1.9.12" />
|
<script src="https://unpkg.com/htmx.org@1.9.12" />
|
||||||
</head>
|
</head>
|
||||||
<body>{children}</body>
|
<body>{children}</body>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ export const Header = ({ loggedIn }: { loggedIn?: boolean }) => {
|
||||||
rightNav = (
|
rightNav = (
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/results">History</a>
|
<a href="/history">History</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="/logout">Logout</a>
|
<a href="/logout">Logout</a>
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,12 @@ export async function mainConverter(
|
||||||
|
|
||||||
const possibleConversions: { [key: string]: string[] } = {};
|
const possibleConversions: { [key: string]: string[] } = {};
|
||||||
|
|
||||||
for (const from of [...propertiesImage.from, ...propertiesPandoc.from]) {
|
for (const from of propertiesImage.from) {
|
||||||
possibleConversions[from] = [...propertiesImage.to, ...propertiesPandoc.to];
|
possibleConversions[from] = propertiesImage.to;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const from of propertiesPandoc.from) {
|
||||||
|
possibleConversions[from] = propertiesPandoc.to;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getPossibleConversions = (from: string): string[] => {
|
export const getPossibleConversions = (from: string): string[] => {
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,128 @@ import { exec } from "node:child_process";
|
||||||
|
|
||||||
export const properties = {
|
export const properties = {
|
||||||
from: [
|
from: [
|
||||||
"md", "html", "docx", "pdf", "tex", "txt", "bibtex", "biblatex", "commonmark", "commonmark_x", "creole", "csljson", "csv", "tsv", "docbook", "dokuwiki", "endnotexml", "epub", "fb2", "gfm", "haddock", "ipynb", "jats", "jira", "json", "latex", "markdown", "markdown_mmd", "markdown_phpextra", "markdown_strict", "mediawiki", "man", "muse", "native", "odt", "opml", "org", "ris", "rtf", "rst", "t2t", "textile", "tikiwiki", "twiki", "vimwiki"
|
"textile",
|
||||||
|
"tikiwiki",
|
||||||
|
"tsv",
|
||||||
|
"twiki",
|
||||||
|
"typst",
|
||||||
|
"vimwiki",
|
||||||
|
"biblatex",
|
||||||
|
"bibtex",
|
||||||
|
"bits",
|
||||||
|
"commonmark",
|
||||||
|
"commonmark_x",
|
||||||
|
"creole",
|
||||||
|
"csljson",
|
||||||
|
"csv",
|
||||||
|
"djot",
|
||||||
|
"docbook",
|
||||||
|
"docx",
|
||||||
|
"dokuwiki",
|
||||||
|
"endnotexml",
|
||||||
|
"epub",
|
||||||
|
"fb2",
|
||||||
|
"gfm",
|
||||||
|
"haddock",
|
||||||
|
"html",
|
||||||
|
"ipynb",
|
||||||
|
"jats",
|
||||||
|
"jira",
|
||||||
|
"json",
|
||||||
|
"latex",
|
||||||
|
"man",
|
||||||
|
"markdown",
|
||||||
|
"markdown_mmd",
|
||||||
|
"markdown_phpextra",
|
||||||
|
"markdown_strict",
|
||||||
|
"mediawiki",
|
||||||
|
"muse",
|
||||||
|
"pandoc native",
|
||||||
|
"opml",
|
||||||
|
"org",
|
||||||
|
"ris",
|
||||||
|
"rst",
|
||||||
|
"rtf",
|
||||||
|
"t2t",
|
||||||
],
|
],
|
||||||
to: [
|
to: [
|
||||||
"asciidoc", "asciidoctor", "beamer", "bibtex", "biblatex", "commonmark", "commonmark_x", "context", "csljson", "docbook", "docbook4", "docbook5", "docx", "dokuwiki", "epub", "epub3", "epub2", "fb2", "gfm", "haddock", "html", "html5", "html4", "icml", "ipynb", "jats_archiving", "jats_articleauthoring", "jats_publishing", "jats", "jira", "json", "latex", "man", "markdown", "markdown_mmd", "markdown_phpextra", "markdown_strict", "markua", "mediawiki", "ms", "muse", "native", "odt", "opml", "opendocument", "org", "pdf", "plain", "pptx", "rst", "rtf", "texinfo", "textile", "slideous", "slidy", "dzslides", "revealjs", "s5", "tei", "xwiki", "zimwiki"
|
"tei",
|
||||||
]
|
"texinfo",
|
||||||
|
"textile",
|
||||||
|
"typst",
|
||||||
|
"xwiki",
|
||||||
|
"zimwiki",
|
||||||
|
"asciidoc",
|
||||||
|
"asciidoc_legacy",
|
||||||
|
"asciidoctor",
|
||||||
|
"beamer",
|
||||||
|
"biblatex",
|
||||||
|
"bibtex",
|
||||||
|
"chunkedhtml",
|
||||||
|
"commonmark",
|
||||||
|
"commonmark_x",
|
||||||
|
"context",
|
||||||
|
"csljson",
|
||||||
|
"djot",
|
||||||
|
"docbook",
|
||||||
|
"docbook4",
|
||||||
|
"docbook5",
|
||||||
|
"docx",
|
||||||
|
"dokuwiki",
|
||||||
|
"dzslides",
|
||||||
|
"epub",
|
||||||
|
"epub2",
|
||||||
|
"epub3",
|
||||||
|
"fb2",
|
||||||
|
"gfm",
|
||||||
|
"haddock",
|
||||||
|
"html",
|
||||||
|
"html4",
|
||||||
|
"html5",
|
||||||
|
"icml",
|
||||||
|
"ipynb",
|
||||||
|
"jats",
|
||||||
|
"jats_archiving",
|
||||||
|
"jats_articleauthoring",
|
||||||
|
"jats_publishing",
|
||||||
|
"jira",
|
||||||
|
"json",
|
||||||
|
"latex",
|
||||||
|
"man",
|
||||||
|
"markdown",
|
||||||
|
"markdown_mmd",
|
||||||
|
"markdown_phpextra",
|
||||||
|
"markdown_strict",
|
||||||
|
"markua",
|
||||||
|
"mediawiki",
|
||||||
|
"ms",
|
||||||
|
"muse",
|
||||||
|
"pandoc native",
|
||||||
|
"odt",
|
||||||
|
"opendocument",
|
||||||
|
"opml",
|
||||||
|
"org",
|
||||||
|
"pdf",
|
||||||
|
"plain",
|
||||||
|
"pptx",
|
||||||
|
"revealjs",
|
||||||
|
"rst",
|
||||||
|
"rtf",
|
||||||
|
"s5",
|
||||||
|
"slideous",
|
||||||
|
"slidy",
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||||
export function convert(filePath: string, fileType: string, convertTo: string, targetPath: string, options?: any) {
|
export function convert(
|
||||||
|
filePath: string,
|
||||||
|
fileType: string,
|
||||||
|
convertTo: string,
|
||||||
|
targetPath: string,
|
||||||
|
options?: any,
|
||||||
|
) {
|
||||||
return exec(
|
return exec(
|
||||||
`pandoc ${filePath} -f ${fileType} -t ${convertTo} -o ${targetPath}`,
|
`pandoc "${filePath}" -f ${fileType} -t ${convertTo} -o "${targetPath}"`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -178,7 +178,20 @@ const app = new Elysia()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.get("/login", () => {
|
.get("/login", async ({ jwt, redirect, cookie: { auth } }) => {
|
||||||
|
console.log("login handler");
|
||||||
|
// if already logged in, redirect to home
|
||||||
|
if (auth?.value) {
|
||||||
|
const user = await jwt.verify(auth.value);
|
||||||
|
console.log(user);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
return redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
auth.remove();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseHtml title="ConvertX | Login">
|
<BaseHtml title="ConvertX | Login">
|
||||||
<Header />
|
<Header />
|
||||||
|
|
@ -220,15 +233,6 @@ const app = new Elysia()
|
||||||
.post(
|
.post(
|
||||||
"/login",
|
"/login",
|
||||||
async function handler({ body, set, redirect, jwt, cookie: { auth } }) {
|
async function handler({ body, set, redirect, jwt, cookie: { auth } }) {
|
||||||
// if already logged in, redirect to home
|
|
||||||
if (auth?.value) {
|
|
||||||
const user = await jwt.verify(auth.value);
|
|
||||||
if (user) {
|
|
||||||
return redirect("/");
|
|
||||||
}
|
|
||||||
auth.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
.get(body.email)) as IUser;
|
||||||
|
|
@ -514,7 +518,7 @@ const app = new Elysia()
|
||||||
return redirect(`/results/${jobId.value}`);
|
return redirect(`/results/${jobId.value}`);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.get("/histt", async ({ jwt, redirect, cookie: { auth } }) => {
|
.get("/history", async ({ jwt, redirect, cookie: { auth } }) => {
|
||||||
console.log("results page");
|
console.log("results page");
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
console.log("no auth value");
|
console.log("no auth value");
|
||||||
|
|
@ -549,7 +553,7 @@ const app = new Elysia()
|
||||||
return (
|
return (
|
||||||
<BaseHtml title="ConvertX | Results">
|
<BaseHtml title="ConvertX | Results">
|
||||||
<Header loggedIn />
|
<Header loggedIn />
|
||||||
<main class="container-fluid">
|
<main class="container">
|
||||||
<article>
|
<article>
|
||||||
<h1>Results</h1>
|
<h1>Results</h1>
|
||||||
<table>
|
<table>
|
||||||
|
|
@ -619,7 +623,7 @@ const app = new Elysia()
|
||||||
return (
|
return (
|
||||||
<BaseHtml title="ConvertX | Result">
|
<BaseHtml title="ConvertX | Result">
|
||||||
<Header loggedIn />
|
<Header loggedIn />
|
||||||
<main class="container-fluid">
|
<main class="container">
|
||||||
<article>
|
<article>
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<h1>Results</h1>
|
<h1>Results</h1>
|
||||||
|
|
@ -690,8 +694,12 @@ const app = new Elysia()
|
||||||
if (!job) {
|
if (!job) {
|
||||||
return redirect("/results");
|
return redirect("/results");
|
||||||
}
|
}
|
||||||
|
// parse from url encoded string
|
||||||
|
const userId = decodeURIComponent(params.userId);
|
||||||
|
const jobId = decodeURIComponent(params.jobId);
|
||||||
|
const fileName = decodeURIComponent(params.fileName);
|
||||||
|
|
||||||
const filePath = `${outputDir}${params.userId}/${params.jobId}/${params.fileName}`;
|
const filePath = `${outputDir}${userId}/${jobId}/${fileName}`;
|
||||||
return Bun.file(filePath);
|
return Bun.file(filePath);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
BIN
src/public/android-chrome-192x192.png
Normal file
BIN
src/public/android-chrome-192x192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
BIN
src/public/android-chrome-512x512.png
Normal file
BIN
src/public/android-chrome-512x512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
src/public/apple-touch-icon.png
Normal file
BIN
src/public/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
BIN
src/public/favicon-16x16.png
Normal file
BIN
src/public/favicon-16x16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 476 B |
BIN
src/public/favicon-32x32.png
Normal file
BIN
src/public/favicon-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 960 B |
BIN
src/public/favicon.ico
Normal file
BIN
src/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -28,9 +28,7 @@ fileInput.addEventListener("change", (e) => {
|
||||||
fileType = file.name.split(".").pop();
|
fileType = file.name.split(".").pop();
|
||||||
console.log(file.type);
|
console.log(file.type);
|
||||||
fileInput.setAttribute("accept", `.${fileType}`);
|
fileInput.setAttribute("accept", `.${fileType}`);
|
||||||
|
setTitle();
|
||||||
const title = document.querySelector("h1");
|
|
||||||
title.textContent = `Convert .${fileType}`;
|
|
||||||
|
|
||||||
fetch("/conversions", {
|
fetch("/conversions", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
@ -57,6 +55,11 @@ fileInput.addEventListener("change", (e) => {
|
||||||
uploadFiles(files);
|
uploadFiles(files);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const setTitle = () => {
|
||||||
|
const title = document.querySelector("h1");
|
||||||
|
title.textContent = `Convert ${fileType ? `.${fileType}` : "___"}`;
|
||||||
|
};
|
||||||
|
|
||||||
// Add a onclick for the delete button
|
// Add a onclick for the delete button
|
||||||
const deleteRow = (target) => {
|
const deleteRow = (target) => {
|
||||||
const filename = target.parentElement.parentElement.children[0].textContent;
|
const filename = target.parentElement.parentElement.children[0].textContent;
|
||||||
|
|
@ -67,6 +70,13 @@ const deleteRow = (target) => {
|
||||||
const index = fileNames.indexOf(filename);
|
const index = fileNames.indexOf(filename);
|
||||||
fileNames.splice(index, 1);
|
fileNames.splice(index, 1);
|
||||||
|
|
||||||
|
// if fileNames is empty, reset fileType
|
||||||
|
if (fileNames.length === 0) {
|
||||||
|
fileType = null;
|
||||||
|
fileInput.removeAttribute("accept");
|
||||||
|
setTitle();
|
||||||
|
}
|
||||||
|
|
||||||
fetch("/delete", {
|
fetch("/delete", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ filename: filename }),
|
body: JSON.stringify({ filename: filename }),
|
||||||
|
|
|
||||||
19
src/public/site.webmanifest
Normal file
19
src/public/site.webmanifest
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "ConvertX | Self Hosted File Converter",
|
||||||
|
"short_name": "ConvertX",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#a5d601",
|
||||||
|
"background_color": "#13171f",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue