mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-30 00:01:25 +00:00
Proof of concept for Plugin system (fixes #3562)
This commit is contained in:
parent
7540b02723
commit
a65ccaf665
12 changed files with 4439 additions and 2295 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -34,3 +34,6 @@ dev_pgdata/
|
||||||
|
|
||||||
# database dumps
|
# database dumps
|
||||||
*.sqldump
|
*.sqldump
|
||||||
|
|
||||||
|
# compiled example plugin
|
||||||
|
example_plugin/plugin.wasm
|
||||||
|
|
1354
Cargo.lock
generated
1354
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -49,8 +49,6 @@ else
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$PWD"
|
|
||||||
|
|
||||||
LOG_DIR=target/log
|
LOG_DIR=target/log
|
||||||
mkdir -p $LOG_DIR
|
mkdir -p $LOG_DIR
|
||||||
|
|
||||||
|
@ -75,6 +73,11 @@ LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_delta.hjson \
|
||||||
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_delta" \
|
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_delta" \
|
||||||
target/lemmy_server >$LOG_DIR/lemmy_delta.out 2>&1 &
|
target/lemmy_server >$LOG_DIR/lemmy_delta.out 2>&1 &
|
||||||
|
|
||||||
|
# plugin setup
|
||||||
|
pushd example_plugin
|
||||||
|
tinygo build -o plugin.wasm -target wasi main.go
|
||||||
|
popd
|
||||||
|
|
||||||
echo "start epsilon"
|
echo "start epsilon"
|
||||||
# An instance who has a blocklist, with lemmy-alpha blocked
|
# An instance who has a blocklist, with lemmy-alpha blocked
|
||||||
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_epsilon.hjson \
|
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_epsilon.hjson \
|
||||||
|
|
|
@ -11,7 +11,7 @@ killall -s1 lemmy_server || true
|
||||||
popd
|
popd
|
||||||
|
|
||||||
pnpm i
|
pnpm i
|
||||||
pnpm api-test || true
|
pnpm api-test-post || true
|
||||||
|
|
||||||
killall -s1 lemmy_server || true
|
killall -s1 lemmy_server || true
|
||||||
killall -s1 pict-rs || true
|
killall -s1 pict-rs || true
|
||||||
|
|
|
@ -763,3 +763,14 @@ test("Fetch post with redirect", async () => {
|
||||||
let gammaPost2 = await gamma.resolveObject(form);
|
let gammaPost2 = await gamma.resolveObject(form);
|
||||||
expect(gammaPost2.post).toBeDefined();
|
expect(gammaPost2.post).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Create a post", async () => {
|
||||||
|
let community = await createCommunity(epsilon);
|
||||||
|
let postRes = createPost(
|
||||||
|
epsilon,
|
||||||
|
community.community_view.community.id,
|
||||||
|
"https://example.com/",
|
||||||
|
"plugin should block this",
|
||||||
|
);
|
||||||
|
await expect(postRes).rejects.toStrictEqual(Error("plugin_error"));
|
||||||
|
});
|
||||||
|
|
|
@ -28,8 +28,11 @@ uuid = { workspace = true }
|
||||||
moka.workspace = true
|
moka.workspace = true
|
||||||
once_cell.workspace = true
|
once_cell.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
webmention = "0.5.0"
|
webmention = "0.5.0"
|
||||||
accept-language = "3.1.0"
|
accept-language = "3.1.0"
|
||||||
|
extism = "1.2.0"
|
||||||
|
extism-convert = "1.2.0"
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
[package.metadata.cargo-machete]
|
||||||
ignored = ["futures"]
|
ignored = ["futures"]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Json;
|
use actix_web::web::Json;
|
||||||
|
use extism::*;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_post_response,
|
build_response::build_post_response,
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
@ -46,6 +47,8 @@ use lemmy_utils::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::{ffi::OsStr, fs::read_dir};
|
||||||
use tracing::Instrument;
|
use tracing::Instrument;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use webmention::{Webmention, WebmentionError};
|
use webmention::{Webmention, WebmentionError};
|
||||||
|
@ -123,6 +126,8 @@ pub async fn create_post(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
plugin_hook("api_create_post", data.clone())?;
|
||||||
|
|
||||||
let post_form = PostInsertForm::builder()
|
let post_form = PostInsertForm::builder()
|
||||||
.name(data.name.trim().to_string())
|
.name(data.name.trim().to_string())
|
||||||
.url(url)
|
.url(url)
|
||||||
|
@ -202,3 +207,31 @@ pub async fn create_post(
|
||||||
|
|
||||||
build_post_response(&context, community_id, &local_user_view.person, post_id).await
|
build_post_response(&context, community_id, &local_user_view.person, post_id).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_plugins() -> LemmyResult<Plugin> {
|
||||||
|
// TODO: make dir configurable via env var
|
||||||
|
let plugin_paths = read_dir("example_plugin")?;
|
||||||
|
|
||||||
|
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 manifest = Manifest::new(wasm_files);
|
||||||
|
let plugin = Plugin::new(&manifest, [], true)?;
|
||||||
|
Ok(plugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn plugin_hook<T: Serialize>(name: &'static str, data: T) -> LemmyResult<()> {
|
||||||
|
let mut plugin = load_plugins()?;
|
||||||
|
if plugin.function_exists(name) {
|
||||||
|
let res = plugin
|
||||||
|
.call::<extism_convert::Json<T>, &str>(name, data.into())
|
||||||
|
.map_err(|e| LemmyErrorType::PluginError(e.to_string()));
|
||||||
|
dbg!(&res);
|
||||||
|
println!("{}", res?);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -177,6 +177,7 @@ pub enum LemmyErrorType {
|
||||||
InvalidBotAction,
|
InvalidBotAction,
|
||||||
CantBlockLocalInstance,
|
CantBlockLocalInstance,
|
||||||
UrlWithoutDomain,
|
UrlWithoutDomain,
|
||||||
|
PluginError(String),
|
||||||
Unknown(String),
|
Unknown(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
5
example_plugin/go.mod
Normal file
5
example_plugin/go.mod
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module example_plugin
|
||||||
|
|
||||||
|
go 1.22.0
|
||||||
|
|
||||||
|
require github.com/extism/go-pdk v1.0.2 // indirect
|
2
example_plugin/go.sum
Normal file
2
example_plugin/go.sum
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
github.com/extism/go-pdk v1.0.2 h1:UB7oTW3tw2zoMlsUdBEDAAbhQg9OudzgNeyCwQYZ730=
|
||||||
|
github.com/extism/go-pdk v1.0.2/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4=
|
32
example_plugin/main.go
Normal file
32
example_plugin/main.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/extism/go-pdk"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreatePost struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
// skipping other fields for now
|
||||||
|
}
|
||||||
|
|
||||||
|
//export api_create_post
|
||||||
|
func api_create_post() int32 {
|
||||||
|
params := CreatePost{}
|
||||||
|
// use json input helper, which automatically unmarshals the plugin input into your struct
|
||||||
|
err := pdk.InputJSON(¶ms)
|
||||||
|
if err != nil {
|
||||||
|
pdk.SetError(err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if params.Body == "plugin should block this" {
|
||||||
|
pdk.SetError(errors.New("blocked by plugin"))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
greeting := `Created post "` + params.Name + `"!`
|
||||||
|
pdk.OutputString(greeting)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {}
|
Loading…
Reference in a new issue