mirror of
https://github.com/danog/telegram-tt.git
synced 2025-01-22 05:11:55 +01:00
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 {
|
||||
GRAMJS_SESSION_ID_KEY, INACTIVE_MARKER, LEGACY_SESSION_KEY, PAGE_TITLE,
|
||||
LEGACY_SESSION_KEY, INACTIVE_MARKER, SESSION_USER_KEY, PAGE_TITLE,
|
||||
} from './config';
|
||||
import { pick } from './util/iteratees';
|
||||
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 hasLegacySession = localStorage.getItem(LEGACY_SESSION_KEY);
|
||||
const hasSession = localStorage.getItem(SESSION_USER_KEY) || localStorage.getItem(LEGACY_SESSION_KEY);
|
||||
|
||||
return (hasSession || hasLegacySession) ? renderMain() : <Auth />;
|
||||
return hasSession ? renderMain() : <Auth />;
|
||||
};
|
||||
|
||||
function renderMain() {
|
||||
|
@ -105,12 +105,8 @@ export function onAuthError(err: Error) {
|
||||
});
|
||||
}
|
||||
|
||||
export function onAuthReady(sessionId: string, sessionJson: string) {
|
||||
onUpdate({
|
||||
...buildAuthStateUpdate('authorizationStateReady'),
|
||||
sessionId,
|
||||
sessionJson,
|
||||
});
|
||||
export function onAuthReady() {
|
||||
onUpdate(buildAuthStateUpdate('authorizationStateReady'));
|
||||
}
|
||||
|
||||
export function onCurrentUserUpdate(currentUser: ApiUser) {
|
||||
|
@ -4,7 +4,9 @@ import {
|
||||
import { Logger as GramJsLogger } from '../../../lib/gramjs/extensions/index';
|
||||
import { TwoFaParams } from '../../../lib/gramjs/client/2fa';
|
||||
|
||||
import { ApiMediaFormat, ApiOnProgress, OnApiUpdate } from '../../types';
|
||||
import {
|
||||
ApiMediaFormat, ApiOnProgress, ApiSessionData, OnApiUpdate,
|
||||
} from '../../types';
|
||||
|
||||
import {
|
||||
DEBUG, DEBUG_GRAMJS, UPLOAD_WORKERS, IS_TEST, APP_VERSION,
|
||||
@ -30,7 +32,7 @@ let onUpdate: OnApiUpdate;
|
||||
let client: TelegramClient;
|
||||
let isConnected = false;
|
||||
|
||||
export async function init(sessionInfo: string, _onUpdate: OnApiUpdate) {
|
||||
export async function init(_onUpdate: OnApiUpdate, sessionData?: ApiSessionData) {
|
||||
onUpdate = _onUpdate;
|
||||
|
||||
if (DEBUG) {
|
||||
@ -38,12 +40,8 @@ export async function init(sessionInfo: string, _onUpdate: OnApiUpdate) {
|
||||
console.log('>>> START INIT API');
|
||||
}
|
||||
|
||||
const session = IS_TEST
|
||||
? new sessions.LocalStorageSession(sessionInfo)
|
||||
: new sessions.IdbSession(sessionInfo);
|
||||
|
||||
client = new TelegramClient(
|
||||
session,
|
||||
new sessions.CallbackSession(sessionData, onSessionUpdate),
|
||||
process.env.TELEGRAM_T_API_ID,
|
||||
process.env.TELEGRAM_T_API_HASH,
|
||||
{
|
||||
@ -72,17 +70,14 @@ export async function init(sessionInfo: string, _onUpdate: OnApiUpdate) {
|
||||
onError: onAuthError,
|
||||
});
|
||||
|
||||
const newSessionId = await session.save();
|
||||
const sessionJson = JSON.stringify(session.getSessionData(true));
|
||||
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('>>> FINISH INIT API');
|
||||
// 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' });
|
||||
|
||||
void fetchCurrentUser();
|
||||
@ -109,6 +104,13 @@ export function getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
function onSessionUpdate(sessionData: ApiSessionData) {
|
||||
onUpdate({
|
||||
'@type': 'updateSession',
|
||||
sessionData,
|
||||
});
|
||||
}
|
||||
|
||||
function handleGramJsUpdate(update: any) {
|
||||
if (update instanceof connection.UpdateConnectionState) {
|
||||
isConnected = update.state === connection.UpdateConnectionState.connected;
|
||||
@ -125,7 +127,7 @@ export async function invokeRequest<T extends GramJs.AnyRequest>(
|
||||
if (!isConnected) {
|
||||
if (DEBUG) {
|
||||
// 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;
|
||||
|
@ -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 { API_THROTTLE_RESET_UPDATES, API_UPDATE_THROTTLE } from '../../config';
|
||||
@ -16,7 +18,7 @@ import * as methods from './methods';
|
||||
|
||||
let onUpdate: OnApiUpdate;
|
||||
|
||||
export async function initApi(_onUpdate: OnApiUpdate, sessionInfo = '') {
|
||||
export async function initApi(_onUpdate: OnApiUpdate, sessionData?: ApiSessionData) {
|
||||
onUpdate = _onUpdate;
|
||||
|
||||
initUpdater(handleUpdate);
|
||||
@ -28,7 +30,7 @@ export async function initApi(_onUpdate: OnApiUpdate, sessionInfo = '') {
|
||||
initManagement(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> {
|
||||
|
@ -1,6 +1,6 @@
|
||||
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 { WorkerMessageEvent, ThenArg, OriginRequest } from './types';
|
||||
|
||||
@ -20,7 +20,7 @@ const requestStatesByCallback = new Map<AnyToVoidFunction, RequestStates>();
|
||||
|
||||
// TODO Re-use `util/WorkerConnector.ts`
|
||||
|
||||
export function initApi(onUpdate: OnApiUpdate, sessionInfo = '') {
|
||||
export function initApi(onUpdate: OnApiUpdate, sessionData?: ApiSessionData) {
|
||||
if (!worker) {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
@ -33,7 +33,7 @@ export function initApi(onUpdate: OnApiUpdate, sessionInfo = '') {
|
||||
|
||||
return makeRequest({
|
||||
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';
|
||||
|
||||
export type ThenArg<T> = T extends Promise<infer U> ? U : T;
|
||||
@ -27,7 +27,7 @@ export interface WorkerMessageEvent {
|
||||
export type OriginRequest = {
|
||||
type: 'initApi';
|
||||
messageId?: string;
|
||||
args: [string];
|
||||
args: [ApiSessionData | undefined];
|
||||
} | {
|
||||
type: 'callMethod';
|
||||
messageId?: string;
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { ApiDocument } from './messages';
|
||||
|
||||
export * from './users';
|
||||
export * from './chats';
|
||||
export * from './messages';
|
||||
@ -7,52 +5,4 @@ export * from './updates';
|
||||
export * from './media';
|
||||
export * from './payments';
|
||||
export * from './settings';
|
||||
|
||||
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 * from './misc';
|
||||
|
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,
|
||||
} from './messages';
|
||||
import { ApiUser, ApiUserFullInfo, ApiUserStatus } from './users';
|
||||
import { ApiSessionData } from './misc';
|
||||
|
||||
export type ApiUpdateReady = {
|
||||
'@type': 'updateApiReady';
|
||||
@ -35,13 +36,16 @@ export type ApiUpdateConnectionStateType = (
|
||||
export type ApiUpdateAuthorizationState = {
|
||||
'@type': 'updateAuthorizationState';
|
||||
authorizationState: ApiUpdateAuthorizationStateType;
|
||||
sessionId?: string;
|
||||
sessionJson?: string;
|
||||
isCodeViaApp?: boolean;
|
||||
hint?: string;
|
||||
qrCode?: { token: string; expires: number };
|
||||
};
|
||||
|
||||
export type ApiUpdateSession = {
|
||||
'@type': 'updateSession';
|
||||
sessionData?: ApiSessionData;
|
||||
};
|
||||
|
||||
export type ApiUpdateAuthorizationError = {
|
||||
'@type': 'updateAuthorizationError';
|
||||
message: string;
|
||||
@ -363,7 +367,7 @@ export type ApiUpdatePrivacy = {
|
||||
};
|
||||
|
||||
export type ApiUpdate = (
|
||||
ApiUpdateReady |
|
||||
ApiUpdateReady | ApiUpdateSession |
|
||||
ApiUpdateAuthorizationState | ApiUpdateAuthorizationError | ApiUpdateConnectionState | ApiUpdateCurrentUser |
|
||||
ApiUpdateChat | ApiUpdateChatInbox | ApiUpdateChatTypingStatus | ApiUpdateChatFullInfo | ApiUpdatePinnedChatIds |
|
||||
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_GRAMJS = false;
|
||||
|
||||
export const GRAMJS_SESSION_ID_KEY = 'GramJs:sessionId';
|
||||
export const LEGACY_SESSION_KEY = 'user_auth';
|
||||
export const SESSION_USER_KEY = 'user_auth';
|
||||
export const LEGACY_SESSION_KEY = 'GramJs:sessionId';
|
||||
|
||||
export const GLOBAL_STATE_CACHE_DISABLED = false;
|
||||
export const GLOBAL_STATE_CACHE_KEY = 'tt-global-state';
|
||||
|
@ -11,8 +11,8 @@ import {
|
||||
GLOBAL_STATE_CACHE_DISABLED,
|
||||
GLOBAL_STATE_CACHE_KEY,
|
||||
GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT,
|
||||
GRAMJS_SESSION_ID_KEY,
|
||||
MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN, GLOBAL_STATE_CACHE_USER_LIST_LIMIT,
|
||||
LEGACY_SESSION_KEY,
|
||||
MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN, GLOBAL_STATE_CACHE_USER_LIST_LIMIT, SESSION_USER_KEY,
|
||||
} from '../config';
|
||||
import { IS_MOBILE_SCREEN } from '../util/environment';
|
||||
import { pick } from '../util/iteratees';
|
||||
@ -44,8 +44,8 @@ export function initCache() {
|
||||
|
||||
export function loadCache(initialState: GlobalState) {
|
||||
if (!GLOBAL_STATE_CACHE_DISABLED) {
|
||||
const hasActiveSession = localStorage.getItem(GRAMJS_SESSION_ID_KEY);
|
||||
if (hasActiveSession) {
|
||||
const hasSession = localStorage.getItem(SESSION_USER_KEY) || localStorage.getItem(LEGACY_SESSION_KEY);
|
||||
if (hasSession) {
|
||||
isAllowed = true;
|
||||
addCallback(updateCacheThrottled);
|
||||
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 LocalStorageSession = require('./LocalStorageSession');
|
||||
const IdbSession = require('./IdbSession');
|
||||
const CallbackSession = require('./CallbackSession');
|
||||
|
||||
module.exports = {
|
||||
Memory,
|
||||
@ -10,4 +11,5 @@ module.exports = {
|
||||
CacheApiSession,
|
||||
LocalStorageSession,
|
||||
IdbSession,
|
||||
CallbackSession,
|
||||
};
|
||||
|
@ -7,34 +7,30 @@ import { GlobalState } from '../../../global/types';
|
||||
import {
|
||||
LANG_CACHE_NAME,
|
||||
CUSTOM_BG_CACHE_NAME,
|
||||
GRAMJS_SESSION_ID_KEY,
|
||||
MEDIA_CACHE_NAME,
|
||||
MEDIA_CACHE_NAME_AVATARS,
|
||||
MEDIA_PROGRESSIVE_CACHE_NAME,
|
||||
LEGACY_SESSION_KEY,
|
||||
} from '../../../config';
|
||||
import { initApi, callApi } from '../../../api/gramjs';
|
||||
import { unsubscribe } from '../../../util/notifications';
|
||||
import * as cacheApi from '../../../util/cacheApi';
|
||||
import { HistoryWrapper } from '../../../util/history';
|
||||
import { updateAppBadge } from '../../../util/appBadge';
|
||||
import {
|
||||
storeSession,
|
||||
loadStoredSession,
|
||||
clearStoredSession,
|
||||
importLegacySession,
|
||||
clearLegacySessions,
|
||||
} from './sessions';
|
||||
|
||||
addReducer('initApi', (global: GlobalState, actions) => {
|
||||
let sessionInfo = localStorage.getItem(GRAMJS_SESSION_ID_KEY) || undefined;
|
||||
(async () => {
|
||||
await importLegacySession();
|
||||
void clearLegacySessions();
|
||||
|
||||
if (!sessionInfo) {
|
||||
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);
|
||||
void initApi(actions.apiUpdate, loadStoredSession());
|
||||
})();
|
||||
});
|
||||
|
||||
addReducer('setAuthPhoneNumber', (global, actions, payload) => {
|
||||
@ -116,10 +112,7 @@ addReducer('gotToAuthQrCode', (global) => {
|
||||
});
|
||||
|
||||
addReducer('saveSession', (global, actions, payload) => {
|
||||
const { sessionId, sessionJson } = payload!;
|
||||
localStorage.setItem(GRAMJS_SESSION_ID_KEY, sessionId);
|
||||
|
||||
exportLegacySession(sessionJson, global.currentUserId!);
|
||||
storeSession(payload.sessionData, global.currentUserId);
|
||||
});
|
||||
|
||||
addReducer('signOut', () => {
|
||||
@ -132,20 +125,21 @@ addReducer('signOut', () => {
|
||||
});
|
||||
|
||||
addReducer('reset', () => {
|
||||
localStorage.removeItem(GRAMJS_SESSION_ID_KEY);
|
||||
clearLegacySession();
|
||||
clearStoredSession();
|
||||
|
||||
cacheApi.clear(MEDIA_CACHE_NAME);
|
||||
cacheApi.clear(MEDIA_CACHE_NAME_AVATARS);
|
||||
cacheApi.clear(MEDIA_PROGRESSIVE_CACHE_NAME);
|
||||
cacheApi.clear(CUSTOM_BG_CACHE_NAME);
|
||||
void cacheApi.clear(MEDIA_CACHE_NAME);
|
||||
void cacheApi.clear(MEDIA_CACHE_NAME_AVATARS);
|
||||
void cacheApi.clear(MEDIA_PROGRESSIVE_CACHE_NAME);
|
||||
void cacheApi.clear(CUSTOM_BG_CACHE_NAME);
|
||||
|
||||
const langCachePrefix = LANG_CACHE_NAME.replace(/\d+$/, '');
|
||||
const langCacheVersion = (LANG_CACHE_NAME.match(/\d+$/) || [0])[0];
|
||||
for (let i = 0; i < langCacheVersion; i++) {
|
||||
cacheApi.clear(`${langCachePrefix}${i === 0 ? '' : i}`);
|
||||
void cacheApi.clear(`${langCachePrefix}${i === 0 ? '' : i}`);
|
||||
}
|
||||
|
||||
void clearLegacySessions();
|
||||
|
||||
updateAppBadge(0);
|
||||
|
||||
getDispatch().init();
|
||||
@ -188,23 +182,3 @@ addReducer('deleteDeviceToken', (global) => {
|
||||
delete newGlobal.push;
|
||||
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,
|
||||
ApiUpdateAuthorizationError,
|
||||
ApiUpdateConnectionState,
|
||||
ApiUpdateSession,
|
||||
ApiUpdateCurrentUser,
|
||||
} from '../../../api/types';
|
||||
import { DEBUG, LEGACY_SESSION_KEY } from '../../../config';
|
||||
import { DEBUG, SESSION_USER_KEY } from '../../../config';
|
||||
import { subscribe } from '../../../util/notifications';
|
||||
import { updateUser } from '../../reducers';
|
||||
import { setLanguage } from '../../../util/langProvider';
|
||||
@ -41,6 +42,10 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
|
||||
onUpdateConnectionState(update);
|
||||
break;
|
||||
|
||||
case 'updateSession':
|
||||
onUpdateSession(update);
|
||||
break;
|
||||
|
||||
case 'updateCurrentUser':
|
||||
onUpdateCurrentUser(update);
|
||||
break;
|
||||
@ -102,11 +107,6 @@ function onUpdateAuthorizationState(update: ApiUpdateAuthorizationState) {
|
||||
});
|
||||
break;
|
||||
case 'authorizationStateReady': {
|
||||
const { sessionId, sessionJson } = update;
|
||||
if (sessionId && global.authRememberMe) {
|
||||
getDispatch().saveSession({ sessionId, sessionJson });
|
||||
}
|
||||
|
||||
if (wasAuthReady) {
|
||||
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) {
|
||||
const { currentUser } = update;
|
||||
|
||||
@ -153,15 +163,15 @@ function onUpdateCurrentUser(update: ApiUpdateCurrentUser) {
|
||||
currentUserId: currentUser.id,
|
||||
});
|
||||
|
||||
updateLegacySessionUserId(currentUser.id);
|
||||
updateSessionUserId(currentUser.id);
|
||||
}
|
||||
|
||||
function updateLegacySessionUserId(currentUserId: number) {
|
||||
const legacySessionJson = localStorage.getItem(LEGACY_SESSION_KEY);
|
||||
if (!legacySessionJson) return;
|
||||
function updateSessionUserId(currentUserId: number) {
|
||||
const sessionUserAuth = localStorage.getItem(SESSION_USER_KEY);
|
||||
if (!sessionUserAuth) return;
|
||||
|
||||
const legacySession = JSON.parse(legacySessionJson);
|
||||
legacySession.id = currentUserId;
|
||||
const userAuth = JSON.parse(sessionUserAuth);
|
||||
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