Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ listens on 127.0.0.1:3030, and should only be exposed to an external network
behind a proxy that supports both HTTP and WebSocket protocols (only tested
with `nginx`).

## Environment Variables

- `CORS_ALLOWED_ORIGINS`: Comma-separated list of allowed origins for CORS requests to the `/api/rpc` endpoint (e.g., `"https://example.com,https://app.example.com"`). Set to `"*"` to allow any origin (not recommended for production). If not set, defaults to allowing common localhost origins for development (`http://localhost:3000,http://localhost:3030,http://127.0.0.1:3000,http://127.0.0.1:3030`).

# Development

```
Expand Down
4 changes: 2 additions & 2 deletions backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = []
dynamic = ["slog-term", "tower-http"]
dynamic = ["slog-term"]

[dependencies]
anyhow = "1.0"
Expand Down Expand Up @@ -39,7 +39,7 @@ tokio = { version = "1.28", features = [
"sync",
"io-util",
] }
tower-http = { version = "0.4", features = ["fs"], optional = true }
tower-http = { version = "0.4", features = ["fs", "cors"] }
zstd = "0.12"

[dev-dependencies]
Expand Down
47 changes: 47 additions & 0 deletions backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use axum::{
extract::Path,
response::Response,
};
use http::{HeaderValue, Method};
use tower_http::cors::{AllowOrigin, CorsLayer};
#[cfg(feature = "dynamic")]
use tower_http::services::ServeDir;

Expand Down Expand Up @@ -146,7 +148,52 @@ async fn main() -> Result<(), anyhow::Error> {
)
.route("/*path", get(serve_static_routes));

// Configure CORS based on environment variables
// CORS_ALLOWED_ORIGINS: comma-separated list of allowed origins (e.g., "http://localhost:3000,https://example.com")
// Set to "*" to allow any origin (not recommended for production)
// If not set, defaults to allowing localhost origins in development
let cors = {
let allowed_origins = std::env::var("CORS_ALLOWED_ORIGINS")
.unwrap_or_else(|_| {
// Default to common development origins if not specified
"http://localhost:3000,http://localhost:3030,http://127.0.0.1:3000,http://127.0.0.1:3030".to_string()
});

if allowed_origins.trim() == "*" {
// Allow any origin (use with caution)
info!(
ROOT_LOGGER,
"CORS configured to allow ANY origin - not recommended for production"
);
CorsLayer::new()
.allow_origin(tower_http::cors::Any)
.allow_methods([Method::GET, Method::POST, Method::OPTIONS])
.allow_headers(tower_http::cors::Any)
} else {
let origins: Vec<HeaderValue> = allowed_origins
.split(',')
.filter_map(|origin| origin.trim().parse::<HeaderValue>().ok())
.collect();

if origins.is_empty() {
// If no valid origins, fall back to same-origin only
info!(
ROOT_LOGGER,
"No valid CORS origins configured, using same-origin policy"
);
CorsLayer::new()
} else {
info!(ROOT_LOGGER, "CORS origins configured: {:?}", origins);
CorsLayer::new()
.allow_origin(AllowOrigin::list(origins))
.allow_methods([Method::GET, Method::POST, Method::OPTIONS])
.allow_headers(tower_http::cors::Any)
}
}
};

let app = app
.layer(cors)
.layer(Extension(backend_storage))
.layer(Extension(stats));

Expand Down
2 changes: 1 addition & 1 deletion mechanics/src/hands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl<'de> Deserialize<'de> for Hands {
if let serde_json::Value::Object(obj) = helper.hands {
for (key, value) in obj {
let player_id = PlayerID::from_str(&key)
.map_err(|_| de::Error::custom(format!("invalid PlayerID: {}", key)))?;
.map_err(|_| de::Error::custom(format!("invalid PlayerID: {key}")))?;
let card_map: HashMap<Card, usize> =
serde_json::from_value(value).map_err(de::Error::custom)?;
hands_map.insert(player_id, card_map);
Expand Down
Loading