박권수

Merge branch 'server' into web

...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
12 "@google-cloud/storage": "^5.14.2", 12 "@google-cloud/storage": "^5.14.2",
13 "@koa/cors": "^3.1.0", 13 "@koa/cors": "^3.1.0",
14 "firebase-admin": "^9.11.1", 14 "firebase-admin": "^9.11.1",
15 + "google-auth-library": "^7.10.0",
15 "koa-body": "^4.2.0", 16 "koa-body": "^4.2.0",
16 "moment": "^2.29.1", 17 "moment": "^2.29.1",
17 "moment-timezone": "^0.5.33", 18 "moment-timezone": "^0.5.33",
...@@ -1835,9 +1836,9 @@ ...@@ -1835,9 +1836,9 @@
1835 } 1836 }
1836 }, 1837 },
1837 "node_modules/google-auth-library": { 1838 "node_modules/google-auth-library": {
1838 - "version": "7.9.2", 1839 + "version": "7.10.0",
1839 - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.9.2.tgz", 1840 + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.10.0.tgz",
1840 - "integrity": "sha512-HjxbJt660a+YUTYAgYor87JCuBZvjUSNBExk4bXTEaMuCn8IHSDeHmFxKqThuDPrLCiKJp8blk/Ze8f7SI4N6g==", 1841 + "integrity": "sha512-ICsqaU+lxMHVlDUzMrfVIEqnARw2AwBiZ/2KnNM6BcTf9Nott+Af87DTIzmlnW865p3REUP2MVL0xkPC3a61aQ==",
1841 "dependencies": { 1842 "dependencies": {
1842 "arrify": "^2.0.0", 1843 "arrify": "^2.0.0",
1843 "base64-js": "^1.3.0", 1844 "base64-js": "^1.3.0",
...@@ -5277,9 +5278,9 @@ ...@@ -5277,9 +5278,9 @@
5277 } 5278 }
5278 }, 5279 },
5279 "google-auth-library": { 5280 "google-auth-library": {
5280 - "version": "7.9.2", 5281 + "version": "7.10.0",
5281 - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.9.2.tgz", 5282 + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.10.0.tgz",
5282 - "integrity": "sha512-HjxbJt660a+YUTYAgYor87JCuBZvjUSNBExk4bXTEaMuCn8IHSDeHmFxKqThuDPrLCiKJp8blk/Ze8f7SI4N6g==", 5283 + "integrity": "sha512-ICsqaU+lxMHVlDUzMrfVIEqnARw2AwBiZ/2KnNM6BcTf9Nott+Af87DTIzmlnW865p3REUP2MVL0xkPC3a61aQ==",
5283 "requires": { 5284 "requires": {
5284 "arrify": "^2.0.0", 5285 "arrify": "^2.0.0",
5285 "base64-js": "^1.3.0", 5286 "base64-js": "^1.3.0",
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
20 "@google-cloud/storage": "^5.14.2", 20 "@google-cloud/storage": "^5.14.2",
21 "@koa/cors": "^3.1.0", 21 "@koa/cors": "^3.1.0",
22 "firebase-admin": "^9.11.1", 22 "firebase-admin": "^9.11.1",
23 + "google-auth-library": "^7.10.0",
23 "koa-body": "^4.2.0", 24 "koa-body": "^4.2.0",
24 "moment": "^2.29.1", 25 "moment": "^2.29.1",
25 "moment-timezone": "^0.5.33", 26 "moment-timezone": "^0.5.33",
......
...@@ -93,6 +93,7 @@ exports.searchHospital = async ctx => { ...@@ -93,6 +93,7 @@ exports.searchHospital = async ctx => {
93 }; 93 };
94 }; 94 };
95 95
96 +//의사 회원가입
96 exports.doctorRegister = async ctx => { 97 exports.doctorRegister = async ctx => {
97 const { 98 const {
98 userId, 99 userId,
...@@ -193,6 +194,7 @@ exports.doctorRegister = async ctx => { ...@@ -193,6 +194,7 @@ exports.doctorRegister = async ctx => {
193 194
194 } 195 }
195 196
197 +//로컬 로그인
196 exports.login = async(ctx) => { 198 exports.login = async(ctx) => {
197 const { userId, password, deviceToken } = ctx.request.body; 199 const { userId, password, deviceToken } = ctx.request.body;
198 200
...@@ -211,7 +213,7 @@ exports.login = async(ctx) => { ...@@ -211,7 +213,7 @@ exports.login = async(ctx) => {
211 } 213 }
212 214
213 const user = await User.findByUserId(userId); 215 const user = await User.findByUserId(userId);
214 - if(!user || !user.userTypeCd) { 216 + if(!user || !user.userTypeCd || user.authTypeCd !== 'NORMAL') {
215 ctx.status = 401; 217 ctx.status = 401;
216 ctx.body = { 218 ctx.body = {
217 error : '존재하지 않는 회원입니다.', 219 error : '존재하지 않는 회원입니다.',
...@@ -259,6 +261,180 @@ exports.login = async(ctx) => { ...@@ -259,6 +261,180 @@ exports.login = async(ctx) => {
259 261
260 }; 262 };
261 263
264 +//social Register
265 +exports.socialRegister = async ctx => {
266 + const { socialType } = ctx.params;
267 + const { accessToken, deviceToken } = ctx.request.body;
268 +
269 + const verifyingToken =
270 + socialType.toUpperCase() === 'GOOGLE' ? async () => {
271 + //id_token
272 + const result = jwt.decode(accessToken);
273 +
274 + return {
275 + userId : result.email,
276 + userNm : result.name,
277 + contact : null,
278 + birth : null,
279 + };
280 + }
281 + : socialType.toUpperCase() === 'NAVER' ? async () => {
282 + const url = 'https://openapi.naver.com/v1/nid/me';
283 + const result = await axios.get(url, {
284 + headers : {
285 + Authorization : `Bearer ${accessToken}`,
286 + },
287 + });
288 +
289 + const { email, mobile, name, birthday, birthyear } = result.data.response;
290 +
291 + return {
292 + userId : email,
293 + userNm : name,
294 + contact : mobile,
295 + birth : `${birthyear}-${birthday}`,
296 + };
297 + }
298 + : socialType.toUpperCase() === 'KAKAO' ? async () => {
299 + const url = 'https://kapi.kakao.com/v2/user/me';
300 + const result = await axios.get(url, {
301 + headers : {
302 + Authorization : `Bearer ${accessToken}`,
303 + },
304 + });
305 +
306 + console.log(result);
307 +
308 + return result;
309 + } : () => null;
310 +
311 +
312 + const verifyingInfo = await verifyingToken();
313 + if(!verifyingInfo || !verifyingInfo.userId) {
314 + ctx.status = 403;
315 + ctx.body = {
316 + error : '잘못된 요청',
317 + };
318 +
319 + return;
320 + }
321 +
322 + const { userId, userNm, birth, contact } = verifyingInfo;
323 +
324 + const existUser = await User.findByUserId(userId);
325 + if(existUser) {
326 + ctx.status = 409;
327 + ctx.body = {
328 + error : '이미 가입된 회원',
329 + };
330 +
331 + return;
332 + }
333 +
334 + const user = new User({
335 + userId,
336 + hashedPassword : null,
337 + authTypeCd : socialType.toUpperCase(),
338 + useYn : 'Y',
339 + });
340 +
341 + const profile = new Profile({
342 + userId,
343 + userNm,
344 + birth,
345 + contact,
346 + deviceToken,
347 + });
348 +
349 + await user.save();
350 + await profile.save();
351 +
352 + ctx.status = 201;
353 +
354 +};
355 +
356 +//social Login
357 +exports.socialLogin = async ctx => {
358 + const { socialType } = ctx.params;
359 + const { accessToken, deviceToken, } = ctx.request.body;
360 +
361 + const verifyingToken =
362 + socialType.toUpperCase() === 'GOOGLE' ? async () => {
363 + //id_token : google Login
364 + const result = jwt.decode(accessToken);
365 +
366 + return result.email;
367 + }
368 + : socialType.toUpperCase() === 'NAVER' ? async () => {
369 + //naver Login
370 + const url = 'https://openapi.naver.com/v1/nid/me';
371 + const result = await axios.get(url, {
372 + headers : {
373 + Authorization : `Bearer ${accessToken}`,
374 + },
375 + });
376 +
377 + return result.data.response.email;
378 + }
379 + : socialType.toUpperCase() === 'KAKAO' ? async () => {
380 + //kakao Login
381 + const url = 'https://kapi.kakao.com/v2/user/me';
382 + const result = await axios.get(url, {
383 + headers : {
384 + Authorization : `Bearer ${accessToken}`,
385 + },
386 + });
387 +
388 + console.log(result);
389 +
390 + return result;
391 + } : () => null;
392 +
393 +
394 + const userId = await verifyingToken();
395 + if(!userId) {
396 + ctx.status = 403;
397 + ctx.body = {
398 + error : '잘못된 요청입니다',
399 + };
400 +
401 + return;
402 + }
403 +
404 + const user = await User.findByUserId(userId);
405 + if(!user || user.useYn !== 'Y') {
406 + ctx.status = 404;
407 + ctx.body = {
408 + error : '존재하지 않는 회원입니다.',
409 + };
410 +
411 + return;
412 + } else if (user.authTypeCd !== socialType.toUpperCase()) {
413 + ctx.status = 400;
414 + ctx.body = {
415 + error : '잘못된 소셜 로그인입니다.',
416 + };
417 +
418 + return;
419 + }
420 +
421 + const profile = await Profile.findOne({ userId });
422 + if(profile.deviceToken !== deviceToken) {
423 + profile.updateDeviceToken(deviceToken);
424 + await profile.save();
425 + }
426 +
427 +
428 + const token = await user.generateToken();
429 +
430 + ctx.status = 200;
431 + ctx.body = {
432 + userTypeCd : user.userTypeCd,
433 + token,
434 + };
435 +
436 +};
437 +
262 exports.logout = async(ctx) => { 438 exports.logout = async(ctx) => {
263 ctx.cookies.set('access_token', null, { 439 ctx.cookies.set('access_token', null, {
264 httpOnly : true, 440 httpOnly : true,
......
...@@ -5,7 +5,7 @@ const authCtrl = require('./auth.ctrl') ...@@ -5,7 +5,7 @@ const authCtrl = require('./auth.ctrl')
5 const auth = new Router() 5 const auth = new Router()
6 6
7 /** 7 /**
8 - * 회원가입 (email type) : 환자 회원가입 8 + * 로컬 회원가입 (email type) : 환자 회원가입
9 * url : http://localhost:4000/api/auth/register 9 * url : http://localhost:4000/api/auth/register
10 * request parameter : userId, password, passwordCheck 10 * request parameter : userId, password, passwordCheck
11 * return : null 11 * return : null
...@@ -21,7 +21,7 @@ auth.post('/register', authCtrl.register) ...@@ -21,7 +21,7 @@ auth.post('/register', authCtrl.register)
21 auth.get('/hospital', authCtrl.searchHospital); 21 auth.get('/hospital', authCtrl.searchHospital);
22 22
23 /** 23 /**
24 - * 회원가입 (email type) : 의사 회원가입 24 + * 로컬 회원가입 (email type) : 의사 회원가입
25 * url : http://localhost:4000/api/auth/register/doctor 25 * url : http://localhost:4000/api/auth/register/doctor
26 * request parameter : userId, password, passwordCheck, doctorInfo(File) 26 * request parameter : userId, password, passwordCheck, doctorInfo(File)
27 * return : null 27 * return : null
...@@ -29,14 +29,30 @@ auth.get('/hospital', authCtrl.searchHospital); ...@@ -29,14 +29,30 @@ auth.get('/hospital', authCtrl.searchHospital);
29 auth.post('/register/doctor', KoaBody, authCtrl.doctorRegister) 29 auth.post('/register/doctor', KoaBody, authCtrl.doctorRegister)
30 30
31 /** 31 /**
32 - * 로그인 (email type) 32 + * 로컬 로그인 (email type)
33 * url : http://localhost:4000/api/auth/login 33 * url : http://localhost:4000/api/auth/login
34 * request parameter : userId, password 34 * request parameter : userId, password
35 - * return : userId 35 + * return : token, userTypeCd
36 */ 36 */
37 auth.post('/login', authCtrl.login) 37 auth.post('/login', authCtrl.login)
38 38
39 /** 39 /**
40 + * 소셜 회원가입(Google, Naver, Kakao)
41 + * url : http://localhost:4000/api/auth/register/${socialType}
42 + * request parameter : accessToken
43 + * return : status
44 + */
45 +auth.post('/register/social/:socialType', authCtrl.socialRegister);
46 +
47 +/**
48 + * 소셜 로그인(Google, Naver, Kakao)
49 + * url : http://localhost:4000/api/auth/login/${socialType}
50 + * request parameter
51 + * return : token, userTypeCd
52 + */
53 +auth.post('/login/social/:socialType', authCtrl.socialLogin);
54 +
55 +/**
40 * 로그아웃 56 * 로그아웃
41 * url : http://localhost:4000/api/auth/logout 57 * url : http://localhost:4000/api/auth/logout
42 * request parameter : null 58 * request parameter : null
......
...@@ -15,6 +15,7 @@ const jwt = require('jsonwebtoken'); ...@@ -15,6 +15,7 @@ const jwt = require('jsonwebtoken');
15 15
16 const { uploadQrCode, viewQrCode } = require('../../util/GoogleCloudStorage'); 16 const { uploadQrCode, viewQrCode } = require('../../util/GoogleCloudStorage');
17 const QrCodeUtil = require('../../util/QrCodeUtil'); 17 const QrCodeUtil = require('../../util/QrCodeUtil');
18 +const { sendPushMessage } = require('../../util/FCM');
18 19
19 20
20 /** 21 /**
...@@ -341,7 +342,19 @@ exports.writeReqBottleFeedback = async ctx => { ...@@ -341,7 +342,19 @@ exports.writeReqBottleFeedback = async ctx => {
341 doctorId : userId, 342 doctorId : userId,
342 feedback, 343 feedback,
343 }); 344 });
344 - newFeedback.save(); 345 + await newFeedback.save();
346 +
347 +
348 + //feedback 알람 보내기
349 + const hub = await Hub.findOne({ hubId : bottle.hubId });
350 + const patientProfile = await Profile.findOne({ userId : hub.userId });
351 + if(patientProfile) {
352 + sendPushMessage({
353 + deviceToken : patientProfile.deviceToken,
354 + title : '의사에게 새로운 알람이 도착했습니다.',
355 + body : feedback,
356 + });
357 + }
345 358
346 ctx.status = 200; 359 ctx.status = 200;
347 360
......
...@@ -8,6 +8,7 @@ const jwtMiddleware = async (ctx, next) => { ...@@ -8,6 +8,7 @@ const jwtMiddleware = async (ctx, next) => {
8 } 8 }
9 9
10 try { 10 try {
11 + // eslint-disable-next-line no-undef
11 const decoded = jwt.verify(token, process.env.JWT_SECRET); 12 const decoded = jwt.verify(token, process.env.JWT_SECRET);
12 ctx.state.user = { 13 ctx.state.user = {
13 _id : decoded._id, 14 _id : decoded._id,
......
...@@ -5,9 +5,10 @@ const jwt = require('jsonwebtoken'); ...@@ -5,9 +5,10 @@ const jwt = require('jsonwebtoken');
5 const Schema = mongoose.Schema; 5 const Schema = mongoose.Schema;
6 6
7 const UserSchema = new Schema ({ 7 const UserSchema = new Schema ({
8 - userId : { type: String, required : true, unique : true, lowercase : true, }, 8 + userId : { type: String, required : true, unique : true, lowercase : true, trim : true },
9 - hashedPassword : { type : String, required : true }, 9 + hashedPassword : { type : String, required : true, },
10 - userTypeCd : { type : String, required : true, default : 'NORMAL' }, 10 + userTypeCd : { type : String, required : true, default : 'NORMAL', uppercase : true, },
11 + authTypeCd : { type : String, required : true, default : 'NORMAL', uppercase : true, },
11 useYn : { type : String, default : 'W', required : true, }, 12 useYn : { type : String, default : 'W', required : true, },
12 }); 13 });
13 14
......
...@@ -20,20 +20,6 @@ const updateMedicineInfo = require('../lib/UpdatingMedicineInfo'); ...@@ -20,20 +20,6 @@ const updateMedicineInfo = require('../lib/UpdatingMedicineInfo');
20 const { sendPushMessage } = require('./FCM'); 20 const { sendPushMessage } = require('./FCM');
21 21
22 22
23 -// //매년 1월 1일 00시 00분에 1살씩 추가
24 -// exports.CheckNewYear = () => {
25 -// cron.schedule('0 0 0 1 1 *', async () => {
26 -// const profileList = await Profile.find();
27 -// profileList.forEach(async profile => {
28 -// await profile.updateUserAge();
29 -// profile.save();
30 -// });
31 -
32 -// }, {
33 -// timezone : 'Asia/Tokyo',
34 -// });
35 -// };
36 -
37 //매월 1일 0시 0분에 약 정보 업데이트 23 //매월 1일 0시 0분에 약 정보 업데이트
38 exports.updateMedicineData = async () => { 24 exports.updateMedicineData = async () => {
39 cron.schedule('0 0 0 1 * *', () => { 25 cron.schedule('0 0 0 1 * *', () => {
...@@ -76,7 +62,8 @@ exports.pushNotifyByDosage = async() => { ...@@ -76,7 +62,8 @@ exports.pushNotifyByDosage = async() => {
76 const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId }); 62 const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId });
77 pushNotify({ 63 pushNotify({
78 deviceToken, 64 deviceToken,
79 - message : medicine.name + '을 복용하셔야 합니다.', 65 + title : '약 복용 시간입니다',
66 + body : medicine.name + '을 복용하셔야 합니다.',
80 }); 67 });
81 } 68 }
82 } 69 }
...@@ -102,7 +89,8 @@ exports.pushNotifyByDosage = async() => { ...@@ -102,7 +89,8 @@ exports.pushNotifyByDosage = async() => {
102 const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId }); 89 const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId });
103 pushNotify({ 90 pushNotify({
104 deviceToken, 91 deviceToken,
105 - message : medicine.name + '을 복용하셔야 합니다.', 92 + title : '약 복용 시간입니다',
93 + body : medicine.name + '을 복용하셔야 합니다.',
106 }); 94 });
107 } 95 }
108 } 96 }
...@@ -128,7 +116,8 @@ exports.pushNotifyByDosage = async() => { ...@@ -128,7 +116,8 @@ exports.pushNotifyByDosage = async() => {
128 const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId }); 116 const medicine = await Medicine.findOne({ medicineId : bottleMedicine.medicineId });
129 pushNotify({ 117 pushNotify({
130 deviceToken, 118 deviceToken,
131 - message : medicine.name + '을 복용하셔야 합니다.', 119 + title : '약 복용 시간입니다',
120 + body : medicine.name + '을 복용하셔야 합니다.',
132 }); 121 });
133 } 122 }
134 } 123 }
...@@ -139,10 +128,11 @@ exports.pushNotifyByDosage = async() => { ...@@ -139,10 +128,11 @@ exports.pushNotifyByDosage = async() => {
139 128
140 }; 129 };
141 130
142 -const pushNotify = ({ deviceToken, message }) => { 131 +const pushNotify = ({ deviceToken, title, body }) => {
143 //toDo : deviceToken을 받아서 push notification을 발송하는 함수 132 //toDo : deviceToken을 받아서 push notification을 발송하는 함수
144 sendPushMessage({ 133 sendPushMessage({
145 deviceToken, 134 deviceToken,
146 - message, 135 + title,
136 + body,
147 }); 137 });
148 }; 138 };
......
1 const BottleMedicine = require('../models/bottleMedicine'); 1 const BottleMedicine = require('../models/bottleMedicine');
2 const TakeMedicineHist = require('../models/takeMedicineHistory'); 2 const TakeMedicineHist = require('../models/takeMedicineHistory');
3 3
4 +
4 //message subscribe 후 message를 가공한 이후 해당 데이터를 보낼 topic과 message를 리턴하는 함수 5 //message subscribe 후 message를 가공한 이후 해당 데이터를 보낼 topic과 message를 리턴하는 함수
5 exports.dataPublish = async (topic, message) => { 6 exports.dataPublish = async (topic, message) => {
6 //client가 subscribe를 하면 메시지를 보낸 약병의 topic과 message를 가공 및 보낸 약병의 bottleId를 가져옴 7 //client가 subscribe를 하면 메시지를 보낸 약병의 topic과 message를 가공 및 보낸 약병의 bottleId를 가져옴
......
...@@ -7,11 +7,11 @@ exports.initializeFCM = () => { ...@@ -7,11 +7,11 @@ exports.initializeFCM = () => {
7 }); 7 });
8 }; 8 };
9 9
10 -exports.sendPushMessage = async ({ deviceToken, message }) => { 10 +exports.sendPushMessage = async ({ deviceToken, title, body }) => {
11 const notifyMessage = { 11 const notifyMessage = {
12 notification : { 12 notification : {
13 - title : '약 먹을 시간입니다', 13 + title,
14 - body : message, 14 + body,
15 }, 15 },
16 token : deviceToken, 16 token : deviceToken,
17 }; 17 };
......
...@@ -2133,10 +2133,17 @@ ...@@ -2133,10 +2133,17 @@
2133 "string_decoder" "~1.1.1" 2133 "string_decoder" "~1.1.1"
2134 "util-deprecate" "~1.0.1" 2134 "util-deprecate" "~1.0.1"
2135 2135
2136 +<<<<<<< HEAD
2136 "readable-stream@~2.3.6": 2137 "readable-stream@~2.3.6":
2137 "integrity" "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==" 2138 "integrity" "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw=="
2138 "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" 2139 "resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz"
2139 "version" "2.3.7" 2140 "version" "2.3.7"
2141 +=======
2142 +"google-auth-library@^7.0.0", "google-auth-library@^7.0.2", "google-auth-library@^7.10.0", "google-auth-library@^7.6.1":
2143 + "integrity" "sha512-ICsqaU+lxMHVlDUzMrfVIEqnARw2AwBiZ/2KnNM6BcTf9Nott+Af87DTIzmlnW865p3REUP2MVL0xkPC3a61aQ=="
2144 + "resolved" "https://registry.npmjs.org/google-auth-library/-/google-auth-library-7.10.0.tgz"
2145 + "version" "7.10.0"
2146 +>>>>>>> server
2140 dependencies: 2147 dependencies:
2141 "core-util-is" "~1.0.0" 2148 "core-util-is" "~1.0.0"
2142 "inherits" "~2.0.3" 2149 "inherits" "~2.0.3"
......