Created getCollectionLatestFile fileService

This commit is contained in:
Abhinav-grd 2021-01-05 15:53:28 +05:30
parent 34d1a261b9
commit f30cbedcb2

View file

@ -1,174 +1,240 @@
import { getEndpoint } from "utils/common/apiUtil"; import { getEndpoint } from 'utils/common/apiUtil';
import HTTPService from "./HTTPService"; import HTTPService from './HTTPService';
import * as Comlink from "comlink"; import * as Comlink from 'comlink';
import { getData, LS_KEYS } from "utils/storage/localStorage"; import { getData, LS_KEYS } from 'utils/storage/localStorage';
import localForage from "localforage"; import localForage from 'localforage';
const CryptoWorker: any = typeof window !== 'undefined' const CryptoWorker: any =
&& Comlink.wrap(new Worker("worker/crypto.worker.js", { type: 'module' })); typeof window !== 'undefined' &&
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
const ENDPOINT = getEndpoint(); const ENDPOINT = getEndpoint();
localForage.config({ localForage.config({
driver: localForage.INDEXEDDB, driver: localForage.INDEXEDDB,
name: 'ente-files', name: 'ente-files',
version: 1.0, version: 1.0,
storeName: 'files', storeName: 'files',
}); });
export interface fileAttribute { export interface fileAttribute {
encryptedData: string; encryptedData: string;
decryptionHeader: string; decryptionHeader: string;
creationTime: number; creationTime: number;
fileType: number; fileType: number;
}; }
export interface user { export interface user {
id: number; id: number;
name: string; name: string;
email: string; email: string;
} }
export interface collection { export interface collection {
id: string; id: string;
owner: user; owner: user;
key: string; key: string;
name: string; name: string;
type: string; type: string;
creationTime: number; creationTime: number;
encryptedKey: string; encryptedKey: string;
keyDecryptionNonce: string; keyDecryptionNonce: string;
isDeleted: boolean; isDeleted: boolean;
} }
export interface file { export interface file {
id: number; id: number;
collectionID: number; collectionID: number;
file: fileAttribute; file: fileAttribute;
thumbnail: fileAttribute; thumbnail: fileAttribute;
metadata: fileAttribute; metadata: fileAttribute;
encryptedKey: string; encryptedKey: string;
keyDecryptionNonce: string; keyDecryptionNonce: string;
key: Uint8Array; key: Uint8Array;
src: string; src: string;
msrc: string; msrc: string;
html: string; html: string;
w: number; w: number;
h: number; h: number;
isDeleted: boolean; isDeleted: boolean;
dataIndex: number; dataIndex: number;
}; }
export interface collectionLatestImage {
collectionName: string;
collectionID: number;
thumb: string;
}
const getCollectionKey = async (collection: collection, key: Uint8Array) => { const getCollectionKey = async (collection: collection, key: Uint8Array) => {
const worker = await new CryptoWorker(); const worker = await new CryptoWorker();
const userID = getData(LS_KEYS.USER).id; const userID = getData(LS_KEYS.USER).id;
var decryptedKey; var decryptedKey;
if (collection.owner.id == userID) { if (collection.owner.id == userID) {
decryptedKey = await worker.decrypt( decryptedKey = await worker.decrypt(
await worker.fromB64(collection.encryptedKey), await worker.fromB64(collection.encryptedKey),
await worker.fromB64(collection.keyDecryptionNonce), await worker.fromB64(collection.keyDecryptionNonce),
key); key
} else { );
const keyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES); } else {
const secretKey = await worker.decrypt( const keyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
await worker.fromB64(keyAttributes.encryptedSecretKey), const secretKey = await worker.decrypt(
await worker.fromB64(keyAttributes.secretKeyDecryptionNonce), await worker.fromB64(keyAttributes.encryptedSecretKey),
key); await worker.fromB64(keyAttributes.secretKeyDecryptionNonce),
decryptedKey = await worker.boxSealOpen( key
await worker.fromB64(collection.encryptedKey), );
await worker.fromB64(keyAttributes.publicKey), decryptedKey = await worker.boxSealOpen(
secretKey); await worker.fromB64(collection.encryptedKey),
} await worker.fromB64(keyAttributes.publicKey),
return { secretKey
...collection, );
key: decryptedKey }
}; return {
} ...collection,
key: decryptedKey,
};
};
const getCollections = async (token: string, sinceTime: string, key: Uint8Array): Promise<collection[]> => { const getCollections = async (
const resp = await HTTPService.get(`${ENDPOINT}/collections`, { token: string,
'token': token, sinceTime: string,
'sinceTime': sinceTime, key: Uint8Array
}); ): Promise<collection[]> => {
const resp = await HTTPService.get(`${ENDPOINT}/collections`, {
token: token,
sinceTime: sinceTime,
});
const promises: Promise<collection>[] = resp.data.collections.map( const promises: Promise<collection>[] = resp.data.collections.map(
(collection: collection) => getCollectionKey(collection, key)); (collection: collection) => getCollectionKey(collection, key)
return await Promise.all(promises); );
} return await Promise.all(promises);
};
export const fetchCollections = async (token: string, key: string) => { export const fetchCollections = async (token: string, key: string) => {
const worker = await new CryptoWorker(); const worker = await new CryptoWorker();
return getCollections(token, "0", await worker.fromB64(key)); return getCollections(token, '0', await worker.fromB64(key));
} };
export const getFiles = async (sinceTime: string, token: string, limit: string, key: string, collections: collection[]) => { export const getFiles = async (
const worker = await new CryptoWorker(); sinceTime: string,
let files: Array<file> = await localForage.getItem<file[]>('files') || []; token: string,
for (const index in collections) { limit: string,
const collection = collections[index]; key: string,
if (collection.isDeleted) { collections: collection[]
// TODO: Remove files in this collection from localForage and cache ) => {
continue; const worker = await new CryptoWorker();
} let files: Array<file> = (await localForage.getItem<file[]>('files')) || [];
let time = await localForage.getItem<string>(`${collection.id}-time`) || sinceTime; for (const index in collections) {
let resp; const collection = collections[index];
do { if (collection.isDeleted) {
resp = await HTTPService.get(`${ENDPOINT}/collections/diff`, { // TODO: Remove files in this collection from localForage and cache
'collectionID': collection.id, sinceTime: time, token, limit, continue;
});
const promises: Promise<file>[] = resp.data.diff.map(
async (file: file) => {
file.key = await worker.decrypt(
await worker.fromB64(file.encryptedKey),
await worker.fromB64(file.keyDecryptionNonce),
collection.key);
file.metadata = await worker.decryptMetadata(file);
return file;
});
files.push(...await Promise.all(promises));
files = files.sort((a, b) => b.metadata.creationTime - a.metadata.creationTime);
if (resp.data.diff.length) {
time = (resp.data.diff.slice(-1)[0].updationTime).toString();
}
} while (resp.data.diff.length);
await localForage.setItem(`${collection.id}-time`, time);
} }
files = files.filter(item => !item.isDeleted) let time =
await localForage.setItem('files', files); (await localForage.getItem<string>(`${collection.id}-time`)) || sinceTime;
return files; let resp;
} do {
resp = await HTTPService.get(`${ENDPOINT}/collections/diff`, {
collectionID: collection.id,
sinceTime: time,
token,
limit,
});
const promises: Promise<file>[] = resp.data.diff.map(
async (file: file) => {
file.key = await worker.decrypt(
await worker.fromB64(file.encryptedKey),
await worker.fromB64(file.keyDecryptionNonce),
collection.key
);
file.metadata = await worker.decryptMetadata(file);
return file;
}
);
files.push(...(await Promise.all(promises)));
files = files.sort(
(a, b) => b.metadata.creationTime - a.metadata.creationTime
);
if (resp.data.diff.length) {
time = resp.data.diff.slice(-1)[0].updationTime.toString();
}
} while (resp.data.diff.length);
await localForage.setItem(`${collection.id}-time`, time);
}
files = files.filter((item) => !item.isDeleted);
await localForage.setItem('files', files);
return files;
};
export const getPreview = async (token: string, file: file) => { export const getPreview = async (token: string, file: file) => {
const cache = await caches.open('thumbs'); const cache = await caches.open('thumbs');
const cacheResp: Response = await cache.match(file.id.toString()); const cacheResp: Response = await cache.match(file.id.toString());
if (cacheResp) { if (cacheResp) {
return URL.createObjectURL(await cacheResp.blob()); return URL.createObjectURL(await cacheResp.blob());
} }
const resp = await HTTPService.get( const resp = await HTTPService.get(
`${ENDPOINT}/files/preview/${file.id}`, `${ENDPOINT}/files/preview/${file.id}`,
{ token }, null, { responseType: 'arraybuffer' }, { token },
); null,
const worker = await new CryptoWorker(); { responseType: 'arraybuffer' }
const decrypted: any = await worker.decryptThumbnail( );
new Uint8Array(resp.data), const worker = await new CryptoWorker();
await worker.fromB64(file.thumbnail.decryptionHeader), const decrypted: any = await worker.decryptThumbnail(
file.key); new Uint8Array(resp.data),
try { await worker.fromB64(file.thumbnail.decryptionHeader),
await cache.put(file.id.toString(), new Response(new Blob([decrypted]))); file.key
} catch (e) { );
// TODO: handle storage full exception. try {
} await cache.put(file.id.toString(), new Response(new Blob([decrypted])));
return URL.createObjectURL(new Blob([decrypted])); } catch (e) {
} // TODO: handle storage full exception.
}
return URL.createObjectURL(new Blob([decrypted]));
};
export const getFile = async (token: string, file: file) => { export const getFile = async (token: string, file: file) => {
const resp = await HTTPService.get( const resp = await HTTPService.get(
`${ENDPOINT}/files/download/${file.id}`, `${ENDPOINT}/files/download/${file.id}`,
{ token }, null, { responseType: 'arraybuffer' }, { token },
); null,
const worker = await new CryptoWorker(); { responseType: 'arraybuffer' }
const decrypted: any = await worker.decryptFile( );
new Uint8Array(resp.data), const worker = await new CryptoWorker();
await worker.fromB64(file.file.decryptionHeader), const decrypted: any = await worker.decryptFile(
file.key); new Uint8Array(resp.data),
return URL.createObjectURL(new Blob([decrypted])); await worker.fromB64(file.file.decryptionHeader),
} file.key
);
return URL.createObjectURL(new Blob([decrypted]));
};
export const getCollectionLatestFile = async (
collections: collection[],
data: file[]
): Promise<collectionLatestImage[]> => {
let collectionIdSet = new Set();
let collectionIdNameMap = new Object();
collections.forEach((collection) => {
collectionIdNameMap[collection.id] = collection.name;
});
console.log();
return Promise.all(
data
.filter((item) => {
if (!collectionIdSet.has(item.collectionID)) {
collectionIdSet.add(item.collectionID);
return true;
}
return false;
})
.map(async (item) => {
const token = getData(LS_KEYS.USER).token;
const url = await getPreview(token, item);
return {
thumb: url,
collectionID: item.collectionID,
collectionName: collectionIdNameMap[item.collectionID].toString(),
};
})
);
};