| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  | import _ from 'lodash'; | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  | import loopback from 'loopback'; | 
					
						
							|  |  |  | import path from 'path'; | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  | import dedent from 'dedent'; | 
					
						
							|  |  |  | import { Observable } from 'rx'; | 
					
						
							| 
									
										
										
										
											2016-02-09 20:54:49 -08:00
										 |  |  | import debug from 'debug'; | 
					
						
							| 
									
										
										
										
											2017-08-26 00:27:05 +02:00
										 |  |  | import { isEmail } from 'validator'; | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import { | 
					
						
							|  |  |  |   ifNoUser401, | 
					
						
							|  |  |  |   ifNoUserSend | 
					
						
							|  |  |  | } from '../utils/middleware'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-09 20:54:49 -08:00
										 |  |  | import { observeQuery } from '../utils/rx'; | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-05 19:58:44 -07:00
										 |  |  | import { | 
					
						
							| 
									
										
										
										
											2017-12-21 01:15:23 +00:00
										 |  |  |   respWebDesignId, | 
					
						
							|  |  |  |   frontEndLibsId, | 
					
						
							|  |  |  |   jsAlgoDataStructId, | 
					
						
							| 
									
										
										
										
											2015-12-09 14:34:33 -08:00
										 |  |  |   frontEndChallengeId, | 
					
						
							| 
									
										
										
										
											2017-12-21 01:15:23 +00:00
										 |  |  |   dataVisId, | 
					
						
							|  |  |  |   apisMicroservicesId, | 
					
						
							|  |  |  |   backEndChallengeId, | 
					
						
							|  |  |  |   infosecQaId | 
					
						
							| 
									
										
										
										
											2015-10-05 19:58:44 -07:00
										 |  |  | } from '../utils/constantStrings.json'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-06 21:08:24 -07:00
										 |  |  | import { | 
					
						
							|  |  |  |   completeCommitment$ | 
					
						
							|  |  |  | } from '../utils/commit'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:58:37 -08:00
										 |  |  | import certTypes from '../utils/certTypes.json'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 11:34:44 -08:00
										 |  |  | const log = debug('fcc:certification'); | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  | const renderCertifedEmail = loopback.template(path.join( | 
					
						
							|  |  |  |   __dirname, | 
					
						
							|  |  |  |   '..', | 
					
						
							|  |  |  |   'views', | 
					
						
							|  |  |  |   'emails', | 
					
						
							|  |  |  |   'certified.ejs' | 
					
						
							|  |  |  | )); | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  | const sendMessageToNonUser = ifNoUserSend( | 
					
						
							|  |  |  |   'must be logged in to complete.' | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-09 20:54:49 -08:00
										 |  |  | function isCertified(ids, challengeMap = {}) { | 
					
						
							|  |  |  |   return _.every(ids, ({ id }) => challengeMap[id]); | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:58:37 -08:00
										 |  |  | function getIdsForCert$(id, Challenge) { | 
					
						
							|  |  |  |   return observeQuery( | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |     Challenge, | 
					
						
							|  |  |  |     'findById', | 
					
						
							| 
									
										
										
										
											2016-01-11 15:58:37 -08:00
										 |  |  |     id, | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2015-10-05 19:58:44 -07:00
										 |  |  |       id: true, | 
					
						
							|  |  |  |       tests: true, | 
					
						
							|  |  |  |       name: true, | 
					
						
							|  |  |  |       challengeType: true | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  |     .shareReplay(); | 
					
						
							| 
									
										
										
										
											2016-01-11 15:58:37 -08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  | // sendCertifiedEmail(
 | 
					
						
							|  |  |  | //   {
 | 
					
						
							|  |  |  | //     email: String,
 | 
					
						
							|  |  |  | //     username: String,
 | 
					
						
							| 
									
										
										
										
											2017-12-21 01:15:23 +00:00
										 |  |  | //     isRespWebDesignCert: Boolean,
 | 
					
						
							|  |  |  | //     isFrontEndLibsCert: Boolean,
 | 
					
						
							|  |  |  | //     isJsAlgoDataStructCert: Boolean,
 | 
					
						
							|  |  |  | //     isDataVisCert: Boolean,
 | 
					
						
							|  |  |  | //     isApisMicroservicesCert: Boolean,
 | 
					
						
							|  |  |  | //     isInfosecQaCert: Boolean
 | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  | //   },
 | 
					
						
							|  |  |  | //   send$: Observable
 | 
					
						
							|  |  |  | // ) => Observable
 | 
					
						
							|  |  |  | function sendCertifiedEmail( | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     email, | 
					
						
							|  |  |  |     name, | 
					
						
							|  |  |  |     username, | 
					
						
							| 
									
										
										
										
											2017-12-21 01:15:23 +00:00
										 |  |  |     isRespWebDesignCert, | 
					
						
							|  |  |  |     isFrontEndLibsCert, | 
					
						
							|  |  |  |     isJsAlgoDataStructCert, | 
					
						
							|  |  |  |     isDataVisCert, | 
					
						
							|  |  |  |     isApisMicroservicesCert, | 
					
						
							|  |  |  |     isInfosecQaCert | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  |   }, | 
					
						
							|  |  |  |   send$ | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   if ( | 
					
						
							| 
									
										
										
										
											2017-08-26 00:27:05 +02:00
										 |  |  |     !isEmail(email) || | 
					
						
							| 
									
										
										
										
											2017-12-21 01:15:23 +00:00
										 |  |  |     !isRespWebDesignCert || | 
					
						
							|  |  |  |     !isFrontEndLibsCert || | 
					
						
							|  |  |  |     !isJsAlgoDataStructCert || | 
					
						
							|  |  |  |     !isDataVisCert || | 
					
						
							|  |  |  |     !isApisMicroservicesCert || | 
					
						
							|  |  |  |     !isInfosecQaCert | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  |   ) { | 
					
						
							|  |  |  |     return Observable.just(false); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   const notifyUser = { | 
					
						
							|  |  |  |     type: 'email', | 
					
						
							|  |  |  |     to: email, | 
					
						
							| 
									
										
										
										
											2017-09-08 10:52:40 -07:00
										 |  |  |     from: 'team@freeCodeCamp.org', | 
					
						
							|  |  |  |     subject: dedent`
 | 
					
						
							|  |  |  |       Congratulations on completing all of the | 
					
						
							|  |  |  |       freeCodeCamp certificates! | 
					
						
							|  |  |  |     `,
 | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  |     text: renderCertifedEmail({ | 
					
						
							|  |  |  |       username, | 
					
						
							|  |  |  |       name | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2017-08-26 00:27:05 +02:00
										 |  |  |   return send$(notifyUser).map(() => true); | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:58:37 -08:00
										 |  |  | export default function certificate(app) { | 
					
						
							|  |  |  |   const router = app.loopback.Router(); | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  |   const { Email, Challenge } = app.models; | 
					
						
							| 
									
										
										
										
											2016-01-11 15:58:37 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const certTypeIds = { | 
					
						
							|  |  |  |     [certTypes.frontEnd]: getIdsForCert$(frontEndChallengeId, Challenge), | 
					
						
							| 
									
										
										
										
											2017-12-21 01:15:23 +00:00
										 |  |  |     [certTypes.backEnd]: getIdsForCert$(backEndChallengeId, Challenge), | 
					
						
							|  |  |  |     [certTypes.respWebDesign]: getIdsForCert$(respWebDesignId, Challenge), | 
					
						
							|  |  |  |     [certTypes.frontEndLibs]: getIdsForCert$(frontEndLibsId, Challenge), | 
					
						
							|  |  |  |     [certTypes.jsAlgoDataStruct]: getIdsForCert$(jsAlgoDataStructId, Challenge), | 
					
						
							|  |  |  |     [certTypes.dataVis]: getIdsForCert$(dataVisId, Challenge), | 
					
						
							|  |  |  |     [certTypes.apisMicroservices]: getIdsForCert$( | 
					
						
							|  |  |  |       apisMicroservicesId, | 
					
						
							|  |  |  |       Challenge | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |     [certTypes.infosecQa]: getIdsForCert$(infosecQaId, Challenge) | 
					
						
							| 
									
										
										
										
											2016-01-11 15:58:37 -08:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   router.post( | 
					
						
							|  |  |  |     '/certificate/verify/front-end', | 
					
						
							|  |  |  |     ifNoUser401, | 
					
						
							| 
									
										
										
										
											2016-01-11 15:58:37 -08:00
										 |  |  |     verifyCert.bind(null, certTypes.frontEnd) | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   router.post( | 
					
						
							| 
									
										
										
										
											2015-12-09 14:34:33 -08:00
										 |  |  |     '/certificate/verify/back-end', | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |     ifNoUser401, | 
					
						
							| 
									
										
										
										
											2016-01-11 15:58:37 -08:00
										 |  |  |     verifyCert.bind(null, certTypes.backEnd) | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-21 01:15:23 +00:00
										 |  |  |   router.post( | 
					
						
							|  |  |  |     '/certificate/verify/responsive-web-design', | 
					
						
							|  |  |  |     ifNoUser401, | 
					
						
							|  |  |  |     verifyCert.bind(null, certTypes.respWebDesign) | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   router.post( | 
					
						
							|  |  |  |     '/certificate/verify/front-end-libraries', | 
					
						
							|  |  |  |     ifNoUser401, | 
					
						
							|  |  |  |     verifyCert.bind(null, certTypes.frontEndLibs) | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   router.post( | 
					
						
							|  |  |  |     '/certificate/verify/javascript-algorithms-data-structures', | 
					
						
							|  |  |  |     ifNoUser401, | 
					
						
							|  |  |  |     verifyCert.bind(null, certTypes.jsAlgoDataStruct) | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:58:37 -08:00
										 |  |  |   router.post( | 
					
						
							|  |  |  |     '/certificate/verify/data-visualization', | 
					
						
							|  |  |  |     ifNoUser401, | 
					
						
							|  |  |  |     verifyCert.bind(null, certTypes.dataVis) | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-21 01:15:23 +00:00
										 |  |  |   router.post( | 
					
						
							|  |  |  |     '/certificate/verify/apis-microservices', | 
					
						
							|  |  |  |     ifNoUser401, | 
					
						
							|  |  |  |     verifyCert.bind(null, certTypes.apisMicroservices) | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   router.post( | 
					
						
							|  |  |  |     '/certificate/verify/information-security-quality-assurance', | 
					
						
							|  |  |  |     ifNoUser401, | 
					
						
							|  |  |  |     verifyCert.bind(null, certTypes.infosecQa) | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |   router.post( | 
					
						
							|  |  |  |     '/certificate/honest', | 
					
						
							|  |  |  |     sendMessageToNonUser, | 
					
						
							|  |  |  |     postHonest | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   app.use(router); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 15:58:37 -08:00
										 |  |  |   function verifyCert(certType, req, res, next) { | 
					
						
							| 
									
										
										
										
											2016-02-09 20:54:49 -08:00
										 |  |  |     const { user } = req; | 
					
						
							| 
									
										
										
										
											2016-04-08 14:24:21 -07:00
										 |  |  |     return user.getChallengeMap$() | 
					
						
							| 
									
										
										
										
											2016-04-06 21:08:19 -07:00
										 |  |  |       .flatMap(() => certTypeIds[certType]) | 
					
						
							| 
									
										
										
										
											2015-10-05 19:58:44 -07:00
										 |  |  |       .flatMap(challenge => { | 
					
						
							|  |  |  |         const { | 
					
						
							|  |  |  |           id, | 
					
						
							|  |  |  |           tests, | 
					
						
							|  |  |  |           name, | 
					
						
							|  |  |  |           challengeType | 
					
						
							|  |  |  |         } = challenge; | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |         if ( | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  |           user[certType] || | 
					
						
							|  |  |  |           !isCertified(tests, user.challengeMap) | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |         ) { | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  |           return Observable.just(false); | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-05-03 00:45:34 -07:00
										 |  |  |         const updateData = { | 
					
						
							|  |  |  |           $set: { | 
					
						
							|  |  |  |             [`challengeMap.${id}`]: { | 
					
						
							|  |  |  |               id, | 
					
						
							|  |  |  |               name, | 
					
						
							|  |  |  |               completedDate: new Date(), | 
					
						
							|  |  |  |               challengeType | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             [certType]: true | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         // set here so sendCertifiedEmail works properly
 | 
					
						
							|  |  |  |         // not used otherwise
 | 
					
						
							|  |  |  |         user[certType] = true; | 
					
						
							|  |  |  |         user.challengeMap[id] = { completedDate: new Date() }; | 
					
						
							|  |  |  |         return Observable.combineLatest( | 
					
						
							|  |  |  |           // update user data
 | 
					
						
							|  |  |  |           user.update$(updateData), | 
					
						
							|  |  |  |           // If user has committed to nonprofit,
 | 
					
						
							|  |  |  |           // this will complete their pledge
 | 
					
						
							|  |  |  |           completeCommitment$(user), | 
					
						
							|  |  |  |           // sends notification email is user has all three certs
 | 
					
						
							|  |  |  |           // if not it noop
 | 
					
						
							|  |  |  |           sendCertifiedEmail(user, Email.send$), | 
					
						
							|  |  |  |           ({ count }, pledgeOrMessage) => ({ count, pledgeOrMessage }) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |           .map( | 
					
						
							|  |  |  |             ({ count, pledgeOrMessage }) => { | 
					
						
							|  |  |  |               if (typeof pledgeOrMessage === 'string') { | 
					
						
							|  |  |  |                 log(pledgeOrMessage); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |               log(`${count} documents updated`); | 
					
						
							|  |  |  |               return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |       }) | 
					
						
							|  |  |  |       .subscribe( | 
					
						
							| 
									
										
										
										
											2016-02-09 20:54:49 -08:00
										 |  |  |         (didCertify) => { | 
					
						
							|  |  |  |           if (didCertify) { | 
					
						
							| 
									
										
										
										
											2016-09-15 18:24:48 +02:00
										 |  |  |             // Check if they have a name set
 | 
					
						
							|  |  |  |             if (user.name === '') { | 
					
						
							|  |  |  |               return res.status(200).send( | 
					
						
							|  |  |  |                 dedent`
 | 
					
						
							| 
									
										
										
										
											2017-09-08 10:52:40 -07:00
										 |  |  |                   We need your name so we can put it on your certificate. | 
					
						
							| 
									
										
										
										
											2016-09-15 18:24:48 +02:00
										 |  |  |                   <a href="https://github.com/settings/profile">Add your | 
					
						
							|  |  |  |                   name to your GitHub account</a>, then go to your | 
					
						
							| 
									
										
										
										
											2017-08-26 00:07:44 +02:00
										 |  |  |                   <a href="https://www.freecodecamp.org/settings">settings | 
					
						
							| 
									
										
										
										
											2016-09-15 18:24:48 +02:00
										 |  |  |                   page</a> and click the "update my portfolio from GitHub" | 
					
						
							|  |  |  |                   button. Then we can issue your certificate. | 
					
						
							|  |  |  |                   `
 | 
					
						
							|  |  |  |                 ); | 
					
						
							|  |  |  |              } | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |             return res.status(200).send(true); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return res.status(200).send( | 
					
						
							|  |  |  |             dedent`
 | 
					
						
							| 
									
										
										
										
											2015-10-07 02:22:04 -07:00
										 |  |  |               Looks like you have not completed the neccessary steps. | 
					
						
							|  |  |  |               Please return to the challenge map. | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |             `
 | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         next | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function postHonest(req, res, next) { | 
					
						
							| 
									
										
										
										
											2016-02-09 20:54:49 -08:00
										 |  |  |     return req.user.update$({ $set: { isHonest: true } }).subscribe( | 
					
						
							|  |  |  |       () => res.status(200).send(true), | 
					
						
							|  |  |  |       next | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2015-10-02 11:47:36 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } |