feat: stripe integration

This commit is contained in:
Vasily Zubarev
2025-04-24 15:27:44 +02:00
parent 38a5c0f814
commit abd5ad8403
31 changed files with 559 additions and 112 deletions

View File

@@ -1,7 +1,7 @@
"use server"
import { ActionState } from "@/lib/actions"
import { getCurrentUser } from "@/lib/auth"
import { getCurrentUser, isSubscriptionExpired } from "@/lib/auth"
import { getDirectorySize, getUserUploadsDirectory, isEnoughStorageToUploadFile, unsortedFilePath } from "@/lib/files"
import { createFile } from "@/models/files"
import { updateUser } from "@/models/users"
@@ -23,6 +23,13 @@ export async function uploadFilesAction(formData: FormData): Promise<ActionState
return { success: false, error: `Insufficient storage to upload these files` }
}
if (isSubscriptionExpired(user)) {
return {
success: false,
error: "Your subscription has expired, please upgrade your account or buy new subscription plan",
}
}
// Process each file
const uploadedFiles = await Promise.all(
files.map(async (file) => {

View File

@@ -1,9 +1,10 @@
import { SubscriptionExpired } from "@/components/auth/subscription-expired"
import ScreenDropArea from "@/components/files/screen-drop-area"
import MobileMenu from "@/components/sidebar/mobile-menu"
import { AppSidebar } from "@/components/sidebar/sidebar"
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"
import { Toaster } from "@/components/ui/sonner"
import { getCurrentUser } from "@/lib/auth"
import { getCurrentUser, isSubscriptionExpired } from "@/lib/auth"
import config from "@/lib/config"
import { getUnsortedFilesCount } from "@/models/files"
import type { Metadata, Viewport } from "next"
@@ -39,7 +40,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
avatar: user.avatar || undefined,
storageUsed: user.storageUsed || 0,
storageLimit: user.storageLimit || -1,
tokenBalance: user.tokenBalance || 0,
aiBalance: user.aiBalance || 0,
}
return (
@@ -52,7 +53,10 @@ export default async function RootLayout({ children }: { children: React.ReactNo
unsortedFilesCount={unsortedFilesCount}
isSelfHosted={config.selfHosted.isEnabled}
/>
<SidebarInset className="w-full h-full mt-[60px] md:mt-0 overflow-auto">{children}</SidebarInset>
<SidebarInset className="w-full h-full mt-[60px] md:mt-0 overflow-auto">
{isSubscriptionExpired(user) && <SubscriptionExpired />}
{children}
</SidebarInset>
</SidebarProvider>
<Toaster />
</ScreenDropArea>

View File

@@ -13,7 +13,7 @@ const settingsCategories = [
href: "/settings",
},
{
title: "My Profile",
title: "Profile & Plan",
href: "/settings/profile",
},
{

View File

@@ -2,7 +2,7 @@
import { transactionFormSchema } from "@/forms/transactions"
import { ActionState } from "@/lib/actions"
import { getCurrentUser } from "@/lib/auth"
import { getCurrentUser, isSubscriptionExpired } from "@/lib/auth"
import {
getDirectorySize,
getTransactionFileUploadPath,
@@ -138,11 +138,19 @@ export async function uploadTransactionFilesAction(formData: FormData): Promise<
const userUploadsDirectory = await getUserUploadsDirectory(user)
// Check limits
const totalFileSize = files.reduce((acc, file) => acc + file.size, 0)
if (!isEnoughStorageToUploadFile(user, totalFileSize)) {
return { success: false, error: `Insufficient storage to upload new files` }
}
if (isSubscriptionExpired(user)) {
return {
success: false,
error: "Your subscription has expired, please upgrade your account or buy new subscription plan",
}
}
const fileRecords = await Promise.all(
files.map(async (file) => {
const fileUuid = randomUUID()

View File

@@ -6,7 +6,7 @@ import { buildLLMPrompt } from "@/ai/prompt"
import { fieldsToJsonSchema } from "@/ai/schema"
import { transactionFormSchema } from "@/forms/transactions"
import { ActionState } from "@/lib/actions"
import { getCurrentUser } from "@/lib/auth"
import { getCurrentUser, isSubscriptionExpired } from "@/lib/auth"
import config from "@/lib/config"
import { getTransactionFileUploadPath, getUserUploadsDirectory } from "@/lib/files"
import { DEFAULT_PROMPT_ANALYSE_NEW_FILE } from "@/models/defaults"
@@ -36,8 +36,20 @@ export async function analyzeFileAction(
return { success: false, error: "OpenAI API key is not set" }
}
if (!config.selfHosted.isEnabled && user.tokenBalance < 0) {
return { success: false, error: "You used all your AI tokens, please upgrade your account" }
if (!config.selfHosted.isEnabled) {
if (user.aiBalance <= 0) {
return {
success: false,
error: "You used all of your pre-paid AI scans, please upgrade your account or buy new subscription plan",
}
}
if (isSubscriptionExpired(user)) {
return {
success: false,
error: "Your subscription has expired, please upgrade your account or buy new subscription plan",
}
}
}
let attachments: AnalyzeAttachment[] = []
@@ -62,7 +74,7 @@ export async function analyzeFileAction(
console.log("Analysis results:", results)
if (results.data?.tokensUsed && results.data.tokensUsed > 0) {
await updateUser(user.id, { tokenBalance: { decrement: results.data.tokensUsed } })
await updateUser(user.id, { aiBalance: { decrement: 1 } })
}
return results