import { Inject, Injectable, Logger } from '@nestjs/common'; import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken, } from '@smatch-corp/nestjs-pothos'; import { Builder } from '../Graphql/graphql.builder'; import { PrismaService } from '../Prisma/prisma.service'; import { clerkClient } from '@clerk/express'; import { UnauthorizedException } from '@nestjs/common'; import { MailService } from '../Mail/mail.service'; @Injectable() export class UserSchema extends PothosSchema { constructor( @Inject(SchemaBuilderToken) private readonly builder: Builder, private readonly prisma: PrismaService, private readonly mailService: MailService, ) { super(); } // Types section @PothosRef() user() { return this.builder.prismaObject('User', { description: 'A user in the system.', fields: (t) => ({ id: t.exposeID('id', { description: 'The ID of the user.', }), name: t.exposeString('name', { description: 'The name of the user.', }), email: t.exposeString('email', { description: 'The email of the user.', }), phoneNumber: t.exposeString('phoneNumber', { description: 'The phone number of the user.', }), bankBin: t.exposeString('bankBin', { description: 'The bank bin of the user.', }), bankAccountNumber: t.exposeString('bankAccountNumber', { description: 'The bank account number of the user.', }), role: t.exposeString('role', { nullable: true, description: 'The role of the user.', }), avatarUrl: t.exposeString('avatarUrl', { nullable: true, description: 'The avatar URL of the user.', }), createdAt: t.expose('createdAt', { type: 'DateTime', nullable: true, description: 'The date and time the user was created.', }), updatedAt: t.expose('updatedAt', { type: 'DateTime', nullable: true, description: 'The date and time the user was updated.', }), orders: t.relation('orders', { description: 'The orders of the user.', }), serviceFeedbacks: t.relation('serviceFeedbacks', { description: 'The service feedbacks of the user.', }), files: t.relation('files', { description: 'The files of the user.', }), sendingMessage: t.relation('sendingMessage', { description: 'The sending message of the user.', }), center: t.relation('center', { description: 'The center of the user.', }), customerChatRoom: t.relation('customerChatRoom', { description: 'The customer chat room of the user.', }), mentorChatRoom: t.relation('mentorChatRoom', { description: 'The mentor chat room of the user.', }), mentor: t.relation('mentor', { description: 'The mentor of the user.', }), workshopSubscription: t.relation('workshopSubscription', { description: 'The workshop subscription of the user.', }), adminNote: t.relation('adminNote', { description: 'The admin note of the user.', }), }), }); } // Query section @Pothos() init(): void { this.builder.queryFields((t) => ({ session: t.field({ type: 'Json', args: { sessionId: t.arg({ type: 'String', required: true }), }, resolve: async (_, { sessionId }) => { const session = await clerkClient.sessions.getSession(sessionId); return JSON.parse(JSON.stringify(session)); }, }), newSession: t.field({ type: 'String', args: { userId: t.arg({ type: 'String', required: true, }), }, resolve: async (_, { userId }) => { const session = await clerkClient.signInTokens.createSignInToken({ userId, expiresInSeconds: 60 * 60 * 24, }); return session.id; }, }), me: t.prismaField({ description: 'Retrieve the current user by token.', type: this.user(), resolve: async (query, root, args, ctx, info) => { // get session id from X-Session-Id const sessionId = ctx.req.headers['x-session-id']; if (!sessionId) throw new UnauthorizedException({ message: 'No session ID found', }); // verify the token const session = await clerkClient.sessions.getSession( sessionId as string, ); if (!session) throw new UnauthorizedException(); const user = await this.prisma.user.findUnique({ where: { id: session.userId }, }); if (!user) throw new UnauthorizedException(); ctx.me = user; return user; }, }), users: t.prismaField({ description: '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) => { return await this.prisma.user.findMany({ ...query, take: args.take ?? undefined, skip: args.skip ?? undefined, orderBy: args.orderBy ?? undefined, where: args.filter ?? undefined, }); }, }), user: t.prismaField({ 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) => { return await this.prisma.user.findUniqueOrThrow({ ...query, where: args.where, }); }, }), userBySession: t.prismaField({ description: 'Retrieve a single user by their session ID.', type: this.user(), args: { sessionId: t.arg({ type: 'String', required: true }), }, resolve: async (query, root, args, ctx, info) => { // check if the token is valid const session = await clerkClient.sessions.getSession(args.sessionId); Logger.log(session, 'Session'); return await this.prisma.user.findFirstOrThrow({ ...query, where: { id: session.userId, }, }); }, }), })); // Mutation section this.builder.mutationFields((t) => ({ updateUser: t.prismaField({ description: 'Update an existing user.', type: this.user(), args: { input: t.arg({ type: this.builder.generator.getUpdateInput('User'), required: true, }), where: t.arg({ type: this.builder.generator.getWhereUnique('User'), required: true, }), }, resolve: async (query, root, args, ctx, info) => { return await this.prisma.user.update({ ...query, where: args.where, data: args.input, }); }, }), // 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) => { await this.mailService.sendTemplateEmail( 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', { center_name: 'băng đĩa lậu hải ngoại', invite_url: 'https://epess.org', }, ); return 'Email sent'; }, }), })); } }