Skip to content

Commit 7a71ae5

Browse files
committed
Add RAG UI + fixed file storage + embedding pipelin + stream updates
1 parent e30e191 commit 7a71ae5

32 files changed

+3013
-304
lines changed

app/(app)/api/files/route.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { NextResponse } from 'next/server';
2-
import { getManagedFilesByUserId } from '@/lib/db/queries';
2+
import { getManagedFilesByUserId, deleteManagedFile } from '@/lib/db/queries';
33
import { auth } from '@/app/(auth)/auth';
44

55
export async function GET() {
@@ -22,3 +22,14 @@ export async function GET() {
2222
);
2323
}
2424
}
25+
26+
export async function DELETE(request: Request) {
27+
const session = await auth();
28+
if (!session || !session.user?.id) {
29+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
30+
}
31+
32+
const { id } = await request.json();
33+
const deleted = await deleteManagedFile(id);
34+
return NextResponse.json(deleted);
35+
}

app/(app)/api/files/upload/route.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { NextResponse } from 'next/server';
33
import { z } from 'zod';
44

55
import { auth } from '@/app/(auth)/auth';
6-
import { createManagedFile, type NewManagedFile } from '@/lib/db/queries';
6+
import {
7+
createManagedFile,
8+
type NewManagedFile,
9+
createTagIfNotExists,
10+
} from '@/lib/db/queries';
711
import { inngest } from '@/src/inngest/client';
812

913
// Use Blob instead of File since File is not available in Node.js environment
@@ -88,15 +92,29 @@ export async function POST(request: Request) {
8892
}
8993

9094
// Prepare data for createManagedFile, excluding auto-generated fields
95+
const tagsParam = formData.get('tags');
96+
let tags: string[] = [];
97+
if (tagsParam) {
98+
const parsedTags = JSON.parse(tagsParam as string);
99+
if (Array.isArray(parsedTags)) {
100+
tags = parsedTags.filter((tag) => typeof tag === 'string').slice(0, 10);
101+
102+
// Save each tag to the tags table
103+
for (const tag of tags) {
104+
await createTagIfNotExists(tag);
105+
}
106+
}
107+
}
108+
91109
const newFileData: NewManagedFile = {
92110
name: originalFilename,
93111
blobUrl: blobData.url,
94112
blobDownloadUrl: blobData.url,
95113
mimeType: file.type,
96114
size: file.size,
115+
aiSummary: null, // Initialize aiSummary as null
116+
tags: tags, // Use the processed tags array
97117
userId: session.user.id,
98-
tags: [], // Default tags
99-
aiSummary: null, // Explicitly set aiSummary to null
100118
};
101119

102120
// 2. Create initial record in our database
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { getSubscriptionToken } from '@inngest/realtime';
2+
import { auth } from '@/app/(auth)/auth';
3+
import { NextResponse } from 'next/server';
4+
import { inngest } from '@/src/inngest/client';
5+
6+
// ex. /api/get-subscribe-token
7+
export async function POST() {
8+
const session = await auth();
9+
10+
if (!session || !session.user?.id) {
11+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
12+
}
13+
14+
const token = await getSubscriptionToken(inngest, {
15+
channel: `user:${session.user.id}`,
16+
topics: ['embed-file-status'],
17+
});
18+
19+
return NextResponse.json({ token }, { status: 200 });
20+
}

app/(app)/api/tags/route.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { auth } from '@/app/(auth)/auth';
2+
import { getAllTags } from '@/lib/db/queries';
3+
4+
export async function GET(request: Request) {
5+
const session = await auth();
6+
7+
if (!session) {
8+
return new Response('Unauthorized', { status: 401 });
9+
}
10+
11+
const tags = await getAllTags();
12+
return Response.json(tags, { status: 200 });
13+
}

app/(app)/files/page.tsx

