Skip to content

Commit 46eb43f

Browse files
brrockcgoinglove
andauthored
feat(file-storage): image uploads, generate profile with ai (#257)
@cgoinglove want to work together? --------- Co-authored-by: choi sung keun <[email protected]> Co-authored-by: cgoing <[email protected]>
1 parent c120623 commit 46eb43f

39 files changed

+5161
-1125
lines changed

.env.example

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,19 @@ NOT_ALLOW_ADD_MCP_SERVERS=
9191
# Maximum timeout for MCP tool calls in milliseconds (default: no timeout)
9292
# Useful for long-running MCP tools. Example: 600000 (10 minutes)
9393
MCP_MAX_TOTAL_TIMEOUT=
94+
95+
96+
# === File Storage ===
97+
98+
# -- Vercel Blob example --
99+
# Pull the token locally with `vercel env pull` when testing against Vercel Blob.
100+
# FILE_STORAGE_TYPE=vercel-blob
101+
# FILE_STORAGE_PREFIX=uploads
102+
# BLOB_READ_WRITE_TOKEN=
103+
104+
105+
# -- S3 (planned driver) --
106+
# FILE_STORAGE_TYPE=s3
107+
# FILE_STORAGE_PREFIX=uploads
108+
# FILE_STORAGE_S3_BUCKET=
109+
# FILE_STORAGE_S3_REGION=

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,4 @@ local-data
5959
.mcp-config.json
6060
openai-compatible.config.ts
6161
certificates
62+
public/uploads

README.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![Local First](https://img.shields.io/badge/Local-First-blue)](https://localfirstweb.dev/)
55
[![Discord](https://img.shields.io/discord/1374047276074537103?label=Discord&logo=discord&color=5865F2)](https://discord.gg/gCRu69Upnp)
66

7-
[![Deploy with Vercel](https://vercel.com/button)](<https://vercel.com/new/clone?repository-url=https://github.com/cgoinglove/better-chatbot&env=BETTER_AUTH_SECRET&env=OPENAI_API_KEY&env=GOOGLE_GENERATIVE_AI_API_KEY&env=ANTHROPIC_API_KEY&envDescription=BETTER_AUTH_SECRET+is+required+(enter+any+secret+value).+At+least+one+LLM+provider+API+key+(OpenAI,+Claude,+or+Google)+is+required,+but+you+can+add+all+of+them.+See+the+link+below+for+details.&envLink=https://github.com/cgoinglove/better-chatbot/blob/main/.env.example&demo-title=better-chatbot&demo-description=An+Open-Source+Chatbot+Template+Built+With+Next.js+and+the+AI+SDK+by+Vercel.&products=[{"type":"integration","protocol":"storage","productSlug":"neon","integrationSlug":"neon"},{"type":"integration","protocol":"storage","productSlug":"upstash-kv","integrationSlug":"upstash"}]>)
7+
[![Deploy with Vercel](https://vercel.com/button)](<https://vercel.com/new/clone?repository-url=https://github.com/cgoinglove/better-chatbot&env=BETTER_AUTH_SECRET&env=OPENAI_API_KEY&env=GOOGLE_GENERATIVE_AI_API_KEY&env=ANTHROPIC_API_KEY&envDescription=BETTER_AUTH_SECRET+is+required+(enter+any+secret+value).+At+least+one+LLM+provider+API+key+(OpenAI,+Claude,+or+Google)+is+required,+but+you+can+add+all+of+them.+See+the+link+below+for+details.&envLink=https://github.com/cgoinglove/better-chatbot/blob/main/.env.example&demo-title=better-chatbot&demo-description=An+Open-Source+Chatbot+Template+Built+With+Next.js+and+the+AI+SDK+by+Vercel.&products=[{"type":"integration","protocol":"storage","productSlug":"neon","integrationSlug":"neon"},{"type":"integration","protocol":"storage","productSlug":"upstash-kv","integrationSlug":"upstash"},{"type":"blob"}]>)
88

99
🚀 **[Live Demo](https://better-chatbot-demo.vercel.app/)** | See the experience in action in the [preview](#preview) below!
1010

@@ -83,6 +83,7 @@ Open [http://localhost:3000](http://localhost:3000) in your browser to get start
8383
- [🔌 MCP Server Setup \& Tool Testing](#-mcp-server-setup--tool-testing)
8484
- [🐳 Docker Hosting Guide](#-docker-hosting-guide)
8585
- [▲ Vercel Hosting Guide](#-vercel-hosting-guide)
86+
- [🗂️ File Storage Drivers](#️-file-storage-drivers)
8687
- [🎯 System Prompts \& Chat Customization](#-system-prompts--chat-customization)
8788
- [🔐 OAuth Sign-In Setup](#-oauth-sign-in-setup)
8889
- [🕵🏿 Adding openAI like providers](#-adding-openai-like-providers)
@@ -330,6 +331,19 @@ EXA_API_KEY=your_exa_api_key_here
330331
# Whether to use file-based MCP config (default: false)
331332
FILE_BASED_MCP_CONFIG=false
332333
334+
# === File Storage ===
335+
# Vercel Blob is the default storage driver (works in both local dev and production)
336+
# Pull the token locally with `vercel env pull`
337+
FILE_STORAGE_TYPE=vercel-blob
338+
FILE_STORAGE_PREFIX=uploads
339+
BLOB_READ_WRITE_TOKEN=
340+
341+
# -- S3 (coming soon) --
342+
# FILE_STORAGE_TYPE=s3
343+
# FILE_STORAGE_PREFIX=uploads
344+
# FILE_STORAGE_S3_BUCKET=
345+
# FILE_STORAGE_S3_REGION=
346+
333347
# (Optional)
334348
# === OAuth Settings ===
335349
# Fill in these values only if you want to enable Google/GitHub/Microsoft login
@@ -378,6 +392,10 @@ Step-by-step setup guides for running and configuring better-chatbot.
378392

379393
- Deploy the chatbot to Vercel with simple setup steps for production use.
380394

395+
#### [🗂️ File Storage Drivers](./docs/tips-guides/file-storage.md)
396+
397+
- Cloud-based file storage with Vercel Blob (default) for seamless uploads in both development and production. S3 support coming soon.
398+
381399
#### [🎯 System Prompts & Chat Customization](./docs/tips-guides/system-prompts-and-customization.md)
382400

383401
- Personalize your chatbot experience with custom system prompts, user preferences, and MCP tool instructions
@@ -405,7 +423,8 @@ Step-by-step setup guides for running and configuring better-chatbot.
405423

406424
Planned features coming soon to better-chatbot:
407425

408-
- [ ] **File Attach & Image Generation**
426+
- [x] **File Upload & Storage** (Vercel Blob integration)
427+
- [ ] **Image Generation**
409428
- [ ] **Collaborative Document Editing** (like OpenAI Canvas: user & assistant co-editing)
410429
- [ ] **RAG (Retrieval-Augmented Generation)**
411430
- [ ] **Web-based Compute** (with [WebContainers](https://webcontainers.io) integration)

docs/tips-guides/file-storage.md

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# File Storage Setup
2+
3+
> **Note**: This documentation was written by Claude 3.5 Sonnet.
4+
5+
This project supports **cloud-based file storage** for handling file uploads and downloads.
6+
7+
## Overview
8+
9+
Files are stored with **public access** by default, making them accessible via URL. This is useful for sharing uploaded content, displaying images, and integrating with external services.
10+
11+
## Storage Drivers
12+
13+
The project supports two storage backends:
14+
15+
- **Vercel Blob** - Default for all deployments (recommended)
16+
- **S3** - Planned for AWS/S3-compatible storage
17+
18+
**Vercel Blob** is the default storage driver and works seamlessly in both local development and production environments.
19+
20+
## Configuration
21+
22+
### Environment Variables
23+
24+
```ini
25+
# Storage driver selection (defaults to vercel-blob)
26+
FILE_STORAGE_TYPE=vercel-blob # or s3 (coming soon)
27+
28+
# Optional: Subdirectory prefix for organizing files
29+
FILE_STORAGE_PREFIX=uploads
30+
31+
# === Vercel Blob (FILE_STORAGE_TYPE=vercel-blob) ===
32+
BLOB_READ_WRITE_TOKEN=<auto on Vercel>
33+
VERCEL_BLOB_CALLBACK_URL= # Optional: For local webhook testing with ngrok
34+
35+
# === S3 (FILE_STORAGE_TYPE=s3, not yet implemented) ===
36+
# FILE_STORAGE_S3_BUCKET=
37+
# FILE_STORAGE_S3_REGION=
38+
# AWS_ACCESS_KEY_ID=
39+
# AWS_SECRET_ACCESS_KEY=
40+
```
41+
42+
### Quick Start with Vercel Blob
43+
44+
Vercel Blob works in both local development and production environments:
45+
46+
1. Go to your Vercel project → **Storage** tab
47+
2. Click **Connect Database****Blob****Continue**
48+
3. Name it (e.g., "Files") and click **Create**
49+
4. Pull environment variables locally:
50+
51+
```bash
52+
vercel env pull
53+
```
54+
55+
That's it! File uploads will now work seamlessly in both development and production.
56+
57+
## Client Upload
58+
59+
The `useFileUpload` hook **automatically selects the optimal upload method** based on your storage backend:
60+
61+
- **Vercel Blob**: Direct browser → CDN upload (fastest, default)
62+
- **S3**: Presigned URL upload (when implemented)
63+
64+
```tsx
65+
"use client";
66+
67+
import { useFileUpload } from "hooks/use-presigned-upload";
68+
69+
function FileUploadComponent() {
70+
const { upload, isUploading } = useFileUpload();
71+
72+
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
73+
const file = e.target.files?.[0];
74+
if (!file) return;
75+
76+
const result = await upload(file);
77+
if (!result) return; // Upload failed (error shown via toast)
78+
79+
// File uploaded successfully
80+
console.log("Public URL:", result.url);
81+
console.log("Pathname (key):", result.pathname);
82+
};
83+
84+
return (
85+
<input type="file" onChange={handleFileChange} disabled={isUploading} />
86+
);
87+
}
88+
```
89+
90+
### Upload Flow
91+
92+
#### Vercel Blob (Direct Upload)
93+
94+
```mermaid
95+
sequenceDiagram
96+
participant Browser
97+
participant UploadURL as /api/storage/upload-url
98+
participant Vercel as Vercel Blob CDN
99+
100+
Browser->>UploadURL: POST (request client token)
101+
Note over Browser,UploadURL: User authenticated
102+
UploadURL->>Vercel: Generate client token
103+
Vercel-->>UploadURL: Return token
104+
UploadURL-->>Browser: Return token + URL
105+
Browser->>Vercel: PUT file (with token)
106+
Vercel-->>Browser: Upload complete
107+
Vercel->>UploadURL: Webhook: upload completed
108+
Note over UploadURL: Optional: Save to DB
109+
```
110+
111+
### Features
112+
113+
-**Cloud-Based Storage**: Vercel Blob provides globally distributed CDN
114+
-**Works Everywhere**: Same storage in development and production
115+
-**Direct Client Upload**: Browser uploads directly to CDN (fastest)
116+
-**Public Access**: All files get public URLs
117+
-**Authentication**: Users must be logged in to upload
118+
-**Collision Prevention**: UUID-based file naming
119+
-**Type Safety**: Full TypeScript support with unified interface
120+
121+
## Server-Side Upload
122+
123+
For server-side uploads (e.g., programmatically generated files):
124+
125+
```ts
126+
import { serverFileStorage } from "lib/file-storage";
127+
128+
const result = await serverFileStorage.upload(buffer, {
129+
filename: "generated-image.png",
130+
contentType: "image/png",
131+
});
132+
133+
console.log("Public URL:", result.sourceUrl);
134+
```
135+
136+
## Upload Completion Webhook
137+
138+
The `/api/storage/upload-url` endpoint handles the `onUploadCompleted` webhook from Vercel Blob. You can add custom logic here:
139+
140+
```ts
141+
// src/app/api/storage/upload-url/route.ts
142+
143+
onUploadCompleted: async ({ blob, tokenPayload }) => {
144+
const { userId } = JSON.parse(tokenPayload);
145+
146+
// Save to database
147+
await db.files.create({
148+
url: blob.url,
149+
pathname: blob.pathname,
150+
userId,
151+
size: blob.size,
152+
contentType: blob.contentType,
153+
});
154+
155+
// Send notification
156+
// await sendNotification(userId, "File uploaded!");
157+
};
158+
```
159+
160+
## Advanced
161+
162+
### Local Development with Vercel Blob Webhooks
163+
164+
To test Vercel Blob's `onUploadCompleted` webhook locally, use [ngrok](https://ngrok.com/):
165+
166+
```bash
167+
# Terminal 1: Start your app
168+
pnpm dev
169+
170+
# Terminal 2: Start ngrok
171+
ngrok http 3000
172+
173+
# Add to .env.local
174+
VERCEL_BLOB_CALLBACK_URL=https://abc123.ngrok-free.app
175+
```
176+
177+
Without ngrok, uploads will work but `onUploadCompleted` won't be called locally.
178+
179+
### Custom Storage Backend
180+
181+
To implement a custom storage driver (e.g., Cloudflare R2, MinIO, S3):
182+
183+
1. Create a new file in `src/lib/file-storage/` (e.g., `r2-file-storage.ts`)
184+
2. Implement the `FileStorage` interface from `file-storage.interface.ts`
185+
3. Add your driver to `index.ts`
186+
4. Update `FILE_STORAGE_TYPE` environment variable
187+
188+
The `FileStorage` interface provides:
189+
190+
- `upload()` - Server-side file upload
191+
- `createUploadUrl()` - Generate presigned URL for client uploads (optional)
192+
- `download()`, `delete()`, `exists()`, `getMetadata()`, `getSourceUrl()`
193+
194+
### Storage Comparison
195+
196+
| Feature | Vercel Blob | S3 (Planned) |
197+
| -------------------- | ------------------- | ------------------ |
198+
| Direct Client Upload | ✅ Yes | ✅ Yes (presigned) |
199+
| CDN | ✅ Global | Configurable |
200+
| Cost | Pay-as-you-go | Pay-as-you-go |
201+
| Best For | All deployments | AWS ecosystem |
202+
| Setup Complexity | Minimal | Moderate |
203+
| Local Development | ✅ Works with token | ✅ Works |
204+
205+
## Why Not Local Filesystem?
206+
207+
Local filesystem storage is **not supported** because:
208+
209+
1. **AI APIs can't access localhost**: When AI APIs receive `http://localhost:3000/file.png`, they cannot fetch the file
210+
2. **Serverless incompatibility**: Platforms like Vercel don't support persistent filesystem
211+
3. **No CDN**: Files aren't globally distributed
212+
213+
**Solution**: Vercel Blob provides a free tier and works seamlessly in both local development and production. Simply run `vercel env pull` to get your token locally.

docs/tips-guides/vercel.md

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,33 @@
11
# Vercel Deployment Guide
22

33
1. **Click this button** to start the deployment process:
4-
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/cgoinglove/better-chatbot&env=BETTER_AUTH_SECRET&env=OPENAI_API_KEY&env=GOOGLE_GENERATIVE_AI_API_KEY&env=ANTHROPIC_API_KEY&envDescription=BETTER_AUTH_SECRET+is+required+(enter+any+secret+value).+At+least+one+LLM+provider+API+key+(OpenAI,+Claude,+or+Google)+is+required,+but+you+can+add+all+of+them.+See+the+link+below+for+details.&envLink=https://github.com/cgoinglove/better-chatbot/blob/main/.env.example&demo-title=better-chatbot&demo-description=An+Open-Source+Chatbot+Template+Built+With+Next.js+and+the+AI+SDK+by+Vercel.&products=[{"type":"integration","protocol":"storage","productSlug":"neon","integrationSlug":"neon"},{"type":"integration","protocol":"storage","productSlug":"upstash-kv","integrationSlug":"upstash"}])
4+
[![Deploy with Vercel](https://vercel.com/button)](<https://vercel.com/new/clone?repository-url=https://github.com/cgoinglove/better-chatbot&env=BETTER_AUTH_SECRET&env=OPENAI_API_KEY&env=GOOGLE_GENERATIVE_AI_API_KEY&env=ANTHROPIC_API_KEY&envDescription=BETTER_AUTH_SECRET+is+required+(enter+any+secret+value).+At+least+one+LLM+provider+API+key+(OpenAI,+Claude,+or+Google)+is+required,+but+you+can+add+all+of+them.+See+the+link+below+for+details.&envLink=https://github.com/cgoinglove/better-chatbot/blob/main/.env.example&demo-title=better-chatbot&demo-description=An+Open-Source+Chatbot+Template+Built+With+Next.js+and+the+AI+SDK+by+Vercel.&products=[{"type":"integration","protocol":"storage","productSlug":"neon","integrationSlug":"neon"},{"type":"integration","protocol":"storage","productSlug":"upstash-kv","integrationSlug":"upstash"},{"type":"blob"}]>)
55

66
2. **Click the "Create" button** on Vercel to begin setting up your project.
77

88
<img width="1254" alt="step2" src="https://github.com/user-attachments/assets/66806fa8-2d55-4e57-ad7e-2f37ef037a97" />
99

10-
1110
3. **Add Neon Postgres and create a database.**
1211
Click the "Neon Postgres Add" button to create a database. This is available even on the free plan.
1312

1413
<img width="1329" alt="step3" src="https://github.com/user-attachments/assets/d0f3848e-e45b-4c20-843e-7c452c297e51" />
1514

16-
1715
4. **Add Environment Variables.**
1816
You can enter placeholder values for the environment variables at this stage, or use the actual values if you have them ready. If you prefer, you can enter temporary values now and update them later in the project's settings after deployment is complete. However, **BETTER_AUTH_SECRET** must be set at this stage.
1917

2018
- You can generate an BETTER_AUTH_SECRET [here](https://auth-secret-gen.vercel.app/).
2119

2220
<img width="1469" alt="step4" src="https://github.com/user-attachments/assets/a0ca9aab-e32a-42ff-8714-707cda387c2a" />
2321

24-
2522
5. **Deployment and Project Creation.**
2623
Once the above steps are complete, deployment will begin automatically and your project will be created.
2724

2825
6. **Enter LLM Provider API Keys in Project Settings.**
2926
After deployment, go to your project's **Settings > Environments** tab. Here, enter the API keys for the LLM providers you wish to use. You only need to enter the keys for the providers you actually plan to use—other fields can be left blank or filled with dummy values.
3027

3128
- Example environment file: [example.env](https://github.com/cgoinglove/better-chatbot/blob/main/.env.example)
32-
33-
<img width="1712" alt="step6" src="https://github.com/user-attachments/assets/2d197389-a865-46ac-9156-40cad64258ca" />
3429

30+
<img width="1712" alt="step6" src="https://github.com/user-attachments/assets/2d197389-a865-46ac-9156-40cad64258ca" />
3531

3632
## Notes
3733

messages/en.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,11 @@
194194
"Chat": {
195195
"Error": "Chat Error",
196196
"thisMessageWasNotSavedPleaseTryTheChatAgain": "This message was not saved. Please try the chat again.",
197+
"uploadImage": "Upload Image",
198+
"imageUploadedSuccessfully": "Image uploaded successfully",
199+
"pleaseUploadImageFile": "Please upload an image file",
200+
"imageSizeMustBeLessThan10MB": "Image size must be less than 10MB",
201+
"failedToUploadImage": "Failed to upload image",
197202
"Greeting": {
198203
"goodMorning": "Good morning, {name}",
199204
"goodAfternoon": "Good afternoon, {name}",
@@ -623,6 +628,32 @@
623628
"tokensAcross": "{tokens} tokens across {count} model{count, plural, =1 {} other {s}} in {period}.",
624629
"mostActive": "Most active: {model} ({tokens} tokens).",
625630
"summaryPrefix": "📊 Summary: ",
631+
"uploadPhoto": "Upload Photo",
632+
"chooseDefault": "Choose Default",
633+
"useEmoji": "Use Emoji",
634+
"generateWithAI": "Generate with AI",
635+
"changeProfilePhoto": "Change Profile Photo",
636+
"selectDefaultAvatar": "Choose Default Avatar",
637+
"selectDefaultAvatarDescription": "Select one of the default avatars below",
638+
"chooseEmojiAvatar": "Choose Emoji Avatar",
639+
"chooseEmojiAvatarDescription": "Select an emoji to use as your profile photo",
640+
"generateAvatarWithAI": "Generate Avatar with AI",
641+
"generateAvatarWithAIDescription": "Describe your ideal profile picture and let AI create it",
642+
"aiProvider": "AI Provider",
643+
"describeYourAvatar": "Describe your avatar",
644+
"avatarPromptPlaceholder": "e.g., A cute puppy in Studio Ghibli style",
645+
"generating": "Generating...",
646+
"regenerate": "Regenerate",
647+
"useThisAvatar": "Use This Avatar",
648+
"profilePhotoUpdatedSuccessfully": "Profile photo updated successfully",
649+
"failedToUpdateProfilePhoto": "Failed to update profile photo",
650+
"pleaseEnterPrompt": "Please enter a prompt",
651+
"imageGeneratedSuccessfully": "Image generated successfully!",
652+
"failedToGenerateImage": "Failed to generate image",
653+
"failedToSaveImage": "Failed to save image",
654+
"pleaseUploadValidImage": "Please upload a valid image (JPEG, PNG, or WebP)",
655+
"imageSizeMustBeLessThan": "Image size must be less than 5MB",
656+
"select": "Select",
626657
"msgs": "msgs",
627658
"unknown": "Unknown",
628659
"passwordUpdatedSuccessfully": "Password updated successfully",

0 commit comments

Comments
 (0)