@@ -4,22 +4,25 @@ import {pipe} from 'fp-ts/lib/function';
44import { parseEmailAddressFromBody } from './parse-email-address-from-body' ;
55import * as E from 'fp-ts/Either' ;
66import * as O from 'fp-ts/Option' ;
7+ import * as TE from 'fp-ts/TaskEither' ;
78import { publish } from 'pubsub-js' ;
89import passport from 'passport' ;
9- import { magicLink } from './magic-link' ;
10+ import { emailVerificationLink , magicLink } from './magic-link' ;
1011import { logInPage } from './log-in-page' ;
1112import { checkYourMailPage } from './check-your-mail' ;
1213import { oopsPage , isolatedPageTemplate } from '../templates' ;
1314import { StatusCodes } from 'http-status-codes' ;
1415import { getUserFromSession } from './get-user-from-session' ;
1516import { Dependencies } from '../dependencies' ;
17+ import { Config } from '../configuration' ;
1618import {
1719 html ,
1820 HtmlSubstitution ,
1921 CompleteHtmlDocument ,
2022 sanitizeString ,
2123 safe ,
2224} from '../types/html' ;
25+ import { verifyEmail } from '../commands/members/verify-email' ;
2326
2427export const logIn =
2528 ( deps : Dependencies ) =>
@@ -58,37 +61,96 @@ export const auth = (req: Request, res: Response<CompleteHtmlDocument>) => {
5861} ;
5962
6063export const invalidLink =
61- ( logInPath : HtmlSubstitution ) =>
64+ ( linkPath : HtmlSubstitution , copy ? : HtmlSubstitution ) =>
6265 ( req : Request , res : Response < CompleteHtmlDocument > ) => {
6366 res
6467 . status ( StatusCodes . UNAUTHORIZED )
6568 . send (
6669 oopsPage (
67- html `The link you have used is (no longer) valid. Go back to the
68- < a href =${ logInPath } > log in</ a > page.`
70+ copy ??
71+ html `The link you have used is (no longer) valid. Go back to the
72+ < a href =${ linkPath } > log in</ a > page.`
6973 )
7074 ) ;
7175 } ;
7276
73- export const landing = ( req : Request , res : Response < CompleteHtmlDocument > ) => {
74- const index = req . originalUrl . indexOf ( '?' ) ;
75- const suffix = index === - 1 ? '' : req . originalUrl . slice ( index ) ;
76- const url = '/auth/callback' + suffix ;
77- res . status ( StatusCodes . OK ) . send (
78- isolatedPageTemplate ( sanitizeString ( 'Redirecting...' ) ) ( html `
79- <!doctype html>
80- < html >
81- < head >
82- < meta http-equiv ="refresh " content ="0; url='${ safe ( url ) } ' " />
83- </ head >
84- < body > </ body >
85- </ html >
86- ` )
87- ) ;
88- } ;
77+ export const landing =
78+ ( callbackPath : string ) =>
79+ ( req : Request , res : Response < CompleteHtmlDocument > ) => {
80+ const index = req . originalUrl . indexOf ( '?' ) ;
81+ const suffix = index === - 1 ? '' : req . originalUrl . slice ( index ) ;
82+ const url = callbackPath + suffix ;
83+ res . status ( StatusCodes . OK ) . send (
84+ isolatedPageTemplate ( sanitizeString ( 'Redirecting...' ) ) ( html `
85+ <!doctype html>
86+ < html >
87+ < head >
88+ < meta http-equiv ="refresh " content ="0; url='${ safe ( url ) } ' " />
89+ </ head >
90+ < body > </ body >
91+ </ html >
92+ ` )
93+ ) ;
94+ } ;
8995
9096export const callback = ( invalidLinkPath : string ) =>
9197 passport . authenticate ( magicLink . name , {
9298 failureRedirect : invalidLinkPath ,
9399 successRedirect : '/' ,
94100 } ) as RequestHandler ;
101+
102+ export const verifyEmailCallback =
103+ ( deps : Dependencies , conf : Config , invalidLinkPath : string ) : RequestHandler =>
104+ ( req , res ) => {
105+ pipe (
106+ req . query ,
107+ emailVerificationLink . decodeFromQuery ( deps . logger , conf ) ,
108+ E . match (
109+ error => {
110+ deps . logger . info (
111+ { error} ,
112+ 'Failed to decode email verification link'
113+ ) ;
114+ res . redirect ( invalidLinkPath ) ;
115+ } ,
116+ payload => {
117+ const input = {
118+ memberNumber : payload . memberNumber ,
119+ email : payload . emailAddress ,
120+ } ;
121+ const resource = verifyEmail . resource ( input ) ;
122+ void pipe (
123+ deps . getResourceEvents ( resource ) ,
124+ TE . chain ( ( { events, version} ) => {
125+ const event = verifyEmail . process ( {
126+ command : {
127+ ...input ,
128+ actor : { tag : 'system' } ,
129+ } ,
130+ events,
131+ } ) ;
132+ if ( O . isNone ( event ) ) {
133+ return TE . right ( undefined ) ;
134+ }
135+ return pipe (
136+ deps . commitEvent ( resource , version ) ( event . value ) ,
137+ TE . map ( ( ) => undefined )
138+ ) ;
139+ } ) ,
140+ TE . match (
141+ failure => {
142+ deps . logger . error (
143+ { failure} ,
144+ 'Failed to verify member email from callback'
145+ ) ;
146+ res . redirect ( invalidLinkPath ) ;
147+ } ,
148+ ( ) => {
149+ res . redirect ( '/me' ) ;
150+ }
151+ )
152+ ) ( ) ;
153+ }
154+ )
155+ ) ;
156+ } ;
0 commit comments