Skip to main content

systemprompt_content/
branding_provider.rs

1use std::sync::Arc;
2
3use anyhow::Result;
4use async_trait::async_trait;
5use serde_json::Value;
6use systemprompt_cloud::constants::storage;
7use systemprompt_models::ContentConfigRaw;
8use systemprompt_models::services::ServicesConfig;
9use systemprompt_provider_contracts::{PageContext, PageDataProvider};
10
11#[derive(Debug, Clone, Copy, Default)]
12pub struct DefaultBrandingProvider;
13
14fn resolve_content_raw<'a>(ctx: &'a PageContext<'_>) -> Result<&'a ContentConfigRaw> {
15    if let Some(services) = ctx.content_config::<ServicesConfig>() {
16        return Ok(&services.content.raw);
17    }
18    ctx.content_config::<ContentConfigRaw>()
19        .ok_or_else(|| anyhow::anyhow!("ContentConfig not available in PageContext"))
20}
21
22#[async_trait]
23impl PageDataProvider for DefaultBrandingProvider {
24    fn provider_id(&self) -> &'static str {
25        "default-branding"
26    }
27
28    async fn provide_page_data(&self, ctx: &PageContext<'_>) -> Result<Value> {
29        let content_config = resolve_content_raw(ctx)?;
30
31        let org = &content_config.metadata.structured_data.organization;
32        let branding = &ctx.web_config.branding;
33
34        Ok(serde_json::json!({
35            "ORG_NAME": org.name,
36            "ORG_URL": org.url,
37            "ORG_LOGO": org.logo,
38            "LOGO_PATH": branding.logo.primary.svg.as_deref().unwrap_or(""),
39            "FAVICON_PATH": &branding.favicon,
40            "TWITTER_HANDLE": &branding.twitter_handle,
41            "DISPLAY_SITENAME": branding.display_sitename,
42            "CSS_BASE_PATH": format!("/{}", storage::CSS),
43            "JS_BASE_PATH": format!("/{}", storage::JS),
44            "HEADER_CTA_URL": "/",
45        }))
46    }
47}
48
49pub fn default_branding_provider() -> Arc<dyn PageDataProvider> {
50    Arc::new(DefaultBrandingProvider)
51}