mirror of
https://github.com/codeflash-ai/codeflash-internal.git
synced 2026-05-04 18:25:18 +00:00
## Summary - **Fix CI build failure**: Auth0Client crashes during Next.js prerendering when env vars aren't set. Returns a no-op stub (`getSession → null`) when domain is missing — semantically correct for static generation - **Lazy-load markdown libs (~260kb)**: ReactMarkdown, remarkGfm, and react-syntax-highlighter were eagerly imported in monaco-diff-viewer but only rendered when user expands "Generated Tests". Extracted into a dynamic component - **Parallelize repo detail query**: `getRepositoryById` ran the activity count sequentially after the repo lookup. Since `repoId` is already available, all three queries now run in parallel ## Test plan - [ ] CI `build` check passes (was failing since #2598) - [ ] Trace page still renders generated tests correctly when expanded - [ ] Repository detail page loads correctly with activity status
132 lines
3.9 KiB
TypeScript
132 lines
3.9 KiB
TypeScript
import { Request, Response, NextFunction } from "express"
|
|
import {
|
|
prisma,
|
|
createCheckoutSession,
|
|
getSubscription as fetchSubscription,
|
|
cancelSubscription as cancelStripeSubscription,
|
|
} from "@codeflash-ai/common"
|
|
import * as Sentry from "@sentry/node"
|
|
import { logger } from "../utils/logger.js"
|
|
import { missingRequiredFields, subscriptionNotFound } from "../exceptions/index.js"
|
|
|
|
// Dependencies interface for easier testing
|
|
export interface SubscriptionDependencies {
|
|
prisma: {
|
|
subscriptions: {
|
|
findUnique: (params: any) => Promise<any>
|
|
}
|
|
}
|
|
createCheckoutSession: typeof createCheckoutSession
|
|
fetchSubscription: typeof fetchSubscription // Add this
|
|
cancelStripeSubscription: typeof cancelStripeSubscription
|
|
Sentry: {
|
|
captureException: (error: any) => void
|
|
}
|
|
}
|
|
|
|
// Default dependencies
|
|
let dependencies: SubscriptionDependencies = {
|
|
prisma,
|
|
createCheckoutSession,
|
|
fetchSubscription,
|
|
cancelStripeSubscription,
|
|
Sentry,
|
|
}
|
|
|
|
// For testing - allow dependency injection
|
|
export function setSubscriptionDependencies(deps: Partial<SubscriptionDependencies>) {
|
|
dependencies = { ...dependencies, ...deps }
|
|
}
|
|
|
|
export function resetSubscriptionDependencies() {
|
|
dependencies = {
|
|
prisma,
|
|
createCheckoutSession,
|
|
fetchSubscription,
|
|
cancelStripeSubscription,
|
|
Sentry,
|
|
}
|
|
}
|
|
|
|
// Get a user's subscription details
|
|
export async function getSubscription(req: Request, res: Response, next: NextFunction) {
|
|
const userId = req.query.userId as string
|
|
|
|
if (!userId) {
|
|
next(missingRequiredFields("userId"))
|
|
return
|
|
}
|
|
|
|
try {
|
|
// Get subscription with usage data (includes lazy reset)
|
|
const subscription = await dependencies.fetchSubscription(userId)
|
|
|
|
if (!subscription) {
|
|
next(subscriptionNotFound(userId))
|
|
return
|
|
}
|
|
|
|
return res.json({
|
|
plan: subscription.plan_type,
|
|
status: subscription.subscription_status,
|
|
usageCount: subscription.optimizations_used,
|
|
usageLimit: subscription.optimizations_limit,
|
|
renewalDate: subscription.current_period_end,
|
|
totalLifetimeOptimizations: subscription.total_lifetime_optimizations,
|
|
})
|
|
} catch (error) {
|
|
logger.errorWithSentry("Error getting subscription", req, {}, error as Error)
|
|
dependencies.Sentry.captureException(error)
|
|
next(error) // Pass errors to the error handler
|
|
}
|
|
}
|
|
|
|
// Create a checkout session for upgrading subscription
|
|
export async function createCheckout(req: Request, res: Response, next: NextFunction) {
|
|
const { userId, priceId, successUrl, cancelUrl, period } = req.body
|
|
|
|
if (!userId || !priceId) {
|
|
next(missingRequiredFields("userId, priceId"))
|
|
return
|
|
}
|
|
|
|
try {
|
|
// Use the common function with options object
|
|
const checkoutUrl = await dependencies.createCheckoutSession(userId, priceId, {
|
|
successUrl: successUrl || `${process.env.WEBAPP_URL}/app/billing?success=true`,
|
|
cancelUrl: cancelUrl || `${process.env.WEBAPP_URL}/app/billing?canceled=true`,
|
|
period: period || (priceId === process.env.STRIPE_PRO_PRICE_YEARLY_ID ? "yearly" : "monthly"),
|
|
})
|
|
|
|
return res.json({ url: checkoutUrl })
|
|
} catch (error) {
|
|
logger.errorWithSentry(
|
|
"Error creating checkout session",
|
|
req,
|
|
{ successUrl, cancelUrl, period },
|
|
error as Error,
|
|
)
|
|
dependencies.Sentry.captureException(error)
|
|
next(error) // Pass errors to the error handler
|
|
}
|
|
}
|
|
|
|
// Cancel a subscription
|
|
export async function cancelSubscription(req: Request, res: Response, next: NextFunction) {
|
|
const { userId } = req.body
|
|
|
|
if (!userId) {
|
|
next(missingRequiredFields("userId"))
|
|
return
|
|
}
|
|
|
|
try {
|
|
// Use the common function for cancellation
|
|
await dependencies.cancelStripeSubscription(userId)
|
|
return res.json({ status: "canceled" })
|
|
} catch (error) {
|
|
logger.errorWithSentry("Error canceling subscription", req, {}, error as Error)
|
|
dependencies.Sentry.captureException(error)
|
|
next(error) // Pass errors to the error handler
|
|
}
|
|
}
|