Lines changed: 15 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,177 +1,20 @@
1-
'use client';
1+
import { auth } from '../../(auth)/auth';
2+
import { redirect } from 'next/navigation';
3+
import FileManager from '@/components/file-manager/file-manager';
4+
import { inngest } from '@/src/inngest/client';
5+
import { getSubscriptionToken } from '@inngest/realtime';
26

3-
import React, { useMemo, useState, useCallback } from 'react';
4-
import { useRouter } from 'next/navigation';
5-
import useSWR from 'swr';
6-
import { Button } from '@/components/ui/button';
7-
import {
8-
PlusIcon,
9-
FileTextIcon,
10-
FilterIcon,
11-
ChevronLeftIcon,
12-
ChevronRightIcon,
13-
Loader2Icon,
14-
} from 'lucide-react';
15-
import { TAXONOMY } from '@/lib/business/taxonomy';
16-
import type { ManagedFile } from '@/lib/db/schema';
17-
import { FileUploadModal } from '@/components/file-manager/file-upload-modal';
18-
import { fetcher } from '@/lib/utils';
19-
import FileListItem from '@/components/file-manager/file-list-item';
7+
export default async function Page() {
8+
const session = await auth();
209

21-
export default function FilesPage() {
22-
const router = useRouter();
23-
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
24-
const [currentPage, setCurrentPage] = useState(1);
25-
const itemsPerPage = 10;
10+
if (!session) {
11+
redirect('/api/auth/guest');
12+
}
2613

27-
// Fetch files using SWR
28-
const {
29-
data: files,
30-
error,
31-
mutate,
32-
} = useSWR<ManagedFile[]>('/api/files', fetcher);
14+
const token = await getSubscriptionToken(inngest, {
15+
channel: `user:${session.user.id}`,
16+
topics: ['embed-file-status'],
17+
});
3318

34-
const isLoading = !files && !error;
35-
36-
const paginatedFiles = useMemo(() => {
37-
if (!files) return [];
38-
const startIndex = (currentPage - 1) * itemsPerPage;
39-
return files.slice(startIndex, startIndex + itemsPerPage);
40-
}, [files, currentPage, itemsPerPage]);
41-
42-
const totalPages = useMemo(() => {
43-
if (!files) return 1;
44-
return Math.ceil(files.length / itemsPerPage);
45-
}, [files, itemsPerPage]);
46-
47-
const handleFileClick = (fileId: string) => {
48-
router.push(`/files/${fileId}`);
49-
};
50-
51-
const handleUploadSuccess = useCallback(
52-
(uploadedFileId: string) => {
53-
console.log('File uploaded successfully:', uploadedFileId);
54-
setIsUploadModalOpen(false);
55-
// Refresh files list
56-
mutate();
57-
},
58-
[mutate],
59-
);
60-
61-
return (
62-
<div className="container mx-auto p-4 md:p-6 lg:p-8 h-full flex flex-col">
63-
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 gap-4">
64-
<h1 className="text-2xl font-semibold">File Manager</h1>
65-
<Button onClick={() => setIsUploadModalOpen(true)}>
66-
<PlusIcon className="mr-2 h-4 w-4" /> Add File
67-
</Button>
68-
</div>
69-
70-
{/* Filters and Sorters Placeholder */}
71-
<div className="mb-4 p-4 border rounded-lg bg-gray-50 dark:bg-gray-800/50">
72-
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 items-end">
73-
<div>
74-
<label
75-
htmlFor="filter-taxonomy"
76-
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
77-
>
78-
Filter by Category
79-
</label>
80-
<select
81-
id="filter-taxonomy"
82-
className="block w-full p-2 border-gray-300 dark:border-gray-600 rounded-md shadow-sm sm:text-sm dark:bg-gray-700 dark:text-white focus:ring-indigo-500 focus:border-indigo-500"
83-
>
84-
<option>All Categories</option>
85-
{TAXONOMY.map((cat) => (
86-
<option key={cat.name} value={cat.name}>
87-
{cat.name}
88-
</option>
89-
))}
90-
</select>
91-
</div>
92-
<div>
93-
<label
94-
htmlFor="sort-by"
95-
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
96-
>
97-
Sort by
98-
</label>
99-
<select
100-
id="sort-by"
101-
className="block w-full p-2 border-gray-300 dark:border-gray-600 rounded-md shadow-sm sm:text-sm dark:bg-gray-700 dark:text-white focus:ring-indigo-500 focus:border-indigo-500"
102-
>
103-
<option>Date Uploaded (Newest)</option>
104-
<option>Date Uploaded (Oldest)</option>
105-
<option>Name (A-Z)</option>
106-
<option>Name (Z-A)</option>
107-
<option>Size (Largest)</option>
108-
<option>Size (Smallest)</option>
109-
</select>
110-
</div>
111-
<Button variant="outline" className="w-full md:w-auto self-end">
112-
<FilterIcon className="mr-2 h-4 w-4" /> Apply Filters
113-
</Button>
114-
</div>
115-
</div>
116-
117-
{isLoading ? (
118-
<div className="text-center text-gray-500 dark:text-gray-400 mt-10 flex-grow flex flex-col justify-center items-center">
119-
<Loader2Icon className="w-12 h-12 mb-4 animate-spin text-gray-400 dark:text-gray-500" />
120-
<p className="text-xl font-semibold">Loading files...</p>
121-
</div>
122-
) : error ? (
123-
<div className="text-center text-red-500 dark:text-red-400 mt-10 flex-grow flex flex-col justify-center items-center">
124-
<p className="text-xl font-semibold">Failed to load files</p>
125-
<p>Please try again later</p>
126-
</div>
127-
) : files && files.length === 0 ? (
128-
<div className="text-center text-gray-500 dark:text-gray-400 mt-10 flex-grow flex flex-col justify-center items-center">
129-
<FileTextIcon className="w-16 h-16 mb-4 text-gray-400 dark:text-gray-500" />
130-
<p className="text-xl font-semibold">No files uploaded yet.</p>
131-
<p>Click "Add File" to get started.</p>
132-
</div>
133-
) : (
134-
<div className="flex-grow overflow-y-auto">
135-
<ul className="bg-white dark:bg-gray-800/30 shadow overflow-hidden sm:rounded-md">
136-
{paginatedFiles.map((file) => (
137-
<FileListItem
138-
key={file.id}
139-
file={file}
140-
onClick={() => handleFileClick(file.id)}
141-
/>
142-
))}
143-
</ul>
144-
</div>
145-
)}
146-
147-
{/* Pagination Controls */}
148-
{files && totalPages > 1 && (
149-
<div className="mt-6 flex items-center justify-between border-t pt-4">
150-
<Button
151-
variant="outline"
152-
onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}
153-
disabled={currentPage === 1}
154-
>
155-
<ChevronLeftIcon className="mr-2 h-4 w-4" /> Previous
156-
</Button>
157-
<span className="text-sm text-gray-700 dark:text-gray-300">
158-
Page {currentPage} of {totalPages}
159-
</span>
160-
<Button
161-
variant="outline"
162-
onClick={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
163-
disabled={currentPage === totalPages}
164-
>
165-
Next <ChevronRightIcon className="ml-2 h-4 w-4" />
166-
</Button>
167-
</div>
168-
)}
169-
170-
<FileUploadModal
171-
isOpen={isUploadModalOpen}
172-
onOpenChange={setIsUploadModalOpen}
173-
onUploadSuccess={handleUploadSuccess}
174-
/>
175-
</div>
176-
);
19+
return <FileManager token={token} />;
17720
}

components/artifact/artifact-messages.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ function PureArtifactMessages({
6161
requiresScrollPadding={
6262
hasSentMessage && index === messages.length - 1
6363
}
64+
onViewSource={() => {
65+
console.log('view source');
66+
}}
6467
/>
6568
))}
6669

0 commit comments

Comments
 (0)