patch issues

This commit is contained in:
Abhinav 2023-11-10 15:08:10 +05:30
parent 9ad0954a37
commit 52c2f89412
35 changed files with 419 additions and 62 deletions

Binary file not shown.

View file

@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { TOTP, HOTP } from 'otpauth';
import { Code } from 'types/authenticator/code';
import { Code } from 'types/code';
import TimerProgress from './TimerProgress';
import { t } from 'i18next';
import { ButtonBase, Snackbar } from '@mui/material';

View file

@ -1,6 +1,6 @@
import React, { useContext, useEffect, useState } from 'react';
import OTPDisplay from 'components/OTPDisplay';
import { getAuthCodes } from 'services/authenticator/authenticatorService';
import { getAuthCodes } from 'services';
import { CustomError } from '@ente/shared/error';
import { PHOTOS_PAGES as PAGES } from '@ente/shared/constants/pages';
import { useRouter } from 'next/router';

View file

@ -1,7 +1,7 @@
import { HttpStatusCode } from 'axios';
import HTTPService from '@ente/shared/network/HTTPService';
import { AuthEntity, AuthKey } from 'types/authenticator/api';
import { Code } from 'types/authenticator/code';
import { AuthEntity, AuthKey } from 'types/api';
import { Code } from 'types/code';
import ComlinkCryptoWorker from '@ente/shared/crypto';
import { getEndpoint } from '@ente/shared/network/api';
import { getActualKey } from '@ente/shared/user';

View file

@ -21,7 +21,6 @@
"@tensorflow/tfjs-core": "^4.10.0",
"@tensorflow/tfjs-tflite": "^0.0.1-alpha.7",
"@zip.js/zip.js": "^2.4.2",
"axios": "^1.4.0",
"bip39": "^3.0.4",
"blazeface-back": "^0.0.9",
"bootstrap": "^4.5.2",

View file

@ -24,6 +24,7 @@
"@ente/shared": "*",
"@mui/icons-material": "5.14.1",
"@mui/material": "5.11.16",
"axios": "^1.4.0",
"is-electron": "^2.2.2",
"next": "13.5.6",
"react": "18.2.0",

View file

@ -13,7 +13,6 @@ import {
UpdateSRPAndKeysRequest,
UpdateSRPAndKeysResponse,
} from '@ente/accounts/types/srp';
import { getToken } from '@ente/shared/storage/localStorage/helpers';
import { ApiError, CustomError } from '@ente/shared/error';
import { HttpStatusCode } from 'axios';
import { logError } from '@ente/shared/sentry';
@ -35,10 +34,10 @@ export const getSRPAttributes = async (
};
export const startSRPSetup = async (
token: string,
setupSRPRequest: SetupSRPRequest
): Promise<SetupSRPResponse> => {
try {
const token = getToken();
const resp = await HTTPService.post(
`${ENDPOINT}/users/srp/setup`,
setupSRPRequest,
@ -56,10 +55,10 @@ export const startSRPSetup = async (
};
export const completeSRPSetup = async (
token: string,
completeSRPSetupRequest: CompleteSRPSetupRequest
) => {
try {
const token = getToken();
const resp = await HTTPService.post(
`${ENDPOINT}/users/srp/complete`,
completeSRPSetupRequest,

View file

@ -3,7 +3,7 @@ import { getEndpoint } from '@ente/shared/network/api';
import { getToken } from '@ente/shared/storage/localStorage/helpers';
import { KeyAttributes } from '@ente/shared/user/types';
import { ApiError } from '@ente/shared/error';
import { ApiError, CustomError } from '@ente/shared/error';
import { HttpStatusCode } from 'axios';
import {
UserVerificationResponse,
@ -14,22 +14,22 @@ import {
} from '@ente/accounts/types/user';
import { B64EncryptionResult } from '@ente/shared/crypto/types';
import { logError } from '@ente/shared/sentry';
import { APPS, OTT_CLIENTS } from '@ente/shared/apps/constants';
const ENDPOINT = getEndpoint();
export const sendOtt = (appName: string, email: string) => {
export const sendOtt = (appName: APPS, email: string) => {
return HTTPService.post(`${ENDPOINT}/users/ott`, {
email,
client: appName,
client: OTT_CLIENTS.get(appName),
});
};
export const verifyOtt = (email: string, ott: string) =>
HTTPService.post(`${ENDPOINT}/users/verify-email`, { email, ott });
export const putAttributes = async (keyAttributes: KeyAttributes) => {
const token = getToken();
await HTTPService.put(
export const putAttributes = (token: string, keyAttributes: KeyAttributes) =>
HTTPService.put(
`${ENDPOINT}/users/attributes`,
{ keyAttributes },
undefined,
@ -37,23 +37,24 @@ export const putAttributes = async (keyAttributes: KeyAttributes) => {
'X-Auth-Token': token,
}
);
};
export const _logout = async () => {
// ignore if token missing can be triggered during sign up.
if (!getToken()) return true;
try {
const token = getToken();
await HTTPService.post(`${ENDPOINT}/users/logout`, null, undefined, {
'X-Auth-Token': getToken(),
'X-Auth-Token': token,
});
return true;
} catch (e) {
// ignore if token missing can be triggered during sign up.
if (e instanceof Error && e.message === CustomError.TOKEN_MISSING) {
return;
}
// ignore if unauthorized, can be triggered during on token expiry.
if (
else if (
e instanceof ApiError &&
e.httpStatusCode === HttpStatusCode.Unauthorized
) {
return true;
return;
}
logError(e, '/users/logout failed');
throw e;
@ -88,9 +89,6 @@ export const removeTwoFactor = async (sessionID: string, secret: string) => {
};
export const changeEmail = async (email: string, ott: string) => {
if (!getToken()) {
return null;
}
await HTTPService.post(
`${ENDPOINT}/users/change-email`,
{
@ -105,9 +103,6 @@ export const changeEmail = async (email: string, ott: string) => {
};
export const sendOTTForEmailChange = async (email: string) => {
if (!getToken()) {
return null;
}
await HTTPService.post(`${ENDPOINT}/users/ott`, {
email,
client: 'web',

View file

@ -12,10 +12,11 @@ import { Input } from '@mui/material';
import SingleInputForm, {
SingleInputFormProps,
} from '@ente/shared/components/SingleInputForm';
import { APPS } from '@ente/shared/apps/constants';
interface LoginProps {
signUp: () => void;
appName: string;
appName: APPS;
}
export default function Login(props: LoginProps) {

View file

@ -91,7 +91,7 @@ export default function ChangePassword({ appName, router }: PageProps) {
const srpA = convertBufferToBase64(srpClient.computeA());
const { setupID, srpB } = await startSRPSetup({
const { setupID, srpB } = await startSRPSetup(token, {
srpUserID,
srpSalt,
srpVerifier,

View file

@ -137,9 +137,6 @@ export default function Credentials({
const getKeyAttributes: VerifyMasterPasswordFormProps['getKeyAttributes'] =
async (kek: string) => {
try {
if (!srpAttributes) {
throw Error('SRP attributes are missing');
}
const cryptoWorker = await ComlinkCryptoWorker.getInstance();
const response = await loginViaSRP(srpAttributes, kek);
if (response.twoFactorSessionID) {
@ -176,10 +173,7 @@ export default function Credentials({
return keyAttributes;
}
} catch (e) {
if (
e instanceof Error &&
e.message !== CustomError.TWO_FACTOR_ENABLED
) {
if (e.message !== CustomError.TWO_FACTOR_ENABLED) {
logError(e, 'getKeyAttributes failed');
}
throw e;

View file

@ -31,6 +31,7 @@ import LinkButton from '@ente/shared/components/LinkButton';
import { PageProps } from '@ente/shared/apps/types';
export default function Generate({ router, appContext, appName }: PageProps) {
const [token, setToken] = useState<string>();
const [user, setUser] = useState<User>();
const [recoverModalView, setRecoveryModalView] = useState(false);
const [loading, setLoading] = useState(true);
@ -54,6 +55,7 @@ export default function Generate({ router, appContext, appName }: PageProps) {
} else if (keyAttributes?.encryptedKey) {
router.push(PAGES.CREDENTIALS);
} else {
setToken(user.token);
setLoading(false);
}
};
@ -66,7 +68,7 @@ export default function Generate({ router, appContext, appName }: PageProps) {
const { keyAttributes, masterKey, srpSetupAttributes } =
await generateKeyAndSRPAttributes(passphrase);
await putAttributes(keyAttributes);
await putAttributes(token, keyAttributes);
await configureSRP(srpSetupAttributes);
await generateAndSaveIntermediateKeyAttributes(
passphrase,

View file

@ -36,7 +36,6 @@ export default function Recover({ router, appContext }: PageProps) {
useState(false);
useEffect(() => {
router.prefetch(PAGES.GALLERY);
const user = getData(LS_KEYS.USER);
if (!user || !user.email || !user.twoFactorSessionID) {
router.push(PAGES.ROOT);

View file

@ -8,7 +8,6 @@ import VerifyTwoFactor, {
} from '@ente/accounts/components/two-factor/VerifyForm';
import { encryptWithRecoveryKey } from '@ente/shared/crypto/helpers';
import { setData, LS_KEYS, getData } from '@ente/shared/storage/localStorage';
import { PAGES } from '@ente/accounts/constants/pages';
import { TwoFactorSecret } from '@ente/accounts/types/user';
import Card from '@mui/material/Card';
import { Box, CardContent, Typography } from '@mui/material';
@ -16,7 +15,7 @@ import { TwoFactorSetup } from '@ente/accounts/components/two-factor/setup';
import LinkButton from '@ente/shared/components/LinkButton';
import { PageProps } from '@ente/shared/apps/types';
import { logError } from '@ente/shared/sentry';
import { APPS } from '@ente/shared/apps/constants';
import { APP_HOMES } from '@ente/shared/apps/constants';
export enum SetupMode {
QR_CODE,
@ -36,7 +35,6 @@ export default function SetupTwoFactor({ router, appName }: PageProps) {
const twoFactorSecret = await setupTwoFactor();
setTwoFactorSecret(twoFactorSecret);
} catch (e) {
console.log(e);
logError(e, 'failed to get two factor setup code');
}
};
@ -56,11 +54,7 @@ export default function SetupTwoFactor({ router, appName }: PageProps) {
...getData(LS_KEYS.USER),
isTwoFactorEnabled: true,
});
if (appName === APPS.AUTH) {
router.push(PAGES.AUTH);
} else {
router.push(PAGES.GALLERY);
}
router.push(APP_HOMES.get(appName));
};
return (

View file

@ -99,6 +99,7 @@ export default function VerifyPage({ appContext, router, appName }: PageProps) {
} else {
if (getData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES)) {
await putAttributes(
token,
getData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES)
);
}

View file

@ -16,6 +16,7 @@ import { generateLoginSubKey } from '@ente/shared/crypto/helpers';
import { UserVerificationResponse } from '@ente/accounts/types/user';
import { logError } from '@ente/shared/sentry';
import { addLocalLog } from '@ente/shared/logging';
import { getToken } from '@ente/shared/storage/localStorage/helpers';
const SRP_PARAMS = SRP.params['4096'];
@ -42,7 +43,8 @@ export const configureSRP = async ({
const srpA = convertBufferToBase64(srpClient.computeA());
addLocalLog(() => `srp a: ${srpA}`);
const { setupID, srpB } = await startSRPSetup({
const token = getToken();
const { setupID, srpB } = await startSRPSetup(token, {
srpA,
srpUserID,
srpSalt,
@ -53,7 +55,7 @@ export const configureSRP = async ({
const srpM1 = convertBufferToBase64(srpClient.computeM1());
const { srpM2 } = await completeSRPSetup({
const { srpM2 } = await completeSRPSetup(token, {
srpM1,
setupID,
});

View file

@ -3,11 +3,13 @@ import { _logout } from '../api/user';
import { PAGES } from '../constants/pages';
import { clearKeys } from '@ente/shared/storage/sessionStorage';
import { clearData } from '@ente/shared/storage/localStorage';
import { deleteAllCache } from '@ente/shared/storage/cacheStorage/helpers';
import { logError } from '@ente/shared/sentry';
import { clearFiles } from '@ente/shared/storage/localForage/helpers';
import router from 'next/router';
import ElectronAPIs from '@ente/shared/electron';
import isElectron from 'is-electron';
import ElectronAPIs from '@ente/shared/electron';
import { Events, eventBus } from '@ente/shared/events';
export const logoutUser = async () => {
try {
@ -15,7 +17,6 @@ export const logoutUser = async () => {
await _logout();
} catch (e) {
// ignore
logError(e, 'clear InMemoryStore failed');
}
try {
InMemoryStore.clear();
@ -33,11 +34,11 @@ export const logoutUser = async () => {
} catch (e) {
logError(e, 'clearData failed');
}
// try {
// await deleteAllCache();
// } catch (e) {
// logError(e, 'deleteAllCache failed');
// }
try {
await deleteAllCache();
} catch (e) {
logError(e, 'deleteAllCache failed');
}
try {
await clearFiles();
} catch (e) {
@ -50,11 +51,11 @@ export const logoutUser = async () => {
logError(e, 'clearElectronStore failed');
}
}
// try {
// eventBus.emit(Events.LOGOUT);
// } catch (e) {
// logError(e, 'Error in logout handlers');
// }
try {
eventBus.emit(Events.LOGOUT);
} catch (e) {
logError(e, 'Error in logout handlers');
}
router.push(PAGES.ROOT);
} catch (e) {
logError(e, 'logoutUser failed');

View file

@ -29,3 +29,8 @@ export const APP_HOMES = new Map([
[APPS.PHOTOS, PHOTOS_PAGES.GALLERY],
[APPS.AUTH, AUTH_PAGES.AUTH],
]);
export const OTT_CLIENTS = new Map([
[APPS.PHOTOS, 'web'],
[APPS.AUTH, 'totp'],
]);

View file

@ -1,3 +1,7 @@
import { LimitedCache } from '@ente/shared/storage/cacheStorage/types';
import { ElectronFile } from '@ente/shared/upload/types';
import { WatchMapping } from '@ente/shared/watchFolder/types';
export interface AppUpdateInfo {
autoUpdatable: boolean;
version: string;
@ -14,18 +18,48 @@ export interface ElectronAPIsType {
selectDirectory: () => Promise<string>;
sendNotification: (content: string) => void;
readTextFile: (path: string) => Promise<string>;
showUploadFilesDialog: () => Promise<ElectronFile[]>;
showUploadDirsDialog: () => Promise<ElectronFile[]>;
getPendingUploads: () => Promise<{
files: ElectronFile[];
collectionName: string;
type: string;
}>;
setToUploadFiles: (type: string, filePaths: string[]) => void;
showUploadZipDialog: () => Promise<{
zipPaths: string[];
files: ElectronFile[];
}>;
getElectronFilesFromGoogleZip: (
filePath: string
) => Promise<ElectronFile[]>;
setToUploadCollection: (collectionName: string) => void;
getDirFiles: (dirPath: string) => Promise<ElectronFile[]>;
getWatchMappings: () => WatchMapping[];
updateWatchMappingSyncedFiles: (
folderPath: string,
files: WatchMapping['syncedFiles']
) => void;
updateWatchMappingIgnoredFiles: (
folderPath: string,
files: WatchMapping['ignoredFiles']
) => void;
addWatchMapping: (
collectionName: string,
folderPath: string,
uploadStrategy: number
) => Promise<void>;
removeWatchMapping: (folderPath: string) => Promise<void>;
registerWatcherFunctions: (
addFile: (file: ElectronFile) => Promise<void>,
removeFile: (path: string) => Promise<void>,
removeFolder: (folderPath: string) => Promise<void>
) => void;
isFolder: (dirPath: string) => Promise<boolean>;
clearElectronStore: () => void;
setEncryptionKey: (encryptionKey: string) => Promise<void>;
getEncryptionKey: () => Promise<string>;
openDiskCache: (cacheName: string) => Promise<LimitedCache>;
deleteDiskCache: (cacheName: string) => Promise<boolean>;
logToDisk: (msg: string) => void;
convertToJPEG: (
@ -40,7 +74,18 @@ export interface ElectronAPIsType {
skipAppUpdate: (version: string) => void;
getSentryUserID: () => Promise<string>;
getAppVersion: () => Promise<string>;
runFFmpegCmd: (
cmd: string[],
inputFile: File | ElectronFile,
outputFileName: string,
dontTimeout?: boolean
) => Promise<File>;
muteUpdateNotification: (version: string) => void;
generateImageThumbnail: (
inputFile: File | ElectronFile,
maxDimension: number,
maxSize: number
) => Promise<Uint8Array>;
logRendererProcessMemoryUsage: (message: string) => Promise<void>;
registerForegroundEventListener: (onForeground: () => void) => void;
openDirectory: (dirPath: string) => Promise<void>;
@ -49,4 +94,7 @@ export interface ElectronAPIsType {
deleteFile: (path: string) => void;
rename: (oldPath: string, newPath: string) => Promise<void>;
updateOptOutOfCrashReports: (optOut: boolean) => Promise<void>;
computeImageEmbedding: (imageData: Uint8Array) => Promise<Float32Array>;
computeTextEmbedding: (text: string) => Promise<Float32Array>;
getPlatform: () => Promise<'mac' | 'windows' | 'linux'>;
}

View file

@ -0,0 +1,12 @@
import { EventEmitter } from 'eventemitter3';
// When registering event handlers,
// handle errors to avoid unhandled rejection or propagation to emit call
export enum Events {
LOGOUT = 'logout',
FILE_UPLOADED = 'fileUploaded',
LOCAL_FILES_UPDATED = 'localFilesUpdated',
}
export const eventBus = new EventEmitter<Events>();

View file

@ -1,3 +1,13 @@
import isElectron from 'is-electron';
export function runningInBrowser() {
return typeof window !== 'undefined';
}
export function runningInWorker() {
return typeof importScripts === 'function';
}
export function runningInElectron() {
return isElectron();
}

View file

@ -7,6 +7,8 @@ import isElectron from 'is-electron';
import { getAppEnv } from '@ente/shared/apps/env';
import { APP_ENV } from '@ente/shared/apps/constants';
import { isDisableSentryFlagSet } from '@ente/shared/apps/env';
import { ApiError } from '../error';
import { HttpStatusCode } from 'axios';
export async function getSentryUserID() {
if (isElectron()) {
@ -37,7 +39,10 @@ function makeID(length) {
export function isErrorUnnecessaryForSentry(error: any) {
if (error?.message?.includes('Network Error')) {
return true;
} else if (error?.status === 401) {
} else if (
error instanceof ApiError &&
error.httpStatusCode === HttpStatusCode.Unauthorized
) {
return true;
}
return false;

View file

@ -0,0 +1,5 @@
export enum CACHES {
THUMBS = 'thumbs',
FACE_CROPS = 'face-crops',
FILES = 'files',
}

View file

@ -0,0 +1,48 @@
import { LimitedCacheStorage } from './types';
import { runningInElectron, runningInWorker } from '@ente/shared/platform';
import { WorkerElectronCacheStorageService } from './workerElectron/service';
import ElectronAPIs from '@ente/shared/electron';
class cacheStorageFactory {
workerElectronCacheStorageServiceInstance: WorkerElectronCacheStorageService;
getCacheStorage(): LimitedCacheStorage {
if (runningInElectron()) {
if (runningInWorker()) {
if (!this.workerElectronCacheStorageServiceInstance) {
this.workerElectronCacheStorageServiceInstance =
new WorkerElectronCacheStorageService();
}
return this.workerElectronCacheStorageServiceInstance;
} else {
return {
open(cacheName) {
return ElectronAPIs.openDiskCache(cacheName);
},
delete(cacheName) {
return ElectronAPIs.deleteDiskCache(cacheName);
},
};
}
} else {
return transformBrowserCacheStorageToLimitedCacheStorage(caches);
}
}
}
export const CacheStorageFactory = new cacheStorageFactory();
function transformBrowserCacheStorageToLimitedCacheStorage(
caches: CacheStorage
): LimitedCacheStorage {
return {
async open(cacheName) {
const cache = await caches.open(cacheName);
return {
match: cache.match.bind(cache),
put: cache.put.bind(cache),
delete: cache.delete.bind(cache),
};
},
delete: caches.delete.bind(caches),
};
}

View file

@ -0,0 +1,48 @@
import { CACHES } from './constants';
import { CacheStorageService } from '.';
import { logError } from '@ente/shared/sentry';
export async function cached(
cacheName: string,
id: string,
get: () => Promise<Blob>
): Promise<Blob> {
const cache = await CacheStorageService.open(cacheName);
const cacheResponse = await cache.match(id);
let result: Blob;
if (cacheResponse) {
result = await cacheResponse.blob();
} else {
result = await get();
try {
await cache.put(id, new Response(result));
} catch (e) {
// TODO: handle storage full exception.
console.error('Error while storing file to cache: ', id);
}
}
return result;
}
export async function getBlobFromCache(
cacheName: string,
url: string
): Promise<Blob> {
const cache = await CacheStorageService.open(cacheName);
const response = await cache.match(url);
return response.blob();
}
export async function deleteAllCache() {
try {
await CacheStorageService.delete(CACHES.THUMBS);
await CacheStorageService.delete(CACHES.FACE_CROPS);
await CacheStorageService.delete(CACHES.FILES);
} catch (e) {
logError(e, 'deleteAllCache failed'); // log and ignore
}
}

View file

@ -0,0 +1,33 @@
import { logError } from '@ente/shared/sentry';
import { CacheStorageFactory } from './factory';
const SecurityError = 'SecurityError';
const INSECURE_OPERATION = 'The operation is insecure.';
async function openCache(cacheName: string) {
try {
return await CacheStorageFactory.getCacheStorage().open(cacheName);
} catch (e) {
// ignoring insecure operation error, as it is thrown in incognito mode in firefox
if (e.name === SecurityError && e.message === INSECURE_OPERATION) {
// no-op
} else {
// log and ignore, we don't want to break the caller flow, when cache is not available
logError(e, 'openCache failed');
}
}
}
async function deleteCache(cacheName: string) {
try {
return await CacheStorageFactory.getCacheStorage().delete(cacheName);
} catch (e) {
// ignoring insecure operation error, as it is thrown in incognito mode in firefox
if (e.name === SecurityError && e.message === INSECURE_OPERATION) {
// no-op
} else {
// log and ignore, we don't want to break the caller flow, when cache is not available
logError(e, 'deleteCache failed');
}
}
}
export const CacheStorageService = { open: openCache, delete: deleteCache };

View file

@ -0,0 +1,20 @@
export interface LimitedCacheStorage {
open: (cacheName: string) => Promise<LimitedCache>;
delete: (cacheName: string) => Promise<boolean>;
}
export interface LimitedCache {
match: (key: string) => Promise<Response>;
put: (key: string, data: Response) => Promise<void>;
delete: (key: string) => Promise<boolean>;
}
export interface ProxiedLimitedCacheStorage {
open: (cacheName: string) => Promise<ProxiedWorkerLimitedCache>;
delete: (cacheName: string) => Promise<boolean>;
}
export interface ProxiedWorkerLimitedCache {
match: (key: string) => Promise<ArrayBuffer>;
put: (key: string, data: ArrayBuffer) => Promise<void>;
delete: (key: string) => Promise<boolean>;
}

View file

@ -0,0 +1,41 @@
import * as Comlink from 'comlink';
import {
LimitedCache,
ProxiedLimitedCacheStorage,
ProxiedWorkerLimitedCache,
} from '@ente/shared/storage/cacheStorage/types';
import { serializeResponse, deserializeToResponse } from './utils/proxy';
import ElectronAPIs from '@ente/shared/electron';
export class WorkerElectronCacheStorageClient
implements ProxiedLimitedCacheStorage
{
async open(cacheName: string) {
const cache = await ElectronAPIs.openDiskCache(cacheName);
return Comlink.proxy({
match: Comlink.proxy(transformMatch(cache.match.bind(cache))),
put: Comlink.proxy(transformPut(cache.put.bind(cache))),
delete: Comlink.proxy(cache.delete.bind(cache)),
});
}
async delete(cacheName: string) {
return await ElectronAPIs.deleteDiskCache(cacheName);
}
}
function transformMatch(
fn: LimitedCache['match']
): ProxiedWorkerLimitedCache['match'] {
return async (key: string) => {
return serializeResponse(await fn(key));
};
}
function transformPut(
fn: LimitedCache['put']
): ProxiedWorkerLimitedCache['put'] {
return async (key: string, data: ArrayBuffer) => {
fn(key, deserializeToResponse(data));
};
}

View file

@ -0,0 +1,55 @@
import * as Comlink from 'comlink';
import {
LimitedCache,
LimitedCacheStorage,
ProxiedWorkerLimitedCache,
} from '../types';
import { WorkerElectronCacheStorageClient } from './client';
import { wrap } from 'comlink';
import { deserializeToResponse, serializeResponse } from './utils/proxy';
export class WorkerElectronCacheStorageService implements LimitedCacheStorage {
proxiedElectronCacheService: Comlink.Remote<WorkerElectronCacheStorageClient>;
ready: Promise<any>;
constructor() {
this.ready = this.init();
}
async init() {
const electronCacheStorageProxy =
wrap<typeof WorkerElectronCacheStorageClient>(self);
this.proxiedElectronCacheService =
await new electronCacheStorageProxy();
}
async open(cacheName: string) {
await this.ready;
const cache = await this.proxiedElectronCacheService.open(cacheName);
return {
match: transformMatch(cache.match.bind(cache)),
put: transformPut(cache.put.bind(cache)),
delete: cache.delete.bind(cache),
};
}
async delete(cacheName: string) {
await this.ready;
return await this.proxiedElectronCacheService.delete(cacheName);
}
}
function transformMatch(
fn: ProxiedWorkerLimitedCache['match']
): LimitedCache['match'] {
return async (key: string) => {
return deserializeToResponse(await fn(key));
};
}
function transformPut(
fn: ProxiedWorkerLimitedCache['put']
): LimitedCache['put'] {
return async (key: string, data: Response) => {
fn(key, await serializeResponse(data));
};
}

View file

@ -0,0 +1,11 @@
export function serializeResponse(response: Response) {
if (response) {
return response.arrayBuffer();
}
}
export function deserializeToResponse(arrayBuffer: ArrayBuffer) {
if (arrayBuffer) {
return new Response(arrayBuffer);
}
}

View file

@ -0,0 +1,4 @@
export enum UPLOAD_STRATEGY {
SINGLE_COLLECTION,
COLLECTION_PER_FOLDER,
}

View file

@ -0,0 +1,24 @@
import { UPLOAD_STRATEGY } from '@ente/shared/upload/constants';
import { ElectronFile } from '@ente/shared/upload/types';
export interface WatchMappingSyncedFile {
path: string;
uploadedFileID: number;
collectionID: number;
}
export interface WatchMapping {
rootFolderName: string;
folderPath: string;
uploadStrategy: UPLOAD_STRATEGY;
syncedFiles: WatchMappingSyncedFile[];
ignoredFiles: string[];
}
export interface EventQueueItem {
type: 'upload' | 'trash';
folderPath: string;
collectionName?: string;
paths?: string[];
files?: ElectronFile[];
}

View file

@ -2969,6 +2969,7 @@ __metadata:
"@mui/icons-material": "npm:5.14.1"
"@mui/material": "npm:5.11.16"
"@typescript-eslint/parser": "npm:^5.59.2"
axios: "npm:^1.4.0"
eslint: "npm:^8.28.0"
husky: "npm:^7.0.1"
is-electron: "npm:^2.2.2"
@ -6051,7 +6052,6 @@ __metadata:
"@types/yup": "npm:^0.29.7"
"@types/zxcvbn": "npm:^4.4.1"
"@zip.js/zip.js": "npm:^2.4.2"
axios: "npm:^1.4.0"
bip39: "npm:^3.0.4"
blazeface-back: "npm:^0.0.9"
bootstrap: "npm:^4.5.2"