@pextran/db 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -0
- package/package.json +48 -0
- package/src/README.md +1 -0
- package/src/index.ts +5 -0
- package/src/prisma.ts +40 -0
- package/src/repository/approvalRepository.ts +122 -0
- package/src/repository/employeesRepository.ts +161 -0
- package/src/repository/mediaRepository.ts +228 -0
package/README.md
ADDED
|
File without changes
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pextran/db",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "pextran DB Utilities",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"types": "src/index.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"src"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc --noEmit",
|
|
12
|
+
"prepublishOnly": "npm run build",
|
|
13
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
14
|
+
"patch": "pnpm pkg:addonly && pnpm run build && git add package.json ../../pnpm-lock.yaml && (git diff --staged --quiet || HUSKY=0 git commit -m \"chore(@pextran/db): sync dependencies\" --no-verify) && HUSKY=0 npm version patch && npm publish && git push --follow-tags",
|
|
15
|
+
"pkg:addonly": "pnpm remove @pextran/core && pnpm add @pextran/core@latest --ignore-scripts",
|
|
16
|
+
"pkg": "pnpm run pkg:addonly && git add . && git commit --no-verify -m \"pextran packages upgraded\"",
|
|
17
|
+
"pkg:push": "pnpm install && pnpm run pkg && git push"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"typescript",
|
|
21
|
+
"db",
|
|
22
|
+
"utils"
|
|
23
|
+
],
|
|
24
|
+
"author": "Hassen Mohammed",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"next": "^14.0.0",
|
|
28
|
+
"next-auth": "5.0.0-beta.4",
|
|
29
|
+
"react": "^18.0.0",
|
|
30
|
+
"react-dom": "^18.0.0",
|
|
31
|
+
"zustand": "^4.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/jsonwebtoken": "^9.0.9",
|
|
35
|
+
"@types/node": "^20.6.0",
|
|
36
|
+
"@types/react": "^18.0.0",
|
|
37
|
+
"@types/react-dom": "^18.0.0",
|
|
38
|
+
"typescript": "^5.2.2"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "restricted"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@pextran/core": "^0.0.5",
|
|
45
|
+
"jsonwebtoken": "^9.0.2",
|
|
46
|
+
"pdf-lib": "^1.17.1"
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Arcisol DB Package
|
package/src/index.ts
ADDED
package/src/prisma.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface IPrisma {
|
|
2
|
+
employee: {
|
|
3
|
+
findFirst: (args: any) => Promise<any>
|
|
4
|
+
}
|
|
5
|
+
tenant: {
|
|
6
|
+
findFirst: (args: any) => Promise<any>
|
|
7
|
+
}
|
|
8
|
+
organization: {
|
|
9
|
+
findFirst: (args: any) => Promise<any>
|
|
10
|
+
}
|
|
11
|
+
role: {
|
|
12
|
+
findMany: (args: any) => Promise<any>
|
|
13
|
+
}
|
|
14
|
+
// Add media model methods to satisfy mediaRepository.ts usage
|
|
15
|
+
media: {
|
|
16
|
+
findFirst: (args: any) => Promise<any>
|
|
17
|
+
findUnique: (args: any) => Promise<any>
|
|
18
|
+
findMany: (args: any) => Promise<any>
|
|
19
|
+
create: (args: any) => Promise<any>
|
|
20
|
+
update: (args: any) => Promise<any>
|
|
21
|
+
delete: (args: any) => Promise<any>
|
|
22
|
+
}
|
|
23
|
+
// Add approval model methods to satisfy approvalRepository.ts usage
|
|
24
|
+
approval: {
|
|
25
|
+
findFirst: (args: any) => Promise<any>
|
|
26
|
+
findUnique: (args: any) => Promise<any>
|
|
27
|
+
findMany: (args: any) => Promise<any>
|
|
28
|
+
create: (args: any) => Promise<any>
|
|
29
|
+
update: (args: any) => Promise<any>
|
|
30
|
+
delete: (args: any) => Promise<any>
|
|
31
|
+
groupBy: (args: any) => Promise<any>
|
|
32
|
+
}
|
|
33
|
+
department: {
|
|
34
|
+
findFirst: (args: any) => Promise<any>
|
|
35
|
+
}
|
|
36
|
+
resourceApproval: {
|
|
37
|
+
findFirst: (args: any) => Promise<any>
|
|
38
|
+
delete: (args: any) => Promise<any>
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use server'
|
|
2
|
+
import { ApprovalDTO, logger, NexiResponse, ResourceApprovalDTO, ResponseFactory } from "@pextran/core";
|
|
3
|
+
import { IPrisma } from "../prisma";
|
|
4
|
+
|
|
5
|
+
export async function findApprovalById(prisma: IPrisma, tenant: string, id: string): Promise<NexiResponse<ApprovalDTO>> {
|
|
6
|
+
try {
|
|
7
|
+
const approval = await prisma.approval.findFirst({
|
|
8
|
+
where: { id, tenant },
|
|
9
|
+
orderBy: { version: 'desc' },
|
|
10
|
+
take: 1
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (!approval) {
|
|
14
|
+
return ResponseFactory.error('Approval not found');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return ResponseFactory.success(approvalDbToDTO(approval));
|
|
18
|
+
} catch (error) {
|
|
19
|
+
logger.error('Error finding approval by ID:', error);
|
|
20
|
+
return ResponseFactory.error('Error finding approval');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function findApprovalByResourceType(
|
|
25
|
+
prisma: IPrisma,
|
|
26
|
+
tenant: string,
|
|
27
|
+
resourceType: string
|
|
28
|
+
): Promise<NexiResponse<ApprovalDTO>> {
|
|
29
|
+
try {
|
|
30
|
+
const approval = await prisma.approval.findFirst({
|
|
31
|
+
where: {
|
|
32
|
+
resourceType,
|
|
33
|
+
tenant
|
|
34
|
+
},
|
|
35
|
+
orderBy: { version: 'desc' },
|
|
36
|
+
take: 1
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (!approval) {
|
|
40
|
+
return ResponseFactory.error('Approval not found for resource type');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return ResponseFactory.success(approvalDbToDTO(approval));
|
|
44
|
+
} catch (error) {
|
|
45
|
+
logger.error('Error finding approval by resource type:', error);
|
|
46
|
+
return ResponseFactory.error('Error finding approval');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function getApprovalsForTenant(
|
|
51
|
+
prisma: IPrisma,
|
|
52
|
+
tenant: string
|
|
53
|
+
): Promise<NexiResponse<ApprovalDTO[]>> {
|
|
54
|
+
try {
|
|
55
|
+
// First, get the latest version for each resourceType
|
|
56
|
+
const latestApprovalIds = await prisma.approval.groupBy({
|
|
57
|
+
by: ['resourceType'],
|
|
58
|
+
where: { tenant },
|
|
59
|
+
_max: {
|
|
60
|
+
version: true
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Then fetch the full approval records for the latest versions
|
|
65
|
+
const approvals = await Promise.all(
|
|
66
|
+
latestApprovalIds.map(async (group: any) => {
|
|
67
|
+
return await prisma.approval.findFirst({
|
|
68
|
+
where: {
|
|
69
|
+
tenant,
|
|
70
|
+
resourceType: group.resourceType,
|
|
71
|
+
version: group._max.version
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Filter out any null results and convert to DTOs
|
|
78
|
+
const approvalDTOs = approvals
|
|
79
|
+
.filter((approval: any) => approval !== null)
|
|
80
|
+
.map(approvalDbToDTO);
|
|
81
|
+
|
|
82
|
+
return ResponseFactory.success(approvalDTOs);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
logger.error('Error getting approvals for tenant:', error);
|
|
85
|
+
return ResponseFactory.error('Error retrieving approvals');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function approvalDbToDTO(approval: any): ApprovalDTO {
|
|
90
|
+
return {
|
|
91
|
+
id: approval.id,
|
|
92
|
+
version: approval.version,
|
|
93
|
+
resourceType: approval.resourceType,
|
|
94
|
+
moduleId: approval.moduleId,
|
|
95
|
+
approvalLevels: approval.approvalLevels,
|
|
96
|
+
tenant: approval.tenant,
|
|
97
|
+
createdAt: approval.createdAt?.toISOString(),
|
|
98
|
+
updatedAt: approval.updatedAt?.toISOString(),
|
|
99
|
+
audits: approval.audits || []
|
|
100
|
+
} as ApprovalDTO;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export async function getResourceApprovalsForResource(
|
|
104
|
+
prisma: IPrisma,
|
|
105
|
+
tenant: string,
|
|
106
|
+
resourceId: string,
|
|
107
|
+
resourceType: string
|
|
108
|
+
): Promise<NexiResponse<ResourceApprovalDTO | null>> {
|
|
109
|
+
try {
|
|
110
|
+
const resourceApproval = await prisma.resourceApproval.findFirst({
|
|
111
|
+
where: {
|
|
112
|
+
tenant,
|
|
113
|
+
resourceId,
|
|
114
|
+
resourceType
|
|
115
|
+
}
|
|
116
|
+
}) as unknown as ResourceApprovalDTO;
|
|
117
|
+
return ResponseFactory.success(resourceApproval);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
logger.error('Error getting resource approvals for resource:', error);
|
|
120
|
+
return ResponseFactory.error('Failed to get resource approvals for resource');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { AuthClaims, DepartmentClaims, MODULES, RoleDTO } from "@pextran/core";
|
|
2
|
+
import { IPrisma } from "../prisma";
|
|
3
|
+
|
|
4
|
+
export async function getEmployeeClaims(prisma: IPrisma, tenantId: string, email: string, role: string, getOrgLogo: (orgId: string) => Promise<string | null>): Promise<{ claims: AuthClaims } | null> {
|
|
5
|
+
if (!email || !tenantId) return null
|
|
6
|
+
|
|
7
|
+
const employee = await prisma.employee.findFirst({
|
|
8
|
+
where: {
|
|
9
|
+
orgId: tenantId,
|
|
10
|
+
email: {
|
|
11
|
+
equals: email,
|
|
12
|
+
mode: 'insensitive'
|
|
13
|
+
},
|
|
14
|
+
active: true,
|
|
15
|
+
deleted: false
|
|
16
|
+
},
|
|
17
|
+
select: {
|
|
18
|
+
id: true,
|
|
19
|
+
email: true,
|
|
20
|
+
userId: true,
|
|
21
|
+
emailVerified: true,
|
|
22
|
+
firstName: true,
|
|
23
|
+
middleName: true,
|
|
24
|
+
lastName: true,
|
|
25
|
+
phone: true,
|
|
26
|
+
roles: true,
|
|
27
|
+
preferences: true,
|
|
28
|
+
orgId: true,
|
|
29
|
+
employment: true,
|
|
30
|
+
compensations: true,
|
|
31
|
+
financialDetails: true,
|
|
32
|
+
metadata: true,
|
|
33
|
+
}
|
|
34
|
+
}) as any
|
|
35
|
+
|
|
36
|
+
if (!employee) {
|
|
37
|
+
console.warn(`No employee found ! request tenantId:${tenantId}, email:${email}`)
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
let department: DepartmentClaims | null = null;
|
|
41
|
+
if (employee.employment?.departmentId) {
|
|
42
|
+
const departmentData = await prisma.department.findFirst({
|
|
43
|
+
where: { id: employee.employment?.departmentId, tenant: tenantId },
|
|
44
|
+
select: {
|
|
45
|
+
id: true,
|
|
46
|
+
name: true,
|
|
47
|
+
managerId: true
|
|
48
|
+
}
|
|
49
|
+
}) as any
|
|
50
|
+
department = {
|
|
51
|
+
id: departmentData?.id,
|
|
52
|
+
name: departmentData?.name,
|
|
53
|
+
isHead: departmentData?.managerId === employee.id
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
employee.profilePicture = await getOrgLogo(tenantId);
|
|
58
|
+
|
|
59
|
+
const tenantData = await prisma.tenant.findFirst({
|
|
60
|
+
where: { id: employee?.orgId },
|
|
61
|
+
select: {
|
|
62
|
+
id: true,
|
|
63
|
+
organization: {
|
|
64
|
+
select: {
|
|
65
|
+
name: true
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
modules: true,
|
|
69
|
+
preferences: true,
|
|
70
|
+
subscription: true
|
|
71
|
+
}
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const orgData = await prisma.organization.findFirst({
|
|
75
|
+
where: { id: employee?.orgId },
|
|
76
|
+
select: {
|
|
77
|
+
email: true,
|
|
78
|
+
website: true,
|
|
79
|
+
phone: true
|
|
80
|
+
}
|
|
81
|
+
}) as any
|
|
82
|
+
|
|
83
|
+
const roles = await prisma.role.findMany({
|
|
84
|
+
where: { orgId: tenantId, key: { in: employee.roles } },
|
|
85
|
+
select: {
|
|
86
|
+
key: true,
|
|
87
|
+
permissions: true,
|
|
88
|
+
modules: true
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const otherRoles: { role: string, modules: MODULES[] }[] = [];
|
|
93
|
+
tenantData.modules.forEach((m: MODULES) => {
|
|
94
|
+
const rolesWithModule = roles.filter((r: RoleDTO) => r.modules.includes(m) || r.modules.includes(MODULES.ALL)) as RoleDTO[]
|
|
95
|
+
if (rolesWithModule.length > 0) {
|
|
96
|
+
rolesWithModule.forEach((role: RoleDTO) => {
|
|
97
|
+
if (otherRoles.find((r: { role: string, modules: MODULES[] }) => r.role === role.key)) {
|
|
98
|
+
//update the modules
|
|
99
|
+
const modules = otherRoles.find((r: { role: string, modules: MODULES[] }) => r.role === role.key)!.modules
|
|
100
|
+
const mergedModules = modules.concat(role.modules)
|
|
101
|
+
const uniqueModules = mergedModules.filter((m, idx) => mergedModules.indexOf(m) === idx)
|
|
102
|
+
otherRoles.find((r: { role: string, modules: MODULES[] }) => r.role === role.key)!.modules = uniqueModules
|
|
103
|
+
} else {
|
|
104
|
+
otherRoles.push({ role: role.key, modules: role.modules })
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
if (role && roles.find((r: RoleDTO) => r.key === role)) {
|
|
111
|
+
employee.preferences = { ...(employee.preferences as any), activeRole: role }
|
|
112
|
+
} else if (!(employee.preferences as any)?.activeRole && roles.length > 0) {
|
|
113
|
+
employee.preferences = { ...(employee.preferences as any), activeRole: roles[0].key }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const claims = {
|
|
117
|
+
claims: {
|
|
118
|
+
employee: {
|
|
119
|
+
id: employee.id,
|
|
120
|
+
userId: employee.userId,
|
|
121
|
+
email: employee.email,
|
|
122
|
+
emailVerified: employee.emailVerified,
|
|
123
|
+
firstName: employee.firstName,
|
|
124
|
+
middleName: employee.middleName || '',
|
|
125
|
+
lastName: employee.lastName,
|
|
126
|
+
phone: employee.phone || '',
|
|
127
|
+
name: employee.firstName + ' ' + employee.lastName,
|
|
128
|
+
roles: roles.map((r: RoleDTO) => r.key === employee.preferences?.activeRole ? ({
|
|
129
|
+
role: r.key,
|
|
130
|
+
permissions: r.permissions,
|
|
131
|
+
modules: r.modules
|
|
132
|
+
}) : ({
|
|
133
|
+
role: r.key,
|
|
134
|
+
modules: r.modules
|
|
135
|
+
})),
|
|
136
|
+
preferences: employee.preferences,
|
|
137
|
+
profilePicture: employee.profilePicture,
|
|
138
|
+
department,
|
|
139
|
+
otherRoles: otherRoles
|
|
140
|
+
},
|
|
141
|
+
tenant: {
|
|
142
|
+
id: tenantData?.id,
|
|
143
|
+
name: tenantData?.organization?.name,
|
|
144
|
+
modules: tenantData?.modules,
|
|
145
|
+
preferences: tenantData?.preferences,
|
|
146
|
+
subscription: tenantData?.subscription
|
|
147
|
+
},
|
|
148
|
+
org: {
|
|
149
|
+
id: tenantData?.id,
|
|
150
|
+
name: tenantData?.organization?.name,
|
|
151
|
+
email: orgData?.email,
|
|
152
|
+
website: orgData?.website,
|
|
153
|
+
logo: employee.profilePicture,
|
|
154
|
+
phone: orgData?.phone
|
|
155
|
+
},
|
|
156
|
+
user: {} as any
|
|
157
|
+
} as AuthClaims
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return claims as any
|
|
161
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
'use server'
|
|
2
|
+
import { CATEGORY_UTILIZED_MEDIA_TYPES, logger, MediaDTO, MediaType, MULTI_FILE_MEDIA_TYPES, NexiResponse, ResourceType, ResponseFactory } from "@pextran/core";
|
|
3
|
+
import { IPrisma } from "../prisma";
|
|
4
|
+
|
|
5
|
+
export async function upsertMedia(prisma: IPrisma, tenant: string, media: MediaDTO, resourceId: string, resourceType?: ResourceType): Promise<MediaDTO> {
|
|
6
|
+
const upserted = await upsertMedias(prisma, tenant, [media], resourceId, resourceType) || [];
|
|
7
|
+
return upserted[0];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function upsertMedias(prisma: IPrisma, tenant: string, medias: MediaDTO[], resourceId: string, resourceType?: ResourceType): Promise<MediaDTO[]> {
|
|
11
|
+
|
|
12
|
+
//if medias is empty, return empty array
|
|
13
|
+
if (!medias || medias.length === 0) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const updatedMedias: MediaDTO[] = [];
|
|
18
|
+
// console.log('upserting medias:', medias, tenant, resourceId);
|
|
19
|
+
for (const media of (medias || []).filter(m => !!m && (m.url || m.signedUrl)) || []) {
|
|
20
|
+
// console.log('upserting media:', media);
|
|
21
|
+
const { modified, ...rest } = media;
|
|
22
|
+
let updatedMedia = {
|
|
23
|
+
...rest,
|
|
24
|
+
tenant,
|
|
25
|
+
resourceId,
|
|
26
|
+
resourceType,
|
|
27
|
+
name: media.name || ''
|
|
28
|
+
} as MediaDTO
|
|
29
|
+
// console.log('updatedMedia:', updatedMedia);
|
|
30
|
+
let id = media.id
|
|
31
|
+
let existingButDeleted = false;
|
|
32
|
+
//ttachments have order is as unique key allowing multiple media of same type
|
|
33
|
+
const type = media.type as MediaType;
|
|
34
|
+
if (!id && !MULTI_FILE_MEDIA_TYPES.includes(type) && !(media.category && CATEGORY_UTILIZED_MEDIA_TYPES.includes(type))) {
|
|
35
|
+
const existingMD = await prisma.media.findFirst({
|
|
36
|
+
where: {
|
|
37
|
+
tenant, resourceId, type, deleted: false
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
if (existingMD?.id) {
|
|
41
|
+
id = existingMD.id;
|
|
42
|
+
existingButDeleted = existingMD.deleted;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let order = media.order || 1;
|
|
47
|
+
|
|
48
|
+
if (!id && MULTI_FILE_MEDIA_TYPES.includes(type)) {
|
|
49
|
+
//get the last attachment and increment the order
|
|
50
|
+
const lastAttachment = await prisma.media.findFirst({
|
|
51
|
+
where: {
|
|
52
|
+
tenant, resourceId, type
|
|
53
|
+
},
|
|
54
|
+
orderBy: {
|
|
55
|
+
order: 'desc'
|
|
56
|
+
}
|
|
57
|
+
}).then(m => m?.order);
|
|
58
|
+
|
|
59
|
+
order = lastAttachment ? lastAttachment + 1 : 1;
|
|
60
|
+
}
|
|
61
|
+
updatedMedia = {
|
|
62
|
+
...updatedMedia,
|
|
63
|
+
order
|
|
64
|
+
} as MediaDTO;
|
|
65
|
+
|
|
66
|
+
let existingMedia: MediaDTO | null = null;
|
|
67
|
+
if (id) {
|
|
68
|
+
existingMedia = await prisma.media.findUnique({
|
|
69
|
+
where: { id, tenant }
|
|
70
|
+
}) as unknown as MediaDTO;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!id && !MULTI_FILE_MEDIA_TYPES.includes(type) && CATEGORY_UTILIZED_MEDIA_TYPES.includes(type)) {
|
|
74
|
+
//check if media with same name and type already exists
|
|
75
|
+
const existingMedia = await prisma.media.findFirst({
|
|
76
|
+
where: { tenant, resourceId, type, category: updatedMedia.category ?? null, name: media.name }
|
|
77
|
+
})
|
|
78
|
+
if (existingMedia?.id) {
|
|
79
|
+
id = existingMedia.id;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!id && !MULTI_FILE_MEDIA_TYPES.includes(type) && !CATEGORY_UTILIZED_MEDIA_TYPES.includes(type)) {
|
|
83
|
+
//check if media with same name and type already exists
|
|
84
|
+
const existingMedia = await prisma.media.findFirst({
|
|
85
|
+
where: { tenant, resourceId, type, name: media.name }
|
|
86
|
+
})
|
|
87
|
+
if (existingMedia?.id) {
|
|
88
|
+
id = existingMedia.id;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!id || !existingMedia) {
|
|
93
|
+
const { id, ...rest } = updatedMedia;
|
|
94
|
+
// console.log('creating media:', rest);
|
|
95
|
+
const md = await prisma.media.create({
|
|
96
|
+
data: rest
|
|
97
|
+
}) as unknown as MediaDTO;
|
|
98
|
+
updatedMedias.push(md);
|
|
99
|
+
} else if (modified || existingButDeleted) {
|
|
100
|
+
// console.log('updating media:', updatedMedia);
|
|
101
|
+
const md = await prisma.media.update({
|
|
102
|
+
where: { id, tenant },
|
|
103
|
+
data: updatedMedia
|
|
104
|
+
}) as unknown as MediaDTO;
|
|
105
|
+
updatedMedias.push(md);
|
|
106
|
+
} else {
|
|
107
|
+
updatedMedias.push(updatedMedia);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return updatedMedias;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const getOrgLogo = async (prisma: IPrisma, orgId: string): Promise<string | null> => {
|
|
114
|
+
const logo = await prisma.media.findFirst({
|
|
115
|
+
where: { tenant: orgId, type: MediaType.Logo, resourceId: orgId, deleted: false }
|
|
116
|
+
}) as unknown as MediaDTO;
|
|
117
|
+
return logo?.url || logo?.signedUrl || null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const moveAndDeleteMedia = async (prisma: IPrisma, tenant: string, id: string, resourceId: string): Promise<MediaDTO | null> => {
|
|
121
|
+
const newId = resourceId + '_' + new Date().getTime().toString();
|
|
122
|
+
const movedMedia = await prisma.media.update({
|
|
123
|
+
where: { tenant, id },
|
|
124
|
+
data: {
|
|
125
|
+
deleted: true,
|
|
126
|
+
resourceId: newId
|
|
127
|
+
}
|
|
128
|
+
}) as unknown as MediaDTO;
|
|
129
|
+
return movedMedia;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const findMediasForResourceId = async (prisma: IPrisma, tenant: string, resourceId: string): Promise<NexiResponse<MediaDTO[]>> => {
|
|
133
|
+
console.log('findMediasForResourceId:', tenant, resourceId);
|
|
134
|
+
const medias = await prisma.media.findMany({
|
|
135
|
+
where: { tenant, resourceId, deleted: false }
|
|
136
|
+
}) as unknown as MediaDTO[];
|
|
137
|
+
// console.log('medias:', medias);
|
|
138
|
+
return ResponseFactory.success(medias as MediaDTO[]);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const findMediasForTypeAndResourceId = async (prisma: IPrisma, tenant: string, type: MediaType, resourceId: string, includeDeleted: boolean = false): Promise<MediaDTO[]> => {
|
|
142
|
+
const medias = includeDeleted ? await prisma.media.findMany({
|
|
143
|
+
where: { tenant, type, resourceId }
|
|
144
|
+
}) as unknown as MediaDTO[] : await prisma.media.findMany({
|
|
145
|
+
where: { tenant, type, resourceId, deleted: false }
|
|
146
|
+
}) as unknown as MediaDTO[];
|
|
147
|
+
return medias;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const findMediasForTypeAndResourceIdAndCategory = async (prisma: IPrisma, tenant: string, type: MediaType, resourceId: string, category: string, includeDeleted: boolean = false): Promise<MediaDTO[]> => {
|
|
151
|
+
const medias = includeDeleted ? await prisma.media.findMany({
|
|
152
|
+
where: { tenant, type, resourceId, category }
|
|
153
|
+
}) as unknown as MediaDTO[] : await prisma.media.findMany({
|
|
154
|
+
where: { tenant, type, resourceId, category, deleted: false }
|
|
155
|
+
}) as unknown as MediaDTO[];
|
|
156
|
+
return medias;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const findMediaById = async (prisma: IPrisma, id: string): Promise<MediaDTO | null> => {
|
|
160
|
+
|
|
161
|
+
return await prisma.media.findFirst({
|
|
162
|
+
where: { id, deleted: false },
|
|
163
|
+
}) as unknown as MediaDTO;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export const getMediasForQuery = async (prisma: IPrisma, query: Record<string, string>, tenant: string): Promise<MediaDTO[]> => {
|
|
167
|
+
const medias = await prisma.media.findMany({
|
|
168
|
+
where: {
|
|
169
|
+
tenant,
|
|
170
|
+
AND: Object.entries(query).map(([key, value]) => ({
|
|
171
|
+
[key]: { contains: value, mode: 'insensitive' }
|
|
172
|
+
})),
|
|
173
|
+
deleted: false
|
|
174
|
+
}
|
|
175
|
+
}) as unknown as MediaDTO[];
|
|
176
|
+
return medias as MediaDTO[];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export const deleteMedia = async (prisma: IPrisma, tenant: string, id: string): Promise<MediaDTO | null> => {
|
|
180
|
+
try {
|
|
181
|
+
return await prisma.media.update({
|
|
182
|
+
where: { tenant, id },
|
|
183
|
+
data: {
|
|
184
|
+
deleted: true
|
|
185
|
+
}
|
|
186
|
+
}) as unknown as MediaDTO;
|
|
187
|
+
|
|
188
|
+
// this will flag the media as deleted and associated images will be deleted from the file storage by cron job
|
|
189
|
+
} catch (error) {
|
|
190
|
+
logger.error('Error deleting media', { error, tenant, id });
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export const getDeletedMedias = async (prisma: IPrisma): Promise<MediaDTO[]> => {
|
|
196
|
+
try {
|
|
197
|
+
return await prisma.media.findMany({
|
|
198
|
+
where: { deleted: true },
|
|
199
|
+
select: {
|
|
200
|
+
id: true,
|
|
201
|
+
tenant: true,
|
|
202
|
+
type: true,
|
|
203
|
+
resourceId: true,
|
|
204
|
+
resourceType: true,
|
|
205
|
+
name: true,
|
|
206
|
+
url: true,
|
|
207
|
+
signedUrl: true,
|
|
208
|
+
deleted: true,
|
|
209
|
+
order: true,
|
|
210
|
+
storagePath: true
|
|
211
|
+
}
|
|
212
|
+
}) as unknown as MediaDTO[];
|
|
213
|
+
} catch (error) {
|
|
214
|
+
logger.error('Error getting deleted medias', { error });
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export const hardDeleteMedia = async (prisma: IPrisma, tenant: string, id: string): Promise<MediaDTO | null> => {
|
|
220
|
+
try {
|
|
221
|
+
return await prisma.media.delete({
|
|
222
|
+
where: { tenant, id }
|
|
223
|
+
}) as unknown as MediaDTO;
|
|
224
|
+
} catch (error) {
|
|
225
|
+
logger.error('Error hard deleting media', { error, tenant, id });
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
}
|