diff --git a/backend/src/handlers/posts.rs b/backend/src/handlers/posts.rs index 46e872d..cc9a66e 100644 --- a/backend/src/handlers/posts.rs +++ b/backend/src/handlers/posts.rs @@ -85,7 +85,7 @@ pub async fn create_post( } let images = extract_images(&payload.content); - if images.is_empty() { + if images.is_empty() && state.site_mode == crate::models::SiteMode::Atelier { return Err(AppError::BadRequest( "A gallery entry must include at least one image (![](url) in the markdown body)." .to_string(), diff --git a/backend/src/main.rs b/backend/src/main.rs index bb4c92a..692e9f1 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -20,7 +20,7 @@ use tower_http::{ use tracing::{error, info, warn}; use crate::handlers::contact::RATE_LIMIT_WINDOW_MS; -use crate::models::{ImageDim, PostInfo}; +use crate::models::{ImageDim, PostInfo, SiteMode}; pub struct CachedPost { pub info: PostInfo, @@ -31,6 +31,7 @@ pub struct AppState { pub admin_token: String, pub data_dir: PathBuf, pub cookie_secure: bool, + pub site_mode: SiteMode, pub post_lock: Mutex<()>, pub posts_cache: RwLock>, pub image_dims_cache: RwLock>, @@ -55,8 +56,25 @@ async fn main() { let cookie_secure = env::var("COOKIE_SECURE") .map(|v| v != "false" && v != "0") .unwrap_or(true); + let site_mode = env::var("SITE_MODE") + .map(|v| { + if v.to_lowercase() == "blog" { + SiteMode::Blog + } else { + SiteMode::Atelier + } + }) + .unwrap_or(SiteMode::Atelier); - info!("Initializing backend with data dir: {:?}", data_dir); + info!( + "Initializing backend with data dir: {:?}, mode: {:?}", + data_dir, + if site_mode == SiteMode::Blog { + "blog" + } else { + "atelier" + } + ); let posts_dir = data_dir.join("posts"); let uploads_dir = data_dir.join("uploads"); @@ -71,6 +89,7 @@ async fn main() { admin_token, data_dir, cookie_secure, + site_mode, post_lock: Mutex::new(()), posts_cache: RwLock::new(Vec::new()), image_dims_cache: RwLock::new(HashMap::new()), diff --git a/backend/src/models.rs b/backend/src/models.rs index 29af6e9..208760f 100644 --- a/backend/src/models.rs +++ b/backend/src/models.rs @@ -2,6 +2,19 @@ use chrono::NaiveDate; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub enum SiteMode { + Blog, + Atelier, +} + +impl Default for SiteMode { + fn default() -> Self { + Self::Atelier + } +} + #[derive(Serialize, Deserialize, Clone)] pub struct ContactLink { pub kind: String, diff --git a/docker-compose.yml b/docker-compose.yml index dae5f2d..8a11295 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,6 +17,7 @@ services: - DATA_DIR=/app/data - COOKIE_SECURE=${COOKIE_SECURE:-true} - FRONTEND_ORIGIN=${FRONTEND_ORIGIN:-} + - SITE_MODE=${SITE_MODE:-atelier} - RUST_LOG=${RUST_LOG:-info} restart: unless-stopped networks: diff --git a/frontend/src/components/react/admin/editor/usePostMeta.ts b/frontend/src/components/react/admin/editor/usePostMeta.ts index 16371ed..e67b9f0 100644 --- a/frontend/src/components/react/admin/editor/usePostMeta.ts +++ b/frontend/src/components/react/admin/editor/usePostMeta.ts @@ -51,7 +51,7 @@ export function usePostMeta({ editSlug, getContent, setContent, mode = 'atelier' notify('Title, slug, and body are required.', 'error'); return; } - if (!/!\[[^\]]*\]\([^)]+\)/.test(content)) { + if (mode === 'atelier' && !/!\[[^\]]*\]\([^)]+\)/.test(content)) { notify( 'Add at least one image before saving — drag, paste, or use the Add image button.', 'error',