Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"onAutoForward": "notify"
}
},
"postCreateCommand": "bun install",
"postCreateCommand": "bun install && bun prisma generate",
"remoteUser": "root",
"mounts": ["source=${localWorkspaceFolder}/data,target=/app/data,type=bind"],
"containerEnv": {
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/check-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,8 @@ jobs:
- name: Install dependencies
run: bun install

- name: Generate Prisma client
run: bun prisma generate

- name: Run lint
run: bun run lint
9 changes: 7 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ RUN unzip -j bun-linux-*.zip -d /usr/local/bin && \
FROM base AS install
RUN mkdir -p /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)
RUN mkdir -p /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
WORKDIR /app
Expand Down Expand Up @@ -93,6 +97,7 @@ RUN ARCH=$(uname -m) && \
COPY --from=install /temp/prod/node_modules node_modules
COPY --from=prerelease /app/public/ /app/public/
COPY --from=prerelease /app/dist /app/dist
COPY --from=prerelease /app/prisma /app/prisma

# COPY . .
RUN mkdir data
Expand Down
70 changes: 70 additions & 0 deletions bun.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import globals from "globals";
import tseslint from "typescript-eslint";

export default tseslint.config(
{
ignores: ["**/dist/**", "**/node_modules/**", "eslint.config.ts"],
},
js.configs.recommended,
tseslint.configs.recommended,
{
plugins: {
"better-tailwindcss": eslintPluginBetterTailwindcss,
},
ignores: ["**/node_modules/**", "eslint.config.ts"],
languageOptions: {
parser: eslintParserTypeScript,
parserOptions: {
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
"@elysiajs/jwt": "^1.4.0",
"@elysiajs/static": "^1.4.4",
"@kitajs/html": "^4.2.10",
"@prisma/client": "^6.19.0",
"elysia": "^1.4.13",
"prisma": "^6.19.0",
"sanitize-filename": "^1.6.3",
"tar": "^7.5.1"
},
Expand Down
30 changes: 30 additions & 0 deletions prisma/migrations/0_init/migration.sql
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -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 changes: 65 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -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")
}
17 changes: 10 additions & 7 deletions src/converters/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Cookie } from "elysia";
import db from "../db/db";
import prisma from "../db/db";
import { MAX_CONVERT_PROCESS } from "../helpers/env";
import { normalizeFiletype, normalizeOutputFiletype } from "../helpers/normalizeFiletype";
import { convert as convertassimp, properties as propertiesassimp } from "./assimp";
Expand Down Expand Up @@ -146,10 +146,6 @@ export async function handleConvert(
converterName: string,
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)) {
const toProcess: Promise<string>[] = [];
for (const fileName of chunk) {
Expand All @@ -165,9 +161,16 @@ export async function handleConvert(
toProcess.push(
new Promise((resolve, reject) => {
mainConverter(filePath, fileType, convertTo, targetPath, {}, converterName)
.then((r) => {
.then(async (r) => {
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);
})
Expand Down
69 changes: 32 additions & 37 deletions src/db/db.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,38 @@
import { mkdirSync } from "node:fs";
import { Database } from "bun:sqlite";
import fs from "node:fs";
import { PrismaClient } from "@prisma/client";
import { execSync } from "node:child_process";

mkdirSync("./data", { recursive: true });
const db = new Database("./data/mydb.sqlite", { create: true });

if (!db.query("SELECT * FROM sqlite_master WHERE type='table'").get()) {
db.exec(`
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;`);
// ensure db exists
if (!fs.existsSync("./data/mydb.sqlite")) {
// run bun prisma migrate deploy with child_process
console.log("Database not found, creating a new one...");
execSync("bun prisma migrate deploy");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the standard way to do it?

Not that it is anything wrong with it just want to double check

}

const dbVersion = (db.query("PRAGMA user_version").get() as { user_version?: number }).user_version;
if (dbVersion === 0) {
db.exec("ALTER TABLE file_names ADD COLUMN status TEXT DEFAULT 'not started';");
db.exec("PRAGMA user_version = 1;");
console.log("Updated database to version 1.");
// The db version before we switched to Prisma
const prisma = new PrismaClient();
const legacyVersion = await prisma.$queryRaw<{ user_version: bigint }[]>`PRAGMA user_version;`;
if (legacyVersion[0]?.user_version === 1n) {
// 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
db.exec("PRAGMA journal_mode = WAL;");
console.log("Running database migrations...");

// 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 db;
export default prisma;
23 changes: 0 additions & 23 deletions src/db/types.ts

This file was deleted.

25 changes: 15 additions & 10 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { html } from "@elysiajs/html";
import { staticPlugin } from "@elysiajs/static";
import { Elysia } from "elysia";
import "./helpers/printVersions";
import db from "./db/db";
import { Jobs } from "./db/types";
import prisma from "./db/db";
import { AUTO_DELETE_EVERY_N_HOURS, WEBROOT } from "./helpers/env";
import { chooseConverter } from "./pages/chooseConverter";
import { convert } from "./pages/convert";
Expand All @@ -22,6 +21,9 @@ import { healthcheck } from "./pages/healthcheck";
export const uploadsDir = "./data/uploads/";
export const outputDir = "./data/output/";

// Fix for Elysia issue with Bun, (see https://github.com/oven-sh/bun/issues/12161)
process.getBuiltinModule = require;

const app = new Elysia({
serve: {
maxRequestBodySize: Number.MAX_SAFE_INTEGER,
Expand Down Expand Up @@ -66,25 +68,28 @@ app.listen(3000);

console.log(`🦊 Elysia is running at http://${app.server?.hostname}:${app.server?.port}${WEBROOT}`);

const clearJobs = () => {
const jobs = db
.query("SELECT * FROM jobs WHERE date_created < ?")
.as(Jobs)
.all(new Date(Date.now() - AUTO_DELETE_EVERY_N_HOURS * 60 * 60 * 1000).toISOString());
const clearJobs = async () => {
const jobs = await prisma.job.findMany({
where: {
dateCreated: {
lt: new Date(Date.now() - AUTO_DELETE_EVERY_N_HOURS * 60 * 60 * 1000).toISOString(),
},
},
});

for (const job of jobs) {
// delete the directories
rmSync(`${outputDir}${job.user_id}/${job.id}`, {
rmSync(`${outputDir}${job.userId}/${job.id}`, {
recursive: true,
force: true,
});
rmSync(`${uploadsDir}${job.user_id}/${job.id}`, {
rmSync(`${uploadsDir}${job.userId}/${job.id}`, {
recursive: true,
force: true,
});

// 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);
Expand Down
Loading