mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 06:32:03 +00:00
fix(mood): assert tag-link entry ownership and guard mood migrations with IF NOT EXISTS
Thread the acting user id through createTagLinks, replaceTagLinks, and replaceRatedFactorLinks, and assert the target MoodEntry belongs to that user before writing any link. The check runs against the same client (tx or singleton) as the write, so it shares one snapshot. Behaviour is unchanged for the existing POST and bulk paths, where the entry is freshly created and owned; the guard closes a latent cross-user link hazard for the planned PATCH mood-edit route. A mismatched or missing entry throws MoodEntryOwnershipError before any catalog or join touch. Add IF NOT EXISTS to every ADD COLUMN in migration 0119 so a clean replay is idempotent, matching the 0117 convention. (cherry picked from commit 1bf983f6c2c5e44796c99983d57c1b1d90cd7099)
This commit is contained in:
1 parent
cfb97593a3
commit
e77e8ae705
8 files changed
+110
-11
No files matched your search
@@ -150,7 +150,7 @@ export const PUT = apiHandler(
|
||||
// is present in the body. `null` clears every link; an omitted
|
||||
// field leaves the links untouched.
|
||||
if (data.tagKeys !== undefined) {
|
||||
await replaceTagLinks(id, data.tagKeys ?? [], tx);
|
||||
await replaceTagLinks(id, user.id, data.tagKeys ?? [], tx);
|
||||
}
|
||||
|
||||
// v1.8.5 — read the persisted link keys back so the update
|
||||
|
||||
@@ -251,6 +251,7 @@ describe("POST /api/mood-entries — entry + tag-links transaction (v1.8.5)", ()
|
||||
// arg is the rated-factor set (empty here — binary tag only).
|
||||
expect(createTagLinks).toHaveBeenCalledWith(
|
||||
"mood-1",
|
||||
"user-1",
|
||||
["happy"],
|
||||
txClient,
|
||||
[],
|
||||
@@ -331,6 +332,7 @@ describe("POST /api/mood-entries — rated factors (v1.12.0)", () => {
|
||||
expect(res.status).toBe(201);
|
||||
expect(createTagLinks).toHaveBeenCalledWith(
|
||||
"mood-rf",
|
||||
"user-1",
|
||||
["happy"],
|
||||
txClient,
|
||||
[{ key: "factor_work", rating: 4 }],
|
||||
|
||||
@@ -207,6 +207,7 @@ describe("POST /api/mood-entries/bulk — structured tagKeys (v1.12.0)", () => {
|
||||
// outside the upsert tx) and the rated-factor set (4th, empty here).
|
||||
expect(createTagLinks).toHaveBeenCalledWith(
|
||||
"entry-1",
|
||||
"user-1",
|
||||
["movies", "gaming"],
|
||||
prisma,
|
||||
[],
|
||||
@@ -257,6 +258,7 @@ describe("POST /api/mood-entries/bulk — structured tagKeys (v1.12.0)", () => {
|
||||
expect(json.data.inserted).toBe(1);
|
||||
expect(createTagLinks).toHaveBeenCalledWith(
|
||||
"entry-1",
|
||||
"user-1",
|
||||
["movies"],
|
||||
prisma,
|
||||
[{ key: "factor_work", rating: 4 }],
|
||||
|
||||
@@ -249,6 +249,7 @@ async function postBulk(request: NextRequest): Promise<Response> {
|
||||
) {
|
||||
await createTagLinks(
|
||||
result.id,
|
||||
user.id,
|
||||
entry.tagKeys ?? [],
|
||||
prisma,
|
||||
entry.ratedFactors ?? [],
|
||||
|
||||
@@ -214,6 +214,7 @@ async function postMoodEntry(request: NextRequest) {
|
||||
// throws `RatedFactorOutOfRangeError`, rolling the tx back.
|
||||
await createTagLinks(
|
||||
created.id,
|
||||
user.id,
|
||||
tagKeys ?? [],
|
||||
tx,
|
||||
ratedFactors ?? [],
|
||||
|
||||
Reference in new issue
Block a user