@valentinkolb/filegate 2.4.0 → 2.5.3

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.
Files changed (88) hide show
  1. package/README.md +109 -512
  2. package/dist/activity.d.ts +15 -0
  3. package/dist/activity.d.ts.map +1 -0
  4. package/dist/activity.js +21 -0
  5. package/dist/activity.js.map +1 -0
  6. package/dist/capabilities.d.ts +9 -0
  7. package/dist/capabilities.d.ts.map +1 -0
  8. package/dist/capabilities.js +11 -0
  9. package/dist/capabilities.js.map +1 -0
  10. package/dist/client.d.ts +37 -0
  11. package/dist/client.d.ts.map +1 -0
  12. package/dist/client.js +77 -0
  13. package/dist/client.js.map +1 -0
  14. package/dist/core.d.ts +26 -0
  15. package/dist/core.d.ts.map +1 -0
  16. package/dist/core.js +58 -0
  17. package/dist/core.js.map +1 -0
  18. package/dist/downloads.d.ts +9 -0
  19. package/dist/downloads.d.ts.map +1 -0
  20. package/dist/downloads.js +11 -0
  21. package/dist/downloads.js.map +1 -0
  22. package/dist/errors.d.ts +18 -0
  23. package/dist/errors.d.ts.map +1 -0
  24. package/dist/errors.js +42 -0
  25. package/dist/errors.js.map +1 -0
  26. package/dist/index-client.d.ts +17 -0
  27. package/dist/index-client.d.ts.map +1 -0
  28. package/dist/index-client.js +29 -0
  29. package/dist/index-client.js.map +1 -0
  30. package/dist/index.d.ts +14 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +7 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/nodes.d.ts +27 -0
  35. package/dist/nodes.d.ts.map +1 -0
  36. package/dist/nodes.js +71 -0
  37. package/dist/nodes.js.map +1 -0
  38. package/dist/paths.d.ts +45 -0
  39. package/dist/paths.d.ts.map +1 -0
  40. package/dist/paths.js +71 -0
  41. package/dist/paths.js.map +1 -0
  42. package/dist/search.d.ts +17 -0
  43. package/dist/search.d.ts.map +1 -0
  44. package/dist/search.js +25 -0
  45. package/dist/search.js.map +1 -0
  46. package/dist/stats.d.ts +9 -0
  47. package/dist/stats.d.ts.map +1 -0
  48. package/dist/stats.js +11 -0
  49. package/dist/stats.js.map +1 -0
  50. package/dist/transfers.d.ts +9 -0
  51. package/dist/transfers.d.ts.map +1 -0
  52. package/dist/transfers.js +14 -0
  53. package/dist/transfers.js.map +1 -0
  54. package/dist/types.d.ts +285 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +2 -0
  57. package/dist/types.js.map +1 -0
  58. package/dist/uploads.d.ts +246 -0
  59. package/dist/uploads.d.ts.map +1 -0
  60. package/dist/uploads.js +580 -0
  61. package/dist/uploads.js.map +1 -0
  62. package/dist/utils.d.ts +25 -0
  63. package/dist/utils.d.ts.map +1 -0
  64. package/dist/utils.js +41 -0
  65. package/dist/utils.js.map +1 -0
  66. package/dist/versions.d.ts +76 -0
  67. package/dist/versions.d.ts.map +1 -0
  68. package/dist/versions.js +82 -0
  69. package/dist/versions.js.map +1 -0
  70. package/package.json +36 -40
  71. package/LICENSE +0 -21
  72. package/src/client.ts +0 -436
  73. package/src/config.ts +0 -41
  74. package/src/handlers/files.ts +0 -696
  75. package/src/handlers/indexHandler.ts +0 -107
  76. package/src/handlers/search.ts +0 -144
  77. package/src/handlers/thumbnail.ts +0 -174
  78. package/src/handlers/upload.ts +0 -401
  79. package/src/index.ts +0 -131
  80. package/src/lib/index.ts +0 -325
  81. package/src/lib/openapi.ts +0 -48
  82. package/src/lib/ownership.ts +0 -133
  83. package/src/lib/path.ts +0 -128
  84. package/src/lib/response.ts +0 -10
  85. package/src/lib/scanner.ts +0 -121
  86. package/src/lib/validator.ts +0 -21
  87. package/src/schemas.ts +0 -376
  88. package/src/utils.ts +0 -282
