implement redis cache for context

This commit is contained in:
2024-10-29 00:56:23 +07:00
parent ae1aa64b41
commit 34cea2ccd3
18 changed files with 477 additions and 55 deletions

88
package-lock.json generated
View File

@@ -48,6 +48,7 @@
"graphql-tools": "^9.0.1",
"graphql-upload": "15.0.2",
"graphql-ws": "^5.16.0",
"ioredis": "^5.4.1",
"jsonwebtoken": "^9.0.2",
"minio": "^8.0.1",
"nestjs-minio": "^2.6.2",
@@ -3390,6 +3391,12 @@
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@ioredis/commands": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz",
"integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==",
"license": "MIT"
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -7836,6 +7843,15 @@
"node": ">=0.8"
}
},
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -8358,6 +8374,15 @@
"node": ">=0.4.0"
}
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -10779,6 +10804,30 @@
"loose-envify": "^1.0.0"
}
},
"node_modules/ioredis": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.4.1.tgz",
"integrity": "sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==",
"license": "MIT",
"dependencies": {
"@ioredis/commands": "^1.1.1",
"cluster-key-slot": "^1.1.0",
"debug": "^4.3.4",
"denque": "^2.1.0",
"lodash.defaults": "^4.2.0",
"lodash.isarguments": "^3.1.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0",
"standard-as-callback": "^2.1.0"
},
"engines": {
"node": ">=12.22.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/ioredis"
}
},
"node_modules/ipaddr.js": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz",
@@ -12463,6 +12512,12 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash.defaults": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
"integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
"license": "MIT"
},
"node_modules/lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
@@ -12475,6 +12530,12 @@
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
"license": "MIT"
},
"node_modules/lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==",
"license": "MIT"
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
@@ -14993,6 +15054,27 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
"license": "MIT",
"dependencies": {
"redis-errors": "^1.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/reflect-metadata": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
@@ -15816,6 +15898,12 @@
"node": ">=8"
}
},
"node_modules/standard-as-callback": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz",
"integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==",
"license": "MIT"
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",

View File

@@ -68,6 +68,7 @@
"graphql-tools": "^9.0.1",
"graphql-upload": "15.0.2",
"graphql-ws": "^5.16.0",
"ioredis": "^5.4.1",
"jsonwebtoken": "^9.0.2",
"minio": "^8.0.1",
"nestjs-minio": "^2.6.2",

View File

@@ -1,11 +1,11 @@
import { Global, Module } from '@nestjs/common';
import { AppConfigSchema } from './appconfig.schema';
import { AppConfigService } from './appconfig.service';
@Global()
@Module({
imports: [AppConfigSchema],
providers: [AppConfigSchema],
exports: [AppConfigSchema],
providers: [AppConfigSchema, AppConfigService],
exports: [AppConfigSchema, AppConfigService],
})
export class AppConfigModule {}

View File

@@ -26,8 +26,12 @@ export class AppConfigSchema extends PothosSchema {
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' }),
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',
}),
@@ -45,7 +49,7 @@ export class AppConfigSchema extends PothosSchema {
type: [this.appConfig()],
description: 'Get all app configs',
args: this.builder.generator.findManyArgs('Config'),
resolve: async (query, root, args, ctx, info) => {
resolve: async (query, root, args) => {
return await this.prisma.config.findMany({
...query,
where: args.filter ?? undefined,
@@ -56,6 +60,53 @@ export class AppConfigSchema extends PothosSchema {
});
},
}),
appConfig: t.prismaField({
type: this.appConfig(),
description: 'Get an app config by key',
args: this.builder.generator.findUniqueArgs('Config'),
resolve: async (query, root, args) => {
return await this.prisma.config.findUnique({
...query,
where: args.where ?? undefined,
});
},
}),
}));
// Mutations
this.builder.mutationFields((t) => ({
createAppConfig: t.prismaField({
type: this.appConfig(),
description: 'Create an app config',
args: {
input: t.arg({
type: this.builder.generator.getCreateInput('Config'),
required: true,
}),
},
resolve: async (query, root, args) => {
return await this.prisma.config.create({
...query,
data: args.input,
});
},
}),
createAppConfigs: t.prismaField({
type: [this.appConfig()],
description: 'Create multiple app configs',
args: {
input: t.arg({
type: this.builder.generator.getCreateManyInput('Config'),
required: true,
}),
},
resolve: async (query, root, args) => {
return await this.prisma.config.createManyAndReturn({
...query,
data: args.input,
});
},
}),
}));
}
}

