Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
839a45d
Implement code changes to enhance functionality and improve performance
xavimondev Apr 20, 2025
b1c1b9a
refactor application components and update branding to Snap2SQL
xavimondev Apr 20, 2025
169cc65
update footer component to include text size for better readability
xavimondev Apr 21, 2025
92cb895
refactor environment variables, update prompts, and enhance code stru…
xavimondev Apr 21, 2025
8cfcd9b
feat: enhance database deployment and schema management features
xavimondev Apr 25, 2025
41cbf16
refactor code structure for improved readability and maintainability
xavimondev Apr 25, 2025
b261c38
refactor: reorganize imports and enhance alert component in database …
xavimondev Apr 25, 2025
38d5ace
refactor code structure for improved readability and maintainability
xavimondev May 2, 2025
1695900
remove unused code and streamline schema management in store
xavimondev May 2, 2025
6e6996b
add rate limit
xavimondev May 3, 2025
5faa21b
refactor move DB_SCHEMA and prompts to a new utility file for better …
xavimondev May 4, 2025
080fdd6
feat: implement AI generation route with rate limiting and error hand…
xavimondev May 4, 2025
dc61b4c
remove deprecated code generation routes for cleanup
xavimondev May 4, 2025
4a5b451
remove unused generation-related constants from constants.ts
xavimondev May 4, 2025
a49f396
update databaseFormat state initialization and improve Header compone…
xavimondev May 6, 2025
04eb689
remove unused components
xavimondev May 6, 2025
532b213
add small ui improvements
xavimondev May 6, 2025
1480133
update version
xavimondev May 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18.18.2
v20.18.1
6 changes: 4 additions & 2 deletions apps/web/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
OPENAI_API_KEY=
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
GOOGLE_GENERATIVE_AI_API_KEY=
DEFAULT_PROVIDER=openai

UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
28 changes: 0 additions & 28 deletions apps/web/actions.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'
import { streamText } from 'ai'
import { createOpenAI } from '@ai-sdk/openai'
import { PROMPT } from '@/prompt'
import { generateObject } from 'ai'
import { openai } from '@ai-sdk/openai'
import { uptash } from '@/utils/rate-limit'
import { headers } from 'next/headers'

export const runtime = 'edge'
import { DB_SCHEMA, prompts } from '@/utils/ai'