package/README.md CHANGED
@@ -1,569 +1,166 @@
1
- # Filegate
1
+ # @valentinkolb/filegate
2
2
 
3
- Secure file proxy for building custom file management systems. Streaming uploads, chunked transfers, Unix permissions.
3
+ TypeScript client for Filegate. Use it from trusted server runtimes to call the
4
+ Filegate REST API, and from browsers only with scoped direct upload/download
5
+ URLs minted by your application server.
4
6
 
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)
7
+ Filegate keeps files as normal Linux files on configured mounts. The client
8
+ covers path and ID lookup, direct URLs, upload sessions, transfers, search,
9
+ activity, and stats.
31
10
 
32
- ## Quick Start
33
-
34
- ### 1. Start Filegate with Docker
11
+ ## Install
35
12
 
36
13
  ```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
14
+ npm i @valentinkolb/filegate
44
15
  ```
45
16
 
46
- ### 2. Install the Client
47
-
48
- ```bash
49
- npm install @valentinkolb/filegate
50
- ```
17
+ ## Server client
51
18
 
52
- ### 3. Configure Environment
19
+ Use the default instance when `FILEGATE_URL` and `FILEGATE_TOKEN` are available
20
+ in the server runtime:
53
21
 
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
22
+ ```ts
62
23
  import { filegate } from "@valentinkolb/filegate/client";
63
24
 
64
- const result = await filegate.upload.single({
65
- path: "/data/uploads",
66
- filename: "document.pdf",
67
- data: fileBuffer,
68
- });
25
+ process.env.FILEGATE_URL = "http://127.0.0.1:8080";
26
+ process.env.FILEGATE_TOKEN = "dev-token";
69
27
 
70
- if (result.ok) {
71
- console.log("Uploaded:", result.data.path);
72
- }
28
+ const roots = await filegate.paths.get();
29
+ const caps = await filegate.capabilities.get();
73
30
  ```
74
31
 
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" });
32
+ Use an explicit instance for dependency injection:
81
33
 
82
- if (result.ok) {
83
- const blob = await result.data.blob();
84
- }
34
+ ```ts
35
+ import { Filegate } from "@valentinkolb/filegate/client";
85
36
 
86
- // Open in browser instead of downloading
87
- const preview = await filegate.download({
88
- path: "/data/uploads/image.png",
89
- inline: true,
37
+ const fg = new Filegate({
38
+ baseUrl: "https://filegate.internal.example",
39
+ token: "<filegate-token>",
40
+ fetchImpl: fetch,
90
41
  });
91
- ```
92
-
93
- ## Core Concepts
94
42
 
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
43
+ const node = await fg.paths.get("photos/2026");
101
44
  ```
102
45
 
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)
46
+ The client namespaces match the API surface:
108
47
 
109
- Symlinks that point outside base paths are also blocked.
48
+ | Namespace | Meaning |
49
+ | --- | --- |
50
+ | `paths` | Path-based lookup, upload, and directory listing. |
51
+ | `nodes` | ID-based metadata, content, mkdir, delete, and metadata edits. |
52
+ | `uploads` | One-shot direct upload URLs and resumable upload sessions. |
53
+ | `downloads` | Scoped direct download URLs. |
54
+ | `transfers` | Move and copy operations. |
55
+ | `search` | Indexed path search. |
56
+ | `index` | Index stats and rescan operations. |
57
+ | `stats` | Runtime, mount, cache, filesystem, and process stats. |
58
+ | `capabilities` | Server upload/download limits for adaptive clients. |
59
+ | `versions` | File version history on supported mounts. |
60
+ | `activity` | Recent API activity from the bounded server ring buffer. |
110
61
 
111
- ### Auto-Create Parent Directories
62
+ ## Browser uploads
112
63
 
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:
64
+ Do not expose the Filegate bearer token to browser code. The browser should ask
65
+ your application server for permission to upload. The application server then
66
+ creates direct Filegate upload URLs or direct upload sessions and returns the
67
+ scoped credentials to the browser.
114
68
 
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:
69
+ ```ts
70
+ import { upload } from "@valentinkolb/filegate/client";
129
71
 
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--)
72
+ await upload({
73
+ files,
74
+ path: "photos/inbox",
75
+ allow: async (request) => {
76
+ const response = await fetch("/api/filegate/uploads/allow", {
77
+ method: "POST",
78
+ headers: { "Content-Type": "application/json" },
79
+ body: JSON.stringify(request),
80
+ });
81
+ return response.json();
82
+ },
83
+ onEvent: (event) => {
84
+ console.log(event.type, event);
85
+ },
138
86
  });
