diff --git a/package.json b/package.json index 16c866e..f97ceb7 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "@nestjs/schedule": "^4.1.0", "@nestjs/swagger": "^7.3.1", "@nestjs/typeorm": "^10.0.2", + "@types/multer": "^1.4.11", "algebra.js": "^0.2.6", "bcrypt": "^5.1.1", "class-transformer": "^0.5.1", diff --git a/src/app.module.ts b/src/app.module.ts index e3cece8..40bb073 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -63,6 +63,7 @@ import { TermConditionModule } from './modules/web-information/term-condition/te import { TermConditionModel } from './modules/web-information/term-condition/data/models/term-condition.model'; import { FaqModel } from './modules/web-information/faq/data/models/faq.model'; import { FaqModule } from './modules/web-information/faq/faq.module'; +import { UploadModule } from './modules/configuration/upload/upload.module'; @Module({ imports: [ @@ -117,6 +118,7 @@ import { FaqModule } from './modules/web-information/faq/faq.module'; LogModule, MidtransModule, SessionModule, + UploadModule, // user TenantModule, diff --git a/src/core/helpers/path/move-file-path.helper.ts b/src/core/helpers/path/move-file-path.helper.ts new file mode 100644 index 0000000..040ad09 --- /dev/null +++ b/src/core/helpers/path/move-file-path.helper.ts @@ -0,0 +1,39 @@ +import * as fs from 'fs'; + +export function MoveFilePathHelper() {} + +// export class MoveFileToDirIdHelper { +// public srcDir: string; +// public fileName: string; + +// constructor(private file_url: string = null) {} + +// execute(): string { +// try { +// this.getSrcDir(); +// this.getFileName(); + +// const copyFile = `${this.fileName}-copy`; +// fs.mkdirSync(`./uploads/${this.srcDir}`, { recursive: true }); +// fs.copyFileSync( +// this.file_url, +// `./uploads/${this.srcDir}/${this.fileName}`, +// ); +// fs.unlinkSync(`${this.file_url}`); + +// this.file_url = `uploads/${this.srcDir}/${this.fileName}`; +// } catch (error) { +// console.error('Error moving file:', error); +// } + +// return this.file_url; +// } + +// private getSrcDir(): void { +// this.srcDir = this.file_url.split('/').slice(2, -1).join('/'); +// } + +// private getFileName(): void { +// this.fileName = this.file_url.split('/').slice(-1).join('/'); +// } +// } diff --git a/src/core/helpers/path/upload-store-path.helper.ts b/src/core/helpers/path/upload-store-path.helper.ts new file mode 100644 index 0000000..2fbb495 --- /dev/null +++ b/src/core/helpers/path/upload-store-path.helper.ts @@ -0,0 +1,44 @@ +import { extname } from 'path'; +import { v4 as uuidv4 } from 'uuid'; +import { HttpException, HttpStatus } from '@nestjs/common'; +import * as fs from 'fs'; +import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface'; +import { diskStorage } from 'multer'; + +const MB = 1024 * 1024; + +const fileFilter = (req, file, callback) => { + if (file.mimetype.match(/\/(jpg|jpeg|png)$/)) { + callback(null, true); + } else { + callback( + new HttpException( + `Unsupported file type ${extname(file.originalname)}`, + HttpStatus.BAD_REQUEST, + ), + false, + ); + } +}; + +const editFileName = (req, file, callback) => { + const fileExtName = extname(file.originalname); + const randomName = uuidv4(); + callback(null, `${randomName}${fileExtName}`); +}; + +const destinationPath = (req, file, cb) => { + let modulePath = req.body.module; + if (req.body.sub_module) modulePath = `${modulePath}/${req.body.sub_module}`; + + fs.mkdirSync(`./uploads/tmp/${modulePath}`, { recursive: true }); + cb(null, `./uploads/tmp/${modulePath}`); +}; + +export const StoreFileConfig: MulterOptions = { + storage: diskStorage({ + destination: destinationPath, + filename: editFileName, + }), + fileFilter: fileFilter, +}; diff --git a/src/database/migrations/1721031712642-update-refund.ts b/src/database/migrations/1721031712642-update-refund.ts index 4bbd3c3..09c5d4c 100644 --- a/src/database/migrations/1721031712642-update-refund.ts +++ b/src/database/migrations/1721031712642-update-refund.ts @@ -1,14 +1,17 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; +import { MigrationInterface, QueryRunner } from 'typeorm'; export class UpdateRefund1721031712642 implements MigrationInterface { - name = 'UpdateRefund1721031712642' + name = 'UpdateRefund1721031712642'; - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "refunds" ADD "refund_sub_total" numeric`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "refunds" DROP COLUMN "refund_sub_total"`); - } + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "refunds" ADD "refund_sub_total" numeric`, + ); + } + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "refunds" DROP COLUMN "refund_sub_total"`, + ); + } } diff --git a/src/modules/configuration/upload/domain/entities/upload.entity.ts b/src/modules/configuration/upload/domain/entities/upload.entity.ts new file mode 100644 index 0000000..2e97daa --- /dev/null +++ b/src/modules/configuration/upload/domain/entities/upload.entity.ts @@ -0,0 +1,5 @@ +export interface UploadEntity { + module: string; + sub_module: string; + file: any; +} diff --git a/src/modules/configuration/upload/infrastructure/dto/upload.dto.ts b/src/modules/configuration/upload/infrastructure/dto/upload.dto.ts new file mode 100644 index 0000000..f8bcd48 --- /dev/null +++ b/src/modules/configuration/upload/infrastructure/dto/upload.dto.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { UploadEntity } from '../../domain/entities/upload.entity'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; + +export class UploadDto implements UploadEntity { + @ApiProperty({ + type: 'string', + required: true, + default: TABLE_NAME.ITEM, + }) + module: string; + + @ApiProperty({ + type: 'string', + required: false, + default: TABLE_NAME.ITEM, + }) + sub_module: string; + + @ApiProperty({ type: 'string', format: 'binary', required: true }) + file: any; +} diff --git a/src/modules/configuration/upload/infrastructure/upload.controller.ts b/src/modules/configuration/upload/infrastructure/upload.controller.ts new file mode 100644 index 0000000..d754545 --- /dev/null +++ b/src/modules/configuration/upload/infrastructure/upload.controller.ts @@ -0,0 +1,34 @@ +import { + Controller, + Post, + UseInterceptors, + Body, + UploadedFile, +} from '@nestjs/common'; +import { ModuleRef } from '@nestjs/core'; +import { ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { UploadDto } from './dto/upload.dto'; +import { Public } from 'src/core/guards'; +import { StoreFileConfig } from 'src/core/helpers/path/upload-store-path.helper'; + +@ApiTags('uploads') +@Controller('uploads') +@Public(true) +export class UploadController { + constructor(private moduleRef: ModuleRef) {} + + @Post() + @ApiConsumes('multipart/form-data') + @ApiBody({ type: UploadDto }) + @UseInterceptors(FileInterceptor('file', StoreFileConfig)) + async storeFile( + @UploadedFile() file: Express.Multer.File, + @Body() body: UploadDto, + ): Promise { + return { + path: file.path, + full_path: `${process.env.ASSETS}${file.path}`, + }; + } +} diff --git a/src/modules/configuration/upload/upload.module.ts b/src/modules/configuration/upload/upload.module.ts new file mode 100644 index 0000000..ffcdc4b --- /dev/null +++ b/src/modules/configuration/upload/upload.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { MulterModule } from '@nestjs/platform-express'; +import { UploadController } from './infrastructure/upload.controller'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + MulterModule.register({ + dest: './uploads', + }), + ], + controllers: [UploadController], + providers: [], +}) +export class UploadModule {} diff --git a/yarn.lock b/yarn.lock index 1d756f8..382e0c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1081,7 +1081,7 @@ "@types/range-parser" "*" "@types/send" "*" -"@types/express@^4.17.13": +"@types/express@*", "@types/express@^4.17.13": version "4.17.21" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== @@ -1178,6 +1178,13 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== +"@types/multer@^1.4.11": + version "1.4.11" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.11.tgz#c70792670513b4af1159a2b60bf48cc932af55c5" + integrity sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w== + dependencies: + "@types/express" "*" + "@types/node@*", "@types/node@^20.12.13": version "20.14.10" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.10.tgz#a1a218290f1b6428682e3af044785e5874db469a"