Skip to content

Commit

Permalink
Merge branch 'main' into keerthi/file_download_method
Browse files Browse the repository at this point in the history
  • Loading branch information
tmthecoder committed Nov 18, 2024
2 parents a633471 + 524b649 commit 0917b6f
Show file tree
Hide file tree
Showing 10 changed files with 494 additions and 53 deletions.
6 changes: 5 additions & 1 deletion packages/file-service/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ lerna-debug.log*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/extensions.json

.env.dev.local
.env.gh-actions.local
.env.*
2 changes: 1 addition & 1 deletion packages/file-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"test:e2e": "jest --config ./test/jest-e2e.json --runInBand",
"test:e2e:watch": "jest --config ./test/jest-e2e.json --runInBand --watchAll",
"tsc": "tsc",
"sentry:sourcemaps": "sentry-cli sourcemaps inject --org bits-of-good-development --project juno-email-service ./dist && sentry-cli sourcemaps upload --org bits-of-good-development --project juno-email-service ./dist"
Expand Down
2 changes: 2 additions & 0 deletions packages/file-service/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import { SentryModule } from '@sentry/nestjs/setup';
import { HealthModule } from './modules/health/health.module';
import { FileUploadModule } from './modules/file_upload/file_upload.module';
import { FileProviderModule } from './modules/file_provider/file_provider.module';
import { FileBucketModule } from './modules/file_bucket/file_bucket.module';