139
87
  ```
140
88
 
141
- If ownership is not specified, files are created with the user running Filegate (typically root in Docker).
89
+ The high-level helper uses the transfer shape from the allow response. Small
90
+ files can use direct one-shot URLs. Larger files can use resumable sessions
91
+ with parallel segment uploads. Conflict handling is explicit; `skip-existing`
92
+ is the default.
142
93
 
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.
94
+ For a single direct upload URL:
144
95
 
145
- This feature is intended for scenarios like NFS shares exposed through Filegate, where preserving the original permission structure is required.
96
+ ```ts
97
+ import { uploadDirect } from "@valentinkolb/filegate/client";
146
98
 
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
99
+ await uploadDirect(uploadUrlFromYourServer, file, {
100
+ onSuccess: ({ node }) => console.log(node.id),
182
101
  });
183
102
  ```
184
103
 
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
104
+ ## Browser downloads
194
105
 
195
- Filegate can generate image thumbnails on-the-fly using Sharp. No caching - thumbnails are generated per request (typically 5-20ms).
106
+ Mint the direct download URL on your application server and redirect the
107
+ browser:
196
108
 
197
- ```typescript
198
- // Get a 200x200 cover thumbnail (default)
199
- const result = await client.thumbnail.image({
200
- path: "/data/photos/vacation.jpg",
109
+ ```ts
110
+ const direct = await fg.downloads.createDirectURL({
111
+ nodeId: "<node-id>",
112
+ expiresInSeconds: 5 * 60,
201
113
  });
202
114
 
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
115
+ return Response.redirect(direct.downloadUrl, 303);
255
116
  ```
256
117
 
257
- ### Chunked Uploads
118
+ Direct file downloads support `GET`, `HEAD`, and byte ranges. Directory
119
+ downloads stream tar archives.
258
120
 
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
121
+ ## Resumable sessions
264
122
 
265
- The [Browser Utilities](#browser-utilities) help with checksum calculation, chunking, and progress tracking. They work both in the browser and on the server.
123
+ Use upload sessions when you need resumable, parallel uploads with explicit
124
+ commit:
266
125
 
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
126
+ ```ts
127
+ const session = await fg.uploads.sessions.create({
128
+ path: "videos/input.mov",
129
+ size,
130
+ checksum,
131
+ segmentSize: 16 * 1024 * 1024,
132
+ onConflict: "error",
275
133
  });
276
134
 
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,
135
+ for (const segment of session.segments) {
136
+ await fg.uploads.sessions.segments.put({
137
+ sessionId: session.id,
138
+ index: segment.index,
139
+ body: segmentBody,
140
+ checksum: segmentChecksum,
285
141
  });
286
142
  }
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
143
 
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
- });
144
+ const committed = await fg.uploads.sessions.commit({ sessionId: session.id });
145
+ console.log(committed.node.id);
519
146
  ```
520
147
 
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 |
148
+ Segment PUTs are idempotent when the content matches. Commit is explicit and
149
+ safe to retry after success.
541
150
 
542
- ## Security
151
+ ## Utilities
543
152
 
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
153
+ Pure helpers live under `@valentinkolb/filegate/utils` and do not require a
154
+ Filegate token:
558
155
 
559
- # Run server
560
- FILE_PROXY_TOKEN=dev ALLOWED_BASE_PATHS=/tmp bun run src/index.ts
156
+ ```ts
157
+ import { uploads } from "@valentinkolb/filegate/utils";
561
158
 
562
- # Run tests
563
- bun run test:unit
564
- bun run test:integration:run
159
+ const checksum = await uploads.checksum.sha256(file);
565
160
  ```
566
161
 
567
- ## License
162
+ ## Documentation
568
163
 
569
- MIT
164
+ - Project README: https://github.com/ValentinKolb/filegate#readme
165
+ - TypeScript guide: https://github.com/ValentinKolb/filegate/blob/main/docs/ts-client.md
166
+ - HTTP routes: https://github.com/ValentinKolb/filegate/blob/main/docs/http-routes.md