Refactor Category and Center schemas to improve field descriptions and code clarity; update email templates for consistency in messaging. Add Redis service to CenterSchema for session management. Bump epess-database subproject commit reference to indicate changes. Clean up logging in ClerkService and ServiceSchema.

This commit is contained in:
2024-11-27 01:09:13 +07:00
parent c9435a6e04
commit 2581ca396f
11 changed files with 56 additions and 59 deletions

View File

@@ -1,10 +1,5 @@
import { Inject, Injectable } from '@nestjs/common' import { Inject, Injectable } from '@nestjs/common'
import { import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'
Pothos,
PothosRef,
PothosSchema,
SchemaBuilderToken,
} from '@smatch-corp/nestjs-pothos'
import { Builder } from '../Graphql/graphql.builder' import { Builder } from '../Graphql/graphql.builder'
import { PrismaService } from '../Prisma/prisma.service' import { PrismaService } from '../Prisma/prisma.service'
@@ -28,6 +23,9 @@ export class CategorySchema extends PothosSchema {
name: t.exposeString('name', { name: t.exposeString('name', {
description: 'The name of the category.', description: 'The name of the category.',
}), }),
description: t.exposeString('description', {
description: 'The description of the category.',
}),
subCategory: t.relation('subCategory', { subCategory: t.relation('subCategory', {
description: 'The subcategory of the category.', description: 'The subcategory of the category.',
}), }),
@@ -46,16 +44,17 @@ export class CategorySchema extends PothosSchema {
name: t.exposeString('name', { name: t.exposeString('name', {
description: 'The name of the subcategory.', description: 'The name of the subcategory.',
}), }),
description: t.exposeString('description', {
description: 'The description of the subcategory.',
}),
categoryId: t.exposeID('categoryId', { categoryId: t.exposeID('categoryId', {
description: description: 'The ID of the category that the subcategory belongs to.',
'The ID of the category that the subcategory belongs to.',
}), }),
category: t.relation('category', { category: t.relation('category', {
description: 'The category that the subcategory belongs to.', description: 'The category that the subcategory belongs to.',
}), }),
serviceAndCategory: t.relation('serviceAndCategory', { serviceAndCategory: t.relation('serviceAndCategory', {
description: description: 'The service and category that the subcategory belongs to.',
'The service and category that the subcategory belongs to.',
}), }),
}), }),
}) })
@@ -65,8 +64,7 @@ export class CategorySchema extends PothosSchema {
init(): void { init(): void {
this.builder.queryFields((t) => ({ this.builder.queryFields((t) => ({
categories: t.prismaField({ categories: t.prismaField({
description: description: 'Retrieve a list of categories with optional filtering, ordering, and pagination.',
'Retrieve a list of categories with optional filtering, ordering, and pagination.',
type: [this.category()], type: [this.category()],
args: this.builder.generator.findManyArgs('Category'), args: this.builder.generator.findManyArgs('Category'),
resolve: async (query, _root, args) => { resolve: async (query, _root, args) => {
@@ -91,8 +89,7 @@ export class CategorySchema extends PothosSchema {
}, },
}), }),
subCategories: t.prismaField({ subCategories: t.prismaField({
description: description: 'Retrieve a list of subcategories with optional filtering, ordering, and pagination.',
'Retrieve a list of subcategories with optional filtering, ordering, and pagination.',
type: [this.subCategory()], type: [this.subCategory()],
args: this.builder.generator.findManyArgs('SubCategory'), args: this.builder.generator.findManyArgs('SubCategory'),
resolve: async (query, _root, args) => { resolve: async (query, _root, args) => {

View File

@@ -1,15 +1,12 @@
import { Inject, Injectable, Logger } from '@nestjs/common' import { Inject, Injectable, Logger } from '@nestjs/common'
import { import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'
Pothos,
PothosRef,
PothosSchema,
SchemaBuilderToken,
} from '@smatch-corp/nestjs-pothos'
import { Builder } from '../Graphql/graphql.builder' import { Builder } from '../Graphql/graphql.builder'
import { PrismaService } from '../Prisma/prisma.service' import { PrismaService } from '../Prisma/prisma.service'
import { MinioService } from '../Minio/minio.service' import { MinioService } from '../Minio/minio.service'
import { CenterStatus, Role } from '@prisma/client' import { CenterStatus, Role } from '@prisma/client'
import { MailService } from '../Mail/mail.service' import { MailService } from '../Mail/mail.service'
import { clerkClient } from '@clerk/express'
import { RedisService } from 'src/Redis/redis.service'
@Injectable() @Injectable()
export class CenterSchema extends PothosSchema { export class CenterSchema extends PothosSchema {
@@ -18,6 +15,7 @@ export class CenterSchema extends PothosSchema {
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly minioService: MinioService, private readonly minioService: MinioService,
private readonly mailService: MailService, private readonly mailService: MailService,
private readonly redisService: RedisService,
) { ) {
super() super()
} }
@@ -54,11 +52,7 @@ export class CenterSchema extends PothosSchema {
resolve: async (center) => { resolve: async (center) => {
// get file id from logoUrl // get file id from logoUrl
const logoFileId = center.logoUrl?.split('/').pop()?.split('?')[0] const logoFileId = center.logoUrl?.split('/').pop()?.split('?')[0]
return await this.minioService.updatePresignUrl( return await this.minioService.updatePresignUrl(logoFileId ?? '', 'files', center.logoUrl ?? undefined)
logoFileId ?? '',
'files',
center.logoUrl ?? undefined,
)
}, },
}), }),
logoFile: t.relation('logoFile', { logoFile: t.relation('logoFile', {
@@ -103,8 +97,7 @@ export class CenterSchema extends PothosSchema {
init(): void { init(): void {
this.builder.queryFields((t) => ({ this.builder.queryFields((t) => ({
centers: t.prismaField({ centers: t.prismaField({
description: description: 'Retrieve a list of centers with optional filtering, ordering, and pagination.',
'Retrieve a list of centers with optional filtering, ordering, and pagination.',
type: [this.center()], type: [this.center()],
args: this.builder.generator.findManyArgs('Center'), args: this.builder.generator.findManyArgs('Center'),
resolve: async (query, _root, args) => { resolve: async (query, _root, args) => {
@@ -221,8 +214,14 @@ export class CenterSchema extends PothosSchema {
required: false, required: false,
}), }),
}, },
resolve: async (query, _root, args) => { resolve: async (query, _root, args, ctx) => {
return await this.prisma.$transaction(async (prisma) => { return await this.prisma.$transaction(async (prisma) => {
if (ctx.isSubscription) {
throw new Error('Not allowed in subscription')
}
if (ctx.http.me?.role !== Role.ADMIN && ctx.http.me?.role !== Role.MODERATOR) {
throw new Error('Not allowed')
}
const center = await prisma.center.findUnique({ const center = await prisma.center.findUnique({
...query, ...query,
where: { where: {
@@ -249,6 +248,14 @@ export class CenterSchema extends PothosSchema {
if (!centerOwner) { if (!centerOwner) {
throw new Error('User not found') throw new Error('User not found')
} }
// get active clerk session and invalidate cache
const sessionList = await clerkClient.sessions.getSessionList({
userId: centerOwnerId,
})
// clear all session cache in redis
sessionList.data.forEach(async (session) => {
await this.redisService.del(session.id)
})
await prisma.user.update({ await prisma.user.update({
where: { where: {
id: centerOwnerId, id: centerOwnerId,
@@ -257,6 +264,7 @@ export class CenterSchema extends PothosSchema {
role: Role.CENTER_OWNER, role: Role.CENTER_OWNER,
}, },
}) })
// update center status // update center status
const updatedCenter = await prisma.center.update({ const updatedCenter = await prisma.center.update({
...query, ...query,
@@ -264,9 +272,7 @@ export class CenterSchema extends PothosSchema {
id: args.centerId, id: args.centerId,
}, },
data: { data: {
centerStatus: args.approve centerStatus: args.approve ? CenterStatus.APPROVED : CenterStatus.REJECTED,
? CenterStatus.APPROVED
: CenterStatus.REJECTED,
}, },
}) })
// mail to center owner if approved // mail to center owner if approved

View File

@@ -24,11 +24,10 @@ export class ClerkService {
// session.removed // session.removed
// session.ended // session.ended
// email.created // email.created
console.log(eventType, data) Logger.log(eventType, data, 'ClerkService')
if (eventType === 'user.created') { if (eventType === 'user.created') {
this.eventUserCreated(data) this.eventUserCreated(data)
} }
if (eventType === 'user.updated') { if (eventType === 'user.updated') {
this.eventUserUpdated(data) this.eventUserUpdated(data)
} }
@@ -57,7 +56,6 @@ export class ClerkService {
const primary_email_address_id = data.primary_email_address_id const primary_email_address_id = data.primary_email_address_id
// get primary email address on email_addresses by querying email_addresses with primary_email_address_id // get primary email address on email_addresses by querying email_addresses with primary_email_address_id
let primary_email_address = data.email_addresses.find((email: any) => email.id === primary_email_address_id) let primary_email_address = data.email_addresses.find((email: any) => email.id === primary_email_address_id)
console.log(primary_email_address)
if (!primary_email_address) { if (!primary_email_address) {
primary_email_address = '' primary_email_address = ''
} }
@@ -87,7 +85,6 @@ export class ClerkService {
} }
async eventUserUpdated(data: any) { async eventUserUpdated(data: any) {
console.log(data)
const user_id = data.id const user_id = data.id
const name = `${data.first_name} ${data.last_name}` const name = `${data.first_name} ${data.last_name}`
await this.prisma.user.update({ await this.prisma.user.update({
@@ -97,7 +94,6 @@ export class ClerkService {
} }
async eventSessionCreated(data: any) { async eventSessionCreated(data: any) {
console.log(data)
// check if user exists in database or create user // check if user exists in database or create user
const user = await this.prisma.user.findUnique({ const user = await this.prisma.user.findUnique({
where: { id: data.user_id }, where: { id: data.user_id },
@@ -105,7 +101,6 @@ export class ClerkService {
if (!user) { if (!user) {
// get user info from clerk // get user info from clerk
const userInfo = await clerkClient.users.getUser(data.user_id) const userInfo = await clerkClient.users.getUser(data.user_id)
console.log(userInfo)
try { try {
await this.prisma.user.create({ await this.prisma.user.create({
data: { data: {
@@ -123,18 +118,18 @@ export class ClerkService {
} }
eventSessionRevoked(data: any) { eventSessionRevoked(data: any) {
console.log(data) Logger.log(data, 'ClerkService')
} }
eventSessionRemoved(data: any) { eventSessionRemoved(data: any) {
console.log(data) Logger.log(data, 'ClerkService')
} }
eventSessionEnded(data: any) { eventSessionEnded(data: any) {
console.log(data) Logger.log(data, 'ClerkService')
} }
eventEmailCreated(data: any) { eventEmailCreated(data: any) {
console.log(data) Logger.log(data, 'ClerkService')
} }
} }

View File

@@ -59,7 +59,7 @@ html
body body
.container .container
.header .header
h1 Chúc mừng Trung tâm #{CENTER_NAME} đã được phê duyệt h1 Chúc mừng #{CENTER_NAME} đã được phê duyệt
.content .content
p Kính gửi Quý Trung tâm, p Kính gửi Quý Trung tâm,
p Chúng tôi vui mừng thông báo rằng trung tâm #{CENTER_NAME} của bạn đã được phê duyệt trên nền tảng của chúng tôi. p Chúng tôi vui mừng thông báo rằng trung tâm #{CENTER_NAME} của bạn đã được phê duyệt trên nền tảng của chúng tôi.

View File

@@ -67,7 +67,7 @@ html
body body
.container .container
.header .header
h1 Thông báo từ chối Trung tâm #{CENTER_NAME} h1 Thông báo từ chối #{CENTER_NAME}
.content .content
p Kính gửi Quý Trung tâm, p Kính gửi Quý Trung tâm,
p Chúng tôi rất tiếc thông báo rằng trung tâm #{CENTER_NAME} của bạn chưa được phê duyệt trên nền tảng của chúng tôi. p Chúng tôi rất tiếc thông báo rằng trung tâm #{CENTER_NAME} của bạn chưa được phê duyệt trên nền tảng của chúng tôi.

View File

@@ -59,10 +59,10 @@ html
body body
.container .container
.header .header
h1 Thư mời làm việc từ Trung tâm #{center_name} h1 Thư mời làm việc từ #{center_name}
.content .content
p Chào bạn, p Chào bạn,
p Chúng tôi rất vui mừng thông báo rằng bạn đã được mời làm người hướng dẫn tại trung tâm #{center_name}. p Chúng tôi rất vui mừng thông báo rằng bạn đã được mời làm người hướng dẫn tại #{center_name}.
p Để tiếp tục quá trình ứng tuyển, vui lòng nhấn vào nút dưới đây để truy cập vào trang nộp resume của bạn. p Để tiếp tục quá trình ứng tuyển, vui lòng nhấn vào nút dưới đây để truy cập vào trang nộp resume của bạn.
a.button(href=invite_url) Nộp Resume a.button(href=invite_url) Nộp Resume
p Nếu bạn có bất kỳ thắc mắc nào, đừng ngần ngại liên hệ với chúng tôi. p Nếu bạn có bất kỳ thắc mắc nào, đừng ngần ngại liên hệ với chúng tôi.

View File

@@ -62,7 +62,7 @@ html
h1 Thông báo kết quả ứng tuyển của #{USER_NAME} tại #{CENTER_NAME} h1 Thông báo kết quả ứng tuyển của #{USER_NAME} tại #{CENTER_NAME}
.content .content
p Chào #{USER_NAME}, p Chào #{USER_NAME},
p Chúng tôi rất tiếc thông báo rằng bạn chưa được phê duyệt trở thành Mentor tại trung tâm #{CENTER_NAME} lần này. p Chúng tôi rất tiếc thông báo rằng bạn chưa được phê duyệt trở thành Mentor tại #{CENTER_NAME} lần này.
p Chúng tôi khuyến khích bạn tiếp tục nâng cao kỹ năng và kinh nghiệm, và mong đợi đơn ứng tuyển của bạn trong tương lai. p Chúng tôi khuyến khích bạn tiếp tục nâng cao kỹ năng và kinh nghiệm, và mong đợi đơn ứng tuyển của bạn trong tương lai.
p Bạn có thể truy cập trang web của chúng tôi để biết thêm thông tin: p Bạn có thể truy cập trang web của chúng tôi để biết thêm thông tin:
a.button(href="https://center.epess.org") Truy cập Trung tâm a.button(href="https://center.epess.org") Truy cập Trung tâm

View File

@@ -61,7 +61,7 @@ html
.header .header
h1 Chúc mừng Dịch vụ #{SERVICE_NAME} đã được phê duyệt h1 Chúc mừng Dịch vụ #{SERVICE_NAME} đã được phê duyệt
.content .content
p Kính gửi Quý Trung tâm #{CENTER_NAME}, p Kính gửi #{CENTER_NAME},
p Chúng tôi vui mừng thông báo rằng dịch vụ #{SERVICE_NAME} của bạn đã được phê duyệt trên nền tảng của chúng tôi. p Chúng tôi vui mừng thông báo rằng dịch vụ #{SERVICE_NAME} của bạn đã được phê duyệt trên nền tảng của chúng tôi.
p Vui lòng nhấn vào nút dưới đây để truy cập vào dịch vụ của bạn: p Vui lòng nhấn vào nút dưới đây để truy cập vào dịch vụ của bạn:
a.button(href="https://center.epess.org") Truy cập Dịch vụ a.button(href="https://center.epess.org") Truy cập Dịch vụ

View File

@@ -69,7 +69,7 @@ html
.header .header
h1 Thông báo từ chối Dịch vụ #{SERVICE_NAME} h1 Thông báo từ chối Dịch vụ #{SERVICE_NAME}
.content .content
p Kính gửi Quý Trung tâm #{CENTER_NAME}, p Kính gửi #{CENTER_NAME},
p Chúng tôi rất tiếc thông báo rằng dịch vụ #{SERVICE_NAME} của bạn chưa được phê duyệt trên nền tảng của chúng tôi. p Chúng tôi rất tiếc thông báo rằng dịch vụ #{SERVICE_NAME} của bạn chưa được phê duyệt trên nền tảng của chúng tôi.
.note .note
p Lý do từ chối: p Lý do từ chối:

View File

@@ -364,7 +364,6 @@ export class ServiceSchema extends PothosSchema {
const mentorEmails = await prisma.user.findMany({ const mentorEmails = await prisma.user.findMany({
where: { id: { in: mentorIds } }, where: { id: { in: mentorIds } },
}) })
Logger.log(mentorEmails, 'ServiceSchema')
const emails = [centerOwner.email, ...mentorEmails.map((mentor) => mentor.email)] const emails = [centerOwner.email, ...mentorEmails.map((mentor) => mentor.email)]
if (args.approve) { if (args.approve) {
await this.mailService.sendTemplateEmail(emails, 'Thông báo về trạng thái dịch vụ', 'ServiceApproved', { await this.mailService.sendTemplateEmail(emails, 'Thông báo về trạng thái dịch vụ', 'ServiceApproved', {

File diff suppressed because one or more lines are too long