feat: refactors and add support for upload/download progress

This commit is contained in:
thecodrr
2021-09-26 11:47:13 +05:00
parent db87d05b3e
commit 7d16b8f388
21 changed files with 229 additions and 136 deletions

View File

@@ -7,7 +7,7 @@ beforeEach(async () => {
test("create vault", () =>
databaseTest().then(async (db) => {
await expect(db.vault.create("password")).resolves.toBe(true);
const vaultKey = await db.context.read("vaultKey");
const vaultKey = await db.storage.read("vaultKey");
expect(vaultKey).toBeDefined();
expect(vaultKey.iv).toBeDefined();
expect(vaultKey.cipher).toBeDefined();

View File

@@ -1,5 +1,6 @@
import Notes from "../collections/notes";
import Storage from "../database/storage";
import FileStorage from "../database/fs";
import Notebooks from "../collections/notebooks";
import Trash from "../collections/trash";
import Tags from "../collections/tags";
@@ -28,12 +29,12 @@ var NNEventSource;
class Database {
/**
*
* @param {any} context
* @param {any} storage
* @param {EventSource} eventsource
*/
constructor(context, eventsource, fs) {
this.context = new Storage(context);
this.fs = fs;
constructor(storage, eventsource, fs) {
this.storage = new Storage(storage);
this.fs = new FileStorage(fs, storage);
NNEventSource = eventsource;
this._syncTimeout = 0;
}
@@ -62,10 +63,10 @@ class Database {
});
EV.subscribe(EVENTS.databaseUpdated, this._onDBWrite.bind(this));
this.session = new Session(this.context);
this.session = new Session(this.storage);
await this._validate();
this.user = new UserManager(this);
this.user = new UserManager(this.storage);
this.syncer = new Sync(this);
this.vault = new Vault(this);
this.conflicts = new Conflicts(this);
@@ -152,7 +153,7 @@ class Database {
await this.user.logout(true, "Password Changed");
break;
case "emailConfirmed":
const token = await this.context.read("token");
const token = await this.storage.read("token");
await this.user.tokenManager._refreshToken(token);
await this.user.fetchUser(true);
EV.publish(EVENTS.userEmailConfirmed);
@@ -166,7 +167,7 @@ class Database {
}
async lastSynced() {
return this.context.read("lastSynced");
return this.storage.read("lastSynced");
}
async _onDBWrite(item) {

View File

@@ -13,8 +13,8 @@ class Migrations {
async init() {
this.dbVersion =
(await this._db.context.read("v")) || CURRENT_DATABASE_VERSION;
this._db.context.write("v", this.dbVersion);
(await this._db.storage.read("v")) || CURRENT_DATABASE_VERSION;
this._db.storage.write("v", this.dbVersion);
}
async migrate() {
@@ -55,7 +55,7 @@ class Migrations {
},
];
await this._migrator.migrate(collections, (item) => item, this.dbVersion);
await this._db.context.write("v", CURRENT_DATABASE_VERSION);
await this._db.storage.write("v", CURRENT_DATABASE_VERSION);
EV.publish(EVENTS.databaseMigrated, {
prev: this.dbVersion,

View File

@@ -13,17 +13,17 @@ class Monographs {
async deinit() {
this.monographs = [];
await this._db.context.write("monographs", this.monographs);
await this._db.storage.write("monographs", this.monographs);
}
async init() {
const user = await this._db.user.getUser();
const token = await this._db.user.tokenManager.getAccessToken();
if (!user || !token || !user.isEmailConfirmed) return;
let monographs = await this._db.context.read("monographs", true);
let monographs = await this._db.storage.read("monographs", true);
try {
monographs = await http.get(`${Constants.API_HOST}/monographs`, token);
await this._db.context.write("monographs", monographs);
await this._db.storage.write("monographs", monographs);
} catch (e) {
console.error(e);
}
@@ -77,7 +77,7 @@ class Monographs {
};
if (opts.password) {
monograph.encryptedContent = await this._db.context.encrypt(
monograph.encryptedContent = await this._db.storage.encrypt(
{ password: opts.password },
JSON.stringify({ type: content.type, data: content.data })
);

View File

@@ -9,7 +9,7 @@ class Outbox {
}
async init() {
this.outbox = (await this._db.context.read("outbox")) || {};
this.outbox = (await this._db.storage.read("outbox")) || {};
for (var id in this.outbox) {
const data = this.outbox[id];
@@ -18,7 +18,7 @@ class Outbox {
case "change_password":
const key = await this._db.user.getEncryptionKey();
const { email } = await this._db.user.getUser();
await this._db.context.deriveCryptoKey(`_uk_@${email}`, {
await this._db.storage.deriveCryptoKey(`_uk_@${email}`, {
password: data.newPassword,
salt: key.salt,
});
@@ -31,14 +31,14 @@ class Outbox {
async add(id, data, action) {
this.outbox[id] = data;
await this._db.context.write("outbox", this.outbox);
await this._db.storage.write("outbox", this.outbox);
await action();
await this.delete(id);
}
delete(id) {
delete this.outbox[id];
return this._db.context.write("outbox", this.outbox);
return this._db.storage.write("outbox", this.outbox);
}
}
export default Outbox;

View File

@@ -4,15 +4,15 @@ class Session {
* @param {import("../database/storage").default} context
*/
constructor(context) {
this._context = context;
this._storage = context;
}
get() {
return this._context.read("t");
return this._storage.read("t");
}
set() {
return this._context.write("t", Date.now());
return this._storage.write("t", Date.now());
}
async valid() {

View File

@@ -14,7 +14,7 @@ class Settings {
}
async init() {
var settings = await this._db.context.read("settings");
var settings = await this._db.storage.read("settings");
this._initSettings(settings);
await this._saveSettings();
@@ -138,7 +138,7 @@ class Settings {
}
async _saveSettings() {
await this._db.context.write("settings", this._settings);
await this._db.storage.write("settings", this._settings);
}
}
export default Settings;

View File

@@ -32,7 +32,7 @@ class Collector {
_serialize(item) {
if (!item) return null;
return this._db.context.encrypt(this.key, JSON.stringify(item));
return this._db.storage.encrypt(this.key, JSON.stringify(item));
}
_encrypt(array) {

View File

@@ -9,12 +9,12 @@ class Conflicts {
async recalculate() {
if (this._db.notes.conflicted.length <= 0) {
await this._db.context.write("hasConflicts", false);
await this._db.storage.write("hasConflicts", false);
}
}
async check() {
let hasConflicts = await this._db.context.read("hasConflicts");
let hasConflicts = await this._db.storage.read("hasConflicts");
if (hasConflicts) {
const mergeConflictError = new Error(
"Merge conflicts detected. Please resolve all conflicts to continue syncing."

View File

@@ -41,7 +41,7 @@ export default class Sync {
this._db = db;
this._collector = new Collector(this._db);
this._merger = new Merger(this._db);
this._tokenManager = new TokenManager(this._db);
this._tokenManager = new TokenManager(this._db.storage);
this._isSyncing = false;
}
@@ -53,7 +53,7 @@ export default class Sync {
}
async _performChecks() {
let lastSynced = (await this._db.context.read("lastSynced")) || 0;
let lastSynced = (await this._db.storage.read("lastSynced")) || 0;
let token = await this._tokenManager.getAccessToken();
// update the conflicts status and if find any, throw
@@ -66,7 +66,7 @@ export default class Sync {
async start(full, force) {
if (this._isSyncing) return false;
if (force) await this._db.context.write("lastSynced", 0);
if (force) await this._db.storage.write("lastSynced", 0);
let { lastSynced, token } = await this._performChecks();
try {
@@ -84,7 +84,7 @@ export default class Sync {
// merge the server response
await this._merger.merge(serverResponse, lastSynced);
await this._downloadAttachments(token);
// await this._downloadAttachments(token);
}
// check for conflicts and throw
@@ -95,7 +95,7 @@ export default class Sync {
// update our lastSynced time
if (lastSynced) {
await this._db.context.write("lastSynced", lastSynced);
await this._db.storage.write("lastSynced", lastSynced);
}
return true;
@@ -123,7 +123,7 @@ export default class Sync {
// update our lastSynced time
if (lastSynced) {
await this._db.context.write("lastSynced", lastSynced);
await this._db.storage.write("lastSynced", lastSynced);
}
EV.publish(EVENTS.appRefreshRequested);
@@ -137,7 +137,7 @@ export default class Sync {
// last edited (but unsynced) time resulting in edited notes
// not getting synced.
// if (serverResponse.lastSynced) {
// await this._db.context.write("lastSynced", serverResponse.lastSynced);
// await this._db.storage.write("lastSynced", serverResponse.lastSynced);
// }
}
@@ -154,37 +154,24 @@ export default class Sync {
async _uploadAttachments(token) {
const attachments = this._db.attachments.pending;
console.log("Uploading attachments", this._db.attachments.pending);
for (let attachment of attachments) {
for (var i = 0; i < attachments.length; ++i) {
const attachment = attachments[i];
EV.publish(EVENTS.attachmentsLoading, {
type: "upload",
total: attachments.length,
current: i,
});
const { hash } = attachment.metadata;
const url = await this._getPresignedURL(hash, token, "PUT");
const uploadResult = await this._db.fs.uploadFile(hash, { url });
if (!uploadResult) throw new Error("Failed to upload file.");
const isUploaded = await this._db.fs.uploadFile(hash);
if (!isUploaded) throw new Error("Failed to upload file.");
await this._db.attachments.markAsUploaded(attachment.id);
}
}
async _downloadAttachments(token) {
const attachments = this._db.attachments.media;
console.log("Downloading attachments", attachments);
for (let attachment of attachments) {
const { hash } = attachment.metadata;
const url = `${Constants.API_HOST}/s3?name=${hash}`;
const downloadResult = await this._db.fs.downloadFile(hash, {
url,
headers: { Authorization: `Bearer ${token}` },
});
if (!downloadResult) throw new Error("Failed to download file.");
}
}
async _getPresignedURL(filename, token, verb) {
const response = await fetch(`${Constants.API_HOST}/s3?name=${filename}`, {
method: verb,
headers: {
Authorization: `Bearer ${token}`,
},
EV.publish(EVENTS.attachmentsLoading, {
type: "upload",
total: attachments.length,
current: attachments.length,
});
if (response.ok) return await response.text();
throw new Error("Couldn't get presigned url.");
}
}

View File

@@ -25,7 +25,7 @@ class Merger {
async _deserialize(item, migrate = true) {
const deserialized = JSON.parse(
await this._db.context.decrypt(this.key, item)
await this._db.storage.decrypt(this.key, item)
);
deserialized.remote = true;
if (!migrate) return deserialized;
@@ -147,7 +147,7 @@ class Merger {
// otherwise we trigger the conflicts
await this._db.content.add({ ...local, conflicted: remote });
await this._db.notes.add({ id: local.noteId, conflicted: true });
await this._db.context.write("hasConflicts", true);
await this._db.storage.write("hasConflicts", true);
}
}
);

View File

@@ -13,14 +13,14 @@ var isRefreshingToken = false;
class TokenManager {
/**
*
* @param {import("./index").default} db
* @param {import("../database/storage").default} storage
*/
constructor(db) {
this._db = db;
constructor(storage) {
this._storage = storage;
}
async getToken(renew = true, forceRenew = false) {
let token = await this._db.context.read("token");
let token = await this._storage.read("token");
if (!token) return;
if (forceRenew || (renew && this._isTokenExpired(token))) {
await this._refreshToken(token);
@@ -107,7 +107,7 @@ class TokenManager {
saveToken(tokenResponse) {
let token = { ...tokenResponse, t: Date.now() };
return this._db.context.write("token", token);
return this._storage.write("token", token);
}
async getAccessTokenFromAuthorizationCode(userId, authCode) {

View File

@@ -22,11 +22,11 @@ const ENDPOINTS = {
class UserManager {
/**
*
* @param {import("./index").default} db
* @param {import("../database/storage").default} storage
*/
constructor(db) {
this._db = db;
this.tokenManager = new TokenManager(db);
constructor(storage) {
this._storage = storage;
this.tokenManager = new TokenManager(storage);
}
async init() {
@@ -36,7 +36,7 @@ class UserManager {
}
async signup(email, password) {
const hashedPassword = await this._db.context.hash(password, email);
const hashedPassword = await this._storage.hash(password, email);
await http.post(`${constants.API_HOST}${ENDPOINTS.signup}`, {
email,
password: hashedPassword,
@@ -48,7 +48,7 @@ class UserManager {
async login(email, password, hashedPassword) {
if (!hashedPassword) {
hashedPassword = await this._db.context.hash(password, email);
hashedPassword = await this._storage.hash(password, email);
}
await this.tokenManager.saveToken(
@@ -63,7 +63,7 @@ class UserManager {
const user = await this.fetchUser();
setUserPersonalizationBytes(user.salt);
await this._db.context.deriveCryptoKey(`_uk_@${user.email}`, {
await this._storage.deriveCryptoKey(`_uk_@${user.email}`, {
password,
salt: user.salt,
});
@@ -83,7 +83,7 @@ class UserManager {
} catch (e) {
console.error(e);
} finally {
await this._db.context.clear();
await this._storage.clear();
EV.publish(EVENTS.userLoggedOut, reason);
EV.publish(EVENTS.appRefreshRequested);
}
@@ -91,11 +91,11 @@ class UserManager {
setUser(user) {
if (!user) return;
return this._db.context.write("user", user);
return this._storage.write("user", user);
}
getUser() {
return this._db.context.read("user");
return this._storage.read("user");
}
async deleteUser(password) {
@@ -104,7 +104,7 @@ class UserManager {
const user = await this.getUser();
await http.post(
`${constants.API_HOST}${ENDPOINTS.deleteUser}`,
{ password: await this._db.context.hash(password, user.email) },
{ password: await this._storage.hash(password, user.email) },
token
);
await this.logout(false, "Account deleted.");
@@ -155,7 +155,7 @@ class UserManager {
async getEncryptionKey() {
const user = await this.getUser();
if (!user) return;
const key = await this._db.context.getCryptoKey(`_uk_@${user.email}`);
const key = await this._storage.getCryptoKey(`_uk_@${user.email}`);
return { key, salt: user.salt };
}
@@ -181,8 +181,8 @@ class UserManager {
const user = await this.getUser();
if (!user) return false;
const key = await this.getEncryptionKey();
const cipher = await this._db.context.encrypt(key, "notesnook");
const plainText = await this._db.context.decrypt({ password }, cipher);
const cipher = await this._storage.encrypt(key, "notesnook");
const plainText = await this._storage.decrypt({ password }, cipher);
return plainText === "notesnook";
} catch (e) {
return false;
@@ -197,12 +197,12 @@ class UserManager {
const { email, salt } = await this.getUser();
var hashedData = {};
if (data.old_password)
hashedData.old_password = await this._db.context.hash(
hashedData.old_password = await this._storage.hash(
data.old_password,
email
);
if (data.new_password)
hashedData.new_password = await this._db.context.hash(
hashedData.new_password = await this._storage.hash(
data.new_password,
email
);
@@ -215,20 +215,21 @@ class UserManager {
},
token
);
await this._db.outbox.add(
type,
{ newPassword: data.new_password },
async () => {
await this._db.sync(true);
// TODO
// await this._db.outbox.add(
// type,
// { newPassword: data.new_password },
// async () => {
// await this._db.sync(true);
await this._db.context.deriveCryptoKey(`_uk_@${email}`, {
password: data.new_password,
salt,
});
// await this._storage.deriveCryptoKey(`_uk_@${email}`, {
// password: data.new_password,
// salt,
// });
await this._db.sync(false, true);
}
);
// await this._db.sync(false, true);
// }
// );
return true;
}
}

View File

@@ -27,7 +27,7 @@ export default class Vault {
*/
constructor(db) {
this._db = db;
this._context = db.context;
this._storage = db.storage;
this._key = "svvaads1212#2123";
this._vaultPassword = null;
this.ERRORS = {
@@ -48,13 +48,13 @@ export default class Vault {
async create(password) {
if (!(await sendCheckUserStatusEvent(CHECK_IDS.vaultAdd))) return;
const vaultKey = await this._context.read("vaultKey");
const vaultKey = await this._storage.read("vaultKey");
if (!vaultKey || !vaultKey.cipher || !vaultKey.iv) {
const encryptedData = await this._context.encrypt(
const encryptedData = await this._storage.encrypt(
{ password },
this._key
);
await this._context.write("vaultKey", encryptedData);
await this._storage.write("vaultKey", encryptedData);
this._password = password;
}
return true;
@@ -67,10 +67,10 @@ export default class Vault {
* @returns {Promise<Boolean>}
*/
async unlock(password) {
const vaultKey = await this._context.read("vaultKey");
const vaultKey = await this._storage.read("vaultKey");
if (!(await this.exists(vaultKey))) throw new Error(this.ERRORS.noVault);
try {
await this._context.decrypt({ password }, vaultKey);
await this._storage.decrypt({ password }, vaultKey);
} catch (e) {
throw new Error(this.ERRORS.wrongPassword);
}
@@ -84,7 +84,7 @@ export default class Vault {
for (var note of lockedNotes) {
await this._unlockNote(note, oldPassword, true);
}
await this._context.remove("vaultKey");
await this._storage.remove("vaultKey");
await this.create(newPassword);
for (var note of lockedNotes) {
await this._lockNote(note, newPassword);
@@ -106,7 +106,7 @@ export default class Vault {
...this._db.notes.locked.map((note) => note.id)
);
}
await this._context.remove("vaultKey");
await this._storage.remove("vaultKey");
this._password = null;
}
@@ -155,7 +155,7 @@ export default class Vault {
}
async exists(vaultKey) {
if (!vaultKey) vaultKey = await this._context.read("vaultKey");
if (!vaultKey) vaultKey = await this._storage.read("vaultKey");
return vaultKey && vaultKey.cipher && vaultKey.iv;
}
@@ -179,7 +179,7 @@ export default class Vault {
/** @private */
async _encryptContent(contentId, content, type, password) {
let encryptedContent = await this._context.encrypt(
let encryptedContent = await this._storage.encrypt(
{ password },
JSON.stringify(content)
);
@@ -191,7 +191,7 @@ export default class Vault {
async _decryptContent(contentId, password) {
let encryptedContent = await this._db.content.raw(contentId);
let decryptedContent = await this._context.decrypt(
let decryptedContent = await this._storage.decrypt(
{ password },
encryptedContent.data
);
@@ -257,12 +257,12 @@ export default class Vault {
/** @inner */
async _getKey() {
if (await this.exists()) return await this._context.read("vaultKey");
if (await this.exists()) return await this._storage.read("vaultKey");
}
/** @inner */
async _setKey(vaultKey) {
if (!vaultKey) return;
await this._context.write("vaultKey", vaultKey);
await this._storage.write("vaultKey", vaultKey);
}
}

View File

@@ -1,7 +1,9 @@
import Collection from "./collection";
import id from "../utils/id";
import { deleteItem } from "../utils/array";
import { deleteItem, hasItem } from "../utils/array";
import hosts from "../utils/constants";
import { EV, EVENTS } from "../common";
import dataurl from "../utils/dataurl";
export default class Attachments extends Collection {
constructor(db, name, cached) {
@@ -9,12 +11,13 @@ export default class Attachments extends Collection {
this.key = null;
}
async _initEncryptionKey() {
async _getEncryptionKey() {
if (!this.key) this.key = await this._db.user.getEncryptionKey();
if (!this.key)
throw new Error(
"Failed to get user encryption key. Cannot cache attachments."
);
return this.key;
}
/**
@@ -78,13 +81,9 @@ export default class Attachments extends Collection {
const attachment = this.all.find((a) => a.metadata.hash === hash);
if (!attachment || !deleteItem(attachment.noteIds, noteId)) return;
if (!attachment.noteIds.length) {
const isDeleted = await this._db.fs.deleteFile(attachment.metadata.hash);
if (!isDeleted) return;
attachment.dateDeleted = Date.now();
const token = await this._db.user.tokenManager.getToken();
const result = await this.fs.deleteFile(attachment.metadata.hash, {
url: `${hosts.API_HOST}/s3?name=${attachment.metadata.hash}`,
headers: { Authorization: `Bearer ${token}` },
});
if (!result) return;
}
return this._collection.updateItem(attachment);
}
@@ -92,10 +91,10 @@ export default class Attachments extends Collection {
async get(hash) {
const attachment = this.all.find((a) => a.metadata.hash === hash);
if (!attachment) return;
await this._initEncryptionKey();
const data = await this._db.fs.readEncrypted(
attachment.metadata.hash,
this.key,
await this._getEncryptionKey(),
{
iv: attachment.iv,
salt: attachment.salt,
@@ -104,8 +103,7 @@ export default class Attachments extends Collection {
outputType: "base64",
}
);
attachment.data = data;
return attachment;
return { data, ...attachment };
}
async attachment(id) {
@@ -121,11 +119,49 @@ export default class Attachments extends Collection {
}
async save(data, type) {
await this._initEncryptionKey();
return await this._db.fs.writeEncrypted(null, {
return await this._db.fs.writeEncrypted(
null,
data,
type,
key: this.key,
await this._getEncryptionKey()
);
}
async download(noteId) {
const attachments = this.media.filter((attachment) =>
hasItem(attachment.noteIds, noteId)
);
console.log("Downloading attachments", attachments);
for (let i = 0; i < attachments.length; i++) {
EV.publish(EVENTS.attachmentsLoading, {
type: "download",
total: attachments.length,
current: i,
});
const { hash } = attachments[i].metadata;
const attachmentExists = await this._db.fs.exists(hash);
if (attachmentExists) continue;
const isDownloaded = await this._db.fs.downloadFile(hash);
if (!isDownloaded) continue;
const attachment = await this.get(hash);
if (!attachment) continue;
EV.publish(EVENTS.mediaAttachmentDownloaded, {
hash,
src: dataurl.fromObject({
type: attachment.metadata.type,
data: attachment.data,
}),
});
}
EV.publish(EVENTS.attachmentsLoading, {
type: "download",
total: attachments.length,
current: attachments.length,
});
}

View File

@@ -30,8 +30,8 @@ class Collection {
*/
constructor(db, name, cached) {
this._db = db;
if (cached) this._collection = new CachedCollection(this._db.context, name);
else this._collection = new IndexedCollection(this._db.context, name);
if (cached) this._collection = new CachedCollection(this._db.storage, name);
else this._collection = new IndexedCollection(this._db.storage, name);
}
}
export default Collection;

View File

@@ -37,6 +37,8 @@ export const EVENTS = {
appRefreshRequested: "app:refreshRequested",
noteRemoved: "note:removed",
tokenRefreshed: "token:refreshed",
attachmentsLoading: "attachments:loading",
mediaAttachmentDownloaded: "attachments:mediaDownloaded",
};
export const CURRENT_DATABASE_VERSION = 5.2;

View File

@@ -20,7 +20,7 @@ export default class Backup {
}
lastBackupTime() {
return this._db.context.read("lastBackupTime");
return this._db.storage.read("lastBackupTime");
}
/**
@@ -35,22 +35,22 @@ export default class Backup {
if (!validTypes.some((t) => t === type))
throw new Error("Invalid type. It must be one of 'mobile' or 'web'.");
let keys = await this._db.context.getAllKeys();
let keys = await this._db.storage.getAllKeys();
let data = filterData(
Object.fromEntries(await this._db.context.readMulti(keys))
Object.fromEntries(await this._db.storage.readMulti(keys))
);
let hash = {};
if (encrypt) {
const key = await this._db.user.getEncryptionKey();
data = await this._db.context.encrypt(key, JSON.stringify(data));
data = await this._db.storage.encrypt(key, JSON.stringify(data));
} else {
hash = { hash: SparkMD5.hash(JSON.stringify(data)), hash_type: "md5" };
}
// save backup time
await this._db.context.write("lastBackupTime", Date.now());
await this._db.storage.write("lastBackupTime", Date.now());
return JSON.stringify({
version: CURRENT_DATABASE_VERSION,
type,
@@ -77,7 +77,7 @@ export default class Backup {
//check if we have encrypted data
if (db.salt && db.iv) {
if (!key) key = await this._db.user.getEncryptionKey();
backup.data = JSON.parse(await this._db.context.decrypt(key, db));
backup.data = JSON.parse(await this._db.storage.decrypt(key, db));
} else if (!this._verify(backup))
throw new Error("Backup file has been tempered, aborting...");

View File

@@ -0,0 +1,65 @@
import hosts from "../utils/constants";
import TokenManager from "../api/token-manager";
export default class FileStorage {
constructor(fs, storage) {
this.fs = fs;
this.tokenManager = new TokenManager(storage);
}
async downloadFile(hash) {
const url = `${hosts.API_HOST}/s3?name=${hash}`;
const token = await this.tokenManager.getAccessToken();
return await this.fs.downloadFile(hash, {
url,
headers: { Authorization: `Bearer ${token}` },
});
}
async uploadFile(hash) {
const token = await this.tokenManager.getAccessToken();
const url = await this._getPresignedURL(hash, token, "PUT");
return await this.fs.uploadFile(hash, { url });
}
readEncrypted(filename, encryptionKey, cipherData) {
return this.fs.readEncrypted(filename, encryptionKey, cipherData);
}
writeEncrypted(filename, data, type, encryptionKey) {
return this._db.fs.writeEncrypted(filename, {
data,
type,
key: encryptionKey,
});
}
async deleteFile(filename) {
const token = await this.tokenManager.getToken();
const url = `${hosts.API_HOST}/s3?name=${hash}`;
return await this.fs.deleteFile(filename, {
url,
headers: { Authorization: `Bearer ${token}` },
});
}
/**
*
* @param {string} filename
* @returns {Promise<boolean>}
*/
exists(filename) {
return this.fs.exists(filename);
}
async _getPresignedURL(filename, token, verb) {
const response = await fetch(`${hosts.API_HOST}/s3?name=${filename}`, {
method: verb,
headers: {
Authorization: `Bearer ${token}`,
},
});
if (response.ok) return await response.text();
throw new Error("Couldn't get presigned url.");
}
}

View File

@@ -8,7 +8,7 @@ function toObject(dataurl) {
}
function fromObject({ type, data }) {
//const { groups } = REGEX.exec(dataurl);
if (REGEX.test(data)) return data;
return `data:${type};base64,${data}`;
}

View File

@@ -16,10 +16,11 @@ class EventManager {
subscribe(name, handler, once = false) {
if (!name || !handler) throw new Error("name and handler are required.");
this._registry.set(handler, { name, once });
return { unsubscribe: () => this.unsubscribe(name, handler) };
}
unsubscribe(_name, handler) {
this._registry.delete(handler);
return this._registry.delete(handler);
}
publish(name, ...args) {