@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.
@@ -0,0 +1,135 @@
1
+ /**
2
+ * ShutdownManager - Graceful shutdown for @kadi.build/file-sharing
3
+ *
4
+ * Manages ordered shutdown of components with priority-based callback execution,
5
+ * timeout handling, and process signal integration.
6
+ *
7
+ * Migrated from src/shutdownManager.js
8
+ */
9
+
10
+ import { EventEmitter } from 'events';
11
+
12
+ export class ShutdownManager extends EventEmitter {
13
+ constructor(config = {}) {
14
+ super();
15
+
16
+ this.config = {
17
+ gracefulTimeout: 30000,
18
+ finishActiveDownloads: true,
19
+ forceKillTimeout: 60000,
20
+ ...config
21
+ };
22
+
23
+ this.isShuttingDown = false;
24
+ this.shutdownCallbacks = [];
25
+ this._signalHandlersInstalled = false;
26
+ }
27
+
28
+ /**
29
+ * Register a shutdown callback with priority
30
+ * Lower priority numbers execute first.
31
+ * @param {Function} callback - Async callback to execute during shutdown
32
+ * @param {number} priority - Execution priority (lower = earlier, default 10)
33
+ */
34
+ register(callback, priority = 10) {
35
+ this.shutdownCallbacks.push({ callback, priority });
36
+ this.shutdownCallbacks.sort((a, b) => a.priority - b.priority);
37
+ }
38
+
39
+ /**
40
+ * Unregister a previously registered callback
41
+ * @param {Function} callback - The callback to remove
42
+ */
43
+ unregister(callback) {
44
+ this.shutdownCallbacks = this.shutdownCallbacks.filter(
45
+ (entry) => entry.callback !== callback
46
+ );
47
+ }
48
+
49
+ /**
50
+ * Install process signal handlers (SIGINT, SIGTERM)
51
+ */
52
+ installSignalHandlers() {
53
+ if (this._signalHandlersInstalled) return;
54
+
55
+ const handler = () => {
56
+ this.shutdown().catch((err) => {
57
+ this.emit('shutdown:error', err);
58
+ });
59
+ };
60
+
61
+ process.on('SIGINT', handler);
62
+ process.on('SIGTERM', handler);
63
+ this._signalHandlersInstalled = true;
64
+ }
65
+
66
+ /**
67
+ * Perform graceful shutdown
68
+ * Executes all registered callbacks in priority order with timeout.
69
+ */
70
+ async shutdown() {
71
+ if (this.isShuttingDown) {
72
+ return;
73
+ }
74
+
75
+ this.isShuttingDown = true;
76
+ this.emit('shutdown:start');
77
+
78
+ const total = this.shutdownCallbacks.length;
79
+ let completed = 0;
80
+
81
+ const timeoutPromise = new Promise((_, reject) => {
82
+ setTimeout(
83
+ () => reject(new Error('Shutdown timeout exceeded')),
84
+ this.config.gracefulTimeout
85
+ );
86
+ });
87
+
88
+ try {
89
+ await Promise.race([
90
+ this._executeCallbacks((count) => {
91
+ completed = count;
92
+ this.emit('shutdown:progress', { completed, total });
93
+ }),
94
+ timeoutPromise
95
+ ]);
96
+
97
+ this.emit('shutdown:complete');
98
+ } catch (error) {
99
+ this.emit('shutdown:timeout', error);
100
+ await this.forceShutdown();
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Force shutdown - skip remaining callbacks
106
+ */
107
+ async forceShutdown() {
108
+ this.emit('shutdown:force');
109
+ this.isShuttingDown = true;
110
+ // Clear remaining callbacks
111
+ this.shutdownCallbacks = [];
112
+ this.emit('shutdown:complete');
113
+ }
114
+
115
+ /**
116
+ * Execute all registered callbacks in order
117
+ * @param {Function} onProgress - Progress callback
118
+ * @private
119
+ */
120
+ async _executeCallbacks(onProgress) {
121
+ let completed = 0;
122
+
123
+ for (const { callback } of this.shutdownCallbacks) {
124
+ try {
125
+ await callback();
126
+ } catch (error) {
127
+ this.emit('shutdown:error', error);
128
+ }
129
+ completed++;
130
+ if (onProgress) onProgress(completed);
131
+ }
132
+ }
133
+ }
134
+
135
+ export default ShutdownManager;
package/src/index.js ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @kadi.build/file-sharing - Main entry point
3
+ *
4
+ * File sharing service with tunneling and local S3-compatible interface.
5
+ * Integrates @kadi.build/file-manager and @kadi.build/tunnel-services.
6
+ */
7
+
8
+ // Core classes
9
+ export { FileSharingServer } from './FileSharingServer.js';
10
+ export { HttpServerProvider } from './HttpServerProvider.js';
11
+ export { S3Server } from './S3Server.js';
12
+ export { DownloadMonitor } from './DownloadMonitor.js';
13
+ export { ShutdownManager } from './ShutdownManager.js';
14
+ export { MonitoringDashboard } from './MonitoringDashboard.js';
15
+ export { EventNotifier } from './EventNotifier.js';
16
+
17
+ // Re-export from dependencies for convenience
18
+ export { FileManager, createFileManager } from '@kadi.build/file-manager';
19
+ export { TunnelManager } from '@kadi.build/tunnel-services';
20
+
21
+ // Default export
22
+ export { FileSharingServer as default } from './FileSharingServer.js';
23
+
24
+ /**
25
+ * Create a FileSharingServer with default options
26
+ * @param {object} options - Configuration options
27
+ * @returns {FileSharingServer}
28
+ */
29
+ export function createFileSharingServer(options = {}) {
30
+ return new FileSharingServer(options);
31
+ }
32
+
33
+ /**
34
+ * Quick share - one-liner to share a directory
35
+ * @param {string} directory - Directory to share
36
+ * @param {object} options - Options
37
+ * @param {number} [options.port=3000] - HTTP port
38
+ * @param {boolean} [options.tunnel=false] - Enable tunnel
39
+ * @param {string} [options.tunnelService='kadi'] - Tunnel service name
40
+ * @param {string} [options.kadiToken] - KĀDI auth token (or set KADI_TUNNEL_TOKEN env)
41
+ * @param {string} [options.ngrokAuthToken] - Ngrok auth token (or set NGROK_AUTHTOKEN env)
42
+ * @param {object} [options.auth] - HTTP auth config ({apiKey} or {username,password})
43
+ * @param {object} [options.tunnelOptions] - Extra options passed to TunnelManager
44
+ * @returns {Promise<{ server: FileSharingServer, localUrl: string, publicUrl?: string }>}
45
+ */
46
+ export async function createQuickShare(directory, options = {}) {
47
+ const server = new FileSharingServer({
48
+ staticDir: directory,
49
+ port: options.port || 3000,
50
+ auth: options.auth || null,
51
+ tunnel: options.tunnel
52
+ ? {
53
+ enabled: true,
54
+ service: options.tunnelService || 'kadi',
55
+ kadiToken: options.kadiToken,
56
+ ngrokAuthToken: options.ngrokAuthToken,
57
+ ...(options.tunnelOptions || {})
58
+ }
59
+ : { enabled: false }
60
+ });
61
+
62
+ const info = await server.start();
63
+
64
+ return {
65
+ server,
66
+ localUrl: info.localUrl,
67
+ publicUrl: info.publicUrl || undefined
68
+ };
69
+ }