diff --git a/src/controllers/02-user-controller.ts b/src/controllers/02-user-controller.ts index a697223..a6cbe44 100644 --- a/src/controllers/02-user-controller.ts +++ b/src/controllers/02-user-controller.ts @@ -29,6 +29,7 @@ import { removeUserRoles, } from "../services/keycloak"; import { isSystem } from "../utils/keycloak"; +import { fileLocation, listFile } from "../utils/minio"; if (!process.env.MINIO_BUCKET) { throw Error("Require MinIO bucket."); @@ -316,13 +317,7 @@ export class UserController extends Controller { if (!record) throw new HttpError(HttpStatus.NOT_FOUND, "User cannot be found.", "userNotFound"); - return Object.assign(record, { - profileImageUrl: await minio.presignedGetObject( - MINIO_BUCKET, - imageLocation(record.id), - 60 * 60, - ), - }); + return record; } @Post() @@ -463,18 +458,7 @@ export class UserController extends Controller { this.setStatus(HttpStatus.CREATED); - return Object.assign(record, { - profileImageUrl: await minio.presignedPutObject( - MINIO_BUCKET, - imageLocation(record.id), - 12 * 60 * 60, - ), - profileImageUploadUrl: await minio.presignedPutObject( - MINIO_BUCKET, - imageLocation(record.id), - 12 * 60 * 60, - ), - }); + return record; } @Put("{userId}") @@ -654,18 +638,7 @@ export class UserController extends Controller { } } - return Object.assign(record, { - profileImageUrl: await minio.presignedGetObject( - MINIO_BUCKET, - imageLocation(record.id), - 12 * 60 * 60, - ), - profileImageUploadUrl: await minio.presignedPutObject( - MINIO_BUCKET, - imageLocation(record.id), - 12 * 60 * 60, - ), - }); + return record; } @Delete("{userId}") @@ -815,6 +788,70 @@ function attachmentLocation(uid: string) { return `user-attachment/${uid}`; } +@Route("api/v1/user/{userId}/profile-image") +@Tags("User") +export class UserProfileController extends Controller { + @Get() + @Security("keycloak") + async listImage(@Path() userId: string) { + const record = await prisma.user.findFirst({ + where: { id: userId }, + }); + + if (!record) { + throw new HttpError(HttpStatus.NOT_FOUND, "User cannot be found.", "userNotFound"); + } + + return await listFile(fileLocation.user.profile(userId)); + } + + @Get("{name}") + async getImage(@Request() req: RequestWithUser, @Path() userId: string, @Path() name: string) { + const record = await prisma.user.findFirst({ + where: { id: userId }, + }); + + if (!record) { + throw new HttpError(HttpStatus.NOT_FOUND, "User cannot be found.", "userNotFound"); + } + + return req.res?.redirect( + await minio.presignedGetObject( + MINIO_BUCKET, + fileLocation.user.profile(userId, name), + 12 * 60 * 60, + ), + ); + } + + @Put("{name}") + @Security("keycloak") + async putImage(@Request() req: RequestWithUser, @Path() userId: string, @Path() name: string) { + const record = await prisma.user.findFirst({ + where: { id: userId }, + }); + + if (!record) { + throw new HttpError(HttpStatus.NOT_FOUND, "User cannot be found.", "userNotFound"); + } + + return req.res?.redirect( + await minio.presignedPutObject( + MINIO_BUCKET, + fileLocation.user.profile(userId, name), + 12 * 60 * 60, + ), + ); + } + + @Delete("{name}") + async deleteAttachment(@Path() userId: string, @Path() name: string) { + await minio.removeObject(MINIO_BUCKET, fileLocation.user.profile(userId, name), { + forceDelete: true, + }); + } +} + @Route("api/v1/user/{userId}/attachment") @Tags("User") @Security("keycloak") diff --git a/src/utils/minio.ts b/src/utils/minio.ts index ff999eb..00cef04 100644 --- a/src/utils/minio.ts +++ b/src/utils/minio.ts @@ -6,6 +6,22 @@ if (!process.env.MINIO_BUCKET) { const MINIO_BUCKET = process.env.MINIO_BUCKET; +export async function listFile(path: string) { + return await new Promise((resolve, reject) => { + const item: string[] = []; + + const stream = minio.listObjectsV2(MINIO_BUCKET, path); + + stream.on("data", (v) => v && v.name && item.push(v.name)); + stream.on("end", () => resolve(item)); + stream.on("error", () => reject(new Error("MinIO error."))); + }); +} + +export async function deleteFile(path: string) { + await minio.removeObject(MINIO_BUCKET, path, { forceDelete: true }); +} + export async function deleteFolder(path: string) { new Promise((resolve, reject) => { const item: string[] = []; @@ -29,4 +45,7 @@ export const fileLocation = { map: (branchId: string) => `branch/map-img-${branchId}`, bank: (branchId: string, bankId: string) => `branch/bank-qr-${branchId}-${bankId}`, }, + user: { + profile: (userId: string, name?: string) => `user/profile-image-${userId}/${name || ""}`, + }, };