chore: updated apb things, restructured a bit
This commit is contained in:
parent
a9229adec8
commit
86e84d88aa
15 changed files with 237 additions and 288 deletions
|
@ -99,7 +99,13 @@ async fn main() {
|
|||
let ctx = server::Context::new(db, args.domain)
|
||||
.await.expect("failed creating server context");
|
||||
|
||||
let router = routes::activitypub::router().with_state(ctx);
|
||||
use routes::activitypub::ActivityPubRouter;
|
||||
use routes::mastodon::MastodonRouter;
|
||||
|
||||
let router = axum::Router::new()
|
||||
.ap_routes()
|
||||
.mastodon_routes() // no-op if mastodon feature is disabled
|
||||
.with_state(ctx);
|
||||
|
||||
// run our app with hyper, listening locally on port 3000
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
|
||||
|
@ -113,7 +119,6 @@ async fn main() {
|
|||
}
|
||||
|
||||
|
||||
|
||||
async fn fetch(db: &sea_orm::DatabaseConnection, uri: &str, save: bool) -> reqwest::Result<()> {
|
||||
use apb::{Base, Object};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{routes::activitypub::PUBLIC_TARGET, model::{config, credential}};
|
||||
use crate::model::{config, credential};
|
||||
use super::{activity, object, user, Audience};
|
||||
use openssl::rsa::Rsa;
|
||||
use sea_orm::IntoActiveModel;
|
||||
|
@ -10,7 +10,7 @@ pub async fn faker(db: &sea_orm::DatabaseConnection, domain: String, count: u64)
|
|||
let test_user = super::user::Model {
|
||||
id: format!("{domain}/users/test"),
|
||||
name: Some("μpub".into()),
|
||||
domain: crate::routes::activitypub::domain(&domain),
|
||||
domain: clean_domain(&domain),
|
||||
preferred_username: "test".to_string(),
|
||||
summary: Some("hello world! i'm manually generated but served dynamically from db! check progress at https://git.alemi.dev/upub.git".to_string()),
|
||||
following: None,
|
||||
|
@ -64,7 +64,7 @@ pub async fn faker(db: &sea_orm::DatabaseConnection, domain: String, count: u64)
|
|||
comments: Set(0),
|
||||
likes: Set(0),
|
||||
shares: Set(0),
|
||||
to: Set(Audience(vec![PUBLIC_TARGET.to_string()])),
|
||||
to: Set(Audience(vec![apb::target::PUBLIC.to_string()])),
|
||||
bto: Set(Audience::default()),
|
||||
cc: Set(Audience(vec![])),
|
||||
bcc: Set(Audience::default()),
|
||||
|
@ -77,7 +77,7 @@ pub async fn faker(db: &sea_orm::DatabaseConnection, domain: String, count: u64)
|
|||
object: Set(Some(format!("{domain}/objects/{oid}"))),
|
||||
target: Set(None),
|
||||
published: Set(chrono::Utc::now() - std::time::Duration::from_secs(60*i)),
|
||||
to: Set(Audience(vec![PUBLIC_TARGET.to_string()])),
|
||||
to: Set(Audience(vec![apb::target::PUBLIC.to_string()])),
|
||||
bto: Set(Audience::default()),
|
||||
cc: Set(Audience(vec![])),
|
||||
bcc: Set(Audience::default()),
|
||||
|
@ -86,3 +86,10 @@ pub async fn faker(db: &sea_orm::DatabaseConnection, domain: String, count: u64)
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clean_domain(domain: &str) -> String {
|
||||
domain
|
||||
.replace("http://", "")
|
||||
.replace("https://", "")
|
||||
.replace('/', "")
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use sea_orm::entity::prelude::*;
|
||||
|
||||
use apb::{Collection, Actor, PublicKey, ActorType};
|
||||
use crate::routes::activitypub;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "users")]
|
||||
|
@ -40,7 +39,7 @@ pub struct Model {
|
|||
impl Model {
|
||||
pub fn new(object: &impl Actor) -> Result<Self, super::FieldError> {
|
||||
let ap_id = object.id().ok_or(super::FieldError("id"))?.to_string();
|
||||
let (domain, preferred_username) = activitypub::split_id(&ap_id);
|
||||
let (domain, preferred_username) = split_user_id(&ap_id);
|
||||
Ok(Model {
|
||||
id: ap_id, preferred_username, domain,
|
||||
actor_type: object.actor_type().ok_or(super::FieldError("type"))?,
|
||||
|
@ -121,3 +120,13 @@ impl Related<super::addressing::Entity> for Entity {
|
|||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
fn split_user_id(id: &str) -> (String, String) {
|
||||
let clean = id
|
||||
.replace("http://", "")
|
||||
.replace("https://", "");
|
||||
let mut splits = clean.split('/');
|
||||
let first = splits.next().unwrap_or("");
|
||||
let last = splits.last().unwrap_or(first);
|
||||
(first.to_string(), last.to_string())
|
||||
}
|
||||
|
|
25
src/routes/activitypub/application.rs
Normal file
25
src/routes/activitypub/application.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use apb::{ActorMut, BaseMut, ObjectMut, PublicKeyMut};
|
||||
use axum::{extract::State, http::StatusCode, Json};
|
||||
|
||||
use crate::{server::Context, url};
|
||||
|
||||
use super::jsonld::LD;
|
||||
|
||||
|
||||
pub async fn view(State(ctx): State<Context>) -> Result<Json<serde_json::Value>, StatusCode> {
|
||||
Ok(Json(
|
||||
serde_json::Value::new_object()
|
||||
.set_id(Some(&url!(ctx, "")))
|
||||
.set_actor_type(Some(apb::ActorType::Application))
|
||||
.set_name(Some("μpub"))
|
||||
.set_summary(Some("micro social network, federated"))
|
||||
.set_published(Some(ctx.app().created))
|
||||
.set_public_key(apb::Node::object(
|
||||
serde_json::Value::new_object()
|
||||
.set_id(Some(&url!(ctx, "#main-key")))
|
||||
.set_owner(Some(&url!(ctx, "")))
|
||||
.set_public_key_pem(&ctx.app().public_key)
|
||||
))
|
||||
.ld_context()
|
||||
))
|
||||
}
|
48
src/routes/activitypub/auth.rs
Normal file
48
src/routes/activitypub/auth.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
use axum::{http::StatusCode, extract::State, Json};
|
||||
use rand::Rng;
|
||||
use sea_orm::{ColumnTrait, Condition, EntityTrait, QueryFilter};
|
||||
|
||||
use crate::{model, server::Context};
|
||||
|
||||
|
||||
#[derive(Debug, Clone, serde::Deserialize)]
|
||||
pub struct LoginForm {
|
||||
email: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
pub async fn login(State(ctx): State<Context>, Json(login): Json<LoginForm>) -> Result<Json<serde_json::Value>, StatusCode> {
|
||||
// TODO salt the pwd
|
||||
match model::credential::Entity::find()
|
||||
.filter(Condition::all()
|
||||
.add(model::credential::Column::Email.eq(login.email))
|
||||
.add(model::credential::Column::Password.eq(sha256::digest(login.password)))
|
||||
)
|
||||
.one(ctx.db())
|
||||
.await
|
||||
{
|
||||
Ok(Some(x)) => {
|
||||
// TODO should probably use crypto-safe rng
|
||||
let token : String = rand::thread_rng()
|
||||
.sample_iter(&rand::distributions::Alphanumeric)
|
||||
.take(128)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
model::session::Entity::insert(
|
||||
model::session::ActiveModel {
|
||||
id: sea_orm::ActiveValue::Set(token.clone()),
|
||||
actor: sea_orm::ActiveValue::Set(x.id),
|
||||
expires: sea_orm::ActiveValue::Set(chrono::Utc::now() + std::time::Duration::from_secs(3600 * 6)),
|
||||
}
|
||||
)
|
||||
.exec(ctx.db())
|
||||
.await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
Ok(Json(serde_json::Value::String(token)))
|
||||
},
|
||||
Ok(None) => Err(StatusCode::UNAUTHORIZED),
|
||||
Err(e) => {
|
||||
tracing::error!("error querying db for user credentials: {e}");
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ use sea_orm::{ColumnTrait, Condition, EntityTrait, Order, QueryFilter, QueryOrde
|
|||
|
||||
use crate::{server::auth::{AuthIdentity, Identity}, errors::UpubError, model, server::Context, url};
|
||||
|
||||
use super::{activity::ap_activity, jsonld::LD, JsonLD, Pagination, PUBLIC_TARGET};
|
||||
use super::{activity::ap_activity, jsonld::LD, JsonLD, Pagination};
|
||||
|
||||
|
||||
pub async fn get(
|
||||
|
@ -20,7 +20,7 @@ pub async fn page(
|
|||
let limit = page.batch.unwrap_or(20).min(50);
|
||||
let offset = page.offset.unwrap_or(0);
|
||||
let mut condition = Condition::any()
|
||||
.add(model::addressing::Column::Actor.eq(PUBLIC_TARGET));
|
||||
.add(model::addressing::Column::Actor.eq(apb::target::PUBLIC));
|
||||
if let Identity::Local(user) = auth {
|
||||
condition = condition
|
||||
.add(model::addressing::Column::Actor.eq(user));
|
||||
|
|
|
@ -3,90 +3,56 @@ pub mod inbox;
|
|||
pub mod outbox;
|
||||
pub mod object;
|
||||
pub mod activity;
|
||||
pub mod application;
|
||||
pub mod auth;
|
||||
pub mod well_known;
|
||||
pub mod router;
|
||||
|
||||
pub mod jsonld;
|
||||
pub use jsonld::JsonLD;
|
||||
|
||||
use axum::{extract::State, http::StatusCode, response::IntoResponse, routing::{get, post}, Json, Router};
|
||||
use rand::Rng;
|
||||
use sea_orm::{ColumnTrait, Condition, EntityTrait, QueryFilter};
|
||||
use axum::{http::StatusCode, response::IntoResponse, routing::{get, post}, Router};
|
||||
|
||||
use apb::{PublicKeyMut, ActorMut, ActorType, Link, Object, ObjectMut, BaseMut, Node};
|
||||
use crate::{model, server::Context, url};
|
||||
|
||||
use self::jsonld::LD;
|
||||
|
||||
pub fn router() -> Router<crate::server::Context> {
|
||||
use crate::routes::activitypub as ap; // TODO use self ?
|
||||
|
||||
Router::new()
|
||||
// core server inbox/outbox, maybe for feeds? TODO do we need these?
|
||||
.route("/", get(ap::view))
|
||||
// TODO shared inboxes and instance stream will come later, just use users *boxes for now
|
||||
.route("/inbox", get(ap::inbox::get))
|
||||
// .route("/inbox", post(ap::inbox::post))
|
||||
// .route("/outbox", get(ap::outbox::get))
|
||||
// .route("/outbox", get(ap::outbox::post))
|
||||
// AUTH routes
|
||||
.route("/auth", post(ap::auth))
|
||||
// .well-known and discovery
|
||||
.route("/.well-known/webfinger", get(ap::well_known::webfinger))
|
||||
.route("/.well-known/host-meta", get(ap::well_known::host_meta))
|
||||
.route("/.well-known/nodeinfo", get(ap::well_known::nodeinfo_discovery))
|
||||
.route("/nodeinfo/:version", get(ap::well_known::nodeinfo))
|
||||
// actor routes
|
||||
.route("/users/:id", get(ap::user::view))
|
||||
.route("/users/:id/inbox", post(ap::user::inbox::post))
|
||||
.route("/users/:id/inbox", get(ap::user::inbox::get))
|
||||
.route("/users/:id/inbox/page", get(ap::user::inbox::page))
|
||||
.route("/users/:id/outbox", post(ap::user::outbox::post))
|
||||
.route("/users/:id/outbox", get(ap::user::outbox::get))
|
||||
.route("/users/:id/outbox/page", get(ap::user::outbox::page))
|
||||
.route("/users/:id/followers", get(ap::user::following::get::<false>))
|
||||
.route("/users/:id/followers/page", get(ap::user::following::page::<false>))
|
||||
.route("/users/:id/following", get(ap::user::following::get::<true>))
|
||||
.route("/users/:id/following/page", get(ap::user::following::page::<true>))
|
||||
// specific object routes
|
||||
.route("/activities/:id", get(ap::activity::view))
|
||||
.route("/objects/:id", get(ap::object::view))
|
||||
pub trait ActivityPubRouter {
|
||||
fn ap_routes(self) -> Self;
|
||||
}
|
||||
|
||||
pub trait Addressed : Object {
|
||||
fn addressed(&self) -> Vec<String>;
|
||||
}
|
||||
|
||||
impl Addressed for serde_json::Value {
|
||||
fn addressed(&self) -> Vec<String> {
|
||||
let mut to : Vec<String> = self.to().map(|x| x.href().to_string()).collect();
|
||||
to.append(&mut self.bto().map(|x| x.href().to_string()).collect());
|
||||
to.append(&mut self.cc().map(|x| x.href().to_string()).collect());
|
||||
to.append(&mut self.bcc().map(|x| x.href().to_string()).collect());
|
||||
to
|
||||
impl ActivityPubRouter for Router<crate::server::Context> {
|
||||
fn ap_routes(self) -> Self {
|
||||
use crate::routes::activitypub as ap; // TODO use self ?
|
||||
|
||||
self
|
||||
// core server inbox/outbox, maybe for feeds? TODO do we need these?
|
||||
.route("/", get(ap::application::view))
|
||||
// TODO shared inboxes and instance stream will come later, just use users *boxes for now
|
||||
.route("/inbox", get(ap::inbox::get))
|
||||
// .route("/inbox", post(ap::inbox::post))
|
||||
// .route("/outbox", get(ap::outbox::get))
|
||||
// .route("/outbox", get(ap::outbox::post))
|
||||
// AUTH routes
|
||||
.route("/auth", post(ap::auth::login))
|
||||
// .well-known and discovery
|
||||
.route("/.well-known/webfinger", get(ap::well_known::webfinger))
|
||||
.route("/.well-known/host-meta", get(ap::well_known::host_meta))
|
||||
.route("/.well-known/nodeinfo", get(ap::well_known::nodeinfo_discovery))
|
||||
.route("/nodeinfo/:version", get(ap::well_known::nodeinfo))
|
||||
// actor routes
|
||||
.route("/users/:id", get(ap::user::view))
|
||||
.route("/users/:id/inbox", post(ap::user::inbox::post))
|
||||
.route("/users/:id/inbox", get(ap::user::inbox::get))
|
||||
.route("/users/:id/inbox/page", get(ap::user::inbox::page))
|
||||
.route("/users/:id/outbox", post(ap::user::outbox::post))
|
||||
.route("/users/:id/outbox", get(ap::user::outbox::get))
|
||||
.route("/users/:id/outbox/page", get(ap::user::outbox::page))
|
||||
.route("/users/:id/followers", get(ap::user::following::get::<false>))
|
||||
.route("/users/:id/followers/page", get(ap::user::following::page::<false>))
|
||||
.route("/users/:id/following", get(ap::user::following::get::<true>))
|
||||
.route("/users/:id/following/page", get(ap::user::following::page::<true>))
|
||||
// specific object routes
|
||||
.route("/activities/:id", get(ap::activity::view))
|
||||
.route("/objects/:id", get(ap::object::view))
|
||||
}
|
||||
}
|
||||
|
||||
pub const PUBLIC_TARGET : &str = "https://www.w3.org/ns/activitystreams#Public";
|
||||
|
||||
pub fn split_id(id: &str) -> (String, String) {
|
||||
let clean = id
|
||||
.replace("http://", "")
|
||||
.replace("https://", "");
|
||||
let mut splits = clean.split('/');
|
||||
let first = splits.next().unwrap_or("");
|
||||
let last = splits.last().unwrap_or(first);
|
||||
(first.to_string(), last.to_string())
|
||||
}
|
||||
|
||||
pub fn domain(domain: &str) -> String {
|
||||
domain
|
||||
.replace("http://", "")
|
||||
.replace("https://", "")
|
||||
.replace('/', "")
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
// TODO i don't really like how pleroma/mastodon do it actually, maybe change this?
|
||||
pub struct Pagination {
|
||||
|
@ -104,87 +70,3 @@ impl IntoResponse for CreationResult {
|
|||
.into_response()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn view(State(ctx): State<Context>) -> Result<Json<serde_json::Value>, StatusCode> {
|
||||
Ok(Json(
|
||||
serde_json::Value::new_object()
|
||||
.set_id(Some(&url!(ctx, "")))
|
||||
.set_actor_type(Some(ActorType::Application))
|
||||
.set_name(Some("μpub"))
|
||||
.set_summary(Some("micro social network, federated"))
|
||||
.set_published(Some(ctx.app().created))
|
||||
.set_public_key(Node::object(
|
||||
serde_json::Value::new_object()
|
||||
.set_id(Some(&url!(ctx, "#main-key")))
|
||||
.set_owner(Some(&url!(ctx, "")))
|
||||
.set_public_key_pem(&ctx.app().public_key)
|
||||
))
|
||||
.ld_context()
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone, serde::Deserialize)]
|
||||
pub struct LoginForm {
|
||||
email: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
pub async fn auth(State(ctx): State<Context>, Json(login): Json<LoginForm>) -> Result<Json<serde_json::Value>, StatusCode> {
|
||||
// TODO salt the pwd
|
||||
match model::credential::Entity::find()
|
||||
.filter(Condition::all()
|
||||
.add(model::credential::Column::Email.eq(login.email))
|
||||
.add(model::credential::Column::Password.eq(sha256::digest(login.password)))
|
||||
)
|
||||
.one(ctx.db())
|
||||
.await
|
||||
{
|
||||
Ok(Some(x)) => {
|
||||
// TODO should probably use crypto-safe rng
|
||||
let token : String = rand::thread_rng()
|
||||
.sample_iter(&rand::distributions::Alphanumeric)
|
||||
.take(128)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
model::session::Entity::insert(
|
||||
model::session::ActiveModel {
|
||||
id: sea_orm::ActiveValue::Set(token.clone()),
|
||||
actor: sea_orm::ActiveValue::Set(x.id),
|
||||
expires: sea_orm::ActiveValue::Set(chrono::Utc::now() + std::time::Duration::from_secs(3600 * 6)),
|
||||
}
|
||||
)
|
||||
.exec(ctx.db())
|
||||
.await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
Ok(Json(serde_json::Value::String(token)))
|
||||
},
|
||||
Ok(None) => Err(StatusCode::UNAUTHORIZED),
|
||||
Err(e) => {
|
||||
tracing::error!("error querying db for user credentials: {e}");
|
||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[axum::async_trait]
|
||||
pub trait APOutbox {
|
||||
async fn create_note(&self, uid: String, object: serde_json::Value) -> crate::Result<String>;
|
||||
async fn create(&self, uid: String, activity: serde_json::Value) -> crate::Result<String>;
|
||||
async fn like(&self, uid: String, activity: serde_json::Value) -> crate::Result<String>;
|
||||
async fn follow(&self, uid: String, activity: serde_json::Value) -> crate::Result<String>;
|
||||
async fn accept(&self, uid: String, activity: serde_json::Value) -> crate::Result<String>;
|
||||
async fn reject(&self, _uid: String, _activity: serde_json::Value) -> crate::Result<String>;
|
||||
async fn undo(&self, uid: String, activity: serde_json::Value) -> crate::Result<String>;
|
||||
}
|
||||
|
||||
#[axum::async_trait]
|
||||
pub trait APInbox {
|
||||
async fn create(&self, activity: serde_json::Value) -> crate::Result<()>;
|
||||
async fn like(&self, activity: serde_json::Value) -> crate::Result<()>;
|
||||
async fn follow(&self, activity: serde_json::Value) -> crate::Result<()>;
|
||||
async fn accept(&self, activity: serde_json::Value) -> crate::Result<()>;
|
||||
async fn reject(&self, activity: serde_json::Value) -> crate::Result<()>;
|
||||
async fn undo(&self, activity: serde_json::Value) -> crate::Result<()>;
|
||||
async fn delete(&self, activity: serde_json::Value) -> crate::Result<()>;
|
||||
async fn update(&self, activity: serde_json::Value) -> crate::Result<()>;
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
use axum::{routing::{get, post}, Router};
|
||||
use sea_orm::DatabaseConnection;
|
||||
use crate::routes::activitypub as ap;
|
||||
|
||||
pub async fn serve(db: DatabaseConnection, domain: String) -> std::io::Result<()> {
|
||||
// build our application with a single route
|
||||
let app = Router::new()
|
||||
// core server inbox/outbox, maybe for feeds? TODO do we need these?
|
||||
.route("/", get(ap::view))
|
||||
// TODO shared inboxes and instance stream will come later, just use users *boxes for now
|
||||
.route("/inbox", get(ap::inbox::get))
|
||||
// .route("/inbox", post(ap::inbox::post))
|
||||
// .route("/outbox", get(ap::outbox::get))
|
||||
// .route("/outbox", get(ap::outbox::post))
|
||||
// AUTH routes
|
||||
.route("/auth", post(ap::auth))
|
||||
// .well-known and discovery
|
||||
.route("/.well-known/webfinger", get(ap::well_known::webfinger))
|
||||
.route("/.well-known/host-meta", get(ap::well_known::host_meta))
|
||||
.route("/.well-known/nodeinfo", get(ap::well_known::nodeinfo_discovery))
|
||||
.route("/nodeinfo/:version", get(ap::well_known::nodeinfo))
|
||||
// actor routes
|
||||
.route("/users/:id", get(ap::user::view))
|
||||
.route("/users/:id/inbox", post(ap::user::inbox::post))
|
||||
.route("/users/:id/inbox", get(ap::user::inbox::get))
|
||||
.route("/users/:id/inbox/page", get(ap::user::inbox::page))
|
||||
.route("/users/:id/outbox", post(ap::user::outbox::post))
|
||||
.route("/users/:id/outbox", get(ap::user::outbox::get))
|
||||
.route("/users/:id/outbox/page", get(ap::user::outbox::page))
|
||||
.route("/users/:id/followers", get(ap::user::following::get::<false>))
|
||||
.route("/users/:id/followers/page", get(ap::user::following::page::<false>))
|
||||
.route("/users/:id/following", get(ap::user::following::get::<true>))
|
||||
.route("/users/:id/following/page", get(ap::user::following::page::<true>))
|
||||
// specific object routes
|
||||
.route("/activities/:id", get(ap::activity::view))
|
||||
.route("/objects/:id", get(ap::object::view))
|
||||
.with_state(
|
||||
crate::server::Context::new(db, domain).await.expect("could not create server state")
|
||||
);
|
||||
|
||||
// run our app with hyper, listening locally on port 3000
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
|
||||
|
||||
axum::serve(listener, app)
|
||||
.await
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
use axum::{extract::{Path, Query, State}, http::StatusCode, Json};
|
||||
use sea_orm::{ColumnTrait, Condition, EntityTrait, Order, QueryFilter, QueryOrder, QuerySelect};
|
||||
|
||||
use apb::{ActivityType, ObjectType, Base, BaseType};
|
||||
use crate::{routes::activitypub::{activity::ap_activity, jsonld::LD, APInbox, JsonLD, Pagination}, server::{Context, auth::{AuthIdentity, Identity}}, errors::UpubError, model, url};
|
||||
use apb::{server::Inbox, ActivityType, Base, BaseType, ObjectType};
|
||||
use crate::{routes::activitypub::{activity::ap_activity, jsonld::LD, JsonLD, Pagination}, server::{Context, auth::{AuthIdentity, Identity}}, errors::UpubError, model, url};
|
||||
|
||||
pub async fn get(
|
||||
State(ctx): State<Context>,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use axum::{extract::{Path, Query, State}, http::StatusCode, Json};
|
||||
use sea_orm::{EntityTrait, Order, QueryOrder, QuerySelect};
|
||||
|
||||
use apb::{AcceptType, ActivityMut, ActivityType, Base, BaseType, Node, ObjectType, RejectType};
|
||||
use crate::{routes::activitypub::{jsonld::LD, APOutbox, CreationResult, JsonLD, Pagination}, server::auth::{AuthIdentity, Identity}, errors::UpubError, model, server::Context, url};
|
||||
use apb::{server::Outbox, AcceptType, ActivityMut, ActivityType, Base, BaseType, Node, ObjectType, RejectType};
|
||||
use crate::{routes::activitypub::{jsonld::LD, CreationResult, JsonLD, Pagination}, server::auth::{AuthIdentity, Identity}, errors::UpubError, model, server::Context, url};
|
||||
|
||||
pub async fn get(
|
||||
State(ctx): State<Context>,
|
||||
|
|
|
@ -1,69 +1,72 @@
|
|||
use axum::{http::StatusCode, routing::{delete, get, patch, post}, Router};
|
||||
use crate::server::Context;
|
||||
|
||||
#[allow(unused)]
|
||||
async fn todo() -> StatusCode { StatusCode::NOT_IMPLEMENTED }
|
||||
|
||||
#[allow(unused)]
|
||||
pub async fn mastodon_api_routes(router: Router<Context>) -> Router<Context> {
|
||||
router.nest(
|
||||
// TODO Oauth is just under /oauth
|
||||
"/api/v1", Router::new()
|
||||
.route("/apps", post(todo)) // create an application
|
||||
.route("/apps/verify_credentials", post(todo)) // confirm that the app's oauth2 credentials work
|
||||
.route("/emails/confirmations", post(todo))
|
||||
.route("/accounts", post(todo))
|
||||
.route("/accounts/verify_credentials", get(todo))
|
||||
.route("/accounts/update_credentials", patch(todo))
|
||||
.route("/accounts/:id", get(todo))
|
||||
.route("/accounts/:id/statuses", get(todo))
|
||||
.route("/accounts/:id/followers", get(todo))
|
||||
.route("/accounts/:id/following", get(todo))
|
||||
.route("/accounts/:id/featured_tags", get(todo))
|
||||
.route("/accounts/:id/lists", get(todo))
|
||||
.route("/accounts/:id/follow", post(todo))
|
||||
.route("/accounts/:id/unfollow", post(todo))
|
||||
.route("/accounts/:id/remove_from_followers", post(todo))
|
||||
.route("/accounts/:id/block", post(todo))
|
||||
.route("/accounts/:id/unblock", post(todo))
|
||||
.route("/accounts/:id/mute", post(todo))
|
||||
.route("/accounts/:id/unmute", post(todo))
|
||||
.route("/accounts/:id/pin", post(todo))
|
||||
.route("/accounts/:id/unpin", post(todo))
|
||||
.route("/accounts/:id/note", post(todo))
|
||||
.route("/accounts/relationships", get(todo))
|
||||
.route("/accounts/familiar_followers", get(todo))
|
||||
.route("/accounts/search", get(todo))
|
||||
.route("/accounts/lookup", get(todo))
|
||||
.route("/accounts/:id/identity_proofs", get(todo))
|
||||
.route("/bookmarks", get(todo))
|
||||
.route("/favourites", get(todo))
|
||||
.route("/mutes", get(todo))
|
||||
.route("/blocks", get(todo))
|
||||
.route("/domain_blocks", get(todo))
|
||||
.route("/domain_blocks", post(todo))
|
||||
.route("/domain_blocks", delete(todo))
|
||||
// TODO filters! api v2
|
||||
.route("/reports", post(todo))
|
||||
.route("/follow_requests", get(todo))
|
||||
.route("/follow_requests/:account_id/authorize", get(todo))
|
||||
.route("/follow_requests/:account_id/reject", get(todo))
|
||||
.route("/endorsements", get(todo))
|
||||
.route("/featured_tags", get(todo))
|
||||
.route("/featured_tags", post(todo))
|
||||
.route("/featured_tags/:id", delete(todo))
|
||||
.route("/featured_tags/suggestions", get(todo))
|
||||
.route("/preferences", get(todo))
|
||||
.route("/followed_tags", get(todo))
|
||||
// TODO suggestions! api v2
|
||||
.route("/suggestions", get(todo))
|
||||
.route("/suggestions/:account_id", delete(todo))
|
||||
.route("/tags/:id", get(todo))
|
||||
.route("/tags/:id/follow", post(todo))
|
||||
.route("/tags/:id/unfollow", post(todo))
|
||||
.route("/profile/avatar", delete(todo))
|
||||
.route("/profile/header", delete(todo))
|
||||
.route("/statuses", post(todo))
|
||||
|
||||
)
|
||||
pub trait MastodonRouter {
|
||||
fn mastodon_routes(self) -> Self;
|
||||
}
|
||||
|
||||
impl MastodonRouter for Router<Context> {
|
||||
fn mastodon_routes(self) -> Self {
|
||||
self.nest(
|
||||
// TODO Oauth is just under /oauth
|
||||
"/api/v1", Router::new()
|
||||
.route("/apps", post(todo)) // create an application
|
||||
.route("/apps/verify_credentials", post(todo)) // confirm that the app's oauth2 credentials work
|
||||
.route("/emails/confirmations", post(todo))
|
||||
.route("/accounts", post(todo))
|
||||
.route("/accounts/verify_credentials", get(todo))
|
||||
.route("/accounts/update_credentials", patch(todo))
|
||||
.route("/accounts/:id", get(todo))
|
||||
.route("/accounts/:id/statuses", get(todo))
|
||||
.route("/accounts/:id/followers", get(todo))
|
||||
.route("/accounts/:id/following", get(todo))
|
||||
.route("/accounts/:id/featured_tags", get(todo))
|
||||
.route("/accounts/:id/lists", get(todo))
|
||||
.route("/accounts/:id/follow", post(todo))
|
||||
.route("/accounts/:id/unfollow", post(todo))
|
||||
.route("/accounts/:id/remove_from_followers", post(todo))
|
||||
.route("/accounts/:id/block", post(todo))
|
||||
.route("/accounts/:id/unblock", post(todo))
|
||||
.route("/accounts/:id/mute", post(todo))
|
||||
.route("/accounts/:id/unmute", post(todo))
|
||||
.route("/accounts/:id/pin", post(todo))
|
||||
.route("/accounts/:id/unpin", post(todo))
|
||||
.route("/accounts/:id/note", post(todo))
|
||||
.route("/accounts/relationships", get(todo))
|
||||
.route("/accounts/familiar_followers", get(todo))
|
||||
.route("/accounts/search", get(todo))
|
||||
.route("/accounts/lookup", get(todo))
|
||||
.route("/accounts/:id/identity_proofs", get(todo))
|
||||
.route("/bookmarks", get(todo))
|
||||
.route("/favourites", get(todo))
|
||||
.route("/mutes", get(todo))
|
||||
.route("/blocks", get(todo))
|
||||
.route("/domain_blocks", get(todo))
|
||||
.route("/domain_blocks", post(todo))
|
||||
.route("/domain_blocks", delete(todo))
|
||||
// TODO filters! api v2
|
||||
.route("/reports", post(todo))
|
||||
.route("/follow_requests", get(todo))
|
||||
.route("/follow_requests/:account_id/authorize", get(todo))
|
||||
.route("/follow_requests/:account_id/reject", get(todo))
|
||||
.route("/endorsements", get(todo))
|
||||
.route("/featured_tags", get(todo))
|
||||
.route("/featured_tags", post(todo))
|
||||
.route("/featured_tags/:id", delete(todo))
|
||||
.route("/featured_tags/suggestions", get(todo))
|
||||
.route("/preferences", get(todo))
|
||||
.route("/followed_tags", get(todo))
|
||||
// TODO suggestions! api v2
|
||||
.route("/suggestions", get(todo))
|
||||
.route("/suggestions/:account_id", delete(todo))
|
||||
.route("/tags/:id", get(todo))
|
||||
.route("/tags/:id/follow", post(todo))
|
||||
.route("/tags/:id/unfollow", post(todo))
|
||||
.route("/profile/avatar", delete(todo))
|
||||
.route("/profile/header", delete(todo))
|
||||
.route("/statuses", post(todo))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,3 +5,12 @@ pub mod web;
|
|||
|
||||
#[cfg(feature = "mastodon")]
|
||||
pub mod mastodon;
|
||||
|
||||
#[cfg(not(feature = "mastodon"))]
|
||||
pub mod mastodon {
|
||||
pub trait MastodonRouter {
|
||||
fn mastodon_routes(self) -> Self { self }
|
||||
}
|
||||
|
||||
impl MastodonRouter for axum::Router<crate::server::Context> {}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use apb::{BaseMut, CollectionMut, CollectionPageMut};
|
|||
use openssl::rsa::Rsa;
|
||||
use sea_orm::{ColumnTrait, Condition, DatabaseConnection, EntityTrait, QueryFilter, QuerySelect, SelectColumns, Set};
|
||||
|
||||
use crate::{model, routes::activitypub::{jsonld::LD, PUBLIC_TARGET}};
|
||||
use crate::{model, routes::activitypub::jsonld::LD};
|
||||
|
||||
use super::{dispatcher::Dispatcher, fetcher::Fetcher};
|
||||
|
||||
|
@ -176,7 +176,7 @@ impl Context {
|
|||
.iter()
|
||||
.filter(|to| !to.is_empty())
|
||||
.filter(|to| Context::server(to) != self.base())
|
||||
.filter(|to| to != &PUBLIC_TARGET)
|
||||
.filter(|to| to != &apb::target::PUBLIC)
|
||||
.map(|to| model::delivery::ActiveModel {
|
||||
id: sea_orm::ActiveValue::NotSet,
|
||||
actor: Set(from.to_string()),
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
use apb::{Activity, Base, Object};
|
||||
use apb::{target::Addressed, Activity, Base, Object};
|
||||
use sea_orm::{sea_query::Expr, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter, Set};
|
||||
|
||||
use crate::{errors::{LoggableError, UpubError}, model, routes::activitypub::{APInbox, Addressed}};
|
||||
use crate::{errors::{LoggableError, UpubError}, model};
|
||||
|
||||
use super::Context;
|
||||
|
||||
|
||||
#[axum::async_trait]
|
||||
impl APInbox for Context {
|
||||
impl apb::server::Inbox for Context {
|
||||
type Error = UpubError;
|
||||
type Activity = serde_json::Value;
|
||||
|
||||
async fn create(&self, activity: serde_json::Value) -> crate::Result<()> {
|
||||
let activity_model = model::activity::Model::new(&activity)?;
|
||||
let activity_targets = activity.addressed();
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
use apb::{Activity, ActivityMut, BaseMut, Node, ObjectMut};
|
||||
use apb::{target::Addressed, Activity, ActivityMut, BaseMut, Node, ObjectMut};
|
||||
use sea_orm::{EntityTrait, IntoActiveModel, Set};
|
||||
|
||||
use crate::{errors::UpubError, model, routes::activitypub::{APOutbox, Addressed}};
|
||||
use crate::{errors::UpubError, model};
|
||||
|
||||
use super::Context;
|
||||
|
||||
|
||||
#[axum::async_trait]
|
||||
impl APOutbox for Context {
|
||||
impl apb::server::Outbox for Context {
|
||||
type Error = UpubError;
|
||||
type Object = serde_json::Value;
|
||||
type Activity = serde_json::Value;
|
||||
|
||||
async fn create_note(&self, uid: String, object: serde_json::Value) -> crate::Result<String> {
|
||||
let oid = self.oid(uuid::Uuid::new_v4().to_string());
|
||||
let aid = self.aid(uuid::Uuid::new_v4().to_string());
|
||||
|
|
Loading…
Reference in a new issue