ente/src/services/fileService.ts

169 lines
5.7 KiB
TypeScript
Raw Normal View History

import { getEndpoint } from "utils/common/apiUtil";
import HTTPService from "./HTTPService";
2020-09-27 17:18:57 +00:00
import * as Comlink from "comlink";
2020-11-07 11:10:49 +00:00
import { getData, LS_KEYS } from "utils/storage/localStorage";
2020-11-20 08:47:20 +00:00
import localForage from "localforage";
2020-09-27 17:18:57 +00:00
2020-10-19 03:01:34 +00:00
const CryptoWorker: any = typeof window !== 'undefined'
2020-09-30 21:22:58 +00:00
&& 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-10-19 03:01:34 +00:00
export interface fileAttribute {
encryptedData: string;
decryptionHeader: string;
2020-11-20 08:47:20 +00:00
creationTime: number;
2020-11-26 15:57:20 +00:00
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-10-19 03:01:34 +00:00
export interface collection {
2020-11-20 08:47:20 +00:00
id: string;
2020-11-07 11:10:49 +00:00
owner: user;
2020-10-19 03:01:34 +00:00
key: string;
name: string;
type: string;
creationTime: number;
2020-11-07 11:10:49 +00:00
encryptedKey: string;
keyDecryptionNonce: string;
2020-11-20 11:22:04 +00:00
isDeleted: boolean;
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;
key: Uint8Array;
src: string;
2020-11-24 04:40:02 +00:00
msrc: string;
2020-11-26 16:39:43 +00:00
html: string;
2020-10-19 03:01:34 +00:00
w: number;
h: number;
};
2020-11-07 11:10:49 +00:00
const getCollectionKey = async (collection: collection, key: Uint8Array) => {
2020-09-30 21:24:40 +00:00
const worker = await new CryptoWorker();
2020-11-07 11:10:49 +00:00
const userID = getData(LS_KEYS.USER).id;
var decryptedKey;
if (collection.owner.id == userID) {
decryptedKey = await worker.decrypt(
await worker.fromB64(collection.encryptedKey),
await worker.fromB64(collection.keyDecryptionNonce),
key);
} else {
2020-11-20 06:59:27 +00:00
const keyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
const secretKey = await worker.decrypt(
await worker.fromB64(keyAttributes.encryptedSecretKey),
await worker.fromB64(keyAttributes.secretKeyDecryptionNonce),
key);
decryptedKey = await worker.boxSealOpen(
await worker.fromB64(collection.encryptedKey),
await worker.fromB64(keyAttributes.publicKey),
secretKey);
2020-11-07 11:10:49 +00:00
}
2020-10-19 03:01:34 +00:00
return {
...collection,
2020-11-07 11:10:49 +00:00
key: decryptedKey
2020-10-19 03:01:34 +00:00
};
}
2020-11-07 11:10:49 +00:00
const getCollections = async (token: string, sinceTime: string, key: Uint8Array): Promise<collection[]> => {
2020-11-20 09:13:20 +00:00
const resp = await HTTPService.get(`${ENDPOINT}/collections`, {
2020-11-07 11:10:49 +00:00
'token': token,
'sinceTime': sinceTime,
2020-10-19 03:01:34 +00:00
});
const promises: Promise<collection>[] = resp.data.collections.map(
2020-11-07 11:10:49 +00:00
(collection: collection) => getCollectionKey(collection, key));
2020-10-19 03:01:34 +00:00
return await Promise.all(promises);
2020-09-20 15:18:35 +00:00
}
export const getFiles = async (sinceTime: string, token: string, limit: string, key: string) => {
2020-10-19 03:01:34 +00:00
const worker = await new CryptoWorker();
2020-11-07 11:10:49 +00:00
const collections = await getCollections(token, "0", await worker.fromB64(key));
2020-11-20 08:47:20 +00:00
var files: Array<file> = await localForage.getItem<file[]>('files') || [];
2020-11-07 11:10:49 +00:00
for (const index in collections) {
const collection = collections[index];
2020-11-20 11:22:04 +00:00
if (collection.isDeleted) {
2020-11-25 07:02:21 +00:00
// TODO: Remove files in this collection from localForage and cache
2020-11-20 11:22:04 +00:00
continue;
}
2020-11-20 08:47:20 +00:00
let time = await localForage.getItem<string>(`${collection.id}-time`) || sinceTime;
let resp;
do {
resp = await HTTPService.get(`${ENDPOINT}/collections/diff`, {
'collectionID': collection.id, sinceTime: time, token, limit,
2020-11-07 11:10:49 +00:00
});
2020-11-20 08:47:20 +00:00
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);
2020-11-07 11:10:49 +00:00
}
2020-11-20 08:47:20 +00:00
await localForage.setItem('files', files);
2020-11-07 11:10:49 +00:00
return files;
}
2020-09-20 15:18:35 +00:00
2020-10-19 03:01:34 +00:00
export const getPreview = async (token: string, file: file) => {
2020-11-24 20:16:19 +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());
}
2020-09-20 15:18:35 +00:00
const resp = await HTTPService.get(
2020-10-19 03:01:34 +00:00
`${ENDPOINT}/files/preview/${file.id}`,
2020-09-20 15:18:35 +00:00
{ token }, null, { responseType: 'arraybuffer' },
);
2020-10-19 03:01:34 +00:00
const worker = await new CryptoWorker();
const decrypted: any = await worker.decryptThumbnail(
2020-10-19 03:01:34 +00:00
new Uint8Array(resp.data),
await worker.fromB64(file.thumbnail.decryptionHeader),
file.key);
2020-11-25 07:02:21 +00:00
try {
await cache.put(file.id.toString(), new Response(new Blob([decrypted])));
} catch (e) {
// TODO: handle storage full exception.
}
2020-10-19 03:01:34 +00:00
return URL.createObjectURL(new Blob([decrypted]));
2020-09-20 15:18:35 +00:00
}
export const getFile = async (token: string, file: file) => {
const resp = await HTTPService.get(
`${ENDPOINT}/files/download/${file.id}`,
{ token }, null, { 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]));
}