1import { promises as fs } from 'fs'; 2import gql from 'graphql-tag'; 3 4import * as Log from '../../log'; 5import * as Analytics from '../../utils/analytics/rudderstackClient'; 6import { getDevelopmentCodeSigningDirectory } from '../../utils/codesigning'; 7import { graphqlClient } from '../graphql/client'; 8import { CurrentUserQuery } from '../graphql/generated'; 9import { UserQuery } from '../graphql/queries/UserQuery'; 10import { fetchAsync } from '../rest/client'; 11import { APISettings } from '../settings'; 12import UserSettings from './UserSettings'; 13 14export type Actor = NonNullable<CurrentUserQuery['meActor']>; 15 16let currentUser: Actor | undefined; 17 18export const ANONYMOUS_USERNAME = 'anonymous'; 19 20/** 21 * Resolve the name of the actor, either normal user or robot user. 22 * This should be used whenever the "current user" needs to be displayed. 23 * The display name CANNOT be used as project owner. 24 */ 25export function getActorDisplayName(user?: Actor): string { 26 switch (user?.__typename) { 27 case 'User': 28 return user.username; 29 case 'Robot': 30 return user.firstName ? `${user.firstName} (robot)` : 'robot'; 31 default: 32 return ANONYMOUS_USERNAME; 33 } 34} 35 36export async function getUserAsync(): Promise<Actor | undefined> { 37 const hasCredentials = UserSettings.getAccessToken() || UserSettings.getSession()?.sessionSecret; 38 if (!APISettings.isOffline && !currentUser && hasCredentials) { 39 const user = await UserQuery.currentUserAsync(); 40 currentUser = user ?? undefined; 41 if (user) { 42 await Analytics.setUserDataAsync(user.id, { 43 username: getActorDisplayName(user), 44 user_id: user.id, 45 user_type: user.__typename, 46 }); 47 } 48 } 49 return currentUser; 50} 51 52export async function loginAsync(json: { 53 username: string; 54 password: string; 55 otp?: string; 56}): Promise<void> { 57 const res = await fetchAsync('auth/loginAsync', { 58 method: 'POST', 59 body: JSON.stringify(json), 60 }); 61 const { 62 data: { sessionSecret }, 63 } = await res.json(); 64 const result = await graphqlClient 65 .query( 66 gql` 67 query UserQuery { 68 viewer { 69 id 70 username 71 } 72 } 73 `, 74 {}, 75 { 76 fetchOptions: { 77 headers: { 78 'expo-session': sessionSecret, 79 }, 80 }, 81 additionalTypenames: [] /* UserQuery has immutable fields */, 82 } 83 ) 84 .toPromise(); 85 const { 86 data: { viewer }, 87 } = result; 88 await UserSettings.setSessionAsync({ 89 sessionSecret, 90 userId: viewer.id, 91 username: viewer.username, 92 currentConnection: 'Username-Password-Authentication', 93 }); 94} 95 96export async function logoutAsync(): Promise<void> { 97 currentUser = undefined; 98 await Promise.all([ 99 fs.rm(getDevelopmentCodeSigningDirectory(), { recursive: true, force: true }), 100 UserSettings.setSessionAsync(undefined), 101 ]); 102 Log.log('Logged out'); 103} 104