push code len ne

This commit is contained in:
2024-10-28 20:56:21 +07:00
parent 5c1d4e92af
commit ae1aa64b41
7 changed files with 267 additions and 23 deletions

View File

@@ -1,10 +1,61 @@
import { Inject, Injectable } from '@nestjs/common'; 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 { Builder } from 'src/Graphql/graphql.builder';
import { AppConfigService } from './appconfig.service';
import { PrismaService } from 'src/Prisma/prisma.service';
@Injectable() @Injectable()
export class AppConfigSchema extends PothosSchema { 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(); 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,
});
},
}),
}));
}
} }

View File

@@ -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) {}
}

View File

@@ -96,7 +96,7 @@ export class CenterSchema extends PothosSchema {
'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, ctx, info) => { resolve: async (query, root, args) => {
return await this.prisma.center.findMany({ return await this.prisma.center.findMany({
...query, ...query,
skip: args.skip ?? undefined, skip: args.skip ?? undefined,
@@ -110,7 +110,7 @@ export class CenterSchema extends PothosSchema {
type: this.center(), type: this.center(),
description: 'Retrieve a single center by its unique identifier.', description: 'Retrieve a single center by its unique identifier.',
args: this.builder.generator.findUniqueArgs('Center'), args: this.builder.generator.findUniqueArgs('Center'),
resolve: async (query, root, args, ctx, info) => { resolve: async (query, root, args) => {
return await this.prisma.center.findUnique({ return await this.prisma.center.findUnique({
...query, ...query,
where: args.where, where: args.where,
@@ -124,7 +124,7 @@ export class CenterSchema extends PothosSchema {
args: { args: {
userId: t.arg({ type: 'String', required: true }), 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({ return await this.prisma.center.findFirst({
where: { where: {
centerMentors: { centerMentors: {
@@ -149,7 +149,7 @@ export class CenterSchema extends PothosSchema {
required: true, required: true,
}), }),
}, },
resolve: async (query, root, args, ctx, info) => { resolve: async (query, root, args) => {
return await this.prisma.center.create({ return await this.prisma.center.create({
...query, ...query,
data: args.input, data: args.input,
@@ -169,7 +169,7 @@ export class CenterSchema extends PothosSchema {
required: true, required: true,
}), }),
}, },
resolve: async (query, root, args, ctx, info) => { resolve: async (query, root, args) => {
return await this.prisma.center.update({ return await this.prisma.center.update({
...query, ...query,
where: args.where, where: args.where,
@@ -186,7 +186,7 @@ export class CenterSchema extends PothosSchema {
required: true, required: true,
}), }),
}, },
resolve: async (query, root, args, ctx, info) => { resolve: async (query, root, args) => {
return await this.prisma.center.delete({ return await this.prisma.center.delete({
...query, ...query,
where: args.where, where: args.where,
@@ -205,8 +205,12 @@ export class CenterSchema extends PothosSchema {
type: 'Boolean', type: 'Boolean',
required: true, 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) => { return await this.prisma.$transaction(async (prisma) => {
const center = await prisma.center.findUnique({ const center = await prisma.center.findUnique({
...query, ...query,
@@ -254,17 +258,36 @@ export class CenterSchema extends PothosSchema {
: CenterStatus.REJECTED, : CenterStatus.REJECTED,
}, },
}); });
// mail to center owner // mail to center owner if approved
try { if (args.approve) {
await this.mailService.sendEmail( try {
centerOwner.email, await this.mailService.sendTemplateEmail(
args.approve centerOwner.email,
? 'Your center has been approved' 'Thông báo phê duyệt đăng ký trung tâm',
: 'Your center has been rejected', 'CenterApproved',
args.approve ? 'center-approved' : 'center-rejected', {
); CENTER_NAME: center.name,
} catch (error) { },
Logger.error(error, 'CenterSchema'); );
} 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; return updatedCenter;
}); });

View File

@@ -267,7 +267,8 @@ export class CenterMentorSchema extends PothosSchema {
notedByUserId: ctx.me.id, notedByUserId: ctx.me.id,
}, },
}); });
const updatedUser = await prisma.user.update({ // update user role
await prisma.user.update({
where: args.where, where: args.where,
data: { data: {
role: 'CENTER_MENTOR', role: 'CENTER_MENTOR',

View File

@@ -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

View File

@@ -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

View File

@@ -24,8 +24,14 @@ async function bootstrap() {
process.env.JWT_RS256_PRIVATE_KEY = privateKey; process.env.JWT_RS256_PRIVATE_KEY = privateKey;
process.env.JWT_RS256_PUBLIC_KEY = publicKey; process.env.JWT_RS256_PUBLIC_KEY = publicKey;
Logger.log(`Private key: ${privateKey.slice(0, 10)}...`, 'Bootstrap'); Logger.log(
Logger.log(`Public key: ${publicKey.slice(0, 10)}...`, 'Bootstrap'); `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 const corsOrigin = (process.env.CORS_ORIGIN ?? '').split(','); // split by comma to array
app.enableCors({ app.enableCors({