UNPKG

edge-core-js

Version:

Edge account & wallet management library

188 lines (154 loc) 4.37 kB
import { asObject, asOptional, asString, uncleaner } from 'cleaners' import { bridgifyObject, close } from 'yaob' import { shuffle } from '../../util/shuffle' import { asLobbyLoginPayload } from '../login/edge' import { fetchLobbyRequest, sendLobbyReply } from '../login/lobby' import { decryptChildKey, sanitizeLoginStash, searchTree, syncLogin } from '../login/login' import { getStashById } from '../login/login-selectors' import { ensureAccountExists } from './account-init' const wasLobbyLoginPayload = uncleaner(asLobbyLoginPayload) const asAppIdInfo = asObject({ appName: asString, darkImageUrl: asOptional(asString), lightImageUrl: asOptional(asString), imageUrl: asOptional(asString) }) /** * Translate an appId into a user-presentable icon and string. */ export async function fetchAppIdInfo( ai, appId ) { try { const [infoServerUri] = shuffle(ai.props.state.infoServers) const url = `${infoServerUri}/v1/appIdInfo/${appId}` const response = await ai.props.io.fetch(url, { corsBypass: 'never' }) if (response.status === 404) { return { appName: appId } } if (!response.ok) { throw new Error(`Fetching ${url} returned ${response.status}`) } const clean = asAppIdInfo(await response.json()) // Upgrade legacy responses: if (clean.lightImageUrl == null) clean.lightImageUrl = clean.imageUrl if (clean.darkImageUrl == null) clean.darkImageUrl = clean.imageUrl return clean } catch (error) { // Log failures, but still return the appId as a fallback: ai.props.onError(error) return { appName: appId } } } /** * Performs an edge login, approving the request in the provided lobby JSON. */ async function approveLoginRequest( ai, accountId, appId, lobbyId, lobbyJson ) { const { sessionKey } = ai.props.state.accounts[accountId] // For crash errors: ai.props.log.breadcrumb('approveLoginRequest', {}) // Ensure that the login object & account repo exist: await syncLogin(ai, sessionKey) const { stashTree } = getStashById(ai, sessionKey.loginId) const newStashTree = await ensureAccountExists( ai, stashTree, sessionKey, appId ) const appStash = searchTree(newStashTree, stash => stash.appId === appId) if (appStash == null) { throw new Error('Failed to create the requested login object') } const appKey = decryptChildKey(newStashTree, sessionKey, appStash.loginId) // Send the reply: const replyData = wasLobbyLoginPayload({ appId, loginKey: appKey.loginKey, loginStash: sanitizeLoginStash(newStashTree, appId) }) await sendLobbyReply(ai, lobbyId, lobbyJson, replyData) let timeout const accountApi = ai.props.output.accounts[accountId].accountApi if (accountApi != null) { accountApi.on('close', () => { if (timeout != null) clearTimeout(timeout) }) } timeout = setTimeout(() => { timeout = undefined syncLogin(ai, sessionKey) .then(() => { timeout = setTimeout(() => { timeout = undefined syncLogin(ai, sessionKey).catch(error => ai.props.onError(error)) }, 20000) }) .catch(error => ai.props.onError(error)) }, 10000) } /** * Fetches the contents of a lobby and returns them as an EdgeLobby API. */ export async function makeLobbyApi( ai, accountId, lobbyId ) { // Look up the lobby on the server: const lobbyJson = await fetchLobbyRequest(ai, lobbyId) // If the lobby has a login request, set up that API: let loginRequest if (lobbyJson.loginRequest != null) { loginRequest = await unpackLoginRequest( ai, accountId, lobbyId, lobbyJson, lobbyJson.loginRequest.appId ) } const out = { loginRequest } bridgifyObject(out) return out } async function unpackLoginRequest( ai, accountId, lobbyId, lobbyJson, appId ) { // For crash errors: ai.props.log.breadcrumb('unpackLoginRequest', {}) const info = await fetchAppIdInfo(ai, appId) // Make the API: const out = { appId, displayName: info.appName, displayImageDarkUrl: info.darkImageUrl, displayImageLightUrl: info.lightImageUrl, approve() { return approveLoginRequest(ai, accountId, appId, lobbyId, lobbyJson) }, async close() { close(out) } } bridgifyObject(out) return out }