From c3b76068ea33240e6e63ea88caa3de26670da04d Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Wed, 24 Sep 2025 09:46:30 +0800 Subject: [PATCH 1/4] GPT-5 goooo --- apps/web/app/Layout/providers.tsx | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/apps/web/app/Layout/providers.tsx b/apps/web/app/Layout/providers.tsx index e221b2aa5..5e1304904 100644 --- a/apps/web/app/Layout/providers.tsx +++ b/apps/web/app/Layout/providers.tsx @@ -103,6 +103,44 @@ export function Devtools() { function CapDevtools() { const flags = useFeatureFlags(); + // Server actions are defined inline so we can keep everything in this devtools + // file. These only run in development and will throw if somehow invoked in prod. + async function promoteToPro() { + "use server"; + if (process.env.NODE_ENV !== "development") { + throw new Error("promoteToPro can only be used in development"); + } + const { getCurrentUser } = await import("@cap/database/auth/session"); + const { db } = await import("@cap/database"); + const { users } = await import("@cap/database/schema"); + const { eq } = await import("drizzle-orm"); + + const user = await getCurrentUser(); + if (!user) throw new Error("No current user session"); + await db() + .update(users) + .set({ stripeSubscriptionStatus: "active" }) + .where(eq(users.id, user.id)); + } + + async function demoteFromPro() { + "use server"; + if (process.env.NODE_ENV !== "development") { + throw new Error("demoteFromPro can only be used in development"); + } + const { getCurrentUser } = await import("@cap/database/auth/session"); + const { db } = await import("@cap/database"); + const { users } = await import("@cap/database/schema"); + const { eq } = await import("drizzle-orm"); + + const user = await getCurrentUser(); + if (!user) throw new Error("No current user session"); + await db() + .update(users) + .set({ stripeSubscriptionStatus: null }) + .where(eq(users.id, user.id)); + } + return (

Cap Devtools

@@ -122,6 +160,32 @@ function CapDevtools() { Enable Upload Progress UI
+ {process.env.NODE_ENV === "development" && ( +
+

Cap Pro

+

+ Toggle the current user\'s Pro status (dev only) +

+
+
+ +
+
+ +
+
+
+ )} ); } From 4abfd7d796f0a39bde7bebddfaeff67a69dc0138 Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Wed, 24 Sep 2025 15:12:38 +0800 Subject: [PATCH 2/4] cleanup AI slop --- apps/web/app/Layout/devtoolsServer.ts | 34 +++++++++++ apps/web/app/Layout/providers.tsx | 87 ++++++++------------------- 2 files changed, 58 insertions(+), 63 deletions(-) create mode 100644 apps/web/app/Layout/devtoolsServer.ts diff --git a/apps/web/app/Layout/devtoolsServer.ts b/apps/web/app/Layout/devtoolsServer.ts new file mode 100644 index 000000000..b4aab5f3e --- /dev/null +++ b/apps/web/app/Layout/devtoolsServer.ts @@ -0,0 +1,34 @@ +"use server"; + +import { db } from "@cap/database"; +import { getCurrentUser } from "@cap/database/auth/session"; +import { users } from "@cap/database/schema"; +import { eq } from "drizzle-orm"; + +export async function promoteToPro() { + "use server"; + + if (process.env.NODE_ENV !== "development") + throw new Error("promoteToPro can only be used in development"); + + const user = await getCurrentUser(); + if (!user) throw new Error("No current user session"); + await db() + .update(users) + .set({ stripeSubscriptionStatus: "active" }) + .where(eq(users.id, user.id)); +} + +export async function demoteFromPro() { + "use server"; + + if (process.env.NODE_ENV !== "development") + throw new Error("demoteFromPro can only be used in development"); + + const user = await getCurrentUser(); + if (!user) throw new Error("No current user session"); + await db() + .update(users) + .set({ stripeSubscriptionStatus: null }) + .where(eq(users.id, user.id)); +} diff --git a/apps/web/app/Layout/providers.tsx b/apps/web/app/Layout/providers.tsx index 5e1304904..412273d86 100644 --- a/apps/web/app/Layout/providers.tsx +++ b/apps/web/app/Layout/providers.tsx @@ -72,6 +72,7 @@ export function ReactQueryProvider({ import { SessionProvider as NASessionProvider } from "next-auth/react"; import { featureFlags, useFeatureFlags } from "./features"; +import { demoteFromPro, promoteToPro } from "./devtoolsServer"; export function SessionProvider({ children }: PropsWithChildren) { return {children}; @@ -103,44 +104,6 @@ export function Devtools() { function CapDevtools() { const flags = useFeatureFlags(); - // Server actions are defined inline so we can keep everything in this devtools - // file. These only run in development and will throw if somehow invoked in prod. - async function promoteToPro() { - "use server"; - if (process.env.NODE_ENV !== "development") { - throw new Error("promoteToPro can only be used in development"); - } - const { getCurrentUser } = await import("@cap/database/auth/session"); - const { db } = await import("@cap/database"); - const { users } = await import("@cap/database/schema"); - const { eq } = await import("drizzle-orm"); - - const user = await getCurrentUser(); - if (!user) throw new Error("No current user session"); - await db() - .update(users) - .set({ stripeSubscriptionStatus: "active" }) - .where(eq(users.id, user.id)); - } - - async function demoteFromPro() { - "use server"; - if (process.env.NODE_ENV !== "development") { - throw new Error("demoteFromPro can only be used in development"); - } - const { getCurrentUser } = await import("@cap/database/auth/session"); - const { db } = await import("@cap/database"); - const { users } = await import("@cap/database/schema"); - const { eq } = await import("drizzle-orm"); - - const user = await getCurrentUser(); - if (!user) throw new Error("No current user session"); - await db() - .update(users) - .set({ stripeSubscriptionStatus: null }) - .where(eq(users.id, user.id)); - } - return (

Cap Devtools

@@ -160,32 +123,30 @@ function CapDevtools() { Enable Upload Progress UI
- {process.env.NODE_ENV === "development" && ( -
-

Cap Pro

-

- Toggle the current user\'s Pro status (dev only) -

-
-
- -
-
- -
-
+
+

Cap Pro

+

+ Toggle the current user's Pro status (dev only) +

+
+
+ +
+
+ +
- )} +
); } From ea8a256b4f3d54348b2fbce3282cea4d4a1f5e79 Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Wed, 24 Sep 2025 15:17:43 +0800 Subject: [PATCH 3/4] fix --- apps/web/app/Layout/devtoolsServer.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/web/app/Layout/devtoolsServer.ts b/apps/web/app/Layout/devtoolsServer.ts index b4aab5f3e..ceeeee87e 100644 --- a/apps/web/app/Layout/devtoolsServer.ts +++ b/apps/web/app/Layout/devtoolsServer.ts @@ -15,7 +15,11 @@ export async function promoteToPro() { if (!user) throw new Error("No current user session"); await db() .update(users) - .set({ stripeSubscriptionStatus: "active" }) + .set({ + stripeCustomerId: "development", + stripeSubscriptionId: "development", + stripeSubscriptionStatus: "active", + }) .where(eq(users.id, user.id)); } @@ -29,6 +33,10 @@ export async function demoteFromPro() { if (!user) throw new Error("No current user session"); await db() .update(users) - .set({ stripeSubscriptionStatus: null }) + .set({ + stripeCustomerId: null, + stripeSubscriptionId: null, + stripeSubscriptionStatus: null, + }) .where(eq(users.id, user.id)); } From a57506776fce1454ce807a8ba5d849084a9d5fd1 Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Wed, 24 Sep 2025 15:18:39 +0800 Subject: [PATCH 4/4] format --- apps/web/app/Layout/providers.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/app/Layout/providers.tsx b/apps/web/app/Layout/providers.tsx index 412273d86..f72f87784 100644 --- a/apps/web/app/Layout/providers.tsx +++ b/apps/web/app/Layout/providers.tsx @@ -71,8 +71,8 @@ export function ReactQueryProvider({ } import { SessionProvider as NASessionProvider } from "next-auth/react"; -import { featureFlags, useFeatureFlags } from "./features"; import { demoteFromPro, promoteToPro } from "./devtoolsServer"; +import { featureFlags, useFeatureFlags } from "./features"; export function SessionProvider({ children }: PropsWithChildren) { return {children};