diff --git a/lib/affiliate/directory.ts b/lib/affiliate/directory.ts index 8b9a912..a5546a1 100644 --- a/lib/affiliate/directory.ts +++ b/lib/affiliate/directory.ts @@ -90,36 +90,17 @@ export async function addOrRefreshProgram(input: string, addedBy: string | null) } return { ok: false, error: found.error }; } - const { data, error } = await svc - .from("affiliate_programs") - .upsert( - { - origin: found.origin, - descriptor: found.raw, - verified: found.verified, - fetched_at: now, - error: null, - added_by: addedBy, - updated_at: now, - }, - { onConflict: "origin" }, - ) - .select("id, origin, descriptor, verified, fetched_at, error") - .single(); - if (error || !data) { - // The unique index is on lower(origin), which upsert cannot target; fall back to update-or-insert. - const existing = await directoryRow(found.origin); - if (existing) { - await svc - .from("affiliate_programs") - .update({ descriptor: found.raw, verified: found.verified, fetched_at: now, error: null, updated_at: now }) - .eq("id", existing.id); - const row = await directoryRow(found.origin); - return row ? { ok: true, row, warnings: found.warnings } : { ok: false, error: "Could not store the program." }; - } - return { ok: false, error: error?.message ?? "Could not store the program." }; - } - return { ok: true, row: toRow(data), warnings: found.warnings }; + // The unique index is on lower(origin), which a PostgREST upsert cannot + // name, so: update the row that exists, else insert. + const existing = await directoryRow(found.origin); + const patch = { descriptor: found.raw, verified: found.verified, fetched_at: now, error: null, updated_at: now }; + const { error } = existing + ? await svc.from("affiliate_programs").update(patch).eq("id", existing.id) + : await svc.from("affiliate_programs").insert({ origin: found.origin, added_by: addedBy, ...patch }); + if (error) return { ok: false, error: error.message }; + const row = await directoryRow(found.origin); + if (!row) return { ok: false, error: "Could not store the program." }; + return { ok: true, row, warnings: found.warnings }; } /** Re-read every merchant not read in the last day (spec, "Directories" rule 1). */ @@ -233,14 +214,12 @@ export async function joinExternal( const mine = await ensureMembershipForUser(user); if (!mine) return { ok: false, error: "Could not create your CrawlProof affiliate profile." }; - const { data: inserted, error: insErr } = await svc - .from("affiliate_joins") - .upsert( - { owner_id: user.id, origin: row.origin, program_id: program.id, status: "pending", terms: program.pays, updated_at: new Date().toISOString() }, - { onConflict: "owner_id,origin,program_id", ignoreDuplicates: false }, - ) - .select(JOIN_COLUMNS) - .single(); + // Same lower(origin) index as the directory: a refused or ended twin is + // reopened in place, otherwise a fresh row is inserted. + const fresh = { owner_id: user.id, origin: row.origin, program_id: program.id, status: "pending", terms: program.pays, error: null, updated_at: new Date().toISOString() }; + const { data: inserted, error: insErr } = twin + ? await svc.from("affiliate_joins").update(fresh).eq("id", twin.id).select(JOIN_COLUMNS).single() + : await svc.from("affiliate_joins").insert(fresh).select(JOIN_COLUMNS).single(); if (insErr || !inserted) return { ok: false, error: insErr?.message ?? "Could not start the join." }; const site = env.siteUrl.replace(/\/$/, "");