고원빈

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
1 +{
2 + "name": "server",
3 + "version": "1.0.0",
4 + "lockfileVersion": 1,
5 + "requires": true,
6 + "dependencies": {
7 + "balanced-match": {
8 + "version": "1.0.2",
9 + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
10 + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
11 + },
12 + "base64-js": {
13 + "version": "1.5.1",
14 + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
15 + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
16 + },
17 + "bl": {
18 + "version": "4.1.0",
19 + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
20 + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
21 + "requires": {
22 + "buffer": "^5.5.0",
23 + "inherits": "^2.0.4",
24 + "readable-stream": "^3.4.0"
25 + }
26 + },
27 + "brace-expansion": {
28 + "version": "1.1.11",
29 + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
30 + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
31 + "requires": {
32 + "balanced-match": "^1.0.0",
33 + "concat-map": "0.0.1"
34 + }
35 + },
36 + "buffer": {
37 + "version": "5.7.1",
38 + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
39 + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
40 + "requires": {
41 + "base64-js": "^1.3.1",
42 + "ieee754": "^1.1.13"
43 + }
44 + },
45 + "buffer-from": {
46 + "version": "1.1.1",
47 + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
48 + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
49 + },
50 + "callback-stream": {
51 + "version": "1.1.0",
52 + "resolved": "https://registry.npmjs.org/callback-stream/-/callback-stream-1.1.0.tgz",
53 + "integrity": "sha1-RwGlEmbwbgbqpx/BcjOCLYdfSQg=",
54 + "requires": {
55 + "inherits": "^2.0.1",
56 + "readable-stream": "> 1.0.0 < 3.0.0"
57 + },
58 + "dependencies": {
59 + "readable-stream": {
60 + "version": "2.3.7",
61 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
62 + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
63 + "requires": {
64 + "core-util-is": "~1.0.0",
65 + "inherits": "~2.0.3",
66 + "isarray": "~1.0.0",
67 + "process-nextick-args": "~2.0.0",
68 + "safe-buffer": "~5.1.1",
69 + "string_decoder": "~1.1.1",
70 + "util-deprecate": "~1.0.1"
71 + }
72 + },
73 + "safe-buffer": {
74 + "version": "5.1.2",
75 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
76 + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
77 + },
78 + "string_decoder": {
79 + "version": "1.1.1",
80 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
81 + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
82 + "requires": {
83 + "safe-buffer": "~5.1.0"
84 + }
85 + }
86 + }
87 + },
88 + "commist": {
89 + "version": "1.1.0",
90 + "resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz",
91 + "integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==",
92 + "requires": {
93 + "leven": "^2.1.0",
94 + "minimist": "^1.1.0"
95 + }
96 + },
97 + "concat-map": {
98 + "version": "0.0.1",
99 + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
100 + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
101 + },
102 + "concat-stream": {
103 + "version": "2.0.0",
104 + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
105 + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
106 + "requires": {
107 + "buffer-from": "^1.0.0",
108 + "inherits": "^2.0.3",
109 + "readable-stream": "^3.0.2",
110 + "typedarray": "^0.0.6"
111 + }
112 + },
113 + "core-util-is": {
114 + "version": "1.0.2",
115 + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
116 + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
117 + },
118 + "debug": {
119 + "version": "4.3.1",
120 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
121 + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
122 + "requires": {
123 + "ms": "2.1.2"
124 + }
125 + },
126 + "duplexify": {
127 + "version": "3.7.1",
128 + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
129 + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
130 + "requires": {
131 + "end-of-stream": "^1.0.0",
132 + "inherits": "^2.0.1",
133 + "readable-stream": "^2.0.0",
134 + "stream-shift": "^1.0.0"
135 + },
136 + "dependencies": {
137 + "readable-stream": {
138 + "version": "2.3.7",
139 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
140 + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
141 + "requires": {
142 + "core-util-is": "~1.0.0",
143 + "inherits": "~2.0.3",
144 + "isarray": "~1.0.0",
145 + "process-nextick-args": "~2.0.0",
146 + "safe-buffer": "~5.1.1",
147 + "string_decoder": "~1.1.1",
148 + "util-deprecate": "~1.0.1"
149 + }
150 + },
151 + "safe-buffer": {
152 + "version": "5.1.2",
153 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
154 + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
155 + },
156 + "string_decoder": {
157 + "version": "1.1.1",
158 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
159 + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
160 + "requires": {
161 + "safe-buffer": "~5.1.0"
162 + }
163 + }
164 + }
165 + },
166 + "end-of-stream": {
167 + "version": "1.4.4",
168 + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
169 + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
170 + "requires": {
171 + "once": "^1.4.0"
172 + }
173 + },
174 + "extend": {
175 + "version": "3.0.2",
176 + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
177 + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
178 + },
179 + "fs.realpath": {
180 + "version": "1.0.0",
181 + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
182 + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
183 + },
184 + "glob": {
185 + "version": "7.1.6",
186 + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
187 + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
188 + "requires": {
189 + "fs.realpath": "^1.0.0",
190 + "inflight": "^1.0.4",
191 + "inherits": "2",
192 + "minimatch": "^3.0.4",
193 + "once": "^1.3.0",
194 + "path-is-absolute": "^1.0.0"
195 + }
196 + },
197 + "glob-parent": {
198 + "version": "3.1.0",
199 + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
200 + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
201 + "requires": {
202 + "is-glob": "^3.1.0",
203 + "path-dirname": "^1.0.0"
204 + }
205 + },
206 + "glob-stream": {
207 + "version": "6.1.0",
208 + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
209 + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=",
210 + "requires": {
211 + "extend": "^3.0.0",
212 + "glob": "^7.1.1",
213 + "glob-parent": "^3.1.0",
214 + "is-negated-glob": "^1.0.0",
215 + "ordered-read-streams": "^1.0.0",
216 + "pumpify": "^1.3.5",
217 + "readable-stream": "^2.1.5",
218 + "remove-trailing-separator": "^1.0.1",
219 + "to-absolute-glob": "^2.0.0",
220 + "unique-stream": "^2.0.2"
221 + },
222 + "dependencies": {
223 + "readable-stream": {
224 + "version": "2.3.7",
225 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
226 + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
227 + "requires": {
228 + "core-util-is": "~1.0.0",
229 + "inherits": "~2.0.3",
230 + "isarray": "~1.0.0",
231 + "process-nextick-args": "~2.0.0",
232 + "safe-buffer": "~5.1.1",
233 + "string_decoder": "~1.1.1",
234 + "util-deprecate": "~1.0.1"
235 + }
236 + },
237 + "safe-buffer": {
238 + "version": "5.1.2",
239 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
240 + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
241 + },
242 + "string_decoder": {
243 + "version": "1.1.1",
244 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
245 + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
246 + "requires": {
247 + "safe-buffer": "~5.1.0"
248 + }
249 + }
250 + }
251 + },
252 + "help-me": {
253 + "version": "1.1.0",
254 + "resolved": "https://registry.npmjs.org/help-me/-/help-me-1.1.0.tgz",
255 + "integrity": "sha1-jy1QjQYAtKRW2i8IZVbn5cBWo8Y=",
256 + "requires": {
257 + "callback-stream": "^1.0.2",
258 + "glob-stream": "^6.1.0",
259 + "through2": "^2.0.1",
260 + "xtend": "^4.0.0"
261 + }
262 + },
263 + "ieee754": {
264 + "version": "1.2.1",
265 + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
266 + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
267 + },
268 + "inflight": {
269 + "version": "1.0.6",
270 + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
271 + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
272 + "requires": {
273 + "once": "^1.3.0",
274 + "wrappy": "1"
275 + }
276 + },
277 + "inherits": {
278 + "version": "2.0.4",
279 + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
280 + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
281 + },
282 + "is-absolute": {
283 + "version": "1.0.0",
284 + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
285 + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
286 + "requires": {
287 + "is-relative": "^1.0.0",
288 + "is-windows": "^1.0.1"
289 + }
290 + },
291 + "is-extglob": {
292 + "version": "2.1.1",
293 + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
294 + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
295 + },
296 + "is-glob": {
297 + "version": "3.1.0",
298 + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
299 + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
300 + "requires": {
301 + "is-extglob": "^2.1.0"
302 + }
303 + },
304 + "is-negated-glob": {
305 + "version": "1.0.0",
306 + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
307 + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI="
308 + },
309 + "is-relative": {
310 + "version": "1.0.0",
311 + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
312 + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
313 + "requires": {
314 + "is-unc-path": "^1.0.0"
315 + }
316 + },
317 + "is-unc-path": {
318 + "version": "1.0.0",
319 + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
320 + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
321 + "requires": {
322 + "unc-path-regex": "^0.1.2"
323 + }
324 + },
325 + "is-windows": {
326 + "version": "1.0.2",
327 + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
328 + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
329 + },
330 + "isarray": {
331 + "version": "1.0.0",
332 + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
333 + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
334 + },
335 + "json-stable-stringify-without-jsonify": {
336 + "version": "1.0.1",
337 + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
338 + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
339 + },
340 + "leven": {
341 + "version": "2.1.0",
342 + "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
343 + "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA="
344 + },
345 + "minimatch": {
346 + "version": "3.0.4",
347 + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
348 + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
349 + "requires": {
350 + "brace-expansion": "^1.1.7"
351 + }
352 + },
353 + "minimist": {
354 + "version": "1.2.5",
355 + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
356 + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
357 + },
358 + "mqtt": {
359 + "version": "4.2.6",
360 + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.6.tgz",
361 + "integrity": "sha512-GpxVObyOzL0CGPBqo6B04GinN8JLk12NRYAIkYvARd9ZCoJKevvOyCaWK6bdK/kFSDj3LPDnCsJbezzNlsi87Q==",
362 + "requires": {
363 + "commist": "^1.0.0",
364 + "concat-stream": "^2.0.0",
365 + "debug": "^4.1.1",
366 + "help-me": "^1.0.1",
367 + "inherits": "^2.0.3",
368 + "minimist": "^1.2.5",
369 + "mqtt-packet": "^6.6.0",
370 + "pump": "^3.0.0",
371 + "readable-stream": "^3.6.0",
372 + "reinterval": "^1.1.0",
373 + "split2": "^3.1.0",
374 + "ws": "^7.3.1",
375 + "xtend": "^4.0.2"
376 + }
377 + },
378 + "mqtt-packet": {
379 + "version": "6.9.1",
380 + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.9.1.tgz",
381 + "integrity": "sha512-0+u0ZoRj6H6AuzNY5d8qzXzyXmFI19gkdPRA14kGfKvbqYcpOL+HWUGHjtCxHqjm8CscwsH+dX0+Rxx4se5HSA==",
382 + "requires": {
383 + "bl": "^4.0.2",
384 + "debug": "^4.1.1",
385 + "process-nextick-args": "^2.0.1"
386 + }
387 + },
388 + "ms": {
389 + "version": "2.1.2",
390 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
391 + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
392 + },
393 + "once": {
394 + "version": "1.4.0",
395 + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
396 + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
397 + "requires": {
398 + "wrappy": "1"
399 + }
400 + },
401 + "ordered-read-streams": {
402 + "version": "1.0.1",
403 + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
404 + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=",
405 + "requires": {
406 + "readable-stream": "^2.0.1"
407 + },
408 + "dependencies": {
409 + "readable-stream": {
410 + "version": "2.3.7",
411 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
412 + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
413 + "requires": {
414 + "core-util-is": "~1.0.0",
415 + "inherits": "~2.0.3",
416 + "isarray": "~1.0.0",
417 + "process-nextick-args": "~2.0.0",
418 + "safe-buffer": "~5.1.1",
419 + "string_decoder": "~1.1.1",
420 + "util-deprecate": "~1.0.1"
421 + }
422 + },
423 + "safe-buffer": {
424 + "version": "5.1.2",
425 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
426 + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
427 + },
428 + "string_decoder": {
429 + "version": "1.1.1",
430 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
431 + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
432 + "requires": {
433 + "safe-buffer": "~5.1.0"
434 + }
435 + }
436 + }
437 + },
438 + "path-dirname": {
439 + "version": "1.0.2",
440 + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
441 + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
442 + },
443 + "path-is-absolute": {
444 + "version": "1.0.1",
445 + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
446 + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
447 + },
448 + "process-nextick-args": {
449 + "version": "2.0.1",
450 + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
451 + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
452 + },
453 + "pump": {
454 + "version": "3.0.0",
455 + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
456 + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
457 + "requires": {
458 + "end-of-stream": "^1.1.0",
459 + "once": "^1.3.1"
460 + }
461 + },
462 + "pumpify": {
463 + "version": "1.5.1",
464 + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
465 + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
466 + "requires": {
467 + "duplexify": "^3.6.0",
468 + "inherits": "^2.0.3",
469 + "pump": "^2.0.0"
470 + },
471 + "dependencies": {
472 + "pump": {
473 + "version": "2.0.1",
474 + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
475 + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
476 + "requires": {
477 + "end-of-stream": "^1.1.0",
478 + "once": "^1.3.1"
479 + }
480 + }
481 + }
482 + },
483 + "readable-stream": {
484 + "version": "3.6.0",
485 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
486 + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
487 + "requires": {
488 + "inherits": "^2.0.3",
489 + "string_decoder": "^1.1.1",
490 + "util-deprecate": "^1.0.1"
491 + }
492 + },
493 + "reinterval": {
494 + "version": "1.1.0",
495 + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz",
496 + "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc="
497 + },
498 + "remove-trailing-separator": {
499 + "version": "1.1.0",
500 + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
501 + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
502 + },
503 + "safe-buffer": {
504 + "version": "5.2.1",
505 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
506 + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
507 + },
508 + "split2": {
509 + "version": "3.2.2",
510 + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
511 + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
512 + "requires": {
513 + "readable-stream": "^3.0.0"
514 + }
515 + },
516 + "stream-shift": {
517 + "version": "1.0.1",
518 + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
519 + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="
520 + },
521 + "string_decoder": {
522 + "version": "1.3.0",
523 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
524 + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
525 + "requires": {
526 + "safe-buffer": "~5.2.0"
527 + }
528 + },
529 + "through2": {
530 + "version": "2.0.5",
531 + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
532 + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
533 + "requires": {
534 + "readable-stream": "~2.3.6",
535 + "xtend": "~4.0.1"
536 + },
537 + "dependencies": {
538 + "readable-stream": {
539 + "version": "2.3.7",
540 + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
541 + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
542 + "requires": {
543 + "core-util-is": "~1.0.0",
544 + "inherits": "~2.0.3",
545 + "isarray": "~1.0.0",
546 + "process-nextick-args": "~2.0.0",
547 + "safe-buffer": "~5.1.1",
548 + "string_decoder": "~1.1.1",
549 + "util-deprecate": "~1.0.1"
550 + }
551 + },
552 + "safe-buffer": {
553 + "version": "5.1.2",
554 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
555 + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
556 + },
557 + "string_decoder": {
558 + "version": "1.1.1",
559 + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
560 + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
561 + "requires": {
562 + "safe-buffer": "~5.1.0"
563 + }
564 + }
565 + }
566 + },
567 + "through2-filter": {
568 + "version": "3.0.0",
569 + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
570 + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
571 + "requires": {
572 + "through2": "~2.0.0",
573 + "xtend": "~4.0.0"
574 + }
575 + },
576 + "to-absolute-glob": {
577 + "version": "2.0.2",
578 + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
579 + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=",
580 + "requires": {
581 + "is-absolute": "^1.0.0",
582 + "is-negated-glob": "^1.0.0"
583 + }
584 + },
585 + "typedarray": {
586 + "version": "0.0.6",
587 + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
588 + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
589 + },
590 + "unc-path-regex": {
591 + "version": "0.1.2",
592 + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
593 + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo="
594 + },
595 + "unique-stream": {
596 + "version": "2.3.1",
597 + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
598 + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
599 + "requires": {
600 + "json-stable-stringify-without-jsonify": "^1.0.1",
601 + "through2-filter": "^3.0.0"
602 + }
603 + },
604 + "util-deprecate": {
605 + "version": "1.0.2",
606 + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
607 + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
608 + },
609 + "wrappy": {
610 + "version": "1.0.2",
611 + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
612 + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
613 + },
614 + "ws": {
615 + "version": "7.4.5",
616 + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
617 + "integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g=="
618 + },
619 + "xtend": {
620 + "version": "4.0.2",
621 + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
622 + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
623 + }
624 + }
625 +}
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