feat: file upload customer branch

This commit is contained in:
Methapon Metanipat 2024-09-16 17:20:51 +07:00
parent 3a8bb7a82d
commit 472db99c50
2 changed files with 313 additions and 115 deletions

View file

@ -17,7 +17,6 @@ import { RequestWithUser } from "../interfaces/user";
import prisma from "../db"; import prisma from "../db";
import HttpStatus from "../interfaces/http-status"; import HttpStatus from "../interfaces/http-status";
import HttpError from "../interfaces/http-error"; import HttpError from "../interfaces/http-error";
import minio, { deleteFolder } from "../services/minio";
import { isSystem } from "../utils/keycloak"; import { isSystem } from "../utils/keycloak";
import { import {
branchRelationPermInclude, branchRelationPermInclude,
@ -27,12 +26,8 @@ import {
import { filterStatus } from "../services/prisma"; import { filterStatus } from "../services/prisma";
import { connectOrDisconnect, connectOrNot, whereAddressQuery } from "../utils/relation"; import { connectOrDisconnect, connectOrNot, whereAddressQuery } from "../utils/relation";
import { notFoundError, relationError } from "../utils/error"; import { notFoundError, relationError } from "../utils/error";
import { deleteFile, deleteFolder, fileLocation, getFile, listFile, setFile } from "../utils/minio";
if (!process.env.MINIO_BUCKET) {
throw Error("Require MinIO bucket.");
}
const MINIO_BUCKET = process.env.MINIO_BUCKET;
const MANAGE_ROLES = [ const MANAGE_ROLES = [
"system", "system",
"head_of_admin", "head_of_admin",
@ -51,14 +46,6 @@ function globalAllow(user: RequestWithUser["user"]) {
const permissionCond = createPermCondition(globalAllow); const permissionCond = createPermCondition(globalAllow);
const permissionCheck = createPermCheck(globalAllow); const permissionCheck = createPermCheck(globalAllow);
function imageLocation(id: string) {
return `employee/profile-img-${id}`;
}
function attachmentLocation(customerId: string, branchId: string) {
return `customer/${customerId}/branch/${branchId}`;
}
export type CustomerBranchCreate = { export type CustomerBranchCreate = {
customerId: string; customerId: string;
@ -299,16 +286,7 @@ export class CustomerBranchController extends Controller {
]); ]);
return { return {
result: await Promise.all( result,
result.map(async (v) => ({
...v,
profileImageUrl: await minio.presignedGetObject(
MINIO_BUCKET,
imageLocation(v.id),
12 * 60 * 60,
),
})),
),
page, page,
pageSize, pageSize,
total, total,
@ -514,117 +492,323 @@ export class CustomerBranchController extends Controller {
include: { createdBy: true, updatedBy: true }, include: { createdBy: true, updatedBy: true },
where: { id: branchId }, where: { id: branchId },
}) })
.then((v) => { .then((v) => deleteFolder(fileLocation.customerBranch.attachment(branchId)).then(() => v));
deleteFolder(MINIO_BUCKET, `${attachmentLocation(record.customerId, branchId)}/`);
return v;
});
} }
} }
@Route("api/v1/customer-branch/{branchId}/attachment") // @Route("api/v1/customer-branch/{branchId}/attachment")
// @Tags("Customer Branch")
// @Security("keycloak")
// export class CustomerAttachmentController extends Controller {
// @Get()
// async listAttachment(@Path() branchId: string) {
// const record = await prisma.customerBranch.findFirst({
// where: { id: branchId },
// });
//
// if (!record) {
// throw new HttpError(
// HttpStatus.NOT_FOUND,
// "Customer branch cannot be found.",
// "customerBranchNotFound",
// );
// }
//
// const list = await new Promise<string[]>((resolve, reject) => {
// const item: string[] = [];
//
// const stream = minio.listObjectsV2(
// MINIO_BUCKET,
// `${attachmentLocation(record.customerId, branchId)}/`,
// );
//
// stream.on("data", (v) => v && v.name && item.push(v.name));
// stream.on("end", () => resolve(item));
// stream.on("error", () => reject(new Error("MinIO error.")));
// });
//
// return list.map((v) => v.split("/").at(-1) as string);
// }
//
// @Get("{filename}")
// async getAttachment(
// @Request() req: RequestWithUser,
// @Path() branchId: string,
// @Path() filename: string,
// ) {
// const record = await prisma.customerBranch.findFirst({
// where: { id: branchId },
// });
//
// if (!record) {
// throw new HttpError(
// HttpStatus.NOT_FOUND,
// "Customer branch cannot be found.",
// "customerBranchNotFound",
// );
// }
//
// return await minio.presignedGetObject(
// MINIO_BUCKET,
// `${attachmentLocation(record.customerId, branchId)}/${filename}`,
// 12 * 60 * 60,
// );
// }
//
// @Put("{filename}")
// async addAttachment(
// @Request() req: RequestWithUser,
// @Path() branchId: string,
// @Path() filename: string,
// ) {
// const record = await prisma.customerBranch.findFirst({
// where: { id: branchId },
// });
//
// if (!record) {
// throw new HttpError(
// HttpStatus.NOT_FOUND,
// "Customer branch cannot be found.",
// "customerBranchNotFound",
// );
// }
//
// return req.res?.redirect(
// await minio.presignedPutObject(
// MINIO_BUCKET,
// `${attachmentLocation(record.customerId, branchId)}/${filename}`,
// 12 * 60 * 60,
// ),
// );
// }
//
// @Delete("{filename}")
// async deleteAttachment(@Path() branchId: string, @Path() filename: string) {
// const record = await prisma.customerBranch.findFirst({
// where: { id: branchId },
// });
//
// if (!record) throw notFoundError("Customer Branch");
//
// await minio.removeObject(
// MINIO_BUCKET,
// `${attachmentLocation(record.customerId, branchId)}/${filename}`,
// { forceDelete: true },
// );
// }
// }
@Route("api/v1/customer-branch/{branchId}")
@Tags("Customer Branch") @Tags("Customer Branch")
@Security("keycloak") export class CustomerBranchFileController extends Controller {
export class CustomerAttachmentController extends Controller { private async checkPermission(user: RequestWithUser["user"], id: string) {
@Get() const data = await prisma.customerBranch.findFirst({
async listAttachment(@Path() branchId: string) { where: { id },
const record = await prisma.customerBranch.findFirst({ include: {
where: { id: branchId }, customer: {
include: {
registeredBranch: {
include: branchRelationPermInclude(user),
},
},
},
},
}); });
if (!data) throw notFoundError("Customer Branch");
if (!record) { await permissionCheck(user, data.customer.registeredBranch);
throw new HttpError(
HttpStatus.NOT_FOUND,
"Customer branch cannot be found.",
"customerBranchNotFound",
);
}
const list = await new Promise<string[]>((resolve, reject) => {
const item: string[] = [];
const stream = minio.listObjectsV2(
MINIO_BUCKET,
`${attachmentLocation(record.customerId, branchId)}/`,
);
stream.on("data", (v) => v && v.name && item.push(v.name));
stream.on("end", () => resolve(item));
stream.on("error", () => reject(new Error("MinIO error.")));
});
return list.map((v) => v.split("/").at(-1) as string);
} }
@Get("{filename}") @Get("attachment")
async getAttachment( @Security("keycloak")
@Request() req: RequestWithUser, async listAttachment(@Request() req: RequestWithUser, @Path() branchId: string) {
@Path() branchId: string, await this.checkPermission(req.user, branchId);
@Path() filename: string, return await listFile(fileLocation.customerBranch.attachment(branchId));
) {
const record = await prisma.customerBranch.findFirst({
where: { id: branchId },
});
if (!record) {
throw new HttpError(
HttpStatus.NOT_FOUND,
"Customer branch cannot be found.",
"customerBranchNotFound",
);
}
return await minio.presignedGetObject(
MINIO_BUCKET,
`${attachmentLocation(record.customerId, branchId)}/${filename}`,
12 * 60 * 60,
);
} }
@Put("{filename}") @Get("attachment/{name}")
async addAttachment( @Security("keycloak")
async getAttachment(@Path() branchId: string, @Path() name: string) {
return await getFile(fileLocation.customerBranch.attachment(branchId, name));
}
@Put("attachment/{name}")
@Security("keycloak")
async putAttachment(
@Request() req: RequestWithUser, @Request() req: RequestWithUser,
@Path() branchId: string, @Path() branchId: string,
@Path() filename: string, @Path() name: string,
) { ) {
const record = await prisma.customerBranch.findFirst({ await this.checkPermission(req.user, branchId);
where: { id: branchId }, return req.res?.redirect(await setFile(fileLocation.customerBranch.attachment(branchId, name)));
}); }
if (!record) { @Delete("attachment/{name}")
throw new HttpError( @Security("keycloak")
HttpStatus.NOT_FOUND, async delAttachment(
"Customer branch cannot be found.", @Request() req: RequestWithUser,
"customerBranchNotFound", @Path() branchId: string,
); @Path() name: string,
} ) {
await this.checkPermission(req.user, branchId);
return await deleteFile(fileLocation.customerBranch.attachment(branchId, name));
}
@Get("file-citizen")
@Security("keycloak")
async listCitizen(@Request() req: RequestWithUser, @Path() branchId: string) {
await this.checkPermission(req.user, branchId);
return await listFile(fileLocation.customerBranch.attachment(branchId));
}
@Get("file-citizen/{id}")
@Security("keycloak")
async getCitizen(@Path() branchId: string, @Path() id: string) {
return await getFile(fileLocation.customerBranch.attachment(branchId, id));
}
@Put("file-citizen/{id}")
@Security("keycloak")
async putCitizen(@Request() req: RequestWithUser, @Path() branchId: string, @Path() id: string) {
await this.checkPermission(req.user, branchId);
return req.res?.redirect(await setFile(fileLocation.customerBranch.attachment(branchId, id)));
}
@Delete("file-citizen/{id}")
@Security("keycloak")
async delCitizen(@Request() req: RequestWithUser, @Path() branchId: string, @Path() id: string) {
await this.checkPermission(req.user, branchId);
return await deleteFile(fileLocation.customerBranch.citizen(branchId, id));
}
@Get("file-power-of-attorney")
@Security("keycloak")
async listPoa(@Request() req: RequestWithUser, @Path() branchId: string) {
await this.checkPermission(req.user, branchId);
return await listFile(fileLocation.customerBranch.powerOfAttorney(branchId));
}
@Get("file-power-of-attorney/{id}")
@Security("keycloak")
async getPoa(@Path() branchId: string, @Path() id: string) {
return await getFile(fileLocation.customerBranch.powerOfAttorney(branchId, id));
}
@Put("file-power-of-attorney/{id}")
@Security("keycloak")
async putPoa(@Request() req: RequestWithUser, @Path() branchId: string, @Path() id: string) {
await this.checkPermission(req.user, branchId);
return req.res?.redirect( return req.res?.redirect(
await minio.presignedPutObject( await setFile(fileLocation.customerBranch.powerOfAttorney(branchId, id)),
MINIO_BUCKET,
`${attachmentLocation(record.customerId, branchId)}/${filename}`,
12 * 60 * 60,
),
); );
} }
@Delete("{filename}") @Delete("file-power-of-attorney/{id}")
async deleteAttachment(@Path() branchId: string, @Path() filename: string) { @Security("keycloak")
const record = await prisma.customerBranch.findFirst({ async delPoa(@Request() req: RequestWithUser, @Path() branchId: string, @Path() id: string) {
where: { id: branchId }, await this.checkPermission(req.user, branchId);
}); return await deleteFile(fileLocation.customerBranch.powerOfAttorney(branchId, id));
}
if (!record) { @Get("file-house-registration")
throw new HttpError( @Security("keycloak")
HttpStatus.NOT_FOUND, async listHouseRegis(@Request() req: RequestWithUser, @Path() branchId: string) {
"Customer branch cannot be found.", await this.checkPermission(req.user, branchId);
"customerBranchNotFound", return await listFile(fileLocation.customerBranch.houseRegistration(branchId));
); }
}
await minio.removeObject( @Get("file-house-registration/{id}")
MINIO_BUCKET, @Security("keycloak")
`${attachmentLocation(record.customerId, branchId)}/${filename}`, async getHouseRegis(@Path() branchId: string, @Path() id: string) {
{ forceDelete: true }, return await getFile(fileLocation.customerBranch.houseRegistration(branchId, id));
}
@Put("file-house-registration/{id}")
@Security("keycloak")
async putHouseRegis(
@Request() req: RequestWithUser,
@Path() branchId: string,
@Path() id: string,
) {
await this.checkPermission(req.user, branchId);
return req.res?.redirect(
await setFile(fileLocation.customerBranch.houseRegistration(branchId, id)),
); );
} }
@Delete("file-house-registration/{id}")
@Security("keycloak")
async delHouseRegis(
@Request() req: RequestWithUser,
@Path() branchId: string,
@Path() id: string,
) {
await this.checkPermission(req.user, branchId);
return await deleteFile(fileLocation.customerBranch.houseRegistration(branchId, id));
}
@Get("file-commercial-registration")
@Security("keycloak")
async listCommercialRegis(@Request() req: RequestWithUser, @Path() branchId: string) {
await this.checkPermission(req.user, branchId);
return await listFile(fileLocation.customerBranch.commercialRegistration(branchId));
}
@Get("file-commercial-registration/{id}")
@Security("keycloak")
async getCommercialRegis(@Path() branchId: string, @Path() id: string) {
return await getFile(fileLocation.customerBranch.commercialRegistration(branchId, id));
}
@Put("file-commercial-registration/{id}")
@Security("keycloak")
async putCommercialRegis(
@Request() req: RequestWithUser,
@Path() branchId: string,
@Path() id: string,
) {
await this.checkPermission(req.user, branchId);
return req.res?.redirect(
await setFile(fileLocation.customerBranch.commercialRegistration(branchId, id)),
);
}
@Delete("file-commercial-registration/{id}")
@Security("keycloak")
async delCommercialRegis(
@Request() req: RequestWithUser,
@Path() branchId: string,
@Path() id: string,
) {
await this.checkPermission(req.user, branchId);
return await deleteFile(fileLocation.customerBranch.commercialRegistration(branchId, id));
}
@Get("file-vat-registration")
@Security("keycloak")
async listVatRegis(@Request() req: RequestWithUser, @Path() branchId: string) {
await this.checkPermission(req.user, branchId);
return await listFile(fileLocation.customerBranch.vatRegistration(branchId));
}
@Get("file-vat-registration/{id}")
@Security("keycloak")
async getVatRegis(@Path() branchId: string, @Path() id: string) {
return await getFile(fileLocation.customerBranch.vatRegistration(branchId, id));
}
@Put("file-vat-registration/{id}")
@Security("keycloak")
async putVatRegis(@Request() req: RequestWithUser, @Path() branchId: string, @Path() id: string) {
await this.checkPermission(req.user, branchId);
return req.res?.redirect(
await setFile(fileLocation.customerBranch.vatRegistration(branchId, id)),
);
}
@Delete("file-vat-registration/{id}")
@Security("keycloak")
async delVatRegis(@Request() req: RequestWithUser, @Path() branchId: string, @Path() id: string) {
await this.checkPermission(req.user, branchId);
return await deleteFile(fileLocation.customerBranch.vatRegistration(branchId, id));
}
} }

