Merge f6f675d49a into 53c1a54df5
This commit is contained in:
commit
aa33bf6bc0
22 changed files with 415 additions and 202 deletions
|
|
@ -44,7 +44,7 @@
|
||||||
"onAutoForward": "notify"
|
"onAutoForward": "notify"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"postCreateCommand": "bun install",
|
"postCreateCommand": "bun install && bun prisma generate",
|
||||||
"remoteUser": "root",
|
"remoteUser": "root",
|
||||||
"mounts": ["source=${localWorkspaceFolder}/data,target=/app/data,type=bind"],
|
"mounts": ["source=${localWorkspaceFolder}/data,target=/app/data,type=bind"],
|
||||||
"containerEnv": {
|
"containerEnv": {
|
||||||
|
|
|
||||||
3
.github/workflows/check-lint.yml
vendored
3
.github/workflows/check-lint.yml
vendored
|
|
@ -27,5 +27,8 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: bun install
|
run: bun install
|
||||||
|
|
||||||
|
- name: Generate Prisma client
|
||||||
|
run: bun prisma generate
|
||||||
|
|
||||||
- name: Run lint
|
- name: Run lint
|
||||||
run: bun run lint
|
run: bun run lint
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,16 @@ RUN unzip -j bun-linux-*.zip -d /usr/local/bin && \
|
||||||
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
|
COPY prisma /temp/dev/prisma/
|
||||||
|
RUN sed -i 's|file:../data/mydb.sqlite|file:/app/data/mydb.sqlite|g' /temp/dev/prisma/schema.prisma
|
||||||
|
RUN cd /temp/dev && bun install --frozen-lockfile && bun prisma generate
|
||||||
|
|
||||||
# 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
|
COPY prisma /temp/prod/prisma/
|
||||||
|
RUN sed -i 's|file:../data/mydb.sqlite|file:/app/data/mydb.sqlite|g' /temp/prod/prisma/schema.prisma
|
||||||
|
RUN cd /temp/prod && bun install --frozen-lockfile --production && bun prisma generate
|
||||||
|
|
||||||
FROM base AS prerelease
|
FROM base AS prerelease
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
@ -93,6 +97,7 @@ RUN ARCH=$(uname -m) && \
|
||||||
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 --from=prerelease /app/prisma /app/prisma
|
||||||
|
|
||||||
# COPY . .
|
# COPY . .
|
||||||
RUN mkdir data
|
RUN mkdir data
|
||||||
|
|
|
||||||
70
bun.lock
70
bun.lock
|
|
@ -8,7 +8,9 @@
|
||||||
"@elysiajs/jwt": "^1.4.0",
|
"@elysiajs/jwt": "^1.4.0",
|
||||||
"@elysiajs/static": "^1.4.4",
|
"@elysiajs/static": "^1.4.4",
|
||||||
"@kitajs/html": "^4.2.10",
|
"@kitajs/html": "^4.2.10",
|
||||||
|
"@prisma/client": "^6.19.0",
|
||||||
"elysia": "^1.4.13",
|
"elysia": "^1.4.13",
|
||||||
|
"prisma": "^6.19.0",
|
||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
"tar": "^7.5.1",
|
"tar": "^7.5.1",
|
||||||
},
|
},
|
||||||
|
|
@ -174,8 +176,24 @@
|
||||||
|
|
||||||
"@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="],
|
"@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="],
|
||||||
|
|
||||||
|
"@prisma/client": ["@prisma/client@6.19.0", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-QXFT+N/bva/QI2qoXmjBzL7D6aliPffIwP+81AdTGq0FXDoLxLkWivGMawG8iM5B9BKfxLIXxfWWAF6wbuJU6g=="],
|
||||||
|
|
||||||
|
"@prisma/config": ["@prisma/config@6.19.0", "", { "dependencies": { "c12": "3.1.0", "deepmerge-ts": "7.1.5", "effect": "3.18.4", "empathic": "2.0.0" } }, "sha512-zwCayme+NzI/WfrvFEtkFhhOaZb/hI+X8TTjzjJ252VbPxAl2hWHK5NMczmnG9sXck2lsXrxIZuK524E25UNmg=="],
|
||||||
|
|
||||||
|
"@prisma/debug": ["@prisma/debug@6.19.0", "", {}, "sha512-8hAdGG7JmxrzFcTzXZajlQCidX0XNkMJkpqtfbLV54wC6LSSX6Vni25W/G+nAANwLnZ2TmwkfIuWetA7jJxJFA=="],
|
||||||
|
|
||||||
|
"@prisma/engines": ["@prisma/engines@6.19.0", "", { "dependencies": { "@prisma/debug": "6.19.0", "@prisma/engines-version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", "@prisma/fetch-engine": "6.19.0", "@prisma/get-platform": "6.19.0" } }, "sha512-pMRJ+1S6NVdXoB8QJAPIGpKZevFjxhKt0paCkRDTZiczKb7F4yTgRP8M4JdVkpQwmaD4EoJf6qA+p61godDokw=="],
|
||||||
|
|
||||||
|
"@prisma/engines-version": ["@prisma/engines-version@6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", "", {}, "sha512-gV7uOBQfAFlWDvPJdQxMT1aSRur3a0EkU/6cfbAC5isV67tKDWUrPauyaHNpB+wN1ebM4A9jn/f4gH+3iHSYSQ=="],
|
||||||
|
|
||||||
|
"@prisma/fetch-engine": ["@prisma/fetch-engine@6.19.0", "", { "dependencies": { "@prisma/debug": "6.19.0", "@prisma/engines-version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", "@prisma/get-platform": "6.19.0" } }, "sha512-OOx2Lda0DGrZ1rodADT06ZGqHzr7HY7LNMaFE2Vp8dp146uJld58sRuasdX0OiwpHgl8SqDTUKHNUyzEq7pDdQ=="],
|
||||||
|
|
||||||
|
"@prisma/get-platform": ["@prisma/get-platform@6.19.0", "", { "dependencies": { "@prisma/debug": "6.19.0" } }, "sha512-ym85WDO2yDhC3fIXHWYpG3kVMBA49cL1XD2GCsCF8xbwoy2OkDQY44gEbAt2X46IQ4Apq9H6g0Ex1iFfPqEkHA=="],
|
||||||
|
|
||||||
"@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g=="],
|
"@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g=="],
|
||||||
|
|
||||||
|
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
|
||||||
|
|
||||||
"@tailwindcss/cli": ["@tailwindcss/cli@4.1.16", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "enhanced-resolve": "^5.18.3", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.1.16" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-dsnANPrh2ZooHyZ/8uJhc9ecpcYtufToc21NY09NS9vF16rxPCjJ8dP7TUAtPqlUJTHSmRkN2hCdoYQIlgh4fw=="],
|
"@tailwindcss/cli": ["@tailwindcss/cli@4.1.16", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "@tailwindcss/node": "4.1.16", "@tailwindcss/oxide": "4.1.16", "enhanced-resolve": "^5.18.3", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.1.16" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-dsnANPrh2ZooHyZ/8uJhc9ecpcYtufToc21NY09NS9vF16rxPCjJ8dP7TUAtPqlUJTHSmRkN2hCdoYQIlgh4fw=="],
|
||||||
|
|
||||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.16", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.16" } }, "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw=="],
|
"@tailwindcss/node": ["@tailwindcss/node@4.1.16", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.19", "source-map-js": "^1.2.1", "tailwindcss": "4.1.16" } }, "sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw=="],
|
||||||
|
|
@ -266,12 +284,18 @@
|
||||||
|
|
||||||
"bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
|
"bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
|
||||||
|
|
||||||
|
"c12": ["c12@3.1.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^16.6.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.4.2", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw=="],
|
||||||
|
|
||||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||||
|
|
||||||
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||||
|
|
||||||
|
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||||
|
|
||||||
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
||||||
|
|
||||||
|
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
|
||||||
|
|
||||||
"cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="],
|
"cliui": ["cliui@9.0.1", "", { "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w=="],
|
||||||
|
|
||||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||||
|
|
@ -282,6 +306,10 @@
|
||||||
|
|
||||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||||
|
|
||||||
|
"confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
|
||||||
|
|
||||||
|
"consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
|
||||||
|
|
||||||
"cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
|
"cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
|
||||||
|
|
||||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||||
|
|
@ -292,12 +320,24 @@
|
||||||
|
|
||||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||||
|
|
||||||
|
"deepmerge-ts": ["deepmerge-ts@7.1.5", "", {}, "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw=="],
|
||||||
|
|
||||||
|
"defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="],
|
||||||
|
|
||||||
|
"destr": ["destr@2.0.5", "", {}, "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA=="],
|
||||||
|
|
||||||
"detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
|
"detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="],
|
||||||
|
|
||||||
|
"dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="],
|
||||||
|
|
||||||
|
"effect": ["effect@3.18.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA=="],
|
||||||
|
|
||||||
"elysia": ["elysia@1.4.13", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.2.2", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-6QaWQEm7QN1UCo1TPpEjaRJPHUmnM7R29y6LY224frDGk5PrpAnWmdHkoZxkcv+JRWp1j2ROr2IHbxHbG/jRjw=="],
|
"elysia": ["elysia@1.4.13", "", { "dependencies": { "cookie": "^1.0.2", "exact-mirror": "0.2.2", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-6QaWQEm7QN1UCo1TPpEjaRJPHUmnM7R29y6LY224frDGk5PrpAnWmdHkoZxkcv+JRWp1j2ROr2IHbxHbG/jRjw=="],
|
||||||
|
|
||||||
"emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="],
|
"emoji-regex": ["emoji-regex@10.5.0", "", {}, "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg=="],
|
||||||
|
|
||||||
|
"empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="],
|
||||||
|
|
||||||
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
"enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
|
||||||
|
|
||||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||||
|
|
@ -324,6 +364,10 @@
|
||||||
|
|
||||||
"exact-mirror": ["exact-mirror@0.2.2", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-CrGe+4QzHZlnrXZVlo/WbUZ4qQZq8C0uATQVGVgXIrNXgHDBBNFD1VRfssRA2C9t3RYvh3MadZSdg2Wy7HBoQA=="],
|
"exact-mirror": ["exact-mirror@0.2.2", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-CrGe+4QzHZlnrXZVlo/WbUZ4qQZq8C0uATQVGVgXIrNXgHDBBNFD1VRfssRA2C9t3RYvh3MadZSdg2Wy7HBoQA=="],
|
||||||
|
|
||||||
|
"exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="],
|
||||||
|
|
||||||
|
"fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "^6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="],
|
||||||
|
|
||||||
"fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
|
"fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="],
|
||||||
|
|
||||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||||
|
|
@ -360,6 +404,8 @@
|
||||||
|
|
||||||
"get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
"get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||||
|
|
||||||
|
"giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="],
|
||||||
|
|
||||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||||
|
|
||||||
"globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="],
|
"globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="],
|
||||||
|
|
@ -470,10 +516,16 @@
|
||||||
|
|
||||||
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
|
"node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="],
|
||||||
|
|
||||||
|
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
|
||||||
|
|
||||||
"npm-normalize-package-bin": ["npm-normalize-package-bin@4.0.0", "", {}, "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w=="],
|
"npm-normalize-package-bin": ["npm-normalize-package-bin@4.0.0", "", {}, "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w=="],
|
||||||
|
|
||||||
"npm-run-all2": ["npm-run-all2@8.0.4", "", { "dependencies": { "ansi-styles": "^6.2.1", "cross-spawn": "^7.0.6", "memorystream": "^0.3.1", "picomatch": "^4.0.2", "pidtree": "^0.6.0", "read-package-json-fast": "^4.0.0", "shell-quote": "^1.7.3", "which": "^5.0.0" }, "bin": { "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js", "npm-run-all": "bin/npm-run-all/index.js", "npm-run-all2": "bin/npm-run-all/index.js" } }, "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA=="],
|
"npm-run-all2": ["npm-run-all2@8.0.4", "", { "dependencies": { "ansi-styles": "^6.2.1", "cross-spawn": "^7.0.6", "memorystream": "^0.3.1", "picomatch": "^4.0.2", "pidtree": "^0.6.0", "read-package-json-fast": "^4.0.0", "shell-quote": "^1.7.3", "which": "^5.0.0" }, "bin": { "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js", "npm-run-all": "bin/npm-run-all/index.js", "npm-run-all2": "bin/npm-run-all/index.js" } }, "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA=="],
|
||||||
|
|
||||||
|
"nypm": ["nypm@0.6.2", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.2", "pathe": "^2.0.3", "pkg-types": "^2.3.0", "tinyexec": "^1.0.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g=="],
|
||||||
|
|
||||||
|
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
|
||||||
|
|
||||||
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
|
"openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="],
|
||||||
|
|
||||||
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
||||||
|
|
@ -492,6 +544,10 @@
|
||||||
|
|
||||||
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
||||||
|
|
||||||
|
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||||
|
|
||||||
|
"perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
|
||||||
|
|
||||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
|
|
||||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||||
|
|
@ -500,6 +556,8 @@
|
||||||
|
|
||||||
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
|
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
|
||||||
|
|
||||||
|
"pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
|
||||||
|
|
||||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||||
|
|
||||||
"postcss-import": ["postcss-import@16.1.1", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ=="],
|
"postcss-import": ["postcss-import@16.1.1", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ=="],
|
||||||
|
|
@ -512,16 +570,24 @@
|
||||||
|
|
||||||
"prism-react-renderer": ["prism-react-renderer@2.4.1", "", { "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": ">=16.0.0" } }, "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig=="],
|
"prism-react-renderer": ["prism-react-renderer@2.4.1", "", { "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": ">=16.0.0" } }, "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig=="],
|
||||||
|
|
||||||
|
"prisma": ["prisma@6.19.0", "", { "dependencies": { "@prisma/config": "6.19.0", "@prisma/engines": "6.19.0" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw=="],
|
||||||
|
|
||||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||||
|
|
||||||
|
"pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="],
|
||||||
|
|
||||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||||
|
|
||||||
|
"rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="],
|
||||||
|
|
||||||
"react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="],
|
"react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="],
|
||||||
|
|
||||||
"read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
|
"read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
|
||||||
|
|
||||||
"read-package-json-fast": ["read-package-json-fast@4.0.0", "", { "dependencies": { "json-parse-even-better-errors": "^4.0.0", "npm-normalize-package-bin": "^4.0.0" } }, "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg=="],
|
"read-package-json-fast": ["read-package-json-fast@4.0.0", "", { "dependencies": { "json-parse-even-better-errors": "^4.0.0", "npm-normalize-package-bin": "^4.0.0" } }, "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg=="],
|
||||||
|
|
||||||
|
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||||
|
|
||||||
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
|
"resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
|
||||||
|
|
||||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||||
|
|
@ -570,6 +636,8 @@
|
||||||
|
|
||||||
"tar": ["tar@7.5.1", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g=="],
|
"tar": ["tar@7.5.1", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g=="],
|
||||||
|
|
||||||
|
"tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||||
|
|
||||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||||
|
|
||||||
"token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ=="],
|
"token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ=="],
|
||||||
|
|
@ -652,6 +720,8 @@
|
||||||
|
|
||||||
"@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=="],
|
||||||
|
|
||||||
|
"c12/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||||
|
|
||||||
"cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
"cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||||
|
|
||||||
"eslint/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"eslint/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,15 @@ import globals from "globals";
|
||||||
import tseslint from "typescript-eslint";
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
export default tseslint.config(
|
export default tseslint.config(
|
||||||
|
{
|
||||||
|
ignores: ["**/dist/**", "**/node_modules/**", "eslint.config.ts"],
|
||||||
|
},
|
||||||
js.configs.recommended,
|
js.configs.recommended,
|
||||||
tseslint.configs.recommended,
|
tseslint.configs.recommended,
|
||||||
{
|
{
|
||||||
plugins: {
|
plugins: {
|
||||||
"better-tailwindcss": eslintPluginBetterTailwindcss,
|
"better-tailwindcss": eslintPluginBetterTailwindcss,
|
||||||
},
|
},
|
||||||
ignores: ["**/node_modules/**", "eslint.config.ts"],
|
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
parser: eslintParserTypeScript,
|
parser: eslintParserTypeScript,
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@
|
||||||
"@elysiajs/jwt": "^1.4.0",
|
"@elysiajs/jwt": "^1.4.0",
|
||||||
"@elysiajs/static": "^1.4.4",
|
"@elysiajs/static": "^1.4.4",
|
||||||
"@kitajs/html": "^4.2.10",
|
"@kitajs/html": "^4.2.10",
|
||||||
|
"@prisma/client": "^6.19.0",
|
||||||
"elysia": "^1.4.13",
|
"elysia": "^1.4.13",
|
||||||
|
"prisma": "^6.19.0",
|
||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
"tar": "^7.5.1"
|
"tar": "^7.5.1"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
30
prisma/migrations/0_init/migration.sql
Normal file
30
prisma/migrations/0_init/migration.sql
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "users" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"password" TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "jobs" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"user_id" INTEGER NOT NULL,
|
||||||
|
"date_created" TEXT NOT NULL,
|
||||||
|
"status" TEXT NOT NULL DEFAULT 'not started',
|
||||||
|
"num_files" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
CONSTRAINT "jobs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "file_names" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"job_id" INTEGER NOT NULL,
|
||||||
|
"file_name" TEXT NOT NULL,
|
||||||
|
"output_file_name" TEXT NOT NULL,
|
||||||
|
"status" TEXT NOT NULL DEFAULT 'not started',
|
||||||
|
CONSTRAINT "file_names_job_id_fkey" FOREIGN KEY ("job_id") REFERENCES "jobs" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "users_email_key" ON "users"("email");
|
||||||
|
|
||||||
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (e.g., Git)
|
||||||
|
provider = "sqlite"
|
||||||
65
prisma/schema.prisma
Normal file
65
prisma/schema.prisma
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
// This is your Prisma schema file,
|
||||||
|
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "sqlite"
|
||||||
|
url = "file:../data/mydb.sqlite"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A user of the application
|
||||||
|
model User {
|
||||||
|
/// The unique identifier for the user
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
/// The email address of the user
|
||||||
|
email String @unique
|
||||||
|
/// The password of the user
|
||||||
|
password String
|
||||||
|
/// The jobs associated with the user
|
||||||
|
jobs Job[]
|
||||||
|
|
||||||
|
@@map("users")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A job created by a user
|
||||||
|
model Job {
|
||||||
|
/// The unique identifier for the job
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
/// The ID of the user who created the job
|
||||||
|
userId Int @map("user_id")
|
||||||
|
/// The date and time when the job was created
|
||||||
|
dateCreated String @map("date_created")
|
||||||
|
/// The current status of the job
|
||||||
|
status String @default("not started")
|
||||||
|
/// The number of files associated with the job
|
||||||
|
numFiles Int @default(0) @map("num_files")
|
||||||
|
/// The files associated with the job
|
||||||
|
files File[]
|
||||||
|
|
||||||
|
/// The user who created the job
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@map("jobs")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A file associated with a job
|
||||||
|
model File {
|
||||||
|
/// The unique identifier for the file
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
/// The ID of the job this file belongs to
|
||||||
|
jobId Int @map("job_id")
|
||||||
|
/// The name of the input file
|
||||||
|
fileName String @map("file_name")
|
||||||
|
/// The name of the output file
|
||||||
|
outputFileName String @map("output_file_name")
|
||||||
|
/// The current status of the file
|
||||||
|
status String @default("not started")
|
||||||
|
|
||||||
|
/// The job this file belongs to
|
||||||
|
job Job @relation(fields: [jobId], references: [id])
|
||||||
|
|
||||||
|
@@map("file_names")
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Cookie } from "elysia";
|
import { Cookie } from "elysia";
|
||||||
import db from "../db/db";
|
import prisma from "../db/db";
|
||||||
import { MAX_CONVERT_PROCESS } from "../helpers/env";
|
import { MAX_CONVERT_PROCESS } from "../helpers/env";
|
||||||
import { normalizeFiletype, normalizeOutputFiletype } from "../helpers/normalizeFiletype";
|
import { normalizeFiletype, normalizeOutputFiletype } from "../helpers/normalizeFiletype";
|
||||||
import { convert as convertassimp, properties as propertiesassimp } from "./assimp";
|
import { convert as convertassimp, properties as propertiesassimp } from "./assimp";
|
||||||
|
|
@ -146,10 +146,6 @@ export async function handleConvert(
|
||||||
converterName: string,
|
converterName: string,
|
||||||
jobId: Cookie<string | undefined>,
|
jobId: Cookie<string | undefined>,
|
||||||
) {
|
) {
|
||||||
const query = db.query(
|
|
||||||
"INSERT INTO file_names (job_id, file_name, output_file_name, status) VALUES (?1, ?2, ?3, ?4)",
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const chunk of chunks(fileNames, MAX_CONVERT_PROCESS)) {
|
for (const chunk of chunks(fileNames, MAX_CONVERT_PROCESS)) {
|
||||||
const toProcess: Promise<string>[] = [];
|
const toProcess: Promise<string>[] = [];
|
||||||
for (const fileName of chunk) {
|
for (const fileName of chunk) {
|
||||||
|
|
@ -165,9 +161,16 @@ export async function handleConvert(
|
||||||
toProcess.push(
|
toProcess.push(
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
mainConverter(filePath, fileType, convertTo, targetPath, {}, converterName)
|
mainConverter(filePath, fileType, convertTo, targetPath, {}, converterName)
|
||||||
.then((r) => {
|
.then(async (r) => {
|
||||||
if (jobId.value) {
|
if (jobId.value) {
|
||||||
query.run(jobId.value, fileName, newFileName, r);
|
await prisma.file.create({
|
||||||
|
data: {
|
||||||
|
jobId: parseInt(jobId.value, 10),
|
||||||
|
fileName,
|
||||||
|
outputFileName: newFileName,
|
||||||
|
status: r,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
resolve(r);
|
resolve(r);
|
||||||
})
|
})
|
||||||
|
|
|
||||||
69
src/db/db.ts
69
src/db/db.ts
|
|
@ -1,43 +1,38 @@
|
||||||
import { mkdirSync } from "node:fs";
|
import fs from "node:fs";
|
||||||
import { Database } from "bun:sqlite";
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
import { execSync } from "node:child_process";
|
||||||
|
|
||||||
mkdirSync("./data", { recursive: true });
|
// ensure db exists
|
||||||
const db = new Database("./data/mydb.sqlite", { create: true });
|
if (!fs.existsSync("./data/mydb.sqlite")) {
|
||||||
|
// run bun prisma migrate deploy with child_process
|
||||||
if (!db.query("SELECT * FROM sqlite_master WHERE type='table'").get()) {
|
console.log("Database not found, creating a new one...");
|
||||||
db.exec(`
|
execSync("bun prisma migrate deploy");
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
email TEXT NOT NULL,
|
|
||||||
password TEXT NOT NULL
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS file_names (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
job_id INTEGER NOT NULL,
|
|
||||||
file_name TEXT NOT NULL,
|
|
||||||
output_file_name TEXT NOT NULL,
|
|
||||||
status TEXT DEFAULT 'not started',
|
|
||||||
FOREIGN KEY (job_id) REFERENCES jobs(id)
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS jobs (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
user_id INTEGER NOT NULL,
|
|
||||||
date_created TEXT NOT NULL,
|
|
||||||
status TEXT DEFAULT 'not started',
|
|
||||||
num_files INTEGER DEFAULT 0,
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
||||||
);
|
|
||||||
PRAGMA user_version = 1;`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const dbVersion = (db.query("PRAGMA user_version").get() as { user_version?: number }).user_version;
|
// The db version before we switched to Prisma
|
||||||
if (dbVersion === 0) {
|
const prisma = new PrismaClient();
|
||||||
db.exec("ALTER TABLE file_names ADD COLUMN status TEXT DEFAULT 'not started';");
|
const legacyVersion = await prisma.$queryRaw<{ user_version: bigint }[]>`PRAGMA user_version;`;
|
||||||
db.exec("PRAGMA user_version = 1;");
|
if (legacyVersion[0]?.user_version === 1n) {
|
||||||
console.log("Updated database to version 1.");
|
// close prisma connection
|
||||||
|
await prisma.$disconnect();
|
||||||
|
// Existing legacy database found, needs migration
|
||||||
|
console.log("Legacy database found. Skipping initial migration...");
|
||||||
|
execSync("bun prisma migrate resolve --applied 0_init");
|
||||||
|
// reconnect prisma
|
||||||
|
await prisma.$connect();
|
||||||
|
// set user_version to 2
|
||||||
|
await prisma.$executeRaw`PRAGMA user_version = 2;`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable WAL mode
|
console.log("Running database migrations...");
|
||||||
db.exec("PRAGMA journal_mode = WAL;");
|
|
||||||
|
|
||||||
export default db;
|
// run any pending migrations
|
||||||
|
await prisma.$disconnect();
|
||||||
|
execSync("bun prisma migrate deploy");
|
||||||
|
await prisma.$connect();
|
||||||
|
|
||||||
|
await prisma.$queryRaw`PRAGMA journal_mode = WAL;`.catch((e) => {
|
||||||
|
console.error("Failed to set journal mode to WAL:", e);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default prisma;
|
||||||
|
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
export class Filename {
|
|
||||||
id!: number;
|
|
||||||
job_id!: number;
|
|
||||||
file_name!: string;
|
|
||||||
output_file_name!: string;
|
|
||||||
status!: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Jobs {
|
|
||||||
finished_files!: number;
|
|
||||||
id!: number;
|
|
||||||
user_id!: number;
|
|
||||||
date_created!: string;
|
|
||||||
status!: string;
|
|
||||||
num_files!: number;
|
|
||||||
files_detailed!: Filename[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class User {
|
|
||||||
id!: number;
|
|
||||||
email!: string;
|
|
||||||
password!: string;
|
|
||||||
}
|
|
||||||
|
|
@ -3,8 +3,7 @@ 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 db from "./db/db";
|
import prisma from "./db/db";
|
||||||
import { Jobs } from "./db/types";
|
|
||||||
import { AUTO_DELETE_EVERY_N_HOURS, WEBROOT } from "./helpers/env";
|
import { AUTO_DELETE_EVERY_N_HOURS, WEBROOT } from "./helpers/env";
|
||||||
import { chooseConverter } from "./pages/chooseConverter";
|
import { chooseConverter } from "./pages/chooseConverter";
|
||||||
import { convert } from "./pages/convert";
|
import { convert } from "./pages/convert";
|
||||||
|
|
@ -69,25 +68,28 @@ app.listen(3000);
|
||||||
|
|
||||||
console.log(`🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}${WEBROOT}`);
|
console.log(`🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}${WEBROOT}`);
|
||||||
|
|
||||||
const clearJobs = () => {
|
const clearJobs = async () => {
|
||||||
const jobs = db
|
const jobs = await prisma.job.findMany({
|
||||||
.query("SELECT * FROM jobs WHERE date_created < ?")
|
where: {
|
||||||
.as(Jobs)
|
dateCreated: {
|
||||||
.all(new Date(Date.now() - AUTO_DELETE_EVERY_N_HOURS * 60 * 60 * 1000).toISOString());
|
lt: 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
|
||||||
rmSync(`${outputDir}${job.user_id}/${job.id}`, {
|
rmSync(`${outputDir}${job.userId}/${job.id}`, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
force: true,
|
force: true,
|
||||||
});
|
});
|
||||||
rmSync(`${uploadsDir}${job.user_id}/${job.id}`, {
|
rmSync(`${uploadsDir}${job.userId}/${job.id}`, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
force: true,
|
force: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// delete the job
|
// delete the job
|
||||||
db.query("DELETE FROM jobs WHERE id = ?").run(job.id);
|
await prisma.job.delete({ where: { id: job.id } });
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(clearJobs, AUTO_DELETE_EVERY_N_HOURS * 60 * 60 * 1000);
|
setTimeout(clearJobs, AUTO_DELETE_EVERY_N_HOURS * 60 * 60 * 1000);
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@ import { Elysia, t } from "elysia";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
import { outputDir, uploadsDir } from "..";
|
import { outputDir, uploadsDir } from "..";
|
||||||
import { handleConvert } from "../converters/main";
|
import { handleConvert } from "../converters/main";
|
||||||
import db from "../db/db";
|
import prisma from "../db/db";
|
||||||
import { Jobs } from "../db/types";
|
|
||||||
import { WEBROOT } from "../helpers/env";
|
import { WEBROOT } from "../helpers/env";
|
||||||
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
import { normalizeFiletype } from "../helpers/normalizeFiletype";
|
||||||
import { userService } from "./user";
|
import { userService } from "./user";
|
||||||
|
|
@ -25,10 +24,15 @@ export const convert = new Elysia().use(userService).post(
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingJob = db
|
const parsedJobId = parseInt(jobId.value, 10);
|
||||||
.query("SELECT * FROM jobs WHERE id = ? AND user_id = ?")
|
const parsedUserId = parseInt(user.id, 10);
|
||||||
.as(Jobs)
|
|
||||||
.get(jobId.value, user.id);
|
const existingJob = await prisma.job.findFirst({
|
||||||
|
where: {
|
||||||
|
id: parsedJobId,
|
||||||
|
userId: parsedUserId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!existingJob) {
|
if (!existingJob) {
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
|
|
@ -61,17 +65,23 @@ export const convert = new Elysia().use(userService).post(
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
db.query("UPDATE jobs SET num_files = ?1, status = 'pending' WHERE id = ?2").run(
|
await prisma.job.update({
|
||||||
fileNames.length,
|
where: { id: parsedJobId },
|
||||||
jobId.value,
|
data: {
|
||||||
);
|
numFiles: fileNames.length,
|
||||||
|
status: "pending",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// Start the conversion process in the background
|
// Start the conversion process in the background
|
||||||
handleConvert(fileNames, userUploadsDir, userOutputDir, convertTo, converterName, jobId)
|
handleConvert(fileNames, userUploadsDir, userOutputDir, convertTo, converterName, jobId)
|
||||||
.then(() => {
|
.then(async () => {
|
||||||
// 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(jobId.value);
|
await prisma.job.update({
|
||||||
|
where: { id: parsedJobId },
|
||||||
|
data: { status: "completed" },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete all uploaded files in userUploadsDir
|
// Delete all uploaded files in userUploadsDir
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,12 @@ export const deleteFile = new Elysia().use(userService).post(
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingJob = await db
|
const existingJob = await db.job.findFirst({
|
||||||
.query("SELECT * FROM jobs WHERE id = ? AND user_id = ?")
|
where: {
|
||||||
.get(jobId.value, user.id);
|
id: parseInt(jobId.value, 10),
|
||||||
|
userId: parseInt(user.id, 10),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!existingJob) {
|
if (!existingJob) {
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
|
|
|
||||||
|
|
@ -4,32 +4,35 @@ import { outputDir, uploadsDir } from "..";
|
||||||
import db from "../db/db";
|
import db from "../db/db";
|
||||||
import { WEBROOT } from "../helpers/env";
|
import { WEBROOT } from "../helpers/env";
|
||||||
import { userService } from "./user";
|
import { userService } from "./user";
|
||||||
import { Jobs } from "../db/types";
|
|
||||||
|
|
||||||
export const deleteJob = new Elysia().use(userService).get(
|
export const deleteJob = new Elysia().use(userService).get(
|
||||||
"/delete/:userId/:jobId",
|
"/delete/:userId/:jobId",
|
||||||
async ({ params, redirect, user }) => {
|
async ({ params, redirect, user }) => {
|
||||||
const job = db
|
const job = await db.job.findFirst({
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
where: {
|
||||||
.as(Jobs)
|
userId: parseInt(user.id, 10),
|
||||||
.get(user.id, params.jobId);
|
id: parseInt(params.jobId, 10),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
return redirect(`${WEBROOT}/results`, 302);
|
return redirect(`${WEBROOT}/results`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete the directories
|
// delete the directories
|
||||||
rmSync(`${outputDir}${job.user_id}/${job.id}`, {
|
rmSync(`${outputDir}${job.userId}/${job.id}`, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
force: true,
|
force: true,
|
||||||
});
|
});
|
||||||
rmSync(`${uploadsDir}${job.user_id}/${job.id}`, {
|
rmSync(`${uploadsDir}${job.userId}/${job.id}`, {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
force: true,
|
force: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// delete the job
|
// delete the job
|
||||||
db.query("DELETE FROM jobs WHERE id = ?").run(job.id);
|
await db.job.delete({
|
||||||
|
where: { id: job.id },
|
||||||
|
});
|
||||||
return redirect(`${WEBROOT}/history`, 302);
|
return redirect(`${WEBROOT}/history`, 302);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,12 @@ export const download = new Elysia()
|
||||||
.get(
|
.get(
|
||||||
"/download/:userId/:jobId/:fileName",
|
"/download/:userId/:jobId/:fileName",
|
||||||
async ({ params, redirect, user }) => {
|
async ({ params, redirect, user }) => {
|
||||||
const job = await db
|
const job = await db.job.findFirst({
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
where: {
|
||||||
.get(user.id, params.jobId);
|
userId: parseInt(user.id, 10),
|
||||||
|
id: parseInt(params.jobId, 10),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
return redirect(`${WEBROOT}/results`, 302);
|
return redirect(`${WEBROOT}/results`, 302);
|
||||||
|
|
@ -34,9 +37,12 @@ export const download = new Elysia()
|
||||||
.get(
|
.get(
|
||||||
"/archive/:userId/:jobId",
|
"/archive/:userId/:jobId",
|
||||||
async ({ params, redirect, user }) => {
|
async ({ params, redirect, user }) => {
|
||||||
const job = await db
|
const job = await db.job.findFirst({
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
where: {
|
||||||
.get(user.id, params.jobId);
|
userId: parseInt(user.id, 10),
|
||||||
|
id: parseInt(params.jobId, 10),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
return redirect(`${WEBROOT}/results`, 302);
|
return redirect(`${WEBROOT}/results`, 302);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import { Elysia } from "elysia";
|
import { Elysia } from "elysia";
|
||||||
import { BaseHtml } from "../components/base";
|
import { BaseHtml } from "../components/base";
|
||||||
import { Header } from "../components/header";
|
import { Header } from "../components/header";
|
||||||
import db from "../db/db";
|
import prisma from "../db/db";
|
||||||
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";
|
||||||
|
|
||||||
|
|
@ -17,17 +16,20 @@ export const history = new Elysia().use(userService).get(
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
let userJobs = db.query("SELECT * FROM jobs WHERE user_id = ?").as(Jobs).all(user.id).reverse();
|
const userId = parseInt(user.id, 10);
|
||||||
|
|
||||||
for (const job of userJobs) {
|
const userJobs = await prisma.job.findMany({
|
||||||
const files = db.query("SELECT * FROM file_names WHERE job_id = ?").as(Filename).all(job.id);
|
where: {
|
||||||
|
userId,
|
||||||
job.finished_files = files.length;
|
files: {
|
||||||
job.files_detailed = files;
|
some: {}, // Ensures only jobs with files are included
|
||||||
}
|
},
|
||||||
|
},
|
||||||
// Filter out jobs with no files
|
orderBy: { id: "desc" },
|
||||||
userJobs = userJobs.filter((job) => job.num_files > 0);
|
include: {
|
||||||
|
files: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseHtml webroot={WEBROOT} title="ConvertX | Results">
|
<BaseHtml webroot={WEBROOT} title="ConvertX | Results">
|
||||||
|
|
@ -127,9 +129,9 @@ export const history = new Elysia().use(userService).get(
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</td>
|
</td>
|
||||||
<td safe>{new Date(job.date_created).toLocaleTimeString(LANGUAGE)}</td>
|
<td safe>{new Date(job.dateCreated).toLocaleTimeString(LANGUAGE)}</td>
|
||||||
<td>{job.num_files}</td>
|
<td>{job.numFiles}</td>
|
||||||
<td class="max-sm:hidden">{job.finished_files}</td>
|
<td class="max-sm:hidden">{job.files.length}</td>
|
||||||
<td safe>{job.status}</td>
|
<td safe>{job.status}</td>
|
||||||
<td>
|
<td>
|
||||||
<a
|
<a
|
||||||
|
|
@ -147,10 +149,10 @@ export const history = new Elysia().use(userService).get(
|
||||||
<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">Detailed File Information:</div>
|
<div class="mb-1 font-semibold">Detailed File Information:</div>
|
||||||
{job.files_detailed.map((file: Filename) => (
|
{job.files.map((file) => (
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="w-5/12 truncate" title={file.file_name} safe>
|
<span class="w-5/12 truncate" title={file.fileName} safe>
|
||||||
{file.file_name}
|
{file.fileName}
|
||||||
</span>
|
</span>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|
@ -164,8 +166,8 @@ export const history = new Elysia().use(userService).get(
|
||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="w-5/12 truncate" title={file.output_file_name} safe>
|
<span class="w-5/12 truncate" title={file.outputFileName} safe>
|
||||||
{file.output_file_name}
|
{file.outputFileName}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ 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 db from "../db/db";
|
import db from "../db/db";
|
||||||
import { Filename, Jobs } from "../db/types";
|
import { Job, File } from "@prisma/client";
|
||||||
import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
|
import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env";
|
||||||
import { DownloadIcon } from "../icons/download";
|
import { DownloadIcon } from "../icons/download";
|
||||||
import { DeleteIcon } from "../icons/delete";
|
import { DeleteIcon } from "../icons/delete";
|
||||||
|
|
@ -19,8 +19,8 @@ function ResultsArticle({
|
||||||
user: {
|
user: {
|
||||||
id: string;
|
id: string;
|
||||||
} & JWTPayloadSpec;
|
} & JWTPayloadSpec;
|
||||||
job: Jobs;
|
job: Job;
|
||||||
files: Filename[];
|
files: File[];
|
||||||
outputPath: string;
|
outputPath: string;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -29,11 +29,11 @@ function ResultsArticle({
|
||||||
<h1 class="text-xl">Results</h1>
|
<h1 class="text-xl">Results</h1>
|
||||||
<div class="flex flex-row gap-4">
|
<div class="flex flex-row gap-4">
|
||||||
<a
|
<a
|
||||||
style={files.length !== job.num_files ? "pointer-events: none;" : ""}
|
style={files.length !== job.numFiles ? "pointer-events: none;" : ""}
|
||||||
href={`${WEBROOT}/archive/${user.id}/${job.id}`}
|
href={`${WEBROOT}/archive/${user.id}/${job.id}`}
|
||||||
download={`converted_files_${job.id}.tar`}
|
download={`converted_files_${job.id}.tar`}
|
||||||
class="flex btn-primary flex-row gap-2 text-contrast"
|
class="flex btn-primary flex-row gap-2 text-contrast"
|
||||||
{...(files.length !== job.num_files ? { disabled: true, "aria-busy": "true" } : "")}
|
{...(files.length !== job.numFiles ? { disabled: true, "aria-busy": "true" } : "")}
|
||||||
>
|
>
|
||||||
<DownloadIcon /> <p>Tar</p>
|
<DownloadIcon /> <p>Tar</p>
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -41,18 +41,18 @@ function ResultsArticle({
|
||||||
<DownloadIcon /> <p>All</p>
|
<DownloadIcon /> <p>All</p>
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
style={files.length !== job.num_files ? "pointer-events: none;" : ""}
|
style={files.length !== job.numFiles ? "pointer-events: none;" : ""}
|
||||||
class="flex btn-primary flex-row gap-2 text-contrast"
|
class="flex btn-primary flex-row gap-2 text-contrast"
|
||||||
href={`${WEBROOT}/delete/${user.id}/${job.id}`}
|
href={`${WEBROOT}/delete/${user.id}/${job.id}`}
|
||||||
{...(files.length !== job.num_files ? { disabled: true, "aria-busy": "true" } : "")}
|
{...(files.length !== job.numFiles ? { disabled: true, "aria-busy": "true" } : "")}
|
||||||
>
|
>
|
||||||
<DeleteIcon /> <p>Delete</p>
|
<DeleteIcon /> <p>Delete</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<progress
|
<progress
|
||||||
max={job.num_files}
|
max={job.numFiles}
|
||||||
{...(files.length === job.num_files ? { value: files.length } : "")}
|
{...(files.length === job.numFiles ? { 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
|
||||||
|
|
@ -101,7 +101,7 @@ function ResultsArticle({
|
||||||
{files.map((file) => (
|
{files.map((file) => (
|
||||||
<tr>
|
<tr>
|
||||||
<td safe class="max-w-[20vw] truncate">
|
<td safe class="max-w-[20vw] truncate">
|
||||||
{file.output_file_name}
|
{file.outputFileName}
|
||||||
</td>
|
</td>
|
||||||
<td safe>{file.status}</td>
|
<td safe>{file.status}</td>
|
||||||
<td class="flex flex-row gap-4">
|
<td class="flex flex-row gap-4">
|
||||||
|
|
@ -110,7 +110,7 @@ function ResultsArticle({
|
||||||
text-accent-500 underline
|
text-accent-500 underline
|
||||||
hover:text-accent-400
|
hover:text-accent-400
|
||||||
`}
|
`}
|
||||||
href={`${WEBROOT}/download/${outputPath}${file.output_file_name}`}
|
href={`${WEBROOT}/download/${outputPath}${file.outputFileName}`}
|
||||||
>
|
>
|
||||||
<EyeIcon />
|
<EyeIcon />
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -119,8 +119,8 @@ function ResultsArticle({
|
||||||
text-accent-500 underline
|
text-accent-500 underline
|
||||||
hover:text-accent-400
|
hover:text-accent-400
|
||||||
`}
|
`}
|
||||||
href={`${WEBROOT}/download/${outputPath}${file.output_file_name}`}
|
href={`${WEBROOT}/download/${outputPath}${file.outputFileName}`}
|
||||||
download={file.output_file_name}
|
download={file.outputFileName}
|
||||||
>
|
>
|
||||||
<DownloadIcon />
|
<DownloadIcon />
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -143,10 +143,15 @@ export const results = new Elysia()
|
||||||
job_id.remove();
|
job_id.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = db
|
const userId = parseInt(user.id, 10);
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
const jobId = parseInt(params.jobId, 10);
|
||||||
.as(Jobs)
|
|
||||||
.get(user.id, params.jobId);
|
const job = await db.job.findFirst({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
id: jobId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
set.status = 404;
|
set.status = 404;
|
||||||
|
|
@ -155,12 +160,13 @@ export const results = new Elysia()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputPath = `${user.id}/${params.jobId}/`;
|
const outputPath = `${userId}/${jobId}/`;
|
||||||
|
|
||||||
const files = db
|
const files = await db.file.findMany({
|
||||||
.query("SELECT * FROM file_names WHERE job_id = ?")
|
where: {
|
||||||
.as(Filename)
|
jobId,
|
||||||
.all(params.jobId);
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseHtml webroot={WEBROOT} title="ConvertX | Result">
|
<BaseHtml webroot={WEBROOT} title="ConvertX | Result">
|
||||||
|
|
@ -189,10 +195,15 @@ export const results = new Elysia()
|
||||||
job_id.remove();
|
job_id.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
const job = db
|
const userId = parseInt(user.id, 10);
|
||||||
.query("SELECT * FROM jobs WHERE user_id = ? AND id = ?")
|
const jobId = parseInt(params.jobId, 10);
|
||||||
.as(Jobs)
|
|
||||||
.get(user.id, params.jobId);
|
const job = await db.job.findFirst({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
id: jobId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!job) {
|
if (!job) {
|
||||||
set.status = 404;
|
set.status = 404;
|
||||||
|
|
@ -201,12 +212,13 @@ export const results = new Elysia()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputPath = `${user.id}/${params.jobId}/`;
|
const outputPath = `${userId}/${jobId}/`;
|
||||||
|
|
||||||
const files = db
|
const files = await db.file.findMany({
|
||||||
.query("SELECT * FROM file_names WHERE job_id = ?")
|
where: {
|
||||||
.as(Filename)
|
jobId,
|
||||||
.all(params.jobId);
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return <ResultsArticle user={user} job={job} files={files} outputPath={outputPath} />;
|
return <ResultsArticle user={user} job={job} files={files} outputPath={outputPath} />;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ 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 {
|
import {
|
||||||
ACCOUNT_REGISTRATION,
|
ACCOUNT_REGISTRATION,
|
||||||
ALLOW_UNAUTHENTICATED,
|
ALLOW_UNAUTHENTICATED,
|
||||||
|
|
@ -31,6 +30,8 @@ export const root = new Elysia().use(userService).get(
|
||||||
|
|
||||||
// validate jwt
|
// validate jwt
|
||||||
let user: ({ id: string } & JWTPayloadSpec) | false = false;
|
let user: ({ id: string } & JWTPayloadSpec) | false = false;
|
||||||
|
let userId: number | null = null;
|
||||||
|
|
||||||
if (ALLOW_UNAUTHENTICATED) {
|
if (ALLOW_UNAUTHENTICATED) {
|
||||||
const newUserId = String(
|
const newUserId = String(
|
||||||
UNAUTHENTICATED_USER_SHARING
|
UNAUTHENTICATED_USER_SHARING
|
||||||
|
|
@ -42,6 +43,8 @@ export const root = new Elysia().use(userService).get(
|
||||||
});
|
});
|
||||||
|
|
||||||
user = { id: newUserId };
|
user = { id: newUserId };
|
||||||
|
userId = parseInt(newUserId, 10);
|
||||||
|
|
||||||
if (!auth) {
|
if (!auth) {
|
||||||
return {
|
return {
|
||||||
message: "No auth cookie, perhaps your browser is blocking cookies.",
|
message: "No auth cookie, perhaps your browser is blocking cookies.",
|
||||||
|
|
@ -64,8 +67,13 @@ export const root = new Elysia().use(userService).get(
|
||||||
user.id &&
|
user.id &&
|
||||||
(Number.parseInt(user.id) < 2 ** 24 || !ALLOW_UNAUTHENTICATED)
|
(Number.parseInt(user.id) < 2 ** 24 || !ALLOW_UNAUTHENTICATED)
|
||||||
) {
|
) {
|
||||||
// Make sure user exists in db
|
userId = parseInt(user.id, 10);
|
||||||
const existingUser = db.query("SELECT * FROM users WHERE id = ?").as(User).get(user.id);
|
|
||||||
|
const existingUser = await db.user.findFirst({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!existingUser) {
|
if (!existingUser) {
|
||||||
if (auth?.value) {
|
if (auth?.value) {
|
||||||
|
|
@ -76,19 +84,29 @@ export const root = new Elysia().use(userService).get(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!user || userId === null) {
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a new job
|
// create a new job
|
||||||
db.query("INSERT INTO jobs (user_id, date_created) VALUES (?, ?)").run(
|
await db.job.create({
|
||||||
user.id,
|
data: {
|
||||||
new Date().toISOString(),
|
userId,
|
||||||
);
|
dateCreated: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const { id } = db
|
const { id } = (await db.job.findFirst({
|
||||||
.query("SELECT id FROM jobs WHERE user_id = ? ORDER BY id DESC")
|
where: {
|
||||||
.get(user.id) as { id: number };
|
userId,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
id: "desc",
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
},
|
||||||
|
})) as { id: number };
|
||||||
|
|
||||||
if (!jobId) {
|
if (!jobId) {
|
||||||
return { message: "Cookies should be enabled to use this app." };
|
return { message: "Cookies should be enabled to use this app." };
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,12 @@ export const upload = new Elysia().use(userService).post(
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingJob = await db
|
const existingJob = await db.job.findFirst({
|
||||||
.query("SELECT * FROM jobs WHERE id = ? AND user_id = ?")
|
where: {
|
||||||
.get(jobId.value, user.id);
|
id: parseInt(jobId.value, 10),
|
||||||
|
userId: parseInt(user.id, 10),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!existingJob) {
|
if (!existingJob) {
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
import { jwt } from "@elysiajs/jwt";
|
import { jwt } from "@elysiajs/jwt";
|
||||||
import { Elysia, t } from "elysia";
|
import { Elysia, t } from "elysia";
|
||||||
import { BaseHtml } from "../components/base";
|
import prisma from "../db/db";
|
||||||
import { Header } from "../components/header";
|
|
||||||
import db from "../db/db";
|
|
||||||
import { User } from "../db/types";
|
|
||||||
import {
|
import {
|
||||||
ACCOUNT_REGISTRATION,
|
ACCOUNT_REGISTRATION,
|
||||||
ALLOW_UNAUTHENTICATED,
|
ALLOW_UNAUTHENTICATED,
|
||||||
|
|
@ -12,8 +9,10 @@ import {
|
||||||
HTTP_ALLOWED,
|
HTTP_ALLOWED,
|
||||||
WEBROOT,
|
WEBROOT,
|
||||||
} from "../helpers/env";
|
} from "../helpers/env";
|
||||||
|
import { BaseHtml } from "../components/base";
|
||||||
|
import { Header } from "../components/header";
|
||||||
|
|
||||||
export let FIRST_RUN = db.query("SELECT * FROM users").get() === null || false;
|
export let FIRST_RUN = (await prisma.user.findFirst()) === null || false;
|
||||||
|
|
||||||
export const userService = new Elysia({ name: "user/service" })
|
export const userService = new Elysia({ name: "user/service" })
|
||||||
.use(
|
.use(
|
||||||
|
|
@ -191,7 +190,7 @@ export const user = new Elysia()
|
||||||
FIRST_RUN = false;
|
FIRST_RUN = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingUser = await db.query("SELECT * FROM users WHERE email = ?").get(email);
|
const existingUser = await prisma.user.findUnique({ where: { email } });
|
||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
set.status = 400;
|
set.status = 400;
|
||||||
return {
|
return {
|
||||||
|
|
@ -200,9 +199,12 @@ 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(email, savedPassword);
|
const user = await prisma.user.create({
|
||||||
|
data: {
|
||||||
const user = db.query("SELECT * FROM users WHERE email = ?").as(User).get(email);
|
email,
|
||||||
|
password: savedPassword,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
set.status = 500;
|
set.status = 500;
|
||||||
|
|
@ -318,7 +320,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.query("SELECT * FROM users WHERE email = ?").as(User).get(body.email);
|
const existingUser = await prisma.user.findUnique({ where: { email: body.email } });
|
||||||
|
|
||||||
if (!existingUser) {
|
if (!existingUser) {
|
||||||
set.status = 403;
|
set.status = 403;
|
||||||
|
|
@ -381,7 +383,9 @@ export const user = new Elysia()
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userData = db.query("SELECT * FROM users WHERE id = ?").as(User).get(user.id);
|
const userId = Number(user.id);
|
||||||
|
|
||||||
|
const userData = await prisma.user.findUnique({ where: { id: userId } });
|
||||||
|
|
||||||
if (!userData) {
|
if (!userData) {
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
|
|
@ -465,7 +469,10 @@ export const user = new Elysia()
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return redirect(`${WEBROOT}/login`, 302);
|
return redirect(`${WEBROOT}/login`, 302);
|
||||||
}
|
}
|
||||||
const existingUser = db.query("SELECT * FROM users WHERE id = ?").as(User).get(user.id);
|
|
||||||
|
const userId = Number(user.id);
|
||||||
|
|
||||||
|
const existingUser = await prisma.user.findUnique({ where: { id: userId } });
|
||||||
|
|
||||||
if (!existingUser) {
|
if (!existingUser) {
|
||||||
if (auth?.value) {
|
if (auth?.value) {
|
||||||
|
|
@ -483,30 +490,22 @@ export const user = new Elysia()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const fields = [];
|
const updates: { email?: string; password?: string } = {};
|
||||||
const values = [];
|
|
||||||
|
|
||||||
if (body.email) {
|
if (body.email) {
|
||||||
const existingUser = await db
|
const emailInUse = await prisma.user.findUnique({ where: { email: body.email } });
|
||||||
.query("SELECT id FROM users WHERE email = ?")
|
if (emailInUse && emailInUse.id !== userId) {
|
||||||
.as(User)
|
|
||||||
.get(body.email);
|
|
||||||
if (existingUser && existingUser.id.toString() !== user.id) {
|
|
||||||
set.status = 409;
|
set.status = 409;
|
||||||
return { message: "Email already in use." };
|
return { message: "Email already in use." };
|
||||||
}
|
}
|
||||||
fields.push("email");
|
updates.email = body.email;
|
||||||
values.push(body.email);
|
|
||||||
}
|
}
|
||||||
if (body.newPassword) {
|
if (body.newPassword) {
|
||||||
fields.push("password");
|
updates.password = await Bun.password.hash(body.newPassword);
|
||||||
values.push(await Bun.password.hash(body.newPassword));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields.length > 0) {
|
if (Object.keys(updates).length > 0) {
|
||||||
db.query(
|
await prisma.user.update({ where: { id: userId }, data: updates });
|
||||||
`UPDATE users SET ${fields.map((field) => `${field}=?`).join(", ")} WHERE id=?`,
|
|
||||||
).run(...values, user.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect(`${WEBROOT}/`, 302);
|
return redirect(`${WEBROOT}/`, 302);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue