mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
fix(self-host): unfurl media hosted by the instance itself (#2451)
This commit is contained in:
@@ -112,10 +112,13 @@ impl MediaProxyClient {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn external_proxy_url(&self, input_url: &str) -> Option<String> {
|
||||
if input_url == self.public_endpoint
|
||||
pub fn is_own_url(&self, input_url: &str) -> bool {
|
||||
input_url == self.public_endpoint
|
||||
|| input_url.starts_with(&format!("{}/", self.public_endpoint))
|
||||
{
|
||||
}
|
||||
|
||||
pub fn external_proxy_url(&self, input_url: &str) -> Option<String> {
|
||||
if self.is_own_url(input_url) {
|
||||
return Some(input_url.to_owned());
|
||||
}
|
||||
let parsed = Url::parse(input_url).ok()?;
|
||||
@@ -160,6 +163,20 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn own_urls_are_recognised_by_their_public_endpoint() {
|
||||
let mp = MediaProxyClient::new_with_public_endpoint(
|
||||
"http://media-proxy:8080",
|
||||
"secret",
|
||||
Some("https://chat.example.test/media/"),
|
||||
reqwest::Client::new(),
|
||||
);
|
||||
assert!(mp.is_own_url("https://chat.example.test/media"));
|
||||
assert!(mp.is_own_url("https://chat.example.test/media/attachments/1/2/cat.gif"));
|
||||
assert!(!mp.is_own_url("https://chat.example.test/mediafiles/1.gif"));
|
||||
assert!(!mp.is_own_url("https://static.klipy.com/ii/c8/28/HkAKKCzZ.webp"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn external_proxy_url_uses_public_endpoint_and_plain_path() {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use crate::direct_media::{MediaKind, detect_media_kind};
|
||||
use crate::media_proxy::{MediaProxyClient, embed_media_flags};
|
||||
use crate::media_proxy::{MediaMetadata, MediaProxyClient, embed_media_flags};
|
||||
use crate::types::{EmbedMedia, MessageEmbed, NsfwMode};
|
||||
use url::Url;
|
||||
|
||||
@@ -71,9 +71,28 @@ pub async fn build_direct_media_embed(
|
||||
nsfw_mode: NsfwMode,
|
||||
kind: MediaKind,
|
||||
) -> anyhow::Result<MessageEmbed> {
|
||||
let url_str = url.to_string();
|
||||
let nsfw_str = MediaProxyClient::nsfw_mode_str(nsfw_mode);
|
||||
let meta = media_proxy.get_metadata(&url_str, nsfw_str).await?;
|
||||
let meta = media_proxy.get_metadata(url.as_str(), nsfw_str).await?;
|
||||
Ok(media_embed(url, &meta, kind))
|
||||
}
|
||||
|
||||
pub async fn build_own_media_embed(
|
||||
media_proxy: &MediaProxyClient,
|
||||
url: &Url,
|
||||
nsfw_mode: NsfwMode,
|
||||
) -> anyhow::Result<Option<MessageEmbed>> {
|
||||
let nsfw_str = MediaProxyClient::nsfw_mode_str(nsfw_mode);
|
||||
let meta = media_proxy.get_metadata(url.as_str(), nsfw_str).await?;
|
||||
let Some(kind) =
|
||||
media_kind_from_content_type(&meta.content_type).or_else(|| detect_media_kind(url))
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(Some(media_embed(url, &meta, kind)))
|
||||
}
|
||||
|
||||
fn media_embed(url: &Url, meta: &MediaMetadata, kind: MediaKind) -> MessageEmbed {
|
||||
let url_str = url.to_string();
|
||||
let media = EmbedMedia {
|
||||
url: Some(url_str.clone()),
|
||||
content_type: Some(meta.content_type.clone()),
|
||||
@@ -82,7 +101,7 @@ pub async fn build_direct_media_embed(
|
||||
height: meta.height,
|
||||
placeholder: meta.placeholder.clone(),
|
||||
duration: meta.duration.map(|duration| duration as u32),
|
||||
flags: embed_media_flags(&meta),
|
||||
flags: embed_media_flags(meta),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@@ -104,7 +123,7 @@ pub async fn build_direct_media_embed(
|
||||
}
|
||||
};
|
||||
embed.url = Some(url_str);
|
||||
Ok(embed)
|
||||
embed
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -121,6 +121,10 @@ impl UnfurlShard {
|
||||
) -> anyhow::Result<UnfurlResult> {
|
||||
let parsed = Url::parse(url_str)?;
|
||||
|
||||
if self.media_proxy.is_own_url(parsed.as_str()) {
|
||||
return Ok(self.resolve_own_media(&parsed, nsfw_mode).await);
|
||||
}
|
||||
|
||||
let (fetch_url, matched_resolver_idx) = self.find_transform(&parsed);
|
||||
|
||||
let ctx = ResolveContext {
|
||||
@@ -177,6 +181,25 @@ impl UnfurlShard {
|
||||
})
|
||||
}
|
||||
|
||||
async fn resolve_own_media(&self, url: &Url, nsfw_mode: NsfwMode) -> UnfurlResult {
|
||||
match resolvers::media::build_own_media_embed(&self.media_proxy, url, nsfw_mode).await {
|
||||
Ok(Some(embed)) => self.finalize_result(ResolverResult {
|
||||
embeds: vec![embed],
|
||||
}),
|
||||
Ok(None) => UnfurlResult {
|
||||
embeds: Vec::new(),
|
||||
cache_ttl_seconds: None,
|
||||
},
|
||||
Err(err) => {
|
||||
tracing::warn!(error = %err, url = %url, "own media unfurl failed");
|
||||
UnfurlResult {
|
||||
embeds: Vec::new(),
|
||||
cache_ttl_seconds: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_transform(&self, url: &Url) -> (Url, Option<usize>) {
|
||||
for (i, resolver) in self.resolvers.iter().enumerate() {
|
||||
if let Some(transformed) = resolver.transform_url(url) {
|
||||
|
||||
Reference in New Issue
Block a user