Implement replay-safe authorized public HTTP API

This commit is contained in:
ms2k 2026-07-17 21:23:18 +09:00
commit 4c93c25692
2 changed files with 3940 additions and 345 deletions

View file

@ -48,6 +48,51 @@ pub struct MutationOutcome {
pub response: Value,
}
pub async fn observe_mutation(
pool: &PgPool,
scope: MutationScope<'_>,
kind: &str,
eligibility_claim: &[u8],
request_fingerprint: RequestFingerprint,
) -> Result<Option<MutationOutcome>, DurableError> {
let existing = sqlx::query_as::<_, (String, Vec<u8>, Vec<u8>, String, Value)>(
"SELECT kind, eligibility_claim, request_fingerprint, response_status, response_body
FROM mutation_records
WHERE scope_subject = $1
AND scope_operation = $2
AND operation_key = $3",
)
.bind(scope.subject.as_str())
.bind(scope.operation)
.bind(scope.key)
.fetch_optional(pool)
.await?;
existing
.map(
|(
stored_kind,
stored_eligibility_claim,
stored_request_fingerprint,
response_status,
response_body,
)| {
observe_existing(
kind,
eligibility_claim,
request_fingerprint.as_bytes(),
StoredRecord {
kind: stored_kind,
eligibility_claim: stored_eligibility_claim,
request_fingerprint: stored_request_fingerprint,
response_status,
response_body,
},
)
},
)
.transpose()
}
#[derive(Clone, Debug)]
struct StoredRecord {
kind: String,

View file

@ -19,6 +19,7 @@ pub use cursor::{CursorCodec, KeysetCursor, KeysetPosition, first_page_cursor, n
pub use error::DurableError;
pub use ledger::{
MutationDisposition, MutationOutcome, MutationStatus, execute_mutation, execute_mutation_in_tx,
observe_mutation,
};
pub use migrate::{migrate, migrator};
pub use outbox::{