ente/src/services/fileService.ts

200 lines
5.9 KiB
TypeScript
Raw Normal View History

import { getEndpoint } from 'utils/common/apiUtil';
import HTTPService from './HTTPService';
import * as Comlink from 'comlink';
import localForage from 'localforage';
import { collection } from './collectionService';
2020-09-27 17:18:57 +00:00
const CryptoWorker: any =
typeof window !== 'undefined' &&
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
const ENDPOINT = getEndpoint();
2020-11-20 08:47:20 +00:00
localForage.config({
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 {
encryptedData: Uint8Array | string;
decryptionHeader: string;
creationTime: number;
fileType: number;
}
2020-10-19 03:01:34 +00:00
2020-11-07 11:10:49 +00:00
export interface user {
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 {
id: number;
collectionID: number;
file: fileAttribute;
thumbnail: fileAttribute;
metadata: fileAttribute;
encryptedKey: string;
keyDecryptionNonce: string;
2021-01-18 02:26:34 +00:00
key: string;
src: string;
msrc: string;
html: string;
w: number;
h: number;
isDeleted: boolean;
dataIndex: number;
updationTime: number;
}
2020-10-19 03:01:34 +00:00
export const fetchData = async (token, collections) => {
const resp = await fetchFiles(
token,
collections
);
return (
resp.map((item) => ({
...item,
w: window.innerWidth,
h: window.innerHeight,
}))
);
}
export const localFiles = async () => {
let files: Array<file> = (await localForage.getItem<file[]>('files')) || [];
return files;
}
export const fetchFiles = async (
token: string,
collections: collection[]
) => {
let files = await localFiles();
const fetchedFiles = await getFiles(collections, null, 100, token);
files.push(...fetchedFiles);
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 = [];
for (const [_, file] of latestFiles) {
if (!file.isDeleted)
files.push(file);
}
files = files.sort(
(a, b) => b.metadata.creationTime - a.metadata.creationTime
);
await localForage.setItem('files', files);
return files;
};
export const getFiles = async (collections: 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>[] = [];
for (const index in collections) {
const collection = collections[index];
if (collection.isDeleted) {
// TODO: Remove files in this collection from localForage and cache
continue;
}
let time =
sinceTime || (await localForage.getItem<string>(`${collection.id}-time`)) || "0";
let resp;
do {
resp = await HTTPService.get(`${ENDPOINT}/collections/diff`, {
collectionID: collection.id,
sinceTime: time,
limit: limit.toString(),
},
{
'X-Auth-Token': token
});
2021-01-25 07:05:45 +00:00
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);
}
return file;
}
2021-01-25 07:05:45 +00:00
));
2021-01-25 07:05:45 +00:00
if (resp.data.diff.length) {
time = resp.data.diff.slice(-1)[0].updationTime.toString();
}
} while (resp.data.diff.length === limit);
2021-01-25 07:05:45 +00:00
await localForage.setItem(`${collection.id}-time`, time);
}
return Promise.all(promises);
} catch (e) {
console.log("Get files failed" + e);
}
}
2020-10-19 03:01:34 +00:00
export const getPreview = async (token: string, file: file) => {
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,
{ '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]));
} catch (e) {
2021-01-25 07:05:45 +00:00
console.log("get preview Failed" + e);
}
};
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,
{ '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);
}
};