This commit is contained in:
Andras Schmelczer 2026-05-17 10:16:30 +01:00
parent 47d89f6fad
commit 017902b8e6
82 changed files with 331466 additions and 54841 deletions

View file

@ -2,6 +2,7 @@
mod aggregation;
mod auth;
mod bugsink;
mod checkout_sessions;
mod consts;
mod data;
@ -29,6 +30,7 @@ use axum::Router;
use clap::Parser;
use consts::SERVICE_CALL_TIMEOUT;
use tower::limit::ConcurrencyLimitLayer;
use tower::ServiceBuilder;
use tower_http::compression::CompressionLayer;
use tower_http::cors::{AllowHeaders, AllowMethods, CorsLayer};
@ -223,10 +225,87 @@ struct Cli {
/// Google OAuth client secret for PocketBase SSO
#[arg(long, env = "GOOGLE_OAUTH_CLIENT_SECRET")]
google_oauth_client_secret: String,
/// Bugsink DSN for backend error reporting
#[arg(long, env = "BUGSINK_DSN", hide_env_values = true)]
bugsink_dsn: Option<String>,
/// Bugsink DSN injected into the browser app; falls back to BUGSINK_DSN when omitted
#[arg(long, env = "FRONTEND_BUGSINK_DSN", hide_env_values = true)]
frontend_bugsink_dsn: Option<String>,
/// Bugsink/Sentry environment name
#[arg(long, env = "BUGSINK_ENVIRONMENT")]
bugsink_environment: Option<String>,
/// Bugsink/Sentry release name
#[arg(long, env = "BUGSINK_RELEASE")]
bugsink_release: Option<String>,
/// Include default PII in Bugsink events
#[arg(long, env = "BUGSINK_SEND_DEFAULT_PII", default_value_t = false)]
bugsink_send_default_pii: bool,
}
async fn capture_server_error_responses(
request: axum::extract::Request,
next: middleware::Next,
) -> axum::response::Response {
let method = request.method().clone();
let path = request.uri().path().to_owned();
let response = next.run(request).await;
let status = response.status();
if status.is_server_error() {
sentry::with_scope(
|scope| {
scope.set_tag("http.status_code", status.as_u16().to_string());
scope.set_tag("http.method", method.to_string());
scope.set_tag("http.route", path.clone());
},
|| {
sentry::capture_message(
&format!("HTTP {status} response from {method} {path}"),
sentry::Level::Error,
);
},
);
}
response
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
let bugsink_environment = cli
.bugsink_environment
.clone()
.or_else(|| bugsink::env_nonempty("SENTRY_ENVIRONMENT"));
let bugsink_release = cli
.bugsink_release
.clone()
.or_else(|| bugsink::env_nonempty("SENTRY_RELEASE"));
let backend_bugsink_dsn = cli
.bugsink_dsn
.clone()
.or_else(|| bugsink::env_nonempty("SENTRY_DSN"));
let _bugsink_guard = bugsink::init_backend(&bugsink::BackendConfig {
dsn: backend_bugsink_dsn.clone(),
environment: bugsink_environment.clone(),
release: bugsink_release.clone(),
send_default_pii: cli.bugsink_send_default_pii,
});
let bugsink_frontend_config = bugsink::frontend_config(
cli.frontend_bugsink_dsn
.clone()
.or_else(|| bugsink::env_nonempty("PUBLIC_BUGSINK_DSN"))
.or(backend_bugsink_dsn),
bugsink_environment.clone(),
bugsink_release.clone(),
cli.bugsink_send_default_pii,
);
let file_appender = tracing_appender::rolling::daily("logs", "server.log");
let (non_blocking, _guard) = tracing_appender::non_blocking(file_appender);
@ -234,6 +313,7 @@ async fn main() -> anyhow::Result<()> {
tracing_subscriber::registry()
.with(env_filter)
.with(sentry::integrations::tracing::layer())
.with(tracing_subscriber::fmt::layer().with_ansi(true))
.with(
tracing_subscriber::fmt::layer()
@ -245,8 +325,9 @@ async fn main() -> anyhow::Result<()> {
// Initialize Prometheus metrics
let metrics_handle = metrics::init_metrics();
info!("Prometheus metrics initialized");
let cli = Cli::parse();
if bugsink_frontend_config.is_some() || _bugsink_guard.is_some() {
info!("Bugsink error reporting configured");
}
for (label, path) in [
("Properties", &cli.properties),
@ -483,6 +564,7 @@ async fn main() -> anyhow::Result<()> {
stripe_secret_key: cli.stripe_secret_key,
stripe_webhook_secret: cli.stripe_webhook_secret,
stripe_referral_coupon_id: cli.stripe_referral_coupon_id,
bugsink_frontend_config,
};
let shared = Arc::new(SharedState::new(app_state));
@ -670,9 +752,7 @@ async fn main() -> anyhow::Result<()> {
.route("/health", get(|| async { "ok" }))
.route(
"/metrics",
get(move |connect_info| {
metrics::metrics_handler(metrics_handle.clone(), connect_info)
}),
get(move |connect_info| metrics::metrics_handler(metrics_handle.clone(), connect_info)),
)
.with_state(shared.clone());
@ -696,9 +776,17 @@ async fn main() -> anyhow::Result<()> {
},
))
.layer(middleware::from_fn(static_cache_headers))
.layer(middleware::from_fn(capture_server_error_responses))
.layer(cors)
.layer(CompressionLayer::new().zstd(true).gzip(true))
.layer(TraceLayer::new_for_http());
.layer(TraceLayer::new_for_http())
.layer(
ServiceBuilder::new()
.layer(sentry::integrations::tower::NewSentryLayer::<
axum::extract::Request,
>::new_from_top())
.layer(sentry::integrations::tower::SentryHttpLayer::new()),
);
// Lock all current and future memory pages to prevent swapping
unsafe {