Compare commits

...

3 commits

Author SHA1 Message Date
e3831650ca
feat: add frontend url to users 2024-05-29 21:37:21 +02:00
3fee57891d
feat: add unique index on relations 2024-05-29 21:37:09 +02:00
69cff08b5b
fix(uriproxy): users -> actors 2024-05-29 21:36:55 +02:00
14 changed files with 62 additions and 25 deletions

View file

@ -0,0 +1,31 @@
use sea_orm_migration::prelude::*;
use super::m20240524_000002_create_relations_likes_shares::Relations;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_index(
Index::create()
.unique()
.name("index-relations-follower-following")
.table(Relations::Table)
.col(Relations::Following)
.col(Relations::Follower)
.to_owned()
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_index(Index::drop().name("index-relations-follower-following").table(Relations::Table).to_owned())
.await?;
Ok(())
}
}

View file

@ -5,6 +5,7 @@ mod m20240524_000002_create_relations_likes_shares;
mod m20240524_000003_create_users_auth_and_config;
mod m20240524_000004_create_addressing_deliveries;
mod m20240524_000005_create_attachments_tags_mentions;
mod m20240529_000001_add_relation_unique_index;
pub struct Migrator;
@ -17,6 +18,7 @@ impl MigratorTrait for Migrator {
Box::new(m20240524_000003_create_users_auth_and_config::Migration),
Box::new(m20240524_000004_create_addressing_deliveries::Migration),
Box::new(m20240524_000005_create_attachments_tags_mentions::Migration),
Box::new(m20240529_000001_add_relation_unique_index::Migration),
]
}
}

View file

@ -70,6 +70,10 @@ pub async fn view(
user = user.set_following_count(None);
}
if let Some(ref fe) = ctx.cfg().instance.frontend {
user = user.set_url(Node::link(format!("{fe}/actors/{id}")));
}
Ok(JsonLD(user.ld_context()))
},
// remote user

View file

