Skip to content

Commit

Permalink
Merge pull request #54 from BinaryStudioAcademy/task/OV-37-integrate-…
Browse files Browse the repository at this point in the history
…aws-s3-and-file-uploading-plugin

OV-37: Integrate AWS S3 and add file upload plugin
  • Loading branch information
nikita-remeslov authored Aug 27, 2024
2 parents e05cf25 + 1d92fbc commit df33fa5
Show file tree
Hide file tree
Showing 8 changed files with 1,896 additions and 157 deletions.
9 changes: 9 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,12 @@ DB_CONNECTION_STRING=[db_client]://[db_username]:[db_user_password]@localhost:[d
DB_DIALECT=pg
DB_POOL_MIN=2
DB_POOL_MAX=10

#
# AWS
#
AWS_ACCESS_KEY_ID=see-in-slack
AWS_SECRET_ACCESS_KEY=see-in-slack
AWS_S3_REGION=eu-north-1
AWS_S3_BUCKET_NAME=bsa-2024-outreachvids
AWS_CLOUDFRONT_DOMAIN_ID=d2tm5q3cg1nlwf
3 changes: 3 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
"tsx": "4.17.0"
},
"dependencies": {
"@aws-sdk/client-s3": "3.635.0",
"@aws-sdk/s3-request-presigner": "3.635.0",
"@fastify/multipart": "8.3.0",
"@fastify/static": "7.0.4",
"@fastify/swagger": "8.15.0",
"@fastify/swagger-ui": "4.0.1",
Expand Down
36 changes: 36 additions & 0 deletions backend/src/common/config/base-config.package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,42 @@ class BaseConfig implements Config {
default: null,
},
},
AWS: {
ACCESS_KEY_ID: {
doc: 'AWS access key id',
format: String,
env: 'AWS_ACCESS_KEY_ID',
default: null,
},
SECRET_ACCESS_KEY: {
doc: 'AWS secret access key',
format: String,
env: 'AWS_SECRET_ACCESS_KEY',
default: null,
},
S3: {
REGION: {
doc: 'AWS S3 region',
format: String,
env: 'AWS_S3_REGION',
default: null,
},
BUCKET_NAME: {
doc: 'AWS S3 bucket name',
format: String,
env: 'AWS_S3_BUCKET_NAME',
default: null,
},
},
CLOUDFRONT: {
DOMAIN_ID: {
doc: 'AWS CloudFront domain id',
format: String,
env: 'AWS_CLOUDFRONT_DOMAIN_ID',
default: null,
},
},
},
});
}
}
Expand Down
11 changes: 11 additions & 0 deletions backend/src/common/config/types/environment-schema.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ type EnvironmentSchema = {
POOL_MIN: number;
POOL_MAX: number;
};
AWS: {
ACCESS_KEY_ID: string;
SECRET_ACCESS_KEY: string;
S3: {
REGION: string;
BUCKET_NAME: string;
};
CLOUDFRONT: {
DOMAIN_ID: string;
};
};
};

export { type EnvironmentSchema };
12 changes: 12 additions & 0 deletions backend/src/common/server-application/base-server-app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

import fastifyMultipart from '@fastify/multipart';
import fastifyStatic from '@fastify/static';
import swagger, { type StaticDocumentSpec } from '@fastify/swagger';
import swaggerUi from '@fastify/swagger-ui';
Expand Down Expand Up @@ -122,6 +123,15 @@ class BaseServerApp implements ServerApp {
);
}

private registerPlugins(): void {
this.app.register(fastifyMultipart, {
limits: {
fileSize: Number.POSITIVE_INFINITY,
files: 1,
},
});
}

private initValidationCompiler(): void {
this.app.setValidatorCompiler(
({ schema }: { schema: ValidationSchema }) => {
Expand Down Expand Up @@ -200,6 +210,8 @@ class BaseServerApp implements ServerApp {

await this.initMiddlewares();

this.registerPlugins();

this.initValidationCompiler();

this.initErrorHandler();
Expand Down
63 changes: 63 additions & 0 deletions backend/src/common/services/file/file.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
GetObjectCommand,
PutObjectCommand,
S3Client,
} from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

import { type BaseConfig } from '~/common/config/base-config.package.js';

class FileService {
private config: BaseConfig;
private client: S3Client;
private bucketName: string;
private cfDistributionId: string;

public constructor(config: BaseConfig) {
this.config = config;

this.client = this.initClient();
this.bucketName = this.config.ENV.AWS.S3.BUCKET_NAME;
this.cfDistributionId = this.config.ENV.AWS.CLOUDFRONT.DOMAIN_ID;
}

private initClient(): S3Client {
return new S3Client({
credentials: {
accessKeyId: this.config.ENV.AWS.ACCESS_KEY_ID,
secretAccessKey: this.config.ENV.AWS.SECRET_ACCESS_KEY,
},
region: this.config.ENV.AWS.S3.REGION,
});
}

public uploadFile = async (
buffer: Buffer,
fileName: string,
): Promise<void> => {
const command = new PutObjectCommand({
Bucket: this.bucketName,
Key: fileName,
Body: buffer,
});

await this.client.send(command);
};

public getFileUrl = async (fileName: string): Promise<string> => {
const getFileObject = new GetObjectCommand({
Bucket: this.bucketName,
Key: fileName,
});

return await getSignedUrl(this.client, getFileObject, {
expiresIn: 3600,
});
};

public getCloudFrontFileUrl = (fileName: string): string => {
return `https://${this.cfDistributionId}.cloudfront.net/${fileName}`;
};
}

export { FileService };
5 changes: 4 additions & 1 deletion backend/src/common/services/services.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { config } from '../config/config.js';
import { CryptService } from './crypt/crypt.service.js';
import { FileService } from './file/file.service.js';

const cryptService = new CryptService();
const fileService = new FileService(config);

export { cryptService };
export { cryptService, fileService };
Loading

0 comments on commit df33fa5

Please sign in to comment.