Merge branch 'test1'

This commit is contained in:
aprilthepink 2024-06-27 05:17:03 +02:00
commit b52024d726
13 changed files with 163 additions and 11 deletions

10
Cargo.lock generated
View file

@ -571,6 +571,15 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64-url"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38e2b6c78c06f7288d5e3c3d683bde35a79531127c83b087e5d0d77c974b4b28"
dependencies = [
"base64 0.22.1",
]
[[package]]
name = "base64ct"
version = "1.6.0"
@ -2061,6 +2070,7 @@ dependencies = [
"async-recursion",
"async-trait",
"async_once",
"base64-url",
"chrono",
"clap",
"dotenv",

View file

@ -33,6 +33,7 @@ time = { version = "0.3.36", features = ["serde"] }
serde_derive = "1.0.201"
dotenv = "0.15.0"
async-recursion = "1.1.1"
base64-url = "3.0.0"
[dependencies.sea-orm]
version = "0.12.0"

View file

@ -4,6 +4,7 @@ mod m20220101_000001_post_table;
mod m20240417_230111_user_table;
mod m20240417_233430_post_user_keys;
mod m20240505_002524_user_follow_relation;
mod m20240626_030922_store_ap_json_in_posts;
pub struct Migrator;
@ -15,6 +16,7 @@ impl MigratorTrait for Migrator {
Box::new(m20240417_230111_user_table::Migration),
Box::new(m20240417_233430_post_user_keys::Migration),
Box::new(m20240505_002524_user_follow_relation::Migration),
Box::new(m20240626_030922_store_ap_json_in_posts::Migration),
]
}
}

View file

@ -0,0 +1,35 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Post::Table)
.add_column_if_not_exists(ColumnDef::new(Post::ApJson).string())
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Post::Table)
.drop_column(Post::ApJson)
.to_owned(),
)
.await
}
}
#[derive(DeriveIden)]
pub enum Post {
Table,
ApJson,
}

View file

