update service status

This commit is contained in:
2024-10-23 15:43:50 +07:00
parent 7e55f4093b
commit 2872ac69ef
14 changed files with 212 additions and 32 deletions

22
package-lock.json generated
View File

@@ -25,6 +25,8 @@
"@nestjs/swagger": "^7.4.2",
"@pothos/core": "^4.2.0",
"@pothos/plugin-add-graphql": "^4.1.0",
"@pothos/plugin-authz": "^3.5.10",
"@pothos/plugin-errors": "^4.2.0",
"@pothos/plugin-prisma": "^4.2.1",
"@pothos/plugin-prisma-utils": "^1.2.0",
"@pothos/plugin-relay": "^4.3.0",
@@ -4860,6 +4862,26 @@
"graphql": ">=16.6.0"
}
},
"node_modules/@pothos/plugin-authz": {
"version": "3.5.10",
"resolved": "https://registry.npmjs.org/@pothos/plugin-authz/-/plugin-authz-3.5.10.tgz",
"integrity": "sha512-HIrz72+KnbvJjRKEtuy+c/J69jxET/yiSKLx/q3IV8FC4GyPKnPxlwuGlMczxlpK6+UG0gE0ziCAwY6LWEd2Yg==",
"license": "ISC",
"peerDependencies": {
"@graphql-authz/core": "*",
"graphql": ">=15.1.0"
}
},
"node_modules/@pothos/plugin-errors": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@pothos/plugin-errors/-/plugin-errors-4.2.0.tgz",
"integrity": "sha512-Yt87hn7z0+1bv7bhrJCD5GH9+NTO/5CJEkbebyM4scnnmQ0rpqyO/yViIirtzCXk/NKBHEiLrthL2frQxUmJ5w==",
"license": "ISC",
"peerDependencies": {
"@pothos/core": "*",
"graphql": ">=16.6.0"
}
},
"node_modules/@pothos/plugin-prisma": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@pothos/plugin-prisma/-/plugin-prisma-4.2.1.tgz",

View File

@@ -43,6 +43,8 @@
"@nestjs/swagger": "^7.4.2",
"@pothos/core": "^4.2.0",
"@pothos/plugin-add-graphql": "^4.1.0",
"@pothos/plugin-authz": "^3.5.10",
"@pothos/plugin-errors": "^4.2.0",
"@pothos/plugin-prisma": "^4.2.1",
"@pothos/plugin-prisma-utils": "^1.2.0",
"@pothos/plugin-relay": "^4.3.0",

View File

