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