Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 18 additions & 15 deletions app/api/analyses/[id]/run/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NextRequest } from 'next/server'
import { NextRequest, NextResponse } from 'next/server'
import { aiConfigErrorMessage, getAnthropicClient, isAiConfigured } from '@/lib/ai-gateway'
import { z } from 'zod'
import { getCurrentAccessToken, getCurrentUser } from '@/lib/auth'
Expand Down Expand Up @@ -132,11 +132,27 @@ const CODE_EXTENSIONS = new Set([
])

export async function POST(
request: NextRequest,
req: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
// Await params in Next.js 16
const { id } = await params

// Check authentication first - redirect if not authenticated
const accessToken = await getCurrentAccessToken().catch(() => null)
if (!accessToken) {
const redirectUrl = new URL('/api/auth/github/login', req.url)
redirectUrl.searchParams.set('returnTo', `/dashboard/analyses/${id}`)
return NextResponse.redirect(redirectUrl)
Comment on lines +142 to +146
}

const user = await getCurrentUser().catch(() => null)
if (!user) {
const redirectUrl = new URL('/api/auth/github/login', req.url)
redirectUrl.searchParams.set('returnTo', `/dashboard/analyses/${id}`)
return NextResponse.redirect(redirectUrl)
}

// Create a stream for progress updates
const encoder = new TextEncoder()
const stream = new ReadableStream({
Expand All @@ -146,25 +162,12 @@ export async function POST(
}

try {
const accessToken = await getCurrentAccessToken()
if (!accessToken) {
send({ error: 'Sign in with GitHub before running an analysis.' })
controller.close()
return
}
if (!isAiConfigured()) {
send({ error: aiConfigErrorMessage() })
controller.close()
return
}

const user = await getCurrentUser()
if (!user) {
send({ error: 'Sign in with GitHub before running an analysis.' })
controller.close()
return
}

let sub = await getSubscriptionByGithubId(user.github_id).catch(() => null)
if (!sub) {
sub = await upsertSubscription({ github_id: user.github_id }).catch(() => null)
Expand Down
4 changes: 2 additions & 2 deletions app/api/checkout/route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from 'next/server'
import { getCurrentUser } from '@/lib/auth'
import { getAppUrl, getProPriceId, getStripe, isStripeConfigured } from '@/lib/stripe'
import { getAppUrl, getPriceId, getStripe, isStripeConfigured } from '@/lib/stripe'
import { updateUserBilling } from '@/lib/queries'

export async function GET(request: NextRequest) {
Expand Down Expand Up @@ -40,7 +40,7 @@ export async function GET(request: NextRequest) {
customer: customerId,
line_items: [
{
price: getProPriceId(),
price: getPriceId(),
quantity: 1,
},
],
Expand Down
19 changes: 2 additions & 17 deletions app/api/stripe/webhook/route.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import { NextRequest, NextResponse } from 'next/server'
import { getStripe, getWebhookSecret } from '@/lib/stripe'
import { upsertSubscription, getSubscriptionByStripeCustomerId, getUserByGithubId } from '@/lib/queries'
import { getStripeWebhookSecret, getStripe, getPriceIdForPlan } from '@/lib/stripe'
import { upsertSubscription, getSubscriptionByStripeCustomerId, getUserByGithubId, updateUserBilling } from '@/lib/queries'
import { grantCredits, CREDITS } from '@/lib/credits'
import type Stripe from 'stripe'
import { CREDITS, grantCredits } from '@/lib/credits'
import {
getSubscriptionByStripeCustomerId,
getUserByGithubId,
updateUserBilling,
upsertSubscription,
} from '@/lib/queries'
import { getPriceIdForPlan, getStripe } from '@/lib/stripe'

export const dynamic = 'force-dynamic'
export const runtime = 'nodejs'
Expand Down Expand Up @@ -136,13 +128,6 @@ export async function POST(request: NextRequest) {
const webhookSecret = getWebhookSecret()
if (!signature || !webhookSecret) {
return NextResponse.json({ error: 'Webhook not configured' }, { status: 400 })
if (!process.env.STRIPE_WEBHOOK_SECRET || !process.env.STRIPE_SECRET_KEY) {
console.error('[stripe/webhook] Missing STRIPE_WEBHOOK_SECRET or STRIPE_SECRET_KEY in environment')
return NextResponse.json({ error: 'Webhook not configured' }, { status: 503 })
}

if (!signature) {
return NextResponse.json({ error: 'Missing stripe-signature header' }, { status: 400 })
}

let body: string
Expand Down
19 changes: 14 additions & 5 deletions app/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getCurrentUser } from '@/lib/auth'
import { DashboardHeader } from '@/components/dashboard-header'
import { DashboardSidebar } from '@/components/dashboard-sidebar'

export default async function DashboardLayout({
children,
Expand All @@ -14,10 +14,19 @@ export default async function DashboardLayout({
}

return (
<div className="min-h-screen bg-black text-white">
<DashboardHeader user={user} />
<main className="container mx-auto px-4 sm:px-6 lg:px-8 py-8 md:py-10">
{children}
<div className="flex h-screen bg-[#0D1117]">
{/* Sidebar */}
<DashboardSidebar user={user ? {
name: user.name || 'Developer',
email: user.email,
plan: 'pro'
} : undefined} />
Comment on lines +19 to +23

{/* Main Content */}
<main className="flex-1 overflow-auto ml-64">
<div className="p-8">
{children}
</div>
</main>
</div>
)
Expand Down
211 changes: 211 additions & 0 deletions components/dashboard-sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
'use client'

import Link from 'next/link'
import { usePathname } from 'next/navigation'
import {
Zap, LayoutDashboard, FolderGit2, Lightbulb, Bot,
BarChart3, Clock, GitPullRequest, Settings, Plug,
CreditCard, LogOut
} from 'lucide-react'
import { cn } from '@/lib/utils'

interface SidebarProps {
user?: {
name?: string
email?: string
plan?: 'free' | 'pro' | 'scale'
}
}

export function DashboardSidebar({ user }: SidebarProps) {
const pathname = usePathname()

const isActive = (href: string) => pathname === href

return (
<aside className="fixed left-0 top-0 h-screen w-64 bg-[#0D1117] border-r border-[#30363D] flex flex-col overflow-y-auto">
{/* Header */}
<div className="sticky top-0 p-4 border-b border-[#30363D] bg-[#0D1117]/95 backdrop-blur">
<Link href="/dashboard/analyses" className="flex items-center gap-2 px-3 py-2.5 rounded-lg bg-[#06B6D4] hover:bg-[#06B6D4]/90 transition-colors font-semibold text-[#0D1117]">
<Zap className="h-4 w-4" />
New Analysis
</Link>
</div>

{/* Navigation */}
<nav className="flex-1 px-3 py-4 space-y-1">

{/* Main */}
<div className="mb-6">
<Link
href="/dashboard"
className={cn(
'flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors text-sm font-medium',
isActive('/dashboard')
? 'bg-[#06B6D4]/10 border border-[#06B6D4]/30 text-[#06B6D4]'
: 'text-[#8B949E] hover:text-[#F0F6FC]'
)}
>
<LayoutDashboard className="h-4 w-4" />
Dashboard
</Link>
</div>

{/* Content */}
<div className="space-y-1 mb-6">
<p className="px-3 py-1.5 text-xs font-mono text-[#6B7280] uppercase tracking-widest">Content</p>

<Link
href="/dashboard/repositories"
className={cn(
'flex items-center justify-between px-3 py-2 rounded-lg transition-colors text-sm',
isActive('/dashboard/repositories')
? 'text-[#06B6D4]'
: 'text-[#8B949E] hover:text-[#F0F6FC]'
)}
>
<div className="flex items-center gap-3">
<FolderGit2 className="h-4 w-4" />
Repositories
</div>
<span className="text-xs bg-[#161B22] px-2 py-0.5 rounded border border-[#30363D]">12</span>
</Link>

<Link
href="/dashboard/idea-board"
className={cn(
'flex items-center justify-between px-3 py-2 rounded-lg transition-colors text-sm',
isActive('/dashboard/idea-board')
? 'text-[#F59E0B]'
: 'text-[#8B949E] hover:text-[#F0F6FC]'
)}
>
<div className="flex items-center gap-3">
<Lightbulb className="h-4 w-4" />
Ideas
</div>
<span className="text-xs bg-[#161B22] px-2 py-0.5 rounded border border-[#30363D]">47</span>
</Link>

<Link
href="/dashboard/agents"
className={cn(
'flex items-center gap-3 px-3 py-2 rounded-lg transition-colors text-sm',
isActive('/dashboard/agents')
? 'text-[#06B6D4]'
: 'text-[#8B949E] hover:text-[#F0F6FC]'
)}
>
<Bot className="h-4 w-4" />
Agents
</Link>
</div>

{/* Features */}
<div className="space-y-1 mb-6">
<p className="px-3 py-1.5 text-xs font-mono text-[#6B7280] uppercase tracking-widest">Features</p>

<Link
href="/dashboard/demand-intel"
className={cn(
'flex items-center gap-3 px-3 py-2 rounded-lg transition-colors text-sm text-[#8B949E] hover:text-[#F0F6FC]'
)}
>
<BarChart3 className="h-4 w-4" />
Demand Intel
</Link>

<Link
href="/dashboard/scheduled-scans"
className={cn(
'flex items-center gap-3 px-3 py-2 rounded-lg transition-colors text-sm text-[#8B949E] hover:text-[#F0F6FC]'
)}
>
<Clock className="h-4 w-4" />
Scheduled Scans
</Link>

<Link
href="/dashboard/idea-pipeline"
className={cn(
'flex items-center gap-3 px-3 py-2 rounded-lg transition-colors text-sm text-[#8B949E] hover:text-[#F0F6FC]'
)}
>
<GitPullRequest className="h-4 w-4" />
Idea Pipeline
</Link>
</div>

{/* Workspace */}
<div className="space-y-1">
<p className="px-3 py-1.5 text-xs font-mono text-[#6B7280] uppercase tracking-widest">Workspace</p>

<Link
href="/dashboard/integrations"
className={cn(
'flex items-center gap-3 px-3 py-2 rounded-lg transition-colors text-sm text-[#8B949E] hover:text-[#F0F6FC]'
)}
>
<Plug className="h-4 w-4" />
Integrations
</Link>

<Link
href="/dashboard/billing"
className={cn(
'flex items-center gap-3 px-3 py-2 rounded-lg transition-colors text-sm text-[#8B949E] hover:text-[#F0F6FC]'
)}
>
<CreditCard className="h-4 w-4" />
Billing
</Link>

<Link
href="/dashboard/settings"
className={cn(
'flex items-center gap-3 px-3 py-2 rounded-lg transition-colors text-sm text-[#8B949E] hover:text-[#F0F6FC]'
)}
>
<Settings className="h-4 w-4" />
Settings
</Link>
</div>
</nav>

{/* Recent Projects */}
<div className="border-t border-[#30363D] p-4 space-y-2">
<p className="px-2 text-xs font-mono text-[#6B7280] uppercase tracking-widest">Recent</p>
<div className="space-y-1">
<Link href="#" className="block px-2 py-1.5 text-xs text-[#8B949E] hover:text-[#F0F6FC] rounded hover:bg-[#161B22] transition-colors">
repofuse-core
</Link>
<Link href="#" className="block px-2 py-1.5 text-xs text-[#8B949E] hover:text-[#F0F6FC] rounded hover:bg-[#161B22] transition-colors">
repo-app-architect
</Link>
<Link href="#" className="block px-2 py-1.5 text-xs text-[#8B949E] hover:text-[#F0F6FC] rounded hover:bg-[#161B22] transition-colors">
memorial-sqr
</Link>
</div>
</div>

{/* User Profile */}
<div className="border-t border-[#30363D] p-4 space-y-3">
<div className="flex items-center gap-3">
<div className="h-10 w-10 rounded-full bg-[#06B6D4] flex items-center justify-center text-[#0D1117] font-bold text-sm">
{user?.name?.charAt(0) || 'U'}
</div>
<div className="flex-1 min-w-0">
<p className="text-xs font-semibold text-[#F0F6FC] truncate">{user?.name || 'User'}</p>
<p className="text-[10px] text-[#06B6D4] font-mono uppercase tracking-widest">
{user?.plan === 'pro' ? 'PRO PLAN' : 'FREE PLAN'}
</p>
</div>
</div>
<button className="w-full flex items-center gap-2 px-3 py-2 rounded-lg text-xs font-medium text-[#8B949E] hover:text-[#F0F6FC] hover:bg-[#161B22] transition-colors">
<LogOut className="h-3.5 w-3.5" />
Sign Out
</button>
Comment on lines +204 to +207
</div>
</aside>
)
}
Loading