Enhance RustyCMS: Update .gitignore to include demo assets, improve admin UI dependency management, and add new translations for asset management. Implement asset date filtering and enhance content forms with asset previews. Introduce caching mechanisms for improved performance and add support for draft status in content entries.

This commit is contained in:
Peter Meier
2026-03-12 16:03:26 +01:00
parent 7795a238e1
commit 22b4367c47
24 changed files with 900 additions and 131 deletions

View File

@@ -11,6 +11,7 @@ use tower_http::trace::{DefaultOnResponse, TraceLayer};
use tracing::{info_span, Level};
use tracing_subscriber::EnvFilter;
use rustycms::api::cache::ContentCache;
use rustycms::api::handlers::AppState;
use rustycms::schema::SchemaRegistry;
use rustycms::store::{filesystem::FileStore, sqlite::SqliteStore, ContentStore};
@@ -35,16 +36,50 @@ struct Cli {
host: String,
}
/// Auto-detect locale subdirectories in the content dir.
/// Matches 2-3 letter directory names (e.g. "de", "en", "fra") that contain at
/// least one subdirectory themselves, ignoring known non-locale dirs like "assets".
fn detect_locales(content_dir: &std::path::Path) -> Option<Vec<String>> {
let entries = std::fs::read_dir(content_dir).ok()?;
let mut locales: Vec<String> = entries
.filter_map(|e| e.ok())
.filter(|e| e.file_type().map(|ft| ft.is_dir()).unwrap_or(false))
.filter_map(|e| {
let name = e.file_name().to_string_lossy().to_string();
if name == "assets" || name.starts_with('.') {
return None;
}
let is_locale = (2..=3).contains(&name.len()) && name.chars().all(|c| c.is_ascii_lowercase());
if !is_locale {
return None;
}
let has_subdirs = std::fs::read_dir(e.path())
.ok()
.map(|rd| rd.filter_map(|e| e.ok()).any(|e| e.file_type().map(|ft| ft.is_dir()).unwrap_or(false)))
.unwrap_or(false);
if has_subdirs { Some(name) } else { None }
})
.collect();
if locales.is_empty() {
return None;
}
locales.sort();
tracing::info!("Auto-detected locales from content directory: {:?}", locales);
Some(locales)
}
fn reload_schemas(
types_dir: &PathBuf,
server_url: &str,
registry: &Arc<RwLock<SchemaRegistry>>,
openapi_spec: &Arc<RwLock<serde_json::Value>>,
cache: &Arc<ContentCache>,
) {
let types_dir = types_dir.clone();
let server_url = server_url.to_string();
let registry = Arc::clone(registry);
let openapi_spec = Arc::clone(openapi_spec);
let cache = Arc::clone(cache);
std::thread::spawn(move || {
let rt = tokio::runtime::Handle::current();
rt.block_on(async move {
@@ -53,7 +88,8 @@ fn reload_schemas(
let spec = rustycms::api::openapi::generate_spec(&new_registry, &server_url);
*registry.write().await = new_registry;
*openapi_spec.write().await = spec;
tracing::info!("Hot-reload: schemas and OpenAPI spec updated");
cache.invalidate_all().await;
tracing::info!("Hot-reload: schemas and OpenAPI spec updated, content cache cleared");
}
Err(e) => {
tracing::error!("Hot-reload failed: {}", e);
@@ -129,7 +165,8 @@ async fn main() -> anyhow::Result<()> {
let locales: Option<Vec<String>> = std::env::var("RUSTYCMS_LOCALES")
.ok()
.map(|s| s.split(',').map(|l| l.trim().to_string()).filter(|l| !l.is_empty()).collect())
.filter(|v: &Vec<String>| !v.is_empty());
.filter(|v: &Vec<String>| !v.is_empty())
.or_else(|| detect_locales(&cli.content_dir));
if let Some(ref locs) = locales {
tracing::info!("Multilingual: locales {:?} (default: {})", locs, &locs[0]);
}
@@ -147,7 +184,7 @@ async fn main() -> anyhow::Result<()> {
openapi_spec: Arc::clone(&openapi_spec),
types_dir: cli.types_dir.clone(),
api_key,
cache,
cache: Arc::clone(&cache),
transform_cache,
http_client,
locales,
@@ -187,9 +224,9 @@ async fn main() -> anyhow::Result<()> {
let _watcher = watcher;
while rx.recv().is_ok() {
// Debounce: wait for editor to finish writing, drain extra events, then reload once
std::thread::sleep(Duration::from_millis(500));
std::thread::sleep(Duration::from_millis(800));
while rx.try_recv().is_ok() {}
reload_schemas(&types_dir_watch, &server_url_watch, &registry, &openapi_spec);
reload_schemas(&types_dir_watch, &server_url_watch, &registry, &openapi_spec, &cache);
}
});
tracing::info!("Hot-reload: watching {}", cli.types_dir.display());