|
@@ -1,35 +1,37 @@
|
|
|
-import { getEndpoint } from "utils/common/apiUtil";
|
|
|
-import { getData, LS_KEYS } from "utils/storage/localStorage";
|
|
|
-import { file, user, getFiles } from "./fileService";
|
|
|
+import { getEndpoint } from 'utils/common/apiUtil';
|
|
|
+import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
|
|
+import { file, user, getFiles } from './fileService';
|
|
|
import localForage from 'localforage';
|
|
|
|
|
|
-import HTTPService from "./HTTPService";
|
|
|
+import HTTPService from './HTTPService';
|
|
|
import * as Comlink from 'comlink';
|
|
|
-import { keyEncryptionResult } from "./uploadService";
|
|
|
-import { getActualKey, getToken } from "utils/common/key";
|
|
|
-
|
|
|
+import { keyEncryptionResult } from './uploadService';
|
|
|
+import { getActualKey, getToken } from 'utils/common/key';
|
|
|
|
|
|
const CryptoWorker: any =
|
|
|
typeof window !== 'undefined' &&
|
|
|
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
|
|
|
const ENDPOINT = getEndpoint();
|
|
|
|
|
|
-
|
|
|
enum CollectionType {
|
|
|
- folder = "folder",
|
|
|
- favorites = "favorites",
|
|
|
- album = "album",
|
|
|
+ folder = 'folder',
|
|
|
+ favorites = 'favorites',
|
|
|
+ album = 'album',
|
|
|
}
|
|
|
|
|
|
+const COLLECTION_UPDATION_TIME = 'collection-updation-time';
|
|
|
+const FAV_COLLECTION = 'fav-collection';
|
|
|
+const COLLECTIONS = 'collections';
|
|
|
+
|
|
|
export interface collection {
|
|
|
- id: string;
|
|
|
+ id: number;
|
|
|
owner: user;
|
|
|
key?: string;
|
|
|
name?: string;
|
|
|
encryptedName?: string;
|
|
|
nameDecryptionNonce?: string;
|
|
|
type: string;
|
|
|
- attributes: collectionAttributes
|
|
|
+ attributes: collectionAttributes;
|
|
|
sharees: user[];
|
|
|
updationTime: number;
|
|
|
encryptedKey: string;
|
|
@@ -39,16 +41,18 @@ export interface collection {
|
|
|
|
|
|
interface collectionAttributes {
|
|
|
encryptedPath?: string;
|
|
|
- pathDecryptionNonce?: string
|
|
|
-};
|
|
|
+ pathDecryptionNonce?: string;
|
|
|
+}
|
|
|
|
|
|
-export interface collectionLatestFile {
|
|
|
- collection: collection
|
|
|
+export interface CollectionAndItsLatestFile {
|
|
|
+ collection: collection;
|
|
|
file: file;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-const getCollectionSecrets = async (collection: collection, masterKey: string) => {
|
|
|
+const getCollectionSecrets = async (
|
|
|
+ collection: collection,
|
|
|
+ masterKey: string
|
|
|
+) => {
|
|
|
const worker = await new CryptoWorker();
|
|
|
const userID = getData(LS_KEYS.USER).id;
|
|
|
let decryptedKey: string;
|
|
@@ -58,7 +62,6 @@ const getCollectionSecrets = async (collection: collection, masterKey: string) =
|
|
|
collection.keyDecryptionNonce,
|
|
|
masterKey
|
|
|
);
|
|
|
-
|
|
|
} else {
|
|
|
const keyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
|
|
|
const secretKey = await worker.decryptB64(
|
|
@@ -72,10 +75,13 @@ const getCollectionSecrets = async (collection: collection, masterKey: string) =
|
|
|
secretKey
|
|
|
);
|
|
|
}
|
|
|
- collection.name = collection.name || await worker.decryptString(
|
|
|
- collection.encryptedName,
|
|
|
- collection.nameDecryptionNonce,
|
|
|
- decryptedKey);
|
|
|
+ collection.name =
|
|
|
+ collection.name ||
|
|
|
+ (await worker.decryptString(
|
|
|
+ collection.encryptedName,
|
|
|
+ collection.nameDecryptionNonce,
|
|
|
+ decryptedKey
|
|
|
+ ));
|
|
|
return {
|
|
|
...collection,
|
|
|
key: decryptedKey,
|
|
@@ -88,85 +94,131 @@ const getCollections = async (
|
|
|
key: string
|
|
|
): Promise<collection[]> => {
|
|
|
try {
|
|
|
- const resp = await HTTPService.get(`${ENDPOINT}/collections`, {
|
|
|
- sinceTime: sinceTime,
|
|
|
- }, { 'X-Auth-Token': token, });
|
|
|
+ const resp = await HTTPService.get(
|
|
|
+ `${ENDPOINT}/collections`,
|
|
|
+ {
|
|
|
+ sinceTime: sinceTime,
|
|
|
+ },
|
|
|
+ { 'X-Auth-Token': token }
|
|
|
+ );
|
|
|
const promises: Promise<collection>[] = resp.data.collections.map(
|
|
|
(collection: collection) => getCollectionSecrets(collection, key)
|
|
|
);
|
|
|
return await Promise.all(promises);
|
|
|
- }
|
|
|
- catch (e) {
|
|
|
- console.log("getCollections failed- " + e);
|
|
|
+ } catch (e) {
|
|
|
+ console.log('getCollections failed- ' + e);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
export const getLocalCollections = async (): Promise<collection[]> => {
|
|
|
- const collections = await localForage.getItem('collections') as collection[] ?? [];
|
|
|
+ const collections: collection[] =
|
|
|
+ (await localForage.getItem(COLLECTIONS)) ?? [];
|
|
|
return collections;
|
|
|
-}
|
|
|
-export const fetchUpdatedCollections = async (token: string, key: string) => {
|
|
|
- const collectionUpdateTime = await localForage.getItem('collection-update-time') as string;
|
|
|
- const updatedCollections = await getCollections(token, collectionUpdateTime ?? '0', key) || [];
|
|
|
- const favCollection = await localForage.getItem('fav-collection') as collection[] ?? updatedCollections.filter(collection => collection.type === CollectionType.favorites);
|
|
|
+};
|
|
|
+
|
|
|
+export const syncCollections = async (token: string, key: string) => {
|
|
|
const localCollections = await getLocalCollections();
|
|
|
- const allCollectionsInstances = [...localCollections, ...updatedCollections];
|
|
|
- var latestCollectionsInstances = new Map<string, collection>();
|
|
|
+ const lastCollectionUpdationTime =
|
|
|
+ (await localForage.getItem<string>(COLLECTION_UPDATION_TIME)) ?? '0';
|
|
|
+ const updatedCollections =
|
|
|
+ (await getCollections(token, lastCollectionUpdationTime, key)) || [];
|
|
|
+
|
|
|
+ if (updatedCollections.length == 0) {
|
|
|
+ return localCollections;
|
|
|
+ }
|
|
|
+ setLocalFavoriteCollection(updatedCollections);
|
|
|
+ const allCollectionsInstances = [
|
|
|
+ ...localCollections,
|
|
|
+ ...updatedCollections,
|
|
|
+ ];
|
|
|
+ var latestCollectionsInstances = new Map<number, collection>();
|
|
|
allCollectionsInstances.forEach((collection) => {
|
|
|
- if (!latestCollectionsInstances.has(collection.id) || latestCollectionsInstances.get(collection.id).updationTime < collection.updationTime) {
|
|
|
+ if (
|
|
|
+ !latestCollectionsInstances.has(collection.id) ||
|
|
|
+ latestCollectionsInstances.get(collection.id).updationTime <
|
|
|
+ collection.updationTime
|
|
|
+ ) {
|
|
|
latestCollectionsInstances.set(collection.id, collection);
|
|
|
}
|
|
|
});
|
|
|
- let collections = [];
|
|
|
+
|
|
|
+ let collections = [],
|
|
|
+ updationTime = await localForage.getItem<number>(
|
|
|
+ COLLECTION_UPDATION_TIME
|
|
|
+ );
|
|
|
for (const [_, collection] of latestCollectionsInstances) {
|
|
|
- collections.push(collection);
|
|
|
+ if (!collection.isDeleted) {
|
|
|
+ collections.push(collection);
|
|
|
+ updationTime = Math.max(updationTime, collection.updationTime);
|
|
|
+ }
|
|
|
}
|
|
|
- await localForage.setItem('fav-collection', favCollection);
|
|
|
- await localForage.setItem('collections', collections);
|
|
|
- return updatedCollections;
|
|
|
+ await localForage.setItem(COLLECTION_UPDATION_TIME, updationTime);
|
|
|
+ await localForage.setItem(COLLECTIONS, collections);
|
|
|
+ return collections;
|
|
|
};
|
|
|
|
|
|
-export const getCollectionLatestFile = (
|
|
|
+export const getCollectionAndItsLatestFile = (
|
|
|
collections: collection[],
|
|
|
files: file[]
|
|
|
-): collectionLatestFile[] => {
|
|
|
+): CollectionAndItsLatestFile[] => {
|
|
|
const latestFile = new Map<number, file>();
|
|
|
const collectionMap = new Map<number, collection>();
|
|
|
|
|
|
- collections.forEach(collection => collectionMap.set(Number(collection.id), collection));
|
|
|
- files.forEach(file => {
|
|
|
+ collections.forEach((collection) =>
|
|
|
+ collectionMap.set(collection.id, collection)
|
|
|
+ );
|
|
|
+ files.forEach((file) => {
|
|
|
if (!latestFile.has(file.collectionID)) {
|
|
|
- latestFile.set(file.collectionID, file)
|
|
|
+ latestFile.set(file.collectionID, file);
|
|
|
}
|
|
|
});
|
|
|
- let allCollectionLatestFile: collectionLatestFile[] = [];
|
|
|
+ let allCollectionAndItsLatestFile: CollectionAndItsLatestFile[] = [];
|
|
|
for (const [collectionID, file] of latestFile) {
|
|
|
- allCollectionLatestFile.push({ collection: collectionMap.get(collectionID), file });
|
|
|
+ allCollectionAndItsLatestFile.push({
|
|
|
+ collection: collectionMap.get(collectionID),
|
|
|
+ file,
|
|
|
+ });
|
|
|
}
|
|
|
- return allCollectionLatestFile;
|
|
|
-}
|
|
|
+ return allCollectionAndItsLatestFile;
|
|
|
+};
|
|
|
|
|
|
export const getFavItemIds = async (files: file[]): Promise<Set<number>> => {
|
|
|
+ let favCollection = await localForage.getItem<collection>(FAV_COLLECTION);
|
|
|
+ if (!favCollection) return new Set();
|
|
|
|
|
|
- let favCollection: collection = (await localForage.getItem<collection>('fav-collection'))[0];
|
|
|
- if (!favCollection)
|
|
|
- return new Set();
|
|
|
-
|
|
|
- return new Set(files.filter(file => file.collectionID === Number(favCollection.id)).map((file): number => file.id));
|
|
|
-}
|
|
|
+ return new Set(
|
|
|
+ files
|
|
|
+ .filter((file) => file.collectionID === favCollection.id)
|
|
|
+ .map((file): number => file.id)
|
|
|
+ );
|
|
|
+};
|
|
|
|
|
|
export const createAlbum = async (albumName: string) => {
|
|
|
return AddCollection(albumName, CollectionType.album);
|
|
|
-}
|
|
|
-
|
|
|
+};
|
|
|
|
|
|
-export const AddCollection = async (collectionName: string, type: CollectionType) => {
|
|
|
+export const AddCollection = async (
|
|
|
+ collectionName: string,
|
|
|
+ type: CollectionType
|
|
|
+) => {
|
|
|
const worker = await new CryptoWorker();
|
|
|
const encryptionKey = await getActualKey();
|
|
|
const token = getToken();
|
|
|
const collectionKey: string = await worker.generateMasterKey();
|
|
|
- const { encryptedData: encryptedKey, nonce: keyDecryptionNonce }: keyEncryptionResult = await worker.encryptToB64(collectionKey, encryptionKey);
|
|
|
- const { encryptedData: encryptedName, nonce: nameDecryptionNonce }: keyEncryptionResult = await worker.encryptToB64(collectionName, collectionKey);
|
|
|
+ const {
|
|
|
+ encryptedData: encryptedKey,
|
|
|
+ nonce: keyDecryptionNonce,
|
|
|
+ }: keyEncryptionResult = await worker.encryptToB64(
|
|
|
+ collectionKey,
|
|
|
+ encryptionKey
|
|
|
+ );
|
|
|
+ const {
|
|
|
+ encryptedData: encryptedName,
|
|
|
+ nonce: nameDecryptionNonce,
|
|
|
+ }: keyEncryptionResult = await worker.encryptToB64(
|
|
|
+ collectionName,
|
|
|
+ collectionKey
|
|
|
+ );
|
|
|
const newCollection: collection = {
|
|
|
id: null,
|
|
|
owner: null,
|
|
@@ -178,76 +230,126 @@ export const AddCollection = async (collectionName: string, type: CollectionType
|
|
|
attributes: {},
|
|
|
sharees: null,
|
|
|
updationTime: null,
|
|
|
- isDeleted: false
|
|
|
+ isDeleted: false,
|
|
|
};
|
|
|
- let createdCollection: collection = await createCollection(newCollection, token);
|
|
|
- createdCollection = await getCollectionSecrets(createdCollection, encryptionKey);
|
|
|
+ let createdCollection: collection = await createCollection(
|
|
|
+ newCollection,
|
|
|
+ token
|
|
|
+ );
|
|
|
+ createdCollection = await getCollectionSecrets(
|
|
|
+ createdCollection,
|
|
|
+ encryptionKey
|
|
|
+ );
|
|
|
return createdCollection;
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
-const createCollection = async (collectionData: collection, token: string): Promise<collection> => {
|
|
|
+const createCollection = async (
|
|
|
+ collectionData: collection,
|
|
|
+ token: string
|
|
|
+): Promise<collection> => {
|
|
|
try {
|
|
|
- const response = await HTTPService.post(`${ENDPOINT}/collections`, collectionData, null, { 'X-Auth-Token': token });
|
|
|
+ const response = await HTTPService.post(
|
|
|
+ `${ENDPOINT}/collections`,
|
|
|
+ collectionData,
|
|
|
+ null,
|
|
|
+ { 'X-Auth-Token': token }
|
|
|
+ );
|
|
|
return response.data.collection;
|
|
|
} catch (e) {
|
|
|
- console.log("create Collection failed " + e);
|
|
|
+ console.log('create Collection failed ' + e);
|
|
|
}
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
export const addToFavorites = async (file: file) => {
|
|
|
- let favCollection: collection = (await localForage.getItem<collection>('fav-collection'))[0];
|
|
|
+ let favCollection: collection = await localForage.getItem<collection>(
|
|
|
+ FAV_COLLECTION
|
|
|
+ );
|
|
|
if (!favCollection) {
|
|
|
- favCollection = await AddCollection("Favorites", CollectionType.favorites);
|
|
|
- await localForage.setItem('fav-collection', favCollection);
|
|
|
+ favCollection = await AddCollection(
|
|
|
+ 'Favorites',
|
|
|
+ CollectionType.favorites
|
|
|
+ );
|
|
|
+ await localForage.setItem(FAV_COLLECTION, favCollection);
|
|
|
}
|
|
|
- await addtoCollection(favCollection, [file])
|
|
|
-}
|
|
|
+ await addToCollection(favCollection, [file]);
|
|
|
+};
|
|
|
|
|
|
export const removeFromFavorites = async (file: file) => {
|
|
|
- let favCollection: collection = (await localForage.getItem<collection>('fav-collection'))[0];
|
|
|
- await removeFromCollection(favCollection, [file])
|
|
|
-}
|
|
|
+ let favCollection: collection = await localForage.getItem<collection>(
|
|
|
+ FAV_COLLECTION
|
|
|
+ );
|
|
|
+ await removeFromCollection(favCollection, [file]);
|
|
|
+};
|
|
|
|
|
|
-const addtoCollection = async (collection: collection, files: file[]) => {
|
|
|
+const addToCollection = async (collection: collection, files: file[]) => {
|
|
|
try {
|
|
|
const params = new Object();
|
|
|
const worker = await new CryptoWorker();
|
|
|
const token = getToken();
|
|
|
- params["collectionID"] = collection.id;
|
|
|
- await Promise.all(files.map(async file => {
|
|
|
- file.collectionID = Number(collection.id);
|
|
|
- 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
|
|
|
+ params['collectionID'] = collection.id;
|
|
|
+ await Promise.all(
|
|
|
+ files.map(async (file) => {
|
|
|
+ file.collectionID = collection.id;
|
|
|
+ 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;
|
|
|
})
|
|
|
- return file;
|
|
|
- }));
|
|
|
- await HTTPService.post(`${ENDPOINT}/collections/add-files`, params, null, { 'X-Auth-Token': token });
|
|
|
+ );
|
|
|
+ await HTTPService.post(
|
|
|
+ `${ENDPOINT}/collections/add-files`,
|
|
|
+ params,
|
|
|
+ null,
|
|
|
+ { 'X-Auth-Token': token }
|
|
|
+ );
|
|
|
} catch (e) {
|
|
|
- console.log("Add to collection Failed " + e);
|
|
|
+ console.log('Add to collection Failed ' + e);
|
|
|
}
|
|
|
-}
|
|
|
+};
|
|
|
const removeFromCollection = async (collection: collection, files: file[]) => {
|
|
|
try {
|
|
|
const params = new Object();
|
|
|
const token = getToken();
|
|
|
- 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 });
|
|
|
+ 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 }
|
|
|
+ );
|
|
|
} catch (e) {
|
|
|
- console.log("remove from collection failed " + e);
|
|
|
+ console.log('remove from collection failed ' + e);
|
|
|
}
|
|
|
-}
|
|
|
+};
|
|
|
|
|
|
+const setLocalFavoriteCollection = async (collections: collection[]) => {
|
|
|
+ const localFavCollection = await localForage.getItem(FAV_COLLECTION);
|
|
|
+ if (localFavCollection) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const favCollection = collections.filter(
|
|
|
+ (collection) => collection.type == CollectionType.favorites
|
|
|
+ );
|
|
|
+ if (favCollection.length > 0) {
|
|
|
+ await localForage.setItem(FAV_COLLECTION, favCollection[0]);
|
|
|
+ }
|
|
|
+};
|