diff --git a/crates/appview/src/routes.rs b/crates/appview/src/routes.rs index 243d42f..8995f0a 100644 --- a/crates/appview/src/routes.rs +++ b/crates/appview/src/routes.rs @@ -22,6 +22,7 @@ use axum::{ use chrono::{DateTime, TimeZone, Utc}; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; +use tower_http::cors::{Any, CorsLayer}; use crate::state::AppState; @@ -31,6 +32,21 @@ pub mod types; use types::{PostRow, PostRowWithIndexed, ProfileResponse, SearchResponse, TimelineResponse}; pub fn router(state: AppState) -> Router { + // CORS: the Tauri webview's origin is the Vite dev server + // (`http://127.0.0.1:1430`) in dev or the bundled `tauri://` / + // `asset://` origin in production. Either way it's a cross-origin + // fetch against this service's `http://127.0.0.1:2584` listen + // address, so the browser blocks the response without an explicit + // allow-origin header. We allow any origin — the AppView's + // public read endpoints (`/api/...`) carry no auth cookie and + // the AppView runs alongside the user's own PDS, not on the + // open internet; production deployments behind a reverse proxy + // can tighten this via the proxy itself. + let cors = CorsLayer::new() + .allow_origin(Any) + .allow_methods(Any) + .allow_headers(Any); + Router::new() .route("/", get(root)) .route("/api/timeline/home", get(timeline_home)) @@ -40,6 +56,7 @@ pub fn router(state: AppState) -> Router { .route("/api/post/*uri", get(post_by_uri)) .route("/healthz", get(healthz)) .route("/internal/ingest-commit", post(crate::ingest::ingest_commit)) + .layer(cors) .with_state(state) }