2021-01-05 10:23:28 +00:00
|
|
|
import { getEndpoint } from 'utils/common/apiUtil';
|
|
|
|
import HTTPService from './HTTPService';
|
2021-03-12 06:58:27 +00:00
|
|
|
import localForage from 'utils/storage/localForage';
|
|
|
|
|
2021-01-13 12:44:12 +00:00
|
|
|
import { collection } from './collectionService';
|
2021-03-04 12:14:45 +00:00
|
|
|
import { DataStream, MetadataObject } from './uploadService';
|
2021-02-25 16:31:49 +00:00
|
|
|
import CryptoWorker from 'utils/crypto/cryptoWorker';
|
2021-03-16 06:49:34 +00:00
|
|
|
import { getToken } from 'utils/common/key';
|
2020-09-27 17:18:57 +00:00
|
|
|
|
2020-09-19 21:20:10 +00:00
|
|
|
const ENDPOINT = getEndpoint();
|
2021-02-24 16:05:26 +00:00
|
|
|
const DIFF_LIMIT: number = 2500;
|
2020-09-19 21:20:10 +00:00
|
|
|
|
2021-02-09 05:43:05 +00:00
|
|
|
const FILES = 'files';
|
|
|
|
|
2020-10-19 03:01:34 +00:00
|
|
|
export interface fileAttribute {
|
2021-03-04 12:14:45 +00:00
|
|
|
encryptedData?: DataStream | Uint8Array;
|
2021-02-16 09:44:25 +00:00
|
|
|
objectKey?: string;
|
2021-01-08 03:51:59 +00:00
|
|
|
decryptionHeader: string;
|
2021-01-05 10:23:28 +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;
|
2021-02-16 09:44:25 +00:00
|
|
|
metadata: MetadataObject;
|
2021-01-08 03:51:59 +00:00
|
|
|
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
|
|
|
|
2021-03-16 06:49:34 +00:00
|
|
|
export const syncData = async (collections) => {
|
|
|
|
const { files: resp, isUpdated } = await syncFiles(collections);
|
2020-09-19 21:20:10 +00:00
|
|
|
|
2021-02-08 16:17:17 +00:00
|
|
|
return {
|
|
|
|
data: resp.map((item) => ({
|
2021-01-11 08:13:53 +00:00
|
|
|
...item,
|
|
|
|
w: window.innerWidth,
|
|
|
|
h: window.innerHeight,
|
2021-02-08 16:17:17 +00:00
|
|
|
})),
|
|
|
|
isUpdated,
|
|
|
|
};
|
|
|
|
};
|
2021-01-11 08:13:53 +00:00
|
|
|
|
2021-01-27 06:14:02 +00:00
|
|
|
export const localFiles = async () => {
|
2021-02-09 05:43:05 +00:00
|
|
|
let files: Array<file> = (await localForage.getItem<file[]>(FILES)) || [];
|
2021-01-27 06:14:02 +00:00
|
|
|
return files;
|
2021-02-08 16:17:17 +00:00
|
|
|
};
|
2021-01-27 06:14:02 +00:00
|
|
|
|
2021-03-16 06:49:34 +00:00
|
|
|
export const syncFiles = async (collections: collection[]) => {
|
2021-01-27 06:14:02 +00:00
|
|
|
let files = await localFiles();
|
2021-02-08 16:17:17 +00:00
|
|
|
let isUpdated = false;
|
2021-02-09 05:07:46 +00:00
|
|
|
files = await removeDeletedCollectionFiles(collections, files);
|
2021-02-05 18:51:14 +00:00
|
|
|
for (let collection of collections) {
|
2021-03-16 06:49:34 +00:00
|
|
|
if (!getToken()) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-02-08 17:07:17 +00:00
|
|
|
const lastSyncTime =
|
|
|
|
(await localForage.getItem<number>(`${collection.id}-time`)) ?? 0;
|
|
|
|
if (collection.updationTime === lastSyncTime) {
|
2021-02-08 07:33:46 +00:00
|
|
|
continue;
|
2021-02-08 11:01:05 +00:00
|
|
|
}
|
2021-02-08 16:17:17 +00:00
|
|
|
isUpdated = true;
|
2021-02-08 17:07:17 +00:00
|
|
|
let fetchedFiles =
|
2021-03-16 06:49:34 +00:00
|
|
|
(await getFiles(collection, lastSyncTime, DIFF_LIMIT)) ?? [];
|
2021-02-08 10:56:47 +00:00
|
|
|
files.push(...fetchedFiles);
|
2021-03-16 09:41:11 +00:00
|
|
|
var latestVersionFiles = new Map<string, file>();
|
2021-02-08 10:56:47 +00:00
|
|
|
files.forEach((file) => {
|
2021-03-16 09:41:11 +00:00
|
|
|
const uid = `${file.collectionID}-${file.id}`;
|
2021-02-08 16:17:17 +00:00
|
|
|
if (
|
2021-03-16 09:41:11 +00:00
|
|
|
!latestVersionFiles.has(uid) ||
|
|
|
|
latestVersionFiles.get(uid).updationTime < file.updationTime
|
2021-02-08 16:17:17 +00:00
|
|
|
) {
|
2021-03-16 09:41:11 +00:00
|
|
|
latestVersionFiles.set(uid, file);
|
2021-02-08 10:56:47 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
files = [];
|
|
|
|
for (const [_, file] of latestVersionFiles) {
|
2021-02-08 16:17:17 +00:00
|
|
|
if (file.isDeleted) {
|
2021-02-08 10:56:47 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
files.push(file);
|
2021-02-06 07:54:36 +00:00
|
|
|
}
|
2021-02-08 10:56:47 +00:00
|
|
|
files = files.sort(
|
|
|
|
(a, b) => b.metadata.creationTime - a.metadata.creationTime
|
|
|
|
);
|
|
|
|
await localForage.setItem('files', files);
|
2021-02-08 16:17:17 +00:00
|
|
|
await localForage.setItem(
|
|
|
|
`${collection.id}-time`,
|
2021-02-08 17:07:17 +00:00
|
|
|
collection.updationTime
|
2021-02-08 16:17:17 +00:00
|
|
|
);
|
2021-02-05 18:51:14 +00:00
|
|
|
}
|
2021-02-08 16:17:17 +00:00
|
|
|
return { files, isUpdated };
|
2021-01-14 11:31:20 +00:00
|
|
|
};
|
|
|
|
|
2021-02-08 16:17:17 +00:00
|
|
|
export const getFiles = async (
|
|
|
|
collection: collection,
|
2021-02-09 05:07:46 +00:00
|
|
|
sinceTime: number,
|
2021-03-16 06:49:34 +00:00
|
|
|
limit: number
|
2021-02-08 16:17:17 +00:00
|
|
|
): 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
|
|
|
let time =
|
2021-02-08 16:17:17 +00:00
|
|
|
sinceTime ||
|
2021-02-09 05:07:46 +00:00
|
|
|
(await localForage.getItem<number>(`${collection.id}-time`)) ||
|
|
|
|
0;
|
2021-02-05 18:51:14 +00:00
|
|
|
let resp;
|
|
|
|
do {
|
2021-03-16 06:49:34 +00:00
|
|
|
const token = getToken();
|
|
|
|
if (!token) {
|
|
|
|
break;
|
|
|
|
}
|
2021-02-08 16:17:17 +00:00
|
|
|
resp = await HTTPService.get(
|
|
|
|
`${ENDPOINT}/collections/diff`,
|
|
|
|
{
|
2021-02-17 08:35:19 +00:00
|
|
|
collectionID: collection.id,
|
|
|
|
sinceTime: time,
|
|
|
|
limit: limit,
|
2021-02-08 16:17:17 +00:00
|
|
|
},
|
2021-02-05 18:51:14 +00:00
|
|
|
{
|
2021-02-08 16:17:17 +00:00
|
|
|
'X-Auth-Token': token,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
promises.push(
|
|
|
|
...resp.data.diff.map(async (file: file) => {
|
2021-02-05 18:51:14 +00:00
|
|
|
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-02-08 16:17:17 +00:00
|
|
|
})
|
|
|
|
);
|
2021-02-05 18:51:14 +00:00
|
|
|
|
|
|
|
if (resp.data.diff.length) {
|
2021-02-17 08:35:19 +00:00
|
|
|
time = resp.data.diff.slice(-1)[0].updationTime;
|
2021-02-05 18:51:14 +00:00
|
|
|
}
|
|
|
|
} while (resp.data.diff.length === limit);
|
|
|
|
return await Promise.all(promises);
|
2021-01-25 07:05:45 +00:00
|
|
|
} catch (e) {
|
2021-03-08 12:36:10 +00:00
|
|
|
console.error('Get files failed', e);
|
2021-01-14 11:31:20 +00:00
|
|
|
}
|
2021-02-08 16:17:17 +00:00
|
|
|
};
|
2021-01-05 10:23:28 +00:00
|
|
|
|
2021-02-08 17:07:17 +00:00
|
|
|
const removeDeletedCollectionFiles = async (
|
|
|
|
collections: collection[],
|
|
|
|
files: file[]
|
|
|
|
) => {
|
2021-02-08 16:17:17 +00:00
|
|
|
const syncedCollectionIds = new Set<number>();
|
|
|
|
for (let collection of collections) {
|
|
|
|
syncedCollectionIds.add(collection.id);
|
|
|
|
}
|
|
|
|
files = files.filter((file) => syncedCollectionIds.has(file.collectionID));
|
2021-02-09 05:07:46 +00:00
|
|
|
return files;
|
2021-02-08 16:17:17 +00:00
|
|
|
};
|