From a9b0bd8b47ec9d248c2c185d191b172d71025ab8 Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 28 May 2026 20:15:13 -0700 Subject: [PATCH 1/7] Alter schema + add form field --- drizzle.config.ts | 15 ++ package-lock.json | 12 ++ server/db/sqlite/schema/schema.ts | 1 + server/setup/migrations.ts | 206 +++++++++++++++++++++++++ src/components/CreateShareLinkForm.tsx | 20 ++- 5 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 drizzle.config.ts create mode 100644 server/setup/migrations.ts diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 000000000..d8344f942 --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,15 @@ +import { APP_PATH } from "@server/lib/consts"; +import { defineConfig } from "drizzle-kit"; +import path from "path"; + +const schema = [path.join("server", "db", "sqlite", "schema")]; + +export default defineConfig({ + dialect: "sqlite", + schema: schema, + out: path.join("server", "migrations"), + verbose: true, + dbCredentials: { + url: path.join(APP_PATH, "db", "db.sqlite") + } +}); diff --git a/package-lock.json b/package-lock.json index ec51c2a5d..a706e838f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8223,12 +8223,15 @@ "license": "MIT", "optional": true, "peer": true +<<<<<<< HEAD }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" +======= +>>>>>>> db3424784 (Alter schema + add form field) }, "node_modules/@types/ws": { "version": "8.18.1", @@ -10749,9 +10752,12 @@ "integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==", "license": "(MPL-2.0 OR Apache-2.0)", "peer": true, +<<<<<<< HEAD "engines": { "node": ">=20" }, +======= +>>>>>>> db3424784 (Alter schema + add form field) "optionalDependencies": { "@types/trusted-types": "^2.0.7" } @@ -17526,9 +17532,15 @@ } }, "node_modules/tailwindcss": { +<<<<<<< HEAD "version": "4.3.0", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", +======= + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", +>>>>>>> db3424784 (Alter schema + add form field) "license": "MIT" }, "node_modules/tapable": { diff --git a/server/db/sqlite/schema/schema.ts b/server/db/sqlite/schema/schema.ts index 2376927d2..b1947ccd7 100644 --- a/server/db/sqlite/schema/schema.ts +++ b/server/db/sqlite/schema/schema.ts @@ -1101,6 +1101,7 @@ export const resourceAccessToken = sqliteTable("resourceAccessToken", { resourceId: integer("resourceId") .notNull() .references(() => resources.resourceId, { onDelete: "cascade" }), + path: text("path"), tokenHash: text("tokenHash").notNull(), sessionLength: integer("sessionLength").notNull(), expiresAt: integer("expiresAt"), diff --git a/server/setup/migrations.ts b/server/setup/migrations.ts new file mode 100644 index 000000000..0bbc86ee3 --- /dev/null +++ b/server/setup/migrations.ts @@ -0,0 +1,206 @@ +#! /usr/bin/env node +import { migrate } from "drizzle-orm/better-sqlite3/migrator"; +import { db, exists } from "../db/sqlite"; +import path from "path"; +import semver from "semver"; +import { versionMigrations } from "../db/sqlite"; +import { __DIRNAME, APP_PATH, APP_VERSION } from "@server/lib/consts"; +import { SqliteError } from "better-sqlite3"; +import fs from "fs"; +import m1 from "./scriptsSqlite/1.0.0-beta1"; +import m2 from "./scriptsSqlite/1.0.0-beta2"; +import m3 from "./scriptsSqlite/1.0.0-beta3"; +import m4 from "./scriptsSqlite/1.0.0-beta5"; +import m5 from "./scriptsSqlite/1.0.0-beta6"; +import m6 from "./scriptsSqlite/1.0.0-beta9"; +import m7 from "./scriptsSqlite/1.0.0-beta10"; +import m8 from "./scriptsSqlite/1.0.0-beta12"; +import m13 from "./scriptsSqlite/1.0.0-beta13"; +import m15 from "./scriptsSqlite/1.0.0-beta15"; +import m16 from "./scriptsSqlite/1.0.0"; +import m17 from "./scriptsSqlite/1.1.0"; +import m18 from "./scriptsSqlite/1.2.0"; +import m19 from "./scriptsSqlite/1.3.0"; +import m20 from "./scriptsSqlite/1.5.0"; +import m21 from "./scriptsSqlite/1.6.0"; +import m22 from "./scriptsSqlite/1.7.0"; +import m23 from "./scriptsSqlite/1.8.0"; +import m24 from "./scriptsSqlite/1.9.0"; +import m25 from "./scriptsSqlite/1.10.0"; +import m26 from "./scriptsSqlite/1.10.1"; +import m27 from "./scriptsSqlite/1.10.2"; +import m28 from "./scriptsSqlite/1.11.0"; +import m29 from "./scriptsSqlite/1.11.1"; +import m30 from "./scriptsSqlite/1.12.0"; +import m31 from "./scriptsSqlite/1.13.0"; +import m32 from "./scriptsSqlite/1.14.0"; +import m33 from "./scriptsSqlite/1.15.0"; + +// THIS CANNOT IMPORT ANYTHING FROM THE SERVER +// EXCEPT FOR THE DATABASE AND THE SCHEMA + +// Define the migration list with versions and their corresponding functions +const migrations = [ + { version: "1.0.0-beta.1", run: m1 }, + { version: "1.0.0-beta.2", run: m2 }, + { version: "1.0.0-beta.3", run: m3 }, + { version: "1.0.0-beta.5", run: m4 }, + { version: "1.0.0-beta.6", run: m5 }, + { version: "1.0.0-beta.9", run: m6 }, + { version: "1.0.0-beta.10", run: m7 }, + { version: "1.0.0-beta.12", run: m8 }, + { version: "1.0.0-beta.13", run: m13 }, + { version: "1.0.0-beta.15", run: m15 }, + { version: "1.0.0", run: m16 }, + { version: "1.1.0", run: m17 }, + { version: "1.2.0", run: m18 }, + { version: "1.3.0", run: m19 }, + { version: "1.5.0", run: m20 }, + { version: "1.6.0", run: m21 }, + { version: "1.7.0", run: m22 }, + { version: "1.8.0", run: m23 }, + { version: "1.9.0", run: m24 }, + { version: "1.10.0", run: m25 }, + { version: "1.10.1", run: m26 }, + { version: "1.10.2", run: m27 }, + { version: "1.11.0", run: m28 }, + { version: "1.11.1", run: m29 }, + { version: "1.12.0", run: m30 }, + { version: "1.13.0", run: m31 }, + { version: "1.14.0", run: m32 }, + { version: "1.15.0", run: m33 } + // Add new migrations here as they are created +] as const; + +await run(); + +async function run() { + // run the migrations + await runMigrations(); +} + +function backupDb() { + // make dir config/db/backups + const appPath = APP_PATH; + const dbDir = path.join(appPath, "db"); + + const backupsDir = path.join(dbDir, "backups"); + + // check if the backups directory exists and create it if it doesn't + if (!fs.existsSync(backupsDir)) { + fs.mkdirSync(backupsDir, { recursive: true }); + } + + // copy the db.sqlite file to backups + // add the date to the filename + const date = new Date(); + const dateString = `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}_${date.getHours()}-${date.getMinutes()}-${date.getSeconds()}`; + const dbPath = path.join(dbDir, "db.sqlite"); + const backupPath = path.join(backupsDir, `db_${dateString}.sqlite`); + fs.copyFileSync(dbPath, backupPath); +} + +export async function runMigrations() { + if (process.env.DISABLE_MIGRATIONS) { + console.log("Migrations are disabled. Skipping..."); + return; + } + try { + const appVersion = APP_VERSION; + + if (exists) { + await executeScripts(); + } else { + console.log("Running migrations..."); + try { + migrate(db, { + migrationsFolder: path.join(__DIRNAME, "init") // put here during the docker build + }); + console.log("Migrations completed successfully."); + } catch (error) { + console.error("Error running migrations:", error); + } + + await db + .insert(versionMigrations) + .values({ + version: appVersion, + executedAt: Date.now() + }) + .execute(); + } + } catch (e) { + console.error("Error running migrations:", e); + await new Promise((resolve) => + setTimeout(resolve, 1000 * 60 * 60 * 24 * 1) + ); + } +} + +async function executeScripts() { + try { + // Get the last executed version from the database + const lastExecuted = await db.select().from(versionMigrations); + + // Filter and sort migrations + const pendingMigrations = lastExecuted + .map((m) => m) + .sort((a, b) => semver.compare(b.version, a.version)); + const startVersion = pendingMigrations[0]?.version ?? APP_VERSION; + console.log(`Starting migrations from version ${startVersion}`); + + const migrationsToRun = migrations.filter((migration) => + semver.gt(migration.version, startVersion) + ); + + console.log( + "Migrations to run:", + migrationsToRun.map((m) => m.version).join(", ") + ); + + // Run migrations in order + for (const migration of migrationsToRun) { + console.log(`Running migration ${migration.version}`); + + try { + if (!process.env.DISABLE_BACKUP_ON_MIGRATION) { + // Backup the database before running the migration + backupDb(); + } + + await migration.run(); + + // Update version in database + await db + .insert(versionMigrations) + .values({ + version: migration.version, + executedAt: Date.now() + }) + .execute(); + + console.log( + `Successfully completed migration ${migration.version}` + ); + } catch (e) { + if ( + e instanceof SqliteError && + e.code === "SQLITE_CONSTRAINT_UNIQUE" + ) { + console.error("Migration has already run! Skipping..."); + continue; + } + console.error( + `Failed to run migration ${migration.version}:`, + e + ); + throw e; // Re-throw to stop migration process + } + } + + console.log("All migrations completed successfully"); + } catch (error) { + console.error("Migration process failed:", error); + throw error; + } +} diff --git a/src/components/CreateShareLinkForm.tsx b/src/components/CreateShareLinkForm.tsx index 2e5dbe655..32b8dbd7b 100644 --- a/src/components/CreateShareLinkForm.tsx +++ b/src/components/CreateShareLinkForm.tsx @@ -112,6 +112,7 @@ export default function CreateShareLinkForm({ resourceId: z.number({ message: t("shareErrorSelectResource") }), resourceName: z.string(), resourceUrl: z.string(), + path: z.string().optional(), timeUnit: z.string(), timeValue: z.coerce.number().int().positive().min(1), title: z.string().optional() @@ -172,7 +173,8 @@ export default function CreateShareLinkForm({ resource: values.resourceName || "Resource" + values.resourceId - }) + }), + path: values.path } ) .catch((e) => { @@ -302,6 +304,22 @@ export default function CreateShareLinkForm({ )} /> + ( + + + {t("sharePathOptional")} + + + + + + + )} + /> + Date: Sun, 8 Feb 2026 20:23:05 +0100 Subject: [PATCH 2/7] Adjust validation to allow creation with (optional) path --- server/routers/accessToken/generateAccessToken.ts | 5 ++++- src/components/CreateShareLinkForm.tsx | 12 ++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/server/routers/accessToken/generateAccessToken.ts b/server/routers/accessToken/generateAccessToken.ts index 9d0a7a7df..66921f23e 100644 --- a/server/routers/accessToken/generateAccessToken.ts +++ b/server/routers/accessToken/generateAccessToken.ts @@ -27,6 +27,7 @@ import { OpenAPITags, registry } from "@server/openApi"; export const generateAccessTokenBodySchema = z.strictObject({ validForSeconds: z.int().positive().optional(), // seconds title: z.string().optional(), + path: z.string().optional(), description: z.string().optional() }); @@ -85,7 +86,7 @@ export async function generateAccessToken( } const { resourceId } = parsedParams.data; - const { validForSeconds, title, description } = parsedBody.data; + const { validForSeconds, title, path, description } = parsedBody.data; const [resource] = await db .select() @@ -121,6 +122,7 @@ export async function generateAccessToken( expiresAt: expiresAt || null, sessionLength: sessionLength, title: title || null, + path: path || null, description: description || null, createdAt: new Date().getTime() }) @@ -131,6 +133,7 @@ export async function generateAccessToken( expiresAt: resourceAccessToken.expiresAt, sessionLength: resourceAccessToken.sessionLength, title: resourceAccessToken.title, + path: resourceAccessToken.path, description: resourceAccessToken.description, createdAt: resourceAccessToken.createdAt }) diff --git a/src/components/CreateShareLinkForm.tsx b/src/components/CreateShareLinkForm.tsx index 32b8dbd7b..d43adc968 100644 --- a/src/components/CreateShareLinkForm.tsx +++ b/src/components/CreateShareLinkForm.tsx @@ -306,11 +306,13 @@ export default function CreateShareLinkForm({ ( - {t("sharePathOptional")} + {t( + "shareTitleOptional" + )} @@ -322,13 +324,11 @@ export default function CreateShareLinkForm({ ( - {t( - "shareTitleOptional" - )} + {t("sharePathOptional")} From 94408aad21821f2b416151bbb0358f4767894018 Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 28 May 2026 20:19:19 -0700 Subject: [PATCH 3/7] Add path onto redirectUrl --- server/routers/resource/authWithAccessToken.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/routers/resource/authWithAccessToken.ts b/server/routers/resource/authWithAccessToken.ts index a580ee40b..195dbafe2 100644 --- a/server/routers/resource/authWithAccessToken.ts +++ b/server/routers/resource/authWithAccessToken.ts @@ -167,7 +167,12 @@ export async function authWithAccessToken( let redirectUrl = `${resource.ssl ? "https" : "http"}://${resource.fullDomain}`; const postAuthPath = normalizePostAuthPath(resource.postAuthPath); - if (postAuthPath) { + if (tokenItem.path) { + // add the path from the access token to the redirect URL, ensuring there is exactly one slash between the domain and the path + redirectUrl = + redirectUrl.replace(/\/?$/, "/") + + tokenItem.path.replace(/^\/?/, ""); + } else if (postAuthPath) { redirectUrl = redirectUrl + postAuthPath; } From ad7dcddf24762c0fea0351fa2508a3eaba4b7600 Mon Sep 17 00:00:00 2001 From: NHClaessens Date: Sun, 8 Feb 2026 20:34:30 +0100 Subject: [PATCH 4/7] Add translations --- messages/bg-BG.json | 1 + messages/cs-CZ.json | 1 + messages/de-DE.json | 5 +++-- messages/en-US.json | 1 + messages/es-ES.json | 1 + messages/fr-FR.json | 1 + messages/it-IT.json | 1 + messages/ko-KR.json | 1 + messages/nb-NO.json | 1 + messages/nl-NL.json | 1 + messages/pl-PL.json | 1 + messages/pt-PT.json | 1 + messages/ru-RU.json | 1 + messages/tr-TR.json | 1 + messages/zh-CN.json | 1 + 15 files changed, 17 insertions(+), 2 deletions(-) diff --git a/messages/bg-BG.json b/messages/bg-BG.json index 3870a811f..181f4e11f 100644 --- a/messages/bg-BG.json +++ b/messages/bg-BG.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "Възникна грешка при създаването на връзката за споделяне", "shareCreateDescription": "Всеки с тази връзка може да получи достъп до ресурса", "shareTitleOptional": "Заглавие (по избор)", + "sharePathOptional": "Път (по избор)", "expireIn": "Изтече", "neverExpire": "Никога не изтича", "shareExpireDescription": "Времето на изтичане е колко дълго връзката ще бъде използваема и ще предоставя достъп до ресурса. След това време, връзката няма да работи и потребителите, които са я използвали, ще загубят достъп до ресурса.", diff --git a/messages/cs-CZ.json b/messages/cs-CZ.json index 4b1909063..13e4f80d0 100644 --- a/messages/cs-CZ.json +++ b/messages/cs-CZ.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "Při vytváření odkazu došlo k chybě", "shareCreateDescription": "Kdokoliv s tímto odkazem může přistupovat ke zdroji", "shareTitleOptional": "Název (volitelné)", + "sharePathOptional": "Cesta (volitelné)", "expireIn": "Platnost vyprší za", "neverExpire": "Nikdy nevyprší", "shareExpireDescription": "Doba platnosti určuje, jak dlouho bude odkaz použitelný a bude poskytovat přístup ke zdroji. Po této době odkaz již nebude fungovat a uživatelé kteří tento odkaz používali ztratí přístup ke zdroji.", diff --git a/messages/de-DE.json b/messages/de-DE.json index 663a0789b..b0ec53b94 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -176,8 +176,9 @@ "shareErrorCreateDescription": "Beim Erstellen des Freigabelinks ist ein Fehler aufgetreten", "shareCreateDescription": "Jeder mit diesem Link kann auf die Ressource zugreifen", "shareTitleOptional": "Titel (optional)", - "expireIn": "Läuft ab in", - "neverExpire": "Läuft nie ab", + "sharePathOptional": "Pfad (optional)", + "expireIn": "Verfällt in", + "neverExpire": "Nie ablaufen", "shareExpireDescription": "Ablaufzeit ist, wie lange der Link verwendet werden kann und bietet Zugriff auf die Ressource. Nach dieser Zeit wird der Link nicht mehr funktionieren und Benutzer, die diesen Link benutzt haben, verlieren den Zugriff auf die Ressource.", "shareSeeOnce": "Sie können diesen Link nur einmal sehen. Bitte kopieren Sie ihn.", "shareAccessHint": "Jeder mit diesem Link kann auf die Ressource zugreifen. Teilen Sie sie mit Vorsicht.", diff --git a/messages/en-US.json b/messages/en-US.json index 3999c9c69..bf038b031 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "An error occurred while creating the share link", "shareCreateDescription": "Anyone with this link can access the resource", "shareTitleOptional": "Title (optional)", + "sharePathOptional": "Path (optional)", "expireIn": "Expire In", "neverExpire": "Never expire", "shareExpireDescription": "Expiration time is how long the link will be usable and provide access to the resource. After this time, the link will no longer work, and users who used this link will lose access to the resource.", diff --git a/messages/es-ES.json b/messages/es-ES.json index f42d0d495..0124fda79 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "Se ha producido un error al crear el enlace compartido", "shareCreateDescription": "Cualquiera con este enlace puede acceder al recurso", "shareTitleOptional": "Título (opcional)", + "sharePathOptional": "Ruta (opcional)", "expireIn": "Caduca en", "neverExpire": "Nunca expirar", "shareExpireDescription": "El tiempo de caducidad es cuánto tiempo el enlace será utilizable y proporcionará acceso al recurso. Después de este tiempo, el enlace ya no funcionará, y los usuarios que usaron este enlace perderán el acceso al recurso.", diff --git a/messages/fr-FR.json b/messages/fr-FR.json index 02f5e0d41..dce9b53ef 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "Une erreur s'est produite lors de la création du lien partageable", "shareCreateDescription": "N'importe qui avec ce lien peut accéder à la ressource", "shareTitleOptional": "Titre (facultatif)", + "sharePathOptional": "Chemin (facultatif)", "expireIn": "Expire dans", "neverExpire": "N'expire jamais", "shareExpireDescription": "Le délai d'expiration correspond à la période pendant laquelle le lien sera utilisable et permettra d'accéder à la ressource. Passé ce délai, le lien ne fonctionnera plus et les utilisateurs qui l'ont utilisé perdront l'accès à la ressource.", diff --git a/messages/it-IT.json b/messages/it-IT.json index 1892e4985..547ce627e 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "Si è verificato un errore durante la creazione del link di condivisione", "shareCreateDescription": "Chiunque con questo link può accedere alla risorsa", "shareTitleOptional": "Titolo (facoltativo)", + "sharePathOptional": "Percorso (facoltativo)", "expireIn": "Scadenza In", "neverExpire": "Nessuna scadenza", "shareExpireDescription": "Il tempo di scadenza indica per quanto tempo il link sarà utilizzabile e fornirà accesso alla risorsa. Dopo questo tempo, il link non funzionerà più e gli utenti che hanno utilizzato questo link perderanno l'accesso alla risorsa.", diff --git a/messages/ko-KR.json b/messages/ko-KR.json index 3b78b86a0..4f477060c 100644 --- a/messages/ko-KR.json +++ b/messages/ko-KR.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "공유 링크를 생성하는 동안 오류가 발생했습니다", "shareCreateDescription": "이 링크가 있는 누구나 리소스에 접근할 수 있습니다.", "shareTitleOptional": "제목 (선택 사항)", + "sharePathOptional": "경로 (선택 사항)", "expireIn": "만료됨", "neverExpire": "만료되지 않음", "shareExpireDescription": "만료 시간은 링크가 사용 가능하고 리소스에 접근할 수 있는 기간입니다. 이 시간이 지나면 링크는 더 이상 작동하지 않으며, 이 링크를 사용한 사용자는 리소스에 대한 접근 권한을 잃게 됩니다.", diff --git a/messages/nb-NO.json b/messages/nb-NO.json index d70df295b..697ca9408 100644 --- a/messages/nb-NO.json +++ b/messages/nb-NO.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "Det oppsto en feil ved opprettelse av delingslenken", "shareCreateDescription": "Alle med denne lenken får tilgang til ressursen", "shareTitleOptional": "Tittel (valgfritt)", + "sharePathOptional": "Sti (valgfritt)", "expireIn": "Utløper om", "neverExpire": "Utløper aldri", "shareExpireDescription": "Utløpstid er hvor lenge lenken vil være brukbar og gi tilgang til ressursen. Etter denne tiden vil lenken ikke lenger fungere, og brukere som brukte denne lenken vil miste tilgangen til ressursen.", diff --git a/messages/nl-NL.json b/messages/nl-NL.json index 58c5587de..d45e06c11 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "Fout opgetreden tijdens het maken van de share link", "shareCreateDescription": "Iedereen met deze link heeft toegang tot de pagina", "shareTitleOptional": "Titel (optioneel)", + "sharePathOptional": "Pad (optioneel)", "expireIn": "Vervalt in", "neverExpire": "Nooit verlopen", "shareExpireDescription": "Vervaltijd is hoe lang de link bruikbaar is en geeft toegang tot de bron. Na deze tijd zal de link niet meer werken en zullen gebruikers die deze link hebben gebruikt de toegang tot de pagina verliezen.", diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 9675fe84a..8515a2c08 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "Wystąpił błąd podczas tworzenia linku udostępniania", "shareCreateDescription": "Każdy z tym linkiem może uzyskać dostęp do zasobu", "shareTitleOptional": "Tytuł (opcjonalnie)", + "sharePathOptional": "Ścieżka (opcjonalnie)", "expireIn": "Wygasa za", "neverExpire": "Nigdy nie wygasa", "shareExpireDescription": "Czas wygaśnięcia to jak długo link będzie mógł być użyty i zapewni dostęp do zasobu. Po tym czasie link nie będzie już działał, a użytkownicy, którzy użyli tego linku, utracą dostęp do zasobu.", diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 075a9d8c5..1df1161e3 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "Ocorreu um erro ao criar o link de compartilhamento", "shareCreateDescription": "Qualquer um com este link pode aceder o recurso", "shareTitleOptional": "Título (opcional)", + "sharePathOptional": "Caminho (opcional)", "expireIn": "Expira em", "neverExpire": "Nunca expirar", "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os utilizadores que usaram este link perderão acesso ao recurso.", diff --git a/messages/ru-RU.json b/messages/ru-RU.json index 3e10d79c9..85bdefdee 100644 --- a/messages/ru-RU.json +++ b/messages/ru-RU.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "Произошла ошибка при создании общей ссылки", "shareCreateDescription": "Любой, у кого есть эта ссылка, может получить доступ к ресурсу", "shareTitleOptional": "Заголовок (необязательно)", + "sharePathOptional": "Путь (необязательно)", "expireIn": "Срок действия", "neverExpire": "Бессрочный доступ", "shareExpireDescription": "Срок действия - это период, в течение которого ссылка будет работать и предоставлять доступ к ресурсу. После этого времени ссылка перестанет работать, и пользователи, использовавшие эту ссылку, потеряют доступ к ресурсу.", diff --git a/messages/tr-TR.json b/messages/tr-TR.json index e83139470..4ac7a63e6 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "Paylaşım bağlantısı oluşturulurken bir hata oluştu", "shareCreateDescription": "Bu bağlantıya sahip olan herkes kaynağa erişebilir", "shareTitleOptional": "Başlık (isteğe bağlı)", + "sharePathOptional": "Yol (isteğe bağlı)", "expireIn": "Süresi Dolacak", "neverExpire": "Hiçbir Zaman Sona Ermez", "shareExpireDescription": "Son kullanma süresi, bağlantının kullanılabilir ve kaynağa erişim sağlayacak süresidir. Bu süreden sonra bağlantı çalışmayı durduracak ve bu bağlantıyı kullanan kullanıcılar kaynağa erişimini kaybedecektir.", diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 4f27045ad..2aa84a754 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -176,6 +176,7 @@ "shareErrorCreateDescription": "创建共享链接时出错", "shareCreateDescription": "任何具有此链接的人都可以访问资源", "shareTitleOptional": "标题 (可选)", + "sharePathOptional": "路径 (可选)", "expireIn": "过期时间", "neverExpire": "永不过期", "shareExpireDescription": "过期时间是链接可以使用并提供对资源的访问时间。 此时间后,链接将不再工作,使用此链接的用户将失去对资源的访问。", From db5d1d4a163aca18bbbfe2bb7ffaec574c1fe968 Mon Sep 17 00:00:00 2001 From: NHClaessens Date: Sun, 8 Feb 2026 21:11:09 +0100 Subject: [PATCH 5/7] Update postgres schema --- server/db/pg/schema/schema.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/db/pg/schema/schema.ts b/server/db/pg/schema/schema.ts index 1033b7538..a329b8486 100644 --- a/server/db/pg/schema/schema.ts +++ b/server/db/pg/schema/schema.ts @@ -772,6 +772,7 @@ export const resourceAccessToken = pgTable("resourceAccessToken", { resourceId: integer("resourceId") .notNull() .references(() => resources.resourceId, { onDelete: "cascade" }), + path: varchar("path"), tokenHash: varchar("tokenHash").notNull(), sessionLength: bigint("sessionLength", { mode: "number" }).notNull(), expiresAt: bigint("expiresAt", { mode: "number" }), From ffe198839a5b02063ad99e3ee6daf5e1ca2bd5b3 Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 28 May 2026 20:25:34 -0700 Subject: [PATCH 6/7] Reset translations --- messages/bg-BG.json | 1 - messages/cs-CZ.json | 1 - messages/de-DE.json | 5 ++--- messages/es-ES.json | 1 - messages/fr-FR.json | 1 - messages/it-IT.json | 1 - messages/ko-KR.json | 1 - messages/nb-NO.json | 1 - messages/nl-NL.json | 1 - messages/pl-PL.json | 1 - messages/pt-PT.json | 1 - messages/ru-RU.json | 1 - messages/tr-TR.json | 1 - messages/zh-CN.json | 1 - package-lock.json | 12 ------------ 15 files changed, 2 insertions(+), 28 deletions(-) diff --git a/messages/bg-BG.json b/messages/bg-BG.json index 181f4e11f..3870a811f 100644 --- a/messages/bg-BG.json +++ b/messages/bg-BG.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "Възникна грешка при създаването на връзката за споделяне", "shareCreateDescription": "Всеки с тази връзка може да получи достъп до ресурса", "shareTitleOptional": "Заглавие (по избор)", - "sharePathOptional": "Път (по избор)", "expireIn": "Изтече", "neverExpire": "Никога не изтича", "shareExpireDescription": "Времето на изтичане е колко дълго връзката ще бъде използваема и ще предоставя достъп до ресурса. След това време, връзката няма да работи и потребителите, които са я използвали, ще загубят достъп до ресурса.", diff --git a/messages/cs-CZ.json b/messages/cs-CZ.json index 13e4f80d0..4b1909063 100644 --- a/messages/cs-CZ.json +++ b/messages/cs-CZ.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "Při vytváření odkazu došlo k chybě", "shareCreateDescription": "Kdokoliv s tímto odkazem může přistupovat ke zdroji", "shareTitleOptional": "Název (volitelné)", - "sharePathOptional": "Cesta (volitelné)", "expireIn": "Platnost vyprší za", "neverExpire": "Nikdy nevyprší", "shareExpireDescription": "Doba platnosti určuje, jak dlouho bude odkaz použitelný a bude poskytovat přístup ke zdroji. Po této době odkaz již nebude fungovat a uživatelé kteří tento odkaz používali ztratí přístup ke zdroji.", diff --git a/messages/de-DE.json b/messages/de-DE.json index b0ec53b94..663a0789b 100644 --- a/messages/de-DE.json +++ b/messages/de-DE.json @@ -176,9 +176,8 @@ "shareErrorCreateDescription": "Beim Erstellen des Freigabelinks ist ein Fehler aufgetreten", "shareCreateDescription": "Jeder mit diesem Link kann auf die Ressource zugreifen", "shareTitleOptional": "Titel (optional)", - "sharePathOptional": "Pfad (optional)", - "expireIn": "Verfällt in", - "neverExpire": "Nie ablaufen", + "expireIn": "Läuft ab in", + "neverExpire": "Läuft nie ab", "shareExpireDescription": "Ablaufzeit ist, wie lange der Link verwendet werden kann und bietet Zugriff auf die Ressource. Nach dieser Zeit wird der Link nicht mehr funktionieren und Benutzer, die diesen Link benutzt haben, verlieren den Zugriff auf die Ressource.", "shareSeeOnce": "Sie können diesen Link nur einmal sehen. Bitte kopieren Sie ihn.", "shareAccessHint": "Jeder mit diesem Link kann auf die Ressource zugreifen. Teilen Sie sie mit Vorsicht.", diff --git a/messages/es-ES.json b/messages/es-ES.json index 0124fda79..f42d0d495 100644 --- a/messages/es-ES.json +++ b/messages/es-ES.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "Se ha producido un error al crear el enlace compartido", "shareCreateDescription": "Cualquiera con este enlace puede acceder al recurso", "shareTitleOptional": "Título (opcional)", - "sharePathOptional": "Ruta (opcional)", "expireIn": "Caduca en", "neverExpire": "Nunca expirar", "shareExpireDescription": "El tiempo de caducidad es cuánto tiempo el enlace será utilizable y proporcionará acceso al recurso. Después de este tiempo, el enlace ya no funcionará, y los usuarios que usaron este enlace perderán el acceso al recurso.", diff --git a/messages/fr-FR.json b/messages/fr-FR.json index dce9b53ef..02f5e0d41 100644 --- a/messages/fr-FR.json +++ b/messages/fr-FR.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "Une erreur s'est produite lors de la création du lien partageable", "shareCreateDescription": "N'importe qui avec ce lien peut accéder à la ressource", "shareTitleOptional": "Titre (facultatif)", - "sharePathOptional": "Chemin (facultatif)", "expireIn": "Expire dans", "neverExpire": "N'expire jamais", "shareExpireDescription": "Le délai d'expiration correspond à la période pendant laquelle le lien sera utilisable et permettra d'accéder à la ressource. Passé ce délai, le lien ne fonctionnera plus et les utilisateurs qui l'ont utilisé perdront l'accès à la ressource.", diff --git a/messages/it-IT.json b/messages/it-IT.json index 547ce627e..1892e4985 100644 --- a/messages/it-IT.json +++ b/messages/it-IT.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "Si è verificato un errore durante la creazione del link di condivisione", "shareCreateDescription": "Chiunque con questo link può accedere alla risorsa", "shareTitleOptional": "Titolo (facoltativo)", - "sharePathOptional": "Percorso (facoltativo)", "expireIn": "Scadenza In", "neverExpire": "Nessuna scadenza", "shareExpireDescription": "Il tempo di scadenza indica per quanto tempo il link sarà utilizzabile e fornirà accesso alla risorsa. Dopo questo tempo, il link non funzionerà più e gli utenti che hanno utilizzato questo link perderanno l'accesso alla risorsa.", diff --git a/messages/ko-KR.json b/messages/ko-KR.json index 4f477060c..3b78b86a0 100644 --- a/messages/ko-KR.json +++ b/messages/ko-KR.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "공유 링크를 생성하는 동안 오류가 발생했습니다", "shareCreateDescription": "이 링크가 있는 누구나 리소스에 접근할 수 있습니다.", "shareTitleOptional": "제목 (선택 사항)", - "sharePathOptional": "경로 (선택 사항)", "expireIn": "만료됨", "neverExpire": "만료되지 않음", "shareExpireDescription": "만료 시간은 링크가 사용 가능하고 리소스에 접근할 수 있는 기간입니다. 이 시간이 지나면 링크는 더 이상 작동하지 않으며, 이 링크를 사용한 사용자는 리소스에 대한 접근 권한을 잃게 됩니다.", diff --git a/messages/nb-NO.json b/messages/nb-NO.json index 697ca9408..d70df295b 100644 --- a/messages/nb-NO.json +++ b/messages/nb-NO.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "Det oppsto en feil ved opprettelse av delingslenken", "shareCreateDescription": "Alle med denne lenken får tilgang til ressursen", "shareTitleOptional": "Tittel (valgfritt)", - "sharePathOptional": "Sti (valgfritt)", "expireIn": "Utløper om", "neverExpire": "Utløper aldri", "shareExpireDescription": "Utløpstid er hvor lenge lenken vil være brukbar og gi tilgang til ressursen. Etter denne tiden vil lenken ikke lenger fungere, og brukere som brukte denne lenken vil miste tilgangen til ressursen.", diff --git a/messages/nl-NL.json b/messages/nl-NL.json index d45e06c11..58c5587de 100644 --- a/messages/nl-NL.json +++ b/messages/nl-NL.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "Fout opgetreden tijdens het maken van de share link", "shareCreateDescription": "Iedereen met deze link heeft toegang tot de pagina", "shareTitleOptional": "Titel (optioneel)", - "sharePathOptional": "Pad (optioneel)", "expireIn": "Vervalt in", "neverExpire": "Nooit verlopen", "shareExpireDescription": "Vervaltijd is hoe lang de link bruikbaar is en geeft toegang tot de bron. Na deze tijd zal de link niet meer werken en zullen gebruikers die deze link hebben gebruikt de toegang tot de pagina verliezen.", diff --git a/messages/pl-PL.json b/messages/pl-PL.json index 8515a2c08..9675fe84a 100644 --- a/messages/pl-PL.json +++ b/messages/pl-PL.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "Wystąpił błąd podczas tworzenia linku udostępniania", "shareCreateDescription": "Każdy z tym linkiem może uzyskać dostęp do zasobu", "shareTitleOptional": "Tytuł (opcjonalnie)", - "sharePathOptional": "Ścieżka (opcjonalnie)", "expireIn": "Wygasa za", "neverExpire": "Nigdy nie wygasa", "shareExpireDescription": "Czas wygaśnięcia to jak długo link będzie mógł być użyty i zapewni dostęp do zasobu. Po tym czasie link nie będzie już działał, a użytkownicy, którzy użyli tego linku, utracą dostęp do zasobu.", diff --git a/messages/pt-PT.json b/messages/pt-PT.json index 1df1161e3..075a9d8c5 100644 --- a/messages/pt-PT.json +++ b/messages/pt-PT.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "Ocorreu um erro ao criar o link de compartilhamento", "shareCreateDescription": "Qualquer um com este link pode aceder o recurso", "shareTitleOptional": "Título (opcional)", - "sharePathOptional": "Caminho (opcional)", "expireIn": "Expira em", "neverExpire": "Nunca expirar", "shareExpireDescription": "Tempo de expiração é quanto tempo o link será utilizável e oferecerá acesso ao recurso. Após este tempo, o link não funcionará mais, e os utilizadores que usaram este link perderão acesso ao recurso.", diff --git a/messages/ru-RU.json b/messages/ru-RU.json index 85bdefdee..3e10d79c9 100644 --- a/messages/ru-RU.json +++ b/messages/ru-RU.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "Произошла ошибка при создании общей ссылки", "shareCreateDescription": "Любой, у кого есть эта ссылка, может получить доступ к ресурсу", "shareTitleOptional": "Заголовок (необязательно)", - "sharePathOptional": "Путь (необязательно)", "expireIn": "Срок действия", "neverExpire": "Бессрочный доступ", "shareExpireDescription": "Срок действия - это период, в течение которого ссылка будет работать и предоставлять доступ к ресурсу. После этого времени ссылка перестанет работать, и пользователи, использовавшие эту ссылку, потеряют доступ к ресурсу.", diff --git a/messages/tr-TR.json b/messages/tr-TR.json index 4ac7a63e6..e83139470 100644 --- a/messages/tr-TR.json +++ b/messages/tr-TR.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "Paylaşım bağlantısı oluşturulurken bir hata oluştu", "shareCreateDescription": "Bu bağlantıya sahip olan herkes kaynağa erişebilir", "shareTitleOptional": "Başlık (isteğe bağlı)", - "sharePathOptional": "Yol (isteğe bağlı)", "expireIn": "Süresi Dolacak", "neverExpire": "Hiçbir Zaman Sona Ermez", "shareExpireDescription": "Son kullanma süresi, bağlantının kullanılabilir ve kaynağa erişim sağlayacak süresidir. Bu süreden sonra bağlantı çalışmayı durduracak ve bu bağlantıyı kullanan kullanıcılar kaynağa erişimini kaybedecektir.", diff --git a/messages/zh-CN.json b/messages/zh-CN.json index 2aa84a754..4f27045ad 100644 --- a/messages/zh-CN.json +++ b/messages/zh-CN.json @@ -176,7 +176,6 @@ "shareErrorCreateDescription": "创建共享链接时出错", "shareCreateDescription": "任何具有此链接的人都可以访问资源", "shareTitleOptional": "标题 (可选)", - "sharePathOptional": "路径 (可选)", "expireIn": "过期时间", "neverExpire": "永不过期", "shareExpireDescription": "过期时间是链接可以使用并提供对资源的访问时间。 此时间后,链接将不再工作,使用此链接的用户将失去对资源的访问。", diff --git a/package-lock.json b/package-lock.json index a706e838f..ec51c2a5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8223,15 +8223,12 @@ "license": "MIT", "optional": true, "peer": true -<<<<<<< HEAD }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" -======= ->>>>>>> db3424784 (Alter schema + add form field) }, "node_modules/@types/ws": { "version": "8.18.1", @@ -10752,12 +10749,9 @@ "integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==", "license": "(MPL-2.0 OR Apache-2.0)", "peer": true, -<<<<<<< HEAD "engines": { "node": ">=20" }, -======= ->>>>>>> db3424784 (Alter schema + add form field) "optionalDependencies": { "@types/trusted-types": "^2.0.7" } @@ -17532,15 +17526,9 @@ } }, "node_modules/tailwindcss": { -<<<<<<< HEAD "version": "4.3.0", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", -======= - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", - "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", ->>>>>>> db3424784 (Alter schema + add form field) "license": "MIT" }, "node_modules/tapable": { From 8483616b04d06973e1f354b375b1e39fa05d893e Mon Sep 17 00:00:00 2001 From: Owen Date: Thu, 28 May 2026 20:27:25 -0700 Subject: [PATCH 7/7] Unstage ignored files --- drizzle.config.ts | 15 --- server/setup/migrations.ts | 206 ------------------------------------- 2 files changed, 221 deletions(-) delete mode 100644 drizzle.config.ts delete mode 100644 server/setup/migrations.ts diff --git a/drizzle.config.ts b/drizzle.config.ts deleted file mode 100644 index d8344f942..000000000 --- a/drizzle.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { APP_PATH } from "@server/lib/consts"; -import { defineConfig } from "drizzle-kit"; -import path from "path"; - -const schema = [path.join("server", "db", "sqlite", "schema")]; - -export default defineConfig({ - dialect: "sqlite", - schema: schema, - out: path.join("server", "migrations"), - verbose: true, - dbCredentials: { - url: path.join(APP_PATH, "db", "db.sqlite") - } -}); diff --git a/server/setup/migrations.ts b/server/setup/migrations.ts deleted file mode 100644 index 0bbc86ee3..000000000 --- a/server/setup/migrations.ts +++ /dev/null @@ -1,206 +0,0 @@ -#! /usr/bin/env node -import { migrate } from "drizzle-orm/better-sqlite3/migrator"; -import { db, exists } from "../db/sqlite"; -import path from "path"; -import semver from "semver"; -import { versionMigrations } from "../db/sqlite"; -import { __DIRNAME, APP_PATH, APP_VERSION } from "@server/lib/consts"; -import { SqliteError } from "better-sqlite3"; -import fs from "fs"; -import m1 from "./scriptsSqlite/1.0.0-beta1"; -import m2 from "./scriptsSqlite/1.0.0-beta2"; -import m3 from "./scriptsSqlite/1.0.0-beta3"; -import m4 from "./scriptsSqlite/1.0.0-beta5"; -import m5 from "./scriptsSqlite/1.0.0-beta6"; -import m6 from "./scriptsSqlite/1.0.0-beta9"; -import m7 from "./scriptsSqlite/1.0.0-beta10"; -import m8 from "./scriptsSqlite/1.0.0-beta12"; -import m13 from "./scriptsSqlite/1.0.0-beta13"; -import m15 from "./scriptsSqlite/1.0.0-beta15"; -import m16 from "./scriptsSqlite/1.0.0"; -import m17 from "./scriptsSqlite/1.1.0"; -import m18 from "./scriptsSqlite/1.2.0"; -import m19 from "./scriptsSqlite/1.3.0"; -import m20 from "./scriptsSqlite/1.5.0"; -import m21 from "./scriptsSqlite/1.6.0"; -import m22 from "./scriptsSqlite/1.7.0"; -import m23 from "./scriptsSqlite/1.8.0"; -import m24 from "./scriptsSqlite/1.9.0"; -import m25 from "./scriptsSqlite/1.10.0"; -import m26 from "./scriptsSqlite/1.10.1"; -import m27 from "./scriptsSqlite/1.10.2"; -import m28 from "./scriptsSqlite/1.11.0"; -import m29 from "./scriptsSqlite/1.11.1"; -import m30 from "./scriptsSqlite/1.12.0"; -import m31 from "./scriptsSqlite/1.13.0"; -import m32 from "./scriptsSqlite/1.14.0"; -import m33 from "./scriptsSqlite/1.15.0"; - -// THIS CANNOT IMPORT ANYTHING FROM THE SERVER -// EXCEPT FOR THE DATABASE AND THE SCHEMA - -// Define the migration list with versions and their corresponding functions -const migrations = [ - { version: "1.0.0-beta.1", run: m1 }, - { version: "1.0.0-beta.2", run: m2 }, - { version: "1.0.0-beta.3", run: m3 }, - { version: "1.0.0-beta.5", run: m4 }, - { version: "1.0.0-beta.6", run: m5 }, - { version: "1.0.0-beta.9", run: m6 }, - { version: "1.0.0-beta.10", run: m7 }, - { version: "1.0.0-beta.12", run: m8 }, - { version: "1.0.0-beta.13", run: m13 }, - { version: "1.0.0-beta.15", run: m15 }, - { version: "1.0.0", run: m16 }, - { version: "1.1.0", run: m17 }, - { version: "1.2.0", run: m18 }, - { version: "1.3.0", run: m19 }, - { version: "1.5.0", run: m20 }, - { version: "1.6.0", run: m21 }, - { version: "1.7.0", run: m22 }, - { version: "1.8.0", run: m23 }, - { version: "1.9.0", run: m24 }, - { version: "1.10.0", run: m25 }, - { version: "1.10.1", run: m26 }, - { version: "1.10.2", run: m27 }, - { version: "1.11.0", run: m28 }, - { version: "1.11.1", run: m29 }, - { version: "1.12.0", run: m30 }, - { version: "1.13.0", run: m31 }, - { version: "1.14.0", run: m32 }, - { version: "1.15.0", run: m33 } - // Add new migrations here as they are created -] as const; - -await run(); - -async function run() { - // run the migrations - await runMigrations(); -} - -function backupDb() { - // make dir config/db/backups - const appPath = APP_PATH; - const dbDir = path.join(appPath, "db"); - - const backupsDir = path.join(dbDir, "backups"); - - // check if the backups directory exists and create it if it doesn't - if (!fs.existsSync(backupsDir)) { - fs.mkdirSync(backupsDir, { recursive: true }); - } - - // copy the db.sqlite file to backups - // add the date to the filename - const date = new Date(); - const dateString = `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}_${date.getHours()}-${date.getMinutes()}-${date.getSeconds()}`; - const dbPath = path.join(dbDir, "db.sqlite"); - const backupPath = path.join(backupsDir, `db_${dateString}.sqlite`); - fs.copyFileSync(dbPath, backupPath); -} - -export async function runMigrations() { - if (process.env.DISABLE_MIGRATIONS) { - console.log("Migrations are disabled. Skipping..."); - return; - } - try { - const appVersion = APP_VERSION; - - if (exists) { - await executeScripts(); - } else { - console.log("Running migrations..."); - try { - migrate(db, { - migrationsFolder: path.join(__DIRNAME, "init") // put here during the docker build - }); - console.log("Migrations completed successfully."); - } catch (error) { - console.error("Error running migrations:", error); - } - - await db - .insert(versionMigrations) - .values({ - version: appVersion, - executedAt: Date.now() - }) - .execute(); - } - } catch (e) { - console.error("Error running migrations:", e); - await new Promise((resolve) => - setTimeout(resolve, 1000 * 60 * 60 * 24 * 1) - ); - } -} - -async function executeScripts() { - try { - // Get the last executed version from the database - const lastExecuted = await db.select().from(versionMigrations); - - // Filter and sort migrations - const pendingMigrations = lastExecuted - .map((m) => m) - .sort((a, b) => semver.compare(b.version, a.version)); - const startVersion = pendingMigrations[0]?.version ?? APP_VERSION; - console.log(`Starting migrations from version ${startVersion}`); - - const migrationsToRun = migrations.filter((migration) => - semver.gt(migration.version, startVersion) - ); - - console.log( - "Migrations to run:", - migrationsToRun.map((m) => m.version).join(", ") - ); - - // Run migrations in order - for (const migration of migrationsToRun) { - console.log(`Running migration ${migration.version}`); - - try { - if (!process.env.DISABLE_BACKUP_ON_MIGRATION) { - // Backup the database before running the migration - backupDb(); - } - - await migration.run(); - - // Update version in database - await db - .insert(versionMigrations) - .values({ - version: migration.version, - executedAt: Date.now() - }) - .execute(); - - console.log( - `Successfully completed migration ${migration.version}` - ); - } catch (e) { - if ( - e instanceof SqliteError && - e.code === "SQLITE_CONSTRAINT_UNIQUE" - ) { - console.error("Migration has already run! Skipping..."); - continue; - } - console.error( - `Failed to run migration ${migration.version}:`, - e - ); - throw e; // Re-throw to stop migration process - } - } - - console.log("All migrations completed successfully"); - } catch (error) { - console.error("Migration process failed:", error); - throw error; - } -}