topnic-https 0.2.13 → 1.2.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/package.json +1 -1
- package/src/sharing/shareManager.js +15 -31
- package/src/sharing/tunnelServer.js +110 -0
package/package.json
CHANGED
@@ -1,55 +1,39 @@
|
|
1
|
-
const
|
1
|
+
const TunnelServer = require('./tunnelServer');
|
2
2
|
const { nanoid } = require('nanoid/non-secure');
|
3
3
|
|
4
4
|
class ShareManager {
|
5
5
|
constructor() {
|
6
6
|
this.activeShares = new Map();
|
7
|
+
this.tunnelServer = TunnelServer;
|
7
8
|
}
|
8
9
|
|
9
|
-
async
|
10
|
-
|
11
|
-
const url = await ngrok.connect({
|
12
|
-
addr: port,
|
13
|
-
onStatusChange: status => {
|
14
|
-
if (status === 'closed') {
|
15
|
-
console.log('🔄 تم إغلاق النفق');
|
16
|
-
}
|
17
|
-
},
|
18
|
-
onLogEvent: data => {
|
19
|
-
if (data.err) {
|
20
|
-
console.error('❌ خطأ في النفق:', data.err);
|
21
|
-
}
|
22
|
-
}
|
23
|
-
});
|
24
|
-
|
25
|
-
return { url, disconnect: ngrok.disconnect };
|
26
|
-
} catch (error) {
|
27
|
-
throw new Error(`فشل إنشاء النفق: ${error.message}`);
|
28
|
-
}
|
10
|
+
async init() {
|
11
|
+
await this.tunnelServer.start();
|
29
12
|
}
|
30
13
|
|
31
14
|
async createShare(port, duration) {
|
32
15
|
try {
|
33
|
-
const
|
34
|
-
const
|
16
|
+
const tunnelId = await this.tunnelServer.createTunnel(port);
|
17
|
+
const publicIp = this.tunnelServer.publicIp;
|
18
|
+
const shareUrl = `http://${publicIp}:8000`;
|
35
19
|
|
36
20
|
const share = {
|
37
|
-
id:
|
38
|
-
url:
|
39
|
-
tunnel,
|
21
|
+
id: tunnelId,
|
22
|
+
url: shareUrl,
|
40
23
|
startTime: Date.now(),
|
41
24
|
duration: duration * 60 * 1000
|
42
25
|
};
|
43
26
|
|
44
|
-
this.activeShares.set(
|
27
|
+
this.activeShares.set(tunnelId, share);
|
45
28
|
|
46
29
|
setTimeout(() => {
|
47
|
-
this.closeShare(
|
30
|
+
this.closeShare(tunnelId);
|
48
31
|
}, share.duration);
|
49
32
|
|
50
33
|
return {
|
51
|
-
id:
|
52
|
-
url:
|
34
|
+
id: tunnelId,
|
35
|
+
url: shareUrl,
|
36
|
+
localUrl: `http://localhost:8000`
|
53
37
|
};
|
54
38
|
} catch (error) {
|
55
39
|
throw new Error(`فشل إنشاء المشاركة: ${error.message}`);
|
@@ -62,7 +46,7 @@ class ShareManager {
|
|
62
46
|
throw new Error('لم يتم العثور على المشاركة');
|
63
47
|
}
|
64
48
|
|
65
|
-
|
49
|
+
this.tunnelServer.closeTunnel(shareId);
|
66
50
|
this.activeShares.delete(shareId);
|
67
51
|
}
|
68
52
|
|
@@ -0,0 +1,110 @@
|
|
1
|
+
const http = require('http');
|
2
|
+
const https = require('https');
|
3
|
+
const WebSocket = require('ws');
|
4
|
+
const { nanoid } = require('nanoid/non-secure');
|
5
|
+
const os = require('os');
|
6
|
+
|
7
|
+
class TunnelServer {
|
8
|
+
constructor() {
|
9
|
+
this.tunnels = new Map();
|
10
|
+
this.server = http.createServer(this.handleRequest.bind(this));
|
11
|
+
this.wss = new WebSocket.Server({ server: this.server });
|
12
|
+
this.publicIp = this.getPublicIp();
|
13
|
+
}
|
14
|
+
|
15
|
+
getPublicIp() {
|
16
|
+
const interfaces = os.networkInterfaces();
|
17
|
+
let address;
|
18
|
+
|
19
|
+
for (const iface of Object.values(interfaces)) {
|
20
|
+
for (const alias of iface) {
|
21
|
+
if (alias.family === 'IPv4' && !alias.internal) {
|
22
|
+
address = alias.address;
|
23
|
+
break;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
if (address) break;
|
27
|
+
}
|
28
|
+
|
29
|
+
return address;
|
30
|
+
}
|
31
|
+
|
32
|
+
async start(port = 8000) {
|
33
|
+
return new Promise((resolve) => {
|
34
|
+
this.server.listen(port, '0.0.0.0', () => {
|
35
|
+
console.log(`🚀 خادم النفق يعمل على المنفذ ${port}`);
|
36
|
+
console.log(`📡 عنوان IP العام: ${this.publicIp}`);
|
37
|
+
resolve();
|
38
|
+
});
|
39
|
+
});
|
40
|
+
}
|
41
|
+
|
42
|
+
async createTunnel(targetPort) {
|
43
|
+
const tunnelId = nanoid(6);
|
44
|
+
const tunnel = {
|
45
|
+
id: tunnelId,
|
46
|
+
targetPort,
|
47
|
+
clients: new Set(),
|
48
|
+
createdAt: Date.now()
|
49
|
+
};
|
50
|
+
|
51
|
+
this.tunnels.set(tunnelId, tunnel);
|
52
|
+
return tunnelId;
|
53
|
+
}
|
54
|
+
|
55
|
+
async handleRequest(req, res) {
|
56
|
+
const tunnelId = req.headers.host.split('.')[0];
|
57
|
+
const tunnel = this.tunnels.get(tunnelId);
|
58
|
+
|
59
|
+
if (!tunnel) {
|
60
|
+
res.writeHead(404);
|
61
|
+
res.end('النفق غير موجود');
|
62
|
+
return;
|
63
|
+
}
|
64
|
+
|
65
|
+
// إعادة توجيه الطلب إلى الخادم المحلي
|
66
|
+
const options = {
|
67
|
+
hostname: 'localhost',
|
68
|
+
port: tunnel.targetPort,
|
69
|
+
path: req.url,
|
70
|
+
method: req.method,
|
71
|
+
headers: req.headers
|
72
|
+
};
|
73
|
+
|
74
|
+
const proxyReq = http.request(options, (proxyRes) => {
|
75
|
+
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
76
|
+
proxyRes.pipe(res);
|
77
|
+
});
|
78
|
+
|
79
|
+
req.pipe(proxyReq);
|
80
|
+
|
81
|
+
proxyReq.on('error', (error) => {
|
82
|
+
console.error('خطأ في النفق:', error);
|
83
|
+
res.writeHead(502);
|
84
|
+
res.end('خطأ في الاتصال بالخادم المحلي');
|
85
|
+
});
|
86
|
+
}
|
87
|
+
|
88
|
+
handleConnection(ws, req) {
|
89
|
+
const tunnelId = req.headers.host.split('.')[0];
|
90
|
+
const tunnel = this.tunnels.get(tunnelId);
|
91
|
+
|
92
|
+
if (tunnel) {
|
93
|
+
tunnel.clients.add(ws);
|
94
|
+
|
95
|
+
ws.on('close', () => {
|
96
|
+
tunnel.clients.delete(ws);
|
97
|
+
});
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
closeTunnel(tunnelId) {
|
102
|
+
const tunnel = this.tunnels.get(tunnelId);
|
103
|
+
if (tunnel) {
|
104
|
+
tunnel.clients.forEach(client => client.close());
|
105
|
+
this.tunnels.delete(tunnelId);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
module.exports = new TunnelServer();
|