Basic Plugin Support - Syntax Highlighter Added - WIP
This commit is contained in:
parent
1c873d23b5
commit
a6a56a11c7
8 changed files with 367 additions and 7 deletions
|
@ -20,4 +20,5 @@ sanitize-filename = "0.3.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
actix-web-httpauth = "0.6.0"
|
actix-web-httpauth = "0.6.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
rutie = "0.8.4"
|
|
@ -22,5 +22,10 @@ COPY --from=builder /usr/src/microbin/target/release/microbin /usr/local/bin/mic
|
||||||
# copy /static folder containing the stylesheets
|
# copy /static folder containing the stylesheets
|
||||||
COPY --from=builder /usr/src/microbin/static /usr/local/bin/static
|
COPY --from=builder /usr/src/microbin/static /usr/local/bin/static
|
||||||
|
|
||||||
|
# Install Ruby (no need if you're disabling all plugins)
|
||||||
|
RUN \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y ruby
|
||||||
|
|
||||||
# run the binary
|
# run the binary
|
||||||
CMD ["microbin"]
|
CMD ["microbin"]
|
78
plugins/BasicSyntaxHighlighter.rb
Normal file
78
plugins/BasicSyntaxHighlighter.rb
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
module MBP
|
||||||
|
module BasicSyntaxHighlighter
|
||||||
|
|
||||||
|
# Plugin Properties
|
||||||
|
|
||||||
|
def self.get_id()
|
||||||
|
"BasicSyntaxHighlighter"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_name()
|
||||||
|
"Basic Syntax Highlighter Plugin"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_version()
|
||||||
|
"1.0.0"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_author()
|
||||||
|
"Daniel Szabo"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_webpage()
|
||||||
|
"https://github.com/szabodanika/microbin"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_description()
|
||||||
|
"This plugin will simply color keywords and special characters in four different colors based on some very basic RegEx - it is meant to univesally make code pastas more readable but is not a robust syntax highlighter solution."
|
||||||
|
end
|
||||||
|
|
||||||
|
# Plugin Event Hooks
|
||||||
|
|
||||||
|
def self.init()
|
||||||
|
# Ignore event
|
||||||
|
"OK"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.on_pasta_created(content)
|
||||||
|
# We do not modify stored content
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.on_pasta_read(content)
|
||||||
|
|
||||||
|
tokens = {
|
||||||
|
|
||||||
|
"orchid" => [/([0-9])/, /([t|T][r|R][u|U][e|E]|[f|F][a|A][l|L][s|S][e|E])/],
|
||||||
|
|
||||||
|
"palevioletred" => ['(', ')', '{', '}', '[', ']'],
|
||||||
|
|
||||||
|
"royalblue" => [/(\s(for|while|do|select|async|await|mut|break|continue|in|as|switch|let|fn|async|if|else|elseif|new|switch|match|case|default|public|protected|private|return|class|interface|static|final|const|var|int|integer|boolean|float|double|module|def|end|void))(?![a-z])/],
|
||||||
|
|
||||||
|
"mediumorchid" => [/(:|\.|;|=|>|<|\?|!|#|%|@|\^|&|\*|\|)/],
|
||||||
|
|
||||||
|
"mediumseagreen" => [/(\".*\")/, /(\'.*\')/]
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.each { | color, tokens |
|
||||||
|
for token in tokens do
|
||||||
|
if(token.class == String)
|
||||||
|
content.gsub!(token, "$$#{color}$$" + token + "$$/#{color}$$")
|
||||||
|
elsif
|
||||||
|
content.gsub!(token, "$$#{color}$$" + '\1' + "$$/#{color}$$")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.each { | color, tokens |
|
||||||
|
content.gsub!("$$#{color}$$", "<span style='color:#{color}'>");
|
||||||
|
content.gsub!("$$/#{color}$$", "</span>");
|
||||||
|
};
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
78
plugins/MBPlugin.rb
Normal file
78
plugins/MBPlugin.rb
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
require 'rutie'
|
||||||
|
|
||||||
|
module MB
|
||||||
|
|
||||||
|
class MBPlugin
|
||||||
|
# Plugin Properties
|
||||||
|
|
||||||
|
def self.get_id()
|
||||||
|
"[Enter Plugin ID]"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_name()
|
||||||
|
"[Eenter Plugin Name]"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_version()
|
||||||
|
"1.0.0"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_author()
|
||||||
|
"[Enter Author name]"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_webpage()
|
||||||
|
"[Enter Web URL]"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_description()
|
||||||
|
"[Enter Description]"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Plugin Event Hooks
|
||||||
|
|
||||||
|
def self.init()
|
||||||
|
raise "Operation not supported";
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.on_pasta_deleted(id, content, created, expiration, file)
|
||||||
|
raise "Operation not supported";
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.on_pasta_expired(id, content, created, expiration, file)
|
||||||
|
raise "Operation not supported";
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.on_pasta_created(id, content, created, expiration, file)
|
||||||
|
raise "Operation not supported";
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.on_pasta_read(id, content, created, expiration, file)
|
||||||
|
raise "Operation not supported";
|
||||||
|
end
|
||||||
|
|
||||||
|
# Rust Function Calls
|
||||||
|
|
||||||
|
def self.init()
|
||||||
|
raise "Operation not supported";
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.P=on_pasta_deleted(id, content, created, expiration, file)
|
||||||
|
raise "Operation not supported";
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.on_pasta_expired(id, content, created, expiration, file)
|
||||||
|
raise "Operation not supported";
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.on_pasta_created(id, content, created, expiration, file)
|
||||||
|
raise "Operation not supported";
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.on_pasta_read(id, content, created, expiration, file)
|
||||||
|
raise "Operation not supported";
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
42
plugins/helloWorld.rb
Normal file
42
plugins/helloWorld.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
module MBP
|
||||||
|
class HelloWorld < MBPlugin
|
||||||
|
|
||||||
|
def self.get_id()
|
||||||
|
"HelloWorld"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_name()
|
||||||
|
"Hello World Plugin"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_version()
|
||||||
|
"1.0.0"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_description()
|
||||||
|
"This is just a demo plugin. It does not do anything."
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_author()
|
||||||
|
"Daniel Szabo"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_webpage()
|
||||||
|
"https://github.com/szabodanika/microbin"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.init()
|
||||||
|
# Ignore event
|
||||||
|
"OK"
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.on_pasta_created(content)
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.on_pasta_read(content)
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
|
@ -24,7 +24,7 @@ pub fn to_animal_names(mut number: u64) -> String {
|
||||||
number -= digit * ANIMAL_NAMES.len().pow(power) as u64;
|
number -= digit * ANIMAL_NAMES.len().pow(power) as u64;
|
||||||
if power > 0 {
|
if power > 0 {
|
||||||
power -= 1;
|
power -= 1;
|
||||||
} else if power <= 0 || number == 0 {
|
} else if power == 0 || number == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
32
src/main.rs
32
src/main.rs
|
@ -29,6 +29,7 @@ use crate::pasta::Pasta;
|
||||||
mod animalnumbers;
|
mod animalnumbers;
|
||||||
mod dbio;
|
mod dbio;
|
||||||
mod pasta;
|
mod pasta;
|
||||||
|
mod plugins;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref ARGS: Args = Args::parse();
|
static ref ARGS: Args = Args::parse();
|
||||||
|
@ -47,6 +48,12 @@ struct Args {
|
||||||
#[clap(short, long, default_value_t = 1)]
|
#[clap(short, long, default_value_t = 1)]
|
||||||
threads: u8,
|
threads: u8,
|
||||||
|
|
||||||
|
#[clap(short, long)]
|
||||||
|
wide: bool,
|
||||||
|
|
||||||
|
#[clap(short, long, default_value_t = 3)]
|
||||||
|
animals: u8,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
hide_header: bool,
|
hide_header: bool,
|
||||||
|
|
||||||
|
@ -103,7 +110,7 @@ struct ErrorTemplate<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "pasta.html")]
|
#[template(path = "pasta.html", escape = "none")]
|
||||||
struct PastaTemplate<'a> {
|
struct PastaTemplate<'a> {
|
||||||
pasta: &'a Pasta,
|
pasta: &'a Pasta,
|
||||||
args: &'a Args,
|
args: &'a Args,
|
||||||
|
@ -165,7 +172,8 @@ async fn create(data: web::Data<AppState>, mut payload: Multipart) -> Result<Htt
|
||||||
}
|
}
|
||||||
"content" => {
|
"content" => {
|
||||||
while let Some(chunk) = field.try_next().await? {
|
while let Some(chunk) = field.try_next().await? {
|
||||||
new_pasta.content = std::str::from_utf8(&chunk).unwrap().to_string();
|
new_pasta.content =
|
||||||
|
plugins::on_pasta_created(std::str::from_utf8(&chunk).unwrap());
|
||||||
new_pasta.pasta_type = if is_valid_url(new_pasta.content.as_str()) {
|
new_pasta.pasta_type = if is_valid_url(new_pasta.content.as_str()) {
|
||||||
String::from("url")
|
String::from("url")
|
||||||
} else {
|
} else {
|
||||||
|
@ -223,9 +231,23 @@ async fn getpasta(data: web::Data<AppState>, id: web::Path<String>) -> HttpRespo
|
||||||
|
|
||||||
for pasta in pastas.iter() {
|
for pasta in pastas.iter() {
|
||||||
if pasta.id == id {
|
if pasta.id == id {
|
||||||
return HttpResponse::Found()
|
let pasta_copy = Pasta {
|
||||||
.content_type("text/html")
|
id: pasta.id,
|
||||||
.body(PastaTemplate { pasta, args: &ARGS }.render().unwrap());
|
content: plugins::on_pasta_read(&pasta.content),
|
||||||
|
file: pasta.file.to_string(),
|
||||||
|
created: pasta.created,
|
||||||
|
pasta_type: pasta.pasta_type.to_string(),
|
||||||
|
expiration: pasta.expiration,
|
||||||
|
};
|
||||||
|
|
||||||
|
return HttpResponse::Found().content_type("text/html").body(
|
||||||
|
PastaTemplate {
|
||||||
|
pasta: &pasta_copy,
|
||||||
|
args: &ARGS,
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
134
src/plugins.rs
Normal file
134
src/plugins.rs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
extern crate rutie;
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use log::{error, log};
|
||||||
|
use rutie::{AnyException, AnyObject, Object, RString, VM};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{BufReader, Read};
|
||||||
|
use std::{fs, io};
|
||||||
|
|
||||||
|
const CACHE_PLUGINS: bool = false;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref PLUGIN_IDENTIFIERS: Vec<String> = init();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init() -> Vec<String> {
|
||||||
|
VM::init();
|
||||||
|
|
||||||
|
let plugin_paths = load_plugin_paths();
|
||||||
|
|
||||||
|
let plugin_codes = read_plugins(plugin_paths.clone());
|
||||||
|
|
||||||
|
feed_plugins(plugin_codes);
|
||||||
|
|
||||||
|
let identifiers = get_plugin_identifiers(plugin_paths);
|
||||||
|
|
||||||
|
init_plugins(&identifiers);
|
||||||
|
|
||||||
|
identifiers
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pasta_filter(s: &str) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_pasta_read(s: &str) -> String {
|
||||||
|
let mut processed_content: String = String::from(s);
|
||||||
|
|
||||||
|
for PLUGIN_IDENTIFIER in PLUGIN_IDENTIFIERS.iter() {
|
||||||
|
processed_content = eval_for_string(PLUGIN_IDENTIFIER, "on_pasta_read", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
processed_content
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_pasta_created(s: &str) -> String {
|
||||||
|
let mut processed_content: String = String::from(s);
|
||||||
|
|
||||||
|
for PLUGIN_IDENTIFIER in PLUGIN_IDENTIFIERS.iter() {
|
||||||
|
processed_content = eval_for_string(PLUGIN_IDENTIFIER, "on_pasta_created", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
processed_content
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_plugins(plugin_identifiers: &Vec<String>) {
|
||||||
|
for PLUGIN_IDENTIFIER in plugin_identifiers.iter() {
|
||||||
|
eval_for_string(PLUGIN_IDENTIFIER, "init", "");
|
||||||
|
|
||||||
|
let init_result = eval_for_string(&PLUGIN_IDENTIFIER, "init", "");
|
||||||
|
let id = eval_for_string(&PLUGIN_IDENTIFIER, "get_id", "");
|
||||||
|
let name = eval_for_string(&id, "get_name", "");
|
||||||
|
let version = eval_for_string(&id, "get_version", "");
|
||||||
|
|
||||||
|
log::info!("Initialised plugin {id} - {name} ({version})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_for_string(plugin_id: &str, function: &str, parameter: &str) -> String {
|
||||||
|
match VM::eval(&*format!("MBP::{}::{}({})", plugin_id, function, parameter)) {
|
||||||
|
Ok(result) => match result.try_convert_to::<RString>() {
|
||||||
|
Ok(ruby_string) => ruby_string.to_string(),
|
||||||
|
Err(err) => err.to_string(),
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
log::error!(
|
||||||
|
"Failed to run function '{}' on plugin {}: {}",
|
||||||
|
function,
|
||||||
|
plugin_id,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
err.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_plugin_paths() -> Vec<String> {
|
||||||
|
let paths = fs::read_dir("./plugins").expect("Failed to access ./plugins library.");
|
||||||
|
|
||||||
|
let mut plugin_paths: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
for path in paths {
|
||||||
|
plugin_paths.push(path.unwrap().path().to_str().unwrap().parse().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin_paths
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_plugins(plugin_paths: Vec<String>) -> Vec<String> {
|
||||||
|
let mut plugin_codes: Vec<String> = Vec::new();
|
||||||
|
|
||||||
|
for plugin_path in plugin_paths {
|
||||||
|
let plugin_code = match fs::read_to_string(&plugin_path) {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("Failed to read plugin file {}: {}", plugin_path, err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
plugin_codes.push(plugin_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin_codes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn feed_plugins(plugin_codes: Vec<String>) {
|
||||||
|
for plugin_code in plugin_codes {
|
||||||
|
match VM::eval(plugin_code.as_str()) {
|
||||||
|
Ok(result) => {}
|
||||||
|
Err(error) => {
|
||||||
|
log::error!("Failed to initialise plugin: {}", error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_plugin_identifiers(plugin_paths: Vec<String>) -> Vec<String> {
|
||||||
|
let mut plugin_ids: Vec<String> = Vec::new();
|
||||||
|
for plugin_path in plugin_paths {
|
||||||
|
plugin_ids.push(plugin_path.replace("./plugins/", "").replace(".rb", ""))
|
||||||
|
}
|
||||||
|
plugin_ids
|
||||||
|
}
|
Loading…
Reference in a new issue