OG image hardening, install rate-limit logging, and duplicate-company prevention#399
OG image hardening, install rate-limit logging, and duplicate-company prevention#399leerob wants to merge 2 commits into
Conversation
OG image routes were 500ing (~5% of traffic) on relative logo URLs and Satori layout constraints. Normalize image src to absolute URLs, add explicit display:flex on multi-child nodes, and set Cache-Control plus a 1d revalidate so social crawlers hit cached PNGs. track-install now returns a non-throwing rate-limit result instead of throwing ActionError, so expected rate limiting no longer shows up as error-level logs. The client only increments the count on success and toasts on rate limit. Co-authored-by: Cursor <cursoragent@cursor.com>
Companies could be created repeatedly (e.g. 7x "Contentful") because the upsert only conflicted on a fresh client-side id and a BEFORE INSERT trigger silently uniquified slugs. Enforce uniqueness instead: - Add a normalized name_key column + case-insensitive unique index so duplicates are rejected at the DB level, even under concurrent submits. - Rewrite upsert-company to edit by id only when the row exists, and reuse the existing company on a name conflict instead of creating a duplicate. - Generate the company form id once per mount so it stops changing on every render. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Reuses another user's company
- On insert unique violation, the handler now only reuses the existing company when owner_id matches the authenticated user; otherwise it throws the duplicate-name error.
Or push these changes by commenting:
@cursor push d722d437e2
Preview (d722d437e2)
diff --git a/apps/cursor/src/actions/upsert-company.ts b/apps/cursor/src/actions/upsert-company.ts
--- a/apps/cursor/src/actions/upsert-company.ts
+++ b/apps/cursor/src/actions/upsert-company.ts
@@ -121,16 +121,16 @@
if (error.code === UNIQUE_VIOLATION) {
const { data: existing } = await supabase
.from("companies")
- .select("id, slug")
+ .select("id, slug, owner_id")
.eq("name_key", nameKey)
.maybeSingle();
- if (existing) {
+ if (existing && existing.owner_id === userId) {
if (shouldRedirect) {
redirect(`/c/${existing.slug}`);
}
- return existing;
+ return { id: existing.id, slug: existing.slug };
}
throw new ActionError("A company with this name already exists.");You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 6baf7c5. Configure here.
| redirect(`/c/${existing.slug}`); | ||
| } | ||
|
|
||
| return existing; |
There was a problem hiding this comment.
Reuses another user's company
Medium Severity · Logic Bug
When a new company insert hits a name unique violation, the handler returns the existing row by name_key without checking owner_id. A user creating a name already owned by someone else can succeed and be redirected to that other company's profile instead of receiving “A company with this name already exists.”
Reviewed by Cursor Bugbot for commit 6baf7c5. Configure here.



Summary
This branch bundles three fixes:
Prevent duplicate company records
BEFORE INSERTtrigger silently uniquified slugs (contentful-1,contentful-2, …) instead of rejecting dupes.name_keygenerated column + a case-insensitive unique index (companies_name_key_unique) so duplicates are rejected at the DB level, even under concurrent/double submits (migrationsupabase/migrations/20260526_companies_unique_name.sql).upsert-companyto treat a request as an edit only when the row already exists, and to reuse the existing company on a name conflict (idempotent) instead of creating a duplicate.OG image hardening
lib/og.tsxrendering more resilient across the variousopengraph-imageroutes.Install rate-limit logging
track-install.Test plan
Note: the company de-dup migration and a one-time cleanup of 260 pre-existing duplicate rows were already applied to the production database; the cleanup is reversible via the
companies_dedup_backup_20260526table.Made with Cursor
Note
Medium Risk
Company insert/upsert behavior and a schema migration affect data integrity and concurrent creates; OG and install changes are lower risk but touch many routes and user-visible counts.
Overview
This PR hardens company creation, Open Graph images, and plugin install tracking in three parallel tracks.
Duplicate companies: A migration adds a stored
name_key(lower(btrim(name))) and a unique index so names are unique case-insensitively at the database.upsert-companynow treats edits only when the row exists (not merely when a clientidis present), uses explicitupdate/insertinstead of id-only upsert, trims names, maps unique violations to user-facing errors on edit, and on insert reuses the existing company byname_keyso double-submit/retry is idempotent. The company form keeps a stablenanoidper mount so upload paths and submitted ids do not change every render.OG images: Shared
lib/oggainsresolveOgImageUrlfor absolute Satori URLs,Cache-Controlon generated images, andrevalidate = 86400on OG routes. Profile/plugin OG templates use resolved logo/avatar URLs and adddisplay: "flex"on text nodes for layout reliability.Install rate limits:
track-installreturns{ tracked, rateLimited }instead of throwing; the plugin detail page bumps install count only when tracked and shows a toast when rate-limited.Reviewed by Cursor Bugbot for commit 6baf7c5. Bugbot is set up for automated code reviews on this repo. Configure here.