commit expose quizAttempt
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
import { clerkClient } from '@clerk/express'
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common'
|
||||
import { Message, Role } from '@prisma/client'
|
||||
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos'
|
||||
import { ChatroomSchema } from '../ChatRoom/chatroom.schema'
|
||||
import { Builder, SchemaContext } from '../Graphql/graphql.builder'
|
||||
import { MailService } from '../Mail/mail.service'
|
||||
import { MessageSchema } from '../Message/message.schema'
|
||||
import { PrismaService } from '../Prisma/prisma.service'
|
||||
import { PubSubEvent } from '../common/pubsub/pubsub-event'
|
||||
import { DateTimeUtils } from '../common/utils/datetime.utils'
|
||||
import { clerkClient } from '@clerk/express';
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
import { Message, Role } from '@prisma/client';
|
||||
import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos';
|
||||
import { ChatroomSchema } from '../ChatRoom/chatroom.schema';
|
||||
import { Builder, SchemaContext } from '../Graphql/graphql.builder';
|
||||
import { MailService } from '../Mail/mail.service';
|
||||
import { MessageSchema } from '../Message/message.schema';
|
||||
import { PrismaService } from '../Prisma/prisma.service';
|
||||
import { PubSubEvent } from '../common/pubsub/pubsub-event';
|
||||
import { DateTimeUtils } from '../common/utils/datetime.utils';
|
||||
@Injectable()
|
||||
export class UserSchema extends PothosSchema {
|
||||
constructor(
|
||||
@@ -16,9 +16,9 @@ export class UserSchema extends PothosSchema {
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly mailService: MailService,
|
||||
private readonly messageSchema: MessageSchema,
|
||||
private readonly chatRoomSchema: ChatroomSchema,
|
||||
private readonly chatRoomSchema: ChatroomSchema
|
||||
) {
|
||||
super()
|
||||
super();
|
||||
}
|
||||
|
||||
// Types section
|
||||
@@ -109,8 +109,11 @@ export class UserSchema extends PothosSchema {
|
||||
banned: t.exposeBoolean('banned', {
|
||||
description: 'The banned status of the user.',
|
||||
}),
|
||||
quizAttempt: t.relation('QuizAttempt', {
|
||||
description: 'The quiz attempt of the user',
|
||||
}),
|
||||
}),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@PothosRef()
|
||||
@@ -134,7 +137,7 @@ export class UserSchema extends PothosSchema {
|
||||
description: 'The last message of the chat room.',
|
||||
}),
|
||||
}),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Query section
|
||||
@@ -147,8 +150,8 @@ export class UserSchema extends PothosSchema {
|
||||
sessionId: t.arg({ type: 'String', required: true }),
|
||||
},
|
||||
resolve: async (_, { sessionId }) => {
|
||||
const session = await clerkClient.sessions.getSession(sessionId)
|
||||
return JSON.parse(JSON.stringify(session))
|
||||
const session = await clerkClient.sessions.getSession(sessionId);
|
||||
return JSON.parse(JSON.stringify(session));
|
||||
},
|
||||
}),
|
||||
newSession: t.field({
|
||||
@@ -163,8 +166,8 @@ export class UserSchema extends PothosSchema {
|
||||
const session = await clerkClient.signInTokens.createSignInToken({
|
||||
userId,
|
||||
expiresInSeconds: 60 * 60 * 24,
|
||||
})
|
||||
return session.id
|
||||
});
|
||||
return session.id;
|
||||
},
|
||||
}),
|
||||
me: t.prismaField({
|
||||
@@ -172,9 +175,9 @@ export class UserSchema extends PothosSchema {
|
||||
type: this.user(),
|
||||
resolve: async (_query, _root, _args, ctx) => {
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
throw new Error('Not allowed');
|
||||
}
|
||||
return ctx.http.me
|
||||
return ctx.http.me;
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -186,11 +189,11 @@ export class UserSchema extends PothosSchema {
|
||||
},
|
||||
resolve: async (_parent, args, ctx) => {
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
throw new Error('Not allowed');
|
||||
}
|
||||
const me = ctx.http.me
|
||||
const me = ctx.http.me;
|
||||
if (!me) {
|
||||
throw new Error('User not found')
|
||||
throw new Error('User not found');
|
||||
}
|
||||
|
||||
// get chat rooms that the user is a part of
|
||||
@@ -202,7 +205,7 @@ export class UserSchema extends PothosSchema {
|
||||
lastActivity: 'desc',
|
||||
},
|
||||
take: args.take ?? 10,
|
||||
})
|
||||
});
|
||||
|
||||
// get the last message for each chat room
|
||||
const lastMessages = await Promise.all(
|
||||
@@ -214,28 +217,28 @@ export class UserSchema extends PothosSchema {
|
||||
orderBy: {
|
||||
sentAt: 'desc',
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
if (!lastMessage) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
const sender = lastMessage.senderId
|
||||
? await this.prisma.user.findUnique({
|
||||
where: { id: lastMessage.senderId },
|
||||
})
|
||||
: undefined
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
chatRoom: chatRoom,
|
||||
lastActivity: lastMessage.sentAt,
|
||||
sender: sender,
|
||||
message: lastMessage,
|
||||
}
|
||||
}),
|
||||
)
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return lastMessages.filter((msg) => msg !== null)
|
||||
return lastMessages.filter((msg) => msg !== null);
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -250,7 +253,7 @@ export class UserSchema extends PothosSchema {
|
||||
skip: args.skip ?? undefined,
|
||||
orderBy: args.orderBy ?? undefined,
|
||||
where: args.filter ?? undefined,
|
||||
})
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -262,11 +265,11 @@ export class UserSchema extends PothosSchema {
|
||||
const user = await this.prisma.user.findUnique({
|
||||
...query,
|
||||
where: args.where,
|
||||
})
|
||||
});
|
||||
if (!user) {
|
||||
throw new Error('User not found')
|
||||
throw new Error('User not found');
|
||||
}
|
||||
return user
|
||||
return user;
|
||||
},
|
||||
}),
|
||||
userBySession: t.prismaField({
|
||||
@@ -277,17 +280,17 @@ export class UserSchema extends PothosSchema {
|
||||
},
|
||||
resolve: async (query, _root, args) => {
|
||||
// check if the token is valid
|
||||
const session = await clerkClient.sessions.getSession(args.sessionId)
|
||||
Logger.log(session, 'Session')
|
||||
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) => ({
|
||||
@@ -309,7 +312,7 @@ export class UserSchema extends PothosSchema {
|
||||
...query,
|
||||
where: args.where,
|
||||
data: args.input,
|
||||
})
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -359,32 +362,32 @@ export class UserSchema extends PothosSchema {
|
||||
},
|
||||
resolve: async (_query, args, ctx, _info) => {
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
throw new Error('Not allowed');
|
||||
}
|
||||
const id = ctx.http.me?.id
|
||||
const id = ctx.http.me?.id;
|
||||
if (!id) {
|
||||
throw new Error('User not found')
|
||||
throw new Error('User not found');
|
||||
}
|
||||
if (args.imageBlob) {
|
||||
const { mimetype, createReadStream } = await args.imageBlob
|
||||
const { mimetype, createReadStream } = await args.imageBlob;
|
||||
if (mimetype && createReadStream) {
|
||||
const stream = createReadStream()
|
||||
const chunks: Uint8Array[] = []
|
||||
const stream = createReadStream();
|
||||
const chunks: Uint8Array[] = [];
|
||||
|
||||
for await (const chunk of stream) {
|
||||
chunks.push(chunk)
|
||||
chunks.push(chunk);
|
||||
}
|
||||
|
||||
const buffer = Buffer.concat(chunks)
|
||||
const buffer = Buffer.concat(chunks);
|
||||
const { id: userId, imageUrl } = await clerkClient.users.updateUserProfileImage(id, {
|
||||
file: new Blob([buffer]),
|
||||
})
|
||||
});
|
||||
await this.prisma.user.update({
|
||||
where: { id: userId },
|
||||
data: {
|
||||
avatarUrl: imageUrl,
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,7 +395,7 @@ export class UserSchema extends PothosSchema {
|
||||
const clerkUser = await clerkClient.users.updateUser(id, {
|
||||
firstName: args.firstName as string,
|
||||
lastName: args.lastName as string,
|
||||
})
|
||||
});
|
||||
// update bank account number and bank bin to database
|
||||
if (args.input?.bankAccountNumber) {
|
||||
await this.prisma.user.update({
|
||||
@@ -400,7 +403,7 @@ export class UserSchema extends PothosSchema {
|
||||
data: {
|
||||
bankAccountNumber: args.input.bankAccountNumber,
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (args.input?.bankBin) {
|
||||
@@ -409,7 +412,7 @@ export class UserSchema extends PothosSchema {
|
||||
data: {
|
||||
bankBin: args.input.bankBin,
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (args.firstName || args.lastName) {
|
||||
@@ -418,13 +421,13 @@ export class UserSchema extends PothosSchema {
|
||||
data: {
|
||||
name: `${args.firstName || ''} ${args.lastName || ''}`.trim(),
|
||||
},
|
||||
})
|
||||
});
|
||||
}
|
||||
// invalidate cache
|
||||
await ctx.http.invalidateCache()
|
||||
await ctx.http.invalidateCache();
|
||||
return await this.prisma.user.findUniqueOrThrow({
|
||||
where: { id: clerkUser.id },
|
||||
})
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -436,35 +439,30 @@ export class UserSchema extends PothosSchema {
|
||||
resolve: async (_parent, args, ctx) => {
|
||||
// check context
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
throw new Error('Not allowed');
|
||||
}
|
||||
// check context is admin
|
||||
|
||||
if (ctx.http.me?.role !== Role.ADMIN) {
|
||||
throw new Error(`Only admin can invite moderator`)
|
||||
throw new Error(`Only admin can invite moderator`);
|
||||
}
|
||||
return this.prisma.$transaction(async (tx) => {
|
||||
let user
|
||||
let user;
|
||||
// perform update role
|
||||
try {
|
||||
user = await tx.user.update({
|
||||
where: { email: args.email },
|
||||
data: { role: 'MODERATOR' },
|
||||
})
|
||||
});
|
||||
} catch (_error) {
|
||||
throw new Error(`User ${args.email} not found`)
|
||||
throw new Error(`User ${args.email} not found`);
|
||||
}
|
||||
// send email
|
||||
await this.mailService.sendTemplateEmail(
|
||||
[args.email],
|
||||
'Thông báo chọn lựa quản trị viên cho người điều hành',
|
||||
'ModeratorInvitation',
|
||||
{
|
||||
USER_NAME: user.name,
|
||||
},
|
||||
)
|
||||
return 'Invited'
|
||||
})
|
||||
await this.mailService.sendTemplateEmail([args.email], 'Thông báo chọn lựa quản trị viên cho người điều hành', 'ModeratorInvitation', {
|
||||
USER_NAME: user.name,
|
||||
});
|
||||
return 'Invited';
|
||||
});
|
||||
},
|
||||
}),
|
||||
// send test notification
|
||||
@@ -479,11 +477,11 @@ export class UserSchema extends PothosSchema {
|
||||
},
|
||||
resolve: async (_, args, ctx) => {
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
throw new Error('Not allowed');
|
||||
}
|
||||
const me = ctx.http.me
|
||||
const me = ctx.http.me;
|
||||
if (!me) {
|
||||
throw new Error('User not found')
|
||||
throw new Error('User not found');
|
||||
}
|
||||
// create message
|
||||
const message = await this.prisma.message.create({
|
||||
@@ -497,10 +495,10 @@ export class UserSchema extends PothosSchema {
|
||||
context: args.input.context ?? undefined,
|
||||
metadata: args.input.metadata ?? undefined,
|
||||
},
|
||||
})
|
||||
});
|
||||
// publish message
|
||||
await ctx.http.pubSub.publish(`${PubSubEvent.NEW_MESSAGE}.${message.recipientId}`, message)
|
||||
return message
|
||||
await ctx.http.pubSub.publish(`${PubSubEvent.NEW_MESSAGE}.${message.recipientId}`, message);
|
||||
return message;
|
||||
},
|
||||
}),
|
||||
banUser: t.field({
|
||||
@@ -510,38 +508,38 @@ export class UserSchema extends PothosSchema {
|
||||
},
|
||||
resolve: async (_parent, args, ctx) => {
|
||||
if (ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
throw new Error('Not allowed');
|
||||
}
|
||||
if (ctx.http.me?.role !== Role.ADMIN && ctx.http.me?.role !== Role.MODERATOR) {
|
||||
throw new Error(`Only admin or moderator can ban user`)
|
||||
throw new Error(`Only admin or moderator can ban user`);
|
||||
}
|
||||
if (args.userId === ctx.http.me?.id) {
|
||||
throw new Error(`Cannot ban yourself`)
|
||||
throw new Error(`Cannot ban yourself`);
|
||||
}
|
||||
// get banning user info
|
||||
const banningUser = await this.prisma.user.findUnique({
|
||||
where: { id: args.userId },
|
||||
})
|
||||
});
|
||||
if (!banningUser) {
|
||||
throw new Error(`User ${args.userId} not found`)
|
||||
throw new Error(`User ${args.userId} not found`);
|
||||
}
|
||||
// if banning user is moderator or admin, throw error
|
||||
if (banningUser.role === Role.MODERATOR || banningUser.role === Role.ADMIN) {
|
||||
throw new Error(`Cannot ban moderator or admin`)
|
||||
throw new Error(`Cannot ban moderator or admin`);
|
||||
}
|
||||
// ban user from clerk
|
||||
await clerkClient.users.banUser(args.userId)
|
||||
await clerkClient.users.banUser(args.userId);
|
||||
// invalidate cache
|
||||
await ctx.http.invalidateCache()
|
||||
await ctx.http.invalidateCache();
|
||||
// update user banned status
|
||||
await this.prisma.user.update({
|
||||
where: { id: args.userId },
|
||||
data: { banned: true },
|
||||
})
|
||||
return 'Banned'
|
||||
});
|
||||
return 'Banned';
|
||||
},
|
||||
}),
|
||||
}))
|
||||
}));
|
||||
|
||||
// Subscription section
|
||||
this.builder.subscriptionFields((t) => ({
|
||||
@@ -549,16 +547,13 @@ export class UserSchema extends PothosSchema {
|
||||
type: this.messageSchema.message(),
|
||||
subscribe: async (_, _args, ctx) => {
|
||||
if (!ctx.isSubscription) {
|
||||
throw new Error('Not allowed')
|
||||
throw new Error('Not allowed');
|
||||
}
|
||||
|
||||
return ctx.websocket.pubSub.asyncIterator([
|
||||
`${PubSubEvent.NEW_MESSAGE}.${ctx.websocket.me?.id}`,
|
||||
`${PubSubEvent.NOTIFICATION}.${ctx.websocket.me?.id}`,
|
||||
]) as unknown as AsyncIterable<Message>
|
||||
return ctx.websocket.pubSub.asyncIterator([`${PubSubEvent.NEW_MESSAGE}.${ctx.websocket.me?.id}`, `${PubSubEvent.NOTIFICATION}.${ctx.websocket.me?.id}`]) as unknown as AsyncIterable<Message>;
|
||||
},
|
||||
resolve: async (payload: Message) => payload,
|
||||
}),
|
||||
}))
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user