Magazi is a content distribution platform that gates access to files using ATProtocol (Bluesky) identity and cryptographic proofs. download.ngerakines.me/
atprotocol appview atprotocol-attestations
11
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 109 lines 4.2 kB view raw
1use std::fs; 2use std::path::PathBuf; 3 4use anyhow::{Context, Result}; 5use atproto_identity::key::{KeyData, identify_key}; 6use serde::{Deserialize, Serialize}; 7 8use crate::entitlements::default_requirements; 9 10/// A catalog entry from catalog.json (without download URL). 11#[derive(Clone, Debug, Serialize, Deserialize)] 12pub struct CatalogEntry { 13 pub id: String, 14 pub name: String, 15 pub description: String, 16 /// Optional content type override for download and getBlob responses. 17 /// When set, this value is used instead of mime_guess inference. 18 #[serde(skip_serializing_if = "Option::is_none")] 19 pub content_type: Option<String>, 20 /// Optional FontAwesome icon class (e.g., "fa-solid fa-book"). 21 /// When not set, defaults to "fa-regular fa-file". 22 #[serde(skip_serializing_if = "Option::is_none")] 23 pub icon: Option<String>, 24 /// JSONLogic rule for determining entitlement to this catalog item. 25 /// When not set, uses the default requirements (authenticated + supporter proof + broker proof). 26 #[serde(default = "default_requirements")] 27 pub requirements: serde_json::Value, 28} 29 30#[derive(Clone)] 31pub struct Config { 32 pub http_port: u16, 33 pub http_external: String, 34 pub cookie_key: Vec<u8>, 35 pub oauth_client_credentials: KeyData, 36 pub creator_identity: String, 37 pub broker_identity: String, 38 pub download_key: KeyData, 39 pub files_path: PathBuf, 40 pub catalog: Vec<CatalogEntry>, 41} 42 43impl Config { 44 pub fn from_env() -> Result<Self> { 45 let http_port: u16 = std::env::var("HTTP_PORT") 46 .context("HTTP_PORT not set")? 47 .parse() 48 .context("HTTP_PORT must be a valid port number")?; 49 50 let http_external = std::env::var("HTTP_EXTERNAL").context("HTTP_EXTERNAL not set")?; 51 52 let cookie_key_b64 = std::env::var("HTTP_COOKIE_KEY").context("HTTP_COOKIE_KEY not set")?; 53 let cookie_key = 54 base64::Engine::decode(&base64::engine::general_purpose::STANDARD, &cookie_key_b64) 55 .context("HTTP_COOKIE_KEY must be valid base64")?; 56 57 let oauth_client_credentials_str = std::env::var("OAUTH_CLIENT_CREDENTIALS") 58 .context("OAUTH_CLIENT_CREDENTIALS not set")?; 59 let oauth_client_credentials = identify_key(&oauth_client_credentials_str) 60 .map_err(|e| anyhow::anyhow!("Invalid OAUTH_CLIENT_CREDENTIALS: {}", e))?; 61 62 let creator_identity = 63 std::env::var("CREATOR_IDENTITY").context("CREATOR_IDENTITY not set")?; 64 65 let broker_identity = 66 std::env::var("BROKER_IDENTITY").context("BROKER_IDENTITY not set")?; 67 68 let download_key_str = std::env::var("DOWNLOAD_KEY").context("DOWNLOAD_KEY not set")?; 69 let download_key = identify_key(&download_key_str) 70 .map_err(|e| anyhow::anyhow!("Invalid DOWNLOAD_KEY: {}", e))?; 71 72 let files_path = PathBuf::from(std::env::var("FILES").context("FILES not set")?); 73 74 let catalog_path = PathBuf::from(std::env::var("CATALOG").context("CATALOG not set")?); 75 let catalog_contents = fs::read_to_string(&catalog_path) 76 .with_context(|| format!("Failed to read catalog file: {}", catalog_path.display()))?; 77 let catalog: Vec<CatalogEntry> = serde_json::from_str(&catalog_contents) 78 .with_context(|| format!("Failed to parse catalog file: {}", catalog_path.display()))?; 79 80 Ok(Config { 81 http_port, 82 http_external, 83 cookie_key, 84 oauth_client_credentials, 85 creator_identity, 86 broker_identity, 87 download_key, 88 files_path, 89 catalog, 90 }) 91 } 92 93 pub fn client_id(&self) -> String { 94 format!("https://{}/oauth-client-metadata.json", self.http_external) 95 } 96 97 pub fn redirect_uri(&self) -> String { 98 format!("https://{}/login/callback", self.http_external) 99 } 100 101 pub fn external_url(&self) -> String { 102 format!("https://{}", self.http_external) 103 } 104 105 /// Find a catalog entry by its ID (CID). 106 pub fn find_catalog_entry(&self, id: &str) -> Option<&CatalogEntry> { 107 self.catalog.iter().find(|e| e.id == id) 108 } 109}