Merge branch 'main' into AddUnitTests
This commit is contained in:
commit
c3d461f102
22 changed files with 160 additions and 80 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`
|
||||||
|
|
|
||||||
26
.github/ISSUE_TEMPLATE/converter_request.md
vendored
Normal file
26
.github/ISSUE_TEMPLATE/converter_request.md
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
name: Converter request
|
||||||
|
about: Suggest a converter for this project
|
||||||
|
title: "[Converter Request]"
|
||||||
|
labels: "converter request"
|
||||||
|
assignees: ""
|
||||||
|
---
|
||||||
|
|
||||||
|
**What file formats are missing?**
|
||||||
|
|
||||||
|
<!-- Provide an example of what you would like to convert -->
|
||||||
|
|
||||||
|
**What converter should be added**
|
||||||
|
|
||||||
|
<!-- It has to be free and preferably open source -->
|
||||||
|
|
||||||
|
**Are you willing to add it?**
|
||||||
|
|
||||||
|
<!-- Adding a converter is very easy just copy one of the existing and modify it -->
|
||||||
|
|
||||||
|
- [ ] Yes
|
||||||
|
- [ ] No
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
|
||||||
|
<!-- 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**
|
||||||
|
|
|
||||||
31
.github/workflows/check-lint.yml
vendored
Normal file
31
.github/workflows/check-lint.yml
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
name: Check Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ["main"]
|
||||||
|
pull_request:
|
||||||
|
branches: ["main"]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
name: Run linting checks
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: 1.2.2
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install
|
||||||
|
|
||||||
|
- name: Run lint
|
||||||
|
run: bun run lint
|
||||||
31
.github/workflows/docker-publish.yml
vendored
31
.github/workflows/docker-publish.yml
vendored
|
|
@ -10,7 +10,6 @@ on:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
env:
|
env:
|
||||||
GHCR_IMAGE: ghcr.io/c4illin/convertx
|
|
||||||
IMAGE_NAME: ${{ github.repository }}
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
DOCKERHUB_USERNAME: c4illin
|
DOCKERHUB_USERNAME: c4illin
|
||||||
|
|
||||||
|
|
@ -32,8 +31,7 @@ jobs:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
attestations: write
|
attestations: write
|
||||||
checks: write
|
id-token: write
|
||||||
actions: read
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.platform == 'linux/amd64' && 'ubuntu-24.04' || matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' }}
|
runs-on: ${{ matrix.platform == 'linux/amd64' && 'ubuntu-24.04' || matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' }}
|
||||||
|
|
||||||
|
|
@ -53,11 +51,15 @@ jobs:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: downcase REPO
|
||||||
|
run: |
|
||||||
|
echo "REPO=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}"
|
||||||
|
|
||||||
- name: Docker meta default
|
- name: Docker meta default
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: ${{ env.GHCR_IMAGE }}
|
images: ghcr.io/${{ env.REPO }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
@ -82,7 +84,8 @@ jobs:
|
||||||
platforms: ${{ matrix.platform }}
|
platforms: ${{ matrix.platform }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
annotations: ${{ steps.meta.outputs.annotations }}
|
annotations: ${{ steps.meta.outputs.annotations }}
|
||||||
outputs: type=image,name=${{ env.GHCR_IMAGE }},push-by-digest=true,name-canonical=true,push=true,oci-mediatypes=true
|
outputs: type=image,name=ghcr.io/${{ env.REPO }},push-by-digest=true,name-canonical=true,oci-mediatypes=true
|
||||||
|
push: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
|
||||||
cache-from: type=gha,scope=${{ matrix.platform }}
|
cache-from: type=gha,scope=${{ matrix.platform }}
|
||||||
cache-to: type=gha,mode=max,scope=${{ matrix.platform }}
|
cache-to: type=gha,mode=max,scope=${{ matrix.platform }}
|
||||||
|
|
||||||
|
|
@ -101,30 +104,36 @@ jobs:
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
merge:
|
merge:
|
||||||
|
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||||
name: Merge Docker manifests
|
name: Merge Docker manifests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
attestations: write
|
contents: write
|
||||||
contents: read
|
|
||||||
packages: write
|
packages: write
|
||||||
|
attestations: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
needs:
|
needs:
|
||||||
- build
|
- build
|
||||||
steps:
|
steps:
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v5
|
||||||
with:
|
with:
|
||||||
path: /tmp/digests
|
path: /tmp/digests
|
||||||
pattern: digests-*
|
pattern: digests-*
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: downcase REPO
|
||||||
|
run: |
|
||||||
|
echo "REPO=${GITHUB_REPOSITORY@L}" >> "${GITHUB_ENV}"
|
||||||
|
|
||||||
- name: Extract Docker metadata
|
- name: Extract Docker metadata
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
${{ env.GHCR_IMAGE }}
|
ghcr.io/${{ env.REPO }}
|
||||||
${{ env.IMAGE_NAME }}
|
${{ env.IMAGE_NAME }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
|
|
@ -157,8 +166,8 @@ jobs:
|
||||||
--annotation='index:org.opencontainers.image.created=${{ steps.timestamp.outputs.timestamp }}' \
|
--annotation='index:org.opencontainers.image.created=${{ steps.timestamp.outputs.timestamp }}' \
|
||||||
--annotation='index:org.opencontainers.image.url=${{ github.event.repository.url }}' \
|
--annotation='index:org.opencontainers.image.url=${{ github.event.repository.url }}' \
|
||||||
--annotation='index:org.opencontainers.image.source=${{ github.event.repository.url }}' \
|
--annotation='index:org.opencontainers.image.source=${{ github.event.repository.url }}' \
|
||||||
$(printf '${{ env.GHCR_IMAGE }}@sha256:%s ' *)
|
$(printf 'ghcr.io/${{ env.REPO }}@sha256:%s ' *)
|
||||||
|
|
||||||
- name: Inspect image
|
- name: Inspect image
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools inspect '${{ env.GHCR_IMAGE }}:${{ steps.meta.outputs.version }}'
|
docker buildx imagetools inspect 'ghcr.io/${{ env.REPO }}:${{ steps.meta.outputs.version }}'
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -46,6 +46,7 @@ package-lock.json
|
||||||
/output
|
/output
|
||||||
/db
|
/db
|
||||||
/data
|
/data
|
||||||
|
/dist
|
||||||
/Bruno
|
/Bruno
|
||||||
/tsconfig.tsbuildinfo
|
/tsconfig.tsbuildinfo
|
||||||
/public/generated.css
|
/public/generated.css
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,10 +77,13 @@ RUN apt-get update && apt-get install -y \
|
||||||
|
|
||||||
COPY --from=install /temp/prod/node_modules node_modules
|
COPY --from=install /temp/prod/node_modules node_modules
|
||||||
COPY --from=prerelease /app/public/generated.css /app/public/
|
COPY --from=prerelease /app/public/generated.css /app/public/
|
||||||
COPY . .
|
COPY --from=prerelease /app/dist /app/dist
|
||||||
|
|
||||||
|
# COPY . .
|
||||||
|
RUN mkdir data
|
||||||
|
|
||||||
EXPOSE 3000/tcp
|
EXPOSE 3000/tcp
|
||||||
# used for calibre
|
# used for calibre
|
||||||
ENV QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox"
|
ENV QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox"
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENTRYPOINT [ "bun", "run", "./src/index.tsx" ]
|
ENTRYPOINT [ "bun", "run", "dist/src/index.js" ]
|
||||||
|
|
|
||||||
12
README.md
12
README.md
|
|
@ -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.
|
||||||
|
|
@ -133,19 +132,10 @@ Tutorial in chinese: <https://xzllll.com/24092901/>
|
||||||
2. `bun install`
|
2. `bun install`
|
||||||
3. `bun run dev`
|
3. `bun run dev`
|
||||||
|
|
||||||
Pull requests are welcome! See below and open issues for the list of todos.
|
Pull requests are welcome! See open issues for the list of todos. The ones tagged with "converter request" are quite easy. Help with docs and cleaning up in issues are also very welcome!
|
||||||
|
|
||||||
Use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) for commit messages.
|
Use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) for commit messages.
|
||||||
|
|
||||||
## Todo
|
|
||||||
|
|
||||||
- [ ] Add options for converters
|
|
||||||
- [ ] Add tests
|
|
||||||
- [ ] Make errors logs visible from the web ui
|
|
||||||
- [ ] Add more converters:
|
|
||||||
- [ ] [deark](https://github.com/jsummers/deark)
|
|
||||||
- [ ] LibreOffice
|
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
<a href="https://github.com/C4illin/ConvertX/graphs/contributors">
|
<a href="https://github.com/C4illin/ConvertX/graphs/contributors">
|
||||||
|
|
|
||||||
5
bun.lock
5
bun.lock
|
|
@ -22,7 +22,6 @@
|
||||||
"@types/bun": "1.2.2",
|
"@types/bun": "1.2.2",
|
||||||
"@types/node": "^24.0.0",
|
"@types/node": "^24.0.0",
|
||||||
"@typescript-eslint/parser": "^8.34.0",
|
"@typescript-eslint/parser": "^8.34.0",
|
||||||
"bun-types": "1.2.17",
|
|
||||||
"eslint": "^9.28.0",
|
"eslint": "^9.28.0",
|
||||||
"eslint-plugin-better-tailwindcss": "^3.1.0",
|
"eslint-plugin-better-tailwindcss": "^3.1.0",
|
||||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
|
|
@ -275,7 +274,7 @@
|
||||||
|
|
||||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="],
|
"bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="],
|
||||||
|
|
||||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||||
|
|
||||||
|
|
@ -655,8 +654,6 @@
|
||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
"@types/bun/bun-types": ["bun-types@1.2.2", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-RCbMH5elr9gjgDGDhkTTugA21XtJAy/9jkKe/G3WR2q17VPGhcquf9Sir6uay9iW+7P/BV0CAHA1XlHXMAVKHg=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.4", "", {}, "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A=="],
|
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.4", "", {}, "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
import js from "@eslint/js";
|
import js from "@eslint/js";
|
||||||
import eslintParserTypeScript from "@typescript-eslint/parser";
|
import eslintParserTypeScript from "@typescript-eslint/parser";
|
||||||
import type { Linter } from "eslint";
|
|
||||||
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
|
import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
|
||||||
import simpleImportSortPlugin from "eslint-plugin-simple-import-sort";
|
import simpleImportSortPlugin from "eslint-plugin-simple-import-sort";
|
||||||
import globals from "globals";
|
import globals from "globals";
|
||||||
import tseslint from "typescript-eslint";
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
export default [
|
export default tseslint.config(
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
...tseslint.configs.recommended,
|
...tseslint.configs.recommended,
|
||||||
// ...tailwind.configs["flat/recommended"],
|
// ...tailwind.configs["flat/recommended"],
|
||||||
|
|
@ -15,7 +14,7 @@ export default [
|
||||||
"simple-import-sort": simpleImportSortPlugin,
|
"simple-import-sort": simpleImportSortPlugin,
|
||||||
"better-tailwindcss": eslintPluginBetterTailwindcss,
|
"better-tailwindcss": eslintPluginBetterTailwindcss,
|
||||||
},
|
},
|
||||||
ignores: ["**/node_modules/**"],
|
ignores: ["**/node_modules/**", "eslint.config.ts"],
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parser: eslintParserTypeScript,
|
parser: eslintParserTypeScript,
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
|
|
@ -26,10 +25,9 @@ export default [
|
||||||
},
|
},
|
||||||
globals: {
|
globals: {
|
||||||
...globals.node,
|
...globals.node,
|
||||||
...globals.browser,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
files: ["**/*.{js,mjs,cjs,jsx,tsx,ts}"],
|
files: ["**/*.{tsx,ts}"],
|
||||||
settings: {
|
settings: {
|
||||||
"better-tailwindcss": {
|
"better-tailwindcss": {
|
||||||
entryPoint: "src/main.css",
|
entryPoint: "src/main.css",
|
||||||
|
|
@ -63,4 +61,13 @@ export default [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
] as Linter.Config[];
|
{
|
||||||
|
files: ["**/*.{js,cjs,mjs,jsx}"],
|
||||||
|
extends: [tseslint.configs.disableTypeChecked],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://unpkg.com/knip@5/schema.json",
|
"$schema": "https://unpkg.com/knip@5/schema.json",
|
||||||
"entry": ["src/index.tsx"],
|
"project": ["src/**/*.ts", "src/**/*.tsx"],
|
||||||
"project": ["src/**/*.ts", "src/**/*.tsx", "src/main.css"],
|
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"entry": ["src/main.css"]
|
"entry": ["src/main.css"]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
"format": "run-p 'format:*'",
|
"format": "run-p 'format:*'",
|
||||||
"format:eslint": "eslint --fix .",
|
"format:eslint": "eslint --fix .",
|
||||||
"format:prettier": "prettier --write .",
|
"format:prettier": "prettier --write .",
|
||||||
"build": "bun x @tailwindcss/cli -i ./src/main.css -o ./public/generated.css",
|
"build:js": "tsc",
|
||||||
|
"build": "bun x @tailwindcss/cli -i ./src/main.css -o ./public/generated.css && bun run build:js",
|
||||||
"lint": "run-p 'lint:*'",
|
"lint": "run-p 'lint:*'",
|
||||||
"lint:tsc": "tsc --noEmit",
|
"lint:tsc": "tsc --noEmit",
|
||||||
"lint:knip": "knip",
|
"lint:knip": "knip",
|
||||||
|
|
@ -38,7 +39,6 @@
|
||||||
"@types/bun": "1.2.2",
|
"@types/bun": "1.2.2",
|
||||||
"@types/node": "^24.0.0",
|
"@types/node": "^24.0.0",
|
||||||
"@typescript-eslint/parser": "^8.34.0",
|
"@typescript-eslint/parser": "^8.34.0",
|
||||||
"bun-types": "1.2.17",
|
|
||||||
"eslint": "^9.28.0",
|
"eslint": "^9.28.0",
|
||||||
"eslint-plugin-better-tailwindcss": "^3.1.0",
|
"eslint-plugin-better-tailwindcss": "^3.1.0",
|
||||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,6 @@
|
||||||
"lockFileMaintenance": {
|
"lockFileMaintenance": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"automerge": true
|
"automerge": true
|
||||||
}
|
},
|
||||||
|
"ignoreDeps": ["bun-types", "@types/bun"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
|
|
@ -120,10 +121,10 @@ 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,14 +134,12 @@ 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) {
|
||||||
|
|
@ -155,27 +154,22 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function mainConverter(
|
async function mainConverter(
|
||||||
inputFilePath: string,
|
inputFilePath: string,
|
||||||
fileTypeOriginal: string,
|
fileTypeOriginal: string,
|
||||||
convertTo: string,
|
convertTo: string,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@
|
||||||
"target": "ES2021",
|
"target": "ES2021",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"allowImportingTsExtensions": true,
|
// "allowImportingTsExtensions": true,
|
||||||
"noEmit": true,
|
"outDir": "dist",
|
||||||
|
"noEmit": false,
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
|
|
@ -24,7 +25,10 @@
|
||||||
// "noUnusedParameters": true,
|
// "noUnusedParameters": true,
|
||||||
"exactOptionalPropertyTypes": true,
|
"exactOptionalPropertyTypes": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noImplicitOverride": true
|
"noImplicitOverride": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"esModuleInterop": true
|
||||||
// "noImplicitReturns": true
|
// "noImplicitReturns": true
|
||||||
}
|
},
|
||||||
|
"include": ["src", "package.json", "reset.d.ts"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue