mirror of
https://github.com/nullishamy/ferri.git
synced 2025-04-29 20:29:23 +00:00
feat: logging; fixes; error handloing
This commit is contained in:
parent
9c7c2858cc
commit
3719fae102
18 changed files with 228 additions and 106 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
@ -1264,6 +1264,8 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
@ -2161,6 +2163,8 @@ dependencies = [
|
||||||
"rocket",
|
"rocket",
|
||||||
"rocket_db_pools",
|
"rocket_db_pools",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
@ -2740,6 +2744,16 @@ dependencies = [
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-serde"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-subscriber"
|
name = "tracing-subscriber"
|
||||||
version = "0.3.19"
|
version = "0.3.19"
|
||||||
|
@ -2750,12 +2764,15 @@ dependencies = [
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"regex",
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"sharded-slab",
|
"sharded-slab",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log",
|
"tracing-log",
|
||||||
|
"tracing-serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -9,4 +9,10 @@ rocket = { version = "0.5.1", features = ["json"] }
|
||||||
sqlx = { version = "0.7", features = [ "runtime-tokio", "sqlite", "macros" ], default-features = false }
|
sqlx = { version = "0.7", features = [ "runtime-tokio", "sqlite", "macros" ], default-features = false }
|
||||||
uuid = { version = "1.16.0", features = ["v4"] }
|
uuid = { version = "1.16.0", features = ["v4"] }
|
||||||
chrono = "0.4.40"
|
chrono = "0.4.40"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
|
thiserror = "2.0.12"
|
||||||
|
|
||||||
|
tracing = "0.1.40"
|
||||||
|
tracing-appender = "0.2.3"
|
||||||
|
tracing-log = "0.2.0"
|
||||||
|
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "json", "registry", "smallvec"] }
|
|
@ -5,13 +5,15 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = "1.0.140"
|
|
||||||
sqlx = { workspace = true }
|
sqlx = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
|
rand = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
rsa = { version = "0.9.8", features = ["sha2"] }
|
rsa = { version = "0.9.8", features = ["sha2"] }
|
||||||
rand = { workspace = true }
|
|
||||||
url = "2.5.4"
|
url = "2.5.4"
|
||||||
|
serde_json = "1.0.140"
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::ap::{Actor, User, http};
|
use crate::ap::{Actor, User, http};
|
||||||
use chrono::{DateTime, Local};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::Sqlite;
|
use sqlx::Sqlite;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use tracing::{event, Level};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ActivityType {
|
pub enum ActivityType {
|
||||||
|
@ -28,7 +29,7 @@ pub struct Activity<T: Serialize + Debug> {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub ty: ActivityType,
|
pub ty: ActivityType,
|
||||||
pub object: T,
|
pub object: T,
|
||||||
pub published: DateTime<Local>,
|
pub published: DateTime<Utc>,
|
||||||
pub to: Vec<String>,
|
pub to: Vec<String>,
|
||||||
pub cc: Vec<String>,
|
pub cc: Vec<String>,
|
||||||
}
|
}
|
||||||
|
@ -39,7 +40,7 @@ impl<T: Serialize + Debug + Default> Default for Activity<T> {
|
||||||
id: Default::default(),
|
id: Default::default(),
|
||||||
ty: ActivityType::Unknown,
|
ty: ActivityType::Unknown,
|
||||||
object: Default::default(),
|
object: Default::default(),
|
||||||
published: Local::now(),
|
published: Utc::now(),
|
||||||
to: Default::default(),
|
to: Default::default(),
|
||||||
cc: Default::default(),
|
cc: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -102,19 +103,18 @@ impl<'a> Outbox<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn post<T: Serialize + Debug>(&self, activity: OutgoingActivity<T>) {
|
pub async fn post<T: Serialize + Debug>(&self, activity: OutgoingActivity<T>) {
|
||||||
dbg!(&activity);
|
event!(Level::INFO, ?activity, "activity in outbox");
|
||||||
|
|
||||||
let raw = RawActivity {
|
let raw = RawActivity {
|
||||||
context: "https://www.w3.org/ns/activitystreams".to_string(),
|
context: "https://www.w3.org/ns/activitystreams".to_string(),
|
||||||
id: activity.req.id,
|
id: activity.req.id.clone(),
|
||||||
ty: activity.req.ty.to_raw(),
|
ty: activity.req.ty.to_raw(),
|
||||||
actor: self.user.actor().id().to_string(),
|
actor: self.user.actor().id().to_string(),
|
||||||
object: activity.req.object,
|
object: activity.req.object,
|
||||||
published: activity.req.published.to_rfc3339(),
|
published: activity.req.published.to_rfc3339(),
|
||||||
};
|
};
|
||||||
|
|
||||||
dbg!(&raw);
|
let outbox_res = self
|
||||||
|
|
||||||
let follow_res = self
|
|
||||||
.transport
|
.transport
|
||||||
.post(activity.to.inbox())
|
.post(activity.to.inbox())
|
||||||
.activity()
|
.activity()
|
||||||
|
@ -127,7 +127,10 @@ impl<'a> Outbox<'a> {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
dbg!(follow_res);
|
event!(Level::DEBUG,
|
||||||
|
outbox_res, activity = activity.req.id,
|
||||||
|
"got response for outbox dispatch"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_user(user: User, transport: &'a OutboxTransport) -> Outbox<'a> {
|
pub fn for_user(user: User, transport: &'a OutboxTransport) -> Outbox<'a> {
|
||||||
|
|
|
@ -12,6 +12,7 @@ use rsa::{
|
||||||
|
|
||||||
use base64::prelude::*;
|
use base64::prelude::*;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
use tracing::{event, Level};
|
||||||
|
|
||||||
pub struct HttpClient {
|
pub struct HttpClient {
|
||||||
client: reqwest::Client,
|
client: reqwest::Client,
|
||||||
|
@ -59,7 +60,8 @@ impl RequestBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send(self) -> Result<Response, reqwest::Error> {
|
pub async fn send(self) -> Result<Response, reqwest::Error> {
|
||||||
dbg!(&self.inner);
|
event!(Level::DEBUG, ?self.inner, "sending an http request");
|
||||||
|
|
||||||
self.inner.send().await
|
self.inner.send().await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub use user::*;
|
||||||
mod post;
|
mod post;
|
||||||
pub use post::*;
|
pub use post::*;
|
||||||
|
|
||||||
pub const AS_CONTEXT: &'static str = "https://www.w3.org/ns/activitystreams";
|
pub const AS_CONTEXT: &str = "https://www.w3.org/ns/activitystreams";
|
||||||
|
|
||||||
pub fn new_id() -> String {
|
pub fn new_id() -> String {
|
||||||
Uuid::new_v4().to_string()
|
Uuid::new_v4().to_string()
|
||||||
|
|
|
@ -3,7 +3,7 @@ use chrono::{DateTime, Utc};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sqlx::Sqlite;
|
use sqlx::Sqlite;
|
||||||
|
|
||||||
const POST_TYPE: &'static str = "Post";
|
const POST_TYPE: &str = "Post";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Post {
|
pub struct Post {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use sqlx::Sqlite;
|
use sqlx::Sqlite;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Actor {
|
pub struct Actor {
|
||||||
|
@ -20,6 +21,10 @@ impl Actor {
|
||||||
pub fn inbox(&self) -> &str {
|
pub fn inbox(&self) -> &str {
|
||||||
&self.inbox
|
&self.inbox
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn outbox(&self) -> &str {
|
||||||
|
&self.outbox
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -30,6 +35,13 @@ pub struct User {
|
||||||
display_name: String,
|
display_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum UserError {
|
||||||
|
#[error("user `{0}` not found")]
|
||||||
|
NotFound(String),
|
||||||
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub fn id(&self) -> &str {
|
pub fn id(&self) -> &str {
|
||||||
&self.id
|
&self.id
|
||||||
|
@ -55,7 +67,7 @@ impl User {
|
||||||
format!("https://ferri.amy.mov/users/{}", self.id())
|
format!("https://ferri.amy.mov/users/{}", self.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn from_id(uuid: &str, conn: impl sqlx::Executor<'_, Database = Sqlite>) -> User {
|
pub async fn from_id(uuid: &str, conn: impl sqlx::Executor<'_, Database = Sqlite>) -> Result<User, UserError> {
|
||||||
let user = sqlx::query!(
|
let user = sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
SELECT u.*, a.id as "actor_own_id", a.inbox, a.outbox
|
SELECT u.*, a.id as "actor_own_id", a.inbox, a.outbox
|
||||||
|
@ -65,10 +77,11 @@ impl User {
|
||||||
"#,
|
"#,
|
||||||
uuid
|
uuid
|
||||||
)
|
)
|
||||||
.fetch_one(conn)
|
.fetch_one(conn)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.map_err(|_| UserError::NotFound(uuid.to_string()))?;
|
||||||
User {
|
|
||||||
|
Ok(User {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
actor: Actor {
|
actor: Actor {
|
||||||
|
@ -77,7 +90,7 @@ impl User {
|
||||||
outbox: user.outbox,
|
outbox: user.outbox,
|
||||||
},
|
},
|
||||||
display_name: user.display_name,
|
display_name: user.display_name,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn from_username(
|
pub async fn from_username(
|
||||||
|
|
|
@ -5,12 +5,14 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
main = { path = "../ferri-main/" }
|
main = { path = "../ferri-main/" }
|
||||||
rocket = { workspace = true }
|
|
||||||
rocket_db_pools = { version = "0.2.0", features = ["sqlx_sqlite"] }
|
rocket_db_pools = { version = "0.2.0", features = ["sqlx_sqlite"] }
|
||||||
|
url = "2.5.4"
|
||||||
|
|
||||||
|
rocket = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
sqlx = { workspace = true }
|
sqlx = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
rand = { workspace = true }
|
rand = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
url = "2.5.4"
|
tracing-subscriber = { workspace = true }
|
|
@ -1,17 +1,17 @@
|
||||||
use crate::timeline::TimelineStatus;
|
use crate::timeline::TimelineStatus;
|
||||||
use chrono::Local;
|
|
||||||
use main::ap::{self, http::HttpClient};
|
use main::ap::{self, http::HttpClient};
|
||||||
use rocket::{
|
use rocket::{
|
||||||
FromForm, State,
|
FromForm, State,
|
||||||
form::Form,
|
form::Form,
|
||||||
post,
|
post,
|
||||||
|
get,
|
||||||
serde::{Deserialize, Serialize, json::Json},
|
serde::{Deserialize, Serialize, json::Json},
|
||||||
};
|
};
|
||||||
use rocket_db_pools::Connection;
|
use rocket_db_pools::Connection;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::api::user::CredentialAcount;
|
use crate::api::user::CredentialAcount;
|
||||||
use crate::{AuthenticatedUser, Db, types::content};
|
use crate::{AuthenticatedUser, Db};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, FromForm)]
|
#[derive(Serialize, Deserialize, Debug, FromForm)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
|
@ -19,17 +19,36 @@ pub struct Status {
|
||||||
status: String,
|
status: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, FromForm)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct StatusContext {
|
||||||
|
ancestors: Vec<Status>,
|
||||||
|
descendants: Vec<Status>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/statuses/<status>/context")]
|
||||||
|
pub async fn status_context(
|
||||||
|
status: &str,
|
||||||
|
user: AuthenticatedUser,
|
||||||
|
mut db: Connection<Db>
|
||||||
|
) -> Json<StatusContext> {
|
||||||
|
Json(StatusContext {
|
||||||
|
ancestors: vec![],
|
||||||
|
descendants: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async fn create_status(
|
async fn create_status(
|
||||||
user: AuthenticatedUser,
|
user: AuthenticatedUser,
|
||||||
mut db: Connection<Db>,
|
mut db: Connection<Db>,
|
||||||
http: &HttpClient,
|
http: &HttpClient,
|
||||||
status: &Status,
|
status: &Status,
|
||||||
) -> TimelineStatus {
|
) -> TimelineStatus {
|
||||||
let user = ap::User::from_id(&user.username, &mut **db).await;
|
let user = ap::User::from_id(&user.id, &mut **db).await.unwrap();
|
||||||
let outbox = ap::Outbox::for_user(user.clone(), http);
|
let outbox = ap::Outbox::for_user(user.clone(), http);
|
||||||
|
|
||||||
let post_id = ap::new_id();
|
let post_id = ap::new_id();
|
||||||
let now = ap::new_ts();
|
let now = ap::now();
|
||||||
|
|
||||||
let post = ap::Post::from_parts(post_id, status.status.clone(), user.clone())
|
let post = ap::Post::from_parts(post_id, status.status.clone(), user.clone())
|
||||||
.to(format!("{}/followers", user.uri()))
|
.to(format!("{}/followers", user.uri()))
|
||||||
|
@ -55,6 +74,7 @@ async fn create_status(
|
||||||
ty: ap::ActivityType::Create,
|
ty: ap::ActivityType::Create,
|
||||||
object: post.clone().to_ap(),
|
object: post.clone().to_ap(),
|
||||||
to: vec![format!("{}/followers", user.uri())],
|
to: vec![format!("{}/followers", user.uri())],
|
||||||
|
published: now,
|
||||||
cc: vec!["https://www.w3.org/ns/activitystreams#Public".to_string()],
|
cc: vec!["https://www.w3.org/ns/activitystreams#Public".to_string()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,7 +37,6 @@ pub async fn home(
|
||||||
limit: i64,
|
limit: i64,
|
||||||
user: AuthenticatedUser,
|
user: AuthenticatedUser,
|
||||||
) -> Json<Vec<TimelineStatus>> {
|
) -> Json<Vec<TimelineStatus>> {
|
||||||
dbg!(user);
|
|
||||||
let posts = sqlx::query!(
|
let posts = sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
SELECT p.id as "post_id", u.id as "user_id", p.content, p.uri as "post_uri",
|
SELECT p.id as "post_id", u.id as "user_id", p.content, p.uri as "post_uri",
|
||||||
|
|
|
@ -5,6 +5,7 @@ use rocket::{
|
||||||
};
|
};
|
||||||
use rocket_db_pools::Connection;
|
use rocket_db_pools::Connection;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use rocket::response::status::NotFound;
|
||||||
|
|
||||||
use crate::timeline::{TimelineAccount, TimelineStatus};
|
use crate::timeline::{TimelineAccount, TimelineStatus};
|
||||||
use crate::{AuthenticatedUser, Db, http::HttpClient};
|
use crate::{AuthenticatedUser, Db, http::HttpClient};
|
||||||
|
@ -62,9 +63,12 @@ pub async fn new_follow(
|
||||||
http: &State<HttpClient>,
|
http: &State<HttpClient>,
|
||||||
uuid: &str,
|
uuid: &str,
|
||||||
user: AuthenticatedUser,
|
user: AuthenticatedUser,
|
||||||
) {
|
) -> Result<(), NotFound<String>> {
|
||||||
let follower = ap::User::from_actor_id(&user.actor_id, &mut **db).await;
|
let follower = ap::User::from_actor_id(&user.actor_id, &mut **db).await;
|
||||||
let followed = ap::User::from_id(uuid, &mut **db).await;
|
|
||||||
|
let followed = ap::User::from_id(uuid, &mut **db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| NotFound(e.to_string()))?;
|
||||||
|
|
||||||
let outbox = ap::Outbox::for_user(follower.clone(), http.inner());
|
let outbox = ap::Outbox::for_user(follower.clone(), http.inner());
|
||||||
|
|
||||||
|
@ -83,6 +87,8 @@ pub async fn new_follow(
|
||||||
|
|
||||||
req.save(&mut **db).await;
|
req.save(&mut **db).await;
|
||||||
outbox.post(req).await;
|
outbox.post(req).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/accounts/<uuid>")]
|
#[get("/accounts/<uuid>")]
|
||||||
|
@ -90,10 +96,12 @@ pub async fn account(
|
||||||
mut db: Connection<Db>,
|
mut db: Connection<Db>,
|
||||||
uuid: &str,
|
uuid: &str,
|
||||||
user: AuthenticatedUser,
|
user: AuthenticatedUser,
|
||||||
) -> Json<TimelineAccount> {
|
) -> Result<Json<TimelineAccount>, NotFound<String>> {
|
||||||
let user = ap::User::from_id(uuid, &mut **db).await;
|
let user = ap::User::from_id(uuid, &mut **db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| NotFound(e.to_string()))?;
|
||||||
let user_uri = format!("https://ferri.amy.mov/users/{}", user.username());
|
let user_uri = format!("https://ferri.amy.mov/users/{}", user.username());
|
||||||
Json(CredentialAcount {
|
Ok(Json(CredentialAcount {
|
||||||
id: user.id().to_string(),
|
id: user.id().to_string(),
|
||||||
username: user.username().to_string(),
|
username: user.username().to_string(),
|
||||||
acct: user.username().to_string(),
|
acct: user.username().to_string(),
|
||||||
|
@ -112,7 +120,7 @@ pub async fn account(
|
||||||
following_count: 1,
|
following_count: 1,
|
||||||
statuses_count: 1,
|
statuses_count: 1,
|
||||||
last_status_at: "2025-04-10T22:14:34Z".to_string(),
|
last_status_at: "2025-04-10T22:14:34Z".to_string(),
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/accounts/<uuid>/statuses?<limit>")]
|
#[get("/accounts/<uuid>/statuses?<limit>")]
|
||||||
|
@ -121,8 +129,10 @@ pub async fn statuses(
|
||||||
uuid: &str,
|
uuid: &str,
|
||||||
limit: Option<i64>,
|
limit: Option<i64>,
|
||||||
user: AuthenticatedUser,
|
user: AuthenticatedUser,
|
||||||
) -> Json<Vec<TimelineStatus>> {
|
) -> Result<Json<Vec<TimelineStatus>>, NotFound<String>> {
|
||||||
let user = ap::User::from_id(uuid, &mut **db).await;
|
let user = ap::User::from_id(uuid, &mut **db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| NotFound(e.to_string()))?;
|
||||||
|
|
||||||
let uid = user.id();
|
let uid = user.id();
|
||||||
let posts = sqlx::query!(
|
let posts = sqlx::query!(
|
||||||
|
@ -181,5 +191,5 @@ pub async fn statuses(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Json(out)
|
Ok(Json(out))
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,5 @@ pub async fn test(http: &State<HttpClient>) -> &'static str {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
dbg!(follow);
|
|
||||||
|
|
||||||
"Hello, world!"
|
"Hello, world!"
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ use rocket_db_pools::Connection;
|
||||||
use sqlx::Sqlite;
|
use sqlx::Sqlite;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use tracing::{event, span, Level, debug, warn, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Db,
|
Db,
|
||||||
|
@ -14,12 +15,12 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
fn handle_delete_activity(activity: activity::DeleteActivity) {
|
fn handle_delete_activity(activity: activity::DeleteActivity) {
|
||||||
dbg!(activity);
|
warn!(?activity, "unimplemented delete activity");
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_actor(
|
async fn create_actor(
|
||||||
user: &Person,
|
user: &Person,
|
||||||
actor: String,
|
actor: &str,
|
||||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
||||||
) {
|
) {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
|
@ -39,7 +40,7 @@ async fn create_actor(
|
||||||
|
|
||||||
async fn create_user(
|
async fn create_user(
|
||||||
user: &Person,
|
user: &Person,
|
||||||
actor: String,
|
actor: &str,
|
||||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
||||||
) {
|
) {
|
||||||
// HACK: Allow us to formulate a `user@host` username by assuming the actor is on the same host as the user
|
// HACK: Allow us to formulate a `user@host` username by assuming the actor is on the same host as the user
|
||||||
|
@ -84,7 +85,7 @@ async fn create_follow(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_follow_activity(
|
async fn handle_follow_activity(
|
||||||
followed_account: String,
|
followed_account: &str,
|
||||||
activity: activity::FollowActivity,
|
activity: activity::FollowActivity,
|
||||||
http: &HttpClient,
|
http: &HttpClient,
|
||||||
mut db: Connection<Db>,
|
mut db: Connection<Db>,
|
||||||
|
@ -99,8 +100,8 @@ async fn handle_follow_activity(
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
create_actor(&user, activity.actor.clone(), &mut **db).await;
|
create_actor(&user, &activity.actor, &mut **db).await;
|
||||||
create_user(&user, activity.actor.clone(), &mut **db).await;
|
create_user(&user, &activity.actor, &mut **db).await;
|
||||||
create_follow(&activity, &mut **db).await;
|
create_follow(&activity, &mut **db).await;
|
||||||
|
|
||||||
let follower = ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
let follower = ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
||||||
|
@ -128,11 +129,17 @@ async fn handle_follow_activity(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_like_activity(activity: activity::LikeActivity, mut db: Connection<Db>) {
|
async fn handle_like_activity(activity: activity::LikeActivity, mut db: Connection<Db>) {
|
||||||
|
warn!(?activity, "unimplemented like activity");
|
||||||
|
|
||||||
let target_post = sqlx::query!("SELECT * FROM post WHERE uri = ?1", activity.object)
|
let target_post = sqlx::query!("SELECT * FROM post WHERE uri = ?1", activity.object)
|
||||||
.fetch_one(&mut **db)
|
.fetch_one(&mut **db)
|
||||||
.await
|
.await;
|
||||||
.unwrap();
|
|
||||||
dbg!(&target_post);
|
if let Ok(post) = target_post {
|
||||||
|
warn!(?post, "tried to like post");
|
||||||
|
} else {
|
||||||
|
warn!(post = ?activity.object, "could not find post");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_create_activity(
|
async fn handle_create_activity(
|
||||||
|
@ -141,6 +148,8 @@ async fn handle_create_activity(
|
||||||
mut db: Connection<Db>,
|
mut db: Connection<Db>,
|
||||||
) {
|
) {
|
||||||
assert!(&activity.object.ty == "Note");
|
assert!(&activity.object.ty == "Note");
|
||||||
|
debug!("resolving user {}", activity.actor);
|
||||||
|
|
||||||
let user = http
|
let user = http
|
||||||
.get(&activity.actor)
|
.get(&activity.actor)
|
||||||
.activity()
|
.activity()
|
||||||
|
@ -151,10 +160,14 @@ async fn handle_create_activity(
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
create_actor(&user, activity.actor.clone(), &mut **db).await;
|
debug!("creating actor {}", activity.actor);
|
||||||
create_user(&user, activity.actor.clone(), &mut **db).await;
|
create_actor(&user, &activity.actor, &mut **db).await;
|
||||||
|
|
||||||
|
debug!("creating user {}", activity.actor);
|
||||||
|
create_user(&user, &activity.actor, &mut **db).await;
|
||||||
|
|
||||||
let user = ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
let user = ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
||||||
|
debug!("user created {:?}", user);
|
||||||
|
|
||||||
let user_id = user.id();
|
let user_id = user.id();
|
||||||
let now = Local::now().to_rfc3339();
|
let now = Local::now().to_rfc3339();
|
||||||
|
@ -162,6 +175,8 @@ async fn handle_create_activity(
|
||||||
let post_id = Uuid::new_v4().to_string();
|
let post_id = Uuid::new_v4().to_string();
|
||||||
let uri = activity.id;
|
let uri = activity.id;
|
||||||
|
|
||||||
|
info!(post_id, "creating post");
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO post (id, uri, user_id, content, created_at)
|
INSERT INTO post (id, uri, user_id, content, created_at)
|
||||||
|
@ -173,14 +188,19 @@ async fn handle_create_activity(
|
||||||
content,
|
content,
|
||||||
now
|
now
|
||||||
)
|
)
|
||||||
.execute(&mut **db)
|
.execute(&mut **db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/users/<user>/inbox", data = "<body>")]
|
#[post("/users/<user>/inbox", data = "<body>")]
|
||||||
pub async fn inbox(db: Connection<Db>, http: &State<HttpClient>, user: String, body: String) {
|
pub async fn inbox(db: Connection<Db>, http: &State<HttpClient>, user: &str, body: String) {
|
||||||
let min = serde_json::from_str::<activity::MinimalActivity>(&body).unwrap();
|
let min = serde_json::from_str::<activity::MinimalActivity>(&body).unwrap();
|
||||||
|
let inbox_span = span!(Level::INFO, "inbox-post", user_id = user);
|
||||||
|
|
||||||
|
let _enter = inbox_span.enter();
|
||||||
|
event!(Level::INFO, ?min, "received an activity");
|
||||||
|
|
||||||
match min.ty.as_str() {
|
match min.ty.as_str() {
|
||||||
"Delete" => {
|
"Delete" => {
|
||||||
let activity = serde_json::from_str::<activity::DeleteActivity>(&body).unwrap();
|
let activity = serde_json::from_str::<activity::DeleteActivity>(&body).unwrap();
|
||||||
|
@ -198,11 +218,11 @@ pub async fn inbox(db: Connection<Db>, http: &State<HttpClient>, user: String, b
|
||||||
let activity = serde_json::from_str::<activity::LikeActivity>(&body).unwrap();
|
let activity = serde_json::from_str::<activity::LikeActivity>(&body).unwrap();
|
||||||
handle_like_activity(activity, db).await;
|
handle_like_activity(activity, db).await;
|
||||||
}
|
}
|
||||||
unknown => {
|
act => {
|
||||||
eprintln!("WARN: Unknown activity '{}' - {}", unknown, body);
|
warn!(act, body, "unknown activity");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg!(min);
|
debug!("body in inbox: {}", body);
|
||||||
println!("Body in inbox: {}", body);
|
drop(_enter)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,10 @@ pub async fn authorize(
|
||||||
// This will act as a token for the user, but we will in future say that it expires very shortly
|
// This will act as a token for the user, but we will in future say that it expires very shortly
|
||||||
// and can only be used for obtaining an access token etc
|
// and can only be used for obtaining an access token etc
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO auth (token, user_id)
|
INSERT INTO auth (token, user_id)
|
||||||
VALUES (?1, ?2)
|
VALUES (?1, ?2)
|
||||||
"#,
|
"#,
|
||||||
code,
|
code,
|
||||||
user_id
|
user_id
|
||||||
)
|
)
|
||||||
|
@ -35,7 +35,7 @@ pub async fn authorize(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let id_token = main::gen_token(10);
|
let id_token = main::gen_token(10);
|
||||||
|
|
||||||
// Add an oauth entry for the `code` which /oauth/token will rewrite
|
// Add an oauth entry for the `code` which /oauth/token will rewrite
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
|
@ -67,7 +67,7 @@ pub struct Token {
|
||||||
|
|
||||||
#[derive(Deserialize, Debug, FromForm)]
|
#[derive(Deserialize, Debug, FromForm)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
struct NewTokenRequest {
|
pub struct NewTokenRequest {
|
||||||
client_id: String,
|
client_id: String,
|
||||||
redirect_uri: String,
|
redirect_uri: String,
|
||||||
grant_type: String,
|
grant_type: String,
|
||||||
|
@ -77,19 +77,15 @@ struct NewTokenRequest {
|
||||||
|
|
||||||
#[post("/oauth/token", data = "<req>")]
|
#[post("/oauth/token", data = "<req>")]
|
||||||
pub async fn new_token(req: Form<NewTokenRequest>, mut db: Connection<Db>) -> Json<Token> {
|
pub async fn new_token(req: Form<NewTokenRequest>, mut db: Connection<Db>) -> Json<Token> {
|
||||||
let oauth = sqlx::query!(
|
let oauth = sqlx::query!("
|
||||||
r#"
|
|
||||||
SELECT o.*, a.*
|
SELECT o.*, a.*
|
||||||
FROM oauth o
|
FROM oauth o
|
||||||
INNER JOIN auth a ON a.token = ?2
|
INNER JOIN auth a ON a.token = ?2
|
||||||
WHERE o.access_token = ?1
|
WHERE o.access_token = ?1
|
||||||
"#,
|
", req.code, req.code)
|
||||||
req.code,
|
.fetch_one(&mut **db)
|
||||||
req.code
|
.await
|
||||||
)
|
.unwrap();
|
||||||
.fetch_one(&mut **db)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let access_token = main::gen_token(15);
|
let access_token = main::gen_token(15);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use main::ap;
|
use main::ap;
|
||||||
use rocket::{get, http::ContentType, serde::json::Json};
|
use rocket::{get, http::ContentType, serde::json::Json};
|
||||||
use rocket_db_pools::Connection;
|
use rocket_db_pools::Connection;
|
||||||
|
use rocket::response::status::NotFound;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Db,
|
Db,
|
||||||
|
@ -20,7 +21,6 @@ pub async fn inbox(user: String) -> Json<OrderedCollection> {
|
||||||
|
|
||||||
#[get("/users/<user>/outbox")]
|
#[get("/users/<user>/outbox")]
|
||||||
pub async fn outbox(user: String) -> Json<OrderedCollection> {
|
pub async fn outbox(user: String) -> Json<OrderedCollection> {
|
||||||
dbg!(&user);
|
|
||||||
Json(OrderedCollection {
|
Json(OrderedCollection {
|
||||||
ty: "OrderedCollection".to_string(),
|
ty: "OrderedCollection".to_string(),
|
||||||
total_items: 0,
|
total_items: 0,
|
||||||
|
@ -29,8 +29,11 @@ pub async fn outbox(user: String) -> Json<OrderedCollection> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/users/<uuid>/followers")]
|
#[get("/users/<uuid>/followers")]
|
||||||
pub async fn followers(mut db: Connection<Db>, uuid: &str) -> Json<OrderedCollection> {
|
pub async fn followers(mut db: Connection<Db>, uuid: &str) -> Result<Json<OrderedCollection>, NotFound<String>> {
|
||||||
let target = ap::User::from_id(uuid, &mut **db).await;
|
let target = ap::User::from_id(uuid, &mut **db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| NotFound(e.to_string()))?;
|
||||||
|
|
||||||
let actor_id = target.actor_id();
|
let actor_id = target.actor_id();
|
||||||
|
|
||||||
let followers = sqlx::query!(
|
let followers = sqlx::query!(
|
||||||
|
@ -44,19 +47,22 @@ pub async fn followers(mut db: Connection<Db>, uuid: &str) -> Json<OrderedCollec
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Json(OrderedCollection {
|
Ok(Json(OrderedCollection {
|
||||||
ty: "OrderedCollection".to_string(),
|
ty: "OrderedCollection".to_string(),
|
||||||
total_items: 1,
|
total_items: 1,
|
||||||
ordered_items: followers
|
ordered_items: followers
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|f| f.follower_id)
|
.map(|f| f.follower_id)
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/users/<uuid>/following")]
|
#[get("/users/<uuid>/following")]
|
||||||
pub async fn following(mut db: Connection<Db>, uuid: &str) -> Json<OrderedCollection> {
|
pub async fn following(mut db: Connection<Db>, uuid: &str) -> Result<Json<OrderedCollection>, NotFound<String>> {
|
||||||
let target = ap::User::from_id(uuid, &mut **db).await;
|
let target = ap::User::from_id(uuid, &mut **db)
|
||||||
|
.await
|
||||||
|
.map_err(|e| NotFound(e.to_string()))?;
|
||||||
|
|
||||||
let actor_id = target.actor_id();
|
let actor_id = target.actor_id();
|
||||||
|
|
||||||
let following = sqlx::query!(
|
let following = sqlx::query!(
|
||||||
|
@ -70,14 +76,14 @@ pub async fn following(mut db: Connection<Db>, uuid: &str) -> Json<OrderedCollec
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Json(OrderedCollection {
|
Ok(Json(OrderedCollection {
|
||||||
ty: "OrderedCollection".to_string(),
|
ty: "OrderedCollection".to_string(),
|
||||||
total_items: 1,
|
total_items: 1,
|
||||||
ordered_items: following
|
ordered_items: following
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|f| f.followed_id)
|
.map(|f| f.followed_id)
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/users/<uuid>/posts/<post>")]
|
#[get("/users/<uuid>/posts/<post>")]
|
||||||
|
@ -111,14 +117,17 @@ pub async fn post(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/users/<uuid>")]
|
#[get("/users/<uuid>")]
|
||||||
pub async fn user(mut db: Connection<Db>, uuid: &str) -> (ContentType, Json<Person>) {
|
pub async fn user(mut db: Connection<Db>, uuid: &str) -> Result<(ContentType, Json<Person>), NotFound<String>> {
|
||||||
let user = ap::User::from_id(uuid, &mut **db).await;
|
let user = ap::User::from_id(uuid, &mut **db)
|
||||||
(
|
.await
|
||||||
|
.map_err(|e| NotFound(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
activity_type(),
|
activity_type(),
|
||||||
Json(Person {
|
Json(Person {
|
||||||
context: "https://www.w3.org/ns/activitystreams".to_string(),
|
context: "https://www.w3.org/ns/activitystreams".to_string(),
|
||||||
ty: "Person".to_string(),
|
ty: "Person".to_string(),
|
||||||
id: user.id().to_string(),
|
id: format!("https://ferri.amy.mov/users/{}", user.id()),
|
||||||
name: user.username().to_string(),
|
name: user.username().to_string(),
|
||||||
preferred_username: user.display_name().to_string(),
|
preferred_username: user.display_name().to_string(),
|
||||||
followers: format!("https://ferri.amy.mov/users/{}/followers", uuid),
|
followers: format!("https://ferri.amy.mov/users/{}/followers", uuid),
|
||||||
|
@ -132,5 +141,5 @@ pub async fn user(mut db: Connection<Db>, uuid: &str) -> (ContentType, Json<Pers
|
||||||
public_key: include_str!("../../../public.pem").to_string(),
|
public_key: include_str!("../../../public.pem").to_string(),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use main::ap;
|
use main::ap;
|
||||||
use rocket::{get, serde::json::Json};
|
use rocket::{get, serde::json::Json};
|
||||||
use rocket_db_pools::Connection;
|
use rocket_db_pools::Connection;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Db,
|
Db,
|
||||||
|
@ -20,7 +21,8 @@ pub async fn host_meta() -> &'static str {
|
||||||
// https://mastodon.social/.well-known/webfinger?resource=acct:gargron@mastodon.social
|
// https://mastodon.social/.well-known/webfinger?resource=acct:gargron@mastodon.social
|
||||||
#[get("/.well-known/webfinger?<resource>")]
|
#[get("/.well-known/webfinger?<resource>")]
|
||||||
pub async fn webfinger(mut db: Connection<Db>, resource: &str) -> Json<WebfingerResponse> {
|
pub async fn webfinger(mut db: Connection<Db>, resource: &str) -> Json<WebfingerResponse> {
|
||||||
println!("Webfinger request for {}", resource);
|
info!(?resource, "incoming webfinger request");
|
||||||
|
|
||||||
let acct = resource.strip_prefix("acct:").unwrap();
|
let acct = resource.strip_prefix("acct:").unwrap();
|
||||||
let (user, _) = acct.split_once("@").unwrap();
|
let (user, _) = acct.split_once("@").unwrap();
|
||||||
let user = ap::User::from_username(user, &mut **db).await;
|
let user = ap::User::from_username(user, &mut **db).await;
|
||||||
|
@ -29,13 +31,13 @@ pub async fn webfinger(mut db: Connection<Db>, resource: &str) -> Json<Webfinger
|
||||||
subject: resource.to_string(),
|
subject: resource.to_string(),
|
||||||
aliases: vec![
|
aliases: vec![
|
||||||
format!("https://ferri.amy.mov/users/{}", user.id()),
|
format!("https://ferri.amy.mov/users/{}", user.id()),
|
||||||
format!("https://ferri.amy.mov/@{}", user.username()),
|
format!("https://ferri.amy.mov/{}", user.username()),
|
||||||
],
|
],
|
||||||
links: vec![
|
links: vec![
|
||||||
Link {
|
Link {
|
||||||
rel: "http://webfinger.net/rel/profile-page".to_string(),
|
rel: "http://webfinger.net/rel/profile-page".to_string(),
|
||||||
ty: Some("text/html".to_string()),
|
ty: Some("text/html".to_string()),
|
||||||
href: Some(format!("https://ferri.amy.mov/@{}", user.username())),
|
href: Some(format!("https://ferri.amy.mov/{}", user.username())),
|
||||||
},
|
},
|
||||||
Link {
|
Link {
|
||||||
rel: "self".to_string(),
|
rel: "self".to_string(),
|
||||||
|
|
|
@ -3,12 +3,14 @@ use endpoints::{
|
||||||
custom, inbox, oauth, user, well_known,
|
custom, inbox, oauth, user, well_known,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use tracing::Level;
|
||||||
|
use tracing_subscriber::fmt;
|
||||||
|
|
||||||
use main::ap::http;
|
use main::ap::http;
|
||||||
use main::config::Config;
|
use main::config::Config;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
Build, Request, Rocket, build, get,
|
Build, Request, Rocket, build, get,
|
||||||
http::{ContentType, Status},
|
http::{ContentType, Status},
|
||||||
outcome::IntoOutcome,
|
|
||||||
request::{FromRequest, Outcome},
|
request::{FromRequest, Outcome},
|
||||||
routes,
|
routes,
|
||||||
};
|
};
|
||||||
|
@ -24,20 +26,20 @@ pub struct Db(sqlx::SqlitePool);
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
async fn user_profile(cfg: &rocket::State<Config>) -> (ContentType, &'static str) {
|
async fn user_profile(cfg: &rocket::State<Config>) -> (ContentType, &'static str) {
|
||||||
dbg!(cfg);
|
|
||||||
(ContentType::HTML, "<p>hello</p>")
|
(ContentType::HTML, "<p>hello</p>")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/activities/<activity>")]
|
#[get("/activities/<activity>")]
|
||||||
async fn activity_endpoint(activity: String) {
|
async fn activity_endpoint(activity: String) {
|
||||||
dbg!(activity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct AuthenticatedUser {
|
struct AuthenticatedUser {
|
||||||
username: String,
|
pub username: String,
|
||||||
token: String,
|
pub id: String,
|
||||||
actor_id: String,
|
pub token: String,
|
||||||
|
pub actor_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -52,7 +54,7 @@ impl<'a> FromRequest<'a> for AuthenticatedUser {
|
||||||
type Error = LoginError;
|
type Error = LoginError;
|
||||||
async fn from_request(request: &'a Request<'_>) -> Outcome<AuthenticatedUser, LoginError> {
|
async fn from_request(request: &'a Request<'_>) -> Outcome<AuthenticatedUser, LoginError> {
|
||||||
let token = request.headers().get_one("Authorization");
|
let token = request.headers().get_one("Authorization");
|
||||||
|
|
||||||
if let Some(token) = token {
|
if let Some(token) = token {
|
||||||
let token = token
|
let token = token
|
||||||
.strip_prefix("Bearer")
|
.strip_prefix("Bearer")
|
||||||
|
@ -60,29 +62,49 @@ impl<'a> FromRequest<'a> for AuthenticatedUser {
|
||||||
.unwrap_or(token);
|
.unwrap_or(token);
|
||||||
|
|
||||||
let mut conn = request.guard::<Connection<Db>>().await.unwrap();
|
let mut conn = request.guard::<Connection<Db>>().await.unwrap();
|
||||||
let auth = sqlx::query!(r#"
|
let auth = sqlx::query!(
|
||||||
|
r#"
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM auth a
|
FROM auth a
|
||||||
INNER JOIN user u ON a.user_id = u.id
|
INNER JOIN user u ON a.user_id = u.id
|
||||||
WHERE token = ?1
|
WHERE token = ?1
|
||||||
"#, token)
|
"#,
|
||||||
.fetch_one(&mut **conn)
|
token
|
||||||
.await;
|
)
|
||||||
|
.fetch_one(&mut **conn)
|
||||||
|
.await;
|
||||||
|
|
||||||
if let Ok(auth) = auth {
|
if let Ok(auth) = auth {
|
||||||
return Outcome::Success(AuthenticatedUser {
|
return Outcome::Success(AuthenticatedUser {
|
||||||
token: auth.token,
|
token: auth.token,
|
||||||
|
id: auth.id,
|
||||||
username: auth.display_name,
|
username: auth.display_name,
|
||||||
actor_id: auth.actor_id,
|
actor_id: auth.actor_id,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Outcome::Forward(Status::Unauthorized)
|
Outcome::Forward(Status::Unauthorized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn launch(cfg: Config) -> Rocket<Build> {
|
pub fn launch(cfg: Config) -> Rocket<Build> {
|
||||||
|
let format = fmt::format()
|
||||||
|
.with_ansi(true)
|
||||||
|
.without_time()
|
||||||
|
.with_level(true)
|
||||||
|
.with_target(false)
|
||||||
|
.with_thread_names(false)
|
||||||
|
.with_source_location(false)
|
||||||
|
.compact();
|
||||||
|
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.event_format(format)
|
||||||
|
.with_writer(std::io::stdout)
|
||||||
|
.init();
|
||||||
|
|
||||||
let http_client = http::HttpClient::new();
|
let http_client = http::HttpClient::new();
|
||||||
build()
|
build()
|
||||||
.manage(cfg)
|
.manage(cfg)
|
||||||
|
@ -114,6 +136,7 @@ pub fn launch(cfg: Config) -> Rocket<Build> {
|
||||||
.mount(
|
.mount(
|
||||||
"/api/v1",
|
"/api/v1",
|
||||||
routes![
|
routes![
|
||||||
|
api::status::status_context,
|
||||||
api::status::new_status,
|
api::status::new_status,
|
||||||
api::status::new_status_json,
|
api::status::new_status_json,
|
||||||
api::user::new_follow,
|
api::user::new_follow,
|
||||||
|
|
Loading…
Add table
Reference in a new issue