Prepare for DB migartions
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { Database as BunSqliteDatabase } from "bun:sqlite";
|
import { Database as BunSqliteDatabase } from "bun:sqlite";
|
||||||
import { AttachmentId, PieceId, RepertoireId, RequestId, SessionId, Sha256_Bin, UserId } from "common";
|
import { AttachmentId, PieceId, RepertoireId, RequestId, SessionId, Sha256_Bin, UserId } from "common";
|
||||||
|
import { HashSet } from "effect";
|
||||||
import { ColumnType, CompiledQuery, CreateTableBuilder, Kysely, Selectable } from "kysely";
|
import { ColumnType, CompiledQuery, CreateTableBuilder, Kysely, Selectable } from "kysely";
|
||||||
import { BunSqliteDialect } from "kysely-bun-sqlite";
|
import { BunSqliteDialect } from "kysely-bun-sqlite";
|
||||||
|
|
||||||
@@ -15,9 +16,11 @@ export interface Database {
|
|||||||
Attachment: AttachmentTable;
|
Attachment: AttachmentTable;
|
||||||
File: FileTable;
|
File: FileTable;
|
||||||
Piece: PieceTable;
|
Piece: PieceTable;
|
||||||
|
Option: OptionTable;
|
||||||
Repertoire: RepertoireTable;
|
Repertoire: RepertoireTable;
|
||||||
RepertoireEntry: RepertoireEntryTable;
|
RepertoireEntry: RepertoireEntryTable;
|
||||||
Session: SessionTable;
|
Session: SessionTable;
|
||||||
|
sqlite_schema: SqliteSchemaTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SystemInformation {
|
export interface SystemInformation {
|
||||||
@@ -52,6 +55,11 @@ export interface FileTable {
|
|||||||
data: ColumnType<Uint8Array, Uint8Array, never>;
|
data: ColumnType<Uint8Array, Uint8Array, never>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OptionTable {
|
||||||
|
key: ColumnType<string, string, never>;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PieceData {
|
export interface PieceData {
|
||||||
name: string;
|
name: string;
|
||||||
composer: string | null;
|
composer: string | null;
|
||||||
@@ -91,13 +99,23 @@ export interface SessionTable extends SessionData {
|
|||||||
expiresAt: string;
|
expiresAt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SqliteSchemaTable {
|
||||||
|
type: ColumnType<string, never, never>;
|
||||||
|
name: ColumnType<string, never, never>;
|
||||||
|
tbl_name: ColumnType<string, never, never>;
|
||||||
|
rootpage: ColumnType<number, never, never>;
|
||||||
|
sql: ColumnType<string | null, never, never>;
|
||||||
|
}
|
||||||
|
|
||||||
export type AccessLog = Selectable<AccessLogTable>;
|
export type AccessLog = Selectable<AccessLogTable>;
|
||||||
export type Attachment = Selectable<AttachmentTable>;
|
export type Attachment = Selectable<AttachmentTable>;
|
||||||
export type File = Selectable<FileTable>;
|
export type File = Selectable<FileTable>;
|
||||||
|
export type Option = Selectable<OptionTable>;
|
||||||
export type Piece = Selectable<PieceTable>;
|
export type Piece = Selectable<PieceTable>;
|
||||||
export type Repertoire = Selectable<RepertoireTable>;
|
export type Repertoire = Selectable<RepertoireTable>;
|
||||||
export type RepertoireEntry = Selectable<RepertoireEntryTable>;
|
export type RepertoireEntry = Selectable<RepertoireEntryTable>;
|
||||||
export type Session = Selectable<SessionTable>;
|
export type Session = Selectable<SessionTable>;
|
||||||
|
export type SqliteSchema = Selectable<SqliteSchemaTable>;
|
||||||
|
|
||||||
function systemInformation<TB extends string, C extends string>(schema: CreateTableBuilder<TB, C>) {
|
function systemInformation<TB extends string, C extends string>(schema: CreateTableBuilder<TB, C>) {
|
||||||
return schema
|
return schema
|
||||||
@@ -115,112 +133,133 @@ export async function initDatabase(filename: string = "db.sqlite3"): Promise<Kys
|
|||||||
|
|
||||||
await db.executeQuery(CompiledQuery.raw("PRAGMA foreign_keys = ON"));
|
await db.executeQuery(CompiledQuery.raw("PRAGMA foreign_keys = ON"));
|
||||||
|
|
||||||
await db.schema
|
const tables = await db
|
||||||
.createTable("AccessLog")
|
.selectFrom("sqlite_schema")
|
||||||
.ifNotExists()
|
.select("name")
|
||||||
.addColumn("requestId", "text", (c) => c.notNull().primaryKey())
|
.where("type", "=", "table")
|
||||||
.addColumn("timestamp", "text", (c) => c.notNull())
|
.execute()
|
||||||
.addColumn("method", "text", (c) => c.notNull())
|
.then((tables) => HashSet.make(...tables.map(({ name }) => name)));
|
||||||
.addColumn("pathname", "text", (c) => c.notNull())
|
|
||||||
.addColumn("query", "text", (c) => c.notNull())
|
|
||||||
.addColumn("ip", "text")
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
await db.schema
|
if (!HashSet.has(tables, "Option")) {
|
||||||
.createIndex("AccessLog_timestamp")
|
await db.schema
|
||||||
.ifNotExists()
|
.createTable("AccessLog")
|
||||||
.on("AccessLog")
|
.ifNotExists()
|
||||||
.column("timestamp")
|
.addColumn("requestId", "text", (c) => c.notNull().primaryKey())
|
||||||
.execute();
|
.addColumn("timestamp", "text", (c) => c.notNull())
|
||||||
|
.addColumn("method", "text", (c) => c.notNull())
|
||||||
|
.addColumn("pathname", "text", (c) => c.notNull())
|
||||||
|
.addColumn("query", "text", (c) => c.notNull())
|
||||||
|
.addColumn("ip", "text")
|
||||||
|
.execute();
|
||||||
|
|
||||||
await db.schema
|
await db.schema
|
||||||
.createTable("File")
|
.createIndex("AccessLog_timestamp")
|
||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
.addColumn("sha256", "blob", (c) => c.notNull().primaryKey())
|
.on("AccessLog")
|
||||||
.addColumn("data", "blob", (c) => c.notNull())
|
.column("timestamp")
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
await db.schema
|
await db.schema
|
||||||
.createTable("User")
|
.createTable("File")
|
||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
.addColumn("userId", "text", (c) => c.notNull().primaryKey())
|
.addColumn("sha256", "blob", (c) => c.notNull().primaryKey())
|
||||||
.addColumn("username", "text", (c) => c.notNull().unique())
|
.addColumn("data", "blob", (c) => c.notNull())
|
||||||
.addColumn("password", "text", (c) => c.notNull())
|
.execute();
|
||||||
.addColumn("admin", "boolean", (c) => c.notNull())
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
await db.schema
|
await db.schema
|
||||||
.createTable("Piece")
|
.createTable("User")
|
||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
.addColumn("pieceId", "text", (c) => c.notNull().primaryKey())
|
.addColumn("userId", "text", (c) => c.notNull().primaryKey())
|
||||||
.addColumn("name", "text", (c) => c.notNull())
|
.addColumn("username", "text", (c) => c.notNull().unique())
|
||||||
.addColumn("composer", "text")
|
.addColumn("password", "text", (c) => c.notNull())
|
||||||
.addColumn("lyricist", "text")
|
.addColumn("admin", "boolean", (c) => c.notNull())
|
||||||
.addColumn("arranger", "text")
|
.execute();
|
||||||
.$call(systemInformation)
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
await db.schema
|
await db.schema
|
||||||
.createIndex("Piece_name_composer_arranger")
|
.createTable("Piece")
|
||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
.on("Piece")
|
.addColumn("pieceId", "text", (c) => c.notNull().primaryKey())
|
||||||
.columns(["name", "composer", "arranger"])
|
.addColumn("name", "text", (c) => c.notNull())
|
||||||
.execute();
|
.addColumn("composer", "text")
|
||||||
|
.addColumn("lyricist", "text")
|
||||||
|
.addColumn("arranger", "text")
|
||||||
|
.$call(systemInformation)
|
||||||
|
.execute();
|
||||||
|
|
||||||
await db.schema
|
await db.schema
|
||||||
.createTable("Repertoire")
|
.createIndex("Piece_name_composer_arranger")
|
||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
.addColumn("repertoireId", "text", (c) => c.notNull().primaryKey())
|
.on("Piece")
|
||||||
.addColumn("name", "text", (c) => c.notNull())
|
.columns(["name", "composer", "arranger"])
|
||||||
.$call(systemInformation)
|
.execute();
|
||||||
.execute();
|
|
||||||
|
|
||||||
await db.schema
|
await db.schema
|
||||||
.createIndex("Repertoire_name")
|
.createTable("Repertoire")
|
||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
.on("Repertoire")
|
.addColumn("repertoireId", "text", (c) => c.notNull().primaryKey())
|
||||||
.column("name")
|
.addColumn("name", "text", (c) => c.notNull())
|
||||||
.execute();
|
.$call(systemInformation)
|
||||||
|
.execute();
|
||||||
|
|
||||||
await db.schema
|
await db.schema
|
||||||
.createTable("RepertoireEntry")
|
.createIndex("Repertoire_name")
|
||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
.addColumn("repertoireId", "text", (c) => c.notNull().references("Repertoire.repertoireId").onDelete("cascade").onUpdate("cascade"))
|
.on("Repertoire")
|
||||||
.addColumn("order", "integer", (c) => c.notNull())
|
.column("name")
|
||||||
.addColumn("pieceId", "text", (c) => c.notNull().references("Piece.pieceId").onDelete("restrict").onUpdate("restrict"))
|
.execute();
|
||||||
.addPrimaryKeyConstraint("pk_RepertoireEntry", ["repertoireId", "order"])
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
await db.schema
|
await db.schema
|
||||||
.createTable("Session")
|
.createTable("RepertoireEntry")
|
||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
.addColumn("sessionId", "text", (c) => c.notNull().primaryKey())
|
.addColumn("repertoireId", "text", (c) => c.notNull().references("Repertoire.repertoireId").onDelete("cascade").onUpdate("cascade"))
|
||||||
.addColumn("state", "text")
|
.addColumn("order", "integer", (c) => c.notNull())
|
||||||
.addColumn("codeVerifier", "text")
|
.addColumn("pieceId", "text", (c) => c.notNull().references("Piece.pieceId").onDelete("restrict").onUpdate("restrict"))
|
||||||
.addColumn("accessToken", "text")
|
.addPrimaryKeyConstraint("pk_RepertoireEntry", ["repertoireId", "order"])
|
||||||
.addColumn("idToken", "text")
|
.execute();
|
||||||
.addColumn("refreshToken", "text")
|
|
||||||
.addColumn("external", "boolean")
|
|
||||||
.addColumn("expiresAt", "text", (c) => c.notNull())
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
await db.schema
|
await db.schema
|
||||||
.createTable("Attachment")
|
.createTable("Session")
|
||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
.addColumn("attachmentId", "text", (c) => c.notNull().primaryKey())
|
.addColumn("sessionId", "text", (c) => c.notNull().primaryKey())
|
||||||
.addColumn("pieceId", "text", (c) => c.notNull().references("Piece.pieceId").onDelete("cascade").onUpdate("cascade"))
|
.addColumn("state", "text")
|
||||||
.addColumn("sha256", "blob", (c) => c.notNull().references("File.sha256").onDelete("restrict").onUpdate("restrict"))
|
.addColumn("codeVerifier", "text")
|
||||||
.addColumn("filename", "text", (c) => c.notNull())
|
.addColumn("accessToken", "text")
|
||||||
.addColumn("mediaType", "text", (c) => c.notNull())
|
.addColumn("idToken", "text")
|
||||||
.$call(systemInformation)
|
.addColumn("refreshToken", "text")
|
||||||
.execute();
|
.addColumn("external", "boolean")
|
||||||
|
.addColumn("expiresAt", "text", (c) => c.notNull())
|
||||||
|
.execute();
|
||||||
|
|
||||||
await db.schema
|
await db.schema
|
||||||
.createIndex("Attachment_pieceId_filename")
|
.createTable("Attachment")
|
||||||
.ifNotExists()
|
.ifNotExists()
|
||||||
.on("Attachment")
|
.addColumn("attachmentId", "text", (c) => c.notNull().primaryKey())
|
||||||
.columns(["pieceId", "filename"])
|
.addColumn("pieceId", "text", (c) => c.notNull().references("Piece.pieceId").onDelete("cascade").onUpdate("cascade"))
|
||||||
.execute();
|
.addColumn("sha256", "blob", (c) => c.notNull().references("File.sha256").onDelete("restrict").onUpdate("restrict"))
|
||||||
|
.addColumn("filename", "text", (c) => c.notNull())
|
||||||
|
.addColumn("mediaType", "text", (c) => c.notNull())
|
||||||
|
.$call(systemInformation)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createIndex("Attachment_pieceId_filename")
|
||||||
|
.ifNotExists()
|
||||||
|
.on("Attachment")
|
||||||
|
.columns(["pieceId", "filename"])
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db.schema
|
||||||
|
.createTable("Option")
|
||||||
|
.ifNotExists()
|
||||||
|
.addColumn("key", "text", (c) => c.notNull().primaryKey())
|
||||||
|
.addColumn("value", "text", (c) => c.notNull())
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
await db
|
||||||
|
.insertInto("Option")
|
||||||
|
.values({ key: "database_version", value: "0" })
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user