고원빈

Merge branch 'server'

.DS_Store
*.log
design/DataTable.pptx
\ No newline at end of file
......
.env
/node_modules
*.log
\ No newline at end of file
# SMB (스마트 약병)
# 박권수 : Web Server & Database
- `Server` : **Node.JS**
- `Web Framework` : **Koa**
- `DBMS` : **Mongo DB**
- `Networking` : **HTTP, MQTT**
# How To Use
1. **Node, Mongo DB Install**
```html
brew install node
brew install mongodb-community@4.4
```
2. **ServiceKey, Mongo DB URL Setting**
```html
// .env
SERVER_PORT=
MONGO_URL=
JWT_SECRET=
SERVICE_KEY=
```
3. **Server On**
```html
npm start
```
# DataBase Table & Field
- **유저 Table / 허브(가칭) Table**
![https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable1.png?raw=true](https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable1.png?raw=true)
- **약병 Table**
![https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable2.png?raw=true](https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable2.png?raw=true)
- **약 정보 Table**
![https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable3.png?raw=true](https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable3.png?raw=true)
# ToDo
- [x] **MQTT Hosting**
→ 5 / 7 : [test.mosquitto.org](http://test.mosquitto.org) 와 raspberry 3.0 model B - mosquitto 설치로 다중 broker 연결, publish & subscribe 확인
- [x] **Middle Ware**
→ 5 / 9 : jwtMiddleWare ⇒ access tokening
- [x] **인증 구현**
→ 5 / 9 : Register, Login, Logout, and Access Token
- [x] **데이터테이블 수정 및 추가 기능 구현**
→ 5 / 9 : schema is changed
- [x] 데이터 처리 로직 구현
- [x] Node.JS의 특정 유저의 MQTT client를 어떻게 모듈화 시킬까 ?
- [x] API 유저 인증 추가
→ 5 / 11 : 각 API에 Authorization 추가
- [x] Bottle API : 데이터 요청 message publishing 추가
→ 5 / 11: Bottle Info 조회 시, Broker로 약병의 현재 상태 요청 메시지 전송
- [x] Hub, Bottle, User unregister 추가 및 연관 데이터 처리
- [x] logic return value 및 status
→ 5 / 11 : ctx.body, status 추가
- [ ] Private IP의 브로커를 웹서버와 연결
- [x] Native Application에 전달할 데이터 규칙 정하기
- [ ] WebServer AWS 배포
- [ ] 안드로이드 <> 서버 <> 브로커 <> 약병 연결하기
⇒ 안드로이드에서 블루투스로 약병 찾은 후, 해당 약병의 정보를 서버로 전송, 서버는 이 정보를 브로커에게 전송 후 블루투스 통신?
- [x] bottleCtrl : lookUpInfo 함수에서 req 보낸 후 응답받은 새로운 bottle을 출력해야 한다.
- [x] Hub 이름 짓기
→ Care Bridge
- [ ] 약병 데이터 업데이트 시간 한국시간으로
[Schedule](https://www.notion.so/cdcc6627a8344c8da56ffb3856bfc1b9)
\ No newline at end of file
const Koa = require('koa');
const Router = require('koa-router');
const bodyparser = require('koa-bodyparser');
const Mongoose = require('mongoose');
const api = require('./src/api');
const updateMedicineInfo = require('./src/lib/UpdatingMedicineInfo');
const MqttServer = require('./src/util/MqttServer');
require('dotenv').config();
const { SERVER_PORT, MONGO_URL } = process.env;
const app = new Koa();
const router = new Router();
Mongoose.connect(MONGO_URL, {
useFindAndModify : false,
useNewUrlParser : true,
useUnifiedTopology: true,
useCreateIndex : true
}).then(() => {
console.log('\x1b[1;32mMongo DB is connected : ', MONGO_URL, '\x1b[0m');
updateMedicineInfo.updateMedicineInfo();
}).catch(e => {
console.log(e);
})
app.use(bodyparser());
router.use('/api', api.routes());
app.use(router.routes()).use(router.allowedMethods());
app.listen(SERVER_PORT, () => {
console.log('\x1b[1;36mPORT : ', SERVER_PORT, 'is connected\x1b[0m');
MqttServer.on();
})
\ No newline at end of file
This diff is collapsed. Click to expand it.
{
"name": "server",
"version": "1.0.0",
"description": "for Smart Medicine Bottle IoT Server",
"main": "index.js",
"scripts": {
"start": "nodemon",
"test": "node test"
},
"repository": {
"type": "git",
"url": "http://khuhub.khu.ac.kr/2021-1-capstone-design1/RIT_Project1.git"
},
"keywords": [
"IoT"
],
"author": "박권수",
"license": "ISC",
"dependencies": {
"mqtt": "^4.2.6"
}
}
//회원가입, 로그인 및 로그아웃에 관한 api
const User = require('../../models/user');
const Joi = require('joi');
exports.register = async(ctx) => {
const { userId, password, passwordCheck } = ctx.request.body;
const schema = Joi.object().keys({
userId : Joi.string().email().max(50).required(),
password : Joi.string().required(),
passwordCheck : Joi.string().required(),
})
const result = schema.validate(ctx.request.body);
if(result.error || password !== passwordCheck) {
ctx.status = 400;
return;
}
const existUser = await User.findByUserId(userId);
if(existUser) {
ctx.status = 409;
return;
}
const user = new User({
userId
});
await user.setPassword(password);
await user.save();
ctx.status = 201;
};
exports.login = async(ctx) => {
const { userId, password } = ctx.request.body;
const schema = Joi.object().keys({
userId : Joi.string().email().max(50).required(),
password : Joi.string().required()
})
const result = schema.validate(ctx.request.body);
if(result.error) {
ctx.status = 400;
return;
}
const user = await User.findByUserId(userId);
if(!user) {
ctx.stauts = 401;
return;
}
const isPasswordTrue = await user.checkPassword(password);
if(!isPasswordTrue) {
ctx.status = 401;
return;
}
const token = await user.generateToken();
ctx.cookies.set('access_token', token, {
httpOnly : true,
maxAge : 1000 * 60 * 60 * 24 * 30
});
ctx.status = 200;
ctx.body = {
userId,
token
};
};
exports.logout = async(ctx) => {
ctx.cookies.set('access_token', null, {
httpOnly : true,
maxAge : 0
});
ctx.status = 204;
};
\ No newline at end of file
const Router = require('koa-router');
const authCtrl = require('./auth.ctrl');
const auth = new Router();
/**
* 회원가입 (email type)
* url : http://localhost:4000/api/auth/register
* request parameter : userId, password, passwordCheck
* return : null
*/
auth.post('/register', authCtrl.register);
/**
* 로그인 (email type)
* url : http://localhost:4000/api/auth/login
* request parameter : userId, password
* return : userId
*/
auth.post('/login', authCtrl.login);
/**
* 로그아웃
* url : http://localhost:4000/api/auth/logout
* request parameter : null
* return : null
*/
auth.post('/logout', authCtrl.logout);
module.exports = auth;
\ No newline at end of file
//어플에서 약병 등록 및, 약병에 관한 정보 조회 = 여기서 mqtt통신으로 broker에 데이터를 요청한다.
const Bottle = require('../../models/bottle');
const Hub = require('../../models/hub');
const Medicine = require('../../models/medicine');
const Mqtt = require('../../lib/MqttModule');
const jwt = require('jsonwebtoken');
//약병 등록
exports.bottleConnect = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { bottleId, hubId } = ctx.request.body;
const isExistBottle = await Bottle.findByBottleId(bottleId);
if(isExistBottle) {
ctx.status = 409;
return;
}
const hub = await Hub.findByHubId(hubId);
if(!hub) {
ctx.status = 404;
return;
}
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const hosting = hub.getHubHost();
if(!hosting) {
ctx.status = 404;
return;
}
const newBottle = new Bottle({
bottleId,
hubId
});
const client = await Mqtt.mqttOn(hosting);
const topic = 'bottle/' + newBottle.getBottleId() + '/bts';
Mqtt.mqttSubscribe(client, topic);
await newBottle.save();
ctx.status = 201;
};
//약병 등록 해제
exports.bottleDisconnect = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { bottleId } = ctx.params;
const bottle = await Bottle.findByBottleId(bottleId);
if(!bottle) {
ctx.status = 404;
return;
}
const hub = await Hub.findByHubId(bottle.getHubId());
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const hosting = hub.getHubHost();
const client = await Mqtt.mqttOn(hosting);
const topic = 'bottle/' + bottleId + '/bts';
Mqtt.mqttUnsubscribe(client, topic);
await Bottle.deleteOne({ bottleId });
ctx.status = 204;
};
//약병 정보를 조회 -> 약병에 현재 데이터를 요청한다. message : req
exports.lookupInfo = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { bottleId } = ctx.params;
const isBottleExist = await Bottle.findByBottleId(bottleId);
if(!isBottleExist) {
ctx.status = 404;
return;
}
const hub = await Hub.findByHubId(isBottleExist.getHubId());
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const hosting = hub.getHubHost();
//서버에서 bottle로 데이터를 요청한다.
const client = await Mqtt.mqttOn(hosting);
const topic = 'bottle/' + bottleId + '/stb';
const message = 'req';
await Mqtt.mqttPublishMessage(client, { topic, message });
const bottle = await Bottle.findByBottleId(bottleId);
ctx.status = 200;
ctx.body = bottle;
}
//약병의 ID를 찾아서 약의 정보를 등록 : Post
exports.setMedicine = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { bottleId } = ctx.params;
const { medicineId, dosage } = ctx.request.body;
const bottle = await Bottle.findByBottleId(bottleId);
if(!bottle) {
ctx.status = 404;
return;
}
const hub = await Hub.findByHubId(bottle.getHubId());
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const medicine = await Medicine.findByMedicineId(medicineId);
if(!medicine) {
ctx.status = 404;
return;
}
await Bottle.findOneAndUpdate({
bottleId
}, {
medicineId,
dosage : parseInt(dosage)
});
ctx.status = 200;
}
//로그인한 유저의 약병 리스트 가져오기
exports.getBottleList = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { hubId } = ctx.params;
const hub = await Hub.findByHubId(hubId);
if(!hub) {
ctx.status = 404;
return;
}
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const bottleList = await Bottle.find({ hubId });
if(!bottleList || !bottleList.length) {
ctx.status = 404;
return;
}
ctx.status = 200;
ctx.body = bottleList;
}
\ No newline at end of file
const Router = require('koa-router');
const bottleCtrl = require('./bottle.ctrl');
const bottle = new Router();
/**
* 약병 연결
* request parameter : bottleId, hubId
* url : http://localhost:4000/api/bottle
* return : null
*/
bottle.post('/', bottleCtrl.bottleConnect);
/**
* 약병 연결 해제
* request parameter : x
* url : http://localhost:4000/api/bottle/:bottleId
* return : null
*/
bottle.delete('/:bottleId', bottleCtrl.bottleDisconnect);
/**
* 약병 정보 확인
* request parameter : x
* url : http://localhost:4000/api/bottle/:bottleId
* return : bottle(json type)
*/
bottle.get('/:bottleId', bottleCtrl.lookupInfo);
/**
* 약병에 약 등록 = 약 검색 후 약 ID(medicineId)와 복용 정보 보고 사용자가 약 복용량(dosage) 입력
* request parameter : medicineId, dosage
* url : http://localhost:4000/api/bottle/:bottleId
* return : bottle(json type)
*/
bottle.patch('/:bottleId', bottleCtrl.setMedicine);
/**
* 현재 로그인한 유저의 허브 중, 해당 허브에 등록된 약병 리스트를 가져옴
* request parameter : x
* url : http://localhost:4000/api/bottle/hub/:hubId
* return : bottle List(json type List)
*/
bottle.get('/hub/:hubId', bottleCtrl.getBottleList)
module.exports = bottle;
\ No newline at end of file
//허브(Mqtt Broker)등록 및 삭제
const Hub = require('../../models/hub');
const Mqtt = require('../../lib/MqttModule');
const DataProcess = require('../../lib/DataProcess');
const jwt = require('jsonwebtoken');
exports.hubConnect = async (ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { hubId, host, port } = ctx.request.body;
const isExistHub = await Hub.findByHubId(hubId);
if(isExistHub) {
ctx.status = 409;
return;
}
const hosting = {
host,
port
};
Mqtt.mqttOn(hosting, DataProcess.dataPublish);
const hub = new Hub({
hubId,
hosting,
userId
});
await hub.save();
ctx.status = 201;
ctx.body = hub;
};
exports.getHubList = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const hubList = await Hub.find({ userId });
if(!hubList || !hubList.length) {
ctx.status = 404;
return;
}
ctx.status = 200;
ctx.body = hubList;
};
exports.hubDisconnect = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { hubId } = ctx.params;
const hub = await Hub.findByHubId(hubId);
if(!hub) {
ctx.status = 404;
return;
}
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const hosting = await hub.getHubHost();
Mqtt.mqttOff(hosting);
await Hub.deleteOne({ hubId });
ctx.status = 204;
};
\ No newline at end of file
const Router = require('koa-router');
const hubCtrl = require('./hub.ctrl');
const hub = new Router();
/**
* 허브 등록
* request parameter : hubId, host, port
* url : http://localhost:4000/api/hub
* return : hub(json type)
*/
hub.post('/', hubCtrl.hubConnect);
/**
* 로그인한 유저의 허브 목록 가져오기
* request parameter : X
* url : http://localhost:4000/api/hub
* return : hub List(json type)
*/
hub.get('/', hubCtrl.getHubList);
/**
* 허브 등록 해제
* request parameter : x
* url : http://localhost:4000/api/hub/:hubId
* return : null
*/
hub.delete('/:hubId', hubCtrl.hubDisconnect);
module.exports = hub;
\ No newline at end of file
const Router = require('koa-router');
const auth = require('./auth');
const bottle = require('./bottle');
const hub = require('./hub');
const medicine = require('./medicine');
const api = new Router();
api.use('/auth', auth.routes());
api.use('/bottle', bottle.routes());
api.use('/hub', hub.routes());
api.use('/medicine', medicine.routes());
module.exports = api;
\ No newline at end of file
const Router = require('koa-router');
const medicineCtrl = require('./medicine.ctrl');
const medicine = new Router();
/**
* 약 검색 후 검색 대상 가져오기
* request parameter : name, company, target 중 하나
* url : http://localhost:4000/api/medicine
* return : medicine List(json 타입의 List)
*/
medicine.post('/', medicineCtrl.medicineSearch);
/**
* 약 검색 후 검색 대상 가져오기
* request parameter : x
* url : http://localhost:4000/api/medicine/:mdedicineId
* return : medicine(json type)
*/
medicine.get('/:medicineId', medicineCtrl.medicineGet);
module.exports = medicine;
\ No newline at end of file
//약의 정보를 검색하는 API : 약명, 제조사, 효능
const Medicine = require('../../models/medicine');
exports.medicineSearch = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { name, company, target } = ctx.request.body;
let result = [];
if (name && name !== '' && name !== undefined)
result = await medicineSearch_ByName(name);
else if (company && company !== '' && company !== undefined)
result = await medicineSearch_ByCompany(company);
else if (target && target !== '' && target !== undefined)
result = await medicineSearch_ByTarget(target);
if(!result.length) {
ctx.status = 404;
return;
}
ctx.status = 200;
ctx.body = result;
}
exports.medicineGet = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { medicineId } = ctx.params;
const medicine = await Medicine.findByMedicineId(medicineId);
if(!medicine) {
ctx.status = 404;
return;
}
ctx.status = 200;
ctx.body = medicine;
}
//이름으로 약 검색
const medicineSearch_ByName = async(name) => {
const result = await Medicine.findByName(name);
return result;
}
//제조사명으로 약 검색
const medicineSearch_ByCompany = async(company) => {
const result = await Medicine.findByCompany(company);
return result;
}
//타겟 병명으로 약 검색
const medicineSearch_ByTarget = async(target) => {
const result = await Medicine.findByTarget(target);
return result;
}
\ No newline at end of file
const Bottle = require('../models/bottle');
//message subscribe 후 message를 가공한 이후 해당 데이터를 보낼 topic과 message를 리턴하는 함수
exports.dataPublish = async (topic, message) => {
//client가 subscribe를 하면 메시지를 보낸 약병의 topic과 message를 가공 및 보낸 약병의 bottleId를 가져옴
const data = await factoring(topic, message);
const { bottleId } = data;
//가공된 데이터를 bottleId의 약병에 업데이트
await bottleInfoUpdate(data);
//가공된 데이터를 메시지로 만들어 topic과 message 리턴
const result = await transPublishingTopicAndMessage(bottleId);
return result;
};
//Hub topic : bottle/bottleId
//Hub로부터 받은 message : 개폐여부/온도/습도/초음파센서
const factoring = async (topic, message) => {
const bottleId = parseInt(topic.split('/')[1]);
const data = message.split('/');
let [isOpen, temperature, humidity, balance] = data;
if(isOpen === '0')
balance = await balanceFactoring(balance);
else balance = '-1';
const openDate = new Date();
return {
bottleId,
isOpen,
openDate,
temperature,
humidity,
balance
};
}
const balanceFactoring = (balance) => {
const max = 10; //Digital Lead Sensor Maximum Value
const slicingBalance = max / 5;
if(parseInt(balance) < slicingBalance || parseInt(balance) > max * 2)
return '80';
else if(parseInt(balance) < slicingBalance * 2)
return '60';
else if(parseInt(balance) < slicingBalance * 3)
return '40';
else if(parseInt(balance) < slicingBalance * 4)
return '20';
else return '0';
}
//bottleId가 포함된 data를 받아서 해당 약병의 data를 업데이트한다.
const bottleInfoUpdate = async(data) => {
let { bottleId, isOpen, openDate, temperature, humidity, balance } = data;
bottleId = parseInt(bottleId);
isOpen = parseInt(isOpen);
temperature = parseFloat(temperature);
humidity = parseFloat(humidity);
balance = parseInt(balance);
if(isOpen) {
await Bottle.findOneAndUpdate({
bottleId
}, { recentOpen : openDate });
}
if(balance !== -1) {
await Bottle.findOneAndUpdate({
bottleId
}, { balance })
}
await Bottle.findOneAndUpdate({
bottleId
}, {
temperature,
humidity
});
}
//해당 MQTT Broker(client)에 bottleId의 정보에 관한 topic과 message를 리턴한다.
const transPublishingTopicAndMessage = async(bottleId) => {
const topic = 'bottle/' + bottleId + '/stb';
const bottle = await Bottle.findByBottleId(bottleId);
const recentOpen = bottle.getRecentOpenDate();
const dosage = bottle.getDosage();
const message = 'res/' + await transDate(recentOpen) + '/' + dosage;
return {
topic,
message
};
}
//날짜를 mmdd로 변환해주는 함수
const transDate = (date) => {
return (date.getMonth() + 1 < 10 ? '0' + String(date.getMonth() + 1) : String(date.getMonth() + 1))
+ (date.getDate() < 10 ? '0' + String(date.getDate()) : String(date.getDate()));
}
\ No newline at end of file
const mqtt = require('mqtt');
const clientList = [];
exports.mqttOn = async (hosting, func) => {
const filterIndex = clientList.findIndex(client => {
return (client.options.clientId === hosting.clientId
&& client.options.host === hosting.host
&& client.options.port === hosting.port)
});
if(filterIndex === -1) {
const client = mqtt.connect(hosting);
clientList.push(client);
client.on('connect', () => {
console.log(`Hub connected: `, client.connected);
});
client.on('message', async (topic, message, packet) => {
const result = await func(topic, message.toString());
console.log('\x1b[1;32msubscribe : topic', topic, 'message : ', message.toString(), '\x1b[0m');
this.mqttPublishMessage(client, result);
});
return client;
}
return clientList[filterIndex];
};
exports.mqttSubscribe = (client, topic) => {
client.subscribe(topic);
};
exports.mqttPublishMessage = (client, { topic, message }) => {
client.publish(topic, message, () => {
console.log('\x1b[1;33mpublish : topic', topic, 'message : ', message, '\x1b[0m');
});
};
exports.mqttUnsubscribe = (client, topic) => {
client.unsubscribe(topic, () => {
console.log('unsubscribe', topic);
});
};
exports.mqttOff = (hosting) => {
const filterIndex = clientList.findIndex(client => {
return (client.options.clientId === hosting.clientId
&& client.options.host === hosting.host
&& client.options.port === hosting.port)
});
if(filterIndex !== -1) {
clientList[filterIndex].end();
clientList.splice(filterIndex, 1);
}
}
\ No newline at end of file
const axios = require('axios');
const Medicine = require('../models/medicine');
exports.updateMedicineInfo = async() => {
const itemArray = await getItemsList(getQueryURL);
await exportJsonData(itemArray);
console.log('\x1b[1;35mAll of data is updated!\x1b[0m');
}
//queryUrl을 return하는 함수 : 한 페이지에 100개의 item씩 요청할 수 있다.
const getQueryURL = (i) => {
const url = "http://apis.data.go.kr/1471000/DrbEasyDrugInfoService/getDrbEasyDrugList";
const queryParams = '?' + encodeURIComponent('ServiceKey') + '=' + process.env.SERVICE_KEY;
const pageNum = '&' + encodeURIComponent('pageNo') + '=' + encodeURIComponent(i);
const numOfItem = '&' + encodeURIComponent('numOfRows') + '=' + encodeURIComponent(100);
const output = '&' + encodeURIComponent('type') + '=' + encodeURIComponent('json');
return url + queryParams + pageNum + numOfItem + output;
}
//모든 page의 item을 list에 push해서 return하는 함수
const getItemsList = async(queryUrl) => {
let i = 1, getItem = null, items = null;
const result = [];
while(true) {
getItem = await axios.get(queryUrl(i));
items = getItem.data.body.items;
if(items === undefined)
return result;
result.push(...items);
console.log('\x1b[100mmedicine data getting processing... : page', i, 'done\x1b[0m');
i++;
}
}
//itemArray에 있는 모든 data를 MongoDB의 SMB collections에 저장함
const exportJsonData = (itemList) => {
itemList.forEach(async item => {
const medicineId = item.itemSeq;
const medicineInfo = {
name : item.itemName,
company : item.entpName,
target : await slicingInfo(item.efcyQesitm),
dosage : await slicingInfo(item.useMethodQesitm),
warn : await slicingInfo(item.atpnWarnQesitm ?
item.atpnWarnQesitm + '\n' + item.atpnQesitm
: item.atpnQesitm),
antiEffect : await slicingInfo(item.seQesitm)
};
Medicine.findOneAndUpdate({
medicineId
}, medicineInfo, {
upsert : true
}).exec();
})
}
//복용 정보에서 불필요한 태그를 제거하고 제거된 값을 반환한다.
const slicingInfo = async (info) => {
let result = info;
if(info) {
result = await info.split('<p>').join('')
.split('</p>').join('')
.split('<sup>').join('')
.split('</sup>').join('')
.split('null').join('');
}
return result;
}
\ No newline at end of file
const jwt = require("jsonwebtoken");
const User = require('../models/user');
const jwtMiddleware = async (ctx, next) => {
const token = ctx.cookies.get("access_token");
if(!token) {
return next();
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
ctx.state.user = {
_id : decoded._id,
userId : decoded.userId
};
const now = Math.floor(Date.now() / 1000);
if (decoded.exp - now < 60 * 60 * 24 * 7) {
const user = await User.findById(decoded._id);
const token = user.generateToken();
ctx.cookies.set('access_token', token, {
httpOnly : true,
maxAge : 1000 * 60 * 60 * 24 * 30
})
}
} catch(e) {
ctx.state.user = null;
}
return next();
};
module.exports = jwtMiddleware;
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const BottleSchema = new Schema ({
bottleId : { type : Number, required : true, unique : true },
temperature : { type : Number, default : 0 },
humidity : { type : Number, default : 0 },
balance : { type : Number, default : 0 },
recentOpen : { type : Date, default : Date.now },
medicineId : { type : Number, default : null, },
hubId : Number,
dosage : { type : Number, default : 0 }
})
BottleSchema.statics.findByBottleId = function(bottleId) {
return this.findOne({ bottleId });
};
BottleSchema.methods.getBottleId = function() {
return this.bottleId;
};
BottleSchema.methods.getRecentOpenDate = function() {
return this.recentOpen;
};
BottleSchema.methods.getTemperature = function() {
return this.temperature;
};
BottleSchema.methods.getHumidity = function() {
return this.humidity;
};
BottleSchema.methods.getBalance = function() {
return this.balance;
};
BottleSchema.methods.getDosage = function() {
return this.dosage;
};
BottleSchema.methods.getMedicineId = function() {
return this.medicineId;
};
BottleSchema.methods.getHubId = function() {
return this.hubId;
};
module.exports = mongoose.model('Bottle', BottleSchema);
\ No newline at end of file
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const HubSchema = new Schema ({
hubId : { type : Number, required : true, unique : true },
hosting : { type : Object, default : null },
userId : { type : String, default : null },
});
HubSchema.statics.findByHubId = function(hubId) {
return this.findOne({ hubId })
};
HubSchema.methods.setHubHost = function(hosting) {
this.hosting = hosting;
};
HubSchema.methods.getHubHost = function() {
return this.hosting;
};
HubSchema.methods.setHub_UserId = function(userId) {
this.userId = userId;
};
HubSchema.methods.getHub_UserId = function() {
return this.userId;
};
module.exports = mongoose.model('Hub', HubSchema);
\ No newline at end of file
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const MedicineSchema = new Schema ({
medicineId : { type : Number, required : true, unique : true },
name : { type : String, required : true },
company : String,
target : { type : String, required : true },
dosage : { type : String, required : true },
warn : { type : String, required : true },
antiEffect : { type : String, required : true }
})
MedicineSchema.statics.findByName = async function(name) {
const all = await this.find().exec();
const result = all.filter(item => {
return item.name.includes(name)
});
return result;
};
MedicineSchema.statics.findByCompany = async function(company) {
const all = await this.find().exec();
const result = all.filter(item => {
return item.company.includes(company)
});
return result;
};
MedicineSchema.statics.findByTarget = async function(target) {
const all = await this.find().exec();
const result = all.filter(item => {
return item.target.includes(target)
});
return result;
};
MedicineSchema.statics.findByMedicineId = function(medicineId) {
return this.findOne({ medicineId })
};
module.exports = mongoose.model('Medicine', MedicineSchema);
\ No newline at end of file
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
userId : { type: String, require : true, unique : true, lowercase : true },
hashedPassword : { type : String, default : null }
});
UserSchema.methods.setPassword = async function(password) {
const hash = await bcrypt.hash(password, 10);
this.hashedPassword = hash;
};
UserSchema.methods.checkPassword = async function(password) {
const result = await bcrypt.compare(password, this.hashedPassword)
return result;
};
UserSchema.statics.findByUserId = async function(userId) {
return this.findOne({ userId });
};
UserSchema.methods.generateToken = function() {
const token = jwt.sign (
{
_id : this._id,
userId : this.userId
},
process.env.JWT_SECRET,
{ expiresIn : '30d' }
);
return token;
};
module.exports = mongoose.model("User", UserSchema);
\ No newline at end of file
const Mqtt = require('../lib/MqttModule');
const DataProcess = require('../lib/DataProcess');
const Hub = require('../models/hub');
const Bottle = require('../models/bottle');
exports.on = async() => {
await subscribeOn();
console.log('\x1b[1;34mMQTT Server On\x1b[0m');
};
const subscribeOn = async () => {
const bottleList = await Bottle.find();
bottleList.forEach(async(bottle) => {
const topic = 'bottle/' + bottle.getBottleId() + '/bts';
const hub = await Hub.findByHubId(bottle.getHubId());
const client = await Mqtt.mqttOn(hub.getHubHost(), DataProcess.dataPublish);
Mqtt.mqttSubscribe(client, topic);
})
};
\ No newline at end of file