Replace client-side tag URLs with collection IDs

This commit is contained in:
silverpill 2023-01-13 21:28:26 +00:00
parent be67972760
commit d09770913b
8 changed files with 55 additions and 19 deletions

View file

@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Changed
- Make `delete-emoji` command accept emoji name and hostname instead of ID.
- Replaced client-side tag URLs with collection IDs.
### Security

View file

@ -11,6 +11,7 @@ use crate::activitypub::{
local_actor_subscribers,
local_emoji_id,
local_object_id,
local_tag_collection,
},
types::{Attachment, EmojiTag, EmojiTagImage, LinkTag, SimpleTag},
vocabulary::*,
@ -25,7 +26,6 @@ use crate::models::{
users::types::User,
};
use crate::utils::files::get_file_url;
use crate::web_client::urls::get_tag_page_url;
#[allow(dead_code)]
#[derive(Serialize)]
@ -136,11 +136,11 @@ pub fn build_note(
tags.push(Tag::SimpleTag(tag));
};
for tag_name in &post.tags {
let tag_page_url = get_tag_page_url(instance_url, tag_name);
let tag_href = local_tag_collection(instance_url, tag_name);
let tag = SimpleTag {
name: format!("#{}", tag_name),
tag_type: HASHTAG.to_string(),
href: tag_page_url,
href: tag_href,
};
tags.push(Tag::SimpleTag(tag));
};
@ -327,7 +327,11 @@ mod tests {
username: "author".to_string(),
..Default::default()
};
let post = Post { author, ..Default::default() };
let post = Post {
author,
tags: vec!["test".to_string()],
..Default::default()
};
let note = build_note(INSTANCE_HOSTNAME, INSTANCE_URL, &post);
assert_eq!(
@ -345,6 +349,13 @@ mod tests {
assert_eq!(note.cc, vec![
local_actor_followers(INSTANCE_URL, "author"),
]);
assert_eq!(note.tag.len(), 1);
let tag = match note.tag[0] {
Tag::SimpleTag(ref tag) => tag,
_ => panic!(),
};
assert_eq!(tag.name, "#test");
assert_eq!(tag.href, "https://example.com/collections/tags/test");
}
#[test]

View file

@ -24,6 +24,7 @@ impl LocalActorCollection {
}
}
// Mastodon and Pleroma use the same actor ID format
pub fn local_actor_id(instance_url: &str, username: &str) -> String {
format!("{}/users/{}", instance_url, username)
}
@ -65,6 +66,10 @@ pub fn local_emoji_id(instance_url: &str, emoji_name: &str) -> String {
format!("{}/objects/emojis/{}", instance_url, emoji_name)
}
pub fn local_tag_collection(instance_url: &str, tag_name: &str) -> String {
format!("{}/collections/tags/{}", instance_url, tag_name)
}
pub fn parse_local_actor_id(
instance_url: &str,
actor_id: &str,

View file

@ -18,7 +18,11 @@ use crate::models::{
posts::queries::{get_post_by_id, get_posts_by_author},
users::queries::get_user_by_name,
};
use crate::web_client::urls::{get_post_page_url, get_profile_page_url};
use crate::web_client::urls::{
get_post_page_url,
get_profile_page_url,
get_tag_page_url,
};
use super::actors::types::{get_local_actor, get_instance_actor};
use super::builders::create_note::{
build_emoji_tag,
@ -364,6 +368,18 @@ pub async fn emoji_view(
Ok(response)
}
#[get("/collections/tags/{tag_name}")]
pub async fn tag_view(
config: web::Data<Config>,
tag_name: web::Path<String>,
) -> Result<HttpResponse, HttpError> {
let page_url = get_tag_page_url(&config.instance_url(), &tag_name);
let response = HttpResponse::Found()
.append_header(("Location", page_url))
.finish();
Ok(response)
}
#[cfg(test)]
mod tests {
use actix_web::http::{

View file

@ -159,6 +159,7 @@ async fn main() -> std::io::Result<()> {
.service(activitypub::instance_actor_scope())
.service(activitypub::object_view)
.service(activitypub::emoji_view)
.service(activitypub::tag_view)
.service(atom::get_atom_feed)
.service(nodeinfo::get_nodeinfo)
.service(nodeinfo::get_nodeinfo_2_0)

View file

@ -285,7 +285,7 @@ pub async fn search(
posts,
).await?;
let hashtags = tags.into_iter()
.map(Tag::from_tag_name)
.map(|tag_name| Tag::from_tag_name(&config.instance_url(), tag_name))
.collect();
Ok(SearchResults { accounts, statuses, hashtags })
}

View file

@ -2,6 +2,7 @@ use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::activitypub::identifiers::local_tag_collection;
use crate::mastodon_api::accounts::types::Account;
use crate::mastodon_api::media::types::Attachment;
use crate::models::{
@ -39,11 +40,11 @@ pub struct Tag {
}
impl Tag {
pub fn from_tag_name(tag_name: String) -> Self {
pub fn from_tag_name(instance_url: &str, tag_name: String) -> Self {
let tag_url = local_tag_collection(instance_url, &tag_name);
Tag {
name: tag_name,
// TODO: add link to tag page
url: "".to_string(),
url: tag_url,
}
}
}
@ -108,7 +109,7 @@ impl Status {
.map(|item| Mention::from_profile(item, instance_url))
.collect();
let tags: Vec<Tag> = post.tags.into_iter()
.map(Tag::from_tag_name)
.map(|tag_name| Tag::from_tag_name(instance_url, tag_name))
.collect();
let emojis: Vec<CustomEmoji> = post.emojis.into_iter()
.map(|emoji| CustomEmoji::from_db(instance_url, emoji))

View file

@ -1,7 +1,7 @@
use regex::{Captures, Regex};
use crate::activitypub::identifiers::local_tag_collection;
use crate::errors::ValidationError;
use crate::web_client::urls::get_tag_page_url;
use super::links::is_inside_code_block;
const HASHTAG_RE: &str = r"(?m)(?P<before>^|\s|>|[\(])#(?P<tag>[^\s<]+)";
@ -45,11 +45,11 @@ pub fn replace_hashtags(instance_url: &str, text: &str, tags: &[String]) -> Stri
let tag_name = tag.to_lowercase();
let after = secondary_caps["after"].to_string();
if tags.contains(&tag_name) {
let tag_page_url = get_tag_page_url(instance_url, &tag_name);
let tag_url = local_tag_collection(instance_url, &tag_name);
return format!(
r#"{}<a class="hashtag" href="{}">#{}</a>{}"#,
before,
tag_page_url,
tag_url,
tag,
after,
);
@ -101,13 +101,14 @@ mod tests {
let output = replace_hashtags(INSTANCE_URL, TEXT_WITH_TAGS, &tags);
let expected_output = concat!(
r#"@user1@server1 some text <a class="hashtag" href="https://example.com/tag/testtag">#TestTag</a>."#, "\n",
r#"<a class="hashtag" href="https://example.com/tag/tag1">#TAG1</a> <a class="hashtag" href="https://example.com/tag/tag1">#tag1</a> "#,
r#"@user1@server1 some text <a class="hashtag" href="https://example.com/collections/tags/testtag">#TestTag</a>."#, "\n",
r#"<a class="hashtag" href="https://example.com/collections/tags/tag1">#TAG1</a> "#,
r#"<a class="hashtag" href="https://example.com/collections/tags/tag1">#tag1</a> "#,
r#"#test_underscore #test*special "#,
r#"more text (<a class="hashtag" href="https://example.com/tag/tag2">#tag2</a>) text "#,
r#"<a class="hashtag" href="https://example.com/tag/tag3">#tag3</a>, "#,
r#"<a class="hashtag" href="https://example.com/tag/tag4">#tag4</a>:<br>"#,
r#"end with <a class="hashtag" href="https://example.com/tag/tag5">#tag5</a>"#,
r#"more text (<a class="hashtag" href="https://example.com/collections/tags/tag2">#tag2</a>) text "#,
r#"<a class="hashtag" href="https://example.com/collections/tags/tag3">#tag3</a>, "#,
r#"<a class="hashtag" href="https://example.com/collections/tags/tag4">#tag4</a>:<br>"#,
r#"end with <a class="hashtag" href="https://example.com/collections/tags/tag5">#tag5</a>"#,
);
assert_eq!(output, expected_output);
}