Global, mono-binary to libraries and binaries
This separates the previous mono-binary setup into separate libraries and binaries. Specifically it split the old since api/ingestor binary into an Atproto, and DB library, as well as an api, and ingestor binary. Atproto Lib Was mostly untouched. The original URI implementation was changed to use FromStr, otherwise only imports were changed. DB Lib Is mostly unused, so there wasn't much that needed to be changed. Some new files were added so that future work on it can hit the ground running. Api Binary Is almost entirely the same. Imports were changed and the ingestor code of main was removed. Ingestor Binary Was almost entirely refactored. An interface to made injestors was added, and it was modularized. The only shared code is in Ingestor.start(), and collections.rs's macros, but that is mostly boilerplate.
This commit is contained in:
parent
45acaaa601
commit
eb28549a0f
31 changed files with 582 additions and 636 deletions
14
api/Cargo.toml
Normal file
14
api/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "api"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
atproto.workspace = true
|
||||
axum = { version = "0.8.3", features = ["json"] }
|
||||
http = "1.3.1"
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
tracing.workspace = true
|
||||
35
api/src/main.rs
Normal file
35
api/src/main.rs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
use crate::router::{
|
||||
Router,
|
||||
Endpoint,
|
||||
xrpc::{
|
||||
QueryInput,
|
||||
ProcedureInput,
|
||||
Response,
|
||||
error,
|
||||
},
|
||||
};
|
||||
use atproto::Nsid;
|
||||
use http::status::StatusCode;
|
||||
|
||||
mod router;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let subscriber = tracing_subscriber::FmtSubscriber::new();
|
||||
let _ = tracing::subscriber::set_global_default(subscriber);
|
||||
|
||||
let mut router = Router::new();
|
||||
let get_nsid = Nsid::new(String::from("me.woach.get")).expect("me.woach.get is a valid nsid");
|
||||
let post_nsid = Nsid::new(String::from("me.woach.post")).expect("me.woach.post is a valid nsid");
|
||||
router = router.add_endpoint(Endpoint::new_xrpc_query(get_nsid, test));
|
||||
router = router.add_endpoint(Endpoint::new_xrpc_procedure(post_nsid, test2));
|
||||
router.serve().await;
|
||||
}
|
||||
|
||||
async fn test(_data: QueryInput) -> Response {
|
||||
error(StatusCode::OK, "error", "message")
|
||||
}
|
||||
|
||||
async fn test2(_data: ProcedureInput) -> Response {
|
||||
error(StatusCode::OK, "error", "message")
|
||||
}
|
||||
59
api/src/router.rs
Normal file
59
api/src/router.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
use crate::router::xrpc::{
|
||||
XrpcEndpoint,
|
||||
XrpcHandler,
|
||||
QueryInput,
|
||||
ProcedureInput,
|
||||
};
|
||||
use atproto::Nsid;
|
||||
use axum::Router as AxumRouter;
|
||||
use core::net::SocketAddr;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
pub struct Router {
|
||||
addr: SocketAddr,
|
||||
router: AxumRouter,
|
||||
}
|
||||
|
||||
// In case server ever needs to support more than just XRPC
|
||||
pub enum Endpoint {
|
||||
Xrpc(XrpcEndpoint),
|
||||
}
|
||||
impl Endpoint {
|
||||
pub fn new_xrpc_query<Q>(nsid: Nsid, query: Q) -> Self
|
||||
where
|
||||
Q: XrpcHandler<QueryInput> + Clone
|
||||
{
|
||||
Endpoint::Xrpc(XrpcEndpoint::new_query(nsid,query))
|
||||
}
|
||||
pub fn new_xrpc_procedure<P>(nsid: Nsid, procedure: P) -> Self
|
||||
where
|
||||
P: XrpcHandler<ProcedureInput> + Clone
|
||||
{
|
||||
Endpoint::Xrpc(XrpcEndpoint::new_procedure(nsid,procedure))
|
||||
}
|
||||
}
|
||||
|
||||
pub mod xrpc;
|
||||
|
||||
impl Router {
|
||||
pub fn new() -> Self {
|
||||
let mut router = AxumRouter::new();
|
||||
router = XrpcEndpoint::not_implemented().add_to_router(router);
|
||||
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)), 6702);
|
||||
Router { router, addr }
|
||||
}
|
||||
|
||||
pub fn add_endpoint(mut self, endpoint: Endpoint) -> Self {
|
||||
match endpoint {
|
||||
Endpoint::Xrpc(ep) => self.router = ep.add_to_router(self.router),
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn serve(self) {
|
||||
let listener = TcpListener::bind(self.addr).await.unwrap();
|
||||
|
||||
axum::serve(listener, self.router).await.unwrap();
|
||||
}
|
||||
}
|
||||
181
api/src/router/xrpc.rs
Normal file
181
api/src/router/xrpc.rs
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
pin::Pin,
|
||||
future::Future,
|
||||
};
|
||||
use atproto::Nsid;
|
||||
use axum::{
|
||||
extract::{
|
||||
Json,
|
||||
Query,
|
||||
Request,
|
||||
FromRequest,
|
||||
FromRequestParts,
|
||||
rejection::QueryRejection,
|
||||
},
|
||||
body::Bytes,
|
||||
routing::{
|
||||
get,
|
||||
post,
|
||||
method_routing::MethodRouter,
|
||||
},
|
||||
http::{
|
||||
StatusCode,
|
||||
request::Parts,
|
||||
},
|
||||
Router as axumRouter,
|
||||
};
|
||||
use serde_json::{Value, json};
|
||||
|
||||
enum Path {
|
||||
Nsid(Nsid),
|
||||
NotImplemented,
|
||||
}
|
||||
|
||||
pub struct XrpcEndpoint {
|
||||
path: Path,
|
||||
resolver: MethodRouter,
|
||||
}
|
||||
|
||||
pub type Response = (StatusCode, Json<Value>);
|
||||
pub fn error(code: StatusCode, error: &str, message: &str) -> Response {
|
||||
(
|
||||
code,
|
||||
Json(json!({
|
||||
"error": error,
|
||||
"message": message
|
||||
}))
|
||||
)
|
||||
}
|
||||
pub fn response(code: StatusCode, message: &str) -> Response {
|
||||
error(code, "", message)
|
||||
}
|
||||
|
||||
pub struct QueryInput {
|
||||
parameters: HashMap<String, String>,
|
||||
}
|
||||
impl<S> FromRequestParts<S> for QueryInput
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Response;
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, _state: &S)
|
||||
-> Result<Self, Self::Rejection> {
|
||||
let query_params: Result<Query<HashMap<String, String>>, QueryRejection> = Query::try_from_uri(&parts.uri);
|
||||
match query_params {
|
||||
Ok(p) => Ok(QueryInput { parameters: p.0 }),
|
||||
Err(e) => Err(error(StatusCode::BAD_REQUEST, "Bad Parameters", &e.body_text())),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct ProcedureInput {
|
||||
parameters: HashMap<String, String>,
|
||||
input: Json<Value>,
|
||||
}
|
||||
impl<S> FromRequest<S> for ProcedureInput
|
||||
where
|
||||
Bytes: FromRequest<S>,
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = Response;
|
||||
|
||||
async fn from_request(req: Request, state: &S)
|
||||
-> Result<Self, Self::Rejection> {
|
||||
let query_params: Result<Query<HashMap<String, String>>, QueryRejection> = Query::try_from_uri(req.uri());
|
||||
let parameters = match query_params {
|
||||
Ok(p) => p.0,
|
||||
Err(e) => return Err(error(StatusCode::BAD_REQUEST, "Bad Parameters", &e.body_text())),
|
||||
};
|
||||
|
||||
let json_value = Json::<Value>::from_request(req, state).await;
|
||||
let input: Json<Value> = match json_value {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Err(error(StatusCode::BAD_REQUEST, "Bad Parameters", &e.body_text())),
|
||||
};
|
||||
|
||||
Ok(ProcedureInput { parameters, input })
|
||||
}
|
||||
}
|
||||
|
||||
pub trait XrpcHandler<Input>: Send + Sync + 'static {
|
||||
fn call(&self, input: Input)
|
||||
-> Pin<Box<dyn Future<Output = Response> + Send>>;
|
||||
}
|
||||
impl<F, Fut> XrpcHandler<QueryInput> for F
|
||||
where
|
||||
F: Fn(QueryInput) -> Fut + Send + Sync + 'static,
|
||||
Fut: Future<Output = Response> + Send + 'static,
|
||||
{
|
||||
fn call(&self, input: QueryInput)
|
||||
-> Pin<Box<dyn Future<Output = Response>+ Send>> {
|
||||
Box::pin((self)(input))
|
||||
}
|
||||
}
|
||||
impl<F, Fut> XrpcHandler<ProcedureInput> for F
|
||||
where
|
||||
F: Fn(ProcedureInput) -> Fut + Send + Sync + 'static,
|
||||
Fut: Future<Output = Response> + Send + 'static,
|
||||
{
|
||||
fn call(&self, input: ProcedureInput)
|
||||
-> Pin<Box<dyn Future<Output = Response>+ Send>> {
|
||||
Box::pin((self)(input))
|
||||
}
|
||||
}
|
||||
|
||||
impl XrpcEndpoint {
|
||||
pub fn new_query<Q>(nsid: Nsid, query: Q) -> Self
|
||||
where
|
||||
Q: XrpcHandler<QueryInput> + Clone
|
||||
{
|
||||
XrpcEndpoint {
|
||||
path: Path::Nsid(nsid),
|
||||
resolver: get(async move | mut parts: Parts | -> Response {
|
||||
match QueryInput::from_request_parts(&mut parts, &()).await {
|
||||
Ok(qi) => query.call(qi).await,
|
||||
Err(e) => e
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_procedure<P>(nsid: Nsid, procedure: P) -> Self
|
||||
where
|
||||
P: XrpcHandler<ProcedureInput> + Clone
|
||||
{
|
||||
XrpcEndpoint {
|
||||
path: Path::Nsid(nsid),
|
||||
resolver: post(async move | req: Request | -> Response {
|
||||
match ProcedureInput::from_request(req, &()).await {
|
||||
Ok(pi) => procedure.call(pi).await,
|
||||
Err(e) => e
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_to_router(self, router: axumRouter) -> axumRouter {
|
||||
let path = match self.path {
|
||||
Path::Nsid(nsid) => &("/xrpc/".to_owned() + nsid.as_str()),
|
||||
Path::NotImplemented => "/xrpc/{*nsid}",
|
||||
};
|
||||
|
||||
router.route(path, self.resolver)
|
||||
}
|
||||
|
||||
pub fn not_implemented() -> Self {
|
||||
let resolver = (
|
||||
StatusCode::NOT_IMPLEMENTED,
|
||||
Json(json!({
|
||||
"error": "MethodNotImplemented",
|
||||
"message": "Method Not Implemented"
|
||||
}))
|
||||
);
|
||||
|
||||
XrpcEndpoint {
|
||||
path: Path::NotImplemented,
|
||||
resolver: get(resolver.clone()).post(resolver),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue