feat(migrations): create email_send_log table#37
Conversation
|
Warning Review limit reached
Next review available in: 55 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThis PR adds a single Supabase migration creating the ChangesEmail Send Log Migration
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~5 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Updates to Preview Branch (feat/email-send-log-table) ↗︎
Tasks are run on every commit but only new migration files are pushed.
View logs for this Workflow Run ↗︎. |
There was a problem hiding this comment.
3 issues found across 1 file
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="supabase/migrations/20260630210000_create_email_send_log.sql">
<violation number="1" location="supabase/migrations/20260630210000_create_email_send_log.sql:11">
P1: Enable RLS on email_send_log before merge. Otherwise a public Supabase table containing captured email request bodies may be exposed or mutable through client roles depending on grants/policies.</violation>
<violation number="2" location="supabase/migrations/20260630210000_create_email_send_log.sql:16">
P2: Add a CHECK constraint for the three supported status values. This keeps the log queryable for sent/send_failed/rejected instead of silently accepting misspelled states.</violation>
<violation number="3" location="supabase/migrations/20260630210000_create_email_send_log.sql:18">
P2: Enforce the raw_body truncation in the schema, not only in the API writer. A CHECK length limit or bounded VARCHAR prevents accidental storage of full customer email payloads.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| -- so a send is debuggable several days back. Written by api `logEmailAttempt` | ||
| -- (recoupable/api PR #731). Append-only; no updated_at. | ||
|
|
||
| CREATE TABLE IF NOT EXISTS public.email_send_log ( |
There was a problem hiding this comment.
P1: Enable RLS on email_send_log before merge. Otherwise a public Supabase table containing captured email request bodies may be exposed or mutable through client roles depending on grants/policies.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/migrations/20260630210000_create_email_send_log.sql, line 11:
<comment>Enable RLS on email_send_log before merge. Otherwise a public Supabase table containing captured email request bodies may be exposed or mutable through client roles depending on grants/policies.</comment>
<file context>
@@ -0,0 +1,28 @@
+-- so a send is debuggable several days back. Written by api `logEmailAttempt`
+-- (recoupable/api PR #731). Append-only; no updated_at.
+
+CREATE TABLE IF NOT EXISTS public.email_send_log (
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
+ created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
</file context>
| created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), | ||
| account_id UUID, | ||
| chat_id TEXT, | ||
| status TEXT NOT NULL, -- 'sent' | 'send_failed' | 'rejected' |
There was a problem hiding this comment.
P2: Add a CHECK constraint for the three supported status values. This keeps the log queryable for sent/send_failed/rejected instead of silently accepting misspelled states.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/migrations/20260630210000_create_email_send_log.sql, line 16:
<comment>Add a CHECK constraint for the three supported status values. This keeps the log queryable for sent/send_failed/rejected instead of silently accepting misspelled states.</comment>
<file context>
@@ -0,0 +1,28 @@
+ created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
+ account_id UUID,
+ chat_id TEXT,
+ status TEXT NOT NULL, -- 'sent' | 'send_failed' | 'rejected'
+ body_parsed BOOLEAN NOT NULL DEFAULT false,
+ raw_body TEXT, -- truncated copy of the request body
</file context>
| chat_id TEXT, | ||
| status TEXT NOT NULL, -- 'sent' | 'send_failed' | 'rejected' | ||
| body_parsed BOOLEAN NOT NULL DEFAULT false, | ||
| raw_body TEXT, -- truncated copy of the request body |
There was a problem hiding this comment.
P2: Enforce the raw_body truncation in the schema, not only in the API writer. A CHECK length limit or bounded VARCHAR prevents accidental storage of full customer email payloads.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/migrations/20260630210000_create_email_send_log.sql, line 18:
<comment>Enforce the raw_body truncation in the schema, not only in the API writer. A CHECK length limit or bounded VARCHAR prevents accidental storage of full customer email payloads.</comment>
<file context>
@@ -0,0 +1,28 @@
+ chat_id TEXT,
+ status TEXT NOT NULL, -- 'sent' | 'send_failed' | 'rejected'
+ body_parsed BOOLEAN NOT NULL DEFAULT false,
+ raw_body TEXT, -- truncated copy of the request body
+ to_count INTEGER,
+ subject TEXT,
</file context>
6bcf3fa to
0b683e8
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
supabase/migrations/20260630210000_create_email_send_log.sql (1)
23-23: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winConsider a CHECK constraint to enforce the status enum.
statusis documented as'sent' | 'send_failed' | 'rejected'only via comment, with no DB-level enforcement. A typo or future code change could insert an unexpected value silently.♻️ Suggested constraint
- status TEXT NOT NULL, -- 'sent' | 'send_failed' | 'rejected' + status TEXT NOT NULL CHECK (status IN ('sent', 'send_failed', 'rejected')),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@supabase/migrations/20260630210000_create_email_send_log.sql` at line 23, The email send log table currently documents status values only in a comment, so add a database-level CHECK constraint in the migration that creates the table to enforce the allowed values. Update the CREATE TABLE statement for the email send log table so the status column is restricted to the existing enum-like set used by the schema comment: sent, send_failed, and rejected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@supabase/migrations/20260630210000_create_email_send_log.sql`:
- Line 19: The `email_send_log` table definition uses `uuid_generate_v4()`
without ensuring the required extension is enabled. Update the migration in
`create_email_send_log.sql` to either add the missing `uuid-ossp` extension
setup before the `CREATE TABLE` statement or switch the `id` default to
`gen_random_uuid()` to match the existing schema pattern.
---
Nitpick comments:
In `@supabase/migrations/20260630210000_create_email_send_log.sql`:
- Line 23: The email send log table currently documents status values only in a
comment, so add a database-level CHECK constraint in the migration that creates
the table to enforce the allowed values. Update the CREATE TABLE statement for
the email send log table so the status column is restricted to the existing
enum-like set used by the schema comment: sent, send_failed, and rejected.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 09a094ca-f41b-4c56-92a9-9e83ee406ac4
📒 Files selected for processing (1)
supabase/migrations/20260630210000_create_email_send_log.sql
| -- lifecycle is a future, separate concern keyed off resend_id. | ||
|
|
||
| CREATE TABLE IF NOT EXISTS public.email_send_log ( | ||
| id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n -i "create extension.*uuid" supabase/migrationsRepository: recoupable/database
Length of output: 157
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- target migration ---'
cat -n supabase/migrations/20260630210000_create_email_send_log.sql
echo
echo '--- repository search: uuid_generate_v4 / uuid-ossp / gen_random_uuid / create extension ---'
rg -n -i "uuid_generate_v4|uuid-ossp|gen_random_uuid|create extension" .
echo
echo '--- migration files mentioning uuid ---'
rg -n -i "uuid_generate_v4|uuid-ossp|gen_random_uuid|create extension" supabase/migrationsRepository: recoupable/database
Length of output: 29884
Add the UUID extension or switch to gen_random_uuid()
uuid_generate_v4() depends on uuid-ossp, and there’s no migration here that enables it. This table creation will fail unless the extension is provisioned elsewhere; using gen_random_uuid() would match the rest of the schema.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@supabase/migrations/20260630210000_create_email_send_log.sql` at line 19, The
`email_send_log` table definition uses `uuid_generate_v4()` without ensuring the
required extension is enabled. Update the migration in
`create_email_send_log.sql` to either add the missing `uuid-ossp` extension
setup before the `CREATE TABLE` statement or switch the `id` default to
`gen_random_uuid()` to match the existing schema pattern.
Durable log of every POST /api/emails attempt (sent / send_failed / rejected) so an empty/malformed send is debuggable within ~a week without the ephemeral agent sandbox. Captures what the API received (raw_body, full) + the outcome (status, resend_id) tied to account_id/chat_id/created_at. Append-only, loose ids, no FKs (like error_logs). 7 columns — the simplest shape that meets the observability goal. Written by recoupable/api PR #731. Ref: recoupable/chat#1829 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0b683e8 to
6834e42
Compare
What
Adds
email_send_log— a durable record of everyPOST /api/emailsattempt (sent/send_failed/rejected), so a send is debuggable several days back without the ephemeral agent sandbox.Why
Empty footer-only "Message from Recoup" emails reached customers and we could not recover what the API received (malformed bodies swallowed, route logged nothing, sandbox ephemeral). This table captures it.
Design (KISS — reviewed twice)
Append-only; loose ids, no FKs (like
error_logs). 7 columns:id, created_at, account_id, chat_id, status, resend_id. raw_body (full — bounded by the platform request-size limit), error+(created_at DESC)/(account_id)indexes.chat_idonly (TEXT, matchingchats.id).chats.session_idreaches the session andsessions.account_idthe account, so a separatesession_idcolumn would be redundant — andsession_idisn't in the/api/emailscontract anyway, so dropping it avoids extending that contract.account_iddenormalized (free at the API; the main query dimension).to_count / subject / html_length / text_length(derivable fromraw_body).email_eventstable keyed offresend_id, not more columns here. (A retention/purge job forraw_body(the real size/PII control, since we keep the full body now) and achat_idindex for the verify-the-send lookup are follow-ups alongside that work.)Pairs with
logEmailAttemptwrites this table. This migration lands first; then #731's code is reconciled to match (chat_id, drop the 4 fields) before it merges.Part of recoupable/chat#1829.
🤖 Generated with Claude Code
Summary by CodeRabbit