瀏覽代碼

:construction: Continuing work on integrating email transac

juan_spike
tomit4 3 年之前
父節點
當前提交
934b15f194

+ 2
- 0
backend/lib/plugins/user.js 查看文件

13
 const UserLoginRoute = require('../routes/user/login')
13
 const UserLoginRoute = require('../routes/user/login')
14
 const UserSignupRoute = require('../routes/user/signup')
14
 const UserSignupRoute = require('../routes/user/signup')
15
 const UserEmailRoute = require('../routes/user/email.js')
15
 const UserEmailRoute = require('../routes/user/email.js')
16
+const UserVerifyEmailRoute = require('../routes/user/verifyemail.js')
16
 const UserPassword = require('../routes/user/authentication')
17
 const UserPassword = require('../routes/user/authentication')
17
 
18
 
18
 const UserService = require('../services/user')
19
 const UserService = require('../services/user')
51
         await server.route(UserProfileCreateRoute)
52
         await server.route(UserProfileCreateRoute)
52
         await server.route(UserProfilesListRoute)
53
         await server.route(UserProfilesListRoute)
53
         await server.route(UserEmailRoute)
54
         await server.route(UserEmailRoute)
55
+        await server.route(UserVerifyEmailRoute)
54
         await server.route(UserPassword)
56
         await server.route(UserPassword)
55
     },
57
     },
56
 }
58
 }

+ 0
- 2
backend/lib/routes/user/email.js 查看文件

