mirror of
https://github.com/versia-pub/activitypub.git
synced 2025-12-06 06:38:20 +01:00
Merge branch 'test1'
This commit is contained in:
commit
b52024d726
10
Cargo.lock
generated
10
Cargo.lock
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
35
migration/src/m20240626_030922_store_ap_json_in_posts.rs
Normal file
35
migration/src/m20240626_030922_store_ap_json_in_posts.rs
Normal 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,
|
||||
}
|
||||
|
|
@ -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 {}", ¬e.attributed_to);
|
||||
let encoded_url = base_url_encode(¬e.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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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?;
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
23
src/main.rs
23
src/main.rs
|
|
@ -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(¬e).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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
30
src/utils.rs
30
src/utils.rs
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue