Enhance documentation and admin UI: Add detailed implementation guidelines in CLAUDE.md, introduce a referrer index in README.md, and update admin UI translations for improved user experience. Update package dependencies for better functionality and performance.
This commit is contained in:
@@ -11,6 +11,7 @@ use axum::Json;
|
||||
use serde_json::{json, Value};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::referrers::{Referrer, ReferrerIndex};
|
||||
use crate::schema::types::{SchemaDefinition, VALID_FIELD_TYPES};
|
||||
use crate::schema::validator;
|
||||
use crate::schema::SchemaRegistry;
|
||||
@@ -53,6 +54,10 @@ pub struct AppState {
|
||||
pub stores: Option<HashMap<String, Arc<dyn ContentStore>>>,
|
||||
/// Assets dir per environment when environments is set. Key = env name.
|
||||
pub assets_dirs: Option<HashMap<String, PathBuf>>,
|
||||
/// Reverse index for "who references this entry?". Updated on create/update/delete. None when environments are used.
|
||||
pub referrer_index: Option<Arc<RwLock<ReferrerIndex>>>,
|
||||
/// Path to persist referrer index (e.g. content/_referrers.json).
|
||||
pub referrer_index_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@@ -726,6 +731,7 @@ pub async fn create_entry(
|
||||
|
||||
// Normalize reference arrays: objects with _slug → slug strings (so clients can send resolved refs)
|
||||
validator::normalize_reference_arrays(&schema, &mut body);
|
||||
validator::normalize_multi_select(&schema, &mut body);
|
||||
|
||||
// Validate against schema (type checks, constraints, strict mode, …)
|
||||
let errors = validator::validate_content(&schema, &body);
|
||||
@@ -766,6 +772,27 @@ pub async fn create_entry(
|
||||
.map_err(ApiError::from)?;
|
||||
state.cache.invalidate_collection(&env, &collection).await;
|
||||
|
||||
// Update referrer index: this entry references (ref_coll, ref_slug)
|
||||
if let (Some(ref idx), Some(ref path)) = (&state.referrer_index, &state.referrer_index_path) {
|
||||
let refs = validator::extract_references(&schema, &body);
|
||||
let referrer = Referrer {
|
||||
collection: collection.clone(),
|
||||
slug: slug.clone(),
|
||||
field: String::new(), // we add per (ref_coll, ref_slug, field) below
|
||||
locale: locale_ref.map(str::to_string),
|
||||
};
|
||||
let mut index = idx.write().await;
|
||||
for (ref_coll, ref_slug, field) in refs {
|
||||
let mut r = referrer.clone();
|
||||
r.field = field;
|
||||
index.add_referrer(&ref_coll, &ref_slug, r);
|
||||
}
|
||||
drop(index);
|
||||
if let Err(e) = idx.read().await.save(path) {
|
||||
tracing::warn!("Failed to save referrer index: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Return created entry (with reference format)
|
||||
let entry = store
|
||||
.get(&collection, &slug, locale_ref)
|
||||
@@ -846,6 +873,7 @@ pub async fn update_entry(
|
||||
|
||||
// Normalize reference arrays: objects with _slug → slug strings (so clients can send resolved refs)
|
||||
validator::normalize_reference_arrays(&schema, &mut body);
|
||||
validator::normalize_multi_select(&schema, &mut body);
|
||||
|
||||
// Load existing content for readonly check
|
||||
let existing = store
|
||||
@@ -902,6 +930,28 @@ pub async fn update_entry(
|
||||
.map_err(ApiError::from)?;
|
||||
state.cache.invalidate_collection(&env, &collection).await;
|
||||
|
||||
// Update referrer index: remove old refs from this entry, add new refs
|
||||
if let (Some(ref idx), Some(ref path)) = (&state.referrer_index, &state.referrer_index_path) {
|
||||
let mut index = idx.write().await;
|
||||
index.remove_all_referrers_from(&collection, &slug);
|
||||
let refs = validator::extract_references(&schema, &body);
|
||||
let referrer = Referrer {
|
||||
collection: collection.clone(),
|
||||
slug: slug.clone(),
|
||||
field: String::new(),
|
||||
locale: locale_ref.map(str::to_string),
|
||||
};
|
||||
for (ref_coll, ref_slug, field) in refs {
|
||||
let mut r = referrer.clone();
|
||||
r.field = field;
|
||||
index.add_referrer(&ref_coll, &ref_slug, r);
|
||||
}
|
||||
drop(index);
|
||||
if let Err(e) = idx.read().await.save(path) {
|
||||
tracing::warn!("Failed to save referrer index: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Return updated entry (with reference format)
|
||||
let entry = store
|
||||
.get(&collection, &slug, locale_ref)
|
||||
@@ -933,6 +983,22 @@ pub async fn update_entry(
|
||||
Ok(Json(formatted))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GET /api/content/:collection/:slug/referrers
|
||||
// ---------------------------------------------------------------------------
|
||||
pub async fn get_referrers(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Path((collection, slug)): Path<(String, String)>,
|
||||
) -> Result<Json<Value>, ApiError> {
|
||||
let referrers = match &state.referrer_index {
|
||||
Some(idx) => idx.read().await.get_referrers(&collection, &slug),
|
||||
None => vec![],
|
||||
};
|
||||
Ok(Json(
|
||||
serde_json::to_value(&referrers).unwrap_or_else(|_| json!([])),
|
||||
))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GET /api/locales
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -982,6 +1048,17 @@ pub async fn delete_entry(
|
||||
)));
|
||||
}
|
||||
|
||||
// Update referrer index: remove this entry from every (ref_coll, ref_slug) it referenced; and remove key (collection, slug)
|
||||
if let (Some(ref idx), Some(ref path)) = (&state.referrer_index, &state.referrer_index_path) {
|
||||
let mut index = idx.write().await;
|
||||
index.remove_all_referrers_from(&collection, &slug);
|
||||
index.remove_referenced_entry(&collection, &slug);
|
||||
drop(index);
|
||||
if let Err(e) = idx.read().await.save(path) {
|
||||
tracing::warn!("Failed to save referrer index: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
store
|
||||
.delete(&collection, &slug, locale_ref)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user