mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-25 11:19:29 -04:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aee4c653eb | ||
|
|
53c502e75c |
@@ -82,6 +82,7 @@ fn do_build_cors_config(is_production: bool) -> Cors {
|
||||
actix_http::header::ACCESS_CONTROL_ALLOW_CREDENTIALS, // https://stackoverflow.com/a/46412839
|
||||
actix_http::header::HeaderName::from_static("x-requested-with"), // Tabulator Ajax sends
|
||||
actix_http::header::HeaderName::from_static("session"), // Custom header sent by Three.js Storyteller Studio
|
||||
actix_http::header::HeaderName::from_static("x-artcraft-version"), // ArtCraft client app release, surfaced in error alerts
|
||||
])
|
||||
.max_age(3600)
|
||||
}
|
||||
|
||||
@@ -63,6 +63,12 @@ mod tests {
|
||||
media_file_token: None,
|
||||
inference_job_token: None,
|
||||
third_party_id: None,
|
||||
http_host: None,
|
||||
http_origin: None,
|
||||
http_referer: None,
|
||||
http_user_agent: None,
|
||||
artcraft_version: None,
|
||||
trace_id: None,
|
||||
request_ip_address: None,
|
||||
avt_cookie_token: None,
|
||||
session_token: None,
|
||||
@@ -228,6 +234,12 @@ mod tests {
|
||||
media_file_token: None,
|
||||
inference_job_token: None,
|
||||
third_party_id: None,
|
||||
http_host: None,
|
||||
http_origin: None,
|
||||
http_referer: None,
|
||||
http_user_agent: None,
|
||||
artcraft_version: None,
|
||||
trace_id: None,
|
||||
request_ip_address: None,
|
||||
avt_cookie_token: None,
|
||||
session_token: None,
|
||||
|
||||
@@ -37,6 +37,27 @@ pub struct NotificationDetails {
|
||||
/// HTTP status code associated with the event, if any.
|
||||
pub(crate) http_status_code: Option<u16>,
|
||||
|
||||
/// Hostname the client used to reach this server (from the `Host` or
|
||||
/// forwarded-host headers), if any. Useful when a server is reachable
|
||||
/// via multiple domains (eg. api.storyteller.ai, api.fakeyou.com).
|
||||
pub(crate) http_host: Option<String>,
|
||||
|
||||
/// `Origin` header of the request, if any. For browser XHR/fetch this is
|
||||
/// the scheme+host of the calling frontend.
|
||||
pub(crate) http_origin: Option<String>,
|
||||
|
||||
/// `Referer` header of the request, if any. For browser XHR/fetch this is
|
||||
/// the URL of the page that made the call (possibly truncated to the
|
||||
/// origin by the browser's referrer policy).
|
||||
pub(crate) http_referer: Option<String>,
|
||||
|
||||
/// `User-Agent` header of the request, if any.
|
||||
pub(crate) http_user_agent: Option<String>,
|
||||
|
||||
/// `X-ArtCraft-Version` header of the request, if any. Sent by ArtCraft
|
||||
/// clients to identify which app release made the call.
|
||||
pub(crate) artcraft_version: Option<String>,
|
||||
|
||||
/// User token associated with the event, if any.
|
||||
pub(crate) user_token: Option<String>,
|
||||
|
||||
@@ -188,6 +209,26 @@ impl NotificationDetails {
|
||||
parts.push(format!("HTTP Status Code: {}", status_code));
|
||||
}
|
||||
|
||||
if let Some(host) = &self.http_host {
|
||||
parts.push(format!("HTTP Host: {}", host));
|
||||
}
|
||||
|
||||
if let Some(origin) = &self.http_origin {
|
||||
parts.push(format!("HTTP Origin: {}", origin));
|
||||
}
|
||||
|
||||
if let Some(referer) = &self.http_referer {
|
||||
parts.push(format!("HTTP Referer: {}", referer));
|
||||
}
|
||||
|
||||
if let Some(user_agent) = &self.http_user_agent {
|
||||
parts.push(format!("User Agent: {}", user_agent));
|
||||
}
|
||||
|
||||
if let Some(version) = &self.artcraft_version {
|
||||
parts.push(format!("ArtCraft Version: {}", version));
|
||||
}
|
||||
|
||||
if let Some(ip) = &self.request_ip_address {
|
||||
parts.push(format!("Request IP: {}", ip));
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ pub struct NotificationDetailsBuilder {
|
||||
pub(crate) http_method: Option<String>,
|
||||
pub(crate) http_path: Option<String>,
|
||||
pub(crate) http_status_code: Option<u16>,
|
||||
pub(crate) http_host: Option<String>,
|
||||
pub(crate) http_origin: Option<String>,
|
||||
pub(crate) http_referer: Option<String>,
|
||||
pub(crate) http_user_agent: Option<String>,
|
||||
pub(crate) artcraft_version: Option<String>,
|
||||
|
||||
pub(crate) user_token: Option<String>,
|
||||
pub(crate) media_file_token: Option<String>,
|
||||
@@ -53,6 +58,11 @@ impl NotificationDetailsBuilder {
|
||||
http_method: None,
|
||||
http_path: None,
|
||||
http_status_code: None,
|
||||
http_host: None,
|
||||
http_origin: None,
|
||||
http_referer: None,
|
||||
http_user_agent: None,
|
||||
artcraft_version: None,
|
||||
user_token: None,
|
||||
media_file_token: None,
|
||||
inference_job_token: None,
|
||||
@@ -84,6 +94,11 @@ impl NotificationDetailsBuilder {
|
||||
http_method: None,
|
||||
http_path: None,
|
||||
http_status_code: None,
|
||||
http_host: None,
|
||||
http_origin: None,
|
||||
http_referer: None,
|
||||
http_user_agent: None,
|
||||
artcraft_version: None,
|
||||
user_token: None,
|
||||
media_file_token: None,
|
||||
inference_job_token: None,
|
||||
@@ -138,6 +153,31 @@ impl NotificationDetailsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_http_host(mut self, http_host: Option<String>) -> Self {
|
||||
self.http_host = http_host;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_http_origin(mut self, http_origin: Option<String>) -> Self {
|
||||
self.http_origin = http_origin;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_http_referer(mut self, http_referer: Option<String>) -> Self {
|
||||
self.http_referer = http_referer;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_http_user_agent(mut self, http_user_agent: Option<String>) -> Self {
|
||||
self.http_user_agent = http_user_agent;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_artcraft_version(mut self, artcraft_version: Option<String>) -> Self {
|
||||
self.artcraft_version = artcraft_version;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_user_token(mut self, user_token: Option<String>) -> Self {
|
||||
self.user_token = user_token;
|
||||
self
|
||||
@@ -202,6 +242,11 @@ impl NotificationDetailsBuilder {
|
||||
http_method: self.http_method,
|
||||
http_path: self.http_path,
|
||||
http_status_code: self.http_status_code,
|
||||
http_host: self.http_host,
|
||||
http_origin: self.http_origin,
|
||||
http_referer: self.http_referer,
|
||||
http_user_agent: self.http_user_agent,
|
||||
artcraft_version: self.artcraft_version,
|
||||
user_token: self.user_token,
|
||||
media_file_token: self.media_file_token,
|
||||
inference_job_token: self.inference_job_token,
|
||||
|
||||
+5
@@ -46,6 +46,11 @@ pub(crate) fn check_common_web_error(
|
||||
.set_http_path(Some(path.to_string()))
|
||||
.set_trace_id(metadata.trace_id.clone())
|
||||
.set_http_status_code(Some(500))
|
||||
.set_http_host(metadata.http_host.clone())
|
||||
.set_http_origin(metadata.http_origin.clone())
|
||||
.set_http_referer(metadata.http_referer.clone())
|
||||
.set_http_user_agent(metadata.http_user_agent.clone())
|
||||
.set_artcraft_version(metadata.artcraft_version.clone())
|
||||
.set_request_ip_address(metadata.request_ip_address.clone())
|
||||
.set_avt_cookie_token(metadata.avt_cookie_token.clone())
|
||||
.set_session_token(metadata.session_token.clone())
|
||||
|
||||
+5
@@ -32,6 +32,11 @@ pub(crate) fn check_status_code_fallback(
|
||||
.set_http_path(Some(path.to_string()))
|
||||
.set_trace_id(metadata.trace_id.clone())
|
||||
.set_http_status_code(Some(status_code))
|
||||
.set_http_host(metadata.http_host.clone())
|
||||
.set_http_origin(metadata.http_origin.clone())
|
||||
.set_http_referer(metadata.http_referer.clone())
|
||||
.set_http_user_agent(metadata.http_user_agent.clone())
|
||||
.set_artcraft_version(metadata.artcraft_version.clone())
|
||||
.set_request_ip_address(metadata.request_ip_address.clone())
|
||||
.set_avt_cookie_token(metadata.avt_cookie_token.clone())
|
||||
.set_session_token(metadata.session_token.clone())
|
||||
|
||||
+42
@@ -1,8 +1,10 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use actix_web::dev::ServiceRequest;
|
||||
use actix_web::http::header::{HeaderName, ORIGIN, REFERER, USER_AGENT};
|
||||
use actix_web::web;
|
||||
use actix_web::HttpMessage;
|
||||
use actix_web::HttpRequest;
|
||||
use http_server_common::request::get_request_ip::get_request_ip;
|
||||
use log::debug;
|
||||
|
||||
@@ -10,6 +12,9 @@ use trace_id::TraceId;
|
||||
|
||||
use crate::state::server_state::ServerState;
|
||||
|
||||
/// Optional header sent by ArtCraft clients identifying the app release.
|
||||
const ARTCRAFT_VERSION_HEADER: &str = "x-artcraft-version";
|
||||
|
||||
/// Optional per-request context extracted by the error alerting middleware
|
||||
/// and forwarded to each handler-specific check. Every field fails open:
|
||||
/// if extraction errors, the field is `None`.
|
||||
@@ -21,6 +26,18 @@ pub(crate) struct RequestDebuggingMetadata {
|
||||
pub session_user_token: Option<String>,
|
||||
/// Set by `TraceIdMiddleware` (outermost middleware) on every request.
|
||||
pub trace_id: Option<String>,
|
||||
/// Hostname the client used to reach this server (Host / forwarded-host
|
||||
/// headers). Distinguishes api.storyteller.ai vs api.fakeyou.com etc.
|
||||
pub http_host: Option<String>,
|
||||
/// `Origin` header — scheme+host of the frontend page making the XHR call.
|
||||
pub http_origin: Option<String>,
|
||||
/// `Referer` header — URL of the frontend page making the XHR call
|
||||
/// (may be truncated to the origin by the browser's referrer policy).
|
||||
pub http_referer: Option<String>,
|
||||
/// `User-Agent` header.
|
||||
pub http_user_agent: Option<String>,
|
||||
/// `X-ArtCraft-Version` header — app release of the ArtCraft client, if sent.
|
||||
pub artcraft_version: Option<String>,
|
||||
}
|
||||
|
||||
impl RequestDebuggingMetadata {
|
||||
@@ -62,12 +79,37 @@ impl RequestDebuggingMetadata {
|
||||
.map(|payload| (Some(payload.session_token), payload.maybe_user_token))
|
||||
.unwrap_or((None, None));
|
||||
|
||||
// `connection_info()` prefers Forwarded / X-Forwarded-Host over the raw
|
||||
// Host header, so this reflects the hostname the client actually used
|
||||
// even behind the load balancer.
|
||||
let http_host = Some(http_request.connection_info().host().to_string())
|
||||
.filter(|host| !host.is_empty());
|
||||
|
||||
let http_origin = get_header_value(http_request, ORIGIN);
|
||||
let http_referer = get_header_value(http_request, REFERER);
|
||||
let http_user_agent = get_header_value(http_request, USER_AGENT);
|
||||
let artcraft_version =
|
||||
get_header_value(http_request, HeaderName::from_static(ARTCRAFT_VERSION_HEADER));
|
||||
|
||||
Self {
|
||||
request_ip_address,
|
||||
avt_cookie_token,
|
||||
session_token,
|
||||
session_user_token,
|
||||
trace_id,
|
||||
http_host,
|
||||
http_origin,
|
||||
http_referer,
|
||||
http_user_agent,
|
||||
artcraft_version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a header as a UTF-8 string; `None` if absent or not valid UTF-8.
|
||||
fn get_header_value(request: &HttpRequest, header_name: HeaderName) -> Option<String> {
|
||||
request.headers()
|
||||
.get(header_name)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(|value| value.to_string())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user