diff --git a/server/apps/gateway/src/app.module.ts b/server/apps/gateway/src/app.module.ts index 4e63cc86..bf0d70f6 100644 --- a/server/apps/gateway/src/app.module.ts +++ b/server/apps/gateway/src/app.module.ts @@ -2,7 +2,8 @@ import { Module } from '@nestjs/common'; import { AuthModule } from './auth/auth.module'; import { UploadModule } from './upload/upload.module'; import { CoursesModule } from './courses/courses.module'; +import { PubSubModule } from './pubsub/pubsub.module'; @Module({ - imports: [AuthModule, UploadModule, CoursesModule], + imports: [AuthModule, UploadModule, CoursesModule,PubSubModule], }) export class AppModule {} diff --git a/server/apps/gateway/src/proto/upload.proto b/server/apps/gateway/src/proto/upload.proto index 2c6d61ba..a4748f17 100644 --- a/server/apps/gateway/src/proto/upload.proto +++ b/server/apps/gateway/src/proto/upload.proto @@ -1,7 +1,19 @@ syntax = "proto3"; -package googlecloudstorage; +package googlecloudstorage; // Este será el paquete común para ambos servicios +// Servicio PubSub +service PubSubService { + rpc Test (TestRequest) returns (TestResponse); +} + +message TestRequest {} + +message TestResponse { + string message = 1; +} + +// Servicio GoogleCloudStorage service GoogleCloudStorageService { rpc UploadFile (UploadFileRequest) returns (UploadFileResponse); } diff --git a/server/apps/gateway/src/pubsub/pubsub.gateway.controller.ts b/server/apps/gateway/src/pubsub/pubsub.gateway.controller.ts new file mode 100644 index 00000000..eaf5c009 --- /dev/null +++ b/server/apps/gateway/src/pubsub/pubsub.gateway.controller.ts @@ -0,0 +1,17 @@ +import { Controller, Get, Param } from '@nestjs/common'; +import { PubSubGatewayService } from './pubsub.gateway.service'; + +@Controller('pubsub') +export class PubSubGatewayController { + constructor(private readonly pubSubGatewayService: PubSubGatewayService) {} + + @Get('test') + async test() { + return this.pubSubGatewayService.test(); + } + + @Get('listen/:subscriptionName') + async listenForMessages(@Param('subscriptionName') subscriptionName: string) { + return this.pubSubGatewayService.listenForMessages(subscriptionName); + } +} \ No newline at end of file diff --git a/server/apps/gateway/src/pubsub/pubsub.gateway.service.ts b/server/apps/gateway/src/pubsub/pubsub.gateway.service.ts new file mode 100644 index 00000000..c0df97ba --- /dev/null +++ b/server/apps/gateway/src/pubsub/pubsub.gateway.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@nestjs/common'; +import { Inject } from '@nestjs/common'; +import { ClientGrpc } from '@nestjs/microservices'; +import { Observable } from 'rxjs'; + +interface PubSubServiceClient { + test(request: {}): Observable<{ message: string }>; + listenForMessages(request: { subscriptionName: string }): Observable<{ status: string, message: string }>; +} + +@Injectable() +export class PubSubGatewayService { + private pubSubService: PubSubServiceClient; + + constructor(@Inject('PUBSUB_SERVICE') private readonly client: ClientGrpc) {} + + onModuleInit() { + this.pubSubService = this.client.getService('PubSubService'); + } +t + async test() { + const result = await this.pubSubService.test({}).toPromise(); + return result.message; + } + + async listenForMessages(subscriptionName: string) { + const result = await this.pubSubService.listenForMessages({ subscriptionName }).toPromise(); + return result; + } +} \ No newline at end of file diff --git a/server/apps/gateway/src/pubsub/pubsub.module.ts b/server/apps/gateway/src/pubsub/pubsub.module.ts new file mode 100644 index 00000000..58984dad --- /dev/null +++ b/server/apps/gateway/src/pubsub/pubsub.module.ts @@ -0,0 +1,23 @@ +import { Module } from '@nestjs/common'; +import { ClientsModule, Transport } from '@nestjs/microservices'; +import { PubSubGatewayController } from './pubsub.gateway.controller'; +import { PubSubGatewayService } from './pubsub.gateway.service'; + +@Module({ + imports: [ + ClientsModule.register([ + { + name: 'PUBSUB_SERVICE', + transport: Transport.GRPC, + options: { + package: 'googlecloudstorage', + protoPath: 'src/proto/upload.proto', + url: '0.0.0.0:50051', + }, + }, + ]), + ], + controllers: [PubSubGatewayController], + providers: [PubSubGatewayService], +}) +export class PubSubModule {} \ No newline at end of file diff --git a/server/apps/uploadGCould/package-lock.json b/server/apps/uploadGCould/package-lock.json index 2eeec426..75d67b62 100644 --- a/server/apps/uploadGCould/package-lock.json +++ b/server/apps/uploadGCould/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@google-cloud/pubsub": "4.9.0", "@google-cloud/storage": "7.14.0", "@grpc/grpc-js": "1.8.17", "@grpc/proto-loader": "0.7.7", @@ -902,6 +903,15 @@ "node": ">=14.0.0" } }, + "node_modules/@google-cloud/precise-date": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@google-cloud/precise-date/-/precise-date-4.0.0.tgz", + "integrity": "sha512-1TUx3KdaU3cN7nfCdNf+UVqA/PSX29Cjcox3fZZBtINlRrXVTmUkQnCKv2MbBUbCopbK4olAT1IHl76uZyCiVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@google-cloud/projectify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz", @@ -920,6 +930,31 @@ "node": ">=14" } }, + "node_modules/@google-cloud/pubsub": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@google-cloud/pubsub/-/pubsub-4.9.0.tgz", + "integrity": "sha512-VLGRwWwjEnyC+NVEiScCRGfVBJzAw9fT5IM3YvC6mlEkv8llr5vcVsoDjv1EbE0P31I601RqlLXH7s6J9tqpfA==", + "license": "Apache-2.0", + "dependencies": { + "@google-cloud/paginator": "^5.0.0", + "@google-cloud/precise-date": "^4.0.0", + "@google-cloud/projectify": "^4.0.0", + "@google-cloud/promisify": "^4.0.0", + "@opentelemetry/api": "~1.9.0", + "@opentelemetry/semantic-conventions": "~1.26.0", + "arrify": "^2.0.0", + "extend": "^3.0.2", + "google-auth-library": "^9.3.0", + "google-gax": "^4.3.3", + "heap-js": "^2.2.0", + "is-stream-ended": "^0.1.4", + "lodash.snakecase": "^4.1.1", + "p-defer": "^3.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@google-cloud/storage": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.14.0.tgz", @@ -2224,6 +2259,24 @@ "npm": ">=5.0.0" } }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.26.0.tgz", + "integrity": "sha512-U9PJlOswJPSgQVPI+XEuNLElyFWkb0hAiMg+DExD9V0St03X2lPHGMdxMY/LrVmoukuIpXJ12oyrOtEZ4uXFkw==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -5923,6 +5976,42 @@ "node": ">=14" } }, + "node_modules/google-gax": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-4.4.1.tgz", + "integrity": "sha512-Phyp9fMfA00J3sZbJxbbB4jC55b7DBjE3F6poyL3wKMEBVKA79q6BGuHcTiM28yOzVql0NDbRL8MLLh8Iwk9Dg==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.10.9", + "@grpc/proto-loader": "^0.7.13", + "@types/long": "^4.0.0", + "abort-controller": "^3.0.0", + "duplexify": "^4.0.0", + "google-auth-library": "^9.3.0", + "node-fetch": "^2.7.0", + "object-hash": "^3.0.0", + "proto3-json-serializer": "^2.0.2", + "protobufjs": "^7.3.2", + "retry-request": "^7.0.0", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-gax/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -6029,6 +6118,15 @@ "node": ">= 0.4" } }, + "node_modules/heap-js": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/heap-js/-/heap-js-2.5.0.tgz", + "integrity": "sha512-kUGoI3p7u6B41z/dp33G6OaL7J4DRqRYwVmeIlwLClx7yaaAy7hoDExnuejTKtuDwfcatGmddHDEOjf6EyIxtQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/hexoid": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-2.0.0.tgz", @@ -6430,6 +6528,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-stream-ended": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.4.tgz", + "integrity": "sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==", + "license": "MIT" + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -7498,6 +7602,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7924,6 +8034,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", @@ -8025,6 +8144,15 @@ "node": ">=0.10.0" } }, + "node_modules/p-defer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-3.0.0.tgz", + "integrity": "sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -8387,6 +8515,18 @@ "node": ">= 6" } }, + "node_modules/proto3-json-serializer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", + "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", + "license": "Apache-2.0", + "dependencies": { + "protobufjs": "^7.2.5" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/protobufjs": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", diff --git a/server/apps/uploadGCould/package.json b/server/apps/uploadGCould/package.json index a146fd62..be361d76 100644 --- a/server/apps/uploadGCould/package.json +++ b/server/apps/uploadGCould/package.json @@ -21,6 +21,7 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@google-cloud/pubsub": "4.9.0", "@google-cloud/storage": "7.14.0", "@grpc/grpc-js": "1.8.17", "@grpc/proto-loader": "0.7.7", diff --git a/server/apps/uploadGCould/protos/upload.proto b/server/apps/uploadGCould/protos/upload.proto index 2c6d61ba..a4748f17 100644 --- a/server/apps/uploadGCould/protos/upload.proto +++ b/server/apps/uploadGCould/protos/upload.proto @@ -1,7 +1,19 @@ syntax = "proto3"; -package googlecloudstorage; +package googlecloudstorage; // Este será el paquete común para ambos servicios +// Servicio PubSub +service PubSubService { + rpc Test (TestRequest) returns (TestResponse); +} + +message TestRequest {} + +message TestResponse { + string message = 1; +} + +// Servicio GoogleCloudStorage service GoogleCloudStorageService { rpc UploadFile (UploadFileRequest) returns (UploadFileResponse); } diff --git a/server/apps/uploadGCould/src/app.module.ts b/server/apps/uploadGCould/src/app.module.ts index c22ed720..1c3a9046 100644 --- a/server/apps/uploadGCould/src/app.module.ts +++ b/server/apps/uploadGCould/src/app.module.ts @@ -2,10 +2,11 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { GoogleCloudStorageService } from './storage.service'; import { StorageController } from './storage.controller'; - +import { PubSubController } from './pubsub.controller'; +import { PubSubService } from './pubsub.service'; @Module({ imports: [ConfigModule.forRoot()], - controllers: [StorageController], - providers: [GoogleCloudStorageService], + controllers: [StorageController,PubSubController], + providers: [GoogleCloudStorageService,PubSubService], }) export class GoogleCloudStorageModule {} \ No newline at end of file diff --git a/server/apps/uploadGCould/src/pubsub.controller.ts b/server/apps/uploadGCould/src/pubsub.controller.ts new file mode 100644 index 00000000..9a9a6408 --- /dev/null +++ b/server/apps/uploadGCould/src/pubsub.controller.ts @@ -0,0 +1,23 @@ +import { Controller } from '@nestjs/common'; +import { GrpcMethod } from '@nestjs/microservices'; +import { PubSubService } from './pubsub.service'; + +@Controller() +export class PubSubController { + constructor(private readonly pubSubService: PubSubService) {} + + @GrpcMethod('PubSubService', 'Test') + test(): { message: string } { + return { message: 'PubSub service is active!' }; + } + + @GrpcMethod('PubSubService', 'ListenForMessages') + async listenForMessages(data: { subscriptionName: string }): Promise<{ status: string, message: string }> { + try { + const result = await this.pubSubService.listenForMessages(data.subscriptionName); + return { status: 'success', message: result as string }; + } catch (error) { + return { status: 'error', message: error.message }; + } + } +} \ No newline at end of file diff --git a/server/apps/uploadGCould/src/pubsub.service.ts b/server/apps/uploadGCould/src/pubsub.service.ts new file mode 100644 index 00000000..90368d02 --- /dev/null +++ b/server/apps/uploadGCould/src/pubsub.service.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@nestjs/common'; +import { GrpcMethod } from '@nestjs/microservices'; +import { ConfigService } from '@nestjs/config'; +import { PubSub } from '@google-cloud/pubsub'; +import { resolve } from 'path'; + +@Injectable() +export class PubSubService { + private readonly pubsub; + + constructor(private readonly configService: ConfigService) { + this.pubsub = new PubSub({ + projectId: this.configService.get('PROJECT_ID'), + keyFilename: resolve(process.cwd(), 'gccKey.json'), + }); + } + + async listenForMessages(subscriptionName: string) { + const subscription = this.pubsub.subscription(subscriptionName); + + return new Promise((resolve, reject) => { + subscription.on('message', (message) => { + try { + console.log(`Received message: ${message.data}`); + message.ack(); + } catch (e) { + console.log('Error parsing message', e); + } + }); + + subscription.on('error', (error) => { + console.error('Error receiving message:', error); + reject(error); + }); + + resolve(`Listening for messages on ${subscriptionName}`); + }); + } +} \ No newline at end of file