mirror of
https://github.com/kmc7468/arkvault.git
synced 2025-12-16 06:58:46 +00:00
파일/디렉터리 생성/이름 변경시 로그를 남기도록 변경
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
import { and, eq, isNull } from "drizzle-orm";
|
import { and, eq, isNull } from "drizzle-orm";
|
||||||
import db from "./drizzle";
|
import db from "./drizzle";
|
||||||
import { IntegrityError } from "./error";
|
import { IntegrityError } from "./error";
|
||||||
import { directory, file, mek } from "./schema";
|
import { directory, directoryLog, file, fileLog, mek } from "./schema";
|
||||||
|
|
||||||
type DirectoryId = "root" | number;
|
type DirectoryId = "root" | number;
|
||||||
|
|
||||||
export interface NewDirectoryParams {
|
export interface NewDirectoryParams {
|
||||||
userId: number;
|
|
||||||
parentId: DirectoryId;
|
parentId: DirectoryId;
|
||||||
|
userId: number;
|
||||||
mekVersion: number;
|
mekVersion: number;
|
||||||
encDek: string;
|
encDek: string;
|
||||||
dekVersion: Date;
|
dekVersion: Date;
|
||||||
@@ -16,9 +16,9 @@ export interface NewDirectoryParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface NewFileParams {
|
export interface NewFileParams {
|
||||||
path: string;
|
|
||||||
parentId: DirectoryId;
|
parentId: DirectoryId;
|
||||||
userId: number;
|
userId: number;
|
||||||
|
path: string;
|
||||||
mekVersion: number;
|
mekVersion: number;
|
||||||
encDek: string;
|
encDek: string;
|
||||||
dekVersion: Date;
|
dekVersion: Date;
|
||||||
@@ -40,14 +40,23 @@ export const registerDirectory = async (params: NewDirectoryParams) => {
|
|||||||
throw new IntegrityError("Inactive MEK version");
|
throw new IntegrityError("Inactive MEK version");
|
||||||
}
|
}
|
||||||
|
|
||||||
await tx.insert(directory).values({
|
const newDirectories = await tx
|
||||||
createdAt: new Date(),
|
.insert(directory)
|
||||||
parentId: params.parentId === "root" ? null : params.parentId,
|
.values({
|
||||||
userId: params.userId,
|
parentId: params.parentId === "root" ? null : params.parentId,
|
||||||
mekVersion: params.mekVersion,
|
userId: params.userId,
|
||||||
encDek: params.encDek,
|
mekVersion: params.mekVersion,
|
||||||
dekVersion: params.dekVersion,
|
encDek: params.encDek,
|
||||||
encName: { ciphertext: params.encName, iv: params.encNameIv },
|
dekVersion: params.dekVersion,
|
||||||
|
encName: { ciphertext: params.encName, iv: params.encNameIv },
|
||||||
|
})
|
||||||
|
.returning({ id: directory.id });
|
||||||
|
const { id: directoryId } = newDirectories[0]!;
|
||||||
|
await tx.insert(directoryLog).values({
|
||||||
|
directoryId,
|
||||||
|
timestamp: new Date(),
|
||||||
|
action: "create",
|
||||||
|
newName: { ciphertext: params.encName, iv: params.encNameIv },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ behavior: "exclusive" },
|
{ behavior: "exclusive" },
|
||||||
@@ -99,6 +108,12 @@ export const setDirectoryEncName = async (
|
|||||||
.update(directory)
|
.update(directory)
|
||||||
.set({ encName: { ciphertext: encName, iv: encNameIv } })
|
.set({ encName: { ciphertext: encName, iv: encNameIv } })
|
||||||
.where(and(eq(directory.userId, userId), eq(directory.id, directoryId)));
|
.where(and(eq(directory.userId, userId), eq(directory.id, directoryId)));
|
||||||
|
await tx.insert(directoryLog).values({
|
||||||
|
directoryId,
|
||||||
|
timestamp: new Date(),
|
||||||
|
action: "rename",
|
||||||
|
newName: { ciphertext: encName, iv: encNameIv },
|
||||||
|
});
|
||||||
},
|
},
|
||||||
{ behavior: "exclusive" },
|
{ behavior: "exclusive" },
|
||||||
);
|
);
|
||||||
@@ -148,17 +163,26 @@ export const registerFile = async (params: NewFileParams) => {
|
|||||||
throw new IntegrityError("Inactive MEK version");
|
throw new IntegrityError("Inactive MEK version");
|
||||||
}
|
}
|
||||||
|
|
||||||
await tx.insert(file).values({
|
const newFiles = await tx
|
||||||
path: params.path,
|
.insert(file)
|
||||||
parentId: params.parentId === "root" ? null : params.parentId,
|
.values({
|
||||||
createdAt: new Date(),
|
path: params.path,
|
||||||
userId: params.userId,
|
parentId: params.parentId === "root" ? null : params.parentId,
|
||||||
mekVersion: params.mekVersion,
|
userId: params.userId,
|
||||||
contentType: params.contentType,
|
mekVersion: params.mekVersion,
|
||||||
encDek: params.encDek,
|
contentType: params.contentType,
|
||||||
dekVersion: params.dekVersion,
|
encDek: params.encDek,
|
||||||
encContentIv: params.encContentIv,
|
dekVersion: params.dekVersion,
|
||||||
encName: { ciphertext: params.encName, iv: params.encNameIv },
|
encContentIv: params.encContentIv,
|
||||||
|
encName: { ciphertext: params.encName, iv: params.encNameIv },
|
||||||
|
})
|
||||||
|
.returning({ id: file.id });
|
||||||
|
const { id: fileId } = newFiles[0]!;
|
||||||
|
await tx.insert(fileLog).values({
|
||||||
|
fileId,
|
||||||
|
timestamp: new Date(),
|
||||||
|
action: "create",
|
||||||
|
newName: { ciphertext: params.encName, iv: params.encNameIv },
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ behavior: "exclusive" },
|
{ behavior: "exclusive" },
|
||||||
@@ -210,6 +234,12 @@ export const setFileEncName = async (
|
|||||||
.update(file)
|
.update(file)
|
||||||
.set({ encName: { ciphertext: encName, iv: encNameIv } })
|
.set({ encName: { ciphertext: encName, iv: encNameIv } })
|
||||||
.where(and(eq(file.userId, userId), eq(file.id, fileId)));
|
.where(and(eq(file.userId, userId), eq(file.id, fileId)));
|
||||||
|
await tx.insert(fileLog).values({
|
||||||
|
fileId,
|
||||||
|
timestamp: new Date(),
|
||||||
|
action: "rename",
|
||||||
|
newName: { ciphertext: encName, iv: encNameIv },
|
||||||
|
});
|
||||||
},
|
},
|
||||||
{ behavior: "exclusive" },
|
{ behavior: "exclusive" },
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export const directory = sqliteTable(
|
|||||||
"directory",
|
"directory",
|
||||||
{
|
{
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
||||||
parentId: integer("parent_id"),
|
parentId: integer("parent_id"),
|
||||||
userId: integer("user_id")
|
userId: integer("user_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
@@ -34,16 +33,25 @@ export const directory = sqliteTable(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const directoryLog = sqliteTable("directory_log", {
|
||||||
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
|
directoryId: integer("directory_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => directory.id, { onDelete: "cascade" }),
|
||||||
|
timestamp: integer("timestamp", { mode: "timestamp_ms" }).notNull(),
|
||||||
|
action: text("action", { enum: ["create", "rename"] }).notNull(),
|
||||||
|
newName: ciphertext("new_name"),
|
||||||
|
});
|
||||||
|
|
||||||
export const file = sqliteTable(
|
export const file = sqliteTable(
|
||||||
"file",
|
"file",
|
||||||
{
|
{
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
path: text("path").notNull().unique(),
|
|
||||||
parentId: integer("parent_id").references(() => directory.id),
|
parentId: integer("parent_id").references(() => directory.id),
|
||||||
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
||||||
userId: integer("user_id")
|
userId: integer("user_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => user.id),
|
.references(() => user.id),
|
||||||
|
path: text("path").notNull().unique(),
|
||||||
mekVersion: integer("master_encryption_key_version").notNull(),
|
mekVersion: integer("master_encryption_key_version").notNull(),
|
||||||
encDek: text("encrypted_data_encryption_key").notNull().unique(), // Base64
|
encDek: text("encrypted_data_encryption_key").notNull().unique(), // Base64
|
||||||
dekVersion: integer("data_encryption_key_version", { mode: "timestamp_ms" }).notNull(),
|
dekVersion: integer("data_encryption_key_version", { mode: "timestamp_ms" }).notNull(),
|
||||||
@@ -58,3 +66,13 @@ export const file = sqliteTable(
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const fileLog = sqliteTable("file_log", {
|
||||||
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||||
|
fileId: integer("file_id")
|
||||||
|
.notNull()
|
||||||
|
.references(() => file.id, { onDelete: "cascade" }),
|
||||||
|
timestamp: integer("timestamp", { mode: "timestamp_ms" }).notNull(),
|
||||||
|
action: text("action", { enum: ["create", "rename"] }).notNull(),
|
||||||
|
newName: ciphertext("new_name"),
|
||||||
|
});
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { z } from "zod";
|
|||||||
export const directoryInfoResponse = z.object({
|
export const directoryInfoResponse = z.object({
|
||||||
metadata: z
|
metadata: z
|
||||||
.object({
|
.object({
|
||||||
createdAt: z.string().datetime(),
|
|
||||||
mekVersion: z.number().int().positive(),
|
mekVersion: z.number().int().positive(),
|
||||||
dek: z.string().base64().nonempty(),
|
dek: z.string().base64().nonempty(),
|
||||||
dekVersion: z.string().datetime(),
|
dekVersion: z.string().datetime(),
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import mime from "mime";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
export const fileInfoResponse = z.object({
|
export const fileInfoResponse = z.object({
|
||||||
createdAt: z.string().datetime(),
|
|
||||||
mekVersion: z.number().int().positive(),
|
mekVersion: z.number().int().positive(),
|
||||||
dek: z.string().base64().nonempty(),
|
dek: z.string().base64().nonempty(),
|
||||||
dekVersion: z.string().datetime(),
|
dekVersion: z.string().datetime(),
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export const getDirectoryInformation = async (userId: number, directoryId: "root
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
metadata: directory && {
|
metadata: directory && {
|
||||||
createdAt: directory.createdAt,
|
|
||||||
mekVersion: directory.mekVersion,
|
mekVersion: directory.mekVersion,
|
||||||
encDek: directory.encDek,
|
encDek: directory.encDek,
|
||||||
dekVersion: directory.dekVersion,
|
dekVersion: directory.dekVersion,
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ export const getFileInformation = async (userId: number, fileId: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
createdAt: file.createdAt,
|
|
||||||
mekVersion: file.mekVersion,
|
mekVersion: file.mekVersion,
|
||||||
encDek: file.encDek,
|
encDek: file.encDek,
|
||||||
dekVersion: file.dekVersion,
|
dekVersion: file.dekVersion,
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export const GET: RequestHandler = async ({ locals, params }) => {
|
|||||||
return json(
|
return json(
|
||||||
directoryInfoResponse.parse({
|
directoryInfoResponse.parse({
|
||||||
metadata: metadata && {
|
metadata: metadata && {
|
||||||
createdAt: metadata.createdAt.toISOString(),
|
|
||||||
mekVersion: metadata.mekVersion,
|
mekVersion: metadata.mekVersion,
|
||||||
dek: metadata.encDek,
|
dek: metadata.encDek,
|
||||||
dekVersion: metadata.dekVersion.toISOString(),
|
dekVersion: metadata.dekVersion.toISOString(),
|
||||||
|
|||||||
@@ -16,11 +16,10 @@ export const GET: RequestHandler = async ({ locals, params }) => {
|
|||||||
if (!zodRes.success) error(400, "Invalid path parameters");
|
if (!zodRes.success) error(400, "Invalid path parameters");
|
||||||
const { id } = zodRes.data;
|
const { id } = zodRes.data;
|
||||||
|
|
||||||
const { createdAt, mekVersion, encDek, dekVersion, contentType, encContentIv, encName } =
|
const { mekVersion, encDek, dekVersion, contentType, encContentIv, encName } =
|
||||||
await getFileInformation(userId, id);
|
await getFileInformation(userId, id);
|
||||||
return json(
|
return json(
|
||||||
fileInfoResponse.parse({
|
fileInfoResponse.parse({
|
||||||
createdAt: createdAt.toISOString(),
|
|
||||||
mekVersion,
|
mekVersion,
|
||||||
dek: encDek,
|
dek: encDek,
|
||||||
dekVersion: dekVersion.toISOString(),
|
dekVersion: dekVersion.toISOString(),
|
||||||
|
|||||||
Reference in New Issue
Block a user