Alter schema + add form field

This commit is contained in:
Owen
2026-05-28 20:15:13 -07:00
parent a32acf7c69
commit a9b0bd8b47
5 changed files with 253 additions and 1 deletions

15
drizzle.config.ts Normal file
View File

@@ -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")
}
});

12
package-lock.json generated
View File

@@ -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": {

View File

@@ -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"),

206
server/setup/migrations.ts Normal file
View File

@@ -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;
}
}

View File

@@ -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<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({
)}
/>
<FormField
control={form.control}
name="path"
render={({ field }) => (
<FormItem>
<FormLabel>
{t("sharePathOptional")}
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="title"