diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5262ef8212f..b1b15f36135 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,6 +107,8 @@ jobs: tags: ${{ steps.meta.outputs.tags }} provenance: false sbom: false + cache-from: ${{ github.ref == 'refs/heads/main' && format('type=registry,ref={0}:latest-amd64', matrix.ghcr_image) || '' }} + cache-to: type=inline # Build ARM64 images for GHCR (main branch only, runs in parallel) build-ghcr-arm64: @@ -158,6 +160,10 @@ jobs: tags: ${{ steps.meta.outputs.tags }} provenance: false sbom: false + cache-from: | + type=registry,ref=${{ matrix.image }}:latest-arm64 + type=registry,ref=${{ matrix.image }}:${{ github.sha }}-arm64 + cache-to: type=inline # Create GHCR multi-arch manifests (only for main, after both builds) create-ghcr-manifests: diff --git a/.github/workflows/docs-embeddings.yml b/.github/workflows/docs-embeddings.yml index 5f51d4b0cc7..e01024a0e67 100644 --- a/.github/workflows/docs-embeddings.yml +++ b/.github/workflows/docs-embeddings.yml @@ -24,8 +24,19 @@ jobs: with: node-version: latest + - name: Cache Bun dependencies + uses: actions/cache@v4 + with: + path: | + ~/.bun/install/cache + node_modules + **/node_modules + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Install dependencies - run: bun install + run: bun install --frozen-lockfile - name: Process docs embeddings working-directory: ./apps/sim diff --git a/.github/workflows/i18n.yml b/.github/workflows/i18n.yml index d71cdffd617..e98fc6922b5 100644 --- a/.github/workflows/i18n.yml +++ b/.github/workflows/i18n.yml @@ -28,6 +28,17 @@ jobs: with: bun-version: 1.2.22 + - name: Cache Bun dependencies + uses: actions/cache@v4 + with: + path: | + ~/.bun/install/cache + node_modules + **/node_modules + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Run Lingo.dev translations env: LINGODOTDEV_API_KEY: ${{ secrets.LINGODOTDEV_API_KEY }} @@ -117,10 +128,21 @@ jobs: with: bun-version: 1.2.22 + - name: Cache Bun dependencies + uses: actions/cache@v4 + with: + path: | + ~/.bun/install/cache + node_modules + **/node_modules + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Install dependencies run: | cd apps/docs - bun install + bun install --frozen-lockfile - name: Build documentation to verify translations run: | diff --git a/.github/workflows/images.yml b/.github/workflows/images.yml index ff6344c78ad..46b3b30bf30 100644 --- a/.github/workflows/images.yml +++ b/.github/workflows/images.yml @@ -97,6 +97,8 @@ jobs: tags: ${{ steps.meta.outputs.tags }} provenance: false sbom: false + cache-from: ${{ github.ref == 'refs/heads/main' && format('type=registry,ref={0}:latest-amd64', matrix.ghcr_image) || '' }} + cache-to: type=inline build-ghcr-arm64: name: Build ARM64 (GHCR Only) @@ -143,6 +145,10 @@ jobs: tags: ${{ steps.meta.outputs.tags }} provenance: false sbom: false + cache-from: | + type=registry,ref=${{ matrix.image }}:latest-arm64 + type=registry,ref=${{ matrix.image }}:${{ github.sha }}-arm64 + cache-to: type=inline create-ghcr-manifests: name: Create GHCR Manifests diff --git a/.github/workflows/migrations.yml b/.github/workflows/migrations.yml index 2bfb6ca1c66..cc1b471a866 100644 --- a/.github/workflows/migrations.yml +++ b/.github/workflows/migrations.yml @@ -18,8 +18,19 @@ jobs: with: bun-version: 1.2.22 + - name: Cache Bun dependencies + uses: actions/cache@v4 + with: + path: | + ~/.bun/install/cache + node_modules + **/node_modules + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Install dependencies - run: bun install + run: bun install --frozen-lockfile - name: Apply migrations working-directory: ./packages/db diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml index 3e48ac6dc23..163a2ecd822 100644 --- a/.github/workflows/publish-cli.yml +++ b/.github/workflows/publish-cli.yml @@ -24,9 +24,20 @@ jobs: node-version: '18' registry-url: 'https://registry.npmjs.org/' + - name: Cache Bun dependencies + uses: actions/cache@v4 + with: + path: | + ~/.bun/install/cache + node_modules + **/node_modules + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Install dependencies working-directory: packages/cli - run: bun install + run: bun install --frozen-lockfile - name: Build package working-directory: packages/cli diff --git a/.github/workflows/publish-ts-sdk.yml b/.github/workflows/publish-ts-sdk.yml index 5158705fb31..9d8b3b44aaa 100644 --- a/.github/workflows/publish-ts-sdk.yml +++ b/.github/workflows/publish-ts-sdk.yml @@ -24,8 +24,19 @@ jobs: node-version: '22' registry-url: 'https://registry.npmjs.org/' + - name: Cache Bun dependencies + uses: actions/cache@v4 + with: + path: | + ~/.bun/install/cache + node_modules + **/node_modules + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Install dependencies - run: bun install + run: bun install --frozen-lockfile - name: Run tests working-directory: packages/ts-sdk diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 3a7a69f9652..fa10b0dd44b 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -23,6 +23,17 @@ jobs: with: node-version: latest + - name: Cache Bun dependencies + uses: actions/cache@v4 + with: + path: | + ~/.bun/install/cache + node_modules + **/node_modules + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/trigger-deploy.yml b/.github/workflows/trigger-deploy.yml index 88a059b282e..a3fed7619eb 100644 --- a/.github/workflows/trigger-deploy.yml +++ b/.github/workflows/trigger-deploy.yml @@ -29,8 +29,19 @@ jobs: with: bun-version: 1.2.22 + - name: Cache Bun dependencies + uses: actions/cache@v4 + with: + path: | + ~/.bun/install/cache + node_modules + **/node_modules + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + restore-keys: | + ${{ runner.os }}-bun- + - name: Install dependencies - run: bun install + run: bun install --frozen-lockfile - name: Deploy to Trigger.dev (Staging) if: github.ref == 'refs/heads/staging' diff --git a/apps/sim/app/api/billing/portal/route.ts b/apps/sim/app/api/billing/portal/route.ts index 017fbb8bd7b..959a83cd7f4 100644 --- a/apps/sim/app/api/billing/portal/route.ts +++ b/apps/sim/app/api/billing/portal/route.ts @@ -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) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/cancel-subscription.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/cancel-subscription.tsx index 1f5ea569aaf..fd81cec55ec 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/cancel-subscription.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/cancel-subscription.tsx @@ -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 (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 }) - 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) + // 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 ( <>
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'}
Subscription will be cancelled at end of billing period
-