Add files via upload
This commit is contained in:
parent
2075e51c44
commit
fdb0c0d7eb
1 changed files with 42 additions and 33 deletions
|
|
@ -1,3 +1,5 @@
|
|||
// @ts-nocheck
|
||||
|
||||
import { Elysia, t } from "elysia";
|
||||
import { BaseHtml } from "../components/base";
|
||||
import { Header } from "../components/header";
|
||||
|
|
@ -12,31 +14,21 @@ import { userService } from "./user";
|
|||
import { outputDir } from "..";
|
||||
import { sendFileToErugo } from "../helpers/erugo";
|
||||
|
||||
type JobsWithOptionalNumFiles = Jobs & {
|
||||
num_files?: number;
|
||||
};
|
||||
|
||||
function ResultsArticle({
|
||||
job,
|
||||
files,
|
||||
outputPath,
|
||||
}: {
|
||||
job: JobsWithOptionalNumFiles;
|
||||
job: Jobs;
|
||||
files: Filename[];
|
||||
outputPath: string;
|
||||
}) {
|
||||
const maxFiles =
|
||||
typeof job.num_files === "number" && Number.isFinite(job.num_files)
|
||||
? Number(job.num_files)
|
||||
: Number(files.length);
|
||||
|
||||
const doneFiles = Number(
|
||||
files.filter((f) => String(f.status ?? "").toLowerCase() === "done").length,
|
||||
);
|
||||
|
||||
const isDone = maxFiles > 0 ? doneFiles >= maxFiles : true;
|
||||
const maxFiles = Number((job as any).num_files ?? 0);
|
||||
const doneFiles = Number(files.filter((f: any) => String((f as any).status || '').toLowerCase() === 'done').length);
|
||||
const isDone = doneFiles === maxFiles;
|
||||
|
||||
const disabledLinkClass = "pointer-events-none opacity-50";
|
||||
const busyAttrs = { disabled: true, "aria-busy": "true" } as const;
|
||||
|
||||
return (
|
||||
<article class="article">
|
||||
|
|
@ -49,7 +41,7 @@ function ResultsArticle({
|
|||
!isDone ? disabledLinkClass : ""
|
||||
}`}
|
||||
href={`${WEBROOT}/delete/${job.id}`}
|
||||
aria-disabled={!isDone ? "true" : undefined}
|
||||
{...(!isDone ? busyAttrs : {})}
|
||||
>
|
||||
<DeleteIcon /> <p>Delete</p>
|
||||
</a>
|
||||
|
|
@ -59,8 +51,8 @@ function ResultsArticle({
|
|||
!isDone ? disabledLinkClass : ""
|
||||
}`}
|
||||
href={`${WEBROOT}/archive/${job.id}`}
|
||||
download
|
||||
aria-disabled={!isDone ? "true" : undefined}
|
||||
download={`converted_files_${job.id}.tar`}
|
||||
{...(!isDone ? busyAttrs : {})}
|
||||
>
|
||||
<DownloadIcon /> <p>Tar</p>
|
||||
</a>
|
||||
|
|
@ -69,8 +61,7 @@ function ResultsArticle({
|
|||
id="cxDownloadAll"
|
||||
type="button"
|
||||
class="flex btn-primary flex-row gap-2 text-contrast"
|
||||
disabled={!isDone ? "true" : undefined}
|
||||
aria-busy={!isDone ? "true" : undefined}
|
||||
{...(!isDone ? busyAttrs : {})}
|
||||
>
|
||||
<DownloadIcon /> <p>All</p>
|
||||
</button>
|
||||
|
|
@ -124,7 +115,7 @@ function ResultsArticle({
|
|||
<a
|
||||
class="text-accent-500 hover:text-accent-400"
|
||||
href={`${WEBROOT}/download/${outputPath}${file.output_file_name}`}
|
||||
download
|
||||
download={file.output_file_name}
|
||||
>
|
||||
<DownloadIcon />
|
||||
</a>
|
||||
|
|
@ -205,7 +196,7 @@ function ResultsArticle({
|
|||
<textarea
|
||||
id="cxShareDescription"
|
||||
class="w-full rounded border border-neutral-700 bg-neutral-950 px-3 py-2 text-neutral-100 outline-none focus:border-accent-500"
|
||||
rows="3"
|
||||
rows={3}
|
||||
placeholder="Message to share recipients (optional)"
|
||||
></textarea>
|
||||
</div>
|
||||
|
|
@ -214,7 +205,7 @@ function ResultsArticle({
|
|||
<button
|
||||
id="cxShareSubmit"
|
||||
type="button"
|
||||
class="flex btn-primary items-center gap-2"
|
||||
class="btn-primary flex items-center gap-2"
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
|
|
@ -252,6 +243,11 @@ function ResultsArticle({
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPORTANT FIX:
|
||||
* Results HTML is re-rendered via /progress/:jobId and replaces the article + modal.
|
||||
* So we MUST NOT keep stale DOM references. We re-query elements on each action.
|
||||
*/
|
||||
const shareJs = `
|
||||
(function () {
|
||||
const WEBROOT = ${JSON.stringify(WEBROOT)};
|
||||
|
|
@ -310,11 +306,13 @@ const shareJs = `
|
|||
currentFileName = null;
|
||||
}
|
||||
|
||||
// Delegated: always works even after /progress replaces the article
|
||||
document.addEventListener("click", (e) => {
|
||||
const t = e.target;
|
||||
const btn = t && (t.closest ? t.closest('[data-share="true"]') : null);
|
||||
if (!btn) return;
|
||||
|
||||
// kill old handlers (e.g. alert() from older results.js)
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (e.stopImmediatePropagation) e.stopImmediatePropagation();
|
||||
|
|
@ -324,6 +322,7 @@ const shareJs = `
|
|||
openModal(jobId, fileName);
|
||||
}, true);
|
||||
|
||||
// Delegated close (because modal DOM is replaced during progress polling)
|
||||
document.addEventListener("click", (e) => {
|
||||
const id = e.target && e.target.id;
|
||||
if (id === "cxShareClose" || id === "cxShareCancel") {
|
||||
|
|
@ -331,6 +330,7 @@ const shareJs = `
|
|||
closeModal();
|
||||
}
|
||||
if (id === "cxShareModal") {
|
||||
// click outside dialog closes
|
||||
closeModal();
|
||||
}
|
||||
}, true);
|
||||
|
|
@ -340,6 +340,7 @@ const shareJs = `
|
|||
if (e.key === "Escape" && r.modal && !r.modal.classList.contains("hidden")) closeModal();
|
||||
});
|
||||
|
||||
// Delegated copy
|
||||
document.addEventListener("click", async (e) => {
|
||||
const t = e.target;
|
||||
if (!t || t.id !== "cxShareCopy") return;
|
||||
|
|
@ -358,6 +359,7 @@ const shareJs = `
|
|||
}
|
||||
}, true);
|
||||
|
||||
// Delegated submit
|
||||
document.addEventListener("click", async (e) => {
|
||||
const t = e.target;
|
||||
if (!t || t.id !== "cxShareSubmit") return;
|
||||
|
|
@ -401,13 +403,13 @@ const shareJs = `
|
|||
}
|
||||
|
||||
const url =
|
||||
(json && typeof json === "object" && json.share_url) ||
|
||||
(json && typeof json === "object" && json.share_link) ||
|
||||
(json && typeof json === "object" && json.data && json.data.url) ||
|
||||
(json && typeof json === "object" && json.data && json.data.share && json.data.share.url) ||
|
||||
json?.share_url ||
|
||||
json?.share_link ||
|
||||
json?.data?.url ||
|
||||
json?.data?.share?.url ||
|
||||
null;
|
||||
|
||||
if (url && typeof url === "string") {
|
||||
if (url) {
|
||||
r.linkEl.value = url;
|
||||
r.linkBlock.classList.remove("hidden");
|
||||
}
|
||||
|
|
@ -424,6 +426,7 @@ const shareJs = `
|
|||
}
|
||||
}, true);
|
||||
|
||||
// Download All: delegated, because button is replaced during progress polling
|
||||
document.addEventListener("click", (e) => {
|
||||
const t = e.target;
|
||||
const btn = t && (t.closest ? t.closest("#cxDownloadAll") : null);
|
||||
|
|
@ -445,6 +448,7 @@ const shareJs = `
|
|||
|
||||
export const results = new Elysia()
|
||||
.use(userService)
|
||||
|
||||
.get(
|
||||
"/results-share.js",
|
||||
() =>
|
||||
|
|
@ -456,6 +460,7 @@ export const results = new Elysia()
|
|||
}),
|
||||
{ auth: true },
|
||||
)
|
||||
|
||||
.get(
|
||||
"/results/:jobId",
|
||||
async ({ params, set, cookie: { job_id }, user }) => {
|
||||
|
|
@ -464,7 +469,7 @@ export const results = new Elysia()
|
|||
const job = db
|
||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
||||
.as(Jobs)
|
||||
.get(user.id, params.jobId) as JobsWithOptionalNumFiles | null;
|
||||
.get(user.id, params.jobId);
|
||||
|
||||
if (!job) {
|
||||
set.status = 404;
|
||||
|
|
@ -491,10 +496,10 @@ export const results = new Elysia()
|
|||
<ResultsArticle job={job} files={files} outputPath={outputPath} />
|
||||
</main>
|
||||
|
||||
{/* existing file */}
|
||||
{/* keep existing file */}
|
||||
<script src={`${WEBROOT}/results.js`} defer />
|
||||
|
||||
{/* override handlers */}
|
||||
{/* our override must also load */}
|
||||
<script src={`${WEBROOT}/results-share.js`} defer />
|
||||
</>
|
||||
</BaseHtml>
|
||||
|
|
@ -502,6 +507,7 @@ export const results = new Elysia()
|
|||
},
|
||||
{ auth: true },
|
||||
)
|
||||
|
||||
.post(
|
||||
"/progress/:jobId",
|
||||
async ({ set, params, cookie: { job_id }, user }) => {
|
||||
|
|
@ -510,7 +516,7 @@ export const results = new Elysia()
|
|||
const job = db
|
||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
||||
.as(Jobs)
|
||||
.get(user.id, params.jobId) as JobsWithOptionalNumFiles | null;
|
||||
.get(user.id, params.jobId);
|
||||
|
||||
if (!job) {
|
||||
set.status = 404;
|
||||
|
|
@ -528,6 +534,7 @@ export const results = new Elysia()
|
|||
},
|
||||
{ auth: true },
|
||||
)
|
||||
|
||||
.post(
|
||||
"/share-to-erugo/:jobId",
|
||||
async ({ params, body, user, set }) => {
|
||||
|
|
@ -570,7 +577,7 @@ export const results = new Elysia()
|
|||
|
||||
const result = await sendFileToErugo(payload);
|
||||
return result;
|
||||
} catch (err: unknown) {
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
set.status = 500;
|
||||
return { message: "Failed to share with Erugo" };
|
||||
|
|
@ -586,3 +593,5 @@ export const results = new Elysia()
|
|||
}),
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue