-
NodeJS/채팅 만들기 (BackEnd)Language/NodeJS 2022. 9. 2. 10:41
frontend는 별도 backend는 node js로 구성된 서비스
실질적인 작업은 일주일도 하지 못하고 완성된 작품이라서 부족한게 많지만 다음에는 좀더 좋게 만들기 위해서 간단히 기록해 둔다.
다음에는 타입스크립트 및 세션베이스로 고려해보자.
package.json
{ "name": "test-nodejs", "version": "1.0.0", "description": "test nodejs", "main": "index.js", "author": "kand.deokjoon", "license": "ISC", "type": "module", "scripts": { "build": "babel . --ignore node_modules,build --out-dir build", "start:local": "MODE=local nodemon --watch src/ src/server.js" }, "dependencies": { "chalk-template": "^0.2.0", "cors": "^2.8.5", "express": "^4.17.2", "http": "^0.0.1-security", "module": "^1.2.5", "moment": "^2.29.1", "sockjs": "^0.3.24", "url": "^0.11.0" }, "devDependencies": { "@babel/cli": "^7.16.0", "@babel/core": "^7.16.5", "@babel/node": "^7.16.5", "@babel/preset-env": "^7.16.5", "nodemon": "^2.0.15" } }
server.js
import Server from './lib/server.js' global.g_channels = {} global.g_clients = {} const server = new Server()
socket-events.js
import chalk from 'chalk' import constants from './lib/constants.js' import log from './lib/log.js' import socketService from './services/socket-service.js' import chatService from './services/chat-service.js' const socketEvents = { /** * sockjs - connection event * * @param {*} socket */ connection: (socket) => { log.socket(`${chalk.underline(socket.id)} : connected (${socket.headers['x-forwarded-for']})`) // const params = url.parse(socket.url, true).query // log.info(`seller_no : ${params.seller_no}`) /** * sockjs - connection data event */ socket.on('data', (message) => { try { const params = JSON.parse(message) console.log('\r\n\r\n\r\n') log.info('######################################## DATA STATED ########################################') log.info(`${chalk.bold.magenta.dim('[DATA-TYPE]')} [${params.type}] - (${params.action})`) if (params.type === constants.SOCKET.TYPE) { switch (params.action) { case constants.SOCKET.ACTION.CONNECT: socketService.connect(socket, params) break } } else if (params.type === constants.EASYTALK.TYPE) { switch (params.action) { case constants.EASYTALK.ACTION.JOIN_CHANNEL: if (chatService.isValidUserParams(socket.id, params) === false) { return log.error('권한없음') } chatService.join(socket, params) break case constants.EASYTALK.ACTION.SEND_MESSAGE: if (chatService.isValidChannelParams(socket.id, params) === false) { return log.error('권한없음') } chatService.send(socket.id, params) break case constants.EASYTALK.ACTION.EXIT_CHANNEL: chatService.exit(socket.id, params.is_forever) break case constants.EASYTALK.ACTION.EMIT_CHANNEL_MESSAGE: chatService.emitChannelMessage(params) break } } } catch (err) { return log.error(err) } }) /** * sockjs - connection close event */ socket.on('close', () => { try { if (g_clients.hasOwnProperty(socket.id) === true) { log.socket(`${chalk.underline(socket.id)} : disconnected (${g_clients[socket.id].ip})`) socketService.close(socket.id) } else { log.socket(`${chalk.underline(socket.id)} : disconnected (${socket.headers['x-forwarded-for']})`) } } catch (err) { return log.error(err) } }) }, } export default socketEvents
Config
config/config.js
import baseConfig from './base-config.js' import prdConfig from './prd-config.js' import qaConfig from './qa-config.js' import devConfig from './dev-config.js' import demoConfig from './demo-config.js' import localConfig from './local-config.js' let config = baseConfig; const SERVER_HOSTS = Object.freeze({ dev: { 'test-dev-001': { EZP_API_HOST: 'dev-001.test.co.kr' }, ... }, qa: { 'test-qa-001': { EZP_API_HOST: 'qa-001.test.co.kr' }, ... }, ... }) console.log('TEST process.env.MODE : ', process.env.MODE) console.log('TEST process.env.HOST_NAME : ', process.env.HOST_NAME) if (process.env.MODE == 'local') { config = Object.assign({}, config, localConfig); } else { if (process.env.MODE == 'prd') { config = Object.assign({}, config, prdConfig); } else if (process.env.MODE == 'qa') { config = Object.assign({}, config, qaConfig); } else if (process.env.MODE == 'dev') { config = Object.assign({}, config, devConfig); } else if (process.env.MODE == 'demo') { config = Object.assign({}, config, demoConfig); } // 서버별 URL 틀려서 API 주소 변경 config.api.host = SERVER_HOSTS[process.env.MODE][process.env.HOST_NAME]['EZP_API_HOST'] console.log('TEST config.api.host : ', config.api.host) } export default config
config/base-config.js
export default { mode: "common", log: true, port: 10002, url: "/", ssl: { use: false, key: "/path/to/your/ssl.key", cert: "/path/to/your/ssl.crt" } }
config/local-config.js
export default { mode: "local", api: { host: 'test.apitest.com', port: 80, serviceId: "test-node", signature: "sdas" } }
Library
lib/server.js
import express from 'express' import sockjs from 'sockjs' import fs from 'fs' import chalk from 'chalk' import * as http from 'http' import https from 'https' import cors from 'cors' import config from '../config/config.js' import log from './log.js' import socketEvents from '../socket-events.js' import routers from '../routes/index.js' // import session from "express-session" export default class Server { constructor() { log.start(`mode ${chalk.bold.red.dim(config.mode.toUpperCase())}`) this.port = process.env.PORT || config.port this.app = express() this.server = null this.sockjs_server = null this.configuration() } /** * configuration */ configuration() { this.setRouter() this.createServer() this.setServerEvent() this.createSocketServer() // this.sessionTest() } /** * routers setting */ setRouter() { this.app.use('/', routers.index) } /** * create server */ createServer() { // http or https setting if (config.ssl.use === false) { log.start('no ssl') this.server = http.createServer(this.app) } else { log.start('ssl') const opt = { key: fs.readFileSync(config.ssl.key), cert: fs.readFileSync(config.ssl.cert) } this.server = https.createServer(opt, this.app) } } /** * set server event */ setServerEvent() { // this.server.listen(this.port) this.server.listen(this.port, '0.0.0.0', () => { // log.start(`server on port ${this.port}!`) }) // server - listening event this.server.on('listening', (args) => { // log.start(`server listening ${this.port}`) const addr = this.server.address() const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port log.start('Listening at ' + bind) }) // server - error event this.server.on('error', (error) => { log.error('server error', error) }) } /** * sockjs - create server */ createSocketServer() { // sockjs - create server this.sockjs_server = sockjs.createServer() this.sockjs_server.installHandlers(this.server, { prefix: '/socket.io', log: function () { } } ) // sockjs - connection event this.sockjs_server.on('connection', socketEvents.connection) } }
lib/request.js
import * as http from 'http' import https from 'https' import log from './log.js' class Request { constructor() { } setOptions(options = {}) { this.options = options } send(options) { try { // log.test('api url', this.options.path) log.test('this.options', this.options) log.test('this.options.host', this.options.host) // TODO. test 및 status code에 따른 분기처리 let req = null if (this.options.port === 443) { req = https.request(this.options, res => { res.setEncoding("utf8") res.on('data', options.ondata) }) } else { req = http.request(this.options, res => { res.setEncoding("utf8") res.on('data', options.ondata) }) } req.on('error', e => { log.error(e) }) req.write(JSON.stringify(options.data)) req.end() } catch(err) { log.error(err) } } } export default Request
lib/log.js
import chalk from 'chalk' import moment from 'moment' moment.locale('ko') const log = { test: (title, message = '') => { if (message === '') { message = title log.print(chalk.rgb(255, 255, 0).bold.dim('[Test] '), message) } else { const type = chalk.rgb(255, 255, 0).bold.dim('[Test] ') + chalk.rgb(255, 255, 0).bold.dim(title) log.print(type, message) } }, socket: message => log.print(chalk.bold.magenta('[Socket] '), message), start: message => log.print(chalk.bold.green.dim('[Node Start] '), message), stop: message => log.print(chalk.bold.red.dim('[Node Stop] '), message), error: message => log.print(chalk.bold.red.dim('[Error] '), message), info: message => log.print(chalk.bold.blue('[Info] '), message), message: message => log.print(chalk.bold.cyan.dim('[Message] '), message), print: (type, message) => console.log(`[${moment(new Date()).format('HH:mm:ss')}] ${type} : `, message) } export default log
lib/constants.js
const constants = { SOCKET: { TYPE: 'socket', ACTION: { CONNECT: 'socket_connect', REFRESH_EASY_TALK_TOP: 'refresh_easy_talk_top', RELOAD_PAGE: 'reload_page' }, TARGET: { SELF: 'self', OTHER: 'other' } }, EASYTALK: { TYPE: 'talk', ACTION: { JOIN_CHANNEL: 'join_channel', SEND_MESSAGE: 'send_message', EXIT_CHANNEL: 'exit_channel', EMIT_CHANNEL_MESSAGE: 'emit_channel_message', RECEIVE_MESSAGE: 'receive_message', REFRESH_CHANNEL: 'refresh_channel', RESET_CHANNEL: 'reset_channel' } }, COMMON: { USER_TYPE_BUYER: 'B', // 쇼핑몰 USER_TYPE_SELLER: 'S', // 도매처 SEND_TYPE_MESSAGE: '10' // 전송 유형 (10: 문자, 20: 주문문의, 21: 주문 문의 (일반주문 상품), 22: 주문 문의 (바로주문 상품) , 30: 상품 문의 (일반주문) } } const deepFreeze = obj => { const propNames = Object.getOwnPropertyNames(obj) for (const name of propNames) { const value = obj[name]; if (value && typeof value === "object") { deepFreeze(value); } } return Object.freeze(obj); } export default deepFreeze(constants)
lib/util/socket-utils.js
import moment from 'moment' import constants from '../constants.js' import log from '../log.js' moment.locale('ko') const socketUtils = { /** * send to clients * * @param {*} to_clients * @param {*} customParams */ sendToClients: (to_clients, customParams) => { for (const [key, to_client] of Object.entries(to_clients)) { to_client.socket.write(JSON.stringify({ ...customParams })); } }, /** * send to join clients by socket id * * @param {*} from_socket_id * @param {*} customParams */ sendToJoinClientsBySocketId: (from_socket_id, customParams) => { const from_client = g_clients[from_socket_id] const to_sockets = g_channels[from_client.channel_id].sockets for (const [key, to_socket] of Object.entries(to_sockets)) { const to_client = g_clients[key] const target = from_client.user_type === to_client.user_type ? constants.SOCKET.TARGET.SELF : constants.SOCKET.TARGET.OTHER to_socket.write(JSON.stringify({ type: constants.EASYTALK.TYPE, target: target, ...customParams })); } }, /** * get join clients by user type * * @param {*} user_type * @param {*} buyer_no * @param {*} seller_no * @returns */ getJoinClientsByUserType: (user_type, buyer_no, seller_no) => { return Object.values(g_clients).filter(client => { if ( client.user_type === user_type && client.buyer_no === buyer_no && client.seller_no === seller_no ) { return true } }) }, /** * get all clients by socket id * * @param {*} from_socket_id * @returns */ getAllClientsBySocketId: from_socket_id => { const { buyer_no, seller_no } = g_clients[from_socket_id] return Object.values(g_clients).filter(client => { if ( client.user_type === constants.COMMON.USER_TYPE_BUYER && client.buyer_no === buyer_no || client.user_type === constants.COMMON.USER_TYPE_SELLER && client.seller_no === seller_no ) { return true } }) }, /** * get all clients by user no * * @param {*} buyer_no * @param {*} seller_no * @returns */ getAllClientsByUserNo: (buyer_no, seller_no) => { return Object.values(g_clients).filter(client => { if ( client.user_type === constants.COMMON.USER_TYPE_BUYER && client.buyer_no === buyer_no || client.user_type === constants.COMMON.USER_TYPE_SELLER && client.seller_no === seller_no ) { return true } }) }, /** * get self clients by socket id * * @param {*} from_socket_id * @returns */ getSelfClientsBySocketId: from_socket_id => { const { user_type, buyer_no, seller_no } = g_clients[from_socket_id] return Object.values(g_clients).filter(client => { if ( (user_type === constants.COMMON.USER_TYPE_BUYER && client.buyer_no === buyer_no) || (user_type === constants.COMMON.USER_TYPE_SELLER && client.seller_no === seller_no)) { return true } }) }, /** * get other clients by socket id * * @param {*} from_socket_id * @returns */ getOtherClientsBySocketId: from_socket_id => { const { user_type, buyer_no, seller_no } = g_clients[from_socket_id] return Object.values(g_clients).filter(client => { if ( (user_type === constants.COMMON.USER_TYPE_BUYER && client.user_type === constants.COMMON.USER_TYPE_SELLER && client.seller_no === seller_no) || (user_type === constants.COMMON.USER_TYPE_SELLER && client.user_type === constants.COMMON.USER_TYPE_BUYER && client.buyer_no === buyer_no)) { return true } }) }, /** * is target join by socket id * * @param {*} from_socket_id * @returns */ isJoinTargetBySocketId: from_socket_id => { const { user_type, buyer_no, seller_no, channel_id } = g_clients[from_socket_id] for (const socket_id of Object.keys(g_channels[channel_id].sockets)) { const client = g_clients[socket_id] if (user_type === constants.COMMON.USER_TYPE_BUYER && client.user_type === constants.COMMON.USER_TYPE_SELLER && client.buyer_no === buyer_no && client.seller_no === seller_no) { return 'T' } else if (user_type === constants.COMMON.USER_TYPE_SELLER && client.user_type === constants.COMMON.USER_TYPE_BUYER && client.buyer_no === buyer_no && client.seller_no === seller_no) { return 'T' } } return 'F' }, /** * is join channel * * @param {*} user_type * @param {*} buyer_no * @param {*} seller_no * @returns */ isJoinChannel: (user_type, buyer_no, seller_no) => { const channel_id = `${buyer_no}-${seller_no}` if (Object.keys(g_channels).includes(channel_id) === true) { for (const socket_id of Object.keys(g_channels[channel_id].sockets)) { const client = g_clients[socket_id] if (client.user_type === user_type && client.buyer_no === buyer_no && client.seller_no === seller_no) { return 'T' } } } return 'F' }, /** * add channel and socket by channel id * * @param {*} channel_id * @param {*} socket */ addChannelAndSocketByChannelId: (channel_id, socket) => { if (Object.keys(g_channels).includes(channel_id) === true) { // 기존 채널 let channelSockets = g_channels[channel_id].sockets if (Object.keys(channelSockets).includes(socket.id) === false) { channelSockets[socket.id] = socket } } else { // 신규 채널 g_channels[channel_id] = { sockets: [] } g_channels[channel_id].sockets[socket.id] = socket } }, /** * add client * * @param {*} channel_id * @param {*} user_type * @param {*} buyer_no * @param {*} seller_no * @param {*} socket */ addClient: (channel_id, user_type, buyer_no, seller_no, socket) => { g_clients[socket.id] = { user_type: user_type, buyer_no: buyer_no, seller_no: seller_no, socket: socket, ip: socket.headers['x-forwarded-for'] } if (channel_id !== null) { g_clients[socket.id].channel_id = channel_id } }, /** * delete channel by socket id * * @param {*} delete_socket_id */ deleteChannelBySocketId: delete_socket_id => { if (Object.keys(g_clients).includes(delete_socket_id) === true) { if (g_clients[delete_socket_id].hasOwnProperty('channel_id') === true) { const before_channel_id = g_clients[delete_socket_id].channel_id log.test('before_channel_id', before_channel_id) delete g_channels[before_channel_id].sockets[delete_socket_id] if (Object.keys(g_channels[before_channel_id].sockets).length === 0) { delete g_channels[before_channel_id] } } } }, /** * delete client by socket id * * @param {*} delete_socket_id */ deleteClientBySocketId: delete_socket_id => { delete g_clients[delete_socket_id] }, /** * delete join client by socket id * * @param {*} delete_socket_id */ deleteJoinClientBySocketId: delete_socket_id => { delete g_clients[delete_socket_id].channel_id if (g_clients[delete_socket_id].user_type === constants.COMMON.USER_TYPE_BUYER) { delete g_clients[delete_socket_id].seller_no } else if (g_clients[delete_socket_id].user_type === constants.COMMON.USER_TYPE_SELLER) { delete g_clients[delete_socket_id].buyer_no } } } export default socketUtils
Services
services/socket-service.js
import moment from 'moment' import log from '../lib/log.js' import socketUtils from '../lib/util/socket-utils.js' moment.locale('ko') const socketService = { /** * connect * * @param {*} socket * @param {*} params */ connect: (socket, params) => { const { buyer_no, seller_no, user_type } = params socketUtils.addClient(null, user_type, buyer_no, seller_no, socket) log.test('channels', g_channels) log.test('clients', g_clients) }, /** * close * * @param {*} socket */ close: (from_socket_id) => { socketUtils.deleteChannelBySocketId(from_socket_id) socketUtils.deleteClientBySocketId(from_socket_id) log.test('close from_socket_id', from_socket_id) log.test('close channels', g_channels) log.test('close clients', g_clients) } } export default socketService
services/chat-service.js
import moment from 'moment' import constants from '../lib/constants.js' import log from '../lib/log.js' import ezpApi from '../api/ezp-api.js' import socketUtils from '../lib/util/socket-utils.js' moment.locale('ko') const chatService = { /** * is valid user params * * @param {*} from_socket_id * @param {*} params * @returns */ isValidUserParams: (from_socket_id, params) => { if (Object.keys(g_clients).includes(from_socket_id) === true) { const from_client = g_clients[from_socket_id] if (from_client.user_type === constants.COMMON.USER_TYPE_BUYER && from_client.user_type === params.user_type && from_client.buyer_no === params.buyer_no) { return true } else if (from_client.user_type === constants.COMMON.USER_TYPE_SELLER && from_client.user_type === params.user_type && from_client.seller_no === params.seller_no) { return true } else { return false } } return true }, /** * is valid channel params * @param {*} from_socket_id * @param {*} params * @returns */ isValidChannelParams: (from_socket_id, params) => { if (Object.keys(g_clients).includes(from_socket_id) === true) { const from_client = g_clients[from_socket_id] if (from_client.user_type === params.user_type && from_client.buyer_no === params.buyer_no && from_client.seller_no === params.seller_no) { return true } else { return false } } return false }, /** * join * * @param {*} socket * @param {*} params */ join: (socket, params) => { const { user_type, buyer_no, seller_no } = params const channel_id = `${buyer_no}-${seller_no}` socketUtils.deleteChannelBySocketId(socket.id) socketUtils.addChannelAndSocketByChannelId(channel_id, socket) socketUtils.addClient(channel_id, user_type, buyer_no, seller_no, socket) ezpApi.join({ user_type: user_type, buyer_no: buyer_no, seller_no: seller_no, success: res => { try { if (res.meta.code === 200) { log.test('SUCCESS res.code', res.meta.code) // TOP 새로고침 const self_clients = socketUtils.getSelfClientsBySocketId(socket.id) /* socketUtils.sendToClients(self_clients, { type: constants.SOCKET.TYPE, action: constants.SOCKET.ACTION.REFRESH_EASY_TALK_TOP, unread_count: Number(res.response.target_unread_count) }) */ const self_other_clients = Object.values(self_clients).filter(client => { // 자신 제외 if (client.socket.id !== socket.id) { return true } }) // 채널 새로고침 socketUtils.sendToClients(self_other_clients, { type: constants.EASYTALK.TYPE, action: constants.EASYTALK.ACTION.REFRESH_CHANNEL }) } else { log.test('ERROR res.code', res.meta.code) } } catch (err) { return log.error(err); } } }) log.test('join channels', g_channels) log.test('join clients', g_clients) }, /** * send * * @param {*} socket * @param {*} params */ send: (from_socket_id, params) => { const { buyer_no, seller_no, message } = params const send_ts = moment(new Date()).format('YYYY.MM.DD HH:mm:ss') const is_join_target = socketUtils.isJoinTargetBySocketId(from_socket_id) log.test('is_join_target', is_join_target) ezpApi.send({ ...params, send_ts, is_join_target, success: res => { try { if (res.meta.code === 200) { log.test('SUCCESS res.code', res.meta.code) /* if (is_join_target === 'F') { // TOP 새로고침 const other_clients = socketUtils.getOtherClientsBySocketId(from_socket_id) socketUtils.sendToClients(other_clients, { type: constants.SOCKET.TYPE, action: constants.SOCKET.ACTION.REFRESH_EASY_TALK_TOP, unread_count: Number(res.response.target_unread_count) }) } */ // 채널 새로고침 const all_clients = socketUtils.getAllClientsBySocketId(from_socket_id) log.test('all_clients', all_clients); socketUtils.sendToClients(all_clients, { type: constants.EASYTALK.TYPE, action: constants.EASYTALK.ACTION.REFRESH_CHANNEL, buyer_no: buyer_no, seller_no: seller_no }) } else { log.test('ERROR res.code', res.meta.code) } } catch (err) { return log.error(err); } } }) // 채널 접속자들 대상으로 메세지 전송 socketUtils.sendToJoinClientsBySocketId(from_socket_id, { action: constants.EASYTALK.ACTION.RECEIVE_MESSAGE, send_type: constants.COMMON.SEND_TYPE_MESSAGE, message: message, send_ts: send_ts }) }, /** * exit * * @param {*} socket */ exit: (from_socket_id, is_forever) => { const { user_type, buyer_no, seller_no } = g_clients[from_socket_id] if (is_forever === true) { // 나가기 ezpApi.exit({ user_type, buyer_no, seller_no, success: res => { try { log.test('API exit', res) // 채널 나가기 - 대상 const self_clients = socketUtils.getSelfClientsBySocketId(from_socket_id) socketUtils.deleteChannelBySocketId(from_socket_id) socketUtils.deleteJoinClientBySocketId(from_socket_id) // 채널 나가기 - 리로드 socketUtils.sendToClients(self_clients, { type: constants.EASYTALK.TYPE, action: constants.EASYTALK.ACTION.RESET_CHANNEL, buyer_no: buyer_no, seller_no: seller_no }) } catch (err) { return log.error(err); } } }) } else { // 뒤로가기 socketUtils.deleteChannelBySocketId(from_socket_id) socketUtils.deleteJoinClientBySocketId(from_socket_id) } }, /** * 이지톡 - 메세지 조회 및 전송 * * @param {*} params */ emitChannelMessage: (params) => { const { buyer_no, seller_no, easytalk_message_nos } = params const is_join_seller = socketUtils.isJoinChannel(constants.COMMON.USER_TYPE_SELLER, buyer_no, seller_no) ezpApi.getEasypickMessages({ buyer_no, seller_no, easytalk_message_nos, is_join_seller, success: res => { try { if (res.meta.code === 200) { log.test('SUCCESS res.code', res.meta.code) const join_buyer_clients = socketUtils.getJoinClientsByUserType(constants.COMMON.USER_TYPE_BUYER, buyer_no, seller_no) const join_seller_clients = socketUtils.getJoinClientsByUserType(constants.COMMON.USER_TYPE_SELLER, buyer_no, seller_no) for (const row of res.response.message_list) { let send_params = { action: constants.EASYTALK.ACTION.RECEIVE_MESSAGE, type: constants.EASYTALK.TYPE, send_type: row.send_type, message: row.send_message, message_json: row.message_json, send_ts: row.send_ts } // 쇼핑몰 전송 socketUtils.sendToClients(join_buyer_clients, { target: constants.SOCKET.TARGET.SELF, ...send_params }) // 도매처 전송 socketUtils.sendToClients(join_seller_clients, { target: constants.SOCKET.TARGET.OTHER, ...send_params }) } // 채널 새로고침 const all_clients = socketUtils.getAllClientsByUserNo(buyer_no, seller_no) socketUtils.sendToClients(all_clients, { type: constants.EASYTALK.TYPE, action: constants.EASYTALK.ACTION.REFRESH_CHANNEL }) } else { log.test('ERROR res.code', res.meta.code) } } catch (err) { return log.error(err); } } }) }, } export default chatService
Api
api/test-api.js
import crypto from 'crypto' import log from '../lib/log.js' import config from '../config/config.js' import Request from '../lib/request.js' import constants from '../lib/constants.js' const ezpApi = { /** * eazy pick api - headers * * @returns */ getHeaders: () => { const timestamp = new Date().getTime() const signature = crypto.createHmac('sha256', config.api.signature).update(`0=${config.api.serviceId}&1=${timestamp}`).digest().toString('base64') return { "Content-Type": "application/json; charset=utf-8", "ezp-service-id": config.api.serviceId, "ezp-current-timestamp": timestamp, "ezp-signature": signature } }, /** * send message * * @param {*} params * @param {*} send_dt * @returns */ send: ({ user_type, buyer_no, seller_no, message, send_ts, is_join_target, success }) => { try { const request = new Request(); request.setOptions({ host: config.api.host, port: config.api.port, path: '/api/v2/easytalk/send', method: 'POST', headers: ezpApi.getHeaders() }); request.send({ data: { send_type: constants.COMMON.SEND_TYPE_MESSAGE, send_user_type: user_type, buyer_no: buyer_no, seller_no: seller_no, send_message: message, send_ts: send_ts, is_join_target: is_join_target }, ondata: (res) => { success(JSON.parse(res)) } }) } catch (err) { return log.error(err); } }, /** * exit * * @param {*} user_type * @param {*} buyer_no * @param {*} seller_no * @returns */ exit: ({ user_type, buyer_no, seller_no, success }) => { try { const request = new Request(); request.setOptions({ host: config.api.host, port: config.api.port, path: '/api/v2/easytalk/exit', method: 'POST', headers: ezpApi.getHeaders() }); request.send({ data: { user_type: user_type, buyer_no: buyer_no, seller_no: seller_no, }, ondata: (res) => { success(JSON.parse(res)) } }) } catch (err) { return log.error(err); } }, /** * join * * @param {*} param0 * @returns */ join: ({ user_type, buyer_no, seller_no, success }) => { try { const request = new Request(); request.setOptions({ host: config.api.host, port: config.api.port, path: '/api/v2/easytalk/join', method: 'POST', headers: ezpApi.getHeaders() }); request.send({ data: { user_type: user_type, buyer_no: buyer_no, seller_no: seller_no }, ondata: (res) => { success(JSON.parse(res)) } }) } catch (err) { return log.error(err); } }, /** * 이지톡 - 메세지 조회 * * @param {*} param0 * @returns */ getEasypickMessages: ({ buyer_no, seller_no, easytalk_message_nos, is_join_seller, success }) => { try { const request = new Request(); request.setOptions({ host: config.api.host, port: config.api.port, path: '/api/v2/easytalk/getEasypickMessages', method: 'POST', headers: ezpApi.getHeaders() }); request.send({ data: { buyer_no: buyer_no, seller_no: seller_no, is_join_seller: is_join_seller, easytalk_message_nos: easytalk_message_nos }, ondata: (res) => { success(JSON.parse(res)) } }) } catch (err) { return log.error(err); } } } export default ezpApi
Docker
docker-build.sh
#!/usr/bin/env bash docker build -t image-ezp-node:0.1 -f $PWD/docker/Dockerfile .
docker-run.sh
#!/usr/bin/env bash HOSTNAME=`hostname -s` run_script() { MODE='local' if [[ $HOSTNAME =~ ^test-dev ]]; then MODE='dev' elif [[ $HOSTNAME =~ ^test-qa ]]; then MODE='qa' elif [[ $HOSTNAME =~ ^test- || $HOSTNAME =~ ^ezp- ]]; then MODE='prd' elif [[ $HOSTNAME =~ ^test-demo ]]; then MODE='demo' fi if [[ $MODE == 'local' ]]; then docker run --name server-ezp-node -d \ -p 10002:10002 \ -v /home/djkang/project-src/ezp-node/src:/home/ezp-node/src \ --env MODE=$MODE \ --env HOST_NAME=$HOSTNAME \ image-ezp-node:0.1 sh /start-node.sh else docker run --name server-ezp-node -u 1000 -d \ -p 10002:10002 \ -v /home/mes-docker/ezp-node/src:/home/ezp-node/src \ --read-only \ --env MODE=$MODE \ --env HOST_NAME=$HOSTNAME \ --restart=always \ image-ezp-node:0.1 sh /start-node.sh fi } docker stop server-ezp-node docker rm server-ezp-node run_script
docker/Dockerfile
FROM node:16-alpine WORKDIR /home/ezp-node COPY ./yarn* ./package.json ./ COPY ./docker/start-node.sh / ENV TZ=Asia/Seoul # RUN yarn RUN yarn install &&\ chmod +x /start-node.sh
docker/start-node.sh
#!/usr/bin/env bash if [ $MODE = 'local' ]; then getent hosts host.docker.internal | awk '{ print $1 " test.aaaa.com" }' >> /etc/hosts # yarn --cwd /home/djkang/project-src/ezp-node start:dev yarn start:local else cd /home/ezp-node/ node src/server.js fi