@igorvaryvoda/sirv-upload-widget 0.1.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 +185 -0
- package/package.json +77 -0
package/README.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# @sirv/upload-widget
|
|
2
|
+
|
|
3
|
+
A React file upload widget for [Sirv CDN](https://sirv.com) with batch uploads, CSV/Excel import, and file browser.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Drag & drop** file upload with progress tracking
|
|
8
|
+
- **Batch uploads** with configurable concurrency
|
|
9
|
+
- **CSV/Excel import** for bulk URL imports
|
|
10
|
+
- **Sirv file picker** to browse existing files
|
|
11
|
+
- **HEIC/HEIF conversion** for iPhone photos
|
|
12
|
+
- **Presigned URL support** for secure direct uploads
|
|
13
|
+
- **Customizable styling** via CSS variables
|
|
14
|
+
- **TypeScript** support with full type definitions
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @sirv/upload-widget
|
|
20
|
+
# or
|
|
21
|
+
yarn add @sirv/upload-widget
|
|
22
|
+
# or
|
|
23
|
+
pnpm add @sirv/upload-widget
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### 1. Create a presign endpoint on your backend
|
|
29
|
+
|
|
30
|
+
The widget uploads directly to Sirv using presigned URLs. Your backend just needs one endpoint:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
// app/api/sirv/presign/route.ts (Next.js)
|
|
34
|
+
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
|
|
35
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
|
|
36
|
+
|
|
37
|
+
const s3 = new S3Client({
|
|
38
|
+
endpoint: 'https://s3.sirv.com',
|
|
39
|
+
region: 'us-east-1',
|
|
40
|
+
credentials: {
|
|
41
|
+
accessKeyId: process.env.SIRV_S3_KEY!,
|
|
42
|
+
secretAccessKey: process.env.SIRV_S3_SECRET!,
|
|
43
|
+
},
|
|
44
|
+
forcePathStyle: true,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
export async function POST(req: Request) {
|
|
48
|
+
const { filename, contentType, folder } = await req.json()
|
|
49
|
+
const key = `${folder}/${filename}`.replace(/^\/+/, '')
|
|
50
|
+
|
|
51
|
+
const uploadUrl = await getSignedUrl(s3, new PutObjectCommand({
|
|
52
|
+
Bucket: process.env.SIRV_BUCKET!,
|
|
53
|
+
Key: key,
|
|
54
|
+
ContentType: contentType,
|
|
55
|
+
}), { expiresIn: 300 })
|
|
56
|
+
|
|
57
|
+
return Response.json({
|
|
58
|
+
uploadUrl,
|
|
59
|
+
publicUrl: `https://${process.env.SIRV_BUCKET}.sirv.com/${key}`,
|
|
60
|
+
path: '/' + key,
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. Use the widget in your React app
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import { SirvUploader } from '@sirv/upload-widget'
|
|
69
|
+
import '@sirv/upload-widget/styles.css'
|
|
70
|
+
|
|
71
|
+
export default function UploadPage() {
|
|
72
|
+
return (
|
|
73
|
+
<SirvUploader
|
|
74
|
+
presignEndpoint="/api/sirv/presign"
|
|
75
|
+
folder="/uploads"
|
|
76
|
+
onUpload={(files) => {
|
|
77
|
+
console.log('Uploaded files:', files)
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Props
|
|
85
|
+
|
|
86
|
+
| Prop | Type | Default | Description |
|
|
87
|
+
|------|------|---------|-------------|
|
|
88
|
+
| `presignEndpoint` | `string` | - | **Recommended.** URL to get presigned upload URLs |
|
|
89
|
+
| `proxyEndpoint` | `string` | - | Alternative: URL for full proxy mode |
|
|
90
|
+
| `folder` | `string` | `"/"` | Default upload folder |
|
|
91
|
+
| `onUpload` | `(files: SirvFile[]) => void` | - | Callback when files are uploaded |
|
|
92
|
+
| `onError` | `(error: string, file?: SirvFile) => void` | - | Callback on upload errors |
|
|
93
|
+
| `features` | `object` | - | Enable/disable features (see below) |
|
|
94
|
+
| `maxFiles` | `number` | `50` | Maximum files for batch upload |
|
|
95
|
+
| `maxFileSize` | `number` | `10485760` | Maximum file size in bytes |
|
|
96
|
+
| `autoUpload` | `boolean` | `true` | Start upload immediately on file selection |
|
|
97
|
+
| `concurrency` | `number` | `3` | Number of concurrent uploads |
|
|
98
|
+
| `onConflict` | `'overwrite' \| 'rename' \| 'skip' \| 'ask'` | `'rename'` | Filename conflict handling |
|
|
99
|
+
| `disabled` | `boolean` | `false` | Disable the widget |
|
|
100
|
+
| `compact` | `boolean` | `false` | Compact mode for smaller spaces |
|
|
101
|
+
| `labels` | `object` | - | Custom labels for i18n |
|
|
102
|
+
| `className` | `string` | - | Custom CSS class |
|
|
103
|
+
|
|
104
|
+
### Features Object
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
features?: {
|
|
108
|
+
batch?: boolean // Enable batch upload (default: true)
|
|
109
|
+
csvImport?: boolean // Enable CSV/Excel import (default: true)
|
|
110
|
+
filePicker?: boolean // Enable Sirv file browser (default: true)
|
|
111
|
+
dragDrop?: boolean // Enable drag & drop (default: true)
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Styling
|
|
116
|
+
|
|
117
|
+
Customize the widget using CSS variables:
|
|
118
|
+
|
|
119
|
+
```css
|
|
120
|
+
.sirv-uploader {
|
|
121
|
+
--sirv-primary: #0066cc;
|
|
122
|
+
--sirv-primary-hover: #0052a3;
|
|
123
|
+
--sirv-bg: #ffffff;
|
|
124
|
+
--sirv-text: #1e293b;
|
|
125
|
+
--sirv-border: #e2e8f0;
|
|
126
|
+
--sirv-radius: 8px;
|
|
127
|
+
/* ... see styles.css for all variables */
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Individual Components
|
|
132
|
+
|
|
133
|
+
For custom layouts, you can use the components individually:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import {
|
|
137
|
+
DropZone,
|
|
138
|
+
FileList,
|
|
139
|
+
FilePicker,
|
|
140
|
+
SpreadsheetImport,
|
|
141
|
+
useSirvUpload,
|
|
142
|
+
} from '@sirv/upload-widget'
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## TypeScript
|
|
146
|
+
|
|
147
|
+
Full TypeScript support with exported types:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import type {
|
|
151
|
+
SirvFile,
|
|
152
|
+
SirvUploaderProps,
|
|
153
|
+
PresignRequest,
|
|
154
|
+
PresignResponse,
|
|
155
|
+
} from '@sirv/upload-widget'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Backend Examples
|
|
159
|
+
|
|
160
|
+
### Cloudflare Workers (One-Click Deploy)
|
|
161
|
+
|
|
162
|
+
[](https://deploy.workers.cloudflare.com/?url=https://github.com/IgorVaryvoda/sirv-uploader/tree/main/examples/cloudflare-worker)
|
|
163
|
+
|
|
164
|
+
You'll be prompted for your Sirv account name and S3 credentials during deployment.
|
|
165
|
+
|
|
166
|
+
### Other Examples
|
|
167
|
+
|
|
168
|
+
See the `/examples` folder for:
|
|
169
|
+
- `cloudflare-worker/` - Cloudflare Workers (with deploy button)
|
|
170
|
+
- `nextjs-presign.ts` - Next.js API route
|
|
171
|
+
- `express-presign.ts` - Express.js server
|
|
172
|
+
|
|
173
|
+
## How It Works
|
|
174
|
+
|
|
175
|
+
1. User selects files in the widget
|
|
176
|
+
2. Widget requests a presigned URL from your backend
|
|
177
|
+
3. Your backend generates the URL using AWS SDK with Sirv's S3 endpoint
|
|
178
|
+
4. Widget uploads directly to Sirv using the presigned URL
|
|
179
|
+
5. File is available at `https://youraccount.sirv.com/path/to/file.jpg`
|
|
180
|
+
|
|
181
|
+
This approach keeps your Sirv credentials secure on the server while allowing fast, direct uploads from the browser.
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@igorvaryvoda/sirv-upload-widget",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A React file upload widget for Sirv CDN with batch uploads, CSV/Excel import, and file browser",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./styles.css": "./dist/styles.css"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": [
|
|
20
|
+
"*.css"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"clean": "rm -rf dist",
|
|
27
|
+
"test": "vitest",
|
|
28
|
+
"test:run": "vitest run",
|
|
29
|
+
"test:coverage": "vitest run --coverage"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
33
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"clsx": "^2.1.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@testing-library/dom": "^10.4.1",
|
|
40
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
41
|
+
"@testing-library/react": "^16.3.2",
|
|
42
|
+
"@types/react": "^19.0.0",
|
|
43
|
+
"@types/react-dom": "^19.0.0",
|
|
44
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
45
|
+
"jsdom": "^27.4.0",
|
|
46
|
+
"react": "^19.0.0",
|
|
47
|
+
"react-dom": "^19.0.0",
|
|
48
|
+
"tsup": "^8.0.0",
|
|
49
|
+
"typescript": "^5.0.0",
|
|
50
|
+
"vitest": "^4.0.17"
|
|
51
|
+
},
|
|
52
|
+
"optionalDependencies": {
|
|
53
|
+
"exceljs": "^4.4.0",
|
|
54
|
+
"heic2any": "^0.0.4"
|
|
55
|
+
},
|
|
56
|
+
"keywords": [
|
|
57
|
+
"sirv",
|
|
58
|
+
"upload",
|
|
59
|
+
"file-upload",
|
|
60
|
+
"react",
|
|
61
|
+
"cdn",
|
|
62
|
+
"image-upload",
|
|
63
|
+
"batch-upload",
|
|
64
|
+
"csv-import",
|
|
65
|
+
"drag-drop"
|
|
66
|
+
],
|
|
67
|
+
"author": "Sirv",
|
|
68
|
+
"license": "MIT",
|
|
69
|
+
"publishConfig": {
|
|
70
|
+
"access": "public"
|
|
71
|
+
},
|
|
72
|
+
"repository": {
|
|
73
|
+
"type": "git",
|
|
74
|
+
"url": "https://github.com/sirv/upload-widget"
|
|
75
|
+
},
|
|
76
|
+
"homepage": "https://sirv.com"
|
|
77
|
+
}
|