Showing
24 changed files
with
1201 additions
and
0 deletions
server/.gitignore
0 → 100644
server/README.md
0 → 100644
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 |
server/index.js
0 → 100644
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 |
server/package-lock.json
0 → 100644
This diff is collapsed. Click to expand it.
server/package.json
0 → 100644
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 | +} |
server/src/api/auth/auth.ctrl.js
0 → 100644
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 |
server/src/api/auth/index.js
0 → 100644
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 |
server/src/api/bottle/bottle.ctrl.js
0 → 100644
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 |
server/src/api/bottle/index.js
0 → 100644
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 |
server/src/api/hub/hub.ctrl.js
0 → 100644
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 |
server/src/api/hub/index.js
0 → 100644
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 |
server/src/api/index.js
0 → 100644
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 |
server/src/api/medicine/index.js
0 → 100644
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 |
server/src/api/medicine/medicine.ctrl.js
0 → 100644
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 |
server/src/lib/DataProcess.js
0 → 100644
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 |
server/src/lib/MqttModule.js
0 → 100644
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 |
server/src/lib/UpdatingMedicineInfo.js
0 → 100644
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 |
server/src/lib/jwtMiddleWare.js
0 → 100644
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; |
server/src/models/bottle.js
0 → 100644
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 |
server/src/models/hub.js
0 → 100644
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 |
server/src/models/medicine.js
0 → 100644
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 |
server/src/models/user.js
0 → 100644
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 |
server/src/util/MqttServer.js
0 → 100644
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 |
-
Please register or login to post a comment