edge-core-js
Version:
Edge account & wallet management library
147 lines (111 loc) • 3.04 kB
JavaScript
import { asObject, asString } from 'cleaners'
import { bridgifyObject, close, update, watchMethod } from 'yaob'
import { asBase64 } from '../../types/server-cleaners'
import { makeAccount } from '../account/account-init'
import { makeLobby } from './lobby'
import { searchTree, syncLogin } from './login'
import { getStashById } from './login-selectors'
import { asLoginStash, saveStash } from './login-stash'
export const asLobbyLoginPayload = asObject({
appId: asString,
loginKey: asBase64,
loginStash: asLoginStash
})
/**
* Turns a reply into a logged-in account.
*/
async function unpackAccount(
ai,
payload,
appId,
opts
) {
const { now = new Date() } = opts
const { loginKey, loginStash: stashTree } = payload
// For crash errors:
ai.props.log.breadcrumb('unpackAccount', {})
// Find the appropriate child:
const child = searchTree(stashTree, stash => stash.appId === appId)
if (child == null) {
throw new Error(`Cannot find requested appId: "${appId}"`)
}
// Rescue any existing vouchers:
try {
const old = getStashById(ai, child.loginId)
child.voucherId = old.stash.voucherId
child.voucherAuth = old.stash.voucherAuth
} catch (error) {}
stashTree.lastLogin = now
await saveStash(ai, stashTree)
// This is almost guaranteed to blow up spectacularly:
const sessionKey = {
loginId: child.loginId,
loginKey
}
await syncLogin(ai, sessionKey)
return await makeAccount(ai, sessionKey, 'edgeLogin', opts)
}
/**
* Creates a new account request lobby on the server.
*/
export async function requestEdgeLogin(
ai,
appId,
opts = {}
) {
function handleSoftError(error) {
out.error = error
update(out)
}
function handleError(error) {
// Stop the long-polling:
for (const cleanup of cleanups) cleanup()
// Update the API:
out.state = 'error'
out.error = error
update(out)
close(out)
}
async function handleReply(reply) {
// Stop the long-polling:
for (const cleanup of cleanups) cleanup()
// Decode the reply:
const payload = asLobbyLoginPayload(reply)
const { username } = payload.loginStash
out.state = 'started'
out.username = username
update(out)
// Log in:
const account = await unpackAccount(ai, payload, appId, opts)
out.state = 'done'
out.account = account
update(out)
close(out)
}
async function cancelRequest() {
// Stop the long-polling:
for (const cleanup of cleanups) cleanup()
// Update the API:
out.state = 'closed'
update(out)
close(out)
}
const lobby = await makeLobby(ai, { loginRequest: { appId } })
const cleanups = [
lobby.close,
lobby.on('error', handleSoftError),
lobby.on('reply', reply => {
handleReply(reply).catch(handleError)
})
]
const out = {
id: lobby.lobbyId,
cancelRequest,
watch: watchMethod,
state: 'pending',
account: undefined,
error: undefined,
username: undefined
}
return bridgifyObject(out)
}