[Cast] Use common shared packages
This commit is contained in:
parent
a3bf49e2d2
commit
1e583414d3
|
@ -1,53 +1,5 @@
|
||||||
import * as Sentry from '@sentry/nextjs';
|
import { setupSentry } from '@ente/shared/sentry/config/sentry.config.base';
|
||||||
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';
|
|
||||||
|
|
||||||
const HAS_OPTED_OUT_OF_CRASH_REPORTING =
|
const DEFAULT_SENTRY_DSN =
|
||||||
runningInBrowser() && getHasOptedOutOfCrashReports();
|
'https://bd3656fc40d74d5e8f278132817963a3@sentry.ente.io/2';
|
||||||
|
setupSentry(DEFAULT_SENTRY_DSN);
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
|
@ -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 { syncFiles } from 'services/fileService';
|
||||||
import { EnteFile } from 'types/file';
|
import { EnteFile } from 'types/file';
|
||||||
import { downloadFileAsBlob, isRawFileFromFileName } from 'utils/file';
|
import { downloadFileAsBlob, isRawFileFromFileName } from 'utils/file';
|
||||||
|
import { logError } from 'utils/sentry';
|
||||||
|
|
||||||
export const SlideshowContext = createContext<{
|
export const SlideshowContext = createContext<{
|
||||||
showNextSlide: () => void;
|
showNextSlide: () => void;
|
||||||
|
@ -36,9 +37,13 @@ export default function Slideshow() {
|
||||||
'targetCollectionKey'
|
'targetCollectionKey'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const castToken = window.localStorage.getItem('token');
|
||||||
|
|
||||||
const collection = await getCollectionWithKey(
|
const collection = await getCollectionWithKey(
|
||||||
Number(requestedCollectionID),
|
Number(requestedCollectionID),
|
||||||
requestedCollectionKey
|
requestedCollectionKey,
|
||||||
|
'cast',
|
||||||
|
castToken
|
||||||
);
|
);
|
||||||
|
|
||||||
const files = await syncFiles('normal', [collection], () => {});
|
const files = await syncFiles('normal', [collection], () => {});
|
||||||
|
@ -48,7 +53,9 @@ export default function Slideshow() {
|
||||||
files.filter((file) => isFileEligibleForCast(file))
|
files.filter((file) => isFileEligibleForCast(file))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch (e) {
|
||||||
|
logError(e, 'error during sync');
|
||||||
|
alert('error, redirect to home' + e);
|
||||||
router.push('/');
|
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 { getActualKey } from '@ente/shared/user';
|
||||||
import { batch } from '@ente/shared/batch';
|
import { batch } from '@ente/shared/batch';
|
||||||
import { getPublicKey } from './userService';
|
|
||||||
import HTTPService from './HTTPService';
|
|
||||||
import { EnteFile } from 'types/file';
|
import { EnteFile } from 'types/file';
|
||||||
import { logError } from 'utils/sentry';
|
|
||||||
import { CustomError } from 'utils/error';
|
import { CustomError } from 'utils/error';
|
||||||
import {
|
import {
|
||||||
sortFiles,
|
sortFiles,
|
||||||
|
@ -71,6 +66,10 @@ import { EncryptedMagicMetadata } from 'types/magicMetadata';
|
||||||
import { VISIBILITY_STATE } from 'types/magicMetadata';
|
import { VISIBILITY_STATE } from 'types/magicMetadata';
|
||||||
import { getEndpoint } from '@ente/shared/network/api';
|
import { getEndpoint } from '@ente/shared/network/api';
|
||||||
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
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 ENDPOINT = getEndpoint();
|
||||||
const COLLECTION_TABLE = 'collections';
|
const COLLECTION_TABLE = 'collections';
|
||||||
|
@ -347,10 +346,17 @@ export const getCollection = async (
|
||||||
|
|
||||||
export const getCollectionWithKey = async (
|
export const getCollectionWithKey = async (
|
||||||
collectionID: number,
|
collectionID: number,
|
||||||
key: string
|
key: string,
|
||||||
|
tokenType: 'user' | 'cast' = 'user',
|
||||||
|
castToken?: string
|
||||||
): Promise<Collection> => {
|
): Promise<Collection> => {
|
||||||
try {
|
try {
|
||||||
const token = getToken();
|
let token;
|
||||||
|
if (tokenType === 'user') {
|
||||||
|
token = getToken();
|
||||||
|
} else {
|
||||||
|
token = castToken!;
|
||||||
|
}
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return;
|
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 (
|
export const unshareCollection = async (
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
withUserEmail: string
|
withUserEmail: string
|
||||||
|
|
|
@ -3,10 +3,8 @@ import {
|
||||||
getRenderableFileURL,
|
getRenderableFileURL,
|
||||||
createTypedObjectURL,
|
createTypedObjectURL,
|
||||||
} from 'utils/file';
|
} from 'utils/file';
|
||||||
import HTTPService from './HTTPService';
|
|
||||||
import { EnteFile } from 'types/file';
|
import { EnteFile } from 'types/file';
|
||||||
|
|
||||||
import { logError } from 'utils/sentry';
|
|
||||||
import { FILE_TYPE } from 'constants/file';
|
import { FILE_TYPE } from 'constants/file';
|
||||||
import { CustomError } from 'utils/error';
|
import { CustomError } from 'utils/error';
|
||||||
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
|
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
|
||||||
|
@ -16,9 +14,10 @@ import { Remote } from 'comlink';
|
||||||
import { DedicatedCryptoWorker } from 'worker/crypto.worker';
|
import { DedicatedCryptoWorker } from 'worker/crypto.worker';
|
||||||
import { LimitedCache } from 'types/cache';
|
import { LimitedCache } from 'types/cache';
|
||||||
import { retryAsyncFunction } from 'utils/network';
|
import { retryAsyncFunction } from 'utils/network';
|
||||||
import { addLogLine } from 'utils/logging';
|
|
||||||
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
||||||
import { getFileURL, getThumbnailURL } from '@ente/shared/network/api';
|
import { getFileURL, getThumbnailURL } from '@ente/shared/network/api';
|
||||||
|
import HTTPService from '@ente/shared/network/HTTPService';
|
||||||
|
import { logError } from '@ente/shared/sentry';
|
||||||
|
|
||||||
class DownloadManager {
|
class DownloadManager {
|
||||||
private fileObjectURLPromise = new Map<
|
private fileObjectURLPromise = new Map<
|
||||||
|
|
|
@ -3,7 +3,7 @@ import localForage from 'utils/storage/localForage';
|
||||||
|
|
||||||
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
import { getToken } from '@ente/shared/storage/localStorage/helpers';
|
||||||
import { Collection } from 'types/collection';
|
import { Collection } from 'types/collection';
|
||||||
import HTTPService from './HTTPService';
|
|
||||||
import { logError } from 'utils/sentry';
|
import { logError } from 'utils/sentry';
|
||||||
import {
|
import {
|
||||||
decryptFile,
|
decryptFile,
|
||||||
|
@ -29,6 +29,7 @@ import {
|
||||||
} from './collectionService';
|
} from './collectionService';
|
||||||
import { REQUEST_BATCH_SIZE } from 'constants/api';
|
import { REQUEST_BATCH_SIZE } from 'constants/api';
|
||||||
import { batch } from '@ente/shared/batch';
|
import { batch } from '@ente/shared/batch';
|
||||||
|
import HTTPService from '@ente/shared/network/HTTPService';
|
||||||
|
|
||||||
const ENDPOINT = getEndpoint();
|
const ENDPOINT = getEndpoint();
|
||||||
const FILES_TABLE = 'files';
|
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,
|
getRenderableFileURL,
|
||||||
createTypedObjectURL,
|
createTypedObjectURL,
|
||||||
} from 'utils/file';
|
} from 'utils/file';
|
||||||
import HTTPService from './HTTPService';
|
|
||||||
import { EnteFile } from 'types/file';
|
import { EnteFile } from 'types/file';
|
||||||
|
|
||||||
import { logError } from 'utils/sentry';
|
|
||||||
import { FILE_TYPE } from 'constants/file';
|
import { FILE_TYPE } from 'constants/file';
|
||||||
import { CustomError } from 'utils/error';
|
import { CustomError } from 'utils/error';
|
||||||
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
|
import ComlinkCryptoWorker from 'utils/comlink/ComlinkCryptoWorker';
|
||||||
|
@ -17,6 +15,8 @@ import {
|
||||||
getPublicCollectionFileURL,
|
getPublicCollectionFileURL,
|
||||||
getPublicCollectionThumbnailURL,
|
getPublicCollectionThumbnailURL,
|
||||||
} from '@ente/shared/network/api';
|
} from '@ente/shared/network/api';
|
||||||
|
import HTTPService from '@ente/shared/network/HTTPService';
|
||||||
|
import { logError } from '@ente/shared/sentry';
|
||||||
|
|
||||||
class PublicCollectionDownloadManager {
|
class PublicCollectionDownloadManager {
|
||||||
private fileObjectURLPromise = new Map<
|
private fileObjectURLPromise = new Map<
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { logError } from '@ente/shared/sentry';
|
||||||
import { ElectronFile } from 'types/upload';
|
import { ElectronFile } from 'types/upload';
|
||||||
import { convertBytesToHumanReadable } from 'utils/file/size';
|
import { convertBytesToHumanReadable } from 'utils/file/size';
|
||||||
import { logError } from 'utils/sentry';
|
|
||||||
|
|
||||||
export async function getUint8ArrayView(
|
export async function getUint8ArrayView(
|
||||||
file: Blob | ElectronFile
|
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 QueueProcessor from 'services/queueProcessor';
|
||||||
import { CustomError } from 'utils/error';
|
import { CustomError } from 'utils/error';
|
||||||
import { retryAsyncFunction } from 'utils/network';
|
import { retryAsyncFunction } from 'utils/network';
|
||||||
import { logError } from 'utils/sentry';
|
|
||||||
import { addLogLine } from 'utils/logging';
|
import { addLogLine } from 'utils/logging';
|
||||||
import { DedicatedConvertWorker } from 'worker/convert.worker';
|
import { DedicatedConvertWorker } from 'worker/convert.worker';
|
||||||
import { ComlinkWorker } from 'utils/comlink/comlinkWorker';
|
import { ComlinkWorker } from 'utils/comlink/comlinkWorker';
|
||||||
import { convertBytesToHumanReadable } from 'utils/file/size';
|
import { convertBytesToHumanReadable } from 'utils/file/size';
|
||||||
import { getDedicatedConvertWorker } from 'utils/comlink/ComlinkConvertWorker';
|
import { getDedicatedConvertWorker } from 'utils/comlink/ComlinkConvertWorker';
|
||||||
|
import { logError } from '@ente/shared/sentry';
|
||||||
|
|
||||||
const WORKER_POOL_SIZE = 2;
|
const WORKER_POOL_SIZE = 2;
|
||||||
const MAX_CONVERSION_IN_PARALLEL = 1;
|
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…
Reference in a new issue