From d8f2f59044f0c14f86c72a4e7a5cc75b05c39840 Mon Sep 17 00:00:00 2001 From: AnandaTon Date: Tue, 28 May 2024 16:06:09 +0700 Subject: [PATCH 01/13] =?UTF-8?q?=E0=B9=81=E0=B8=81=E0=B9=89=E0=B9=84?= =?UTF-8?q?=E0=B8=82=E0=B8=8A=E0=B8=B7=E0=B9=88=E0=B8=AD=E0=B8=95=E0=B8=B1?= =?UTF-8?q?=E0=B8=A7=E0=B9=81=E0=B8=9B=E0=B8=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/ProfileController.ts | 24 ++++++++++---------- src/controllers/ProfileEmployeeController.ts | 14 ++++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index c5010d98..4aac4fbf 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -301,7 +301,7 @@ export class ProfileController extends Controller { const _child3 = child3 ? `${child3.orgChild3Name}/` : ""; const _child4 = child4 ? `${child4.orgChild4Name}/` : ""; - const profile = { + const Profile = { CitizenId: profiles?.citizenId ?? null, Prefix: profiles?.prefix != null ? profiles.prefix : "", FirstName: profiles?.firstName != null ? profiles.firstName : "", @@ -362,7 +362,7 @@ export class ProfileController extends Controller { where: { profileId: id }, select: ["certificateType", "issuer", "certificateNo", "issueDate"], }); - const cert = certs.map((item) => ({ + const Cert = certs.map((item) => ({ CertificateType: item.certificateType ?? null, Issuer: item.issuer ?? null, CertificateNo: item.certificateNo ?? null, @@ -372,7 +372,7 @@ export class ProfileController extends Controller { select: ["startDate", "endDate", "place", "department"], where: { profileId: id }, }); - const training = trainings.map((item) => ({ + const Training = trainings.map((item) => ({ institute: item.department ?? null, start: Extension.ToThaiShortDate(item.startDate) ?? null, end: Extension.ToThaiShortDate(item.endDate) ?? null, @@ -385,7 +385,7 @@ export class ProfileController extends Controller { select: ["refCommandDate", "refCommandNo", "detail"], where: { profileId: id }, }); - const discipline = disciplines.map((item) => ({ + const Discipline = disciplines.map((item) => ({ DisciplineYear: new Date(item.refCommandDate).getFullYear() ?? null, DisciplineDetail: item.detail ?? null, RefNo: item.refCommandNo ?? null, @@ -395,7 +395,7 @@ export class ProfileController extends Controller { select: ["startDate", "endDate", "educationLevel", "degree", "field", "institute"], where: { profileId: id }, }); - const education = educations.map((item) => ({ + const Education = educations.map((item) => ({ Institute: item.institute ?? null, Start: new Date(item.startDate).getFullYear() ?? null, End: new Date(item.endDate).getFullYear() ?? null, @@ -416,7 +416,7 @@ export class ProfileController extends Controller { where: { profileId: id }, }); - const salary = salarys.map((item) => ({ + const Salary = salarys.map((item) => ({ SalaryDate: Extension.ToThaiShortDate(item.date) ?? null, Position: item.position ?? null, PosNo: item.posNo ?? null, @@ -429,12 +429,12 @@ export class ProfileController extends Controller { OcFullPath: `${_child4}${_child3}${_child2}${_child1}${_root}`, })); return new HttpSuccess({ - profile, - cert, - training, - discipline, - education, - salary, + Profile, + Cert, + Training, + Discipline, + Education, + Salary, }); } /** diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index 1918c3d7..d615bbf5 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -314,7 +314,7 @@ export class ProfileEmployeeController extends Controller { const _child3 = child3 ? `${child3.orgChild3Name}/` : ""; const _child4 = child4 ? `${child4.orgChild4Name}/` : ""; - const profile = { + const Profile = { CitizenId: profiles?.citizenId ?? null, Prefix: profiles?.prefix != null ? profiles.prefix : "", FirstName: profiles?.firstName != null ? profiles.firstName : "", @@ -375,7 +375,7 @@ export class ProfileEmployeeController extends Controller { where: { profileEmployeeId: id }, select: ["certificateType", "issuer", "certificateNo", "issueDate"], }); - const cert = certs.map((item) => ({ + const Cert = certs.map((item) => ({ CertificateType: item.certificateType ?? null, Issuer: item.issuer ?? null, CertificateNo: item.certificateNo ?? null, @@ -385,7 +385,7 @@ export class ProfileEmployeeController extends Controller { select: ["startDate", "endDate", "place", "department"], where: { profileEmployeeId: id }, }); - const training = trainings.map((item) => ({ + const Training = trainings.map((item) => ({ institute: item.department ?? null, start: Extension.ToThaiShortDate(item.startDate) ?? null, end: Extension.ToThaiShortDate(item.endDate) ?? null, @@ -398,7 +398,7 @@ export class ProfileEmployeeController extends Controller { select: ["refCommandDate", "refCommandNo", "detail"], where: { profileEmployeeId: id }, }); - const discipline = disciplines.map((item) => ({ + const Discipline = disciplines.map((item) => ({ DisciplineYear: new Date(item.refCommandDate).getFullYear() ?? null, DisciplineDetail: item.detail ?? null, RefNo: item.refCommandNo ?? null, @@ -408,7 +408,7 @@ export class ProfileEmployeeController extends Controller { select: ["startDate", "endDate", "educationLevel", "degree", "field", "institute"], where: { profileEmployeeId: id }, }); - const education = educations.map((item) => ({ + const Education = educations.map((item) => ({ Institute: item.institute ?? null, Start: new Date(item.startDate).getFullYear() ?? null, End: new Date(item.endDate).getFullYear() ?? null, @@ -429,7 +429,7 @@ export class ProfileEmployeeController extends Controller { where: { profileEmployeeId: id }, }); - const salary = salarys.map((item) => ({ + const Salary = salarys.map((item) => ({ SalaryDate: Extension.ToThaiShortDate(item.date) ?? null, Position: item.position ?? null, PosNo: item.posNo ?? null, @@ -441,7 +441,7 @@ export class ProfileEmployeeController extends Controller { FullName: `${profiles?.prefix} ${profiles?.firstName} ${profiles?.lastName}`, OcFullPath: `${_child4}${_child3}${_child2}${_child1}${_root}`, })); - return new HttpSuccess({ profile, cert, training, discipline, education, salary }); + return new HttpSuccess({ Profile, Cert, Training, Discipline, Education, Salary }); } /** From 9ee8240b1c618f12c1c0ca8ed4a76572af95dcc7 Mon Sep 17 00:00:00 2001 From: Bright Date: Tue, 28 May 2024 16:23:09 +0700 Subject: [PATCH 02/13] OrgDotnetController (update) --- .../OrganizationDotnetController.ts | 309 ++++++++++++------ 1 file changed, 212 insertions(+), 97 deletions(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index 58fcc341..04feff71 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -19,39 +19,12 @@ import { AppDataSource } from "../database/data-source"; import HttpSuccess from "../interfaces/http-success"; import HttpStatus from "../interfaces/http-status"; import HttpError from "../interfaces/http-error"; -import { - Profile, - CreateProfile, - UpdateProfile, - ProfileHistory, - CreateProfileAllFields, -} from "../entities/Profile"; +import { RequestWithUser } from "../middlewares/user"; +import { Profile } from "../entities/Profile"; import { Brackets, IsNull, Like, Not } from "typeorm"; import { OrgRevision } from "../entities/OrgRevision"; -import { PosMaster } from "../entities/PosMaster"; -import { PosLevel } from "../entities/PosLevel"; -import { PosType } from "../entities/PosType"; -import { - calculateAge, - calculateRetireDate, - calculateRetireLaw, - calculateRetireYear, -} from "../interfaces/utils"; -import { RequestWithUser } from "../middlewares/user"; -import { Position } from "../entities/Position"; +import { OrgRoot } from "../entities/OrgRoot"; import { ProfileEmployee } from "../entities/ProfileEmployee"; -import { Province } from "../entities/Province"; -import { District } from "../entities/District"; -import { SubDistrict } from "../entities/SubDistrict"; -import { ProfileCertificate } from "../entities/ProfileCertificate"; -import { ProfileTraining } from "../entities/ProfileTraining"; -import { ProfileDiscipline } from "../entities/ProfileDiscipline"; -import { ProfileEducation } from "../entities/ProfileEducation"; -import { ProfileSalary } from "../entities/ProfileSalary"; -import { ProfileFamilyCouple } from "../entities/ProfileFamilyCouple"; -import { ProfileFamilyMother } from "../entities/ProfileFamilyMother"; -import { ProfileFamilyFather } from "../entities/ProfileFamilyFather"; -import Extension from "../interfaces/extension"; @Route("api/v1/org/dotnet") @Tags("Dotnet") @@ -62,25 +35,9 @@ import Extension from "../interfaces/extension"; ) @SuccessResponse(HttpStatus.OK, "สำเร็จ") export class OrganizationDotnetController extends Controller { + private orgRootRepo = AppDataSource.getRepository(OrgRoot); private orgRevisionRepo = AppDataSource.getRepository(OrgRevision); - private posMasterRepo = AppDataSource.getRepository(PosMaster); private profileRepo = AppDataSource.getRepository(Profile); - private profileEmpRepo = AppDataSource.getRepository(ProfileEmployee); - private profileHistoryRepo = AppDataSource.getRepository(ProfileHistory); - private posLevelRepo = AppDataSource.getRepository(PosLevel); - private posTypeRepo = AppDataSource.getRepository(PosType); - private positionRepository = AppDataSource.getRepository(Position); - private provinceRepository = AppDataSource.getRepository(Province); - private districtRepository = AppDataSource.getRepository(District); - private subDistrict = AppDataSource.getRepository(SubDistrict); - private certificateRepository = AppDataSource.getRepository(ProfileCertificate); - private profileFamilyCoupleRepository = AppDataSource.getRepository(ProfileFamilyCouple); - private profileFamilyMotherRepository = AppDataSource.getRepository(ProfileFamilyMother); - private profileFamilyFatherRepository = AppDataSource.getRepository(ProfileFamilyFather); - private trainingRepository = AppDataSource.getRepository(ProfileTraining); - private disciplineRepository = AppDataSource.getRepository(ProfileDiscipline); - private educationRepository = AppDataSource.getRepository(ProfileEducation); - private salaryRepository = AppDataSource.getRepository(ProfileSalary); /** * 1. API Search Profile @@ -89,7 +46,7 @@ export class OrganizationDotnetController extends Controller { * */ @Post("search") - public async searchProfile( + public async SearchProfile( @Body() body: { citizenId?: string | null; @@ -118,71 +75,229 @@ export class OrganizationDotnetController extends Controller { }), ); } - - const profiles = await queryBuilder.getMany(); - - if (!profiles.length) { - throw new HttpError(HttpStatus.BAD_REQUEST, "ไม่พบข้อมูลโปรไฟล์"); - } - - const formattedProfiles = profiles.map((profile) => ({ - avatar: profile.avatar, - avatarName: profile.avatarName, - rank: profile.rank, - prefix: profile.prefix, - firstName: profile.firstName, - lastName: profile.lastName, - citizenId: profile.citizenId, - position: profile.position, - posLevelId: profile.posLevelId, - posLevelName: profile.posLevel.posLevelName, - posTypeId: profile.posTypeId, - posTypeName: profile.posType.posTypeName, - email: profile.email, - phone: profile.phone, - keycloak: profile.keycloak, - isProbation: profile.isProbation, - isLeave: profile.isLeave, - leaveReason: profile.leaveReason, - dateRetire: profile.dateRetire, - dateAppoint: profile.dateAppoint, - dateRetireLaw: profile.dateRetireLaw, - dateStart: profile.dateStart, - govAgeAbsent: profile.govAgeAbsent, - govAgePlus: profile.govAgePlus, - birthDate: profile.birthDate, - reasonSameDate: profile.reasonSameDate, - ethnicity: profile.ethnicity, - telephoneNumber: profile.telephoneNumber, - nationality: profile.nationality, - gender: profile.gender, - relationship: profile.relationship, - religion: profile.religion, - bloodGroup: profile.bloodGroup, - })); - - return new HttpSuccess(formattedProfiles); + const profiles = await queryBuilder.getMany(); + return new HttpSuccess(profiles); } + /** + * 6. Search Employee + * + * @summary 6. Search Employee + * + */ + @Post("search-employee") + public async SearchProfileEmployee( + @Body() + body: { + citizenId?: string | null; + firstName?: string | null; + lastName?: string | null; + }, + ) { + const profileRepository = AppDataSource.getRepository(ProfileEmployee); + const queryBuilder = profileRepository + .createQueryBuilder("profile") + .leftJoinAndSelect("profile.posLevel", "posLevel") + .leftJoinAndSelect("profile.posType", "posType"); + + if (body.citizenId || body.firstName || body.lastName) { + queryBuilder.where( + new Brackets((qb) => { + if (body.citizenId) { + qb.orWhere("profile.citizenId LIKE :citizenId", { citizenId: `%${body.citizenId}%` }); + } + if (body.firstName) { + qb.orWhere("profile.firstName LIKE :firstName", { firstName: `%${body.firstName}%` }); + } + if (body.lastName) { + qb.orWhere("profile.lastName LIKE :lastName", { lastName: `%${body.lastName}%` }); + } + }), + ); + } + const profileEmp = await queryBuilder.getMany(); + return new HttpSuccess(profileEmp); + } + + /** + * 2. API ข้อมูลหน่วยงานตามโครงสร้าง + * + * @summary 2. API ข้อมูลหน่วยงานตามโครงสร้าง + * + * @param {string} id Id หน่วยงาน + */ + @Get("org/{id}") + async GetOrganizationById(@Path() id: string) { + const orgRoot = await this.orgRootRepo.findOne({ + where: { id: id }, + }); + if (!orgRoot) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); + return new HttpSuccess(orgRoot); + } + + @Get("agency/{id}") + async GetOrgAgencyById(@Path() id: string) { + const orgRoot = await this.orgRootRepo.findOne({ + where: { id: id }, + }); + if (!orgRoot) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); + return new HttpSuccess(orgRoot); + } + + @Get("go-agency/{id}") + async GetOrgGoAgencyById(@Path() id: string) { + const orgRoot = await this.orgRootRepo.findOne({ + where: { id: id }, + }); + if (!orgRoot) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); + return new HttpSuccess(orgRoot); + } + /** * 3. API Get Profile จาก keycloak id * * @summary 3. API Get Profile จาก keycloak id * + * @param {string} keycloakId Id keycloak */ - @Get("keycloak/{id}") - async getProfileByKeycloakId(@Path() id: string) { + @Get("keycloak/{keycloakId}") + async GetProfileByKeycloakIdAsync(@Path() keycloakId: string) { const profile = await this.profileRepo.findOne({ relations: { posLevel: true, posType: true, }, - where: { keycloak: id }, + where: { keycloak: keycloakId }, }); - if (!profile) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); - return new HttpSuccess(profile); } + /** + * 7.Get ข้อมูล GetUserFullName + * + * @summary 7.1 Get ข้อมูล GetUserFullName + * + * @param {string} keycloakId Id keycloak + */ + @Get("user-fullname/{keycloakId}") + async GetUserFullName(@Path() keycloakId: string) { + const profile = await this.profileRepo.findOne({ + where: { keycloak: keycloakId }, + select: ["prefix", "firstName", "lastName"] + }); + if (!profile) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); + const fullName = profile? `${profile.prefix}${profile.firstName} ${profile.lastName}` : "-"; + return new HttpSuccess(fullName); + } + + /** + * 7.Get ข้อมูล GetUserOCId + * + * @summary 7.2 Get ข้อมูล GetUserOCId + * + * @param {string} keycloakId Id keycloak + */ + @Get("user-oc/{keycloakId}") + async getProfileByKeycloak(@Path() keycloakId: string) { + const profile = await this.profileRepo.findOne({ + where: { keycloak: keycloakId }, + relations: ["posLevel", "posType", "current_holders", "current_holders.orgRoot"], + }); + if (!profile) { + throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลบุคคลนี้ในระบบ"); + } + + const orgRevisionPublish = await this.orgRevisionRepo + .createQueryBuilder("orgRevision") + .where("orgRevision.orgRevisionIsDraft = false") + .andWhere("orgRevision.orgRevisionIsCurrent = true") + .getOne(); + if (!orgRevisionPublish) { + throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบแบบร่างโครงสร้าง"); + } + + const root = + profile.current_holders == null || + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot == null + ? null + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot; + + const _profile: any = { + profileId: profile.id, + prefix: profile.prefix, + rank: profile.rank, + avatar: profile.avatar, + avatarName: profile.avatarName, + firstName: profile.firstName, + lastName: profile.lastName, + citizenId: profile.citizenId, + birthDate: profile.birthDate, + position: profile.position, + rootId: root == null ? null : root.id, + root: root == null ? null : root.orgRootName, + rootShortName: root == null ? null : root.orgRootShortName, + }; + return new HttpSuccess(_profile); + } + + /** + * 8. หา root OC Id + * + * @summary 8. หา root OC Id + * + * @param {string} ocId Id หน่วยงาน + */ + @Get("root-oc/{ocId}") + async GetRootOcId(@Path() ocId: string) { + const orgRoot = await this.orgRootRepo.findOne({ + where: { id: ocId }, + }); + if (!orgRoot) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); + const root = orgRoot? orgRoot.id : ""; + return new HttpSuccess(root); + } + + /** + * 5. เอารายชื่อคนที่มีการ ,map keycloak id แล้ว + * + * @summary 5. เอารายชื่อคนที่มีการ ,map keycloak id แล้ว + * + */ + @Get("keycloak") + async GetProfileWithKeycloak() { + const profile = await this.profileRepo.find({ + where: { keycloak: Not(IsNull()) || Not(""), }, + }); + return new HttpSuccess(profile); + } + + /** + * 4. API Update รอบการลงเวลา ในตาราง profile + * + * @summary 4. API Update รอบการลงเวลา ในตาราง profile + * + */ + @Put("update-dutytime") + async UpdateDutyTimeAsync( + @Request() req: RequestWithUser, + @Body() body: { + profileId: string; + roundId: string; + effectiveDate: Date; + } + ) { + const profile = await this.profileRepo.findOne({ + where: { id: body.profileId }, + }); + if (!profile) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); + Object.assign(profile, body); + profile.dutyTimeId = body.roundId; + profile.dutyTimeEffectiveDate = body.effectiveDate; + profile.lastUpdateUserId = req.user.sub; + profile.lastUpdateFullName = req.user.name; + await this.profileRepo.save(profile); + return new HttpSuccess(); + } + } From 0f82f9ae78d7b4f6f2a0bbaf31665785adcd6934 Mon Sep 17 00:00:00 2001 From: AnandaTon Date: Wed, 29 May 2024 09:40:58 +0700 Subject: [PATCH 03/13] Update ProfileController.ts --- src/controllers/ProfileController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 4aac4fbf..14299b1d 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -355,7 +355,7 @@ export class ProfileController extends Controller { AppointDate: profiles?.dateAppoint, BirthDate: profiles?.birthDate ? Extension.ToThaiShortDate(profiles.birthDate) : null, RetireDate: profiles?.dateRetireLaw, - AvatarId: profiles?.avatar ?? null, + // AvatarId: profiles?.avatar ?? null, }; const certs = await this.certificateRepository.find({ From 4d9053dcdc9d978f180cd9fd39ede6f52dd8a071 Mon Sep 17 00:00:00 2001 From: Kittapath Date: Wed, 29 May 2024 10:14:11 +0700 Subject: [PATCH 04/13] no message --- src/controllers/ProfileController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 14299b1d..f51adc5b 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -424,7 +424,7 @@ export class ProfileController extends Controller { RefAll: item.refCommandNo ?? null, PositionType: item.positionType ?? null, PositionLevel: item.positionLevel ?? null, - PositionAmount: item.positionSalaryAmount ?? null, + PositionAmount: item.positionSalaryAmount == null? null: item.positionSalaryAmount.toString(), FullName: `${profiles?.prefix} ${profiles?.firstName} ${profiles?.lastName}`, OcFullPath: `${_child4}${_child3}${_child2}${_child1}${_root}`, })); From c64ffd8ea8c3c7f02b3c07b072e317a35941d20f Mon Sep 17 00:00:00 2001 From: AnandaTon Date: Wed, 29 May 2024 11:00:13 +0700 Subject: [PATCH 05/13] =?UTF-8?q?=E0=B9=81=E0=B8=81=E0=B9=89=E0=B9=84?= =?UTF-8?q?=E0=B8=82=20type=20=E0=B9=80=E0=B8=9B=E0=B9=8A=E0=B8=99=20Strin?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/ProfileController.ts | 21 +++++++++++--------- src/controllers/ProfileEmployeeController.ts | 20 ++++++++++--------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index f51adc5b..10b24067 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -307,13 +307,15 @@ export class ProfileController extends Controller { FirstName: profiles?.firstName != null ? profiles.firstName : "", LastName: profiles?.lastName != null ? profiles.lastName : "", FullName: `${profiles?.prefix} ${profiles?.firstName} ${profiles?.lastName}`, - BirthDay: profiles?.birthDate ? new Date(profiles.birthDate).getDate() : null, + BirthDay: profiles?.birthDate ? new Date(profiles.birthDate).getDate().toString() : null, BirthDayText: profiles.birthDate != null ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate)) : "", - BirthMonth: profiles?.birthDate ? new Date(profiles.birthDate).getMonth() + 1 : null, // Months are zero-based - BirthYear: profiles?.birthDate ? new Date(profiles.birthDate).getFullYear() : null, + BirthMonth: profiles?.birthDate + ? new Date(profiles.birthDate).getMonth() + (1).toString() + : null, // Months are zero-based + BirthYear: profiles?.birthDate ? new Date(profiles.birthDate).getFullYear().toString() : null, BirthYearText: profiles.birthDate != null ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate)) @@ -374,8 +376,8 @@ export class ProfileController extends Controller { }); const Training = trainings.map((item) => ({ institute: item.department ?? null, - start: Extension.ToThaiShortDate(item.startDate) ?? null, - end: Extension.ToThaiShortDate(item.endDate) ?? null, + start: Extension.ToThaiShortDate(item.startDate).toString() ?? null, + end: Extension.ToThaiShortDate(item.endDate).toString() ?? null, level: "", degree: "", field: item.place ?? null, @@ -386,7 +388,7 @@ export class ProfileController extends Controller { where: { profileId: id }, }); const Discipline = disciplines.map((item) => ({ - DisciplineYear: new Date(item.refCommandDate).getFullYear() ?? null, + DisciplineYear: new Date(item.refCommandDate).getFullYear().toString() ?? null, DisciplineDetail: item.detail ?? null, RefNo: item.refCommandNo ?? null, })); @@ -397,8 +399,8 @@ export class ProfileController extends Controller { }); const Education = educations.map((item) => ({ Institute: item.institute ?? null, - Start: new Date(item.startDate).getFullYear() ?? null, - End: new Date(item.endDate).getFullYear() ?? null, + Start: new Date(item.startDate).getFullYear().toString() ?? null, + End: new Date(item.endDate).getFullYear().toString() ?? null, Level: item.educationLevel ?? null, Degree: item.degree ?? null, Field: item.field ?? null, @@ -424,7 +426,8 @@ export class ProfileController extends Controller { RefAll: item.refCommandNo ?? null, PositionType: item.positionType ?? null, PositionLevel: item.positionLevel ?? null, - PositionAmount: item.positionSalaryAmount == null? null: item.positionSalaryAmount.toString(), + PositionAmount: + item.positionSalaryAmount == null ? null : item.positionSalaryAmount.toString(), FullName: `${profiles?.prefix} ${profiles?.firstName} ${profiles?.lastName}`, OcFullPath: `${_child4}${_child3}${_child2}${_child1}${_root}`, })); diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index d615bbf5..2007fe1e 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -320,13 +320,15 @@ export class ProfileEmployeeController extends Controller { FirstName: profiles?.firstName != null ? profiles.firstName : "", LastName: profiles?.lastName != null ? profiles.lastName : "", FullName: `${profiles?.prefix} ${profiles?.firstName} ${profiles?.lastName}`, - BirthDay: profiles?.birthDate ? new Date(profiles.birthDate).getDate() : null, + BirthDay: profiles?.birthDate ? new Date(profiles.birthDate).getDate().toString() : null, BirthDayText: profiles.birthDate != null ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate)) : "", - BirthMonth: profiles?.birthDate ? new Date(profiles.birthDate).getMonth() + 1 : null, // Months are zero-based - BirthYear: profiles?.birthDate ? new Date(profiles.birthDate).getFullYear() : null, + BirthMonth: profiles?.birthDate + ? new Date(profiles.birthDate).getMonth() + (1).toString() + : null, // Months are zero-based + BirthYear: profiles?.birthDate ? new Date(profiles.birthDate).getFullYear().toString() : null, BirthYearText: profiles.birthDate != null ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate)) @@ -368,7 +370,7 @@ export class ProfileEmployeeController extends Controller { AppointDate: profiles?.dateAppoint, BirthDate: profiles?.birthDate ? Extension.ToThaiShortDate(profiles.birthDate) : null, RetireDate: profiles?.dateRetireLaw, - AvatarId: profiles?.avatar ?? null, + // AvatarId: profiles?.avatar ?? null, }; const certs = await this.certificateRepository.find({ @@ -387,8 +389,8 @@ export class ProfileEmployeeController extends Controller { }); const Training = trainings.map((item) => ({ institute: item.department ?? null, - start: Extension.ToThaiShortDate(item.startDate) ?? null, - end: Extension.ToThaiShortDate(item.endDate) ?? null, + start: Extension.ToThaiShortDate(item.startDate).toString() ?? null, + end: Extension.ToThaiShortDate(item.endDate).toString() ?? null, level: "", degree: "", field: item.place ?? null, @@ -399,7 +401,7 @@ export class ProfileEmployeeController extends Controller { where: { profileEmployeeId: id }, }); const Discipline = disciplines.map((item) => ({ - DisciplineYear: new Date(item.refCommandDate).getFullYear() ?? null, + DisciplineYear: new Date(item.refCommandDate).getFullYear().toString() ?? null, DisciplineDetail: item.detail ?? null, RefNo: item.refCommandNo ?? null, })); @@ -410,8 +412,8 @@ export class ProfileEmployeeController extends Controller { }); const Education = educations.map((item) => ({ Institute: item.institute ?? null, - Start: new Date(item.startDate).getFullYear() ?? null, - End: new Date(item.endDate).getFullYear() ?? null, + Start: new Date(item.startDate).getFullYear().toString() ?? null, + End: new Date(item.endDate).getFullYear().toString() ?? null, Level: item.educationLevel ?? null, Degree: item.degree ?? null, Field: item.field ?? null, From dcca84ac96e3836b9c656e55950b2395a42b2e12 Mon Sep 17 00:00:00 2001 From: Bright Date: Wed, 29 May 2024 11:34:49 +0700 Subject: [PATCH 06/13] all node --- .../OrganizationDotnetController.ts | 156 +++++++++++++++++- 1 file changed, 154 insertions(+), 2 deletions(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index 04feff71..5ff254b4 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -25,6 +25,7 @@ import { Brackets, IsNull, Like, Not } from "typeorm"; import { OrgRevision } from "../entities/OrgRevision"; import { OrgRoot } from "../entities/OrgRoot"; import { ProfileEmployee } from "../entities/ProfileEmployee"; +import { Position } from "../entities/Position"; @Route("api/v1/org/dotnet") @Tags("Dotnet") @@ -38,6 +39,7 @@ export class OrganizationDotnetController extends Controller { private orgRootRepo = AppDataSource.getRepository(OrgRoot); private orgRevisionRepo = AppDataSource.getRepository(OrgRevision); private profileRepo = AppDataSource.getRepository(Profile); + private positionRepository = AppDataSource.getRepository(Position); /** * 1. API Search Profile @@ -174,7 +176,7 @@ export class OrganizationDotnetController extends Controller { } /** - * 7.Get ข้อมูล GetUserFullName + * 7.1 Get ข้อมูล GetUserFullName * * @summary 7.1 Get ข้อมูล GetUserFullName * @@ -192,7 +194,7 @@ export class OrganizationDotnetController extends Controller { } /** - * 7.Get ข้อมูล GetUserOCId + * 7.2 Get ข้อมูล GetUserOCId * * @summary 7.2 Get ข้อมูล GetUserOCId * @@ -238,6 +240,156 @@ export class OrganizationDotnetController extends Controller { root: root == null ? null : root.orgRootName, rootShortName: root == null ? null : root.orgRootShortName, }; + return new HttpSuccess(profile); + } + + /** + * 7.3 Get ข้อมูล GetUserOCId (all node) + * + * @summary 7.3 Get ข้อมูล GetUserOCId (all node) + * + * @param {string} keycloakId Id keycloak + */ + @Get("user-oc-all/{keycloakId}") + async getAllProfileByKeycloak(@Path() keycloakId: string) { + const profile = await this.profileRepo.findOne({ + where: { keycloak: keycloakId }, + relations: [ + "profileSalary", + "profileEducations", + "current_holders", + "current_holders.orgRoot", + "current_holders.orgChild1", + "current_holders.orgChild2", + "current_holders.orgChild3", + "current_holders.orgChild4", + ], + }); + if (!profile) { + throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูลบุคคลนี้ในระบบ"); + } + + const orgRevisionPublish = await this.orgRevisionRepo + .createQueryBuilder("orgRevision") + .where("orgRevision.orgRevisionIsDraft = false") + .andWhere("orgRevision.orgRevisionIsCurrent = true") + .getOne(); + if (!orgRevisionPublish) { + throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบแบบร่างโครงสร้าง"); + } + + const posMaster = + profile.current_holders == null || + profile.current_holders.length == 0 || + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id) == null + ? null + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id); + + const root = + profile.current_holders == null || + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot == null + ? null + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgRoot; + + const child1 = + profile.current_holders == null || + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild1 == + null + ? null + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild1; + + const child2 = + profile.current_holders == null || + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild2 == + null + ? null + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild2; + + const child3 = + profile.current_holders == null || + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild3 == + null + ? null + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild3; + + const child4 = + profile.current_holders == null || + profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild4 == + null + ? null + : profile.current_holders.find((x) => x.orgRevisionId == orgRevisionPublish.id)?.orgChild4; + + const position = await this.positionRepository.findOne({ + relations: ["posExecutive"], + where: { + posMasterId: posMaster?.id, + }, + }); + + const _profile: any = { + profileId: profile.id, + prefix: profile.prefix, + rank: profile.rank, + avatar: profile.avatar, + avatarName: profile.avatarName, + firstName: profile.firstName, + lastName: profile.lastName, + citizenId: profile.citizenId, + birthDate: profile.birthDate, + position: profile.position, + posMaster: posMaster == null ? null : posMaster.posMasterNo, + posMasterNo: posMaster == null ? null : posMaster.posMasterNo, + posLevelName: profile.posLevel == null ? null : profile.posLevel.posLevelName, + posLevelRank: profile.posLevel == null ? null : profile.posLevel.posLevelRank, + posLevelId: profile.posLevel == null ? null : profile.posLevel.id, + posTypeName: profile.posType == null ? null : profile.posType.posTypeName, + posTypeRank: profile.posType == null ? null : profile.posType.posTypeRank, + posTypeId: profile.posType == null ? null : profile.posType.id, + posExecutiveName: + position == null || position.posExecutive == null + ? null + : position.posExecutive.posExecutiveName, + posExecutivePriority: + position == null || position.posExecutive == null + ? null + : position.posExecutive.posExecutivePriority, + posExecutiveId: + position == null || position.posExecutive == null ? null : position.posExecutive.id, + rootId: root == null ? null : root.id, + root: root == null ? null : root.orgRootName, + rootShortName: root == null ? null : root.orgRootShortName, + child1Id: child1 == null ? null : child1.id, + child1: child1 == null ? null : child1.orgChild1Name, + child1ShortName: child1 == null ? null : child1.orgChild1ShortName, + child2Id: child2 == null ? null : child2.id, + child2: child2 == null ? null : child2.orgChild2Name, + child2ShortName: child2 == null ? null : child2.orgChild2ShortName, + child3Id: child3 == null ? null : child3.id, + child3: child3 == null ? null : child3.orgChild3Name, + child3ShortName: child3 == null ? null : child3.orgChild3ShortName, + child4Id: child4 == null ? null : child4.id, + child4: child4 == null ? null : child4.orgChild4Name, + child4ShortName: child4 == null ? null : child4.orgChild4ShortName, + node: null, + nodeId: null, + }; + + if (_profile.child4Id != null) { + _profile.node = 4; + _profile.nodeId = _profile.child4Id; + } else if (_profile.child3Id != null) { + _profile.node = 3; + _profile.nodeId = _profile.child3Id; + } else if (_profile.child2Id != null) { + _profile.node = 2; + _profile.nodeId = _profile.child2Id; + } else if (_profile.child1Id != null) { + _profile.node = 1; + _profile.nodeId = _profile.child1Id; + } else if (_profile.rootId != null) { + _profile.node = 0; + _profile.nodeId = _profile.rootId; + } return new HttpSuccess(_profile); } From 4cfe61f0a2826bb4d6e16551be8d1216cca18fd2 Mon Sep 17 00:00:00 2001 From: AnandaTon Date: Wed, 29 May 2024 11:39:56 +0700 Subject: [PATCH 07/13] =?UTF-8?q?=E0=B9=81=E0=B8=81=E0=B9=89=E0=B9=84?= =?UTF-8?q?=E0=B8=82=20type=20=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20Rank?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/ProfileController.ts | 5 +++-- src/controllers/ProfileEmployeeController.ts | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 10b24067..17796845 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -310,7 +310,7 @@ export class ProfileController extends Controller { BirthDay: profiles?.birthDate ? new Date(profiles.birthDate).getDate().toString() : null, BirthDayText: profiles.birthDate != null - ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate)) + ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate).toString()) : "", BirthMonth: profiles?.birthDate ? new Date(profiles.birthDate).getMonth() + (1).toString() @@ -318,7 +318,7 @@ export class ProfileController extends Controller { BirthYear: profiles?.birthDate ? new Date(profiles.birthDate).getFullYear().toString() : null, BirthYearText: profiles.birthDate != null - ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate)) + ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate).toString()) : "", Address: "", District: "", @@ -423,6 +423,7 @@ export class ProfileController extends Controller { Position: item.position ?? null, PosNo: item.posNo ?? null, Salary: "", + Rank: item.positionLevel ?? null, RefAll: item.refCommandNo ?? null, PositionType: item.positionType ?? null, PositionLevel: item.positionLevel ?? null, diff --git a/src/controllers/ProfileEmployeeController.ts b/src/controllers/ProfileEmployeeController.ts index 2007fe1e..5e82667e 100644 --- a/src/controllers/ProfileEmployeeController.ts +++ b/src/controllers/ProfileEmployeeController.ts @@ -323,7 +323,7 @@ export class ProfileEmployeeController extends Controller { BirthDay: profiles?.birthDate ? new Date(profiles.birthDate).getDate().toString() : null, BirthDayText: profiles.birthDate != null - ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate)) + ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate).toString()) : "", BirthMonth: profiles?.birthDate ? new Date(profiles.birthDate).getMonth() + (1).toString() @@ -331,7 +331,7 @@ export class ProfileEmployeeController extends Controller { BirthYear: profiles?.birthDate ? new Date(profiles.birthDate).getFullYear().toString() : null, BirthYearText: profiles.birthDate != null - ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate)) + ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate).toString()) : "", Address: "", District: "", @@ -436,6 +436,7 @@ export class ProfileEmployeeController extends Controller { Position: item.position ?? null, PosNo: item.posNo ?? null, Salary: "", + Rank: item.positionLevel ?? null, RefAll: item.refCommandNo ?? null, PositionType: item.positionType ?? null, PositionLevel: item.positionLevel ?? null, From 9004721331ab9760636933fae24001268d97a434 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" Date: Wed, 29 May 2024 14:01:53 +0700 Subject: [PATCH 08/13] UserController --- src/controllers/UserController.ts | 249 ++++++++++++++ src/keycloak/index.ts | 542 ++++++++++++++++++++++++++++++ 2 files changed, 791 insertions(+) create mode 100644 src/controllers/UserController.ts create mode 100644 src/keycloak/index.ts diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts new file mode 100644 index 00000000..ccfa676d --- /dev/null +++ b/src/controllers/UserController.ts @@ -0,0 +1,249 @@ +import { + Body, + Controller, + Delete, + Get, + Path, + Post, + Put, + Query, + Request, + Route, + Security, + Tags, +} from "tsoa"; +import { + addUserGroup, + addUserRoles, + createGroup, + createUser, + deleteGroup, + deleteUser, + editUser, + getGroups, + getRoles, + getUser, + getUserGroups, + getUserList, + removeUserGroup, + removeUserRoles, +} from "../keycloak"; +// import * as io from "../lib/websocket"; +// import elasticsearch from "../elasticsearch"; +// import { StorageFolder } from "../interfaces/storage-fs"; + +// if (!process.env.MINIO_BUCKET) throw Error("Default MinIO bucket must be specified."); +// if (!process.env.ELASTICSEARCH_INDEX) throw Error("Default ElasticSearch index must be specified."); + +// const DEFAULT_INDEX = process.env.ELASTICSEARCH_INDEX; + +function stripLeadingSlash(str: string) { + return str.replace(/^\//, ""); +} + +@Route("keycloak") +@Tags("Single-Sign On") +@Security("bearerAuth") +export class KeycloakController extends Controller { + @Get("user/{id}") + async getUser(@Path() id: string) { + return await getUser(id); + } + + @Post("user") + @Security("bearerAuth", ["system", "admin"]) + async createUser( + @Request() request: { user: { sub: string; preferred_username: string } }, + @Body() + body: { + username: string; + password: string; + firstName?: string; + lastName?: string; + email?: string; + }, + ) { + const userId = await createUser(body.username, body.password, { + firstName: body.firstName, + lastName: body.lastName, + email: body.email, + requiredActions: ["UPDATE_PASSWORD"], + }); + + if (typeof userId !== "string") { + throw new Error("ไม่สามารถติดต่อกับระบบจัดการผู้ใช้งานได้"); + } + + const now = new Date().toISOString(); + const folderData: any = { + pathname: stripLeadingSlash(`${body.username.trim()}/`), + path: "", + name: body.username.trim(), + hidden: false, + permissionGroup: [], + permissionUser: [], + permissionOther: { + create: false, + read: false, + update: false, + delete: false, + perm: false, + }, + favourite: false, + color: "default", + type: "folder", + owner: body.username, + ownerId: userId, + createdAt: now, + createdBy: request.user.preferred_username, + createdByUserId: request.user.sub, + updatedAt: now, + updatedBy: request.user.preferred_username, + updatedByUserId: request.user.sub, + }; + + // await elasticsearch.index({ + // index: DEFAULT_INDEX!, + // document: folderData, + // refresh: "wait_for", + // }); + + // io.getInstance()?.emit("FolderCreate", folderData); + + return userId; + } + + @Put("user/{userId}") + async editUser( + @Path() userId: string, + @Body() + body: { + username?: string; + password?: string; + firstName?: string; + lastName?: string; + email?: string; + }, + ) { + return await editUser(userId, body); + } + + @Delete("user/{userId}") + @Security("bearerAuth", ["system", "admin"]) + async deleteUser(@Path() userId: string) { + return await deleteUser(userId).then(async (v) => { + if (!v) throw new Error("ไม่สามารถติดต่อกับระบบจัดการผู้ใช้งานได้"); + // await elasticsearch.deleteByQuery({ + // index: DEFAULT_INDEX, + // query: { + // bool: { + // must: [ + // { prefix: { pathname: stripLeadingSlash(`${userId}/`) } }, + // { match: { type: "folder" } }, + // ], + // }, + // }, + // }); + // delete file that is not uploaded + // await elasticsearch.deleteByQuery({ + // index: DEFAULT_INDEX, + // query: { + // bool: { + // must: [ + // { prefix: { pathname: stripLeadingSlash(`${userId}/`) } }, + // { match: { upload: false } }, + // ], + // }, + // }, + // }); + + // io.getInstance()?.emit("FolderDelete", { pathname: userId + "/" }); + }); + } + + @Get("role") + async getRole() { + const role = await getRoles(); + if (Array.isArray(role)) + return role.filter( + (a) => + !["uma_authorization", "offline_access", "default-roles"].some((b) => a.name.includes(b)), + ); + throw new Error("Failed. Cannot get role."); + } + + @Post("{userId}/role") + async addRole(@Path() userId: string, @Body() body: { role: string[] }) { + const list = await getRoles(); + + if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server."); + + const result = await addUserRoles( + userId, + list.filter((v) => body.role.includes(v.id)), + ); + + if (!result) throw new Error("Failed. Cannot set user's role."); + } + + @Delete("{userId}/role/{roleId}") + async deleteRole(@Path() userId: string, @Path() roleId: string) { + const list = await getRoles(); + + if (!Array.isArray(list)) throw new Error("Failed. Cannot get role(s) data from the server."); + + const result = await removeUserRoles( + userId, + list.filter((v) => roleId === v.id), + ); + if (!result) throw new Error("Failed. Cannot remove user's role."); + } + + @Get("user") + async getUserList(@Query() search = "") { + const result = await getUserList(search); + + if (Array.isArray(result)) { + return result; + } + throw new Error("Failed. Cannot get user list."); + } + + @Get("group") + async getGroup() { + const group = await getGroups(); + if (Array.isArray(group)) return group; + throw new Error("Failed. Cannot get group."); + } + + @Post("group") + async createGroup(@Body() body: { name: string }) { + const result = await createGroup(body.name); + if (!result) throw new Error("Failed. Cannot create group."); + } + + @Delete("group/{groupId}") + async deleteGroup(@Path() groupId: string) { + const result = await deleteGroup(groupId); + if (!result) throw new Error("Failed. Cannot delete group."); + } + + @Get("user/{userId}/group") + async getUserGroup(@Path() userId: string) { + const result = await getUserGroups(userId); + if (!result) throw new Error("Failed. Cannot list group to user."); + return result; + } + + @Post("user/{userId}/group/{groupId}") + async addUserGroup(@Path() userId: string, @Path() groupId: string) { + const result = await addUserGroup(userId, groupId); + if (!result) throw new Error("Failed. Cannot assign group to user."); + } + + @Delete("user/{userId}/group/{groupId}") + async removeUserGroup(@Path() userId: string, @Path() groupId: string) { + const result = await removeUserGroup(userId, groupId); + if (!result) throw new Error("Failed. Cannot remove group to user."); + } +} diff --git a/src/keycloak/index.ts b/src/keycloak/index.ts new file mode 100644 index 00000000..da953e9f --- /dev/null +++ b/src/keycloak/index.ts @@ -0,0 +1,542 @@ +import { DecodedJwt, createDecoder } from "fast-jwt"; + +const KC_URL = process.env.KC_URL; +const KC_REALM = process.env.KC_REALM; +const KC_CLIENT_ID = process.env.KC_SERVICE_ACCOUNT_CLIENT_ID; +const KC_SECRET = process.env.KC_SERVICE_ACCOUNT_SECRET; + +console.log(process.env.KC_URL); + +let token: string | null = null; +let decoded: DecodedJwt | null = null; + +const jwtDecode = createDecoder({ complete: true }); + +/** + * Check if token is expired or will expire in 30 seconds + * @returns true if expire or can't get exp, false otherwise + */ +export function isTokenExpired(token: string, beforeExpire: number = 30) { + decoded = jwtDecode(token); + + if (decoded && decoded.payload.exp) { + return Date.now() / 1000 >= decoded.payload.exp - beforeExpire; + } + + return true; +} + +/** + * Get token from keycloak if needed + */ +export async function getToken() { + if (!KC_CLIENT_ID || !KC_SECRET) { + throw new Error("KC_CLIENT_ID and KC_SECRET are required to used this feature."); + } + + if (token && !isTokenExpired(token)) return token; + + const body = new URLSearchParams(); + + body.append("client_id", KC_CLIENT_ID); + body.append("client_secret", KC_SECRET); + body.append("grant_type", "client_credentials"); + + const res = await fetch(`${KC_URL}/realms/${KC_REALM}/protocol/openid-connect/token`, { + method: "POST", + body: body, + }).catch((e) => console.error(e)); + + if (!res) { + throw new Error("Cannot get token from keycloak."); + } + + const data = (await res.json()) as any; + + if (data && data.access_token) { + token = data.access_token; + } + + return token; +} + +/** + * Create keycloak user by given username and password with roles + * + * Client must have permission to manage realm's user + * + * @returns user uuid or true if success, false otherwise. + */ +export async function createUser(username: string, password: string, opts?: Record) { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users`, { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + method: "POST", + body: JSON.stringify({ + enabled: true, + credentials: [{ type: "password", value: password }], + username, + ...opts, + }), + }).catch((e) => console.log("Keycloak Error: ", e)); + + if (!res) return false; + if (!res.ok) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + const path = res.headers.get("Location"); + const id = path?.split("/").at(-1); + return id || true; +} + +/** + * Get keycloak user by uuid + * + * Client must have permission to manage realm's user + * + * @returns user if success, false otherwise. + */ +export async function getUser(userId: string) { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}`, { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + }).catch((e) => console.log("Keycloak Error: ", e)); + if (!res) return false; + if (!res.ok) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + return await res.json(); +} + +/** + * Get keycloak user list + * + * Client must have permission to manage realm's user + * + * @returns user list if success, false otherwise. + */ +export async function getUserList(search = "") { + const res = await fetch( + `${KC_URL}/admin/realms/${KC_REALM}/users`.concat(!!search ? `?search=${search}` : ""), + { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + }, + ).catch((e) => console.log("Keycloak Error: ", e)); + + if (!res) return false; + if (!res.ok) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + console.log(res.json); + + return ((await res.json()) as any[]).map((v: Record) => ({ + id: v.id, + username: v.username, + firstName: v.firstName, + lastName: v.lastName, + email: v.email, + })); +} + +/** + * Update keycloak user by uuid + * + * Client must have permission to manage realm's user + * + * @returns user uuid or true if success, false otherwise. + */ +export async function editUser(userId: string, opts: Record) { + const { password, ...rest } = opts; + + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}`, { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + method: "PUT", + body: JSON.stringify({ + enabled: true, + credentials: (password && [{ type: "password", value: opts?.password }]) || undefined, + ...rest, + }), + }).catch((e) => console.log("Keycloak Error: ", e)); + + if (!res) return false; + if (!res.ok) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + const path = res.headers.get("Location"); + const id = path?.split("/").at(-1); + return id || true; +} + +/** + * Delete keycloak user by uuid + * + * Client must have permission to manage realm's user + * + * @returns user true if success, false otherwise. + */ +export async function deleteUser(userId: string) { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}`, { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + method: "DELETE", + }).catch((e) => console.log("Keycloak Error: ", e)); + + if (!res) return false; + if (!res.ok) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + return true; +} + +/** + * Get roles list or specific role data + * + * Client must have permission to get realms roles + * + * @returns role's info (array if not specify name) if success, null if not found, false otherwise. + */ +export async function getRoles(name?: string) { + const res = await fetch( + `${KC_URL}/admin/realms/${KC_REALM}/roles`.concat((name && `/${name}`) || ""), + { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + }, + }, + ).catch((e) => console.log(e)); + + if (!res) return false; + if (!res.ok && res.status !== 404) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + if (res.status === 404) { + return null; + } + + const data = (await res.json()) as any; + + if (Array.isArray(data)) { + return data.map((v: Record) => ({ id: v.id, name: v.name })); + } + + return { + id: data.id, + name: data.name, + }; +} + +/** + * Get roles list of user + * + * Client must have permission to get realms roles + * + * @returns role's info (array if not specify name) if success, null if not found, false otherwise. + */ +export async function getUserRoles(userId: string) { + const res = await fetch( + `${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/role-mappings/realm`, + { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + }, + }, + ).catch((e) => console.log(e)); + + if (!res) return false; + if (!res.ok && res.status !== 404) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + if (res.status === 404) { + return null; + } + + const data = (await res.json()) as any; + + if (Array.isArray(data)) { + return data.map((v: Record) => ({ id: v.id, name: v.name })); + } + + return { + id: data.id, + name: data.name, + }; +} + +/** + * Assign role to user + * + * Client must have permission to manage realm's user roles + * + * @returns true if success, false otherwise. + */ +export async function addUserRoles(userId: string, roles: { id: string; name: string }[]) { + const res = await fetch( + `${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/role-mappings/realm`, + { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + method: "POST", + body: JSON.stringify(roles), + }, + ).catch((e) => console.log(e)); + + if (!res) return false; + if (!res.ok) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + return true; +} + +/** + * Remove role from user + * + * Client must have permission to manage realm's user roles + * + * @returns true if success, false otherwise. + */ +export async function removeUserRoles(userId: string, roles: { id: string; name: string }[]) { + const res = await fetch( + `${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/role-mappings/realm`, + { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + method: "DELETE", + body: JSON.stringify(roles), + }, + ).catch((e) => console.log(e)); + + if (!res) return false; + if (!res.ok) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + return true; +} + +/** + * Get group list or specific group data + * + * Client must have permission to manage realms group + * + * @returns group's info (array if not specify name) if success, null if not found, false otherwise. + */ +export async function getGroups(id?: string) { + const res = await fetch( + `${KC_URL}/admin/realms/${KC_REALM}/groups`.concat((id && `/${id}`) || ""), + { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + }, + }, + ).catch((e) => console.log(e)); + + if (!res) return false; + if (!res.ok && res.status !== 404) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + if (res.status === 404) { + return null; + } + + const data = (await res.json()) as any; + + if (Array.isArray(data)) { + return data.map((v: Record) => ({ id: v.id, name: v.name })); + } + + return { + id: data.id, + name: data.name, + }; +} + +/** + * Create group + * + * Client must have permission to manage realms group + * + * @returns true if success, false otherwise. + */ +export async function createGroup(name: string) { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups`, { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + method: "POST", + body: JSON.stringify({ name }), + }).catch((e) => console.log(e)); + + if (!res) return false; + if (!res.ok && res.status !== 404) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + return true; +} + +/** + * Edit group + * + * Client must have permission to manage realms group + * + * @returns true if success, false otherwise. + */ +export async function editGroup(id: string, name: string) { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups/${id}`, { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + "content-type": `application/json`, + }, + method: "PUT", + body: JSON.stringify({ name }), + }).catch((e) => console.log(e)); + + if (!res) return false; + if (!res.ok && res.status !== 404) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + return true; +} + +/** + * Delete group + * + * Client must have permission to manage realms group + * + * @returns true if success, false otherwise. + */ +export async function deleteGroup(id: string) { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups/${id}`, { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + }, + method: "DELETE", + }).catch((e) => console.log(e)); + + if (!res) return false; + if (!res.ok && res.status !== 404) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + return true; +} + +/** + * Get group list or specific group data + * + * Client must have permission to manage realms group + * + * @returns group's info (array if not specify name) if success, null if not found, false otherwise. + */ +export async function getUserGroups(userId?: string) { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/groups`, { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + }, + }).catch((e) => console.log(e)); + + if (!res) return false; + if (!res.ok && res.status !== 404) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + if (res.status === 404) { + return null; + } + + const data = (await res.json()) as any; + + if (Array.isArray(data)) { + return data.map((v: Record) => ({ id: v.id, name: v.name })); + } + + return { + id: data.id, + name: data.name, + }; +} + +/** + * Add group to user + * + * Client must have permission to manage user group + * + * @returns true if success, false otherwise. + */ +export async function addUserGroup(userId: string, groupId: string) { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/groups/${groupId}`, { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + }, + method: "PUT", + }).catch((e) => console.log(e)); + + if (!res) return false; + if (!res.ok && res.status !== 404) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + return true; +} + +/** + * Delete group from user + * + * Client must have permission to manage user group + * + * @returns true if success, false otherwise. + */ +export async function removeUserGroup(userId: string, groupId: string) { + const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/groups/${groupId}`, { + // prettier-ignore + headers: { + "authorization": `Bearer ${await getToken()}`, + }, + method: "PUT", + }).catch((e) => console.log(e)); + + if (!res) return false; + if (!res.ok && res.status !== 404) { + return Boolean(console.error("Keycloak Error Response: ", await res.json())); + } + + return true; +} From 3a258897fbe0a2b7604ae0a4c58a2f06ebc8ae3b Mon Sep 17 00:00:00 2001 From: AnandaTon Date: Wed, 29 May 2024 14:20:06 +0700 Subject: [PATCH 09/13] =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=20Extension.ToThaiNumber=20=E0=B9=83=E0=B8=99=20profi?= =?UTF-8?q?le?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/ProfileController.ts | 60 ++++++++++++++++------------ 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 17796845..9d68cf03 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -302,20 +302,25 @@ export class ProfileController extends Controller { const _child4 = child4 ? `${child4.orgChild4Name}/` : ""; const Profile = { - CitizenId: profiles?.citizenId ?? null, + CitizenId: + profiles.citizenId != null ? Extension.ToThaiNumber(profiles.citizenId.toString()) : "", Prefix: profiles?.prefix != null ? profiles.prefix : "", FirstName: profiles?.firstName != null ? profiles.firstName : "", LastName: profiles?.lastName != null ? profiles.lastName : "", FullName: `${profiles?.prefix} ${profiles?.firstName} ${profiles?.lastName}`, - BirthDay: profiles?.birthDate ? new Date(profiles.birthDate).getDate().toString() : null, + BirthDay: profiles?.birthDate + ? Extension.ToThaiNumber(new Date(profiles.birthDate).getDate().toString()) + : null, BirthDayText: profiles.birthDate != null ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate).toString()) : "", BirthMonth: profiles?.birthDate - ? new Date(profiles.birthDate).getMonth() + (1).toString() + ? Extension.ToThaiNumber(new Date(profiles.birthDate).getMonth() + (1).toString()) : null, // Months are zero-based - BirthYear: profiles?.birthDate ? new Date(profiles.birthDate).getFullYear().toString() : null, + BirthYear: profiles?.birthDate + ? Extension.ToThaiNumber(new Date(profiles.birthDate).getFullYear().toString()) + : null, BirthYearText: profiles.birthDate != null ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate).toString()) @@ -326,24 +331,21 @@ export class ProfileController extends Controller { Province: "", Telephone: profiles?.telephoneNumber ?? null, CoupleLastNameOld: profileFamilyCouple?.coupleLastNameOld ?? null, - CouplePrefix: - profileFamilyCouple?.couplePrefix != null ? profileFamilyCouple.couplePrefix : "", + CouplePrefix: profileFamilyCouple?.couplePrefix ?? "", CoupleFullName: profileFamilyCouple?.couplePrefix || profileFamilyCouple?.coupleFirstName || profileFamilyCouple?.coupleLastNameOld ? `${profileFamilyCouple?.couplePrefix ?? ""} ${profileFamilyCouple?.coupleFirstName ?? ""} ${profileFamilyCouple?.coupleLastNameOld ?? ""}`.trim() : null, - FatherPrefix: - profileFamilyFather?.fatherPrefix != null ? profileFamilyFather.fatherPrefix : "", + FatherPrefix: profileFamilyFather?.fatherPrefix ?? "", FatherFullName: profileFamilyFather?.fatherPrefix || profileFamilyFather?.fatherFirstName || profileFamilyFather?.fatherLastName ? `${profileFamilyFather?.fatherPrefix ?? ""} ${profileFamilyFather?.fatherFirstName ?? ""} ${profileFamilyFather?.fatherLastName ?? ""}`.trim() : null, - MotherPrefix: - profileFamilyMother?.motherPrefix != null ? profileFamilyMother.motherPrefix : "", + MotherPrefix: profileFamilyMother?.motherPrefix ?? "", MotherFullName: profileFamilyMother?.motherPrefix || profileFamilyMother?.motherFirstName || @@ -355,8 +357,13 @@ export class ProfileController extends Controller { Institute: "", StartDate: profiles?.dateStart, AppointDate: profiles?.dateAppoint, - BirthDate: profiles?.birthDate ? Extension.ToThaiShortDate(profiles.birthDate) : null, - RetireDate: profiles?.dateRetireLaw, + BirthDate: profiles?.birthDate + ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate)) + : null, + RetireDate: + profiles.dateRetireLaw != null + ? Extension.ToThaiNumber(profiles.dateRetireLaw.toString()) + : "", // AvatarId: profiles?.avatar ?? null, }; @@ -367,20 +374,20 @@ export class ProfileController extends Controller { const Cert = certs.map((item) => ({ CertificateType: item.certificateType ?? null, Issuer: item.issuer ?? null, - CertificateNo: item.certificateNo ?? null, - IssueDate: Extension.ToThaiShortDate(item.issueDate) ?? null, + CertificateNo: Extension.ToThaiNumber(item.certificateNo) ?? null, + IssueDate: Extension.ToThaiNumber(Extension.ToThaiShortDate(item.issueDate)) ?? null, })); const trainings = await this.trainingRepository.find({ select: ["startDate", "endDate", "place", "department"], where: { profileId: id }, }); const Training = trainings.map((item) => ({ - institute: item.department ?? null, - start: Extension.ToThaiShortDate(item.startDate).toString() ?? null, - end: Extension.ToThaiShortDate(item.endDate).toString() ?? null, - level: "", - degree: "", - field: item.place ?? null, + Institute: item.department ?? null, + Start: Extension.ToThaiNumber(Extension.ToThaiShortDate(item.startDate).toString()) ?? null, + End: Extension.ToThaiNumber(Extension.ToThaiShortDate(item.endDate).toString()) ?? null, + Level: "", + Degree: "", + Field: item.place ?? null, })); const disciplines = await this.disciplineRepository.find({ @@ -388,7 +395,8 @@ export class ProfileController extends Controller { where: { profileId: id }, }); const Discipline = disciplines.map((item) => ({ - DisciplineYear: new Date(item.refCommandDate).getFullYear().toString() ?? null, + DisciplineYear: + Extension.ToThaiNumber(new Date(item.refCommandDate).getFullYear().toString()) ?? null, DisciplineDetail: item.detail ?? null, RefNo: item.refCommandNo ?? null, })); @@ -399,8 +407,8 @@ export class ProfileController extends Controller { }); const Education = educations.map((item) => ({ Institute: item.institute ?? null, - Start: new Date(item.startDate).getFullYear().toString() ?? null, - End: new Date(item.endDate).getFullYear().toString() ?? null, + Start: Extension.ToThaiNumber(new Date(item.startDate).getFullYear().toString()) ?? null, + End: Extension.ToThaiNumber(new Date(item.endDate).getFullYear().toString()) ?? null, Level: item.educationLevel ?? null, Degree: item.degree ?? null, Field: item.field ?? null, @@ -419,7 +427,7 @@ export class ProfileController extends Controller { }); const Salary = salarys.map((item) => ({ - SalaryDate: Extension.ToThaiShortDate(item.date) ?? null, + SalaryDate: Extension.ToThaiNumber(Extension.ToThaiShortDate(item.date)) ?? null, Position: item.position ?? null, PosNo: item.posNo ?? null, Salary: "", @@ -428,7 +436,9 @@ export class ProfileController extends Controller { PositionType: item.positionType ?? null, PositionLevel: item.positionLevel ?? null, PositionAmount: - item.positionSalaryAmount == null ? null : item.positionSalaryAmount.toString(), + item.positionSalaryAmount == null + ? null + : Extension.ToThaiNumber(item.positionSalaryAmount.toString()), FullName: `${profiles?.prefix} ${profiles?.firstName} ${profiles?.lastName}`, OcFullPath: `${_child4}${_child3}${_child2}${_child1}${_root}`, })); From 2fb1f4f8418edeae6e5e083a38f93a760b440871 Mon Sep 17 00:00:00 2001 From: "DESKTOP-1R2VSQH\\Lenovo ThinkPad E490" Date: Wed, 29 May 2024 14:20:37 +0700 Subject: [PATCH 10/13] keycloak --- src/controllers/UserController.ts | 33 ++++--------------------------- src/keycloak/index.ts | 2 -- 2 files changed, 4 insertions(+), 31 deletions(-) diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index ccfa676d..935223fd 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -129,37 +129,12 @@ export class KeycloakController extends Controller { } @Delete("user/{userId}") - @Security("bearerAuth", ["system", "admin"]) async deleteUser(@Path() userId: string) { - return await deleteUser(userId).then(async (v) => { - if (!v) throw new Error("ไม่สามารถติดต่อกับระบบจัดการผู้ใช้งานได้"); - // await elasticsearch.deleteByQuery({ - // index: DEFAULT_INDEX, - // query: { - // bool: { - // must: [ - // { prefix: { pathname: stripLeadingSlash(`${userId}/`) } }, - // { match: { type: "folder" } }, - // ], - // }, - // }, - // }); - // delete file that is not uploaded - // await elasticsearch.deleteByQuery({ - // index: DEFAULT_INDEX, - // query: { - // bool: { - // must: [ - // { prefix: { pathname: stripLeadingSlash(`${userId}/`) } }, - // { match: { upload: false } }, - // ], - // }, - // }, - // }); - - // io.getInstance()?.emit("FolderDelete", { pathname: userId + "/" }); - }); + const result = await deleteUser(userId); + if (!result) throw new Error("Failed. Cannot delete userId."); } + // @Security("bearerAuth", ["system", "admin"]) + @Get("role") async getRole() { diff --git a/src/keycloak/index.ts b/src/keycloak/index.ts index da953e9f..61d54851 100644 --- a/src/keycloak/index.ts +++ b/src/keycloak/index.ts @@ -139,8 +139,6 @@ export async function getUserList(search = "") { return Boolean(console.error("Keycloak Error Response: ", await res.json())); } - console.log(res.json); - return ((await res.json()) as any[]).map((v: Record) => ({ id: v.id, username: v.username, From 13e565d2d441fc643795eafd90dbc261ddcf81a1 Mon Sep 17 00:00:00 2001 From: waruneeauy Date: Wed, 29 May 2024 14:29:24 +0700 Subject: [PATCH 11/13] updated env example --- .env.example | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.env.example b/.env.example index daeaf3ab..61695b0c 100644 --- a/.env.example +++ b/.env.example @@ -10,3 +10,9 @@ DB_PORT=3306 DB_USERNAME=root DB_PASSWORD= DB_NAME=dev + +KC_URL=http://192.168.1.50:8080 +KC_REALM=dev +KC_SERVICE_ACCOUNT_CLIENT_ID=dev-service +KC_SERVICE_ACCOUNT_SECRET= +MANAGEMENT_ROLE=storage_management \ No newline at end of file From a0b4bef3a40085c229c351348599d8245accefc4 Mon Sep 17 00:00:00 2001 From: AnandaTon Date: Wed, 29 May 2024 14:55:13 +0700 Subject: [PATCH 12/13] Update ProfileController.ts --- src/controllers/ProfileController.ts | 126 ++++++++++++++------------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/src/controllers/ProfileController.ts b/src/controllers/ProfileController.ts index 9d68cf03..8dbcbdd7 100644 --- a/src/controllers/ProfileController.ts +++ b/src/controllers/ProfileController.ts @@ -301,71 +301,73 @@ export class ProfileController extends Controller { const _child3 = child3 ? `${child3.orgChild3Name}/` : ""; const _child4 = child4 ? `${child4.orgChild4Name}/` : ""; - const Profile = { - CitizenId: - profiles.citizenId != null ? Extension.ToThaiNumber(profiles.citizenId.toString()) : "", - Prefix: profiles?.prefix != null ? profiles.prefix : "", - FirstName: profiles?.firstName != null ? profiles.firstName : "", - LastName: profiles?.lastName != null ? profiles.lastName : "", - FullName: `${profiles?.prefix} ${profiles?.firstName} ${profiles?.lastName}`, - BirthDay: profiles?.birthDate - ? Extension.ToThaiNumber(new Date(profiles.birthDate).getDate().toString()) - : null, - BirthDayText: - profiles.birthDate != null - ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate).toString()) - : "", - BirthMonth: profiles?.birthDate - ? Extension.ToThaiNumber(new Date(profiles.birthDate).getMonth() + (1).toString()) - : null, // Months are zero-based - BirthYear: profiles?.birthDate - ? Extension.ToThaiNumber(new Date(profiles.birthDate).getFullYear().toString()) - : null, - BirthYearText: - profiles.birthDate != null - ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate).toString()) - : "", - Address: "", - District: "", - Area: "", - Province: "", - Telephone: profiles?.telephoneNumber ?? null, - CoupleLastNameOld: profileFamilyCouple?.coupleLastNameOld ?? null, - CouplePrefix: profileFamilyCouple?.couplePrefix ?? "", - CoupleFullName: - profileFamilyCouple?.couplePrefix || - profileFamilyCouple?.coupleFirstName || - profileFamilyCouple?.coupleLastNameOld - ? `${profileFamilyCouple?.couplePrefix ?? ""} ${profileFamilyCouple?.coupleFirstName ?? ""} ${profileFamilyCouple?.coupleLastNameOld ?? ""}`.trim() + const Profile = [ + { + CitizenId: + profiles.citizenId != null ? Extension.ToThaiNumber(profiles.citizenId.toString()) : "", + Prefix: profiles?.prefix != null ? profiles.prefix : "", + FirstName: profiles?.firstName != null ? profiles.firstName : "", + LastName: profiles?.lastName != null ? profiles.lastName : "", + FullName: `${profiles?.prefix} ${profiles?.firstName} ${profiles?.lastName}`, + BirthDay: profiles?.birthDate + ? Extension.ToThaiNumber(new Date(profiles.birthDate).getDate().toString()) : null, - FatherPrefix: profileFamilyFather?.fatherPrefix ?? "", - FatherFullName: - profileFamilyFather?.fatherPrefix || - profileFamilyFather?.fatherFirstName || - profileFamilyFather?.fatherLastName - ? `${profileFamilyFather?.fatherPrefix ?? ""} ${profileFamilyFather?.fatherFirstName ?? ""} ${profileFamilyFather?.fatherLastName ?? ""}`.trim() + BirthDayText: + profiles.birthDate != null + ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate).toString()) + : "", + BirthMonth: profiles?.birthDate + ? Extension.ToThaiNumber(new Date(profiles.birthDate).getMonth() + (1).toString()) + : null, // Months are zero-based + BirthYear: profiles?.birthDate + ? Extension.ToThaiNumber(new Date(profiles.birthDate).getFullYear().toString()) : null, - MotherPrefix: profileFamilyMother?.motherPrefix ?? "", - MotherFullName: - profileFamilyMother?.motherPrefix || - profileFamilyMother?.motherFirstName || - profileFamilyMother?.motherLastName - ? `${profileFamilyMother?.motherPrefix ?? ""} ${profileFamilyMother?.motherFirstName ?? ""} ${profileFamilyMother?.motherLastName ?? ""}`.trim() + BirthYearText: + profiles.birthDate != null + ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate).toString()) + : "", + Address: "", + District: "", + Area: "", + Province: "", + Telephone: profiles?.telephoneNumber ?? null, + CoupleLastNameOld: profileFamilyCouple?.coupleLastNameOld ?? null, + CouplePrefix: profileFamilyCouple?.couplePrefix ?? "", + CoupleFullName: + profileFamilyCouple?.couplePrefix || + profileFamilyCouple?.coupleFirstName || + profileFamilyCouple?.coupleLastNameOld + ? `${profileFamilyCouple?.couplePrefix ?? ""} ${profileFamilyCouple?.coupleFirstName ?? ""} ${profileFamilyCouple?.coupleLastNameOld ?? ""}`.trim() + : null, + FatherPrefix: profileFamilyFather?.fatherPrefix ?? "", + FatherFullName: + profileFamilyFather?.fatherPrefix || + profileFamilyFather?.fatherFirstName || + profileFamilyFather?.fatherLastName + ? `${profileFamilyFather?.fatherPrefix ?? ""} ${profileFamilyFather?.fatherFirstName ?? ""} ${profileFamilyFather?.fatherLastName ?? ""}`.trim() + : null, + MotherPrefix: profileFamilyMother?.motherPrefix ?? "", + MotherFullName: + profileFamilyMother?.motherPrefix || + profileFamilyMother?.motherFirstName || + profileFamilyMother?.motherLastName + ? `${profileFamilyMother?.motherPrefix ?? ""} ${profileFamilyMother?.motherFirstName ?? ""} ${profileFamilyMother?.motherLastName ?? ""}`.trim() + : null, + OcFullPath: `${_child4}${_child3}${_child2}${_child1}${_root}`, + Division: "", + Institute: "", + StartDate: profiles?.dateStart, + AppointDate: profiles?.dateAppoint, + BirthDate: profiles?.birthDate + ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate)) : null, - OcFullPath: `${_child4}${_child3}${_child2}${_child1}${_root}`, - Division: "", - Institute: "", - StartDate: profiles?.dateStart, - AppointDate: profiles?.dateAppoint, - BirthDate: profiles?.birthDate - ? Extension.ToThaiNumber(Extension.ToThaiShortDate(profiles.birthDate)) - : null, - RetireDate: - profiles.dateRetireLaw != null - ? Extension.ToThaiNumber(profiles.dateRetireLaw.toString()) - : "", - // AvatarId: profiles?.avatar ?? null, - }; + RetireDate: + profiles.dateRetireLaw != null + ? Extension.ToThaiNumber(profiles.dateRetireLaw.toString()) + : "", + // AvatarId: profiles?.avatar ?? null, + }, + ]; const certs = await this.certificateRepository.find({ where: { profileId: id }, From 857b0624f496cf8076873ac01132636bc790e028 Mon Sep 17 00:00:00 2001 From: Bright Date: Wed, 29 May 2024 15:04:42 +0700 Subject: [PATCH 13/13] =?UTF-8?q?=E0=B9=80=E0=B8=9E=E0=B8=B4=E0=B9=88?= =?UTF-8?q?=E0=B8=A1=E0=B9=80=E0=B8=87=E0=B8=B4=E0=B8=99=E0=B9=80=E0=B8=94?= =?UTF-8?q?=E0=B8=B7=E0=B8=AD=E0=B8=99=E0=B8=A5=E0=B9=88=E0=B8=B2=E0=B8=AA?= =?UTF-8?q?=E0=B8=B8=E0=B8=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OrganizationDotnetController.ts | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/controllers/OrganizationDotnetController.ts b/src/controllers/OrganizationDotnetController.ts index 5ff254b4..d921b77a 100644 --- a/src/controllers/OrganizationDotnetController.ts +++ b/src/controllers/OrganizationDotnetController.ts @@ -168,11 +168,67 @@ export class OrganizationDotnetController extends Controller { relations: { posLevel: true, posType: true, + profileSalary: true }, where: { keycloak: keycloakId }, + order:{ + profileSalary:{ + date: "DESC" + } + } }); if (!profile) throw new HttpError(HttpStatus.NOT_FOUND, "ไม่พบข้อมูล"); - return new HttpSuccess(profile); + + const mapProfile = { + id: profile.id, + avatar: profile.avatar, + avatarName: profile.avatarName, + rank: profile.rank, + prefix: profile.prefix, + firstName: profile.firstName, + lastName: profile.lastName, + citizenId: profile.citizenId, + position: profile.position, + posLevelId: profile.posLevelId, + email: profile.email, + phone: profile.phone, + keycloak: profile.keycloak, + isProbation: profile.isProbation, + isLeave: profile.isLeave, + leaveReason: profile.leaveReason, + dateRetire: profile.dateRetire, + dateAppoint: profile.dateAppoint, + dateRetireLaw: profile.dateRetireLaw, + dateStart: profile.dateStart, + govAgeAbsent: profile.govAgeAbsent, + govAgePlus: profile.govAgePlus, + birthDate: profile.birthDate, + reasonSameDate: profile.reasonSameDate, + telephoneNumber: profile.telephoneNumber, + nationality: profile.nationality, + gender: profile.gender, + relationship: profile.relationship, + religion: profile.religion, + bloodGroup: profile.bloodGroup, + registrationAddress: profile.registrationAddress, + registrationProvinceId: profile.registrationProvinceId, + registrationDistrictId: profile.registrationDistrictId, + registrationSubDistrictId: profile.registrationSubDistrictId, + registrationZipCode: profile.registrationZipCode, + currentAddress: profile.currentAddress, + currentProvinceId: profile.currentProvinceId, + currentSubDistrictId: profile.currentSubDistrictId, + currentZipCode: profile.currentZipCode, + dutyTimeId: profile.dutyTimeId, + dutyTimeEffectiveDate: profile.dutyTimeEffectiveDate, + posLevel: profile.posLevel? profile.posLevel : null, + posType: profile.posType? profile.posType : null, + profileSalary: profile.profileSalary.length > 0 + ? profile.profileSalary[0] + : null + } + + return new HttpSuccess(mapProfile); } /**