Skip to content

Commit

Permalink
Merge pull request #504 from andrechristikan/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
andrechristikan authored Jan 24, 2024
2 parents 4e51a44 + b327d63 commit 3dd7a99
Show file tree
Hide file tree
Showing 54 changed files with 2,486 additions and 2,198 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@

## Important

> Very limited documentation, and will move to stateful authorization
> Very limited documentation
* The features will be relate with AWS / Amazon web service
* Stateless Authorization
* If you want to implement `database transactions`. You must run MongoDB as a `replication set`.
* If you want to implement `Google SSO`. You must have google cloud console account, then create your own SSO / Credential API to get the `clientId` and `clientSecret`.
* If you change the environment value of `APP_ENV` to `production`, that will trigger.
1. CorsMiddleware will implement `src/configs/middleware.config.ts`.
1. CorsMiddleware will implement config from `src/configs/middleware.config.ts`.
2. Documentation will `disable`.
* For monitoring, this project will use sentry.io, and only send `500` or `internal server error`.

Expand All @@ -52,9 +52,9 @@
* [x] Debugger env change
* [x] Add exception filter to Sentry.io
* [x] Add Email Module with AWS SES
* [ ] CI Push docker image to AWS ECR
* [ ] CD Using AWS ECS and deploy to AWS EC2
* [ ] Update Package, and remove unused package
* [ ] CI Push docker image to AWS ECR, `currently using Docker Hub`
* [ ] CD Using AWS ECS and deploy to AWS EC2, `currently only using AWS EC2`
* [x] Update Package, and remove unused package
* [ ] Update Documentation, add behaviors
* [ ] Update Documentation, and include an diagram for easier comprehension
* [ ] Add Redis / Move to stateful Authorization Token (security and ux reason)
Expand Down
69 changes: 34 additions & 35 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ack-nestjs-boilerplate",
"version": "5.6.3",
"version": "5.6.5",
"description": "Ack NestJs Boilerplate",
"repository": {
"type": "git",
Expand Down Expand Up @@ -47,48 +47,47 @@
"rollback": "yarn rollback:email && yarn rollback:apikey && yarn rollback:user && yarn rollback:role"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.473.0",
"@aws-sdk/client-ses": "^3.473.0",
"@aws-sdk/client-s3": "^3.498.0",
"@aws-sdk/client-ses": "^3.498.0",
"@casl/ability": "^6.5.0",
"@faker-js/faker": "^8.3.1",
"@joi/date": "^2.1.0",
"@nestjs/axios": "^3.0.1",
"@nestjs/common": "^10.2.10",
"@nestjs/common": "^10.3.1",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.2.10",
"@nestjs/core": "^10.3.1",
"@nestjs/jwt": "^10.2.0",
"@nestjs/mongoose": "^10.0.2",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.2.10",
"@nestjs/platform-express": "^10.3.1",
"@nestjs/schedule": "^4.0.0",
"@nestjs/swagger": "^7.1.17",
"@nestjs/swagger": "^7.2.0",
"@nestjs/terminus": "^10.2.0",
"@nestjs/throttler": "^5.1.0",
"@ntegral/nestjs-sentry": "^4.0.0",
"@sentry/node": "^7.87.0",
"@nestjs/throttler": "^5.1.1",
"@ntegral/nestjs-sentry": "^4.0.1",
"@sentry/node": "^7.95.0",
"@types/response-time": "^2.3.8",
"axios": "^1.6.2",
"axios": "^1.6.5",
"bcryptjs": "^2.4.3",
"case": "^1.6.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"class-validator": "^0.14.1",
"crypto-js": "^4.2.0",
"google-auth-library": "^9.4.1",
"google-auth-library": "^9.4.2",
"helmet": "^7.1.0",
"joi": "^17.11.0",
"moment": "^2.29.4",
"moment-timezone": "^0.5.43",
"mongoose": "^8.0.3",
"joi": "^17.12.0",
"moment": "^2.30.1",
"moment-timezone": "^0.5.44",
"mongoose": "^8.1.0",
"nest-winston": "^1.9.4",
"nestjs-command": "^3.1.4",
"nestjs-i18n": "^10.4.0",
"passport": "^0.7.0",
"passport-headerapikey": "^1.2.2",
"passport-jwt": "^4.0.1",
"reflect-metadata": "^0.2.0",
"reflect-metadata": "^0.2.1",
"response-time": "^2.3.2",
"rimraf": "^5.0.5",
"rotating-file-stream": "^3.1.1",
"rotating-file-stream": "^3.2.1",
"rxjs": "^7.8.1",
"ua-parser-js": "^1.0.37",
"winston": "^3.11.0",
Expand All @@ -98,35 +97,35 @@
"yarn": "^1.22.21"
},
"devDependencies": {
"@nestjs/cli": "^10.2.1",
"@nestjs/schematics": "^10.0.3",
"@nestjs/testing": "^10.2.10",
"@nestjs/cli": "^10.3.0",
"@nestjs/schematics": "^10.1.0",
"@nestjs/testing": "^10.3.1",
"@types/bcryptjs": "^2.4.6",
"@types/bytes": "^3.1.4",
"@types/cors": "^2.8.17",
"@types/cron": "^2.0.1",
"@types/crypto-js": "^4.2.1",
"@types/crypto-js": "^4.2.2",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.11",
"@types/lodash": "^4.14.202",
"@types/ms": "^0.7.34",
"@types/multer": "^1.4.11",
"@types/node": "^20.10.4",
"@types/passport-jwt": "^3.0.13",
"@types/supertest": "^2.0.16",
"@types/node": "^20.11.6",
"@types/passport-jwt": "^4.0.0",
"@types/supertest": "^6.0.2",
"@types/ua-parser-js": "^0.7.39",
"@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"cspell": "^8.1.3",
"eslint": "^8.55.0",
"@typescript-eslint/eslint-plugin": "^6.19.1",
"@typescript-eslint/parser": "^6.19.1",
"cspell": "^8.3.2",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-import": "^2.29.1",
"husky": "^8.0.3",
"jest": "^29.7.0",
"prettier": "^3.1.1",
"supertest": "^6.3.3",
"ts-jest": "^29.1.1",
"prettier": "^3.2.4",
"supertest": "^6.3.4",
"ts-jest": "^29.1.2",
"ts-loader": "^9.5.1",
"ts-node": "^10.9.2",
"ts-prune": "^0.10.3",
Expand Down
2 changes: 2 additions & 0 deletions src/app/constants/app.constant.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export const APP_LANGUAGE = 'en';

export const APP_TZ = 'Asia/Jakarta';
2 changes: 0 additions & 2 deletions src/app/constants/app.enum.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,3 @@ export enum ENUM_APP_ENVIRONMENT {
STAGING = 'staging',
DEVELOPMENT = 'development',
}

export const APP_TZ = 'Asia/Jakarta';
17 changes: 9 additions & 8 deletions src/common/api-key/controllers/api-key.admin.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,15 @@ export class ApiKeyAdminController {
...type,
};

const apiKeys: ApiKeyDoc[] = await this.apiKeyService.findAll(find, {
paging: {
limit: _limit,
offset: _offset,
},
order: _order,
plainObject: true,
});
const apiKeys: ApiKeyEntity[] =
await this.apiKeyService.findAll<ApiKeyEntity>(find, {
paging: {
limit: _limit,
offset: _offset,
},
order: _order,
plainObject: true,
});
const total: number = await this.apiKeyService.getTotal(find);
const totalPage: number = this.paginationService.totalPage(
total,
Expand Down
9 changes: 3 additions & 6 deletions src/common/api-key/interfaces/api-key.service.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import {
ApiKeyUpdateDto,
} from 'src/common/api-key/dtos/api-key.update.dto';
import { IApiKeyCreated } from 'src/common/api-key/interfaces/api-key.interface';
import {
ApiKeyDoc,
ApiKeyEntity,
} from 'src/common/api-key/repository/entities/api-key.entity';
import { ApiKeyDoc } from 'src/common/api-key/repository/entities/api-key.entity';
import {
IDatabaseCreateOptions,
IDatabaseFindAllOptions,
Expand All @@ -21,10 +18,10 @@ import {
} from 'src/common/database/interfaces/database.interface';

export interface IApiKeyService {
findAll(
findAll<T>(
find?: Record<string, any>,
options?: IDatabaseFindAllOptions
): Promise<ApiKeyEntity[]>;
): Promise<T[]>;
findOneById(
_id: string,
options?: IDatabaseFindOneOptions
Expand Down
6 changes: 3 additions & 3 deletions src/common/api-key/services/api-key.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ export class ApiKeyService implements IApiKeyService {
this.env = this.configService.get<string>('app.env');
}

async findAll(
async findAll<T = ApiKeyDoc>(
find?: Record<string, any>,
options?: IDatabaseFindAllOptions
): Promise<ApiKeyDoc[]> {
return this.apiKeyRepository.findAll<ApiKeyDoc>(find, options);
): Promise<T[]> {
return this.apiKeyRepository.findAll<T>(find, options);
}

async findOneById(
Expand Down
8 changes: 7 additions & 1 deletion src/common/aws/interfaces/aws.interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { ObjectCannedACL } from '@aws-sdk/client-s3';

export interface IAwsS3PutItemOptions {
path: string;
path?: string;
customFilename?: string;
acl?: ObjectCannedACL;
}

export interface IAwsS3RandomFilename {
path: string;
customFilename: string;
}
46 changes: 18 additions & 28 deletions src/common/aws/interfaces/aws.s3-service.interface.ts
Original file line number Diff line number Diff line change
@@ -1,53 +1,43 @@
import {
CompletedPart,
HeadBucketCommandOutput,
UploadPartRequest,
} from '@aws-sdk/client-s3';
import { HeadBucketCommandOutput, UploadPartRequest } from '@aws-sdk/client-s3';
import { IAwsS3PutItemOptions } from 'src/common/aws/interfaces/aws.interface';
import {
AwsS3MultipartPartsSerialization,
AwsS3MultipartSerialization,
} from 'src/common/aws/serializations/aws.s3-multipart.serialization';
import { AwsS3Serialization } from 'src/common/aws/serializations/aws.s3.serialization';
import { IFile } from 'src/common/file/interfaces/file.interface';
import { Readable } from 'stream';

export interface IAwsS3Service {
checkBucketExistence(): Promise<HeadBucketCommandOutput>;
listBucket(): Promise<string[]>;
listItemInBucket(prefix?: string): Promise<AwsS3Serialization[]>;
getItemInBucket(
filename: string,
path?: string
pathWithFilename: string
): Promise<Readable | ReadableStream<any> | Blob>;
putItemInBucket(
filename: string,
content:
| string
| Uint8Array
| Buffer
| Readable
| ReadableStream
| Blob,
file: IFile,
options?: IAwsS3PutItemOptions
): Promise<AwsS3Serialization>;
deleteItemInBucket(filename: string): Promise<void>;
deleteItemsInBucket(filenames: string[]): Promise<void>;
deleteItemInBucket(pathWithFilename: string): Promise<void>;
deleteItemsInBucket(pathWithFilename: string[]): Promise<void>;
deleteFolder(dir: string): Promise<void>;
createMultiPart(
filename: string,
file: IFile,
maxPartNumber: number,
options?: IAwsS3PutItemOptions
): Promise<AwsS3MultipartSerialization>;
uploadPart(
path: string,
content: UploadPartRequest['Body'] | string | Uint8Array | Buffer,
uploadId: string,
partNumber: number
multipart: AwsS3MultipartSerialization,
partNumber: number,
content: UploadPartRequest['Body'] | string | Uint8Array | Buffer
): Promise<AwsS3MultipartPartsSerialization>;
completeMultipart(
path: string,
uploadId: string,
parts: CompletedPart[]
): Promise<void>;
abortMultipart(path: string, uploadId: string): Promise<void>;
updateMultiPart(
{ size, parts, ...others }: AwsS3MultipartSerialization,
part: AwsS3MultipartPartsSerialization
): Promise<AwsS3MultipartSerialization>;
completeMultipart(multipart: AwsS3MultipartSerialization): Promise<void>;
abortMultipart(multipart: AwsS3MultipartSerialization): Promise<void>;
getFilenameFromCompletedUrl(completedUrl: string): Promise<string>;
createRandomFilename(path?: string): Promise<Record<string, any>>;
}
30 changes: 19 additions & 11 deletions src/common/aws/serializations/aws.s3-multipart.serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,23 @@ export class AwsS3MultipartPartsSerialization {
description: 'ETag from aws after init multipart',
})
@Type(() => String)
ETag: string;
eTag: string;

@ApiProperty({
required: true,
nullable: false,
example: 1,
})
@Type(() => Number)
PartNumber: number;
partNumber: number;

@ApiProperty({
required: true,
nullable: false,
example: 1,
})
@Type(() => Number)
size: number;
}

export class AwsS3MultipartSerialization extends AwsS3Serialization {
Expand All @@ -33,26 +41,26 @@ export class AwsS3MultipartSerialization extends AwsS3Serialization {
uploadId: string;

@ApiProperty({
required: false,
nullable: true,
required: true,
nullable: false,
example: 1,
description: 'Last part number uploaded',
})
@Type(() => Number)
partNumber?: number;
lastPartNumber: number;

@ApiProperty({
required: false,
nullable: true,
required: true,
nullable: false,
example: 200,
description: 'Max part number, or length of the chunk',
})
@Type(() => Number)
maxPartNumber?: number;
maxPartNumber: number;

@ApiProperty({
required: false,
nullable: true,
required: true,
nullable: false,
oneOf: [
{
$ref: getSchemaPath(AwsS3MultipartPartsSerialization),
Expand All @@ -61,5 +69,5 @@ export class AwsS3MultipartSerialization extends AwsS3Serialization {
],
})
@Type(() => AwsS3MultipartPartsSerialization)
parts?: AwsS3MultipartPartsSerialization[];
parts: AwsS3MultipartPartsSerialization[];
}
Loading

0 comments on commit 3dd7a99

Please sign in to comment.