mirror of
https://github.com/nullishamy/ferri.git
synced 2025-04-30 04:39:20 +00:00
Compare commits
No commits in common. "d252131e0da1ac51f34370c8f07c4a927c9991e1" and "d59660da3738b78a2d990a0b2f2eac7f41aadacb" have entirely different histories.
d252131e0d
...
d59660da37
11 changed files with 124 additions and 370 deletions
|
@ -47,26 +47,17 @@ async fn main() {
|
||||||
)
|
)
|
||||||
.execute(&mut *conn)
|
.execute(&mut *conn)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let ts = main::ap::new_ts();
|
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO user (
|
INSERT INTO user (id, username, actor_id, display_name)
|
||||||
id, acct, url, remote, username,
|
VALUES (?1, ?2, ?3, ?4)
|
||||||
actor_id, display_name, created_at
|
|
||||||
)
|
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)
|
|
||||||
"#,
|
"#,
|
||||||
"9b9d497b-2731-435f-a929-e609ca69dac9",
|
"9b9d497b-2731-435f-a929-e609ca69dac9",
|
||||||
"amy",
|
"amy",
|
||||||
"https://ferri.amy.mov/@amy",
|
|
||||||
false,
|
|
||||||
"amy",
|
|
||||||
"https://ferri.amy.mov/users/9b9d497b-2731-435f-a929-e609ca69dac9",
|
"https://ferri.amy.mov/users/9b9d497b-2731-435f-a929-e609ca69dac9",
|
||||||
"amy",
|
"amy"
|
||||||
ts
|
|
||||||
)
|
)
|
||||||
.execute(&mut *conn)
|
.execute(&mut *conn)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
pub mod ap;
|
pub mod ap;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod types_rewrite;
|
mod types_rewrite;
|
||||||
|
|
||||||
use rand::{Rng, distributions::Alphanumeric};
|
use rand::{Rng, distributions::Alphanumeric};
|
||||||
|
|
||||||
|
|
|
@ -26,36 +26,3 @@ impl From<ap::Actor> for db::Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<db::User> for api::Account {
|
|
||||||
fn from(val: db::User) -> api::Account {
|
|
||||||
api::Account {
|
|
||||||
id: val.id,
|
|
||||||
username: val.username,
|
|
||||||
acct: val.acct,
|
|
||||||
display_name: val.display_name,
|
|
||||||
|
|
||||||
locked: false,
|
|
||||||
bot: false,
|
|
||||||
|
|
||||||
created_at: val.created_at.to_rfc3339(),
|
|
||||||
attribution_domains: vec![],
|
|
||||||
|
|
||||||
note: "".to_string(),
|
|
||||||
url: val.url,
|
|
||||||
|
|
||||||
avatar: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
|
||||||
avatar_static: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
|
||||||
header: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
|
||||||
header_static: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
|
||||||
|
|
||||||
followers_count: 0,
|
|
||||||
following_count: 0,
|
|
||||||
statuses_count: 0,
|
|
||||||
last_status_at: val.posts.last_post_at.map(|ts| ts.to_rfc3339()),
|
|
||||||
|
|
||||||
emojis: vec![],
|
|
||||||
fields: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
use crate::types_rewrite::{ObjectUuid, ObjectUri, db};
|
|
||||||
use sqlx::SqliteConnection;
|
|
||||||
use thiserror::Error;
|
|
||||||
use tracing::info;
|
|
||||||
use chrono::{NaiveDateTime, DateTime, Utc};
|
|
||||||
|
|
||||||
const SQLITE_TIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum FetchError {
|
|
||||||
#[error("an unknown error occured when fetching: {0}")]
|
|
||||||
Unknown(String)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_ts(ts: String) -> Option<DateTime<Utc>> {
|
|
||||||
NaiveDateTime::parse_from_str(&ts, SQLITE_TIME_FMT)
|
|
||||||
.ok()
|
|
||||||
.map(|nt| nt.and_utc())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn user_by_id(id: ObjectUuid, conn: &mut SqliteConnection) -> Result<db::User, FetchError> {
|
|
||||||
info!("fetching user by uuid '{:?}' from the database", id);
|
|
||||||
|
|
||||||
let record = sqlx::query!(r#"
|
|
||||||
SELECT
|
|
||||||
u.id as "user_id",
|
|
||||||
u.username,
|
|
||||||
u.actor_id,
|
|
||||||
u.display_name,
|
|
||||||
a.inbox,
|
|
||||||
a.outbox,
|
|
||||||
u.url,
|
|
||||||
u.acct,
|
|
||||||
u.remote,
|
|
||||||
u.created_at
|
|
||||||
FROM "user" u
|
|
||||||
INNER JOIN "actor" a ON u.actor_id = a.id
|
|
||||||
WHERE u.id = ?1
|
|
||||||
"#, id.0)
|
|
||||||
.fetch_one(&mut *conn)
|
|
||||||
.await
|
|
||||||
.map_err(|e| FetchError::Unknown(e.to_string()))?;
|
|
||||||
|
|
||||||
let follower_count = sqlx::query_scalar!(r#"
|
|
||||||
SELECT COUNT(follower_id)
|
|
||||||
FROM "follow"
|
|
||||||
WHERE followed_id = ?1
|
|
||||||
"#, record.actor_id)
|
|
||||||
.fetch_one(&mut *conn)
|
|
||||||
.await
|
|
||||||
.map_err(|e| FetchError::Unknown(e.to_string()))?;
|
|
||||||
|
|
||||||
let last_post_at = sqlx::query_scalar!(r#"
|
|
||||||
SELECT datetime(p.created_at)
|
|
||||||
FROM post p
|
|
||||||
WHERE p.user_id = ?1
|
|
||||||
ORDER BY datetime(p.created_at) DESC
|
|
||||||
LIMIT 1
|
|
||||||
"#, record.user_id)
|
|
||||||
.fetch_one(&mut *conn)
|
|
||||||
.await
|
|
||||||
.map_err(|e| FetchError::Unknown(e.to_string()))?
|
|
||||||
.and_then(|ts| {
|
|
||||||
info!("parsing timestamp {}", ts);
|
|
||||||
parse_ts(ts)
|
|
||||||
});
|
|
||||||
|
|
||||||
let user_created = parse_ts(record.created_at).expect("no db corruption");
|
|
||||||
|
|
||||||
info!("user {:?} has {} followers", id, follower_count);
|
|
||||||
info!("user {:?} last posted {:?}", id, last_post_at);
|
|
||||||
|
|
||||||
Ok(db::User {
|
|
||||||
id: ObjectUuid(record.user_id),
|
|
||||||
actor: db::Actor {
|
|
||||||
id: ObjectUri(record.actor_id),
|
|
||||||
inbox: record.inbox,
|
|
||||||
outbox: record.outbox
|
|
||||||
},
|
|
||||||
acct: record.acct,
|
|
||||||
remote: record.remote,
|
|
||||||
username: record.username,
|
|
||||||
display_name: record.display_name,
|
|
||||||
created_at: user_created,
|
|
||||||
url: record.url,
|
|
||||||
posts: db::UserPosts {
|
|
||||||
last_post_at
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
pub mod convert;
|
mod convert;
|
||||||
pub mod fetch;
|
pub use convert::*;
|
||||||
|
|
||||||
pub const AS_CONTEXT_RAW: &'static str = "https://www.w3.org/ns/activitystreams";
|
pub const AS_CONTEXT_RAW: &'static str = "https://www.w3.org/ns/activitystreams";
|
||||||
pub fn as_context() -> ObjectContext {
|
pub fn as_context() -> ObjectContext {
|
||||||
|
@ -16,10 +16,7 @@ pub enum ObjectContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||||
pub struct ObjectUri(pub String);
|
pub struct ObjectUri(String);
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
|
||||||
pub struct ObjectUuid(pub String);
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||||
pub struct Object {
|
pub struct Object {
|
||||||
|
@ -29,35 +26,15 @@ pub struct Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod db {
|
pub mod db {
|
||||||
use chrono::{DateTime, Utc};
|
use serde::{Serialize, Deserialize};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||||
pub struct Actor {
|
pub struct Actor {
|
||||||
pub id: ObjectUri,
|
pub id: ObjectUri,
|
||||||
pub inbox: String,
|
pub inbox: String,
|
||||||
pub outbox: String,
|
pub outbox: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub struct UserPosts {
|
|
||||||
// User may have no posts
|
|
||||||
pub last_post_at: Option<DateTime<Utc>>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
pub struct User {
|
|
||||||
pub id: ObjectUuid,
|
|
||||||
pub actor: Actor,
|
|
||||||
pub username: String,
|
|
||||||
pub display_name: String,
|
|
||||||
pub acct: String,
|
|
||||||
pub remote: bool,
|
|
||||||
pub url: String,
|
|
||||||
pub created_at: DateTime<Utc>,
|
|
||||||
|
|
||||||
pub posts: UserPosts
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod ap {
|
pub mod ap {
|
||||||
|
@ -72,33 +49,6 @@ pub mod ap {
|
||||||
pub inbox: String,
|
pub inbox: String,
|
||||||
pub outbox: String,
|
pub outbox: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
|
||||||
pub struct Person {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub obj: Object,
|
|
||||||
|
|
||||||
pub following: String,
|
|
||||||
pub followers: String,
|
|
||||||
|
|
||||||
pub summary: String,
|
|
||||||
pub inbox: String,
|
|
||||||
pub outbox: String,
|
|
||||||
|
|
||||||
pub preferred_username: String,
|
|
||||||
pub name: String,
|
|
||||||
|
|
||||||
pub public_key: Option<UserKey>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
|
||||||
pub struct UserKey {
|
|
||||||
pub id: String,
|
|
||||||
pub owner: String,
|
|
||||||
|
|
||||||
#[serde(rename = "publicKeyPem")]
|
|
||||||
pub public_key: String,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod api {
|
pub mod api {
|
||||||
|
@ -108,51 +58,6 @@ pub mod api {
|
||||||
// API will not really use actors so treat them as DB actors
|
// API will not really use actors so treat them as DB actors
|
||||||
// until we require specificity
|
// until we require specificity
|
||||||
pub type Actor = db::Actor;
|
pub type Actor = db::Actor;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
|
||||||
pub struct Account {
|
|
||||||
pub id: ObjectUuid,
|
|
||||||
pub username: String,
|
|
||||||
pub acct: String,
|
|
||||||
pub display_name: String,
|
|
||||||
|
|
||||||
pub locked: bool,
|
|
||||||
pub bot: bool,
|
|
||||||
|
|
||||||
pub created_at: String,
|
|
||||||
pub attribution_domains: Vec<String>,
|
|
||||||
|
|
||||||
pub note: String,
|
|
||||||
pub url: String,
|
|
||||||
|
|
||||||
pub avatar: String,
|
|
||||||
pub avatar_static: String,
|
|
||||||
pub header: String,
|
|
||||||
pub header_static: String,
|
|
||||||
|
|
||||||
pub followers_count: i64,
|
|
||||||
pub following_count: i64,
|
|
||||||
pub statuses_count: i64,
|
|
||||||
pub last_status_at: Option<String>,
|
|
||||||
|
|
||||||
pub emojis: Vec<Emoji>,
|
|
||||||
pub fields: Vec<CustomField>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
|
||||||
pub struct Emoji {
|
|
||||||
pub shortcode: String,
|
|
||||||
pub url: String,
|
|
||||||
pub static_url: String,
|
|
||||||
pub visible_in_picker: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
|
||||||
pub struct CustomField {
|
|
||||||
pub name: String,
|
|
||||||
pub value: String,
|
|
||||||
pub verified_at: Option<String>,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -162,7 +67,6 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn ap_actor_to_db() {
|
fn ap_actor_to_db() {
|
||||||
let domain = "https://example.com";
|
let domain = "https://example.com";
|
||||||
|
|
||||||
let ap = ap::Actor {
|
let ap = ap::Actor {
|
||||||
obj: Object {
|
obj: Object {
|
||||||
context: as_context(),
|
context: as_context(),
|
||||||
|
|
|
@ -39,60 +39,35 @@ pub async fn home(
|
||||||
config: &State<Config>,
|
config: &State<Config>,
|
||||||
_user: AuthenticatedUser,
|
_user: AuthenticatedUser,
|
||||||
) -> Json<Vec<TimelineStatus>> {
|
) -> Json<Vec<TimelineStatus>> {
|
||||||
#[derive(sqlx::FromRow, Debug)]
|
let posts = sqlx::query!(
|
||||||
struct Post {
|
r#"
|
||||||
is_boost_source: bool,
|
SELECT p.id as "post_id", u.id as "user_id", p.content, p.uri as "post_uri",
|
||||||
post_id: String,
|
u.username, u.display_name, u.actor_id, p.created_at, p.boosted_post_id
|
||||||
user_id: String,
|
FROM post p
|
||||||
post_uri: String,
|
INNER JOIN user u on p.user_id = u.id
|
||||||
content: String,
|
|
||||||
created_at: String,
|
|
||||||
boosted_post_id: Option<String>,
|
|
||||||
display_name: String,
|
|
||||||
username: String
|
|
||||||
}
|
|
||||||
|
|
||||||
let posts = sqlx::query_as::<_, Post>(
|
|
||||||
r#"
|
|
||||||
WITH RECURSIVE get_home_timeline_with_boosts(
|
|
||||||
id, boosted_post_id, is_boost_source
|
|
||||||
) AS
|
|
||||||
(
|
|
||||||
SELECT p.id, p.boosted_post_id, 0 as is_boost_source
|
|
||||||
FROM post p
|
|
||||||
WHERE p.user_id IN (
|
|
||||||
SELECT u.id
|
|
||||||
FROM follow f
|
|
||||||
INNER JOIN user u ON u.actor_id = f.followed_id
|
|
||||||
WHERE f.follower_id = $1
|
|
||||||
)
|
|
||||||
UNION
|
|
||||||
SELECT p.id, p.boosted_post_id, 1 as is_boost_source
|
|
||||||
FROM post p
|
|
||||||
JOIN get_home_timeline_with_boosts tl ON tl.boosted_post_id = p.id
|
|
||||||
)
|
|
||||||
SELECT is_boost_source, p.id as "post_id", u.id as "user_id",
|
|
||||||
p.content, p.uri as "post_uri", u.username, u.display_name,
|
|
||||||
u.actor_id, p.created_at, p.boosted_post_id
|
|
||||||
FROM get_home_timeline_with_boosts
|
|
||||||
JOIN post p ON p.id = get_home_timeline_with_boosts.id
|
|
||||||
JOIN user u ON u.id = p.user_id;
|
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.bind("https://ferri.amy.mov/users/9b9d497b-2731-435f-a929-e609ca69dac9")
|
.fetch_all(&mut **db)
|
||||||
.fetch_all(&mut **db)
|
.await
|
||||||
.await
|
.unwrap();
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
dbg!(&posts);
|
|
||||||
|
|
||||||
let mut out = Vec::<TimelineStatus>::new();
|
let mut out = Vec::<TimelineStatus>::new();
|
||||||
for record in posts.iter() {
|
for record in posts {
|
||||||
let mut boost: Option<Box<TimelineStatus>> = None;
|
let mut boost: Option<Box<TimelineStatus>> = None;
|
||||||
if let Some(ref boosted_id) = record.boosted_post_id {
|
if let Some(boosted_id) = record.boosted_post_id {
|
||||||
let user_uri = config.user_url(&record.user_id);
|
let record = sqlx::query!(
|
||||||
let record = posts.iter().find(|p| &p.post_id == boosted_id).unwrap();
|
r#"
|
||||||
|
SELECT p.id as "post_id", u.id as "user_id", p.content, p.uri as "post_uri",
|
||||||
|
u.username, u.display_name, u.actor_id, p.created_at, p.boosted_post_id
|
||||||
|
FROM post p
|
||||||
|
INNER JOIN user u on p.user_id = u.id
|
||||||
|
WHERE p.id = ?1
|
||||||
|
"#, boosted_id)
|
||||||
|
.fetch_one(&mut **db)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let user_uri = config.user_url(&record.user_id);
|
||||||
boost = Some(Box::new(TimelineStatus {
|
boost = Some(Box::new(TimelineStatus {
|
||||||
id: record.post_id.clone(),
|
id: record.post_id.clone(),
|
||||||
created_at: record.created_at.clone(),
|
created_at: record.created_at.clone(),
|
||||||
|
@ -135,51 +110,49 @@ pub async fn home(
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !record.is_boost_source {
|
let user_uri = config.user_web_url(&record.username);
|
||||||
let user_uri = config.user_web_url(&record.username);
|
out.push(TimelineStatus {
|
||||||
out.push(TimelineStatus {
|
id: record.post_id.clone(),
|
||||||
id: record.post_id.clone(),
|
created_at: record.created_at.clone(),
|
||||||
created_at: record.created_at.clone(),
|
in_reply_to_id: None,
|
||||||
in_reply_to_id: None,
|
in_reply_to_account_id: None,
|
||||||
in_reply_to_account_id: None,
|
content: record.content.clone(),
|
||||||
content: record.content.clone(),
|
visibility: "public".to_string(),
|
||||||
visibility: "public".to_string(),
|
spoiler_text: "".to_string(),
|
||||||
spoiler_text: "".to_string(),
|
sensitive: false,
|
||||||
sensitive: false,
|
uri: record.post_uri.clone(),
|
||||||
uri: record.post_uri.clone(),
|
url: record.post_uri.clone(),
|
||||||
url: record.post_uri.clone(),
|
replies_count: 0,
|
||||||
replies_count: 0,
|
reblogs_count: 0,
|
||||||
reblogs_count: 0,
|
favourites_count: 0,
|
||||||
favourites_count: 0,
|
favourited: false,
|
||||||
favourited: false,
|
reblogged: false,
|
||||||
reblogged: false,
|
reblog: boost,
|
||||||
reblog: boost,
|
muted: false,
|
||||||
muted: false,
|
bookmarked: false,
|
||||||
bookmarked: false,
|
media_attachments: vec![],
|
||||||
media_attachments: vec![],
|
account: CredentialAcount {
|
||||||
account: CredentialAcount {
|
id: record.user_id.clone(),
|
||||||
id: record.user_id.clone(),
|
username: record.username.clone(),
|
||||||
username: record.username.clone(),
|
acct: record.username.clone(),
|
||||||
acct: record.username.clone(),
|
display_name: record.display_name.clone(),
|
||||||
display_name: record.display_name.clone(),
|
locked: false,
|
||||||
locked: false,
|
bot: false,
|
||||||
bot: false,
|
created_at: "2025-04-10T22:12:09Z".to_string(),
|
||||||
created_at: "2025-04-10T22:12:09Z".to_string(),
|
attribution_domains: vec![],
|
||||||
attribution_domains: vec![],
|
note: "".to_string(),
|
||||||
note: "".to_string(),
|
url: user_uri,
|
||||||
url: user_uri,
|
avatar: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||||
avatar: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
avatar_static: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||||
avatar_static: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
header: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||||
header: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
header_static: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
||||||
header_static: "https://ferri.amy.mov/assets/pfp.png".to_string(),
|
followers_count: 1,
|
||||||
followers_count: 1,
|
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(),
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Json(out)
|
Json(out)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use main::ap::http::HttpClient;
|
||||||
use rocket::{State, get, response::status};
|
use rocket::{State, get, response::status};
|
||||||
use rocket_db_pools::Connection;
|
use rocket_db_pools::Connection;
|
||||||
use main::ap;
|
use main::ap;
|
||||||
|
@ -7,7 +8,7 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Db,
|
Db,
|
||||||
types::{self, webfinger},
|
types::{self, activity, content, webfinger},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[get("/finger/<account>")]
|
#[get("/finger/<account>")]
|
||||||
|
@ -85,17 +86,44 @@ pub async fn resolve_user(acct: &str, host: &str) -> types::Person {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/test")]
|
#[get("/test")]
|
||||||
pub async fn test(
|
pub async fn test(http: &State<HttpClient>, outbound: &State<OutboundQueue>) -> &'static str {
|
||||||
outbound: &State<OutboundQueue>,
|
|
||||||
mut db: Connection<Db>
|
|
||||||
) -> &'static str {
|
|
||||||
use main::types_rewrite::{ObjectUuid, fetch, api};
|
|
||||||
outbound.0.send(ap::QueueMessage::Heartbeat);
|
outbound.0.send(ap::QueueMessage::Heartbeat);
|
||||||
|
|
||||||
|
let user = resolve_user("amy@fedi.amy.mov", "fedi.amy.mov").await;
|
||||||
|
|
||||||
let id = ObjectUuid("9b9d497b-2731-435f-a929-e609ca69dac9".to_string());
|
let post = activity::CreateActivity {
|
||||||
let user= dbg!(fetch::user_by_id(id, &mut **db).await.unwrap());
|
id: "https://ferri.amy.mov/activities/amy/20".to_string(),
|
||||||
let apu: api::Account = user.into();
|
ty: "Create".to_string(),
|
||||||
dbg!(apu);
|
actor: "https://ferri.amy.mov/users/amy".to_string(),
|
||||||
|
object: content::Post {
|
||||||
|
context: "https://www.w3.org/ns/activitystreams".to_string(),
|
||||||
|
id: "https://ferri.amy.mov/users/amy/posts/20".to_string(),
|
||||||
|
ty: "Note".to_string(),
|
||||||
|
content: "My first post".to_string(),
|
||||||
|
ts: "2025-04-10T10:48:11Z".to_string(),
|
||||||
|
to: vec!["https://ferri.amy.mov/users/amy/followers".to_string()],
|
||||||
|
cc: vec!["https://www.w3.org/ns/activitystreams#Public".to_string()],
|
||||||
|
attributed_to: None
|
||||||
|
},
|
||||||
|
ts: "2025-04-10T10:48:11Z".to_string(),
|
||||||
|
to: vec!["https://ferri.amy.mov/users/amy/followers".to_string()],
|
||||||
|
cc: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let key_id = "https://ferri.amy.mov/users/amy#main-key";
|
||||||
|
let follow = http
|
||||||
|
.post(user.inbox)
|
||||||
|
.json(&post)
|
||||||
|
.sign(key_id)
|
||||||
|
.activity()
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
dbg!(follow);
|
||||||
|
|
||||||
"Hello, world!"
|
"Hello, world!"
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,34 +50,19 @@ async fn create_user(
|
||||||
let host = url.host_str().unwrap();
|
let host = url.host_str().unwrap();
|
||||||
info!("creating user '{}'@'{}' ({:#?})", user.preferred_username, host, user);
|
info!("creating user '{}'@'{}' ({:#?})", user.preferred_username, host, user);
|
||||||
|
|
||||||
let (acct, remote) = if host != "ferri.amy.mov" {
|
let username = format!("{}@{}", user.preferred_username, host);
|
||||||
(format!("{}@{}", user.preferred_username, host), true)
|
|
||||||
} else {
|
|
||||||
(user.preferred_username.clone(), false)
|
|
||||||
};
|
|
||||||
|
|
||||||
let url = format!("https://ferri.amy.mov/{}", acct);
|
|
||||||
|
|
||||||
let uuid = Uuid::new_v4().to_string();
|
let uuid = Uuid::new_v4().to_string();
|
||||||
// FIXME: Pull from user
|
|
||||||
let ts = main::ap::new_ts();
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO user (
|
INSERT INTO user (id, username, actor_id, display_name)
|
||||||
id, acct, url, remote, username,
|
VALUES (?1, ?2, ?3, ?4)
|
||||||
actor_id, display_name, created_at
|
|
||||||
)
|
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)
|
|
||||||
ON CONFLICT(actor_id) DO NOTHING;
|
ON CONFLICT(actor_id) DO NOTHING;
|
||||||
"#,
|
"#,
|
||||||
uuid,
|
uuid,
|
||||||
acct,
|
username,
|
||||||
url,
|
|
||||||
remote,
|
|
||||||
user.preferred_username,
|
|
||||||
actor,
|
actor,
|
||||||
user.name,
|
user.name
|
||||||
ts
|
|
||||||
)
|
)
|
||||||
.execute(conn)
|
.execute(conn)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -8,12 +8,12 @@ use rocket::{
|
||||||
};
|
};
|
||||||
use rocket_db_pools::Connection;
|
use rocket_db_pools::Connection;
|
||||||
|
|
||||||
#[get("/oauth/authorize?<client_id>&<scope>&<redirect_uri>&<response_type>")]
|
#[get("/oauth/authorize?<client_id>&<scope>&<redirect_uri>&<_response_type>")]
|
||||||
pub async fn authorize(
|
pub async fn authorize(
|
||||||
client_id: &str,
|
client_id: &str,
|
||||||
scope: &str,
|
scope: &str,
|
||||||
redirect_uri: &str,
|
redirect_uri: &str,
|
||||||
response_type: &str,
|
_response_type: &str,
|
||||||
mut db: Connection<Db>,
|
mut db: Connection<Db>,
|
||||||
) -> Redirect {
|
) -> Redirect {
|
||||||
// For now, we will always authorize the request and assign it to an admin user
|
// For now, we will always authorize the request and assign it to an admin user
|
||||||
|
|
|
@ -2,10 +2,6 @@ CREATE TABLE IF NOT EXISTS user
|
||||||
(
|
(
|
||||||
-- UUID
|
-- UUID
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
acct TEXT NOT NULL,
|
|
||||||
url TEXT NOT NULL,
|
|
||||||
created_at TEXT NOT NULL,
|
|
||||||
remote BOOLEAN NOT NULL,
|
|
||||||
username TEXT NOT NULL,
|
username TEXT NOT NULL,
|
||||||
actor_id TEXT NOT NULL UNIQUE,
|
actor_id TEXT NOT NULL UNIQUE,
|
||||||
display_name TEXT NOT NULL,
|
display_name TEXT NOT NULL,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
CREATE TABLE IF NOT EXISTS post
|
CREATE TABLE IF NOT EXISTS post
|
||||||
(
|
(
|
||||||
id TEXT PRIMARY KEY NOT NULL,
|
id TEXT PRIMARY KEY NOT NULL,
|
||||||
uri TEXT NOT NULL UNIQUE,
|
uri TEXT NOT NULL,
|
||||||
user_id TEXT NOT NULL,
|
user_id TEXT NOT NULL,
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
created_at TEXT NOT NULL,
|
created_at TEXT NOT NULL,
|
||||||
|
|
Loading…
Add table
Reference in a new issue