@Module({
imports: [
SentryModule.forRoot(),
HealthModule,
FileUploadModule,
FileProviderModule,
FileBucketModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Controller } from '@nestjs/common';
import { FileBucketService } from './file_bucket.service';
import { FileBucketProto } from 'juno-proto';
import { RpcException } from '@nestjs/microservices';

@Controller()
@FileBucketProto.BucketFileServiceControllerMethods()
export class FileBucketController
implements FileBucketProto.BucketFileServiceController
{
constructor(private readonly fileBucketService: FileBucketService) {}

async registerBucket(
request: FileBucketProto.RegisterBucketRequest,
): Promise<FileBucketProto.Bucket> {
try {
return await this.fileBucketService.registerBucket(request);
} catch (error) {
throw new RpcException({
code: error.code,
message: `Failed to register bucket: ${error.message}`,
});
}
}

async removeBucket(
request: FileBucketProto.RemoveBucketRequest,
): Promise<FileBucketProto.Bucket> {
try {
return await this.fileBucketService.removeBucket(request);
} catch (error) {
throw new RpcException({
code: error.code,
message: `Failed to remove bucket: ${error.message}`,
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Module } from '@nestjs/common';
import { FileBucketController } from './file_bucket.controller';
import { FileBucketService } from './file_bucket.service';
import { ConfigModule } from '@nestjs/config';
import { ClientsModule, Transport } from '@nestjs/microservices';
import {
FileBucketProto,
FileBucketProtoFile,
FileProviderProto,
FileProviderProtoFile,
} from 'juno-proto';
import { join } from 'path';

@Module({
imports: [
ConfigModule.forRoot({
envFilePath: join(__dirname, '../../../../../.env.local'),
}),
ClientsModule.register([
{
name: FileBucketProto.BUCKET_DB_SERVICE_NAME,
transport: Transport.GRPC,
options: {
url: process.env.DB_SERVICE_ADDR,
package: FileBucketProto.JUNO_FILE_SERVICE_BUCKET_PACKAGE_NAME,
protoPath: FileBucketProtoFile,
},
},
{
name: FileProviderProto.FILE_PROVIDER_DB_SERVICE_NAME,
transport: Transport.GRPC,
options: {
url: process.env.DB_SERVICE_ADDR,
package: FileProviderProto.JUNO_FILE_SERVICE_PROVIDER_PACKAGE_NAME,
protoPath: FileProviderProtoFile,
},
},
]),
],
controllers: [FileBucketController],
providers: [FileBucketService],
})
export class FileBucketModule {}
123 changes: 123 additions & 0 deletions packages/file-service/src/modules/file_bucket/file_bucket.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { Injectable, Inject, OnModuleInit } from '@nestjs/common';
import {
S3Client,
CreateBucketCommand,
DeleteBucketCommand,
} from '@aws-sdk/client-s3';
import { FileBucketProto, FileProviderProto } from 'juno-proto';
import { ClientGrpc, RpcException } from '@nestjs/microservices';
import { status } from '@grpc/grpc-js';
import { lastValueFrom } from 'rxjs';

@Injectable()
export class FileBucketService implements OnModuleInit {
private fileDBService: FileBucketProto.BucketDbServiceClient;
private fileProviderDBService: FileProviderProto.FileProviderDbServiceClient;

constructor(
@Inject(FileBucketProto.BUCKET_DB_SERVICE_NAME)
private fileDBClient: ClientGrpc,
@Inject(FileProviderProto.FILE_PROVIDER_DB_SERVICE_NAME)
private fileProviderDBClient: ClientGrpc,
) {}

onModuleInit() {
this.fileDBService =
this.fileDBClient.getService<FileBucketProto.BucketDbServiceClient>(
FileBucketProto.BUCKET_DB_SERVICE_NAME,
);
this.fileProviderDBService =
this.fileProviderDBClient.getService<FileProviderProto.FileProviderDbServiceClient>(
FileProviderProto.FILE_PROVIDER_DB_SERVICE_NAME,
);
}

async getS3ClientForProvider(
providerName: string,
region: string,
): Promise<S3Client> {
try {
const provider = await lastValueFrom(
this.fileProviderDBService.getProvider({
providerName: providerName,
}),
);

const metadata = {
...JSON.parse(provider.metadata),
region: region,
credentials: JSON.parse(provider.accessKey),
};
return new S3Client(metadata);
} catch (error) {
throw new RpcException({
code: status.INTERNAL,
message: `Failed to initialize S3 client: ${error.message} `,
});
}
}

async registerBucket(
request: FileBucketProto.RegisterBucketRequest,
): Promise<FileBucketProto.Bucket> {
try {
const s3Client = await this.getS3ClientForProvider(
request.fileProviderName,
'us-east-1',
);
const createBucketCommand = new CreateBucketCommand({
Bucket: request.name,
});
await s3Client.send(createBucketCommand);

const dbBucket = await lastValueFrom(
this.fileDBService.createBucket(request),
);
return dbBucket;
} catch (error) {
if (error.message.includes('Bucket already exists')) {
throw new RpcException({
code: status.ALREADY_EXISTS,
message: 'Bucket already exists',
});
}
throw new RpcException({
code: status.INTERNAL,
message: `Failed to create bucket: ${error.message} `,
});
}
}

async removeBucket(
request: FileBucketProto.RemoveBucketRequest,
): Promise<FileBucketProto.Bucket> {
try {
const bucket = await lastValueFrom(
this.fileDBService.deleteBucket({
name: request.name,
configId: request.configId,
}),
);
const s3Client = await this.getS3ClientForProvider(
bucket.fileProviderName,
'us-east-1',
);
const deleteBucketCommand = new DeleteBucketCommand({
Bucket: request.name,
});
await s3Client.send(deleteBucketCommand);
return bucket;
} catch (error) {
if (error.message.includes('NoSuchBucket')) {
throw new RpcException({
code: status.NOT_FOUND,
message: 'Bucket not found',
});
}
throw new RpcException({
code: status.INTERNAL,
message: `Failed to delete bucket: ${error.message} `,
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,36 +62,46 @@ export class FileUploadController implements FileServiceController {
const region = request.region ? request.region : 'us-east-1';

//Try connecting to s3 client
const provider = await lastValueFrom(
this.fileProviderDbService.getProvider({
providerName: providerName,
}),
);

const metadata = {
...JSON.parse(provider['metadata']),
region: region,
credentials: JSON.parse(provider['accessKey']),
};
const client = new S3Client(metadata);

//Get File
const fileId = {
bucketName: bucketName,
configId: configId,
path: fileName,
};
const fileRequest = { fileId };
const file = await firstValueFrom(this.fileDBService.getFile(fileRequest));
if (!file) {

try {
//Get File
const fileId = {
bucketName: bucketName,
configId: configId,
path: fileName,
};
const fileRequest = { fileId };
const file = await firstValueFrom(
this.fileDBService.getFile(fileRequest),
);
if (!file) {
throw new RpcException({
code: status.NOT_FOUND,
message: 'File not found',
});
}
} catch (e) {
console.log(e);
throw new RpcException({
code: status.NOT_FOUND,
message: 'File not found',
message: `File not found: ${e}`,
});
}

//get url
try {
const provider = await lastValueFrom(
this.fileProviderDbService.getProvider({
providerName: providerName,
}),
);

const metadata = {
...JSON.parse(provider['metadata']),
region: region,
credentials: JSON.parse(provider['accessKey']),
};
const client = new S3Client(metadata);
const getcommand = new GetObjectCommand({
Bucket: bucketName,
Key: fileName,
Expand All @@ -104,7 +114,7 @@ export class FileUploadController implements FileServiceController {
console.log(err);
throw new RpcException({
code: status.NOT_FOUND,
message: 'Signed URL Not Found',
message: `Signed URL Not Found: ${err}`,
});
}
}
Expand All @@ -130,27 +140,35 @@ export class FileUploadController implements FileServiceController {
});
}

const region = request.region ? request.region : 'us-east-1';

const provider = await lastValueFrom(
this.fileProviderDbService.getProvider({
providerName: request.providerName,
}),
);
const accessKey = provider['accessKey'];
const metadata = {
...JSON.parse(provider['metadata']),
region: region,
credentials: JSON.parse(accessKey),
};

const s3Client = new S3Client(metadata);
const command = new PutObjectCommand({
Bucket: request.bucketName,
Key: request.fileName,
});
const url = await getSignedUrl(s3Client, command, { expiresIn: 3600 });
let url = '';
try {
const region = request.region ? request.region : 'us-east-1';

const provider = await lastValueFrom(
this.fileProviderDbService.getProvider({
providerName: request.providerName,
}),
);
const accessKey = provider['accessKey'];
const metadata = {
...JSON.parse(provider['metadata']),
region: region,
credentials: JSON.parse(accessKey),
};

const s3Client = new S3Client(metadata);
const command = new PutObjectCommand({
Bucket: request.bucketName,
Key: request.fileName,
});
url = await getSignedUrl(s3Client, command, { expiresIn: 3600 });
} catch (err) {
console.log(err);
throw new RpcException({
code: status.NOT_FOUND,
message: `Could not create signed url: ${err}`,
});
}
try {
// Save file to DB
await lastValueFrom(
Expand Down
Loading

0 comments on commit 0917b6f

Please sign in to comment.