enabled context and fix some api
This commit is contained in:
@@ -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
|
||||||
|
|||||||
Submodule epess-database updated: 16d3ef350a...2eceaaa3c2
@@ -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', {
|
||||||
|
|||||||
@@ -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,27 +214,104 @@ 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(),
|
||||||
adminNote: {
|
|
||||||
create: {
|
|
||||||
content: args.adminNote ?? '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
// 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
|
// if rejected, update adminNote
|
||||||
return await prisma.user.update({
|
await this.mailService.sendTemplateEmail(
|
||||||
where: args.where,
|
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: {
|
data: {
|
||||||
adminNote: {
|
adminNote: {
|
||||||
create: { content: args.adminNote ?? '' },
|
create: {
|
||||||
|
content: args.adminNote ?? '',
|
||||||
|
notedByUserId: ctx.me.id,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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,19 +71,22 @@ 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,
|
||||||
path: process.env.API_PATH + '/graphql',
|
inject: [GraphqlService],
|
||||||
debug: process.env.NODE_ENV === 'development' || false,
|
useFactory: async (graphqlService: GraphqlService) => ({
|
||||||
playground: process.env.NODE_ENV === 'development' || false,
|
path: process.env.API_PATH + '/graphql',
|
||||||
introspection: process.env.NODE_ENV === 'development' || false,
|
debug: process.env.NODE_ENV === 'development' || false,
|
||||||
installSubscriptionHandlers: true,
|
playground: process.env.NODE_ENV === 'development' || false,
|
||||||
subscriptions: {
|
introspection: process.env.NODE_ENV === 'development' || false,
|
||||||
'graphql-ws': true,
|
installSubscriptionHandlers: true,
|
||||||
},
|
subscriptions: {
|
||||||
context: async ({ req }: { req: Request }) => ({
|
'graphql-ws': true,
|
||||||
...initContextCache(),
|
},
|
||||||
me: await new GraphqlService(new PrismaService()).acquireContext(req),
|
context: async ({ req }: { req: Request }) => ({
|
||||||
|
...initContextCache(),
|
||||||
|
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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
72
src/Mail/templates/MentorApproved.pug
Normal file
72
src/Mail/templates/MentorApproved.pug
Normal 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
|
||||||
73
src/Mail/templates/MentorRejected.pug
Normal file
73
src/Mail/templates/MentorRejected.pug
Normal 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
|
||||||
@@ -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
Reference in New Issue
Block a user