1 |
|
2 | import pEvent from 'p-event';
|
3 | import isIp from 'is-ip';
|
4 |
|
5 | const getIp = async ({isSecondTry = false} = {}) => {
|
6 | try {
|
7 | const peerConnection = new RTCPeerConnection({iceServers: []});
|
8 |
|
9 | peerConnection.createDataChannel('');
|
10 | peerConnection.createOffer(peerConnection.setLocalDescription.bind(peerConnection), () => {});
|
11 |
|
12 | const {candidate} = await pEvent(peerConnection, 'icecandidate', {
|
13 | timeout: 10_000,
|
14 | });
|
15 |
|
16 | peerConnection.close();
|
17 |
|
18 | if (!(candidate && candidate.candidate)) {
|
19 | return;
|
20 | }
|
21 |
|
22 | const result = candidate.candidate.split(' ')[4];
|
23 | if (!result.endsWith('.local')) {
|
24 | return result;
|
25 | }
|
26 |
|
27 | if (isSecondTry) {
|
28 | return;
|
29 | }
|
30 |
|
31 | const inputDevices = await navigator.mediaDevices.enumerateDevices();
|
32 | const inputDeviceTypes = new Set(inputDevices.map(({kind}) => kind));
|
33 |
|
34 | const constraints = {};
|
35 | if (inputDeviceTypes.has('audioinput')) {
|
36 | constraints.audio = true;
|
37 | } else if (inputDeviceTypes.has('videoinput')) {
|
38 | constraints.video = true;
|
39 | } else {
|
40 | return;
|
41 | }
|
42 |
|
43 | const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
|
44 | for (const track of mediaStream.getTracks()) {
|
45 | track.stop();
|
46 | }
|
47 |
|
48 | return await getIp({isSecondTry: true});
|
49 | } catch {}
|
50 | };
|
51 |
|
52 | export async function internalIpV6() {
|
53 | const result = await getIp();
|
54 | if (isIp.v6(result)) {
|
55 | return result;
|
56 | }
|
57 | }
|
58 |
|
59 | export async function internalIpV4() {
|
60 | const result = await getIp();
|
61 | if (isIp.v4(result)) {
|
62 | return result;
|
63 | }
|
64 | }
|
65 |
|
66 | export function internalIpV6Sync() {
|
67 | return undefined;
|
68 | }
|
69 |
|
70 | export function internalIpV4Sync() {
|
71 | return undefined;
|
72 | }
|