fix(email): create the email_logs table the service has been writing to - #1704
Open
MOHITKOURAV01 wants to merge 1 commit into
Open
fix(email): create the email_logs table the service has been writing to#1704MOHITKOURAV01 wants to merge 1 commit into
MOHITKOURAV01 wants to merge 1 commit into
Conversation
emailService has read from and written to email_logs since the order
confirmation feature landed. No migration ever created it. Both failures were
caught and discarded -- the INSERT behind a bare .catch(() => {}) with a comment
claiming the table was created dynamically, the SELECT behind a catch that fell
through to a 100-entry array in module scope -- so every write has been dropped
and the admin log view has been serving a per-process buffer that empties on
restart.
Add 0052_email_logs.sql with the columns the service actually reads and writes.
sent_at carries a default because getEmailLogs orders on it and recordEmailLog
never inserts it; without one every row sorts as NULL. order_id is nullable and
carries no foreign key: recordEmailLog passes null when there is no order, and a
log entry has to outlive the thing it describes, which ON DELETE CASCADE would
prevent.
Stop hiding the failures. The write path reports what it could not persist, the
read path says when it is serving the memory buffer instead of the table, and an
empty table is treated as an answer rather than as a broken database -- that
fall-through made a working install look like a failed one the moment it had
nothing to show, and hid the fact that the buffer was all anyone was reading.
🔍 Quality Gate Report✅ All quality gates passed!
|
🤖 AI Code Review🔴 Score: 50/100 | AI review unavailable at this time. Automated AI review — a human maintainer will also review. |
|
Someone is attempting to deploy a commit to the Bhuvansh's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
💡 Suggested reviewers based on relevant file history: @Aditya8369, @Pcmhacker-hero |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1699
What was wrong
backend/services/emailService.jsreads from and writes toemail_logs. Nothing inmigrations/creates it.Both failures were caught and thrown away:
Nothing creates it dynamically. MySQL answered
ER_NO_SUCH_TABLEon every insert, the.catchdiscarded it, andrecordEmailLogreturned as if it had succeeded.getEmailLogscaught the same error on theSELECTand fell through toemailLogBuffer— a 100-entry array in module scope.So the "audit trail" was: capped at 100, empty after every restart or deploy, and different per instance in a multi-instance deployment. There is no record anywhere of a failed delivery once a process recycles, which is exactly what an operator needs when a customer says their confirmation never arrived.
What this adds
migrations/0052_email_logs.sql— the table, shaped by what the service actually issues rather than invented:sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP—getEmailLogsselects and orders on it;recordEmailLognever inserts it. Without a default every row sorts as NULL and "recent logs" means nothing.order_id CHAR(36) NULL, no foreign key —recordEmailLogpassesnullwhenever the caller has no order, and a log entry has to outlive the thing it describes. An order erased under a data-deletion request must not take the record of what was mailed about it along with it, which is exactly whatON DELETE CASCADEwould do.CHAR(36)matchesorders.id.status/channelas VARCHAR, not ENUM — the service already writes four statuses and two channels, and a new transport should not need a migration before it can log that it ran.error TEXT— SMTP failures are routinely longer than 255 characters.sent_atfor the only query the service makes today (ORDER BY sent_at DESC LIMIT ?is a filesort over the whole table without it, and this table only grows), plusorder_idand(status, sent_at)for the two questions an operator actually arrives with.It takes
0052rather than0050: three migrations already collide on0049, and resolving that needs0050and0051(#1700). Taking a number those two do not want keeps the two changes independent.emailService.jsstops hiding the failures. The write path reports what it could not persist and marks the entrypersisted: false; the read path says when it is serving the memory buffer instead of the table; and an empty result set is treated as an answer rather than a failure — the oldif (rows && rows.length > 0)fall-through made a working database look like a broken one the moment it had nothing to show, and hid the fact that the buffer was all anyone had ever been reading.Guard
backend/tests/emailLogsSchema.test.js, 27 cases. Nothing in the repo runs migrations or touches MySQL —check:syntaxparses,check:bootmounts,check:modulesrequires — and the suite that shipped with the feature asserted on the fallback buffer, which works fine with no table at all. So the check is static: parse theINSERTandSELECTout of the service, parse theCREATE TABLEbody out of the migrations, and assert the two describe the same columns.It also pins the migration is the table's only owner (
migrations/README.md: "A table has exactly one owning migration" — a secondCREATE TABLE IF NOT EXISTSis skipped silently), that the filename matches the runner'sNNNN_name.sqlpattern, and that the swallowing patterns cannot come back.Verification
Confirmed the guard is real by removing the migration and reverting the service, then re-running the new test:
The existing
tests/emailService.test.jsstill passes unchanged — it exercises the buffer fallback, which this keeps, now with a line saying it is being used.CI note — merge #1701 first
The Syntax check job fails on this branch, and it is not this change:
frontend/scripts/shop.jsis unparsable onmain— the responsive refactor duplicated and interleaved its initialization block. Every open PR against this repository inherits it, and becausecheck:syntaxis the first CI job and the other two are gated on it, Backend tests and Server boots are skipped rather than run. That is why this PR shows no test result.#1701 fixes it. Once that merges, this branch picks the fix up from
mainwith no rebase needed — the two touch no file in common. All gates pass locally on this branch's changes:and I verified the whole set merges cleanly by merging all five of these branches together locally: no conflicts,
check:syntaxgreen at 659 files, and the full Jest suite at 2976 passing.The
Vercelcheck fails on every PR in this repository with "Authorization required to deploy" against thebhuvanshs-projectsteam, unrelated to any change.