@ -6,7 +6,7 @@ use crate::{
person::DbUser,
post::{DbPost, Note},
},
utils::generate_random_object_id,
utils::{base_url_encode, generate_create_id, generate_random_object_id},
};
use activitypub_federation::{
activity_sending::SendActivityTask,
@ -32,14 +32,20 @@ pub struct CreatePost {
}
impl CreatePost {
pub async fn send(note: Note, inbox: Url, data: &Data<StateHandle>) -> Result<(), Error> {
pub async fn send(
note: Note,
db_entry: post::Model,
inbox: Url,
data: &Data<StateHandle>,
) -> Result<(), Error> {
print!("Sending reply to {}", &note.attributed_to);
let encoded_url = base_url_encode(&note.id.clone().into());
let create = CreatePost {
actor: note.attributed_to.clone(),
to: note.to.clone(),
object: note,
kind: CreateType::Create,
id: generate_random_object_id(data.domain())?,
id: generate_create_id(data.domain(), &db_entry.id, &encoded_url)?,
};
let create_with_context = WithContext::new_default(create);
let sends = SendActivityTask::prepare(

View file

@ -77,7 +77,7 @@ impl Accept {
actor: follow_req.object.clone(),
object: follow_req,
kind: AcceptType::Accept,
id: generate_follow_accept_id(data.domain(), follow_relation.id)?,
id: generate_follow_accept_id(data.domain(), follow_relation.id.to_string().as_str())?,
};
let create_with_context = WithContext::new_default(create);
let sends = SendActivityTask::prepare(

View file

@ -24,6 +24,7 @@ pub struct Model {
pub spoiler_text: Option<String>,
pub creator: String,
pub url: String,
pub ap_json: Option<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View file

@ -229,6 +229,7 @@ pub async fn receive_lysand_note(
reply_id: Set(reply_uuid),
quoting_id: Set(quote_uuid),
spoiler_text: Set(note.subject),
ap_json: Set(Some(serde_json::to_string(&ap_note).unwrap())),
..Default::default()
};
let res = post.insert(DB.get().unwrap()).await?;

View file

@ -1,4 +1,7 @@
use activitypub_federation::{traits::Object, FEDERATION_CONTENT_TYPE};
use activitypub_federation::{
protocol::context::WithContext, traits::Object, FEDERATION_CONTENT_TYPE,
};
use activitystreams_kinds::{activity::CreateType, object};
use actix_web::{get, web, HttpResponse};
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
@ -8,7 +11,9 @@ use crate::{
post::{self, Entity},
prelude,
},
error, Response, DB, FEDERATION_CONFIG,
error, objects,
utils::{base_url_decode, generate_create_id},
Response, DB, FEDERATION_CONFIG,
};
#[get("/apbridge/object/{post}")]
@ -35,3 +40,40 @@ async fn fetch_post(
.await?,
))
}
#[get("/apbridge/create/{id}/{base64url}")]
async fn create_activity(
path: web::Path<(String, String)>,
state: web::Data<State>,
) -> actix_web::Result<HttpResponse, error::Error> {
let db = DB.get().unwrap();
let url = base_url_decode(path.1.as_str());
let post = prelude::Post::find()
.filter(post::Column::Id.eq(path.0.as_str()))
.one(db)
.await?;
let post = match post {
Some(post) => post,
None => return Ok(HttpResponse::NotFound().finish()),
};
let ap_post = crate::objects::post::Note::from_db(&post);
let data = FEDERATION_CONFIG.get().unwrap();
let create = crate::activities::create_post::CreatePost {
actor: ap_post.attributed_to.clone(),
to: ap_post.to.clone(),
object: ap_post,
kind: CreateType::Create,
id: generate_create_id(&data.to_request_data().domain(), &path.0, &path.1)?,
};
let create_with_context = WithContext::new_default(create);
Ok(HttpResponse::Ok()
.content_type(FEDERATION_CONTENT_TYPE)
.json(create_with_context))
}

View file

@ -15,6 +15,7 @@ use clap::Parser;
use database::Database;
use entities::post;
use http::{http_get_user, http_post_user_inbox, webfinger};
use lysand::http::{create_activity, fetch_post};
use objects::person::DbUser;
use sea_orm::{ActiveModelTrait, DatabaseConnection, Set};
use serde::{Deserialize, Serialize};
@ -89,7 +90,7 @@ async fn post_manually(
let id: ObjectId<post::Model> = generate_random_object_id(data.domain())?.into();
let note = Note {
kind: Default::default(),
id,
id: id.clone(),
sensitive: false,
attributed_to: Url::parse(&local_user.id).unwrap().into(),
to: vec![public()],
@ -99,8 +100,26 @@ async fn post_manually(
cc: vec![].into(),
};
let post = entities::post::ActiveModel {
id: Set(uuid::Uuid::now_v7().to_string()),
creator: Set(local_user.id.clone()),
content: Set(note.content.clone()),
sensitive: Set(false),
created_at: Set(Utc::now()),
local: Set(true),
updated_at: Set(Some(Utc::now())),
content_type: Set("Note".to_string()),
visibility: Set("public".to_string()),
url: Set(id.to_string()),
ap_json: Set(Some(serde_json::to_string(&note).unwrap())),
..Default::default()
};
let post = post.insert(DB.get().unwrap()).await?;
CreatePost::send(
note,
post,
target.shared_inbox_or_inbox(),
&data.to_request_data(),
)
@ -238,6 +257,8 @@ async fn main() -> actix_web::Result<(), anyhow::Error> {
.route("/{user}/inbox", web::post().to(http_post_user_inbox))
.route("/.well-known/webfinger", web::get().to(webfinger))
.service(index)
.service(fetch_post)
.service(create_activity)
})
.bind(SERVER_URL.to_string())?
.workers(num_cpus::get())

View file

@ -176,4 +176,9 @@ impl Actor for user::Model {
fn inbox(&self) -> Url {
Url::parse(&self.inbox).unwrap()
}
//TODO: Differenciate shared inbox
fn shared_inbox(&self) -> Option<Url> {
None
}
}

View file

@ -44,6 +44,12 @@ pub struct Note {
pub(crate) cc: Option<Vec<Url>>,
}
impl Note {
pub fn from_db(post: &post::Model) -> Self {
serde_json::from_str(&post.ap_json.as_ref().unwrap()).unwrap()
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Mention {
pub href: Url,

View file

@ -12,13 +12,35 @@ pub fn generate_user_id(domain: &str, uuid: &str) -> Result<Url, ParseError> {
pub fn generate_random_object_id(domain: &str) -> Result<Url, ParseError> {
let id: String = uuid::Uuid::new_v4().to_string();
Url::parse(&format!("https://{}/apbridge/object/{}", domain, id))
generate_object_id(domain, &id)
}
/// Generate a follow accept id
pub fn generate_follow_accept_id(domain: &str, db_id: i32) -> Result<Url, ParseError> {
pub fn generate_follow_accept_id(domain: &str, db_id: &str) -> Result<Url, ParseError> {
Url::parse(&format!("https://{}/apbridge/follow/{}", domain, db_id))
}
// TODO for later aprl: needs to be base64url!!!
pub fn generate_create_id(
domain: &str,
create_db_id: &str,
basesixfour_url: &str,
) -> Result<Url, ParseError> {
Url::parse(&format!(
"https://{}/apbridge/activity/follow/{}",
domain, db_id
"https://{}/apbridge/create/{}/{}",
domain, create_db_id, basesixfour_url
))
}
pub fn generate_random_create_id(domain: &str, basesixfour_url: &str) -> Result<Url, ParseError> {
let id: String = uuid::Uuid::new_v4().to_string();
generate_create_id(domain, &id, basesixfour_url)
}
pub fn base_url_encode(url: &Url) -> String {
base64_url::encode(&url.to_string())
}
pub fn base_url_decode(encoded: &str) -> String {
String::from_utf8(base64_url::decode(encoded).unwrap()).unwrap()
}