Created getCollectionLatestFile fileService
This commit is contained in:
parent
34d1a261b9
commit
f30cbedcb2
|
@ -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(),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue