-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathIPNStatus.jsx
More file actions
216 lines (189 loc) · 8.18 KB
/
IPNStatus.jsx
File metadata and controls
216 lines (189 loc) · 8.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import React, { useState, useEffect, useRef } from 'react';
import { initIPN } from '../ipn-init';
const IPNStatus = () => {
const [ipn, setIpn] = useState(null);
const initRef = useRef(false);
useEffect(() => {
const wsCallback = (message) => {
try {
const data = JSON.parse(message);
// 在这里处理接收到的消息
console.log("Received JSON message via callback at ws server:", data);
} catch (e) {
console.log("Received raw message at ws server:", message);
}
};
const initIPNInstance = async () => {
if (initRef.current) return;
initRef.current = true;
const ipnInstance = await initIPN({ wsCallback });
if (ipnInstance) {
setIpn(ipnInstance);
}
}
initIPNInstance();
}, []);
const [ipnState, setIpnState] = useState("NoState");
const [netMap, setNetMap] = useState(null);
const [browseToURL, setBrowseToURL] = useState(null);
const [goPanicError, setGoPanicError] = useState(null);
const [netCheckResult, setNetCheckResult] = useState(null);
const [isChecking, setIsChecking] = useState(false);
const [isHTTPServerRunning, setIsHTTPServerRunning] = useState(false);
useEffect(() => {
if (!ipn) return;
ipn.run({
notifyState: handleIPNState,
notifyNetMap: handleNetMap,
notifyBrowseToURL: handleBrowseToURL,
notifyPanicRecover: handleGoPanic,
});
}, [ipn]);
const handleIPNState = async (state) => {
setIpnState(state);
if (state === "NeedsLogin") {
ipn?.login();
} else if (["Running", "NeedsMachineAuth"].includes(state)) {
setBrowseToURL(undefined);
if (!isHTTPServerRunning) {
const res = await ipn.startHTTPServer(8848)
console.log(res)
setIsHTTPServerRunning(true);
}
}
};
const handleNetMap = (netMapStr) => {
try {
const netMapData = JSON.parse(netMapStr);
setNetMap(netMapData);
console.log("Network Map:", netMapData);
} catch (err) {
console.error("Failed to parse netMap:", err);
}
};
const handleBrowseToURL = (url) => {
if (ipnState === "Running") return;
setBrowseToURL(url);
};
const handleGoPanic = (error) => {
console.error("Go panic:", error);
setGoPanicError(error);
setTimeout(() => setGoPanicError(null), 10000);
};
const handleNetCheck = async () => {
setIsChecking(true);
try {
const result = await ipn.netCheck();
const data = JSON.parse(result);
console.log(data)
setNetCheckResult(data);
} catch (err) {
console.log(err.toString());
} finally {
setIsChecking(false);
}
};
return (
<div className="ipn-status">
{/* 状态显示 */}
<div className="status-badge">
ipnstate:{ipnState}
</div>
<button onClick={handleNetCheck}>
netCheck
</button>
{/* 错误显示 */}
{goPanicError && (
<div className="error-message">
<p>Error: {goPanicError}</p>
</div>
)}
{/* 授权 URL 显示 */}
{browseToURL && (
<div className="auth-url">
<p>Please authenticate at:</p>
<a href={browseToURL} target="_blank" rel="noopener noreferrer">
{browseToURL}
</a>
</div>
)}
{/* 机器认证提示 */}
{ipnState === "NeedsMachineAuth" && (
<div className="auth-message">
<p>An administrator needs to approve this device.</p>
</div>
)}
{/* 网络状态显示 */}
{netMap && ipnState === "Running" && (
<div className="network-status" style={{ display: 'flex', flexDirection: 'row', gap: '10px' }}>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<h3>peers</h3>
{netMap.peers.map((peer) => (
<p key={peer.name}>
name: {peer.name}<br />
addresses: {peer.addresses.join(', ')}<br />
online: {peer.online ? 'true' : 'false'}<br />
<button onClick={async () => {
ipn.fetch(`http://${peer.addresses[0]}:8848/hello`)
.then(res => res.text())
.then(text => console.log(text))
}}>
fetch this peer on /hello
</button>
{/* ws is not available, previous success is because local tailscale client is running*/}
{/* <button onClick={async () => {
// 使用Tailscale网络中的WebSocket连接
const ws = new WebSocket(`ws://${peer.addresses[0]}:8848/ws`);
ws.onopen = () => {
console.log(`WebSocket connected to \nws://${peer.addresses[0]}:8848/ws`);
// 发送测试消息
setInterval(() => {
console.log(`send message to ${peer.addresses[0]}`)
ws.send(JSON.stringify({
type: 'hello',
from: netMap.self.name,
message: `Hello from ${netMap.self.addresses[0]} in ws !`
}));
}, 1000);
};
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
console.log(`Received message from ${peer.name}:`, data);
} catch (e) {
console.log(`Received raw message from ${peer.name}:`, event.data);
}
};
ws.onerror = (error) => {
console.error(`WebSocket error with ${peer.name}:`, error);
};
ws.onclose = () => {
console.log(`WebSocket connection with ${peer.name} closed`);
};
}}>
connect ws
</button> */}
</p>
))}
</div>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<h3>self</h3>
<p>name: {netMap.self.name}</p>
<p>addresses: {netMap.self.addresses.join(', ')}</p>
</div>
</div>
)}
{/* Tailnet Lock 提示 */}
{netMap?.lockedOut && (
<div className="locked-message">
<p>
This instance needs to be signed due to tailnet lock being enabled.
Run the following command on a trusted device:
</p>
<pre>tailscale lock sign {netMap.self?.nodeKey}</pre>
</div>
)}
</div>
);
};
export default IPNStatus;