diff --git a/main.ts b/main.ts index 2ae9795..163a40c 100644 --- a/main.ts +++ b/main.ts @@ -1,51 +1,46 @@ import { App, MarkdownView, Plugin, PluginSettingTab, Setting } from "obsidian"; import { encryptMarkdown } from "src/encryption"; +import { NoteSharingService } from "src/NoteSharingService"; // Remember to rename these classes and interfaces! interface MyPluginSettings { - mySetting: string; + serverUrl: string; } const DEFAULT_SETTINGS: MyPluginSettings = { - mySetting: "default", + serverUrl: "http://localhost:8080", }; export default class MyPlugin extends Plugin { settings: MyPluginSettings; + noteSharingService: NoteSharingService; async onload() { await this.loadSettings(); + this.noteSharingService = new NoteSharingService( + this.settings.serverUrl + ); + + // This adds a settings tab so the user can configure various aspects of the plugin + this.addSettingTab(new SettingsTab(this.app, this)); // This adds a complex command that can check whether the current state of the app allows execution of the command this.addCommand({ - id: "open-sample-modal-complex", - name: "Open sample modal (complex)", + id: "obsidian-note-sharing-share-note", + name: "Create share link", checkCallback: (checking: boolean) => { // Conditions to check const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView); if (markdownView) { - // If checking is true, we're simply "checking" if the command can be run. - // If checking is false, then we want to actually perform the operation. if (!checking) { - this.shareNote(markdownView); + this.noteSharingService.shareNote(markdownView); } - - // This command will only show up in Command Palette when the check function returns true return true; } }, }); - - // This adds a settings tab so the user can configure various aspects of the plugin - this.addSettingTab(new SampleSettingTab(this.app, this)); - } - - // ENCRYPT AND SHARE MD NOTE - private shareNote(mdView: MarkdownView) { - const cryptData = encryptMarkdown(mdView); - console.log(cryptData); } onunload() {} @@ -60,10 +55,11 @@ export default class MyPlugin extends Plugin { async saveSettings() { await this.saveData(this.settings); + this.noteSharingService.serverUrl = this.settings.serverUrl; } } -class SampleSettingTab extends PluginSettingTab { +class SettingsTab extends PluginSettingTab { plugin: MyPlugin; constructor(app: App, plugin: MyPlugin) { @@ -76,18 +72,17 @@ class SampleSettingTab extends PluginSettingTab { containerEl.empty(); - containerEl.createEl("h2", { text: "Settings for my awesome plugin." }); + containerEl.createEl("h2", { text: "Obsidian Note Sharing" }); new Setting(containerEl) - .setName("Setting #1") - .setDesc("It's a secret") + .setName("Server URL") + .setDesc("Server URL hosting the encrypted notes.") .addText((text) => text - .setPlaceholder("Enter your secret") - .setValue(this.plugin.settings.mySetting) + .setPlaceholder("enter URL") + .setValue(this.plugin.settings.serverUrl) .onChange(async (value) => { - console.log("Secret: " + value); - this.plugin.settings.mySetting = value; + this.plugin.settings.serverUrl = value; await this.plugin.saveSettings(); }) ); diff --git a/manifest.json b/manifest.json index 576d4f9..8870bf7 100644 --- a/manifest.json +++ b/manifest.json @@ -1,6 +1,6 @@ { "id": "obsidian-sample-plugin", - "name": "Obsidian Note Sharing (working title)", + "name": "Obsidian Note Sharing", "version": "1.0.1", "minAppVersion": "0.12.0", "description": "This is a sample plugin for Obsidian. This plugin demonstrates some of the capabilities of the Obsidian API.", diff --git a/src/NoteSharingService.ts b/src/NoteSharingService.ts new file mode 100644 index 0000000..164fab5 --- /dev/null +++ b/src/NoteSharingService.ts @@ -0,0 +1,45 @@ +import { MarkdownView, requestUrl } from "obsidian"; +import { encryptMarkdown } from "./encryption"; + +export class NoteSharingService { + private _url: string; + + constructor(serverUrl: string) { + this.serverUrl = serverUrl; + } + + /** + * @param mdView Markdown file to share. + * @returns link to shared note with attached decryption key. + */ + public async shareNote(mdView: MarkdownView): Promise { + const cryptData = encryptMarkdown(mdView); + let url = await this.postNote(cryptData.ciphertext, cryptData.hmac); + url += `#${cryptData.key}`; + console.log(`Note shared: ${url}`); + return url; + } + + private async postNote(ciphertext: string, hmac: string): Promise { + const res = await requestUrl({ + url: `${this._url}/note`, + method: "POST", + contentType: "application/json", + body: JSON.stringify({ ciphertext, hmac }), + }); + + if (res.status == 200 && res.json != null) { + const id = res.json.id; + return `${this._url}/note/${id}`; + } + throw Error("Did not get expected response from server on note POST."); + } + + public set serverUrl(newUrl: string) { + newUrl = newUrl.replace(/([^:]\/)\/+/g, "$1"); + if (newUrl[newUrl.length - 1] == "/") { + newUrl = newUrl.substring(0, newUrl.length - 1); + } + this._url = newUrl; + } +} diff --git a/src/encryption.ts b/src/encryption.ts index bb41ab7..f38b181 100644 --- a/src/encryption.ts +++ b/src/encryption.ts @@ -1,13 +1,13 @@ import { MarkdownView, moment } from "obsidian"; import { generateKey, encryptString } from "./crypto"; -interface encryptedMarkdown { +export interface EncryptedMarkdown { ciphertext: string; hmac: string; key: string; } -export function encryptMarkdown(mdView: MarkdownView): encryptedMarkdown { +export function encryptMarkdown(mdView: MarkdownView): EncryptedMarkdown { const plaintext = mdView.getViewData(); const key = generateKey(moment.now() + plaintext); const { ciphertext, hmac } = encryptString(plaintext, key);