feat: Implement data source management and analysis flow, allowing users to add and analyze websites for project opportunities.

This commit is contained in:
2026-02-03 20:35:03 +00:00
parent 885bbbf954
commit c47614bc66
9 changed files with 587 additions and 54 deletions

View File

@@ -1,44 +1,14 @@
import { NextRequest, NextResponse } from 'next/server'
import { isAuthenticatedNextjs } from "@convex-dev/auth/nextjs/server";
import { convexAuthNextjsToken, isAuthenticatedNextjs } from "@convex-dev/auth/nextjs/server";
import { fetchQuery } from "convex/nextjs";
import { api } from "@/convex/_generated/api";
import { z } from 'zod'
import { generateSearchQueries, getDefaultPlatforms } from '@/lib/query-generator'
import { executeSearches, scoreOpportunities } from '@/lib/search-executor'
import type { EnhancedProductAnalysis, SearchConfig, PlatformConfig } from '@/lib/types'
const searchSchema = z.object({
analysis: z.object({
productName: z.string(),
tagline: z.string(),
description: z.string(),
features: z.array(z.object({
name: z.string(),
description: z.string(),
benefits: z.array(z.string()),
useCases: z.array(z.string())
})),
problemsSolved: z.array(z.object({
problem: z.string(),
severity: z.enum(['high', 'medium', 'low']),
currentWorkarounds: z.array(z.string()),
emotionalImpact: z.string(),
searchTerms: z.array(z.string())
})),
keywords: z.array(z.object({
term: z.string(),
type: z.string(),
searchVolume: z.string(),
intent: z.string(),
funnel: z.string(),
emotionalIntensity: z.string()
})),
competitors: z.array(z.object({
name: z.string(),
differentiator: z.string(),
theirStrength: z.string(),
switchTrigger: z.string(),
theirWeakness: z.string()
}))
}),
projectId: z.string(),
config: z.object({
platforms: z.array(z.object({
id: z.string(),
@@ -65,7 +35,23 @@ export async function POST(request: NextRequest) {
}
const body = await request.json()
const { analysis, config } = searchSchema.parse(body)
const { projectId, config } = searchSchema.parse(body)
const token = await convexAuthNextjsToken();
const searchContext = await fetchQuery(
api.projects.getSearchContext,
{ projectId: projectId as any },
{ token }
);
if (!searchContext.context) {
return NextResponse.json(
{ error: 'No analysis available for selected sources.' },
{ status: 400 }
);
}
const analysis = searchContext.context as EnhancedProductAnalysis
console.log('🔍 Starting opportunity search...')
console.log(` Product: ${analysis.productName}`)
@@ -105,7 +91,8 @@ export async function POST(request: NextRequest) {
platform: q.platform,
strategy: q.strategy,
priority: q.priority
}))
})),
missingSources: searchContext.missingSources ?? []
}
})