From f104a02f94414e329d71884f99b76923f2fa3fd4 Mon Sep 17 00:00:00 2001 From: Leander Cain Slotosch Date: Thu, 7 May 2026 15:45:48 +0200 Subject: [PATCH 1/5] Make INSFORGE_URL required --- .env.example | 3 +++ .gitignore | 5 +++-- .vscode/mcp.json | 14 -------------- README.md | 14 +++++++------- lib/env.ts | 39 ++++++++++++++++++++++++++++++--------- 5 files changed, 43 insertions(+), 32 deletions(-) create mode 100644 .env.example delete mode 100644 .vscode/mcp.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..fe346fc --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +DECODO_API_KEY= +INSFORGE_API_KEY= +INSFORGE_URL=https://YOUR_REF.YOUR_REGION.insforge.app diff --git a/.gitignore b/.gitignore index e8eb7fb..f3bc09a 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,8 @@ /build # misc +.vscode +.idea .DS_Store *.pem @@ -32,6 +34,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* +!.env.example # vercel .vercel @@ -39,5 +42,3 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts - -.env.local diff --git a/.vscode/mcp.json b/.vscode/mcp.json deleted file mode 100644 index 5fcbc46..0000000 --- a/.vscode/mcp.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "servers": { - "mcp": { - "type": "stdio", - "command": "npx", - "args": ["-y", "@insforge/mcp@latest"], - "env": { - "API_KEY": "your_api_key_here", - "API_BASE_URL": "https://uk39hjmz.us-east.insforge.app" - } - } - }, - "inputs": [] -} \ No newline at end of file diff --git a/README.md b/README.md index 22f27d1..a6dad7a 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Validly is a Next.js application that scrapes weekly Reddit discussions from any - Node.js 20+ installed - A [Decodo](https://visit.decodo.com/oNza7b) API key (for Reddit scraping) -- An [Insforge](https://insforge.dev) API key (for AI analysis) +- An [Insforge](https://insforge.dev) project backend URL (`*.insforge.app`) and API key (for AI analysis) ## 🛠️ Installation @@ -50,9 +50,9 @@ Validly is a Next.js application that scrapes weekly Reddit discussions from any # Required DECODO_API_KEY=your_decodo_api_key_here INSFORGE_API_KEY=your_insforge_api_key_here + INSFORGE_URL=https://YOUR_REF.YOUR_REGION.insforge.app # Optional - Customize Insforge configuration - INSFORGE_URL=https://api.insforge.dev INSFORGE_MODEL=openai/gpt-4o-mini INSFORGE_RESULTS_TABLE=validated_saas_ideas @@ -80,11 +80,11 @@ Validly is a Next.js application that scrapes weekly Reddit discussions from any 3. Navigate to your dashboard 4. Copy your API key -### Insforge API Key +### Insforge API key and backend URL 1. Visit [insforge.dev](https://insforge.dev) -2. Create an account -3. Go to your project settings -4. Generate and copy your API key +2. Create an account and open your project +3. Copy the backend/API base URL (format `https://..insforge.app`) into `INSFORGE_URL` +4. Generate and copy your API key into `INSFORGE_API_KEY` ## 📁 Project Structure @@ -166,7 +166,7 @@ Analyzes a subreddit for SaaS opportunities. |----------|----------|---------|-------------| | `DECODO_API_KEY` | ✅ Yes | - | Your Decodo API key for Reddit scraping | | `INSFORGE_API_KEY` | ✅ Yes | - | Your Insforge API key for AI analysis | -| `INSFORGE_URL` | ❌ No | `https://api.insforge.dev` | Insforge API base URL | +| `INSFORGE_URL` | ✅ Yes | - | Insforge project backend URL (`https://..insforge.app`) | | `INSFORGE_MODEL` | ❌ No | `openai/gpt-4o-mini` | AI model to use | | `INSFORGE_RESULTS_TABLE` | ❌ No | - | Optional database table name | | `DECODO_PROXY_POOL` | ❌ No | `residential` | Proxy pool type for scraping | diff --git a/lib/env.ts b/lib/env.ts index 26697e0..31735b5 100644 --- a/lib/env.ts +++ b/lib/env.ts @@ -1,18 +1,39 @@ import { z } from "zod"; -const optionalUrl = z - .string() +const insforgeProjectUrlSchema = z + .string({ + error: (issue) => + issue.input === undefined + ? "INSFORGE_URL is required: copy your project backend URL from the InsForge dashboard (format https://..insforge.app)." + : issue.message, + }) .trim() - .optional() - .transform((value) => (value ? value.replace(/\/+$/, "") : undefined)) - .refine((value) => !value || /^https?:\/\//.test(value), { - message: "Expected a valid URL", - }); + .min( + 1, + "INSFORGE_URL is required: copy your project backend URL from the InsForge dashboard (format https://..insforge.app).", + ) + .transform((value) => value.replace(/\/+$/, "")) + .refine((value) => /^https?:\/\//.test(value), { + error: "INSFORGE_URL must be a valid http(s) URL", + }) + .refine( + (value) => { + try { + return new URL(value).hostname.toLowerCase() !== "api.insforge.dev"; + } catch { + return false; + } + }, + { + error: + "INSFORGE_URL must be your project backend (*.insforge.app from the InsForge dashboard), not api.insforge.dev.", + }, + ); const serverEnvSchema = z.object({ DECODO_API_KEY: z.string().trim().min(1, "DECODO_API_KEY is required"), INSFORGE_API_KEY: z.string().trim().min(1, "INSFORGE_API_KEY is required"), - INSFORGE_URL: optionalUrl, + INSFORGE_URL: insforgeProjectUrlSchema, INSFORGE_MODEL: z.string().trim().optional(), INSFORGE_RESULTS_TABLE: z.string().trim().optional(), DECODO_PROXY_POOL: z.string().trim().optional(), @@ -58,7 +79,7 @@ export function getServerEnv(): ServerEnv { decodoHeadlessMode: parsedEnv.DECODO_HEADLESS_MODE || "html", decodoTimeoutMs: parsedEnv.DECODO_TIMEOUT_MS ?? 20000, insforgeApiKey: parsedEnv.INSFORGE_API_KEY, - insforgeUrl: parsedEnv.INSFORGE_URL ?? "https://api.insforge.dev", + insforgeUrl: parsedEnv.INSFORGE_URL, insforgeModel: parsedEnv.INSFORGE_MODEL ?? "openai/gpt-4o-mini", insforgeTimeoutMs: parsedEnv.INSFORGE_TIMEOUT_MS ?? 90000, insforgeResultsTable: parsedEnv.INSFORGE_RESULTS_TABLE || undefined, From 3568b620e3280a40d33da663073799f663a39c29 Mon Sep 17 00:00:00 2001 From: Leander Cain Slotosch Date: Thu, 7 May 2026 15:46:10 +0200 Subject: [PATCH 2/5] Remove unused component --- app/page.tsx | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 2af0fa6..fc588f4 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -150,41 +150,6 @@ function IdeaCard({ idea }: { idea: SaasIdea }) { ); } -function ThreadCard({ post }: { post: RedditPost }) { - return ( -
-
-
-

