2021-01-05 10:23:28 +00:00
|
|
|
import { getEndpoint } from 'utils/common/apiUtil';
|
|
|
|
import HTTPService from './HTTPService';
|
|
|
|
import * as Comlink from 'comlink';
|
|
|
|
import localForage from 'localforage';
|
2021-01-13 12:44:12 +00:00
|
|
|
import { collection } from './collectionService';
|
2020-09-27 17:18:57 +00:00
|
|
|
|
2021-01-05 10:23:28 +00:00
|
|
|
const CryptoWorker: any =
|
2021-01-08 03:51:59 +00:00
|
|
|
typeof window !== 'undefined' &&
|
|
|
|
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
|
2020-09-19 21:20:10 +00:00
|
|
|
const ENDPOINT = getEndpoint();
|
|
|
|
|
2020-11-20 08:47:20 +00:00
|
|
|
localForage.config({
|
2021-01-08 03:51:59 +00:00
|
|
|
driver: localForage.INDEXEDDB,
|
|
|
|
name: 'ente-files',
|
|
|
|
version: 1.0,
|
|
|
|
storeName: 'files',
|
2020-11-20 08:47:20 +00:00
|
|
|
});
|
|
|
|
|
2020-10-19 03:01:34 +00:00
|
|
|
export interface fileAttribute {
|
2021-01-09 03:34:43 +00:00
|
|
|
encryptedData: Uint8Array | string;
|
2021-01-08 03:51:59 +00:00
|
|
|
decryptionHeader: string;
|
|
|
|
creationTime: number;
|
|
|
|
fileType: number;
|
2021-01-05 10:23:28 +00:00
|
|
|
}
|
2020-10-19 03:01:34 +00:00
|
|
|
|
2020-11-07 11:10:49 +00:00
|
|
|
export interface user {
|
2021-01-08 03:51:59 +00:00
|
|
|
id: number;
|
|
|
|
name: string;
|
|
|
|
email: string;
|
2020-11-07 11:10:49 +00:00
|
|
|
}
|
|
|
|
|
2020-10-19 03:01:34 +00:00
|
|
|
|
|
|
|
export interface file {
|
2021-01-08 03:51:59 +00:00
|
|
|
id: number;
|
|
|
|
collectionID: number;
|
|
|
|
file: fileAttribute;
|
|
|
|
thumbnail: fileAttribute;
|
|
|
|
metadata: fileAttribute;
|
|
|
|
encryptedKey: string;
|
|
|
|
keyDecryptionNonce: string;
|
2021-01-18 02:26:34 +00:00
|
|
|
key: string;
|
2021-01-08 03:51:59 +00:00
|
|
|
src: string;
|
|
|
|
msrc: string;
|
|
|
|
html: string;
|
|
|
|
w: number;
|
|
|
|
h: number;
|
|
|
|
isDeleted: boolean;
|
|
|
|
dataIndex: number;
|
2021-01-20 13:51:22 +00:00
|
|
|
updationTime: number;
|
2021-01-05 10:23:28 +00:00
|
|
|
}
|
2020-09-19 21:20:10 +00:00
|
|
|
|
|
|
|
|
2020-10-19 03:01:34 +00:00
|
|
|
|
2021-01-13 12:44:12 +00:00
|
|
|
export const fetchData = async (token, collections) => {
|
2021-01-14 11:31:20 +00:00
|
|
|
const resp = await fetchFiles(
|
2021-01-11 08:13:53 +00:00
|
|
|
token,
|
|
|
|
collections
|
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
resp.map((item) => ({
|
|
|
|
...item,
|
|
|
|
w: window.innerWidth,
|
|
|
|
h: window.innerHeight,
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-01-27 06:14:02 +00:00
|
|
|
export const localFiles = async () => {
|
|
|
|
let files: Array<file> = (await localForage.getItem<file[]>('files')) || [];
|
|
|
|
return files;
|
|
|
|
}
|
|
|
|
|
2021-01-14 11:31:20 +00:00
|
|
|
export const fetchFiles = async (
|
2021-01-08 03:51:59 +00:00
|
|
|
token: string,
|
|
|
|
collections: collection[]
|
2021-01-05 10:23:28 +00:00
|
|
|
) => {
|
2021-01-27 06:14:02 +00:00
|
|
|
let files = await localFiles();
|
2021-02-08 07:33:46 +00:00
|
|
|
const collectionUpdationTime = new Map<number, number>();
|
2021-02-05 18:51:14 +00:00
|
|
|
let fetchedFiles = [];
|
2021-02-08 07:33:46 +00:00
|
|
|
let deletedCollection = new Set<number>();
|
2021-02-05 18:51:14 +00:00
|
|
|
for (let collection of collections) {
|
2021-02-06 07:54:36 +00:00
|
|
|
if (collection.isDeleted) {
|
|
|
|
deletedCollection.add(collection.id);
|
|
|
|
}
|
2021-02-05 18:51:14 +00:00
|
|
|
const files = await getFiles(collection, null, 100, token);
|
|
|
|
fetchedFiles.push(...files);
|
2021-02-08 07:33:46 +00:00
|
|
|
collectionUpdationTime.set(collection.id, files.length > 0 ? files.slice(-1)[0].updationTime : 0);
|
2021-02-05 18:51:14 +00:00
|
|
|
}
|
2021-01-14 11:31:20 +00:00
|
|
|
files.push(...fetchedFiles);
|
2021-01-20 13:51:22 +00:00
|
|
|
var latestFiles = new Map<string, file>();
|
|
|
|
files.forEach((file) => {
|
|
|
|
let uid = `${file.collectionID}-${file.id}`;
|
|
|
|
if (!latestFiles.has(uid) || latestFiles.get(uid).updationTime < file.updationTime) {
|
|
|
|
latestFiles.set(uid, file);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
files = [];
|
2021-02-03 08:02:54 +00:00
|
|
|
for (const [_, file] of latestFiles) {
|
2021-02-08 07:33:46 +00:00
|
|
|
if (file.isDeleted || deletedCollection.has(file.collectionID)) {
|
|
|
|
continue;
|
2021-02-06 07:54:36 +00:00
|
|
|
}
|
2021-02-08 07:33:46 +00:00
|
|
|
files.push(file);
|
2021-01-20 13:51:22 +00:00
|
|
|
}
|
2021-01-14 11:31:20 +00:00
|
|
|
files = files.sort(
|
|
|
|
(a, b) => b.metadata.creationTime - a.metadata.creationTime
|
|
|
|
);
|
|
|
|
await localForage.setItem('files', files);
|
2021-02-05 18:51:14 +00:00
|
|
|
for (let [collectionID, updationTime] of collectionUpdationTime) {
|
|
|
|
await localForage.setItem(`${collectionID}-time`, updationTime);
|
|
|
|
}
|
2021-02-05 18:59:34 +00:00
|
|
|
let updationTime = await localForage.getItem('collection-update-time') as number;
|
|
|
|
for (let collection of collections) {
|
|
|
|
updationTime = Math.max(updationTime, collection.updationTime);
|
|
|
|
}
|
|
|
|
await localForage.setItem('collection-update-time', updationTime);
|
2021-01-14 11:31:20 +00:00
|
|
|
return files;
|
|
|
|
};
|
|
|
|
|
2021-02-05 18:51:14 +00:00
|
|
|
export const getFiles = async (collection: collection, sinceTime: string, limit: number, token: string): Promise<file[]> => {
|
2021-01-25 07:05:45 +00:00
|
|
|
try {
|
|
|
|
const worker = await new CryptoWorker();
|
|
|
|
let promises: Promise<file>[] = [];
|
2021-02-05 18:51:14 +00:00
|
|
|
if (collection.isDeleted) {
|
|
|
|
// TODO: Remove files in this collection from localForage and cache
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let time =
|
|
|
|
sinceTime || (await localForage.getItem<string>(`${collection.id}-time`)) || "0";
|
|
|
|
let resp;
|
|
|
|
do {
|
|
|
|
resp = await HTTPService.get(`${ENDPOINT}/collections/diff`, {
|
2021-02-08 07:33:46 +00:00
|
|
|
collectionID: collection.id.toString(),
|
2021-02-05 18:51:14 +00:00
|
|
|
sinceTime: time,
|
|
|
|
limit: limit.toString(),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'X-Auth-Token': token
|
|
|
|
});
|
|
|
|
promises.push(...resp.data.diff.map(
|
|
|
|
async (file: file) => {
|
|
|
|
if (!file.isDeleted) {
|
|
|
|
|
|
|
|
file.key = await worker.decryptB64(
|
|
|
|
file.encryptedKey,
|
|
|
|
file.keyDecryptionNonce,
|
|
|
|
collection.key
|
|
|
|
);
|
|
|
|
file.metadata = await worker.decryptMetadata(file);
|
2021-01-20 13:51:22 +00:00
|
|
|
}
|
2021-02-05 18:51:14 +00:00
|
|
|
return file;
|
2021-01-25 07:05:45 +00:00
|
|
|
}
|
2021-02-05 18:51:14 +00:00
|
|
|
));
|
|
|
|
|
|
|
|
if (resp.data.diff.length) {
|
|
|
|
time = resp.data.diff.slice(-1)[0].updationTime.toString();
|
|
|
|
}
|
|
|
|
} while (resp.data.diff.length === limit);
|
|
|
|
return await Promise.all(promises);
|
2021-01-25 07:05:45 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.log("Get files failed" + e);
|
2021-01-14 11:31:20 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-19 03:01:34 +00:00
|
|
|
export const getPreview = async (token: string, file: file) => {
|
2021-01-08 03:51:59 +00:00
|
|
|
try {
|
2021-01-25 07:05:45 +00:00
|
|
|
const cache = await caches.open('thumbs');
|
|
|
|
const cacheResp: Response = await cache.match(file.id.toString());
|
|
|
|
if (cacheResp) {
|
|
|
|
return URL.createObjectURL(await cacheResp.blob());
|
|
|
|
}
|
|
|
|
const resp = await HTTPService.get(
|
|
|
|
`${ENDPOINT}/files/preview/${file.id}`,
|
|
|
|
null,
|
2021-01-26 06:39:10 +00:00
|
|
|
{ 'X-Auth-Token': token },
|
2021-01-25 07:05:45 +00:00
|
|
|
{ responseType: 'arraybuffer' }
|
|
|
|
);
|
|
|
|
const worker = await new CryptoWorker();
|
|
|
|
const decrypted: any = await worker.decryptThumbnail(
|
|
|
|
new Uint8Array(resp.data),
|
|
|
|
await worker.fromB64(file.thumbnail.decryptionHeader),
|
|
|
|
file.key
|
|
|
|
);
|
|
|
|
try {
|
|
|
|
await cache.put(file.id.toString(), new Response(new Blob([decrypted])));
|
|
|
|
} catch (e) {
|
|
|
|
// TODO: handle storage full exception.
|
|
|
|
}
|
|
|
|
return URL.createObjectURL(new Blob([decrypted]));
|
2021-01-08 03:51:59 +00:00
|
|
|
} catch (e) {
|
2021-01-25 07:05:45 +00:00
|
|
|
console.log("get preview Failed" + e);
|
2021-01-08 03:51:59 +00:00
|
|
|
}
|
2021-01-05 10:23:28 +00:00
|
|
|
};
|
2020-10-19 03:07:16 +00:00
|
|
|
|
|
|
|
export const getFile = async (token: string, file: file) => {
|
2021-01-25 07:05:45 +00:00
|
|
|
try {
|
|
|
|
const resp = await HTTPService.get(
|
|
|
|
`${ENDPOINT}/files/download/${file.id}`,
|
|
|
|
null,
|
2021-01-26 06:39:10 +00:00
|
|
|
{ 'X-Auth-Token': token },
|
2021-01-25 07:05:45 +00:00
|
|
|
{ responseType: 'arraybuffer' }
|
|
|
|
);
|
|
|
|
const worker = await new CryptoWorker();
|
|
|
|
const decrypted: any = await worker.decryptFile(
|
|
|
|
new Uint8Array(resp.data),
|
|
|
|
await worker.fromB64(file.file.decryptionHeader),
|
|
|
|
file.key
|
|
|
|
);
|
|
|
|
return URL.createObjectURL(new Blob([decrypted]));
|
|
|
|
}
|
|
|
|
catch (e) {
|
|
|
|
console.log("get file failed " + e);
|
|
|
|
}
|
2021-01-05 10:23:28 +00:00
|
|
|
};
|
|
|
|
|