Refactor sessions: Switch to Webogram style, clean up legacy artifacts
This commit is contained in:
parent
bdbd676a9b
commit
fe5b45e4f6
@ -4,7 +4,7 @@ import React, { withGlobal } from './lib/teact/teactn';
|
|||||||
import { GlobalActions, GlobalState } from './global/types';
|
import { GlobalActions, GlobalState } from './global/types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
GRAMJS_SESSION_ID_KEY, INACTIVE_MARKER, LEGACY_SESSION_KEY, PAGE_TITLE,
|
LEGACY_SESSION_KEY, INACTIVE_MARKER, SESSION_USER_KEY, PAGE_TITLE,
|
||||||
} from './config';
|
} from './config';
|
||||||
import { pick } from './util/iteratees';
|
import { pick } from './util/iteratees';
|
||||||
import { updateSizes } from './util/windowSize';
|
import { updateSizes } from './util/windowSize';
|
||||||
@ -55,10 +55,9 @@ const App: FC<StateProps & DispatchProps> = ({ authState, disconnect }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasSession = localStorage.getItem(GRAMJS_SESSION_ID_KEY);
|
const hasSession = localStorage.getItem(SESSION_USER_KEY) || localStorage.getItem(LEGACY_SESSION_KEY);
|
||||||
const hasLegacySession = localStorage.getItem(LEGACY_SESSION_KEY);
|
|
||||||
|
|
||||||
return (hasSession || hasLegacySession) ? renderMain() : <Auth />;
|
return hasSession ? renderMain() : <Auth />;
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderMain() {
|
function renderMain() {
|
||||||
|
|||||||
@ -105,12 +105,8 @@ export function onAuthError(err: Error) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onAuthReady(sessionId: string, sessionJson: string) {
|
export function onAuthReady() {
|
||||||
onUpdate({
|
onUpdate(buildAuthStateUpdate('authorizationStateReady'));
|
||||||
...buildAuthStateUpdate('authorizationStateReady'),
|
|
||||||
sessionId,
|
|
||||||
sessionJson,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onCurrentUserUpdate(currentUser: ApiUser) {
|
export function onCurrentUserUpdate(currentUser: ApiUser) {
|
||||||
|
|||||||
@ -4,7 +4,9 @@ import {
|
|||||||
import { Logger as GramJsLogger } from '../../../lib/gramjs/extensions/index';
|
import { Logger as GramJsLogger } from '../../../lib/gramjs/extensions/index';
|
||||||
import { TwoFaParams } from '../../../lib/gramjs/client/2fa';
|
import { TwoFaParams } from '../../../lib/gramjs/client/2fa';
|
||||||
|
|
||||||
import { ApiMediaFormat, ApiOnProgress, OnApiUpdate } from '../../types';
|
import {
|
||||||
|
ApiMediaFormat, ApiOnProgress, ApiSessionData, OnApiUpdate,
|
||||||
|
} from '../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DEBUG, DEBUG_GRAMJS, UPLOAD_WORKERS, IS_TEST, APP_VERSION,
|
DEBUG, DEBUG_GRAMJS, UPLOAD_WORKERS, IS_TEST, APP_VERSION,
|
||||||
@ -30,7 +32,7 @@ let onUpdate: OnApiUpdate;
|
|||||||
let client: TelegramClient;
|
let client: TelegramClient;
|
||||||
let isConnected = false;
|
let isConnected = false;
|
||||||
|
|
||||||
export async function init(sessionInfo: string, _onUpdate: OnApiUpdate) {
|
export async function init(_onUpdate: OnApiUpdate, sessionData?: ApiSessionData) {
|
||||||
onUpdate = _onUpdate;
|
onUpdate = _onUpdate;
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@ -38,12 +40,8 @@ export async function init(sessionInfo: string, _onUpdate: OnApiUpdate) {
|
|||||||
console.log('>>> START INIT API');
|
console.log('>>> START INIT API');
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = IS_TEST
|
|
||||||
? new sessions.LocalStorageSession(sessionInfo)
|
|
||||||
: new sessions.IdbSession(sessionInfo);
|
|
||||||
|
|
||||||
client = new TelegramClient(
|
client = new TelegramClient(
|
||||||
session,
|
new sessions.CallbackSession(sessionData, onSessionUpdate),
|
||||||
process.env.TELEGRAM_T_API_ID,
|
process.env.TELEGRAM_T_API_ID,
|
||||||
process.env.TELEGRAM_T_API_HASH,
|
process.env.TELEGRAM_T_API_HASH,
|
||||||
{
|
{
|
||||||
@ -72,17 +70,14 @@ export async function init(sessionInfo: string, _onUpdate: OnApiUpdate) {
|
|||||||
onError: onAuthError,
|
onError: onAuthError,
|
||||||
});
|
});
|
||||||
|
|
||||||
const newSessionId = await session.save();
|
|
||||||
const sessionJson = JSON.stringify(session.getSessionData(true));
|
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('>>> FINISH INIT API');
|
console.log('>>> FINISH INIT API');
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('[GramJs/client] CONNECTED as ', newSessionId);
|
console.log('[GramJs/client] CONNECTED');
|
||||||
}
|
}
|
||||||
|
|
||||||
onAuthReady(newSessionId, sessionJson);
|
onAuthReady();
|
||||||
onUpdate({ '@type': 'updateApiReady' });
|
onUpdate({ '@type': 'updateApiReady' });
|
||||||
|
|
||||||
void fetchCurrentUser();
|
void fetchCurrentUser();
|
||||||
@ -109,6 +104,13 @@ export function getClient() {
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onSessionUpdate(sessionData: ApiSessionData) {
|
||||||
|
onUpdate({
|
||||||
|
'@type': 'updateSession',
|
||||||
|
sessionData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handleGramJsUpdate(update: any) {
|
function handleGramJsUpdate(update: any) {
|
||||||
if (update instanceof connection.UpdateConnectionState) {
|
if (update instanceof connection.UpdateConnectionState) {
|
||||||
isConnected = update.state === connection.UpdateConnectionState.connected;
|
isConnected = update.state === connection.UpdateConnectionState.connected;
|
||||||
@ -125,7 +127,7 @@ export async function invokeRequest<T extends GramJs.AnyRequest>(
|
|||||||
if (!isConnected) {
|
if (!isConnected) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.warn(`[GramJs/client] INVOKE ${request.className} ERROR: Client is not connected`);
|
console.warn(`[GramJs/client] INVOKE ERROR ${request.className}: Client is not connected`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import { ApiOnProgress, ApiUpdate, OnApiUpdate } from '../types';
|
import {
|
||||||
|
ApiOnProgress, ApiSessionData, ApiUpdate, OnApiUpdate,
|
||||||
|
} from '../types';
|
||||||
import { Methods, MethodArgs, MethodResponse } from './methods/types';
|
import { Methods, MethodArgs, MethodResponse } from './methods/types';
|
||||||
|
|
||||||
import { API_THROTTLE_RESET_UPDATES, API_UPDATE_THROTTLE } from '../../config';
|
import { API_THROTTLE_RESET_UPDATES, API_UPDATE_THROTTLE } from '../../config';
|
||||||
@ -16,7 +18,7 @@ import * as methods from './methods';
|
|||||||
|
|
||||||
let onUpdate: OnApiUpdate;
|
let onUpdate: OnApiUpdate;
|
||||||
|
|
||||||
export async function initApi(_onUpdate: OnApiUpdate, sessionInfo = '') {
|
export async function initApi(_onUpdate: OnApiUpdate, sessionData?: ApiSessionData) {
|
||||||
onUpdate = _onUpdate;
|
onUpdate = _onUpdate;
|
||||||
|
|
||||||
initUpdater(handleUpdate);
|
initUpdater(handleUpdate);
|
||||||
@ -28,7 +30,7 @@ export async function initApi(_onUpdate: OnApiUpdate, sessionInfo = '') {
|
|||||||
initManagement(handleUpdate);
|
initManagement(handleUpdate);
|
||||||
initTwoFaSettings(handleUpdate);
|
initTwoFaSettings(handleUpdate);
|
||||||
|
|
||||||
await initClient(sessionInfo, handleUpdate);
|
await initClient(handleUpdate, sessionData);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function callApi<T extends keyof Methods>(fnName: T, ...args: MethodArgs<T>): MethodResponse<T> {
|
export function callApi<T extends keyof Methods>(fnName: T, ...args: MethodArgs<T>): MethodResponse<T> {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import Worker from 'worker-loader!./worker';
|
import Worker from 'worker-loader!./worker';
|
||||||
|
|
||||||
import { ApiOnProgress, OnApiUpdate } from '../../types';
|
import { ApiOnProgress, ApiSessionData, OnApiUpdate } from '../../types';
|
||||||
import { Methods, MethodArgs, MethodResponse } from '../methods/types';
|
import { Methods, MethodArgs, MethodResponse } from '../methods/types';
|
||||||
import { WorkerMessageEvent, ThenArg, OriginRequest } from './types';
|
import { WorkerMessageEvent, ThenArg, OriginRequest } from './types';
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ const requestStatesByCallback = new Map<AnyToVoidFunction, RequestStates>();
|
|||||||
|
|
||||||
// TODO Re-use `util/WorkerConnector.ts`
|
// TODO Re-use `util/WorkerConnector.ts`
|
||||||
|
|
||||||
export function initApi(onUpdate: OnApiUpdate, sessionInfo = '') {
|
export function initApi(onUpdate: OnApiUpdate, sessionData?: ApiSessionData) {
|
||||||
if (!worker) {
|
if (!worker) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
@ -33,7 +33,7 @@ export function initApi(onUpdate: OnApiUpdate, sessionInfo = '') {
|
|||||||
|
|
||||||
return makeRequest({
|
return makeRequest({
|
||||||
type: 'initApi',
|
type: 'initApi',
|
||||||
args: [sessionInfo],
|
args: [sessionData],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ApiUpdate } from '../../types';
|
import { ApiSessionData, ApiUpdate } from '../../types';
|
||||||
import { Methods, MethodArgs, MethodResponse } from '../methods/types';
|
import { Methods, MethodArgs, MethodResponse } from '../methods/types';
|
||||||
|
|
||||||
export type ThenArg<T> = T extends Promise<infer U> ? U : T;
|
export type ThenArg<T> = T extends Promise<infer U> ? U : T;
|
||||||
@ -27,7 +27,7 @@ export interface WorkerMessageEvent {
|
|||||||
export type OriginRequest = {
|
export type OriginRequest = {
|
||||||
type: 'initApi';
|
type: 'initApi';
|
||||||
messageId?: string;
|
messageId?: string;
|
||||||
args: [string];
|
args: [ApiSessionData | undefined];
|
||||||
} | {
|
} | {
|
||||||
type: 'callMethod';
|
type: 'callMethod';
|
||||||
messageId?: string;
|
messageId?: string;
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import { ApiDocument } from './messages';
|
|
||||||
|
|
||||||
export * from './users';
|
export * from './users';
|
||||||
export * from './chats';
|
export * from './chats';
|
||||||
export * from './messages';
|
export * from './messages';
|
||||||
@ -7,52 +5,4 @@ export * from './updates';
|
|||||||
export * from './media';
|
export * from './media';
|
||||||
export * from './payments';
|
export * from './payments';
|
||||||
export * from './settings';
|
export * from './settings';
|
||||||
|
export * from './misc';
|
||||||
export interface ApiOnProgress {
|
|
||||||
(
|
|
||||||
progress: number, // Float between 0 and 1.
|
|
||||||
...args: any[]
|
|
||||||
): void;
|
|
||||||
|
|
||||||
isCanceled?: boolean;
|
|
||||||
acceptsBuffer?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApiAttachment {
|
|
||||||
blobUrl: string;
|
|
||||||
filename: string;
|
|
||||||
mimeType: string;
|
|
||||||
size: number;
|
|
||||||
quick?: {
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
duration?: number;
|
|
||||||
};
|
|
||||||
voice?: {
|
|
||||||
duration: number;
|
|
||||||
waveform: number[];
|
|
||||||
};
|
|
||||||
previewBlobUrl?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApiWallpaper {
|
|
||||||
slug: string;
|
|
||||||
document: ApiDocument;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ApiSession {
|
|
||||||
hash: string;
|
|
||||||
isCurrent: boolean;
|
|
||||||
isOfficialApp: boolean;
|
|
||||||
isPasswordPending: boolean;
|
|
||||||
deviceModel: string;
|
|
||||||
platform: string;
|
|
||||||
systemVersion: string;
|
|
||||||
appName: string;
|
|
||||||
appVersion: string;
|
|
||||||
dateCreated: number;
|
|
||||||
dateActive: number;
|
|
||||||
ip: string;
|
|
||||||
country: string;
|
|
||||||
region: string;
|
|
||||||
}
|
|
||||||
|
|||||||
56
src/api/types/misc.ts
Normal file
56
src/api/types/misc.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { ApiDocument } from './messages';
|
||||||
|
|
||||||
|
export interface ApiOnProgress {
|
||||||
|
(
|
||||||
|
progress: number, // Float between 0 and 1.
|
||||||
|
...args: any[]
|
||||||
|
): void;
|
||||||
|
|
||||||
|
isCanceled?: boolean;
|
||||||
|
acceptsBuffer?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiAttachment {
|
||||||
|
blobUrl: string;
|
||||||
|
filename: string;
|
||||||
|
mimeType: string;
|
||||||
|
size: number;
|
||||||
|
quick?: {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
duration?: number;
|
||||||
|
};
|
||||||
|
voice?: {
|
||||||
|
duration: number;
|
||||||
|
waveform: number[];
|
||||||
|
};
|
||||||
|
previewBlobUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiWallpaper {
|
||||||
|
slug: string;
|
||||||
|
document: ApiDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiSession {
|
||||||
|
hash: string;
|
||||||
|
isCurrent: boolean;
|
||||||
|
isOfficialApp: boolean;
|
||||||
|
isPasswordPending: boolean;
|
||||||
|
deviceModel: string;
|
||||||
|
platform: string;
|
||||||
|
systemVersion: string;
|
||||||
|
appName: string;
|
||||||
|
appVersion: string;
|
||||||
|
dateCreated: number;
|
||||||
|
dateActive: number;
|
||||||
|
ip: string;
|
||||||
|
country: string;
|
||||||
|
region: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiSessionData {
|
||||||
|
mainDcId: number;
|
||||||
|
keys: Record<number, string | number[]>;
|
||||||
|
hashes: Record<number, string | number[]>;
|
||||||
|
}
|
||||||
@ -9,6 +9,7 @@ import {
|
|||||||
ApiMessage, ApiPhoto, ApiPoll, ApiStickerSet, ApiThreadInfo,
|
ApiMessage, ApiPhoto, ApiPoll, ApiStickerSet, ApiThreadInfo,
|
||||||
} from './messages';
|
} from './messages';
|
||||||
import { ApiUser, ApiUserFullInfo, ApiUserStatus } from './users';
|
import { ApiUser, ApiUserFullInfo, ApiUserStatus } from './users';
|
||||||
|
import { ApiSessionData } from './misc';
|
||||||
|
|
||||||
export type ApiUpdateReady = {
|
export type ApiUpdateReady = {
|
||||||
'@type': 'updateApiReady';
|
'@type': 'updateApiReady';
|
||||||
@ -35,13 +36,16 @@ export type ApiUpdateConnectionStateType = (
|
|||||||
export type ApiUpdateAuthorizationState = {
|
export type ApiUpdateAuthorizationState = {
|
||||||
'@type': 'updateAuthorizationState';
|
'@type': 'updateAuthorizationState';
|
||||||
authorizationState: ApiUpdateAuthorizationStateType;
|
authorizationState: ApiUpdateAuthorizationStateType;
|
||||||
sessionId?: string;
|
|
||||||
sessionJson?: string;
|
|
||||||
isCodeViaApp?: boolean;
|
isCodeViaApp?: boolean;
|
||||||
hint?: string;
|
hint?: string;
|
||||||
qrCode?: { token: string; expires: number };
|
qrCode?: { token: string; expires: number };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ApiUpdateSession = {
|
||||||
|
'@type': 'updateSession';
|
||||||
|
sessionData?: ApiSessionData;
|
||||||
|
};
|
||||||
|
|
||||||
export type ApiUpdateAuthorizationError = {
|
export type ApiUpdateAuthorizationError = {
|
||||||
'@type': 'updateAuthorizationError';
|
'@type': 'updateAuthorizationError';
|
||||||
message: string;
|
message: string;
|
||||||
@ -363,7 +367,7 @@ export type ApiUpdatePrivacy = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ApiUpdate = (
|
export type ApiUpdate = (
|
||||||
ApiUpdateReady |
|
ApiUpdateReady | ApiUpdateSession |
|
||||||
ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser |
|
ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser |
|
||||||
ApiUpdateChat | ApiUpdateChatInbox | ApiUpdateChatTypingStatus | ApiUpdateChatFullInfo | ApiUpdatePinnedChatIds |
|
ApiUpdateChat | ApiUpdateChatInbox | ApiUpdateChatTypingStatus | ApiUpdateChatFullInfo | ApiUpdatePinnedChatIds |
|
||||||
ApiUpdateChatMembers | ApiUpdateChatJoin | ApiUpdateChatLeave | ApiUpdateChatPinned | ApiUpdatePinnedMessageIds |
|
ApiUpdateChatMembers | ApiUpdateChatJoin | ApiUpdateChatLeave | ApiUpdateChatPinned | ApiUpdatePinnedMessageIds |
|
||||||
|
|||||||
@ -15,8 +15,8 @@ export const IS_PERF = process.env.APP_ENV === 'perf';
|
|||||||
export const DEBUG_ALERT_MSG = 'Shoot!\nSomething went wrong, please see the error details in Dev Tools Console.';
|
export const DEBUG_ALERT_MSG = 'Shoot!\nSomething went wrong, please see the error details in Dev Tools Console.';
|
||||||
export const DEBUG_GRAMJS = false;
|
export const DEBUG_GRAMJS = false;
|
||||||
|
|
||||||
export const GRAMJS_SESSION_ID_KEY = 'GramJs:sessionId';
|
export const SESSION_USER_KEY = 'user_auth';
|
||||||
export const LEGACY_SESSION_KEY = 'user_auth';
|
export const LEGACY_SESSION_KEY = 'GramJs:sessionId';
|
||||||
|
|
||||||
export const GLOBAL_STATE_CACHE_DISABLED = false;
|
export const GLOBAL_STATE_CACHE_DISABLED = false;
|
||||||
export const GLOBAL_STATE_CACHE_KEY = 'tt-global-state';
|
export const GLOBAL_STATE_CACHE_KEY = 'tt-global-state';
|
||||||
|
|||||||
@ -11,8 +11,8 @@ import {
|
|||||||
GLOBAL_STATE_CACHE_DISABLED,
|
GLOBAL_STATE_CACHE_DISABLED,
|
||||||
GLOBAL_STATE_CACHE_KEY,
|
GLOBAL_STATE_CACHE_KEY,
|
||||||
GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT,
|
GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT,
|
||||||
GRAMJS_SESSION_ID_KEY,
|
LEGACY_SESSION_KEY,
|
||||||
MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN, GLOBAL_STATE_CACHE_USER_LIST_LIMIT,
|
MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN, GLOBAL_STATE_CACHE_USER_LIST_LIMIT, SESSION_USER_KEY,
|
||||||
} from '../config';
|
} from '../config';
|
||||||
import { IS_MOBILE_SCREEN } from '../util/environment';
|
import { IS_MOBILE_SCREEN } from '../util/environment';
|
||||||
import { pick } from '../util/iteratees';
|
import { pick } from '../util/iteratees';
|
||||||
@ -44,8 +44,8 @@ export function initCache() {
|
|||||||
|
|
||||||
export function loadCache(initialState: GlobalState) {
|
export function loadCache(initialState: GlobalState) {
|
||||||
if (!GLOBAL_STATE_CACHE_DISABLED) {
|
if (!GLOBAL_STATE_CACHE_DISABLED) {
|
||||||
const hasActiveSession = localStorage.getItem(GRAMJS_SESSION_ID_KEY);
|
const hasSession = localStorage.getItem(SESSION_USER_KEY) || localStorage.getItem(LEGACY_SESSION_KEY);
|
||||||
if (hasActiveSession) {
|
if (hasSession) {
|
||||||
isAllowed = true;
|
isAllowed = true;
|
||||||
addCallback(updateCacheThrottled);
|
addCallback(updateCacheThrottled);
|
||||||
return readCache(initialState);
|
return readCache(initialState);
|
||||||
|
|||||||
108
src/lib/gramjs/sessions/CallbackSession.js
Normal file
108
src/lib/gramjs/sessions/CallbackSession.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
const MemorySession = require('./Memory');
|
||||||
|
const AuthKey = require('../crypto/AuthKey');
|
||||||
|
const utils = require('../Utils');
|
||||||
|
|
||||||
|
class CallbackSession extends MemorySession {
|
||||||
|
constructor(sessionData, callback) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._sessionData = sessionData;
|
||||||
|
this._callback = callback;
|
||||||
|
|
||||||
|
this._authKeys = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
get authKey() {
|
||||||
|
throw new Error('Not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
set authKey(value) {
|
||||||
|
throw new Error('Not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
if (!this._sessionData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
mainDcId,
|
||||||
|
keys,
|
||||||
|
hashes,
|
||||||
|
} = this._sessionData;
|
||||||
|
const {
|
||||||
|
ipAddress,
|
||||||
|
port,
|
||||||
|
} = utils.getDC(mainDcId);
|
||||||
|
|
||||||
|
this.setDC(mainDcId, ipAddress, port, true);
|
||||||
|
|
||||||
|
await Promise.all(Object.keys(keys)
|
||||||
|
.map(async (dcId) => {
|
||||||
|
const key = typeof keys[dcId] === 'string'
|
||||||
|
? Buffer.from(keys[dcId], 'hex')
|
||||||
|
: Buffer.from(keys[dcId]);
|
||||||
|
|
||||||
|
if (hashes[dcId]) {
|
||||||
|
const hash = typeof hashes[dcId] === 'string'
|
||||||
|
? Buffer.from(hashes[dcId], 'hex')
|
||||||
|
: Buffer.from(hashes[dcId]);
|
||||||
|
|
||||||
|
this._authKeys[dcId] = new AuthKey(key, hash);
|
||||||
|
} else {
|
||||||
|
this._authKeys[dcId] = new AuthKey();
|
||||||
|
await this._authKeys[dcId].setKey(key, true);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
setDC(dcId, serverAddress, port, skipOnUpdate = false) {
|
||||||
|
this._dcId = dcId;
|
||||||
|
this._serverAddress = serverAddress;
|
||||||
|
this._port = port;
|
||||||
|
|
||||||
|
delete this._authKeys[dcId];
|
||||||
|
|
||||||
|
if (!skipOnUpdate) {
|
||||||
|
void this._onUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAuthKey(dcId = this._dcId) {
|
||||||
|
return this._authKeys[dcId];
|
||||||
|
}
|
||||||
|
|
||||||
|
setAuthKey(authKey, dcId = this._dcId) {
|
||||||
|
this._authKeys[dcId] = authKey;
|
||||||
|
|
||||||
|
void this._onUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
getSessionData() {
|
||||||
|
const sessionData = {
|
||||||
|
mainDcId: this._dcId,
|
||||||
|
keys: {},
|
||||||
|
hashes: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
Object
|
||||||
|
.keys(this._authKeys)
|
||||||
|
.forEach((dcId) => {
|
||||||
|
const authKey = this._authKeys[dcId];
|
||||||
|
sessionData.keys[dcId] = authKey._key.toString('hex');
|
||||||
|
sessionData.hashes[dcId] = authKey._hash.toString('hex');
|
||||||
|
});
|
||||||
|
|
||||||
|
return sessionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
_onUpdate() {
|
||||||
|
this._callback(this.getSessionData());
|
||||||
|
}
|
||||||
|
|
||||||
|
delete() {
|
||||||
|
this._callback(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CallbackSession;
|
||||||
@ -3,6 +3,7 @@ const StringSession = require('./StringSession');
|
|||||||
const CacheApiSession = require('./CacheApiSession');
|
const CacheApiSession = require('./CacheApiSession');
|
||||||
const LocalStorageSession = require('./LocalStorageSession');
|
const LocalStorageSession = require('./LocalStorageSession');
|
||||||
const IdbSession = require('./IdbSession');
|
const IdbSession = require('./IdbSession');
|
||||||
|
const CallbackSession = require('./CallbackSession');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Memory,
|
Memory,
|
||||||
@ -10,4 +11,5 @@ module.exports = {
|
|||||||
CacheApiSession,
|
CacheApiSession,
|
||||||
LocalStorageSession,
|
LocalStorageSession,
|
||||||
IdbSession,
|
IdbSession,
|
||||||
|
CallbackSession,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,34 +7,30 @@ import { GlobalState } from '../../../global/types';
|
|||||||
import {
|
import {
|
||||||
LANG_CACHE_NAME,
|
LANG_CACHE_NAME,
|
||||||
CUSTOM_BG_CACHE_NAME,
|
CUSTOM_BG_CACHE_NAME,
|
||||||
GRAMJS_SESSION_ID_KEY,
|
|
||||||
MEDIA_CACHE_NAME,
|
MEDIA_CACHE_NAME,
|
||||||
MEDIA_CACHE_NAME_AVATARS,
|
MEDIA_CACHE_NAME_AVATARS,
|
||||||
MEDIA_PROGRESSIVE_CACHE_NAME,
|
MEDIA_PROGRESSIVE_CACHE_NAME,
|
||||||
LEGACY_SESSION_KEY,
|
|
||||||
} from '../../../config';
|
} from '../../../config';
|
||||||
import { initApi, callApi } from '../../../api/gramjs';
|
import { initApi, callApi } from '../../../api/gramjs';
|
||||||
import { unsubscribe } from '../../../util/notifications';
|
import { unsubscribe } from '../../../util/notifications';
|
||||||
import * as cacheApi from '../../../util/cacheApi';
|
import * as cacheApi from '../../../util/cacheApi';
|
||||||
import { HistoryWrapper } from '../../../util/history';
|
import { HistoryWrapper } from '../../../util/history';
|
||||||
import { updateAppBadge } from '../../../util/appBadge';
|
import { updateAppBadge } from '../../../util/appBadge';
|
||||||
|
import {
|
||||||
|
storeSession,
|
||||||
|
loadStoredSession,
|
||||||
|
clearStoredSession,
|
||||||
|
importLegacySession,
|
||||||
|
clearLegacySessions,
|
||||||
|
} from './sessions';
|
||||||
|
|
||||||
addReducer('initApi', (global: GlobalState, actions) => {
|
addReducer('initApi', (global: GlobalState, actions) => {
|
||||||
let sessionInfo = localStorage.getItem(GRAMJS_SESSION_ID_KEY) || undefined;
|
(async () => {
|
||||||
|
await importLegacySession();
|
||||||
|
void clearLegacySessions();
|
||||||
|
|
||||||
if (!sessionInfo) {
|
void initApi(actions.apiUpdate, loadStoredSession());
|
||||||
const legacySessionJson = localStorage.getItem(LEGACY_SESSION_KEY);
|
})();
|
||||||
if (legacySessionJson) {
|
|
||||||
const { dcID: legacySessionMainDc } = JSON.parse(legacySessionJson);
|
|
||||||
const legacySessionMainKeyRaw = localStorage.getItem(`dc${legacySessionMainDc}_auth_key`);
|
|
||||||
if (legacySessionMainKeyRaw) {
|
|
||||||
const legacySessionMainDcKey = legacySessionMainKeyRaw.replace(/"/g, '');
|
|
||||||
sessionInfo = `session:${legacySessionMainDc}:${legacySessionMainDcKey}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void initApi(actions.apiUpdate, sessionInfo);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
addReducer('setAuthPhoneNumber', (global, actions, payload) => {
|
addReducer('setAuthPhoneNumber', (global, actions, payload) => {
|
||||||
@ -116,10 +112,7 @@ addReducer('gotToAuthQrCode', (global) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
addReducer('saveSession', (global, actions, payload) => {
|
addReducer('saveSession', (global, actions, payload) => {
|
||||||
const { sessionId, sessionJson } = payload!;
|
storeSession(payload.sessionData, global.currentUserId);
|
||||||
localStorage.setItem(GRAMJS_SESSION_ID_KEY, sessionId);
|
|
||||||
|
|
||||||
exportLegacySession(sessionJson, global.currentUserId!);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
addReducer('signOut', () => {
|
addReducer('signOut', () => {
|
||||||
@ -132,20 +125,21 @@ addReducer('signOut', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
addReducer('reset', () => {
|
addReducer('reset', () => {
|
||||||
localStorage.removeItem(GRAMJS_SESSION_ID_KEY);
|
clearStoredSession();
|
||||||
clearLegacySession();
|
|
||||||
|
|
||||||
cacheApi.clear(MEDIA_CACHE_NAME);
|
void cacheApi.clear(MEDIA_CACHE_NAME);
|
||||||
cacheApi.clear(MEDIA_CACHE_NAME_AVATARS);
|
void cacheApi.clear(MEDIA_CACHE_NAME_AVATARS);
|
||||||
cacheApi.clear(MEDIA_PROGRESSIVE_CACHE_NAME);
|
void cacheApi.clear(MEDIA_PROGRESSIVE_CACHE_NAME);
|
||||||
cacheApi.clear(CUSTOM_BG_CACHE_NAME);
|
void cacheApi.clear(CUSTOM_BG_CACHE_NAME);
|
||||||
|
|
||||||
const langCachePrefix = LANG_CACHE_NAME.replace(/\d+$/, '');
|
const langCachePrefix = LANG_CACHE_NAME.replace(/\d+$/, '');
|
||||||
const langCacheVersion = (LANG_CACHE_NAME.match(/\d+$/) || [0])[0];
|
const langCacheVersion = (LANG_CACHE_NAME.match(/\d+$/) || [0])[0];
|
||||||
for (let i = 0; i < langCacheVersion; i++) {
|
for (let i = 0; i < langCacheVersion; i++) {
|
||||||
cacheApi.clear(`${langCachePrefix}${i === 0 ? '' : i}`);
|
void cacheApi.clear(`${langCachePrefix}${i === 0 ? '' : i}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clearLegacySessions();
|
||||||
|
|
||||||
updateAppBadge(0);
|
updateAppBadge(0);
|
||||||
|
|
||||||
getDispatch().init();
|
getDispatch().init();
|
||||||
@ -188,23 +182,3 @@ addReducer('deleteDeviceToken', (global) => {
|
|||||||
delete newGlobal.push;
|
delete newGlobal.push;
|
||||||
setGlobal(newGlobal);
|
setGlobal(newGlobal);
|
||||||
});
|
});
|
||||||
|
|
||||||
function exportLegacySession(sessionJson: string, currentUserId: number) {
|
|
||||||
const { mainDcId, keys } = JSON.parse(sessionJson);
|
|
||||||
const legacySession = { dcID: mainDcId, id: currentUserId };
|
|
||||||
localStorage.setItem(LEGACY_SESSION_KEY, JSON.stringify(legacySession));
|
|
||||||
localStorage.setItem('dc', mainDcId);
|
|
||||||
Object.keys(keys).forEach((dcId) => {
|
|
||||||
localStorage.setItem(`dc${dcId}_auth_key`, `"${keys[dcId]}"`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearLegacySession() {
|
|
||||||
localStorage.removeItem('dc5_auth_key');
|
|
||||||
localStorage.removeItem('dc4_auth_key');
|
|
||||||
localStorage.removeItem('dc3_auth_key');
|
|
||||||
localStorage.removeItem('dc2_auth_key');
|
|
||||||
localStorage.removeItem('dc1_auth_key');
|
|
||||||
localStorage.removeItem('dc');
|
|
||||||
localStorage.removeItem(LEGACY_SESSION_KEY);
|
|
||||||
}
|
|
||||||
|
|||||||
100
src/modules/actions/api/sessions.ts
Normal file
100
src/modules/actions/api/sessions.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import * as idb from 'idb-keyval';
|
||||||
|
|
||||||
|
import { ApiSessionData } from '../../../api/types';
|
||||||
|
|
||||||
|
import { DEBUG, LEGACY_SESSION_KEY, SESSION_USER_KEY } from '../../../config';
|
||||||
|
import * as cacheApi from '../../../util/cacheApi';
|
||||||
|
|
||||||
|
const DC_IDS = [1, 2, 3, 4, 5];
|
||||||
|
|
||||||
|
export function storeSession(sessionData: ApiSessionData, currentUserId?: number) {
|
||||||
|
const { mainDcId, keys, hashes } = sessionData;
|
||||||
|
|
||||||
|
localStorage.setItem(SESSION_USER_KEY, JSON.stringify({ dcID: mainDcId, id: currentUserId }));
|
||||||
|
localStorage.setItem('dc', String(mainDcId));
|
||||||
|
Object.keys(keys).map(Number).forEach((dcId) => {
|
||||||
|
localStorage.setItem(`dc${dcId}_auth_key`, JSON.stringify(keys[dcId]));
|
||||||
|
});
|
||||||
|
Object.keys(hashes).map(Number).forEach((dcId) => {
|
||||||
|
localStorage.setItem(`dc${dcId}_hash`, JSON.stringify(hashes[dcId]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearStoredSession() {
|
||||||
|
[
|
||||||
|
SESSION_USER_KEY,
|
||||||
|
'dc',
|
||||||
|
...DC_IDS.map((dcId) => `dc${dcId}_auth_key`),
|
||||||
|
...DC_IDS.map((dcId) => `dc${dcId}_hash`),
|
||||||
|
].forEach((key) => {
|
||||||
|
localStorage.removeItem(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadStoredSession(): ApiSessionData | undefined {
|
||||||
|
const legacySessionJson = localStorage.getItem(SESSION_USER_KEY);
|
||||||
|
if (!legacySessionJson) return undefined;
|
||||||
|
|
||||||
|
const { dcID: mainDcId } = JSON.parse(legacySessionJson);
|
||||||
|
const keys: Record<number, string> = {};
|
||||||
|
const hashes: Record<number, string> = {};
|
||||||
|
|
||||||
|
DC_IDS.forEach((dcId) => {
|
||||||
|
try {
|
||||||
|
const key = localStorage.getItem(`dc${dcId}_auth_key`);
|
||||||
|
if (key) {
|
||||||
|
keys[dcId] = JSON.parse(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hash = localStorage.getItem(`dc${dcId}_hash`);
|
||||||
|
if (hash) {
|
||||||
|
hashes[dcId] = JSON.parse(hash);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (DEBUG) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn('Failed to load stored session', err);
|
||||||
|
}
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!Object.keys(keys).length) return undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
mainDcId,
|
||||||
|
keys,
|
||||||
|
hashes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function importLegacySession() {
|
||||||
|
const sessionId = localStorage.getItem(LEGACY_SESSION_KEY);
|
||||||
|
if (!sessionId) return;
|
||||||
|
|
||||||
|
const sessionJson = await idb.get(`GramJs:${sessionId}`);
|
||||||
|
try {
|
||||||
|
const sessionData = JSON.parse(sessionJson) as ApiSessionData;
|
||||||
|
storeSession(sessionData);
|
||||||
|
} catch (err) {
|
||||||
|
if (DEBUG) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.warn('Failed to load legacy session', err);
|
||||||
|
}
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove previously created IndexedDB and cache API sessions
|
||||||
|
export async function clearLegacySessions() {
|
||||||
|
localStorage.removeItem(LEGACY_SESSION_KEY);
|
||||||
|
|
||||||
|
const idbKeys = await idb.keys();
|
||||||
|
|
||||||
|
await Promise.all<Promise<any>>([
|
||||||
|
cacheApi.clear('GramJs'),
|
||||||
|
...idbKeys
|
||||||
|
.filter((k) => typeof k === 'string' && k.startsWith('GramJs:GramJs-session-'))
|
||||||
|
.map((k) => idb.del(k)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
@ -9,9 +9,10 @@ import {
|
|||||||
ApiUpdateAuthorizationState,
|
ApiUpdateAuthorizationState,
|
||||||
ApiUpdateAuthorizationError,
|
ApiUpdateAuthorizationError,
|
||||||
ApiUpdateConnectionState,
|
ApiUpdateConnectionState,
|
||||||
|
ApiUpdateSession,
|
||||||
ApiUpdateCurrentUser,
|
ApiUpdateCurrentUser,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import { DEBUG, LEGACY_SESSION_KEY } from '../../../config';
|
import { DEBUG, SESSION_USER_KEY } from '../../../config';
|
||||||
import { subscribe } from '../../../util/notifications';
|
import { subscribe } from '../../../util/notifications';
|
||||||
import { updateUser } from '../../reducers';
|
import { updateUser } from '../../reducers';
|
||||||
import { setLanguage } from '../../../util/langProvider';
|
import { setLanguage } from '../../../util/langProvider';
|
||||||
@ -41,6 +42,10 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
|
|||||||
onUpdateConnectionState(update);
|
onUpdateConnectionState(update);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'updateSession':
|
||||||
|
onUpdateSession(update);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'updateCurrentUser':
|
case 'updateCurrentUser':
|
||||||
onUpdateCurrentUser(update);
|
onUpdateCurrentUser(update);
|
||||||
break;
|
break;
|
||||||
@ -102,11 +107,6 @@ function onUpdateAuthorizationState(update: ApiUpdateAuthorizationState) {
|
|||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'authorizationStateReady': {
|
case 'authorizationStateReady': {
|
||||||
const { sessionId, sessionJson } = update;
|
|
||||||
if (sessionId && global.authRememberMe) {
|
|
||||||
getDispatch().saveSession({ sessionId, sessionJson });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wasAuthReady) {
|
if (wasAuthReady) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -145,6 +145,16 @@ function onUpdateConnectionState(update: ApiUpdateConnectionState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onUpdateSession(update: ApiUpdateSession) {
|
||||||
|
if (!getGlobal().authRememberMe) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { sessionData } = update;
|
||||||
|
|
||||||
|
getDispatch().saveSession({ sessionData });
|
||||||
|
}
|
||||||
|
|
||||||
function onUpdateCurrentUser(update: ApiUpdateCurrentUser) {
|
function onUpdateCurrentUser(update: ApiUpdateCurrentUser) {
|
||||||
const { currentUser } = update;
|
const { currentUser } = update;
|
||||||
|
|
||||||
@ -153,15 +163,15 @@ function onUpdateCurrentUser(update: ApiUpdateCurrentUser) {
|
|||||||
currentUserId: currentUser.id,
|
currentUserId: currentUser.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
updateLegacySessionUserId(currentUser.id);
|
updateSessionUserId(currentUser.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateLegacySessionUserId(currentUserId: number) {
|
function updateSessionUserId(currentUserId: number) {
|
||||||
const legacySessionJson = localStorage.getItem(LEGACY_SESSION_KEY);
|
const sessionUserAuth = localStorage.getItem(SESSION_USER_KEY);
|
||||||
if (!legacySessionJson) return;
|
if (!sessionUserAuth) return;
|
||||||
|
|
||||||
const legacySession = JSON.parse(legacySessionJson);
|
const userAuth = JSON.parse(sessionUserAuth);
|
||||||
legacySession.id = currentUserId;
|
userAuth.id = currentUserId;
|
||||||
|
|
||||||
localStorage.setItem(LEGACY_SESSION_KEY, JSON.stringify(legacySession));
|
localStorage.setItem(SESSION_USER_KEY, JSON.stringify(userAuth));
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user