mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-22 12:21:18 +00:00
allow loading multiple plugins
This commit is contained in:
parent
2ef6e2e43b
commit
28da169259
3 changed files with 72 additions and 67 deletions
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
check_apub_id_valid_with_strictness,
|
check_apub_id_valid_with_strictness,
|
||||||
local_site_data_cached,
|
local_site_data_cached,
|
||||||
objects::{read_from_string_or_source_opt, verify_is_remote_object},
|
objects::{read_from_string_or_source_opt, verify_is_remote_object},
|
||||||
plugins::{call_plugin, load_plugins},
|
plugins::Plugins,
|
||||||
protocol::{
|
protocol::{
|
||||||
objects::{
|
objects::{
|
||||||
page::{Attachment, AttributedTo, Hashtag, HashtagType, Page, PageType},
|
page::{Attachment, AttributedTo, Hashtag, HashtagType, Page, PageType},
|
||||||
|
@ -51,7 +51,6 @@ use lemmy_utils::{
|
||||||
};
|
};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use stringreader::StringReader;
|
use stringreader::StringReader;
|
||||||
use tracing::info;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
const MAX_TITLE_LENGTH: usize = 200;
|
const MAX_TITLE_LENGTH: usize = 200;
|
||||||
|
@ -277,25 +276,13 @@ impl Object for ApubPost {
|
||||||
.build()
|
.build()
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: move this all into helper function
|
let mut plugins = Plugins::load()?;
|
||||||
let before_plugin_hook = "federation_before_receive_post";
|
plugins.call("federation_before_receive_post", &mut form)?;
|
||||||
info!("Calling plugin hook {}", &before_plugin_hook);
|
|
||||||
if let Some(mut plugins) = load_plugins()? {
|
|
||||||
if plugins.function_exists(&before_plugin_hook) {
|
|
||||||
call_plugin(plugins, &before_plugin_hook, &mut form)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let timestamp = page.updated.or(page.published).unwrap_or_else(naive_now);
|
let timestamp = page.updated.or(page.published).unwrap_or_else(naive_now);
|
||||||
let mut post = Post::insert_apub(&mut context.pool(), timestamp, &form).await?;
|
let mut post = Post::insert_apub(&mut context.pool(), timestamp, &form).await?;
|
||||||
|
|
||||||
let after_plugin_hook = "federation_after_receive_post";
|
plugins.call("federation_after_receive_post", &mut post)?;
|
||||||
info!("Calling plugin hook {}", &after_plugin_hook);
|
|
||||||
if let Some(mut plugins) = load_plugins()? {
|
|
||||||
if plugins.function_exists(&after_plugin_hook) {
|
|
||||||
call_plugin(plugins, &after_plugin_hook, &mut post)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generate_post_link_metadata(
|
generate_post_link_metadata(
|
||||||
post.clone(),
|
post.clone(),
|
||||||
|
|
|
@ -2,38 +2,59 @@ use extism::{Manifest, Plugin};
|
||||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{ffi::OsStr, fs::read_dir};
|
use std::{ffi::OsStr, fs::read_dir};
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
pub fn load_plugins() -> LemmyResult<Option<Plugin>> {
|
pub struct Plugins {
|
||||||
// TODO: make dir configurable via env var
|
plugins: Vec<Plugin>,
|
||||||
// TODO: should only read fs once at startup for performance
|
}
|
||||||
let plugin_paths = read_dir("plugins")?;
|
|
||||||
|
|
||||||
let mut wasm_files = vec![];
|
impl Plugins {
|
||||||
for path in plugin_paths {
|
pub fn load() -> LemmyResult<Self> {
|
||||||
let path = path?.path();
|
// TODO: make dir configurable via env var
|
||||||
if path.extension() == Some(OsStr::new("wasm")) {
|
// TODO: should only read fs once at startup for performance
|
||||||
wasm_files.push(path);
|
let plugin_paths = read_dir("plugins")?;
|
||||||
|
|
||||||
|
let mut wasm_files = vec![];
|
||||||
|
for path in plugin_paths {
|
||||||
|
let path = path?.path();
|
||||||
|
if path.extension() == Some(OsStr::new("wasm")) {
|
||||||
|
wasm_files.push(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
let plugins = wasm_files
|
||||||
|
.into_iter()
|
||||||
|
.map(|w| {
|
||||||
|
let manifest = Manifest::new(vec![w]);
|
||||||
|
Plugin::new(manifest, [], true).unwrap()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Ok(Self { plugins })
|
||||||
}
|
}
|
||||||
if !wasm_files.is_empty() {
|
|
||||||
// TODO: what if theres more than one plugin for the same hook?
|
|
||||||
let manifest = Manifest::new(wasm_files);
|
|
||||||
let plugin = Plugin::new(manifest, [], true)?;
|
|
||||||
Ok(Some(plugin))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call_plugin<T: Serialize + for<'de> Deserialize<'de> + Clone>(
|
pub fn exists(&mut self, name: &str) -> bool {
|
||||||
mut plugins: Plugin,
|
for p in &mut self.plugins {
|
||||||
name: &str,
|
if p.function_exists(name) {
|
||||||
data: &mut T,
|
return true;
|
||||||
) -> LemmyResult<()> {
|
}
|
||||||
*data = plugins
|
}
|
||||||
.call::<extism_convert::Json<T>, extism_convert::Json<T>>(name, (*data).clone().into())
|
false
|
||||||
.map_err(|e| LemmyErrorType::PluginError(e.to_string()))?
|
}
|
||||||
.0
|
|
||||||
.into();
|
pub fn call<T: Serialize + for<'de> Deserialize<'de> + Clone>(
|
||||||
Ok(())
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
data: &mut T,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
info!("Calling plugin hook {name}");
|
||||||
|
for p in &mut self.plugins {
|
||||||
|
if p.function_exists(name) {
|
||||||
|
*data = p
|
||||||
|
.call::<extism_convert::Json<T>, extism_convert::Json<T>>(name, (*data).clone().into())
|
||||||
|
.map_err(|e| LemmyErrorType::PluginError(e.to_string()))?
|
||||||
|
.0
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use actix_web::{
|
||||||
};
|
};
|
||||||
use core::future::Ready;
|
use core::future::Ready;
|
||||||
use futures_util::future::LocalBoxFuture;
|
use futures_util::future::LocalBoxFuture;
|
||||||
use lemmy_apub::plugins::{call_plugin, load_plugins};
|
use lemmy_apub::plugins::Plugins;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::{future::ready, rc::Rc};
|
use std::{future::ready, rc::Rc};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
@ -61,33 +61,30 @@ where
|
||||||
let path = service_req.path().replace("/api/v3/", "").replace("/", "_");
|
let path = service_req.path().replace("/api/v3/", "").replace("/", "_");
|
||||||
// TODO: naming can be a bit silly, `POST /api/v3/post` becomes `api_before_post_post`
|
// TODO: naming can be a bit silly, `POST /api/v3/post` becomes `api_before_post_post`
|
||||||
let before_plugin_hook = format!("api_before_{method}_{path}").to_lowercase();
|
let before_plugin_hook = format!("api_before_{method}_{path}").to_lowercase();
|
||||||
|
let mut plugins = Plugins::load()?;
|
||||||
|
|
||||||
info!("Calling plugin hook {}", &before_plugin_hook);
|
info!("Calling plugin hook {}", &before_plugin_hook);
|
||||||
if let Some(mut plugins) = load_plugins()? {
|
if plugins.exists(&before_plugin_hook) {
|
||||||
if plugins.function_exists(&before_plugin_hook) {
|
let payload = service_req.extract::<Bytes>().await?;
|
||||||
let payload = service_req.extract::<Bytes>().await?;
|
let mut json: Value = serde_json::from_slice(&payload.to_vec()).unwrap_or(Value::Null);
|
||||||
|
plugins.call(&before_plugin_hook, &mut json)?;
|
||||||
let mut json: Value = serde_json::from_slice(&payload.to_vec())?;
|
let (_, mut new_payload) = Payload::create(true);
|
||||||
call_plugin(plugins, &before_plugin_hook, &mut json)?;
|
new_payload.unread_data(Bytes::from(serde_json::to_vec(&json)?));
|
||||||
|
service_req.set_payload(new_payload.into());
|
||||||
let (_, mut new_payload) = Payload::create(true);
|
|
||||||
new_payload.unread_data(Bytes::from(serde_json::to_vec(&json)?));
|
|
||||||
service_req.set_payload(new_payload.into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut res = svc.call(service_req).await?;
|
let mut res = svc.call(service_req).await?;
|
||||||
|
|
||||||
let after_plugin_hook = format!("api_after_{method}_{path}").to_lowercase();
|
let after_plugin_hook = format!("api_after_{method}_{path}").to_lowercase();
|
||||||
|
|
||||||
info!("Calling plugin hook {}", &after_plugin_hook);
|
info!("Calling plugin hook {}", &after_plugin_hook);
|
||||||
if let Some(mut plugins) = load_plugins()? {
|
if plugins.exists(&after_plugin_hook) {
|
||||||
if plugins.function_exists(&before_plugin_hook) {
|
res = res.map_body(|_, body| {
|
||||||
res = res.map_body(|_, body| {
|
let mut json: Value =
|
||||||
let mut json: Value =
|
serde_json::from_slice(&body.try_into_bytes().unwrap().to_vec()).unwrap();
|
||||||
serde_json::from_slice(&body.try_into_bytes().unwrap().to_vec()).unwrap();
|
plugins.call(&after_plugin_hook, &mut json).unwrap();
|
||||||
call_plugin(plugins, &after_plugin_hook, &mut json).unwrap();
|
BoxBody::new(Bytes::from(serde_json::to_vec(&json).unwrap()))
|
||||||
BoxBody::new(Bytes::from(serde_json::to_vec(&json).unwrap()))
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
|
|
Loading…
Reference in a new issue