systemprompt_content/
homepage_prerenderer.rs1use std::path::PathBuf;
7use std::sync::Arc;
8
9use async_trait::async_trait;
10use systemprompt_cloud::constants::storage;
11use systemprompt_models::ContentConfigRaw;
12use systemprompt_provider_contracts::{
13 PagePrepareContext, PagePrerenderer, PageRenderSpec, ProviderError, ProviderResult, WebConfig,
14};
15
16fn resolve_content_raw<'a>(
17 ctx: &'a PagePrepareContext<'_>,
18) -> ProviderResult<&'a ContentConfigRaw> {
19 ctx.content_config::<ContentConfigRaw>().ok_or_else(|| {
20 ProviderError::Configuration("ContentConfig not available in context".into())
21 })
22}
23
24const PAGE_TYPE: &str = "homepage";
25const TEMPLATE_NAME: &str = "homepage";
26const OUTPUT_FILE: &str = "index.html";
27
28#[derive(Debug, Clone, Copy, Default)]
29pub struct DefaultHomepagePrerenderer;
30
31impl DefaultHomepagePrerenderer {
32 #[must_use]
33 pub const fn new() -> Self {
34 Self
35 }
36
37 fn extract_branding(
38 web_config: &WebConfig,
39 content_config: &ContentConfigRaw,
40 ) -> HomepageBranding {
41 let org = &content_config.metadata.structured_data.organization;
42 let branding = &web_config.branding;
43
44 HomepageBranding {
45 org_name: org.name.clone(),
46 org_url: org.url.clone(),
47 org_logo: org.logo.clone(),
48 logo_path: branding
49 .logo
50 .primary
51 .svg
52 .clone()
53 .unwrap_or_else(String::new),
54 favicon_path: branding.favicon.clone(),
55 twitter_handle: branding.twitter_handle.clone(),
56 display_sitename: branding.display_sitename,
57 }
58 }
59}
60
61struct HomepageBranding {
62 org_name: String,
63 org_url: String,
64 org_logo: String,
65 logo_path: String,
66 favicon_path: String,
67 twitter_handle: String,
68 display_sitename: bool,
69}
70
71#[async_trait]
72impl PagePrerenderer for DefaultHomepagePrerenderer {
73 fn page_type(&self) -> &str {
74 PAGE_TYPE
75 }
76
77 fn priority(&self) -> u32 {
78 100
79 }
80
81 async fn prepare(
82 &self,
83 ctx: &PagePrepareContext<'_>,
84 ) -> ProviderResult<Option<PageRenderSpec>> {
85 let content_config = resolve_content_raw(ctx)?;
86
87 let branding = Self::extract_branding(ctx.web_config, content_config);
88
89 let base_data = serde_json::json!({
90 "site": ctx.web_config,
91 "ORG_NAME": branding.org_name,
92 "ORG_URL": branding.org_url,
93 "ORG_LOGO": branding.org_logo,
94 "LOGO_PATH": branding.logo_path,
95 "FAVICON_PATH": branding.favicon_path,
96 "TWITTER_HANDLE": branding.twitter_handle,
97 "DISPLAY_SITENAME": branding.display_sitename,
98 "HEADER_CTA_URL": "/",
99 "JS_BASE_PATH": format!("/{}", storage::JS),
100 "CSS_BASE_PATH": format!("/{}", storage::CSS)
101 });
102
103 Ok(Some(PageRenderSpec::new(
104 TEMPLATE_NAME,
105 base_data,
106 PathBuf::from(OUTPUT_FILE),
107 )))
108 }
109}
110
111pub fn default_homepage_prerenderer() -> Arc<dyn PagePrerenderer> {
112 Arc::new(DefaultHomepagePrerenderer::new())
113}