recieve webmentions
This commit is contained in:
parent
90b9d53bf8
commit
9bbfcf74c8
|
@ -1033,6 +1033,7 @@ dependencies = [
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"twapi-ureq",
|
"twapi-ureq",
|
||||||
"ureq",
|
"ureq",
|
||||||
|
"url 2.1.1",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -9,27 +9,28 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
color-eyre = "0.5"
|
color-eyre = "0.5"
|
||||||
twapi-ureq = "0.1.5"
|
futures-io = "0.3"
|
||||||
|
hex = "0.4"
|
||||||
kankyo = "0.3"
|
kankyo = "0.3"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mime = "0.3.0"
|
mime = "0.3.0"
|
||||||
paseto = { version = "1.0", features = ["easy_tokens", "v2"] }
|
paseto = { version = "1.0", features = ["easy_tokens", "v2"] }
|
||||||
ring = { version = "^0.16", features = ["std"] }
|
prometheus = { version = "0.10", default-features = false, features = ["process"] }
|
||||||
rand = "0"
|
rand = "0"
|
||||||
|
ring = { version = "^0.16", features = ["std"] }
|
||||||
|
rocket = "0.4"
|
||||||
|
rocket_prometheus = "0.7.0"
|
||||||
rusty_ulid = "0.10"
|
rusty_ulid = "0.10"
|
||||||
serde_json = "^1"
|
serde_json = "^1"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
rocket = "0.4"
|
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-log = "0.1"
|
tracing-log = "0.1"
|
||||||
tracing-subscriber = "0.2"
|
tracing-subscriber = "0.2"
|
||||||
|
twapi-ureq = "0.1.5"
|
||||||
ureq = { version = "1", features = ["json", "charset"] }
|
ureq = { version = "1", features = ["json", "charset"] }
|
||||||
uuid = { version = "0.7", features = ["serde", "v4"] }
|
uuid = { version = "0.7", features = ["serde", "v4"] }
|
||||||
rocket_prometheus = "0.7.0"
|
url = "2"
|
||||||
prometheus = { version = "0.10", default-features = false, features = ["process"] }
|
|
||||||
futures-io = "0.3"
|
|
||||||
hex = "0.4"
|
|
||||||
|
|
||||||
jsonfeed = { git = "https://github.com/Xe/site" }
|
jsonfeed = { git = "https://github.com/Xe/site" }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
DROP INDEX webmentions_source_target;
|
|
@ -0,0 +1,2 @@
|
||||||
|
CREATE UNIQUE INDEX webmentions_source_target
|
||||||
|
ON webmentions(source_url, target_url);
|
|
@ -13,6 +13,7 @@ use rocket_contrib::json::Json;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
pub mod switch;
|
pub mod switch;
|
||||||
|
pub mod webmention;
|
||||||
|
|
||||||
#[get("/members")]
|
#[get("/members")]
|
||||||
#[instrument(skip(conn), err)]
|
#[instrument(skip(conn), err)]
|
||||||
|
@ -79,6 +80,12 @@ pub enum Error {
|
||||||
|
|
||||||
#[error("web API interop error: {0}")]
|
#[error("web API interop error: {0}")]
|
||||||
Web(#[from] web::Error),
|
Web(#[from] web::Error),
|
||||||
|
|
||||||
|
#[error("URL parsing error: {0}")]
|
||||||
|
URL(#[from] url::ParseError),
|
||||||
|
|
||||||
|
#[error("invalid webmention: {0}")]
|
||||||
|
InvalidWebMention(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T = ()> = std::result::Result<T, Error>;
|
pub type Result<T = ()> = std::result::Result<T, Error>;
|
||||||
|
@ -88,6 +95,7 @@ impl<'a> Responder<'a> for Error {
|
||||||
error!("{}", self);
|
error!("{}", self);
|
||||||
match self {
|
match self {
|
||||||
Error::NotFound => Err(Status::NotFound),
|
Error::NotFound => Err(Status::NotFound),
|
||||||
|
Error::InvalidWebMention(_) => Err(Status::BadRequest),
|
||||||
_ => Err(Status::InternalServerError),
|
_ => Err(Status::InternalServerError),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
use super::{Error, Result};
|
||||||
|
use crate::{models, schema, MainDatabase};
|
||||||
|
use diesel::prelude::*;
|
||||||
|
use rocket::{
|
||||||
|
request::Form,
|
||||||
|
response::{self, Responder},
|
||||||
|
Request, Response,
|
||||||
|
};
|
||||||
|
use rocket_contrib::json::Json;
|
||||||
|
use rusty_ulid::generate_ulid_string;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(FromForm, Debug)]
|
||||||
|
pub struct WebMention {
|
||||||
|
source: String,
|
||||||
|
target: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebMention {
|
||||||
|
fn check(&self) -> Result {
|
||||||
|
if self.source == self.target {
|
||||||
|
return Err(Error::InvalidWebMention("source == target".into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let u: Url = Url::parse(&self.source)?;
|
||||||
|
match u.scheme() {
|
||||||
|
"http" | "https" => {}
|
||||||
|
_ => return Err(Error::InvalidWebMention("invalid source scheme".into())),
|
||||||
|
}
|
||||||
|
|
||||||
|
u.host_str()
|
||||||
|
.ok_or(Error::InvalidWebMention("no host found in target".into()))?;
|
||||||
|
|
||||||
|
let u: Url = Url::parse(&self.target)?;
|
||||||
|
match u.scheme() {
|
||||||
|
"http" | "https" => {}
|
||||||
|
_ => return Err(Error::InvalidWebMention("invalid target scheme".into())),
|
||||||
|
}
|
||||||
|
|
||||||
|
match u
|
||||||
|
.host_str()
|
||||||
|
.ok_or(Error::InvalidWebMention("no host found in target".into()))?
|
||||||
|
{
|
||||||
|
"christine.website" | "cetacean.club" => {}
|
||||||
|
_ => return Err(Error::InvalidWebMention("invalid target host".into())),
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<models::WebMention> for WebMention {
|
||||||
|
fn into(self) -> models::WebMention {
|
||||||
|
models::WebMention {
|
||||||
|
id: generate_ulid_string(),
|
||||||
|
source_url: self.source,
|
||||||
|
target_url: self.target,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Responder<'a> for models::WebMention {
|
||||||
|
fn respond_to(self, _: &Request) -> response::Result<'a> {
|
||||||
|
Response::build()
|
||||||
|
.raw_header(
|
||||||
|
"Location",
|
||||||
|
format!("https://mi.christine.website/api/webmention/{}", self.id),
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/webmention/accept", data = "<mention>")]
|
||||||
|
#[instrument(skip(conn, mention), err)]
|
||||||
|
pub fn accept(conn: MainDatabase, mention: Form<WebMention>) -> Result<models::WebMention> {
|
||||||
|
use schema::webmentions;
|
||||||
|
|
||||||
|
let mention = mention.into_inner();
|
||||||
|
mention.check()?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
source = &mention.source[..],
|
||||||
|
target = &mention.target[..],
|
||||||
|
"webmention received"
|
||||||
|
);
|
||||||
|
|
||||||
|
let wm: models::WebMention = mention.into();
|
||||||
|
diesel::insert_into(webmentions::table)
|
||||||
|
.values(&wm)
|
||||||
|
.execute(&*conn)
|
||||||
|
.map_err(Error::Database)?;
|
||||||
|
|
||||||
|
Ok(wm)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/webmention/<mention_id>")]
|
||||||
|
#[instrument(skip(conn), err)]
|
||||||
|
pub fn get(conn: MainDatabase, mention_id: String) -> Result<Json<models::WebMention>> {
|
||||||
|
use schema::webmentions::dsl::webmentions;
|
||||||
|
|
||||||
|
Ok(Json(
|
||||||
|
webmentions
|
||||||
|
.find(mention_id)
|
||||||
|
.get_result(&*conn)
|
||||||
|
.map_err(Error::Database)?,
|
||||||
|
))
|
||||||
|
}
|
|
@ -57,6 +57,8 @@ fn main() -> Result<()> {
|
||||||
api::switch::get,
|
api::switch::get,
|
||||||
api::switch::list,
|
api::switch::list,
|
||||||
api::switch::switch,
|
api::switch::switch,
|
||||||
|
api::webmention::accept,
|
||||||
|
api::webmention::get,
|
||||||
api::get_members,
|
api::get_members,
|
||||||
api::token_info,
|
api::token_info,
|
||||||
api::tweet,
|
api::tweet,
|
||||||
|
|
|
@ -50,3 +50,11 @@ pub struct NewSwitch {
|
||||||
pub struct UpdateSwitchTime {
|
pub struct UpdateSwitchTime {
|
||||||
pub ended_at: Option<NaiveDateTime>,
|
pub ended_at: Option<NaiveDateTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Queryable, Associations, Insertable, Serialize)]
|
||||||
|
#[table_name = "webmentions"]
|
||||||
|
pub struct WebMention {
|
||||||
|
pub id: String,
|
||||||
|
pub source_url: String,
|
||||||
|
pub target_url: String,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue