From 472c9a83b5164a35ed307546396c859d0f1bc05a Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Thu, 2 Oct 2025 08:17:00 -0700 Subject: [PATCH 01/71] BAN-113 chore: add socket.io-client --- .../src/business/notifications.gateway.ts | 0 package.json | 1 + yarn.lock | 26 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 apps/gate/src/business/notifications.gateway.ts diff --git a/apps/gate/src/business/notifications.gateway.ts b/apps/gate/src/business/notifications.gateway.ts new file mode 100644 index 00000000..e69de29b diff --git a/package.json b/package.json index 60e89671..abd68382 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "sharp": "^0.34.2", + "socket.io-client": "^4.8.1", "typeorm": "^0.3.23", "uuid": "^11.1.0", "uuidv4": "^6.2.13" diff --git a/yarn.lock b/yarn.lock index f0695ab4..d733e760 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4883,6 +4883,17 @@ encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" +engine.io-client@~6.6.1: + version "6.6.3" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.6.3.tgz#815393fa24f30b8e6afa8f77ccca2f28146be6de" + integrity sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + engine.io-parser "~5.2.1" + ws "~8.17.1" + xmlhttprequest-ssl "~2.1.1" + engine.io-parser@~5.2.1: version "5.2.3" resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f" @@ -9161,6 +9172,16 @@ socket.io-adapter@~2.5.2: debug "~4.3.4" ws "~8.17.1" +socket.io-client@^4.8.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.8.1.tgz#1941eca135a5490b94281d0323fe2a35f6f291cb" + integrity sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.2" + engine.io-client "~6.6.1" + socket.io-parser "~4.2.4" + socket.io-parser@~4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" @@ -10150,6 +10171,11 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== +xmlhttprequest-ssl@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz#e9e8023b3f29ef34b97a859f584c5e6c61418e23" + integrity sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ== + xss@^1.0.8: version "1.0.15" resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.15.tgz#96a0e13886f0661063028b410ed1b18670f4e59a" From db76b10346f34d5703e41b85e7941164587ed173 Mon Sep 17 00:00:00 2001 From: Ivan Ivanov Date: Fri, 3 Oct 2025 03:44:47 -0700 Subject: [PATCH 02/71] BAN-113 feat: websocket refactor in proccess --- apps/business/src/business-command.service.ts | 2 +- .../src/notifications-consumer.service.ts | 1 + .../payment-services/paypal/paypal.service.ts | 6 +- apps/gate/src/business/business.module.ts | 16 ++++- .../src/business/notifications.gateway.ts | 46 +++++++++++++ .../helper/socket-auth.helper.ts | 6 +- .../notification-service.interface.ts | 5 +- .../notifications/notifications.gateway.ts | 66 +++++++++++++++---- .../notifications/notifications.service.ts | 5 ++ 9 files changed, 129 insertions(+), 24 deletions(-) diff --git a/apps/business/src/business-command.service.ts b/apps/business/src/business-command.service.ts index 8563485d..74f39fad 100644 --- a/apps/business/src/business-command.service.ts +++ b/apps/business/src/business-command.service.ts @@ -92,7 +92,7 @@ export class BusinessCommandService { await this.businessQueryService.getCurrentUserSubscriptions( subscription.userId, ); - //todo* the second one subscription should starts from end of the first one (get first expiresAt, get time difference between now and first expiresAt, add this to startAt of the new subscription) + const price = getSubscriptionPrice(subscription.subscriptionType); const updatePlanDto = { subscriptionType: subscription.subscriptionType, diff --git a/apps/business/src/notifications-consumer.service.ts b/apps/business/src/notifications-consumer.service.ts index 5ecae996..eeb691ea 100644 --- a/apps/business/src/notifications-consumer.service.ts +++ b/apps/business/src/notifications-consumer.service.ts @@ -33,6 +33,7 @@ export class NotificationConsumer extends WorkerHost { delete notificationsObjectsAray.differ; notificationsObjectsAray.shift(); notificationsObjectsAray.map(async (item) => { + console.log('🚀 ~ NotificationConsumer ~ process ~ item:', item); await this.notificationGateway.send( item, WebsocketEvents.DaysToExpires, diff --git a/apps/business/src/payment/payment-services/paypal/paypal.service.ts b/apps/business/src/payment/payment-services/paypal/paypal.service.ts index 69800d89..d08e3e32 100644 --- a/apps/business/src/payment/payment-services/paypal/paypal.service.ts +++ b/apps/business/src/payment/payment-services/paypal/paypal.service.ts @@ -220,9 +220,9 @@ export class PayPalService implements IPaymentService { }, }, }; - //todo! To start a PayPal subscription plan immediately, set the trial_duration to 0 days when creating the subscription plan in the PayPal Developer portal or via the API, - //todo! which effectively bypasses the trial period and initiates the subscription right away. Alternatively, you can set the trial_duration_unit to "month" - //todo! but specify trial_duration as 0, which achieves the same result of starting the subscription immediately without a trial. + // To start a PayPal subscription plan immediately, set the trial_duration to 0 days when creating the subscription plan in the PayPal Developer portal or via the API, + // which effectively bypasses the trial period and initiates the subscription right away. Alternatively, you can set the trial_duration_unit to "month" + // but specify trial_duration as 0, which achieves the same result of starting the subscription immediately without a trial. const response = await axios.post( 'https://api-m.sandbox.paypal.com/v1/billing/subscriptions', diff --git a/apps/gate/src/business/business.module.ts b/apps/gate/src/business/business.module.ts index 52e71c3b..e6f480fe 100644 --- a/apps/gate/src/business/business.module.ts +++ b/apps/gate/src/business/business.module.ts @@ -3,10 +3,22 @@ import { BusinessService } from './business.service'; import { BusinessController } from './business.controller'; import { HttpModule } from '@nestjs/axios'; import { GateService } from '../../../../apps/libs/gateService'; +import { NotificationsGateway } from './notifications.gateway'; +import { JwtModule } from '@nestjs/jwt'; +import { ConfigModule, ConfigService } from '@nestjs/config'; @Module({ - imports: [HttpModule], + imports: [ + HttpModule, + JwtModule.registerAsync({ + inject: [ConfigService], + imports: [ConfigModule], + useFactory: (configService: ConfigService) => ({ + secret: configService.get('JWT_SECRET'), + }), + }), + ], controllers: [BusinessController], - providers: [BusinessService, GateService], + providers: [BusinessService, GateService, NotificationsGateway], }) export class BusinessModule {} diff --git a/apps/gate/src/business/notifications.gateway.ts b/apps/gate/src/business/notifications.gateway.ts index e69de29b..14d319f5 100644 --- a/apps/gate/src/business/notifications.gateway.ts +++ b/apps/gate/src/business/notifications.gateway.ts @@ -0,0 +1,46 @@ +import { socketAuthMiddleware } from '../../../../apps/libs/common/notifications/helper/socket-auth.helper'; +import { OnModuleInit } from '@nestjs/common'; +import { Server, Socket } from 'socket.io'; +import { + ConnectedSocket, + MessageBody, + OnGatewayConnection, + SubscribeMessage, + WebSocketGateway, + WebSocketServer, +} from '@nestjs/websockets'; +import { JwtService } from '@nestjs/jwt'; + +@WebSocketGateway(3007, { namespace: 'notifications' }) +export class NotificationsGateway implements OnModuleInit, OnGatewayConnection { + @WebSocketServer() server: Server; + private connectedSockets: string[] = []; + constructor(private readonly jwtService: JwtService) {} + + handleConnection(socket: Socket) { + console.log(`${socket.id} connected`); + //todo* make auth -> connectedSockets.push({socket.id, userId}) -> send to notifications -> add there to array + this.connectedSockets.push(socket.id); + this.server.emit('connectedSocket', { + userId: socket.data.user, + socketId: socket.id, + }); + } + + afterInit(server: Server) { + const authMiddleware = socketAuthMiddleware(this.jwtService); + server.use(authMiddleware); + } + + onModuleInit() { + this.server.on('message_from_gateway', (data: any) => { + console.log('Received from gateway:', data); + }); + } + + @SubscribeMessage('send_to_gateway') + handleMessage(@MessageBody() data: any, @ConnectedSocket() client: Socket) { + console.log('message', data); + client.emit('message_from_gateway', 'Hello from Gateway!'); + } +} diff --git a/apps/libs/common/notifications/helper/socket-auth.helper.ts b/apps/libs/common/notifications/helper/socket-auth.helper.ts index b29cd787..acedc0e5 100644 --- a/apps/libs/common/notifications/helper/socket-auth.helper.ts +++ b/apps/libs/common/notifications/helper/socket-auth.helper.ts @@ -10,9 +10,11 @@ export const socketAuthMiddleware = ( ): SocketMiddleware => { return async (socket: Socket, next) => { try { - const token = socket.handshake.headers?.authorization; + console.log('authorization:', socket.handshake.auth?.authorization); + const token = socket.handshake.auth?.authorization; + console.log('🚀 ~ socketAuthMiddleware ~ token:', token); if (!token) next(new WsException('Socket Unauthorized Exception')); - const payload = jwtService.verify(token.trim()); + const payload = jwtService.verify(token); if (!payload) next(new WsException('Socket Unauthorized Exception')); socket.data.user = payload['id']; next(); diff --git a/apps/libs/common/notifications/interfaces/notification-service.interface.ts b/apps/libs/common/notifications/interfaces/notification-service.interface.ts index ed70c61c..84c884b3 100644 --- a/apps/libs/common/notifications/interfaces/notification-service.interface.ts +++ b/apps/libs/common/notifications/interfaces/notification-service.interface.ts @@ -8,10 +8,7 @@ import { } from '@nestjs/websockets'; import { ExpiresInDuration } from 'apps/business/src/constants/expires-in-duration.enum'; -export interface INotificationsService - extends OnGatewayConnection, - OnGatewayInit, - OnGatewayDisconnect { +export interface INotificationsService { send( notification: INotification, event: WebsocketEvents, diff --git a/apps/libs/common/notifications/notifications.gateway.ts b/apps/libs/common/notifications/notifications.gateway.ts index 78098058..be5e96da 100644 --- a/apps/libs/common/notifications/notifications.gateway.ts +++ b/apps/libs/common/notifications/notifications.gateway.ts @@ -2,33 +2,75 @@ import { NotificationResponseDto } from '../../../../apps/libs/Business/dto/resp import { ExpiresInDuration } from '../../../../apps/business/src/constants/expires-in-duration.enum'; import { WebsocketEvents } from '../../../../apps/business/src/constants/websocket.event.enum'; import { INotificationsService } from './interfaces/notification-service.interface'; -import { WebSocketGateway, WebSocketServer } from '@nestjs/websockets'; +import { + OnGatewayConnection, + OnGatewayInit, + WebSocketGateway, + WebSocketServer, +} from '@nestjs/websockets'; import { INotification } from './interfaces/notification.interface'; import { socketAuthMiddleware } from './helper/socket-auth.helper'; import { NotificationsService } from './notifications.service'; import { Socket, Server } from 'socket.io'; import { JwtService } from '@nestjs/jwt'; +import { OnModuleInit } from '@nestjs/common'; +import { io, Socket as Socket1 } from 'socket.io-client'; -@WebSocketGateway() -export class NotificationsGateway implements INotificationsService { +export class NotificationsGateway + implements + INotificationsService, + OnModuleInit, + OnGatewayConnection, + OnGatewayInit +{ private connectedClients: Map = new Map(); - @WebSocketServer() - private server: Server; + private socket: Socket1; constructor( private readonly notificationsService: NotificationsService, - private readonly jwtService: JwtService, + // private readonly jwtService: JwtService, ) {} - afterInit(server: any) { - const authMiddleware = socketAuthMiddleware(this.jwtService); - server.use(authMiddleware); + async onModuleInit() { + const jwtService = new JwtService({ global: true }); + const token = await jwtService.signAsync( + { id: 'd25a77e9-1e92-469f-8e01-c325e8220cc9' }, + { secret: 'secret_jwt_1234' }, + ); + // console.log('🚀 ~ NotificationsGateway ~ onModuleInit ~ token:', token); + this.socket = io('http://localhost:3007/notifications', { + auth: { authorization: token }, + }); + this.socket.on('connect', () => { + console.log('Connected to WebSocket Gateway!'); + }); + this.socket.on('message_from_gateway', (data: any) => { + console.log('Received from gateway:', data); + }); + this.socket.on('connectedSocket', (data: any) => { + console.log('connectedSocket', data); + }); + this.socket.emit('send_to_gateway', 'hello'); } - handleDisconnect(socket: Socket) { - this.notificationsService.handleDisconnect(socket); + + sendMessageToGateway(message: string) { + this.socket.emit('send_to_gateway', message); // Emit events to the gateway } + afterInit(server: any) { + // const authMiddleware = socketAuthMiddleware(this.jwtService); + // server.use(authMiddleware); + } + // handleDisconnect(socket: Socket) { + // this.notificationsService.handleDisconnect(socket); + // } + handleConnection(socket: Socket) { - this.notificationsService.handleConnection(socket); + console.log( + '🚀 ~ NotificationsGateway ~ handleConnection ~ socket:', + socket.id, + ); + // this.notificationsService.handleConnection(socket); + this.sendMessageToGateway(`connected ${socket.id}`); } async saveNotification( diff --git a/apps/libs/common/notifications/notifications.service.ts b/apps/libs/common/notifications/notifications.service.ts index ed4de7ee..5b734fd4 100644 --- a/apps/libs/common/notifications/notifications.service.ts +++ b/apps/libs/common/notifications/notifications.service.ts @@ -25,6 +25,11 @@ export class NotificationsService implements INotificationsService { this.redisClient.call(''); } + //todo* add addClient / removeClient + addClient(socket: Socket) {} + + removeClient(socket: Socket) {} + handleDisconnect(socket: Socket) { this.connectedClients.delete(socket.id); From 982a0e4555bc31754e3b46edddc7e4e2da37ec52 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Tue, 14 Oct 2025 15:29:58 -0700 Subject: [PATCH 03/71] jenkinsfile --- apps/gate/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gate/Jenkinsfile b/apps/gate/Jenkinsfile index ff4cff94..052116f4 100644 --- a/apps/gate/Jenkinsfile +++ b/apps/gate/Jenkinsfile @@ -5,7 +5,7 @@ pipeline { ENV_TYPE = "production" PORT = 3869 NAMESPACE = "yogram-ru" - REGISTRY_HOSTNAME = "idogmat" + REGISTRY_HOSTNAME = "backtobackend226" PROJECT = "gate-yogram" SERVICE="gate" REGISTRY = "registry.hub.docker.com" From c17f5306f950de37d18ee4dbd247dd711499d4c3 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Tue, 14 Oct 2025 15:51:09 -0700 Subject: [PATCH 04/71] js --- apps/gate/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gate/Jenkinsfile b/apps/gate/Jenkinsfile index 052116f4..ff4cff94 100644 --- a/apps/gate/Jenkinsfile +++ b/apps/gate/Jenkinsfile @@ -5,7 +5,7 @@ pipeline { ENV_TYPE = "production" PORT = 3869 NAMESPACE = "yogram-ru" - REGISTRY_HOSTNAME = "backtobackend226" + REGISTRY_HOSTNAME = "idogmat" PROJECT = "gate-yogram" SERVICE="gate" REGISTRY = "registry.hub.docker.com" From f1720664ebf0e8093789dcc88b4ca5cfd0c0a871 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 12:21:17 -0700 Subject: [PATCH 05/71] try --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 59bc2e8b..ff60c275 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,4 +30,4 @@ // "@files/*": ["apps/files/*"] } } -} \ No newline at end of file +} From 66f391fb5bfeed34f21e40ee112cfea1938c9e69 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 12:24:18 -0700 Subject: [PATCH 06/71] BAN-113 --- apps/gate/src/app.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gate/src/app.module.ts b/apps/gate/src/app.module.ts index 5736acb1..768ea3c2 100644 --- a/apps/gate/src/app.module.ts +++ b/apps/gate/src/app.module.ts @@ -14,7 +14,7 @@ import { SignupModule } from './signup/signup.module'; import { AuthModule } from './auth/auth.module'; import { PostsModule } from './posts/posts.module'; import { BusinessModule } from './business/business.module'; - +// const getEnvFilePath = (env: EnvironmentsTypes) => { const defaultEnvFilePath = [ 'apps/gate/src/.env.development', From 56e39e12b3b59be3aa4f75266cd531cebb364666 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 12:31:18 -0700 Subject: [PATCH 07/71] BAN-113: jenkins --- apps/gate/Jenkinsfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/gate/Jenkinsfile b/apps/gate/Jenkinsfile index ff4cff94..5b35bdc4 100644 --- a/apps/gate/Jenkinsfile +++ b/apps/gate/Jenkinsfile @@ -3,13 +3,13 @@ pipeline { agent any environment { ENV_TYPE = "production" - PORT = 3869 + PORT = 4046 NAMESPACE = "yogram-ru" REGISTRY_HOSTNAME = "idogmat" - PROJECT = "gate-yogram" + PROJECT = "yogram" SERVICE="gate" REGISTRY = "registry.hub.docker.com" - DEPLOYMENT_NAME = "gate-yogram-deployment" + DEPLOYMENT_NAME = "yogram-deployment" IMAGE_NAME = "${env.BUILD_ID}_${env.ENV_TYPE}_${env.GIT_COMMIT}" DOCKER_BUILD_NAME = "${env.REGISTRY_HOSTNAME}/${env.PROJECT}:${env.IMAGE_NAME}" } From 8078370aa7e068aab32e88404e5c3f9bd282989e Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 12:33:42 -0700 Subject: [PATCH 08/71] BAN-113 deployment --- apps/gate/deployment.yaml | 77 +++++++++++++-------------------------- 1 file changed, 26 insertions(+), 51 deletions(-) diff --git a/apps/gate/deployment.yaml b/apps/gate/deployment.yaml index 410047b8..502f911d 100644 --- a/apps/gate/deployment.yaml +++ b/apps/gate/deployment.yaml @@ -22,148 +22,123 @@ spec: - containerPort: PORT_CONTAINER env: - - name: USERS_PROD_SERVICE_URL - valueFrom: - secretKeyRef: - name: gate-yogram-production-config-secret - key: USERS_PROD_SERVICE_URL - name: JWT_SECRET valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: JWT_SECRET - name: VERIFY_TOKEN_EXPIRES valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: VERIFY_TOKEN_EXPIRES - name: ACCESS_TOKEN_EXPIRES valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: ACCESS_TOKEN_EXPIRES - name: REFRESH_TOKEN_EXPIRES valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: REFRESH_TOKEN_EXPIRES - name: RESEND_EMAIL_VERIFY_PAGE valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: RESEND_EMAIL_VERIFY_PAGE - name: SEND_RESTORE_PASSWORD_EMAIL_PAGE valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: SEND_RESTORE_PASSWORD_EMAIL_PAGE - name: RESTORE_PASSWORD_PAGE valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: RESTORE_PASSWORD_PAGE - name: LOGIN_PAGE valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: LOGIN_PAGE - name: RECAPTCHA_HOSTNAME valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: RECAPTCHA_HOSTNAME - name: RECAPTCHA_URL valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: RECAPTCHA_URL - name: RECAPTCHA_SECRET_KEY valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: RECAPTCHA_SECRET_KEY - name: GOOGLE_OAUTH_URI valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: GOOGLE_OAUTH_URI - name: GOOGLE_CLIENT_ID valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: GOOGLE_CLIENT_ID - name: GOOGLE_CLIENT_SECRET valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: GOOGLE_CLIENT_SECRET - name: GOOGLE_REDIRECT_URI valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: GOOGLE_REDIRECT_URI - name: RMQ_URL valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: RMQ_URL - name: FORGOT_PASSWORD_TOKEN_EXPIRES valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: FORGOT_PASSWORD_TOKEN_EXPIRES - name: POSTS_PROD_SERVICE_URL valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: POSTS_PROD_SERVICE_URL - name: REDIS_USER valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: REDIS_USER - name: REDIS_PASSWORD valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: REDIS_PASSWORD - name: REDIS_HOST valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: REDIS_HOST - name: REDIS_PORT valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: REDIS_PORT - - name: USERS_SERVICE_URL - valueFrom: - secretKeyRef: - name: gate-yogram-production-config-secret - key: USERS_SERVICE_URL - - name: POSTS_SERVICE_URL - valueFrom: - secretKeyRef: - name: gate-yogram-production-config-secret - key: POSTS_SERVICE_URL - - name: FILES_SERVICE_URL - valueFrom: - secretKeyRef: - name: gate-yogram-production-config-secret - key: FILES_SERVICE_URL - - name: FILES_PROD_SERVICE_URL - valueFrom: - secretKeyRef: - name: gate-yogram-production-config-secret - key: FILES_PROD_SERVICE_URL - - name: BUSINESS_SERVICE_URL + - name: POSTS_PROD_SERVICE_URL valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret - key: BUSINESS_SERVICE_URL + name: yogram-production-config-secret + key: POSTS_PROD_SERVICE_URL - name: NODE_ENV valueFrom: secretKeyRef: - name: gate-yogram-production-config-secret + name: yogram-production-config-secret key: NODE_ENV From acc992db459f0f7fd450b7eec3ffa6cc3dde3dad Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 12:58:15 -0700 Subject: [PATCH 09/71] ... --- apps/gate/deployment.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/gate/deployment.yaml b/apps/gate/deployment.yaml index 502f911d..cc03e200 100644 --- a/apps/gate/deployment.yaml +++ b/apps/gate/deployment.yaml @@ -107,11 +107,6 @@ spec: secretKeyRef: name: yogram-production-config-secret key: FORGOT_PASSWORD_TOKEN_EXPIRES - - name: POSTS_PROD_SERVICE_URL - valueFrom: - secretKeyRef: - name: yogram-production-config-secret - key: POSTS_PROD_SERVICE_URL - name: REDIS_USER valueFrom: secretKeyRef: From 23e67145ffca36400093415e028c7583075ece8f Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 13:54:27 -0700 Subject: [PATCH 10/71] BAN-113 --- apps/gate/deployment.yaml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/gate/deployment.yaml b/apps/gate/deployment.yaml index cc03e200..d46dbe32 100644 --- a/apps/gate/deployment.yaml +++ b/apps/gate/deployment.yaml @@ -22,6 +22,11 @@ spec: - containerPort: PORT_CONTAINER env: + - name: USERS_PROD_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: USERS_PROD_SERVICE_URL - name: JWT_SECRET valueFrom: secretKeyRef: @@ -107,6 +112,11 @@ spec: secretKeyRef: name: yogram-production-config-secret key: FORGOT_PASSWORD_TOKEN_EXPIRES + - name: POSTS_PROD_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: POSTS_PROD_SERVICE_URL - name: REDIS_USER valueFrom: secretKeyRef: @@ -127,11 +137,6 @@ spec: secretKeyRef: name: yogram-production-config-secret key: REDIS_PORT - - name: POSTS_PROD_SERVICE_URL - valueFrom: - secretKeyRef: - name: yogram-production-config-secret - key: POSTS_PROD_SERVICE_URL - name: NODE_ENV valueFrom: secretKeyRef: From a69524de7b1a14b73a9f7653cce678540bddf476 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 14:30:16 -0700 Subject: [PATCH 11/71] users deployment --- apps/users/Jenkinsfile | 6 ++--- apps/users/deployment.yaml | 54 +++++++------------------------------- 2 files changed, 12 insertions(+), 48 deletions(-) diff --git a/apps/users/Jenkinsfile b/apps/users/Jenkinsfile index c09a9c6a..e33f7d5a 100644 --- a/apps/users/Jenkinsfile +++ b/apps/users/Jenkinsfile @@ -3,13 +3,13 @@ pipeline { agent any environment { ENV_TYPE = "production" - PORT = 3870 + PORT = 4049 NAMESPACE = "yogram-ru" REGISTRY_HOSTNAME = "idogmat" - PROJECT = "users-yogram" + PROJECT = "yogram-users" SERVICE="users" REGISTRY = "registry.hub.docker.com" - DEPLOYMENT_NAME = "users-yogram-deployment" + DEPLOYMENT_NAME = "yogram-users-deployment" IMAGE_NAME = "${env.BUILD_ID}_${env.ENV_TYPE}_${env.GIT_COMMIT}" DOCKER_BUILD_NAME = "${env.REGISTRY_HOSTNAME}/${env.PROJECT}:${env.IMAGE_NAME}" } diff --git a/apps/users/deployment.yaml b/apps/users/deployment.yaml index ba25cb30..c56d46a1 100644 --- a/apps/users/deployment.yaml +++ b/apps/users/deployment.yaml @@ -20,65 +20,29 @@ spec: image: REGISTRY_HOSTNAME/PROJECT:TAG_VERSION ports: - containerPort: PORT_CONTAINER - env: - - name: POSTGRES_TYPE - valueFrom: - secretKeyRef: - name: users-yogram-production-config-secret - key: POSTGRES_TYPE - - name: POSTGRES_MIGRATION_TABLE - valueFrom: - secretKeyRef: - name: users-yogram-production-config-secret - key: POSTGRES_MIGRATION_TABLE - - name: SYNCHRONIZE - valueFrom: - secretKeyRef: - name: users-yogram-production-config-secret - key: SYNCHRONIZE - - name: AUTOLOAD_ENTITIES - valueFrom: - secretKeyRef: - name: users-yogram-production-config-secret - key: AUTOLOAD_ENTITIES - - name: DROP_SCHEMA - valueFrom: - secretKeyRef: - name: users-yogram-production-config-secret - key: DROP_SCHEMA - - name: USERS_PROD_SERVICE_URL - valueFrom: - secretKeyRef: - name: users-yogram-production-config-secret - key: USERS_PROD_SERVICE_URL - name: RMQ_URL valueFrom: secretKeyRef: - name: users-yogram-production-config-secret + name: yogram-users-production-config-secret key: RMQ_URL - - name: FILES_SERVICE_URL - valueFrom: - secretKeyRef: - name: users-yogram-production-config-secret - key: FILES_SERVICE_URL - name: BUCKET valueFrom: secretKeyRef: - name: users-yogram-production-config-secret + name: yogram-users-production-config-secret key: BUCKET - - name: FILES_SERVICE_AVATAR_UPLOAD_PATH + - name: FILES_SERVICE_URL valueFrom: secretKeyRef: - name: users-yogram-production-config-secret - key: FILES_SERVICE_AVATAR_UPLOAD_PATH - - name: FILES_SERVICE_CHUNKS_DIR + name: yogram-users-production-config-secret + key: FILES_SERVICE_URL + - name: FILES_SERVICE_AVATAR_UPLOAD_PATH valueFrom: secretKeyRef: - name: users-yogram-production-config-secret - key: FILES_SERVICE_CHUNKS_DIR + name: yogram-users-production-config-secret + key: FILES_SERVICE_AVATAR_UPLOAD_PATH - name: POSTGRES_URL valueFrom: secretKeyRef: - name: users-yogram-production-config-secret + name: yogram-users-production-config-secret key: POSTGRES_URL From a1dfc07dd1ba29ff013f6d5c261472f1045f48f8 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 14:53:31 -0700 Subject: [PATCH 12/71] posts deployment --- apps/posts/Jenkinsfile | 6 +-- apps/posts/deployment.yaml | 95 ++++++++++++-------------------------- 2 files changed, 33 insertions(+), 68 deletions(-) diff --git a/apps/posts/Jenkinsfile b/apps/posts/Jenkinsfile index a4eeaaa6..e4c68b72 100644 --- a/apps/posts/Jenkinsfile +++ b/apps/posts/Jenkinsfile @@ -3,13 +3,13 @@ pipeline { agent any environment { ENV_TYPE = "production" - PORT = 3914 + PORT = 4050 NAMESPACE = "yogram-ru" REGISTRY_HOSTNAME = "idogmat" - PROJECT = "posts-yogram" + PROJECT = "yogram-posts" SERVICE="posts" REGISTRY = "registry.hub.docker.com" - DEPLOYMENT_NAME = "posts-yogram-deployment" + DEPLOYMENT_NAME = "yogram-posts-deployment" IMAGE_NAME = "${env.BUILD_ID}_${env.ENV_TYPE}_${env.GIT_COMMIT}" DOCKER_BUILD_NAME = "${env.REGISTRY_HOSTNAME}/${env.PROJECT}:${env.IMAGE_NAME}" } diff --git a/apps/posts/deployment.yaml b/apps/posts/deployment.yaml index afeb95dc..57002f8c 100644 --- a/apps/posts/deployment.yaml +++ b/apps/posts/deployment.yaml @@ -16,69 +16,34 @@ spec: project: PROJECT spec: containers: - - name: PROJECT - image: REGISTRY_HOSTNAME/PROJECT:TAG_VERSION - ports: - - containerPort: PORT_CONTAINER + - name: PROJECT + image: REGISTRY_HOSTNAME/PROJECT:TAG_VERSION + ports: + - containerPort: PORT_CONTAINER - env: - - name: POSTGRES_TYPE - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: POSTGRES_TYPE - - name: POSTGRES_MIGRATION_TABLE - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: POSTGRES_MIGRATION_TABLE - - name: SYNCHRONIZE - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: SYNCHRONIZE - - name: AUTOLOAD_ENTITIES - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: AUTOLOAD_ENTITIES - - name: DROP_SCHEMA - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: DROP_SCHEMA - - name: RMQ_URL - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: RMQ_URL - - name: FILES_SERVICE_URL - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: FILES_SERVICE_URL - - name: BUCKET - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: BUCKET - - name: FILES_SERVICE_POSTS_UPLOAD_PATH - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: FILES_SERVICE_POSTS_UPLOAD_PATH - - name: FILES_SERVICE_CHUNKS_DIR - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: FILES_SERVICE_CHUNKS_DIR - - name: USERS_SERVICE_URL - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: USERS_SERVICE_URL - - name: POSTGRES_URL - valueFrom: - secretKeyRef: - name: posts-yogram-production-config-secret - key: POSTGRES_URL + env: + - name: POSTGRES_TYPE + valueFrom: + secretKeyRef: + name: yogram-posts-production-config-secret + key: POSTGRES_TYPE + - name: RMQ_URL + valueFrom: + secretKeyRef: + name: yogram-posts-production-config-secret + key: RMQ_URL + - name: FILES_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-posts-production-config-secret + key: FILES_SERVICE_URL + - name: BUCKET + valueFrom: + secretKeyRef: + name: yogram-posts-production-config-secret + key: BUCKET + - name: FILES_SERVICE_POSTS_UPLOAD_PATH + valueFrom: + secretKeyRef: + name: yogram-posts-production-config-secret + key: FILES_SERVICE_POSTS_UPLOAD_PATH From aaeb0ec4c0046009457dce9b8b22729895cd354b Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 17:28:36 -0700 Subject: [PATCH 13/71] files deploy --- apps/files/Jenkinsfile | 6 ++--- apps/files/deployment.yaml | 53 +++++++++++++++++--------------------- apps/posts/deployment.yaml | 15 +++++++++++ 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/apps/files/Jenkinsfile b/apps/files/Jenkinsfile index e59c4299..4ce0b3e2 100644 --- a/apps/files/Jenkinsfile +++ b/apps/files/Jenkinsfile @@ -3,13 +3,13 @@ pipeline { agent any environment { ENV_TYPE = "production" - PORT = 3930 + PORT = 4051 NAMESPACE = "yogram-ru" REGISTRY_HOSTNAME = "idogmat" - PROJECT = "files-yogram" + PROJECT = "yogram-files" SERVICE="files" REGISTRY = "registry.hub.docker.com" - DEPLOYMENT_NAME = "files-yogram-deployment" + DEPLOYMENT_NAME = "yogram-files-deployment" IMAGE_NAME = "${env.BUILD_ID}_${env.ENV_TYPE}_${env.GIT_COMMIT}" DOCKER_BUILD_NAME = "${env.REGISTRY_HOSTNAME}/${env.PROJECT}:${env.IMAGE_NAME}" } diff --git a/apps/files/deployment.yaml b/apps/files/deployment.yaml index 23e26479..9d81285c 100644 --- a/apps/files/deployment.yaml +++ b/apps/files/deployment.yaml @@ -22,48 +22,43 @@ spec: - containerPort: PORT_CONTAINER env: - - name: AWS_SECRET_KEY - valueFrom: - secretKeyRef: - name: files-yogram-production-config-secret - key: AWS_SECRET_KEY - - name: AWS_REGION - valueFrom: - secretKeyRef: - name: files-yogram-production-config-secret - key: AWS_REGION - - name: RMQ_URL + - name: FILES_SERVICE_AVATAR_UPLOAD_PATH valueFrom: secretKeyRef: - name: files-yogram-production-config-secret - key: RMQ_URL - - name: BUCKET + name: yogram-files-production-config-secret + key: FILES_SERVICE_AVATAR_UPLOAD_PATH + - name: FILES_SERVICE_CHUNKS_DIR valueFrom: secretKeyRef: - name: files-yogram-production-config-secret - key: BUCKET + name: yogram-files-production-config-secret + key: FILES_SERVICE_CHUNKS_DIR - name: UPLOAD_SERVICE_URL_PREFIX valueFrom: secretKeyRef: - name: files-yogram-production-config-secret + name: yogram-files-production-config-secret key: UPLOAD_SERVICE_URL_PREFIX - - name: FILES_SERVICE_CHUNKS_DIR - valueFrom: - secretKeyRef: - name: files-yogram-production-config-secret - key: FILES_SERVICE_CHUNKS_DIR - - name: FILES_SERVICE_AVATAR_UPLOAD_PATH + - name: AWS_REGION valueFrom: secretKeyRef: - name: files-yogram-production-config-secret - key: FILES_SERVICE_AVATAR_UPLOAD_PATH - - name: AWS_ACCOUNT_ID + name: yogram-files-production-config-secret + key: AWS_REGION + - name: AWS_SECRET_KEY valueFrom: secretKeyRef: - name: files-yogram-production-config-secret - key: AWS_ACCOUNT_ID + name: yogram-files-production-config-secret + key: AWS_SECRET_KEY - name: AWS_ACCESS_KEY valueFrom: secretKeyRef: - name: files-yogram-production-config-secret + name: yogram-files-production-config-secret key: AWS_ACCESS_KEY + - name: RMQ_URL + valueFrom: + secretKeyRef: + name: yogram-files-production-config-secret + key: RMQ_URL + - name: BUCKET + valueFrom: + secretKeyRef: + name: yogram-files-production-config-secret + key: BUCKET diff --git a/apps/posts/deployment.yaml b/apps/posts/deployment.yaml index 57002f8c..05b53ed8 100644 --- a/apps/posts/deployment.yaml +++ b/apps/posts/deployment.yaml @@ -47,3 +47,18 @@ spec: secretKeyRef: name: yogram-posts-production-config-secret key: FILES_SERVICE_POSTS_UPLOAD_PATH + - name: FILES_SERVICE_CHUNKS_DIR + valueFrom: + secretKeyRef: + name: yogram-posts-production-config-secret + key: FILES_SERVICE_CHUNKS_DIR + - name: USERS_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-posts-production-config-secret + key: USERS_SERVICE_URL + - name: POSTGRES_URL + valueFrom: + secretKeyRef: + name: yogram-posts-production-config-secret + key: POSTGRES_URL From d41d5b041ef42e437607cada6315069c79525942 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 17:52:06 -0700 Subject: [PATCH 14/71] business deployment --- apps/business/Jenkinsfile | 6 ++-- apps/business/deployment.yaml | 59 ++++++++++++++++------------------- 2 files changed, 30 insertions(+), 35 deletions(-) diff --git a/apps/business/Jenkinsfile b/apps/business/Jenkinsfile index 7abb8b69..279045d0 100644 --- a/apps/business/Jenkinsfile +++ b/apps/business/Jenkinsfile @@ -3,13 +3,13 @@ pipeline { agent any environment { ENV_TYPE = "production" - PORT = 3986 + PORT = 4052 NAMESPACE = "yogram-ru" REGISTRY_HOSTNAME = "idogmat" - PROJECT = "business" + PROJECT = "yogram-business" SERVICE="business" REGISTRY = "registry.hub.docker.com" - DEPLOYMENT_NAME = "business-deployment" + DEPLOYMENT_NAME = "yogram-business-deployment" IMAGE_NAME = "${env.BUILD_ID}_${env.ENV_TYPE}_${env.GIT_COMMIT}" DOCKER_BUILD_NAME = "${env.REGISTRY_HOSTNAME}/${env.PROJECT}:${env.IMAGE_NAME}" } diff --git a/apps/business/deployment.yaml b/apps/business/deployment.yaml index 65e66d67..2f5a6eea 100644 --- a/apps/business/deployment.yaml +++ b/apps/business/deployment.yaml @@ -22,63 +22,58 @@ spec: - containerPort: PORT_CONTAINER env: - - name: POSTGRES_TYPE - valueFrom: - secretKeyRef: - name: business-production-config-secret - key: POSTGRES_TYPE - - name: BUSINESS_SERVICE_URL - valueFrom: - secretKeyRef: - name: business-production-config-secret - key: BUSINESS_SERVICE_URL - - name: POSTGRES_URL_SLAVE - valueFrom: - secretKeyRef: - name: business-production-config-secret - key: POSTGRES_URL_SLAVE - - name: PAYPAL_CLIENT_ID - valueFrom: - secretKeyRef: - name: business-production-config-secret - key: PAYPAL_CLIENT_ID - name: PAYPAL_SECRET valueFrom: secretKeyRef: - name: business-production-config-secret + name: yogram-business-production-config-secret key: PAYPAL_SECRET - name: USERS_SERVICE_URL valueFrom: secretKeyRef: - name: business-production-config-secret + name: yogram-business-production-config-secret key: USERS_SERVICE_URL - - name: REDIS_USER - valueFrom: - secretKeyRef: - name: business-production-config-secret - key: REDIS_USER - name: REDIS_PASSWORD valueFrom: secretKeyRef: - name: business-production-config-secret + name: yogram-business-production-config-secret key: REDIS_PASSWORD - name: REDIS_HOST valueFrom: secretKeyRef: - name: business-production-config-secret + name: yogram-business-production-config-secret key: REDIS_HOST - name: REDIS_PORT valueFrom: secretKeyRef: - name: business-production-config-secret + name: yogram-business-production-config-secret key: REDIS_PORT - name: TIME_PERIOD valueFrom: secretKeyRef: - name: business-production-config-secret + name: yogram-business-production-config-secret key: TIME_PERIOD + - name: REDIS_USER + valueFrom: + secretKeyRef: + name: yogram-business-production-config-secret + key: REDIS_USER + - name: BUSINESS_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-business-production-config-secret + key: BUSINESS_SERVICE_URL + - name: POSTGRES_TYPE + valueFrom: + secretKeyRef: + name: yogram-business-production-config-secret + key: POSTGRES_TYPE - name: POSTGRES_URL valueFrom: secretKeyRef: - name: business-production-config-secret + name: yogram-business-production-config-secret key: POSTGRES_URL + - name: PAYPAL_CLIENT_ID + valueFrom: + secretKeyRef: + name: yogram-business-production-config-secret + key: PAYPAL_CLIENT_ID From e50ffffbb889ca96555c23862e4be1374f7a6407 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 18:07:56 -0700 Subject: [PATCH 15/71] Deployment --- apps/gate/deployment.yaml | 25 ++++++++++++++++++++----- apps/users/deployment.yaml | 6 ++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/apps/gate/deployment.yaml b/apps/gate/deployment.yaml index d46dbe32..9e44d97e 100644 --- a/apps/gate/deployment.yaml +++ b/apps/gate/deployment.yaml @@ -112,11 +112,6 @@ spec: secretKeyRef: name: yogram-production-config-secret key: FORGOT_PASSWORD_TOKEN_EXPIRES - - name: POSTS_PROD_SERVICE_URL - valueFrom: - secretKeyRef: - name: yogram-production-config-secret - key: POSTS_PROD_SERVICE_URL - name: REDIS_USER valueFrom: secretKeyRef: @@ -137,6 +132,26 @@ spec: secretKeyRef: name: yogram-production-config-secret key: REDIS_PORT + - name: USERS_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: USERS_SERVICE_URL + - name: POSTS_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: POSTS_SERVICE_URL + - name: FILES_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: FILES_SERVICE_URL + - name: BUSINESS_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: BUSINESS_SERVICE_URL - name: NODE_ENV valueFrom: secretKeyRef: diff --git a/apps/users/deployment.yaml b/apps/users/deployment.yaml index c56d46a1..20fc4abd 100644 --- a/apps/users/deployment.yaml +++ b/apps/users/deployment.yaml @@ -20,6 +20,7 @@ spec: image: REGISTRY_HOSTNAME/PROJECT:TAG_VERSION ports: - containerPort: PORT_CONTAINER + env: - name: RMQ_URL valueFrom: @@ -41,6 +42,11 @@ spec: secretKeyRef: name: yogram-users-production-config-secret key: FILES_SERVICE_AVATAR_UPLOAD_PATH + - name: FILES_SERVICE_CHUNKS_DIR + valueFrom: + secretKeyRef: + name: yogram-users-production-config-secret + key: FILES_SERVICE_CHUNKS_DIR - name: POSTGRES_URL valueFrom: secretKeyRef: From a671f62c3ead604c2e84edc238bece1f1cddb04b Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 18:46:20 -0700 Subject: [PATCH 16/71] mailer deploy --- apps/mailer/Jenkinsfile | 6 +++--- apps/mailer/deployment.yaml | 14 +++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/mailer/Jenkinsfile b/apps/mailer/Jenkinsfile index e600a5e7..8ebe6fdb 100644 --- a/apps/mailer/Jenkinsfile +++ b/apps/mailer/Jenkinsfile @@ -3,13 +3,13 @@ pipeline { agent any environment { ENV_TYPE = "production" - PORT = 3884 + PORT = 4053 NAMESPACE = "yogram-ru" REGISTRY_HOSTNAME = "idogmat" - PROJECT = "mailer" + PROJECT = "yogram-mailer" SERVICE="mailer" REGISTRY = "registry.hub.docker.com" - DEPLOYMENT_NAME = "mailer-deployment" + DEPLOYMENT_NAME = "yogram-mailer-deployment" IMAGE_NAME = "${env.BUILD_ID}_${env.ENV_TYPE}_${env.GIT_COMMIT}" DOCKER_BUILD_NAME = "${env.REGISTRY_HOSTNAME}/${env.PROJECT}:${env.IMAGE_NAME}" } diff --git a/apps/mailer/deployment.yaml b/apps/mailer/deployment.yaml index 37f6e317..8fb166a5 100644 --- a/apps/mailer/deployment.yaml +++ b/apps/mailer/deployment.yaml @@ -25,35 +25,35 @@ spec: - name: SMTP_USER valueFrom: secretKeyRef: - name: mailer-production-config-secret + name: yogram-mailer-production-config-secret key: SMTP_USER - name: SMTP_HOST valueFrom: secretKeyRef: - name: mailer-production-config-secret + name: yogram-mailer-production-config-secret key: SMTP_HOST - name: SMTP_PASS valueFrom: secretKeyRef: - name: mailer-production-config-secret + name: yogram-mailer-production-config-secret key: SMTP_PASS - name: SMTP_PORT valueFrom: secretKeyRef: - name: mailer-production-config-secret + name: yogram-mailer-production-config-secret key: SMTP_PORT - name: JWT_SECRET valueFrom: secretKeyRef: - name: mailer-production-config-secret + name: yogram-mailer-production-config-secret key: JWT_SECRET - name: VERIFY_TOKEN_EXPIRES valueFrom: secretKeyRef: - name: mailer-production-config-secret + name: yogram-mailer-production-config-secret key: VERIFY_TOKEN_EXPIRES - name: RMQ_URL valueFrom: secretKeyRef: - name: mailer-production-config-secret + name: yogram-mailer-production-config-secret key: RMQ_URL From a228f20ce4c3a6717c6ab75c9628d0127cd61144 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 16 Oct 2025 19:08:18 -0700 Subject: [PATCH 17/71] gate fix config --- apps/gate/src/settings/configuration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gate/src/settings/configuration.ts b/apps/gate/src/settings/configuration.ts index 96a78170..cd83320e 100644 --- a/apps/gate/src/settings/configuration.ts +++ b/apps/gate/src/settings/configuration.ts @@ -21,7 +21,7 @@ export const getConfiguration = () => { process.env.NODE_ENV === 'DEVELOPMENT' || process.env.NODE_ENV === 'TESTING' ? `http://localhost:${process.env[u + '_PORT']}/api/v1` - : `${process.env[u + '_PROD_SERVICE_URL']}/api/v1`, + : `${process.env[u + '_SERVICE_URL']}/api/v1`, }); return acc; }, From 10dde1a55713ee52d167a0a44846767632c09407 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 15:22:02 -0700 Subject: [PATCH 18/71] gate prod --- apps/gate/deployment.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/gate/deployment.yaml b/apps/gate/deployment.yaml index 9e44d97e..f25cebbf 100644 --- a/apps/gate/deployment.yaml +++ b/apps/gate/deployment.yaml @@ -22,11 +22,6 @@ spec: - containerPort: PORT_CONTAINER env: - - name: USERS_PROD_SERVICE_URL - valueFrom: - secretKeyRef: - name: yogram-production-config-secret - key: USERS_PROD_SERVICE_URL - name: JWT_SECRET valueFrom: secretKeyRef: @@ -112,6 +107,11 @@ spec: secretKeyRef: name: yogram-production-config-secret key: FORGOT_PASSWORD_TOKEN_EXPIRES + - name: POSTS_PROD_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: POSTS_PROD_SERVICE_URL - name: REDIS_USER valueFrom: secretKeyRef: From 0b60125c5ade56da6e3ddcfa2256a79fbce24a65 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 15:31:14 -0700 Subject: [PATCH 19/71] env gate change --- apps/gate/src/settings/configuration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gate/src/settings/configuration.ts b/apps/gate/src/settings/configuration.ts index cd83320e..b9cde58e 100644 --- a/apps/gate/src/settings/configuration.ts +++ b/apps/gate/src/settings/configuration.ts @@ -10,7 +10,7 @@ export const EnvironmentMode = { TESTING: 'TESTING', }; export const Environments = Object.keys(EnvironmentMode); - +// export const getConfiguration = () => { const SERVICES_NAMES = ['USERS', 'POSTS', 'FILES', 'BUSINESS']; From 761799da7ce9263d04443fdc46188238c1bb502a Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 15:47:23 -0700 Subject: [PATCH 20/71] logs --- apps/gate/src/auth/auth.controller.ts | 2 ++ apps/gate/src/auth/guards/login.guard.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/apps/gate/src/auth/auth.controller.ts b/apps/gate/src/auth/auth.controller.ts index 7d6e09d1..4cff5d34 100644 --- a/apps/gate/src/auth/auth.controller.ts +++ b/apps/gate/src/auth/auth.controller.ts @@ -66,6 +66,8 @@ export class AuthController { @Req() req: Request, @Res({ passthrough: true }) res: Response, ): Promise<{ accessToken: string }> { + console.log('auth login'); + const userAgent = req.headers['user-agent']; const [accessToken, refreshToken] = await this.authService.proccessLogin( user.id, diff --git a/apps/gate/src/auth/guards/login.guard.ts b/apps/gate/src/auth/guards/login.guard.ts index 06fb571f..2ad20631 100644 --- a/apps/gate/src/auth/guards/login.guard.ts +++ b/apps/gate/src/auth/guards/login.guard.ts @@ -18,6 +18,7 @@ export class LoginGuard implements CanActivate { async canActivate(context: ExecutionContext) { const request = context.switchToHttp().getRequest(); const loginDto: LoginDto = request.body; + console.log('🚀 ~ LoginGuard ~ canActivate ~ loginDto:', loginDto); const user = await this.usersGateService.requestHttpServiceGet( HttpServices.Users, `users/login/${loginDto.email}`, From 11909c8d41dc68b6ac0841e8253b0e702ab0a34c Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 15:53:52 -0700 Subject: [PATCH 21/71] ENV PORT GATE --- apps/gate/deployment.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/gate/deployment.yaml b/apps/gate/deployment.yaml index f25cebbf..c31c5286 100644 --- a/apps/gate/deployment.yaml +++ b/apps/gate/deployment.yaml @@ -152,6 +152,11 @@ spec: secretKeyRef: name: yogram-production-config-secret key: BUSINESS_SERVICE_URL + - name: PORT + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: PORT - name: NODE_ENV valueFrom: secretKeyRef: From da6b12507a65b37bd0ac74f7c6aa143b24d46963 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 19:27:47 -0700 Subject: [PATCH 22/71] remove api/v1 from gate configuration --- apps/gate/src/settings/configuration.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/gate/src/settings/configuration.ts b/apps/gate/src/settings/configuration.ts index b9cde58e..b711248e 100644 --- a/apps/gate/src/settings/configuration.ts +++ b/apps/gate/src/settings/configuration.ts @@ -13,7 +13,7 @@ export const Environments = Object.keys(EnvironmentMode); // export const getConfiguration = () => { const SERVICES_NAMES = ['USERS', 'POSTS', 'FILES', 'BUSINESS']; - + //todo! api/v1 problem when call microservices const SERVICES_URLS = SERVICES_NAMES.reduce>( (acc, u) => { Object.assign(acc, { @@ -21,13 +21,13 @@ export const getConfiguration = () => { process.env.NODE_ENV === 'DEVELOPMENT' || process.env.NODE_ENV === 'TESTING' ? `http://localhost:${process.env[u + '_PORT']}/api/v1` - : `${process.env[u + '_SERVICE_URL']}/api/v1`, + : `${process.env[u + '_SERVICE_URL']}`, }); return acc; }, {}, ); - console.log('SERVICES_URLS', SERVICES_URLS); + console.log('SERVICES_URLS', SERVICES_URLS); // http://yogram-users-service.yogram-ru:4049/api/v1 return { NODE_ENV: (Environments.includes(process.env.NODE_ENV?.trim()) From 2207266e673834da2e84d9a9bf3d06ab86e008f7 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 19:56:05 -0700 Subject: [PATCH 23/71] gate deployment --- apps/gate/deployment.yaml | 11 ++++++++++- apps/gate/src/app.module.ts | 2 +- apps/gate/src/auth/auth.controller.ts | 2 -- apps/gate/src/auth/guards/login.guard.ts | 1 - apps/gate/src/settings/configuration.ts | 8 ++++---- 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/gate/deployment.yaml b/apps/gate/deployment.yaml index c31c5286..bc14dea3 100644 --- a/apps/gate/deployment.yaml +++ b/apps/gate/deployment.yaml @@ -20,7 +20,6 @@ spec: image: REGISTRY_HOSTNAME/PROJECT:TAG_VERSION ports: - containerPort: PORT_CONTAINER - env: - name: JWT_SECRET valueFrom: @@ -157,6 +156,16 @@ spec: secretKeyRef: name: yogram-production-config-secret key: PORT + - name: FILES_PROD_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: FILES_PROD_SERVICE_URL + - name: POSTS_PROD_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: POSTS_PROD_SERVICE_URL - name: NODE_ENV valueFrom: secretKeyRef: diff --git a/apps/gate/src/app.module.ts b/apps/gate/src/app.module.ts index 768ea3c2..5736acb1 100644 --- a/apps/gate/src/app.module.ts +++ b/apps/gate/src/app.module.ts @@ -14,7 +14,7 @@ import { SignupModule } from './signup/signup.module'; import { AuthModule } from './auth/auth.module'; import { PostsModule } from './posts/posts.module'; import { BusinessModule } from './business/business.module'; -// + const getEnvFilePath = (env: EnvironmentsTypes) => { const defaultEnvFilePath = [ 'apps/gate/src/.env.development', diff --git a/apps/gate/src/auth/auth.controller.ts b/apps/gate/src/auth/auth.controller.ts index 4cff5d34..7d6e09d1 100644 --- a/apps/gate/src/auth/auth.controller.ts +++ b/apps/gate/src/auth/auth.controller.ts @@ -66,8 +66,6 @@ export class AuthController { @Req() req: Request, @Res({ passthrough: true }) res: Response, ): Promise<{ accessToken: string }> { - console.log('auth login'); - const userAgent = req.headers['user-agent']; const [accessToken, refreshToken] = await this.authService.proccessLogin( user.id, diff --git a/apps/gate/src/auth/guards/login.guard.ts b/apps/gate/src/auth/guards/login.guard.ts index 2ad20631..06fb571f 100644 --- a/apps/gate/src/auth/guards/login.guard.ts +++ b/apps/gate/src/auth/guards/login.guard.ts @@ -18,7 +18,6 @@ export class LoginGuard implements CanActivate { async canActivate(context: ExecutionContext) { const request = context.switchToHttp().getRequest(); const loginDto: LoginDto = request.body; - console.log('🚀 ~ LoginGuard ~ canActivate ~ loginDto:', loginDto); const user = await this.usersGateService.requestHttpServiceGet( HttpServices.Users, `users/login/${loginDto.email}`, diff --git a/apps/gate/src/settings/configuration.ts b/apps/gate/src/settings/configuration.ts index b711248e..96a78170 100644 --- a/apps/gate/src/settings/configuration.ts +++ b/apps/gate/src/settings/configuration.ts @@ -10,10 +10,10 @@ export const EnvironmentMode = { TESTING: 'TESTING', }; export const Environments = Object.keys(EnvironmentMode); -// + export const getConfiguration = () => { const SERVICES_NAMES = ['USERS', 'POSTS', 'FILES', 'BUSINESS']; - //todo! api/v1 problem when call microservices + const SERVICES_URLS = SERVICES_NAMES.reduce>( (acc, u) => { Object.assign(acc, { @@ -21,13 +21,13 @@ export const getConfiguration = () => { process.env.NODE_ENV === 'DEVELOPMENT' || process.env.NODE_ENV === 'TESTING' ? `http://localhost:${process.env[u + '_PORT']}/api/v1` - : `${process.env[u + '_SERVICE_URL']}`, + : `${process.env[u + '_PROD_SERVICE_URL']}/api/v1`, }); return acc; }, {}, ); - console.log('SERVICES_URLS', SERVICES_URLS); // http://yogram-users-service.yogram-ru:4049/api/v1 + console.log('SERVICES_URLS', SERVICES_URLS); return { NODE_ENV: (Environments.includes(process.env.NODE_ENV?.trim()) From 40769e945f955f7e19d4b868a57ee272d6916a8f Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 20:10:46 -0700 Subject: [PATCH 24/71] ddd --- apps/gate/src/settings/configuration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gate/src/settings/configuration.ts b/apps/gate/src/settings/configuration.ts index 96a78170..33e4d226 100644 --- a/apps/gate/src/settings/configuration.ts +++ b/apps/gate/src/settings/configuration.ts @@ -13,7 +13,7 @@ export const Environments = Object.keys(EnvironmentMode); export const getConfiguration = () => { const SERVICES_NAMES = ['USERS', 'POSTS', 'FILES', 'BUSINESS']; - + // const SERVICES_URLS = SERVICES_NAMES.reduce>( (acc, u) => { Object.assign(acc, { From 5d55be9d605cc1bba51fb203c463d215a85ec6af Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 20:18:05 -0700 Subject: [PATCH 25/71] fff --- apps/gate/deployment.yaml | 5 +++++ apps/gate/src/settings/configuration.ts | 2 +- apps/libs/gateService/index.ts | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/gate/deployment.yaml b/apps/gate/deployment.yaml index bc14dea3..fa63ec19 100644 --- a/apps/gate/deployment.yaml +++ b/apps/gate/deployment.yaml @@ -166,6 +166,11 @@ spec: secretKeyRef: name: yogram-production-config-secret key: POSTS_PROD_SERVICE_URL + - name: USERS_PROD_SERVICE_URL + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: USERS_PROD_SERVICE_URL - name: NODE_ENV valueFrom: secretKeyRef: diff --git a/apps/gate/src/settings/configuration.ts b/apps/gate/src/settings/configuration.ts index 33e4d226..96a78170 100644 --- a/apps/gate/src/settings/configuration.ts +++ b/apps/gate/src/settings/configuration.ts @@ -13,7 +13,7 @@ export const Environments = Object.keys(EnvironmentMode); export const getConfiguration = () => { const SERVICES_NAMES = ['USERS', 'POSTS', 'FILES', 'BUSINESS']; - // + const SERVICES_URLS = SERVICES_NAMES.reduce>( (acc, u) => { Object.assign(acc, { diff --git a/apps/libs/gateService/index.ts b/apps/libs/gateService/index.ts index 77860c57..cee61582 100644 --- a/apps/libs/gateService/index.ts +++ b/apps/libs/gateService/index.ts @@ -33,6 +33,11 @@ export class GateService { async requestHttpServicePost(service, path, payload, headers) { try { + console.log( + 'users login url =', + [this.services[service], path].join('/'), + ); + const { data } = await lastValueFrom( this.httpService.post( [this.services[service], path].join('/'), From fd4511d53a87e6c110d7fa54e7f725d94c8f4c13 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 20:34:51 -0700 Subject: [PATCH 26/71] .... --- apps/libs/gateService/index.ts | 9 ++++----- apps/users/src/users.controller.ts | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/libs/gateService/index.ts b/apps/libs/gateService/index.ts index cee61582..d68e2b64 100644 --- a/apps/libs/gateService/index.ts +++ b/apps/libs/gateService/index.ts @@ -33,11 +33,6 @@ export class GateService { async requestHttpServicePost(service, path, payload, headers) { try { - console.log( - 'users login url =', - [this.services[service], path].join('/'), - ); - const { data } = await lastValueFrom( this.httpService.post( [this.services[service], path].join('/'), @@ -56,6 +51,10 @@ export class GateService { async requestHttpServiceGet(service, path, headers) { try { + console.log( + 'users login url =', + [this.services[service], path].join('/'), + ); const { data } = await lastValueFrom( this.httpService.get([this.services[service], path].join('/'), { headers, diff --git a/apps/users/src/users.controller.ts b/apps/users/src/users.controller.ts index 89b2f304..3a9ef8e7 100644 --- a/apps/users/src/users.controller.ts +++ b/apps/users/src/users.controller.ts @@ -102,6 +102,7 @@ export class UsersController { @Get('users/login/:email') async userLogin(@Param() email: string): Promise { + console.log('🚀 ~ UsersController ~ userLogin ~ email:', email); return await this.queryBus.execute(new UserLoginQuery(email['email'])); } From 49c7d3961bcc5a0a79fb3171f3172d753ccc167f Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 21:01:19 -0700 Subject: [PATCH 27/71] autoLoadEntities true --- apps/libs/common/database/database.module.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/libs/common/database/database.module.ts b/apps/libs/common/database/database.module.ts index 34d13b0c..6373e47c 100644 --- a/apps/libs/common/database/database.module.ts +++ b/apps/libs/common/database/database.module.ts @@ -30,9 +30,9 @@ export class DatabaseModule { migrationsTableName: configService.get('migrationsTableName'), entities: [`${__dirname}/infrastructure/**/*.entity{.ts,.js}`], migrations: [`${__dirname}/../../db/migrations/*{.ts,.js}`], - autoLoadEntities: configService.get('autoLoadEntities'), - synchronize: configService.get('synchronize'), - dropSchema: configService.get('dropSchema'), + autoLoadEntities: true, + synchronize: false, + dropSchema: false, }, dataSourceFactory: async (options) => { const dataSource = await new DataSource(options).initialize(); From 266a081287275c8bd7a85a984e806784cfd701af Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 21:14:12 -0700 Subject: [PATCH 28/71] business typeorm --- apps/business/src/business.module.ts | 12 ++++++------ apps/libs/gateService/index.ts | 4 ---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/apps/business/src/business.module.ts b/apps/business/src/business.module.ts index d992b45f..91e0f02f 100644 --- a/apps/business/src/business.module.ts +++ b/apps/business/src/business.module.ts @@ -116,18 +116,18 @@ export const NOTIFICATION_SCHEDULER = 'NOTIFICATION_SCHEDULER'; url: configService.get('url'), migrationsTableName: configService.get('migrationsTableName'), migrations: [`${__dirname}/../../db/migrations/*{.ts,.js}`], - autoLoadEntities: configService.get('autoLoadEntities'), - synchronize: configService.get('synchronize'), - dropSchema: configService.get('dropSchema'), + autoLoadEntities: true, + synchronize: false, + dropSchema: false, }, slaves: [ { url: configService.get('urlSlave'), migrationsTableName: configService.get('migrationsTableName'), migrations: [`${__dirname}/../../db/migrations/*{.ts,.js}`], - autoLoadEntities: configService.get('autoLoadEntities'), - synchronize: configService.get('synchronize'), - dropSchema: configService.get('dropSchema'), + autoLoadEntities: true, + synchronize: false, + dropSchema: false, }, ], }, diff --git a/apps/libs/gateService/index.ts b/apps/libs/gateService/index.ts index d68e2b64..77860c57 100644 --- a/apps/libs/gateService/index.ts +++ b/apps/libs/gateService/index.ts @@ -51,10 +51,6 @@ export class GateService { async requestHttpServiceGet(service, path, headers) { try { - console.log( - 'users login url =', - [this.services[service], path].join('/'), - ); const { data } = await lastValueFrom( this.httpService.get([this.services[service], path].join('/'), { headers, From ba4720e23cd55e0a47b471d62dc7ed667ef63963 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 17 Oct 2025 21:28:34 -0700 Subject: [PATCH 29/71] business typeorm --- apps/business/src/business.module.ts | 68 ++++++++++++++-------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/apps/business/src/business.module.ts b/apps/business/src/business.module.ts index 80e61b39..e192bf19 100644 --- a/apps/business/src/business.module.ts +++ b/apps/business/src/business.module.ts @@ -105,40 +105,40 @@ export const NOTIFICATION_SCHEDULER = 'NOTIFICATION_SCHEDULER'; }), DatabaseModule.register(), TypeOrmModule.forFeature([Payment, Subscription]), - TypeOrmModule.forRootAsync({ - imports: [ConfigModule], - inject: [ConfigService], - useFactory: (configService: ConfigService) => { - return { - type: configService.get('type').toString(), - replication: { - defaultMode: 'master', - master: { - url: configService.get('url'), - migrationsTableName: configService.get('migrationsTableName'), - migrations: [`${__dirname}/../../db/migrations/*{.ts,.js}`], - autoLoadEntities: true, - synchronize: false, - dropSchema: false, - }, - slaves: [ - { - url: configService.get('urlSlave'), - migrationsTableName: configService.get('migrationsTableName'), - migrations: [`${__dirname}/../../db/migrations/*{.ts,.js}`], - autoLoadEntities: true, - synchronize: false, - dropSchema: false, - }, - ], - }, - entities: [Payment, Subscription], - }; - }, - dataSourceFactory: async (options) => { - return await new DataSource(options).initialize(); - }, - }), + // TypeOrmModule.forRootAsync({ + // imports: [ConfigModule], + // inject: [ConfigService], + // useFactory: (configService: ConfigService) => { + // return { + // type: configService.get('type').toString(), + // replication: { + // defaultMode: 'master', + // master: { + // url: configService.get('url'), + // migrationsTableName: configService.get('migrationsTableName'), + // migrations: [`${__dirname}/../../db/migrations/*{.ts,.js}`], + // autoLoadEntities: true, + // synchronize: false, + // dropSchema: false, + // }, + // slaves: [ + // { + // url: configService.get('urlSlave'), + // migrationsTableName: configService.get('migrationsTableName'), + // migrations: [`${__dirname}/../../db/migrations/*{.ts,.js}`], + // autoLoadEntities: true, + // synchronize: false, + // dropSchema: false, + // }, + // ], + // }, + // entities: [Payment, Subscription], + // }; + // }, + // dataSourceFactory: async (options) => { + // return await new DataSource(options).initialize(); + // }, + // }), ], controllers: [BusinessController], providers: [ From bb10776e6e874561e34d3f4674ccef00f934cbf0 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Tue, 21 Oct 2025 20:55:39 -0700 Subject: [PATCH 30/71] BAN--113 fix: remove cors --- apps/gate/src/main.ts | 52 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/apps/gate/src/main.ts b/apps/gate/src/main.ts index cd628351..1db3f30d 100644 --- a/apps/gate/src/main.ts +++ b/apps/gate/src/main.ts @@ -17,33 +17,33 @@ async function bootstrap() { }); // todo! try cors origin: * and app.set('trust proxy', true); // app.set('trust proxy', true); - app.enableCors({ - allowedHeaders: [ - 'Access-Control-Allow-Origin', - 'Access-Control-Allow-Headers', - 'Content-Type', - 'Authorization', - 'Content-Length', - 'Host', - 'Accept', - 'Accept-Encoding', - 'Connection', - 'User-Agent', - 'x-recaptcha-token', - ], - exposedHeaders: ['Set-cookie'], + // app.enableCors({ + // allowedHeaders: [ + // 'Access-Control-Allow-Origin', + // 'Access-Control-Allow-Headers', + // 'Content-Type', + // 'Authorization', + // 'Content-Length', + // 'Host', + // 'Accept', + // 'Accept-Encoding', + // 'Connection', + // 'User-Agent', + // 'x-recaptcha-token', + // ], + // exposedHeaders: ['Set-cookie'], - origin: [ - 'https://yogram.ru', - 'http://localhost:5173', - 'http://localhost:56938', - 'https://localhost:3000', - 'http://localhost:3000', - 'http://localhost', - '*', - ], - credentials: true, - }); + // origin: [ + // 'https://yogram.ru', + // 'http://localhost:5173', + // 'http://localhost:56938', + // 'https://localhost:3000', + // 'http://localhost:3000', + // 'http://localhost', + // '*', + // ], + // credentials: true, + // }); const { port, env } = applyAppSettings(app); From 82c6cc6f85926486a5649a12bd4b6b577dd0df78 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 22 Oct 2025 10:23:33 -0700 Subject: [PATCH 31/71] BAN-113 cors * --- apps/gate/src/main.ts | 53 ++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/apps/gate/src/main.ts b/apps/gate/src/main.ts index 1db3f30d..ae89fbea 100644 --- a/apps/gate/src/main.ts +++ b/apps/gate/src/main.ts @@ -17,33 +17,34 @@ async function bootstrap() { }); // todo! try cors origin: * and app.set('trust proxy', true); // app.set('trust proxy', true); - // app.enableCors({ - // allowedHeaders: [ - // 'Access-Control-Allow-Origin', - // 'Access-Control-Allow-Headers', - // 'Content-Type', - // 'Authorization', - // 'Content-Length', - // 'Host', - // 'Accept', - // 'Accept-Encoding', - // 'Connection', - // 'User-Agent', - // 'x-recaptcha-token', - // ], - // exposedHeaders: ['Set-cookie'], + app.enableCors({ + allowedHeaders: [ + // 'Access-Control-Allow-Origin', + // 'Access-Control-Allow-Headers', + // 'Content-Type', + // 'Authorization', + // 'Content-Length', + // 'Host', + // 'Accept', + // 'Accept-Encoding', + // 'Connection', + // 'User-Agent', + // 'x-recaptcha-token', + '*', + ], + exposedHeaders: ['Set-cookie'], - // origin: [ - // 'https://yogram.ru', - // 'http://localhost:5173', - // 'http://localhost:56938', - // 'https://localhost:3000', - // 'http://localhost:3000', - // 'http://localhost', - // '*', - // ], - // credentials: true, - // }); + origin: [ + // 'https://yogram.ru', + // 'http://localhost:5173', + // 'http://localhost:56938', + // 'https://localhost:3000', + // 'http://localhost:3000', + // 'http://localhost', + '*', + ], + credentials: true, + }); const { port, env } = applyAppSettings(app); From 8aeb7801f78bef552712707685eab29c2594dc8e Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 22 Oct 2025 19:29:49 -0700 Subject: [PATCH 32/71] return cors --- apps/gate/src/main.ts | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/apps/gate/src/main.ts b/apps/gate/src/main.ts index ae89fbea..cd628351 100644 --- a/apps/gate/src/main.ts +++ b/apps/gate/src/main.ts @@ -19,28 +19,27 @@ async function bootstrap() { // app.set('trust proxy', true); app.enableCors({ allowedHeaders: [ - // 'Access-Control-Allow-Origin', - // 'Access-Control-Allow-Headers', - // 'Content-Type', - // 'Authorization', - // 'Content-Length', - // 'Host', - // 'Accept', - // 'Accept-Encoding', - // 'Connection', - // 'User-Agent', - // 'x-recaptcha-token', - '*', + 'Access-Control-Allow-Origin', + 'Access-Control-Allow-Headers', + 'Content-Type', + 'Authorization', + 'Content-Length', + 'Host', + 'Accept', + 'Accept-Encoding', + 'Connection', + 'User-Agent', + 'x-recaptcha-token', ], exposedHeaders: ['Set-cookie'], origin: [ - // 'https://yogram.ru', - // 'http://localhost:5173', - // 'http://localhost:56938', - // 'https://localhost:3000', - // 'http://localhost:3000', - // 'http://localhost', + 'https://yogram.ru', + 'http://localhost:5173', + 'http://localhost:56938', + 'https://localhost:3000', + 'http://localhost:3000', + 'http://localhost', '*', ], credentials: true, From e52025637936a0eee361e369763c6fe6ebfc6725 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 22 Oct 2025 19:55:27 -0700 Subject: [PATCH 33/71] + * headers --- apps/gate/src/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/gate/src/main.ts b/apps/gate/src/main.ts index cd628351..e437acc2 100644 --- a/apps/gate/src/main.ts +++ b/apps/gate/src/main.ts @@ -30,6 +30,7 @@ async function bootstrap() { 'Connection', 'User-Agent', 'x-recaptcha-token', + '*', ], exposedHeaders: ['Set-cookie'], From 5423827e5fd21610c4d760fbed3bd2f646fc3597 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 23 Oct 2025 17:36:53 -0700 Subject: [PATCH 34/71] return link when subscribe --- .../swagger/subscribe-swagger.decorator.ts | 14 ++++++++++++-- apps/gate/src/business/business.controller.ts | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/business/src/decorators/swagger/subscribe-swagger.decorator.ts b/apps/business/src/decorators/swagger/subscribe-swagger.decorator.ts index 2323c756..88ee7a90 100644 --- a/apps/business/src/decorators/swagger/subscribe-swagger.decorator.ts +++ b/apps/business/src/decorators/swagger/subscribe-swagger.decorator.ts @@ -8,6 +8,15 @@ import { import { applyDecorators, HttpStatus } from '@nestjs/common'; import { SubscribeDto } from '../../../../../apps/libs/Business/dto/input/subscribe.dto'; import { PaymentType } from '../../../../../apps/libs/Business/constants/payment-type.enum'; +import { IsString } from 'class-validator'; +import { Expose } from 'class-transformer'; + +@Expose() +class SubscribeResponseDto { + @Expose() + @IsString() + link: string; +} export const SubscribeSwagger = () => applyDecorators( @@ -29,8 +38,9 @@ export const SubscribeSwagger = () => }), ApiBody({ type: SubscribeDto }), ApiResponse({ - status: HttpStatus.OK, - description: 'Success and redirected', + status: HttpStatus.TEMPORARY_REDIRECT, + type: SubscribeResponseDto, + description: 'Need to open this link in browser', }), ApiResponse({ status: HttpStatus.BAD_REQUEST, diff --git a/apps/gate/src/business/business.controller.ts b/apps/gate/src/business/business.controller.ts index 496ab5f5..47b7e59f 100644 --- a/apps/gate/src/business/business.controller.ts +++ b/apps/gate/src/business/business.controller.ts @@ -117,7 +117,8 @@ export class BusinessController { payment, ); console.log('link:', response.link); - res.status(200).redirect(303, response.link); + // res.status(200).redirect(303, response.link); + return res.status(307).json({ link: response.link }); } @Public() From 5af9a5f5bd77c7d11aa9a9e237d06a209ea0d895 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 23 Oct 2025 20:35:35 -0700 Subject: [PATCH 35/71] add paypal cors --- apps/gate/src/main.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/gate/src/main.ts b/apps/gate/src/main.ts index e437acc2..8657ed8a 100644 --- a/apps/gate/src/main.ts +++ b/apps/gate/src/main.ts @@ -35,6 +35,8 @@ async function bootstrap() { exposedHeaders: ['Set-cookie'], origin: [ + 'https://developer.paypal.com', + 'https://www.sandbox.paypal.com', 'https://yogram.ru', 'http://localhost:5173', 'http://localhost:56938', From 7f2e9013a66430b46dbe07ca6fae7f0da4b48226 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 24 Oct 2025 17:20:50 -0700 Subject: [PATCH 36/71] BAN-113 redirect to the front at the end of subscription process --- apps/business/deployment.yaml | 5 +++++ apps/business/src/api/business.controller.ts | 12 +++++++++++- apps/business/src/settings/configuration.ts | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/business/deployment.yaml b/apps/business/deployment.yaml index 2f5a6eea..58292cff 100644 --- a/apps/business/deployment.yaml +++ b/apps/business/deployment.yaml @@ -72,6 +72,11 @@ spec: secretKeyRef: name: yogram-business-production-config-secret key: POSTGRES_URL + - name: PROFILE_SETTINGS_PAGE + valueFrom: + secretKeyRef: + name: yogram-business-production-config-secret + key: PROFILE_SETTINGS_PAGE - name: PAYPAL_CLIENT_ID valueFrom: secretKeyRef: diff --git a/apps/business/src/api/business.controller.ts b/apps/business/src/api/business.controller.ts index 0c02d205..05556732 100644 --- a/apps/business/src/api/business.controller.ts +++ b/apps/business/src/api/business.controller.ts @@ -21,6 +21,7 @@ import { Param, Patch, Post, + Res, Sse, } from '@nestjs/common'; import { @@ -38,6 +39,8 @@ import { import { NotificationResponseDto } from 'apps/libs/Business/dto/response/response-notification.dto'; import { ReadNotificationCommand } from '../application/command/read-notification.command'; import { ReadNotificationDto } from '../../../../apps/libs/Business/dto/input/read-notification.dto'; +import { Response } from 'express'; +import { ConfigService } from '@nestjs/config'; @Controller() export class BusinessController { @@ -45,6 +48,7 @@ export class BusinessController { constructor( private readonly commandBus: CommandBus, private readonly queryBus: QueryBus, + private readonly configService: ConfigService, ) { this.eventEmitter = new EventEmitter2(); } @@ -57,8 +61,14 @@ export class BusinessController { @Post('business/paypal-proccess') async paypalProcess( @Body('subscriptionId') subscriptionId: string, + @Res() res: Response, ): Promise { - await this.commandBus.execute(new SaveSubscriptionCommand(subscriptionId)); + const subscription = await this.commandBus.execute( + new SaveSubscriptionCommand(subscriptionId), + ); + let page = this.configService.get('PROFILE_SETTINGS_PAGE'); + page = page.replace('replace', subscription.userId); + res.redirect(301, page); } @HttpCode(200) diff --git a/apps/business/src/settings/configuration.ts b/apps/business/src/settings/configuration.ts index e16035b1..64cc238e 100644 --- a/apps/business/src/settings/configuration.ts +++ b/apps/business/src/settings/configuration.ts @@ -30,5 +30,6 @@ export const getConfiguration = () => { dropSchema: process.env.DROP_SCHEMA === 'true', JWT_SECRET: process.env.JWT_SECRET, TIME_PERIOD: process.env.TIME_PERIOD, + PROFILE_SETTINGS_PAGE: process.env.PROFILE_SETTINGS_PAGE, }; }; From 434c31656615aea4eaf3a326a0f1b14e8b2fb9f7 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 24 Oct 2025 17:28:59 -0700 Subject: [PATCH 37/71] BAN-113 log --- apps/business/src/api/business.controller.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/business/src/api/business.controller.ts b/apps/business/src/api/business.controller.ts index 05556732..110f2e15 100644 --- a/apps/business/src/api/business.controller.ts +++ b/apps/business/src/api/business.controller.ts @@ -68,6 +68,7 @@ export class BusinessController { ); let page = this.configService.get('PROFILE_SETTINGS_PAGE'); page = page.replace('replace', subscription.userId); + console.log('🚀 ~ BusinessController ~ paypalProcess ~ page:', page); res.redirect(301, page); } From aa62862f93a92e3f6e66a965ba121ae29738e60a Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 24 Oct 2025 17:52:27 -0700 Subject: [PATCH 38/71] BAN-113 comment notif --- .../command/save-subscribtion.handler.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/apps/business/src/application/command/save-subscribtion.handler.ts b/apps/business/src/application/command/save-subscribtion.handler.ts index cbf4d5c7..63742c54 100644 --- a/apps/business/src/application/command/save-subscribtion.handler.ts +++ b/apps/business/src/application/command/save-subscribtion.handler.ts @@ -32,16 +32,17 @@ export class SaveSubscriptionHandler }; const key = getNotificationKey(subscription, notification); - await this.notificationGateway.saveNotification( - key, - notification, - 2629746000, - ); - await this.notificationGateway.send( - notification, - WebsocketEvents.SubscriptionActive, - 30000, - ); + // todo uncomment notif + // await this.notificationGateway.saveNotification( + // key, + // notification, + // 2629746000, + // ); + // await this.notificationGateway.send( + // notification, + // WebsocketEvents.SubscriptionActive, + // 30000, + // ); return subscription; } } From e4414d57f7c5d09a30222ede82e2e66d56f754f5 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Sun, 26 Oct 2025 19:09:40 -0700 Subject: [PATCH 39/71] BAN-113 google redirect --- apps/business/src/api/business.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/business/src/api/business.controller.ts b/apps/business/src/api/business.controller.ts index 110f2e15..b6a609b1 100644 --- a/apps/business/src/api/business.controller.ts +++ b/apps/business/src/api/business.controller.ts @@ -69,7 +69,7 @@ export class BusinessController { let page = this.configService.get('PROFILE_SETTINGS_PAGE'); page = page.replace('replace', subscription.userId); console.log('🚀 ~ BusinessController ~ paypalProcess ~ page:', page); - res.redirect(301, page); + res.redirect('https://www.google.com/'); } @HttpCode(200) From 6e2e0a868ef0baeafe537d3558fa5e0dd4fbf1c0 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Sun, 26 Oct 2025 21:24:12 -0700 Subject: [PATCH 40/71] BAN-113 user cant have more than 2 not expired subscriptions simultaniously off --- apps/business/src/business-command.service.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/business/src/business-command.service.ts b/apps/business/src/business-command.service.ts index 74f39fad..9412a582 100644 --- a/apps/business/src/business-command.service.ts +++ b/apps/business/src/business-command.service.ts @@ -36,11 +36,11 @@ export class BusinessCommandService { subscribeDto.userId, ); - if (currentSubscriptions.length > 1) { - throw new BadRequestException( - 'BusinessCommandService error: user cant have more than 2 not expired subscriptions simultaniously', - ); - } + // if (currentSubscriptions.length > 1) { + // throw new BadRequestException( + // 'BusinessCommandService error: user cant have more than 2 not expired subscriptions simultaniously', + // ); + // } currentSubscriptions?.map((subscription) => { if ( From 9727632623bda3053b8e4c88398854eacea13808 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Sun, 26 Oct 2025 21:56:55 -0700 Subject: [PATCH 41/71] BAN-113 business redirect gate --- apps/business/src/api/business.controller.ts | 10 +++++----- apps/gate/deployment.yaml | 6 ++++++ apps/gate/src/business/business.controller.ts | 11 ++++++++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/apps/business/src/api/business.controller.ts b/apps/business/src/api/business.controller.ts index b6a609b1..280a22ab 100644 --- a/apps/business/src/api/business.controller.ts +++ b/apps/business/src/api/business.controller.ts @@ -63,13 +63,13 @@ export class BusinessController { @Body('subscriptionId') subscriptionId: string, @Res() res: Response, ): Promise { - const subscription = await this.commandBus.execute( + return await this.commandBus.execute( new SaveSubscriptionCommand(subscriptionId), ); - let page = this.configService.get('PROFILE_SETTINGS_PAGE'); - page = page.replace('replace', subscription.userId); - console.log('🚀 ~ BusinessController ~ paypalProcess ~ page:', page); - res.redirect('https://www.google.com/'); + // let page = this.configService.get('PROFILE_SETTINGS_PAGE'); + // page = page.replace('replace', subscription.userId); + // console.log('🚀 ~ BusinessController ~ paypalProcess ~ page:', page); + // res.redirect(301, 'https://www.google.com/'); } @HttpCode(200) diff --git a/apps/gate/deployment.yaml b/apps/gate/deployment.yaml index fa63ec19..3bdf82b8 100644 --- a/apps/gate/deployment.yaml +++ b/apps/gate/deployment.yaml @@ -20,6 +20,7 @@ spec: image: REGISTRY_HOSTNAME/PROJECT:TAG_VERSION ports: - containerPort: PORT_CONTAINER + env: - name: JWT_SECRET valueFrom: @@ -171,6 +172,11 @@ spec: secretKeyRef: name: yogram-production-config-secret key: USERS_PROD_SERVICE_URL + - name: PROFILE_SETTINGS_PAGE + valueFrom: + secretKeyRef: + name: yogram-production-config-secret + key: PROFILE_SETTINGS_PAGE - name: NODE_ENV valueFrom: secretKeyRef: diff --git a/apps/gate/src/business/business.controller.ts b/apps/gate/src/business/business.controller.ts index 47b7e59f..8ce5418b 100644 --- a/apps/gate/src/business/business.controller.ts +++ b/apps/gate/src/business/business.controller.ts @@ -127,12 +127,21 @@ export class BusinessController { async paypalProcess( @Req() req: Request, @Query('payment') payment: PaymentType, + @Res() res: Response, ): Promise { if (req.body.event_type === PaypalEvents.BillingSubscriptionActivated) { - return await this.businessService.paypalProccess( + const subscription = await this.businessService.paypalProccess( req.body.resource.id, payment, ); + console.log( + '🚀 ~ BusinessController ~ paypalProcess ~ subscription:', + subscription['userId'], + ); + let page = this.configService.get('PROFILE_SETTINGS_PAGE'); + page = page.replace('replace', subscription['userId']); + console.log('🚀 ~ BusinessController ~ paypalProcess ~ page:', page); + res.redirect(301, page); } } From 2a5f320b7b95a4f7f43632ada9a95cedafa4725a Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Mon, 27 Oct 2025 20:13:48 -0700 Subject: [PATCH 42/71] BAN-113 feat: cancel subscription --- apps/business/src/api/business.controller.ts | 7 ++ .../command/cancel-subscription.handler.ts | 19 ++++ apps/business/src/business-command.service.ts | 80 +++++++++++++++-- apps/business/src/business-query.service.ts | 3 +- apps/business/src/business.module.ts | 6 ++ .../src/db/1761610470647-business_dev.ts | 11 +++ .../src/db/1761610624210-business_dev.ts | 11 +++ .../src/db/1761610763416-business_dev.ts | 11 +++ .../migrations/1761610631993-business_dev.ts | 86 +++++++++++++++++++ .../migrations/1761610767438-business_dev.ts | 86 +++++++++++++++++++ .../interfaces/payment-service.interface.ts | 2 + .../payment-services/paypal/paypal.service.ts | 30 +++++++ apps/gate/src/business/business.controller.ts | 10 +++ apps/gate/src/business/business.service.ts | 13 +++ ...activate-subscription-swagger.decorator.ts | 2 +- .../swagger/cancel-subscription.swagger.ts | 44 ++++++++++ apps/libs/Business/constants/path.constant.ts | 1 + ...nitTable.ts => 1761615094093-users_dev.ts} | 2 +- ...nitTable.ts => 1761615103355-users_dev.ts} | 4 +- 19 files changed, 418 insertions(+), 10 deletions(-) create mode 100644 apps/business/src/application/command/cancel-subscription.handler.ts create mode 100644 apps/business/src/db/1761610470647-business_dev.ts create mode 100644 apps/business/src/db/1761610624210-business_dev.ts create mode 100644 apps/business/src/db/1761610763416-business_dev.ts create mode 100644 apps/business/src/db/migrations/1761610631993-business_dev.ts create mode 100644 apps/business/src/db/migrations/1761610767438-business_dev.ts create mode 100644 apps/gate/src/business/decorators/swagger/cancel-subscription.swagger.ts rename apps/users/src/db/{1759504948061-InitTable.ts => 1761615094093-users_dev.ts} (75%) rename apps/users/src/db/migrations/{1759504952602-InitTable.ts => 1761615103355-users_dev.ts} (97%) diff --git a/apps/business/src/api/business.controller.ts b/apps/business/src/api/business.controller.ts index 280a22ab..04ddcfe7 100644 --- a/apps/business/src/api/business.controller.ts +++ b/apps/business/src/api/business.controller.ts @@ -41,6 +41,7 @@ import { ReadNotificationCommand } from '../application/command/read-notificatio import { ReadNotificationDto } from '../../../../apps/libs/Business/dto/input/read-notification.dto'; import { Response } from 'express'; import { ConfigService } from '@nestjs/config'; +import { CancelSubscriptionCommand } from '../application/command/cancel-subscription.handler'; @Controller() export class BusinessController { @@ -124,6 +125,12 @@ export class BusinessController { return await this.commandBus.execute(new ActivateSubscriptionCommand(id)); } + @Patch('business/subscriptions/:id/cancel') + async cancelSubscription(@Param('id') id: string): Promise { + console.log('🚀 ~ BusinessController ~ cancelSubscription ~ id:', id); + return await this.commandBus.execute(new CancelSubscriptionCommand(id)); + } + @Get('business/subscriptions/get/:id') async getCurrentSubscriptions( @Param('id') userId: string, diff --git a/apps/business/src/application/command/cancel-subscription.handler.ts b/apps/business/src/application/command/cancel-subscription.handler.ts new file mode 100644 index 00000000..263444ab --- /dev/null +++ b/apps/business/src/application/command/cancel-subscription.handler.ts @@ -0,0 +1,19 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { BusinessCommandService } from '../../business-command.service'; + +export class CancelSubscriptionCommand { + constructor(public readonly id: string) {} +} + +@CommandHandler(CancelSubscriptionCommand) +export class CancelSubscriptionHandler + implements ICommandHandler +{ + constructor( + private readonly businessCommandService: BusinessCommandService, + ) {} + + async execute({ id }: CancelSubscriptionCommand): Promise { + return await this.businessCommandService.cancelSubscription(id); + } +} diff --git a/apps/business/src/business-command.service.ts b/apps/business/src/business-command.service.ts index 9412a582..12231636 100644 --- a/apps/business/src/business-command.service.ts +++ b/apps/business/src/business-command.service.ts @@ -1,5 +1,6 @@ import { BadRequestException, + ConflictException, HttpException, Injectable, NotFoundException, @@ -36,11 +37,11 @@ export class BusinessCommandService { subscribeDto.userId, ); - // if (currentSubscriptions.length > 1) { - // throw new BadRequestException( - // 'BusinessCommandService error: user cant have more than 2 not expired subscriptions simultaniously', - // ); - // } + if (currentSubscriptions.length > 1) { + throw new BadRequestException( + 'BusinessCommandService error: user cant have more than 2 not expired subscriptions simultaniously', + ); + } currentSubscriptions?.map((subscription) => { if ( @@ -57,6 +58,10 @@ export class BusinessCommandService { currentSubscriptions.length === 1 ? new Date(currentSubscriptions[0].expiresAt) : new Date(); + console.log( + '🚀 ~ BusinessCommandService ~ subscribe ~ start_date:', + start_date, + ); const response = await this.paymentService.subscribeToPlan( subscribeDto.subscriptionType, @@ -177,6 +182,11 @@ export class BusinessCommandService { throw new NotFoundException( 'BusinessCommandService error: subscription does not exist', ); + if (subscription.status === SubscriptionStatus.Canceled) { + throw new ConflictException( + 'BusinessCommandService error: subscription was canceled', + ); + } const currentSubscriptions = await this.businessQueryService.getCurrentUserSubscriptions( subscription.userId, @@ -233,6 +243,66 @@ export class BusinessCommandService { ); } + async cancelSubscription(id: string): Promise { + console.log('🚀 ~ BusinessCommandService ~ cancelSubscription ~ id:', id); + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction('READ COMMITTED'); + try { + const subscription = await this.businessQueryService.getSubscription( + id, + queryRunner.manager, + ); + if (!subscription) + throw new NotFoundException( + 'BusinessCommandService error: subscription does not exist', + ); + const currentSubscriptions = + await this.businessQueryService.getCurrentUserSubscriptions( + subscription.userId, + queryRunner.manager, + ); + const paypalSubscription = await this.paymentService.getSubscription(id); + console.log( + '🚀 ~ BusinessCommandService ~ cancelSubscription ~ paypalSubscription:', + paypalSubscription, + ); + if (!paypalSubscription) + throw new NotFoundException( + 'BusinessCommandService error: paypal subscription does not exist', + ); + await this.paymentService.cancelSubscription(id); + + subscription.status = SubscriptionStatus.Canceled; + const updatedSubscription = + await this.businessCommandRepository.saveSubscription( + subscription, + queryRunner.manager, + ); + // todo! problem if second is suspended + // if (currentSubscriptions.length === 2) { + // const anotherSubscription: Subscription[] = currentSubscriptions.filter( + // (subscr) => subscr.id !== subscription.id, + // ); + // if (anotherSubscription[0].status === SubscriptionStatus.Suspended) { + // await this.activateSubscription( + // anotherSubscription[0].subscriptionId, + // ); + // } + // } + await queryRunner.commitTransaction(); + } catch (err) { + console.log('BusinessCommandService cancelSubscription ~ err:', err); + await queryRunner.rollbackTransaction(); + throw new HttpException( + err.response, + err.response.httpStatusCode || err.httpStatusCode, + ); + } finally { + await queryRunner.release(); + } + } + async updateSubscription( id: string, subscriptionUpdateDto: SubscriptionUpdateDto, diff --git a/apps/business/src/business-query.service.ts b/apps/business/src/business-query.service.ts index 7575907a..68268666 100644 --- a/apps/business/src/business-query.service.ts +++ b/apps/business/src/business-query.service.ts @@ -80,7 +80,8 @@ export class BusinessQueryService { if ( subscription.expiresAt && new Date(subscription.expiresAt) > new Date() && - subscription.status !== SubscriptionStatus.Approval_Pending + subscription.status !== SubscriptionStatus.Approval_Pending && + subscription.status !== SubscriptionStatus.Canceled ) { if (subscription.status === SubscriptionStatus.Active) { subscription['nextPayment'] = subscription.expiresAt; diff --git a/apps/business/src/business.module.ts b/apps/business/src/business.module.ts index e192bf19..0ab0b25e 100644 --- a/apps/business/src/business.module.ts +++ b/apps/business/src/business.module.ts @@ -64,6 +64,10 @@ import { BullModule } from '@nestjs/bullmq'; import { NotificationsProducer } from './notifications-producer.service'; import { NotificationConsumer } from './notifications-consumer.service'; import { DatabaseModule } from '../../../apps/libs/common/database/database.module'; +import { + CancelSubscriptionCommand, + CancelSubscriptionHandler, +} from './application/command/cancel-subscription.handler'; const getEnvFilePath = (env: EnvironmentsTypes) => { const defaultEnvFilePath = ['apps/business/src/.env.development']; @@ -154,6 +158,8 @@ export const NOTIFICATION_SCHEDULER = 'NOTIFICATION_SCHEDULER'; SuspendSubscriptionHandler, ActivateSubscriptionHandler, ActivateSubscriptionCommand, + CancelSubscriptionCommand, + CancelSubscriptionHandler, SubscriptionUpdatedCommand, SubscriptionUpdatedHandler, SubscriptionExpiredCommand, diff --git a/apps/business/src/db/1761610470647-business_dev.ts b/apps/business/src/db/1761610470647-business_dev.ts new file mode 100644 index 00000000..c653d5fa --- /dev/null +++ b/apps/business/src/db/1761610470647-business_dev.ts @@ -0,0 +1,11 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class BusinessDev1761610470647 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + } + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/apps/business/src/db/1761610624210-business_dev.ts b/apps/business/src/db/1761610624210-business_dev.ts new file mode 100644 index 00000000..5fab5538 --- /dev/null +++ b/apps/business/src/db/1761610624210-business_dev.ts @@ -0,0 +1,11 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class BusinessDev1761610624210 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + } + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/apps/business/src/db/1761610763416-business_dev.ts b/apps/business/src/db/1761610763416-business_dev.ts new file mode 100644 index 00000000..5dba4d88 --- /dev/null +++ b/apps/business/src/db/1761610763416-business_dev.ts @@ -0,0 +1,11 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class BusinessDev1761610763416 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + } + + public async down(queryRunner: QueryRunner): Promise { + } + +} diff --git a/apps/business/src/db/migrations/1761610631993-business_dev.ts b/apps/business/src/db/migrations/1761610631993-business_dev.ts new file mode 100644 index 00000000..1826c2f4 --- /dev/null +++ b/apps/business/src/db/migrations/1761610631993-business_dev.ts @@ -0,0 +1,86 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class BusinessDev1761610631993 implements MigrationInterface { + name = 'BusinessDev1761610631993' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TYPE "public"."payments_paymenttype_enum" AS ENUM('paypal', 'stripe') + `); + await queryRunner.query(` + CREATE TABLE "payments" ( + "id" uuid NOT NULL DEFAULT uuid_generate_v4(), + "createdAt" date NOT NULL DEFAULT now(), + "updatedAt" date NOT NULL DEFAULT now(), + "deletedAt" date, + "userId" uuid NOT NULL, + "paymentType" "public"."payments_paymenttype_enum" NOT NULL, + "price" integer NOT NULL, + "subscriptionId" uuid, + CONSTRAINT "PK_197ab7af18c93fbb0c9b28b4a59" PRIMARY KEY ("id") + ) + `); + await queryRunner.query(` + CREATE TYPE "public"."subscriptions_status_enum" AS ENUM( + 'ACTIVE', + 'CANCELED', + 'INACTIVE', + 'SUSPENDED', + 'APPROVAL_PENDING' + ) + `); + await queryRunner.query(` + CREATE TYPE "public"."subscriptions_subscriptiontype_enum" AS ENUM('1', '7', '30') + `); + await queryRunner.query(` + CREATE TYPE "public"."subscriptions_paymenttype_enum" AS ENUM('paypal', 'stripe') + `); + await queryRunner.query(` + CREATE TABLE "subscriptions" ( + "id" uuid NOT NULL DEFAULT uuid_generate_v4(), + "createdAt" date NOT NULL DEFAULT now(), + "updatedAt" date NOT NULL DEFAULT now(), + "deletedAt" date, + "subscriptionId" character varying NOT NULL, + "paymentId" uuid, + "userId" uuid NOT NULL, + "status" "public"."subscriptions_status_enum" NOT NULL, + "subscriptionType" "public"."subscriptions_subscriptiontype_enum", + "paymentType" "public"."subscriptions_paymenttype_enum" NOT NULL, + "startAt" date, + "expiresAt" date, + CONSTRAINT "PK_a87248d73155605cf782be9ee5e" PRIMARY KEY ("id") + ) + `); + await queryRunner.query(` + ALTER TABLE "payments" + ADD CONSTRAINT "FK_2017d0cbfdbfec6b1b388e6aa08" FOREIGN KEY ("subscriptionId") REFERENCES "subscriptions"("id") ON DELETE + SET NULL ON UPDATE NO ACTION + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "payments" DROP CONSTRAINT "FK_2017d0cbfdbfec6b1b388e6aa08" + `); + await queryRunner.query(` + DROP TABLE "subscriptions" + `); + await queryRunner.query(` + DROP TYPE "public"."subscriptions_paymenttype_enum" + `); + await queryRunner.query(` + DROP TYPE "public"."subscriptions_subscriptiontype_enum" + `); + await queryRunner.query(` + DROP TYPE "public"."subscriptions_status_enum" + `); + await queryRunner.query(` + DROP TABLE "payments" + `); + await queryRunner.query(` + DROP TYPE "public"."payments_paymenttype_enum" + `); + } + +} diff --git a/apps/business/src/db/migrations/1761610767438-business_dev.ts b/apps/business/src/db/migrations/1761610767438-business_dev.ts new file mode 100644 index 00000000..e049e37d --- /dev/null +++ b/apps/business/src/db/migrations/1761610767438-business_dev.ts @@ -0,0 +1,86 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class BusinessDev1761610767438 implements MigrationInterface { + name = 'BusinessDev1761610767438' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE TYPE "public"."payments_paymenttype_enum" AS ENUM('paypal', 'stripe') + `); + await queryRunner.query(` + CREATE TABLE "payments" ( + "id" uuid NOT NULL DEFAULT uuid_generate_v4(), + "createdAt" date NOT NULL DEFAULT now(), + "updatedAt" date NOT NULL DEFAULT now(), + "deletedAt" date, + "userId" uuid NOT NULL, + "paymentType" "public"."payments_paymenttype_enum" NOT NULL, + "price" integer NOT NULL, + "subscriptionId" uuid, + CONSTRAINT "PK_197ab7af18c93fbb0c9b28b4a59" PRIMARY KEY ("id") + ) + `); + await queryRunner.query(` + CREATE TYPE "public"."subscriptions_status_enum" AS ENUM( + 'ACTIVE', + 'CANCELED', + 'INACTIVE', + 'SUSPENDED', + 'APPROVAL_PENDING' + ) + `); + await queryRunner.query(` + CREATE TYPE "public"."subscriptions_subscriptiontype_enum" AS ENUM('1', '7', '30') + `); + await queryRunner.query(` + CREATE TYPE "public"."subscriptions_paymenttype_enum" AS ENUM('paypal', 'stripe') + `); + await queryRunner.query(` + CREATE TABLE "subscriptions" ( + "id" uuid NOT NULL DEFAULT uuid_generate_v4(), + "createdAt" date NOT NULL DEFAULT now(), + "updatedAt" date NOT NULL DEFAULT now(), + "deletedAt" date, + "subscriptionId" character varying NOT NULL, + "paymentId" uuid, + "userId" uuid NOT NULL, + "status" "public"."subscriptions_status_enum" NOT NULL, + "subscriptionType" "public"."subscriptions_subscriptiontype_enum", + "paymentType" "public"."subscriptions_paymenttype_enum" NOT NULL, + "startAt" date, + "expiresAt" date, + CONSTRAINT "PK_a87248d73155605cf782be9ee5e" PRIMARY KEY ("id") + ) + `); + await queryRunner.query(` + ALTER TABLE "payments" + ADD CONSTRAINT "FK_2017d0cbfdbfec6b1b388e6aa08" FOREIGN KEY ("subscriptionId") REFERENCES "subscriptions"("id") ON DELETE + SET NULL ON UPDATE NO ACTION + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "payments" DROP CONSTRAINT "FK_2017d0cbfdbfec6b1b388e6aa08" + `); + await queryRunner.query(` + DROP TABLE "subscriptions" + `); + await queryRunner.query(` + DROP TYPE "public"."subscriptions_paymenttype_enum" + `); + await queryRunner.query(` + DROP TYPE "public"."subscriptions_subscriptiontype_enum" + `); + await queryRunner.query(` + DROP TYPE "public"."subscriptions_status_enum" + `); + await queryRunner.query(` + DROP TABLE "payments" + `); + await queryRunner.query(` + DROP TYPE "public"."payments_paymenttype_enum" + `); + } + +} diff --git a/apps/business/src/payment/interfaces/payment-service.interface.ts b/apps/business/src/payment/interfaces/payment-service.interface.ts index 05ec3d56..c526b263 100644 --- a/apps/business/src/payment/interfaces/payment-service.interface.ts +++ b/apps/business/src/payment/interfaces/payment-service.interface.ts @@ -30,4 +30,6 @@ export abstract class IPaymentService { abstract suspendSubscription(id: string): Promise; abstract activateSubscription(id: string): Promise; + + abstract cancelSubscription(id: string): Promise; } diff --git a/apps/business/src/payment/payment-services/paypal/paypal.service.ts b/apps/business/src/payment/payment-services/paypal/paypal.service.ts index d08e3e32..2a94712a 100644 --- a/apps/business/src/payment/payment-services/paypal/paypal.service.ts +++ b/apps/business/src/payment/payment-services/paypal/paypal.service.ts @@ -311,6 +311,36 @@ export class PayPalService implements IPaymentService { } } + async cancelSubscription(id: string): Promise { + const token = await this.authentication(); + const status = (await this.getSubscription(id)).status; + console.log('🚀 ~ PayPalService ~ cancelSubscription ~ status:', status); + if (status === 'CANCELLED') + throw new ConflictException( + 'PayPalService error: subscription is canceled already', + ); + + try { + return await axios.post( + `https://api-m.sandbox.paypal.com/v1/billing/subscriptions/${id}/cancel`, + { reason: 'Activate' }, + { + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }, + ); + } catch (err) { + console.log( + '🚀 ~ PayPalService ~ activateSubscription ~ err:', + err.response.data.details, + ); + throw new HttpException(err.response.data, err.response.status); + } + } + async getSubscription(id: string): Promise { const token = await this.authentication(); return ( diff --git a/apps/gate/src/business/business.controller.ts b/apps/gate/src/business/business.controller.ts index 8ce5418b..4271036c 100644 --- a/apps/gate/src/business/business.controller.ts +++ b/apps/gate/src/business/business.controller.ts @@ -42,6 +42,7 @@ import { Request, Response } from 'express'; import axios from 'axios'; import { ReadNotificationDto } from '../../../../apps/libs/Business/dto/input/read-notification.dto'; import { ReadNotificationSwagger } from './decorators/swagger/read-notification.swagger.decorator'; +import { CancelSubscriptionSwagger } from './decorators/swagger/cancel-subscription.swagger'; @Controller('business') export class BusinessController { @@ -201,6 +202,15 @@ export class BusinessController { return await this.businessService.activateSubscription(id, payment); } + @CancelSubscriptionSwagger() + @Patch('subscriptions/:id/cancel') + async cancelSubscription( + @Param('id') id: string, + @Query('payment') payment: PaymentType, + ): Promise { + return await this.businessService.cancelSubscription(id, payment); + } + @GetPaymentsSwagger() @Get('payments') async getPayments( diff --git a/apps/gate/src/business/business.service.ts b/apps/gate/src/business/business.service.ts index ffdff6f7..26087cb4 100644 --- a/apps/gate/src/business/business.service.ts +++ b/apps/gate/src/business/business.service.ts @@ -126,6 +126,19 @@ export class BusinessService { ); } + async cancelSubscription(id: string, payment: PaymentType): Promise { + const path = [ + [HttpBusinessPath.CancelSubscription.replace(':id', id)].join('/'), + `payment=${payment}`, + ].join('?'); + return await this.gateService.requestHttpServicePatch( + HttpServices.Business, + path, + {}, + {}, + ); + } + async getPayments( payment: PaymentType, pagination: IPagination, diff --git a/apps/gate/src/business/decorators/swagger/activate-subscription-swagger.decorator.ts b/apps/gate/src/business/decorators/swagger/activate-subscription-swagger.decorator.ts index 3db64839..e36c1eef 100644 --- a/apps/gate/src/business/decorators/swagger/activate-subscription-swagger.decorator.ts +++ b/apps/gate/src/business/decorators/swagger/activate-subscription-swagger.decorator.ts @@ -39,7 +39,7 @@ export function ActivateSubscriptionSwagger() { }), ApiResponse({ status: 409, - description: 'PayPalService error: subscription is active already', + description: 'PayPalService error: subscription is active already ', }), ); } diff --git a/apps/gate/src/business/decorators/swagger/cancel-subscription.swagger.ts b/apps/gate/src/business/decorators/swagger/cancel-subscription.swagger.ts new file mode 100644 index 00000000..6509f89b --- /dev/null +++ b/apps/gate/src/business/decorators/swagger/cancel-subscription.swagger.ts @@ -0,0 +1,44 @@ +import { + ApiHeader, + ApiOperation, + ApiParam, + ApiQuery, + ApiResponse, +} from '@nestjs/swagger'; +import { applyDecorators } from '@nestjs/common'; +import { PaymentType } from '../../../../../../apps/libs/Business/constants/payment-type.enum'; + +export function CancelSubscriptionSwagger() { + return applyDecorators( + ApiHeader({ + name: 'Authorization', + description: 'Authorization with bearer token', + }), + ApiParam({ + name: 'id', + type: 'string', + example: 'I-1CWCLXSVTX7R', + }), + ApiQuery({ + name: 'payment', + required: true, + type: 'string', + example: 'payment=paypal', + enum: PaymentType, + }), + ApiOperation({ + summary: 'Cancel subscription.', + }), + ApiResponse({ + status: 200, + }), + ApiResponse({ + status: 404, + description: 'BusinessCommandService error: subscription does not exist', + }), + ApiResponse({ + status: 409, + description: 'PayPalService error: subscription is canceled already', + }), + ); +} diff --git a/apps/libs/Business/constants/path.constant.ts b/apps/libs/Business/constants/path.constant.ts index 8605829b..a9279245 100644 --- a/apps/libs/Business/constants/path.constant.ts +++ b/apps/libs/Business/constants/path.constant.ts @@ -3,6 +3,7 @@ export const HttpBusinessPath = { CurrentSubscriptions: 'business/subscriptions/get', SuspendSubscription: 'business/subscriptions/:id/suspend', ActivateSubscription: 'business/subscriptions/:id/activate', + CancelSubscription: 'business/subscriptions/:id/cancel', PaypalProcess: 'business/paypal-proccess', SubscriptionsExpired: 'business/subscriptions/expired', SubscriptionsUpdated: 'business/subscriptions/updated', diff --git a/apps/users/src/db/1759504948061-InitTable.ts b/apps/users/src/db/1761615094093-users_dev.ts similarity index 75% rename from apps/users/src/db/1759504948061-InitTable.ts rename to apps/users/src/db/1761615094093-users_dev.ts index 5dfd6887..fd90d536 100644 --- a/apps/users/src/db/1759504948061-InitTable.ts +++ b/apps/users/src/db/1761615094093-users_dev.ts @@ -1,6 +1,6 @@ import { MigrationInterface, QueryRunner } from "typeorm"; -export class InitTable1759504948061 implements MigrationInterface { +export class UsersDev1761615094093 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { } diff --git a/apps/users/src/db/migrations/1759504952602-InitTable.ts b/apps/users/src/db/migrations/1761615103355-users_dev.ts similarity index 97% rename from apps/users/src/db/migrations/1759504952602-InitTable.ts rename to apps/users/src/db/migrations/1761615103355-users_dev.ts index 62f791df..551caabc 100644 --- a/apps/users/src/db/migrations/1759504952602-InitTable.ts +++ b/apps/users/src/db/migrations/1761615103355-users_dev.ts @@ -1,7 +1,7 @@ import { MigrationInterface, QueryRunner } from "typeorm"; -export class InitTable1759504952602 implements MigrationInterface { - name = 'InitTable1759504952602' +export class UsersDev1761615103355 implements MigrationInterface { + name = 'UsersDev1761615103355' public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(` From 516c0d2607ad419b329d0e383abb0bfbee5f7180 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Mon, 27 Oct 2025 22:48:14 -0700 Subject: [PATCH 43/71] BAN-113 feat: paypal does not spam webhooks --- apps/business/src/api/business.controller.ts | 3 +- apps/business/src/business-command.service.ts | 66 +++++++++++++++---- apps/gate/src/business/business.controller.ts | 4 +- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/apps/business/src/api/business.controller.ts b/apps/business/src/api/business.controller.ts index 04ddcfe7..4dc844ad 100644 --- a/apps/business/src/api/business.controller.ts +++ b/apps/business/src/api/business.controller.ts @@ -64,9 +64,10 @@ export class BusinessController { @Body('subscriptionId') subscriptionId: string, @Res() res: Response, ): Promise { - return await this.commandBus.execute( + const subscription = await this.commandBus.execute( new SaveSubscriptionCommand(subscriptionId), ); + res.status(200).json(subscription); // let page = this.configService.get('PROFILE_SETTINGS_PAGE'); // page = page.replace('replace', subscription.userId); // console.log('🚀 ~ BusinessController ~ paypalProcess ~ page:', page); diff --git a/apps/business/src/business-command.service.ts b/apps/business/src/business-command.service.ts index 12231636..5ec8f0f0 100644 --- a/apps/business/src/business-command.service.ts +++ b/apps/business/src/business-command.service.ts @@ -13,10 +13,11 @@ import { IPaymentService } from './payment/interfaces/payment-service.interface' import { SaveSubscriptionDto } from './payment/payment-services/paypal/dto/save-subscription.dto'; import { Subscription } from './infrastructure/entity/subscription.entity'; import { SubscriptionStatus } from './payment/payment-services/paypal/constants/subscription-status.enum'; -import { DataSource, EntityManager } from 'typeorm'; +import { DataSource, EntityManager, Repository } from 'typeorm'; import { BusinessQueryService } from './business-query.service'; import { SubscriptionUpdateDto } from './dto/subscription-update.dto'; import { NotificationsGateway } from '../../../apps/libs/common/notifications/notifications.gateway'; +import { InjectRepository } from '@nestjs/typeorm'; @Injectable() export class BusinessCommandService { @@ -28,6 +29,8 @@ export class BusinessCommandService { private readonly paymentService: IPaymentService, private readonly businessQueryService: BusinessQueryService, private readonly notificationGateway: NotificationsGateway, + @InjectRepository(Subscription) + private readonly subscriptionCommandRepository: Repository, private readonly dataSource: DataSource, ) {} @@ -37,13 +40,23 @@ export class BusinessCommandService { subscribeDto.userId, ); - if (currentSubscriptions.length > 1) { + const currentSubscriptionsWithoutCancelled = currentSubscriptions.filter( + (subscription) => { + return subscription.status !== SubscriptionStatus.Canceled; + }, + ); + console.log( + '🚀 ~ BusinessCommandService ~ subscribe ~ currentSubscriptionsWithoutCancelled:', + currentSubscriptionsWithoutCancelled, + ); + + if (currentSubscriptionsWithoutCancelled.length > 1) { throw new BadRequestException( 'BusinessCommandService error: user cant have more than 2 not expired subscriptions simultaniously', ); } - currentSubscriptions?.map((subscription) => { + currentSubscriptionsWithoutCancelled?.map((subscription) => { if ( new Date(subscription.expiresAt) > new Date() && subscription.subscriptionType === subscribeDto.subscriptionType @@ -98,6 +111,11 @@ export class BusinessCommandService { subscription.userId, ); + const currentSubscriptionsWithoutCancelled = + currentSubscriptionsArr.filter((subscription) => { + return subscription.status !== SubscriptionStatus.Canceled; + }); + const price = getSubscriptionPrice(subscription.subscriptionType); const updatePlanDto = { subscriptionType: subscription.subscriptionType, @@ -112,8 +130,8 @@ export class BusinessCommandService { ); //* expires+1 when it second subscription const startDate = new Date( - currentSubscriptionsArr.length === 1 - ? currentSubscriptionsArr[0].expiresAt + currentSubscriptionsWithoutCancelled.length === 1 + ? currentSubscriptionsWithoutCancelled[0].expiresAt : paypalSubscription.start_time, ); @@ -145,11 +163,31 @@ export class BusinessCommandService { subscription.userId, queryRunner.manager, ); - const firstSubscription: Subscription[] = currentSubscriptions.filter( - (subscr) => subscr.id !== subscription.id, + + const currentSubscriptionsWithoutCancelled1 = currentSubscriptions.filter( + (subscription) => { + return subscription.status !== SubscriptionStatus.Canceled; + }, ); - if (currentSubscriptions.length === 2) { + console.log( + '🚀 ~ BusinessCommandService ~ saveSubscription ~ currentSubscriptionsWithoutCancelled1:', + currentSubscriptionsWithoutCancelled1, + ); + // get suspended + const firstSubscription: Subscription[] = + currentSubscriptionsWithoutCancelled1.filter( + (subscr) => subscr.id !== subscription.id, + ); + console.log( + '🚀 ~ BusinessCommandService ~ saveSubscription ~ firstSubscription:', + firstSubscription, + ); + if (currentSubscriptionsWithoutCancelled1.length === 2) { firstSubscription[0].status = SubscriptionStatus.Suspended; + console.log( + '🚀 ~ BusinessCommandService ~ saveSubscription ~ firstSubscription[0]:', + firstSubscription[0], + ); await this.suspendSubscription(firstSubscription[0].subscriptionId); } const subscription1 = await this.paymentService.getSubscription( @@ -274,11 +312,13 @@ export class BusinessCommandService { await this.paymentService.cancelSubscription(id); subscription.status = SubscriptionStatus.Canceled; - const updatedSubscription = - await this.businessCommandRepository.saveSubscription( - subscription, - queryRunner.manager, - ); + console.log( + '🚀 ~ BusinessCommandService ~ cancelSubscription ~ subscription:', + subscription, + ); + await queryRunner.manager.save(subscription); + // await this.subscriptionCommandRepository.save(subscription); + // todo! problem if second is suspended // if (currentSubscriptions.length === 2) { // const anotherSubscription: Subscription[] = currentSubscriptions.filter( diff --git a/apps/gate/src/business/business.controller.ts b/apps/gate/src/business/business.controller.ts index 4271036c..0522cc0b 100644 --- a/apps/gate/src/business/business.controller.ts +++ b/apps/gate/src/business/business.controller.ts @@ -142,7 +142,9 @@ export class BusinessController { let page = this.configService.get('PROFILE_SETTINGS_PAGE'); page = page.replace('replace', subscription['userId']); console.log('🚀 ~ BusinessController ~ paypalProcess ~ page:', page); - res.redirect(301, page); + // res.redirect(301, page); + // res.sendStatus(200); + return subscription; } } From e86f5875626532d669ab96835e8baea0033aad13 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 5 Nov 2025 16:31:41 -0800 Subject: [PATCH 44/71] BAN logs --- apps/business/src/business-command.service.ts | 5 +---- .../payment/interfaces/payment-service.interface.ts | 1 + .../payment/payment-services/paypal/paypal.service.ts | 10 ++++++++++ apps/business/src/payment/payment.factory.ts | 8 +++++++- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/apps/business/src/business-command.service.ts b/apps/business/src/business-command.service.ts index 5ec8f0f0..f4c96f29 100644 --- a/apps/business/src/business-command.service.ts +++ b/apps/business/src/business-command.service.ts @@ -45,10 +45,6 @@ export class BusinessCommandService { return subscription.status !== SubscriptionStatus.Canceled; }, ); - console.log( - '🚀 ~ BusinessCommandService ~ subscribe ~ currentSubscriptionsWithoutCancelled:', - currentSubscriptionsWithoutCancelled, - ); if (currentSubscriptionsWithoutCancelled.length > 1) { throw new BadRequestException( @@ -77,6 +73,7 @@ export class BusinessCommandService { ); const response = await this.paymentService.subscribeToPlan( + subscribeDto.userId, subscribeDto.subscriptionType, currentSubscriptions.length === 1 ? start_date.toISOString() : undefined, ); diff --git a/apps/business/src/payment/interfaces/payment-service.interface.ts b/apps/business/src/payment/interfaces/payment-service.interface.ts index c526b263..769aab4a 100644 --- a/apps/business/src/payment/interfaces/payment-service.interface.ts +++ b/apps/business/src/payment/interfaces/payment-service.interface.ts @@ -2,6 +2,7 @@ import { SubscriptionType } from '../../../../../apps/libs/Business/constants/su export abstract class IPaymentService { abstract subscribeToPlan( + userId: string, subscriptionType: SubscriptionType, startAt?: string, ): Promise; diff --git a/apps/business/src/payment/payment-services/paypal/paypal.service.ts b/apps/business/src/payment/payment-services/paypal/paypal.service.ts index 2a94712a..d3cd4622 100644 --- a/apps/business/src/payment/payment-services/paypal/paypal.service.ts +++ b/apps/business/src/payment/payment-services/paypal/paypal.service.ts @@ -14,6 +14,7 @@ import { Client, Environment, LogLevel } from '@paypal/paypal-server-sdk'; import { createBusinessPlan } from './helpers/create-business-plan.helper'; import { SubscriptionStatus } from './constants/subscription-status.enum'; import axios from 'axios'; +import { ConfigService } from '@nestjs/config'; export class PayPalService implements IPaymentService { private client: Client; @@ -21,6 +22,7 @@ export class PayPalService implements IPaymentService { private readonly client_id: string, private readonly client_secret: string, private readonly businessServiceUrl: string, + private readonly configService: ConfigService, ) { this.client = new Client({ clientCredentialsAuthCredentials: { @@ -179,6 +181,7 @@ export class PayPalService implements IPaymentService { } async subscribeToPlan( + userId: string, subscriptionType: SubscriptionType, startAt?: string, ): Promise { @@ -205,6 +208,11 @@ export class PayPalService implements IPaymentService { const nextDay = startAt ? startAt : new Date(today.setDate(today.getDate() + 1)).toISOString(); + + let returnUrl = this.configService.get('PROFILE_SETTINGS_PAGE'); + returnUrl = returnUrl.replace('replace', userId); + console.log('🚀 ~ PayPalService ~ subscribeToPlan ~ returnUrl:', returnUrl); + const subscribe = { plan_id: plan['id'], quantity: 1, @@ -218,6 +226,8 @@ export class PayPalService implements IPaymentService { payer_selected: 'PAYPAL', payee_preferred: 'IMMEDIATE_PAYMENT_REQUIRED', }, + return_url: returnUrl, + cancel_url: '', }, }; // To start a PayPal subscription plan immediately, set the trial_duration to 0 days when creating the subscription plan in the PayPal Developer portal or via the API, diff --git a/apps/business/src/payment/payment.factory.ts b/apps/business/src/payment/payment.factory.ts index 2e4fb22f..592b6027 100644 --- a/apps/business/src/payment/payment.factory.ts +++ b/apps/business/src/payment/payment.factory.ts @@ -3,6 +3,7 @@ import { PayPalService } from './payment-services/paypal/paypal.service'; import { StripeService } from './payment-services/stripe/stripe.service'; import { PaymentType } from '../../../../apps/libs/Business/constants/payment-type.enum'; import { RequestContext } from 'nestjs-request-context'; +import { ConfigService } from '@nestjs/config'; @Injectable({ scope: Scope.REQUEST }) export class PaymentFactory { @@ -32,7 +33,12 @@ export class PaymentFactory { switch (service) { case PaymentType.PAYPAL: { - return new PayPalService(clientId, secret, businessServiceUrl); + return new PayPalService( + clientId, + secret, + businessServiceUrl, + new ConfigService(), + ); } case PaymentType.STRIPE: { return this.stripeService; From 7c82d971158e8384c9ddab35fd208e3e22010841 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 5 Nov 2025 20:52:14 -0800 Subject: [PATCH 45/71] BAN-113 --- .../src/payment/payment-services/paypal/paypal.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/business/src/payment/payment-services/paypal/paypal.service.ts b/apps/business/src/payment/payment-services/paypal/paypal.service.ts index d3cd4622..6b296f7e 100644 --- a/apps/business/src/payment/payment-services/paypal/paypal.service.ts +++ b/apps/business/src/payment/payment-services/paypal/paypal.service.ts @@ -202,7 +202,7 @@ export class PayPalService implements IPaymentService { if (!plan) throw new BadRequestException('Paypal error: plan does not exist'); // console.log('plan', plan); - + // const token = await this.authentication(); const today = new Date(); const nextDay = startAt @@ -210,6 +210,7 @@ export class PayPalService implements IPaymentService { : new Date(today.setDate(today.getDate() + 1)).toISOString(); let returnUrl = this.configService.get('PROFILE_SETTINGS_PAGE'); + console.log('🚀 ~ PayPalService ~ subscribeToPlan ~ returnUrl:', returnUrl); returnUrl = returnUrl.replace('replace', userId); console.log('🚀 ~ PayPalService ~ subscribeToPlan ~ returnUrl:', returnUrl); From 6769141bc3852693e2760d50cf5b4f022c192f7b Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 6 Nov 2025 10:14:06 -0800 Subject: [PATCH 46/71] BAN-113 feat: add successUrl and cancelUrl --- .../src/payment/payment-services/paypal/paypal.service.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/business/src/payment/payment-services/paypal/paypal.service.ts b/apps/business/src/payment/payment-services/paypal/paypal.service.ts index 6b296f7e..663f14d7 100644 --- a/apps/business/src/payment/payment-services/paypal/paypal.service.ts +++ b/apps/business/src/payment/payment-services/paypal/paypal.service.ts @@ -210,9 +210,11 @@ export class PayPalService implements IPaymentService { : new Date(today.setDate(today.getDate() + 1)).toISOString(); let returnUrl = this.configService.get('PROFILE_SETTINGS_PAGE'); - console.log('🚀 ~ PayPalService ~ subscribeToPlan ~ returnUrl:', returnUrl); returnUrl = returnUrl.replace('replace', userId); - console.log('🚀 ~ PayPalService ~ subscribeToPlan ~ returnUrl:', returnUrl); + const successUrl = [returnUrl, 'success=1'].join('?'); + const cancelUrl = [returnUrl, 'success=0'].join('?'); + console.log('🚀 ~ PayPalService successUrl:', successUrl); + console.log('🚀 ~ PayPalService cancelUrl:', cancelUrl); const subscribe = { plan_id: plan['id'], @@ -228,7 +230,7 @@ export class PayPalService implements IPaymentService { payee_preferred: 'IMMEDIATE_PAYMENT_REQUIRED', }, return_url: returnUrl, - cancel_url: '', + cancel_url: cancelUrl, }, }; // To start a PayPal subscription plan immediately, set the trial_duration to 0 days when creating the subscription plan in the PayPal Developer portal or via the API, From 21afc38cf7a6aa131931909fb6bb3dd38b5ae517 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 6 Nov 2025 10:15:44 -0800 Subject: [PATCH 47/71] /... --- .../src/payment/payment-services/paypal/paypal.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/business/src/payment/payment-services/paypal/paypal.service.ts b/apps/business/src/payment/payment-services/paypal/paypal.service.ts index 663f14d7..014ab3f5 100644 --- a/apps/business/src/payment/payment-services/paypal/paypal.service.ts +++ b/apps/business/src/payment/payment-services/paypal/paypal.service.ts @@ -229,7 +229,7 @@ export class PayPalService implements IPaymentService { payer_selected: 'PAYPAL', payee_preferred: 'IMMEDIATE_PAYMENT_REQUIRED', }, - return_url: returnUrl, + return_url: successUrl, cancel_url: cancelUrl, }, }; From f65a36d15e652ee730999f241956a656372598c8 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 6 Nov 2025 16:28:42 -0800 Subject: [PATCH 48/71] BAN-113 feat: add expiresAt to paymentresponsedto --- .../repository/query/business-query.repository.ts | 14 +++++++++++++- .../Business/dto/response/response-payment.dto.ts | 6 +++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/business/src/infrastructure/repository/query/business-query.repository.ts b/apps/business/src/infrastructure/repository/query/business-query.repository.ts index 2f6b72bd..ced1435f 100644 --- a/apps/business/src/infrastructure/repository/query/business-query.repository.ts +++ b/apps/business/src/infrastructure/repository/query/business-query.repository.ts @@ -65,10 +65,22 @@ export class BusinessQueryRepository take: pagination.limit, order: sort, where: filter, + relations: { + subscription: true, + }, }); const paginatedResponse: PaymentsPaginatedResponseDto = { - items: plainToInstance(ResponsePaymentDto, payments[0]), + items: plainToInstance( + ResponsePaymentDto, + payments[0].map((payment) => { + if (payment.subscription) { + payment['expiresAt'] = payment.subscription.expiresAt; + delete payment.subscription; + } + return payment; + }), + ), totalItems: payments[1], page: pagination.page, limit: pagination.limit, diff --git a/apps/libs/Business/dto/response/response-payment.dto.ts b/apps/libs/Business/dto/response/response-payment.dto.ts index f4519204..63f56e29 100644 --- a/apps/libs/Business/dto/response/response-payment.dto.ts +++ b/apps/libs/Business/dto/response/response-payment.dto.ts @@ -1,3 +1,7 @@ +import { Expose } from 'class-transformer'; import { Payment } from '../../../../../apps/business/src/infrastructure/entity/payment.entity'; -export class ResponsePaymentDto extends Payment {} +export class ResponsePaymentDto extends Payment { + @Expose() + expiresAt: Date; +} From 7253032ba804463733fc739c61c0187cdd3affdf Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Tue, 20 Jan 2026 13:35:56 -0800 Subject: [PATCH 49/71] ffff --- apps/gate/src/auth/auth.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gate/src/auth/auth.module.ts b/apps/gate/src/auth/auth.module.ts index 8535abc6..72930f47 100644 --- a/apps/gate/src/auth/auth.module.ts +++ b/apps/gate/src/auth/auth.module.ts @@ -18,7 +18,7 @@ import { GoogleOauth } from './oauth/google.oauth'; import { SessionProvider } from './session/session.provider'; import { RedisModule } from '../../../../apps/libs/common/redis/redis.module'; import { RefreshGuard } from './guards/refresh.guard'; - +// @Module({ imports: [ forwardRef(() => UsersModule), From bcf64f874ab3f9b34d64dcdbfbe70bda1144fb3e Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 21 Jan 2026 16:36:33 -0800 Subject: [PATCH 50/71] add injectable() --- .../notifications/notifications.gateway.ts | 16 ++++------------ .../notifications/notifications.service.ts | 1 + 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/apps/libs/common/notifications/notifications.gateway.ts b/apps/libs/common/notifications/notifications.gateway.ts index be5e96da..0db74f78 100644 --- a/apps/libs/common/notifications/notifications.gateway.ts +++ b/apps/libs/common/notifications/notifications.gateway.ts @@ -2,20 +2,16 @@ import { NotificationResponseDto } from '../../../../apps/libs/Business/dto/resp import { ExpiresInDuration } from '../../../../apps/business/src/constants/expires-in-duration.enum'; import { WebsocketEvents } from '../../../../apps/business/src/constants/websocket.event.enum'; import { INotificationsService } from './interfaces/notification-service.interface'; -import { - OnGatewayConnection, - OnGatewayInit, - WebSocketGateway, - WebSocketServer, -} from '@nestjs/websockets'; +import { OnGatewayConnection, OnGatewayInit } from '@nestjs/websockets'; import { INotification } from './interfaces/notification.interface'; -import { socketAuthMiddleware } from './helper/socket-auth.helper'; + import { NotificationsService } from './notifications.service'; import { Socket, Server } from 'socket.io'; import { JwtService } from '@nestjs/jwt'; -import { OnModuleInit } from '@nestjs/common'; +import { Injectable, OnModuleInit } from '@nestjs/common'; import { io, Socket as Socket1 } from 'socket.io-client'; +@Injectable() export class NotificationsGateway implements INotificationsService, @@ -65,10 +61,6 @@ export class NotificationsGateway // } handleConnection(socket: Socket) { - console.log( - '🚀 ~ NotificationsGateway ~ handleConnection ~ socket:', - socket.id, - ); // this.notificationsService.handleConnection(socket); this.sendMessageToGateway(`connected ${socket.id}`); } diff --git a/apps/libs/common/notifications/notifications.service.ts b/apps/libs/common/notifications/notifications.service.ts index 5b734fd4..42d44669 100644 --- a/apps/libs/common/notifications/notifications.service.ts +++ b/apps/libs/common/notifications/notifications.service.ts @@ -59,6 +59,7 @@ export class NotificationsService implements INotificationsService { ? '' : 'dev:' }notifications:user:${userId}:notification:*`; + const stream = this.redisClient.scanStream({ match: match, }); From 940291801b64180cc6baa501a8eba04d856ab5b2 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 21 Jan 2026 16:42:41 -0800 Subject: [PATCH 51/71] feat: // --- apps/libs/common/notifications/notifications.gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/libs/common/notifications/notifications.gateway.ts b/apps/libs/common/notifications/notifications.gateway.ts index 0db74f78..3f0aa624 100644 --- a/apps/libs/common/notifications/notifications.gateway.ts +++ b/apps/libs/common/notifications/notifications.gateway.ts @@ -10,7 +10,7 @@ import { Socket, Server } from 'socket.io'; import { JwtService } from '@nestjs/jwt'; import { Injectable, OnModuleInit } from '@nestjs/common'; import { io, Socket as Socket1 } from 'socket.io-client'; - +// @Injectable() export class NotificationsGateway implements From 083e856fda7564f9c53944b72d7796ab1f4db9bc Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 21 Jan 2026 16:50:06 -0800 Subject: [PATCH 52/71] /try/// --- apps/libs/common/notifications/notifications.gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/libs/common/notifications/notifications.gateway.ts b/apps/libs/common/notifications/notifications.gateway.ts index 3f0aa624..e174c4f3 100644 --- a/apps/libs/common/notifications/notifications.gateway.ts +++ b/apps/libs/common/notifications/notifications.gateway.ts @@ -10,7 +10,7 @@ import { Socket, Server } from 'socket.io'; import { JwtService } from '@nestjs/jwt'; import { Injectable, OnModuleInit } from '@nestjs/common'; import { io, Socket as Socket1 } from 'socket.io-client'; -// +//try @Injectable() export class NotificationsGateway implements From 8bca5d89c9e3a49bbf6822a4ab8cdd98b49151da Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 21 Jan 2026 17:03:11 -0800 Subject: [PATCH 53/71] //try//// --- apps/libs/common/notifications/notifications.gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/libs/common/notifications/notifications.gateway.ts b/apps/libs/common/notifications/notifications.gateway.ts index e174c4f3..fbf4a866 100644 --- a/apps/libs/common/notifications/notifications.gateway.ts +++ b/apps/libs/common/notifications/notifications.gateway.ts @@ -10,7 +10,7 @@ import { Socket, Server } from 'socket.io'; import { JwtService } from '@nestjs/jwt'; import { Injectable, OnModuleInit } from '@nestjs/common'; import { io, Socket as Socket1 } from 'socket.io-client'; -//try +//try//// @Injectable() export class NotificationsGateway implements From 3d8d6c1a27964f007432a344bcaa722953429c92 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 21 Jan 2026 17:25:25 -0800 Subject: [PATCH 54/71] try --- apps/libs/common/notifications/notifications.gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/libs/common/notifications/notifications.gateway.ts b/apps/libs/common/notifications/notifications.gateway.ts index fbf4a866..0db74f78 100644 --- a/apps/libs/common/notifications/notifications.gateway.ts +++ b/apps/libs/common/notifications/notifications.gateway.ts @@ -10,7 +10,7 @@ import { Socket, Server } from 'socket.io'; import { JwtService } from '@nestjs/jwt'; import { Injectable, OnModuleInit } from '@nestjs/common'; import { io, Socket as Socket1 } from 'socket.io-client'; -//try//// + @Injectable() export class NotificationsGateway implements From e0161203291e534f646a639b3e36e1d6c91d93a0 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 21 Jan 2026 17:54:49 -0800 Subject: [PATCH 55/71] init --- apps/business/Jenkinsfile | 6 +++--- apps/business/deployment.yaml | 25 ++++++++++++------------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/apps/business/Jenkinsfile b/apps/business/Jenkinsfile index 279045d0..400fdcd9 100644 --- a/apps/business/Jenkinsfile +++ b/apps/business/Jenkinsfile @@ -3,13 +3,13 @@ pipeline { agent any environment { ENV_TYPE = "production" - PORT = 4052 + PORT = 4161 NAMESPACE = "yogram-ru" REGISTRY_HOSTNAME = "idogmat" - PROJECT = "yogram-business" + PROJECT = "business-yogram" SERVICE="business" REGISTRY = "registry.hub.docker.com" - DEPLOYMENT_NAME = "yogram-business-deployment" + DEPLOYMENT_NAME = "business-yogram-deployment" IMAGE_NAME = "${env.BUILD_ID}_${env.ENV_TYPE}_${env.GIT_COMMIT}" DOCKER_BUILD_NAME = "${env.REGISTRY_HOSTNAME}/${env.PROJECT}:${env.IMAGE_NAME}" } diff --git a/apps/business/deployment.yaml b/apps/business/deployment.yaml index 58292cff..1cd17f67 100644 --- a/apps/business/deployment.yaml +++ b/apps/business/deployment.yaml @@ -20,65 +20,64 @@ spec: image: REGISTRY_HOSTNAME/PROJECT:TAG_VERSION ports: - containerPort: PORT_CONTAINER - env: - name: PAYPAL_SECRET valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: PAYPAL_SECRET - name: USERS_SERVICE_URL valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: USERS_SERVICE_URL - name: REDIS_PASSWORD valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: REDIS_PASSWORD - name: REDIS_HOST valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: REDIS_HOST - name: REDIS_PORT valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: REDIS_PORT - name: TIME_PERIOD valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: TIME_PERIOD - name: REDIS_USER valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: REDIS_USER - name: BUSINESS_SERVICE_URL valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: BUSINESS_SERVICE_URL - name: POSTGRES_TYPE valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: POSTGRES_TYPE - name: POSTGRES_URL valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: POSTGRES_URL - name: PROFILE_SETTINGS_PAGE valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: PROFILE_SETTINGS_PAGE - name: PAYPAL_CLIENT_ID valueFrom: secretKeyRef: - name: yogram-business-production-config-secret + name: business-yogram-production-config-secret key: PAYPAL_CLIENT_ID From 7782edf837d69eaa61f2496354d362656b71b271 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 21 Jan 2026 18:09:19 -0800 Subject: [PATCH 56/71] .... --- apps/business/Jenkinsfile | 50 +++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/apps/business/Jenkinsfile b/apps/business/Jenkinsfile index 400fdcd9..67e23ba3 100644 --- a/apps/business/Jenkinsfile +++ b/apps/business/Jenkinsfile @@ -20,31 +20,31 @@ pipeline { checkout scm } } - stage('Unit tests') { - steps { - script { - sh ''' - export NVM_DIR="$HOME/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" - nvm use --lts - yarn install - yarn test - ''' - } - } - } - stage('e2e tests') { - steps { - script { - sh ''' - export NVM_DIR="$HOME/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" - nvm use --lts - yarn test:e2e - ''' - } - } - } + // stage('Unit tests') { + // steps { + // script { + // sh ''' + // export NVM_DIR="$HOME/.nvm" + // [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + // nvm use --lts + // yarn install + // yarn test + // ''' + // } + // } + // } + // stage('e2e tests') { + // steps { + // script { + // sh ''' + // export NVM_DIR="$HOME/.nvm" + // [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + // nvm use --lts + // yarn test:e2e + // ''' + // } + // } + // } stage('Build docker image') { steps { echo "Build image started..." From 67a6b946fdaa49839c58c5cb2ff7956319fb3354 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 21 Jan 2026 19:07:04 -0800 Subject: [PATCH 57/71] fix: node_env --- apps/business/src/api/business.controller.ts | 1 + apps/libs/gateService/index.ts | 10 ++++++++++ package.json | 8 ++++---- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/apps/business/src/api/business.controller.ts b/apps/business/src/api/business.controller.ts index 4dc844ad..7aa1d60a 100644 --- a/apps/business/src/api/business.controller.ts +++ b/apps/business/src/api/business.controller.ts @@ -156,6 +156,7 @@ export class BusinessController { async getUserNotifications( @Param('id') id: string, ): Promise { + console.log('🚀 ~ BusinessController ~ getUserNotifications ~ id:', id); return await this.queryBus.execute(new GetUserNotificationsQuery(id)); } diff --git a/apps/libs/gateService/index.ts b/apps/libs/gateService/index.ts index 77860c57..e5bb7f85 100644 --- a/apps/libs/gateService/index.ts +++ b/apps/libs/gateService/index.ts @@ -23,12 +23,17 @@ export class GateService { // : `${this.configService.get('USERS_PROD_SERVICE_URL')}/api/v1`; // remove all constructor before this line // example for normal switch service throw config in micro + Object.assign(this.services, { POSTS: this.configService.get('POSTS_SERVICE_URL'), USERS: this.configService.get('USERS_SERVICE_URL'), FILES: this.configService.get('FILES_SERVICE_URL'), BUSINESS: this.configService.get('BUSINESS_SERVICE_URL'), }); + console.log( + 'BUSINESS_SERVICE_URL index', + this.configService.get('BUSINESS_SERVICE_URL'), + ); } async requestHttpServicePost(service, path, payload, headers) { @@ -51,6 +56,11 @@ export class GateService { async requestHttpServiceGet(service, path, headers) { try { + console.log( + 'requestHttpServiceGet', + [this.services[service], path].join('/'), + ); + const { data } = await lastValueFrom( this.httpService.get([this.services[service], path].join('/'), { headers, diff --git a/package.json b/package.json index abd68382..e329457c 100644 --- a/package.json +++ b/package.json @@ -20,16 +20,16 @@ "start:users": "cross-env NODE_ENV=PRODUCTION node dist/apps/users/main", "start:dev:users": "cross-env NODE_ENV=DEVELOPMENT nest start users --watch", "build:mailer": "nest build mailer", - "start:mailer": "node dist/apps/mailer/main", + "start:mailer": "cross-env NODE_ENV=PRODUCTION node dist/apps/mailer/main", "start:dev:mailer": "cross-env NODE_ENV=DEVELOPMENT nest start mailer --watch", "build:posts": "nest build posts", - "start:posts": "node dist/apps/posts/main", + "start:posts": "cross-env NODE_ENV=PRODUCTION node dist/apps/posts/main", "start:dev:posts": "cross-env NODE_ENV=DEVELOPMENT nest start posts --watch", "build:files": "nest build files", - "start:files": "node dist/apps/files/main", + "start:files": "cross-env NODE_ENV=PRODUCTION node dist/apps/files/main", "start:dev:files": "cross-env NODE_ENV=DEVELOPMENT nest start files --watch", "build:business": "nest build business", - "start:business": "node dist/apps/business/main", + "start:business": "cross-env NODE_ENV=PRODUCTION node dist/apps/business/main", "start:dev:business": "cross-env NODE_ENV=DEVELOPMENT nest start business --watch", "migration": "cross-env node scripts/typeorm-migration.js", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", From b73527860a7403c9d9f5243a98712593bcd52fea Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 21 Jan 2026 19:19:38 -0800 Subject: [PATCH 58/71] dddd --- apps/gate/Jenkinsfile | 50 +++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/apps/gate/Jenkinsfile b/apps/gate/Jenkinsfile index 5b35bdc4..d2eef2e7 100644 --- a/apps/gate/Jenkinsfile +++ b/apps/gate/Jenkinsfile @@ -20,31 +20,31 @@ pipeline { checkout scm } } - stage('Unit tests') { - steps { - script { - sh ''' - export NVM_DIR="$HOME/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" - nvm use --lts - yarn install - yarn test - ''' - } - } - } - stage('e2e tests') { - steps { - script { - sh ''' - export NVM_DIR="$HOME/.nvm" - [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" - nvm use --lts - yarn test:e2e - ''' - } - } - } + // stage('Unit tests') { + // steps { + // script { + // sh ''' + // export NVM_DIR="$HOME/.nvm" + // [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + // nvm use --lts + // yarn install + // yarn test + // ''' + // } + // } + // } + // stage('e2e tests') { + // steps { + // script { + // sh ''' + // export NVM_DIR="$HOME/.nvm" + // [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + // nvm use --lts + // yarn test:e2e + // ''' + // } + // } + // } stage('Build docker image') { steps { echo "Build image started..." From 5ac7d73595486c609ddb2c8cab89d14ef967d7db Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Wed, 21 Jan 2026 19:45:33 -0800 Subject: [PATCH 59/71] ddd --- apps/gate/src/settings/configuration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gate/src/settings/configuration.ts b/apps/gate/src/settings/configuration.ts index 96a78170..a3a5785a 100644 --- a/apps/gate/src/settings/configuration.ts +++ b/apps/gate/src/settings/configuration.ts @@ -27,7 +27,7 @@ export const getConfiguration = () => { }, {}, ); - console.log('SERVICES_URLS', SERVICES_URLS); + console.log('SERVICES_URLS:', SERVICES_URLS); return { NODE_ENV: (Environments.includes(process.env.NODE_ENV?.trim()) From 7cd02f2382271fa5aaa690da561a3d8b94451978 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 23 Jan 2026 11:56:30 -0800 Subject: [PATCH 60/71] feat: logs notif --- apps/business/src/business.module.ts | 18 ++--- apps/business/src/main.ts | 5 ++ .../src/notifications-consumer.service.ts | 4 ++ .../src/notifications-producer.service.ts | 8 ++- .../notifications/notifications.gateway.ts | 70 ++++++++----------- .../notifications/notifications.module.ts | 5 +- .../common/notifications/ws-auth.adapter.ts | 41 +++++++++++ 7 files changed, 95 insertions(+), 56 deletions(-) create mode 100644 apps/libs/common/notifications/ws-auth.adapter.ts diff --git a/apps/business/src/business.module.ts b/apps/business/src/business.module.ts index 0ab0b25e..1b333285 100644 --- a/apps/business/src/business.module.ts +++ b/apps/business/src/business.module.ts @@ -64,10 +64,6 @@ import { BullModule } from '@nestjs/bullmq'; import { NotificationsProducer } from './notifications-producer.service'; import { NotificationConsumer } from './notifications-consumer.service'; import { DatabaseModule } from '../../../apps/libs/common/database/database.module'; -import { - CancelSubscriptionCommand, - CancelSubscriptionHandler, -} from './application/command/cancel-subscription.handler'; const getEnvFilePath = (env: EnvironmentsTypes) => { const defaultEnvFilePath = ['apps/business/src/.env.development']; @@ -121,18 +117,18 @@ export const NOTIFICATION_SCHEDULER = 'NOTIFICATION_SCHEDULER'; // url: configService.get('url'), // migrationsTableName: configService.get('migrationsTableName'), // migrations: [`${__dirname}/../../db/migrations/*{.ts,.js}`], - // autoLoadEntities: true, - // synchronize: false, - // dropSchema: false, + // autoLoadEntities: configService.get('autoLoadEntities'), + // synchronize: configService.get('synchronize'), + // dropSchema: configService.get('dropSchema'), // }, // slaves: [ // { // url: configService.get('urlSlave'), // migrationsTableName: configService.get('migrationsTableName'), // migrations: [`${__dirname}/../../db/migrations/*{.ts,.js}`], - // autoLoadEntities: true, - // synchronize: false, - // dropSchema: false, + // autoLoadEntities: configService.get('autoLoadEntities'), + // synchronize: configService.get('synchronize'), + // dropSchema: configService.get('dropSchema'), // }, // ], // }, @@ -158,8 +154,6 @@ export const NOTIFICATION_SCHEDULER = 'NOTIFICATION_SCHEDULER'; SuspendSubscriptionHandler, ActivateSubscriptionHandler, ActivateSubscriptionCommand, - CancelSubscriptionCommand, - CancelSubscriptionHandler, SubscriptionUpdatedCommand, SubscriptionUpdatedHandler, SubscriptionExpiredCommand, diff --git a/apps/business/src/main.ts b/apps/business/src/main.ts index 82222296..0e87680a 100644 --- a/apps/business/src/main.ts +++ b/apps/business/src/main.ts @@ -2,12 +2,17 @@ import { NestFactory } from '@nestjs/core'; import { useContainer } from 'class-validator'; import { applyAppSettings } from './settings/main.settings'; import { BusinessModule } from './business.module'; +import { WsAuthAdapter } from 'apps/libs/common/notifications/ws-auth.adapter'; +import { JwtService } from '@nestjs/jwt'; async function bootstrap() { const app = await NestFactory.create(BusinessModule); const { port, env, host } = applyAppSettings(app); useContainer(app.select(BusinessModule), { fallbackOnErrors: true }); + const jwtService = app.get(JwtService); + + // app.useWebSocketAdapter(new WsAuthAdapter(jwtService, app)); await app.listen(port); console.log( diff --git a/apps/business/src/notifications-consumer.service.ts b/apps/business/src/notifications-consumer.service.ts index eeb691ea..34a59564 100644 --- a/apps/business/src/notifications-consumer.service.ts +++ b/apps/business/src/notifications-consumer.service.ts @@ -21,6 +21,10 @@ export class NotificationConsumer extends WorkerHost { await this.notificationGateway.getExpiresInNotifications( ExpiresInDuration[item], ); + console.log( + '🚀 ~ NotificationConsumer ~ process ~ result:', + result, + ); return result; } }), diff --git a/apps/business/src/notifications-producer.service.ts b/apps/business/src/notifications-producer.service.ts index bb9b86bd..e24e4db6 100644 --- a/apps/business/src/notifications-producer.service.ts +++ b/apps/business/src/notifications-producer.service.ts @@ -8,13 +8,19 @@ export class NotificationsProducer implements OnApplicationBootstrap { constructor( @InjectQueue('NOTIFICATION_SCHEDULER') private notificationsQueue: Queue, private readonly configService: ConfigService, - ) {} + ) { + console.log('NotificationsProducer starts'); + } async onApplicationBootstrap() { + console.log('TIME_PERIOD', this.configService.get('TIME_PERIOD')); + await this.sendNotificationToQueue(); } async sendNotificationToQueue() { + console.log('sendNotificationToQueue...'); + await this.notificationsQueue.add( 'NOTIFICATION_SCHEDULER', {}, diff --git a/apps/libs/common/notifications/notifications.gateway.ts b/apps/libs/common/notifications/notifications.gateway.ts index 0db74f78..83b718eb 100644 --- a/apps/libs/common/notifications/notifications.gateway.ts +++ b/apps/libs/common/notifications/notifications.gateway.ts @@ -1,16 +1,22 @@ +import { + ConnectedSocket, + MessageBody, + OnGatewayConnection, + OnGatewayInit, + SubscribeMessage, + WebSocketGateway, + WebSocketServer, +} from '@nestjs/websockets'; import { NotificationResponseDto } from '../../../../apps/libs/Business/dto/response/response-notification.dto'; import { ExpiresInDuration } from '../../../../apps/business/src/constants/expires-in-duration.enum'; import { WebsocketEvents } from '../../../../apps/business/src/constants/websocket.event.enum'; import { INotificationsService } from './interfaces/notification-service.interface'; -import { OnGatewayConnection, OnGatewayInit } from '@nestjs/websockets'; import { INotification } from './interfaces/notification.interface'; - import { NotificationsService } from './notifications.service'; -import { Socket, Server } from 'socket.io'; -import { JwtService } from '@nestjs/jwt'; import { Injectable, OnModuleInit } from '@nestjs/common'; -import { io, Socket as Socket1 } from 'socket.io-client'; +import { Socket, Server } from 'socket.io'; +@WebSocketGateway() @Injectable() export class NotificationsGateway implements @@ -20,51 +26,33 @@ export class NotificationsGateway OnGatewayInit { private connectedClients: Map = new Map(); - private socket: Socket1; - constructor( - private readonly notificationsService: NotificationsService, - // private readonly jwtService: JwtService, - ) {} + private socket: Socket; + @WebSocketServer() + server: Server; // The Socket.IO server instance + constructor(private readonly notificationsService: NotificationsService) {} - async onModuleInit() { - const jwtService = new JwtService({ global: true }); - const token = await jwtService.signAsync( - { id: 'd25a77e9-1e92-469f-8e01-c325e8220cc9' }, - { secret: 'secret_jwt_1234' }, + @SubscribeMessage('message') + handleMessage( + @ConnectedSocket() client: Socket, + @MessageBody() data: string, + ): void { + console.log( + `Received message from client ${client.id} on 'message' channel: ${data}`, ); - // console.log('🚀 ~ NotificationsGateway ~ onModuleInit ~ token:', token); - this.socket = io('http://localhost:3007/notifications', { - auth: { authorization: token }, - }); - this.socket.on('connect', () => { - console.log('Connected to WebSocket Gateway!'); - }); - this.socket.on('message_from_gateway', (data: any) => { - console.log('Received from gateway:', data); - }); - this.socket.on('connectedSocket', (data: any) => { - console.log('connectedSocket', data); - }); - this.socket.emit('send_to_gateway', 'hello'); - } - sendMessageToGateway(message: string) { - this.socket.emit('send_to_gateway', message); // Emit events to the gateway + client.emit('messageReceived', `Server received your message: ${data}`); } - afterInit(server: any) { - // const authMiddleware = socketAuthMiddleware(this.jwtService); - // server.use(authMiddleware); + async onModuleInit() { + console.log('websocket'); } - // handleDisconnect(socket: Socket) { - // this.notificationsService.handleDisconnect(socket); - // } - handleConnection(socket: Socket) { - // this.notificationsService.handleConnection(socket); - this.sendMessageToGateway(`connected ${socket.id}`); + afterInit(server: Server) { + console.log('sfterInit', server.sockets); } + handleConnection(socket: Socket) {} + async saveNotification( key: string, notification: INotification, diff --git a/apps/libs/common/notifications/notifications.module.ts b/apps/libs/common/notifications/notifications.module.ts index eb7b6d4c..6194e2d7 100644 --- a/apps/libs/common/notifications/notifications.module.ts +++ b/apps/libs/common/notifications/notifications.module.ts @@ -4,6 +4,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; import { DynamicModule, Module } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; import { RedisModule } from '../redis/redis.module'; +import { WsAuthAdapter } from './ws-auth.adapter'; @Module({}) export class NotificationsModule { @@ -21,8 +22,8 @@ export class NotificationsModule { }), }), ], - providers: [NotificationsService, NotificationsGateway], - exports: [NotificationsGateway], + providers: [WsAuthAdapter, NotificationsService, NotificationsGateway], + exports: [NotificationsGateway, WsAuthAdapter], }; } } diff --git a/apps/libs/common/notifications/ws-auth.adapter.ts b/apps/libs/common/notifications/ws-auth.adapter.ts new file mode 100644 index 00000000..8a3a1057 --- /dev/null +++ b/apps/libs/common/notifications/ws-auth.adapter.ts @@ -0,0 +1,41 @@ +import { IoAdapter } from '@nestjs/platform-socket.io'; +import { INestApplicationContext, Logger } from '@nestjs/common'; +import { Server, Socket } from 'socket.io'; +import { JwtService } from '@nestjs/jwt'; + +export class WsAuthAdapter extends IoAdapter { + private readonly logger = new Logger(WsAuthAdapter.name); + + constructor( + private jwtService: JwtService, + private readonly app: INestApplicationContext, + ) { + super(app); + } + + createIOServer(port: number, options?: any): Server { + const server = super.createIOServer(port, options); + server.use(async (socket: Socket, next) => { + const token = socket.handshake.auth.token; + console.log('🚀 ~ WsAuthAdapter ~ createIOServer ~ token:', token); + if (!token) { + this.logger.error('No authorization token provided'); + return next(new Error('Authentication error')); + } + try { + const user = await this.jwtService.verifyAsync(token); + + if (!user) { + return next(new Error('Authentication error')); + } + + (socket as any).user = user; + next(); + } catch (error) { + this.logger.error('Token validation failed', error.stack); + next(new Error('Authentication error')); + } + }); + return server; + } +} From 6fe1a1123fa5aee9c726000d0e99d271db35eb6c Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 23 Jan 2026 12:10:50 -0800 Subject: [PATCH 61/71] log --- apps/business/src/notifications-consumer.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/business/src/notifications-consumer.service.ts b/apps/business/src/notifications-consumer.service.ts index 34a59564..55088504 100644 --- a/apps/business/src/notifications-consumer.service.ts +++ b/apps/business/src/notifications-consumer.service.ts @@ -12,6 +12,8 @@ export class NotificationConsumer extends WorkerHost { } async process(job: Job, token?: string): Promise { + console.log('@Processor(NOTIFICATION_SCHEDULER)'); + const values = Object.values(ExpiresInDuration); let notificationsArray = ( await Promise.all( From 1d4c318c41170c47c10568dce7451de79a5afd80 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Sun, 25 Jan 2026 13:04:07 -0800 Subject: [PATCH 62/71] add subsription to payemntDto --- apps/libs/Business/dto/response/response-payment.dto.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/libs/Business/dto/response/response-payment.dto.ts b/apps/libs/Business/dto/response/response-payment.dto.ts index 63f56e29..09e1e25e 100644 --- a/apps/libs/Business/dto/response/response-payment.dto.ts +++ b/apps/libs/Business/dto/response/response-payment.dto.ts @@ -1,7 +1,10 @@ import { Expose } from 'class-transformer'; import { Payment } from '../../../../../apps/business/src/infrastructure/entity/payment.entity'; +import { Subscription } from 'apps/business/src/infrastructure/entity/subscription.entity'; export class ResponsePaymentDto extends Payment { @Expose() expiresAt: Date; + @Expose() + subscription: Subscription; } From 48f86db9c280674e75f905b88846b7e619adf209 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Sun, 25 Jan 2026 13:34:22 -0800 Subject: [PATCH 63/71] fff --- .../repository/query/business-query.repository.ts | 8 ++++---- apps/libs/Business/dto/response/response-payment.dto.ts | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/business/src/infrastructure/repository/query/business-query.repository.ts b/apps/business/src/infrastructure/repository/query/business-query.repository.ts index ced1435f..3a1dd2c6 100644 --- a/apps/business/src/infrastructure/repository/query/business-query.repository.ts +++ b/apps/business/src/infrastructure/repository/query/business-query.repository.ts @@ -74,10 +74,10 @@ export class BusinessQueryRepository items: plainToInstance( ResponsePaymentDto, payments[0].map((payment) => { - if (payment.subscription) { - payment['expiresAt'] = payment.subscription.expiresAt; - delete payment.subscription; - } + // if (payment.subscription) { + // payment['expiresAt'] = payment.subscription.expiresAt; + // delete payment.subscription; + // } return payment; }), ), diff --git a/apps/libs/Business/dto/response/response-payment.dto.ts b/apps/libs/Business/dto/response/response-payment.dto.ts index 09e1e25e..64c59785 100644 --- a/apps/libs/Business/dto/response/response-payment.dto.ts +++ b/apps/libs/Business/dto/response/response-payment.dto.ts @@ -1,4 +1,4 @@ -import { Expose } from 'class-transformer'; +import { Expose, Type } from 'class-transformer'; import { Payment } from '../../../../../apps/business/src/infrastructure/entity/payment.entity'; import { Subscription } from 'apps/business/src/infrastructure/entity/subscription.entity'; @@ -6,5 +6,6 @@ export class ResponsePaymentDto extends Payment { @Expose() expiresAt: Date; @Expose() + @Type(() => Subscription) subscription: Subscription; } From c83c79f88ba55c6b76de02417baba5e072a5396d Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Mon, 26 Jan 2026 12:20:39 -0800 Subject: [PATCH 64/71] feat: save notif --- .../command/save-subscribtion.handler.ts | 20 +++++++++---------- apps/business/src/business.module.ts | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/apps/business/src/application/command/save-subscribtion.handler.ts b/apps/business/src/application/command/save-subscribtion.handler.ts index 63742c54..6b6ad5ba 100644 --- a/apps/business/src/application/command/save-subscribtion.handler.ts +++ b/apps/business/src/application/command/save-subscribtion.handler.ts @@ -33,16 +33,16 @@ export class SaveSubscriptionHandler const key = getNotificationKey(subscription, notification); // todo uncomment notif - // await this.notificationGateway.saveNotification( - // key, - // notification, - // 2629746000, - // ); - // await this.notificationGateway.send( - // notification, - // WebsocketEvents.SubscriptionActive, - // 30000, - // ); + await this.notificationGateway.saveNotification( + key, + notification, + 2629746000, + ); + await this.notificationGateway.send( + notification, + WebsocketEvents.SubscriptionActive, + 30000, + ); return subscription; } } diff --git a/apps/business/src/business.module.ts b/apps/business/src/business.module.ts index 1b333285..1de5a52f 100644 --- a/apps/business/src/business.module.ts +++ b/apps/business/src/business.module.ts @@ -64,6 +64,7 @@ import { BullModule } from '@nestjs/bullmq'; import { NotificationsProducer } from './notifications-producer.service'; import { NotificationConsumer } from './notifications-consumer.service'; import { DatabaseModule } from '../../../apps/libs/common/database/database.module'; +import { CancelSubscriptionCommand } from './application/command/cancel-subscription.handler'; const getEnvFilePath = (env: EnvironmentsTypes) => { const defaultEnvFilePath = ['apps/business/src/.env.development']; @@ -157,6 +158,7 @@ export const NOTIFICATION_SCHEDULER = 'NOTIFICATION_SCHEDULER'; SubscriptionUpdatedCommand, SubscriptionUpdatedHandler, SubscriptionExpiredCommand, + CancelSubscriptionCommand, SubscriptionExpiredHandler, GetUserNotificationsQuery, ReadNotificationHandler, From 3e172f8629978c6e8835c7cb8a8c87350f14f78a Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Mon, 26 Jan 2026 13:09:55 -0800 Subject: [PATCH 65/71] fff --- apps/business/src/business.module.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/business/src/business.module.ts b/apps/business/src/business.module.ts index 1de5a52f..ae8b18ea 100644 --- a/apps/business/src/business.module.ts +++ b/apps/business/src/business.module.ts @@ -64,7 +64,10 @@ import { BullModule } from '@nestjs/bullmq'; import { NotificationsProducer } from './notifications-producer.service'; import { NotificationConsumer } from './notifications-consumer.service'; import { DatabaseModule } from '../../../apps/libs/common/database/database.module'; -import { CancelSubscriptionCommand } from './application/command/cancel-subscription.handler'; +import { + CancelSubscriptionCommand, + CancelSubscriptionHandler, +} from './application/command/cancel-subscription.handler'; const getEnvFilePath = (env: EnvironmentsTypes) => { const defaultEnvFilePath = ['apps/business/src/.env.development']; @@ -158,7 +161,7 @@ export const NOTIFICATION_SCHEDULER = 'NOTIFICATION_SCHEDULER'; SubscriptionUpdatedCommand, SubscriptionUpdatedHandler, SubscriptionExpiredCommand, - CancelSubscriptionCommand, + CancelSubscriptionHandler, SubscriptionExpiredHandler, GetUserNotificationsQuery, ReadNotificationHandler, From 17b06792bf38a765c84a943452c36a55f7b5a7a4 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Mon, 26 Jan 2026 14:51:38 -0800 Subject: [PATCH 66/71] =?UTF-8?q?=D0=B5=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/business/src/business-command.service.ts | 2 ++ apps/libs/common/notifications/notifications.service.ts | 3 +++ 2 files changed, 5 insertions(+) diff --git a/apps/business/src/business-command.service.ts b/apps/business/src/business-command.service.ts index f4c96f29..fbcb210b 100644 --- a/apps/business/src/business-command.service.ts +++ b/apps/business/src/business-command.service.ts @@ -398,6 +398,8 @@ export class BusinessCommandService { } async updateNotification(notificationId: string) { + console.log('updateNotification ~ updateNotification:'); + await this.notificationGateway.createIndex(); return await this.notificationGateway.updateNotification(notificationId); } } diff --git a/apps/libs/common/notifications/notifications.service.ts b/apps/libs/common/notifications/notifications.service.ts index 42d44669..c01afd8c 100644 --- a/apps/libs/common/notifications/notifications.service.ts +++ b/apps/libs/common/notifications/notifications.service.ts @@ -178,7 +178,9 @@ export class NotificationsService implements INotificationsService { process.env.NODE_ENV !== EnvironmentMode.TESTING ? 'notifications:Idx' : 'dev:notifications:Idx'; + console.log('getNotificationById ~ index:', index); notificationId = notificationId.replaceAll('-', '\\-'); + console.log('getNotificationById ~ notificationId:', notificationId); const notification = await this.redisClient.call( 'FT.SEARCH', index, @@ -197,6 +199,7 @@ export class NotificationsService implements INotificationsService { async updateNotification(notificationId: string): Promise { const notification = await this.getNotificationById(notificationId); + console.log('updateNotification ~ notification:', notification); const readedAt = new Date().getTime(); await this.redisClient.hset(notification[1], 'readAt', readedAt); } From c46ee053c5046f1215aee3015d6c33aac03b748c Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Mon, 26 Jan 2026 15:11:54 -0800 Subject: [PATCH 67/71] fixed read notif --- apps/business/src/business-command.service.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/business/src/business-command.service.ts b/apps/business/src/business-command.service.ts index fbcb210b..f4c96f29 100644 --- a/apps/business/src/business-command.service.ts +++ b/apps/business/src/business-command.service.ts @@ -398,8 +398,6 @@ export class BusinessCommandService { } async updateNotification(notificationId: string) { - console.log('updateNotification ~ updateNotification:'); - await this.notificationGateway.createIndex(); return await this.notificationGateway.updateNotification(notificationId); } } From f7374b6bc86e43646b802d06fe637c002f370868 Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Thu, 29 Jan 2026 21:58:17 -0800 Subject: [PATCH 68/71] feat: notif works --- apps/business/src/api/business.controller.ts | 4 + .../command/read-notification.command.ts | 2 +- .../command/save-subscribtion.handler.ts | 8 +- .../command/subscription-updated.handler.ts | 8 +- .../query/get-user-notifications.handler.ts | 6 +- apps/business/src/business-command.service.ts | 8 +- apps/business/src/business-query.service.ts | 6 +- apps/business/src/business.module.ts | 43 ++-- apps/business/src/notifications.service.ts | 206 ++++++++++++++++++ apps/business/src/payment/payment.factory.ts | 2 +- apps/gate/src/business/business.controller.ts | 4 +- apps/gate/src/business/business.module.ts | 33 ++- apps/gate/src/business/business.service.ts | 38 ++-- .../business/constants/business-sse.enum.ts | 4 + .../constants/expires-in-duration.enum.ts | 5 + .../constants/websocket.event.enum.ts | 4 + .../helper/create-array-from-object.helper.ts | 13 ++ .../helper/get-notification-key.helper.ts | 14 ++ .../helper/get-subscription-price.helper.ts | 15 ++ .../notifications-consumer.service.ts | 55 +++++ .../notifications-producer.service.ts | 39 ++++ apps/gate/src/main.ts | 2 +- apps/index.ts | 125 +++++++++++ .../dto/response/response-notification.dto.ts | 2 + .../helper/socket-auth.helper.ts | 4 +- .../interfaces/notification.interface.ts | 1 + .../notifications/notifications.gateway.ts | 32 ++- .../notifications/notifications.module.ts | 4 +- .../notifications/notifications.service.ts | 51 +++-- package.json | 1 + yarn.lock | 22 +- 31 files changed, 681 insertions(+), 80 deletions(-) create mode 100644 apps/business/src/notifications.service.ts create mode 100644 apps/gate/src/business/constants/business-sse.enum.ts create mode 100644 apps/gate/src/business/constants/expires-in-duration.enum.ts create mode 100644 apps/gate/src/business/constants/websocket.event.enum.ts create mode 100644 apps/gate/src/business/helper/create-array-from-object.helper.ts create mode 100644 apps/gate/src/business/helper/get-notification-key.helper.ts create mode 100644 apps/gate/src/business/helper/get-subscription-price.helper.ts create mode 100644 apps/gate/src/business/notifications-consumer.service.ts create mode 100644 apps/gate/src/business/notifications-producer.service.ts create mode 100644 apps/index.ts diff --git a/apps/business/src/api/business.controller.ts b/apps/business/src/api/business.controller.ts index 7aa1d60a..df045a35 100644 --- a/apps/business/src/api/business.controller.ts +++ b/apps/business/src/api/business.controller.ts @@ -64,6 +64,10 @@ export class BusinessController { @Body('subscriptionId') subscriptionId: string, @Res() res: Response, ): Promise { + console.log( + '🚀 ~ BusinessController ~ paypalProcess ~ subscriptionId:', + subscriptionId, + ); const subscription = await this.commandBus.execute( new SaveSubscriptionCommand(subscriptionId), ); diff --git a/apps/business/src/application/command/read-notification.command.ts b/apps/business/src/application/command/read-notification.command.ts index 5a5b7b98..bd152791 100644 --- a/apps/business/src/application/command/read-notification.command.ts +++ b/apps/business/src/application/command/read-notification.command.ts @@ -14,6 +14,6 @@ export class ReadNotificationHandler ) {} async execute({ notificationId }: ReadNotificationCommand): Promise { - return await this.businessCommandService.updateNotification(notificationId); + // return await this.businessCommandService.updateNotification(notificationId); } } diff --git a/apps/business/src/application/command/save-subscribtion.handler.ts b/apps/business/src/application/command/save-subscribtion.handler.ts index 6b6ad5ba..344922a1 100644 --- a/apps/business/src/application/command/save-subscribtion.handler.ts +++ b/apps/business/src/application/command/save-subscribtion.handler.ts @@ -6,6 +6,7 @@ import { BusinessCommandService } from '../../business-command.service'; import { WebsocketEvents } from '../../constants/websocket.event.enum'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { v4 } from 'uuid'; +import { NotificationsService } from '../../notifications.service'; export class SaveSubscriptionCommand { constructor(public readonly id: string) {} } @@ -16,7 +17,7 @@ export class SaveSubscriptionHandler { constructor( private readonly businessCommandService: BusinessCommandService, - private readonly notificationGateway: NotificationsGateway, + private readonly notificationsService: NotificationsService, ) {} async execute({ id }: SaveSubscriptionCommand): Promise { @@ -29,16 +30,17 @@ export class SaveSubscriptionHandler userId: subscription.userId, expiresAt: new Date(subscription.expiresAt).getTime(), createdAt: new Date(subscription.createdAt).getTime(), + delivered: false, }; const key = getNotificationKey(subscription, notification); // todo uncomment notif - await this.notificationGateway.saveNotification( + await this.notificationsService.saveNotification( key, notification, 2629746000, ); - await this.notificationGateway.send( + await this.notificationsService.send( notification, WebsocketEvents.SubscriptionActive, 30000, diff --git a/apps/business/src/application/command/subscription-updated.handler.ts b/apps/business/src/application/command/subscription-updated.handler.ts index f329ee4a..d4a4346b 100644 --- a/apps/business/src/application/command/subscription-updated.handler.ts +++ b/apps/business/src/application/command/subscription-updated.handler.ts @@ -6,6 +6,7 @@ import { WebsocketEvents } from '../../constants/websocket.event.enum'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { v4 } from 'uuid'; import { Subscription } from '../../infrastructure/entity/subscription.entity'; +import { NotificationsService } from '../../notifications.service'; export class SubscriptionUpdatedCommand { constructor(public readonly subscriptionId: string) {} @@ -17,7 +18,7 @@ export class SubscriptionUpdatedHandler { constructor( private readonly businessCommandService: BusinessCommandService, - private readonly notificationGateway: NotificationsGateway, + private readonly notificationsService: NotificationsService, ) {} async execute({ subscriptionId, @@ -34,14 +35,15 @@ export class SubscriptionUpdatedHandler userId: subscription.userId, expiresAt: new Date(subscription.expiresAt).getTime(), createdAt: new Date(subscription.createdAt).getTime(), + delivered: false, }; const key = getNotificationKey(subscription, notification); - await this.notificationGateway.saveNotification( + await this.notificationsService.saveNotification( key, notification, 2629746000, ); - await this.notificationGateway.send( + await this.notificationsService.send( notification, WebsocketEvents.SubscriptionActive, 30000, diff --git a/apps/business/src/application/query/get-user-notifications.handler.ts b/apps/business/src/application/query/get-user-notifications.handler.ts index e6e051b1..e1229471 100644 --- a/apps/business/src/application/query/get-user-notifications.handler.ts +++ b/apps/business/src/application/query/get-user-notifications.handler.ts @@ -1,6 +1,6 @@ import { INotification } from '../../../../../apps/libs/common/notifications/interfaces/notification.interface'; -import { NotificationsGateway } from '../../../../../apps/libs/common/notifications/notifications.gateway'; import { IQueryHandler, QueryHandler } from '@nestjs/cqrs'; +import { NotificationsService } from '../../notifications.service'; export class GetUserNotificationsQuery { constructor(public readonly userId: string) {} @@ -10,11 +10,11 @@ export class GetUserNotificationsQuery { export class GetUserNotificationsHandler implements IQueryHandler { - constructor(private readonly notificationGateway: NotificationsGateway) {} + constructor(private readonly notificationsService: NotificationsService) {} async execute({ userId, }: GetUserNotificationsQuery): Promise { - return await this.notificationGateway.getUserNotifications(userId); + return await this.notificationsService.getUserNotifications(userId); } } diff --git a/apps/business/src/business-command.service.ts b/apps/business/src/business-command.service.ts index f4c96f29..12d5c2a3 100644 --- a/apps/business/src/business-command.service.ts +++ b/apps/business/src/business-command.service.ts @@ -28,7 +28,7 @@ export class BusinessCommandService { >, private readonly paymentService: IPaymentService, private readonly businessQueryService: BusinessQueryService, - private readonly notificationGateway: NotificationsGateway, + // private readonly notificationGateway: NotificationsGateway, @InjectRepository(Subscription) private readonly subscriptionCommandRepository: Repository, private readonly dataSource: DataSource, @@ -397,7 +397,7 @@ export class BusinessCommandService { return await this.businessCommandRepository.saveSubscription(subscription); } - async updateNotification(notificationId: string) { - return await this.notificationGateway.updateNotification(notificationId); - } + // async updateNotification(notificationId: string) { + // return await this.notificationGateway.updateNotification(notificationId); + // } } diff --git a/apps/business/src/business-query.service.ts b/apps/business/src/business-query.service.ts index 68268666..a52fcad7 100644 --- a/apps/business/src/business-query.service.ts +++ b/apps/business/src/business-query.service.ts @@ -26,8 +26,8 @@ export class BusinessQueryService { Subscription >, private readonly paymentService: IPaymentService, - private readonly notificationGateway: NotificationsGateway, - private readonly notificationsProducer: NotificationsProducer, + // private readonly notificationGateway: NotificationsGateway, + // private readonly notificationsProducer: NotificationsProducer, ) {} async getPaymentServiceSubscription(id: string) { @@ -105,6 +105,6 @@ export class BusinessQueryService { } async getExpiresInNotifications(expiresIn: ExpiresInDuration): Promise { - return await this.notificationGateway.getExpiresInNotifications(expiresIn); + // return await this.notificationGateway.getExpiresInNotifications(expiresIn); } } diff --git a/apps/business/src/business.module.ts b/apps/business/src/business.module.ts index ae8b18ea..78dd49e6 100644 --- a/apps/business/src/business.module.ts +++ b/apps/business/src/business.module.ts @@ -68,6 +68,9 @@ import { CancelSubscriptionCommand, CancelSubscriptionHandler, } from './application/command/cancel-subscription.handler'; +import { NotificationsService } from './notifications.service'; +import { RedisModule } from 'apps/libs/common/redis/redis.module'; +import { JwtModule } from '@nestjs/jwt'; const getEnvFilePath = (env: EnvironmentsTypes) => { const defaultEnvFilePath = ['apps/business/src/.env.development']; @@ -80,23 +83,32 @@ export const NOTIFICATION_SCHEDULER = 'NOTIFICATION_SCHEDULER'; @Module({ imports: [ RequestContextModule, - NotificationsModule.register(), - BullModule.forRootAsync({ + // NotificationsModule.register(), + // BullModule.forRootAsync({ + // imports: [ConfigModule], + // inject: [ConfigService], + // useFactory: (configService: ConfigService) => ({ + // connection: { + // username: configService.get('REDIS_USER'), + // port: configService.get('REDIS_PORT'), + // host: configService.get('REDIS_HOST'), + // password: configService.get('REDIS_PASSWORD'), + // }, + // }), + // }), + // BullModule.registerQueue({ + // name: 'NOTIFICATION_SCHEDULER', + // prefix: 'scheduler:', + // }), + JwtModule.registerAsync({ imports: [ConfigModule], inject: [ConfigService], - useFactory: (configService: ConfigService) => ({ - connection: { - username: configService.get('REDIS_USER'), - port: configService.get('REDIS_PORT'), - host: configService.get('REDIS_HOST'), - password: configService.get('REDIS_PASSWORD'), - }, + useFactory: async (configService: ConfigService) => ({ + global: true, + secret: configService.get('JWT_SECRET'), }), }), - BullModule.registerQueue({ - name: 'NOTIFICATION_SCHEDULER', - prefix: 'scheduler:', - }), + RedisModule, CqrsModule, PaymentModule, ConfigModule.forRoot({ @@ -169,8 +181,9 @@ export const NOTIFICATION_SCHEDULER = 'NOTIFICATION_SCHEDULER'; GetUserNotificationsHandler, GetPaymentsHandler, GetPaymentsQuery, - NotificationsProducer, - NotificationConsumer, + NotificationsService, + // NotificationsProducer, + // NotificationConsumer, { provide: IBusinessCommandRepository, useClass: BusinessCommandRepository, diff --git a/apps/business/src/notifications.service.ts b/apps/business/src/notifications.service.ts new file mode 100644 index 00000000..952fe94c --- /dev/null +++ b/apps/business/src/notifications.service.ts @@ -0,0 +1,206 @@ +import { + ConflictException, + Inject, + Injectable, + NotFoundException, +} from '@nestjs/common'; +import { WsException } from '@nestjs/websockets'; +import { Socket } from 'socket.io'; +import Redis from 'ioredis'; +import { NotificationResponseDto } from 'apps/libs/Business/dto/response/response-notification.dto'; +import { ExpiresInDuration } from './constants/expires-in-duration.enum'; +import { WebsocketEvents } from './constants/websocket.event.enum'; +import { EnvironmentMode } from './settings/configuration'; +import { INotificationsService } from 'apps/libs/common/notifications/interfaces/notification-service.interface'; +import { INotification } from 'apps/libs/common/notifications/interfaces/notification.interface'; +import { REDIS_CLIENT } from 'apps/libs/common/redis/redis-client.factory'; + +@Injectable() +export class NotificationsService implements INotificationsService { + private connectedClients: Map = new Map(); + + constructor(@Inject(REDIS_CLIENT) private readonly redisClient: Redis) {} + + afterInit(server: any) { + this.redisClient.call(''); + } + + //todo* add addClient / removeClient + addClient(socket: Socket) {} + + removeClient(socket: Socket) {} + + handleDisconnect(socket: Socket) { + this.connectedClients.delete(socket.id); + + socket.on('disconnect', (err) => { + console.log('disconected err', err); + }); + } + + //HSET + async saveNotification( + key: string, + notification: INotification, + ttl?: number, + ): Promise { + const result = await this.redisClient.hset(key, notification); + ttl ? await this.redisClient.expire(key, ttl) : null; + return result; + } + + async getUserNotifications( + userId: string, + ): Promise { + let notificationsArray = []; + const match = `${ + process.env.NODE_ENV !== EnvironmentMode.DEVELOPMENT && + process.env.NODE_ENV !== EnvironmentMode.TESTING + ? '' + : 'dev:' + }notifications:user:${userId}:notification:*`; + + const stream = this.redisClient.scanStream({ + match: match, + }); + const promise = new Promise((res, rej) => { + stream.on('data', async (keys) => { + for (let i = 0; i < keys.length; i++) { + const notification = await this.redisClient.hgetall(keys[i]); + notificationsArray.push(notification); + } + }); + stream.on('end', () => { + res(notificationsArray); + }); + }); + await promise; + return notificationsArray.sort( + (a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), + ); + } + + async handleConnection(socket: Socket) { + this.connectedClients.set(socket.data.user, socket); + } + + async send( + notification: INotification, + event: WebsocketEvents, + delay: number, + ): Promise { + const socket = this.connectedClients.get(notification.userId); + if (socket) { + setTimeout(() => { + socket.emit(event, notification.message); + }, delay); + } + } + + async createIndex() { + try { + // await this.redisClient.call('FT.DROPINDEX', 'dev:notifications:Idx'); + + await this.redisClient.call( + 'FT.CREATE', + process.env.NODE_ENV !== EnvironmentMode.DEVELOPMENT && + process.env.NODE_ENV !== EnvironmentMode.TESTING + ? 'notifications:Idx' + : 'dev:notifications:Idx', + 'ON', + 'HASH', + 'PREFIX', + '1', + process.env.NODE_ENV !== EnvironmentMode.DEVELOPMENT && + process.env.NODE_ENV !== EnvironmentMode.TESTING + ? 'notifications' + : 'dev:notifications', + 'SCHEMA', + 'subscriptionId', + 'TAG', + 'id', + 'TAG', + 'userId', + 'TAG', + 'expiresAt', + 'NUMERIC', + 'SORTABLE', + ); + } catch (err) { + console.error('Error creating index:', err.message); + throw new WsException(err); + } + } + // todo return expiresAt - now === 7 + // todo when renew subscription(update) create new notification with the same subscriptionId + async getExpiresInNotifications(expiresInDuration: ExpiresInDuration) { + try { + const todayTimestamp = new Date().getTime(); + const results = await this.redisClient.call( + 'FT.AGGREGATE', + process.env.NODE_ENV !== EnvironmentMode.DEVELOPMENT && + process.env.NODE_ENV !== EnvironmentMode.TESTING + ? 'notifications:Idx' + : 'dev:notifications:Idx', + '*', + 'LOAD', + '7', + '@expiresAt', + '@message', + '@subscriptionId', + 'userId', + '@createdAt', + '@readAt', + '@id', + 'APPLY', + `(@expiresAt - ${todayTimestamp})`, + 'AS', + 'differ', // Search query + 'FILTER', + expiresInDuration === ExpiresInDuration.Day + ? `@differ > 0 && @differ < ${expiresInDuration + 86400 * 1000}` + : expiresInDuration === ExpiresInDuration.Week + ? `@differ > ${expiresInDuration} && @differ < ${expiresInDuration + 86400 * 1000}` + : expiresInDuration === ExpiresInDuration.Month + ? `@differ > ${expiresInDuration} && @differ < ${expiresInDuration + 86400 * 1000}` + : null, + ); + return results; + } catch (error) { + console.error('Error searching data:', error); + } + } + + async getNotificationById(notificationId: string): Promise { + const index = + process.env.NODE_ENV !== EnvironmentMode.DEVELOPMENT && + process.env.NODE_ENV !== EnvironmentMode.TESTING + ? 'notifications:Idx' + : 'dev:notifications:Idx'; + console.log('getNotificationById ~ index:', index); + notificationId = notificationId.replaceAll('-', '\\-'); + console.log('getNotificationById ~ notificationId:', notificationId); + const notification = await this.redisClient.call( + 'FT.SEARCH', + index, + `@id:{${notificationId}}`, + ); + if (!notification) + throw new NotFoundException( + 'NotificationsService error: notification not found', + ); + if (notification[2][7] !== '') + throw new ConflictException( + 'NotificationsService error: notification has already been read', + ); + return notification; + } + + async updateNotification(notificationId: string): Promise { + const notification = await this.getNotificationById(notificationId); + console.log('updateNotification ~ notification:', notification); + const readedAt = new Date().getTime(); + await this.redisClient.hset(notification[1], 'readAt', readedAt); + } +} diff --git a/apps/business/src/payment/payment.factory.ts b/apps/business/src/payment/payment.factory.ts index 592b6027..b63bd93a 100644 --- a/apps/business/src/payment/payment.factory.ts +++ b/apps/business/src/payment/payment.factory.ts @@ -1,4 +1,4 @@ -import { BadRequestException, Injectable, Scope } from '@nestjs/common'; +import { BadRequestException, Inject, Injectable, Scope } from '@nestjs/common'; import { PayPalService } from './payment-services/paypal/paypal.service'; import { StripeService } from './payment-services/stripe/stripe.service'; import { PaymentType } from '../../../../apps/libs/Business/constants/payment-type.enum'; diff --git a/apps/gate/src/business/business.controller.ts b/apps/gate/src/business/business.controller.ts index 0522cc0b..d712f6a0 100644 --- a/apps/gate/src/business/business.controller.ts +++ b/apps/gate/src/business/business.controller.ts @@ -129,7 +129,7 @@ export class BusinessController { @Req() req: Request, @Query('payment') payment: PaymentType, @Res() res: Response, - ): Promise { + ): Promise { if (req.body.event_type === PaypalEvents.BillingSubscriptionActivated) { const subscription = await this.businessService.paypalProccess( req.body.resource.id, @@ -144,7 +144,7 @@ export class BusinessController { console.log('🚀 ~ BusinessController ~ paypalProcess ~ page:', page); // res.redirect(301, page); // res.sendStatus(200); - return subscription; + return 200; } } diff --git a/apps/gate/src/business/business.module.ts b/apps/gate/src/business/business.module.ts index e6f480fe..f2801bdf 100644 --- a/apps/gate/src/business/business.module.ts +++ b/apps/gate/src/business/business.module.ts @@ -3,13 +3,37 @@ import { BusinessService } from './business.service'; import { BusinessController } from './business.controller'; import { HttpModule } from '@nestjs/axios'; import { GateService } from '../../../../apps/libs/gateService'; -import { NotificationsGateway } from './notifications.gateway'; import { JwtModule } from '@nestjs/jwt'; import { ConfigModule, ConfigService } from '@nestjs/config'; +import { NotificationsModule } from 'apps/libs/common/notifications/notifications.module'; +import { RequestContextModule } from 'nestjs-request-context'; +import { BullModule } from '@nestjs/bullmq'; +import { NotificationConsumer } from './notifications-consumer.service'; +import { NotificationsProducer } from './notifications-producer.service'; +import { ScheduleModule } from '@nestjs/schedule'; @Module({ imports: [ + RequestContextModule, + ScheduleModule.forRoot(), + BullModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (configService: ConfigService) => ({ + connection: { + username: configService.get('REDIS_USER'), + port: configService.get('REDIS_PORT'), + host: configService.get('REDIS_HOST'), + password: configService.get('REDIS_PASSWORD'), + }, + }), + }), + BullModule.registerQueue({ + name: 'NOTIFICATION_SCHEDULER', + prefix: 'scheduler:', + }), HttpModule, + NotificationsModule.register(), JwtModule.registerAsync({ inject: [ConfigService], imports: [ConfigModule], @@ -19,6 +43,11 @@ import { ConfigModule, ConfigService } from '@nestjs/config'; }), ], controllers: [BusinessController], - providers: [BusinessService, GateService, NotificationsGateway], + providers: [ + BusinessService, + GateService, + NotificationConsumer, + NotificationsProducer, + ], }) export class BusinessModule {} diff --git a/apps/gate/src/business/business.service.ts b/apps/gate/src/business/business.service.ts index 26087cb4..9e76b75f 100644 --- a/apps/gate/src/business/business.service.ts +++ b/apps/gate/src/business/business.service.ts @@ -10,10 +10,14 @@ import { PaymentType } from '../../../../apps/libs/Business/constants/payment-ty import { SubscribeDto } from '../../../../apps/libs/Business/dto/input/subscribe.dto'; import { GateService } from '../../../../apps/libs/gateService'; import { Injectable } from '@nestjs/common'; +import { NotificationsService } from 'apps/libs/common/notifications/notifications.service'; @Injectable() export class BusinessService { - constructor(private readonly gateService: GateService) {} + constructor( + private readonly gateService: GateService, + private readonly notificationsService: NotificationsService, + ) {} async subscribe( subscribeDto: SubscribeDto, @@ -32,9 +36,15 @@ export class BusinessService { subscriptionId: string, payment: PaymentType, ): Promise { + console.log('🚀 ~ BusinessService ~ paypalProccess ~ payment:', payment); + console.log( + '🚀 ~ BusinessService ~ paypalProccess ~ subscriptionId:', + subscriptionId, + ); const path = [HttpBusinessPath.PaypalProcess, `payment=${payment}`].join( '?', ); + console.log('🚀 ~ BusinessService ~ paypalProccess ~ path:', path); return await this.gateService.requestHttpServicePost( HttpServices.Business, path, @@ -160,20 +170,22 @@ export class BusinessService { } async getUserNotifications(userId: string): Promise { - const path = [HttpBusinessPath.GetNotifications, userId].join('/'); - return await this.gateService.requestHttpServiceGet( - HttpServices.Business, - path, - {}, - ); + // const path = [HttpBusinessPath.GetNotifications, userId].join('/'); + // return await this.gateService.requestHttpServiceGet( + // HttpServices.Business, + // path, + // {}, + // ); + return await this.notificationsService.getUserNotifications(userId); } async readNotification(notificationId: string): Promise { - return await this.gateService.requestHttpServicePatch( - HttpServices.Business, - HttpBusinessPath.ReadNotification, - { notificationId }, - {}, - ); + // return await this.gateService.requestHttpServicePatch( + // HttpServices.Business, + // HttpBusinessPath.ReadNotification, + // { notificationId }, + // {}, + // ); + return await this.notificationsService.updateNotification(notificationId); } } diff --git a/apps/gate/src/business/constants/business-sse.enum.ts b/apps/gate/src/business/constants/business-sse.enum.ts new file mode 100644 index 00000000..93ad8748 --- /dev/null +++ b/apps/gate/src/business/constants/business-sse.enum.ts @@ -0,0 +1,4 @@ +export enum BusinessSse { + PaymentSuccess = 'paymentSuccess', + PaymentError = 'paymentError', +} diff --git a/apps/gate/src/business/constants/expires-in-duration.enum.ts b/apps/gate/src/business/constants/expires-in-duration.enum.ts new file mode 100644 index 00000000..3454ddb7 --- /dev/null +++ b/apps/gate/src/business/constants/expires-in-duration.enum.ts @@ -0,0 +1,5 @@ +export enum ExpiresInDuration { + Day = 86400 * 1000, + Week = 86400 * 7 * 1000, + Month = 86400 * 30 * 1000, +} diff --git a/apps/gate/src/business/constants/websocket.event.enum.ts b/apps/gate/src/business/constants/websocket.event.enum.ts new file mode 100644 index 00000000..57196d0e --- /dev/null +++ b/apps/gate/src/business/constants/websocket.event.enum.ts @@ -0,0 +1,4 @@ +export enum WebsocketEvents { + SubscriptionActive = 'subscription.active', + DaysToExpires = 'days.to.expires', +} diff --git a/apps/gate/src/business/helper/create-array-from-object.helper.ts b/apps/gate/src/business/helper/create-array-from-object.helper.ts new file mode 100644 index 00000000..e027e10d --- /dev/null +++ b/apps/gate/src/business/helper/create-array-from-object.helper.ts @@ -0,0 +1,13 @@ +export const createObjectFromArrayReduce = (arr: any) => { + for (let i = 1; i < arr.length; i++) { + arr[i] = arr[i].reduce((acc, current, index, array) => { + if (index % 2 === 0 && index + 1 < array.length) { + acc[current] = array[index + 1]; + } else if (index % 2 === 0 && index + 1 >= array.length) { + acc[current] = undefined; + } + return acc; + }, {}); + } + return arr; +}; diff --git a/apps/gate/src/business/helper/get-notification-key.helper.ts b/apps/gate/src/business/helper/get-notification-key.helper.ts new file mode 100644 index 00000000..c9a3a347 --- /dev/null +++ b/apps/gate/src/business/helper/get-notification-key.helper.ts @@ -0,0 +1,14 @@ +import { INotification } from 'apps/libs/common/notifications/interfaces/notification.interface'; +import { EnvironmentMode } from '../../settings/configuration'; + +export const getNotificationKey = ( + subscription: any, + notification: INotification, +): string => { + return `${ + process.env.NODE_ENV !== EnvironmentMode.DEVELOPMENT && + process.env.NODE_ENV !== EnvironmentMode.TESTING + ? '' + : 'dev:' + }notifications:user:${subscription.userId}:notification:${notification.id}`; +}; diff --git a/apps/gate/src/business/helper/get-subscription-price.helper.ts b/apps/gate/src/business/helper/get-subscription-price.helper.ts new file mode 100644 index 00000000..dd809620 --- /dev/null +++ b/apps/gate/src/business/helper/get-subscription-price.helper.ts @@ -0,0 +1,15 @@ +import { BadRequestException } from '@nestjs/common'; +import { SubscriptionType } from 'apps/libs/Business/constants/subscription-type.enum'; + +export function getSubscriptionPrice(subscriptionType: SubscriptionType) { + switch (subscriptionType) { + case SubscriptionType.OneDay: + return 10; + case SubscriptionType.SevenDays: + return 50; + case SubscriptionType.Month: + return 100; + default: + throw new BadRequestException('error: invalid subscriptionPrice value'); + } +} diff --git a/apps/gate/src/business/notifications-consumer.service.ts b/apps/gate/src/business/notifications-consumer.service.ts new file mode 100644 index 00000000..05c464ae --- /dev/null +++ b/apps/gate/src/business/notifications-consumer.service.ts @@ -0,0 +1,55 @@ +import { NotificationsGateway } from 'apps/libs/common/notifications/notifications.gateway'; +import { createObjectFromArrayReduce } from './helper/create-array-from-object.helper'; +import { ExpiresInDuration } from './constants/expires-in-duration.enum'; +import { WebsocketEvents } from './constants/websocket.event.enum'; +import { Processor, WorkerHost } from '@nestjs/bullmq'; +import { Job } from 'bullmq'; + +@Processor('NOTIFICATION_SCHEDULER') +export class NotificationConsumer extends WorkerHost { + constructor(private readonly notificationGateway: NotificationsGateway) { + super(); + } + + async process(job: Job, token?: string): Promise { + console.log('@Processor(NOTIFICATION_SCHEDULER)'); + + const values = Object.values(ExpiresInDuration); + let notificationsArray = ( + await Promise.all( + values.map(async (item, index) => { + if (index < Object.values(ExpiresInDuration).length / 2) { + const result = + await this.notificationGateway.getExpiresInNotifications( + ExpiresInDuration[item], + ); + return result; + } + }), + ) + ).filter((item) => item !== undefined); + console.log( + '🚀 ~ NotificationConsumer ~ process ~ notificationsArray:', + notificationsArray, + ); + + await Promise.all( + notificationsArray.map(async (item) => { + const notificationsObjectsAray = createObjectFromArrayReduce(item); + delete notificationsObjectsAray.differ; + notificationsObjectsAray.shift(); + for (const item of notificationsObjectsAray) { + console.log('🚀 ~ NotificationConsumer ~ process ~ item:', item); + if (item.delivered === 'false') { + await this.notificationGateway.send( + item, + WebsocketEvents.DaysToExpires, + 0, + ); + } + } + }), + ); + return {}; + } +} diff --git a/apps/gate/src/business/notifications-producer.service.ts b/apps/gate/src/business/notifications-producer.service.ts new file mode 100644 index 00000000..3f058535 --- /dev/null +++ b/apps/gate/src/business/notifications-producer.service.ts @@ -0,0 +1,39 @@ +import { Injectable, OnApplicationBootstrap } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { InjectQueue } from '@nestjs/bullmq'; +import { Queue } from 'bullmq'; +import { Cron, CronExpression } from '@nestjs/schedule'; +import { NotificationsGateway } from 'apps/libs/common/notifications/notifications.gateway'; + +@Injectable() +export class NotificationsProducer implements OnApplicationBootstrap { + constructor( + @InjectQueue('NOTIFICATION_SCHEDULER') private notificationsQueue: Queue, + private readonly configService: ConfigService, + private readonly notificationGateway: NotificationsGateway, + ) { + console.log('NotificationsProducer starts'); + } + + async onApplicationBootstrap() { + console.log('TIME_PERIOD', this.configService.get('TIME_PERIOD')); + + await this.sendNotificationToQueue(); + } + + @Cron(CronExpression.EVERY_10_SECONDS) + async sendNotificationToQueue() { + console.log('sendNotificationToQueue...'); + + await this.notificationsQueue.add( + 'NOTIFICATION_SCHEDULER', + {}, + { + // repeat: { every: this.configService.get('TIME_PERIOD') }, + removeOnComplete: true, + removeOnFail: true, + }, + ); + console.log('after'); + } +} diff --git a/apps/gate/src/main.ts b/apps/gate/src/main.ts index 8657ed8a..633ab974 100644 --- a/apps/gate/src/main.ts +++ b/apps/gate/src/main.ts @@ -51,7 +51,7 @@ async function bootstrap() { const { port, env } = applyAppSettings(app); useContainer(app.select(AppModule), { fallbackOnErrors: true }); - await app.init(); + // await app.init(); await app.listen(port, () => { console.log('App starting service GATE listen port: ', port, 'ENV: ', env); }); diff --git a/apps/index.ts b/apps/index.ts new file mode 100644 index 00000000..917a90c1 --- /dev/null +++ b/apps/index.ts @@ -0,0 +1,125 @@ +export {}; + +// Метод для получения списка сотрудников через API1 +const getEmployees = async () => { + // Эмулируем вызов API через возврат промиса + return Promise.resolve([ + { id: 1, name: 'Вася', department: 'Frontend' }, + { id: 2, name: 'Петя', department: 'Backend' }, + { id: 3, name: 'Дима', department: 'Frontend' }, + { id: 4, name: 'Оля', department: 'Backend' }, + { id: 5, name: 'Саша', department: 'Frontend' }, + { id: 6, name: 'Олег', department: 'Testing' }, + ]); +}; + +// Зарплаты сотрудников из API +const getEmployeeSalary = async (employeeId: number) => { + // Эмулируем вызов API через возврат промиса + const salaryByEmployeeId = { + '1': 10000, + '2': 12000, + '3': 10500, + '4': 15000, + '5': 8000, + '6': 9000, + }; + + return Promise.resolve(salaryByEmployeeId[employeeId]); +}; + +// ********************************************************* + +const getAllEmployees = async () => { + const result = await getEmployees(); + const backend = result.filter((item) => { + if (item.department === 'Backend') return item; + }); + console.log('getAllEmployees', backend); +}; + +const getMaxSalary = async () => { + let maxSalary = 0; + let i = 1; + let process = true; + while (process) { + try { + const salary = await getEmployeeSalary(i); + if (!salary) process = false; + + if (salary === 0) ++i; + + if (salary >= maxSalary) { + maxSalary = salary; + ++i; + } else ++i; + } catch (error) { + process = false; + return maxSalary; + } + } + return maxSalary; +}; + +const getF = async () => { + let totalDepartmentsSpends = []; + + for (let i = 1; i <= 6; i++) { + const salary = await getEmployeeSalary(i); + totalDepartmentsSpends.push([i, salary]); + } + + const sortedSalary = totalDepartmentsSpends.sort((a, b) => b[1] - a[1]); + + const employees = await getEmployees(); + const names = sortedSalary.map((salary) => { + const empl = employees.find((emp) => { + if (emp.id === salary[0]) { + return emp; + } + }); + return { id: empl.id, name: empl.name, salary: salary[1] }; + }); + console.log('🚀 ~ getF ~ names:', names); +}; + +const superLastTask = async () => { + const Departments = ['Frontend', 'Backend', 'Testing']; + + const employees = await getEmployees(); + let totalDepartmentsSpends = []; + for (let dep of Departments) { + let depRecord = { department: dep, spends: 0 }; + + for (let empl of employees) { + if (empl.department === dep) { + const spend = await getEmployeeSalary(empl.id); + depRecord.spends += spend; + } + } + + totalDepartmentsSpends.push(depRecord); + } + console.log('totalDepartmentsSpends:', totalDepartmentsSpends); + + const totalEmployees = Departments.map((dep) => { + const totalEmployees = employees.reduce((acc, cur, i) => { + return cur.department === dep ? (acc += 1) : acc; + }, 0); + return { department: dep, totalEmployees }; + }); + + console.log('totalEmployees:', totalEmployees); +}; + +// await superLastTask(); + +// Дано: +// 1. Функция для получения списка сотрудников через API1 +// 2. Функция для получения зарплаты каждого сотрудника через API2 + +// Необходимо реализовать функции, которые вернут в качестве результата: +// 1. Список сотрудников только Backend +// 2. Размер максимальной зарплаты +// 3. Список имен сотрудников, отсортированный по размеру зарплаты +// 4. Статистика по каждому отделу: сумма затрат, количество сотрудников, средняя з/п, максимальная з/п diff --git a/apps/libs/Business/dto/response/response-notification.dto.ts b/apps/libs/Business/dto/response/response-notification.dto.ts index b32725ef..b8c8f29a 100644 --- a/apps/libs/Business/dto/response/response-notification.dto.ts +++ b/apps/libs/Business/dto/response/response-notification.dto.ts @@ -15,4 +15,6 @@ export class NotificationResponseDto implements INotification { expiresAt: number; @ApiProperty({ description: 'timestamp' }) createdAt: number; + @ApiProperty({ description: 'boolean' }) + delivered: boolean; } diff --git a/apps/libs/common/notifications/helper/socket-auth.helper.ts b/apps/libs/common/notifications/helper/socket-auth.helper.ts index acedc0e5..e7fcbe82 100644 --- a/apps/libs/common/notifications/helper/socket-auth.helper.ts +++ b/apps/libs/common/notifications/helper/socket-auth.helper.ts @@ -10,8 +10,8 @@ export const socketAuthMiddleware = ( ): SocketMiddleware => { return async (socket: Socket, next) => { try { - console.log('authorization:', socket.handshake.auth?.authorization); - const token = socket.handshake.auth?.authorization; + console.log('authorization:', socket.handshake.headers.authorization); + const token = socket.handshake.headers.authorization; console.log('🚀 ~ socketAuthMiddleware ~ token:', token); if (!token) next(new WsException('Socket Unauthorized Exception')); const payload = jwtService.verify(token); diff --git a/apps/libs/common/notifications/interfaces/notification.interface.ts b/apps/libs/common/notifications/interfaces/notification.interface.ts index fa189219..7999354e 100644 --- a/apps/libs/common/notifications/interfaces/notification.interface.ts +++ b/apps/libs/common/notifications/interfaces/notification.interface.ts @@ -6,4 +6,5 @@ export interface INotification { createdAt: number; readAt: number; expiresAt: number; + delivered: boolean; } diff --git a/apps/libs/common/notifications/notifications.gateway.ts b/apps/libs/common/notifications/notifications.gateway.ts index 83b718eb..7437938a 100644 --- a/apps/libs/common/notifications/notifications.gateway.ts +++ b/apps/libs/common/notifications/notifications.gateway.ts @@ -13,11 +13,12 @@ import { WebsocketEvents } from '../../../../apps/business/src/constants/websock import { INotificationsService } from './interfaces/notification-service.interface'; import { INotification } from './interfaces/notification.interface'; import { NotificationsService } from './notifications.service'; -import { Injectable, OnModuleInit } from '@nestjs/common'; +import { OnModuleInit } from '@nestjs/common'; import { Socket, Server } from 'socket.io'; +import { socketAuthMiddleware } from './helper/socket-auth.helper'; +import { JwtService } from '@nestjs/jwt'; -@WebSocketGateway() -@Injectable() +@WebSocketGateway(3007, { namespace: 'notifications' }) export class NotificationsGateway implements INotificationsService, @@ -25,11 +26,16 @@ export class NotificationsGateway OnGatewayConnection, OnGatewayInit { - private connectedClients: Map = new Map(); - private socket: Socket; + private connectedSockets: string[] = []; + @WebSocketServer() server: Server; // The Socket.IO server instance - constructor(private readonly notificationsService: NotificationsService) {} + constructor( + private readonly notificationsService: NotificationsService, + private readonly jwtService: JwtService, + ) { + console.log('WebSocketServer'); + } @SubscribeMessage('message') handleMessage( @@ -48,10 +54,20 @@ export class NotificationsGateway } afterInit(server: Server) { - console.log('sfterInit', server.sockets); + const authMiddleware = socketAuthMiddleware(this.jwtService); + server.use(authMiddleware); } - handleConnection(socket: Socket) {} + handleConnection(socket: Socket) { + console.log(`${socket.id} connected`); + //todo* make auth -> connectedSockets.push({socket.id, userId}) -> send to notifications -> add there to array + this.connectedSockets.push(socket.id); + this.notificationsService.addClient(socket); + this.server.emit('connectedSocket', { + userId: socket.data.user, + socketId: socket.id, + }); + } async saveNotification( key: string, diff --git a/apps/libs/common/notifications/notifications.module.ts b/apps/libs/common/notifications/notifications.module.ts index 6194e2d7..d5de38c1 100644 --- a/apps/libs/common/notifications/notifications.module.ts +++ b/apps/libs/common/notifications/notifications.module.ts @@ -22,8 +22,8 @@ export class NotificationsModule { }), }), ], - providers: [WsAuthAdapter, NotificationsService, NotificationsGateway], - exports: [NotificationsGateway, WsAuthAdapter], + providers: [NotificationsService, NotificationsGateway], + exports: [NotificationsGateway, NotificationsService], }; } } diff --git a/apps/libs/common/notifications/notifications.service.ts b/apps/libs/common/notifications/notifications.service.ts index c01afd8c..870a3cee 100644 --- a/apps/libs/common/notifications/notifications.service.ts +++ b/apps/libs/common/notifications/notifications.service.ts @@ -26,9 +26,13 @@ export class NotificationsService implements INotificationsService { } //todo* add addClient / removeClient - addClient(socket: Socket) {} + addClient(socket: Socket) { + this.connectedClients.set(socket.data.user, socket); + } - removeClient(socket: Socket) {} + removeClient(socket: Socket) { + this.connectedClients.delete(socket.data.user); + } handleDisconnect(socket: Socket) { this.connectedClients.delete(socket.id); @@ -83,6 +87,7 @@ export class NotificationsService implements INotificationsService { async handleConnection(socket: Socket) { this.connectedClients.set(socket.data.user, socket); + console.log('connectedClients', this.connectedClients); } async send( @@ -91,10 +96,12 @@ export class NotificationsService implements INotificationsService { delay: number, ): Promise { const socket = this.connectedClients.get(notification.userId); + console.log('🚀 ~ NotificationsService ~ send ~ socket:', socket.id); if (socket) { setTimeout(() => { socket.emit(event, notification.message); }, delay); + await this.updateNotificationDelivery(notification.id); } } @@ -135,9 +142,10 @@ export class NotificationsService implements INotificationsService { // todo return expiresAt - now === 7 // todo when renew subscription(update) create new notification with the same subscriptionId async getExpiresInNotifications(expiresInDuration: ExpiresInDuration) { + // await this.createIndex(); try { const todayTimestamp = new Date().getTime(); - const results = await this.redisClient.call( + let results = await this.redisClient.call( 'FT.AGGREGATE', process.env.NODE_ENV !== EnvironmentMode.DEVELOPMENT && process.env.NODE_ENV !== EnvironmentMode.TESTING @@ -145,27 +153,31 @@ export class NotificationsService implements INotificationsService { : 'dev:notifications:Idx', '*', 'LOAD', - '7', + '8', '@expiresAt', '@message', '@subscriptionId', - 'userId', + '@userId', '@createdAt', '@readAt', '@id', + '@delivered', 'APPLY', + //todo! end of month has problems, usually use 86400 `(@expiresAt - ${todayTimestamp})`, 'AS', 'differ', // Search query 'FILTER', expiresInDuration === ExpiresInDuration.Day - ? `@differ > 0 && @differ < ${expiresInDuration + 86400 * 1000}` + ? //todo! end of month has problems, usually use 86400 + `@differ > 0 && @differ < ${expiresInDuration + 1164400 * 1000}` : expiresInDuration === ExpiresInDuration.Week ? `@differ > ${expiresInDuration} && @differ < ${expiresInDuration + 86400 * 1000}` : expiresInDuration === ExpiresInDuration.Month ? `@differ > ${expiresInDuration} && @differ < ${expiresInDuration + 86400 * 1000}` : null, ); + return results; } catch (error) { console.error('Error searching data:', error); @@ -178,22 +190,23 @@ export class NotificationsService implements INotificationsService { process.env.NODE_ENV !== EnvironmentMode.TESTING ? 'notifications:Idx' : 'dev:notifications:Idx'; - console.log('getNotificationById ~ index:', index); + notificationId = notificationId.replaceAll('-', '\\-'); - console.log('getNotificationById ~ notificationId:', notificationId); + const notification = await this.redisClient.call( 'FT.SEARCH', index, `@id:{${notificationId}}`, ); - if (!notification) - throw new NotFoundException( - 'NotificationsService error: notification not found', - ); - if (notification[2][7] !== '') - throw new ConflictException( - 'NotificationsService error: notification has already been read', - ); + + // if (!notification) + // throw new NotFoundException( + // 'NotificationsService error: notification not found', + // ); + // if (notification[2][8] !== '') + // throw new ConflictException( + // 'NotificationsService error: notification has already been read', + // ); return notification; } @@ -203,4 +216,10 @@ export class NotificationsService implements INotificationsService { const readedAt = new Date().getTime(); await this.redisClient.hset(notification[1], 'readAt', readedAt); } + + async updateNotificationDelivery(notificationId: string): Promise { + const notification = await this.getNotificationById(notificationId); + + await this.redisClient.hset(notification[1], 'delivered', 'true'); + } } diff --git a/package.json b/package.json index e329457c..45922551 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "@nestjs/microservices": "^11.1.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/platform-socket.io": "^11.1.6", + "@nestjs/schedule": "^6.1.0", "@nestjs/swagger": "^11.1.5", "@nestjs/typeorm": "^11.0.0", "@nestjs/websockets": "^11.1.6", diff --git a/yarn.lock b/yarn.lock index d733e760..dbd62400 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2092,6 +2092,13 @@ socket.io "4.8.1" tslib "2.8.1" +"@nestjs/schedule@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@nestjs/schedule/-/schedule-6.1.0.tgz#4653383c6aaf82e19754eae281ec1036da41febe" + integrity sha512-W25Ydc933Gzb1/oo7+bWzzDiOissE+h/dhIAPugA39b9MuIzBbLybuXpc1AjoQLczO3v0ldmxaffVl87W0uqoQ== + dependencies: + cron "4.3.5" + "@nestjs/schematics@^10.0.0", "@nestjs/schematics@^10.0.1": version "10.2.3" resolved "https://registry.yarnpkg.com/@nestjs/schematics/-/schematics-10.2.3.tgz#6053f43c5065b9e825cd08c4db1bf6bcbc9a6a62" @@ -3046,6 +3053,11 @@ resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== +"@types/luxon@~3.7.0": + version "3.7.1" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.7.1.tgz#ef51b960ff86801e4e2de80c68813a96e529d531" + integrity sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg== + "@types/methods@^1.1.4": version "1.1.4" resolved "https://registry.yarnpkg.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547" @@ -4466,6 +4478,14 @@ cron-parser@^4.9.0: dependencies: luxon "^3.2.1" +cron@4.3.5: + version "4.3.5" + resolved "https://registry.yarnpkg.com/cron/-/cron-4.3.5.tgz#53112ec0f5260722b89633ed72f005dc782864f1" + integrity sha512-hKPP7fq1+OfyCqoePkKfVq7tNAdFwiQORr4lZUHwrf0tebC65fYEeWgOrXOL6prn1/fegGOdTfrM6e34PJfksg== + dependencies: + "@types/luxon" "~3.7.0" + luxon "~3.7.0" + cross-env@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" @@ -7174,7 +7194,7 @@ lru-cache@^7.10.1, lru-cache@^7.14.1, lru-cache@^7.7.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== -luxon@^3.2.1: +luxon@^3.2.1, luxon@~3.7.0: version "3.7.2" resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.7.2.tgz#d697e48f478553cca187a0f8436aff468e3ba0ba" integrity sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew== From f69245a508594c32a12132a375c288c9b7145fba Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 30 Jan 2026 13:35:31 -0800 Subject: [PATCH 69/71] 3000 port websockets --- apps/libs/common/notifications/notifications.gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/libs/common/notifications/notifications.gateway.ts b/apps/libs/common/notifications/notifications.gateway.ts index 7437938a..81d3ef24 100644 --- a/apps/libs/common/notifications/notifications.gateway.ts +++ b/apps/libs/common/notifications/notifications.gateway.ts @@ -18,7 +18,7 @@ import { Socket, Server } from 'socket.io'; import { socketAuthMiddleware } from './helper/socket-auth.helper'; import { JwtService } from '@nestjs/jwt'; -@WebSocketGateway(3007, { namespace: 'notifications' }) +@WebSocketGateway(0, { namespace: 'notification-event' }) export class NotificationsGateway implements INotificationsService, From d6b23c62f0a89ad035086f93a615dc5b5d9bbfcb Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 30 Jan 2026 14:11:09 -0800 Subject: [PATCH 70/71] fff --- apps/libs/common/notifications/notifications.gateway.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/libs/common/notifications/notifications.gateway.ts b/apps/libs/common/notifications/notifications.gateway.ts index 81d3ef24..c5d0d112 100644 --- a/apps/libs/common/notifications/notifications.gateway.ts +++ b/apps/libs/common/notifications/notifications.gateway.ts @@ -18,7 +18,7 @@ import { Socket, Server } from 'socket.io'; import { socketAuthMiddleware } from './helper/socket-auth.helper'; import { JwtService } from '@nestjs/jwt'; -@WebSocketGateway(0, { namespace: 'notification-event' }) +@WebSocketGateway(0, { namespace: 'event/notification' }) export class NotificationsGateway implements INotificationsService, From fa8e33da77a73cc4fdb86c2fb70ebd03dc06d58c Mon Sep 17 00:00:00 2001 From: backtobackend226 Date: Fri, 30 Jan 2026 22:13:49 -0800 Subject: [PATCH 71/71] hgggg --- apps/gate/src/business/business.controller.ts | 1 + apps/gate/src/business/business.service.ts | 4 ++++ .../notifications/notifications.gateway.ts | 19 ++++++++++--------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/gate/src/business/business.controller.ts b/apps/gate/src/business/business.controller.ts index d712f6a0..fe1c3e30 100644 --- a/apps/gate/src/business/business.controller.ts +++ b/apps/gate/src/business/business.controller.ts @@ -142,6 +142,7 @@ export class BusinessController { let page = this.configService.get('PROFILE_SETTINGS_PAGE'); page = page.replace('replace', subscription['userId']); console.log('🚀 ~ BusinessController ~ paypalProcess ~ page:', page); + // await this.businessService.subscriptionActivatedEvent(subscription) // res.redirect(301, page); // res.sendStatus(200); return 200; diff --git a/apps/gate/src/business/business.service.ts b/apps/gate/src/business/business.service.ts index 9e76b75f..2a1d804e 100644 --- a/apps/gate/src/business/business.service.ts +++ b/apps/gate/src/business/business.service.ts @@ -169,6 +169,10 @@ export class BusinessService { ); } + // async subscriptionActivatedEvent(subscription: void) { + // await this + // } + async getUserNotifications(userId: string): Promise { // const path = [HttpBusinessPath.GetNotifications, userId].join('/'); // return await this.gateService.requestHttpServiceGet( diff --git a/apps/libs/common/notifications/notifications.gateway.ts b/apps/libs/common/notifications/notifications.gateway.ts index c5d0d112..61c4a202 100644 --- a/apps/libs/common/notifications/notifications.gateway.ts +++ b/apps/libs/common/notifications/notifications.gateway.ts @@ -2,6 +2,7 @@ import { ConnectedSocket, MessageBody, OnGatewayConnection, + OnGatewayDisconnect, OnGatewayInit, SubscribeMessage, WebSocketGateway, @@ -24,6 +25,7 @@ export class NotificationsGateway INotificationsService, OnModuleInit, OnGatewayConnection, + OnGatewayDisconnect, OnGatewayInit { private connectedSockets: string[] = []; @@ -58,15 +60,14 @@ export class NotificationsGateway server.use(authMiddleware); } - handleConnection(socket: Socket) { - console.log(`${socket.id} connected`); - //todo* make auth -> connectedSockets.push({socket.id, userId}) -> send to notifications -> add there to array - this.connectedSockets.push(socket.id); - this.notificationsService.addClient(socket); - this.server.emit('connectedSocket', { - userId: socket.data.user, - socketId: socket.id, - }); + handleConnection(@ConnectedSocket() client: Socket) { + this.notificationsService.addClient(client); + client.emit('connection', `${client.data.user} connected`); + } + + handleDisconnect(@ConnectedSocket() client: Socket) { + this.notificationsService.removeClient(client); + client.emit('disconnect', `${client.data.user} disconnected`); } async saveNotification(