mirror of
https://github.com/nullishamy/ferri.git
synced 2025-04-30 04:39:20 +00:00
Compare commits
No commits in common. "5f346922f5682a89243dfcc2b2a8399f23be7a65" and "d252131e0da1ac51f34370c8f07c4a927c9991e1" have entirely different histories.
5f346922f5
...
d252131e0d
13 changed files with 167 additions and 275 deletions
|
@ -1,18 +1,24 @@
|
||||||
use crate::types_rewrite::{ObjectUuid, ObjectUri, db};
|
use crate::types_rewrite::{ObjectUuid, ObjectUri, db};
|
||||||
use sqlx::SqliteConnection;
|
use sqlx::SqliteConnection;
|
||||||
|
use thiserror::Error;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use chrono::{NaiveDateTime, DateTime, Utc};
|
use chrono::{NaiveDateTime, DateTime, Utc};
|
||||||
use crate::types_rewrite::DbError;
|
|
||||||
|
|
||||||
const SQLITE_TIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
|
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>> {
|
fn parse_ts(ts: String) -> Option<DateTime<Utc>> {
|
||||||
NaiveDateTime::parse_from_str(&ts, SQLITE_TIME_FMT)
|
NaiveDateTime::parse_from_str(&ts, SQLITE_TIME_FMT)
|
||||||
.ok()
|
.ok()
|
||||||
.map(|nt| nt.and_utc())
|
.map(|nt| nt.and_utc())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn user_by_id(id: ObjectUuid, conn: &mut SqliteConnection) -> Result<db::User, DbError> {
|
pub async fn user_by_id(id: ObjectUuid, conn: &mut SqliteConnection) -> Result<db::User, FetchError> {
|
||||||
info!("fetching user by uuid '{:?}' from the database", id);
|
info!("fetching user by uuid '{:?}' from the database", id);
|
||||||
|
|
||||||
let record = sqlx::query!(r#"
|
let record = sqlx::query!(r#"
|
||||||
|
@ -33,7 +39,7 @@ pub async fn user_by_id(id: ObjectUuid, conn: &mut SqliteConnection) -> Result<d
|
||||||
"#, id.0)
|
"#, id.0)
|
||||||
.fetch_one(&mut *conn)
|
.fetch_one(&mut *conn)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| DbError::FetchError(e.to_string()))?;
|
.map_err(|e| FetchError::Unknown(e.to_string()))?;
|
||||||
|
|
||||||
let follower_count = sqlx::query_scalar!(r#"
|
let follower_count = sqlx::query_scalar!(r#"
|
||||||
SELECT COUNT(follower_id)
|
SELECT COUNT(follower_id)
|
||||||
|
@ -42,7 +48,7 @@ pub async fn user_by_id(id: ObjectUuid, conn: &mut SqliteConnection) -> Result<d
|
||||||
"#, record.actor_id)
|
"#, record.actor_id)
|
||||||
.fetch_one(&mut *conn)
|
.fetch_one(&mut *conn)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| DbError::FetchError(e.to_string()))?;
|
.map_err(|e| FetchError::Unknown(e.to_string()))?;
|
||||||
|
|
||||||
let last_post_at = sqlx::query_scalar!(r#"
|
let last_post_at = sqlx::query_scalar!(r#"
|
||||||
SELECT datetime(p.created_at)
|
SELECT datetime(p.created_at)
|
||||||
|
@ -53,7 +59,7 @@ pub async fn user_by_id(id: ObjectUuid, conn: &mut SqliteConnection) -> Result<d
|
||||||
"#, record.user_id)
|
"#, record.user_id)
|
||||||
.fetch_one(&mut *conn)
|
.fetch_one(&mut *conn)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| DbError::FetchError(e.to_string()))?
|
.map_err(|e| FetchError::Unknown(e.to_string()))?
|
||||||
.and_then(|ts| {
|
.and_then(|ts| {
|
||||||
info!("parsing timestamp {}", ts);
|
info!("parsing timestamp {}", ts);
|
||||||
parse_ts(ts)
|
parse_ts(ts)
|
|
@ -1,30 +0,0 @@
|
||||||
use crate::types_rewrite::db;
|
|
||||||
use sqlx::SqliteConnection;
|
|
||||||
use crate::types_rewrite::DbError;
|
|
||||||
|
|
||||||
pub async fn new_user(user: db::User, conn: &mut SqliteConnection) -> Result<db::User, DbError> {
|
|
||||||
let ts = user.created_at.to_rfc3339();
|
|
||||||
sqlx::query!(r#"
|
|
||||||
INSERT INTO user (id, acct, url, created_at, remote, username, actor_id, display_name)
|
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)
|
|
||||||
"#, user.id.0, user.acct, user.url, ts,
|
|
||||||
user.remote, user.username, user.actor.id.0, user.display_name
|
|
||||||
)
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
.map_err(|e| DbError::CreationError(e.to_string()))?;
|
|
||||||
|
|
||||||
Ok(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn new_actor(actor: db::Actor, conn: &mut SqliteConnection) -> Result<db::Actor, DbError> {
|
|
||||||
sqlx::query!(r#"
|
|
||||||
INSERT INTO actor (id, inbox, outbox)
|
|
||||||
VALUES (?1, ?2, ?3)
|
|
||||||
"#, actor.id.0, actor.inbox, actor.outbox)
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
.map_err(|e| DbError::CreationError(e.to_string()))?;
|
|
||||||
|
|
||||||
Ok(actor)
|
|
||||||
}
|
|
|
@ -1,19 +1,7 @@
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use thiserror::Error;
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
pub mod convert;
|
pub mod convert;
|
||||||
pub mod get;
|
pub mod fetch;
|
||||||
pub mod make;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum DbError {
|
|
||||||
#[error("an unknown error occured when creating: {0}")]
|
|
||||||
CreationError(String),
|
|
||||||
#[error("an unknown error occured when fetching: {0}")]
|
|
||||||
FetchError(String)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -27,18 +15,12 @@ pub enum ObjectContext {
|
||||||
Vec(Vec<serde_json::Value>),
|
Vec(Vec<serde_json::Value>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||||
pub struct ObjectUri(pub String);
|
pub struct ObjectUri(pub String);
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||||
pub struct ObjectUuid(pub String);
|
pub struct ObjectUuid(pub String);
|
||||||
|
|
||||||
impl ObjectUuid {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self(Uuid::new_v4().to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||||
pub struct Object {
|
pub struct Object {
|
||||||
#[serde(rename = "@context")]
|
#[serde(rename = "@context")]
|
||||||
|
@ -50,20 +32,20 @@ pub mod db {
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(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, Clone)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct UserPosts {
|
pub struct UserPosts {
|
||||||
// User may have no posts
|
// User may have no posts
|
||||||
pub last_post_at: Option<DateTime<Utc>>
|
pub last_post_at: Option<DateTime<Utc>>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: ObjectUuid,
|
pub id: ObjectUuid,
|
||||||
pub actor: Actor,
|
pub actor: Actor,
|
||||||
|
@ -171,107 +153,6 @@ pub mod api {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
pub verified_at: Option<String>,
|
pub verified_at: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Instance {
|
|
||||||
pub domain: String,
|
|
||||||
pub title: String,
|
|
||||||
pub version: String,
|
|
||||||
pub source_url: String,
|
|
||||||
pub description: String,
|
|
||||||
pub thumbnail: Thumbnail,
|
|
||||||
pub icon: Vec<Icon>,
|
|
||||||
pub languages: Vec<String>,
|
|
||||||
pub configuration: Configuration,
|
|
||||||
pub registrations: Registrations,
|
|
||||||
pub contact: Contact,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Configuration {
|
|
||||||
pub urls: Urls,
|
|
||||||
pub accounts: Accounts,
|
|
||||||
pub statuses: Statuses,
|
|
||||||
pub media_attachments: MediaAttachments,
|
|
||||||
pub polls: Polls,
|
|
||||||
pub translation: Translation,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Accounts {
|
|
||||||
pub max_featured_tags: i64,
|
|
||||||
pub max_pinned_statuses: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct MediaAttachments {
|
|
||||||
pub supported_mime_types: Vec<String>,
|
|
||||||
pub description_limit: i64,
|
|
||||||
pub image_size_limit: i64,
|
|
||||||
pub image_matrix_limit: i64,
|
|
||||||
pub video_size_limit: i64,
|
|
||||||
pub video_frame_rate_limit: i64,
|
|
||||||
pub video_matrix_limit: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Polls {
|
|
||||||
pub max_options: i64,
|
|
||||||
pub max_characters_per_option: i64,
|
|
||||||
pub min_expiration: i64,
|
|
||||||
pub max_expiration: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Statuses {
|
|
||||||
pub max_characters: i64,
|
|
||||||
pub max_media_attachments: i64,
|
|
||||||
pub characters_reserved_per_url: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Translation {
|
|
||||||
pub enabled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Urls {
|
|
||||||
pub streaming: String,
|
|
||||||
pub about: String,
|
|
||||||
pub privacy_policy: String,
|
|
||||||
pub terms_of_service: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Contact {
|
|
||||||
pub email: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Field {
|
|
||||||
pub name: String,
|
|
||||||
pub value: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Icon {
|
|
||||||
pub src: String,
|
|
||||||
pub size: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Registrations {
|
|
||||||
pub enabled: bool,
|
|
||||||
pub approval_required: bool,
|
|
||||||
pub reason_required: bool,
|
|
||||||
pub message: Option<String>,
|
|
||||||
pub min_age: i64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Thumbnail {
|
|
||||||
pub url: String,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -2,7 +2,7 @@ use rocket::{get, serde::json::Json, State};
|
||||||
|
|
||||||
use crate::Config;
|
use crate::Config;
|
||||||
|
|
||||||
use main::types_rewrite::api::{
|
use crate::types::instance::{
|
||||||
Accounts, Configuration, Contact, Instance, MediaAttachments, Polls, Registrations, Statuses,
|
Accounts, Configuration, Contact, Instance, MediaAttachments, Polls, Registrations, Statuses,
|
||||||
Thumbnail, Translation, Urls,
|
Thumbnail, Translation, Urls,
|
||||||
};
|
};
|
||||||
|
|
|
@ -51,8 +51,7 @@ pub async fn home(
|
||||||
display_name: String,
|
display_name: String,
|
||||||
username: String
|
username: String
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: query! can't cope with this. returns a type error
|
|
||||||
let posts = sqlx::query_as::<_, Post>(
|
let posts = sqlx::query_as::<_, Post>(
|
||||||
r#"
|
r#"
|
||||||
WITH RECURSIVE get_home_timeline_with_boosts(
|
WITH RECURSIVE get_home_timeline_with_boosts(
|
||||||
|
@ -85,6 +84,8 @@ pub async fn home(
|
||||||
.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.iter() {
|
||||||
let mut boost: Option<Box<TimelineStatus>> = None;
|
let mut boost: Option<Box<TimelineStatus>> = None;
|
||||||
|
|
|
@ -89,11 +89,11 @@ pub async fn test(
|
||||||
outbound: &State<OutboundQueue>,
|
outbound: &State<OutboundQueue>,
|
||||||
mut db: Connection<Db>
|
mut db: Connection<Db>
|
||||||
) -> &'static str {
|
) -> &'static str {
|
||||||
use main::types_rewrite::{ObjectUuid, get, api};
|
use main::types_rewrite::{ObjectUuid, fetch, api};
|
||||||
outbound.0.send(ap::QueueMessage::Heartbeat);
|
outbound.0.send(ap::QueueMessage::Heartbeat);
|
||||||
|
|
||||||
let id = ObjectUuid("9b9d497b-2731-435f-a929-e609ca69dac9".to_string());
|
let id = ObjectUuid("9b9d497b-2731-435f-a929-e609ca69dac9".to_string());
|
||||||
let user= dbg!(get::user_by_id(id, &mut **db).await.unwrap());
|
let user= dbg!(fetch::user_by_id(id, &mut **db).await.unwrap());
|
||||||
let apu: api::Account = user.into();
|
let apu: api::Account = user.into();
|
||||||
dbg!(apu);
|
dbg!(apu);
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use tracing::Instrument;
|
use tracing::Instrument;
|
||||||
|
use main::ap;
|
||||||
use rocket::serde::json::serde_json;
|
use rocket::serde::json::serde_json;
|
||||||
use rocket::{State, post};
|
use rocket::{State, post};
|
||||||
use rocket_db_pools::Connection;
|
use rocket_db_pools::Connection;
|
||||||
use sqlx::SqliteConnection;
|
|
||||||
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, error};
|
use tracing::{event, span, Level, debug, warn, info, error};
|
||||||
use crate::http_wrapper::HttpWrapper;
|
use crate::http_wrapper::HttpWrapper;
|
||||||
|
|
||||||
use main::types_rewrite::{make, db, ObjectUuid, ObjectUri, self, ap};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Db,
|
Db,
|
||||||
http::HttpClient,
|
http::HttpClient,
|
||||||
types::{content::Post, activity},
|
types::{Person, content::Post, activity},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn handle_delete_activity(activity: activity::DeleteActivity) {
|
fn handle_delete_activity(activity: activity::DeleteActivity) {
|
||||||
|
@ -23,7 +21,7 @@ fn handle_delete_activity(activity: activity::DeleteActivity) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_actor(
|
async fn create_actor(
|
||||||
user: &ap::Person,
|
user: &Person,
|
||||||
actor: &str,
|
actor: &str,
|
||||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
||||||
) {
|
) {
|
||||||
|
@ -43,7 +41,7 @@ async fn create_actor(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_user(
|
async fn create_user(
|
||||||
user: &ap::Person,
|
user: &Person,
|
||||||
actor: &str,
|
actor: &str,
|
||||||
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
conn: impl sqlx::Executor<'_, Database = Sqlite>,
|
||||||
) {
|
) {
|
||||||
|
@ -105,106 +103,36 @@ async fn create_follow(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RemoteInfo {
|
|
||||||
acct: String,
|
|
||||||
web_url: String,
|
|
||||||
is_remote: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_remote_info(actor_url: &str, person: &ap::Person) -> RemoteInfo {
|
|
||||||
let url = Url::parse(&actor_url).unwrap();
|
|
||||||
let host = url.host_str().unwrap();
|
|
||||||
|
|
||||||
let (acct, remote) = if host != "ferri.amy.mov" {
|
|
||||||
(format!("{}@{}", person.preferred_username, host), true)
|
|
||||||
} else {
|
|
||||||
(person.preferred_username.clone(), false)
|
|
||||||
};
|
|
||||||
|
|
||||||
let url = format!("https://ferri.amy.mov/{}", acct);
|
|
||||||
|
|
||||||
RemoteInfo {
|
|
||||||
acct: acct.to_string(),
|
|
||||||
web_url: url,
|
|
||||||
is_remote: remote
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn resolve_actor<'a>(
|
|
||||||
actor_url: &str,
|
|
||||||
http: &HttpWrapper<'a>,
|
|
||||||
conn: &mut SqliteConnection
|
|
||||||
) -> Result<db::User, types_rewrite::DbError> {
|
|
||||||
let person = {
|
|
||||||
let res = http.get_person(&actor_url).await;
|
|
||||||
if let Err(e) = res {
|
|
||||||
error!("could not load user {}: {}", actor_url, e.to_string());
|
|
||||||
return Err(types_rewrite::DbError::FetchError(
|
|
||||||
format!("could not load user {}: {}", actor_url, e.to_string())
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
res.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let user_id = ObjectUuid::new();
|
|
||||||
let remote_info = get_remote_info(actor_url, &person);
|
|
||||||
|
|
||||||
let actor = db::Actor {
|
|
||||||
id: ObjectUri(actor_url.to_string()),
|
|
||||||
inbox: person.inbox.clone(),
|
|
||||||
outbox: person.outbox.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
info!("creating actor {}", actor_url);
|
|
||||||
|
|
||||||
let actor = make::new_actor(actor.clone(), conn).await.unwrap_or(actor);
|
|
||||||
|
|
||||||
info!("creating user {} ({:#?})", remote_info.acct, person);
|
|
||||||
|
|
||||||
let user = db::User {
|
|
||||||
id: user_id,
|
|
||||||
actor,
|
|
||||||
username: person.name,
|
|
||||||
display_name: person.preferred_username,
|
|
||||||
acct: remote_info.acct,
|
|
||||||
remote: remote_info.is_remote,
|
|
||||||
url: remote_info.web_url,
|
|
||||||
created_at: main::ap::now(),
|
|
||||||
|
|
||||||
posts: db::UserPosts {
|
|
||||||
last_post_at: None
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(make::new_user(user.clone(), conn).await.unwrap_or(user))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_follow_activity<'a>(
|
async fn handle_follow_activity<'a>(
|
||||||
followed_account: &str,
|
followed_account: &str,
|
||||||
activity: activity::FollowActivity,
|
activity: activity::FollowActivity,
|
||||||
http: HttpWrapper<'a>,
|
http: HttpWrapper<'a>,
|
||||||
mut db: Connection<Db>,
|
mut db: Connection<Db>,
|
||||||
) {
|
) {
|
||||||
let actor = resolve_actor(&activity.actor, &http, &mut **db)
|
let user = http.get_person(&activity.actor).await;
|
||||||
.await.unwrap();
|
if let Err(e) = user {
|
||||||
|
error!("could not load user {}: {}", activity.actor, e.to_string());
|
||||||
info!("{:?} follows {}", actor, followed_account);
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let user = user.unwrap();
|
||||||
|
|
||||||
|
create_actor(&user, &activity.actor, &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 = main::ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
let follower = ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
||||||
let followed = main::ap::User::from_id(&followed_account, &mut **db).await.unwrap();
|
let followed = ap::User::from_id(&followed_account, &mut **db).await.unwrap();
|
||||||
let outbox = main::ap::Outbox::for_user(followed.clone(), http.client());
|
let outbox = ap::Outbox::for_user(followed.clone(), http.client());
|
||||||
|
|
||||||
let activity = main::ap::Activity {
|
let activity = ap::Activity {
|
||||||
id: format!("https://ferri.amy.mov/activities/{}", Uuid::new_v4()),
|
id: format!("https://ferri.amy.mov/activities/{}", Uuid::new_v4()),
|
||||||
ty: main::ap::ActivityType::Accept,
|
ty: ap::ActivityType::Accept,
|
||||||
object: activity.id,
|
object: activity.id,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let req = main::ap::OutgoingActivity {
|
let req = ap::OutgoingActivity {
|
||||||
signed_by: format!(
|
signed_by: format!(
|
||||||
"https://ferri.amy.mov/users/{}#main-key",
|
"https://ferri.amy.mov/users/{}#main-key",
|
||||||
followed.username()
|
followed.username()
|
||||||
|
@ -289,27 +217,24 @@ async fn handle_boost_activity<'a>(
|
||||||
debug!("creating user {}", attribution);
|
debug!("creating user {}", attribution);
|
||||||
create_user(&post_user, &attribution, &mut **db).await;
|
create_user(&post_user, &attribution, &mut **db).await;
|
||||||
|
|
||||||
let attributed_user = main::ap::User::from_actor_id(&attribution, &mut **db).await;
|
let attributed_user = ap::User::from_actor_id(&attribution, &mut **db).await;
|
||||||
let actor_user = main::ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
let actor_user = ap::User::from_actor_id(&activity.actor, &mut **db).await;
|
||||||
|
|
||||||
let base_id = main::ap::new_id();
|
let base_id = ap::new_id();
|
||||||
let now = main::ap::new_ts();
|
let now = ap::new_ts();
|
||||||
|
|
||||||
let reblog_id = main::ap::new_id();
|
let reblog_id = ap::new_id();
|
||||||
|
|
||||||
let attr_id = attributed_user.id();
|
let attr_id = attributed_user.id();
|
||||||
// HACK: ON CONFLICT is to avoid duplicate remote posts coming in
|
|
||||||
// check this better in future
|
|
||||||
sqlx::query!("
|
sqlx::query!("
|
||||||
INSERT INTO post (id, uri, user_id, content, created_at)
|
INSERT INTO post (id, uri, user_id, content, created_at)
|
||||||
VALUES (?1, ?2, ?3, ?4, ?5)
|
VALUES (?1, ?2, ?3, ?4, ?5)
|
||||||
ON CONFLICT(uri) DO NOTHING
|
|
||||||
", reblog_id, post.id, attr_id, post.content, post.ts)
|
", reblog_id, post.id, attr_id, post.content, post.ts)
|
||||||
.execute(&mut **db)
|
.execute(&mut **db)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let uri = format!("https://ferri.amy.mov/users/{}/posts/{}", actor_user.id(), base_id);
|
let uri = format!("https://ferri.amy.mov/users/{}/posts/{}", actor_user.id(), post.id);
|
||||||
let user_id = actor_user.id();
|
let user_id = actor_user.id();
|
||||||
|
|
||||||
sqlx::query!("
|
sqlx::query!("
|
||||||
|
@ -344,7 +269,7 @@ async fn handle_create_activity<'a>(
|
||||||
debug!("creating user {}", activity.actor);
|
debug!("creating user {}", activity.actor);
|
||||||
create_user(&user, &activity.actor, &mut **db).await;
|
create_user(&user, &activity.actor, &mut **db).await;
|
||||||
|
|
||||||
let user = main::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);
|
debug!("user created {:?}", user);
|
||||||
|
|
||||||
let user_id = user.id();
|
let user_id = user.id();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{error, event, Level};
|
use tracing::{error, event, Level};
|
||||||
use crate::http::HttpClient;
|
use crate::http::HttpClient;
|
||||||
use main::types_rewrite::ap;
|
use crate::types::Person;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
pub struct HttpWrapper<'a> {
|
pub struct HttpWrapper<'a> {
|
||||||
|
@ -65,7 +65,7 @@ impl <'a> HttpWrapper<'a> {
|
||||||
Ok(decoded.unwrap())
|
Ok(decoded.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_person(&self, url: &str) -> Result<ap::Person, HttpError> {
|
pub async fn get_person(&self, url: &str) -> Result<Person, HttpError> {
|
||||||
self.get("Person", url).await
|
self.get("Person", url).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ use rocket::serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
#[deprecated]
|
|
||||||
pub struct Post {
|
pub struct Post {
|
||||||
// FIXME: This is because Masto sends an array but we don't care
|
// FIXME: This is because Masto sends an array but we don't care
|
||||||
#[serde(rename = "@context")]
|
#[serde(rename = "@context")]
|
||||||
|
|
115
ferri-server/src/types/instance.rs
Normal file
115
ferri-server/src/types/instance.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Instance {
|
||||||
|
pub domain: String,
|
||||||
|
pub title: String,
|
||||||
|
pub version: String,
|
||||||
|
pub source_url: String,
|
||||||
|
pub description: String,
|
||||||
|
pub thumbnail: Thumbnail,
|
||||||
|
pub icon: Vec<Icon>,
|
||||||
|
pub languages: Vec<String>,
|
||||||
|
pub configuration: Configuration,
|
||||||
|
pub registrations: Registrations,
|
||||||
|
pub contact: Contact,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Configuration {
|
||||||
|
pub urls: Urls,
|
||||||
|
pub accounts: Accounts,
|
||||||
|
pub statuses: Statuses,
|
||||||
|
pub media_attachments: MediaAttachments,
|
||||||
|
pub polls: Polls,
|
||||||
|
pub translation: Translation,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Accounts {
|
||||||
|
pub max_featured_tags: i64,
|
||||||
|
pub max_pinned_statuses: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct MediaAttachments {
|
||||||
|
pub supported_mime_types: Vec<String>,
|
||||||
|
pub description_limit: i64,
|
||||||
|
pub image_size_limit: i64,
|
||||||
|
pub image_matrix_limit: i64,
|
||||||
|
pub video_size_limit: i64,
|
||||||
|
pub video_frame_rate_limit: i64,
|
||||||
|
pub video_matrix_limit: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Polls {
|
||||||
|
pub max_options: i64,
|
||||||
|
pub max_characters_per_option: i64,
|
||||||
|
pub min_expiration: i64,
|
||||||
|
pub max_expiration: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Statuses {
|
||||||
|
pub max_characters: i64,
|
||||||
|
pub max_media_attachments: i64,
|
||||||
|
pub characters_reserved_per_url: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Translation {
|
||||||
|
pub enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Urls {
|
||||||
|
pub streaming: String,
|
||||||
|
pub about: String,
|
||||||
|
pub privacy_policy: String,
|
||||||
|
pub terms_of_service: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Contact {
|
||||||
|
pub email: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Field {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Icon {
|
||||||
|
pub src: String,
|
||||||
|
pub size: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Registrations {
|
||||||
|
pub enabled: bool,
|
||||||
|
pub approval_required: bool,
|
||||||
|
pub reason_required: bool,
|
||||||
|
pub message: Option<String>,
|
||||||
|
pub min_age: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Thumbnail {
|
||||||
|
pub url: String,
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
pub mod activity;
|
pub mod activity;
|
||||||
pub mod content;
|
pub mod content;
|
||||||
|
pub mod instance;
|
||||||
pub mod oauth;
|
pub mod oauth;
|
||||||
pub mod webfinger;
|
pub mod webfinger;
|
||||||
|
|
||||||
|
@ -8,7 +9,6 @@ use rocket::serde::{Deserialize, Serialize};
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
#[deprecated]
|
|
||||||
pub struct UserKey {
|
pub struct UserKey {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub owner: String,
|
pub owner: String,
|
||||||
|
@ -19,7 +19,6 @@ pub struct UserKey {
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
#[deprecated]
|
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
// FIXME: This is because Masto sends an array but we don't care
|
// FIXME: This is because Masto sends an array but we don't care
|
||||||
#[serde(rename = "@context")]
|
#[serde(rename = "@context")]
|
||||||
|
|
|
@ -5,7 +5,6 @@ use rocket::{
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, FromForm, Clone)]
|
#[derive(Serialize, Deserialize, Debug, FromForm, Clone)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
#[deprecated]
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub client_name: String,
|
pub client_name: String,
|
||||||
pub redirect_uris: Vec<String>,
|
pub redirect_uris: Vec<String>,
|
||||||
|
@ -14,7 +13,6 @@ pub struct App {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
#[deprecated]
|
|
||||||
pub struct CredentialApplication {
|
pub struct CredentialApplication {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub scopes: String,
|
pub scopes: String,
|
||||||
|
|
|
@ -2,7 +2,6 @@ use rocket::serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
#[deprecated]
|
|
||||||
pub struct Link {
|
pub struct Link {
|
||||||
pub rel: String,
|
pub rel: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
|
@ -12,7 +11,6 @@ pub struct Link {
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
#[deprecated]
|
|
||||||
pub struct WebfingerResponse {
|
pub struct WebfingerResponse {
|
||||||
pub subject: String,
|
pub subject: String,
|
||||||
pub aliases: Vec<String>,
|
pub aliases: Vec<String>,
|
||||||
|
|
Loading…
Add table
Reference in a new issue