commit
f90f2ee4a4
@ -0,0 +1 @@
|
||||
/target
|
@ -0,0 +1,3 @@
|
||||
[submodule "NotePoster"]
|
||||
path = NotePoster
|
||||
url = https://gitea.itycodes.org/itycodes/NotePoster
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "rust_fedi"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
axum = "0.8.3"
|
||||
env_logger = "0.11.8"
|
||||
rsa = "0.9.8"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
sha2 = "0.10.8"
|
||||
tokio = { version = "1.44.2", features = ["full"] }
|
@ -0,0 +1 @@
|
||||
Subproject commit d594dbc68635e746361a8ef0a0d0583d800c0506
|
@ -0,0 +1,20 @@
|
||||
use axum::http::HeaderMap;
|
||||
use axum::body::Bytes;
|
||||
|
||||
use core::slice::SlicePattern;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
pub async fn inbox_post(headers: HeaderMap, bytes: Bytes) {
|
||||
println!("Headers: {:#?}", headers);
|
||||
let _ = File::create("./last.json").map(|mut res| {
|
||||
res.write_all(&bytes).expect("Failed writing");
|
||||
println!("Writing file");
|
||||
});
|
||||
let json = String::from_utf8(Vec::from(bytes.as_slice())).expect("Invalid UTF-8");
|
||||
let json: serde_json::Value = serde_json::from_str(&json).expect("Invalid JSON");
|
||||
match serde_json::to_string_pretty(&json) {
|
||||
Ok(pretty_json) => println!("Received JSON:\n{}\n\n", pretty_json),
|
||||
Err(err) => println!("Failed to format JSON: {}\n\n", err),
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
#![feature(slice_pattern)]
|
||||
|
||||
mod inbox;
|
||||
mod users;
|
||||
mod webfinger;
|
||||
|
||||
use axum::{
|
||||
Router,
|
||||
http::{StatusCode, Uri},
|
||||
routing::{get, post},
|
||||
};
|
||||
use inbox::inbox_post;
|
||||
use users::users_get;
|
||||
use webfinger::webfinger_get;
|
||||
|
||||
const HOST: &'static str = "testing4host.com";
|
||||
const USER: &'static str = "test71";
|
||||
|
||||
async fn fallback(uri: Uri) -> (StatusCode, String) {
|
||||
println!("Unknown url! {}", uri);
|
||||
(StatusCode::NOT_FOUND, format!("No route for {uri}"))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(root))
|
||||
.route("/.well-known/webfinger", get(webfinger_get))
|
||||
.route("/users/{users}/", get(users_get))
|
||||
.route("/users/{users}", get(users_get))
|
||||
.route("/inbox/{users}/", post(inbox_post))
|
||||
.fallback(fallback);
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
|
||||
async fn root() -> &'static str {
|
||||
"Hello World!"
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
use axum::{extract::Path, response::Response};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::USER;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct UsersPublicKey {
|
||||
id: String,
|
||||
owner: String,
|
||||
#[serde(rename = "publicKeyPem")]
|
||||
public_key_pem: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct UsersResponse {
|
||||
#[serde(rename = "@context")]
|
||||
context: Vec<String>,
|
||||
r#type: String,
|
||||
name: String,
|
||||
id: String,
|
||||
inbox: String,
|
||||
outbox: String,
|
||||
following: String,
|
||||
followers: String,
|
||||
summary: String,
|
||||
url: String,
|
||||
published: String,
|
||||
tag: Vec<String>,
|
||||
attachment: Vec<String>,
|
||||
icon: String,
|
||||
#[serde(rename = "preferredUsername")]
|
||||
preferred_username: String,
|
||||
#[serde(rename = "publicKey")]
|
||||
public_key: UsersPublicKey,
|
||||
}
|
||||
|
||||
pub async fn users_get(Path(user): Path<String>) -> Response {
|
||||
println!("users: {}", user);
|
||||
assert_eq!(user, USER);
|
||||
|
||||
Response::builder()
|
||||
.header("content-type", "application/activity+json; charset=utf-8")
|
||||
.body(
|
||||
serde_json::to_string(&UsersResponse {
|
||||
context: vec!["https://www.w3.org/ns/activitystreams".to_string(), "https://w3id.org/security/v1".to_string()],
|
||||
r#type: "Person".to_string(),
|
||||
name: "Testing Slasher".to_string(),
|
||||
id: "https://testing4host.com/users/test71/".to_string(),
|
||||
inbox: "https://testing4host.com/inbox/test71/".to_string(),
|
||||
outbox: "https://testing4host.com/outbox/test71/".to_string(),
|
||||
followers: "https://testing4host.com/followers/test71/".to_string(),
|
||||
following: "https://testing4host.com/following/test71/".to_string(),
|
||||
summary: "Hi!".to_string(),
|
||||
url: "https://testing4host.com/@test71/".to_string(),
|
||||
published: "2024-07-13T00:00:00Z".to_string(),
|
||||
tag: vec![],
|
||||
attachment: vec![],
|
||||
icon: "https://testing4host.com/avatar/test71".to_string(),
|
||||
preferred_username: "test71".to_string(),
|
||||
public_key: UsersPublicKey {
|
||||
id: "https://testing4host.com/users/test71/#main-key".to_string(),
|
||||
owner: "https://testing4host.com/users/test71/".to_string(),
|
||||
public_key_pem: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuqubzHLVItM9522mUYSd\nFkpPs0GQalcXVKBFgBCz611fpRzKS5xSGQ8BUVSn/uGkYnOxnh1j41Nk59VUQqCe\nj6d2PUnASbQHc7hcUAPwsgXxZ2LR0z5YTflNIpymd5q3hMqv7NVf05LQnm1t+bbv\nIhiH382J70Ob5FYcae4Shtp5xeBA8IzD9Wo2DwNjH3oP83fybHIEOSofccXB+DI+\np6DZPg2E2/DPpXqVMTmwurlIYX/9jYKYALE9LwX0jY/60lRYkzag9Gr57nlEZH2g\nOOZiy2JmTSvwSWc2LfQyU789w/+b5x9E+lJl1uA4g5HN9F+nEadLRjeMIKyWFdq7\nywIDAQAB\n-----END PUBLIC KEY-----\n".to_string() }
|
||||
})
|
||||
.unwrap()
|
||||
.into(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
use axum::extract::Query;
|
||||
use axum::response::Response;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{HOST, USER};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WebfingerQuery {
|
||||
resource: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct WebfingerResponse {
|
||||
subject: String,
|
||||
alias: Vec<String>,
|
||||
links: Vec<WebfigerLink>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct WebfigerLink {
|
||||
rel: String,
|
||||
r#type: String,
|
||||
href: String,
|
||||
}
|
||||
|
||||
pub async fn webfinger_get(Query(WebfingerQuery { resource }): Query<WebfingerQuery>) -> Response {
|
||||
println!("webfinger: {}", resource);
|
||||
let Some((typ, val)) = resource.split_once(':') else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!(typ, "acct");
|
||||
let Some((usr, dom)) = val.split_once('@') else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!(usr, USER);
|
||||
assert_eq!(dom, HOST);
|
||||
Response::builder()
|
||||
.header("content-type", "application/jrd+json; charset=utf-8")
|
||||
.body(
|
||||
serde_json::to_string(&WebfingerResponse {
|
||||
subject: format!("acct:{USER}@{HOST}"),
|
||||
alias: vec![format!("https://{HOST}/users/{USER}/")],
|
||||
links: vec![WebfigerLink {
|
||||
rel: "self".to_string(),
|
||||
r#type: "application/activity+json".to_string(),
|
||||
href: format!("https://{HOST}/users/{USER}/"),
|
||||
}],
|
||||
})
|
||||
.unwrap()
|
||||
.into(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
Loading…
Reference in new issue