v0.2.0:11 + v0.2.0:12 — Archive, Settings, agent surface, machines redesign
Two release cycles prepared together: v0.2.0:11 (policy archive + safe- delete cleanup + brand-consistent confirm modals) and v0.2.0:12 (Settings tab + agent-friendly operator API + machines tab redesign + buyer-facing copy alignment). Highlights: - Migration 0015: policies.archived_at column. Archive button on tier cards; safe-delete relaxed to ignore revoked-license tombstones; renewal worker refuses archived policies. - Migration 0016: scoped_api_keys table. Four roles (read-only, license-issuer, support, full-admin) with bounded scopes. Master admin_api_key still works on every endpoint; scoped keys gated on endpoints wired through require_scope(). - New /v1/openapi.json — public, no auth. Curated OpenAPI 3.1 spec for agent / SDK discovery. - New Settings tab: Operator name + Payment providers panel + API keys management. Replaces 8 StartOS Actions (Zaprite all, BTCPay all, operator name, switch-provider). StartOS Actions pruned to 4 install-time essentials. - Machines tab rewritten: global default view grouped by product, filter pills with counts, quick-stats row, drill-down via new "Machines" button on each Licenses-tab row. New repo helper list_machines_admin joins machines x licenses x products server-side. - Branded confirmModal replaces every native window.confirm() call in the admin UI (7 callsites). - Enforce mode killed: KEYSAT_LICENSE_ENFORCE compile-time flag retired; daemon always boots; missing self-license -> Creator (free) tier. "Unlicensed" label gone from admin UI. - Zaprite gated on the new zaprite_payments entitlement (renamed from card_payments to reflect the broader gateway). - Creator code cap 5 -> 10. - KEYSAT_AGENT_GUIDE.md: auth, role-to-scope mapping, error envelope, webhook events, worked recipes. - Buyer-facing copy aligned with new positioning: "Bitcoin-native self-hosted software licensing" everywhere on production surfaces. - Cross-product safety section (Section 9a) added to KEYSAT_INTEGRATION.md. - 5 new API integration smoke tests covering OpenAPI, scoped API keys CRUD, role-elevation guard, and Zaprite-tier gating. Test count: 83 passing (was 78). All migration tests pass against 0015 and 0016 applied to populated DBs.
This commit is contained in:
@@ -577,6 +577,55 @@ async fn renew_one(state: &AppState, sub: &Subscription) -> Result<()> {
|
||||
);
|
||||
}
|
||||
|
||||
// 0a. Refuse to renew an archived policy. The operator has
|
||||
// explicitly taken this tier out of circulation. We dispatch a
|
||||
// clear webhook + audit event so the operator can decide
|
||||
// whether to unarchive or accept the lapse. The sub is left in
|
||||
// its current state — the lapsing worker will eventually move
|
||||
// it to `lapsed` when its grace period expires.
|
||||
let policy_for_check =
|
||||
crate::db::repo::get_policy_by_id(&state.db, &sub.policy_id).await?;
|
||||
if let Some(policy) = policy_for_check.as_ref() {
|
||||
if policy.archived_at.is_some() {
|
||||
tracing::warn!(
|
||||
sub_id = %sub.id,
|
||||
policy_id = %sub.policy_id,
|
||||
policy_slug = %policy.slug,
|
||||
"skipping renewal: policy is archived",
|
||||
);
|
||||
let _ = crate::db::repo::insert_audit(
|
||||
&state.db,
|
||||
"renewal_worker",
|
||||
None,
|
||||
"subscription.renewal_skipped_archived",
|
||||
Some("subscription"),
|
||||
Some(&sub.id),
|
||||
None,
|
||||
None,
|
||||
&json!({
|
||||
"policy_id": sub.policy_id,
|
||||
"policy_slug": policy.slug,
|
||||
"reason": "policy_archived",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
crate::webhooks::dispatch(
|
||||
state,
|
||||
"subscription.renewal_skipped",
|
||||
&json!({
|
||||
"subscription_id": sub.id,
|
||||
"license_id": sub.license_id,
|
||||
"product_id": sub.product_id,
|
||||
"policy_id": sub.policy_id,
|
||||
"policy_slug": policy.slug,
|
||||
"reason": "policy_archived",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// 1. Convert listed price to sats. SAT-currency subs are an
|
||||
// identity (no rate fetcher hit); fiat subs re-quote each
|
||||
// cycle (per MULTI_CURRENCY_DESIGN.md decision).
|
||||
|
||||
Reference in New Issue
Block a user