mirror of
https://github.com/C4illin/ConvertX.git
synced 2025-11-04 22:13:32 +00:00
Compare commits
104 Commits
feature-po
...
v0.3.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5efac9423 | ||
|
|
cceca9a924 | ||
|
|
4d4c13a8d8 | ||
|
|
f8f90ebd69 | ||
|
|
648d5070e2 | ||
|
|
801cf28d1e | ||
|
|
fae2ba9c54 | ||
|
|
10d20a8786 | ||
|
|
c9bc1e237e | ||
|
|
48a76a46b3 | ||
|
|
4dcb796e1b | ||
|
|
5952103bcd | ||
|
|
fd05ee5cd5 | ||
|
|
7e7d238c7a | ||
|
|
755a4170f2 | ||
|
|
263ba415f5 | ||
|
|
317260098a | ||
|
|
5304e94b4e | ||
|
|
aab2b311cf | ||
|
|
baa7ea40e6 | ||
|
|
481a11b610 | ||
|
|
c09fe296b1 | ||
|
|
f023aae753 | ||
|
|
5cd9544b55 | ||
|
|
97c23ba65c | ||
|
|
0ffda40ac8 | ||
|
|
cb639907ee | ||
|
|
0166842b78 | ||
|
|
277a35b5df | ||
|
|
42124e08b2 | ||
|
|
13169574f0 | ||
|
|
6fb07f0d13 | ||
|
|
8121114ccb | ||
|
|
81881af1c1 | ||
|
|
bbb4117e9d | ||
|
|
d7ec8179d8 | ||
|
|
cef60afee3 | ||
|
|
ccf116acde | ||
|
|
f609984c90 | ||
|
|
663e654c80 | ||
|
|
31050dbf66 | ||
|
|
8918c418f4 | ||
|
|
56266e0da8 | ||
|
|
a3f5b5153a | ||
|
|
6b4c7a16e0 | ||
|
|
7c7756713b | ||
|
|
15d4233a82 | ||
|
|
a1411a7559 | ||
|
|
853a4c4f32 | ||
|
|
b05b0a14b0 | ||
|
|
ff680cb295 | ||
|
|
31e1a3124c | ||
|
|
70278ef0b6 | ||
|
|
0a33fb32e7 | ||
|
|
26f52a5122 | ||
|
|
8f8de4295a | ||
|
|
660b342f2e | ||
|
|
6306a99740 | ||
|
|
5ca6f45809 | ||
|
|
3ea3e1dd01 | ||
|
|
2fddfbe24a | ||
|
|
25df58ba82 | ||
|
|
249bccdc7d | ||
|
|
ec0e2db0e9 | ||
|
|
ef9b68e0da | ||
|
|
31789738fc | ||
|
|
5fa349a80e | ||
|
|
5dfd0f6f44 | ||
|
|
bfa6301570 | ||
|
|
3ea52c4faf | ||
|
|
391e62bfee | ||
|
|
4d1da58f74 | ||
|
|
6dec9ae93b | ||
|
|
b466a6de99 | ||
|
|
186681ef44 | ||
|
|
1e2273b7c4 | ||
|
|
8d17f59a58 | ||
|
|
d8fcd15aeb | ||
|
|
8cc0eee254 | ||
|
|
e4b69023d9 | ||
|
|
7d40890636 | ||
|
|
3ecd2c62ae | ||
|
|
16cabab0d0 | ||
|
|
3e1c9e147f | ||
|
|
5e7a0f5634 | ||
|
|
61b02206c0 | ||
|
|
e19a32fc6b | ||
|
|
1712fea1d3 | ||
|
|
1ac4808a64 | ||
|
|
84fd5367ce | ||
|
|
337cfdc15b | ||
|
|
b979bd4f13 | ||
|
|
3cab902752 | ||
|
|
1d0dd2a69f | ||
|
|
17f439210a | ||
|
|
9970fd3f89 | ||
|
|
8b7bcceb7b | ||
|
|
93ebdabf6f | ||
|
|
0b278c989b | ||
|
|
518e771afe | ||
|
|
6578a02478 | ||
|
|
e76b8a97f0 | ||
|
|
54d9aecbf9 | ||
|
|
f2befc5edb |
32
.github/dependabot.yml
vendored
32
.github/dependabot.yml
vendored
@@ -5,19 +5,19 @@
|
|||||||
|
|
||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
# Maintain dependencies for npm
|
- package-ecosystem: npm
|
||||||
- package-ecosystem: "npm" # See documentation for possible values
|
versioning-strategy: increase
|
||||||
versioning-strategy: increase
|
directory: "/"
|
||||||
directory: "/" # Location of package manifests
|
schedule:
|
||||||
schedule:
|
interval: daily
|
||||||
interval: "daily"
|
commit-message:
|
||||||
# Maintain dependencies for GitHub Actions
|
prefix: "build"
|
||||||
- package-ecosystem: "github-actions"
|
include: "scope"
|
||||||
directory: "/"
|
open-pull-requests-limit: 10
|
||||||
schedule:
|
- package-ecosystem: github-actions
|
||||||
interval: "daily"
|
directory: "/"
|
||||||
# Maintain dependencies for Docker
|
schedule:
|
||||||
- package-ecosystem: "docker"
|
interval: weekly
|
||||||
directory: "/"
|
commit-message:
|
||||||
schedule:
|
prefix: "build"
|
||||||
interval: "daily"
|
include: "scope"
|
||||||
2
.github/workflows/bun-dependabot.yml
vendored
2
.github/workflows/bun-dependabot.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
if: github.actor == 'dependabot[bot]'
|
if: github.actor == 'dependabot[bot]'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: oven-sh/setup-bun@v1
|
- uses: oven-sh/setup-bun@v2
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|||||||
2
.github/workflows/docker-publish.yml
vendored
2
.github/workflows/docker-publish.yml
vendored
@@ -58,7 +58,7 @@ jobs:
|
|||||||
# https://github.com/docker/build-push-action
|
# https://github.com/docker/build-push-action
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
id: build-and-push
|
id: build-and-push
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
|||||||
25
.github/workflows/release-please.yml
vendored
Normal file
25
.github/workflows/release-please.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
name: release-please
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-please:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: googleapis/release-please-action@v4
|
||||||
|
with:
|
||||||
|
# this assumes that you have created a personal access token
|
||||||
|
# (PAT) and configured it as a GitHub action secret named
|
||||||
|
# `MY_RELEASE_PLEASE_TOKEN` (this secret name is not important).
|
||||||
|
token: ${{ secrets.MY_RELEASE_PLEASE_TOKEN }}
|
||||||
|
# token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# this is a built-in strategy in release-please, see "Action Inputs"
|
||||||
|
# for more options
|
||||||
|
release-type: node
|
||||||
21
.github/workflows/remove-docker-tag.yml
vendored
Normal file
21
.github/workflows/remove-docker-tag.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Remove Docker Tag
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
remove-docker-tag:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
|
||||||
|
# (required)
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Remove Docker Tag
|
||||||
|
uses: ArchieAtkinson/remove-dockertag-action@v0.0
|
||||||
|
with:
|
||||||
|
tag_name: master
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
51
CHANGELOG.md
Normal file
51
CHANGELOG.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [0.3.1](https://github.com/C4illin/ConvertX/compare/v0.3.0...v0.3.1) (2024-06-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* release releases ([4d4c13a](https://github.com/C4illin/ConvertX/commit/4d4c13a8d85ec7c9209ad41cdbea7d4380b0edbf))
|
||||||
|
|
||||||
|
## [0.3.0](https://github.com/C4illin/ConvertX/compare/v0.2.0...v0.3.0) (2024-06-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add version number to log ([4dcb796](https://github.com/C4illin/ConvertX/commit/4dcb796e1bd27badc078d0638076cd9f1e81c4a4)), closes [#44](https://github.com/C4illin/ConvertX/issues/44)
|
||||||
|
* change to xelatex ([fae2ba9](https://github.com/C4illin/ConvertX/commit/fae2ba9c54461dccdccd1bfb5e76398540d11d0b))
|
||||||
|
* print version of installed converters to log ([801cf28](https://github.com/C4illin/ConvertX/commit/801cf28d1e5edac9353b0b16be75a4fb48470b8a))
|
||||||
|
|
||||||
|
## [0.2.0](https://github.com/C4illin/ConvertX/compare/v0.1.2...v0.2.0) (2024-06-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add libjxl for jpegxl conversion ([ff680cb](https://github.com/C4illin/ConvertX/commit/ff680cb29534a25c3148a90fd064bb86c71fb482))
|
||||||
|
* change from debian to alpine ([1316957](https://github.com/C4illin/ConvertX/commit/13169574f0134ae236f8d41287bb73930b575e82)), closes [#34](https://github.com/C4illin/ConvertX/issues/34)
|
||||||
|
|
||||||
|
## [0.1.2](https://github.com/C4illin/ConvertX/compare/v0.1.1...v0.1.2) (2024-06-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix incorrect redirect ([25df58b](https://github.com/C4illin/ConvertX/commit/25df58ba82321aaa6617811a6995cb96c2a00a40)), closes [#23](https://github.com/C4illin/ConvertX/issues/23)
|
||||||
|
|
||||||
|
## [0.1.1](https://github.com/C4illin/ConvertX/compare/v0.1.0...v0.1.1) (2024-05-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* :bug: make sure all redirects are 302 ([9970fd3](https://github.com/C4illin/ConvertX/commit/9970fd3f89190af96f8762edc3817d1e03082b3a)), closes [#12](https://github.com/C4illin/ConvertX/issues/12)
|
||||||
|
|
||||||
|
## 0.1.0 (2024-05-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* remove file from file list in index.html ([787ff97](https://github.com/C4illin/ConvertX/commit/787ff9741ecbbf4fb4c02b43bd22a214a173fd7b))
|
||||||
|
|
||||||
|
|
||||||
|
### Miscellaneous Chores
|
||||||
|
|
||||||
|
* release 0.1.0 ([54d9aec](https://github.com/C4illin/ConvertX/commit/54d9aecbf949689b12aa7e5e8e9be7b9032f4431))
|
||||||
63
Debian.Dockerfile
Normal file
63
Debian.Dockerfile
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
FROM oven/bun:1-debian as base
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# install dependencies into temp directory
|
||||||
|
# this will cache them and speed up future builds
|
||||||
|
FROM base AS install
|
||||||
|
RUN mkdir -p /temp/dev
|
||||||
|
COPY package.json bun.lockb /temp/dev/
|
||||||
|
RUN cd /temp/dev && bun install --frozen-lockfile
|
||||||
|
|
||||||
|
# install with --production (exclude devDependencies)
|
||||||
|
RUN mkdir -p /temp/prod
|
||||||
|
COPY package.json bun.lockb /temp/prod/
|
||||||
|
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
||||||
|
|
||||||
|
# FROM base AS install-libjxl-tools
|
||||||
|
# download
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# copy node_modules from temp directory
|
||||||
|
# then copy all (non-ignored) project files into the image
|
||||||
|
# FROM base AS prerelease
|
||||||
|
# COPY --from=install /temp/dev/node_modules node_modules
|
||||||
|
# COPY . .
|
||||||
|
|
||||||
|
# # [optional] tests & build
|
||||||
|
# ENV NODE_ENV=production
|
||||||
|
# RUN bun test
|
||||||
|
# RUN bun run build
|
||||||
|
|
||||||
|
# copy production dependencies and source code into final image
|
||||||
|
FROM base AS release
|
||||||
|
LABEL maintainer="Emrik Östling (C4illin)"
|
||||||
|
LABEL description="ConvertX: self-hosted online file converter supporting 700+ file formats."
|
||||||
|
LABEL repo="https://github.com/C4illin/ConvertX"
|
||||||
|
|
||||||
|
# install additional dependencies
|
||||||
|
RUN rm -rf /var/lib/apt/lists/partial && apt-get update -o Acquire::CompressionTypes::Order::=gz \
|
||||||
|
&& apt-get install -y \
|
||||||
|
pandoc \
|
||||||
|
texlive-latex-recommended \
|
||||||
|
texlive-fonts-recommended \
|
||||||
|
texlive-latex-extra \
|
||||||
|
ffmpeg \
|
||||||
|
graphicsmagick \
|
||||||
|
ghostscript \
|
||||||
|
libvips-tools
|
||||||
|
|
||||||
|
# # libjxl is not available in the official debian repositories
|
||||||
|
# RUN wget https://github.com/libjxl/libjxl/releases/download/v0.10.2/jxl-debs-amd64-debian-bullseye-v0.10.2.tar.gz -O /tmp/jxl-debs-amd64-debian-bullseye-v0.10.2.tar.gz \
|
||||||
|
# && mkdir -p /tmp/libjxl \
|
||||||
|
# && tar -xvf /tmp/jxl-debs-amd64-debian-bullseye-v0.10.2.tar.gz -C /tmp/libjxl \
|
||||||
|
# && dpkg -i /tmp/libjxl/libjxl_0.10.2_amd64.deb /tmp/libjxl/jxl_0.10.2_amd64.deb \
|
||||||
|
# && rm -rf /tmp/jxl-debs-amd64-debian-bullseye-v0.10.2.tar.gz /tmp/libjxl
|
||||||
|
|
||||||
|
COPY --from=install /temp/prod/node_modules node_modules
|
||||||
|
# COPY --from=prerelease /app/src/index.tsx /app/src/
|
||||||
|
# COPY --from=prerelease /app/package.json .
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 3000/tcp
|
||||||
|
ENTRYPOINT [ "bun", "run", "./src/index.tsx" ]
|
||||||
18
Dockerfile
18
Dockerfile
@@ -1,4 +1,4 @@
|
|||||||
FROM oven/bun:1-debian as base
|
FROM oven/bun:1-alpine as base
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# install dependencies into temp directory
|
# install dependencies into temp directory
|
||||||
@@ -31,16 +31,19 @@ LABEL description="ConvertX: self-hosted online file converter supporting 700+ f
|
|||||||
LABEL repo="https://github.com/C4illin/ConvertX"
|
LABEL repo="https://github.com/C4illin/ConvertX"
|
||||||
|
|
||||||
# install additional dependencies
|
# install additional dependencies
|
||||||
RUN rm -rf /var/lib/apt/lists/partial && apt-get update -o Acquire::CompressionTypes::Order::=gz \
|
RUN apk --no-cache add \
|
||||||
&& apt-get install -y \
|
|
||||||
pandoc \
|
pandoc \
|
||||||
texlive-latex-recommended \
|
texlive \
|
||||||
texlive-fonts-recommended \
|
texlive-xetex \
|
||||||
texlive-latex-extra \
|
texmf-dist-latexextra \
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
graphicsmagick \
|
graphicsmagick \
|
||||||
ghostscript \
|
ghostscript \
|
||||||
libvips-tools
|
vips-tools \
|
||||||
|
libjxl-tools
|
||||||
|
|
||||||
|
# this might be needed for some latex use cases, will add it if needed.
|
||||||
|
# texmf-dist-fontsextra \
|
||||||
|
|
||||||
COPY --from=install /temp/prod/node_modules node_modules
|
COPY --from=install /temp/prod/node_modules node_modules
|
||||||
# COPY --from=prerelease /app/src/index.tsx /app/src/
|
# COPY --from=prerelease /app/src/index.tsx /app/src/
|
||||||
@@ -48,4 +51,5 @@ COPY --from=install /temp/prod/node_modules node_modules
|
|||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
EXPOSE 3000/tcp
|
EXPOSE 3000/tcp
|
||||||
|
ENV NODE_ENV=production
|
||||||
ENTRYPOINT [ "bun", "run", "./src/index.tsx" ]
|
ENTRYPOINT [ "bun", "run", "./src/index.tsx" ]
|
||||||
25
README.md
25
README.md
@@ -1,8 +1,13 @@
|
|||||||

|

|
||||||
# ConvertX
|
# ConvertX
|
||||||
[](https://github.com/C4illin/ConvertX/actions/workflows/docker-publish.yml)
|
[](https://github.com/C4illin/ConvertX/actions/workflows/docker-publish.yml)
|
||||||
|
[](https://github.com/C4illin/ConvertX/pkgs/container/convertx)
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
A self-hosted online file converter. Supports 831 different formats. Written with Typescript, Bun and Elysia.
|
A self-hosted online file converter. Supports 831 different formats. Written with TypeScript, Bun and Elysia.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@@ -12,13 +17,14 @@ A self-hosted online file converter. Supports 831 different formats. Written wit
|
|||||||
|
|
||||||
## Converters supported
|
## Converters supported
|
||||||
|
|
||||||
| Converter | Use case | Converts from | Converts to |
|
| Converter | Use case | Converts from | Converts to |
|
||||||
|----------------|---------------|---------------|-------------|
|
|------------------------------------------------------------------------------|---------------|---------------|-------------|
|
||||||
| Vips | Images (fast) | 45 | 23 |
|
| [libjxl](https://github.com/libjxl/libjxl) | JPEG XL | 11 | 11 |
|
||||||
| PDFLaTeX | Documents | 1 | 1 |
|
| [Vips](https://github.com/libvips/libvips) | Images | 45 | 23 |
|
||||||
| Pandoc | Documents | 43 | 65 |
|
| [XeLaTeX](https://tug.org/xetex/) | Documents | 1 | 1 |
|
||||||
| GraphicsMagick | Images | 166 | 133 |
|
| [Pandoc](https://pandoc.org/) | Documents | 43 | 65 |
|
||||||
| FFmpeg | Video | ~473 | ~280 |
|
| [GraphicsMagick](http://www.graphicsmagick.org/) | Images | 166 | 133 |
|
||||||
|
| [FFmpeg](https://ffmpeg.org/) | Video | ~473 | ~280 |
|
||||||
|
|
||||||
<!-- many ffmpeg fileformats are duplicates -->
|
<!-- many ffmpeg fileformats are duplicates -->
|
||||||
|
|
||||||
@@ -28,7 +34,7 @@ A self-hosted online file converter. Supports 831 different formats. Written wit
|
|||||||
# docker-compose.yml
|
# docker-compose.yml
|
||||||
services:
|
services:
|
||||||
convertx:
|
convertx:
|
||||||
image: ghcr.io/c4illin/convertx:main
|
image: ghcr.io/c4illin/convertx
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
environment: # Defaults are listed below. All are optional.
|
environment: # Defaults are listed below. All are optional.
|
||||||
@@ -60,6 +66,7 @@ Tutorial in french: https://belginux.com/installer-convertx-avec-docker/
|
|||||||
- [ ] Divide index.tsx into smaller components
|
- [ ] Divide index.tsx into smaller components
|
||||||
- [ ] Add tests
|
- [ ] Add tests
|
||||||
- [ ] Add searchable list of formats
|
- [ ] Add searchable list of formats
|
||||||
|
- [ ] Make the upload button nicer and more easy to drop files on. Support copy paste as well if possible.
|
||||||
|
|
||||||
## Contributors
|
## Contributors
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ services:
|
|||||||
convertx:
|
convertx:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
|
# dockerfile: Debian.Dockerfile
|
||||||
volumes:
|
volumes:
|
||||||
- ./data:/app/data
|
- ./data:/app/data
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
26
package.json
26
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "convertx-frontend",
|
"name": "convertx-frontend",
|
||||||
"version": "1.0.50",
|
"version": "0.3.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun run --watch src/index.tsx",
|
"dev": "bun run --watch src/index.tsx",
|
||||||
"hot": "bun run --hot src/index.tsx",
|
"hot": "bun run --hot src/index.tsx",
|
||||||
@@ -12,29 +12,31 @@
|
|||||||
"@elysiajs/html": "^1.0.2",
|
"@elysiajs/html": "^1.0.2",
|
||||||
"@elysiajs/jwt": "^1.0.2",
|
"@elysiajs/jwt": "^1.0.2",
|
||||||
"@elysiajs/static": "^1.0.3",
|
"@elysiajs/static": "^1.0.3",
|
||||||
"elysia": "^1.0.22",
|
"elysia": "^1.0.25"
|
||||||
"node-poppler": "^7.2.0"
|
|
||||||
},
|
},
|
||||||
"module": "src/index.tsx",
|
"module": "src/index.tsx",
|
||||||
"bun-create": {
|
"bun-create": {
|
||||||
"start": "bun run src/index.tsx"
|
"start": "bun run src/index.tsx"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.7.3",
|
"@biomejs/biome": "1.8.2",
|
||||||
"@ianvs/prettier-plugin-sort-imports": "^4.2.1",
|
"@ianvs/prettier-plugin-sort-imports": "^4.3.0",
|
||||||
"@kitajs/ts-html-plugin": "^4.0.1",
|
"@kitajs/ts-html-plugin": "^4.0.1",
|
||||||
"@picocss/pico": "^2.0.6",
|
"@picocss/pico": "^2.0.6",
|
||||||
"@total-typescript/ts-reset": "^0.5.1",
|
"@total-typescript/ts-reset": "^0.5.1",
|
||||||
"@types/bun": "^1.1.3",
|
"@types/bun": "^1.1.6",
|
||||||
"@types/eslint": "^8.56.10",
|
"@types/eslint": "^8.56.10",
|
||||||
"@types/node": "^20.12.13",
|
"@types/node": "^20.14.9",
|
||||||
"@types/ws": "^8.5.10",
|
"@types/ws": "^8.5.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.11.0",
|
"@typescript-eslint/eslint-plugin": "^7.14.1",
|
||||||
"@typescript-eslint/parser": "^7.11.0",
|
"@typescript-eslint/parser": "^7.14.1",
|
||||||
"cpy-cli": "^5.0.0",
|
"cpy-cli": "^5.0.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.3.2",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.5.2"
|
||||||
}
|
},
|
||||||
|
"trustedDependencies": [
|
||||||
|
"@biomejs/biome"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
71
src/converters/libjxl.ts
Normal file
71
src/converters/libjxl.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { exec } from "node:child_process";
|
||||||
|
|
||||||
|
// declare possible conversions
|
||||||
|
export const properties = {
|
||||||
|
from: {
|
||||||
|
jxl: ["jxl"],
|
||||||
|
images: [
|
||||||
|
"apng",
|
||||||
|
"exr",
|
||||||
|
"gif",
|
||||||
|
"jpeg",
|
||||||
|
"pam",
|
||||||
|
"pfm",
|
||||||
|
"pgm",
|
||||||
|
"pgx",
|
||||||
|
"png",
|
||||||
|
"ppm",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
jxl: [
|
||||||
|
"apng",
|
||||||
|
"exr",
|
||||||
|
"gif",
|
||||||
|
"jpeg",
|
||||||
|
"pam",
|
||||||
|
"pfm",
|
||||||
|
"pgm",
|
||||||
|
"pgx",
|
||||||
|
"png",
|
||||||
|
"ppm",
|
||||||
|
],
|
||||||
|
images: ["jxl"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function convert(
|
||||||
|
filePath: string,
|
||||||
|
fileType: string,
|
||||||
|
convertTo: string,
|
||||||
|
targetPath: string,
|
||||||
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||||
|
options?: any,
|
||||||
|
): Promise<string> {
|
||||||
|
let tool = "";
|
||||||
|
if (fileType === "jxl") {
|
||||||
|
tool = "djxl";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (convertTo === "jxl") {
|
||||||
|
tool = "cjxl";
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
exec(`${tool} "${filePath}" "${targetPath}"`, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
reject(`error: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve("success");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -16,11 +16,14 @@ import {
|
|||||||
} from "./graphicsmagick";
|
} from "./graphicsmagick";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
convert as convertPdflatex,
|
convert as convertxelatex,
|
||||||
properties as propertiesPdflatex,
|
properties as propertiesxelatex,
|
||||||
} from "./pdflatex";
|
} from "./xelatex";
|
||||||
|
|
||||||
import { convert as convertPoppler, properties as propertiesPoppler } from "./poppler";
|
import {
|
||||||
|
convert as convertLibjxl,
|
||||||
|
properties as propertiesLibjxl,
|
||||||
|
} from "./libjxl";
|
||||||
|
|
||||||
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
||||||
|
|
||||||
@@ -52,17 +55,17 @@ const properties: {
|
|||||||
) => any;
|
) => any;
|
||||||
};
|
};
|
||||||
} = {
|
} = {
|
||||||
|
libjxl: {
|
||||||
|
properties: propertiesLibjxl,
|
||||||
|
converter: convertLibjxl,
|
||||||
|
},
|
||||||
vips: {
|
vips: {
|
||||||
properties: propertiesImage,
|
properties: propertiesImage,
|
||||||
converter: convertImage,
|
converter: convertImage,
|
||||||
},
|
},
|
||||||
pdflatex: {
|
xelatex: {
|
||||||
properties: propertiesPdflatex,
|
properties: propertiesxelatex,
|
||||||
converter: convertPdflatex,
|
converter: convertxelatex,
|
||||||
},
|
|
||||||
poppler: {
|
|
||||||
properties: propertiesPoppler,
|
|
||||||
converter: convertPoppler,
|
|
||||||
},
|
},
|
||||||
pandoc: {
|
pandoc: {
|
||||||
properties: propertiesPandoc,
|
properties: propertiesPandoc,
|
||||||
@@ -108,9 +111,8 @@ export async function mainConverter(
|
|||||||
|
|
||||||
for (const key in converterObj.properties.from) {
|
for (const key in converterObj.properties.from) {
|
||||||
if (
|
if (
|
||||||
// HOW??
|
converterObj?.properties?.from[key]?.includes(fileType) &&
|
||||||
converterObj.properties.from[key].includes(fileType) &&
|
converterObj?.properties?.to[key]?.includes(convertTo)
|
||||||
converterObj.properties.to[key].includes(convertTo)
|
|
||||||
) {
|
) {
|
||||||
converterFunc = converterObj.converter;
|
converterFunc = converterObj.converter;
|
||||||
break;
|
break;
|
||||||
@@ -214,9 +216,9 @@ for (const converterName in properties) {
|
|||||||
|
|
||||||
for (const key in converterProperties.to) {
|
for (const key in converterProperties.to) {
|
||||||
if (allTargets[converterName]) {
|
if (allTargets[converterName]) {
|
||||||
allTargets[converterName].push(...converterProperties.to[key]);
|
allTargets[converterName].push(...(converterProperties.to[key] || []));
|
||||||
} else {
|
} else {
|
||||||
allTargets[converterName] = converterProperties.to[key];
|
allTargets[converterName] = converterProperties.to[key] || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -235,9 +237,9 @@ for (const converterName in properties) {
|
|||||||
|
|
||||||
for (const key in converterProperties.from) {
|
for (const key in converterProperties.from) {
|
||||||
if (allInputs[converterName]) {
|
if (allInputs[converterName]) {
|
||||||
allInputs[converterName].push(...converterProperties.from[key]);
|
allInputs[converterName].push(...(converterProperties.from[key] || []));
|
||||||
} else {
|
} else {
|
||||||
allInputs[converterName] = converterProperties.from[key];
|
allInputs[converterName] = converterProperties.from[key] || [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,9 +127,15 @@ export function convert(
|
|||||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||||
options?: any,
|
options?: any,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
|
// set xelatex here
|
||||||
|
const xelatex = ["pdf", "latex"];
|
||||||
|
let option = "";
|
||||||
|
if (xelatex.includes(convertTo)) {
|
||||||
|
option = "--pdf-engine=xelatex";
|
||||||
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
exec(
|
exec(
|
||||||
`pandoc "${filePath}" -f ${fileType} -t ${convertTo} -o "${targetPath}"`,
|
`pandoc ${option} "${filePath}" -f ${fileType} -t ${convertTo} -o "${targetPath}"`,
|
||||||
(error, stdout, stderr) => {
|
(error, stdout, stderr) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(`error: ${error}`);
|
reject(`error: ${error}`);
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
const { Poppler } = require("node-poppler");
|
|
||||||
const poppler = new Poppler();
|
|
||||||
|
|
||||||
export const properties = {
|
|
||||||
from: {
|
|
||||||
text: ["pdf"],
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
text: [
|
|
||||||
"jpeg",
|
|
||||||
"png",
|
|
||||||
"tiff",
|
|
||||||
"eps",
|
|
||||||
"icc",
|
|
||||||
"pdf",
|
|
||||||
"svg",
|
|
||||||
"ps",
|
|
||||||
"html",
|
|
||||||
"text",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export function convert(
|
|
||||||
filePath: string,
|
|
||||||
fileType: string,
|
|
||||||
convertTo: string,
|
|
||||||
targetPath: string,
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
||||||
options?: any,
|
|
||||||
): Promise<string> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const cairoFiles = [
|
|
||||||
"jpeg",
|
|
||||||
"png",
|
|
||||||
"tiff",
|
|
||||||
"eps",
|
|
||||||
"icc",
|
|
||||||
"pdf",
|
|
||||||
"svg",
|
|
||||||
"ps",
|
|
||||||
];
|
|
||||||
|
|
||||||
if (cairoFiles.includes(convertTo)) {
|
|
||||||
const popplerOptions: {
|
|
||||||
jpegFile?: boolean;
|
|
||||||
pngFile?: boolean;
|
|
||||||
tiffFile?: boolean;
|
|
||||||
epsFile?: boolean;
|
|
||||||
iccFile?: boolean;
|
|
||||||
pdfFile?: boolean;
|
|
||||||
svgFile?: boolean;
|
|
||||||
psFile?: boolean;
|
|
||||||
} = {};
|
|
||||||
|
|
||||||
switch (convertTo) {
|
|
||||||
case "jpeg":
|
|
||||||
popplerOptions.jpegFile = true;
|
|
||||||
break;
|
|
||||||
case "png":
|
|
||||||
popplerOptions.pngFile = true;
|
|
||||||
break;
|
|
||||||
case "tiff":
|
|
||||||
popplerOptions.tiffFile = true;
|
|
||||||
break;
|
|
||||||
case "eps":
|
|
||||||
popplerOptions.epsFile = true;
|
|
||||||
break;
|
|
||||||
case "icc":
|
|
||||||
popplerOptions.iccFile = true;
|
|
||||||
break;
|
|
||||||
case "pdf":
|
|
||||||
popplerOptions.pdfFile = true;
|
|
||||||
break;
|
|
||||||
case "svg":
|
|
||||||
popplerOptions.svgFile = true;
|
|
||||||
break;
|
|
||||||
case "ps":
|
|
||||||
popplerOptions.psFile = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
reject(`Invalid convertTo option: ${convertTo}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
poppler
|
|
||||||
.pdfToCairo(filePath, targetPath, popplerOptions)
|
|
||||||
.then(() => {
|
|
||||||
resolve("success");
|
|
||||||
})
|
|
||||||
.catch((err: Error) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
} else if (convertTo === "html") {
|
|
||||||
poppler
|
|
||||||
.pdfToHtml(filePath, targetPath)
|
|
||||||
.then(() => {
|
|
||||||
resolve("success");
|
|
||||||
})
|
|
||||||
.catch((err: Error) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
} else if (convertTo === "text") {
|
|
||||||
poppler
|
|
||||||
.pdfToText(filePath, targetPath)
|
|
||||||
.then(() => {
|
|
||||||
resolve("success");
|
|
||||||
})
|
|
||||||
.catch((err: Error) => {
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
reject(`Invalid convertTo option: ${convertTo}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -19,9 +19,13 @@ export function convert(
|
|||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// const fileName: string = (targetPath.split("/").pop() as string).replace(".pdf", "")
|
// const fileName: string = (targetPath.split("/").pop() as string).replace(".pdf", "")
|
||||||
const outputPath = targetPath.split("/").slice(0, -1).join("/").replace("./", "")
|
const outputPath = targetPath
|
||||||
|
.split("/")
|
||||||
|
.slice(0, -1)
|
||||||
|
.join("/")
|
||||||
|
.replace("./", "");
|
||||||
exec(
|
exec(
|
||||||
`pdflatex -interaction=nonstopmode -output-directory="${outputPath}" "${filePath}"`,
|
`latexmk -xelatex -interaction=nonstopmode -output-directory="${outputPath}" "${filePath}"`,
|
||||||
(error, stdout, stderr) => {
|
(error, stdout, stderr) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(`error: ${error}`);
|
reject(`error: ${error}`);
|
||||||
@@ -25,8 +25,6 @@ export const normalizeOutputFiletype = (filetype: string): string => {
|
|||||||
return "tex";
|
return "tex";
|
||||||
case "markdown":
|
case "markdown":
|
||||||
return "md";
|
return "md";
|
||||||
case "text":
|
|
||||||
return "txt";
|
|
||||||
default:
|
default:
|
||||||
return lowercaseFiletype;
|
return lowercaseFiletype;
|
||||||
}
|
}
|
||||||
|
|||||||
75
src/helpers/printVersions.ts
Normal file
75
src/helpers/printVersions.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { exec } from "node:child_process";
|
||||||
|
import { version } from "../../package.json";
|
||||||
|
console.log(`ConvertX v${version}`);
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "production") {
|
||||||
|
exec("cat /etc/os-release", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Not running on docker, this is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split('PRETTY_NAME="')[1]?.split('"')[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("pandoc -v", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Pandoc is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("ffmpeg -version", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("FFmpeg is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("vips -v", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Vips is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("gm version", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("GraphicsMagick is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("djxl --version", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("libjxl-tools is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
exec("xelatex -version", (error, stdout) => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Tex Live with XeTeX is not installed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdout) {
|
||||||
|
console.log(stdout.split("\n")[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -19,6 +19,10 @@ import {
|
|||||||
normalizeFiletype,
|
normalizeFiletype,
|
||||||
normalizeOutputFiletype,
|
normalizeOutputFiletype,
|
||||||
} from "./helpers/normalizeFiletype";
|
} from "./helpers/normalizeFiletype";
|
||||||
|
import "./helpers/printVersions";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const db = new Database("./data/mydb.sqlite", { create: true });
|
const db = new Database("./data/mydb.sqlite", { create: true });
|
||||||
const uploadsDir = "./data/uploads/";
|
const uploadsDir = "./data/uploads/";
|
||||||
@@ -121,7 +125,7 @@ const app = new Elysia()
|
|||||||
)
|
)
|
||||||
.get("/setup", ({ redirect }) => {
|
.get("/setup", ({ redirect }) => {
|
||||||
if (!FIRST_RUN) {
|
if (!FIRST_RUN) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -164,7 +168,7 @@ const app = new Elysia()
|
|||||||
})
|
})
|
||||||
.get("/register", ({ redirect }) => {
|
.get("/register", ({ redirect }) => {
|
||||||
if (!ACCOUNT_REGISTRATION) {
|
if (!ACCOUNT_REGISTRATION) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -206,7 +210,7 @@ const app = new Elysia()
|
|||||||
"/register",
|
"/register",
|
||||||
async ({ body, set, redirect, jwt, cookie: { auth } }) => {
|
async ({ body, set, redirect, jwt, cookie: { auth } }) => {
|
||||||
if (!ACCOUNT_REGISTRATION && !FIRST_RUN) {
|
if (!ACCOUNT_REGISTRATION && !FIRST_RUN) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FIRST_RUN) {
|
if (FIRST_RUN) {
|
||||||
@@ -253,13 +257,13 @@ const app = new Elysia()
|
|||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
});
|
});
|
||||||
|
|
||||||
return redirect("/");
|
return redirect("/", 302);
|
||||||
},
|
},
|
||||||
{ body: t.Object({ email: t.String(), password: t.String() }) },
|
{ body: t.Object({ email: t.String(), password: t.String() }) },
|
||||||
)
|
)
|
||||||
.get("/login", async ({ jwt, redirect, cookie: { auth } }) => {
|
.get("/login", async ({ jwt, redirect, cookie: { auth } }) => {
|
||||||
if (FIRST_RUN) {
|
if (FIRST_RUN) {
|
||||||
return redirect("/setup");
|
return redirect("/setup", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if already logged in, redirect to home
|
// if already logged in, redirect to home
|
||||||
@@ -267,7 +271,7 @@ const app = new Elysia()
|
|||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
return redirect("/");
|
return redirect("/", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
auth.remove();
|
auth.remove();
|
||||||
@@ -361,7 +365,7 @@ const app = new Elysia()
|
|||||||
sameSite: "strict",
|
sameSite: "strict",
|
||||||
});
|
});
|
||||||
|
|
||||||
return redirect("/");
|
return redirect("/", 302);
|
||||||
},
|
},
|
||||||
{ body: t.Object({ email: t.String(), password: t.String() }) },
|
{ body: t.Object({ email: t.String(), password: t.String() }) },
|
||||||
)
|
)
|
||||||
@@ -370,27 +374,27 @@ const app = new Elysia()
|
|||||||
auth.remove();
|
auth.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
})
|
})
|
||||||
.post("/logoff", ({ redirect, cookie: { auth } }) => {
|
.post("/logoff", ({ redirect, cookie: { auth } }) => {
|
||||||
if (auth?.value) {
|
if (auth?.value) {
|
||||||
auth.remove();
|
auth.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
})
|
})
|
||||||
.get("/", async ({ jwt, redirect, cookie: { auth, jobId } }) => {
|
.get("/", async ({ jwt, redirect, cookie: { auth, jobId } }) => {
|
||||||
if (FIRST_RUN) {
|
if (FIRST_RUN) {
|
||||||
return redirect("/setup");
|
return redirect("/setup", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
// validate jwt
|
// validate jwt
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure user exists in db
|
// make sure user exists in db
|
||||||
@@ -402,7 +406,7 @@ const app = new Elysia()
|
|||||||
if (auth?.value) {
|
if (auth?.value) {
|
||||||
auth.remove();
|
auth.remove();
|
||||||
}
|
}
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new job
|
// create a new job
|
||||||
@@ -509,16 +513,16 @@ const app = new Elysia()
|
|||||||
"/upload",
|
"/upload",
|
||||||
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
|
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!jobId?.value) {
|
if (!jobId?.value) {
|
||||||
return redirect("/");
|
return redirect("/", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingJob = await db
|
const existingJob = await db
|
||||||
@@ -526,7 +530,7 @@ const app = new Elysia()
|
|||||||
.get(jobId.value, user.id);
|
.get(jobId.value, user.id);
|
||||||
|
|
||||||
if (!existingJob) {
|
if (!existingJob) {
|
||||||
return redirect("/");
|
return redirect("/", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
|
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
|
||||||
@@ -557,16 +561,16 @@ const app = new Elysia()
|
|||||||
"/delete",
|
"/delete",
|
||||||
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
|
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!jobId?.value) {
|
if (!jobId?.value) {
|
||||||
return redirect("/");
|
return redirect("/", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingJob = await db
|
const existingJob = await db
|
||||||
@@ -574,7 +578,7 @@ const app = new Elysia()
|
|||||||
.get(jobId.value, user.id);
|
.get(jobId.value, user.id);
|
||||||
|
|
||||||
if (!existingJob) {
|
if (!existingJob) {
|
||||||
return redirect("/");
|
return redirect("/", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
|
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
|
||||||
@@ -587,16 +591,16 @@ const app = new Elysia()
|
|||||||
"/convert",
|
"/convert",
|
||||||
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
|
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!jobId?.value) {
|
if (!jobId?.value) {
|
||||||
return redirect("/");
|
return redirect("/", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingJob = (await db
|
const existingJob = (await db
|
||||||
@@ -604,7 +608,7 @@ const app = new Elysia()
|
|||||||
.get(jobId.value, user.id)) as IJobs;
|
.get(jobId.value, user.id)) as IJobs;
|
||||||
|
|
||||||
if (!existingJob) {
|
if (!existingJob) {
|
||||||
return redirect("/");
|
return redirect("/", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
|
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
|
||||||
@@ -627,7 +631,7 @@ const app = new Elysia()
|
|||||||
const fileNames = JSON.parse(body.file_names) as string[];
|
const fileNames = JSON.parse(body.file_names) as string[];
|
||||||
|
|
||||||
if (!Array.isArray(fileNames) || fileNames.length === 0) {
|
if (!Array.isArray(fileNames) || fileNames.length === 0) {
|
||||||
return redirect("/");
|
return redirect("/", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
db.run(
|
db.run(
|
||||||
@@ -677,7 +681,7 @@ const app = new Elysia()
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Redirect the client immediately
|
// Redirect the client immediately
|
||||||
return redirect(`/results/${jobId.value}`);
|
return redirect(`/results/${jobId.value}`, 302);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
@@ -688,12 +692,12 @@ const app = new Elysia()
|
|||||||
)
|
)
|
||||||
.get("/history", async ({ jwt, redirect, cookie: { auth } }) => {
|
.get("/history", async ({ jwt, redirect, cookie: { auth } }) => {
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
let userJobs = db
|
let userJobs = db
|
||||||
@@ -751,7 +755,7 @@ const app = new Elysia()
|
|||||||
"/results/:jobId",
|
"/results/:jobId",
|
||||||
async ({ params, jwt, set, redirect, cookie: { auth, job_id } }) => {
|
async ({ params, jwt, set, redirect, cookie: { auth, job_id } }) => {
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (job_id?.value) {
|
if (job_id?.value) {
|
||||||
@@ -761,7 +765,7 @@ const app = new Elysia()
|
|||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = (await db
|
const job = (await db
|
||||||
@@ -846,7 +850,7 @@ const app = new Elysia()
|
|||||||
"/progress/:jobId",
|
"/progress/:jobId",
|
||||||
async ({ jwt, set, params, redirect, cookie: { auth, job_id } }) => {
|
async ({ jwt, set, params, redirect, cookie: { auth, job_id } }) => {
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (job_id?.value) {
|
if (job_id?.value) {
|
||||||
@@ -856,7 +860,7 @@ const app = new Elysia()
|
|||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = (await db
|
const job = (await db
|
||||||
@@ -934,12 +938,12 @@ const app = new Elysia()
|
|||||||
"/download/:userId/:jobId/:fileName",
|
"/download/:userId/:jobId/:fileName",
|
||||||
async ({ params, jwt, redirect, cookie: { auth } }) => {
|
async ({ params, jwt, redirect, cookie: { auth } }) => {
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = await db
|
const job = await db
|
||||||
@@ -947,7 +951,7 @@ const app = new Elysia()
|
|||||||
.get(user.id, params.jobId);
|
.get(user.id, params.jobId);
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
return redirect("/results");
|
return redirect("/results", 302);
|
||||||
}
|
}
|
||||||
// parse from url encoded string
|
// parse from url encoded string
|
||||||
const userId = decodeURIComponent(params.userId);
|
const userId = decodeURIComponent(params.userId);
|
||||||
@@ -960,12 +964,12 @@ const app = new Elysia()
|
|||||||
)
|
)
|
||||||
.get("/converters", async ({ jwt, redirect, cookie: { auth } }) => {
|
.get("/converters", async ({ jwt, redirect, cookie: { auth } }) => {
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -1022,12 +1026,12 @@ const app = new Elysia()
|
|||||||
async ({ params, jwt, redirect, cookie: { auth } }) => {
|
async ({ params, jwt, redirect, cookie: { auth } }) => {
|
||||||
// TODO: Implement zip download
|
// TODO: Implement zip download
|
||||||
if (!auth?.value) {
|
if (!auth?.value) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect("/login");
|
return redirect("/login", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = await db
|
const job = await db
|
||||||
@@ -1035,7 +1039,7 @@ const app = new Elysia()
|
|||||||
.get(user.id, params.jobId);
|
.get(user.id, params.jobId);
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
return redirect("/results");
|
return redirect("/results", 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userId = decodeURIComponent(params.userId);
|
const userId = decodeURIComponent(params.userId);
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
article {
|
|
||||||
/* height: 300px; */
|
|
||||||
/* width: 300px; */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
div.icon {
|
div.icon {
|
||||||
height: 100px;
|
height: 100px;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
|
|||||||
Reference in New Issue
Block a user