@ -111,7 +111,7 @@ impl Context {
/// get full user id uri
pub fn uid(&self, id: &str) -> String {
uriproxy::uri(self.base(), UriClass::User, id)
uriproxy::uri(self.base(), UriClass::Actor, id)
}
/// get full object id uri

View file

@ -2,7 +2,7 @@ use base64::Engine;
#[derive(Clone, Copy)]
pub enum UriClass {
User,
Actor,
Object,
Activity,
Context,
@ -11,7 +11,7 @@ pub enum UriClass {
impl AsRef<str> for UriClass {
fn as_ref(&self) -> &str {
match self {
Self::User => "users",
Self::Actor => "actors",
Self::Object => "objects",
Self::Activity => "activities",
Self::Context => "context",
@ -41,7 +41,7 @@ pub fn decompose_id(full_id: &str) -> String {
full_id // https://example.org/actors/test/followers/page?offset=42
.replace("https://", "")
.replace("http://", "")
.split('/') // ['example.org', 'users', 'test', 'followers', 'page?offset=42' ]
.split('/') // ['example.org', 'actors', 'test', 'followers', 'page?offset=42' ]
.nth(2) // 'test'
.unwrap_or("")
.to_string()

View file

@ -14,7 +14,7 @@ pub fn ActivityLine(activity: crate::Object) -> impl IntoView {
let actor = CACHE.get_or(&actor_id, serde_json::Value::String(actor_id.clone()).into());
let kind = activity.activity_type().unwrap_or(apb::ActivityType::Activity);
let href = match kind {
apb::ActivityType::Follow => Uri::web(U::User, &object_id),
apb::ActivityType::Follow => Uri::web(U::Actor, &object_id),
// TODO for update check what's being updated
_ => Uri::web(U::Object, &object_id),
};

View file

@ -14,7 +14,7 @@ pub fn LoginBox(
view! {
<div>
<div class="w-100" class:hidden=move || !auth.present() >
"hi "<a href={move || Uri::web(U::User, &auth.username() )} >{move || auth.username() }</a>
"hi "<a href={move || Uri::web(U::Actor, &auth.username() )} >{move || auth.username() }</a>
<input style="float:right" type="submit" value="logout" on:click=move |_| {
token_tx.set(None);
home_tl.reset(format!("{URL_BASE}/outbox/page"));

View file

@ -86,7 +86,7 @@ pub fn PostBox(advanced: WriteSignal<bool>) -> impl IntoView {
mentions.get()
.map(|x| x.into_iter().map(|u| match CACHE.get(&u) {
Some(u) => view! { <span class="nowrap"><span class="emoji mr-s ml-s">"📨"</span><ActorStrip object=u /></span> }.into_view(),
None => view! { <span class="nowrap"><span class="emoji mr-s ml-s">"📨"</span><a href={Uri::web(U::User, &u)}>{u}</a></span> }.into_view(),
None => view! { <span class="nowrap"><span class="emoji mr-s ml-s">"📨"</span><a href={Uri::web(U::Actor, &u)}>{u}</a></span> }.into_view(),
})
.collect_view())
}

View file

@ -211,7 +211,7 @@ async fn process_activities(activities: Vec<serde_json::Value>, auth: Auth) -> V
if let Some(object_id) = activity.object().id() {
if !gonna_fetch.contains(&object_id) {
let fetch_kind = match activity_type {
apb::ActivityType::Follow => U::User,
apb::ActivityType::Follow => U::Actor,
_ => U::Object,
};
gonna_fetch.insert(object_id.clone());
@ -235,20 +235,20 @@ async fn process_activities(activities: Vec<serde_json::Value>, auth: Auth) -> V
if let Some(uid) = activity.attributed_to().id() {
if CACHE.get(&uid).is_none() && !gonna_fetch.contains(&uid) {
gonna_fetch.insert(uid.clone());
sub_tasks.push(Box::pin(fetch_and_update(U::User, uid, auth)));
sub_tasks.push(Box::pin(fetch_and_update(U::Actor, uid, auth)));
}
}
if let Some(uid) = activity.actor().id() {
if CACHE.get(&uid).is_none() && !gonna_fetch.contains(&uid) {
gonna_fetch.insert(uid.clone());
sub_tasks.push(Box::pin(fetch_and_update(U::User, uid, auth)));
sub_tasks.push(Box::pin(fetch_and_update(U::Actor, uid, auth)));
}
}
}
for user in actors_seen {
sub_tasks.push(Box::pin(fetch_and_update(U::User, user, auth)));
sub_tasks.push(Box::pin(fetch_and_update(U::Actor, user, auth)));
}
futures::future::join_all(sub_tasks).await;
@ -269,9 +269,9 @@ async fn fetch_and_update_with_user(kind: U, id: String, auth: Auth) {
if let Some(actor_id) = match kind {
U::Object => obj.attributed_to().id(),
U::Activity => obj.actor().id(),
U::User | U::Context => None,
U::Actor | U::Context => None,
} {
fetch_and_update(U::User, actor_id, auth).await;
fetch_and_update(U::Actor, actor_id, auth).await;
}
}
}

View file

@ -10,7 +10,7 @@ pub fn ActorStrip(object: crate::Object) -> impl IntoView {
let domain = object.id().unwrap_or_default().replace("https://", "").split('/').next().unwrap_or_default().to_string();
let avatar = object.icon().get().map(|x| x.url().id().unwrap_or(DEFAULT_AVATAR_URL.into())).unwrap_or(DEFAULT_AVATAR_URL.into());
view! {
<a href={Uri::web(U::User, &actor_id)} class="clean hover">
<a href={Uri::web(U::Actor, &actor_id)} class="clean hover">
<img src={avatar} class="avatar inline mr-s" /><b>{username}</b><small>@{domain}</small>
</a>
}
@ -20,11 +20,11 @@ pub fn ActorStrip(object: crate::Object) -> impl IntoView {
pub fn ActorBanner(object: crate::Object) -> impl IntoView {
match object.as_ref() {
serde_json::Value::String(id) => view! {
<div><b>?</b>" "<a class="clean hover" href={Uri::web(U::User, id)}>{Uri::pretty(id)}</a></div>
<div><b>?</b>" "<a class="clean hover" href={Uri::web(U::Actor, id)}>{Uri::pretty(id)}</a></div>
},
serde_json::Value::Object(_) => {
let uid = object.id().unwrap_or_default().to_string();
let uri = Uri::web(U::User, &uid);
let uri = Uri::web(U::Actor, &uid);
let avatar_url = object.icon().get().map(|x| x.url().id().unwrap_or(DEFAULT_AVATAR_URL.into())).unwrap_or(DEFAULT_AVATAR_URL.into());
let display_name = object.name().unwrap_or_default().to_string();
let username = object.preferred_username().unwrap_or_default().to_string();

View file

@ -84,7 +84,7 @@ pub fn DebugPage() -> impl IntoView {
" raw :: "
<a href={move|| Uri::web(U::Object, &text.get())} >obj</a>
" :: "
<a href={move|| Uri::web(U::User, &text.get())} >usr</a>
<a href={move|| Uri::web(U::Actor, &text.get())} >usr</a>
" :: "
<a href=move || cached_query().0 target="_blank" rel="nofollow noreferrer">ext</a>
" :: "

View file

@ -21,9 +21,9 @@ pub fn ObjectPage(tl: Timeline) -> impl IntoView {
let obj = Arc::new(obj);
if let Some(author) = obj.attributed_to().id() {
if let Ok(user) = Http::fetch::<serde_json::Value>(
&Uri::api(U::User, &author, true), auth
&Uri::api(U::Actor, &author, true), auth
).await {
CACHE.put(Uri::full(U::User, &author), Arc::new(user));
CACHE.put(Uri::full(U::Actor, &author), Arc::new(user));
}
}
CACHE.put(Uri::full(U::Object, &oid), obj.clone());

View file

@ -11,7 +11,7 @@ pub fn SearchPage() -> impl IntoView {
let user = create_local_resource(
move || use_query_map().get().get("q").cloned().unwrap_or_default(),
move |q| {
let user_fetch = Uri::api(U::User, &q, true);
let user_fetch = Uri::api(U::Actor, &q, true);
async move { Some(Arc::new(Http::fetch::<serde_json::Value>(&user_fetch, auth).await.ok()?)) }
}
);

View file

@ -27,16 +27,16 @@ pub fn UserPage(tl: Timeline) -> impl IntoView {
.get("id")
.cloned()
.unwrap_or_default();
let uid = uriproxy::uri(URL_BASE, uriproxy::UriClass::User, &id);
let uid = uriproxy::uri(URL_BASE, uriproxy::UriClass::Actor, &id);
let _uid = uid.clone();
let actor = create_local_resource(move || _uid.clone(), move |id| {
async move {
match CACHE.get(&Uri::full(U::User, &id)) {
match CACHE.get(&Uri::full(U::Actor, &id)) {
Some(x) => Some(x.clone()),
None => {
let user : serde_json::Value = Http::fetch(&Uri::api(U::User, &id, true), auth).await.ok()?;
let user : serde_json::Value = Http::fetch(&Uri::api(U::Actor, &id, true), auth).await.ok()?;
let user = Arc::new(user);
CACHE.put(Uri::full(U::User, &id), user.clone());
CACHE.put(Uri::full(U::Actor, &id), user.clone());
Some(user)
},
}
@ -80,7 +80,7 @@ pub fn UserPage(tl: Timeline) -> impl IntoView {
let following = object.following_count().unwrap_or(0);
let followers = object.followers_count().unwrap_or(0);
let statuses = object.statuses_count().unwrap_or(0);
let tl_url = format!("{}/outbox/page", Uri::api(U::User, &id.clone(), false));
let tl_url = format!("{}/outbox/page", Uri::api(U::Actor, &id.clone(), false));
if !tl.next.get().starts_with(&tl_url) {
tl.reset(tl_url);
}