refactor: file upload and image
This commit is contained in:
parent
ceb72f9c50
commit
b1bd14f6aa
1 changed files with 91 additions and 119 deletions
|
|
@ -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, { presignedGetObjectIfExist } from "../services/minio";
|
|
||||||
import { isSystem } from "../utils/keycloak";
|
import { isSystem } from "../utils/keycloak";
|
||||||
import { filterStatus } from "../services/prisma";
|
import { filterStatus } from "../services/prisma";
|
||||||
import {
|
import {
|
||||||
|
|
@ -26,13 +25,13 @@ import {
|
||||||
createPermCondition,
|
createPermCondition,
|
||||||
} from "../services/permission";
|
} from "../services/permission";
|
||||||
import { connectOrDisconnect, connectOrNot } from "../utils/relation";
|
import { connectOrDisconnect, connectOrNot } from "../utils/relation";
|
||||||
import { throwRelationError } from "../utils/error";
|
import { throwNotFound, throwRelationError } from "../utils/error";
|
||||||
|
import { deleteFile, fileLocation, getFile, listFile, setFile } from "../utils/minio";
|
||||||
|
|
||||||
if (!process.env.MINIO_BUCKET) {
|
if (!process.env.MINIO_BUCKET) {
|
||||||
throw Error("Require 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",
|
||||||
|
|
@ -50,14 +49,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/${id}/profile-image`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function attachmentLocation(id: string, filename?: string) {
|
|
||||||
return `employee/${id}/attachment/${filename || ""}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
type EmployeeCreate = {
|
type EmployeeCreate = {
|
||||||
customerBranchId: string;
|
customerBranchId: string;
|
||||||
|
|
||||||
|
|
@ -364,17 +355,9 @@ export class EmployeeController extends Controller {
|
||||||
where: { id: employeeId },
|
where: { id: employeeId },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!record) {
|
if (!record) return throwNotFound("Employee");
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.assign(record, {
|
return record;
|
||||||
profileImageUrl: await presignedGetObjectIfExist(
|
|
||||||
MINIO_BUCKET,
|
|
||||||
imageLocation(employeeId),
|
|
||||||
12 * 60 * 60,
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
|
|
@ -750,9 +733,7 @@ export class EmployeeController extends Controller {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!record) {
|
if (!record) return throwNotFound("Employee");
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound");
|
|
||||||
}
|
|
||||||
|
|
||||||
await permissionCheck(req.user, record.customerBranch.customer.registeredBranch);
|
await permissionCheck(req.user, record.customerBranch.customer.registeredBranch);
|
||||||
|
|
||||||
|
|
@ -778,114 +759,105 @@ export class EmployeeController extends Controller {
|
||||||
where: { masterId: employeeId },
|
where: { masterId: employeeId },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get("{employeeId}/image")
|
|
||||||
async getImageByEmployeeId(@Request() req: RequestWithUser, @Path() employeeId: string) {
|
|
||||||
const url = await presignedGetObjectIfExist(MINIO_BUCKET, imageLocation(employeeId), 60 * 60);
|
|
||||||
|
|
||||||
if (!url) {
|
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "Image cannot be found", "imageNotFound");
|
|
||||||
}
|
|
||||||
|
|
||||||
return req.res?.redirect(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Put("{employeeId}/image")
|
|
||||||
@Security("keycloak", MANAGE_ROLES)
|
|
||||||
async setImageByEmployeeId(@Request() req: RequestWithUser, @Path() employeeId: string) {
|
|
||||||
const record = await prisma.employee.findFirst({
|
|
||||||
where: { id: employeeId },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!record) {
|
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound");
|
|
||||||
}
|
|
||||||
|
|
||||||
return req.res?.redirect(
|
|
||||||
await minio.presignedPutObject(MINIO_BUCKET, imageLocation(employeeId), 12 * 60 * 60),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Route("api/v1/employee/{employeeId}/attachment")
|
@Route("api/v1/employee/{employeeId}")
|
||||||
@Tags("Employee")
|
@Tags("Branch")
|
||||||
@Security("keycloak")
|
export class EmployeeFileController extends Controller {
|
||||||
export class EmployeeAttachmentController extends Controller {
|
private async checkPermission(user: RequestWithUser["user"], id: string) {
|
||||||
@Get()
|
const data = await prisma.employee.findFirst({
|
||||||
async listAttachment(@Path() employeeId: string) {
|
where: { id },
|
||||||
const record = await prisma.employee.findFirst({
|
include: {
|
||||||
where: { id: employeeId },
|
customerBranch: {
|
||||||
|
include: {
|
||||||
|
customer: {
|
||||||
|
include: {
|
||||||
|
registeredBranch: {
|
||||||
|
include: branchRelationPermInclude(user),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
if (!data) return throwNotFound("Employee");
|
||||||
if (!record) {
|
await permissionCheck(user, data.customerBranch.customer.registeredBranch);
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = await new Promise<string[]>((resolve, reject) => {
|
@Get("image")
|
||||||
const item: string[] = [];
|
@Security("keycloak")
|
||||||
|
async listImage(@Request() req: RequestWithUser, @Path() employeeId: string) {
|
||||||
const stream = minio.listObjectsV2(MINIO_BUCKET, attachmentLocation(employeeId));
|
await this.checkPermission(req.user, employeeId);
|
||||||
|
return await listFile(fileLocation.employee.img(employeeId));
|
||||||
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("image/{name}")
|
||||||
async getAttachment(@Path() employeeId: string, @Path() filename: string) {
|
async getImage(
|
||||||
const record = await prisma.employee.findFirst({
|
|
||||||
where: { id: employeeId },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!record) {
|
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound");
|
|
||||||
}
|
|
||||||
|
|
||||||
return await minio.presignedGetObject(
|
|
||||||
MINIO_BUCKET,
|
|
||||||
attachmentLocation(record.id, filename),
|
|
||||||
12 * 60 * 60,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Put("{filename}")
|
|
||||||
async addAttachment(
|
|
||||||
@Request() req: RequestWithUser,
|
@Request() req: RequestWithUser,
|
||||||
@Path() employeeId: string,
|
@Path() employeeId: string,
|
||||||
@Path() filename: string,
|
@Path() name: string,
|
||||||
) {
|
) {
|
||||||
const record = await prisma.employee.findFirst({
|
return req.res?.redirect(await getFile(fileLocation.employee.img(employeeId, name)));
|
||||||
where: { id: employeeId },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!record) {
|
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return req.res?.redirect(
|
@Put("image/{name}")
|
||||||
await minio.presignedPutObject(
|
@Security("keycloak")
|
||||||
MINIO_BUCKET,
|
async putImage(
|
||||||
attachmentLocation(record.id, filename),
|
@Request() req: RequestWithUser,
|
||||||
12 * 60 * 60,
|
@Path() employeeId: string,
|
||||||
),
|
@Path() name: string,
|
||||||
);
|
) {
|
||||||
|
if (!req.headers["content-type"]?.startsWith("image/")) {
|
||||||
|
throw new HttpError(HttpStatus.BAD_REQUEST, "Not a valid image.", "notValidImage");
|
||||||
|
}
|
||||||
|
await this.checkPermission(req.user, employeeId);
|
||||||
|
return req.res?.redirect(await setFile(fileLocation.employee.img(employeeId, name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete("{filename}")
|
@Delete("image/{name}")
|
||||||
async deleteAttachment(@Path() employeeId: string, @Path() filename: string) {
|
@Security("keycloak")
|
||||||
const record = await prisma.employee.findFirst({
|
async delImage(
|
||||||
where: { id: employeeId },
|
@Request() req: RequestWithUser,
|
||||||
});
|
@Path() employeeId: string,
|
||||||
|
@Path() name: string,
|
||||||
if (!record) {
|
) {
|
||||||
throw new HttpError(HttpStatus.NOT_FOUND, "Employee cannot be found.", "employeeNotFound");
|
await this.checkPermission(req.user, employeeId);
|
||||||
|
return await deleteFile(fileLocation.employee.img(employeeId, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
await minio.removeObject(MINIO_BUCKET, attachmentLocation(record.id, filename), {
|
@Get("attachment")
|
||||||
forceDelete: true,
|
@Security("keycloak")
|
||||||
});
|
async listAttachment(@Request() req: RequestWithUser, @Path() employeeId: string) {
|
||||||
|
await this.checkPermission(req.user, employeeId);
|
||||||
|
return await listFile(fileLocation.employee.attachment(employeeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get("attachment/{name}")
|
||||||
|
@Security("keycloak")
|
||||||
|
async getAttachment(@Path() employeeId: string, @Path() name: string) {
|
||||||
|
return await getFile(fileLocation.employee.attachment(employeeId, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Put("attachment/{name}")
|
||||||
|
@Security("keycloak")
|
||||||
|
async putAttachment(
|
||||||
|
@Request() req: RequestWithUser,
|
||||||
|
@Path() employeeId: string,
|
||||||
|
@Path() name: string,
|
||||||
|
) {
|
||||||
|
await this.checkPermission(req.user, employeeId);
|
||||||
|
return req.res?.redirect(await setFile(fileLocation.employee.attachment(employeeId, name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete("attachment/{name}")
|
||||||
|
@Security("keycloak")
|
||||||
|
async delAttachment(
|
||||||
|
@Request() req: RequestWithUser,
|
||||||
|
@Path() employeeId: string,
|
||||||
|
@Path() name: string,
|
||||||
|
) {
|
||||||
|
await this.checkPermission(req.user, employeeId);
|
||||||
|
return await deleteFile(fileLocation.employee.attachment(employeeId, name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue