Skip to content

Commit

Permalink
admin-imgUrl 작업중 (#528)
Browse files Browse the repository at this point in the history
* admin-imgUrl

* POST admin/ingredient - create ingredient
  • Loading branch information
hanyiseo2 authored Jul 29, 2023
1 parent 3ffe960 commit 5390f4e
Show file tree
Hide file tree
Showing 12 changed files with 457 additions and 55 deletions.
219 changes: 189 additions & 30 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@babel/preset-env": "^7.16.11",
"@babel/preset-typescript": "^7.16.7",
"@opensearch-project/opensearch": "^1.2.0",
"aws-sdk": "^2.1101.0",
"aws-sdk": "^2.1418.0",
"babel-plugin-module-resolver": "^4.1.0",
"body-parser": "^1.20.1",
"chai": "^4.3.6",
Expand All @@ -48,6 +48,7 @@
"lodash": "^4.17.21",
"mongoose": "^6.4.6",
"morgan": "^1.10.0",
"multer": "^1.4.5-lts.1",
"mysql2": "^2.3.3",
"node-cron": "^3.0.2",
"parseurl": "^1.3.3",
Expand All @@ -73,6 +74,7 @@
"@types/lodash": "^4.14.180",
"@types/mocha": "^9.1.0",
"@types/morgan": "^1.9.3",
"@types/multer": "^1.4.7",
"@types/node": "^17.0.45",
"@types/node-cron": "^3.0.2",
"@types/sinon": "^10.0.15",
Expand Down
8 changes: 6 additions & 2 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import properties from '@properties';

import { logger } from '@modules/winston';
import makeMorgan from '@modules/morgan';

import { HttpError } from '@errors';
import statusCode from '@utils/statusCode';
import { verifyTokenMiddleware, encryptPassword } from '@middleware/auth';
Expand All @@ -23,8 +22,10 @@ const {
specs,
swaggerMetadataHandler,
} = require('@modules/swagger');

import { sequelize } from './models';
import { multerConfig } from './config/multerConfig';
import multer from 'multer';

sequelize.sync();

require('@utils/db/mongoose.js');
Expand All @@ -51,13 +52,16 @@ const corsOptionsDelegate: CorsOptionsDelegate<express.Request> = function (

app.use(cors(corsOptionsDelegate));
app.use(bodyParser.json({ limit: '5mb' }));
const upload = multer({ storage: multerConfig.storage });

app.use(
makeMorgan((message: string) => {
logger.http(message);
})
);

app.use(upload.single('file'));

app.use('/docs', swaggerUi.serve, swaggerUi.setup(specs));

app.use(swaggerMetadataHandler);
Expand Down
3 changes: 3 additions & 0 deletions src/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ module.exports = {
dialect: process.env.MYSQL_DEV_DIALECT,
timezone: '+09:00',
logging: false,
s3AccessKey: process.env.AWS_ACCESS_KEY_ID,
s3SecretKey: process.env.AWS_SECRET_ACCESS_KEY,
bucketName: process.env.AWS_BUCKET_NAME,
},
test: {
username: process.env.MYSQL_TST_USERNAME,
Expand Down
15 changes: 15 additions & 0 deletions src/config/multerConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import multer from 'multer';
type FileNameCallback = (error: Error | null, filename: string) => void;

export const multerConfig = {
storage: multer.diskStorage({
destination: 'perfumes/',
filename: function (
_req: any,
file: Express.Multer.File,
cb: FileNameCallback
) {
cb(null, file.originalname);
},
}),
};
10 changes: 10 additions & 0 deletions src/config/s3Config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import AWS from 'aws-sdk';
import config from './config';

const storage: AWS.S3 = new AWS.S3({
accessKeyId: config.development.s3AccessKey,
secretAccessKey: config.development.s3SecretKey,
region: 'ap-northeast-2',
});

export default storage;
147 changes: 133 additions & 14 deletions src/controllers/Admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { ListAndCountDTO } from '@src/data/dto';
import IngredientCategoryService from '@src/service/IngredientCategoryService';
import { DuplicatedEntryError } from '@src/utils/errors/errors';
import * as Hangul from 'hangul-js';
import ImageService from '@src/service/ImageService';
let Admin: AdminService = new AdminService();
let Perfume: PerfumeService = new PerfumeService();
let Ingredient: IngredientService = new IngredientService();
Expand Down Expand Up @@ -441,9 +442,16 @@ export const createIngredientCategory: RequestHandler = async (
}
};

export async function createImageUrl(
file: Express.Multer.File
): Promise<string> {
const fileLocation: string = await ImageService.uploadImagefileToS3(file);
return fileLocation;
}

/**
* @swagger
* /admin/perfumes:
* /admin/perfume:
* post:
* tags:
* - admin
Expand All @@ -452,14 +460,64 @@ export const createIngredientCategory: RequestHandler = async (
* operationId: createPerfume
* produces:
* - application/json
* consumes:
* - multipart/form-data
* parameters:
* - name: body
* in: body
* required: true
* schema:
* $ref: '#/definitions/PerfumeInput'
* responses:
* 200:
* description: success
* schema:
* type: object
* properties:
* message:
* type: string
* 400:
* description: 요청 실패
* 409:
* description: 같은 이름의 카테고리가 존재할 때
* schema:
* type: object
* x-swagger-router-controller: Admin
*/

export const createPerfume: RequestHandler = async (
req: Request,
res: Response
) => {
const { name, englishName, brandIdx, abundanceRate, Notes } = req.body;
try {
await Perfume.create(name, englishName, brandIdx, abundanceRate, Notes);
res.status(StatusCode.OK).json({
message: '성공',
});
} catch (e: any) {
if (e instanceof DuplicatedEntryError) {
res.status(StatusCode.CONFLICT).json(
new ResponseDTO(MSG_EXIST_DUPLICATE_ENTRY, false)
);
} else {
res.status(StatusCode.BAD_REQUEST).json(
new SimpleResponseDTO(e.message)
);
}
}
};

/**
* @swagger
* /admin/perfume/img:
* post:
* tags:
* - admin
* summary: 향수 추가
* description: 향수 추가
* operationId: createPerfumeImg
* consumes:
* - multipart/form-data
* parameters:
* - name: file
* in: formData
* type: file
Expand All @@ -480,21 +538,19 @@ export const createIngredientCategory: RequestHandler = async (
* type: object
* x-swagger-router-controller: Admin
*/
export const createPerfume: RequestHandler = async (

export const createPerfumeImg: RequestHandler = async (
req: Request,
res: Response
) => {
const { name, englishName, brandIdx, abundanceRate, Notes, imageUrl } =
req.body;
console.log(req.file);
if (!req.file)
return res
.status(400)
.json({ error: 'cannot find file from the request' });
const imageUrl = await createImageUrl(req.file);
try {
await Perfume.create(
name,
englishName,
brandIdx,
abundanceRate,
Notes,
imageUrl
);
await Perfume.createImg(imageUrl);
res.status(StatusCode.OK).json({
message: '성공',
});
Expand Down Expand Up @@ -655,3 +711,66 @@ export const createBrand: RequestHandler = async (
}
}
};

/**
* @swagger
* /admin/Ingredient:
* post:
* tags:
* - admin
* summary: 향료 추가
* description: 향료 추가
* operationId: createIngredient
* produces:
* - application/json
* parameters:
* - name: body
* in: body
* required: true
* schema:
* type: object
* properties:
* name:
* type: string
* seriesIdx:
* type: number
* categoryIdx:
* type: number
* responses:
* 200:
* description: success
* schema:
* type: object
* properties:
* message:
* type: string
* 400:
* description: 요청 실패
* 409:
* description: 같은 이름의 카테고리가 존재할 때
* schema:
* type: object
* x-swagger-router-controller: Admin
*/
export const createIngredient: RequestHandler = async (
req: Request,
res: Response
) => {
const { name, seriesIdx, categoryIdx } = req.body;
try {
await Ingredient.create(name, seriesIdx, categoryIdx);
res.status(StatusCode.OK).json({
message: '성공',
});
} catch (e: any) {
if (e instanceof DuplicatedEntryError) {
res.status(StatusCode.CONFLICT).json(
new ResponseDTO(MSG_EXIST_DUPLICATE_ENTRY, false)
);
} else {
res.status(StatusCode.BAD_REQUEST).json(
new SimpleResponseDTO(e.message)
);
}
}
};
11 changes: 11 additions & 0 deletions src/dao/IngredientDao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,17 @@ class IngredientDao {
order: [['createdAt', 'desc']],
});
}

async create(name: string, seriesIdx: number, categoryIdx: number) {
return Ingredient.create({
name,
seriesIdx,
categoryIdx,
englishName: '',
description: '',
imageUrl: '',
});
}
}

export default IngredientDao;
15 changes: 12 additions & 3 deletions src/dao/PerfumeDao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,8 +446,7 @@ class PerfumeDao {
englishName: string,
brandIdx: number,
abundanceRate: number,
Notes: Array<any>,
imageUrl: string
Notes: Array<any>
) {
await sequelize.transaction(async (transaction) => {
const created = await Perfume.create(
Expand All @@ -456,7 +455,7 @@ class PerfumeDao {
englishName,
brandIdx,
abundanceRate,
imageUrl,
imageUrl: '',
story: '',
volumeAndPrice: '',
},
Expand All @@ -470,6 +469,16 @@ class PerfumeDao {
return created;
});
}

async createImg(imageUrl: string | any) {
const created = await Perfume.update(
{
imageUrl,
},
{ where: {} }
);
return created;
}
}

export default PerfumeDao;
44 changes: 43 additions & 1 deletion src/service/ImageService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import S3FileDao from '@src/dao/S3FileDao';
import config from '../config/config';
import fs from 'fs';
import AWS from 'aws-sdk';
import {
DuplicatedEntryError,
FailedToCreateError,
} from '@src/utils/errors/errors';

class ImageService {
s3FileDao: S3FileDao;
Expand All @@ -21,6 +28,41 @@ class ImageService {

return [defaultImage];
}
}

static async uploadImagefileToS3(
fileData: Express.Multer.File
): Promise<string> {
try {
console.log('Inside the Service');
const fileContent: Buffer = fs.readFileSync(fileData.path);

const storage: AWS.S3 = new AWS.S3({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: 'ap-northeast-2',
});
console.log('Inside the Service2');

const params: {
Bucket: string;
Key: string;
Body: Buffer;
} = {
Bucket: config.development.bucketName as string,
Key: fileData.originalname,
Body: fileContent,
};
console.log('Inside the Service3');

const result = await storage.upload(params).promise();
console.log(result.Location);
return result.Location;
} catch (err: Error | any) {
if (err.parent?.errno === 1062) {
throw new DuplicatedEntryError();
}
throw new FailedToCreateError();
}
}
}
export default ImageService;
Loading

0 comments on commit 5390f4e

Please sign in to comment.