enabled context and fix some api

This commit is contained in:
2024-10-28 20:21:36 +07:00
parent f0e90ba234
commit 5c1d4e92af
10 changed files with 266 additions and 42 deletions

View File

@@ -11,7 +11,7 @@ services:
- ./src:/app/src - ./src:/app/src
environment: environment:
- NODE_ENV=development - NODE_ENV=development
- DISABLE_AUTH=true - DISABLE_AUTH=false
- CENTER_BASE_URL=http://localhost:3000 - CENTER_BASE_URL=http://localhost:3000
- DATABASE_URL=postgresql://your_username:your_password@10.0.27.1:5432/epess - DATABASE_URL=postgresql://your_username:your_password@10.0.27.1:5432/epess
- CLERK_PUBLISHABLE_KEY=pk_test_aW4tY2hpbXAtOTcuY2xlcmsuYWNjb3VudHMuZGV2JA - CLERK_PUBLISHABLE_KEY=pk_test_aW4tY2hpbXAtOTcuY2xlcmsuYWNjb3VudHMuZGV2JA

View File

@@ -28,7 +28,7 @@ export class AdminNoteSchema extends PothosSchema {
content: t.exposeString('content', { content: t.exposeString('content', {
description: 'The content of the admin note.', description: 'The content of the admin note.',
}), }),
notedByUserId: t.exposeID('notedByUserId', { notedByUserId: t.exposeString('notedByUserId', {
description: 'The ID of the user who created the admin note.', description: 'The ID of the user who created the admin note.',
}), }),
notedBy: t.relation('notedBy', { notedBy: t.relation('notedBy', {

View File

@@ -9,7 +9,6 @@ import { Builder } from '../Graphql/graphql.builder';
import { PrismaService } from '../Prisma/prisma.service'; import { PrismaService } from '../Prisma/prisma.service';
import { MailService } from '../Mail/mail.service'; import { MailService } from '../Mail/mail.service';
import { JwtUtils } from '../common/utils/jwt.utils'; import { JwtUtils } from '../common/utils/jwt.utils';
import { UserSchema } from 'src/User/user.schema';
@Injectable() @Injectable()
export class CenterMentorSchema extends PothosSchema { export class CenterMentorSchema extends PothosSchema {
constructor( constructor(
@@ -17,7 +16,6 @@ export class CenterMentorSchema extends PothosSchema {
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly mailService: MailService, private readonly mailService: MailService,
private readonly jwtUtils: JwtUtils, private readonly jwtUtils: JwtUtils,
private readonly userSchema: UserSchema,
) { ) {
super(); super();
} }
@@ -48,7 +46,7 @@ export class CenterMentorSchema extends PothosSchema {
managedService: t.relation('managedService', { managedService: t.relation('managedService', {
description: 'The managed services of the center mentor.', description: 'The managed services of the center mentor.',
}), }),
adminNote: t.relation('AdminNote', { adminNote: t.relation('adminNote', {
description: 'The admin note of the center mentor.', description: 'The admin note of the center mentor.',
}), }),
}), }),
@@ -138,7 +136,7 @@ export class CenterMentorSchema extends PothosSchema {
args: { args: {
email: t.arg({ type: 'String', required: true }), email: t.arg({ type: 'String', required: true }),
}, },
resolve: async (query, root, args, ctx, info) => { resolve: async (query, root, args, ctx) => {
return this.prisma.$transaction(async (prisma) => { return this.prisma.$transaction(async (prisma) => {
// get centerId by user id from context // get centerId by user id from context
const userId = ctx.me.id; const userId = ctx.me.id;
@@ -180,8 +178,8 @@ export class CenterMentorSchema extends PothosSchema {
centerId: t.arg({ type: 'String', required: true }), centerId: t.arg({ type: 'String', required: true }),
}, },
description: 'Test invite center mentor.', description: 'Test invite center mentor.',
resolve: async (query, root, args, ctx, info) => { resolve: async (query, root, args) => {
return this.prisma.$transaction(async (prisma) => { return this.prisma.$transaction(async () => {
// sign token // sign token
const token = this.jwtUtils.signTokenRS256( const token = this.jwtUtils.signTokenRS256(
{ centerId: args.centerId, email: args.email }, { centerId: args.centerId, email: args.email },
@@ -204,7 +202,7 @@ export class CenterMentorSchema extends PothosSchema {
}, },
}), }),
approveOrRejectCenterMentor: t.prismaField({ approveOrRejectCenterMentor: t.prismaField({
type: this.userSchema.user(), type: this.centerMentor(),
description: 'Approve or reject a center mentor.', description: 'Approve or reject a center mentor.',
args: { args: {
where: t.arg({ where: t.arg({
@@ -216,30 +214,107 @@ export class CenterMentorSchema extends PothosSchema {
}, },
resolve: async (query, root, args, ctx, info) => { resolve: async (query, root, args, ctx, info) => {
return this.prisma.$transaction(async (prisma) => { return this.prisma.$transaction(async (prisma) => {
// validate input
if (args.approved && !args.adminNote) {
throw new Error('Admin note is required');
}
// get mentor info
const mentor = await prisma.user.findUnique({
where: args.where,
});
if (!mentor) {
throw new Error('Mentor not found');
}
// get centerMentor
const centerMentor = await prisma.centerMentor.findUnique({
where: { mentorId: mentor.id },
});
if (!centerMentor) {
throw new Error('Center mentor not found');
}
// get center
const center = await prisma.center.findUnique({
where: { id: centerMentor.centerId },
});
if (!center) {
throw new Error('Center not found');
}
// get email
const email = await prisma.user.findUnique({
where: args.where,
select: { email: true },
});
if (!email) {
throw new Error('Email is required');
}
// if approved, update role to mentor // if approved, update role to mentor
if (args.approved) { if (args.approved) {
return await prisma.user.update({ // send mail to user
await this.mailService.sendTemplateEmail(
email.email,
'Thông báo về việc được chấp nhận làm mentor',
'MentorApproved',
{
CENTER_NAME: center.name,
USER_NAME: mentor.name,
},
);
// create adminNote
const adminNote = await prisma.adminNote.create({
data: {
content: args.adminNote ?? '',
mentorId: mentor.id,
notedByUserId: ctx.me.id,
},
});
const updatedUser = await prisma.user.update({
where: args.where, where: args.where,
data: { data: {
role: 'CENTER_MENTOR', role: 'CENTER_MENTOR',
updatedAt: new Date(), updatedAt: new Date(),
},
});
// update centerMentor
const updatedCenterMentor = await prisma.centerMentor.update({
where: {
mentorId_centerId: {
mentorId: mentor.id,
centerId: centerMentor.centerId,
},
},
data: {
adminNote: { connect: { id: adminNote.id } },
},
});
return updatedCenterMentor;
}
// if rejected, update adminNote
await this.mailService.sendTemplateEmail(
email.email,
'Thông báo về việc không được chấp nhận làm mentor',
'MentorRejected',
{
CENTER_NAME: center.name,
USER_NAME: mentor.name,
},
);
return await prisma.centerMentor.update({
where: {
mentorId_centerId: {
mentorId: mentor.id,
centerId: centerMentor.centerId,
},
},
data: {
adminNote: { adminNote: {
create: { create: {
content: args.adminNote ?? '', content: args.adminNote ?? '',
notedByUserId: ctx.me.id,
updatedAt: new Date(),
}, },
}, },
}, },
}); });
}
// if rejected, update adminNote
return await prisma.user.update({
where: args.where,
data: {
adminNote: {
create: { content: args.adminNote ?? '' },
},
},
});
}); });
}, },
}), }),

View File

@@ -1,4 +1,4 @@
import { Global, Logger, MiddlewareConsumer, Module } from '@nestjs/common'; import { Global, MiddlewareConsumer, Module } from '@nestjs/common';
import { AdminNoteModule } from '../AdminNote/adminnote.module'; import { AdminNoteModule } from '../AdminNote/adminnote.module';
import { ApolloDriverConfig } from '@nestjs/apollo'; import { ApolloDriverConfig } from '@nestjs/apollo';
@@ -71,8 +71,10 @@ import { initContextCache } from '@pothos/core';
useFactory: (prisma: PrismaService) => new Builder(prisma), useFactory: (prisma: PrismaService) => new Builder(prisma),
}, },
}), }),
GraphQLModule.forRoot<ApolloDriverConfig>({ GraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: PothosApolloDriver, driver: PothosApolloDriver,
inject: [GraphqlService],
useFactory: async (graphqlService: GraphqlService) => ({
path: process.env.API_PATH + '/graphql', path: process.env.API_PATH + '/graphql',
debug: process.env.NODE_ENV === 'development' || false, debug: process.env.NODE_ENV === 'development' || false,
playground: process.env.NODE_ENV === 'development' || false, playground: process.env.NODE_ENV === 'development' || false,
@@ -83,7 +85,8 @@ import { initContextCache } from '@pothos/core';
}, },
context: async ({ req }: { req: Request }) => ({ context: async ({ req }: { req: Request }) => ({
...initContextCache(), ...initContextCache(),
me: await new GraphqlService(new PrismaService()).acquireContext(req), me: await graphqlService.acquireContext(req),
}),
}), }),
}), }),
], ],
@@ -104,7 +107,7 @@ import { initContextCache } from '@pothos/core';
inject: [Builder], inject: [Builder],
}, },
], ],
exports: [Builder, PrismaCrudGenerator], exports: [Builder, PrismaCrudGenerator, GraphqlService],
}) })
export class GraphqlModule { export class GraphqlModule {
configure(consumer: MiddlewareConsumer) { configure(consumer: MiddlewareConsumer) {

View File

@@ -14,8 +14,8 @@ export class GraphqlService {
const disableAuth = process.env.DISABLE_AUTH === 'true'; const disableAuth = process.env.DISABLE_AUTH === 'true';
try { try {
sessionId = req.headers['x-session-id'] as string; sessionId = req.headers['x-session-id'] as string;
//eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (error) { } catch (error) {
Logger.error('Error acquiring context', error);
if (disableAuth) { if (disableAuth) {
return null; return null;
} }
@@ -35,6 +35,7 @@ export class GraphqlService {
if (!user) { if (!user) {
throw new UnauthorizedException('User not found'); throw new UnauthorizedException('User not found');
} }
Logger.log(`User ${user.name} with id ${user.id} acquired context`);
return user; return user;
} }
} }

View File

@@ -0,0 +1,72 @@
doctype html
html
head
meta(charset="UTF-8")
title Thông báo phê duyệt Mentor tại Trung tâm #{CENTER_NAME}
style.
body {
font-family: Arial, sans-serif;
background-color: #f0f8ff;
color: #333;
margin: 0;
padding: 0;
}
.container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.header {
text-align: center;
background-color: #457D84; /* Medium teal */
color: #ffffff;
padding: 15px;
border-radius: 8px 8px 0 0;
}
.header h1 {
margin: 0;
font-size: 24px;
}
.content {
padding: 20px;
color: #333;
}
.content p {
font-size: 16px;
line-height: 1.5;
}
.button {
display: inline-block;
padding: 12px 20px;
background-color: #2BD4E2; /* Bright aqua */
color: #ffffff;
text-decoration: none;
font-size: 16px;
border-radius: 5px;
text-align: center;
margin: 20px 0;
}
.footer {
text-align: center;
font-size: 14px;
color: #555;
padding: 10px;
border-top: 1px solid #e0e0e0;
}
body
.container
.header
h1 Chúc mừng #{USER_NAME} đã trở thành Mentor tại #{CENTER_NAME}
.content
p Chào #{USER_NAME},
p Chúng tôi vui mừng thông báo rằng bạn đã được phê duyệt trở thành Mentor tại trung tâm #{CENTER_NAME}.
p Vui lòng nhấn vào nút dưới đây để truy cập vào trung tâm:
a.button(href="https://center.epess.org") Truy cập Trung tâm
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.
.footer
p Trân trọng,
p EPESS
p Nền tảng hỗ trợ viết luận

View File

@@ -0,0 +1,73 @@
doctype html
html
head
meta(charset="UTF-8")
title Thông báo kết quả ứng tuyển tại Trung tâm #{CENTER_NAME}
style.
body {
font-family: Arial, sans-serif;
background-color: #f0f8ff;
color: #333;
margin: 0;
padding: 0;
}
.container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.header {
text-align: center;
background-color: #d9534f; /* Red color for rejection */
color: #ffffff;
padding: 15px;
border-radius: 8px 8px 0 0;
}
.header h1 {
margin: 0;
font-size: 24px;
}
.content {
padding: 20px;
color: #333;
}
.content p {
font-size: 16px;
line-height: 1.5;
}
.button {
display: inline-block;
padding: 12px 20px;
background-color: #2BD4E2; /* Bright aqua */
color: #ffffff;
text-decoration: none;
font-size: 16px;
border-radius: 5px;
text-align: center;
margin: 20px 0;
}
.footer {
text-align: center;
font-size: 14px;
color: #555;
padding: 10px;
border-top: 1px solid #e0e0e0;
}
body
.container
.header
h1 Thông báo kết quả ứng tuyển của #{USER_NAME} tại #{CENTER_NAME}
.content
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 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:
a.button(href="https://center.epess.org") Truy cập Trung tâm
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.
.footer
p Trân trọng,
p EPESS
p Nền tảng hỗ trợ viết luận

View File

@@ -14,7 +14,7 @@ export class PrismaService extends PrismaClient implements OnModuleInit {
super({ super({
log: [ log: [
{ {
emit: 'stdout', emit: 'event',
level: 'query', level: 'query',
}, },
{ {

File diff suppressed because one or more lines are too long