카테고리 관련 DB 스키마/코드를 Kysely 기반으로 마이그레이션

This commit is contained in:
static
2025-01-21 10:57:32 +09:00
parent 698d2455ff
commit 2a2d01b50e
8 changed files with 281 additions and 187 deletions

View File

@@ -1,52 +1,27 @@
import {
sqliteTable,
text,
integer,
foreignKey,
type AnySQLiteColumn,
} from "drizzle-orm/sqlite-core";
import { mek } from "./mek";
import { user } from "./user";
import type { Generated } from "kysely";
import type { Ciphertext } from "./util";
const ciphertext = (name: string) =>
text(name, { mode: "json" }).$type<{
ciphertext: string; // Base64
iv: string; // Base64
}>();
interface CategoryTable {
id: Generated<number>;
parent_id: number | null;
user_id: number;
master_encryption_key_version: number;
encrypted_data_encryption_key: string; // Base64
data_encryption_key_version: Date;
encrypted_name: Ciphertext;
}
export const category = sqliteTable(
"category",
{
id: integer("id").primaryKey({ autoIncrement: true }),
parentId: integer("parent_id").references((): AnySQLiteColumn => category.id, {
onDelete: "cascade",
}),
userId: integer("user_id")
.notNull()
.references(() => user.id),
mekVersion: integer("master_encryption_key_version").notNull(),
encDek: text("encrypted_data_encryption_key").notNull().unique(), // Base64
dekVersion: integer("data_encryption_key_version", { mode: "timestamp_ms" }).notNull(),
encName: ciphertext("encrypted_name").notNull(),
},
(t) => ({
ref1: foreignKey({
columns: [t.parentId],
foreignColumns: [t.id],
}),
ref2: foreignKey({
columns: [t.userId, t.mekVersion],
foreignColumns: [mek.userId, mek.version],
}),
}),
);
interface CategoryLogTable {
id: Generated<number>;
category_id: number;
timestamp: Date;
action: "create" | "rename";
new_name: Ciphertext | null;
}
export const categoryLog = sqliteTable("category_log", {
id: integer("id").primaryKey({ autoIncrement: true }),
categoryId: integer("category_id")
.notNull()
.references(() => category.id, { onDelete: "cascade" }),
timestamp: integer("timestamp", { mode: "timestamp_ms" }).notNull(),
action: text("action", { enum: ["create", "rename"] }).notNull(),
newName: ciphertext("new_name"),
});
declare module "./index" {
interface Database {
category: CategoryTable;
category_log: CategoryLogTable;
}
}

View File

@@ -1,9 +1,5 @@
import type { ColumnType, Generated } from "kysely";
export type Ciphertext = {
ciphertext: string; // Base64
iv: string; // Base64
};
import type { Ciphertext } from "./util";
interface DirectoryTable {
id: Generated<number>;
@@ -45,26 +41,15 @@ interface FileLogTable {
id: Generated<number>;
file_id: number;
timestamp: ColumnType<Date, Date, never>;
action: "create" | "rename";
action: "create" | "rename" | "add-to-category" | "remove-from-category";
new_name: Ciphertext | null;
category_id: number | null;
}
export const fileCategory = sqliteTable(
"file_category",
{
fileId: integer("file_id")
.notNull()
.references(() => file.id, { onDelete: "cascade" }),
categoryId: integer("category_id")
.notNull()
.references(() => category.id, { onDelete: "cascade" }),
},
(t) => ({
pk: primaryKey({
columns: [t.fileId, t.categoryId],
}),
}),
);
interface FileCategoryTable {
file_id: number;
category_id: number;
}
declare module "./index" {
interface Database {
@@ -72,5 +57,6 @@ declare module "./index" {
directory_log: DirectoryLogTable;
file: FileTable;
file_log: FileLogTable;
file_category: FileCategoryTable;
}
}

View File

@@ -5,6 +5,7 @@ export * from "./hsk";
export * from "./mek";
export * from "./session";
export * from "./user";
export * from "./util";
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface Database {}

View File

@@ -0,0 +1,4 @@
export type Ciphertext = {
ciphertext: string; // Base64
iv: string; // Base64
};