2f1803d1e678ebdecd80ce219cc263befa69dfd2
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5219a45a4b |
Keep only the forum ids of a Discourse webhook event
The Discourse receiver stored each verified event body whole in core.webhook_events.payload, so every forum signup's email address, username, name, avatar, profile fields and external id stayed in the database and its backups, members and non-members alike (#163). Nothing reads a stored Discourse payload back: the receiver handles the event from the body it was delivered. The receiver now parses the body once, after it takes the dedupe key, and stores only the ids processing reads: {"user":{"id":...}} for user_created, {"group_user":{"group_id":...,"user_id":...}} for the two group events. A missing id is stored as 0. A body that does not parse, or lacks the object its event names, is stored as {} and recorded failed. Email mode still reads the address from the delivered body. Two answers change. A signed body that is not JSON, or is empty, failed the JSONB insert: 500, no row, and redelivery of the same bytes, which can only fail again. It now gets a failed row and 200, as every recorded event does. A body Go parses but JSONB rejects (a \u0000 escape, invalid UTF-8) answered 500 on every delivery; its ids are now stored and it is processed. docs/models/payments-billing.md says what each receiver stores, and the comments of scripts/db-reader-role.sql stop calling the column raw provider payloads; no reader column changes. The OpenSpec change discourse-events-keep-forum-ids is added with this commit. Ride-along: the test helper newMapping drops a return value nothing read. Deliberate breaks, scripts/mutation-check -side new, 29 records, all CAUGHT: the insert stores the raw body (8 rows of both tests) keep copies the email into the stored user keep drops UserID from the stored group user (both group rows) keep swaps GroupID and UserID (both group rows) keep takes the user object for every event type (3 rows) keep stores a user with id 0 when the user object is nil a parse error stores keep of the partly filled view the parse-error path answers 500 (the three parse-error rows) resolvePerson is called with an empty email an event of another type is not dropped a group event without group_user reconciles from the user object the insert writes the body into provider_environment the processed body is the stored one (NUL escape, invalid UTF-8) the stored group user omits a zero group_id, and a zero user_id |
||
|
|
7382877ed9 |
Record a tier delete as an act, inside one transaction on the runner
A plan-wide change to the catalog left no record of itself, so nothing could say which change moved an organization or offer to run it again (#187). Migration 00027 adds that record: core.acts, one row per act with its cause, actor, note and request id, and core.plan_act_details, the act's plan-specific detail. Both tables are enclosed: only core.record_plan_act writes them, under the exclusive rendezvous for a catalog cause, and core_writer can only read them. core_reader reads every column but the note, which scripts/db-reader-role.sql lists as personal. runPlanAct is the one shape the plan acts will share: begin, find the pools, lock them in ascending id, read again under the locks, act and commit, with every failure named by its step and a refusal returned as it is. The tier delete is the first act on it. Inside its exclusive transaction it counts the tier's live holders (active and suspended), reads the ladder's tiers and the org types defaulting to it, applies the last-tier guard, records the act and deletes and renumbers; before this, the count and the guard's read ran outside the transaction, and a failed read of the org types let the delete through. A delete of a product that is no longer a tier of the ladder, a repeated confirm, writes nothing. The handler only parses, calls and renders: - A lock wait or a deadlock at any step answers "Another change collided with this one; try again." (#216). - Every other failure answers "Failed to remove tier. Reference: {request id}." and logs the step through the request's logger. - A tier held only by suspended positions is a live holder: the tier page routes it to Preview removal, and the plain delete refuses it. The plan-act golden harness builds the act's queriers through the new PlanActs seam, so a scenario can fail a statement inside the act's own transaction, and every golden gains the act and detail sections. Most gain them empty. Behaviour changes in delete-begin-fails (the log line names the step and carries the request id; the answer carries the reference), delete-bottom-tier, delete-top-tier and tier-change-during-conferral/a-tier-delete (the act row), delete-held-tier and delete-last-tier-of-a-default (the reads now run inside the transaction; the answers are unchanged) and contention (the delete answers the collision sentence; later placeholders renumber because the delete's log line now carries a request id). New: delete-org-types-unread and delete-suspended-holder. The design's failure table for the tier delete now gives the reference answer for a failed holder count and a failed tier read, as the approved copy for the delete states, and a row for the repeated delete. The full suite passes. |
||
|
|
56e65b669c |
Read invoices back from Stripe and archive the change
Stripe delivers webhook events in the endpoint's API version. Under a version later than the 2025-02-24.acacia stripe-go v81 pins, the payload-shaped invoice handlers projected every invoice with no subscription link and no lines, and every payment with no payment intent or method. - Invoice events read only the invoice id and run ReconcileInvoice, which reads the invoice, its lines and its payment intent's charges back at the pinned version and converges the core rows under a per-invoice lock, in any event order. A payment row is one attempt, keyed by its Stripe charge (Stripe store migration 00005). - Two clocks: created_at is when the console wrote a row; invoices gain issued_at and payments paid_at (core migration 00022, backfilled), and every invoice and payment list, the activity feed and the composite's latest invoice read those. - An hourly and at-boot invoice sweep reads back invoices never read, open invoices once a day, and invoices of dead-lettered events; the provider page states when it last finished. The first run is the repair of invoices projected so far. - The API version skew is logged once per version, and payment_method.attached links payments recorded before it. - A restricted key now needs Invoices read and Charges read. - scripts/db-reader-role.sql maintains console_reader, a login role that reads the console's tables without personal data, documented in docs/database-management.md and checked against a golden column list by TestConsoleReaderColumns. The demo snapshot keeps owners and privileges so the role's checks hold on it. - The demo seed and test fixtures carry issue and paid times. status/design-feedback.md gains four upstream notes, one written when #60 was closed. OpenSpec change stripe-invoice-reconcile is archived and its deltas synced into openspec/specs/. Follow-ups are #150, #151 and #152. |