@kadi.build/file-sharing 1.0.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 +477 -0
- package/package.json +60 -0
- package/src/DownloadMonitor.js +199 -0
- package/src/EventNotifier.js +178 -0
- package/src/FileSharingServer.js +598 -0
- package/src/HttpServerProvider.js +538 -0
- package/src/MonitoringDashboard.js +181 -0
- package/src/S3Server.js +984 -0
- package/src/ShutdownManager.js +135 -0
- package/src/index.js +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
# @kadi.build/file-sharing
|
|
2
|
+
|
|
3
|
+
File sharing service with tunneling, authentication, and a local S3-compatible interface.
|
|
4
|
+
Integrates [`@kadi.build/file-manager`](https://www.npmjs.com/package/@kadi.build/file-manager) and [`@kadi.build/tunnel-services`](https://www.npmjs.com/package/@kadi.build/tunnel-services) into one turnkey solution.
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
|
|
8
|
+
- **HTTP File Server** — Static file serving with directory listing, range requests, CORS, and multi-scheme authentication
|
|
9
|
+
- **Local S3-Compatible API** — Emulates AWS S3 endpoints locally so you can use `@aws-sdk/client-s3` against your local filesystem
|
|
10
|
+
- **Tunnel Integration** — Expose your local server publicly via KĀDI (default), ngrok, serveo, localtunnel, or pinggy
|
|
11
|
+
- **Authentication** — Basic Auth, Bearer Token, and API Key (header / query param) for HTTP; AWS SigV4 / SigV2 / Bearer for S3
|
|
12
|
+
- **Secrets Management** — Automatic `.env` loading (walks up parent directories for monorepo support) + environment variables + explicit config
|
|
13
|
+
- **Download Monitoring** — Track active downloads, progress, speed, and completion statistics
|
|
14
|
+
- **Graceful Shutdown** — Priority-ordered shutdown with timeout and force-kill
|
|
15
|
+
- **Webhook Notifications** — Event-driven notifications to external endpoints
|
|
16
|
+
- **Monitoring Dashboard** — Console-based real-time dashboard
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @kadi.build/file-sharing
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### Basic File Sharing
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
import { FileSharingServer } from '@kadi.build/file-sharing';
|
|
30
|
+
|
|
31
|
+
const server = new FileSharingServer({
|
|
32
|
+
staticDir: '/path/to/files',
|
|
33
|
+
port: 3000
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
await server.start();
|
|
37
|
+
console.log(`Serving files at ${server.getInfo().localUrl}`);
|
|
38
|
+
await server.stop();
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### With Public Tunnel (KĀDI)
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
const server = new FileSharingServer({
|
|
45
|
+
staticDir: '/path/to/files',
|
|
46
|
+
port: 3000,
|
|
47
|
+
tunnel: {
|
|
48
|
+
enabled: true
|
|
49
|
+
// KĀDI is the default — reads token from KADI_TUNNEL_TOKEN env var or .env
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await server.start();
|
|
54
|
+
console.log(`Public URL: ${server.tunnelUrl}`);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### With Authentication
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
const server = new FileSharingServer({
|
|
61
|
+
staticDir: '/path/to/files',
|
|
62
|
+
port: 3000,
|
|
63
|
+
auth: { apiKey: 'my-secret-key' }, // or { username: 'admin', password: 'secret' }
|
|
64
|
+
tunnel: { enabled: true }
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await server.start();
|
|
68
|
+
// Clients must send X-API-Key header, Bearer token, or ?apiKey= query param
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### With Local S3 API
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
import { FileSharingServer } from '@kadi.build/file-sharing';
|
|
75
|
+
import { S3Client, ListObjectsV2Command, PutObjectCommand } from '@aws-sdk/client-s3';
|
|
76
|
+
|
|
77
|
+
const server = new FileSharingServer({
|
|
78
|
+
staticDir: '/path/to/files',
|
|
79
|
+
port: 3000,
|
|
80
|
+
enableS3: true,
|
|
81
|
+
s3Port: 9000
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await server.start();
|
|
85
|
+
|
|
86
|
+
// Use the standard AWS SDK against your LOCAL filesystem
|
|
87
|
+
const s3 = new S3Client({
|
|
88
|
+
endpoint: 'http://localhost:9000',
|
|
89
|
+
region: 'us-east-1',
|
|
90
|
+
credentials: { accessKeyId: 'minioadmin', secretAccessKey: 'minioadmin' },
|
|
91
|
+
forcePathStyle: true
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const response = await s3.send(new ListObjectsV2Command({ Bucket: 'local' }));
|
|
95
|
+
console.log(response.Contents);
|
|
96
|
+
|
|
97
|
+
await s3.send(new PutObjectCommand({
|
|
98
|
+
Bucket: 'local',
|
|
99
|
+
Key: 'uploaded.txt',
|
|
100
|
+
Body: 'Hello from S3!'
|
|
101
|
+
}));
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Quick Share (One-Liner)
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
import { createQuickShare } from '@kadi.build/file-sharing';
|
|
108
|
+
|
|
109
|
+
const { server, localUrl, publicUrl } = await createQuickShare('./my-files', {
|
|
110
|
+
tunnel: true,
|
|
111
|
+
auth: { apiKey: 'share-key-123' }
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
console.log(`Share this link: ${publicUrl}`);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Secrets & Authentication
|
|
120
|
+
|
|
121
|
+
### How Secrets Are Loaded
|
|
122
|
+
|
|
123
|
+
Secrets are resolved in **priority order** (first match wins):
|
|
124
|
+
|
|
125
|
+
1. **Explicit constructor config** — values you pass directly
|
|
126
|
+
2. **Environment variables** — `process.env.*`
|
|
127
|
+
3. **`.env` file** — automatically found by walking up parent directories from `process.cwd()`
|
|
128
|
+
|
|
129
|
+
The `.env` loader supports monorepo layouts: if your `.env` is at the workspace root and the package runs from `packages/file-sharing/`, it will find it automatically.
|
|
130
|
+
|
|
131
|
+
### Environment Variables
|
|
132
|
+
|
|
133
|
+
| Variable | Description | Default |
|
|
134
|
+
|----------|-------------|---------|
|
|
135
|
+
| **Tunnel — KĀDI** | | |
|
|
136
|
+
| `KADI_TUNNEL_TOKEN` | KĀDI authentication token | *(required for KĀDI)* |
|
|
137
|
+
| `KADI_TUNNEL_SERVER` | KĀDI broker address | `broker.kadi.build` |
|
|
138
|
+
| `KADI_TUNNEL_DOMAIN` | Tunnel domain | `tunnel.kadi.build` |
|
|
139
|
+
| `KADI_TUNNEL_PORT` | KĀDI frps server port | `7000` |
|
|
140
|
+
| `KADI_TUNNEL_SSH_PORT` | KĀDI SSH gateway port | `2200` |
|
|
141
|
+
| `KADI_TUNNEL_MODE` | Connection mode: `ssh`, `frpc`, or `auto` | `auto` |
|
|
142
|
+
| `KADI_AGENT_ID` | Agent identifier for proxy naming | `kadi` |
|
|
143
|
+
| **Tunnel — Ngrok** | | |
|
|
144
|
+
| `NGROK_AUTHTOKEN` | Ngrok auth token (also accepts `NGROK_AUTH_TOKEN`) | — |
|
|
145
|
+
| **HTTP Authentication** | | |
|
|
146
|
+
| `KADI_AUTH_API_KEY` | API key for Bearer / X-API-Key auth | — |
|
|
147
|
+
| `KADI_AUTH_USERNAME` | HTTP Basic auth username | — |
|
|
148
|
+
| `KADI_AUTH_PASSWORD` | HTTP Basic auth password | — |
|
|
149
|
+
| **S3 Authentication** | | |
|
|
150
|
+
| `KADI_S3_ACCESS_KEY` | S3 access key ID | `minioadmin` |
|
|
151
|
+
| `KADI_S3_SECRET_KEY` | S3 secret access key | `minioadmin` |
|
|
152
|
+
|
|
153
|
+
### `.env` File Example
|
|
154
|
+
|
|
155
|
+
```env
|
|
156
|
+
# Tunnel credentials
|
|
157
|
+
KADI_TUNNEL_TOKEN=your-kadi-token-here
|
|
158
|
+
KADI_TUNNEL_SERVER=broker.kadi.build
|
|
159
|
+
KADI_TUNNEL_DOMAIN=tunnel.kadi.build
|
|
160
|
+
KADI_TUNNEL_PORT=7000
|
|
161
|
+
KADI_TUNNEL_SSH_PORT=2200
|
|
162
|
+
KADI_TUNNEL_MODE=ssh
|
|
163
|
+
KADI_AGENT_ID=my-agent
|
|
164
|
+
|
|
165
|
+
# Optional: Ngrok (fallback)
|
|
166
|
+
NGROK_AUTHTOKEN=your-ngrok-token
|
|
167
|
+
|
|
168
|
+
# HTTP auth (optional — protects file downloads)
|
|
169
|
+
KADI_AUTH_API_KEY=my-secret-api-key
|
|
170
|
+
|
|
171
|
+
# S3 credentials (optional — default minioadmin/minioadmin)
|
|
172
|
+
KADI_S3_ACCESS_KEY=my-access-key
|
|
173
|
+
KADI_S3_SECRET_KEY=my-secret-key
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### HTTP Authentication Schemes
|
|
177
|
+
|
|
178
|
+
When `auth` is configured (via config, env vars, or `.env`), the HTTP server supports three authentication schemes:
|
|
179
|
+
|
|
180
|
+
| Scheme | How to Authenticate |
|
|
181
|
+
|--------|---------------------|
|
|
182
|
+
| **Basic Auth** | `Authorization: Basic <base64(user:pass)>` |
|
|
183
|
+
| **Bearer Token** | `Authorization: Bearer <apiKey>` |
|
|
184
|
+
| **API Key** | `X-API-Key: <apiKey>` header or `?apiKey=<key>` query param |
|
|
185
|
+
|
|
186
|
+
If `auth.apiKey` is set, all three token-based methods are accepted.
|
|
187
|
+
If `auth.username` + `auth.password` are set, only Basic Auth is accepted.
|
|
188
|
+
|
|
189
|
+
### S3 Authentication
|
|
190
|
+
|
|
191
|
+
When custom S3 credentials are set (anything other than the default `minioadmin`/`minioadmin`), the S3 server validates requests:
|
|
192
|
+
|
|
193
|
+
| Method | How It Works |
|
|
194
|
+
|--------|-------------|
|
|
195
|
+
| **AWS SigV4** | Standard `Authorization: AWS4-HMAC-SHA256 Credential=<accessKeyId>/...` |
|
|
196
|
+
| **AWS SigV2** | Legacy `Authorization: AWS <accessKeyId>:signature` |
|
|
197
|
+
| **Bearer** | Convenience `Authorization: Bearer <accessKeyId>` |
|
|
198
|
+
| **Pre-signed URL** | `?X-Amz-Credential=<accessKeyId>/...` query param |
|
|
199
|
+
|
|
200
|
+
> With default credentials (`minioadmin`/`minioadmin`), auth is skipped for backward compatibility unless you set `enforceAuth: true` in the S3 config.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## API Reference
|
|
205
|
+
|
|
206
|
+
### FileSharingServer
|
|
207
|
+
|
|
208
|
+
Main orchestrating class.
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
const server = new FileSharingServer({
|
|
212
|
+
// File serving
|
|
213
|
+
staticDir: process.cwd(), // Directory to serve
|
|
214
|
+
port: 3000, // HTTP port
|
|
215
|
+
host: '0.0.0.0', // Bind address
|
|
216
|
+
enableDirectoryListing: true, // Show directory listing
|
|
217
|
+
cors: true, // Enable CORS
|
|
218
|
+
|
|
219
|
+
// Authentication (or use env vars / .env)
|
|
220
|
+
auth: null, // { apiKey: 'key' } or { username: 'u', password: 'p' }
|
|
221
|
+
|
|
222
|
+
// S3 API
|
|
223
|
+
enableS3: false, // Enable S3-compatible API
|
|
224
|
+
s3Port: 9000, // S3 API port
|
|
225
|
+
s3Config: { // Passed to S3Server
|
|
226
|
+
accessKeyId: 'minioadmin',
|
|
227
|
+
secretAccessKey: 'minioadmin',
|
|
228
|
+
bucketName: 'local',
|
|
229
|
+
region: 'us-east-1',
|
|
230
|
+
enforceAuth: false // Force auth even with default creds
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
// Tunnel
|
|
234
|
+
tunnel: {
|
|
235
|
+
enabled: false, // Start tunnel on server.start()
|
|
236
|
+
service: 'kadi', // 'kadi' | 'ngrok' | 'serveo' | 'localtunnel' | 'pinggy'
|
|
237
|
+
autoFallback: true, // Fall back to other services on failure
|
|
238
|
+
autoReconnect: true, // Auto-reconnect on tunnel drop
|
|
239
|
+
reconnectDelay: 5000, // ms between reconnect attempts
|
|
240
|
+
// KĀDI-specific (or use env vars / .env)
|
|
241
|
+
kadiToken: undefined,
|
|
242
|
+
kadiServer: undefined,
|
|
243
|
+
kadiDomain: undefined,
|
|
244
|
+
kadiPort: undefined, // frps server port (NOT the local port)
|
|
245
|
+
kadiSshPort: undefined,
|
|
246
|
+
kadiMode: undefined, // 'ssh' | 'frpc' | 'auto'
|
|
247
|
+
kadiAgentId: undefined,
|
|
248
|
+
// Ngrok-specific
|
|
249
|
+
ngrokAuthToken: undefined,
|
|
250
|
+
// Advanced
|
|
251
|
+
managerOptions: {} // Extra options passed to TunnelManager
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
// Shutdown
|
|
255
|
+
shutdown: {
|
|
256
|
+
gracefulTimeout: 30000,
|
|
257
|
+
finishActiveDownloads: true,
|
|
258
|
+
forceKillTimeout: 60000
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
// Monitoring
|
|
262
|
+
monitoring: {
|
|
263
|
+
enabled: true,
|
|
264
|
+
dashboard: false, // Enable console dashboard
|
|
265
|
+
webhooks: [] // ['https://hooks.example.com/events']
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
#### Methods
|
|
271
|
+
|
|
272
|
+
| Method | Returns | Description |
|
|
273
|
+
|--------|---------|-------------|
|
|
274
|
+
| `start()` | `Promise<ServerInfo>` | Start HTTP server (+ S3 and tunnel if enabled) |
|
|
275
|
+
| `stop()` | `Promise<void>` | Gracefully stop all servers and tunnels |
|
|
276
|
+
| `enableTunnel(options?)` | `Promise<TunnelInfo>` | Enable public tunnel after start |
|
|
277
|
+
| `disableTunnel()` | `Promise<void>` | Close active tunnel |
|
|
278
|
+
| `enableS3(options?)` | `Promise<{endpoint, port}>` | Enable S3 API dynamically |
|
|
279
|
+
| `disableS3()` | `Promise<void>` | Disable S3 API |
|
|
280
|
+
| `getInfo()` | `ServerInfo` | Get server info, URLs, and tunnel status |
|
|
281
|
+
| `getStats()` | `DownloadStats` | Get download statistics |
|
|
282
|
+
| `listFiles(subPath?)` | `Promise<FileEntry[]>` | List files in served directory |
|
|
283
|
+
| `addWebhook(url, events?)` | `void` | Register a webhook endpoint |
|
|
284
|
+
| `removeWebhook(url)` | `void` | Remove a webhook |
|
|
285
|
+
|
|
286
|
+
#### Properties
|
|
287
|
+
|
|
288
|
+
| Property | Type | Description |
|
|
289
|
+
|----------|------|-------------|
|
|
290
|
+
| `isRunning` | `boolean` | Whether the server is running |
|
|
291
|
+
| `port` | `number` | Current HTTP port |
|
|
292
|
+
| `tunnelUrl` | `string \| null` | Current public tunnel URL |
|
|
293
|
+
| `staticDir` | `string` | Directory being served |
|
|
294
|
+
| `tunnel` | `object \| null` | Active tunnel info |
|
|
295
|
+
| `tunnelManager` | `TunnelManager` | Underlying tunnel manager |
|
|
296
|
+
|
|
297
|
+
#### Events
|
|
298
|
+
|
|
299
|
+
| Event | Payload | When |
|
|
300
|
+
|-------|---------|------|
|
|
301
|
+
| `started` | `ServerInfo` | Server fully started |
|
|
302
|
+
| `stopping` | — | Shutdown initiated |
|
|
303
|
+
| `stopped` | — | Server fully stopped |
|
|
304
|
+
| `download:start` | `{id, file, ...}` | File download begins |
|
|
305
|
+
| `download:complete` | `{id, file, duration, ...}` | Download finished |
|
|
306
|
+
| `download:error` | `{id, error}` | Download failed |
|
|
307
|
+
| `upload:complete` | `{file, size}` | S3 upload finished |
|
|
308
|
+
| `s3:started` | `{port, endpoint}` | S3 server started |
|
|
309
|
+
| `s3:get` | `{bucket, key}` | S3 GetObject |
|
|
310
|
+
| `s3:put` | `{bucket, key}` | S3 PutObject |
|
|
311
|
+
| `s3:delete` | `{bucket, key}` | S3 DeleteObject |
|
|
312
|
+
| `tunnel:created` | `TunnelInfo` | Tunnel established |
|
|
313
|
+
| `tunnel:closed` | — | Tunnel shut down |
|
|
314
|
+
| `tunnel:error` | `Error` | Tunnel failed (non-fatal) |
|
|
315
|
+
| `http:started` | `{port, host}` | HTTP server listening |
|
|
316
|
+
| `http:error` | `Error` | HTTP error |
|
|
317
|
+
|
|
318
|
+
### HttpServerProvider
|
|
319
|
+
|
|
320
|
+
Low-level HTTP file server with authentication.
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
import { HttpServerProvider } from '@kadi.build/file-sharing/http';
|
|
324
|
+
|
|
325
|
+
const httpServer = new HttpServerProvider({
|
|
326
|
+
port: 3000,
|
|
327
|
+
staticDir: './files',
|
|
328
|
+
enableDirectoryListing: true,
|
|
329
|
+
cors: true,
|
|
330
|
+
auth: { apiKey: 'my-key' } // or { username: 'u', password: 'p' }
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
await httpServer.start();
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### S3Server
|
|
337
|
+
|
|
338
|
+
Local S3-compatible API server. Files are stored **on disk**, not on AWS.
|
|
339
|
+
|
|
340
|
+
```javascript
|
|
341
|
+
import { S3Server } from '@kadi.build/file-sharing/s3';
|
|
342
|
+
|
|
343
|
+
const s3 = new S3Server({
|
|
344
|
+
port: 9000,
|
|
345
|
+
rootDir: './data',
|
|
346
|
+
bucketName: 'local',
|
|
347
|
+
region: 'us-east-1',
|
|
348
|
+
accessKeyId: 'my-key', // default: 'minioadmin'
|
|
349
|
+
secretAccessKey: 'my-secret', // default: 'minioadmin'
|
|
350
|
+
enforceAuth: true // validate even with default creds
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
await s3.start();
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Supported S3 Operations:**
|
|
357
|
+
|
|
358
|
+
- `ListBuckets`, `CreateBucket`, `DeleteBucket`, `HeadBucket`
|
|
359
|
+
- `ListObjects`, `ListObjectsV2`
|
|
360
|
+
- `GetObject` (with range requests)
|
|
361
|
+
- `PutObject`
|
|
362
|
+
- `DeleteObject`
|
|
363
|
+
- `HeadObject`
|
|
364
|
+
- `CreateMultipartUpload`, `UploadPart`, `CompleteMultipartUpload`
|
|
365
|
+
|
|
366
|
+
### createQuickShare
|
|
367
|
+
|
|
368
|
+
One-liner to share a directory with optional tunnel and auth.
|
|
369
|
+
|
|
370
|
+
```javascript
|
|
371
|
+
import { createQuickShare } from '@kadi.build/file-sharing';
|
|
372
|
+
|
|
373
|
+
const { server, localUrl, publicUrl } = await createQuickShare('./my-files', {
|
|
374
|
+
port: 3000, // HTTP port (default: 3000)
|
|
375
|
+
tunnel: true, // Enable tunnel (default: false)
|
|
376
|
+
tunnelService: 'kadi', // Tunnel service (default: 'kadi')
|
|
377
|
+
kadiToken: 'token', // Or set KADI_TUNNEL_TOKEN env
|
|
378
|
+
ngrokAuthToken: 'token', // Or set NGROK_AUTHTOKEN env
|
|
379
|
+
auth: { apiKey: 'share-key' }, // Protect downloads
|
|
380
|
+
tunnelOptions: {} // Extra TunnelManager options
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
console.log(`Local: ${localUrl}`);
|
|
384
|
+
console.log(`Public: ${publicUrl}`);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### DownloadMonitor
|
|
388
|
+
|
|
389
|
+
Track download progress and statistics.
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
import { DownloadMonitor } from '@kadi.build/file-sharing';
|
|
393
|
+
|
|
394
|
+
const monitor = new DownloadMonitor();
|
|
395
|
+
|
|
396
|
+
monitor.on('download:start', (dl) => console.log(`Started: ${dl.file}`));
|
|
397
|
+
monitor.on('download:progress', (dl) => console.log(`${dl.progress.toFixed(1)}%`));
|
|
398
|
+
monitor.on('download:complete', (dl) => console.log(`Done in ${dl.duration}ms`));
|
|
399
|
+
|
|
400
|
+
monitor.startDownload('id', { file: 'test.txt', totalSize: 1000 });
|
|
401
|
+
monitor.updateProgress('id', 500);
|
|
402
|
+
monitor.completeDownload('id');
|
|
403
|
+
|
|
404
|
+
console.log(monitor.getStats());
|
|
405
|
+
// { activeCount, completedCount, totalBytes, peakConcurrent, ... }
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### ShutdownManager
|
|
409
|
+
|
|
410
|
+
Graceful shutdown with priority-ordered callbacks.
|
|
411
|
+
|
|
412
|
+
```javascript
|
|
413
|
+
import { ShutdownManager } from '@kadi.build/file-sharing';
|
|
414
|
+
|
|
415
|
+
const sm = new ShutdownManager({ gracefulTimeout: 10000 });
|
|
416
|
+
|
|
417
|
+
sm.register(async () => console.log('Close DB'), 1); // priority 1 = first
|
|
418
|
+
sm.register(async () => console.log('Close HTTP'), 10); // priority 10 = later
|
|
419
|
+
|
|
420
|
+
await sm.shutdown();
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Sub-path Exports
|
|
426
|
+
|
|
427
|
+
```javascript
|
|
428
|
+
// Main export — all classes and utilities
|
|
429
|
+
import { FileSharingServer, createQuickShare } from '@kadi.build/file-sharing';
|
|
430
|
+
|
|
431
|
+
// S3 server only
|
|
432
|
+
import { S3Server } from '@kadi.build/file-sharing/s3';
|
|
433
|
+
|
|
434
|
+
// HTTP server only
|
|
435
|
+
import { HttpServerProvider } from '@kadi.build/file-sharing/http';
|
|
436
|
+
|
|
437
|
+
// Re-exports from dependencies (convenience)
|
|
438
|
+
import { FileManager, createFileManager, TunnelManager } from '@kadi.build/file-sharing';
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Tunnel Services
|
|
444
|
+
|
|
445
|
+
KĀDI is the default and recommended tunnel service. It supports two connection modes:
|
|
446
|
+
|
|
447
|
+
| Mode | How It Works | Requirements |
|
|
448
|
+
|------|-------------|--------------|
|
|
449
|
+
| **SSH** (default) | `ssh -R` through KĀDI's SSH gateway — zero-dependency | SSH client in `$PATH` |
|
|
450
|
+
| **frpc** | Uses the frp client binary for higher reliability | `frpc` binary in `$PATH` |
|
|
451
|
+
| **auto** | Prefers `frpc` if available, falls back to SSH | — |
|
|
452
|
+
|
|
453
|
+
Set `KADI_TUNNEL_MODE=ssh` (or `frpc`) in your `.env` to force a specific mode.
|
|
454
|
+
|
|
455
|
+
If KĀDI credentials are not provided, the tunnel will automatically fall back to free services (serveo → localtunnel → pinggy → localhost.run) unless `autoFallback: false` is set.
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## Dependencies
|
|
460
|
+
|
|
461
|
+
| Package | Purpose |
|
|
462
|
+
|---------|---------|
|
|
463
|
+
| `@kadi.build/file-manager` | File management utilities |
|
|
464
|
+
| `@kadi.build/tunnel-services` | Multi-provider tunnel management |
|
|
465
|
+
| `express` | HTTP framework |
|
|
466
|
+
| `cors` | CORS middleware |
|
|
467
|
+
| `mime-types` | MIME type detection |
|
|
468
|
+
| `xml2js` | XML generation for S3 responses |
|
|
469
|
+
| `chalk` | Console styling |
|
|
470
|
+
|
|
471
|
+
## Requirements
|
|
472
|
+
|
|
473
|
+
- Node.js ≥ 18.0.0
|
|
474
|
+
|
|
475
|
+
## License
|
|
476
|
+
|
|
477
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kadi.build/file-sharing",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "File sharing service with tunneling and local S3-compatible interface",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./src/index.js",
|
|
10
|
+
"types": "./types/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./s3": {
|
|
13
|
+
"import": "./src/S3Server.js"
|
|
14
|
+
},
|
|
15
|
+
"./http": {
|
|
16
|
+
"import": "./src/HttpServerProvider.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"src/",
|
|
21
|
+
"types/",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "node tests/run-all-tests.js",
|
|
26
|
+
"test:smoke": "node tests/test-dependency-smoke.js",
|
|
27
|
+
"test:http": "node tests/test-http-server.js",
|
|
28
|
+
"test:s3": "node tests/test-s3-server.js",
|
|
29
|
+
"test:integration": "node tests/test-file-sharing.js",
|
|
30
|
+
"test:monitor": "node tests/test-download-monitor.js",
|
|
31
|
+
"test:shutdown": "node tests/test-shutdown-manager.js",
|
|
32
|
+
"test:streaming": "node tests/test-streaming.js"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"file-sharing",
|
|
36
|
+
"http-server",
|
|
37
|
+
"s3-compatible",
|
|
38
|
+
"tunnel",
|
|
39
|
+
"kadi",
|
|
40
|
+
"file-server",
|
|
41
|
+
"download-monitor"
|
|
42
|
+
],
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@kadi.build/file-manager": "^1.0.0",
|
|
45
|
+
"@kadi.build/tunnel-services": "^1.0.2",
|
|
46
|
+
"chalk": "^5.3.0",
|
|
47
|
+
"cors": "^2.8.5",
|
|
48
|
+
"express": "^4.18.0",
|
|
49
|
+
"mime-types": "^2.1.35",
|
|
50
|
+
"xml2js": "^0.5.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@aws-sdk/client-s3": "^3.0.0",
|
|
54
|
+
"supertest": "^6.0.0"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=18.0.0"
|
|
58
|
+
},
|
|
59
|
+
"license": "MIT"
|
|
60
|
+
}
|