add invite staff

This commit is contained in:
2024-10-27 16:48:56 +07:00
parent cb6a210308
commit 1a7b5a5fb6
6 changed files with 81 additions and 13 deletions

View File

@@ -215,10 +215,7 @@ export class CenterSchema extends PothosSchema {
throw new Error('Center not found'); throw new Error('Center not found');
} }
// check if center is already approved or rejected // check if center is already approved or rejected
if ( if (center.centerStatus !== CenterStatus.PENDING) {
center.centerStatus === CenterStatus.APPROVED ||
center.centerStatus === CenterStatus.REJECTED
) {
throw new Error('Center is already approved or rejected'); throw new Error('Center is already approved or rejected');
} }
// find center owner and promote to staff // find center owner and promote to staff

View File

@@ -7,12 +7,15 @@ import {
} from '@smatch-corp/nestjs-pothos'; } from '@smatch-corp/nestjs-pothos';
import { Builder } from '../Graphql/graphql.builder'; import { Builder } from '../Graphql/graphql.builder';
import { PrismaService } from '../Prisma/prisma.service'; import { PrismaService } from '../Prisma/prisma.service';
import { MailService } from '../Mail/mail.service';
import { JwtUtils } from '../common/utils/jwt.utils';
@Injectable() @Injectable()
export class CenterStaffSchema extends PothosSchema { export class CenterStaffSchema extends PothosSchema {
constructor( constructor(
@Inject(SchemaBuilderToken) private readonly builder: Builder, @Inject(SchemaBuilderToken) private readonly builder: Builder,
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly mailService: MailService,
private readonly jwtUtils: JwtUtils,
) { ) {
super(); super();
} }
@@ -121,6 +124,45 @@ export class CenterStaffSchema extends PothosSchema {
}); });
}, },
}), }),
inviteCenterStaff: t.prismaField({
type: this.centerStaff(),
description: 'Invite a new center staff member.',
args: {
email: t.arg({ type: 'String', required: true }),
},
resolve: async (query, root, args, ctx, info) => {
return this.prisma.$transaction(async (prisma) => {
// get centerId by user id from context
const userId = ctx.me.id;
if (!userId) {
throw new Error('User ID is required');
}
// get centerId by user id
const center = await prisma.center.findUnique({
where: { centerOwnerId: userId },
});
if (!center) {
throw new Error('Center not found');
}
// build signature
const token = this.jwtUtils.signTokenRS256(
{ centerId: center.id, email: args.email },
'1d',
);
// build invite url
const inviteUrl = `${process.env.CENTER_BASE_URL}/invite?token=${token}`;
// mail to user with params centerId, email
await this.mailService.sendEmail(
args.email,
'Invite to center',
`You are invited to join the center ${center.name}.
Please click the link below to join the center:
${inviteUrl}`,
);
return null;
});
},
}),
})); }));
} }
} }

View File

@@ -33,6 +33,7 @@ export interface SchemaContext {
generator: PrismaCrudGenerator<BuilderTypes>; generator: PrismaCrudGenerator<BuilderTypes>;
} }
// extend prisma types to contain string type
export interface SchemaBuilderOption { export interface SchemaBuilderOption {
Context: SchemaContext; Context: SchemaContext;
PrismaTypes: PrismaTypes; PrismaTypes: PrismaTypes;

View File

@@ -241,10 +241,7 @@ export class ServiceSchema extends PothosSchema {
if (!service) { if (!service) {
throw new Error('Service not found'); throw new Error('Service not found');
} }
if ( if (service.status !== ServiceStatus.PENDING) {
service.status === ServiceStatus.APPROVED ||
service.status === ServiceStatus.REJECTED
) {
throw new Error('Service is already approved or rejected'); throw new Error('Service is already approved or rejected');
} }
// update service status // update service status

View File

@@ -1,9 +1,11 @@
import { Module } from '@nestjs/common'; import { Global, Module } from '@nestjs/common';
import { CommonGraphqlError } from './graphql/common.graphql.error';
import { CommonGraphqlError } from './graphql/common.graphql.error';
import { JwtUtils } from './utils/jwt.utils';
@Global()
@Module({ @Module({
imports: [], imports: [],
providers: [CommonGraphqlError], providers: [CommonGraphqlError, JwtUtils],
exports: [CommonGraphqlError], exports: [CommonGraphqlError, JwtUtils],
}) })
export class CommonModule {} export class CommonModule {}

View File

@@ -0,0 +1,29 @@
import { sign, verify } from 'jsonwebtoken';
import { Injectable } from '@nestjs/common';
@Injectable()
export class JwtUtils {
signToken(payload: string, expiresIn: string) {
return sign(payload, process.env.JWT_SECRET!, { expiresIn });
}
//eslint-disable-next-line @typescript-eslint/no-explicit-any
signTokenRS256(payload: any, expiresIn: string) {
const privateKey = process.env.JWT_RS256_PRIVATE_KEY!;
return sign(payload, privateKey, {
algorithm: 'RS256',
expiresIn,
});
}
verifyTokenRS256(token: string) {
const publicKey = process.env.JWT_RS256_PUBLIC_KEY!;
return verify(token, publicKey, {
algorithms: ['RS256'],
});
}
verifyToken(token: string) {
return verify(token, process.env.JWT_SECRET!);
}
}