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