Compare commits

...

14 Commits

Author SHA1 Message Date
Vikhyath Mondreti
a02016e247 v0.4.24: sso for chat deployment, usage indicator for file storage, mcp improvements, local kb file storage 2025-10-27 15:32:53 -07:00
Vikhyath Mondreti
7f1ff7fd86 fix(billing): should allow restoring subscription (#1728)
* fix(already-cancelled-sub): UI should allow restoring subscription

* restore functionality fixed

* fix
2025-10-25 12:59:57 -07:00
Waleed
9b2490c4b1 v0.4.23: webflow tools + triggers, copilot api key fix (#1723)
* fix(debug-mode): remove duplicate debug mode flag (#1714)

* feat(i18n): update translations (#1709)

* improvement(condition): added variable and envvar highlighting for condition input (#1718)

* fix(dashboard): add additional context for paginated logs in dashboard, add empty state when selected cell has no data (#1719)

* fix(dashboard): add additional context for paginated logs in dashboard, add empty state when selected cell has no data

* apps/sim

* renaming

* remove relative import

* feat(tools): added webflow OAuth + tools (#1720)

* feat(tools): added webflow OAuth + tools

* remove itemId from delete item

* remove siteId

* added webhook triggers + oauth scopes + site/collection selector

* update sample payload for webflow triggers

* cleanup

* fix discord color

* feat(i18n): update translations (#1721)

* improvement(schedule): fix UI bug with schedule modal (#1722)
2025-10-23 14:48:09 -07:00
Vikhyath Mondreti
71ae27b6cd v0.4.22: fix execution context pass for google sheets 2025-10-21 19:48:06 -07:00
Vikhyath Mondreti
1b7437af14 v0.4.21: more internal auth changes, supabase vector search tool 2025-10-21 18:49:09 -07:00
Vikhyath Mondreti
9751c9f5c4 v0.4.20: internal request, kb url fixes, docs styling 2025-10-21 13:00:00 -07:00
Vikhyath Mondreti
641e353d03 v0.4.19: landing page fix 2025-10-19 16:06:12 -07:00
Vikhyath Mondreti
e4ddeb09d6 v0.4.18: file upload tools, copilot upgrade, docs changes, model filtering 2025-10-19 15:38:39 -07:00
Waleed
da091dfe8a v0.4.17: input format + files support for webhooks, docs updates, dashboard improvements 2025-10-16 16:51:55 -07:00
Vikhyath Mondreti
04f109c1f4 v0.4.16: executions dashboard, UI fixes, zep tools, slack fixes 2025-10-15 19:39:10 -07:00
Waleed
2bc8c7bf39 v0.4.15: helm chart updates, telegram tools, youtube tools, file upload support for more blocks 2025-10-14 20:49:45 -07:00
Siddharth Ganesan
fb0fa1fd21 v0.4.14: canvas speedup and copilot context window 2025-10-13 20:23:49 -07:00
Vikhyath Mondreti
7f82ed381a v0.4.13: bugfixes for dev containers, posthog redirect, helm updates 2025-10-13 14:21:18 -07:00
Waleed
219a065a7c v0.4.12: guardrails, mistral models, privacy policy updates (#1608)
* improvement(performance): remove unused source/target indices, add index on snapshot id (#1603)

* fix(blog): rename building to blogs with redirect (#1604)

* improvement(privacy-policy): updated privacy policy for google (#1602)

* updated privacy policy for google

* update terms, privacy, and emails to incl address and update verbiage

* feat(guardrails): added guardrails block/tools and docs (#1605)

* Adding guardrails block

* ack PR comments

* cleanup checkbox in dark mode

* cleanup

* fix supabase tools

* fix(inference-billing): fix inference billing when stream is true via API, add drag-and-drop functionality to deployed chat (#1606)

* fix(inference): fix inference billing when stream is true via API

* add drag-and-drop to deployed chat

* feat(mistal): added mistral as a provider, updated model prices (#1607)

* feat(mistal): added mistral as a provider, updated model prices

* remove the ability for a block to reference its own outluts

* fixed order of responses for guardrails block

* feat(versions): added the ability to rename deployment versions (#1610)

* fix(vulns): fix various vulnerabilities and enhanced code security (#1611)

* fix(vulns): fix SSRF vulnerabilities

* cleanup

* cleanup

* regen docs

* remove unused deps

* fix failing tests

* cleanup

* update deps

* regen bun lock
2025-10-11 22:23:47 -07:00
5 changed files with 71 additions and 58 deletions

View File

@@ -1,6 +1,6 @@
import { db } from '@sim/db'
import { subscription as subscriptionTable, user } from '@sim/db/schema'
import { and, eq } from 'drizzle-orm'
import { and, eq, or } from 'drizzle-orm'
import { type NextRequest, NextResponse } from 'next/server'
import { getSession } from '@/lib/auth'
import { requireStripeClient } from '@/lib/billing/stripe-client'
@@ -38,7 +38,10 @@ export async function POST(request: NextRequest) {
.where(
and(
eq(subscriptionTable.referenceId, organizationId),
eq(subscriptionTable.status, 'active')
or(
eq(subscriptionTable.status, 'active'),
eq(subscriptionTable.cancelAtPeriodEnd, true)
)
)
)
.limit(1)

View File

@@ -12,7 +12,6 @@ import {
AlertDialogTitle,
} from '@/components/ui/alert-dialog'
import { Button } from '@/components/ui/button'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { useSession, useSubscription } from '@/lib/auth-client'
import { createLogger } from '@/lib/logs/console/logger'
import { getBaseUrl } from '@/lib/urls/utils'
@@ -30,6 +29,7 @@ interface CancelSubscriptionProps {
}
subscriptionData?: {
periodEnd?: Date | null
cancelAtPeriodEnd?: boolean
}
}
@@ -127,35 +127,48 @@ export function CancelSubscription({ subscription, subscriptionData }: CancelSub
const subscriptionStatus = getSubscriptionStatus()
const activeOrgId = activeOrganization?.id
// For team/enterprise plans, get the subscription ID from organization store
if ((subscriptionStatus.isTeam || subscriptionStatus.isEnterprise) && activeOrgId) {
const orgSubscription = useOrganizationStore.getState().subscriptionData
if (orgSubscription?.id && orgSubscription?.cancelAtPeriodEnd) {
// Restore the organization subscription
if (!betterAuthSubscription.restore) {
throw new Error('Subscription restore not available')
}
const result = await betterAuthSubscription.restore({
referenceId: activeOrgId,
subscriptionId: orgSubscription.id,
})
logger.info('Organization subscription restored successfully', result)
if (isCancelAtPeriodEnd) {
if (!betterAuthSubscription.restore) {
throw new Error('Subscription restore not available')
}
let referenceId: string
let subscriptionId: string | undefined
if ((subscriptionStatus.isTeam || subscriptionStatus.isEnterprise) && activeOrgId) {
const orgSubscription = useOrganizationStore.getState().subscriptionData
referenceId = activeOrgId
subscriptionId = orgSubscription?.id
} else {
// For personal subscriptions, use user ID and let better-auth find the subscription
referenceId = session.user.id
subscriptionId = undefined
}
logger.info('Restoring subscription', { referenceId, subscriptionId })
// Build restore params - only include subscriptionId if we have one (team/enterprise)
const restoreParams: any = { referenceId }
if (subscriptionId) {
restoreParams.subscriptionId = subscriptionId
}
const result = await betterAuthSubscription.restore(restoreParams)
logger.info('Subscription restored successfully', result)
}
// Refresh state and close
await refresh()
if (activeOrgId) {
await loadOrganizationSubscription(activeOrgId)
await refreshOrganization().catch(() => {})
}
setIsDialogOpen(false)
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Failed to keep subscription'
const errorMessage = error instanceof Error ? error.message : 'Failed to restore subscription'
setError(errorMessage)
logger.error('Failed to keep subscription', { error })
logger.error('Failed to restore subscription', { error })
} finally {
setIsLoading(false)
}
@@ -190,19 +203,15 @@ export function CancelSubscription({ subscription, subscriptionData }: CancelSub
const periodEndDate = getPeriodEndDate()
// Check if subscription is set to cancel at period end
const isCancelAtPeriodEnd = (() => {
const subscriptionStatus = getSubscriptionStatus()
if (subscriptionStatus.isTeam || subscriptionStatus.isEnterprise) {
return useOrganizationStore.getState().subscriptionData?.cancelAtPeriodEnd === true
}
return false
})()
const isCancelAtPeriodEnd = subscriptionData?.cancelAtPeriodEnd === true
return (
<>
<div className='flex items-center justify-between'>
<div>
<span className='font-medium text-sm'>Manage Subscription</span>
<span className='font-medium text-sm'>
{isCancelAtPeriodEnd ? 'Restore Subscription' : 'Manage Subscription'}
</span>
{isCancelAtPeriodEnd && (
<p className='mt-1 text-muted-foreground text-xs'>
You'll keep access until {formatDate(periodEndDate)}
@@ -217,10 +226,12 @@ export function CancelSubscription({ subscription, subscriptionData }: CancelSub
'h-8 rounded-[8px] font-medium text-xs transition-all duration-200',
error
? 'border-red-500 text-red-500 dark:border-red-500 dark:text-red-500'
: 'text-muted-foreground hover:border-red-500 hover:bg-red-500 hover:text-white dark:hover:border-red-500 dark:hover:bg-red-500'
: isCancelAtPeriodEnd
? 'text-muted-foreground hover:border-green-500 hover:bg-green-500 hover:text-white dark:hover:border-green-500 dark:hover:bg-green-500'
: 'text-muted-foreground hover:border-red-500 hover:bg-red-500 hover:text-white dark:hover:border-red-500 dark:hover:bg-red-500'
)}
>
{error ? 'Error' : 'Manage'}
{error ? 'Error' : isCancelAtPeriodEnd ? 'Restore' : 'Manage'}
</Button>
</div>
@@ -228,11 +239,11 @@ export function CancelSubscription({ subscription, subscriptionData }: CancelSub
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>
{isCancelAtPeriodEnd ? 'Manage' : 'Cancel'} {subscription.plan} subscription?
{isCancelAtPeriodEnd ? 'Restore' : 'Cancel'} {subscription.plan} subscription?
</AlertDialogTitle>
<AlertDialogDescription>
{isCancelAtPeriodEnd
? 'Your subscription is set to cancel at the end of the billing period. You can reactivate it or manage other settings.'
? 'Your subscription is set to cancel at the end of the billing period. Would you like to keep your subscription active?'
: `You'll be redirected to Stripe to manage your subscription. You'll keep access until ${formatDate(
periodEndDate
)}, then downgrade to free plan.`}{' '}
@@ -260,38 +271,23 @@ export function CancelSubscription({ subscription, subscriptionData }: CancelSub
<AlertDialogFooter className='flex'>
<AlertDialogCancel
className='h-9 w-full rounded-[8px]'
onClick={handleKeep}
onClick={isCancelAtPeriodEnd ? () => setIsDialogOpen(false) : handleKeep}
disabled={isLoading}
>
Keep Subscription
{isCancelAtPeriodEnd ? 'Cancel' : 'Keep Subscription'}
</AlertDialogCancel>
{(() => {
const subscriptionStatus = getSubscriptionStatus()
if (
subscriptionStatus.isPaid &&
(activeOrganization?.id
? useOrganizationStore.getState().subscriptionData?.cancelAtPeriodEnd
: false)
) {
if (subscriptionStatus.isPaid && isCancelAtPeriodEnd) {
return (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<div className='w-full'>
<AlertDialogAction
disabled
className='h-9 w-full cursor-not-allowed rounded-[8px] bg-muted text-muted-foreground opacity-50'
>
Continue
</AlertDialogAction>
</div>
</TooltipTrigger>
<TooltipContent side='top'>
<p>Subscription will be cancelled at end of billing period</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<AlertDialogAction
onClick={handleKeep}
className='h-9 w-full rounded-[8px] bg-green-500 text-white transition-all duration-200 hover:bg-green-600 dark:bg-green-500 dark:hover:bg-green-600'
disabled={isLoading}
>
{isLoading ? 'Restoring...' : 'Restore Subscription'}
</AlertDialogAction>
)
}
return (

View File

@@ -523,6 +523,7 @@ export function Subscription({ onOpenChange }: SubscriptionProps) {
}}
subscriptionData={{
periodEnd: subscriptionData?.periodEnd || null,
cancelAtPeriodEnd: subscriptionData?.cancelAtPeriodEnd,
}}
/>
</div>

View File

@@ -220,6 +220,7 @@ export async function getSimplifiedBillingSummary(
metadata: any
stripeSubscriptionId: string | null
periodEnd: Date | string | null
cancelAtPeriodEnd?: boolean
// Usage details
usage: {
current: number
@@ -318,6 +319,7 @@ export async function getSimplifiedBillingSummary(
metadata: subscription.metadata || null,
stripeSubscriptionId: subscription.stripeSubscriptionId || null,
periodEnd: subscription.periodEnd || null,
cancelAtPeriodEnd: subscription.cancelAtPeriodEnd || undefined,
// Usage details
usage: {
current: usageData.currentUsage,
@@ -393,6 +395,7 @@ export async function getSimplifiedBillingSummary(
metadata: subscription?.metadata || null,
stripeSubscriptionId: subscription?.stripeSubscriptionId || null,
periodEnd: subscription?.periodEnd || null,
cancelAtPeriodEnd: subscription?.cancelAtPeriodEnd || undefined,
// Usage details
usage: {
current: currentUsage,
@@ -450,5 +453,14 @@ function getDefaultBillingSummary(type: 'individual' | 'organization') {
lastPeriodCost: 0,
daysRemaining: 0,
},
...(type === 'organization' && {
organizationData: {
seatCount: 0,
memberCount: 0,
totalBasePrice: 0,
totalCurrentUsage: 0,
totalOverage: 0,
},
}),
}
}

View File

@@ -29,6 +29,7 @@ export interface SubscriptionData {
metadata: any | null
stripeSubscriptionId: string | null
periodEnd: Date | null
cancelAtPeriodEnd?: boolean
usage: UsageData
billingBlocked?: boolean
}