- Source Thread -

-

{post.title}

-
- - Open Thread - -
- -
- {post.comments.length > 0 ? post.comments.map((comment) => ( -
- {comment} -
- )) : ( -

- No comments were extracted for this thread, but the title still contributed to the analysis. -

- )} -
-
- ); -} - export default function Home() { const [subreddit, setSubreddit] = useState("saas"); const [lastSubreddit, setLastSubreddit] = useState("saas"); From 52861545abcf34f206a147c424ea068936a4f864 Mon Sep 17 00:00:00 2001 From: Leander Cain Slotosch Date: Thu, 7 May 2026 15:48:10 +0200 Subject: [PATCH 3/5] Update repository metadata and fix styles --- CONTRIBUTING.md | 4 ++-- README.md | 2 +- app/page.tsx | 14 +++++++------- package.json | 8 ++++++++ 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3f6e25..b08e45b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,10 +4,10 @@ Thank you for your interest in contributing to Validly! This document provides g ## Getting Started -1. Fork the repository on GitHub +1. Fork [Nova-Designs-Creative/validly](https://github.com/Nova-Designs-Creative/validly) on GitHub 2. Clone your fork locally: ```bash - git clone https://github.com/yourusername/validly.git + git clone https://github.com//validly.git cd validly ``` 3. Install dependencies: diff --git a/README.md b/README.md index a6dad7a..2cd5365 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Validly is a Next.js application that scrapes weekly Reddit discussions from any 1. **Clone the repository** ```bash - git clone https://github.com/yourusername/validly.git + git clone https://github.com/Nova-Designs-Creative/validly.git cd validly ``` diff --git a/app/page.tsx b/app/page.tsx index fc588f4..eb0ca05 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; -import type { AnalyzeIdeasResponse, RedditPost, SaasIdea } from "@/lib/types"; +import type { AnalyzeIdeasResponse, SaasIdea } from "@/lib/types"; const EXAMPLE_SUBREDDITS = ["saas", "smallbusiness", "freelance", "marketing"]; const LOADING_PHASES = [ @@ -216,7 +216,7 @@ export default function Home() { return (
-
+

@@ -232,7 +232,7 @@ export default function Home() {

-
+