23
         handler: async function (request, h) {
23
         handler: async function (request, h) {
24
             const { userService } = request.server.services()
24
             const { userService } = request.server.services()
25
             const userEmail = request.payload.email
25
             const userEmail = request.payload.email
26
-            const userPw = request.payload.password
27
-            console.log('this.answered :=>', request.payload)
28
             try {
26
             try {
29
                 const emailSent = userService.emailSent(userEmail)
27
                 const emailSent = userService.emailSent(userEmail)
30
                 return {
28
                 return {

+ 58
- 0
backend/lib/routes/user/verifyemail.js 查看文件

1
+'use strict'
2
+
3
+const Joi = require('joi')
4
+
5
+const pluginConfig = {
6
+    handlerType: 'email',
7
+    docs: {
8
+        get: {
9
+            description: 'verifies confirmation email',
10
+            notes: 'Verifies the email from the stored hash',
11
+        },
12
+    },
13
+}
14
+
15
+module.exports = {
16
+    method: 'GET',
17
+    path: '/verify/{hash}',
18
+    options: {
19
+        ...pluginConfig.docs.get,
20
+        tags: ['api'],
21
+        auth: false,
22
+        cors: true,
23
+        handler: async function (request, h) {
24
+            const { userService } = request.server.services()
25
+            const hashToMatch = await userService.hashedEmail
26
+            const hash = request.params.hash
27
+            const hashesMatch = hashToMatch === hash ? true : false
28
+            try {
29
+                if (hashesMatch) {
30
+                    return {
31
+                        ok: true,
32
+                        handler: pluginConfig.handlerType,
33
+                        data: { hashesMatch: hashesMatch },
34
+                    }
35
+                }
36
+            } catch (err) {
37
+                return {
38
+                    ok: false,
39
+                    handler: pluginConfig.handlerType,
40
+                    data: {
41
+                        error: err,
42
+                    },
43
+                }
44
+            }
45
+        },
46
+        validate: {
47
+            failAction: 'log',
48
+        },
49
+        response: {
50
+            schema: Joi.object({
51
+                ok: Joi.bool(),
52
+                handler: Joi.string(),
53
+                data: Joi.object(),
54
+            }).label('verify_email_res'),
55
+            failAction: 'log',
56
+        },
57
+    },
58
+}

+ 14
- 4
backend/lib/services/user.js 查看文件

1
 'use strict'
1
 'use strict'
2
 require('dotenv').config()
2
 require('dotenv').config()
3
+const crypto = require('crypto')
3
 const Util = require('util')
4
 const Util = require('util')
4
 const Jwt = require('@hapi/jwt')
5
 const Jwt = require('@hapi/jwt')
5
 const Schmervice = require('@hapipal/schmervice')
6
 const Schmervice = require('@hapipal/schmervice')
13
 
14
 
14
 const apiInstance = new SibApiV3Sdk.TransactionalEmailsApi()
15
 const apiInstance = new SibApiV3Sdk.TransactionalEmailsApi()
15
 
16
 
17
+const hashEmail = async email => {
18
+    try {
19
+        return crypto.createHmac('sha256', '').update(email).digest('hex')
20
+    } catch (err) {
21
+        console.error('ERROR :=>', err)
22
+        return undefined
23
+    }
24
+}
25
+
16
 const hasher = async (pwd, steak) => {
26
 const hasher = async (pwd, steak) => {
17
     const hash = await pwd.hash(steak)
27
     const hash = await pwd.hash(steak)
18
     const result = await pwd.verify(steak, hash)
28
     const result = await pwd.verify(steak, hash)
52
     constructor(...args) {
62
     constructor(...args) {
53
         super(...args)
63
         super(...args)
54
         const pwd = new SecurePassword()
64
         const pwd = new SecurePassword()
65
+        this.hashedEmail = ''
55
         this.pwd = {
66
         this.pwd = {
56
             hash: Util.promisify(pwd.hash.bind(pwd)),
67
             hash: Util.promisify(pwd.hash.bind(pwd)),
57
             verify: Util.promisify(pwd.verify.bind(pwd)),
68
             verify: Util.promisify(pwd.verify.bind(pwd)),
245
             ],
256
             ],
246
             templateId: 1,
257
             templateId: 1,
247
             params: {
258
             params: {
248
-                // TODO: create basic hashing email above and cache hash...
249
-                email: 'tobehashedemail',
259
+                // TODO: Change this in production...
260
+                link: `localhost:3000/verify/${await hashEmail(userEmail)}`,
250
             },
261
             },
251
         }
262
         }
252
 
263
 
253
         await apiInstance.sendTransacEmail(sendSmtpEmail).then(
264
         await apiInstance.sendTransacEmail(sendSmtpEmail).then(
254
             data => {
265
             data => {
255
-                console.log('data from sendTransacEmail :=>', data)
256
                 return {
266
                 return {
257
                     wasSuccessfull: true,
267
                     wasSuccessfull: true,
258
                     data: data,
268
                     data: data,
259
                 }
269
                 }
260
             },
270
             },
261
             error => {
271
             error => {
262
-                console.error('ERROR :=>', error)
263
                 return {
272
                 return {
264
                     wasSuccessfull: false,
273
                     wasSuccessfull: false,
265
                     error: error,
274
                     error: error,
266
                 }
275
                 }
267
             },
276
             },
268
         )
277
         )
278
+        this.hashedEmail = hashEmail(userEmail)
269
     }
279
     }
270
 }
280
 }

+ 4
- 1
frontend/src/components/onboarding/Auth.vue 查看文件

9
 
9
 
10
 <script>
10
 <script>
11
 import { Authenticator } from '../../services/auth.service.js'
11
 import { Authenticator } from '../../services/auth.service.js'
12
+import { signupUser } from '../../services/user.service.js'
12
 
13
 
13
 export default {
14
 export default {
14
     name: 'Auth',
15
     name: 'Auth',
25
     },
26
     },
26
     emits: ['update-answers'],
27
     emits: ['update-answers'],
27
     data: () => ({
28
     data: () => ({
28
-        authenticator: false,
29
+        authenticator: {},
29
     }),
30
     }),
30
     created() {
31
     created() {
31
         this.authenticator = new Authenticator()
32
         this.authenticator = new Authenticator()
32
         console.log('this.answered :=>', this.answered)
33
         console.log('this.answered :=>', this.answered)
33
         this.authenticator.sendAuthEmail(this.answered)
34
         this.authenticator.sendAuthEmail(this.answered)
35
+        // TODO: Consider waiting until email hash is verified...(i.e. not in created)
36
+        signupUser(this.answered)
34
     },
37
     },
35
     methods: {
38
     methods: {
36
         // TODO: remove test button above and use a watcher instead to emit this
39
         // TODO: remove test button above and use a watcher instead to emit this

+ 10
- 1
frontend/src/router/index.js 查看文件

7
 import LoginView from '../views/LoginView.vue'
7
 import LoginView from '../views/LoginView.vue'
8
 import SurveyView from '../views/SurveyView.vue'
8
 import SurveyView from '../views/SurveyView.vue'
9
 import OnboardingView from '../views/OnboardingView.vue'
9
 import OnboardingView from '../views/OnboardingView.vue'
10
+import VerifyView from '../views/VerifyView.vue'
10
 import MessagesView from '../views/MessagesView.vue'
11
 import MessagesView from '../views/MessagesView.vue'
11
 
12
 
12
 const routes = [
13
 const routes = [
57
         name: `SurveyView`,
58
         name: `SurveyView`,
58
         meta: { requiresAuth: true, requiresCompleteProfile: false },
59
         meta: { requiresAuth: true, requiresCompleteProfile: false },
59
     },
60
     },
61
+    // TODO: remove after better implementation is found for verifying email
62
+    // hash
60
     {
63
     {
61
-        path: `/onboarding`,
64
+        path: `/onboarding/:hash?`,
62
         component: OnboardingView,
65
         component: OnboardingView,
63
         name: `OnboardingView`,
66
         name: `OnboardingView`,
64
         meta: { requiresAuth: true, requiresCompleteProfile: false },
67
         meta: { requiresAuth: true, requiresCompleteProfile: false },
65
     },
68
     },
69
+    {
70
+        path: `/verify/:email?`,
71
+        component: VerifyView,
72
+        name: `VerifyView`,
73
+        meta: { requiresAuth: true, requiresCompleteProfile: false },
74
+    },
66
     {
75
     {
67
         path: `/login`,
76
         path: `/login`,
68
         component: LoginView,
77
         component: LoginView,

+ 4
- 1
frontend/src/services/auth.service.js 查看文件

3
 class Authenticator {
3
 class Authenticator {
4
     constructor() {
4
     constructor() {
5
         this.curentUser = null
5
         this.curentUser = null
6
-        this.authenticated = false
7
     }
6
     }
8
     async sendAuthEmail(answered) {
7
     async sendAuthEmail(answered) {
9
         const emailWasSent = await db.post(`/user/sendemail/`, answered)
8
         const emailWasSent = await db.post(`/user/sendemail/`, answered)
10
         console.log('emailwasSent :=>', emailWasSent)
9
         console.log('emailwasSent :=>', emailWasSent)
11
     }
10
     }
11
+    async verifyAuthEmail(hash) {
12
+        const isVerified = await db.get(`/user/verify/${hash}`)
13
+        return isVerified.hashesMatch
14
+    }
12
 }
15
 }
13
 
16
 
14
 export { Authenticator }
17
 export { Authenticator }

+ 1
- 1
frontend/src/utils/lang.js 查看文件

33
 const allSteps = {
33
 const allSteps = {
34
     usa: {
34
     usa: {
35
         email: 'email',
35
         email: 'email',
36
-        password: 'password',
37
         name: 'name',
36
         name: 'name',
37
+        password: 'password',
38
         seeking: 'seeking',
38
         seeking: 'seeking',
39
         aspect01: 'aspect-1',
39
         aspect01: 'aspect-1',
40
         aspect02: 'aspect-2',
40
         aspect02: 'aspect-2',

+ 22
- 5
frontend/src/views/OnboardingView.vue 查看文件

6
     )
6
     )
7
         .answers(v-for='(value, key) in answered')
7
         .answers(v-for='(value, key) in answered')
8
             span(v-if='key == "name" && value && currentStep == 2') Hi {{ value }}!
8
             span(v-if='key == "name" && value && currentStep == 2') Hi {{ value }}!
9
-        //- h3(v-if='currentStep == 1') Welcome to Siimee Onboarding! Let's get started!
10
         br
9
         br
11
         .step(v-for='(step, i) in survey.steps')
10
         .step(v-for='(step, i) in survey.steps')
12
             component(
11
             component(
13
                 :is='step.component'
12
                 :is='step.component'
14
                 :question='step'
13
                 :question='step'
14
+                :answered='answered'
15
                 :currentStep='currentStep'
15
                 :currentStep='currentStep'
16
                 :surveyStepsCount='survey.steps.length'
16
                 :surveyStepsCount='survey.steps.length'
17
                 @handle-submit='onSubmit'
17
                 @handle-submit='onSubmit'
18
                 @update-answers='updateAnswers'
18
                 @update-answers='updateAnswers'
19
+                @resume-survey='resumeSurvey'
19
                 v-if='step && currentStep == i'
20
                 v-if='step && currentStep == i'
20
             ) 
21
             ) 
21
         .invalidResponseMessage(
22
         .invalidResponseMessage(
32
 </template>
33
 </template>
33
 
34
 
34
 <script>
35
 <script>
36
+import { Authenticator } from '../services/auth.service.js'
35
 import { surveyFactory } from '@/utils'
37
 import { surveyFactory } from '@/utils'
36
 import stepViews from '@/components/onboarding'
38
 import stepViews from '@/components/onboarding'
39
+import VerifyView from './VerifyView.vue'
37
 import SurveyCompleteView from './SurveyCompleteView.vue'
40
 import SurveyCompleteView from './SurveyCompleteView.vue'
38
 
41
 
39
 // import savesurveybyprofileid - call it on submit
42
 // import savesurveybyprofileid - call it on submit
52
         survey: null,
55
         survey: null,
53
         currentProfileId: null,
56
         currentProfileId: null,
54
         invalidResponse: false,
57
         invalidResponse: false,
58
+        // TODO: CURRENTLY WORKING ON**
59
+        isAuthenticated: false,
60
+        authenticator: {},
55
     }),
61
     }),
56
     async created() {
62
     async created() {
57
         this.survey = await surveyFactory.createSurvey()
63
         this.survey = await surveyFactory.createSurvey()
58
-        console.log('this.survey :=>', this.survey)
64
+        this.authenticator = new Authenticator()
65
+        // TODO: Replace with better implementation, hacky workaround for now...
66
+        if (this.$route.params.hash) {
67
+            const hashEmail = this.$route.params.hash
68
+            const hashesMatch = this.authenticator.verifyAuthEmail(hashEmail)
69
+            if (hashesMatch) {
70
+                this.currentStep = 4
71
+            } else {
72
+                console.log('nope, not at all :=>')
73
+            }
74
+        }
59
     },
75
     },
60
     methods: {
76
     methods: {
61
         onSubmit() {
77
         onSubmit() {
64
         goToStep(num) {
80
         goToStep(num) {
65
             this.currentStep = num
81
             this.currentStep = num
66
         },
82
         },
83
+        resumeSurvey() {
84
+            this.goToStep(4)
85
+        },
67
         async updateAnswers(payload) {
86
         async updateAnswers(payload) {
68
             // null payload is passed on splash page
87
             // null payload is passed on splash page
69
             if (payload) {
88
             if (payload) {
70
-                // authentication achieved if payload.resume = true
71
-                if (payload.resume) this.currentStep = 3
72
-                this.invalidResponse = false
89
+                // this.invalidResponse = false
73
                 const k = payload.question.survey_stage
90
                 const k = payload.question.survey_stage
74
                 this.answered[k] = payload.input
91
                 this.answered[k] = payload.input
75
 
92
 

+ 39
- 0
frontend/src/views/VerifyView.vue 查看文件

1
+<template lang="pug">
2
+.wait-message
3
+    p.verify-message Thanks for authenticating your email {{ name }}!
4
+    p.verify-message Please give us a moment to redirect you back to the survey
5
+</template>
6
+
7
+<script>
8
+import { Authenticator } from '../services/auth.service.js'
9
+export default {
10
+    name: 'VerifyView',
11
+    emits: ['resume-survey'],
12
+    data: () => ({
13
+        authenticator: {},
14
+    }),
15
+    created() {
16
+        this.authenticator = new Authenticator()
17
+        const hashEmail = this.$route.params.email
18
+        const hashesMatch = this.authenticator.verifyAuthEmail(hashEmail)
19
+        if (hashesMatch) {
20
+            // this.$router.push(`/onboarding/${hashEmail}`)
21
+            this.$emit('resume-survey')
22
+        }
23
+        // else {
24
+        // render ERROR message above or redirect to 404 (or both?)
25
+        // }
26
+    },
27
+}
28
+</script>
29
+
30
+<style>
31
+.wait-message {
32
+    margin: 5rem auto;
33
+    text-align: center;
34
+    width: 90%;
35
+    max-width: 35rem;
36
+    font-size: 150%;
37
+    font-weight: bold;
38
+}
39
+</style>

Loading…
取消
儲存