fix(appview): add CorsLayer so Tauri webview can hit /api/*

The Tauri webview's origin (the Vite dev server on port 1430, or
the bundled tauri:// / asset:// origin in production) is
cross-origin against the AppView's listen address (port 2584).
Without an `Access-Control-Allow-Origin` response header the
browser blocks the fetch before `response.json()` runs and the
UI surfaces the failure as a SyntaxError on /api/profile.

The previous workaround (a Tauri command that returns the
AppView base URL) got us to the right URL but didn't address the
underlying CORS preflight failure. Add `tower_http::cors::CorsLayer`
with `allow_origin(Any)` to the AppView router — the AppView's
public read endpoints carry no auth cookie and the service runs
adjacent to the user's own PDS rather than the open internet,
so any-origin is safe. Production deployments behind a reverse
proxy can tighten the allow list at the proxy.
This commit is contained in:
tomdebone
2026-07-18 19:04:28 +02:00
parent e6aa28ca4c
commit aba84cbaa9
+17
View File
@@ -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)
}