Merge branch 'develop' into adiDev
This commit is contained in:
commit
44be85a749
11 changed files with 2297 additions and 2 deletions
728
src/controllers/ApiManageController.ts
Normal file
728
src/controllers/ApiManageController.ts
Normal file
|
|
@ -0,0 +1,728 @@
|
|||
import { ApiKey } from "./../entities/ApiKey";
|
||||
import { ApiAttribute } from "./../entities/ApiAttribute";
|
||||
import { CreateApi } from "./../entities/ApiName";
|
||||
import { PosMaster } from "../entities/PosMaster";
|
||||
import { OrgChild4 } from "../entities/OrgChild4";
|
||||
import { OrgChild3 } from "../entities/OrgChild3";
|
||||
import { OrgChild2 } from "../entities/OrgChild2";
|
||||
import { OrgChild1 } from "../entities/OrgChild1";
|
||||
import { OrgRoot } from "../entities/OrgRoot";
|
||||
import { ProfileEmployee } from "../entities/ProfileEmployee";
|
||||
import { ProfileSalary } from "../entities/ProfileSalary";
|
||||
import { ProfileOther } from "../entities/ProfileOther";
|
||||
import { ProfileNopaid } from "../entities/ProfileNopaid";
|
||||
import { ProfileLeave } from "../entities/ProfileLeave";
|
||||
import { ProfileInsignia } from "../entities/ProfileInsignia";
|
||||
import { ProfileHonor } from "../entities/ProfileHonor";
|
||||
import { ProfileGovernment } from "../entities/ProfileGovernment";
|
||||
import { ProfileFamilyMother } from "../entities/ProfileFamilyMother";
|
||||
import { ProfileFamilyFather } from "../entities/ProfileFamilyFather";
|
||||
import { ProfileFamilyCouple } from "../entities/ProfileFamilyCouple";
|
||||
import { ProfileEducation } from "../entities/ProfileEducation";
|
||||
import { ProfileDuty } from "../entities/ProfileDuty";
|
||||
import { ProfileDiscipline } from "../entities/ProfileDiscipline";
|
||||
import { ProfileDevelopment } from "../entities/ProfileDevelopment";
|
||||
import { ProfileChildren } from "../entities/ProfileChildren";
|
||||
import { ProfileChangeName } from "../entities/ProfileChangeName";
|
||||
import { ProfileCertificate } from "../entities/ProfileCertificate";
|
||||
import { ProfileAssessment } from "../entities/ProfileAssessment";
|
||||
import { ProfileAbility } from "../entities/ProfileAbility";
|
||||
import {
|
||||
Controller,
|
||||
Post,
|
||||
Delete,
|
||||
Route,
|
||||
Security,
|
||||
Tags,
|
||||
Body,
|
||||
Path,
|
||||
Request,
|
||||
Response,
|
||||
Get,
|
||||
Put,
|
||||
Query,
|
||||
} from "tsoa";
|
||||
import { AppDataSource } from "../database/data-source";
|
||||
import HttpSuccess from "../interfaces/http-success";
|
||||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import HttpError from "../interfaces/http-error";
|
||||
import { RequestWithUser } from "../middlewares/user";
|
||||
import { ApiName } from "../entities/ApiName";
|
||||
import { Profile } from "../entities/Profile";
|
||||
|
||||
@Route("api/v1/org/api-manage")
|
||||
@Tags("ApiManage")
|
||||
@Security("bearerAuth")
|
||||
@Response(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
|
||||
)
|
||||
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 systems = [
|
||||
{
|
||||
code: "registry",
|
||||
name: "ข้อมูลทะเบียนประวัติข้าราชการ",
|
||||
},
|
||||
{
|
||||
code: "registry_emp",
|
||||
name: "ข้อมูลทะเบียนประวัติลูกจ้างประจำ",
|
||||
},
|
||||
{
|
||||
code: "registry_temp",
|
||||
name: "ข้อมูลทะเบียนประวัติลูกจ้างชั่วคราว",
|
||||
},
|
||||
{
|
||||
code: "organization",
|
||||
name: "ข้อมูลโครงสร้างและอัตรากำลัง",
|
||||
},
|
||||
];
|
||||
/**
|
||||
* list systems
|
||||
* @summary รายการ systems
|
||||
*/
|
||||
@Get("/systems")
|
||||
async listSystems() {
|
||||
return new HttpSuccess(this.systems);
|
||||
}
|
||||
|
||||
/**
|
||||
* list fields by systems
|
||||
* @summary รายการ fields ตาม systems
|
||||
*/
|
||||
@Get("/:system/fields")
|
||||
async listAttribute(
|
||||
@Request() req: RequestWithUser,
|
||||
@Path("system") system: "registry" | "registry_emp" | "registry_temp" | "organization",
|
||||
): Promise<HttpSuccess | HttpError> {
|
||||
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"],
|
||||
},
|
||||
];
|
||||
|
||||
const result = entities
|
||||
.filter((s) => s.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",
|
||||
)
|
||||
.map((column) => ({
|
||||
propertyName: column.propertyName,
|
||||
type: typeof column.type === "string" ? column.type : "string",
|
||||
comment: column.comment,
|
||||
key: column.propertyName,
|
||||
})),
|
||||
}));
|
||||
|
||||
return new HttpSuccess(result);
|
||||
} catch (error) {
|
||||
throw new HttpError(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
(error instanceof Error ? error.message : String(error)) ||
|
||||
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list api
|
||||
* @summary รายการ api
|
||||
*/
|
||||
@Get("/lists")
|
||||
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" | "" = "",
|
||||
): Promise<HttpSuccess | HttpError> {
|
||||
try {
|
||||
if (!req.user.role.includes("SUPER_ADMIN")) {
|
||||
throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้");
|
||||
}
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
const queryBuilder = AppDataSource.getRepository(ApiName)
|
||||
.createQueryBuilder("apiName")
|
||||
.select([
|
||||
"apiName.id",
|
||||
"apiName.name",
|
||||
"apiName.code",
|
||||
"apiName.pathApi",
|
||||
"apiName.methodApi",
|
||||
"apiName.system",
|
||||
"apiName.isActive",
|
||||
"apiName.createdAt",
|
||||
"apiName.lastUpdatedAt",
|
||||
]);
|
||||
|
||||
if (keyword?.trim()) {
|
||||
queryBuilder.where(
|
||||
"(apiName.name LIKE :keyword OR apiName.code LIKE :keyword OR apiName.pathApi LIKE :keyword)",
|
||||
{ keyword: `%${keyword.trim()}%` },
|
||||
);
|
||||
}
|
||||
|
||||
if (system) {
|
||||
queryBuilder.andWhere("apiName.system = :system", { system });
|
||||
}
|
||||
|
||||
const [apiNames, total] = await queryBuilder
|
||||
.skip(offset)
|
||||
.take(pageSize)
|
||||
.orderBy("apiName.lastUpdatedAt", "DESC")
|
||||
.addOrderBy("apiName.createdAt", "ASC")
|
||||
.getManyAndCount();
|
||||
|
||||
return new HttpSuccess({
|
||||
data: apiNames,
|
||||
total,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new HttpError(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
(error instanceof Error ? error.message : String(error)) ||
|
||||
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* สร้าง api
|
||||
* @summary สร้าง api
|
||||
*/
|
||||
@Post("")
|
||||
async createApi(
|
||||
@Request() req: RequestWithUser,
|
||||
@Body() apiData: CreateApi,
|
||||
): Promise<HttpSuccess | HttpError> {
|
||||
try {
|
||||
const queryRunner = AppDataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
if (!req.user.role.includes("SUPER_ADMIN")) {
|
||||
throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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 || "",
|
||||
})),
|
||||
);
|
||||
|
||||
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();
|
||||
}
|
||||
} catch (error) {
|
||||
throw new HttpError(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
(error instanceof Error ? error.message : String(error)) ||
|
||||
"เกิดข้อผิดพลาด ไม่สามารถบันทึกข้อมูลได้ กรุณาลองใหม่ในภายหลัง",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* รายละเอียด api ตามไอดี
|
||||
* @summary รายละเอียด api ตามไอดี
|
||||
*/
|
||||
@Get("/{id}")
|
||||
async getApiById(
|
||||
@Request() req: RequestWithUser,
|
||||
@Path("id") id: string,
|
||||
): Promise<HttpSuccess | HttpError> {
|
||||
try {
|
||||
if (!req.user.role.includes("SUPER_ADMIN")) {
|
||||
throw new HttpError(HttpStatusCode.FORBIDDEN, "คุณไม่มีสิทธิ์ในการเข้าถึงข้อมูลนี้");
|
||||
}
|
||||
|
||||
const apiName = await AppDataSource.getRepository(ApiName).findOne({
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
code: true,
|
||||
pathApi: true,
|
||||
methodApi: true,
|
||||
system: true,
|
||||
isActive: true,
|
||||
apiAttributes: true,
|
||||
},
|
||||
where: { id },
|
||||
relations: ["apiAttributes"],
|
||||
order: {
|
||||
apiAttributes: {
|
||||
ordering: "ASC",
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!apiName) {
|
||||
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ต้องการแก้ไข");
|
||||
}
|
||||
|
||||
const items = {
|
||||
...apiName,
|
||||
apiAttributes: apiName.apiAttributes.map((attr) => ({
|
||||
tbName: attr.tbName,
|
||||
propertyKey: attr.propertyKey,
|
||||
})),
|
||||
};
|
||||
return new HttpSuccess(items);
|
||||
} catch (error) {
|
||||
throw new HttpError(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
(error instanceof Error ? error.message : String(error)) ||
|
||||
"เกิดข้อผิดพลาด ไม่สามารถบันทึกข้อมูลได้ กรุณาลองใหม่ในภายหลัง",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* แก้ไข api
|
||||
* @summary แก้ไขข้อมูล api
|
||||
*/
|
||||
@Put("/{id}")
|
||||
async editApi(
|
||||
@Request() req: RequestWithUser,
|
||||
@Path("id") id: string,
|
||||
@Body() apiData: CreateApi,
|
||||
): Promise<HttpSuccess | HttpError> {
|
||||
try {
|
||||
const queryRunner = AppDataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
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();
|
||||
}
|
||||
} catch (error) {
|
||||
throw new HttpError(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
(error instanceof Error ? error.message : String(error)) ||
|
||||
"เกิดข้อผิดพลาด ไม่สามารถบันทึกข้อมูลได้ กรุณาลองใหม่ในภายหลัง",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ลบ api
|
||||
* @summary ลบข้อมูล api
|
||||
*/
|
||||
@Delete("/{id}")
|
||||
async deleteApi(
|
||||
@Request() req: RequestWithUser,
|
||||
@Path("id") id: string,
|
||||
): Promise<HttpSuccess | HttpError> {
|
||||
try {
|
||||
const queryRunner = AppDataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
}
|
||||
} catch (error) {
|
||||
throw new HttpError(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
(error instanceof Error ? error.message : String(error)) ||
|
||||
"เกิดข้อผิดพลาด ไม่สามารถบันทึกข้อมูลได้ กรุณาลองใหม่ในภายหลัง",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* list systems
|
||||
* @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",
|
||||
},
|
||||
{
|
||||
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"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "code",
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "page",
|
||||
in: "query",
|
||||
required: false,
|
||||
schema: {
|
||||
type: "integer",
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
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",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
json["components"] = {
|
||||
securitySchemes: {
|
||||
ApiKeyAuth: {
|
||||
type: "apiKey",
|
||||
in: "header",
|
||||
name: "X-API-Key",
|
||||
},
|
||||
},
|
||||
};
|
||||
json["security"] = [
|
||||
{
|
||||
ApiKeyAuth: [],
|
||||
},
|
||||
];
|
||||
return new HttpSuccess(json);
|
||||
}
|
||||
}
|
||||
82
src/controllers/ApiWebServiceController.ts
Normal file
82
src/controllers/ApiWebServiceController.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import {
|
||||
Controller,
|
||||
Post,
|
||||
Route,
|
||||
Security,
|
||||
Tags,
|
||||
Body,
|
||||
Path,
|
||||
Request,
|
||||
Response,
|
||||
Get,
|
||||
Query,
|
||||
} from "tsoa";
|
||||
import { AppDataSource } from "../database/data-source";
|
||||
import HttpSuccess from "../interfaces/http-success";
|
||||
import HttpStatusCode from "../interfaces/http-status";
|
||||
import HttpError from "../interfaces/http-error";
|
||||
import { ApiName } from "../entities/ApiName";
|
||||
import { Profile } from "../entities/Profile";
|
||||
import { isPermissionRequest } from "../middlewares/authWebService";
|
||||
import { RequestWithUserWebService } from "../middlewares/user";
|
||||
@Route("api/v2/org/api-service")
|
||||
@Tags("ApiKey")
|
||||
@Security("webServiceAuth")
|
||||
@Response(
|
||||
HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
"เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
|
||||
)
|
||||
export class ApiWebServiceController extends Controller {
|
||||
private apiNameRepository = AppDataSource.getRepository(ApiName);
|
||||
|
||||
/**
|
||||
* list fields by systems
|
||||
* @summary รายการ fields ตาม systems
|
||||
*/
|
||||
@Get("/:system/:code")
|
||||
async listAttribute(
|
||||
@Request() request: RequestWithUserWebService,
|
||||
@Path("system") system: "registry" | "registry_emp" | "registry_temp" | "organization",
|
||||
@Path("code") code: string,
|
||||
@Query("page") page: number = 1,
|
||||
@Query("pageSize") pageSize: number = 100,
|
||||
): Promise<HttpSuccess | HttpError> {
|
||||
// try {
|
||||
const apiName = await this.apiNameRepository.findOne({
|
||||
where: { code },
|
||||
select: ["id", "code", "methodApi", "system", "isActive"],
|
||||
relations: ["apiAttributes"],
|
||||
order: {
|
||||
apiAttributes: {
|
||||
ordering: "ASC",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!apiName || apiName.system != system || !apiName.isActive || apiName.methodApi != "GET") {
|
||||
throw new HttpError(HttpStatusCode.NOT_FOUND, "ไม่พบ API ที่ระบุ");
|
||||
}
|
||||
await isPermissionRequest(request, apiName.id);
|
||||
const offset = (page - 1) * pageSize;
|
||||
|
||||
const propertyKey = apiName.apiAttributes.map((attr) => `${attr.tbName}.${attr.propertyKey}`);
|
||||
const queryBuilder = AppDataSource.getRepository(Profile)
|
||||
.createQueryBuilder("Profile")
|
||||
.select(propertyKey);
|
||||
|
||||
const [items, total] = await queryBuilder
|
||||
.skip(offset)
|
||||
.take(pageSize)
|
||||
.orderBy("Profile.createdAt", "DESC")
|
||||
.getManyAndCount();
|
||||
|
||||
return new HttpSuccess({ items, total });
|
||||
// } catch (error) {
|
||||
// throw new HttpError(
|
||||
// HttpStatusCode.INTERNAL_SERVER_ERROR,
|
||||
// (error instanceof Error ? error.message : String(error)) ||
|
||||
// "เกิดข้อผิดพลาด ไม่สามารถแสดงรายการได้ กรุณาลองใหม่ในภายหลัง",
|
||||
// );
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
@ -1696,7 +1696,7 @@ export class ProfileController extends Controller {
|
|||
}
|
||||
else {
|
||||
portfolios = portfolios.map((x:any) => ({
|
||||
name: x.name ? x.name : "-",
|
||||
name: x.name ? Extension.ToThaiNumber(x.name) : "-",
|
||||
year: x.year ? Extension.ToThaiNumber(x.year.toString()) : "-"
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,9 @@ export const AppDataSource = new DataSource({
|
|||
logging: true,
|
||||
// timezone: "Z",
|
||||
entities:
|
||||
process.env.NODE_ENV !== "production" ? ["src/entities/*.ts"] : ["dist/entities/*{.ts,.js}"],
|
||||
process.env.NODE_ENV !== "production"
|
||||
? ["src/entities/**/*.ts"]
|
||||
: ["dist/entities/**/*{.ts,.js}"],
|
||||
migrations:
|
||||
process.env.NODE_ENV !== "production"
|
||||
? ["src/migration/**/*.ts"]
|
||||
|
|
|
|||
39
src/entities/ApiAttribute.ts
Normal file
39
src/entities/ApiAttribute.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { ApiName } from "./ApiName";
|
||||
import { Entity, Column, ManyToOne, JoinColumn } from "typeorm";
|
||||
import { EntityBase } from "./base/Base";
|
||||
|
||||
@Entity("apiAttribute")
|
||||
export class ApiAttribute extends EntityBase {
|
||||
@Column({
|
||||
nullable: false,
|
||||
comment: "ชื่อตาราง",
|
||||
length: 50,
|
||||
})
|
||||
tbName: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
comment: "คีย์ของแอตทริบิวต์",
|
||||
length: 255,
|
||||
})
|
||||
propertyKey: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
comment: "ลำดับการแสดงผล",
|
||||
default: 0,
|
||||
type: "int",
|
||||
})
|
||||
ordering: number;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
comment: "ไอดีของ API Name",
|
||||
length: 36, // UUID length
|
||||
})
|
||||
apiNameId: string;
|
||||
|
||||
@ManyToOne(() => ApiName, (apiName) => apiName.apiAttributes)
|
||||
@JoinColumn({ name: "apiNameId" })
|
||||
apiName: ApiName;
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { ApiAttribute } from "./ApiAttribute";
|
||||
import { Entity, Column, ManyToMany, JoinTable, OneToMany } from "typeorm";
|
||||
import { EntityBase } from "./base/Base";
|
||||
import { ApiKey } from "./ApiKey";
|
||||
|
|
@ -29,10 +30,61 @@ export class ApiName extends EntityBase {
|
|||
})
|
||||
methodApi: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
comment: "code สำหรับการเรียก API",
|
||||
length: 8,
|
||||
})
|
||||
code: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
comment: "code ระบบสำหรับการเรียก API",
|
||||
length: 50,
|
||||
default: "registry",
|
||||
})
|
||||
system: string;
|
||||
|
||||
@Column({
|
||||
nullable: false,
|
||||
comment: "สถานะการใช้งาน",
|
||||
type: "boolean",
|
||||
default: false,
|
||||
})
|
||||
isActive: boolean;
|
||||
|
||||
@ManyToMany(() => ApiKey, (apiKey) => apiKey.apiNames)
|
||||
@JoinTable()
|
||||
apiKeys: ApiKey[];
|
||||
|
||||
@OneToMany(() => ApiHistory, (v) => v.apiName)
|
||||
apiHistorys: ApiHistory[];
|
||||
|
||||
@OneToMany(() => ApiAttribute, (v) => v.apiName)
|
||||
apiAttributes: ApiAttribute[];
|
||||
}
|
||||
|
||||
export class CreateApi {
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column()
|
||||
methodApi: "GET" | "POST" | "PUT";
|
||||
|
||||
@Column()
|
||||
system: string;
|
||||
|
||||
@Column()
|
||||
isActive: boolean;
|
||||
|
||||
@Column()
|
||||
apiAttributes: CreateApiAttribute[];
|
||||
}
|
||||
|
||||
export class CreateApiAttribute {
|
||||
@Column()
|
||||
tbName: string;
|
||||
|
||||
@Column()
|
||||
propertyKey: string[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { createDecoder, createVerifier } from "fast-jwt";
|
|||
|
||||
import HttpError from "../interfaces/http-error";
|
||||
import HttpStatus from "../interfaces/http-status";
|
||||
import { handleWebServiceAuth } from "./authWebService";
|
||||
|
||||
if (!process.env.AUTH_PUBLIC_KEY && !process.env.AUTH_REALM_URL) {
|
||||
throw new Error("Require keycloak AUTH_PUBLIC_KEY or AUTH_REALM_URL.");
|
||||
|
|
@ -30,6 +31,11 @@ export async function expressAuthentication(
|
|||
return { preferred_username: "bypassed" };
|
||||
}
|
||||
|
||||
// เพิ่มการจัดการสำหรับ Web Service Authentication
|
||||
if (securityName === "webServiceAuth") {
|
||||
return await handleWebServiceAuth(request);
|
||||
}
|
||||
|
||||
if (securityName !== "bearerAuth") throw new Error("ไม่ทราบวิธีการยืนยันตัวตน");
|
||||
|
||||
const token = request.headers["authorization"]?.includes("Bearer ")
|
||||
|
|
|
|||
56
src/middlewares/authWebService.ts
Normal file
56
src/middlewares/authWebService.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { RequestWithUserWebService } from "./user";
|
||||
import { AppDataSource } from "../database/data-source";
|
||||
import { ApiKey } from "../entities/ApiKey";
|
||||
import * as express from "express";
|
||||
|
||||
import HttpError from "../interfaces/http-error";
|
||||
import HttpStatus from "../interfaces/http-status";
|
||||
|
||||
// เพิ่มฟังก์ชันสำหรับจัดการ Web Service Authentication
|
||||
export async function handleWebServiceAuth(request: express.Request) {
|
||||
// ตัวอย่างการใช้ API Key
|
||||
const apiKey = request.headers["x-api-key"] as string;
|
||||
|
||||
if (!apiKey) {
|
||||
throw new HttpError(HttpStatus.UNAUTHORIZED, "ไม่พบข้อมูลสำหรับยืนยันตัวตน");
|
||||
}
|
||||
|
||||
// ตรวจสอบ API Key กับฐานข้อมูล
|
||||
const apiKeyData = await AppDataSource.getRepository(ApiKey).findOne({
|
||||
select: { id: true, name: true, keyApi: true },
|
||||
where: { keyApi: apiKey },
|
||||
relations: ["apiNames"],
|
||||
});
|
||||
if (!apiKeyData) {
|
||||
throw new HttpError(HttpStatus.UNAUTHORIZED, "ไม่สามารถยืนยันตัวตนได้");
|
||||
}
|
||||
|
||||
// บันทึก log data สำหรับ web service
|
||||
if (!request.app.locals.logData) {
|
||||
request.app.locals.logData = {};
|
||||
}
|
||||
|
||||
request.app.locals.logData.id = apiKeyData.id;
|
||||
request.app.locals.logData.name = apiKeyData.name;
|
||||
request.app.locals.logData.accessApi = apiKeyData.apiNames.map((x) => x.id) ?? [];
|
||||
|
||||
// ส่งคืนข้อมูลผู้ใช้ที่ยืนยันตัวตน
|
||||
return {
|
||||
id: apiKeyData.id,
|
||||
name: apiKeyData.name,
|
||||
type: "web-service",
|
||||
accessApi: apiKeyData.apiNames.map((x) => x.id) ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
export function isPermissionRequest(
|
||||
request: RequestWithUserWebService,
|
||||
apiId: string,
|
||||
): Promise<boolean> {
|
||||
// ฟังก์ชันนี้ใช้เพื่อตรวจสอบสิทธิ์ของผู้ใช้ที่ร้องขอ API โดยตรวจสอบว่า user มีสิทธิ์เข้าถึง API ที่ร้องขอหรือไม่
|
||||
const hasPermission = request.user.accessApi.includes(apiId);
|
||||
if (!hasPermission) {
|
||||
throw new HttpError(HttpStatus.FORBIDDEN, "คุณไม่มีสิทธิ์เข้าถึง API นี้");
|
||||
}
|
||||
return Promise.resolve(hasPermission);
|
||||
}
|
||||
|
|
@ -11,3 +11,11 @@ export type RequestWithUser = Request & {
|
|||
role: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export type RequestWithUserWebService = Request & {
|
||||
user: {
|
||||
id: string;
|
||||
name: string;
|
||||
accessApi: string[];
|
||||
};
|
||||
};
|
||||
|
|
|
|||
1316
src/migration/1754549669635-update_api_name.ts
Normal file
1316
src/migration/1754549669635-update_api_name.ts
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -23,6 +23,12 @@
|
|||
"name": "Authorization",
|
||||
"description": "Keycloak Bearer Token",
|
||||
"in": "header"
|
||||
},
|
||||
"webServiceAuth": {
|
||||
"type": "apiKey",
|
||||
"name": "X-API-Key",
|
||||
"description": "API KEY สำหรับ Web Service",
|
||||
"in": "header"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue