From ae1aa64b417f42af8e3aaf4342145a86cb68c041 Mon Sep 17 00:00:00 2001 From: Ly Tuan Kiet Date: Mon, 28 Oct 2024 20:56:21 +0700 Subject: [PATCH] push code len ne --- src/AppConfig/appconfig.schema.ts | 55 +++++++++++++++- src/AppConfig/appconfig.service.ts | 7 +++ src/Center/center.schema.ts | 59 +++++++++++------ src/CenterMentor/centermentor.schema.ts | 3 +- src/Mail/templates/CenterApproved.pug | 72 +++++++++++++++++++++ src/Mail/templates/CenterRejected.pug | 84 +++++++++++++++++++++++++ src/main.ts | 10 ++- 7 files changed, 267 insertions(+), 23 deletions(-) create mode 100644 src/Mail/templates/CenterApproved.pug create mode 100644 src/Mail/templates/CenterRejected.pug diff --git a/src/AppConfig/appconfig.schema.ts b/src/AppConfig/appconfig.schema.ts index e3c32de..8c27e38 100644 --- a/src/AppConfig/appconfig.schema.ts +++ b/src/AppConfig/appconfig.schema.ts @@ -1,10 +1,61 @@ import { Inject, Injectable } from '@nestjs/common'; -import { PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'; +import { + PothosRef, + PothosSchema, + SchemaBuilderToken, +} from '@smatch-corp/nestjs-pothos'; import { Builder } from 'src/Graphql/graphql.builder'; +import { AppConfigService } from './appconfig.service'; +import { PrismaService } from 'src/Prisma/prisma.service'; @Injectable() export class AppConfigSchema extends PothosSchema { - constructor(@Inject(SchemaBuilderToken) private readonly builder: Builder) { + constructor( + @Inject(SchemaBuilderToken) private readonly builder: Builder, + private readonly appConfigService: AppConfigService, + private readonly prisma: PrismaService, + ) { super(); } + + @PothosRef() + appConfig() { + return this.builder.prismaObject('Config', { + description: 'An app config', + fields: (t) => ({ + id: t.exposeID('id', { + description: 'The unique identifier for the config', + }), + name: t.exposeString('name', { description: 'The name of the config' }), + key: t.exposeString('key', { description: 'The key of the config' }), + value: t.exposeString('value', { + description: 'The value of the config', + }), + visible: t.exposeBoolean('visible', { + description: 'Whether the config is visible', + }), + }), + }); + } + + @PothosRef() + init(): void { + this.builder.queryFields((t) => ({ + appConfigs: t.prismaField({ + type: [this.appConfig()], + description: 'Get all app configs', + args: this.builder.generator.findManyArgs('Config'), + resolve: async (query, root, args, ctx, info) => { + return await this.prisma.config.findMany({ + ...query, + where: args.filter ?? undefined, + orderBy: args.orderBy ?? undefined, + cursor: args.cursor ?? undefined, + skip: args.skip ?? undefined, + take: args.take ?? undefined, + }); + }, + }), + })); + } } diff --git a/src/AppConfig/appconfig.service.ts b/src/AppConfig/appconfig.service.ts index e69de29..9921559 100644 --- a/src/AppConfig/appconfig.service.ts +++ b/src/AppConfig/appconfig.service.ts @@ -0,0 +1,7 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from 'src/Prisma/prisma.service'; + +@Injectable() +export class AppConfigService { + constructor(private readonly prisma: PrismaService) {} +} diff --git a/src/Center/center.schema.ts b/src/Center/center.schema.ts index 6c10b1f..119e6fa 100644 --- a/src/Center/center.schema.ts +++ b/src/Center/center.schema.ts @@ -96,7 +96,7 @@ export class CenterSchema extends PothosSchema { 'Retrieve a list of centers with optional filtering, ordering, and pagination.', type: [this.center()], args: this.builder.generator.findManyArgs('Center'), - resolve: async (query, root, args, ctx, info) => { + resolve: async (query, root, args) => { return await this.prisma.center.findMany({ ...query, skip: args.skip ?? undefined, @@ -110,7 +110,7 @@ export class CenterSchema extends PothosSchema { type: this.center(), description: 'Retrieve a single center by its unique identifier.', args: this.builder.generator.findUniqueArgs('Center'), - resolve: async (query, root, args, ctx, info) => { + resolve: async (query, root, args) => { return await this.prisma.center.findUnique({ ...query, where: args.where, @@ -124,7 +124,7 @@ export class CenterSchema extends PothosSchema { args: { userId: t.arg({ type: 'String', required: true }), }, - resolve: async (query, root, args, ctx, info) => { + resolve: async (query, root, args) => { return await this.prisma.center.findFirst({ where: { centerMentors: { @@ -149,7 +149,7 @@ export class CenterSchema extends PothosSchema { required: true, }), }, - resolve: async (query, root, args, ctx, info) => { + resolve: async (query, root, args) => { return await this.prisma.center.create({ ...query, data: args.input, @@ -169,7 +169,7 @@ export class CenterSchema extends PothosSchema { required: true, }), }, - resolve: async (query, root, args, ctx, info) => { + resolve: async (query, root, args) => { return await this.prisma.center.update({ ...query, where: args.where, @@ -186,7 +186,7 @@ export class CenterSchema extends PothosSchema { required: true, }), }, - resolve: async (query, root, args, ctx, info) => { + resolve: async (query, root, args) => { return await this.prisma.center.delete({ ...query, where: args.where, @@ -205,8 +205,12 @@ export class CenterSchema extends PothosSchema { type: 'Boolean', required: true, }), + adminNote: t.arg({ + type: 'String', + required: false, + }), }, - resolve: async (query, root, args, ctx, info) => { + resolve: async (query, root, args) => { return await this.prisma.$transaction(async (prisma) => { const center = await prisma.center.findUnique({ ...query, @@ -254,17 +258,36 @@ export class CenterSchema extends PothosSchema { : CenterStatus.REJECTED, }, }); - // mail to center owner - try { - await this.mailService.sendEmail( - centerOwner.email, - args.approve - ? 'Your center has been approved' - : 'Your center has been rejected', - args.approve ? 'center-approved' : 'center-rejected', - ); - } catch (error) { - Logger.error(error, 'CenterSchema'); + // mail to center owner if approved + if (args.approve) { + try { + await this.mailService.sendTemplateEmail( + centerOwner.email, + 'Thông báo phê duyệt đăng ký trung tâm', + 'CenterApproved', + { + CENTER_NAME: center.name, + }, + ); + } catch (error) { + Logger.error(error, 'CenterSchema'); + } + } + if (!args.approve) { + // mail to center owner if rejected + try { + await this.mailService.sendTemplateEmail( + centerOwner.email, + 'Thông báo từ chối đăng ký trung tâm', + 'CenterRejected', + { + CENTER_NAME: center.name, + ADMIN_NOTE: args.adminNote, + }, + ); + } catch (error) { + Logger.error(error, 'CenterSchema'); + } } return updatedCenter; }); diff --git a/src/CenterMentor/centermentor.schema.ts b/src/CenterMentor/centermentor.schema.ts index cdbfebb..abcb4dd 100644 --- a/src/CenterMentor/centermentor.schema.ts +++ b/src/CenterMentor/centermentor.schema.ts @@ -267,7 +267,8 @@ export class CenterMentorSchema extends PothosSchema { notedByUserId: ctx.me.id, }, }); - const updatedUser = await prisma.user.update({ + // update user role + await prisma.user.update({ where: args.where, data: { role: 'CENTER_MENTOR', diff --git a/src/Mail/templates/CenterApproved.pug b/src/Mail/templates/CenterApproved.pug new file mode 100644 index 0000000..6954c9a --- /dev/null +++ b/src/Mail/templates/CenterApproved.pug @@ -0,0 +1,72 @@ +doctype html +html + head + meta(charset="UTF-8") + title Thông báo phê duyệt 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 Trung tâm #{CENTER_NAME} đã được phê duyệt + .content + 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 Vui lòng nhấn vào nút dưới đây để truy cập vào trung tâm của bạn: + 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 diff --git a/src/Mail/templates/CenterRejected.pug b/src/Mail/templates/CenterRejected.pug new file mode 100644 index 0000000..dd6ee52 --- /dev/null +++ b/src/Mail/templates/CenterRejected.pug @@ -0,0 +1,84 @@ +doctype html +html + head + meta(charset="UTF-8") + title Thông báo từ chố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; + } + .note { + background-color: #f8d7da; + color: #721c24; + padding: 15px; + border-radius: 5px; + margin: 20px 0; + border: 1px solid #f5c6cb; + } + .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 từ chối Trung tâm #{CENTER_NAME} + .content + 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. + .note + p Lý do từ chối: + p #{ADMIN_NOTE} + p Chúng tôi khuyến khích bạn xem xét lại thông tin và nộp đơn đăng ký lại 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 diff --git a/src/main.ts b/src/main.ts index af4acf5..1ee1d78 100644 --- a/src/main.ts +++ b/src/main.ts @@ -24,8 +24,14 @@ async function bootstrap() { process.env.JWT_RS256_PRIVATE_KEY = privateKey; process.env.JWT_RS256_PUBLIC_KEY = publicKey; - Logger.log(`Private key: ${privateKey.slice(0, 10)}...`, 'Bootstrap'); - Logger.log(`Public key: ${publicKey.slice(0, 10)}...`, 'Bootstrap'); + Logger.log( + `Private key: ${privateKey.slice(0, 10).replace(/\n/g, '')}...`, + 'Bootstrap', + ); + Logger.log( + `Public key: ${publicKey.slice(0, 10).replace(/\n/g, '')}...`, + 'Bootstrap', + ); const corsOrigin = (process.env.CORS_ORIGIN ?? '').split(','); // split by comma to array app.enableCors({