Fix canonical media links in preview and generation
This commit is contained in:
@@ -1218,6 +1218,105 @@ mod tests {
|
|||||||
assert!(pagefind_body.contains("window.pagefind"));
|
assert!(pagefind_body.contains("window.pagefind"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preview_rewrites_media_aliases_and_serves_the_canonical_file() {
|
||||||
|
let _guard = preview_port_guard().lock().unwrap();
|
||||||
|
let (dir, db) = setup_preview_fixture();
|
||||||
|
std::fs::create_dir_all(dir.path().join("media/2022/11")).unwrap();
|
||||||
|
std::fs::write(
|
||||||
|
dir.path().join("media/2022/11/canonical-id.jpg"),
|
||||||
|
b"canonical image",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
queries::media::insert_media(
|
||||||
|
db.conn(),
|
||||||
|
&Media {
|
||||||
|
id: "canonical-id".into(),
|
||||||
|
project_id: "project-1".into(),
|
||||||
|
filename: "canonical-id.jpg".into(),
|
||||||
|
original_name: "20221111_0177.jpg".into(),
|
||||||
|
mime_type: "image/jpeg".into(),
|
||||||
|
size: 15,
|
||||||
|
width: Some(100),
|
||||||
|
height: Some(80),
|
||||||
|
title: None,
|
||||||
|
alt: None,
|
||||||
|
caption: None,
|
||||||
|
author: None,
|
||||||
|
language: None,
|
||||||
|
file_path: "media/2022/11/canonical-id.jpg".into(),
|
||||||
|
sidecar_path: "media/2022/11/canonical-id.jpg.meta".into(),
|
||||||
|
checksum: None,
|
||||||
|
tags: Vec::new(),
|
||||||
|
created_at: 1_668_124_800_000,
|
||||||
|
updated_at: 1_668_124_800_000,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let markdown = "[Open image](/media/2022/11/20221111_0177.jpg?download=1#preview)\n\n";
|
||||||
|
let mut post = make_draft_post();
|
||||||
|
post.content = Some(markdown.into());
|
||||||
|
queries::post::insert_post(db.conn(), &post).unwrap();
|
||||||
|
|
||||||
|
let server = start_preview_server(
|
||||||
|
dir.path().join("bds.db"),
|
||||||
|
dir.path().to_path_buf(),
|
||||||
|
"project-1".into(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
let preview_html = client
|
||||||
|
.get(format!(
|
||||||
|
"http://{PREVIEW_HOST}:{PREVIEW_PORT}/__draft/post-1"
|
||||||
|
))
|
||||||
|
.send()
|
||||||
|
.unwrap()
|
||||||
|
.text()
|
||||||
|
.unwrap();
|
||||||
|
let image_response = client
|
||||||
|
.get(format!(
|
||||||
|
"http://{PREVIEW_HOST}:{PREVIEW_PORT}/media/2022/11/canonical-id.jpg?download=1"
|
||||||
|
))
|
||||||
|
.send()
|
||||||
|
.unwrap();
|
||||||
|
let image_status = image_response.status();
|
||||||
|
let image_content_type = image_response
|
||||||
|
.headers()
|
||||||
|
.get(header::CONTENT_TYPE)
|
||||||
|
.unwrap()
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
let image_body = image_response.bytes().unwrap();
|
||||||
|
server.stop().unwrap();
|
||||||
|
|
||||||
|
let mut published_post = post;
|
||||||
|
published_post.status = PostStatus::Published;
|
||||||
|
let generated = crate::render::build_site_render_artifacts(
|
||||||
|
db.conn(),
|
||||||
|
dir.path(),
|
||||||
|
"project-1",
|
||||||
|
&make_metadata(),
|
||||||
|
&[(published_post, markdown.into())],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let generated_html = &generated
|
||||||
|
.pages
|
||||||
|
.iter()
|
||||||
|
.find(|page| page.url_path == "/2024/03/09/hello")
|
||||||
|
.unwrap()
|
||||||
|
.html;
|
||||||
|
let expected = "href=\"/media/2022/11/canonical-id.jpg?download=1#preview\"";
|
||||||
|
|
||||||
|
assert!(preview_html.contains(expected));
|
||||||
|
assert!(generated_html.contains(expected));
|
||||||
|
assert!(preview_html.contains("src=\"/media/2022/11/canonical-id.jpg\""));
|
||||||
|
assert!(generated_html.contains("src=\"/media/2022/11/canonical-id.jpg\""));
|
||||||
|
assert_eq!(image_status, StatusCode::OK);
|
||||||
|
assert_eq!(image_content_type, "image/jpeg");
|
||||||
|
assert_eq!(image_body.as_ref(), b"canonical image");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn preview_server_serves_style_preview() {
|
fn preview_server_serves_style_preview() {
|
||||||
let _guard = preview_port_guard().lock().unwrap();
|
let _guard = preview_port_guard().lock().unwrap();
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use crate::render::{
|
|||||||
};
|
};
|
||||||
use crate::scripting::{CoreHost, HostApi, UnavailableHost};
|
use crate::scripting::{CoreHost, HostApi, UnavailableHost};
|
||||||
use crate::util::frontmatter::{read_script_file, read_template_file, read_translation_file};
|
use crate::util::frontmatter::{read_script_file, read_template_file, read_translation_file};
|
||||||
use crate::util::slugify;
|
use crate::util::{slugify, year_month_from_unix_ms};
|
||||||
|
|
||||||
const STARTER_SINGLE_POST_TEMPLATE: &str =
|
const STARTER_SINGLE_POST_TEMPLATE: &str =
|
||||||
include_str!("../../../../assets/starter-templates/single-post.liquid");
|
include_str!("../../../../assets/starter-templates/single-post.liquid");
|
||||||
@@ -187,6 +187,7 @@ fn build_site_render_artifacts_with_mode(
|
|||||||
.map(|media| (media.id.clone(), media))
|
.map(|media| (media.id.clone(), media))
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
let project_media = media_items.iter().map(media_context).collect::<Vec<_>>();
|
let project_media = media_items.iter().map(media_context).collect::<Vec<_>>();
|
||||||
|
let canonical_media_map = canonical_media_paths(&media_items);
|
||||||
let project_tags = build_published_tag_counts(published_posts, &tags);
|
let project_tags = build_published_tag_counts(published_posts, &tags);
|
||||||
|
|
||||||
let mut artifacts = SiteRenderArtifacts::default();
|
let mut artifacts = SiteRenderArtifacts::default();
|
||||||
@@ -238,6 +239,7 @@ fn build_site_render_artifacts_with_mode(
|
|||||||
&category_settings,
|
&category_settings,
|
||||||
&menu_items,
|
&menu_items,
|
||||||
&post_data_json_by_id,
|
&post_data_json_by_id,
|
||||||
|
&canonical_media_map,
|
||||||
&project_media,
|
&project_media,
|
||||||
&project_tags,
|
&project_tags,
|
||||||
&bundle,
|
&bundle,
|
||||||
@@ -331,6 +333,7 @@ fn build_site_render_artifacts_with_mode(
|
|||||||
&canonical_map,
|
&canonical_map,
|
||||||
&menu_items,
|
&menu_items,
|
||||||
&post_data_json_by_id,
|
&post_data_json_by_id,
|
||||||
|
&canonical_media_map,
|
||||||
&project_media,
|
&project_media,
|
||||||
&project_tags,
|
&project_tags,
|
||||||
&bundle,
|
&bundle,
|
||||||
@@ -805,6 +808,7 @@ fn render_list_route(
|
|||||||
category_settings: &HashMap<String, CategorySettings>,
|
category_settings: &HashMap<String, CategorySettings>,
|
||||||
menu_items: &[Value],
|
menu_items: &[Value],
|
||||||
post_data_json_by_id: &HashMap<String, Value>,
|
post_data_json_by_id: &HashMap<String, Value>,
|
||||||
|
canonical_media_path_by_source_path: &HashMap<String, String>,
|
||||||
project_media: &[Value],
|
project_media: &[Value],
|
||||||
project_tags: &[Value],
|
project_tags: &[Value],
|
||||||
bundle: &TemplateBundle,
|
bundle: &TemplateBundle,
|
||||||
@@ -849,7 +853,7 @@ fn render_list_route(
|
|||||||
"total_items": route.total_items,
|
"total_items": route.total_items,
|
||||||
"items_per_page": route.items_per_page,
|
"items_per_page": route.items_per_page,
|
||||||
"canonical_post_path_by_slug": canonical_map,
|
"canonical_post_path_by_slug": canonical_map,
|
||||||
"canonical_media_path_by_source_path": canonical_media_paths(posts),
|
"canonical_media_path_by_source_path": canonical_media_path_by_source_path,
|
||||||
"post_data_json_by_id": post_data_json_by_id,
|
"post_data_json_by_id": post_data_json_by_id,
|
||||||
"project": { "media": project_media },
|
"project": { "media": project_media },
|
||||||
"Tags": project_tags,
|
"Tags": project_tags,
|
||||||
@@ -883,6 +887,7 @@ fn render_post_route(
|
|||||||
canonical_post_path_by_slug: &HashMap<String, String>,
|
canonical_post_path_by_slug: &HashMap<String, String>,
|
||||||
menu_items: &[Value],
|
menu_items: &[Value],
|
||||||
post_data_json_by_id: &HashMap<String, Value>,
|
post_data_json_by_id: &HashMap<String, Value>,
|
||||||
|
canonical_media_path_by_source_path: &HashMap<String, String>,
|
||||||
project_media: &[Value],
|
project_media: &[Value],
|
||||||
project_tags: &[Value],
|
project_tags: &[Value],
|
||||||
bundle: &TemplateBundle,
|
bundle: &TemplateBundle,
|
||||||
@@ -959,7 +964,7 @@ fn render_post_route(
|
|||||||
"tag_color_by_name": taxonomy_counts.2,
|
"tag_color_by_name": taxonomy_counts.2,
|
||||||
"backlinks": backlinks,
|
"backlinks": backlinks,
|
||||||
"canonical_post_path_by_slug": canonical_post_path_by_slug,
|
"canonical_post_path_by_slug": canonical_post_path_by_slug,
|
||||||
"canonical_media_path_by_source_path": canonical_media_paths(all_posts),
|
"canonical_media_path_by_source_path": canonical_media_path_by_source_path,
|
||||||
"post_data_json_by_id": post_data_json_by_id,
|
"post_data_json_by_id": post_data_json_by_id,
|
||||||
"project": { "media": project_media },
|
"project": { "media": project_media },
|
||||||
"Tags": project_tags,
|
"Tags": project_tags,
|
||||||
@@ -1229,31 +1234,22 @@ fn canonical_post_path_by_slug(
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn canonical_media_paths(posts: &[RenderPostRecord]) -> HashMap<String, String> {
|
fn canonical_media_paths(media_items: &[Media]) -> HashMap<String, String> {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
for record in posts {
|
for media in media_items {
|
||||||
for media_reference in extract_media_refs(&record.body_markdown) {
|
let target = canonical_media_path(media);
|
||||||
map.entry(media_reference.clone())
|
let (year, month) = year_month_from_unix_ms(media.created_at);
|
||||||
.or_insert_with(|| media_reference.clone());
|
for source in [
|
||||||
|
format!("media/{year}/{month}/{}", media.original_name).to_lowercase(),
|
||||||
|
format!("bds-media://{}", media.id),
|
||||||
|
media.file_path.trim_start_matches('/').to_lowercase(),
|
||||||
|
] {
|
||||||
|
map.entry(source).or_insert_with(|| target.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
map
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_media_refs(markdown: &str) -> Vec<String> {
|
|
||||||
let mut refs = Vec::new();
|
|
||||||
let mut remainder = markdown;
|
|
||||||
while let Some(index) = remainder.find("bds-media://") {
|
|
||||||
let rest = &remainder[index..];
|
|
||||||
let end = rest
|
|
||||||
.find(|ch: char| ch == ')' || ch == ']' || ch.is_whitespace())
|
|
||||||
.unwrap_or(rest.len());
|
|
||||||
refs.push(rest[..end].to_string());
|
|
||||||
remainder = &rest[end..];
|
|
||||||
}
|
|
||||||
refs
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_linked_media_by_post_id(
|
fn build_linked_media_by_post_id(
|
||||||
conn: &Connection,
|
conn: &Connection,
|
||||||
posts: &[RenderPostRecord],
|
posts: &[RenderPostRecord],
|
||||||
|
|||||||
Reference in New Issue
Block a user