mirror of
https://github.com/logseq/logseq.git
synced 2026-04-24 14:14:55 +00:00
improve(plugin): basic impls of file storage api for specific plugin context
This commit is contained in:
@@ -220,7 +220,11 @@ class LSPluginCaller extends EventEmitter {
|
||||
|
||||
this._call = async (...args: any) => {
|
||||
// parent all will get message before handshaked
|
||||
await refChild.call(LSPMSGFn(pl.id), { type: args[0], payload: args[1] || {} })
|
||||
await refChild.call(LSPMSGFn(pl.id), {
|
||||
type: args[0], payload: Object.assign(args[1] || {}, {
|
||||
$$pid: pl.id
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
this._callUserModel = async (type, payload: any) => {
|
||||
@@ -263,7 +267,9 @@ class LSPluginCaller extends EventEmitter {
|
||||
|
||||
// TODO: support sync call
|
||||
// @ts-ignore Call in same thread
|
||||
this._pluginLocal?.emit(type, payload)
|
||||
this._pluginLocal?.emit(type, Object.assign(payload, {
|
||||
$$pid: pl.id
|
||||
}))
|
||||
|
||||
return actor?.promise
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
setupInjectedUI,
|
||||
deferred,
|
||||
invokeHostExportedApi,
|
||||
isObject, withFileProtocol
|
||||
isObject, withFileProtocol, IS_DEV, getSDKPathRoot
|
||||
} from './helpers'
|
||||
import * as pluginHelpers from './helpers'
|
||||
import Debug from 'debug'
|
||||
@@ -477,7 +477,7 @@ class PluginLocal
|
||||
|
||||
if (!entry.endsWith('.js')) return
|
||||
|
||||
let sdkPath = await invokeHostExportedApi('_callApplication', 'getAppPath')
|
||||
let sdkPathRoot = await getSDKPathRoot()
|
||||
let entryPath = await invokeHostExportedApi(
|
||||
'write_user_tmp_file',
|
||||
`${this._id}_index.html`,
|
||||
@@ -486,7 +486,7 @@ class PluginLocal
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>logseq plugin entry</title>
|
||||
<script src="${sdkPath}/js/lsplugin.user.js"></script>
|
||||
<script src="${sdkPathRoot}/lsplugin.user.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import * as CSS from 'csstype'
|
||||
import { LSPluginCaller } from './LSPlugin.caller'
|
||||
import { LSPluginFileStorage } from './modules/LSPlugin.Storage'
|
||||
|
||||
export type PluginLocalIdentity = string
|
||||
|
||||
@@ -494,4 +495,6 @@ export interface ILSPluginUser extends EventEmitter<LSPluginUserEvents> {
|
||||
App: IAppProxy & Record<string, any>
|
||||
Editor: IEditorProxy & Record<string, any>
|
||||
DB: IDBProxy
|
||||
|
||||
FileStorage: LSPluginFileStorage
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import Debug from 'debug'
|
||||
import * as CSS from 'csstype'
|
||||
import { snakeCase } from 'snake-case'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
import { LSPluginFileStorage } from './modules/LSPlugin.Storage'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -154,7 +155,7 @@ const editor: Partial<IEditorProxy> = {
|
||||
return false
|
||||
}
|
||||
|
||||
const key = + '_' + this.baseInfo.id
|
||||
const key = +'_' + this.baseInfo.id
|
||||
const label = tag
|
||||
const type = 'block-context-menu-item'
|
||||
|
||||
@@ -192,6 +193,8 @@ export class LSPluginUser extends EventEmitter<LSPluginUserEvents> implements IL
|
||||
*/
|
||||
private _ui = new Map<number, uiState>()
|
||||
|
||||
private _fileStorage: LSPluginFileStorage
|
||||
|
||||
/**
|
||||
* handler of before unload plugin
|
||||
* @private
|
||||
@@ -226,6 +229,9 @@ export class LSPluginUser extends EventEmitter<LSPluginUserEvents> implements IL
|
||||
actor?.reject(e)
|
||||
}
|
||||
})
|
||||
|
||||
// modules
|
||||
this._fileStorage = new LSPluginFileStorage(this)
|
||||
}
|
||||
|
||||
async ready (
|
||||
@@ -400,6 +406,10 @@ export class LSPluginUser extends EventEmitter<LSPluginUserEvents> implements IL
|
||||
get DB (): IDBProxy {
|
||||
return this._makeUserProxy(db)
|
||||
}
|
||||
|
||||
get FileStorage (): LSPluginFileStorage {
|
||||
return this._fileStorage
|
||||
}
|
||||
}
|
||||
|
||||
export * from './LSPlugin'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { StyleString, UIOptions } from './LSPlugin'
|
||||
import { PluginLocal } from './LSPlugin.core'
|
||||
import { snakeCase } from 'snake-case'
|
||||
import * as path from 'path'
|
||||
|
||||
interface IObject {
|
||||
[key: string]: any;
|
||||
@@ -13,6 +14,31 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export const IS_DEV = process.env.NODE_ENV === 'development'
|
||||
|
||||
let _appPathRoot
|
||||
|
||||
export async function getAppPathRoot () {
|
||||
if (_appPathRoot) {
|
||||
return _appPathRoot
|
||||
}
|
||||
|
||||
return (_appPathRoot =
|
||||
await invokeHostExportedApi('_callApplication', 'getAppPath')
|
||||
)
|
||||
}
|
||||
|
||||
export async function getSDKPathRoot () {
|
||||
if (IS_DEV) {
|
||||
// TODO: cache in preference file
|
||||
return localStorage.getItem('LSP_DEV_SDK_ROOT') || 'http://localhost:8080'
|
||||
}
|
||||
|
||||
const appPathRoot = await getAppPathRoot()
|
||||
|
||||
return path.join(appPathRoot, 'js')
|
||||
}
|
||||
|
||||
export function isObject (item: any) {
|
||||
return (item === Object(item) && !Array.isArray(item))
|
||||
}
|
||||
|
||||
87
libs/src/modules/LSPlugin.Storage.ts
Normal file
87
libs/src/modules/LSPlugin.Storage.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { LSPluginUser } from '../LSPlugin.user'
|
||||
|
||||
export interface IAsyncStorage {
|
||||
getItem (key: string): Promise<string | undefined>
|
||||
|
||||
setItem (key: string, value: string): Promise<void>
|
||||
|
||||
removeItem (key: string): Promise<void>
|
||||
|
||||
hasItem (key: string): Promise<boolean>
|
||||
|
||||
clear (): Promise<void>
|
||||
}
|
||||
|
||||
/**
|
||||
* A storage based on local files under specific context
|
||||
*/
|
||||
class LSPluginFileStorage implements IAsyncStorage {
|
||||
/**
|
||||
* @param ctx
|
||||
*/
|
||||
constructor (
|
||||
private ctx: LSPluginUser
|
||||
) {}
|
||||
|
||||
/**
|
||||
* plugin id
|
||||
*/
|
||||
get ctxId () {
|
||||
return this.ctx.baseInfo.id
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key A string as file name that support nested directory
|
||||
* @param value Storage value
|
||||
*/
|
||||
setItem (key: string, value: string): Promise<void> {
|
||||
return this.ctx.caller.callAsync(`api:call`, {
|
||||
method: 'write-plugin-storage-file',
|
||||
args: [this.ctxId, key, value]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key
|
||||
*/
|
||||
getItem (key: string): Promise<string | undefined> {
|
||||
return this.ctx.caller.callAsync(`api:call`, {
|
||||
method: 'read-plugin-storage-file',
|
||||
args: [this.ctxId, key]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key
|
||||
*/
|
||||
removeItem (key: string): Promise<void> {
|
||||
return this.ctx.caller.call(`api:call`, {
|
||||
method: 'unlink-plugin-storage-file',
|
||||
args: [this.ctxId, key]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the storage
|
||||
*/
|
||||
clear (): Promise<void> {
|
||||
return this.ctx.caller.call(`api:call`, {
|
||||
method: 'clear-plugin-storage-files',
|
||||
args: [this.ctxId]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key
|
||||
*/
|
||||
hasItem (key: string): Promise<boolean> {
|
||||
return this.ctx.caller.callAsync(`api:call`, {
|
||||
method: 'exist-plugin-storage-file',
|
||||
args: [this.ctxId, key]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
LSPluginFileStorage
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
|
||||
|
||||
module.exports = {
|
||||
@@ -16,11 +17,14 @@ module.exports = {
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({
|
||||
process: 'process/browser',
|
||||
}),
|
||||
// new BundleAnalyzerPlugin()
|
||||
],
|
||||
output: {
|
||||
library: "LSPluginEntry",
|
||||
libraryTarget: "umd",
|
||||
library: 'LSPluginEntry',
|
||||
libraryTarget: 'umd',
|
||||
filename: 'lsplugin.user.js',
|
||||
path: path.resolve(__dirname, 'dist')
|
||||
},
|
||||
|
||||
@@ -18,6 +18,12 @@
|
||||
(defmethod handle :mkdir [_window [_ dir]]
|
||||
(fs/mkdirSync dir))
|
||||
|
||||
(defmethod handle :mkdir-recur [_window [_ dir]]
|
||||
(fs/mkdirSync dir #js {:recursive true}))
|
||||
|
||||
(defmethod handle :rmdir-recur [_window [_ dir]]
|
||||
(fs/rmdirSync dir #js {:recursive true}))
|
||||
|
||||
;; {encoding: 'utf8', withFileTypes: true}
|
||||
(defn- readdir
|
||||
[dir]
|
||||
|
||||
@@ -43,6 +43,10 @@
|
||||
[dir]
|
||||
(protocol/mkdir! (get-fs dir) dir))
|
||||
|
||||
(defn mkdir-recur!
|
||||
[dir]
|
||||
(protocol/mkdir-recur! (get-fs dir) dir))
|
||||
|
||||
(defn readdir
|
||||
[dir]
|
||||
(protocol/readdir (get-fs dir) dir))
|
||||
|
||||
@@ -65,12 +65,14 @@
|
||||
protocol/Fs
|
||||
(mkdir! [this dir]
|
||||
(ipc/ipc "mkdir" dir))
|
||||
(mkdir-recur! [this dir]
|
||||
(ipc/ipc "mkdir-recur" dir))
|
||||
(readdir [this dir] ; recursive
|
||||
(ipc/ipc "readdir" dir))
|
||||
(unlink! [this path _opts]
|
||||
(ipc/ipc "unlink" path))
|
||||
(rmdir! [this dir]
|
||||
nil)
|
||||
(ipc/ipc "rmdir-recur" dir))
|
||||
(read-file [this dir path _options]
|
||||
(let [path (concat-path dir path)]
|
||||
(ipc/ipc "readFile" path)))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
(defprotocol Fs
|
||||
(mkdir! [this dir])
|
||||
(mkdir-recur! [this dir])
|
||||
(readdir [this dir])
|
||||
(unlink! [this path opts])
|
||||
(rmdir! [this dir])
|
||||
|
||||
@@ -93,16 +93,84 @@
|
||||
path (util/node-path.join path "package.json")]
|
||||
(fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-mtime? true}))))
|
||||
|
||||
(defn ^:private write_dotdir_file!
|
||||
[file content sub-root]
|
||||
(p/let [repo ""
|
||||
path (plugin-handler/get-ls-dotdir-root)
|
||||
path (util/node-path.join path sub-root)
|
||||
exist? (fs/file-exists? path "")
|
||||
_ (when-not exist? (fs/mkdir-recur! path))
|
||||
user-path (util/node-path.join path file)
|
||||
sub-dir? (string/starts-with? user-path path)
|
||||
_ (if-not sub-dir? (do (log/info :debug user-path) (throw "write file denied")))
|
||||
user-path-root (util/node-path.dirname user-path)
|
||||
exist? (fs/file-exists? user-path-root "")
|
||||
_ (when-not exist? (fs/mkdir-recur! user-path-root))
|
||||
_ (fs/write-file! repo "" user-path content {:skip-mtime? true})]
|
||||
user-path))
|
||||
|
||||
(defn ^:private read_dotdir_file
|
||||
[file sub-root]
|
||||
(p/let [repo ""
|
||||
path (plugin-handler/get-ls-dotdir-root)
|
||||
path (util/node-path.join path sub-root)
|
||||
user-path (util/node-path.join path file)
|
||||
sub-dir? (string/starts-with? user-path path)
|
||||
_ (if-not sub-dir? (do (log/info :debug user-path) (throw "read file denied")))
|
||||
exist? (fs/file-exists? "" user-path)
|
||||
_ (when-not exist? (do (log/info :debug user-path) (throw "file not existed")))
|
||||
content (fs/read-file "" user-path)]
|
||||
content))
|
||||
|
||||
(defn ^:private unlink_dotdir_file!
|
||||
[file sub-root]
|
||||
(p/let [repo ""
|
||||
path (plugin-handler/get-ls-dotdir-root)
|
||||
path (util/node-path.join path sub-root)
|
||||
user-path (util/node-path.join path file)
|
||||
sub-dir? (string/starts-with? user-path path)
|
||||
_ (if-not sub-dir? (do (log/info :debug user-path) (throw "access file denied")))
|
||||
exist? (fs/file-exists? "" user-path)
|
||||
_ (when-not exist? (do (log/info :debug user-path) (throw "file not existed")))
|
||||
_ (fs/unlink! user-path {})]))
|
||||
|
||||
(def ^:export write_user_tmp_file
|
||||
(fn [file content]
|
||||
(p/let [repo ""
|
||||
path (plugin-handler/get-ls-dotdir-root)
|
||||
path (util/node-path.join path "tmp")
|
||||
exist? (fs/file-exists? path "")
|
||||
_ (when-not exist? (fs/mkdir! path))
|
||||
path (util/node-path.join path file)
|
||||
_ (fs/write-file! repo "" path content {:skip-mtime? true})]
|
||||
path)))
|
||||
(write_dotdir_file! file content "tmp")))
|
||||
|
||||
(def ^:export write_plugin_storage_file
|
||||
(fn [plugin-id file content]
|
||||
(write_dotdir_file!
|
||||
file content
|
||||
(let [plugin-id (util/node-path.basename plugin-id)]
|
||||
(util/node-path.join "storages" plugin-id)))))
|
||||
|
||||
(def ^:export read_plugin_storage_file
|
||||
(fn [plugin-id file]
|
||||
(let [plugin-id (util/node-path.basename plugin-id)]
|
||||
(read_dotdir_file
|
||||
file (util/node-path.join "storages" plugin-id)))))
|
||||
|
||||
(def ^:export unlink_plugin_storage_file
|
||||
(fn [plugin-id file]
|
||||
(let [plugin-id (util/node-path.basename plugin-id)]
|
||||
(unlink_dotdir_file!
|
||||
file (util/node-path.join "storages" plugin-id)))))
|
||||
|
||||
(def ^:export exist_plugin_storage_file
|
||||
(fn [plugin-id file]
|
||||
(p/let [root (plugin-handler/get-ls-dotdir-root)
|
||||
plugin-id (util/node-path.basename plugin-id)
|
||||
exist? (fs/file-exists?
|
||||
(util/node-path.join root "storages" plugin-id)
|
||||
file)]
|
||||
exist?)))
|
||||
|
||||
(def ^:export clear_plugin_storage_files
|
||||
(fn [plugin-id]
|
||||
(p/let [root (plugin-handler/get-ls-dotdir-root)
|
||||
plugin-id (util/node-path.basename plugin-id)]
|
||||
(fs/rmdir! (util/node-path.join root "storages" plugin-id)))))
|
||||
|
||||
(def ^:export load_user_preferences
|
||||
(fn []
|
||||
|
||||
Reference in New Issue
Block a user