View file

@ -60,6 +60,20 @@ export const fileLocation = {
customer: { customer: {
img: (customerId: string, name?: string) => `customer/img-${customerId}/${name || ""}`, img: (customerId: string, name?: string) => `customer/img-${customerId}/${name || ""}`,
}, },
customerBranch: {
attachment: (customerBranchId: string, name?: string) =>
`customer-branch/attachment-${customerBranchId}/${name || ""}`,
citizen: (customerBranchId: string, citizenId?: string) =>
`customer-branch/citizen-${customerBranchId}/${citizenId || ""}`,
houseRegistration: (customerBranchId: string, houseRegistrationId?: string) =>
`customer-branch/house-registration-${customerBranchId}/${houseRegistrationId || ""}`,
commercialRegistration: (customerBranchId: string, commercialRegistrationId?: string) =>
`customer-branch/commercial-registration-${customerBranchId}/${commercialRegistrationId || ""}`,
vatRegistration: (customerBranchId: string, vatRegistrationId?: string) =>
`customer-branch/vat-registration-${customerBranchId}/${vatRegistrationId || ""}`,
powerOfAttorney: (customerBranchId: string, powerOfAttorneyId?: string) =>
`customer-branch/power-of-attorney-${customerBranchId}/${powerOfAttorneyId || ""}`,
},
employee: { employee: {
img: (employeeId: string, name?: string) => `employee/img-${employeeId}/${name || ""}`, img: (employeeId: string, name?: string) => `employee/img-${employeeId}/${name || ""}`,
attachment: (employeeId: string, name?: string) => attachment: (employeeId: string, name?: string) =>