diff --git a/.gitignore b/.gitignore index b1eaa60565..35fe06d2e6 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,8 @@ next-env.d.ts # Docker postgres_data/ .env*.local + +#agent +.agent +.agents +.qwen \ No newline at end of file diff --git a/app/(login)/actions.ts b/app/(login)/actions.ts index 532adc0ef4..32141d1e01 100644 --- a/app/(login)/actions.ts +++ b/app/(login)/actions.ts @@ -19,7 +19,7 @@ import { import { comparePasswords, hashPassword, setSession } from '@/lib/auth/session'; import { redirect } from 'next/navigation'; import { cookies } from 'next/headers'; -import { createCheckoutSession } from '@/lib/payments/stripe'; +import { createCheckoutSession, stripe } from '@/lib/payments/stripe'; import { getUser, getUserWithTeam } from '@/lib/db/queries'; import { validatedAction, @@ -323,14 +323,46 @@ export const deleteAccount = validatedActionWithUser( .where(eq(users.id, user.id)); if (userWithTeam?.teamId) { - await db - .delete(teamMembers) + const owners = await db + .select({ count: sql`count(*)` }) + .from(teamMembers) .where( and( - eq(teamMembers.userId, user.id), - eq(teamMembers.teamId, userWithTeam.teamId) + eq(teamMembers.teamId, userWithTeam.teamId), + eq(teamMembers.role, 'owner') ) ); + + if (owners[0].count === 1 && userWithTeam.role === 'owner') { + const team = await db + .select() + .from(teams) + .where(eq(teams.id, userWithTeam.teamId)) + .limit(1); + + if (team.length > 0 && team[0].stripeSubscriptionId) { + try { + await stripe.subscriptions.cancel(team[0].stripeSubscriptionId); + } catch (error) { + console.error('Failed to cancel Stripe subscription:', error); + // Continue with team cleanup even if Stripe cancellation fails + } + } + + await db.delete(teamMembers).where(eq(teamMembers.teamId, userWithTeam.teamId)); + await db.delete(activityLogs).where(eq(activityLogs.teamId, userWithTeam.teamId)); + await db.delete(invitations).where(eq(invitations.teamId, userWithTeam.teamId)); + await db.delete(teams).where(eq(teams.id, userWithTeam.teamId)); + } else { + await db + .delete(teamMembers) + .where( + and( + eq(teamMembers.userId, user.id), + eq(teamMembers.teamId, userWithTeam.teamId) + ) + ); + } } (await cookies()).delete('session'); diff --git a/app/layout.tsx b/app/layout.tsx index 9c2596780b..9027c72e8c 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -15,11 +15,13 @@ export const viewport: Viewport = { const manrope = Manrope({ subsets: ['latin'] }); -export default function RootLayout({ +export default async function RootLayout({ children }: { children: React.ReactNode; }) { + const [user, team] = await Promise.all([getUser(), getTeamForUser()]); + return ( diff --git a/lib/db/queries.ts b/lib/db/queries.ts index 1bcdf644b9..fbc7ac4dde 100644 --- a/lib/db/queries.ts +++ b/lib/db/queries.ts @@ -68,7 +68,8 @@ export async function getUserWithTeam(userId: number) { const result = await db .select({ user: users, - teamId: teamMembers.teamId + teamId: teamMembers.teamId, + role: teamMembers.role }) .from(users) .leftJoin(teamMembers, eq(users.id, teamMembers.userId)) diff --git a/lib/db/seed.ts b/lib/db/seed.ts index 4b62ab31ea..149d79d8ee 100644 --- a/lib/db/seed.ts +++ b/lib/db/seed.ts @@ -17,7 +17,7 @@ async function createStripeProducts() { currency: 'usd', recurring: { interval: 'month', - trial_period_days: 7, + trial_period_days: 14, }, }); @@ -32,7 +32,7 @@ async function createStripeProducts() { currency: 'usd', recurring: { interval: 'month', - trial_period_days: 7, + trial_period_days: 14, }, }); diff --git a/lib/payments/stripe.ts b/lib/payments/stripe.ts index 6f24b794dc..cc365241fc 100644 --- a/lib/payments/stripe.ts +++ b/lib/payments/stripe.ts @@ -8,7 +8,7 @@ import { } from '@/lib/db/queries'; export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { - apiVersion: '2025-04-30.basil' + apiVersion: '2025-08-27.basil' }); export async function createCheckoutSession({ diff --git a/tsconfig.json b/tsconfig.json index f969d125d7..b2173cadd3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "baseUrl": ".", "plugins": [