export async function POST(req: Request) {
let customApiKey = cookies().get('api-key')?.value

if (process.env.NODE_ENV === 'production' && !customApiKey) {
return NextResponse.json(
{
data: undefined,
message: 'Missing API KEY – make sure to set it.'
},
{ status: 400 }
)
}
const ratelimit =
process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN ? uptash : false

export async function POST(req: Request) {
if (
process.env.NODE_ENV === 'development' &&
(!process.env.OPENAI_API_KEY || process.env.OPENAI_API_KEY === '')
Expand All @@ -32,27 +23,33 @@ export async function POST(req: Request) {
)
}

if (process.env.NODE_ENV === 'development') {
customApiKey = process.env.OPENAI_API_KEY
}
if (process.env.NODE_ENV === 'production') {
if (ratelimit) {
const ip = (await headers()).get('x-forwarded-for') ?? 'local'

const openai = createOpenAI({
apiKey: customApiKey,
compatibility: 'strict'
})
const { success } = await ratelimit.limit(ip)
if (!success) {
return NextResponse.json(
{ message: 'You have reached your request limit for the day.' },
{ status: 429 }
)
}
}
}

const { prompt: base64 } = await req.json()
const { prompt: base64, databaseFormat } = await req.json()

try {
const result = await streamText({
model: openai('gpt-4o'),
const result = await generateObject({
model: openai('gpt-4.1-mini'),
schema: DB_SCHEMA,
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: PROMPT
text: prompts[databaseFormat] as string
},
{
type: 'image',
Expand All @@ -61,22 +58,22 @@ export async function POST(req: Request) {
]
}
],
maxTokens: 4096,
temperature: 0.2
})

return result.toAIStreamResponse()
return NextResponse.json({
data: result.object.results
})
} catch (error) {
// console.log(Object.keys(error))
// @ts-ignore
const statusCode = error?.lastError?.statusCode ?? error.statusCode
let errorMessage = 'An error has ocurred with API Completions. Please try again.'

if (statusCode === 401) {
errorMessage = 'The provided API Key is invalid. Please enter a valid API Key.'
} else if (statusCode === 429) {
} /*else if (statusCode === 429) {
errorMessage = 'You exceeded your current quota, please check your plan and billing details.'
}
}*/

return NextResponse.json(
{
Expand Down
42 changes: 0 additions & 42 deletions apps/web/app/api/check-connection/route.ts

This file was deleted.

27 changes: 5 additions & 22 deletions apps/web/app/api/deploy/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { NextResponse } from 'next/server'
import { drizzle } from 'drizzle-orm/postgres-js'
import postgres from 'postgres'
import { sql } from 'drizzle-orm'
import { deploySchema } from '@/utils/database'

type ResponseJson = {
url: string
Expand All @@ -20,26 +18,11 @@ export async function POST(req: Request) {
{ status: 400 }
)
}
// Disable prefetch as it is not supported for "Transaction" pool mode
const client = postgres(url, { prepare: false })
const db = drizzle(client)
// Check if connection is successful
try {
await db.execute(sql`SELECT NOW()`)
} catch (error) {
// @ts-ignore
let message = error.code
if (message === 'SASL_SIGNATURE_MISMATCH') {
message = 'Database password is missing.'
} else if (message === 'ENOTFOUND') {
message =
'Your connection URL is invalid. Please double-check it and make the necessary corrections.'
}
return NextResponse.json({ error: message }, { status: 500 })
}

// Execute the migration
await db.execute(sql.raw(sqlSchema))
const response = await deploySchema(url, sqlSchema)
if (!response.success) {
return NextResponse.json({ error: response.message }, { status: 500 })
}

return NextResponse.json({
message: 'Database Schema deployed successfully'
Expand Down
38 changes: 29 additions & 9 deletions apps/web/app/api/gemini-generation/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { NextResponse } from 'next/server'
import { StreamingTextResponse, streamText } from 'ai'
import { generateObject } from 'ai'
import { google } from '@ai-sdk/google'
import { PROMPT } from '@/prompt'
import { uptash } from '@/utils/rate-limit'
import { headers } from 'next/headers'

export const runtime = 'edge'
import { DB_SCHEMA, prompts } from '@/utils/ai'

const ratelimit =
process.env.UPSTASH_REDIS_REST_URL && process.env.UPSTASH_REDIS_REST_TOKEN ? uptash : false

export async function POST(req: Request) {
if (process.env.NODE_ENV === 'development' && !process.env.GOOGLE_GENERATIVE_AI_API_KEY) {
Expand All @@ -16,18 +20,33 @@ export async function POST(req: Request) {
)
}

const { prompt: base64 } = await req.json()
if (process.env.NODE_ENV === 'production') {
if (ratelimit) {
const ip = (await headers()).get('x-forwarded-for') ?? 'local'

const { success } = await ratelimit.limit(ip)
if (!success) {
return NextResponse.json(
{ message: 'You have reached your request limit for the day.' },
{ status: 429 }
)
}
}
}

const { prompt: base64, databaseFormat } = await req.json()

try {
const result = await streamText({
model: google('models/gemini-1.5-flash-latest'),
const result = await generateObject({
model: google('gemini-2.0-flash-001'),
schema: DB_SCHEMA,
messages: [
{
role: 'user',
content: [
{
type: 'text',
text: PROMPT
text: prompts[databaseFormat] as string
},
{
type: 'image',
Expand All @@ -36,11 +55,12 @@ export async function POST(req: Request) {
]
}
],
maxTokens: 1024,
temperature: 0.2
})

return new StreamingTextResponse(result.toAIStream())
return NextResponse.json({
data: result.object.results
})
} catch (error) {
let errorMessage = 'An error has ocurred with API Completions. Please try again.'
// @ts-ignore
Expand Down
32 changes: 0 additions & 32 deletions apps/web/app/api/get-generation/route.ts

This file was deleted.

Binary file modified apps/web/app/favicon.ico
Binary file not shown.
26 changes: 19 additions & 7 deletions apps/web/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,31 @@ import { Footer } from '@/components/footer'

const inter = Inter({ subsets: ['latin'] })

const title = 'vdbs - Transform Designs to Database Schemas with AI'
const title = 'Snap2SQL - Convert diagrams to SQL with AI'
const description =
'Manage database migrations with a single click, accessing AI-generated SQL code from a database diagram and a command line script for instant local execution.'
'Snap2SQL lets you instantly convert database diagrams into clean SQL schemas using AI. Support for MySQL and PostgreSQL. Try your first scan free!'

export const metadata: Metadata = {
metadataBase: new URL(APP_URL),
title,
description,
keywords: ['vision ai', 'supabase', 'postgress', 'sql', 'migrations'],
keywords: [
'ERD to SQL',
'diagram to SQL',
'convert ERD',
'SQL schema generator',
'AI SQL builder',
'database diagram OCR',
'MySQL generator',
'PostgreSQL schema',
'Snap2SQL',
'ER diagram parser'
],
openGraph: {
title,
description,
url: '/',
siteName: 'vdbs',
siteName: 'snap2sql',
locale: 'en_US',
type: 'website',
images: [
Expand All @@ -37,11 +48,12 @@ export const metadata: Metadata = {
}
}

export default function RootLayout({ children }: { children: React.ReactNode }): JSX.Element {
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang='en'>
<body className={cn(inter.className, 'dark p-5 lg:p-12 min-h-dvh flex flex-col')}>
<Header />
<body
className={cn(inter.className, 'dark container mx-auto px-4 py-8 min-h-dvh flex flex-col')}
>
<main className='grow flex flex-col lg:flex-row gap-6'>
<div className='absolute inset-0 -z-10 size-full bg-transparent bg-[radial-gradient(#e5e7eb_-4px,transparent_1px)] [background-size:16px_16px]'></div>
{children}
Expand Down
Loading