2021-02-08 17:02:28 +00:00
|
|
|
import { getEndpoint } from 'utils/common/apiUtil';
|
|
|
|
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
|
|
|
import { file, user, getFiles } from './fileService';
|
2021-01-14 11:31:20 +00:00
|
|
|
import localForage from 'localforage';
|
|
|
|
|
2021-02-08 17:02:28 +00:00
|
|
|
import HTTPService from './HTTPService';
|
2021-01-13 05:31:02 +00:00
|
|
|
import * as Comlink from 'comlink';
|
2021-02-08 17:02:28 +00:00
|
|
|
import { keyEncryptionResult } from './uploadService';
|
|
|
|
import { getActualKey, getToken } from 'utils/common/key';
|
2021-01-13 05:31:02 +00:00
|
|
|
|
|
|
|
const CryptoWorker: any =
|
|
|
|
typeof window !== 'undefined' &&
|
|
|
|
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
|
|
|
|
const ENDPOINT = getEndpoint();
|
|
|
|
|
2021-01-13 12:43:46 +00:00
|
|
|
enum CollectionType {
|
2021-02-08 17:02:28 +00:00
|
|
|
folder = 'folder',
|
|
|
|
favorites = 'favorites',
|
|
|
|
album = 'album',
|
2021-01-13 12:43:46 +00:00
|
|
|
}
|
|
|
|
|
2021-02-08 17:02:28 +00:00
|
|
|
const COLLECTION_UPDATION_TIME = 'collection-updation-time';
|
|
|
|
const FAV_COLLECTION = 'fav-collection';
|
|
|
|
const COLLECTIONS = 'collections';
|
|
|
|
|
2021-01-13 12:43:46 +00:00
|
|
|
export interface collection {
|
2021-02-08 07:33:46 +00:00
|
|
|
id: number;
|
2021-01-13 12:43:46 +00:00
|
|
|
owner: user;
|
2021-01-18 02:27:00 +00:00
|
|
|
key?: string;
|
2021-01-21 02:24:17 +00:00
|
|
|
name?: string;
|
|
|
|
encryptedName?: string;
|
|
|
|
nameDecryptionNonce?: string;
|
2021-01-14 09:41:09 +00:00
|
|
|
type: string;
|
2021-02-08 17:02:28 +00:00
|
|
|
attributes: collectionAttributes;
|
2021-01-13 12:43:46 +00:00
|
|
|
sharees: user[];
|
|
|
|
updationTime: number;
|
|
|
|
encryptedKey: string;
|
|
|
|
keyDecryptionNonce: string;
|
|
|
|
isDeleted: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface collectionAttributes {
|
|
|
|
encryptedPath?: string;
|
2021-02-08 17:02:28 +00:00
|
|
|
pathDecryptionNonce?: string;
|
|
|
|
}
|
2021-01-13 12:43:46 +00:00
|
|
|
|
2021-02-09 06:04:19 +00:00
|
|
|
export interface CollectionAndItsLatestFile {
|
2021-02-08 17:02:28 +00:00
|
|
|
collection: collection;
|
2021-01-14 11:31:20 +00:00
|
|
|
file: file;
|
|
|
|
}
|
|
|
|
|
2021-02-08 17:02:28 +00:00
|
|
|
const getCollectionSecrets = async (
|
|
|
|
collection: collection,
|
|
|
|
masterKey: string
|
|
|
|
) => {
|
2021-01-13 05:31:02 +00:00
|
|
|
const worker = await new CryptoWorker();
|
|
|
|
const userID = getData(LS_KEYS.USER).id;
|
2021-01-18 02:27:00 +00:00
|
|
|
let decryptedKey: string;
|
2021-01-13 05:31:02 +00:00
|
|
|
if (collection.owner.id == userID) {
|
2021-01-18 02:27:00 +00:00
|
|
|
decryptedKey = await worker.decryptB64(
|
|
|
|
collection.encryptedKey,
|
|
|
|
collection.keyDecryptionNonce,
|
|
|
|
masterKey
|
2021-01-13 05:31:02 +00:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
const keyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
|
2021-01-18 02:27:00 +00:00
|
|
|
const secretKey = await worker.decryptB64(
|
|
|
|
keyAttributes.encryptedSecretKey,
|
|
|
|
keyAttributes.secretKeyDecryptionNonce,
|
|
|
|
masterKey
|
2021-01-13 05:31:02 +00:00
|
|
|
);
|
|
|
|
decryptedKey = await worker.boxSealOpen(
|
2021-01-21 02:24:17 +00:00
|
|
|
collection.encryptedKey,
|
|
|
|
keyAttributes.publicKey,
|
|
|
|
secretKey
|
2021-01-13 05:31:02 +00:00
|
|
|
);
|
|
|
|
}
|
2021-02-08 17:02:28 +00:00
|
|
|
collection.name =
|
|
|
|
collection.name ||
|
|
|
|
(await worker.decryptString(
|
|
|
|
collection.encryptedName,
|
|
|
|
collection.nameDecryptionNonce,
|
|
|
|
decryptedKey
|
|
|
|
));
|
2021-01-13 05:31:02 +00:00
|
|
|
return {
|
|
|
|
...collection,
|
|
|
|
key: decryptedKey,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const getCollections = async (
|
|
|
|
token: string,
|
|
|
|
sinceTime: string,
|
2021-01-18 02:27:00 +00:00
|
|
|
key: string
|
2021-01-13 05:31:02 +00:00
|
|
|
): Promise<collection[]> => {
|
2021-01-25 07:05:45 +00:00
|
|
|
try {
|
2021-02-08 17:02:28 +00:00
|
|
|
const resp = await HTTPService.get(
|
|
|
|
`${ENDPOINT}/collections`,
|
|
|
|
{
|
|
|
|
sinceTime: sinceTime,
|
|
|
|
},
|
|
|
|
{ 'X-Auth-Token': token }
|
|
|
|
);
|
2021-01-25 12:00:05 +00:00
|
|
|
const promises: Promise<collection>[] = resp.data.collections.map(
|
2021-01-25 07:05:45 +00:00
|
|
|
(collection: collection) => getCollectionSecrets(collection, key)
|
|
|
|
);
|
|
|
|
return await Promise.all(promises);
|
2021-02-08 17:02:28 +00:00
|
|
|
} catch (e) {
|
|
|
|
console.log('getCollections failed- ' + e);
|
2021-01-25 07:05:45 +00:00
|
|
|
}
|
2021-01-13 05:31:02 +00:00
|
|
|
};
|
|
|
|
|
2021-01-31 12:10:45 +00:00
|
|
|
export const getLocalCollections = async (): Promise<collection[]> => {
|
2021-02-09 05:42:23 +00:00
|
|
|
const collections: collection[] =
|
|
|
|
(await localForage.getItem(COLLECTIONS)) ?? [];
|
2021-01-31 12:10:45 +00:00
|
|
|
return collections;
|
2021-02-08 17:02:28 +00:00
|
|
|
};
|
|
|
|
|
2021-02-08 16:10:11 +00:00
|
|
|
export const syncCollections = async (token: string, key: string) => {
|
2021-01-31 12:10:45 +00:00
|
|
|
const localCollections = await getLocalCollections();
|
2021-02-08 17:02:28 +00:00
|
|
|
const lastCollectionUpdationTime =
|
|
|
|
(await localForage.getItem<string>(COLLECTION_UPDATION_TIME)) ?? '0';
|
|
|
|
const updatedCollections =
|
|
|
|
(await getCollections(token, lastCollectionUpdationTime, key)) || [];
|
|
|
|
|
|
|
|
if (updatedCollections.length == 0) {
|
2021-02-08 16:10:11 +00:00
|
|
|
return localCollections;
|
2021-02-08 10:37:59 +00:00
|
|
|
}
|
2021-02-08 17:02:28 +00:00
|
|
|
setLocalFavoriteCollection(updatedCollections);
|
|
|
|
const allCollectionsInstances = [
|
|
|
|
...localCollections,
|
|
|
|
...updatedCollections,
|
|
|
|
];
|
2021-02-08 07:33:46 +00:00
|
|
|
var latestCollectionsInstances = new Map<number, collection>();
|
2021-02-03 08:02:54 +00:00
|
|
|
allCollectionsInstances.forEach((collection) => {
|
2021-02-08 17:02:28 +00:00
|
|
|
if (
|
|
|
|
!latestCollectionsInstances.has(collection.id) ||
|
|
|
|
latestCollectionsInstances.get(collection.id).updationTime <
|
|
|
|
collection.updationTime
|
|
|
|
) {
|
2021-02-03 08:02:54 +00:00
|
|
|
latestCollectionsInstances.set(collection.id, collection);
|
|
|
|
}
|
|
|
|
});
|
2021-02-08 10:37:59 +00:00
|
|
|
|
2021-02-08 17:02:28 +00:00
|
|
|
let collections = [],
|
|
|
|
updationTime = await localForage.getItem<number>(
|
|
|
|
COLLECTION_UPDATION_TIME
|
|
|
|
);
|
2021-02-03 08:02:54 +00:00
|
|
|
for (const [_, collection] of latestCollectionsInstances) {
|
2021-02-08 17:02:28 +00:00
|
|
|
if (!collection.isDeleted) {
|
2021-02-06 07:51:49 +00:00
|
|
|
collections.push(collection);
|
2021-02-08 17:02:28 +00:00
|
|
|
updationTime = Math.max(updationTime, collection.updationTime);
|
2021-02-08 07:33:46 +00:00
|
|
|
}
|
2021-02-03 08:02:54 +00:00
|
|
|
}
|
2021-02-08 17:02:28 +00:00
|
|
|
await localForage.setItem(COLLECTION_UPDATION_TIME, updationTime);
|
|
|
|
await localForage.setItem(COLLECTIONS, collections);
|
2021-02-08 16:10:11 +00:00
|
|
|
return collections;
|
2021-01-13 05:31:02 +00:00
|
|
|
};
|
2021-01-13 12:43:46 +00:00
|
|
|
|
2021-02-09 06:04:19 +00:00
|
|
|
export const getCollectionAndItsLatestFile = (
|
2021-01-14 11:31:20 +00:00
|
|
|
collections: collection[],
|
2021-02-05 16:57:41 +00:00
|
|
|
files: file[]
|
2021-02-09 06:04:19 +00:00
|
|
|
): CollectionAndItsLatestFile[] => {
|
2021-02-05 16:57:41 +00:00
|
|
|
const latestFile = new Map<number, file>();
|
|
|
|
const collectionMap = new Map<number, collection>();
|
|
|
|
|
2021-02-08 17:02:28 +00:00
|
|
|
collections.forEach((collection) =>
|
2021-02-09 05:07:46 +00:00
|
|
|
collectionMap.set(collection.id, collection)
|
2021-02-08 17:02:28 +00:00
|
|
|
);
|
|
|
|
files.forEach((file) => {
|
2021-02-05 16:57:41 +00:00
|
|
|
if (!latestFile.has(file.collectionID)) {
|
2021-02-08 17:02:28 +00:00
|
|
|
latestFile.set(file.collectionID, file);
|
2021-02-05 16:57:41 +00:00
|
|
|
}
|
|
|
|
});
|
2021-02-09 06:04:19 +00:00
|
|
|
let allCollectionAndItsLatestFile: CollectionAndItsLatestFile[] = [];
|
2021-02-05 16:57:41 +00:00
|
|
|
for (const [collectionID, file] of latestFile) {
|
2021-02-09 06:04:19 +00:00
|
|
|
allCollectionAndItsLatestFile.push({
|
2021-02-08 17:02:28 +00:00
|
|
|
collection: collectionMap.get(collectionID),
|
|
|
|
file,
|
|
|
|
});
|
2021-02-05 16:57:41 +00:00
|
|
|
}
|
2021-02-09 06:04:19 +00:00
|
|
|
return allCollectionAndItsLatestFile;
|
2021-02-08 17:02:28 +00:00
|
|
|
};
|
2021-01-14 11:31:20 +00:00
|
|
|
|
2021-01-20 12:05:04 +00:00
|
|
|
export const getFavItemIds = async (files: file[]): Promise<Set<number>> => {
|
2021-02-08 17:02:28 +00:00
|
|
|
let favCollection = await localForage.getItem<collection>(FAV_COLLECTION);
|
|
|
|
if (!favCollection) return new Set();
|
2021-01-20 12:05:04 +00:00
|
|
|
|
2021-02-08 17:02:28 +00:00
|
|
|
return new Set(
|
|
|
|
files
|
2021-02-09 05:07:46 +00:00
|
|
|
.filter((file) => file.collectionID === favCollection.id)
|
2021-02-08 17:02:28 +00:00
|
|
|
.map((file): number => file.id)
|
|
|
|
);
|
|
|
|
};
|
2021-01-20 12:05:04 +00:00
|
|
|
|
2021-01-15 17:04:33 +00:00
|
|
|
export const createAlbum = async (albumName: string) => {
|
2021-01-15 11:11:06 +00:00
|
|
|
return AddCollection(albumName, CollectionType.album);
|
2021-02-08 17:02:28 +00:00
|
|
|
};
|
2021-01-15 11:11:06 +00:00
|
|
|
|
2021-02-08 17:02:28 +00:00
|
|
|
export const AddCollection = async (
|
|
|
|
collectionName: string,
|
|
|
|
type: CollectionType
|
|
|
|
) => {
|
2021-01-13 12:43:46 +00:00
|
|
|
const worker = await new CryptoWorker();
|
2021-01-15 11:11:06 +00:00
|
|
|
const encryptionKey = await getActualKey();
|
|
|
|
const token = getToken();
|
2021-01-19 04:32:12 +00:00
|
|
|
const collectionKey: string = await worker.generateMasterKey();
|
2021-02-08 17:02:28 +00:00
|
|
|
const {
|
|
|
|
encryptedData: encryptedKey,
|
|
|
|
nonce: keyDecryptionNonce,
|
|
|
|
}: keyEncryptionResult = await worker.encryptToB64(
|
|
|
|
collectionKey,
|
|
|
|
encryptionKey
|
|
|
|
);
|
|
|
|
const {
|
|
|
|
encryptedData: encryptedName,
|
|
|
|
nonce: nameDecryptionNonce,
|
|
|
|
}: keyEncryptionResult = await worker.encryptToB64(
|
|
|
|
collectionName,
|
|
|
|
collectionKey
|
|
|
|
);
|
2021-01-13 12:43:46 +00:00
|
|
|
const newCollection: collection = {
|
|
|
|
id: null,
|
|
|
|
owner: null,
|
|
|
|
encryptedKey,
|
|
|
|
keyDecryptionNonce,
|
2021-01-21 02:24:17 +00:00
|
|
|
encryptedName,
|
|
|
|
nameDecryptionNonce,
|
2021-01-15 11:11:06 +00:00
|
|
|
type,
|
2021-01-13 12:43:46 +00:00
|
|
|
attributes: {},
|
|
|
|
sharees: null,
|
|
|
|
updationTime: null,
|
2021-02-08 17:02:28 +00:00
|
|
|
isDeleted: false,
|
2021-01-13 12:43:46 +00:00
|
|
|
};
|
2021-02-08 17:02:28 +00:00
|
|
|
let createdCollection: collection = await createCollection(
|
|
|
|
newCollection,
|
|
|
|
token
|
|
|
|
);
|
|
|
|
createdCollection = await getCollectionSecrets(
|
|
|
|
createdCollection,
|
|
|
|
encryptionKey
|
|
|
|
);
|
2021-01-13 12:43:46 +00:00
|
|
|
return createdCollection;
|
2021-02-08 17:02:28 +00:00
|
|
|
};
|
2021-01-13 12:43:46 +00:00
|
|
|
|
2021-02-08 17:02:28 +00:00
|
|
|
const createCollection = async (
|
|
|
|
collectionData: collection,
|
|
|
|
token: string
|
|
|
|
): Promise<collection> => {
|
2021-01-25 07:05:45 +00:00
|
|
|
try {
|
2021-02-08 17:02:28 +00:00
|
|
|
const response = await HTTPService.post(
|
|
|
|
`${ENDPOINT}/collections`,
|
|
|
|
collectionData,
|
|
|
|
null,
|
|
|
|
{ 'X-Auth-Token': token }
|
|
|
|
);
|
2021-01-25 07:05:45 +00:00
|
|
|
return response.data.collection;
|
|
|
|
} catch (e) {
|
2021-02-08 17:02:28 +00:00
|
|
|
console.log('create Collection failed ' + e);
|
2021-01-25 07:05:45 +00:00
|
|
|
}
|
2021-02-08 17:02:28 +00:00
|
|
|
};
|
2021-01-15 11:11:06 +00:00
|
|
|
|
|
|
|
export const addToFavorites = async (file: file) => {
|
2021-02-08 17:02:28 +00:00
|
|
|
let favCollection: collection = await localForage.getItem<collection>(
|
|
|
|
FAV_COLLECTION
|
|
|
|
);
|
2021-01-15 11:11:06 +00:00
|
|
|
if (!favCollection) {
|
2021-02-08 17:02:28 +00:00
|
|
|
favCollection = await AddCollection(
|
|
|
|
'Favorites',
|
|
|
|
CollectionType.favorites
|
|
|
|
);
|
|
|
|
await localForage.setItem(FAV_COLLECTION, favCollection);
|
2021-01-15 11:11:06 +00:00
|
|
|
}
|
2021-02-09 06:18:46 +00:00
|
|
|
await addToCollection(favCollection, [file]);
|
2021-02-08 17:02:28 +00:00
|
|
|
};
|
2021-01-15 11:11:06 +00:00
|
|
|
|
2021-01-20 13:04:27 +00:00
|
|
|
export const removeFromFavorites = async (file: file) => {
|
2021-02-09 05:07:46 +00:00
|
|
|
let favCollection: collection = await localForage.getItem<collection>(
|
|
|
|
FAV_COLLECTION
|
|
|
|
);
|
2021-02-08 17:02:28 +00:00
|
|
|
await removeFromCollection(favCollection, [file]);
|
|
|
|
};
|
2021-01-20 13:04:27 +00:00
|
|
|
|
2021-02-09 06:18:46 +00:00
|
|
|
const addToCollection = async (collection: collection, files: file[]) => {
|
2021-01-25 07:05:45 +00:00
|
|
|
try {
|
|
|
|
const params = new Object();
|
|
|
|
const worker = await new CryptoWorker();
|
|
|
|
const token = getToken();
|
2021-02-08 17:02:28 +00:00
|
|
|
params['collectionID'] = collection.id;
|
|
|
|
await Promise.all(
|
|
|
|
files.map(async (file) => {
|
2021-02-09 05:07:46 +00:00
|
|
|
file.collectionID = collection.id;
|
2021-02-08 17:02:28 +00:00
|
|
|
const newEncryptedKey: keyEncryptionResult = await worker.encryptToB64(
|
|
|
|
file.key,
|
|
|
|
collection.key
|
|
|
|
);
|
|
|
|
file.encryptedKey = newEncryptedKey.encryptedData;
|
|
|
|
file.keyDecryptionNonce = newEncryptedKey.nonce;
|
|
|
|
if (params['files'] == undefined) {
|
|
|
|
params['files'] = [];
|
|
|
|
}
|
|
|
|
params['files'].push({
|
|
|
|
id: file.id,
|
|
|
|
encryptedKey: file.encryptedKey,
|
|
|
|
keyDecryptionNonce: file.keyDecryptionNonce,
|
|
|
|
});
|
|
|
|
return file;
|
2021-01-25 07:05:45 +00:00
|
|
|
})
|
2021-02-08 17:02:28 +00:00
|
|
|
);
|
|
|
|
await HTTPService.post(
|
|
|
|
`${ENDPOINT}/collections/add-files`,
|
|
|
|
params,
|
|
|
|
null,
|
|
|
|
{ 'X-Auth-Token': token }
|
|
|
|
);
|
2021-01-25 07:05:45 +00:00
|
|
|
} catch (e) {
|
2021-02-08 17:02:28 +00:00
|
|
|
console.log('Add to collection Failed ' + e);
|
2021-01-25 07:05:45 +00:00
|
|
|
}
|
2021-02-08 17:02:28 +00:00
|
|
|
};
|
2021-01-20 13:04:27 +00:00
|
|
|
const removeFromCollection = async (collection: collection, files: file[]) => {
|
2021-01-25 07:05:45 +00:00
|
|
|
try {
|
|
|
|
const params = new Object();
|
|
|
|
const token = getToken();
|
2021-02-08 17:02:28 +00:00
|
|
|
params['collectionID'] = collection.id;
|
|
|
|
await Promise.all(
|
|
|
|
files.map(async (file) => {
|
|
|
|
if (params['fileIDs'] == undefined) {
|
|
|
|
params['fileIDs'] = [];
|
|
|
|
}
|
|
|
|
params['fileIDs'].push(file.id);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
await HTTPService.post(
|
|
|
|
`${ENDPOINT}/collections/remove-files`,
|
|
|
|
params,
|
|
|
|
null,
|
|
|
|
{ 'X-Auth-Token': token }
|
|
|
|
);
|
2021-01-25 07:05:45 +00:00
|
|
|
} catch (e) {
|
2021-02-08 17:02:28 +00:00
|
|
|
console.log('remove from collection failed ' + e);
|
2021-01-25 07:05:45 +00:00
|
|
|
}
|
2021-02-08 17:02:28 +00:00
|
|
|
};
|
2021-01-20 13:04:27 +00:00
|
|
|
|
2021-02-08 17:02:28 +00:00
|
|
|
const setLocalFavoriteCollection = async (collections: collection[]) => {
|
|
|
|
const localFavCollection = await localForage.getItem(FAV_COLLECTION);
|
2021-02-09 05:07:46 +00:00
|
|
|
if (localFavCollection) {
|
|
|
|
return;
|
|
|
|
}
|
2021-02-08 17:02:28 +00:00
|
|
|
const favCollection = collections.filter(
|
|
|
|
(collection) => collection.type == CollectionType.favorites
|
|
|
|
);
|
2021-02-09 05:07:46 +00:00
|
|
|
if (favCollection.length > 0) {
|
2021-02-08 17:02:28 +00:00
|
|
|
await localForage.setItem(FAV_COLLECTION, favCollection[0]);
|
2021-02-09 05:07:46 +00:00
|
|
|
}
|
2021-02-08 17:02:28 +00:00
|
|
|
};
|