@uploadbox/nextjs 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +206 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# @uploadbox/nextjs
|
|
2
|
+
|
|
3
|
+
Next.js route handler for Uploadbox — presigned URL uploads with your own S3 bucket.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@uploadbox/nextjs)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm i @uploadbox/nextjs @uploadbox/core
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**Peer dependency:** Next.js 14, 15, or 16.
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
### 1. Define your file router
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// src/lib/uploadbox.ts
|
|
22
|
+
import { f } from "@uploadbox/core";
|
|
23
|
+
|
|
24
|
+
export const router = {
|
|
25
|
+
imageUploader: f({ image: { maxFileSize: "4MB", maxFileCount: 5 } })
|
|
26
|
+
.middleware(async ({ auth }) => {
|
|
27
|
+
return { userId: auth?.apiKeyId };
|
|
28
|
+
})
|
|
29
|
+
.onUploadComplete(async ({ file, metadata }) => {
|
|
30
|
+
console.log("Upload complete:", file.url);
|
|
31
|
+
return { fileId: file.key };
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type AppRouter = typeof router;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Create the route handler
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
// src/app/api/uploadbox/route.ts
|
|
42
|
+
import { createRouteHandler } from "@uploadbox/nextjs";
|
|
43
|
+
import { router } from "@/lib/uploadbox";
|
|
44
|
+
|
|
45
|
+
export const { GET, POST } = createRouteHandler({ router });
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
S3 credentials are read from environment variables by default (`UPLOADBOX_S3_BUCKET`, `UPLOADBOX_AWS_REGION`, `UPLOADBOX_AWS_ACCESS_KEY_ID`, `UPLOADBOX_AWS_SECRET_ACCESS_KEY`), or pass a `config` object explicitly.
|
|
49
|
+
|
|
50
|
+
### 3. Add the React components
|
|
51
|
+
|
|
52
|
+
See [`@uploadbox/react`](https://www.npmjs.com/package/@uploadbox/react) for the client-side integration.
|
|
53
|
+
|
|
54
|
+
## Lifecycle Hooks
|
|
55
|
+
|
|
56
|
+
Lifecycle hooks let you integrate authentication, quotas, and database tracking without coupling your upload logic to a specific database.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
export const { GET, POST } = createRouteHandler({
|
|
60
|
+
router,
|
|
61
|
+
hooks: {
|
|
62
|
+
onAuthenticate: async (req) => {
|
|
63
|
+
const token = req.headers.get("authorization");
|
|
64
|
+
const user = await verifyToken(token);
|
|
65
|
+
return { apiKeyId: user.id, apiKeyName: user.name };
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
onQuotaCheck: async ({ auth, totalSize, fileCount }) => {
|
|
69
|
+
const usage = await getStorageUsage(auth.apiKeyId);
|
|
70
|
+
if (usage + totalSize > MAX_STORAGE) {
|
|
71
|
+
throw UploadboxError.quotaExceeded();
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
onUploadStarted: async (events) => {
|
|
76
|
+
await db.insert(uploads).values(events.map(toRecord));
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
onUploadCompleted: async (event) => {
|
|
80
|
+
await db.update(uploads)
|
|
81
|
+
.set({ status: "completed" })
|
|
82
|
+
.where(eq(uploads.key, event.file.key));
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
onUploadFailed: async (event) => {
|
|
86
|
+
await db.update(uploads)
|
|
87
|
+
.set({ status: "failed", error: event.error })
|
|
88
|
+
.where(eq(uploads.key, event.fileKey));
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
onFileVerified: async (fileKey) => {
|
|
92
|
+
// Fallback lookup when in-memory state is lost (e.g., multi-instance deploys)
|
|
93
|
+
return db.query.uploads.findFirst({ where: eq(uploads.key, fileKey) });
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Available Hooks
|
|
100
|
+
|
|
101
|
+
| Hook | When | Purpose |
|
|
102
|
+
|------|------|---------|
|
|
103
|
+
| `onAuthenticate` | Every request | Return `AuthContext` or `undefined` for anonymous |
|
|
104
|
+
| `onQuotaCheck` | Before presigning | Throw `UploadboxError.quotaExceeded()` to deny |
|
|
105
|
+
| `onUploadStarted` | After presigned URLs generated | Track pending uploads |
|
|
106
|
+
| `onFileVerified` | On complete, if in-memory miss | Fallback lookup for file data |
|
|
107
|
+
| `onUploadCompleted` | After `onUploadComplete` succeeds | Update DB, trigger webhooks |
|
|
108
|
+
| `onUploadFailed` | On upload failure | Log errors, clean up |
|
|
109
|
+
| `onMultipartStarted` | Multipart upload created | Track large uploads |
|
|
110
|
+
| `onMultipartCompleted` | Multipart upload finished | Update status |
|
|
111
|
+
| `onMultipartAborted` | Multipart upload cancelled | Clean up |
|
|
112
|
+
| `onResolveConfig` | Every request | Per-tenant S3 config (BYOB) |
|
|
113
|
+
|
|
114
|
+
## Rate Limiting
|
|
115
|
+
|
|
116
|
+
Built-in sliding window rate limiter, no external dependencies:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
export const { GET, POST } = createRouteHandler({
|
|
120
|
+
router,
|
|
121
|
+
rateLimit: {
|
|
122
|
+
requestsPerMinute: 60,
|
|
123
|
+
uploadsPerHour: 100,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Limits are applied per API key or client IP.
|
|
129
|
+
|
|
130
|
+
## Processing Pipeline
|
|
131
|
+
|
|
132
|
+
Run post-upload processing hooks (image resize, virus scan, etc.):
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
import { createImageResizeHook } from "@uploadbox/core/hooks/image-resize";
|
|
136
|
+
import { createVirusScanHook } from "@uploadbox/core/hooks/virus-scan";
|
|
137
|
+
|
|
138
|
+
export const { GET, POST } = createRouteHandler({
|
|
139
|
+
router,
|
|
140
|
+
processing: {
|
|
141
|
+
hooks: [
|
|
142
|
+
createImageResizeHook({ maxWidth: 1920, format: "webp" }),
|
|
143
|
+
createVirusScanHook({ clamavUrl: "http://localhost:3310" }),
|
|
144
|
+
],
|
|
145
|
+
mode: "sequential", // or "parallel"
|
|
146
|
+
continueOnError: true,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Or use `runProcessingPipeline` directly:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { runProcessingPipeline } from "@uploadbox/nextjs";
|
|
155
|
+
|
|
156
|
+
const results = await runProcessingPipeline(pipelineConfig, file, metadata, s3Client, config);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Hosted Mode
|
|
160
|
+
|
|
161
|
+
For the Uploadbox hosted platform, use `createHostedHandler` which adds platform-integrated auth, quotas, and event tracking:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { createHostedHandler } from "@uploadbox/nextjs";
|
|
165
|
+
|
|
166
|
+
export const { GET, POST } = createHostedHandler({
|
|
167
|
+
router,
|
|
168
|
+
platform: {
|
|
169
|
+
platformUrl: "https://uploadbox.dev",
|
|
170
|
+
projectId: "proj_abc123",
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Configuration
|
|
176
|
+
|
|
177
|
+
Full `createRouteHandler` options:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
createRouteHandler({
|
|
181
|
+
router, // FileRouter (required)
|
|
182
|
+
config: { // S3 config (or use env vars)
|
|
183
|
+
bucket: "my-bucket",
|
|
184
|
+
region: "us-east-1",
|
|
185
|
+
accessKeyId: "...",
|
|
186
|
+
secretAccessKey: "...",
|
|
187
|
+
endpoint: "...", // MinIO, R2, Wasabi
|
|
188
|
+
forcePathStyle: true,
|
|
189
|
+
cdnBaseUrl: "https://cdn.example.com",
|
|
190
|
+
presignedUrlExpiry: 3600,
|
|
191
|
+
},
|
|
192
|
+
hooks: { ... }, // LifecycleHooks
|
|
193
|
+
rateLimit: { ... }, // RateLimitConfig
|
|
194
|
+
processing: { ... }, // ProcessingPipelineConfig
|
|
195
|
+
s3ClientFactory: (cfg) => createCustomClient(cfg),
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Related Packages
|
|
200
|
+
|
|
201
|
+
- [`@uploadbox/core`](https://www.npmjs.com/package/@uploadbox/core) — Core utilities, builder pattern, S3 operations
|
|
202
|
+
- [`@uploadbox/react`](https://www.npmjs.com/package/@uploadbox/react) — React components and hooks
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uploadbox/nextjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@uploadbox/core": "0.
|
|
15
|
+
"@uploadbox/core": "0.2.0"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
18
|
"next": "^14.0.0 || ^15.0.0 || ^16.0.0"
|