@valentinkolb/filegate 2.3.6 → 2.5.2
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/dist/activity.d.ts +15 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +21 -0
- package/dist/activity.js.map +1 -0
- package/dist/capabilities.d.ts +9 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +11 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/client.d.ts +37 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +77 -0
- package/dist/client.js.map +1 -0
- package/dist/core.d.ts +26 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +58 -0
- package/dist/core.js.map +1 -0
- package/dist/downloads.d.ts +9 -0
- package/dist/downloads.d.ts.map +1 -0
- package/dist/downloads.js +11 -0
- package/dist/downloads.js.map +1 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +42 -0
- package/dist/errors.js.map +1 -0
- package/dist/index-client.d.ts +17 -0
- package/dist/index-client.d.ts.map +1 -0
- package/dist/index-client.js +29 -0
- package/dist/index-client.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes.d.ts +27 -0
- package/dist/nodes.d.ts.map +1 -0
- package/dist/nodes.js +71 -0
- package/dist/nodes.js.map +1 -0
- package/dist/paths.d.ts +45 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +71 -0
- package/dist/paths.js.map +1 -0
- package/dist/search.d.ts +17 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +25 -0
- package/dist/search.js.map +1 -0
- package/dist/stats.d.ts +9 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +11 -0
- package/dist/stats.js.map +1 -0
- package/dist/transfers.d.ts +9 -0
- package/dist/transfers.d.ts.map +1 -0
- package/dist/transfers.js +14 -0
- package/dist/transfers.js.map +1 -0
- package/dist/types.d.ts +285 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/uploads.d.ts +246 -0
- package/dist/uploads.d.ts.map +1 -0
- package/dist/uploads.js +580 -0
- package/dist/uploads.js.map +1 -0
- package/dist/utils.d.ts +25 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +41 -0
- package/dist/utils.js.map +1 -0
- package/dist/versions.d.ts +76 -0
- package/dist/versions.d.ts.map +1 -0
- package/dist/versions.js +82 -0
- package/dist/versions.js.map +1 -0
- package/package.json +36 -41
- package/LICENSE +0 -21
- package/README.md +0 -569
- package/src/client.ts +0 -436
- package/src/config.ts +0 -37
- package/src/handlers/files.ts +0 -513
- package/src/handlers/search.ts +0 -137
- package/src/handlers/thumbnail.ts +0 -148
- package/src/handlers/upload.ts +0 -379
- package/src/index.ts +0 -94
- package/src/lib/openapi.ts +0 -47
- package/src/lib/ownership.ts +0 -133
- package/src/lib/path.ts +0 -128
- package/src/lib/response.ts +0 -10
- package/src/lib/validator.ts +0 -21
- package/src/schemas.ts +0 -329
- package/src/utils.ts +0 -282
package/README.md
DELETED
|
@@ -1,569 +0,0 @@
|
|
|
1
|
-
# Filegate
|
|
2
|
-
|
|
3
|
-
Secure file proxy for building custom file management systems. Streaming uploads, chunked transfers, Unix permissions.
|
|
4
|
-
|
|
5
|
-
```
|
|
6
|
-
Browser/App Your Backend Filegate Filesystem
|
|
7
|
-
| | | |
|
|
8
|
-
| upload request | | |
|
|
9
|
-
|------------------->| | |
|
|
10
|
-
| | proxy to filegate | |
|
|
11
|
-
| |-------------------->| |
|
|
12
|
-
| | | write file |
|
|
13
|
-
| | |------------------->|
|
|
14
|
-
| | | |
|
|
15
|
-
| |<--------------------|<-------------------|
|
|
16
|
-
|<-------------------| | |
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Filegate runs behind your backend, not as a public-facing service. Your backend handles authentication and authorization, then proxies requests to Filegate. You control access logic - Filegate handles file operations.
|
|
20
|
-
|
|
21
|
-
## Features
|
|
22
|
-
|
|
23
|
-
- Streaming uploads and downloads (no memory buffering)
|
|
24
|
-
- Resumable chunked uploads with SHA-256 verification
|
|
25
|
-
- Directory downloads as TAR archives
|
|
26
|
-
- Unix file permissions (uid, gid, mode)
|
|
27
|
-
- Glob-based file search
|
|
28
|
-
- Type-safe client with full TypeScript support
|
|
29
|
-
- OpenAPI documentation
|
|
30
|
-
- Minimal dependencies (Hono, Zod - no database required)
|
|
31
|
-
|
|
32
|
-
## Quick Start
|
|
33
|
-
|
|
34
|
-
### 1. Start Filegate with Docker
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
docker run -d \
|
|
38
|
-
--name filegate \
|
|
39
|
-
-p 4000:4000 \
|
|
40
|
-
-e FILE_PROXY_TOKEN=your-secret-token \
|
|
41
|
-
-e ALLOWED_BASE_PATHS=/data \
|
|
42
|
-
-v /path/to/your/files:/data \
|
|
43
|
-
ghcr.io/valentinkolb/filegate:latest
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### 2. Install the Client
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
npm install @valentinkolb/filegate
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### 3. Configure Environment
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
export FILEGATE_URL=http://localhost:4000
|
|
56
|
-
export FILEGATE_TOKEN=your-secret-token
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### 4. Upload a File
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
import { filegate } from "@valentinkolb/filegate/client";
|
|
63
|
-
|
|
64
|
-
const result = await filegate.upload.single({
|
|
65
|
-
path: "/data/uploads",
|
|
66
|
-
filename: "document.pdf",
|
|
67
|
-
data: fileBuffer,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
if (result.ok) {
|
|
71
|
-
console.log("Uploaded:", result.data.path);
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### 5. Download a File
|
|
76
|
-
|
|
77
|
-
```typescript
|
|
78
|
-
import { filegate } from "@valentinkolb/filegate/client";
|
|
79
|
-
|
|
80
|
-
const result = await filegate.download({ path: "/data/uploads/document.pdf" });
|
|
81
|
-
|
|
82
|
-
if (result.ok) {
|
|
83
|
-
const blob = await result.data.blob();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Open in browser instead of downloading
|
|
87
|
-
const preview = await filegate.download({
|
|
88
|
-
path: "/data/uploads/image.png",
|
|
89
|
-
inline: true,
|
|
90
|
-
});
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Core Concepts
|
|
94
|
-
|
|
95
|
-
### Base Paths
|
|
96
|
-
|
|
97
|
-
Filegate restricts all file operations to explicitly allowed directories called "base paths". This is a security boundary - files outside these paths cannot be accessed.
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
ALLOWED_BASE_PATHS=/data/uploads,/data/exports
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
With this configuration:
|
|
104
|
-
- `/data/uploads/file.txt` - allowed
|
|
105
|
-
- `/data/exports/report.pdf` - allowed
|
|
106
|
-
- `/home/user/file.txt` - forbidden
|
|
107
|
-
- `/data/../etc/passwd` - forbidden (path traversal blocked)
|
|
108
|
-
|
|
109
|
-
Symlinks that point outside base paths are also blocked.
|
|
110
|
-
|
|
111
|
-
### Auto-Create Parent Directories
|
|
112
|
-
|
|
113
|
-
When uploading files, Filegate automatically creates any missing parent directories. This simplifies folder uploads where nested structures need to be created on-the-fly:
|
|
114
|
-
|
|
115
|
-
```typescript
|
|
116
|
-
// Parent directories /data/new/nested/path will be created automatically
|
|
117
|
-
await client.upload.single({
|
|
118
|
-
path: "/data/new/nested/path",
|
|
119
|
-
filename: "file.txt",
|
|
120
|
-
data: buffer,
|
|
121
|
-
});
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
This applies to both simple uploads and chunked uploads.
|
|
125
|
-
|
|
126
|
-
### File Ownership
|
|
127
|
-
|
|
128
|
-
Filegate can set Unix file ownership on uploaded files:
|
|
129
|
-
|
|
130
|
-
```typescript
|
|
131
|
-
await client.upload.single({
|
|
132
|
-
path: "/data/uploads",
|
|
133
|
-
filename: "file.txt",
|
|
134
|
-
data: buffer,
|
|
135
|
-
uid: 1000, // Owner user ID
|
|
136
|
-
gid: 1000, // Owner group ID
|
|
137
|
-
mode: "644", // Unix permissions (rw-r--r--)
|
|
138
|
-
});
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
If ownership is not specified, files are created with the user running Filegate (typically root in Docker).
|
|
142
|
-
|
|
143
|
-
Filegate does not validate whether the specified uid/gid exists on the system, nor does it verify that the requesting user matches the specified ownership. Your backend is responsible for this validation.
|
|
144
|
-
|
|
145
|
-
This feature is intended for scenarios like NFS shares exposed through Filegate, where preserving the original permission structure is required.
|
|
146
|
-
|
|
147
|
-
### Transfer (Move/Copy)
|
|
148
|
-
|
|
149
|
-
The `transfer` endpoint handles both moving and copying files or directories:
|
|
150
|
-
|
|
151
|
-
```typescript
|
|
152
|
-
// Move (rename) a file - same base path only
|
|
153
|
-
await client.transfer({
|
|
154
|
-
from: "/data/old-name.txt",
|
|
155
|
-
to: "/data/new-name.txt",
|
|
156
|
-
mode: "move",
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Copy within same base path - no ownership required
|
|
160
|
-
await client.transfer({
|
|
161
|
-
from: "/data/file.txt",
|
|
162
|
-
to: "/data/backup/file.txt",
|
|
163
|
-
mode: "copy",
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// Copy across different base paths - ownership required
|
|
167
|
-
await client.transfer({
|
|
168
|
-
from: "/data/file.txt",
|
|
169
|
-
to: "/backup/file.txt",
|
|
170
|
-
mode: "copy",
|
|
171
|
-
uid: 1000,
|
|
172
|
-
gid: 1000,
|
|
173
|
-
fileMode: "644",
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
// Allow overwriting existing files (default: false)
|
|
177
|
-
await client.transfer({
|
|
178
|
-
from: "/data/new-file.txt",
|
|
179
|
-
to: "/data/existing-file.txt",
|
|
180
|
-
mode: "copy",
|
|
181
|
-
ensureUniqueName: false, // Overwrite if target exists
|
|
182
|
-
});
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
**Rules:**
|
|
186
|
-
- `mode: "move"` - Only within the same base path (uses filesystem rename)
|
|
187
|
-
- `mode: "copy"` without ownership - Only within the same base path
|
|
188
|
-
- `mode: "copy"` with ownership - Allows cross-base copying (ownership is applied recursively)
|
|
189
|
-
- Both operations work recursively on directories
|
|
190
|
-
- `ensureUniqueName: true` (default) - Appends `-01`, `-02`, etc. if target exists
|
|
191
|
-
- `ensureUniqueName: false` - Overwrites existing target file
|
|
192
|
-
|
|
193
|
-
### Thumbnails
|
|
194
|
-
|
|
195
|
-
Filegate can generate image thumbnails on-the-fly using Sharp. No caching - thumbnails are generated per request (typically 5-20ms).
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
// Get a 200x200 cover thumbnail (default)
|
|
199
|
-
const result = await client.thumbnail.image({
|
|
200
|
-
path: "/data/photos/vacation.jpg",
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
if (result.ok) {
|
|
204
|
-
const blob = await result.data.blob();
|
|
205
|
-
// Use as image source
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Customized thumbnail
|
|
209
|
-
const result = await client.thumbnail.image({
|
|
210
|
-
path: "/data/photos/vacation.jpg",
|
|
211
|
-
width: 400,
|
|
212
|
-
height: 300,
|
|
213
|
-
fit: "contain", // Fit within bounds, preserve aspect ratio
|
|
214
|
-
format: "webp", // Output format
|
|
215
|
-
quality: 90, // Higher quality
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
// Smart cropping with attention detection
|
|
219
|
-
const result = await client.thumbnail.image({
|
|
220
|
-
path: "/data/photos/portrait.jpg",
|
|
221
|
-
width: 150,
|
|
222
|
-
height: 150,
|
|
223
|
-
fit: "cover",
|
|
224
|
-
position: "attention", // Focus on interesting areas
|
|
225
|
-
});
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
**Options:**
|
|
229
|
-
|
|
230
|
-
| Parameter | Default | Description |
|
|
231
|
-
|-----------|---------|-------------|
|
|
232
|
-
| `width` | 200 | Width in pixels (max 2000) |
|
|
233
|
-
| `height` | 200 | Height in pixels (max 2000) |
|
|
234
|
-
| `fit` | `cover` | `cover`, `contain`, `fill`, `inside`, `outside` |
|
|
235
|
-
| `position` | `center` | Crop position: `center`, `top`, `bottom`, `left`, `right`, `entropy`, `attention` |
|
|
236
|
-
| `format` | `webp` | Output: `webp`, `jpeg`, `png`, `avif` |
|
|
237
|
-
| `quality` | 80 | Quality 1-100 |
|
|
238
|
-
|
|
239
|
-
**Fit modes:**
|
|
240
|
-
- `cover` - Fill the box, crop excess (best for uniform grids)
|
|
241
|
-
- `contain` - Fit within box, preserve aspect ratio (may have letterboxing)
|
|
242
|
-
- `fill` - Stretch to exact size (distorts)
|
|
243
|
-
- `inside` - Like contain, but never upscale
|
|
244
|
-
- `outside` - Like cover, but never downscale
|
|
245
|
-
|
|
246
|
-
**Supported input formats:** JPEG, PNG, WebP, AVIF, TIFF, GIF, SVG
|
|
247
|
-
|
|
248
|
-
**Caching:** Thumbnails include `ETag`, `Last-Modified`, and `Cache-Control: immutable` headers. Simply pass through the response:
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
const result = await client.thumbnail.image({ path: "/data/photo.jpg" });
|
|
252
|
-
if (!result.ok) return c.json({ error: result.error }, result.status);
|
|
253
|
-
|
|
254
|
-
return result.data; // Response with all headers
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### Chunked Uploads
|
|
258
|
-
|
|
259
|
-
For large files, use chunked uploads. They support:
|
|
260
|
-
- Resume after connection failure
|
|
261
|
-
- Progress tracking
|
|
262
|
-
- Per-chunk checksum verification
|
|
263
|
-
- Automatic assembly when complete
|
|
264
|
-
|
|
265
|
-
The [Browser Utilities](#browser-utilities) help with checksum calculation, chunking, and progress tracking. They work both in the browser and on the server.
|
|
266
|
-
|
|
267
|
-
```typescript
|
|
268
|
-
// Start or resume upload
|
|
269
|
-
const start = await client.upload.chunked.start({
|
|
270
|
-
path: "/data/uploads",
|
|
271
|
-
filename: "large-file.zip",
|
|
272
|
-
size: file.size,
|
|
273
|
-
checksum: "sha256:abc123...", // Checksum of entire file
|
|
274
|
-
chunkSize: 5 * 1024 * 1024, // 5MB chunks
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
// Upload each chunk
|
|
278
|
-
for (let i = 0; i < start.data.totalChunks; i++) {
|
|
279
|
-
if (start.data.uploadedChunks.includes(i)) continue; // Skip already uploaded
|
|
280
|
-
|
|
281
|
-
await client.upload.chunked.send({
|
|
282
|
-
uploadId: start.data.uploadId,
|
|
283
|
-
index: i,
|
|
284
|
-
data: chunkData,
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
Uploads automatically expire after 24 hours of inactivity.
|
|
290
|
-
|
|
291
|
-
## Configuration
|
|
292
|
-
|
|
293
|
-
All configuration is done through environment variables.
|
|
294
|
-
|
|
295
|
-
| Variable | Required | Default | Description |
|
|
296
|
-
|----------|----------|---------|-------------|
|
|
297
|
-
| `FILE_PROXY_TOKEN` | Yes | - | Bearer token for API authentication |
|
|
298
|
-
| `ALLOWED_BASE_PATHS` | Yes | - | Comma-separated list of allowed directories |
|
|
299
|
-
| `PORT` | No | 4000 | Server port |
|
|
300
|
-
| `MAX_UPLOAD_MB` | No | 500 | Maximum upload size in MB |
|
|
301
|
-
| `MAX_DOWNLOAD_MB` | No | 5000 | Maximum download size in MB |
|
|
302
|
-
| `MAX_CHUNK_SIZE_MB` | No | 50 | Maximum chunk size in MB |
|
|
303
|
-
| `SEARCH_MAX_RESULTS` | No | 100 | Maximum search results returned |
|
|
304
|
-
| `SEARCH_MAX_RECURSIVE_WILDCARDS` | No | 10 | Maximum `**` wildcards in glob patterns |
|
|
305
|
-
| `UPLOAD_EXPIRY_HOURS` | No | 24 | Hours until incomplete uploads expire |
|
|
306
|
-
| `UPLOAD_TEMP_DIR` | No | /tmp/filegate-uploads | Directory for temporary chunk storage |
|
|
307
|
-
| `DISK_CLEANUP_INTERVAL_HOURS` | No | 6 | Interval for cleaning orphaned chunks |
|
|
308
|
-
|
|
309
|
-
### Development Mode
|
|
310
|
-
|
|
311
|
-
For local development without root permissions, you can override file ownership:
|
|
312
|
-
|
|
313
|
-
```bash
|
|
314
|
-
DEV_UID_OVERRIDE=1000
|
|
315
|
-
DEV_GID_OVERRIDE=1000
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
This applies the specified uid/gid instead of the requested values. Do not use in production.
|
|
319
|
-
|
|
320
|
-
## Docker Compose Example
|
|
321
|
-
|
|
322
|
-
```yaml
|
|
323
|
-
services:
|
|
324
|
-
filegate:
|
|
325
|
-
image: ghcr.io/valentinkolb/filegate:latest
|
|
326
|
-
ports:
|
|
327
|
-
- "4000:4000"
|
|
328
|
-
environment:
|
|
329
|
-
FILE_PROXY_TOKEN: ${FILE_PROXY_TOKEN}
|
|
330
|
-
ALLOWED_BASE_PATHS: /data
|
|
331
|
-
volumes:
|
|
332
|
-
- ./data:/data
|
|
333
|
-
- filegate-chunks:/tmp/filegate-uploads
|
|
334
|
-
|
|
335
|
-
volumes:
|
|
336
|
-
filegate-chunks:
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
## Client API
|
|
340
|
-
|
|
341
|
-
The client provides a type-safe interface for all Filegate operations.
|
|
342
|
-
|
|
343
|
-
### Installation
|
|
344
|
-
|
|
345
|
-
```bash
|
|
346
|
-
npm install @valentinkolb/filegate
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### Default Instance
|
|
350
|
-
|
|
351
|
-
Set `FILEGATE_URL` and `FILEGATE_TOKEN` environment variables, then import the pre-configured client:
|
|
352
|
-
|
|
353
|
-
```typescript
|
|
354
|
-
import { filegate } from "@valentinkolb/filegate/client";
|
|
355
|
-
|
|
356
|
-
await filegate.info({ path: "/data/uploads" });
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
### Custom Instance
|
|
360
|
-
|
|
361
|
-
For more control or multiple Filegate servers, create instances manually:
|
|
362
|
-
|
|
363
|
-
```typescript
|
|
364
|
-
import { Filegate } from "@valentinkolb/filegate/client";
|
|
365
|
-
|
|
366
|
-
const client = new Filegate({
|
|
367
|
-
url: "http://localhost:4000",
|
|
368
|
-
token: "your-secret-token",
|
|
369
|
-
});
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
### Methods
|
|
373
|
-
|
|
374
|
-
```typescript
|
|
375
|
-
// Get file or directory info
|
|
376
|
-
await client.info({ path: "/data/file.txt", showHidden: false });
|
|
377
|
-
|
|
378
|
-
// Get directory info with recursive sizes (slower)
|
|
379
|
-
await client.info({ path: "/data/uploads", computeSizes: true });
|
|
380
|
-
|
|
381
|
-
// Download file (returns streaming Response)
|
|
382
|
-
await client.download({ path: "/data/file.txt" });
|
|
383
|
-
|
|
384
|
-
// Download and open in browser (inline)
|
|
385
|
-
await client.download({ path: "/data/image.png", inline: true });
|
|
386
|
-
|
|
387
|
-
// Download directory as TAR archive
|
|
388
|
-
await client.download({ path: "/data/folder" });
|
|
389
|
-
|
|
390
|
-
// Simple upload
|
|
391
|
-
await client.upload.single({
|
|
392
|
-
path: "/data/uploads",
|
|
393
|
-
filename: "file.txt",
|
|
394
|
-
data: buffer,
|
|
395
|
-
uid: 1000,
|
|
396
|
-
gid: 1000,
|
|
397
|
-
mode: "644",
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
// Chunked upload
|
|
401
|
-
await client.upload.chunked.start({ ... });
|
|
402
|
-
await client.upload.chunked.send({ ... });
|
|
403
|
-
|
|
404
|
-
// Create directory
|
|
405
|
-
await client.mkdir({ path: "/data/new-folder", mode: "755" });
|
|
406
|
-
|
|
407
|
-
// Delete file or directory
|
|
408
|
-
await client.delete({ path: "/data/old-file.txt" });
|
|
409
|
-
|
|
410
|
-
// Transfer: Move or copy files/directories
|
|
411
|
-
await client.transfer({
|
|
412
|
-
from: "/data/old.txt",
|
|
413
|
-
to: "/data/new.txt",
|
|
414
|
-
mode: "move", // or "copy"
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
// Transfer with ownership (required for cross-base copy)
|
|
418
|
-
await client.transfer({
|
|
419
|
-
from: "/data/file.txt",
|
|
420
|
-
to: "/backup/file.txt",
|
|
421
|
-
mode: "copy",
|
|
422
|
-
uid: 1000,
|
|
423
|
-
gid: 1000,
|
|
424
|
-
fileMode: "644",
|
|
425
|
-
dirMode: "755",
|
|
426
|
-
ensureUniqueName: true, // default: append -01, -02 if target exists
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
// Search files with glob patterns
|
|
430
|
-
await client.glob({
|
|
431
|
-
paths: ["/data/uploads"],
|
|
432
|
-
pattern: "**/*.pdf",
|
|
433
|
-
limit: 50,
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
// Search directories only
|
|
437
|
-
await client.glob({
|
|
438
|
-
paths: ["/data"],
|
|
439
|
-
pattern: "**/*",
|
|
440
|
-
files: false,
|
|
441
|
-
directories: true,
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
// Search both files and directories
|
|
445
|
-
await client.glob({
|
|
446
|
-
paths: ["/data"],
|
|
447
|
-
pattern: "**/*",
|
|
448
|
-
directories: true,
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
// Generate image thumbnail
|
|
452
|
-
await client.thumbnail.image({
|
|
453
|
-
path: "/data/photo.jpg",
|
|
454
|
-
width: 200,
|
|
455
|
-
height: 200,
|
|
456
|
-
fit: "cover",
|
|
457
|
-
position: "center",
|
|
458
|
-
format: "webp",
|
|
459
|
-
quality: 80,
|
|
460
|
-
});
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
### Response Format
|
|
464
|
-
|
|
465
|
-
All methods return a discriminated union:
|
|
466
|
-
|
|
467
|
-
```typescript
|
|
468
|
-
type Response<T> =
|
|
469
|
-
| { ok: true; data: T }
|
|
470
|
-
| { ok: false; error: string; status: number };
|
|
471
|
-
|
|
472
|
-
const result = await client.info({ path: "/data/file.txt" });
|
|
473
|
-
|
|
474
|
-
if (result.ok) {
|
|
475
|
-
console.log(result.data.size);
|
|
476
|
-
} else {
|
|
477
|
-
console.error(result.error); // "not found", "path not allowed", etc.
|
|
478
|
-
}
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
## Browser Utilities
|
|
482
|
-
|
|
483
|
-
Utilities for chunked uploads that work both in the browser and on the server. They handle file chunking, SHA-256 checksum calculation, progress tracking, and retry logic.
|
|
484
|
-
|
|
485
|
-
```typescript
|
|
486
|
-
import { chunks, formatBytes } from "@valentinkolb/filegate/utils";
|
|
487
|
-
|
|
488
|
-
// Prepare a file for chunked upload
|
|
489
|
-
const upload = await chunks.prepare({
|
|
490
|
-
file: fileInput.files[0],
|
|
491
|
-
chunkSize: 5 * 1024 * 1024,
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
console.log(upload.checksum); // "sha256:..."
|
|
495
|
-
console.log(upload.totalChunks); // Number of chunks
|
|
496
|
-
console.log(formatBytes({ bytes: upload.fileSize })); // "52.4 MB"
|
|
497
|
-
|
|
498
|
-
// Subscribe to progress updates
|
|
499
|
-
upload.subscribe((state) => {
|
|
500
|
-
console.log(`${state.percent}% - ${state.status}`);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
// Upload all chunks with retry and concurrency
|
|
504
|
-
await upload.sendAll({
|
|
505
|
-
skip: alreadyUploadedChunks,
|
|
506
|
-
retries: 3,
|
|
507
|
-
concurrency: 3,
|
|
508
|
-
fn: async ({ index, data }) => {
|
|
509
|
-
await fetch("/api/upload/chunk", {
|
|
510
|
-
method: "POST",
|
|
511
|
-
headers: {
|
|
512
|
-
"X-Upload-Id": uploadId,
|
|
513
|
-
"X-Chunk-Index": String(index),
|
|
514
|
-
},
|
|
515
|
-
body: data,
|
|
516
|
-
});
|
|
517
|
-
},
|
|
518
|
-
});
|
|
519
|
-
```
|
|
520
|
-
|
|
521
|
-
## API Endpoints
|
|
522
|
-
|
|
523
|
-
All `/files/*` endpoints require `Authorization: Bearer <token>`.
|
|
524
|
-
|
|
525
|
-
| Method | Path | Description |
|
|
526
|
-
|--------|------|-------------|
|
|
527
|
-
| GET | `/health` | Health check |
|
|
528
|
-
| GET | `/docs` | OpenAPI documentation (Scalar UI) |
|
|
529
|
-
| GET | `/openapi.json` | OpenAPI specification |
|
|
530
|
-
| GET | `/llms.txt` | LLM-friendly markdown documentation |
|
|
531
|
-
| GET | `/files/info` | Get file or directory info. Use `?computeSizes=true` for recursive dir sizes |
|
|
532
|
-
| GET | `/files/content` | Download file or directory (TAR). Use `?inline=true` to view in browser |
|
|
533
|
-
| PUT | `/files/content` | Upload file |
|
|
534
|
-
| POST | `/files/mkdir` | Create directory |
|
|
535
|
-
| DELETE | `/files/delete` | Delete file or directory |
|
|
536
|
-
| POST | `/files/transfer` | Move or copy file/directory. Cross-base copy requires ownership |
|
|
537
|
-
| GET | `/files/search` | Search with glob pattern. Use `?directories=true` to include folders |
|
|
538
|
-
| POST | `/files/upload/start` | Start or resume chunked upload |
|
|
539
|
-
| POST | `/files/upload/chunk` | Upload a chunk |
|
|
540
|
-
| GET | `/files/thumbnail/image` | Generate image thumbnail on-the-fly |
|
|
541
|
-
|
|
542
|
-
## Security
|
|
543
|
-
|
|
544
|
-
Filegate implements multiple security layers:
|
|
545
|
-
|
|
546
|
-
- **Path validation**: All paths are validated against allowed base paths
|
|
547
|
-
- **Symlink protection**: Symlinks pointing outside base paths are blocked
|
|
548
|
-
- **Path traversal prevention**: Sequences like `../` are normalized and checked
|
|
549
|
-
- **Size limits**: Configurable limits for uploads, downloads, and chunks
|
|
550
|
-
- **Search limits**: Glob pattern complexity is limited to prevent DoS
|
|
551
|
-
- **Secure headers**: X-Frame-Options, X-Content-Type-Options, etc.
|
|
552
|
-
|
|
553
|
-
## Development
|
|
554
|
-
|
|
555
|
-
```bash
|
|
556
|
-
# Install dependencies
|
|
557
|
-
bun install
|
|
558
|
-
|
|
559
|
-
# Run server
|
|
560
|
-
FILE_PROXY_TOKEN=dev ALLOWED_BASE_PATHS=/tmp bun run src/index.ts
|
|
561
|
-
|
|
562
|
-
# Run tests
|
|
563
|
-
bun run test:unit
|
|
564
|
-
bun run test:integration:run
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
## License
|
|
568
|
-
|
|
569
|
-
MIT
|