From 37c1eb8b469a7927032d95db5dc52c889008d4b6 Mon Sep 17 00:00:00 2001 From: Anand Chowdhary Date: Fri, 13 Nov 2020 18:16:08 +0530 Subject: [PATCH] :sparkles: Add Cloudinary module --- src/app.module.ts | 2 + src/config/configuration.interface.ts | 6 ++ src/config/configuration.ts | 5 ++ src/providers/cloudinary/cloudinary.module.ts | 10 +++ .../cloudinary/cloudinary.service.ts | 64 +++++++++++++++++++ 5 files changed, 87 insertions(+) create mode 100644 src/providers/cloudinary/cloudinary.module.ts create mode 100644 src/providers/cloudinary/cloudinary.service.ts diff --git a/src/app.module.ts b/src/app.module.ts index cf7d36558..1c1614684 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -29,6 +29,7 @@ import { StripeModule } from './modules/stripe/stripe.module'; import { UsersModule } from './modules/users/users.module'; import { WebhooksModule } from './modules/webhooks/webhooks.module'; import { AirtableModule } from './providers/airtable/airtable.module'; +import { CloudinaryModule } from './providers/cloudinary/cloudinary.module'; import { DnsModule } from './providers/dns/dns.module'; import { ElasticSearchModule } from './providers/elasticsearch/elasticsearch.module'; import { GeolocationModule } from './providers/geolocation/geolocation.module'; @@ -70,6 +71,7 @@ import { TasksModule } from './providers/tasks/tasks.module'; SlackModule, AirtableModule, S3Module, + CloudinaryModule, ], providers: [ { diff --git a/src/config/configuration.interface.ts b/src/config/configuration.interface.ts index 17e12f64b..c88083ea4 100644 --- a/src/config/configuration.interface.ts +++ b/src/config/configuration.interface.ts @@ -103,4 +103,10 @@ export interface Configuration { region: string; bucket?: string; }; + + cloudinary: { + cloudName: string; + apiKey: string; + apiSecret: string; + }; } diff --git a/src/config/configuration.ts b/src/config/configuration.ts index 03a0d7d90..f23b5477a 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -115,6 +115,11 @@ const configuration: Configuration = { region: process.env.S3_REGION ?? '', bucket: process.env.S3_BUCKET, }, + cloudinary: { + cloudName: process.env.CLOUDINARY_CLOUD_NAME ?? '', + apiKey: process.env.CLOUDINARY_API_KEY ?? '', + apiSecret: process.env.CLOUDINARY_API_SECRET ?? '', + }, }; const configFunction: ConfigFactory = () => configuration; diff --git a/src/providers/cloudinary/cloudinary.module.ts b/src/providers/cloudinary/cloudinary.module.ts new file mode 100644 index 000000000..7d4153c69 --- /dev/null +++ b/src/providers/cloudinary/cloudinary.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { CloudinaryService } from './cloudinary.service'; + +@Module({ + imports: [ConfigModule], + providers: [CloudinaryService], + exports: [CloudinaryService], +}) +export class CloudinaryModule {} diff --git a/src/providers/cloudinary/cloudinary.service.ts b/src/providers/cloudinary/cloudinary.service.ts new file mode 100644 index 000000000..688d87a68 --- /dev/null +++ b/src/providers/cloudinary/cloudinary.service.ts @@ -0,0 +1,64 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import cloudinary, { UploadApiResponse } from 'cloudinary'; +import { Readable, Stream } from 'stream'; +import { Configuration } from '../../config/configuration.interface'; + +/** + * MultiStream class helps convert buffers to a streams + * @source https://github.com/gagle/node-streamifier + */ +class MultiStream extends Readable { + _object: Buffer | string | undefined; + constructor(object: Buffer | string, options: any = {}) { + super(); + this._object = object; + Stream.Readable.call(this, { + highWaterMark: options.highWaterMark, + encoding: options.encoding, + }); + } + _read() { + this.push(this._object); + this._object = undefined; + } +} + +/** + * + * @param object - Object to convert + * @param options - Configuration (encoding and highWaterMark) + */ +const createReadStream = (object: Buffer | string, options?: any) => + new MultiStream(object, options); + +@Injectable() +export class CloudinaryService { + private logger = new Logger(CloudinaryService.name); + + constructor(private configService: ConfigService) { + const config = this.configService.get( + 'cloudinary', + ); + if (config.cloudName) + cloudinary.v2.config({ + cloud_name: config.cloudName, + api_key: config.apiKey, + api_secret: config.apiSecret, + }); + else this.logger.warn('Cloudinary API key not found'); + } + + upload(buffer: Buffer | string, folder: string): Promise { + return new Promise((resolve, reject) => { + const uploadStream = cloudinary.v2.uploader.upload_stream( + { folder }, + (error, result) => { + if (result) return resolve(result); + reject(error); + }, + ); + createReadStream(buffer).pipe(uploadStream); + }); + } +}