mirror of
https://github.com/C4illin/ConvertX.git
synced 2025-11-05 22:43:31 +00:00
Merge pull request #413 from C4illin/devcontainer
This commit is contained in:
69
.devcontainer/Dockerfile
Normal file
69
.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
FROM debian:trixie-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
unzip \
|
||||||
|
git \
|
||||||
|
ca-certificates \
|
||||||
|
build-essential \
|
||||||
|
assimp-utils \
|
||||||
|
calibre \
|
||||||
|
dasel \
|
||||||
|
dcraw \
|
||||||
|
dvisvgm \
|
||||||
|
ffmpeg \
|
||||||
|
ghostscript \
|
||||||
|
graphicsmagick \
|
||||||
|
imagemagick-7.q16 \
|
||||||
|
inkscape \
|
||||||
|
latexmk \
|
||||||
|
libheif-examples \
|
||||||
|
libjxl-tools \
|
||||||
|
libreoffice \
|
||||||
|
libva2 \
|
||||||
|
libvips-tools \
|
||||||
|
libemail-outlook-message-perl \
|
||||||
|
lmodern \
|
||||||
|
mupdf-tools \
|
||||||
|
pandoc \
|
||||||
|
poppler-utils \
|
||||||
|
potrace \
|
||||||
|
python3-numpy \
|
||||||
|
resvg \
|
||||||
|
texlive \
|
||||||
|
texlive-fonts-recommended \
|
||||||
|
texlive-latex-extra \
|
||||||
|
texlive-latex-recommended \
|
||||||
|
texlive-xetex \
|
||||||
|
--no-install-recommends \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN ARCH=$(uname -m) && \
|
||||||
|
if [ "$ARCH" = "aarch64" ]; then \
|
||||||
|
curl -fsSL -o bun-linux-aarch64.zip https://github.com/oven-sh/bun/releases/download/bun-v1.2.2/bun-linux-aarch64.zip; \
|
||||||
|
else \
|
||||||
|
curl -fsSL -o bun-linux-x64-baseline.zip https://github.com/oven-sh/bun/releases/download/bun-v1.2.2/bun-linux-x64-baseline.zip; \
|
||||||
|
fi && \
|
||||||
|
unzip -j bun-linux-*.zip -d /usr/local/bin && \
|
||||||
|
rm bun-linux-*.zip && \
|
||||||
|
chmod +x /usr/local/bin/bun
|
||||||
|
|
||||||
|
RUN ARCH=$(uname -m) && \
|
||||||
|
if [ "$ARCH" = "aarch64" ]; then \
|
||||||
|
VTRACER_ASSET="vtracer-aarch64-unknown-linux-musl.tar.gz"; \
|
||||||
|
else \
|
||||||
|
VTRACER_ASSET="vtracer-x86_64-unknown-linux-musl.tar.gz"; \
|
||||||
|
fi && \
|
||||||
|
curl -L -o /tmp/vtracer.tar.gz "https://github.com/visioncortex/vtracer/releases/download/0.6.4/${VTRACER_ASSET}" && \
|
||||||
|
tar -xzf /tmp/vtracer.tar.gz -C /tmp/ && \
|
||||||
|
mv /tmp/vtracer /usr/local/bin/vtracer && \
|
||||||
|
chmod +x /usr/local/bin/vtracer && \
|
||||||
|
rm /tmp/vtracer.tar.gz
|
||||||
|
|
||||||
|
RUN mkdir -p data
|
||||||
|
ENV NODE_ENV=development
|
||||||
|
ENV QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox"
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["bun", "run", "dev"]
|
||||||
50
.devcontainer/devcontainer.json
Normal file
50
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"name": "ConvertX Development Environment",
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/devcontainers/features/git:1": {},
|
||||||
|
"ghcr.io/devcontainers/features/github-cli:1": {}
|
||||||
|
},
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"ms-vscode.vscode-typescript-next",
|
||||||
|
"bradlc.vscode-tailwindcss",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"ms-vscode.vscode-json",
|
||||||
|
"ms-vscode.vscode-docker",
|
||||||
|
"oven.bun-vscode"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": "explicit"
|
||||||
|
},
|
||||||
|
"typescript.preferences.importModuleSpecifier": "relative",
|
||||||
|
"typescript.suggest.autoImports": true,
|
||||||
|
"tailwindCSS.includeLanguages": {
|
||||||
|
"typescript": "javascript",
|
||||||
|
"typescriptreact": "javascript"
|
||||||
|
},
|
||||||
|
"files.associations": {
|
||||||
|
"*.css": "tailwindcss"
|
||||||
|
},
|
||||||
|
"terminal.integrated.defaultProfile.linux": "bash"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"forwardPorts": [3000],
|
||||||
|
"portsAttributes": {
|
||||||
|
"3000": {
|
||||||
|
"label": "ConvertX Application",
|
||||||
|
"onAutoForward": "notify"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"postCreateCommand": "bun install",
|
||||||
|
"remoteUser": "root",
|
||||||
|
"mounts": ["source=${localWorkspaceFolder}/data,target=/app/data,type=bind"]
|
||||||
|
}
|
||||||
104
.gitignore
vendored
104
.gitignore
vendored
@@ -1,52 +1,52 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
/node_modules
|
||||||
/.pnp
|
/.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
|
|
||||||
# next.js
|
# next.js
|
||||||
/.next/
|
/.next/
|
||||||
/out/
|
/out/
|
||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.pem
|
*.pem
|
||||||
|
|
||||||
# debug
|
# debug
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env.local
|
.env.local
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
.env.production.local
|
.env.production.local
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
|
|
||||||
**/*.trace
|
**/*.trace
|
||||||
**/*.zip
|
**/*.zip
|
||||||
**/*.tar.gz
|
**/*.tar.gz
|
||||||
**/*.tgz
|
**/*.tgz
|
||||||
**/*.log
|
**/*.log
|
||||||
package-lock.json
|
package-lock.json
|
||||||
**/*.bun
|
**/*.bun
|
||||||
/src/uploads
|
/src/uploads
|
||||||
/uploads
|
/uploads
|
||||||
/mydb.sqlite
|
/mydb.sqlite
|
||||||
/output
|
/output
|
||||||
/db
|
/db
|
||||||
/data
|
/data
|
||||||
/dist
|
/dist
|
||||||
/Bruno
|
/Bruno
|
||||||
/tsconfig.tsbuildinfo
|
/tsconfig.tsbuildinfo
|
||||||
/public/generated.css
|
/public/generated.css
|
||||||
|
|||||||
206
Dockerfile
206
Dockerfile
@@ -1,104 +1,104 @@
|
|||||||
FROM debian:trixie-slim AS base
|
FROM debian:trixie-slim AS base
|
||||||
LABEL org.opencontainers.image.source="https://github.com/C4illin/ConvertX"
|
LABEL org.opencontainers.image.source="https://github.com/C4illin/ConvertX"
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# install bun
|
# install bun
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
curl \
|
curl \
|
||||||
unzip \
|
unzip \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# if architecture is arm64, use the arm64 version of bun
|
# if architecture is arm64, use the arm64 version of bun
|
||||||
RUN ARCH=$(uname -m) && \
|
RUN ARCH=$(uname -m) && \
|
||||||
if [ "$ARCH" = "aarch64" ]; then \
|
if [ "$ARCH" = "aarch64" ]; then \
|
||||||
curl -fsSL -o bun-linux-aarch64.zip https://github.com/oven-sh/bun/releases/download/bun-v1.2.2/bun-linux-aarch64.zip; \
|
curl -fsSL -o bun-linux-aarch64.zip https://github.com/oven-sh/bun/releases/download/bun-v1.2.2/bun-linux-aarch64.zip; \
|
||||||
else \
|
else \
|
||||||
curl -fsSL -o bun-linux-x64-baseline.zip https://github.com/oven-sh/bun/releases/download/bun-v1.2.2/bun-linux-x64-baseline.zip; \
|
curl -fsSL -o bun-linux-x64-baseline.zip https://github.com/oven-sh/bun/releases/download/bun-v1.2.2/bun-linux-x64-baseline.zip; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
RUN unzip -j bun-linux-*.zip -d /usr/local/bin && \
|
RUN unzip -j bun-linux-*.zip -d /usr/local/bin && \
|
||||||
rm bun-linux-*.zip && \
|
rm bun-linux-*.zip && \
|
||||||
chmod +x /usr/local/bin/bun
|
chmod +x /usr/local/bin/bun
|
||||||
|
|
||||||
# install dependencies into temp directory
|
# install dependencies into temp directory
|
||||||
# this will cache them and speed up future builds
|
# this will cache them and speed up future builds
|
||||||
FROM base AS install
|
FROM base AS install
|
||||||
RUN mkdir -p /temp/dev
|
RUN mkdir -p /temp/dev
|
||||||
COPY package.json bun.lock /temp/dev/
|
COPY package.json bun.lock /temp/dev/
|
||||||
RUN cd /temp/dev && bun install --frozen-lockfile
|
RUN cd /temp/dev && bun install --frozen-lockfile
|
||||||
|
|
||||||
# install with --production (exclude devDependencies)
|
# install with --production (exclude devDependencies)
|
||||||
RUN mkdir -p /temp/prod
|
RUN mkdir -p /temp/prod
|
||||||
COPY package.json bun.lock /temp/prod/
|
COPY package.json bun.lock /temp/prod/
|
||||||
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
RUN cd /temp/prod && bun install --frozen-lockfile --production
|
||||||
|
|
||||||
FROM base AS prerelease
|
FROM base AS prerelease
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=install /temp/dev/node_modules node_modules
|
COPY --from=install /temp/dev/node_modules node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# ENV NODE_ENV=production
|
# ENV NODE_ENV=production
|
||||||
RUN bun run build
|
RUN bun run build
|
||||||
|
|
||||||
# copy production dependencies and source code into final image
|
# copy production dependencies and source code into final image
|
||||||
FROM base AS release
|
FROM base AS release
|
||||||
|
|
||||||
# install additional dependencies
|
# install additional dependencies
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
assimp-utils \
|
assimp-utils \
|
||||||
calibre \
|
calibre \
|
||||||
dasel \
|
dasel \
|
||||||
dcraw \
|
dcraw \
|
||||||
dvisvgm \
|
dvisvgm \
|
||||||
ffmpeg \
|
ffmpeg \
|
||||||
ghostscript \
|
ghostscript \
|
||||||
graphicsmagick \
|
graphicsmagick \
|
||||||
imagemagick-7.q16 \
|
imagemagick-7.q16 \
|
||||||
inkscape \
|
inkscape \
|
||||||
latexmk \
|
latexmk \
|
||||||
libheif-examples \
|
libheif-examples \
|
||||||
libjxl-tools \
|
libjxl-tools \
|
||||||
libreoffice \
|
libreoffice \
|
||||||
libva2 \
|
libva2 \
|
||||||
libvips-tools \
|
libvips-tools \
|
||||||
libemail-outlook-message-perl \
|
libemail-outlook-message-perl \
|
||||||
lmodern \
|
lmodern \
|
||||||
mupdf-tools \
|
mupdf-tools \
|
||||||
pandoc \
|
pandoc \
|
||||||
poppler-utils \
|
poppler-utils \
|
||||||
potrace \
|
potrace \
|
||||||
python3-numpy \
|
python3-numpy \
|
||||||
resvg \
|
resvg \
|
||||||
texlive \
|
texlive \
|
||||||
texlive-fonts-recommended \
|
texlive-fonts-recommended \
|
||||||
texlive-latex-extra \
|
texlive-latex-extra \
|
||||||
texlive-latex-recommended \
|
texlive-latex-recommended \
|
||||||
texlive-xetex \
|
texlive-xetex \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Install VTracer binary
|
# Install VTracer binary
|
||||||
RUN ARCH=$(uname -m) && \
|
RUN ARCH=$(uname -m) && \
|
||||||
if [ "$ARCH" = "aarch64" ]; then \
|
if [ "$ARCH" = "aarch64" ]; then \
|
||||||
VTRACER_ASSET="vtracer-aarch64-unknown-linux-musl.tar.gz"; \
|
VTRACER_ASSET="vtracer-aarch64-unknown-linux-musl.tar.gz"; \
|
||||||
else \
|
else \
|
||||||
VTRACER_ASSET="vtracer-x86_64-unknown-linux-musl.tar.gz"; \
|
VTRACER_ASSET="vtracer-x86_64-unknown-linux-musl.tar.gz"; \
|
||||||
fi && \
|
fi && \
|
||||||
curl -L -o /tmp/vtracer.tar.gz "https://github.com/visioncortex/vtracer/releases/download/0.6.4/${VTRACER_ASSET}" && \
|
curl -L -o /tmp/vtracer.tar.gz "https://github.com/visioncortex/vtracer/releases/download/0.6.4/${VTRACER_ASSET}" && \
|
||||||
tar -xzf /tmp/vtracer.tar.gz -C /tmp/ && \
|
tar -xzf /tmp/vtracer.tar.gz -C /tmp/ && \
|
||||||
mv /tmp/vtracer /usr/local/bin/vtracer && \
|
mv /tmp/vtracer /usr/local/bin/vtracer && \
|
||||||
chmod +x /usr/local/bin/vtracer && \
|
chmod +x /usr/local/bin/vtracer && \
|
||||||
rm /tmp/vtracer.tar.gz
|
rm /tmp/vtracer.tar.gz
|
||||||
|
|
||||||
COPY --from=install /temp/prod/node_modules node_modules
|
COPY --from=install /temp/prod/node_modules node_modules
|
||||||
COPY --from=prerelease /app/public/ /app/public/
|
COPY --from=prerelease /app/public/ /app/public/
|
||||||
COPY --from=prerelease /app/dist /app/dist
|
COPY --from=prerelease /app/dist /app/dist
|
||||||
|
|
||||||
# COPY . .
|
# COPY . .
|
||||||
RUN mkdir data
|
RUN mkdir data
|
||||||
|
|
||||||
EXPOSE 3000/tcp
|
EXPOSE 3000/tcp
|
||||||
# used for calibre
|
# used for calibre
|
||||||
ENV QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox"
|
ENV QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox"
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENTRYPOINT [ "bun", "run", "dist/src/index.js" ]
|
ENTRYPOINT [ "bun", "run", "dist/src/index.js" ]
|
||||||
15
bun.lock
15
bun.lock
@@ -21,7 +21,6 @@
|
|||||||
"@total-typescript/ts-reset": "^0.6.1",
|
"@total-typescript/ts-reset": "^0.6.1",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/node": "^24.6.2",
|
"@types/node": "^24.6.2",
|
||||||
"@types/tar": "^6.1.13",
|
|
||||||
"@typescript-eslint/parser": "^8.45.0",
|
"@typescript-eslint/parser": "^8.45.0",
|
||||||
"eslint": "^9.37.0",
|
"eslint": "^9.37.0",
|
||||||
"eslint-plugin-better-tailwindcss": "^3.7.9",
|
"eslint-plugin-better-tailwindcss": "^3.7.9",
|
||||||
@@ -240,8 +239,6 @@
|
|||||||
|
|
||||||
"@types/react": ["@types/react@19.1.15", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-+kLxJpaJzXybyDyFXYADyP1cznTO8HSuBpenGlnKOAkH4hyNINiywvXS/tGJhsrGGP/gM185RA3xpjY0Yg4erA=="],
|
"@types/react": ["@types/react@19.1.15", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-+kLxJpaJzXybyDyFXYADyP1cznTO8HSuBpenGlnKOAkH4hyNINiywvXS/tGJhsrGGP/gM185RA3xpjY0Yg4erA=="],
|
||||||
|
|
||||||
"@types/tar": ["@types/tar@6.1.13", "", { "dependencies": { "@types/node": "*", "minipass": "^4.0.0" } }, "sha512-IznnlmU5f4WcGTh2ltRu/Ijpmk8wiWXfF0VA4s+HPjHZgvFggk1YaIkbo5krX/zUCzWF8N/l4+W/LNxnvAJ8nw=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.45.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/type-utils": "8.45.0", "@typescript-eslint/utils": "8.45.0", "@typescript-eslint/visitor-keys": "8.45.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.45.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg=="],
|
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.45.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/type-utils": "8.45.0", "@typescript-eslint/utils": "8.45.0", "@typescript-eslint/visitor-keys": "8.45.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.45.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg=="],
|
||||||
|
|
||||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.45.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/types": "8.45.0", "@typescript-eslint/typescript-estree": "8.45.0", "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ=="],
|
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.45.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.45.0", "@typescript-eslint/types": "8.45.0", "@typescript-eslint/typescript-estree": "8.45.0", "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ=="],
|
||||||
@@ -470,7 +467,7 @@
|
|||||||
|
|
||||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||||
|
|
||||||
"minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
|
"minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
||||||
|
|
||||||
"minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
|
"minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
|
||||||
|
|
||||||
@@ -636,8 +633,6 @@
|
|||||||
|
|
||||||
"@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
"@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||||
|
|
||||||
"@isaacs/fs-minipass/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
|
||||||
|
|
||||||
"@napi-rs/wasm-runtime/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
|
"@napi-rs/wasm-runtime/@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
|
||||||
|
|
||||||
"@napi-rs/wasm-runtime/@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="],
|
"@napi-rs/wasm-runtime/@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="],
|
||||||
@@ -658,8 +653,6 @@
|
|||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
"@types/tar/@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "~7.12.0" } }, "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||||
@@ -674,10 +667,6 @@
|
|||||||
|
|
||||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||||
|
|
||||||
"minizlib/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
|
||||||
|
|
||||||
"tar/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
|
|
||||||
|
|
||||||
"tsconfig-paths-webpack-plugin/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"tsconfig-paths-webpack-plugin/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
"@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
|
"@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
|
||||||
@@ -704,8 +693,6 @@
|
|||||||
|
|
||||||
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
|
|
||||||
"@types/tar/@types/node/undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="],
|
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
|
||||||
|
|
||||||
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
"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": "run-p 'format:*'",
|
"format": "npm-run-all 'format:*'",
|
||||||
"format:eslint": "eslint --fix .",
|
"format:eslint": "eslint --fix .",
|
||||||
"format:prettier": "prettier --write .",
|
"format:prettier": "prettier --write .",
|
||||||
"build:js": "tsc",
|
"build:js": "tsc",
|
||||||
"build": "bun x @tailwindcss/cli -i ./src/main.css -o ./public/generated.css && bun run build:js",
|
"build": "bun x @tailwindcss/cli -i ./src/main.css -o ./public/generated.css && bun run build:js",
|
||||||
"lint": "run-p 'lint:*'",
|
"lint": "npm-run-all 'lint:*'",
|
||||||
"lint:tsc": "tsc --noEmit",
|
"lint:tsc": "tsc --noEmit",
|
||||||
"lint:knip": "knip",
|
"lint:knip": "knip",
|
||||||
"lint:eslint": "eslint .",
|
"lint:eslint": "eslint .",
|
||||||
@@ -38,7 +38,6 @@
|
|||||||
"@total-typescript/ts-reset": "^0.6.1",
|
"@total-typescript/ts-reset": "^0.6.1",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/node": "^24.6.2",
|
"@types/node": "^24.6.2",
|
||||||
"@types/tar": "^6.1.13",
|
|
||||||
"@typescript-eslint/parser": "^8.45.0",
|
"@typescript-eslint/parser": "^8.45.0",
|
||||||
"eslint": "^9.37.0",
|
"eslint": "^9.37.0",
|
||||||
"eslint-plugin-better-tailwindcss": "^3.7.9",
|
"eslint-plugin-better-tailwindcss": "^3.7.9",
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
User-agent: *
|
User-agent: *
|
||||||
Disallow: /
|
Disallow: /
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { Elysia, t } from 'elysia'
|
import { Elysia } from "elysia";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
import * as tar from "tar";
|
import * as tar from "tar";
|
||||||
import { outputDir } from "..";
|
import { outputDir } from "..";
|
||||||
@@ -29,33 +29,37 @@ export const download = new Elysia()
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
auth: true,
|
auth: true,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
.get("/archive/:userId/:jobId", async ({ params, redirect, user }) => {
|
.get(
|
||||||
const job = await db
|
"/archive/:userId/:jobId",
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
async ({ params, redirect, user }) => {
|
||||||
.get(user.id, params.jobId);
|
const job = await db
|
||||||
|
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
||||||
|
.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}`;
|
||||||
const outputTar = path.join(outputPath, `converted_files_${jobId}.tar`);
|
const outputTar = path.join(outputPath, `converted_files_${jobId}.tar`);
|
||||||
|
|
||||||
await tar.create(
|
await tar.create(
|
||||||
{
|
{
|
||||||
file: outputTar,
|
file: outputTar,
|
||||||
cwd: outputPath,
|
cwd: outputPath,
|
||||||
filter: (path) => {
|
filter: (path) => {
|
||||||
return !path.match(".*\\.tar");
|
return !path.match(".*\\.tar");
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
["."],
|
||||||
["."],
|
);
|
||||||
);
|
return Bun.file(outputTar);
|
||||||
return Bun.file(outputTar);
|
},
|
||||||
}, {
|
{
|
||||||
auth: true,
|
auth: true,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import { Filename, Jobs } from "../db/types";
|
|||||||
import { ALLOW_UNAUTHENTICATED, HIDE_HISTORY, LANGUAGE, WEBROOT } from "../helpers/env";
|
import { ALLOW_UNAUTHENTICATED, HIDE_HISTORY, LANGUAGE, WEBROOT } from "../helpers/env";
|
||||||
import { userService } from "./user";
|
import { userService } from "./user";
|
||||||
|
|
||||||
export const history = new Elysia()
|
export const history = new Elysia().use(userService).get(
|
||||||
.use(userService)
|
"/history",
|
||||||
.get("/history", async ({ jwt, redirect, user }) => {
|
async ({ redirect, user }) => {
|
||||||
if (HIDE_HISTORY) {
|
if (HIDE_HISTORY) {
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
}
|
}
|
||||||
@@ -208,6 +208,8 @@ export const history = new Elysia()
|
|||||||
</>
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
}, {
|
},
|
||||||
auth: true
|
{
|
||||||
});
|
auth: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import { getAllInputs, getAllTargets } from "../converters/main";
|
|||||||
import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
|
import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
|
||||||
import { userService } from "./user";
|
import { userService } from "./user";
|
||||||
|
|
||||||
export const listConverters = new Elysia()
|
export const listConverters = new Elysia().use(userService).get(
|
||||||
.use(userService)
|
"/converters",
|
||||||
.get("/converters", async () => {
|
async () => {
|
||||||
return (
|
return (
|
||||||
<BaseHtml webroot={WEBROOT} title="ConvertX | Converters">
|
<BaseHtml webroot={WEBROOT} title="ConvertX | Converters">
|
||||||
<>
|
<>
|
||||||
@@ -68,6 +68,8 @@ export const listConverters = new Elysia()
|
|||||||
</>
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
}, {
|
},
|
||||||
auth: true
|
{
|
||||||
});
|
auth: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|||||||
@@ -136,72 +136,80 @@ function ResultsArticle({
|
|||||||
|
|
||||||
export const results = new Elysia()
|
export const results = new Elysia()
|
||||||
.use(userService)
|
.use(userService)
|
||||||
.get("/results/:jobId", async ({ params, jwt, set, redirect, cookie: { job_id }, user }) => {
|
.get(
|
||||||
if (job_id?.value) {
|
"/results/:jobId",
|
||||||
// Clear the job_id cookie since we are viewing the results
|
async ({ params, set, cookie: { job_id }, user }) => {
|
||||||
job_id.remove();
|
if (job_id?.value) {
|
||||||
}
|
// Clear the job_id cookie since we are viewing the results
|
||||||
|
job_id.remove();
|
||||||
|
}
|
||||||
|
|
||||||
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 webroot={WEBROOT} allowUnauthenticated={ALLOW_UNAUTHENTICATED} loggedIn />
|
<Header webroot={WEBROOT} allowUnauthenticated={ALLOW_UNAUTHENTICATED} loggedIn />
|
||||||
<main
|
<main
|
||||||
class={`
|
class={`
|
||||||
w-full flex-1 px-2
|
w-full flex-1 px-2
|
||||||
sm:px-4
|
sm:px-4
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<ResultsArticle user={user} job={job} files={files} outputPath={outputPath} />
|
<ResultsArticle user={user} job={job} files={files} outputPath={outputPath} />
|
||||||
</main>
|
</main>
|
||||||
<script src={`${WEBROOT}/results.js`} defer />
|
<script src={`${WEBROOT}/results.js`} defer />
|
||||||
</>
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
}, { auth: true })
|
},
|
||||||
.post("/progress/:jobId", async ({ jwt, set, params, cookie: { job_id }, user }) => {
|
{ auth: true },
|
||||||
if (job_id?.value) {
|
)
|
||||||
// Clear the job_id cookie since we are viewing the results
|
.post(
|
||||||
job_id.remove();
|
"/progress/:jobId",
|
||||||
}
|
async ({ set, params, cookie: { job_id }, user }) => {
|
||||||
|
if (job_id?.value) {
|
||||||
|
// Clear the job_id cookie since we are viewing the results
|
||||||
|
job_id.remove();
|
||||||
|
}
|
||||||
|
|
||||||
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 user={user} job={job} files={files} outputPath={outputPath} />;
|
return <ResultsArticle user={user} job={job} files={files} outputPath={outputPath} />;
|
||||||
}, { auth: true });
|
},
|
||||||
|
{ auth: true },
|
||||||
|
);
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ import {
|
|||||||
} from "../helpers/env";
|
} from "../helpers/env";
|
||||||
import { FIRST_RUN, userService } from "./user";
|
import { FIRST_RUN, userService } from "./user";
|
||||||
|
|
||||||
export const root = new Elysia()
|
export const root = new Elysia().use(userService).get(
|
||||||
.use(userService)
|
"/",
|
||||||
.get("/", async ({ jwt, redirect, cookie: { auth, jobId } }) => {
|
async ({ jwt, redirect, cookie: { auth, jobId } }) => {
|
||||||
if (!ALLOW_UNAUTHENTICATED) {
|
if (!ALLOW_UNAUTHENTICATED) {
|
||||||
if (FIRST_RUN) {
|
if (FIRST_RUN) {
|
||||||
return redirect(`${WEBROOT}/setup`, 302);
|
return redirect(`${WEBROOT}/setup`, 302);
|
||||||
@@ -240,9 +240,11 @@ export const root = new Elysia()
|
|||||||
</>
|
</>
|
||||||
</BaseHtml>
|
</BaseHtml>
|
||||||
);
|
);
|
||||||
}, {
|
},
|
||||||
|
{
|
||||||
cookie: t.Cookie({
|
cookie: t.Cookie({
|
||||||
auth: t.Optional(t.String()),
|
auth: t.Optional(t.String()),
|
||||||
jobId: t.Optional(t.String()),
|
jobId: t.Optional(t.String()),
|
||||||
})
|
}),
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|||||||
@@ -39,30 +39,29 @@ export const userService = new Elysia({ name: "user/service" })
|
|||||||
optionalSession: t.Cookie({
|
optionalSession: t.Cookie({
|
||||||
auth: t.Optional(t.String()),
|
auth: t.Optional(t.String()),
|
||||||
jobId: t.Optional(t.String()),
|
jobId: t.Optional(t.String()),
|
||||||
})
|
}),
|
||||||
})
|
})
|
||||||
.macro("auth", {
|
.macro("auth", {
|
||||||
cookie: "session", async resolve({
|
cookie: "session",
|
||||||
status, jwt, cookie: { auth }
|
async resolve({ status, jwt, cookie: { auth } }) {
|
||||||
}) {
|
|
||||||
if (!auth.value) {
|
if (!auth.value) {
|
||||||
return status(401, {
|
return status(401, {
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Unauthorized'
|
message: "Unauthorized",
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
const user = await jwt.verify(auth.value);
|
const user = await jwt.verify(auth.value);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return status(401, {
|
return status(401, {
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Unauthorized'
|
message: "Unauthorized",
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
user
|
user,
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const user = new Elysia()
|
export const user = new Elysia()
|
||||||
@@ -237,82 +236,85 @@ export const user = new Elysia()
|
|||||||
},
|
},
|
||||||
{ body: "signIn" },
|
{ body: "signIn" },
|
||||||
)
|
)
|
||||||
.get("/login", async ({ jwt, redirect, cookie: { auth } }) => {
|
.get(
|
||||||
if (FIRST_RUN) {
|
"/login",
|
||||||
return redirect(`${WEBROOT}/setup`, 302);
|
async ({ jwt, redirect, cookie: { auth } }) => {
|
||||||
}
|
if (FIRST_RUN) {
|
||||||
|
return redirect(`${WEBROOT}/setup`, 302);
|
||||||
// if already logged in, redirect to home
|
|
||||||
if (auth?.value) {
|
|
||||||
const user = await jwt.verify(auth.value);
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auth.remove();
|
// if already logged in, redirect to home
|
||||||
}
|
if (auth?.value) {
|
||||||
|
const user = await jwt.verify(auth.value);
|
||||||
|
|
||||||
return (
|
if (user) {
|
||||||
<BaseHtml webroot={WEBROOT} title="ConvertX | Login">
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
<>
|
}
|
||||||
<Header
|
|
||||||
webroot={WEBROOT}
|
auth.remove();
|
||||||
accountRegistration={ACCOUNT_REGISTRATION}
|
}
|
||||||
allowUnauthenticated={ALLOW_UNAUTHENTICATED}
|
|
||||||
hideHistory={HIDE_HISTORY}
|
return (
|
||||||
/>
|
<BaseHtml webroot={WEBROOT} title="ConvertX | Login">
|
||||||
<main
|
<>
|
||||||
class={`
|
<Header
|
||||||
w-full flex-1 px-2
|
webroot={WEBROOT}
|
||||||
sm:px-4
|
accountRegistration={ACCOUNT_REGISTRATION}
|
||||||
`}
|
allowUnauthenticated={ALLOW_UNAUTHENTICATED}
|
||||||
>
|
hideHistory={HIDE_HISTORY}
|
||||||
<article class="article">
|
/>
|
||||||
<form method="post" class="flex flex-col gap-4">
|
<main
|
||||||
<fieldset class="mb-4 flex flex-col gap-4">
|
class={`
|
||||||
<label class="flex flex-col gap-1">
|
w-full flex-1 px-2
|
||||||
Email
|
sm:px-4
|
||||||
<input
|
`}
|
||||||
type="email"
|
>
|
||||||
name="email"
|
<article class="article">
|
||||||
class="rounded-sm bg-neutral-800 p-3"
|
<form method="post" class="flex flex-col gap-4">
|
||||||
placeholder="Email"
|
<fieldset class="mb-4 flex flex-col gap-4">
|
||||||
autocomplete="email"
|
<label class="flex flex-col gap-1">
|
||||||
required
|
Email
|
||||||
/>
|
<input
|
||||||
</label>
|
type="email"
|
||||||
<label class="flex flex-col gap-1">
|
name="email"
|
||||||
Password
|
class="rounded-sm bg-neutral-800 p-3"
|
||||||
<input
|
placeholder="Email"
|
||||||
type="password"
|
autocomplete="email"
|
||||||
name="password"
|
required
|
||||||
class="rounded-sm bg-neutral-800 p-3"
|
/>
|
||||||
placeholder="Password"
|
</label>
|
||||||
autocomplete="current-password"
|
<label class="flex flex-col gap-1">
|
||||||
required
|
Password
|
||||||
/>
|
<input
|
||||||
</label>
|
type="password"
|
||||||
</fieldset>
|
name="password"
|
||||||
<div class="flex flex-row gap-4">
|
class="rounded-sm bg-neutral-800 p-3"
|
||||||
{ACCOUNT_REGISTRATION ? (
|
placeholder="Password"
|
||||||
<a
|
autocomplete="current-password"
|
||||||
href={`${WEBROOT}/register`}
|
required
|
||||||
role="button"
|
/>
|
||||||
class="w-full btn-secondary text-center"
|
</label>
|
||||||
>
|
</fieldset>
|
||||||
Register
|
<div class="flex flex-row gap-4">
|
||||||
</a>
|
{ACCOUNT_REGISTRATION ? (
|
||||||
) : null}
|
<a
|
||||||
<input type="submit" value="Login" class="w-full btn-primary" />
|
href={`${WEBROOT}/register`}
|
||||||
</div>
|
role="button"
|
||||||
</form>
|
class="w-full btn-secondary text-center"
|
||||||
</article>
|
>
|
||||||
</main>
|
Register
|
||||||
</>
|
</a>
|
||||||
</BaseHtml>
|
) : null}
|
||||||
);
|
<input type="submit" value="Login" class="w-full btn-primary" />
|
||||||
}, { body: "signIn", cookie: "optionalSession" }
|
</div>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
|
</BaseHtml>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ body: "signIn", cookie: "optionalSession" },
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
"/login",
|
"/login",
|
||||||
@@ -373,83 +375,86 @@ export const user = new Elysia()
|
|||||||
|
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
})
|
})
|
||||||
.get("/account", async ({ user, redirect }) => {
|
.get(
|
||||||
|
"/account",
|
||||||
|
async ({ user, redirect }) => {
|
||||||
|
if (!user) {
|
||||||
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
|
}
|
||||||
|
|
||||||
if (!user) {
|
const userData = db.query("SELECT * FROM users WHERE id = ?").as(User).get(user.id);
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
|
||||||
}
|
|
||||||
|
|
||||||
const userData = db.query("SELECT * FROM users WHERE id = ?").as(User).get(user.id);
|
if (!userData) {
|
||||||
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
|
}
|
||||||
|
|
||||||
if (!userData) {
|
return (
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
<BaseHtml webroot={WEBROOT} title="ConvertX | Account">
|
||||||
}
|
<>
|
||||||
|
<Header
|
||||||
return (
|
webroot={WEBROOT}
|
||||||
<BaseHtml webroot={WEBROOT} title="ConvertX | Account">
|
accountRegistration={ACCOUNT_REGISTRATION}
|
||||||
<>
|
allowUnauthenticated={ALLOW_UNAUTHENTICATED}
|
||||||
<Header
|
hideHistory={HIDE_HISTORY}
|
||||||
webroot={WEBROOT}
|
loggedIn
|
||||||
accountRegistration={ACCOUNT_REGISTRATION}
|
/>
|
||||||
allowUnauthenticated={ALLOW_UNAUTHENTICATED}
|
<main
|
||||||
hideHistory={HIDE_HISTORY}
|
class={`
|
||||||
loggedIn
|
w-full flex-1 px-2
|
||||||
/>
|
sm:px-4
|
||||||
<main
|
`}
|
||||||
class={`
|
>
|
||||||
w-full flex-1 px-2
|
<article class="article">
|
||||||
sm:px-4
|
<form method="post" class="flex flex-col gap-4">
|
||||||
`}
|
<fieldset class="mb-4 flex flex-col gap-4">
|
||||||
>
|
<label class="flex flex-col gap-1">
|
||||||
<article class="article">
|
Email
|
||||||
<form method="post" class="flex flex-col gap-4">
|
<input
|
||||||
<fieldset class="mb-4 flex flex-col gap-4">
|
type="email"
|
||||||
<label class="flex flex-col gap-1">
|
name="email"
|
||||||
Email
|
class="rounded-sm bg-neutral-800 p-3"
|
||||||
<input
|
placeholder="Email"
|
||||||
type="email"
|
autocomplete="email"
|
||||||
name="email"
|
value={userData.email}
|
||||||
class="rounded-sm bg-neutral-800 p-3"
|
required
|
||||||
placeholder="Email"
|
/>
|
||||||
autocomplete="email"
|
</label>
|
||||||
value={userData.email}
|
<label class="flex flex-col gap-1">
|
||||||
required
|
Password (leave blank for unchanged)
|
||||||
/>
|
<input
|
||||||
</label>
|
type="password"
|
||||||
<label class="flex flex-col gap-1">
|
name="newPassword"
|
||||||
Password (leave blank for unchanged)
|
class="rounded-sm bg-neutral-800 p-3"
|
||||||
<input
|
placeholder="Password"
|
||||||
type="password"
|
autocomplete="new-password"
|
||||||
name="newPassword"
|
/>
|
||||||
class="rounded-sm bg-neutral-800 p-3"
|
</label>
|
||||||
placeholder="Password"
|
<label class="flex flex-col gap-1">
|
||||||
autocomplete="new-password"
|
Current Password
|
||||||
/>
|
<input
|
||||||
</label>
|
type="password"
|
||||||
<label class="flex flex-col gap-1">
|
name="password"
|
||||||
Current Password
|
class="rounded-sm bg-neutral-800 p-3"
|
||||||
<input
|
placeholder="Password"
|
||||||
type="password"
|
autocomplete="current-password"
|
||||||
name="password"
|
required
|
||||||
class="rounded-sm bg-neutral-800 p-3"
|
/>
|
||||||
placeholder="Password"
|
</label>
|
||||||
autocomplete="current-password"
|
</fieldset>
|
||||||
required
|
<div role="group">
|
||||||
/>
|
<input type="submit" value="Update" class="w-full btn-primary" />
|
||||||
</label>
|
</div>
|
||||||
</fieldset>
|
</form>
|
||||||
<div role="group">
|
</article>
|
||||||
<input type="submit" value="Update" class="w-full btn-primary" />
|
</main>
|
||||||
</div>
|
</>
|
||||||
</form>
|
</BaseHtml>
|
||||||
</article>
|
);
|
||||||
</main>
|
},
|
||||||
</>
|
{
|
||||||
</BaseHtml>
|
auth: true,
|
||||||
);
|
},
|
||||||
}, {
|
)
|
||||||
auth: true
|
|
||||||
})
|
|
||||||
.post(
|
.post(
|
||||||
"/account",
|
"/account",
|
||||||
async function handler({ body, set, redirect, jwt, cookie: { auth } }) {
|
async function handler({ body, set, redirect, jwt, cookie: { auth } }) {
|
||||||
@@ -513,6 +518,6 @@ export const user = new Elysia()
|
|||||||
newPassword: t.MaybeEmpty(t.String()),
|
newPassword: t.MaybeEmpty(t.String()),
|
||||||
password: t.String(),
|
password: t.String(),
|
||||||
}),
|
}),
|
||||||
cookie: "session"
|
cookie: "session",
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user