View File

@@ -262,7 +262,7 @@ export class CenterSchema extends PothosSchema {
if (args.approve) {
try {
await this.mailService.sendTemplateEmail(
centerOwner.email,
[centerOwner.email],
'Thông báo phê duyệt đăng ký trung tâm',
'CenterApproved',
{
@@ -277,7 +277,7 @@ export class CenterSchema extends PothosSchema {
// mail to center owner if rejected
try {
await this.mailService.sendTemplateEmail(
centerOwner.email,
[centerOwner.email],
'Thông báo từ chối đăng ký trung tâm',
'CenterRejected',
{

View File

@@ -56,7 +56,7 @@ export class CenterMentorSchema extends PothosSchema {
@Pothos()
init(): void {
this.builder.queryFields((t) => ({
centerMentor: t.prismaField({
centerMentors: t.prismaField({
description:
'Retrieve a list of center mentors with optional filtering, ordering, and pagination.',
type: [this.centerMentor()],
@@ -159,7 +159,7 @@ export class CenterMentorSchema extends PothosSchema {
const inviteUrl = `${process.env.CENTER_BASE_URL}/invite?token=${token}`;
// mail to user with params centerId, email
await this.mailService.sendTemplateEmail(
args.email,
[args.email],
'Invite to center',
'MentorInvitation',
{
@@ -189,7 +189,7 @@ export class CenterMentorSchema extends PothosSchema {
const inviteUrl = `${process.env.CENTER_BASE_URL}/invite?token=${token}`;
// mail to user with params centerId, email
await this.mailService.sendTemplateEmail(
args.email,
[args.email],
'Invite to center',
'MentorInvitation',
{
@@ -251,7 +251,7 @@ export class CenterMentorSchema extends PothosSchema {
if (args.approved) {
// send mail to user
await this.mailService.sendTemplateEmail(
email.email,
[email.email],
'Thông báo về việc được chấp nhận làm mentor',
'MentorApproved',
{
@@ -291,7 +291,7 @@ export class CenterMentorSchema extends PothosSchema {
}
// if rejected, update adminNote
await this.mailService.sendTemplateEmail(
email.email,
[email.email],
'Thông báo về việc không được chấp nhận làm mentor',
'MentorRejected',
{

View File

@@ -2,6 +2,7 @@ import { Global, MiddlewareConsumer, Module } from '@nestjs/common';
import { AdminNoteModule } from '../AdminNote/adminnote.module';
import { ApolloDriverConfig } from '@nestjs/apollo';
import { AppConfigModule } from '../AppConfig/appconfig.module';
import { Builder } from './graphql.builder';
import { CategoryModule } from '../Category/category.module';
import { CenterMentorModule } from '../CenterMentor/centermentor.module';
@@ -21,6 +22,8 @@ import { PothosModule } from '@smatch-corp/nestjs-pothos';
import { PrismaCrudGenerator } from './graphql.generator';
import { PrismaModule } from '../Prisma/prisma.module';
import { PrismaService } from '../Prisma/prisma.service';
import { RedisModule } from 'src/Redis/redis.module';
import { RedisService } from 'src/Redis/redis.service';
import { RefundTicketModule } from '../RefundTicket/refundticket.module';
import { Request } from 'express';
import { ResumeModule } from '../Resume/resume.module';
@@ -42,6 +45,8 @@ import { initContextCache } from '@pothos/core';
imports: [
CommonModule,
PrismaModule,
RedisModule,
AppConfigModule,
UserModule,
CenterModule,
ServiceModule,
@@ -93,8 +98,9 @@ import { initContextCache } from '@pothos/core';
providers: [
{
provide: GraphqlService,
useFactory: (prisma: PrismaService) => new GraphqlService(prisma),
inject: [PrismaService],
useFactory: (prisma: PrismaService, redis: RedisService) =>
new GraphqlService(prisma, redis),
inject: [PrismaService, 'REDIS_CLIENT'],
},
{
provide: Builder,

View File

@@ -1,12 +1,22 @@
import { Injectable, Logger, UnauthorizedException } from '@nestjs/common';
import {
Inject,
Injectable,
Logger,
UnauthorizedException,
} from '@nestjs/common';
import { PrismaService } from '../Prisma/prisma.service';
import { Request } from 'express';
import { clerkClient } from '@clerk/express';
import { RedisService } from '../Redis/redis.service';
@Injectable()
export class GraphqlService {
constructor(private readonly prisma: PrismaService) {}
constructor(
private readonly prisma: PrismaService,
@Inject('REDIS_CLIENT') private readonly redis: RedisService,
) {}
async acquireContext(req: Request) {
// get x-session-id from headers
@@ -24,6 +34,11 @@ export class GraphqlService {
if (disableAuth) {
return null;
}
// redis context cache
const cachedUser = await this.redis.getUser(sessionId);
if (cachedUser) {
return cachedUser;
}
// check if the token is valid
const session = await clerkClient.sessions.getSession(sessionId as string);
if (!session) {
@@ -35,7 +50,7 @@ export class GraphqlService {
if (!user) {
throw new UnauthorizedException('User not found');
}
Logger.log(`User ${user.name} with id ${user.id} acquired context`);
await this.redis.setUser(sessionId, user, session.expireAt);
return user;
}
}

View File

@@ -30,7 +30,7 @@ export class MailService {
}
async sendTemplateEmail(
to: string,
to: string[],
subject: string,
template: string,
context: any,

View File

@@ -0,0 +1,72 @@
doctype html
html
head
meta(charset="UTF-8")
title Thông báo phê duyệt Dịch vụ #{SERVICE_NAME} của 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 Dịch vụ #{SERVICE_NAME} đã được phê duyệt
.content
p Kính gửi Quý Trung tâm #{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 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ụ
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 Dịch vụ #{SERVICE_NAME} của 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 Dịch vụ #{SERVICE_NAME}
.content
p Kính gửi Quý Trung tâm #{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.
.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

File diff suppressed because one or more lines are too long

View File

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

15
src/Redis/redis.module.ts Normal file
View File

@@ -0,0 +1,15 @@
import { Global, Module } from '@nestjs/common';
import { RedisService } from './redis.service';
@Global()
@Module({
providers: [
{
provide: 'REDIS_CLIENT',
useClass: RedisService,
},
],
exports: ['REDIS_CLIENT'],
})
export class RedisModule {}

View File

@@ -0,0 +1,41 @@
import { Injectable } from '@nestjs/common';
import { Redis } from 'ioredis';
import { User } from '@prisma/client';
@Injectable()
export class RedisService {
private readonly redis: Redis;
constructor() {
this.redis = new Redis(process.env.REDIS_URL as string);
}
async get(key: string) {
return await this.redis.get(key);
}
async set(key: string, value: string, expireAt: number) {
return await this.redis.set(key, value, 'EXAT', expireAt);
}
async del(key: string) {
return await this.redis.del(key);
}
async close() {
return await this.redis.quit();
}
async getUser(sessionId: string) {
const userData = await this.get(sessionId);
if (!userData) {
return null;
}
const retrievedUser: User = JSON.parse(userData);
return retrievedUser;
}
async setUser(sessionId: string, user: User, expireAt: number) {
return await this.set(sessionId, JSON.stringify(user), expireAt);
}
}

View File

@@ -1,4 +1,4 @@
import { Inject, Injectable } from '@nestjs/common';
import { Inject, Injectable, Logger } from '@nestjs/common';
import {
Pothos,
PothosRef,
@@ -237,6 +237,10 @@ export class ServiceSchema extends PothosSchema {
type: 'Boolean',
required: true,
}),
adminNote: t.arg({
type: 'String',
required: false,
}),
},
resolve: async (query, root, args, ctx, info) => {
return await this.prisma.$transaction(async (prisma) => {
@@ -258,9 +262,15 @@ export class ServiceSchema extends PothosSchema {
status: args.approve
? ServiceStatus.APPROVED
: ServiceStatus.REJECTED,
adminNote: {
create: {
content: args.adminNote ?? '',
notedByUserId: ctx.me.id,
},
},
},
});
// mail to center owner and mentor who requested the service
// mail to all mentor or center owner for the center
const center = await prisma.center.findUnique({
where: { id: service.centerId },
});
@@ -276,15 +286,36 @@ export class ServiceSchema extends PothosSchema {
const centerMentor = await prisma.centerMentor.findMany({
where: { centerId: service.centerId },
});
const mentorEmails = centerMentor.map((mentor) => mentor.mentorId);
const emails = [centerOwner.email, ...mentorEmails];
for (const email of emails) {
await this.mailService.sendEmail(
email,
args.approve
? 'Your service has been approved'
: 'Your service has been rejected',
args.approve ? 'service-approved' : 'service-rejected',
const mentorIds = centerMentor.map((mentor) => mentor.mentorId);
// get mentor emails
const mentorEmails = await prisma.user.findMany({
where: { id: { in: mentorIds } },
});
Logger.log(mentorEmails, 'ServiceSchema');
const emails = [
centerOwner.email,
...mentorEmails.map((mentor) => mentor.email),
];
if (args.approve) {
await this.mailService.sendTemplateEmail(
emails,
'Thông báo về trạng thái dịch vụ',
'ServiceApproved',
{
SERVICE_NAME: service.name,
CENTER_NAME: center.name,
},
);
} else {
await this.mailService.sendTemplateEmail(
emails,
'Thông báo về trạng thái dịch vụ',
'ServiceRejected',
{
SERVICE_NAME: service.name,
CENTER_NAME: center.name,
ADMIN_NOTE: args.adminNote ?? 'Không có lý do',
},
);
}
return updatedService;

View File

@@ -129,7 +129,7 @@ export class UserSchema extends PothosSchema {
me: t.prismaField({
description: 'Retrieve the current user by token.',
type: this.user(),
resolve: async (query, root, args, ctx, info) => {
resolve: async (query, root, args, ctx) => {
// get session id from X-Session-Id
const sessionId = ctx.req.headers['x-session-id'];
if (!sessionId)
@@ -155,7 +155,7 @@ export class UserSchema extends PothosSchema {
'Retrieve a list of users with optional filtering, ordering, and pagination.',
type: [this.user()],
args: this.builder.generator.findManyArgs('User'),
resolve: async (query, root, args, ctx, info) => {
resolve: async (query, root, args) => {
return await this.prisma.user.findMany({
...query,
take: args.take ?? undefined,
@@ -170,7 +170,7 @@ export class UserSchema extends PothosSchema {
description: 'Retrieve a single user by their unique identifier.',
type: this.user(),
args: this.builder.generator.findUniqueArgs('User'),
resolve: async (query, root, args, ctx, info) => {
resolve: async (query, root, args) => {
return await this.prisma.user.findUniqueOrThrow({
...query,
where: args.where,
@@ -183,7 +183,7 @@ export class UserSchema extends PothosSchema {
args: {
sessionId: t.arg({ type: 'String', required: true }),
},
resolve: async (query, root, args, ctx, info) => {
resolve: async (query, root, args) => {
// check if the token is valid
const session = await clerkClient.sessions.getSession(args.sessionId);
Logger.log(session, 'Session');
@@ -212,7 +212,7 @@ export class UserSchema extends PothosSchema {
required: true,
}),
},
resolve: async (query, root, args, ctx, info) => {
resolve: async (query, root, args) => {
return await this.prisma.user.update({
...query,
where: args.where,
@@ -221,29 +221,14 @@ export class UserSchema extends PothosSchema {
},
}),
// banUser: t.prismaField({
// description: 'Ban a user.',
// type: this.user(),
// args: {
// userId: t.arg({ type: 'String', required: true }),
// },
// resolve: async (query, root, args, ctx, info) => {
// return await this.prisma.user.update({
// ...query,
// where: { id: args.userId },
// data: { banned: true },
// });
// },
// }),
sendEmailTest: t.field({
type: 'String',
args: {
to: t.arg({ type: 'String', required: true }),
},
resolve: async (_parent, args, _context, _info) => {
resolve: async (_parent, args) => {
await this.mailService.sendTemplateEmail(
args.to,
[args.to],
'Bạn đã được mời làm việc tại Trung tâm băng đĩa lậu hải ngoại',
'MentorInvitation',
{

View File

@@ -50,7 +50,7 @@ export class WorkshopSubscriptionSchema extends PothosSchema {
args: this.builder.generator.findUniqueArgs('WorkshopSubscription'),
description:
'Retrieve a single workshop subscription by its unique identifier.',
resolve: async (query, root, args, ctx, info) => {
resolve: async (query, root, args) => {
return await this.prisma.workshopSubscription.findUnique({
...query,
where: args.where,
@@ -62,7 +62,7 @@ export class WorkshopSubscriptionSchema extends PothosSchema {
args: this.builder.generator.findManyArgs('WorkshopSubscription'),
description:
'Retrieve a list of workshop subscriptions with optional filtering, ordering, and pagination.',
resolve: async (query, root, args, ctx, info) => {
resolve: async (query, root, args) => {
return await this.prisma.workshopSubscription.findMany({
...query,
skip: args.skip ?? undefined,