mirror of
https://github.com/kmc7468/arkvault.git
synced 2026-02-04 08:06:56 +00:00
이전 버전에서 업로드된 파일을 청크 업로드 방식으로 마이그레이션할 수 있는 기능 추가
This commit is contained in:
@@ -9,6 +9,7 @@ type IntegrityErrorMessages =
|
||||
// File
|
||||
| "Directory not found"
|
||||
| "File not found"
|
||||
| "File is not legacy"
|
||||
| "File not found in category"
|
||||
| "File already added to category"
|
||||
| "Invalid DEK version"
|
||||
|
||||
@@ -334,6 +334,16 @@ export const getAllFileIds = async (userId: number) => {
|
||||
return files.map(({ id }) => id);
|
||||
};
|
||||
|
||||
export const getLegacyFileIds = async (userId: number) => {
|
||||
const files = await db
|
||||
.selectFrom("file")
|
||||
.select("id")
|
||||
.where("user_id", "=", userId)
|
||||
.where("encrypted_content_iv", "is not", null)
|
||||
.execute();
|
||||
return files.map(({ id }) => id);
|
||||
};
|
||||
|
||||
export const getAllFileIdsByContentHmac = async (
|
||||
userId: number,
|
||||
hskVersion: number,
|
||||
@@ -482,6 +492,52 @@ export const unregisterFile = async (userId: number, fileId: number) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const migrateFileContent = async (
|
||||
trx: typeof db,
|
||||
userId: number,
|
||||
fileId: number,
|
||||
newPath: string,
|
||||
encContentHash: string,
|
||||
) => {
|
||||
const file = await trx
|
||||
.selectFrom("file")
|
||||
.select(["path", "encrypted_content_iv"])
|
||||
.where("id", "=", fileId)
|
||||
.where("user_id", "=", userId)
|
||||
.limit(1)
|
||||
.forUpdate()
|
||||
.executeTakeFirst();
|
||||
|
||||
if (!file) {
|
||||
throw new IntegrityError("File not found");
|
||||
}
|
||||
if (!file.encrypted_content_iv) {
|
||||
throw new IntegrityError("File is not legacy");
|
||||
}
|
||||
|
||||
await trx
|
||||
.updateTable("file")
|
||||
.set({
|
||||
path: newPath,
|
||||
encrypted_content_iv: null,
|
||||
encrypted_content_hash: encContentHash,
|
||||
})
|
||||
.where("id", "=", fileId)
|
||||
.where("user_id", "=", userId)
|
||||
.execute();
|
||||
|
||||
await trx
|
||||
.insertInto("file_log")
|
||||
.values({
|
||||
file_id: fileId,
|
||||
timestamp: new Date(),
|
||||
action: "migrate",
|
||||
})
|
||||
.execute();
|
||||
|
||||
return file.path;
|
||||
};
|
||||
|
||||
export const addFileToCategory = async (fileId: number, categoryId: number) => {
|
||||
await db.transaction().execute(async (trx) => {
|
||||
try {
|
||||
|
||||
@@ -41,7 +41,7 @@ interface FileLogTable {
|
||||
id: Generated<number>;
|
||||
file_id: number;
|
||||
timestamp: ColumnType<Date, Date, never>;
|
||||
action: "create" | "rename" | "add-to-category" | "remove-from-category";
|
||||
action: "create" | "rename" | "migrate" | "add-to-category" | "remove-from-category";
|
||||
new_name: Ciphertext | null;
|
||||
category_id: number | null;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Ciphertext } from "./utils";
|
||||
|
||||
interface UploadSessionTable {
|
||||
id: string;
|
||||
type: "file" | "thumbnail";
|
||||
type: "file" | "thumbnail" | "migration";
|
||||
user_id: number;
|
||||
path: string;
|
||||
total_chunks: number;
|
||||
|
||||
@@ -31,6 +31,11 @@ interface ThumbnailUploadSession extends BaseUploadSession {
|
||||
dekVersion: Date;
|
||||
}
|
||||
|
||||
interface MigrationUploadSession extends BaseUploadSession {
|
||||
type: "migration";
|
||||
fileId: number;
|
||||
}
|
||||
|
||||
export const createFileUploadSession = async (
|
||||
params: Omit<FileUploadSession, "type" | "uploadedChunks">,
|
||||
) => {
|
||||
@@ -118,6 +123,39 @@ export const createThumbnailUploadSession = async (
|
||||
});
|
||||
};
|
||||
|
||||
export const createMigrationUploadSession = async (
|
||||
params: Omit<MigrationUploadSession, "type" | "uploadedChunks">,
|
||||
) => {
|
||||
await db.transaction().execute(async (trx) => {
|
||||
const file = await trx
|
||||
.selectFrom("file")
|
||||
.select("encrypted_content_iv")
|
||||
.where("id", "=", params.fileId)
|
||||
.where("user_id", "=", params.userId)
|
||||
.limit(1)
|
||||
.forUpdate()
|
||||
.executeTakeFirst();
|
||||
if (!file) {
|
||||
throw new IntegrityError("File not found");
|
||||
} else if (!file.encrypted_content_iv) {
|
||||
throw new IntegrityError("File is not legacy");
|
||||
}
|
||||
|
||||
await trx
|
||||
.insertInto("upload_session")
|
||||
.values({
|
||||
id: params.id,
|
||||
type: "migration",
|
||||
user_id: params.userId,
|
||||
path: params.path,
|
||||
total_chunks: params.totalChunks,
|
||||
expires_at: params.expiresAt,
|
||||
file_id: params.fileId,
|
||||
})
|
||||
.execute();
|
||||
});
|
||||
};
|
||||
|
||||
export const getUploadSession = async (sessionId: string, userId: number) => {
|
||||
const session = await db
|
||||
.selectFrom("upload_session")
|
||||
@@ -148,7 +186,7 @@ export const getUploadSession = async (sessionId: string, userId: number) => {
|
||||
encCreatedAt: session.encrypted_created_at,
|
||||
encLastModifiedAt: session.encrypted_last_modified_at!,
|
||||
} satisfies FileUploadSession;
|
||||
} else {
|
||||
} else if (session.type === "thumbnail") {
|
||||
return {
|
||||
type: "thumbnail",
|
||||
id: session.id,
|
||||
@@ -160,6 +198,17 @@ export const getUploadSession = async (sessionId: string, userId: number) => {
|
||||
fileId: session.file_id!,
|
||||
dekVersion: session.data_encryption_key_version!,
|
||||
} satisfies ThumbnailUploadSession;
|
||||
} else {
|
||||
return {
|
||||
type: "migration",
|
||||
id: session.id,
|
||||
userId: session.user_id,
|
||||
path: session.path,
|
||||
totalChunks: session.total_chunks,
|
||||
uploadedChunks: session.uploaded_chunks,
|
||||
expiresAt: session.expires_at,
|
||||
fileId: session.file_id!,
|
||||
} satisfies MigrationUploadSession;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user