:wInitial rusty commit

This commit is contained in:
Daniel Mowitz 2023-08-24 15:19:01 +02:00
parent 6342553d54
commit 84667e348b
2 changed files with 149 additions and 0 deletions

19
Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "webmention-filer"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
#webmention = { version = "0.5", features = ["receive"] }
webmention = { git = "https://github.com/DanielMowitz/webmention.git", features = [ "receive" ] }
native-tls = "0.2"
url = { version = "2.2", features = [ "serde" ] } # working with URLs
anyhow = "1" # wrapping errors
rocket = "=0.5.0-rc.3"
clap = { version = "4.3", features = [ "derive" ] } # for CLI
tokio = "1"
chrono = "0.4"
async-std = "1.12"

130
src/main.rs Normal file
View file

@ -0,0 +1,130 @@
#[macro_use] extern crate rocket;
use url::Url;
use clap::Parser;
mod storage {
use std::sync::{Arc, Mutex};
use webmention::{Webmention, storage::WebmentionStorage, error::WebmentionError};
use url::Url;
use std::fs::{OpenOptions, create_dir_all};
use std::io::prelude::*;
#[derive(Debug)]
pub struct DailyFileStorage {
storage_dir: String,
mentions: Arc<Mutex<Vec<Webmention>>>,
}
impl DailyFileStorage {
pub fn new(storage_dir: String) -> Self {
DailyFileStorage {
storage_dir,
mentions: Arc::new(Mutex::new(Vec::new())),
}
}
}
impl WebmentionStorage for DailyFileStorage {
fn store(&self, mention: Webmention) -> Result<(), WebmentionError> {
{
let now = chrono::offset::Local::now();
let daily_file_name = format!("{}/{}",
self.storage_dir,
now.date_naive());
let mention_line = format!("{}\t{} mentioned {}",
now.time(),
mention.source,
mention.target);
create_dir_all(self.storage_dir.clone()).unwrap();
let mut daily_file = OpenOptions::new()
.write(true)
.append(true)
.create(true)
.open(daily_file_name)
.unwrap();
if let Err(e) = writeln!(daily_file, "{}", mention_line) {
eprintln!("Couldn't write to file: {}", e);
}
}
Ok(())
}
fn lookup_by_target(&self, url: Url) -> Result<Vec<Webmention>, WebmentionError> {
let mut view: Vec<Webmention> = Vec::new();
let lock = self.mentions.lock().unwrap();
for mention in lock.iter() {
if mention.target.eq(&url) {
view.push(mention.clone());
}
}
Ok(view)
}
}
}
mod receive {
use rocket::{form::Form, State};
use url::Url;
use crate::storage::DailyFileStorage;
#[derive(FromForm)]
pub struct WebmentionAttempt {
source: String,
target: String,
}
#[post("/webmention", data = "<webmention>")]
pub async fn webmention_endpoint(
storage: &State<DailyFileStorage>,
config_url: &State<Url>,
webmention: Form<WebmentionAttempt>,
) -> Option<&'static str> {
let urls = (
Url::parse(&webmention.source),
Url::parse(&webmention.target),
);
if let Ok(source_url) = urls.0 {
if let Ok(target_url) = urls.1 {
if target_url.domain() != config_url.domain() {
return None;
}
println!("Calling store with source {} and target {}", &source_url, &target_url);
match webmention::receive_webmention(
&**storage,
&source_url,
&target_url,
).await {
Ok(true) => return Some("OK"),
Ok(false) => {println!("Webmention check did not succeed!"); return None},
Err(e) => {println!("{:?}", e); return None},
}
}
}
return None
}
}
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
#[arg(short, long)]
domain: String,
#[arg(short, long, default_value_t = String::from("/tmp/webmention-storage"))]
storage_dir: String,
}
#[launch]
fn rocket() -> _ {
let args = Args::parse();
rocket::build()
.manage(crate::storage::DailyFileStorage::new(args.storage_dir))
.manage(Url::parse(&args.domain).unwrap())
.mount("/", routes![receive::webmention_endpoint])
}