jws-backend/src/controllers/04-institution-controller.ts
2025-02-10 10:03:32 +07:00

291 lines
7.7 KiB
TypeScript

import { Prisma } from "@prisma/client";
import {
Body,
Controller,
Delete,
Get,
Head,
OperationId,
Path,
Post,
Put,
Query,
Request,
Route,
Security,
Tags,
} from "tsoa";
import prisma from "../db";
import { isUsedError, notFoundError } from "../utils/error";
import { queryOrNot } from "../utils/relation";
import { RequestWithUser } from "../interfaces/user";
import { deleteFile, fileLocation, getFile, getPresigned, listFile, setFile } from "../utils/minio";
import HttpError from "../interfaces/http-error";
import HttpStatus from "../interfaces/http-status";
import { Status } from "../generated/kysely/types";
type InstitutionPayload = {
name: string;
nameEN: string;
code: string;
addressEN: string;
address: string;
soi?: string | null;
soiEN?: string | null;
moo?: string | null;
mooEN?: string | null;
street?: string | null;
streetEN?: string | null;
subDistrictId: string;
districtId: string;
provinceId: string;
selectedImage?: string | null;
};
@Route("api/v1/institution")
@Tags("Institution")
export class InstitutionController extends Controller {
@Get()
@Security("keycloak")
@OperationId("getInstitutionList")
async getInstitutionList(
@Query() query: string = "",
@Query() page: number = 1,
@Query() pageSize: number = 30,
@Query() group?: string,
) {
return this.getInstitutionListByCriteria(query, page, pageSize, group);
}
@Post("list")
@Security("keycloak")
@OperationId("getInstitutionListByCriteria")
async getInstitutionListByCriteria(
@Query() query: string = "",
@Query() page: number = 1,
@Query() pageSize: number = 30,
@Query() group?: string,
@Body()
body?: {
group?: string[];
},
) {
const where = {
group: body?.group ? { in: body.group } : group,
OR: queryOrNot<Prisma.InstitutionWhereInput[]>(query, [
{ name: { contains: query } },
{ code: { contains: query, mode: "insensitive" } },
]),
} satisfies Prisma.InstitutionWhereInput;
const [result, total] = await prisma.$transaction([
prisma.institution.findMany({
where,
include: {
province: true,
district: true,
subDistrict: true,
},
take: pageSize,
skip: (page - 1) * pageSize,
}),
prisma.institution.count({ where }),
]);
return { result, page, pageSize, total };
}
@Get("{institutionId}")
@Security("keycloak")
@OperationId("getInstitution")
async getInstitution(@Path() institutionId: string, @Query() group?: string) {
return await prisma.institution.findFirst({
include: {
province: true,
district: true,
subDistrict: true,
},
where: { id: institutionId, group },
});
}
@Post()
@Security("keycloak")
@OperationId("createInstitution")
async createInstitution(
@Body()
body: InstitutionPayload & {
status?: Status;
},
) {
return await prisma.$transaction(async (tx) => {
const last = await tx.runningNo.upsert({
where: {
key: `INST_${body.code}`,
},
create: {
key: `INST_${body.code}`,
value: 1,
},
update: { value: { increment: 1 } },
});
return await tx.institution.create({
data: {
...body,
code: `${body.code}${last.value.toString().padStart(5, "0")}`,
group: body.code,
},
});
});
}
@Put("{institutionId}")
@Security("keycloak")
@OperationId("updateInstitution")
async updateInstitution(
@Path() institutionId: string,
@Body()
body: InstitutionPayload & {
status?: "ACTIVE" | "INACTIVE";
},
) {
return await prisma.institution.update({
where: { id: institutionId },
data: body,
});
}
@Delete("{institutionId}")
@Security("keycloak")
@OperationId("deleteInstitution")
async deleteInstitution(@Path() institutionId: string) {
return await prisma.$transaction(async (tx) => {
const record = await tx.institution.findFirst({
where: { id: institutionId },
include: {
taskOrder: {
take: 1,
},
},
});
if (!record) throw notFoundError("Institution");
if (record.status !== "CREATED" || record.taskOrder.length > 0) {
throw isUsedError("Institution");
}
return await tx.institution.delete({
where: { id: institutionId },
});
});
}
}
@Route("api/v1/institution/{institutionId}")
@Tags("Institution")
export class InstitutionFileController extends Controller {
private async checkPermission(_user: RequestWithUser["user"], id: string) {
const data = await prisma.institution.findUnique({
where: { id },
});
if (!data) throw notFoundError("Institution");
}
@Get("image")
@Security("keycloak")
async listImage(@Request() req: RequestWithUser, @Path() institutionId: string) {
await this.checkPermission(req.user, institutionId);
return await listFile(fileLocation.institution.img(institutionId));
}
@Get("image/{name}")
async getImage(
@Request() req: RequestWithUser,
@Path() institutionId: string,
@Path() name: string,
) {
return req.res?.redirect(await getFile(fileLocation.institution.img(institutionId, name)));
}
@Head("image/{name}")
async headImage(
@Request() req: RequestWithUser,
@Path() institutionId: string,
@Path() name: string,
) {
return req.res?.redirect(
await getPresigned("head", fileLocation.institution.img(institutionId, name)),
);
}
@Put("image/{name}")
@Security("keycloak")
async putImage(
@Request() req: RequestWithUser,
@Path() institutionId: 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, institutionId);
return req.res?.redirect(await setFile(fileLocation.institution.img(institutionId, name)));
}
@Delete("image/{name}")
@Security("keycloak")
async delImage(
@Request() req: RequestWithUser,
@Path() institutionId: string,
@Path() name: string,
) {
await this.checkPermission(req.user, institutionId);
return await deleteFile(fileLocation.institution.img(institutionId, name));
}
@Get("attachment")
@Security("keycloak")
async listAttachment(@Request() req: RequestWithUser, @Path() institutionId: string) {
await this.checkPermission(req.user, institutionId);
return await listFile(fileLocation.institution.attachment(institutionId));
}
@Get("attachment/{name}")
@Security("keycloak")
async getAttachment(@Path() institutionId: string, @Path() name: string) {
return await getFile(fileLocation.institution.attachment(institutionId, name));
}
@Head("attachment/{name}")
@Security("keycloak")
async headAttachment(@Path() institutionId: string, @Path() name: string) {
return await getPresigned("head", fileLocation.institution.attachment(institutionId, name));
}
@Put("attachment/{name}")
@Security("keycloak")
async putAttachment(
@Request() req: RequestWithUser,
@Path() institutionId: string,
@Path() name: string,
) {
await this.checkPermission(req.user, institutionId);
return await setFile(fileLocation.institution.attachment(institutionId, name));
}
@Delete("attachment/{name}")
@Security("keycloak")
async delAttachment(
@Request() req: RequestWithUser,
@Path() institutionId: string,
@Path() name: string,
) {
await this.checkPermission(req.user, institutionId);
return await deleteFile(fileLocation.institution.attachment(institutionId, name));
}
}