Skip to content

Commit

Permalink
feat(master-asset): add compress image on upload
Browse files Browse the repository at this point in the history
  • Loading branch information
ayocodingit committed Aug 23, 2024
1 parent 55091da commit 31a3ed1
Show file tree
Hide file tree
Showing 16 changed files with 227 additions and 26 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=
AWS_BUCKET=

#Generator File
GENERATOR_FILE_URL=
48 changes: 48 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
},
"dependencies": {
"@aws-sdk/client-s3": "^3.310.0",
"axios": "^1.7.4",
"body-parser": "^1.20.1",
"compression": "^1.7.4",
"cors": "^2.8.5",
Expand Down
3 changes: 3 additions & 0 deletions src/config/config.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ export interface Config {
bucket: string
region: string
}
generator_file: {
url: string
}
}
5 changes: 3 additions & 2 deletions src/config/config.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export default Joi.object({
FILE_URI: Joi.string().uri().optional(),
FILE_TYPE: Joi.string()
.optional()
.default('image/jpg,image/png,image/jpeg,image/svg+xml'),
FILE_MAX: Joi.number().optional().default(10),
.default('image/jpg,image/png,image/jpeg,image/svg+xml,image/webp'),
FILE_MAX: Joi.number().optional().default(50),
DB_HOST: Joi.string().required(),
DB_PORT: Joi.number().required(),
DB_USERNAME: Joi.string().required(),
Expand All @@ -25,4 +25,5 @@ export default Joi.object({
AWS_REGION: Joi.string().optional(),
JWT_ACCESS_SECRET: Joi.string().required(),
JWT_ALGORITHM: Joi.string().default('HS256'),
GENERATOR_FILE_URL: Joi.string().uri().optional(),
})
3 changes: 3 additions & 0 deletions src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ const config: Config = {
bucket: env.AWS_BUCKET,
region: env.AWS_REGION,
},
generator_file: {
url: env.GENERATOR_FILE_URL,
},
}

export default config
67 changes: 67 additions & 0 deletions src/external/fileGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import axios from 'axios'
import { Config } from '../config/config.interface'
import Logger from '../pkg/logger'
import S3 from './s3'
import error from '../pkg/error'

class FileGenerator {
constructor(
private config: Config,
private s3: S3,
private logger: Logger
) {}

public async ImageCompression(
uri: string,
quality: number,
convertTo: string,
path: string
) {
try {
const { url, size } = await this.send('convert-image', {
url: uri,
quality,
convertTo,
})

const { data, headers } = await axios.get(url, {
responseType: 'arraybuffer',
})

const contentType = headers['content-type'] || ''

await this.s3.Upload(data, path, contentType)
this.logger.Info('process compress image success', {
category: 'compress-image',
})
return size
} catch (error: any) {
this.logger.Error(
'process compress image failed: ' + error.message,
{ category: 'compress-image' }
)
throw error
}
}

private async send(path: string, body: object) {
try {
const { data } = await axios.post(
this.config.generator_file.url + '/' + path,
body
)

return data.data
} catch (err: any) {
const message = err.message

this.logger.Error(message, {
category: FileGenerator.name,
})

throw new error(err.status, message)
}
}
}

export default FileGenerator
6 changes: 1 addition & 5 deletions src/helpers/file.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import path from 'path'

export const CustomPathFile = (newPath: string, file: any) => {
const ext = path.extname(file.filename)
if (!ext) file.filename = file.filename + path.extname(file.originalname)
return `${newPath}/${file.filename}`
return `${newPath}/${file.originalname}`
}
1 change: 1 addition & 0 deletions src/helpers/regex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export const RegexSanitize = /^[ a-zA-Z0-9_,.()'"&\?\-/]+$/
export const RegexObjectID = /^[0-9a-fA-F]{24}$/
export const RegexContentTypeImage = /^image\//
export const RegexExtensionImage = /.png|.jpg|.jpeg|.svg|.webp/i
export const RegexContentTypeImageNotCompressed = /image\/svg\+xml|image\/webp/
1 change: 0 additions & 1 deletion src/helpers/requestParams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export const GetRequestParams = (query: Record<string, any>): RequestParams => {
sort_order = 'asc'
}


return {
...query,
page,
Expand Down
3 changes: 3 additions & 0 deletions src/modules/images/delivery/http/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class Handler {
category: req.body.category,
tags: req.body.tags,
file: req.file || {},
compression: req.body.compression,
quality: req.body.quality,
convertTo: req.body.convertTo,
})
}

Expand Down
6 changes: 3 additions & 3 deletions src/modules/images/entity/interface.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
export interface Store {
file: File
caption: string
title: string
description: string
category: string
tags: string[]
compression: boolean
quality: number
convertTo: string
}

export interface File {
Expand Down
17 changes: 11 additions & 6 deletions src/modules/images/entity/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,19 @@ const file = Joi.object({
})

export const Store = Joi.object({
caption: Joi.string().regex(RegexSanitize).optional(),
category: Joi.string().alphanum().required(),
tags: Joi.array()
.items(Joi.string().alphanum())
.optional()
.default([])
.unique((a, b) => a == b, { ignoreUndefined: true }),
title: Joi.string().regex(RegexSanitize).optional().default(null),
description: Joi.string().regex(RegexSanitize).optional().default(null),
compression: Joi.boolean().optional().default(false),
quality: Joi.number().min(1).max(100).when('compression', {
is: true,
then: Joi.required(),
otherwise: Joi.optional(),
}),
convertTo: Joi.string().valid('jpeg', 'webp').when('compression', {
is: true,
then: Joi.required(),
otherwise: Joi.optional(),
}),
file,
})
4 changes: 3 additions & 1 deletion src/modules/images/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Handler from './delivery/http/handler'
import Repository from './repository/mongo/repository'
import { Config } from '../../config/config.interface'
import S3 from '../../external/s3'
import FileGenerator from '../../external/fileGenerator'

class Images {
constructor(
Expand All @@ -13,8 +14,9 @@ class Images {
private config: Config
) {
const s3 = new S3(config)
const fileGenerator = new FileGenerator(config, s3, logger)
const repository = new Repository(logger)
const usecase = new Usecase(logger, repository, s3)
const usecase = new Usecase(logger, repository, s3, fileGenerator)
this.loadHttp(usecase)
}

Expand Down
10 changes: 10 additions & 0 deletions src/modules/images/repository/mongo/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ class Repository {

return schemaNew.save()
}

public async UpdateSize(id: string, size: number) {
return imageSchema.findByIdAndUpdate(id, {
'file.size': size,
})
}

public async FindByPath(path: string) {
return imageSchema.findOne({ 'file.path': path }).exec()
}
}

export default Repository
Loading

0 comments on commit 31a3ed1

Please sign in to comment.