고원빈

Merge branch 'server'

1 .DS_Store 1 .DS_Store
2 *.log 2 *.log
3 +
3 design/DataTable.pptx 4 design/DataTable.pptx
...\ No newline at end of file ...\ No newline at end of file
......
1 +.env
2 +/node_modules
3 +*.log
...\ No newline at end of file ...\ No newline at end of file
1 +# SMB (스마트 약병)
2 +
3 +# 박권수 : Web Server & Database
4 +
5 +- `Server` : **Node.JS**
6 +- `Web Framework` : **Koa**
7 +- `DBMS` : **Mongo DB**
8 +- `Networking` : **HTTP, MQTT**
9 +
10 +# How To Use
11 +
12 +1. **Node, Mongo DB Install**
13 +
14 +```html
15 +brew install node
16 +brew install mongodb-community@4.4
17 +```
18 +
19 + 2. **ServiceKey, Mongo DB URL Setting**
20 +
21 +```html
22 +// .env
23 +SERVER_PORT=
24 +MONGO_URL=
25 +JWT_SECRET=
26 +SERVICE_KEY=
27 +```
28 +
29 + 3. **Server On**
30 +
31 +```html
32 +npm start
33 +```
34 +
35 +# DataBase Table & Field
36 +
37 +- **유저 Table / 허브(가칭) Table**
38 +
39 +![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)
40 +
41 +- **약병 Table**
42 +
43 +![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)
44 +
45 +- **약 정보 Table**
46 +
47 +![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)
48 +
49 +# ToDo
50 +
51 +- [x] **MQTT Hosting**
52 +
53 +→ 5 / 7 : [test.mosquitto.org](http://test.mosquitto.org) 와 raspberry 3.0 model B - mosquitto 설치로 다중 broker 연결, publish & subscribe 확인
54 +
55 +- [x] **Middle Ware**
56 +
57 +→ 5 / 9 : jwtMiddleWare ⇒ access tokening
58 +
59 +- [x] **인증 구현**
60 +
61 +→ 5 / 9 : Register, Login, Logout, and Access Token
62 +
63 +- [x] **데이터테이블 수정 및 추가 기능 구현**
64 +
65 +→ 5 / 9 : schema is changed
66 +
67 +- [x] 데이터 처리 로직 구현
68 +- [x] Node.JS의 특정 유저의 MQTT client를 어떻게 모듈화 시킬까 ?
69 +- [x] API 유저 인증 추가
70 +
71 +→ 5 / 11 : 각 API에 Authorization 추가
72 +
73 +- [x] Bottle API : 데이터 요청 message publishing 추가
74 +
75 +→ 5 / 11: Bottle Info 조회 시, Broker로 약병의 현재 상태 요청 메시지 전송
76 +
77 +- [x] Hub, Bottle, User unregister 추가 및 연관 데이터 처리
78 +- [x] logic return value 및 status
79 +
80 +→ 5 / 11 : ctx.body, status 추가
81 +
82 +- [ ] Private IP의 브로커를 웹서버와 연결
83 +- [x] Native Application에 전달할 데이터 규칙 정하기
84 +- [ ] WebServer AWS 배포
85 +- [ ] 안드로이드 <> 서버 <> 브로커 <> 약병 연결하기
86 +
87 +⇒ 안드로이드에서 블루투스로 약병 찾은 후, 해당 약병의 정보를 서버로 전송, 서버는 이 정보를 브로커에게 전송 후 블루투스 통신?
88 +
89 +- [x] bottleCtrl : lookUpInfo 함수에서 req 보낸 후 응답받은 새로운 bottle을 출력해야 한다.
90 +- [x] Hub 이름 짓기
91 +
92 +→ Care Bridge
93 +
94 +- [ ] 약병 데이터 업데이트 시간 한국시간으로
95 +
96 +[Schedule](https://www.notion.so/cdcc6627a8344c8da56ffb3856bfc1b9)
...\ No newline at end of file ...\ No newline at end of file
1 +const Koa = require('koa');
2 +const Router = require('koa-router');
3 +const bodyparser = require('koa-bodyparser');
4 +
5 +const Mongoose = require('mongoose');
6 +const api = require('./src/api');
7 +const updateMedicineInfo = require('./src/lib/UpdatingMedicineInfo');
8 +const MqttServer = require('./src/util/MqttServer');
9 +
10 +require('dotenv').config();
11 +const { SERVER_PORT, MONGO_URL } = process.env;
12 +
13 +const app = new Koa();
14 +const router = new Router();
15 +
16 +
17 +Mongoose.connect(MONGO_URL, {
18 + useFindAndModify : false,
19 + useNewUrlParser : true,
20 + useUnifiedTopology: true,
21 + useCreateIndex : true
22 +}).then(() => {
23 + console.log('\x1b[1;32mMongo DB is connected : ', MONGO_URL, '\x1b[0m');
24 + updateMedicineInfo.updateMedicineInfo();
25 +}).catch(e => {
26 + console.log(e);
27 +})
28 +
29 +app.use(bodyparser());
30 +router.use('/api', api.routes());
31 +app.use(router.routes()).use(router.allowedMethods());
32 +
33 +app.listen(SERVER_PORT, () => {
34 + console.log('\x1b[1;36mPORT : ', SERVER_PORT, 'is connected\x1b[0m');
35 + MqttServer.on();
36 +})
...\ No newline at end of file ...\ No newline at end of file
This diff is collapsed. Click to expand it.
1 +{
2 + "name": "server",
3 + "version": "1.0.0",
4 + "description": "for Smart Medicine Bottle IoT Server",
5 + "main": "index.js",
6 + "scripts": {
7 + "start": "nodemon",
8 + "test": "node test"
9 + },
10 + "repository": {
11 + "type": "git",
12 + "url": "http://khuhub.khu.ac.kr/2021-1-capstone-design1/RIT_Project1.git"
13 + },
14 + "keywords": [
15 + "IoT"
16 + ],
17 + "author": "박권수",
18 + "license": "ISC",
19 + "dependencies": {
20 + "mqtt": "^4.2.6"
21 + }
22 +}
1 +//회원가입, 로그인 및 로그아웃에 관한 api
2 +const User = require('../../models/user');
3 +const Joi = require('joi');
4 +
5 +exports.register = async(ctx) => {
6 + const { userId, password, passwordCheck } = ctx.request.body;
7 +
8 + const schema = Joi.object().keys({
9 + userId : Joi.string().email().max(50).required(),
10 + password : Joi.string().required(),
11 + passwordCheck : Joi.string().required(),
12 + })
13 +
14 + const result = schema.validate(ctx.request.body);
15 + if(result.error || password !== passwordCheck) {
16 + ctx.status = 400;
17 + return;
18 + }
19 +
20 + const existUser = await User.findByUserId(userId);
21 + if(existUser) {
22 + ctx.status = 409;
23 + return;
24 + }
25 +
26 + const user = new User({
27 + userId
28 + });
29 +
30 + await user.setPassword(password);
31 + await user.save();
32 +
33 + ctx.status = 201;
34 +
35 +};
36 +
37 +exports.login = async(ctx) => {
38 + const { userId, password } = ctx.request.body;
39 +
40 + const schema = Joi.object().keys({
41 + userId : Joi.string().email().max(50).required(),
42 + password : Joi.string().required()
43 + })
44 +
45 + const result = schema.validate(ctx.request.body);
46 + if(result.error) {
47 + ctx.status = 400;
48 + return;
49 + }
50 +
51 + const user = await User.findByUserId(userId);
52 + if(!user) {
53 + ctx.stauts = 401;
54 + return;
55 + }
56 +
57 + const isPasswordTrue = await user.checkPassword(password);
58 + if(!isPasswordTrue) {
59 + ctx.status = 401;
60 + return;
61 + }
62 +
63 + const token = await user.generateToken();
64 + ctx.cookies.set('access_token', token, {
65 + httpOnly : true,
66 + maxAge : 1000 * 60 * 60 * 24 * 30
67 + });
68 +
69 + ctx.status = 200;
70 + ctx.body = {
71 + userId,
72 + token
73 + };
74 +
75 +};
76 +
77 +exports.logout = async(ctx) => {
78 + ctx.cookies.set('access_token', null, {
79 + httpOnly : true,
80 + maxAge : 0
81 + });
82 +
83 + ctx.status = 204;
84 +};
...\ No newline at end of file ...\ No newline at end of file
1 +const Router = require('koa-router');
2 +const authCtrl = require('./auth.ctrl');
3 +
4 +const auth = new Router();
5 +
6 +/**
7 + * 회원가입 (email type)
8 + * url : http://localhost:4000/api/auth/register
9 + * request parameter : userId, password, passwordCheck
10 + * return : null
11 + */
12 +auth.post('/register', authCtrl.register);
13 +
14 +/**
15 + * 로그인 (email type)
16 + * url : http://localhost:4000/api/auth/login
17 + * request parameter : userId, password
18 + * return : userId
19 + */
20 +auth.post('/login', authCtrl.login);
21 +
22 +/**
23 + * 로그아웃
24 + * url : http://localhost:4000/api/auth/logout
25 + * request parameter : null
26 + * return : null
27 + */
28 +auth.post('/logout', authCtrl.logout);
29 +
30 +module.exports = auth;
...\ No newline at end of file ...\ No newline at end of file
1 +//어플에서 약병 등록 및, 약병에 관한 정보 조회 = 여기서 mqtt통신으로 broker에 데이터를 요청한다.
2 +const Bottle = require('../../models/bottle');
3 +const Hub = require('../../models/hub');
4 +const Medicine = require('../../models/medicine');
5 +const Mqtt = require('../../lib/MqttModule');
6 +const jwt = require('jsonwebtoken');
7 +
8 +//약병 등록
9 +exports.bottleConnect = async(ctx) => {
10 + const token = ctx.req.headers.authorization;
11 + if(!token || !token.length) {
12 + ctx.status = 401;
13 + return;
14 + }
15 +
16 + const { userId } = jwt.verify(token, process.env.JWT_SECRET);
17 + const { bottleId, hubId } = ctx.request.body;
18 +
19 + const isExistBottle = await Bottle.findByBottleId(bottleId);
20 + if(isExistBottle) {
21 + ctx.status = 409;
22 + return;
23 + }
24 +
25 + const hub = await Hub.findByHubId(hubId);
26 + if(!hub) {
27 + ctx.status = 404;
28 + return;
29 + }
30 + if(hub.getHub_UserId() !== userId) {
31 + ctx.status = 403;
32 + return;
33 + }
34 +
35 + const hosting = hub.getHubHost();
36 + if(!hosting) {
37 + ctx.status = 404;
38 + return;
39 + }
40 +
41 +
42 + const newBottle = new Bottle({
43 + bottleId,
44 + hubId
45 + });
46 +
47 + const client = await Mqtt.mqttOn(hosting);
48 + const topic = 'bottle/' + newBottle.getBottleId() + '/bts';
49 + Mqtt.mqttSubscribe(client, topic);
50 +
51 + await newBottle.save();
52 +
53 + ctx.status = 201;
54 +};
55 +
56 +//약병 등록 해제
57 +exports.bottleDisconnect = async(ctx) => {
58 + const token = ctx.req.headers.authorization;
59 + if(!token || !token.length) {
60 + ctx.status = 401;
61 + return;
62 + }
63 +
64 + const { userId } = jwt.verify(token, process.env.JWT_SECRET);
65 + const { bottleId } = ctx.params;
66 +
67 + const bottle = await Bottle.findByBottleId(bottleId);
68 + if(!bottle) {
69 + ctx.status = 404;
70 + return;
71 + }
72 +
73 + const hub = await Hub.findByHubId(bottle.getHubId());
74 + if(hub.getHub_UserId() !== userId) {
75 + ctx.status = 403;
76 + return;
77 + }
78 +
79 + const hosting = hub.getHubHost();
80 +
81 + const client = await Mqtt.mqttOn(hosting);
82 + const topic = 'bottle/' + bottleId + '/bts';
83 + Mqtt.mqttUnsubscribe(client, topic);
84 +
85 + await Bottle.deleteOne({ bottleId });
86 +
87 + ctx.status = 204;
88 +
89 +};
90 +
91 +//약병 정보를 조회 -> 약병에 현재 데이터를 요청한다. message : req
92 +exports.lookupInfo = async(ctx) => {
93 + const token = ctx.req.headers.authorization;
94 + if(!token || !token.length) {
95 + ctx.status = 401;
96 + return;
97 + }
98 +
99 + const { userId } = jwt.verify(token, process.env.JWT_SECRET);
100 + const { bottleId } = ctx.params;
101 +
102 + const isBottleExist = await Bottle.findByBottleId(bottleId);
103 + if(!isBottleExist) {
104 + ctx.status = 404;
105 + return;
106 + }
107 +
108 + const hub = await Hub.findByHubId(isBottleExist.getHubId());
109 + if(hub.getHub_UserId() !== userId) {
110 + ctx.status = 403;
111 + return;
112 + }
113 +
114 + const hosting = hub.getHubHost();
115 + //서버에서 bottle로 데이터를 요청한다.
116 + const client = await Mqtt.mqttOn(hosting);
117 + const topic = 'bottle/' + bottleId + '/stb';
118 + const message = 'req';
119 + await Mqtt.mqttPublishMessage(client, { topic, message });
120 +
121 + const bottle = await Bottle.findByBottleId(bottleId);
122 +
123 + ctx.status = 200;
124 + ctx.body = bottle;
125 +}
126 +
127 +//약병의 ID를 찾아서 약의 정보를 등록 : Post
128 +exports.setMedicine = async(ctx) => {
129 + const token = ctx.req.headers.authorization;
130 + if(!token || !token.length) {
131 + ctx.status = 401;
132 + return;
133 + }
134 +
135 + const { userId } = jwt.verify(token, process.env.JWT_SECRET);
136 + const { bottleId } = ctx.params;
137 + const { medicineId, dosage } = ctx.request.body;
138 +
139 + const bottle = await Bottle.findByBottleId(bottleId);
140 + if(!bottle) {
141 + ctx.status = 404;
142 + return;
143 + }
144 +
145 + const hub = await Hub.findByHubId(bottle.getHubId());
146 + if(hub.getHub_UserId() !== userId) {
147 + ctx.status = 403;
148 + return;
149 + }
150 +
151 + const medicine = await Medicine.findByMedicineId(medicineId);
152 + if(!medicine) {
153 + ctx.status = 404;
154 + return;
155 + }
156 +
157 + await Bottle.findOneAndUpdate({
158 + bottleId
159 + }, {
160 + medicineId,
161 + dosage : parseInt(dosage)
162 + });
163 +
164 + ctx.status = 200;
165 +}
166 +
167 +//로그인한 유저의 약병 리스트 가져오기
168 +exports.getBottleList = async(ctx) => {
169 + const token = ctx.req.headers.authorization;
170 + if(!token || !token.length) {
171 + ctx.status = 401;
172 + return;
173 + }
174 +
175 + const { userId } = jwt.verify(token, process.env.JWT_SECRET);
176 + const { hubId } = ctx.params;
177 +
178 + const hub = await Hub.findByHubId(hubId);
179 + if(!hub) {
180 + ctx.status = 404;
181 + return;
182 + }
183 +
184 + if(hub.getHub_UserId() !== userId) {
185 + ctx.status = 403;
186 + return;
187 + }
188 +
189 + const bottleList = await Bottle.find({ hubId });
190 + if(!bottleList || !bottleList.length) {
191 + ctx.status = 404;
192 + return;
193 + }
194 +
195 + ctx.status = 200;
196 + ctx.body = bottleList;
197 +
198 +}
...\ No newline at end of file ...\ No newline at end of file
1 +const Router = require('koa-router');
2 +const bottleCtrl = require('./bottle.ctrl');
3 +
4 +const bottle = new Router();
5 +
6 +/**
7 + * 약병 연결
8 + * request parameter : bottleId, hubId
9 + * url : http://localhost:4000/api/bottle
10 + * return : null
11 + */
12 +bottle.post('/', bottleCtrl.bottleConnect);
13 +
14 +/**
15 + * 약병 연결 해제
16 + * request parameter : x
17 + * url : http://localhost:4000/api/bottle/:bottleId
18 + * return : null
19 + */
20 +bottle.delete('/:bottleId', bottleCtrl.bottleDisconnect);
21 +
22 +/**
23 + * 약병 정보 확인
24 + * request parameter : x
25 + * url : http://localhost:4000/api/bottle/:bottleId
26 + * return : bottle(json type)
27 + */
28 +bottle.get('/:bottleId', bottleCtrl.lookupInfo);
29 +
30 +/**
31 + * 약병에 약 등록 = 약 검색 후 약 ID(medicineId)와 복용 정보 보고 사용자가 약 복용량(dosage) 입력
32 + * request parameter : medicineId, dosage
33 + * url : http://localhost:4000/api/bottle/:bottleId
34 + * return : bottle(json type)
35 + */
36 +bottle.patch('/:bottleId', bottleCtrl.setMedicine);
37 +
38 +/**
39 + * 현재 로그인한 유저의 허브 중, 해당 허브에 등록된 약병 리스트를 가져옴
40 + * request parameter : x
41 + * url : http://localhost:4000/api/bottle/hub/:hubId
42 + * return : bottle List(json type List)
43 + */
44 +bottle.get('/hub/:hubId', bottleCtrl.getBottleList)
45 +
46 +module.exports = bottle;
...\ No newline at end of file ...\ No newline at end of file
1 +//허브(Mqtt Broker)등록 및 삭제
2 +const Hub = require('../../models/hub');
3 +const Mqtt = require('../../lib/MqttModule');
4 +const DataProcess = require('../../lib/DataProcess');
5 +const jwt = require('jsonwebtoken');
6 +
7 +exports.hubConnect = async (ctx) => {
8 + const token = ctx.req.headers.authorization;
9 + if(!token || !token.length) {
10 + ctx.status = 401;
11 + return;
12 + }
13 +
14 + const { userId } = jwt.verify(token, process.env.JWT_SECRET);
15 + const { hubId, host, port } = ctx.request.body;
16 +
17 + const isExistHub = await Hub.findByHubId(hubId);
18 + if(isExistHub) {
19 + ctx.status = 409;
20 + return;
21 + }
22 +
23 + const hosting = {
24 + host,
25 + port
26 + };
27 +
28 + Mqtt.mqttOn(hosting, DataProcess.dataPublish);
29 +
30 + const hub = new Hub({
31 + hubId,
32 + hosting,
33 + userId
34 + });
35 +
36 + await hub.save();
37 +
38 + ctx.status = 201;
39 + ctx.body = hub;
40 +};
41 +
42 +exports.getHubList = async(ctx) => {
43 + const token = ctx.req.headers.authorization;
44 + if(!token || !token.length) {
45 + ctx.status = 401;
46 + return;
47 + }
48 +
49 + const { userId } = jwt.verify(token, process.env.JWT_SECRET);
50 + const hubList = await Hub.find({ userId });
51 + if(!hubList || !hubList.length) {
52 + ctx.status = 404;
53 + return;
54 + }
55 +
56 + ctx.status = 200;
57 + ctx.body = hubList;
58 +};
59 +
60 +exports.hubDisconnect = async(ctx) => {
61 + const token = ctx.req.headers.authorization;
62 + if(!token || !token.length) {
63 + ctx.status = 401;
64 + return;
65 + }
66 +
67 + const { userId } = jwt.verify(token, process.env.JWT_SECRET);
68 + const { hubId } = ctx.params;
69 +
70 + const hub = await Hub.findByHubId(hubId);
71 + if(!hub) {
72 + ctx.status = 404;
73 + return;
74 + }
75 + if(hub.getHub_UserId() !== userId) {
76 + ctx.status = 403;
77 + return;
78 + }
79 +
80 + const hosting = await hub.getHubHost();
81 + Mqtt.mqttOff(hosting);
82 +
83 + await Hub.deleteOne({ hubId });
84 +
85 + ctx.status = 204;
86 +};
...\ No newline at end of file ...\ No newline at end of file
1 +const Router = require('koa-router');
2 +const hubCtrl = require('./hub.ctrl');
3 +
4 +const hub = new Router();
5 +
6 +/**
7 + * 허브 등록
8 + * request parameter : hubId, host, port
9 + * url : http://localhost:4000/api/hub
10 + * return : hub(json type)
11 + */
12 +hub.post('/', hubCtrl.hubConnect);
13 +
14 +/**
15 + * 로그인한 유저의 허브 목록 가져오기
16 + * request parameter : X
17 + * url : http://localhost:4000/api/hub
18 + * return : hub List(json type)
19 + */
20 +hub.get('/', hubCtrl.getHubList);
21 +
22 +/**
23 + * 허브 등록 해제
24 + * request parameter : x
25 + * url : http://localhost:4000/api/hub/:hubId
26 + * return : null
27 + */
28 +hub.delete('/:hubId', hubCtrl.hubDisconnect);
29 +
30 +module.exports = hub;
...\ No newline at end of file ...\ No newline at end of file
1 +const Router = require('koa-router');
2 +const auth = require('./auth');
3 +const bottle = require('./bottle');
4 +const hub = require('./hub');
5 +const medicine = require('./medicine');
6 +
7 +const api = new Router();
8 +
9 +api.use('/auth', auth.routes());
10 +api.use('/bottle', bottle.routes());
11 +api.use('/hub', hub.routes());
12 +api.use('/medicine', medicine.routes());
13 +
14 +module.exports = api;
...\ No newline at end of file ...\ No newline at end of file
1 +const Router = require('koa-router');
2 +const medicineCtrl = require('./medicine.ctrl');
3 +
4 +const medicine = new Router();
5 +
6 +/**
7 + * 약 검색 후 검색 대상 가져오기
8 + * request parameter : name, company, target 중 하나
9 + * url : http://localhost:4000/api/medicine
10 + * return : medicine List(json 타입의 List)
11 + */
12 +medicine.post('/', medicineCtrl.medicineSearch);
13 +
14 +/**
15 + * 약 검색 후 검색 대상 가져오기
16 + * request parameter : x
17 + * url : http://localhost:4000/api/medicine/:mdedicineId
18 + * return : medicine(json type)
19 + */
20 +medicine.get('/:medicineId', medicineCtrl.medicineGet);
21 +
22 +module.exports = medicine;
...\ No newline at end of file ...\ No newline at end of file
1 +//약의 정보를 검색하는 API : 약명, 제조사, 효능
2 +const Medicine = require('../../models/medicine');
3 +
4 +exports.medicineSearch = async(ctx) => {
5 + const token = ctx.req.headers.authorization;
6 + if(!token || !token.length) {
7 + ctx.status = 401;
8 + return;
9 + }
10 +
11 + const { name, company, target } = ctx.request.body;
12 +
13 + let result = [];
14 +
15 + if (name && name !== '' && name !== undefined)
16 + result = await medicineSearch_ByName(name);
17 +
18 + else if (company && company !== '' && company !== undefined)
19 + result = await medicineSearch_ByCompany(company);
20 +
21 + else if (target && target !== '' && target !== undefined)
22 + result = await medicineSearch_ByTarget(target);
23 +
24 + if(!result.length) {
25 + ctx.status = 404;
26 + return;
27 + }
28 +
29 + ctx.status = 200;
30 + ctx.body = result;
31 +}
32 +
33 +exports.medicineGet = async(ctx) => {
34 + const token = ctx.req.headers.authorization;
35 + if(!token || !token.length) {
36 + ctx.status = 401;
37 + return;
38 + }
39 +
40 + const { medicineId } = ctx.params;
41 + const medicine = await Medicine.findByMedicineId(medicineId);
42 + if(!medicine) {
43 + ctx.status = 404;
44 + return;
45 + }
46 +
47 + ctx.status = 200;
48 + ctx.body = medicine;
49 +
50 +}
51 +
52 +//이름으로 약 검색
53 +const medicineSearch_ByName = async(name) => {
54 + const result = await Medicine.findByName(name);
55 + return result;
56 +}
57 +
58 +//제조사명으로 약 검색
59 +const medicineSearch_ByCompany = async(company) => {
60 + const result = await Medicine.findByCompany(company);
61 + return result;
62 +}
63 +
64 +//타겟 병명으로 약 검색
65 +const medicineSearch_ByTarget = async(target) => {
66 + const result = await Medicine.findByTarget(target);
67 + return result;
68 +}
...\ No newline at end of file ...\ No newline at end of file
1 +const Bottle = require('../models/bottle');
2 +
3 +//message subscribe 후 message를 가공한 이후 해당 데이터를 보낼 topic과 message를 리턴하는 함수
4 +exports.dataPublish = async (topic, message) => {
5 + //client가 subscribe를 하면 메시지를 보낸 약병의 topic과 message를 가공 및 보낸 약병의 bottleId를 가져옴
6 + const data = await factoring(topic, message);
7 + const { bottleId } = data;
8 +
9 + //가공된 데이터를 bottleId의 약병에 업데이트
10 + await bottleInfoUpdate(data);
11 + //가공된 데이터를 메시지로 만들어 topic과 message 리턴
12 + const result = await transPublishingTopicAndMessage(bottleId);
13 +
14 + return result;
15 +
16 +};
17 +
18 +//Hub topic : bottle/bottleId
19 +//Hub로부터 받은 message : 개폐여부/온도/습도/초음파센서
20 +const factoring = async (topic, message) => {
21 + const bottleId = parseInt(topic.split('/')[1]);
22 + const data = message.split('/');
23 + let [isOpen, temperature, humidity, balance] = data;
24 +
25 + if(isOpen === '0')
26 + balance = await balanceFactoring(balance);
27 + else balance = '-1';
28 +
29 + const openDate = new Date();
30 +
31 + return {
32 + bottleId,
33 + isOpen,
34 + openDate,
35 + temperature,
36 + humidity,
37 + balance
38 + };
39 +
40 +}
41 +
42 +const balanceFactoring = (balance) => {
43 + const max = 10; //Digital Lead Sensor Maximum Value
44 + const slicingBalance = max / 5;
45 +
46 + if(parseInt(balance) < slicingBalance || parseInt(balance) > max * 2)
47 + return '80';
48 + else if(parseInt(balance) < slicingBalance * 2)
49 + return '60';
50 + else if(parseInt(balance) < slicingBalance * 3)
51 + return '40';
52 + else if(parseInt(balance) < slicingBalance * 4)
53 + return '20';
54 + else return '0';
55 +
56 +}
57 +
58 +//bottleId가 포함된 data를 받아서 해당 약병의 data를 업데이트한다.
59 +const bottleInfoUpdate = async(data) => {
60 + let { bottleId, isOpen, openDate, temperature, humidity, balance } = data;
61 +
62 + bottleId = parseInt(bottleId);
63 + isOpen = parseInt(isOpen);
64 + temperature = parseFloat(temperature);
65 + humidity = parseFloat(humidity);
66 + balance = parseInt(balance);
67 +
68 + if(isOpen) {
69 + await Bottle.findOneAndUpdate({
70 + bottleId
71 + }, { recentOpen : openDate });
72 + }
73 +
74 + if(balance !== -1) {
75 + await Bottle.findOneAndUpdate({
76 + bottleId
77 + }, { balance })
78 + }
79 +
80 + await Bottle.findOneAndUpdate({
81 + bottleId
82 + }, {
83 + temperature,
84 + humidity
85 + });
86 +}
87 +
88 +//해당 MQTT Broker(client)에 bottleId의 정보에 관한 topic과 message를 리턴한다.
89 +const transPublishingTopicAndMessage = async(bottleId) => {
90 + const topic = 'bottle/' + bottleId + '/stb';
91 +
92 + const bottle = await Bottle.findByBottleId(bottleId);
93 + const recentOpen = bottle.getRecentOpenDate();
94 + const dosage = bottle.getDosage();
95 +
96 + const message = 'res/' + await transDate(recentOpen) + '/' + dosage;
97 +
98 + return {
99 + topic,
100 + message
101 + };
102 +}
103 +
104 +//날짜를 mmdd로 변환해주는 함수
105 +const transDate = (date) => {
106 + return (date.getMonth() + 1 < 10 ? '0' + String(date.getMonth() + 1) : String(date.getMonth() + 1))
107 + + (date.getDate() < 10 ? '0' + String(date.getDate()) : String(date.getDate()));
108 +}
...\ No newline at end of file ...\ No newline at end of file
1 +const mqtt = require('mqtt');
2 +const clientList = [];
3 +
4 +exports.mqttOn = async (hosting, func) => {
5 + const filterIndex = clientList.findIndex(client => {
6 + return (client.options.clientId === hosting.clientId
7 + && client.options.host === hosting.host
8 + && client.options.port === hosting.port)
9 + });
10 +
11 + if(filterIndex === -1) {
12 + const client = mqtt.connect(hosting);
13 + clientList.push(client);
14 +
15 + client.on('connect', () => {
16 + console.log(`Hub connected: `, client.connected);
17 + });
18 +
19 + client.on('message', async (topic, message, packet) => {
20 + const result = await func(topic, message.toString());
21 + console.log('\x1b[1;32msubscribe : topic', topic, 'message : ', message.toString(), '\x1b[0m');
22 + this.mqttPublishMessage(client, result);
23 + });
24 +
25 + return client;
26 + }
27 +
28 + return clientList[filterIndex];
29 +};
30 +
31 +exports.mqttSubscribe = (client, topic) => {
32 + client.subscribe(topic);
33 +};
34 +
35 +exports.mqttPublishMessage = (client, { topic, message }) => {
36 + client.publish(topic, message, () => {
37 + console.log('\x1b[1;33mpublish : topic', topic, 'message : ', message, '\x1b[0m');
38 + });
39 +};
40 +
41 +exports.mqttUnsubscribe = (client, topic) => {
42 + client.unsubscribe(topic, () => {
43 + console.log('unsubscribe', topic);
44 + });
45 +};
46 +
47 +exports.mqttOff = (hosting) => {
48 + const filterIndex = clientList.findIndex(client => {
49 + return (client.options.clientId === hosting.clientId
50 + && client.options.host === hosting.host
51 + && client.options.port === hosting.port)
52 + });
53 +
54 + if(filterIndex !== -1) {
55 + clientList[filterIndex].end();
56 + clientList.splice(filterIndex, 1);
57 + }
58 +}
...\ No newline at end of file ...\ No newline at end of file
1 +const axios = require('axios');
2 +const Medicine = require('../models/medicine');
3 +
4 +exports.updateMedicineInfo = async() => {
5 + const itemArray = await getItemsList(getQueryURL);
6 + await exportJsonData(itemArray);
7 +
8 + console.log('\x1b[1;35mAll of data is updated!\x1b[0m');
9 +}
10 +
11 +//queryUrl을 return하는 함수 : 한 페이지에 100개의 item씩 요청할 수 있다.
12 +const getQueryURL = (i) => {
13 + const url = "http://apis.data.go.kr/1471000/DrbEasyDrugInfoService/getDrbEasyDrugList";
14 + const queryParams = '?' + encodeURIComponent('ServiceKey') + '=' + process.env.SERVICE_KEY;
15 + const pageNum = '&' + encodeURIComponent('pageNo') + '=' + encodeURIComponent(i);
16 + const numOfItem = '&' + encodeURIComponent('numOfRows') + '=' + encodeURIComponent(100);
17 + const output = '&' + encodeURIComponent('type') + '=' + encodeURIComponent('json');
18 +
19 + return url + queryParams + pageNum + numOfItem + output;
20 +}
21 +
22 +//모든 page의 item을 list에 push해서 return하는 함수
23 +const getItemsList = async(queryUrl) => {
24 + let i = 1, getItem = null, items = null;
25 + const result = [];
26 +
27 + while(true) {
28 + getItem = await axios.get(queryUrl(i));
29 + items = getItem.data.body.items;
30 +
31 + if(items === undefined)
32 + return result;
33 +
34 + result.push(...items);
35 + console.log('\x1b[100mmedicine data getting processing... : page', i, 'done\x1b[0m');
36 + i++;
37 + }
38 +}
39 +
40 +//itemArray에 있는 모든 data를 MongoDB의 SMB collections에 저장함
41 +const exportJsonData = (itemList) => {
42 + itemList.forEach(async item => {
43 + const medicineId = item.itemSeq;
44 + const medicineInfo = {
45 + name : item.itemName,
46 + company : item.entpName,
47 + target : await slicingInfo(item.efcyQesitm),
48 + dosage : await slicingInfo(item.useMethodQesitm),
49 + warn : await slicingInfo(item.atpnWarnQesitm ?
50 + item.atpnWarnQesitm + '\n' + item.atpnQesitm
51 + : item.atpnQesitm),
52 + antiEffect : await slicingInfo(item.seQesitm)
53 + };
54 +
55 + Medicine.findOneAndUpdate({
56 + medicineId
57 + }, medicineInfo, {
58 + upsert : true
59 + }).exec();
60 + })
61 +}
62 +
63 +//복용 정보에서 불필요한 태그를 제거하고 제거된 값을 반환한다.
64 +const slicingInfo = async (info) => {
65 + let result = info;
66 +
67 + if(info) {
68 + result = await info.split('<p>').join('')
69 + .split('</p>').join('')
70 + .split('<sup>').join('')
71 + .split('</sup>').join('')
72 + .split('null').join('');
73 + }
74 +
75 + return result;
76 +}
...\ No newline at end of file ...\ No newline at end of file
1 +const jwt = require("jsonwebtoken");
2 +const User = require('../models/user');
3 +
4 +const jwtMiddleware = async (ctx, next) => {
5 + const token = ctx.cookies.get("access_token");
6 + if(!token) {
7 + return next();
8 + }
9 +
10 + try {
11 + const decoded = jwt.verify(token, process.env.JWT_SECRET);
12 + ctx.state.user = {
13 + _id : decoded._id,
14 + userId : decoded.userId
15 + };
16 + const now = Math.floor(Date.now() / 1000);
17 + if (decoded.exp - now < 60 * 60 * 24 * 7) {
18 + const user = await User.findById(decoded._id);
19 + const token = user.generateToken();
20 +
21 + ctx.cookies.set('access_token', token, {
22 + httpOnly : true,
23 + maxAge : 1000 * 60 * 60 * 24 * 30
24 + })
25 + }
26 +
27 + } catch(e) {
28 + ctx.state.user = null;
29 + }
30 +
31 + return next();
32 +
33 +};
34 +
35 +module.exports = jwtMiddleware;
1 +const mongoose = require('mongoose');
2 +
3 +const Schema = mongoose.Schema;
4 +
5 +const BottleSchema = new Schema ({
6 + bottleId : { type : Number, required : true, unique : true },
7 + temperature : { type : Number, default : 0 },
8 + humidity : { type : Number, default : 0 },
9 + balance : { type : Number, default : 0 },
10 + recentOpen : { type : Date, default : Date.now },
11 + medicineId : { type : Number, default : null, },
12 + hubId : Number,
13 + dosage : { type : Number, default : 0 }
14 +})
15 +
16 +BottleSchema.statics.findByBottleId = function(bottleId) {
17 + return this.findOne({ bottleId });
18 +};
19 +
20 +BottleSchema.methods.getBottleId = function() {
21 + return this.bottleId;
22 +};
23 +
24 +BottleSchema.methods.getRecentOpenDate = function() {
25 + return this.recentOpen;
26 +};
27 +
28 +BottleSchema.methods.getTemperature = function() {
29 + return this.temperature;
30 +};
31 +
32 +BottleSchema.methods.getHumidity = function() {
33 + return this.humidity;
34 +};
35 +
36 +BottleSchema.methods.getBalance = function() {
37 + return this.balance;
38 +};
39 +
40 +BottleSchema.methods.getDosage = function() {
41 + return this.dosage;
42 +};
43 +
44 +BottleSchema.methods.getMedicineId = function() {
45 + return this.medicineId;
46 +};
47 +
48 +BottleSchema.methods.getHubId = function() {
49 + return this.hubId;
50 +};
51 +
52 +module.exports = mongoose.model('Bottle', BottleSchema);
...\ No newline at end of file ...\ No newline at end of file
1 +const mongoose = require('mongoose');
2 +
3 +const Schema = mongoose.Schema;
4 +
5 +const HubSchema = new Schema ({
6 + hubId : { type : Number, required : true, unique : true },
7 + hosting : { type : Object, default : null },
8 + userId : { type : String, default : null },
9 +});
10 +
11 +HubSchema.statics.findByHubId = function(hubId) {
12 + return this.findOne({ hubId })
13 +};
14 +
15 +HubSchema.methods.setHubHost = function(hosting) {
16 + this.hosting = hosting;
17 +};
18 +
19 +HubSchema.methods.getHubHost = function() {
20 + return this.hosting;
21 +};
22 +
23 +HubSchema.methods.setHub_UserId = function(userId) {
24 + this.userId = userId;
25 +};
26 +
27 +HubSchema.methods.getHub_UserId = function() {
28 + return this.userId;
29 +};
30 +
31 +module.exports = mongoose.model('Hub', HubSchema);
...\ No newline at end of file ...\ No newline at end of file
1 +const mongoose = require('mongoose');
2 +
3 +const Schema = mongoose.Schema;
4 +
5 +const MedicineSchema = new Schema ({
6 + medicineId : { type : Number, required : true, unique : true },
7 + name : { type : String, required : true },
8 + company : String,
9 + target : { type : String, required : true },
10 + dosage : { type : String, required : true },
11 + warn : { type : String, required : true },
12 + antiEffect : { type : String, required : true }
13 +})
14 +
15 +MedicineSchema.statics.findByName = async function(name) {
16 + const all = await this.find().exec();
17 + const result = all.filter(item => {
18 + return item.name.includes(name)
19 + });
20 +
21 + return result;
22 +};
23 +
24 +MedicineSchema.statics.findByCompany = async function(company) {
25 + const all = await this.find().exec();
26 + const result = all.filter(item => {
27 + return item.company.includes(company)
28 + });
29 +
30 + return result;
31 +};
32 +
33 +MedicineSchema.statics.findByTarget = async function(target) {
34 + const all = await this.find().exec();
35 + const result = all.filter(item => {
36 + return item.target.includes(target)
37 + });
38 +
39 + return result;
40 +};
41 +
42 +MedicineSchema.statics.findByMedicineId = function(medicineId) {
43 + return this.findOne({ medicineId })
44 +};
45 +
46 +
47 +module.exports = mongoose.model('Medicine', MedicineSchema);
...\ No newline at end of file ...\ No newline at end of file
1 +const mongoose = require('mongoose');
2 +const bcrypt = require('bcrypt');
3 +const jwt = require('jsonwebtoken');
4 +
5 +const Schema = mongoose.Schema;
6 +
7 +const UserSchema = new Schema({
8 + userId : { type: String, require : true, unique : true, lowercase : true },
9 + hashedPassword : { type : String, default : null }
10 +});
11 +
12 +UserSchema.methods.setPassword = async function(password) {
13 + const hash = await bcrypt.hash(password, 10);
14 + this.hashedPassword = hash;
15 +};
16 +
17 +UserSchema.methods.checkPassword = async function(password) {
18 + const result = await bcrypt.compare(password, this.hashedPassword)
19 + return result;
20 +};
21 +
22 +UserSchema.statics.findByUserId = async function(userId) {
23 + return this.findOne({ userId });
24 +};
25 +
26 +UserSchema.methods.generateToken = function() {
27 + const token = jwt.sign (
28 + {
29 + _id : this._id,
30 + userId : this.userId
31 + },
32 + process.env.JWT_SECRET,
33 + { expiresIn : '30d' }
34 + );
35 + return token;
36 +};
37 +
38 +module.exports = mongoose.model("User", UserSchema);
...\ No newline at end of file ...\ No newline at end of file
1 +const Mqtt = require('../lib/MqttModule');
2 +const DataProcess = require('../lib/DataProcess');
3 +const Hub = require('../models/hub');
4 +const Bottle = require('../models/bottle');
5 +
6 +exports.on = async() => {
7 + await subscribeOn();
8 + console.log('\x1b[1;34mMQTT Server On\x1b[0m');
9 +};
10 +
11 +const subscribeOn = async () => {
12 + const bottleList = await Bottle.find();
13 +
14 + bottleList.forEach(async(bottle) => {
15 + const topic = 'bottle/' + bottle.getBottleId() + '/bts';
16 + const hub = await Hub.findByHubId(bottle.getHubId());
17 + const client = await Mqtt.mqttOn(hub.getHubHost(), DataProcess.dataPublish);
18 + Mqtt.mqttSubscribe(client, topic);
19 + })
20 +};
...\ No newline at end of file ...\ No newline at end of file