mirror of
https://github.com/streetwriters/notesnook.git
synced 2026-09-01 19:49:54 +02:00
core: migrate monographs to typescript
This commit is contained in:
@@ -26,7 +26,7 @@ const TEST_TIMEOUT = 30 * 1000;
|
||||
afterAll(async () => {
|
||||
const db = await databaseTest();
|
||||
await login(db);
|
||||
await db.monographs.init();
|
||||
await db.monographs.refresh();
|
||||
|
||||
for (const id of db.monographs.monographs) {
|
||||
await db.monographs.unpublish(id);
|
||||
@@ -39,7 +39,7 @@ afterAll(async () => {
|
||||
// databaseTest().then(async (db) => {
|
||||
// await db.user.login(user.email, user.password, user.hashedPassword);
|
||||
|
||||
// await db.monographs.init();
|
||||
// await db.monographs.refresh();
|
||||
|
||||
// expect(db.monographs.all).toBeGreaterThanOrEqual(0);
|
||||
// }));
|
||||
@@ -49,7 +49,7 @@ test(
|
||||
() =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await login(db);
|
||||
await db.monographs.init();
|
||||
await db.monographs.refresh();
|
||||
|
||||
const monographId = await db.monographs.publish(id);
|
||||
|
||||
@@ -70,7 +70,7 @@ test(
|
||||
() =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await login(db);
|
||||
await db.monographs.init();
|
||||
await db.monographs.refresh();
|
||||
|
||||
const monographId = await db.monographs.publish(id);
|
||||
let monograph = await db.monographs.get(monographId);
|
||||
@@ -93,7 +93,7 @@ test(
|
||||
() =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
await login(db);
|
||||
await db.monographs.init();
|
||||
await db.monographs.refresh();
|
||||
|
||||
await db.monographs.publish(id);
|
||||
expect(db.monographs.all.find((m) => m.id === id)).toBeDefined();
|
||||
|
||||
@@ -37,7 +37,7 @@ import Migrations from "./migrations";
|
||||
import Outbox from "./outbox";
|
||||
import UserManager from "./user-manager";
|
||||
import http from "../utils/http";
|
||||
import Monographs from "./monographs";
|
||||
import { Monographs } from "./monographs";
|
||||
import { Offers } from "./offers";
|
||||
import { Attachments } from "../collections/attachments";
|
||||
import { Debug } from "./debug";
|
||||
@@ -176,7 +176,7 @@ class Database {
|
||||
await this.fs().cancel(attachment.metadata.hash, "download");
|
||||
});
|
||||
EV.subscribe(EVENTS.userLoggedOut, async () => {
|
||||
await this.monographs.deinit();
|
||||
await this.monographs.clear();
|
||||
await this.fs().clear();
|
||||
this.disconnectSSE();
|
||||
});
|
||||
@@ -211,7 +211,9 @@ class Database {
|
||||
|
||||
await this.trash.init();
|
||||
|
||||
this.monographs.init().catch(console.error);
|
||||
// we must not wait on network requests that's why
|
||||
// no await
|
||||
this.monographs.refresh();
|
||||
}
|
||||
|
||||
disconnectSSE() {
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import http from "../utils/http";
|
||||
import Constants from "../utils/constants";
|
||||
|
||||
class Monographs {
|
||||
/**
|
||||
*
|
||||
* @param {import("./index").default} db
|
||||
*/
|
||||
constructor(db) {
|
||||
this._db = db;
|
||||
this.monographs = undefined;
|
||||
}
|
||||
|
||||
async deinit() {
|
||||
this.monographs = undefined;
|
||||
await this._db.storage().write("monographs", this.monographs);
|
||||
}
|
||||
|
||||
async init() {
|
||||
try {
|
||||
const user = await this._db.user.getUser();
|
||||
const token = await this._db.tokenManager.getAccessToken();
|
||||
if (!user || !token || !user.isEmailConfirmed) return;
|
||||
let monographs = await this._db.storage().read("monographs", true);
|
||||
monographs = await http.get(`${Constants.API_HOST}/monographs`, token);
|
||||
await this._db.storage().write("monographs", monographs);
|
||||
|
||||
if (monographs) this.monographs = monographs;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if note is published.
|
||||
* @param {string} noteId id of the note
|
||||
* @returns {boolean} Whether note is published or not.
|
||||
*/
|
||||
isPublished(noteId) {
|
||||
return this.monographs && this.monographs.indexOf(noteId) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get note published monograph id
|
||||
* @param {string} noteId id of the note
|
||||
* @returns Monograph Id
|
||||
*/
|
||||
monograph(noteId) {
|
||||
if (!this.monographs) return;
|
||||
|
||||
return this.monographs[this.monographs.indexOf(noteId)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a note as a monograph
|
||||
* @param {string} noteId id of the note to publish
|
||||
* @param {{password: string, selfDestruct: boolean}} opts Publish options
|
||||
* @returns
|
||||
*/
|
||||
async publish(noteId, opts = { password: undefined, selfDestruct: false }) {
|
||||
if (!this.monographs) await this.init();
|
||||
|
||||
let update = !!this.isPublished(noteId);
|
||||
|
||||
const user = await this._db.user.getUser();
|
||||
const token = await this._db.tokenManager.getAccessToken();
|
||||
if (!user || !token) throw new Error("Please login to publish a note.");
|
||||
|
||||
const note = this._db.notes.note(noteId);
|
||||
if (!note) throw new Error("No such note found.");
|
||||
|
||||
const content = await this._db.content.downloadMedia(
|
||||
`monograph-${noteId}`,
|
||||
await this._db.content.raw(note.data.contentId),
|
||||
false
|
||||
);
|
||||
if (!content) throw new Error("This note has no content.");
|
||||
|
||||
const monograph = {
|
||||
id: noteId,
|
||||
title: note.title,
|
||||
userId: user.id,
|
||||
selfDestruct: opts.selfDestruct
|
||||
};
|
||||
|
||||
if (opts.password) {
|
||||
monograph.encryptedContent = await this._db
|
||||
.storage()
|
||||
.encrypt(
|
||||
{ password: opts.password },
|
||||
JSON.stringify({ type: content.type, data: content.data })
|
||||
);
|
||||
} else {
|
||||
monograph.content = JSON.stringify({
|
||||
type: content.type,
|
||||
data: content.data
|
||||
});
|
||||
}
|
||||
|
||||
const method = update ? http.patch.json : http.post.json;
|
||||
|
||||
const { id } = await method(
|
||||
`${Constants.API_HOST}/monographs`,
|
||||
monograph,
|
||||
token
|
||||
);
|
||||
|
||||
this.monographs.push(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpublish a note
|
||||
* @param {string} noteId id of the note to unpublish
|
||||
*/
|
||||
async unpublish(noteId) {
|
||||
if (!this.monographs) await this.init();
|
||||
|
||||
const user = await this._db.user.getUser();
|
||||
const token = await this._db.tokenManager.getAccessToken();
|
||||
if (!user || !token) throw new Error("Please login to publish a note.");
|
||||
|
||||
// const note = this._db.notes.note(noteId);
|
||||
// if (!note) throw new Error("No such note found.");
|
||||
|
||||
if (!this.isPublished(noteId))
|
||||
throw new Error("This note is not published.");
|
||||
|
||||
await http.delete(`${Constants.API_HOST}/monographs/${noteId}`, token);
|
||||
|
||||
this.monographs.splice(this.monographs.indexOf(noteId), 1);
|
||||
}
|
||||
|
||||
get all() {
|
||||
if (!this.monographs) return [];
|
||||
|
||||
return this._db.notes.all.filter(
|
||||
(note) => this.monographs.indexOf(note.id) > -1
|
||||
);
|
||||
}
|
||||
|
||||
async get(monographId) {
|
||||
return await http.get(`${Constants.API_HOST}/monographs/${monographId}`);
|
||||
}
|
||||
}
|
||||
export default Monographs;
|
||||
177
packages/core/src/api/monographs.ts
Normal file
177
packages/core/src/api/monographs.ts
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import http from "../utils/http";
|
||||
import Constants from "../utils/constants";
|
||||
import Database from ".";
|
||||
import { Note, isDeleted } from "../types";
|
||||
import { isUnencryptedContent } from "../collections/content";
|
||||
import { Cipher } from "@notesnook/crypto";
|
||||
|
||||
type BaseMonograph = {
|
||||
id: string;
|
||||
title: string;
|
||||
userId: string;
|
||||
selfDestruct: boolean;
|
||||
};
|
||||
type UnencryptedMonograph = BaseMonograph & {
|
||||
content: string;
|
||||
};
|
||||
type EncryptedMonograph = BaseMonograph & {
|
||||
encryptedContent: Cipher<"base64">;
|
||||
};
|
||||
type Monograph = UnencryptedMonograph | EncryptedMonograph;
|
||||
|
||||
export class Monographs {
|
||||
monographs: string[] = [];
|
||||
constructor(private readonly db: Database) {}
|
||||
|
||||
async clear() {
|
||||
this.monographs = [];
|
||||
await this.db.storage().write("monographs", this.monographs);
|
||||
}
|
||||
|
||||
async refresh() {
|
||||
try {
|
||||
const user = await this.db.user.getUser();
|
||||
const token = await this.db.tokenManager.getAccessToken();
|
||||
if (!user || !token || !user.isEmailConfirmed) return;
|
||||
|
||||
const monographs = await http.get(
|
||||
`${Constants.API_HOST}/monographs`,
|
||||
token
|
||||
);
|
||||
await this.db.storage().write("monographs", monographs);
|
||||
if (monographs) this.monographs = monographs;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if note is published.
|
||||
*/
|
||||
isPublished(noteId: string) {
|
||||
return this.monographs && this.monographs.indexOf(noteId) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get note published monograph id
|
||||
*/
|
||||
monograph(noteId: string) {
|
||||
return this.monographs[this.monographs.indexOf(noteId)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a note as a monograph
|
||||
*/
|
||||
async publish(
|
||||
noteId: string,
|
||||
opts: { password?: string; selfDestruct?: boolean } = {}
|
||||
) {
|
||||
if (!this.monographs.length) await this.refresh();
|
||||
|
||||
const update = !!this.isPublished(noteId);
|
||||
|
||||
const user = await this.db.user.getUser();
|
||||
const token = await this.db.tokenManager.getAccessToken();
|
||||
if (!user || !token) throw new Error("Please login to publish a note.");
|
||||
|
||||
const note = this.db.notes.note(noteId);
|
||||
if (!note) throw new Error("No such note found.");
|
||||
if (!note.data.contentId) throw new Error("Cannot publish an empty note.");
|
||||
|
||||
const contentItem = await this.db.content.raw(note.data.contentId);
|
||||
|
||||
if (!contentItem || isDeleted(contentItem))
|
||||
throw new Error("Could not find content for this note.");
|
||||
|
||||
if (!isUnencryptedContent(contentItem))
|
||||
throw new Error("Cannot published locked notes.");
|
||||
|
||||
const content = await this.db.content.downloadMedia(
|
||||
`monograph-${noteId}`,
|
||||
contentItem,
|
||||
false
|
||||
);
|
||||
|
||||
const monograph: Monograph = {
|
||||
id: noteId,
|
||||
title: note.title,
|
||||
userId: user.id,
|
||||
selfDestruct: opts.selfDestruct || false,
|
||||
...(opts.password
|
||||
? {
|
||||
encryptedContent: await this.db
|
||||
.storage()
|
||||
.encrypt(
|
||||
{ password: opts.password },
|
||||
JSON.stringify({ type: content.type, data: content.data })
|
||||
)
|
||||
}
|
||||
: {
|
||||
content: JSON.stringify({
|
||||
type: content.type,
|
||||
data: content.data
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
const method = update ? http.patch.json : http.post.json;
|
||||
|
||||
const { id } = await method(
|
||||
`${Constants.API_HOST}/monographs`,
|
||||
monograph,
|
||||
token
|
||||
);
|
||||
|
||||
this.monographs.push(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpublish a note
|
||||
*/
|
||||
async unpublish(noteId: string) {
|
||||
if (!this.monographs.length) await this.refresh();
|
||||
|
||||
const user = await this.db.user.getUser();
|
||||
const token = await this.db.tokenManager.getAccessToken();
|
||||
if (!user || !token) throw new Error("Please login to publish a note.");
|
||||
|
||||
if (!this.isPublished(noteId))
|
||||
throw new Error("This note is not published.");
|
||||
|
||||
await http.delete(`${Constants.API_HOST}/monographs/${noteId}`, token);
|
||||
|
||||
this.monographs.splice(this.monographs.indexOf(noteId), 1);
|
||||
}
|
||||
|
||||
get all() {
|
||||
if (!this.monographs.length) return [];
|
||||
|
||||
return this.monographs
|
||||
.map((noteId) => this.db.notes.note(noteId)?.data)
|
||||
.filter(Boolean) as Note[];
|
||||
}
|
||||
|
||||
get(monographId: string) {
|
||||
return http.get(`${Constants.API_HOST}/monographs/${monographId}`);
|
||||
}
|
||||
}
|
||||
@@ -320,7 +320,7 @@ class Sync {
|
||||
|
||||
async stop(lastSynced: number) {
|
||||
// refresh monographs on sync completed
|
||||
await this.db.monographs.init();
|
||||
await this.db.monographs.refresh();
|
||||
|
||||
this.logger.info("Stopping sync", { lastSynced });
|
||||
const storedLastSynced = await this.db.lastSynced();
|
||||
@@ -361,9 +361,6 @@ class Sync {
|
||||
false,
|
||||
lastSynced
|
||||
);
|
||||
|
||||
// refresh monographs on sync completed
|
||||
await this.db.monographs.init();
|
||||
}
|
||||
|
||||
async processChunk(
|
||||
|
||||
Reference in New Issue
Block a user