chore: format files
This commit is contained in:
parent
858ee28ef2
commit
81f109f830
15 changed files with 77 additions and 63 deletions
7
.github/ISSUE_TEMPLATE/bug_report.md
vendored
7
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
|
@ -1,10 +1,9 @@
|
||||||
---
|
---
|
||||||
name: Bug report
|
name: Bug report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ""
|
||||||
labels: bug
|
labels: bug
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
|
|
@ -12,10 +11,12 @@ A clear and concise description of what the bug is.
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
|
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '....'
|
2. Click on '....'
|
||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
4. See error
|
4. See error
|
||||||
|
|
||||||
**Checklist:**
|
**Checklist:**
|
||||||
|
|
||||||
- [ ] I am accessing ConvertX over HTTPS or have `HTTP_ALLOWED=true`
|
- [ ] I am accessing ConvertX over HTTPS or have `HTTP_ALLOWED=true`
|
||||||
|
|
|
||||||
10
.github/ISSUE_TEMPLATE/converter_request.md
vendored
10
.github/ISSUE_TEMPLATE/converter_request.md
vendored
|
|
@ -3,20 +3,24 @@ name: Converter request
|
||||||
about: Suggest an converter for this project
|
about: Suggest an converter for this project
|
||||||
title: "[Converter Request]"
|
title: "[Converter Request]"
|
||||||
labels: "converter request"
|
labels: "converter request"
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**What file formats is missing?**
|
**What file formats is missing?**
|
||||||
|
|
||||||
<!-- Provide an example of what you would like to convert -->
|
<!-- Provide an example of what you would like to convert -->
|
||||||
|
|
||||||
**What converter should be added**
|
**What converter should be added**
|
||||||
|
|
||||||
<!-- It has to be free and preferably open source -->
|
<!-- It has to be free and preferably open source -->
|
||||||
|
|
||||||
**Are you willing to add it?**
|
**Are you willing to add it?**
|
||||||
|
|
||||||
<!-- Adding a converter is very easy just copy one of the existing and modify it -->
|
<!-- Adding a converter is very easy just copy one of the existing and modify it -->
|
||||||
|
|
||||||
- [ ] Yes
|
- [ ] Yes
|
||||||
- [ ] No
|
- [ ] No
|
||||||
|
|
||||||
**Additional context**
|
**Additional context**
|
||||||
<!-- Add any other context or screenshots about the feature request here. -->
|
|
||||||
|
<!-- Add any other context or screenshots about the feature request here. -->
|
||||||
|
|
|
||||||
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
3
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
|
@ -3,8 +3,7 @@ name: Feature request
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project
|
||||||
title: "[Feature Request]"
|
title: "[Feature Request]"
|
||||||
labels: enhancement
|
labels: enhancement
|
||||||
assignees: ''
|
assignees: ""
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
**Describe the solution you'd like**
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,9 @@
|
||||||
|
|
||||||
## [0.14.1](https://github.com/C4illin/ConvertX/compare/v0.14.0...v0.14.1) (2025-06-04)
|
## [0.14.1](https://github.com/C4illin/ConvertX/compare/v0.14.0...v0.14.1) (2025-06-04)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* change to baseline build ([6ea3058](https://github.com/C4illin/ConvertX/commit/6ea3058e66262f7a14633bddcecd5573948f524a)), closes [#311](https://github.com/C4illin/ConvertX/issues/311)
|
- change to baseline build ([6ea3058](https://github.com/C4illin/ConvertX/commit/6ea3058e66262f7a14633bddcecd5573948f524a)), closes [#311](https://github.com/C4illin/ConvertX/issues/311)
|
||||||
|
|
||||||
## [0.14.0](https://github.com/C4illin/ConvertX/compare/v0.13.0...v0.14.0) (2025-06-03)
|
## [0.14.0](https://github.com/C4illin/ConvertX/compare/v0.13.0...v0.14.0) (2025-06-03)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,6 @@ All are optional, JWT_SECRET is recommended to be set.
|
||||||
| LANGUAGE | en | Language to format date strings in, specified as a [BCP 47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag) |
|
| LANGUAGE | en | Language to format date strings in, specified as a [BCP 47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag) |
|
||||||
| UNAUTHENTICATED_USER_SHARING | false | Shares conversion history between all unauthenticated users |
|
| UNAUTHENTICATED_USER_SHARING | false | Shares conversion history between all unauthenticated users |
|
||||||
|
|
||||||
|
|
||||||
### Docker images
|
### Docker images
|
||||||
|
|
||||||
There is a `:latest` tag that is updated with every release and a `:main` tag that is updated with every push to the main branch. `:latest` is recommended for normal use.
|
There is a `:latest` tag that is updated with every release and a `:main` tag that is updated with every push to the main branch. `:latest` is recommended for normal use.
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,5 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"automerge": true
|
"automerge": true
|
||||||
},
|
},
|
||||||
"ignoreDeps": [
|
"ignoreDeps": ["bun-types", "@types/bun"]
|
||||||
"bun-types",
|
|
||||||
"@types/bun"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,24 +66,20 @@ export async function convert(
|
||||||
options?: unknown,
|
options?: unknown,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
execFile(
|
execFile("ebook-convert", [filePath, targetPath], (error, stdout, stderr) => {
|
||||||
"ebook-convert",
|
if (error) {
|
||||||
[filePath, targetPath],
|
reject(`error: ${error}`);
|
||||||
(error, stdout, stderr) => {
|
}
|
||||||
if (error) {
|
|
||||||
reject(`error: ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stdout) {
|
if (stdout) {
|
||||||
console.log(`stdout: ${stdout}`);
|
console.log(`stdout: ${stdout}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stderr) {
|
if (stderr) {
|
||||||
console.error(`stderr: ${stderr}`);
|
console.error(`stderr: ${stderr}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve("Done");
|
resolve("Done");
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Cookie } from "elysia";
|
||||||
import db from "../db/db";
|
import db from "../db/db";
|
||||||
import { MAX_CONVERT_PROCESS } from "../helpers/env";
|
import { MAX_CONVERT_PROCESS } from "../helpers/env";
|
||||||
import { normalizeFiletype, normalizeOutputFiletype } from "../helpers/normalizeFiletype";
|
import { normalizeFiletype, normalizeOutputFiletype } from "../helpers/normalizeFiletype";
|
||||||
|
|
@ -119,11 +120,11 @@ const properties: Record<
|
||||||
};
|
};
|
||||||
|
|
||||||
function chunks<T>(arr: T[], size: number): T[][] {
|
function chunks<T>(arr: T[], size: number): T[][] {
|
||||||
if(size <= 0){
|
if (size <= 0) {
|
||||||
return [arr]
|
return [arr];
|
||||||
}
|
}
|
||||||
return Array.from({ length: Math.ceil(arr.length / size) }, (_: T, i: number) =>
|
return Array.from({ length: Math.ceil(arr.length / size) }, (_: T, i: number) =>
|
||||||
arr.slice(i * size, i * size + size)
|
arr.slice(i * size, i * size + size),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,17 +134,15 @@ export async function handleConvert(
|
||||||
userOutputDir: string,
|
userOutputDir: string,
|
||||||
convertTo: string,
|
convertTo: string,
|
||||||
converterName: string,
|
converterName: string,
|
||||||
jobId: any
|
jobId: Cookie<string | undefined>,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
const query = db.query(
|
const query = db.query(
|
||||||
"INSERT INTO file_names (job_id, file_name, output_file_name, status) VALUES (?1, ?2, ?3, ?4)",
|
"INSERT INTO file_names (job_id, file_name, output_file_name, status) VALUES (?1, ?2, ?3, ?4)",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
for (const chunk of chunks(fileNames, MAX_CONVERT_PROCESS)) {
|
for (const chunk of chunks(fileNames, MAX_CONVERT_PROCESS)) {
|
||||||
const toProcess: Promise<string>[] = [];
|
const toProcess: Promise<string>[] = [];
|
||||||
for(const fileName of chunk) {
|
for (const fileName of chunk) {
|
||||||
const filePath = `${userUploadsDir}${fileName}`;
|
const filePath = `${userUploadsDir}${fileName}`;
|
||||||
const fileTypeOrig = fileName.split(".").pop() ?? "";
|
const fileTypeOrig = fileName.split(".").pop() ?? "";
|
||||||
const fileType = normalizeFiletype(fileTypeOrig);
|
const fileType = normalizeFiletype(fileTypeOrig);
|
||||||
|
|
@ -154,21 +153,16 @@ export async function handleConvert(
|
||||||
);
|
);
|
||||||
const targetPath = `${userOutputDir}${newFileName}`;
|
const targetPath = `${userOutputDir}${newFileName}`;
|
||||||
toProcess.push(
|
toProcess.push(
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
mainConverter(
|
mainConverter(filePath, fileType, convertTo, targetPath, {}, converterName)
|
||||||
filePath,
|
.then((r) => {
|
||||||
fileType,
|
|
||||||
convertTo,
|
|
||||||
targetPath,
|
|
||||||
{},
|
|
||||||
converterName,
|
|
||||||
).then(r => {
|
|
||||||
if (jobId.value) {
|
if (jobId.value) {
|
||||||
query.run(jobId.value, fileName, newFileName, r);
|
query.run(jobId.value, fileName, newFileName, r);
|
||||||
}
|
}
|
||||||
resolve(r);
|
resolve(r);
|
||||||
}).catch(c => reject(c));
|
})
|
||||||
})
|
.catch((c) => reject(c));
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await Promise.all(toProcess);
|
await Promise.all(toProcess);
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export function convert(
|
||||||
// msgconvert will output to the same directory as the input file with .eml extension
|
// msgconvert will output to the same directory as the input file with .eml extension
|
||||||
// We need to use --outfile to specify the target path
|
// We need to use --outfile to specify the target path
|
||||||
const args = ["--outfile", targetPath, filePath];
|
const args = ["--outfile", targetPath, filePath];
|
||||||
|
|
||||||
execFile("msgconvert", args, (error, stdout, stderr) => {
|
execFile("msgconvert", args, (error, stdout, stderr) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(new Error(`msgconvert failed: ${error.message}`));
|
reject(new Error(`msgconvert failed: ${error.message}`));
|
||||||
|
|
@ -33,13 +33,19 @@ export function convert(
|
||||||
if (stderr) {
|
if (stderr) {
|
||||||
// Log sanitized stderr to avoid exposing sensitive paths
|
// Log sanitized stderr to avoid exposing sensitive paths
|
||||||
const sanitizedStderr = stderr.replace(/(\/[^\s]+)/g, "[REDACTED_PATH]");
|
const sanitizedStderr = stderr.replace(/(\/[^\s]+)/g, "[REDACTED_PATH]");
|
||||||
console.warn(`msgconvert stderr: ${sanitizedStderr.length > 200 ? sanitizedStderr.slice(0, 200) + '...' : sanitizedStderr}`);
|
console.warn(
|
||||||
|
`msgconvert stderr: ${sanitizedStderr.length > 200 ? sanitizedStderr.slice(0, 200) + "..." : sanitizedStderr}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(targetPath);
|
resolve(targetPath);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
reject(new Error(`Unsupported conversion from ${fileType} to ${convertTo}. Only MSG to EML conversion is currently supported.`));
|
reject(
|
||||||
|
new Error(
|
||||||
|
`Unsupported conversion from ${fileType} to ${convertTo}. Only MSG to EML conversion is currently supported.`,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,10 @@ export const WEBROOT = process.env.WEBROOT ?? "";
|
||||||
|
|
||||||
export const LANGUAGE = process.env.LANGUAGE?.toLowerCase() || "en";
|
export const LANGUAGE = process.env.LANGUAGE?.toLowerCase() || "en";
|
||||||
|
|
||||||
export const MAX_CONVERT_PROCESS = process.env.MAX_CONVERT_PROCESS && Number(process.env.MAX_CONVERT_PROCESS) > 0 ? Number(process.env.MAX_CONVERT_PROCESS) : 0
|
export const MAX_CONVERT_PROCESS =
|
||||||
|
process.env.MAX_CONVERT_PROCESS && Number(process.env.MAX_CONVERT_PROCESS) > 0
|
||||||
|
? Number(process.env.MAX_CONVERT_PROCESS)
|
||||||
|
: 0;
|
||||||
|
|
||||||
export const UNAUTHENTICATED_USER_SHARING =
|
export const UNAUTHENTICATED_USER_SHARING =
|
||||||
process.env.UNAUTHENTICATED_USER_SHARING?.toLowerCase() === "true" || false;
|
process.env.UNAUTHENTICATED_USER_SHARING?.toLowerCase() === "true" || false;
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,11 @@ export const convert = new Elysia().use(userService).post(
|
||||||
|
|
||||||
const convertTo = normalizeFiletype(body.convert_to.split(",")[0] ?? "");
|
const convertTo = normalizeFiletype(body.convert_to.split(",")[0] ?? "");
|
||||||
const converterName = body.convert_to.split(",")[1];
|
const converterName = body.convert_to.split(",")[1];
|
||||||
|
|
||||||
|
if (!converterName) {
|
||||||
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
|
}
|
||||||
|
|
||||||
const fileNames = JSON.parse(body.file_names) as string[];
|
const fileNames = JSON.parse(body.file_names) as string[];
|
||||||
|
|
||||||
for (let i = 0; i < fileNames.length; i++) {
|
for (let i = 0; i < fileNames.length; i++) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
|
import path from "node:path";
|
||||||
import { Elysia } from "elysia";
|
import { Elysia } from "elysia";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
|
import * as tar from "tar";
|
||||||
import { outputDir } from "..";
|
import { outputDir } from "..";
|
||||||
import db from "../db/db";
|
import db from "../db/db";
|
||||||
import { WEBROOT } from "../helpers/env";
|
import { WEBROOT } from "../helpers/env";
|
||||||
import { userService } from "./user";
|
import { userService } from "./user";
|
||||||
import path from "node:path";
|
|
||||||
import * as tar from "tar";
|
|
||||||
|
|
||||||
export const download = new Elysia()
|
export const download = new Elysia()
|
||||||
.use(userService)
|
.use(userService)
|
||||||
|
|
@ -58,8 +58,17 @@ export const download = new Elysia()
|
||||||
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}`;
|
||||||
const outputTar = path.join(outputPath, `converted_files_${jobId}.tar`)
|
const outputTar = path.join(outputPath, `converted_files_${jobId}.tar`);
|
||||||
|
|
||||||
await tar.create({file: outputTar, cwd: outputPath, filter: (path) => { return !path.match(".*\\.tar"); }}, ["."]);
|
await tar.create(
|
||||||
|
{
|
||||||
|
file: outputTar,
|
||||||
|
cwd: outputPath,
|
||||||
|
filter: (path) => {
|
||||||
|
return !path.match(".*\\.tar");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
["."],
|
||||||
|
);
|
||||||
return Bun.file(outputTar);
|
return Bun.file(outputTar);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { Html } from "@elysiajs/html";
|
import { Html } from "@elysiajs/html";
|
||||||
|
import { JWTPayloadSpec } from "@elysiajs/jwt";
|
||||||
import { Elysia } from "elysia";
|
import { Elysia } from "elysia";
|
||||||
import { BaseHtml } from "../components/base";
|
import { BaseHtml } from "../components/base";
|
||||||
import { Header } from "../components/header";
|
import { Header } from "../components/header";
|
||||||
|
|
@ -6,7 +7,6 @@ import db from "../db/db";
|
||||||
import { Filename, Jobs } from "../db/types";
|
import { Filename, Jobs } from "../db/types";
|
||||||
import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
|
import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
|
||||||
import { userService } from "./user";
|
import { userService } from "./user";
|
||||||
import { JWTPayloadSpec } from "@elysiajs/jwt";
|
|
||||||
|
|
||||||
function ResultsArticle({
|
function ResultsArticle({
|
||||||
user,
|
user,
|
||||||
|
|
@ -26,7 +26,7 @@ function ResultsArticle({
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<div class="mb-4 flex items-center justify-between">
|
||||||
<h1 class="text-xl">Results</h1>
|
<h1 class="text-xl">Results</h1>
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a
|
||||||
style={files.length !== job.num_files ? "pointer-events: none;" : ""}
|
style={files.length !== job.num_files ? "pointer-events: none;" : ""}
|
||||||
href={`${WEBROOT}/archive/${user.id}/${job.id}`}
|
href={`${WEBROOT}/archive/${user.id}/${job.id}`}
|
||||||
download={`converted_files_${job.id}.tar`}
|
download={`converted_files_${job.id}.tar`}
|
||||||
|
|
@ -35,7 +35,7 @@ function ResultsArticle({
|
||||||
type="button"
|
type="button"
|
||||||
class="float-right w-40 btn-primary"
|
class="float-right w-40 btn-primary"
|
||||||
{...(files.length !== job.num_files ? { disabled: true, "aria-busy": "true" } : "")}
|
{...(files.length !== job.num_files ? { disabled: true, "aria-busy": "true" } : "")}
|
||||||
>
|
>
|
||||||
{files.length === job.num_files ? "Download All" : "Converting..."}
|
{files.length === job.num_files ? "Download All" : "Converting..."}
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,9 @@ export const root = new Elysia()
|
||||||
let user: ({ id: string } & JWTPayloadSpec) | false = false;
|
let user: ({ id: string } & JWTPayloadSpec) | false = false;
|
||||||
if (ALLOW_UNAUTHENTICATED) {
|
if (ALLOW_UNAUTHENTICATED) {
|
||||||
const newUserId = String(
|
const newUserId = String(
|
||||||
UNAUTHENTICATED_USER_SHARING ? 0 : randomInt(2 ** 24, Math.min(2 ** 48 + 2 ** 24 - 1, Number.MAX_SAFE_INTEGER)),
|
UNAUTHENTICATED_USER_SHARING
|
||||||
|
? 0
|
||||||
|
: randomInt(2 ** 24, Math.min(2 ** 48 + 2 ** 24 - 1, Number.MAX_SAFE_INTEGER)),
|
||||||
);
|
);
|
||||||
const accessToken = await jwt.sign({
|
const accessToken = await jwt.sign({
|
||||||
id: newUserId,
|
id: newUserId,
|
||||||
|
|
|
||||||
|
|
@ -44,4 +44,4 @@
|
||||||
/* lime-400 */
|
/* lime-400 */
|
||||||
--accent-400: oklch(84.1% 0.238 128.85);
|
--accent-400: oklch(84.1% 0.238 128.85);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue