From 025ce8f7633b778e90826c6c7d8e60942d128cd7 Mon Sep 17 00:00:00 2001 From: Matiss Jurevics Date: Wed, 4 Feb 2026 15:37:59 +0000 Subject: [PATCH] fixed docker --- app/api/analyze-manual/route.ts | 4 +- app/api/analyze/route.ts | 4 +- app/api/checkout/route.ts | 6 +-- app/api/search/route.ts | 22 ++++++--- app/layout.tsx | 5 +- app/onboarding/page.tsx | 26 +++++----- components/features-section.tsx | 2 +- components/hero-section.tsx | 2 +- convex/analysisJobs.ts | 7 ++- convex/opportunities.ts | 2 +- convex/searchJobs.ts | 7 ++- lib/openai.ts | 22 +++++++-- lib/scraper.ts | 2 +- package-lock.json | 86 ++++++++++++++++----------------- package.json | 4 +- tsconfig.json | 27 ++++++++--- 16 files changed, 135 insertions(+), 93 deletions(-) diff --git a/app/api/analyze-manual/route.ts b/app/api/analyze-manual/route.ts index ec6443d..ad678ca 100644 --- a/app/api/analyze-manual/route.ts +++ b/app/api/analyze-manual/route.ts @@ -90,7 +90,7 @@ export async function POST(request: NextRequest) { status: "failed", error: "OpenAI API key not configured", timeline: timeline.map((item) => - item.status === "running" ? { ...item, status: "failed" } : item + item.status === "running" ? { ...item, status: "failed" as const } : item ), }, { token } @@ -259,7 +259,7 @@ export async function POST(request: NextRequest) { status: "failed", error: error.message || "Manual analysis failed", timeline: timeline.map((item) => - item.status === "running" ? { ...item, status: "failed" } : item + item.status === "running" ? { ...item, status: "failed" as const } : item ), }, { token } diff --git a/app/api/analyze/route.ts b/app/api/analyze/route.ts index e52899c..8bd6935 100644 --- a/app/api/analyze/route.ts +++ b/app/api/analyze/route.ts @@ -89,7 +89,7 @@ export async function POST(request: NextRequest) { status: "failed", error: "OpenAI API key not configured", timeline: timeline.map((item) => - item.status === "running" ? { ...item, status: "failed" } : item + item.status === "running" ? { ...item, status: "failed" as const } : item ), }, { token } @@ -270,7 +270,7 @@ export async function POST(request: NextRequest) { status: "failed", error: error.message || "Analysis failed", timeline: timeline.map((item) => - item.status === "running" ? { ...item, status: "failed" } : item + item.status === "running" ? { ...item, status: "failed" as const } : item ), }, { token } diff --git a/app/api/checkout/route.ts b/app/api/checkout/route.ts index 16ef5b9..ca8ead4 100644 --- a/app/api/checkout/route.ts +++ b/app/api/checkout/route.ts @@ -1,7 +1,7 @@ import { Checkout } from "@polar-sh/nextjs"; -import { NextResponse } from "next/server"; +import { NextRequest, NextResponse } from "next/server"; -export const GET = async () => { +export const GET = async (request: NextRequest) => { if (!process.env.POLAR_ACCESS_TOKEN || !process.env.POLAR_SUCCESS_URL) { return NextResponse.json( { @@ -17,5 +17,5 @@ export const GET = async () => { successUrl: process.env.POLAR_SUCCESS_URL, }); - return handler(); + return handler(request); }; diff --git a/app/api/search/route.ts b/app/api/search/route.ts index 4aaff4c..50b1baa 100644 --- a/app/api/search/route.ts +++ b/app/api/search/route.ts @@ -39,6 +39,7 @@ const bodySchema = z.object({ }) export async function POST(request: NextRequest) { + let ageFilters: SerperAgeFilter | undefined try { const requestId = request.headers.get("x-request-id") ?? undefined; if (!(await isAuthenticatedNextjs())) { @@ -51,7 +52,7 @@ export async function POST(request: NextRequest) { const body = await request.json() const { analysis, minAgeDays, maxAgeDays } = bodySchema.parse(body) - const ageFilters: SerperAgeFilter = { + ageFilters = { minAgeDays, maxAgeDays, } @@ -287,15 +288,15 @@ async function analyzeOpportunities( const relevanceScore = Math.min(keywordScore + problemScore, 1) // Determine intent - let intent: Opportunity['intent'] = 'looking-for' + let intent: Opportunity['intent'] = 'looking' if (content.includes('frustrated') || content.includes('hate') || content.includes('sucks')) { intent = 'frustrated' } else if (content.includes('alternative') || content.includes('switching')) { - intent = 'alternative' + intent = 'comparing' } else if (content.includes('vs') || content.includes('comparison') || content.includes('better')) { - intent = 'comparison' + intent = 'comparing' } else if (content.includes('how to') || content.includes('fix') || content.includes('solution')) { - intent = 'problem-solving' + intent = 'learning' } // Find matching persona @@ -305,16 +306,21 @@ async function analyzeOpportunities( if (relevanceScore >= 0.3) { opportunities.push({ + id: result.url, title: result.title, url: result.url, + platform: result.source, source: result.source, snippet: result.snippet.slice(0, 300), relevanceScore, - painPoints: matchedProblems.slice(0, 3), - suggestedApproach: generateApproach(intent, analysis.productName), matchedKeywords: matchedKeywords.slice(0, 5), + matchedProblems: matchedProblems.slice(0, 3), matchedPersona, - intent + intent, + emotionalIntensity: intent === 'frustrated' ? 'high' : matchedProblems.length > 0 ? 'medium' : 'low', + status: 'new', + suggestedApproach: generateApproach(intent, analysis.productName), + softPitch: false }) } } diff --git a/app/layout.tsx b/app/layout.tsx index 4f4c036..7def0ff 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,12 +1,9 @@ import type { Metadata } from 'next' -import { Inter } from 'next/font/google' import './globals.css' import ConvexClientProvider from './ConvexClientProvider' import { ConvexAuthNextjsServerProvider } from "@convex-dev/auth/nextjs/server"; import { ThemeProvider } from "@/components/theme-provider"; -const inter = Inter({ subsets: ['latin'] }) - export const metadata: Metadata = { title: 'Sanati - Find Product Opportunities', description: 'AI-powered product research and opportunity finding', @@ -20,7 +17,7 @@ export default function RootLayout({ return ( - + { const userId = await getAuthUserId(ctx); diff --git a/convex/opportunities.ts b/convex/opportunities.ts index 59b17ce..ce54aea 100644 --- a/convex/opportunities.ts +++ b/convex/opportunities.ts @@ -58,7 +58,7 @@ export const listByProject = query({ if (args.minScore !== undefined) { queryBuilder = queryBuilder.filter((q) => - q.gte(q.field("relevanceScore"), args.minScore) + q.gte(q.field("relevanceScore"), args.minScore as number) ); } diff --git a/convex/searchJobs.ts b/convex/searchJobs.ts index 8d33292..30a1394 100644 --- a/convex/searchJobs.ts +++ b/convex/searchJobs.ts @@ -64,7 +64,12 @@ export const update = mutation({ export const listByProject = query({ args: { projectId: v.id("projects"), - status: v.optional(v.string()), + status: v.optional(v.union( + v.literal("pending"), + v.literal("running"), + v.literal("completed"), + v.literal("failed") + )), }, handler: async (ctx, args) => { const userId = await getAuthUserId(ctx); diff --git a/lib/openai.ts b/lib/openai.ts index 931b9a5..1f9ccc0 100644 --- a/lib/openai.ts +++ b/lib/openai.ts @@ -180,13 +180,20 @@ Return JSON: const analysis = JSON.parse(jsonStr) return { + id: result.url, title: result.title, url: result.url, + platform: result.source, source: result.source, snippet: result.snippet.slice(0, 300), relevanceScore: analysis.relevanceScore || 0, - painPoints: analysis.painPoints || [], - suggestedApproach: analysis.suggestedApproach || '' + emotionalIntensity: (analysis.painPoints || []).length > 2 ? "high" : (analysis.painPoints || []).length > 0 ? "medium" : "low", + intent: "looking", + matchedKeywords: [], + matchedProblems: analysis.painPoints || [], + status: "new", + suggestedApproach: analysis.suggestedApproach || '', + softPitch: false, } } catch (e) { // Fallback simple analysis @@ -195,13 +202,20 @@ Return JSON: const relevance = Math.min(overlap / Math.max(product.keywords.length * 0.5, 1), 1) return { + id: result.url, title: result.title, url: result.url, + platform: result.source, source: result.source, snippet: result.snippet.slice(0, 300), relevanceScore: relevance, - painPoints: ['Related to product domain'], - suggestedApproach: 'Share relevant insights about their problem' + emotionalIntensity: "low", + intent: "looking", + matchedKeywords: product.keywords.slice(0, 5), + matchedProblems: ['Related to product domain'], + status: "new", + suggestedApproach: 'Share relevant insights about their problem', + softPitch: false, } } } diff --git a/lib/scraper.ts b/lib/scraper.ts index c6c2c8b..77aa503 100644 --- a/lib/scraper.ts +++ b/lib/scraper.ts @@ -24,7 +24,7 @@ export async function scrapeWebsite(url: string): Promise { let browser try { browser = await puppeteer.launch({ - headless: 'new', + headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'] }) diff --git a/package-lock.json b/package-lock.json index a94e453..21f6573 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,7 +33,7 @@ "jose": "^6.1.3", "lucide-react": "^0.563.0", "mini-svg-data-uri": "^1.4.4", - "next": "^15.0.0", + "next": "16.1.6", "next-themes": "^0.4.6", "openai": "^4.28.0", "puppeteer": "^22.0.0", @@ -1119,15 +1119,15 @@ } }, "node_modules/@next/env": { - "version": "15.5.11", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.11.tgz", - "integrity": "sha512-g9s5SS9gC7GJCEOR3OV3zqs7C5VddqxP9X+/6BpMbdXRkqsWfFf2CJPBZNvNEtAkKTNuRgRXAgNxSAXzfLdaTg==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz", + "integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==", "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz", - "integrity": "sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz", + "integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==", "cpu": [ "arm64" ], @@ -1141,9 +1141,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz", - "integrity": "sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz", + "integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==", "cpu": [ "x64" ], @@ -1157,9 +1157,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz", - "integrity": "sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz", + "integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==", "cpu": [ "arm64" ], @@ -1173,9 +1173,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz", - "integrity": "sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz", + "integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==", "cpu": [ "arm64" ], @@ -1189,9 +1189,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz", - "integrity": "sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz", + "integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==", "cpu": [ "x64" ], @@ -1205,9 +1205,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz", - "integrity": "sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz", + "integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==", "cpu": [ "x64" ], @@ -1221,9 +1221,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz", - "integrity": "sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz", + "integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==", "cpu": [ "arm64" ], @@ -1237,9 +1237,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.7", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz", - "integrity": "sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz", + "integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==", "cpu": [ "x64" ], @@ -2821,7 +2821,6 @@ }, "node_modules/baseline-browser-mapping": { "version": "2.9.19", - "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -4083,13 +4082,14 @@ } }, "node_modules/next": { - "version": "15.5.11", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.11.tgz", - "integrity": "sha512-L2KPiKmqTDpRdeVDdPjhf43g2/VPe0NCNndq7OKDCgOLWtxe1kbr/zXGIZtYY7kZEAjRf7Bj/mwUFSr+tYC2Yg==", + "version": "16.1.6", + "resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz", + "integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==", "license": "MIT", "dependencies": { - "@next/env": "15.5.11", + "@next/env": "16.1.6", "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" @@ -4098,18 +4098,18 @@ "next": "dist/bin/next" }, "engines": { - "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.7", - "@next/swc-darwin-x64": "15.5.7", - "@next/swc-linux-arm64-gnu": "15.5.7", - "@next/swc-linux-arm64-musl": "15.5.7", - "@next/swc-linux-x64-gnu": "15.5.7", - "@next/swc-linux-x64-musl": "15.5.7", - "@next/swc-win32-arm64-msvc": "15.5.7", - "@next/swc-win32-x64-msvc": "15.5.7", - "sharp": "^0.34.3" + "@next/swc-darwin-arm64": "16.1.6", + "@next/swc-darwin-x64": "16.1.6", + "@next/swc-linux-arm64-gnu": "16.1.6", + "@next/swc-linux-arm64-musl": "16.1.6", + "@next/swc-linux-x64-gnu": "16.1.6", + "@next/swc-linux-x64-musl": "16.1.6", + "@next/swc-win32-arm64-msvc": "16.1.6", + "@next/swc-win32-x64-msvc": "16.1.6", + "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", diff --git a/package.json b/package.json index 967078c..0733b07 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "dev": "next dev", - "build": "next build", + "build": "NEXT_DISABLE_TURBOPACK=1 next build", "start": "next start", "lint": "next lint" }, @@ -34,7 +34,7 @@ "jose": "^6.1.3", "lucide-react": "^0.563.0", "mini-svg-data-uri": "^1.4.4", - "next": "^15.0.0", + "next": "16.1.6", "next-themes": "^0.4.6", "openai": "^4.28.0", "puppeteer": "^22.0.0", diff --git a/tsconfig.json b/tsconfig.json index e7ff90f..9e9bbf7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,10 @@ { "compilerOptions": { - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -10,7 +14,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", + "jsx": "react-jsx", "incremental": true, "plugins": [ { @@ -18,9 +22,20 @@ } ], "paths": { - "@/*": ["./*"] - } + "@/*": [ + "./*" + ] + }, + "target": "ES2017" }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] }