@@ -18,11 +18,17 @@ import SmartSubscriptionPlugin, {
subscribeOptionsFromIterator,
} from '@pothos/plugin-smart-subscriptions';
import RelayPlugin from '@pothos/plugin-relay';
import ErrorsPlugin from '@pothos/plugin-errors';
import AuthzPlugin from '@pothos/plugin-authz';
import { User } from '@prisma/client';
// import { rules } from '../common/graphql/common.graphql.auth-rule';
export interface SchemaContext {
req: Request;
res: Response;
generator: PrismaCrudGenerator<BuilderTypes>;
me: User;
pubSub: PubSub;
generator: PrismaCrudGenerator<BuilderTypes>;
}
export interface SchemaBuilderOption {
@@ -32,6 +38,7 @@ export interface SchemaBuilderOption {
Connection: {
totalCount: number | (() => number | Promise<number>);
};
// AuthZRule: keyof typeof rules;
Scalars: {
DateTime: {
Input: Date;
@@ -60,6 +67,8 @@ export class Builder extends SchemaBuilder<SchemaBuilderOption> {
SimpleObjectPlugin,
SmartSubscriptionPlugin,
RelayPlugin,
ErrorsPlugin,
AuthzPlugin,
],
smartSubscriptions: {
debounceDelay: 1000,
@@ -75,6 +84,9 @@ export class Builder extends SchemaBuilder<SchemaBuilderOption> {
onUnusedQuery: process.env.NODE_ENV === 'production' ? null : 'warn',
dmmf: getDatamodel(),
},
errors: {
defaultTypes: [],
},
});
this.generator = new PrismaCrudGenerator<BuilderTypes>(this);
this.addScalarType('DateTime', DateTimeResolver);
@@ -83,7 +95,7 @@ export class Builder extends SchemaBuilder<SchemaBuilderOption> {
this.queryType({});
this.mutationType({});
// this.subscriptionType({});
this.subscriptionType({});
this.globalConnectionField('totalCount', (t) =>
t.int({

View File

@@ -6,6 +6,7 @@ import { PothosApolloDriver } from '@smatch-corp/nestjs-pothos-apollo-driver';
import { Builder } from './graphql.builder';
import { PrismaService } from '../Prisma/prisma.service';
import { GraphQLValidationMiddleware } from '../middlewares/graphql.middleware';
import { CommonModule } from '../common/common.module';
import { PrismaModule } from '../Prisma/prisma.module';
import { UserModule } from '../User/user.module';
import { CenterModule } from '../Center/center.module';
@@ -33,6 +34,7 @@ import { ManagedServiceModule } from '../ManagedService/managedservice.module';
@Global()
@Module({
imports: [
CommonModule,
PrismaModule,
UserModule,
CenterModule,
@@ -64,9 +66,9 @@ import { ManagedServiceModule } from '../ManagedService/managedservice.module';
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: PothosApolloDriver,
path: process.env.API_PATH + '/graphql',
debug: process.env.NODE_ENV === 'development',
playground: true,
introspection: true,
debug: process.env.NODE_ENV === 'development' || false,
playground: process.env.NODE_ENV === 'development' || false,
introspection: process.env.NODE_ENV === 'development' || false,
installSubscriptionHandlers: true,
subscriptions: {
'graphql-ws': true,

View File

@@ -79,18 +79,66 @@ export class MessageSchema extends PothosSchema {
});
},
}),
messagesByChatRoomId: t.prismaField({
type: [this.message()],
description: 'Retrieve a list of messages by chat room ID.',
args: this.builder.generator.findManyArgs('Message'),
resolve: async (query, root, args) => {
return await this.prisma.message.findMany({
...query,
where: args.filter ?? undefined,
});
},
}),
}));
// mutations
this.builder.mutationFields((t) => ({
sendMessage: t.prismaField({
type: this.message(),
description: 'Send a message to a chat room.',
args: {
input: t.arg({
type: this.builder.generator.getCreateInput('Message'),
description: 'The message to send.',
required: true,
}),
},
resolve: async (query, root, args, ctx, info) => {
const message = await this.prisma.message.create({
...query,
data: args.input,
});
ctx.pubSub.publish('MESSAGE_SENT', message);
return message;
},
}),
}));
// subscriptions
// this.builder.subscriptionFields((t) => ({
// messageSent: t.field({
// subscribe: (_parent, _args, ctx) => {
// return ctx.pubSub.asyncIterator('MESSAGE_SENT');
// },
// resolve: (payload) => payload as any,
// }),
// }));
/* The code snippet `subscriptions` is currently commented out in the provided TypeScript class. It
appears to be a placeholder or a section where subscription-related logic or fields could be
defined. In GraphQL, subscriptions are used to listen for real-time events or changes in data
and receive updates when those events occur. */
this.builder.subscriptionFields((t) => ({
messageSent: t.field({
subscribe: (_, __, ctx) => {
return {
[Symbol.asyncIterator]: () =>
ctx.pubSub.asyncIterator('MESSAGE_SENT'),
};
},
type: this.message(), // Add the type property
resolve: (payload) =>
payload as {
message: 'Json';
id: string;
senderId: string;
chatRoomId: string;
sentAt: Date;
},
}),
}));
}
}

View File

@@ -36,7 +36,24 @@ export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
this.logger.log('Try to connect database...');
await this.$connect();
for (let attempt = 1; attempt <= 3; attempt++) {
try {
await this.$connect();
break; // Exit loop if connection is successful
} catch (error) {
if (attempt < 3) {
this.logger.warn(
`Connection attempt ${attempt} failed. Retrying in 5000ms...`,
);
await new Promise((resolve) => setTimeout(resolve, 5000));
} else {
this.logger.error(
'Failed to connect to the database after 3 attempts.',
);
throw error; // Rethrow the error after 3 failed attempts
}
}
}
this.logger.log('Connected.');
}

View File

@@ -8,6 +8,7 @@ import {
import { Builder } from '../Graphql/graphql.builder';
import { PrismaService } from '../Prisma/prisma.service';
import { MinioService } from '../Minio/minio.service';
import { ServiceStatus } from '@prisma/client';
@Injectable()
export class ServiceSchema extends PothosSchema {
constructor(
@@ -55,6 +56,11 @@ export class ServiceSchema extends PothosSchema {
imageFileUrl: t.exposeString('imageFileUrl', {
description: 'The URL of the image file for the service.',
}),
status: t.expose('status', {
type: ServiceStatus,
nullable: true,
description: 'The status of the service.',
}),
createdAt: t.expose('createdAt', {
type: 'DateTime',
nullable: true,
@@ -125,6 +131,7 @@ export class ServiceSchema extends PothosSchema {
description:
'Retrieve a list of services with optional filtering, ordering, and pagination.',
type: [this.service()],
args: this.builder.generator.findManyArgs('Service'),
resolve: async (query, root, args, ctx, info) => {
return await this.prisma.service.findMany({

View File

@@ -8,6 +8,9 @@ import {
import { Builder } from '../Graphql/graphql.builder';
import { PrismaService } from '../Prisma/prisma.service';
import { clerkClient } from '@clerk/express';
import type { Session } from '@clerk/express';
import { UnauthorizedException } from '@nestjs/common';
@Injectable()
export class UserSchema extends PothosSchema {
constructor(
@@ -89,17 +92,51 @@ export class UserSchema extends PothosSchema {
@Pothos()
init(): void {
this.builder.queryFields((t) => ({
// me: t.prismaField({
// description: 'Retrieve the current user.',
// type: this.user(),
// resolve: async (query, root, args, ctx, info) => {
// const sessionId = ctx.req.headers.sessionId;
// const userId = await clerkClient.users.getUser()
// return await this.prisma.user.findUnique({
// where: { id: ctx.req.headers.authorization },
// });
// },
// }),
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.',
type: this.user(),
resolve: async (query, root, args, ctx, info) => {
const sessionCookie = ctx.req.headers.cookie
?.split('; ')
.find((row) => row.startsWith('__session='))
?.split('=')[1];
if (!sessionCookie)
throw new UnauthorizedException({
message: 'No session cookie found',
});
const session = await clerkClient.sessions.getSession(sessionCookie);
if (!session) throw new UnauthorizedException();
return await this.prisma.user.findUnique({
where: { id: session.userId },
});
},
}),
users: t.prismaField({
description:

View File

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

View File

@@ -0,0 +1,24 @@
import { Inject, Injectable } from '@nestjs/common';
import { Builder } from '../../Graphql/graphql.builder';
import {
PothosRef,
PothosSchema,
SchemaBuilderToken,
} from '@smatch-corp/nestjs-pothos';
@Injectable()
export class CommonGraphqlError extends PothosSchema {
constructor(@Inject(SchemaBuilderToken) private readonly builder: Builder) {
super();
}
@PothosRef()
error() {
return this.builder.objectType(Error, {
name: 'Error',
fields: (t) => ({
message: t.exposeString('message'),
}),
});
}
}

View File

@@ -2,6 +2,7 @@ import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.js';
import { clerkMiddleware } from '@clerk/express';
import { Logger } from '@nestjs/common';
async function bootstrap() {
@@ -11,11 +12,7 @@ async function bootstrap() {
app.enableCors({
origin: corsOrigin,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
allowedHeaders: [
'Content-Type',
'Authorization',
'x-apollo-operation-name',
],
allowedHeaders: ['Content-Type', '*', 'x-apollo-operation-name'],
credentials: true,
});
@@ -46,6 +43,9 @@ async function bootstrap() {
},
};
// clerk middleware
app.use(clerkMiddleware({}));
// graphql upload
app.use(
graphqlUploadExpress({

File diff suppressed because one or more lines are too long