Skip to content

feat: implement document upload infrastructure, UI components, and ba…#2

Merged
ivancidev merged 4 commits into
mainfrom
feature/implement-rag
Jun 9, 2026
Merged

feat: implement document upload infrastructure, UI components, and ba…#2
ivancidev merged 4 commits into
mainfrom
feature/implement-rag

Conversation

@ivancidev

@ivancidev ivancidev commented Jun 9, 2026

Copy link
Copy Markdown
Owner

PR Summary by Qodo

Implement RAG upload + chat API with Supabase vectors, Cohere embeddings, and Groq
✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🕐 40+ Minutes

Grey Divider

Walkthroughs

User Description

…ckend integration for PDF and text processing

AI Description
• Add /api/upload to parse PDFs/text, chunk content, embed via Cohere, and index Supabase.
• Add /api/chat RAG endpoint: embed query, retrieve context via Supabase RPC, answer with Groq.
• Route frontend calls through internal APIs and refresh landing/upload UI styling.
Diagram
graph TD
  UUI(["Upload UI"]) --> AUP[["/api/upload"]] --> DP["PDF/text processing"] --> COH{{"Cohere Embed"}} --> SB[("Supabase pgvector")]
  CUI(["Chat UI"]) --> ACH[["/api/chat"]] --> COH --> SB
  ACH --> GRQ{{"Groq Chat"}}

  subgraph Legend
    direction LR
    _ui(["UI Page"]) ~~~ _api[["API Route"]] ~~~ _proc["Processor"] ~~~ _ext{{"External API"}} ~~~ _db[("Database")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use Supabase Edge Functions for upload/chat APIs
  • ➕ Keeps server-side logic closer to Supabase and can simplify secret management
  • ➕ Potentially lower latency to Supabase for vector retrieval
  • ➖ PDF parsing via pdf-parse may be difficult or impossible on Edge runtimes
  • ➖ Operational complexity if mixing Edge + Node runtimes across endpoints
2. Keep n8n webhooks as the sole backend (as in existing prompt doc)
  • ➕ Reduces custom backend code in the Next.js app
  • ➕ Centralizes orchestration and retries in n8n
  • ➖ Extra network hop and more moving parts for core UX flows
  • ➖ Harder to version and test alongside the frontend compared to in-repo routes
3. Use Supabase client with RLS + per-user auth instead of service role
  • ➕ Improves security posture by avoiding service role access in application code
  • ➕ Enables per-user document isolation with database-enforced policies
  • ➖ Requires auth flows and schema/policy work (more product scope)
  • ➖ May complicate server-side ingestion if embeddings storage must bypass RLS

Recommendation: The current Node.js route approach is a pragmatic fit given PDF parsing requirements and the need to keep secrets server-side. However, reviewers should explicitly validate (1) service-role usage is only on the server and never exposed to clients, (2) Node runtime requirements (pdf-parse and Supabase packages indicate Node >=20.16+) match deployment, and (3) documentation alignment—doqify-prompt.md currently states “no backend routes needed,” which conflicts with this implementation.

Grey Divider

File Changes

Enhancement (11)
route.ts Create RAG chat endpoint using Supabase vector search and Groq completion +103/-0

Create RAG chat endpoint using Supabase vector search and Groq completion

• Adds POST /api/chat that embeds the question (Cohere), retrieves top-matching chunks via Supabase RPC match_documents, and sends a context-grounded system prompt to Groq. Returns answer plus unique source filenames.

app/api/chat/route.ts


route.ts Create upload/index endpoint for PDFs and pasted text +122/-0

Create upload/index endpoint for PDFs and pasted text

• Adds POST /api/upload supporting multipart PDF upload or JSON text payloads, extracts/validates text, chunks with overlap, batches Cohere embeddings, and stores records in Supabase. Implements overwrite semantics by deleting prior chunks for the same document name.

app/api/upload/route.ts


page.tsx Polish landing page copy and visual styling +41/-35

Polish landing page copy and visual styling

• Updates feature descriptions and refreshes layout/CTA styling with background glow effects and refined Tailwind classes. Content now references the underlying RAG stack (Supabase/Cohere/Groq).

app/page.tsx


page.tsx Upgrade upload page layout styling and messaging +16/-13

Upgrade upload page layout styling and messaging

• Adds glow background effects, updates headings and explanatory text to reference vectorization, and refines container/badge styling around the upload tabs and document list.

app/upload/page.tsx


DocumentList.tsx Restyle document list and status badges +23/-19

Restyle document list and status badges

• Adjusts empty state and list row visuals, improves hover/spacing, and updates status color tokens and badge styling for uploading/ready/error states.

features/upload/components/DocumentList.tsx


DropZone.tsx Restyle dropzone interactions and tighten max-size hint +24/-27

Restyle dropzone interactions and tighten max-size hint

• Refreshes Tailwind styling for active/reject states and icon treatment, plus tweaks copy (including max size hint). Removes the file-level "use client" directive even though hooks are used, which may require ensuring a parent client boundary.

features/upload/components/DropZone.tsx


TextInput.tsx Restyle text upload form and CTA button +11/-11

Restyle text upload form and CTA button

• Updates label typography, input/textarea styling, placeholders, and submit button visuals while preserving validation logic and disabled states during upload.

features/upload/components/TextInput.tsx


UploadTabs.tsx Enhance upload method tabs and processing callout +18/-10

Enhance upload method tabs and processing callout

• Switches Tabs to a secondary variant, applies stronger selected-state styling, and upgrades the uploading banner to a more descriptive processing message (text extraction + embeddings).

features/upload/components/UploadTabs.tsx


documentProcessor.ts Add PDF parsing and robust text chunking utilities +151/-0

Add PDF parsing and robust text chunking utilities

• Implements PDF text extraction using pdf-parse with dynamic worker resolution (including pnpm layout and Windows file:// handling) plus a chunker that prefers natural boundaries and supports overlap with safeguards.

lib/documentProcessor.ts


embeddings.ts Add Cohere embedding client for document/query vectors +66/-0

Add Cohere embedding client for document/query vectors

• Implements getEmbeddings() calling Cohere v1/embed with model embed-multilingual-v3.0 and handling both float-typed and array embedding response shapes, with environment validation and error surfacing.

lib/embeddings.ts


groq.ts Add Groq chat completion client +48/-0

Add Groq chat completion client

• Implements getGroqChatCompletion() against Groq's OpenAI-compatible endpoint with configurable model/temperature and environment validation.

lib/groq.ts


Documentation (1)
doqify-prompt.md Add build prompt/documentation for project setup and UI structure +458/-0

Add build prompt/documentation for project setup and UI structure

• Introduces extensive instructions for editor-assisted project scaffolding, UI component expectations, and environment configuration. Note: the doc states n8n-only backend, which diverges from the added /api/* routes in this PR.

doqify-prompt.md


Other (5)
.env.example Add Supabase, Cohere, and Groq environment variable examples +8/-0

Add Supabase, Cohere, and Groq environment variable examples

• Introduces placeholders for Supabase URL and service role key plus Cohere/Groq API keys to support server-side embedding and chat completion flows.

.env.example


n8n.ts Repoint client webhook calls to internal Next.js API routes +2/-2

Repoint client webhook calls to internal Next.js API routes

• Changes upload/query endpoints from NEXT_PUBLIC_N8N_* env URLs to /api/upload and /api/chat, shifting the integration from external webhooks to in-app backend routes.

lib/n8n.ts


supabase.ts Add server-side Supabase client using service role key +15/-0

Add server-side Supabase client using service role key

• Creates a Supabase client configured with service role credentials and disables session persistence/refresh to suit server-side API route usage.

lib/supabase.ts


package.json Add Supabase and PDF parsing dependencies +3/-0

Add Supabase and PDF parsing dependencies

• Adds @supabase/supabase-js and pdf-parse runtime deps and @types/pdf-parse for TypeScript support to enable server-side ingestion and PDF processing.

package.json


pnpm-lock.yaml Lock new Supabase and pdf-parse dependency graph +206/-0

Lock new Supabase and pdf-parse dependency graph

• Updates the lockfile with Supabase packages and pdf-parse/pdfjs-dist (including optional native canvas binaries), reflecting the new server-side capabilities.

pnpm-lock.yaml


Grey Divider

Qodo Logo

…ckend integration for PDF and text processing
@ivancidev ivancidev self-assigned this Jun 9, 2026
@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
doqify Ready Ready Preview, Comment Jun 9, 2026 4:17am

@qodo-code-review

qodo-code-review Bot commented Jun 9, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (1) 📎 Requirement gaps (0) 🎨 UX issues (0)

Grey Divider


Action required

1. DropZone missing use client 📘 Rule violation ≡ Correctness
Description
features/upload/components/DropZone.tsx uses React hooks (useCallback, useDropzone) but the PR
removed the required "use client" directive, causing Next.js to treat it as a Server Component and
fail at build/runtime. This breaks the repository’s Next.js App Router component conventions and can
disrupt the upload UI because UploadTabs (a Client Component) imports DropZone.
Code

features/upload/components/DropZone.tsx[R1-3]

import { useCallback } from "react";
import { useDropzone } from "react-dropzone";
import { CloudUpload, FileText, AlertCircle } from "lucide-react";
Evidence
The repo’s rules require following Next.js App Router conventions to avoid build/runtime failures,
and the modified DropZone now imports/uses client-only hooks without a top-level "use client"
directive, which is invalid for Server Components. Because DropZone is also imported by
UploadTabs, a known Client Component, the missing directive will surface as an invalid import
graph / server-component hook usage error during build or runtime.

AGENTS.md: Use Project-Specific Next.js APIs and Avoid Deprecated/Removed Features: AGENTS.md: Use Project-Specific Next.js APIs and Avoid Deprecated/Removed Features: AGENTS.md: Use Project-Specific Next.js APIs and Avoid Deprecated/Removed Features: AGENTS.md: Use Project-Specific Next.js APIs and Avoid Deprecated/Removed Features: AGENTS.md: Use Project-Specific Next.js APIs and Avoid Deprecated/Removed Features: AGENTS.md: Use Project-Specific Next.js APIs and Avoid Deprecated/Removed Features: AGENTS.md: Use Project-Specific Next.js APIs and Avoid Deprecated/Removed Features: AGENTS.md: Use Project-Specific Next.js APIs and Avoid Deprecated/Removed Features
features/upload/components/DropZone.tsx[1-27]
features/upload/components/DropZone.tsx[1-28]
features/upload/components/UploadTabs.tsx[1-6]
features/upload/components/UploadTabs.tsx[38-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`features/upload/components/DropZone.tsx` uses client-only React hooks (`useCallback`, `useDropzone`) but is missing the required `"use client";` directive at the top of the file, so Next.js treats it as a Server Component by default and will error at build/runtime.
## Issue Context
This PR removed `"use client"` from `DropZone.tsx` while keeping hook usage, and `UploadTabs.tsx` is a Client Component that imports `DropZone`, making the issue surface as a Next.js App Router Server/Client component boundary violation.
## Fix Focus Areas
- features/upload/components/DropZone.tsx[1-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Unauthenticated service-role API 🐞 Bug ⛨ Security
Description
The new /api/upload and /api/chat routes perform privileged Supabase operations using a client
initialized with SUPABASE_SERVICE_ROLE_KEY, but the routes have no authentication/authorization
checks. Any caller who can reach these endpoints can insert/delete document chunks and query
document contents via vector search, and can also burn Cohere/Groq API quota.
Code

app/api/upload/route.ts[R76-97]

+    // Overwrite behavior: Delete existing document chunks with the same name
+    // to avoid duplicate search results.
+    const { error: deleteError } = await supabase
+      .from("documents")
+      .delete()
+      .eq("name", name);
+
+    if (deleteError) {
+      console.warn("Could not delete old chunks (non-blocking):", deleteError.message, deleteError.details);
+    }
+
+    // Insert new chunks
+    const records = chunks.map((chunk, index) => ({
+      name,
+      content: chunk,
+      embedding: embeddings[index],
+    }));
+
+    const { error: insertError } = await supabase
+      .from("documents")
+      .insert(records);
+
Evidence
The Supabase client is created with the service-role key, and both API routes use it for
dele****************PC with no auth checks. Client-side code calls these endpoints, demonstrating
they are intended to be hit from the browser.

lib/supabase.ts[3-15]
app/api/upload/route.ts[76-97]
app/api/chat/route.ts[32-40]
lib/n8n.ts[4-6]
features/upload/hooks/useUpload.ts[1-6]
features/upload/hooks/useUpload.ts[12-27]
features/chat/hooks/useChat.ts[1-5]
features/chat/hooks/useChat.ts[11-26]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`/api/upload` and `/api/chat` are publicly callable but use a Supabase client created with `SUPABASE_SERVICE_ROLE_KEY`, enabling unauthorized document mutation/query and paid API usage.
### Issue Context
- Client-side hooks call `/api/upload` and `/api/chat` directly.
- Service-role bypasses RLS; without per-user scoping this becomes admin access from the internet.
### Fix Focus Areas
- app/api/upload/route.ts[9-18]
- app/api/upload/route.ts[76-97]
- app/api/chat/route.ts[8-40]
- lib/supabase.ts[3-15]
- lib/n8n.ts[4-6]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. No upload size validation 🐞 Bug ☼ Reliability
Description
The upload route reads the full uploaded file into memory and attempts to parse it without enforcing
file size or MIME-type checks, which can cause memory/timeouts and unnecessary embedding spend. The
UI claims a 4MB limit, but the DropZone configuration does not enforce it either.
Code

app/api/upload/route.ts[R14-34]

+    const contentType = request.headers.get("content-type") || "";
+
+    if (contentType.includes("multipart/form-data")) {
+      const formData = await request.formData();
+      const file = formData.get("file") as File;
+      if (!file) {
+        return NextResponse.json(
+          { success: false, error: "No file provided" },
+          { status: 400 }
+        );
+      }
+      name = file.name;
+      const arrayBuffer = await file.arrayBuffer();
+      const buffer = Buffer.from(arrayBuffer);
+      text = await parsePDF(buffer);
+    } else {
+      // JSON payload for pasted text
+      const body = await request.json();
+      text = body.text;
+      name = body.name;
+    }
Evidence
The server converts the full upload into a Buffer and parses it without any size/type checks, while
the DropZone config only restricts extensions and count (not max size) despite stating a 4MB limit
in the UI.

app/api/upload/route.ts[14-34]
features/upload/components/DropZone.tsx[21-27]
features/upload/components/DropZone.tsx[79-84]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Uploads lack hard validation for file type and size; the server buffers the entire file and parses it, and the client dropzone doesn’t enforce the stated limit.
### Issue Context
- UI text says "Máx. 4 MB".
- `useDropzone` is missing `maxSize`.
- Server route does not check `file.size`, `file.type`, or `Content-Length`.
### Fix Focus Areas
- app/api/upload/route.ts[14-34]
- features/upload/components/DropZone.tsx[21-27]
- features/upload/components/DropZone.tsx[79-84]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Supabase errors exposed 🐞 Bug ⛨ Security
Description
The upload API returns Supabase error message/details/hint directly in the JSON response, which can
expose internal database/schema information unnecessarily. This information disclosure can make
targeted abuse easier and should be replaced with a generic client-safe error.
Code

app/api/upload/route.ts[R98-106]

+    if (insertError) {
+      console.error("Supabase insert error:", insertError.message, insertError.details, insertError.hint);
+      return NextResponse.json(
+        { 
+          success: false, 
+          error: `Failed to store document: ${insertError.message}. Details: ${insertError.details || 'None'}. Hint: ${insertError.hint || 'None'}` 
+        },
+        { status: 500 }
+      );
Evidence
The route explicitly interpolates insertError.details and insertError.hint into the
client-facing error string.

app/api/upload/route.ts[98-106]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The API response includes `insertError.details` and `insertError.hint`, leaking internal DB information to clients.
### Issue Context
Detailed error fields are useful for server logs but should not be returned to browsers.
### Fix Focus Areas
- app/api/upload/route.ts[98-106]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines 1 to 3
import { useCallback } from "react";
import { useDropzone } from "react-dropzone";
import { CloudUpload, FileText, AlertCircle } from "lucide-react";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. dropzone missing use client 📘 Rule violation ≡ Correctness

features/upload/components/DropZone.tsx uses React hooks (useCallback, useDropzone) but the PR
removed the required "use client" directive, causing Next.js to treat it as a Server Component and
fail at build/runtime. This breaks the repository’s Next.js App Router component conventions and can
disrupt the upload UI because UploadTabs (a Client Component) imports DropZone.
Agent Prompt
## Issue description
`features/upload/components/DropZone.tsx` uses client-only React hooks (`useCallback`, `useDropzone`) but is missing the required `"use client";` directive at the top of the file, so Next.js treats it as a Server Component by default and will error at build/runtime.

## Issue Context
This PR removed `"use client"` from `DropZone.tsx` while keeping hook usage, and `UploadTabs.tsx` is a Client Component that imports `DropZone`, making the issue surface as a Next.js App Router Server/Client component boundary violation.

## Fix Focus Areas
- features/upload/components/DropZone.tsx[1-10]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread app/api/upload/route.ts
Comment on lines +76 to +97
// Overwrite behavior: Delete existing document chunks with the same name
// to avoid duplicate search results.
const { error: deleteError } = await supabase
.from("documents")
.delete()
.eq("name", name);

if (deleteError) {
console.warn("Could not delete old chunks (non-blocking):", deleteError.message, deleteError.details);
}

// Insert new chunks
const records = chunks.map((chunk, index) => ({
name,
content: chunk,
embedding: embeddings[index],
}));

const { error: insertError } = await supabase
.from("documents")
.insert(records);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Unauthenticated service-role api 🐞 Bug ⛨ Security

The new /api/upload and /api/chat routes perform privileged Supabase operations using a client
initialized with SUPABASE_SERVICE_ROLE_KEY, but the routes have no authentication/authorization
checks. Any caller who can reach these endpoints can insert/delete document chunks and query
document contents via vector search, and can also burn Cohere/Groq API quota.
Agent Prompt
### Issue description
`/api/upload` and `/api/chat` are publicly callable but use a Supabase client created with `SUPABASE_SERVICE_ROLE_KEY`, enabling unauthorized document mutation/query and paid API usage.

### Issue Context
- Client-side hooks call `/api/upload` and `/api/chat` directly.
- Service-role bypasses RLS; without per-user scoping this becomes admin access from the internet.

### Fix Focus Areas
- app/api/upload/route.ts[9-18]
- app/api/upload/route.ts[76-97]
- app/api/chat/route.ts[8-40]
- lib/supabase.ts[3-15]
- lib/n8n.ts[4-6]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@ivancidev
ivancidev merged commit 7165680 into main Jun 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant