1
0
Fork 0
mirror of https://github.com/Nutomic/ibis.git synced 2025-01-30 23:31:35 +00:00
ibis/src/backend/api/mod.rs

141 lines
4.6 KiB
Rust
Raw Normal View History

2024-01-03 12:29:25 +00:00
use crate::backend::api::article::create_article;
use crate::backend::api::article::{edit_article, fork_article, get_article};
use crate::backend::api::instance::follow_instance;
use crate::backend::api::instance::get_local_instance;
use crate::backend::api::user::login_user;
use crate::backend::api::user::register_user;
use crate::backend::api::user::validate;
use crate::backend::database::conflict::{ApiConflict, DbConflict};
use crate::backend::database::instance::DbInstance;
use crate::backend::database::user::LocalUserView;
use crate::backend::database::MyDataHandle;
use crate::backend::error::MyResult;
2024-01-03 16:06:52 +00:00
use crate::common::DbEdit;
use crate::common::{ArticleView, DbArticle};
2023-12-13 12:32:44 +00:00
use activitypub_federation::config::Data;
use activitypub_federation::fetch::object_id::ObjectId;
use axum::extract::Query;
use axum::routing::{get, post};
use axum::{
extract::TypedHeader,
headers::authorization::{Authorization, Bearer},
http::Request,
http::StatusCode,
middleware::{self, Next},
response::Response,
2023-12-19 14:32:14 +00:00
Extension,
};
2023-12-13 12:32:44 +00:00
use axum::{Json, Router};
use axum_macros::debug_handler;
use futures::future::try_join_all;
2024-01-03 12:29:25 +00:00
use log::warn;
2024-01-03 16:06:52 +00:00
use serde::{Deserialize, Serialize};
2023-12-13 12:32:44 +00:00
use url::Url;
pub mod article;
pub mod instance;
pub mod user;
pub fn api_routes() -> Router {
Router::new()
.route(
"/article",
get(get_article).post(create_article).patch(edit_article),
)
.route("/article/fork", post(fork_article))
.route("/edit_conflicts", get(edit_conflicts))
.route("/resolve_instance", get(resolve_instance))
.route("/resolve_article", get(resolve_article))
.route("/instance", get(get_local_instance))
.route("/instance/follow", post(follow_instance))
.route("/search", get(search_article))
.route("/user/register", post(register_user))
.route("/user/login", post(login_user))
.route_layer(middleware::from_fn(auth))
}
async fn auth<B>(
data: Data<MyDataHandle>,
auth: Option<TypedHeader<Authorization<Bearer>>>,
mut request: Request<B>,
next: Next<B>,
) -> Result<Response, StatusCode> {
if let Some(auth) = auth {
let user = validate(auth.token(), &data).await.map_err(|e| {
warn!("Failed to validate auth token: {e}");
StatusCode::UNAUTHORIZED
})?;
request.extensions_mut().insert(user);
}
let response = next.run(request).await;
Ok(response)
2023-12-13 12:32:44 +00:00
}
#[derive(Deserialize, Serialize)]
pub struct ResolveObject {
pub id: Url,
}
/// Fetch a remote instance actor. This automatically synchronizes the remote articles collection to
/// the local instance, and allows for interactions such as following.
#[debug_handler]
async fn resolve_instance(
Query(query): Query<ResolveObject>,
data: Data<MyDataHandle>,
) -> MyResult<Json<DbInstance>> {
2024-01-03 16:06:52 +00:00
// TODO: workaround because axum makes it hard to have multiple routes on /
let id = format!("{}instance", query.id);
let instance: DbInstance = ObjectId::parse(&id)?.dereference(&data).await?;
2023-12-13 12:32:44 +00:00
Ok(Json(instance))
}
/// Fetch a remote article, including edits collection. Allows viewing and editing. Note that new
/// article changes can only be received if we follow the instance, or if it is refetched manually.
#[debug_handler]
async fn resolve_article(
Query(query): Query<ResolveObject>,
data: Data<MyDataHandle>,
) -> MyResult<Json<ArticleView>> {
let article: DbArticle = ObjectId::from(query.id).dereference(&data).await?;
let edits = DbEdit::read_for_article(&article, &data.db_connection)?;
let latest_version = edits.last().unwrap().hash.clone();
Ok(Json(ArticleView {
article,
edits,
latest_version,
}))
}
/// Get a list of all unresolved edit conflicts.
#[debug_handler]
2023-12-19 14:32:14 +00:00
async fn edit_conflicts(
Extension(user): Extension<LocalUserView>,
data: Data<MyDataHandle>,
) -> MyResult<Json<Vec<ApiConflict>>> {
let conflicts = DbConflict::list(&user.local_user, &data.db_connection)?;
2023-12-13 12:32:44 +00:00
let conflicts: Vec<ApiConflict> = try_join_all(conflicts.into_iter().map(|c| {
let data = data.reset_request_count();
async move { c.to_api_conflict(&data).await }
}))
.await?
.into_iter()
.flatten()
.collect();
Ok(Json(conflicts))
}
#[derive(Deserialize, Serialize, Clone)]
pub struct SearchArticleData {
pub query: String,
}
/// Search articles for matching title or body text.
#[debug_handler]
async fn search_article(
Query(query): Query<SearchArticleData>,
data: Data<MyDataHandle>,
) -> MyResult<Json<Vec<DbArticle>>> {
let article = DbArticle::search(&query.query, &data.db_connection)?;
Ok(Json(article))
}