mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 05:42:19 +00:00
fix(02-11): release guardian lock before provider egress
- Commit a final authorization claim under the lifecycle lock\n- Keep provider calls and retry outcomes outside database transactions\n- Retain authorization claims with notification delivery diagnostics
This commit is contained in:
1 parent
1818bd874b
commit
4b79f83cf7
9 files changed
+560
-110
No files matched your search
@@ -0,0 +1,32 @@
|
||||
-- v1.37.0 — a managed Guardian notification receives a durable final
|
||||
-- authorization claim under the profile lifecycle lock before provider I/O.
|
||||
-- This records only delivery principals and outcome, never health content or
|
||||
-- channel credentials. Retention follows the notification attempt cleanup.
|
||||
CREATE TABLE "notification_egress_authorizations" (
|
||||
"id" TEXT NOT NULL,
|
||||
"record_user_id" TEXT NOT NULL,
|
||||
"recipient_user_id" TEXT NOT NULL,
|
||||
"channel" TEXT NOT NULL,
|
||||
"event_type" TEXT NOT NULL,
|
||||
"authorized_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"completed_at" TIMESTAMP(3),
|
||||
"outcome" TEXT,
|
||||
|
||||
CONSTRAINT "notification_egress_authorizations_pkey" PRIMARY KEY ("id"),
|
||||
CONSTRAINT "notification_egress_authorizations_record_user_id_fkey"
|
||||
FOREIGN KEY ("record_user_id") REFERENCES "users"("id")
|
||||
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "notification_egress_authorizations_recipient_user_id_fkey"
|
||||
FOREIGN KEY ("recipient_user_id") REFERENCES "users"("id")
|
||||
ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX "notification_egress_authorizations_authorized_at_idx"
|
||||
ON "notification_egress_authorizations" ("authorized_at");
|
||||
|
||||
CREATE INDEX "notification_egress_authorizations_record_user_id_recipient_user_id_authorized_at_idx"
|
||||
ON "notification_egress_authorizations" (
|
||||
"record_user_id",
|
||||
"recipient_user_id",
|
||||
"authorized_at" DESC
|
||||
);
|
||||
@@ -863,6 +863,8 @@ model User {
|
||||
pushAttemptsForRecord PushAttempt[] @relation("PushAttemptRecordUser")
|
||||
pushAttemptsReceived PushAttempt[] @relation("PushAttemptRecipientUser")
|
||||
notificationEvents NotificationEvent[]
|
||||
notificationEgressAuthorizationsForRecord NotificationEgressAuthorization[] @relation("NotificationEgressAuthorizationRecordUser")
|
||||
notificationEgressAuthorizationsReceived NotificationEgressAuthorization[] @relation("NotificationEgressAuthorizationRecipientUser")
|
||||
telegramReminderMessages TelegramReminderMessage[]
|
||||
// v1.10.3 — per-(user, day) Strain personal-anchor cache (day-total
|
||||
// TRIMP + running EWMA reference) so the nightly Strain engine anchors
|
||||
@@ -5825,6 +5827,32 @@ model NotificationEvent {
|
||||
@@map("notification_events")
|
||||
}
|
||||
|
||||
// A managed Guardian channel send crosses a revocable authorization boundary.
|
||||
// The dispatcher creates this claim while it holds the managed-profile
|
||||
// lifecycle lock, then commits before starting provider I/O. That committed
|
||||
// claim is the linearization point: a revocation that commits first prevents
|
||||
// the send, while a revocation that commits later does not cancel the one
|
||||
// already-authorized provider operation. No notification content, channel
|
||||
// credentials, or event deduplication keys are retained here. Completion is
|
||||
// observational only; authorization and retry decisions never read it.
|
||||
model NotificationEgressAuthorization {
|
||||
id String @id @default(cuid())
|
||||
recordUserId String @map("record_user_id")
|
||||
recipientUserId String @map("recipient_user_id")
|
||||
channel String
|
||||
eventType String @map("event_type")
|
||||
authorizedAt DateTime @default(now()) @map("authorized_at")
|
||||
completedAt DateTime? @map("completed_at")
|
||||
outcome String?
|
||||
|
||||
recordUser User @relation("NotificationEgressAuthorizationRecordUser", fields: [recordUserId], references: [id], onDelete: Cascade)
|
||||
recipientUser User @relation("NotificationEgressAuthorizationRecipientUser", fields: [recipientUserId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([authorizedAt], map: "notification_egress_authorizations_authorized_at_idx")
|
||||
@@index([recordUserId, recipientUserId, authorizedAt(sort: Desc)])
|
||||
@@map("notification_egress_authorizations")
|
||||
}
|
||||
|
||||
// `result` is one of `"ok"` | `"error"` | `"skipped"` — `skipped`
|
||||
// covers the "no recipient" / "config missing" soft cases.
|
||||
//
|
||||
|
||||
Reference in new issue
Block a user