From 2cb071c81db416836394c2af3ea8e051719cfd82 Mon Sep 17 00:00:00 2001 From: Dean Rie Date: Thu, 8 Oct 2026 18:37:03 +0300 Subject: [PATCH 1/2] fix(plugins): fail the delete action when no row was removed (cherry picked from commit f3a7e69c9eb5f39f8841fe863771fdfec3cfd099) --- apps/cursor/src/actions/delete-plugin.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/cursor/src/actions/delete-plugin.ts b/apps/cursor/src/actions/delete-plugin.ts index 1b74e971..5c6737d6 100644 --- a/apps/cursor/src/actions/delete-plugin.ts +++ b/apps/cursor/src/actions/delete-plugin.ts @@ -33,16 +33,26 @@ export const deletePluginAction = authActionClient ); } - const { error } = await supabase + const { data: deleted, error } = await supabase .from("plugins") .delete() .eq("id", id) - .eq("owner_id", userId); + .eq("owner_id", userId) + .select("id"); if (error) { throw new ActionError(`Failed to delete plugin: ${error.message}`); } + // A delete that matches no rows is not an error for PostgREST, so without + // this check a row the policy refuses to delete would still produce the + // "Plugin deleted." toast while staying live and keeping its slug. + if (!deleted || deleted.length === 0) { + throw new ActionError( + "The plugin could not be deleted. It is still listed; contact support to have it removed.", + ); + } + // Deletions must disappear from cached lists immediately for the owner. updateTag("plugins"); updateTag(`plugin-${existing.slug}`); From 5ea74abb0e7e63189a627c3ae94cfa8bc1b890ec Mon Sep 17 00:00:00 2001 From: Dean Rie Date: Thu, 8 Oct 2026 20:24:44 +0300 Subject: [PATCH 2/2] fix(plugins): render plugin logos unoptimized so any manifest logo host works (cherry picked from commit f00fa3ba88fd00c4ec45ccf7872ce314d8cebadc) --- AGENTS.md | 2 +- apps/cursor/src/components/plugins/detail/plugin-logo.tsx | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index b2d1a7b2..19e228df 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,7 @@ Lint from repo root: `bunx biome ci .` (matches CI in `.github/workflows/ci.yml` - App env lives in `apps/cursor/.env` (gitignored). For local dev it uses the Supabase CLI's well-known local default keys (`supabase status` prints them) with `NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321`. `CRON_SECRET` guards `/api/queue/plugin-scans/drain` and `/api/cron/*`. - The login page only offers GitHub/Google OAuth, which are not configured locally. Create a confirmed user via the GoTrue admin API (`POST {SUPABASE_URL}/auth/v1/admin/users` with the secret key, `"email_confirm": true`); a DB trigger provisions the `public.users` profile row. To get a browser session, add a temporary route that calls `supabase.auth.signInWithPassword(...)` with the server client (do not commit it). Put the user's id in `ADMIN_USER_IDS`/`NEXT_PUBLIC_ADMIN_USER_IDS` to use `/admin/plugins`. - Cache Components (`cacheComponents: true`) makes pages with `generateStaticParams` throw 500 on an empty database — seed at least one active plugin. Use `bun run seed:extract` / `seed:insert`, but note: the insert script must be run as `bun run --conditions=react-server --env-file=apps/cursor/.env apps/cursor/src/scripts/insert-from-jsonl.ts` or the `server-only` import throws under plain `bun run`. GitHub Code Search (used by `seed:extract` discovery) is heavily rate-limited; hand-writing `apps/cursor/.seed/candidates.json` with `{"candidates":[{"owner":...,"repo":...,"source":"seed:topic","matchedQuery":"manual"}]}` skips discovery. -- Seeded plugin logos hosted on `raw.githubusercontent.com` 500 plugin pages because that host is not in `next.config.mjs` `images.remotePatterns`; null them: `update plugins set logo = null where logo like 'https://raw.githubusercontent.com%'`. +- Plugin logos are rendered `unoptimized` in `PluginLogo`, so hosts outside `next.config.mjs` `images.remotePatterns` (for example `raw.githubusercontent.com` for manifests with a relative `logo`) no longer 500 the plugin page. Only `next/image` usages that go through the optimizer need the allowlist. - After changing `.env` or database content backing cached pages, restart `bun dev` (and `rm -rf apps/cursor/.next` if stale renders persist) — hot reload does not invalidate Cache Components output. - Without `CURSOR_API_KEY`, plugin submission works but the security scan errors (plugin lands in the admin "Scan issues" queue, where an admin can publish it manually). This is expected locally. - Supabase `edge_runtime` is disabled in `supabase/config.toml` (no edge functions in this app; its boot probe needs deno.land which is blocked). diff --git a/apps/cursor/src/components/plugins/detail/plugin-logo.tsx b/apps/cursor/src/components/plugins/detail/plugin-logo.tsx index 34aa0fda..0ca65d9d 100644 --- a/apps/cursor/src/components/plugins/detail/plugin-logo.tsx +++ b/apps/cursor/src/components/plugins/detail/plugin-logo.tsx @@ -42,6 +42,12 @@ export function PluginLogo({ logo.endsWith(".svg") && "invert", )} onError={() => setError(true)} + // Logos come from whatever host the plugin manifest points at (raw + // GitHub content for relative paths, anything for absolute URLs), so + // the image optimizer's remotePatterns allowlist would 500 the page + // for most of them. The card and leaderboard already render the same + // URLs as plain ; do the same here. + unoptimized /> ); }