import { Response , Request } from "express" ; import { ConnectStripeArgs , LogInArgs } from "./types" ; import { IResolvers } from "apollo-server-express" ; import { Viewer , Database , User } from "../../../lib/types" ; import { Google , Stripe } from "../../../lib/api" ; import crypto from "crypto" ; import { authorize } from "../../../lib/utils" ; /**
 * 要写入的cookie字段
 */ const cookieOptions = { httpOnly : true , sameSite : true , signed : true , secure : process . env . NODE_ENV === "development" ? false : true , } ; const logInViaGoogle = async ( code , token , db , res ) : Promise < User | undefined > => { const { user } = await Google . logIn ( code ) ; if ( ! user ) { throw new Error ( "谷歌登录失败" ) ; } console . log ( `谷歌认证成功，开始提取需要的google用户信息` ) ; const userNamesList = user . names && user . names . length ? user . names : null ; const userPhotosList = user . photos && user . photos . length ? user . photos : null ; const userEmailsList = user . emailAddresses && user . emailAddresses . length ? user . emailAddresses : null ; const userName = userNamesList ? userNamesList [ 0 ] . displayName : null ; const userId = userNamesList && userNamesList [ 0 ] . metadata && userNamesList [ 0 ] . metadata . source ? userNamesList [ 0 ] . metadata . source . id : null ; const userAvatar = userPhotosList && userPhotosList [ 0 ] . url ? userPhotosList [ 0 ] . url : null ; const userEmail = userEmailsList && userEmailsList [ 0 ] . value ? userEmailsList [ 0 ] . value : null ; if ( ! userId || ! userName || ! userAvatar || ! userEmail ) { throw new Error ( "谷歌用户数据出错" ) ; } const updateRes = await db . users . findOneAndUpdate ( { _id : userId } , { $set : { name : userName , avatar : userAvatar , contact : userEmail , token , } , } , { returnOriginal : false } ) ; let viewer = updateRes . value ; if ( ! viewer ) { console . log ( `用户首次登录，需要向数据库存入该用户` ) ; const insertResult = await db . users . insertOne ( { _id : userId , token , name : userName , avatar : userAvatar , contact : userEmail , income : 0 , bookings : [ ] , listings : [ ] , } ) ; console . log ( `存入用户数据成功` ) ; viewer = insertResult . ops [ 0 ] ; } console . log ( `开始向客户端写入cookie` ) ; res . cookie ( "viewer" , userId , { ... cookieOptions , maxAge : 365 * 24 * 60 * 60 * 1000 , } ) ; return viewer ; } ; const logInViaCookie = async ( token , db , req , res ) : Promise < User | undefined > => { const updateRes = await db . users . findOneAndUpdate ( { _id : req . signedCookies . viewer } , { $set : { token } } , { returnOriginal : false } ) ; let viewer = updateRes . value ; if ( ! viewer ) { res . clearCookie ( "viewer" , cookieOptions ) ; } return viewer ; } ; export const viewerResolvers = { Query : { /**
         * 为OAuth登录提供 Google登录地址
         */ authUrl : ( ) : string => { try { return Google . authUrl ; } catch ( error ) { throw new Error ( `获取Google authUrl 出现错误${ error }` ) ; } } , } , Mutation : { logIn : async ( _root , { input } , { db , res , req } ) : Promise < Viewer > => { try { const code = input ? input . code : null ; console . log ( `登录授权码为: ${ code }` ) ; const token = crypto . randomBytes ( 16 ) . toString ( "hex" ) ; console . log ( `每次生成的token为:${ token }` ) ; /**
                 * 如果本次登录没有携带code，发生行为可能是如下之一：
                 *
                 * - F5刷新页面，前端再次发起LogIn mutation
                 *
                 * - 用户登录过后一段时间再次访问该网站
                 *
                 * 将进行 cookie持久化登录 认证
                 */ const viewer = code ? await logInViaGoogle ( code , token , db , res ) : await logInViaCookie ( token , db , req , res ) ; if ( ! viewer ) { return { didRequest : true } ; } return { _id : viewer . _id , token : viewer . token , avatar : viewer . avatar , walletId : viewer . walletId , didRequest : true , } ; } catch ( error ) { throw new Error ( `登录失败: ${ error }` ) ; } } , logOut : ( _root , _args , { res } ) : Viewer => { try { res . clearCookie ( "viewer" , cookieOptions ) ; return { didRequest : true } ; } catch ( error ) { throw new Error ( `注销失败: ${ error }` ) ; } } , connectStripe : async ( _root , { input } , { db , req } ) : Promise < Viewer > => { try { const { code } = input ; let viewer = await authorize ( db , req ) ; if ( ! viewer ) { throw new Error ( "该用户没有权限连接到stripe" ) ; } const wallet = await Stripe . connect ( code ) ; if ( ! wallet ) { throw new Error ( "stripe连接出错" ) ; } const updateRes = await db . users . findOneAndUpdate ( { _id : viewer . _id } , { $set : { walletId : wallet . stripe_user_id } } , { returnOriginal : false } ) ; if ( ! updateRes . value ) { throw new Error ( "DB更新用户数据出错，在connectStripe函数中" ) ; } viewer = updateRes . value ; return { _id : viewer . _id , token : viewer . token , avatar : viewer . avatar , walletId : viewer . walletId , didRequest : true } ; } catch ( e ) { throw new Error ( `连接到stripe失败，全局捕获错误: ${ e }` ) ; } } , param _root param _args param db param req disconnectStripe : async ( _root , _args , { db , req } ) : Promise < Viewer > => { try { let viewer = await authorize ( db , req ) ; if ( ! viewer ) { throw new Error ( "viewer cannot be found" ) ; } const updateRes = await db . users . findOneAndUpdate ( { _id : viewer . _id } , { $set : { walletId : null } } , { returnOriginal : false } ) ; if ( ! updateRes . value ) { throw new Error ( "viewer could not be updated" ) ; } viewer = updateRes . value ; return { _id : viewer . _id , token : viewer . token , avatar : viewer . avatar , walletId : viewer . walletId , didRequest : true } ; } catch ( error ) { throw new Error ( `Failed to disconnect with Stripe: ${ error }` ) ; } } } , Viewer : { id : ( viewer ) : string | undefined => { return viewer . _id ; } , hasWallet : ( viewer ) : boolean | undefined => { return viewer . walletId ? true : undefined ; } , } , } ;