import * as hapi from "@hapi/hapi" ; import open from "open" ; import { CONSTS , bold , underline , spinner , writeError } from "../utils" ; import { setAuthConfig } from "../filesystem" ; export type HandlerData = { email : string ; cookies : ReadonlyArray < string > ; } ; export const isHandlerData = ( data ) : data is HandlerData => { if ( typeof data != "object" ) return false ; const { email , cookies } = data as Partial < HandlerData > ; return ( Boolean ( email && cookies ) && Array . isArray ( cookies ) && ! cookies . some ( ( c ) => typeof c !== "string" ) ) ; } ; const Routes = { authentication : ( server ) => ( onSuccess , onFail ) : hapi . ServerRoute => ( { method : "POST" , path : "/" , handler : authenticationHandler ( server ) ( onSuccess , onFail ) , } ) , notFound : { method : [ "GET" , "POST" ] , path : "/{any*}" , handler : ( request , h ) : hapi . ResponseObject => { return h . response ( `not found: [${ request . method }]: ${ request . url . toString ( ) }` ) . code ( 404 ) ; } , } , } ; export function validatePayload ( payload ) { if ( Buffer . isBuffer ( payload ) ) return null ; return isHandlerData ( payload ) ? payload : null ; } const authenticationHandler = ( server ) => ( onSuccess , onFail ) => { return ( request , h ) => { try { const data = validatePayload ( request . payload as Buffer | string | Record < string , unknown > ) ; if ( ! data ) { onFail ( ) ; h . response ( "Error with cookies" ) . code ( 400 ) ; throw new Error ( "It seems the server didn't respond properly, please contact us." ) ; } onSuccess ( data ) ; return h . response ( data ) . code ( 200 ) ; } finally { void server . stop ( { timeout : 10000 } ) ; } } ; } ; function buildServer ( base , port , host ) { const server = hapi . server ( { port , host , routes : { cors : { origin : [ base ] , headers : [ "Origin" , "X-Requested-With" , "Content-Type" , "Accept" ] , } , } , } ) ; return server ; } export function askSingleChar ( title ) { return new Promise ( ( resolve ) => { process . stdout . write ( title ) ; const rawMode = process . stdin . isRaw ; if ( process . stdin . setRawMode ) process . stdin . setRawMode ( true ) ; function handler ( key ) { const response = key . toString ( "utf-8" ) ; if ( process . stdin . setRawMode ) process . stdin . setRawMode ( Boolean ( rawMode ) ) ; process . stdin . removeListener ( "data" , handler ) ; resolve ( response ) ; } process . stdin . on ( "data" , handler ) ; } ) ; } export async function startServerAndOpenBrowser ( url , action , base = CONSTS . DEFAULT_BASE , port = CONSTS . DEFAULT_SERVER_PORT ) { const confirmation = await askSingleChar ( `>> Press any key to open the browser to ${ action } or q to exit:` ) ; if ( confirmation === "q" || confirmation === "\u0003" ) return process . exit ( - 1 ) ; const s = spinner ( "Waiting for the browser response" ) ; const onLoginFail = ( ) => { s . stop ( ) ; writeError ( `We failed to log you into your Prismic account` ) ; console . log ( `Run ${ bold ( "npx slicemachine init" ) } again!` ) ; process . exit ( - 1 ) ; } ; function onSuccess ( data ) { s . succeed ( `Logged in as ${ bold ( data . email ) }` ) . stop ( ) ; setAuthConfig ( data . cookies , base ) ; } function onFail ( ) { onLoginFail ( ) ; } const server = buildServer ( base , port , "localhost" ) ; server . route ( [ Routes . authentication ( server ) ( onSuccess , onFail ) , Routes . notFound , ] ) ; return server . start ( ) . then ( ( ) => { console . log ( "\nOpening browser to " + underline ( url ) ) ; s . start ( ) ; void open ( url ) ; return { onLoginFail , } ; } ) ; }