@valentinkolb/filegate 0.0.1
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/LICENSE +21 -0
- package/README.md +429 -0
- package/package.json +56 -0
- package/src/client.ts +354 -0
- package/src/config.ts +37 -0
- package/src/handlers/files.ts +423 -0
- package/src/handlers/search.ts +119 -0
- package/src/handlers/upload.ts +332 -0
- package/src/index.ts +83 -0
- package/src/lib/openapi.ts +47 -0
- package/src/lib/ownership.ts +64 -0
- package/src/lib/path.ts +103 -0
- package/src/lib/response.ts +10 -0
- package/src/lib/validator.ts +21 -0
- package/src/schemas.ts +170 -0
- package/src/utils.ts +282 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Valentin Kolb
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# Filegate
|
|
2
|
+
|
|
3
|
+
A secure, high-performance file proxy server built with Bun + Hono + Zod.
|
|
4
|
+
|
|
5
|
+
Features:
|
|
6
|
+
- Streaming uploads/downloads
|
|
7
|
+
- Directory download as ZIP
|
|
8
|
+
- Resumable chunked uploads with SHA-256 verification
|
|
9
|
+
- Glob-based file search
|
|
10
|
+
- Type-safe client API with config objects
|
|
11
|
+
- Browser-compatible utils for chunked uploads
|
|
12
|
+
- OpenAPI documentation with Scalar UI
|
|
13
|
+
- LLM-friendly markdown docs at `/llms.txt`
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
export FILE_PROXY_TOKEN=your-secret-token
|
|
19
|
+
export ALLOWED_BASE_PATHS=/export/homes,/export/groups
|
|
20
|
+
|
|
21
|
+
bun run src/index.ts
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Documentation
|
|
25
|
+
|
|
26
|
+
- **Scalar UI:** http://localhost:4000/docs
|
|
27
|
+
- **OpenAPI JSON:** http://localhost:4000/openapi.json
|
|
28
|
+
- **LLM Markdown:** http://localhost:4000/llms.txt
|
|
29
|
+
|
|
30
|
+
## Configuration
|
|
31
|
+
|
|
32
|
+
| Variable | Required | Default | Description |
|
|
33
|
+
|----------|----------|---------|-------------|
|
|
34
|
+
| `FILE_PROXY_TOKEN` | Yes | - | Bearer token for authentication |
|
|
35
|
+
| `ALLOWED_BASE_PATHS` | Yes | - | Comma-separated allowed paths |
|
|
36
|
+
| `REDIS_URL` | No | localhost:6379 | Redis connection URL (for chunked uploads) |
|
|
37
|
+
| `PORT` | No | 4000 | Server port |
|
|
38
|
+
|
|
39
|
+
**Size Limits:**
|
|
40
|
+
|
|
41
|
+
| Variable | Default | Description |
|
|
42
|
+
|----------|---------|-------------|
|
|
43
|
+
| `MAX_UPLOAD_MB` | 500 | Maximum file size for uploads (simple + chunked) |
|
|
44
|
+
| `MAX_DOWNLOAD_MB` | 5000 | Maximum file/directory size for downloads |
|
|
45
|
+
| `MAX_CHUNK_SIZE_MB` | 50 | Maximum chunk size (server rejects larger chunks) |
|
|
46
|
+
|
|
47
|
+
**Search:**
|
|
48
|
+
|
|
49
|
+
| Variable | Default | Description |
|
|
50
|
+
|----------|---------|-------------|
|
|
51
|
+
| `SEARCH_MAX_RESULTS` | 100 | Max files returned by search |
|
|
52
|
+
| `SEARCH_MAX_RECURSIVE_WILDCARDS` | 10 | Max `**` wildcards allowed in glob patterns (prevents DoS) |
|
|
53
|
+
|
|
54
|
+
**Other:**
|
|
55
|
+
|
|
56
|
+
| Variable | Default | Description |
|
|
57
|
+
|----------|---------|-------------|
|
|
58
|
+
| `UPLOAD_EXPIRY_HOURS` | 24 | Chunked upload expiry (resets on each chunk) |
|
|
59
|
+
| `DISK_CLEANUP_INTERVAL_HOURS` | 6 | Interval to clean orphaned chunk files |
|
|
60
|
+
| `DEV_UID_OVERRIDE` | - | Override file ownership UID (dev mode only) |
|
|
61
|
+
| `DEV_GID_OVERRIDE` | - | Override file ownership GID (dev mode only) |
|
|
62
|
+
|
|
63
|
+
## API Endpoints
|
|
64
|
+
|
|
65
|
+
All `/files/*` endpoints require `Authorization: Bearer <token>`.
|
|
66
|
+
|
|
67
|
+
| Method | Path | Description |
|
|
68
|
+
|--------|------|-------------|
|
|
69
|
+
| GET | `/health` | Health check (public) |
|
|
70
|
+
| GET | `/docs` | Scalar API docs (public) |
|
|
71
|
+
| GET | `/openapi.json` | OpenAPI spec (public) |
|
|
72
|
+
| GET | `/llms.txt` | LLM-friendly docs (public) |
|
|
73
|
+
| GET | `/files/info` | File/directory info |
|
|
74
|
+
| GET | `/files/content` | Download file or directory (ZIP) |
|
|
75
|
+
| PUT | `/files/content` | Upload file |
|
|
76
|
+
| POST | `/files/mkdir` | Create directory |
|
|
77
|
+
| DELETE | `/files/delete` | Delete file/directory |
|
|
78
|
+
| POST | `/files/move` | Move (same basepath) |
|
|
79
|
+
| POST | `/files/copy` | Copy (same basepath) |
|
|
80
|
+
| GET | `/files/search` | Glob search |
|
|
81
|
+
| POST | `/files/upload/start` | Start/resume chunked upload |
|
|
82
|
+
| POST | `/files/upload/chunk` | Upload chunk |
|
|
83
|
+
|
|
84
|
+
## Examples
|
|
85
|
+
|
|
86
|
+
### Get Directory Info
|
|
87
|
+
```bash
|
|
88
|
+
curl -H "Authorization: Bearer $TOKEN" \
|
|
89
|
+
"http://localhost:4000/files/info?path=/export/homes/alice&showHidden=false"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Upload File
|
|
93
|
+
```bash
|
|
94
|
+
curl -X PUT \
|
|
95
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
96
|
+
-H "X-File-Path: /export/homes/alice" \
|
|
97
|
+
-H "X-File-Name: report.pdf" \
|
|
98
|
+
-H "X-Owner-UID: 1000" \
|
|
99
|
+
-H "X-Owner-GID: 1000" \
|
|
100
|
+
-H "X-File-Mode: 600" \
|
|
101
|
+
--data-binary @report.pdf \
|
|
102
|
+
"http://localhost:4000/files/content"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Search Files
|
|
106
|
+
```bash
|
|
107
|
+
curl -H "Authorization: Bearer $TOKEN" \
|
|
108
|
+
"http://localhost:4000/files/search?paths=/export/homes/alice,/export/groups/team&pattern=**/*.pdf"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Chunked Upload
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# 1. Start upload
|
|
115
|
+
curl -X POST \
|
|
116
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
117
|
+
-H "Content-Type: application/json" \
|
|
118
|
+
-d '{
|
|
119
|
+
"path": "/export/homes/alice",
|
|
120
|
+
"filename": "large.zip",
|
|
121
|
+
"size": 104857600,
|
|
122
|
+
"checksum": "sha256:abc123...",
|
|
123
|
+
"chunkSize": 10485760
|
|
124
|
+
}' \
|
|
125
|
+
"http://localhost:4000/files/upload/start"
|
|
126
|
+
|
|
127
|
+
# 2. Upload chunks
|
|
128
|
+
curl -X POST \
|
|
129
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
130
|
+
-H "X-Upload-Id: <uploadId>" \
|
|
131
|
+
-H "X-Chunk-Index: 0" \
|
|
132
|
+
-H "X-Chunk-Checksum: sha256:def456..." \
|
|
133
|
+
--data-binary @chunk0.bin \
|
|
134
|
+
"http://localhost:4000/files/upload/chunk"
|
|
135
|
+
|
|
136
|
+
# Repeat for all chunks - auto-completes on last chunk
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Client API
|
|
140
|
+
|
|
141
|
+
Type-safe client for server-side usage. All methods use config objects for better readability.
|
|
142
|
+
|
|
143
|
+
### Installation
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { Filegate } from "@valentinkolb/filegate/client";
|
|
147
|
+
import { chunks, formatBytes } from "@valentinkolb/filegate/utils";
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Default Instance (via env vars)
|
|
151
|
+
|
|
152
|
+
Set `FILEGATE_URL` and `FILEGATE_TOKEN`, then import the pre-configured instance:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { filegate } from "@valentinkolb/filegate/client";
|
|
156
|
+
|
|
157
|
+
const info = await filegate.info({ path: "/export/homes/alice" });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Custom Instance
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { Filegate } from "@valentinkolb/filegate/client";
|
|
164
|
+
|
|
165
|
+
const client = new Filegate({
|
|
166
|
+
url: "http://localhost:4000",
|
|
167
|
+
token: "your-token",
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Client Methods
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// Get file/directory info
|
|
175
|
+
const info = await client.info({ path: "/export/homes/alice", showHidden: true });
|
|
176
|
+
|
|
177
|
+
// Download file (returns Response with streaming body)
|
|
178
|
+
const file = await client.download({ path: "/export/homes/alice/report.pdf" });
|
|
179
|
+
if (file.ok) {
|
|
180
|
+
const text = await file.data.text();
|
|
181
|
+
const buffer = await file.data.arrayBuffer();
|
|
182
|
+
// Or stream directly: file.data.body
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Download directory as ZIP
|
|
186
|
+
const zip = await client.download({ path: "/export/homes/alice/documents" });
|
|
187
|
+
|
|
188
|
+
// Upload file (simple)
|
|
189
|
+
await client.upload.single({
|
|
190
|
+
path: "/export/homes/alice",
|
|
191
|
+
filename: "report.pdf",
|
|
192
|
+
data: fileData,
|
|
193
|
+
uid: 1000,
|
|
194
|
+
gid: 1000,
|
|
195
|
+
mode: "644",
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Upload file (chunked) - for large files
|
|
199
|
+
const startResult = await client.upload.chunked.start({
|
|
200
|
+
path: "/export/homes/alice",
|
|
201
|
+
filename: "large.zip",
|
|
202
|
+
size: file.size,
|
|
203
|
+
checksum: "sha256:...",
|
|
204
|
+
chunkSize: 5 * 1024 * 1024,
|
|
205
|
+
uid: 1000,
|
|
206
|
+
gid: 1000,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Send chunks
|
|
210
|
+
await client.upload.chunked.send({
|
|
211
|
+
uploadId: startResult.data.uploadId,
|
|
212
|
+
index: 0,
|
|
213
|
+
data: chunkData,
|
|
214
|
+
checksum: "sha256:...",
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Create directory
|
|
218
|
+
await client.mkdir({ path: "/export/homes/alice/new-folder", mode: "750" });
|
|
219
|
+
|
|
220
|
+
// Move/copy (within same basepath)
|
|
221
|
+
await client.move({ from: "/export/homes/alice/old.txt", to: "/export/homes/alice/new.txt" });
|
|
222
|
+
await client.copy({ from: "/export/homes/alice/file.txt", to: "/export/homes/alice/backup.txt" });
|
|
223
|
+
|
|
224
|
+
// Delete
|
|
225
|
+
await client.delete({ path: "/export/homes/alice/trash" });
|
|
226
|
+
|
|
227
|
+
// Search with glob patterns
|
|
228
|
+
const results = await client.glob({
|
|
229
|
+
paths: ["/export/homes/alice", "/export/groups/team"],
|
|
230
|
+
pattern: "**/*.pdf",
|
|
231
|
+
showHidden: false,
|
|
232
|
+
limit: 100,
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Browser Utils
|
|
237
|
+
|
|
238
|
+
Browser-compatible utilities for chunked uploads. Framework-agnostic with reactive state management.
|
|
239
|
+
|
|
240
|
+
### Basic Usage
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import { chunks, formatBytes } from "@valentinkolb/filegate/utils";
|
|
244
|
+
|
|
245
|
+
// Prepare upload (calculates checksum and chunk info)
|
|
246
|
+
const upload = await chunks.prepare({ file, chunkSize: 5 * 1024 * 1024 });
|
|
247
|
+
|
|
248
|
+
// Access properties
|
|
249
|
+
upload.file // Original File/Blob
|
|
250
|
+
upload.fileSize // Total size in bytes
|
|
251
|
+
upload.chunkSize // Chunk size in bytes
|
|
252
|
+
upload.totalChunks // Number of chunks
|
|
253
|
+
upload.checksum // "sha256:..." of entire file
|
|
254
|
+
|
|
255
|
+
// Format bytes for display
|
|
256
|
+
formatBytes({ bytes: upload.fileSize }); // "52.43 MB"
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### State Management
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// Subscribe to state changes (framework-agnostic)
|
|
263
|
+
const unsubscribe = upload.subscribe((state) => {
|
|
264
|
+
console.log(`${state.percent}% - ${state.status}`);
|
|
265
|
+
// state: { uploaded: number, total: number, percent: number, status: "pending" | "uploading" | "completed" | "error" }
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Mark chunk as completed
|
|
269
|
+
upload.complete({ index: 0 });
|
|
270
|
+
|
|
271
|
+
// Reset state
|
|
272
|
+
upload.reset();
|
|
273
|
+
|
|
274
|
+
// Unsubscribe when done
|
|
275
|
+
unsubscribe();
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Chunk Access
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
// Get specific chunk (sync, returns Blob)
|
|
282
|
+
const chunk = upload.get({ index: 0 });
|
|
283
|
+
|
|
284
|
+
// Calculate chunk checksum
|
|
285
|
+
const hash = await upload.hash({ data: chunk });
|
|
286
|
+
|
|
287
|
+
// Iterate over all chunks
|
|
288
|
+
for await (const { index, data, total } of upload) {
|
|
289
|
+
console.log(`Chunk ${index + 1}/${total}`);
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Upload Helpers
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
// Send single chunk with retry
|
|
297
|
+
await upload.send({
|
|
298
|
+
index: 0,
|
|
299
|
+
retries: 3,
|
|
300
|
+
fn: async ({ index, data }) => {
|
|
301
|
+
await fetch("/api/upload/chunk", {
|
|
302
|
+
method: "POST",
|
|
303
|
+
headers: { "X-Chunk-Index": String(index) },
|
|
304
|
+
body: data,
|
|
305
|
+
});
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// Send all chunks (with skip for resume, concurrency, retries)
|
|
310
|
+
await upload.sendAll({
|
|
311
|
+
skip: [0, 1], // Already uploaded chunks (from resume)
|
|
312
|
+
retries: 3,
|
|
313
|
+
concurrency: 3, // Parallel uploads
|
|
314
|
+
fn: async ({ index, data }) => {
|
|
315
|
+
await fetch("/api/upload/chunk", { ... });
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Complete Example (Browser)
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
import { chunks } from "@valentinkolb/filegate/utils";
|
|
324
|
+
|
|
325
|
+
async function uploadFile(file: File, targetPath: string, onProgress?: (state) => void) {
|
|
326
|
+
const upload = await chunks.prepare({ file, chunkSize: 5 * 1024 * 1024 });
|
|
327
|
+
|
|
328
|
+
if (onProgress) upload.subscribe(onProgress);
|
|
329
|
+
|
|
330
|
+
// 1. Start/Resume upload
|
|
331
|
+
const { uploadId, uploadedChunks, completed } = await fetch("/api/upload/start", {
|
|
332
|
+
method: "POST",
|
|
333
|
+
headers: { "Content-Type": "application/json" },
|
|
334
|
+
body: JSON.stringify({
|
|
335
|
+
path: targetPath,
|
|
336
|
+
filename: file.name,
|
|
337
|
+
size: upload.fileSize,
|
|
338
|
+
checksum: upload.checksum,
|
|
339
|
+
chunkSize: upload.chunkSize,
|
|
340
|
+
}),
|
|
341
|
+
}).then(r => r.json());
|
|
342
|
+
|
|
343
|
+
if (completed) return; // Already done
|
|
344
|
+
|
|
345
|
+
// 2. Upload all chunks (skipping already uploaded)
|
|
346
|
+
await upload.sendAll({
|
|
347
|
+
skip: uploadedChunks,
|
|
348
|
+
retries: 3,
|
|
349
|
+
fn: async ({ index, data }) => {
|
|
350
|
+
await fetch("/api/upload/chunk", {
|
|
351
|
+
method: "POST",
|
|
352
|
+
headers: {
|
|
353
|
+
"X-Upload-Id": uploadId,
|
|
354
|
+
"X-Chunk-Index": String(index),
|
|
355
|
+
"X-Chunk-Checksum": await upload.hash({ data }),
|
|
356
|
+
},
|
|
357
|
+
body: data,
|
|
358
|
+
});
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Usage with React
|
|
364
|
+
function UploadButton() {
|
|
365
|
+
const [progress, setProgress] = useState({ percent: 0, status: "pending" });
|
|
366
|
+
|
|
367
|
+
const handleUpload = async (file: File) => {
|
|
368
|
+
await uploadFile(file, "/documents", setProgress);
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
return <div>{progress.percent}% - {progress.status}</div>;
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Streaming Proxy (Server-Side)
|
|
376
|
+
|
|
377
|
+
The download response streams directly - perfect for proxying without buffering:
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
// In your proxy server (e.g., with Hono, Express, etc.)
|
|
381
|
+
app.get("/api/files/download", async (c) => {
|
|
382
|
+
const path = c.req.query("path");
|
|
383
|
+
|
|
384
|
+
// Get streaming response from Filegate
|
|
385
|
+
const response = await client.download({ path });
|
|
386
|
+
|
|
387
|
+
if (!response.ok) {
|
|
388
|
+
return c.json({ error: response.error }, response.status);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Stream directly to client - no buffering!
|
|
392
|
+
return new Response(response.data.body, {
|
|
393
|
+
headers: {
|
|
394
|
+
"Content-Type": response.data.headers.get("Content-Type") || "application/octet-stream",
|
|
395
|
+
"Content-Disposition": response.data.headers.get("Content-Disposition") || "",
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## Security
|
|
402
|
+
|
|
403
|
+
- Path validation with symlink protection
|
|
404
|
+
- Base path escape prevention
|
|
405
|
+
- Same-basepath enforcement for move/copy
|
|
406
|
+
- SHA-256 checksum verification
|
|
407
|
+
- Configurable upload/download size limits
|
|
408
|
+
- Glob pattern limits (max length 500 chars, configurable recursive wildcard limit)
|
|
409
|
+
- Security headers (X-Frame-Options, X-Content-Type-Options, etc.)
|
|
410
|
+
|
|
411
|
+
## Testing
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
# Run unit tests
|
|
415
|
+
bun run test:unit
|
|
416
|
+
|
|
417
|
+
# Run integration tests (requires Docker)
|
|
418
|
+
bun run test:integration:run
|
|
419
|
+
|
|
420
|
+
# Run all tests
|
|
421
|
+
bun run test:all
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
## Tech Stack
|
|
425
|
+
|
|
426
|
+
- **Runtime:** Bun
|
|
427
|
+
- **Framework:** Hono
|
|
428
|
+
- **Validation:** Zod
|
|
429
|
+
- **Docs:** hono-openapi + Scalar
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@valentinkolb/filegate",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Secure, high-performance file proxy server with streaming uploads, chunked uploads, and TAR downloads",
|
|
5
|
+
"module": "index.ts",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/valentinkolb/filegate.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"file-server",
|
|
14
|
+
"file-proxy",
|
|
15
|
+
"chunked-upload",
|
|
16
|
+
"streaming",
|
|
17
|
+
"bun",
|
|
18
|
+
"hono"
|
|
19
|
+
],
|
|
20
|
+
"files": [
|
|
21
|
+
"src",
|
|
22
|
+
"package.json",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"test": "bun test --preload ./tests/setup.ts tests/lib tests/schemas.test.ts tests/config.test.ts tests/utils.test.ts",
|
|
27
|
+
"test:watch": "bun test --preload ./tests/setup.ts --watch",
|
|
28
|
+
"test:unit": "bun test --preload ./tests/setup.ts tests/lib tests/schemas.test.ts tests/config.test.ts tests/utils.test.ts",
|
|
29
|
+
"test:integration": "bun test tests/integration",
|
|
30
|
+
"test:integration:up": "docker compose -f compose.test.yml up -d --build --wait",
|
|
31
|
+
"test:integration:down": "docker compose -f compose.test.yml down -v",
|
|
32
|
+
"test:integration:run": "bun run test:integration:up && bun run test:integration && bun run test:integration:down",
|
|
33
|
+
"test:all": "bun run test:unit && bun run test:integration:run"
|
|
34
|
+
},
|
|
35
|
+
"exports": {
|
|
36
|
+
".": "./src/index.ts",
|
|
37
|
+
"./client": "./src/client.ts",
|
|
38
|
+
"./utils": "./src/utils.ts",
|
|
39
|
+
"./schemas": "./src/schemas.ts"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/bun": "latest"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"typescript": "^5"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@hono/standard-validator": "^0.2.2",
|
|
49
|
+
"@scalar/hono-api-reference": "^0.9.37",
|
|
50
|
+
"@scalar/openapi-to-markdown": "^0.3.31",
|
|
51
|
+
"hono": "^4.11.7",
|
|
52
|
+
"hono-openapi": "^1.2.0",
|
|
53
|
+
"sanitize-filename": "^1.6.3",
|
|
54
|
+
"zod": "^4.3.6"
|
|
55
|
+
}
|
|
56
|
+
}
|