generated from corrad-software/corrad-af-2024
Compare commits
No commits in common. "3c5a2e570460005d2b9abc3f446ddefbaf198707" and "d4880c491e3491be4f09fbfbc0e0a9f8b5cfb1b8" have entirely different histories.
3c5a2e5704
...
d4880c491e
17639
package-lock.json
generated
17639
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,6 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nuxtjs/tailwindcss": "^6.8.0",
|
"@nuxtjs/tailwindcss": "^6.8.0",
|
||||||
"@pinia-plugin-persistedstate/nuxt": "^1.1.1",
|
"@pinia-plugin-persistedstate/nuxt": "^1.1.1",
|
||||||
"@types/jsonwebtoken": "^9.0.9",
|
|
||||||
"@vite-pwa/nuxt": "^0.1.0",
|
"@vite-pwa/nuxt": "^0.1.0",
|
||||||
"eslint": "^8.39.0",
|
"eslint": "^8.39.0",
|
||||||
"eslint-plugin-vue": "^9.16.1",
|
"eslint-plugin-vue": "^9.16.1",
|
||||||
|
@ -41,11 +41,7 @@ model department {
|
|||||||
org_id Int
|
org_id Int
|
||||||
cabinets cabinets[]
|
cabinets cabinets[]
|
||||||
organization organization @relation(fields: [org_id], references: [org_id], onDelete: Cascade, onUpdate: NoAction, map: "department_organization_FK")
|
organization organization @relation(fields: [org_id], references: [org_id], onDelete: Cascade, onUpdate: NoAction, map: "department_organization_FK")
|
||||||
<<<<<<< HEAD
|
|
||||||
user user[]
|
|
||||||
=======
|
|
||||||
users sys_user[]
|
users sys_user[]
|
||||||
>>>>>>> d4880c491e3491be4f09fbfbc0e0a9f8b5cfb1b8
|
|
||||||
|
|
||||||
@@index([org_id], map: "department_organization_FK")
|
@@index([org_id], map: "department_organization_FK")
|
||||||
}
|
}
|
||||||
@ -62,8 +58,6 @@ model cabinets {
|
|||||||
department department? @relation(fields: [dp_id], references: [dp_id], onDelete: NoAction, onUpdate: NoAction, map: "cabinets_department_FK")
|
department department? @relation(fields: [dp_id], references: [dp_id], onDelete: NoAction, onUpdate: NoAction, map: "cabinets_department_FK")
|
||||||
|
|
||||||
@@index([dp_id], map: "cabinets_department_FK")
|
@@index([dp_id], map: "cabinets_department_FK")
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model sys_user {
|
model sys_user {
|
||||||
@ -81,7 +75,6 @@ model sys_user {
|
|||||||
department department @relation(fields: [dp_id], references: [dp_id], onDelete: NoAction, onUpdate: NoAction, map: "sys_user_department_FK")
|
department department @relation(fields: [dp_id], references: [dp_id], onDelete: NoAction, onUpdate: NoAction, map: "sys_user_department_FK")
|
||||||
|
|
||||||
@@index([dp_id], map: "sys_user_department_FK")
|
@@index([dp_id], map: "sys_user_department_FK")
|
||||||
>>>>>>> d4880c491e3491be4f09fbfbc0e0a9f8b5cfb1b8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model role {
|
model role {
|
||||||
@ -95,24 +88,6 @@ model role {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model user {
|
model user {
|
||||||
<<<<<<< HEAD
|
|
||||||
userID Int @id @default(autoincrement())
|
|
||||||
userSecretKey String? @db.VarChar(255)
|
|
||||||
userUsername String? @db.VarChar(255)
|
|
||||||
userPassword String? @db.VarChar(255)
|
|
||||||
userFullName String? @db.VarChar(255)
|
|
||||||
userEmail String? @db.VarChar(255)
|
|
||||||
userPhone String? @db.VarChar(255)
|
|
||||||
userStatus String? @db.VarChar(255)
|
|
||||||
dp_id Int?
|
|
||||||
userCreatedDate DateTime? @db.DateTime(0)
|
|
||||||
userModifiedDate DateTime? @db.DateTime(0)
|
|
||||||
audit audit[]
|
|
||||||
department department? @relation(fields: [dp_id], references: [dp_id], onDelete: NoAction, onUpdate: NoAction, map: "user_department_FK")
|
|
||||||
userrole userrole[]
|
|
||||||
|
|
||||||
@@index([dp_id], map: "user_department_FK")
|
|
||||||
=======
|
|
||||||
userID Int @id @default(autoincrement())
|
userID Int @id @default(autoincrement())
|
||||||
userSecretKey String? @db.VarChar(255)
|
userSecretKey String? @db.VarChar(255)
|
||||||
userUsername String? @db.VarChar(255)
|
userUsername String? @db.VarChar(255)
|
||||||
@ -125,7 +100,6 @@ model user {
|
|||||||
userModifiedDate DateTime? @db.DateTime(0)
|
userModifiedDate DateTime? @db.DateTime(0)
|
||||||
audit audit[]
|
audit audit[]
|
||||||
userrole userrole[]
|
userrole userrole[]
|
||||||
>>>>>>> d4880c491e3491be4f09fbfbc0e0a9f8b5cfb1b8
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model userrole {
|
model userrole {
|
||||||
@ -140,98 +114,7 @@ model userrole {
|
|||||||
@@index([userRoleUserID], map: "FK_userrole_user")
|
@@index([userRoleUserID], map: "FK_userrole_user")
|
||||||
}
|
}
|
||||||
|
|
||||||
model dms_settings {
|
|
||||||
settingID Int @id @default(autoincrement())
|
|
||||||
userRoles String? @db.Text
|
|
||||||
rbacEnabled Boolean? @default(true)
|
|
||||||
userGroups String? @db.Text
|
|
||||||
permissionView Boolean? @default(true)
|
|
||||||
permissionEdit Boolean? @default(true)
|
|
||||||
permissionDelete Boolean? @default(false)
|
|
||||||
permissionDownload Boolean? @default(true)
|
|
||||||
permissionShare Boolean? @default(true)
|
|
||||||
ssoEnabled Boolean? @default(false)
|
|
||||||
mfaRequired Boolean? @default(false)
|
|
||||||
ldapIntegration Boolean? @default(false)
|
|
||||||
sessionTimeout Int? @default(8)
|
|
||||||
folderMaxDepth Int? @default(5)
|
|
||||||
folderDefaultStructure String? @db.Text
|
|
||||||
folderTemplates String? @db.Text
|
|
||||||
namingAutoGenerate Boolean? @default(true)
|
|
||||||
namingMandatoryFields String? @db.Text
|
|
||||||
namingPattern String? @default("{department}_{title}_{date}") @db.VarChar(255)
|
|
||||||
retentionEnabled Boolean? @default(true)
|
|
||||||
retentionDefaultDays Int? @default(2555)
|
|
||||||
retentionArchiveBeforeDelete Boolean? @default(true)
|
|
||||||
versionControlEnabled Boolean? @default(true)
|
|
||||||
versionControlMaxVersions Int? @default(10)
|
|
||||||
versionControlAutoVersioning Boolean? @default(true)
|
|
||||||
metadataCustomFields String? @db.LongText
|
|
||||||
taggingPredefinedTags String? @db.Text
|
|
||||||
taggingUserGeneratedTags Boolean? @default(true)
|
|
||||||
taggingTagSuggestions Boolean? @default(true)
|
|
||||||
classificationAutoEnabled Boolean? @default(true)
|
|
||||||
classificationRules String? @db.Text
|
|
||||||
workflowApprovalEnabled Boolean? @default(true)
|
|
||||||
workflowDefaultFlow String? @default("department-head-approval") @db.VarChar(255)
|
|
||||||
workflowCustomFlows String? @db.Text
|
|
||||||
notificationEmail Boolean? @default(true)
|
|
||||||
notificationInApp Boolean? @default(true)
|
|
||||||
notificationUploadAlerts Boolean? @default(true)
|
|
||||||
notificationDeadlineReminders Boolean? @default(true)
|
|
||||||
automationTriggers String? @db.Text
|
|
||||||
automationActions String? @db.Text
|
|
||||||
uploadAllowedFileTypes String? @db.Text
|
|
||||||
uploadBlockedFileTypes String? @db.Text
|
|
||||||
uploadFileSizeLimit Int? @default(100)
|
|
||||||
uploadQuotaPerUser Int? @default(5000)
|
|
||||||
uploadQuotaPerGroup Int? @default(50000)
|
|
||||||
uploadQuotaPerProject Int? @default(100000)
|
|
||||||
storageType String? @default("local") @db.VarChar(100)
|
|
||||||
storagePath String? @default("/var/uploads/edms") @db.VarChar(500)
|
|
||||||
storageBackupEnabled Boolean? @default(true)
|
|
||||||
storageCompressionEnabled Boolean? @default(false)
|
|
||||||
systemTimezone String? @default("Asia/Kuala_Lumpur") @db.VarChar(100)
|
|
||||||
systemBackupSchedule String? @default("daily") @db.VarChar(100)
|
|
||||||
systemLogLevel String? @default("info") @db.VarChar(100)
|
|
||||||
systemMaintenanceMode Boolean? @default(false)
|
|
||||||
systemAutoUpdates Boolean? @default(false)
|
|
||||||
systemMonitoring Boolean? @default(true)
|
|
||||||
systemPerformanceMetrics Boolean? @default(true)
|
|
||||||
settingCreatedDate DateTime? @default(now()) @db.DateTime(0)
|
|
||||||
settingModifiedDate DateTime? @default(now()) @db.DateTime(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
model site_settings {
|
model site_settings {
|
||||||
<<<<<<< HEAD
|
|
||||||
settingID Int @id @default(autoincrement())
|
|
||||||
siteName String? @default("corradAF") @db.VarChar(255)
|
|
||||||
siteNameFontSize Int? @default(18)
|
|
||||||
siteDescription String? @db.Text
|
|
||||||
siteLogo String? @db.VarChar(500)
|
|
||||||
siteLoadingLogo String? @db.VarChar(500)
|
|
||||||
siteFavicon String? @db.VarChar(500)
|
|
||||||
siteLoginLogo String? @db.VarChar(500)
|
|
||||||
showSiteNameInHeader Boolean? @default(true)
|
|
||||||
customCSS String? @db.LongText
|
|
||||||
themeMode String? @default("biasa") @db.VarChar(100)
|
|
||||||
customThemeFile String? @db.VarChar(500)
|
|
||||||
currentFont String? @db.VarChar(100)
|
|
||||||
fontSource String? @db.VarChar(100)
|
|
||||||
seoTitle String? @db.VarChar(255)
|
|
||||||
seoDescription String? @db.Text
|
|
||||||
seoKeywords String? @db.Text
|
|
||||||
seoAuthor String? @db.VarChar(255)
|
|
||||||
seoOgImage String? @db.VarChar(500)
|
|
||||||
seoTwitterCard String? @default("summary_large_image") @db.VarChar(100)
|
|
||||||
seoCanonicalUrl String? @db.VarChar(500)
|
|
||||||
seoRobots String? @default("index, follow") @db.VarChar(100)
|
|
||||||
seoGoogleAnalytics String? @db.VarChar(255)
|
|
||||||
seoGoogleTagManager String? @db.VarChar(255)
|
|
||||||
seoFacebookPixel String? @db.VarChar(255)
|
|
||||||
settingCreatedDate DateTime? @default(now()) @db.DateTime(0)
|
|
||||||
settingModifiedDate DateTime? @default(now()) @db.DateTime(0)
|
|
||||||
=======
|
|
||||||
settingID Int @id @default(autoincrement())
|
settingID Int @id @default(autoincrement())
|
||||||
siteName String? @default("corradAF") @db.VarChar(255)
|
siteName String? @default("corradAF") @db.VarChar(255)
|
||||||
siteNameFontSize Int? @default(18)
|
siteNameFontSize Int? @default(18)
|
||||||
@ -333,5 +216,4 @@ model dms_settings {
|
|||||||
|
|
||||||
settingCreatedDate DateTime? @default(now()) @db.DateTime(0)
|
settingCreatedDate DateTime? @default(now()) @db.DateTime(0)
|
||||||
settingModifiedDate DateTime? @default(now()) @db.DateTime(0)
|
settingModifiedDate DateTime? @default(now()) @db.DateTime(0)
|
||||||
>>>>>>> d4880c491e3491be4f09fbfbc0e0a9f8b5cfb1b8
|
|
||||||
}
|
}
|
||||||
|
@ -1,126 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
import sha256 from "crypto-js/sha256";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get request body
|
|
||||||
const body = await readBody(event);
|
|
||||||
|
|
||||||
// Validate required fields
|
|
||||||
if (!body.username || !body.password) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Username and password are required"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find user by username
|
|
||||||
const user = await prisma.user.findFirst({
|
|
||||||
where: {
|
|
||||||
userUsername: body.username
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
department: {
|
|
||||||
select: {
|
|
||||||
dp_id: true,
|
|
||||||
dp_name: true,
|
|
||||||
organization: {
|
|
||||||
select: {
|
|
||||||
org_id: true,
|
|
||||||
org_name: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
userrole: {
|
|
||||||
select: {
|
|
||||||
role: {
|
|
||||||
select: {
|
|
||||||
roleID: true,
|
|
||||||
roleName: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "User not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user is active
|
|
||||||
if (user.userStatus !== "ACTIVE") {
|
|
||||||
return {
|
|
||||||
statusCode: 403,
|
|
||||||
message: "User account is not active"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify password
|
|
||||||
const hashedPassword = sha256(body.password).toString();
|
|
||||||
if (user.userPassword !== hashedPassword) {
|
|
||||||
return {
|
|
||||||
statusCode: 401,
|
|
||||||
message: "Invalid password"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create audit log for successful login
|
|
||||||
await prisma.audit.create({
|
|
||||||
data: {
|
|
||||||
auditIP: getRequestIP(event),
|
|
||||||
auditURL: getRequestURL(event),
|
|
||||||
auditURLMethod: 'POST',
|
|
||||||
auditAction: 'USER_LOGIN',
|
|
||||||
auditDetails: JSON.stringify({
|
|
||||||
userID: user.userID,
|
|
||||||
username: user.userUsername
|
|
||||||
}),
|
|
||||||
auditUserID: user.userID,
|
|
||||||
auditUsername: user.userUsername
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Extract roles for response
|
|
||||||
const roles = user.userrole.map(ur => ur.role.roleName);
|
|
||||||
|
|
||||||
// Prepare user data for response (remove sensitive information)
|
|
||||||
const userData = {
|
|
||||||
userID: user.userID,
|
|
||||||
username: user.userUsername,
|
|
||||||
fullName: user.userFullName,
|
|
||||||
email: user.userEmail,
|
|
||||||
phone: user.userPhone,
|
|
||||||
department: user.department,
|
|
||||||
roles: roles
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
message: "Login successful",
|
|
||||||
data: userData
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Login error:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function getRequestIP(event) {
|
|
||||||
return event.node.req.headers['x-forwarded-for'] ||
|
|
||||||
event.node.req.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequestURL(event) {
|
|
||||||
return event.node.req.url;
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get department ID from route
|
|
||||||
const id = parseInt(event.context.params.id);
|
|
||||||
|
|
||||||
if (isNaN(id)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid department ID"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department exists and get related entities
|
|
||||||
const existingDept = await prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: id
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
cabinets: {
|
|
||||||
select: {
|
|
||||||
cb_id: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
users: {
|
|
||||||
select: {
|
|
||||||
su_id: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingDept) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Department not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department has related cabinets
|
|
||||||
if (existingDept.cabinets.length > 0) {
|
|
||||||
return {
|
|
||||||
statusCode: 409,
|
|
||||||
message: "Cannot delete department with existing cabinets. Remove all cabinets first.",
|
|
||||||
cabinetCount: existingDept.cabinets.length
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department has related users
|
|
||||||
if (existingDept.users.length > 0) {
|
|
||||||
return {
|
|
||||||
statusCode: 409,
|
|
||||||
message: "Cannot delete department with existing users. Reassign or remove all users first.",
|
|
||||||
userCount: existingDept.users.length
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete department
|
|
||||||
const department = await prisma.department.delete({
|
|
||||||
where: {
|
|
||||||
dp_id: id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create audit log
|
|
||||||
await prisma.audit.create({
|
|
||||||
data: {
|
|
||||||
auditIP: getRequestIP(event),
|
|
||||||
auditURL: getRequestURL(event),
|
|
||||||
auditURLMethod: 'DELETE',
|
|
||||||
auditAction: 'DELETE_DEPARTMENT',
|
|
||||||
auditDetails: JSON.stringify(existingDept),
|
|
||||||
auditUserID: null,
|
|
||||||
auditUsername: null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
message: "Department deleted successfully"
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error deleting department:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function getRequestIP(event) {
|
|
||||||
return event.node.req.headers['x-forwarded-for'] ||
|
|
||||||
event.node.req.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequestURL(event) {
|
|
||||||
return event.node.req.url;
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get department ID from route
|
|
||||||
const id = parseInt(event.context.params.id);
|
|
||||||
|
|
||||||
if (isNaN(id)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid department ID"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get department with related data
|
|
||||||
const department = await prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: id
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
organization: {
|
|
||||||
select: {
|
|
||||||
org_id: true,
|
|
||||||
org_name: true,
|
|
||||||
org_country: true,
|
|
||||||
org_state: true,
|
|
||||||
org_active: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cabinets: {
|
|
||||||
select: {
|
|
||||||
cb_id: true,
|
|
||||||
cb_name: true,
|
|
||||||
cb_private: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
select: {
|
|
||||||
userID: true,
|
|
||||||
userUsername: true,
|
|
||||||
userFullName: true,
|
|
||||||
userEmail: true,
|
|
||||||
userStatus: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!department) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Department not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
data: department
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching department:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,129 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get department ID from route
|
|
||||||
const id = parseInt(event.context.params.id);
|
|
||||||
|
|
||||||
if (isNaN(id)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid department ID"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get request body
|
|
||||||
const body = await readBody(event);
|
|
||||||
|
|
||||||
console.log("PUT Department body:", JSON.stringify(body));
|
|
||||||
|
|
||||||
// Validate request body
|
|
||||||
if (!body || typeof body !== 'object') {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid request body, expected JSON object",
|
|
||||||
received: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department exists
|
|
||||||
const existingDept = await prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingDept) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Department not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate required fields
|
|
||||||
if (!body.dp_name) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Department name is required",
|
|
||||||
receivedBody: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// If org_id is provided, check if organization exists
|
|
||||||
if (body.org_id !== undefined) {
|
|
||||||
const orgId = parseInt(body.org_id);
|
|
||||||
|
|
||||||
if (isNaN(orgId)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid organization ID format"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const organization = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_id: orgId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!organization) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Organization not found",
|
|
||||||
org_id: body.org_id
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update department
|
|
||||||
const department = await prisma.department.update({
|
|
||||||
where: {
|
|
||||||
dp_id: id
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
dp_name: body.dp_name,
|
|
||||||
org_id: body.org_id !== undefined ? parseInt(body.org_id) : existingDept.org_id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create audit log
|
|
||||||
await prisma.audit.create({
|
|
||||||
data: {
|
|
||||||
auditIP: getRequestIP(event),
|
|
||||||
auditURL: getRequestURL(event),
|
|
||||||
auditURLMethod: 'PUT',
|
|
||||||
auditAction: 'UPDATE_DEPARTMENT',
|
|
||||||
auditDetails: JSON.stringify({
|
|
||||||
before: existingDept,
|
|
||||||
after: department
|
|
||||||
}),
|
|
||||||
auditUserID: null,
|
|
||||||
auditUsername: null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
message: "Department updated successfully",
|
|
||||||
data: department
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating department:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function getRequestIP(event) {
|
|
||||||
return event.node.req.headers['x-forwarded-for'] ||
|
|
||||||
event.node.req.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequestURL(event) {
|
|
||||||
return event.node.req.url;
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
import prisma from "../../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get department ID from route
|
|
||||||
const departmentId = parseInt(event.context.params.id);
|
|
||||||
|
|
||||||
if (isNaN(departmentId)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid department ID"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department exists
|
|
||||||
const department = await prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: departmentId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!department) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Department not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get users for this department
|
|
||||||
const users = await prisma.user.findMany({
|
|
||||||
where: {
|
|
||||||
dp_id: departmentId
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
userFullName: 'asc'
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
userID: true,
|
|
||||||
userUsername: true,
|
|
||||||
userFullName: true,
|
|
||||||
userEmail: true,
|
|
||||||
userPhone: true,
|
|
||||||
userStatus: true,
|
|
||||||
userCreatedDate: true,
|
|
||||||
userModifiedDate: true,
|
|
||||||
userrole: {
|
|
||||||
select: {
|
|
||||||
userRoleID: true,
|
|
||||||
role: {
|
|
||||||
select: {
|
|
||||||
roleID: true,
|
|
||||||
roleName: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
data: users,
|
|
||||||
meta: {
|
|
||||||
department: {
|
|
||||||
dp_id: department.dp_id,
|
|
||||||
dp_name: department.dp_name
|
|
||||||
},
|
|
||||||
total: users.length
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching users by department:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,88 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get query parameters
|
|
||||||
const query = getQuery(event);
|
|
||||||
const page = parseInt(query.page) || 1;
|
|
||||||
const limit = parseInt(query.limit) || 10;
|
|
||||||
const search = query.search || '';
|
|
||||||
const orgId = query.org_id ? parseInt(query.org_id) : null;
|
|
||||||
|
|
||||||
// Calculate pagination
|
|
||||||
const skip = (page - 1) * limit;
|
|
||||||
|
|
||||||
// Define filters
|
|
||||||
const filters = {
|
|
||||||
where: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Filter by organization if provided
|
|
||||||
if (orgId) {
|
|
||||||
filters.where.org_id = orgId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add search filter if provided
|
|
||||||
if (search) {
|
|
||||||
filters.where = {
|
|
||||||
...filters.where,
|
|
||||||
dp_name: {
|
|
||||||
contains: search
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get departments with pagination
|
|
||||||
const [departments, total] = await Promise.all([
|
|
||||||
prisma.department.findMany({
|
|
||||||
...filters,
|
|
||||||
skip,
|
|
||||||
take: limit,
|
|
||||||
orderBy: {
|
|
||||||
dp_name: 'asc'
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
organization: {
|
|
||||||
select: {
|
|
||||||
org_id: true,
|
|
||||||
org_name: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_count: {
|
|
||||||
select: {
|
|
||||||
cabinets: true,
|
|
||||||
user: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
prisma.department.count(filters)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Calculate pagination metadata
|
|
||||||
const totalPages = Math.ceil(total / limit);
|
|
||||||
const hasNextPage = page < totalPages;
|
|
||||||
const hasPrevPage = page > 1;
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
data: departments,
|
|
||||||
meta: {
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
total,
|
|
||||||
totalPages,
|
|
||||||
hasNextPage,
|
|
||||||
hasPrevPage
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching departments:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,96 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get request body
|
|
||||||
const body = await readBody(event);
|
|
||||||
|
|
||||||
console.log("POST Department body:", JSON.stringify(body));
|
|
||||||
|
|
||||||
// Validate request body
|
|
||||||
if (!body || typeof body !== 'object') {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid request body, expected JSON object",
|
|
||||||
received: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate required fields
|
|
||||||
if (!body.dp_name) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Department name is required",
|
|
||||||
receivedBody: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!body.org_id) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Organization ID (org_id) is required",
|
|
||||||
receivedBody: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if organization exists
|
|
||||||
const organization = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_id: parseInt(body.org_id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!organization) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Organization not found",
|
|
||||||
org_id: body.org_id
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create department
|
|
||||||
const department = await prisma.department.create({
|
|
||||||
data: {
|
|
||||||
dp_name: body.dp_name,
|
|
||||||
org_id: parseInt(body.org_id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create audit log
|
|
||||||
await prisma.audit.create({
|
|
||||||
data: {
|
|
||||||
auditIP: getRequestIP(event),
|
|
||||||
auditURL: getRequestURL(event),
|
|
||||||
auditURLMethod: 'POST',
|
|
||||||
auditAction: 'CREATE_DEPARTMENT',
|
|
||||||
auditDetails: JSON.stringify(department),
|
|
||||||
auditUserID: null,
|
|
||||||
auditUsername: null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 201,
|
|
||||||
message: "Department created successfully",
|
|
||||||
data: department
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error creating department:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function getRequestIP(event) {
|
|
||||||
return event.node.req.headers['x-forwarded-for'] ||
|
|
||||||
event.node.req.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequestURL(event) {
|
|
||||||
return event.node.req.url;
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get organization ID from route
|
|
||||||
const id = parseInt(event.context.params.id);
|
|
||||||
|
|
||||||
if (isNaN(id)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid organization ID"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if organization exists
|
|
||||||
const existingOrg = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_id: id
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
departments: {
|
|
||||||
select: {
|
|
||||||
dp_id: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingOrg) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Organization not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if organization has departments
|
|
||||||
if (existingOrg.departments.length > 0) {
|
|
||||||
return {
|
|
||||||
statusCode: 409,
|
|
||||||
message: "Cannot delete organization with existing departments. Remove all departments first."
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete organization
|
|
||||||
const organization = await prisma.organization.delete({
|
|
||||||
where: {
|
|
||||||
org_id: id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create audit log
|
|
||||||
await prisma.audit.create({
|
|
||||||
data: {
|
|
||||||
auditIP: getRequestIP(event),
|
|
||||||
auditURL: getRequestURL(event),
|
|
||||||
auditURLMethod: 'DELETE',
|
|
||||||
auditAction: 'DELETE_ORGANIZATION',
|
|
||||||
auditDetails: JSON.stringify(existingOrg),
|
|
||||||
auditUserID: null,
|
|
||||||
auditUsername: null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
message: "Organization deleted successfully"
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error deleting organization:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function getRequestIP(event) {
|
|
||||||
return event.node.req.headers['x-forwarded-for'] ||
|
|
||||||
event.node.req.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequestURL(event) {
|
|
||||||
return event.node.req.url;
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get organization ID from route
|
|
||||||
const id = parseInt(event.context.params.id);
|
|
||||||
|
|
||||||
if (isNaN(id)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid organization ID"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get organization with its departments
|
|
||||||
const organization = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_id: id
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
departments: {
|
|
||||||
select: {
|
|
||||||
dp_id: true,
|
|
||||||
dp_name: true,
|
|
||||||
_count: {
|
|
||||||
select: {
|
|
||||||
users: true,
|
|
||||||
cabinets: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!organization) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Organization not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
data: organization
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching organization:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,126 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get organization ID from route
|
|
||||||
const id = parseInt(event.context.params.id);
|
|
||||||
|
|
||||||
if (isNaN(id)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid organization ID"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get request body
|
|
||||||
const body = await readBody(event);
|
|
||||||
|
|
||||||
console.log("Request body:", JSON.stringify(body));
|
|
||||||
|
|
||||||
// Validate required fields
|
|
||||||
if (!body || typeof body !== 'object') {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid request body, expected JSON object",
|
|
||||||
received: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!body.org_name) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Organization name is required",
|
|
||||||
receivedBody: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if organization exists
|
|
||||||
const existingOrg = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_id: id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingOrg) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Organization not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the name is already taken by another organization
|
|
||||||
if (body.org_name !== existingOrg.org_name) {
|
|
||||||
const nameExists = await prisma.organization.findFirst({
|
|
||||||
where: {
|
|
||||||
org_name: body.org_name,
|
|
||||||
org_id: {
|
|
||||||
not: id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (nameExists) {
|
|
||||||
return {
|
|
||||||
statusCode: 409,
|
|
||||||
message: "Organization with this name already exists"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update organization
|
|
||||||
const organization = await prisma.organization.update({
|
|
||||||
where: {
|
|
||||||
org_id: id
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
org_name: body.org_name,
|
|
||||||
org_address1: body.org_address1 !== undefined ? body.org_address1 : existingOrg.org_address1,
|
|
||||||
org_address2: body.org_address2 !== undefined ? body.org_address2 : existingOrg.org_address2,
|
|
||||||
org_postcode: body.org_postcode !== undefined ? parseInt(body.org_postcode) : existingOrg.org_postcode,
|
|
||||||
org_state: body.org_state !== undefined ? body.org_state : existingOrg.org_state,
|
|
||||||
org_country: body.org_country !== undefined ? body.org_country : existingOrg.org_country,
|
|
||||||
org_active: body.org_active !== undefined ? parseInt(body.org_active) : existingOrg.org_active
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create audit log
|
|
||||||
await prisma.audit.create({
|
|
||||||
data: {
|
|
||||||
auditIP: getRequestIP(event),
|
|
||||||
auditURL: getRequestURL(event),
|
|
||||||
auditURLMethod: 'PUT',
|
|
||||||
auditAction: 'UPDATE_ORGANIZATION',
|
|
||||||
auditDetails: JSON.stringify({
|
|
||||||
before: existingOrg,
|
|
||||||
after: organization
|
|
||||||
}),
|
|
||||||
auditUserID: null,
|
|
||||||
auditUsername: null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
message: "Organization updated successfully",
|
|
||||||
data: organization
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating organization:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function getRequestIP(event) {
|
|
||||||
return event.node.req.headers['x-forwarded-for'] ||
|
|
||||||
event.node.req.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequestURL(event) {
|
|
||||||
return event.node.req.url;
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
import prisma from "../../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get organization ID from route
|
|
||||||
const orgId = parseInt(event.context.params.id);
|
|
||||||
|
|
||||||
if (isNaN(orgId)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid organization ID"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if organization exists
|
|
||||||
const organization = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_id: orgId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!organization) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Organization not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get departments for this organization
|
|
||||||
const departments = await prisma.department.findMany({
|
|
||||||
where: {
|
|
||||||
org_id: orgId
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
dp_name: 'asc'
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
_count: {
|
|
||||||
select: {
|
|
||||||
cabinets: true,
|
|
||||||
users: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
data: departments,
|
|
||||||
meta: {
|
|
||||||
organization: {
|
|
||||||
org_id: organization.org_id,
|
|
||||||
org_name: organization.org_name
|
|
||||||
},
|
|
||||||
total: departments.length
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching departments by organization:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,77 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get query parameters
|
|
||||||
const query = getQuery(event);
|
|
||||||
const page = parseInt(query.page) || 1;
|
|
||||||
const limit = parseInt(query.limit) || 10;
|
|
||||||
const search = query.search || '';
|
|
||||||
|
|
||||||
// Calculate pagination
|
|
||||||
const skip = (page - 1) * limit;
|
|
||||||
|
|
||||||
// Define filters
|
|
||||||
const filters = {
|
|
||||||
where: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add search filter if provided
|
|
||||||
if (search) {
|
|
||||||
filters.where = {
|
|
||||||
OR: [
|
|
||||||
{ org_name: { contains: search } },
|
|
||||||
{ org_address1: { contains: search } },
|
|
||||||
{ org_state: { contains: search } },
|
|
||||||
{ org_country: { contains: search } }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get organizations with pagination
|
|
||||||
const [organizations, total] = await Promise.all([
|
|
||||||
prisma.organization.findMany({
|
|
||||||
...filters,
|
|
||||||
skip,
|
|
||||||
take: limit,
|
|
||||||
orderBy: {
|
|
||||||
org_name: 'asc'
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
_count: {
|
|
||||||
select: {
|
|
||||||
departments: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
prisma.organization.count(filters)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Calculate pagination metadata
|
|
||||||
const totalPages = Math.ceil(total / limit);
|
|
||||||
const hasNextPage = page < totalPages;
|
|
||||||
const hasPrevPage = page > 1;
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
data: organizations,
|
|
||||||
meta: {
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
total,
|
|
||||||
totalPages,
|
|
||||||
hasNextPage,
|
|
||||||
hasPrevPage
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching organizations:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,92 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get request body
|
|
||||||
const body = await readBody(event);
|
|
||||||
|
|
||||||
console.log("POST Request body:", JSON.stringify(body));
|
|
||||||
|
|
||||||
// Validate request body
|
|
||||||
if (!body || typeof body !== 'object') {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid request body, expected JSON object",
|
|
||||||
received: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate required fields
|
|
||||||
if (!body.org_name) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Organization name is required",
|
|
||||||
receivedBody: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if organization already exists
|
|
||||||
const existingOrg = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_name: body.org_name
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existingOrg) {
|
|
||||||
return {
|
|
||||||
statusCode: 409,
|
|
||||||
message: "Organization with this name already exists"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create organization
|
|
||||||
const organization = await prisma.organization.create({
|
|
||||||
data: {
|
|
||||||
org_name: body.org_name,
|
|
||||||
org_address1: body.org_address1 || null,
|
|
||||||
org_address2: body.org_address2 || null,
|
|
||||||
org_postcode: body.org_postcode ? parseInt(body.org_postcode) : null,
|
|
||||||
org_state: body.org_state || null,
|
|
||||||
org_country: body.org_country || null,
|
|
||||||
org_active: body.org_active !== undefined ? parseInt(body.org_active) : 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create audit log
|
|
||||||
await prisma.audit.create({
|
|
||||||
data: {
|
|
||||||
auditIP: getRequestIP(event),
|
|
||||||
auditURL: getRequestURL(event),
|
|
||||||
auditURLMethod: 'POST',
|
|
||||||
auditAction: 'CREATE_ORGANIZATION',
|
|
||||||
auditDetails: JSON.stringify(organization),
|
|
||||||
auditUserID: null,
|
|
||||||
auditUsername: null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 201,
|
|
||||||
message: "Organization created successfully",
|
|
||||||
data: organization
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error creating organization:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function getRequestIP(event) {
|
|
||||||
return event.node.req.headers['x-forwarded-for'] ||
|
|
||||||
event.node.req.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequestURL(event) {
|
|
||||||
return event.node.req.url;
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get user ID from route
|
|
||||||
const id = parseInt(event.context.params.id);
|
|
||||||
|
|
||||||
if (isNaN(id)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid user ID"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user exists
|
|
||||||
const existingUser = await prisma.user.findUnique({
|
|
||||||
where: {
|
|
||||||
userID: id
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
userrole: {
|
|
||||||
select: {
|
|
||||||
userRoleID: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingUser) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "User not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user has any roles assigned
|
|
||||||
if (existingUser.userrole && existingUser.userrole.length > 0) {
|
|
||||||
// Delete all associated user roles first
|
|
||||||
await prisma.userrole.deleteMany({
|
|
||||||
where: {
|
|
||||||
userRoleUserID: id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a sanitized copy of user data for audit log
|
|
||||||
const sanitizedUser = {
|
|
||||||
...existingUser,
|
|
||||||
userPassword: "[REDACTED]",
|
|
||||||
userSecretKey: "[REDACTED]"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Delete user
|
|
||||||
await prisma.user.delete({
|
|
||||||
where: {
|
|
||||||
userID: id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create audit log
|
|
||||||
await prisma.audit.create({
|
|
||||||
data: {
|
|
||||||
auditIP: getRequestIP(event),
|
|
||||||
auditURL: getRequestURL(event),
|
|
||||||
auditURLMethod: 'DELETE',
|
|
||||||
auditAction: 'DELETE_USER',
|
|
||||||
auditDetails: JSON.stringify(sanitizedUser),
|
|
||||||
auditUserID: null,
|
|
||||||
auditUsername: null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
message: "User deleted successfully"
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error deleting user:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function getRequestIP(event) {
|
|
||||||
return event.node.req.headers['x-forwarded-for'] ||
|
|
||||||
event.node.req.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequestURL(event) {
|
|
||||||
return event.node.req.url;
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get user ID from route
|
|
||||||
const id = parseInt(event.context.params.id);
|
|
||||||
|
|
||||||
if (isNaN(id)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid user ID"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user with related data
|
|
||||||
const user = await prisma.user.findUnique({
|
|
||||||
where: {
|
|
||||||
userID: id
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
userID: true,
|
|
||||||
userUsername: true,
|
|
||||||
userFullName: true,
|
|
||||||
userEmail: true,
|
|
||||||
userPhone: true,
|
|
||||||
userStatus: true,
|
|
||||||
dp_id: true,
|
|
||||||
userCreatedDate: true,
|
|
||||||
userModifiedDate: true,
|
|
||||||
department: {
|
|
||||||
select: {
|
|
||||||
dp_id: true,
|
|
||||||
dp_name: true,
|
|
||||||
organization: {
|
|
||||||
select: {
|
|
||||||
org_id: true,
|
|
||||||
org_name: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
userrole: {
|
|
||||||
select: {
|
|
||||||
userRoleID: true,
|
|
||||||
role: {
|
|
||||||
select: {
|
|
||||||
roleID: true,
|
|
||||||
roleName: true,
|
|
||||||
roleDescription: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "User not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
data: user
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching user:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,162 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
import sha256 from "crypto-js/sha256";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get user ID from route
|
|
||||||
const id = parseInt(event.context.params.id);
|
|
||||||
|
|
||||||
if (isNaN(id)) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid user ID"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get request body
|
|
||||||
const body = await readBody(event);
|
|
||||||
|
|
||||||
console.log("PUT User body:", JSON.stringify({
|
|
||||||
...body,
|
|
||||||
userPassword: body.userPassword ? '[REDACTED]' : undefined
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Validate request body
|
|
||||||
if (!body || typeof body !== 'object') {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid request body, expected JSON object",
|
|
||||||
received: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user exists
|
|
||||||
const existingUser = await prisma.user.findUnique({
|
|
||||||
where: {
|
|
||||||
userID: id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingUser) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "User not found"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department exists if department ID is provided
|
|
||||||
if (body.dp_id) {
|
|
||||||
const department = await prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: parseInt(body.dp_id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!department) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Department not found",
|
|
||||||
dp_id: body.dp_id
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if username is taken by another user
|
|
||||||
if (body.userUsername && body.userUsername !== existingUser.userUsername) {
|
|
||||||
const usernameExists = await prisma.user.findFirst({
|
|
||||||
where: {
|
|
||||||
userUsername: body.userUsername,
|
|
||||||
userID: {
|
|
||||||
not: id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (usernameExists) {
|
|
||||||
return {
|
|
||||||
statusCode: 409,
|
|
||||||
message: "Username is already taken by another user"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare update data
|
|
||||||
const updateData = {};
|
|
||||||
|
|
||||||
// Only update fields that are provided
|
|
||||||
if (body.userUsername !== undefined) updateData.userUsername = body.userUsername;
|
|
||||||
if (body.userFullName !== undefined) updateData.userFullName = body.userFullName;
|
|
||||||
if (body.userEmail !== undefined) updateData.userEmail = body.userEmail;
|
|
||||||
if (body.userPhone !== undefined) updateData.userPhone = body.userPhone;
|
|
||||||
if (body.userStatus !== undefined) updateData.userStatus = body.userStatus ? body.userStatus.toUpperCase() : null;
|
|
||||||
if (body.dp_id !== undefined) updateData.dp_id = body.dp_id ? parseInt(body.dp_id) : null;
|
|
||||||
|
|
||||||
// Hash password if provided
|
|
||||||
if (body.userPassword) {
|
|
||||||
updateData.userPassword = sha256(body.userPassword).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update modified date
|
|
||||||
updateData.userModifiedDate = new Date();
|
|
||||||
|
|
||||||
// Update user
|
|
||||||
const user = await prisma.user.update({
|
|
||||||
where: {
|
|
||||||
userID: id
|
|
||||||
},
|
|
||||||
data: updateData
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create audit log
|
|
||||||
await prisma.audit.create({
|
|
||||||
data: {
|
|
||||||
auditIP: getRequestIP(event),
|
|
||||||
auditURL: getRequestURL(event),
|
|
||||||
auditURLMethod: 'PUT',
|
|
||||||
auditAction: 'UPDATE_USER',
|
|
||||||
auditDetails: JSON.stringify({
|
|
||||||
before: {
|
|
||||||
...existingUser,
|
|
||||||
userPassword: "[REDACTED]"
|
|
||||||
},
|
|
||||||
after: {
|
|
||||||
...user,
|
|
||||||
userPassword: "[REDACTED]"
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
auditUserID: null,
|
|
||||||
auditUsername: null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove password from response
|
|
||||||
const userResponse = {
|
|
||||||
...user,
|
|
||||||
userPassword: undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
message: "User updated successfully",
|
|
||||||
data: userResponse
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error updating user:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function getRequestIP(event) {
|
|
||||||
return event.node.req.headers['x-forwarded-for'] ||
|
|
||||||
event.node.req.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequestURL(event) {
|
|
||||||
return event.node.req.url;
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get query parameters
|
|
||||||
const query = getQuery(event);
|
|
||||||
const page = parseInt(query.page) || 1;
|
|
||||||
const limit = parseInt(query.limit) || 10;
|
|
||||||
const search = query.search || '';
|
|
||||||
const departmentId = query.dp_id ? parseInt(query.dp_id) : null;
|
|
||||||
const status = query.status || null;
|
|
||||||
|
|
||||||
// Calculate pagination
|
|
||||||
const skip = (page - 1) * limit;
|
|
||||||
|
|
||||||
// Define filters
|
|
||||||
const filters = {
|
|
||||||
where: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Filter by department if provided
|
|
||||||
if (departmentId) {
|
|
||||||
filters.where.dp_id = departmentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter by status if provided
|
|
||||||
if (status) {
|
|
||||||
filters.where.userStatus = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add search filter if provided
|
|
||||||
if (search) {
|
|
||||||
filters.where = {
|
|
||||||
...filters.where,
|
|
||||||
OR: [
|
|
||||||
{ userUsername: { contains: search } },
|
|
||||||
{ userFullName: { contains: search } },
|
|
||||||
{ userEmail: { contains: search } }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get users with pagination
|
|
||||||
const [users, total] = await Promise.all([
|
|
||||||
prisma.user.findMany({
|
|
||||||
...filters,
|
|
||||||
skip,
|
|
||||||
take: limit,
|
|
||||||
orderBy: {
|
|
||||||
userFullName: 'asc'
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
userID: true,
|
|
||||||
userUsername: true,
|
|
||||||
userFullName: true,
|
|
||||||
userEmail: true,
|
|
||||||
userPhone: true,
|
|
||||||
userStatus: true,
|
|
||||||
dp_id: true,
|
|
||||||
userCreatedDate: true,
|
|
||||||
userModifiedDate: true,
|
|
||||||
department: {
|
|
||||||
select: {
|
|
||||||
dp_id: true,
|
|
||||||
dp_name: true,
|
|
||||||
organization: {
|
|
||||||
select: {
|
|
||||||
org_id: true,
|
|
||||||
org_name: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
userrole: {
|
|
||||||
select: {
|
|
||||||
userRoleID: true,
|
|
||||||
role: {
|
|
||||||
select: {
|
|
||||||
roleID: true,
|
|
||||||
roleName: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
prisma.user.count(filters)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Calculate pagination metadata
|
|
||||||
const totalPages = Math.ceil(total / limit);
|
|
||||||
const hasNextPage = page < totalPages;
|
|
||||||
const hasPrevPage = page > 1;
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
data: users,
|
|
||||||
meta: {
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
total,
|
|
||||||
totalPages,
|
|
||||||
hasNextPage,
|
|
||||||
hasPrevPage
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching users:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,153 +0,0 @@
|
|||||||
import prisma from "../../utils/prisma";
|
|
||||||
import sha256 from "crypto-js/sha256";
|
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
|
||||||
try {
|
|
||||||
// Get request body
|
|
||||||
const body = await readBody(event);
|
|
||||||
|
|
||||||
console.log("POST User body:", JSON.stringify(body));
|
|
||||||
|
|
||||||
// Validate request body
|
|
||||||
if (!body || typeof body !== 'object') {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Invalid request body, expected JSON object",
|
|
||||||
received: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate required fields
|
|
||||||
if (!body.userUsername) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Username is required",
|
|
||||||
receivedBody: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!body.userPassword) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Password is required",
|
|
||||||
receivedBody: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!body.userFullName) {
|
|
||||||
return {
|
|
||||||
statusCode: 400,
|
|
||||||
message: "Full name is required",
|
|
||||||
receivedBody: body
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department exists if department ID is provided
|
|
||||||
if (body.dp_id) {
|
|
||||||
const department = await prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: parseInt(body.dp_id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!department) {
|
|
||||||
return {
|
|
||||||
statusCode: 404,
|
|
||||||
message: "Department not found",
|
|
||||||
dp_id: body.dp_id
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if username already exists
|
|
||||||
const existingUser = await prisma.user.findFirst({
|
|
||||||
where: {
|
|
||||||
userUsername: body.userUsername
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existingUser) {
|
|
||||||
return {
|
|
||||||
statusCode: 409,
|
|
||||||
message: "Username already exists"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create random secret key
|
|
||||||
const secretKey = generateRandomKey(32);
|
|
||||||
|
|
||||||
// Encrypt password with SHA256
|
|
||||||
const hashedPassword = sha256(body.userPassword).toString();
|
|
||||||
|
|
||||||
// Create user
|
|
||||||
const user = await prisma.user.create({
|
|
||||||
data: {
|
|
||||||
userSecretKey: secretKey,
|
|
||||||
userUsername: body.userUsername,
|
|
||||||
userPassword: hashedPassword,
|
|
||||||
userFullName: body.userFullName,
|
|
||||||
userEmail: body.userEmail || null,
|
|
||||||
userPhone: body.userPhone || null,
|
|
||||||
userStatus: body.userStatus ? body.userStatus.toUpperCase() : "ACTIVE",
|
|
||||||
dp_id: body.dp_id ? parseInt(body.dp_id) : null,
|
|
||||||
userCreatedDate: new Date(),
|
|
||||||
userModifiedDate: new Date()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create audit log
|
|
||||||
await prisma.audit.create({
|
|
||||||
data: {
|
|
||||||
auditIP: getRequestIP(event),
|
|
||||||
auditURL: getRequestURL(event),
|
|
||||||
auditURLMethod: 'POST',
|
|
||||||
auditAction: 'CREATE_USER',
|
|
||||||
auditDetails: JSON.stringify({
|
|
||||||
...user,
|
|
||||||
userPassword: "[REDACTED]" // Redact password in audit log
|
|
||||||
}),
|
|
||||||
auditUserID: null,
|
|
||||||
auditUsername: null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove password from response
|
|
||||||
const userResponse = {
|
|
||||||
...user,
|
|
||||||
userPassword: undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 201,
|
|
||||||
message: "User created successfully",
|
|
||||||
data: userResponse
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error creating user:", error);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
message: "Internal server error",
|
|
||||||
error: error.message
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function getRequestIP(event) {
|
|
||||||
return event.node.req.headers['x-forwarded-for'] ||
|
|
||||||
event.node.req.connection.remoteAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequestURL(event) {
|
|
||||||
return event.node.req.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateRandomKey(length) {
|
|
||||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
||||||
let result = '';
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
@ -1,301 +0,0 @@
|
|||||||
import prisma from './prisma';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collection of utility functions for working with departments
|
|
||||||
*/
|
|
||||||
export const DepartmentUtils = {
|
|
||||||
/**
|
|
||||||
* Create a new department
|
|
||||||
*
|
|
||||||
* @param {Object} data Department data
|
|
||||||
* @param {string} data.dp_name Department name
|
|
||||||
* @param {number} data.org_id Organization ID
|
|
||||||
* @returns {Promise<Object>} Created department
|
|
||||||
*/
|
|
||||||
async create(data) {
|
|
||||||
if (!data.dp_name) {
|
|
||||||
throw new Error('Department name is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data.org_id) {
|
|
||||||
throw new Error('Organization ID is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if organization exists
|
|
||||||
const organization = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_id: parseInt(data.org_id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!organization) {
|
|
||||||
throw new Error(`Organization with ID ${data.org_id} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return prisma.department.create({
|
|
||||||
data: {
|
|
||||||
dp_name: data.dp_name,
|
|
||||||
org_id: parseInt(data.org_id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all departments with pagination and filtering
|
|
||||||
*
|
|
||||||
* @param {Object} options Query options
|
|
||||||
* @param {number} options.page Page number
|
|
||||||
* @param {number} options.limit Items per page
|
|
||||||
* @param {string} options.search Search term
|
|
||||||
* @param {number} options.orgId Filter by organization ID
|
|
||||||
* @returns {Promise<Object>} Departments and pagination metadata
|
|
||||||
*/
|
|
||||||
async getAll({ page = 1, limit = 10, search = '', orgId = null }) {
|
|
||||||
const skip = (page - 1) * limit;
|
|
||||||
|
|
||||||
// Define filters
|
|
||||||
const filters = {
|
|
||||||
where: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Filter by organization if provided
|
|
||||||
if (orgId) {
|
|
||||||
filters.where.org_id = parseInt(orgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add search filter if provided
|
|
||||||
if (search) {
|
|
||||||
filters.where = {
|
|
||||||
...filters.where,
|
|
||||||
dp_name: {
|
|
||||||
contains: search
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get departments with pagination
|
|
||||||
const [departments, total] = await Promise.all([
|
|
||||||
prisma.department.findMany({
|
|
||||||
...filters,
|
|
||||||
skip,
|
|
||||||
take: limit,
|
|
||||||
orderBy: {
|
|
||||||
dp_name: 'asc'
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
organization: {
|
|
||||||
select: {
|
|
||||||
org_id: true,
|
|
||||||
org_name: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_count: {
|
|
||||||
select: {
|
|
||||||
cabinets: true,
|
|
||||||
user: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
prisma.department.count(filters)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Calculate pagination metadata
|
|
||||||
const totalPages = Math.ceil(total / limit);
|
|
||||||
|
|
||||||
return {
|
|
||||||
departments,
|
|
||||||
pagination: {
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
total,
|
|
||||||
totalPages,
|
|
||||||
hasNextPage: page < totalPages,
|
|
||||||
hasPrevPage: page > 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get department by ID
|
|
||||||
*
|
|
||||||
* @param {number} id Department ID
|
|
||||||
* @returns {Promise<Object|null>} Department or null if not found
|
|
||||||
*/
|
|
||||||
async getById(id) {
|
|
||||||
if (isNaN(parseInt(id))) {
|
|
||||||
throw new Error('Invalid department ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
return prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: parseInt(id)
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
organization: {
|
|
||||||
select: {
|
|
||||||
org_id: true,
|
|
||||||
org_name: true,
|
|
||||||
org_country: true,
|
|
||||||
org_state: true,
|
|
||||||
org_active: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cabinets: {
|
|
||||||
select: {
|
|
||||||
cb_id: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
select: {
|
|
||||||
userID: true,
|
|
||||||
userUsername: true,
|
|
||||||
userFullName: true,
|
|
||||||
userEmail: true,
|
|
||||||
userStatus: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a department
|
|
||||||
*
|
|
||||||
* @param {number} id Department ID
|
|
||||||
* @param {Object} data Department data
|
|
||||||
* @returns {Promise<Object>} Updated department
|
|
||||||
*/
|
|
||||||
async update(id, data) {
|
|
||||||
if (isNaN(parseInt(id))) {
|
|
||||||
throw new Error('Invalid department ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department exists
|
|
||||||
const existingDept = await prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: parseInt(id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingDept) {
|
|
||||||
throw new Error('Department not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate required fields
|
|
||||||
if (!data.dp_name) {
|
|
||||||
throw new Error('Department name is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
// If org_id is provided, check if organization exists
|
|
||||||
if (data.org_id !== undefined) {
|
|
||||||
const orgId = parseInt(data.org_id);
|
|
||||||
|
|
||||||
if (isNaN(orgId)) {
|
|
||||||
throw new Error('Invalid organization ID format');
|
|
||||||
}
|
|
||||||
|
|
||||||
const organization = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_id: orgId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!organization) {
|
|
||||||
throw new Error(`Organization with ID ${data.org_id} not found`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return prisma.department.update({
|
|
||||||
where: {
|
|
||||||
dp_id: parseInt(id)
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
dp_name: data.dp_name,
|
|
||||||
org_id: data.org_id !== undefined ? parseInt(data.org_id) : existingDept.org_id
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a department
|
|
||||||
*
|
|
||||||
* @param {number} id Department ID
|
|
||||||
* @returns {Promise<Object>} Deleted department
|
|
||||||
*/
|
|
||||||
async delete(id) {
|
|
||||||
if (isNaN(parseInt(id))) {
|
|
||||||
throw new Error('Invalid department ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department exists and get related entities
|
|
||||||
const existingDept = await prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: parseInt(id)
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
cabinets: {
|
|
||||||
select: {
|
|
||||||
cb_id: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
select: {
|
|
||||||
userID: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingDept) {
|
|
||||||
throw new Error('Department not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department has related cabinets
|
|
||||||
if (existingDept.cabinets.length > 0) {
|
|
||||||
throw new Error(`Cannot delete department with existing cabinets. Remove all cabinets first (${existingDept.cabinets.length} found).`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department has related users
|
|
||||||
if (existingDept.user.length > 0) {
|
|
||||||
throw new Error(`Cannot delete department with existing users. Reassign or remove all users first (${existingDept.user.length} found).`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return prisma.department.delete({
|
|
||||||
where: {
|
|
||||||
dp_id: parseInt(id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get departments by organization ID
|
|
||||||
*
|
|
||||||
* @param {number} orgId Organization ID
|
|
||||||
* @returns {Promise<Array>} List of departments
|
|
||||||
*/
|
|
||||||
async getByOrganization(orgId) {
|
|
||||||
if (isNaN(parseInt(orgId))) {
|
|
||||||
throw new Error('Invalid organization ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
return prisma.department.findMany({
|
|
||||||
where: {
|
|
||||||
org_id: parseInt(orgId)
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
dp_name: 'asc'
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
_count: {
|
|
||||||
select: {
|
|
||||||
cabinets: true,
|
|
||||||
user: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DepartmentUtils;
|
|
@ -1,240 +0,0 @@
|
|||||||
import prisma from './prisma';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collection of utility functions for working with organizations
|
|
||||||
*/
|
|
||||||
export const OrganizationUtils = {
|
|
||||||
/**
|
|
||||||
* Create a new organization
|
|
||||||
*
|
|
||||||
* @param {Object} data Organization data
|
|
||||||
* @returns {Promise<Object>} Created organization
|
|
||||||
*/
|
|
||||||
async create(data) {
|
|
||||||
if (!data.org_name) {
|
|
||||||
throw new Error('Organization name is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if organization already exists
|
|
||||||
const existingOrg = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_name: data.org_name
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existingOrg) {
|
|
||||||
throw new Error('Organization with this name already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
return prisma.organization.create({
|
|
||||||
data: {
|
|
||||||
org_name: data.org_name,
|
|
||||||
org_address1: data.org_address1 || null,
|
|
||||||
org_address2: data.org_address2 || null,
|
|
||||||
org_postcode: data.org_postcode ? parseInt(data.org_postcode) : null,
|
|
||||||
org_state: data.org_state || null,
|
|
||||||
org_country: data.org_country || null,
|
|
||||||
org_active: data.org_active !== undefined ? parseInt(data.org_active) : 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all organizations with pagination
|
|
||||||
*
|
|
||||||
* @param {Object} options Query options
|
|
||||||
* @param {number} options.page Page number
|
|
||||||
* @param {number} options.limit Items per page
|
|
||||||
* @param {string} options.search Search term
|
|
||||||
* @returns {Promise<Object>} Organizations and pagination metadata
|
|
||||||
*/
|
|
||||||
async getAll({ page = 1, limit = 10, search = '' }) {
|
|
||||||
const skip = (page - 1) * limit;
|
|
||||||
|
|
||||||
// Define filters
|
|
||||||
const filters = {
|
|
||||||
where: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add search filter if provided
|
|
||||||
if (search) {
|
|
||||||
filters.where = {
|
|
||||||
OR: [
|
|
||||||
{ org_name: { contains: search } },
|
|
||||||
{ org_address1: { contains: search } },
|
|
||||||
{ org_state: { contains: search } },
|
|
||||||
{ org_country: { contains: search } }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get organizations with pagination
|
|
||||||
const [organizations, total] = await Promise.all([
|
|
||||||
prisma.organization.findMany({
|
|
||||||
...filters,
|
|
||||||
skip,
|
|
||||||
take: limit,
|
|
||||||
orderBy: {
|
|
||||||
org_name: 'asc'
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
_count: {
|
|
||||||
select: {
|
|
||||||
departments: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
prisma.organization.count(filters)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Calculate pagination metadata
|
|
||||||
const totalPages = Math.ceil(total / limit);
|
|
||||||
|
|
||||||
return {
|
|
||||||
organizations,
|
|
||||||
pagination: {
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
total,
|
|
||||||
totalPages,
|
|
||||||
hasNextPage: page < totalPages,
|
|
||||||
hasPrevPage: page > 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get organization by ID
|
|
||||||
*
|
|
||||||
* @param {number} id Organization ID
|
|
||||||
* @returns {Promise<Object|null>} Organization or null if not found
|
|
||||||
*/
|
|
||||||
async getById(id) {
|
|
||||||
if (isNaN(parseInt(id))) {
|
|
||||||
throw new Error('Invalid organization ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
return prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_id: parseInt(id)
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
departments: {
|
|
||||||
select: {
|
|
||||||
dp_id: true,
|
|
||||||
dp_name: true,
|
|
||||||
_count: {
|
|
||||||
select: {
|
|
||||||
users: true,
|
|
||||||
cabinets: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update an organization
|
|
||||||
*
|
|
||||||
* @param {number} id Organization ID
|
|
||||||
* @param {Object} data Organization data
|
|
||||||
* @returns {Promise<Object>} Updated organization
|
|
||||||
*/
|
|
||||||
async update(id, data) {
|
|
||||||
if (isNaN(parseInt(id))) {
|
|
||||||
throw new Error('Invalid organization ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data.org_name) {
|
|
||||||
throw new Error('Organization name is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if organization exists
|
|
||||||
const existingOrg = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_id: parseInt(id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingOrg) {
|
|
||||||
throw new Error('Organization not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if name is already taken by another organization
|
|
||||||
if (data.org_name !== existingOrg.org_name) {
|
|
||||||
const nameExists = await prisma.organization.findFirst({
|
|
||||||
where: {
|
|
||||||
org_name: data.org_name,
|
|
||||||
org_id: {
|
|
||||||
not: parseInt(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (nameExists) {
|
|
||||||
throw new Error('Organization with this name already exists');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return prisma.organization.update({
|
|
||||||
where: {
|
|
||||||
org_id: parseInt(id)
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
org_name: data.org_name,
|
|
||||||
org_address1: data.org_address1 || null,
|
|
||||||
org_address2: data.org_address2 || null,
|
|
||||||
org_postcode: data.org_postcode ? parseInt(data.org_postcode) : null,
|
|
||||||
org_state: data.org_state || null,
|
|
||||||
org_country: data.org_country || null,
|
|
||||||
org_active: data.org_active !== undefined ? parseInt(data.org_active) : existingOrg.org_active
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete an organization
|
|
||||||
*
|
|
||||||
* @param {number} id Organization ID
|
|
||||||
* @returns {Promise<Object>} Deleted organization
|
|
||||||
*/
|
|
||||||
async delete(id) {
|
|
||||||
if (isNaN(parseInt(id))) {
|
|
||||||
throw new Error('Invalid organization ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if organization exists
|
|
||||||
const existingOrg = await prisma.organization.findUnique({
|
|
||||||
where: {
|
|
||||||
org_id: parseInt(id)
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
departments: {
|
|
||||||
select: {
|
|
||||||
dp_id: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingOrg) {
|
|
||||||
throw new Error('Organization not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if organization has departments
|
|
||||||
if (existingOrg.departments.length > 0) {
|
|
||||||
throw new Error('Cannot delete organization with existing departments. Remove all departments first.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return prisma.organization.delete({
|
|
||||||
where: {
|
|
||||||
org_id: parseInt(id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OrganizationUtils;
|
|
@ -1,490 +0,0 @@
|
|||||||
import prisma from './prisma';
|
|
||||||
import sha256 from 'crypto-js/sha256';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collection of utility functions for working with users
|
|
||||||
*/
|
|
||||||
export const UserUtils = {
|
|
||||||
/**
|
|
||||||
* Create a new user
|
|
||||||
*
|
|
||||||
* @param {Object} data User data
|
|
||||||
* @param {string} data.userUsername Username
|
|
||||||
* @param {string} data.userPassword Password (will be hashed)
|
|
||||||
* @param {string} data.userFullName Full name
|
|
||||||
* @param {string} data.userEmail Email (optional)
|
|
||||||
* @param {string} data.userPhone Phone (optional)
|
|
||||||
* @param {string} data.userStatus Status (optional, defaults to "active")
|
|
||||||
* @param {number} data.dp_id Department ID (optional)
|
|
||||||
* @returns {Promise<Object>} Created user (without password)
|
|
||||||
*/
|
|
||||||
async create(data) {
|
|
||||||
// Validate required fields
|
|
||||||
if (!data.userUsername) {
|
|
||||||
throw new Error('Username is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data.userPassword) {
|
|
||||||
throw new Error('Password is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data.userFullName) {
|
|
||||||
throw new Error('Full name is required');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if username already exists
|
|
||||||
const existingUser = await prisma.user.findFirst({
|
|
||||||
where: {
|
|
||||||
userUsername: data.userUsername
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existingUser) {
|
|
||||||
throw new Error('Username already exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department exists if provided
|
|
||||||
if (data.dp_id) {
|
|
||||||
const department = await prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: parseInt(data.dp_id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!department) {
|
|
||||||
throw new Error(`Department with ID ${data.dp_id} not found`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create random secret key
|
|
||||||
const secretKey = this.generateRandomKey(32);
|
|
||||||
|
|
||||||
// Encrypt password with SHA256
|
|
||||||
const hashedPassword = sha256(data.userPassword).toString();
|
|
||||||
|
|
||||||
// Create user
|
|
||||||
const user = await prisma.user.create({
|
|
||||||
data: {
|
|
||||||
userSecretKey: secretKey,
|
|
||||||
userUsername: data.userUsername,
|
|
||||||
userPassword: hashedPassword,
|
|
||||||
userFullName: data.userFullName,
|
|
||||||
userEmail: data.userEmail || null,
|
|
||||||
userPhone: data.userPhone || null,
|
|
||||||
userStatus: data.userStatus ? data.userStatus.toUpperCase() : "ACTIVE",
|
|
||||||
dp_id: data.dp_id ? parseInt(data.dp_id) : null,
|
|
||||||
userCreatedDate: new Date(),
|
|
||||||
userModifiedDate: new Date()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return user without password
|
|
||||||
const { userPassword, ...userWithoutPassword } = user;
|
|
||||||
return userWithoutPassword;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all users with pagination and filtering
|
|
||||||
*
|
|
||||||
* @param {Object} options Query options
|
|
||||||
* @param {number} options.page Page number
|
|
||||||
* @param {number} options.limit Items per page
|
|
||||||
* @param {string} options.search Search term
|
|
||||||
* @param {number} options.departmentId Filter by department ID
|
|
||||||
* @param {string} options.status Filter by status
|
|
||||||
* @returns {Promise<Object>} Users and pagination metadata
|
|
||||||
*/
|
|
||||||
async getAll({ page = 1, limit = 10, search = '', departmentId = null, status = null }) {
|
|
||||||
const skip = (page - 1) * limit;
|
|
||||||
|
|
||||||
// Define filters
|
|
||||||
const filters = {
|
|
||||||
where: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Filter by department if provided
|
|
||||||
if (departmentId) {
|
|
||||||
filters.where.dp_id = parseInt(departmentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter by status if provided
|
|
||||||
if (status) {
|
|
||||||
filters.where.userStatus = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add search filter if provided
|
|
||||||
if (search) {
|
|
||||||
filters.where = {
|
|
||||||
...filters.where,
|
|
||||||
OR: [
|
|
||||||
{ userUsername: { contains: search } },
|
|
||||||
{ userFullName: { contains: search } },
|
|
||||||
{ userEmail: { contains: search } }
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get users with pagination
|
|
||||||
const [users, total] = await Promise.all([
|
|
||||||
prisma.user.findMany({
|
|
||||||
...filters,
|
|
||||||
skip,
|
|
||||||
take: limit,
|
|
||||||
orderBy: {
|
|
||||||
userFullName: 'asc'
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
userID: true,
|
|
||||||
userUsername: true,
|
|
||||||
userFullName: true,
|
|
||||||
userEmail: true,
|
|
||||||
userPhone: true,
|
|
||||||
userStatus: true,
|
|
||||||
dp_id: true,
|
|
||||||
userCreatedDate: true,
|
|
||||||
userModifiedDate: true,
|
|
||||||
department: {
|
|
||||||
select: {
|
|
||||||
dp_id: true,
|
|
||||||
dp_name: true,
|
|
||||||
organization: {
|
|
||||||
select: {
|
|
||||||
org_id: true,
|
|
||||||
org_name: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
userrole: {
|
|
||||||
select: {
|
|
||||||
userRoleID: true,
|
|
||||||
role: {
|
|
||||||
select: {
|
|
||||||
roleID: true,
|
|
||||||
roleName: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
prisma.user.count(filters)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Calculate pagination metadata
|
|
||||||
const totalPages = Math.ceil(total / limit);
|
|
||||||
|
|
||||||
return {
|
|
||||||
users,
|
|
||||||
pagination: {
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
total,
|
|
||||||
totalPages,
|
|
||||||
hasNextPage: page < totalPages,
|
|
||||||
hasPrevPage: page > 1
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get user by ID
|
|
||||||
*
|
|
||||||
* @param {number} id User ID
|
|
||||||
* @returns {Promise<Object|null>} User or null if not found
|
|
||||||
*/
|
|
||||||
async getById(id) {
|
|
||||||
if (isNaN(parseInt(id))) {
|
|
||||||
throw new Error('Invalid user ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await prisma.user.findUnique({
|
|
||||||
where: {
|
|
||||||
userID: parseInt(id)
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
userID: true,
|
|
||||||
userUsername: true,
|
|
||||||
userFullName: true,
|
|
||||||
userEmail: true,
|
|
||||||
userPhone: true,
|
|
||||||
userStatus: true,
|
|
||||||
dp_id: true,
|
|
||||||
userCreatedDate: true,
|
|
||||||
userModifiedDate: true,
|
|
||||||
department: {
|
|
||||||
select: {
|
|
||||||
dp_id: true,
|
|
||||||
dp_name: true,
|
|
||||||
organization: {
|
|
||||||
select: {
|
|
||||||
org_id: true,
|
|
||||||
org_name: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
userrole: {
|
|
||||||
select: {
|
|
||||||
userRoleID: true,
|
|
||||||
role: {
|
|
||||||
select: {
|
|
||||||
roleID: true,
|
|
||||||
roleName: true,
|
|
||||||
roleDescription: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return user;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a user
|
|
||||||
*
|
|
||||||
* @param {number} id User ID
|
|
||||||
* @param {Object} data User data to update
|
|
||||||
* @returns {Promise<Object>} Updated user (without password)
|
|
||||||
*/
|
|
||||||
async update(id, data) {
|
|
||||||
if (isNaN(parseInt(id))) {
|
|
||||||
throw new Error('Invalid user ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user exists
|
|
||||||
const existingUser = await prisma.user.findUnique({
|
|
||||||
where: {
|
|
||||||
userID: parseInt(id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingUser) {
|
|
||||||
throw new Error('User not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department exists if provided
|
|
||||||
if (data.dp_id !== undefined) {
|
|
||||||
if (data.dp_id !== null) {
|
|
||||||
const department = await prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: parseInt(data.dp_id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!department) {
|
|
||||||
throw new Error(`Department with ID ${data.dp_id} not found`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if username is taken by another user
|
|
||||||
if (data.userUsername && data.userUsername !== existingUser.userUsername) {
|
|
||||||
const usernameExists = await prisma.user.findFirst({
|
|
||||||
where: {
|
|
||||||
userUsername: data.userUsername,
|
|
||||||
userID: {
|
|
||||||
not: parseInt(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (usernameExists) {
|
|
||||||
throw new Error('Username is already taken by another user');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare update data
|
|
||||||
const updateData = {};
|
|
||||||
|
|
||||||
// Only update fields that are provided
|
|
||||||
if (data.userUsername !== undefined) updateData.userUsername = data.userUsername;
|
|
||||||
if (data.userFullName !== undefined) updateData.userFullName = data.userFullName;
|
|
||||||
if (data.userEmail !== undefined) updateData.userEmail = data.userEmail;
|
|
||||||
if (data.userPhone !== undefined) updateData.userPhone = data.userPhone;
|
|
||||||
if (data.userStatus !== undefined) updateData.userStatus = data.userStatus ? data.userStatus.toUpperCase() : null;
|
|
||||||
if (data.dp_id !== undefined) updateData.dp_id = data.dp_id ? parseInt(data.dp_id) : null;
|
|
||||||
|
|
||||||
// Hash password if provided
|
|
||||||
if (data.userPassword) {
|
|
||||||
updateData.userPassword = sha256(data.userPassword).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update modified date
|
|
||||||
updateData.userModifiedDate = new Date();
|
|
||||||
|
|
||||||
// Update user
|
|
||||||
const user = await prisma.user.update({
|
|
||||||
where: {
|
|
||||||
userID: parseInt(id)
|
|
||||||
},
|
|
||||||
data: updateData
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return user without password
|
|
||||||
const { userPassword, ...userWithoutPassword } = user;
|
|
||||||
return userWithoutPassword;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a user
|
|
||||||
*
|
|
||||||
* @param {number} id User ID
|
|
||||||
* @returns {Promise<Object>} Deleted user (without password)
|
|
||||||
*/
|
|
||||||
async delete(id) {
|
|
||||||
if (isNaN(parseInt(id))) {
|
|
||||||
throw new Error('Invalid user ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user exists
|
|
||||||
const existingUser = await prisma.user.findUnique({
|
|
||||||
where: {
|
|
||||||
userID: parseInt(id)
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
userrole: {
|
|
||||||
select: {
|
|
||||||
userRoleID: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existingUser) {
|
|
||||||
throw new Error('User not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user has any roles assigned
|
|
||||||
if (existingUser.userrole && existingUser.userrole.length > 0) {
|
|
||||||
// Delete all associated user roles first
|
|
||||||
await prisma.userrole.deleteMany({
|
|
||||||
where: {
|
|
||||||
userRoleUserID: parseInt(id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete user
|
|
||||||
const user = await prisma.user.delete({
|
|
||||||
where: {
|
|
||||||
userID: parseInt(id)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return user without password
|
|
||||||
const { userPassword, ...userWithoutPassword } = user;
|
|
||||||
return userWithoutPassword;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get users by department ID
|
|
||||||
*
|
|
||||||
* @param {number} departmentId Department ID
|
|
||||||
* @returns {Promise<Array>} List of users
|
|
||||||
*/
|
|
||||||
async getByDepartment(departmentId) {
|
|
||||||
if (isNaN(parseInt(departmentId))) {
|
|
||||||
throw new Error('Invalid department ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if department exists
|
|
||||||
const department = await prisma.department.findUnique({
|
|
||||||
where: {
|
|
||||||
dp_id: parseInt(departmentId)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!department) {
|
|
||||||
throw new Error('Department not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get users for this department
|
|
||||||
return prisma.user.findMany({
|
|
||||||
where: {
|
|
||||||
dp_id: parseInt(departmentId)
|
|
||||||
},
|
|
||||||
orderBy: {
|
|
||||||
userFullName: 'asc'
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
userID: true,
|
|
||||||
userUsername: true,
|
|
||||||
userFullName: true,
|
|
||||||
userEmail: true,
|
|
||||||
userPhone: true,
|
|
||||||
userStatus: true,
|
|
||||||
userCreatedDate: true,
|
|
||||||
userModifiedDate: true,
|
|
||||||
userrole: {
|
|
||||||
select: {
|
|
||||||
userRoleID: true,
|
|
||||||
role: {
|
|
||||||
select: {
|
|
||||||
roleID: true,
|
|
||||||
roleName: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify user credentials
|
|
||||||
*
|
|
||||||
* @param {string} username Username
|
|
||||||
* @param {string} password Raw password
|
|
||||||
* @returns {Promise<Object|null>} User data if credentials are valid, null otherwise
|
|
||||||
*/
|
|
||||||
async verifyCredentials(username, password) {
|
|
||||||
if (!username || !password) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user by username
|
|
||||||
const user = await prisma.user.findFirst({
|
|
||||||
where: {
|
|
||||||
userUsername: username
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check password
|
|
||||||
const hashedPassword = sha256(password).toString();
|
|
||||||
if (user.userPassword !== hashedPassword) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return user without password
|
|
||||||
const { userPassword, ...userWithoutPassword } = user;
|
|
||||||
return userWithoutPassword;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a random key
|
|
||||||
*
|
|
||||||
* @param {number} length Length of the key
|
|
||||||
* @returns {string} Random key
|
|
||||||
*/
|
|
||||||
generateRandomKey(length) {
|
|
||||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
||||||
let result = '';
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default UserUtils;
|
|
Loading…
x
Reference in New Issue
Block a user