chore: format all files
This commit is contained in:
parent
ff2c0057e8
commit
1be11708c4
45 changed files with 2828 additions and 3057 deletions
18
.github/workflows/dockerhub-description.yml
vendored
18
.github/workflows/dockerhub-description.yml
vendored
|
|
@ -15,13 +15,13 @@ jobs:
|
||||||
dockerHubDescription:
|
dockerHubDescription:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Docker Hub Description
|
- name: Docker Hub Description
|
||||||
uses: peter-evans/dockerhub-description@v4
|
uses: peter-evans/dockerhub-description@v4
|
||||||
with:
|
with:
|
||||||
username: ${{ env.DOCKERHUB_USERNAME }}
|
username: ${{ env.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
repository: ${{ env.IMAGE_NAME }}
|
repository: ${{ env.IMAGE_NAME }}
|
||||||
short-description: ${{ github.event.repository.description }}
|
short-description: ${{ github.event.repository.description }}
|
||||||
enable-url-completion: true
|
enable-url-completion: true
|
||||||
|
|
|
||||||
10
.github/workflows/remove-docker-tag.yml
vendored
10
.github/workflows/remove-docker-tag.yml
vendored
|
|
@ -14,8 +14,8 @@ jobs:
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Remove Docker Tag
|
- name: Remove Docker Tag
|
||||||
uses: ArchieAtkinson/remove-dockertag-action@v0.0
|
uses: ArchieAtkinson/remove-dockertag-action@v0.0
|
||||||
with:
|
with:
|
||||||
tag_name: master
|
tag_name: master
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
|
||||||
156
CHANGELOG.md
156
CHANGELOG.md
|
|
@ -2,243 +2,209 @@
|
||||||
|
|
||||||
## [0.13.0](https://github.com/C4illin/ConvertX/compare/v0.12.1...v0.13.0) (2025-05-14)
|
## [0.13.0](https://github.com/C4illin/ConvertX/compare/v0.12.1...v0.13.0) (2025-05-14)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* add HIDE_HISTORY option to control visibility of history page ([9d1c931](https://github.com/C4illin/ConvertX/commit/9d1c93155cc33ed6c83f9e5122afff8f28d0e4bf))
|
- add HIDE_HISTORY option to control visibility of history page ([9d1c931](https://github.com/C4illin/ConvertX/commit/9d1c93155cc33ed6c83f9e5122afff8f28d0e4bf))
|
||||||
* add potrace converter ([bdbd4a1](https://github.com/C4illin/ConvertX/commit/bdbd4a122c09559b089b985ea12c5f3e085107da))
|
- add potrace converter ([bdbd4a1](https://github.com/C4illin/ConvertX/commit/bdbd4a122c09559b089b985ea12c5f3e085107da))
|
||||||
* Add support for .HIF files ([70705c1](https://github.com/C4illin/ConvertX/commit/70705c1850d470296df85958c02a01fb5bc3a25f))
|
- Add support for .HIF files ([70705c1](https://github.com/C4illin/ConvertX/commit/70705c1850d470296df85958c02a01fb5bc3a25f))
|
||||||
* add support for drag/drop of images ([ff2ef74](https://github.com/C4illin/ConvertX/commit/ff2ef7413542cf10ba7a6e246763bcecd6829ec1))
|
- add support for drag/drop of images ([ff2ef74](https://github.com/C4illin/ConvertX/commit/ff2ef7413542cf10ba7a6e246763bcecd6829ec1))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* add timezone support ([4b5c732](https://github.com/C4illin/ConvertX/commit/4b5c732380bc844dccf340ea1eb4f8bfe3bb44a5)), closes [#258](https://github.com/C4illin/ConvertX/issues/258)
|
- add timezone support ([4b5c732](https://github.com/C4illin/ConvertX/commit/4b5c732380bc844dccf340ea1eb4f8bfe3bb44a5)), closes [#258](https://github.com/C4illin/ConvertX/issues/258)
|
||||||
|
|
||||||
## [0.12.1](https://github.com/C4illin/ConvertX/compare/v0.12.0...v0.12.1) (2025-03-20)
|
## [0.12.1](https://github.com/C4illin/ConvertX/compare/v0.12.0...v0.12.1) (2025-03-20)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* rollback to bun 1.2.2 ([cdae798](https://github.com/C4illin/ConvertX/commit/cdae798fcf5879e4adea87386a38748b9a1e1ddc))
|
- rollback to bun 1.2.2 ([cdae798](https://github.com/C4illin/ConvertX/commit/cdae798fcf5879e4adea87386a38748b9a1e1ddc))
|
||||||
|
|
||||||
## [0.12.0](https://github.com/C4illin/ConvertX/compare/v0.11.1...v0.12.0) (2025-03-06)
|
## [0.12.0](https://github.com/C4illin/ConvertX/compare/v0.11.1...v0.12.0) (2025-03-06)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* added progress bar for file upload ([db60f35](https://github.com/C4illin/ConvertX/commit/db60f355b2973f43f8e5990e6fe4e351b959b659))
|
- added progress bar for file upload ([db60f35](https://github.com/C4illin/ConvertX/commit/db60f355b2973f43f8e5990e6fe4e351b959b659))
|
||||||
* made every upload file independent ([cc54bdc](https://github.com/C4illin/ConvertX/commit/cc54bdcbe764c41cc3273485d072fd3178ad2dca))
|
- made every upload file independent ([cc54bdc](https://github.com/C4illin/ConvertX/commit/cc54bdcbe764c41cc3273485d072fd3178ad2dca))
|
||||||
* replace exec with execFile ([9263d17](https://github.com/C4illin/ConvertX/commit/9263d17609dc4b2b367eb7fee67b3182e283b3a3))
|
- replace exec with execFile ([9263d17](https://github.com/C4illin/ConvertX/commit/9263d17609dc4b2b367eb7fee67b3182e283b3a3))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* add libheif ([6b92540](https://github.com/C4illin/ConvertX/commit/6b9254047c0598963aee1d99e20ba1650a0368bf))
|
- add libheif ([6b92540](https://github.com/C4illin/ConvertX/commit/6b9254047c0598963aee1d99e20ba1650a0368bf))
|
||||||
* add libheif ([decfea5](https://github.com/C4illin/ConvertX/commit/decfea5dc9627b216bb276a9e1578c32cfa1deb6)), closes [#202](https://github.com/C4illin/ConvertX/issues/202)
|
- add libheif ([decfea5](https://github.com/C4illin/ConvertX/commit/decfea5dc9627b216bb276a9e1578c32cfa1deb6)), closes [#202](https://github.com/C4illin/ConvertX/issues/202)
|
||||||
* added onerror log ([ae4bbc8](https://github.com/C4illin/ConvertX/commit/ae4bbc8baacbaf67763c62ea44140bb21cc17230))
|
- added onerror log ([ae4bbc8](https://github.com/C4illin/ConvertX/commit/ae4bbc8baacbaf67763c62ea44140bb21cc17230))
|
||||||
* refactored uploadFile to only accept a single file instead of multiple ([dc82a43](https://github.com/C4illin/ConvertX/commit/dc82a438d4104b79ff423d502a6779a43928968a))
|
- refactored uploadFile to only accept a single file instead of multiple ([dc82a43](https://github.com/C4illin/ConvertX/commit/dc82a438d4104b79ff423d502a6779a43928968a))
|
||||||
* update libheif to 1.19.5 ([fba5e21](https://github.com/C4illin/ConvertX/commit/fba5e212e8d0eaba8971e239e35aeb521f3cd813)), closes [#202](https://github.com/C4illin/ConvertX/issues/202)
|
- update libheif to 1.19.5 ([fba5e21](https://github.com/C4illin/ConvertX/commit/fba5e212e8d0eaba8971e239e35aeb521f3cd813)), closes [#202](https://github.com/C4illin/ConvertX/issues/202)
|
||||||
|
|
||||||
## [0.11.1](https://github.com/C4illin/ConvertX/compare/v0.11.0...v0.11.1) (2025-02-07)
|
## [0.11.1](https://github.com/C4illin/ConvertX/compare/v0.11.0...v0.11.1) (2025-02-07)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* mobile view overflow ([bec58ac](https://github.com/C4illin/ConvertX/commit/bec58ac59f9600e35385b9e21d174f3ab1b42b1d))
|
- mobile view overflow ([bec58ac](https://github.com/C4illin/ConvertX/commit/bec58ac59f9600e35385b9e21d174f3ab1b42b1d))
|
||||||
|
|
||||||
## [0.11.0](https://github.com/C4illin/ConvertX/compare/v0.10.1...v0.11.0) (2025-02-05)
|
## [0.11.0](https://github.com/C4illin/ConvertX/compare/v0.10.1...v0.11.0) (2025-02-05)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* add deps for vaapi ([2bbbd03](https://github.com/C4illin/ConvertX/commit/2bbbd03554d384a4488143f29e5fc863cfdf333b)), closes [#192](https://github.com/C4illin/ConvertX/issues/192)
|
- add deps for vaapi ([2bbbd03](https://github.com/C4illin/ConvertX/commit/2bbbd03554d384a4488143f29e5fc863cfdf333b)), closes [#192](https://github.com/C4illin/ConvertX/issues/192)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* don't crash if file is not found ([16f27c1](https://github.com/C4illin/ConvertX/commit/16f27c13bbc1c0e5fa2316f3db11d0918524053b))
|
- don't crash if file is not found ([16f27c1](https://github.com/C4illin/ConvertX/commit/16f27c13bbc1c0e5fa2316f3db11d0918524053b))
|
||||||
* install numpy for inkscape ([0e61051](https://github.com/C4illin/ConvertX/commit/0e61051fc6be188164c3865b4fb579c140859fdc))
|
- install numpy for inkscape ([0e61051](https://github.com/C4illin/ConvertX/commit/0e61051fc6be188164c3865b4fb579c140859fdc))
|
||||||
|
|
||||||
## [0.10.1](https://github.com/C4illin/ConvertX/compare/v0.10.0...v0.10.1) (2025-01-21)
|
## [0.10.1](https://github.com/C4illin/ConvertX/compare/v0.10.0...v0.10.1) (2025-01-21)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* ffmpeg works without ffmpeg_args ([3b7ea88](https://github.com/C4illin/ConvertX/commit/3b7ea88b7382f7c21b120bdc9bda5bb10547f55d)), closes [#212](https://github.com/C4illin/ConvertX/issues/212)
|
- ffmpeg works without ffmpeg_args ([3b7ea88](https://github.com/C4illin/ConvertX/commit/3b7ea88b7382f7c21b120bdc9bda5bb10547f55d)), closes [#212](https://github.com/C4illin/ConvertX/issues/212)
|
||||||
|
|
||||||
## [0.10.0](https://github.com/C4illin/ConvertX/compare/v0.9.0...v0.10.0) (2025-01-18)
|
## [0.10.0](https://github.com/C4illin/ConvertX/compare/v0.9.0...v0.10.0) (2025-01-18)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* add calibre ([03d3edf](https://github.com/C4illin/ConvertX/commit/03d3edfff65c252dd4b8922fc98257c089c1ff74)), closes [#191](https://github.com/C4illin/ConvertX/issues/191)
|
- add calibre ([03d3edf](https://github.com/C4illin/ConvertX/commit/03d3edfff65c252dd4b8922fc98257c089c1ff74)), closes [#191](https://github.com/C4illin/ConvertX/issues/191)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* add FFMPEG_ARGS env variable ([f537c81](https://github.com/C4illin/ConvertX/commit/f537c81db7815df8017f834e3162291197e1c40f)), closes [#190](https://github.com/C4illin/ConvertX/issues/190)
|
- add FFMPEG_ARGS env variable ([f537c81](https://github.com/C4illin/ConvertX/commit/f537c81db7815df8017f834e3162291197e1c40f)), closes [#190](https://github.com/C4illin/ConvertX/issues/190)
|
||||||
* add qt6-qtbase-private-dev from community repo ([95dbc9f](https://github.com/C4illin/ConvertX/commit/95dbc9f678bec7e6e2c03587e1473fb8ff708ea3))
|
- add qt6-qtbase-private-dev from community repo ([95dbc9f](https://github.com/C4illin/ConvertX/commit/95dbc9f678bec7e6e2c03587e1473fb8ff708ea3))
|
||||||
* skip account setup when ALLOW_UNAUTHENTICATED is true ([538c5b6](https://github.com/C4illin/ConvertX/commit/538c5b60c9e27a8184740305475245da79bae143))
|
- skip account setup when ALLOW_UNAUTHENTICATED is true ([538c5b6](https://github.com/C4illin/ConvertX/commit/538c5b60c9e27a8184740305475245da79bae143))
|
||||||
|
|
||||||
## [0.9.0](https://github.com/C4illin/ConvertX/compare/v0.8.1...v0.9.0) (2024-11-21)
|
## [0.9.0](https://github.com/C4illin/ConvertX/compare/v0.8.1...v0.9.0) (2024-11-21)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* add inkscape for vector images ([f3740e9](https://github.com/C4illin/ConvertX/commit/f3740e9ded100b8500f3613517960248bbd3c210))
|
- add inkscape for vector images ([f3740e9](https://github.com/C4illin/ConvertX/commit/f3740e9ded100b8500f3613517960248bbd3c210))
|
||||||
* Allow to chose webroot ([36cb6cc](https://github.com/C4illin/ConvertX/commit/36cb6cc589d80d0a87fa8dbe605db71a9a2570f9)), closes [#180](https://github.com/C4illin/ConvertX/issues/180)
|
- Allow to chose webroot ([36cb6cc](https://github.com/C4illin/ConvertX/commit/36cb6cc589d80d0a87fa8dbe605db71a9a2570f9)), closes [#180](https://github.com/C4illin/ConvertX/issues/180)
|
||||||
* disable convert when uploading ([58e220e](https://github.com/C4illin/ConvertX/commit/58e220e82d7f9c163d6ea4dc31092c08a3e254f4)), closes [#177](https://github.com/C4illin/ConvertX/issues/177)
|
- disable convert when uploading ([58e220e](https://github.com/C4illin/ConvertX/commit/58e220e82d7f9c163d6ea4dc31092c08a3e254f4)), closes [#177](https://github.com/C4illin/ConvertX/issues/177)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* treat unknown as m4a ([1a442d6](https://github.com/C4illin/ConvertX/commit/1a442d6e69606afef63b1e7df36aa83d111fa23d)), closes [#178](https://github.com/C4illin/ConvertX/issues/178)
|
- treat unknown as m4a ([1a442d6](https://github.com/C4illin/ConvertX/commit/1a442d6e69606afef63b1e7df36aa83d111fa23d)), closes [#178](https://github.com/C4illin/ConvertX/issues/178)
|
||||||
* wait for both upload and selection ([4c05fd7](https://github.com/C4illin/ConvertX/commit/4c05fd72bbbf91ee02327f6fcbf749b78272376b)), closes [#177](https://github.com/C4illin/ConvertX/issues/177)
|
- wait for both upload and selection ([4c05fd7](https://github.com/C4illin/ConvertX/commit/4c05fd72bbbf91ee02327f6fcbf749b78272376b)), closes [#177](https://github.com/C4illin/ConvertX/issues/177)
|
||||||
|
|
||||||
## [0.8.1](https://github.com/C4illin/ConvertX/compare/v0.8.0...v0.8.1) (2024-10-05)
|
## [0.8.1](https://github.com/C4illin/ConvertX/compare/v0.8.0...v0.8.1) (2024-10-05)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* disable convert button when input is empty ([78844d7](https://github.com/C4illin/ConvertX/commit/78844d7bd55990789ed07c81e49043e688cbe656)), closes [#151](https://github.com/C4illin/ConvertX/issues/151)
|
- disable convert button when input is empty ([78844d7](https://github.com/C4illin/ConvertX/commit/78844d7bd55990789ed07c81e49043e688cbe656)), closes [#151](https://github.com/C4illin/ConvertX/issues/151)
|
||||||
* resize to fit for ico ([b4e53db](https://github.com/C4illin/ConvertX/commit/b4e53dbb8e70b3a95b44e5b756759d16117a87e1)), closes [#157](https://github.com/C4illin/ConvertX/issues/157)
|
- resize to fit for ico ([b4e53db](https://github.com/C4illin/ConvertX/commit/b4e53dbb8e70b3a95b44e5b756759d16117a87e1)), closes [#157](https://github.com/C4illin/ConvertX/issues/157)
|
||||||
* treat jfif as jpeg ([339b79f](https://github.com/C4illin/ConvertX/commit/339b79f786131deb93f0d5683e03178fdcab1ef5)), closes [#163](https://github.com/C4illin/ConvertX/issues/163)
|
- treat jfif as jpeg ([339b79f](https://github.com/C4illin/ConvertX/commit/339b79f786131deb93f0d5683e03178fdcab1ef5)), closes [#163](https://github.com/C4illin/ConvertX/issues/163)
|
||||||
|
|
||||||
## [0.8.0](https://github.com/C4illin/ConvertX/compare/v0.7.0...v0.8.0) (2024-09-30)
|
## [0.8.0](https://github.com/C4illin/ConvertX/compare/v0.7.0...v0.8.0) (2024-09-30)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* add light theme, fixes [#156](https://github.com/C4illin/ConvertX/issues/156) ([72636c5](https://github.com/C4illin/ConvertX/commit/72636c5059ebf09c8fece2e268293650b2f8ccf6))
|
- add light theme, fixes [#156](https://github.com/C4illin/ConvertX/issues/156) ([72636c5](https://github.com/C4illin/ConvertX/commit/72636c5059ebf09c8fece2e268293650b2f8ccf6))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* add support for usd for assimp, [#144](https://github.com/C4illin/ConvertX/issues/144) ([2057167](https://github.com/C4illin/ConvertX/commit/20571675766209ad1251f07e687d29a6791afc8b))
|
- add support for usd for assimp, [#144](https://github.com/C4illin/ConvertX/issues/144) ([2057167](https://github.com/C4illin/ConvertX/commit/20571675766209ad1251f07e687d29a6791afc8b))
|
||||||
* cleanup formats and add opus, fixes [#159](https://github.com/C4illin/ConvertX/issues/159) ([ae1dfaf](https://github.com/C4illin/ConvertX/commit/ae1dfafc9d9116a57b08c2f7fc326990e00824b0))
|
- cleanup formats and add opus, fixes [#159](https://github.com/C4illin/ConvertX/issues/159) ([ae1dfaf](https://github.com/C4illin/ConvertX/commit/ae1dfafc9d9116a57b08c2f7fc326990e00824b0))
|
||||||
* support .awb and clean up, fixes [#153](https://github.com/C4illin/ConvertX/issues/153), [#92](https://github.com/C4illin/ConvertX/issues/92) ([1c9e67f](https://github.com/C4illin/ConvertX/commit/1c9e67fc3201e0e5dee91e8981adf34daaabf33a))
|
- support .awb and clean up, fixes [#153](https://github.com/C4illin/ConvertX/issues/153), [#92](https://github.com/C4illin/ConvertX/issues/92) ([1c9e67f](https://github.com/C4illin/ConvertX/commit/1c9e67fc3201e0e5dee91e8981adf34daaabf33a))
|
||||||
|
|
||||||
## [0.7.0](https://github.com/C4illin/ConvertX/compare/v0.6.0...v0.7.0) (2024-09-26)
|
## [0.7.0](https://github.com/C4illin/ConvertX/compare/v0.6.0...v0.7.0) (2024-09-26)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Add support for 3d assets through assimp converter ([63a4328](https://github.com/C4illin/ConvertX/commit/63a4328d4a1e01df3e0ec4a877bad8c8ffe71129))
|
- Add support for 3d assets through assimp converter ([63a4328](https://github.com/C4illin/ConvertX/commit/63a4328d4a1e01df3e0ec4a877bad8c8ffe71129))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* wrong layout on search with few options ([8817389](https://github.com/C4illin/ConvertX/commit/88173891ba2d69da46eda46f3f598a9b54f26f96))
|
- wrong layout on search with few options ([8817389](https://github.com/C4illin/ConvertX/commit/88173891ba2d69da46eda46f3f598a9b54f26f96))
|
||||||
|
|
||||||
## [0.6.0](https://github.com/C4illin/ConvertX/compare/v0.5.0...v0.6.0) (2024-09-25)
|
## [0.6.0](https://github.com/C4illin/ConvertX/compare/v0.5.0...v0.6.0) (2024-09-25)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* ui remake with tailwind ([22f823c](https://github.com/C4illin/ConvertX/commit/22f823c535b20382981f86a13616b830a1f3392f))
|
- ui remake with tailwind ([22f823c](https://github.com/C4illin/ConvertX/commit/22f823c535b20382981f86a13616b830a1f3392f))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* rename css file to force update cache, fixes [#141](https://github.com/C4illin/ConvertX/issues/141) ([47139a5](https://github.com/C4illin/ConvertX/commit/47139a550bd3d847da288c61bf8f88953b79c673))
|
- rename css file to force update cache, fixes [#141](https://github.com/C4illin/ConvertX/issues/141) ([47139a5](https://github.com/C4illin/ConvertX/commit/47139a550bd3d847da288c61bf8f88953b79c673))
|
||||||
|
|
||||||
## [0.5.0](https://github.com/C4illin/ConvertX/compare/v0.4.1...v0.5.0) (2024-09-20)
|
## [0.5.0](https://github.com/C4illin/ConvertX/compare/v0.4.1...v0.5.0) (2024-09-20)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* add option to customize how often files are automatically deleted ([317c932](https://github.com/C4illin/ConvertX/commit/317c932c2a26280bf37ed3d3bf9b879413590f5a))
|
- add option to customize how often files are automatically deleted ([317c932](https://github.com/C4illin/ConvertX/commit/317c932c2a26280bf37ed3d3bf9b879413590f5a))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* improve file name replacement logic ([60ba7c9](https://github.com/C4illin/ConvertX/commit/60ba7c93fbdc961f3569882fade7cc13dee7a7a5))
|
- improve file name replacement logic ([60ba7c9](https://github.com/C4illin/ConvertX/commit/60ba7c93fbdc961f3569882fade7cc13dee7a7a5))
|
||||||
|
|
||||||
## [0.4.1](https://github.com/C4illin/ConvertX/compare/v0.4.0...v0.4.1) (2024-09-15)
|
## [0.4.1](https://github.com/C4illin/ConvertX/compare/v0.4.0...v0.4.1) (2024-09-15)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* allow non lowercase true and false values, fixes [#122](https://github.com/C4illin/ConvertX/issues/122) ([bef1710](https://github.com/C4illin/ConvertX/commit/bef1710e3376baa7e25c107ded20a40d18b8c6b0))
|
- allow non lowercase true and false values, fixes [#122](https://github.com/C4illin/ConvertX/issues/122) ([bef1710](https://github.com/C4illin/ConvertX/commit/bef1710e3376baa7e25c107ded20a40d18b8c6b0))
|
||||||
|
|
||||||
## [0.4.0](https://github.com/C4illin/ConvertX/compare/v0.3.3...v0.4.0) (2024-08-26)
|
## [0.4.0](https://github.com/C4illin/ConvertX/compare/v0.3.3...v0.4.0) (2024-08-26)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* add option for unauthenticated file conversions [#114](https://github.com/C4illin/ConvertX/issues/114) ([f0d0e43](https://github.com/C4illin/ConvertX/commit/f0d0e4392983c3e4c530304ea88e023fda9bcac0))
|
- add option for unauthenticated file conversions [#114](https://github.com/C4illin/ConvertX/issues/114) ([f0d0e43](https://github.com/C4illin/ConvertX/commit/f0d0e4392983c3e4c530304ea88e023fda9bcac0))
|
||||||
* add resvg converter ([d5eeef9](https://github.com/C4illin/ConvertX/commit/d5eeef9f6884b2bb878508bed97ea9ceaa662995))
|
- add resvg converter ([d5eeef9](https://github.com/C4illin/ConvertX/commit/d5eeef9f6884b2bb878508bed97ea9ceaa662995))
|
||||||
* add robots.txt ([6597c1d](https://github.com/C4illin/ConvertX/commit/6597c1d7caeb4dfb6bc47b442e4dfc9840ad12b7))
|
- add robots.txt ([6597c1d](https://github.com/C4illin/ConvertX/commit/6597c1d7caeb4dfb6bc47b442e4dfc9840ad12b7))
|
||||||
* Add search bar for formats ([53fff59](https://github.com/C4illin/ConvertX/commit/53fff594fc4d69306abcb2a5cad890fcd0953a58))
|
- Add search bar for formats ([53fff59](https://github.com/C4illin/ConvertX/commit/53fff594fc4d69306abcb2a5cad890fcd0953a58))
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* keep unauthenticated user logged in if allowed [#114](https://github.com/C4illin/ConvertX/issues/114) ([bc4ad49](https://github.com/C4illin/ConvertX/commit/bc4ad492852fad8cb832a0c03485cccdd7f7b117))
|
- keep unauthenticated user logged in if allowed [#114](https://github.com/C4illin/ConvertX/issues/114) ([bc4ad49](https://github.com/C4illin/ConvertX/commit/bc4ad492852fad8cb832a0c03485cccdd7f7b117))
|
||||||
* pdf support in vips ([8ca4f15](https://github.com/C4illin/ConvertX/commit/8ca4f1587df7f358893941c656d78d75f04dac93))
|
- pdf support in vips ([8ca4f15](https://github.com/C4illin/ConvertX/commit/8ca4f1587df7f358893941c656d78d75f04dac93))
|
||||||
* Slow click on conversion popup does not work ([4d9c4d6](https://github.com/C4illin/ConvertX/commit/4d9c4d64aa0266f3928935ada68d91ac81f638aa))
|
- Slow click on conversion popup does not work ([4d9c4d6](https://github.com/C4illin/ConvertX/commit/4d9c4d64aa0266f3928935ada68d91ac81f638aa))
|
||||||
|
|
||||||
## [0.3.3](https://github.com/C4illin/ConvertX/compare/v0.3.2...v0.3.3) (2024-07-30)
|
## [0.3.3](https://github.com/C4illin/ConvertX/compare/v0.3.2...v0.3.3) (2024-07-30)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* downgrade @elysiajs/html dependency to version 1.0.2 ([c714ade](https://github.com/C4illin/ConvertX/commit/c714ade3e23865ba6cfaf76c9e7259df1cda222c))
|
- downgrade @elysiajs/html dependency to version 1.0.2 ([c714ade](https://github.com/C4illin/ConvertX/commit/c714ade3e23865ba6cfaf76c9e7259df1cda222c))
|
||||||
|
|
||||||
## [0.3.2](https://github.com/C4illin/ConvertX/compare/v0.3.1...v0.3.2) (2024-07-09)
|
## [0.3.2](https://github.com/C4illin/ConvertX/compare/v0.3.1...v0.3.2) (2024-07-09)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* increase max request body to support large uploads ([3ae2db5](https://github.com/C4illin/ConvertX/commit/3ae2db5d9b36fe3dcd4372ddcd32aa573ea59aa6)), closes [#64](https://github.com/C4illin/ConvertX/issues/64)
|
- increase max request body to support large uploads ([3ae2db5](https://github.com/C4illin/ConvertX/commit/3ae2db5d9b36fe3dcd4372ddcd32aa573ea59aa6)), closes [#64](https://github.com/C4illin/ConvertX/issues/64)
|
||||||
|
|
||||||
## [0.3.1](https://github.com/C4illin/ConvertX/compare/v0.3.0...v0.3.1) (2024-06-27)
|
## [0.3.1](https://github.com/C4illin/ConvertX/compare/v0.3.0...v0.3.1) (2024-06-27)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* release releases ([4d4c13a](https://github.com/C4illin/ConvertX/commit/4d4c13a8d85ec7c9209ad41cdbea7d4380b0edbf))
|
- 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)
|
## [0.3.0](https://github.com/C4illin/ConvertX/compare/v0.2.0...v0.3.0) (2024-06-27)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* add version number to log ([4dcb796](https://github.com/C4illin/ConvertX/commit/4dcb796e1bd27badc078d0638076cd9f1e81c4a4)), closes [#44](https://github.com/C4illin/ConvertX/issues/44)
|
- 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))
|
- 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))
|
- 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)
|
## [0.2.0](https://github.com/C4illin/ConvertX/compare/v0.1.2...v0.2.0) (2024-06-20)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* add libjxl for jpegxl conversion ([ff680cb](https://github.com/C4illin/ConvertX/commit/ff680cb29534a25c3148a90fd064bb86c71fb482))
|
- 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)
|
- 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)
|
## [0.1.2](https://github.com/C4illin/ConvertX/compare/v0.1.1...v0.1.2) (2024-06-10)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* fix incorrect redirect ([25df58b](https://github.com/C4illin/ConvertX/commit/25df58ba82321aaa6617811a6995cb96c2a00a40)), closes [#23](https://github.com/C4illin/ConvertX/issues/23)
|
- 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)
|
## [0.1.1](https://github.com/C4illin/ConvertX/compare/v0.1.0...v0.1.1) (2024-05-30)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### 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)
|
- :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)
|
## 0.1.0 (2024-05-30)
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* remove file from file list in index.html ([787ff97](https://github.com/C4illin/ConvertX/commit/787ff9741ecbbf4fb4c02b43bd22a214a173fd7b))
|
- remove file from file list in index.html ([787ff97](https://github.com/C4illin/ConvertX/commit/787ff9741ecbbf4fb4c02b43bd22a214a173fd7b))
|
||||||
|
|
||||||
|
|
||||||
### Miscellaneous Chores
|
### Miscellaneous Chores
|
||||||
|
|
||||||
* release 0.1.0 ([54d9aec](https://github.com/C4illin/ConvertX/commit/54d9aecbf949689b12aa7e5e8e9be7b9032f4431))
|
- release 0.1.0 ([54d9aec](https://github.com/C4illin/ConvertX/commit/54d9aecbf949689b12aa7e5e8e9be7b9032f4431))
|
||||||
|
|
|
||||||
67
README.md
67
README.md
|
|
@ -11,6 +11,7 @@
|
||||||

|

|
||||||
|
|
||||||
<a href="https://trendshift.io/repositories/13818" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13818" alt="C4illin%2FConvertX | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
<a href="https://trendshift.io/repositories/13818" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13818" alt="C4illin%2FConvertX | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
<!--  -->
|
<!--  -->
|
||||||
|
|
||||||
A self-hosted online file converter. Supports over a thousand different formats. Written with TypeScript, Bun and Elysia.
|
A self-hosted online file converter. Supports over a thousand different formats. Written with TypeScript, Bun and Elysia.
|
||||||
|
|
@ -24,23 +25,22 @@ A self-hosted online file converter. Supports over a thousand different formats.
|
||||||
|
|
||||||
## Converters supported
|
## Converters supported
|
||||||
|
|
||||||
| Converter | Use case | Converts from | Converts to |
|
| Converter | Use case | Converts from | Converts to |
|
||||||
|------------------------------------------------------------------------------|---------------|---------------|-------------|
|
| ------------------------------------------------ | ---------------- | ------------- | ----------- |
|
||||||
| [libjxl](https://github.com/libjxl/libjxl) | JPEG XL | 11 | 11 |
|
| [libjxl](https://github.com/libjxl/libjxl) | JPEG XL | 11 | 11 |
|
||||||
| [resvg](https://github.com/RazrFalcon/resvg) | SVG | 1 | 1 |
|
| [resvg](https://github.com/RazrFalcon/resvg) | SVG | 1 | 1 |
|
||||||
| [Vips](https://github.com/libvips/libvips) | Images | 45 | 23 |
|
| [Vips](https://github.com/libvips/libvips) | Images | 45 | 23 |
|
||||||
| [libheif](https://github.com/strukturag/libheif) | HEIF | 2 | 4 |
|
| [libheif](https://github.com/strukturag/libheif) | HEIF | 2 | 4 |
|
||||||
| [XeLaTeX](https://tug.org/xetex/) | LaTeX | 1 | 1 |
|
| [XeLaTeX](https://tug.org/xetex/) | LaTeX | 1 | 1 |
|
||||||
| [Calibre](https://calibre-ebook.com/) | E-books | 26 | 19 |
|
| [Calibre](https://calibre-ebook.com/) | E-books | 26 | 19 |
|
||||||
| [Pandoc](https://pandoc.org/) | Documents | 43 | 65 |
|
| [Pandoc](https://pandoc.org/) | Documents | 43 | 65 |
|
||||||
| [dvisvgm](https://dvisvgm.de/) | Vector images | 4 | 2 |
|
| [dvisvgm](https://dvisvgm.de/) | Vector images | 4 | 2 |
|
||||||
| [ImageMagick](https://imagemagick.org/) | Images | 245 | 183 |
|
| [ImageMagick](https://imagemagick.org/) | Images | 245 | 183 |
|
||||||
| [GraphicsMagick](http://www.graphicsmagick.org/) | Images | 167 | 130 |
|
| [GraphicsMagick](http://www.graphicsmagick.org/) | Images | 167 | 130 |
|
||||||
| [Inkscape](https://inkscape.org/) | Vector images | 7 | 17 |
|
| [Inkscape](https://inkscape.org/) | Vector images | 7 | 17 |
|
||||||
| [Assimp](https://github.com/assimp/assimp) | 3D Assets | 77 | 23 |
|
| [Assimp](https://github.com/assimp/assimp) | 3D Assets | 77 | 23 |
|
||||||
| [FFmpeg](https://ffmpeg.org/) | Video | ~472 | ~199 |
|
| [FFmpeg](https://ffmpeg.org/) | Video | ~472 | ~199 |
|
||||||
| [Potrace](https://potrace.sourceforge.net/) | Raster to vector | 4 | 11 |
|
| [Potrace](https://potrace.sourceforge.net/) | Raster to vector | 4 | 11 |
|
||||||
|
|
||||||
|
|
||||||
<!-- many ffmpeg fileformats are duplicates -->
|
<!-- many ffmpeg fileformats are duplicates -->
|
||||||
|
|
||||||
|
|
@ -80,16 +80,16 @@ If you get unable to open database file run `chown -R $USER:$USER path` on the p
|
||||||
|
|
||||||
All are optional, JWT_SECRET is recommended to be set.
|
All are optional, JWT_SECRET is recommended to be set.
|
||||||
|
|
||||||
| Name | Default | Description |
|
| Name | Default | Description |
|
||||||
|---------------------------|---------|-------------|
|
| ------------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
||||||
| JWT_SECRET | when unset it will use the value from randomUUID() | A long and secret string used to sign the JSON Web Token |
|
| JWT_SECRET | when unset it will use the value from randomUUID() | A long and secret string used to sign the JSON Web Token |
|
||||||
| ACCOUNT_REGISTRATION | false | Allow users to register accounts |
|
| ACCOUNT_REGISTRATION | false | Allow users to register accounts |
|
||||||
| HTTP_ALLOWED | false | Allow HTTP connections, only set this to true locally |
|
| HTTP_ALLOWED | false | Allow HTTP connections, only set this to true locally |
|
||||||
| ALLOW_UNAUTHENTICATED | false | Allow unauthenticated users to use the service, only set this to true locally |
|
| ALLOW_UNAUTHENTICATED | false | Allow unauthenticated users to use the service, only set this to true locally |
|
||||||
| AUTO_DELETE_EVERY_N_HOURS | 24 | Checks every n hours for files older then n hours and deletes them, set to 0 to disable |
|
| AUTO_DELETE_EVERY_N_HOURS | 24 | Checks every n hours for files older then n hours and deletes them, set to 0 to disable |
|
||||||
| WEBROOT | | The address to the root path setting this to "/convert" will serve the website on "example.com/convert/" |
|
| WEBROOT | | The address to the root path setting this to "/convert" will serve the website on "example.com/convert/" |
|
||||||
| FFMPEG_ARGS | | Arguments to pass to ffmpeg, e.g. `-preset veryfast` |
|
| FFMPEG_ARGS | | Arguments to pass to ffmpeg, e.g. `-preset veryfast` |
|
||||||
| HIDE_HISTORY | false | Hide the history page |
|
| HIDE_HISTORY | false | Hide the history page |
|
||||||
|
|
||||||
### Docker images
|
### Docker images
|
||||||
|
|
||||||
|
|
@ -97,15 +97,16 @@ There is a `:latest` tag that is updated with every release and a `:main` tag th
|
||||||
|
|
||||||
The image is available on [GitHub Container Registry](https://github.com/C4illin/ConvertX/pkgs/container/ConvertX) and [Docker Hub](https://hub.docker.com/r/c4illin/convertx).
|
The image is available on [GitHub Container Registry](https://github.com/C4illin/ConvertX/pkgs/container/ConvertX) and [Docker Hub](https://hub.docker.com/r/c4illin/convertx).
|
||||||
|
|
||||||
| Image | What it is |
|
| Image | What it is |
|
||||||
|-------|------------|
|
| -------------------------------------- | -------------------------------- |
|
||||||
| `image: ghcr.io/c4illin/convertx` | The latest release on ghcr |
|
| `image: ghcr.io/c4illin/convertx` | The latest release on ghcr |
|
||||||
| `image: ghcr.io/c4illin/convertx:main` | The latest commit on ghcr |
|
| `image: ghcr.io/c4illin/convertx:main` | The latest commit on ghcr |
|
||||||
| `image: c4illin/convertx` | The latest release on docker hub |
|
| `image: c4illin/convertx` | The latest release on docker hub |
|
||||||
| `image: c4illin/convertx:main` | The latest commit on docker hub |
|
| `image: c4illin/convertx:main` | The latest commit on docker hub |
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
<!-- Dockerhub was introduced in 0.9.0 and older releases -->
|
<!-- Dockerhub was introduced in 0.9.0 and older releases -->
|
||||||
|
|
||||||
### Tutorial
|
### Tutorial
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,7 @@
|
||||||
"attributePosition": "auto"
|
"attributePosition": "auto"
|
||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
"ignore": [
|
"ignore": ["**/node_modules/**", "**/pico.lime.min.css"]
|
||||||
"**/node_modules/**",
|
|
||||||
"**/pico.lime.min.css"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"organizeImports": {
|
"organizeImports": {
|
||||||
"enabled": true
|
"enabled": true
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@ export default [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
...eslintPluginBetterTailwindcss.configs["recommended-warn"]?.rules,
|
...(eslintPluginBetterTailwindcss.configs["recommended-warn"] ?? {}).rules,
|
||||||
...eslintPluginBetterTailwindcss.configs["stylistic-warn"]?.rules,
|
...(eslintPluginBetterTailwindcss.configs["stylistic-warn"] ?? {}).rules,
|
||||||
// "tailwindcss/classnames-order": "off",
|
// "tailwindcss/classnames-order": "off",
|
||||||
"better-tailwindcss/multiline": [
|
"better-tailwindcss/multiline": [
|
||||||
"warn",
|
"warn",
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,15 @@
|
||||||
"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",
|
||||||
"format": "eslint --fix .",
|
"format": "run-p 'format:*'",
|
||||||
|
"format:eslint": "eslint --fix .",
|
||||||
|
"format:prettier": "prettier --write .",
|
||||||
"build": "bunx @tailwindcss/cli -i ./src/main.css -o ./public/generated.css",
|
"build": "bunx @tailwindcss/cli -i ./src/main.css -o ./public/generated.css",
|
||||||
"lint": "run-p 'lint:*'",
|
"lint": "run-p 'lint:*'",
|
||||||
"lint:tsc": "tsc --noEmit",
|
"lint:tsc": "tsc --noEmit",
|
||||||
"lint:knip": "knip",
|
"lint:knip": "knip",
|
||||||
"lint:eslint": "eslint ."
|
"lint:eslint": "eslint .",
|
||||||
|
"lint:prettier": "prettier --check ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@elysiajs/html": "^1.3.0",
|
"@elysiajs/html": "^1.3.0",
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
const config = {
|
const config = {
|
||||||
arrowParens: "always",
|
arrowParens: "always",
|
||||||
printWidth: 80,
|
printWidth: 100,
|
||||||
singleQuote: false,
|
singleQuote: false,
|
||||||
semi: true,
|
semi: true,
|
||||||
tabWidth: 2,
|
tabWidth: 2,
|
||||||
|
|
|
||||||
|
|
@ -72,9 +72,7 @@ function handleFile(file) {
|
||||||
const selectContainer = document.querySelector("form .select_container");
|
const selectContainer = document.querySelector("form .select_container");
|
||||||
|
|
||||||
const updateSearchBar = () => {
|
const updateSearchBar = () => {
|
||||||
const convertToInput = document.querySelector(
|
const convertToInput = document.querySelector("input[name='convert_to_search']");
|
||||||
"input[name='convert_to_search']",
|
|
||||||
);
|
|
||||||
const convertToPopup = document.querySelector(".convert_to_popup");
|
const convertToPopup = document.querySelector(".convert_to_popup");
|
||||||
const convertToGroupElements = document.querySelectorAll(".convert_to_group");
|
const convertToGroupElements = document.querySelectorAll(".convert_to_group");
|
||||||
const convertToGroups = {};
|
const convertToGroups = {};
|
||||||
|
|
@ -195,8 +193,7 @@ const deleteRow = (target) => {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
})
|
}).catch((err) => console.log(err));
|
||||||
.catch((err) => console.log(err));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const uploadFile = (file) => {
|
const uploadFile = (file) => {
|
||||||
|
|
@ -234,7 +231,7 @@ const uploadFile = (file) => {
|
||||||
console.log(`upload progress (${file.name}):`, (100 * sent) / total);
|
console.log(`upload progress (${file.name}):`, (100 * sent) / total);
|
||||||
|
|
||||||
let progressbar = file.htmlRow.getElementsByTagName("progress");
|
let progressbar = file.htmlRow.getElementsByTagName("progress");
|
||||||
progressbar[0].value = ((100 * sent) / total);
|
progressbar[0].value = (100 * sent) / total;
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.onerror = (e) => {
|
xhr.onerror = (e) => {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
"extends": [
|
"extends": ["config:recommended", ":disableDependencyDashboard"],
|
||||||
"config:recommended",
|
|
||||||
":disableDependencyDashboard"
|
|
||||||
],
|
|
||||||
"lockFileMaintenance": {
|
"lockFileMaintenance": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"automerge": true
|
"automerge": true
|
||||||
|
|
|
||||||
|
|
@ -17,23 +17,9 @@ export const BaseHtml = ({
|
||||||
<meta name="webroot" content={webroot} />
|
<meta name="webroot" content={webroot} />
|
||||||
<title safe>{title}</title>
|
<title safe>{title}</title>
|
||||||
<link rel="stylesheet" href={`${webroot}/generated.css`} />
|
<link rel="stylesheet" href={`${webroot}/generated.css`} />
|
||||||
<link
|
<link rel="apple-touch-icon" sizes="180x180" href={`${webroot}/apple-touch-icon.png`} />
|
||||||
rel="apple-touch-icon"
|
<link rel="icon" type="image/png" sizes="32x32" href={`${webroot}/favicon-32x32.png`} />
|
||||||
sizes="180x180"
|
<link rel="icon" type="image/png" sizes="16x16" href={`${webroot}/favicon-16x16.png`} />
|
||||||
href={`${webroot}/apple-touch-icon.png`}
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="icon"
|
|
||||||
type="image/png"
|
|
||||||
sizes="32x32"
|
|
||||||
href={`${webroot}/favicon-32x32.png`}
|
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="icon"
|
|
||||||
type="image/png"
|
|
||||||
sizes="16x16"
|
|
||||||
href={`${webroot}/favicon-16x16.png`}
|
|
||||||
/>
|
|
||||||
<link rel="manifest" href={`${webroot}/site.webmanifest`} />
|
<link rel="manifest" href={`${webroot}/site.webmanifest`} />
|
||||||
</head>
|
</head>
|
||||||
<body class="flex min-h-screen w-full flex-col bg-neutral-900 text-neutral-200">
|
<body class="flex min-h-screen w-full flex-col bg-neutral-900 text-neutral-200">
|
||||||
|
|
|
||||||
|
|
@ -29,24 +29,20 @@ export function convert(
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
execFile(
|
execFile("dvisvgm", [...inputArgs, filePath, "-o", targetPath], (error, stdout, stderr) => {
|
||||||
"dvisvgm",
|
if (error) {
|
||||||
[...inputArgs, filePath, "-o", 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");
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -728,9 +728,7 @@ export async function convert(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse FFMPEG_ARGS environment variable into array
|
// Parse FFMPEG_ARGS environment variable into array
|
||||||
const ffmpegArgs = process.env.FFMPEG_ARGS
|
const ffmpegArgs = process.env.FFMPEG_ARGS ? process.env.FFMPEG_ARGS.split(/\s+/) : [];
|
||||||
? process.env.FFMPEG_ARGS.split(/\s+/)
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
execFile(
|
execFile(
|
||||||
|
|
|
||||||
|
|
@ -317,24 +317,20 @@ export function convert(
|
||||||
options?: unknown,
|
options?: unknown,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
execFile(
|
execFile("gm", ["convert", filePath, targetPath], (error, stdout, stderr) => {
|
||||||
"gm",
|
if (error) {
|
||||||
["convert", 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");
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -452,12 +452,7 @@ export function convert(
|
||||||
let inputArgs: string[] = [];
|
let inputArgs: string[] = [];
|
||||||
|
|
||||||
if (convertTo === "ico") {
|
if (convertTo === "ico") {
|
||||||
outputArgs = [
|
outputArgs = ["-define", "icon:auto-resize=256,128,64,48,32,16", "-background", "none"];
|
||||||
"-define",
|
|
||||||
"icon:auto-resize=256,128,64,48,32,16",
|
|
||||||
"-background",
|
|
||||||
"none",
|
|
||||||
];
|
|
||||||
|
|
||||||
if (fileType === "svg") {
|
if (fileType === "svg") {
|
||||||
// this might be a bit too much, but it works
|
// this might be a bit too much, but it works
|
||||||
|
|
|
||||||
|
|
@ -36,24 +36,20 @@ export function convert(
|
||||||
options?: unknown,
|
options?: unknown,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
execFile(
|
execFile("inkscape", [filePath, "-o", targetPath], (error, stdout, stderr) => {
|
||||||
"inkscape",
|
if (error) {
|
||||||
[filePath, "-o", 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");
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,7 @@ import { execFile } from "child_process";
|
||||||
|
|
||||||
export const properties = {
|
export const properties = {
|
||||||
from: {
|
from: {
|
||||||
images: [
|
images: ["avci", "avcs", "avif", "h264", "heic", "heics", "heif", "heifs", "hif", "mkv", "mp4"],
|
||||||
"avci",
|
|
||||||
"avcs",
|
|
||||||
"avif",
|
|
||||||
"h264",
|
|
||||||
"heic",
|
|
||||||
"heics",
|
|
||||||
"heif",
|
|
||||||
"heifs",
|
|
||||||
"hif",
|
|
||||||
"mkv",
|
|
||||||
"mp4",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
images: ["jpeg", "png", "y4m"],
|
images: ["jpeg", "png", "y4m"],
|
||||||
|
|
@ -30,24 +18,20 @@ export function convert(
|
||||||
options?: unknown,
|
options?: unknown,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
execFile(
|
execFile("heif-convert", [filePath, targetPath], (error, stdout, stderr) => {
|
||||||
"heif-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");
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,32 +4,10 @@ import { execFile } from "node:child_process";
|
||||||
export const properties = {
|
export const properties = {
|
||||||
from: {
|
from: {
|
||||||
jxl: ["jxl"],
|
jxl: ["jxl"],
|
||||||
images: [
|
images: ["apng", "exr", "gif", "jpeg", "pam", "pfm", "pgm", "pgx", "png", "ppm"],
|
||||||
"apng",
|
|
||||||
"exr",
|
|
||||||
"gif",
|
|
||||||
"jpeg",
|
|
||||||
"pam",
|
|
||||||
"pfm",
|
|
||||||
"pgm",
|
|
||||||
"pgx",
|
|
||||||
"png",
|
|
||||||
"ppm",
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
jxl: [
|
jxl: ["apng", "exr", "gif", "jpeg", "pam", "pfm", "pgm", "pgx", "png", "ppm"],
|
||||||
"apng",
|
|
||||||
"exr",
|
|
||||||
"gif",
|
|
||||||
"jpeg",
|
|
||||||
"pam",
|
|
||||||
"pfm",
|
|
||||||
"pgm",
|
|
||||||
"pgx",
|
|
||||||
"png",
|
|
||||||
"ppm",
|
|
||||||
],
|
|
||||||
images: ["jxl"],
|
images: ["jxl"],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,21 @@
|
||||||
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
||||||
import { convert as convertassimp, properties as propertiesassimp } from "./assimp";
|
import { convert as convertassimp, properties as propertiesassimp } from "./assimp";
|
||||||
|
import { convert as convertCalibre, properties as propertiesCalibre } from "./calibre";
|
||||||
|
import { convert as convertDvisvgm, properties as propertiesDvisvgm } from "./dvisvgm";
|
||||||
import { convert as convertFFmpeg, properties as propertiesFFmpeg } from "./ffmpeg";
|
import { convert as convertFFmpeg, properties as propertiesFFmpeg } from "./ffmpeg";
|
||||||
import { convert as convertGraphicsmagick, properties as propertiesGraphicsmagick } from "./graphicsmagick";
|
import {
|
||||||
|
convert as convertGraphicsmagick,
|
||||||
|
properties as propertiesGraphicsmagick,
|
||||||
|
} from "./graphicsmagick";
|
||||||
|
import { convert as convertImagemagick, properties as propertiesImagemagick } from "./imagemagick";
|
||||||
import { convert as convertInkscape, properties as propertiesInkscape } from "./inkscape";
|
import { convert as convertInkscape, properties as propertiesInkscape } from "./inkscape";
|
||||||
|
import { convert as convertLibheif, properties as propertiesLibheif } from "./libheif";
|
||||||
import { convert as convertLibjxl, properties as propertiesLibjxl } from "./libjxl";
|
import { convert as convertLibjxl, properties as propertiesLibjxl } from "./libjxl";
|
||||||
import { convert as convertPandoc, properties as propertiesPandoc } from "./pandoc";
|
import { convert as convertPandoc, properties as propertiesPandoc } from "./pandoc";
|
||||||
|
import { convert as convertPotrace, properties as propertiesPotrace } from "./potrace";
|
||||||
import { convert as convertresvg, properties as propertiesresvg } from "./resvg";
|
import { convert as convertresvg, properties as propertiesresvg } from "./resvg";
|
||||||
import { convert as convertImage, properties as propertiesImage } from "./vips";
|
import { convert as convertImage, properties as propertiesImage } from "./vips";
|
||||||
import { convert as convertxelatex, properties as propertiesxelatex } from "./xelatex";
|
import { convert as convertxelatex, properties as propertiesxelatex } from "./xelatex";
|
||||||
import { convert as convertCalibre, properties as propertiesCalibre } from "./calibre";
|
|
||||||
import { convert as convertLibheif, properties as propertiesLibheif } from "./libheif";
|
|
||||||
import { convert as convertPotrace, properties as propertiesPotrace } from "./potrace";
|
|
||||||
import { convert as convertImagemagick, properties as propertiesImagemagick } from "./imagemagick";
|
|
||||||
import { convert as convertDvisvgm, properties as propertiesDvisvgm } from "./dvisvgm";
|
|
||||||
|
|
||||||
|
|
||||||
// This should probably be reconstructed so that the functions are not imported instead the functions hook into this to make the converters more modular
|
// This should probably be reconstructed so that the functions are not imported instead the functions hook into this to make the converters more modular
|
||||||
|
|
||||||
|
|
@ -113,7 +115,7 @@ export async function mainConverter(
|
||||||
) {
|
) {
|
||||||
const fileType = normalizeFiletype(fileTypeOriginal);
|
const fileType = normalizeFiletype(fileTypeOriginal);
|
||||||
|
|
||||||
let converterFunc: typeof properties["libjxl"]["converter"] | undefined;
|
let converterFunc: (typeof properties)["libjxl"]["converter"] | undefined;
|
||||||
|
|
||||||
if (converterName) {
|
if (converterName) {
|
||||||
converterFunc = properties[converterName]?.converter;
|
converterFunc = properties[converterName]?.converter;
|
||||||
|
|
@ -139,20 +141,12 @@ export async function mainConverter(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!converterFunc) {
|
if (!converterFunc) {
|
||||||
console.log(
|
console.log(`No available converter supports converting from ${fileType} to ${convertTo}.`);
|
||||||
`No available converter supports converting from ${fileType} to ${convertTo}.`,
|
|
||||||
);
|
|
||||||
return "File type not supported";
|
return "File type not supported";
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await converterFunc(
|
const result = await converterFunc(inputFilePath, fileType, convertTo, targetPath, options);
|
||||||
inputFilePath,
|
|
||||||
fileType,
|
|
||||||
convertTo,
|
|
||||||
targetPath,
|
|
||||||
options,
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`Converted ${inputFilePath} from ${fileType} to ${convertTo} successfully using ${converterName}.`,
|
`Converted ${inputFilePath} from ${fileType} to ${convertTo} successfully using ${converterName}.`,
|
||||||
|
|
@ -192,8 +186,7 @@ for (const converterName in properties) {
|
||||||
possibleTargets[extension] = {};
|
possibleTargets[extension] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
possibleTargets[extension][converterName] =
|
possibleTargets[extension][converterName] = converterProperties.to[key] || [];
|
||||||
converterProperties.to[key] || [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,33 +5,45 @@ export const properties = {
|
||||||
images: ["pnm", "pbm", "pgm", "bmp"],
|
images: ["pnm", "pbm", "pgm", "bmp"],
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
images: ["svg", "pdf", "pdfpage", "eps", "postscript", "ps", "dxf", "geojson", "pgm", "gimppath", "xfig"],
|
images: [
|
||||||
|
"svg",
|
||||||
|
"pdf",
|
||||||
|
"pdfpage",
|
||||||
|
"eps",
|
||||||
|
"postscript",
|
||||||
|
"ps",
|
||||||
|
"dxf",
|
||||||
|
"geojson",
|
||||||
|
"pgm",
|
||||||
|
"gimppath",
|
||||||
|
"xfig",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function convert(
|
export function convert(
|
||||||
filePath: string,
|
filePath: string,
|
||||||
fileType: string,
|
fileType: string,
|
||||||
convertTo: string,
|
convertTo: string,
|
||||||
targetPath: string,
|
targetPath: string,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
options?: unknown,
|
options?: unknown,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
execFile("potrace", [filePath, "-o", targetPath, "-b", convertTo], (error, stdout, stderr) => {
|
execFile("potrace", [filePath, "-o", targetPath, "-b", convertTo], (error, stdout, stderr) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(`error: ${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");
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,24 +119,20 @@ export function convert(
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
execFile(
|
execFile("vips", [action, filePath, targetPath], (error, stdout, stderr) => {
|
||||||
"vips",
|
if (error) {
|
||||||
[action, 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");
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,20 +19,11 @@ 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
|
const outputPath = targetPath.split("/").slice(0, -1).join("/").replace("./", "");
|
||||||
.split("/")
|
|
||||||
.slice(0, -1)
|
|
||||||
.join("/")
|
|
||||||
.replace("./", "");
|
|
||||||
|
|
||||||
execFile(
|
execFile(
|
||||||
"latexmk",
|
"latexmk",
|
||||||
[
|
["-xelatex", "-interaction=nonstopmode", `-output-directory=${outputPath}`, filePath],
|
||||||
"-xelatex",
|
|
||||||
"-interaction=nonstopmode",
|
|
||||||
`-output-directory=${outputPath}`,
|
|
||||||
filePath,
|
|
||||||
],
|
|
||||||
(error, stdout, stderr) => {
|
(error, stdout, stderr) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(`error: ${error}`);
|
reject(`error: ${error}`);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { Database } from "bun:sqlite";
|
import { Database } from "bun:sqlite";
|
||||||
|
|
||||||
const db = new Database("./data/mydb.sqlite", { create: true });
|
const db = new Database("./data/mydb.sqlite", { create: true });
|
||||||
|
|
||||||
if (!db.query("SELECT * FROM sqlite_master WHERE type='table'").get()) {
|
if (!db.query("SELECT * FROM sqlite_master WHERE type='table'").get()) {
|
||||||
|
|
@ -27,13 +28,9 @@ CREATE TABLE IF NOT EXISTS jobs (
|
||||||
PRAGMA user_version = 1;`);
|
PRAGMA user_version = 1;`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbVersion = (
|
const dbVersion = (db.query("PRAGMA user_version").get() as { user_version?: number }).user_version;
|
||||||
db.query("PRAGMA user_version").get() as { user_version?: number }
|
|
||||||
).user_version;
|
|
||||||
if (dbVersion === 0) {
|
if (dbVersion === 0) {
|
||||||
db.exec(
|
db.exec("ALTER TABLE file_names ADD COLUMN status TEXT DEFAULT 'not started';");
|
||||||
"ALTER TABLE file_names ADD COLUMN status TEXT DEFAULT 'not started';",
|
|
||||||
);
|
|
||||||
db.exec("PRAGMA user_version = 1;");
|
db.exec("PRAGMA user_version = 1;");
|
||||||
console.log("Updated database to version 1.");
|
console.log("Updated database to version 1.");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
export const ACCOUNT_REGISTRATION =
|
export const ACCOUNT_REGISTRATION =
|
||||||
process.env.ACCOUNT_REGISTRATION?.toLowerCase() === "true" || false;
|
process.env.ACCOUNT_REGISTRATION?.toLowerCase() === "true" || false;
|
||||||
|
|
||||||
export const HTTP_ALLOWED =
|
export const HTTP_ALLOWED = process.env.HTTP_ALLOWED?.toLowerCase() === "true" || false;
|
||||||
process.env.HTTP_ALLOWED?.toLowerCase() === "true" || false;
|
|
||||||
|
|
||||||
export const ALLOW_UNAUTHENTICATED =
|
export const ALLOW_UNAUTHENTICATED =
|
||||||
process.env.ALLOW_UNAUTHENTICATED?.toLowerCase() === "true" || false;
|
process.env.ALLOW_UNAUTHENTICATED?.toLowerCase() === "true" || false;
|
||||||
|
|
@ -11,7 +10,6 @@ export const AUTO_DELETE_EVERY_N_HOURS = process.env.AUTO_DELETE_EVERY_N_HOURS
|
||||||
? Number(process.env.AUTO_DELETE_EVERY_N_HOURS)
|
? Number(process.env.AUTO_DELETE_EVERY_N_HOURS)
|
||||||
: 24;
|
: 24;
|
||||||
|
|
||||||
export const HIDE_HISTORY =
|
export const HIDE_HISTORY = process.env.HIDE_HISTORY?.toLowerCase() === "true" || false;
|
||||||
process.env.HIDE_HISTORY?.toLowerCase() === "true" || false;
|
|
||||||
|
|
||||||
export const WEBROOT = process.env.WEBROOT ?? "";
|
export const WEBROOT = process.env.WEBROOT ?? "";
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,21 @@ import { rmSync } from "node:fs";
|
||||||
import { mkdir } from "node:fs/promises";
|
import { mkdir } from "node:fs/promises";
|
||||||
import { html } from "@elysiajs/html";
|
import { html } from "@elysiajs/html";
|
||||||
import { staticPlugin } from "@elysiajs/static";
|
import { staticPlugin } from "@elysiajs/static";
|
||||||
|
|
||||||
import { Elysia } from "elysia";
|
import { Elysia } from "elysia";
|
||||||
import "./helpers/printVersions";
|
import "./helpers/printVersions";
|
||||||
import { AUTO_DELETE_EVERY_N_HOURS, WEBROOT } from "./helpers/env";
|
|
||||||
import { user } from "./pages/user";
|
|
||||||
import { root } from "./pages/root"
|
|
||||||
import { upload } from "./pages/upload"
|
|
||||||
import { history } from "./pages/history";
|
|
||||||
import { convert } from "./pages/convert"
|
|
||||||
import { download } from "./pages/download"
|
|
||||||
import { results } from "./pages/results";
|
|
||||||
import { deleteFile } from "./pages/deleteFile";
|
|
||||||
import { listConverters } from "./pages/listConverters";
|
|
||||||
import { chooseConverter } from "./pages/chooseConverter";
|
|
||||||
import db from "./db/db";
|
import db from "./db/db";
|
||||||
import { Jobs } from "./db/types";
|
import { Jobs } from "./db/types";
|
||||||
|
import { AUTO_DELETE_EVERY_N_HOURS, WEBROOT } from "./helpers/env";
|
||||||
|
import { chooseConverter } from "./pages/chooseConverter";
|
||||||
|
import { convert } from "./pages/convert";
|
||||||
|
import { deleteFile } from "./pages/deleteFile";
|
||||||
|
import { download } from "./pages/download";
|
||||||
|
import { history } from "./pages/history";
|
||||||
|
import { listConverters } from "./pages/listConverters";
|
||||||
|
import { results } from "./pages/results";
|
||||||
|
import { root } from "./pages/root";
|
||||||
|
import { upload } from "./pages/upload";
|
||||||
|
import { user } from "./pages/user";
|
||||||
|
|
||||||
mkdir("./data", { recursive: true }).catch(console.error);
|
mkdir("./data", { recursive: true }).catch(console.error);
|
||||||
|
|
||||||
|
|
@ -64,19 +63,13 @@ if (process.env.NODE_ENV !== "production") {
|
||||||
|
|
||||||
app.listen(3000);
|
app.listen(3000);
|
||||||
|
|
||||||
console.log(
|
console.log(`🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}${WEBROOT}`);
|
||||||
`🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}${WEBROOT}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const clearJobs = () => {
|
const clearJobs = () => {
|
||||||
const jobs = db
|
const jobs = db
|
||||||
.query("SELECT * FROM jobs WHERE date_created < ?")
|
.query("SELECT * FROM jobs WHERE date_created < ?")
|
||||||
.as(Jobs)
|
.as(Jobs)
|
||||||
.all(
|
.all(new Date(Date.now() - AUTO_DELETE_EVERY_N_HOURS * 60 * 60 * 1000).toISOString());
|
||||||
new Date(
|
|
||||||
Date.now() - AUTO_DELETE_EVERY_N_HOURS * 60 * 60 * 1000,
|
|
||||||
).toISOString(),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const job of jobs) {
|
for (const job of jobs) {
|
||||||
// delete the directories
|
// delete the directories
|
||||||
|
|
|
||||||
|
|
@ -1,75 +1,67 @@
|
||||||
import Elysia, { t } from "elysia";
|
|
||||||
import { userService } from "./user";
|
|
||||||
import { Html } from "@elysiajs/html";
|
import { Html } from "@elysiajs/html";
|
||||||
import {
|
import Elysia, { t } from "elysia";
|
||||||
getPossibleTargets,
|
import { getPossibleTargets } from "../converters/main";
|
||||||
} from "../converters/main";
|
import { userService } from "./user";
|
||||||
|
|
||||||
export const chooseConverter = new Elysia()
|
export const chooseConverter = new Elysia().use(userService).post(
|
||||||
.use(userService)
|
"/conversions",
|
||||||
.post(
|
({ body }) => {
|
||||||
"/conversions",
|
return (
|
||||||
({ body }) => {
|
<>
|
||||||
return (
|
<article
|
||||||
<>
|
class={`
|
||||||
<article
|
convert_to_popup absolute z-2 m-0 hidden h-[50vh] max-h-[50vh] w-full flex-col
|
||||||
class={`
|
overflow-x-hidden overflow-y-auto rounded bg-neutral-800
|
||||||
convert_to_popup absolute z-2 m-0 hidden h-[50vh] max-h-[50vh] w-full flex-col
|
sm:h-[30vh]
|
||||||
overflow-x-hidden overflow-y-auto rounded bg-neutral-800
|
`}
|
||||||
sm:h-[30vh]
|
>
|
||||||
`}
|
{Object.entries(getPossibleTargets(body.fileType)).map(([converter, targets]) => (
|
||||||
>
|
<article
|
||||||
{Object.entries(getPossibleTargets(body.fileType)).map(
|
class="convert_to_group flex w-full flex-col border-b border-neutral-700 p-4"
|
||||||
([converter, targets]) => (
|
data-converter={converter}
|
||||||
<article
|
>
|
||||||
class="convert_to_group flex w-full flex-col border-b border-neutral-700 p-4"
|
<header class="mb-2 w-full text-xl font-bold" safe>
|
||||||
data-converter={converter}
|
{converter}
|
||||||
>
|
</header>
|
||||||
<header class="mb-2 w-full text-xl font-bold" safe>
|
<ul class="convert_to_target flex flex-row flex-wrap gap-1">
|
||||||
{converter}
|
{targets.map((target) => (
|
||||||
</header>
|
<button
|
||||||
<ul class="convert_to_target flex flex-row flex-wrap gap-1">
|
// https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953
|
||||||
{targets.map((target) => (
|
tabindex={0}
|
||||||
<button
|
class={`
|
||||||
// https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953
|
target rounded bg-neutral-700 p-1 text-base
|
||||||
tabindex={0}
|
hover:bg-neutral-600
|
||||||
class={`
|
`}
|
||||||
target rounded bg-neutral-700 p-1 text-base
|
data-value={`${target},${converter}`}
|
||||||
hover:bg-neutral-600
|
data-target={target}
|
||||||
`}
|
data-converter={converter}
|
||||||
data-value={`${target},${converter}`}
|
type="button"
|
||||||
data-target={target}
|
safe
|
||||||
data-converter={converter}
|
>
|
||||||
type="button"
|
{target}
|
||||||
safe
|
</button>
|
||||||
>
|
))}
|
||||||
{target}
|
</ul>
|
||||||
</button>
|
</article>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</article>
|
||||||
</article>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<select name="convert_to" aria-label="Convert to" required hidden>
|
<select name="convert_to" aria-label="Convert to" required hidden>
|
||||||
<option selected disabled value="">
|
<option selected disabled value="">
|
||||||
Convert to
|
Convert to
|
||||||
</option>
|
</option>
|
||||||
{Object.entries(getPossibleTargets(body.fileType)).map(
|
{Object.entries(getPossibleTargets(body.fileType)).map(([converter, targets]) => (
|
||||||
([converter, targets]) => (
|
<optgroup label={converter}>
|
||||||
<optgroup label={converter}>
|
{targets.map((target) => (
|
||||||
{targets.map((target) => (
|
<option value={`${target},${converter}`} safe>
|
||||||
<option value={`${target},${converter}`} safe>
|
{target}
|
||||||
{target}
|
</option>
|
||||||
</option>
|
))}
|
||||||
))}
|
</optgroup>
|
||||||
</optgroup>
|
))}
|
||||||
),
|
</select>
|
||||||
)}
|
</>
|
||||||
</select>
|
);
|
||||||
</>
|
},
|
||||||
);
|
{ body: t.Object({ fileType: t.String() }) },
|
||||||
},
|
);
|
||||||
{ body: t.Object({ fileType: t.String() }) },
|
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,11 @@ import { Elysia, t } from "elysia";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
import { outputDir, uploadsDir } from "..";
|
import { outputDir, uploadsDir } from "..";
|
||||||
import { mainConverter } from "../converters/main";
|
import { mainConverter } from "../converters/main";
|
||||||
import { WEBROOT } from "../helpers/env";
|
|
||||||
import db from "../db/db";
|
import db from "../db/db";
|
||||||
import {
|
|
||||||
normalizeFiletype,
|
|
||||||
normalizeOutputFiletype,
|
|
||||||
} from "../helpers/normalizeFiletype";
|
|
||||||
import { userService } from "./user";
|
|
||||||
import { Jobs } from "../db/types";
|
import { Jobs } from "../db/types";
|
||||||
|
import { WEBROOT } from "../helpers/env";
|
||||||
|
import { normalizeFiletype, normalizeOutputFiletype } from "../helpers/normalizeFiletype";
|
||||||
|
import { userService } from "./user";
|
||||||
|
|
||||||
export const convert = new Elysia().use(userService).post(
|
export const convert = new Elysia().use(userService).post(
|
||||||
"/convert",
|
"/convert",
|
||||||
|
|
@ -44,10 +41,7 @@ export const convert = new Elysia().use(userService).post(
|
||||||
try {
|
try {
|
||||||
await mkdir(userOutputDir, { recursive: true });
|
await mkdir(userOutputDir, { recursive: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(`Failed to create the output directory: ${userOutputDir}.`, error);
|
||||||
`Failed to create the output directory: ${userOutputDir}.`,
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const convertTo = normalizeFiletype(body.convert_to.split(",")[0] ?? "");
|
const convertTo = normalizeFiletype(body.convert_to.split(",")[0] ?? "");
|
||||||
|
|
@ -62,9 +56,10 @@ export const convert = new Elysia().use(userService).post(
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
db.query(
|
db.query("UPDATE jobs SET num_files = ?1, status = 'pending' WHERE id = ?2").run(
|
||||||
"UPDATE jobs SET num_files = ?1, status = 'pending' WHERE id = ?2",
|
fileNames.length,
|
||||||
).run(fileNames.length, jobId.value);
|
jobId.value,
|
||||||
|
);
|
||||||
|
|
||||||
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)",
|
||||||
|
|
@ -99,9 +94,7 @@ export const convert = new Elysia().use(userService).post(
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// All conversions are done, update the job status to 'completed'
|
// All conversions are done, update the job status to 'completed'
|
||||||
if (jobId.value) {
|
if (jobId.value) {
|
||||||
db.query("UPDATE jobs SET status = 'completed' WHERE id = ?1").run(
|
db.query("UPDATE jobs SET status = 'completed' WHERE id = ?1").run(jobId.value);
|
||||||
jobId.value,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete all uploaded files in userUploadsDir
|
// delete all uploaded files in userUploadsDir
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,41 @@
|
||||||
import { Elysia, t } from "elysia";
|
|
||||||
import { userService } from "./user";
|
|
||||||
import { unlink } from "node:fs/promises";
|
import { unlink } from "node:fs/promises";
|
||||||
import { WEBROOT } from "../helpers/env";
|
import { Elysia, t } from "elysia";
|
||||||
import { uploadsDir } from "..";
|
import { uploadsDir } from "..";
|
||||||
import db from "../db/db";
|
import db from "../db/db";
|
||||||
|
import { WEBROOT } from "../helpers/env";
|
||||||
|
import { userService } from "./user";
|
||||||
|
|
||||||
export const deleteFile = new Elysia()
|
export const deleteFile = new Elysia().use(userService).post(
|
||||||
.use(userService)
|
"/delete",
|
||||||
.post(
|
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
|
||||||
"/delete",
|
if (!auth?.value) {
|
||||||
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
if (!auth?.value) {
|
}
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!jobId?.value) {
|
if (!jobId?.value) {
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingJob = await db
|
const existingJob = await db
|
||||||
.query("SELECT * FROM jobs WHERE id = ? AND user_id = ?")
|
.query("SELECT * FROM jobs WHERE id = ? AND user_id = ?")
|
||||||
.get(jobId.value, user.id);
|
.get(jobId.value, user.id);
|
||||||
|
|
||||||
if (!existingJob) {
|
if (!existingJob) {
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
|
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
|
||||||
|
|
||||||
await unlink(`${userUploadsDir}${body.filename}`);
|
await unlink(`${userUploadsDir}${body.filename}`);
|
||||||
},
|
|
||||||
{ body: t.Object({ filename: t.String() }) },
|
return {
|
||||||
)
|
message: "File deleted successfully.",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{ body: t.Object({ filename: t.String() }) },
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
|
|
||||||
import { Elysia } from "elysia";
|
import { Elysia } from "elysia";
|
||||||
import { userService } from "./user";
|
|
||||||
import { WEBROOT } from "../helpers/env";
|
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
import { outputDir } from "..";
|
import { outputDir } from "..";
|
||||||
import db from "../db/db";
|
import db from "../db/db";
|
||||||
|
import { WEBROOT } from "../helpers/env";
|
||||||
|
import { userService } from "./user";
|
||||||
|
|
||||||
export const download = new Elysia()
|
export const download = new Elysia()
|
||||||
.use(userService)
|
.use(userService)
|
||||||
|
|
@ -36,31 +35,28 @@ export const download = new Elysia()
|
||||||
return Bun.file(filePath);
|
return Bun.file(filePath);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.get(
|
.get("/zip/:userId/:jobId", async ({ params, jwt, redirect, cookie: { auth } }) => {
|
||||||
"/zip/:userId/:jobId",
|
// TODO: Implement zip download
|
||||||
async ({ params, jwt, redirect, cookie: { auth } }) => {
|
if (!auth?.value) {
|
||||||
// TODO: Implement zip download
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
if (!auth?.value) {
|
}
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = await db
|
const job = await db
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
||||||
.get(user.id, params.jobId);
|
.get(user.id, params.jobId);
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
return redirect(`${WEBROOT}/results`, 302);
|
return redirect(`${WEBROOT}/results`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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}/);
|
||||||
|
|
||||||
// return Bun.zip(outputPath);
|
// return Bun.zip(outputPath);
|
||||||
},
|
});
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
|
import { Html } from "@elysiajs/html";
|
||||||
import { Elysia } from "elysia";
|
import { Elysia } from "elysia";
|
||||||
import { BaseHtml } from "../components/base";
|
import { BaseHtml } from "../components/base";
|
||||||
import { Html } from "@elysiajs/html";
|
|
||||||
import { Header } from "../components/header";
|
import { Header } from "../components/header";
|
||||||
import { userService } from "./user";
|
|
||||||
import { ALLOW_UNAUTHENTICATED, HIDE_HISTORY, WEBROOT } from "../helpers/env";
|
|
||||||
import { Filename, Jobs } from "../db/types";
|
|
||||||
import db from "../db/db";
|
import db from "../db/db";
|
||||||
|
import { Filename, Jobs } from "../db/types";
|
||||||
|
import { ALLOW_UNAUTHENTICATED, HIDE_HISTORY, WEBROOT } from "../helpers/env";
|
||||||
|
import { userService } from "./user";
|
||||||
|
|
||||||
export const history = new Elysia()
|
export const history = new Elysia()
|
||||||
.use(userService)
|
.use(userService)
|
||||||
|
|
@ -23,17 +23,10 @@ export const history = new Elysia()
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
let userJobs = db
|
let userJobs = db.query("SELECT * FROM jobs WHERE user_id = ?").as(Jobs).all(user.id).reverse();
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ?")
|
|
||||||
.as(Jobs)
|
|
||||||
.all(user.id)
|
|
||||||
.reverse();
|
|
||||||
|
|
||||||
for (const job of userJobs) {
|
for (const job of userJobs) {
|
||||||
const files = db
|
const files = db.query("SELECT * FROM file_names WHERE job_id = ?").as(Filename).all(job.id);
|
||||||
.query("SELECT * FROM file_names WHERE job_id = ?")
|
|
||||||
.as(Filename)
|
|
||||||
.all(job.id);
|
|
||||||
|
|
||||||
job.finished_files = files.length;
|
job.finished_files = files.length;
|
||||||
job.files_detailed = files;
|
job.files_detailed = files;
|
||||||
|
|
@ -123,10 +116,7 @@ export const history = new Elysia()
|
||||||
{userJobs.map((job) => (
|
{userJobs.map((job) => (
|
||||||
<>
|
<>
|
||||||
<tr id={`job-row-${job.id}`}>
|
<tr id={`job-row-${job.id}`}>
|
||||||
<td
|
<td class="job-details-toggle cursor-pointer" data-job-id={job.id}>
|
||||||
class="job-details-toggle cursor-pointer"
|
|
||||||
data-job-id={job.id}
|
|
||||||
>
|
|
||||||
<svg
|
<svg
|
||||||
id={`arrow-${job.id}`}
|
id={`arrow-${job.id}`}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|
@ -143,9 +133,7 @@ export const history = new Elysia()
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</td>
|
</td>
|
||||||
<td safe>
|
<td safe>{new Date(job.date_created).toLocaleTimeString()}</td>
|
||||||
{new Date(job.date_created).toLocaleTimeString()}
|
|
||||||
</td>
|
|
||||||
<td>{job.num_files}</td>
|
<td>{job.num_files}</td>
|
||||||
<td class="max-sm:hidden">{job.finished_files}</td>
|
<td class="max-sm:hidden">{job.finished_files}</td>
|
||||||
<td safe>{job.status}</td>
|
<td safe>{job.status}</td>
|
||||||
|
|
@ -164,43 +152,29 @@ export const history = new Elysia()
|
||||||
<tr id={`details-${job.id}`} class="hidden">
|
<tr id={`details-${job.id}`} class="hidden">
|
||||||
<td colspan="6">
|
<td colspan="6">
|
||||||
<div class="p-2 text-sm text-neutral-500">
|
<div class="p-2 text-sm text-neutral-500">
|
||||||
<div class="mb-1 font-semibold">
|
<div class="mb-1 font-semibold">Detailed File Information:</div>
|
||||||
Detailed File Information:
|
{job.files_detailed.map((file: Filename) => (
|
||||||
</div>
|
<div class="flex items-center">
|
||||||
{job.files_detailed.map(
|
<span class="w-5/12 truncate" title={file.file_name} safe>
|
||||||
(file: Filename) => (
|
{file.file_name}
|
||||||
<div
|
</span>
|
||||||
class="flex items-center"
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
class="mx-2 inline-block h-4 w-4 text-neutral-500"
|
||||||
>
|
>
|
||||||
<span
|
<path
|
||||||
class="w-5/12 truncate"
|
fill-rule="evenodd"
|
||||||
title={file.file_name}
|
d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"
|
||||||
safe
|
clip-rule="evenodd"
|
||||||
>
|
/>
|
||||||
{file.file_name}
|
</svg>
|
||||||
</span>
|
<span class="w-5/12 truncate" title={file.output_file_name} safe>
|
||||||
<svg
|
{file.output_file_name}
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
</span>
|
||||||
viewBox="0 0 20 20"
|
</div>
|
||||||
fill="currentColor"
|
))}
|
||||||
class="mx-2 inline-block h-4 w-4 text-neutral-500"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<span
|
|
||||||
class="w-5/12 truncate"
|
|
||||||
title={file.output_file_name}
|
|
||||||
safe
|
|
||||||
>
|
|
||||||
{file.output_file_name}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -239,4 +213,4 @@ export const history = new Elysia()
|
||||||
</>
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,10 @@
|
||||||
import Elysia from "elysia";
|
|
||||||
import { userService } from "./user";
|
|
||||||
import { Html } from "@elysiajs/html";
|
import { Html } from "@elysiajs/html";
|
||||||
import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
|
import Elysia from "elysia";
|
||||||
import { BaseHtml } from "../components/base";
|
import { BaseHtml } from "../components/base";
|
||||||
import { Header } from "../components/header";
|
import { Header } from "../components/header";
|
||||||
import {
|
import { getAllInputs, getAllTargets } from "../converters/main";
|
||||||
getAllInputs,
|
import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
|
||||||
getAllTargets,
|
import { userService } from "./user";
|
||||||
} from "../converters/main";
|
|
||||||
|
|
||||||
export const listConverters = new Elysia()
|
export const listConverters = new Elysia()
|
||||||
.use(userService)
|
.use(userService)
|
||||||
|
|
@ -24,11 +21,7 @@ export const listConverters = new Elysia()
|
||||||
return (
|
return (
|
||||||
<BaseHtml webroot={WEBROOT} title="ConvertX | Converters">
|
<BaseHtml webroot={WEBROOT} title="ConvertX | Converters">
|
||||||
<>
|
<>
|
||||||
<Header
|
<Header webroot={WEBROOT} allowUnauthenticated={ALLOW_UNAUTHENTICATED} loggedIn />
|
||||||
webroot={WEBROOT}
|
|
||||||
allowUnauthenticated={ALLOW_UNAUTHENTICATED}
|
|
||||||
loggedIn
|
|
||||||
/>
|
|
||||||
<main
|
<main
|
||||||
class={`
|
class={`
|
||||||
w-full flex-1 px-2
|
w-full flex-1 px-2
|
||||||
|
|
@ -53,32 +46,30 @@ export const listConverters = new Elysia()
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{Object.entries(getAllTargets()).map(
|
{Object.entries(getAllTargets()).map(([converter, targets]) => {
|
||||||
([converter, targets]) => {
|
const inputs = getAllInputs(converter);
|
||||||
const inputs = getAllInputs(converter);
|
return (
|
||||||
return (
|
<tr>
|
||||||
<tr>
|
<td safe>{converter}</td>
|
||||||
<td safe>{converter}</td>
|
<td>
|
||||||
<td>
|
Count: {inputs.length}
|
||||||
Count: {inputs.length}
|
<ul>
|
||||||
<ul>
|
{inputs.map((input) => (
|
||||||
{inputs.map((input) => (
|
<li safe>{input}</li>
|
||||||
<li safe>{input}</li>
|
))}
|
||||||
))}
|
</ul>
|
||||||
</ul>
|
</td>
|
||||||
</td>
|
<td>
|
||||||
<td>
|
Count: {targets.length}
|
||||||
Count: {targets.length}
|
<ul>
|
||||||
<ul>
|
{targets.map((target) => (
|
||||||
{targets.map((target) => (
|
<li safe>{target}</li>
|
||||||
<li safe>{target}</li>
|
))}
|
||||||
))}
|
</ul>
|
||||||
</ul>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
);
|
||||||
);
|
})}
|
||||||
},
|
|
||||||
)}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</article>
|
</article>
|
||||||
|
|
@ -86,4 +77,4 @@ export const listConverters = new Elysia()
|
||||||
</>
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,214 +1,215 @@
|
||||||
import { Elysia } from "elysia";
|
|
||||||
import { userService } from "./user";
|
|
||||||
import { Html } from "@elysiajs/html";
|
import { Html } from "@elysiajs/html";
|
||||||
import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
|
import { Elysia } from "elysia";
|
||||||
import { Filename, Jobs } from "../db/types";
|
|
||||||
import { Header } from "../components/header";
|
|
||||||
import { BaseHtml } from "../components/base";
|
import { BaseHtml } from "../components/base";
|
||||||
|
import { Header } from "../components/header";
|
||||||
import db from "../db/db";
|
import db from "../db/db";
|
||||||
|
import { Filename, Jobs } from "../db/types";
|
||||||
|
import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
|
||||||
|
import { userService } from "./user";
|
||||||
|
|
||||||
function ResultsArticle({ job, files, outputPath }: { job: Jobs, files: Filename[], outputPath: string }) {
|
function ResultsArticle({
|
||||||
return (<article class="article">
|
job,
|
||||||
<div class="mb-4 flex items-center justify-between">
|
files,
|
||||||
<h1 class="text-xl">Results</h1>
|
outputPath,
|
||||||
<div>
|
}: {
|
||||||
<button
|
job: Jobs;
|
||||||
type="button"
|
files: Filename[];
|
||||||
class="float-right w-40 btn-primary"
|
outputPath: string;
|
||||||
onclick="downloadAll()"
|
}) {
|
||||||
{...(files.length !== job.num_files
|
return (
|
||||||
? { disabled: true, "aria-busy": "true" }
|
<article class="article">
|
||||||
: "")}
|
<div class="mb-4 flex items-center justify-between">
|
||||||
>
|
<h1 class="text-xl">Results</h1>
|
||||||
{files.length === job.num_files
|
<div>
|
||||||
? "Download All"
|
<button
|
||||||
: "Converting..."}
|
type="button"
|
||||||
</button>
|
class="float-right w-40 btn-primary"
|
||||||
|
onclick="downloadAll()"
|
||||||
|
{...(files.length !== job.num_files ? { disabled: true, "aria-busy": "true" } : "")}
|
||||||
|
>
|
||||||
|
{files.length === job.num_files ? "Download All" : "Converting..."}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<progress
|
||||||
<progress
|
max={job.num_files}
|
||||||
max={job.num_files}
|
value={files.length}
|
||||||
value={files.length}
|
class={`
|
||||||
class={`
|
mb-4 inline-block h-2 w-full appearance-none overflow-hidden rounded-full border-0
|
||||||
mb-4 inline-block h-2 w-full appearance-none overflow-hidden rounded-full border-0
|
bg-neutral-700 bg-none text-accent-500 accent-accent-500
|
||||||
bg-neutral-700 bg-none text-accent-500 accent-accent-500
|
[&::-moz-progress-bar]:bg-accent-500 [&::-webkit-progress-value]:rounded-full
|
||||||
[&::-moz-progress-bar]:bg-accent-500 [&::-webkit-progress-value]:rounded-full
|
[&::-webkit-progress-value]:[background:none]
|
||||||
[&::-webkit-progress-value]:[background:none]
|
[&[value]::-webkit-progress-value]:bg-accent-500
|
||||||
[&[value]::-webkit-progress-value]:bg-accent-500
|
[&[value]::-webkit-progress-value]:transition-[inline-size]
|
||||||
[&[value]::-webkit-progress-value]:transition-[inline-size]
|
`}
|
||||||
`}
|
/>
|
||||||
/>
|
<table
|
||||||
<table class={`
|
class={`
|
||||||
w-full table-auto rounded bg-neutral-900 text-left
|
w-full table-auto rounded bg-neutral-900 text-left
|
||||||
[&_td]:p-4
|
[&_td]:p-4
|
||||||
[&_tr]:rounded-sm [&_tr]:border-b [&_tr]:border-neutral-800
|
[&_tr]:rounded-sm [&_tr]:border-b [&_tr]:border-neutral-800
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
|
||||||
<th class={`
|
|
||||||
px-2 py-2
|
|
||||||
sm:px-4
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
Converted File Name
|
|
||||||
</th>
|
|
||||||
<th class={`
|
|
||||||
px-2 py-2
|
|
||||||
sm:px-4
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
</th>
|
|
||||||
<th class={`
|
|
||||||
px-2 py-2
|
|
||||||
sm:px-4
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
View
|
|
||||||
</th>
|
|
||||||
<th class={`
|
|
||||||
px-2 py-2
|
|
||||||
sm:px-4
|
|
||||||
`}>
|
|
||||||
Download
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{files.map((file) => (
|
|
||||||
<tr>
|
<tr>
|
||||||
<td safe class="max-w-[20vw] truncate">
|
<th
|
||||||
{file.output_file_name}
|
class={`
|
||||||
</td>
|
px-2 py-2
|
||||||
<td safe>{file.status}</td>
|
sm:px-4
|
||||||
<td>
|
`}
|
||||||
<a
|
>
|
||||||
class={`
|
Converted File Name
|
||||||
text-accent-500 underline
|
</th>
|
||||||
hover:text-accent-400
|
<th
|
||||||
`}
|
class={`
|
||||||
href={`${WEBROOT}/download/${outputPath}${file.output_file_name}`}
|
px-2 py-2
|
||||||
>
|
sm:px-4
|
||||||
View
|
`}
|
||||||
</a>
|
>
|
||||||
</td>
|
Status
|
||||||
<td>
|
</th>
|
||||||
<a
|
<th
|
||||||
class={`
|
class={`
|
||||||
text-accent-500 underline
|
px-2 py-2
|
||||||
hover:text-accent-400
|
sm:px-4
|
||||||
`}
|
`}
|
||||||
href={`${WEBROOT}/download/${outputPath}${file.output_file_name}`}
|
>
|
||||||
download={file.output_file_name}
|
View
|
||||||
>
|
</th>
|
||||||
Download
|
<th
|
||||||
</a>
|
class={`
|
||||||
</td>
|
px-2 py-2
|
||||||
|
sm:px-4
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
Download
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
{files.map((file) => (
|
||||||
</article>
|
<tr>
|
||||||
)
|
<td safe class="max-w-[20vw] truncate">
|
||||||
|
{file.output_file_name}
|
||||||
|
</td>
|
||||||
|
<td safe>{file.status}</td>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
class={`
|
||||||
|
text-accent-500 underline
|
||||||
|
hover:text-accent-400
|
||||||
|
`}
|
||||||
|
href={`${WEBROOT}/download/${outputPath}${file.output_file_name}`}
|
||||||
|
>
|
||||||
|
View
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a
|
||||||
|
class={`
|
||||||
|
text-accent-500 underline
|
||||||
|
hover:text-accent-400
|
||||||
|
`}
|
||||||
|
href={`${WEBROOT}/download/${outputPath}${file.output_file_name}`}
|
||||||
|
download={file.output_file_name}
|
||||||
|
>
|
||||||
|
Download
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const results = new Elysia()
|
export const results = new Elysia()
|
||||||
.use(userService)
|
.use(userService)
|
||||||
.get(
|
.get("/results/:jobId", async ({ params, jwt, set, redirect, cookie: { auth, job_id } }) => {
|
||||||
"/results/:jobId",
|
if (!auth?.value) {
|
||||||
async ({ params, jwt, set, redirect, cookie: { auth, job_id } }) => {
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
if (!auth?.value) {
|
}
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (job_id?.value) {
|
if (job_id?.value) {
|
||||||
// clear the job_id cookie since we are viewing the results
|
// clear the job_id cookie since we are viewing the results
|
||||||
job_id.remove();
|
job_id.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = db
|
const job = db
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
||||||
.as(Jobs)
|
.as(Jobs)
|
||||||
.get(user.id, params.jobId);
|
.get(user.id, params.jobId);
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
set.status = 404;
|
set.status = 404;
|
||||||
return {
|
return {
|
||||||
message: "Job not found.",
|
message: "Job not found.",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputPath = `${user.id}/${params.jobId}/`;
|
const outputPath = `${user.id}/${params.jobId}/`;
|
||||||
|
|
||||||
const files = db
|
const files = db
|
||||||
.query("SELECT * FROM file_names WHERE job_id = ?")
|
.query("SELECT * FROM file_names WHERE job_id = ?")
|
||||||
.as(Filename)
|
.as(Filename)
|
||||||
.all(params.jobId);
|
.all(params.jobId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseHtml webroot={WEBROOT} title="ConvertX | Result">
|
<BaseHtml webroot={WEBROOT} title="ConvertX | Result">
|
||||||
<>
|
<>
|
||||||
<Header
|
<Header webroot={WEBROOT} allowUnauthenticated={ALLOW_UNAUTHENTICATED} loggedIn />
|
||||||
webroot={WEBROOT}
|
<main
|
||||||
allowUnauthenticated={ALLOW_UNAUTHENTICATED}
|
class={`
|
||||||
loggedIn
|
w-full flex-1 px-2
|
||||||
/>
|
sm:px-4
|
||||||
<main
|
`}
|
||||||
class={`
|
>
|
||||||
w-full flex-1 px-2
|
<ResultsArticle job={job} files={files} outputPath={outputPath} />
|
||||||
sm:px-4
|
</main>
|
||||||
`}
|
<script src={`${WEBROOT}/results.js`} defer />
|
||||||
>
|
</>
|
||||||
<ResultsArticle job={job} files={files} outputPath={outputPath} />
|
</BaseHtml>
|
||||||
</main>
|
);
|
||||||
<script src={`${WEBROOT}/results.js`} defer />
|
})
|
||||||
</>
|
.post("/progress/:jobId", async ({ jwt, set, params, redirect, cookie: { auth, job_id } }) => {
|
||||||
</BaseHtml>
|
if (!auth?.value) {
|
||||||
);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
},
|
}
|
||||||
)
|
|
||||||
.post(
|
|
||||||
"/progress/:jobId",
|
|
||||||
async ({ jwt, set, params, redirect, cookie: { auth, job_id } }) => {
|
|
||||||
if (!auth?.value) {
|
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (job_id?.value) {
|
if (job_id?.value) {
|
||||||
// clear the job_id cookie since we are viewing the results
|
// clear the job_id cookie since we are viewing the results
|
||||||
job_id.remove();
|
job_id.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = db
|
const job = db
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
||||||
.as(Jobs)
|
.as(Jobs)
|
||||||
.get(user.id, params.jobId);
|
.get(user.id, params.jobId);
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
set.status = 404;
|
set.status = 404;
|
||||||
return {
|
return {
|
||||||
message: "Job not found.",
|
message: "Job not found.",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputPath = `${user.id}/${params.jobId}/`;
|
const outputPath = `${user.id}/${params.jobId}/`;
|
||||||
|
|
||||||
const files = db
|
const files = db
|
||||||
.query("SELECT * FROM file_names WHERE job_id = ?")
|
.query("SELECT * FROM file_names WHERE job_id = ?")
|
||||||
.as(Filename)
|
.as(Filename)
|
||||||
.all(params.jobId);
|
.all(params.jobId);
|
||||||
|
|
||||||
return <ResultsArticle job={job} files={files} outputPath={outputPath} />;
|
return <ResultsArticle job={job} files={files} outputPath={outputPath} />;
|
||||||
},
|
});
|
||||||
)
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
import { Elysia } from "elysia";
|
|
||||||
import { Html } from "@elysiajs/html";
|
|
||||||
import { FIRST_RUN, userService } from "./user";
|
|
||||||
import { ACCOUNT_REGISTRATION, ALLOW_UNAUTHENTICATED, HIDE_HISTORY, HTTP_ALLOWED, WEBROOT } from "../helpers/env";
|
|
||||||
import { JWTPayloadSpec } from "@elysiajs/jwt";
|
|
||||||
import { randomInt } from "node:crypto";
|
import { randomInt } from "node:crypto";
|
||||||
|
import { Html } from "@elysiajs/html";
|
||||||
|
import { JWTPayloadSpec } from "@elysiajs/jwt";
|
||||||
|
import { Elysia } from "elysia";
|
||||||
import { BaseHtml } from "../components/base";
|
import { BaseHtml } from "../components/base";
|
||||||
import { Header } from "../components/header";
|
import { Header } from "../components/header";
|
||||||
import { getAllTargets } from "../converters/main";
|
import { getAllTargets } from "../converters/main";
|
||||||
import db from "../db/db";
|
import db from "../db/db";
|
||||||
import { User } from "../db/types";
|
import { User } from "../db/types";
|
||||||
|
import {
|
||||||
|
ACCOUNT_REGISTRATION,
|
||||||
|
ALLOW_UNAUTHENTICATED,
|
||||||
|
HIDE_HISTORY,
|
||||||
|
HTTP_ALLOWED,
|
||||||
|
WEBROOT,
|
||||||
|
} from "../helpers/env";
|
||||||
|
import { FIRST_RUN, userService } from "./user";
|
||||||
|
|
||||||
export const root = new Elysia()
|
export const root = new Elysia()
|
||||||
.use(userService)
|
.use(userService)
|
||||||
|
|
@ -27,10 +33,7 @@ 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(
|
||||||
randomInt(
|
randomInt(2 ** 24, Math.min(2 ** 48 + 2 ** 24 - 1, Number.MAX_SAFE_INTEGER)),
|
||||||
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,
|
||||||
|
|
@ -54,20 +57,19 @@ export const root = new Elysia()
|
||||||
} else if (auth?.value) {
|
} else if (auth?.value) {
|
||||||
user = await jwt.verify(auth.value);
|
user = await jwt.verify(auth.value);
|
||||||
|
|
||||||
if (user !== false && user.id) {
|
if (
|
||||||
if (Number.parseInt(user.id) < 2 ** 24 || !ALLOW_UNAUTHENTICATED) {
|
user !== false &&
|
||||||
// make sure user exists in db
|
user.id &&
|
||||||
const existingUser = db
|
(Number.parseInt(user.id) < 2 ** 24 || !ALLOW_UNAUTHENTICATED)
|
||||||
.query("SELECT * FROM users WHERE id = ?")
|
) {
|
||||||
.as(User)
|
// make sure user exists in db
|
||||||
.get(user.id);
|
const existingUser = db.query("SELECT * FROM users WHERE id = ?").as(User).get(user.id);
|
||||||
|
|
||||||
if (!existingUser) {
|
if (!existingUser) {
|
||||||
if (auth?.value) {
|
if (auth?.value) {
|
||||||
auth.remove();
|
auth.remove();
|
||||||
}
|
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
|
||||||
}
|
}
|
||||||
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -82,11 +84,9 @@ export const root = new Elysia()
|
||||||
new Date().toISOString(),
|
new Date().toISOString(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const id = (
|
const { id } = db
|
||||||
db
|
.query("SELECT id FROM jobs WHERE user_id = ? ORDER BY id DESC")
|
||||||
.query("SELECT id FROM jobs WHERE user_id = ? ORDER BY id DESC")
|
.get(user.id) as { id: number };
|
||||||
.get(user.id) as { id: number }
|
|
||||||
).id;
|
|
||||||
|
|
||||||
if (!jobId) {
|
if (!jobId) {
|
||||||
return { message: "Cookies should be enabled to use this app." };
|
return { message: "Cookies should be enabled to use this app." };
|
||||||
|
|
@ -172,62 +172,53 @@ export const root = new Elysia()
|
||||||
sm:h-[30vh]
|
sm:h-[30vh]
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{Object.entries(getAllTargets()).map(
|
{Object.entries(getAllTargets()).map(([converter, targets]) => (
|
||||||
([converter, targets]) => (
|
<article
|
||||||
<article
|
class={`
|
||||||
class={`
|
convert_to_group flex w-full flex-col border-b border-neutral-700 p-4
|
||||||
convert_to_group flex w-full flex-col border-b border-neutral-700 p-4
|
`}
|
||||||
`}
|
data-converter={converter}
|
||||||
data-converter={converter}
|
>
|
||||||
>
|
<header class="mb-2 w-full text-xl font-bold" safe>
|
||||||
<header class="mb-2 w-full text-xl font-bold" safe>
|
{converter}
|
||||||
{converter}
|
</header>
|
||||||
</header>
|
<ul class="convert_to_target flex flex-row flex-wrap gap-1">
|
||||||
<ul class="convert_to_target flex flex-row flex-wrap gap-1">
|
{targets.map((target) => (
|
||||||
{targets.map((target) => (
|
<button
|
||||||
<button
|
// https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953
|
||||||
// https://stackoverflow.com/questions/121499/when-a-blur-event-occurs-how-can-i-find-out-which-element-focus-went-to#comment82388679_33325953
|
tabindex={0}
|
||||||
tabindex={0}
|
class={`
|
||||||
class={`
|
target rounded bg-neutral-700 p-1 text-base
|
||||||
target rounded bg-neutral-700 p-1 text-base
|
hover:bg-neutral-600
|
||||||
hover:bg-neutral-600
|
`}
|
||||||
`}
|
data-value={`${target},${converter}`}
|
||||||
data-value={`${target},${converter}`}
|
data-target={target}
|
||||||
data-target={target}
|
data-converter={converter}
|
||||||
data-converter={converter}
|
type="button"
|
||||||
type="button"
|
safe
|
||||||
safe
|
>
|
||||||
>
|
{target}
|
||||||
{target}
|
</button>
|
||||||
</button>
|
))}
|
||||||
))}
|
</ul>
|
||||||
</ul>
|
</article>
|
||||||
</article>
|
))}
|
||||||
),
|
|
||||||
)}
|
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
{/* Hidden element which determines the format to convert the file too and the converter to use */}
|
{/* Hidden element which determines the format to convert the file too and the converter to use */}
|
||||||
<select
|
<select name="convert_to" aria-label="Convert to" required hidden>
|
||||||
name="convert_to"
|
|
||||||
aria-label="Convert to"
|
|
||||||
required
|
|
||||||
hidden
|
|
||||||
>
|
|
||||||
<option selected disabled value="">
|
<option selected disabled value="">
|
||||||
Convert to
|
Convert to
|
||||||
</option>
|
</option>
|
||||||
{Object.entries(getAllTargets()).map(
|
{Object.entries(getAllTargets()).map(([converter, targets]) => (
|
||||||
([converter, targets]) => (
|
<optgroup label={converter}>
|
||||||
<optgroup label={converter}>
|
{targets.map((target) => (
|
||||||
{targets.map((target) => (
|
<option value={`${target},${converter}`} safe>
|
||||||
<option value={`${target},${converter}`} safe>
|
{target}
|
||||||
{target}
|
</option>
|
||||||
</option>
|
))}
|
||||||
))}
|
</optgroup>
|
||||||
</optgroup>
|
))}
|
||||||
),
|
|
||||||
)}
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
@ -246,4 +237,4 @@ export const root = new Elysia()
|
||||||
</>
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,51 +1,48 @@
|
||||||
import { Elysia, t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
import { userService } from "./user";
|
import db from "../db/db";
|
||||||
import { WEBROOT } from "../helpers/env";
|
import { WEBROOT } from "../helpers/env";
|
||||||
import { uploadsDir } from "../index";
|
import { uploadsDir } from "../index";
|
||||||
import db from "../db/db";
|
import { userService } from "./user";
|
||||||
|
|
||||||
|
export const upload = new Elysia().use(userService).post(
|
||||||
|
"/upload",
|
||||||
|
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
|
||||||
|
if (!auth?.value) {
|
||||||
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
|
}
|
||||||
|
|
||||||
export const upload = new Elysia()
|
const user = await jwt.verify(auth.value);
|
||||||
.use(userService)
|
if (!user) {
|
||||||
.post(
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
"/upload",
|
}
|
||||||
async ({ body, redirect, jwt, cookie: { auth, jobId } }) => {
|
|
||||||
if (!auth?.value) {
|
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await jwt.verify(auth.value);
|
if (!jobId?.value) {
|
||||||
if (!user) {
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!jobId?.value) {
|
const existingJob = await db
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
.query("SELECT * FROM jobs WHERE id = ? AND user_id = ?")
|
||||||
}
|
.get(jobId.value, user.id);
|
||||||
|
|
||||||
const existingJob = await db
|
if (!existingJob) {
|
||||||
.query("SELECT * FROM jobs WHERE id = ? AND user_id = ?")
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
.get(jobId.value, user.id);
|
}
|
||||||
|
|
||||||
if (!existingJob) {
|
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
|
||||||
}
|
|
||||||
|
|
||||||
const userUploadsDir = `${uploadsDir}${user.id}/${jobId.value}/`;
|
if (body?.file) {
|
||||||
|
if (Array.isArray(body.file)) {
|
||||||
if (body?.file) {
|
for (const file of body.file) {
|
||||||
if (Array.isArray(body.file)) {
|
await Bun.write(`${userUploadsDir}${file.name}`, file);
|
||||||
for (const file of body.file) {
|
|
||||||
await Bun.write(`${userUploadsDir}${file.name}`, file);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await Bun.write(`${userUploadsDir}${body.file["name"]}`, body.file);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
await Bun.write(`${userUploadsDir}${body.file["name"]}`, body.file);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: "Files uploaded successfully.",
|
message: "Files uploaded successfully.",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{ body: t.Object({ file: t.Files() }) },
|
{ body: t.Object({ file: t.Files() }) },
|
||||||
)
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,22 @@
|
||||||
import { Elysia, t } from "elysia";
|
import { randomUUID } from "node:crypto";
|
||||||
import { Html } from "@elysiajs/html";
|
import { Html } from "@elysiajs/html";
|
||||||
|
import { jwt } from "@elysiajs/jwt";
|
||||||
|
import { Elysia, t } from "elysia";
|
||||||
import { BaseHtml } from "../components/base";
|
import { BaseHtml } from "../components/base";
|
||||||
import { Header } from "../components/header";
|
import { Header } from "../components/header";
|
||||||
import { jwt } from "@elysiajs/jwt";
|
|
||||||
import { randomUUID } from "node:crypto";
|
|
||||||
|
|
||||||
import { ACCOUNT_REGISTRATION, WEBROOT, HIDE_HISTORY, ALLOW_UNAUTHENTICATED, HTTP_ALLOWED } from "../helpers/env";
|
|
||||||
import db from "../db/db";
|
import db from "../db/db";
|
||||||
import { User } from "../db/types";
|
import { User } from "../db/types";
|
||||||
|
import {
|
||||||
|
ACCOUNT_REGISTRATION,
|
||||||
|
ALLOW_UNAUTHENTICATED,
|
||||||
|
HIDE_HISTORY,
|
||||||
|
HTTP_ALLOWED,
|
||||||
|
WEBROOT,
|
||||||
|
} from "../helpers/env";
|
||||||
|
|
||||||
export let FIRST_RUN = db.query("SELECT * FROM users").get() === null || false;
|
export let FIRST_RUN = db.query("SELECT * FROM users").get() === null || false;
|
||||||
|
|
||||||
export const userService = new Elysia({ name: 'user/service' })
|
export const userService = new Elysia({ name: "user/service" })
|
||||||
.use(
|
.use(
|
||||||
jwt({
|
jwt({
|
||||||
name: "jwt",
|
name: "jwt",
|
||||||
|
|
@ -25,35 +30,31 @@ export const userService = new Elysia({ name: 'user/service' })
|
||||||
.model({
|
.model({
|
||||||
signIn: t.Object({
|
signIn: t.Object({
|
||||||
email: t.String(),
|
email: t.String(),
|
||||||
password: t.String()
|
password: t.String(),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.macro({
|
.macro({
|
||||||
isSignIn(enabled: boolean) {
|
isSignIn(enabled: boolean) {
|
||||||
if (!enabled) return
|
if (!enabled) return;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
async beforeHandle({
|
async beforeHandle({ status, jwt, cookie: { auth } }) {
|
||||||
status,
|
|
||||||
jwt,
|
|
||||||
cookie: { auth },
|
|
||||||
}) {
|
|
||||||
if (auth?.value) {
|
if (auth?.value) {
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
user
|
user,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return status(401, {
|
return status(401, {
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Unauthorized'
|
message: "Unauthorized",
|
||||||
})
|
});
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
export const user = new Elysia()
|
export const user = new Elysia()
|
||||||
.use(userService)
|
.use(userService)
|
||||||
|
|
@ -72,9 +73,7 @@ export const user = new Elysia()
|
||||||
>
|
>
|
||||||
<h1 class="my-8 text-3xl">Welcome to ConvertX!</h1>
|
<h1 class="my-8 text-3xl">Welcome to ConvertX!</h1>
|
||||||
<article class="article p-0">
|
<article class="article p-0">
|
||||||
<header class="w-full bg-neutral-800 p-4">
|
<header class="w-full bg-neutral-800 p-4">Create your account</header>
|
||||||
Create your account
|
|
||||||
</header>
|
|
||||||
<form method="post" action={`${WEBROOT}/register`} class="p-4">
|
<form method="post" action={`${WEBROOT}/register`} class="p-4">
|
||||||
<fieldset class="mb-4 flex flex-col gap-4">
|
<fieldset class="mb-4 flex flex-col gap-4">
|
||||||
<label class="flex flex-col gap-1">
|
<label class="flex flex-col gap-1">
|
||||||
|
|
@ -166,11 +165,7 @@ export const user = new Elysia()
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<input
|
<input type="submit" value="Register" class="w-full btn-primary" />
|
||||||
type="submit"
|
|
||||||
value="Register"
|
|
||||||
class="w-full btn-primary"
|
|
||||||
/>
|
|
||||||
</form>
|
</form>
|
||||||
</article>
|
</article>
|
||||||
</main>
|
</main>
|
||||||
|
|
@ -189,9 +184,7 @@ export const user = new Elysia()
|
||||||
FIRST_RUN = false;
|
FIRST_RUN = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingUser = await db
|
const existingUser = await db.query("SELECT * FROM users WHERE email = ?").get(email);
|
||||||
.query("SELECT * FROM users WHERE email = ?")
|
|
||||||
.get(email);
|
|
||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
set.status = 400;
|
set.status = 400;
|
||||||
return {
|
return {
|
||||||
|
|
@ -200,15 +193,9 @@ export const user = new Elysia()
|
||||||
}
|
}
|
||||||
const savedPassword = await Bun.password.hash(password);
|
const savedPassword = await Bun.password.hash(password);
|
||||||
|
|
||||||
db.query("INSERT INTO users (email, password) VALUES (?, ?)").run(
|
db.query("INSERT INTO users (email, password) VALUES (?, ?)").run(email, savedPassword);
|
||||||
email,
|
|
||||||
savedPassword,
|
|
||||||
);
|
|
||||||
|
|
||||||
const user = db
|
const user = db.query("SELECT * FROM users WHERE email = ?").as(User).get(email);
|
||||||
.query("SELECT * FROM users WHERE email = ?")
|
|
||||||
.as(User)
|
|
||||||
.get(email);
|
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
set.status = 500;
|
set.status = 500;
|
||||||
|
|
@ -239,7 +226,7 @@ export const user = new Elysia()
|
||||||
|
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
},
|
},
|
||||||
{ body: 'signIn' },
|
{ body: "signIn" },
|
||||||
)
|
)
|
||||||
.get("/login", async ({ jwt, redirect, cookie: { auth } }) => {
|
.get("/login", async ({ jwt, redirect, cookie: { auth } }) => {
|
||||||
if (FIRST_RUN) {
|
if (FIRST_RUN) {
|
||||||
|
|
@ -308,11 +295,7 @@ export const user = new Elysia()
|
||||||
Register
|
Register
|
||||||
</a>
|
</a>
|
||||||
) : null}
|
) : null}
|
||||||
<input
|
<input type="submit" value="Login" class="w-full btn-primary" />
|
||||||
type="submit"
|
|
||||||
value="Login"
|
|
||||||
class="w-full btn-primary"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</article>
|
</article>
|
||||||
|
|
@ -324,10 +307,7 @@ export const user = new Elysia()
|
||||||
.post(
|
.post(
|
||||||
"/login",
|
"/login",
|
||||||
async function handler({ body, set, redirect, jwt, cookie: { auth } }) {
|
async function handler({ body, set, redirect, jwt, cookie: { auth } }) {
|
||||||
const existingUser = db
|
const existingUser = db.query("SELECT * FROM users WHERE email = ?").as(User).get(body.email);
|
||||||
.query("SELECT * FROM users WHERE email = ?")
|
|
||||||
.as(User)
|
|
||||||
.get(body.email);
|
|
||||||
|
|
||||||
if (!existingUser) {
|
if (!existingUser) {
|
||||||
set.status = 403;
|
set.status = 403;
|
||||||
|
|
@ -336,10 +316,7 @@ export const user = new Elysia()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const validPassword = await Bun.password.verify(
|
const validPassword = await Bun.password.verify(body.password, existingUser.password);
|
||||||
body.password,
|
|
||||||
existingUser.password,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!validPassword) {
|
if (!validPassword) {
|
||||||
set.status = 403;
|
set.status = 403;
|
||||||
|
|
@ -370,7 +347,7 @@ export const user = new Elysia()
|
||||||
|
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
},
|
},
|
||||||
{ body: 'signIn' },
|
{ body: "signIn" },
|
||||||
)
|
)
|
||||||
.get("/logoff", ({ redirect, cookie: { auth } }) => {
|
.get("/logoff", ({ redirect, cookie: { auth } }) => {
|
||||||
if (auth?.value) {
|
if (auth?.value) {
|
||||||
|
|
@ -396,10 +373,7 @@ export const user = new Elysia()
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userData = db
|
const userData = db.query("SELECT * FROM users WHERE id = ?").as(User).get(user.id);
|
||||||
.query("SELECT * FROM users WHERE id = ?")
|
|
||||||
.as(User)
|
|
||||||
.get(user.id);
|
|
||||||
|
|
||||||
if (!userData) {
|
if (!userData) {
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
|
|
@ -459,11 +433,7 @@ export const user = new Elysia()
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div role="group">
|
<div role="group">
|
||||||
<input
|
<input type="submit" value="Update" class="w-full btn-primary" />
|
||||||
type="submit"
|
|
||||||
value="Update"
|
|
||||||
class="w-full btn-primary"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</article>
|
</article>
|
||||||
|
|
@ -483,10 +453,7 @@ export const user = new Elysia()
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
}
|
}
|
||||||
const existingUser = db
|
const existingUser = db.query("SELECT * FROM users WHERE id = ?").as(User).get(user.id);
|
||||||
.query("SELECT * FROM users WHERE id = ?")
|
|
||||||
.as(User)
|
|
||||||
.get(user.id);
|
|
||||||
|
|
||||||
if (!existingUser) {
|
if (!existingUser) {
|
||||||
if (auth?.value) {
|
if (auth?.value) {
|
||||||
|
|
@ -495,10 +462,7 @@ export const user = new Elysia()
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const validPassword = await Bun.password.verify(
|
const validPassword = await Bun.password.verify(body.password, existingUser.password);
|
||||||
body.password,
|
|
||||||
existingUser.password,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!validPassword) {
|
if (!validPassword) {
|
||||||
set.status = 403;
|
set.status = 403;
|
||||||
|
|
@ -542,4 +506,4 @@ export const user = new Elysia()
|
||||||
password: t.String(),
|
password: t.String(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue