feat: migrations.

This commit is contained in:
Mattia Belletti
2025-01-07 13:47:36 +01:00
parent 8ce48e04fa
commit a122f769cd
5 changed files with 415 additions and 762 deletions

1015
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,12 @@
import { Database } from "./types"; // this is the Database interface we defined earlier
import { createPool } from "mysql2"; // do not use 'mysql2/promises'!
import { Kysely, MysqlDialect } from "kysely";
import {
Kysely,
Migration,
MigrationProvider,
Migrator,
MysqlDialect,
} from "kysely";
import { env } from "process";
const dialect = new MysqlDialect({
@@ -9,7 +15,7 @@ const dialect = new MysqlDialect({
host: env.DATABASE_HOST || "",
user: env.DATABASE_USER || "",
password: env.DATABASE_PASSWORD || "",
port: parseInt(env.DATABASE_PORT || "3308", 10),
port: parseInt(env.DATABASE_PORT || "3306", 10),
connectionLimit: parseInt(
env.CONDATABASE_CONNECTIONLIMITNECTIONLIMIT || "10",
10
@@ -37,3 +43,32 @@ export const db = new Kysely<Database>({
}
},
});
import * as migrations from "./migrations";
class InnerMigrationProvider implements MigrationProvider {
getMigrations(): Promise<Record<string, Migration>> {
return Promise.resolve(migrations);
}
}
export async function migrateToLatest() {
const migrator = new Migrator({
db,
provider: new InnerMigrationProvider(),
});
const { error, results } = await migrator.migrateToLatest();
results?.forEach((it) => {
if (it.status === "Success") {
console.log(`migration "${it.migrationName}" was executed successfully`);
} else if (it.status === "Error") {
console.error(`failed to execute migration "${it.migrationName}"`);
}
});
if (error) {
throw new Error(JSON.stringify(error));
}
}

View File

@@ -0,0 +1,64 @@
import { Kysely } from "kysely";
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable("session_info")
.addColumn("id", "char(36)", (col) => col.primaryKey())
.addColumn("game_name", "varchar(255)", (col) => col.notNull())
.addColumn("version", "varchar(255)", (col) => col.notNull())
.addColumn("save_id", "char(255)", (col) => col.notNull())
.execute();
await db.schema
.createTable("log_entries")
.addColumn("id", "int4", (col) => col.primaryKey().autoIncrement())
.addColumn("session_info_id", "char(36)", (col) =>
col
.notNull()
.references("session_info.id")
.onDelete("cascade")
.onUpdate("cascade")
)
.addColumn("message", "varchar(255)", (col) => col.notNull())
.addColumn("timestamp", "datetime", (col) => col.notNull())
.addColumn("category", "varchar(255)", (col) => col.defaultTo(null))
.execute();
await db.schema
.createIndex("log_entries_session_info_id")
.on("log_entries")
.column("session_info_id")
.execute();
await db.schema
.createIndex("log_entries_category")
.on("log_entries")
.column("category")
.execute();
await db.schema
.createTable("log_metadata")
.addColumn("id", "int4", (col) => col.primaryKey().autoIncrement())
.addColumn("log_entry_id", "int4", (col) =>
col
.notNull()
.references("log_entries.id")
.onDelete("cascade")
.onUpdate("cascade")
)
.addColumn("key", "varchar(50)", (col) => col.notNull())
.addColumn("value", "varchar(255)", (col) => col.notNull())
.execute();
await db.schema
.createIndex("log_metadata_log_entry_id")
.on("log_metadata")
.column("log_entry_id")
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema.dropTable("log_metadata").execute();
await db.schema.dropTable("log_entries").execute();
await db.schema.dropTable("session_info").execute();
}

View File

@@ -0,0 +1 @@
export * as migration20250107T104543020Z from "./2025-01-07T10-45-43.020Z";

View File

@@ -6,36 +6,44 @@ import path from "path";
import { installCors } from "./install-cors";
import { installMeta } from "./install-meta";
import { OpenAPIObject } from "openapi3-ts/oas31";
import { migrateToLatest } from "./db/init";
const app = express();
migrateToLatest()
.then(() => {
const app = express();
// enable cors
installCors(app);
// enable cors
installCors(app);
// serve che client files
app.use(express.static("./dist/client"));
// serve che client files
app.use(express.static("./dist/client"));
// enable body parser to parse json and urlencoded data
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// enable body parser to parse json and urlencoded data
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// install the openapi/swagger/etc... metadata about the api
const openAPIObject = installMeta(app);
// install the openapi/swagger/etc... metadata about the api
const openAPIObject = installMeta(app);
// install the ts-rest router
installRouter(app, openAPIObject as unknown as OpenAPIObject /* required because of mixups in versioning betwee @ts-rest/open-api, @anatine/zop-openapi and openapi3-ts */);
// install the ts-rest router
installRouter(
app,
openAPIObject as unknown as OpenAPIObject /* required because of mixups in versioning betwee @ts-rest/open-api, @anatine/zop-openapi and openapi3-ts */
);
// handle every other GET route with index.html
app.get("*", function (_req, res, next) {
if (_req.method === "GET") {
res.sendFile(path.join(__dirname, "../client/index.html"));
} else {
next();
}
});
// handle every other GET route with index.html
app.get("*", function (_req, res, next) {
if (_req.method === "GET") {
res.sendFile(path.join(__dirname, "../client/index.html"));
} else {
next();
}
});
const port = parseInt(process.env.HTTP_PORT || "1111", 10);
const host = process.env.HTTP_HOST || "localhost";
app.listen(port, host, () => {
console.log(`Listening on http://${host}:${port}`);
});
const port = parseInt(process.env.HTTP_PORT || "1111", 10);
const host = process.env.HTTP_HOST || "localhost";
app.listen(port, host, () => {
console.log(`Listening on http://${host}:${port}`);
});
})
.catch(console.error);