고원빈

Merge branch 'server'

.DS_Store
*.log
design/DataTable.pptx
\ No newline at end of file
......
.env
/node_modules
*.log
\ No newline at end of file
# SMB (스마트 약병)
# 박권수 : Web Server & Database
- `Server` : **Node.JS**
- `Web Framework` : **Koa**
- `DBMS` : **Mongo DB**
- `Networking` : **HTTP, MQTT**
# How To Use
1. **Node, Mongo DB Install**
```html
brew install node
brew install mongodb-community@4.4
```
2. **ServiceKey, Mongo DB URL Setting**
```html
// .env
SERVER_PORT=
MONGO_URL=
JWT_SECRET=
SERVICE_KEY=
```
3. **Server On**
```html
npm start
```
# DataBase Table & Field
- **유저 Table / 허브(가칭) Table**
![https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable1.png?raw=true](https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable1.png?raw=true)
- **약병 Table**
![https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable2.png?raw=true](https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable2.png?raw=true)
- **약 정보 Table**
![https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable3.png?raw=true](https://github.com/Park-KwonSoo/Smart_Medicine_Bottle/blob/server/design/DataTable3.png?raw=true)
# ToDo
- [x] **MQTT Hosting**
→ 5 / 7 : [test.mosquitto.org](http://test.mosquitto.org) 와 raspberry 3.0 model B - mosquitto 설치로 다중 broker 연결, publish & subscribe 확인
- [x] **Middle Ware**
→ 5 / 9 : jwtMiddleWare ⇒ access tokening
- [x] **인증 구현**
→ 5 / 9 : Register, Login, Logout, and Access Token
- [x] **데이터테이블 수정 및 추가 기능 구현**
→ 5 / 9 : schema is changed
- [x] 데이터 처리 로직 구현
- [x] Node.JS의 특정 유저의 MQTT client를 어떻게 모듈화 시킬까 ?
- [x] API 유저 인증 추가
→ 5 / 11 : 각 API에 Authorization 추가
- [x] Bottle API : 데이터 요청 message publishing 추가
→ 5 / 11: Bottle Info 조회 시, Broker로 약병의 현재 상태 요청 메시지 전송
- [x] Hub, Bottle, User unregister 추가 및 연관 데이터 처리
- [x] logic return value 및 status
→ 5 / 11 : ctx.body, status 추가
- [ ] Private IP의 브로커를 웹서버와 연결
- [x] Native Application에 전달할 데이터 규칙 정하기
- [ ] WebServer AWS 배포
- [ ] 안드로이드 <> 서버 <> 브로커 <> 약병 연결하기
⇒ 안드로이드에서 블루투스로 약병 찾은 후, 해당 약병의 정보를 서버로 전송, 서버는 이 정보를 브로커에게 전송 후 블루투스 통신?
- [x] bottleCtrl : lookUpInfo 함수에서 req 보낸 후 응답받은 새로운 bottle을 출력해야 한다.
- [x] Hub 이름 짓기
→ Care Bridge
- [ ] 약병 데이터 업데이트 시간 한국시간으로
[Schedule](https://www.notion.so/cdcc6627a8344c8da56ffb3856bfc1b9)
\ No newline at end of file
const Koa = require('koa');
const Router = require('koa-router');
const bodyparser = require('koa-bodyparser');
const Mongoose = require('mongoose');
const api = require('./src/api');
const updateMedicineInfo = require('./src/lib/UpdatingMedicineInfo');
const MqttServer = require('./src/util/MqttServer');
require('dotenv').config();
const { SERVER_PORT, MONGO_URL } = process.env;
const app = new Koa();
const router = new Router();
Mongoose.connect(MONGO_URL, {
useFindAndModify : false,
useNewUrlParser : true,
useUnifiedTopology: true,
useCreateIndex : true
}).then(() => {
console.log('\x1b[1;32mMongo DB is connected : ', MONGO_URL, '\x1b[0m');
updateMedicineInfo.updateMedicineInfo();
}).catch(e => {
console.log(e);
})
app.use(bodyparser());
router.use('/api', api.routes());
app.use(router.routes()).use(router.allowedMethods());
app.listen(SERVER_PORT, () => {
console.log('\x1b[1;36mPORT : ', SERVER_PORT, 'is connected\x1b[0m');
MqttServer.on();
})
\ No newline at end of file
{
"name": "server",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"requires": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0"
}
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"buffer-from": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
},
"callback-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/callback-stream/-/callback-stream-1.1.0.tgz",
"integrity": "sha1-RwGlEmbwbgbqpx/BcjOCLYdfSQg=",
"requires": {
"inherits": "^2.0.1",
"readable-stream": "> 1.0.0 < 3.0.0"
},
"dependencies": {
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
"commist": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/commist/-/commist-1.1.0.tgz",
"integrity": "sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==",
"requires": {
"leven": "^2.1.0",
"minimist": "^1.1.0"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"concat-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
"requires": {
"buffer-from": "^1.0.0",
"inherits": "^2.0.3",
"readable-stream": "^3.0.2",
"typedarray": "^0.0.6"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"debug": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
"requires": {
"ms": "2.1.2"
}
},
"duplexify": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
"integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
"requires": {
"end-of-stream": "^1.0.0",
"inherits": "^2.0.1",
"readable-stream": "^2.0.0",
"stream-shift": "^1.0.0"
},
"dependencies": {
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"requires": {
"once": "^1.4.0"
}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"glob-parent": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
"integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
"requires": {
"is-glob": "^3.1.0",
"path-dirname": "^1.0.0"
}
},
"glob-stream": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
"integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=",
"requires": {
"extend": "^3.0.0",
"glob": "^7.1.1",
"glob-parent": "^3.1.0",
"is-negated-glob": "^1.0.0",
"ordered-read-streams": "^1.0.0",
"pumpify": "^1.3.5",
"readable-stream": "^2.1.5",
"remove-trailing-separator": "^1.0.1",
"to-absolute-glob": "^2.0.0",
"unique-stream": "^2.0.2"
},
"dependencies": {
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
"help-me": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/help-me/-/help-me-1.1.0.tgz",
"integrity": "sha1-jy1QjQYAtKRW2i8IZVbn5cBWo8Y=",
"requires": {
"callback-stream": "^1.0.2",
"glob-stream": "^6.1.0",
"through2": "^2.0.1",
"xtend": "^4.0.0"
}
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"is-absolute": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
"integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
"requires": {
"is-relative": "^1.0.0",
"is-windows": "^1.0.1"
}
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
},
"is-glob": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
"integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
"requires": {
"is-extglob": "^2.1.0"
}
},
"is-negated-glob": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
"integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI="
},
"is-relative": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
"integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
"requires": {
"is-unc-path": "^1.0.0"
}
},
"is-unc-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
"integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
"requires": {
"unc-path-regex": "^0.1.2"
}
},
"is-windows": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
"integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="
},
"leven": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
"integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"mqtt": {
"version": "4.2.6",
"resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.6.tgz",
"integrity": "sha512-GpxVObyOzL0CGPBqo6B04GinN8JLk12NRYAIkYvARd9ZCoJKevvOyCaWK6bdK/kFSDj3LPDnCsJbezzNlsi87Q==",
"requires": {
"commist": "^1.0.0",
"concat-stream": "^2.0.0",
"debug": "^4.1.1",
"help-me": "^1.0.1",
"inherits": "^2.0.3",
"minimist": "^1.2.5",
"mqtt-packet": "^6.6.0",
"pump": "^3.0.0",
"readable-stream": "^3.6.0",
"reinterval": "^1.1.0",
"split2": "^3.1.0",
"ws": "^7.3.1",
"xtend": "^4.0.2"
}
},
"mqtt-packet": {
"version": "6.9.1",
"resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-6.9.1.tgz",
"integrity": "sha512-0+u0ZoRj6H6AuzNY5d8qzXzyXmFI19gkdPRA14kGfKvbqYcpOL+HWUGHjtCxHqjm8CscwsH+dX0+Rxx4se5HSA==",
"requires": {
"bl": "^4.0.2",
"debug": "^4.1.1",
"process-nextick-args": "^2.0.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"ordered-read-streams": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
"integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=",
"requires": {
"readable-stream": "^2.0.1"
},
"dependencies": {
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
"path-dirname": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA="
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"pumpify": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
"integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
"requires": {
"duplexify": "^3.6.0",
"inherits": "^2.0.3",
"pump": "^2.0.0"
},
"dependencies": {
"pump": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
"integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
}
}
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"reinterval": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz",
"integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc="
},
"remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"split2": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz",
"integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==",
"requires": {
"readable-stream": "^3.0.0"
}
},
"stream-shift": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
"integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ=="
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
}
},
"through2": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
"requires": {
"readable-stream": "~2.3.6",
"xtend": "~4.0.1"
},
"dependencies": {
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
"through2-filter": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
"integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
"requires": {
"through2": "~2.0.0",
"xtend": "~4.0.0"
}
},
"to-absolute-glob": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
"integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=",
"requires": {
"is-absolute": "^1.0.0",
"is-negated-glob": "^1.0.0"
}
},
"typedarray": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
"unc-path-regex": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
"integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo="
},
"unique-stream": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
"integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
"requires": {
"json-stable-stringify-without-jsonify": "^1.0.1",
"through2-filter": "^3.0.0"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"ws": {
"version": "7.4.5",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g=="
},
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
}
}
}
{
"name": "server",
"version": "1.0.0",
"description": "for Smart Medicine Bottle IoT Server",
"main": "index.js",
"scripts": {
"start": "nodemon",
"test": "node test"
},
"repository": {
"type": "git",
"url": "http://khuhub.khu.ac.kr/2021-1-capstone-design1/RIT_Project1.git"
},
"keywords": [
"IoT"
],
"author": "박권수",
"license": "ISC",
"dependencies": {
"mqtt": "^4.2.6"
}
}
//회원가입, 로그인 및 로그아웃에 관한 api
const User = require('../../models/user');
const Joi = require('joi');
exports.register = async(ctx) => {
const { userId, password, passwordCheck } = ctx.request.body;
const schema = Joi.object().keys({
userId : Joi.string().email().max(50).required(),
password : Joi.string().required(),
passwordCheck : Joi.string().required(),
})
const result = schema.validate(ctx.request.body);
if(result.error || password !== passwordCheck) {
ctx.status = 400;
return;
}
const existUser = await User.findByUserId(userId);
if(existUser) {
ctx.status = 409;
return;
}
const user = new User({
userId
});
await user.setPassword(password);
await user.save();
ctx.status = 201;
};
exports.login = async(ctx) => {
const { userId, password } = ctx.request.body;
const schema = Joi.object().keys({
userId : Joi.string().email().max(50).required(),
password : Joi.string().required()
})
const result = schema.validate(ctx.request.body);
if(result.error) {
ctx.status = 400;
return;
}
const user = await User.findByUserId(userId);
if(!user) {
ctx.stauts = 401;
return;
}
const isPasswordTrue = await user.checkPassword(password);
if(!isPasswordTrue) {
ctx.status = 401;
return;
}
const token = await user.generateToken();
ctx.cookies.set('access_token', token, {
httpOnly : true,
maxAge : 1000 * 60 * 60 * 24 * 30
});
ctx.status = 200;
ctx.body = {
userId,
token
};
};
exports.logout = async(ctx) => {
ctx.cookies.set('access_token', null, {
httpOnly : true,
maxAge : 0
});
ctx.status = 204;
};
\ No newline at end of file
const Router = require('koa-router');
const authCtrl = require('./auth.ctrl');
const auth = new Router();
/**
* 회원가입 (email type)
* url : http://localhost:4000/api/auth/register
* request parameter : userId, password, passwordCheck
* return : null
*/
auth.post('/register', authCtrl.register);
/**
* 로그인 (email type)
* url : http://localhost:4000/api/auth/login
* request parameter : userId, password
* return : userId
*/
auth.post('/login', authCtrl.login);
/**
* 로그아웃
* url : http://localhost:4000/api/auth/logout
* request parameter : null
* return : null
*/
auth.post('/logout', authCtrl.logout);
module.exports = auth;
\ No newline at end of file
//어플에서 약병 등록 및, 약병에 관한 정보 조회 = 여기서 mqtt통신으로 broker에 데이터를 요청한다.
const Bottle = require('../../models/bottle');
const Hub = require('../../models/hub');
const Medicine = require('../../models/medicine');
const Mqtt = require('../../lib/MqttModule');
const jwt = require('jsonwebtoken');
//약병 등록
exports.bottleConnect = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { bottleId, hubId } = ctx.request.body;
const isExistBottle = await Bottle.findByBottleId(bottleId);
if(isExistBottle) {
ctx.status = 409;
return;
}
const hub = await Hub.findByHubId(hubId);
if(!hub) {
ctx.status = 404;
return;
}
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const hosting = hub.getHubHost();
if(!hosting) {
ctx.status = 404;
return;
}
const newBottle = new Bottle({
bottleId,
hubId
});
const client = await Mqtt.mqttOn(hosting);
const topic = 'bottle/' + newBottle.getBottleId() + '/bts';
Mqtt.mqttSubscribe(client, topic);
await newBottle.save();
ctx.status = 201;
};
//약병 등록 해제
exports.bottleDisconnect = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { bottleId } = ctx.params;
const bottle = await Bottle.findByBottleId(bottleId);
if(!bottle) {
ctx.status = 404;
return;
}
const hub = await Hub.findByHubId(bottle.getHubId());
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const hosting = hub.getHubHost();
const client = await Mqtt.mqttOn(hosting);
const topic = 'bottle/' + bottleId + '/bts';
Mqtt.mqttUnsubscribe(client, topic);
await Bottle.deleteOne({ bottleId });
ctx.status = 204;
};
//약병 정보를 조회 -> 약병에 현재 데이터를 요청한다. message : req
exports.lookupInfo = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { bottleId } = ctx.params;
const isBottleExist = await Bottle.findByBottleId(bottleId);
if(!isBottleExist) {
ctx.status = 404;
return;
}
const hub = await Hub.findByHubId(isBottleExist.getHubId());
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const hosting = hub.getHubHost();
//서버에서 bottle로 데이터를 요청한다.
const client = await Mqtt.mqttOn(hosting);
const topic = 'bottle/' + bottleId + '/stb';
const message = 'req';
await Mqtt.mqttPublishMessage(client, { topic, message });
const bottle = await Bottle.findByBottleId(bottleId);
ctx.status = 200;
ctx.body = bottle;
}
//약병의 ID를 찾아서 약의 정보를 등록 : Post
exports.setMedicine = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { bottleId } = ctx.params;
const { medicineId, dosage } = ctx.request.body;
const bottle = await Bottle.findByBottleId(bottleId);
if(!bottle) {
ctx.status = 404;
return;
}
const hub = await Hub.findByHubId(bottle.getHubId());
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const medicine = await Medicine.findByMedicineId(medicineId);
if(!medicine) {
ctx.status = 404;
return;
}
await Bottle.findOneAndUpdate({
bottleId
}, {
medicineId,
dosage : parseInt(dosage)
});
ctx.status = 200;
}
//로그인한 유저의 약병 리스트 가져오기
exports.getBottleList = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { hubId } = ctx.params;
const hub = await Hub.findByHubId(hubId);
if(!hub) {
ctx.status = 404;
return;
}
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const bottleList = await Bottle.find({ hubId });
if(!bottleList || !bottleList.length) {
ctx.status = 404;
return;
}
ctx.status = 200;
ctx.body = bottleList;
}
\ No newline at end of file
const Router = require('koa-router');
const bottleCtrl = require('./bottle.ctrl');
const bottle = new Router();
/**
* 약병 연결
* request parameter : bottleId, hubId
* url : http://localhost:4000/api/bottle
* return : null
*/
bottle.post('/', bottleCtrl.bottleConnect);
/**
* 약병 연결 해제
* request parameter : x
* url : http://localhost:4000/api/bottle/:bottleId
* return : null
*/
bottle.delete('/:bottleId', bottleCtrl.bottleDisconnect);
/**
* 약병 정보 확인
* request parameter : x
* url : http://localhost:4000/api/bottle/:bottleId
* return : bottle(json type)
*/
bottle.get('/:bottleId', bottleCtrl.lookupInfo);
/**
* 약병에 약 등록 = 약 검색 후 약 ID(medicineId)와 복용 정보 보고 사용자가 약 복용량(dosage) 입력
* request parameter : medicineId, dosage
* url : http://localhost:4000/api/bottle/:bottleId
* return : bottle(json type)
*/
bottle.patch('/:bottleId', bottleCtrl.setMedicine);
/**
* 현재 로그인한 유저의 허브 중, 해당 허브에 등록된 약병 리스트를 가져옴
* request parameter : x
* url : http://localhost:4000/api/bottle/hub/:hubId
* return : bottle List(json type List)
*/
bottle.get('/hub/:hubId', bottleCtrl.getBottleList)
module.exports = bottle;
\ No newline at end of file
//허브(Mqtt Broker)등록 및 삭제
const Hub = require('../../models/hub');
const Mqtt = require('../../lib/MqttModule');
const DataProcess = require('../../lib/DataProcess');
const jwt = require('jsonwebtoken');
exports.hubConnect = async (ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { hubId, host, port } = ctx.request.body;
const isExistHub = await Hub.findByHubId(hubId);
if(isExistHub) {
ctx.status = 409;
return;
}
const hosting = {
host,
port
};
Mqtt.mqttOn(hosting, DataProcess.dataPublish);
const hub = new Hub({
hubId,
hosting,
userId
});
await hub.save();
ctx.status = 201;
ctx.body = hub;
};
exports.getHubList = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const hubList = await Hub.find({ userId });
if(!hubList || !hubList.length) {
ctx.status = 404;
return;
}
ctx.status = 200;
ctx.body = hubList;
};
exports.hubDisconnect = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { userId } = jwt.verify(token, process.env.JWT_SECRET);
const { hubId } = ctx.params;
const hub = await Hub.findByHubId(hubId);
if(!hub) {
ctx.status = 404;
return;
}
if(hub.getHub_UserId() !== userId) {
ctx.status = 403;
return;
}
const hosting = await hub.getHubHost();
Mqtt.mqttOff(hosting);
await Hub.deleteOne({ hubId });
ctx.status = 204;
};
\ No newline at end of file
const Router = require('koa-router');
const hubCtrl = require('./hub.ctrl');
const hub = new Router();
/**
* 허브 등록
* request parameter : hubId, host, port
* url : http://localhost:4000/api/hub
* return : hub(json type)
*/
hub.post('/', hubCtrl.hubConnect);
/**
* 로그인한 유저의 허브 목록 가져오기
* request parameter : X
* url : http://localhost:4000/api/hub
* return : hub List(json type)
*/
hub.get('/', hubCtrl.getHubList);
/**
* 허브 등록 해제
* request parameter : x
* url : http://localhost:4000/api/hub/:hubId
* return : null
*/
hub.delete('/:hubId', hubCtrl.hubDisconnect);
module.exports = hub;
\ No newline at end of file
const Router = require('koa-router');
const auth = require('./auth');
const bottle = require('./bottle');
const hub = require('./hub');
const medicine = require('./medicine');
const api = new Router();
api.use('/auth', auth.routes());
api.use('/bottle', bottle.routes());
api.use('/hub', hub.routes());
api.use('/medicine', medicine.routes());
module.exports = api;
\ No newline at end of file
const Router = require('koa-router');
const medicineCtrl = require('./medicine.ctrl');
const medicine = new Router();
/**
* 약 검색 후 검색 대상 가져오기
* request parameter : name, company, target 중 하나
* url : http://localhost:4000/api/medicine
* return : medicine List(json 타입의 List)
*/
medicine.post('/', medicineCtrl.medicineSearch);
/**
* 약 검색 후 검색 대상 가져오기
* request parameter : x
* url : http://localhost:4000/api/medicine/:mdedicineId
* return : medicine(json type)
*/
medicine.get('/:medicineId', medicineCtrl.medicineGet);
module.exports = medicine;
\ No newline at end of file
//약의 정보를 검색하는 API : 약명, 제조사, 효능
const Medicine = require('../../models/medicine');
exports.medicineSearch = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { name, company, target } = ctx.request.body;
let result = [];
if (name && name !== '' && name !== undefined)
result = await medicineSearch_ByName(name);
else if (company && company !== '' && company !== undefined)
result = await medicineSearch_ByCompany(company);
else if (target && target !== '' && target !== undefined)
result = await medicineSearch_ByTarget(target);
if(!result.length) {
ctx.status = 404;
return;
}
ctx.status = 200;
ctx.body = result;
}
exports.medicineGet = async(ctx) => {
const token = ctx.req.headers.authorization;
if(!token || !token.length) {
ctx.status = 401;
return;
}
const { medicineId } = ctx.params;
const medicine = await Medicine.findByMedicineId(medicineId);
if(!medicine) {
ctx.status = 404;
return;
}
ctx.status = 200;
ctx.body = medicine;
}
//이름으로 약 검색
const medicineSearch_ByName = async(name) => {
const result = await Medicine.findByName(name);
return result;
}
//제조사명으로 약 검색
const medicineSearch_ByCompany = async(company) => {
const result = await Medicine.findByCompany(company);
return result;
}
//타겟 병명으로 약 검색
const medicineSearch_ByTarget = async(target) => {
const result = await Medicine.findByTarget(target);
return result;
}
\ No newline at end of file
const Bottle = require('../models/bottle');
//message subscribe 후 message를 가공한 이후 해당 데이터를 보낼 topic과 message를 리턴하는 함수
exports.dataPublish = async (topic, message) => {
//client가 subscribe를 하면 메시지를 보낸 약병의 topic과 message를 가공 및 보낸 약병의 bottleId를 가져옴
const data = await factoring(topic, message);
const { bottleId } = data;
//가공된 데이터를 bottleId의 약병에 업데이트
await bottleInfoUpdate(data);
//가공된 데이터를 메시지로 만들어 topic과 message 리턴
const result = await transPublishingTopicAndMessage(bottleId);
return result;
};
//Hub topic : bottle/bottleId
//Hub로부터 받은 message : 개폐여부/온도/습도/초음파센서
const factoring = async (topic, message) => {
const bottleId = parseInt(topic.split('/')[1]);
const data = message.split('/');
let [isOpen, temperature, humidity, balance] = data;
if(isOpen === '0')
balance = await balanceFactoring(balance);
else balance = '-1';
const openDate = new Date();
return {
bottleId,
isOpen,
openDate,
temperature,
humidity,
balance
};
}
const balanceFactoring = (balance) => {
const max = 10; //Digital Lead Sensor Maximum Value
const slicingBalance = max / 5;
if(parseInt(balance) < slicingBalance || parseInt(balance) > max * 2)
return '80';
else if(parseInt(balance) < slicingBalance * 2)
return '60';
else if(parseInt(balance) < slicingBalance * 3)
return '40';
else if(parseInt(balance) < slicingBalance * 4)
return '20';
else return '0';
}
//bottleId가 포함된 data를 받아서 해당 약병의 data를 업데이트한다.
const bottleInfoUpdate = async(data) => {
let { bottleId, isOpen, openDate, temperature, humidity, balance } = data;
bottleId = parseInt(bottleId);
isOpen = parseInt(isOpen);
temperature = parseFloat(temperature);
humidity = parseFloat(humidity);
balance = parseInt(balance);
if(isOpen) {
await Bottle.findOneAndUpdate({
bottleId
}, { recentOpen : openDate });
}
if(balance !== -1) {
await Bottle.findOneAndUpdate({
bottleId
}, { balance })
}
await Bottle.findOneAndUpdate({
bottleId
}, {
temperature,
humidity
});
}
//해당 MQTT Broker(client)에 bottleId의 정보에 관한 topic과 message를 리턴한다.
const transPublishingTopicAndMessage = async(bottleId) => {
const topic = 'bottle/' + bottleId + '/stb';
const bottle = await Bottle.findByBottleId(bottleId);
const recentOpen = bottle.getRecentOpenDate();
const dosage = bottle.getDosage();
const message = 'res/' + await transDate(recentOpen) + '/' + dosage;
return {
topic,
message
};
}
//날짜를 mmdd로 변환해주는 함수
const transDate = (date) => {
return (date.getMonth() + 1 < 10 ? '0' + String(date.getMonth() + 1) : String(date.getMonth() + 1))
+ (date.getDate() < 10 ? '0' + String(date.getDate()) : String(date.getDate()));
}
\ No newline at end of file
const mqtt = require('mqtt');
const clientList = [];
exports.mqttOn = async (hosting, func) => {
const filterIndex = clientList.findIndex(client => {
return (client.options.clientId === hosting.clientId
&& client.options.host === hosting.host
&& client.options.port === hosting.port)
});
if(filterIndex === -1) {
const client = mqtt.connect(hosting);
clientList.push(client);
client.on('connect', () => {
console.log(`Hub connected: `, client.connected);
});
client.on('message', async (topic, message, packet) => {
const result = await func(topic, message.toString());
console.log('\x1b[1;32msubscribe : topic', topic, 'message : ', message.toString(), '\x1b[0m');
this.mqttPublishMessage(client, result);
});
return client;
}
return clientList[filterIndex];
};
exports.mqttSubscribe = (client, topic) => {
client.subscribe(topic);
};
exports.mqttPublishMessage = (client, { topic, message }) => {
client.publish(topic, message, () => {
console.log('\x1b[1;33mpublish : topic', topic, 'message : ', message, '\x1b[0m');
});
};
exports.mqttUnsubscribe = (client, topic) => {
client.unsubscribe(topic, () => {
console.log('unsubscribe', topic);
});
};
exports.mqttOff = (hosting) => {
const filterIndex = clientList.findIndex(client => {
return (client.options.clientId === hosting.clientId
&& client.options.host === hosting.host
&& client.options.port === hosting.port)
});
if(filterIndex !== -1) {
clientList[filterIndex].end();
clientList.splice(filterIndex, 1);
}
}
\ No newline at end of file
const axios = require('axios');
const Medicine = require('../models/medicine');
exports.updateMedicineInfo = async() => {
const itemArray = await getItemsList(getQueryURL);
await exportJsonData(itemArray);
console.log('\x1b[1;35mAll of data is updated!\x1b[0m');
}
//queryUrl을 return하는 함수 : 한 페이지에 100개의 item씩 요청할 수 있다.
const getQueryURL = (i) => {
const url = "http://apis.data.go.kr/1471000/DrbEasyDrugInfoService/getDrbEasyDrugList";
const queryParams = '?' + encodeURIComponent('ServiceKey') + '=' + process.env.SERVICE_KEY;
const pageNum = '&' + encodeURIComponent('pageNo') + '=' + encodeURIComponent(i);
const numOfItem = '&' + encodeURIComponent('numOfRows') + '=' + encodeURIComponent(100);
const output = '&' + encodeURIComponent('type') + '=' + encodeURIComponent('json');
return url + queryParams + pageNum + numOfItem + output;
}
//모든 page의 item을 list에 push해서 return하는 함수
const getItemsList = async(queryUrl) => {
let i = 1, getItem = null, items = null;
const result = [];
while(true) {
getItem = await axios.get(queryUrl(i));
items = getItem.data.body.items;
if(items === undefined)
return result;
result.push(...items);
console.log('\x1b[100mmedicine data getting processing... : page', i, 'done\x1b[0m');
i++;
}
}
//itemArray에 있는 모든 data를 MongoDB의 SMB collections에 저장함
const exportJsonData = (itemList) => {
itemList.forEach(async item => {
const medicineId = item.itemSeq;
const medicineInfo = {
name : item.itemName,
company : item.entpName,
target : await slicingInfo(item.efcyQesitm),
dosage : await slicingInfo(item.useMethodQesitm),
warn : await slicingInfo(item.atpnWarnQesitm ?
item.atpnWarnQesitm + '\n' + item.atpnQesitm
: item.atpnQesitm),
antiEffect : await slicingInfo(item.seQesitm)
};
Medicine.findOneAndUpdate({
medicineId
}, medicineInfo, {
upsert : true
}).exec();
})
}
//복용 정보에서 불필요한 태그를 제거하고 제거된 값을 반환한다.
const slicingInfo = async (info) => {
let result = info;
if(info) {
result = await info.split('<p>').join('')
.split('</p>').join('')
.split('<sup>').join('')
.split('</sup>').join('')
.split('null').join('');
}
return result;
}
\ No newline at end of file
const jwt = require("jsonwebtoken");
const User = require('../models/user');
const jwtMiddleware = async (ctx, next) => {
const token = ctx.cookies.get("access_token");
if(!token) {
return next();
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
ctx.state.user = {
_id : decoded._id,
userId : decoded.userId
};
const now = Math.floor(Date.now() / 1000);
if (decoded.exp - now < 60 * 60 * 24 * 7) {
const user = await User.findById(decoded._id);
const token = user.generateToken();
ctx.cookies.set('access_token', token, {
httpOnly : true,
maxAge : 1000 * 60 * 60 * 24 * 30
})
}
} catch(e) {
ctx.state.user = null;
}
return next();
};
module.exports = jwtMiddleware;
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const BottleSchema = new Schema ({
bottleId : { type : Number, required : true, unique : true },
temperature : { type : Number, default : 0 },
humidity : { type : Number, default : 0 },
balance : { type : Number, default : 0 },
recentOpen : { type : Date, default : Date.now },
medicineId : { type : Number, default : null, },
hubId : Number,
dosage : { type : Number, default : 0 }
})
BottleSchema.statics.findByBottleId = function(bottleId) {
return this.findOne({ bottleId });
};
BottleSchema.methods.getBottleId = function() {
return this.bottleId;
};
BottleSchema.methods.getRecentOpenDate = function() {
return this.recentOpen;
};
BottleSchema.methods.getTemperature = function() {
return this.temperature;
};
BottleSchema.methods.getHumidity = function() {
return this.humidity;
};
BottleSchema.methods.getBalance = function() {
return this.balance;
};
BottleSchema.methods.getDosage = function() {
return this.dosage;
};
BottleSchema.methods.getMedicineId = function() {
return this.medicineId;
};
BottleSchema.methods.getHubId = function() {
return this.hubId;
};
module.exports = mongoose.model('Bottle', BottleSchema);
\ No newline at end of file
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const HubSchema = new Schema ({
hubId : { type : Number, required : true, unique : true },
hosting : { type : Object, default : null },
userId : { type : String, default : null },
});
HubSchema.statics.findByHubId = function(hubId) {
return this.findOne({ hubId })
};
HubSchema.methods.setHubHost = function(hosting) {
this.hosting = hosting;
};
HubSchema.methods.getHubHost = function() {
return this.hosting;
};
HubSchema.methods.setHub_UserId = function(userId) {
this.userId = userId;
};
HubSchema.methods.getHub_UserId = function() {
return this.userId;
};
module.exports = mongoose.model('Hub', HubSchema);
\ No newline at end of file
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const MedicineSchema = new Schema ({
medicineId : { type : Number, required : true, unique : true },
name : { type : String, required : true },
company : String,
target : { type : String, required : true },
dosage : { type : String, required : true },
warn : { type : String, required : true },
antiEffect : { type : String, required : true }
})
MedicineSchema.statics.findByName = async function(name) {
const all = await this.find().exec();
const result = all.filter(item => {
return item.name.includes(name)
});
return result;
};
MedicineSchema.statics.findByCompany = async function(company) {
const all = await this.find().exec();
const result = all.filter(item => {
return item.company.includes(company)
});
return result;
};
MedicineSchema.statics.findByTarget = async function(target) {
const all = await this.find().exec();
const result = all.filter(item => {
return item.target.includes(target)
});
return result;
};
MedicineSchema.statics.findByMedicineId = function(medicineId) {
return this.findOne({ medicineId })
};
module.exports = mongoose.model('Medicine', MedicineSchema);
\ No newline at end of file
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
userId : { type: String, require : true, unique : true, lowercase : true },
hashedPassword : { type : String, default : null }
});
UserSchema.methods.setPassword = async function(password) {
const hash = await bcrypt.hash(password, 10);
this.hashedPassword = hash;
};
UserSchema.methods.checkPassword = async function(password) {
const result = await bcrypt.compare(password, this.hashedPassword)
return result;
};
UserSchema.statics.findByUserId = async function(userId) {
return this.findOne({ userId });
};
UserSchema.methods.generateToken = function() {
const token = jwt.sign (
{
_id : this._id,
userId : this.userId
},
process.env.JWT_SECRET,
{ expiresIn : '30d' }
);
return token;
};
module.exports = mongoose.model("User", UserSchema);
\ No newline at end of file
const Mqtt = require('../lib/MqttModule');
const DataProcess = require('../lib/DataProcess');
const Hub = require('../models/hub');
const Bottle = require('../models/bottle');
exports.on = async() => {
await subscribeOn();
console.log('\x1b[1;34mMQTT Server On\x1b[0m');
};
const subscribeOn = async () => {
const bottleList = await Bottle.find();
bottleList.forEach(async(bottle) => {
const topic = 'bottle/' + bottle.getBottleId() + '/bts';
const hub = await Hub.findByHubId(bottle.getHubId());
const client = await Mqtt.mqttOn(hub.getHubHost(), DataProcess.dataPublish);
Mqtt.mqttSubscribe(client, topic);
})
};
\ No newline at end of file