mirror of
https://github.com/modrinth/code.git
synced 2026-08-25 00:55:25 +00:00
Tilitfy webhook changes (#6267)
* Tilitfy webhook changes * add env * fix
This commit is contained in:
@@ -64,7 +64,7 @@ The website and app `prepr` commands
|
|||||||
|
|
||||||
Each project may have its own `CLAUDE.md` with detailed instructions:
|
Each project may have its own `CLAUDE.md` with detailed instructions:
|
||||||
|
|
||||||
- [`apps/labrinth/CLAUDE.md`](apps/labrinth/CLAUDE.md) — Backend API
|
- [`apps/labrinth/AGENTS.md`](apps/labrinth/AGENTS.md) — Backend API
|
||||||
- [`apps/frontend/CLAUDE.md`](apps/frontend/CLAUDE.md) - Frontend Website
|
- [`apps/frontend/CLAUDE.md`](apps/frontend/CLAUDE.md) - Frontend Website
|
||||||
|
|
||||||
## Code Guidelines
|
## Code Guidelines
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ TREMENDOUS_PRIVATE_KEY=none
|
|||||||
TREMENDOUS_CAMPAIGN_ID=none
|
TREMENDOUS_CAMPAIGN_ID=none
|
||||||
TILTIFY_CLIENT_ID=
|
TILTIFY_CLIENT_ID=
|
||||||
TILTIFY_CLIENT_SECRET=
|
TILTIFY_CLIENT_SECRET=
|
||||||
|
TILTIFY_WEBHOOK_SIGNING_KEY=
|
||||||
TILTIFY_PRIDE_26_CAMPAIGN_ID=
|
TILTIFY_PRIDE_26_CAMPAIGN_ID=
|
||||||
|
|
||||||
HCAPTCHA_SECRET=none
|
HCAPTCHA_SECRET=none
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ TREMENDOUS_PRIVATE_KEY=none
|
|||||||
TREMENDOUS_CAMPAIGN_ID=none
|
TREMENDOUS_CAMPAIGN_ID=none
|
||||||
TILTIFY_CLIENT_ID=
|
TILTIFY_CLIENT_ID=
|
||||||
TILTIFY_CLIENT_SECRET=
|
TILTIFY_CLIENT_SECRET=
|
||||||
|
TILTIFY_WEBHOOK_SIGNING_KEY=
|
||||||
TILTIFY_PRIDE_26_CAMPAIGN_ID=
|
TILTIFY_PRIDE_26_CAMPAIGN_ID=
|
||||||
|
|
||||||
HCAPTCHA_SECRET=none
|
HCAPTCHA_SECRET=none
|
||||||
|
|||||||
@@ -298,6 +298,7 @@ vars! {
|
|||||||
TILTIFY_CLIENT_ID: String = "";
|
TILTIFY_CLIENT_ID: String = "";
|
||||||
TILTIFY_CLIENT_SECRET: String = "";
|
TILTIFY_CLIENT_SECRET: String = "";
|
||||||
TILTIFY_PRIDE_26_CAMPAIGN_ID: String = "";
|
TILTIFY_PRIDE_26_CAMPAIGN_ID: String = "";
|
||||||
|
TILTIFY_WEBHOOK_SIGNING_KEY: String = "";
|
||||||
|
|
||||||
// server pinging
|
// server pinging
|
||||||
SERVER_PING_MAX_CONCURRENT: usize = 16usize;
|
SERVER_PING_MAX_CONCURRENT: usize = 16usize;
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
use actix_web::{get, post, web};
|
use actix_web::{HttpRequest, get, post, web};
|
||||||
use chrono::{DateTime, Utc};
|
use base64::Engine;
|
||||||
|
use chrono::{DateTime, Duration, Utc};
|
||||||
use eyre::eyre;
|
use eyre::eyre;
|
||||||
|
use hmac::{Hmac, Mac};
|
||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use sha2::Sha256;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use tracing::{info, warn};
|
use tracing::{info, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@@ -134,11 +137,17 @@ impl CampaignDonation {
|
|||||||
#[utoipa::path]
|
#[utoipa::path]
|
||||||
#[post("/webhook")]
|
#[post("/webhook")]
|
||||||
pub async fn tiltify_webhook(
|
pub async fn tiltify_webhook(
|
||||||
|
req: HttpRequest,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
redis: web::Data<RedisPool>,
|
redis: web::Data<RedisPool>,
|
||||||
payouts_queue: web::Data<PayoutsQueue>,
|
payouts_queue: web::Data<PayoutsQueue>,
|
||||||
web::Json(raw_payload): web::Json<serde_json::Value>,
|
body: String,
|
||||||
) -> Result<(), ApiError> {
|
) -> Result<(), ApiError> {
|
||||||
|
verify_tiltify_webhook_signature(&req, &body)?;
|
||||||
|
|
||||||
|
let raw_payload = serde_json::from_str::<serde_json::Value>(&body)
|
||||||
|
.wrap_internal_err_with(|| eyre!("invalid Tiltify webhook JSON"))?;
|
||||||
|
|
||||||
// deserialize the JSON in the request handler, not in the params,
|
// deserialize the JSON in the request handler, not in the params,
|
||||||
// since if the JSON fails to deserialize then it's *our* fault,
|
// since if the JSON fails to deserialize then it's *our* fault,
|
||||||
// not the caller's.
|
// not the caller's.
|
||||||
@@ -237,6 +246,52 @@ pub async fn tiltify_webhook(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn verify_tiltify_webhook_signature(
|
||||||
|
req: &HttpRequest,
|
||||||
|
body: &str,
|
||||||
|
) -> Result<(), ApiError> {
|
||||||
|
let signature = req
|
||||||
|
.headers()
|
||||||
|
.get("X-Tiltify-Signature")
|
||||||
|
.and_then(|x| x.to_str().ok())
|
||||||
|
.wrap_request_err("missing Tiltify webhook signature")?;
|
||||||
|
let signature = base64::engine::general_purpose::STANDARD
|
||||||
|
.decode(signature)
|
||||||
|
.wrap_request_err("invalid Tiltify webhook signature")?;
|
||||||
|
|
||||||
|
let timestamp = req
|
||||||
|
.headers()
|
||||||
|
.get("X-Tiltify-Timestamp")
|
||||||
|
.and_then(|x| x.to_str().ok())
|
||||||
|
.wrap_request_err("missing Tiltify webhook timestamp")?;
|
||||||
|
let parsed_timestamp = DateTime::parse_from_rfc3339(timestamp)
|
||||||
|
.wrap_request_err("invalid Tiltify webhook timestamp")?;
|
||||||
|
let parsed_timestamp = parsed_timestamp.with_timezone(&Utc);
|
||||||
|
let age = Utc::now().signed_duration_since(parsed_timestamp);
|
||||||
|
if age < -Duration::minutes(1) || age > Duration::minutes(1) {
|
||||||
|
return Err(ApiError::Request(eyre!(
|
||||||
|
"expired Tiltify webhook timestamp",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ENV.TILTIFY_WEBHOOK_SIGNING_KEY.is_empty() {
|
||||||
|
return Err(ApiError::Internal(eyre!(
|
||||||
|
"TILTIFY_WEBHOOK_SIGNING_KEY must be set"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut mac: Hmac<Sha256> =
|
||||||
|
Hmac::new_from_slice(ENV.TILTIFY_WEBHOOK_SIGNING_KEY.as_bytes())
|
||||||
|
.wrap_internal_err("initializing Tiltify webhook HMAC")?;
|
||||||
|
mac.update(timestamp.as_bytes());
|
||||||
|
mac.update(b".");
|
||||||
|
mac.update(body.as_bytes());
|
||||||
|
mac.verify_slice(&signature)
|
||||||
|
.wrap_request_err("invalid Tiltify webhook signature")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[utoipa::path]
|
#[utoipa::path]
|
||||||
#[get("/pride-26")]
|
#[get("/pride-26")]
|
||||||
pub async fn pride_26(
|
pub async fn pride_26(
|
||||||
|
|||||||
Reference in New Issue
Block a user