RustyCMS: file-based headless CMS — API, Admin UI (content, types, assets), Docker/Caddy, image transform; only demo type and demo content in version control
Made-with: Cursor
This commit is contained in:
45
src/main.rs
45
src/main.rs
@@ -8,7 +8,7 @@ use tokio::sync::RwLock;
|
||||
use axum::http::header::HeaderValue;
|
||||
use tower_http::cors::{AllowOrigin, CorsLayer};
|
||||
use tower_http::trace::{DefaultOnResponse, TraceLayer};
|
||||
use tracing::Level;
|
||||
use tracing::{info_span, Level};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use rustycms::api::handlers::AppState;
|
||||
@@ -19,11 +19,11 @@ use rustycms::store::{filesystem::FileStore, sqlite::SqliteStore, ContentStore};
|
||||
#[command(name = "rustycms", about = "A file-based headless CMS written in Rust")]
|
||||
struct Cli {
|
||||
/// Path to the directory containing type definitions (*.json5)
|
||||
#[arg(long, default_value = "./types")]
|
||||
#[arg(long, default_value = "./types", env = "RUSTYCMS_TYPES_DIR")]
|
||||
types_dir: PathBuf,
|
||||
|
||||
/// Path to the directory containing content files
|
||||
#[arg(long, default_value = "./content")]
|
||||
#[arg(long, default_value = "./content", env = "RUSTYCMS_CONTENT_DIR")]
|
||||
content_dir: PathBuf,
|
||||
|
||||
/// Port to listen on
|
||||
@@ -93,17 +93,11 @@ async fn main() -> anyhow::Result<()> {
|
||||
.unwrap_or_else(|_| "sqlite:content.db".into());
|
||||
tracing::info!("Using SQLite store: {}", url);
|
||||
let s = SqliteStore::new(&url).await?;
|
||||
for name in registry.collection_names() {
|
||||
s.ensure_collection_dir(&name).await?;
|
||||
}
|
||||
std::sync::Arc::new(s)
|
||||
}
|
||||
_ => {
|
||||
tracing::info!("Using file store: {}", cli.content_dir.display());
|
||||
let s = FileStore::new(&cli.content_dir);
|
||||
for name in registry.collection_names() {
|
||||
s.ensure_collection_dir(&name).await?;
|
||||
}
|
||||
std::sync::Arc::new(s)
|
||||
}
|
||||
}
|
||||
@@ -140,6 +134,13 @@ async fn main() -> anyhow::Result<()> {
|
||||
tracing::info!("Multilingual: locales {:?} (default: {})", locs, &locs[0]);
|
||||
}
|
||||
|
||||
let assets_dir = cli.content_dir.join("assets");
|
||||
|
||||
// RUSTYCMS_BASE_URL is the public URL of the API (e.g. https://api.example.com).
|
||||
// Used to expand relative /api/assets/ paths to absolute URLs in responses.
|
||||
// Falls back to the local server_url (http://host:port).
|
||||
let base_url = std::env::var("RUSTYCMS_BASE_URL").unwrap_or_else(|_| server_url.clone());
|
||||
|
||||
let state = Arc::new(AppState {
|
||||
registry: Arc::clone(®istry),
|
||||
store,
|
||||
@@ -150,6 +151,8 @@ async fn main() -> anyhow::Result<()> {
|
||||
transform_cache,
|
||||
http_client,
|
||||
locales,
|
||||
assets_dir,
|
||||
base_url,
|
||||
});
|
||||
|
||||
// Hot-reload: watch types_dir and reload schemas on change
|
||||
@@ -203,11 +206,25 @@ async fn main() -> anyhow::Result<()> {
|
||||
Err(_) => CorsLayer::permissive(),
|
||||
};
|
||||
|
||||
let trace = TraceLayer::new_for_http().on_response(
|
||||
DefaultOnResponse::new()
|
||||
.level(Level::INFO)
|
||||
.latency_unit(tower_http::LatencyUnit::Millis),
|
||||
);
|
||||
let trace = TraceLayer::new_for_http()
|
||||
.make_span_with(|request: &axum::http::Request<axum::body::Body>| {
|
||||
let method = request.method().as_str();
|
||||
let uri = request
|
||||
.uri()
|
||||
.path_and_query()
|
||||
.map(|pq| pq.as_str().to_string())
|
||||
.unwrap_or_else(|| request.uri().path().to_string());
|
||||
info_span!(
|
||||
"request",
|
||||
method = %method,
|
||||
uri = %uri,
|
||||
)
|
||||
})
|
||||
.on_response(
|
||||
DefaultOnResponse::new()
|
||||
.level(Level::INFO)
|
||||
.latency_unit(tower_http::LatencyUnit::Millis),
|
||||
);
|
||||
let app = rustycms::api::routes::create_router(state)
|
||||
.layer(cors)
|
||||
.layer(trace);
|
||||
|
||||
Reference in New Issue
Block a user