Implemented the database schema, utility functions, and API endpoints for the PDF conversation history management feature. This enables users to save and retrieve their PDF files and conversation records.
File: supabase/migrations/0006_create_conversation_tables.sql
Created three main database components:
-
Extended user_pdfs table with new columns:
page_count- Number of pages in the PDFtext_summary- Text summary of the PDFparse_status- Status of PDF parsing (pending, parsing, completed, failed)parsed_at- Timestamp when parsing completed
-
pdf_conversations table - Tracks conversations per PDF:
id- Unique conversation IDpdf_id- Reference to PDFuser_id- Reference to usermessage_count- Denormalized count for performancelast_message_at- Timestamp of most recent message- Unique constraint on (pdf_id, user_id)
-
conversation_messages table - Stores individual messages:
id- Unique message IDconversation_id- Reference to conversationpdf_id- Reference to PDFuser_id- Reference to userrole- Either 'user' or 'assistant'content- Message contenttokens- Token count for billingprocessing_time- Processing time in milliseconds
Indexes Created:
idx_pdf_conversations_user_id- For user queriesidx_pdf_conversations_pdf_id- For PDF queriesidx_conversation_messages_conversation_id- For conversation queriesidx_conversation_messages_pdf_id- For PDF queriesidx_conversation_messages_user_id- For user queriesidx_conversation_messages_created_at- For time-based queries
RLS Policies:
- Users can only view/insert their own conversations and messages
- Automatic cascade deletion when PDF is deleted
Helper Functions:
update_conversation_stats()- Trigger to update message count and last message time
savePDFInfo()- Save or update PDF metadatacreateOrGetConversation()- Create or retrieve conversation recordupdatePDFParseStatus()- Update PDF parsing status
saveConversationMessage()- Save individual messagesaveConversationExchange()- Save user question + assistant responsegetConversationStats()- Get conversation statisticsgetConversationTokenCount()- Calculate total tokens useddeleteConversationMessages()- Delete all messages in conversation
getPDFList()- Get user's PDFs with conversation stats, sorting, paginationgetPDFWithStats()- Get single PDF with statsuserOwnsPDF()- Verify user ownershipgetUserPDFCount()- Get total PDF count
getConversationHistory()- Get paginated conversation messagesgetRecentMessages()- Get last N messagesgetConversationMessageCount()- Get message countsearchConversationMessages()- Search messages by contentgetConversationStats()- Get detailed conversation statistics
deletePDF()- Delete PDF and cascade delete conversations/messagesdeleteAllUserPDFs()- Delete all PDFs for a usersoftDeletePDF()- Mark PDF as deleted without removing data
Purpose: Get user's PDF list with conversation statistics
Query Parameters:
limit(default: 50, max: 100) - Number of PDFs per pageoffset(default: 0) - Pagination offsetsortBy(default: 'uploadedAt') - Sort field: uploadedAt, conversationCount, lastConversationAtsortOrder(default: 'desc') - Sort direction: asc, desc
Response:
{
"success": true,
"data": {
"total": 10,
"pdfs": [
{
"id": "uuid",
"filename": "document.pdf",
"fileSize": 1024000,
"pageCount": 25,
"parseStatus": "completed",
"uploadedAt": "2026-03-10T08:00:00Z",
"conversationCount": 5,
"lastConversationAt": "2026-03-10T09:30:00Z"
}
],
"pagination": {
"limit": 50,
"offset": 0,
"hasMore": false
}
},
"timestamp": "2026-03-10T10:00:00Z"
}Purpose: Get conversation history for a specific PDF
Query Parameters:
limit(default: 100, max: 100) - Messages per pageoffset(default: 0) - Pagination offset
Response:
{
"success": true,
"data": {
"pdfId": "uuid",
"filename": "document.pdf",
"pageCount": 25,
"messages": [
{
"id": "uuid",
"role": "user",
"content": "What is this document about?",
"createdAt": "2026-03-10T09:00:00Z"
},
{
"id": "uuid",
"role": "assistant",
"content": "This document is about...",
"createdAt": "2026-03-10T09:01:00Z",
"tokens": 150,
"processingTime": 2500
}
],
"pagination": {
"limit": 100,
"offset": 0,
"total": 10,
"hasMore": false
}
},
"timestamp": "2026-03-10T10:00:00Z"
}Purpose: Delete a PDF and all its associated data
Response:
{
"success": true,
"data": {
"pdfId": "uuid",
"messagesDeleted": 10,
"conversationsDeleted": 1
},
"message": "PDF and all associated data deleted successfully",
"timestamp": "2026-03-10T10:00:00Z"
}Error Responses:
- 401 Unauthorized - User not authenticated
- 403 Forbidden - User doesn't own the PDF
- 404 Not Found - PDF not found
- 400 Bad Request - Invalid parameters
- 500 Server Error - Internal error
File: src/app/api/upload/route.ts
Updated to:
- Call
savePDFInfo()to save PDF metadata - Call
createOrGetConversation()to create conversation record - Handle errors gracefully without blocking upload
File: src/app/api/chat/route.ts
Updated to:
- Call
recordQuotaUsage()for AI calls - Prepare for conversation message saving (requires full response collection)
Note: Full conversation saving requires collecting the complete streamed response before saving. This will be enhanced in the next phase.
src/
├── lib/
│ ├── pdf/
│ │ ├── save-pdf-info.ts (NEW)
│ │ ├── get-pdf-list.ts (NEW)
│ │ └── delete-pdf.ts (NEW)
│ └── chat/
│ ├── save-conversation.ts (NEW)
│ └── get-conversation-history.ts (NEW)
└── app/
└── api/
└── pdfs/
├── list/
│ └── route.ts (NEW)
├── [id]/
│ ├── route.ts (NEW - DELETE)
│ └── conversations/
│ └── route.ts (NEW - GET)
└── (existing endpoints)
supabase/
└── migrations/
└── 0006_create_conversation_tables.sql (NEW)
ALTER TABLE user_pdfs ADD COLUMN page_count INTEGER;
ALTER TABLE user_pdfs ADD COLUMN text_summary TEXT;
ALTER TABLE user_pdfs ADD COLUMN parse_status TEXT DEFAULT 'pending';
ALTER TABLE user_pdfs ADD COLUMN parsed_at TIMESTAMP;CREATE TABLE pdf_conversations (
id UUID PRIMARY KEY,
pdf_id UUID REFERENCES user_pdfs(id) ON DELETE CASCADE,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
message_count INTEGER DEFAULT 0,
last_message_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(pdf_id, user_id)
);CREATE TABLE conversation_messages (
id UUID PRIMARY KEY,
conversation_id UUID REFERENCES pdf_conversations(id) ON DELETE CASCADE,
pdf_id UUID REFERENCES user_pdfs(id) ON DELETE CASCADE,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
role TEXT CHECK (role IN ('user', 'assistant')),
content TEXT NOT NULL,
tokens INTEGER,
processing_time INTEGER,
created_at TIMESTAMP DEFAULT NOW()
);- Collect full streamed response before saving
- Save conversation messages after each chat completion
- Update conversation stats automatically
- Create PDF list component
- Create conversation history viewer
- Add delete confirmation dialog
- Integrate with main page
- Unit tests for all utility functions
- Integration tests for API endpoints
- E2E tests for complete workflows
✅ RLS Policies: All tables have row-level security enabled ✅ User Verification: All endpoints verify user ownership ✅ Parameter Validation: All inputs are validated ✅ Error Handling: Proper error responses with appropriate HTTP status codes ✅ Cascade Deletion: Conversations and messages are automatically deleted with PDF
✅ Indexes: Created on frequently queried columns ✅ Denormalization: Message count stored in pdf_conversations for fast queries ✅ Pagination: All list endpoints support pagination ✅ Sorting: Flexible sorting options for better UX
All endpoints include comprehensive error handling:
- 401 Unauthorized - User not authenticated
- 403 Forbidden - Access denied
- 404 Not Found - Resource not found
- 400 Bad Request - Invalid parameters
- 500 Server Error - Internal errors
All functions include detailed console logging for debugging:
- Function entry/exit
- Parameter values
- Query results
- Error details
-
Unit Tests:
- Test each utility function with valid/invalid inputs
- Mock Supabase client
- Test error scenarios
-
Integration Tests:
- Test complete workflows (upload → chat → retrieve)
- Test permission checks
- Test cascade deletion
-
E2E Tests:
- Test from user perspective
- Test multiple users
- Test concurrent operations
- Run migration:
supabase migration up - Deploy updated API routes
- Deploy updated upload/chat APIs
- Test all endpoints
- Deploy frontend components (in next phase)
Monitor these metrics:
- API response times
- Database query performance
- Error rates
- User adoption
- Conversation Search: Full-text search across all conversations
- Conversation Export: Export conversations as PDF/Markdown
- Conversation Sharing: Share conversation links
- Conversation Tags: Tag conversations for organization
- Conversation Analytics: Track usage patterns
- Batch Operations: Delete multiple PDFs at once
- Archiving: Archive old conversations instead of deleting