Skip to content

Commit 1cffa8e

Browse files
committed
feat: r2 integration
1 parent aaf4429 commit 1cffa8e

File tree

8 files changed

+1737
-21
lines changed

8 files changed

+1737
-21
lines changed

README.md

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Demo implementations are available in the [`examples/`](./examples/) directory f
1717

1818
- 🗄️ **D1 Database Integration**: Leverage Cloudflare D1 as your primary database via Drizzle ORM.
1919
- 🔌 **KV Storage Integration**: Optionally use Cloudflare KV for secondary storage (e.g., session caching).
20+
- 📁 **R2 File Storage**: Upload, download, and manage user files with Cloudflare R2 object storage and database tracking.
2021
- 📍 **Automatic Geolocation Tracking**: Enrich user sessions with location data derived from Cloudflare.
2122
- 🌐 **Cloudflare IP Detection**: Utilize Cloudflare's IP detection headers out-of-the-box.
2223
- 🔍 **Rich Client-Side Context**: Access timezone, city, country, region, and more via the client plugin.
@@ -27,7 +28,7 @@ Demo implementations are available in the [`examples/`](./examples/) directory f
2728
- [x] Geolocation
2829
- [x] D1
2930
- [x] KV
30-
- [ ] R2
31+
- [x] R2
3132
- [ ] Cloudflare Images
3233
- [ ] Durable Objects
3334

@@ -52,6 +53,7 @@ Demo implementations are available in the [`examples/`](./examples/) directory f
5253
- [7. Initialize the Client](#7-initialize-the-client)
5354
- [Usage Examples](#usage-examples)
5455
- [Accessing Geolocation Data](#accessing-geolocation-data)
56+
- [R2 File Storage Guide](./docs/r2.md)
5557
- [License](#license)
5658
- [Contributing](#contributing)
5759

@@ -69,11 +71,12 @@ bun add better-auth-cloudflare
6971

7072
## Configuration Options
7173

72-
| Option | Type | Default | Description |
73-
| --------------------- | ------- | ------- | ---------------------------------------------- |
74-
| `autoDetectIpAddress` | boolean | `true` | Auto-detect IP address from Cloudflare headers |
75-
| `geolocationTracking` | boolean | `true` | Track geolocation data in the session table |
76-
| `cf` | object | `{}` | Cloudflare geolocation context |
74+
| Option | Type | Default | Description |
75+
| --------------------- | ------- | ----------- | ---------------------------------------------- |
76+
| `autoDetectIpAddress` | boolean | `true` | Auto-detect IP address from Cloudflare headers |
77+
| `geolocationTracking` | boolean | `true` | Track geolocation data in the session table |
78+
| `cf` | object | `{}` | Cloudflare geolocation context |
79+
| `r2` | object | `undefined` | R2 bucket configuration for file storage |
7780

7881
## Setup
7982

@@ -158,6 +161,17 @@ function createAuth(env?: CloudflareBindings, cf?: IncomingRequestCfProperties)
158161
}
159162
: undefined,
160163
kv: env?.KV,
164+
// Optional: Enable R2 file storage
165+
r2: {
166+
bucket: env.R2_BUCKET,
167+
maxFileSize: 10 * 1024 * 1024, // 10MB
168+
allowedTypes: [".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx"],
169+
additionalFields: {
170+
category: { type: "string", required: false },
171+
isPublic: { type: "boolean", required: false },
172+
description: { type: "string", required: false },
173+
},
174+
},
161175
},
162176
{
163177
emailAndPassword: {
@@ -261,8 +275,7 @@ import { createAuthClient } from "better-auth/client";
261275
import { cloudflareClient } from "better-auth-cloudflare/client";
262276

263277
const authClient = createAuthClient({
264-
// baseURL: "/api/auth", // Optional: Uncomment and adjust if your auth API routes are not at /api/auth
265-
plugins: [cloudflareClient()], // Add the Cloudflare client plugin for geolocation features
278+
plugins: [cloudflareClient()], // includes geolocation and R2 file features (if configured)
266279
});
267280

268281
export default authClient;
@@ -305,6 +318,58 @@ const displayLocationInfo = async () => {
305318
displayLocationInfo();
306319
```
307320

321+
### R2 File Storage
322+
323+
If you've configured R2 in your server setup, you can upload and manage files:
324+
325+
```typescript
326+
import authClient from "@/lib/authClient";
327+
328+
// Upload a file with metadata
329+
const uploadFile = async (file: File) => {
330+
const result = await authClient.uploadFile(file, {
331+
category: "documents",
332+
isPublic: false,
333+
description: "Important document",
334+
});
335+
336+
if (result.error) {
337+
console.error("Upload failed:", result.error.message || "Failed to upload file. Please try again.");
338+
} else {
339+
console.log("File uploaded:", result.data);
340+
}
341+
};
342+
343+
// List user's files
344+
const listFiles = async () => {
345+
const result = await authClient.files.list();
346+
if (result.data) {
347+
console.log("User files:", result.data);
348+
}
349+
};
350+
351+
// Download a file
352+
const downloadFile = async (fileId: string, filename: string) => {
353+
const result = await authClient.files.download({ fileId });
354+
if (result.error) {
355+
console.error("Download failed:", result.error);
356+
return;
357+
}
358+
359+
// Extract blob and create download
360+
const response = result.data;
361+
const blob = response instanceof Response ? await response.blob() : response;
362+
const url = window.URL.createObjectURL(blob);
363+
const a = document.createElement("a");
364+
a.href = url;
365+
a.download = filename;
366+
a.click();
367+
window.URL.revokeObjectURL(url);
368+
};
369+
```
370+
371+
For complete R2 file storage documentation, see the [R2 File Storage Guide](./docs/r2.md).
372+
308373
## License
309374

310375
[MIT](./LICENSE)

0 commit comments

Comments
 (0)