mirror of
https://github.com/Such-Software/smirk-backend-core.git
synced 2026-10-20 16:58:40 +02:00
feat(config): effective config via ArcSwap + DB overlay at boot (Stage 2)
Wire the settings overlay into the live config path: - AppState.config: Config -> Arc<ArcSwap<Config>> (hot-swappable effective config) + config_base: Arc<Config> (immutable env-only). New AppState::cfg() accessor; every config read goes through it (state.cfg().field), so a runtime-safe operator edit is seen on the next request. One Config type, no parallel path. - Boot (main.rs): load_settings_overlay -> Config::apply_overlay(validate) -> build every client/worker/router from the EFFECTIVE config. A persisted overlay that no longer validates is ignored (boot env-only + loud log), never bricks. Env-only when ADMIN_KEY_INTEGRITY_SECRET is unset. - Migrated ~59 state.config.X reads to state.cfg().X across api/*; bound guards where a reference outlives the statement; integrity_secret returns owned String. - Test harness builds AppState with ArcSwap + config_base (arc-swap dev-dep). Behavior-preserving: with no operator_settings rows, effective == env config. cargo check --all-targets clean; lib tests 256/0.
This commit is contained in:
+15
-15
@@ -66,14 +66,14 @@ fn admin_manager(state: &AppState) -> Result<&AdminSessionManager, AppError> {
|
||||
fn admin_verify_url(state: &AppState) -> String {
|
||||
format!(
|
||||
"{}/admin/auth/verify",
|
||||
state.config.admin.public_url.trim_end_matches('/')
|
||||
state.cfg().admin.public_url.trim_end_matches('/')
|
||||
)
|
||||
}
|
||||
|
||||
/// A stable per-instance id bound into the signed action, so a challenge signed
|
||||
/// for this instance cannot be relayed to another.
|
||||
fn admin_instance_id(state: &AppState) -> String {
|
||||
hex::encode(Sha256::digest(state.config.admin.public_url.as_bytes()))[..16].to_string()
|
||||
hex::encode(Sha256::digest(state.cfg().admin.public_url.as_bytes()))[..16].to_string()
|
||||
}
|
||||
|
||||
fn pubkey_prefix(pubkey: &str) -> String {
|
||||
@@ -121,7 +121,7 @@ pub async fn admin_plane_guard(
|
||||
.unwrap_or("");
|
||||
// Compute the response on both paths, then apply the security headers once so
|
||||
// the 403 Host-reject carries them too.
|
||||
let mut resp = if host_allowed(host, state.config.admin.onion.as_deref()) {
|
||||
let mut resp = if host_allowed(host, state.cfg().admin.onion.as_deref()) {
|
||||
next.run(req).await
|
||||
} else {
|
||||
(StatusCode::FORBIDDEN, "forbidden").into_response()
|
||||
@@ -190,7 +190,7 @@ pub async fn admin_guard(
|
||||
|
||||
// Live, uncached allowlist re-check (MAC re-verified inside): must be active
|
||||
// AND activated (a pending key is not yet authorized for protected routes).
|
||||
let secret = &state.config.admin.key_integrity_secret;
|
||||
let secret = &state.cfg().admin.key_integrity_secret;
|
||||
let key = state
|
||||
.db
|
||||
.get_active_admin_key(&info.pubkey, secret)
|
||||
@@ -279,7 +279,7 @@ pub async fn admin_verify(
|
||||
) -> Result<Json<AdminTokenResponse>, AppError> {
|
||||
let mgr = admin_manager(&state)?;
|
||||
let ip = client_ip(&state, &headers, peer);
|
||||
let secret = &state.config.admin.key_integrity_secret;
|
||||
let secret = &state.cfg().admin.key_integrity_secret;
|
||||
|
||||
// 1. Prove the signature + bindings (purpose/nonce/descriptor/instance). The
|
||||
// descriptor binds the verify URL with an EMPTY body (the proof rides in the
|
||||
@@ -335,7 +335,7 @@ pub async fn admin_verify(
|
||||
let pair = mgr.create_token_pair(&pubkey, session_id)?;
|
||||
let refresh_hash = hash_refresh_token(
|
||||
&pair.refresh_token,
|
||||
&state.config.secrets.refresh_token_pepper,
|
||||
&state.cfg().secrets.refresh_token_pepper,
|
||||
);
|
||||
let audit = NewAdminAudit {
|
||||
action: "admin_login".into(),
|
||||
@@ -386,7 +386,7 @@ pub async fn admin_refresh(
|
||||
|
||||
// Re-authorize against the live allowlist (closes the "8h refresh is the real
|
||||
// blast radius" gap).
|
||||
let secret = &state.config.admin.key_integrity_secret;
|
||||
let secret = &state.cfg().admin.key_integrity_secret;
|
||||
let key = state
|
||||
.db
|
||||
.get_active_admin_key(&pubkey, secret)
|
||||
@@ -403,7 +403,7 @@ pub async fn admin_refresh(
|
||||
.ok_or_else(admin_auth_fail)?;
|
||||
let presented = hash_refresh_token(
|
||||
&req.refresh_token,
|
||||
&state.config.secrets.refresh_token_pepper,
|
||||
&state.cfg().secrets.refresh_token_pepper,
|
||||
);
|
||||
let hash_ok: bool = presented
|
||||
.as_bytes()
|
||||
@@ -435,7 +435,7 @@ pub async fn admin_logout(
|
||||
) -> Result<Json<OkResponse>, AppError> {
|
||||
let ctx = admin_guard(&state, &headers).await?;
|
||||
state.db.revoke_admin_session(ctx.session_id).await?;
|
||||
let secret = &state.config.admin.key_integrity_secret;
|
||||
let secret = &state.cfg().admin.key_integrity_secret;
|
||||
let _ = state
|
||||
.db
|
||||
.record_admin_audit(
|
||||
@@ -520,7 +520,7 @@ fn pending_key(state: &AppState, pubkey: String, label: Option<String>) -> NewAd
|
||||
scope: "admin".into(),
|
||||
created_by_kind: "admin".into(),
|
||||
activation_deadline: Some(
|
||||
Utc::now() + Duration::days(state.config.admin.pending_key_ttl_days as i64),
|
||||
Utc::now() + Duration::days(state.cfg().admin.pending_key_ttl_days as i64),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -536,7 +536,7 @@ pub async fn admin_keys_add(
|
||||
let pubkey = req.pubkey.to_lowercase();
|
||||
validate_admin_pubkey(&pubkey)?;
|
||||
|
||||
let secret = &state.config.admin.key_integrity_secret;
|
||||
let secret = &state.cfg().admin.key_integrity_secret;
|
||||
let audit = NewAdminAudit {
|
||||
action: "admin_key_added".into(),
|
||||
actor_kind: "admin".into(),
|
||||
@@ -552,7 +552,7 @@ pub async fn admin_keys_add(
|
||||
pending_key(&state, pubkey, req.label),
|
||||
&audit,
|
||||
secret,
|
||||
state.config.admin.max_keys as i64,
|
||||
state.cfg().admin.max_keys as i64,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
@@ -593,7 +593,7 @@ pub async fn admin_keys_revoke(
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<OkResponse>, AppError> {
|
||||
let ctx = admin_guard(&state, &headers).await?;
|
||||
let secret = &state.config.admin.key_integrity_secret;
|
||||
let secret = &state.cfg().admin.key_integrity_secret;
|
||||
let audit = NewAdminAudit {
|
||||
action: "admin_key_revoked".into(),
|
||||
actor_kind: "admin".into(),
|
||||
@@ -632,7 +632,7 @@ pub async fn admin_keys_rotate(
|
||||
let ctx = admin_guard(&state, &headers).await?;
|
||||
let pubkey = req.pubkey.to_lowercase();
|
||||
validate_admin_pubkey(&pubkey)?;
|
||||
let secret = &state.config.admin.key_integrity_secret;
|
||||
let secret = &state.cfg().admin.key_integrity_secret;
|
||||
let audit = NewAdminAudit {
|
||||
action: "admin_key_rotated".into(),
|
||||
actor_kind: "admin".into(),
|
||||
@@ -679,7 +679,7 @@ pub async fn admin_features(
|
||||
headers: HeaderMap,
|
||||
) -> Result<Json<AdminFeaturesResponse>, AppError> {
|
||||
admin_guard(&state, &headers).await?;
|
||||
let cfg = &state.config;
|
||||
let cfg = &state.cfg();
|
||||
let effective = crate::api::capabilities::effective_capabilities(cfg);
|
||||
|
||||
let mut downgrades = Vec::new();
|
||||
|
||||
+16
-16
@@ -170,15 +170,15 @@ fn validate_username(username: &str) -> Result<(), AppError> {
|
||||
/// validation only requires `ALTCHA_HMAC_KEY` when the feature is enabled). When
|
||||
/// disabled we never call `verify_payload`, so the empty key is never used.
|
||||
fn pow_applies(state: &AppState, pubkey_hash_lc: &str) -> bool {
|
||||
state.config.pow.enabled && crate::core::pow::required_for(&state.config.pow, pubkey_hash_lc)
|
||||
state.cfg().pow.enabled && crate::core::pow::required_for(&state.cfg().pow, pubkey_hash_lc)
|
||||
}
|
||||
|
||||
/// The canonical absolute URL a NIP-98 token must bind for `path` (the value of
|
||||
/// the event's `u` tag). Built from `config.identity.public_api_url`, never the
|
||||
/// request Host. Fail closed when unset (Nostr identity is disabled).
|
||||
fn nip98_url(state: &AppState, path: &str) -> Result<String, AppError> {
|
||||
let base = state
|
||||
.config
|
||||
let cfg = state.cfg();
|
||||
let base = cfg
|
||||
.identity
|
||||
.public_api_url
|
||||
.as_deref()
|
||||
@@ -215,7 +215,7 @@ async fn issue_session(
|
||||
|
||||
let refresh_token_hash = hash_refresh_token(
|
||||
&pair.refresh_token,
|
||||
&state.config.secrets.refresh_token_pepper,
|
||||
&state.cfg().secrets.refresh_token_pepper,
|
||||
);
|
||||
let expires_at = Utc::now() + state.sessions.refresh_token_expiry();
|
||||
|
||||
@@ -276,7 +276,7 @@ async fn upsert_all_keys(
|
||||
pub async fn pow_challenge(
|
||||
State(state): State<Arc<AppState>>,
|
||||
) -> Result<Json<altcha::Challenge>, AppError> {
|
||||
let challenge = crate::core::pow::issue_challenge(&state.config.pow)?;
|
||||
let challenge = crate::core::pow::issue_challenge(&state.cfg().pow)?;
|
||||
Ok(Json(challenge))
|
||||
}
|
||||
|
||||
@@ -622,13 +622,13 @@ fn enforce_pow(
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
crate::core::pow::verify_payload(&state.config.pow, solution)?;
|
||||
crate::core::pow::verify_payload(&state.cfg().pow, solution)?;
|
||||
info!(pow = "ok", "PoW solution accepted (new user)");
|
||||
} else if let Some(solution) = solution {
|
||||
// Supplied but not required: verify anyway (clear error on malformed),
|
||||
// but only if the feature is enabled so we never touch an empty key.
|
||||
if state.config.pow.enabled {
|
||||
crate::core::pow::verify_payload(&state.config.pow, solution)?;
|
||||
if state.cfg().pow.enabled {
|
||||
crate::core::pow::verify_payload(&state.cfg().pow, solution)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -723,15 +723,15 @@ async fn plan_gate_consume(
|
||||
.is_some_and(|s| !s.is_empty());
|
||||
|
||||
match plan_gates(
|
||||
state.config.registration.gate_mode,
|
||||
state.config.registration.require_invite,
|
||||
state.config.registration.payment.require_payment,
|
||||
state.cfg().registration.gate_mode,
|
||||
state.cfg().registration.require_invite,
|
||||
state.cfg().registration.payment.require_payment,
|
||||
has_invite,
|
||||
has_payment,
|
||||
) {
|
||||
GatePlan::Open => Ok(GatedRegistration { invite_code_hash: None, payment: None }),
|
||||
GatePlan::All => {
|
||||
let invite_code_hash = if state.config.registration.require_invite {
|
||||
let invite_code_hash = if state.cfg().registration.require_invite {
|
||||
Some(require_invite_hash(invite_code)?)
|
||||
} else {
|
||||
None
|
||||
@@ -776,7 +776,7 @@ async fn verify_payment_settled(
|
||||
pubkey_hash: &str,
|
||||
payment_invoice_id: Option<&str>,
|
||||
) -> Result<Option<String>, AppError> {
|
||||
if returning || !state.config.registration.payment.require_payment {
|
||||
if returning || !state.cfg().registration.payment.require_payment {
|
||||
return Ok(None);
|
||||
}
|
||||
// Gate on but no provider built — config validation prevents this, so it is
|
||||
@@ -932,7 +932,7 @@ pub async fn payment_invoice(
|
||||
State(state): State<Arc<AppState>>,
|
||||
Json(req): Json<PaymentInvoiceRequest>,
|
||||
) -> Result<Json<PaymentInvoiceResponse>, AppError> {
|
||||
let cfg = &state.config.registration.payment;
|
||||
let cfg = &state.cfg().registration.payment;
|
||||
if !cfg.require_payment {
|
||||
return Err(AppError::ValidationError(
|
||||
"This instance does not require registration payment.".into(),
|
||||
@@ -1175,7 +1175,7 @@ pub async fn refresh_token(
|
||||
|
||||
let token_hash = hash_refresh_token(
|
||||
&req.refresh_token,
|
||||
&state.config.secrets.refresh_token_pepper,
|
||||
&state.cfg().secrets.refresh_token_pepper,
|
||||
);
|
||||
let session = state
|
||||
.db
|
||||
@@ -1256,7 +1256,7 @@ pub async fn logout(
|
||||
) -> Result<Json<LogoutResponse>, AppError> {
|
||||
let token_hash = hash_refresh_token(
|
||||
&req.refresh_token,
|
||||
&state.config.secrets.refresh_token_pepper,
|
||||
&state.cfg().secrets.refresh_token_pepper,
|
||||
);
|
||||
if let Some(session) = state.db.get_session_by_token_hash(&token_hash).await? {
|
||||
let _ = state.db.revoke_session(session.id).await;
|
||||
|
||||
@@ -354,7 +354,7 @@ fn tips_advertised(config: &Config) -> bool {
|
||||
)]
|
||||
#[instrument(skip(state))]
|
||||
pub async fn capabilities(State(state): State<Arc<AppState>>) -> Json<CapabilitiesResponse> {
|
||||
Json(effective_capabilities(&state.config))
|
||||
Json(effective_capabilities(&state.cfg()))
|
||||
}
|
||||
|
||||
/// Capability route, RELATIVE to the `/api/v1` mount point. Public (no auth).
|
||||
|
||||
+14
-14
@@ -41,7 +41,7 @@ const PURPOSE_EXPORT: &str = "erasure_export";
|
||||
// ── helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
fn ensure_enabled(state: &AppState) -> Result<(), AppError> {
|
||||
if state.config.retention.erasure_enabled {
|
||||
if state.cfg().retention.erasure_enabled {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(AppError::NotFound("erasure is not enabled".into()))
|
||||
@@ -51,8 +51,8 @@ fn ensure_enabled(state: &AppState) -> Result<(), AppError> {
|
||||
/// The signed-action `u`-tag base for `path` (e.g. `/account/erasure`), from
|
||||
/// `PUBLIC_API_URL` (which already includes the `/api/v1` prefix).
|
||||
fn erasure_url(state: &AppState, path: &str) -> Result<String, AppError> {
|
||||
let base = state
|
||||
.config
|
||||
let cfg = state.cfg();
|
||||
let base = cfg
|
||||
.identity
|
||||
.public_api_url
|
||||
.as_deref()
|
||||
@@ -64,8 +64,8 @@ fn subject_hash(npub: &str) -> String {
|
||||
hex::encode(Sha256::digest(npub.as_bytes()))
|
||||
}
|
||||
|
||||
fn integrity_secret(state: &AppState) -> &str {
|
||||
&state.config.admin.key_integrity_secret
|
||||
fn integrity_secret(state: &AppState) -> String {
|
||||
state.cfg().admin.key_integrity_secret.clone()
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@@ -191,7 +191,7 @@ pub async fn request_erasure(
|
||||
Json(body): Json<ProofRequest>,
|
||||
) -> Result<Json<ErasureRequestResponse>, AppError> {
|
||||
ensure_enabled(&state)?;
|
||||
let grace = state.config.retention.grace_period_hours as i64;
|
||||
let grace = state.cfg().retention.grace_period_hours as i64;
|
||||
let (pubkey, user) = prove(&state, &body, PURPOSE_REQUEST, "/account/erasure", None).await?;
|
||||
|
||||
// Constant-shape across present/absent AND across repeated calls: a STABLE
|
||||
@@ -209,13 +209,13 @@ pub async fn request_erasure(
|
||||
.db
|
||||
.record_admin_audit(
|
||||
&audit("account_erasure_requested", &req.id),
|
||||
integrity_secret(&state),
|
||||
&integrity_secret(&state),
|
||||
)
|
||||
.await;
|
||||
info!(erasure_id = %req.id, "erasure requested");
|
||||
req.id.to_string()
|
||||
}
|
||||
None => synthetic_erasure_id(&pubkey, integrity_secret(&state)),
|
||||
None => synthetic_erasure_id(&pubkey, &integrity_secret(&state)),
|
||||
};
|
||||
Ok(Json(ErasureRequestResponse {
|
||||
erasure_id,
|
||||
@@ -244,7 +244,7 @@ pub async fn confirm_erasure(
|
||||
.await?;
|
||||
let user = user.ok_or_else(|| AppError::NotFound("no such erasure request".into()))?;
|
||||
|
||||
let grace = state.config.retention.grace_period_hours as i64;
|
||||
let grace = state.cfg().retention.grace_period_hours as i64;
|
||||
let req = state
|
||||
.db
|
||||
.confirm_erasure_request(id, user.id, grace)
|
||||
@@ -256,7 +256,7 @@ pub async fn confirm_erasure(
|
||||
.db
|
||||
.record_admin_audit(
|
||||
&audit("account_erasure_confirmed", &req.id),
|
||||
integrity_secret(&state),
|
||||
&integrity_secret(&state),
|
||||
)
|
||||
.await;
|
||||
info!(erasure_id = %req.id, "erasure confirmed");
|
||||
@@ -295,7 +295,7 @@ pub async fn cancel_erasure(
|
||||
.db
|
||||
.record_admin_audit(
|
||||
&audit("account_erasure_cancelled", &req.id),
|
||||
integrity_secret(&state),
|
||||
&integrity_secret(&state),
|
||||
)
|
||||
.await;
|
||||
Ok(Json(StatusResponse {
|
||||
@@ -359,7 +359,7 @@ pub async fn export(
|
||||
details: None,
|
||||
ip_address: None,
|
||||
},
|
||||
integrity_secret(&state),
|
||||
&integrity_secret(&state),
|
||||
)
|
||||
.await;
|
||||
Ok(Json(ExportResponse {
|
||||
@@ -388,11 +388,11 @@ fn audit(action: &str, erasure_id: &Uuid) -> NewAdminAudit {
|
||||
#[instrument(skip(state))]
|
||||
pub async fn run_erasure_sweep(state: &Arc<AppState>, batch: i64) -> Result<u64, AppError> {
|
||||
let due = state.db.due_erasure_requests(batch).await?;
|
||||
let purge = state.config.retention.purge_login_events;
|
||||
let purge = state.cfg().retention.purge_login_events;
|
||||
let secret = integrity_secret(state);
|
||||
let mut done = 0;
|
||||
for req in due {
|
||||
match state.db.execute_erasure(req.id, purge, secret).await {
|
||||
match state.db.execute_erasure(req.id, purge, &secret).await {
|
||||
Ok(true) => done += 1,
|
||||
// false = another node/sweep already claimed it, or it was cancelled.
|
||||
Ok(false) => {}
|
||||
|
||||
+5
-5
@@ -92,19 +92,19 @@ pub fn build_server_info(config: &Config) -> ServerInfo {
|
||||
/// is off (matching an unmatched route — the `{error,code}` envelope would itself
|
||||
/// hint the route exists, so it is deliberately not used here).
|
||||
pub async fn server_info(State(state): State<Arc<AppState>>) -> Response {
|
||||
if !state.config.landing.enabled {
|
||||
if !state.cfg().landing.enabled {
|
||||
return StatusCode::NOT_FOUND.into_response();
|
||||
}
|
||||
Json(build_server_info(&state.config)).into_response()
|
||||
Json(build_server_info(&state.cfg())).into_response()
|
||||
}
|
||||
|
||||
/// `GET /` — minimal HTML rendered from the read model; bare `404` when off.
|
||||
pub async fn root(State(state): State<Arc<AppState>>) -> Response {
|
||||
if !state.config.landing.enabled {
|
||||
if !state.cfg().landing.enabled {
|
||||
return StatusCode::NOT_FOUND.into_response();
|
||||
}
|
||||
let title = state
|
||||
.config
|
||||
let cfg = state.cfg();
|
||||
let title = cfg
|
||||
.landing
|
||||
.title
|
||||
.as_deref()
|
||||
|
||||
@@ -69,7 +69,7 @@ pub fn client_ip(state: &AppState, headers: &HeaderMap, peer: SocketAddr) -> IpA
|
||||
let peer_ip = peer.ip();
|
||||
|
||||
let peer_is_trusted = state
|
||||
.config
|
||||
.cfg()
|
||||
.trusted_proxies
|
||||
.iter()
|
||||
.any(|net| net.contains(peer_ip));
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ pub async fn well_known_nostr(
|
||||
let name = raw.to_lowercase();
|
||||
if let Some(user) = state.db.get_user_by_username(&name).await? {
|
||||
if let Some(pubkey) = user.nostr_pubkey {
|
||||
relays.insert(pubkey.clone(), relay_hints(&state.config));
|
||||
relays.insert(pubkey.clone(), relay_hints(&state.cfg()));
|
||||
names.insert(name, pubkey);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -52,7 +52,7 @@ pub struct StatusResp {
|
||||
|
||||
/// Reject when the premium tier is not enabled on this instance.
|
||||
fn ensure_enabled(state: &AppState) -> Result<(), AppError> {
|
||||
if state.config.premium.enabled {
|
||||
if state.cfg().premium.enabled {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(AppError::ValidationError(
|
||||
@@ -83,8 +83,8 @@ pub async fn invoice(
|
||||
let user_id = extract_user_id_from_token(&state, &headers).await?;
|
||||
ensure_enabled(&state)?;
|
||||
|
||||
let plan = state
|
||||
.config
|
||||
let cfg = state.cfg();
|
||||
let plan = cfg
|
||||
.premium
|
||||
.plans
|
||||
.iter()
|
||||
@@ -106,8 +106,8 @@ pub async fn invoice(
|
||||
.as_ref()
|
||||
.ok_or_else(|| AppError::Internal("payment provider not configured".into()))?;
|
||||
|
||||
let pay = &state.config.registration.payment;
|
||||
let currency = state.config.premium.currency.clone();
|
||||
let pay = &state.cfg().registration.payment;
|
||||
let currency = state.cfg().premium.currency.clone();
|
||||
let invoice = provider
|
||||
.create_invoice(&InvoiceRequest {
|
||||
amount: plan.amount.clone(),
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ pub struct PricesResponse {
|
||||
)]
|
||||
#[instrument(skip(state))]
|
||||
pub async fn prices(State(state): State<Arc<AppState>>) -> Result<Json<PricesResponse>, AppError> {
|
||||
if !state.config.features.prices {
|
||||
if !state.cfg().features.prices {
|
||||
return Err(AppError::NotFound(
|
||||
"price feed is not enabled on this server".into(),
|
||||
));
|
||||
|
||||
+4
-4
@@ -160,7 +160,7 @@ pub struct ClawbackTipResponse {
|
||||
|
||||
/// Reject when tips are not enabled on this instance.
|
||||
fn ensure_tips_enabled(state: &AppState) -> Result<(), AppError> {
|
||||
if state.config.features.tips {
|
||||
if state.cfg().features.tips {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(AppError::ValidationError(
|
||||
@@ -171,7 +171,7 @@ fn ensure_tips_enabled(state: &AppState) -> Result<(), AppError> {
|
||||
|
||||
/// Whether `asset` is a tip-capable chain enabled on this instance.
|
||||
fn supported_tip_asset(state: &AppState, asset: &str) -> bool {
|
||||
let c = &state.config.features.chains;
|
||||
let c = &state.cfg().features.chains;
|
||||
match asset {
|
||||
"btc" => c.btc,
|
||||
"ltc" => c.ltc,
|
||||
@@ -382,7 +382,7 @@ pub async fn create_social_tip(
|
||||
};
|
||||
|
||||
let share_url = state
|
||||
.config
|
||||
.cfg()
|
||||
.tip_share_base
|
||||
.as_ref()
|
||||
.map(|base| format!("{}/{}", base.trim_end_matches('/'), tip.id));
|
||||
@@ -573,7 +573,7 @@ pub async fn attach_funding(
|
||||
.await?;
|
||||
|
||||
let share_url = state
|
||||
.config
|
||||
.cfg()
|
||||
.tip_share_base
|
||||
.as_ref()
|
||||
.map(|base| format!("{}/{}", base.trim_end_matches('/'), tip.id));
|
||||
|
||||
@@ -202,8 +202,8 @@ pub async fn scan(
|
||||
// grin-wallet fallback, so it is enforced before either scan path is chosen.
|
||||
if let Some(h) = req.start_height {
|
||||
let tip = grin_tip(&state).await?;
|
||||
state.config.restore.enforce("grin", h, tip)?;
|
||||
state.config.restore.enforce_restore_pow(
|
||||
state.cfg().restore.enforce("grin", h, tip)?;
|
||||
state.cfg().restore.enforce_restore_pow(
|
||||
"grin",
|
||||
&req.rewind_hash,
|
||||
h,
|
||||
|
||||
@@ -63,7 +63,7 @@ const RELAY_TTL_DAYS: i64 = 7;
|
||||
|
||||
/// 400 unless the relay feature is enabled on this server.
|
||||
fn ensure_enabled(state: &AppState) -> Result<(), AppError> {
|
||||
if state.config.features.grin_relay {
|
||||
if state.cfg().features.grin_relay {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(AppError::NotFound(
|
||||
|
||||
@@ -320,8 +320,8 @@ pub async fn register(
|
||||
// Restore: gate the scan depth against this instance's policy (the
|
||||
// backfill cost lands on our LWS). `None` (create) needs no check.
|
||||
let tip = client.get_blockchain_height().await?;
|
||||
state.config.restore.enforce(&asset, h, tip)?;
|
||||
state.config.restore.enforce_restore_pow(
|
||||
state.cfg().restore.enforce(&asset, h, tip)?;
|
||||
state.cfg().restore.enforce_restore_pow(
|
||||
&asset,
|
||||
&req.address,
|
||||
h,
|
||||
|
||||
+1
-1
@@ -297,7 +297,7 @@ pub async fn website_verify(
|
||||
.create_token_pair(user.id, Platform::Web, session_id)?;
|
||||
let refresh_token_hash = hash_refresh_token(
|
||||
&pair.refresh_token,
|
||||
&state.config.secrets.refresh_token_pepper,
|
||||
&state.cfg().secrets.refresh_token_pepper,
|
||||
);
|
||||
let expires_at = Utc::now() + state.sessions.refresh_token_expiry();
|
||||
state
|
||||
|
||||
Reference in New Issue
Block a user