From 4318a7b631ee9d5b37d28bebfe581316d18b72cc Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Thu, 14 Aug 2025 11:17:13 +0700 Subject: [PATCH] fix api service v2 to v1 & fix type & review code --- src/controllers/ApiManageController.ts | 870 +++++++++++---------- src/controllers/ApiWebServiceController.ts | 30 +- src/interfaces/api-type.ts | 16 + 3 files changed, 485 insertions(+), 431 deletions(-) create mode 100644 src/interfaces/api-type.ts diff --git a/src/controllers/ApiManageController.ts b/src/controllers/ApiManageController.ts index 295fed4c..bc96af1a 100644 --- a/src/controllers/ApiManageController.ts +++ b/src/controllers/ApiManageController.ts @@ -1,3 +1,4 @@ +import { Position } from "./../entities/Position"; import { ApiKey } from "./../entities/ApiKey"; import { ApiAttribute } from "./../entities/ApiAttribute"; import { CreateApi } from "./../entities/ApiName"; @@ -49,7 +50,7 @@ import HttpError from "../interfaces/http-error"; import { RequestWithUser } from "../middlewares/user"; import { ApiName } from "../entities/ApiName"; import { Profile } from "../entities/Profile"; - +import { SystemDefinition, EntityDefinition, SystemCode } from "./../interfaces/api-type"; @Route("api/v1/org/api-manage") @Tags("ApiManage") @Security("bearerAuth") @@ -58,36 +59,37 @@ import { Profile } from "../entities/Profile"; "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง", ) export class ApiManageController extends Controller { - // private apiNameRepository = AppDataSource.getRepository(ApiName); - private profileRepository = AppDataSource.getRepository(Profile); - private profileEmployeeRepository = AppDataSource.getRepository(ProfileEmployee); - private profileGovernmentRepository = AppDataSource.getRepository(ProfileGovernment); - private profileProfileSalaryRepository = AppDataSource.getRepository(ProfileSalary); - private profileAbilityRepository = AppDataSource.getRepository(ProfileAbility); - private profileAssessmentRepository = AppDataSource.getRepository(ProfileAssessment); - private profileCertificateRepository = AppDataSource.getRepository(ProfileCertificate); - private profileHonorRepository = AppDataSource.getRepository(ProfileHonor); - private profileInsigniaRepository = AppDataSource.getRepository(ProfileInsignia); - private profileLeaveRepository = AppDataSource.getRepository(ProfileLeave); - private profileChangeNameRepository = AppDataSource.getRepository(ProfileChangeName); - private profileDevelopmentRepository = AppDataSource.getRepository(ProfileDevelopment); - private profileDisciplineRepository = AppDataSource.getRepository(ProfileDiscipline); - private profileDutyRepository = AppDataSource.getRepository(ProfileDuty); - private profileEducationRepository = AppDataSource.getRepository(ProfileEducation); - private profileFamilyCoupleRepository = AppDataSource.getRepository(ProfileFamilyCouple); - private profileFamilyFatherRepository = AppDataSource.getRepository(ProfileFamilyFather); - private profileFamilyMotherRepository = AppDataSource.getRepository(ProfileFamilyMother); - private profileChildrenRepository = AppDataSource.getRepository(ProfileChildren); - private profileNopaidRepository = AppDataSource.getRepository(ProfileNopaid); - private profileOtherRepository = AppDataSource.getRepository(ProfileOther); - private orgRootRepository = AppDataSource.getRepository(OrgRoot); - private orgChild1Repository = AppDataSource.getRepository(OrgChild1); - private orgChild2Repository = AppDataSource.getRepository(OrgChild2); - private orgChild3Repository = AppDataSource.getRepository(OrgChild3); - private orgChild4Repository = AppDataSource.getRepository(OrgChild4); - private posMasterRepository = AppDataSource.getRepository(PosMaster); + private readonly profileRepository = AppDataSource.getRepository(Profile); + private readonly profileEmployeeRepository = AppDataSource.getRepository(ProfileEmployee); + private readonly profileGovernmentRepository = AppDataSource.getRepository(ProfileGovernment); + private readonly profileProfileSalaryRepository = AppDataSource.getRepository(ProfileSalary); + private readonly profileAbilityRepository = AppDataSource.getRepository(ProfileAbility); + private readonly profileAssessmentRepository = AppDataSource.getRepository(ProfileAssessment); + private readonly profileCertificateRepository = AppDataSource.getRepository(ProfileCertificate); + private readonly profileHonorRepository = AppDataSource.getRepository(ProfileHonor); + private readonly profileInsigniaRepository = AppDataSource.getRepository(ProfileInsignia); + private readonly profileLeaveRepository = AppDataSource.getRepository(ProfileLeave); + private readonly profileChangeNameRepository = AppDataSource.getRepository(ProfileChangeName); + private readonly profileDevelopmentRepository = AppDataSource.getRepository(ProfileDevelopment); + private readonly profileDisciplineRepository = AppDataSource.getRepository(ProfileDiscipline); + private readonly profileDutyRepository = AppDataSource.getRepository(ProfileDuty); + private readonly profileEducationRepository = AppDataSource.getRepository(ProfileEducation); + private readonly profileFamilyCoupleRepository = AppDataSource.getRepository(ProfileFamilyCouple); + private readonly profileFamilyFatherRepository = AppDataSource.getRepository(ProfileFamilyFather); + private readonly profileFamilyMotherRepository = AppDataSource.getRepository(ProfileFamilyMother); + private readonly profileChildrenRepository = AppDataSource.getRepository(ProfileChildren); + private readonly profileNopaidRepository = AppDataSource.getRepository(ProfileNopaid); + private readonly profileOtherRepository = AppDataSource.getRepository(ProfileOther); + private readonly orgRootRepository = AppDataSource.getRepository(OrgRoot); + private readonly orgChild1Repository = AppDataSource.getRepository(OrgChild1); + private readonly orgChild2Repository = AppDataSource.getRepository(OrgChild2); + private readonly orgChild3Repository = AppDataSource.getRepository(OrgChild3); + private readonly orgChild4Repository = AppDataSource.getRepository(OrgChild4); + private readonly posMasterRepository = AppDataSource.getRepository(PosMaster); + private readonly positionRepository = AppDataSource.getRepository(Position); - private systems = [ + // รายการระบบ + private readonly systems: SystemDefinition[] = [ { code: "registry", name: "ข้อมูลทะเบียนประวัติข้าราชการ", @@ -105,12 +107,236 @@ export class ApiManageController extends Controller { name: "ข้อมูลโครงสร้างและอัตรากำลัง", }, ]; + + // รายการเอนทิตีทั้งหมด + private readonly entities: EntityDefinition[] = [ + { + name: "Profile", + repository: this.profileRepository, + description: "ข้อมูลหลัก", + isMain: true, + system: ["registry"], + }, + { + name: "ProfileEmployee", + repository: this.profileEmployeeRepository, + description: "ข้อมูลหลัก", + isMain: true, + system: ["registry_emp", "registry_temp"], + }, + { + name: "ProfileGovernment", + repository: this.profileGovernmentRepository, + description: "ข้อมูลราชการ", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileSalary", + repository: this.profileProfileSalaryRepository, + description: "ข้อมูลตำแหน่ง/เงินเดือน", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileAbility", + repository: this.profileAbilityRepository, + description: "ข้อมูลความสามารถ", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileAssessment", + repository: this.profileAssessmentRepository, + description: "ข้อมูลการประเมิน", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileCertificate", + repository: this.profileCertificateRepository, + description: "ข้อมูลใบรับรอง", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileHonor", + repository: this.profileHonorRepository, + description: "ข้อมูลเกียรติบัตร", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileInsignia", + repository: this.profileInsigniaRepository, + description: "ข้อมูลเครื่องหมาย", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileLeave", + repository: this.profileLeaveRepository, + description: "ข้อมูลการลา", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileChangeName", + repository: this.profileChangeNameRepository, + description: "ข้อมูลการเปลี่ยนชื่อ", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileDevelopment", + repository: this.profileDevelopmentRepository, + description: "ข้อมูลการพัฒนา", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileDiscipline", + repository: this.profileDisciplineRepository, + description: "ข้อมูลวินัย", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileDuty", + repository: this.profileDutyRepository, + description: "ข้อมูลหน้าที่ปฏิบัติราชการ", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileEducation", + repository: this.profileEducationRepository, + description: "ข้อมูลการศึกษา", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileFamilyCouple", + repository: this.profileFamilyCoupleRepository, + description: "ข้อมูลคู่สมรส", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileFamilyFather", + repository: this.profileFamilyFatherRepository, + description: "ข้อมูลบิดา", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileFamilyMother", + repository: this.profileFamilyMotherRepository, + description: "ข้อมูลมารดา", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileChildren", + repository: this.profileChildrenRepository, + description: "ข้อมูลบุตร", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileNopaid", + repository: this.profileNopaidRepository, + description: "ข้อมูลการไม่จ่ายเงินเดือน", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "ProfileOther", + repository: this.profileOtherRepository, + description: "ข้อมูลอื่นๆ", + system: ["registry", "registry_emp", "registry_temp"], + }, + { + name: "OrgRoot", + repository: this.orgRootRepository, + description: "ข้อมูลหน่วยงาน", + isMain: true, + system: ["organization"], + }, + { + name: "OrgChild1", + repository: this.orgChild1Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 1", + system: ["organization"], + }, + { + name: "OrgChild2", + repository: this.orgChild2Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 2", + system: ["organization"], + }, + { + name: "OrgChild3", + repository: this.orgChild3Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 3", + system: ["organization"], + }, + { + name: "OrgChild4", + repository: this.orgChild4Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 4", + system: ["organization"], + }, + { + name: "PosMaster", + repository: this.posMasterRepository, + description: "ข้อมูลอัตรากำลัง", + isMain: true, + system: ["position"], + }, + { + name: "position", + repository: this.positionRepository, + description: "ข้อมูลตำแหน่ง", + system: ["position"], + }, + { + name: "OrgRoot", + repository: this.orgRootRepository, + description: "ข้อมูลหน่วยงาน", + system: ["position"], + }, + { + name: "OrgChild1", + repository: this.orgChild1Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 1", + system: ["position"], + }, + { + name: "OrgChild2", + repository: this.orgChild2Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 2", + system: ["position"], + }, + { + name: "OrgChild3", + repository: this.orgChild3Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 3", + system: ["position"], + }, + { + name: "OrgChild4", + repository: this.orgChild4Repository, + description: "ข้อมูลส่วนราชการ ระดับที่ 4", + system: ["position"], + }, + ]; + + private readonly DEFAULT_PAGE_SIZE = 10; // ขนาดหน้าเริ่มต้น + private readonly EXCLUDED_COLUMNS = ["createdUserId", "lastUpdateUserId"]; // ฟิลด์ที่ไม่ต้องการแสดงในผลลัพธ์ + + private validateSuperAdminRole(user: any): void { + if (!user.role.includes("SUPER_ADMIN")) { + throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); + } + } + + private generateApiCode(): string { + return Math.random().toString(36).substring(2, 10).toUpperCase(); + } + + private createApiPath(system: SystemCode = "registry", code: string): string { + return `/api/v1/org/api-service/${system}/${code}`; + } + /** * list systems * @summary รายการ systems */ @Get("/systems") - async listSystems() { + async listSystems(): Promise { return new HttpSuccess(this.systems); } @@ -121,194 +347,23 @@ export class ApiManageController extends Controller { @Get("/:system/fields") async listAttribute( @Request() req: RequestWithUser, - @Path("system") system: "registry" | "registry_emp" | "registry_temp" | "organization", + @Path("system") system: SystemCode, ): Promise { try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } - const entities = [ - { - name: "Profile", - repository: this.profileRepository, - description: "ข้อมูลหลัก", - isMain: true, - system: ["registry"], - }, - { - name: "ProfileEmployee", - repository: this.profileEmployeeRepository, - description: "ข้อมูลหลัก", - isMain: true, - system: ["registry_emp", "registry_temp"], - }, - { - name: "ProfileGovernment", - repository: this.profileGovernmentRepository, - description: "ข้อมูลราชการ", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileSalary", - repository: this.profileProfileSalaryRepository, - description: "ข้อมูลตำแหน่ง/เงินเดือน", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileAbility", - repository: this.profileAbilityRepository, - description: "ข้อมูลความสามารถ", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileAssessment", - repository: this.profileAssessmentRepository, - description: "ข้อมูลการประเมิน", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileCertificate", - repository: this.profileCertificateRepository, - description: "ข้อมูลใบรับรอง", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileHonor", - repository: this.profileHonorRepository, - description: "ข้อมูลเกียรติบัตร", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileInsignia", - repository: this.profileInsigniaRepository, - description: "ข้อมูลเครื่องหมาย", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileLeave", - repository: this.profileLeaveRepository, - description: "ข้อมูลการลา", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileChangeName", - repository: this.profileChangeNameRepository, - description: "ข้อมูลการเปลี่ยนชื่อ", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileDevelopment", - repository: this.profileDevelopmentRepository, - description: "ข้อมูลการพัฒนา", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileDiscipline", - repository: this.profileDisciplineRepository, - description: "ข้อมูลวินัย", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileDuty", - repository: this.profileDutyRepository, - description: "ข้อมูลหน้าที่ปฏิบัติราชการ", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileEducation", - repository: this.profileEducationRepository, - description: "ข้อมูลการศึกษา", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileFamilyCouple", - repository: this.profileFamilyCoupleRepository, - description: "ข้อมูลคู่สมรส", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileFamilyFather", - repository: this.profileFamilyFatherRepository, - description: "ข้อมูลบิดา", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileFamilyMother", - repository: this.profileFamilyMotherRepository, - description: "ข้อมูลมารดา", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileChildren", - repository: this.profileChildrenRepository, - description: "ข้อมูลบุตร", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileNopaid", - repository: this.profileNopaidRepository, - description: "ข้อมูลการไม่จ่ายเงินเดือน", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "ProfileOther", - repository: this.profileOtherRepository, - description: "ข้อมูลอื่นๆ", - system: ["registry", "registry_emp", "registry_temp"], - }, - { - name: "OrgRoot", - repository: this.orgRootRepository, - description: "ข้อมูลหน่วยงาน", - isMain: true, - system: ["organization"], - }, - { - name: "OrgChild1", - repository: this.orgChild1Repository, - description: "ข้อมูลส่วนราชการ ระดับที่ 1", - system: ["organization"], - }, - { - name: "OrgChild2", - repository: this.orgChild2Repository, - description: "ข้อมูลส่วนราชการ ระดับที่ 2", - system: ["organization"], - }, - { - name: "OrgChild3", - repository: this.orgChild3Repository, - description: "ข้อมูลส่วนราชการ ระดับที่ 3", - system: ["organization"], - }, - { - name: "OrgChild4", - repository: this.orgChild4Repository, - description: "ข้อมูลส่วนราชการ ระดับที่ 4", - system: ["organization"], - }, - { - name: "PosMaster", - repository: this.posMasterRepository, - description: "ข้อมูลตำแหน่ง", - system: ["organization"], - }, - ]; + this.validateSuperAdminRole(req.user); - const result = entities - .filter((s) => s.system.includes(system)) + const result = this.entities + .filter((entity) => entity.system.includes(system)) .map(({ name, repository, description, isMain }) => ({ tb: name, description, isMain: isMain || false, propertys: repository.metadata.columns .filter( - (column) => - !column.isPrimary && - column.propertyName !== "createdUserId" && - column.propertyName !== "lastUpdateUserId", + (column: any) => + !column.isPrimary && !this.EXCLUDED_COLUMNS.includes(column.propertyName), ) - .map((column) => ({ + .map((column: any) => ({ propertyName: column.propertyName, type: typeof column.type === "string" ? column.type : "string", comment: column.comment, @@ -334,20 +389,18 @@ export class ApiManageController extends Controller { async listApi( @Request() req: RequestWithUser, @Query("page") page: number = 1, - @Query("pageSize") pageSize: number = 10, - @Query() keyword: string = "", - @Query() system: "registry" | "registry_emp" | "registry_temp" | "organization" | "" = "", - @Query() isActive: boolean = true, + @Query("pageSize") pageSize: number = this.DEFAULT_PAGE_SIZE, + @Query("keyword") keyword: string = "", + @Query("system") system: SystemCode | undefined = undefined, + @Query("isActive") isActive: boolean = true, ): Promise { try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } - const offset = (page - 1) * pageSize; + this.validateSuperAdminRole(req.user); + const offset = (page - 1) * pageSize; const queryBuilder = AppDataSource.getRepository(ApiName) .createQueryBuilder("apiName") - .where("apiName.isActive = :isActive", { isActive: isActive ?? true }) + .where("apiName.isActive = :isActive", { isActive }) .select([ "apiName.id", "apiName.name", @@ -370,7 +423,6 @@ export class ApiManageController extends Controller { if (system) { queryBuilder.andWhere("apiName.system = :system", { system }); } - // console.log("query ===> ", queryBuilder.getQuery()); const [apiNames, total] = await queryBuilder .skip(offset) @@ -404,59 +456,54 @@ export class ApiManageController extends Controller { @Request() req: RequestWithUser, @Body() apiData: CreateApi, ): Promise { + const queryRunner = AppDataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + try { - const queryRunner = AppDataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); + this.validateSuperAdminRole(req.user); - try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } + const code = this.generateApiCode(); + const postData = { + name: apiData.name, + code, + pathApi: this.createApiPath(apiData.system as SystemCode, code), + methodApi: apiData.methodApi || "GET", + system: apiData.system || "registry", + isActive: apiData.isActive || false, + createdUserId: req.user?.sub, + createdFullName: req.user?.name || "", + }; - const code = Math.random().toString(36).substring(2, 10).toUpperCase(); - const postData = { - name: apiData.name, - code, - pathApi: `/api/v2/org/api-service/${apiData.system}/${code}`, - methodApi: apiData.methodApi || "GET", - system: apiData.system || "registry", - isActive: apiData.isActive || false, - createdUserId: req.user?.sub || undefined, - createdFullName: req.user?.name || "", - }; - const apiName = await queryRunner.manager.getRepository(ApiName).save(postData); + const apiName = await queryRunner.manager.getRepository(ApiName).save(postData); - if (apiData.apiAttributes?.length) { - let orderingCounter = 0; - const attributesToSave = apiData.apiAttributes.flatMap((attr) => - attr.propertyKey.map((propertyKey) => ({ - apiNameId: apiName.id, - tbName: attr.tbName, - propertyKey, - ordering: orderingCounter++, - createdUserId: req.user?.sub || undefined, - createdFullName: req.user?.name || "", - })), - ); + if (apiData.apiAttributes?.length) { + let orderingCounter = 0; + const attributesToSave = apiData.apiAttributes.flatMap((attr) => + attr.propertyKey.map((propertyKey) => ({ + apiNameId: apiName.id, + tbName: attr.tbName, + propertyKey, + ordering: orderingCounter++, + createdUserId: req.user?.sub, + createdFullName: req.user?.name || "", + })), + ); - await queryRunner.manager.getRepository(ApiAttribute).save(attributesToSave); - } - - await queryRunner.commitTransaction(); - return new HttpSuccess(apiName.id); - } catch (transactionError) { - await queryRunner.rollbackTransaction(); - throw transactionError; - } finally { - await queryRunner.release(); + await queryRunner.manager.getRepository(ApiAttribute).save(attributesToSave); } + + await queryRunner.commitTransaction(); + return new HttpSuccess(apiName.id); } catch (error) { + await queryRunner.rollbackTransaction(); throw new HttpError( HttpStatusCode.INTERNAL_SERVER_ERROR, (error instanceof Error ? error.message : String(error)) || "เกิดข้อผิดพลาด ไม่สามารถบันทึกข้อมูลได้ กรุณาลองใหม่ในภายหลัง", ); + } finally { + await queryRunner.release(); } } @@ -470,9 +517,7 @@ export class ApiManageController extends Controller { @Path("id") id: string, ): Promise { try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } + this.validateSuperAdminRole(req.user); const apiName = await AppDataSource.getRepository(ApiName).findOne({ select: { @@ -493,6 +538,7 @@ export class ApiManageController extends Controller { }, }, }); + if (!apiName) { throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ต้องการแก้ไข"); } @@ -504,6 +550,7 @@ export class ApiManageController extends Controller { propertyKey: attr.propertyKey, })), }; + return new HttpSuccess(items); } catch (error) { throw new HttpError( @@ -524,63 +571,58 @@ export class ApiManageController extends Controller { @Path("id") id: string, @Body() apiData: CreateApi, ): Promise { + const queryRunner = AppDataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + try { - const queryRunner = AppDataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); + this.validateSuperAdminRole(req.user); - try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } - - const apiName = await queryRunner.manager.getRepository(ApiName).findOneBy({ id }); - if (!apiName) { - throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ต้องการแก้ไข"); - } - - const updateData = { - name: apiData.name, - pathApi: `/api/v2/org/api-service/${apiData.system}/${apiName.code}`, - methodApi: apiData.methodApi || "GET", - system: apiData.system || "registry", - isActive: apiData.isActive || false, - lastUpdateUserId: req.user?.sub || undefined, - lastUpdateFullName: req.user?.name || "", - }; - await queryRunner.manager.getRepository(ApiName).update(id, updateData); - - await queryRunner.manager.getRepository(ApiAttribute).delete({ apiNameId: id }); - if (apiData.apiAttributes?.length) { - let orderingCounter = 0; - const attributesToSave = apiData.apiAttributes.flatMap((attr) => - attr.propertyKey.map((propertyKey) => ({ - apiNameId: apiName.id, - tbName: attr.tbName, - propertyKey, - ordering: orderingCounter++, - lastUpdateUserId: req.user?.sub || undefined, - lastUpdateFullName: req.user?.name || "", - })), - ); - - await queryRunner.manager.getRepository(ApiAttribute).save(attributesToSave); - } - - await queryRunner.commitTransaction(); - return new HttpSuccess(); - } catch (transactionError) { - await queryRunner.rollbackTransaction(); - throw transactionError; - } finally { - await queryRunner.release(); + const apiName = await queryRunner.manager.getRepository(ApiName).findOneBy({ id }); + if (!apiName) { + throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ต้องการแก้ไข"); } + + const updateData = { + name: apiData.name, + pathApi: this.createApiPath(apiData.system as SystemCode, apiName.code), + methodApi: apiData.methodApi || "GET", + system: apiData.system || "registry", + isActive: apiData.isActive || false, + lastUpdateUserId: req.user?.sub, + lastUpdateFullName: req.user?.name || "", + }; + + await queryRunner.manager.getRepository(ApiName).update(id, updateData); + await queryRunner.manager.getRepository(ApiAttribute).delete({ apiNameId: id }); + + if (apiData.apiAttributes?.length) { + let orderingCounter = 0; + const attributesToSave = apiData.apiAttributes.flatMap((attr) => + attr.propertyKey.map((propertyKey) => ({ + apiNameId: apiName.id, + tbName: attr.tbName, + propertyKey, + ordering: orderingCounter++, + lastUpdateUserId: req.user?.sub, + lastUpdateFullName: req.user?.name || "", + })), + ); + + await queryRunner.manager.getRepository(ApiAttribute).save(attributesToSave); + } + + await queryRunner.commitTransaction(); + return new HttpSuccess(); } catch (error) { + await queryRunner.rollbackTransaction(); throw new HttpError( HttpStatusCode.INTERNAL_SERVER_ERROR, (error instanceof Error ? error.message : String(error)) || "เกิดข้อผิดพลาด ไม่สามารถบันทึกข้อมูลได้ กรุณาลองใหม่ในภายหลัง", ); + } finally { + await queryRunner.release(); } } @@ -593,46 +635,40 @@ export class ApiManageController extends Controller { @Request() req: RequestWithUser, @Path("id") id: string, ): Promise { + const queryRunner = AppDataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + try { - const queryRunner = AppDataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); + this.validateSuperAdminRole(req.user); - try { - if (!req.user.role.includes("SUPER_ADMIN")) { - throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้"); - } + const checkUsed = await AppDataSource.getRepository(ApiKey) + .createQueryBuilder("apiKey") + .innerJoin("apiKey.apiNames", "apiName") + .where("apiName.id = :id", { id }) + .getCount(); - const checkUsed = await AppDataSource.getRepository(ApiKey) - .createQueryBuilder("apiKey") - .innerJoin("apiKey.apiNames", "apiName") - .where("apiName.id = :id", { id }) - .getCount(); - - if (checkUsed > 0) { - throw new HttpError( - HttpStatusCode.BAD_REQUEST, - "ไม่สามารถลบ API นี้ได้ เนื่องจากมีการใช้งานอยู่", - ); - } - - await queryRunner.manager.getRepository(ApiAttribute).delete({ apiNameId: id }); - await queryRunner.manager.getRepository(ApiName).delete({ id }); - - await queryRunner.commitTransaction(); - return new HttpSuccess(); - } catch (transactionError) { - await queryRunner.rollbackTransaction(); - throw transactionError; - } finally { - await queryRunner.release(); + if (checkUsed > 0) { + throw new HttpError( + HttpStatusCode.BAD_REQUEST, + "ไม่สามารถลบ API นี้ได้ เนื่องจากมีการใช้งานอยู่", + ); } + + await queryRunner.manager.getRepository(ApiAttribute).delete({ apiNameId: id }); + await queryRunner.manager.getRepository(ApiName).delete({ id }); + + await queryRunner.commitTransaction(); + return new HttpSuccess(); } catch (error) { + await queryRunner.rollbackTransaction(); throw new HttpError( HttpStatusCode.INTERNAL_SERVER_ERROR, (error instanceof Error ? error.message : String(error)) || "เกิดข้อผิดพลาด ไม่สามารถบันทึกข้อมูลได้ กรุณาลองใหม่ในภายหลัง", ); + } finally { + await queryRunner.release(); } } @@ -641,94 +677,94 @@ export class ApiManageController extends Controller { * @summary รายการ systems */ @Get("/manual/swagger") - async getManualRequestWebService() { - const json: any = {}; - // create swagger documentation for manual request only ApiWebServiceController - json["openapi"] = "3.0.0"; - json["info"] = { - title: "Request Web Service", - version: "1.0.0", - description: "This is a manual request web service.", - }; - json["servers"] = [ - { - url: process.env.API_URL?.replace("/api/v1", "") || "http://localhost:13009", + async getManualRequestWebService(): Promise { + const json = { + openapi: "3.0.0", + info: { + title: "Request Web Service", + version: "1.0.0", + description: "This is a manual request web service.", }, - { - url: "http://localhost:13009", - description: "Local server", - }, - ]; - json["paths"] = { - "/api/v2/org/api-service/{system}/{code}": { - get: { - summary: "Get Registry Data", - parameters: [ - { - name: "system", - in: "path", - required: true, - schema: { - type: "string", - enum: ["registry", "registry_emp", "registry_temp", "organization"], + servers: [ + { + url: process.env.API_URL?.replace("/api/v1", "") || "http://localhost:13009", + }, + { + url: "http://localhost:13009", + description: "Local server", + }, + ], + paths: { + "/api/v1/org/api-service/{system}/{code}": { + get: { + summary: "Get Registry Data", + parameters: [ + { + name: "system", + in: "path", + required: true, + schema: { + type: "string", + enum: ["registry", "registry_emp", "registry_temp", "organization", "position"], + }, }, - }, - { - name: "code", - in: "path", - required: true, - schema: { - type: "string", + { + name: "code", + in: "path", + required: true, + schema: { + type: "string", + }, }, - }, - { - name: "page", - in: "query", - required: false, - schema: { - type: "integer", - default: 1, + { + name: "page", + in: "query", + required: false, + schema: { + type: "integer", + default: 1, + }, }, - }, - { - name: "pageSize", - in: "query", - required: false, - schema: { - type: "integer", - default: 100, + { + name: "pageSize", + in: "query", + required: false, + schema: { + type: "integer", + default: 100, + }, + }, + ], + responses: { + 200: { + description: "Successful response", + }, + 400: { + description: "Bad request", + }, + 500: { + description: "Internal server error", }, - }, - ], - responses: { - 200: { - description: "Successful response", - }, - 400: { - description: "Bad request", - }, - 500: { - description: "Internal server error", }, }, }, }, - }; - - json["components"] = { - securitySchemes: { - ApiKeyAuth: { - type: "apiKey", - in: "header", - name: "X-API-Key", + components: { + securitySchemes: { + ApiKeyAuth: { + type: "apiKey", + in: "header", + name: "X-API-Key", + }, }, }, + security: [ + { + ApiKeyAuth: [], + }, + ], }; - json["security"] = [ - { - ApiKeyAuth: [], - }, - ]; + return new HttpSuccess(json); } } diff --git a/src/controllers/ApiWebServiceController.ts b/src/controllers/ApiWebServiceController.ts index 124f0914..672d3169 100644 --- a/src/controllers/ApiWebServiceController.ts +++ b/src/controllers/ApiWebServiceController.ts @@ -8,7 +8,8 @@ import { isPermissionRequest } from "../middlewares/authWebService"; import { RequestWithUserWebService } from "../middlewares/user"; import { OrgRevision } from "../entities/OrgRevision"; import { ApiHistory } from "../entities/ApiHistory"; -@Route("api/v2/org/api-service") +import { SystemCode } from "./../interfaces/api-type"; +@Route("api/v1/org/api-service") @Tags("ApiKey") @Security("webServiceAuth") @Response( @@ -27,7 +28,8 @@ export class ApiWebServiceController extends Controller { @Get("/:system/:code") async listAttribute( @Request() request: RequestWithUserWebService, - @Path("system") system: "registry" | "registry_emp" | "registry_temp" | "organization", + @Path("system") + system: SystemCode, @Path("code") code: string, @Query("page") page: number = 1, @Query("pageSize") pageSize: number = 100, @@ -50,18 +52,18 @@ export class ApiWebServiceController extends Controller { const offset = (page - 1) * pageSize; const propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`); - let Main: string = ""; + let tbMain: string = ""; let condition: string = "1=1"; if (system == "registry") { - Main = "Profile"; + tbMain = "Profile"; } else if (system == "registry_emp") { - Main = "ProfileEmployee"; + tbMain = "ProfileEmployee"; condition = `ProfileEmployee.employeeClass = "PERM"`; } else if (system == "registry_temp") { - Main = "ProfileEmployee"; + tbMain = "ProfileEmployee"; condition = `ProfileEmployee.employeeClass = "TEMP"`; } else { - Main = "OrgRoot"; + tbMain = "OrgRoot"; const revision = await this.orgRevisionRepository.findOne({ select: ["id"], where: { orgRevisionIsCurrent: true, orgRevisionIsDraft: false }, @@ -69,7 +71,7 @@ export class ApiWebServiceController extends Controller { condition = `OrgRoot.orgRevisionId = "${revision?.id}"`; } - const repo = AppDataSource.getRepository(Main); + const repo = AppDataSource.getRepository(tbMain); const metadata = repo.metadata; const relationMap: Record = {}; @@ -80,17 +82,17 @@ export class ApiWebServiceController extends Controller { // ดึงเฉพาะตารางรอง (ถ้าเลือกไว้) let propertyOtherKey: any[] = []; propertyOtherKey = [ - ...new Set(propertyKey.map((x) => x.split(".")[0]).filter((tb) => tb !== Main)), + ...new Set(propertyKey.map((x) => x.split(".")[0]).filter((tb) => tb !== tbMain)), ]; - const queryBuilder = repo.createQueryBuilder(Main); + const queryBuilder = repo.createQueryBuilder(tbMain); // join กับตารารอง if (propertyOtherKey.length > 0) { propertyOtherKey.forEach((tb) => { const relationName = relationMap[tb]; if (relationName) { - queryBuilder.leftJoin(`${Main}.${relationName}`, tb); + queryBuilder.leftJoin(`${tbMain}.${relationName}`, tb); } }); } @@ -105,8 +107,8 @@ export class ApiWebServiceController extends Controller { const primaryColumns = metadata.primaryColumns; primaryColumns.forEach((col) => { pk = col.propertyName; - if (!propertyKey.includes(`${Main}.${pk}`)) { - propertyKey.push(`${Main}.${pk}`); + if (!propertyKey.includes(`${tbMain}.${pk}`)) { + propertyKey.push(`${tbMain}.${pk}`); } }); @@ -133,7 +135,7 @@ export class ApiWebServiceController extends Controller { return x; }); - // console.log("queryBuilder ===> ", queryBuilder.getQuery()); + console.log("queryBuilder ===> ", queryBuilder.getQuery()); // save api history after query success const history = { diff --git a/src/interfaces/api-type.ts b/src/interfaces/api-type.ts new file mode 100644 index 00000000..0ddff375 --- /dev/null +++ b/src/interfaces/api-type.ts @@ -0,0 +1,16 @@ +type SystemCode = "registry" | "registry_emp" | "registry_temp" | "organization" | "position"; + +interface SystemDefinition { + code: SystemCode; + name: string; +} + +interface EntityDefinition { + name: string; + repository: any; + description: string; + isMain?: boolean; + system: SystemCode[]; +} + +export { SystemCode, SystemDefinition, EntityDefinition };