2021-01-18 22:34:41 +04:00
import sessionStorage from '../sessionStorage' ;
2020-02-06 22:43:07 +07:00
2020-10-15 19:54:54 +03:00
import MTPNetworker , { MTMessage } from './networker' ;
2020-10-29 19:11:09 +02:00
import { isObject } from './bin_utils' ;
2020-02-06 22:43:07 +07:00
import networkerFactory from './networkerFactory' ;
2020-10-02 23:33:32 +03:00
//import { telegramMeWebService } from './mtproto';
2020-02-06 22:43:07 +07:00
import authorizer from './authorizer' ;
2020-10-07 23:49:52 +03:00
import dcConfigurator , { ConnectionType , TransportType } from './dcConfigurator' ;
2020-06-21 15:25:17 +03:00
import { logger } from '../logger' ;
2020-10-07 16:57:33 +03:00
import type { InvokeApiOptions } from '../../types' ;
import type { MethodDeclMap } from '../../layer' ;
import { CancellablePromise , deferredPromise } from '../../helpers/cancellablePromise' ;
2020-10-29 19:11:09 +02:00
import { bytesFromHex , bytesToHex } from '../../helpers/bytes' ;
2020-12-11 04:06:16 +02:00
//import { clamp } from '../../helpers/number';
2021-02-10 08:06:22 +02:00
import { isSafari } from '../../helpers/userAgent' ;
2021-02-13 19:32:10 +04:00
import App from '../../config/app' ;
import { MOUNT_CLASS_TO } from '../../config/debug' ;
2020-04-26 15:19:17 +03:00
2020-05-03 15:46:05 +03:00
/// #if !MTPROTO_WORKER
2020-11-15 05:33:47 +02:00
import rootScope from '../rootScope' ;
2020-05-03 15:46:05 +03:00
/// #endif
2020-10-28 18:11:57 +02:00
/ * v a r n e t w o r k e r = a p i M a n a g e r . c a c h e d N e t w o r k e r s . w e b s o c k e t . u p l o a d [ 2 ] ;
networker . wrapMtpMessage ( {
_ : 'msgs_state_req' ,
msg_ids : [ "6888292542796810828" ]
} , {
notContentRelated : true
} ) . then ( res = > {
console . log ( 'status' , res ) ;
} ) ; * /
2020-04-28 20:30:54 +03:00
//console.error('apiManager included!');
2020-09-18 18:52:21 +03:00
// TODO: если запрос словил флуд, нужно сохранять е г о параметры и возвращать тот же промис на новый такой же запрос, например - загрузка истории
2020-02-06 22:43:07 +07:00
2020-09-29 05:03:26 +03:00
export type ApiError = Partial < {
code : number ,
type : string ,
description : string ,
originalError : any ,
stack : string ,
handled : boolean ,
input : string ,
message : ApiError
} > ;
2020-12-11 04:06:16 +02:00
/ * c l a s s R o t a t a b l e A r r a y < T > {
public array : Array < T > = [ ] ;
private lastIndex = - 1 ;
public get ( ) {
this . lastIndex = clamp ( this . lastIndex + 1 , 0 , this . array . length - 1 ) ;
return this . array [ this . lastIndex ] ;
}
} * /
2020-02-06 22:43:07 +07:00
export class ApiManager {
2020-10-07 23:49:52 +03:00
public cachedNetworkers : {
[ transportType in TransportType ] : {
[ connectionType in ConnectionType ] : {
2020-12-11 04:06:16 +02:00
[ dcId : number ] : MTPNetworker [ ]
2020-10-07 23:49:52 +03:00
}
}
} = { } as any ;
2020-02-06 22:43:07 +07:00
public cachedExportPromise : { [ x : number ] : Promise < unknown > } = { } ;
2020-12-11 04:06:16 +02:00
private gettingNetworkers : { [ dcIdAndType : string ] : Promise < MTPNetworker > } = { } ;
public baseDcId = 0 ;
2020-02-06 22:43:07 +07:00
2020-11-16 00:34:48 +02:00
//public telegramMeNotified = false;
2020-02-06 22:43:07 +07:00
private log : ReturnType < typeof logger > = logger ( 'API' ) ;
2020-10-15 19:54:54 +03:00
2020-12-11 04:06:16 +02:00
private afterMessageTempIds : { [ tempId : string ] : string } = { } ;
2020-10-26 02:09:42 +02:00
//private lol = false;
2020-02-06 22:43:07 +07:00
constructor ( ) {
//MtpSingleInstanceService.start();
2020-12-11 04:06:16 +02:00
/ * A p p S t o r a g e . g e t < n u m b e r > ( ' d c ' ) . t h e n ( ( d c I d ) = > {
if ( dcId ) {
this . baseDcId = dcId ;
2020-02-06 22:43:07 +07:00
}
} ) ; * /
}
2020-11-16 00:34:48 +02:00
/ * p u b l i c t e l e g r a m M e N o t i f y ( n e w V a l u e : b o o l e a n ) {
2020-02-06 22:43:07 +07:00
if ( this . telegramMeNotified !== newValue ) {
this . telegramMeNotified = newValue ;
2020-10-02 23:33:32 +03:00
//telegramMeWebService.setAuthorized(this.telegramMeNotified);
2020-02-06 22:43:07 +07:00
}
2020-11-16 00:34:48 +02:00
} * /
2020-02-06 22:43:07 +07:00
// mtpSetUserAuth
2020-12-11 04:06:16 +02:00
public setUserAuth ( userId : number ) {
2021-01-18 22:34:41 +04:00
sessionStorage . set ( {
2020-12-11 04:06:16 +02:00
user_auth : userId
2020-02-06 22:43:07 +07:00
} ) ;
2020-11-16 00:34:48 +02:00
//this.telegramMeNotify(true);
2020-05-03 15:46:05 +03:00
/// #if !MTPROTO_WORKER
2020-12-11 04:06:16 +02:00
rootScope . broadcast ( 'user_auth' , userId ) ;
2020-05-03 15:46:05 +03:00
/// #endif
2020-02-06 22:43:07 +07:00
}
2020-05-06 04:03:31 +03:00
2020-12-11 04:06:16 +02:00
public setBaseDcId ( dcId : number ) {
this . baseDcId = dcId ;
2020-11-16 00:34:48 +02:00
2021-01-18 22:34:41 +04:00
sessionStorage . set ( {
2020-12-11 04:06:16 +02:00
dc : this.baseDcId
2020-11-16 00:34:48 +02:00
} ) ;
2020-05-06 04:03:31 +03:00
}
2020-02-06 22:43:07 +07:00
// mtpLogOut
public async logOut() {
2021-01-18 22:34:41 +04:00
const storageKeys : Array < string > = [ ] ;
2020-02-06 22:43:07 +07:00
2021-01-18 22:34:41 +04:00
const prefix = 'dc' ;
2020-12-11 04:06:16 +02:00
for ( let dcId = 1 ; dcId <= 5 ; dcId ++ ) {
storageKeys . push ( prefix + dcId + '_auth_key' ) ;
//storageKeys.push(prefix + dcId + '_auth_keyId');
2020-02-06 22:43:07 +07:00
}
// WebPushApiManager.forceUnsubscribe(); // WARNING
2021-01-18 22:34:41 +04:00
const storageResult = await Promise . all ( storageKeys . map ( key = > sessionStorage . get ( key as any ) ) ) ;
2020-02-06 22:43:07 +07:00
2021-01-18 22:34:41 +04:00
const logoutPromises = [ ] ;
2020-02-06 22:43:07 +07:00
for ( let i = 0 ; i < storageResult . length ; i ++ ) {
if ( storageResult [ i ] ) {
2020-12-11 04:06:16 +02:00
logoutPromises . push ( this . invokeApi ( 'auth.logOut' , { } , { dcId : i + 1 , ignoreErrors : true } ) ) ;
2020-02-06 22:43:07 +07:00
}
}
2020-12-11 04:42:21 +02:00
const clear = ( ) = > {
//console.error('apiManager: logOut clear');
2020-12-11 04:06:16 +02:00
this . baseDcId = 0 ;
2020-11-16 00:34:48 +02:00
//this.telegramMeNotify(false);
2021-01-18 22:34:41 +04:00
const promise = sessionStorage . clear ( ) ;
2020-12-08 21:48:44 +02:00
promise . finally ( ( ) = > {
self . postMessage ( { type : 'reload' } ) ;
} ) ;
2020-12-11 04:42:21 +02:00
} ;
setTimeout ( clear , 1 e3 ) ;
//return;
return Promise . all ( logoutPromises ) . then ( ( ) = > {
} , ( error ) = > {
error . handled = true ;
} ) . finally ( clear ) / * . then ( ( ) = > {
2020-04-19 00:55:20 +03:00
location . pathname = '/' ;
2020-05-06 04:03:31 +03:00
} ) * / ;
2020-02-06 22:43:07 +07:00
}
// mtpGetNetworker
2020-12-11 04:06:16 +02:00
public getNetworker ( dcId : number , options : InvokeApiOptions = { } ) : Promise < MTPNetworker > {
2021-02-15 13:26:26 +04:00
const connectionType : ConnectionType = options . fileDownload ? 'download' : ( options . fileUpload ? 'upload' : 'client' ) ;
//const connectionType: ConnectionType = 'client';
2020-10-28 18:11:57 +02:00
/// #if MTPROTO_HTTP_UPLOAD
// @ts-ignore
2021-02-13 13:59:35 +04:00
const transportType : TransportType = connectionType === 'upload' && isSafari ? 'https' : 'websocket' ;
2021-02-04 02:30:23 +02:00
//const transportType: TransportType = connectionType !== 'client' ? 'https' : 'websocket';
2020-10-28 18:11:57 +02:00
/// #else
// @ts-ignore
const transportType = 'websocket' ;
/// #endif
2020-10-07 23:49:52 +03:00
if ( ! this . cachedNetworkers . hasOwnProperty ( transportType ) ) {
this . cachedNetworkers [ transportType ] = {
client : { } ,
download : { } ,
upload : { }
} ;
}
const cache = this . cachedNetworkers [ transportType ] [ connectionType ] ;
2020-12-11 04:06:16 +02:00
if ( ! ( dcId in cache ) ) {
cache [ dcId ] = [ ] ;
}
2020-02-06 22:43:07 +07:00
2020-12-11 04:06:16 +02:00
const networkers = cache [ dcId ] ;
2021-02-15 13:26:26 +04:00
if ( networkers . length >= /* 1 */ ( connectionType === 'client' || transportType === 'https' ? 1 : ( connectionType === 'download' ? 3 : 3 ) ) ) {
2021-02-04 08:58:16 +02:00
let i = networkers . length - 1 , found = false ;
for ( ; i >= 0 ; -- i ) {
if ( networkers [ i ] . isOnline ) {
found = true ;
break ;
}
}
const networker = found ? networkers . splice ( i , 1 ) [ 0 ] : networkers . pop ( ) ;
2020-12-11 04:06:16 +02:00
networkers . unshift ( networker ) ;
return Promise . resolve ( networker ) ;
2020-02-06 22:43:07 +07:00
}
2020-12-11 04:06:16 +02:00
const getKey = [ dcId , transportType , connectionType ] . join ( '-' ) ;
2020-02-06 22:43:07 +07:00
if ( this . gettingNetworkers [ getKey ] ) {
return this . gettingNetworkers [ getKey ] ;
}
2020-06-06 09:39:35 +03:00
2020-12-11 04:06:16 +02:00
const ak = 'dc' + dcId + '_auth_key' ;
const akId = 'dc' + dcId + '_auth_keyId' ;
const ss = 'dc' + dcId + '_server_salt' ;
2020-02-06 22:43:07 +07:00
2021-01-18 22:34:41 +04:00
return this . gettingNetworkers [ getKey ] = Promise . all ( [ ak , akId , ss ] . map ( key = > sessionStorage . get ( key as any ) ) )
2020-12-11 04:06:16 +02:00
. then ( async ( [ authKeyHex , authKeyIdHex , serverSaltHex ] ) = > {
const transport = dcConfigurator . chooseServer ( dcId , connectionType , transportType , false ) ;
2020-06-06 09:39:35 +03:00
let networker : MTPNetworker ;
2021-02-04 02:30:23 +02:00
if ( authKeyHex && authKeyHex . length === 512 ) {
if ( ! serverSaltHex || serverSaltHex . length !== 16 ) {
2020-02-06 22:43:07 +07:00
serverSaltHex = 'AAAAAAAAAAAAAAAA' ;
}
2020-06-06 09:39:35 +03:00
const authKey = bytesFromHex ( authKeyHex ) ;
2020-12-11 04:06:16 +02:00
const authKeyId = new Uint8Array ( bytesFromHex ( authKeyIdHex ) ) ;
2020-06-06 09:39:35 +03:00
const serverSalt = bytesFromHex ( serverSaltHex ) ;
2020-02-06 22:43:07 +07:00
2020-12-11 04:06:16 +02:00
networker = networkerFactory . getNetworker ( dcId , authKey , authKeyId , serverSalt , transport , options ) ;
2020-06-06 09:39:35 +03:00
} else {
try { // if no saved state
2020-12-11 04:06:16 +02:00
const auth = await authorizer . auth ( dcId ) ;
2020-06-06 09:39:35 +03:00
const storeObj = {
[ ak ] : bytesToHex ( auth . authKey ) ,
2020-12-11 04:06:16 +02:00
[ akId ] : auth . authKeyId . hex ,
2020-06-06 09:39:35 +03:00
[ ss ] : bytesToHex ( auth . serverSalt )
} ;
2021-01-18 22:34:41 +04:00
sessionStorage . set ( storeObj ) ;
2020-06-06 09:39:35 +03:00
2020-12-11 04:06:16 +02:00
networker = networkerFactory . getNetworker ( dcId , auth . authKey , auth . authKeyId , auth . serverSalt , transport , options ) ;
2020-06-06 09:39:35 +03:00
} catch ( error ) {
this . log ( 'Get networker error' , error , error . stack ) ;
delete this . gettingNetworkers [ getKey ] ;
throw error ;
}
2020-02-06 22:43:07 +07:00
}
2020-11-15 05:33:47 +02:00
/ * n e t w o r k e r . o n C o n n e c t i o n S t a t u s C h a n g e = ( o n l i n e ) = > {
console . log ( 'status:' , online ) ;
} ; * /
2020-02-06 22:43:07 +07:00
delete this . gettingNetworkers [ getKey ] ;
2020-12-11 04:06:16 +02:00
networkers . unshift ( networker ) ;
return networker ;
2020-02-06 22:43:07 +07:00
} ) ;
}
// mtpInvokeApi
2020-10-07 16:57:33 +03:00
public invokeApi < T extends keyof MethodDeclMap > ( method : T , params : MethodDeclMap [ T ] [ 'req' ] = { } , options : InvokeApiOptions = { } ) : CancellablePromise < MethodDeclMap [ T ] [ "res" ] > {
2020-02-17 19:18:06 +07:00
///////this.log('Invoke api', method, params, options);
2020-09-29 05:03:26 +03:00
2020-10-26 02:09:42 +02:00
/ * i f ( ! t h i s . l o l ) {
networkerFactory . updatesProcessor ( { _ : 'new_session_created' } , true ) ;
this . lol = true ;
} * /
2020-10-07 16:57:33 +03:00
const deferred = deferredPromise < MethodDeclMap [ T ] [ 'res' ] > ( ) ;
2020-09-29 05:03:26 +03:00
2020-12-24 02:59:44 +02:00
let afterMessageIdTemp = options . afterMessageId ;
2020-12-11 04:06:16 +02:00
if ( afterMessageIdTemp ) {
2020-10-15 19:54:54 +03:00
deferred . finally ( ( ) = > {
2020-12-11 04:06:16 +02:00
delete this . afterMessageTempIds [ afterMessageIdTemp ] ;
2020-10-15 19:54:54 +03:00
} ) ;
}
2020-10-07 16:57:33 +03:00
if ( MOUNT_CLASS_TO ) {
deferred . finally ( ( ) = > {
clearInterval ( interval ) ;
} ) ;
const startTime = Date . now ( ) ;
const interval = MOUNT_CLASS_TO . setInterval ( ( ) = > {
this . log . error ( 'Request is still processing:' , method , params , options , 'time:' , ( Date . now ( ) - startTime ) / 1000 ) ;
//this.cachedUploadNetworkers[2].requestMessageStatus();
2020-10-26 02:09:42 +02:00
} , 5 e3 ) ;
2020-10-07 16:57:33 +03:00
}
const rejectPromise = ( error : ApiError ) = > {
if ( ! error ) {
error = { type : 'ERROR_EMPTY' } ;
} else if ( ! isObject ( error ) ) {
error = { message : error } ;
}
deferred . reject ( error ) ;
2021-02-04 02:30:23 +02:00
if ( error . code === 401 && error . type === 'SESSION_REVOKED' ) {
2020-10-07 16:57:33 +03:00
this . logOut ( ) ;
}
if ( options . ignoreErrors ) {
return ;
}
2020-02-06 22:43:07 +07:00
2021-02-04 02:30:23 +02:00
if ( error . code === 406 ) {
2020-10-07 16:57:33 +03:00
error . handled = true ;
}
2020-02-06 22:43:07 +07:00
2020-10-07 16:57:33 +03:00
if ( ! options . noErrorBox ) {
error . input = method ;
error . stack = stack || ( error . originalError && error . originalError . stack ) || error . stack || ( new Error ( ) ) . stack ;
setTimeout ( ( ) = > {
if ( ! error . handled ) {
2021-02-04 02:30:23 +02:00
if ( error . code === 401 ) {
2020-10-07 16:57:33 +03:00
this . logOut ( ) ;
} else {
// ErrorService.show({error: error}); // WARNING
}
error . handled = true ;
}
} , 100 ) ;
}
} ;
2020-12-11 04:06:16 +02:00
let dcId : number ;
2020-10-07 16:57:33 +03:00
2020-11-16 00:34:48 +02:00
let cachedNetworker : MTPNetworker ;
let stack = ( new Error ( ) ) . stack || 'empty stack' ;
const performRequest = ( networker : MTPNetworker ) = > {
2020-12-11 04:06:16 +02:00
if ( afterMessageIdTemp ) {
options . afterMessageId = this . afterMessageTempIds [ afterMessageIdTemp ] ;
2020-10-15 19:54:54 +03:00
}
const promise = ( cachedNetworker = networker ) . wrapApiCall ( method , params , options ) ;
2020-12-11 04:06:16 +02:00
if ( options . prepareTempMessageId ) {
this . afterMessageTempIds [ options . prepareTempMessageId ] = ( options as MTMessage ) . messageId ;
2020-10-15 19:54:54 +03:00
}
return promise . then ( deferred . resolve , ( error : ApiError ) = > {
2020-10-07 16:57:33 +03:00
//if(!options.ignoreErrors) {
2021-02-04 02:30:23 +02:00
if ( error . type !== 'FILE_REFERENCE_EXPIRED' && error . type !== 'MSG_WAIT_FAILED' ) {
2020-12-11 04:06:16 +02:00
this . log . error ( 'Error' , error . code , error . type , this . baseDcId , dcId , method , params ) ;
2020-10-07 16:57:33 +03:00
}
2021-02-04 02:30:23 +02:00
if ( error . code === 401 && this . baseDcId === dcId ) {
if ( error . type !== 'SESSION_PASSWORD_NEEDED' ) {
2021-01-18 22:34:41 +04:00
sessionStorage . remove ( 'dc' )
sessionStorage . remove ( 'user_auth' ) ; // ! возможно тут вообще не нужно это делать, но нужно проверить случай с USER_DEACTIVATED (https://core.telegram.org/api/errors)
2020-11-16 00:34:48 +02:00
//this.telegramMeNotify(false);
}
2020-10-07 16:57:33 +03:00
rejectPromise ( error ) ;
2021-02-04 02:30:23 +02:00
} else if ( error . code === 401 && this . baseDcId && dcId !== this . baseDcId ) {
2020-12-11 04:06:16 +02:00
if ( this . cachedExportPromise [ dcId ] === undefined ) {
2020-11-16 00:34:48 +02:00
const promise = new Promise ( ( exportResolve , exportReject ) = > {
2020-12-11 04:06:16 +02:00
this . invokeApi ( 'auth.exportAuthorization' , { dc_id : dcId } , { noErrorBox : true } ) . then ( ( exportedAuth ) = > {
2020-10-07 16:57:33 +03:00
this . invokeApi ( 'auth.importAuthorization' , {
id : exportedAuth.id ,
bytes : exportedAuth.bytes
2020-12-11 04:06:16 +02:00
} , { dcId , noErrorBox : true } ) . then ( exportResolve , exportReject ) ;
2020-10-07 16:57:33 +03:00
} , exportReject ) ;
} ) ;
2020-12-11 04:06:16 +02:00
this . cachedExportPromise [ dcId ] = promise ;
2020-09-29 05:03:26 +03:00
}
2020-02-06 22:43:07 +07:00
2020-12-11 04:06:16 +02:00
this . cachedExportPromise [ dcId ] . then ( ( ) = > {
2020-10-07 16:57:33 +03:00
//(cachedNetworker = networker).wrapApiCall(method, params, options).then(deferred.resolve, rejectPromise);
this . invokeApi ( method , params , options ) . then ( deferred . resolve , rejectPromise ) ;
} , rejectPromise ) ;
2021-02-04 02:30:23 +02:00
} else if ( error . code === 303 ) {
2021-02-04 02:11:34 +02:00
const newDcId = + error . type . match ( /^(PHONE_MIGRATE_|NETWORK_MIGRATE_|USER_MIGRATE_|FILE_MIGRATE_)(\d+)/ ) [ 2 ] ;
2021-02-04 02:30:23 +02:00
if ( newDcId !== dcId ) {
2020-12-11 04:06:16 +02:00
if ( options . dcId ) {
options . dcId = newDcId ;
2020-10-07 16:57:33 +03:00
} else {
2020-12-11 04:06:16 +02:00
this . setBaseDcId ( newDcId ) ;
2020-02-06 22:43:07 +07:00
}
2020-12-11 04:06:16 +02:00
this . getNetworker ( newDcId , options ) . then ( ( networker ) = > {
2020-10-07 16:57:33 +03:00
networker . wrapApiCall ( method , params , options ) . then ( deferred . resolve , rejectPromise ) ;
2020-02-06 22:43:07 +07:00
} , rejectPromise ) ;
2020-10-07 16:57:33 +03:00
}
2021-02-04 02:30:23 +02:00
} else if ( ! options . rawError && error . code === 420 ) {
2020-11-16 00:34:48 +02:00
const waitTime = + error . type . match ( /^FLOOD_WAIT_(\d+)/ ) [ 1 ] || 10 ;
2020-10-07 16:57:33 +03:00
if ( waitTime > ( options . floodMaxTimeout !== undefined ? options.floodMaxTimeout : 60 ) ) {
return rejectPromise ( error ) ;
}
setTimeout ( ( ) = > {
performRequest ( cachedNetworker ) ;
} , waitTime /* (waitTime + 5) */ * 1000 ) ; // 03.02.2020
2021-02-04 02:30:23 +02:00
} else if ( ! options . rawError && error . code === 500 ) {
2020-12-24 02:59:44 +02:00
if ( error . type === 'MSG_WAIT_FAILED' ) {
afterMessageIdTemp = undefined ;
delete options . afterMessageId ;
delete this . afterMessageTempIds [ options . prepareTempMessageId ] ;
performRequest ( cachedNetworker ) ;
return ;
}
2020-11-16 00:34:48 +02:00
const now = Date . now ( ) ;
2020-10-07 16:57:33 +03:00
if ( options . stopTime ) {
if ( now >= options . stopTime ) {
2020-02-06 22:43:07 +07:00
return rejectPromise ( error ) ;
}
}
2020-10-07 16:57:33 +03:00
options . waitTime = options . waitTime ? Math . min ( 60 , options . waitTime * 1.5 ) : 1 ;
setTimeout ( ( ) = > {
performRequest ( cachedNetworker ) ;
} , options . waitTime * 1000 ) ;
} else {
rejectPromise ( error ) ;
}
} ) ;
}
2020-12-11 04:06:16 +02:00
if ( dcId = ( options . dcId || this . baseDcId ) ) {
this . getNetworker ( dcId , options ) . then ( performRequest , rejectPromise ) ;
2020-10-07 16:57:33 +03:00
} else {
2021-01-18 22:34:41 +04:00
sessionStorage . get ( 'dc' ) . then ( ( baseDcId ) = > {
2020-12-11 04:06:16 +02:00
this . getNetworker ( this . baseDcId = dcId = baseDcId || App . baseDcId , options ) . then ( performRequest , rejectPromise ) ;
2020-10-07 16:57:33 +03:00
} ) ;
}
return deferred ;
2020-02-06 22:43:07 +07:00
}
}
2020-10-07 16:57:33 +03:00
const apiManager = new ApiManager ( ) ;
MOUNT_CLASS_TO && ( MOUNT_CLASS_TO . apiManager = apiManager ) ;
export default apiManager ;