[Cast] Use common shared packages
This commit is contained in:
parent
a3bf49e2d2
commit
1e583414d3
19 changed files with 34 additions and 1482 deletions
|
@ -1,53 +1,5 @@
|
|||
import * as Sentry from '@sentry/nextjs';
|
||||
import { getSentryUserID } from 'utils/user';
|
||||
import { getHasOptedOutOfCrashReports } from 'utils/storage/index';
|
||||
import { runningInBrowser } from '@ente/shared/apps/browser';
|
||||
import { getSentryTunnelURL } from '@ente/shared/network/api';
|
||||
import {
|
||||
getSentryDSN,
|
||||
getSentryENV,
|
||||
getSentryRelease,
|
||||
getIsSentryEnabled,
|
||||
} from 'constants/sentry';
|
||||
import { setupSentry } from '@ente/shared/sentry/config/sentry.config.base';
|
||||
|
||||
const HAS_OPTED_OUT_OF_CRASH_REPORTING =
|
||||
runningInBrowser() && getHasOptedOutOfCrashReports();
|
||||
|
||||
if (!HAS_OPTED_OUT_OF_CRASH_REPORTING) {
|
||||
const SENTRY_DSN = getSentryDSN();
|
||||
const SENTRY_ENV = getSentryENV();
|
||||
const SENTRY_RELEASE = getSentryRelease();
|
||||
const IS_ENABLED = getIsSentryEnabled();
|
||||
|
||||
Sentry.init({
|
||||
dsn: SENTRY_DSN,
|
||||
enabled: IS_ENABLED,
|
||||
environment: SENTRY_ENV,
|
||||
release: SENTRY_RELEASE,
|
||||
attachStacktrace: true,
|
||||
autoSessionTracking: false,
|
||||
tunnel: getSentryTunnelURL(),
|
||||
beforeSend(event) {
|
||||
event.request = event.request || {};
|
||||
const currentURL = new URL(document.location.href);
|
||||
currentURL.hash = '';
|
||||
event.request.url = currentURL;
|
||||
return event;
|
||||
},
|
||||
integrations: function (i) {
|
||||
return i.filter(function (i) {
|
||||
return i.name !== 'Breadcrumbs';
|
||||
});
|
||||
},
|
||||
// ...
|
||||
// Note: if you want to override the automatic release value, do not set a
|
||||
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
|
||||
// that it will also get attached to your source maps
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
Sentry.setUser({ id: await getSentryUserID() });
|
||||
};
|
||||
|
||||
main();
|
||||
}
|
||||
const DEFAULT_SENTRY_DSN =
|
||||
'https://bd3656fc40d74d5e8f278132817963a3@sentry.ente.io/2';
|
||||
setupSentry(DEFAULT_SENTRY_DSN);
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
import * as Sentry from '@sentry/nextjs';
|
||||
import {
|
||||
getSentryDSN,
|
||||
getSentryENV,
|
||||
getSentryRelease,
|
||||
getIsSentryEnabled,
|
||||
} from 'constants/sentry';
|
||||
|
||||
import { getSentryUserID } from 'utils/user';
|
||||
|
||||
const SENTRY_DSN = getSentryDSN();
|
||||
const SENTRY_ENV = getSentryENV();
|
||||
const SENTRY_RELEASE = getSentryRelease();
|
||||
const IS_ENABLED = getIsSentryEnabled();
|
||||
|
||||
Sentry.init({
|
||||
dsn: SENTRY_DSN,
|
||||
enabled: IS_ENABLED,
|
||||
environment: SENTRY_ENV,
|
||||
release: SENTRY_RELEASE,
|
||||
autoSessionTracking: false,
|
||||
});
|
||||
|
||||
const main = async () => {
|
||||
Sentry.setUser({ id: await getSentryUserID() });
|
||||
};
|
||||
|
||||
main();
|
|
@ -7,6 +7,7 @@ import { getCollectionWithKey } from 'services/collectionService';
|
|||
import { syncFiles } from 'services/fileService';
|
||||
import { EnteFile } from 'types/file';
|
||||
import { downloadFileAsBlob, isRawFileFromFileName } from 'utils/file';
|
||||
import { logError } from 'utils/sentry';
|
||||
|
||||
export const SlideshowContext = createContext<{
|
||||
showNextSlide: () => void;
|
||||
|
@ -36,9 +37,13 @@ export default function Slideshow() {
|
|||
'targetCollectionKey'
|
||||
);
|
||||
|
||||
const castToken = window.localStorage.getItem('token');
|
||||
|
||||
const collection = await getCollectionWithKey(
|
||||
Number(requestedCollectionID),
|
||||
requestedCollectionKey
|
||||
requestedCollectionKey,
|
||||
'cast',
|
||||
castToken
|
||||
);
|
||||
|
||||
const files = await syncFiles('normal', [collection], () => {});
|
||||
|
@ -48,7 +53,9 @@ export default function Slideshow() {
|
|||
files.filter((file) => isFileEligibleForCast(file))
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
} catch (e) {
|
||||
logError(e, 'error during sync');
|
||||
alert('error, redirect to home' + e);
|
||||
router.push('/');
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,241 +0,0 @@
|
|||
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||
import { addLogLine } from 'utils/logging';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { ApiError, CustomError, isApiErrorResponse } from 'utils/error';
|
||||
|
||||
interface IHTTPHeaders {
|
||||
[headerKey: string]: any;
|
||||
}
|
||||
|
||||
interface IQueryPrams {
|
||||
[paramName: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service to manage all HTTP calls.
|
||||
*/
|
||||
class HTTPService {
|
||||
constructor() {
|
||||
axios.interceptors.response.use(
|
||||
(response) => Promise.resolve(response),
|
||||
(error) => {
|
||||
const config = error.config as AxiosRequestConfig;
|
||||
if (error.response) {
|
||||
const response = error.response as AxiosResponse;
|
||||
let apiError: ApiError;
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
if (isApiErrorResponse(response.data)) {
|
||||
const responseData = response.data;
|
||||
logError(error, 'HTTP Service Error', {
|
||||
url: config.url,
|
||||
method: config.method,
|
||||
xRequestId: response.headers['x-request-id'],
|
||||
httpStatus: response.status,
|
||||
errMessage: responseData.message,
|
||||
errCode: responseData.code,
|
||||
});
|
||||
apiError = new ApiError(
|
||||
responseData.message,
|
||||
responseData.code,
|
||||
response.status
|
||||
);
|
||||
} else {
|
||||
if (response.status >= 400 && response.status < 500) {
|
||||
apiError = new ApiError(
|
||||
CustomError.CLIENT_ERROR,
|
||||
'',
|
||||
response.status
|
||||
);
|
||||
} else {
|
||||
apiError = new ApiError(
|
||||
CustomError.ServerError,
|
||||
'',
|
||||
response.status
|
||||
);
|
||||
}
|
||||
}
|
||||
logError(apiError, 'HTTP Service Error', {
|
||||
url: config.url,
|
||||
method: config.method,
|
||||
cfRay: response.headers['cf-ray'],
|
||||
xRequestId: response.headers['x-request-id'],
|
||||
httpStatus: response.status,
|
||||
});
|
||||
throw apiError;
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
addLogLine(
|
||||
'request failed- no response',
|
||||
`url: ${config.url}`,
|
||||
`method: ${config.method}`
|
||||
);
|
||||
return Promise.reject(error);
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
addLogLine(
|
||||
'request failed- axios error',
|
||||
`url: ${config.url}`,
|
||||
`method: ${config.method}`
|
||||
);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* header object to be append to all api calls.
|
||||
*/
|
||||
private headers: IHTTPHeaders = {
|
||||
'content-type': 'application/json',
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the headers to the given object.
|
||||
*/
|
||||
public setHeaders(headers: IHTTPHeaders) {
|
||||
this.headers = {
|
||||
...this.headers,
|
||||
...headers,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a header to list of headers.
|
||||
*/
|
||||
public appendHeader(key: string, value: string) {
|
||||
this.headers = {
|
||||
...this.headers,
|
||||
[key]: value,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given header.
|
||||
*/
|
||||
public removeHeader(key: string) {
|
||||
this.headers[key] = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns axios interceptors.
|
||||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
public getInterceptors() {
|
||||
return axios.interceptors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic HTTP request.
|
||||
* This is done so that developer can use any functionality
|
||||
* provided by axios. Here, only the set headers are spread
|
||||
* over what was sent in config.
|
||||
*/
|
||||
public async request(config: AxiosRequestConfig, customConfig?: any) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
config.headers = {
|
||||
...this.headers,
|
||||
...config.headers,
|
||||
};
|
||||
if (customConfig?.cancel) {
|
||||
config.cancelToken = new axios.CancelToken(
|
||||
(c) => (customConfig.cancel.exec = c)
|
||||
);
|
||||
}
|
||||
return await axios({ ...config, ...customConfig });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request.
|
||||
*/
|
||||
public get(
|
||||
url: string,
|
||||
params?: IQueryPrams,
|
||||
headers?: IHTTPHeaders,
|
||||
customConfig?: any
|
||||
) {
|
||||
return this.request(
|
||||
{
|
||||
headers,
|
||||
method: 'GET',
|
||||
params,
|
||||
url,
|
||||
},
|
||||
customConfig
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post request
|
||||
*/
|
||||
public post(
|
||||
url: string,
|
||||
data?: any,
|
||||
params?: IQueryPrams,
|
||||
headers?: IHTTPHeaders,
|
||||
customConfig?: any
|
||||
) {
|
||||
return this.request(
|
||||
{
|
||||
data,
|
||||
headers,
|
||||
method: 'POST',
|
||||
params,
|
||||
url,
|
||||
},
|
||||
customConfig
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put request
|
||||
*/
|
||||
public put(
|
||||
url: string,
|
||||
data: any,
|
||||
params?: IQueryPrams,
|
||||
headers?: IHTTPHeaders,
|
||||
customConfig?: any
|
||||
) {
|
||||
return this.request(
|
||||
{
|
||||
data,
|
||||
headers,
|
||||
method: 'PUT',
|
||||
params,
|
||||
url,
|
||||
},
|
||||
customConfig
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete request
|
||||
*/
|
||||
public delete(
|
||||
url: string,
|
||||
data: any,
|
||||
params?: IQueryPrams,
|
||||
headers?: IHTTPHeaders,
|
||||
customConfig?: any
|
||||
) {
|
||||
return this.request(
|
||||
{
|
||||
data,
|
||||
headers,
|
||||
method: 'DELETE',
|
||||
params,
|
||||
url,
|
||||
},
|
||||
customConfig
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a Singleton Service.
|
||||
// This will help me maintain common headers / functionality
|
||||
// at a central place.
|
||||
export default new HTTPService();
|
|
@ -1,11 +1,6 @@
|
|||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
import localForage from 'utils/storage/localForage';
|
||||
import { getActualKey } from '@ente/shared/user';
|
||||
import { batch } from '@ente/shared/batch';
|
||||
import { getPublicKey } from './userService';
|
||||
import HTTPService from './HTTPService';
|
||||
import { EnteFile } from 'types/file';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { CustomError } from 'utils/error';
|
||||
import {
|
||||
sortFiles,
|
||||
|
@ -71,6 +66,10 @@ import { EncryptedMagicMetadata } from 'types/magicMetadata';
|
|||
import { VISIBILITY_STATE } from 'types/magicMetadata';
|
||||
import { getEndpoint } from '@ente/shared/network/api';
|
||||
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
||||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
import { LS_KEYS, getData } from '@ente/shared/storage/localStorage';
|
||||
import localForage from '@ente/shared/storage/localForage';
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
const COLLECTION_TABLE = 'collections';
|
||||
|
@ -347,10 +346,17 @@ export const getCollection = async (
|
|||
|
||||
export const getCollectionWithKey = async (
|
||||
collectionID: number,
|
||||
key: string
|
||||
key: string,
|
||||
tokenType: 'user' | 'cast' = 'user',
|
||||
castToken?: string
|
||||
): Promise<Collection> => {
|
||||
try {
|
||||
const token = getToken();
|
||||
let token;
|
||||
if (tokenType === 'user') {
|
||||
token = getToken();
|
||||
} else {
|
||||
token = castToken!;
|
||||
}
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
@ -974,39 +980,6 @@ export const renameCollection = async (
|
|||
);
|
||||
};
|
||||
|
||||
export const shareCollection = async (
|
||||
collection: Collection,
|
||||
withUserEmail: string,
|
||||
role: string
|
||||
) => {
|
||||
try {
|
||||
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
|
||||
const token = getToken();
|
||||
const publicKey: string = await getPublicKey(withUserEmail);
|
||||
const encryptedKey = await cryptoWorker.boxSeal(
|
||||
collection.key,
|
||||
publicKey
|
||||
);
|
||||
const shareCollectionRequest = {
|
||||
collectionID: collection.id,
|
||||
email: withUserEmail,
|
||||
role: role,
|
||||
encryptedKey,
|
||||
};
|
||||
await HTTPService.post(
|
||||
`${ENDPOINT}/collections/share`,
|
||||
shareCollectionRequest,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, 'share collection failed ');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const unshareCollection = async (
|
||||
collection: Collection,
|
||||
withUserEmail: string
|
||||
|
|
|
@ -3,10 +3,8 @@ import {
|
|||
getRenderableFileURL,
|
||||
createTypedObjectURL,
|
||||
} from 'utils/file';
|
||||
import HTTPService from './HTTPService';
|
||||
import { EnteFile } from 'types/file';
|
||||
|
||||
import { logError } from 'utils/sentry';
|
||||
import { FILE_TYPE } from 'constants/file';
|
||||
import { CustomError } from 'utils/error';
|
||||
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
|
||||
|
@ -16,9 +14,10 @@ import { Remote } from 'comlink';
|
|||
import { DedicatedCryptoWorker } from 'worker/crypto.worker';
|
||||
import { LimitedCache } from 'types/cache';
|
||||
import { retryAsyncFunction } from 'utils/network';
|
||||
import { addLogLine } from 'utils/logging';
|
||||
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
||||
import { getFileURL, getThumbnailURL } from '@ente/shared/network/api';
|
||||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
|
||||
class DownloadManager {
|
||||
private fileObjectURLPromise = new Map<
|
||||
|
|
|
@ -3,7 +3,7 @@ import localForage from 'utils/storage/localForage';
|
|||
|
||||
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
||||
import { Collection } from 'types/collection';
|
||||
import HTTPService from './HTTPService';
|
||||
|
||||
import { logError } from 'utils/sentry';
|
||||
import {
|
||||
decryptFile,
|
||||
|
@ -29,6 +29,7 @@ import {
|
|||
} from './collectionService';
|
||||
import { REQUEST_BATCH_SIZE } from 'constants/api';
|
||||
import { batch } from '@ente/shared/batch';
|
||||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
const FILES_TABLE = 'files';
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import { logError } from 'utils/sentry';
|
||||
import HTTPService from './HTTPService';
|
||||
import { getEndpoint } from '@ente/shared/network/api';
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
|
||||
export const getKexValue = async (key: string) => {
|
||||
let resp;
|
||||
try {
|
||||
resp = await HTTPService.get(`${ENDPOINT}/kex/get`, {
|
||||
identifier: key,
|
||||
});
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get kex value');
|
||||
throw e;
|
||||
}
|
||||
|
||||
return resp.data.wrappedKey;
|
||||
};
|
||||
|
||||
export const setKexValue = async (key: string, value: string) => {
|
||||
try {
|
||||
await HTTPService.put(ENDPOINT + '/kex/add', {
|
||||
customIdentifier: key,
|
||||
wrappedKey: value,
|
||||
});
|
||||
} catch (e) {
|
||||
logError(e, 'failed to set kex value');
|
||||
throw e;
|
||||
}
|
||||
};
|
|
@ -3,10 +3,8 @@ import {
|
|||
getRenderableFileURL,
|
||||
createTypedObjectURL,
|
||||
} from 'utils/file';
|
||||
import HTTPService from './HTTPService';
|
||||
import { EnteFile } from 'types/file';
|
||||
|
||||
import { logError } from 'utils/sentry';
|
||||
import { FILE_TYPE } from 'constants/file';
|
||||
import { CustomError } from 'utils/error';
|
||||
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
|
||||
|
@ -17,6 +15,8 @@ import {
|
|||
getPublicCollectionFileURL,
|
||||
getPublicCollectionThumbnailURL,
|
||||
} from '@ente/shared/network/api';
|
||||
import HTTPService from '@ente/shared/network/HTTPService';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
|
||||
class PublicCollectionDownloadManager {
|
||||
private fileObjectURLPromise = new Map<
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { logError } from '@ente/shared/sentry';
|
||||
import { ElectronFile } from 'types/upload';
|
||||
import { convertBytesToHumanReadable } from 'utils/file/size';
|
||||
import { logError } from 'utils/sentry';
|
||||
|
||||
export async function getUint8ArrayView(
|
||||
file: Blob | ElectronFile
|
||||
|
|
|
@ -1,757 +0,0 @@
|
|||
import { PAGES } from 'constants/pages';
|
||||
import { clearKeys } from 'utils/storage/sessionStorage';
|
||||
import router from 'next/router';
|
||||
import { clearData, getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
import localForage from 'utils/storage/localForage';
|
||||
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
||||
import HTTPService from './HTTPService';
|
||||
import {
|
||||
computeVerifierHelper,
|
||||
generateLoginSubKey,
|
||||
generateSRPClient,
|
||||
getRecoveryKey,
|
||||
} from 'utils/crypto';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { eventBus, Events } from './events';
|
||||
import {
|
||||
KeyAttributes,
|
||||
RecoveryKey,
|
||||
TwoFactorSecret,
|
||||
TwoFactorVerificationResponse,
|
||||
TwoFactorRecoveryResponse,
|
||||
UserDetails,
|
||||
DeleteChallengeResponse,
|
||||
GetRemoteStoreValueResponse,
|
||||
SetupSRPRequest,
|
||||
CreateSRPSessionResponse,
|
||||
UserVerificationResponse,
|
||||
GetFeatureFlagResponse,
|
||||
SetupSRPResponse,
|
||||
CompleteSRPSetupRequest,
|
||||
CompleteSRPSetupResponse,
|
||||
SRPSetupAttributes,
|
||||
SRPAttributes,
|
||||
UpdateSRPAndKeysRequest,
|
||||
UpdateSRPAndKeysResponse,
|
||||
GetSRPAttributesResponse,
|
||||
} from 'types/user';
|
||||
import { ApiError, CustomError } from 'utils/error';
|
||||
import isElectron from 'is-electron';
|
||||
// import safeStorageService from './electron/safeStorage';
|
||||
// import { deleteAllCache } from 'utils/storage/cache';
|
||||
import { B64EncryptionResult } from 'types/crypto';
|
||||
import { getLocalFamilyData, isPartOfFamily } from 'utils/user/family';
|
||||
import { AxiosResponse, HttpStatusCode } from 'axios';
|
||||
import { APPS, getAppName } from 'constants/apps';
|
||||
import { addLocalLog } from 'utils/logging';
|
||||
import { convertBase64ToBuffer, convertBufferToBase64 } from 'utils/user';
|
||||
import { setLocalMapEnabled } from 'utils/storage';
|
||||
import InMemoryStore, { MS_KEYS } from './InMemoryStore';
|
||||
import {
|
||||
getEndpoint,
|
||||
getFamilyPortalURL,
|
||||
isDevDeployment,
|
||||
} from '@ente/shared/network/api';
|
||||
|
||||
const ENDPOINT = getEndpoint();
|
||||
|
||||
const HAS_SET_KEYS = 'hasSetKeys';
|
||||
|
||||
export const sendOtt = (email: string) => {
|
||||
const appName = getAppName();
|
||||
return HTTPService.post(`${ENDPOINT}/users/ott`, {
|
||||
email,
|
||||
client: appName === APPS.AUTH ? 'totp' : 'web',
|
||||
});
|
||||
};
|
||||
|
||||
export const getPublicKey = async (email: string) => {
|
||||
const token = getToken();
|
||||
|
||||
const resp = await HTTPService.get(
|
||||
`${ENDPOINT}/users/public-key`,
|
||||
{ email },
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
return resp.data.publicKey;
|
||||
};
|
||||
|
||||
export const getPaymentToken = async () => {
|
||||
const token = getToken();
|
||||
|
||||
const resp = await HTTPService.get(
|
||||
`${ENDPOINT}/users/payment-token`,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
return resp.data['paymentToken'];
|
||||
};
|
||||
|
||||
export const getFamiliesToken = async () => {
|
||||
try {
|
||||
const token = getToken();
|
||||
|
||||
const resp = await HTTPService.get(
|
||||
`${ENDPOINT}/users/families-token`,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
return resp.data['familiesToken'];
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get family token');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const getRoadmapRedirectURL = async () => {
|
||||
try {
|
||||
const token = getToken();
|
||||
|
||||
const resp = await HTTPService.get(
|
||||
`${ENDPOINT}/users/roadmap/v2`,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
return resp.data['url'];
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get roadmap url');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const verifyOtt = (email: string, ott: string) =>
|
||||
HTTPService.post(`${ENDPOINT}/users/verify-email`, { email, ott });
|
||||
|
||||
export const putAttributes = (token: string, keyAttributes: KeyAttributes) =>
|
||||
HTTPService.put(`${ENDPOINT}/users/attributes`, { keyAttributes }, null, {
|
||||
'X-Auth-Token': token,
|
||||
});
|
||||
|
||||
export const setRecoveryKey = (token: string, recoveryKey: RecoveryKey) =>
|
||||
HTTPService.put(`${ENDPOINT}/users/recovery-key`, recoveryKey, null, {
|
||||
'X-Auth-Token': token,
|
||||
});
|
||||
|
||||
export const logoutUser = async () => {
|
||||
try {
|
||||
try {
|
||||
// ignore server logout result as logoutUser can be triggered before sign up or on token expiry
|
||||
await _logout();
|
||||
} catch (e) {
|
||||
//ignore
|
||||
}
|
||||
try {
|
||||
InMemoryStore.clear();
|
||||
} catch (e) {
|
||||
logError(e, 'clear InMemoryStore failed');
|
||||
}
|
||||
try {
|
||||
clearKeys();
|
||||
} catch (e) {
|
||||
logError(e, 'clearKeys failed');
|
||||
}
|
||||
try {
|
||||
clearData();
|
||||
} catch (e) {
|
||||
logError(e, 'clearData failed');
|
||||
}
|
||||
try {
|
||||
// await deleteAllCache();
|
||||
} catch (e) {
|
||||
logError(e, 'deleteAllCache failed');
|
||||
}
|
||||
try {
|
||||
await clearFiles();
|
||||
} catch (e) {
|
||||
logError(e, 'clearFiles failed');
|
||||
}
|
||||
if (isElectron()) {
|
||||
try {
|
||||
// safeStorageService.clearElectronStore();
|
||||
} catch (e) {
|
||||
logError(e, 'clearElectronStore failed');
|
||||
}
|
||||
}
|
||||
try {
|
||||
eventBus.emit(Events.LOGOUT);
|
||||
} catch (e) {
|
||||
logError(e, 'Error in logout handlers');
|
||||
}
|
||||
router.push(PAGES.ROOT);
|
||||
} catch (e) {
|
||||
logError(e, 'logoutUser failed');
|
||||
}
|
||||
};
|
||||
|
||||
export const clearFiles = async () => {
|
||||
await localForage.clear();
|
||||
};
|
||||
|
||||
export const isTokenValid = async (token: string) => {
|
||||
try {
|
||||
const resp = await HTTPService.get(
|
||||
`${ENDPOINT}/users/session-validity/v2`,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
try {
|
||||
if (resp.data[HAS_SET_KEYS] === undefined) {
|
||||
throw Error('resp.data.hasSetKey undefined');
|
||||
}
|
||||
if (!resp.data['hasSetKeys']) {
|
||||
try {
|
||||
await putAttributes(
|
||||
token,
|
||||
getData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES)
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, 'put attribute failed');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logError(e, 'hasSetKeys not set in session validity response');
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
logError(e, 'session-validity api call failed');
|
||||
if (
|
||||
e instanceof ApiError &&
|
||||
e.httpStatusCode === HttpStatusCode.Unauthorized
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const setupTwoFactor = async () => {
|
||||
const resp = await HTTPService.post(
|
||||
`${ENDPOINT}/users/two-factor/setup`,
|
||||
null,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': getToken(),
|
||||
}
|
||||
);
|
||||
return resp.data as TwoFactorSecret;
|
||||
};
|
||||
|
||||
export const enableTwoFactor = async (
|
||||
code: string,
|
||||
recoveryEncryptedTwoFactorSecret: B64EncryptionResult
|
||||
) => {
|
||||
await HTTPService.post(
|
||||
`${ENDPOINT}/users/two-factor/enable`,
|
||||
{
|
||||
code,
|
||||
encryptedTwoFactorSecret:
|
||||
recoveryEncryptedTwoFactorSecret.encryptedData,
|
||||
twoFactorSecretDecryptionNonce:
|
||||
recoveryEncryptedTwoFactorSecret.nonce,
|
||||
},
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': getToken(),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const verifyTwoFactor = async (code: string, sessionID: string) => {
|
||||
const resp = await HTTPService.post(
|
||||
`${ENDPOINT}/users/two-factor/verify`,
|
||||
{
|
||||
code,
|
||||
sessionID,
|
||||
},
|
||||
null
|
||||
);
|
||||
return resp.data as TwoFactorVerificationResponse;
|
||||
};
|
||||
|
||||
export const recoverTwoFactor = async (sessionID: string) => {
|
||||
const resp = await HTTPService.get(`${ENDPOINT}/users/two-factor/recover`, {
|
||||
sessionID,
|
||||
});
|
||||
return resp.data as TwoFactorRecoveryResponse;
|
||||
};
|
||||
|
||||
export const removeTwoFactor = async (sessionID: string, secret: string) => {
|
||||
const resp = await HTTPService.post(`${ENDPOINT}/users/two-factor/remove`, {
|
||||
sessionID,
|
||||
secret,
|
||||
});
|
||||
return resp.data as TwoFactorVerificationResponse;
|
||||
};
|
||||
|
||||
export const disableTwoFactor = async () => {
|
||||
await HTTPService.post(`${ENDPOINT}/users/two-factor/disable`, null, null, {
|
||||
'X-Auth-Token': getToken(),
|
||||
});
|
||||
};
|
||||
|
||||
export const getTwoFactorStatus = async () => {
|
||||
const resp = await HTTPService.get(
|
||||
`${ENDPOINT}/users/two-factor/status`,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': getToken(),
|
||||
}
|
||||
);
|
||||
return resp.data['status'];
|
||||
};
|
||||
|
||||
export const _logout = async () => {
|
||||
if (!getToken()) return true;
|
||||
try {
|
||||
await HTTPService.post(`${ENDPOINT}/users/logout`, null, null, {
|
||||
'X-Auth-Token': getToken(),
|
||||
});
|
||||
return true;
|
||||
} catch (e) {
|
||||
logError(e, '/users/logout failed');
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const sendOTTForEmailChange = async (email: string) => {
|
||||
if (!getToken()) {
|
||||
return null;
|
||||
}
|
||||
await HTTPService.post(`${ENDPOINT}/users/ott`, {
|
||||
email,
|
||||
client: 'web',
|
||||
purpose: 'change',
|
||||
});
|
||||
};
|
||||
|
||||
export const changeEmail = async (email: string, ott: string) => {
|
||||
if (!getToken()) {
|
||||
return null;
|
||||
}
|
||||
await HTTPService.post(
|
||||
`${ENDPOINT}/users/change-email`,
|
||||
{
|
||||
email,
|
||||
ott,
|
||||
},
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': getToken(),
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const getUserDetailsV2 = async (): Promise<UserDetails> => {
|
||||
try {
|
||||
const token = getToken();
|
||||
|
||||
const resp = await HTTPService.get(
|
||||
`${ENDPOINT}/users/details/v2`,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
return resp.data;
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get user details v2');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const getFamilyPortalRedirectURL = async () => {
|
||||
try {
|
||||
const jwtToken = await getFamiliesToken();
|
||||
const isFamilyCreated = isPartOfFamily(getLocalFamilyData());
|
||||
return `${getFamilyPortalURL()}?token=${jwtToken}&isFamilyCreated=${isFamilyCreated}&redirectURL=${
|
||||
window.location.origin
|
||||
}/gallery`;
|
||||
} catch (e) {
|
||||
logError(e, 'unable to generate to family portal URL');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const getAccountDeleteChallenge = async () => {
|
||||
try {
|
||||
const token = getToken();
|
||||
|
||||
const resp = await HTTPService.get(
|
||||
`${ENDPOINT}/users/delete-challenge`,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
return resp.data as DeleteChallengeResponse;
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get account delete challenge');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteAccount = async (
|
||||
challenge: string,
|
||||
reason: string,
|
||||
feedback: string
|
||||
) => {
|
||||
try {
|
||||
const token = getToken();
|
||||
if (!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
await HTTPService.delete(
|
||||
`${ENDPOINT}/users/delete`,
|
||||
{ challenge, reason, feedback },
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, 'deleteAccount api call failed');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
// Ensure that the keys in local storage are not malformed by verifying that the
|
||||
// recoveryKey can be decrypted with the masterKey.
|
||||
// Note: This is not bullet-proof.
|
||||
export const validateKey = async () => {
|
||||
try {
|
||||
await getRecoveryKey();
|
||||
return true;
|
||||
} catch (e) {
|
||||
await logoutUser();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const getFaceSearchEnabledStatus = async () => {
|
||||
try {
|
||||
const token = getToken();
|
||||
const resp: AxiosResponse<GetRemoteStoreValueResponse> =
|
||||
await HTTPService.get(
|
||||
`${ENDPOINT}/remote-store`,
|
||||
{
|
||||
key: 'faceSearchEnabled',
|
||||
defaultValue: false,
|
||||
},
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
return resp.data.value === 'true';
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get face search enabled status');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const updateFaceSearchEnabledStatus = async (newStatus: boolean) => {
|
||||
try {
|
||||
const token = getToken();
|
||||
await HTTPService.post(
|
||||
`${ENDPOINT}/remote-store/update`,
|
||||
{
|
||||
key: 'faceSearchEnabled',
|
||||
value: newStatus.toString(),
|
||||
},
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, 'failed to update face search enabled status');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const syncMapEnabled = async () => {
|
||||
try {
|
||||
const status = await getMapEnabledStatus();
|
||||
setLocalMapEnabled(status);
|
||||
} catch (e) {
|
||||
logError(e, 'failed to sync map enabled status');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const getMapEnabledStatus = async () => {
|
||||
try {
|
||||
const token = getToken();
|
||||
const resp: AxiosResponse<GetRemoteStoreValueResponse> =
|
||||
await HTTPService.get(
|
||||
`${ENDPOINT}/remote-store`,
|
||||
{
|
||||
key: 'mapEnabled',
|
||||
defaultValue: false,
|
||||
},
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
return resp.data.value === 'true';
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get map enabled status');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const updateMapEnabledStatus = async (newStatus: boolean) => {
|
||||
try {
|
||||
const token = getToken();
|
||||
await HTTPService.post(
|
||||
`${ENDPOINT}/remote-store/update`,
|
||||
{
|
||||
key: 'mapEnabled',
|
||||
value: newStatus.toString(),
|
||||
},
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, 'failed to update map enabled status');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export async function getDisableCFUploadProxyFlag(): Promise<boolean> {
|
||||
try {
|
||||
const disableCFUploadProxy =
|
||||
process.env.NEXT_PUBLIC_DISABLE_CF_UPLOAD_PROXY;
|
||||
if (isDevDeployment() && typeof disableCFUploadProxy !== 'undefined') {
|
||||
return disableCFUploadProxy === 'true';
|
||||
}
|
||||
const featureFlags = (
|
||||
await fetch('https://static.ente.io/feature_flags.json')
|
||||
).json() as GetFeatureFlagResponse;
|
||||
return featureFlags.disableCFUploadProxy;
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get feature flags');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const getSRPAttributes = async (
|
||||
email: string
|
||||
): Promise<SRPAttributes | null> => {
|
||||
try {
|
||||
const resp = await HTTPService.get(`${ENDPOINT}/users/srp/attributes`, {
|
||||
email,
|
||||
});
|
||||
return (resp.data as GetSRPAttributesResponse).attributes;
|
||||
} catch (e) {
|
||||
logError(e, 'failed to get SRP attributes');
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const configureSRP = async ({
|
||||
srpSalt,
|
||||
srpUserID,
|
||||
srpVerifier,
|
||||
loginSubKey,
|
||||
}: SRPSetupAttributes) => {
|
||||
try {
|
||||
const srpConfigureInProgress = InMemoryStore.get(
|
||||
MS_KEYS.SRP_CONFIGURE_IN_PROGRESS
|
||||
);
|
||||
if (srpConfigureInProgress) {
|
||||
throw Error('SRP configure already in progress');
|
||||
}
|
||||
InMemoryStore.set(MS_KEYS.SRP_CONFIGURE_IN_PROGRESS, true);
|
||||
const srpClient = await generateSRPClient(
|
||||
srpSalt,
|
||||
srpUserID,
|
||||
loginSubKey
|
||||
);
|
||||
|
||||
const srpA = convertBufferToBase64(srpClient.computeA());
|
||||
|
||||
addLocalLog(() => `srp a: ${srpA}`);
|
||||
const token = getToken();
|
||||
const { setupID, srpB } = await startSRPSetup(token, {
|
||||
srpA,
|
||||
srpUserID,
|
||||
srpSalt,
|
||||
srpVerifier,
|
||||
});
|
||||
|
||||
srpClient.setB(convertBase64ToBuffer(srpB));
|
||||
|
||||
const srpM1 = convertBufferToBase64(srpClient.computeM1());
|
||||
|
||||
const { srpM2 } = await completeSRPSetup(token, {
|
||||
srpM1,
|
||||
setupID,
|
||||
});
|
||||
|
||||
srpClient.checkM2(convertBase64ToBuffer(srpM2));
|
||||
} catch (e) {
|
||||
logError(e, 'srp configure failed');
|
||||
throw e;
|
||||
} finally {
|
||||
InMemoryStore.set(MS_KEYS.SRP_CONFIGURE_IN_PROGRESS, false);
|
||||
}
|
||||
};
|
||||
|
||||
export const startSRPSetup = async (
|
||||
token: string,
|
||||
setupSRPRequest: SetupSRPRequest
|
||||
): Promise<SetupSRPResponse> => {
|
||||
try {
|
||||
const resp = await HTTPService.post(
|
||||
`${ENDPOINT}/users/srp/setup`,
|
||||
setupSRPRequest,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
|
||||
return resp.data as SetupSRPResponse;
|
||||
} catch (e) {
|
||||
logError(e, 'failed to post SRP attributes');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const completeSRPSetup = async (
|
||||
token: string,
|
||||
completeSRPSetupRequest: CompleteSRPSetupRequest
|
||||
) => {
|
||||
try {
|
||||
const resp = await HTTPService.post(
|
||||
`${ENDPOINT}/users/srp/complete`,
|
||||
completeSRPSetupRequest,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
return resp.data as CompleteSRPSetupResponse;
|
||||
} catch (e) {
|
||||
logError(e, 'failed to complete SRP setup');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const loginViaSRP = async (
|
||||
srpAttributes: SRPAttributes,
|
||||
kek: string
|
||||
): Promise<UserVerificationResponse> => {
|
||||
try {
|
||||
const loginSubKey = await generateLoginSubKey(kek);
|
||||
const srpClient = await generateSRPClient(
|
||||
srpAttributes.srpSalt,
|
||||
srpAttributes.srpUserID,
|
||||
loginSubKey
|
||||
);
|
||||
const srpVerifier = computeVerifierHelper(
|
||||
srpAttributes.srpSalt,
|
||||
srpAttributes.srpUserID,
|
||||
loginSubKey
|
||||
);
|
||||
addLocalLog(() => `srp verifier: ${srpVerifier}`);
|
||||
const srpA = srpClient.computeA();
|
||||
const { srpB, sessionID } = await createSRPSession(
|
||||
srpAttributes.srpUserID,
|
||||
convertBufferToBase64(srpA)
|
||||
);
|
||||
srpClient.setB(convertBase64ToBuffer(srpB));
|
||||
|
||||
const m1 = srpClient.computeM1();
|
||||
addLocalLog(() => `srp m1: ${convertBufferToBase64(m1)}`);
|
||||
const { srpM2, ...rest } = await verifySRPSession(
|
||||
sessionID,
|
||||
srpAttributes.srpUserID,
|
||||
convertBufferToBase64(m1)
|
||||
);
|
||||
addLocalLog(() => `srp verify session successful,srpM2: ${srpM2}`);
|
||||
|
||||
srpClient.checkM2(convertBase64ToBuffer(srpM2));
|
||||
|
||||
addLocalLog(() => `srp server verify successful`);
|
||||
|
||||
return rest;
|
||||
} catch (e) {
|
||||
logError(e, 'srp verify failed');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const createSRPSession = async (srpUserID: string, srpA: string) => {
|
||||
try {
|
||||
const resp = await HTTPService.post(
|
||||
`${ENDPOINT}/users/srp/create-session`,
|
||||
{
|
||||
srpUserID,
|
||||
srpA,
|
||||
}
|
||||
);
|
||||
return resp.data as CreateSRPSessionResponse;
|
||||
} catch (e) {
|
||||
logError(e, 'createSRPSession failed');
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
export const verifySRPSession = async (
|
||||
sessionID: string,
|
||||
srpUserID: string,
|
||||
srpM1: string
|
||||
) => {
|
||||
try {
|
||||
const resp = await HTTPService.post(
|
||||
`${ENDPOINT}/users/srp/verify-session`,
|
||||
{
|
||||
sessionID,
|
||||
srpUserID,
|
||||
srpM1,
|
||||
},
|
||||
null
|
||||
);
|
||||
return resp.data as UserVerificationResponse;
|
||||
} catch (e) {
|
||||
logError(e, 'verifySRPSession failed');
|
||||
if (
|
||||
e instanceof ApiError &&
|
||||
e.httpStatusCode === HttpStatusCode.Forbidden
|
||||
) {
|
||||
throw Error(CustomError.INCORRECT_PASSWORD);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const updateSRPAndKeys = async (
|
||||
token: string,
|
||||
updateSRPAndKeyRequest: UpdateSRPAndKeysRequest
|
||||
): Promise<UpdateSRPAndKeysResponse> => {
|
||||
const resp = await HTTPService.post(
|
||||
`${ENDPOINT}/users/srp/update`,
|
||||
updateSRPAndKeyRequest,
|
||||
null,
|
||||
{
|
||||
'X-Auth-Token': token,
|
||||
}
|
||||
);
|
||||
return resp.data as UpdateSRPAndKeysResponse;
|
||||
};
|
|
@ -1,12 +1,12 @@
|
|||
import QueueProcessor from 'services/queueProcessor';
|
||||
import { CustomError } from 'utils/error';
|
||||
import { retryAsyncFunction } from 'utils/network';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { addLogLine } from 'utils/logging';
|
||||
import { DedicatedConvertWorker } from 'worker/convert.worker';
|
||||
import { ComlinkWorker } from 'utils/comlink/comlinkWorker';
|
||||
import { convertBytesToHumanReadable } from 'utils/file/size';
|
||||
import { getDedicatedConvertWorker } from 'utils/comlink/ComlinkConvertWorker';
|
||||
import { logError } from '@ente/shared/sentry';
|
||||
|
||||
const WORKER_POOL_SIZE = 2;
|
||||
const MAX_CONVERSION_IN_PARALLEL = 1;
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
import * as Sentry from '@sentry/nextjs';
|
||||
import { addLocalLog, addLogLine } from 'utils/logging';
|
||||
import { getSentryUserID } from 'utils/user';
|
||||
import InMemoryStore, { MS_KEYS } from 'services/InMemoryStore';
|
||||
import { getHasOptedOutOfCrashReports } from 'utils/storage';
|
||||
import { ApiError } from 'utils/error';
|
||||
|
||||
export const logError = async (
|
||||
error: any,
|
||||
msg: string,
|
||||
info?: Record<string, unknown>,
|
||||
skipAddLogLine = false
|
||||
) => {
|
||||
const err = errorWithContext(error, msg);
|
||||
if (!skipAddLogLine) {
|
||||
if (error instanceof ApiError) {
|
||||
addLogLine(`error: ${error?.name} ${error?.message}
|
||||
msg: ${msg} errorCode: ${JSON.stringify(error?.errCode)}
|
||||
httpStatusCode: ${JSON.stringify(error?.httpStatusCode)} ${
|
||||
info ? `info: ${JSON.stringify(info)}` : ''
|
||||
}
|
||||
${error?.stack}`);
|
||||
} else {
|
||||
addLogLine(
|
||||
`error: ${error?.name} ${error?.message}
|
||||
msg: ${msg} ${info ? `info: ${JSON.stringify(info)}` : ''}
|
||||
${error?.stack}`
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!InMemoryStore.has(MS_KEYS.OPT_OUT_OF_CRASH_REPORTS)) {
|
||||
const optedOutOfCrashReports = getHasOptedOutOfCrashReports();
|
||||
InMemoryStore.set(
|
||||
MS_KEYS.OPT_OUT_OF_CRASH_REPORTS,
|
||||
optedOutOfCrashReports
|
||||
);
|
||||
}
|
||||
if (InMemoryStore.get(MS_KEYS.OPT_OUT_OF_CRASH_REPORTS)) {
|
||||
addLocalLog(() => `skipping sentry error: ${error?.name}`);
|
||||
return;
|
||||
}
|
||||
if (isErrorUnnecessaryForSentry(error)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Sentry.captureException(err, {
|
||||
level: 'info',
|
||||
user: { id: await getSentryUserID() },
|
||||
contexts: {
|
||||
...(info && {
|
||||
info: info,
|
||||
}),
|
||||
rootCause: { message: error?.message, completeError: error },
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// copy of errorWithContext to prevent importing error util
|
||||
function errorWithContext(originalError: Error, context: string) {
|
||||
const errorWithContext = new Error(context);
|
||||
errorWithContext.stack =
|
||||
errorWithContext.stack.split('\n').slice(2, 4).join('\n') +
|
||||
'\n' +
|
||||
originalError.stack;
|
||||
return errorWithContext;
|
||||
}
|
||||
|
||||
function isErrorUnnecessaryForSentry(error: any) {
|
||||
if (error?.message?.includes('Network Error')) {
|
||||
return true;
|
||||
} else if (error?.status === 401) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
import { Language } from 'constants/locale';
|
||||
import { getData, LS_KEYS, setData } from './localStorage';
|
||||
|
||||
export const isFirstLogin = () =>
|
||||
getData(LS_KEYS.IS_FIRST_LOGIN)?.status ?? false;
|
||||
|
||||
export function setIsFirstLogin(status) {
|
||||
setData(LS_KEYS.IS_FIRST_LOGIN, { status });
|
||||
}
|
||||
|
||||
export const justSignedUp = () =>
|
||||
getData(LS_KEYS.JUST_SIGNED_UP)?.status ?? false;
|
||||
|
||||
export function setJustSignedUp(status) {
|
||||
setData(LS_KEYS.JUST_SIGNED_UP, { status });
|
||||
}
|
||||
|
||||
export function getLivePhotoInfoShownCount() {
|
||||
return getData(LS_KEYS.LIVE_PHOTO_INFO_SHOWN_COUNT)?.count ?? 0;
|
||||
}
|
||||
|
||||
export function setLivePhotoInfoShownCount(count) {
|
||||
setData(LS_KEYS.LIVE_PHOTO_INFO_SHOWN_COUNT, { count });
|
||||
}
|
||||
|
||||
export function getUserLocale(): Language {
|
||||
return getData(LS_KEYS.LOCALE)?.value;
|
||||
}
|
||||
|
||||
export function getLocalMapEnabled(): boolean {
|
||||
return getData(LS_KEYS.MAP_ENABLED)?.value ?? false;
|
||||
}
|
||||
|
||||
export function setLocalMapEnabled(value: boolean) {
|
||||
setData(LS_KEYS.MAP_ENABLED, { value });
|
||||
}
|
||||
|
||||
export function getHasOptedOutOfCrashReports(): boolean {
|
||||
return getData(LS_KEYS.OPT_OUT_OF_CRASH_REPORTS)?.value ?? false;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
import { runningInBrowser } from '@ente/shared/apps/browser';
|
||||
import localForage from 'localforage';
|
||||
|
||||
if (runningInBrowser()) {
|
||||
localForage.config({
|
||||
name: 'ente-files',
|
||||
version: 1.0,
|
||||
storeName: 'files',
|
||||
});
|
||||
}
|
||||
export default localForage;
|
|
@ -1,68 +0,0 @@
|
|||
import { logError } from 'utils/sentry';
|
||||
|
||||
export enum LS_KEYS {
|
||||
USER = 'user',
|
||||
SESSION = 'session',
|
||||
KEY_ATTRIBUTES = 'keyAttributes',
|
||||
ORIGINAL_KEY_ATTRIBUTES = 'originalKeyAttributes',
|
||||
SUBSCRIPTION = 'subscription',
|
||||
FAMILY_DATA = 'familyData',
|
||||
PLANS = 'plans',
|
||||
IS_FIRST_LOGIN = 'isFirstLogin',
|
||||
JUST_SIGNED_UP = 'justSignedUp',
|
||||
SHOW_BACK_BUTTON = 'showBackButton',
|
||||
EXPORT = 'export',
|
||||
AnonymizedUserID = 'anonymizedUserID',
|
||||
THUMBNAIL_FIX_STATE = 'thumbnailFixState',
|
||||
LIVE_PHOTO_INFO_SHOWN_COUNT = 'livePhotoInfoShownCount',
|
||||
LOGS = 'logs',
|
||||
USER_DETAILS = 'userDetails',
|
||||
COLLECTION_SORT_BY = 'collectionSortBy',
|
||||
THEME = 'theme',
|
||||
WAIT_TIME = 'waitTime',
|
||||
API_ENDPOINT = 'apiEndpoint',
|
||||
LOCALE = 'locale',
|
||||
MAP_ENABLED = 'mapEnabled',
|
||||
SRP_SETUP_ATTRIBUTES = 'srpSetupAttributes',
|
||||
SRP_ATTRIBUTES = 'srpAttributes',
|
||||
OPT_OUT_OF_CRASH_REPORTS = 'optOutOfCrashReports',
|
||||
CF_PROXY_DISABLED = 'cfProxyDisabled',
|
||||
}
|
||||
|
||||
export const setData = (key: LS_KEYS, value: object) => {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
};
|
||||
|
||||
export const removeData = (key: LS_KEYS) => {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
localStorage.removeItem(key);
|
||||
};
|
||||
|
||||
export const getData = (key: LS_KEYS) => {
|
||||
try {
|
||||
if (
|
||||
typeof localStorage === 'undefined' ||
|
||||
typeof key === 'undefined' ||
|
||||
typeof localStorage.getItem(key) === 'undefined' ||
|
||||
localStorage.getItem(key) === 'undefined'
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
const data = localStorage.getItem(key);
|
||||
return data && JSON.parse(data);
|
||||
} catch (e) {
|
||||
logError(e, 'Failed to Parse JSON for key ' + key);
|
||||
}
|
||||
};
|
||||
|
||||
export const clearData = () => {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
localStorage.clear();
|
||||
};
|
|
@ -1,32 +0,0 @@
|
|||
export enum SESSION_KEYS {
|
||||
ENCRYPTION_KEY = 'encryptionKey',
|
||||
KEY_ENCRYPTION_KEY = 'keyEncryptionKey',
|
||||
}
|
||||
|
||||
export const setKey = (key: SESSION_KEYS, value: object) => {
|
||||
if (typeof sessionStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
sessionStorage.setItem(key, JSON.stringify(value));
|
||||
};
|
||||
|
||||
export const getKey = (key: SESSION_KEYS) => {
|
||||
if (typeof sessionStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
return JSON.parse(sessionStorage.getItem(key));
|
||||
};
|
||||
|
||||
export const removeKey = (key: SESSION_KEYS) => {
|
||||
if (typeof sessionStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
sessionStorage.removeItem(key);
|
||||
};
|
||||
|
||||
export const clearKeys = () => {
|
||||
if (typeof sessionStorage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
sessionStorage.clear();
|
||||
};
|
|
@ -1,45 +0,0 @@
|
|||
import { FamilyData, FamilyMember, User } from 'types/user';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
|
||||
export function getLocalFamilyData(): FamilyData {
|
||||
return getData(LS_KEYS.FAMILY_DATA);
|
||||
}
|
||||
|
||||
// isPartOfFamily return true if the current user is part of some family plan
|
||||
export function isPartOfFamily(familyData: FamilyData): boolean {
|
||||
return Boolean(
|
||||
familyData && familyData.members && familyData.members.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
// hasNonAdminFamilyMembers return true if the admin user has members in his family
|
||||
export function hasNonAdminFamilyMembers(familyData: FamilyData): boolean {
|
||||
return Boolean(isPartOfFamily(familyData) && familyData.members.length > 1);
|
||||
}
|
||||
|
||||
export function isFamilyAdmin(familyData: FamilyData): boolean {
|
||||
const familyAdmin: FamilyMember = getFamilyPlanAdmin(familyData);
|
||||
const user: User = getData(LS_KEYS.USER);
|
||||
return familyAdmin.email === user.email;
|
||||
}
|
||||
|
||||
export function getFamilyPlanAdmin(familyData: FamilyData): FamilyMember {
|
||||
if (isPartOfFamily(familyData)) {
|
||||
return familyData.members.find((x) => x.isAdmin);
|
||||
} else {
|
||||
logError(
|
||||
Error(
|
||||
'verify user is part of family plan before calling this method'
|
||||
),
|
||||
'invalid getFamilyPlanAdmin call'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function getTotalFamilyUsage(familyData: FamilyData): number {
|
||||
return familyData.members.reduce(
|
||||
(sum, currentMember) => sum + currentMember.usage,
|
||||
0
|
||||
);
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
import isElectron from 'is-electron';
|
||||
import { UserDetails } from 'types/user';
|
||||
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
||||
// import ElectronService from 'services/electron/common';
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
export function makeID(length) {
|
||||
let result = '';
|
||||
const characters =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const charactersLength = characters.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += characters.charAt(
|
||||
Math.floor(Math.random() * charactersLength)
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getSentryUserID() {
|
||||
if (isElectron()) {
|
||||
// return await ElectronService.getSentryUserID();
|
||||
} else {
|
||||
let anonymizeUserID = getData(LS_KEYS.AnonymizedUserID)?.id;
|
||||
if (!anonymizeUserID) {
|
||||
anonymizeUserID = makeID(6);
|
||||
setData(LS_KEYS.AnonymizedUserID, { id: anonymizeUserID });
|
||||
}
|
||||
return anonymizeUserID;
|
||||
}
|
||||
}
|
||||
|
||||
export function getLocalUserDetails(): UserDetails {
|
||||
return getData(LS_KEYS.USER_DETAILS)?.value;
|
||||
}
|
||||
|
||||
export const isInternalUser = () => {
|
||||
const userEmail = getData(LS_KEYS.USER)?.email;
|
||||
if (!userEmail) return false;
|
||||
|
||||
return (
|
||||
userEmail.endsWith('@ente.io') || userEmail === 'kr.anand619@gmail.com'
|
||||
);
|
||||
};
|
||||
|
||||
export const convertBufferToBase64 = (buffer: Buffer) => {
|
||||
return buffer.toString('base64');
|
||||
};
|
||||
|
||||
export const convertBase64ToBuffer = (base64: string) => {
|
||||
return Buffer.from(base64, 'base64');
|
||||
};
|
Loading…
Add table
Reference in a new issue