diff --git a/fluxer_app_proxy/src/routes/spa_index.rs b/fluxer_app_proxy/src/routes/spa_index.rs
index 041a390ae..44dffcd3a 100644
--- a/fluxer_app_proxy/src/routes/spa_index.rs
+++ b/fluxer_app_proxy/src/routes/spa_index.rs
@@ -167,6 +167,11 @@ async fn serve_spa_index(state: &AppState, headers: &HeaderMap) -> Response {
Ok(content) => content,
Err(response) => return response,
};
+ let raw_html = if is_self_hosted(&discovery) {
+ strip_link_preview_metadata(&raw_html)
+ } else {
+ raw_html
+ };
let dev_buster = should_bust_dev_assets.then(current_dev_asset_cache_buster);
let html = match render_spa_document(
@@ -234,6 +239,36 @@ fn render_spa_document(
Ok(document)
}
+fn is_self_hosted(discovery: &DiscoveryResponse) -> bool {
+ discovery
+ .data
+ .get("features")
+ .and_then(|features| features.get("self_hosted"))
+ .and_then(serde_json::Value::as_bool)
+ .unwrap_or(false)
+}
+
+fn strip_link_preview_metadata(html: &str) -> String {
+ let html = remove_elements(html, "
");
+ remove_elements(&html, r#"")
+}
+
+fn remove_elements(html: &str, start: &str, end: &str) -> String {
+ let mut rest = html;
+ let mut output = String::with_capacity(html.len());
+
+ while let Some(index) = rest.find(start) {
+ let Some(length) = rest[index..].find(end) else {
+ break;
+ };
+ output.push_str(&rest[..index]);
+ rest = rest[index + length + end.len()..].trim_start_matches('\n');
+ }
+
+ output.push_str(rest);
+ output
+}
+
async fn refresh_discovery_for_spa(state: &AppState) -> Option {
state
.discovery_cache
@@ -722,6 +757,57 @@ mod tests {
const DISCOVERY_BODY_WITHOUT_ENDPOINTS: &str = r#"{"api_code_version":"proxy-test"}"#;
+ const DISCOVERY_BODY_SELF_HOSTED: &str = r#"{"api_code_version":"proxy-test","endpoints":{"static_cdn":"https://cdn.example.test","media":"https://media.example.test"},"features":{"self_hosted":true}}"#;
+
+ const SHIPPED_APP_SHELL: &str = include_str!("../../../fluxer_app/index.html");
+
+ #[test]
+ fn the_shipped_shell_loses_every_link_preview_field_when_stripped() {
+ assert!(SHIPPED_APP_SHELL.contains(""));
+ assert!(SHIPPED_APP_SHELL.contains(r#""));
+ }
+
+ #[tokio::test]
+ async fn a_self_hosted_instance_serves_no_link_preview_metadata() {
+ let state = assemble_spa_state(
+ ReleaseChannel::Stable,
+ Some(SHIPPED_APP_SHELL),
+ DISCOVERY_BODY_SELF_HOSTED,
+ None,
+ None,
+ )
+ .await;
+
+ let response = serve_spa_index(&state, &HeaderMap::new()).await;
+ assert_eq!(response.status(), StatusCode::OK);
+ let served = read_document(response).await;
+
+ assert!(!served.contains("Fluxer"));
+ assert!(served.contains(r#" String {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();