mirror of
https://github.com/d0zingcat/obsidian-send-to-ghost.git
synced 2026-05-13 15:09:48 +00:00
version 1 done
This commit is contained in:
@@ -3,7 +3,7 @@ import process from "process";
|
||||
import builtins from 'builtin-modules'
|
||||
|
||||
const banner =
|
||||
`/*
|
||||
`/*
|
||||
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||
if you want to view the source, please visit the github repository of this plugin
|
||||
*/
|
||||
@@ -15,7 +15,7 @@ esbuild.build({
|
||||
banner: {
|
||||
js: banner,
|
||||
},
|
||||
entryPoints: ['main.ts'],
|
||||
entryPoints: ['src/main.ts'],
|
||||
bundle: true,
|
||||
external: [
|
||||
'obsidian',
|
||||
|
||||
137
main.ts
137
main.ts
@@ -1,137 +0,0 @@
|
||||
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
|
||||
|
||||
// Remember to rename these classes and interfaces!
|
||||
|
||||
interface MyPluginSettings {
|
||||
mySetting: string;
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: MyPluginSettings = {
|
||||
mySetting: 'default'
|
||||
}
|
||||
|
||||
export default class MyPlugin extends Plugin {
|
||||
settings: MyPluginSettings;
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
// This creates an icon in the left ribbon.
|
||||
const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
|
||||
// Called when the user clicks the icon.
|
||||
new Notice('This is a notice!');
|
||||
});
|
||||
// Perform additional things with the ribbon
|
||||
ribbonIconEl.addClass('my-plugin-ribbon-class');
|
||||
|
||||
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
|
||||
const statusBarItemEl = this.addStatusBarItem();
|
||||
statusBarItemEl.setText('Status Bar Text');
|
||||
|
||||
// This adds a simple command that can be triggered anywhere
|
||||
this.addCommand({
|
||||
id: 'open-sample-modal-simple',
|
||||
name: 'Open sample modal (simple)',
|
||||
callback: () => {
|
||||
new SampleModal(this.app).open();
|
||||
}
|
||||
});
|
||||
// This adds an editor command that can perform some operation on the current editor instance
|
||||
this.addCommand({
|
||||
id: 'sample-editor-command',
|
||||
name: 'Sample editor command',
|
||||
editorCallback: (editor: Editor, view: MarkdownView) => {
|
||||
console.log(editor.getSelection());
|
||||
editor.replaceSelection('Sample Editor Command');
|
||||
}
|
||||
});
|
||||
// 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)',
|
||||
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) {
|
||||
new SampleModal(this.app).open();
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
// If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin)
|
||||
// Using this function will automatically remove the event listener when this plugin is disabled.
|
||||
this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
|
||||
console.log('click', evt);
|
||||
});
|
||||
|
||||
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
|
||||
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
|
||||
}
|
||||
|
||||
onunload() {
|
||||
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
}
|
||||
|
||||
class SampleModal extends Modal {
|
||||
constructor(app: App) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const {contentEl} = this;
|
||||
contentEl.setText('Woah!');
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const {contentEl} = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
class SampleSettingTab extends PluginSettingTab {
|
||||
plugin: MyPlugin;
|
||||
|
||||
constructor(app: App, plugin: MyPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const {containerEl} = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
containerEl.createEl('h2', {text: 'Settings for my awesome plugin.'});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Setting #1')
|
||||
.setDesc('It\'s a secret')
|
||||
.addText(text => text
|
||||
.setPlaceholder('Enter your secret')
|
||||
.setValue(this.plugin.settings.mySetting)
|
||||
.onChange(async (value) => {
|
||||
console.log('Secret: ' + value);
|
||||
this.plugin.settings.mySetting = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"id": "obsidian-sample-plugin",
|
||||
"name": "Sample Plugin",
|
||||
"version": "1.0.1",
|
||||
"id": "obsidian-ghost-publish",
|
||||
"name": "Obsidian Ghost Publish",
|
||||
"version": "1.0.0",
|
||||
"minAppVersion": "0.12.0",
|
||||
"description": "This is a sample plugin for Obsidian. This plugin demonstrates some of the capabilities of the Obsidian API.",
|
||||
"author": "Obsidian",
|
||||
"authorUrl": "https://obsidian.md",
|
||||
"description": "Single click to publish to Ghost",
|
||||
"author": "@jaynguyens <jay@nguyens.co>",
|
||||
"authorUrl": "https://nguyens.co",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
}
|
||||
24
package.json
24
package.json
@@ -1,24 +1,32 @@
|
||||
{
|
||||
"name": "obsidian-sample-plugin",
|
||||
"version": "1.0.1",
|
||||
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
|
||||
"main": "main.js",
|
||||
"name": "obsidian-ghost-publish",
|
||||
"version": "1.0.0",
|
||||
"description": "Obsidian plugin for easy publish to ghost with a single click.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
"dev": "node esbuild.config.mjs",
|
||||
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
||||
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"author": {
|
||||
"name": "Jay Nguyen",
|
||||
"email": "jay@nguyens.co",
|
||||
"url": "https://github.com/jaynguyens/obsidian-ghost-publish"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/markdown-it": "^12.2.3",
|
||||
"@types/node": "^16.11.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.2.0",
|
||||
"@typescript-eslint/parser": "^5.2.0",
|
||||
"builtin-modules": "^3.2.0",
|
||||
"esbuild": "0.13.12",
|
||||
"obsidian": "latest",
|
||||
"obsidian": "^0.14.4",
|
||||
"tslib": "2.3.1",
|
||||
"typescript": "4.4.4"
|
||||
"typescript": "4.4.4",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"markdown-it": "^12.3.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/main.ts
Normal file
45
src/main.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { MarkdownView, Plugin } from 'obsidian';
|
||||
|
||||
import { DEFAULT_SETTINGS, SettingsProp } from './types/index';
|
||||
import { SettingTab } from './settingTab'
|
||||
import { publishPost } from './methods/publishPost';
|
||||
|
||||
|
||||
export default class MyPlugin extends Plugin {
|
||||
settings: SettingsProp;
|
||||
|
||||
async onload() {
|
||||
// load the settings first
|
||||
await this.loadSettings();
|
||||
|
||||
// 2 ways to publish post:
|
||||
|
||||
console.log('this.settings', this.settings)
|
||||
|
||||
// 1. Click on the ghost icon on the left
|
||||
this.addRibbonIcon('ghost', 'Publish Ghost', () => {
|
||||
const view = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
|
||||
publishPost(view, this.settings)
|
||||
});
|
||||
|
||||
// 2. Run the by command + P
|
||||
this.addCommand({
|
||||
id: 'publish',
|
||||
name: 'Publish current document',
|
||||
editorCallback: (_, view: MarkdownView) => {
|
||||
publishPost(view, this.settings)
|
||||
}
|
||||
});
|
||||
|
||||
// This adds a settings tab so the user can configure various aspects of the plugin
|
||||
this.addSettingTab(new SettingTab(this.app, this));
|
||||
|
||||
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
|
||||
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
|
||||
}
|
||||
|
||||
onunload() { }
|
||||
async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()) }
|
||||
async saveSettings() { await this.saveData(this.settings) }
|
||||
}
|
||||
71
src/methods/publishPost.ts
Normal file
71
src/methods/publishPost.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { Notice, request } from "obsidian";
|
||||
|
||||
import { sign } from 'jsonwebtoken';
|
||||
const MarkdownIt = require("markdown-it")
|
||||
|
||||
const md = new MarkdownIt()
|
||||
|
||||
export const publishPost = async (view: any, settings: any) => {
|
||||
|
||||
const content = {
|
||||
title: view.file.basename,
|
||||
data: view.data
|
||||
}
|
||||
|
||||
const version = 'v4'
|
||||
|
||||
console.log('content', content)
|
||||
console.log('settings', settings)
|
||||
|
||||
// Admin API key goes here
|
||||
const key = settings.adminToken;
|
||||
|
||||
// Split the key into ID and SECRET
|
||||
const [id, secret] = key.split(':');
|
||||
|
||||
// Create the token (including decoding secret)
|
||||
const token = sign({}, Buffer.from(secret, 'hex'), {
|
||||
keyid: id,
|
||||
algorithm: 'HS256',
|
||||
expiresIn: '5m',
|
||||
audience: `/${version}/admin/`
|
||||
});
|
||||
|
||||
|
||||
const contentPost = (content: any, settings: any) => ({
|
||||
"posts": [{
|
||||
"title": content.title,
|
||||
"html": md.render(content.data),
|
||||
"status": settings.publishStatus
|
||||
}]
|
||||
})
|
||||
|
||||
const body = contentPost(content, settings);
|
||||
|
||||
|
||||
const result = await request({
|
||||
url: `${settings.url}/ghost/api/${version}/admin/posts/?source=html`,
|
||||
method: "POST",
|
||||
contentType: "application/json",
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': 'app://obsidian.md',
|
||||
'Access-Control-Allow-Methods': 'POST',
|
||||
'Content-Type': 'application/json;charset=utf-8',
|
||||
'Authorization': `Ghost ${token}`
|
||||
},
|
||||
body: JSON.stringify(body)
|
||||
})
|
||||
|
||||
|
||||
const json = JSON.parse(result)
|
||||
|
||||
if (json?.errors) {
|
||||
new Notice(`${json.errors[0].type}! ${json.errors[0].message}`)
|
||||
}
|
||||
|
||||
if (json?.posts) {
|
||||
new Notice(`${json.posts[0].settings.title} has been ${json.posts[0].settings.status} successful!`)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
51
src/settingTab/index.ts
Normal file
51
src/settingTab/index.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { App, PluginSettingTab, Setting } from 'obsidian';
|
||||
import MyPlugin from 'src/main';
|
||||
|
||||
export class SettingTab extends PluginSettingTab {
|
||||
plugin: MyPlugin;
|
||||
|
||||
constructor(app: App, plugin: MyPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
|
||||
containerEl.empty();
|
||||
containerEl.createEl('h2', { text: 'Obsidian Ghost Integration' });
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('API URL')
|
||||
.addText(text => text
|
||||
.setPlaceholder('nguyens.co')
|
||||
.setValue(this.plugin.settings.url)
|
||||
.onChange(async (value) => {
|
||||
console.log('Blog URL: ' + value);
|
||||
this.plugin.settings.url = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Admin API Key')
|
||||
.addText(text => text
|
||||
.setPlaceholder('6251555c94ca6')
|
||||
.setValue(this.plugin.settings.adminToken)
|
||||
.onChange(async (value) => {
|
||||
console.log('admin api key: ' + value);
|
||||
this.plugin.settings.adminToken = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Post publish status')
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption('draft', 'Draft')
|
||||
.addOption('published', 'Publish')
|
||||
.setValue(this.plugin.settings.publishStatus)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.publishStatus = value;
|
||||
await this.plugin.saveSettings();
|
||||
}))
|
||||
}
|
||||
}
|
||||
12
src/types/index.ts
Normal file
12
src/types/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export interface SettingsProp {
|
||||
url: string,
|
||||
adminToken: string,
|
||||
publishStatus: string
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: SettingsProp = {
|
||||
url: '',
|
||||
adminToken: '',
|
||||
publishStatus: 'draft'
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
/* Sets all the text color to red! */
|
||||
body {
|
||||
color: red;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user