Initial commit

master
itycodes 5 days ago
commit f90f2ee4a4

1
.gitignore vendored

@ -0,0 +1 @@
/target

3
.gitmodules vendored

@ -0,0 +1,3 @@
[submodule "NotePoster"]
path = NotePoster
url = https://gitea.itycodes.org/itycodes/NotePoster

1187
Cargo.lock generated

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…
Cancel
Save